Tiny storage(file) system on MCU internal flash memory for Nucleo F4xx. The purpose of SOFBlock class is to provide a way to write data on flash memory in the same way of file handling class in the file system.

Dependents:   storage_on_flash_demo mbed_controller_demo mbed-os-example-blinky-2

March 26, 2015

Seeed Arch Max platform which is based on STM32-F407 is supported.

SOF_block.cpp

Committer:
hillkim7
Date:
2015-01-19
Revision:
0:7f4bc855cb46
Child:
2:e79a9cb05801

File content as of revision 0:7f4bc855cb46:

/*
* @file SOF_block.cpp
*
* @brief MTD device handling of STM32 internal flash memory.
*
*
* History:
*/

#include <stdio.h>
#include <assert.h>
#include "SOF_dev.h"
#include <string.h>

#define LOCAL_DEBUG 0 // turn on local debug

#define DCRLF	"\r\n"

#if LOCAL_DEBUG
#define DPRINTF printf
#define EPRINTF printf
#define DUMP_BLOCK dump_block
#define DASSERT assert
#else
#define DPRINTF(...)
#define EPRINTF(...)
#define DUMP_BLOCK(...)
#define DASSERT(...)
#endif

static uint16_t checksum(const uint8_t* data, int count);

#define SYNC_MARK_BYTE_IN_LEN	0x07	// signature mark for upper byte in the storage_len

#define RESERVED_BLOCK_INFO_SIZE sizeof(BlockInfo_t)

typedef struct
{
	uint16_t for_storage;
	uint16_t for_info;
} BlockChecksum_t;

typedef struct
{
	BlockChecksum_t		csum; 
	uint32_t			storage_len; 
} BlockInfo_t;

typedef struct
{
	uint32_t	begin_offset;
	uint32_t	len;
	uint16_t	storage_csum;
} StorageInfo_t;

class SOF_BlockHandle
{
public:
	SOF_BlockHandle()
		: write_mode_(false)
		, cur_pos_(0)
		, hdev_(SOF_INVALID_HANDLE)
		, storage_max_offset_(0)
		, storage_begin_offset_(0)
		, storage_end_offset_(0)
	{
	}

	bool is_writable() const
	{
		return write_mode_;
	}

	uint32_t total_physical_block_size() const
	{
		return SOF_dev_info(hdev_)->sec_size;
	}

public:
	bool				write_mode_;
	size_t				cur_pos_;
	SOF_DevHandle_t		hdev_;
	uint32_t			storage_max_offset_;
	uint32_t			storage_begin_offset_;
	uint32_t			storage_end_offset_;
};


static bool get_block_info(const SOF_BlockHandle_t handle, size_t seq, BlockInfo_t *info, uint32_t *loc_offset)
{
	uint32_t check_pos = ((seq+1) * sizeof(BlockInfo_t));
	uint32_t info_pos;

	DASSERT(check_pos < handle->total_physical_block_size());
	if (check_pos >= handle->total_physical_block_size())
		return false;

	*loc_offset = info_pos = handle->total_physical_block_size() - check_pos;

	// checksum in the first word
	*((uint32_t*)&info->csum) = SOF_dev_read_word(handle->hdev_, info_pos);
	// storage len in the next word
	info->storage_len = SOF_dev_read_word(handle->hdev_, info_pos + 4);

	return true;
}

static bool is_empty_block_info(BlockInfo_t *info)
{
	uint8_t *p = (uint8_t*)info;

	for (size_t i = 0; i < sizeof(BlockInfo_t); ++i)
		if (p[i] != SOF_ERASED_BYTE_VALUE)
			return false;

	return true;
}

static bool is_valid_block_info(BlockInfo_t *info)
{
	uint16_t csum = checksum((uint8_t*)&info->storage_len, 4);

	if (SYNC_MARK_BYTE_IN_LEN != (info->storage_len >> 24))
	{
		EPRINTF("no sync mark in storage_len=%#x"DCRLF,info->storage_len);
		return false;
	}

	if (csum != info->csum.for_info)
	{
		EPRINTF("CSUM mismatch %#x %#x"DCRLF,csum, info->csum.for_info);
		return false;
	}

	return true;
}

static bool get_empty_info_location(const SOF_BlockHandle_t handle, uint32_t *loc_offset)
{
	BlockInfo_t info;
	uint32_t pos;

	for (size_t seq = 0; get_block_info(handle, seq, &info, &pos); ++seq)
	{
		//DPRINTF("[%u] len=%#x pos=%u"DCRLF,seq, info.storage_len, pos);
		if (is_empty_block_info(&info))
		{
			*loc_offset = pos;
			return true;
		}
	}

	return false;
}

static SOF_Error_t probe_active_storage_info(const SOF_BlockHandle_t handle, StorageInfo_t *storage_info)
{
	BlockInfo_t info, last_info;
	uint32_t pos;
	uint32_t storage_len_sum = 0;

	for (size_t seq = 0; get_block_info(handle, seq, &info, &pos); ++seq)
	{
		if (is_empty_block_info(&info))
		{
			if (seq == 0)
				return kSOF_ErrNoInfo;
			break;
		}
		if (!is_valid_block_info(&info))
		{
			if (storage_info->begin_offset + storage_info->len == pos)
			{
				DPRINTF("data is full: %u"DCRLF,storage_info->begin_offset + storage_info->len);
				break;
			}

			EPRINTF("invalid block at %u"DCRLF,pos);
			return kSOF_ErrBadBlock;
		}

		storage_len_sum += info.storage_len & 0x00FFFFFF;
		last_info = info;
	}

	uint32_t storage_len = last_info.storage_len & 0x00FFFFFF;

	storage_info->begin_offset = storage_len_sum - storage_len;
	storage_info->len = storage_len;
	storage_info->storage_csum = last_info.csum.for_storage;

	return kSOF_ErrNone;
}


#if LOCAL_DEBUG
static void dump_block(SOF_BlockHandle_t handle)
{
	DPRINTF("sector(%u)"DCRLF, SOF_dev_info(handle->hdev_)->sec_no);
	DPRINTF("	offset               =%u"DCRLF, handle->cur_pos_);
	DPRINTF("	writemode            =%d"DCRLF, handle->write_mode_);
	DPRINTF("	storage_max_offset   =%u"DCRLF, handle->storage_max_offset_);
	DPRINTF("	storage_begin_offset =%u"DCRLF, handle->storage_begin_offset_);
	DPRINTF("	storage_end_offset   =%u"DCRLF, handle->storage_end_offset_);
	DPRINTF("	free=%u total=%u"DCRLF,SOF_block_get_free_size(handle), handle->total_physical_block_size());
}
#endif

size_t SOF_block_get_free_size(SOF_BlockHandle_t handle)
{
	DASSERT(handle != NULL);
	if (handle->storage_end_offset_ <= handle->storage_max_offset_-RESERVED_BLOCK_INFO_SIZE)
		return (handle->storage_max_offset_- RESERVED_BLOCK_INFO_SIZE) - handle->storage_end_offset_;
	else
	{
		return 0;
	}
}

uint32_t SOF_block_storage_size(SOF_BlockHandle_t handle)
{
	DASSERT(handle != NULL);
	return handle->storage_end_offset_ - handle->storage_begin_offset_;
}

static uint16_t checksum(const uint8_t* data, int count)
{
	// Fletcher's checksum algorithm
	uint16_t sum1 = 0;
	uint16_t sum2 = 0;
	int index;

	for( index = 0; index < count; ++index )
	{
		sum1 = (sum1 + data[index]) % 255;
		sum2 = (sum2 + sum1) % 255;
	}

	return (sum2 << 8) | sum1;
}

static uint16_t compute_storage_checksum(SOF_BlockHandle_t handle)
{
	uint8_t *addr = SOF_dev_get_hw_addr(handle->hdev_);

	return checksum(addr+handle->storage_begin_offset_, SOF_block_storage_size(handle));
}

static bool write_storage_info(SOF_BlockHandle_t handle)
{
	BlockInfo_t cs;

	cs.storage_len = (SYNC_MARK_BYTE_IN_LEN << 24) | SOF_block_storage_size(handle);
	cs.csum.for_info = checksum((uint8_t*)&cs.storage_len, 4);
	cs.csum.for_storage = compute_storage_checksum(handle);

	DPRINTF("write %#x at %#x"DCRLF,*((uint32_t*)&cs.csum), handle->storage_max_offset_);
	if (SOF_dev_write_word(handle->hdev_, handle->storage_max_offset_, *((uint32_t*)&cs.csum)) < 0)
		return false;

	if (SOF_dev_write_word(handle->hdev_, handle->storage_max_offset_+4, *((uint32_t*)&cs.storage_len)) < 0)
		return false;
	
	return true;
}

static bool create_empty_storage(SOF_DevHandle_t hdev, uint8_t sector_index)
{
	SOF_BlockHandle handle_data;

	handle_data.hdev_ = hdev;

	uint32_t info_begin_offset;

	if (!get_empty_info_location(&handle_data, &info_begin_offset))
	{
		EPRINTF("no info"DCRLF);
		SOF_block_close(&handle_data);
		return false;
	}

	handle_data.storage_max_offset_ = info_begin_offset;
	handle_data.storage_begin_offset_ = 0;
	handle_data.storage_end_offset_ = handle_data.storage_begin_offset_;
	handle_data.cur_pos_ = handle_data.storage_begin_offset_;

	DPRINTF("storage created: begin=%d end=%d free=%d"DCRLF,
		handle_data.storage_begin_offset_, handle_data.storage_end_offset_, SOF_block_get_free_size(&handle_data));

	write_storage_info(&handle_data);

	return true;
}


bool SOF_block_format(uint8_t sector_index)
{
	if (!SOF_dev_is_valid_sector(sector_index))
	{
		DPRINTF("invalid sector_index=%d"DCRLF, sector_index);
		return false;
	}

	SOF_DevHandle_t hdev = SOF_dev_open(sector_index);

	if (hdev == SOF_INVALID_HANDLE)
	{
		DPRINTF("SOF_dev_open(%d) failed"DCRLF, sector_index);
		return false;
	}

	DPRINTF("Flash erase %d"DCRLF, sector_index);
	SOF_dev_erase(hdev);
	create_empty_storage(hdev, sector_index);
	SOF_dev_close(hdev);

	return true;
}

SOF_BlockHandle_t SOF_block_open_storage(uint8_t sector_index, SOF_Error_t *err)
{
	if (!SOF_dev_is_valid_sector(sector_index))
	{
		DPRINTF("invalid sector_index=%d"DCRLF, sector_index);
		return false;
	}

	SOF_DevHandle_t hdev = SOF_dev_open(sector_index);

	if (hdev == SOF_INVALID_HANDLE)
	{
		DPRINTF("SOF_dev_open(%d) failed"DCRLF, sector_index);
		return false;
	}

	SOF_BlockHandle_t handle = new SOF_BlockHandle();

	handle->hdev_ = hdev;

	StorageInfo_t storage_info;

	if ((*err=probe_active_storage_info(handle, &storage_info)) != kSOF_ErrNone)
	{
		delete handle;
		return NULL;
	}

	uint32_t info_begin_offset;

	if (!get_empty_info_location(handle, &info_begin_offset))
	{
		*err = kSOF_ErrBadBlock;
		delete handle;
		return NULL;
	}

	// set max offset that storage grows.
	handle->storage_max_offset_ = info_begin_offset;

	handle->storage_begin_offset_ = storage_info.begin_offset;
	handle->storage_end_offset_ = storage_info.begin_offset + storage_info.len;

	handle->cur_pos_ = handle->storage_begin_offset_;

	DPRINTF("open for read: begin=%d end=%d len=%d free=%d"DCRLF,
		handle->storage_begin_offset_, handle->storage_end_offset_, storage_info.len,
		SOF_block_get_free_size(handle));
	if (compute_storage_checksum(handle) != storage_info.storage_csum)
	{
		EPRINTF("checksum error %#x != %#x"DCRLF, compute_storage_checksum(handle), storage_info.storage_csum);
		*err = kSOF_ErrDataCurrupted;
		delete handle;
		return NULL;
	}

	DUMP_BLOCK(handle);
	*err = kSOF_ErrNone;

	return handle;
}

SOF_BlockHandle_t SOF_block_create_storage(uint8_t sector_index, SOF_Error_t *err)
{
	if (!SOF_dev_is_valid_sector(sector_index))
	{
		DPRINTF("invalid sector_index=%d"DCRLF, sector_index);
		return false;
	}

	SOF_DevHandle_t hdev = SOF_dev_open(sector_index);

	if (hdev == SOF_INVALID_HANDLE)
	{
		DPRINTF("SOF_dev_open(%d) failed"DCRLF, sector_index);
		return false;
	}

	SOF_BlockHandle_t handle = new SOF_BlockHandle();

	handle->hdev_ = hdev;

	StorageInfo_t storage_info;

	if ((*err=probe_active_storage_info(handle, &storage_info)) != kSOF_ErrNone)
	{
		delete handle;
		return NULL;
	}

	uint32_t info_begin_offset;

	if (!get_empty_info_location(handle, &info_begin_offset))
	{
		*err = kSOF_ErrBadBlock;
		delete handle;
		return NULL;
	}

	// set max offset that storage grows.
	handle->storage_max_offset_ = info_begin_offset;

	// writing position is just after previous storage
	handle->storage_begin_offset_ = storage_info.begin_offset + storage_info.len;
	handle->storage_end_offset_ = handle->storage_begin_offset_;

	handle->cur_pos_ = handle->storage_begin_offset_;
	handle->write_mode_ = true;
	DPRINTF("open for write: begin=%d end=%d free=%d"DCRLF,
		handle->storage_begin_offset_, handle->storage_end_offset_, SOF_block_get_free_size(handle));

	DUMP_BLOCK(handle);
	*err = kSOF_ErrNone;

	return handle;
}

bool SOF_block_close(SOF_BlockHandle_t handle)
{
	bool r = true;

	DASSERT(handle != NULL);
	if (handle->write_mode_)
		r = (bool)write_storage_info(handle);
	SOF_dev_close(handle->hdev_);
	delete handle;

	return r;
}

uint8_t *SOF_block_base_addr(SOF_BlockHandle_t handle)
{
	DASSERT(handle != NULL);
	return SOF_dev_get_hw_addr(handle->hdev_) + handle->cur_pos_;
}

bool SOF_block_putc(SOF_BlockHandle_t handle, uint8_t c)
{
	DASSERT(handle != NULL);
	DASSERT(handle->is_writable());

	if (SOF_block_get_free_size(handle) == 0)
	{
		DPRINTF("no free space"DCRLF);
		DUMP_BLOCK(handle);

		return false;
	}

	bool b = SOF_dev_write_byte(handle->hdev_, handle->cur_pos_, c) != -1;
	if (b)
	{
		handle->cur_pos_++;
		handle->storage_end_offset_++;
	}

	return b;
}

size_t SOF_block_write(SOF_BlockHandle_t handle, const uint8_t *p, size_t p_size)
{
	size_t i;

	for (i = 0; i < p_size; ++i)
		if (SOF_block_putc(handle, *p++) != true)
			return i;

	return i;
}

bool SOF_block_getc(SOF_BlockHandle_t handle, uint8_t *c)
{
	DASSERT(handle != NULL);
	DASSERT(handle->is_writable());

	if (handle->cur_pos_ >= handle->storage_end_offset_)
	{
		DPRINTF("end of data\n"DCRLF);
		DUMP_BLOCK(handle);

		return false;
	}

	*c = SOF_dev_read_byte(handle->hdev_, handle->cur_pos_++);

	return true;
}

size_t SOF_block_read(SOF_BlockHandle_t handle, uint8_t *p, size_t p_size)
{
	size_t i;

	for (i = 0; i < p_size; ++i)
		if (!SOF_block_getc(handle, p++))
			break;

	return i;
}

SOF_Error_t SOF_block_get_statics(uint8_t sector_index, SOF_Statics_t *stat)
{
	if (!SOF_dev_is_valid_sector(sector_index))
	{
		DPRINTF("invalid sector_index=%d"DCRLF, sector_index);
		return kSOF_ErrParam;
	}

	SOF_Error_t err;
	SOF_BlockHandle_t hblk = SOF_block_open_storage(sector_index, &err);

	if (hblk == NULL)
	{
		DPRINTF("SOF_block_open_storage(%d) failed"DCRLF, sector_index);
		return err;
	}

	stat->data_addr = SOF_block_base_addr(hblk);
	stat->data_size = SOF_block_storage_size(hblk);
	stat->free_size = SOF_block_get_free_size(hblk);

	SOF_block_close(hblk);

	return kSOF_ErrNone;
}

const SOF_SectorSpec_t *SOF_block_get_info(uint8_t sector_index)
{
	if (!SOF_dev_is_valid_sector(sector_index))
	{
		DPRINTF("invalid sector_index=%d"DCRLF, sector_index);
		return NULL;
	}

	return SOF_dev_info_by_index(sector_index);
}