Fork of https://github.com/ARMmbed/sd-driver. Added pin config for MAX32630FTHR
Dependents: CircularBufferSDCardLib time_between_inerupt
Revision 0:69bfc1595ae5, committed 2018-03-20
- Comitter:
- DVLevine
- Date:
- Tue Mar 20 17:35:00 2018 +0000
- Commit message:
- Initial commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/LICENSE Tue Mar 20 17:35:00 2018 +0000 @@ -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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,612 @@ +# mbed OS SDCard Driver (sd-driver) for FAT32 Filesystem Support + + +Simon Hughes + +20170329 + +Version 0.1.2 + + +# Executive Summary + +The purpose of this document is to describe how to use the mbed OS SDCard +driver (sd-driver) so applications can read/write +data to flash storage cards using the standard POSIX File API +programming interface. The sd-driver uses the SDCard SPI-mode of operation +which is a subset of possible SDCard functionality. + +This repository contains the mbed-os SDCard driver for generic SPI +SDCard support and other resources, as outlined below: + +- `SDBlockDevice.h` and `SDBlockDevice.cpp`. This is the SDCard driver module presenting + a Block Device API (derived from BlockDevice) to the underlying SDCard. +- POSIX File API test cases for testing the FAT32 filesystem on SDCard. + - basic.cpp, a basic set of functional test cases. + - fopen.cpp, more functional tests reading/writing greater volumes of data to SDCard, for example. +- `mbed_lib.json` mbed-os application configuration file with SPI pin configurations for the CI shield and overrides for specific targets. + This file allows the SPI pins to be specified for the target without having to edit the implementation files. +- This README which includes [Summary of POSIX File API Documentation](#summary-posix-api-documentation) + including detailed instruction on how to use the FAT filesystem and SDBlockDevice driver. + +The SDCard driver is maintained in this repository as a component separate from the main mbed OS repository. +Hence the 2 repositories (mbed-os and sd-driver) have to be used together +to deliver the FAT32 Filesystem/SDCard support. This document explains how to do this. + + +# Introduction + +### Overview + +The scope of this document is to describe how applications use the FAT filesystem and sd-driver +components to persistently store data on SDCards. The document is intended to help developers adopt the +mbed OS POSIX File API support, and in particular to help explain: + +- How the software components work together to deliver the storage functionality. +- How to work with the sd-driver and mbed OS to build the examples. The example code can easily + be copied into your new application code. +- How to work with the CI Test Shield, which adds an SDCard slot to those targets that do not have already have one. +- How to run the POSIX File API mbed Greentea test cases, which provide further example code of how to use + the POSIX File API. + +Section 1 provides an Executive Summary, describing the purpose of the sd-driver, the supporting +software, examples, test cases and documentation. + +Section 2 provides an an overview of the material covered including descriptions of the major sections. + +Section 3 provides an overview of the mbed OS filesystem software components, +including the inter-relationships between the application, POSIX file API, the standard c-library, +the mbed OS filesystem and the SDCard driver (sd-driver). + +Section 4 describes how to build and run an example application for reading +and writing data to an SDCard using the POSIX File API. The example begins by describing +the procedure for building and testing on the K64F target. The final sub-sections +describe how to use the test shield to add an SDCard slot to any mbed target, +and hence enable the persistent storage of data on any supported target. + +Section 5 describes an example application which uses the raw +BlockDevice API to read and write data to the SDCard. + +Section 6 describes how to build and run the SDCard POSIX File API mbed Greentea test cases. +There are a number of functional test cases demonstrating how to use the +mbed OS POSIX File API. + +Section 7 describes the POSIX File API and provides links to useful API documentation web pages. + + +### Known mbed-os and sd-driver Compatible Versions + +The following versions of the mbed-os and sd-driver repositories are known to work together: + +- {mbed-os, sd-driver} = {mbed-os-5.4.0-rc2, sd-driver-0.0.1-mbed-os-5.4.0-rc2}. + `K64F`, `NUCLEO_F429ZI` and `UBLOX_EVK_ODIN_W2` fopen and basic filesystem tests working. +- {mbed-os, sd-driver} = {mbed-os-5.4.0, sd-driver-0.0.2-mbed-os-5.4.0}. + `K64F`, `NUCLEO_F429ZI` and `UBLOX_EVK_ODIN_W2` fopen and basic filesystem tests working. +- {mbed-os, sd-driver} = {mbed-os-5.4.1, sd-driver-0.0.3-mbed-os-5.4.1}. +- {mbed-os, sd-driver} = {mbed-os-5.5.1, sd-driver-0.1.0-mbed-os-5.5.1}. +- {mbed-os, sd-driver} = {mbed-os-5.5.4, sd-driver-0.1.1-mbed-os-5.5.4}. +- {mbed-os, sd-driver} = {mbed-os-5.6.1, sd-driver-0.1.2-mbed-os-5.6.1}. + +To find the latest compatible versions, use the following command to see the messages attached to the tags +in the sd-driver repository: + + ex_app7/$ cd sd-driver + ex_app7/sd-driver$ git tag -n + sd-driver-0.0.1-mbed-os-5.3.4 Version compatible with mbed-os-5.3.4, and private_mbedos_filesystems-0.0.1-mbed-os-5.3.4. + sd-driver-0.0.2-mbed-os-5.4.0 Updated README.md to include worked exmaples and restructuring of information. + sd-driver-0.0.3-mbed-os-5.4.1 Version compatible with mbed-os-5.4.1. + sd-driver-0.1.1-mbed-os-5.5.4 Version compatible with mbed-os-5.5.4 + sd-driver-0.1.2-mbed-os-5.6.1 Version compatible with mbed-os-5.6.1 + + +### Known Issues With This Document + +There are no known issues with this document. + + +# Overview of mbed OS Filesystem Software Component Stack + + + ------------------------ + | | + | Application | // This application uses the POSIX File API + | | // to read/write data to persistent storage backends. + ------------------------ + + ------------------------ // POSIX File API (ISO). + + ------------------------ + | | + | libc | // The standard c library implementation + | | // e.g. newlib. + ------------------------ + + ------------------------ // sys_xxx equivalent API. + + ------------------------ + | | + | mbed_retarget.cpp | // Target specific mapping layer. + | | + ------------------------ + + ------------------------ // Filesystem Upper Edge API. + + ------------------------ + | | + | File System | // File system wrappers and implementation. + | | + ------------------------ + + ------------------------ // FS Lower Edge API (Block Store Interface). + + ------------------------ + | Block API | + | Device Driver | // The SDCard driver, for example. + | e.g. sd-driver | + ------------------------ + + ------------------------ // SPI.h interface. + + ------------------------ + | | + | SPI | // SPI subsystem (C++ classes and C-HAL implementation). + | | + ------------------------ + + Figure 1. mbedOS generic architecture of filesystem software stack. + +The figure above shows the mbed OS software component stack used for data +storage on SDCard: + +- At the top level is the application component which uses the standard POSIX File API + to read and write application data to persistent storage. +- The newlib standard library (libc) stdio.h interface (POSIX File API) + implementation is used as it's optimised for resource limited embedded systems. +- mbed_retarget.cpp implements the libc back-end file OS handlers and maps them + to the FileSystem. +- The File System code (hosted in mbed-os) is composed of 2 parts: + - The mbed OS file system wrapper classes (e.g. FileSystem, File, FileBase classes) + which are used to present a consistent API to the retarget module for different + (third-party) file system implementations. + - The FAT filesystem implementation code. + The [FATFS: Generic FAT File System Module](http://elm-chan.org/fsw/ff/00index_e.html) + (ChanFS) has been integrated within mbed-os. +- The Block API Device Driver. The SDCard driver is an example of a persistent storage driver. + It's maintained as a separate component from the mbed OS repository (in this repository). +- The SPI module provides the mbed OS generic SPI API. This functionality is maintained in + mbed OS. + + +# SDCard POSIX File API Example App for Reading/Writing Data + +Refer to [SD driver Example](https://github.com/ARMmbed/mbed-os-example-sd-driver) + + +### <a name="testing-with-an-sdcard-on-target-xyx"></a> Testing with an SDCard on Target XYZ + +The standard way to test is with the mbed CI Test Shield plugged into the +target board. This pin mapping for this configuration is parameterised in +the `mbed_lib.json` file. + +The following is an example of the `mbed_lib.json` file available in the repository: + + { + "config": { + "SPI_CS": "D10", + "SPI_MOSI": "D11", + "SPI_MISO": "D12", + "SPI_CLK": "D13", + "DEVICE_SPI": 1, + "FSFAT_SDCARD_INSTALLED": 1 + }, + "target_overrides": { + "DISCO_F051R8": { + "SPI_MOSI": "SPI_MOSI", + "SPI_MISO": "SPI_MISO", + "SPI_CLK": "SPI_SCK", + "SPI_CS": "SPI_CS" + }, + "KL46Z": { + "SPI_MOSI": "PTD6", + "SPI_MISO": "PTD7", + "SPI_CLK": "PTD5", + "SPI_CS": "PTD4" + }, + "K64F": { + "SPI_MOSI": "PTE3", + "SPI_MISO": "PTE1", + "SPI_CLK": "PTE2", + "SPI_CS": "PTE4" + } + } + +Note the following things about the `mbed_lib.json` file: + +- The `mbed_lib.json` file is used to define target specific symbols for the SPI pins connecting the SDCard slot to the target MCU: + - "SPI\_CS". This is the Chip Select line. + - "SPI\_MOSI". This is the Master Out Slave In data line. + - "SPI\_MISO". This is the Master In Slave Out data line. + - "SPI\_CLK". This is the serial Clock line. +- The default configuration defined in the "config" section is for the standard Arduino header pin mappings for the SPI bus. + The "config" section defines a dictionary mapping functional names to target board Arduino header pins: + - "SPI\_CS": "D10". This causes the MBED\_CONF\_APP\_SPI\_CS symbol to be defined in mbed\_config.h as D10, which is used in the filesystem test implementation. + D10 is defined in the target specific PinNames.h file. + - "SPI\_MOSI": "D11". This causes the MBED\_CONF\_APP\_SPI\_MOSI symbol to be defined in mbed\_config.h. + - "SPI\_MISO": "D12". This causes the MBED\_CONF\_APP\_SPI\_MISO symbol to be defined in mbed\_config.h. + - "SPI\_CLK": "D13". This causes the MBED\_CONF\_APP\_SPI\_CLK symbol to be defined in mbed\_config.h. +- The `"target_overrides"` section is used to override the "SPI\_xxx" symbols for specific target boards, which may have an SDCard slot, for example. + This is the case for the K64F, where the "SPI\_xxx" are mapped to the pin names for the on-board SDCard. + + ``` + "K64F": { + "SPI_MOSI": "PTE3", + "SPI_MISO": "PTE1", + "SPI_CLK": "PTE2", + "SPI_CS": "PTE4" + } + ``` +- Thus, in the absence of any target specific definitions in the `"target_overrides"` section, all boards will default to + using the Arduino header configuration. For those platforms with a `"target_overrides"` section then this configuration + will be used in preference. +- Hence in the case that you want to test a platform with an SDCard inserted into a + fitted CI test shield (rather than the on-board SDCard slot) + and there is a `"target_overrides"` section present in the `mbed_lib.json` file, you must then delete the `"target_overrides"` + section before building. This will result in the default configuration being used (suitable for the CI + Test Shield). +- Note when inserting the v1.0.0 CI Test Shield into the Arduino header of the target platform, the shield pins D0 and + D1 should be bent to be parallel to the shield PCB so they are not inserted into the Arduino header. This is because + some boards use the same UART on DAPLINK and D0/D1, which means the serial debug channel breaks and hence the mbed greentea + test suite will not work correctly. This is mainly on older ST boards and should not be a problem on + `K64F`, `NUCLEO_F429ZI` and `UBLOX_EVK_ODIN_W2`. Note also that the v2.0.0 CI Test Shield doesn't suffer from this + problem and the pins don't need to be bent. +- When inserting the SDCard into the card slot on the CI test shield, make sure the card is fully inserted. + On insertion, there should be a small clicking sound when the card registers, and the back edge of the card + should protrude no more than ~1mm over the edge of the CI test shield PCB. If the SDCard fails to register, + try gently pushing the metal flexible strip in the shape of a spade at the top edge of the SDCard metal slot + casing with a pair of tweezers, bending it a little to lower it into the slot casing. This helps with the + insertion mechanism. + +### Wiring instructions for target NUCLEO_F429ZI with CI Test Shield +![alt text](docs/pics/NUCLEO_F429ZI_wiring_with_ci_test_shield.png "unseen title text") + +**Figure 3. The figure shows how to connect the NUCLEO_F429ZI platform with the CI shield.** + +The above figure shows how to connect the NUCLEO_F429ZI with the v1.0.0 CI test shield. Note: + +- To get the SD Card to work with this platform the CI test shield cannot be connected directly to this board, instead follow the instructions above. +- Any SD-card adapter will work as long as you connect all the relevant pins (MOSI, MISO, SCLK, CS, 3.3V and GND) as illustrated in figure 3. +- The SDCard is fully inserted into the slot and overhangs the PCB by ~1mm. + +# SDBlockDevice Example Application + +The following sample code illustrates how to use the sd-driver Block Device API: + +``` cpp +#include "mbed.h" +#include "SDBlockDevice.h" + +// Instantiate the SDBlockDevice by specifying the SPI pins connected to the SDCard +// socket. The PINS are: +// MOSI (Master Out Slave In) +// MISO (Master In Slave Out) +// SCLK (Serial Clock) +// CS (Chip Select) +SDBlockDevice sd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS); +uint8_t block[512] = "Hello World!\n"; + +int main() +{ + // call the SDBlockDevice instance initialisation method. + if ( 0 != sd.init()) { + printf("Init failed \n"); + return -1; + } + printf("sd size: %llu\n", sd.size()); + printf("sd read size: %llu\n", sd.get_read_size()); + printf("sd program size: %llu\n", sd.get_program_size()); + printf("sd erase size: %llu\n", sd.get_erase_size()); + + // set the frequency + if ( 0 != sd.frequency(5000000)) { + printf("Error setting frequency \n"); + } + + if ( 0 != sd.erase(0, sd.get_erase_size())) { + printf("Error Erasing block \n"); + } + + // Write some the data block to the device + if ( 0 == sd.program(block, 0, 512)) { + // read the data block from the device + if ( 0 == sd.read(block, 0, 512)) { + // print the contents of the block + printf("%s", block); + } + } + + // call the SDBlockDevice instance de-initialisation method. + sd.deinit(); +} +``` + +# SDCard POSIX File API mbed Greentea Test Cases + +This section describes how to build and run the POSIX file API test cases. +The following steps are covered: + +- [Create the FAT/SDCard Application Project](#create-fat-sdcard-application-project). + This section describes how to git clone the mbed OS and sd-driver repositories containing the + code and test cases of interest. +- [Build the mbed OS Test Cases](#build-the-mbedos-test-cases). This section + describes how to build the mbed OS test cases. +- [Insert a microSD Card Into the K64F for Greentea Testing](#greentea-insert-sdcard-into-k64f).This section + describes how to format (if required) a microSD card prior to running the tests. +- [Run the POSIX File Test Case](#run-the-posix-file-test-cases).This section + describes how to run the POSIX file test cases. + + +### <a name="create-fat-sdcard-application-project"></a> Create the FAT/SDCard Application Project + +This section describes how to create an application project combining the mbed-os and +sd-driver repositories into a single project. +In summary the following steps will be covered in this section: + +- A top level application project directory is created. The directory name is ex_app1. +- In the ex_app1 directory, the mbed-os repository is cloned. +- In the ex_app1 directory at the same level as the mbed-os directory, the sd-driver repository is cloned. +- The `mbed_lib.json` file is copied from the `sd-driver/config/mbed_lib.json` to the ex_app1 directory. + +First create the top level application directory ex_app1 and move into it: + + shell:/d/demo_area$ mkdir ex_app1 + shell:/d/demo_area$ pushd ex_app1 + +Next, get a clone of public mbed OS repository in the following way: + + shell:/d/demo_area/ex_app1$ git clone git@github.com:/armmbed/mbed-os + <trace removed> + shell:/d/demo_area/ex_app1$ + +Next, get a clone of the sd-driver repository: + + shell:/d/demo_area/ex_app1$ git clone git@github.com:/armmbed/sd-driver + <trace removed> + shell:/d/demo_area/ex_app1$ + +Note: The `mbed_lib.json` file specifies the SPI bus pin configuration for different targets, +and is discussed in the [Testing with an SDCard on Target XYZ](#testing-with-an-sdcard-on-target-xyx) section. + +### <a name="build-the-mbedos-test-cases"></a> Build the mbed OS Test Cases + +Build the test cases for the K64F target using the following command: + + shell:/d/demo_area/ex_app1$ mbed -v test --compile -t GCC_ARM -m K64F + <trace removed> + shell:/d/demo_area/ex_app1$ + +The build trace is quite extensive but on a successful build you should see the following output at the end of the log: + + Build successes: + * K64F::GCC_ARM::MBED-BUILD + * K64F::GCC_ARM::MBED-OS-FEATURES-FEATURE_LWIP-TESTS-MBEDMICRO-NET-CONNECTIVITY + <trace removed> + * K64F::GCC_ARM::MBED-OS-FEATURES-TESTS-FILESYSTEM-FAT_FILE_SYSTEM + * K64F::GCC_ARM::MBED-OS-FEATURES-TESTS-FILESYSTEM-HEAP_BLOCK_DEVICE + * K64F::GCC_ARM::MBED-OS-FEATURES-TESTS-FILESYSTEM-UTIL_BLOCK_DEVICE + <trace removed> + * K64F::GCC_ARM::SD-DRIVER-TESTS-BLOCK_DEVICE-BASIC + * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-BASIC + * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-DIRS + * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-FILES + * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-FOPEN + * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-PARALLEL + * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-SEEK + + Build skips: + * K64F::GCC_ARM::MBED-OS-FEATURES-FEATURE_LWIP-TESTS-MBEDMICRO-NET-TCP_PACKET_PRESSURE + <trace removed> + + +Notice the following tests in the sd-driver tree are listed above: + +- `SD-DRIVER-TESTS-BLOCK_DEVICE-BASIC` +- `SD-DRIVER-TESTS-FILESYSTEM-BASIC` +- `SD-DRIVER-TESTS-FILESYSTEM-DIRS` +- `SD-DRIVER-TESTS-FILESYSTEM-FILES` +- `SD-DRIVER-TESTS-FILESYSTEM-FOPEN` +- `SD-DRIVER-TESTS-FILESYSTEM-PARALLEL` +- `SD-DRIVER-TESTS-FILESYSTEM-SEEK` + +The FAT32/SDCard test cases are at following locations in the source code tree: + + /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/basic/basic.cpp + /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/fopen/fopen.cpp + /d/demo_area/ex_app1/sd-driver/TESTS/block_device/basic/basic.cpp + /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/dirs/main.cpp + /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/files/main.cpp + /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/parallel/main.cpp + /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/seek/main.cpp + +#### <a name="settting-repos-to-compatible-versions"></a> Setting mbed-os/sd-driver Repositories To Compatible Versions + +The sd-driver master HEAD and the mbed-os master HEAD should be compatible +with one another and therefore no specific tagged versions need to be checked out. +However, in the case that you experience problems building, checkout out the compatible +tagged version of each repository, as shown below: + + shell:/d/demo_area/ex_app1$ pushd mbed-os + shell:/d/demo_area/ex_app1$ git checkout tags/mbed-os-5.4.0 + shell:/d/demo_area/ex_app1$ popd + shell:/d/demo_area/ex_app1$ pushd sd-driver + shell:/d/demo_area/ex_app1$ git checkout tags/sd-driver-0.0.2-mbed-os-5.4.0 + shell:/d/demo_area/ex_app1$ popd + +In the above: + +- `mbed-os-5.4.0` should be replaced with the latest mbed-os release tag. +- For an mbed-os release tag `mbed-os-x.y.z`, use the equivalent sd-driver tag `sd-driver-a.b.c-mbed-os-x.y.z` + where `a.b.c` is the latest version code for the `mbed-os-x.y.z` tag. + +### <a name="greentea-insert-sdcard-into-k64f"></a> Insert SDCard into K64F for Greentea Testing + +See the previous section for [Insert SDCard into K64F](#insert-sdcard-into-k64f) for details. + + +### <a name="run-the-posix-file-test-cases"></a> Run the POSIX File Test Case + +To setup for running the test cases, connect the K64F development board to your +PC using a suitable USB cable. + +All tests can be run using the following command: + + shell:/d/demo_area/ex_app1$ mbedgt -VS + <trace removed> + +However, it's possible to run a particular test case using the following form of the mbedgt command: + + shell:/d/demo_area/ex_app1$ mbedgt -VS --test-by-names=<test-name> + +The names of the tests can be listed using: + + shell:/d/demo_area/ex_app1$ mbedgt -VS --list + +For example, to run the basic test use: + + shell:/d/demo_area/ex_app1$ mbedgt -VS --test-by-names=sd-driver-tests-filesystem-basic + +To run the fopen test use: + + shell:/d/demo_area/ex_app1$ mbedgt -VS --test-by-names=sd-driver-tests-filesystem-fopen + +On a successful run, results similar to the following will be shown: + + mbedgt: test suite report: + +--------------+---------------+-------------------------------------------+--------+--------------------+-------------+ + | target | platform_name | test suite | result | elapsed_time (sec) | copy_method | + +--------------+---------------+-------------------------------------------+--------+--------------------+-------------+ + | K64F-GCC_ARM | K64F | sd-driver-features-tests-filesystem-fopen | OK | 151.46 | shell | + +--------------+---------------+-------------------------------------------+--------+--------------------+-------------+ + mbedgt: test suite results: 1 OK + mbedgt: test case report: + +--------------+---------------+------------------------------------+----------------------------------------------------------------------------------------+--------+--------+--------+--------------------+ + | target | platform_name | test suite | test case | passed | failed | result | elapsed_time (sec) | + +--------------+---------------+------------------------------------+----------------------------------------------------------------------------------------+--------+--------+--------+--------------------+ + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_01: fopen()/fwrite()/fclose() directories/file in multi-dir filepath. | 1 | 0 | OK | 7.57 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_02: fopen(r) pre-existing file try to write it. | 1 | 0 | OK | 0.2 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_03: fopen(w+) pre-existing file try to write it. | 1 | 0 | OK | 0.41 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_04: fopen() with a filename exceeding the maximum length. | 1 | 0 | OK | 0.11 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_06: fopen() with bad filenames (minimal). | 1 | 0 | OK | 0.1 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_07: fopen()/errno handling. | 1 | 0 | OK | 0.07 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_08: ferror()/clearerr()/errno handling. | 1 | 0 | OK | 0.1 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_09: ftell() handling. | 1 | 0 | OK | 0.17 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_10: remove() test. | 1 | 0 | OK | 1.28 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_11: rename(). | 1 | 0 | OK | 2.3 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_12: opendir(), readdir(), closedir() test. | 1 | 0 | OK | 3.57 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_13: mkdir() test. | 1 | 0 | OK | 1.21 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_14: stat() test. | 1 | 0 | OK | 1.47 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_15: format() test. | 1 | 0 | OK | 26.12 | + | K64F-GCC_ARM | K64F | sd-driver-tests-filesystem-fopen | FSFAT_FOPEN_TEST_16: write/check n x 25kB data files. | 1 | 0 | OK | 87.11 | + +--------------+---------------+------------------------------------+----------------------------------------------------------------------------------------+--------+--------+--------+--------------------+ + mbedgt: test case results: 15 OK + mbedgt: completed in 152.35 sec + + +# <a name="summary-posix-api-documentation"></a> Summary of POSIX File API Documentation + +### POSIX File API + +mbed OS supports a subset of the POSIX File API, as outlined below: + +- [clearerr()](https://linux.die.net/man/3/clearerr). + - STATUS: Basic testing implemented. Working. +- [fclose()](https://linux.die.net/man/3/fclose). + - STATUS: Basic testing implemented. Working. +- [ferror()](https://linux.die.net/man/3/clearerr). + - STATUS: Basic testing implemented. + - STATUS: GCC_ARM: Working. + - STATUS: ARMCC: ARMCC has problem with ferror(filep) where filep is NULL. Appears to work for non-NULL pointer. +- [fgetc()](https://linux.die.net/man/3/fgets). + - STATUS: Basic testing implemented. Working. +- [fgets()](https://linux.die.net/man/3/fgets). + - STATUS: Basic testing implemented. Working. +- [fputc()](https://linux.die.net/man/3/fputs). + - STATUS: Unknown. +- [fputs()](https://linux.die.net/man/3/fputs). + - STATUS: Basic testing implemented. Working. +- [fprintf()](https://linux.die.net/man/3/fprintf). + - STATUS: Basic testing implemented. Working. +- [fopen()](https://linux.die.net/man/3/fopen). + - STATUS: Basic testing implemented. Working. +- [freopen()](https://linux.die.net/man/3/fopen). + - STATUS: This is not tested. +- [fread()](https://linux.die.net/man/3/fread). + - STATUS: Basic testing implemented. Working. + - STATUS: n x 25kB stress test working. +- [ftell()](https://linux.die.net/man/3/ftell). + - STATUS: Basic testing implemented. Working. +- [fwrite()](https://linux.die.net/man/3/fwrite). + - STATUS: Basic testing implemented. Working. + - STATUS: n x 25kB stress test working. +- [fseek()](https://linux.die.net/man/3/fseek) + - STATUS: Basic testing implemented. Working. +- [getc()](https://linux.die.net/man/3/fgets). + - STATUS: Basic testing implemented. Working. +- [gets()](https://linux.die.net/man/3/fgets). + - STATUS: Unknown. +- [putc()](https://linux.die.net/man/3/fputs). + - STATUS: Unknown. +- [puts()](https://linux.die.net/man/3/fputs). + - STATUS: Unknown. +- [remove()](https://linux.die.net/man/3/remove) + - STATUS: Basic testing implemented. Working. +- [rewind()](https://linux.die.net/man/3/rewind). + - STATUS: Basic testing implemented. Working. +- [stat()](https://linux.die.net/man/2/stat) + - STATUS: Implemented. Working. + - STATUS: Not supported by ARMCC/IAR libc. +- [tmpfile()](https://linux.die.net/man/3/tmpfile). + - STATUS: Not implemented. +- [tmpnam()](https://linux.die.net/man/3/tmpnam). + - STATUS: Not implemented. + +Supported directory related operations are as follows: + +- [closedir()](https://linux.die.net/man/3/closedir). + - STATUS: Implemented. Working. +- [mkdir()](https://linux.die.net/man/3/mkdir). + - STATUS: Basic testing implemented. Working. +- [opendir()](https://linux.die.net/man/3/opendir). + - STATUS: Implemented. Working. +- [readdir()](https://linux.die.net/man/3/readdir). + - STATUS: Implemented. Working. +- [remove()](https://linux.die.net/man/3/remove). + - STATUS: Basic testing implemented. Working. +- [rename()](https://linux.die.net/man/3/rename). + - STATUS: Implemented. Not tested. +- [rewinddir()](https://linux.die.net/man/3/rewinddir). + - STATUS: Implemented. Found not to work. Test case not present in repo. +- [seekdir()](https://linux.die.net/man/3/seekdir). + - STATUS: Implemented. Found not to work. Test case not present in repo. +- [telldir()](https://linux.die.net/man/3/telldir). + - STATUS: Implemented. Found not to work. Test case not present in repo. + +### errno + +Basic errno reporting is supported, tested and known to be working. + + +# Related Projects Resources + +The following are related mbed storage projects and useful resources: + +- The [mbed-os](https://github.com/ARMmbed/mbed-os) main repository. +- The [mbed-os-example-fat-filesystem](https://github.com/ARMmbed/mbed-os-example-fat-filesystem) repository. + This is an example project for the mbed OS FAT filesystem. +- The [spiflash-driver](https://github.com/armmbed/spiflash-driver) repository. +- The [i2ceeprom-driver](https://github.com/ARMmbed/i2ceeprom-driver.git) repository. +- The [ci-test-shield](https://github.com/ARMmbed/ci-test-shield) repository. This is the project describing + the mbed-os Continuous Integration test shield, together with standard tests. +- The [mbed-HDK](https://github.com/ARMmbed/mbed-HDK) repository containing Hardware Development Kit resources + including the schematics for the CI test shield. +- [POSIX File Interface ISO/IEC 9899:TC2 Documentation](http://www.eng.utah.edu/~cs5785/slides-f10/n1124.pdf). +- [FATFS: Generic FAT File System Module used in mbed OS](http://elm-chan.org/fsw/ff/00index_e.html)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDBlockDevice.cpp Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,1012 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Introduction + * ------------ + * SD and MMC cards support a number of interfaces, but common to them all + * is one based on SPI. Since we already have the mbed SPI Interface, it will + * be used for SD cards. + * + * The main reference I'm using is Chapter 7, "SPI Mode" of: + * http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf + * + * SPI Startup + * ----------- + * The SD card powers up in SD mode. The start-up procedure is complicated + * by the requirement to support older SDCards in a backwards compatible + * way with the new higher capacity variants SDHC and SDHC. + * + * The following figures from the specification with associated text describe + * the SPI mode initialisation process: + * - Figure 7-1: SD Memory Card State Diagram (SPI mode) + * - Figure 7-2: SPI Mode Initialization Flow + * + * Firstly, a low initial clock should be selected (in the range of 100- + * 400kHZ). After initialisation has been completed, the switch to a + * higher clock speed can be made (e.g. 1MHz). Newer cards will support + * higher speeds than the default _transfer_sck defined here. + * + * Next, note the following from the SDCard specification (note to + * Figure 7-1): + * + * In any of the cases CMD1 is not recommended because it may be difficult for the host + * to distinguish between MultiMediaCard and SD Memory Card + * + * Hence CMD1 is not used for the initialisation sequence. + * + * The SPI interface mode is selected by asserting CS low and sending the + * reset command (CMD0). The card will respond with a (R1) response. + * In practice many cards initially respond with 0xff or invalid data + * which is ignored. Data is read until a valid response is received + * or the number of re-reads has exceeded a maximim count. If a valid + * response is not received then the CMD0 can be retried. This + * has been found to successfully initialise cards where the SPI master + * (on MCU) has been reset but the SDCard has not, so the first + * CMD0 may be lost. + * + * CMD8 is optionally sent to determine the voltage range supported, and + * indirectly determine whether it is a version 1.x SD/non-SD card or + * version 2.x. I'll just ignore this for now. + * + * ACMD41 is repeatedly issued to initialise the card, until "in idle" + * (bit 0) of the R1 response goes to '0', indicating it is initialised. + * + * You should also indicate whether the host supports High Capicity cards, + * and check whether the card is high capacity - i'll also ignore this + * + * SPI Protocol + * ------------ + * The SD SPI protocol is based on transactions made up of 8-bit words, with + * the host starting every bus transaction by asserting the CS signal low. The + * card always responds to commands, data blocks and errors. + * + * The protocol supports a CRC, but by default it is off (except for the + * first reset CMD0, where the CRC can just be pre-calculated, and CMD8) + * I'll leave the CRC off I think! + * + * Standard capacity cards have variable data block sizes, whereas High + * Capacity cards fix the size of data block to 512 bytes. I'll therefore + * just always use the Standard Capacity cards with a block size of 512 bytes. + * This is set with CMD16. + * + * You can read and write single blocks (CMD17, CMD25) or multiple blocks + * (CMD18, CMD25). For simplicity, I'll just use single block accesses. When + * the card gets a read command, it responds with a response token, and then + * a data token or an error. + * + * SPI Command Format + * ------------------ + * Commands are 6-bytes long, containing the command, 32-bit argument, and CRC. + * + * +---------------+------------+------------+-----------+----------+--------------+ + * | 01 | cmd[5:0] | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | crc[6:0] | 1 | + * +---------------+------------+------------+-----------+----------+--------------+ + * + * As I'm not using CRC, I can fix that byte to what is needed for CMD0 (0x95) + * + * All Application Specific commands shall be preceded with APP_CMD (CMD55). + * + * SPI Response Format + * ------------------- + * The main response format (R1) is a status byte (normally zero). Key flags: + * idle - 1 if the card is in an idle state/initialising + * cmd - 1 if an illegal command code was detected + * + * +-------------------------------------------------+ + * R1 | 0 | arg | addr | seq | crc | cmd | erase | idle | + * +-------------------------------------------------+ + * + * R1b is the same, except it is followed by a busy signal (zeros) until + * the first non-zero byte when it is ready again. + * + * Data Response Token + * ------------------- + * Every data block written to the card is acknowledged by a byte + * response token + * + * +----------------------+ + * | xxx | 0 | status | 1 | + * +----------------------+ + * 010 - OK! + * 101 - CRC Error + * 110 - Write Error + * + * Single Block Read and Write + * --------------------------- + * + * Block transfers have a byte header, followed by the data, followed + * by a 16-bit CRC. In our case, the data will always be 512 bytes. + * + * +------+---------+---------+- - - -+---------+-----------+----------+ + * | 0xFE | data[0] | data[1] | | data[n] | crc[15:8] | crc[7:0] | + * +------+---------+---------+- - - -+---------+-----------+----------+ + */ + +/* If the target has no SPI support then SDCard is not supported */ +#ifdef DEVICE_SPI + +#include "SDBlockDevice.h" +#include "mbed_debug.h" +#include <errno.h> + +/* Required version: 5.6.1 and above */ +#if defined(MBED_MAJOR_VERSION) && MBED_MAJOR_VERSION >= 5 +#if (MBED_VERSION < MBED_ENCODE_VERSION(5,6,1)) +#error "Incompatible mbed-os version detected! Required 5.6.1 and above" +#endif +#else +#warning "mbed-os version 5.6.1 or above required" +#endif + +#ifndef MBED_CONF_SD_CMD_TIMEOUT +#define MBED_CONF_SD_CMD_TIMEOUT 5000 /*!< Timeout in ms for response */ +#endif + +#ifndef MBED_CONF_SD_CMD0_IDLE_STATE_RETRIES +#define MBED_CONF_SD_CMD0_IDLE_STATE_RETRIES 5 /*!< Number of retries for sending CMDO */ +#endif + +#define SD_COMMAND_TIMEOUT MBED_CONF_SD_CMD_TIMEOUT +#define SD_CMD0_GO_IDLE_STATE_RETRIES MBED_CONF_SD_CMD0_IDLE_STATE_RETRIES +#define SD_DBG 0 /*!< 1 - Enable debugging */ +#define SD_CMD_TRACE 0 /*!< 1 - Enable SD command tracing */ + +#define SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK -5001 /*!< operation would block */ +#define SD_BLOCK_DEVICE_ERROR_UNSUPPORTED -5002 /*!< unsupported operation */ +#define SD_BLOCK_DEVICE_ERROR_PARAMETER -5003 /*!< invalid parameter */ +#define SD_BLOCK_DEVICE_ERROR_NO_INIT -5004 /*!< uninitialized */ +#define SD_BLOCK_DEVICE_ERROR_NO_DEVICE -5005 /*!< device is missing or not connected */ +#define SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED -5006 /*!< write protected */ +#define SD_BLOCK_DEVICE_ERROR_UNUSABLE -5007 /*!< unusable card */ +#define SD_BLOCK_DEVICE_ERROR_NO_RESPONSE -5008 /*!< No response from device */ +#define SD_BLOCK_DEVICE_ERROR_CRC -5009 /*!< CRC error */ +#define SD_BLOCK_DEVICE_ERROR_ERASE -5010 /*!< Erase error: reset/sequence */ +#define SD_BLOCK_DEVICE_ERROR_WRITE -5011 /*!< SPI Write error: !SPI_DATA_ACCEPTED */ + +#define BLOCK_SIZE_HC 512 /*!< Block size supported for SD card is 512 bytes */ +#define WRITE_BL_PARTIAL 0 /*!< Partial block write - Not supported */ +#define CRC_SUPPORT 0 /*!< CRC - Not supported */ +#define SPI_CMD(x) (0x40 | (x & 0x3f)) + +/* R1 Response Format */ +#define R1_NO_RESPONSE (0xFF) +#define R1_RESPONSE_RECV (0x80) +#define R1_IDLE_STATE (1 << 0) +#define R1_ERASE_RESET (1 << 1) +#define R1_ILLEGAL_COMMAND (1 << 2) +#define R1_COM_CRC_ERROR (1 << 3) +#define R1_ERASE_SEQUENCE_ERROR (1 << 4) +#define R1_ADDRESS_ERROR (1 << 5) +#define R1_PARAMETER_ERROR (1 << 6) + +// Types +#define SDCARD_NONE 0 /**< No card is present */ +#define SDCARD_V1 1 /**< v1.x Standard Capacity */ +#define SDCARD_V2 2 /**< v2.x Standard capacity SD card */ +#define SDCARD_V2HC 3 /**< v2.x High capacity SD card */ +#define CARD_UNKNOWN 4 /**< Unknown or unsupported card */ + +/* SIZE in Bytes */ +#define PACKET_SIZE 6 /*!< SD Packet size CMD+ARG+CRC */ +#define R1_RESPONSE_SIZE 1 /*!< Size of R1 response */ +#define R2_RESPONSE_SIZE 2 /*!< Size of R2 response */ +#define R3_R7_RESPONSE_SIZE 5 /*!< Size of R3/R7 response */ + +/* R1b Response */ +#define DEVICE_BUSY (0x00) + +/* R2 Response Format */ +#define R2_CARD_LOCKED (1 << 0) +#define R2_CMD_FAILED (1 << 1) +#define R2_ERROR (1 << 2) +#define R2_CC_ERROR (1 << 3) +#define R2_CC_FAILED (1 << 4) +#define R2_WP_VIOLATION (1 << 5) +#define R2_ERASE_PARAM (1 << 6) +#define R2_OUT_OF_RANGE (1 << 7) + +/* R3 Response : OCR Register */ +#define OCR_HCS_CCS (0x1 << 30) +#define OCR_LOW_VOLTAGE (0x01 << 24) +#define OCR_3_3V (0x1 << 20) + +/* R7 response pattern for CMD8 */ +#define CMD8_PATTERN (0xAA) + +/* CRC Enable */ +#define CRC_ENABLE (0) /*!< CRC 1 - Enable 0 - Disable */ + +/* Control Tokens */ +#define SPI_DATA_RESPONSE_MASK (0x1F) +#define SPI_DATA_ACCEPTED (0x05) +#define SPI_DATA_CRC_ERROR (0x0B) +#define SPI_DATA_WRITE_ERROR (0x0D) +#define SPI_START_BLOCK (0xFE) /*!< For Single Block Read/Write and Multiple Block Read */ +#define SPI_START_BLK_MUL_WRITE (0xFC) /*!< Start Multi-block write */ +#define SPI_STOP_TRAN (0xFD) /*!< Stop Multi-block write */ + +#define SPI_DATA_READ_ERROR_MASK (0xF) /*!< Data Error Token: 4 LSB bits */ +#define SPI_READ_ERROR (0x1 << 0) /*!< Error */ +#define SPI_READ_ERROR_CC (0x1 << 1) /*!< CC Error*/ +#define SPI_READ_ERROR_ECC_C (0x1 << 2) /*!< Card ECC failed */ +#define SPI_READ_ERROR_OFR (0x1 << 3) /*!< Out of Range */ + +SDBlockDevice::SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs, uint64_t hz) + : _sectors(0), _spi(mosi, miso, sclk), _cs(cs), _is_initialized(0) +{ + _cs = 1; + _card_type = SDCARD_NONE; + + // Set default to 100kHz for initialisation and 1MHz for data transfer + _init_sck = 100000; + _transfer_sck = hz; + + // Only HC block size is supported. + _block_size = BLOCK_SIZE_HC; + _erase_size = BLOCK_SIZE_HC; +} + +SDBlockDevice::~SDBlockDevice() +{ + if (_is_initialized) { + deinit(); + } +} + +int SDBlockDevice::_initialise_card() +{ + // Detail debugging is for commands + _dbg = SD_DBG ? SD_CMD_TRACE : 0; + int32_t status = BD_ERROR_OK; + uint32_t response, arg; + + // Initialize the SPI interface: Card by default is in SD mode + _spi_init(); + + // The card is transitioned from SDCard mode to SPI mode by sending the CMD0 + CS Asserted("0") + if (_go_idle_state() != R1_IDLE_STATE) { + debug_if(SD_DBG, "No disk, or could not put SD card in to SPI idle state\n"); + return SD_BLOCK_DEVICE_ERROR_NO_DEVICE; + } + + // Send CMD8, if the card rejects the command then it's probably using the + // legacy protocol, or is a MMC, or just flat-out broken + status = _cmd8(); + if (BD_ERROR_OK != status && SD_BLOCK_DEVICE_ERROR_UNSUPPORTED != status) { + return status; + } + + // Read OCR - CMD58 Response contains OCR register + if (BD_ERROR_OK != (status = _cmd(CMD58_READ_OCR, 0x0, 0x0, &response))) { + return status; + } + + // Check if card supports voltage range: 3.3V + if (!(response & OCR_3_3V)) { + _card_type = CARD_UNKNOWN; + status = SD_BLOCK_DEVICE_ERROR_UNUSABLE; + return status; + } + + // HCS is set 1 for HC/XC capacity cards for ACMD41, if supported + arg = 0x0; + if (SDCARD_V2 == _card_type) { + arg |= OCR_HCS_CCS; + } + + /* Idle state bit in the R1 response of ACMD41 is used by the card to inform the host + * if initialization of ACMD41 is completed. "1" indicates that the card is still initializing. + * "0" indicates completion of initialization. The host repeatedly issues ACMD41 until + * this bit is set to "0". + */ + _spi_timer.start(); + do { + status = _cmd(ACMD41_SD_SEND_OP_COND, arg, 1, &response); + } while ((response & R1_IDLE_STATE) && (_spi_timer.read_ms() < SD_COMMAND_TIMEOUT)); + _spi_timer.stop(); + + // Initialization complete: ACMD41 successful + if ((BD_ERROR_OK != status) || (0x00 != response)) { + _card_type = CARD_UNKNOWN; + debug_if(SD_DBG, "Timeout waiting for card\n"); + return status; + } + + if (SDCARD_V2 == _card_type) { + // Get the card capacity CCS: CMD58 + if (BD_ERROR_OK == (status = _cmd(CMD58_READ_OCR, 0x0, 0x0, &response))) { + // High Capacity card + if (response & OCR_HCS_CCS) { + _card_type = SDCARD_V2HC; + debug_if(SD_DBG, "Card Initialized: High Capacity Card \n"); + } else { + debug_if(SD_DBG, "Card Initialized: Standard Capacity Card: Version 2.x \n"); + } + } + } else { + _card_type = SDCARD_V1; + debug_if(SD_DBG, "Card Initialized: Version 1.x Card\n"); + } + + // Disable CRC + status = _cmd(CMD59_CRC_ON_OFF, 0); + + return status; +} + + +int SDBlockDevice::init() +{ + lock(); + int err = _initialise_card(); + _is_initialized = (err == BD_ERROR_OK); + if (!_is_initialized) { + debug_if(SD_DBG, "Fail to initialize card\n"); + unlock(); + return err; + } + debug_if(SD_DBG, "init card = %d\n", _is_initialized); + _sectors = _sd_sectors(); + // CMD9 failed + if (0 == _sectors) { + unlock(); + return BD_ERROR_DEVICE_ERROR; + } + + // Set block length to 512 (CMD16) + if (_cmd(CMD16_SET_BLOCKLEN, _block_size) != 0) { + debug_if(SD_DBG, "Set %d-byte block timed out\n", _block_size); + unlock(); + return BD_ERROR_DEVICE_ERROR; + } + + // Set SCK for data transfer + err = _freq(); + if (err) { + unlock(); + return err; + } + unlock(); + return BD_ERROR_OK; +} + +int SDBlockDevice::deinit() +{ + lock(); + _is_initialized = false; + _sectors = 0; + unlock(); + return 0; +} + + +int SDBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size) +{ + if (!is_valid_program(addr, size)) { + return SD_BLOCK_DEVICE_ERROR_PARAMETER; + } + + lock(); + if (!_is_initialized) { + unlock(); + return SD_BLOCK_DEVICE_ERROR_NO_INIT; + } + + const uint8_t *buffer = static_cast<const uint8_t*>(b); + int status = BD_ERROR_OK; + uint8_t response; + + // Get block count + bd_addr_t blockCnt = size / _block_size; + + // SDSC Card (CCS=0) uses byte unit address + // SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit) + if(SDCARD_V2HC == _card_type) { + addr = addr / _block_size; + } + + // Send command to perform write operation + if (blockCnt == 1) { + // Single block write command + if (BD_ERROR_OK != (status = _cmd(CMD24_WRITE_BLOCK, addr))) { + unlock(); + return status; + } + + // Write data + response = _write(buffer, SPI_START_BLOCK, _block_size); + + // Only CRC and general write error are communicated via response token + if ((response == SPI_DATA_CRC_ERROR) || (response == SPI_DATA_WRITE_ERROR)) { + debug_if(SD_DBG, "Single Block Write failed: 0x%x \n", response); + status = SD_BLOCK_DEVICE_ERROR_WRITE; + } + } else { + // Pre-erase setting prior to multiple block write operation + _cmd(ACMD23_SET_WR_BLK_ERASE_COUNT, blockCnt, 1); + + // Multiple block write command + if (BD_ERROR_OK != (status = _cmd(CMD25_WRITE_MULTIPLE_BLOCK, addr))) { + unlock(); + return status; + } + + // Write the data: one block at a time + do { + response = _write(buffer, SPI_START_BLK_MUL_WRITE, _block_size); + if (response != SPI_DATA_ACCEPTED) { + debug_if(SD_DBG, "Multiple Block Write failed: 0x%x \n", response); + break; + } + buffer += _block_size; + }while (--blockCnt); // Receive all blocks of data + + /* In a Multiple Block write operation, the stop transmission will be done by + * sending 'Stop Tran' token instead of 'Start Block' token at the beginning + * of the next block + */ + _spi.write(SPI_STOP_TRAN); + } + + _deselect(); + unlock(); + return status; +} + +int SDBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size) +{ + if (!is_valid_read(addr, size)) { + return SD_BLOCK_DEVICE_ERROR_PARAMETER; + } + + lock(); + if (!_is_initialized) { + unlock(); + return SD_BLOCK_DEVICE_ERROR_PARAMETER; + } + + uint8_t *buffer = static_cast<uint8_t *>(b); + int status = BD_ERROR_OK; + bd_addr_t blockCnt = size / _block_size; + + // SDSC Card (CCS=0) uses byte unit address + // SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit) + if (SDCARD_V2HC == _card_type) { + addr = addr / _block_size; + } + + // Write command ro receive data + if (blockCnt > 1) { + status = _cmd(CMD18_READ_MULTIPLE_BLOCK, addr); + } else { + status = _cmd(CMD17_READ_SINGLE_BLOCK, addr); + } + if (BD_ERROR_OK != status) { + unlock(); + return status; + } + + // receive the data : one block at a time + while (blockCnt) { + if (0 != _read(buffer, _block_size)) { + status = SD_BLOCK_DEVICE_ERROR_NO_RESPONSE; + break; + } + buffer += _block_size; + --blockCnt; + } + _deselect(); + + // Send CMD12(0x00000000) to stop the transmission for multi-block transfer + if (size > _block_size) { + status = _cmd(CMD12_STOP_TRANSMISSION, 0x0); + } + unlock(); + return status; +} + +bool SDBlockDevice::_is_valid_trim(bd_addr_t addr, bd_size_t size) +{ + return ( + addr % _erase_size == 0 && + size % _erase_size == 0 && + addr + size <= this->size()); +} + +int SDBlockDevice::trim(bd_addr_t addr, bd_size_t size) +{ + if (!_is_valid_trim(addr, size)) { + return SD_BLOCK_DEVICE_ERROR_PARAMETER; + } + + lock(); + if (!_is_initialized) { + unlock(); + return SD_BLOCK_DEVICE_ERROR_NO_INIT; + } + int status = BD_ERROR_OK; + + size -= _block_size; + // SDSC Card (CCS=0) uses byte unit address + // SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit) + if (SDCARD_V2HC == _card_type) { + size = size / _block_size; + addr = addr / _block_size; + } + + // Start lba sent in start command + if (BD_ERROR_OK != (status = _cmd(CMD32_ERASE_WR_BLK_START_ADDR, addr))) { + unlock(); + return status; + } + + // End lba = addr+size sent in end addr command + if (BD_ERROR_OK != (status = _cmd(CMD33_ERASE_WR_BLK_END_ADDR, addr+size))) { + unlock(); + return status; + } + status = _cmd(CMD38_ERASE, 0x0); + unlock(); + return status; +} + +bd_size_t SDBlockDevice::get_read_size() const +{ + return _block_size; +} + +bd_size_t SDBlockDevice::get_program_size() const +{ + return _block_size; +} + +bd_size_t SDBlockDevice::size() const +{ + return _block_size*_sectors; +} + +void SDBlockDevice::debug(bool dbg) +{ + _dbg = dbg; +} + +int SDBlockDevice::frequency(uint64_t freq) +{ + lock(); + _transfer_sck = freq; + int err = _freq(); + unlock(); + return err; +} + +// PRIVATE FUNCTIONS +int SDBlockDevice::_freq(void) +{ + // Max frequency supported is 25MHZ + if (_transfer_sck <= 25000000) { + _spi.frequency(_transfer_sck); + return 0; + } else { // TODO: Switch function to be implemented for higher frequency + _transfer_sck = 25000000; + _spi.frequency(_transfer_sck); + return -EINVAL; + } +} + +uint8_t SDBlockDevice::_cmd_spi(SDBlockDevice::cmdSupported cmd, uint32_t arg) { + uint8_t response; + char cmdPacket[PACKET_SIZE]; + + // Prepare the command packet + cmdPacket[0] = SPI_CMD(cmd); + cmdPacket[1] = (arg >> 24); + cmdPacket[2] = (arg >> 16); + cmdPacket[3] = (arg >> 8); + cmdPacket[4] = (arg >> 0); + // CMD0 is executed in SD mode, hence should have correct CRC + // CMD8 CRC verification is always enabled + switch(cmd) { + case CMD0_GO_IDLE_STATE: + cmdPacket[5] = 0x95; + break; + case CMD8_SEND_IF_COND: + cmdPacket[5] = 0x87; + break; + default: + cmdPacket[5] = 0xFF; // Make sure bit 0-End bit is high + break; + } + + // send a command + for (int i = 0; i < PACKET_SIZE; i++) { + _spi.write(cmdPacket[i]); + } + + // The received byte immediataly following CMD12 is a stuff byte, + // it should be discarded before receive the response of the CMD12. + if (CMD12_STOP_TRANSMISSION == cmd) { + _spi.write(SPI_FILL_CHAR); + } + + // Loop for response: Response is sent back within command response time (NCR), 0 to 8 bytes for SDC + for (int i = 0; i < 0x10; i++) { + response = _spi.write(SPI_FILL_CHAR); + // Got the response + if (!(response & R1_RESPONSE_RECV)) { + break; + } + } + return response; +} + +int SDBlockDevice::_cmd(SDBlockDevice::cmdSupported cmd, uint32_t arg, bool isAcmd, uint32_t *resp) { + int32_t status = BD_ERROR_OK; + uint32_t response; + + // Select card and wait for card to be ready before sending next command + // Note: next command will fail if card is not ready + _select(); + + // No need to wait for card to be ready when sending the stop command + if (CMD12_STOP_TRANSMISSION != cmd) { + if (false == _wait_ready(SD_COMMAND_TIMEOUT)) { + debug_if(SD_DBG, "Card not ready yet \n"); + } + } + + // Re-try command + for(int i = 0; i < 3; i++) { + // Send CMD55 for APP command first + if (isAcmd) { + response = _cmd_spi(CMD55_APP_CMD, 0x0); + // Wait for card to be ready after CMD55 + if (false == _wait_ready(SD_COMMAND_TIMEOUT)) { + debug_if(SD_DBG, "Card not ready yet \n"); + } + } + + // Send command over SPI interface + response = _cmd_spi(cmd, arg); + if (R1_NO_RESPONSE == response) { + debug_if(SD_DBG, "No response CMD:%d \n", cmd); + continue; + } + break; + } + + // Pass the response to the command call if required + if (NULL != resp) { + *resp = response; + } + + // Process the response R1 : Exit on CRC/Illegal command error/No response + if (R1_NO_RESPONSE == response) { + _deselect(); + debug_if(SD_DBG, "No response CMD:%d response: 0x%x\n",cmd, response); + return SD_BLOCK_DEVICE_ERROR_NO_DEVICE; // No device + } + if (response & R1_COM_CRC_ERROR) { + _deselect(); + debug_if(SD_DBG, "CRC error CMD:%d response 0x%x \n",cmd, response); + return SD_BLOCK_DEVICE_ERROR_CRC; // CRC error + } + if (response & R1_ILLEGAL_COMMAND) { + _deselect(); + debug_if(SD_DBG, "Illegal command CMD:%d response 0x%x\n",cmd, response); + if (CMD8_SEND_IF_COND == cmd) { // Illegal command is for Ver1 or not SD Card + _card_type = CARD_UNKNOWN; + } + return SD_BLOCK_DEVICE_ERROR_UNSUPPORTED; // Command not supported + } + + debug_if(_dbg, "CMD:%d \t arg:0x%x \t Response:0x%x \n", cmd, arg, response); + // Set status for other errors + if ((response & R1_ERASE_RESET) || (response & R1_ERASE_SEQUENCE_ERROR)) { + status = SD_BLOCK_DEVICE_ERROR_ERASE; // Erase error + }else if ((response & R1_ADDRESS_ERROR) || (response & R1_PARAMETER_ERROR)) { + // Misaligned address / invalid address block length + status = SD_BLOCK_DEVICE_ERROR_PARAMETER; + } + + // Get rest of the response part for other commands + switch(cmd) { + case CMD8_SEND_IF_COND: // Response R7 + debug_if(_dbg, "V2-Version Card\n"); + _card_type = SDCARD_V2; + // Note: No break here, need to read rest of the response + case CMD58_READ_OCR: // Response R3 + response = (_spi.write(SPI_FILL_CHAR) << 24); + response |= (_spi.write(SPI_FILL_CHAR) << 16); + response |= (_spi.write(SPI_FILL_CHAR) << 8); + response |= _spi.write(SPI_FILL_CHAR); + debug_if(_dbg, "R3/R7: 0x%x \n", response); + break; + + case CMD12_STOP_TRANSMISSION: // Response R1b + case CMD38_ERASE: + _wait_ready(SD_COMMAND_TIMEOUT); + break; + + case ACMD13_SD_STATUS: // Response R2 + response = _spi.write(SPI_FILL_CHAR); + debug_if(_dbg, "R2: 0x%x \n", response); + break; + + default: // Response R1 + break; + } + + // Pass the updated response to the command + if (NULL != resp) { + *resp = response; + } + + // Do not deselect card if read is in progress. + if (((CMD9_SEND_CSD == cmd) || (ACMD22_SEND_NUM_WR_BLOCKS == cmd) || + (CMD24_WRITE_BLOCK == cmd) || (CMD25_WRITE_MULTIPLE_BLOCK == cmd) || + (CMD17_READ_SINGLE_BLOCK == cmd) || (CMD18_READ_MULTIPLE_BLOCK == cmd)) + && (BD_ERROR_OK == status)) { + return BD_ERROR_OK; + } + // Deselect card + _deselect(); + return status; +} + +int SDBlockDevice::_cmd8() { + uint32_t arg = (CMD8_PATTERN << 0); // [7:0]check pattern + uint32_t response = 0; + int32_t status = BD_ERROR_OK; + + arg |= (0x1 << 8); // 2.7-3.6V // [11:8]supply voltage(VHS) + + status = _cmd(CMD8_SEND_IF_COND, arg, 0x0, &response); + // Verify voltage and pattern for V2 version of card + if ((BD_ERROR_OK == status) && (SDCARD_V2 == _card_type)) { + // If check pattern is not matched, CMD8 communication is not valid + if((response & 0xFFF) != arg) + { + debug_if(SD_DBG, "CMD8 Pattern mismatch 0x%x : 0x%x\n", arg, response); + _card_type = CARD_UNKNOWN; + status = SD_BLOCK_DEVICE_ERROR_UNUSABLE; + } + } + return status; +} + +uint32_t SDBlockDevice::_go_idle_state() { + uint32_t response; + + /* Reseting the MCU SPI master may not reset the on-board SDCard, in which + * case when MCU power-on occurs the SDCard will resume operations as + * though there was no reset. In this scenario the first CMD0 will + * not be interpreted as a command and get lost. For some cards retrying + * the command overcomes this situation. */ + for (int i = 0; i < SD_CMD0_GO_IDLE_STATE_RETRIES; i++) { + _cmd(CMD0_GO_IDLE_STATE, 0x0, 0x0, &response); + if (R1_IDLE_STATE == response) + break; + wait_ms(1); + } + return response; +} + +int SDBlockDevice::_read_bytes(uint8_t *buffer, uint32_t length) { + uint16_t crc; + + // read until start byte (0xFE) + if (false == _wait_token(SPI_START_BLOCK)) { + debug_if(SD_DBG, "Read timeout\n"); + _deselect(); + return SD_BLOCK_DEVICE_ERROR_NO_RESPONSE; + } + + // read data + for (uint32_t i = 0; i < length; i++) { + buffer[i] = _spi.write(SPI_FILL_CHAR); + } + + // Read the CRC16 checksum for the data block + crc = (_spi.write(SPI_FILL_CHAR) << 8); + crc |= _spi.write(SPI_FILL_CHAR); + + _deselect(); + return 0; +} + +int SDBlockDevice::_read(uint8_t *buffer, uint32_t length) { + uint16_t crc; + + // read until start byte (0xFE) + if (false == _wait_token(SPI_START_BLOCK)) { + debug_if(SD_DBG, "Read timeout\n"); + _deselect(); + return SD_BLOCK_DEVICE_ERROR_NO_RESPONSE; + } + + // read data + _spi.write(NULL, 0, (char*)buffer, length); + + // Read the CRC16 checksum for the data block + crc = (_spi.write(SPI_FILL_CHAR) << 8); + crc |= _spi.write(SPI_FILL_CHAR); + + return 0; +} + +uint8_t SDBlockDevice::_write(const uint8_t *buffer, uint8_t token, uint32_t length) { + uint16_t crc = 0xFFFF; + uint8_t response = 0xFF; + + // indicate start of block + _spi.write(token); + + // write the data + _spi.write((char*)buffer, length, NULL, 0); + + // write the checksum CRC16 + _spi.write(crc >> 8); + _spi.write(crc); + + // check the response token + response = _spi.write(SPI_FILL_CHAR); + + // Wait for last block to be written + if (false == _wait_ready(SD_COMMAND_TIMEOUT)) { + debug_if(SD_DBG, "Card not ready yet \n"); + } + + return (response & SPI_DATA_RESPONSE_MASK); +} + +static uint32_t ext_bits(unsigned char *data, int msb, int lsb) { + uint32_t bits = 0; + uint32_t size = 1 + msb - lsb; + for (uint32_t i = 0; i < size; i++) { + uint32_t position = lsb + i; + uint32_t byte = 15 - (position >> 3); + uint32_t bit = position & 0x7; + uint32_t value = (data[byte] >> bit) & 1; + bits |= value << i; + } + return bits; +} + +bd_size_t SDBlockDevice::_sd_sectors() { + uint32_t c_size, c_size_mult, read_bl_len; + uint32_t block_len, mult, blocknr; + uint32_t hc_c_size; + bd_size_t blocks = 0, capacity = 0; + + // CMD9, Response R2 (R1 byte + 16-byte block read) + if (_cmd(CMD9_SEND_CSD, 0x0) != 0x0) { + debug_if(SD_DBG, "Didn't get a response from the disk\n"); + return 0; + } + uint8_t csd[16]; + if (_read_bytes(csd, 16) != 0) { + debug_if(SD_DBG, "Couldn't read csd response from disk\n"); + return 0; + } + + // csd_structure : csd[127:126] + int csd_structure = ext_bits(csd, 127, 126); + switch (csd_structure) { + case 0: + c_size = ext_bits(csd, 73, 62); // c_size : csd[73:62] + c_size_mult = ext_bits(csd, 49, 47); // c_size_mult : csd[49:47] + read_bl_len = ext_bits(csd, 83, 80); // read_bl_len : csd[83:80] - the *maximum* read block length + block_len = 1 << read_bl_len; // BLOCK_LEN = 2^READ_BL_LEN + mult = 1 << (c_size_mult + 2); // MULT = 2^C_SIZE_MULT+2 (C_SIZE_MULT < 8) + blocknr = (c_size + 1) * mult; // BLOCKNR = (C_SIZE+1) * MULT + capacity = blocknr * block_len; // memory capacity = BLOCKNR * BLOCK_LEN + blocks = capacity / _block_size; + debug_if(SD_DBG, "Standard Capacity: c_size: %d \n", c_size); + debug_if(SD_DBG, "Sectors: 0x%x : %llu\n", blocks, blocks); + debug_if(SD_DBG, "Capacity: 0x%x : %llu MB\n", capacity, (capacity/(1024U*1024U))); + + // ERASE_BLK_EN = 1: Erase in multiple of 512 bytes supported + if (ext_bits(csd, 46, 46)) { + _erase_size = BLOCK_SIZE_HC; + } else { + // ERASE_BLK_EN = 1: Erase in multiple of SECTOR_SIZE supported + _erase_size = BLOCK_SIZE_HC * (ext_bits(csd, 45, 39) + 1); + } + break; + + case 1: + hc_c_size = ext_bits(csd, 69, 48); // device size : C_SIZE : [69:48] + blocks = (hc_c_size+1) << 10; // block count = C_SIZE+1) * 1K byte (512B is block size) + debug_if(SD_DBG, "SDHC/SDXC Card: hc_c_size: %d \n", hc_c_size); + debug_if(SD_DBG, "Sectors: 0x%x : %llu\n", blocks, blocks); + debug_if(SD_DBG, "Capacity: %llu MB\n", (blocks/(2048U))); + // ERASE_BLK_EN is fixed to 1, which means host can erase one or multiple of 512 bytes. + _erase_size = BLOCK_SIZE_HC; + break; + + default: + debug_if(SD_DBG, "CSD struct unsupported\r\n"); + return 0; + }; + return blocks; +} + +// SPI function to wait till chip is ready and sends start token +bool SDBlockDevice::_wait_token(uint8_t token) { + _spi_timer.reset(); + _spi_timer.start(); + + do { + if (token == _spi.write(SPI_FILL_CHAR)) { + _spi_timer.stop(); + return true; + } + } while (_spi_timer.read_ms() < 300); // Wait for 300 msec for start token + _spi_timer.stop(); + debug_if(SD_DBG, "_wait_token: timeout\n"); + return false; +} + +// SPI function to wait till chip is ready +// The host controller should wait for end of the process until DO goes high (a 0xFF is received). +bool SDBlockDevice::_wait_ready(uint16_t ms) { + uint8_t response; + _spi_timer.reset(); + _spi_timer.start(); + do { + response = _spi.write(SPI_FILL_CHAR); + if (response == 0xFF) { + _spi_timer.stop(); + return true; + } + } while (_spi_timer.read_ms() < ms); + _spi_timer.stop(); + return false; +} + +// SPI function to wait for count +void SDBlockDevice::_spi_wait(uint8_t count) +{ + for (uint8_t i = 0; i < count; ++i) { + _spi.write(SPI_FILL_CHAR); + } +} + +void SDBlockDevice::_spi_init() { + _spi.lock(); + // Set to SCK for initialization, and clock card with cs = 1 + _spi.frequency(_init_sck); + _spi.format(8, 0); + _spi.set_default_write_value(SPI_FILL_CHAR); + // Initial 74 cycles required for few cards, before selecting SPI mode + _cs = 1; + _spi_wait(10); + _spi.unlock(); +} + +void SDBlockDevice::_select() { + _spi.lock(); + _spi.write(SPI_FILL_CHAR); + _cs = 0; +} + +void SDBlockDevice::_deselect() { + _cs = 1; + _spi.write(SPI_FILL_CHAR); + _spi.unlock(); +} + +#endif /* DEVICE_SPI */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SDBlockDevice.h Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,232 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_SD_BLOCK_DEVICE_H +#define MBED_SD_BLOCK_DEVICE_H + +/* If the target has no SPI support then SDCard is not supported */ +#ifdef DEVICE_SPI + +#include "BlockDevice.h" +#include "mbed.h" +#include "platform/PlatformMutex.h" + +/** Access an SD Card using SPI + * + * @code + * #include "mbed.h" + * #include "SDBlockDevice.h" + * + * SDBlockDevice sd(p5, p6, p7, p12); // mosi, miso, sclk, cs + * uint8_t block[512] = "Hello World!\n"; + * + * int main() { + * sd.init(); + * sd.write(block, 0, 512); + * sd.read(block, 0, 512); + * printf("%s", block); + * sd.deinit(); + * } + */ +class SDBlockDevice : public BlockDevice { +public: + /** Lifetime of an SD card + */ + SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs, uint64_t hz=1000000); + virtual ~SDBlockDevice(); + + /** Initialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int init(); + + /** Deinitialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int deinit(); + + /** Read blocks from a block device + * + * @param buffer Buffer to write blocks to + * @param addr Address of block to begin reading from + * @param size Size to read in bytes, must be a multiple of read block size + * @return 0 on success, negative error code on failure + */ + virtual int read(void *buffer, bd_addr_t addr, bd_size_t size); + + /** Program blocks to a block device + * + * The blocks must have been erased prior to being programmed + * + * @param buffer Buffer of data to write to blocks + * @param addr Address of block to begin writing to + * @param size Size to write in bytes, must be a multiple of program block size + * @return 0 on success, negative error code on failure + */ + virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size); + + /** Mark blocks as no longer in use + * + * This function provides a hint to the underlying block device that a region of blocks + * is no longer in use and may be erased without side effects. Erase must still be called + * before programming, but trimming allows flash-translation-layers to schedule erases when + * the device is not busy. + * + * @param addr Address of block to mark as unused + * @param size Size to mark as unused in bytes, must be a multiple of erase block size + * @return 0 on success, negative error code on failure + */ + virtual int trim(bd_addr_t addr, bd_size_t size); + + /** Get the size of a readable block + * + * @return Size of a readable block in bytes + */ + virtual bd_size_t get_read_size() const; + + /** Get the size of a programable block + * + * @return Size of a programable block in bytes + * @note Must be a multiple of the read size + */ + virtual bd_size_t get_program_size() const; + + /** Get the total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual bd_size_t size() const; + + /** Enable or disable debugging + * + * @param State of debugging + */ + virtual void debug(bool dbg); + + /** Set the transfer frequency + * + * @param Transfer frequency + * @note Max frequency supported is 25MHZ + */ + virtual int frequency(uint64_t freq); + + +private: + /* Commands : Listed below are commands supported + * in SPI mode for SD card : Only Mandatory ones + */ + enum cmdSupported { + CMD_NOT_SUPPORTED = -1, /**< Command not supported error */ + CMD0_GO_IDLE_STATE = 0, /**< Resets the SD Memory Card */ + CMD1_SEND_OP_COND = 1, /**< Sends host capacity support */ + CMD6_SWITCH_FUNC = 6, /**< Check and Switches card function */ + CMD8_SEND_IF_COND = 8, /**< Supply voltage info */ + CMD9_SEND_CSD = 9, /**< Provides Card Specific data */ + CMD10_SEND_CID = 10, /**< Provides Card Identification */ + CMD12_STOP_TRANSMISSION = 12, /**< Forces the card to stop transmission */ + CMD13_SEND_STATUS = 13, /**< Card responds with status */ + CMD16_SET_BLOCKLEN = 16, /**< Length for SC card is set */ + CMD17_READ_SINGLE_BLOCK = 17, /**< Read single block of data */ + CMD18_READ_MULTIPLE_BLOCK = 18, /**< Card transfers data blocks to host until interrupted + by a STOP_TRANSMISSION command */ + CMD24_WRITE_BLOCK = 24, /**< Write single block of data */ + CMD25_WRITE_MULTIPLE_BLOCK = 25, /**< Continuously writes blocks of data until + 'Stop Tran' token is sent */ + CMD27_PROGRAM_CSD = 27, /**< Programming bits of CSD */ + CMD32_ERASE_WR_BLK_START_ADDR = 32, /**< Sets the address of the first write + block to be erased. */ + CMD33_ERASE_WR_BLK_END_ADDR = 33, /**< Sets the address of the last write + block of the continuous range to be erased.*/ + CMD38_ERASE = 38, /**< Erases all previously selected write blocks */ + CMD55_APP_CMD = 55, /**< Extend to Applications specific commands */ + CMD56_GEN_CMD = 56, /**< General Purpose Command */ + CMD58_READ_OCR = 58, /**< Read OCR register of card */ + CMD59_CRC_ON_OFF = 59, /**< Turns the CRC option on or off*/ + // App Commands + ACMD6_SET_BUS_WIDTH = 6, + ACMD13_SD_STATUS = 13, + ACMD22_SEND_NUM_WR_BLOCKS = 22, + ACMD23_SET_WR_BLK_ERASE_COUNT = 23, + ACMD41_SD_SEND_OP_COND = 41, + ACMD42_SET_CLR_CARD_DETECT = 42, + ACMD51_SEND_SCR = 51, + }; + + uint8_t _card_type; + int _cmd(SDBlockDevice::cmdSupported cmd, uint32_t arg, bool isAcmd=0, uint32_t *resp=NULL); + int _cmd8(); + + /* Move the SDCard into the SPI Mode idle state + * + * The card is transitioned from SDCard mode to SPI mode by sending the + * CMD0 (GO_IDLE_STATE) command with CS asserted. See the notes in the + * "SPI Startup" section of the comments at the head of the + * implementation file for further details and specification references. + * + * @return Response form the card. R1_IDLE_STATE (0x1), the successful + * response from CMD0. R1_XXX_XXX for more response + */ + uint32_t _go_idle_state(); + int _initialise_card(); + + bd_size_t _sectors; + bd_size_t _sd_sectors(); + + bool _is_valid_trim(bd_addr_t addr, bd_size_t size); + + /* SPI functions */ + Timer _spi_timer; /**< Timer Class object used for busy wait */ + uint32_t _init_sck; /**< Intial SPI frequency */ + uint32_t _transfer_sck; /**< SPI frequency during data transfer/after initialization */ + SPI _spi; /**< SPI Class object */ + + /* SPI initialization function */ + void _spi_init(); + uint8_t _cmd_spi(SDBlockDevice::cmdSupported cmd, uint32_t arg); + void _spi_wait(uint8_t count); + + bool _wait_token(uint8_t token); /**< Wait for token */ + bool _wait_ready(uint16_t ms=300); /**< 300ms default wait for card to be ready */ + int _read(uint8_t * buffer, uint32_t length); + int _read_bytes(uint8_t * buffer, uint32_t length); + uint8_t _write(const uint8_t *buffer,uint8_t token, uint32_t length); + int _freq(void); + + /* Chip Select and SPI mode select */ + DigitalOut _cs; + void _select(); + void _deselect(); + + virtual void lock() { + _mutex.lock(); + } + + virtual void unlock() { + _mutex.unlock(); + } + + PlatformMutex _mutex; + bd_size_t _block_size; + bd_size_t _erase_size; + bool _is_initialized; + bool _dbg; +}; + +#endif /* DEVICE_SPI */ + +#endif /* MBED_SD_BLOCK_DEVICE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TESTS/block_device/basic/basic.cpp Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,179 @@ +/* + * 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 main.cpp Basic SD Driver Test + */ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#include "SDBlockDevice.h" +#include <stdlib.h> + +using namespace utest::v1; + +#define TEST_BLOCK_COUNT 10 +#define TEST_ERROR_MASK 16 +#define TEST_BLOCK_SIZE 2048 + +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() { + SDBlockDevice sd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS); + + int err = sd.init(); + TEST_ASSERT_EQUAL(0, err); + + err = sd.frequency(8000000); + 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 = (sd.*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 erase_size = sd.get_erase_size(); + bd_size_t block_size = erase_size > TEST_BLOCK_SIZE ? erase_size : TEST_BLOCK_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(sd.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) % sd.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 = sd.trim(block, block_size); + TEST_ASSERT_EQUAL(0, err); + + err = sd.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 = sd.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 = sd.deinit(); + TEST_ASSERT_EQUAL(0, err); +} + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(120, "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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TESTS/filesystem/basic/basic.cpp Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,933 @@ +/* + * 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 "SDBlockDevice.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 <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <algorithm> +/* 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 >>> + */ +#if defined(DEVICE_SPI) && ( defined(MBED_CONF_APP_FSFAT_SDCARD_INSTALLED) || (MBED_CONF_SD_FSFAT_SDCARD_INSTALLED)) + +#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 char *sd_mount_pt = "sd"; +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"; + + + +SDBlockDevice sd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS); +FATFileSystem fs(sd_mount_pt, &sd); + +#define FSFAT_BASIC_MSG(_buf, _max_len, _fmt, ...) \ + do \ + { \ + snprintf((_buf), (_max_len), (_fmt), __VA_ARGS__); \ + }while(0); + +/** @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; +} + +#else + +#define FSFAT_BASIC_TEST_00 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_01 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_02 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_03 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_04 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_05 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_06 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_07 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_08 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_09 fsfat_basic_test_dummy +#define FSFAT_BASIC_TEST_10 fsfat_basic_test_dummy + + +/** @brief fsfat_basic_test_dummy Dummy test case for testing when platform doesnt have an SDCard installed. + * + * @return success always + */ +static control_t fsfat_basic_test_dummy() +{ + printf("Null test\n"); + return CaseNext; +} + +#endif + +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_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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TESTS/filesystem/dirs/main.cpp Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,472 @@ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include <stdlib.h> +#include <errno.h> + +using namespace utest::v1; + +// test configuration +#ifndef MBED_TEST_FILESYSTEM +#define MBED_TEST_FILESYSTEM FATFileSystem +#endif + +#ifndef MBED_TEST_FILESYSTEM_DECL +#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs") +#endif + +#ifndef MBED_TEST_BLOCKDEVICE +#define MBED_TEST_BLOCKDEVICE SDBlockDevice +#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS); +#endif + +#ifndef MBED_TEST_BLOCKDEVICE_DECL +#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd +#endif + +#ifndef MBED_TEST_FILES +#define MBED_TEST_FILES 4 +#endif + +#ifndef MBED_TEST_DIRS +#define MBED_TEST_DIRS 4 +#endif + +#ifndef MBED_TEST_BUFFER +#define MBED_TEST_BUFFER 8192 +#endif + +#ifndef MBED_TEST_TIMEOUT +#define MBED_TEST_TIMEOUT 120 +#endif + + +// declarations +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x +#define INCLUDE(x) STRINGIZE(x.h) + +#include INCLUDE(MBED_TEST_FILESYSTEM) +#include INCLUDE(MBED_TEST_BLOCKDEVICE) + +MBED_TEST_FILESYSTEM_DECL; +MBED_TEST_BLOCKDEVICE_DECL; + +Dir dir[MBED_TEST_DIRS]; +File file[MBED_TEST_FILES]; +DIR *dd[MBED_TEST_DIRS]; +FILE *fd[MBED_TEST_FILES]; +struct dirent ent; +struct dirent *ed; +size_t size; +uint8_t buffer[MBED_TEST_BUFFER]; +uint8_t rbuffer[MBED_TEST_BUFFER]; +uint8_t wbuffer[MBED_TEST_BUFFER]; + + +// tests + +void test_directory_tests() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = MBED_TEST_FILESYSTEM::format(&bd); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_root_directory() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "/"); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_directory_creation() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("potato", 0777); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_file_creation() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "burito", O_CREAT | O_WRONLY); + TEST_ASSERT_EQUAL(0, res); + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void dir_file_check(char *list[], uint32_t elements) { + int res; + while(1) { + res = dir[0].read(&ent); + if (0 == res) { + break; + } + for (int i = 0; i < elements ; i++) { + res = strcmp(ent.d_name, list[i]); + if (0 == res) { + res = ent.d_type; + if ((DT_DIR != res) && (DT_REG != res)) { + TEST_ASSERT(1); + } + break; + } + else if( i == elements) { + TEST_ASSERT_EQUAL(0, res); + } + } + } +} + +void test_directory_iteration() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "/"); + TEST_ASSERT_EQUAL(0, res); + char *dir_list[] = {"potato", "burito", ".", ".."}; + + dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0]))); + + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_directory_failures() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("potato", 0777); + TEST_ASSERT_EQUAL(-EEXIST, res); + res = dir[0].open(&fs, "tomato"); + TEST_ASSERT_EQUAL(-ENOENT, res); + res = dir[0].open(&fs, "burito"); + TEST_ASSERT_NOT_EQUAL(0, res); + res = file[0].open(&fs, "tomato", O_RDONLY); + TEST_ASSERT_EQUAL(-ENOENT, res); + res = file[0].open(&fs, "potato", O_RDONLY); + TEST_ASSERT_NOT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_nested_directories() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("potato/baked", 0777); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("potato/sweet", 0777); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("potato/fried", 0777); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "/"); + TEST_ASSERT_EQUAL(0, res); + char *dir_list[] = {"potato", "baked", "sweet", "fried", ".", ".."}; + dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0]))); + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_multi_block_directory() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("cactus", 0777); + TEST_ASSERT_EQUAL(0, res); + for (int i = 0; i < 128; i++) { + sprintf((char*)buffer, "cactus/test%d", i); + res = fs.mkdir((char*)buffer, 0777); + TEST_ASSERT_EQUAL(0, res); + } + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "cactus"); + TEST_ASSERT_EQUAL(0, res); + +#if (MBED_TEST_FILESYSTEM != FATFileSystem) + char *dir_list[] = {".", ".."}; + dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0]))); +#endif + + for (int i = 0; i < 128; i++) { + sprintf((char*)buffer, "test%d", i); + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, (char*)buffer); + TEST_ASSERT_EQUAL(0, res); + } + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_directory_remove() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = fs.remove("potato"); + TEST_ASSERT_NOT_EQUAL(0, res); + res = fs.remove("potato/sweet"); + TEST_ASSERT_EQUAL(0, res); + res = fs.remove("potato/baked"); + TEST_ASSERT_EQUAL(0, res); + res = fs.remove("potato/fried"); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "potato"); + TEST_ASSERT_EQUAL(0, res); + +#if (MBED_TEST_FILESYSTEM != FATFileSystem) + char *dir_list[] = {".", ".."}; + dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0]))); +#endif + + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.remove("potato"); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "/"); + TEST_ASSERT_EQUAL(0, res); + char *dir_list[] = {"burito", "cactus", ".", ".."}; + dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0]))); + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_directory_rename() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("coldpotato", 0777); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("coldpotato/baked", 0777); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("coldpotato/sweet", 0777); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("coldpotato/fried", 0777); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = fs.rename("coldpotato", "hotpotato"); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "hotpotato"); + TEST_ASSERT_EQUAL(0, res); + char *dir_list[] = {"baked", "sweet", "fried", ".", ".."}; + dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0]))); + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("warmpotato", 0777); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("warmpotato/mushy", 0777); + TEST_ASSERT_EQUAL(0, res); + res = fs.rename("hotpotato", "warmpotato"); + TEST_ASSERT_NOT_EQUAL(0, res); + res = fs.remove("warmpotato/mushy"); + TEST_ASSERT_EQUAL(0, res); + res = fs.remove("warmpotato"); + TEST_ASSERT_EQUAL(0, res); + res = fs.rename("hotpotato", "warmpotato"); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "warmpotato"); + TEST_ASSERT_EQUAL(0, res); + char *dir_list[] = {"baked", "sweet", "fried", ".", ".."}; + dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0]))); + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("coldpotato", 0777); + TEST_ASSERT_EQUAL(0, res); + res = fs.rename("warmpotato/baked", "coldpotato/baked"); + TEST_ASSERT_EQUAL(0, res); + res = fs.rename("warmpotato/sweet", "coldpotato/sweet"); + TEST_ASSERT_EQUAL(0, res); + res = fs.rename("warmpotato/fried", "coldpotato/fried"); + TEST_ASSERT_EQUAL(0, res); + res = fs.remove("coldpotato"); + TEST_ASSERT_NOT_EQUAL(0, res); + res = fs.remove("warmpotato"); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "coldpotato"); + TEST_ASSERT_EQUAL(0, res); + char *dir_list[] = {"baked", "sweet", "fried", ".", ".."}; + dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0]))); + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + + + +// test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Directory tests", test_directory_tests), + Case("Root directory", test_root_directory), + Case("Directory creation", test_directory_creation), + Case("File creation", test_file_creation), + Case("Directory iteration", test_directory_iteration), + Case("Directory failures", test_directory_failures), + Case("Nested directories", test_nested_directories), + Case("Multi-block directory", test_multi_block_directory), + Case("Directory remove", test_directory_remove), + Case("Directory rename", test_directory_rename), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TESTS/filesystem/files/main.cpp Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,314 @@ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include <stdlib.h> +#include <errno.h> + +using namespace utest::v1; + +// test configuration +#ifndef MBED_TEST_FILESYSTEM +#define MBED_TEST_FILESYSTEM FATFileSystem +#endif + +#ifndef MBED_TEST_FILESYSTEM_DECL +#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs") +#endif + +#ifndef MBED_TEST_BLOCKDEVICE +#define MBED_TEST_BLOCKDEVICE SDBlockDevice +#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS); +#endif + +#ifndef MBED_TEST_BLOCKDEVICE_DECL +#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd +#endif + +#ifndef MBED_TEST_FILES +#define MBED_TEST_FILES 4 +#endif + +#ifndef MBED_TEST_DIRS +#define MBED_TEST_DIRS 4 +#endif + +#ifndef MBED_TEST_BUFFER +#define MBED_TEST_BUFFER 8192 +#endif + +#ifndef MBED_TEST_TIMEOUT +#define MBED_TEST_TIMEOUT 120 +#endif + + +// declarations +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x +#define INCLUDE(x) STRINGIZE(x.h) + +#include INCLUDE(MBED_TEST_FILESYSTEM) +#include INCLUDE(MBED_TEST_BLOCKDEVICE) + +MBED_TEST_FILESYSTEM_DECL; +MBED_TEST_BLOCKDEVICE_DECL; + +Dir dir[MBED_TEST_DIRS]; +File file[MBED_TEST_FILES]; +DIR *dd[MBED_TEST_DIRS]; +FILE *fd[MBED_TEST_FILES]; +struct dirent ent; +struct dirent *ed; +size_t size; +uint8_t buffer[MBED_TEST_BUFFER]; +uint8_t rbuffer[MBED_TEST_BUFFER]; +uint8_t wbuffer[MBED_TEST_BUFFER]; + +static char file_counter = 0; +const char *filenames[] = {"smallavacado", "mediumavacado", "largeavacado", + "blockfile", "bigblockfile", "hello", ".", ".."}; + +// tests + +void test_file_tests() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = MBED_TEST_FILESYSTEM::format(&bd); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_simple_file_test() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "hello", O_WRONLY | O_CREAT); + TEST_ASSERT_EQUAL(0, res); + size = strlen("Hello World!\n"); + memcpy(wbuffer, "Hello World!\n", size); + res = file[0].write(wbuffer, size); + TEST_ASSERT_EQUAL(size, res); + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "hello", O_RDONLY); + TEST_ASSERT_EQUAL(0, res); + size = strlen("Hello World!\n"); + res = file[0].read(rbuffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(rbuffer, wbuffer, size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +template <int file_size, int write_size, int read_size> +void test_write_file_test() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + size_t size = file_size; + size_t chunk = write_size; + srand(0); + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, filenames[file_counter], O_WRONLY | O_CREAT); + TEST_ASSERT_EQUAL(0, res); + for (size_t i = 0; i < size; i += chunk) { + chunk = (chunk < size - i) ? chunk : size - i; + for (size_t b = 0; b < chunk; b++) { + buffer[b] = rand() & 0xff; + } + res = file[0].write(buffer, chunk); + TEST_ASSERT_EQUAL(chunk, res); + } + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + size_t size = file_size; + size_t chunk = read_size; + srand(0); + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, filenames[file_counter], O_RDONLY); + TEST_ASSERT_EQUAL(0, res); + for (size_t i = 0; i < size; i += chunk) { + chunk = (chunk < size - i) ? chunk : size - i; + res = file[0].read(buffer, chunk); + TEST_ASSERT_EQUAL(chunk, res); + for (size_t b = 0; b < chunk && i+b < size; b++) { + res = buffer[b]; + TEST_ASSERT_EQUAL(rand() & 0xff, res); + } + } + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + file_counter++; + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_non_overlap_check() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + size_t size = 32; + size_t chunk = 29; + srand(0); + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "smallavacado", O_RDONLY); + TEST_ASSERT_EQUAL(0, res); + for (size_t i = 0; i < size; i += chunk) { + chunk = (chunk < size - i) ? chunk : size - i; + res = file[0].read(buffer, chunk); + TEST_ASSERT_EQUAL(chunk, res); + for (size_t b = 0; b < chunk && i+b < size; b++) { + res = buffer[b]; + TEST_ASSERT_EQUAL(rand() & 0xff, res); + } + } + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + size_t size = 8192; + size_t chunk = 29; + srand(0); + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "mediumavacado", O_RDONLY); + TEST_ASSERT_EQUAL(0, res); + for (size_t i = 0; i < size; i += chunk) { + chunk = (chunk < size - i) ? chunk : size - i; + res = file[0].read(buffer, chunk); + TEST_ASSERT_EQUAL(chunk, res); + for (size_t b = 0; b < chunk && i+b < size; b++) { + res = buffer[b]; + TEST_ASSERT_EQUAL(rand() & 0xff, res); + } + } + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + { + size_t size = 262144; + size_t chunk = 29; + srand(0); + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "largeavacado", O_RDONLY); + TEST_ASSERT_EQUAL(0, res); + for (size_t i = 0; i < size; i += chunk) { + chunk = (chunk < size - i) ? chunk : size - i; + res = file[0].read(buffer, chunk); + TEST_ASSERT_EQUAL(chunk, res); + for (size_t b = 0; b < chunk && i+b < size; b++) { + res = buffer[b]; + TEST_ASSERT_EQUAL(rand() & 0xff, res); + } + } + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_dir_check() { + + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "/"); + TEST_ASSERT_EQUAL(0, res); + int numFiles = sizeof(filenames)/sizeof(filenames[0]); + // Check the filenames in directory + while(1) { + res = dir[0].read(&ent); + if (0 == res) { + break; + } + for (int i=0; i < numFiles ; i++) { + res = strcmp(ent.d_name, filenames[i]); + if (0 == res) { + res = ent.d_type; + if ((DT_REG != res) && (DT_DIR != res)) { + TEST_ASSERT(1); + } + break; + } + else if( i == numFiles) { + TEST_ASSERT_EQUAL(0, res); + } + } + } + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + + +// test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("File tests", test_file_tests), + Case("Simple file test", test_simple_file_test), + Case("Small file test", test_write_file_test<32, 31, 29>), + Case("Medium file test", test_write_file_test<8192, 31, 29>), + Case("Large file test", test_write_file_test<262144, 31, 29>), + Case("Block Size file test", test_write_file_test<9000, 512, 512>), + Case("Multiple block size file test", test_write_file_test<26215, MBED_TEST_BUFFER, MBED_TEST_BUFFER>), + Case("Non-overlap check", test_non_overlap_check), + Case("Dir check", test_dir_check), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TESTS/filesystem/fopen/fopen.cpp Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,1519 @@ +/* + * 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 "SDBlockDevice.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 <stdio.h> +#include <string.h> +#include <stdlib.h> /*rand()*/ +#include <inttypes.h> +#include <errno.h> +/* mbed_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; + +/// @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 >>> + */ + +#if defined(DEVICE_SPI) && ( defined(MBED_CONF_APP_FSFAT_SDCARD_INSTALLED) || (MBED_CONF_SD_FSFAT_SDCARD_INSTALLED)) +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"; + +SDBlockDevice sd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS); +FATFileSystem fs("sd", &sd); + +#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_16 fsfat_fopen_test_16 +#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)) { + if( 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"); + + 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 */ +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) + /* 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: 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); +#endif /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */ + 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); + + 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_15(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_16_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"}, + { "/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"}, + { 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_16(const size_t call_count) +{ + int32_t ret = 0; + fsfat_kv_data_t *node = fsfat_fopen_test_16_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_16_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_16_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_16_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_16_kv_data; + while(node->filename != NULL) { + fsfat_filepath_remove_all((char*) node->filename); + node++; + } + return CaseNext; +} + + +#else + + +#define FSFAT_FOPEN_TEST_01 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_02 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_03 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_04 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_05 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_06 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_07 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_08 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_09 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_10 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_11 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_12 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_13 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_14 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_15 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_16 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_17 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_18 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_19 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_20 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_21 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_22 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_23 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_24 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_25 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_26 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_27 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_28 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_29 fsfat_fopen_test_dummy +#define FSFAT_FOPEN_TEST_30 fsfat_fopen_test_dummy + +/** @brief fsfat_fopen_test_dummy Dummy test case for testing when platform doesnt have an SDCard installed. + * + * @return success always + */ +static control_t fsfat_fopen_test_dummy() +{ + printf("Null test\n"); + return CaseNext; +} + +#endif + + +/// @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_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: format() test.", FSFAT_FOPEN_TEST_15), + Case("FSFAT_FOPEN_TEST_16: write/check n x 25kB data files.", FSFAT_FOPEN_TEST_16), +}; + + +/* Declare your test specification with a custom setup handler */ +Specification specification(greentea_setup, cases); + +int main() +{ + return !Harness::run(specification); +} +/// @endcond
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TESTS/filesystem/parallel/main.cpp Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,184 @@ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include <stdlib.h> +#include <errno.h> + +using namespace utest::v1; + +// test configuration +#ifndef MBED_TEST_FILESYSTEM +#define MBED_TEST_FILESYSTEM FATFileSystem +#endif + +#ifndef MBED_TEST_FILESYSTEM_DECL +#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs") +#endif + +#ifndef MBED_TEST_BLOCKDEVICE +#define MBED_TEST_BLOCKDEVICE SDBlockDevice +#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS); +#endif + +#ifndef MBED_TEST_BLOCKDEVICE_DECL +#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd +#endif + +#ifndef MBED_TEST_FILES +#define MBED_TEST_FILES 4 +#endif + +#ifndef MBED_TEST_DIRS +#define MBED_TEST_DIRS 4 +#endif + +#ifndef MBED_TEST_BUFFER +#define MBED_TEST_BUFFER 512 +#endif + +#ifndef MBED_TEST_TIMEOUT +#define MBED_TEST_TIMEOUT 120 +#endif + +#ifndef MBED_THREAD_COUNT +#define MBED_THREAD_COUNT MBED_TEST_FILES +#endif + +// declarations +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x +#define INCLUDE(x) STRINGIZE(x.h) + +#include INCLUDE(MBED_TEST_FILESYSTEM) +#include INCLUDE(MBED_TEST_BLOCKDEVICE) + +MBED_TEST_FILESYSTEM_DECL; +MBED_TEST_BLOCKDEVICE_DECL; + +Dir dir[MBED_TEST_DIRS]; +File file[MBED_TEST_FILES]; +DIR *dd[MBED_TEST_DIRS]; +FILE *fd[MBED_TEST_FILES]; +struct dirent ent; +struct dirent *ed; + +volatile bool count_done = 0; + +// tests + +void test_file_tests() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = MBED_TEST_FILESYSTEM::format(&bd); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void write_file_data (char count) { + + char filename[10]; + uint8_t wbuffer[MBED_TEST_BUFFER]; + int res; + + sprintf(filename, "%s%d", "data", count); + res = file[count].open(&fs, filename, O_WRONLY | O_CREAT); + TEST_ASSERT_EQUAL(0, res); + + char letter = 'A'+ count; + for (uint32_t i = 0; i < MBED_TEST_BUFFER; i++) { + wbuffer[i] = letter++; + if ('z' == letter) { + letter = 'A' + count; + } + } + + for (uint32_t i = 0; i < 5; i++) { + res = file[count].write(wbuffer, MBED_TEST_BUFFER); + TEST_ASSERT_EQUAL(MBED_TEST_BUFFER, res); + } + + res = file[count].close(); + TEST_ASSERT_EQUAL(0, res); +} + +void read_file_data (char count) { + char filename[10]; + uint8_t rbuffer[MBED_TEST_BUFFER]; + int res; + + sprintf(filename, "%s%d", "data", count); + res = file[count].open(&fs, filename, O_RDONLY); + TEST_ASSERT_EQUAL(0, res); + + for (uint32_t i = 0; i < 5; i++) { + res = file[count].read(rbuffer, MBED_TEST_BUFFER); + TEST_ASSERT_EQUAL(MBED_TEST_BUFFER, res); + char letter = 'A' + count; + for (uint32_t i = 0; i < MBED_TEST_BUFFER; i++) { + res = rbuffer[i]; + TEST_ASSERT_EQUAL(letter++, res); + if ('z' == letter) { + letter = 'A' + count; + } + } + } + + res = file[count].close(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_thread_access_test() { + + Thread *data[MBED_THREAD_COUNT]; + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + + // Write threads in parallel + for (char thread_count = 0; thread_count < MBED_THREAD_COUNT; thread_count++) { + data[thread_count] = new Thread(osPriorityNormal); + data[thread_count]->start(callback((void(*)(void*))write_file_data, (void*)thread_count)); + } + + // Wait for write thread to join before creating read thread + for (char thread_count = 0; thread_count < MBED_THREAD_COUNT; thread_count++) { + data[thread_count]->join(); + delete data[thread_count]; + data[thread_count] = new Thread(osPriorityNormal); + data[thread_count]->start(callback((void(*)(void*))read_file_data, (void*)thread_count)); + } + + // Wait for read threads to join + for (char thread_count = 0; thread_count < MBED_THREAD_COUNT; thread_count++) { + data[thread_count]->join(); + delete data[thread_count]; + } + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +// test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("File tests", test_file_tests), + Case("Filesystem access from multiple threads", test_thread_access_test), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TESTS/filesystem/seek/main.cpp Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,629 @@ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include <stdlib.h> +#include <errno.h> + +using namespace utest::v1; + +// test configuration +#ifndef MBED_TEST_FILESYSTEM +#define MBED_TEST_FILESYSTEM FATFileSystem +#endif + +#ifndef MBED_TEST_FILESYSTEM_DECL +#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs") +#endif + +#ifndef MBED_TEST_BLOCKDEVICE +#define MBED_TEST_BLOCKDEVICE SDBlockDevice +#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS); +#endif + +#ifndef MBED_TEST_BLOCKDEVICE_DECL +#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd +#endif + +#ifndef MBED_TEST_FILES +#define MBED_TEST_FILES 4 +#endif + +#ifndef MBED_TEST_DIRS +#define MBED_TEST_DIRS 4 +#endif + +#ifndef MBED_TEST_BUFFER +#define MBED_TEST_BUFFER 8192 +#endif + +#ifndef MBED_TEST_TIMEOUT +#define MBED_TEST_TIMEOUT 120 +#endif + + +// declarations +#define STRINGIZE(x) STRINGIZE2(x) +#define STRINGIZE2(x) #x +#define INCLUDE(x) STRINGIZE(x.h) + +#include INCLUDE(MBED_TEST_FILESYSTEM) +#include INCLUDE(MBED_TEST_BLOCKDEVICE) + +MBED_TEST_FILESYSTEM_DECL; +MBED_TEST_BLOCKDEVICE_DECL; + +Dir dir[MBED_TEST_DIRS]; +File file[MBED_TEST_FILES]; +DIR *dd[MBED_TEST_DIRS]; +FILE *fd[MBED_TEST_FILES]; +struct dirent ent; +struct dirent *ed; +size_t size; +uint8_t buffer[MBED_TEST_BUFFER]; +uint8_t rbuffer[MBED_TEST_BUFFER]; +uint8_t wbuffer[MBED_TEST_BUFFER]; + + +// tests + +void test_seek_tests() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = MBED_TEST_FILESYSTEM::format(&bd); + TEST_ASSERT_EQUAL(0, res); + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = fs.mkdir("hello", 0777); + TEST_ASSERT_EQUAL(0, res); + for (int i = 0; i < 132; i++) { + sprintf((char*)buffer, "hello/kitty%d", i); + res = file[0].open(&fs, (char*)buffer, + O_WRONLY | O_CREAT | O_APPEND); + TEST_ASSERT_EQUAL(0, res); + + size = strlen("kittycatcat"); + memcpy(buffer, "kittycatcat", size); + for (int j = 0; j < 132; j++) { + file[0].write(buffer, size); + } + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + } + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_simple_dir_seek() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "hello"); + TEST_ASSERT_EQUAL(0, res); +#if (MBED_TEST_FILESYSTEM != FATFileSystem) + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, "."); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, ".."); + TEST_ASSERT_EQUAL(0, res); +#endif + + off_t pos; + int i; + for (i = 0; i < 4; i++) { + sprintf((char*)buffer, "kitty%d", i); + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, (char*)buffer); + TEST_ASSERT_EQUAL(0, res); + pos = dir[0].tell(); + } + res = pos >= 0; + TEST_ASSERT_EQUAL(1, res); + + dir[0].seek(pos); + sprintf((char*)buffer, "kitty%d", i); + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, (char*)buffer); + TEST_ASSERT_EQUAL(0, res); + + dir[0].rewind(); + sprintf((char*)buffer, "kitty%d", 0); +#if (MBED_TEST_FILESYSTEM != FATFileSystem) + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, "."); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, ".."); + TEST_ASSERT_EQUAL(0, res); +#endif + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, (char*)buffer); + TEST_ASSERT_EQUAL(0, res); + + dir[0].seek(pos); + sprintf((char*)buffer, "kitty%d", i); + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, (char*)buffer); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_large_dir_seek() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].open(&fs, "hello"); + TEST_ASSERT_EQUAL(0, res); +#if (MBED_TEST_FILESYSTEM != FATFileSystem) + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, "."); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, ".."); + TEST_ASSERT_EQUAL(0, res); +#endif + + off_t pos; + int i; + for (i = 0; i < 128; i++) { + sprintf((char*)buffer, "kitty%d", i); + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, (char*)buffer); + TEST_ASSERT_EQUAL(0, res); + pos = dir[0].tell(); + } + res = pos >= 0; + TEST_ASSERT_EQUAL(1, res); + + dir[0].seek(pos); + sprintf((char*)buffer, "kitty%d", i); + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, (char*)buffer); + TEST_ASSERT_EQUAL(0, res); + + dir[0].rewind(); + sprintf((char*)buffer, "kitty%d", 0); +#if (MBED_TEST_FILESYSTEM != FATFileSystem) + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, "."); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, ".."); + TEST_ASSERT_EQUAL(0, res); +#endif + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, (char*)buffer); + TEST_ASSERT_EQUAL(0, res); + + dir[0].seek(pos); + sprintf((char*)buffer, "kitty%d", i); + res = dir[0].read(&ent); + TEST_ASSERT_EQUAL(1, res); + res = strcmp(ent.d_name, (char*)buffer); + TEST_ASSERT_EQUAL(0, res); + res = dir[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_simple_file_seek() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "hello/kitty42", O_RDONLY); + TEST_ASSERT_EQUAL(0, res); + + off_t pos; + size = strlen("kittycatcat"); + for (int i = 0; i < 4; i++) { + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + pos = file[0].tell(); + } + res = pos >= 0; + TEST_ASSERT_EQUAL(1, res); + res = file[0].seek(pos, SEEK_SET); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + + file[0].rewind(); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(pos, SEEK_SET); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(-size, SEEK_CUR); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(-size, SEEK_END) >= 0; + TEST_ASSERT_EQUAL(1, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + + size_t size = file[0].size(); + res = file[0].seek(0, SEEK_CUR); + TEST_ASSERT_EQUAL(size, res); + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_large_file_seek() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "hello/kitty42", O_RDONLY); + TEST_ASSERT_EQUAL(0, res); + + off_t pos; + size = strlen("kittycatcat"); + for (int i = 0; i < 128; i++) { + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + pos = file[0].tell(); + } + res = pos >= 0; + TEST_ASSERT_EQUAL(1, res); + res = file[0].seek(pos, SEEK_SET); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + + file[0].rewind(); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(pos, SEEK_SET); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(-size, SEEK_CUR); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(-size, SEEK_END) >= 0; + TEST_ASSERT_EQUAL(1, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + + size_t size = file[0].size(); + res = file[0].seek(0, SEEK_CUR); + TEST_ASSERT_EQUAL(size, res); + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_simple_file_seek_and_write() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "hello/kitty42", O_RDWR); + TEST_ASSERT_EQUAL(0, res); + + off_t pos; + size = strlen("kittycatcat"); + for (int i = 0; i < 4; i++) { + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + pos = file[0].tell(); + } + res = pos >= 0; + TEST_ASSERT_EQUAL(1, res); + + memcpy(buffer, "doggodogdog", size); + res = file[0].seek(pos, SEEK_SET); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].write(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = file[0].seek(pos, SEEK_SET); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "doggodogdog", size); + TEST_ASSERT_EQUAL(0, res); + + file[0].rewind(); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(pos, SEEK_SET); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "doggodogdog", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(-size, SEEK_END) >= 0; + TEST_ASSERT_EQUAL(1, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + + size_t size = file[0].size(); + res = file[0].seek(0, SEEK_CUR); + TEST_ASSERT_EQUAL(size, res); + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_large_file_seek_and_write() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "hello/kitty42", O_RDWR); + TEST_ASSERT_EQUAL(0, res); + + off_t pos; + size = strlen("kittycatcat"); + for (int i = 0; i < 128; i++) { + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + if (i != 4) { + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + } + pos = file[0].tell(); + } + res = pos >= 0; + TEST_ASSERT_EQUAL(1, res); + + memcpy(buffer, "doggodogdog", size); + res = file[0].seek(pos, SEEK_SET); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].write(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = file[0].seek(pos, SEEK_SET); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "doggodogdog", size); + TEST_ASSERT_EQUAL(0, res); + + file[0].rewind(); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(pos, SEEK_SET); + TEST_ASSERT_EQUAL(pos, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "doggodogdog", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(-size, SEEK_END) >= 0; + TEST_ASSERT_EQUAL(1, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + + size_t size = file[0].size(); + res = file[0].seek(0, SEEK_CUR); + TEST_ASSERT_EQUAL(size, res); + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_boundary_seek_and_write() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "hello/kitty42", O_RDWR); + TEST_ASSERT_EQUAL(0, res); + + size = strlen("hedgehoghog"); + const off_t offsets[] = {512, 1020, 513, 1021, 511, 1019}; + + for (int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) { + off_t off = offsets[i]; + memcpy(buffer, "hedgehoghog", size); + res = file[0].seek(off, SEEK_SET); + TEST_ASSERT_EQUAL(off, res); + res = file[0].write(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = file[0].seek(off, SEEK_SET); + TEST_ASSERT_EQUAL(off, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "hedgehoghog", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(0, SEEK_SET); + TEST_ASSERT_EQUAL(0, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "kittycatcat", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].sync(); + TEST_ASSERT_EQUAL(0, res); + } + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + +void test_out_of_bounds_seek() { + int res = bd.init(); + TEST_ASSERT_EQUAL(0, res); + + { + res = fs.mount(&bd); + TEST_ASSERT_EQUAL(0, res); + res = file[0].open(&fs, "hello/kitty42", O_RDWR); + TEST_ASSERT_EQUAL(0, res); + + size = strlen("kittycatcat"); + res = file[0].size(); + TEST_ASSERT_EQUAL(132*size, res); + res = file[0].seek((132+4)*size, + SEEK_SET); + TEST_ASSERT_EQUAL((132+4)*size, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(0, res); + + memcpy(buffer, "porcupineee", size); + res = file[0].write(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = file[0].seek((132+4)*size, + SEEK_SET); + TEST_ASSERT_EQUAL((132+4)*size, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); + res = memcmp(buffer, "porcupineee", size); + TEST_ASSERT_EQUAL(0, res); + res = file[0].seek(132*size, + SEEK_SET); + TEST_ASSERT_EQUAL(132*size, res); + res = file[0].read(buffer, size); + TEST_ASSERT_EQUAL(size, res); +#if (MBED_TEST_FILESYSTEM != FATFileSystem) + // FatFs does not guarantee empty expanded buffer + res = memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size); + TEST_ASSERT_EQUAL(0, res); +#endif + res = file[0].close(); + TEST_ASSERT_EQUAL(0, res); + res = fs.unmount(); + TEST_ASSERT_EQUAL(0, res); + } + + res = bd.deinit(); + TEST_ASSERT_EQUAL(0, res); +} + + + +// test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Seek tests", test_seek_tests), + Case("Simple dir seek", test_simple_dir_seek), + Case("Large dir seek", test_large_dir_seek), + Case("Simple file seek", test_simple_file_seek), + Case("Large file seek", test_large_file_seek), + Case("Simple file seek and write", test_simple_file_seek_and_write), + Case("Large file seek and write", test_large_file_seek_and_write), + Case("Boundary seek and write", test_boundary_seek_and_write), + Case("Out-of-bounds seek", test_out_of_bounds_seek), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config/mbed_lib.json Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,154 @@ +{ + "name": "sd", + "config": { + "SPI_CS": "D10", + "SPI_MOSI": "D11", + "SPI_MISO": "D12", + "SPI_CLK": "D13", + "DEVICE_SPI": 1, + "FSFAT_SDCARD_INSTALLED": 1, + "CMD_TIMEOUT": 5000, + "CMD0_IDLE_STATE_RETRIES" : 5 + }, + "target_overrides": { + "DISCO_F051R8": { + "SPI_MOSI": "SPI_MOSI", + "SPI_MISO": "SPI_MISO", + "SPI_CLK": "SPI_SCK", + "SPI_CS": "SPI_CS" + }, + "DISCO_L476VG": { + "SPI_MOSI": "PE_15", + "SPI_MISO": "PE_14", + "SPI_CLK": "PE_13", + "SPI_CS": "PE_12" + }, + "K20D50M": { + "SPI_MOSI": "PTD2", + "SPI_MISO": "PTD3", + "SPI_CLK": "PTD1", + "SPI_CS": "PTC2" + }, + "KL22F": { + "SPI_MOSI": "PTD6", + "SPI_MISO": "PTD7", + "SPI_CLK": "PTD5", + "SPI_CS": "PTD4" + }, + "KL25Z": { + "SPI_MOSI": "PTD2", + "SPI_MISO": "PTD3", + "SPI_CLK": "PTD1", + "SPI_CS": "PTD0" + }, + "KL43Z": { + "SPI_MOSI": "PTD6", + "SPI_MISO": "PTD7", + "SPI_CLK": "PTD5", + "SPI_CS": "PTD4" + }, + "KL46Z": { + "SPI_MOSI": "PTD6", + "SPI_MISO": "PTD7", + "SPI_CLK": "PTD5", + "SPI_CS": "PTD4" + }, + "K64F": { + "SPI_MOSI": "PTE3", + "SPI_MISO": "PTE1", + "SPI_CLK": "PTE2", + "SPI_CS": "PTE4" + }, + "K66F": { + "SPI_MOSI": "PTE3", + "SPI_MISO": "PTE1", + "SPI_CLK": "PTE2", + "SPI_CS": "PTE4" + }, + "LPC11U37H_401": { + "SPI_MOSI": "SDMOSI", + "SPI_MISO": "SDMISO", + "SPI_CLK": "SDSCLK", + "SPI_CS": "SDSSEL" + }, + "LPC2368": { + "SPI_MOSI": "p11", + "SPI_MISO": "p12", + "SPI_CLK": "p13", + "SPI_CS": "p14" + }, + "NUCLEO_F429ZI": { + "SPI_MOSI": "PC_12", + "SPI_MISO": "PC_11", + "SPI_CLK": "PC_10", + "SPI_CS": "PA_15" + }, + "DISCO_F429ZI": { + "SPI_MOSI": "PC_12", + "SPI_MISO": "PC_11", + "SPI_CLK": "PC_10", + "SPI_CS": "PA_15" + }, + "NUCLEO_L031K6": { + "SPI_MOSI": "SPI_MOSI", + "SPI_MISO": "SPI_MISO", + "SPI_CLK": "SPI_SCK", + "SPI_CS": "SPI_CS" + }, + "NUMAKER_PFM_M453": { + "SPI_MOSI": "PD_13", + "SPI_MISO": "PD_14", + "SPI_CLK": "PD_15", + "SPI_CS": "PD_12" + }, + "NUMAKER_PFM_NUC472": { + "SPI_MOSI": "PF_0", + "SPI_MISO": "PD_15", + "SPI_CLK": "PD_14", + "SPI_CS": "PD_13" + }, + "nRF51822": { + "SPI_MOSI": "p12", + "SPI_MISO": "p13", + "SPI_CLK": "p15", + "SPI_CS": "p14" + }, + "UBLOX_EVK_ODIN_W2": { + "SPI_CS": "D9", + "SPI_MOSI": "D11", + "SPI_MISO": "D12", + "SPI_CLK": "D13" + }, + "RZ_A1H": { + "SPI_MOSI": "P8_5", + "SPI_MISO": "P8_6", + "SPI_CLK": "P8_3", + "SPI_CS": "P8_4" + }, + "GR_LYCHEE": { + "SPI_MOSI": "P5_6", + "SPI_MISO": "P5_7", + "SPI_CLK": "P5_4", + "SPI_CS": "P5_5" + }, + "HEXIWEAR": { + "SPI_MOSI": "PTE3", + "SPI_MISO": "PTE1", + "SPI_CLK": "PTE2", + "SPI_CS": "PTE4" + }, + "TB_SENSE_12": { + "SPI_MOSI": "PC6", + "SPI_MISO": "PC7", + "SPI_CLK": "PC8", + "SPI_CS": "PC9" + }, + "MAX32630FTHR": { + "SPI_MOSI": "P0_5", + "SPI_MISO": "P0_6", + "SPI_CLK": "P0_4", + "SPI_CS": "P0_7" + + } + } +}
Binary file docs/pics/NUCLEO_F429ZI_wiring_with_ci_test_shield.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/fsfat_debug.h Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,88 @@ +/** @file fsfat_debug.h + * + * component debug header file. + */ + + +#ifndef __FSFAT_DEBUG +#define __FSFAT_DEBUG + +#include <stdint.h> +#include <assert.h> +#include <stdio.h> + + +/* Debug Support */ + +#define FSFAT_LOG_NONE 0 +#define FSFAT_LOG_ERR 1 +#define FSFAT_LOG_WARN 2 +#define FSFAT_LOG_NOTICE 3 +#define FSFAT_LOG_INFO 4 +#define FSFAT_LOG_DEBUG 5 +#define FSFAT_LOG_FENTRY 6 + +#define FSFAT_LOG(_fmt, ...) \ + do \ + { \ + printf(_fmt, __VA_ARGS__); \ + }while(0); + +#define noFSFAT_DEBUG +#ifdef FSFAT_DEBUG + +extern uint32_t fsfat_optDebug_g; +extern uint32_t fsfat_optLogLevel_g; + + +/* uncomment for asserts to work */ +/* #undef NDEBUG */ +// todo: port to mbedOSV3++ #include <core-util/assert.h> + +#define FSFAT_INLINE +// todo: port to mbedOSV3++ #define FSFAT_ASSERT CORE_UTIL_ASSERT +#define FSFAT_ASSERT(...) + +#define FSFAT_DBGLOG(_fmt, ...) \ + do \ + { \ + if(fsfat_optDebug_g && (fsfat_optLogLevel_g >= FSFAT_LOG_DEBUG)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + } \ + }while(0); + + +#define FSFAT_ERRLOG(_fmt, ...) \ + do \ + { \ + if(fsfat_optDebug_g && (fsfat_optLogLevel_g >= FSFAT_LOG_ERR)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + } \ + }while(0); + + +#define FSFAT_FENTRYLOG(_fmt, ...) \ + do \ + { \ + if(fsfat_optDebug_g && (fsfat_optLogLevel_g >= FSFAT_LOG_FENTRY)) \ + { \ + printf(_fmt, __VA_ARGS__); \ + } \ + }while(0); + + + + + +#else +#define FSFAT_ASSERT(_x) do { } while(0) +#define FSFAT_INLINE inline +#define FSFAT_DBGLOG(_fmt, ...) do { } while(0) +#define FSFAT_ERRLOG(_fmt, ...) do { } while(0) +#define FSFAT_FENTRYLOG(_fmt, ...) do { } while(0) +#endif /* FSFAT_DEBUG */ + + +#endif /*__FSFAT_DEBUG*/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/fsfat_test.c Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,117 @@ +/* @file fsfat_test.c + * + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * test support code implementation file. + */ + +#include "fsfat_debug.h" +#include "fsfat_test.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <inttypes.h> +#include <ctype.h> + + +#ifdef FSFAT_DEBUG +uint32_t fsfat_optDebug_g = 1; +uint32_t fsfat_optLogLevel_g = FSFAT_LOG_NONE; /*FSFAT_LOG_NONE|FSFAT_LOG_ERR|FSFAT_LOG_DEBUG|FSFAT_LOG_FENTRY; */ +#endif + +/* ruler for measuring text strings */ +/* 1 1 1 1 1 1 1 1 1 1 2 2 2 */ +/* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 */ +/* 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */ + +const uint8_t fsfat_test_byte_data_table[FSFAT_TEST_BYTE_DATA_TABLE_SIZE] = { + 0x2d, 0xf3, 0x31, 0x4c, 0x11, 0x4f, 0xde, 0x0d, 0xbd, 0xbc, 0xa6, 0x78, 0x36, 0x5c, 0x1d, 0x28, + 0x5f, 0xa9, 0x10, 0x65, 0x54, 0x45, 0x21, 0x1a, 0x88, 0xfe, 0x76, 0x45, 0xb9, 0xac, 0x65, 0x9a, + 0x34, 0x9d, 0x73, 0x10, 0xb4, 0xa9, 0x2e, 0x90, 0x95, 0x68, 0xac, 0xfe, 0xc5, 0x2d, 0x15, 0x03, + 0x34, 0x70, 0xf1, 0x1d, 0x48, 0xa1, 0xa0, 0xed, 0x5c, 0x2f, 0xf5, 0x2b, 0xb9, 0x84, 0xbb, 0x45, + 0x32, 0xdd, 0xb1, 0x33, 0x95, 0x2a, 0xbc, 0x26, 0xf0, 0x89, 0xba, 0xf4, 0xbd, 0xf9, 0x5d, 0x2e, + 0x6e, 0x11, 0xc6, 0xa7, 0x78, 0xfc, 0xc9, 0x0e, 0x6b, 0x38, 0xba, 0x14, 0x1b, 0xab, 0x4c, 0x20, + 0x91, 0xe4, 0xb0, 0xf1, 0x2b, 0x14, 0x07, 0x6b, 0xb5, 0xcd, 0xe3, 0x49, 0x75, 0xac, 0xe8, 0x98, + 0xf1, 0x58, 0x8f, 0xd9, 0xc4, 0x8f, 0x00, 0x17, 0xb5, 0x06, 0x6a, 0x33, 0xbd, 0xa7, 0x40, 0x5a, + 0xbf, 0x49, 0xf7, 0x27, 0x1b, 0x4c, 0x3e, 0x6f, 0xe3, 0x08, 0x1f, 0xfd, 0xa6, 0xd4, 0xc7, 0x5f, + 0xa4, 0xa6, 0x82, 0xad, 0x19, 0xd5, 0x5c, 0xd8, 0x3a, 0x49, 0x85, 0xc9, 0x21, 0x83, 0xf6, 0xc6, + 0x84, 0xf9, 0x76, 0x89, 0xf3, 0x2d, 0x17, 0x50, 0x97, 0x38, 0x48, 0x9a, 0xe1, 0x82, 0xcd, 0xac, + 0xa8, 0x1d, 0xd7, 0x96, 0x5e, 0xb3, 0x08, 0xa8, 0x3a, 0xc7, 0x2b, 0x05, 0xaf, 0xdc, 0x16, 0xdf, + 0x48, 0x0f, 0x2a, 0x7e, 0x3a, 0x82, 0xd7, 0x80, 0xd6, 0x49, 0x27, 0x5d, 0xe3, 0x07, 0x62, 0xb3, + 0xc3, 0x6c, 0xba, 0xb2, 0xaa, 0x9f, 0xd9, 0x03, 0x0d, 0x27, 0xa8, 0xe0, 0xd6, 0xee, 0x79, 0x4b, + 0xd6, 0x97, 0x99, 0xb7, 0x11, 0xd6, 0x0d, 0x34, 0xae, 0x99, 0x4a, 0x93, 0x95, 0xd0, 0x5a, 0x34, + 0x19, 0xa2, 0x69, 0x57, 0xcf, 0x7c, 0x3d, 0x98, 0x88, 0x5d, 0x04, 0xf2, 0xd7, 0xac, 0xa5, 0x63 +}; + + +/* @brief test utility function to delete the file identified by filename + */ +int32_t fsfat_test_delete(const char* filename) +{ + FSFAT_FENTRYLOG("%s:entered.\r\n", __func__); + return remove(filename); +} + + +/* @brief test utility function to create a file + * + * @param filename name of the file including path + * @param data data to store in file + * @param len number of bytes of data present in the data buffer. + */ +int32_t fsfat_test_create(const char* filename, const char* data, size_t len) +{ + int32_t ret = -1; + FILE *fp = NULL; + + FSFAT_FENTRYLOG("%s:entered (filename=%s, len=%d).\n", __func__, filename, (int) len); + fp = fopen(filename, "w+"); + if(fp == NULL){ + return ret; + } + ret = fwrite((const void*) data, len, 1, fp); + if(ret < 0){ + fclose(fp); + return ret; + } + fclose(fp); + return ret; +} + + +/* @brief support function for generating a kv_name + * @param name buffer to hold kv name + * @param len length of kv name to generate + * + */ +int32_t fsfat_test_filename_gen(char* name, const size_t len) +{ + size_t i; + uint32_t pos = 0; + + const char* buf = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!$-_@"; + const int buf_len = strlen(buf); + FSFAT_FENTRYLOG("%s:entered\n", __func__); + for(i = 0; i < len; i++) + { + pos = rand() % (buf_len); + name[i] = buf[pos]; + } + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/fsfat_test.h Tue Mar 20 17:35:00 2018 +0000 @@ -0,0 +1,74 @@ +/** @file fsfat_test.h + * + * mbed Microcontroller Library + * Copyright (c) 2006-2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Header file for test support data structures and function API. + */ +#ifndef __FSFAT_TEST_H +#define __FSFAT_TEST_H + +#include <stdint.h> +#include <stdlib.h> +#include <stdbool.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* Defines */ +//#define FSFAT_INIT_1_TABLE_HEAD { "a", ""} +#define FSFAT_INIT_1_TABLE_MID_NODE { "/sd/01234567.txt", "abcdefghijklmnopqrstuvwxyz"} +//#define FSFAT_INIT_1_TABLE_TAIL { "/sd/fopentst/hello/world/animal/wobbly/dog/foot/backrght.txt", "present"} +#define FSFAT_TEST_RW_TABLE_SENTINEL 0xffffffff +#define FSFAT_TEST_BYTE_DATA_TABLE_SIZE 256 +#define FSFAT_UTEST_MSG_BUF_SIZE 256 +#define FSFAT_UTEST_DEFAULT_TIMEOUT_MS 10000 +#define FSFAT_MBED_HOSTTEST_TIMEOUT 60 +#define FSFAT_MAX_FILE_BASENAME 8 +#define FSFAT_MAX_FILE_EXTNAME 3 +#define FSFAT_BUF_MAX_LENGTH 64 +#define FSFAT_FILENAME_MAX_LENGTH 255 + + +/* support macro for make string for utest _MESSAGE macros, which dont support formatted output */ +#define FSFAT_TEST_UTEST_MESSAGE(_buf, _max_len, _fmt, ...) \ + do \ + { \ + snprintf((_buf), (_max_len), (_fmt), __VA_ARGS__); \ + }while(0); + + +/* + * Structures + */ + +/* kv data for test */ +typedef struct fsfat_kv_data_t { + const char* filename; + const char* value; +} fsfat_kv_data_t; + + +extern const uint8_t fsfat_test_byte_data_table[FSFAT_TEST_BYTE_DATA_TABLE_SIZE]; + +int32_t fsfat_test_create(const char* filename, const char* data, size_t len); +int32_t fsfat_test_delete(const char* key_name); +int32_t fsfat_test_filename_gen(char* name, const size_t len); +#ifdef __cplusplus +} +#endif + +#endif /* __FSFAT_TEST_H */