From 20492a186c9c9a03a93aacd421a7568277f59512 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Wed, 19 Jul 2017 11:26:13 +0100 Subject: [PATCH 01/10] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000000..8e62e54ebd --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# flashiap-driver +Block device driver using the mbed OS FlashIAP API From 0615435e1635fbd59b2e80a874e9c1353b6ae906 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Wed, 19 Jul 2017 17:21:41 +0100 Subject: [PATCH 02/10] Working block device * Tested on Realtek RTL8195AM --- FlashIAPBlockDevice.cpp | 157 +++ FlashIAPBlockDevice.h | 124 +++ LICENSE | 165 ++++ TESTS/block_device/basic/main.cpp | 132 +++ TESTS/filesystem/basic/basic.cpp | 926 ++++++++++++++++++ TESTS/filesystem/fopen/fopen.cpp | 1489 +++++++++++++++++++++++++++++ mbed_lib.json | 19 + util/fsfat_debug.h | 88 ++ util/fsfat_test.c | 117 +++ util/fsfat_test.h | 74 ++ 10 files changed, 3291 insertions(+) create mode 100644 FlashIAPBlockDevice.cpp create mode 100644 FlashIAPBlockDevice.h create mode 100644 LICENSE create mode 100644 TESTS/block_device/basic/main.cpp create mode 100644 TESTS/filesystem/basic/basic.cpp create mode 100644 TESTS/filesystem/fopen/fopen.cpp create mode 100644 mbed_lib.json create mode 100644 util/fsfat_debug.h create mode 100644 util/fsfat_test.c create mode 100644 util/fsfat_test.h diff --git a/FlashIAPBlockDevice.cpp b/FlashIAPBlockDevice.cpp new file mode 100644 index 0000000000..cdb0fec6f0 --- /dev/null +++ b/FlashIAPBlockDevice.cpp @@ -0,0 +1,157 @@ +/* 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 "FlashIAPBlockDevice.h" + +#include "mbed.h" + +#include + +#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(uint32_t address, uint32_t size) + : _flash(), _base(address), _size(size) +{ + DEBUG_PRINTF("FlashIAPBlockDevice: %" PRIX32 " %" PRIX32 "\r\n", address, size); +} + +int FlashIAPBlockDevice::init() +{ + DEBUG_PRINTF("init\r\n"); + + return _flash.init(); +} + +int FlashIAPBlockDevice::deinit() +{ + DEBUG_PRINTF("deinit\r\n"); + + 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_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_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_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 +{ + 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 +{ + 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::size() const +{ + DEBUG_PRINTF("size: %" PRIX64 "\r\n", _size); + + return _size; +} diff --git a/FlashIAPBlockDevice.h b/FlashIAPBlockDevice.h new file mode 100644 index 0000000000..5b915b82d4 --- /dev/null +++ b/FlashIAPBlockDevice.h @@ -0,0 +1,124 @@ +/* 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 + +#include "BlockDevice.h" +#include + +#ifndef MBED_CONF_FLASHIAP_BLOCK_DEVICE_BASE_ADDRESS +#error flashiap-block-device.base-address not set +#endif + +#ifndef MBED_CONF_FLASHIAP_BLOCK_DEVICE_SIZE +#error flashiap-block-device.size not set +#endif + +/** BlockDevice using the FlashIAP API + * + * @code + * #include "mbed.h" + * #include "FlashIAPBlockDevice.h" + * + */ +class FlashIAPBlockDevice : public BlockDevice { +public: + /** Creates a FlashIAPBlockDevice, starting at the given address and + * with a certain size. + * + * @param address Starting address for the BlockDevice + * @param size Maximum size allocated to this BlockDevice + */ + FlashIAPBlockDevice(uint32_t address = MBED_CONF_FLASHIAP_BLOCK_DEVICE_BASE_ADDRESS, + uint32_t size = MBED_CONF_FLASHIAP_BLOCK_DEVICE_SIZE); + + /** 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 total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual bd_size_t size() const; + +private: + // Device configuration + FlashIAP _flash; + bd_addr_t _base; + bd_size_t _size; +}; + +#endif /* MBED_FLASHIAP_BLOCK_DEVICE_H */ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..59cd3f8a32 --- /dev/null +++ b/LICENSE @@ -0,0 +1,165 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. diff --git a/TESTS/block_device/basic/main.cpp b/TESTS/block_device/basic/main.cpp new file mode 100644 index 0000000000..4b585a3e3f --- /dev/null +++ b/TESTS/block_device/basic/main.cpp @@ -0,0 +1,132 @@ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#include "FlashIAPBlockDevice.h" +#include + +using namespace utest::v1; + +#define TEST_BLOCK_COUNT 10 +#define TEST_ERROR_MASK 16 + +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}, +}; + + +void test_read_write() { + FlashIAPBlockDevice bd; + + int err = bd.init(); + TEST_ASSERT_EQUAL(0, err); + + for (unsigned a = 0; a < sizeof(ATTRS)/sizeof(ATTRS[0]); a++) { + static const char *prefixes[] = {"", "k", "M", "G"}; + for (int i = 3; i >= 0; i--) { + bd_size_t size = (bd.*ATTRS[a].method)(); + if (size >= (1ULL << 10*i)) { + printf("%s: %llu%sbytes (%llubytes)\n", + ATTRS[a].name, size >> 10*i, prefixes[i], size); + break; + } + } + } + + bd_size_t block_size = bd.get_erase_size(); + uint8_t *write_block = new uint8_t[block_size]; + uint8_t *read_block = new uint8_t[block_size]; + uint8_t *error_mask = new uint8_t[TEST_ERROR_MASK]; + unsigned addrwidth = ceil(log(float(bd.size()-1)) / log(float(16)))+1; + + for (int b = 0; b < TEST_BLOCK_COUNT; b++) { + // Find a random block + bd_addr_t block = (rand()*block_size) % bd.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 = 0; i < block_size; i++) { + write_block[i] = 0xff & rand(); + } + + // Write, sync, and read the block + printf("test %0*llx:%llu...\n", addrwidth, block, block_size); + + err = bd.erase(block, block_size); + TEST_ASSERT_EQUAL(0, err); + + err = bd.program(write_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + printf("write %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", write_block[i]); + } + printf("...\n"); + + err = bd.read(read_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + printf("read %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", read_block[i]); + } + printf("...\n"); + + // Find error mask for debugging + memset(error_mask, 0, TEST_ERROR_MASK); + bd_size_t error_scale = block_size / (TEST_ERROR_MASK*8); + + srand(seed); + for (bd_size_t i = 0; i < TEST_ERROR_MASK*8; i++) { + for (bd_size_t j = 0; j < error_scale; j++) { + if ((0xff & rand()) != read_block[i*error_scale + j]) { + error_mask[i/8] |= 1 << (i%8); + } + } + } + + printf("error %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", error_mask[i]); + } + printf("\n"); + + // Check that the data was unmodified + srand(seed); + for (bd_size_t i = 0; i < block_size; i++) { + TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]); + } + } + + err = bd.deinit(); + TEST_ASSERT_EQUAL(0, err); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(30, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Testing read write random blocks", test_read_write), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +} diff --git a/TESTS/filesystem/basic/basic.cpp b/TESTS/filesystem/basic/basic.cpp new file mode 100644 index 0000000000..63913227b0 --- /dev/null +++ b/TESTS/filesystem/basic/basic.cpp @@ -0,0 +1,926 @@ +/* + * 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. + * + */ + +/* The following copyright notice is reproduced from the glibc project + * REF_LICENCE_GLIBC + * + * Copyright (C) 1991, 1992 Free Software Foundation, Inc. + * This file is part of the GNU C Library. + * + * The GNU C Library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * The GNU C Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the GNU C Library; see the file COPYING.LIB. If + * not, write to the Free Software Foundation, Inc., 675 Mass Ave, + * Cambridge, MA 02139, USA. + */ + + +/** @file basic.cpp POSIX File API (stdio) test cases + * + * Consult the documentation under the test-case functions for + * a description of the individual test case. + * + * this file includes ports for the mbed 2 test cases from the following locations: + * - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/dir_sd/main.cpp. + * - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/file/main.cpp. + * - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/sd/main.cpp + * - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_handle/main.cpp + * - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_stdio/main.cpp. + */ + +#include "mbed.h" +#include "mbed_config.h" +#include "FATFileSystem.h" +#include "test_env.h" +#include "fsfat_debug.h" +#include "fsfat_test.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" + +#include +#include +#include +#include +#include +/* retarget.h is included after errno.h so symbols are mapped to + * consistent values for all toolchains */ +#include "platform/mbed_retarget.h" + +using namespace utest::v1; + +/* DEVICE_SPI + * This symbol is defined in targets.json if the target has a SPI interface, which is required for SDCard support. + * + * MBED_CONF_APP_FSFAT_SDCARD_INSTALLED + * For testing purposes, an SDCard must be installed on the target for the test cases in this file to succeed. + * If the target has an SD card installed then the MBED_CONF_APP_FSFAT_SDCARD_INSTALLED will be generated + * from the mbed_app.json, which includes the line + * { + * "config": { + * "UART_RX": "D0", + * <<< lines removed >>> + * "DEVICE_SPI": 1, + * "MBED_CONF_APP_FSFAT_SDCARD_INSTALLED": 1 + * }, + * <<< lines removed >>> + */ + +//#include "SDBlockDevice.h" +//SDBlockDevice sd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS); + +#include "FlashIAPBlockDevice.h" + +FlashIAPBlockDevice sd; +FATFileSystem fs("sd", &sd); + +#define FSFAT_BASIC_TEST_ fsfat_basic_test_ +#define FSFAT_BASIC_TEST_00 fsfat_basic_test_00 +#define FSFAT_BASIC_TEST_01 fsfat_basic_test_01 +#define FSFAT_BASIC_TEST_02 fsfat_basic_test_02 +#define FSFAT_BASIC_TEST_03 fsfat_basic_test_03 +#define FSFAT_BASIC_TEST_04 fsfat_basic_test_04 +#define FSFAT_BASIC_TEST_05 fsfat_basic_test_05 +#define FSFAT_BASIC_TEST_06 fsfat_basic_test_06 +#define FSFAT_BASIC_TEST_07 fsfat_basic_test_07 +#define FSFAT_BASIC_TEST_08 fsfat_basic_test_08 +#define FSFAT_BASIC_TEST_09 fsfat_basic_test_09 +#define FSFAT_BASIC_TEST_10 fsfat_basic_test_10 + +#define FSFAT_BASIC_MSG_BUF_SIZE 256 +#define FSFAT_BASIC_TEST_05_TEST_STRING "Hello World!" + +static const char *sd_file_path = "/sd/out.txt"; +static const int FSFAT_BASIC_DATA_SIZE = 256; +static char fsfat_basic_msg_g[FSFAT_BASIC_MSG_BUF_SIZE]; +static char fsfat_basic_buffer[1024]; +static const int FSFAT_BASIC_KIB_RW = 128; +static Timer fsfat_basic_timer; +static const char *fsfat_basic_bin_filename = "/sd/testfile.bin"; +static const char *fsfat_basic_bin_filename_test_08 = "testfile.bin"; +static const char *fsfat_basic_bin_filename_test_10 = "0:testfile.bin"; + +#define FSFAT_BASIC_MSG(_buf, _max_len, _fmt, ...) \ + do \ + { \ + snprintf((_buf), (_max_len), (_fmt), __VA_ARGS__); \ + }while(0); + +/** @brief test for operation of SDFileSystem::format() + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_basic_test_(const size_t call_count) +{ + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + int32_t ret = -1; + + /* the allocation_unit of 0 means chanFS will use the default for the card (varies according to capacity). */ + fs.unmount(); + ret = fs.format(&sd); + FSFAT_TEST_UTEST_MESSAGE(fsfat_basic_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to format sdcard (ret=%d)\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + fs.mount(&sd); + return CaseNext; +} + +/** @brief fopen test case + * + * - open a file + * - generate random data items, write the item to the file and store a coy in a buffer for later use. + * - close the file. + * - open the file. + * - read the data items from the file and check they are the same as write. + * - close the file. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_00() +{ + uint8_t data_written[FSFAT_BASIC_DATA_SIZE] = { 0 }; + bool read_result = false; + bool write_result = false; + + // Fill data_written buffer with random data + // Write these data into the file + FSFAT_FENTRYLOG("%s:entered\n", __func__); + { + FSFAT_DBGLOG("%s:SD: Writing ... ", __func__); + FILE *f = fopen(sd_file_path, "w"); + if (f) { + for (int i = 0; i < FSFAT_BASIC_DATA_SIZE; i++) { + data_written[i] = rand() % 0XFF; + fprintf(f, "%c", data_written[i]); + } + write_result = true; + fclose(f); + } + FSFAT_DBGLOG("[%s]\n", write_result ? "OK" : "FAIL"); + } + TEST_ASSERT_MESSAGE(write_result == true, "Error: write_result is set to false."); + + // Read back the data from the file and store them in data_read + { + FSFAT_DBGLOG("%s:SD: Reading data ... ", __func__); + FILE *f = fopen(sd_file_path, "r"); + if (f) { + read_result = true; + for (int i = 0; i < FSFAT_BASIC_DATA_SIZE; i++) { + uint8_t data = fgetc(f); + if (data != data_written[i]) { + read_result = false; + break; + } + } + fclose(f); + } + FSFAT_DBGLOG("[%s]\n", read_result ? "OK" : "FAIL"); + } + TEST_ASSERT_MESSAGE(read_result == true, "Error: read_result is set to false."); + return CaseNext; +} + + +/** @brief test-fseek.c test ported from glibc project. See the licence at REF_LICENCE_GLIBC. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_01() +{ + FILE *fp, *fp1; + int i, j; + int ret = 0; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + fp = fopen (sd_file_path, "w+"); + if (fp == NULL) { + FSFAT_DBGLOG("errno=%d\n", errno); + TEST_ASSERT_MESSAGE(false, "error"); + return CaseNext; + } + + for (i = 0; i < 256; i++) { + putc (i, fp); + } + /* FIXME: freopen() should open the specified file closing the first stream. As can be seen from the + * code below, the old file descriptor fp can still be used, and this should not happen. + */ + fp1 = freopen (sd_file_path, "r", fp); + TEST_ASSERT_MESSAGE(fp1 == fp, "Error: cannot open file for reading"); + + for (i = 1; i <= 255; i++) { + ret = fseek (fp, (long) -i, SEEK_END); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s:Error: fseek() failed (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + + if ((j = getc (fp)) != 256 - i) { + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: SEEK_END failed (j=%d)\n", __func__, j); + TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g); + } + ret = fseek (fp, (long) i, SEEK_SET); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot SEEK_SET (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + + if ((j = getc (fp)) != i) { + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot SEEK_SET (j=%d).\n", __func__, j); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + } + if ((ret = fseek (fp, (long) i, SEEK_SET))) { + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot SEEK_SET (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + } + if ((ret = fseek (fp, (long) (i >= 128 ? -128 : 128), SEEK_CUR))) { + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot SEEK_CUR (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + } + if ((j = getc (fp)) != (i >= 128 ? i - 128 : i + 128)) { + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot SEEK_CUR (j=%d).\n", __func__, j); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + } + } + fclose (fp); + remove(sd_file_path); + return CaseNext; +} + + +/** @brief test_rdwr.c test ported from glibc project. See the licence at REF_LICENCE_GLIBC. + * + * WARNING: this test does not currently work. See WARNING comments below. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_02() +{ + static const char hello[] = "Hello, world.\n"; + static const char replace[] = "Hewwo, world.\n"; + static const size_t replace_from = 2, replace_to = 4; + const char *filename = sd_file_path; + char buf[BUFSIZ]; + FILE *f; + int lose = 0; + int32_t ret = 0; + char *rets = NULL; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + f = fopen(filename, "w+"); + if (f == NULL) { + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot open file for writing (filename=%s).\n", __func__, filename); + TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g); + } + + ret = fputs(hello, f); + if (ret == EOF) { + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fputs() failed to write string to file (filename=%s, string=%s).\n", __func__, filename, hello); + TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g); + } + + rewind(f); + rets = fgets(buf, sizeof(buf), f); + if (rets == NULL) { + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fgets() failed to get string from file (filename=%s).\n", __func__, filename); + TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g); + } + rets = NULL; + + rewind(f); + ret = fputs(buf, f); + if (ret == EOF) { + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fputs() failed to write string to file (filename=%s, string=%s).\n", __func__, filename, buf); + TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g); + } + + rewind(f); + { + register size_t i; + for (i = 0; i < replace_from; ++i) + { + int c = getc(f); + if (c == EOF) + { + FSFAT_DBGLOG("EOF at %u.\n", i); + lose = 1; + break; + } + else if (c != hello[i]) + { + FSFAT_DBGLOG("Got '%c' instead of '%c' at %u.\n", + (unsigned char) c, hello[i], i); + lose = 1; + break; + } + } + } + /* WARNING: printf("%s: here1. (lose = %d)\n", __func__, lose); */ + { + long int where = ftell(f); + if (where == replace_from) + { + register size_t i; + for (i = replace_from; i < replace_to; ++i) { + if (putc(replace[i], f) == EOF) { + FSFAT_DBGLOG("putc('%c') got %s at %u.\n", + replace[i], strerror(errno), i); + lose = 1; + break; + } + /* WARNING: The problem seems to be that putc() is not writing the 'w' chars into the file + * FSFAT_DBGLOG("%s: here1.5. (char = %c, char as int=%d, ret=%d) \n", __func__, replace[i], (int) replace[i], ret); + */ + } + } + else if (where == -1L) + { + FSFAT_DBGLOG("ftell got %s (should be at %u).\n", + strerror(errno), replace_from); + lose = 1; + } + else + { + FSFAT_DBGLOG("ftell returns %ld; should be %u.\n", where, replace_from); + lose = 1; + } + } + + if (!lose) + { + rewind(f); + memset(buf, 0, BUFSIZ); + if (fgets(buf, sizeof(buf), f) == NULL) + { + FSFAT_DBGLOG("fgets got %s.\n", strerror(errno)); + lose = 1; + } + else if (strcmp(buf, replace)) + { + FSFAT_DBGLOG("Read \"%s\" instead of \"%s\".\n", buf, replace); + lose = 1; + } + } + + if (lose) { + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Test Failed. Losing file (filename=%s).\n", __func__, filename); + TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g); + } + remove(filename); + return CaseNext; +} + +/** @brief temptest.c test ported from glibc project. See the licence at REF_LICENCE_GLIBC. + * + * tmpnam() is currently not implemented + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_03() +{ + char *fn = NULL; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + fn = tmpnam((char *) NULL); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: appeared to generate a filename when function is not implemented.\n", __func__); + TEST_ASSERT_MESSAGE(fn == NULL, fsfat_basic_msg_g); + return CaseNext; +} + + +static bool fsfat_basic_fileno_check(const char *name, FILE *stream, int fd) +{ + /* ARMCC stdio.h currently does not define fileno() */ +#ifndef __ARMCC_VERSION + int sfd = fileno (stream); + FSFAT_DBGLOG("(fileno (%s) = %d) %c= %d\n", name, sfd, sfd == fd ? '=' : '!', fd); + + if (sfd == fd) { + return true; + } else { + return false; + } +#else + /* For ARMCC behave as though test had passed. */ + return true; +#endif /* __ARMCC_VERSION */ +} + +/* defines for next test case */ +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif + + +/** @brief tst-fileno.c test ported from glibc project. See the licence at REF_LICENCE_GLIBC. + * + * WARNING: this test does not currently work. See WARNING comments below. + * + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_04() +{ + /* ARMCC stdio.h currently does not define fileno() */ +#ifndef __ARMCC_VERSION + int ret = -1; + ret = fsfat_basic_fileno_check("stdin", stdin, STDIN_FILENO); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: stdin does not have expected file number (expected=%d, fileno=%d.\n", __func__, (int) stdin, fileno(stdin)); + TEST_ASSERT_MESSAGE(ret == true, fsfat_basic_msg_g); + + ret = fsfat_basic_fileno_check("stdout", stdout, STDOUT_FILENO); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: stdout does not have expected file number (expected=%d, fileno=%d.\n", __func__, (int) stdout, fileno(stdout)); + TEST_ASSERT_MESSAGE(ret == true, fsfat_basic_msg_g); + + ret = fsfat_basic_fileno_check("stderr", stderr, STDERR_FILENO); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: stderr does not have expected file number (expected=%d, fileno=%d.\n", __func__, (int) stderr, fileno(stderr)); + TEST_ASSERT_MESSAGE(ret == true, fsfat_basic_msg_g); +#endif /* __ARMCC_VERSION */ + return CaseNext; +} + + +/** @brief basic test to opendir() on a directory. + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/dir_sd/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_05() +{ + FILE *f; + const char *str = FSFAT_BASIC_TEST_05_TEST_STRING; + int ret = 0; + + FSFAT_DBGLOG("%s:Write files\n", __func__); + char filename[32]; + for (int i = 0; i < 10; i++) { + sprintf(filename, "/sd/test_%d.txt", i); + FSFAT_DBGLOG("Creating file: %s\n", filename); + f = fopen(filename, "w"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fopen() failed.\n", __func__); + TEST_ASSERT_MESSAGE(f != NULL, fsfat_basic_msg_g); + + ret = fprintf(f, str); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: writing file.\n", __func__); + TEST_ASSERT_MESSAGE(ret == (int) strlen(str), fsfat_basic_msg_g); + + ret = fclose(f); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fclose() failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + } + + FSFAT_DBGLOG("%s:List files:\n", __func__); + DIR *d = opendir("/sd"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: opendir() failed.\n", __func__); + TEST_ASSERT_MESSAGE(d != NULL, fsfat_basic_msg_g); + + struct dirent *p; + while ((p = readdir(d)) != NULL) + FSFAT_DBGLOG("%s\n", p->d_name); + closedir(d); + + return CaseNext; +} + + +/** @brief basic test to write a file to sd card, and read it back again + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/file/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_06() +{ + int ret = -1; + char mac[16]; + mbed_mac_address(mac); + FSFAT_DBGLOG("mac address: %02x,%02x,%02x,%02x,%02x,%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + + FILE *f; + const char *str = FSFAT_BASIC_TEST_05_TEST_STRING; + int str_len = strlen(FSFAT_BASIC_TEST_05_TEST_STRING); + + f = fopen(sd_file_path, "w"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fopen() failed.\n", __func__); + TEST_ASSERT_MESSAGE(f != NULL, fsfat_basic_msg_g); + + ret = fprintf(f, str); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: writing file.\n", __func__); + TEST_ASSERT_MESSAGE(ret == (int) strlen(str), fsfat_basic_msg_g); + + ret = fclose(f); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fclose() failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + + // Read + f = fopen(sd_file_path, "r"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fopen() failed.\n", __func__); + TEST_ASSERT_MESSAGE(f != NULL, fsfat_basic_msg_g); + + int n = fread(fsfat_basic_buffer, sizeof(unsigned char), str_len, f); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fread() failed.\n", __func__); + TEST_ASSERT_MESSAGE(n == str_len, fsfat_basic_msg_g); + + ret = fclose(f); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fclose() failed.\n", __func__); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + + return CaseNext; +} + + +/** @brief basic test to write a file to sd card. + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_07() +{ + uint8_t data_written[FSFAT_BASIC_DATA_SIZE] = { 0 }; + + // Fill data_written buffer with random data + // Write these data into the file + bool write_result = false; + { + FSFAT_DBGLOG("%s:SD: Writing ... ", __func__); + FILE *f = fopen(sd_file_path, "w"); + if (f) { + for (int i = 0; i < FSFAT_BASIC_DATA_SIZE; i++) { + data_written[i] = rand() % 0XFF; + fprintf(f, "%c", data_written[i]); + } + write_result = true; + fclose(f); + } + FSFAT_DBGLOG("[%s]\n", write_result ? "OK" : "FAIL"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: unexpected write failure.\n", __func__); + TEST_ASSERT_MESSAGE(write_result == true, fsfat_basic_msg_g); + } + + // Read back the data from the file and store them in data_read + bool read_result = false; + { + FSFAT_DBGLOG("%s:SD: Reading data ... ", __func__); + FILE *f = fopen(sd_file_path, "r"); + if (f) { + read_result = true; + for (int i = 0; i < FSFAT_BASIC_DATA_SIZE; i++) { + uint8_t data = fgetc(f); + if (data != data_written[i]) { + read_result = false; + break; + } + } + fclose(f); + } + FSFAT_DBGLOG("[%s]\n", read_result ? "OK" : "FAIL"); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: unexpected read failure.\n", __func__); + TEST_ASSERT_MESSAGE(read_result == true, fsfat_basic_msg_g); + } + return CaseNext; +} + + +static bool fsfat_basic_test_file_write_fhandle(const char *filename, const int kib_rw) +{ + int ret = -1; + File file; + + ret = file.open(&fs, filename, O_WRONLY | O_CREAT | O_TRUNC); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + + int byte_write = 0; + fsfat_basic_timer.start(); + for (int i = 0; i < kib_rw; i++) { + ret = file.write(fsfat_basic_buffer, sizeof(fsfat_basic_buffer)); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to write to file.\n", __func__); + TEST_ASSERT_MESSAGE(ret == sizeof(fsfat_basic_buffer), fsfat_basic_msg_g); + byte_write++; + } + fsfat_basic_timer.stop(); + file.close(); +#ifdef FSFAT_DEBUG + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB write in %.3f sec with speed of %.4f KiB/s\n", byte_write, test_time_sec, speed); +#endif + fsfat_basic_timer.reset(); + return true; +} + + +static bool fsfat_basic_test_file_read_fhandle(const char *filename, const int kib_rw) +{ + int ret = -1; + File file; + ret = file.open(&fs, filename, O_RDONLY); + + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g); + + fsfat_basic_timer.start(); + int byte_read = 0; + while (file.read(fsfat_basic_buffer, sizeof(fsfat_basic_buffer)) == sizeof(fsfat_basic_buffer)) { + byte_read++; + } + fsfat_basic_timer.stop(); + file.close(); +#ifdef FSFAT_DEBUG + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB read in %.3f sec with speed of %.4f KiB/s\n", byte_read, test_time_sec, speed); +#endif + fsfat_basic_timer.reset(); + return true; +} + + +static char fsfat_basic_test_random_char() +{ + return rand() % 100; +} + + +/** @brief basic sd card performance test + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_handle/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_08() +{ + // Test header + FSFAT_DBGLOG("\n%s:SD Card FileHandle Performance Test\n", __func__); + FSFAT_DBGLOG("File name: %s\n", fsfat_basic_bin_filename); + FSFAT_DBGLOG("Buffer size: %d KiB\n", (FSFAT_BASIC_KIB_RW * sizeof(fsfat_basic_buffer)) / 1024); + + // Initialize buffer + srand(0); + char *buffer_end = fsfat_basic_buffer + sizeof(fsfat_basic_buffer); + std::generate (fsfat_basic_buffer, buffer_end, fsfat_basic_test_random_char); + + bool result = true; + for (;;) { + FSFAT_DBGLOG("%s:Write test...\n", __func__); + if (fsfat_basic_test_file_write_fhandle(fsfat_basic_bin_filename_test_08, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + + FSFAT_DBGLOG("%s:Read test...\n", __func__); + if (fsfat_basic_test_file_read_fhandle(fsfat_basic_bin_filename_test_08, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + break; + } + TEST_ASSERT_MESSAGE(result == true, "something went wrong"); + return CaseNext; +} + + +bool fsfat_basic_test_sf_file_write_stdio(const char *filename, const int kib_rw) +{ + int ret = -1; + FILE* file = fopen(filename, "w"); + + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(file != NULL, fsfat_basic_msg_g); + + int byte_write = 0; + fsfat_basic_timer.start(); + for (int i = 0; i < kib_rw; i++) { + ret = fwrite(fsfat_basic_buffer, sizeof(char), sizeof(fsfat_basic_buffer), file); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to write to file.\n", __func__); + TEST_ASSERT_MESSAGE(ret == sizeof(fsfat_basic_buffer), fsfat_basic_msg_g); + byte_write++; + } + fsfat_basic_timer.stop(); + fclose(file); +#ifdef FSFAT_DEBUG + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB write in %.3f sec with speed of %.4f KiB/s\n", byte_write, test_time_sec, speed); +#endif + fsfat_basic_timer.reset(); + return true; +} + + +bool fsfat_basic_test_sf_file_read_stdio(const char *filename, const int kib_rw) +{ + FILE* file = fopen(filename, "r"); + + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(file != NULL, fsfat_basic_msg_g); + fsfat_basic_timer.start(); + int byte_read = 0; + while (fread(fsfat_basic_buffer, sizeof(char), sizeof(fsfat_basic_buffer), file) == sizeof(fsfat_basic_buffer)) { + byte_read++; + } + fsfat_basic_timer.stop(); + fclose(file); +#ifdef FSFAT_DEBUG + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB read in %.3f sec with speed of %.4f KiB/s\n", byte_read, test_time_sec, speed); +#endif + fsfat_basic_timer.reset(); + return true; +} + + +/** @brief basic test to write a file to sd card. + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_stdio/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_09() +{ + // Test header + FSFAT_DBGLOG("\n%s:SD Card Stdio Performance Test\n", __func__); + FSFAT_DBGLOG("File name: %s\n", fsfat_basic_bin_filename); + FSFAT_DBGLOG("Buffer size: %d KiB\n", (FSFAT_BASIC_KIB_RW * sizeof(fsfat_basic_buffer)) / 1024); + + // Initialize buffer + srand(0); + char *buffer_end = fsfat_basic_buffer + sizeof(fsfat_basic_buffer); + std::generate (fsfat_basic_buffer, buffer_end, fsfat_basic_test_random_char); + + bool result = true; + for (;;) { + FSFAT_DBGLOG("%s:Write test...\n", __func__); + if (fsfat_basic_test_sf_file_write_stdio(fsfat_basic_bin_filename, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + + FSFAT_DBGLOG("%s:Read test...\n", __func__); + if (fsfat_basic_test_sf_file_read_stdio(fsfat_basic_bin_filename, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + break; + } + TEST_ASSERT_MESSAGE(result == true, "Expected true result not found"); + return CaseNext; +} + + +bool fsfat_basic_test_file_write_fatfs(const char *filename, const int kib_rw) +{ + FIL file; + FRESULT res = f_open(&file, filename, FA_WRITE | FA_CREATE_ALWAYS); + + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(res == FR_OK, fsfat_basic_msg_g); + + int byte_write = 0; + unsigned int bytes = 0; + fsfat_basic_timer.start(); + for (int i = 0; i < kib_rw; i++) { + res = f_write(&file, fsfat_basic_buffer, sizeof(fsfat_basic_buffer), &bytes); + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to write to file.\n", __func__); + TEST_ASSERT_MESSAGE(res == FR_OK, fsfat_basic_msg_g); + byte_write++; + } + fsfat_basic_timer.stop(); + f_close(&file); +#ifdef FSFAT_DEBUG + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB write in %.3f sec with speed of %.4f KiB/s\n", byte_write, test_time_sec, speed); +#endif + fsfat_basic_timer.reset(); + return true; +} + +bool fsfat_basic_test_file_read_fatfs(const char *filename, const int kib_rw) +{ + FIL file; + FRESULT res = f_open(&file, filename, FA_READ | FA_OPEN_EXISTING); + + FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__); + TEST_ASSERT_MESSAGE(res == FR_OK, fsfat_basic_msg_g); + + fsfat_basic_timer.start(); + int byte_read = 0; + unsigned int bytes = 0; + do { + res = f_read(&file, fsfat_basic_buffer, sizeof(fsfat_basic_buffer), &bytes); + byte_read++; + } while (res == FR_OK && bytes == sizeof(fsfat_basic_buffer)); + fsfat_basic_timer.stop(); + f_close(&file); +#ifdef FSFAT_DEBUG + double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0; + double speed = kib_rw / test_time_sec; + FSFAT_DBGLOG("%d KiB read in %.3f sec with speed of %.4f KiB/s\n", byte_read, test_time_sec, speed); +#endif + fsfat_basic_timer.reset(); + return true; +} + +/** @brief basic test to write a file to sd card. + * + * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_stdio/main.cpp. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_basic_test_10() +{ + // Test header + FSFAT_DBGLOG("\n%sSD Card FatFS Performance Test\n", __func__); + FSFAT_DBGLOG("File name: %s\n", fsfat_basic_bin_filename_test_10); + FSFAT_DBGLOG("Buffer size: %d KiB\n", (FSFAT_BASIC_KIB_RW * sizeof(fsfat_basic_buffer)) / 1024); + + // Initialize buffer + srand(1); + char *buffer_end = fsfat_basic_buffer + sizeof(fsfat_basic_buffer); + std::generate (fsfat_basic_buffer, buffer_end, fsfat_basic_test_random_char); + + bool result = true; + for (;;) { + FSFAT_DBGLOG("%s:Write test...\n", __func__); + if (fsfat_basic_test_file_write_fatfs(fsfat_basic_bin_filename_test_10, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + + FSFAT_DBGLOG("%s:Read test...\n", __func__); + if (fsfat_basic_test_file_read_fatfs(fsfat_basic_bin_filename_test_10, FSFAT_BASIC_KIB_RW) == false) { + result = false; + break; + } + break; + } + TEST_ASSERT_MESSAGE(result == true, "Expected true result not found"); + return CaseNext; +} + +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(300, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FSFAT_BASIC_TEST_ : format() test.", FSFAT_BASIC_TEST_), + Case("FSFAT_BASIC_TEST_00: fopen()/fgetc()/fprintf()/fclose() test.", FSFAT_BASIC_TEST_00), + Case("FSFAT_BASIC_TEST_01: fopen()/fseek()/fclose() test.", FSFAT_BASIC_TEST_01), + /* WARNING: Test case not working but currently not required for PAL support + * Case("FSFAT_BASIC_TEST_02: fopen()/fgets()/fputs()/ftell()/rewind()/remove() test.", FSFAT_BASIC_TEST_02) */ + Case("FSFAT_BASIC_TEST_03: tmpnam() test.", FSFAT_BASIC_TEST_03), + Case("FSFAT_BASIC_TEST_04: fileno() test.", FSFAT_BASIC_TEST_04), + Case("FSFAT_BASIC_TEST_05: opendir() basic test.", FSFAT_BASIC_TEST_05), + Case("FSFAT_BASIC_TEST_06: fread()/fwrite() file to sdcard.", FSFAT_BASIC_TEST_06), + Case("FSFAT_BASIC_TEST_07: sdcard fwrite() file test.", FSFAT_BASIC_TEST_07), + Case("FSFAT_BASIC_TEST_08: FATFileSystem::read()/write() test.", FSFAT_BASIC_TEST_08), + Case("FSFAT_BASIC_TEST_09: POSIX FILE API fread()/fwrite() test.", FSFAT_BASIC_TEST_09), + Case("FSFAT_BASIC_TEST_10: ChanFS read()/write()) test.", FSFAT_BASIC_TEST_10), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} diff --git a/TESTS/filesystem/fopen/fopen.cpp b/TESTS/filesystem/fopen/fopen.cpp new file mode 100644 index 0000000000..6d2a6aef86 --- /dev/null +++ b/TESTS/filesystem/fopen/fopen.cpp @@ -0,0 +1,1489 @@ +/* + * 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. + */ + +/** @file fopen.cpp Test cases to POSIX file fopen() interface. + * + * Please consult the documentation under the test-case functions for + * a description of the individual test case. + */ + +#include "mbed.h" +#include "mbed_config.h" +#include "FATFileSystem.h" +#include "fsfat_debug.h" +#include "fsfat_test.h" +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" + +#include +#include +#include /*rand()*/ +#include +#include +/* mbed_retarget.h is included after errno.h so symbols are mapped to + * consistent values for all toolchains */ +#include "platform/mbed_retarget.h" + +/* This is needed for stat() test, but is not available on ARMCC. + * The following checks whether GCC_ARM compiler is being used because: + * - both the ARMCC compiler and the GCC_ARM compile define __GNUC__. + * - only the ARMCC compiler defines __ARMCC_VERSION. + * - hence if __ARMCC_VERSION is not defined and __GNUC__ is defined, it must be GCC_ARM. */ +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) +#include +#endif +using namespace utest::v1; + +/// @cond FSFAT_DOXYGEN_DISABLE +#ifdef FSFAT_DEBUG +#define FSFAT_FOPEN_GREENTEA_TIMEOUT_S 3000 +#else +#define FSFAT_FOPEN_GREENTEA_TIMEOUT_S 1000 +#endif +/// @endcond + + +/* DEVICE_SPI + * This symbol is defined in targets.json if the target has a SPI interface, which is required for SDCard support. + * + * MBED_CONF_APP_FSFAT_SDCARD_INSTALLED + * For testing purposes, an SDCard must be installed on the target for the test cases in this file to succeed. + * If the target has an SD card installed then the MBED_CONF_APP_FSFAT_SDCARD_INSTALLED will be generated + * from the mbed_app.json, which includes the line + * { + * "config": { + * "UART_RX": "D0", + * <<< lines removed >>> + * "DEVICE_SPI": 1, + * "FSFAT_SDCARD_INSTALLED": 1 + * }, + * <<< lines removed >>> + */ + +#include "FlashIAPBlockDevice.h" + +FlashIAPBlockDevice sd; +FATFileSystem fs("sd", &sd); + +static char fsfat_fopen_utest_msg_g[FSFAT_UTEST_MSG_BUF_SIZE]; +#define FSFAT_FOPEN_TEST_MOUNT_PT_NAME "sd" +#define FSFAT_FOPEN_TEST_MOUNT_PT_PATH "/"FSFAT_FOPEN_TEST_MOUNT_PT_NAME +#define FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1 64 +#define FSFAT_FOPEN_TEST_FILEPATH_MAX_DEPTH 20 +static const char *sd_badfile_path = "/sd/badfile.txt"; +static const char *sd_testfile_path = "/sd/test.txt"; + +#define FSFAT_FOPEN_TEST_00 fsfat_fopen_test_00 +#define FSFAT_FOPEN_TEST_01 fsfat_fopen_test_01 +#define FSFAT_FOPEN_TEST_02 fsfat_fopen_test_02 +#define FSFAT_FOPEN_TEST_03 fsfat_fopen_test_03 +#define FSFAT_FOPEN_TEST_04 fsfat_fopen_test_04 +#define FSFAT_FOPEN_TEST_05 fsfat_fopen_test_05 +#define FSFAT_FOPEN_TEST_06 fsfat_fopen_test_06 +#define FSFAT_FOPEN_TEST_07 fsfat_fopen_test_07 +#define FSFAT_FOPEN_TEST_08 fsfat_fopen_test_08 +#define FSFAT_FOPEN_TEST_09 fsfat_fopen_test_09 +#define FSFAT_FOPEN_TEST_10 fsfat_fopen_test_10 +#define FSFAT_FOPEN_TEST_11 fsfat_fopen_test_11 +#define FSFAT_FOPEN_TEST_12 fsfat_fopen_test_12 +#define FSFAT_FOPEN_TEST_13 fsfat_fopen_test_13 +#define FSFAT_FOPEN_TEST_14 fsfat_fopen_test_14 +#define FSFAT_FOPEN_TEST_15 fsfat_fopen_test_15 +#define FSFAT_FOPEN_TEST_17 fsfat_fopen_test_17 +#define FSFAT_FOPEN_TEST_18 fsfat_fopen_test_18 +#define FSFAT_FOPEN_TEST_19 fsfat_fopen_test_19 +#define FSFAT_FOPEN_TEST_20 fsfat_fopen_test_20 +#define FSFAT_FOPEN_TEST_21 fsfat_fopen_test_21 +#define FSFAT_FOPEN_TEST_22 fsfat_fopen_test_22 +#define FSFAT_FOPEN_TEST_23 fsfat_fopen_test_23 +#define FSFAT_FOPEN_TEST_24 fsfat_fopen_test_24 +#define FSFAT_FOPEN_TEST_25 fsfat_fopen_test_25 +#define FSFAT_FOPEN_TEST_26 fsfat_fopen_test_26 +#define FSFAT_FOPEN_TEST_27 fsfat_fopen_test_27 +#define FSFAT_FOPEN_TEST_28 fsfat_fopen_test_28 +#define FSFAT_FOPEN_TEST_29 fsfat_fopen_test_29 +#define FSFAT_FOPEN_TEST_30 fsfat_fopen_test_30 + + +/* support functions */ + +/* + * open tests that focus on testing fopen() + * fsfat_handle_t fopen(const char* filename, char* data, size_t* len, fsfat_key_desc_t* kdesc) + */ + +/* file data for test_01 */ +static fsfat_kv_data_t fsfat_fopen_test_01_kv_data[] = { + { "/sd/fopentst/hello/world/animal/wobbly/dog/foot/frontlft.txt", "missing"}, + { NULL, NULL}, +}; + + +/** @brief + * Split a file path into its component parts, setting '/' characters to '\0', and returning + * pointers to the file path components in the parts array. For example, if + * filepath = "/sd/fopentst/hello/world/animal/wobbly/dog/foot/frontlft.txt" then + * *parts[0] = "sd" + * *parts[1] = "fopentst" + * *parts[2] = "hello" + * *parts[3] = "world" + * *parts[4] = "animal" + * *parts[5] = "wobbly" + * *parts[6] = "dog" + * *parts[7] = "foot" + * *parts[8] = "frontlft.txt" + * parts[9] = NULL + * + * ARGUMENTS + * @param filepath IN file path string to split into component parts. Expected to start with '/' + * @param parts IN OUT array to hold pointers to parts + * @param num IN number of components available in parts + * + * @return On success, this returns the number of components in the filepath Returns number of compoee + */ +static int32_t fsfat_filepath_split(char* filepath, char* parts[], uint32_t num) +{ + uint32_t i = 0; + int32_t ret = -1; + char* z = filepath; + + while (i < num && *z != '\0') { + if (*z == '/' ) { + *z = '\0'; + parts[i] = ++z; + i++; + } else { + z++; + } + } + if (*z == '\0' && i > 0) { + ret = (int32_t) i; + } + return ret; +} + + +/** @brief + * remove all directories and file in the given filepath + * + * ARGUMENTS + * @param filepath IN file path string to split into component parts. Expected to start with '/' + * + * @return On success, this returns 0, otherwise < 0 is returned; + */ +int32_t fsfat_filepath_remove_all(char* filepath) +{ + int32_t ret = -1; + int32_t len = 0; + char *fpathbuf = NULL; + char *pos = NULL; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + len = strlen(filepath); + fpathbuf = (char*) malloc(len+1); + if (fpathbuf == NULL) { + FSFAT_DBGLOG("%s: failed to duplicate string (out of memory)\n", __func__); + return ret; + } + memset(fpathbuf, 0, len+1); + memcpy(fpathbuf, filepath, len); + + /* delete the leaf node first, and then successively parent directories. */ + pos = fpathbuf + strlen(fpathbuf); + while (pos != fpathbuf) { + /* If the remaining file path is the mount point path then finish as the mount point cannot be removed */ + if (strlen(fpathbuf) == strlen(FSFAT_FOPEN_TEST_MOUNT_PT_PATH) && strncmp(fpathbuf, FSFAT_FOPEN_TEST_MOUNT_PT_PATH, strlen(fpathbuf)) == 0) { + break; + } + ret = remove(fpathbuf); + pos = strrchr(fpathbuf, '/'); + *pos = '\0'; + } + if (fpathbuf) { + free(fpathbuf); + } + return ret; +} + + +/** @brief + * make all directories in the given filepath. Do not create the file if present at end of filepath + * + * ARGUMENTS + * @param filepath IN file path containing directories and file + * @param do_asserts IN set to true if function should assert on errors + * + * @return On success, this returns 0, otherwise < 0 is returned; + */ +static int32_t fsfat_filepath_make_dirs(char* filepath, bool do_asserts) +{ + int32_t i = 0; + int32_t num_parts = 0; + int32_t len = 0; + int32_t ret = -1; + char *fpathbuf = NULL; + char *buf = NULL; + int pos = 0; + char *parts[FSFAT_FOPEN_TEST_FILEPATH_MAX_DEPTH]; + + FSFAT_DBGLOG("%s:entered\n", __func__); + /* find the dirs to create*/ + memset(parts, 0, sizeof(parts)); + len = strlen(filepath); + fpathbuf = (char*) malloc(len+1); + if (fpathbuf == NULL) { + FSFAT_DBGLOG("%s: failed to duplicate string (out of memory)\n", __func__); + return ret; + } + memset(fpathbuf, 0, len+1); + memcpy(fpathbuf, filepath, len); + num_parts = fsfat_filepath_split(fpathbuf, parts, FSFAT_FOPEN_TEST_FILEPATH_MAX_DEPTH); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to split filepath (filename=\"%s\", num_parts=%d)\n", __func__, filepath, (int) num_parts); + TEST_ASSERT_MESSAGE(num_parts > 0, fsfat_fopen_utest_msg_g); + + /* Now create the directories on the directory path. + * Skip creating dir for "/sd" which must be present */ + buf = (char*) malloc(strlen(filepath)+1); + memset(buf, 0, strlen(filepath)+1); + pos = sprintf(buf, "/%s", parts[0]); + for (i = 1; i < num_parts - 1; i++) { + pos += sprintf(buf+pos, "/%s", parts[i]); + FSFAT_DBGLOG("mkdir(%s)\n", buf); + ret = mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (do_asserts == true) { + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create directory (filepath2=\"%s\", ret=%d, errno=%d)\n", __func__, buf, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + } + } + + if (buf) { + free(buf); + } + if (fpathbuf) { + free(fpathbuf); + } + return ret; +} + + +/* FIX ME: errno not set correctly when error occurs. This indicates a problem with the implementation. */ + +/** @brief + * Basic fopen test which does the following: + * - creates file and writes some data to the value blob. + * - closes the newly created file. + * - opens the file (r-only) + * - reads the file data and checks its the same as the previously created data. + * - closes the opened file + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +static control_t fsfat_fopen_test_01(const size_t call_count) +{ + char* read_buf; + int32_t ret = 0; + size_t len = 0; + fsfat_kv_data_t *node; + FILE *fp = NULL; + + FSFAT_DBGLOG("%s:entered\n", __func__); + (void) call_count; + node = fsfat_fopen_test_01_kv_data; + + /* remove file and directory from a previous failed test run, if present */ + fsfat_filepath_remove_all((char*) node->filename); + + /* create dirs */ + ret = fsfat_filepath_make_dirs((char*) node->filename, true); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dirs for filename (filename=\"%s\")(ret=%d)\n", __func__, node->filename, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + FSFAT_DBGLOG("%s:About to create new file (filename=\"%s\", data=\"%s\")\n", __func__, node->filename, node->value); + fp = fopen(node->filename, "w+"); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (filename=\"%s\", data=\"%s\")(ret=%d, errno=%d)\n", __func__, node->filename, node->value, (int) ret, errno); + TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g); + + FSFAT_DBGLOG("%s:length of file=%d (filename=\"%s\", data=\"%s\")\n", __func__, (int) len, node->filename, node->value); + len = strlen(node->value); + ret = fwrite((const void*) node->value, len, 1, fp); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write file (filename=\"%s\", data=\"%s\")(ret=%d)\n", __func__, node->filename, node->value, (int) ret); + TEST_ASSERT_MESSAGE(ret == 1, fsfat_fopen_utest_msg_g); + + FSFAT_DBGLOG("Created file successfully (filename=\"%s\", data=\"%s\")\n", node->filename, node->value); + ret = fclose(fp); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to close file (ret=%d, errno=%d)\n", __func__, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + /* now open the newly created key */ + fp = NULL; + fp = fopen(node->filename, "r"); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file for reading (filename=\"%s\", data=\"%s\")(ret=%d)\n", __func__, node->filename, node->value, (int) ret); + TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g); + + len = strlen(node->value) + 1; + read_buf = (char*) malloc(len); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to allocated read buffer \n", __func__); + TEST_ASSERT_MESSAGE(read_buf != NULL, fsfat_fopen_utest_msg_g); + + FSFAT_DBGLOG("Opened file successfully (filename=\"%s\", data=\"%s\")\n", node->filename, node->value); + memset(read_buf, 0, len); + ret = fread((void*) read_buf, len, 1, fp); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read file (filename=\"%s\", data=\"%s\", read_buf=\"%s\", ret=%d)\n", __func__, node->filename, node->value, read_buf, (int) ret); + /* FIX ME: fread should return the number of items read, not 0 when an item is read successfully. + * This indicates a problem with the implementation, as the correct data is read. The correct assert should be: + * TEST_ASSERT_MESSAGE(ret == 1, fsfat_fopen_utest_msg_g); + * The following assert is curerntly used until the implementation is fixed + */ + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + /* check read data is as expected */ + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: read value data (%s) != expected value data (filename=\"%s\", data=\"%s\", read_buf=\"%s\", ret=%d)\n", __func__, read_buf, node->filename, node->value, read_buf, (int) ret); + TEST_ASSERT_MESSAGE(strncmp(read_buf, node->value, strlen(node->value)) == 0, fsfat_fopen_utest_msg_g); + + if(read_buf){ + free(read_buf); + } + ret = fclose(fp); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: fclose() call failed (ret=%d, errno=%d).\n", __func__, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + return CaseNext; +} + +static fsfat_kv_data_t fsfat_fopen_test_02_data[] = { + FSFAT_INIT_1_TABLE_MID_NODE, + { NULL, NULL}, +}; + +/** + * @brief test to fopen() a pre-existing key and try to write it, which should fail + * as by default pre-existing keys are opened read-only + * + * Basic open test which does the following: + * - creates file with default rw perms and writes some data to the value blob. + * - closes the newly created file. + * - opens the file with the default permissions (read-only) + * - tries to write the file data which should fail because file was not opened with write flag set. + * - closes the opened key + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_02(const size_t call_count) +{ + int32_t ret = -1; + size_t len = 0; + FILE *fp = NULL; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + len = strlen(fsfat_fopen_test_02_data[0].value); + ret = fsfat_test_create(fsfat_fopen_test_02_data[0].filename, (char*) fsfat_fopen_test_02_data[0].value, len); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + + /* by default, owner of key opens with read-only permissions*/ + fp = fopen(fsfat_fopen_test_02_data[0].filename, "r"); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file (filename=\"%s\", ret=%d)\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret); + TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g); + + len = strlen(fsfat_fopen_test_02_data[0].value); + ret = fwrite((const void*) fsfat_fopen_test_02_data[0].value, len, 1, fp); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: call to fwrite() succeeded when should have failed for read-only file (filename=\"%s\")(ret=%d).\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret); + TEST_ASSERT_MESSAGE(ret <= 0, fsfat_fopen_utest_msg_g); + + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: fclose() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(fclose(fp) == 0, fsfat_fopen_utest_msg_g); + + return CaseNext; +} + + +/** + * @brief test to fopen() a pre-existing file and try to write it, which should succeed + * because the key was opened read-write permissions explicitly + * + * Basic open test which does the following: + * - creates file with default rw perms and writes some data to the value blob. + * - closes the newly created file. + * - opens the file with the rw permissions (non default) + * - tries to write the file data which should succeeds because file was opened with write flag set. + * - closes the opened key + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_03(const size_t call_count) +{ + int32_t ret = -1; + size_t len = 0; + FILE *fp = NULL; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + len = strlen(fsfat_fopen_test_02_data[0].value); + ret = fsfat_test_create(fsfat_fopen_test_02_data[0].filename, (char*) fsfat_fopen_test_02_data[0].value, len); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file in store (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + + /* opens with read-write permissions*/ + fp = fopen(fsfat_fopen_test_02_data[0].filename, "w+"); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file (filename=\"%s\")(ret=%d)\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + + len = strlen(fsfat_fopen_test_02_data[0].value); + ret = fwrite((const void*) fsfat_fopen_test_02_data[0].value, len, 1, fp); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: call to fwrite() failed when should have succeeded (filename=\"%s\", ret=%d).\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: fclose() call failed.\n", __func__); + TEST_ASSERT_MESSAGE(fclose(fp) >= 0, fsfat_fopen_utest_msg_g); + + /* clean-up */ + ret = remove(fsfat_fopen_test_02_data[0].filename); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unable to delete file (filename=%s, ret=%d) .\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + + return CaseNext; +} + + +/** @brief test to call fopen() with a filename string that exceeds the maximum length + * - chanFS supports the exFAT format which should support 255 char filenames + * - check that filenames of this length can be created + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_04(const size_t call_count) +{ + char filename_good[FSFAT_FILENAME_MAX_LENGTH+1]; + char filename_bad[FSFAT_FILENAME_MAX_LENGTH+2]; + int32_t ret = -1; + size_t len = 0; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + memset(filename_good, 0, FSFAT_FILENAME_MAX_LENGTH+1); + memset(filename_bad, 0, FSFAT_FILENAME_MAX_LENGTH+2); + ret = fsfat_test_filename_gen(filename_good, FSFAT_FILENAME_MAX_LENGTH); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate filename_good.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: filename_good is not the correct length (filename_good=%s, len=%d, expected=%d).\n", __func__, filename_good, (int) strlen(filename_good), (int) FSFAT_FILENAME_MAX_LENGTH); + TEST_ASSERT_MESSAGE(strlen(filename_good) == FSFAT_FILENAME_MAX_LENGTH, fsfat_fopen_utest_msg_g); + + ret = fsfat_test_filename_gen(filename_bad, FSFAT_FILENAME_MAX_LENGTH+1); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate filename_bad.\n", __func__); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: filename_bad is not the correct length (len=%d, expected=%d).\n", __func__, (int) strlen(filename_bad), (int) FSFAT_FILENAME_MAX_LENGTH+1); + TEST_ASSERT_MESSAGE(strlen(filename_bad) == FSFAT_FILENAME_MAX_LENGTH+1, fsfat_fopen_utest_msg_g); + + len = strlen(filename_good); + ret = fsfat_test_create(filename_good, filename_good, len); + /* FIXME: + * The current implementation can create file with a filename with 9 chars (more than the 8 restriction of FAT32 Short File Names). + * However, the exFAT 255 char filesnames is not supported and hence the following is commented out. Find out what is + * the supported max filename length and change this testcase according. + * + * FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (filename=%s, ret=%d).\n", __func__, filename_good, (int) ret); + * TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + */ + + len = strlen(filename_bad); + ret = fsfat_test_create(filename_bad, filename_bad, len); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: created file in store for filename_bad when should have failed (filename=%s, ret=%d).\n", __func__, filename_bad, (int) ret); + TEST_ASSERT_MESSAGE(ret < 0, fsfat_fopen_utest_msg_g); + return CaseNext; +} + + +/// @cond FSFAT_DOXYGEN_DISABLE +typedef struct fsfat_fopen_kv_name_ascii_node { + uint32_t code; + uint32_t f_allowed : 1; +} fsfat_fopen_kv_name_ascii_node; +/// @endcond + +static const uint32_t fsfat_fopen_kv_name_ascii_table_code_sentinel_g = 256; + +/*@brief table recording ascii character codes permitted in kv names */ +static fsfat_fopen_kv_name_ascii_node fsfat_fopen_kv_name_ascii_table[] = +{ + {0 , true}, /* code 0-33 allowed*/ + {34, false}, /* '"' not allowed */ + {35, true}, /* allowed */ + {42, false}, /* '*' not allowed */ + {43, true}, /* allowed */ + {47, false}, /* '/' not allowed */ + {48, true}, /* allowed */ + {58, false}, /* ':' not allowed */ + {59, true}, /* allowed */ + {60, false}, /* '<' not allowed */ + {61, true}, /* allowed */ + {62, false}, /* '?', '>' not allowed */ + {64, true}, /* allowed */ + {92, false}, /* '\' not allowed */ + {93, true}, /* allowed */ + {124, false}, /* '!' not allowed */ + {125, true}, /* allowed */ + {127, false}, /* DEL not allowed */ + {128, true}, /* allowed */ + {fsfat_fopen_kv_name_ascii_table_code_sentinel_g, false}, /* sentinel */ +}; + + +/// @cond FSFAT_DOXYGEN_DISABLE +enum fsfat_fopen_kv_name_pos { + fsfat_fopen_kv_name_pos_start = 0x0, + fsfat_fopen_kv_name_pos_mid, + fsfat_fopen_kv_name_pos_end, + fsfat_fopen_kv_name_pos_max +}; +/// @endcond + +/** @brief test to call fopen() with filename that in includes illegal characters + * - the character(s) can be at the beginning of the filename + * - the character(s) can be at the end of the filename + * - the character(s) can be somewhere within the filename string + * - a max-length string of random characters (legal and illegal) + * - a max-length string of random illegal characters only + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_05(const size_t call_count) +{ + bool f_allowed = false; + const char *mnt_pt = FSFAT_FOPEN_TEST_MOUNT_PT_PATH; + const char *basename = "goodfile"; + const char *extname = "txt"; + const size_t basename_len = strlen(basename); + const size_t filename_len = strlen(mnt_pt)+strlen(basename)+strlen(extname)+2; /* extra 2 chars for '/' and '.' in "/sd/goodfile.txt" */ + char filename[FSFAT_BUF_MAX_LENGTH]; + size_t len = 0; + uint32_t j = 0; + int32_t ret = 0; + fsfat_fopen_kv_name_ascii_node* node = NULL; + uint32_t pos; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + +#ifdef FSFAT_DEBUG + /* symbol only used why debug is enabled */ + const char* pos_str = NULL; +#endif + + /* create bad keyname strings with invalid character code at start of keyname */ + node = fsfat_fopen_kv_name_ascii_table; + memset(filename, 0, FSFAT_BUF_MAX_LENGTH); + while(node->code != fsfat_fopen_kv_name_ascii_table_code_sentinel_g) + { + /* loop over range */ + for(j = node->code; j < (node+1)->code; j++) + { + if( (j >= 48 && j <= 57) || (j >= 65 && j <= 90) || (j >= 97 && j <= 122)) { + FSFAT_DBGLOG("%s: skipping alpha-numeric ascii character code %d (%c).\n", __func__, (int) j, (char) j); + continue; + } + + /* set the start, mid, last character of the name to the test char code */ + for(pos = (uint32_t) fsfat_fopen_kv_name_pos_start; pos < (uint32_t) fsfat_fopen_kv_name_pos_max; pos++) + { + len = snprintf(filename, filename_len+1, "%s/%s.%s", mnt_pt, basename, extname); + /* overwrite a char at the pos start, mid, end of the filename with an ascii char code (both illegal and legal)*/ + switch(pos) + { + case fsfat_fopen_kv_name_pos_start: + filename[5] = (char) j; /* 5 so at to write the second basename char (bad chars as first char not accepted)*/ + break; + case fsfat_fopen_kv_name_pos_mid: + /* create bad keyname strings with invalid character code in the middle of keyname */ + filename[5+basename_len/2] = (char) j; + break; + case fsfat_fopen_kv_name_pos_end: + /* create bad keyname strings with invalid character code at end of keyname */ + filename[5+basename_len-1] = (char) j; + break; + default: + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected value of pos (pos=%d).\n", __func__, (int) pos); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + break; + } + +#ifdef FSFAT_DEBUG + /* processing only required when debug trace enabled */ + switch(pos) + { + case fsfat_fopen_kv_name_pos_start: + pos_str = "start"; + break; + case fsfat_fopen_kv_name_pos_mid: + pos_str = "middle"; + break; + case fsfat_fopen_kv_name_pos_end: + pos_str = "end"; + break; + default: + break; + } +#endif + ret = fsfat_test_create(filename, (const char*) filename, len); + + /* special cases */ + switch(j) + { + //case 0 : + //case 46 : + // switch(pos) + // { + // /* for code = 0 (null terminator). permitted at mid and end of string */ + // /* for code = 46 ('.'). permitted at mid and end of string but not at start */ + // case fsfat_fopen_kv_name_pos_start: + // f_allowed = false; + // break; + // case fsfat_fopen_kv_name_pos_mid: + // case fsfat_fopen_kv_name_pos_end: + // default: + // f_allowed = true; + // break; + // } + // break; + default: + f_allowed = node->f_allowed; + break; + } + if(f_allowed == true) + { + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file in store when filename contains valid characters (code=%d, ret=%d).\n", __func__, (int) j, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + /* revert FSFAT_LOG for more trace */ + FSFAT_DBGLOG("Successfully created a file with valid keyname containing ascii character code %d (%c) at the %s of the keyname.\n", (int) j, (int) j, pos_str); + FSFAT_LOG("%c", '.'); + + ret = fsfat_test_delete(filename); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete file previously created (code=%d, ret=%d).\n", __func__, (int) j, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + } + else + { /*node->f_allowed == false => not allowed to create kv name with ascii code */ + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: created file in store when filename contains an invalid character (code=%d, ret=%d).\n", __func__, (int) j, (int) ret); + TEST_ASSERT_MESSAGE(ret < 0, fsfat_fopen_utest_msg_g); + /* revert FSFAT_LOG for more trace */ + FSFAT_DBGLOG("Successfully failed to create a file with an invalid keyname containing ascii character code %d at the %s of the keyname.\n", (int) j, pos_str); + FSFAT_LOG("%c", '.'); + } + } + } + node++; + } + + FSFAT_LOG("%c", '\n'); + return CaseNext; +} + + +static const char fsfat_fopen_ascii_illegal_buf_g[] = "\"�'*+,./:;<=>?[\\]|"; + +/** @brief test to call fopen() with filename that in includes + * illegal characters + * - a max-length string of random illegal characters only + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_06(const size_t call_count) +{ + const char *mnt_pt = FSFAT_FOPEN_TEST_MOUNT_PT_PATH; + const char *extname = "txt"; + const size_t filename_len = strlen(mnt_pt)+FSFAT_MAX_FILE_BASENAME+strlen(extname)+2; /* extra 2 chars for '/' and '.' in "/sd/goodfile.txt" */ + char filename[FSFAT_BUF_MAX_LENGTH]; + int32_t i = 0; + int32_t j = 0; + uint32_t pos = 0; + uint32_t len = 0; + int32_t ret = -1; + size_t buf_data_max = 0; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + memset(filename, 0, FSFAT_BUF_MAX_LENGTH); + /* create bad keyname strings with invalid character code at start of keyname */ + buf_data_max = strlen(fsfat_fopen_ascii_illegal_buf_g); + + /* generate a number of illegal filenames */ + for (j = 0; i < FSFAT_MAX_FILE_BASENAME; j++) { + /* generate a kv name of illegal chars*/ + len = snprintf(filename, filename_len+1, "%s/", mnt_pt); + for (i = 0; i < FSFAT_MAX_FILE_BASENAME; i++) { + pos = rand() % (buf_data_max+1); + len += snprintf(filename+len, filename_len+1, "%c", fsfat_fopen_ascii_illegal_buf_g[pos]); + + } + len += snprintf(filename+len, filename_len+1, ".%s", extname); + ret = fsfat_test_create(filename, filename, len); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: created file when filename contains invalid characters (filename=%s, ret=%d).\n", __func__, filename, (int) ret); + TEST_ASSERT_MESSAGE(ret < 0, fsfat_fopen_utest_msg_g); + } + return CaseNext; +} + + +/** @brief test for errno reporting on a failed fopen()call + * + * This test does the following: + * - tries to open a file that does not exist for reading, and checks that a NULL pointer is returned. + * - checks that errno is not 0 as there is an error. + * - checks that ferror() returns 1 indicating an error exists. + * + * Note: see NOTE_1 below. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_07(const size_t call_count) +{ + FILE *f = NULL; + int ret = -1; + int errno_val = 0; + const char *filename = sd_badfile_path; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + errno = 0; + /* this is expect to fail as the file doesnt exist */ + f = fopen(filename,"r"); + + /* Store errno so the current value set is not changed by new function call */ + errno_val = errno; + + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: opened non-existent file for reading (filename=%s, f=%p).\n", __func__, filename, f); + TEST_ASSERT_MESSAGE(f == NULL, fsfat_fopen_utest_msg_g); + + /* check errno is set correctly */ + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: errno has unexpected value (errno != 0 expected) (filename=%s, errno=%d).\n", __func__, filename, errno); + TEST_ASSERT_MESSAGE(errno_val != 0, fsfat_fopen_utest_msg_g); + + return CaseNext; +} + + +/** @brief test for operation of clearerr() and ferror() + * + * The test does the following: + * - opens and then closes a file, but keeps a copy of the FILE pointer fp. + * - set errno to 0. + * - write to the close file with fwrite(fp) which should return 0 (no writes) and set the errno. + * - check the error condition is set with ferror(). + * - clear the error with clearerr(). + * - check the error condition is reset with ferror(). + * + * NOTE_1: GCC/ARMCC support for setting errno + * - Documentation (e.g. fwrite() man page) does not explicity say fwrite() sets errno + * (e.g. for an fwrite() on a read-only file). + * - GCC libc fwrite() appears to set errno as expected. + * - ARMCC & IAR libc fwrite() appears not to set errno. + * + * The following ARMCC documents are silent on whether fwrite() sets errno: + * - "ARM C and C++ Libraries and Floating-Point Support". + * - "RL-ARM User Guide fwrite() section". + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_08(const size_t call_count) +{ + FILE *fp = NULL; + int ret = -1; + int ret_ferror = -1; + const char *filename = sd_testfile_path; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + errno = 0; + fp = fopen(filename,"w+"); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file (filename=%s, f=%p).\n", __func__, filename, fp); + TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g); + + /* close the fp but then try to read or write it */ + ret = fclose(fp); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to close file (ret=%d, errno=%d)\n", __func__, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + /* open file */ + errno = 0; + fp = fopen(filename, "r"); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file for reading (filename=\"%s\", ret=%d)\n", __func__, filename, (int) ret); + TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g); + + /* Perform fwrite() operation that will fail. */ + errno = 0; + ret = fwrite("42!", 4, 1, fp); + + ret_ferror = ferror(fp); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: ferror() failed to report error (filename=%s, ret_ferror=%d).\n", __func__, filename, (int) ret_ferror); + TEST_ASSERT_MESSAGE(ret_ferror != 0, fsfat_fopen_utest_msg_g); + + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: fwrite successfully wrote to read-only file (filename=%s, ret=%d).\n", __func__, filename, (int) ret); + /* the fwrite() should fail and return 0. */ + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) + /* check that errno is set. ARMCC appears not to set errno for fwrite() failure. */ + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected zero value for errno (filename=%s, ret=%d, errno=%d).\n", __func__, filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(errno != 0, fsfat_fopen_utest_msg_g); + + /* check that errno is set to the expected value (this may change differ for different libc's) */ + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: errno != EBADF (filename=%s, ret=%d, errno=%d).\n", __func__, filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(errno == EBADF, fsfat_fopen_utest_msg_g); +#endif /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */ + + /* check clearerr() return clears the error */ + clearerr(fp); + ret = ferror(fp); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: ferror() did not return zero value when error has been cleared (filename=%s, ret=%d).\n", __func__, filename, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + fclose(fp); + return CaseNext; +} + + +/** @brief test for operation of ftell() + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_09(const size_t call_count) +{ + FILE *fp = NULL; + int ret = -1; + int32_t len = 0; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + /* create a file of a certain length */ + len = strlen(fsfat_fopen_test_02_data[0].value); + ret = fsfat_test_create(fsfat_fopen_test_02_data[0].filename, (char*) fsfat_fopen_test_02_data[0].value, len); + + errno = 0; + /* Open the file for reading so the file is not truncated to 0 length. */ + fp = fopen(fsfat_fopen_test_02_data[0].filename, "r"); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file (filename=%s, fp=%p, errno=%d).\n", __func__, fsfat_fopen_test_02_data[0].filename, fp, errno); + TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g); + + errno = 0; + ret = fseek(fp, 0, SEEK_END); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: fseek() failed to SEEK_END (filename=%s, ret=%d, errno=%d).\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + errno = 0; + ret = ftell(fp); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: ftell() failed to report correct offset value (filename=%s, ret=%d, errno=%d).\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == len, fsfat_fopen_utest_msg_g); + + errno = 0; + ret = fclose(fp); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to close file (ret=%d, errno=%d)\n", __func__, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + return CaseNext; +} + + +/* file data for test_10 */ +static fsfat_kv_data_t fsfat_fopen_test_10_kv_data[] = { + { "/sd/test_10/testfile.txt", "test_data"}, + { NULL, NULL}, +}; + +/** @brief test for operation of remove() + * + * Performs the following tests: + * 1. test remove() on a file that exists. This should succeed. + * 2. test remove() on a dir that exists. This should succeed. + * 3. test remove() on a file that doesnt exist. This should fail. check errno set. + * 4. test remove() on a dir that doesnt exist. This should fail. check errno set. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_10(const size_t call_count) +{ + char buf[FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1]; + char *pos = NULL; + int32_t ret = -1; + size_t len = 0; + fsfat_kv_data_t *node = fsfat_fopen_test_10_kv_data; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + TEST_ASSERT(strlen(node->filename) < FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1); + + /* start from a known state i.e. directory to be created in not present */ + fsfat_filepath_remove_all((char*) node->filename); + + /* (1) */ + errno = 0; + ret = fsfat_filepath_make_dirs((char*) node->filename, false); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, node->filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + len = strlen(node->value); + ret = fsfat_test_create(node->filename, (char*) node->value, len); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + + ret = remove(node->filename); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: delete file operation failed (filename=%s, ret=%d) .\n", __func__, node->filename, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + /* (3) */ + ret = remove(node->filename); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: deleted a file that doesn't exist (filename=%s, ret=%d, errno=%d) .\n", __func__, node->filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret != 0, fsfat_fopen_utest_msg_g); + + /* (2) */ + memset(buf, 0, FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1); + memcpy(buf, node->filename, strlen(node->filename)); + pos = strrchr(buf, '/'); + *pos = '\0'; + ret = remove(buf); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: delete directory operation failed (directory name=%s, ret=%d, errno=%d).\n", __func__, buf, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + /* (4) */ + ret = remove(buf); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: deleted a directory that doesn't exist (directory name=%s, ret=%d, errno=%d).\n", __func__, buf, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret != 0, fsfat_fopen_utest_msg_g); + + return CaseNext; +} + + +/* file data for test_11 */ +static fsfat_kv_data_t fsfat_fopen_test_11_kv_data[] = { + { "/sd/test_11/step0.txt", "test_data"}, + { "/sd/test_11/step1.txt", "test_data"}, + { "/sd/test_11/subdir/step3.txt", "test_data"}, + { NULL, NULL}, +}; + +/** @brief test for operation of rename() + * + * This test does the following: + * 1) test rename() on a file that exists to a new filename within the same directory. + * 2) test rename() on a file that exists to a new filename within a different directory. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_11(const size_t call_count) +{ + int32_t ret = -1; + size_t len = 0; + fsfat_kv_data_t *node = fsfat_fopen_test_11_kv_data; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + TEST_ASSERT(strlen(node->filename) < FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1); + + /* start from a known state i.e. directory to be created in not present, files not present */ + while(node->filename != NULL) { + fsfat_filepath_remove_all((char*) node->filename); + node++; + } + + /* create file and directories ready for rename() tests */ + errno = 0; + node = fsfat_fopen_test_11_kv_data; + ret = fsfat_filepath_make_dirs((char*) node->filename, false); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, node->filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + len = strlen(node->value); + ret = fsfat_test_create(node->filename, (char*) node->value, len); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + + errno = 0; + node = &fsfat_fopen_test_11_kv_data[2]; + ret = fsfat_filepath_make_dirs((char*) node->filename, false); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, node->filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + /* (1) */ + ret = rename(fsfat_fopen_test_11_kv_data[0].filename, fsfat_fopen_test_11_kv_data[1].filename); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unable to rename file from (%s) to (%s) (ret=%d, errno=%d).\n", __func__, fsfat_fopen_test_11_kv_data[0].filename, fsfat_fopen_test_11_kv_data[1].filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + /* (2) */ + ret = rename(fsfat_fopen_test_11_kv_data[1].filename, fsfat_fopen_test_11_kv_data[2].filename); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unable to rename file from (%s) to (%s) (ret=%d, errno=%d).\n", __func__, fsfat_fopen_test_11_kv_data[1].filename, fsfat_fopen_test_11_kv_data[2].filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + return CaseNext; +} + + +/* file data for test_12 */ +static fsfat_kv_data_t fsfat_fopen_test_12_kv_data[] = { + { "/sd/test_12/subdir/testfil1.txt", "testfil1.txt"}, + { "/sd/test_12/testfil2.txt", "testfil2.txt"}, + { "/sd/test_12/testfil3.txt", "testfil3.txt"}, + { "/sd/test_12/testfil4.txt", "testfil4.txt"}, + { "/sd/test_12/testfil5.txt", "testfil5.txt"}, + { NULL, NULL}, +}; + +/** @brief test for operation of readdir(). + * + * Note, rewinddir(), telldir() and seekdir() dont appear to work reliably. + * opendir() not available on ARM/IAR toolchains. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_12(const size_t call_count) +{ + char buf[FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1]; + char *pos = NULL; + int32_t count = 0; + int32_t ret = -1; + size_t len = 0; + DIR *dir; + struct dirent *dp; + fsfat_kv_data_t *node = fsfat_fopen_test_12_kv_data; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) + + /* start from a known state i.e. directory to be created in not present */ + while(node->filename != NULL) { + fsfat_filepath_remove_all((char*) node->filename); + node++; + } + + /* create a file */ + node = fsfat_fopen_test_12_kv_data; + errno = 0; + ret = fsfat_filepath_make_dirs((char*) node->filename, false); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, node->filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + node = fsfat_fopen_test_12_kv_data; + while(node->filename != NULL) { + len = strlen(node->value); + ret = fsfat_test_create(node->filename, (char*) node->value, len); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + node++; + } + + node = fsfat_fopen_test_12_kv_data; + memset(buf, 0, FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1); + memcpy(buf, node->filename, strlen(node->filename)); + pos = strrchr(buf, '/'); + *pos = '\0'; + dir = opendir(buf); + + dp = readdir(dir); + TEST_ASSERT_MESSAGE(dp != 0, "Error: readdir() failed\n"); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected object name (name=%s, expected=%s).\n", __func__, dp->d_name, "."); + TEST_ASSERT_MESSAGE(strncmp(dp->d_name, ".", strlen(".")) == 0, fsfat_fopen_utest_msg_g); + dp = readdir(dir); + TEST_ASSERT_MESSAGE(dp != 0, "Error: readdir() failed\n"); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected object name (name=%s, expected=%s).\n", __func__, dp->d_name, ".."); + TEST_ASSERT_MESSAGE(strncmp(dp->d_name, "..", strlen("..")) == 0, fsfat_fopen_utest_msg_g); + + while ((dp = readdir(dir)) != NULL) { + FSFAT_DBGLOG("%s: filename: \"%s\"\n", __func__, dp->d_name); + TEST_ASSERT_MESSAGE(dp != 0, "Error: readdir() failed\n"); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected object name (name=%s, expected=%s).\n", __func__, dp->d_name, fsfat_fopen_test_12_kv_data[count].value); + TEST_ASSERT_MESSAGE(strncmp(dp->d_name, fsfat_fopen_test_12_kv_data[count].value, strlen(fsfat_fopen_test_12_kv_data[count].value)) == 0, fsfat_fopen_utest_msg_g); + count++; + } + closedir(dir); + + /* cleanup */ + node = fsfat_fopen_test_12_kv_data; + while(node->filename != NULL) { + fsfat_filepath_remove_all((char*) node->filename); + node++; + } +#endif /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */ + return CaseNext; +} + + +/* file data for test_13 */ +static fsfat_kv_data_t fsfat_fopen_test_13_kv_data[] = { + /* a file is included in the filepath even though its not created by the test, + * as the fsfat_filepath_make_dirs() works with it present. */ + { "/sd/test_13/dummy.txt", "testdir"}, + { NULL, NULL}, +}; +/** @brief test for operation of mkdir()/remove() + * + * This test checks that: + * - The mkdir() function successfully creates a directory that is not already present. + * - The mkdir() function returns EEXIST when trying to create a directory thats already present. + * - The remove() function successfully removes a directory that is present. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_13(const size_t call_count) +{ + int32_t ret = 0; + + FSFAT_DBGLOG("%s:entered\n", __func__); + (void) call_count; + + /* start from a known state i.e. directory to be created in not present */ + fsfat_filepath_remove_all((char*) fsfat_fopen_test_13_kv_data[0].filename); + + errno = 0; + ret = fsfat_filepath_make_dirs((char*) fsfat_fopen_test_13_kv_data[0].filename, false); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, fsfat_fopen_test_13_kv_data[0].filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + /* check that get a suitable error when try to create it again.*/ + errno = 0; + ret = fsfat_filepath_make_dirs((char*) fsfat_fopen_test_13_kv_data[0].filename, false); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: permitted to create directory when already exists (dirname=%s, ret=%d, errno=%d)\n", __func__, fsfat_fopen_test_13_kv_data[0].filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret != 0, fsfat_fopen_utest_msg_g); + + /* check errno is as expected */ + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: errno != EEXIST (dirname=%s, ret=%d, errno=%d)\n", __func__, fsfat_fopen_test_13_kv_data[0].filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(errno == EEXIST, fsfat_fopen_utest_msg_g); + + ret = fsfat_filepath_remove_all((char*) fsfat_fopen_test_13_kv_data[0].filename); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to remove directory (dirname=%s, ret=%d, errno=%d)\n", __func__, fsfat_fopen_test_13_kv_data[0].filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + return CaseNext; +} + +/* file data for test_14 */ +static fsfat_kv_data_t fsfat_fopen_test_14_kv_data[] = { + /* a file is included in the filepath even though its not created by the test, + * as the fsfat_filepath_make_dirs() works with it present. */ + { "/sd/test_14/testfile.txt", "testdata"}, + { NULL, NULL}, +}; + +/** @brief test for operation of stat() + * + * stat() is currently no supported by ARMCC and IAR toolchains libc. + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_14(const size_t call_count) +{ +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) + + char buf[FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1]; + char *pos = NULL; + int32_t ret = -1; + size_t len = 0; + struct stat file_stat; + fsfat_kv_data_t *node = fsfat_fopen_test_14_kv_data; + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + + TEST_ASSERT(strlen(node->filename) < FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1); + + /* start from a known state i.e. directory to be created in not present */ + fsfat_filepath_remove_all((char*) node->filename); + + /* Create file in a directory. */ + errno = 0; + ret = fsfat_filepath_make_dirs((char*) node->filename, false); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, node->filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + len = strlen(node->value); + ret = fsfat_test_create(node->filename, (char*) node->value, len); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (ret=%d).\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g); + + /* Test stat() on the file returns the correct attribute set */ + memset(&file_stat, 0, sizeof(file_stat)); + ret = stat(node->filename, &file_stat); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: stat() operation on file failed (filename=%s, ret=%d, errno=%d).\n", __func__, node->filename, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: expected st_mode S_IFREG flag not set (filename=%s).\n", __func__, node->filename); + TEST_ASSERT_MESSAGE((file_stat.st_mode & S_IFREG) == S_IFREG, fsfat_fopen_utest_msg_g); + + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected st_mode S_IFDIR flag set (filename=%s).\n", __func__, node->filename); + TEST_ASSERT_MESSAGE((file_stat.st_mode & S_IFDIR) != S_IFDIR, fsfat_fopen_utest_msg_g); + + /* Test stat() on the directory returns the correct attribute set */ + memset(&file_stat, 0, sizeof(file_stat)); + memset(buf, 0, FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1); + memcpy(buf, node->filename, strlen(node->filename)); + pos = strrchr(buf, '/'); + *pos = '\0'; + ret = stat(buf, &file_stat); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: stat() operation on directory failed (directory name=%s, ret=%d, errno=%d).\n", __func__, buf, (int) ret, errno); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected st_mode S_IFREG flag set (directory name=%s).\n", __func__, buf); + TEST_ASSERT_MESSAGE((file_stat.st_mode & S_IFREG) != S_IFREG, fsfat_fopen_utest_msg_g); + + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: expected st_mode S_IFDIR flag not set (directory name=%s).\n", __func__, buf); + TEST_ASSERT_MESSAGE((file_stat.st_mode & S_IFDIR) == S_IFDIR, fsfat_fopen_utest_msg_g); + + /* clean up after successful test */ + fsfat_filepath_remove_all((char*) node->filename); + +#endif /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */ + return CaseNext; +} + +/** @brief test for operation of SDFileSystem::format() + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_00(const size_t call_count) +{ + + FSFAT_FENTRYLOG("%s:entered\n", __func__); + (void) call_count; + int32_t ret = -1; + + /* the allocation_unit of 0 means chanFS will use the default for the card (varies according to capacity). */ + fs.unmount(); + ret = fs.format(&sd); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to format sdcard (ret=%d)\n", __func__, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + fs.mount(&sd); + return CaseNext; +} + + +/* @brief test utility function to create a file of a given size. + * + * A reference data table is used of so that the data file can be later be + * checked with fsfat_test_check_data_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_data_file(const char* filename, size_t len) +{ + int32_t ret = -1; + FILE *fp = NULL; + size_t write_len = 0; + size_t written_len = 0; + int32_t exp = 0; + const int32_t exp_max = 8; /* so as not to exceed FSFAT_TEST_BYTE_DATA_TABLE_SIZE/2 */ + + FSFAT_FENTRYLOG("%s:entered (filename=%s, len=%d).\n", __func__, filename, (int) len); + TEST_ASSERT(len % FSFAT_TEST_BYTE_DATA_TABLE_SIZE == 0); + fp = fopen(filename, "a"); + if(fp == NULL){ + return ret; + } + + while(written_len < len) { + /* write fsfat_test_byte_data_table or part thereof, in 9 writes of sizes + * 1, 2, 4, 8, 16, 32, 64, 128, 1, totalling 256 bytes len permitting. */ + for(exp = 0; (exp <= exp_max) && (written_len < len); exp++){ + write_len = 0x1 << (exp % exp_max); + write_len = len - written_len > write_len ? write_len : len - written_len; + ret = fwrite((const void*) &fsfat_test_byte_data_table[written_len % FSFAT_TEST_BYTE_DATA_TABLE_SIZE], write_len, 1, fp); + written_len += write_len; + if(ret != 1){ + FSFAT_DBGLOG("%s:Error: fwrite() failed (ret=%d)\n", __func__, (int) ret); + ret = -1; + goto out0; + } + } + } + if(written_len == len) { + ret = 0; + } else { + ret = -1; + } +out0: + fclose(fp); + return ret; +} + + +/* @brief test utility function to check the data in the specified file is correct. + * + * The data read from the file is check that it agrees with the data written by + * fsfat_test_create_data_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_check_data_file(const char* filename, size_t len) +{ + int32_t ret = -1; + FILE *fp = NULL; + size_t read_len = 0; + uint8_t buf[FSFAT_TEST_BYTE_DATA_TABLE_SIZE]; + + FSFAT_FENTRYLOG("%s:entered (filename=%s, len=%d).\n", __func__, filename, (int) len); + TEST_ASSERT(len % FSFAT_TEST_BYTE_DATA_TABLE_SIZE == 0); + fp = fopen(filename, "r"); + if(fp == NULL){ + return ret; + } + + while(read_len < len) { + ret = fread((void*) buf, FSFAT_TEST_BYTE_DATA_TABLE_SIZE, 1, fp); + read_len += FSFAT_TEST_BYTE_DATA_TABLE_SIZE; + if(ret == 0){ + /* end of read*/ + FSFAT_DBGLOG("%s:unable to read data\n", __func__); + break; + } + if(memcmp(buf, fsfat_test_byte_data_table, FSFAT_TEST_BYTE_DATA_TABLE_SIZE) != 0) { + FSFAT_DBGLOG("%s:Error: read data not as expected (0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x\n", __func__, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + ret = -1; + goto out0; + } + } + if(read_len == len) { + ret = 0; + } +out0: + fclose(fp); + return ret; +} + +/* file data for test_16 */ +static fsfat_kv_data_t fsfat_fopen_test_15_kv_data[] = { + { "/sd/tst16_0/testfil0.txt", "dummy_data"}, + { "/sd/tst16_1/subdir0/testfil0.txt", "dummy_data"}, + { "/sd/tst16_2/subdir0/subdir1/testfil0.txt", "dummy_data"}, + { "/sd/tst16_3/subdir0/subdir1/subdir2/subdir3/testfil0.txt", "dummy_data"}, + +#if 0 + { "/sd/tst16_4/subdir0/subdir1/subdir2/subdir3/subdir4/testfil0.txt", "dummy_data"}, + { "/sd/tst16_5/subdir0/subdir1/subdir2/subdir3/subdir4/subdir5/testfil0.txt", "dummy_data"}, + { "/sd/tst16_6/subdir0/subdir1/subdir2/subdir3/subdir4/subdir5/subdir6/testfil0.txt", "dummy_data"}, + { "/sd/tst16_7/subdir0/subdir1/subdir2/subdir3/subdir4/subdir5/subdir6/subdir7/testfil0.txt", "dummy_data"}, + { "/sd/tst16_8/subdir0/subdir1/subdir2/subdir3/subdir4/subdir5/subdir6/subdir7/subdir8/testfil0.txt", "dummy_data"}, + { "/sd/tst16_9/subdir0/subdir1/subdir2/subdir3/subdir4/subdir5/subdir6/subdir7/subdir8/subdir9/testfil0.txt", "dummy_data"}, +#endif + { NULL, NULL}, +}; + + +/** @brief stress test to write data to fs + * + * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors. + */ +control_t fsfat_fopen_test_15(const size_t call_count) +{ + int32_t ret = 0; + fsfat_kv_data_t *node = fsfat_fopen_test_15_kv_data; + const int32_t num_blocks = 100; /* each file ~25kB */ + + FSFAT_DBGLOG("%s:entered\n", __func__); + (void) call_count; + + /* remove file and directory from a previous failed test run, if present */ + while(node->filename != NULL) { + fsfat_filepath_remove_all((char*) node->filename); + node++; + } + + /* create dirs */ + node = fsfat_fopen_test_15_kv_data; + while(node->filename != NULL) { + ret = fsfat_filepath_make_dirs((char*) node->filename, true); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dirs for filename (filename=\"%s\")(ret=%d)\n", __func__, node->filename, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + node++; + } + + /* create the data files */ + node = fsfat_fopen_test_15_kv_data; + while(node->filename != NULL) { + ret = fsfat_test_create_data_file(node->filename, num_blocks * FSFAT_TEST_BYTE_DATA_TABLE_SIZE); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create data file (filename=\"%s\")(ret=%d)\n", __func__, node->filename, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + node++; + } + + /* read the data back and check its as expected */ + node = fsfat_fopen_test_15_kv_data; + while(node->filename != NULL) { + ret = fsfat_test_check_data_file(node->filename, num_blocks * FSFAT_TEST_BYTE_DATA_TABLE_SIZE); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to check data file (filename=\"%s\")(ret=%d)\n", __func__, node->filename, (int) ret); + TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g); + node++; + } + + /* clean up */ + node = fsfat_fopen_test_15_kv_data; + while(node->filename != NULL) { + fsfat_filepath_remove_all((char*) node->filename); + node++; + } + return CaseNext; +} + +/// @cond FSFAT_DOXYGEN_DISABLE +utest::v1::status_t greentea_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(FSFAT_FOPEN_GREENTEA_TIMEOUT_S, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Case cases[] = { + /* 1 2 3 4 5 6 7 */ + /* 1234567890123456789012345678901234567890123456789012345678901234567890 */ + Case("FSFAT_FOPEN_TEST_00: format() test.", FSFAT_FOPEN_TEST_00), + Case("FSFAT_FOPEN_TEST_01: fopen()/fwrite()/fclose() directories/file in multi-dir filepath.", FSFAT_FOPEN_TEST_01), + Case("FSFAT_FOPEN_TEST_02: fopen(r) pre-existing file try to write it.", FSFAT_FOPEN_TEST_02), + Case("FSFAT_FOPEN_TEST_03: fopen(w+) pre-existing file try to write it.", FSFAT_FOPEN_TEST_03), + Case("FSFAT_FOPEN_TEST_04: fopen() with a filename exceeding the maximum length.", FSFAT_FOPEN_TEST_04), +#ifdef FOPEN_EXTENDED_TESTING + Case("FSFAT_FOPEN_TEST_05: fopen() with bad filenames (extended).", FSFAT_FOPEN_TEST_05), +#endif + Case("FSFAT_FOPEN_TEST_06: fopen() with bad filenames (minimal).", FSFAT_FOPEN_TEST_06), + Case("FSFAT_FOPEN_TEST_07: fopen()/errno handling.", FSFAT_FOPEN_TEST_07), + Case("FSFAT_FOPEN_TEST_08: ferror()/clearerr()/errno handling.", FSFAT_FOPEN_TEST_08), + Case("FSFAT_FOPEN_TEST_09: ftell() handling.", FSFAT_FOPEN_TEST_09), + Case("FSFAT_FOPEN_TEST_10: remove() test.", FSFAT_FOPEN_TEST_10), + Case("FSFAT_FOPEN_TEST_11: rename().", FSFAT_FOPEN_TEST_11), + Case("FSFAT_FOPEN_TEST_12: opendir(), readdir(), closedir() test.", FSFAT_FOPEN_TEST_12), + Case("FSFAT_FOPEN_TEST_13: mkdir() test.", FSFAT_FOPEN_TEST_13), + Case("FSFAT_FOPEN_TEST_14: stat() test.", FSFAT_FOPEN_TEST_14), + Case("FSFAT_FOPEN_TEST_15: write/check n x 25kB data files.", FSFAT_FOPEN_TEST_15), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond diff --git a/mbed_lib.json b/mbed_lib.json new file mode 100644 index 0000000000..247de9f6e1 --- /dev/null +++ b/mbed_lib.json @@ -0,0 +1,19 @@ +{ + "name": "flashiap-block-device", + "config": { + "base-address": { + "help": "Base address for the block device on the external flash.", + "value": null + }, + "size": { + "help": "Memory allocated for block device.", + "value": null + } + }, + "target_overrides": { + "REALTEK_RTL8195AM": { + "base-address": "0xC0000", + "size": "0x40000" + } + } +} diff --git a/util/fsfat_debug.h b/util/fsfat_debug.h new file mode 100644 index 0000000000..79ad59aeec --- /dev/null +++ b/util/fsfat_debug.h @@ -0,0 +1,88 @@ +/** @file fsfat_debug.h + * + * component debug header file. + */ + + +#ifndef __FSFAT_DEBUG +#define __FSFAT_DEBUG + +#include +#include +#include + + +/* 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 + +#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*/ diff --git a/util/fsfat_test.c b/util/fsfat_test.c new file mode 100644 index 0000000000..98d13d9919 --- /dev/null +++ b/util/fsfat_test.c @@ -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 +#include +#include +#include +#include +#include + + +#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; +} + diff --git a/util/fsfat_test.h b/util/fsfat_test.h new file mode 100644 index 0000000000..a4a6fd12fc --- /dev/null +++ b/util/fsfat_test.h @@ -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 +#include +#include + +#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 */ From 90e82da7e6e5c3c28b1897d8f9796d72bca0cf2a Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Wed, 19 Jul 2017 17:27:52 +0100 Subject: [PATCH 03/10] Update README.md --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e62e54ebd..fd0c4e2804 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,11 @@ # flashiap-driver -Block device driver using the mbed OS FlashIAP API +Block device driver using the mbed OS FlashIAP API. + +# Warning +This driver should only be used on platforms where the FlashIAP implementation is using external flash or in conjunction with a filesystem with wear leveling, that can operate on a page size granularity. + +The BlockDevice API assumes a uniform erase size so the underlying flash must also have uniform sectors. + +# Tested on + +* Realtek RTL8195AM From 6cc52dc2379199d6ee212410449b91097a242fb3 Mon Sep 17 00:00:00 2001 From: Christopher Haster Date: Thu, 21 Sep 2017 15:28:13 -0500 Subject: [PATCH 04/10] Added more warnings about FlashIAP --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fd0c4e2804..982f23094e 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,13 @@ Block device driver using the mbed OS FlashIAP API. # Warning +This driver is **EXPERIMENTAL** and improper usage could kill your board's flash. + This driver should only be used on platforms where the FlashIAP implementation is using external flash or in conjunction with a filesystem with wear leveling, that can operate on a page size granularity. -The BlockDevice API assumes a uniform erase size so the underlying flash must also have uniform sectors. +Additional concerns: +- The BlockDevice API assumes a uniform erase size so the underlying flash must also have uniform sectors. +- The FlashIAP may freeze code execution for a long period of time while writing to flash. Not even high-priority irqs will be allowed to run, which may interrupt background processes. # Tested on From 29cfe74f13d19afdd5145dce9c4555bdb68998a3 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Wed, 1 Nov 2017 11:51:40 -0700 Subject: [PATCH 05/10] Add default values and ignore utilities --- .mbedignore | 2 ++ mbed_lib.json | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 .mbedignore diff --git a/.mbedignore b/.mbedignore new file mode 100644 index 0000000000..34b9762d75 --- /dev/null +++ b/.mbedignore @@ -0,0 +1,2 @@ +util/* + diff --git a/mbed_lib.json b/mbed_lib.json index 247de9f6e1..cd580f5869 100644 --- a/mbed_lib.json +++ b/mbed_lib.json @@ -3,11 +3,11 @@ "config": { "base-address": { "help": "Base address for the block device on the external flash.", - "value": null + "value": "0xFFFFFFFF" }, "size": { "help": "Memory allocated for block device.", - "value": null + "value": "0" } }, "target_overrides": { From 85dba9dd34feef43369b4b1b0b05dd3e790f6415 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Wed, 1 Nov 2017 15:02:40 -0500 Subject: [PATCH 06/10] Update README.md --- README.md | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 982f23094e..998f1ebbe8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ -# flashiap-driver -Block device driver using the mbed OS FlashIAP API. +# Block Device driver build on top of FlashIAP API -# Warning +## Warning This driver is **EXPERIMENTAL** and improper usage could kill your board's flash. This driver should only be used on platforms where the FlashIAP implementation is using external flash or in conjunction with a filesystem with wear leveling, that can operate on a page size granularity. @@ -10,6 +9,24 @@ Additional concerns: - The BlockDevice API assumes a uniform erase size so the underlying flash must also have uniform sectors. - The FlashIAP may freeze code execution for a long period of time while writing to flash. Not even high-priority irqs will be allowed to run, which may interrupt background processes. -# Tested on + +## Configuration + +The driver must be configured with the starting address for the internal flash area reserved for the block device and it's size in bytes. In the application's `mbed_app.json`, add the following lines: + +```json +{ + "target_overrides": { + "K64F": { + "flashiap-block-device.base-address": "", + "flashiap-block-device.size": "" + } + } +} +``` + + +## Tested on * Realtek RTL8195AM +* K64F From 168278a4a567bb056edeef4b3ef52f0deafe8e33 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Wed, 1 Nov 2017 13:03:53 -0700 Subject: [PATCH 07/10] Hide code behind DEVICE_FLASH flag --- FlashIAPBlockDevice.cpp | 4 ++++ FlashIAPBlockDevice.h | 3 +++ 2 files changed, 7 insertions(+) diff --git a/FlashIAPBlockDevice.cpp b/FlashIAPBlockDevice.cpp index cdb0fec6f0..e43e6c24b3 100644 --- a/FlashIAPBlockDevice.cpp +++ b/FlashIAPBlockDevice.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#ifdef DEVICE_FLASH + #include "FlashIAPBlockDevice.h" #include "mbed.h" @@ -155,3 +157,5 @@ bd_size_t FlashIAPBlockDevice::size() const return _size; } + +#endif /* DEVICE_FLASH */ diff --git a/FlashIAPBlockDevice.h b/FlashIAPBlockDevice.h index 5b915b82d4..7999e5e58b 100644 --- a/FlashIAPBlockDevice.h +++ b/FlashIAPBlockDevice.h @@ -17,6 +17,8 @@ #ifndef MBED_FLASHIAP_BLOCK_DEVICE_H #define MBED_FLASHIAP_BLOCK_DEVICE_H +#ifdef DEVICE_FLASH + #include "BlockDevice.h" #include @@ -121,4 +123,5 @@ private: bd_size_t _size; }; +#endif /* DEVICE_FLASH */ #endif /* MBED_FLASHIAP_BLOCK_DEVICE_H */ From 179e91537bd3324c4742cffd647451ee0549d733 Mon Sep 17 00:00:00 2001 From: Marcus Chang Date: Tue, 7 Nov 2017 09:18:49 -0800 Subject: [PATCH 08/10] Move default location for REALTEK_RTL8195AM up 1 MiB --- mbed_lib.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mbed_lib.json b/mbed_lib.json index cd580f5869..49b4cdb7b9 100644 --- a/mbed_lib.json +++ b/mbed_lib.json @@ -12,7 +12,7 @@ }, "target_overrides": { "REALTEK_RTL8195AM": { - "base-address": "0xC0000", + "base-address": "0x1C0000", "size": "0x40000" } } From 5418b0b6fae6a6f479a3730c4fda847fef37e623 Mon Sep 17 00:00:00 2001 From: David Saada Date: Wed, 4 Jul 2018 16:38:01 +0300 Subject: [PATCH 09/10] Implement the get_erase_size API (based on address) --- FlashIAPBlockDevice.cpp | 9 +++++++++ FlashIAPBlockDevice.h | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/FlashIAPBlockDevice.cpp b/FlashIAPBlockDevice.cpp index e43e6c24b3..51fb3f4321 100644 --- a/FlashIAPBlockDevice.cpp +++ b/FlashIAPBlockDevice.cpp @@ -151,6 +151,15 @@ bd_size_t FlashIAPBlockDevice::get_erase_size() const return erase_size; } +bd_size_t FlashIAPBlockDevice::get_erase_size(bd_addr_t addr) const +{ + 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); diff --git a/FlashIAPBlockDevice.h b/FlashIAPBlockDevice.h index 7999e5e58b..39ede5f592 100644 --- a/FlashIAPBlockDevice.h +++ b/FlashIAPBlockDevice.h @@ -110,6 +110,14 @@ public: */ 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 From d1b5c5e995cdbb4003f7cb42ab1282d47a64ad86 Mon Sep 17 00:00:00 2001 From: David Saada Date: Sun, 5 Aug 2018 19:29:22 +0300 Subject: [PATCH 10/10] Change driver initialization behavior: - Remove configuration file (base and size taken from driver). - Add initializtion variables (is initialized and rinit ref count). - Add destructor. --- FlashIAPBlockDevice.cpp | 69 +++++++++++++++++++++++++++++++++++++---- FlashIAPBlockDevice.h | 25 ++++++--------- README.md | 18 ++--------- mbed_lib.json | 19 ------------ 4 files changed, 74 insertions(+), 57 deletions(-) delete mode 100644 mbed_lib.json diff --git a/FlashIAPBlockDevice.cpp b/FlashIAPBlockDevice.cpp index 51fb3f4321..a79f57f482 100644 --- a/FlashIAPBlockDevice.cpp +++ b/FlashIAPBlockDevice.cpp @@ -17,6 +17,7 @@ #ifdef DEVICE_FLASH #include "FlashIAPBlockDevice.h" +#include "mbed_critical.h" #include "mbed.h" @@ -35,23 +36,67 @@ #define DEBUG_PRINTF(...) #endif -FlashIAPBlockDevice::FlashIAPBlockDevice(uint32_t address, uint32_t size) - : _flash(), _base(address), _size(size) +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"); - return _flash.init(); + 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(); } @@ -65,7 +110,7 @@ int FlashIAPBlockDevice::read(void *buffer, int result = BD_ERROR_DEVICE_ERROR; /* Check that the address and size are properly aligned and fit. */ - if (is_valid_read(virtual_address, size)) { + 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; @@ -89,7 +134,7 @@ int FlashIAPBlockDevice::program(const void *buffer, int result = BD_ERROR_DEVICE_ERROR; /* Check that the address and size are properly aligned and fit. */ - if (is_valid_program(virtual_address, size)) { + 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; @@ -114,7 +159,7 @@ int FlashIAPBlockDevice::erase(bd_addr_t virtual_address, int result = BD_ERROR_DEVICE_ERROR; /* Check that the address and size are properly aligned and fit. */ - if (is_valid_erase(virtual_address, size)) { + 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; @@ -135,6 +180,10 @@ bd_size_t FlashIAPBlockDevice::get_read_size() const 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); @@ -144,6 +193,10 @@ bd_size_t FlashIAPBlockDevice::get_program_size() const 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); @@ -153,6 +206,10 @@ bd_size_t FlashIAPBlockDevice::get_erase_size() const 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); diff --git a/FlashIAPBlockDevice.h b/FlashIAPBlockDevice.h index 39ede5f592..54c1a8a486 100644 --- a/FlashIAPBlockDevice.h +++ b/FlashIAPBlockDevice.h @@ -22,14 +22,6 @@ #include "BlockDevice.h" #include -#ifndef MBED_CONF_FLASHIAP_BLOCK_DEVICE_BASE_ADDRESS -#error flashiap-block-device.base-address not set -#endif - -#ifndef MBED_CONF_FLASHIAP_BLOCK_DEVICE_SIZE -#error flashiap-block-device.size not set -#endif - /** BlockDevice using the FlashIAP API * * @code @@ -39,14 +31,13 @@ */ class FlashIAPBlockDevice : public BlockDevice { public: - /** Creates a FlashIAPBlockDevice, starting at the given address and - * with a certain size. - * - * @param address Starting address for the BlockDevice - * @param size Maximum size allocated to this BlockDevice - */ - FlashIAPBlockDevice(uint32_t address = MBED_CONF_FLASHIAP_BLOCK_DEVICE_BASE_ADDRESS, - uint32_t size = MBED_CONF_FLASHIAP_BLOCK_DEVICE_SIZE); + /** 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 * @@ -129,6 +120,8 @@ private: FlashIAP _flash; bd_addr_t _base; bd_size_t _size; + bool _is_initialized; + uint32_t _init_ref_count; }; #endif /* DEVICE_FLASH */ diff --git a/README.md b/README.md index 998f1ebbe8..54165e0550 100644 --- a/README.md +++ b/README.md @@ -6,27 +6,13 @@ This driver is **EXPERIMENTAL** and improper usage could kill your board's flash This driver should only be used on platforms where the FlashIAP implementation is using external flash or in conjunction with a filesystem with wear leveling, that can operate on a page size granularity. Additional concerns: -- The BlockDevice API assumes a uniform erase size so the underlying flash must also have uniform sectors. - The FlashIAP may freeze code execution for a long period of time while writing to flash. Not even high-priority irqs will be allowed to run, which may interrupt background processes. ## Configuration - -The driver must be configured with the starting address for the internal flash area reserved for the block device and it's size in bytes. In the application's `mbed_app.json`, add the following lines: - -```json -{ - "target_overrides": { - "K64F": { - "flashiap-block-device.base-address": "", - "flashiap-block-device.size": "" - } - } -} -``` - +None. ## Tested on -* Realtek RTL8195AM +* K82F * K64F diff --git a/mbed_lib.json b/mbed_lib.json deleted file mode 100644 index 49b4cdb7b9..0000000000 --- a/mbed_lib.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "flashiap-block-device", - "config": { - "base-address": { - "help": "Base address for the block device on the external flash.", - "value": "0xFFFFFFFF" - }, - "size": { - "help": "Memory allocated for block device.", - "value": "0" - } - }, - "target_overrides": { - "REALTEK_RTL8195AM": { - "base-address": "0x1C0000", - "size": "0x40000" - } - } -}