blob: e876dec9eaed195c89f219569c27ef21bf1ab041 [file] [log] [blame]
/*
*
* Copyright (c) 2022 Project CHIP Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* Utilities for interacting with the the Open IoT SDK key-value storage.
*/
#include <string.h>
#include "FlashBlockDevice.h"
#include <OpenIoTSDKPort.h>
extern "C" {
#include <platform_base_address.h>
}
#define FLASH_BASE_ADDRESS (SRAM_BASE_NS) /* 0x01000000 */
#define FLASH_TOTAL_SIZE (SRAM_SIZE) /* 2 MB */
#define FLASH_AREA_IMAGE_SECTOR_SIZE (0x1000) /* 4 KB */
#define FLASH_PAGE_SIZE 256
#define FLASH_END_ADDRESS (FLASH_BASE_ADDRESS + FLASH_TOTAL_SIZE)
#define FLASH_READ_SIZE 1
#define FLASH_ERASE_VALUE 0xFFU
namespace iotsdk {
namespace storage {
FlashBlockDevice::FlashBlockDevice(uint32_t address, uint32_t size) : _base(address), _size(size), _is_initialized(false) {}
FlashBlockDevice::~FlashBlockDevice() {}
bd_status FlashBlockDevice::init()
{
if (!_base)
{
_base = FLASH_BASE_ADDRESS;
}
if (!_size)
{
_size = FLASH_TOTAL_SIZE - (_base - FLASH_BASE_ADDRESS);
}
if (_size + _base > FLASH_TOTAL_SIZE + FLASH_BASE_ADDRESS)
{
return bd_status::INCORRECT_SIZE;
}
if (_base < FLASH_BASE_ADDRESS)
{
return bd_status::INCORRECT_ADDRESS;
}
_is_initialized = true;
return bd_status::OK;
}
bd_status FlashBlockDevice::deinit()
{
if (!_is_initialized)
{
return bd_status::OK;
}
_is_initialized = false;
return bd_status::OK;
}
bd_status FlashBlockDevice::read(void * buffer, bd_addr_t virtual_address, bd_size_t size)
{
if (!_is_initialized)
{
return bd_status::DEVICE_NOT_INITIALIZED;
}
/* Check that the address and size are properly aligned and fit. */
bd_status status = is_valid_read(virtual_address, size);
if (status != bd_status::OK)
{
return status;
}
/* Convert virtual address to the physical address for the device. */
const auto physical_address = static_cast<uint32_t>(_base + virtual_address);
/* Read data */
memcpy(static_cast<uint8_t *>(buffer), (void *) physical_address, static_cast<uint32_t>(size));
return bd_status::OK;
}
bd_status FlashBlockDevice::program(const void * buffer, bd_addr_t virtual_address, bd_size_t size)
{
if (!_is_initialized)
{
return bd_status::DEVICE_NOT_INITIALIZED;
}
/* Check that the address and size are properly aligned and fit. */
bd_status status = is_valid_program(virtual_address, size);
if (status != bd_status::OK)
{
return status;
}
/* Convert virtual address to the physical address for the device. */
auto physical_address = static_cast<uint32_t>(_base + virtual_address);
auto remaining_size = static_cast<uint32_t>(size);
const auto * buf = static_cast<const uint8_t *>(buffer);
while (remaining_size > 0)
{
/* Multiple pages can be programmed at once but cannot cross sector boundaries */
const auto sector_size = FLASH_AREA_IMAGE_SECTOR_SIZE;
const auto sector_end = (physical_address / sector_size + 1) * sector_size;
const auto chunk = (physical_address + remaining_size > sector_end) ? (sector_end - physical_address) : remaining_size;
/* Write data */
memcpy((void *) physical_address, buf, chunk);
physical_address += chunk;
remaining_size -= chunk;
buf += chunk;
}
return bd_status::OK;
}
bd_status FlashBlockDevice::erase(bd_addr_t virtual_address, bd_size_t size)
{
if (!_is_initialized)
{
return bd_status::DEVICE_NOT_INITIALIZED;
}
/* Check that the address and size are properly aligned and fit. */
const bd_status status = is_valid_erase(virtual_address, size);
if (status != bd_status::OK)
{
return status;
}
/* Convert virtual address to the physical address for the device. */
auto physical_address = static_cast<uint32_t>(_base + virtual_address);
const auto sector_size = FLASH_AREA_IMAGE_SECTOR_SIZE;
const auto erase_region = (physical_address + size) - sector_size;
while (physical_address <= erase_region)
{
memset((void *) physical_address, FLASH_ERASE_VALUE, FLASH_AREA_IMAGE_SECTOR_SIZE);
physical_address += sector_size;
}
return bd_status::OK;
}
bd_size_t FlashBlockDevice::get_read_size() const
{
return FLASH_READ_SIZE;
}
bd_size_t FlashBlockDevice::get_program_size() const
{
if (!_is_initialized)
{
return 0;
}
return FLASH_PAGE_SIZE;
}
bd_size_t FlashBlockDevice::get_erase_size() const
{
return 0;
}
bd_size_t FlashBlockDevice::get_erase_size(bd_addr_t addr) const
{
if (!_is_initialized)
{
return 0;
}
if (static_cast<uint64_t>(_base) + addr > UINT32_MAX)
{
return 0;
}
return FLASH_AREA_IMAGE_SECTOR_SIZE;
}
int FlashBlockDevice::get_erase_value() const
{
if (!_is_initialized)
{
return -1;
}
return FLASH_ERASE_VALUE;
}
bd_size_t FlashBlockDevice::size() const
{
return _size;
}
const char * FlashBlockDevice::get_type() const
{
return "FLASH_BD";
}
bd_status FlashBlockDevice::is_valid_read(bd_addr_t addr, bd_size_t size) const
{
if (static_cast<uint64_t>(_base) + addr > UINT32_MAX)
{
return bd_status::INCORRECT_ADDRESS;
}
if (size > UINT32_MAX)
{
return bd_status::INCORRECT_SIZE;
}
return BlockDevice::is_valid_read(addr, size);
}
bd_status FlashBlockDevice::is_valid_program(bd_addr_t addr, bd_size_t size) const
{
if (static_cast<uint64_t>(_base) + addr > UINT32_MAX)
{
return bd_status::INCORRECT_ADDRESS;
}
if (size > UINT32_MAX)
{
return bd_status::INCORRECT_SIZE;
}
return BlockDevice::is_valid_program(addr, size);
}
bd_status FlashBlockDevice::is_valid_erase(bd_addr_t addr, bd_size_t size) const
{
if (static_cast<uint64_t>(_base) + addr > UINT32_MAX)
{
return bd_status::INCORRECT_ADDRESS;
}
if (size > UINT32_MAX)
{
return bd_status::INCORRECT_SIZE;
}
return BlockDevice::is_valid_erase(addr, size);
}
} // namespace storage
} // namespace iotsdk
namespace chip {
namespace DeviceLayer {
namespace Internal {
static iotsdk::storage::FlashBlockDevice gBlockDevice(0, 0);
iotsdk::storage::BlockDevice * GetBlockDevice()
{
return &gBlockDevice;
}
} // namespace Internal
} // namespace DeviceLayer
} // namespace chip