mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #7774 from yossi2le/sd-spif-to-mbed-os
Add default block device support (SD, SPIF and FLASHIAP)pull/7951/head
commit
993c897b55
|
@ -187,12 +187,11 @@ matrix:
|
|||
|
||||
- env:
|
||||
- NAME=littlefs
|
||||
- LITTLEFS=features/filesystem/littlefs
|
||||
- LITTLEFS=features/storage/filesystem/littlefs
|
||||
install:
|
||||
# Install dependencies
|
||||
- sudo apt-get install gcc-arm-embedded fuse libfuse-dev
|
||||
- pip install -r requirements.txt
|
||||
- git clone https://github.com/armmbed/spiflash-driver.git
|
||||
# Print versions
|
||||
- arm-none-eabi-gcc --version
|
||||
- gcc --version
|
||||
|
|
|
@ -0,0 +1,797 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 "DataFlashBlockDevice.h"
|
||||
#include "mbed_critical.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
/* constants */
|
||||
#define DATAFLASH_READ_SIZE 1
|
||||
#define DATAFLASH_PROG_SIZE 1
|
||||
#define DATAFLASH_TIMEOUT 10000
|
||||
#define DATAFLASH_ID_MATCH 0x1F20
|
||||
#define DATAFLASH_ID_DENSITY_MASK 0x001F
|
||||
#define DATAFLASH_PAGE_SIZE_256 0x0100
|
||||
#define DATAFLASH_PAGE_SIZE_264 0x0108
|
||||
#define DATAFLASH_PAGE_SIZE_512 0x0200
|
||||
#define DATAFLASH_PAGE_SIZE_528 0x0210
|
||||
#define DATAFLASH_BLOCK_SIZE_2K 0x0800
|
||||
#define DATAFLASH_BLOCK_SIZE_2K1 0x0840
|
||||
#define DATAFLASH_BLOCK_SIZE_4K 0x1000
|
||||
#define DATAFLASH_BLOCK_SIZE_4K1 0x1080
|
||||
#define DATAFLASH_PAGE_BIT_264 9
|
||||
#define DATAFLASH_PAGE_BIT_528 10
|
||||
|
||||
/* enable debug */
|
||||
#ifndef DATAFLASH_DEBUG
|
||||
#define DATAFLASH_DEBUG 0
|
||||
#endif /* DATAFLASH_DEBUG */
|
||||
|
||||
#if DATAFLASH_DEBUG
|
||||
#define DEBUG_PRINTF(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PRINTF(...)
|
||||
#endif
|
||||
|
||||
void _print_status(uint16_t status);
|
||||
|
||||
/* non-exhaustive opcode list */
|
||||
enum opcode {
|
||||
DATAFLASH_OP_NOP = 0x00,
|
||||
DATAFLASH_OP_STATUS = 0xD7,
|
||||
DATAFLASH_OP_ID = 0x9F,
|
||||
DATAFLASH_OP_READ_LOW_POWER = 0x01,
|
||||
DATAFLASH_OP_READ_LOW_FREQUENCY = 0x03,
|
||||
DATAFLASH_OP_PROGRAM_DIRECT = 0x02, // Program through Buffer 1 without Built-In Erase
|
||||
DATAFLASH_OP_PROGRAM_DIRECT_WITH_ERASE = 0x82,
|
||||
DATAFLASH_OP_ERASE_BLOCK = 0x50,
|
||||
};
|
||||
|
||||
/* non-exhaustive command list */
|
||||
enum command {
|
||||
DATAFLASH_COMMAND_WRITE_DISABLE = 0x3D2A7FA9,
|
||||
DATAFLASH_COMMAND_WRITE_ENABLE = 0x3D2A7F9A,
|
||||
DATAFLASH_COMMAND_BINARY_PAGE_SIZE = 0x3D2A80A6,
|
||||
DATAFLASH_COMMAND_DATAFLASH_PAGE_SIZE = 0x3D2A80A7,
|
||||
};
|
||||
|
||||
/* bit masks for interpreting the status register */
|
||||
enum status_bit {
|
||||
DATAFLASH_BIT_READY = (0x01 << 15),
|
||||
DATAFLASH_BIT_COMPARE = (0x01 << 14),
|
||||
DATAFLASH_BIT_DENSITY = (0x0F << 10),
|
||||
DATAFLASH_BIT_PROTECT = (0x01 << 9),
|
||||
DATAFLASH_BIT_PAGE_SIZE = (0x01 << 8),
|
||||
|
||||
DATAFLASH_BIT_ERASE_PROGRAM_ERROR = (0x01 << 5),
|
||||
DATAFLASH_BIT_SECTOR_LOCKDOWN = (0x01 << 3),
|
||||
DATAFLASH_BIT_PROGRAM_SUSPEND_2 = (0x01 << 2),
|
||||
DATAFLASH_BIT_PROGRAM_SUSPEND_1 = (0x01 << 1),
|
||||
DATAFLASH_BIT_ERASE_SUSPEND = (0x01 << 0),
|
||||
};
|
||||
|
||||
/* bit masks for detecting density from status register */
|
||||
enum status_density {
|
||||
DATAFLASH_STATUS_DENSITY_2_MBIT = (0x05 << 10),
|
||||
DATAFLASH_STATUS_DENSITY_4_MBIT = (0x07 << 10),
|
||||
DATAFLASH_STATUS_DENSITY_8_MBIT = (0x09 << 10),
|
||||
DATAFLASH_STATUS_DENSITY_16_MBIT = (0x0B << 10),
|
||||
DATAFLASH_STATUS_DENSITY_32_MBIT = (0x0D << 10),
|
||||
DATAFLASH_STATUS_DENSITY_64_MBIT = (0x0F << 10),
|
||||
};
|
||||
|
||||
/* code for calculating density */
|
||||
enum id_density {
|
||||
DATAFLASH_ID_DENSITY_2_MBIT = 0x03,
|
||||
DATAFLASH_ID_DENSITY_4_MBIT = 0x04,
|
||||
DATAFLASH_ID_DENSITY_8_MBIT = 0x05,
|
||||
DATAFLASH_ID_DENSITY_16_MBIT = 0x06,
|
||||
DATAFLASH_ID_DENSITY_32_MBIT = 0x07,
|
||||
DATAFLASH_ID_DENSITY_64_MBIT = 0x08,
|
||||
};
|
||||
|
||||
/* typical duration in milliseconds for each operation */
|
||||
enum timing {
|
||||
DATAFLASH_TIMING_ERASE_PROGRAM_PAGE = 17,
|
||||
DATAFLASH_TIMING_PROGRAM_PAGE = 3,
|
||||
DATAFLASH_TIMING_ERASE_PAGE = 12,
|
||||
DATAFLASH_TIMING_ERASE_BLOCK = 45,
|
||||
DATAFLASH_TIMING_ERASE_SECTOR = 700,
|
||||
DATAFLASH_TIMING_ERASE_CHIP = 45000
|
||||
};
|
||||
|
||||
/* frequency domains */
|
||||
enum frequency {
|
||||
DATAFLASH_LOW_POWER_FREQUENCY = 15000000,
|
||||
DATAFLASH_LOW_FREQUENCY = 50000000,
|
||||
DATAFLASH_HIGH_FREQUENCY = 85000000,
|
||||
DATAFLASH_HIGHEST_FREQUENCY = 104000000
|
||||
};
|
||||
|
||||
/* number of dummy bytes required in each frequency domain */
|
||||
enum dummy {
|
||||
DATAFLASH_LOW_POWER_BYTES = 0,
|
||||
DATAFLASH_LOW_FREQUENCY_BYTES = 0,
|
||||
DATAFLASH_HIGH_FREQUENCY_BYTES = 1,
|
||||
DATAFLASH_HIGHEST_FREQUENCY_BYTES = 2
|
||||
};
|
||||
|
||||
DataFlashBlockDevice::DataFlashBlockDevice(PinName mosi,
|
||||
PinName miso,
|
||||
PinName sclk,
|
||||
PinName cs,
|
||||
int freq,
|
||||
PinName nwp)
|
||||
: _spi(mosi, miso, sclk),
|
||||
_cs(cs, 1),
|
||||
_nwp(nwp),
|
||||
_device_size(0),
|
||||
_page_size(0),
|
||||
_block_size(0),
|
||||
_is_initialized(0),
|
||||
_init_ref_count(0)
|
||||
{
|
||||
/* check that frequency is within range */
|
||||
if (freq > DATAFLASH_LOW_FREQUENCY) {
|
||||
|
||||
/* cap frequency at the highest supported one */
|
||||
_spi.frequency(DATAFLASH_LOW_FREQUENCY);
|
||||
|
||||
} else {
|
||||
/* freqency is valid, use as-is */
|
||||
_spi.frequency(freq);
|
||||
}
|
||||
|
||||
/* write protect chip if pin is connected */
|
||||
if (nwp != NC) {
|
||||
_nwp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int DataFlashBlockDevice::init()
|
||||
{
|
||||
DEBUG_PRINTF("init\r\n");
|
||||
|
||||
if (!_is_initialized) {
|
||||
_init_ref_count = 0;
|
||||
}
|
||||
|
||||
uint32_t val = core_util_atomic_incr_u32(&_init_ref_count, 1);
|
||||
|
||||
if (val != 1) {
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
int result = BD_ERROR_DEVICE_ERROR;
|
||||
|
||||
/* read ID register to validate model and set dimensions */
|
||||
uint16_t id = _get_register(DATAFLASH_OP_ID);
|
||||
|
||||
DEBUG_PRINTF("id: %04X\r\n", id & DATAFLASH_ID_MATCH);
|
||||
|
||||
/* get status register to verify the page size mode */
|
||||
uint16_t status = _get_register(DATAFLASH_OP_STATUS);
|
||||
|
||||
/* manufacture ID match */
|
||||
if ((id & DATAFLASH_ID_MATCH) == DATAFLASH_ID_MATCH) {
|
||||
|
||||
/* calculate density */
|
||||
_device_size = 0x8000 << (id & DATAFLASH_ID_DENSITY_MASK);
|
||||
|
||||
bool binary_page_size = true;
|
||||
|
||||
/* check if device is configured for binary page sizes */
|
||||
if ((status & DATAFLASH_BIT_PAGE_SIZE) == DATAFLASH_BIT_PAGE_SIZE) {
|
||||
DEBUG_PRINTF("Page size is binary\r\n");
|
||||
|
||||
#if MBED_CONF_DATAFLASH_DATAFLASH_SIZE
|
||||
/* send reconfiguration command */
|
||||
_write_command(DATAFLASH_COMMAND_DATAFLASH_PAGE_SIZE, NULL, 0);
|
||||
|
||||
/* wait for device to be ready and update return code */
|
||||
result = _sync();
|
||||
|
||||
/* set binary flag */
|
||||
binary_page_size = false;
|
||||
#else
|
||||
/* set binary flag */
|
||||
binary_page_size = true;
|
||||
#endif
|
||||
} else {
|
||||
DEBUG_PRINTF("Page size is not binary\r\n");
|
||||
|
||||
#if MBED_CONF_DATAFLASH_BINARY_SIZE
|
||||
/* send reconfiguration command */
|
||||
_write_command(DATAFLASH_COMMAND_BINARY_PAGE_SIZE, NULL, 0);
|
||||
|
||||
/* wait for device to be ready and update return code */
|
||||
result = _sync();
|
||||
|
||||
/* set binary flag */
|
||||
binary_page_size = true;
|
||||
#else
|
||||
/* set binary flag */
|
||||
binary_page_size = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* set page program size and block erase size */
|
||||
switch (id & DATAFLASH_ID_DENSITY_MASK) {
|
||||
case DATAFLASH_ID_DENSITY_2_MBIT:
|
||||
case DATAFLASH_ID_DENSITY_4_MBIT:
|
||||
case DATAFLASH_ID_DENSITY_8_MBIT:
|
||||
case DATAFLASH_ID_DENSITY_64_MBIT:
|
||||
if (binary_page_size) {
|
||||
_page_size = DATAFLASH_PAGE_SIZE_256;
|
||||
_block_size = DATAFLASH_BLOCK_SIZE_2K;
|
||||
} else {
|
||||
_page_size = DATAFLASH_PAGE_SIZE_264;
|
||||
_block_size = DATAFLASH_BLOCK_SIZE_2K1;
|
||||
|
||||
/* adjust device size */
|
||||
_device_size = (_device_size / DATAFLASH_PAGE_SIZE_256) *
|
||||
DATAFLASH_PAGE_SIZE_264;
|
||||
}
|
||||
break;
|
||||
case DATAFLASH_ID_DENSITY_16_MBIT:
|
||||
case DATAFLASH_ID_DENSITY_32_MBIT:
|
||||
if (binary_page_size) {
|
||||
_page_size = DATAFLASH_PAGE_SIZE_512;
|
||||
_block_size = DATAFLASH_BLOCK_SIZE_4K;
|
||||
} else {
|
||||
_page_size = DATAFLASH_PAGE_SIZE_528;
|
||||
_block_size = DATAFLASH_BLOCK_SIZE_4K1;
|
||||
|
||||
/* adjust device size */
|
||||
_device_size = (_device_size / DATAFLASH_PAGE_SIZE_512) *
|
||||
DATAFLASH_PAGE_SIZE_528;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
DEBUG_PRINTF("density: %" PRIu16 "\r\n", id & DATAFLASH_ID_DENSITY_MASK);
|
||||
DEBUG_PRINTF("size: %" PRIu32 "\r\n", _device_size);
|
||||
DEBUG_PRINTF("page: %" PRIu16 "\r\n", _page_size);
|
||||
DEBUG_PRINTF("block: %" PRIu16 "\r\n", _block_size);
|
||||
|
||||
/* device successfully detected, set OK error code */
|
||||
result = BD_ERROR_OK;
|
||||
}
|
||||
|
||||
/* write protect device when idle */
|
||||
_write_enable(false);
|
||||
|
||||
if (result == BD_ERROR_OK) {
|
||||
_is_initialized = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int DataFlashBlockDevice::deinit()
|
||||
{
|
||||
DEBUG_PRINTF("deinit\r\n");
|
||||
|
||||
if (!_is_initialized) {
|
||||
_init_ref_count = 0;
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
uint32_t val = core_util_atomic_decr_u32(&_init_ref_count, 1);
|
||||
|
||||
if (val) {
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
_is_initialized = false;
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
int DataFlashBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
DEBUG_PRINTF("read: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size);
|
||||
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
int result = BD_ERROR_DEVICE_ERROR;
|
||||
|
||||
/* check parameters are valid and the read is within bounds */
|
||||
if (is_valid_read(addr, size) && buffer) {
|
||||
|
||||
uint8_t *external_buffer = static_cast<uint8_t *>(buffer);
|
||||
|
||||
/* activate device */
|
||||
_cs = 0;
|
||||
|
||||
/* send read opcode */
|
||||
_spi.write(DATAFLASH_OP_READ_LOW_FREQUENCY);
|
||||
|
||||
/* translate address */
|
||||
uint32_t address = _translate_address(addr);
|
||||
|
||||
DEBUG_PRINTF("address: %" PRIX32 "\r\n", address);
|
||||
|
||||
/* send read address */
|
||||
_spi.write((address >> 16) & 0xFF);
|
||||
_spi.write((address >> 8) & 0xFF);
|
||||
_spi.write(address & 0xFF);
|
||||
|
||||
/* clock out one byte at a time and store in external buffer */
|
||||
for (uint32_t index = 0; index < size; index++) {
|
||||
external_buffer[index] = _spi.write(DATAFLASH_OP_NOP);
|
||||
}
|
||||
|
||||
/* deactivate device */
|
||||
_cs = 1;
|
||||
|
||||
result = BD_ERROR_OK;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int DataFlashBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
DEBUG_PRINTF("program: %p %" PRIX64 " %" PRIX64 "\r\n", buffer, addr, size);
|
||||
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
int result = BD_ERROR_DEVICE_ERROR;
|
||||
|
||||
/* check parameters are valid and the write is within bounds */
|
||||
if (is_valid_program(addr, size) && buffer) {
|
||||
|
||||
const uint8_t *external_buffer = static_cast<const uint8_t *>(buffer);
|
||||
|
||||
/* Each write command can only cover one page at a time.
|
||||
Find page and current page offset for handling unaligned writes.
|
||||
*/
|
||||
uint32_t page_number = addr / _page_size;
|
||||
uint32_t page_offset = addr % _page_size;
|
||||
|
||||
/* disable write protection */
|
||||
_write_enable(true);
|
||||
|
||||
/* continue until all bytes have been written */
|
||||
uint32_t bytes_written = 0;
|
||||
while (bytes_written < size) {
|
||||
|
||||
/* find remaining bytes to be written */
|
||||
uint32_t bytes_remaining = size - bytes_written;
|
||||
|
||||
/* cap the value at the page size and offset */
|
||||
if (bytes_remaining > (_page_size - page_offset)) {
|
||||
bytes_remaining = _page_size - page_offset;
|
||||
}
|
||||
|
||||
/* Write one page, bytes_written keeps track of the progress,
|
||||
page_number is the page address, and page_offset is non-zero for
|
||||
unaligned writes.
|
||||
*/
|
||||
result = _write_page(&external_buffer[bytes_written],
|
||||
page_number,
|
||||
page_offset,
|
||||
bytes_remaining);
|
||||
|
||||
/* update loop variables upon success otherwise break loop */
|
||||
if (result == BD_ERROR_OK) {
|
||||
bytes_written += bytes_remaining;
|
||||
page_number++;
|
||||
|
||||
/* After the first successful write,
|
||||
all subsequent writes will be aligned.
|
||||
*/
|
||||
page_offset = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* enable write protection */
|
||||
_write_enable(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int DataFlashBlockDevice::erase(bd_addr_t addr, bd_size_t size)
|
||||
{
|
||||
DEBUG_PRINTF("erase: %" PRIX64 " %" PRIX64 "\r\n", addr, size);
|
||||
|
||||
if (!_is_initialized) {
|
||||
return BD_ERROR_DEVICE_ERROR;
|
||||
}
|
||||
|
||||
int result = BD_ERROR_DEVICE_ERROR;
|
||||
|
||||
/* check parameters are valid and the erase is within bounds */
|
||||
if (is_valid_erase(addr, size)) {
|
||||
|
||||
/* disable write protection */
|
||||
_write_enable(true);
|
||||
|
||||
/* erase one block at a time until the full size has been erased */
|
||||
uint32_t erased = 0;
|
||||
while (erased < size) {
|
||||
|
||||
/* set block erase opcode */
|
||||
uint32_t command = DATAFLASH_OP_ERASE_BLOCK;
|
||||
|
||||
/* translate address */
|
||||
uint32_t address = _translate_address(addr);
|
||||
|
||||
/* set block address */
|
||||
command = (command << 8) | ((address >> 16) & 0xFF);
|
||||
command = (command << 8) | ((address >> 8) & 0xFF);
|
||||
command = (command << 8) | (address & 0xFF);
|
||||
|
||||
/* send command to device */
|
||||
_write_command(command, NULL, 0);
|
||||
|
||||
/* wait until device is ready and update return value */
|
||||
result = _sync();
|
||||
|
||||
/* if erase failed, break loop */
|
||||
if (result != BD_ERROR_OK) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* update loop variables */
|
||||
addr += _block_size;
|
||||
erased += _block_size;
|
||||
}
|
||||
|
||||
/* enable write protection */
|
||||
_write_enable(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bd_size_t DataFlashBlockDevice::get_read_size() const
|
||||
{
|
||||
DEBUG_PRINTF("read size: %d\r\n", DATAFLASH_READ_SIZE);
|
||||
|
||||
return DATAFLASH_READ_SIZE;
|
||||
}
|
||||
|
||||
bd_size_t DataFlashBlockDevice::get_program_size() const
|
||||
{
|
||||
DEBUG_PRINTF("program size: %d\r\n", DATAFLASH_PROG_SIZE);
|
||||
|
||||
return DATAFLASH_PROG_SIZE;
|
||||
}
|
||||
|
||||
bd_size_t DataFlashBlockDevice::get_erase_size() const
|
||||
{
|
||||
DEBUG_PRINTF("erase size: %" PRIX16 "\r\n", _block_size);
|
||||
|
||||
return _block_size;
|
||||
}
|
||||
|
||||
bd_size_t DataFlashBlockDevice::get_erase_size(bd_addr_t addr) const
|
||||
{
|
||||
DEBUG_PRINTF("erase size: %" PRIX16 "\r\n", _block_size);
|
||||
|
||||
return _block_size;
|
||||
}
|
||||
|
||||
bd_size_t DataFlashBlockDevice::size() const
|
||||
{
|
||||
DEBUG_PRINTF("device size: %" PRIX32 "\r\n", _device_size);
|
||||
|
||||
return _device_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function for reading a specific register.
|
||||
* @details Used for reading either the Status Register or Manufacture and ID Register.
|
||||
*
|
||||
* @param opcode Register to be read.
|
||||
* @return value.
|
||||
*/
|
||||
uint16_t DataFlashBlockDevice::_get_register(uint8_t opcode)
|
||||
{
|
||||
DEBUG_PRINTF("_get_register: %" PRIX8 "\r\n", opcode);
|
||||
|
||||
/* activate device */
|
||||
_cs = 0;
|
||||
|
||||
/* write opcode */
|
||||
_spi.write(opcode);
|
||||
|
||||
/* read and store result */
|
||||
int status = (_spi.write(DATAFLASH_OP_NOP));
|
||||
status = (status << 8) | (_spi.write(DATAFLASH_OP_NOP));
|
||||
|
||||
/* deactivate device */
|
||||
_cs = 1;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function for sending command and data to device.
|
||||
* @details The command can be an opcode with address and data or
|
||||
* a 4 byte command without data.
|
||||
*
|
||||
* The supported frequencies and the opcode used do not
|
||||
* require dummy bytes to be sent after command.
|
||||
*
|
||||
* @param command Opcode with address or 4 byte command.
|
||||
* @param buffer Data to be sent after command.
|
||||
* @param size Size of buffer.
|
||||
*/
|
||||
void DataFlashBlockDevice::_write_command(uint32_t command, const uint8_t *buffer, uint32_t size)
|
||||
{
|
||||
DEBUG_PRINTF("_write_command: %" PRIX32 " %p %" PRIX32 "\r\n", command, buffer, size);
|
||||
|
||||
/* activate device */
|
||||
_cs = 0;
|
||||
|
||||
/* send command (opcode with data or 4 byte command) */
|
||||
_spi.write((command >> 24) & 0xFF);
|
||||
_spi.write((command >> 16) & 0xFF);
|
||||
_spi.write((command >> 8) & 0xFF);
|
||||
_spi.write(command & 0xFF);
|
||||
|
||||
/* send optional data */
|
||||
if (buffer && size) {
|
||||
for (uint32_t index = 0; index < size; index++) {
|
||||
_spi.write(buffer[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/* deactivate device */
|
||||
_cs = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable and disable write protection.
|
||||
*
|
||||
* @param enable Boolean for enabling or disabling write protection.
|
||||
*/
|
||||
void DataFlashBlockDevice::_write_enable(bool enable)
|
||||
{
|
||||
DEBUG_PRINTF("_write_enable: %d\r\n", enable);
|
||||
|
||||
/* enable writing, disable write protection */
|
||||
if (enable) {
|
||||
/* if not-write-protected pin is connected, select it */
|
||||
if (_nwp.is_connected()) {
|
||||
_nwp = 1;
|
||||
}
|
||||
|
||||
/* send 4 byte command enabling writes */
|
||||
_write_command(DATAFLASH_COMMAND_WRITE_ENABLE, NULL, 0);
|
||||
} else {
|
||||
|
||||
/* if not-write-protected pin is connected, deselect it */
|
||||
if (_nwp.is_connected()) {
|
||||
_nwp = 0;
|
||||
}
|
||||
|
||||
/* send 4 byte command disabling writes */
|
||||
_write_command(DATAFLASH_COMMAND_WRITE_DISABLE, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sleep and poll status register until device is ready for next command.
|
||||
*
|
||||
* @return BlockDevice compatible error code.
|
||||
*/
|
||||
int DataFlashBlockDevice::_sync(void)
|
||||
{
|
||||
DEBUG_PRINTF("_sync\r\n");
|
||||
|
||||
/* default return value if operation times out */
|
||||
int result = BD_ERROR_DEVICE_ERROR;
|
||||
|
||||
/* Poll device until a hard coded timeout is reached.
|
||||
The polling interval is based on the typical page program time.
|
||||
*/
|
||||
for (uint32_t timeout = 0;
|
||||
timeout < DATAFLASH_TIMEOUT;
|
||||
timeout += DATAFLASH_TIMING_ERASE_PROGRAM_PAGE) {
|
||||
|
||||
/* get status register */
|
||||
uint16_t status = _get_register(DATAFLASH_OP_STATUS);
|
||||
|
||||
/* erase/program bit set, exit with error code set */
|
||||
if (status & DATAFLASH_BIT_ERASE_PROGRAM_ERROR) {
|
||||
DEBUG_PRINTF("DATAFLASH_BIT_ERASE_PROGRAM_ERROR\r\n");
|
||||
break;
|
||||
/* device ready, set OK code set */
|
||||
} else if (status & DATAFLASH_BIT_READY) {
|
||||
DEBUG_PRINTF("DATAFLASH_BIT_READY\r\n");
|
||||
result = BD_ERROR_OK;
|
||||
break;
|
||||
/* wait the typical write period before trying again */
|
||||
} else {
|
||||
DEBUG_PRINTF("wait_ms: %d\r\n", DATAFLASH_TIMING_ERASE_PROGRAM_PAGE);
|
||||
wait_ms(DATAFLASH_TIMING_ERASE_PROGRAM_PAGE);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write single page.
|
||||
* @details Address can be unaligned.
|
||||
*
|
||||
* @param buffer Data to write.
|
||||
* @param addr Address to write from.
|
||||
* @param size Bytes to write. Can at most be the full page.
|
||||
* @return BlockDevice error code.
|
||||
*/
|
||||
int DataFlashBlockDevice::_write_page(const uint8_t *buffer,
|
||||
uint32_t page,
|
||||
uint32_t offset,
|
||||
uint32_t size)
|
||||
{
|
||||
DEBUG_PRINTF("_write_page: %p %" PRIX32 " %" PRIX32 "\r\n", buffer, page, size);
|
||||
|
||||
uint32_t command = DATAFLASH_OP_NOP;
|
||||
|
||||
/* opcode for writing directly to device, in a single command,
|
||||
assuming the page has been erased before hand.
|
||||
*/
|
||||
command = DATAFLASH_OP_PROGRAM_DIRECT;
|
||||
|
||||
uint32_t address = 0;
|
||||
|
||||
/* convert page number and offset into device address based on address format */
|
||||
if (_page_size == DATAFLASH_PAGE_SIZE_264) {
|
||||
address = (page << DATAFLASH_PAGE_BIT_264) | offset;
|
||||
} else if (_page_size == DATAFLASH_PAGE_SIZE_528) {
|
||||
address = (page << DATAFLASH_PAGE_BIT_528) | offset;
|
||||
} else {
|
||||
address = (page * _page_size) | offset;
|
||||
}
|
||||
|
||||
/* set write address */
|
||||
command = (command << 8) | ((address >> 16) & 0xFF);
|
||||
command = (command << 8) | ((address >> 8) & 0xFF);
|
||||
command = (command << 8) | (address & 0xFF);
|
||||
|
||||
/* send write command with data */
|
||||
_write_command(command, buffer, size);
|
||||
|
||||
/* wait until device is ready before continuing */
|
||||
int result = _sync();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Translate address.
|
||||
* @details If the device is configured for non-binary page sizes,
|
||||
* the address is translated from binary to non-binary form.
|
||||
*
|
||||
* @param addr Binary address.
|
||||
* @return Address in format expected by device.
|
||||
*/
|
||||
uint32_t DataFlashBlockDevice::_translate_address(bd_addr_t addr)
|
||||
{
|
||||
uint32_t address = addr;
|
||||
|
||||
/* translate address if page size is non-binary */
|
||||
if (_page_size == DATAFLASH_PAGE_SIZE_264) {
|
||||
address = ((addr / DATAFLASH_PAGE_SIZE_264) << DATAFLASH_PAGE_BIT_264) |
|
||||
(addr % DATAFLASH_PAGE_SIZE_264);
|
||||
} else if (_page_size == DATAFLASH_PAGE_SIZE_528) {
|
||||
address = ((addr / DATAFLASH_PAGE_SIZE_528) << DATAFLASH_PAGE_BIT_528) |
|
||||
(addr % DATAFLASH_PAGE_SIZE_528);
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Internal function for printing out each bit set in status register.
|
||||
*
|
||||
* @param status Status register.
|
||||
*/
|
||||
void _print_status(uint16_t status)
|
||||
{
|
||||
#if DATAFLASH_DEBUG
|
||||
DEBUG_PRINTF("%04X\r\n", status);
|
||||
|
||||
/* device is ready (after write/erase) */
|
||||
if (status & DATAFLASH_BIT_READY) {
|
||||
DEBUG_PRINTF("DATAFLASH_BIT_READY\r\n");
|
||||
}
|
||||
|
||||
/* Buffer comparison failed */
|
||||
if (status & DATAFLASH_BIT_COMPARE) {
|
||||
DEBUG_PRINTF("DATAFLASH_BIT_COMPARE\r\n");
|
||||
}
|
||||
|
||||
/* device size is 2 MB */
|
||||
if (status & DATAFLASH_STATUS_DENSITY_2_MBIT) {
|
||||
DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_2_MBIT\r\n");
|
||||
}
|
||||
|
||||
/* device size is 4 MB */
|
||||
if (status & DATAFLASH_STATUS_DENSITY_4_MBIT) {
|
||||
DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_4_MBIT\r\n");
|
||||
}
|
||||
|
||||
/* device size is 8 MB */
|
||||
if (status & DATAFLASH_STATUS_DENSITY_8_MBIT) {
|
||||
DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_8_MBIT\r\n");
|
||||
}
|
||||
|
||||
/* device size is 16 MB */
|
||||
if (status & DATAFLASH_STATUS_DENSITY_16_MBIT) {
|
||||
DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_16_MBIT\r\n");
|
||||
}
|
||||
|
||||
/* device size is 32 MB */
|
||||
if (status & DATAFLASH_STATUS_DENSITY_32_MBIT) {
|
||||
DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_32_MBIT\r\n");
|
||||
}
|
||||
|
||||
/* device size is 64 MB */
|
||||
if (status & DATAFLASH_STATUS_DENSITY_64_MBIT) {
|
||||
DEBUG_PRINTF("DATAFLASH_STATUS_DENSITY_64_MBIT\r\n");
|
||||
}
|
||||
|
||||
/* sector protectino enabled */
|
||||
if (status & DATAFLASH_BIT_PROTECT) {
|
||||
DEBUG_PRINTF("DATAFLASH_BIT_PROTECT\r\n");
|
||||
}
|
||||
|
||||
/* page size is a power of 2 */
|
||||
if (status & DATAFLASH_BIT_PAGE_SIZE) {
|
||||
DEBUG_PRINTF("DATAFLASH_BIT_PAGE_SIZE\r\n");
|
||||
}
|
||||
|
||||
/* erase/program error */
|
||||
if (status & DATAFLASH_BIT_ERASE_PROGRAM_ERROR) {
|
||||
DEBUG_PRINTF("DATAFLASH_BIT_ERASE_PROGRAM_ERROR\r\n");
|
||||
}
|
||||
|
||||
/* sector lockdown still possible */
|
||||
if (status & DATAFLASH_BIT_SECTOR_LOCKDOWN) {
|
||||
DEBUG_PRINTF("DATAFLASH_BIT_SECTOR_LOCKDOWN\r\n");
|
||||
}
|
||||
|
||||
/* program operation suspended while using buffer 2 */
|
||||
if (status & DATAFLASH_BIT_PROGRAM_SUSPEND_2) {
|
||||
DEBUG_PRINTF("DATAFLASH_BIT_PROGRAM_SUSPEND_2\r\n");
|
||||
}
|
||||
|
||||
/* program operation suspended while using buffer 1 */
|
||||
if (status & DATAFLASH_BIT_PROGRAM_SUSPEND_1) {
|
||||
DEBUG_PRINTF("DATAFLASH_BIT_PROGRAM_SUSPEND_1\r\n");
|
||||
}
|
||||
|
||||
/* erase has been suspended */
|
||||
if (status & DATAFLASH_BIT_ERASE_SUSPEND) {
|
||||
DEBUG_PRINTF("DATAFLASH_BIT_ERASE_SUSPEND\r\n");
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 MBED_DATAFLASH_BLOCK_DEVICE_H
|
||||
#define MBED_DATAFLASH_BLOCK_DEVICE_H
|
||||
|
||||
#include <mbed.h>
|
||||
#include "BlockDevice.h"
|
||||
|
||||
|
||||
/** BlockDevice for DataFlash flash devices
|
||||
*
|
||||
* @code
|
||||
* // Here's an example using the AT45DB on the K64F
|
||||
* #include "mbed.h"
|
||||
* #include "DataFlashBlockDevice.h"
|
||||
*
|
||||
* // Create DataFlash on SPI bus with PTE5 as chip select
|
||||
* DataFlashBlockDevice dataflash(PTE2, PTE4, PTE1, PTE5);
|
||||
*
|
||||
* // Create DataFlash on SPI bus with PTE6 as write-protect
|
||||
* DataFlashBlockDevice dataflash2(PTE2, PTE4, PTE1, PTE5, PTE6);
|
||||
*
|
||||
* int main() {
|
||||
* printf("dataflash test\n");
|
||||
*
|
||||
* // Initialize the SPI flash device and print the memory layout
|
||||
* dataflash.init();
|
||||
* printf("dataflash size: %llu\n", dataflash.size());
|
||||
* printf("dataflash read size: %llu\n", dataflash.get_read_size());
|
||||
* printf("dataflash program size: %llu\n", dataflash.get_program_size());
|
||||
* printf("dataflash erase size: %llu\n", dataflash.get_erase_size());
|
||||
*
|
||||
* // Write "Hello World!" to the first block
|
||||
* char *buffer = (char*)malloc(dataflash.get_erase_size());
|
||||
* sprintf(buffer, "Hello World!\n");
|
||||
* dataflash.erase(0, dataflash.get_erase_size());
|
||||
* dataflash.program(buffer, 0, dataflash.get_erase_size());
|
||||
*
|
||||
* // Read back what was stored
|
||||
* dataflash.read(buffer, 0, dataflash.get_erase_size());
|
||||
* printf("%s", buffer);
|
||||
*
|
||||
* // Deinitialize the device
|
||||
* dataflash.deinit();
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
class DataFlashBlockDevice : public BlockDevice {
|
||||
public:
|
||||
/** Creates a DataFlashBlockDevice on a SPI bus specified by pins
|
||||
*
|
||||
* @param mosi SPI master out, slave in pin
|
||||
* @param miso SPI master in, slave out pin
|
||||
* @param sclk SPI clock pin
|
||||
* @param csel SPI chip select pin
|
||||
* @param nowp GPIO not-write-protect
|
||||
* @param freq Clock speed of the SPI bus (defaults to 40MHz)
|
||||
*/
|
||||
DataFlashBlockDevice(PinName mosi,
|
||||
PinName miso,
|
||||
PinName sclk,
|
||||
PinName csel,
|
||||
int freq = 40000000,
|
||||
PinName nowp = NC);
|
||||
|
||||
/** Initialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int init();
|
||||
|
||||
/** Deinitialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int deinit();
|
||||
|
||||
/** Read blocks from a block device
|
||||
*
|
||||
* @param buffer Buffer to write blocks to
|
||||
* @param addr Address of block to begin reading from
|
||||
* @param size Size to read in bytes, must be a multiple of read block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int read(void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Program blocks to a block device
|
||||
*
|
||||
* The blocks must have been erased prior to being programmed
|
||||
*
|
||||
* @param buffer Buffer of data to write to blocks
|
||||
* @param addr Address of block to begin writing to
|
||||
* @param size Size to write in bytes, must be a multiple of program block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Erase blocks on a block device
|
||||
*
|
||||
* The state of an erased block is undefined until it has been programmed
|
||||
*
|
||||
* @param addr Address of block to begin erasing
|
||||
* @param size Size to erase in bytes, must be a multiple of erase block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int erase(bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Get the size of a readable block
|
||||
*
|
||||
* @return Size of a readable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_read_size() const;
|
||||
|
||||
/** Get the size of a programable block
|
||||
*
|
||||
* @return Size of a programable block in bytes
|
||||
* @note Must be a multiple of the read size
|
||||
*/
|
||||
virtual bd_size_t get_program_size() const;
|
||||
|
||||
/** Get the size of a eraseable block
|
||||
*
|
||||
* @return Size of a eraseable block in bytes
|
||||
* @note Must be a multiple of the program size
|
||||
*/
|
||||
virtual bd_size_t get_erase_size() const;
|
||||
|
||||
/** Get the size of an erasable block given address
|
||||
*
|
||||
* @param addr Address within the erasable block
|
||||
* @return Size of an erasable block in bytes
|
||||
* @note Must be a multiple of the program size
|
||||
*/
|
||||
virtual bd_size_t get_erase_size(bd_addr_t addr) const;
|
||||
|
||||
/** Get the total size of the underlying device
|
||||
*
|
||||
* @return Size of the underlying device in bytes
|
||||
*/
|
||||
virtual bd_size_t size() const;
|
||||
|
||||
private:
|
||||
// Master side hardware
|
||||
SPI _spi;
|
||||
DigitalOut _cs;
|
||||
DigitalOut _nwp;
|
||||
|
||||
// Device configuration
|
||||
uint32_t _device_size;
|
||||
uint16_t _page_size;
|
||||
uint16_t _block_size;
|
||||
bool _is_initialized;
|
||||
uint32_t _init_ref_count;
|
||||
|
||||
// Internal functions
|
||||
uint16_t _get_register(uint8_t opcode);
|
||||
void _write_command(uint32_t command, const uint8_t *buffer, uint32_t size);
|
||||
void _write_enable(bool enable);
|
||||
int _sync(void);
|
||||
int _write_page(const uint8_t *buffer, uint32_t addr, uint32_t offset, uint32_t size);
|
||||
uint32_t _translate_address(bd_addr_t addr);
|
||||
};
|
||||
|
||||
|
||||
#endif /* MBED_DATAFLASH_BLOCK_DEVICE_H */
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "dataflash",
|
||||
"config": {
|
||||
"SPI_MOSI": "NC",
|
||||
"SPI_MISO": "NC",
|
||||
"SPI_CLK": "NC",
|
||||
"SPI_CS": "NC",
|
||||
"binary-size": {
|
||||
"help": "Configure device to use binary address space.",
|
||||
"value": "0"
|
||||
},
|
||||
"dataflash-size": {
|
||||
"help": "Configure device to use DataFlash address space.",
|
||||
"value": "0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef DEVICE_FLASH
|
||||
|
||||
#include "FlashIAPBlockDevice.h"
|
||||
#include "mbed_critical.h"
|
||||
|
||||
#include "mbed.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
|
||||
#define FLASHIAP_READ_SIZE 1
|
||||
|
||||
// Debug available
|
||||
#ifndef FLASHIAP_DEBUG
|
||||
#define FLASHIAP_DEBUG 0
|
||||
#endif
|
||||
|
||||
#if FLASHIAP_DEBUG
|
||||
#define DEBUG_PRINTF(...) printf(__VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_PRINTF(...)
|
||||
#endif
|
||||
|
||||
FlashIAPBlockDevice::FlashIAPBlockDevice()
|
||||
: _flash(), _base(0), _size(0), _is_initialized(false), _init_ref_count(0)
|
||||
{
|
||||
DEBUG_PRINTF("FlashIAPBlockDevice: %" PRIX32 " %" PRIX32 "\r\n", address, size);
|
||||
}
|
||||
|
||||
FlashIAPBlockDevice::FlashIAPBlockDevice(uint32_t address, uint32_t)
|
||||
: _flash(), _base(0), _size(0), _is_initialized(false), _init_ref_count(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FlashIAPBlockDevice::~FlashIAPBlockDevice()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
int FlashIAPBlockDevice::init()
|
||||
{
|
||||
DEBUG_PRINTF("init\r\n");
|
||||
|
||||
if (!_is_initialized) {
|
||||
_init_ref_count = 0;
|
||||
}
|
||||
|
||||
uint32_t val = core_util_atomic_incr_u32(&_init_ref_count, 1);
|
||||
|
||||
if (val != 1) {
|
||||
return BD_ERROR_OK;
|
||||
}
|
||||
|
||||
int ret = _flash.init();
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
_base = _flash.get_flash_start();
|
||||
_size = _flash.get_flash_size();
|
||||
|
||||
_is_initialized = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int FlashIAPBlockDevice::deinit()
|
||||
{
|
||||
DEBUG_PRINTF("deinit\r\n");
|
||||
|
||||
if (!_is_initialized) {
|
||||
_init_ref_count = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t val = core_util_atomic_decr_u32(&_init_ref_count, 1);
|
||||
|
||||
if (val) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
_is_initialized = false;
|
||||
|
||||
return _flash.deinit();
|
||||
}
|
||||
|
||||
int FlashIAPBlockDevice::read(void *buffer,
|
||||
bd_addr_t virtual_address,
|
||||
bd_size_t size)
|
||||
{
|
||||
DEBUG_PRINTF("read: %" PRIX64 " %" PRIX64 "\r\n", virtual_address, size);
|
||||
|
||||
/* Default to error return code; success must be set explicitly. */
|
||||
int result = BD_ERROR_DEVICE_ERROR;
|
||||
|
||||
/* Check that the address and size are properly aligned and fit. */
|
||||
if (_is_initialized && is_valid_read(virtual_address, size)) {
|
||||
|
||||
/* Convert virtual address to the physical address for the device. */
|
||||
bd_addr_t physical_address = _base + virtual_address;
|
||||
|
||||
/* Read data using the internal flash driver. */
|
||||
result = _flash.read(buffer, physical_address, size);
|
||||
|
||||
DEBUG_PRINTF("physical: %" PRIX64 "\r\n", physical_address);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int FlashIAPBlockDevice::program(const void *buffer,
|
||||
bd_addr_t virtual_address,
|
||||
bd_size_t size)
|
||||
{
|
||||
DEBUG_PRINTF("program: %" PRIX64 " %" PRIX64 "\r\n", virtual_address, size);
|
||||
|
||||
/* Default to error return code; success must be set explicitly. */
|
||||
int result = BD_ERROR_DEVICE_ERROR;
|
||||
|
||||
/* Check that the address and size are properly aligned and fit. */
|
||||
if (_is_initialized && is_valid_program(virtual_address, size)) {
|
||||
|
||||
/* Convert virtual address to the physical address for the device. */
|
||||
bd_addr_t physical_address = _base + virtual_address;
|
||||
|
||||
/* Write data using the internal flash driver. */
|
||||
result = _flash.program(buffer, physical_address, size);
|
||||
|
||||
DEBUG_PRINTF("physical: %" PRIX64 " %" PRIX64 "\r\n",
|
||||
physical_address,
|
||||
size);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int FlashIAPBlockDevice::erase(bd_addr_t virtual_address,
|
||||
bd_size_t size)
|
||||
{
|
||||
DEBUG_PRINTF("erase: %" PRIX64 " %" PRIX64 "\r\n", virtual_address, size);
|
||||
|
||||
/* Default to error return code; success must be set explicitly. */
|
||||
int result = BD_ERROR_DEVICE_ERROR;
|
||||
|
||||
/* Check that the address and size are properly aligned and fit. */
|
||||
if (_is_initialized && is_valid_erase(virtual_address, size)) {
|
||||
|
||||
/* Convert virtual address to the physical address for the device. */
|
||||
bd_addr_t physical_address = _base + virtual_address;
|
||||
|
||||
/* Erase sector */
|
||||
result = _flash.erase(physical_address, size);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bd_size_t FlashIAPBlockDevice::get_read_size() const
|
||||
{
|
||||
DEBUG_PRINTF("get_read_size: %d\r\n", FLASHIAP_READ_SIZE);
|
||||
|
||||
return FLASHIAP_READ_SIZE;
|
||||
}
|
||||
|
||||
bd_size_t FlashIAPBlockDevice::get_program_size() const
|
||||
{
|
||||
if (!_is_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t page_size = _flash.get_page_size();
|
||||
|
||||
DEBUG_PRINTF("get_program_size: %" PRIX32 "\r\n", page_size);
|
||||
|
||||
return page_size;
|
||||
}
|
||||
|
||||
bd_size_t FlashIAPBlockDevice::get_erase_size() const
|
||||
{
|
||||
if (!_is_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t erase_size = _flash.get_sector_size(_base);
|
||||
|
||||
DEBUG_PRINTF("get_erase_size: %" PRIX32 "\r\n", erase_size);
|
||||
|
||||
return erase_size;
|
||||
}
|
||||
|
||||
bd_size_t FlashIAPBlockDevice::get_erase_size(bd_addr_t addr) const
|
||||
{
|
||||
if (!_is_initialized) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t erase_size = _flash.get_sector_size(_base + addr);
|
||||
|
||||
DEBUG_PRINTF("get_erase_size: %" PRIX32 "\r\n", erase_size);
|
||||
|
||||
return erase_size;
|
||||
}
|
||||
|
||||
bd_size_t FlashIAPBlockDevice::size() const
|
||||
{
|
||||
DEBUG_PRINTF("size: %" PRIX64 "\r\n", _size);
|
||||
|
||||
return _size;
|
||||
}
|
||||
|
||||
#endif /* DEVICE_FLASH */
|
|
@ -0,0 +1,125 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 MBED_FLASHIAP_BLOCK_DEVICE_H
|
||||
#define MBED_FLASHIAP_BLOCK_DEVICE_H
|
||||
|
||||
#ifdef DEVICE_FLASH
|
||||
|
||||
#include "FlashIAP.h"
|
||||
#include "BlockDevice.h"
|
||||
#include "platform/mbed_toolchain.h"
|
||||
|
||||
/** BlockDevice using the FlashIAP API
|
||||
*
|
||||
*/
|
||||
class FlashIAPBlockDevice : public BlockDevice {
|
||||
public:
|
||||
/** Creates a FlashIAPBlockDevice **/
|
||||
FlashIAPBlockDevice();
|
||||
|
||||
MBED_DEPRECATED("Please use default constructor instead")
|
||||
FlashIAPBlockDevice(uint32_t address, uint32_t size = 0);
|
||||
|
||||
virtual ~FlashIAPBlockDevice();
|
||||
|
||||
/** Initialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int init();
|
||||
|
||||
/** Deinitialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int deinit();
|
||||
|
||||
/** Read blocks from a block device
|
||||
*
|
||||
* @param buffer Buffer to write blocks to
|
||||
* @param addr Address of block to begin reading from
|
||||
* @param size Size to read in bytes, must be a multiple of read block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int read(void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Program blocks to a block device
|
||||
*
|
||||
* The blocks must have been erased prior to being programmed
|
||||
*
|
||||
* @param buffer Buffer of data to write to blocks
|
||||
* @param addr Address of block to begin writing to
|
||||
* @param size Size to write in bytes, must be a multiple of program block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Erase blocks on a block device
|
||||
*
|
||||
* The state of an erased block is undefined until it has been programmed
|
||||
*
|
||||
* @param addr Address of block to begin erasing
|
||||
* @param size Size to erase in bytes, must be a multiple of erase block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int erase(bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Get the size of a readable block
|
||||
*
|
||||
* @return Size of a readable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_read_size() const;
|
||||
|
||||
/** Get the size of a programable block
|
||||
*
|
||||
* @return Size of a programable block in bytes
|
||||
* @note Must be a multiple of the read size
|
||||
*/
|
||||
virtual bd_size_t get_program_size() const;
|
||||
|
||||
/** Get the size of a eraseable block
|
||||
*
|
||||
* @return Size of a eraseable block in bytes
|
||||
* @note Must be a multiple of the program size
|
||||
*/
|
||||
virtual bd_size_t get_erase_size() const;
|
||||
|
||||
/** Get the size of an erasable block given address
|
||||
*
|
||||
* @param addr Address within the erasable block
|
||||
* @return Size of an erasable block in bytes
|
||||
* @note Must be a multiple of the program size
|
||||
*/
|
||||
virtual bd_size_t get_erase_size(bd_addr_t addr) const;
|
||||
|
||||
/** Get the total size of the underlying device
|
||||
*
|
||||
* @return Size of the underlying device in bytes
|
||||
*/
|
||||
virtual bd_size_t size() const;
|
||||
|
||||
private:
|
||||
// Device configuration
|
||||
mbed::FlashIAP _flash;
|
||||
bd_addr_t _base;
|
||||
bd_size_t _size;
|
||||
bool _is_initialized;
|
||||
uint32_t _init_ref_count;
|
||||
};
|
||||
|
||||
#endif /* DEVICE_FLASH */
|
||||
#endif /* MBED_FLASHIAP_BLOCK_DEVICE_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,104 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 fslittle_debug.h
|
||||
*
|
||||
* component debug header file.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __FSLITTLE_DEBUG
|
||||
#define __FSLITTLE_DEBUG
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
/* Debug Support */
|
||||
|
||||
#define FSLITTLE_LOG_NONE 0
|
||||
#define FSLITTLE_LOG_ERR 1
|
||||
#define FSLITTLE_LOG_WARN 2
|
||||
#define FSLITTLE_LOG_NOTICE 3
|
||||
#define FSLITTLE_LOG_INFO 4
|
||||
#define FSLITTLE_LOG_DEBUG 5
|
||||
#define FSLITTLE_LOG_FENTRY 6
|
||||
|
||||
#define FSLITTLE_LOG(_fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
printf(_fmt, __VA_ARGS__); \
|
||||
}while(0);
|
||||
|
||||
#define noFSLITTLE_DEBUG
|
||||
#ifdef FSLITTLE_DEBUG
|
||||
|
||||
extern uint32_t fslittle_optDebug_g;
|
||||
extern uint32_t fslittle_optLogLevel_g;
|
||||
|
||||
|
||||
/* uncomment for asserts to work */
|
||||
/* #undef NDEBUG */
|
||||
// todo: port to mbedOSV3++ #include <core-util/assert.h>
|
||||
|
||||
#define FSLITTLE_INLINE
|
||||
// todo: port to mbedOSV3++ #define FSLITTLE_ASSERT CORE_UTIL_ASSERT
|
||||
#define FSLITTLE_ASSERT(...)
|
||||
|
||||
#define FSLITTLE_DBGLOG(_fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if(fslittle_optDebug_g && (fslittle_optLogLevel_g >= FSLITTLE_LOG_DEBUG)) \
|
||||
{ \
|
||||
printf(_fmt, __VA_ARGS__); \
|
||||
} \
|
||||
}while(0);
|
||||
|
||||
|
||||
#define FSLITTLE_ERRLOG(_fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if(fslittle_optDebug_g && (fslittle_optLogLevel_g >= FSLITTLE_LOG_ERR)) \
|
||||
{ \
|
||||
printf(_fmt, __VA_ARGS__); \
|
||||
} \
|
||||
}while(0);
|
||||
|
||||
|
||||
#define FSLITTLE_FENTRYLOG(_fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if(fslittle_optDebug_g && (fslittle_optLogLevel_g >= FSLITTLE_LOG_FENTRY)) \
|
||||
{ \
|
||||
printf(_fmt, __VA_ARGS__); \
|
||||
} \
|
||||
}while(0);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#else
|
||||
#define FSLITTLE_ASSERT(_x) do { } while(0)
|
||||
#define FSLITTLE_INLINE inline
|
||||
#define FSLITTLE_DBGLOG(_fmt, ...) do { } while(0)
|
||||
#define FSLITTLE_ERRLOG(_fmt, ...) do { } while(0)
|
||||
#define FSLITTLE_FENTRYLOG(_fmt, ...) do { } while(0)
|
||||
#endif /* FSLITTLE_DEBUG */
|
||||
|
||||
|
||||
#endif /*__FSLITTLE_DEBUG*/
|
|
@ -0,0 +1,117 @@
|
|||
/* @file fslittle_test.c
|
||||
*
|
||||
* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2016 ARM Limited
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* test support code implementation file.
|
||||
*/
|
||||
|
||||
#include "fslittle_debug.h"
|
||||
#include "fslittle_test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
#ifdef FSLITTLE_DEBUG
|
||||
uint32_t fslittle_optDebug_g = 1;
|
||||
uint32_t fslittle_optLogLevel_g = FSLITTLE_LOG_NONE; /*FSLITTLE_LOG_NONE|FSLITTLE_LOG_ERR|FSLITTLE_LOG_DEBUG|FSLITTLE_LOG_FENTRY; */
|
||||
#endif
|
||||
|
||||
/* ruler for measuring text strings */
|
||||
/* 1 1 1 1 1 1 1 1 1 1 2 2 2 */
|
||||
/* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 */
|
||||
/* 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */
|
||||
|
||||
const uint8_t fslittle_test_byte_data_table[FSLITTLE_TEST_BYTE_DATA_TABLE_SIZE] = {
|
||||
0x2d, 0xf3, 0x31, 0x4c, 0x11, 0x4f, 0xde, 0x0d, 0xbd, 0xbc, 0xa6, 0x78, 0x36, 0x5c, 0x1d, 0x28,
|
||||
0x5f, 0xa9, 0x10, 0x65, 0x54, 0x45, 0x21, 0x1a, 0x88, 0xfe, 0x76, 0x45, 0xb9, 0xac, 0x65, 0x9a,
|
||||
0x34, 0x9d, 0x73, 0x10, 0xb4, 0xa9, 0x2e, 0x90, 0x95, 0x68, 0xac, 0xfe, 0xc5, 0x2d, 0x15, 0x03,
|
||||
0x34, 0x70, 0xf1, 0x1d, 0x48, 0xa1, 0xa0, 0xed, 0x5c, 0x2f, 0xf5, 0x2b, 0xb9, 0x84, 0xbb, 0x45,
|
||||
0x32, 0xdd, 0xb1, 0x33, 0x95, 0x2a, 0xbc, 0x26, 0xf0, 0x89, 0xba, 0xf4, 0xbd, 0xf9, 0x5d, 0x2e,
|
||||
0x6e, 0x11, 0xc6, 0xa7, 0x78, 0xfc, 0xc9, 0x0e, 0x6b, 0x38, 0xba, 0x14, 0x1b, 0xab, 0x4c, 0x20,
|
||||
0x91, 0xe4, 0xb0, 0xf1, 0x2b, 0x14, 0x07, 0x6b, 0xb5, 0xcd, 0xe3, 0x49, 0x75, 0xac, 0xe8, 0x98,
|
||||
0xf1, 0x58, 0x8f, 0xd9, 0xc4, 0x8f, 0x00, 0x17, 0xb5, 0x06, 0x6a, 0x33, 0xbd, 0xa7, 0x40, 0x5a,
|
||||
0xbf, 0x49, 0xf7, 0x27, 0x1b, 0x4c, 0x3e, 0x6f, 0xe3, 0x08, 0x1f, 0xfd, 0xa6, 0xd4, 0xc7, 0x5f,
|
||||
0xa4, 0xa6, 0x82, 0xad, 0x19, 0xd5, 0x5c, 0xd8, 0x3a, 0x49, 0x85, 0xc9, 0x21, 0x83, 0xf6, 0xc6,
|
||||
0x84, 0xf9, 0x76, 0x89, 0xf3, 0x2d, 0x17, 0x50, 0x97, 0x38, 0x48, 0x9a, 0xe1, 0x82, 0xcd, 0xac,
|
||||
0xa8, 0x1d, 0xd7, 0x96, 0x5e, 0xb3, 0x08, 0xa8, 0x3a, 0xc7, 0x2b, 0x05, 0xaf, 0xdc, 0x16, 0xdf,
|
||||
0x48, 0x0f, 0x2a, 0x7e, 0x3a, 0x82, 0xd7, 0x80, 0xd6, 0x49, 0x27, 0x5d, 0xe3, 0x07, 0x62, 0xb3,
|
||||
0xc3, 0x6c, 0xba, 0xb2, 0xaa, 0x9f, 0xd9, 0x03, 0x0d, 0x27, 0xa8, 0xe0, 0xd6, 0xee, 0x79, 0x4b,
|
||||
0xd6, 0x97, 0x99, 0xb7, 0x11, 0xd6, 0x0d, 0x34, 0xae, 0x99, 0x4a, 0x93, 0x95, 0xd0, 0x5a, 0x34,
|
||||
0x19, 0xa2, 0x69, 0x57, 0xcf, 0x7c, 0x3d, 0x98, 0x88, 0x5d, 0x04, 0xf2, 0xd7, 0xac, 0xa5, 0x63
|
||||
};
|
||||
|
||||
|
||||
/* @brief test utility function to delete the file identified by filename
|
||||
*/
|
||||
int32_t fslittle_test_delete(const char* filename)
|
||||
{
|
||||
FSLITTLE_FENTRYLOG("%s:entered.\r\n", __func__);
|
||||
return remove(filename);
|
||||
}
|
||||
|
||||
|
||||
/* @brief test utility function to create a file
|
||||
*
|
||||
* @param filename name of the file including path
|
||||
* @param data data to store in file
|
||||
* @param len number of bytes of data present in the data buffer.
|
||||
*/
|
||||
int32_t fslittle_test_create(const char* filename, const char* data, size_t len)
|
||||
{
|
||||
int32_t ret = -1;
|
||||
FILE *fp = NULL;
|
||||
|
||||
FSLITTLE_FENTRYLOG("%s:entered (filename=%s, len=%d).\n", __func__, filename, (int) len);
|
||||
fp = fopen(filename, "w+");
|
||||
if(fp == NULL){
|
||||
return ret;
|
||||
}
|
||||
ret = fwrite((const void*) data, len, 1, fp);
|
||||
if(ret < 0){
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* @brief support function for generating a kv_name
|
||||
* @param name buffer to hold kv name
|
||||
* @param len length of kv name to generate
|
||||
*
|
||||
*/
|
||||
int32_t fslittle_test_filename_gen(char* name, const size_t len)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t pos = 0;
|
||||
|
||||
const char* buf = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!$-_@";
|
||||
const int buf_len = strlen(buf);
|
||||
FSLITTLE_FENTRYLOG("%s:entered\n", __func__);
|
||||
for(i = 0; i < len; i++)
|
||||
{
|
||||
pos = rand() % (buf_len);
|
||||
name[i] = buf[pos];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/** @file fslittle_test.h
|
||||
*
|
||||
* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2016 ARM Limited
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Header file for test support data structures and function API.
|
||||
*/
|
||||
#ifndef __FSLITTLE_TEST_H
|
||||
#define __FSLITTLE_TEST_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Defines */
|
||||
//#define FSLITTLE_INIT_1_TABLE_HEAD { "a", ""}
|
||||
#define FSLITTLE_INIT_1_TABLE_MID_NODE { "/sd/01234567.txt", "abcdefghijklmnopqrstuvwxyz"}
|
||||
//#define FSLITTLE_INIT_1_TABLE_TAIL { "/sd/fopentst/hello/world/animal/wobbly/dog/foot/backrght.txt", "present"}
|
||||
#define FSLITTLE_TEST_RW_TABLE_SENTINEL 0xffffffff
|
||||
#define FSLITTLE_TEST_BYTE_DATA_TABLE_SIZE 256
|
||||
#define FSLITTLE_UTEST_MSG_BUF_SIZE 256
|
||||
#define FSLITTLE_UTEST_DEFAULT_TIMEOUT_MS 10000
|
||||
#define FSLITTLE_MBED_HOSTTEST_TIMEOUT 60
|
||||
#define FSLITTLE_MAX_FILE_BASENAME 8
|
||||
#define FSLITTLE_MAX_FILE_EXTNAME 3
|
||||
#define FSLITTLE_BUF_MAX_LENGTH 64
|
||||
#define FSLITTLE_FILENAME_MAX_LENGTH 255
|
||||
|
||||
|
||||
/* support macro for make string for utest _MESSAGE macros, which dont support formatted output */
|
||||
#define FSLITTLE_TEST_UTEST_MESSAGE(_buf, _max_len, _fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
snprintf((_buf), (_max_len), (_fmt), __VA_ARGS__); \
|
||||
}while(0);
|
||||
|
||||
|
||||
/*
|
||||
* Structures
|
||||
*/
|
||||
|
||||
/* kv data for test */
|
||||
typedef struct fslittle_kv_data_t {
|
||||
const char *filename;
|
||||
const char *value;
|
||||
} fslittle_kv_data_t;
|
||||
|
||||
|
||||
extern const uint8_t fslittle_test_byte_data_table[FSLITTLE_TEST_BYTE_DATA_TABLE_SIZE];
|
||||
|
||||
int32_t fslittle_test_create(const char *filename, const char *data, size_t len);
|
||||
int32_t fslittle_test_delete(const char *key_name);
|
||||
int32_t fslittle_test_filename_gen(char *name, const size_t len);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __FSLITTLE_TEST_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,226 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2013 ARM Limited
|
||||
*
|
||||
* 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 MBED_SD_BLOCK_DEVICE_H
|
||||
#define MBED_SD_BLOCK_DEVICE_H
|
||||
|
||||
/* If the target has no SPI support then SDCard is not supported */
|
||||
#ifdef DEVICE_SPI
|
||||
|
||||
#include "BlockDevice.h"
|
||||
#include "mbed.h"
|
||||
#include "platform/PlatformMutex.h"
|
||||
|
||||
/** SDBlockDevice class
|
||||
*
|
||||
* Access an SD Card using SPI
|
||||
*/
|
||||
class SDBlockDevice : public BlockDevice {
|
||||
public:
|
||||
/** Lifetime of an SD card
|
||||
*/
|
||||
SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs, uint64_t hz = 1000000, bool crc_on = 0);
|
||||
virtual ~SDBlockDevice();
|
||||
|
||||
/** Initialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int init();
|
||||
|
||||
/** Deinitialize a block device
|
||||
*
|
||||
* @return 0 on success or a negative error code on failure
|
||||
*/
|
||||
virtual int deinit();
|
||||
|
||||
/** Read blocks from a block device
|
||||
*
|
||||
* @param buffer Buffer to write blocks to
|
||||
* @param addr Address of block to begin reading from
|
||||
* @param size Size to read in bytes, must be a multiple of read block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int read(void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Program blocks to a block device
|
||||
*
|
||||
* The blocks must have been erased prior to being programmed
|
||||
*
|
||||
* @param buffer Buffer of data to write to blocks
|
||||
* @param addr Address of block to begin writing to
|
||||
* @param size Size to write in bytes, must be a multiple of program block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Mark blocks as no longer in use
|
||||
*
|
||||
* This function provides a hint to the underlying block device that a region of blocks
|
||||
* is no longer in use and may be erased without side effects. Erase must still be called
|
||||
* before programming, but trimming allows flash-translation-layers to schedule erases when
|
||||
* the device is not busy.
|
||||
*
|
||||
* @param addr Address of block to mark as unused
|
||||
* @param size Size to mark as unused in bytes, must be a multiple of erase block size
|
||||
* @return 0 on success, negative error code on failure
|
||||
*/
|
||||
virtual int trim(bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Get the size of a readable block
|
||||
*
|
||||
* @return Size of a readable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_read_size() const;
|
||||
|
||||
/** Get the size of a programable block
|
||||
*
|
||||
* @return Size of a programable block in bytes
|
||||
* @note Must be a multiple of the read size
|
||||
*/
|
||||
virtual bd_size_t get_program_size() const;
|
||||
|
||||
/** Get the total size of the underlying device
|
||||
*
|
||||
* @return Size of the underlying device in bytes
|
||||
*/
|
||||
virtual bd_size_t size() const;
|
||||
|
||||
/** Enable or disable debugging
|
||||
*
|
||||
* @param dbg State of debugging
|
||||
*/
|
||||
virtual void debug(bool dbg);
|
||||
|
||||
/** Set the transfer frequency
|
||||
*
|
||||
* @param freq Transfer frequency
|
||||
* @note Max frequency supported is 25MHZ
|
||||
*/
|
||||
virtual int frequency(uint64_t freq);
|
||||
|
||||
|
||||
private:
|
||||
/* Commands : Listed below are commands supported
|
||||
* in SPI mode for SD card : Only Mandatory ones
|
||||
*/
|
||||
enum cmdSupported {
|
||||
CMD_NOT_SUPPORTED = -1, /**< Command not supported error */
|
||||
CMD0_GO_IDLE_STATE = 0, /**< Resets the SD Memory Card */
|
||||
CMD1_SEND_OP_COND = 1, /**< Sends host capacity support */
|
||||
CMD6_SWITCH_FUNC = 6, /**< Check and Switches card function */
|
||||
CMD8_SEND_IF_COND = 8, /**< Supply voltage info */
|
||||
CMD9_SEND_CSD = 9, /**< Provides Card Specific data */
|
||||
CMD10_SEND_CID = 10, /**< Provides Card Identification */
|
||||
CMD12_STOP_TRANSMISSION = 12, /**< Forces the card to stop transmission */
|
||||
CMD13_SEND_STATUS = 13, /**< Card responds with status */
|
||||
CMD16_SET_BLOCKLEN = 16, /**< Length for SC card is set */
|
||||
CMD17_READ_SINGLE_BLOCK = 17, /**< Read single block of data */
|
||||
CMD18_READ_MULTIPLE_BLOCK = 18, /**< Card transfers data blocks to host until interrupted
|
||||
by a STOP_TRANSMISSION command */
|
||||
CMD24_WRITE_BLOCK = 24, /**< Write single block of data */
|
||||
CMD25_WRITE_MULTIPLE_BLOCK = 25, /**< Continuously writes blocks of data until
|
||||
'Stop Tran' token is sent */
|
||||
CMD27_PROGRAM_CSD = 27, /**< Programming bits of CSD */
|
||||
CMD32_ERASE_WR_BLK_START_ADDR = 32, /**< Sets the address of the first write
|
||||
block to be erased. */
|
||||
CMD33_ERASE_WR_BLK_END_ADDR = 33, /**< Sets the address of the last write
|
||||
block of the continuous range to be erased.*/
|
||||
CMD38_ERASE = 38, /**< Erases all previously selected write blocks */
|
||||
CMD55_APP_CMD = 55, /**< Extend to Applications specific commands */
|
||||
CMD56_GEN_CMD = 56, /**< General Purpose Command */
|
||||
CMD58_READ_OCR = 58, /**< Read OCR register of card */
|
||||
CMD59_CRC_ON_OFF = 59, /**< Turns the CRC option on or off*/
|
||||
// App Commands
|
||||
ACMD6_SET_BUS_WIDTH = 6,
|
||||
ACMD13_SD_STATUS = 13,
|
||||
ACMD22_SEND_NUM_WR_BLOCKS = 22,
|
||||
ACMD23_SET_WR_BLK_ERASE_COUNT = 23,
|
||||
ACMD41_SD_SEND_OP_COND = 41,
|
||||
ACMD42_SET_CLR_CARD_DETECT = 42,
|
||||
ACMD51_SEND_SCR = 51,
|
||||
};
|
||||
|
||||
uint8_t _card_type;
|
||||
int _cmd(SDBlockDevice::cmdSupported cmd, uint32_t arg, bool isAcmd = 0, uint32_t *resp = NULL);
|
||||
int _cmd8();
|
||||
|
||||
/* Move the SDCard into the SPI Mode idle state
|
||||
*
|
||||
* The card is transitioned from SDCard mode to SPI mode by sending the
|
||||
* CMD0 (GO_IDLE_STATE) command with CS asserted. See the notes in the
|
||||
* "SPI Startup" section of the comments at the head of the
|
||||
* implementation file for further details and specification references.
|
||||
*
|
||||
* @return Response form the card. R1_IDLE_STATE (0x1), the successful
|
||||
* response from CMD0. R1_XXX_XXX for more response
|
||||
*/
|
||||
uint32_t _go_idle_state();
|
||||
int _initialise_card();
|
||||
|
||||
bd_size_t _sectors;
|
||||
bd_size_t _sd_sectors();
|
||||
|
||||
bool _is_valid_trim(bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/* SPI functions */
|
||||
Timer _spi_timer; /**< Timer Class object used for busy wait */
|
||||
uint32_t _init_sck; /**< Intial SPI frequency */
|
||||
uint32_t _transfer_sck; /**< SPI frequency during data transfer/after initialization */
|
||||
SPI _spi; /**< SPI Class object */
|
||||
|
||||
/* SPI initialization function */
|
||||
void _spi_init();
|
||||
uint8_t _cmd_spi(SDBlockDevice::cmdSupported cmd, uint32_t arg);
|
||||
void _spi_wait(uint8_t count);
|
||||
|
||||
bool _wait_token(uint8_t token); /**< Wait for token */
|
||||
bool _wait_ready(uint16_t ms = 300); /**< 300ms default wait for card to be ready */
|
||||
int _read(uint8_t *buffer, uint32_t length);
|
||||
int _read_bytes(uint8_t *buffer, uint32_t length);
|
||||
uint8_t _write(const uint8_t *buffer, uint8_t token, uint32_t length);
|
||||
int _freq(void);
|
||||
|
||||
/* Chip Select and SPI mode select */
|
||||
DigitalOut _cs;
|
||||
void _select();
|
||||
void _deselect();
|
||||
|
||||
virtual void lock()
|
||||
{
|
||||
_mutex.lock();
|
||||
}
|
||||
|
||||
virtual void unlock()
|
||||
{
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
PlatformMutex _mutex;
|
||||
bd_size_t _block_size;
|
||||
bd_size_t _erase_size;
|
||||
bool _is_initialized;
|
||||
bool _dbg;
|
||||
bool _crc_on;
|
||||
uint32_t _init_ref_count;
|
||||
|
||||
MbedCRC<POLY_7BIT_SD, 7> _crc7;
|
||||
MbedCRC<POLY_16BIT_CCITT, 16> _crc16;
|
||||
};
|
||||
|
||||
#endif /* DEVICE_SPI */
|
||||
|
||||
#endif /* MBED_SD_BLOCK_DEVICE_H */
|
|
@ -0,0 +1,500 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM FATFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#define MBED_TEST_BLOCKDEVICE SDBlockDevice
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 120
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
|
||||
// tests
|
||||
|
||||
void test_directory_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_root_directory()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_creation()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("potato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_file_creation()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "burito", O_CREAT | O_WRONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void dir_file_check(char *list[], uint32_t elements)
|
||||
{
|
||||
int res;
|
||||
while (1) {
|
||||
res = dir[0].read(&ent);
|
||||
if (0 == res) {
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < elements ; i++) {
|
||||
res = strcmp(ent.d_name, list[i]);
|
||||
if (0 == res) {
|
||||
res = ent.d_type;
|
||||
if ((DT_DIR != res) && (DT_REG != res)) {
|
||||
TEST_ASSERT(1);
|
||||
}
|
||||
break;
|
||||
} else if ( i == elements) {
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_directory_iteration()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
char *dir_list[] = {"potato", "burito", ".", ".."};
|
||||
|
||||
dir_file_check(dir_list, (sizeof(dir_list) / sizeof(dir_list[0])));
|
||||
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_failures()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("potato", 0777);
|
||||
TEST_ASSERT_EQUAL(-EEXIST, res);
|
||||
res = dir[0].open(&fs, "tomato");
|
||||
TEST_ASSERT_EQUAL(-ENOTDIR, res);
|
||||
res = dir[0].open(&fs, "burito");
|
||||
TEST_ASSERT_NOT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "tomato", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(-ENOENT, res);
|
||||
res = file[0].open(&fs, "potato", O_RDONLY);
|
||||
TEST_ASSERT_NOT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_nested_directories()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("potato/baked", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("potato/sweet", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("potato/fried", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
char *dir_list[] = {"potato", "baked", "sweet", "fried", ".", ".."};
|
||||
dir_file_check(dir_list, (sizeof(dir_list) / sizeof(dir_list[0])));
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_multi_block_directory()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("cactus", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (int i = 0; i < 128; i++) {
|
||||
sprintf((char *)buffer, "cactus/test%d", i);
|
||||
res = fs.mkdir((char *)buffer, 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "cactus");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
#if (MBED_TEST_FILESYSTEM != FATFileSystem)
|
||||
char *dir_list[] = {".", ".."};
|
||||
dir_file_check(dir_list, (sizeof(dir_list) / sizeof(dir_list[0])));
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < 128; i++) {
|
||||
sprintf((char *)buffer, "test%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char *)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_remove()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("potato");
|
||||
TEST_ASSERT_NOT_EQUAL(0, res);
|
||||
res = fs.remove("potato/sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("potato/baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("potato/fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "potato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
#if (MBED_TEST_FILESYSTEM != FATFileSystem)
|
||||
char *dir_list[] = {".", ".."};
|
||||
dir_file_check(dir_list, (sizeof(dir_list) / sizeof(dir_list[0])));
|
||||
#endif
|
||||
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("potato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
char *dir_list[] = {"burito", "cactus", ".", ".."};
|
||||
dir_file_check(dir_list, (sizeof(dir_list) / sizeof(dir_list[0])));
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_directory_rename()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("coldpotato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("coldpotato/baked", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("coldpotato/sweet", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("coldpotato/fried", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("coldpotato", "hotpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "hotpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
char *dir_list[] = {"baked", "sweet", "fried", ".", ".."};
|
||||
dir_file_check(dir_list, (sizeof(dir_list) / sizeof(dir_list[0])));
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("warmpotato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("warmpotato/mushy", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("hotpotato", "warmpotato");
|
||||
TEST_ASSERT_NOT_EQUAL(0, res);
|
||||
res = fs.remove("warmpotato/mushy");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("warmpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("hotpotato", "warmpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "warmpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
char *dir_list[] = {"baked", "sweet", "fried", ".", ".."};
|
||||
dir_file_check(dir_list, (sizeof(dir_list) / sizeof(dir_list[0])));
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("coldpotato", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("warmpotato/baked", "coldpotato/baked");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("warmpotato/sweet", "coldpotato/sweet");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.rename("warmpotato/fried", "coldpotato/fried");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.remove("coldpotato");
|
||||
TEST_ASSERT_NOT_EQUAL(0, res);
|
||||
res = fs.remove("warmpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "coldpotato");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
char *dir_list[] = {"baked", "sweet", "fried", ".", ".."};
|
||||
dir_file_check(dir_list, (sizeof(dir_list) / sizeof(dir_list[0])));
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("Directory tests", test_directory_tests),
|
||||
Case("Root directory", test_root_directory),
|
||||
Case("Directory creation", test_directory_creation),
|
||||
Case("File creation", test_file_creation),
|
||||
Case("Directory iteration", test_directory_iteration),
|
||||
Case("Directory failures", test_directory_failures),
|
||||
Case("Nested directories", test_nested_directories),
|
||||
Case("Multi-block directory", test_multi_block_directory),
|
||||
Case("Directory remove", test_directory_remove),
|
||||
Case("Directory rename", test_directory_rename),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM FATFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#define MBED_TEST_BLOCKDEVICE SDBlockDevice
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 120
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
static char file_counter = 0;
|
||||
const char *filenames[] = {"smallavacado", "mediumavacado", "largeavacado",
|
||||
"blockfile", "bigblockfile", "hello", ".", ".."
|
||||
};
|
||||
|
||||
// tests
|
||||
|
||||
void test_file_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello", O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
size = strlen("Hello World!\n");
|
||||
memcpy(wbuffer, "Hello World!\n", size);
|
||||
res = file[0].write(wbuffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
size = strlen("Hello World!\n");
|
||||
res = file[0].read(rbuffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(rbuffer, wbuffer, size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
template <int file_size, int write_size, int read_size>
|
||||
void test_write_file_test()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
size_t size = file_size;
|
||||
size_t chunk = write_size;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, filenames[file_counter], O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
for (size_t b = 0; b < chunk; b++) {
|
||||
buffer[b] = rand() & 0xff;
|
||||
}
|
||||
res = file[0].write(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = file_size;
|
||||
size_t chunk = read_size;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, filenames[file_counter], O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = file[0].read(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i + b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
file_counter++;
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_non_overlap_check()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
size_t size = 32;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "smallavacado", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = file[0].read(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i + b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 8192;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "mediumavacado", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = file[0].read(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i + b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = 262144;
|
||||
size_t chunk = 29;
|
||||
srand(0);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "largeavacado", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (size_t i = 0; i < size; i += chunk) {
|
||||
chunk = (chunk < size - i) ? chunk : size - i;
|
||||
res = file[0].read(buffer, chunk);
|
||||
TEST_ASSERT_EQUAL(chunk, res);
|
||||
for (size_t b = 0; b < chunk && i + b < size; b++) {
|
||||
res = buffer[b];
|
||||
TEST_ASSERT_EQUAL(rand() & 0xff, res);
|
||||
}
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_dir_check()
|
||||
{
|
||||
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "/");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
int numFiles = sizeof(filenames) / sizeof(filenames[0]);
|
||||
// Check the filenames in directory
|
||||
while (1) {
|
||||
res = dir[0].read(&ent);
|
||||
if (0 == res) {
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < numFiles ; i++) {
|
||||
res = strcmp(ent.d_name, filenames[i]);
|
||||
if (0 == res) {
|
||||
res = ent.d_type;
|
||||
if ((DT_REG != res) && (DT_DIR != res)) {
|
||||
TEST_ASSERT(1);
|
||||
}
|
||||
break;
|
||||
} else if ( i == numFiles) {
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("File tests", test_file_tests),
|
||||
Case("Simple file test", test_simple_file_test),
|
||||
Case("Small file test", test_write_file_test<32, 31, 29>),
|
||||
Case("Medium file test", test_write_file_test<8192, 31, 29>),
|
||||
Case("Large file test", test_write_file_test<262144, 31, 29>),
|
||||
Case("Block Size file test", test_write_file_test<9000, 512, 512>),
|
||||
Case("Multiple block size file test", test_write_file_test<26215, MBED_TEST_BUFFER, MBED_TEST_BUFFER>),
|
||||
Case("Non-overlap check", test_non_overlap_check),
|
||||
Case("Dir check", test_dir_check),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,206 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM FATFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#define MBED_TEST_BLOCKDEVICE SDBlockDevice
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 512
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 120
|
||||
#endif
|
||||
|
||||
#ifndef MBED_THREAD_COUNT
|
||||
#define MBED_THREAD_COUNT MBED_TEST_FILES
|
||||
#endif
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
|
||||
volatile bool count_done = 0;
|
||||
|
||||
// tests
|
||||
|
||||
void test_file_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void write_file_data (char count)
|
||||
{
|
||||
|
||||
char filename[10];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
int res;
|
||||
|
||||
sprintf(filename, "%s%d", "data", count);
|
||||
res = file[count].open(&fs, filename, O_WRONLY | O_CREAT);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
char letter = 'A' + count;
|
||||
for (uint32_t i = 0; i < MBED_TEST_BUFFER; i++) {
|
||||
wbuffer[i] = letter++;
|
||||
if ('z' == letter) {
|
||||
letter = 'A' + count;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < 5; i++) {
|
||||
res = file[count].write(wbuffer, MBED_TEST_BUFFER);
|
||||
TEST_ASSERT_EQUAL(MBED_TEST_BUFFER, res);
|
||||
}
|
||||
|
||||
res = file[count].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void read_file_data (char count)
|
||||
{
|
||||
char filename[10];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
int res;
|
||||
|
||||
sprintf(filename, "%s%d", "data", count);
|
||||
res = file[count].open(&fs, filename, O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
for (uint32_t i = 0; i < 5; i++) {
|
||||
res = file[count].read(rbuffer, MBED_TEST_BUFFER);
|
||||
TEST_ASSERT_EQUAL(MBED_TEST_BUFFER, res);
|
||||
char letter = 'A' + count;
|
||||
for (uint32_t i = 0; i < MBED_TEST_BUFFER; i++) {
|
||||
res = rbuffer[i];
|
||||
TEST_ASSERT_EQUAL(letter++, res);
|
||||
if ('z' == letter) {
|
||||
letter = 'A' + count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res = file[count].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_thread_access_test()
|
||||
{
|
||||
|
||||
Thread *data[MBED_THREAD_COUNT];
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
// Write threads in parallel
|
||||
for (char thread_count = 0; thread_count < MBED_THREAD_COUNT; thread_count++) {
|
||||
data[thread_count] = new Thread(osPriorityNormal);
|
||||
data[thread_count]->start(callback((void(*)(void *))write_file_data, (void *)thread_count));
|
||||
}
|
||||
|
||||
// Wait for write thread to join before creating read thread
|
||||
for (char thread_count = 0; thread_count < MBED_THREAD_COUNT; thread_count++) {
|
||||
data[thread_count]->join();
|
||||
delete data[thread_count];
|
||||
data[thread_count] = new Thread(osPriorityNormal);
|
||||
data[thread_count]->start(callback((void(*)(void *))read_file_data, (void *)thread_count));
|
||||
}
|
||||
|
||||
// Wait for read threads to join
|
||||
for (char thread_count = 0; thread_count < MBED_THREAD_COUNT; thread_count++) {
|
||||
data[thread_count]->join();
|
||||
delete data[thread_count];
|
||||
}
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("File tests", test_file_tests),
|
||||
Case("Filesystem access from multiple threads", test_thread_access_test),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,656 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 "mbed.h"
|
||||
#include "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
// test configuration
|
||||
#ifndef MBED_TEST_FILESYSTEM
|
||||
#define MBED_TEST_FILESYSTEM FATFileSystem
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILESYSTEM_DECL
|
||||
#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE
|
||||
#define MBED_TEST_BLOCKDEVICE SDBlockDevice
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BLOCKDEVICE_DECL
|
||||
#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_FILES
|
||||
#define MBED_TEST_FILES 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_DIRS
|
||||
#define MBED_TEST_DIRS 4
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_BUFFER
|
||||
#define MBED_TEST_BUFFER 8192
|
||||
#endif
|
||||
|
||||
#ifndef MBED_TEST_TIMEOUT
|
||||
#define MBED_TEST_TIMEOUT 120
|
||||
#endif
|
||||
|
||||
|
||||
// declarations
|
||||
#define STRINGIZE(x) STRINGIZE2(x)
|
||||
#define STRINGIZE2(x) #x
|
||||
#define INCLUDE(x) STRINGIZE(x.h)
|
||||
|
||||
#include INCLUDE(MBED_TEST_FILESYSTEM)
|
||||
#include INCLUDE(MBED_TEST_BLOCKDEVICE)
|
||||
|
||||
MBED_TEST_FILESYSTEM_DECL;
|
||||
MBED_TEST_BLOCKDEVICE_DECL;
|
||||
|
||||
Dir dir[MBED_TEST_DIRS];
|
||||
File file[MBED_TEST_FILES];
|
||||
DIR *dd[MBED_TEST_DIRS];
|
||||
FILE *fd[MBED_TEST_FILES];
|
||||
struct dirent ent;
|
||||
struct dirent *ed;
|
||||
size_t size;
|
||||
uint8_t buffer[MBED_TEST_BUFFER];
|
||||
uint8_t rbuffer[MBED_TEST_BUFFER];
|
||||
uint8_t wbuffer[MBED_TEST_BUFFER];
|
||||
|
||||
|
||||
// tests
|
||||
|
||||
void test_seek_tests()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = MBED_TEST_FILESYSTEM::format(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.mkdir("hello", 0777);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
for (int i = 0; i < 132; i++) {
|
||||
sprintf((char *)buffer, "hello/kitty%d", i);
|
||||
res = file[0].open(&fs, (char *)buffer,
|
||||
O_WRONLY | O_CREAT | O_APPEND);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size = strlen("kittycatcat");
|
||||
memcpy(buffer, "kittycatcat", size);
|
||||
for (int j = 0; j < 132; j++) {
|
||||
file[0].write(buffer, size);
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_dir_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "hello");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
#if (MBED_TEST_FILESYSTEM != FATFileSystem)
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
#endif
|
||||
|
||||
off_t pos;
|
||||
int i;
|
||||
for (i = 0; i < 4; i++) {
|
||||
sprintf((char *)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char *)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = dir[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
dir[0].seek(pos);
|
||||
sprintf((char *)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char *)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
dir[0].rewind();
|
||||
sprintf((char *)buffer, "kitty%d", 0);
|
||||
#if (MBED_TEST_FILESYSTEM != FATFileSystem)
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
#endif
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char *)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
dir[0].seek(pos);
|
||||
sprintf((char *)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char *)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_large_dir_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].open(&fs, "hello");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
#if (MBED_TEST_FILESYSTEM != FATFileSystem)
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
#endif
|
||||
|
||||
off_t pos;
|
||||
int i;
|
||||
for (i = 0; i < 128; i++) {
|
||||
sprintf((char *)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char *)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = dir[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
dir[0].seek(pos);
|
||||
sprintf((char *)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char *)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
dir[0].rewind();
|
||||
sprintf((char *)buffer, "kitty%d", 0);
|
||||
#if (MBED_TEST_FILESYSTEM != FATFileSystem)
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, ".");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, "..");
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
#endif
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char *)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
dir[0].seek(pos);
|
||||
sprintf((char *)buffer, "kitty%d", i);
|
||||
res = dir[0].read(&ent);
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = strcmp(ent.d_name, (char *)buffer);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = dir[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_file_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = file[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
file[0].rewind();
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_END) >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size_t size = file[0].size();
|
||||
res = file[0].seek(0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_large_file_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDONLY);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 128; i++) {
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = file[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
file[0].rewind();
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_END) >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size_t size = file[0].size();
|
||||
res = file[0].seek(0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_simple_file_seek_and_write()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDWR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
pos = file[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
memcpy(buffer, "doggodogdog", size);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].write(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
file[0].rewind();
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_END) >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size_t size = file[0].size();
|
||||
res = file[0].seek(0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_large_file_seek_and_write()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDWR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
off_t pos;
|
||||
size = strlen("kittycatcat");
|
||||
for (int i = 0; i < 128; i++) {
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
if (i != 4) {
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
pos = file[0].tell();
|
||||
}
|
||||
res = pos >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
|
||||
memcpy(buffer, "doggodogdog", size);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].write(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
file[0].rewind();
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(pos, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(pos, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "doggodogdog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(-size, SEEK_END) >= 0;
|
||||
TEST_ASSERT_EQUAL(1, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size_t size = file[0].size();
|
||||
res = file[0].seek(0, SEEK_CUR);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_boundary_seek_and_write()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDWR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size = strlen("hedgehoghog");
|
||||
const off_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
|
||||
|
||||
for (int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
|
||||
off_t off = offsets[i];
|
||||
memcpy(buffer, "hedgehoghog", size);
|
||||
res = file[0].seek(off, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(off, res);
|
||||
res = file[0].write(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].seek(off, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(off, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "hedgehoghog", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(0, SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "kittycatcat", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].sync();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
void test_out_of_bounds_seek()
|
||||
{
|
||||
int res = bd.init();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
{
|
||||
res = fs.mount(&bd);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].open(&fs, "hello/kitty42", O_RDWR);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
size = strlen("kittycatcat");
|
||||
res = file[0].size();
|
||||
TEST_ASSERT_EQUAL(132 * size, res);
|
||||
res = file[0].seek((132 + 4) * size,
|
||||
SEEK_SET);
|
||||
TEST_ASSERT_EQUAL((132 + 4)*size, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
|
||||
memcpy(buffer, "porcupineee", size);
|
||||
res = file[0].write(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = file[0].seek((132 + 4) * size,
|
||||
SEEK_SET);
|
||||
TEST_ASSERT_EQUAL((132 + 4)*size, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
res = memcmp(buffer, "porcupineee", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = file[0].seek(132 * size,
|
||||
SEEK_SET);
|
||||
TEST_ASSERT_EQUAL(132 * size, res);
|
||||
res = file[0].read(buffer, size);
|
||||
TEST_ASSERT_EQUAL(size, res);
|
||||
#if (MBED_TEST_FILESYSTEM != FATFileSystem)
|
||||
// FatFs does not guarantee empty expanded buffer
|
||||
res = memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size);
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
#endif
|
||||
res = file[0].close();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
res = fs.unmount();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
res = bd.deinit();
|
||||
TEST_ASSERT_EQUAL(0, res);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("Seek tests", test_seek_tests),
|
||||
Case("Simple dir seek", test_simple_dir_seek),
|
||||
Case("Large dir seek", test_large_dir_seek),
|
||||
Case("Simple file seek", test_simple_file_seek),
|
||||
Case("Large file seek", test_large_file_seek),
|
||||
Case("Simple file seek and write", test_simple_file_seek_and_write),
|
||||
Case("Large file seek and write", test_large_file_seek_and_write),
|
||||
Case("Boundary seek and write", test_boundary_seek_and_write),
|
||||
Case("Out-of-bounds seek", test_out_of_bounds_seek),
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
{
|
||||
"name": "sd",
|
||||
"config": {
|
||||
"SPI_CS": "NC",
|
||||
"SPI_MOSI": "NC",
|
||||
"SPI_MISO": "NC",
|
||||
"SPI_CLK": "NC",
|
||||
"DEVICE_SPI": 1,
|
||||
"FSFAT_SDCARD_INSTALLED": 1,
|
||||
"CMD_TIMEOUT": 10000,
|
||||
"CMD0_IDLE_STATE_RETRIES": 5,
|
||||
"SD_INIT_FREQUENCY": 100000
|
||||
},
|
||||
"target_overrides": {
|
||||
"DISCO_F051R8": {
|
||||
"SPI_MOSI": "SPI_MOSI",
|
||||
"SPI_MISO": "SPI_MISO",
|
||||
"SPI_CLK": "SPI_SCK",
|
||||
"SPI_CS": "SPI_CS"
|
||||
},
|
||||
"DISCO_L475VG_IOT01A": {
|
||||
"SPI_MOSI": "SPI_MOSI",
|
||||
"SPI_MISO": "SPI_MISO",
|
||||
"SPI_CLK": "SPI_SCK",
|
||||
"SPI_CS": "SPI_CS"
|
||||
},
|
||||
"DISCO_L476VG": {
|
||||
"SPI_MOSI": "PE_15",
|
||||
"SPI_MISO": "PE_14",
|
||||
"SPI_CLK": "PE_13",
|
||||
"SPI_CS": "PE_12"
|
||||
},
|
||||
"K20D50M": {
|
||||
"SPI_MOSI": "PTD2",
|
||||
"SPI_MISO": "PTD3",
|
||||
"SPI_CLK": "PTD1",
|
||||
"SPI_CS": "PTC2"
|
||||
},
|
||||
"KL22F": {
|
||||
"SPI_MOSI": "PTD6",
|
||||
"SPI_MISO": "PTD7",
|
||||
"SPI_CLK": "PTD5",
|
||||
"SPI_CS": "PTD4"
|
||||
},
|
||||
"KL25Z": {
|
||||
"SPI_MOSI": "PTD2",
|
||||
"SPI_MISO": "PTD3",
|
||||
"SPI_CLK": "PTD1",
|
||||
"SPI_CS": "PTD0"
|
||||
},
|
||||
"KL43Z": {
|
||||
"SPI_MOSI": "PTD6",
|
||||
"SPI_MISO": "PTD7",
|
||||
"SPI_CLK": "PTD5",
|
||||
"SPI_CS": "PTD4"
|
||||
},
|
||||
"KL46Z": {
|
||||
"SPI_MOSI": "PTD6",
|
||||
"SPI_MISO": "PTD7",
|
||||
"SPI_CLK": "PTD5",
|
||||
"SPI_CS": "PTD4"
|
||||
},
|
||||
"K64F": {
|
||||
"SPI_MOSI": "PTE3",
|
||||
"SPI_MISO": "PTE1",
|
||||
"SPI_CLK": "PTE2",
|
||||
"SPI_CS": "PTE4"
|
||||
},
|
||||
"K66F": {
|
||||
"SPI_MOSI": "PTE3",
|
||||
"SPI_MISO": "PTE1",
|
||||
"SPI_CLK": "PTE2",
|
||||
"SPI_CS": "PTE4"
|
||||
},
|
||||
"LPC11U37H_401": {
|
||||
"SPI_MOSI": "SDMOSI",
|
||||
"SPI_MISO": "SDMISO",
|
||||
"SPI_CLK": "SDSCLK",
|
||||
"SPI_CS": "SDSSEL"
|
||||
},
|
||||
"LPC2368": {
|
||||
"SPI_MOSI": "p11",
|
||||
"SPI_MISO": "p12",
|
||||
"SPI_CLK": "p13",
|
||||
"SPI_CS": "p14"
|
||||
},
|
||||
"NUCLEO_F411RE": {
|
||||
"SPI_MOSI": "PC_3",
|
||||
"SPI_MISO": "PC_2",
|
||||
"SPI_CLK": "PC_7",
|
||||
"SPI_CS": "PB_9"
|
||||
},
|
||||
"NUCLEO_F429ZI": {
|
||||
"SPI_MOSI": "PC_12",
|
||||
"SPI_MISO": "PC_11",
|
||||
"SPI_CLK": "PC_10",
|
||||
"SPI_CS": "PA_15"
|
||||
},
|
||||
"DISCO_F429ZI": {
|
||||
"SPI_MOSI": "PC_12",
|
||||
"SPI_MISO": "PC_11",
|
||||
"SPI_CLK": "PC_10",
|
||||
"SPI_CS": "PA_15"
|
||||
},
|
||||
"NUCLEO_F746ZG": {
|
||||
"SPI_MOSI": "PC_12",
|
||||
"SPI_MISO": "PC_11",
|
||||
"SPI_CLK": "PC_10",
|
||||
"SPI_CS": "PA_15"
|
||||
},
|
||||
"NUCLEO_F767ZI": {
|
||||
"SPI_MOSI": "PC_12",
|
||||
"SPI_MISO": "PC_11",
|
||||
"SPI_CLK": "PC_10",
|
||||
"SPI_CS": "PA_15"
|
||||
},
|
||||
"NUCLEO_L031K6": {
|
||||
"SPI_MOSI": "SPI_MOSI",
|
||||
"SPI_MISO": "SPI_MISO",
|
||||
"SPI_CLK": "SPI_SCK",
|
||||
"SPI_CS": "SPI_CS"
|
||||
},
|
||||
"NUCLEO_L476RG": {
|
||||
"SPI_MOSI": "SPI_MOSI",
|
||||
"SPI_MISO": "SPI_MISO",
|
||||
"SPI_CLK": "SPI_SCK",
|
||||
"SPI_CS": "SPI_CS"
|
||||
},
|
||||
"NUMAKER_PFM_M453": {
|
||||
"SPI_MOSI": "PD_13",
|
||||
"SPI_MISO": "PD_14",
|
||||
"SPI_CLK": "PD_15",
|
||||
"SPI_CS": "PD_12"
|
||||
},
|
||||
"NUMAKER_PFM_M487": {
|
||||
"SPI_MOSI": "D11",
|
||||
"SPI_MISO": "D12",
|
||||
"SPI_CLK": "D13",
|
||||
"SPI_CS": "D10"
|
||||
},
|
||||
"NUMAKER_PFM_NUC472": {
|
||||
"SPI_MOSI": "PF_0",
|
||||
"SPI_MISO": "PD_15",
|
||||
"SPI_CLK": "PD_14",
|
||||
"SPI_CS": "PD_13"
|
||||
},
|
||||
"nRF51822": {
|
||||
"SPI_MOSI": "p12",
|
||||
"SPI_MISO": "p13",
|
||||
"SPI_CLK": "p15",
|
||||
"SPI_CS": "p14"
|
||||
},
|
||||
"UBLOX_C030_U201": {
|
||||
"SPI_MOSI": "D11",
|
||||
"SPI_MISO": "D12",
|
||||
"SPI_CLK": "D13",
|
||||
"SPI_CS": "D10"
|
||||
},
|
||||
"UBLOX_EVK_ODIN_W2": {
|
||||
"SPI_CS": "D9",
|
||||
"SPI_MOSI": "D11",
|
||||
"SPI_MISO": "D12",
|
||||
"SPI_CLK": "D13"
|
||||
},
|
||||
"MTB_UBLOX_ODIN_W2": {
|
||||
"SPI_CS": "PG_4",
|
||||
"SPI_MOSI": "PE_14",
|
||||
"SPI_MISO": "PE_13",
|
||||
"SPI_CLK": "PE_12"
|
||||
},
|
||||
"RZ_A1H": {
|
||||
"SPI_MOSI": "P8_5",
|
||||
"SPI_MISO": "P8_6",
|
||||
"SPI_CLK": "P8_3",
|
||||
"SPI_CS": "P8_4"
|
||||
},
|
||||
"GR_LYCHEE": {
|
||||
"SPI_MOSI": "P5_6",
|
||||
"SPI_MISO": "P5_7",
|
||||
"SPI_CLK": "P5_4",
|
||||
"SPI_CS": "P5_5"
|
||||
},
|
||||
"HEXIWEAR": {
|
||||
"SPI_MOSI": "PTE3",
|
||||
"SPI_MISO": "PTE1",
|
||||
"SPI_CLK": "PTE2",
|
||||
"SPI_CS": "PTE4"
|
||||
},
|
||||
"MTB_MTS_DRAGONFLY": {
|
||||
"SPI_MOSI": "SPI2_MOSI",
|
||||
"SPI_MISO": "SPI2_MISO",
|
||||
"SPI_CLK": "SPI2_SCK",
|
||||
"SPI_CS": "SPI_CS2"
|
||||
},
|
||||
"TB_SENSE_12": {
|
||||
"SPI_MOSI": "PC6",
|
||||
"SPI_MISO": "PC7",
|
||||
"SPI_CLK": "PC8",
|
||||
"SPI_CS": "PC9"
|
||||
},
|
||||
"LPC1768": {
|
||||
"SPI_MOSI": "p5",
|
||||
"SPI_MISO": "p6",
|
||||
"SPI_CLK": "p7",
|
||||
"SPI_CS": "p8"
|
||||
},
|
||||
"REALTEK_RTL8195AM": {
|
||||
"SPI_MOSI": "D11",
|
||||
"SPI_MISO": "D12",
|
||||
"SPI_CLK": "D13",
|
||||
"SPI_CS": "D9"
|
||||
},
|
||||
"NUCLEO_F207ZG": {
|
||||
"SPI_MOSI": "PC_12",
|
||||
"SPI_MISO": "PC_11",
|
||||
"SPI_CLK": "PC_10",
|
||||
"SPI_CS": "PA_15"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2016 ARM Limited
|
||||
*
|
||||
* 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 fsfat_debug.h
|
||||
*
|
||||
* component debug header file.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __FSFAT_DEBUG
|
||||
#define __FSFAT_DEBUG
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
/* Debug Support */
|
||||
|
||||
#define FSFAT_LOG_NONE 0
|
||||
#define FSFAT_LOG_ERR 1
|
||||
#define FSFAT_LOG_WARN 2
|
||||
#define FSFAT_LOG_NOTICE 3
|
||||
#define FSFAT_LOG_INFO 4
|
||||
#define FSFAT_LOG_DEBUG 5
|
||||
#define FSFAT_LOG_FENTRY 6
|
||||
|
||||
#define FSFAT_LOG(_fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
printf(_fmt, __VA_ARGS__); \
|
||||
}while(0);
|
||||
|
||||
#define noFSFAT_DEBUG
|
||||
#ifdef FSFAT_DEBUG
|
||||
|
||||
extern uint32_t fsfat_optDebug_g;
|
||||
extern uint32_t fsfat_optLogLevel_g;
|
||||
|
||||
|
||||
/* uncomment for asserts to work */
|
||||
/* #undef NDEBUG */
|
||||
// todo: port to mbedOSV3++ #include <core-util/assert.h>
|
||||
|
||||
#define FSFAT_INLINE
|
||||
// todo: port to mbedOSV3++ #define FSFAT_ASSERT CORE_UTIL_ASSERT
|
||||
#define FSFAT_ASSERT(...)
|
||||
|
||||
#define FSFAT_DBGLOG(_fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if(fsfat_optDebug_g && (fsfat_optLogLevel_g >= FSFAT_LOG_DEBUG)) \
|
||||
{ \
|
||||
printf(_fmt, __VA_ARGS__); \
|
||||
} \
|
||||
}while(0);
|
||||
|
||||
|
||||
#define FSFAT_ERRLOG(_fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if(fsfat_optDebug_g && (fsfat_optLogLevel_g >= FSFAT_LOG_ERR)) \
|
||||
{ \
|
||||
printf(_fmt, __VA_ARGS__); \
|
||||
} \
|
||||
}while(0);
|
||||
|
||||
|
||||
#define FSFAT_FENTRYLOG(_fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
if(fsfat_optDebug_g && (fsfat_optLogLevel_g >= FSFAT_LOG_FENTRY)) \
|
||||
{ \
|
||||
printf(_fmt, __VA_ARGS__); \
|
||||
} \
|
||||
}while(0);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#else
|
||||
#define FSFAT_ASSERT(_x) do { } while(0)
|
||||
#define FSFAT_INLINE inline
|
||||
#define FSFAT_DBGLOG(_fmt, ...) do { } while(0)
|
||||
#define FSFAT_ERRLOG(_fmt, ...) do { } while(0)
|
||||
#define FSFAT_FENTRYLOG(_fmt, ...) do { } while(0)
|
||||
#endif /* FSFAT_DEBUG */
|
||||
|
||||
|
||||
#endif /*__FSFAT_DEBUG*/
|
|
@ -0,0 +1,117 @@
|
|||
/* @file fsfat_test.c
|
||||
*
|
||||
* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2016 ARM Limited
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* test support code implementation file.
|
||||
*/
|
||||
|
||||
#include "fsfat_debug.h"
|
||||
#include "fsfat_test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
|
||||
|
||||
#ifdef FSFAT_DEBUG
|
||||
uint32_t fsfat_optDebug_g = 1;
|
||||
uint32_t fsfat_optLogLevel_g = FSFAT_LOG_NONE; /*FSFAT_LOG_NONE|FSFAT_LOG_ERR|FSFAT_LOG_DEBUG|FSFAT_LOG_FENTRY; */
|
||||
#endif
|
||||
|
||||
/* ruler for measuring text strings */
|
||||
/* 1 1 1 1 1 1 1 1 1 1 2 2 2 */
|
||||
/* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 */
|
||||
/* 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */
|
||||
|
||||
const uint8_t fsfat_test_byte_data_table[FSFAT_TEST_BYTE_DATA_TABLE_SIZE] = {
|
||||
0x2d, 0xf3, 0x31, 0x4c, 0x11, 0x4f, 0xde, 0x0d, 0xbd, 0xbc, 0xa6, 0x78, 0x36, 0x5c, 0x1d, 0x28,
|
||||
0x5f, 0xa9, 0x10, 0x65, 0x54, 0x45, 0x21, 0x1a, 0x88, 0xfe, 0x76, 0x45, 0xb9, 0xac, 0x65, 0x9a,
|
||||
0x34, 0x9d, 0x73, 0x10, 0xb4, 0xa9, 0x2e, 0x90, 0x95, 0x68, 0xac, 0xfe, 0xc5, 0x2d, 0x15, 0x03,
|
||||
0x34, 0x70, 0xf1, 0x1d, 0x48, 0xa1, 0xa0, 0xed, 0x5c, 0x2f, 0xf5, 0x2b, 0xb9, 0x84, 0xbb, 0x45,
|
||||
0x32, 0xdd, 0xb1, 0x33, 0x95, 0x2a, 0xbc, 0x26, 0xf0, 0x89, 0xba, 0xf4, 0xbd, 0xf9, 0x5d, 0x2e,
|
||||
0x6e, 0x11, 0xc6, 0xa7, 0x78, 0xfc, 0xc9, 0x0e, 0x6b, 0x38, 0xba, 0x14, 0x1b, 0xab, 0x4c, 0x20,
|
||||
0x91, 0xe4, 0xb0, 0xf1, 0x2b, 0x14, 0x07, 0x6b, 0xb5, 0xcd, 0xe3, 0x49, 0x75, 0xac, 0xe8, 0x98,
|
||||
0xf1, 0x58, 0x8f, 0xd9, 0xc4, 0x8f, 0x00, 0x17, 0xb5, 0x06, 0x6a, 0x33, 0xbd, 0xa7, 0x40, 0x5a,
|
||||
0xbf, 0x49, 0xf7, 0x27, 0x1b, 0x4c, 0x3e, 0x6f, 0xe3, 0x08, 0x1f, 0xfd, 0xa6, 0xd4, 0xc7, 0x5f,
|
||||
0xa4, 0xa6, 0x82, 0xad, 0x19, 0xd5, 0x5c, 0xd8, 0x3a, 0x49, 0x85, 0xc9, 0x21, 0x83, 0xf6, 0xc6,
|
||||
0x84, 0xf9, 0x76, 0x89, 0xf3, 0x2d, 0x17, 0x50, 0x97, 0x38, 0x48, 0x9a, 0xe1, 0x82, 0xcd, 0xac,
|
||||
0xa8, 0x1d, 0xd7, 0x96, 0x5e, 0xb3, 0x08, 0xa8, 0x3a, 0xc7, 0x2b, 0x05, 0xaf, 0xdc, 0x16, 0xdf,
|
||||
0x48, 0x0f, 0x2a, 0x7e, 0x3a, 0x82, 0xd7, 0x80, 0xd6, 0x49, 0x27, 0x5d, 0xe3, 0x07, 0x62, 0xb3,
|
||||
0xc3, 0x6c, 0xba, 0xb2, 0xaa, 0x9f, 0xd9, 0x03, 0x0d, 0x27, 0xa8, 0xe0, 0xd6, 0xee, 0x79, 0x4b,
|
||||
0xd6, 0x97, 0x99, 0xb7, 0x11, 0xd6, 0x0d, 0x34, 0xae, 0x99, 0x4a, 0x93, 0x95, 0xd0, 0x5a, 0x34,
|
||||
0x19, 0xa2, 0x69, 0x57, 0xcf, 0x7c, 0x3d, 0x98, 0x88, 0x5d, 0x04, 0xf2, 0xd7, 0xac, 0xa5, 0x63
|
||||
};
|
||||
|
||||
|
||||
/* @brief test utility function to delete the file identified by filename
|
||||
*/
|
||||
int32_t fsfat_test_delete(const char* filename)
|
||||
{
|
||||
FSFAT_FENTRYLOG("%s:entered.\r\n", __func__);
|
||||
return remove(filename);
|
||||
}
|
||||
|
||||
|
||||
/* @brief test utility function to create a file
|
||||
*
|
||||
* @param filename name of the file including path
|
||||
* @param data data to store in file
|
||||
* @param len number of bytes of data present in the data buffer.
|
||||
*/
|
||||
int32_t fsfat_test_create(const char* filename, const char* data, size_t len)
|
||||
{
|
||||
int32_t ret = -1;
|
||||
FILE *fp = NULL;
|
||||
|
||||
FSFAT_FENTRYLOG("%s:entered (filename=%s, len=%d).\n", __func__, filename, (int) len);
|
||||
fp = fopen(filename, "w+");
|
||||
if(fp == NULL){
|
||||
return ret;
|
||||
}
|
||||
ret = fwrite((const void*) data, len, 1, fp);
|
||||
if(ret < 0){
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
fclose(fp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* @brief support function for generating a kv_name
|
||||
* @param name buffer to hold kv name
|
||||
* @param len length of kv name to generate
|
||||
*
|
||||
*/
|
||||
int32_t fsfat_test_filename_gen(char* name, const size_t len)
|
||||
{
|
||||
size_t i;
|
||||
uint32_t pos = 0;
|
||||
|
||||
const char* buf = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!$-_@";
|
||||
const int buf_len = strlen(buf);
|
||||
FSFAT_FENTRYLOG("%s:entered\n", __func__);
|
||||
for(i = 0; i < len; i++)
|
||||
{
|
||||
pos = rand() % (buf_len);
|
||||
name[i] = buf[pos];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/** @file fsfat_test.h
|
||||
*
|
||||
* mbed Microcontroller Library
|
||||
* Copyright (c) 2006-2016 ARM Limited
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Header file for test support data structures and function API.
|
||||
*/
|
||||
#ifndef __FSFAT_TEST_H
|
||||
#define __FSFAT_TEST_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Defines */
|
||||
//#define FSFAT_INIT_1_TABLE_HEAD { "a", ""}
|
||||
#define FSFAT_INIT_1_TABLE_MID_NODE { "/sd/01234567.txt", "abcdefghijklmnopqrstuvwxyz"}
|
||||
//#define FSFAT_INIT_1_TABLE_TAIL { "/sd/fopentst/hello/world/animal/wobbly/dog/foot/backrght.txt", "present"}
|
||||
#define FSFAT_TEST_RW_TABLE_SENTINEL 0xffffffff
|
||||
#define FSFAT_TEST_BYTE_DATA_TABLE_SIZE 256
|
||||
#define FSFAT_UTEST_MSG_BUF_SIZE 256
|
||||
#define FSFAT_UTEST_DEFAULT_TIMEOUT_MS 10000
|
||||
#define FSFAT_MBED_HOSTTEST_TIMEOUT 60
|
||||
#define FSFAT_MAX_FILE_BASENAME 8
|
||||
#define FSFAT_MAX_FILE_EXTNAME 3
|
||||
#define FSFAT_BUF_MAX_LENGTH 64
|
||||
#define FSFAT_FILENAME_MAX_LENGTH 255
|
||||
|
||||
|
||||
/* support macro for make string for utest _MESSAGE macros, which dont support formatted output */
|
||||
#define FSFAT_TEST_UTEST_MESSAGE(_buf, _max_len, _fmt, ...) \
|
||||
do \
|
||||
{ \
|
||||
snprintf((_buf), (_max_len), (_fmt), __VA_ARGS__); \
|
||||
}while(0);
|
||||
|
||||
|
||||
/*
|
||||
* Structures
|
||||
*/
|
||||
|
||||
/* kv data for test */
|
||||
typedef struct fsfat_kv_data_t {
|
||||
const char* filename;
|
||||
const char* value;
|
||||
} fsfat_kv_data_t;
|
||||
|
||||
|
||||
extern const uint8_t fsfat_test_byte_data_table[FSFAT_TEST_BYTE_DATA_TABLE_SIZE];
|
||||
|
||||
int32_t fsfat_test_create(const char* filename, const char* data, size_t len);
|
||||
int32_t fsfat_test_delete(const char* key_name);
|
||||
int32_t fsfat_test_filename_gen(char* name, const size_t len);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __FSFAT_TEST_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,295 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* 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 MBED_SPIF_BLOCK_DEVICE_H
|
||||
#define MBED_SPIF_BLOCK_DEVICE_H
|
||||
|
||||
#include "SPI.h"
|
||||
#include "DigitalOut.h"
|
||||
#include "BlockDevice.h"
|
||||
|
||||
/** Enum spif standard error codes
|
||||
*
|
||||
* @enum spif_bd_error
|
||||
*/
|
||||
enum spif_bd_error {
|
||||
SPIF_BD_ERROR_OK = 0, /*!< no error */
|
||||
SPIF_BD_ERROR_DEVICE_ERROR = BD_ERROR_DEVICE_ERROR, /*!< device specific error -4001 */
|
||||
SPIF_BD_ERROR_PARSING_FAILED = -4002, /* SFDP Parsing failed */
|
||||
SPIF_BD_ERROR_READY_FAILED = -4003, /* Wait for Mem Ready failed */
|
||||
SPIF_BD_ERROR_WREN_FAILED = -4004, /* Write Enable Failed */
|
||||
};
|
||||
|
||||
|
||||
#define SPIF_MAX_REGIONS 10
|
||||
#define MAX_NUM_OF_ERASE_TYPES 4
|
||||
|
||||
/** BlockDevice for SFDP based flash devices over SPI bus
|
||||
*
|
||||
* @code
|
||||
* // Here's an example using SPI flash device on K82F target
|
||||
* #include "mbed.h"
|
||||
* #include "SPIFBlockDevice.h"
|
||||
*
|
||||
* // Create flash device on SPI bus with PTE5 as chip select
|
||||
* SPIFBlockDevice spif(PTE2, PTE4, PTE1, PTE5);
|
||||
*
|
||||
* int main() {
|
||||
* printf("spif test\n");
|
||||
*
|
||||
* // Initialize the SPI flash device and print the memory layout
|
||||
* spif.init();
|
||||
* printf("spif size: %llu\n", spif.size());
|
||||
* printf("spif read size: %llu\n", spif.get_read_size());
|
||||
* printf("spif program size: %llu\n", spif.get_program_size());
|
||||
* printf("spif erase size: %llu\n", spif.get_erase_size());
|
||||
*
|
||||
* // Write "Hello World!" to the first block
|
||||
* char *buffer = (char*)malloc(spif.get_erase_size());
|
||||
* sprintf(buffer, "Hello World!\n");
|
||||
* spif.erase(0, spif.get_erase_size());
|
||||
* spif.program(buffer, 0, spif.get_erase_size());
|
||||
*
|
||||
* // Read back what was stored
|
||||
* spif.read(buffer, 0, spif.get_erase_size());
|
||||
* printf("%s", buffer);
|
||||
*
|
||||
* // Deinitialize the device
|
||||
* spif.deinit();
|
||||
* }
|
||||
* @endcode
|
||||
*/
|
||||
class SPIFBlockDevice : public BlockDevice {
|
||||
public:
|
||||
/** Creates a SPIFBlockDevice on a SPI bus specified by pins
|
||||
*
|
||||
* @param mosi SPI master out, slave in pin
|
||||
* @param miso SPI master in, slave out pin
|
||||
* @param sclk SPI clock pin
|
||||
* @param csel SPI chip select pin
|
||||
* @param freq Clock speed of the SPI bus (defaults to 40MHz)
|
||||
*/
|
||||
SPIFBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName csel, int freq = 40000000);
|
||||
|
||||
/** Initialize a block device
|
||||
*
|
||||
* @return SPIF_BD_ERROR_OK(0) - success
|
||||
* SPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed
|
||||
* SPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timedout
|
||||
* SPIF_BD_ERROR_PARSING_FAILED - unexpected format or values in one of the SFDP tables
|
||||
*/
|
||||
virtual int init();
|
||||
|
||||
/** Deinitialize a block device
|
||||
*
|
||||
* @return SPIF_BD_ERROR_OK(0) - success
|
||||
*/
|
||||
virtual int deinit();
|
||||
|
||||
/** Desctruct SPIFBlockDevie
|
||||
*/
|
||||
~SPIFBlockDevice() {deinit();}
|
||||
|
||||
/** Read blocks from a block device
|
||||
*
|
||||
* @param buffer Buffer to write blocks to
|
||||
* @param addr Address of block to begin reading from
|
||||
* @param size Size to read in bytes, must be a multiple of read block size
|
||||
* @return SPIF_BD_ERROR_OK(0) - success
|
||||
* SPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed
|
||||
*/
|
||||
virtual int read(void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Program blocks to a block device
|
||||
*
|
||||
* The blocks must have been erased prior to being programmed
|
||||
*
|
||||
* @param buffer Buffer of data to write to blocks
|
||||
* @param addr Address of block to begin writing to
|
||||
* @param size Size to write in bytes, must be a multiple of program block size
|
||||
* @return SPIF_BD_ERROR_OK(0) - success
|
||||
* SPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed
|
||||
* SPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out
|
||||
* SPIF_BD_ERROR_WREN_FAILED - Write Enable failed
|
||||
*/
|
||||
virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Erase blocks on a block device
|
||||
*
|
||||
* The state of an erased block is undefined until it has been programmed
|
||||
*
|
||||
* @param addr Address of block to begin erasing
|
||||
* @param size Size to erase in bytes, must be a multiple of erase block size
|
||||
* @return SPIF_BD_ERROR_OK(0) - success
|
||||
* SPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed
|
||||
* SPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out
|
||||
* SPIF_BD_ERROR_WREN_FAILED - Write Enable failed
|
||||
*/
|
||||
virtual int erase(bd_addr_t addr, bd_size_t size);
|
||||
|
||||
/** Get the size of a readable block
|
||||
*
|
||||
* @return Size of a readable block in bytes
|
||||
*/
|
||||
virtual bd_size_t get_read_size() const;
|
||||
|
||||
/** Get the size of a programable block
|
||||
*
|
||||
* @return Size of a programable block in bytes
|
||||
* @note Must be a multiple of the read size
|
||||
*/
|
||||
virtual bd_size_t get_program_size() const;
|
||||
|
||||
/** Get the size of a eraseable block
|
||||
*
|
||||
* @return Size of a eraseable block in bytes
|
||||
* @note Must be a multiple of the program size
|
||||
*/
|
||||
virtual bd_size_t get_erase_size() const;
|
||||
|
||||
/** Get the size of minimal eraseable sector size of given address
|
||||
*
|
||||
* @param 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 bd_size_t get_erase_size(bd_addr_t addr);
|
||||
|
||||
/** 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;
|
||||
|
||||
/** Get the total size of the underlying device
|
||||
*
|
||||
* @return Size of the underlying device in bytes
|
||||
*/
|
||||
virtual bd_size_t size() const;
|
||||
|
||||
private:
|
||||
|
||||
// Internal functions
|
||||
|
||||
/****************************************/
|
||||
/* SFDP Detection and Parsing Functions */
|
||||
/****************************************/
|
||||
// Parse SFDP Headers and retrieve Basic Param and Sector Map Tables (if exist)
|
||||
int _sfdp_parse_sfdp_headers(uint32_t& basic_table_addr, size_t& basic_table_size,
|
||||
uint32_t& sector_map_table_addr, size_t& sector_map_table_size);
|
||||
|
||||
// Parse and Detect required Basic Parameters from Table
|
||||
int _sfdp_parse_basic_param_table(uint32_t basic_table_addr, size_t basic_table_size);
|
||||
|
||||
// Parse and read information required by Regions Secotr Map
|
||||
int _sfdp_parse_sector_map_table(uint32_t sector_map_table_addr, size_t sector_map_table_size);
|
||||
|
||||
// Detect fastest read Bus mode supported by device
|
||||
int _sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table_ptr, int basic_param_table_size, int& read_inst);
|
||||
|
||||
// Set Page size for program
|
||||
unsigned int _sfdp_detect_page_size(uint8_t *basic_param_table_ptr, int basic_param_table_size);
|
||||
|
||||
// Detect all supported erase types
|
||||
int _sfdp_detect_erase_types_inst_and_size(uint8_t *basic_param_table_ptr, int basic_param_table_size,
|
||||
int& erase4k_inst,
|
||||
int *erase_type_inst_arr, unsigned int *erase_type_size_arr);
|
||||
|
||||
/***********************/
|
||||
/* Utilities Functions */
|
||||
/***********************/
|
||||
// Find the region to which the given offset belong to
|
||||
int _utils_find_addr_region(bd_size_t offset);
|
||||
|
||||
// Iterate on all supported Erase Types of the Region to which the offset belong to.
|
||||
// Iterates from highest type to lowest
|
||||
int _utils_iterate_next_largest_erase_type(uint8_t& bitfield, int size, int offset, int boundry);
|
||||
|
||||
/********************************/
|
||||
/* Calls to SPI Driver APIs */
|
||||
/********************************/
|
||||
// Send Program => Write command to Driver
|
||||
spif_bd_error _spi_send_program_command(int prog_inst, const void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
// Send Read command to Driver
|
||||
//spif_bd_error _spi_send_read_command(uint8_t read_inst, void *buffer, bd_addr_t addr, bd_size_t size);
|
||||
spif_bd_error _spi_send_read_command(int read_inst, uint8_t *buffer, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
// Send Erase Instruction using command_transfer command to Driver
|
||||
spif_bd_error _spi_send_erase_command(int erase_inst, bd_addr_t addr, bd_size_t size);
|
||||
|
||||
// Send Generic command_transfer command to Driver
|
||||
spif_bd_error _spi_send_general_command(int instruction, bd_addr_t addr, char *tx_buffer,
|
||||
size_t tx_length, char *rx_buffer, size_t rx_length);
|
||||
|
||||
// Send set_frequency command to Driver
|
||||
spif_bd_error _spi_set_frequency(int freq);
|
||||
/********************************/
|
||||
|
||||
// Soft Reset Flash Memory
|
||||
int _reset_flash_mem();
|
||||
|
||||
// Configure Write Enable in Status Register
|
||||
int _set_write_enable();
|
||||
|
||||
// Wait on status register until write not-in-progress
|
||||
bool _is_mem_ready();
|
||||
|
||||
private:
|
||||
// Master side hardware
|
||||
mbed::SPI _spi;
|
||||
// Enable CS control (low/high) for SPI driver operatios
|
||||
mbed::DigitalOut _cs;
|
||||
|
||||
// Mutex is used to protect Flash device for some SPI Driver commands that must be done sequentially with no other commands in between
|
||||
// e.g. (1)Set Write Enable, (2)Program, (3)Wait Memory Ready
|
||||
static SingletonPtr<PlatformMutex> _mutex;
|
||||
|
||||
// Command Instructions
|
||||
int _read_instruction;
|
||||
int _prog_instruction;
|
||||
int _erase_instruction;
|
||||
int _erase4k_inst; // Legacy 4K erase instruction (default 0x20h)
|
||||
|
||||
// Up To 4 Erase Types are supported by SFDP (each with its own command Instruction and Size)
|
||||
int _erase_type_inst_arr[MAX_NUM_OF_ERASE_TYPES];
|
||||
unsigned int _erase_type_size_arr[MAX_NUM_OF_ERASE_TYPES];
|
||||
|
||||
// Sector Regions Map
|
||||
int _regions_count; //number of regions
|
||||
int _region_size_bytes[SPIF_MAX_REGIONS]; //regions size in bytes
|
||||
bd_size_t _region_high_boundary[SPIF_MAX_REGIONS]; //region high address offset boundary
|
||||
//Each Region can support a bit combination of any of the 4 Erase Types
|
||||
uint8_t _region_erase_types_bitfield[SPIF_MAX_REGIONS];
|
||||
unsigned int _min_common_erase_size; // minimal common erase size for all regions (0 if none exists)
|
||||
|
||||
unsigned int _page_size_bytes; // Page size - 256 Bytes default
|
||||
bd_size_t _device_size_bytes;
|
||||
|
||||
// Bus configuration
|
||||
unsigned int _address_size; // number of bytes for address
|
||||
unsigned int _read_dummy_and_mode_cycles; // Number of Dummy and Mode Bits required by Read Bus Mode
|
||||
unsigned int _write_dummy_and_mode_cycles; // Number of Dummy and Mode Bits required by Write Bus Mode
|
||||
unsigned int _dummy_and_mode_cycles; // Number of Dummy and Mode Bits required by Current Bus Mode
|
||||
uint32_t _init_ref_count;
|
||||
bool _is_initialized;
|
||||
};
|
||||
|
||||
#endif /* MBED_SPIF_BLOCK_DEVICE_H */
|
|
@ -0,0 +1,297 @@
|
|||
/* mbed Microcontroller Library
|
||||
* Copyright (c) 2018 ARM Limited
|
||||
*
|
||||
* 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 "greentea-client/test_env.h"
|
||||
#include "unity.h"
|
||||
#include "utest.h"
|
||||
#include "SPIFBlockDevice.h"
|
||||
#include "mbed_trace.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace utest::v1;
|
||||
|
||||
#define TEST_BLOCK_COUNT 10
|
||||
#define TEST_ERROR_MASK 16
|
||||
#define SPIF_TEST_NUM_OF_THREADS 5
|
||||
|
||||
const struct {
|
||||
const char *name;
|
||||
bd_size_t (BlockDevice::*method)() const;
|
||||
} ATTRS[] = {
|
||||
{"read size", &BlockDevice::get_read_size},
|
||||
{"program size", &BlockDevice::get_program_size},
|
||||
{"erase size", &BlockDevice::get_erase_size},
|
||||
{"total size", &BlockDevice::size},
|
||||
};
|
||||
|
||||
static SingletonPtr<PlatformMutex> _mutex;
|
||||
|
||||
// Mutex is protecting rand() per srand for buffer writing and verification.
|
||||
// Mutex is also protecting printouts for clear logs.
|
||||
// Mutex is NOT protecting Block Device actions: erase/program/read - which is the purpose of the multithreaded test!
|
||||
void basic_erase_program_read_test(SPIFBlockDevice& blockD, bd_size_t block_size, uint8_t *write_block,
|
||||
uint8_t *read_block, unsigned addrwidth)
|
||||
{
|
||||
int err = 0;
|
||||
_mutex->lock();
|
||||
// Find a random block
|
||||
bd_addr_t block = (rand() * block_size) % blockD.size();
|
||||
|
||||
// Use next random number as temporary seed to keep
|
||||
// the address progressing in the pseudorandom sequence
|
||||
unsigned seed = rand();
|
||||
|
||||
// Fill with random sequence
|
||||
srand(seed);
|
||||
for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) {
|
||||
write_block[i_ind] = 0xff & rand();
|
||||
}
|
||||
// Write, sync, and read the block
|
||||
utest_printf("\ntest %0*llx:%llu...", addrwidth, block, block_size);
|
||||
_mutex->unlock();
|
||||
|
||||
err = blockD.erase(block, block_size);
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
err = blockD.program(write_block, block, block_size);
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
err = blockD.read(read_block, block, block_size);
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
_mutex->lock();
|
||||
// Check that the data was unmodified
|
||||
srand(seed);
|
||||
int val_rand;
|
||||
for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) {
|
||||
val_rand = rand();
|
||||
if ( (0xff & val_rand) != read_block[i_ind] ) {
|
||||
utest_printf("\n Assert Failed Buf Read - block:size: %llx:%llu \n", block, block_size);
|
||||
utest_printf("\n pos: %llu, exp: %02x, act: %02x, wrt: %02x \n", i_ind, (0xff & val_rand), read_block[i_ind],
|
||||
write_block[i_ind] );
|
||||
}
|
||||
TEST_ASSERT_EQUAL(0xff & val_rand, read_block[i_ind]);
|
||||
}
|
||||
_mutex->unlock();
|
||||
}
|
||||
|
||||
void test_spif_random_program_read_erase()
|
||||
{
|
||||
utest_printf("\nTest Random Program Read Erase Starts..\n");
|
||||
|
||||
SPIFBlockDevice blockD(MBED_CONF_SPIF_DRIVER_SPI_MOSI, MBED_CONF_SPIF_DRIVER_SPI_MISO, MBED_CONF_SPIF_DRIVER_SPI_CLK,
|
||||
MBED_CONF_SPIF_DRIVER_SPI_CS);
|
||||
|
||||
int err = blockD.init();
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) {
|
||||
static const char *prefixes[] = {"", "k", "M", "G"};
|
||||
for (int i_ind = 3; i_ind >= 0; i_ind--) {
|
||||
bd_size_t size = (blockD.*ATTRS[atr].method)();
|
||||
if (size >= (1ULL << 10 * i_ind)) {
|
||||
utest_printf("%s: %llu%sbytes (%llubytes)\n",
|
||||
ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bd_size_t block_size = blockD.get_erase_size();
|
||||
unsigned addrwidth = ceil(log(float(blockD.size() - 1)) / log(float(16))) + 1;
|
||||
|
||||
uint8_t *write_block = new (std::nothrow) uint8_t[block_size];
|
||||
uint8_t *read_block = new (std::nothrow) uint8_t[block_size];
|
||||
if (!write_block || !read_block) {
|
||||
utest_printf("\n Not enough memory for test");
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
|
||||
basic_erase_program_read_test(blockD, block_size, write_block, read_block, addrwidth);
|
||||
}
|
||||
|
||||
err = blockD.deinit();
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
end:
|
||||
delete[] write_block;
|
||||
delete[] read_block;
|
||||
}
|
||||
|
||||
void test_spif_unaligned_program()
|
||||
{
|
||||
utest_printf("\nTest Unaligned Program Starts..\n");
|
||||
|
||||
SPIFBlockDevice blockD(MBED_CONF_SPIF_DRIVER_SPI_MOSI, MBED_CONF_SPIF_DRIVER_SPI_MISO, MBED_CONF_SPIF_DRIVER_SPI_CLK,
|
||||
MBED_CONF_SPIF_DRIVER_SPI_CS);
|
||||
|
||||
int err = blockD.init();
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) {
|
||||
static const char *prefixes[] = {"", "k", "M", "G"};
|
||||
for (int i_ind = 3; i_ind >= 0; i_ind--) {
|
||||
bd_size_t size = (blockD.*ATTRS[atr].method)();
|
||||
if (size >= (1ULL << 10 * i_ind)) {
|
||||
utest_printf("%s: %llu%sbytes (%llubytes)\n",
|
||||
ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bd_size_t block_size = blockD.get_erase_size();
|
||||
unsigned addrwidth = ceil(log(float(blockD.size() - 1)) / log(float(16))) + 1;
|
||||
|
||||
uint8_t *write_block = new (std::nothrow) uint8_t[block_size];
|
||||
uint8_t *read_block = new (std::nothrow) uint8_t[block_size];
|
||||
if (!write_block || !read_block ) {
|
||||
utest_printf("\n Not enough memory for test");
|
||||
goto end;
|
||||
}
|
||||
|
||||
{
|
||||
bd_addr_t block = (rand() * block_size) % blockD.size() + 1;
|
||||
|
||||
// Use next random number as temporary seed to keep
|
||||
// the address progressing in the pseudorandom sequence
|
||||
unsigned seed = rand();
|
||||
|
||||
// Fill with random sequence
|
||||
srand(seed);
|
||||
for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) {
|
||||
write_block[i_ind] = 0xff & rand();
|
||||
}
|
||||
|
||||
// Write, sync, and read the block
|
||||
utest_printf("\ntest %0*llx:%llu...", addrwidth, block, block_size);
|
||||
|
||||
err = blockD.erase(block, block_size);
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
//err = blockD.erase(block+4096, block_size);
|
||||
//TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
err = blockD.program(write_block, block, block_size);
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
err = blockD.read(read_block, block, block_size);
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
// Check that the data was unmodified
|
||||
srand(seed);
|
||||
for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) {
|
||||
//printf("index %d\n", i_ind);
|
||||
TEST_ASSERT_EQUAL(0xff & rand(), read_block[i_ind]);
|
||||
}
|
||||
|
||||
err = blockD.deinit();
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
}
|
||||
end:
|
||||
delete[] write_block;
|
||||
delete[] read_block;
|
||||
}
|
||||
|
||||
static void test_spif_thread_job(void *vBlockD/*, int thread_num*/)
|
||||
{
|
||||
static int thread_num = 0;
|
||||
thread_num++;
|
||||
SPIFBlockDevice *blockD = (SPIFBlockDevice *)vBlockD;
|
||||
utest_printf("\n Thread %d Started \n", thread_num);
|
||||
|
||||
bd_size_t block_size = blockD->get_erase_size();
|
||||
unsigned addrwidth = ceil(log(float(blockD->size() - 1)) / log(float(16))) + 1;
|
||||
|
||||
uint8_t *write_block = new (std::nothrow) uint8_t[block_size];
|
||||
uint8_t *read_block = new (std::nothrow) uint8_t[block_size];
|
||||
if (!write_block || !read_block ) {
|
||||
utest_printf("\n Not enough memory for test");
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
|
||||
basic_erase_program_read_test((*blockD), block_size, write_block, read_block, addrwidth);
|
||||
}
|
||||
|
||||
end:
|
||||
delete[] write_block;
|
||||
delete[] read_block;
|
||||
}
|
||||
|
||||
void test_spif_multi_threads()
|
||||
{
|
||||
utest_printf("\nTest Multi Threaded Erase/Program/Read Starts..\n");
|
||||
|
||||
SPIFBlockDevice blockD(MBED_CONF_SPIF_DRIVER_SPI_MOSI, MBED_CONF_SPIF_DRIVER_SPI_MISO, MBED_CONF_SPIF_DRIVER_SPI_CLK,
|
||||
MBED_CONF_SPIF_DRIVER_SPI_CS);
|
||||
|
||||
int err = blockD.init();
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
|
||||
for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) {
|
||||
static const char *prefixes[] = {"", "k", "M", "G"};
|
||||
for (int i_ind = 3; i_ind >= 0; i_ind--) {
|
||||
bd_size_t size = (blockD.*ATTRS[atr].method)();
|
||||
if (size >= (1ULL << 10 * i_ind)) {
|
||||
utest_printf("%s: %llu%sbytes (%llubytes)\n",
|
||||
ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rtos::Thread spif_bd_thread[SPIF_TEST_NUM_OF_THREADS];
|
||||
|
||||
osStatus threadStatus;
|
||||
int i_ind;
|
||||
|
||||
for (i_ind = 0; i_ind < SPIF_TEST_NUM_OF_THREADS; i_ind++) {
|
||||
threadStatus = spif_bd_thread[i_ind].start(test_spif_thread_job, (void *)&blockD);
|
||||
if (threadStatus != 0) {
|
||||
utest_printf("\n Thread %d Start Failed!", i_ind + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (i_ind = 0; i_ind < SPIF_TEST_NUM_OF_THREADS; i_ind++) {
|
||||
spif_bd_thread[i_ind].join();
|
||||
}
|
||||
|
||||
err = blockD.deinit();
|
||||
TEST_ASSERT_EQUAL(0, err);
|
||||
}
|
||||
|
||||
// Test setup
|
||||
utest::v1::status_t test_setup(const size_t number_of_cases)
|
||||
{
|
||||
GREENTEA_SETUP(60, "default_auto");
|
||||
return verbose_test_setup_handler(number_of_cases);
|
||||
}
|
||||
|
||||
Case cases[] = {
|
||||
Case("Testing unaligned program blocks", test_spif_unaligned_program),
|
||||
Case("Testing read write random blocks", test_spif_random_program_read_erase),
|
||||
Case("Testing Multi Threads Erase Program Read", test_spif_multi_threads)
|
||||
};
|
||||
|
||||
Specification specification(test_setup, cases);
|
||||
|
||||
int main()
|
||||
{
|
||||
mbed_trace_init();
|
||||
utest_printf("MAIN STARTS\n");
|
||||
return !Harness::run(specification);
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"name": "spif-driver",
|
||||
"config": {
|
||||
"SPI_MOSI": "NC",
|
||||
"SPI_MISO": "NC",
|
||||
"SPI_CLK": "NC",
|
||||
"SPI_CS": "NC",
|
||||
"SPI_FREQ": "40000000"
|
||||
},
|
||||
"target_overrides": {
|
||||
"K82F": {
|
||||
"SPI_MOSI": "PTE2",
|
||||
"SPI_MISO": "PTE4",
|
||||
"SPI_CLK": "PTE1",
|
||||
"SPI_CS": "PTE5"
|
||||
},
|
||||
"LPC54114": {
|
||||
"SPI_MOSI": "P0_20",
|
||||
"SPI_MISO": "P0_18",
|
||||
"SPI_CLK": "P0_19",
|
||||
"SPI_CS": "P1_2"
|
||||
},
|
||||
"NRF52840_DK": {
|
||||
"SPI_MOSI": "p20",
|
||||
"SPI_MISO": "p21",
|
||||
"SPI_CLK": "p19",
|
||||
"SPI_CS": "p17"
|
||||
},
|
||||
"HEXIWEAR": {
|
||||
"SPI_MOSI": "PTD6",
|
||||
"SPI_MISO": "PTD7",
|
||||
"SPI_CLK": "PTD5",
|
||||
"SPI_CS": "PTD4"
|
||||
},
|
||||
"MTB_UBLOX_ODIN_W2": {
|
||||
"SPI_MOSI": "PE_14",
|
||||
"SPI_MISO": "PE_13",
|
||||
"SPI_CLK": "PE_12",
|
||||
"SPI_CS": "PE_11"
|
||||
},
|
||||
"MTB_ADV_WISE_1530": {
|
||||
"SPI_MOSI": "PC_3",
|
||||
"SPI_MISO": "PC_2",
|
||||
"SPI_CLK": "PB_13",
|
||||
"SPI_CS": "PC_12"
|
||||
},
|
||||
"MTB_MXCHIP_EMW3166": {
|
||||
"SPI_MOSI": "PB_15",
|
||||
"SPI_MISO": "PB_14",
|
||||
"SPI_CLK": "PB_13",
|
||||
"SPI_CS": "PA_10"
|
||||
},
|
||||
"MTB_USI_WM_BN_BM_22": {
|
||||
"SPI_MOSI": "PC_3",
|
||||
"SPI_MISO": "PC_2",
|
||||
"SPI_CLK": "PB_13",
|
||||
"SPI_CS": "PA_6"
|
||||
},
|
||||
"MTB_ADV_WISE_1570": {
|
||||
"SPI_MOSI": "PA_7",
|
||||
"SPI_MISO": "PA_6",
|
||||
"SPI_CLK": "PA_5",
|
||||
"SPI_CS": "PB_12"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -43,6 +43,19 @@ typedef uint64_t bd_size_t;
|
|||
class BlockDevice
|
||||
{
|
||||
public:
|
||||
|
||||
/** Return the default block device
|
||||
*
|
||||
* Returns the default BlockDevice base on configuration json.
|
||||
* Use the components in target.json or application config to change
|
||||
* the default block device.
|
||||
*
|
||||
* An application can override all target settings by implementing
|
||||
* BlockDevice::get_default_instance() themselves - the default
|
||||
* definition is weak, and calls get_target_default_instance().
|
||||
*/
|
||||
static BlockDevice *get_default_instance();
|
||||
|
||||
/** Lifetime of a block device
|
||||
*/
|
||||
virtual ~BlockDevice() {};
|
|
@ -48,6 +48,7 @@ class File;
|
|||
*/
|
||||
class FileSystem : public FileSystemLike {
|
||||
public:
|
||||
|
||||
/** FileSystem lifetime
|
||||
*
|
||||
* @param name Name to add filesystem to tree as
|
||||
|
@ -55,6 +56,21 @@ public:
|
|||
FileSystem(const char *name = NULL);
|
||||
virtual ~FileSystem() {}
|
||||
|
||||
/** Return the default filesystem
|
||||
*
|
||||
* Returns the default FileSystem base on the default BlockDevice configuration.
|
||||
* Use the components in target.json or application config to change
|
||||
* the default block device and affect the default filesystem.
|
||||
* SD block device => FAT filesystem
|
||||
* SPIF block device => LITTLE filesystem
|
||||
* DATAFLASH block device => LITTLE filesystem
|
||||
*
|
||||
* An application can override all target settings by implementing
|
||||
* FileSystem::get_default_instance() themselves - the default
|
||||
* definition is weak, and calls get_target_default_instance().
|
||||
*/
|
||||
static FileSystem *get_default_instance();
|
||||
|
||||
/** Mounts a filesystem to a block device
|
||||
*
|
||||
* @param bd BlockDevice to mount to
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue