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

Files at this revision

API Documentation at this revision

Comitter:
dkato
Date:
Thu Mar 23 08:25:54 2017 +0000
Commit message:
Initial commit

Changed in this revision

README.md Show annotated file Show diff for this revision Revisions of this file
config/mbed_app.json Show annotated file Show diff for this revision Revisions of this file
docs/pics/sd_driver_k64_with_ci_test_shield.jpg Show annotated file Show diff for this revision Revisions of this file
features/TESTS/examples/example1/example1.cpp Show annotated file Show diff for this revision Revisions of this file
features/TESTS/filesystem/basic/basic.cpp Show annotated file Show diff for this revision Revisions of this file
features/TESTS/filesystem/fopen/fopen.cpp Show annotated file Show diff for this revision Revisions of this file
features/TESTS/filesystem/spif/main.cpp Show annotated file Show diff for this revision Revisions of this file
features/filesystem/sd/SDBlockDevice.cpp Show annotated file Show diff for this revision Revisions of this file
features/filesystem/sd/SDBlockDevice.h Show annotated file Show diff for this revision Revisions of this file
features/filesystem/spif/README.md Show annotated file Show diff for this revision Revisions of this file
features/filesystem/spif/SPIFBlockDevice.cpp Show annotated file Show diff for this revision Revisions of this file
features/filesystem/spif/SPIFBlockDevice.h Show annotated file Show diff for this revision Revisions of this file
features/filesystem/test/fsfat_debug.h Show annotated file Show diff for this revision Revisions of this file
features/filesystem/test/fsfat_test.c Show annotated file Show diff for this revision Revisions of this file
features/filesystem/test/fsfat_test.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r b22a1df967cb README.md
--- /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
diff -r 000000000000 -r b22a1df967cb config/mbed_app.json
--- /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"
+        }
+    }
+}
diff -r 000000000000 -r b22a1df967cb docs/pics/sd_driver_k64_with_ci_test_shield.jpg
Binary file docs/pics/sd_driver_k64_with_ci_test_shield.jpg has changed
diff -r 000000000000 -r b22a1df967cb features/TESTS/examples/example1/example1.cpp
--- /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) {}
+}
+
diff -r 000000000000 -r b22a1df967cb features/TESTS/filesystem/basic/basic.cpp
--- /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);
+}
diff -r 000000000000 -r b22a1df967cb features/TESTS/filesystem/fopen/fopen.cpp
--- /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
diff -r 000000000000 -r b22a1df967cb features/TESTS/filesystem/spif/main.cpp
--- /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);
+}
diff -r 000000000000 -r b22a1df967cb features/filesystem/sd/SDBlockDevice.cpp
--- /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 */
diff -r 000000000000 -r b22a1df967cb features/filesystem/sd/SDBlockDevice.h
--- /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 */
diff -r 000000000000 -r b22a1df967cb features/filesystem/spif/README.md
--- /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();
+}
+```
+
diff -r 000000000000 -r b22a1df967cb features/filesystem/spif/SPIFBlockDevice.cpp
--- /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;
+}
diff -r 000000000000 -r b22a1df967cb features/filesystem/spif/SPIFBlockDevice.h
--- /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 */
diff -r 000000000000 -r b22a1df967cb features/filesystem/test/fsfat_debug.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*/
diff -r 000000000000 -r b22a1df967cb features/filesystem/test/fsfat_test.c
--- /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;
+}
+
diff -r 000000000000 -r b22a1df967cb features/filesystem/test/fsfat_test.h
--- /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 */