Create block device for working around a reserved region

Given an underlying block device and the size of the reserved region, a
CyReservedRegionBlockDevice will act as the underlying block device but
operating only outside of the reserved region. It also allows reading
from the reserved region. The reserved region is assumed to start at
address 0 in the underlying block device.
pull/14063/head
Matthew Macovsky 2020-08-13 10:12:19 -07:00 committed by Dustin Crossman
parent 71d6f8446c
commit 95a85a52e8
2 changed files with 304 additions and 0 deletions

View File

@ -0,0 +1,120 @@
/***************************************************************************//**
* \file CyReservedRegionBlockDevice.cpp
*
* \brief
* Block device for working via an underlying block device without altering
* a reserved region.
*
********************************************************************************
* \copyright
* Copyright 2020 Cypress Semiconductor Corporation
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*******************************************************************************/
#include "CyReservedRegionBlockDevice.h"
CyReservedRegionBlockDevice::CyReservedRegionBlockDevice(BlockDevice *underlying_bd, mbed::bd_size_t reserved_region_size)
{
_underlying_bd = underlying_bd;
_reserved_region_size = reserved_region_size;
_reserved_region_end = 0;
}
int CyReservedRegionBlockDevice::init()
{
int status = _underlying_bd->init();
if (status == BD_ERROR_OK)
{
// Round up to start usable region on an erase boundary
// May need to wait until after init() to determine erase size (e.g. QSPI)
_reserved_region_end = _reserved_region_size + get_erase_size() - (_reserved_region_size % get_erase_size());
}
return status;
}
int CyReservedRegionBlockDevice::deinit()
{
int status = _underlying_bd->deinit();
if (status == BD_ERROR_OK)
{
_reserved_region_end = 0;
}
return status;
}
int CyReservedRegionBlockDevice::read(void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size)
{
return _underlying_bd->read(buffer, addr + _reserved_region_end, size);
}
int CyReservedRegionBlockDevice::program(const void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size)
{
return _underlying_bd->program(buffer, addr + _reserved_region_end, size);
}
int CyReservedRegionBlockDevice::erase(mbed::bd_addr_t addr, mbed::bd_size_t size)
{
return _underlying_bd->erase(addr + _reserved_region_end, size);
}
mbed::bd_size_t CyReservedRegionBlockDevice::get_read_size() const
{
return _underlying_bd->get_read_size();
}
mbed::bd_size_t CyReservedRegionBlockDevice::get_program_size() const
{
return _underlying_bd->get_program_size();
}
mbed::bd_size_t CyReservedRegionBlockDevice::get_erase_size() const
{
return _underlying_bd->get_erase_size();
}
mbed::bd_size_t CyReservedRegionBlockDevice::get_erase_size(mbed::bd_addr_t addr) const
{
return _underlying_bd->get_erase_size(addr + _reserved_region_end);
}
int CyReservedRegionBlockDevice::get_erase_value() const
{
return _underlying_bd->get_erase_value();
}
mbed::bd_size_t CyReservedRegionBlockDevice::size() const
{
return _underlying_bd->size() - _reserved_region_end;
}
const char *CyReservedRegionBlockDevice::get_type() const
{
return _underlying_bd->get_type();
}
int CyReservedRegionBlockDevice::reserved_read(void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size)
{
if (addr + size > _reserved_region_end || addr % get_read_size() != 0 || size % get_read_size() != 0)
{
return BD_ERROR_DEVICE_ERROR;
}
return _underlying_bd->read(buffer, addr, size);
}
mbed::bd_addr_t CyReservedRegionBlockDevice::reserved_region_end() const
{
return _reserved_region_end;
}

View File

@ -0,0 +1,184 @@
/***************************************************************************//**
* \file CyReservedRegionBlockDevice.h
*
* \brief
* Block device for working via an underlying block device without altering
* a reserved region.
*
********************************************************************************
* \copyright
* Copyright 2020 Cypress Semiconductor Corporation
* SPDX-License-Identifier: Apache-2.0
*
* 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.
*******************************************************************************/
#ifndef CY_RESERVED_REGION_BLOCK_DEVICE_H
#define CY_RESERVED_REGION_BLOCK_DEVICE_H
#include "mbed.h"
/**
* Block device for working via an underlying block device without altering
* a reserved region.
*/
class CyReservedRegionBlockDevice : public mbed::BlockDevice {
public:
/**
* Create a CyReservedRegionBlockDevice. The reserved region will be created at the beginning of the external storage.
*
* @param[in] underlying_bd The underlying block device to use
* @param[in] reserved_region_size Size of the reserved region (will be rounded up to the nearest erase size).
*/
CyReservedRegionBlockDevice(BlockDevice *underlying_bd, mbed::bd_size_t reserved_region_size);
/**
* Initialize the block device.
*
* @return 0 on success. A nonzero error code from the underlying block device on failure.
*/
virtual int init() override;
/**
* Deinitialize the block device.
*
* @return 0 on success. A nonzero error code from the underlying block device on failure.
*/
virtual int deinit() override;
/**
* Destruct the block device.
*
* @return 0 on success. A nonzero error code from the underlying block device on failure.
*/
virtual ~CyReservedRegionBlockDevice() override
{
deinit();
}
/**
* Read blocks from the block device.
*
* @param[out] buffer Buffer to write blocks to
* @param[in] addr Address of block to begin reading from
* @param[in] size Size to read in bytes, must be a multiple of read block size
* @return 0 on success. A nonzero error code from the underlying block device on failure.
*/
virtual int read(void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size) override;
/**
* Program blocks to the block device.
*
* The blocks must have been erased prior to being programmed.
*
* @param[in] buffer Buffer of data to write to blocks
* @param[in] addr Address of block to begin writing to
* @param[in] size Size to write in bytes, must be a multiple of program block size
* @return 0 on success. A nonzero error code from the underlying block device on failure.
*/
virtual int program(const void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size) override;
/**
* Erase blocks on a block device.
*
* The state of an erased block is undefined until it has been programmed.
*
* @param[in] addr Address of block to begin erasing
* @param[in] size Size to erase in bytes, must be a multiple of erase block size
* @return 0 on success. A nonzero error code from the underlying block device on failure.
*/
virtual int erase(mbed::bd_addr_t addr, mbed::bd_size_t size) override;
/**
* Get the size of a readable block.
*
* @return Size of a readable block in bytes
*/
virtual mbed::bd_size_t get_read_size() const override;
/**
* Get the size of a programmable block.
*
* @return Size of a program block size in bytes
* @note Must be a multiple of the read size.
*/
virtual mbed::bd_size_t get_program_size() const override;
/**
* Get the size of an erasable block.
*
* @return Size of a minimal erase block, common to all regions, in bytes
* @note Must be a multiple of the program size.
*/
virtual mbed::bd_size_t get_erase_size() const override;
/**
* Get the minimal erasable sector size of given address.
*
* @param[in] addr Any address within block queried for erase sector size (can be any address within flash size offset)
* @return Size of minimal erase sector size, in given address region, in bytes
* @note Must be a multiple of the program size.
*/
virtual mbed::bd_size_t get_erase_size(mbed::bd_addr_t addr) const override;
/**
* Get the value of storage byte after it was erased.
*
* If get_erase_value returns a non-negative byte value, the underlying
* storage is set to that value when erased, and storage containing
* that value can be programmed without another erase.
*
* @return The value of storage when erased, or -1 if you can't
* rely on the value of erased storage
*/
virtual int get_erase_value() const override;
/**
* Get the total size of the region of the underlying device available for use.
*
* @return Size of usable region in bytes
*/
virtual mbed::bd_size_t size() const override;
/**
* Get the BlockDevice class type.
*
* @return A string represent the BlockDevice class type.
*/
virtual const char *get_type() const override;
/**
* Read from the reserved region.
*
* @param[out] buffer Buffer to read data into
* @param[in] addr Address to start read from
* @param[in] size Size to read in bytes, must be a multiple of the read block size
* @return 0 on success. A nonzero error code from the underlying block device on failure.
*/
int reserved_read(void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size);
/**
* Get the end address of the reserved region.
*
* @return The end address of the reserved region
*/
mbed::bd_addr_t reserved_region_end() const;
private:
BlockDevice *_underlying_bd;
mbed::bd_size_t _reserved_region_size; // Initial size passed in; used in init() to compute _reserved_region_end
mbed::bd_addr_t _reserved_region_end; // Actual used reserved region end; rounded up to nearest erase size
};
#endif /* CY_RESERVED_REGION_BLOCK_DEVICE_H */