ELEC351 SUBMISSION - Same as on the DLE

/media/uploads/Luka_Danilovic/elec_315_prototype_assembly.jpg

Files at this revision

API Documentation at this revision

Comitter:
Luka_Danilovic
Date:
Wed Jan 10 09:49:43 2018 +0000
Commit message:
ELEC351 SUBMISSION - SAme as on the DLE

Changed in this revision

ELEC351_LIBRARY/ELEC351_LIBRARY.hpp Show annotated file Show diff for this revision Revisions of this file
ELEC351_LIBRARY/displayMaster/displayMaster.cpp Show annotated file Show diff for this revision Revisions of this file
ELEC351_LIBRARY/displayMaster/displayMaster.hpp Show annotated file Show diff for this revision Revisions of this file
ELEC351_LIBRARY/loggingMaster/dateAndTime.cpp Show annotated file Show diff for this revision Revisions of this file
ELEC351_LIBRARY/loggingMaster/dateAndTime.hpp Show annotated file Show diff for this revision Revisions of this file
ELEC351_LIBRARY/recordsMaster/recordsMaster.cpp Show annotated file Show diff for this revision Revisions of this file
ELEC351_LIBRARY/recordsMaster/recordsMaster.hpp Show annotated file Show diff for this revision Revisions of this file
ELEC351_LIBRARY/samplingMaster/samplingMaster.cpp Show annotated file Show diff for this revision Revisions of this file
ELEC351_LIBRARY/samplingMaster/samplingMaster.hpp Show annotated file Show diff for this revision Revisions of this file
Group.txt Show annotated file Show diff for this revision Revisions of this file
README.txt Show annotated file Show diff for this revision Revisions of this file
TextLCD/TextLCD.cpp Show annotated file Show diff for this revision Revisions of this file
TextLCD/TextLCD.h Show annotated file Show diff for this revision Revisions of this file
main/main.cpp Show annotated file Show diff for this revision Revisions of this file
main/main.hpp Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
sd-driver/README.md Show annotated file Show diff for this revision Revisions of this file
sd-driver/SDBlockDevice.cpp Show annotated file Show diff for this revision Revisions of this file
sd-driver/SDBlockDevice.h Show annotated file Show diff for this revision Revisions of this file
sd-driver/TESTS/block_device/basic/basic.cpp Show annotated file Show diff for this revision Revisions of this file
sd-driver/TESTS/filesystem/basic/basic.cpp Show annotated file Show diff for this revision Revisions of this file
sd-driver/TESTS/filesystem/dirs/main.cpp Show annotated file Show diff for this revision Revisions of this file
sd-driver/TESTS/filesystem/files/main.cpp Show annotated file Show diff for this revision Revisions of this file
sd-driver/TESTS/filesystem/fopen/fopen.cpp Show annotated file Show diff for this revision Revisions of this file
sd-driver/TESTS/filesystem/parallel/main.cpp Show annotated file Show diff for this revision Revisions of this file
sd-driver/TESTS/filesystem/seek/main.cpp Show annotated file Show diff for this revision Revisions of this file
sd-driver/config/mbed_lib.json Show annotated file Show diff for this revision Revisions of this file
sd-driver/util/fsfat_debug.h Show annotated file Show diff for this revision Revisions of this file
sd-driver/util/fsfat_test.c Show annotated file Show diff for this revision Revisions of this file
sd-driver/util/fsfat_test.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ELEC351_LIBRARY/ELEC351_LIBRARY.hpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,10 @@
+#ifndef __ELEC351_LIB__ //Inclusion safeguards
+#define __ELEC351_LIB__
+
+#include "dateAndTime.hpp"      // Manages Date and Time set and read
+#include "recordsMaster.hpp"    // Manages storage of sensor record 
+#include "displayMaster.hpp"    /* Manages LCD display -  INCOMPLETE because a
+weird bug prevents correct writing of characters to the display even though the 
+code looks behavioraly the same as the one provided in the "TextLCD" library. */
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ELEC351_LIBRARY/displayMaster/displayMaster.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,147 @@
+#include "mbed.h"
+#include "displayMaster.hpp"
+
+////////////////////////////////////////////////////////////////////////////////
+//                      INCOMPLETE - RUN OUT OF TIME TO DEBUG                 //
+////////////////////////////////////////////////////////////////////////////////
+
+/* Instructions on using the 16x2 LCD panel are found here:
+[https://www.8051projects.net/lcd-interfacing/lcd-4-bit.php]                  */
+/* N.B. I did the code from scratch since I do not have the code from ELEC230 */
+/* N.B. This class contains blocking function so it goes into its own thread  */
+
+C_displayMaster::C_displayMaster(PinName D1, PinName D2, PinName D3 , PinName D4 , PinName RS , PinName RW, PinName EN)
+    :   // Initialisation of the instance
+    _commsBus(D1, D2, D3, D4),
+    _registerSel(RS),
+    _modeSel(RW),
+    _enable(EN)
+{
+    _registerSel    = INSTRUCTION;  // command mode
+    _modeSel        = WRITE;        // write mode
+
+    Thread::wait(20);               // Wait 20ms to ensure powered up
+
+    for (int i=0; i<3; i++) {       // Three times
+        _enable     = DISABLE;      // Invalidate signal
+        _commsBus   = 0x03;         // Send command for powerup
+        _enable     = ENABLE;       // Validate signal
+        Thread::wait(20);           // Wait since BUSY flag is not yet available
+    }
+
+    _enable     = DISABLE;          // Invalidate signal
+    _commsBus   = 0x03;             // Send command for 4 bit mode
+    _enable     = ENABLE;           // Validate signal
+    Thread::wait(20);               // Wait since BUSY flag is not yet available
+
+    // BUSY flag is now available, Can use "writeChar"                        //
+
+    writeChar(0x28, INSTRUCTION);   // 4-bit, 2 Line, 5x7 Dots
+    writeChar(0x0C, INSTRUCTION);   // Display on Cursor off
+    writeChar(0x04, INSTRUCTION);   // Entry mode - no increment & no display shift
+    clear();                        // Clear display for writing (DDRAM cleared)
+
+//    writeChar(0b00100011, DATA);    // Write Test "#" symbol
+//    Thread::wait(1000);             // Wait 1 second
+//    clear();                        // Clear Test "#" symbol
+}
+
+
+//****************************************************************************//
+void C_displayMaster::writeChar(int Char, bool REGISTER)
+{
+    _enable      = DISABLE;         // DISABLE at start
+    _commsBus[3].output();          // Reconfigure pin as output (MSB)
+    _registerSel = REGISTER;        // Chose register to comunicate with
+    _modeSel     = WRITE;           // WRITE to register
+
+    _commsBus    = (Char>>4);       // Top nibble
+    _enable      = ENABLE;          // Validate signal
+    _enable = DISABLE;              // Invalidate signal
+    busyCheck();                    // Chek if clear to write more
+
+    _commsBus    = (Char>>0);       // Bottom nibble
+    _enable      = ENABLE;          // Validate signal
+    _enable = DISABLE;              // Invalidate signal
+    busyCheck();                    // Chek if clear to write more
+}
+
+
+void C_displayMaster::busyCheck()
+{
+    _enable      = DISABLE;         // DISABLE at start
+    _commsBus[3].input();           // Input to listen for BUSY flag (MSB)
+    _registerSel = INSTRUCTION;     // Comunicate with INSTRUCTION register
+    _modeSel     = READ;            // READ from register
+    _enable      = ENABLE;          // Listen for BUSY flag
+    while(_commsBus[3]) {
+        _enable != _enable;         // Repeat read while BUSY flag is set
+    }
+    _enable      = DISABLE;         // Leave DISABLED upon exit
+    _modeSel     = WRITE;           // Return to WRITE mode
+    _commsBus[3].output();          // Reconfigure pin as output (MSB)
+}
+
+
+void C_displayMaster::calcDDRAM(int col, int row)
+{
+    /* The following two IF statments create a continous display meaning that
+    running of the right edge return you to the left edge and running of the
+    bottom edge return you to the top row.                                    */
+
+    if (col>16) {                   // If coloumn is outside of Right edge
+        col =  1;                   // Go back to start of row
+    } else if (col<1) {             // OR If coloumn is outside of Left edge
+        col =  1;                   // Go back to start of row
+    } else {                        // Othetwise
+        col = col;                  // No change in col
+    }
+
+    if (row>2) {                    // If row is outside of Bottom edge
+        row =  1;                   // Go back to top row
+    } else if (row<1) {             // OR If row is outside of Top edge
+        row =  1;                   // Go back to top row
+    } else {                        // Othetwise
+        row = row;                  // No change in row
+    }
+
+    _ADR = (1<<7)+((1<<6)*row-1)+col-1;   // Recalculate DDRAM address
+}
+
+
+void C_displayMaster::clear()
+{
+    writeChar(0x01, INSTRUCTION);   // Clear display for writing (DDRAM cleared)
+    _col = 1;                       // First coloumn
+    _row = 1;                       // First row
+    calcDDRAM(_col, _row);          // Get DDRAM address for writing next char
+}
+
+
+int C_displayMaster::_getc()
+{
+    return -1;                      // Return Fail value since function has no purpose but is needed for character Stream
+}
+
+
+int C_displayMaster::_putc(int character)
+{
+    if (character=='\n') {          // IF new line
+        _row++;                     // Go to new row
+        _col = 1;                   // Go to start of row
+    } else if (character=='\r') {   // IF carridge return
+        _col = 1;                   // Go to start of line
+    } else {                        // ELSE
+        
+        calcDDRAM(_col, _row);          // Get cursor new address (N.B this function also creates a continous display. More details in function).
+        writeChar(_ADR, INSTRUCTION);   // Set cursor to position
+        writeChar(character, DATA);     // Write character
+        
+        _col++;                     // Increment cursor position
+        if (_col>16) {              // IF end of row go to new row
+            _row++;                 // Go to new row
+            _col = 1;               // Go to start of row
+        }
+    }
+    return character;               // Return what was written to indicate sucess
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ELEC351_LIBRARY/displayMaster/displayMaster.hpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,54 @@
+#ifndef displayMaster
+#define displayMaster
+
+#define defaultD1 D7    // Data Bus LSB
+#define defaultD2 D6    // |
+#define defaultD3 D4    // |
+#define defaultD4 D2    // Data Bus MSB
+#define defaultRS D9    // Register Select
+#define defaultRW D0    // Read/Write Select
+#define defaultEN D8    // Enable
+
+/* Instructions on using the 16x2 LCD panel are found here:
+[https://www.8051projects.net/lcd-interfacing/lcd-4-bit.php]                  */
+/* N.B. I did the code from scratch since I do not have the code from ELEC230 */
+/* N.B. This class contains blocking function so it goes into its own thread  */
+
+// Custom 16x2 LCD display driver (4 bit mode only). Works via character stream
+class C_displayMaster: public Stream 
+{
+protected:
+    
+    // Control line commands
+    enum REGISTER {INSTRUCTION, DATA};
+    enum MODE     {WRITE, READ};
+    enum VALIDATE {DISABLE, ENABLE};
+
+    // Control line types
+    BusInOut      _commsBus;
+    DigitalOut    _registerSel;
+    DigitalOut    _modeSel;
+    DigitalOut    _enable;
+    
+    // Private member variables
+    int _col;                   // Cursor coloumn
+    int _row;                   // Cursor row
+    int _ADR;                   // Cursor DDRAM address 
+    
+    // Private member functions
+    void writeChar(int, bool);  // Write character to display
+    void busyCheck();           // Chek if the display is busy
+    void calcDDRAM(int, int);   // Calculate address in DDRAm for row and coloumn
+    
+    virtual int _getc();        // Virtual reading function for character Stream (not needed by user so it is protected)
+    
+public:
+    // Assign deafault pin names if they are not specified when constructor is run
+    C_displayMaster(PinName D1 = defaultD1, PinName D2 = defaultD2, PinName D3 = defaultD3, PinName D4 = defaultD4, PinName RS = defaultRS, PinName RW = defaultRW, PinName EN = defaultEN);
+   
+    void clear();                 // Clear display
+    virtual int _putc(int value); // Virtual writing function for character Stream 
+
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ELEC351_LIBRARY/loggingMaster/dateAndTime.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,57 @@
+#include "mbed.h"
+#include "dateAndTime.hpp"
+
+TDS_DT C_DT::getDT()            // Returns TDS_DT Date & Time format
+{
+    time_t secondsElapsed;      // Object of type time_t
+    struct tm *tempTimeInfo;    // Pointer to a embeded structure containing calendar dates
+
+    time(&secondsElapsed);      // Get time is seconds since epoch. Also hope people are ready for UNIX milenium bug
+    tempTimeInfo = localtime (&secondsElapsed); // Convert current epoch time to calendar time acounting for timezones & daylight saving
+
+    // Reasign date components to custom data type
+    this-> date_time.day = tempTimeInfo->tm_mday;
+    this-> date_time.mnt = 1+tempTimeInfo->tm_mon;      // Months indexed at 1
+    this-> date_time.yr = 1900 + tempTimeInfo->tm_year; // Years since epoch
+
+    // Reasign time components to custom data type
+    this-> date_time.sec = tempTimeInfo->tm_sec;
+    this-> date_time.min = tempTimeInfo->tm_min;
+    this-> date_time.hr = tempTimeInfo->tm_hour;
+
+    return date_time;   // Return custom data type containing Date & Time
+}
+
+
+void C_DT::setD(int day, int mnt, int yr)    // Updates RTC date
+{
+    time_t secondsElapsed;      // Object of type time_t
+    struct tm *tempTimeInfo;    // Pointer to a embeded structure containing calendar dates
+
+    time(&secondsElapsed);      // Get time is seconds since epoch.
+    tempTimeInfo = localtime (&secondsElapsed); // Convert current epoch time to calendar time acounting for timezones & daylight saving
+
+    tempTimeInfo->tm_mday = day;            // Update day
+    tempTimeInfo->tm_mon  = mnt - 1;        // Update month / Indexed at 0
+    tempTimeInfo->tm_year = yr - 1900 ;     // Update year  / Years since epoch
+
+    secondsElapsed = mktime(tempTimeInfo);  // Convert to epoch time
+    set_time(secondsElapsed);               // Update RTC
+}
+
+
+void C_DT::setT(int hr, int min, int sec)   // Updates RTC time
+{
+    time_t secondsElapsed;      // Object of type time_t
+    struct tm *tempTimeInfo;    // Pointer to a embeded structure containing calendar dates
+
+    time(&secondsElapsed);      // Get time is seconds since epoch.
+    tempTimeInfo = localtime (&secondsElapsed); // Convert current epoch time to calendar time acounting for timezones & daylight saving
+
+    tempTimeInfo->tm_sec = sec; // Update seconds
+    tempTimeInfo->tm_min = min; // Update minutes
+    tempTimeInfo->tm_hour = hr; // Update hours
+
+    secondsElapsed = mktime(tempTimeInfo);  // Convert to epoch time
+    set_time(secondsElapsed);               // Update RTC
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ELEC351_LIBRARY/loggingMaster/dateAndTime.hpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,28 @@
+#ifndef __dateAndTime__ //Inclusion safeguards
+#define __dateAndTime__
+
+
+typedef struct __attribute__ ((packed)) { // Store one after another
+
+    int day, mnt, yr;   // Current Date
+    int sec, min, hr;   // Current Time
+   
+} TDS_DT;               // Type Def Struct _ Date Time
+
+
+class C_DT              // Class _ Date Time
+{
+
+    // Constructorless class - constructor not needed
+
+private:
+    TDS_DT date_time;           // TDS_DT format of storing Date & Time
+
+public:
+    TDS_DT getDT();             // Get Date & Time
+    void setD(int, int, int);   // set Date
+    void setT(int, int, int);   // Set Time
+
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ELEC351_LIBRARY/recordsMaster/recordsMaster.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,140 @@
+#include "mbed.h"
+#include <iostream>
+#include "recordsMaster.hpp"
+
+circularBuffer::circularBuffer()
+{
+    // Check allocated buffer size
+    if (sizeof(record)/sizeof(TDS_record) == 120) {
+        bufferLoopBack = 0;
+        cerr <<"\n\rBuffer aquired\n\r\n"<<endl;
+    } else {
+        cerr << "Could not aquire buffer - bad length\n\r" << endl;
+        cerr << "Attempted to aquire buffer of length 120\n\r" << endl;
+        cerr << "Got buffer of " << sizeof(record)/sizeof(TDS_record) << "\n\r\n" << endl;
+    }
+
+    // Create blank record with all data = 0
+    blankRecord.record_Data.temp   =  0;
+    blankRecord.record_Data.pres   =  0;
+    blankRecord.record_Data.ligt   =  0;
+    blankRecord.record_DT.day      =  0;
+    blankRecord.record_DT.mnt      =  0;
+    blankRecord.record_DT.yr       =  0;
+    blankRecord.record_DT.sec      =  0;
+    blankRecord.record_DT.min      =  0;
+    blankRecord.record_DT.hr       =  0;
+
+}
+
+int circularBuffer::write(TDS_record recordData)
+{
+    if (bufferLoopBack > 119) {
+        bufferLoopBack = 0;       // Make circular
+    }
+    recordLock.lock();            // Lock for writing
+    record[bufferLoopBack] = recordData;    // Store data temporarely
+    bufferLoopBack++;             // Set next writing space
+    recordLock.unlock();          // Unlock
+    return 0;
+}
+
+
+int circularBuffer::read(int index)
+{
+    if (index == 0) {                 // If index is 0
+        index = bufferLoopBack-1;     // Read last record
+    } else {
+        index--;                      // Scale form 1->120 to 0->119
+    }
+    if ((index <0)||(index >119)) {   // If reading outside of buffer range
+        return -1;                    // Return fail
+
+    } else {                          // Else
+        recordLock.lock();            // Prevent unsynchronised reads
+        TDS_record tempRecord = record[index];   // Store data temporarely
+        recordLock.unlock();          // Unlock
+        cout <<"\n\n\n\r Record number "<< index+1 <<endl;
+        cout <<"\n\r Date:" <<endl;
+        cout <<"\r "<< tempRecord.record_DT.day <<"."<< tempRecord.record_DT.mnt <<"."<< tempRecord.record_DT.yr <<endl;
+        cout <<"\n\r Time:" <<endl;
+        cout <<"\r "<< tempRecord.record_DT.hr <<":"<< tempRecord.record_DT.min <<":"<< tempRecord.record_DT.sec <<endl;
+        cout <<"\n\r Temperature = " << tempRecord.record_Data.temp <<endl;
+        cout <<"\r Preassure = "     << tempRecord.record_Data.pres <<endl;
+        cout <<"\r Light Level = "   << tempRecord.record_Data.ligt <<endl;
+        return 0;                     // Return sucess
+
+    }
+}
+
+
+int circularBuffer::del(int index)
+{
+    index--;                          // Scale form 1->120 to 0->119
+    if ((index <0)||(index >119)) {   // If reading outside of buffer range
+        return -1;                    // Return fail
+    } else {
+        recordLock.lock();            // Lock for writing
+        record[index] = blankRecord;  // Erase actual data, not just refference
+        recordLock.unlock();          // Unlock
+        return 0;                     // Return sucess
+    }
+}
+
+
+int circularBuffer::readAll()
+{
+    TDS_record tempRecord;
+
+    for (int i=bufferLoopBack; i <120; i++) {   // Read top part of buffer
+        recordLock.lock();            // Prevent unsynchronised reads
+        tempRecord = record[i];       // Store data temporarely
+        recordLock.unlock();          // Unlock
+        cout <<"\n\r Record number "<< i <<endl;
+        cout <<"\n\r Date:" <<endl;
+        cout <<"\n\r"<< tempRecord.record_DT.day <<"."<< tempRecord.record_DT.mnt <<"."<< tempRecord.record_DT.yr <<endl;
+        cout <<"\n\r Time:" <<endl;
+        cout <<"\n\r"<< tempRecord.record_DT.hr <<":"<< tempRecord.record_DT.min <<":"<< tempRecord.record_DT.sec <<endl;
+        cout <<"\n\r Temperature = " << tempRecord.record_Data.temp <<endl;
+        cout <<"\n\r Preassure = "   << tempRecord.record_Data.pres <<endl;
+        cout <<"\n\r Light Level = " << tempRecord.record_Data.ligt <<endl;
+    }
+
+    for (int j=0; j <bufferLoopBack; j++) {   // Read bottom part of buffer
+        recordLock.lock();            // Prevent unsynchronised reads
+        tempRecord = record[j];       // Store data temporarely
+        recordLock.unlock();          // Unlock
+        cout <<"\n\r Record number "<< j <<endl;
+        cout <<"\n\r Date:" <<endl;
+        cout <<"\n\r"<< tempRecord.record_DT.day <<"."<< tempRecord.record_DT.mnt <<"."<< tempRecord.record_DT.yr <<endl;
+        cout <<"\n\r Time:" <<endl;
+        cout <<"\n\r"<< tempRecord.record_DT.hr <<":"<< tempRecord.record_DT.min <<":"<< tempRecord.record_DT.sec <<endl;
+        cout <<"\n\r Temperature = " << tempRecord.record_Data.temp <<endl;
+        cout <<"\n\r Preassure = "   << tempRecord.record_Data.pres <<endl;
+        cout <<"\n\r Light Level = " << tempRecord.record_Data.ligt <<endl;
+    }
+    return 0;                         // Return sucess
+}
+
+
+int circularBuffer::delAll()
+{
+    for (int i=0; i <120; i++) {
+        recordLock.lock();            // Lock for writing
+        record[i] = blankRecord;      // Erase actual data, not just refference
+        recordLock.unlock();          // Unlock
+        bufferLoopBack = 0;           // Reset buffer
+    }
+    return 0;                         // Return sucess
+}
+
+
+int circularBuffer::recNum()
+{
+    return bufferLoopBack+1;          // Return last record number
+}
+
+circularBuffer::~circularBuffer()
+{
+    cerr << "Buffer Destroyed\n\r\n" << endl;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ELEC351_LIBRARY/recordsMaster/recordsMaster.hpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,51 @@
+#ifndef __recordsMaster__ //Inclusion safeguards
+#define __recordsMaster__
+#include "dateAndTime.hpp"
+#include "samplingMaster.hpp"
+
+
+typedef struct __attribute__ ((packed)) { // Store one after another
+
+    TDS_DT          record_DT;   // Type Def Struct _ Date Time
+    TDS_sensorData  record_Data; // Type Def Struct _ sensor Data
+
+} TDS_record;                    // Type Def Struct _ record
+
+
+class circularBuffer             // Class _ circular Buffer
+{
+
+private:
+    TDS_record record[120]; /* Array of 120 TDS_records. Please note
+    that this is not an instance variable and it does not need to be static
+    since it is in the scope of the circularBuffer class which will not be
+    deleted once called, otherwise where will you store your data. */
+    TDS_record blankRecord;      // Blank record
+    int bufferLoopBack;          // End of buffer (if END=1-START => Overrite next)
+    Mutex recordLock;            /* Mutex when working with a data record. This 
+    mutex is used for writing to ensure that the whole record is written before 
+    it can be modified or read. Similarly, the mutex is used when reading to 
+    ensure that the data read belongs to the same record and not to a new record
+    that could have been written in the meantime. One such example that is 
+    safeguarded against is the user reading a record when the sampling interupt 
+    occurs. This would result in Date and Time belonging to one record but the 
+    sensor data to another.
+    Mitigation against deadlocks is handled by the nature of the scheduler since
+    all producer threads push the data to be written to the stack so it is
+    buffered if the mutex is locked. Even in the case of the Highst priority
+    producer thread the data is buffered and the thread will be blocked until
+    another mutex operation (reading or deleting record) is complete. Then the
+    highest priority thread will write the buffered data.                     */
+    
+public:
+    circularBuffer();       // Initilise buffer
+    int write(TDS_record);  // Write single record to buffer
+    int read(int);          // Read single record from buffer
+    int del(int);           // Delete single record from buffer
+    int readAll();          // Read all records from buffer
+    int delAll();           // Delete all records from buffer
+    int recNum();           // Returns last record number
+    ~circularBuffer();      // Warn if buffer destroyed
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ELEC351_LIBRARY/samplingMaster/samplingMaster.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,135 @@
+#include "mbed.h"
+#include "samplingMaster.hpp"
+
+/* Spec said that we are not allowed to use any third party code unless it is
+from ARM or STmicroelectronics, so I wrote my own driver for sampling BMP280 &
+ADC in succesion. Ths code for MPB280 was inspired by the BMP280 Datasheet:
+[https://cdn-shop.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf]
+and BMP280 mbed library by "charlie":
+[https://os.mbed.com/users/CHARLY/code/BMP280/] */
+
+C_sensorData::C_sensorData()
+{
+
+    //********************************LDR ADC*********************************//
+    AnalogIn ldrADC(PA_0);      // Establish Analog In for ADC
+
+    //******************************BMP280 I2C********************************//
+    I2C sensorLink(D14, D15);   // Establish I2C comms
+
+    inst[0] = CONTROL_REGISTER; // Register address
+    inst[1] = 0b01101101;       // Temp x4, Pressure x4, Forced mode
+    sensorLink.write(I2C_adr, inst, 2);
+
+    inst[0] = Tsb_IIR_REGISTER; // Register address
+    inst[1] = 0x00;             // No standby, No IIR filter
+    sensorLink.write(I2C_adr, inst, 2);
+
+    inst[0] = DIG_Tn_REGISTERS; // Read trimming parameters instruction
+    sensorLink.write(I2C_adr, inst, 1);
+    sensorLink.read(I2C_adr, inst, 6);
+
+    /* For instructions how to decode trimming parameters please refer to the
+    BMP280 datasheet, page 21, chapter 3.11.2, table 17: "Compensation parameter
+    storage, naming and data type" */
+    T1Trim = (inst[1]<<8)|inst[0];
+    T2Trim = (inst[3]<<8)|inst[2];
+    T3Trim = (inst[5]<<8)|inst[4];
+
+    inst[0] = DIG_Pn_REGISTERS; // Read trimming parameters instruction
+    sensorLink.write(I2C_adr, inst, 1);
+    sensorLink.read(I2C_adr, inst, 18);
+
+    /* For instructions how to decode trimming parameters please refer to the
+    BMP280 datasheet, page 21, chapter 3.11.2, table 17: "Compensation parameter
+    storage, naming and data type" */
+    P1Trim = (inst[ 1]<<8)|inst[ 0];
+    P2Trim = (inst[ 3]<<8)|inst[ 2];
+    P3Trim = (inst[ 5]<<8)|inst[ 4];
+    P4Trim = (inst[ 7]<<8)|inst[ 6];
+    P5Trim = (inst[ 9]<<8)|inst[ 8];
+    P6Trim = (inst[11]<<8)|inst[10];
+    P7Trim = (inst[13]<<8)|inst[12];
+    P8Trim = (inst[15]<<8)|inst[14];
+    P9Trim = (inst[17]<<8)|inst[16];
+}
+
+TDS_sensorData C_sensorData::read()
+{
+    //*********************************SETUP**********************************//
+    TDS_sensorData sensorData;  // Type def struct to hold the data
+    AnalogIn ldrADC(PA_0);      // Establish Analog In for ADC
+    I2C sensorLink(D14, D15);   // Establish I2C comms
+
+    /* N.B in forced mode you must write to CONTROL_REGISTER to wake the IMU up.
+    It automaticly sleeps when measurments are aquired but leaves the registers
+    readable for the I2C link                                                 */
+    inst[0] = CONTROL_REGISTER; // Register address
+    inst[1] = 0b01101101;       // Temp x4, Pressure x4, Forced mode
+    sensorLink.write(I2C_adr, inst, 2);
+
+    //******************************TEMPERATURE*******************************//
+    inst[0] = TEMP_ADDRESS_VAL; // Start of temperature register
+    sensorLink.write(I2C_adr, inst, 1);
+    sensorLink.read(I2C_adr, &inst[1], 3);
+
+    tempT = (inst[1]<<12)|(inst[2]<<4)|(inst[3]>>4);
+    //             ^             ^            ^ 
+    //      MSB REGISTER  LSB REGISTER  X_LSB REGISTER
+
+    /* For instructions how to compensate values please refer to the
+    BMP280 datasheet, page 21 & 22, chapter 3.11.3, pseudo code */
+    tempA = ((((tempT>>3)-(T1Trim<<1)))*T2Trim)>>11;
+    tempB = (((((tempT>>4)-T1Trim)*((tempT>>4)-T1Trim))>>12)*T3Trim)>>14;
+    tempT = tempA+tempB;
+    sensorData.temp = ((float)((tempT * 5 + 128) >> 8))/100;
+
+    //********************************PRESURE********************************//
+    inst[0] = PRES_ADDRESS_VAL; // Start of presure register
+    sensorLink.write(I2C_adr, inst, 1);
+    sensorLink.read(I2C_adr, &inst[1], 3);
+
+    tempP = (inst[1]<<12)|(inst[2]<<4)|(inst[3]>>4);
+    //             ^             ^            ^ 
+    //      MSB REGISTER  LSB REGISTER  X_LSB REGISTER
+
+    /* For instructions how to compensate values please refer to the
+    BMP280 datasheet, page 21 & 22, chapter 3.11.3, pseudo code.
+
+    Please note that the compensation in the datasheet is done with 64 bit
+    signed values. However, "charlie's" BMP280 driver which does the
+    compensation using 32 bit signed values has inspired me to write my
+    compensation algorithm as follows. This makes the algorithm more efficient
+    without sacrifising relevant acuracy
+
+    N.B the pressure calculated by the following algorithm is in hPa which is
+    a newer unit with equivalence of: 1hPA = 1mbar*/
+
+    tempA = (tempT>>1)-64000;
+    tempB = (((tempA>>2)*(tempA>>2))>>11)*P6Trim;
+    tempB = tempB+((tempA*P5Trim)<<1);
+    tempB = (tempB>>2)+(P4Trim<<16);
+    tempA = (((P3Trim*(((tempA>>2)*(tempA>>2))>>13))>>3)+((P2Trim*tempA)>>1))>>18;
+    tempA = ((32768+tempA)*P1Trim)>>15;
+
+    if(tempA == 0) {
+        sensorData.pres = 0;
+    }
+    tempP = (((1048576-tempP)-(tempB>>12)))*3125; // Compensate for 64 bit loss
+    if(tempP < PREAS_UPER_LIMIT) {
+        tempP = (tempP<<1)/tempA;
+    } else {
+        tempP = (tempP/tempA)*2;
+    }
+
+    tempA = ((int32_t)P9Trim*((int32_t)(((tempP>>3)*(tempP>>3))>>13)))>>12;
+    tempB = (((int32_t)(tempP>>2))*(int32_t)P8Trim)>>13;
+    sensorData.pres = ((float)((tempP+((tempA + tempB+P7Trim)>>4))))/100;
+
+
+    //******************************LIGHT LEVEL*******************************//
+    sensorData.ligt = ldrADC.read();            // Read ldr value
+
+    //RETURN SENSOR DATA//
+    return sensorData;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ELEC351_LIBRARY/samplingMaster/samplingMaster.hpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,55 @@
+#ifndef __samplingMaster__ //Inclusion safeguards
+#define __samplingMaster__
+
+
+typedef struct __attribute__ ((packed)) { // Store one after another
+
+    float temp;     // Temperature
+    float pres;     // Presure
+    float ligt;     // Light level
+
+} TDS_sensorData;   // Type Def Struct _ sensor Data
+
+
+/* Spec said that we are not allowed to use any third party code unless it is
+from ARM or STmicroelectronics, so I wrote my own driver for sampling BMP280 &
+ADC in succesion. Ths code for MPB280 was inspired by the BMP280 Datasheet: 
+[https://cdn-shop.adafruit.com/datasheets/BST-BMP280-DS001-11.pdf]
+and BMP280 mbed library by "charlie": 
+[https://os.mbed.com/users/charly/code/BMP280/] */
+
+
+
+
+/* BMP280 register addresses and values */
+#define I2C_adr 0xEE
+#define CONTROL_REGISTER 0xF4
+#define Tsb_IIR_REGISTER 0xF5
+#define PRES_ADDRESS_VAL 0xF7
+#define TEMP_ADDRESS_VAL 0xFA
+#define DIG_Tn_REGISTERS 0x88
+#define DIG_Pn_REGISTERS 0x8E
+#define PREAS_UPER_LIMIT 0x80000000
+
+
+class C_sensorData              // Class _ sensor Data 
+{
+    private: 
+    PinName  ADC_pin;
+    PinName  SDI_pin;
+    PinName  SCK_pin;
+    char     inst[18];          // Instruction/Data array
+    uint16_t T1Trim;            // Unsigned int of 16 bits for temperature trim
+    int16_t  T2Trim, T3Trim;    // Signed ints of 16 bits for temperature trim
+    uint16_t P1Trim;            // Unsigned int of 16 bits for pressure trim
+    int16_t  P2Trim, P3Trim, P4Trim, P5Trim, P6Trim, P7Trim, P8Trim, P9Trim; /* 
+    Signed ints of 16 bits for temperature trim */
+    int32_t  tempT, tempA, tempB;// Temporary working variables
+    uint32_t tempP;              // Temporary working variables
+    
+    public:
+    C_sensorData();
+    TDS_sensorData read();
+};
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Group.txt	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,4 @@
+Participants: 1
+
+Luka Danilovic
+10497267
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README.txt	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,105 @@
+Hi Nick,
+
+Below is a summary of the parts I have finished and attempted.
+
+Y = Parts finished
+P = Partially working but not finished
+N = Not finished
+
+________________________________________________________________________________
+Task    Marks   Status
+----    -----   ----------------------------------------------------------------
+1.      (10)    Y - Complete
+
+2.      ( 4)    Y - Complete
+
+3.      ( 4)    N - Not attempted (time constraint)
+
+4.      ( 4)    Y - Complete
+
+5.      ( 0)    Y - Exception is the LCD driver 
+
+6.      ( 4)    Y - No spinning, deadlocks or other nasties
+
+7.      (10)    P - Partial attempted (time constraint). I have written the 
+                    following functions to be used with PuTTY but could not 
+                    implment string scaning from terminal. Working Functions are
+                    
+                    READ ALL
+                    DELETE ALL
+                    READ N - from 1 to 120, if you read record 0 it will return 
+                             latest sample
+                    DELETE n
+                    SETDATE dd mm yyyy
+                    SETTIME hh mm ss
+                    SETT t
+                    STATE x
+                    
+                    In the main.cpp, at the bottom of the main function there is
+                    a comented block of code titled "DEMO CODE". If you uncoment
+                    and recompile than the functions SETT t, STATE x, READ ALL, 
+                    DELETE n and DELETE ALL will be demonstrated. The other 
+                    functions are demonstrated as part of normal routine. 
+
+8.      ( 0)    Y - All serial communication to PuTTY is in separate threads to 
+                    the sampling
+
+9.      (10)    P - Attempted, Code present and working in a separate project. 
+                    However there is an issue I could not diagnose. I suspect it
+                    is with MBED TCP server resetting
+                    
+10.     (10)    Y - Care taken to eliminate deadlocks/starvation. Special care 
+                    taken with Mutexes. WDT present and linked to sampling 
+                    interval.
+
+11.     (10)    P - Attempted, Code present and encapsulated into class, however
+                    an issue with initialising the LCD display prevents it from 
+                    working.
+
+12.     ( 4)    Y - The code is buildt on idea that every major module that 
+                    interacts with another (e.g. sampling data then storing 
+                    data) is object based and encapsulated in class if 
+                    necessary. They pass data to each other if necessary. 
+                    ROS operates on simillar principl to what I have achieved 
+                    with my code.
+
+13.     (10)    Y - I tried to make it neat and clear to understand. If there is
+                    something I have not explained well I will try to elaborate
+                    in the demo if you wish.
+
+14.     (10)    N - Not attempted (time constraint)
+
+15.     (10)    Y - Works and is not hot swapable. In the very rare event of a 
+                    IRQ queue overflow the WDT should reset system
+________________________________________________________________________________
+
+As you can see I have not been able to finish all the tasks I have attempted due
+to lack of time as a result of a chain of events which i would like to tell
+you in person. 
+However I have tried to take care to write the code in a structured manner with 
+sence of competency. I have completed the above tasks in about 4 days of time 
+that i have had.
+
+________________________________________________________________________________
+================================================================================
+DEMO:
+
+If you wish to run the demo simply upload the code (The ELEC351 library i wrote
+is included or can be found at:
+"https://os.mbed.com/users/Luka_Danilovic/code/ELEC351_LIBRARY/").
+
+Insert SD card 
+Reset the board untill white LED is fully on
+Date and time will automaticly be set to 10.10.18 @ 10:00:00 (Deadline time)
+Sampling will default to 15 sec interval
+You can uncoment "DEMO CODE" to see functions in action
+
+The IP address of the webpage is 10.0.0.10 (like in the example)
+However, please note that I was having issues with connection being reset (I 
+suspect after handshake or simillar) so I cannot guarantee this will work.
+
+Samples are saved to the SD card along with date and time they were recorded at.
+Hold blue USER_BUTTON untill green LED flashes to unmount the SD card.
+N.B SD card is not hot swapable
+________________________________________________________________________________
+================================================================================
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TextLCD/TextLCD.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,159 @@
+/* mbed TextLCD Library, for a 4-bit LCD based on HD44780
+ * Copyright (c) 2007-2010, sford, http://mbed.org
+ *
+ * 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.
+ */
+
+#include "TextLCD.h"
+#include "mbed.h"
+
+TextLCD::TextLCD(PinName rs, PinName e, PinName d4, PinName d5,
+                 PinName d6, PinName d7, LCDType type) : _rs(rs),
+        _e(e), _d(d4, d5, d6, d7),
+        _type(type) {
+
+    _e  = 1;
+    _rs = 0;            // command mode
+
+    wait(0.015);        // Wait 15ms to ensure powered up
+
+    // send "Display Settings" 3 times (Only top nibble of 0x30 as we've got 4-bit bus)
+    for (int i=0; i<3; i++) {
+        writeByte(0x3);
+        wait(0.00164);  // this command takes 1.64ms, so wait for it
+    }
+    writeByte(0x2);     // 4-bit mode
+    wait(0.000040f);    // most instructions take 40us
+
+    writeCommand(0x28); // Function set 001 BW N F - -
+    writeCommand(0x0C);
+    writeCommand(0x6);  // Cursor Direction and Display Shift : 0000 01 CD S (CD 0-left, 1-right S(hift) 0-no, 1-yes
+    cls();
+}
+
+void TextLCD::character(int column, int row, int c) {
+    int a = address(column, row);
+    writeCommand(a);
+    writeData(c);
+}
+
+void TextLCD::cls() {
+    writeCommand(0x01); // cls, and set cursor to 0
+    wait(0.00164f);     // This command takes 1.64 ms
+    locate(0, 0);
+}
+
+void TextLCD::locate(int column, int row) {
+    _column = column;
+    _row = row;
+}
+
+int TextLCD::_putc(int value) {
+    if (value == '\n') {
+        _column = 0;
+        _row++;
+        if (_row >= rows()) {
+            _row = 0;
+        }
+    } else {
+        character(_column, _row, value);
+        _column++;
+        if (_column >= columns()) {
+            _column = 0;
+            _row++;
+            if (_row >= rows()) {
+                _row = 0;
+            }
+        }
+    }
+    return value;
+}
+
+int TextLCD::_getc() {
+    return -1;
+}
+
+void TextLCD::writeByte(int value) {
+    _d = value >> 4;
+    wait(0.000040f); // most instructions take 40us
+    _e = 0;
+    wait(0.000040f);
+    _e = 1;
+    _d = value >> 0;
+    wait(0.000040f);
+    _e = 0;
+    wait(0.000040f);  // most instructions take 40us
+    _e = 1;
+}
+
+void TextLCD::writeCommand(int command) {
+    _rs = 0;
+    writeByte(command);
+}
+
+void TextLCD::writeData(int data) {
+    _rs = 1;
+    writeByte(data);
+}
+
+int TextLCD::address(int column, int row) {
+    switch (_type) {
+        case LCD20x4:
+            switch (row) {
+                case 0:
+                    return 0x80 + column;
+                case 1:
+                    return 0xc0 + column;
+                case 2:
+                    return 0x94 + column;
+                case 3:
+                    return 0xd4 + column;
+            }
+        case LCD16x2B:
+            return 0x80 + (row * 40) + column;
+        case LCD16x2:
+        case LCD20x2:
+        default:
+            return 0x80 + (row * 0x40) + column;
+    }
+}
+
+int TextLCD::columns() {
+    switch (_type) {
+        case LCD20x4:
+        case LCD20x2:
+            return 20;
+        case LCD16x2:
+        case LCD16x2B:
+        default:
+            return 16;
+    }
+}
+
+int TextLCD::rows() {
+    switch (_type) {
+        case LCD20x4:
+            return 4;
+        case LCD16x2:
+        case LCD16x2B:
+        case LCD20x2:
+        default:
+            return 2;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TextLCD/TextLCD.h	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,111 @@
+/* mbed TextLCD Library, for a 4-bit LCD based on HD44780
+ * Copyright (c) 2007-2010, sford, http://mbed.org
+ *
+ * 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_TEXTLCD_H
+#define MBED_TEXTLCD_H
+
+#include "mbed.h"
+
+/**  A TextLCD interface for driving 4-bit HD44780-based LCDs
+ *
+ * Currently supports 16x2, 20x2 and 20x4 panels
+ *
+ * @code
+ * #include "mbed.h"
+ * #include "TextLCD.h"
+ * 
+ * TextLCD lcd(p10, p12, p15, p16, p29, p30); // rs, e, d4-d7
+ * 
+ * int main() {
+ *     lcd.printf("Hello World!\n");
+ * }
+ * @endcode
+ */
+class TextLCD : public Stream {
+public:
+
+    /** LCD panel format */
+    enum LCDType {
+        LCD16x2     /**< 16x2 LCD panel (default) */
+        , LCD16x2B  /**< 16x2 LCD panel alternate addressing */
+        , LCD20x2   /**< 20x2 LCD panel */
+        , LCD20x4   /**< 20x4 LCD panel */
+    };
+
+    /** Create a TextLCD interface
+     *
+     * @param rs    Instruction/data control line
+     * @param e     Enable line (clock)
+     * @param d4-d7 Data lines for using as a 4-bit interface
+     * @param type  Sets the panel size/addressing mode (default = LCD16x2)
+     */
+    TextLCD(PinName rs, PinName e, PinName d4, PinName d5, PinName d6, PinName d7, LCDType type = LCD16x2);
+
+#if DOXYGEN_ONLY
+    /** Write a character to the LCD
+     *
+     * @param c The character to write to the display
+     */
+    int putc(int c);
+
+    /** Write a formated string to the LCD
+     *
+     * @param format A printf-style format string, followed by the
+     *               variables to use in formating the string.
+     */
+    int printf(const char* format, ...);
+#endif
+
+    /** Locate to a screen column and row
+     *
+     * @param column  The horizontal position from the left, indexed from 0
+     * @param row     The vertical position from the top, indexed from 0
+     */
+    void locate(int column, int row);
+
+    /** Clear the screen and locate to 0,0 */
+    void cls();
+
+    int rows();
+    int columns();
+
+protected:
+
+    // Stream implementation functions
+    virtual int _putc(int value);
+    virtual int _getc();
+
+    int address(int column, int row);
+    void character(int column, int row, int c);
+    void writeByte(int value);
+    void writeCommand(int command);
+    void writeData(int data);
+
+    DigitalOut _rs, _e;
+    BusOut _d;
+    LCDType _type;
+
+    int _column;
+    int _row;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/main.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,267 @@
+#include "main.hpp" // Contains other HPP files and function prototypes (WITH INCLUSION SAFEGUARDS)
+
+
+//**********************************SETUP*************************************//
+C_sensorData sensorData;                    // Initialise samplingMaster
+C_DT  Date_Time;                            // Initialise loggingMaster
+circularBuffer buffer;                      // Initialise recordsMaster
+TextLCD LCD(D9, D8, D7, D6, D4, D2);        // Initialise LCD driver
+SDBlockDevice sd(PB_5, D12, D13, D10);      // Initialise SD card driver
+Thread wdtThread(osPriorityNormal);         // Crate thread for WDT
+Thread samplingThread (osPriorityRealtime); // Create thread for sampling
+Thread puttyThread (osPriorityNormal);      // Create thread for PuTTY
+Thread displayThread (osPriorityNormal);    // Create thread for LCD
+Thread htmlThread (osPriorityNormal, OS_STACK_SIZE*2);  // Create thread for HTML
+Thread sdcardThread (osPriorityNormal,OS_STACK_SIZE*2); // Create thread for sd card
+Ticker samplingTick;                        // Create ticker for sampling
+TDS_record  tempRec;                        // Create temporary storage (GLOBAL)
+EthernetInterface eth;                      // Create eth interface (GLOBAL)
+float interval;                             // Create sampling interval (GLOBAL)
+bool wdtFlag;                               // Create wdt flag (GLOBAL)
+bool state;                                 // Create sampling state (GLOBAL)
+DigitalIn button(USER_BUTTON);              // Create D_in for blue button
+DigitalOut greenLED(PB_11);                 // Create D_out for green LED
+DigitalOut sdSatusLED(PE_15);               // Create D_out for white LED
+
+
+//********************************FUNCTIONS***********************************//
+
+void samplingISR()                          // Sampling interupt
+{
+    samplingThread.signal_set(GET_SAMPLE);  // Signal thread to sample
+}
+
+
+void samplingFunction()                     // Reads sensors and time
+{
+    while(true) {
+
+        Thread::signal_wait(GET_SAMPLE);         // Wait on signal
+        if (state == 1) {                            // If sampling on
+            tempRec.record_Data = sensorData.read(); // Aquire sensors
+            tempRec.record_DT = Date_Time.getDT();   // Aquire date and time
+            buffer.write(tempRec);                   // Record sample
+            Thread::signal_clr(GET_SAMPLE);          // Clear signal
+            
+            // Branch out when sampling is finished
+            displayThread.signal_set(DISP_SAMPLE);   // Signal thread to display sample
+            htmlThread.signal_set(HTML_SAMPLE);      // Signal thread to update webpage
+            sdcardThread.signal_set(SD_SAMPLE);      // Signal thread to write to sd card
+        }
+
+        // WDT flag set
+        wdtFlag = 1;
+        
+    }
+}
+
+
+void wdtFunction()          /* Simplest Watch Dog Timer that resets the system
+                            if the sampling thread does not set a flag every
+                            sampling interval                                 */
+{
+    while(true) {                           // Always perform check
+        Thread::wait(interval*1000+5);      // Block (For lower plwer consumption) until ready to read
+        if (wdtFlag == 0) {                 // If flag not set
+            NVIC_SystemReset();;            // Reset chip
+        } else {                            // else
+            wdtFlag =0;                     // clear flag and loop
+        }
+    }
+}
+
+void displayFunction()                     // Displays sample to LCD & PuTTY
+{
+    while(true) {
+        Thread::signal_wait(DISP_SAMPLE);  // Wait on signal
+            buffer.read(0);                    // Read last record and diplay
+            LCD.cls();                         // Clear LCD
+            LCD.printf("Temp Press Light%.1f  %4.0f %.3f", tempRec.record_Data.temp, tempRec.record_Data.pres, tempRec.record_Data.ligt);
+        Thread::signal_clr(DISP_SAMPLE);   // Clear signal
+    }
+}
+
+
+void sdFunction()                           // Writes data to sd card
+{
+    Thread::wait(10);                       // Powerup time
+    greenLED = 1;                           // Turn off at the start
+    FILE* fp;                               // File pointer
+    int batchCount;                         // Record grouping counter
+    batchCount = 0;                         // Reset
+
+    if ( sd.init() == 0) {                  // Check for initialisation
+        FATFileSystem fs("sd", &sd);        // Create file system
+
+        fp = fopen("/sd/records.txt","a");  // Open file for write
+
+        if (fp != NULL) {                   // Check for sucess
+
+            sdSatusLED = 0;                 // Turn on status LED (white)
+
+            while(button ==0) {             // If dismounting
+
+                batchCount++;
+                Thread::signal_wait(SD_SAMPLE);  // Wait on signal
+
+                TDS_record sdRec = tempRec; // Copy for safe storage
+
+                // Write to file
+                fprintf(fp, "\r Record number: %d \n\r", buffer.recNum()-1);
+                fprintf(fp, " Date: %d.%d.%d \n\r", sdRec.record_DT.day, sdRec.record_DT.mnt, sdRec.record_DT.yr);
+                fprintf(fp, " Time: %d:%d:%d \n\r", sdRec.record_DT.hr, sdRec.record_DT.min, sdRec.record_DT.sec);
+                fprintf(fp, " Temperature = %1.2f  \n\r", sdRec.record_Data.temp);
+                fprintf(fp, " Pressure    = %4.0f  \n\r", sdRec.record_Data.pres);
+                fprintf(fp, " Light Level = %1.3f  \n\r", sdRec.record_Data.ligt);
+                fprintf(fp, " \n\r \n\n");
+
+                if (batchCount == 10) {                 // Save in batches of 10
+                    batchCount =0;                      // Reset counter
+                    fclose(fp);                         // Save & Close file
+                    fp = fopen("/sd/records.txt","a");  // Open file for write
+                }
+
+
+                Thread::signal_clr(SD_SAMPLE);          // Clear signal
+
+            }
+
+            sdSatusLED = 1;              // Turn off status LED (white)
+            fclose(fp);                  // Save & Close file
+            sd.deinit();                 // Uninitialise sd card
+            for (int i=0; i <5; i++) {   // Do 5 times
+                greenLED = 0;
+                wait(0.25);              // Flash LED at 4Hz
+                greenLED = 1;
+                wait(0.25);
+
+            }
+        }
+    }
+}
+
+
+
+
+void htmlFunction() /* Creates and updates webpage. The code for setting up the
+ethernet conection & server is from our lab tasks:
+"https://os.mbed.com/teams/University-of-Plymouth-Stage-2-and-3/code/Task671-mbedos-FZ429-TCP-dynamic/file/76bd6f78cabc/main.cpp/" */
+{
+    TDS_record htmlRec;      /* Temporary storage that can be overwritten only
+    when webpage is refreshed                                                 */
+
+    // Config ethernet connection
+    eth.set_network(IP, NETMASK, GATEWAY);
+    eth.connect();
+
+    TCPServer srv;           //TCP IP Server
+    TCPSocket clt_sock;      //Socket for communication
+    SocketAddress clt_addr;  //Address of incoming connection
+
+    /* Open the server on ethernet stack */
+    srv.open(&eth);
+
+    /* Bind the HTTP port (TCP 80) to the server */
+    srv.bind(eth.get_ip_address(), 80);
+
+    /* Can handle 5 simultaneous connections */
+    srv.listen(5);
+
+    while(true) {
+
+        using namespace std;
+        //Block and wait on an incoming connection
+        srv.accept(&clt_sock, &clt_addr);
+
+        htmlRec = tempRec;      // Copy to safe storage for generating the html
+
+        // Strings to store record parameters
+        // Record number
+        char Num[32];
+        // Date
+        char Date[32];
+        // Time
+        char Time[32];
+        // Sensor data
+        char Temp[32];
+        char Pres[32];
+        char Ligt[32];
+
+
+        //Convert to a C String
+        // Record number
+        sprintf(Num, " <h1> Record Number: %d </h1> \n\r", buffer.recNum());
+        // Date
+        sprintf(Date, "<p> Date: %d.%d.%d </p> \n\r", htmlRec.record_DT.day, htmlRec.record_DT.mnt, htmlRec.record_DT.yr);
+        // Time
+        sprintf(Time, "<p> Time: %d:%d:%d </p> \n\n\r", htmlRec.record_DT.hr, htmlRec.record_DT.min, htmlRec.record_DT.sec);
+        // Sensor Data
+        sprintf(Temp, "<p> Temperature = %1.2f </p> \n\r", htmlRec.record_Data.temp);
+        sprintf(Pres, "<p> Pressure    = %4.0f </p> \n\r", htmlRec.record_Data.pres);
+        sprintf(Ligt, "<p> Light Level = %1.3f </p> \n\r", htmlRec.record_Data.ligt);
+
+        //Uses a C++ string to make it easier to concatenate
+        string webpage;
+
+        //Build the C++ string webpage
+        webpage = HTTP_HEADER;          // Header
+        webpage += Num;                 // Record number
+        webpage += Date;                // Record date
+        webpage += Time;                // Record time
+        webpage += Temp;                // Record temperature
+        webpage += Pres;                // Record preassure
+        webpage += Ligt;                // Record light level
+        webpage += HTTP_FOOTER;         // Webpage
+
+        //Send static HTML webpage (as a C string)
+        clt_sock.send(webpage.c_str(), webpage.size()+8);
+
+    }
+}
+
+
+void changeSamplingT(float interval)
+{
+    if (interval < (float)0.2) {
+        interval = 0.2;                    // Max sampling interval 5Hz
+    }
+    samplingTick.detach();
+    samplingTick.attach(&samplingISR, interval); //Dettach and Re-atttach ISR to ticker
+}
+
+
+//**********************************MAIN**************************************//
+int main()
+{
+    state = 1;                             // Turn on sampling
+    sdSatusLED = 1;                        // Turn sd card status LED off
+    interval = 15;                          // Default sampling interval
+    samplingTick.attach(&samplingISR, interval);  // Attach ISR
+    wdtThread.start(wdtFunction);           // Start WTD thread
+    samplingThread.start(samplingFunction); // Start sampling thread
+    displayThread.start(displayFunction);   // Start display thread
+    htmlThread.start(htmlFunction);         // Start HTML thread
+    sdcardThread.start(sdFunction);         // Start sd card thread
+    Date_Time.setD(10, 10, 2018);           // Set date to submission deadline
+    Date_Time.setT(10, 00, 00);             // Set time to submission deadline
+    LCD.printf("Hello, Waiting  on first sample"); // Show signs of life on LCD
+
+
+    /*=========================== DEMO CODE ==================================*/
+//    interval=1;                 // Set new interval to 1 sec
+//    changeSamplingT(interval);  // Change the interval
+//    Thread::wait(3000);         // Wait for 3 sample intervals
+//    state = 0;                  // Turn off sampling
+//    buffer.del(1);              // Delete record number 1
+//    buffer.read(1);             // Read record number 1
+//    Thread::wait(3000);         // Wait so user can see
+//    buffer.delAll();            // Delete all records
+//    Thread::wait(3000);         // Wait so user can see
+//    buffer.readAll();           // Read all records
+    /*======================= END OF DEMO CODE ===============================*/
+
+    Thread::wait(osWaitForever);            /* Scheduler put thread to sleep
+                                            untill interupt to sample occurs but
+                                            function remains in scope         */
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main/main.hpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,54 @@
+#ifndef __main__ //Inclusion safeguards
+#define __main__
+
+/* Libraries */
+#include "mbed.h"
+#include <iostream>
+#include <string>
+#include "ELEC351_LIBRARY.hpp"  // Including necessary Custom Library files 
+#include "TextLCD.h"            /* Needed since my LCD driver has bug. for more 
+details please see note in "ELEC351_LIBRARY.hpp"                              */
+#include "SDBlockDevice.h"
+#include "FATFileSystem.h"
+#include "EthernetInterface.h"
+#include "TCPServer.h"
+#include "TCPSocket.h"
+
+
+/* Defines */
+#define DEF_SAMP_RATE 15        // Default sampling rate = 15sec
+#define GET_SAMPLE  0x01        // Signal to thread to start sampling
+#define DISP_SAMPLE 0x02        // Signal to thread to diplay sample
+#define HTML_SAMPLE 0x04        // Signal to thread to update webpage
+#define SD_SAMPLE   0x08        // Signal to thread to write tot sd card  
+
+#define IP        "10.0.0.10"
+#define NETMASK   "255.0.0.0"
+#define GATEWAY   "10.0.0.1"
+
+
+/* HTML */
+#define HTTP_STATUS_LINE "HTTP/1.0 200 OK"
+#define HTTP_HEADER_FIELDS "Content-Type: text/html; charset=utf-8"
+#define HTTP_HEADER ""                                          \
+"<html>" "\r\n"                                                 \
+"  <body style=\"display:flex;text-align:center\">" "\r\n"      \
+"    <div style=\"margin:auto\">" "\r\n"
+
+#define HTTP_FOOTER ""                                          \
+"    </div>" "\r\n"                                             \
+"  </body>" "\r\n"                                              \
+"</html>"
+
+#define HTTP_RESPONSE HTTP_STATUS_LINE "\r\n"   \
+                      HTTP_HEADER_FIELDS "\r\n" \
+                      "\r\n"                    \
+                      HTTP_MESSAGE_BODY "\r\n"
+
+
+/* Function prototypes */
+void samplingISR();
+void samplingFunction();
+void changeSamplingT(float interval);
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os.lib	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-os/#2b4ff78ab0a52ef1dc3f2998908453c595e2b2c0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/README.md	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,612 @@
+# mbed OS SDCard Driver (sd-driver) for FAT32 Filesystem Support
+
+
+Simon Hughes
+
+20170329
+
+Version 0.1.2
+
+
+# Executive Summary
+
+The purpose of this document is to describe how to use the mbed OS SDCard
+driver (sd-driver) so applications can read/write
+data to flash storage cards using the standard POSIX File API
+programming interface. The sd-driver uses the SDCard SPI-mode of operation
+which is a subset of possible SDCard functionality.
+
+This repository contains the mbed-os SDCard driver for generic SPI
+SDCard support and other resources, as outlined below:
+
+- `SDBlockDevice.h` and `SDBlockDevice.cpp`. This is the SDCard driver module presenting
+  a Block Device API (derived from BlockDevice) to the underlying SDCard.
+- POSIX File API test cases for testing the FAT32 filesystem on SDCard.
+    - basic.cpp, a basic set of functional test cases.
+    - fopen.cpp, more functional tests reading/writing greater volumes of data to SDCard, for example.
+- `mbed_lib.json` mbed-os application configuration file with SPI pin configurations for the CI shield and overrides for specific targets.
+   This file allows the SPI pins to be specified for the target without having to edit the implementation files.
+- This README which includes [Summary of POSIX File API Documentation](#summary-posix-api-documentation)
+  including detailed instruction on how to use the FAT filesystem and SDBlockDevice driver.
+
+The SDCard driver is maintained in this repository as a component separate from the main mbed OS repository.
+Hence the 2 repositories (mbed-os and sd-driver) have to be used together
+to deliver the FAT32 Filesystem/SDCard support. This document explains how to do this.
+
+
+# Introduction
+
+### Overview
+
+The scope of this document is to describe how applications use the FAT filesystem and sd-driver
+components to persistently store data on SDCards. The document is intended to help developers adopt the
+mbed OS POSIX File API support, and in particular to help explain:
+
+- How the software components work together to deliver the storage functionality.
+- How to work with the sd-driver and mbed OS to build the examples. The example code can easily
+  be copied into your new application code.
+- How to work with the CI Test Shield, which adds an SDCard slot to those targets that do not have already have one.
+- How to run the POSIX File API mbed Greentea test cases, which provide further example code of how to use
+  the POSIX File API.
+
+Section 1 provides an Executive Summary, describing the purpose of the sd-driver, the supporting
+software, examples, test cases and documentation.
+
+Section 2 provides an an overview of the material covered including descriptions of the major sections.
+
+Section 3 provides an overview of the mbed OS filesystem software components,
+including the inter-relationships between the application, POSIX file API, the standard c-library,
+the mbed OS filesystem and the SDCard driver (sd-driver).
+
+Section 4 describes how to build and run an example application for reading
+and writing data to an SDCard using the POSIX File API. The example begins by describing
+the procedure for building and testing on the K64F target. The final sub-sections
+describe how to use the test shield to add an SDCard slot to any mbed target,
+and hence enable the persistent storage of data on any supported target.
+
+Section 5 describes an example application which uses the raw
+BlockDevice API to read and write data to the SDCard.
+
+Section 6 describes how to build and run the SDCard POSIX File API mbed Greentea test cases.
+There are a number of functional test cases demonstrating how to use the
+mbed OS POSIX File API.
+
+Section 7 describes the POSIX File API and provides links to useful API documentation web pages.
+
+
+### Known mbed-os and sd-driver Compatible Versions
+
+The following versions of the mbed-os and sd-driver repositories are known to work together:
+
+- {mbed-os, sd-driver} = {mbed-os-5.4.0-rc2, sd-driver-0.0.1-mbed-os-5.4.0-rc2}.
+  `K64F`, `NUCLEO_F429ZI` and `UBLOX_EVK_ODIN_W2` fopen and basic filesystem tests working.
+- {mbed-os, sd-driver} = {mbed-os-5.4.0, sd-driver-0.0.2-mbed-os-5.4.0}.
+  `K64F`, `NUCLEO_F429ZI` and `UBLOX_EVK_ODIN_W2` fopen and basic filesystem tests working.
+- {mbed-os, sd-driver} = {mbed-os-5.4.1, sd-driver-0.0.3-mbed-os-5.4.1}.
+- {mbed-os, sd-driver} = {mbed-os-5.5.1, sd-driver-0.1.0-mbed-os-5.5.1}.
+- {mbed-os, sd-driver} = {mbed-os-5.5.4, sd-driver-0.1.1-mbed-os-5.5.4}.
+- {mbed-os, sd-driver} = {mbed-os-5.6.1, sd-driver-0.1.2-mbed-os-5.6.1}.
+
+To find the latest compatible versions, use the following command to see the messages attached to the tags
+in the sd-driver repository:
+
+    ex_app7/$ cd sd-driver
+    ex_app7/sd-driver$ git tag -n
+    sd-driver-0.0.1-mbed-os-5.3.4 Version compatible with mbed-os-5.3.4, and private_mbedos_filesystems-0.0.1-mbed-os-5.3.4.
+    sd-driver-0.0.2-mbed-os-5.4.0 Updated README.md to include worked exmaples and restructuring of information.
+    sd-driver-0.0.3-mbed-os-5.4.1 Version compatible with mbed-os-5.4.1.
+    sd-driver-0.1.1-mbed-os-5.5.4 Version compatible with mbed-os-5.5.4
+    sd-driver-0.1.2-mbed-os-5.6.1  Version compatible with mbed-os-5.6.1
+
+
+### Known Issues With This Document
+
+There are no known issues with this document.
+
+
+# Overview of mbed OS Filesystem Software Component Stack
+
+
+    ------------------------
+    |                      |
+    |    Application       |        // This application uses the POSIX File API
+    |                      |        // to read/write data to persistent storage backends.
+    ------------------------
+
+    ------------------------        // POSIX File API (ISO).
+
+    ------------------------
+    |                      |
+    |     libc             |        // The standard c library implementation
+    |                      |        // e.g. newlib.
+    ------------------------
+
+    ------------------------        // sys_xxx equivalent API.
+
+    ------------------------
+    |                      |
+    |  mbed_retarget.cpp   |        // Target specific mapping layer.
+    |                      |
+    ------------------------
+
+    ------------------------        // Filesystem Upper Edge API.
+
+    ------------------------
+    |                      |
+    |     File System      |        // File system wrappers and implementation.
+    |                      |
+    ------------------------
+
+    ------------------------        // FS Lower Edge API (Block Store Interface).
+
+    ------------------------
+    |    Block API         |
+    |    Device Driver     |        // The SDCard driver, for example.
+    |  e.g. sd-driver      |
+    ------------------------
+
+    ------------------------        // SPI.h interface.
+
+    ------------------------
+    |                      |
+    |       SPI            |        // SPI subsystem (C++ classes and C-HAL implementation).
+    |                      |
+    ------------------------
+
+    Figure 1. mbedOS generic architecture of filesystem software stack.
+
+The figure above shows the mbed OS software component stack used for data
+storage on SDCard:
+
+- At the top level is the application component which uses the standard POSIX File API
+  to read and write application data to persistent storage.
+- The newlib standard library (libc) stdio.h interface (POSIX File API)
+  implementation is used as it's optimised for resource limited embedded systems.
+- mbed_retarget.cpp implements the libc back-end file OS handlers and maps them
+  to the FileSystem.
+- The File System code (hosted in mbed-os) is composed of 2 parts:
+    - The mbed OS file system wrapper classes (e.g. FileSystem, File, FileBase classes)
+      which are used to present a consistent API to the retarget module for different
+      (third-party) file system implementations.
+    - The FAT filesystem implementation code.
+      The [FATFS: Generic FAT File System Module](http://elm-chan.org/fsw/ff/00index_e.html)
+      (ChanFS) has been integrated within mbed-os.
+- The Block API Device Driver. The SDCard driver is an example of a persistent storage driver.
+  It's maintained as a separate component from the mbed OS repository (in this repository).
+- The SPI module provides the mbed OS generic SPI API. This functionality is maintained in
+  mbed OS.
+
+
+# SDCard POSIX File API Example App for Reading/Writing Data
+
+Refer to [SD driver Example](https://github.com/ARMmbed/mbed-os-example-sd-driver)
+
+
+### <a name="testing-with-an-sdcard-on-target-xyx"></a> Testing with an SDCard on Target XYZ
+
+The standard way to test is with the mbed CI Test Shield plugged into the
+target board. This pin mapping for this configuration is parameterised in
+the `mbed_lib.json` file.
+
+The following is an example of the `mbed_lib.json` file available in the repository:
+
+    {
+        "config": {
+            "SPI_CS": "D10",
+            "SPI_MOSI": "D11",
+            "SPI_MISO": "D12",
+            "SPI_CLK": "D13",
+            "DEVICE_SPI": 1,
+            "FSFAT_SDCARD_INSTALLED": 1
+        },
+        "target_overrides": {
+            "DISCO_F051R8": {
+                 "SPI_MOSI": "SPI_MOSI",
+                 "SPI_MISO": "SPI_MISO",
+                 "SPI_CLK":  "SPI_SCK",
+                 "SPI_CS":   "SPI_CS"
+            },
+            "KL46Z": {
+                 "SPI_MOSI": "PTD6",
+                 "SPI_MISO": "PTD7",
+                 "SPI_CLK":  "PTD5",
+                 "SPI_CS":   "PTD4"
+            },
+            "K64F": {
+                 "SPI_MOSI": "PTE3",
+                 "SPI_MISO": "PTE1",
+                 "SPI_CLK":  "PTE2",
+                 "SPI_CS":   "PTE4"
+            }
+    }
+
+Note the following things about the `mbed_lib.json` file:
+
+- The `mbed_lib.json` file is used to define target specific symbols for the SPI pins connecting the SDCard slot to the target MCU:
+    - "SPI\_CS". This is the Chip Select line.
+    - "SPI\_MOSI". This is the Master Out Slave In data line.
+    - "SPI\_MISO". This is the Master In Slave Out data line.
+    - "SPI\_CLK".  This is the serial Clock line.
+- The default configuration defined in the "config" section is for the standard Arduino header pin mappings for the SPI bus.
+  The "config" section defines a dictionary mapping functional names to target board Arduino header pins:
+    - "SPI\_CS": "D10". This causes the MBED\_CONF\_APP\_SPI\_CS symbol to be defined in mbed\_config.h as D10, which is used in the filesystem test implementation.
+      D10 is defined in the target specific PinNames.h file.
+    - "SPI\_MOSI": "D11". This causes the MBED\_CONF\_APP\_SPI\_MOSI symbol to be defined in mbed\_config.h.
+    - "SPI\_MISO": "D12". This causes the MBED\_CONF\_APP\_SPI\_MISO symbol to be defined in mbed\_config.h.
+    - "SPI\_CLK": "D13". This causes the MBED\_CONF\_APP\_SPI\_CLK symbol to be defined in mbed\_config.h.
+- The `"target_overrides"` section is used to override the "SPI\_xxx" symbols for specific target boards, which may have an SDCard slot, for example.
+  This is the case for the K64F, where the "SPI\_xxx" are mapped to the pin names for the on-board SDCard.
+
+  ```
+    "K64F": {
+         "SPI_MOSI": "PTE3",
+         "SPI_MISO": "PTE1",
+         "SPI_CLK":  "PTE2",
+         "SPI_CS":   "PTE4"
+    }
+    ```
+- Thus, in the absence of any target specific definitions in the `"target_overrides"` section, all boards will default to
+  using the Arduino header configuration. For those platforms with a `"target_overrides"` section then this configuration
+  will be used in preference.
+- Hence in the case that you want to test a platform with an SDCard inserted into a
+  fitted CI test shield (rather than the on-board SDCard slot)
+  and there is a `"target_overrides"` section present in the `mbed_lib.json` file, you must then delete the `"target_overrides"`
+  section before building. This will result in the default configuration being used (suitable for the CI
+  Test Shield).
+-  Note when inserting the v1.0.0 CI Test Shield into the Arduino header of the target platform, the shield pins D0 and
+  D1 should be bent to be parallel to the shield PCB so they are not inserted into the Arduino header. This is because
+  some boards use the same UART on DAPLINK and D0/D1, which means the serial debug channel breaks and hence the mbed greentea
+  test suite will not work correctly. This is mainly on older ST boards and should not be a problem on
+  `K64F`, `NUCLEO_F429ZI` and `UBLOX_EVK_ODIN_W2`. Note also that the v2.0.0 CI Test Shield doesn't suffer from this
+  problem and the pins don't need to be bent.
+- When inserting the SDCard into the card slot on the CI test shield, make sure the card is fully inserted.
+  On insertion, there should be a small clicking sound when the card registers, and the back edge of the card
+  should protrude no more than ~1mm over the edge of the CI test shield PCB. If the SDCard fails to register,
+  try gently pushing the metal flexible strip in the shape of a spade at the top edge of the SDCard metal slot
+  casing with a pair of tweezers, bending it a little to lower it into the slot casing. This helps with the
+  insertion mechanism.
+
+### Wiring instructions for target NUCLEO_F429ZI with CI Test Shield
+![alt text](docs/pics/NUCLEO_F429ZI_wiring_with_ci_test_shield.png "unseen title text")
+
+**Figure 3. The figure shows how to connect the NUCLEO_F429ZI platform with the CI shield.**
+
+The above figure shows how to connect the NUCLEO_F429ZI with the v1.0.0 CI test shield. Note:
+
+- To get the SD Card to work with this platform the CI test shield cannot be connected directly to this board, instead follow the instructions above.
+- Any SD-card adapter will work as long as you connect all the relevant pins (MOSI, MISO, SCLK, CS, 3.3V and GND) as illustrated in figure 3.
+- The SDCard is fully inserted into the slot and overhangs the PCB by ~1mm.
+
+# SDBlockDevice Example Application
+
+The following sample code illustrates how to use the sd-driver Block Device API:
+
+``` cpp
+#include "mbed.h"
+#include "SDBlockDevice.h"
+
+// Instantiate the SDBlockDevice by specifying the SPI pins connected to the SDCard
+// socket. The PINS are:
+//     MOSI (Master Out Slave In)
+//     MISO (Master In Slave Out)
+//     SCLK (Serial Clock)
+//     CS (Chip Select)
+SDBlockDevice sd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
+uint8_t block[512] = "Hello World!\n";
+
+int main()
+{
+    // call the SDBlockDevice instance initialisation method.
+    if ( 0 != sd.init()) {
+        printf("Init failed \n");
+        return -1;
+    }
+    printf("sd size: %llu\n",         sd.size());
+    printf("sd read size: %llu\n",    sd.get_read_size());
+    printf("sd program size: %llu\n", sd.get_program_size());
+    printf("sd erase size: %llu\n",   sd.get_erase_size());
+
+    // set the frequency
+    if ( 0 != sd.frequency(5000000)) {
+        printf("Error setting frequency \n");
+    }
+
+    if ( 0 != sd.erase(0, sd.get_erase_size())) {
+        printf("Error Erasing block \n");
+    }
+
+    // Write some the data block to the device
+    if ( 0 == sd.program(block, 0, 512)) {
+        // read the data block from the device
+        if ( 0 == sd.read(block, 0, 512)) {
+            // print the contents of the block
+            printf("%s", block);
+        }
+    }
+
+    // call the SDBlockDevice instance de-initialisation method.
+    sd.deinit();
+}
+```
+
+# SDCard POSIX File API mbed Greentea Test Cases
+
+This section describes how to build and run the POSIX file API test cases.
+The following steps are covered:
+
+- [Create the FAT/SDCard Application Project](#create-fat-sdcard-application-project).
+  This section describes how to git clone the mbed OS and sd-driver repositories containing the
+  code and test cases of interest.
+- [Build the mbed OS Test Cases](#build-the-mbedos-test-cases). This section
+  describes how to build the mbed OS test cases.
+- [Insert a microSD Card Into the K64F for Greentea Testing](#greentea-insert-sdcard-into-k64f).This section
+  describes how to format (if required) a microSD card prior to running the tests.
+- [Run the POSIX File Test Case](#run-the-posix-file-test-cases).This section
+  describes how to run the POSIX file test cases.
+
+
+### <a name="create-fat-sdcard-application-project"></a> Create the FAT/SDCard Application Project
+
+This section describes how to create an application project combining the mbed-os and
+sd-driver repositories into a single project.
+In summary the following steps will be covered in this section:
+
+- A top level application project directory is created. The directory name is ex_app1.
+- In the ex_app1 directory, the mbed-os repository is cloned.
+- In the ex_app1 directory at the same level as the mbed-os directory, the sd-driver repository is cloned.
+- The `mbed_lib.json` file is copied from the `sd-driver/config/mbed_lib.json` to the ex_app1 directory.
+
+First create the top level application directory ex_app1 and move into it:
+
+    shell:/d/demo_area$ mkdir ex_app1
+    shell:/d/demo_area$ pushd ex_app1
+
+Next, get a clone of public mbed OS repository in the following way:
+
+    shell:/d/demo_area/ex_app1$ git clone git@github.com:/armmbed/mbed-os
+    <trace removed>
+    shell:/d/demo_area/ex_app1$
+
+Next, get a clone of the sd-driver repository:
+
+    shell:/d/demo_area/ex_app1$ git clone git@github.com:/armmbed/sd-driver
+    <trace removed>
+    shell:/d/demo_area/ex_app1$
+
+Note: The `mbed_lib.json` file specifies the SPI bus pin configuration for different targets,
+and is discussed in the [Testing with an SDCard on Target XYZ](#testing-with-an-sdcard-on-target-xyx) section.
+
+### <a name="build-the-mbedos-test-cases"></a> Build the mbed OS Test Cases
+
+Build the test cases for the K64F target using the following command:
+
+    shell:/d/demo_area/ex_app1$ mbed -v test --compile -t GCC_ARM -m K64F
+    <trace removed>
+    shell:/d/demo_area/ex_app1$
+
+The build trace is quite extensive but on a successful build you should see the following output at the end of the log:
+
+    Build successes:
+      * K64F::GCC_ARM::MBED-BUILD
+      * K64F::GCC_ARM::MBED-OS-FEATURES-FEATURE_LWIP-TESTS-MBEDMICRO-NET-CONNECTIVITY
+      <trace removed>
+      * K64F::GCC_ARM::MBED-OS-FEATURES-TESTS-FILESYSTEM-FAT_FILE_SYSTEM
+      * K64F::GCC_ARM::MBED-OS-FEATURES-TESTS-FILESYSTEM-HEAP_BLOCK_DEVICE
+      * K64F::GCC_ARM::MBED-OS-FEATURES-TESTS-FILESYSTEM-UTIL_BLOCK_DEVICE
+      <trace removed>
+      * K64F::GCC_ARM::SD-DRIVER-TESTS-BLOCK_DEVICE-BASIC
+      * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-BASIC
+      * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-DIRS
+      * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-FILES
+      * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-FOPEN
+      * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-PARALLEL
+      * K64F::GCC_ARM::SD-DRIVER-TESTS-FILESYSTEM-SEEK
+    
+    Build skips:
+      * K64F::GCC_ARM::MBED-OS-FEATURES-FEATURE_LWIP-TESTS-MBEDMICRO-NET-TCP_PACKET_PRESSURE
+      <trace removed>
+
+
+Notice the following tests in the sd-driver tree are listed above:
+
+- `SD-DRIVER-TESTS-BLOCK_DEVICE-BASIC`
+- `SD-DRIVER-TESTS-FILESYSTEM-BASIC`
+- `SD-DRIVER-TESTS-FILESYSTEM-DIRS`
+- `SD-DRIVER-TESTS-FILESYSTEM-FILES`
+- `SD-DRIVER-TESTS-FILESYSTEM-FOPEN`
+- `SD-DRIVER-TESTS-FILESYSTEM-PARALLEL`
+- `SD-DRIVER-TESTS-FILESYSTEM-SEEK`
+
+The FAT32/SDCard test cases are at following locations in the source code tree:
+
+    /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/basic/basic.cpp
+    /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/fopen/fopen.cpp
+    /d/demo_area/ex_app1/sd-driver/TESTS/block_device/basic/basic.cpp
+    /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/dirs/main.cpp
+    /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/files/main.cpp
+    /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/parallel/main.cpp
+    /d/demo_area/ex_app1/sd-driver/TESTS/filesystem/seek/main.cpp
+
+#### <a name="settting-repos-to-compatible-versions"></a> Setting mbed-os/sd-driver Repositories To Compatible Versions
+
+The sd-driver master HEAD and the mbed-os master HEAD should be compatible
+with one another and therefore no specific tagged versions need to be checked out.
+However, in the case that you experience problems building, checkout out the compatible
+tagged version of each repository, as shown below:
+
+    shell:/d/demo_area/ex_app1$ pushd mbed-os
+    shell:/d/demo_area/ex_app1$ git checkout tags/mbed-os-5.4.0
+    shell:/d/demo_area/ex_app1$ popd
+    shell:/d/demo_area/ex_app1$ pushd sd-driver
+    shell:/d/demo_area/ex_app1$ git checkout tags/sd-driver-0.0.2-mbed-os-5.4.0
+    shell:/d/demo_area/ex_app1$ popd
+
+In the above:
+
+- `mbed-os-5.4.0` should be replaced with the latest mbed-os release tag.
+-  For an mbed-os release tag `mbed-os-x.y.z`, use the equivalent sd-driver tag `sd-driver-a.b.c-mbed-os-x.y.z`
+   where `a.b.c` is the latest version code for the `mbed-os-x.y.z` tag.
+
+### <a name="greentea-insert-sdcard-into-k64f"></a> Insert SDCard into K64F for Greentea Testing
+
+See the previous section for [Insert SDCard into K64F](#insert-sdcard-into-k64f) for details.
+
+
+### <a name="run-the-posix-file-test-cases"></a> Run the POSIX File Test Case
+
+To setup for running the test cases, connect the K64F development board to your
+PC using a suitable USB cable.
+
+All tests can be run using the following command:
+
+    shell:/d/demo_area/ex_app1$ mbedgt -VS
+    <trace removed>
+
+However, it's possible to run a particular test case using the following form of the mbedgt command:
+
+    shell:/d/demo_area/ex_app1$ mbedgt -VS --test-by-names=<test-name>
+
+The names of the tests can be listed using:
+
+    shell:/d/demo_area/ex_app1$ mbedgt -VS --list
+
+For example, to run the basic test use:
+
+    shell:/d/demo_area/ex_app1$ mbedgt -VS --test-by-names=sd-driver-tests-filesystem-basic
+
+To run the fopen test use:
+
+    shell:/d/demo_area/ex_app1$ mbedgt -VS --test-by-names=sd-driver-tests-filesystem-fopen
+
+On a successful run, results similar to the following will be shown:
+
+    mbedgt: test suite report:
+    +--------------+---------------+-------------------------------------------+--------+--------------------+-------------+
+    | target       | platform_name | test suite                                | result | elapsed_time (sec) | copy_method |
+    +--------------+---------------+-------------------------------------------+--------+--------------------+-------------+
+    | K64F-GCC_ARM | K64F          | sd-driver-features-tests-filesystem-fopen | OK     | 151.46             | shell       |
+    +--------------+---------------+-------------------------------------------+--------+--------------------+-------------+
+    mbedgt: test suite results: 1 OK
+    mbedgt: test case report:
+    +--------------+---------------+------------------------------------+----------------------------------------------------------------------------------------+--------+--------+--------+--------------------+
+    | target       | platform_name | test suite                         | test case                                                                              | passed | failed | result | elapsed_time (sec) |
+    +--------------+---------------+------------------------------------+----------------------------------------------------------------------------------------+--------+--------+--------+--------------------+
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_01: fopen()/fwrite()/fclose() directories/file in multi-dir filepath. | 1      | 0      | OK     | 7.57               |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_02: fopen(r) pre-existing file try to write it.                       | 1      | 0      | OK     | 0.2                |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_03: fopen(w+) pre-existing file try to write it.                      | 1      | 0      | OK     | 0.41               |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_04: fopen() with a filename exceeding the maximum length.             | 1      | 0      | OK     | 0.11               |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_06: fopen() with bad filenames (minimal).                             | 1      | 0      | OK     | 0.1                |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_07: fopen()/errno handling.                                           | 1      | 0      | OK     | 0.07               |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_08: ferror()/clearerr()/errno handling.                               | 1      | 0      | OK     | 0.1                |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_09: ftell() handling.                                                 | 1      | 0      | OK     | 0.17               |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_10: remove() test.                                                    | 1      | 0      | OK     | 1.28               |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_11: rename().                                                         | 1      | 0      | OK     | 2.3                |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_12: opendir(), readdir(), closedir() test.                            | 1      | 0      | OK     | 3.57               |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_13: mkdir() test.                                                     | 1      | 0      | OK     | 1.21               |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_14: stat() test.                                                      | 1      | 0      | OK     | 1.47               |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_15: format() test.                                                    | 1      | 0      | OK     | 26.12              |
+    | K64F-GCC_ARM | K64F          | sd-driver-tests-filesystem-fopen   | FSFAT_FOPEN_TEST_16: write/check n x 25kB data files.                                  | 1      | 0      | OK     | 87.11              |
+    +--------------+---------------+------------------------------------+----------------------------------------------------------------------------------------+--------+--------+--------+--------------------+
+    mbedgt: test case results: 15 OK
+    mbedgt: completed in 152.35 sec
+
+
+# <a name="summary-posix-api-documentation"></a> Summary of POSIX File API Documentation
+
+### POSIX File API
+
+mbed OS supports a subset of the POSIX File API, as outlined below:
+
+- [clearerr()](https://linux.die.net/man/3/clearerr).
+    - STATUS: Basic testing implemented. Working.
+- [fclose()](https://linux.die.net/man/3/fclose).
+    - STATUS: Basic testing implemented. Working.
+- [ferror()](https://linux.die.net/man/3/clearerr).
+    - STATUS: Basic testing implemented.
+    - STATUS: GCC_ARM: Working.
+    - STATUS: ARMCC: ARMCC has problem with ferror(filep) where filep is NULL. Appears to work for non-NULL pointer.
+- [fgetc()](https://linux.die.net/man/3/fgets).
+    - STATUS: Basic testing implemented. Working.
+- [fgets()](https://linux.die.net/man/3/fgets).
+    - STATUS: Basic testing implemented. Working.
+- [fputc()](https://linux.die.net/man/3/fputs).
+    - STATUS: Unknown.
+- [fputs()](https://linux.die.net/man/3/fputs).
+    - STATUS: Basic testing implemented. Working.
+- [fprintf()](https://linux.die.net/man/3/fprintf).
+    - STATUS: Basic testing implemented. Working.
+- [fopen()](https://linux.die.net/man/3/fopen).
+    - STATUS: Basic testing implemented. Working.
+- [freopen()](https://linux.die.net/man/3/fopen).
+    - STATUS: This is not tested.
+- [fread()](https://linux.die.net/man/3/fread).
+    - STATUS: Basic testing implemented. Working.
+    - STATUS: n x 25kB stress test working.
+- [ftell()](https://linux.die.net/man/3/ftell).
+    - STATUS: Basic testing implemented. Working.
+- [fwrite()](https://linux.die.net/man/3/fwrite).
+    - STATUS: Basic testing implemented. Working.
+    - STATUS: n x 25kB stress test working.
+- [fseek()](https://linux.die.net/man/3/fseek)
+    - STATUS: Basic testing implemented. Working.
+- [getc()](https://linux.die.net/man/3/fgets).
+    - STATUS: Basic testing implemented. Working.
+- [gets()](https://linux.die.net/man/3/fgets).
+    - STATUS: Unknown.
+- [putc()](https://linux.die.net/man/3/fputs).
+    - STATUS: Unknown.
+- [puts()](https://linux.die.net/man/3/fputs).
+    - STATUS: Unknown.
+- [remove()](https://linux.die.net/man/3/remove)
+    - STATUS: Basic testing implemented. Working.
+- [rewind()](https://linux.die.net/man/3/rewind).
+    - STATUS: Basic testing implemented. Working.
+- [stat()](https://linux.die.net/man/2/stat)
+    - STATUS: Implemented. Working.
+    - STATUS: Not supported by ARMCC/IAR libc.
+- [tmpfile()](https://linux.die.net/man/3/tmpfile).
+    - STATUS: Not implemented.
+- [tmpnam()](https://linux.die.net/man/3/tmpnam).
+    - STATUS: Not implemented.
+
+Supported directory related operations are as follows:
+
+- [closedir()](https://linux.die.net/man/3/closedir).
+    - STATUS: Implemented. Working.
+- [mkdir()](https://linux.die.net/man/3/mkdir).
+    - STATUS: Basic testing implemented. Working.
+- [opendir()](https://linux.die.net/man/3/opendir).
+    - STATUS: Implemented. Working.
+- [readdir()](https://linux.die.net/man/3/readdir).
+    - STATUS: Implemented. Working.
+- [remove()](https://linux.die.net/man/3/remove).
+    - STATUS: Basic testing implemented. Working.
+- [rename()](https://linux.die.net/man/3/rename).
+    - STATUS: Implemented. Not tested.
+- [rewinddir()](https://linux.die.net/man/3/rewinddir).
+    - STATUS: Implemented. Found not to work. Test case not present in repo.
+- [seekdir()](https://linux.die.net/man/3/seekdir).
+    - STATUS: Implemented. Found not to work. Test case not present in repo.
+- [telldir()](https://linux.die.net/man/3/telldir).
+    - STATUS: Implemented. Found not to work. Test case not present in repo.
+
+### errno
+
+Basic errno reporting is supported, tested and known to be working.
+
+
+# Related Projects Resources
+
+The following are related mbed storage projects and useful resources:
+
+- The [mbed-os](https://github.com/ARMmbed/mbed-os) main repository.
+- The [mbed-os-example-fat-filesystem](https://github.com/ARMmbed/mbed-os-example-fat-filesystem) repository.
+  This is an example project for the mbed OS FAT filesystem.
+- The [spiflash-driver](https://github.com/armmbed/spiflash-driver) repository.
+- The [i2ceeprom-driver](https://github.com/ARMmbed/i2ceeprom-driver.git) repository.
+- The [ci-test-shield](https://github.com/ARMmbed/ci-test-shield)  repository. This is the project describing
+  the mbed-os Continuous Integration test shield, together with standard tests.
+- The [mbed-HDK](https://github.com/ARMmbed/mbed-HDK) repository containing Hardware Development Kit resources
+  including the schematics for the CI test shield.
+- [POSIX File Interface ISO/IEC 9899:TC2 Documentation](http://www.eng.utah.edu/~cs5785/slides-f10/n1124.pdf).
+- [FATFS: Generic FAT File System Module used in mbed OS](http://elm-chan.org/fsw/ff/00index_e.html)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/SDBlockDevice.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,1004 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2013 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Introduction
+ * ------------
+ * SD and MMC cards support a number of interfaces, but common to them all
+ * is one based on SPI. Since we already have the mbed SPI Interface, it will
+ * be used for SD cards.
+ *
+ * The main reference I'm using is Chapter 7, "SPI Mode" of:
+ *  http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf
+ *
+ * SPI Startup
+ * -----------
+ * The SD card powers up in SD mode. The start-up procedure is complicated
+ * by the requirement to support older SDCards in a backwards compatible
+ * way with the new higher capacity variants SDHC and SDHC.
+ *
+ * The following figures from the specification with associated text describe
+ * the SPI mode initialisation process:
+ *  - Figure 7-1: SD Memory Card State Diagram (SPI mode)
+ *  - Figure 7-2: SPI Mode Initialization Flow
+ *
+ * Firstly, a low initial clock should be selected (in the range of 100-
+ * 400kHZ). After initialisation has been completed, the switch to a
+ * higher clock speed can be made (e.g. 1MHz). Newer cards will support
+ * higher speeds than the default _transfer_sck defined here.
+ *
+ * Next, note the following from the SDCard specification (note to
+ * Figure 7-1):
+ *
+ *  In any of the cases CMD1 is not recommended because it may be difficult for the host
+ *  to distinguish between MultiMediaCard and SD Memory Card
+ *
+ * Hence CMD1 is not used for the initialisation sequence.
+ *
+ * The SPI interface mode is selected by asserting CS low and sending the
+ * reset command (CMD0). The card will respond with a (R1) response.
+ * In practice many cards initially respond with 0xff or invalid data
+ * which is ignored. Data is read until a valid response is received
+ * or the number of re-reads has exceeded a maximim count. If a valid
+ * response is not received then the CMD0 can be retried. This
+ * has been found to successfully initialise cards where the SPI master
+ * (on MCU) has been reset but the SDCard has not, so the first
+ * CMD0 may be lost.
+ *
+ * CMD8 is optionally sent to determine the voltage range supported, and
+ * indirectly determine whether it is a version 1.x SD/non-SD card or
+ * version 2.x. I'll just ignore this for now.
+ *
+ * ACMD41 is repeatedly issued to initialise the card, until "in idle"
+ * (bit 0) of the R1 response goes to '0', indicating it is initialised.
+ *
+ * You should also indicate whether the host supports High Capicity cards,
+ * and check whether the card is high capacity - i'll also ignore this
+ *
+ * SPI Protocol
+ * ------------
+ * The SD SPI protocol is based on transactions made up of 8-bit words, with
+ * the host starting every bus transaction by asserting the CS signal low. The
+ * card always responds to commands, data blocks and errors.
+ *
+ * The protocol supports a CRC, but by default it is off (except for the
+ * first reset CMD0, where the CRC can just be pre-calculated, and CMD8)
+ * I'll leave the CRC off I think!
+ *
+ * Standard capacity cards have variable data block sizes, whereas High
+ * Capacity cards fix the size of data block to 512 bytes. I'll therefore
+ * just always use the Standard Capacity cards with a block size of 512 bytes.
+ * This is set with CMD16.
+ *
+ * You can read and write single blocks (CMD17, CMD25) or multiple blocks
+ * (CMD18, CMD25). For simplicity, I'll just use single block accesses. When
+ * the card gets a read command, it responds with a response token, and then
+ * a data token or an error.
+ *
+ * SPI Command Format
+ * ------------------
+ * Commands are 6-bytes long, containing the command, 32-bit argument, and CRC.
+ *
+ * +---------------+------------+------------+-----------+----------+--------------+
+ * | 01 | cmd[5:0] | arg[31:24] | arg[23:16] | arg[15:8] | arg[7:0] | crc[6:0] | 1 |
+ * +---------------+------------+------------+-----------+----------+--------------+
+ *
+ * As I'm not using CRC, I can fix that byte to what is needed for CMD0 (0x95)
+ *
+ * All Application Specific commands shall be preceded with APP_CMD (CMD55).
+ *
+ * SPI Response Format
+ * -------------------
+ * The main response format (R1) is a status byte (normally zero). Key flags:
+ *  idle - 1 if the card is in an idle state/initialising
+ *  cmd  - 1 if an illegal command code was detected
+ *
+ *    +-------------------------------------------------+
+ * R1 | 0 | arg | addr | seq | crc | cmd | erase | idle |
+ *    +-------------------------------------------------+
+ *
+ * R1b is the same, except it is followed by a busy signal (zeros) until
+ * the first non-zero byte when it is ready again.
+ *
+ * Data Response Token
+ * -------------------
+ * Every data block written to the card is acknowledged by a byte
+ * response token
+ *
+ * +----------------------+
+ * | xxx | 0 | status | 1 |
+ * +----------------------+
+ *              010 - OK!
+ *              101 - CRC Error
+ *              110 - Write Error
+ *
+ * Single Block Read and Write
+ * ---------------------------
+ *
+ * Block transfers have a byte header, followed by the data, followed
+ * by a 16-bit CRC. In our case, the data will always be 512 bytes.
+ *
+ * +------+---------+---------+- -  - -+---------+-----------+----------+
+ * | 0xFE | data[0] | data[1] |        | data[n] | crc[15:8] | crc[7:0] |
+ * +------+---------+---------+- -  - -+---------+-----------+----------+
+ */
+
+/* If the target has no SPI support then SDCard is not supported */
+#ifdef DEVICE_SPI
+
+#include "SDBlockDevice.h"
+#include "mbed_debug.h"
+#include <errno.h>
+
+/* Required version: 5.6.1 and above */
+#if defined(MBED_MAJOR_VERSION) && MBED_MAJOR_VERSION >= 5
+#if (MBED_VERSION < MBED_ENCODE_VERSION(5,6,1))
+#error "Incompatible mbed-os version detected! Required 5.6.1 and above"
+#endif
+#else
+#warning "mbed-os version 5.6.1 or above required"
+#endif
+
+#define SD_COMMAND_TIMEOUT                       5000   /*!< Timeout in ms for response */
+#define SD_CMD0_GO_IDLE_STATE_RETRIES            5      /*!< Number of retries for sending CMDO */
+#define SD_DBG                                   0      /*!< 1 - Enable debugging */
+#define SD_CMD_TRACE                             0      /*!< 1 - Enable SD command tracing */
+
+#define SD_BLOCK_DEVICE_ERROR_WOULD_BLOCK        -5001	/*!< operation would block */
+#define SD_BLOCK_DEVICE_ERROR_UNSUPPORTED        -5002	/*!< unsupported operation */
+#define SD_BLOCK_DEVICE_ERROR_PARAMETER          -5003	/*!< invalid parameter */
+#define SD_BLOCK_DEVICE_ERROR_NO_INIT            -5004	/*!< uninitialized */
+#define SD_BLOCK_DEVICE_ERROR_NO_DEVICE          -5005	/*!< device is missing or not connected */
+#define SD_BLOCK_DEVICE_ERROR_WRITE_PROTECTED    -5006	/*!< write protected */
+#define SD_BLOCK_DEVICE_ERROR_UNUSABLE           -5007  /*!< unusable card */
+#define SD_BLOCK_DEVICE_ERROR_NO_RESPONSE        -5008  /*!< No response from device */
+#define SD_BLOCK_DEVICE_ERROR_CRC                -5009  /*!< CRC error */
+#define SD_BLOCK_DEVICE_ERROR_ERASE              -5010  /*!< Erase error: reset/sequence */
+#define SD_BLOCK_DEVICE_ERROR_WRITE              -5011  /*!< SPI Write error: !SPI_DATA_ACCEPTED */
+
+#define BLOCK_SIZE_HC                            512    /*!< Block size supported for SD card is 512 bytes  */
+#define WRITE_BL_PARTIAL                         0      /*!< Partial block write - Not supported */
+#define CRC_SUPPORT                              0      /*!< CRC - Not supported */
+#define SPI_CMD(x) (0x40 | (x & 0x3f))
+
+/* R1 Response Format */
+#define R1_NO_RESPONSE          (0xFF)
+#define R1_RESPONSE_RECV        (0x80)
+#define R1_IDLE_STATE           (1 << 0)
+#define R1_ERASE_RESET          (1 << 1)
+#define R1_ILLEGAL_COMMAND      (1 << 2)
+#define R1_COM_CRC_ERROR        (1 << 3)
+#define R1_ERASE_SEQUENCE_ERROR (1 << 4)
+#define R1_ADDRESS_ERROR        (1 << 5)
+#define R1_PARAMETER_ERROR      (1 << 6)
+
+// Types
+#define SDCARD_NONE              0           /**< No card is present */
+#define SDCARD_V1                1           /**< v1.x Standard Capacity */
+#define SDCARD_V2                2           /**< v2.x Standard capacity SD card */
+#define SDCARD_V2HC              3           /**< v2.x High capacity SD card */
+#define CARD_UNKNOWN             4           /**< Unknown or unsupported card */
+
+/* SIZE in Bytes */
+#define PACKET_SIZE              6           /*!< SD Packet size CMD+ARG+CRC */
+#define R1_RESPONSE_SIZE         1           /*!< Size of R1 response */
+#define R2_RESPONSE_SIZE         2           /*!< Size of R2 response */
+#define R3_R7_RESPONSE_SIZE      5           /*!< Size of R3/R7 response */
+
+/* R1b Response */
+#define DEVICE_BUSY             (0x00)
+
+/* R2 Response Format */
+#define R2_CARD_LOCKED          (1 << 0)
+#define R2_CMD_FAILED           (1 << 1)
+#define R2_ERROR                (1 << 2)
+#define R2_CC_ERROR             (1 << 3)
+#define R2_CC_FAILED            (1 << 4)
+#define R2_WP_VIOLATION         (1 << 5)
+#define R2_ERASE_PARAM          (1 << 6)
+#define R2_OUT_OF_RANGE         (1 << 7)
+
+/* R3 Response : OCR Register */
+#define OCR_HCS_CCS             (0x1 << 30)
+#define OCR_LOW_VOLTAGE         (0x01 << 24)
+#define OCR_3_3V                (0x1 << 20)
+
+/* R7 response pattern for CMD8 */
+#define CMD8_PATTERN             (0xAA)
+
+/*  CRC Enable  */
+#define CRC_ENABLE               (0)         /*!< CRC 1 - Enable 0 - Disable */
+
+/* Control Tokens   */
+#define SPI_DATA_RESPONSE_MASK   (0x1F)
+#define SPI_DATA_ACCEPTED        (0x05)
+#define SPI_DATA_CRC_ERROR       (0x0B)
+#define SPI_DATA_WRITE_ERROR     (0x0D)
+#define SPI_START_BLOCK          (0xFE)      /*!< For Single Block Read/Write and Multiple Block Read */
+#define SPI_START_BLK_MUL_WRITE  (0xFC)      /*!< Start Multi-block write */
+#define SPI_STOP_TRAN            (0xFD)      /*!< Stop Multi-block write */
+
+#define SPI_DATA_READ_ERROR_MASK (0xF)       /*!< Data Error Token: 4 LSB bits */
+#define SPI_READ_ERROR           (0x1 << 0)  /*!< Error */
+#define SPI_READ_ERROR_CC        (0x1 << 1)  /*!< CC Error*/
+#define SPI_READ_ERROR_ECC_C     (0x1 << 2)  /*!< Card ECC failed */
+#define SPI_READ_ERROR_OFR       (0x1 << 3)  /*!< Out of Range */
+
+SDBlockDevice::SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs, uint64_t hz)
+    : _sectors(0), _spi(mosi, miso, sclk), _cs(cs), _is_initialized(0)
+{
+    _cs = 1;
+    _card_type = SDCARD_NONE;
+
+    // Set default to 100kHz for initialisation and 1MHz for data transfer
+    _init_sck = 100000;
+    _transfer_sck = hz;
+
+    // Only HC block size is supported.
+    _block_size = BLOCK_SIZE_HC;
+    _erase_size = BLOCK_SIZE_HC;
+}
+
+SDBlockDevice::~SDBlockDevice()
+{
+    if (_is_initialized) {
+        deinit();
+    }
+}
+
+int SDBlockDevice::_initialise_card()
+{
+    // Detail debugging is for commands
+    _dbg = SD_DBG ? SD_CMD_TRACE : 0;
+    int32_t status = BD_ERROR_OK;
+    uint32_t response, arg;
+
+    // Initialize the SPI interface: Card by default is in SD mode
+    _spi_init();
+
+    // The card is transitioned from SDCard mode to SPI mode by sending the CMD0 + CS Asserted("0")
+    if (_go_idle_state() != R1_IDLE_STATE) {
+        debug_if(SD_DBG, "No disk, or could not put SD card in to SPI idle state\n");
+        return SD_BLOCK_DEVICE_ERROR_NO_DEVICE;
+    }
+
+    // Send CMD8, if the card rejects the command then it's probably using the
+    // legacy protocol, or is a MMC, or just flat-out broken
+    status = _cmd8();
+    if (BD_ERROR_OK != status && SD_BLOCK_DEVICE_ERROR_UNSUPPORTED != status) {
+        return status;
+    }
+
+    // Read OCR - CMD58 Response contains OCR register
+    if (BD_ERROR_OK != (status = _cmd(CMD58_READ_OCR, 0x0, 0x0, &response))) {
+        return status;
+    }
+
+    // Check if card supports voltage range: 3.3V
+    if (!(response & OCR_3_3V)) {
+        _card_type = CARD_UNKNOWN;
+        status = SD_BLOCK_DEVICE_ERROR_UNUSABLE;
+        return status;
+    }
+
+    // HCS is set 1 for HC/XC capacity cards for ACMD41, if supported
+    arg = 0x0;
+    if (SDCARD_V2 == _card_type) {
+        arg |= OCR_HCS_CCS;
+    }
+
+    /* Idle state bit in the R1 response of ACMD41 is used by the card to inform the host
+     * if initialization of ACMD41 is completed. "1" indicates that the card is still initializing.
+     * "0" indicates completion of initialization. The host repeatedly issues ACMD41 until
+     * this bit is set to "0".
+     */
+    _spi_timer.start();
+    do {
+        status = _cmd(ACMD41_SD_SEND_OP_COND, arg, 1, &response);
+    } while ((response & R1_IDLE_STATE) && (_spi_timer.read_ms() < SD_COMMAND_TIMEOUT));
+    _spi_timer.stop();
+
+    // Initialization complete: ACMD41 successful
+    if ((BD_ERROR_OK != status) || (0x00 != response)) {
+        _card_type = CARD_UNKNOWN;
+        debug_if(SD_DBG, "Timeout waiting for card\n");
+        return status;
+    }
+
+    if (SDCARD_V2 == _card_type) {
+        // Get the card capacity CCS: CMD58
+        if (BD_ERROR_OK == (status = _cmd(CMD58_READ_OCR, 0x0, 0x0, &response))) {
+            // High Capacity card
+            if (response & OCR_HCS_CCS) {
+                _card_type = SDCARD_V2HC;
+                debug_if(SD_DBG, "Card Initialized: High Capacity Card \n");
+            } else {
+                debug_if(SD_DBG, "Card Initialized: Standard Capacity Card: Version 2.x \n");
+            }
+        }
+    } else {
+        _card_type = SDCARD_V1;
+        debug_if(SD_DBG, "Card Initialized: Version 1.x Card\n");
+    }
+
+    // Disable CRC
+    status = _cmd(CMD59_CRC_ON_OFF, 0);
+
+    return status;
+}
+
+
+int SDBlockDevice::init()
+{
+    lock();
+    int err = _initialise_card();
+    _is_initialized = (err == BD_ERROR_OK);
+    if (!_is_initialized) {
+        debug_if(SD_DBG, "Fail to initialize card\n");
+        unlock();
+        return err;
+    }
+    debug_if(SD_DBG, "init card = %d\n", _is_initialized);
+    _sectors = _sd_sectors();
+    // CMD9 failed
+    if (0 == _sectors) {
+        unlock();
+        return BD_ERROR_DEVICE_ERROR;
+    }
+
+    // Set block length to 512 (CMD16)
+    if (_cmd(CMD16_SET_BLOCKLEN, _block_size) != 0) {
+        debug_if(SD_DBG, "Set %d-byte block timed out\n", _block_size);
+        unlock();
+        return BD_ERROR_DEVICE_ERROR;
+    }
+
+    // Set SCK for data transfer
+    err = _freq();
+    if (err) {
+        unlock();
+        return err;
+    }
+    unlock();
+    return BD_ERROR_OK;
+}
+
+int SDBlockDevice::deinit()
+{
+    lock();
+    _is_initialized = false;
+    _sectors = 0;
+    unlock();
+    return 0;
+}
+
+
+int SDBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
+{
+    if (!is_valid_program(addr, size)) {
+        return SD_BLOCK_DEVICE_ERROR_PARAMETER;
+    }
+
+    lock();
+    if (!_is_initialized) {
+        unlock();
+        return SD_BLOCK_DEVICE_ERROR_NO_INIT;
+    }
+
+    const uint8_t *buffer = static_cast<const uint8_t*>(b);
+    int status = BD_ERROR_OK;
+    uint8_t response;
+
+    // Get block count
+    bd_addr_t blockCnt = size / _block_size;
+
+    // SDSC Card (CCS=0) uses byte unit address
+    // SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit)
+    if(SDCARD_V2HC == _card_type) {
+        addr = addr / _block_size;
+    }
+
+    // Send command to perform write operation
+    if (blockCnt == 1) {
+        // Single block write command
+        if (BD_ERROR_OK != (status = _cmd(CMD24_WRITE_BLOCK, addr))) {
+            unlock();
+            return status;
+        }
+
+        // Write data
+        response = _write(buffer, SPI_START_BLOCK, _block_size);
+
+        // Only CRC and general write error are communicated via response token
+        if ((response == SPI_DATA_CRC_ERROR) || (response == SPI_DATA_WRITE_ERROR)) {
+            debug_if(SD_DBG, "Single Block Write failed: 0x%x \n", response);
+            status = SD_BLOCK_DEVICE_ERROR_WRITE;
+        }
+    } else {
+        // Pre-erase setting prior to multiple block write operation
+        _cmd(ACMD23_SET_WR_BLK_ERASE_COUNT, blockCnt, 1);
+
+        // Multiple block write command
+        if (BD_ERROR_OK != (status = _cmd(CMD25_WRITE_MULTIPLE_BLOCK, addr))) {
+            unlock();
+            return status;
+        }
+
+        // Write the data: one block at a time
+        do {
+            response = _write(buffer, SPI_START_BLK_MUL_WRITE, _block_size);
+            if (response != SPI_DATA_ACCEPTED) {
+                debug_if(SD_DBG, "Multiple Block Write failed: 0x%x \n", response);
+                break;
+            }
+            buffer += _block_size;
+        }while (--blockCnt);     // Receive all blocks of data
+
+        /* In a Multiple Block write operation, the stop transmission will be done by
+         * sending 'Stop Tran' token instead of 'Start Block' token at the beginning
+         * of the next block
+         */
+        _spi.write(SPI_STOP_TRAN);
+    }
+
+    _deselect();
+    unlock();
+    return status;
+}
+
+int SDBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
+{
+    if (!is_valid_read(addr, size)) {
+        return SD_BLOCK_DEVICE_ERROR_PARAMETER;
+    }
+
+    lock();
+    if (!_is_initialized) {
+        unlock();
+        return SD_BLOCK_DEVICE_ERROR_PARAMETER;
+    }
+
+    uint8_t *buffer = static_cast<uint8_t *>(b);
+    int status = BD_ERROR_OK;
+    bd_addr_t blockCnt =  size / _block_size;
+
+    // SDSC Card (CCS=0) uses byte unit address
+    // SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit)
+    if (SDCARD_V2HC == _card_type) {
+        addr = addr / _block_size;
+    }
+
+    // Write command ro receive data
+    if (blockCnt > 1) {
+        status = _cmd(CMD18_READ_MULTIPLE_BLOCK, addr);
+    } else {
+        status = _cmd(CMD17_READ_SINGLE_BLOCK, addr);
+    }
+    if (BD_ERROR_OK != status) {
+        unlock();
+        return status;
+    }
+
+    // receive the data : one block at a time
+    while (blockCnt) {
+        if (0 != _read(buffer, _block_size)) {
+            status = SD_BLOCK_DEVICE_ERROR_NO_RESPONSE;
+            break;
+        }
+        buffer += _block_size;
+        --blockCnt;
+    }
+    _deselect();
+
+    // Send CMD12(0x00000000) to stop the transmission for multi-block transfer
+    if (size > _block_size) {
+        status = _cmd(CMD12_STOP_TRANSMISSION, 0x0);
+    }
+    unlock();
+    return status;
+}
+
+bool SDBlockDevice::_is_valid_trim(bd_addr_t addr, bd_size_t size)
+{
+    return (
+        addr % _erase_size == 0 &&
+        size % _erase_size == 0 &&
+        addr + size <= this->size());
+}
+
+int SDBlockDevice::trim(bd_addr_t addr, bd_size_t size)
+{
+    if (!_is_valid_trim(addr, size)) {
+        return SD_BLOCK_DEVICE_ERROR_PARAMETER;
+    }
+
+    lock();
+    if (!_is_initialized) {
+        unlock();
+        return SD_BLOCK_DEVICE_ERROR_NO_INIT;
+    }
+    int status = BD_ERROR_OK;
+
+    size -= _block_size;
+    // SDSC Card (CCS=0) uses byte unit address
+    // SDHC and SDXC Cards (CCS=1) use block unit address (512 Bytes unit)
+    if (SDCARD_V2HC == _card_type) {
+        size = size / _block_size;
+        addr = addr / _block_size;
+    }
+
+    // Start lba sent in start command
+    if (BD_ERROR_OK != (status = _cmd(CMD32_ERASE_WR_BLK_START_ADDR, addr))) {
+        unlock();
+        return status;
+    }
+
+    // End lba = addr+size sent in end addr command
+    if (BD_ERROR_OK != (status = _cmd(CMD33_ERASE_WR_BLK_END_ADDR, addr+size))) {
+        unlock();
+        return status;
+    }
+    status = _cmd(CMD38_ERASE, 0x0);
+    unlock();
+    return status;
+}
+
+bd_size_t SDBlockDevice::get_read_size() const
+{
+    return _block_size;
+}
+
+bd_size_t SDBlockDevice::get_program_size() const
+{
+    return _block_size;
+}
+
+bd_size_t SDBlockDevice::size() const
+{
+    return _block_size*_sectors;
+}
+
+void SDBlockDevice::debug(bool dbg)
+{
+    _dbg = dbg;
+}
+
+int SDBlockDevice::frequency(uint64_t freq)
+{
+    lock();
+    _transfer_sck = freq;
+    int err = _freq();
+    unlock();
+    return err;
+}
+
+// PRIVATE FUNCTIONS
+int SDBlockDevice::_freq(void)
+{
+    // Max frequency supported is 25MHZ
+    if (_transfer_sck <= 25000000) {
+        _spi.frequency(_transfer_sck);
+        return 0;
+    } else {  // TODO: Switch function to be implemented for higher frequency
+        _transfer_sck = 25000000;
+        _spi.frequency(_transfer_sck);
+        return -EINVAL;
+    }
+}
+
+uint8_t SDBlockDevice::_cmd_spi(SDBlockDevice::cmdSupported cmd, uint32_t arg) {
+    uint8_t response;
+    char cmdPacket[PACKET_SIZE];
+
+    // Prepare the command packet
+    cmdPacket[0] = SPI_CMD(cmd);
+    cmdPacket[1] = (arg >> 24);
+    cmdPacket[2] = (arg >> 16);
+    cmdPacket[3] = (arg >> 8);
+    cmdPacket[4] = (arg >> 0);
+    // CMD0 is executed in SD mode, hence should have correct CRC
+    // CMD8 CRC verification is always enabled
+    switch(cmd) {
+        case CMD0_GO_IDLE_STATE:
+            cmdPacket[5] = 0x95;
+            break;
+        case CMD8_SEND_IF_COND:
+            cmdPacket[5] = 0x87;
+            break;
+        default:
+            cmdPacket[5] = 0xFF;    // Make sure bit 0-End bit is high
+            break;
+    }
+
+    // send a command
+    for (int i = 0; i < PACKET_SIZE; i++) {
+        _spi.write(cmdPacket[i]);
+    }
+
+    // The received byte immediataly following CMD12 is a stuff byte,
+    // it should be discarded before receive the response of the CMD12.
+    if (CMD12_STOP_TRANSMISSION == cmd) {
+        _spi.write(SPI_FILL_CHAR);
+    }
+
+    // Loop for response: Response is sent back within command response time (NCR), 0 to 8 bytes for SDC
+    for (int i = 0; i < 0x10; i++) {
+        response = _spi.write(SPI_FILL_CHAR);
+        // Got the response
+        if (!(response & R1_RESPONSE_RECV)) {
+            break;
+        }
+    }
+    return response;
+}
+
+int SDBlockDevice::_cmd(SDBlockDevice::cmdSupported cmd, uint32_t arg, bool isAcmd, uint32_t *resp) {
+    int32_t status = BD_ERROR_OK;
+    uint32_t response;
+
+    // Select card and wait for card to be ready before sending next command
+    // Note: next command will fail if card is not ready
+    _select();
+
+    // No need to wait for card to be ready when sending the stop command
+    if (CMD12_STOP_TRANSMISSION != cmd) {
+        if (false == _wait_ready(SD_COMMAND_TIMEOUT)) {
+            debug_if(SD_DBG, "Card not ready yet \n");
+        }
+    }
+
+    // Re-try command
+    for(int i = 0; i < 3; i++) {
+        // Send CMD55 for APP command first
+        if (isAcmd) {
+            response = _cmd_spi(CMD55_APP_CMD, 0x0);
+            // Wait for card to be ready after CMD55
+            if (false == _wait_ready(SD_COMMAND_TIMEOUT)) {
+                debug_if(SD_DBG, "Card not ready yet \n");
+            }
+        }
+
+        // Send command over SPI interface
+        response = _cmd_spi(cmd, arg);
+        if (R1_NO_RESPONSE == response) {
+            debug_if(SD_DBG, "No response CMD:%d \n", cmd);
+            continue;
+        }
+        break;
+    }
+
+    // Pass the response to the command call if required
+    if (NULL != resp) {
+        *resp = response;
+    }
+
+    // Process the response R1  : Exit on CRC/Illegal command error/No response
+    if (R1_NO_RESPONSE == response) {
+        _deselect();
+        debug_if(SD_DBG, "No response CMD:%d response: 0x%x\n",cmd, response);
+        return SD_BLOCK_DEVICE_ERROR_NO_DEVICE;         // No device
+    }
+    if (response & R1_COM_CRC_ERROR) {
+        _deselect();
+        debug_if(SD_DBG, "CRC error CMD:%d response 0x%x \n",cmd, response);
+        return SD_BLOCK_DEVICE_ERROR_CRC;                // CRC error
+    }
+    if (response & R1_ILLEGAL_COMMAND) {
+        _deselect();
+        debug_if(SD_DBG, "Illegal command CMD:%d response 0x%x\n",cmd, response);
+        if (CMD8_SEND_IF_COND == cmd) {                  // Illegal command is for Ver1 or not SD Card
+            _card_type = CARD_UNKNOWN;
+        }
+        return SD_BLOCK_DEVICE_ERROR_UNSUPPORTED;      // Command not supported
+    }
+
+    debug_if(_dbg, "CMD:%d \t arg:0x%x \t Response:0x%x \n", cmd, arg, response);
+    // Set status for other errors
+    if ((response & R1_ERASE_RESET) || (response & R1_ERASE_SEQUENCE_ERROR)) {
+        status = SD_BLOCK_DEVICE_ERROR_ERASE;            // Erase error
+    }else if ((response & R1_ADDRESS_ERROR) || (response & R1_PARAMETER_ERROR)) {
+        // Misaligned address / invalid address block length
+        status = SD_BLOCK_DEVICE_ERROR_PARAMETER;
+    }
+
+    // Get rest of the response part for other commands
+    switch(cmd) {
+        case CMD8_SEND_IF_COND:             // Response R7
+            debug_if(_dbg, "V2-Version Card\n");
+            _card_type = SDCARD_V2;
+            // Note: No break here, need to read rest of the response
+        case CMD58_READ_OCR:                // Response R3
+            response  = (_spi.write(SPI_FILL_CHAR) << 24);
+            response |= (_spi.write(SPI_FILL_CHAR) << 16);
+            response |= (_spi.write(SPI_FILL_CHAR) << 8);
+            response |= _spi.write(SPI_FILL_CHAR);
+            debug_if(_dbg, "R3/R7: 0x%x \n", response);
+            break;
+
+        case CMD12_STOP_TRANSMISSION:       // Response R1b
+        case CMD38_ERASE:
+            _wait_ready(SD_COMMAND_TIMEOUT);
+            break;
+
+        case ACMD13_SD_STATUS:             // Response R2
+            response = _spi.write(SPI_FILL_CHAR);
+            debug_if(_dbg, "R2: 0x%x \n", response);
+            break;
+
+        default:                            // Response R1
+            break;
+    }
+
+    // Pass the updated response to the command
+    if (NULL != resp) {
+        *resp = response;
+    }
+
+    // Do not deselect card if read is in progress.
+    if (((CMD9_SEND_CSD == cmd) || (ACMD22_SEND_NUM_WR_BLOCKS == cmd) ||
+        (CMD24_WRITE_BLOCK == cmd) || (CMD25_WRITE_MULTIPLE_BLOCK == cmd) ||
+        (CMD17_READ_SINGLE_BLOCK == cmd) || (CMD18_READ_MULTIPLE_BLOCK == cmd))
+        && (BD_ERROR_OK == status)) {
+        return BD_ERROR_OK;
+    }
+    // Deselect card
+    _deselect();
+    return status;
+}
+
+int SDBlockDevice::_cmd8() {
+    uint32_t arg = (CMD8_PATTERN << 0);         // [7:0]check pattern
+    uint32_t response = 0;
+    int32_t status = BD_ERROR_OK;
+
+    arg |= (0x1 << 8);  // 2.7-3.6V             // [11:8]supply voltage(VHS)
+
+    status = _cmd(CMD8_SEND_IF_COND, arg, 0x0, &response);
+    // Verify voltage and pattern for V2 version of card
+    if ((BD_ERROR_OK == status) && (SDCARD_V2 == _card_type)) {
+        // If check pattern is not matched, CMD8 communication is not valid
+        if((response & 0xFFF) != arg)
+        {
+            debug_if(SD_DBG, "CMD8 Pattern mismatch 0x%x : 0x%x\n", arg, response);
+            _card_type = CARD_UNKNOWN;
+            status = SD_BLOCK_DEVICE_ERROR_UNUSABLE;
+        }
+    }
+    return status;
+}
+
+uint32_t SDBlockDevice::_go_idle_state() {
+    uint32_t response;
+
+    /* Reseting the MCU SPI master may not reset the on-board SDCard, in which
+     * case when MCU power-on occurs the SDCard will resume operations as
+     * though there was no reset. In this scenario the first CMD0 will
+     * not be interpreted as a command and get lost. For some cards retrying
+     * the command overcomes this situation. */
+    for (int i = 0; i < SD_CMD0_GO_IDLE_STATE_RETRIES; i++) {
+        _cmd(CMD0_GO_IDLE_STATE, 0x0, 0x0, &response);
+        if (R1_IDLE_STATE == response)
+            break;
+        wait_ms(1);
+    }
+    return response;
+}
+
+int SDBlockDevice::_read_bytes(uint8_t *buffer, uint32_t length) {
+    uint16_t crc;
+
+    // read until start byte (0xFE)
+    if (false == _wait_token(SPI_START_BLOCK)) {
+        debug_if(SD_DBG, "Read timeout\n");
+        _deselect();
+        return SD_BLOCK_DEVICE_ERROR_NO_RESPONSE;
+    }
+
+    // read data
+    for (uint32_t i = 0; i < length; i++) {
+        buffer[i] = _spi.write(SPI_FILL_CHAR);
+    }
+
+    // Read the CRC16 checksum for the data block
+    crc = (_spi.write(SPI_FILL_CHAR) << 8);
+    crc |= _spi.write(SPI_FILL_CHAR);
+
+    _deselect();
+    return 0;
+}
+
+int SDBlockDevice::_read(uint8_t *buffer, uint32_t length) {
+    uint16_t crc;
+
+    // read until start byte (0xFE)
+    if (false == _wait_token(SPI_START_BLOCK)) {
+        debug_if(SD_DBG, "Read timeout\n");
+        _deselect();
+        return SD_BLOCK_DEVICE_ERROR_NO_RESPONSE;
+    }
+
+    // read data
+    _spi.write(NULL, 0, (char*)buffer, length);
+
+    // Read the CRC16 checksum for the data block
+    crc = (_spi.write(SPI_FILL_CHAR) << 8);
+    crc |= _spi.write(SPI_FILL_CHAR);
+
+    return 0;
+}
+
+uint8_t SDBlockDevice::_write(const uint8_t *buffer, uint8_t token, uint32_t length) {
+    uint16_t crc = 0xFFFF;
+    uint8_t response = 0xFF;
+
+    // indicate start of block
+    _spi.write(token);
+
+    // write the data
+    _spi.write((char*)buffer, length, NULL, 0);
+
+    // write the checksum CRC16
+    _spi.write(crc >> 8);
+    _spi.write(crc);
+
+    // check the response token
+    response = _spi.write(SPI_FILL_CHAR);
+
+    // Wait for last block to be written
+    if (false == _wait_ready(SD_COMMAND_TIMEOUT)) {
+        debug_if(SD_DBG, "Card not ready yet \n");
+    }
+
+    return (response & SPI_DATA_RESPONSE_MASK);
+}
+
+static uint32_t ext_bits(unsigned char *data, int msb, int lsb) {
+    uint32_t bits = 0;
+    uint32_t size = 1 + msb - lsb;
+    for (uint32_t i = 0; i < size; i++) {
+        uint32_t position = lsb + i;
+        uint32_t byte = 15 - (position >> 3);
+        uint32_t bit = position & 0x7;
+        uint32_t value = (data[byte] >> bit) & 1;
+        bits |= value << i;
+    }
+    return bits;
+}
+
+bd_size_t SDBlockDevice::_sd_sectors() {
+    uint32_t c_size, c_size_mult, read_bl_len;
+    uint32_t block_len, mult, blocknr;
+    uint32_t hc_c_size;
+    bd_size_t blocks = 0, capacity = 0;
+
+    // CMD9, Response R2 (R1 byte + 16-byte block read)
+    if (_cmd(CMD9_SEND_CSD, 0x0) != 0x0) {
+        debug_if(SD_DBG, "Didn't get a response from the disk\n");
+        return 0;
+    }
+    uint8_t csd[16];
+    if (_read_bytes(csd, 16) != 0) {
+        debug_if(SD_DBG, "Couldn't read csd response from disk\n");
+        return 0;
+    }
+
+    // csd_structure : csd[127:126]
+    int csd_structure = ext_bits(csd, 127, 126);
+    switch (csd_structure) {
+        case 0:
+            c_size = ext_bits(csd, 73, 62);              // c_size        : csd[73:62]
+            c_size_mult = ext_bits(csd, 49, 47);         // c_size_mult   : csd[49:47]
+            read_bl_len = ext_bits(csd, 83, 80);         // read_bl_len   : csd[83:80] - the *maximum* read block length
+            block_len = 1 << read_bl_len;                // BLOCK_LEN = 2^READ_BL_LEN
+            mult = 1 << (c_size_mult + 2);               // MULT = 2^C_SIZE_MULT+2 (C_SIZE_MULT < 8)
+            blocknr = (c_size + 1) * mult;               // BLOCKNR = (C_SIZE+1) * MULT
+            capacity = blocknr * block_len;              // memory capacity = BLOCKNR * BLOCK_LEN
+            blocks = capacity / _block_size;
+            debug_if(SD_DBG, "Standard Capacity: c_size: %d \n", c_size);
+            debug_if(SD_DBG, "Sectors: 0x%x : %llu\n", blocks, blocks);
+            debug_if(SD_DBG, "Capacity: 0x%x : %llu MB\n", capacity, (capacity/(1024U*1024U)));
+
+            // ERASE_BLK_EN = 1: Erase in multiple of 512 bytes supported
+            if (ext_bits(csd, 46, 46)) {
+                _erase_size = BLOCK_SIZE_HC;
+            } else {
+                // ERASE_BLK_EN = 1: Erase in multiple of SECTOR_SIZE supported
+                _erase_size = BLOCK_SIZE_HC * (ext_bits(csd, 45, 39) + 1);
+            }
+            break;
+
+        case 1:
+            hc_c_size = ext_bits(csd, 69, 48);            // device size : C_SIZE : [69:48]
+            blocks = (hc_c_size+1) << 10;                 // block count = C_SIZE+1) * 1K byte (512B is block size)
+            debug_if(SD_DBG, "SDHC/SDXC Card: hc_c_size: %d \n", hc_c_size);
+            debug_if(SD_DBG, "Sectors: 0x%x : %llu\n", blocks, blocks);
+            debug_if(SD_DBG, "Capacity: %llu MB\n", (blocks/(2048U)));
+            // ERASE_BLK_EN is fixed to 1, which means host can erase one or multiple of 512 bytes.
+            _erase_size = BLOCK_SIZE_HC;
+            break;
+
+        default:
+            debug_if(SD_DBG, "CSD struct unsupported\r\n");
+            return 0;
+    };
+    return blocks;
+}
+
+// SPI function to wait till chip is ready and sends start token
+bool SDBlockDevice::_wait_token(uint8_t token) {
+    _spi_timer.reset();
+    _spi_timer.start();
+
+    do {
+        if (token == _spi.write(SPI_FILL_CHAR)) {
+            _spi_timer.stop();
+            return true;
+        }
+    } while (_spi_timer.read_ms() < 300);       // Wait for 300 msec for start token
+    _spi_timer.stop();
+    debug_if(SD_DBG, "_wait_token: timeout\n");
+    return false;
+}
+
+// SPI function to wait till chip is ready
+// The host controller should wait for end of the process until DO goes high (a 0xFF is received).
+bool SDBlockDevice::_wait_ready(uint16_t ms) {
+    uint8_t response;
+    _spi_timer.reset();
+    _spi_timer.start();
+    do {
+        response = _spi.write(SPI_FILL_CHAR);
+        if (response == 0xFF) {
+            _spi_timer.stop();
+            return true;
+        }
+    } while (_spi_timer.read_ms() < ms);
+    _spi_timer.stop();
+    return false;
+}
+
+// SPI function to wait for count
+void SDBlockDevice::_spi_wait(uint8_t count)
+{
+    for (uint8_t i = 0; i < count; ++i) {
+        _spi.write(SPI_FILL_CHAR);
+    }
+}
+
+void SDBlockDevice::_spi_init() {
+    _spi.lock();
+    // Set to SCK for initialization, and clock card with cs = 1
+    _spi.frequency(_init_sck);
+    _spi.format(8, 0);
+    _spi.set_default_write_value(SPI_FILL_CHAR);
+    // Initial 74 cycles required for few cards, before selecting SPI mode
+    _cs = 1;
+    _spi_wait(10);
+    _spi.unlock();
+}
+
+void SDBlockDevice::_select() {
+    _spi.lock();
+    _spi.write(SPI_FILL_CHAR);
+    _cs = 0;
+}
+
+void SDBlockDevice::_deselect() {
+    _cs = 1;
+    _spi.write(SPI_FILL_CHAR);
+    _spi.unlock();
+}
+
+#endif  /* DEVICE_SPI */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/SDBlockDevice.h	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,232 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2013 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MBED_SD_BLOCK_DEVICE_H
+#define MBED_SD_BLOCK_DEVICE_H
+
+/* If the target has no SPI support then SDCard is not supported */
+#ifdef DEVICE_SPI
+
+#include "BlockDevice.h"
+#include "mbed.h"
+#include "platform/PlatformMutex.h"
+
+/** Access an SD Card using SPI
+ *
+ * @code
+ * #include "mbed.h"
+ * #include "SDBlockDevice.h"
+ *
+ * SDBlockDevice sd(p5, p6, p7, p12); // mosi, miso, sclk, cs
+ * uint8_t block[512] = "Hello World!\n";
+ *
+ * int main() {
+ *     sd.init();
+ *     sd.write(block, 0, 512);
+ *     sd.read(block, 0, 512);
+ *     printf("%s", block);
+ *     sd.deinit();
+ * }
+ */
+class SDBlockDevice : public BlockDevice {
+public:
+    /** Lifetime of an SD card
+     */
+    SDBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName cs, uint64_t hz=1000000);
+    virtual ~SDBlockDevice();
+
+    /** Initialize a block device
+     *
+     *  @return         0 on success or a negative error code on failure
+     */
+    virtual int init();
+
+    /** Deinitialize a block device
+     *
+     *  @return         0 on success or a negative error code on failure
+     */
+    virtual int deinit();
+
+    /** Read blocks from a block device
+     *
+     *  @param buffer   Buffer to write blocks to
+     *  @param addr     Address of block to begin reading from
+     *  @param size     Size to read in bytes, must be a multiple of read block size
+     *  @return         0 on success, negative error code on failure
+     */
+    virtual int read(void *buffer, bd_addr_t addr, bd_size_t size);
+
+    /** Program blocks to a block device
+     *
+     *  The blocks must have been erased prior to being programmed
+     *
+     *  @param buffer   Buffer of data to write to blocks
+     *  @param addr     Address of block to begin writing to
+     *  @param size     Size to write in bytes, must be a multiple of program block size
+     *  @return         0 on success, negative error code on failure
+     */
+    virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size);
+
+    /** Mark blocks as no longer in use
+     *
+     *  This function provides a hint to the underlying block device that a region of blocks
+     *  is no longer in use and may be erased without side effects. Erase must still be called
+     *  before programming, but trimming allows flash-translation-layers to schedule erases when
+     *  the device is not busy.
+     *
+     *  @param addr     Address of block to mark as unused
+     *  @param size     Size to mark as unused in bytes, must be a multiple of erase block size
+     *  @return         0 on success, negative error code on failure
+     */
+    virtual int trim(bd_addr_t addr, bd_size_t size);
+
+    /** Get the size of a readable block
+     *
+     *  @return         Size of a readable block in bytes
+     */
+    virtual bd_size_t get_read_size() const;
+
+    /** Get the size of a programable block
+     *
+     *  @return         Size of a programable block in bytes
+     *  @note Must be a multiple of the read size
+     */
+    virtual bd_size_t get_program_size() const;
+
+    /** Get the total size of the underlying device
+     *
+     *  @return         Size of the underlying device in bytes
+     */
+    virtual bd_size_t size() const;
+
+    /** Enable or disable debugging
+     *
+     *  @param          State of debugging
+     */
+    virtual void debug(bool dbg);
+
+    /** Set the transfer frequency
+     *
+     *  @param         Transfer frequency
+     *  @note Max frequency supported is 25MHZ
+     */
+    virtual int frequency(uint64_t freq);
+
+
+private:
+    /* Commands : Listed below are commands supported
+     * in SPI mode for SD card : Only Mandatory ones
+     */
+    enum cmdSupported {
+        CMD_NOT_SUPPORTED = -1,             /**< Command not supported error */
+        CMD0_GO_IDLE_STATE = 0,             /**< Resets the SD Memory Card */
+        CMD1_SEND_OP_COND = 1,              /**< Sends host capacity support */
+        CMD6_SWITCH_FUNC = 6,               /**< Check and Switches card function */
+        CMD8_SEND_IF_COND = 8,              /**< Supply voltage info */
+        CMD9_SEND_CSD = 9,                  /**< Provides Card Specific data */
+        CMD10_SEND_CID = 10,                /**< Provides Card Identification */
+        CMD12_STOP_TRANSMISSION = 12,       /**< Forces the card to stop transmission */
+        CMD13_SEND_STATUS = 13,             /**< Card responds with status */
+        CMD16_SET_BLOCKLEN = 16,            /**< Length for SC card is set */
+        CMD17_READ_SINGLE_BLOCK = 17,       /**< Read single block of data */
+        CMD18_READ_MULTIPLE_BLOCK = 18,     /**< Card transfers data blocks to host until interrupted
+                                                 by a STOP_TRANSMISSION command */
+        CMD24_WRITE_BLOCK = 24,             /**< Write single block of data */
+        CMD25_WRITE_MULTIPLE_BLOCK = 25,    /**< Continuously writes blocks of data until
+                                                 'Stop Tran' token is sent */
+        CMD27_PROGRAM_CSD = 27,             /**< Programming bits of CSD */
+        CMD32_ERASE_WR_BLK_START_ADDR = 32, /**< Sets the address of the first write
+                                                 block to be erased. */
+        CMD33_ERASE_WR_BLK_END_ADDR = 33,   /**< Sets the address of the last write
+                                                 block of the continuous range to be erased.*/
+        CMD38_ERASE = 38,                   /**< Erases all previously selected write blocks */
+        CMD55_APP_CMD = 55,                 /**< Extend to Applications specific commands */
+        CMD56_GEN_CMD = 56,                 /**< General Purpose Command */
+        CMD58_READ_OCR = 58,                /**< Read OCR register of card */
+        CMD59_CRC_ON_OFF = 59,              /**< Turns the CRC option on or off*/
+        // App Commands
+        ACMD6_SET_BUS_WIDTH = 6,
+        ACMD13_SD_STATUS = 13,
+        ACMD22_SEND_NUM_WR_BLOCKS = 22,
+        ACMD23_SET_WR_BLK_ERASE_COUNT = 23,
+        ACMD41_SD_SEND_OP_COND = 41,
+        ACMD42_SET_CLR_CARD_DETECT = 42,
+        ACMD51_SEND_SCR = 51,
+    };
+
+    uint8_t _card_type;
+    int _cmd(SDBlockDevice::cmdSupported cmd, uint32_t arg, bool isAcmd=0, uint32_t *resp=NULL);
+    int _cmd8();
+
+    /*  Move the SDCard into the SPI Mode idle state
+     *
+     *  The card is transitioned from SDCard mode to SPI mode by sending the
+     *  CMD0 (GO_IDLE_STATE) command with CS asserted. See the notes in the
+     *  "SPI Startup" section of the comments at the head of the
+     *  implementation file for further details and specification references.
+     *
+     *  @return         Response form the card. R1_IDLE_STATE (0x1), the successful
+     *                  response from CMD0. R1_XXX_XXX for more response
+     */
+    uint32_t _go_idle_state();
+    int _initialise_card();
+
+    bd_size_t _sectors;
+    bd_size_t _sd_sectors();
+
+    bool _is_valid_trim(bd_addr_t addr, bd_size_t size);
+
+    /* SPI functions */
+    Timer _spi_timer;               /**< Timer Class object used for busy wait */
+    uint32_t _init_sck;             /**< Intial SPI frequency */
+    uint32_t _transfer_sck;         /**< SPI frequency during data transfer/after initialization */
+    SPI _spi;                       /**< SPI Class object */
+
+    /* SPI initialization function */
+    void _spi_init();
+    uint8_t _cmd_spi(SDBlockDevice::cmdSupported cmd, uint32_t arg);
+    void _spi_wait(uint8_t count);
+
+    bool _wait_token(uint8_t token);        /**< Wait for token */
+    bool _wait_ready(uint16_t ms=300);      /**< 300ms default wait for card to be ready */
+    int _read(uint8_t * buffer, uint32_t length);
+    int _read_bytes(uint8_t * buffer, uint32_t length);
+    uint8_t _write(const uint8_t *buffer,uint8_t token, uint32_t length);
+    int _freq(void);
+
+    /* Chip Select and SPI mode select */
+    DigitalOut _cs;
+    void _select();
+    void _deselect();
+
+    virtual void lock() {
+        _mutex.lock();
+    }
+
+    virtual void unlock() {
+        _mutex.unlock();
+    }
+
+    PlatformMutex _mutex;
+    bd_size_t _block_size;
+    bd_size_t _erase_size;
+    bool _is_initialized;
+    bool _dbg;
+};
+
+#endif  /* DEVICE_SPI */
+
+#endif  /* MBED_SD_BLOCK_DEVICE_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/TESTS/block_device/basic/basic.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,179 @@
+/*
+ * mbed Microcontroller Library
+ * Copyright (c) 2006-2016 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* The following copyright notice is reproduced from the glibc project
+ * REF_LICENCE_GLIBC
+ *
+ * Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+ * This file is part of the GNU C Library.
+ *
+ * The GNU C Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The GNU C Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the GNU C Library; see the file COPYING.LIB.  If
+ * not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+ * Cambridge, MA 02139, USA.
+ */
+
+
+/** @file main.cpp Basic SD Driver Test
+ */
+#include "mbed.h"
+#include "greentea-client/test_env.h"
+#include "unity.h"
+#include "utest.h"
+
+#include "SDBlockDevice.h"
+#include <stdlib.h>
+
+using namespace utest::v1;
+
+#define TEST_BLOCK_COUNT        10
+#define TEST_ERROR_MASK         16
+#define TEST_BLOCK_SIZE         2048
+
+const struct {
+    const char *name;
+    bd_size_t (BlockDevice::*method)() const;
+} ATTRS[] = {
+    {"read size",    &BlockDevice::get_read_size},
+    {"program size", &BlockDevice::get_program_size},
+    {"erase size",   &BlockDevice::get_erase_size},
+    {"total size",   &BlockDevice::size},
+};
+
+void test_read_write() {
+    SDBlockDevice sd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
+
+    int err = sd.init();
+    TEST_ASSERT_EQUAL(0, err);
+
+    err = sd.frequency(8000000);
+    TEST_ASSERT_EQUAL(0, err);
+
+    for (unsigned a = 0; a < sizeof(ATTRS)/sizeof(ATTRS[0]); a++) {
+        static const char *prefixes[] = {"", "k", "M", "G"};
+        for (int i = 3; i >= 0; i--) {
+            bd_size_t size = (sd.*ATTRS[a].method)();
+            if (size >= (1ULL << 10*i)) {
+                printf("%s: %llu%sbytes (%llubytes)\n",
+                    ATTRS[a].name, size >> 10*i, prefixes[i], size);
+                break;
+            }
+        }
+    }
+
+    bd_size_t erase_size = sd.get_erase_size();
+    bd_size_t block_size = erase_size > TEST_BLOCK_SIZE ? erase_size : TEST_BLOCK_SIZE;
+
+    uint8_t *write_block = new uint8_t[block_size];
+    uint8_t *read_block = new uint8_t[block_size];
+    uint8_t *error_mask = new uint8_t[TEST_ERROR_MASK];
+    unsigned addrwidth = ceil(log(float(sd.size()-1)) / log(float(16)))+1;
+
+    for (int b = 0; b < TEST_BLOCK_COUNT; b++) {
+        // Find a random block
+        bd_addr_t block = (rand()*block_size) % sd.size();
+
+        // Use next random number as temporary seed to keep
+        // the address progressing in the pseudorandom sequence
+        unsigned seed = rand();
+
+        // Fill with random sequence
+        srand(seed);
+        for (bd_size_t i = 0; i < block_size; i++) {
+            write_block[i] = 0xff & rand();
+        }
+
+        // Write, sync, and read the block
+        printf("test  %0*llx:%llu...\n", addrwidth, block, block_size);
+
+        err = sd.trim(block, block_size);
+        TEST_ASSERT_EQUAL(0, err);
+
+        err = sd.program(write_block, block, block_size);
+        TEST_ASSERT_EQUAL(0, err);
+
+        printf("write %0*llx:%llu ", addrwidth, block, block_size);
+        for (int i = 0; i < 16; i++) {
+            printf("%02x", write_block[i]);
+        }
+        printf("...\n");
+
+        err = sd.read(read_block, block, block_size);
+        TEST_ASSERT_EQUAL(0, err);
+
+        printf("read  %0*llx:%llu ", addrwidth, block, block_size);
+        for (int i = 0; i < 16; i++) {
+            printf("%02x", read_block[i]);
+        }
+        printf("...\n");
+
+        // Find error mask for debugging
+        memset(error_mask, 0, TEST_ERROR_MASK);
+        bd_size_t error_scale = block_size / (TEST_ERROR_MASK*8);
+
+        srand(seed);
+        for (bd_size_t i = 0; i < TEST_ERROR_MASK*8; i++) {
+            for (bd_size_t j = 0; j < error_scale; j++) {
+                if ((0xff & rand()) != read_block[i*error_scale + j]) {
+                    error_mask[i/8] |= 1 << (i%8);
+                }
+            }
+        }
+
+        printf("error %0*llx:%llu ", addrwidth, block, block_size);
+        for (int i = 0; i < 16; i++) {
+            printf("%02x", error_mask[i]);
+        }
+        printf("\n");
+
+        // Check that the data was unmodified
+        srand(seed);
+        for (bd_size_t i = 0; i < block_size; i++) {
+            TEST_ASSERT_EQUAL(0xff & rand(), read_block[i]);
+        }
+    }
+
+    err = sd.deinit();
+    TEST_ASSERT_EQUAL(0, err);
+}
+
+// Test setup
+utest::v1::status_t test_setup(const size_t number_of_cases) {
+    GREENTEA_SETUP(120, "default_auto");
+    return verbose_test_setup_handler(number_of_cases);
+}
+
+Case cases[] = {
+    Case("Testing read write random blocks", test_read_write),
+};
+
+Specification specification(test_setup, cases);
+
+int main() {
+    return !Harness::run(specification);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/TESTS/filesystem/basic/basic.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,933 @@
+/*
+ * mbed Microcontroller Library
+ * Copyright (c) 2006-2016 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/* The following copyright notice is reproduced from the glibc project
+ * REF_LICENCE_GLIBC
+ *
+ * Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+ * This file is part of the GNU C Library.
+ *
+ * The GNU C Library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * The GNU C Library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with the GNU C Library; see the file COPYING.LIB.  If
+ * not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+ * Cambridge, MA 02139, USA.
+ */
+
+
+/** @file basic.cpp POSIX File API (stdio) test cases
+ *
+ * Consult the documentation under the test-case functions for
+ * a description of the individual test case.
+ *
+ * this file includes ports for the mbed 2 test cases from the following locations:
+ *  - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/dir_sd/main.cpp.
+ *  - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/file/main.cpp.
+ *  - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/sd/main.cpp
+ *  - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_handle/main.cpp
+ *  - https://github.com:/armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_stdio/main.cpp.
+ */
+
+#include "mbed.h"
+#include "mbed_config.h"
+#include "FATFileSystem.h"
+#include "SDBlockDevice.h"
+#include "test_env.h"
+#include "fsfat_debug.h"
+#include "fsfat_test.h"
+#include "utest/utest.h"
+#include "unity/unity.h"
+#include "greentea-client/test_env.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <algorithm>
+/* retarget.h is included after errno.h so symbols are mapped to
+ * consistent values for all toolchains */
+#include "platform/mbed_retarget.h"
+
+using namespace utest::v1;
+
+/* DEVICE_SPI
+ *  This symbol is defined in targets.json if the target has a SPI interface, which is required for SDCard support.
+ *
+ * MBED_CONF_APP_FSFAT_SDCARD_INSTALLED
+ *  For testing purposes, an SDCard must be installed on the target for the test cases in this file to succeed.
+ *  If the target has an SD card installed then the MBED_CONF_APP_FSFAT_SDCARD_INSTALLED will be generated
+ *  from the mbed_app.json, which includes the line
+ *    {
+ *    "config": {
+ *        "UART_RX": "D0",
+ *        <<< lines removed >>>
+ *        "DEVICE_SPI": 1,
+ *        "MBED_CONF_APP_FSFAT_SDCARD_INSTALLED": 1
+ *      },
+ *      <<< lines removed >>>
+ */
+#if defined(DEVICE_SPI) && ( defined(MBED_CONF_APP_FSFAT_SDCARD_INSTALLED) || (MBED_CONF_SD_FSFAT_SDCARD_INSTALLED))
+
+#define FSFAT_BASIC_TEST_00      fsfat_basic_test_00
+#define FSFAT_BASIC_TEST_01      fsfat_basic_test_01
+#define FSFAT_BASIC_TEST_02      fsfat_basic_test_02
+#define FSFAT_BASIC_TEST_03      fsfat_basic_test_03
+#define FSFAT_BASIC_TEST_04      fsfat_basic_test_04
+#define FSFAT_BASIC_TEST_05      fsfat_basic_test_05
+#define FSFAT_BASIC_TEST_06      fsfat_basic_test_06
+#define FSFAT_BASIC_TEST_07      fsfat_basic_test_07
+#define FSFAT_BASIC_TEST_08      fsfat_basic_test_08
+#define FSFAT_BASIC_TEST_09      fsfat_basic_test_09
+#define FSFAT_BASIC_TEST_10      fsfat_basic_test_10
+
+#define FSFAT_BASIC_MSG_BUF_SIZE              256
+#define FSFAT_BASIC_TEST_05_TEST_STRING   "Hello World!"
+
+static const char *sd_file_path = "/sd/out.txt";
+static const char *sd_mount_pt = "sd";
+static const int FSFAT_BASIC_DATA_SIZE = 256;
+static char fsfat_basic_msg_g[FSFAT_BASIC_MSG_BUF_SIZE];
+static char fsfat_basic_buffer[1024];
+static const int FSFAT_BASIC_KIB_RW = 128;
+static Timer fsfat_basic_timer;
+static const char *fsfat_basic_bin_filename = "/sd/testfile.bin";
+static const char *fsfat_basic_bin_filename_test_08 = "testfile.bin";
+static const char *fsfat_basic_bin_filename_test_10 = "0:testfile.bin";
+
+
+
+SDBlockDevice sd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
+FATFileSystem fs(sd_mount_pt, &sd);
+
+#define FSFAT_BASIC_MSG(_buf, _max_len, _fmt, ...)   \
+  do                                                            \
+  {                                                             \
+      snprintf((_buf), (_max_len), (_fmt), __VA_ARGS__);        \
+  }while(0);
+
+/** @brief  fopen test case
+ *
+ * - open a file
+ * - generate random data items, write the item to the file and store a coy in a buffer for later use.
+ * - close the file.
+ * - open the file.
+ * - read the data items from the file and check they are the same as write.
+ * - close the file.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_basic_test_00()
+{
+
+    uint8_t data_written[FSFAT_BASIC_DATA_SIZE] = { 0 };
+    bool read_result = false;
+    bool write_result = false;
+
+    // Fill data_written buffer with random data
+    // Write these data into the file
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    {
+        FSFAT_DBGLOG("%s:SD: Writing ... ", __func__);
+        FILE *f = fopen(sd_file_path, "w");
+        if (f) {
+            for (int i = 0; i < FSFAT_BASIC_DATA_SIZE; i++) {
+                data_written[i] = rand() % 0XFF;
+                fprintf(f, "%c", data_written[i]);
+            }
+            write_result = true;
+            fclose(f);
+        }
+        FSFAT_DBGLOG("[%s]\n", write_result ? "OK" : "FAIL");
+    }
+    TEST_ASSERT_MESSAGE(write_result == true, "Error: write_result is set to false.");
+
+    // Read back the data from the file and store them in data_read
+    {
+        FSFAT_DBGLOG("%s:SD: Reading data ... ", __func__);
+        FILE *f = fopen(sd_file_path, "r");
+        if (f) {
+            read_result = true;
+            for (int i = 0; i < FSFAT_BASIC_DATA_SIZE; i++) {
+                uint8_t data = fgetc(f);
+                if (data != data_written[i]) {
+                    read_result = false;
+                    break;
+                }
+            }
+            fclose(f);
+        }
+        FSFAT_DBGLOG("[%s]\n", read_result ? "OK" : "FAIL");
+    }
+    TEST_ASSERT_MESSAGE(read_result == true, "Error: read_result is set to false.");
+    return CaseNext;
+}
+
+
+/** @brief  test-fseek.c test ported from glibc project. See the licence at REF_LICENCE_GLIBC.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_basic_test_01()
+{
+    FILE *fp, *fp1;
+    int i, j;
+    int ret = 0;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    fp = fopen (sd_file_path, "w+");
+    if (fp == NULL) {
+        FSFAT_DBGLOG("errno=%d\n", errno);
+        TEST_ASSERT_MESSAGE(false, "error");
+        return CaseNext;
+    }
+
+    for (i = 0; i < 256; i++) {
+        putc (i, fp);
+    }
+    /* FIXME: freopen() should open the specified file closing the first stream. As can be seen from the
+     * code below, the old file descriptor fp can still be used, and this should not happen.
+     */
+    fp1 = freopen (sd_file_path, "r", fp);
+    TEST_ASSERT_MESSAGE(fp1 == fp, "Error: cannot open file for reading");
+
+    for (i = 1; i <= 255; i++) {
+        ret = fseek (fp, (long) -i, SEEK_END);
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s:Error: fseek() failed (ret=%d).\n", __func__, (int) ret);
+        TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g);
+
+        if ((j = getc (fp)) != 256 - i) {
+            FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: SEEK_END failed (j=%d)\n",  __func__, j);
+            TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g);
+        }
+        ret = fseek (fp, (long) i, SEEK_SET);
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot SEEK_SET (ret=%d).\n", __func__, (int) ret);
+        TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g);
+
+        if ((j = getc (fp)) != i) {
+            FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot SEEK_SET (j=%d).\n", __func__, j);
+            TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g);
+        }
+        if ((ret = fseek (fp, (long) i, SEEK_SET))) {
+            FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot SEEK_SET (ret=%d).\n", __func__, (int) ret);
+            TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g);
+        }
+        if ((ret = fseek (fp, (long) (i >= 128 ? -128 : 128), SEEK_CUR))) {
+            FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot SEEK_CUR (ret=%d).\n", __func__, (int) ret);
+            TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g);
+        }
+        if ((j = getc (fp)) != (i >= 128 ? i - 128 : i + 128)) {
+            FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot SEEK_CUR (j=%d).\n", __func__, j);
+            TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g);
+        }
+    }
+    fclose (fp);
+    remove(sd_file_path);
+    return CaseNext;
+}
+
+
+/** @brief  test_rdwr.c test ported from glibc project. See the licence at REF_LICENCE_GLIBC.
+ *
+ * WARNING: this test does not currently work. See WARNING comments below.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_basic_test_02()
+{
+    static const char hello[] = "Hello, world.\n";
+    static const char replace[] = "Hewwo, world.\n";
+    static const size_t replace_from = 2, replace_to = 4;
+    const char *filename = sd_file_path;
+    char buf[BUFSIZ];
+    FILE *f;
+    int lose = 0;
+    int32_t ret = 0;
+    char *rets = NULL;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    f = fopen(filename, "w+");
+    if (f == NULL) {
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Cannot open file for writing (filename=%s).\n", __func__, filename);
+        TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g);
+    }
+
+    ret = fputs(hello, f);
+    if (ret == EOF) {
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fputs() failed to write string to file (filename=%s, string=%s).\n", __func__, filename, hello);
+        TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g);
+    }
+
+    rewind(f);
+    rets = fgets(buf, sizeof(buf), f);
+    if (rets == NULL) {
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fgets() failed to get string from file (filename=%s).\n", __func__, filename);
+        TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g);
+    }
+    rets = NULL;
+
+    rewind(f);
+    ret = fputs(buf, f);
+    if (ret == EOF) {
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fputs() failed to write string to file (filename=%s, string=%s).\n", __func__, filename, buf);
+        TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g);
+    }
+
+    rewind(f);
+    {
+        register size_t i;
+        for (i = 0; i < replace_from; ++i)
+        {
+            int c = getc(f);
+            if (c == EOF)
+            {
+                FSFAT_DBGLOG("EOF at %u.\n", i);
+                lose = 1;
+                break;
+            }
+            else if (c != hello[i])
+            {
+                FSFAT_DBGLOG("Got '%c' instead of '%c' at %u.\n",
+                (unsigned char) c, hello[i], i);
+                lose = 1;
+                break;
+            }
+        }
+    }
+    /* WARNING: printf("%s: here1. (lose = %d)\n", __func__, lose); */
+    {
+        long int where = ftell(f);
+        if (where == replace_from)
+        {
+            register size_t i;
+            for (i = replace_from; i < replace_to; ++i) {
+                if (putc(replace[i], f) == EOF) {
+                    FSFAT_DBGLOG("putc('%c') got %s at %u.\n",
+                    replace[i], strerror(errno), i);
+                    lose = 1;
+                    break;
+                }
+                /* WARNING: The problem seems to be that putc() is not writing the 'w' chars into the file
+                 * FSFAT_DBGLOG("%s: here1.5. (char = %c, char as int=%d, ret=%d) \n", __func__, replace[i], (int) replace[i], ret);
+                 */
+            }
+        }
+        else if (where == -1L)
+        {
+            FSFAT_DBGLOG("ftell got %s (should be at %u).\n",
+            strerror(errno), replace_from);
+            lose = 1;
+        }
+        else
+        {
+            FSFAT_DBGLOG("ftell returns %ld; should be %u.\n", where, replace_from);
+            lose = 1;
+        }
+    }
+
+    if (!lose)
+    {
+        rewind(f);
+        memset(buf, 0, BUFSIZ);
+        if (fgets(buf, sizeof(buf), f) == NULL)
+        {
+            FSFAT_DBGLOG("fgets got %s.\n", strerror(errno));
+            lose = 1;
+        }
+        else if (strcmp(buf, replace))
+        {
+            FSFAT_DBGLOG("Read \"%s\" instead of \"%s\".\n", buf, replace);
+            lose = 1;
+        }
+    }
+
+    if (lose) {
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: Test Failed. Losing file (filename=%s).\n", __func__, filename);
+        TEST_ASSERT_MESSAGE(false, fsfat_basic_msg_g);
+    }
+    remove(filename);
+    return CaseNext;
+}
+
+/** @brief  temptest.c test ported from glibc project. See the licence at REF_LICENCE_GLIBC.
+ *
+ * tmpnam() is currently not implemented
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_basic_test_03()
+{
+    char *fn = NULL;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    fn = tmpnam((char *) NULL);
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: appeared to generate a filename when function is not implemented.\n", __func__);
+    TEST_ASSERT_MESSAGE(fn == NULL, fsfat_basic_msg_g);
+    return CaseNext;
+}
+
+
+static bool fsfat_basic_fileno_check(const char *name, FILE *stream, int fd)
+{
+    /* ARMCC stdio.h currently does not define fileno() */
+#ifndef __ARMCC_VERSION
+    int sfd = fileno (stream);
+    FSFAT_DBGLOG("(fileno (%s) = %d) %c= %d\n", name, sfd, sfd == fd ? '=' : '!', fd);
+
+    if (sfd == fd) {
+        return true;
+    } else {
+        return false;
+    }
+#else
+    /* For ARMCC behave as though test had passed. */
+    return true;
+#endif  /* __ARMCC_VERSION */
+}
+
+/* defines for next test case */
+#ifndef STDIN_FILENO
+#define STDIN_FILENO     0
+#endif
+
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO    1
+#endif
+
+#ifndef STDERR_FILENO
+#define STDERR_FILENO    2
+#endif
+
+
+/** @brief  tst-fileno.c test ported from glibc project. See the licence at REF_LICENCE_GLIBC.
+ *
+ * WARNING: this test does not currently work. See WARNING comments below.
+ *
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_basic_test_04()
+{
+    /* ARMCC stdio.h currently does not define fileno() */
+#ifndef __ARMCC_VERSION
+    int ret = -1;
+    ret = fsfat_basic_fileno_check("stdin", stdin, STDIN_FILENO);
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: stdin does not have expected file number (expected=%d, fileno=%d.\n", __func__, (int) stdin, fileno(stdin));
+    TEST_ASSERT_MESSAGE(ret == true, fsfat_basic_msg_g);
+
+    ret = fsfat_basic_fileno_check("stdout", stdout, STDOUT_FILENO);
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: stdout does not have expected file number (expected=%d, fileno=%d.\n", __func__, (int) stdout, fileno(stdout));
+    TEST_ASSERT_MESSAGE(ret == true, fsfat_basic_msg_g);
+
+    ret = fsfat_basic_fileno_check("stderr", stderr, STDERR_FILENO);
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: stderr does not have expected file number (expected=%d, fileno=%d.\n", __func__, (int) stderr, fileno(stderr));
+    TEST_ASSERT_MESSAGE(ret == true, fsfat_basic_msg_g);
+#endif  /* __ARMCC_VERSION */
+    return CaseNext;
+}
+
+
+/** @brief  basic test to opendir() on a directory.
+ *
+ * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/dir_sd/main.cpp.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_basic_test_05()
+{
+    FILE *f;
+    const char *str = FSFAT_BASIC_TEST_05_TEST_STRING;
+    int ret = 0;
+
+    FSFAT_DBGLOG("%s:Write files\n", __func__);
+    char filename[32];
+    for (int i = 0; i < 10; i++) {
+        sprintf(filename, "/sd/test_%d.txt", i);
+        FSFAT_DBGLOG("Creating file: %s\n", filename);
+        f = fopen(filename, "w");
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fopen() failed.\n", __func__);
+        TEST_ASSERT_MESSAGE(f != NULL, fsfat_basic_msg_g);
+
+        ret = fprintf(f, str);
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: writing file.\n", __func__);
+        TEST_ASSERT_MESSAGE(ret == (int) strlen(str), fsfat_basic_msg_g);
+
+        ret = fclose(f);
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fclose() failed.\n", __func__);
+        TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g);
+    }
+
+    FSFAT_DBGLOG("%s:List files:\n", __func__);
+    DIR *d = opendir("/sd");
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: opendir() failed.\n", __func__);
+    TEST_ASSERT_MESSAGE(d != NULL, fsfat_basic_msg_g);
+
+    struct dirent *p;
+    while ((p = readdir(d)) != NULL)
+        FSFAT_DBGLOG("%s\n", p->d_name);
+    closedir(d);
+
+    return CaseNext;
+}
+
+
+/** @brief  basic test to write a file to sd card, and read it back again
+ *
+ * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/file/main.cpp.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_basic_test_06()
+{
+    int ret = -1;
+    char mac[16];
+    mbed_mac_address(mac);
+    FSFAT_DBGLOG("mac address: %02x,%02x,%02x,%02x,%02x,%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+
+    FILE *f;
+    const char *str = FSFAT_BASIC_TEST_05_TEST_STRING;
+    int str_len = strlen(FSFAT_BASIC_TEST_05_TEST_STRING);
+
+    f = fopen(sd_file_path, "w");
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fopen() failed.\n", __func__);
+    TEST_ASSERT_MESSAGE(f != NULL, fsfat_basic_msg_g);
+
+    ret = fprintf(f, str);
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: writing file.\n", __func__);
+    TEST_ASSERT_MESSAGE(ret == (int) strlen(str), fsfat_basic_msg_g);
+
+    ret = fclose(f);
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fclose() failed.\n", __func__);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g);
+
+    // Read
+    f = fopen(sd_file_path, "r");
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fopen() failed.\n", __func__);
+    TEST_ASSERT_MESSAGE(f != NULL, fsfat_basic_msg_g);
+
+    int n = fread(fsfat_basic_buffer, sizeof(unsigned char), str_len, f);
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fread() failed.\n", __func__);
+    TEST_ASSERT_MESSAGE(n == str_len, fsfat_basic_msg_g);
+
+    ret = fclose(f);
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: fclose() failed.\n", __func__);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g);
+
+    return CaseNext;
+}
+
+
+/** @brief  basic test to write a file to sd card.
+ *
+ * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd/main.cpp.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_basic_test_07()
+{
+    uint8_t data_written[FSFAT_BASIC_DATA_SIZE] = { 0 };
+
+    // Fill data_written buffer with random data
+    // Write these data into the file
+    bool write_result = false;
+    {
+        FSFAT_DBGLOG("%s:SD: Writing ... ", __func__);
+        FILE *f = fopen(sd_file_path, "w");
+        if (f) {
+            for (int i = 0; i < FSFAT_BASIC_DATA_SIZE; i++) {
+                data_written[i] = rand() % 0XFF;
+                fprintf(f, "%c", data_written[i]);
+            }
+            write_result = true;
+            fclose(f);
+        }
+        FSFAT_DBGLOG("[%s]\n", write_result ? "OK" : "FAIL");
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: unexpected write failure.\n", __func__);
+        TEST_ASSERT_MESSAGE(write_result == true, fsfat_basic_msg_g);
+    }
+
+    // Read back the data from the file and store them in data_read
+    bool read_result = false;
+    {
+        FSFAT_DBGLOG("%s:SD: Reading data ... ", __func__);
+        FILE *f = fopen(sd_file_path, "r");
+        if (f) {
+            read_result = true;
+            for (int i = 0; i < FSFAT_BASIC_DATA_SIZE; i++) {
+                uint8_t data = fgetc(f);
+                if (data != data_written[i]) {
+                    read_result = false;
+                    break;
+                }
+            }
+            fclose(f);
+        }
+        FSFAT_DBGLOG("[%s]\n", read_result ? "OK" : "FAIL");
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: unexpected read failure.\n", __func__);
+        TEST_ASSERT_MESSAGE(read_result == true, fsfat_basic_msg_g);
+    }
+    return CaseNext;
+}
+
+
+static bool fsfat_basic_test_file_write_fhandle(const char *filename, const int kib_rw)
+{
+    int ret = -1;
+    File file;
+
+    ret = file.open(&fs, filename, O_WRONLY | O_CREAT | O_TRUNC);
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g);
+
+    int byte_write = 0;
+    fsfat_basic_timer.start();
+    for (int i = 0; i < kib_rw; i++) {
+        ret = file.write(fsfat_basic_buffer, sizeof(fsfat_basic_buffer));
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to write to file.\n", __func__);
+        TEST_ASSERT_MESSAGE(ret == sizeof(fsfat_basic_buffer), fsfat_basic_msg_g);
+        byte_write++;
+    }
+    fsfat_basic_timer.stop();
+    file.close();
+#ifdef FSFAT_DEBUG
+    double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0;
+    double speed = kib_rw / test_time_sec;
+    FSFAT_DBGLOG("%d KiB write in %.3f sec with speed of %.4f KiB/s\n", byte_write, test_time_sec, speed);
+#endif
+    fsfat_basic_timer.reset();
+    return true;
+}
+
+
+static bool fsfat_basic_test_file_read_fhandle(const char *filename, const int kib_rw)
+{
+    int ret = -1;
+    File file;
+    ret = file.open(&fs, filename, O_RDONLY);
+
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_basic_msg_g);
+
+    fsfat_basic_timer.start();
+    int byte_read = 0;
+    while (file.read(fsfat_basic_buffer, sizeof(fsfat_basic_buffer)) == sizeof(fsfat_basic_buffer)) {
+        byte_read++;
+    }
+    fsfat_basic_timer.stop();
+    file.close();
+#ifdef FSFAT_DEBUG
+    double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0;
+    double speed = kib_rw / test_time_sec;
+    FSFAT_DBGLOG("%d KiB read in %.3f sec with speed of %.4f KiB/s\n", byte_read, test_time_sec, speed);
+#endif
+    fsfat_basic_timer.reset();
+    return true;
+}
+
+
+static char fsfat_basic_test_random_char()
+{
+    return rand() % 100;
+}
+
+
+/** @brief  basic sd card performance test
+ *
+ * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_handle/main.cpp.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_basic_test_08()
+{
+    // Test header
+    FSFAT_DBGLOG("\n%s:SD Card FileHandle Performance Test\n", __func__);
+    FSFAT_DBGLOG("File name: %s\n", fsfat_basic_bin_filename);
+    FSFAT_DBGLOG("Buffer size: %d KiB\n", (FSFAT_BASIC_KIB_RW * sizeof(fsfat_basic_buffer)) / 1024);
+
+    // Initialize buffer
+    srand(0);
+    char *buffer_end = fsfat_basic_buffer + sizeof(fsfat_basic_buffer);
+    std::generate (fsfat_basic_buffer, buffer_end, fsfat_basic_test_random_char);
+
+    bool result = true;
+    for (;;) {
+        FSFAT_DBGLOG("%s:Write test...\n", __func__);
+        if (fsfat_basic_test_file_write_fhandle(fsfat_basic_bin_filename_test_08, FSFAT_BASIC_KIB_RW) == false) {
+            result = false;
+            break;
+        }
+
+        FSFAT_DBGLOG("%s:Read test...\n", __func__);
+        if (fsfat_basic_test_file_read_fhandle(fsfat_basic_bin_filename_test_08, FSFAT_BASIC_KIB_RW) == false) {
+            result = false;
+            break;
+        }
+        break;
+    }
+    TEST_ASSERT_MESSAGE(result == true, "something went wrong");
+    return CaseNext;
+}
+
+
+bool fsfat_basic_test_sf_file_write_stdio(const char *filename, const int kib_rw)
+{
+    int ret = -1;
+    FILE* file = fopen(filename, "w");
+
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__);
+    TEST_ASSERT_MESSAGE(file != NULL, fsfat_basic_msg_g);
+
+    int byte_write = 0;
+    fsfat_basic_timer.start();
+    for (int i = 0; i < kib_rw; i++) {
+        ret = fwrite(fsfat_basic_buffer, sizeof(char), sizeof(fsfat_basic_buffer), file);
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to write to file.\n", __func__);
+        TEST_ASSERT_MESSAGE(ret == sizeof(fsfat_basic_buffer), fsfat_basic_msg_g);
+        byte_write++;
+    }
+    fsfat_basic_timer.stop();
+    fclose(file);
+#ifdef FSFAT_DEBUG
+    double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0;
+    double speed = kib_rw / test_time_sec;
+    FSFAT_DBGLOG("%d KiB write in %.3f sec with speed of %.4f KiB/s\n", byte_write, test_time_sec, speed);
+#endif
+    fsfat_basic_timer.reset();
+    return true;
+}
+
+
+bool fsfat_basic_test_sf_file_read_stdio(const char *filename, const int kib_rw)
+{
+    FILE* file = fopen(filename, "r");
+
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__);
+    TEST_ASSERT_MESSAGE(file != NULL, fsfat_basic_msg_g);
+    fsfat_basic_timer.start();
+    int byte_read = 0;
+    while (fread(fsfat_basic_buffer, sizeof(char), sizeof(fsfat_basic_buffer), file) == sizeof(fsfat_basic_buffer)) {
+        byte_read++;
+    }
+    fsfat_basic_timer.stop();
+    fclose(file);
+#ifdef FSFAT_DEBUG
+    double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0;
+    double speed = kib_rw / test_time_sec;
+    FSFAT_DBGLOG("%d KiB read in %.3f sec with speed of %.4f KiB/s\n", byte_read, test_time_sec, speed);
+#endif
+    fsfat_basic_timer.reset();
+    return true;
+}
+
+
+/** @brief  basic test to write a file to sd card.
+ *
+ * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_stdio/main.cpp.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_basic_test_09()
+{
+    // Test header
+    FSFAT_DBGLOG("\n%s:SD Card Stdio Performance Test\n", __func__);
+    FSFAT_DBGLOG("File name: %s\n", fsfat_basic_bin_filename);
+    FSFAT_DBGLOG("Buffer size: %d KiB\n", (FSFAT_BASIC_KIB_RW * sizeof(fsfat_basic_buffer)) / 1024);
+
+    // Initialize buffer
+    srand(0);
+    char *buffer_end = fsfat_basic_buffer + sizeof(fsfat_basic_buffer);
+    std::generate (fsfat_basic_buffer, buffer_end, fsfat_basic_test_random_char);
+
+    bool result = true;
+    for (;;) {
+        FSFAT_DBGLOG("%s:Write test...\n", __func__);
+        if (fsfat_basic_test_sf_file_write_stdio(fsfat_basic_bin_filename, FSFAT_BASIC_KIB_RW) == false) {
+            result = false;
+            break;
+        }
+
+        FSFAT_DBGLOG("%s:Read test...\n", __func__);
+        if (fsfat_basic_test_sf_file_read_stdio(fsfat_basic_bin_filename, FSFAT_BASIC_KIB_RW) == false) {
+            result = false;
+            break;
+        }
+        break;
+    }
+    TEST_ASSERT_MESSAGE(result == true, "Expected true result not found");
+    return CaseNext;
+}
+
+
+bool fsfat_basic_test_file_write_fatfs(const char *filename, const int kib_rw)
+{
+    FIL file;
+    FRESULT res = f_open(&file, filename, FA_WRITE | FA_CREATE_ALWAYS);
+
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__);
+    TEST_ASSERT_MESSAGE(res == FR_OK, fsfat_basic_msg_g);
+
+    int byte_write = 0;
+    unsigned int bytes = 0;
+    fsfat_basic_timer.start();
+    for (int i = 0; i < kib_rw; i++) {
+        res = f_write(&file, fsfat_basic_buffer, sizeof(fsfat_basic_buffer), &bytes);
+        FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to write to file.\n", __func__);
+        TEST_ASSERT_MESSAGE(res == FR_OK, fsfat_basic_msg_g);
+        byte_write++;
+    }
+    fsfat_basic_timer.stop();
+    f_close(&file);
+#ifdef FSFAT_DEBUG
+    double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0;
+    double speed = kib_rw / test_time_sec;
+    FSFAT_DBGLOG("%d KiB write in %.3f sec with speed of %.4f KiB/s\n", byte_write, test_time_sec, speed);
+#endif
+    fsfat_basic_timer.reset();
+    return true;
+}
+
+bool fsfat_basic_test_file_read_fatfs(const char *filename, const int kib_rw)
+{
+    FIL file;
+    FRESULT res = f_open(&file, filename, FA_READ | FA_OPEN_EXISTING);
+
+    FSFAT_BASIC_MSG(fsfat_basic_msg_g, FSFAT_BASIC_MSG_BUF_SIZE, "%s: Error: failed to open file.\n", __func__);
+    TEST_ASSERT_MESSAGE(res == FR_OK, fsfat_basic_msg_g);
+
+    fsfat_basic_timer.start();
+    int byte_read = 0;
+    unsigned int bytes = 0;
+    do {
+        res = f_read(&file, fsfat_basic_buffer, sizeof(fsfat_basic_buffer), &bytes);
+        byte_read++;
+    } while (res == FR_OK && bytes == sizeof(fsfat_basic_buffer));
+    fsfat_basic_timer.stop();
+    f_close(&file);
+#ifdef FSFAT_DEBUG
+    double test_time_sec = fsfat_basic_timer.read_us() / 1000000.0;
+    double speed = kib_rw / test_time_sec;
+    FSFAT_DBGLOG("%d KiB read in %.3f sec with speed of %.4f KiB/s\n", byte_read, test_time_sec, speed);
+#endif
+    fsfat_basic_timer.reset();
+    return true;
+}
+
+/** @brief  basic test to write a file to sd card.
+ *
+ * This test has been ported from armmbed/mbed-os/features/unsupported/tests/mbed/sd_perf_stdio/main.cpp.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_basic_test_10()
+{
+    // Test header
+    FSFAT_DBGLOG("\n%sSD Card FatFS Performance Test\n", __func__);
+    FSFAT_DBGLOG("File name: %s\n", fsfat_basic_bin_filename_test_10);
+    FSFAT_DBGLOG("Buffer size: %d KiB\n", (FSFAT_BASIC_KIB_RW * sizeof(fsfat_basic_buffer)) / 1024);
+
+    // Initialize buffer
+    srand(1);
+    char *buffer_end = fsfat_basic_buffer + sizeof(fsfat_basic_buffer);
+    std::generate (fsfat_basic_buffer, buffer_end, fsfat_basic_test_random_char);
+
+    bool result = true;
+    for (;;) {
+        FSFAT_DBGLOG("%s:Write test...\n", __func__);
+        if (fsfat_basic_test_file_write_fatfs(fsfat_basic_bin_filename_test_10, FSFAT_BASIC_KIB_RW) == false) {
+            result = false;
+            break;
+        }
+
+        FSFAT_DBGLOG("%s:Read test...\n", __func__);
+        if (fsfat_basic_test_file_read_fatfs(fsfat_basic_bin_filename_test_10, FSFAT_BASIC_KIB_RW) == false) {
+            result = false;
+            break;
+        }
+        break;
+    }
+    TEST_ASSERT_MESSAGE(result == true, "Expected true result not found");
+    return CaseNext;
+}
+
+#else
+
+#define FSFAT_BASIC_TEST_00      fsfat_basic_test_dummy
+#define FSFAT_BASIC_TEST_01      fsfat_basic_test_dummy
+#define FSFAT_BASIC_TEST_02      fsfat_basic_test_dummy
+#define FSFAT_BASIC_TEST_03      fsfat_basic_test_dummy
+#define FSFAT_BASIC_TEST_04      fsfat_basic_test_dummy
+#define FSFAT_BASIC_TEST_05      fsfat_basic_test_dummy
+#define FSFAT_BASIC_TEST_06      fsfat_basic_test_dummy
+#define FSFAT_BASIC_TEST_07      fsfat_basic_test_dummy
+#define FSFAT_BASIC_TEST_08      fsfat_basic_test_dummy
+#define FSFAT_BASIC_TEST_09      fsfat_basic_test_dummy
+#define FSFAT_BASIC_TEST_10      fsfat_basic_test_dummy
+
+
+/** @brief  fsfat_basic_test_dummy    Dummy test case for testing when platform doesnt have an SDCard installed.
+ *
+ * @return success always
+ */
+static control_t fsfat_basic_test_dummy()
+{
+    printf("Null test\n");
+    return CaseNext;
+}
+
+#endif
+
+utest::v1::status_t greentea_setup(const size_t number_of_cases)
+{
+    GREENTEA_SETUP(300, "default_auto");
+    return greentea_test_setup_handler(number_of_cases);
+}
+
+
+Case cases[] = {
+           /*          1         2         3         4         5         6        7  */
+           /* 1234567890123456789012345678901234567890123456789012345678901234567890 */
+        Case("FSFAT_BASIC_TEST_00: fopen()/fgetc()/fprintf()/fclose() test.", FSFAT_BASIC_TEST_00),
+        Case("FSFAT_BASIC_TEST_01: fopen()/fseek()/fclose() test.", FSFAT_BASIC_TEST_01),
+        /* WARNING: Test case not working but currently not required for PAL support
+         * Case("FSFAT_BASIC_TEST_02: fopen()/fgets()/fputs()/ftell()/rewind()/remove() test.", FSFAT_BASIC_TEST_02) */
+        Case("FSFAT_BASIC_TEST_03: tmpnam() test.", FSFAT_BASIC_TEST_03),
+        Case("FSFAT_BASIC_TEST_04: fileno() test.", FSFAT_BASIC_TEST_04),
+        Case("FSFAT_BASIC_TEST_05: opendir() basic test.", FSFAT_BASIC_TEST_05),
+        Case("FSFAT_BASIC_TEST_06: fread()/fwrite() file to sdcard.", FSFAT_BASIC_TEST_06),
+        Case("FSFAT_BASIC_TEST_07: sdcard fwrite() file test.", FSFAT_BASIC_TEST_07),
+        Case("FSFAT_BASIC_TEST_08: FATFileSystem::read()/write() test.", FSFAT_BASIC_TEST_08),
+        Case("FSFAT_BASIC_TEST_09: POSIX FILE API fread()/fwrite() test.", FSFAT_BASIC_TEST_09),
+        Case("FSFAT_BASIC_TEST_10: ChanFS read()/write()) test.", FSFAT_BASIC_TEST_10),
+};
+
+
+/* Declare your test specification with a custom setup handler */
+Specification specification(greentea_setup, cases);
+
+int main()
+{
+    return !Harness::run(specification);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/TESTS/filesystem/dirs/main.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,472 @@
+#include "mbed.h"
+#include "greentea-client/test_env.h"
+#include "unity.h"
+#include "utest.h"
+#include <stdlib.h>
+#include <errno.h>
+
+using namespace utest::v1;
+
+// test configuration
+#ifndef MBED_TEST_FILESYSTEM
+#define MBED_TEST_FILESYSTEM FATFileSystem
+#endif
+
+#ifndef MBED_TEST_FILESYSTEM_DECL
+#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
+#endif
+
+#ifndef MBED_TEST_BLOCKDEVICE
+#define MBED_TEST_BLOCKDEVICE SDBlockDevice
+#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
+#endif
+
+#ifndef MBED_TEST_BLOCKDEVICE_DECL
+#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
+#endif
+
+#ifndef MBED_TEST_FILES
+#define MBED_TEST_FILES 4
+#endif
+
+#ifndef MBED_TEST_DIRS
+#define MBED_TEST_DIRS 4
+#endif
+
+#ifndef MBED_TEST_BUFFER
+#define MBED_TEST_BUFFER 8192
+#endif
+
+#ifndef MBED_TEST_TIMEOUT
+#define MBED_TEST_TIMEOUT 120
+#endif
+
+
+// declarations
+#define STRINGIZE(x) STRINGIZE2(x)
+#define STRINGIZE2(x) #x
+#define INCLUDE(x) STRINGIZE(x.h)
+
+#include INCLUDE(MBED_TEST_FILESYSTEM)
+#include INCLUDE(MBED_TEST_BLOCKDEVICE)
+
+MBED_TEST_FILESYSTEM_DECL;
+MBED_TEST_BLOCKDEVICE_DECL;
+
+Dir dir[MBED_TEST_DIRS];
+File file[MBED_TEST_FILES];
+DIR *dd[MBED_TEST_DIRS];
+FILE *fd[MBED_TEST_FILES];
+struct dirent ent;
+struct dirent *ed;
+size_t size;
+uint8_t buffer[MBED_TEST_BUFFER];
+uint8_t rbuffer[MBED_TEST_BUFFER];
+uint8_t wbuffer[MBED_TEST_BUFFER];
+
+
+// tests
+
+void test_directory_tests() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = MBED_TEST_FILESYSTEM::format(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_root_directory() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].open(&fs, "/");
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_directory_creation() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("potato", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_file_creation() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "burito", O_CREAT | O_WRONLY);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void dir_file_check(char *list[], uint32_t elements) {
+    int res;
+    while(1) {
+        res = dir[0].read(&ent);
+        if (0 == res) {
+            break;
+        }
+        for (int i = 0; i < elements ; i++) {
+            res = strcmp(ent.d_name, list[i]);
+            if (0 == res) {
+                res = ent.d_type;
+                if ((DT_DIR != res) && (DT_REG != res)) {
+                    TEST_ASSERT(1);
+                }
+                break;
+            }
+            else if( i == elements) {
+                TEST_ASSERT_EQUAL(0, res);
+            }
+        }
+    }
+}
+
+void test_directory_iteration() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    res = fs.mount(&bd);
+    TEST_ASSERT_EQUAL(0, res);
+    res = dir[0].open(&fs, "/");
+    TEST_ASSERT_EQUAL(0, res);
+    char *dir_list[] = {"potato", "burito", ".", ".."};
+
+    dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0])));
+
+    res = dir[0].close();
+    TEST_ASSERT_EQUAL(0, res);
+    res = fs.unmount();
+    TEST_ASSERT_EQUAL(0, res);
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_directory_failures() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("potato", 0777);
+        TEST_ASSERT_EQUAL(-EEXIST, res);
+        res = dir[0].open(&fs, "tomato");
+        TEST_ASSERT_EQUAL(-ENOENT, res);
+        res = dir[0].open(&fs, "burito");
+        TEST_ASSERT_NOT_EQUAL(0, res);
+        res = file[0].open(&fs, "tomato", O_RDONLY);
+        TEST_ASSERT_EQUAL(-ENOENT, res);
+        res = file[0].open(&fs, "potato", O_RDONLY);
+        TEST_ASSERT_NOT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_nested_directories() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("potato/baked", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("potato/sweet", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("potato/fried", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].open(&fs, "/");
+        TEST_ASSERT_EQUAL(0, res);
+        char *dir_list[] = {"potato", "baked", "sweet", "fried", ".", ".."};
+        dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0])));
+        res = dir[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_multi_block_directory() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("cactus", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        for (int i = 0; i < 128; i++) {
+            sprintf((char*)buffer, "cactus/test%d", i);
+            res = fs.mkdir((char*)buffer, 0777);
+            TEST_ASSERT_EQUAL(0, res);
+        }
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].open(&fs, "cactus");
+        TEST_ASSERT_EQUAL(0, res);
+
+#if (MBED_TEST_FILESYSTEM != FATFileSystem)
+        char *dir_list[] = {".", ".."};
+        dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0])));
+#endif
+
+        for (int i = 0; i < 128; i++) {
+            sprintf((char*)buffer, "test%d", i);
+            res = dir[0].read(&ent);
+            TEST_ASSERT_EQUAL(1, res);
+            res = strcmp(ent.d_name, (char*)buffer);
+            TEST_ASSERT_EQUAL(0, res);
+        }
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_directory_remove() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.remove("potato");
+        TEST_ASSERT_NOT_EQUAL(0, res);
+        res = fs.remove("potato/sweet");
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.remove("potato/baked");
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.remove("potato/fried");
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].open(&fs, "potato");
+        TEST_ASSERT_EQUAL(0, res);
+
+#if (MBED_TEST_FILESYSTEM != FATFileSystem)
+        char *dir_list[] = {".", ".."};
+        dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0])));
+#endif
+
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.remove("potato");
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].open(&fs, "/");
+        TEST_ASSERT_EQUAL(0, res);
+        char *dir_list[] = {"burito", "cactus", ".", ".."};
+        dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0])));
+        res = dir[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_directory_rename() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("coldpotato", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("coldpotato/baked", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("coldpotato/sweet", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("coldpotato/fried", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.rename("coldpotato", "hotpotato");
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].open(&fs, "hotpotato");
+        TEST_ASSERT_EQUAL(0, res);
+        char *dir_list[] = {"baked", "sweet", "fried", ".", ".."};
+        dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0])));
+        res = dir[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("warmpotato", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("warmpotato/mushy", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.rename("hotpotato", "warmpotato");
+        TEST_ASSERT_NOT_EQUAL(0, res);
+        res = fs.remove("warmpotato/mushy");
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.remove("warmpotato");
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.rename("hotpotato", "warmpotato");
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].open(&fs, "warmpotato");
+        TEST_ASSERT_EQUAL(0, res);
+        char *dir_list[] = {"baked", "sweet", "fried", ".", ".."};
+        dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0])));
+        res = dir[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("coldpotato", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.rename("warmpotato/baked", "coldpotato/baked");
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.rename("warmpotato/sweet", "coldpotato/sweet");
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.rename("warmpotato/fried", "coldpotato/fried");
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.remove("coldpotato");
+        TEST_ASSERT_NOT_EQUAL(0, res);
+        res = fs.remove("warmpotato");
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].open(&fs, "coldpotato");
+        TEST_ASSERT_EQUAL(0, res);
+        char *dir_list[] = {"baked", "sweet", "fried", ".", ".."};
+        dir_file_check(dir_list, (sizeof(dir_list)/sizeof(dir_list[0])));
+        res = dir[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+
+
+// test setup
+utest::v1::status_t test_setup(const size_t number_of_cases) {
+    GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
+    return verbose_test_setup_handler(number_of_cases);
+}
+
+Case cases[] = {
+    Case("Directory tests", test_directory_tests),
+    Case("Root directory", test_root_directory),
+    Case("Directory creation", test_directory_creation),
+    Case("File creation", test_file_creation),
+    Case("Directory iteration", test_directory_iteration),
+    Case("Directory failures", test_directory_failures),
+    Case("Nested directories", test_nested_directories),
+    Case("Multi-block directory", test_multi_block_directory),
+    Case("Directory remove", test_directory_remove),
+    Case("Directory rename", test_directory_rename),
+};
+
+Specification specification(test_setup, cases);
+
+int main() {
+    return !Harness::run(specification);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/TESTS/filesystem/files/main.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,314 @@
+#include "mbed.h"
+#include "greentea-client/test_env.h"
+#include "unity.h"
+#include "utest.h"
+#include <stdlib.h>
+#include <errno.h>
+
+using namespace utest::v1;
+
+// test configuration
+#ifndef MBED_TEST_FILESYSTEM
+#define MBED_TEST_FILESYSTEM FATFileSystem
+#endif
+
+#ifndef MBED_TEST_FILESYSTEM_DECL
+#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
+#endif
+
+#ifndef MBED_TEST_BLOCKDEVICE
+#define MBED_TEST_BLOCKDEVICE SDBlockDevice
+#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
+#endif
+
+#ifndef MBED_TEST_BLOCKDEVICE_DECL
+#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
+#endif
+
+#ifndef MBED_TEST_FILES
+#define MBED_TEST_FILES 4
+#endif
+
+#ifndef MBED_TEST_DIRS
+#define MBED_TEST_DIRS 4
+#endif
+
+#ifndef MBED_TEST_BUFFER
+#define MBED_TEST_BUFFER 8192
+#endif
+
+#ifndef MBED_TEST_TIMEOUT
+#define MBED_TEST_TIMEOUT 120
+#endif
+
+
+// declarations
+#define STRINGIZE(x) STRINGIZE2(x)
+#define STRINGIZE2(x) #x
+#define INCLUDE(x) STRINGIZE(x.h)
+
+#include INCLUDE(MBED_TEST_FILESYSTEM)
+#include INCLUDE(MBED_TEST_BLOCKDEVICE)
+
+MBED_TEST_FILESYSTEM_DECL;
+MBED_TEST_BLOCKDEVICE_DECL;
+
+Dir dir[MBED_TEST_DIRS];
+File file[MBED_TEST_FILES];
+DIR *dd[MBED_TEST_DIRS];
+FILE *fd[MBED_TEST_FILES];
+struct dirent ent;
+struct dirent *ed;
+size_t size;
+uint8_t buffer[MBED_TEST_BUFFER];
+uint8_t rbuffer[MBED_TEST_BUFFER];
+uint8_t wbuffer[MBED_TEST_BUFFER];
+
+static char file_counter = 0;
+const char *filenames[] = {"smallavacado", "mediumavacado", "largeavacado",
+                          "blockfile", "bigblockfile", "hello", ".", ".."};
+
+// tests
+
+void test_file_tests() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = MBED_TEST_FILESYSTEM::format(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_simple_file_test() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "hello", O_WRONLY | O_CREAT);
+        TEST_ASSERT_EQUAL(0, res);
+        size = strlen("Hello World!\n");
+        memcpy(wbuffer, "Hello World!\n", size);
+        res = file[0].write(wbuffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "hello", O_RDONLY);
+        TEST_ASSERT_EQUAL(0, res);
+        size = strlen("Hello World!\n");
+        res = file[0].read(rbuffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(rbuffer, wbuffer, size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+template <int file_size, int write_size, int read_size>
+void test_write_file_test() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        size_t size = file_size;
+        size_t chunk = write_size;
+        srand(0);
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, filenames[file_counter], O_WRONLY | O_CREAT);
+        TEST_ASSERT_EQUAL(0, res);
+        for (size_t i = 0; i < size; i += chunk) {
+            chunk = (chunk < size - i) ? chunk : size - i;
+            for (size_t b = 0; b < chunk; b++) {
+                buffer[b] = rand() & 0xff;
+            }
+            res = file[0].write(buffer, chunk);
+            TEST_ASSERT_EQUAL(chunk, res);
+        }
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        size_t size = file_size;
+        size_t chunk = read_size;
+        srand(0);
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, filenames[file_counter], O_RDONLY);
+        TEST_ASSERT_EQUAL(0, res);
+        for (size_t i = 0; i < size; i += chunk) {
+            chunk = (chunk < size - i) ? chunk : size - i;
+            res = file[0].read(buffer, chunk);
+            TEST_ASSERT_EQUAL(chunk, res);
+            for (size_t b = 0; b < chunk && i+b < size; b++) {
+                res = buffer[b];
+                TEST_ASSERT_EQUAL(rand() & 0xff, res);
+            }
+        }
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    file_counter++;
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_non_overlap_check() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        size_t size = 32;
+        size_t chunk = 29;
+        srand(0);
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "smallavacado", O_RDONLY);
+        TEST_ASSERT_EQUAL(0, res);
+        for (size_t i = 0; i < size; i += chunk) {
+            chunk = (chunk < size - i) ? chunk : size - i;
+            res = file[0].read(buffer, chunk);
+            TEST_ASSERT_EQUAL(chunk, res);
+            for (size_t b = 0; b < chunk && i+b < size; b++) {
+                res = buffer[b];
+                TEST_ASSERT_EQUAL(rand() & 0xff, res);
+            }
+        }
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        size_t size = 8192;
+        size_t chunk = 29;
+        srand(0);
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "mediumavacado", O_RDONLY);
+        TEST_ASSERT_EQUAL(0, res);
+        for (size_t i = 0; i < size; i += chunk) {
+            chunk = (chunk < size - i) ? chunk : size - i;
+            res = file[0].read(buffer, chunk);
+            TEST_ASSERT_EQUAL(chunk, res);
+            for (size_t b = 0; b < chunk && i+b < size; b++) {
+                res = buffer[b];
+                TEST_ASSERT_EQUAL(rand() & 0xff, res);
+            }
+        }
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    {
+        size_t size = 262144;
+        size_t chunk = 29;
+        srand(0);
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "largeavacado", O_RDONLY);
+        TEST_ASSERT_EQUAL(0, res);
+        for (size_t i = 0; i < size; i += chunk) {
+            chunk = (chunk < size - i) ? chunk : size - i;
+            res = file[0].read(buffer, chunk);
+            TEST_ASSERT_EQUAL(chunk, res);
+            for (size_t b = 0; b < chunk && i+b < size; b++) {
+                res = buffer[b];
+                TEST_ASSERT_EQUAL(rand() & 0xff, res);
+            }
+        }
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_dir_check() {
+
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].open(&fs, "/");
+        TEST_ASSERT_EQUAL(0, res);
+        int numFiles = sizeof(filenames)/sizeof(filenames[0]);
+        // Check the filenames in directory
+        while(1) {
+            res = dir[0].read(&ent);
+            if (0 == res) {
+                break;
+            }
+            for (int i=0; i < numFiles ; i++) {
+                res = strcmp(ent.d_name, filenames[i]);
+                if (0 == res) {
+                    res = ent.d_type;
+                    if ((DT_REG != res) && (DT_DIR != res)) {
+                        TEST_ASSERT(1);
+                    }
+                    break;
+                }
+                else if( i == numFiles) {
+                    TEST_ASSERT_EQUAL(0, res);
+                }
+            }
+        }
+        res = dir[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+
+// test setup
+utest::v1::status_t test_setup(const size_t number_of_cases) {
+    GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
+    return verbose_test_setup_handler(number_of_cases);
+}
+
+Case cases[] = {
+    Case("File tests", test_file_tests),
+    Case("Simple file test", test_simple_file_test),
+    Case("Small file test", test_write_file_test<32, 31, 29>),
+    Case("Medium file test", test_write_file_test<8192, 31, 29>),
+    Case("Large file test", test_write_file_test<262144, 31, 29>),
+    Case("Block Size file test", test_write_file_test<9000, 512, 512>),
+    Case("Multiple block size file test", test_write_file_test<26215, MBED_TEST_BUFFER, MBED_TEST_BUFFER>),
+    Case("Non-overlap check", test_non_overlap_check),
+    Case("Dir check", test_dir_check),
+};
+
+Specification specification(test_setup, cases);
+
+int main() {
+    return !Harness::run(specification);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/TESTS/filesystem/fopen/fopen.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,1519 @@
+/*
+ * mbed Microcontroller Library
+ * Copyright (c) 2006-2016 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** @file fopen.cpp Test cases to POSIX file fopen() interface.
+ *
+ * Please consult the documentation under the test-case functions for
+ * a description of the individual test case.
+ */
+
+#include "mbed.h"
+#include "mbed_config.h"
+#include "SDBlockDevice.h"
+#include "FATFileSystem.h"
+#include "fsfat_debug.h"
+#include "fsfat_test.h"
+#include "utest/utest.h"
+#include "unity/unity.h"
+#include "greentea-client/test_env.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>     /*rand()*/
+#include <inttypes.h>
+#include <errno.h>
+/* mbed_retarget.h is included after errno.h so symbols are mapped to
+ * consistent values for all toolchains */
+#include "platform/mbed_retarget.h"
+
+using namespace utest::v1;
+
+/// @cond FSFAT_DOXYGEN_DISABLE
+#ifdef FSFAT_DEBUG
+#define FSFAT_FOPEN_GREENTEA_TIMEOUT_S     3000
+#else
+#define FSFAT_FOPEN_GREENTEA_TIMEOUT_S     1000
+#endif
+/// @endcond
+
+
+/* DEVICE_SPI
+ *  This symbol is defined in targets.json if the target has a SPI interface, which is required for SDCard support.
+ *
+ * MBED_CONF_APP_FSFAT_SDCARD_INSTALLED
+ *  For testing purposes, an SDCard must be installed on the target for the test cases in this file to succeed.
+ *  If the target has an SD card installed then the MBED_CONF_APP_FSFAT_SDCARD_INSTALLED will be generated
+ *  from the mbed_app.json, which includes the line
+ *    {
+ *    "config": {
+ *        "UART_RX": "D0",
+ *        <<< lines removed >>>
+ *        "DEVICE_SPI": 1,
+ *        "FSFAT_SDCARD_INSTALLED": 1
+ *      },
+ *  	<<< lines removed >>>
+ */
+ 
+#if defined(DEVICE_SPI) && ( defined(MBED_CONF_APP_FSFAT_SDCARD_INSTALLED) || (MBED_CONF_SD_FSFAT_SDCARD_INSTALLED))
+static char fsfat_fopen_utest_msg_g[FSFAT_UTEST_MSG_BUF_SIZE];
+#define FSFAT_FOPEN_TEST_MOUNT_PT_NAME      "sd"
+#define FSFAT_FOPEN_TEST_MOUNT_PT_PATH      "/" FSFAT_FOPEN_TEST_MOUNT_PT_NAME
+#define FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1    64
+#define FSFAT_FOPEN_TEST_FILEPATH_MAX_DEPTH 20
+static const char *sd_badfile_path = "/sd/badfile.txt";
+static const char *sd_testfile_path = "/sd/test.txt";
+
+SDBlockDevice sd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
+FATFileSystem fs("sd", &sd);
+
+#define FSFAT_FOPEN_TEST_01      fsfat_fopen_test_01
+#define FSFAT_FOPEN_TEST_02      fsfat_fopen_test_02
+#define FSFAT_FOPEN_TEST_03      fsfat_fopen_test_03
+#define FSFAT_FOPEN_TEST_04      fsfat_fopen_test_04
+#define FSFAT_FOPEN_TEST_05      fsfat_fopen_test_05
+#define FSFAT_FOPEN_TEST_06      fsfat_fopen_test_06
+#define FSFAT_FOPEN_TEST_07      fsfat_fopen_test_07
+#define FSFAT_FOPEN_TEST_08      fsfat_fopen_test_08
+#define FSFAT_FOPEN_TEST_09      fsfat_fopen_test_09
+#define FSFAT_FOPEN_TEST_10      fsfat_fopen_test_10
+#define FSFAT_FOPEN_TEST_11      fsfat_fopen_test_11
+#define FSFAT_FOPEN_TEST_12      fsfat_fopen_test_12
+#define FSFAT_FOPEN_TEST_13      fsfat_fopen_test_13
+#define FSFAT_FOPEN_TEST_14      fsfat_fopen_test_14
+#define FSFAT_FOPEN_TEST_15      fsfat_fopen_test_15
+#define FSFAT_FOPEN_TEST_16      fsfat_fopen_test_16
+#define FSFAT_FOPEN_TEST_17      fsfat_fopen_test_17
+#define FSFAT_FOPEN_TEST_18      fsfat_fopen_test_18
+#define FSFAT_FOPEN_TEST_19      fsfat_fopen_test_19
+#define FSFAT_FOPEN_TEST_20      fsfat_fopen_test_20
+#define FSFAT_FOPEN_TEST_21      fsfat_fopen_test_21
+#define FSFAT_FOPEN_TEST_22      fsfat_fopen_test_22
+#define FSFAT_FOPEN_TEST_23      fsfat_fopen_test_23
+#define FSFAT_FOPEN_TEST_24      fsfat_fopen_test_24
+#define FSFAT_FOPEN_TEST_25      fsfat_fopen_test_25
+#define FSFAT_FOPEN_TEST_26      fsfat_fopen_test_26
+#define FSFAT_FOPEN_TEST_27      fsfat_fopen_test_27
+#define FSFAT_FOPEN_TEST_28      fsfat_fopen_test_28
+#define FSFAT_FOPEN_TEST_29      fsfat_fopen_test_29
+#define FSFAT_FOPEN_TEST_30      fsfat_fopen_test_30
+
+
+/* support functions */
+
+/*
+ * open tests that focus on testing fopen()
+ * fsfat_handle_t fopen(const char* filename, char* data, size_t* len, fsfat_key_desc_t* kdesc)
+ */
+
+/* file data for test_01 */
+static fsfat_kv_data_t fsfat_fopen_test_01_kv_data[] = {
+        { "/sd/fopentst/hello/world/animal/wobbly/dog/foot/frontlft.txt", "missing"},
+        { NULL, NULL},
+};
+
+
+/** @brief
+ * Split a file path into its component parts, setting '/' characters to '\0', and returning
+ * pointers to the file path components in the parts array. For example, if
+ * filepath = "/sd/fopentst/hello/world/animal/wobbly/dog/foot/frontlft.txt" then
+ *  *parts[0] = "sd"
+ *  *parts[1] = "fopentst"
+ *  *parts[2] = "hello"
+ *  *parts[3] = "world"
+ *  *parts[4] = "animal"
+ *  *parts[5] = "wobbly"
+ *  *parts[6] = "dog"
+ *  *parts[7] = "foot"
+ *  *parts[8] = "frontlft.txt"
+ *   parts[9] = NULL
+ *
+ * ARGUMENTS
+ *  @param  filepath     IN file path string to split into component parts. Expected to start with '/'
+ *  @param  parts        IN OUT array to hold pointers to parts
+ *  @param  num          IN number of components available in parts
+ *
+ * @return  On success, this returns the number of components in the filepath Returns number of compoee
+ */
+static int32_t fsfat_filepath_split(char* filepath, char* parts[], uint32_t num)
+{
+    uint32_t i = 0;
+    int32_t ret = -1;
+    char* z = filepath;
+
+    while (i < num && *z != '\0') {
+        if (*z == '/' ) {
+            *z = '\0';
+            parts[i] = ++z;
+            i++;
+        } else {
+            z++;
+        }
+    }
+    if (*z == '\0' && i > 0) {
+        ret = (int32_t) i;
+    }
+    return ret;
+}
+
+
+/** @brief
+ * remove all directories and file in the given filepath
+ *
+ * ARGUMENTS
+ *  @param  filepath     IN file path string to split into component parts. Expected to start with '/'
+ *
+ * @return  On success, this returns 0, otherwise < 0 is returned;
+ */
+int32_t fsfat_filepath_remove_all(char* filepath)
+{
+    int32_t ret = -1;
+    int32_t len = 0;
+    char *fpathbuf = NULL;
+    char *pos = NULL;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    len = strlen(filepath);
+    fpathbuf = (char*) malloc(len+1);
+    if (fpathbuf == NULL) {
+        FSFAT_DBGLOG("%s: failed to duplicate string (out of memory)\n", __func__);
+        return ret;
+    }
+    memset(fpathbuf, 0, len+1);
+    memcpy(fpathbuf, filepath, len);
+
+    /* delete the leaf node first, and then successively parent directories. */
+    pos = fpathbuf + strlen(fpathbuf);
+    while (pos != fpathbuf) {
+        /* If the remaining file path is the mount point path then finish as the mount point cannot be removed */
+        if (strlen(fpathbuf) == strlen(FSFAT_FOPEN_TEST_MOUNT_PT_PATH)) {
+            if( strncmp(fpathbuf, FSFAT_FOPEN_TEST_MOUNT_PT_PATH, strlen(fpathbuf)) == 0) {
+                break;
+            }
+        }
+        ret = remove(fpathbuf);
+        pos = strrchr(fpathbuf, '/');
+        *pos = '\0';
+    }
+    if (fpathbuf) {
+        free(fpathbuf);
+    }
+    return ret;
+}
+
+
+/** @brief
+ * make all directories in the given filepath. Do not create the file if present at end of filepath
+ *
+ * ARGUMENTS
+ *  @param  filepath     IN file path containing directories and file
+ *  @param  do_asserts   IN set to true if function should assert on errors
+ *
+ * @return  On success, this returns 0, otherwise < 0 is returned;
+ */
+static int32_t fsfat_filepath_make_dirs(char* filepath, bool do_asserts)
+{
+    int32_t i = 0;
+    int32_t num_parts = 0;
+    int32_t len = 0;
+    int32_t ret = -1;
+    char *fpathbuf = NULL;
+    char *buf = NULL;
+    int pos = 0;
+    char *parts[FSFAT_FOPEN_TEST_FILEPATH_MAX_DEPTH];
+
+    FSFAT_DBGLOG("%s:entered\n", __func__);
+    /* find the dirs to create*/
+    memset(parts, 0, sizeof(parts));
+    len = strlen(filepath);
+    fpathbuf = (char*) malloc(len+1);
+    if (fpathbuf == NULL) {
+        FSFAT_DBGLOG("%s: failed to duplicate string (out of memory)\n", __func__);
+        return ret;
+    }
+    memset(fpathbuf, 0, len+1);
+    memcpy(fpathbuf, filepath, len);
+    num_parts = fsfat_filepath_split(fpathbuf, parts, FSFAT_FOPEN_TEST_FILEPATH_MAX_DEPTH);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to split filepath (filename=\"%s\", num_parts=%d)\n", __func__, filepath, (int) num_parts);
+    TEST_ASSERT_MESSAGE(num_parts > 0, fsfat_fopen_utest_msg_g);
+
+    /* Now create the directories on the directory path.
+     * Skip creating dir for "/sd" which must be present */
+    buf = (char*) malloc(strlen(filepath)+1);
+    memset(buf, 0, strlen(filepath)+1);
+    pos = sprintf(buf, "/%s", parts[0]);
+    for (i = 1; i < num_parts - 1; i++) {
+        pos += sprintf(buf+pos, "/%s", parts[i]);
+        FSFAT_DBGLOG("mkdir(%s)\n", buf);
+        ret = mkdir(buf, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+        if (do_asserts == true) {
+            FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create directory (filepath2=\"%s\", ret=%d, errno=%d)\n", __func__, buf, (int) ret, errno);
+            TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+        }
+    }
+
+    if (buf) {
+        free(buf);
+    }
+    if (fpathbuf) {
+        free(fpathbuf);
+    }
+    return ret;
+}
+
+
+/* FIX ME: errno not set correctly when error occurs. This indicates a problem with the implementation. */
+
+/** @brief
+ * Basic fopen test which does the following:
+ * - creates file and writes some data to the value blob.
+ * - closes the newly created file.
+ * - opens the file (r-only)
+ * - reads the file data and checks its the same as the previously created data.
+ * - closes the opened file
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+static control_t fsfat_fopen_test_01(const size_t call_count)
+{
+    char* read_buf;
+    int32_t ret = 0;
+    size_t len = 0;
+    fsfat_kv_data_t *node;
+    FILE *fp = NULL;
+
+    FSFAT_DBGLOG("%s:entered\n", __func__);
+    (void) call_count;
+    node = fsfat_fopen_test_01_kv_data;
+
+    /* remove file and directory from a previous failed test run, if present */
+    fsfat_filepath_remove_all((char*) node->filename);
+
+    /* create dirs */
+    ret = fsfat_filepath_make_dirs((char*) node->filename, true);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dirs for filename (filename=\"%s\")(ret=%d)\n", __func__, node->filename, (int) ret);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    FSFAT_DBGLOG("%s:About to create new file (filename=\"%s\", data=\"%s\")\n", __func__, node->filename, node->value);
+    fp = fopen(node->filename, "w+");
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (filename=\"%s\", data=\"%s\")(ret=%d, errno=%d)\n", __func__, node->filename, node->value, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g);
+
+    FSFAT_DBGLOG("%s:length of file=%d (filename=\"%s\", data=\"%s\")\n", __func__, (int) len, node->filename, node->value);
+    len = strlen(node->value);
+    ret = fwrite((const void*) node->value, len, 1, fp);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to write file (filename=\"%s\", data=\"%s\")(ret=%d)\n", __func__, node->filename, node->value, (int) ret);
+    TEST_ASSERT_MESSAGE(ret == 1, fsfat_fopen_utest_msg_g);
+
+    FSFAT_DBGLOG("Created file successfully (filename=\"%s\", data=\"%s\")\n", node->filename, node->value);
+    ret = fclose(fp);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to close file (ret=%d, errno=%d)\n", __func__, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    /* now open the newly created key */
+    fp = NULL;
+    fp = fopen(node->filename, "r");
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file for reading (filename=\"%s\", data=\"%s\")(ret=%d)\n", __func__, node->filename, node->value, (int) ret);
+    TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g);
+
+    len = strlen(node->value) + 1;
+    read_buf = (char*) malloc(len);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to allocated read buffer \n", __func__);
+    TEST_ASSERT_MESSAGE(read_buf != NULL, fsfat_fopen_utest_msg_g);
+
+    FSFAT_DBGLOG("Opened file successfully (filename=\"%s\", data=\"%s\")\n", node->filename, node->value);
+    memset(read_buf, 0, len);
+    ret = fread((void*) read_buf, len, 1, fp);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to read file (filename=\"%s\", data=\"%s\", read_buf=\"%s\", ret=%d)\n", __func__, node->filename, node->value, read_buf, (int) ret);
+    /* FIX ME: fread should return the number of items read, not 0 when an item is read successfully.
+     * This indicates a problem with the implementation, as the correct data is read. The correct assert should be:
+     *   TEST_ASSERT_MESSAGE(ret == 1, fsfat_fopen_utest_msg_g);
+     * The following assert is curerntly used until the implementation is fixed
+     */
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    /* check read data is as expected */
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: read value data (%s) != expected value data (filename=\"%s\", data=\"%s\", read_buf=\"%s\", ret=%d)\n", __func__, read_buf, node->filename, node->value, read_buf, (int) ret);
+    TEST_ASSERT_MESSAGE(strncmp(read_buf, node->value, strlen(node->value)) == 0, fsfat_fopen_utest_msg_g);
+
+    if(read_buf){
+        free(read_buf);
+    }
+    ret = fclose(fp);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: fclose() call failed (ret=%d, errno=%d).\n", __func__, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+    return CaseNext;
+}
+
+static fsfat_kv_data_t fsfat_fopen_test_02_data[] = {
+        FSFAT_INIT_1_TABLE_MID_NODE,
+        { NULL, NULL},
+};
+
+/**
+ * @brief   test to fopen() a pre-existing key and try to write it, which should fail
+ *          as by default pre-existing keys are opened read-only
+ *
+ * Basic open test which does the following:
+ * - creates file with default rw perms and writes some data to the value blob.
+ * - closes the newly created file.
+ * - opens the file with the default permissions (read-only)
+ * - tries to write the file data which should fail because file was not opened with write flag set.
+ * - closes the opened key
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_02(const size_t call_count)
+{
+    int32_t ret = -1;
+    size_t len = 0;
+    FILE *fp = NULL;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+    len = strlen(fsfat_fopen_test_02_data[0].value);
+    ret = fsfat_test_create(fsfat_fopen_test_02_data[0].filename, (char*) fsfat_fopen_test_02_data[0].value, len);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (ret=%d).\n", __func__, (int) ret);
+    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+
+    /* by default, owner of key opens with read-only permissions*/
+    fp = fopen(fsfat_fopen_test_02_data[0].filename, "r");
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file (filename=\"%s\", ret=%d)\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret);
+    TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g);
+
+    len = strlen(fsfat_fopen_test_02_data[0].value);
+    ret = fwrite((const void*) fsfat_fopen_test_02_data[0].value, len, 1, fp);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: call to fwrite() succeeded when should have failed for read-only file (filename=\"%s\")(ret=%d).\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret);
+    TEST_ASSERT_MESSAGE(ret <= 0, fsfat_fopen_utest_msg_g);
+
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: fclose() call failed.\n", __func__);
+    TEST_ASSERT_MESSAGE(fclose(fp) == 0, fsfat_fopen_utest_msg_g);
+
+    return CaseNext;
+}
+
+
+/**
+ * @brief   test to fopen() a pre-existing file and try to write it, which should succeed
+ *          because the key was opened read-write permissions explicitly
+ *
+ * Basic open test which does the following:
+ * - creates file with default rw perms and writes some data to the value blob.
+ * - closes the newly created file.
+ * - opens the file with the rw permissions (non default)
+ * - tries to write the file data which should succeeds because file was opened with write flag set.
+ * - closes the opened key
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_03(const size_t call_count)
+{
+    int32_t ret = -1;
+    size_t len = 0;
+    FILE *fp = NULL;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+    len = strlen(fsfat_fopen_test_02_data[0].value);
+    ret = fsfat_test_create(fsfat_fopen_test_02_data[0].filename, (char*) fsfat_fopen_test_02_data[0].value, len);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file in store (ret=%d).\n", __func__, (int) ret);
+    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+
+    /* opens with read-write permissions*/
+    fp = fopen(fsfat_fopen_test_02_data[0].filename, "w+");
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file (filename=\"%s\")(ret=%d)\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret);
+    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+
+    len = strlen(fsfat_fopen_test_02_data[0].value);
+    ret = fwrite((const void*) fsfat_fopen_test_02_data[0].value, len, 1, fp);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: call to fwrite() failed when should have succeeded (filename=\"%s\", ret=%d).\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret);
+    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: fclose() call failed.\n", __func__);
+    TEST_ASSERT_MESSAGE(fclose(fp) >= 0, fsfat_fopen_utest_msg_g);
+
+    /* clean-up */
+    ret = remove(fsfat_fopen_test_02_data[0].filename);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unable to delete file (filename=%s, ret=%d) .\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret);
+    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+
+    return CaseNext;
+}
+
+
+/** @brief  test to call fopen() with a filename string that exceeds the maximum length
+ * - chanFS supports the exFAT format which should support 255 char filenames
+ * - check that filenames of this length can be created
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_04(const size_t call_count)
+{
+    char filename_good[FSFAT_FILENAME_MAX_LENGTH+1];
+    char filename_bad[FSFAT_FILENAME_MAX_LENGTH+2];
+    int32_t ret = -1;
+    size_t len = 0;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+    memset(filename_good, 0, FSFAT_FILENAME_MAX_LENGTH+1);
+    memset(filename_bad, 0, FSFAT_FILENAME_MAX_LENGTH+2);
+    ret = fsfat_test_filename_gen(filename_good, FSFAT_FILENAME_MAX_LENGTH);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate filename_good.\n", __func__);
+    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: filename_good is not the correct length (filename_good=%s, len=%d, expected=%d).\n", __func__, filename_good, (int) strlen(filename_good), (int) FSFAT_FILENAME_MAX_LENGTH);
+    TEST_ASSERT_MESSAGE(strlen(filename_good) == FSFAT_FILENAME_MAX_LENGTH, fsfat_fopen_utest_msg_g);
+
+    ret = fsfat_test_filename_gen(filename_bad, FSFAT_FILENAME_MAX_LENGTH+1);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unable to generate filename_bad.\n", __func__);
+    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: filename_bad is not the correct length (len=%d, expected=%d).\n", __func__, (int) strlen(filename_bad), (int) FSFAT_FILENAME_MAX_LENGTH+1);
+    TEST_ASSERT_MESSAGE(strlen(filename_bad) == FSFAT_FILENAME_MAX_LENGTH+1, fsfat_fopen_utest_msg_g);
+
+    len = strlen(filename_good);
+    ret = fsfat_test_create(filename_good, filename_good, len);
+    /* FIXME:
+     * The current implementation can create file with a filename with 9 chars (more than the 8 restriction of FAT32 Short File Names).
+     * However, the exFAT 255 char filesnames is not supported and hence the following is commented out. Find out what is
+     * the supported max filename length and change this testcase according.
+     *
+     *  FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (filename=%s, ret=%d).\n", __func__, filename_good, (int) ret);
+     *  TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+     */
+
+    len = strlen(filename_bad);
+    ret = fsfat_test_create(filename_bad, filename_bad, len);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: created file in store for filename_bad when should have failed (filename=%s, ret=%d).\n", __func__, filename_bad, (int) ret);
+    TEST_ASSERT_MESSAGE(ret < 0, fsfat_fopen_utest_msg_g);
+    return CaseNext;
+}
+
+
+/// @cond FSFAT_DOXYGEN_DISABLE
+typedef struct fsfat_fopen_kv_name_ascii_node {
+    uint32_t code;
+    uint32_t f_allowed : 1;
+} fsfat_fopen_kv_name_ascii_node;
+/// @endcond
+
+static const uint32_t fsfat_fopen_kv_name_ascii_table_code_sentinel_g = 256;
+
+/*@brief    table recording ascii character codes permitted in kv names */
+static fsfat_fopen_kv_name_ascii_node fsfat_fopen_kv_name_ascii_table[] =
+{
+        {0 , true},         /* code 0-33 allowed*/
+        {34, false},        /* '"' not allowed */
+        {35, true},         /* allowed */
+        {42, false},        /* '*' not allowed */
+        {43, true},         /* allowed */
+        {47, false},        /* '/' not allowed */
+        {48, true},         /* allowed */
+        {58, false},        /* ':' not allowed */
+        {59, true},         /* allowed */
+        {60, false},        /* '<' not allowed */
+        {61, true},         /* allowed */
+        {62, false},        /* '?', '>' not allowed */
+        {64, true},         /* allowed */
+        {92, false},        /* '\' not allowed */
+        {93, true},         /* allowed */
+        {124, false},        /* '!' not allowed */
+        {125, true},         /* allowed */
+        {127, false},        /* DEL not allowed */
+        {128, true},         /* allowed */
+        {fsfat_fopen_kv_name_ascii_table_code_sentinel_g, false},       /* sentinel */
+};
+
+
+/// @cond FSFAT_DOXYGEN_DISABLE
+enum fsfat_fopen_kv_name_pos {
+    fsfat_fopen_kv_name_pos_start = 0x0,
+    fsfat_fopen_kv_name_pos_mid,
+    fsfat_fopen_kv_name_pos_end,
+    fsfat_fopen_kv_name_pos_max
+};
+/// @endcond
+
+/** @brief  test to call fopen() with filename that in includes illegal characters
+ *          - the character(s) can be at the beginning of the filename
+ *          - the character(s) can be at the end of the filename
+ *          - the character(s) can be somewhere within the filename string
+ *          - a max-length string of random characters (legal and illegal)
+ *          - a max-length string of random illegal characters only
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_05(const size_t call_count)
+{
+    bool f_allowed = false;
+    const char *mnt_pt = FSFAT_FOPEN_TEST_MOUNT_PT_PATH;
+    const char *basename = "goodfile";
+    const char *extname = "txt";
+    const size_t basename_len = strlen(basename);
+    const size_t filename_len = strlen(mnt_pt)+strlen(basename)+strlen(extname)+2;  /* extra 2 chars for '/' and '.' in "/sd/goodfile.txt" */
+    char filename[FSFAT_BUF_MAX_LENGTH];
+    size_t len = 0;
+    uint32_t j = 0;
+    int32_t ret = 0;
+    fsfat_fopen_kv_name_ascii_node* node = NULL;
+    uint32_t pos;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+#ifdef FSFAT_DEBUG
+    /* symbol only used why debug is enabled */
+    const char* pos_str = NULL;
+#endif
+
+    /* create bad keyname strings with invalid character code at start of keyname */
+    node = fsfat_fopen_kv_name_ascii_table;
+    memset(filename, 0, FSFAT_BUF_MAX_LENGTH);
+    while(node->code !=  fsfat_fopen_kv_name_ascii_table_code_sentinel_g)
+    {
+        /* loop over range */
+        for(j = node->code; j < (node+1)->code; j++)
+        {
+            if( (j >= 48 && j <= 57) || (j >= 65 && j <= 90) || (j >= 97 && j <= 122)) {
+                FSFAT_DBGLOG("%s: skipping alpha-numeric ascii character code %d (%c).\n", __func__, (int) j, (char) j);
+                continue;
+            }
+
+            /* set the start, mid, last character of the name to the test char code */
+            for(pos = (uint32_t) fsfat_fopen_kv_name_pos_start; pos < (uint32_t) fsfat_fopen_kv_name_pos_max; pos++)
+            {
+                len = snprintf(filename, filename_len+1, "%s/%s.%s", mnt_pt, basename, extname);
+                /* overwrite a char at the pos start, mid, end of the filename with an ascii char code (both illegal and legal)*/
+                switch(pos)
+                {
+                case fsfat_fopen_kv_name_pos_start:
+                    filename[5] = (char) j; /* 5 so at to write the second basename char (bad chars as first char not accepted)*/
+                    break;
+                case fsfat_fopen_kv_name_pos_mid:
+                    /* create bad keyname strings with invalid character code in the middle of keyname */
+                    filename[5+basename_len/2] = (char) j;
+                    break;
+                case fsfat_fopen_kv_name_pos_end:
+                    /* create bad keyname strings with invalid character code at end of keyname */
+                    filename[5+basename_len-1] = (char) j;
+                    break;
+                default:
+                    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected value of pos (pos=%d).\n", __func__, (int) pos);
+                    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+                    break;
+                }
+
+#ifdef FSFAT_DEBUG
+                /* processing only required when debug trace enabled */
+                switch(pos)
+                {
+                case fsfat_fopen_kv_name_pos_start:
+                    pos_str = "start";
+                    break;
+                case fsfat_fopen_kv_name_pos_mid:
+                    pos_str = "middle";
+                    break;
+                case fsfat_fopen_kv_name_pos_end:
+                    pos_str = "end";
+                    break;
+                default:
+                    break;
+                }
+#endif
+                ret = fsfat_test_create(filename, (const char*) filename, len);
+
+                /* special cases */
+                switch(j)
+                {
+                //case 0 :
+				//case 46 :
+                //    switch(pos)
+                //    {
+                //    /* for code = 0 (null terminator). permitted at mid and end of string */
+                //    /* for code = 46 ('.'). permitted at mid and end of string but not at start */
+                //    case fsfat_fopen_kv_name_pos_start:
+                //        f_allowed = false;
+                //        break;
+                //    case fsfat_fopen_kv_name_pos_mid:
+                //    case fsfat_fopen_kv_name_pos_end:
+                //    default:
+                //        f_allowed = true;
+                //        break;
+                //    }
+                //    break;
+				default:
+					f_allowed = node->f_allowed;
+					break;
+                }
+                if(f_allowed == true)
+                {
+                    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file in store when filename contains valid characters (code=%d, ret=%d).\n", __func__, (int) j, (int) ret);
+                    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+                    /* revert FSFAT_LOG for more trace */
+                    FSFAT_DBGLOG("Successfully created a file with valid keyname containing ascii character code %d (%c) at the %s of the keyname.\n", (int) j, (int) j, pos_str);
+                    FSFAT_LOG("%c", '.');
+
+                    ret = fsfat_test_delete(filename);
+                    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to delete file previously created (code=%d, ret=%d).\n", __func__, (int) j, (int) ret);
+                    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+                }
+                else
+                {   /*node->f_allowed == false => not allowed to create kv name with ascii code */
+                    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: created file in store when filename contains an invalid character (code=%d, ret=%d).\n", __func__, (int) j, (int) ret);
+                    TEST_ASSERT_MESSAGE(ret < 0, fsfat_fopen_utest_msg_g);
+                    /* revert FSFAT_LOG for more trace */
+                    FSFAT_DBGLOG("Successfully failed to create a file with an invalid keyname containing ascii character code %d at the %s of the keyname.\n", (int) j, pos_str);
+                    FSFAT_LOG("%c", '.');
+                }
+            }
+        }
+        node++;
+    }
+
+    FSFAT_LOG("%c", '\n');
+    return CaseNext;
+}
+
+
+static const char fsfat_fopen_ascii_illegal_buf_g[] = "\"�'*+,./:;<=>?[\\]|";
+
+/** @brief  test to call fopen() with filename that in includes
+ *          illegal characters
+ *          - a max-length string of random illegal characters only
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_06(const size_t call_count)
+{
+    const char *mnt_pt = FSFAT_FOPEN_TEST_MOUNT_PT_PATH;
+    const char *extname = "txt";
+    const size_t filename_len = strlen(mnt_pt)+FSFAT_MAX_FILE_BASENAME+strlen(extname)+2;  /* extra 2 chars for '/' and '.' in "/sd/goodfile.txt" */
+    char filename[FSFAT_BUF_MAX_LENGTH];
+    int32_t i = 0;
+    int32_t j = 0;
+    uint32_t pos = 0;
+    uint32_t len = 0;
+    int32_t ret = -1;
+    size_t buf_data_max = 0;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+    memset(filename, 0, FSFAT_BUF_MAX_LENGTH);
+    /* create bad keyname strings with invalid character code at start of keyname */
+    buf_data_max = strlen(fsfat_fopen_ascii_illegal_buf_g);
+
+    /* generate a number of illegal filenames */
+    for (j = 0; i < FSFAT_MAX_FILE_BASENAME; j++) {
+        /* generate a kv name of illegal chars*/
+        len = snprintf(filename, filename_len+1, "%s/", mnt_pt);
+        for (i = 0; i < FSFAT_MAX_FILE_BASENAME; i++) {
+            pos = rand() % (buf_data_max+1);
+            len += snprintf(filename+len, filename_len+1, "%c", fsfat_fopen_ascii_illegal_buf_g[pos]);
+
+        }
+        len += snprintf(filename+len, filename_len+1, ".%s", extname);
+        ret = fsfat_test_create(filename, filename, len);
+        FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: created file when filename contains invalid characters (filename=%s, ret=%d).\n", __func__, filename, (int) ret);
+        TEST_ASSERT_MESSAGE(ret < 0, fsfat_fopen_utest_msg_g);
+    }
+    return CaseNext;
+}
+
+
+/** @brief  test for errno reporting on a failed fopen()call
+ *
+ *	This test does the following:
+ *	- tries to open a file that does not exist for reading, and checks that a NULL pointer is returned.
+ *	- checks that errno is not 0 as there is an error.
+ *	- checks that ferror() returns 1 indicating an error exists.
+ *
+ * Note: see NOTE_1 below.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_07(const size_t call_count)
+{
+	FILE *f = NULL;
+	int ret = -1;
+    int errno_val = 0;
+    const char *filename = sd_badfile_path;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+    errno = 0;
+    /* this is expect to fail as the file doesnt exist */
+    f = fopen(filename,"r");
+
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: opened non-existent file for reading (filename=%s, f=%p).\n", __func__, filename, f);
+    TEST_ASSERT_MESSAGE(f == NULL, fsfat_fopen_utest_msg_g);
+
+    /* check errno is set correctly */
+#if ! defined(__ARMCC_VERSION) && defined(__GNUC__)
+    /* Store errno so the current value set  is not changed by new function call */
+    errno_val = errno;
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: errno has unexpected value (errno != 0 expected) (filename=%s, errno=%d).\n", __func__, filename, errno);
+    TEST_ASSERT_MESSAGE(errno_val != 0, fsfat_fopen_utest_msg_g);
+#endif  /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */
+    return CaseNext;
+}
+
+
+/** @brief  test for operation of clearerr() and ferror()
+ *
+ *  The test does the following:
+ *  - opens and then closes a file, but keeps a copy of the FILE pointer fp.
+ *  - set errno to 0.
+ *  - write to the close file with fwrite(fp) which should return 0 (no writes) and set the errno.
+ *  - check the error condition is set with ferror().
+ *  - clear the error with clearerr().
+ *  - check the error condition is reset with ferror().
+ *
+ * NOTE_1: GCC/ARMCC support for setting errno
+ *  - Documentation (e.g. fwrite() man page) does not explicity say fwrite() sets errno
+ *    (e.g. for an fwrite() on a read-only file).
+ *  - GCC libc fwrite() appears to set errno as expected.
+ *  - ARMCC & IAR libc fwrite() appears not to set errno.
+ *
+ * The following ARMCC documents are silent on whether fwrite() sets errno:
+ * - "ARM C and C++ Libraries and Floating-Point Support".
+ * - "RL-ARM User Guide fwrite() section".
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_08(const size_t call_count)
+{
+    FILE *fp = NULL;
+    int ret = -1;
+    int ret_ferror = -1;
+    const char *filename = sd_testfile_path;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+    errno = 0;
+    fp = fopen(filename,"w+");
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file (filename=%s, f=%p).\n", __func__, filename, fp);
+    TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g);
+
+    /* close the fp but then try to read or write it */
+    ret = fclose(fp);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to close file (ret=%d, errno=%d)\n", __func__, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    /* open file  */
+    errno = 0;
+    fp = fopen(filename, "r");
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file for reading (filename=\"%s\", ret=%d)\n", __func__, filename, (int) ret);
+    TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g);
+
+    /* Perform fwrite() operation that will fail. */
+    errno = 0;
+    ret = fwrite("42!", 4, 1, fp);
+
+    ret_ferror = ferror(fp);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: ferror() failed to report error (filename=%s, ret_ferror=%d).\n", __func__, filename, (int) ret_ferror);
+    TEST_ASSERT_MESSAGE(ret_ferror != 0, fsfat_fopen_utest_msg_g);
+
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: fwrite successfully wrote to read-only file (filename=%s, ret=%d).\n", __func__, filename, (int) ret);
+    /* the fwrite() should fail and return 0. */
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+#if ! defined(__ARMCC_VERSION) && defined(__GNUC__)
+    /* check that errno is set. ARMCC appears not to set errno for fwrite() failure. */
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected zero value for errno (filename=%s, ret=%d, errno=%d).\n", __func__, filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(errno != 0, fsfat_fopen_utest_msg_g);
+
+    /* check that errno is set to the expected value (this may change differ for different libc's) */
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: errno != EBADF (filename=%s, ret=%d, errno=%d).\n", __func__, filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(errno == EBADF, fsfat_fopen_utest_msg_g);
+#endif  /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */
+
+    /* check clearerr() return clears the error */
+    clearerr(fp);
+    ret = ferror(fp);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: ferror() did not return zero value when error has been cleared (filename=%s, ret=%d).\n", __func__, filename, (int) ret);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    fclose(fp);
+    return CaseNext;
+}
+
+
+/** @brief  test for operation of ftell()
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_09(const size_t call_count)
+{
+    FILE *fp = NULL;
+    int ret = -1;
+    int32_t len = 0;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+    /* create a file of a certain length */
+    len = strlen(fsfat_fopen_test_02_data[0].value);
+    ret = fsfat_test_create(fsfat_fopen_test_02_data[0].filename, (char*) fsfat_fopen_test_02_data[0].value, len);
+
+    errno = 0;
+    /* Open the file for reading so the file is not truncated to 0 length. */
+    fp = fopen(fsfat_fopen_test_02_data[0].filename, "r");
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to open file (filename=%s, fp=%p, errno=%d).\n", __func__, fsfat_fopen_test_02_data[0].filename, fp, errno);
+    TEST_ASSERT_MESSAGE(fp != NULL, fsfat_fopen_utest_msg_g);
+
+    errno = 0;
+    ret = fseek(fp, 0, SEEK_END);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: fseek() failed to SEEK_END (filename=%s, ret=%d, errno=%d).\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    errno = 0;
+    ret = ftell(fp);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: ftell() failed to report correct offset value (filename=%s, ret=%d, errno=%d).\n", __func__, fsfat_fopen_test_02_data[0].filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == len, fsfat_fopen_utest_msg_g);
+
+    errno = 0;
+    ret = fclose(fp);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to close file (ret=%d, errno=%d)\n", __func__, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    return CaseNext;
+}
+
+
+/* file data for test_10 */
+static fsfat_kv_data_t fsfat_fopen_test_10_kv_data[] = {
+        { "/sd/test_10/testfile.txt", "test_data"},
+        { NULL, NULL},
+};
+
+/** @brief  test for operation of remove()
+ *
+ * Performs the following tests:
+ *  1. test remove() on a file that exists. This should succeed.
+ *  2. test remove() on a dir that exists. This should succeed.
+ *  3. test remove() on a file that doesnt exist. This should fail. check errno set.
+ *  4. test remove() on a dir that doesnt exist. This should fail. check errno set.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_10(const size_t call_count)
+{
+    char buf[FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1];
+    char *pos = NULL;
+    int32_t ret = -1;
+    size_t len = 0;
+    fsfat_kv_data_t *node = fsfat_fopen_test_10_kv_data;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+    TEST_ASSERT(strlen(node->filename) < FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1);
+
+    /* start from a known state i.e. directory to be created in not present */
+    fsfat_filepath_remove_all((char*) node->filename);
+
+    /* (1) */
+    errno = 0;
+    ret = fsfat_filepath_make_dirs((char*) node->filename, false);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, node->filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    len = strlen(node->value);
+    ret = fsfat_test_create(node->filename, (char*) node->value, len);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (ret=%d).\n", __func__, (int) ret);
+    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+
+    ret = remove(node->filename);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: delete file operation failed (filename=%s, ret=%d) .\n", __func__, node->filename, (int) ret);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    /* (3) */
+    ret = remove(node->filename);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: deleted a file that doesn't exist (filename=%s, ret=%d, errno=%d) .\n", __func__, node->filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret != 0, fsfat_fopen_utest_msg_g);
+
+    /* (2) */
+    memset(buf, 0, FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1);
+    memcpy(buf, node->filename, strlen(node->filename));
+    pos = strrchr(buf, '/');
+    *pos = '\0';
+    ret = remove(buf);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: delete directory operation failed (directory name=%s, ret=%d, errno=%d).\n", __func__, buf, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    /* (4) */
+    ret = remove(buf);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: deleted a directory that doesn't exist (directory name=%s, ret=%d, errno=%d).\n", __func__, buf, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret != 0, fsfat_fopen_utest_msg_g);
+
+    return CaseNext;
+}
+
+
+/* file data for test_11 */
+static fsfat_kv_data_t fsfat_fopen_test_11_kv_data[] = {
+        { "/sd/test_11/step0.txt", "test_data"},
+        { "/sd/test_11/step1.txt", "test_data"},
+        { "/sd/test_11/subdir/step3.txt", "test_data"},
+        { NULL, NULL},
+};
+
+/** @brief  test for operation of rename()
+ *
+ * This test does the following:
+ *  1) test rename() on a file that exists to a new filename within the same directory.
+ *  2) test rename() on a file that exists to a new filename within a different directory.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_11(const size_t call_count)
+{
+    int32_t ret = -1;
+    size_t len = 0;
+    fsfat_kv_data_t *node = fsfat_fopen_test_11_kv_data;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+    TEST_ASSERT(strlen(node->filename) < FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1);
+
+    /* start from a known state i.e. directory to be created in not present, files not present */
+    while(node->filename != NULL) {
+        fsfat_filepath_remove_all((char*) node->filename);
+        node++;
+    }
+
+    /* create file and directories ready for rename() tests */
+    errno = 0;
+    node = fsfat_fopen_test_11_kv_data;
+    ret = fsfat_filepath_make_dirs((char*) node->filename, false);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, node->filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    len = strlen(node->value);
+    ret = fsfat_test_create(node->filename, (char*) node->value, len);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (ret=%d).\n", __func__, (int) ret);
+    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+
+    errno = 0;
+    node = &fsfat_fopen_test_11_kv_data[2];
+    ret = fsfat_filepath_make_dirs((char*) node->filename, false);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, node->filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    /* (1) */
+    ret = rename(fsfat_fopen_test_11_kv_data[0].filename, fsfat_fopen_test_11_kv_data[1].filename);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unable to rename file from (%s) to (%s) (ret=%d, errno=%d).\n", __func__, fsfat_fopen_test_11_kv_data[0].filename, fsfat_fopen_test_11_kv_data[1].filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    /* (2) */
+    ret = rename(fsfat_fopen_test_11_kv_data[1].filename, fsfat_fopen_test_11_kv_data[2].filename);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unable to rename file from (%s) to (%s) (ret=%d, errno=%d).\n", __func__, fsfat_fopen_test_11_kv_data[1].filename, fsfat_fopen_test_11_kv_data[2].filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    return CaseNext;
+}
+
+
+/* file data for test_12 */
+static fsfat_kv_data_t fsfat_fopen_test_12_kv_data[] = {
+        { "/sd/test_12/subdir/testfil1.txt", "testfil1.txt"},
+        { "/sd/test_12/testfil2.txt", "testfil2.txt"},
+        { "/sd/test_12/testfil3.txt", "testfil3.txt"},
+        { "/sd/test_12/testfil4.txt", "testfil4.txt"},
+        { "/sd/test_12/testfil5.txt", "testfil5.txt"},
+        { NULL, NULL},
+};
+
+/** @brief  test for operation of readdir().
+ *
+ * Note, rewinddir(), telldir() and seekdir() dont appear to work reliably.
+ * opendir() not available on ARM/IAR toolchains.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_12(const size_t call_count)
+{
+    char buf[FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1];
+    char *pos = NULL;
+    int32_t count = 0;
+    int32_t ret = -1;
+    size_t len = 0;
+    DIR *dir;
+    struct dirent *dp;
+    fsfat_kv_data_t *node = fsfat_fopen_test_12_kv_data;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+#if ! defined(__ARMCC_VERSION) && defined(__GNUC__)
+
+    /* start from a known state i.e. directory to be created in not present */
+    while(node->filename != NULL) {
+        fsfat_filepath_remove_all((char*) node->filename);
+        node++;
+    }
+
+    /* create a file */
+    node = fsfat_fopen_test_12_kv_data;
+    errno = 0;
+    ret = fsfat_filepath_make_dirs((char*) node->filename, false);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, node->filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    node = fsfat_fopen_test_12_kv_data;
+    while(node->filename != NULL) {
+        len = strlen(node->value);
+        ret = fsfat_test_create(node->filename, (char*) node->value, len);
+        FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (ret=%d).\n", __func__, (int) ret);
+        TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+        node++;
+    }
+
+    node = fsfat_fopen_test_12_kv_data;
+    memset(buf, 0, FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1);
+    memcpy(buf, node->filename, strlen(node->filename));
+    pos = strrchr(buf, '/');
+    *pos = '\0';
+    dir = opendir(buf);
+
+    while ((dp = readdir(dir)) != NULL) {
+        FSFAT_DBGLOG("%s: filename: \"%s\"\n", __func__, dp->d_name);
+        TEST_ASSERT_MESSAGE(dp != 0, "Error: readdir() failed\n");
+        FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected object name (name=%s, expected=%s).\n", __func__, dp->d_name, fsfat_fopen_test_12_kv_data[count].value);
+        TEST_ASSERT_MESSAGE(strncmp(dp->d_name, fsfat_fopen_test_12_kv_data[count].value, strlen(fsfat_fopen_test_12_kv_data[count].value)) == 0, fsfat_fopen_utest_msg_g);
+        count++;
+    }
+    closedir(dir);
+
+    /* cleanup */
+    node = fsfat_fopen_test_12_kv_data;
+    while(node->filename != NULL) {
+        fsfat_filepath_remove_all((char*) node->filename);
+        node++;
+    }
+#endif  /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */
+    return CaseNext;
+}
+
+
+/* file data for test_13 */
+static fsfat_kv_data_t fsfat_fopen_test_13_kv_data[] = {
+        /* a file is included in the filepath even though its not created by the test,
+         * as the fsfat_filepath_make_dirs() works with it present. */
+        { "/sd/test_13/dummy.txt", "testdir"},
+        { NULL, NULL},
+};
+/** @brief  test for operation of mkdir()/remove()
+ *
+ * This test checks that:
+ * - The mkdir() function successfully creates a directory that is not already present.
+ * - The mkdir() function returns EEXIST when trying to create a directory thats already present.
+ * - The remove() function successfully removes a directory that is present.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_13(const size_t call_count)
+{
+    int32_t ret = 0;
+
+    FSFAT_DBGLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+    /* start from a known state i.e. directory to be created in not present */
+    fsfat_filepath_remove_all((char*) fsfat_fopen_test_13_kv_data[0].filename);
+
+    errno = 0;
+    ret = fsfat_filepath_make_dirs((char*) fsfat_fopen_test_13_kv_data[0].filename, false);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, fsfat_fopen_test_13_kv_data[0].filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    /* check that get a suitable error when try to create it again.*/
+    errno = 0;
+    ret = fsfat_filepath_make_dirs((char*) fsfat_fopen_test_13_kv_data[0].filename, false);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: permitted to create directory when already exists (dirname=%s, ret=%d, errno=%d)\n", __func__, fsfat_fopen_test_13_kv_data[0].filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret != 0, fsfat_fopen_utest_msg_g);
+
+    /* check errno is as expected */
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: errno != EEXIST (dirname=%s, ret=%d, errno=%d)\n", __func__, fsfat_fopen_test_13_kv_data[0].filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(errno == EEXIST, fsfat_fopen_utest_msg_g);
+
+    ret = fsfat_filepath_remove_all((char*) fsfat_fopen_test_13_kv_data[0].filename);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to remove directory (dirname=%s, ret=%d, errno=%d)\n", __func__, fsfat_fopen_test_13_kv_data[0].filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    return CaseNext;
+}
+
+/* file data for test_14 */
+static fsfat_kv_data_t fsfat_fopen_test_14_kv_data[] = {
+        /* a file is included in the filepath even though its not created by the test,
+         * as the fsfat_filepath_make_dirs() works with it present. */
+        { "/sd/test_14/testfile.txt", "testdata"},
+        { NULL, NULL},
+};
+
+/** @brief  test for operation of stat()
+ *
+ * stat() is currently no supported by ARMCC and IAR toolchains libc.
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_14(const size_t call_count)
+{
+#if ! defined(__ARMCC_VERSION) && defined(__GNUC__)
+
+	char buf[FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1];
+    char *pos = NULL;
+    int32_t ret = -1;
+    size_t len = 0;
+    struct stat file_stat;
+    fsfat_kv_data_t *node = fsfat_fopen_test_14_kv_data;
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+    TEST_ASSERT(strlen(node->filename) < FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1);
+
+    /* start from a known state i.e. directory to be created in not present */
+    fsfat_filepath_remove_all((char*) node->filename);
+
+    /* Create file in a directory. */
+    errno = 0;
+    ret = fsfat_filepath_make_dirs((char*) node->filename, false);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dir (dirname=%s, ret=%d, errno=%d)\n", __func__, node->filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    len = strlen(node->value);
+    ret = fsfat_test_create(node->filename, (char*) node->value, len);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create file (ret=%d).\n", __func__, (int) ret);
+    TEST_ASSERT_MESSAGE(ret >= 0, fsfat_fopen_utest_msg_g);
+
+    /* Test stat() on the file returns the correct attribute set */
+    memset(&file_stat, 0, sizeof(file_stat));
+    ret = stat(node->filename, &file_stat);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: stat() operation on file failed (filename=%s, ret=%d, errno=%d).\n", __func__, node->filename, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: expected st_mode S_IFREG flag not set (filename=%s).\n", __func__, node->filename);
+    TEST_ASSERT_MESSAGE((file_stat.st_mode & S_IFREG) == S_IFREG, fsfat_fopen_utest_msg_g);
+
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected st_mode S_IFDIR flag set (filename=%s).\n", __func__, node->filename);
+    TEST_ASSERT_MESSAGE((file_stat.st_mode & S_IFDIR) != S_IFDIR, fsfat_fopen_utest_msg_g);
+
+    /* Test stat() on the directory returns the correct attribute set */
+    memset(&file_stat, 0, sizeof(file_stat));
+    memset(buf, 0, FSFAT_FOPEN_TEST_WORK_BUF_SIZE_1);
+    memcpy(buf, node->filename, strlen(node->filename));
+    pos = strrchr(buf, '/');
+    *pos = '\0';
+    ret = stat(buf, &file_stat);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: stat() operation on directory failed (directory name=%s, ret=%d, errno=%d).\n", __func__, buf, (int) ret, errno);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: unexpected st_mode S_IFREG flag set (directory name=%s).\n", __func__, buf);
+    TEST_ASSERT_MESSAGE((file_stat.st_mode & S_IFREG) != S_IFREG, fsfat_fopen_utest_msg_g);
+
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: expected st_mode S_IFDIR flag not set (directory name=%s).\n", __func__, buf);
+    TEST_ASSERT_MESSAGE((file_stat.st_mode & S_IFDIR) == S_IFDIR, fsfat_fopen_utest_msg_g);
+
+    /* clean up after successful test */
+    fsfat_filepath_remove_all((char*) node->filename);
+
+#endif /* ! defined(__ARMCC_VERSION) && defined(__GNUC__) */
+    return CaseNext;
+}
+
+/** @brief  test for operation of SDFileSystem::format()
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_15(const size_t call_count)
+{
+
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    (void) call_count;
+    int32_t ret = -1;
+
+    /* the allocation_unit of 0 means chanFS will use the default for the card (varies according to capacity). */
+    fs.unmount();
+    ret = fs.format(&sd);
+    FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to format sdcard (ret=%d)\n", __func__, (int) ret);
+    TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+    fs.mount(&sd);
+    return CaseNext;
+}
+
+
+/* @brief   test utility function to create a file of a given size.
+ *
+ * A reference data table is used of so that the data file can be later be
+ * checked with fsfat_test_check_data_file().
+ *
+ * @param   filename    name of the file including path
+ * @param   data        data to store in file
+ * @param   len         number of bytes of data present in the data buffer.
+ */
+int32_t fsfat_test_create_data_file(const char* filename, size_t len)
+{
+    int32_t ret = -1;
+    FILE *fp = NULL;
+    size_t write_len = 0;
+    size_t written_len = 0;
+    int32_t exp = 0;
+    const int32_t exp_max = 8;      /* so as not to exceed FSFAT_TEST_BYTE_DATA_TABLE_SIZE/2 */
+
+    FSFAT_FENTRYLOG("%s:entered (filename=%s, len=%d).\n", __func__, filename, (int) len);
+    TEST_ASSERT(len % FSFAT_TEST_BYTE_DATA_TABLE_SIZE == 0);
+    fp = fopen(filename, "a");
+    if(fp == NULL){
+        return ret;
+    }
+
+    while(written_len < len) {
+        /* write fsfat_test_byte_data_table or part thereof, in 9 writes of sizes
+         * 1, 2, 4, 8, 16, 32, 64, 128, 1, totalling 256 bytes len permitting. */
+        for(exp = 0; (exp <= exp_max) && (written_len < len); exp++){
+            write_len = 0x1 << (exp % exp_max);
+            write_len = len - written_len  > write_len ? write_len : len - written_len;
+            ret = fwrite((const void*) &fsfat_test_byte_data_table[written_len % FSFAT_TEST_BYTE_DATA_TABLE_SIZE], write_len, 1, fp);
+            written_len += write_len;
+            if(ret != 1){
+                FSFAT_DBGLOG("%s:Error: fwrite() failed (ret=%d)\n", __func__, (int) ret);
+                ret = -1;
+                goto out0;
+            }
+        }
+    }
+    if(written_len == len) {
+        ret = 0;
+    } else {
+        ret = -1;
+    }
+out0:
+    fclose(fp);
+    return ret;
+}
+
+
+/* @brief   test utility function to check the data in the specified file is correct.
+ *
+ * The data read from the file is check that it agrees with the data written by
+ * fsfat_test_create_data_file().
+ *
+ * @param   filename    name of the file including path
+ * @param   data        data to store in file
+ * @param   len         number of bytes of data present in the data buffer.
+ */
+int32_t fsfat_test_check_data_file(const char* filename, size_t len)
+{
+    int32_t ret = -1;
+    FILE *fp = NULL;
+    size_t read_len = 0;
+    uint8_t buf[FSFAT_TEST_BYTE_DATA_TABLE_SIZE];
+
+    FSFAT_FENTRYLOG("%s:entered (filename=%s, len=%d).\n", __func__, filename, (int) len);
+    TEST_ASSERT(len % FSFAT_TEST_BYTE_DATA_TABLE_SIZE == 0);
+    fp = fopen(filename, "r");
+    if(fp == NULL){
+        return ret;
+    }
+
+    while(read_len < len) {
+        ret = fread((void*) buf, FSFAT_TEST_BYTE_DATA_TABLE_SIZE, 1, fp);
+        read_len += FSFAT_TEST_BYTE_DATA_TABLE_SIZE;
+        if(ret == 0){
+            /* end of read*/
+            FSFAT_DBGLOG("%s:unable to read data\n", __func__);
+            break;
+        }
+        if(memcmp(buf, fsfat_test_byte_data_table, FSFAT_TEST_BYTE_DATA_TABLE_SIZE) != 0) {
+            FSFAT_DBGLOG("%s:Error: read data not as expected (0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x, 0x%2x\n", __func__,
+                    buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
+            ret = -1;
+            goto out0;
+        }
+    }
+    if(read_len == len) {
+        ret = 0;
+    }
+out0:
+    fclose(fp);
+    return ret;
+}
+
+/* file data for test_16 */
+static fsfat_kv_data_t fsfat_fopen_test_16_kv_data[] = {
+        { "/sd/tst16_0/testfil0.txt", "dummy_data"},
+        { "/sd/tst16_1/subdir0/testfil0.txt", "dummy_data"},
+        { "/sd/tst16_2/subdir0/subdir1/testfil0.txt", "dummy_data"},
+        { "/sd/tst16_3/subdir0/subdir1/subdir2/subdir3/testfil0.txt", "dummy_data"},
+        { "/sd/tst16_4/subdir0/subdir1/subdir2/subdir3/subdir4/testfil0.txt", "dummy_data"},
+        { "/sd/tst16_5/subdir0/subdir1/subdir2/subdir3/subdir4/subdir5/testfil0.txt", "dummy_data"},
+        { "/sd/tst16_6/subdir0/subdir1/subdir2/subdir3/subdir4/subdir5/subdir6/testfil0.txt", "dummy_data"},
+        { "/sd/tst16_7/subdir0/subdir1/subdir2/subdir3/subdir4/subdir5/subdir6/subdir7/testfil0.txt", "dummy_data"},
+        { "/sd/tst16_8/subdir0/subdir1/subdir2/subdir3/subdir4/subdir5/subdir6/subdir7/subdir8/testfil0.txt", "dummy_data"},
+        { "/sd/tst16_9/subdir0/subdir1/subdir2/subdir3/subdir4/subdir5/subdir6/subdir7/subdir8/subdir9/testfil0.txt", "dummy_data"},
+        { NULL, NULL},
+};
+
+
+/** @brief  stress test to write data to fs
+ *
+ * @return on success returns CaseNext to continue to next test case, otherwise will assert on errors.
+ */
+control_t fsfat_fopen_test_16(const size_t call_count)
+{
+    int32_t ret = 0;
+    fsfat_kv_data_t *node = fsfat_fopen_test_16_kv_data;
+    const int32_t num_blocks = 100; /* each file ~25kB */
+
+    FSFAT_DBGLOG("%s:entered\n", __func__);
+    (void) call_count;
+
+    /* remove file and directory from a previous failed test run, if present */
+    while(node->filename != NULL) {
+        fsfat_filepath_remove_all((char*) node->filename);
+        node++;
+    }
+
+    /* create dirs */
+    node = fsfat_fopen_test_16_kv_data;
+    while(node->filename != NULL) {
+        ret = fsfat_filepath_make_dirs((char*) node->filename, true);
+        FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create dirs for filename (filename=\"%s\")(ret=%d)\n", __func__, node->filename, (int) ret);
+        TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+        node++;
+    }
+
+    /* create the data files */
+    node = fsfat_fopen_test_16_kv_data;
+    while(node->filename != NULL) {
+        ret = fsfat_test_create_data_file(node->filename, num_blocks * FSFAT_TEST_BYTE_DATA_TABLE_SIZE);
+        FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to create data file (filename=\"%s\")(ret=%d)\n", __func__, node->filename, (int) ret);
+        TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+        node++;
+    }
+
+    /* read the data back and check its as expected */
+    node = fsfat_fopen_test_16_kv_data;
+    while(node->filename != NULL) {
+        ret = fsfat_test_check_data_file(node->filename, num_blocks * FSFAT_TEST_BYTE_DATA_TABLE_SIZE);
+        FSFAT_TEST_UTEST_MESSAGE(fsfat_fopen_utest_msg_g, FSFAT_UTEST_MSG_BUF_SIZE, "%s:Error: failed to check data file (filename=\"%s\")(ret=%d)\n", __func__, node->filename, (int) ret);
+        TEST_ASSERT_MESSAGE(ret == 0, fsfat_fopen_utest_msg_g);
+        node++;
+    }
+
+    /* clean up */
+    node = fsfat_fopen_test_16_kv_data;
+    while(node->filename != NULL) {
+        fsfat_filepath_remove_all((char*) node->filename);
+        node++;
+    }
+    return CaseNext;
+}
+
+
+#else
+
+
+#define FSFAT_FOPEN_TEST_01      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_02      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_03      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_04      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_05      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_06      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_07      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_08      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_09      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_10      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_11      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_12      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_13      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_14      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_15      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_16      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_17      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_18      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_19      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_20      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_21      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_22      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_23      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_24      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_25      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_26      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_27      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_28      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_29      fsfat_fopen_test_dummy
+#define FSFAT_FOPEN_TEST_30      fsfat_fopen_test_dummy
+
+/** @brief  fsfat_fopen_test_dummy    Dummy test case for testing when platform doesnt have an SDCard installed.
+ *
+ * @return success always
+ */
+static control_t fsfat_fopen_test_dummy()
+{
+    printf("Null test\n");
+    return CaseNext;
+}
+
+#endif
+
+
+/// @cond FSFAT_DOXYGEN_DISABLE
+utest::v1::status_t greentea_setup(const size_t number_of_cases)
+{
+    GREENTEA_SETUP(FSFAT_FOPEN_GREENTEA_TIMEOUT_S, "default_auto");
+    return greentea_test_setup_handler(number_of_cases);
+}
+
+Case cases[] = {
+           /*          1         2         3         4         5         6        7  */
+           /* 1234567890123456789012345678901234567890123456789012345678901234567890 */
+        Case("FSFAT_FOPEN_TEST_01: fopen()/fwrite()/fclose() directories/file in multi-dir filepath.", FSFAT_FOPEN_TEST_01),
+        Case("FSFAT_FOPEN_TEST_02: fopen(r) pre-existing file try to write it.", FSFAT_FOPEN_TEST_02),
+        Case("FSFAT_FOPEN_TEST_03: fopen(w+) pre-existing file try to write it.", FSFAT_FOPEN_TEST_03),
+        Case("FSFAT_FOPEN_TEST_04: fopen() with a filename exceeding the maximum length.", FSFAT_FOPEN_TEST_04),
+#ifdef FOPEN_EXTENDED_TESTING
+        Case("FSFAT_FOPEN_TEST_05: fopen() with bad filenames (extended).", FSFAT_FOPEN_TEST_05),
+#endif
+        Case("FSFAT_FOPEN_TEST_06: fopen() with bad filenames (minimal).", FSFAT_FOPEN_TEST_06),
+        Case("FSFAT_FOPEN_TEST_07: fopen()/errno handling.", FSFAT_FOPEN_TEST_07),
+        Case("FSFAT_FOPEN_TEST_08: ferror()/clearerr()/errno handling.", FSFAT_FOPEN_TEST_08),
+        Case("FSFAT_FOPEN_TEST_09: ftell() handling.", FSFAT_FOPEN_TEST_09),
+        Case("FSFAT_FOPEN_TEST_10: remove() test.", FSFAT_FOPEN_TEST_10),
+        Case("FSFAT_FOPEN_TEST_11: rename().", FSFAT_FOPEN_TEST_11),
+        Case("FSFAT_FOPEN_TEST_12: opendir(), readdir(), closedir() test.", FSFAT_FOPEN_TEST_12),
+        Case("FSFAT_FOPEN_TEST_13: mkdir() test.", FSFAT_FOPEN_TEST_13),
+        Case("FSFAT_FOPEN_TEST_14: stat() test.", FSFAT_FOPEN_TEST_14),
+        Case("FSFAT_FOPEN_TEST_15: format() test.", FSFAT_FOPEN_TEST_15),
+        Case("FSFAT_FOPEN_TEST_16: write/check n x 25kB data files.", FSFAT_FOPEN_TEST_16),
+};
+
+
+/* Declare your test specification with a custom setup handler */
+Specification specification(greentea_setup, cases);
+
+int main()
+{
+    return !Harness::run(specification);
+}
+/// @endcond
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/TESTS/filesystem/parallel/main.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,184 @@
+#include "mbed.h"
+#include "greentea-client/test_env.h"
+#include "unity.h"
+#include "utest.h"
+#include <stdlib.h>
+#include <errno.h>
+
+using namespace utest::v1;
+
+// test configuration
+#ifndef MBED_TEST_FILESYSTEM
+#define MBED_TEST_FILESYSTEM FATFileSystem
+#endif
+
+#ifndef MBED_TEST_FILESYSTEM_DECL
+#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
+#endif
+
+#ifndef MBED_TEST_BLOCKDEVICE
+#define MBED_TEST_BLOCKDEVICE SDBlockDevice
+#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
+#endif
+
+#ifndef MBED_TEST_BLOCKDEVICE_DECL
+#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
+#endif
+
+#ifndef MBED_TEST_FILES
+#define MBED_TEST_FILES 4
+#endif
+
+#ifndef MBED_TEST_DIRS
+#define MBED_TEST_DIRS 4
+#endif
+
+#ifndef MBED_TEST_BUFFER
+#define MBED_TEST_BUFFER 512
+#endif
+
+#ifndef MBED_TEST_TIMEOUT
+#define MBED_TEST_TIMEOUT 120
+#endif
+
+#ifndef MBED_THREAD_COUNT
+#define MBED_THREAD_COUNT    MBED_TEST_FILES
+#endif
+
+// declarations
+#define STRINGIZE(x) STRINGIZE2(x)
+#define STRINGIZE2(x) #x
+#define INCLUDE(x) STRINGIZE(x.h)
+
+#include INCLUDE(MBED_TEST_FILESYSTEM)
+#include INCLUDE(MBED_TEST_BLOCKDEVICE)
+
+MBED_TEST_FILESYSTEM_DECL;
+MBED_TEST_BLOCKDEVICE_DECL;
+
+Dir dir[MBED_TEST_DIRS];
+File file[MBED_TEST_FILES];
+DIR *dd[MBED_TEST_DIRS];
+FILE *fd[MBED_TEST_FILES];
+struct dirent ent;
+struct dirent *ed;
+
+volatile bool count_done = 0;
+
+// tests
+
+void test_file_tests() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = MBED_TEST_FILESYSTEM::format(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void write_file_data (char count) {
+
+    char filename[10];
+    uint8_t wbuffer[MBED_TEST_BUFFER];
+    int res;
+
+    sprintf(filename, "%s%d", "data", count);
+    res = file[count].open(&fs, filename, O_WRONLY | O_CREAT);
+    TEST_ASSERT_EQUAL(0, res);
+    
+    char letter = 'A'+ count;
+    for (uint32_t i = 0; i < MBED_TEST_BUFFER; i++) {
+        wbuffer[i] = letter++;
+        if ('z' == letter) {
+            letter = 'A' + count;
+        }    
+    }
+    
+    for (uint32_t i = 0; i < 5; i++) {
+        res = file[count].write(wbuffer, MBED_TEST_BUFFER);
+        TEST_ASSERT_EQUAL(MBED_TEST_BUFFER, res);
+    }
+
+    res = file[count].close();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void read_file_data (char count) {
+    char filename[10];
+    uint8_t rbuffer[MBED_TEST_BUFFER];
+    int res;
+
+    sprintf(filename, "%s%d", "data", count);
+    res = file[count].open(&fs, filename, O_RDONLY);
+    TEST_ASSERT_EQUAL(0, res);
+
+    for (uint32_t i = 0; i < 5; i++) {
+        res = file[count].read(rbuffer, MBED_TEST_BUFFER);
+        TEST_ASSERT_EQUAL(MBED_TEST_BUFFER, res);
+        char letter = 'A' + count;
+        for (uint32_t i = 0; i < MBED_TEST_BUFFER; i++) {
+            res = rbuffer[i];
+            TEST_ASSERT_EQUAL(letter++, res);
+            if ('z' == letter) {
+                letter = 'A' + count;
+            }
+        }
+    }
+
+    res = file[count].close();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_thread_access_test() {
+    
+    Thread *data[MBED_THREAD_COUNT];    
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+    res = fs.mount(&bd);
+    TEST_ASSERT_EQUAL(0, res);
+    
+    // Write threads in parallel
+    for (char thread_count = 0; thread_count < MBED_THREAD_COUNT; thread_count++) {
+        data[thread_count] = new Thread(osPriorityNormal);
+        data[thread_count]->start(callback((void(*)(void*))write_file_data, (void*)thread_count));
+    }
+    
+    // Wait for write thread to join before creating read thread
+    for (char thread_count = 0; thread_count < MBED_THREAD_COUNT; thread_count++) {
+        data[thread_count]->join();
+        delete data[thread_count];
+        data[thread_count] = new Thread(osPriorityNormal);
+        data[thread_count]->start(callback((void(*)(void*))read_file_data, (void*)thread_count));
+    }
+    
+    // Wait for read threads to join
+    for (char thread_count = 0; thread_count < MBED_THREAD_COUNT; thread_count++) {
+        data[thread_count]->join();
+        delete data[thread_count];
+    }
+    res = fs.unmount();
+    TEST_ASSERT_EQUAL(0, res);
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+// test setup
+utest::v1::status_t test_setup(const size_t number_of_cases) {
+    GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
+    return verbose_test_setup_handler(number_of_cases);
+}
+
+Case cases[] = {
+    Case("File tests", test_file_tests),
+    Case("Filesystem access from multiple threads", test_thread_access_test),
+};
+
+Specification specification(test_setup, cases);
+
+int main() {
+    return !Harness::run(specification);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/TESTS/filesystem/seek/main.cpp	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,629 @@
+#include "mbed.h"
+#include "greentea-client/test_env.h"
+#include "unity.h"
+#include "utest.h"
+#include <stdlib.h>
+#include <errno.h>
+
+using namespace utest::v1;
+
+// test configuration
+#ifndef MBED_TEST_FILESYSTEM
+#define MBED_TEST_FILESYSTEM FATFileSystem
+#endif
+
+#ifndef MBED_TEST_FILESYSTEM_DECL
+#define MBED_TEST_FILESYSTEM_DECL MBED_TEST_FILESYSTEM fs("fs")
+#endif
+
+#ifndef MBED_TEST_BLOCKDEVICE
+#define MBED_TEST_BLOCKDEVICE SDBlockDevice
+#define MBED_TEST_BLOCKDEVICE_DECL SDBlockDevice bd(MBED_CONF_SD_SPI_MOSI, MBED_CONF_SD_SPI_MISO, MBED_CONF_SD_SPI_CLK, MBED_CONF_SD_SPI_CS);
+#endif
+
+#ifndef MBED_TEST_BLOCKDEVICE_DECL
+#define MBED_TEST_BLOCKDEVICE_DECL MBED_TEST_BLOCKDEVICE bd
+#endif
+
+#ifndef MBED_TEST_FILES
+#define MBED_TEST_FILES 4
+#endif
+
+#ifndef MBED_TEST_DIRS
+#define MBED_TEST_DIRS 4
+#endif
+
+#ifndef MBED_TEST_BUFFER
+#define MBED_TEST_BUFFER 8192
+#endif
+
+#ifndef MBED_TEST_TIMEOUT
+#define MBED_TEST_TIMEOUT 120
+#endif
+
+
+// declarations
+#define STRINGIZE(x) STRINGIZE2(x)
+#define STRINGIZE2(x) #x
+#define INCLUDE(x) STRINGIZE(x.h)
+
+#include INCLUDE(MBED_TEST_FILESYSTEM)
+#include INCLUDE(MBED_TEST_BLOCKDEVICE)
+
+MBED_TEST_FILESYSTEM_DECL;
+MBED_TEST_BLOCKDEVICE_DECL;
+
+Dir dir[MBED_TEST_DIRS];
+File file[MBED_TEST_FILES];
+DIR *dd[MBED_TEST_DIRS];
+FILE *fd[MBED_TEST_FILES];
+struct dirent ent;
+struct dirent *ed;
+size_t size;
+uint8_t buffer[MBED_TEST_BUFFER];
+uint8_t rbuffer[MBED_TEST_BUFFER];
+uint8_t wbuffer[MBED_TEST_BUFFER];
+
+
+// tests
+
+void test_seek_tests() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = MBED_TEST_FILESYSTEM::format(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.mkdir("hello", 0777);
+        TEST_ASSERT_EQUAL(0, res);
+        for (int i = 0; i < 132; i++) {
+            sprintf((char*)buffer, "hello/kitty%d", i);
+            res = file[0].open(&fs, (char*)buffer,
+                    O_WRONLY | O_CREAT | O_APPEND);
+            TEST_ASSERT_EQUAL(0, res);
+    
+            size = strlen("kittycatcat");
+            memcpy(buffer, "kittycatcat", size);
+            for (int j = 0; j < 132; j++) {
+                file[0].write(buffer, size);
+            }
+            res = file[0].close();
+            TEST_ASSERT_EQUAL(0, res);
+        }
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_simple_dir_seek() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].open(&fs, "hello");
+        TEST_ASSERT_EQUAL(0, res);
+#if (MBED_TEST_FILESYSTEM != FATFileSystem)
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, ".");
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, "..");
+        TEST_ASSERT_EQUAL(0, res);
+#endif
+
+        off_t pos;
+        int i;
+        for (i = 0; i < 4; i++) {
+            sprintf((char*)buffer, "kitty%d", i);
+            res = dir[0].read(&ent);
+            TEST_ASSERT_EQUAL(1, res);
+            res = strcmp(ent.d_name, (char*)buffer);
+            TEST_ASSERT_EQUAL(0, res);
+            pos = dir[0].tell();
+        }
+        res = pos >= 0;
+        TEST_ASSERT_EQUAL(1, res);
+    
+        dir[0].seek(pos);
+        sprintf((char*)buffer, "kitty%d", i);
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, (char*)buffer);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        dir[0].rewind();
+        sprintf((char*)buffer, "kitty%d", 0);
+#if (MBED_TEST_FILESYSTEM != FATFileSystem)
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, ".");
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, "..");
+        TEST_ASSERT_EQUAL(0, res);
+#endif
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, (char*)buffer);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        dir[0].seek(pos);
+        sprintf((char*)buffer, "kitty%d", i);
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, (char*)buffer);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_large_dir_seek() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].open(&fs, "hello");
+        TEST_ASSERT_EQUAL(0, res);
+#if (MBED_TEST_FILESYSTEM != FATFileSystem)
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, ".");
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, "..");
+        TEST_ASSERT_EQUAL(0, res);
+#endif
+
+        off_t pos;
+        int i;
+        for (i = 0; i < 128; i++) {
+            sprintf((char*)buffer, "kitty%d", i);
+            res = dir[0].read(&ent);
+            TEST_ASSERT_EQUAL(1, res);
+            res = strcmp(ent.d_name, (char*)buffer);
+            TEST_ASSERT_EQUAL(0, res);
+            pos = dir[0].tell();
+        }
+        res = pos >= 0;
+        TEST_ASSERT_EQUAL(1, res);
+    
+        dir[0].seek(pos);
+        sprintf((char*)buffer, "kitty%d", i);
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, (char*)buffer);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        dir[0].rewind();
+        sprintf((char*)buffer, "kitty%d", 0);
+#if (MBED_TEST_FILESYSTEM != FATFileSystem)
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, ".");
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, "..");
+        TEST_ASSERT_EQUAL(0, res);
+#endif
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, (char*)buffer);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        dir[0].seek(pos);
+        sprintf((char*)buffer, "kitty%d", i);
+        res = dir[0].read(&ent);
+        TEST_ASSERT_EQUAL(1, res);
+        res = strcmp(ent.d_name, (char*)buffer);
+        TEST_ASSERT_EQUAL(0, res);
+        res = dir[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_simple_file_seek() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "hello/kitty42", O_RDONLY);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        off_t pos;
+        size = strlen("kittycatcat");
+        for (int i = 0; i < 4; i++) {
+            res = file[0].read(buffer, size);
+            TEST_ASSERT_EQUAL(size, res);
+            res = memcmp(buffer, "kittycatcat", size);
+            TEST_ASSERT_EQUAL(0, res);
+            pos = file[0].tell();
+        }
+        res = pos >= 0;
+        TEST_ASSERT_EQUAL(1, res);
+        res = file[0].seek(pos, SEEK_SET);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        file[0].rewind();
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].seek(pos, SEEK_SET);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].seek(-size, SEEK_CUR);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].seek(-size, SEEK_END) >= 0;
+        TEST_ASSERT_EQUAL(1, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        size_t size = file[0].size();
+        res = file[0].seek(0, SEEK_CUR);
+        TEST_ASSERT_EQUAL(size, res);
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_large_file_seek() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "hello/kitty42", O_RDONLY);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        off_t pos;
+        size = strlen("kittycatcat");
+        for (int i = 0; i < 128; i++) {
+            res = file[0].read(buffer, size);
+            TEST_ASSERT_EQUAL(size, res);
+            res = memcmp(buffer, "kittycatcat", size);
+            TEST_ASSERT_EQUAL(0, res);
+            pos = file[0].tell();
+        }
+        res = pos >= 0;
+        TEST_ASSERT_EQUAL(1, res);
+        res = file[0].seek(pos, SEEK_SET);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        file[0].rewind();
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].seek(pos, SEEK_SET);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].seek(-size, SEEK_CUR);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].seek(-size, SEEK_END) >= 0;
+        TEST_ASSERT_EQUAL(1, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        size_t size = file[0].size();
+        res = file[0].seek(0, SEEK_CUR);
+        TEST_ASSERT_EQUAL(size, res);
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_simple_file_seek_and_write() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "hello/kitty42", O_RDWR);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        off_t pos;
+        size = strlen("kittycatcat");
+        for (int i = 0; i < 4; i++) {
+            res = file[0].read(buffer, size);
+            TEST_ASSERT_EQUAL(size, res);
+            res = memcmp(buffer, "kittycatcat", size);
+            TEST_ASSERT_EQUAL(0, res);
+            pos = file[0].tell();
+        }
+        res = pos >= 0;
+        TEST_ASSERT_EQUAL(1, res);
+    
+        memcpy(buffer, "doggodogdog", size);
+        res = file[0].seek(pos, SEEK_SET);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].write(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = file[0].seek(pos, SEEK_SET);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "doggodogdog", size);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        file[0].rewind();
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].seek(pos, SEEK_SET);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "doggodogdog", size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].seek(-size, SEEK_END) >= 0;
+        TEST_ASSERT_EQUAL(1, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        size_t size = file[0].size();
+        res = file[0].seek(0, SEEK_CUR);
+        TEST_ASSERT_EQUAL(size, res);
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_large_file_seek_and_write() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "hello/kitty42", O_RDWR);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        off_t pos;
+        size = strlen("kittycatcat");
+        for (int i = 0; i < 128; i++) {
+            res = file[0].read(buffer, size);
+            TEST_ASSERT_EQUAL(size, res);
+            if (i != 4) {
+                res = memcmp(buffer, "kittycatcat", size);
+                TEST_ASSERT_EQUAL(0, res);
+            }
+            pos = file[0].tell();
+        }
+        res = pos >= 0;
+        TEST_ASSERT_EQUAL(1, res);
+    
+        memcpy(buffer, "doggodogdog", size);
+        res = file[0].seek(pos, SEEK_SET);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].write(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = file[0].seek(pos, SEEK_SET);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "doggodogdog", size);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        file[0].rewind();
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].seek(pos, SEEK_SET);
+        TEST_ASSERT_EQUAL(pos, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "doggodogdog", size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].seek(-size, SEEK_END) >= 0;
+        TEST_ASSERT_EQUAL(1, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "kittycatcat", size);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        size_t size = file[0].size();
+        res = file[0].seek(0, SEEK_CUR);
+        TEST_ASSERT_EQUAL(size, res);
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_boundary_seek_and_write() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "hello/kitty42", O_RDWR);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        size = strlen("hedgehoghog");
+        const off_t offsets[] = {512, 1020, 513, 1021, 511, 1019};
+    
+        for (int i = 0; i < sizeof(offsets) / sizeof(offsets[0]); i++) {
+            off_t off = offsets[i];
+            memcpy(buffer, "hedgehoghog", size);
+            res = file[0].seek(off, SEEK_SET);
+            TEST_ASSERT_EQUAL(off, res);
+            res = file[0].write(buffer, size);
+            TEST_ASSERT_EQUAL(size, res);
+            res = file[0].seek(off, SEEK_SET);
+            TEST_ASSERT_EQUAL(off, res);
+            res = file[0].read(buffer, size);
+            TEST_ASSERT_EQUAL(size, res);
+            res = memcmp(buffer, "hedgehoghog", size);
+            TEST_ASSERT_EQUAL(0, res);
+            res = file[0].seek(0, SEEK_SET);
+            TEST_ASSERT_EQUAL(0, res);
+            res = file[0].read(buffer, size);
+            TEST_ASSERT_EQUAL(size, res);
+            res = memcmp(buffer, "kittycatcat", size);
+            TEST_ASSERT_EQUAL(0, res);
+            res = file[0].sync();
+            TEST_ASSERT_EQUAL(0, res);
+        }
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+void test_out_of_bounds_seek() {
+    int res = bd.init();
+    TEST_ASSERT_EQUAL(0, res);
+
+    {
+        res = fs.mount(&bd);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].open(&fs, "hello/kitty42", O_RDWR);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        size = strlen("kittycatcat");
+        res = file[0].size();
+        TEST_ASSERT_EQUAL(132*size, res);
+        res = file[0].seek((132+4)*size,
+                SEEK_SET);
+        TEST_ASSERT_EQUAL((132+4)*size, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(0, res);
+    
+        memcpy(buffer, "porcupineee", size);
+        res = file[0].write(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = file[0].seek((132+4)*size,
+                SEEK_SET);
+        TEST_ASSERT_EQUAL((132+4)*size, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+        res = memcmp(buffer, "porcupineee", size);
+        TEST_ASSERT_EQUAL(0, res);
+        res = file[0].seek(132*size,
+                SEEK_SET);
+        TEST_ASSERT_EQUAL(132*size, res);
+        res = file[0].read(buffer, size);
+        TEST_ASSERT_EQUAL(size, res);
+#if (MBED_TEST_FILESYSTEM != FATFileSystem)
+        // FatFs does not guarantee empty expanded buffer
+        res = memcmp(buffer, "\0\0\0\0\0\0\0\0\0\0\0", size);
+        TEST_ASSERT_EQUAL(0, res);
+#endif
+        res = file[0].close();
+        TEST_ASSERT_EQUAL(0, res);
+        res = fs.unmount();
+        TEST_ASSERT_EQUAL(0, res);
+    }
+
+    res = bd.deinit();
+    TEST_ASSERT_EQUAL(0, res);
+}
+
+
+
+// test setup
+utest::v1::status_t test_setup(const size_t number_of_cases) {
+    GREENTEA_SETUP(MBED_TEST_TIMEOUT, "default_auto");
+    return verbose_test_setup_handler(number_of_cases);
+}
+
+Case cases[] = {
+    Case("Seek tests", test_seek_tests),
+    Case("Simple dir seek", test_simple_dir_seek),
+    Case("Large dir seek", test_large_dir_seek),
+    Case("Simple file seek", test_simple_file_seek),
+    Case("Large file seek", test_large_file_seek),
+    Case("Simple file seek and write", test_simple_file_seek_and_write),
+    Case("Large file seek and write", test_large_file_seek_and_write),
+    Case("Boundary seek and write", test_boundary_seek_and_write),
+    Case("Out-of-bounds seek", test_out_of_bounds_seek),
+};
+
+Specification specification(test_setup, cases);
+
+int main() {
+    return !Harness::run(specification);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/config/mbed_lib.json	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,139 @@
+{
+    "name": "sd",
+    "config": {
+        "SPI_CS": "D10",
+        "SPI_MOSI": "D11",
+        "SPI_MISO": "D12",
+        "SPI_CLK": "D13",
+        "DEVICE_SPI": 1,
+        "FSFAT_SDCARD_INSTALLED": 1
+    },
+    "target_overrides": {
+        "DISCO_F051R8": {
+             "SPI_MOSI": "SPI_MOSI",
+             "SPI_MISO": "SPI_MISO",
+             "SPI_CLK":  "SPI_SCK",
+             "SPI_CS":   "SPI_CS"
+        },
+        "DISCO_L476VG": {
+          "SPI_MOSI": "PE_15",
+          "SPI_MISO": "PE_14",
+          "SPI_CLK":  "PE_13",
+          "SPI_CS":   "PE_12"
+        },
+        "K20D50M": {
+             "SPI_MOSI": "PTD2",
+             "SPI_MISO": "PTD3",
+             "SPI_CLK":  "PTD1",
+             "SPI_CS":   "PTC2"
+        },
+        "KL22F": {
+             "SPI_MOSI": "PTD6",
+             "SPI_MISO": "PTD7",
+             "SPI_CLK":  "PTD5",
+             "SPI_CS":   "PTD4"
+        },
+        "KL25Z": {
+             "SPI_MOSI": "PTD2",
+             "SPI_MISO": "PTD3",
+             "SPI_CLK":  "PTD1",
+             "SPI_CS":   "PTD0"
+        },
+        "KL43Z": {
+             "SPI_MOSI": "PTD6",
+             "SPI_MISO": "PTD7",
+             "SPI_CLK":  "PTD5",
+             "SPI_CS":   "PTD4"
+        },
+        "KL46Z": {
+             "SPI_MOSI": "PTD6",
+             "SPI_MISO": "PTD7",
+             "SPI_CLK":  "PTD5",
+             "SPI_CS":   "PTD4"
+        },
+        "K64F": {
+             "SPI_MOSI": "PTE3",
+             "SPI_MISO": "PTE1",
+             "SPI_CLK":  "PTE2",
+             "SPI_CS":   "PTE4"
+        },
+        "K66F": {
+             "SPI_MOSI": "PTE3",
+             "SPI_MISO": "PTE1",
+             "SPI_CLK":  "PTE2",
+             "SPI_CS":   "PTE4"
+        },
+        "LPC11U37H_401": {
+             "SPI_MOSI": "SDMOSI",
+             "SPI_MISO": "SDMISO",
+             "SPI_CLK":  "SDSCLK",
+             "SPI_CS":   "SDSSEL"
+        },
+        "LPC2368": {
+             "SPI_MOSI": "p11",
+             "SPI_MISO": "p12",
+             "SPI_CLK":  "p13",
+             "SPI_CS":   "p14"
+        },
+         "NUCLEO_F429ZI": {
+             "SPI_MOSI": "PC_12",
+             "SPI_MISO": "PC_11",
+             "SPI_CLK":  "PC_10",
+             "SPI_CS":   "PA_15"
+         },
+         "DISCO_F429ZI": {
+            "SPI_MOSI": "PC_12",
+            "SPI_MISO": "PC_11",
+            "SPI_CLK":  "PC_10",
+            "SPI_CS":   "PA_15"
+        },
+        "NUCLEO_L031K6": {
+             "SPI_MOSI": "SPI_MOSI",
+             "SPI_MISO": "SPI_MISO",
+             "SPI_CLK":  "SPI_SCK",
+             "SPI_CS":   "SPI_CS"
+        },
+        "NUMAKER_PFM_M453": {
+             "SPI_MOSI": "PD_13",
+             "SPI_MISO": "PD_14",
+             "SPI_CLK":  "PD_15",
+             "SPI_CS":   "PD_12"
+        },
+        "NUMAKER_PFM_NUC472": {
+             "SPI_MOSI": "PF_0",
+             "SPI_MISO": "PD_15",
+             "SPI_CLK":  "PD_14",
+             "SPI_CS":   "PD_13"
+        },
+        "nRF51822": {
+             "SPI_MOSI": "p12",
+             "SPI_MISO": "p13",
+             "SPI_CLK":  "p15",
+             "SPI_CS":   "p14"
+        },
+        "UBLOX_EVK_ODIN_W2": {
+            "SPI_CS": "D9",
+            "SPI_MOSI": "D11",
+            "SPI_MISO": "D12",
+            "SPI_CLK": "D13"
+        },
+        "RZ_A1H": {
+             "SPI_MOSI": "P8_5",
+             "SPI_MISO": "P8_6",
+             "SPI_CLK":  "P8_3",
+             "SPI_CS":   "P8_4"
+        },
+        "HEXIWEAR": {
+             "SPI_MOSI": "PTE3",
+             "SPI_MISO": "PTE1",
+             "SPI_CLK":  "PTE2",
+             "SPI_CS":   "PTE4"
+        },
+        "TB_SENSE_12": {
+             "SPI_MOSI": "PC6",
+             "SPI_MISO": "PC7",
+             "SPI_CLK": "PC8",
+             "SPI_CS": "PC9"
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/util/fsfat_debug.h	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,88 @@
+/** @file fsfat_debug.h
+ *
+ * component debug header file.
+ */
+
+
+#ifndef __FSFAT_DEBUG
+#define __FSFAT_DEBUG
+
+#include <stdint.h>
+#include <assert.h>
+#include <stdio.h>
+
+
+/* Debug Support */
+
+#define FSFAT_LOG_NONE        0
+#define FSFAT_LOG_ERR         1
+#define FSFAT_LOG_WARN        2
+#define FSFAT_LOG_NOTICE      3
+#define FSFAT_LOG_INFO        4
+#define FSFAT_LOG_DEBUG       5
+#define FSFAT_LOG_FENTRY      6
+
+#define FSFAT_LOG(_fmt, ...)                          \
+  do                                                    \
+  {                                                     \
+        printf(_fmt, __VA_ARGS__);                      \
+  }while(0);
+
+#define noFSFAT_DEBUG
+#ifdef FSFAT_DEBUG
+
+extern uint32_t fsfat_optDebug_g;
+extern uint32_t fsfat_optLogLevel_g;
+
+
+/* uncomment for asserts to work */
+/* #undef NDEBUG */
+// todo: port to mbedOSV3++ #include <core-util/assert.h>
+
+#define FSFAT_INLINE
+// todo: port to mbedOSV3++ #define FSFAT_ASSERT  CORE_UTIL_ASSERT
+#define FSFAT_ASSERT(...)
+
+#define FSFAT_DBGLOG(_fmt, ...)                       \
+  do                                                    \
+  {                                                     \
+    if(fsfat_optDebug_g && (fsfat_optLogLevel_g >= FSFAT_LOG_DEBUG))  \
+    {                                                   \
+        printf(_fmt, __VA_ARGS__);                      \
+    }                                                   \
+  }while(0);
+
+
+#define FSFAT_ERRLOG(_fmt, ...)                       \
+  do                                                    \
+  {                                                     \
+    if(fsfat_optDebug_g && (fsfat_optLogLevel_g >= FSFAT_LOG_ERR))  \
+    {                                                   \
+        printf(_fmt, __VA_ARGS__);                      \
+    }                                                   \
+  }while(0);
+
+
+#define FSFAT_FENTRYLOG(_fmt, ...)                       \
+  do                                                    \
+  {                                                     \
+    if(fsfat_optDebug_g && (fsfat_optLogLevel_g >= FSFAT_LOG_FENTRY))  \
+    {                                                   \
+        printf(_fmt, __VA_ARGS__);                      \
+    }                                                   \
+  }while(0);
+
+
+
+
+
+#else
+#define FSFAT_ASSERT(_x)                   do { } while(0)
+#define FSFAT_INLINE                       inline
+#define FSFAT_DBGLOG(_fmt, ...)            do { } while(0)
+#define FSFAT_ERRLOG(_fmt, ...)            do { } while(0)
+#define FSFAT_FENTRYLOG(_fmt, ...)         do { } while(0)
+#endif /* FSFAT_DEBUG */
+
+
+#endif /*__FSFAT_DEBUG*/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/util/fsfat_test.c	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,117 @@
+/* @file fsfat_test.c
+ *
+ * mbed Microcontroller Library
+ * Copyright (c) 2006-2016 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * test support code implementation file.
+ */
+
+#include "fsfat_debug.h"
+#include "fsfat_test.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <inttypes.h>
+#include <ctype.h>
+
+
+#ifdef FSFAT_DEBUG
+uint32_t fsfat_optDebug_g = 1;
+uint32_t fsfat_optLogLevel_g = FSFAT_LOG_NONE; /*FSFAT_LOG_NONE|FSFAT_LOG_ERR|FSFAT_LOG_DEBUG|FSFAT_LOG_FENTRY; */
+#endif
+
+/* ruler for measuring text strings */
+/*                                                                                                    1         1         1         1         1         1         1         1         1         1         2         2         2 */
+/* 0        1         2         3         4         5         6         7         8         9         0         1         2         3         4         5         6         7         8         9         0         1         2 */
+/* 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */
+
+const uint8_t fsfat_test_byte_data_table[FSFAT_TEST_BYTE_DATA_TABLE_SIZE] = {
+    0x2d, 0xf3, 0x31, 0x4c, 0x11, 0x4f, 0xde, 0x0d, 0xbd, 0xbc, 0xa6, 0x78, 0x36, 0x5c, 0x1d, 0x28,
+    0x5f, 0xa9, 0x10, 0x65, 0x54, 0x45, 0x21, 0x1a, 0x88, 0xfe, 0x76, 0x45, 0xb9, 0xac, 0x65, 0x9a,
+    0x34, 0x9d, 0x73, 0x10, 0xb4, 0xa9, 0x2e, 0x90, 0x95, 0x68, 0xac, 0xfe, 0xc5, 0x2d, 0x15, 0x03,
+    0x34, 0x70, 0xf1, 0x1d, 0x48, 0xa1, 0xa0, 0xed, 0x5c, 0x2f, 0xf5, 0x2b, 0xb9, 0x84, 0xbb, 0x45,
+    0x32, 0xdd, 0xb1, 0x33, 0x95, 0x2a, 0xbc, 0x26, 0xf0, 0x89, 0xba, 0xf4, 0xbd, 0xf9, 0x5d, 0x2e,
+    0x6e, 0x11, 0xc6, 0xa7, 0x78, 0xfc, 0xc9, 0x0e, 0x6b, 0x38, 0xba, 0x14, 0x1b, 0xab, 0x4c, 0x20,
+    0x91, 0xe4, 0xb0, 0xf1, 0x2b, 0x14, 0x07, 0x6b, 0xb5, 0xcd, 0xe3, 0x49, 0x75, 0xac, 0xe8, 0x98,
+    0xf1, 0x58, 0x8f, 0xd9, 0xc4, 0x8f, 0x00, 0x17, 0xb5, 0x06, 0x6a, 0x33, 0xbd, 0xa7, 0x40, 0x5a,
+    0xbf, 0x49, 0xf7, 0x27, 0x1b, 0x4c, 0x3e, 0x6f, 0xe3, 0x08, 0x1f, 0xfd, 0xa6, 0xd4, 0xc7, 0x5f,
+    0xa4, 0xa6, 0x82, 0xad, 0x19, 0xd5, 0x5c, 0xd8, 0x3a, 0x49, 0x85, 0xc9, 0x21, 0x83, 0xf6, 0xc6,
+    0x84, 0xf9, 0x76, 0x89, 0xf3, 0x2d, 0x17, 0x50, 0x97, 0x38, 0x48, 0x9a, 0xe1, 0x82, 0xcd, 0xac,
+    0xa8, 0x1d, 0xd7, 0x96, 0x5e, 0xb3, 0x08, 0xa8, 0x3a, 0xc7, 0x2b, 0x05, 0xaf, 0xdc, 0x16, 0xdf,
+    0x48, 0x0f, 0x2a, 0x7e, 0x3a, 0x82, 0xd7, 0x80, 0xd6, 0x49, 0x27, 0x5d, 0xe3, 0x07, 0x62, 0xb3,
+    0xc3, 0x6c, 0xba, 0xb2, 0xaa, 0x9f, 0xd9, 0x03, 0x0d, 0x27, 0xa8, 0xe0, 0xd6, 0xee, 0x79, 0x4b,
+    0xd6, 0x97, 0x99, 0xb7, 0x11, 0xd6, 0x0d, 0x34, 0xae, 0x99, 0x4a, 0x93, 0x95, 0xd0, 0x5a, 0x34,
+    0x19, 0xa2, 0x69, 0x57, 0xcf, 0x7c, 0x3d, 0x98, 0x88, 0x5d, 0x04, 0xf2, 0xd7, 0xac, 0xa5, 0x63
+};
+
+
+/* @brief  test utility function to delete the file identified by filename
+ */
+int32_t fsfat_test_delete(const char* filename)
+{
+    FSFAT_FENTRYLOG("%s:entered.\r\n", __func__);
+    return remove(filename);
+}
+
+
+/* @brief   test utility function to create a file
+ *
+ * @param   filename    name of the file including path
+ * @param   data        data to store in file
+ * @param   len         number of bytes of data present in the data buffer.
+ */
+int32_t fsfat_test_create(const char* filename, const char* data, size_t len)
+{
+    int32_t ret = -1;
+    FILE *fp = NULL;
+
+    FSFAT_FENTRYLOG("%s:entered (filename=%s, len=%d).\n", __func__, filename, (int) len);
+    fp = fopen(filename, "w+");
+    if(fp == NULL){
+        return ret;
+    }
+    ret = fwrite((const void*) data, len, 1, fp);
+    if(ret < 0){
+        fclose(fp);
+        return ret;
+    }
+    fclose(fp);
+    return ret;
+}
+
+
+/* @brief   support function for generating a kv_name
+ * @param   name    buffer to hold kv name
+ * @param   len     length of kv name to generate
+ *
+ */
+int32_t fsfat_test_filename_gen(char* name, const size_t len)
+{
+    size_t i;
+    uint32_t pos = 0;
+
+    const char* buf = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!$-_@";
+    const int buf_len = strlen(buf);
+    FSFAT_FENTRYLOG("%s:entered\n", __func__);
+    for(i = 0; i < len; i++)
+    {
+        pos = rand() % (buf_len);
+        name[i] = buf[pos];
+    }
+    return 0;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sd-driver/util/fsfat_test.h	Wed Jan 10 09:49:43 2018 +0000
@@ -0,0 +1,74 @@
+/** @file fsfat_test.h
+ *
+ * mbed Microcontroller Library
+ * Copyright (c) 2006-2016 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Header file for test support data structures and function API.
+ */
+#ifndef __FSFAT_TEST_H
+#define __FSFAT_TEST_H
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Defines */
+//#define FSFAT_INIT_1_TABLE_HEAD                 { "a", ""}
+#define FSFAT_INIT_1_TABLE_MID_NODE             { "/sd/01234567.txt", "abcdefghijklmnopqrstuvwxyz"}
+//#define FSFAT_INIT_1_TABLE_TAIL                 { "/sd/fopentst/hello/world/animal/wobbly/dog/foot/backrght.txt", "present"}
+#define FSFAT_TEST_RW_TABLE_SENTINEL            0xffffffff
+#define FSFAT_TEST_BYTE_DATA_TABLE_SIZE         256
+#define FSFAT_UTEST_MSG_BUF_SIZE                256
+#define FSFAT_UTEST_DEFAULT_TIMEOUT_MS          10000
+#define FSFAT_MBED_HOSTTEST_TIMEOUT             60
+#define FSFAT_MAX_FILE_BASENAME                 8
+#define FSFAT_MAX_FILE_EXTNAME                  3
+#define FSFAT_BUF_MAX_LENGTH                    64
+#define FSFAT_FILENAME_MAX_LENGTH               255
+
+
+/* support macro for make string for utest _MESSAGE macros, which dont support formatted output */
+#define FSFAT_TEST_UTEST_MESSAGE(_buf, _max_len, _fmt, ...)   \
+  do                                                            \
+  {                                                             \
+      snprintf((_buf), (_max_len), (_fmt), __VA_ARGS__);        \
+  }while(0);
+
+
+/*
+ * Structures
+ */
+
+/* kv data for test */
+typedef struct fsfat_kv_data_t {
+    const char* filename;
+    const char* value;
+} fsfat_kv_data_t;
+
+
+extern const uint8_t fsfat_test_byte_data_table[FSFAT_TEST_BYTE_DATA_TABLE_SIZE];
+
+int32_t fsfat_test_create(const char* filename, const char* data, size_t len);
+int32_t fsfat_test_delete(const char* key_name);
+int32_t fsfat_test_filename_gen(char* name, const size_t len);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __FSFAT_TEST_H */