You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
732 lines
17 KiB
732 lines
17 KiB
/*!
|
|
* STM32F4 Virtual EEPROM driver
|
|
*
|
|
* This module implements a crude virtual EEPROM device stored in on-board
|
|
* flash. The STM32F405 has 4 16kB flash sectors starting at address
|
|
* 0x80000000, followed by a 64kB sector, then 128kB sectors.
|
|
*
|
|
* The Cortex M4 core maps these all to address 0x00000000 when booting
|
|
* from normal flash, so the first sector is reserved for interrupt
|
|
* vectors.
|
|
*
|
|
* Everything else however is free game, and so we use these smaller
|
|
* sectors to store our configuration.
|
|
*
|
|
* Author Stuart Longland <me@vk4msl.id.au>
|
|
* Copyright (C) 2015 FreeDV project.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License version 2.1,
|
|
* as published by the Free Software Foundation. This program is
|
|
* distributed in the hope that it will be useful, but WITHOUT ANY
|
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this program; if not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "stm32f4xx_flash.h"
|
|
#include "stm32f4xx_crc.h"
|
|
#include "vrom.h"
|
|
#include "config.h"
|
|
|
|
#ifndef TARGET_VROM_SECT_CNT
|
|
#define TARGET_VROM_SECT_CNT 3
|
|
#endif
|
|
|
|
#define VROM_SECT_SZ (16384) /*!< Size of a flash sector */
|
|
#define VROM_SECT_CNT (TARGET_VROM_SECT_CNT) /*!< Number of sectors */
|
|
#define VROM_BLOCK_SZ (256) /*!< Size of a flash block */
|
|
|
|
/*!
|
|
* Starting address for the flash area
|
|
*/
|
|
#ifdef TARGET_VROM_START_ADDR
|
|
#define TARGET_VROM_START_ADDR 0x08004000
|
|
#endif
|
|
#define VROM_START_ADDR (TARGET_VROM_START_ADDR)
|
|
|
|
/*!
|
|
* Number of blocks we can fit per sector, including the index block.
|
|
*/
|
|
#define VROM_BLOCK_CNT (VROM_SECT_SZ / VROM_BLOCK_SZ)
|
|
|
|
/*!
|
|
* Number of application blocks we can fit per sector.
|
|
*/
|
|
#define VROM_SECT_APP_BLOCK_CNT (VROM_BLOCK_CNT - 1)
|
|
|
|
/*!
|
|
* Total number of application blocks we can fit in flash.
|
|
*/
|
|
#define VROM_APP_BLOCK_CNT (VROM_SECT_CNT * VROM_SECT_APP_BLOCK_CNT)
|
|
|
|
/*!
|
|
* Maximum number of erase cycles per sector.
|
|
* Table 42 (page 109) of STM32F405 datasheet (DocID022152 Rev 5).
|
|
*/
|
|
#define VROM_MAX_CYCLES (10000)
|
|
|
|
/*!
|
|
* EEPROM block header.
|
|
*/
|
|
struct __attribute__ ((__packed__)) vrom_block_hdr_t {
|
|
/*!
|
|
* CRC32 checksum of the data, offset, size and ROM ID.
|
|
* A CRC32 of 0x00000000 indicates an obsoleted block.
|
|
* A CRC32 of 0xffffffff indicates an erased block.
|
|
*/
|
|
uint32_t crc32;
|
|
/*!
|
|
* ROM ID.
|
|
*/
|
|
uint8_t rom;
|
|
/*!
|
|
* Block number in the virtual EEPROM.
|
|
*/
|
|
uint8_t idx;
|
|
/*!
|
|
* Number of bytes from the virtual EEPROM stored in this block.
|
|
*/
|
|
uint8_t size;
|
|
/*!
|
|
* Reserved for future use.
|
|
*/
|
|
uint8_t reserved;
|
|
};
|
|
|
|
/*!
|
|
* The size of a block header in bytes.
|
|
*/
|
|
#define VROM_BLOCK_HDR_SZ (sizeof(struct vrom_block_hdr_t))
|
|
|
|
/*!
|
|
* The amount of data available for application use.
|
|
*/
|
|
#define VROM_DATA_SZ (VROM_BLOCK_SZ - VROM_BLOCK_HDR_SZ)
|
|
|
|
/*!
|
|
* EEPROM data block.
|
|
*/
|
|
struct __attribute__ ((__packed__)) vrom_data_block_t {
|
|
/*! Block header */
|
|
struct vrom_block_hdr_t header;
|
|
|
|
/*! Block data */
|
|
uint8_t data[VROM_DATA_SZ];
|
|
};
|
|
|
|
/*!
|
|
* The first block in a sector is the sector index block. This indicates
|
|
* the used/free state of the entire block and counts the number of
|
|
* erase cycles for the sector. The index block has no header.
|
|
*/
|
|
struct __attribute__ ((__packed__)) vrom_sector_idx_t {
|
|
/*!
|
|
* Number of erase cycles remaining for the sector.
|
|
* 0xffffffff == unprogrammed.
|
|
*/
|
|
uint32_t cycles_remain;
|
|
/*!
|
|
* Block metadata flags. One for each data block in the sector.
|
|
* Does not include the index block.
|
|
*/
|
|
uint16_t flags[VROM_SECT_APP_BLOCK_CNT];
|
|
};
|
|
|
|
#define VROM_SFLAGS_USED (1 << 0) /*!< Block in use */
|
|
|
|
/*!
|
|
* Return the address of a virtual EEPROM sector header.
|
|
*/
|
|
static const struct vrom_sector_idx_t* vrom_get_sector_hdr(uint8_t sector)
|
|
{
|
|
return (const struct vrom_sector_idx_t*)(
|
|
VROM_START_ADDR + (VROM_SECT_SZ * sector));
|
|
}
|
|
|
|
/*!
|
|
* Return the address of a virtual EEPROM block.
|
|
*/
|
|
static const struct vrom_data_block_t* vrom_get_block(
|
|
uint8_t sector, uint8_t block)
|
|
{
|
|
return (const struct vrom_data_block_t*)(
|
|
(void*)vrom_get_sector_hdr(sector)
|
|
+ (VROM_BLOCK_SZ * (block + 1)));
|
|
}
|
|
|
|
/*!
|
|
* Compute the CRC32 of a block.
|
|
*/
|
|
static uint32_t vrom_crc32(
|
|
const struct vrom_data_block_t* const block)
|
|
{
|
|
struct vrom_data_block_t temp_block;
|
|
uint32_t size = sizeof(temp_block);
|
|
const uint8_t* in = (const uint8_t*)(&temp_block);
|
|
uint32_t tmp;
|
|
uint32_t crc;
|
|
|
|
memcpy(&temp_block, block, sizeof(temp_block));
|
|
temp_block.header.crc32 = 0;
|
|
|
|
CRC_ResetDR();
|
|
while(size) {
|
|
tmp = 0;
|
|
if (size) {
|
|
tmp |= (uint32_t)(*(in++)) << 24;
|
|
size--;
|
|
}
|
|
if (size) {
|
|
tmp |= (uint32_t)(*(in++)) << 16;
|
|
size--;
|
|
}
|
|
if (size) {
|
|
tmp |= (uint32_t)(*(in++)) << 8;
|
|
size--;
|
|
}
|
|
if (size) {
|
|
tmp |= (uint32_t)(*(in++));
|
|
size--;
|
|
}
|
|
crc = CRC_CalcCRC(tmp);
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
/*!
|
|
* Find the block storing the given index.
|
|
*/
|
|
static const struct vrom_data_block_t* vrom_find(uint8_t rom, uint8_t idx)
|
|
{
|
|
int sector, block;
|
|
|
|
for (sector = 0; sector < VROM_SECT_CNT; sector++) {
|
|
const struct vrom_sector_idx_t* sect_hdr
|
|
= vrom_get_sector_hdr(sector);
|
|
if (sect_hdr->cycles_remain == UINT32_MAX)
|
|
/* unformatted */
|
|
continue;
|
|
for (block = 0; block < VROM_SECT_APP_BLOCK_CNT; block++) {
|
|
const struct vrom_data_block_t* block_ptr;
|
|
if (sect_hdr->flags[block] == UINT16_MAX)
|
|
/* unformatted */
|
|
continue;
|
|
if (sect_hdr->flags[block] == 0)
|
|
/* obsolete */
|
|
continue;
|
|
|
|
block_ptr = vrom_get_block(sector, block);
|
|
|
|
/* Verify the content */
|
|
if (vrom_crc32(block_ptr)
|
|
!= block_ptr->header.crc32)
|
|
/* corrupt */
|
|
continue;
|
|
|
|
if (block_ptr->header.rom != rom)
|
|
/* different ROM */
|
|
continue;
|
|
|
|
if (block_ptr->header.idx != idx)
|
|
/* wrong index */
|
|
continue;
|
|
|
|
return block_ptr;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*!
|
|
* Get the sector number of a given address.
|
|
*/
|
|
static uint8_t vrom_sector_num(const void* address)
|
|
{
|
|
/* Get the offset from the base address */
|
|
uint32_t offset = (uint32_t)address - VROM_START_ADDR;
|
|
return offset / VROM_SECT_SZ;
|
|
}
|
|
|
|
/*!
|
|
* Get the block number of a given address.
|
|
*/
|
|
static uint8_t vrom_block_num(const void* address)
|
|
{
|
|
/* Get the sector number */
|
|
uint8_t sector = vrom_sector_num(address);
|
|
|
|
/* Get the offset from the sector base */
|
|
uint32_t offset = (uint32_t)(address
|
|
- (const void*)vrom_get_sector_hdr(sector));
|
|
offset /= VROM_BLOCK_SZ;
|
|
return offset - 1;
|
|
}
|
|
|
|
/*!
|
|
* (Erase and) Format a sector.
|
|
*
|
|
* @retval -EIO Erase failed
|
|
* @retval -EPERM Erase counter depleted.
|
|
*/
|
|
static int vrom_format_sector(const struct vrom_sector_idx_t* sector)
|
|
{
|
|
uint8_t sector_num = vrom_sector_num(sector);
|
|
uint32_t cycles_remain = VROM_MAX_CYCLES;
|
|
if (sector->cycles_remain != UINT32_MAX) {
|
|
if (sector->cycles_remain == 0)
|
|
/* This sector is exhausted */
|
|
return -EPERM;
|
|
|
|
/* This sector has been formatted before */
|
|
cycles_remain = sector->cycles_remain - 1;
|
|
if (FLASH_EraseSector(sector_num + 1, VoltageRange_3))
|
|
/* Erase failed */
|
|
return -EIO;
|
|
}
|
|
|
|
/* Program the new sector cycle counter */
|
|
if (FLASH_ProgramWord((uint32_t)sector,
|
|
cycles_remain) == FLASH_COMPLETE)
|
|
return 0; /* All good */
|
|
/* If we get here, then programming failed */
|
|
return -EIO;
|
|
}
|
|
|
|
/*!
|
|
* Find the next available block.
|
|
*/
|
|
static const struct vrom_data_block_t* vrom_find_free(uint8_t run_gc)
|
|
{
|
|
int sector;
|
|
if (run_gc) {
|
|
for (sector = 0; sector < VROM_SECT_CNT; sector++) {
|
|
uint8_t block;
|
|
uint8_t used = 0;
|
|
const struct vrom_sector_idx_t* sect_hdr
|
|
= vrom_get_sector_hdr(sector);
|
|
if (sect_hdr->cycles_remain == UINT32_MAX)
|
|
/* Already erased */
|
|
continue;
|
|
if (sect_hdr->cycles_remain == 0)
|
|
/* Depleted */
|
|
continue;
|
|
|
|
for (block = 0; block < VROM_SECT_APP_BLOCK_CNT;
|
|
block++) {
|
|
if (sect_hdr->flags[block]) {
|
|
used = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!used) {
|
|
/* We can format this */
|
|
vrom_format_sector(sect_hdr);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (sector = 0; sector < VROM_SECT_CNT; sector++) {
|
|
uint8_t block;
|
|
const struct vrom_sector_idx_t* sect_hdr
|
|
= vrom_get_sector_hdr(sector);
|
|
if (sect_hdr->cycles_remain == UINT32_MAX) {
|
|
/* Unformatted sector. */
|
|
if (vrom_format_sector(sect_hdr))
|
|
/* Couldn't format, keep looking */
|
|
continue;
|
|
}
|
|
for (block = 0; block < VROM_SECT_APP_BLOCK_CNT; block++) {
|
|
if (sect_hdr->flags[block] == UINT16_MAX)
|
|
/* Success */
|
|
return vrom_get_block(sector, block);
|
|
}
|
|
}
|
|
|
|
/* No blocks free, but have we done garbage collection? */
|
|
if (!run_gc)
|
|
return vrom_find_free(1);
|
|
|
|
/* If we get here, then we weren't able to find a free block */
|
|
return NULL;
|
|
}
|
|
|
|
/*!
|
|
* Set flags for a block
|
|
*/
|
|
static int vrom_set_flags(const struct vrom_data_block_t* block,
|
|
uint16_t flags)
|
|
{
|
|
const struct vrom_sector_idx_t* sector =
|
|
vrom_get_sector_hdr(vrom_sector_num(block));
|
|
uint8_t block_num = vrom_block_num(block);
|
|
|
|
/* Compute the new flags settings */
|
|
flags = sector->flags[block_num] & ~flags;
|
|
|
|
/* Write them */
|
|
if (FLASH_ProgramHalfWord(
|
|
(uint32_t)(&(sector->flags[block_num])),
|
|
flags) != FLASH_COMPLETE)
|
|
return -EIO;
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
* Mark a block as being obsolete
|
|
*/
|
|
static int vrom_mark_obsolete(const struct vrom_data_block_t* block)
|
|
{
|
|
/* Blank out the CRC */
|
|
if (FLASH_ProgramWord((uint32_t)(&(block->header.crc32)), 0)
|
|
!= FLASH_COMPLETE)
|
|
return -EIO;
|
|
/* Blank out the ROM ID */
|
|
if (FLASH_ProgramByte((uint32_t)(&(block->header.rom)), 0)
|
|
!= FLASH_COMPLETE)
|
|
return -EIO;
|
|
/* Blank out the index */
|
|
if (FLASH_ProgramByte((uint32_t)(&(block->header.idx)), 0)
|
|
!= FLASH_COMPLETE)
|
|
return -EIO;
|
|
/* Blank out the size */
|
|
if (FLASH_ProgramByte((uint32_t)&(block->header.size), 0)
|
|
!= FLASH_COMPLETE)
|
|
return -EIO;
|
|
/* Blank out the reserved byte */
|
|
if (FLASH_ProgramByte((uint32_t)&(block->header.reserved), 0)
|
|
!= FLASH_COMPLETE)
|
|
return -EIO;
|
|
/* Blank out the flags */
|
|
return vrom_set_flags(block, -1);
|
|
}
|
|
|
|
/*!
|
|
* Write a new block.
|
|
*/
|
|
static int vrom_write_block(uint8_t rom, uint8_t idx, uint8_t size,
|
|
const uint8_t* in)
|
|
{
|
|
/* Find a new home for the block */
|
|
const struct vrom_data_block_t* block = vrom_find_free(0);
|
|
struct vrom_data_block_t new_block;
|
|
uint8_t* out = (uint8_t*)(block);
|
|
uint32_t rem = sizeof(new_block);
|
|
int res;
|
|
|
|
if (!block)
|
|
return -ENOSPC;
|
|
|
|
/* Prepare the new block */
|
|
memset(&new_block, 0xff, sizeof(new_block));
|
|
new_block.header.rom = rom;
|
|
new_block.header.idx = idx;
|
|
new_block.header.size = size;
|
|
memcpy(new_block.data, in, size);
|
|
new_block.header.crc32 = vrom_crc32(&new_block);
|
|
|
|
/* Start writing out the block */
|
|
in = (uint8_t*)(&new_block);
|
|
rem = VROM_BLOCK_SZ;
|
|
while(rem) {
|
|
if (*out != *in) {
|
|
if (FLASH_ProgramByte((uint32_t)out, *in)
|
|
!= FLASH_COMPLETE)
|
|
/* Failed! */
|
|
return -EIO;
|
|
}
|
|
in++;
|
|
out++;
|
|
rem--;
|
|
}
|
|
res = vrom_set_flags(block, VROM_SFLAGS_USED);
|
|
if (res < 0)
|
|
return res;
|
|
return size;
|
|
}
|
|
|
|
/*!
|
|
* Re-write the given block if needed.
|
|
*/
|
|
static int vrom_rewrite_block(const struct vrom_data_block_t* block,
|
|
uint8_t size, const uint8_t* in)
|
|
{
|
|
uint8_t obsolete = 0;
|
|
uint8_t rom = block->header.rom;
|
|
uint8_t idx = block->header.idx;
|
|
const uint8_t* cmp_block = block->data;
|
|
const uint8_t* cmp_in = in;
|
|
uint8_t cmp_sz = size;
|
|
int res;
|
|
while(cmp_sz) {
|
|
if (*cmp_block != *cmp_in) {
|
|
obsolete = 1;
|
|
break;
|
|
}
|
|
cmp_sz--;
|
|
cmp_block++;
|
|
cmp_in++;
|
|
}
|
|
|
|
if (!obsolete)
|
|
/* The block is fine, leave it be. */
|
|
return size;
|
|
|
|
/* Mark the block as obsolete */
|
|
res = vrom_mark_obsolete(block);
|
|
if (res)
|
|
return res;
|
|
return vrom_write_block(rom, idx, size, in);
|
|
}
|
|
|
|
/*!
|
|
* Overwrite the start of a block.
|
|
*/
|
|
static int vrom_overwrite_block(
|
|
const struct vrom_data_block_t* block,
|
|
uint8_t offset, uint8_t size, const uint8_t* in)
|
|
{
|
|
uint8_t data[VROM_DATA_SZ];
|
|
uint16_t block_sz = block->header.size;
|
|
int res;
|
|
|
|
if (!offset && (size >= block->header.size))
|
|
/* Complete overwrite */
|
|
return vrom_rewrite_block(block, size, in);
|
|
|
|
if (offset) {
|
|
/* Overwrite end of block, possible expansion */
|
|
block_sz = offset + size;
|
|
if (block_sz > VROM_DATA_SZ)
|
|
block_sz = VROM_DATA_SZ;
|
|
memcpy(data, block->data, offset);
|
|
memcpy(&data[offset], in, block_sz - offset);
|
|
} else {
|
|
/* Overwrite start of block, no size change */
|
|
memcpy(data, in, size);
|
|
memcpy(&data[size], &(block->data[size]),
|
|
block_sz - size);
|
|
}
|
|
|
|
res = vrom_rewrite_block(block, block_sz, data);
|
|
if (res < 0)
|
|
return res;
|
|
return block_sz;
|
|
}
|
|
|
|
/*!
|
|
* Write data to the virtual EEPROM.
|
|
*/
|
|
static int vrom_write_internal(uint8_t rom,
|
|
uint16_t offset, uint16_t size, const uint8_t* in)
|
|
{
|
|
/* Figure out our starting block and offset */
|
|
uint8_t block_idx = offset / VROM_DATA_SZ;
|
|
uint8_t block_offset = offset % VROM_DATA_SZ;
|
|
int count = 0;
|
|
|
|
/* Locate the first block */
|
|
const struct vrom_data_block_t* block = vrom_find(rom, block_idx);
|
|
|
|
uint8_t block_sz = VROM_DATA_SZ;
|
|
if (block_sz > (size + block_offset))
|
|
block_sz = size + block_offset;
|
|
|
|
if (!block) {
|
|
/* Create a new block */
|
|
uint8_t data[VROM_DATA_SZ];
|
|
int res;
|
|
memset(data, 0xff, sizeof(data));
|
|
memcpy(&data[block_offset], in,
|
|
block_sz-block_offset);
|
|
res = vrom_write_block(rom, block_idx, block_sz, data);
|
|
if (res < 0)
|
|
return res;
|
|
} else {
|
|
/* Overwrite block */
|
|
int res = vrom_overwrite_block(block, block_offset,
|
|
block_sz, in);
|
|
if (res < 0)
|
|
return res;
|
|
count += block_sz;
|
|
}
|
|
|
|
block_idx++;
|
|
size -= block_sz - block_offset;
|
|
|
|
while(size) {
|
|
/* Work out how much data to write */
|
|
if (size < VROM_DATA_SZ)
|
|
block_sz = size;
|
|
else
|
|
block_sz = VROM_DATA_SZ;
|
|
|
|
int res;
|
|
|
|
/* Is there a block covering this range? */
|
|
block = vrom_find(rom, block_idx);
|
|
if (block)
|
|
res = vrom_overwrite_block(
|
|
block, 0, block_sz, in);
|
|
else
|
|
res = vrom_write_block(rom, block_idx,
|
|
block_sz, in);
|
|
|
|
if (res < 0)
|
|
return res;
|
|
|
|
/* Successful write */
|
|
count += res;
|
|
size -= res;
|
|
in += res;
|
|
offset += res;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/*!
|
|
* Read data from a virtual EEPROM.
|
|
* @param rom ROM ID to start reading.
|
|
* @param offset Address offset into ROM to start reading.
|
|
* @param size Number of bytes to read from ROM.
|
|
* @param out Buffer to write ROM content to.
|
|
* @returns Number of bytes read from ROM.
|
|
* @retval -ENXIO ROM not found
|
|
* @retval -ESPIPE Offset past end of ROM.
|
|
*/
|
|
int vrom_read(uint8_t rom, uint16_t offset, uint16_t size, void* out)
|
|
{
|
|
/* Figure out our starting block and offset */
|
|
uint8_t block_idx = offset / VROM_DATA_SZ;
|
|
uint8_t block_offset = offset % VROM_DATA_SZ;
|
|
uint8_t block_sz;
|
|
int count = 0;
|
|
uint8_t* out_ptr = (uint8_t*)out;
|
|
|
|
/* Locate the first block */
|
|
const struct vrom_data_block_t* block = vrom_find(rom, block_idx);
|
|
|
|
if (!block)
|
|
return -ENXIO;
|
|
|
|
if (block_offset >= block->header.size)
|
|
return -ESPIPE;
|
|
|
|
/* Copy the initial bytes */
|
|
block_sz = block->header.size - block_offset;
|
|
if (block_sz > size)
|
|
block_sz = size;
|
|
memcpy(out_ptr, &(block->data[block_offset]), block_sz);
|
|
out_ptr += block_sz;
|
|
size -= block_sz;
|
|
count += block_sz;
|
|
|
|
if (size) {
|
|
/* Look for the next block */
|
|
block = vrom_find(rom, ++block_idx);
|
|
while(size && block) {
|
|
if (block->header.size <= size)
|
|
block_sz = block->header.size;
|
|
else
|
|
block_sz = size;
|
|
memcpy(out_ptr, block->data, block_sz);
|
|
out_ptr += block_sz;
|
|
size -= block_sz;
|
|
count += block_sz;
|
|
|
|
block = vrom_find(rom, ++block_idx);
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/*!
|
|
* Write data to a virtual EEPROM.
|
|
* @param rom ROM ID to start writing.
|
|
* @param offset Address offset into ROM to start writing.
|
|
* @param size Number of bytes to write to ROM.
|
|
* @param in Buffer to write ROM content from.
|
|
* @returns Number of bytes written to ROM.
|
|
* @retval -EIO Programming failed
|
|
* @retval -ENOSPC No free blocks available
|
|
*/
|
|
int vrom_write(uint8_t rom, uint16_t offset, uint16_t size,
|
|
const void* in)
|
|
{
|
|
int res;
|
|
FLASH_Unlock();
|
|
FLASH_ClearFlag(FLASH_FLAG_EOP
|
|
| FLASH_FLAG_OPERR
|
|
| FLASH_FLAG_WRPERR
|
|
| FLASH_FLAG_PGAERR
|
|
| FLASH_FLAG_PGPERR
|
|
| FLASH_FLAG_PGSERR);
|
|
res = vrom_write_internal(rom, offset, size, in);
|
|
FLASH_Lock();
|
|
return res;
|
|
}
|
|
|
|
/*!
|
|
* Erase a virtual EEPROM.
|
|
* @param rom ROM ID to erase.
|
|
* @returns Number of bytes written to ROM.
|
|
* @retval -EIO Programming failed
|
|
* @retval -ENOSPC No free blocks available
|
|
*/
|
|
int vrom_erase(uint8_t rom)
|
|
{
|
|
int sector, block;
|
|
FLASH_Unlock();
|
|
FLASH_ClearFlag(FLASH_FLAG_EOP
|
|
| FLASH_FLAG_OPERR
|
|
| FLASH_FLAG_WRPERR
|
|
| FLASH_FLAG_PGAERR
|
|
| FLASH_FLAG_PGPERR
|
|
| FLASH_FLAG_PGSERR);
|
|
for (sector = 0; sector < VROM_SECT_CNT; sector++) {
|
|
const struct vrom_sector_idx_t* sect_hdr
|
|
= vrom_get_sector_hdr(sector);
|
|
if (sect_hdr->cycles_remain == UINT32_MAX)
|
|
/* unformatted */
|
|
continue;
|
|
for (block = 0; block < VROM_SECT_APP_BLOCK_CNT; block++) {
|
|
int res;
|
|
const struct vrom_data_block_t* block_ptr;
|
|
if (sect_hdr->flags[block] == UINT16_MAX)
|
|
/* unformatted */
|
|
continue;
|
|
if (sect_hdr->flags[block] == 0)
|
|
/* obsolete */
|
|
continue;
|
|
|
|
block_ptr = vrom_get_block(sector, block);
|
|
|
|
/* Verify the content */
|
|
if (vrom_crc32(block_ptr)
|
|
!= block_ptr->header.crc32)
|
|
/* corrupt */
|
|
continue;
|
|
|
|
if (block_ptr->header.rom != rom)
|
|
/* different ROM */
|
|
continue;
|
|
|
|
/*
|
|
* Block is valid, for the correct ROM. Mark it
|
|
* obsolete.
|
|
*/
|
|
res = vrom_mark_obsolete(block_ptr);
|
|
if (res)
|
|
return res;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|