High-speed access version sd-driver.
Dependents: SDBlockDevice_GR_PEACH SDBlockDevice_GR_PEACH HagridOS5
This library has modified SDBlockDevice.h based on revision "14: c7dba87" of sd-driver.
https://github.com/ARMmbed/sd-driver
Revision 0:b22a1df967cb, committed 2017-03-23
- Comitter:
- dkato
- Date:
- Thu Mar 23 08:25:54 2017 +0000
- Commit message:
- Initial commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Thu Mar 23 08:25:54 2017 +0000 @@ -0,0 +1,1271 @@ +# mbed OS SDCard Driver (sd-driver) for FAT32 Filesystem Support + + +Simon Hughes + +20170329 + +Version 1.00 + + +# 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_app.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}. + +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. + + +### 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 + +### Overview + +This section describes how to build and run an example application that +uses the POSIX File API to read and write data to SDCard. The discussion begins by +descibing how to run the example on the FRDM K64F target, but this is later +generalised to all target platforms that have the standard +Arduino form factor headers. Tthe Continuous Integration (CI) Test Shield +can be inserted into the headers to add a SDCard slot to the target. + +The example code is a modified version of the +[mbed-os-example-fat-filesystem](https://github.com/ARMmbed/mbed-os-example-fat-filesystem) example +modified for use with the sd-driver. + +The following sub-sections describe the steps for building and running the example: + +- The [Pre-Requisites](#pre-requisites) section describes the development environment used for this example. + Other similar development environments can be used. +- The [Create the Example Project](#create-the-example-project) section describes how the application project is created + by including the mbed-os and sd-driver code. +- The [Build the Example Project](#build-the-example-project) section describes how to build the example application. +- The [Insert SDCard into K64F](#insert-sdcard-into-k64f) section describes how to select a card and insert it into the + SDCard slot on the K64F. +- The [Run the Example Binary on the K64F](#run-the-example-binary-on-the-k64f) section describes how to run the + example binary on the target and verify the example has run correctly. +- The [Testing with an SDCard on Target XYZ](#testing-with-an-sdcard-on-target-xyx) section describes the use + of Continuous Integration Test Shield, which hosts an SDCard slot. By inserting the CI test shield into the + Arduino headers of an mbed target platform, the SDCard/FAT Filesystem components can be used to store data + persistently on any standard mbed target development board. + + +### <a name="pre-requisites"></a> Pre-Requisites + +To work through this example, you should have a working development environment on your machine. For example, +the following tools should be installed: + +- A compiler e.g. arm-none-eabi-gcc. +- Python 2.7.9 or later. +- [mbed Greentea](https://github.com/armmbed/greentea), the mbed OS test tool. +- Git Bash or a similar git command line tool to interact with the ARM mbed GitHub repositories. +- [mbed-cli](https://github.com/armmbed/mbed-cli), the tool used to make mbed OS application and test builds. + +For more information on how to setup a development environment, please review the documentation on the +[mbed documentation site](https://docs.mbed.com). + + +### <a name="create-the-example-project"></a> Create the Example Project + +First create the top level application directory sd_ex1 and move into it: + + simhug01@E107851:/d/demo_area$ mkdir sd_ex1 + simhug01@E107851:/d/demo_area$ cd sd_ex1 + simhug01@E107851:/d/demo_area/sd_ex1$ + +Next, perform the "mbed new" operation to download the mbed-os repository into this directory: + + simhug01@E107851:/d/demo_area/sd_ex1$ mbed new . + [mbed] Creating new program "ex_sdcard" (git) + [mbed] Adding library "mbed-os" from "https://github.com/ARMmbed/mbed-os" at branch latest + [mbed] Updating reference "mbed-os" -> "https://github.com/ARMmbed/mbed-os/#5faf4b26c5954d15c7c1cccac6498e0c690ad101" + warning: LF will be replaced by CRLF in mbed-os.lib. + The file will have its original line endings in your working directory. + (mx1_venv1) simhug01@E107851:/d/demo_area/sd_ex1$ ls -1 + mbed-os + mbed-os.lib + mbed_settings.py + (mx1_venv1) simhug01@E107851:/d/demo_area/sd_ex1$ + +Next, get add the sd-driver component to the application project: + + simhug01@E107851:/d/demo_area/sd_ex1$ mbed add sd-driver + <trace removed> + simhug01@E107851:/d/demo_area/sd_ex1$ + +Next, copy the example1.cpp file and `mbed_app.json` files from inside the sd-driver directory to the top level sd_ex1 directory: + + simhug01@E107851:/d/demo_area/sd_ex1$ cp sd-driver/features/TESTS/examples/example1.cpp . + simhug01@E107851:/d/demo_area/sd_ex1$ cp sd-driver/config/mbed_app.json . + simhug01@E107851:/d/demo_area/sd_ex1$ + +The `mbed_app.json` file specifies the SPI bus pin configuration for different targets. +The file includes a specific configuration of the K64F which is used +because the mbed compile command specifies the K64F build target. The `mbed_app.json` file +is described in more detail in the [Testing with an SDCard on Target XYZ](#testing-with-an-sdcard-on-target-xyx) section. + +### <a name="build-the-example-project"></a> Build the Example Project + +Next, build the example application: + + simhug01@E107851:/d/demo_area/sd_ex1$ mbed compile -m K64F -t GCC_ARM 2>&1 | tee build_log.txt + + +#### WARNING: "mbed new ." command and possible mbed-os sd-driver versioning incompatibilities + +If you experience problems building the example then it may mean the version +of the mbed-os repository created with the "mbed new ." command is not compatible with +the sd-driver repository version created with "mbed add sd-driver" command. This is because: + +- The "mbed new ." creates the mbed-os repository at the latest "Release" e.g. `mbed-os-5.4.0`. +- The "mbed add sd-driver" command creates the sd-driver repository at the latest version of + master i.e. the tip of master. Changes may be present that are not compatible with + the latest mbed-os release e.g. in preparation for the next release. + +This situation can be resolved by checking out compatible versions of the repositories as +described in the section [Setting mbed-os/sd-driver Repositories To Compatible Versions](#settting-repos-to-compatible-versions) + +### <a name="insert-sdcard-into-k64f"></a> Insert SDCard into K64F + +The examples and test cases have been run on a K64F with the following pre-formatted microSDHC cards: + +- Kingston 2GB mircoSDHC card. +- Kingston 8GB mircoSDHC card. +- SanDisk 16GB mircoSDHC ultra card. + +If the card requires formatting then the following procedure is known to work: + +- Insert microSD card into SD adapter in USB stick (or similar) so the microSD card can be insert into windows PC. +- Within file explorer, right click/Format on the USB drive. +- Select FAT32, 4096 cluster size, Quick Format. +- Format the drive. + +The microSD card should then be ready for use in the K64F. Insert the formatted card +into the SDCard slot on the K64F PCB. + + +### <a name="run-the-example-binary-on-the-k64f"></a> Run the Example Binary on the K64F + +Once the binary is built, copy the binary from `/d/demo_area/sd_ex1/BUILD/K64F/GCC_ARM/example1.bin` to the K64F. +After connecting a serial console and resetting the target, the following trace should be seen: + + Welcome to the filesystem example. + Opening a new file, numbers.txt. done. + Writing decimal numbers to a file (20/20) done. + Closing file. done. + Re-opening file read-only. done. + Dumping file to screen. + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + EOF. + Closing file. done. + Opening root directory. done. + Printing all filenames: + numbers.txt + Closeing root directory. done. + Filesystem Demo complete. + + +### <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_app.json` file. + +The following is an example of the `mbed_app.json` file available in the repository: + + { + "config": { + "UART_RX": "D0", + "UART_TX": "D1", + "DIO_0": "D0", + "DIO_1": "D1", + "DIO_2": "D2", + "DIO_3": "D3", + "DIO_4": "D4", + "DIO_5": "D5", + "DIO_6": "D6", + "DIO_7": "D7", + "DIO_8": "D8", + "DIO_9": "D9", + "SPI_CS": "D10", + "SPI_MOSI": "D11", + "SPI_MISO": "D12", + "SPI_CLK": "D13", + "I2C_SDA": "D14", + "I2C_SCL": "D15", + "I2C_TEMP_ADDR":"0x90", + "I2C_EEPROM_ADDR":"0xA0", + "AIN_0": "A0", + "AIN_1": "A1", + "AIN_2": "A2", + "AIN_3": "A3", + "AIN_4": "A4", + "AIN_5": "A5", + "AOUT" : "A5", + "PWM_0": "D3", + "PWM_1": "D5", + "PWM_2": "D6", + "PWM_3": "D9", + "DEBUG_MSG": 0, + "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" + }, + "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_L031K6": { + "SPI_MOSI": "SPI_MOSI", + "SPI_MISO": "SPI_MISO", + "SPI_CLK": "SPI_SCK", + "SPI_CS": "SPI_CS" + }, + "nRF51822": { + "SPI_MOSI": "p12", + "SPI_MISO": "p13", + "SPI_CLK": "p15", + "SPI_CS": "p14" + }, + "RZ_A1H": { + "SPI_MOSI": "P8_5", + "SPI_MISO": "P8_6", + "SPI_CLK": "P8_3", + "SPI_CS": "P8_4" + } + } + } + +Note the following things about the `mbed_app.json` file: + +- The `mbed_app.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_app.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. + + +### Target K64F with CI Test Shield fitted + +![alt text](docs/pics/sd_driver_k64_with_ci_test_shield.jpg "unseen title text") + +**Figure 2. The figure shows the K64F platform with the CI shield fitted.** + +The above figure shows the K64F with the v1.0.0 CI test shield fitted. Note: + +- The pins D0/D1 (top right of CI test shield) are bent sideways so as not to insert into the header. +- 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: + + + #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(p5, p6, p7, p12); // mosi, miso, sclk, cs + uint8_t block[512] = "Hello World!\n"; + + int main() + { + // call the SDBlockDevice instance initialisation method. + sd.init(); + + // Write some the data block to the device + sd.program(block, 0, 512); + + // read the data block from the device + 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_app.json` file is copied from the `sd-driver/config/mbed_app.json` to the ex_app1 directory. + +First create the top level application directory ex_app1 and move into it: + + simhug01@E107851:/d/demo_area$ mkdir ex_app1 + simhug01@E107851:/d/demo_area$ pushd ex_app1 + +Next, get a clone of public mbed OS repository in the following way: + + simhug01@E107851:/d/demo_area/ex_app1$ git clone git@github.com:/armmbed/mbed-os + <trace removed> + simhug01@E107851:/d/demo_area/ex_app1$ + +Next, get a clone of the sd-driver repository: + + simhug01@E107851:/d/demo_area/ex_app1$ git clone git@github.com:/armmbed/sd-driver + <trace removed> + simhug01@E107851:/d/demo_area/ex_app1$ + +Finally, copy the `mbed_app.json` application configuration file from `sd-driver/config/mbed_app.json` to the ex_app1 directory: + + simhug01@E107851:/d/demo_area/ex_app1$ cp sd-driver/config/mbed_app.json . + simhug01@E107851:/d/demo_area/ex_app1$ + +The `mbed_app.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: + + simhug01@E107851:/d/demo_area/ex_app1$ mbed -v test --compile -t GCC_ARM -m K64F --app-config mbed_app.json 2>&1 | tee build_tests_gcc_20161219_1007.txt + <trace removed> + simhug01@E107851:/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-FEATURES-TESTS-FILESYSTEM-BASIC + * K64F::GCC_ARM::SD-DRIVER-FEATURES-TESTS-FILESYSTEM-FOPEN + + 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: + +- `K64F::GCC_ARM::SD-DRIVER-FEATURES-TESTS-FILESYSTEM-BASIC` +- `K64F::GCC_ARM::SD-DRIVER-FEATURES-TESTS-FILESYSTEM-FOPEN` + + +The FAT32/SDCard test cases are at following locations in the source code tree: + + /d/demo_area/ex_app1/sd-driver/features/TESTS/filesystem/basic/basic.cpp + /d/demo_area/ex_app1/sd-driver/features/TESTS/filesystem/fopen/fopen.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: + + simhug01@E107851:/d/demo_area/ex_app1$ pushd mbed-os + simhug01@E107851:/d/demo_area/ex_app1$ git checkout tags/mbed-os-5.4.0 + simhug01@E107851:/d/demo_area/ex_app1$ popd + simhug01@E107851:/d/demo_area/ex_app1$ pushd sd-driver + simhug01@E107851:/d/demo_area/ex_app1$ git checkout tags/sd-driver-0.0.2-mbed-os-5.4.0 + simhug01@E107851:/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: + + simhug01@E107851:/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: + + simhug01@E107851:/d/demo_area/ex_app1$ mbedgt -VS --test-by-names=<test-name> + +The names of the tests can be listed using: + + simhug01@E107851:/d/demo_area/ex_app1$ mbedgt -VS --list + +For example, to run the basic test use: + + simhug01@E107851:/d/demo_area/ex_app1$ mbedgt -VS --test-by-names=sd-driver-features-tests-filesystem-basic 2>&1 | tee run_tests_basic.txt + +To run the fopen test use: + + simhug01@E107851:/d/demo_area/ex_app1$ mbedgt -VS --test-by-names=sd-driver-features-tests-filesystem-fopen 2>&1 | tee run_tests_fopen.txt + +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-features-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-features-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-features-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-features-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-features-tests-filesystem-fopen | FSFAT_FOPEN_TEST_06: fopen() with bad filenames (minimal). | 1 | 0 | OK | 0.1 | + | K64F-GCC_ARM | K64F | sd-driver-features-tests-filesystem-fopen | FSFAT_FOPEN_TEST_07: fopen()/errno handling. | 1 | 0 | OK | 0.07 | + | K64F-GCC_ARM | K64F | sd-driver-features-tests-filesystem-fopen | FSFAT_FOPEN_TEST_08: ferror()/clearerr()/errno handling. | 1 | 0 | OK | 0.1 | + | K64F-GCC_ARM | K64F | sd-driver-features-tests-filesystem-fopen | FSFAT_FOPEN_TEST_09: ftell() handling. | 1 | 0 | OK | 0.17 | + | K64F-GCC_ARM | K64F | sd-driver-features-tests-filesystem-fopen | FSFAT_FOPEN_TEST_10: remove() test. | 1 | 0 | OK | 1.28 | + | K64F-GCC_ARM | K64F | sd-driver-features-tests-filesystem-fopen | FSFAT_FOPEN_TEST_11: rename(). | 1 | 0 | OK | 2.3 | + | K64F-GCC_ARM | K64F | sd-driver-features-tests-filesystem-fopen | FSFAT_FOPEN_TEST_12: opendir(), readdir(), closedir() test. | 1 | 0 | OK | 3.57 | + | K64F-GCC_ARM | K64F | sd-driver-features-tests-filesystem-fopen | FSFAT_FOPEN_TEST_13: mkdir() test. | 1 | 0 | OK | 1.21 | + | K64F-GCC_ARM | K64F | sd-driver-features-tests-filesystem-fopen | FSFAT_FOPEN_TEST_14: stat() test. | 1 | 0 | OK | 1.47 | + | K64F-GCC_ARM | K64F | sd-driver-features-tests-filesystem-fopen | FSFAT_FOPEN_TEST_15: format() test. | 1 | 0 | OK | 26.12 | + | K64F-GCC_ARM | K64F | sd-driver-features-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 repository](https://github.com/ARMmbed/mbed-os). This is the main mbed OS repository. +- The [mbed-os-example-fat-filesystem repository](https://github.com/ARMmbed/mbed-os-example-fat-filesystem). + This is an example project for the mbed OS FAT filesystem. +- The [spiflash-driver repository](https://github.com/armmbed/spiflash-driver) +- The [i2ceeprom-driver repository](https://github.com/ARMmbed/i2ceeprom-driver.git) +- The [ci-test-shield repository](https://github.com/ARMmbed/ci-test-shield). This is the project describing + the mbed-os Continuous Integration test shield, together with standard tests. +- [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) + + +# Appendix 1: Getting Started with SPIFlash-Driver Example + +## Overview + +This example describes how to build and run the spiflash-driver SPIFBlockDevice examples to +read and write data to a SPI NOR flash part connected to a K64F. + +Hardware required: + +- K64F. +- CI test shield. +- SPI NOR Flash Device wired to Arduino header pins e.g. the Macronix MX25R2035F. The datasheet is available + from the [Macronix website](http://www.macronix.com/). +- Micro USB cable. + +Software required: + +- mbed CLI (with all other dependencies installed). +- ARMCC / GCC_ARM / IAR compiler. +- mbed greentea. +- git account. + +Github repos to use: + +- The [mbed OS repository](https://github.com/armmbed/mbed-os) +- The [SPI Flash Driver repository](https://github.com/armmbed/spiflash-driver) + +## Simple SPIFBlockDevice Example + +This section describes how to create an application project combining the +mbed-os and spiflash-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_app2. +- In the ex_app2 directory, the mbed-os repository is cloned. +- In the ex_app2 directory at the same level as the mbed-os directory, the spiflash-driver repository is cloned. + +First create the top level application directory ex_app2 and move into it: + + simhug01@E107851:/d/demo_area$ mkdir ex_app2 + simhug01@E107851:/d/demo_area$ pushd ex_app2 + +Next, get a clone of public mbed OS repository in the following way: + + simhug01@E107851:/d/demo_area/ex_app2$ git clone git@github.com:/armmbed/mbed-os + <trace removed> + simhug01@E107851:/d/demo_area/ex_app2$ + +Next, get a clone of the spiflash-driver repository: + + simhug01@E107851:/d/demo_area/ex_app2$ git clone git@github.com:/armmbed/spiflash-driver + <trace removed> + simhug01@E107851:/d/demo_area/ex_app2$ + +In the top level directory create the example2.cpp: + + simhug01@E107851:/d/demo_area/ex_app2$ touch example2.cpp + +Copy the [spiflash-driver example code](https://github.com/armmbed/spiflash-driver) +and paste into example2.cpp (reproduced here for convenience and corrected to build for +GCC_ARM): + + // Here's an example using the MX25R SPI flash device on the K82F + #include "mbed.h" + #include "SPIFBlockDevice.h" + + /* This is the original configuration of the SPI Flash Driver + * pins for Freescale K82F development board. We're not using + * this as we're using the CI Test Shield + */ + // Create flash device on SPI bus with PTE5 as chip select + //SPIFBlockDevice spif(PTE2, PTE4, PTE1, PTE5); + + /* This configuration of the SPI Flash Driver pins is for + * the Freescale K64F connecting the SPI pins on the + * Arduino header to the SPI NOR part. + */ + SPIFBlockDevice spif(D11, D12, D13, D10); + + + int main() { + printf("spif test\n"); + + // Initialize the SPI flash device and print the memory layout + spif.init(); + printf("spif size: %llu\n", spif.size()); + printf("spif read size: %llu\n", spif.get_read_size()); + printf("spif program size: %llu\n", spif.get_program_size()); + printf("spif erase size: %llu\n", spif.get_erase_size()); + + // Write "Hello World!" to the first block + char *buffer = (char*) malloc(spif.get_erase_size()); + sprintf(buffer, "Hello World!\n"); + spif.erase(0, spif.get_erase_size()); + spif.program(buffer, 0, spif.get_erase_size()); + + // Read back what was stored + spif.read(buffer, 0, spif.get_erase_size()); + printf("%s", buffer); + + // Deinitialize the device + spif.deinit(); + } + +Note the following modifications to the original code sample: + +- The SPI Flash Driver instance `spif` is given the pin configuration + for the SPI bus from the Arduino header pins D11, D12, D13 and D10, + as noted in the comments. +- The buffer type has been modified to char* to (see line with malloc() above). + +The application can be built with the following command: + + simhug01@E107851:/d/demo_area/ex_app2$ mbed compile -m K64F -t GCC_ARM 2>&1 | tee build_app_ex_app2_log.txt + +Once the binary is built, copy the binary from `/d/demo_area/ex_app2/BUILD/K64F/GCC_ARM/example2.bin` to the K64F. +After connecting a serial console and resetting the target, the following trace should be seen: + + spif test + spif size: 2097152 + spif read size: 1 + spif program size: 1 + spif erase size: 4096 + Hello World! + + +## Build the mbed OS Test Cases + +If you have completed the previous section "Simple SPIFBlockDevice Example" then first prepare the environment by removing the BUILD +directory and hiding or removing the example2.cpp: + + simhug01@E107851:/d/demo_area/ex_app2$ rm -fR BUILD + simhug01@E107851:/d/demo_area/ex_app2$ cp example2.cpp example2_cpp + +Build the test cases for the K64F target using the following command: + + simhug01@E107851:/d/demo_area/ex_app2$ mbed -v test --compile -t GCC_ARM -m K64F 2>&1 | tee build_tests_gcc_20170322_1007.txt + <trace removed> + simhug01@E107851:/d/demo_area/ex_app2$ + +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::MBED-OS-TESTS-STORAGE_ABSTRACTION-BASICAPI + * K64F::GCC_ARM::SPIFLASH-DRIVER-TESTS-BLOCK_DEVICE-SPIF + + + Build skips: + * K64F::GCC_ARM::MBED-OS-FEATURES-FEATURE_LWIP-TESTS-MBEDMICRO-NET-TCP_PACKET_PRESSURE + <trace removed> + +Notice the following test in the spiflash-driver tree listed above: + +- `K64F::GCC_ARM::SPIFLASH-DRIVER-TESTS-BLOCK_DEVICE-SPIF` + + +The SPIFBlockDevice test case is at following locations in the source code tree: + + /d/demo_area/ex_app2/spiflash-driver/TESTS/block_device/spif/main.cpp + +This provides an example of reading and writing data blocks to the block device interface for the SPI NOR part. + +Run the test using the following command: + + simhug01@E107851:/d/demo_area/ex_app2$ mbedgt -VS --test-by-names=spiflash-driver-tests-block_device-spif 2>&1 | tee run_test_gcc_20170322_1007.txt + +The test output should look similar to the following trace: + + (mx_env1) simhug01@E107851:/d/datastore/public/jobs/yr2017/2278/sdh_dev_mx1/ex_app5$ mbedgt -VS --test-by-names=spiflash-driver-tests-block_device-sp + if 2>&1 | tee 2278_run_test_ex_app5_br_master_time_20170322_1207_spif.txt + mbedgt: greentea test automation tool ver. 1.2.5 + mbedgt: using multiple test specifications from current directory! + using 'BUILD\tests\K64F\GCC_ARM\test_spec.json' + mbedgt: detecting connected mbed-enabled devices... + mbedgt: detected 1 device + +---------------+----------------------+-------------+-------------+--------------------------------------------------+ + | platform_name | platform_name_unique | serial_port | mount_point | target_id | + +---------------+----------------------+-------------+-------------+--------------------------------------------------+ + | K64F | K64F[0] | COM46 | E: | 0240000029304e450023500878a3001df131000097969900 | + +---------------+----------------------+-------------+-------------+--------------------------------------------------+ + mbedgt: processing target 'K64F' toolchain 'GCC_ARM' compatible platforms... (note: switch set to --parallel 1) + +---------------+----------------------+-------------+-------------+--------------------------------------------------+ + | platform_name | platform_name_unique | serial_port | mount_point | target_id | + +---------------+----------------------+-------------+-------------+--------------------------------------------------+ + | K64F | K64F[0] | COM46:9600 | E: | 0240000029304e450023500878a3001df131000097969900 | + +---------------+----------------------+-------------+-------------+--------------------------------------------------+ + mbedgt: test case filter (specified with -n option) + test filtered in 'spiflash-driver-tests-block_device-spif' + mbedgt: running 1 test for platform 'K64F' and toolchain 'GCC_ARM' + use 1 instance of execution threads for testing + mbedgt: checking for 'host_tests' directory above image directory structure + 'host_tests' directory not found: two directory levels above image path checked + mbedgt: selecting test case observer... + calling mbedhtrun: mbedhtrun -m K64F -p COM46:9600 -f "BUILD/tests/K64F/GCC_ARM/spiflash-driver/TESTS/block_device/spif/spif.bin" -d E: -C 4 - + c shell -t 0240000029304e450023500878a3001df131000097969900 + mbedgt: mbed-host-test-runner: started + [1490184626.50][HTST][INF] host test executor ver. 1.1.6 + [1490184626.50][HTST][INF] copy image onto target... + [1490184626.50][COPY][INF] Waiting up to 60 sec for '0240000029304e450023500878a3001df131000097969900' mount point (current is 'E:')... + 1 file(s) copied. + [1490184635.79][HTST][INF] starting host test process... + [1490184636.10][CONN][INF] starting connection process... + [1490184636.10][CONN][INF] notify event queue about extra 60 sec timeout for serial port pooling + [1490184636.10][CONN][INF] initializing serial port listener... + [1490184636.10][PLGN][INF] Waiting up to 60 sec for '0240000029304e450023500878a3001df131000097969900' serial port (current is 'COM46')... + [1490184636.12][HTST][INF] setting timeout to: 60 sec + [1490184636.24][SERI][INF] serial(port=COM46, baudrate=9600, timeout=0.01) + <lines deleted to save space> + [1490184649.90][CONN][INF] found KV pair in stream: {{__testcase_name;Testing read write random blocks}}, queued... + [1490184649.97][CONN][RXD] >>> Running case #1: 'Testing read write random blocks'... + [1490184650.02][CONN][INF] found KV pair in stream: {{__testcase_start;Testing read write random blocks}}, queued... + [1490184650.05][CONN][RXD] read size: 1bytes (1bytes) + [1490184650.08][CONN][RXD] program size: 1bytes (1bytes) + [1490184650.12][CONN][RXD] erase size: 4kbytes (4096bytes) + [1490184650.13][CONN][RXD] total size: 2Mbytes (2097152bytes) + [1490184650.17][CONN][RXD] test 002d000:4096... + [1490184650.36][CONN][RXD] write 002d000:4096 aad8573abd84e79e5e3684fa5519aabb... + [1490184650.50][CONN][RXD] read 002d000:4096 aad8573abd84e79e5e3684fa5519aabb... + [1490184650.56][CONN][RXD] error 002d000:4096 00000000000000000000000000000000 + [1490184650.58][CONN][RXD] test 0036000:4096... + [1490184650.77][CONN][RXD] write 0036000:4096 92fc08f5b4113047225a8d3b855e5460... + [1490184650.91][CONN][RXD] read 0036000:4096 92fc08f5b4113047225a8d3b855e5460... + [1490184650.97][CONN][RXD] error 0036000:4096 00000000000000000000000000000000 + [1490184650.99][CONN][RXD] test 00c6000:4096... + [1490184651.16][CONN][RXD] write 00c6000:4096 89a030a34b17ca3545c7b007001ef74f... + [1490184651.32][CONN][RXD] read 00c6000:4096 89a030a34b17ca3545c7b007001ef74f... + [1490184651.38][CONN][RXD] error 00c6000:4096 00000000000000000000000000000000 + [1490184651.40][CONN][RXD] test 00da000:4096... + [1490184651.60][CONN][RXD] write 00da000:4096 446fd0232a3d053af820b69c614b3662... + [1490184651.73][CONN][RXD] read 00da000:4096 446fd0232a3d053af820b69c614b3662... + [1490184651.79][CONN][RXD] error 00da000:4096 00000000000000000000000000000000 + [1490184651.81][CONN][RXD] test 0188000:4096... + [1490184652.00][CONN][RXD] write 0188000:4096 9a36d3c6d4034958cade542a9f1e22c2... + [1490184652.14][CONN][RXD] read 0188000:4096 9a36d3c6d4034958cade542a9f1e22c2... + [1490184652.20][CONN][RXD] error 0188000:4096 00000000000000000000000000000000 + [1490184652.21][CONN][RXD] test 015f000:4096... + [1490184652.42][CONN][RXD] write 015f000:4096 70f83b9cc6713736c60089a0fa55f12d... + [1490184652.55][CONN][RXD] read 015f000:4096 70f83b9cc6713736c60089a0fa55f12d... + [1490184652.61][CONN][RXD] error 015f000:4096 00000000000000000000000000000000 + [1490184652.63][CONN][RXD] test 005c000:4096... + [1490184652.82][CONN][RXD] write 005c000:4096 47a0f043fda26135877bb11c7b7016dc... + [1490184652.96][CONN][RXD] read 005c000:4096 47a0f043fda26135877bb11c7b7016dc... + [1490184653.02][CONN][RXD] error 005c000:4096 00000000000000000000000000000000 + [1490184653.04][CONN][RXD] test 0177000:4096... + [1490184653.24][CONN][RXD] write 0177000:4096 174f13941b6385d4a829f2d066a1e375... + [1490184653.37][CONN][RXD] read 0177000:4096 174f13941b6385d4a829f2d066a1e375... + [1490184653.42][CONN][RXD] error 0177000:4096 00000000000000000000000000000000 + [1490184653.45][CONN][RXD] test 0173000:4096... + [1490184653.65][CONN][RXD] write 0173000:4096 383f0ca8cc86e3225362805329e0d659... + [1490184653.78][CONN][RXD] read 0173000:4096 383f0ca8cc86e3225362805329e0d659... + [1490184653.84][CONN][RXD] error 0173000:4096 00000000000000000000000000000000 + [1490184653.86][CONN][RXD] test 01d9000:4096... + [1490184654.05][CONN][RXD] write 01d9000:4096 73f32decf08112f271131f9837b76f28... + [1490184654.19][CONN][RXD] read 01d9000:4096 73f32decf08112f271131f9837b76f28... + [1490184654.24][CONN][RXD] error 01d9000:4096 00000000000000000000000000000000 + [1490184654.31][CONN][INF] found KV pair in stream: {{__testcase_finish;Testing read write random blocks;1;0}}, queued... + [1490184654.38][CONN][RXD] >>> 'Testing read write random blocks': 1 passed, 0 failed + [1490184654.38][CONN][RXD] + [1490184654.41][CONN][RXD] >>> Test cases: 1 passed, 0 failed + [1490184654.44][CONN][INF] found KV pair in stream: {{__testcase_summary;1;0}}, queued... + [1490184654.47][CONN][INF] found KV pair in stream: {{max_heap_usage;0}}, queued... + [1490184654.48][CONN][INF] found KV pair in stream: {{end;success}}, queued... + [1490184654.48][HTST][ERR] orphan event in main phase: {{max_heap_usage;0}}, timestamp=1490184654.467000 + [1490184654.48][HTST][INF] __notify_complete(True) + [1490184654.50][CONN][INF] found KV pair in stream: {{__exit;0}}, queued... + [1490184654.51][HTST][INF] __exit(0) + [1490184654.52][HTST][INF] __exit_event_queue received + [1490184654.52][HTST][INF] test suite run finished after 4.75 sec... + [1490184654.53][CONN][INF] received special even '__host_test_finished' value='True', finishing + [1490184654.53][HTST][INF] CONN exited with code: 0 + [1490184654.53][HTST][INF] No events in queue + [1490184654.53][HTST][INF] stopped consuming events + [1490184654.53][HTST][INF] host test result() call skipped, received: True + [1490184654.53][HTST][INF] calling blocking teardown() + [1490184654.53][HTST][INF] teardown() finished + [1490184654.53][HTST][INF] {{result;success}} + mbedgt: checking for GCOV data... + mbedgt: mbed-host-test-runner: stopped and returned 'OK' + mbedgt: test on hardware with target id: 0240000029304e450023500878a3001df131000097969900 + mbedgt: test suite 'spiflash-driver-tests-block_device-spif' ......................................... OK in 28.42 sec + test case: 'Testing read write random blocks' ................................................ OK in 4.29 sec + mbedgt: test case summary: 1 pass, 0 failures + mbedgt: all tests finished! + mbedgt: shuffle seed: 0.0217829158 + mbedgt: test suite report: + +--------------+---------------+-----------------------------------------+--------+--------------------+-------------+ + | target | platform_name | test suite | result | elapsed_time (sec) | copy_method | + +--------------+---------------+-----------------------------------------+--------+--------------------+-------------+ + | K64F-GCC_ARM | K64F | spiflash-driver-tests-block_device-spif | OK | 28.42 | 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 | spiflash-driver-tests-block_device-spif | Testing read write random blocks | 1 | 0 | OK | 4.29 | + +--------------+---------------+-----------------------------------------+----------------------------------+--------+--------+--------+--------------------+ + mbedgt: test case results: 1 OK + mbedgt: completed in 35.04 sec + (mx_env1) simhug01@E107851:/d/datastore/public/jobs/yr2017/2278/sdh_dev_mx1/ex_app5$ + + + +# Appendix 2: Getting Started With The I2C EEPROM Driver + +Hardware required: + +- K64F. +- CI test shield. +- Micro USB cable. + +Software required: + +- mbed CLI (with all other dependencies installed). +- ARMCC / GCC / IAR compiler. +- mbed greentea. +- git account. + +Github repos to use: + +- The [mbed OS repository](https://github.com/armmbed/mbed-os) +- The [I2C EEPROM driver repository](https://github.com/ARMmbed/i2ceeprom-driver.git) +- The [CI test shield repository](https://github.com/ARMmbed/ci-test-shield.git) for `mbed_app.json` application configuration file. + +Steps to follow: + +- Create an empty example project in a suitable directory. Move into it. +- Download mbed OS into the example directory via `mbed new .` +- Add the I2C EEPROM driver via `mbed add i2ceeprom-driver` +- Clone the CI test shield repository to another suitable directory. Copy the mbed_app.json + from the CI test shield directory to the top level of the newly created example directory. +- Make sure the I2C pins are SDA on D14 and SCL on D15 and the I2C EEPROM slave address is + 0xA0 in the mbed_app.json that you just copied. +- Connect the target to the host machine. Run `mbed detect` to make sure the target is detected. +- Now we are ready to run the greentea tests on this target with + `mbed test -t ARM -m K64F -n i2ceeprom-driver-tests-block_device-i2cee -v` +- Note that the greentea test above makes use of the main.cpp supplied in the + `TESTS\block_device\i2cee` directory. You can customize this if required or use your own test + application via main.cpp. Be sure to have only 1 main(). If using a custom main() then you + can either have this in the TESTS directory or at the top level example directory. +- The tests should pass. If not, time to debug!! +- For other targets, please change the target ID string in the test command above to the + appropriate one. You can check the supported targets from mbed CLI using `mbed target --supported`. + +The output should be like this: + + + Building library mbed-build (K64F, ARM) + Scan: i2c_ex1 + Scan: FEATURE_BLE + Scan: FEATURE_COMMON_PAL + Scan: FEATURE_LWIP + Scan: FEATURE_UVISOR + Scan: FEATURE_ETHERNET_HOST + Scan: FEATURE_LOWPAN_BORDER_ROUTER + Scan: FEATURE_LOWPAN_HOST + Scan: FEATURE_LOWPAN_ROUTER + Scan: FEATURE_NANOSTACK + Scan: FEATURE_NANOSTACK_FULL + Scan: FEATURE_THREAD_BORDER_ROUTER + Scan: FEATURE_THREAD_END_DEVICE + Scan: FEATURE_THREAD_ROUTER + Scan: FEATURE_STORAGE + Scan: ARM + Scan: FEATURE_LWIP + Scan: FEATURE_STORAGE + Building project i2cee (K64F, ARM) + Scan: ARM + Scan: FEATURE_LWIP + Scan: FEATURE_STORAGE + Scan: i2cee + +-----------+-------+-------+-------+ + | Module | .text | .data | .bss | + +-----------+-------+-------+-------+ + | Misc | 49473 | 420 | 11628 | + | Subtotals | 49473 | 420 | 11628 | + +-----------+-------+-------+-------+ + Allocated Heap: unknown + Allocated Stack: unknown + Total Static RAM memory (data + bss): 12048 bytes + Total RAM memory (data + bss + heap + stack): 12048 bytes + Total Flash memory (text + data + misc): 49893 bytes + Image: BUILD/tests/K64F/ARM/i2ceeprom-driver/TESTS/block_device/i2cee/i2cee.bin + + + Memory map breakdown for built projects (values in Bytes): + +-------+--------+-----------+------------+-------+------+-----------+-------------+ + | name | target | toolchain | static_ram | stack | heap | total_ram | total_flash | + +-------+--------+-----------+------------+-------+------+-----------+-------------+ + | i2cee | K64F | ARM | 12048 | 0 | 0 | 12048 | 49893 | + +-------+--------+-----------+------------+-------+------+-----------+-------------+ + + + Build successes: + * K64F::ARM::I2CEEPROM-DRIVER-TESTS-BLOCK_DEVICE-I2CEE + * K64F::ARM::MBED-BUILD + mbedgt: greentea test automation tool ver. 1.2.5 + mbedgt: test specification file 'C:\Ashok\SiPWorkshop\Filesystem\i2c_ex1\BUILD\tests\K64F\ARM\test_spec.json' (specified with --test-spec option) + mbedgt: using 'C:\Ashok\SiPWorkshop\Filesystem\i2c_ex1\BUILD\tests\K64F\ARM\test_spec.json' from current directory! + mbedgt: detecting connected mbed-enabled devices... + mbedgt: detected 1 device + mbedgt: processing target 'K64F' toolchain 'ARM' compatible platforms... (note: switch set to --parallel 1) + mbedgt: test case filter (specified with -n option) + test filtered in 'i2ceeprom-driver-tests-block_device-i2cee' + mbedgt: running 1 test for platform 'K64F' and toolchain 'ARM' + mbedgt: mbed-host-test-runner: started + mbedgt: checking for GCOV data... + mbedgt: test on hardware with target id: 0240000034544e45002600048e3800285a91000097969900 + mbedgt: test suite 'i2ceeprom-driver-tests-block_device-i2cee' ....................................... OK in 11.79 sec + test case: 'Testing read write random blocks' ................................................ OK in 1.23 sec + mbedgt: test case summary: 1 pass, 0 failures + mbedgt: all tests finished! + mbedgt: shuffle seed: 0.1529521449 + mbedgt: test suite report: + +----------+---------------+-------------------------------------------+--------+--------------------+-------------+ + | target | platform_name | test suite | result | elapsed_time (sec) | copy_method | + +----------+---------------+-------------------------------------------+--------+--------------------+-------------+ + | K64F-ARM | K64F | i2ceeprom-driver-tests-block_device-i2cee | OK | 11.79 | 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-ARM | K64F | i2ceeprom-driver-tests-block_device-i2cee | Testing read write random blocks | 1 | 0 | OK | 1.23 | + +----------+---------------+-------------------------------------------+----------------------------------+--------+--------+--------+--------------------+ + mbedgt: test case results: 1 OK + mbedgt: completed in 13.30 sec
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/config/mbed_app.json Thu Mar 23 08:25:54 2017 +0000 @@ -0,0 +1,130 @@ +{ + "config": { + "UART_RX": "D0", + "UART_TX": "D1", + "DIO_0": "D0", + "DIO_1": "D1", + "DIO_2": "D2", + "DIO_3": "D3", + "DIO_4": "D4", + "DIO_5": "D5", + "DIO_6": "D6", + "DIO_7": "D7", + "DIO_8": "D8", + "DIO_9": "D9", + "SPI_CS": "D10", + "SPI_MOSI": "D11", + "SPI_MISO": "D12", + "SPI_CLK": "D13", + "I2C_SDA": "D14", + "I2C_SCL": "D15", + "I2C_TEMP_ADDR":"0x90", + "I2C_EEPROM_ADDR":"0xA0", + "AIN_0": "A0", + "AIN_1": "A1", + "AIN_2": "A2", + "AIN_3": "A3", + "AIN_4": "A4", + "AIN_5": "A5", + "AOUT" : "A5", + "PWM_0": "D3", + "PWM_1": "D5", + "PWM_2": "D6", + "PWM_3": "D9", + "DEBUG_MSG": 0, + "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" + }, + "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_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" + }, + "RZ_A1H": { + "SPI_MOSI": "P8_5", + "SPI_MISO": "P8_6", + "SPI_CLK": "P8_3", + "SPI_CS": "P8_4" + } + } +}
Binary file docs/pics/sd_driver_k64_with_ci_test_shield.jpg has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/features/TESTS/examples/example1/example1.cpp Thu Mar 23 08:25:54 2017 +0000 @@ -0,0 +1,80 @@ +#include "mbed.h" +#include "FATFileSystem.h" +#include "SDBlockDevice.h" +#include <stdio.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" + + +SDBlockDevice sd(MBED_CONF_APP_SPI_MOSI, MBED_CONF_APP_SPI_MISO, MBED_CONF_APP_SPI_CLK, MBED_CONF_APP_SPI_CS); +FATFileSystem fs("sd", &sd); + +void return_error(int ret_val){ + if (ret_val) + printf("Failure. %d\n", ret_val); + else + printf("done.\n"); +} + +void errno_error(void* ret_val){ + if (ret_val == NULL) + printf(" Failure. %d \n", errno); + else + printf(" done.\n"); +} + +int main() +{ + int error = 0; + printf("Welcome to the filesystem example.\n"); + + printf("Opening a new file, numbers.txt."); + FILE* fd = fopen("/sd/numbers.txt", "w+"); + errno_error(fd); + + for (int i = 0; i < 20; i++){ + printf("Writing decimal numbers to a file (%d/20)\r", i); + fprintf(fd, "%d\n", i); + } + printf("Writing decimal numbers to a file (20/20) done.\n"); + + printf("Closing file."); + fclose(fd); + printf(" done.\n"); + + printf("Re-opening file read-only."); + fd = fopen("/sd/numbers.txt", "r"); + errno_error(fd); + + printf("Dumping file to screen.\n"); + char buff[16] = {0}; + while (!feof(fd)){ + int size = fread(&buff[0], 1, 15, fd); + fwrite(&buff[0], 1, size, stdout); + } + printf("EOF.\n"); + + printf("Closing file."); + fclose(fd); + printf(" done.\n"); + + printf("Opening root directory."); + DIR* dir = opendir("/sd/"); + errno_error(fd); + + struct dirent* de; + printf("Printing all filenames:\n"); + while((de = readdir(dir)) != NULL){ + printf(" %s\n", &(de->d_name)[0]); + } + + printf("Closeing root directory. "); + error = closedir(dir); + return_error(error); + printf("Filesystem Demo complete.\n"); + + while (true) {} +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/features/TESTS/filesystem/basic/basic.cpp Thu Mar 23 08:25:54 2017 +0000 @@ -0,0 +1,923 @@ +/* + * 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) + +#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_APP_SPI_MOSI, MBED_CONF_APP_SPI_MISO, MBED_CONF_APP_SPI_CLK, MBED_CONF_APP_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__, 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__, 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__, 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; + char *str = FSFAT_BASIC_TEST_05_TEST_STRING; + char *buffer = (char *)malloc(sizeof(unsigned char) * strlen(FSFAT_BASIC_TEST_05_TEST_STRING)); + int str_len = strlen(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 == 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; + char *str = FSFAT_BASIC_TEST_05_TEST_STRING; + char *buffer = (char *)malloc(sizeof(unsigned char) * strlen(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 == 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(); + 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); + 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(); + 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); + 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; + } + 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); + 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); + fsfat_basic_timer.reset(); + return true; +} + + +bool fsfat_basic_test_sf_file_read_stdio(const char *filename, const int kib_rw) +{ + bool result = true; + 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); + 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); + + 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; + } + 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); + 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); + 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); + 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); + 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; + } + 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 /* defined(DEVICE_SPI) && defined(MBED_CONF_APP_FSFAT_SDCARD_INSTALLED) */ + +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/features/TESTS/filesystem/fopen/fopen.cpp Thu Mar 23 08:25:54 2017 +0000 @@ -0,0 +1,1530 @@ +/* + * 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" + +/* This is needed for stat() test, but is not available on ARMCC. + * The following checks whether GCC_ARM compiler is being used because: + * - both the ARMCC compiler and the GCC_ARM compile define __GNUC__. + * - only the ARMCC compiler defines __ARMCC_VERSION. + * - hence if __ARMCC_VERSION is not defined and __GNUC__ is defined, it must be GCC_ARM. */ +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) +#include <sys/stat.h> +#endif +using namespace utest::v1; + +/// @cond FSFAT_DOXYGEN_DISABLE +#ifdef FSFAT_DEBUG +#define FSFAT_FOPEN_GREENTEA_TIMEOUT_S 3000 +#else +#define FSFAT_FOPEN_GREENTEA_TIMEOUT_S 1000 +#endif +/// @endcond + + +/* DEVICE_SPI + * This symbol is defined in targets.json if the target has a SPI interface, which is required for SDCard support. + * + * MBED_CONF_APP_FSFAT_SDCARD_INSTALLED + * For testing purposes, an SDCard must be installed on the target for the test cases in this file to succeed. + * If the target has an SD card installed then the MBED_CONF_APP_FSFAT_SDCARD_INSTALLED will be generated + * from the mbed_app.json, which includes the line + * { + * "config": { + * "UART_RX": "D0", + * <<< lines removed >>> + * "DEVICE_SPI": 1, + * "FSFAT_SDCARD_INSTALLED": 1 + * }, + * <<< lines removed >>> + */ +#if defined(DEVICE_SPI) && defined(MBED_CONF_APP_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_APP_SPI_MOSI, MBED_CONF_APP_SPI_MISO, MBED_CONF_APP_SPI_CLK, MBED_CONF_APP_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) && 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); + + /* check ferror() returns non-zero indicating there is an error + * Note ARMCC appears to fault when null FILE* is supplied to ferror() */ + ret = ferror(f); + FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: ferror() did not return non-zero value when error exists (filename=%s, ret=%d).\n", __func__, filename, (int) ret); + TEST_ASSERT_MESSAGE(ret != 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 /* defined(DEVICE_SPI) && defined(MBED_CONF_APP_FSFAT_SDCARD_INSTALLED) */ + + +/// @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/features/TESTS/filesystem/spif/main.cpp Thu Mar 23 08:25:54 2017 +0000 @@ -0,0 +1,180 @@ +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#include "SPIFBlockDevice.h" +#include <stdlib.h> + +using namespace utest::v1; + +#ifndef SPIF_INSTALLED +// todo: sdh +//#define SPIF_INSTALLED defined(TARGET_K82F) +#define SPIF_INSTALLED defined(TARGET_K64F) +#endif + +#if !SPIF_INSTALLED +#error [NOT_SUPPORTED] SPIF Required +#endif + +/* +#if defined(TARGET_K82F) +#define TEST_PINS PTE2, PTE4, PTE1, PTE5 +#define TEST_FREQ 40000000 +#else +#define TEST_PINS D11, D12, D13, D10 +#define TEST_FREQ 1000000 +#endif +*/ +#if defined(TARGET_K64F) +#define TEST_PINS PTD2, PTD3, PTD1, PTD0 +//#define TEST_FREQ 40000000 +//#define TEST_FREQ 1000000 +#define TEST_FREQ 50000 +#else +#error "no TEST_PINS defined" +#endif + + +#define TEST_BLOCK_COUNT 10 +#define TEST_ERROR_MASK 16 + +/* +const struct { + const char *name; + bd_size_t (BlockDevice::*method)() const; +} ATTRS[] = { + {"read size", &BlockDevice::get_read_size}, + {"program size", &BlockDevice::get_program_size}, + {"erase size", &BlockDevice::get_erase_size}, + {"total size", &BlockDevice::size}, +}; +*/ +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}, +}; + +// todo: fix me +void test_read_write() { + SPIFBlockDevice bd(TEST_PINS, TEST_FREQ); + + int err = bd.init(); + TEST_ASSERT_EQUAL(0, err); + + for (unsigned a = 0; a < sizeof(ATTRS)/sizeof(ATTRS[0]); a++) { + static const char *prefixes[] = {"", "k", "M", "G"}; + for (int i = 3; i >= 0; i--) { + bd_size_t size = (bd.*ATTRS[a].method)(); + if (size >= (1ULL << 10*i)) { + printf("%s: %llu%sbytes (%llubytes)\n", + ATTRS[a].name, size >> 10*i, prefixes[i], size); + break; + } + } + } + + + bd_size_t block_size = bd.get_erase_size(); + uint8_t *write_block = new uint8_t[block_size]; + uint8_t *read_block = new uint8_t[block_size]; + uint8_t *error_mask = new uint8_t[TEST_ERROR_MASK]; + +#if ! defined(__ARMCC_VERSION) && defined(__GNUC__) + unsigned addrwidth = ceil(log(bd.size()-1) / log(16))+1; +#else + unsigned addrwidth = 0; +#endif + + for (int b = 0; b < TEST_BLOCK_COUNT; b++) { + // Find a random block + bd_addr_t block = (rand()*block_size) % bd.size(); + + // Use next random number as temporary seed to keep + // the address progressing in the pseudorandom sequence + unsigned seed = rand(); + + // Fill with random sequence + srand(seed); + for (bd_size_t i = 0; i < block_size; i++) { + write_block[i] = 0xff & rand(); + } + + // Write, sync, and read the block + printf("test %0*llx:%llu...\n", addrwidth, block, block_size); + + err = bd.program(write_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + printf("write %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", write_block[i]); + } + printf("...\n"); + + err = bd.read(read_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + printf("read %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", read_block[i]); + } + printf("...\n"); + + + //for (int i = 0; i < block_size; i++) { + // printf("%02x:%02x:%s\n", write_block[i], read_block[i], write_block[i] == read_block[i] ? "1" : "0"); + //} + + // Find error mask for debugging + memset(error_mask, 0, TEST_ERROR_MASK); + bd_size_t error_scale = block_size / (TEST_ERROR_MASK*8); + + srand(seed); + for (bd_size_t i = 0; i < TEST_ERROR_MASK*8; i++) { + for (bd_size_t j = 0; j < error_scale; j++) { + if ((0xff & rand()) != read_block[i*error_scale + j]) { + error_mask[i/8] |= 1 << (i%8); + } + } + } + + printf("error %0*llx:%llu ", addrwidth, block, block_size); + for (int i = 0; i < 16; i++) { + printf("%02x", error_mask[i]); + } + printf("\n"); + + // Check that the data was unmodified + srand(seed); + for (bd_size_t i = 0; i < block_size; i++) { + TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]); + } + } + + err = bd.deinit(); + TEST_ASSERT_EQUAL(0, err); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) { + GREENTEA_SETUP(30, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Testing read write random blocks", test_read_write), +}; + +Specification specification(test_setup, cases); + +int main() { + return !Harness::run(specification); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/features/filesystem/sd/SDBlockDevice.cpp Thu Mar 23 08:25:54 2017 +0000 @@ -0,0 +1,600 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* Introduction + * ------------ + * SD and MMC cards support a number of interfaces, but common to them all + * is one based on SPI. This is the one I'm implmenting because it means + * it is much more portable even though not so performant, and we already + * have the mbed SPI Interface! + * + * 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 SPI interface mode is selected by + * asserting CS low and sending the reset command (CMD0). The card will + * respond with a (R1) response. + * + * 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" + +#define SD_COMMAND_TIMEOUT 5000 + +#define SD_DBG 0 + +#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 */ + +SDBlockDevice::SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs) + : _spi(mosi, miso, sclk), _cs(cs), _is_initialized(0) +{ + _cs = 1; + + // Set default to 100kHz for initialisation and 1MHz for data transfer + _init_sck = 100000; + _transfer_sck = 1000000; +} + +SDBlockDevice::~SDBlockDevice() +{ + if (_is_initialized) { + deinit(); + } +} + +#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 +// - v1.x Standard Capacity +// - v2.x Standard Capacity +// - v2.x High Capacity +// - Not recognised as an SD Card +#define SDCARD_FAIL 0 +#define SDCARD_V1 1 +#define SDCARD_V2 2 +#define SDCARD_V2HC 3 + +int SDBlockDevice::_initialise_card() +{ + _dbg = SD_DBG; + // Set to SCK for initialisation, and clock card with cs = 1 + _spi.lock(); + _spi.frequency(_init_sck); + _cs = 1; + for (int i = 0; i < 16; i++) { + _spi.write(0xFF); + } + _spi.unlock(); + + // send CMD0, should return with all zeros except IDLE STATE set (bit 0) + if (_cmd(0, 0) != R1_IDLE_STATE) { + debug_if(_dbg, "No disk, or could not put SD card in to SPI idle state\n"); + return SD_BLOCK_DEVICE_ERROR_NO_DEVICE; + } + + // send CMD8 to determine whther it is ver 2.x + int r = _cmd8(); + if (r == R1_IDLE_STATE) { + return _initialise_card_v2(); + } else if (r == (R1_IDLE_STATE | R1_ILLEGAL_COMMAND)) { + return _initialise_card_v1(); + } else { + debug_if(_dbg, "Not in idle state after sending CMD8 (not an SD card?)\n"); + return BD_ERROR_DEVICE_ERROR; + } +} + +int SDBlockDevice::_initialise_card_v1() +{ + for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) { + _cmd(55, 0); + if (_cmd(41, 0) == 0) { + _block_size = 512; + debug_if(_dbg, "\n\rInit: SEDCARD_V1\n\r"); + return BD_ERROR_OK; + } + } + + debug_if(_dbg, "Timeout waiting for v1.x card\n"); + return BD_ERROR_DEVICE_ERROR; +} + +int SDBlockDevice::_initialise_card_v2() +{ + for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) { + wait_ms(50); + _cmd58(); + _cmd(55, 0); + if (_cmd(41, 0x40000000) == 0) { + _cmd58(); + debug_if(_dbg, "\n\rInit: SDCARD_V2\n\r"); + _block_size = 1; + return BD_ERROR_OK; + } + } + + debug_if(_dbg, "Timeout waiting for v2.x card\n"); + return BD_ERROR_DEVICE_ERROR; +} + +int SDBlockDevice::init() +{ + _lock.lock(); + int err = _initialise_card(); + _is_initialized = (err == BD_ERROR_OK); + if (!_is_initialized) { + debug_if(_dbg, "Fail to initialize card\n"); + _lock.unlock(); + return err; + } + debug_if(_dbg, "init card = %d\n", _is_initialized); + _sectors = _sd_sectors(); + + // Set block length to 512 (CMD16) + if (_cmd(16, 512) != 0) { + debug_if(_dbg, "Set 512-byte block timed out\n"); + _lock.unlock(); + return BD_ERROR_DEVICE_ERROR; + } + + // Set SCK for data transfer + _spi.frequency(_transfer_sck); + _lock.unlock(); + return BD_ERROR_OK; +} + +int SDBlockDevice::deinit() +{ + 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.lock(); + if (!_is_initialized) { + _lock.unlock(); + return SD_BLOCK_DEVICE_ERROR_NO_INIT; + } + + const uint8_t *buffer = static_cast<const uint8_t*>(b); + while (size > 0) { + bd_addr_t block = addr / 512; + // set write address for single block (CMD24) + if (_cmd(24, block * _block_size) != 0) { + _lock.unlock(); + return BD_ERROR_DEVICE_ERROR; + } + + // send the data block + _write(buffer, 512); + buffer += 512; + addr += 512; + size -= 512; + } + _lock.unlock(); + return 0; +} + +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.lock(); + if (!_is_initialized) { + _lock.unlock(); + return SD_BLOCK_DEVICE_ERROR_PARAMETER; + } + + uint8_t *buffer = static_cast<uint8_t *>(b); + while (size > 0) { + bd_addr_t block = addr / 512; + // set read address for single block (CMD17) + if (_cmd(17, block * _block_size) != 0) { + _lock.unlock(); + return BD_ERROR_DEVICE_ERROR; + } + + // receive the data + _read(buffer, 512); + buffer += 512; + addr += 512; + size -= 512; + } + _lock.unlock(); + return 0; +} + +int SDBlockDevice::erase(bd_addr_t addr, bd_size_t size) +{ + return 0; +} + +bd_size_t SDBlockDevice::get_read_size() const +{ + return 512; +} + +bd_size_t SDBlockDevice::get_program_size() const +{ + return 512; +} + +bd_size_t SDBlockDevice::get_erase_size() const +{ + return 512; +} + +bd_size_t SDBlockDevice::size() const +{ + bd_size_t sectors = 0; + if(_is_initialized) { + sectors = _sectors; + } + return 512*sectors; +} + +void SDBlockDevice::debug(bool dbg) +{ + _dbg = dbg; +} + + +// PRIVATE FUNCTIONS +int SDBlockDevice::_cmd(int cmd, int arg) { + _spi.lock(); + _cs = 0; + + // send a command + _spi.write(0x40 | cmd); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if (!(response & 0x80)) { + _cs = 1; + _spi.write(0xFF); + _spi.unlock(); + return response; + } + } + _cs = 1; + _spi.write(0xFF); + _spi.unlock(); + return -1; // timeout +} +int SDBlockDevice::_cmdx(int cmd, int arg) { + _spi.lock(); + _cs = 0; + + // send a command + _spi.write(0x40 | cmd); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if (!(response & 0x80)) { + _cs = 1; + _spi.unlock(); + return response; + } + } + _cs = 1; + _spi.write(0xFF); + _spi.unlock(); + return -1; // timeout +} + + +int SDBlockDevice::_cmd58() { + _spi.lock(); + _cs = 0; + int arg = 0; + + // send a command + _spi.write(0x40 | 58); + _spi.write(arg >> 24); + _spi.write(arg >> 16); + _spi.write(arg >> 8); + _spi.write(arg >> 0); + _spi.write(0x95); + + // wait for the repsonse (response[7] == 0) + for (int i = 0; i < SD_COMMAND_TIMEOUT; i++) { + int response = _spi.write(0xFF); + if (!(response & 0x80)) { + int ocr = _spi.write(0xFF) << 24; + ocr |= _spi.write(0xFF) << 16; + ocr |= _spi.write(0xFF) << 8; + ocr |= _spi.write(0xFF) << 0; + _cs = 1; + _spi.write(0xFF); + _spi.unlock(); + return response; + } + } + _cs = 1; + _spi.write(0xFF); + _spi.unlock(); + return -1; // timeout +} + +int SDBlockDevice::_cmd8() { + _spi.lock(); + _cs = 0; + + // send a command + _spi.write(0x40 | 8); // CMD8 + _spi.write(0x00); // reserved + _spi.write(0x00); // reserved + _spi.write(0x01); // 3.3v + _spi.write(0xAA); // check pattern + _spi.write(0x87); // crc + + // wait for the repsonse (response[7] == 0) + for (int i = 0; i < SD_COMMAND_TIMEOUT * 1000; i++) { + char response[5]; + response[0] = _spi.write(0xFF); + if (!(response[0] & 0x80)) { + for (int j = 1; j < 5; j++) { + response[i] = _spi.write(0xFF); + } + _cs = 1; + _spi.write(0xFF); + _spi.unlock(); + return response[0]; + } + } + _cs = 1; + _spi.write(0xFF); + _spi.unlock(); + return -1; // timeout +} + +int SDBlockDevice::_read(uint8_t *buffer, uint32_t length) { + _spi.lock(); + _cs = 0; + + // read until start byte (0xFF) + while (_spi.write(0xFF) != 0xFE); + + // read data + for (uint32_t i = 0; i < length; i++) { + buffer[i] = _spi.write(0xFF); + } + _spi.write(0xFF); // checksum + _spi.write(0xFF); + + _cs = 1; + _spi.write(0xFF); + _spi.unlock(); + return 0; +} + +int SDBlockDevice::_write(const uint8_t*buffer, uint32_t length) { + _spi.lock(); + _cs = 0; + + // indicate start of block + _spi.write(0xFE); + + // write the data + for (uint32_t i = 0; i < length; i++) { + _spi.write(buffer[i]); + } + + // write the checksum + _spi.write(0xFF); + _spi.write(0xFF); + + // check the response token + if ((_spi.write(0xFF) & 0x1F) != 0x05) { + _cs = 1; + _spi.write(0xFF); + _spi.unlock(); + return 1; + } + + // wait for write to finish + while (_spi.write(0xFF) == 0); + + _cs = 1; + _spi.write(0xFF); + _spi.unlock(); + return 0; +} + +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; +} + +uint32_t SDBlockDevice::_sd_sectors() { + uint32_t c_size, c_size_mult, read_bl_len; + uint32_t block_len, mult, blocknr, capacity; + uint32_t hc_c_size; + uint32_t blocks; + + // CMD9, Response R2 (R1 byte + 16-byte block read) + if (_cmdx(9, 0) != 0) { + debug_if(_dbg, "Didn't get a response from the disk\n"); + return 0; + } + + uint8_t csd[16]; + if (_read(csd, 16) != 0) { + debug_if(_dbg, "Couldn't read csd response from disk\n"); + return 0; + } + + // csd_structure : csd[127:126] + // c_size : csd[73:62] + // c_size_mult : csd[49:47] + // read_bl_len : csd[83:80] - the *maximum* read block length + + int csd_structure = ext_bits(csd, 127, 126); + + switch (csd_structure) { + case 0: + _block_size = 512; + c_size = ext_bits(csd, 73, 62); + c_size_mult = ext_bits(csd, 49, 47); + read_bl_len = ext_bits(csd, 83, 80); + + block_len = 1 << read_bl_len; + mult = 1 << (c_size_mult + 2); + blocknr = (c_size + 1) * mult; + capacity = blocknr * block_len; + blocks = capacity / 512; + debug_if(_dbg, "\n\rSDBlockDevice\n\rc_size: %d \n\rcapacity: %ld \n\rsectors: %lld\n\r", c_size, capacity, blocks); + break; + + case 1: + _block_size = 1; + hc_c_size = ext_bits(csd, 63, 48); + blocks = (hc_c_size+1)*1024; + debug_if(_dbg, "\n\rSDHC Card \n\rhc_c_size: %d\n\rcapacity: %lld \n\rsectors: %lld\n\r", hc_c_size, blocks*512, blocks); + break; + + default: + debug_if(_dbg, "CSD struct unsupported\r\n"); + return 0; + }; + return blocks; +} + +#endif /* DEVICE_SPI */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/features/filesystem/sd/SDBlockDevice.h Thu Mar 23 08:25:54 2017 +0000 @@ -0,0 +1,175 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2012 ARM Limited + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#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" + +/** 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); + 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); + + /** Erase blocks on a block device + * + * The state of an erased block is undefined until it has been programmed + * + * @param addr Address of block to begin erasing + * @param size Size to erase in bytes, must be a multiple of erase block size + * @return 0 on success, negative error code on failure + */ + virtual int erase(bd_addr_t addr, bd_size_t size); + + /** Get the size of a readable block + * + * @return Size of a readable block in bytes + */ + virtual bd_size_t get_read_size() const; + + /** Get the size of a programable block + * + * @return Size of a programable block in bytes + * @note Must be a multiple of the read size + */ + virtual bd_size_t get_program_size() const; + + /** Get the size of a eraseable block + * + * @return Size of a eraseable block in bytes + * @note Must be a multiple of the program size + */ + virtual bd_size_t get_erase_size() const; + + /** Get the total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual bd_size_t size() const; + + /** Enable or disable debugging + * + * @param State of debugging + */ + virtual void debug(bool dbg); + +protected: + class SPI_HS : public SPI { + public: + SPI_HS(PinName mosi, PinName miso, PinName sclk, PinName ssel=NC) : SPI(mosi, miso, sclk, ssel) {} + + virtual int write(int value) { + aquire(); + int ret = spi_master_write(&_spi, value); + return ret; + } + protected: + virtual void aquire(void) { + if (_owner != this) { + spi_format(&_spi, _bits, _mode, 0); + spi_frequency(&_spi, _hz); + _owner = this; + } + } + }; + int _cmd(int cmd, int arg); + int _cmdx(int cmd, int arg); + int _cmd8(); + int _cmd58(); + int _initialise_card(); + int _initialise_card_v1(); + int _initialise_card_v2(); + + int _read(uint8_t * buffer, uint32_t length); + int _write(const uint8_t *buffer, uint32_t length); + uint32_t _sd_sectors(); + uint32_t _sectors; + + uint32_t _init_sck; + uint32_t _transfer_sck; + + SPI_HS _spi; + DigitalOut _cs; + unsigned _block_size; + bool _is_initialized; + bool _dbg; + Mutex _lock; +}; + + +#endif /* DEVICE_SPI */ + +#endif /* MBED_SD_BLOCK_DEVICE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/features/filesystem/spif/README.md Thu Mar 23 08:25:54 2017 +0000 @@ -0,0 +1,43 @@ +# SPI Flash Driver + +Block device driver for NOR based SPI flash devices that support SFDP. + +NOR based SPI flash supports byte-sized read and writes, with an erase size of around 4kbytes. An erase sets a block to all 1s, with successive writes clearing set bits. + +More info on NOR flash can be found on wikipedia: +https://en.wikipedia.org/wiki/Flash_memory#NOR_memories + +``` cpp +// Here's an example using the MX25R SPI flash device on the K82F +#include "mbed.h" +#include "SPIFBlockDevice.h" + +// Create flash device on SPI bus with PTE5 as chip select +SPIFBlockDevice spif(PTE2, PTE4, PTE1, PTE5); + +int main() +{ + printf("spif test\n"); + + // Initialize the SPI flash device and print the memory layout + spif.init(); + printf("spif size: %llu\n", spif.size()); + printf("spif read size: %llu\n", spif.get_read_size()); + printf("spif program size: %llu\n", spif.get_program_size()); + printf("spif erase size: %llu\n", spif.get_erase_size()); + + // Write "Hello World!" to the first block + uint8_t *buffer = malloc(spif.get_erase_size()); + sprintf(buffer, "Hello World!\n"); + spif.erase(0, spif.get_erase_size()); + spif.program(buffer, 0, spif.get_erase_size()); + + // Read back what was stored + spif.read(buffer, 0, spif.get_erase_size()); + printf("%s", buffer); + + // Deinitialize the device + spif.deinit(); +} +``` +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/features/filesystem/spif/SPIFBlockDevice.cpp Thu Mar 23 08:25:54 2017 +0000 @@ -0,0 +1,348 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "SPIFBlockDevice.h" + + +// Read/write/erase sizes +#define SPIF_READ_SIZE 1 +#define SPIF_PROG_SIZE 1 +#define SPIF_SE_SIZE 4096 +#define SPIF_TIMEOUT 10000 + +// Debug available +#define SPIF_DEBUG 0 + +#define SPIF_BLOCK_DEVICE_ERROR_WOULD_BLOCK -5001 /*!< operation would block */ +#define SPIF_BLOCK_DEVICE_ERROR_UNSUPPORTED -5002 /*!< unsupported operation */ +#define SPIF_BLOCK_DEVICE_ERROR_PARAMETER -5003 /*!< invalid parameter */ +#define SPIF_BLOCK_DEVICE_ERROR_NO_INIT -5004 /*!< uninitialized */ +#define SPIF_BLOCK_DEVICE_ERROR_NO_DEVICE -5005 /*!< device is missing or not connected */ +#define SPIF_BLOCK_DEVICE_ERROR_WRITE_PROTECTED -5006 /*!< write protected */ + +// MX25R Series Register Command Table. +enum ops { + SPIF_NOP = 0x00, // No operation + SPIF_READ = 0x03, // Read data + SPIF_PROG = 0x02, // Program data + SPIF_SE = 0x20, // 4KB Sector Erase + SPIF_CE = 0xc7, // Chip Erase + SPIF_SFDP = 0x5a, // Read SFDP + SPIF_WREN = 0x06, // Write Enable + SPIF_WRDI = 0x04, // Write Disable + SPIF_RDSR = 0x05, // Read Status Register + SPIF_RDID = 0x9f, // Read Manufacturer and JDEC Device ID +}; + +// Status register from RDSR +// [- stuff -| wel | wip ] +// [- 6 -| 1 | 1 ] +#define SPIF_WEL 0x2 +#define SPIF_WIP 0x1 + + +SPIFBlockDevice::SPIFBlockDevice( + PinName mosi, PinName miso, PinName sclk, PinName cs, int freq) + : _spi(mosi, miso, sclk), _cs(cs), _size(0) +{ + _cs = 1; + _spi.frequency(freq); +} + +int SPIFBlockDevice::init() +{ + // Check for vendor specific hacks, these should move into more general + // handling when possible. RDID is not used to verify a device is attached. + uint8_t id[3]; + _cmdread(SPIF_RDID, 0, 3, 0x0, id); + + switch (id[0]) { + case 0xbf: + // SST devices come preset with block protection + // enabled for some regions, issue gbpu instruction to clear + _wren(); + _cmdwrite(0x98, 0, 0, 0x0, NULL); + break; + } + + // Check that device is doing ok + int err = _sync(); + if (err) { + return SPIF_BLOCK_DEVICE_ERROR_NO_DEVICE; + } + + // Check JEDEC serial flash discoverable parameters for device + // specific info + uint8_t header[16]; + _cmdread(SPIF_SFDP, 4, 16, 0x0, header); + + // Verify SFDP signature for sanity + // Also check that major/minor version is acceptable + if (!(memcmp(&header[0], "SFDP", 4) == 0 && header[5] == 1)) { + return BD_ERROR_DEVICE_ERROR; + } + + // The SFDP spec indicates the standard table is always at offset 0 + // in the parameter headers, we check just to be safe + if (!(header[8] == 0 && header[10] == 1)) { + return BD_ERROR_DEVICE_ERROR; + } + + // Parameter table pointer, spi commands are BE, SFDP is LE, + // also sfdp command expects extra read wait byte + uint32_t table_addr = ( + (header[14] << 24) | + (header[13] << 16) | + (header[12] << 8 )); + uint8_t table[8]; + _cmdread(SPIF_SFDP, 4, 8, table_addr, table); + + // Check erase size, currently only supports 4kbytes + // TODO support erase size != 4kbytes? + // TODO support other erase opcodes from the sector descriptions + if ((table[0] & 0x3) != 0x1 || table[1] != SPIF_SE) { + return BD_ERROR_DEVICE_ERROR; + } + + // Check address size, currently only supports 3byte addresses + // TODO support address > 3bytes? + // TODO check for devices larger than 2Gbits? + if ((table[2] & 0x4) != 0 || (table[7] & 0x80) != 0) { + return BD_ERROR_DEVICE_ERROR; + } + + // Get device density, stored as size in bits - 1 + uint32_t density = ( + (table[7] << 24) | + (table[6] << 16) | + (table[5] << 8 ) | + (table[4] << 0 )); + _size = (density/8) + 1; + + return 0; +} + +int SPIFBlockDevice::deinit() +{ + // Latch write disable just to keep noise + // from changing the device + _cmdwrite(SPIF_WRDI, 0, 0, 0x0, NULL); + + return 0; +} + +void SPIFBlockDevice::_cmdread( + uint8_t op, uint32_t addrc, uint32_t retc, + uint32_t addr, uint8_t *rets) +{ + _cs = 0; + _spi.write(op); + + for (uint32_t i = 0; i < addrc; i++) { + _spi.write(0xff & (addr >> 8*(addrc-1 - i))); + } + + for (uint32_t i = 0; i < retc; i++) { + rets[i] = _spi.write(0); + } + _cs = 1; + + if (SPIF_DEBUG) { + printf("spif <- %02x", op); + for (uint32_t i = 0; i < addrc; i++) { + if (i < addrc) { + printf("%02lx", 0xff & (addr >> 8*(addrc-1 - i))); + } else { + printf(" "); + } + } + printf(" "); + for (uint32_t i = 0; i < 16 && i < retc; i++) { + printf("%02x", rets[i]); + } + if (retc > 16) { + printf("..."); + } + printf("\n"); + } +} + +void SPIFBlockDevice::_cmdwrite( + uint8_t op, uint32_t addrc, uint32_t argc, + uint32_t addr, const uint8_t *args) +{ + _cs = 0; + _spi.write(op); + + for (uint32_t i = 0; i < addrc; i++) { + _spi.write(0xff & (addr >> 8*(addrc-1 - i))); + } + + for (uint32_t i = 0; i < argc; i++) { + _spi.write(args[i]); + } + _cs = 1; + + if (SPIF_DEBUG) { + printf("spif -> %02x", op); + for (uint32_t i = 0; i < addrc; i++) { + if (i < addrc) { + printf("%02lx", 0xff & (addr >> 8*(addrc-1 - i))); + } else { + printf(" "); + } + } + printf(" "); + for (uint32_t i = 0; i < 16 && i < argc; i++) { + printf("%02x", args[i]); + } + if (argc > 16) { + printf("..."); + } + printf("\n"); + } +} + +int SPIFBlockDevice::_sync() +{ + for (int i = 0; i < SPIF_TIMEOUT; i++) { + // Read status register until write not-in-progress + uint8_t status; + _cmdread(SPIF_RDSR, 0, 1, 0x0, &status); + + // Check WIP bit + if (!(status & SPIF_WIP)) { + return 0; + } + + wait_ms(1); + } + + return BD_ERROR_DEVICE_ERROR; +} + +int SPIFBlockDevice::_wren() +{ + _cmdwrite(SPIF_WREN, 0, 0, 0x0, NULL); + + for (int i = 0; i < SPIF_TIMEOUT; i++) { + // Read status register until write latch is enabled + uint8_t status; + _cmdread(SPIF_RDSR, 0, 1, 0x0, &status); + + // Check WEL bit + if (status & SPIF_WEL) { + return 0; + } + + wait_ms(1); + } + + return BD_ERROR_DEVICE_ERROR; +} + +int SPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) +{ + // Check the address and size fit onto the chip. + if (!is_valid_read(addr, size)) { + return SPIF_BLOCK_DEVICE_ERROR_PARAMETER; + } + + _cmdread(SPIF_READ, 3, size, addr, static_cast<uint8_t *>(buffer)); + return 0; +} + +int SPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) +{ + // Check the address and size fit onto the chip. + if (!is_valid_program(addr, size)) { + return SPIF_BLOCK_DEVICE_ERROR_PARAMETER; + } + + while (size > 0) { + int err = _wren(); + if (err) { + return err; + } + + // Write up to 256 bytes a page + // TODO handle unaligned programs + uint32_t off = addr % 256; + uint32_t chunk = (off + size < 256) ? size : (256-off); + _cmdwrite(SPIF_PROG, 3, chunk, addr, static_cast<const uint8_t *>(buffer)); + buffer = static_cast<const uint8_t*>(buffer) + chunk; + addr += chunk; + size -= chunk; + + wait_ms(1); + + err = _sync(); + if (err) { + return err; + } + } + + return 0; +} + +int SPIFBlockDevice::erase(bd_addr_t addr, bd_size_t size) +{ + // Check the address and size fit onto the chip. + if (!is_valid_erase(addr, size)) { + return SPIF_BLOCK_DEVICE_ERROR_PARAMETER; + } + + while (size > 0) { + int err = _wren(); + if (err) { + return err; + } + + // Erase 4kbyte sectors + // TODO support other erase sizes? + uint32_t chunk = 4096; + _cmdwrite(SPIF_SE, 3, 0, addr, NULL); + addr += chunk; + size -= chunk; + + err = _sync(); + if (err) { + return err; + } + } + + return 0; +} + +bd_size_t SPIFBlockDevice::get_read_size() const +{ + return SPIF_READ_SIZE; +} + +bd_size_t SPIFBlockDevice::get_program_size() const +{ + return SPIF_PROG_SIZE; +} + +bd_size_t SPIFBlockDevice::get_erase_size() const +{ + return SPIF_SE_SIZE; +} + +bd_size_t SPIFBlockDevice::size() const +{ + return _size; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/features/filesystem/spif/SPIFBlockDevice.h Thu Mar 23 08:25:54 2017 +0000 @@ -0,0 +1,155 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_SPIF_BLOCK_DEVICE_H +#define MBED_SPIF_BLOCK_DEVICE_H + +/* If the target has no SPI support then SPIF is not supported */ +#ifdef DEVICE_SPI + +#include <mbed.h> +#include "BlockDevice.h" + + +/** BlockDevice for SPI based flash devices + * such as the MX25R or SST26F016B + * + * @code + * #include "mbed.h" + * #include "SPIFBlockDevice.h" + * + * // Create mx25r on SPI bus with PTE5 as chip select + * SPIFBlockDevice mx25r(PTE2, PTE4, PTE1, PTE5); + * + * int main() { + * printf("mx25r test\n"); + * mx52r.init(); + * printf("mx25r size: %llu\n", mx25r.size()); + * printf("mx25r read size: %llu\n", mx25r.get_read_size()); + * printf("mx25r program size: %llu\n", mx25r.get_program_size()); + * printf("mx25r erase size: %llu\n", mx25r.get_erase_size()); + * + * uint8_t *buffer = malloc(mx25r.get_erase_size()); + * sprintf(buffer, "Hello World!\n"); + * mx25r.erase(0, mx25r.get_erase_size()); + * mx25r.program(buffer, 0, mx25r.get_erase_size()); + * mx25r.read(buffer, 0, mx25r.get_erase_size()); + * printf("%s", buffer); + * + * mx25r.deinit(); + * } + */ +class SPIFBlockDevice : public BlockDevice +{ +public: + /** Creates a SPIFBlockDevice on a SPI bus specified by pins + * + * @param mosi SPI master out, slave in pin + * @param miso SPI master in, slave out pin + * @param sclk SPI clock pin + * @param csel SPI chip select pin + * @param freq Clock speed of the SPI bus (defaults to 40MHz) + */ + SPIFBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName csel, int freq=4000000); + + /** Initialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int init(); + + /** Deinitialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int deinit(); + + /** Read blocks from a block device + * + * @param buffer Buffer to write blocks to + * @param addr Address of block to begin reading from + * @param size Size to read in bytes, must be a multiple of read block size + * @return 0 on success, negative error code on failure + */ + virtual int read(void *buffer, bd_addr_t addr, bd_size_t size); + + /** Program blocks to a block device + * + * The blocks must have been erased prior to being programmed + * + * @param buffer Buffer of data to write to blocks + * @param addr Address of block to begin writing to + * @param size Size to write in bytes, must be a multiple of program block size + * @return 0 on success, negative error code on failure + */ + virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size); + + /** Erase blocks on a block device + * + * The state of an erased block is undefined until it has been programmed + * + * @param addr Address of block to begin erasing + * @param size Size to erase in bytes, must be a multiple of erase block size + * @return 0 on success, negative error code on failure + */ + virtual int erase(bd_addr_t addr, bd_size_t size); + + /** Get the size of a readable block + * + * @return Size of a readable block in bytes + */ + virtual bd_size_t get_read_size() const; + + /** Get the size of a programable block + * + * @return Size of a programable block in bytes + * @note Must be a multiple of the read size + */ + virtual bd_size_t get_program_size() const; + + /** Get the size of a eraseable block + * + * @return Size of a eraseable block in bytes + * @note Must be a multiple of the program size + */ + virtual bd_size_t get_erase_size() const; + + /** Get the total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual bd_size_t size() const; + +private: + // Master side hardware + SPI _spi; + DigitalOut _cs; + + // Device configuration discovered through sfdp + bd_size_t _size; + + // Internal functions + int _wren(); + int _sync(); + void _cmdread(uint8_t op, uint32_t addrc, uint32_t retc, + uint32_t addr, uint8_t *rets); + void _cmdwrite(uint8_t op, uint32_t addrc, uint32_t argc, + uint32_t addr, const uint8_t *args); +}; + + +#endif /* DEVICE_SPI */ + +#endif /* MBED_SPIF_BLOCK_DEVICE_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/features/filesystem/test/fsfat_debug.h Thu Mar 23 08:25:54 2017 +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/features/filesystem/test/fsfat_test.c Thu Mar 23 08:25:54 2017 +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/features/filesystem/test/fsfat_test.h Thu Mar 23 08:25:54 2017 +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 */