Axeda Ready Demo for Freescale FRDM-KL46Z as accident alert system

Dependencies:   FRDM_MMA8451Q KL46Z-USBHost MAG3110 SocketModem TSI mbed FATFileSystem

Fork of AxedaGo-Freescal_FRDM-KL46Z by Axeda Corp

Files at this revision

API Documentation at this revision

Comitter:
AxedaCorp
Date:
Tue Jul 01 21:31:54 2014 +0000
Child:
1:5ad12c581db4
Commit message:
Made initial

Changed in this revision

AMMPC/axConstants.h Show annotated file Show diff for this revision Revisions of this file
AMMPC/axDomain.c Show annotated file Show diff for this revision Revisions of this file
AMMPC/axDomain.h Show annotated file Show diff for this revision Revisions of this file
AMMPC/axHTTP.c Show annotated file Show diff for this revision Revisions of this file
AMMPC/axHTTP.h Show annotated file Show diff for this revision Revisions of this file
AMMPC/axPlatform.c Show annotated file Show diff for this revision Revisions of this file
AMMPC/axPlatform.h Show annotated file Show diff for this revision Revisions of this file
AMMPC/axSerializer.c Show annotated file Show diff for this revision Revisions of this file
AMMPC/axSerializer.h Show annotated file Show diff for this revision Revisions of this file
AMMPC/axSettings.h Show annotated file Show diff for this revision Revisions of this file
AMMPC/axStatusCodes.h Show annotated file Show diff for this revision Revisions of this file
AMMPC/axToolkit.c Show annotated file Show diff for this revision Revisions of this file
AMMPC/axToolkit.h Show annotated file Show diff for this revision Revisions of this file
AMMPC/axTransport.cpp Show annotated file Show diff for this revision Revisions of this file
AMMPC/axTransport.h Show annotated file Show diff for this revision Revisions of this file
AMMPC/axTypes.h Show annotated file Show diff for this revision Revisions of this file
FRDM_MMA8451Q.lib Show annotated file Show diff for this revision Revisions of this file
FRDM_MMA8451Q/MMA8451Q.cpp Show annotated file Show diff for this revision Revisions of this file
FRDM_MMA8451Q/MMA8451Q.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost.lib Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHost/IUSBEnumerator.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHost/USBDeviceConnected.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHost/USBDeviceConnected.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHost/USBEndpoint.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHost/USBHALHost.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHost/USBHALHost.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHost/USBHost.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHost/USBHost.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHost/USBHostConf.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHost/USBHostHub.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHost/USBHostTypes.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostGPS/USBHostGPS.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostGPS/USBHostGPS.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostHID/USBHostMouse.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostHID/USBHostMouse.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem.lib Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/ccsbcs.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/diskio.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/diskio.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/ff.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/ff.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/ffconf.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/integer.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/FATDirHandle.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/FATDirHandle.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/FATFileHandle.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/FATFileHandle.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/FATFileSystem.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/FATFileSystem/FATFileSystem.h Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/USBHostMSD.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z-USBHost/USBHostMSD/USBHostMSD.h Show annotated file Show diff for this revision Revisions of this file
KL46Z_USBHostC270/BaseUvc.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z_USBHostC270/BaseUvc.h Show annotated file Show diff for this revision Revisions of this file
KL46Z_USBHostC270/CamInfo.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z_USBHostC270/USBHostCam.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z_USBHostC270/USBHostCam.h Show annotated file Show diff for this revision Revisions of this file
KL46Z_USBHostC270/decodeMJPEG.cpp Show annotated file Show diff for this revision Revisions of this file
KL46Z_USBHostC270/decodeMJPEG.h Show annotated file Show diff for this revision Revisions of this file
MAG3110.lib Show annotated file Show diff for this revision Revisions of this file
MAG3110/MAG3110.cpp Show annotated file Show diff for this revision Revisions of this file
MAG3110/MAG3110.h Show annotated file Show diff for this revision Revisions of this file
SocketModem.lib Show annotated file Show diff for this revision Revisions of this file
SocketModem/Socket/Endpoint.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/Socket/Endpoint.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/Socket/Socket.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/Socket/Socket.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/Socket/TCPSocketConnection.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/Socket/TCPSocketConnection.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/Socket/Transport.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/Socket/Transport.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/cellular/Cellular.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/cellular/Cellular.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/include_me.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/io/IPStack.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/io/MTSBufferedIO.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/io/MTSBufferedIO.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/io/MTSSerial.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/io/MTSSerial.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/io/MTSSerialFlowControl.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/io/MTSSerialFlowControl.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/tests/test_MTS_Circular_Buffer.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/tests/test_SMS.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/tests/test_TCP_Socket.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/tests/test_TCP_Socket_Echo.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/tests/test_main.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/tests/test_ping.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/utils/MTSCircularBuffer.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/utils/MTSCircularBuffer.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/utils/MTSText.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/utils/MTSText.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/utils/Vars.h Show annotated file Show diff for this revision Revisions of this file
SocketModem/wifi/Wifi.cpp Show annotated file Show diff for this revision Revisions of this file
SocketModem/wifi/Wifi.h Show annotated file Show diff for this revision Revisions of this file
TSI.lib Show annotated file Show diff for this revision Revisions of this file
TSI/TSISensor.cpp Show annotated file Show diff for this revision Revisions of this file
TSI/TSISensor.h Show annotated file Show diff for this revision Revisions of this file
cJSON/README.txt Show annotated file Show diff for this revision Revisions of this file
cJSON/cJSON.c Show annotated file Show diff for this revision Revisions of this file
cJSON/cJSON.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axConstants.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,52 @@
+
+
+#ifndef _AXCONSTANTS_H_
+#define _AXCONSTANTS_H_
+
+#define PROTOCOL_VERSION "1"
+
+#define AX_ANALOG 1       //Used for Data Items
+#define AX_STRING 2       //Used for Data Items
+#define AX_DIGITAL 3      //Used for Data Items
+
+#define AX_TRUE 1
+#define AX_FALSE 0
+
+#define AX_SCALAR 0
+#define AX_ALARM 10
+#define AX_EVENT 2
+#define AX_FILE 3
+#define AX_LOCATION 4
+
+//used for getContentType() call, should return 'application/json'
+#define MIME_JSON       0
+#define TRANS_TYPE_JSON      0        
+
+
+#define AX_NO_PRIORITY           -1
+#define AX_LOWEST_PRIORITY       1
+#define AX_HIGHEST_PRIORITY    100
+#define AX_REGULAR_PRIORITY     50
+
+#define KEY_CDHEADER      0
+#define KEY_NAME          1
+#define KEY_HINT          2
+#define KEY_FNAME         3
+#define KEY_CTYPE         4
+#define KEY_OCTETSTR      5
+
+//The following defines are for package instruction types
+#define AX_PKG_DOWNLOAD 1
+
+//The following defines are for setting package statuses on the platform
+#define AX_PKG_QUEUED   0
+#define AX_PKG_STARTED  1
+#define AX_PKG_SUCCESS  2
+#define AX_PKG_FAILURE  3
+
+//The following defins are used for printing debug or error messages
+#define AX_DEBUG_MSG	1
+#define AX_ERROR_MSG	2
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axDomain.c	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,612 @@
+/************************************************************************************/
+/* axDomain.c                                                                       */
+/* �2013 Axeda Corporation                                                          */
+/*                                                                                  */
+/* Defines methods for creation and interaction with Axeda domain constructs. This  */
+/* is a device independent implementation which can be applied to any device that   */
+/* supports ANSI C.                                                                 */
+/************************************************************************************/
+#include "axDomain.h"
+#include "string.h"
+#include "axSettings.h"
+#include <stdlib.h>
+#include <stdio.h>
+
+
+
+/************************************************************************************/
+/* createAlarm()                                                                    */
+/*                                                                                  */
+/* Stores the supplied values into the structure pointed to by alm.                 */
+/*                                                                                  */
+/* alm(required) : a pointer to an empty alarm structure                            */
+/* name(required) : the name of the alarm                                           */
+/* description(required) : a more detailed account of the alarm                     */
+/* severity(required) : a numerical value from 0-1000, with 1000 being the highest  */
+/* dataItem(optional) : a pointer to a dataItem structure containing the data item  */
+/*                      that triggered this alarm. Use NULL for a generic alarm.    */
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARG_NULL, AX_GEN_STR_TRUNC, AX_OK     */
+/************************************************************************************/
+int ax_data_createAlarm(ax_alarm *alm, char *alName, char *alDescription, int alSeverity, char *cause, char *reason, int timeOccured, int priority)
+  {
+  int retVal=AX_OK;
+  if((alSeverity <0)||(alSeverity>1000))
+    {
+    return AX_OUT_OF_RANGE;
+    }
+  if((!alm)||(!alName)||(!alDescription))
+    {
+    return AX_ARGNULL;
+    }
+  if(strlen(alName)<=0) {
+    return AX_ARGNULL;
+    }
+
+  snprintf(alm->alarmName, AX_ALM_NAME_S, alName);
+  if(strlen(alName)>AX_ALM_NAME_S){
+      retVal=AX_GEN_STR_TRUNC;
+      }
+
+  snprintf(alm->alarmDescription, AX_ALM_DESC_S, alDescription);
+  if(strlen(alDescription)> AX_ALM_DESC_S) {
+      retVal=AX_GEN_STR_TRUNC;
+      }
+
+  snprintf(alm->alarmCause, AX_ALM_CAUSE_S, cause);
+  if(strlen(cause)>AX_ALM_CAUSE_S) {
+     retVal=AX_GEN_STR_TRUNC;
+     }
+
+  snprintf(alm->alarmReason, AX_ALM_REAS_S, reason);
+  if(strlen(reason)>AX_ALM_REAS_S){
+     retVal=AX_GEN_STR_TRUNC;
+     }
+
+  alm->alarmSeverity=alSeverity;
+  alm->dateAcquired=timeOccured;
+  alm->priority=priority;
+  
+  return retVal;
+  }
+
+/*************************************************************************************/
+/* createDataItem()                                                                  */
+/*                                                                                   */
+/* This function will store information about a dataItem into a structure supplied   */
+/* by the calling method. There are traditionally 3 types of data Items, analog,     */
+/* string and digital.                                                               */
+/*                                                                                   */
+/* destDI(required) : A pointer to the structure where the data will be stored       */
+/* diName(required) : A character array containing the name of the data item         */
+/* diType(required) : A integer value indicating the data Item type. Can be          */
+/*                    AX_STRING, AX_DIGITAL, AX_ANALOG                               */
+/* stringValue(optional): If the type is AX_STRING, this value MUST be populated or  */
+/*                        an error will be returned. This is the value of the        */
+/*                        dataItem. If it is a String use zero for the numericValue  */
+/* numericValue(optional): If the type is AX_ANALOG or AX_DIGITAL this value MUST be */
+/*                         provided or an error will be returned. For AX_ANALOG di's */
+/*                         this value can be any numeric value within language       */
+/*                         constraints. For AX_DIGITAL, this value must be 1 or 0.   */
+/*                         1 will be considered true and 0 will be considered false. */
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARGNULL, AX_GEN_STR_TRUNC, AX_OK       */
+/*                        AX_DI_UNKNOWN_TYPE, AX_UNKNOWN                             */
+/************************************************************************************/
+int ax_data_createDataItem(ax_dataSet *destDI, char *diName, int diType, char *stringValue, double numericValue, int timeAcquired, int priority) {
+  int retVal=AX_UNKNOWN;
+  if(!destDI) { return AX_ARGNULL; }
+  if(!diName) {return AX_ARGNULL; }
+  if((diType>3)&&(diType<1)) {return AX_ARGNULL; }
+  if(timeAcquired<0) {return AX_OUT_OF_RANGE; }
+  if((priority<-1)||(priority>100)) { return AX_OUT_OF_RANGE; }
+  if(destDI->created!=AX_TRUE) {
+      retVal=ax_data_createSet(destDI, timeAcquired, priority);
+      if(retVal!=AX_OK) { return retVal; }
+      }
+  
+  retVal=ax_data_addToSet(destDI, diName, diType, stringValue, numericValue);
+  
+  return retVal;
+  }
+/*************************************************************************************/
+/* createAnalogDataItem()                                                            */
+/*                                                                                   */
+/* This function will store information about a dataItem into a structure supplied   */
+/* by the calling method. This is a special case of the ax_data_createDataItem() call*/
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARGNULL, AX_GEN_STR_TRUNC, AX_OK       */
+/*                        AX_DI_UNKNOWN_TYPE, AX_UNKNOWN                             */
+/*************************************************************************************/
+int ax_data_createAnalogDataItem(ax_dataSet *destDI, char *diName, double value, int timeAcquired, int priority) {
+  return ax_data_createDataItem(destDI, diName, AX_ANALOG, NULL, value, timeAcquired, priority);
+  }
+
+/*************************************************************************************/
+/* createDigitalDataItem()                                                           */
+/*                                                                                   */
+/* This function will store information about a dataItem into a structure supplied   */
+/* by the calling method. There are traditionally 3 types of data Items, analog,     */
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARGNULL, AX_GEN_STR_TRUNC, AX_OK       */
+/*                        AX_DI_UNKNOWN_TYPE, AX_UNKNOWN                             */
+/*************************************************************************************/
+int ax_data_createDigitalDataItem(ax_dataSet *destDI, char *diName, double value, int timeAcquired, int priority) {
+  return ax_data_createDataItem(destDI, diName, AX_DIGITAL, NULL, value, timeAcquired, priority);
+  }
+
+/*************************************************************************************/
+/* createDataItem()                                                                  */
+/*                                                                                   */
+/* This function will store information about a dataItem into a structure supplied   */
+/* by the calling method. There are traditionally 3 types of data Items, analog,     */
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARGNULL, AX_GEN_STR_TRUNC, AX_OK       */
+/*                        AX_DI_UNKNOWN_TYPE, AX_UNKNOWN                             */
+/*************************************************************************************/
+int ax_data_createStringDataItem(ax_dataSet *destDI, char *diName, char *value, int timeAcquired, int priority) {
+  return ax_data_createDataItem(destDI, diName, AX_STRING, value, -1, timeAcquired, priority);
+  }
+
+/************************************************************************************/
+/*ax_data_createSet()                                                               */
+/*                                                                                  */
+/*Creates and populates a dataSet struct pointed to by the *set parameter. A dataSet*/
+/*can have multiple data items added by the ax_data_addToSet() method.              */
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARGNULL, AX_GEN_STR_TRUNC, AX_OK      */
+/************************************************************************************/
+int ax_data_createSet(ax_dataSet *set, int acquisitionTime, int priority) {
+
+  if(!set) { return AX_ARGNULL; }
+  if((priority<-1)||(priority>100)) { return AX_OUT_OF_RANGE; }
+  if(acquisitionTime<0) {return AX_OUT_OF_RANGE;  }
+    set->dataNode_ct=0;
+    set->data_first=NULL;
+    set->data_last=NULL;
+
+    if(acquisitionTime >=0) {
+        set->acquisitionTime=acquisitionTime;
+        }
+    else {
+        set->acquisitionTime=-1;
+    }
+    if((priority >=1)&&(priority<=100)) {
+        set->priority=priority;
+        }
+    else {
+        set->priority=AX_NO_PRIORITY;
+        }
+
+    set->created=AX_TRUE;
+    return AX_OK;
+    }
+/************************************************************************************/
+/*ax_data_addToSet()                                                                */
+/*                                                                                  */
+/*Adds data to a preExisting data set. Data is represented as a linked list and     */
+/*pointed to by a dataSet structure. Adding data to with this method will cause the */
+/*memory to be malloc'd for each data item you set. To remove all data correctly use*/
+/*the ax_data_destroySet() function call. You will get memory leaks if you do not.  */
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARGNULL, AX_GEN_STR_TRUNC, AX_OK      */
+/*                        AX_DI_UNKNOWN_TYPE                                        */
+/************************************************************************************/
+int ax_data_addToSet(ax_dataSet *set, char *diName, int diType, char *stringValue, double numericValue){
+    ax_dataNode *newNode, *temp=NULL;
+    int retVal=AX_UNKNOWN;
+
+    if((diType<1)||(diType>3)){
+            return AX_DI_UNKNOWN_TYPE;
+            }
+
+      if((!set)||(!diName)){
+          return AX_ARGNULL;
+            }
+
+      if((diType==AX_STRING)&&(!stringValue)){
+          return AX_ARGNULL;
+           }
+      if((diType==AX_DIGITAL)&&((numericValue<0)||(numericValue>1))){
+        return AX_OUT_OF_RANGE;
+        }
+    //if you didn't create the data set with the function this will make sure the linked list values are set. It MAY overwrite the time and priority if they are already set.
+    if(set->created!=AX_TRUE) { ax_data_createSet(set, 0, AX_NO_PRIORITY); }  
+
+    newNode=(ax_dataNode*)malloc(sizeof(ax_dataNode));
+
+    retVal=AX_OK;
+    snprintf(newNode->name, AX_DN_NAME_S, diName);
+    if(strlen(diName)>AX_DN_NAME_S) {
+        retVal=AX_GEN_STR_TRUNC;
+        }
+
+    newNode->next=NULL;
+    newNode->type=diType;
+    if(diType==AX_STRING) {
+	snprintf(newNode->sValue, AX_DN_SV_S, stringValue);
+        if(strlen(stringValue)>AX_DN_SV_S) {
+            retVal=AX_GEN_STR_TRUNC; // if the requested value was too large, set a warning to let us know it was truncated
+            }
+        }
+    else {
+        newNode->dValue=numericValue;
+        }
+
+    if(set->data_first==NULL) {
+        set->data_first=newNode;
+        }
+
+    if(set->data_last!=NULL) {
+        temp=set->data_last;
+        temp->next=(ax_dataNode *)newNode;
+        }
+
+
+    set->data_last=newNode;
+    int nodect=set->dataNode_ct;
+    nodect++;
+    set->dataNode_ct=nodect;
+
+    return retVal;
+    }
+/************************************************************************************/
+/*ax_data_addStringToSet()                                                          */
+/*                                                                                  */
+/*Adds data to a preExisting data set. Data is represented as a linked list and     */
+/*pointed to by a dataSet structure. Adding data to with this method will cause the */
+/*memory to be malloc'd for each data item you set. To remove all data correctly use*/
+/*the ax_data_destroySet() function call. You will get memory leaks if you do not.  */
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARGNULL, AX_GEN_STR_TRUNC, AX_OK      */
+/************************************************************************************/
+int ax_data_addStringToSet(ax_dataSet *set, char *diName, char *value) {
+   return ax_data_addToSet(set, diName, AX_STRING, value, -1);
+}
+/************************************************************************************/
+/*ax_data_addAnalogToSet()                                                          */
+/*                                                                                  */
+/*Adds data to a preExisting data set. Data is represented as a linked list and     */
+/*pointed to by a dataSet structure. Adding data to with this method will cause the */
+/*memory to be malloc'd for each data item you set. To remove all data correctly use*/
+/*the ax_data_destroySet() function call. You will get memory leaks if you do not.  */
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARGNULL, AX_OK                        */
+/************************************************************************************/
+int ax_data_addAnalogToSet(ax_dataSet *set, char *diName, double value){
+  return ax_data_addToSet(set, diName, AX_ANALOG, NULL, value);
+}
+/************************************************************************************/
+/*ax_data_addDigitalToSet()                                                         */
+/*                                                                                  */
+/*Adds data to a preExisting data set. Data is represented as a linked list and     */
+/*pointed to by a dataSet structure. Adding data to with this method will cause the */
+/*memory to be malloc'd for each data item you set. To remove all data correctly use*/
+/*the ax_data_destroySet() function call. You will get memory leaks if you do not.  */
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARGNULL, AX_OK                        */
+/************************************************************************************/
+int ax_data_addDigitalToSet(ax_dataSet *set, char *diName, double value) {
+  return ax_data_addToSet(set, diName, AX_DIGITAL, NULL, value);
+  }    
+    
+    
+/************************************************************************************/
+/*ax_data_destroySet()                                                              */
+/*                                                                                  */
+/*Iterates through all data items in a dataset and frees them. This assumes all nodes*/
+/*have been allocated by malloc.If the addToSet() function was being used then that */
+/*will be true.                                                                     */
+/*Possible Return Codes: AX_OK  AX_ARGNULL                                          */
+/************************************************************************************/
+int ax_data_destroySet(ax_dataSet *set) {
+
+    if(set!=NULL) {
+    ax_dataNode *curr=set->data_first, *next;
+    int ctr;
+    for(ctr=0; ctr<set->dataNode_ct; ctr++)
+        {
+        next=(ax_dataNode *)curr->next;
+        free(curr);
+        curr=next;
+        next=NULL; // CYA'ing here
+        }
+    set->data_first=NULL;
+    set->data_last=NULL;
+    }
+    else {
+	return AX_ARGNULL;
+	}
+
+    return AX_OK;
+    }
+
+
+/*************************************************************************************/
+/* createEvent()                                                                     */
+/*                                                                                   */
+/* This function will populate an event structure and check the values to ensure they*/
+/* are within protocol limit                                                         */
+/* Possible Return Codes: AX_ARGNULL, AX_GEN_STR_TRUNC                               */
+/************************************************************************************/
+int ax_data_createEvent(ax_event *evt, char *name, char *description, int timeOccured, int priority)
+  {
+  int retVal=AX_OK;
+  if((!evt)||(!name)||(!description)){
+    return AX_ARGNULL;
+    }
+  if((priority<-1)||(priority>100)) {return AX_OUT_OF_RANGE; }
+
+  snprintf(evt->name,AX_EVT_NAME_S, name);
+  if(strlen(name)>AX_EVT_NAME_S) {
+      retVal= AX_GEN_STR_TRUNC;
+      }
+ 
+  snprintf(evt->description, AX_EVT_DESC_S, description);
+  if(strlen(description)>AX_EVT_DESC_S) {
+      retVal=AX_GEN_STR_TRUNC;
+      }
+
+  evt->dateAcquired=timeOccured;
+  evt->priority=priority;
+  return retVal;
+  }
+  
+
+
+
+/************************************************************************************/
+/* createLocation()                                                                 */
+/*                                                                                  */
+/* This function will populate a location structure storing the current location    */
+/*                                                                                  */
+/* loc (required) : A structure that will be populated with the data                */
+/* lat (required) : A number between 90 and -90 representing the degrees latitude.  */
+/* lon (required) : A number between 180 and -180 representing the degrees longitude*/
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARGNULL, AX_OK                        */
+/************************************************************************************/
+int ax_data_createLocation(ax_location *loc, double lat, double lon, double alt, int timeAcquired, int priority){
+  if(!loc){
+    return AX_ARGNULL;
+    }
+  
+  if((lat>90)||(lat<-90)){
+    return AX_OUT_OF_RANGE;
+    }
+  
+  if((lon>180)||(lon<-180)){
+    return AX_OUT_OF_RANGE;
+    }
+  
+  
+  loc->latitude=lat;  
+  loc->longitude=lon;
+  loc->altitude=alt;
+
+  loc->dateAcquired=timeAcquired;
+  loc->priority=priority;
+  return AX_OK;
+  }
+
+/************************************************************************************/
+/* createModelSerialDeviceId()                                                      */
+/*                                                                                  */
+/* This function will populate a structure that stores data about the device's iden-*/
+/* tification. The Axeda platform allows for multiple ways of identifying a device. */
+/*                                                                                  */
+/* device(required) : a pointer to a structure where the data will be stored        */
+/* model (required) : A model number that describes a particular family of device.  */
+/*                    If this were describing a car it could be a Ford_Mustang      */
+/* serial (required) : A unique Identifier for the device. If it was a car it would */
+/*                    be analagous to the VIN number.                               */
+/* tenant (optional) : The identifier of the Axeda tenant instance. Not currently   */
+/*                      used and should always default to zero.                     */
+/* Possible Return Codes: AX_ARGNULL, AX_GEN_STR_TRUNC, AX_OK                       */
+/************************************************************************************/
+
+int ax_data_createModelSerialDeviceId(ax_deviceID *device, char *model, char *serial, char *tenant) {
+  int retVal=AX_OK;
+    if((!device)||(!model)||(!serial)){
+    return AX_ARGNULL;
+    }
+  snprintf(device->model, AX_MSID_MDL_S, model);
+  if(strlen(model)> AX_MSID_MDL_S) {
+      retVal=AX_GEN_STR_TRUNC;
+      }
+
+  snprintf(device->serial, AX_MSID_SER_S, serial);
+  if(strlen(serial)>AX_MSID_SER_S) {
+      retVal=AX_GEN_STR_TRUNC;
+      }
+
+  memset(device->tenant, '\0', AX_MSID_TEN_S);
+  if(tenant!=NULL) {
+  	snprintf(device->tenant, AX_MSID_TEN_S, "%s", tenant);
+  	if(strlen(tenant)>AX_MSID_TEN_S) { retVal=AX_GEN_STR_TRUNC; }
+      	}
+
+  return retVal;
+  }
+/************************************************************************************/
+/*ax_createPlatform()                                                               */
+/*                                                                                  */
+/*Populates the ax_platform struct pointed to by axedaCloud It will add the hostname*/
+/*ip and port. Other parts of the ax_platform structure can be populated at will    */
+/*                                                                                  */
+/*                                                                                  */
+/* Possible Return Codes: AX_OUT_OF_RANGE, AX_ARG_NULL, AX_GEN_STR_TRUNC, AX_OK     */
+/************************************************************************************/
+int ax_createPlatform(ax_platform *axedaCloud, char *hostname, int ip[], int port)
+  {
+  int retVal=AX_OK;
+//Check that there's an IP or Hostname.
+  if((!ip)&&(!hostname))
+    { return AX_ARGNULL; } 
+  if(!axedaCloud) {return AX_ARGNULL; }
+//Check the port is in bounds of known ports
+  if((port<0)||(port>65536)) {
+	return AX_OUT_OF_RANGE;
+	}
+  if((hostname)&&(strlen(hostname)>0))
+    {
+    snprintf(axedaCloud->hostname, AX_PLAT_HOST_S, hostname);
+    if(strlen(hostname)>AX_PLAT_HOST_S) {
+        retVal=AX_GEN_STR_TRUNC;
+        }
+    }
+   else {
+    return AX_ARG_EMPTY;
+    }
+
+ if(ip)
+    {
+     int i=0;
+    for(i=0; i<4; i++) { if((ip[i]>255)||(ip[i]<0)) return AX_OUT_OF_RANGE; }
+    axedaCloud->ip[0]=ip[0];
+    axedaCloud->ip[1]=ip[1];
+    axedaCloud->ip[2]=ip[2];
+    axedaCloud->ip[3]=ip[3];
+
+    }
+  axedaCloud->port=port;
+  return retVal;
+  }
+/************************************************************************************/
+/* ax_createFile()                                                                  */
+/*                                                                                  */
+/* Populates a file structure with the necessary info and checks the arguments to   */
+/* ensure compliance with the protocol                                              */
+/* Possible Return Codes: AX_UNKNOWN, AX_GEN_STR_TRUNC, AX_OK, AX_ARGNULL           */
+/*                                                                                  */
+/*file (required): A pointer to an already existing ax_file structure to store data */
+/*name (required): A name for the file, this can be any arbitrary string. It will   */
+/*                 appear on the platform after upload                              */
+/*hint (optional): A string that allows for easy retrieval on the platform          */
+/*size (required): The size of the file to send. The end of the file would occur at */
+/*                 data+size                                                       */
+/*data (required): A pointer to the starting address of data to send, it can be the */
+/*                 first cell in an array or any location in memory.                */
+/************************************************************************************/
+int ax_createFile(ax_file *file, char *name, char *hint, int size, unsigned char *data)
+    {
+    int retVal=AX_OK;
+//ARG Checks
+    if(size<=0) { return AX_OUT_OF_RANGE; }
+	if(file==NULL) { return AX_ARGNULL; }
+	if(name==NULL) { return AX_ARGNULL; }
+	if(data==NULL) { return AX_ARGNULL; }
+//Store the data into the structure
+    snprintf(file->name, AX_FILE_NAME_S, name);
+    if(strlen(name)>AX_FILE_NAME_S){
+        retVal=AX_GEN_STR_TRUNC;
+        }
+
+    if(hint!=NULL) {
+	    snprintf(file->hint, AX_FILE_HINT_S, hint);
+	    if(strlen(hint)>AX_FILE_HINT_S) {
+	        retVal=AX_GEN_STR_TRUNC;
+	        }
+	}
+
+    file->size=size;
+    file->data=data;
+
+    return retVal;
+    }
+
+/************************************************************************************/
+/*ax_createPackageInstruction()                                                     */
+/*                                                                                  */
+/*populates an ax_package_instruction structre passed in. This should never need to */
+/*be called by a library implementor. It will only be used for egress notifications */
+/*                                                                                  */
+/*                                                                                  */
+/************************************************************************************/
+int ax_pkg_createPackageInstruction(ax_package_instruction *inst, int instruction_type, char *file_id, char *path, char *filename){
+	int retVal=AX_OK;
+
+	inst->instruction_type=instruction_type;
+	int fdSZ=snprintf(inst->file_id, AX_PKGI_FID_S, file_id);
+	if(strlen(file_id)>fdSZ) {
+       retVal=AX_GEN_STR_TRUNC;
+                      }
+                  
+    int pathSZ=snprintf(inst->path, AX_PKGI_PATH_S, path);
+	if(strlen(path)>pathSZ) {
+       retVal=AX_GEN_STR_TRUNC;
+       }
+
+                  
+    int fnSZ=snprintf(inst->filename, AX_PKGI_FNAME_S, filename);
+	if(strlen(filename)>fnSZ) {
+       retVal=AX_GEN_STR_TRUNC;
+       }
+	inst->next=NULL;
+
+  return retVal;
+  }
+
+/************************************************************************************/
+/*ax_createPackage()                                                                */
+/*                                                                                  */
+/*populates an ax_package structre passed in. This should never need to be called by*/ 
+/*a library implementor. It will only be used for egress notifications              */
+/*                                                                                  */
+/*                                                                                  */
+/************************************************************************************/
+int ax_pkg_createPackage(ax_package *package, char *pkgID, int time, int priority) {
+  int retVal=AX_OK;
+
+  int nmsz=snprintf(package->packageID, AX_PKG_ID_S, pkgID);
+  if(strlen(pkgID)> nmsz) {
+    retVal=AX_GEN_STR_TRUNC;
+	}
+  package->time=time;
+  package->priority=priority;
+  package->instructions=NULL;
+  
+
+return retVal;
+} 
+/************************************************************************************/
+/*ax_pkg_addDLInstruction()                                                          */
+/*                                                                                   */
+/*Adds a file download package to an already existing package. This function will    */ 
+/*dynamically allocate memory for each instruction. That memory will need to be freed*/
+/*When the status is complete                                                        */
+/*                                                                                   */
+/************************************************************************************/
+int ax_pkg_addDLInstruction(ax_package *pkg, char *file_id, char *path, char *filename) {
+  int retVal=AX_UNKNOWN;
+  ax_package_instruction *curr=NULL;
+  ax_package_instruction *newInst=NULL;
+
+  newInst=(ax_package_instruction *)calloc(1, sizeof(ax_package_instruction));
+  retVal=ax_pkg_createPackageInstruction(newInst, AX_PKG_DOWNLOAD, file_id, path, filename);
+
+  if(pkg->instructions!=NULL) {
+	curr=pkg->instructions;
+	//Go to the end of the list
+  	while(curr->next!=NULL) {
+		curr=curr->next;
+		}
+	curr->next=newInst;
+ 	}
+
+  else {
+	pkg->instructions=newInst;
+	}  
+
+
+return retVal;
+}
+/*************************************************************************************/
+/*ax_pkg_destroyPackage()                                                            */
+/*                                                                                   */
+/*Free's lined package Instructions when created by the ax_pkg_add*() function. Be   */
+/*careful not to use this on a package with instructions that were manually declared */
+/*This does not attempt to free the package itself as it may have been declared and  */
+/*not dynamically allocated.                                                         */
+/************************************************************************************/
+int ax_pkg_destroyPackage(ax_package *pkg) {
+  ax_package_instruction *curr=NULL;
+  ax_package_instruction *target=NULL;
+  curr=pkg->instructions;
+  while(curr!=NULL) {
+	target=curr;       //We'll aim at the current node   
+	curr=curr->next;   //store the value for the next node
+    free(target);	   //kill the target node
+   }
+return AX_OK;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axDomain.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,53 @@
+/************************************************************************************/
+/* axDomain.h                                                                       */
+/* ©2013 Axeda Corporation                                                          */
+/*                                                                                  */
+/* Defines methods for creation and interaction with Axeda domain constructs. This  */
+/* is a device independent implementation which can be applied to any device that   */
+/* supports ANSI C.                                                                 */
+/************************************************************************************/
+
+#ifndef AXDOMAIN_H
+#define AXDOMAIN_H
+
+#include "axTypes.h"
+#include "axConstants.h"
+#include "axStatusCodes.h"
+
+#ifdef __cplusplus 
+  extern "C" {
+#endif
+
+int ax_data_createAlarm(ax_alarm *alm, char *alName, char *alDescription, int alSeverity, char *cause, char *reason, int timeOccured, int priority);
+int ax_data_createEvent(ax_event *evt, char *name, char *description, int timeOccured, int priority);
+int ax_data_createRegistration();
+int ax_data_createLocation(ax_location *loc, double lat, double lon, double alt, int timeAcquired, int priority);
+int ax_data_createModelSerialDeviceId(ax_deviceID *device, char *model, char *serial, char *tenant);
+int ax_createFile(ax_file *file, char *name, char *hint, int size, unsigned char *data);
+
+int ax_createPlatform(ax_platform *axedaCloud, char *hostname, int ip[], int port);
+
+
+int ax_data_createDataItem(ax_dataSet *destDI, char *diName, int diType, char *stringValue, double numericValue, int timeAcquired, int priority);
+int ax_data_createAnalogDataItem(ax_dataSet *destDI, char *diName, double value, int timeAcquired, int priority);
+int ax_data_createDigitalDataItem(ax_dataSet *destDI, char *diName, double value, int timeAcquired, int priority);
+int ax_data_createStringDataItem(ax_dataSet *destDI, char *diName, char *value, int timeAcquired, int priority);
+
+int ax_data_createSet(ax_dataSet *set, int acquisitionTime, int priority);
+
+int ax_data_addToSet(ax_dataSet *set, char *diName, int diType, char *stringValue, double numericValue);
+int ax_data_addStringToSet(ax_dataSet *set, char *diName, char *value);
+int ax_data_addAnalogToSet(ax_dataSet *set, char *diName, double value);
+int ax_data_addDigitalToSet(ax_dataSet *set, char *diName, double value);
+int ax_data_destroySet(ax_dataSet *set);
+
+int ax_pkg_createPackageInstruction(ax_package_instruction *package, int instruction_type, char *file_id, char *path, char *filename);
+int ax_pkg_createPackage(ax_package *package, char *pkgID, int time, int priority);
+int ax_pkg_addDLInstruction(ax_package *pkg, char *file_id, char *path, char *filename);
+int ax_pkg_destroyPackage(ax_package *pkg);
+
+#ifdef __cplusplus
+  }
+#endif
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axHTTP.c	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,460 @@
+#include "axHTTP.h"
+#include "axStatusCodes.h"
+#include "axConstants.h"
+#include "ctype.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "axSettings.h"
+#include "axTransport.h"
+
+#define AX_HTTP_OK                  200
+#define AX_HTTP_SYNTAX_INVALID      400     //The Request syntax is incorrect
+#define AX_HTTP_UNAUTHORIZED        401     //You need permission to access that resource. Agent Authentication failure
+#define AX_HTTP_FORBIDDEN           403     //You need permission to access that resource.
+#define AX_HTTP_NOT_FOUND           404     //The resource was not found
+#define AX_HTTP_REQ_INVALID         405     //Content type is invalid, should be application/json most of the time
+#define AX_HTTP_CONTENT_INVALID     422     //The Request is valid syntactically but the content is invalid e.g. wrong instruction
+#define AX_HTTP_SERVER_ERROR        500     //The server attempted to handle your request but a failure occurred; check the server logs.
+
+#define HTTPKEY_GET         0
+#define HTTPKEY_POST        1
+#define HTTPKEY_PUT         2
+#define HTTPKEY_PROTOCOL    3
+#define HTTPKEY_CRLF        4
+
+
+#define HTTPH_HOST          0
+#define HTTPH_TYPE          1
+#define HTTPH_LENGTH        2
+#define HTTPH_CONNECTION    3
+#define HTTPH_XCOUNT        4
+#define HTTPH_CONTENTDIS    5
+#define HTTPH_MULTIPART     6
+#define HTTPH_FORMDATA      7
+#define HTTPH_ACCEPT        8
+
+#define HTTPR_TYPE          0
+#define HTTPR_LENGTH        1
+#define HTTPR_BODY_SEP      2
+#define HTTPR_DELIMS        3
+
+int http_debug=AX_FALSE;
+int x_count=0;
+
+int http_getResponse(HTTP_Transmission *response, ax_socket *source);
+
+char *HTTP_KEY[]= {"GET", "POST", "PUT", "HTTP/1.1", "\r\n"};
+char *HTTP_HEADER[] = { "Host: ", "Content-Type: ", "Content-Length: ", "Connection: close", "x-count: ", "Content-Disposition: ","multipart/form-data; boundary=", "form-data;", "Accept: */*"};
+char *HTTP_RESP[] = { "Content-Type:", "Content-Length:", "\r\n\r\n", " \r\n"}; 
+
+int http_send(HTTP_Transmission *request, char *host, int port, int secure, HTTP_Transmission *response){
+  int retVal=AX_UNKNOWN;
+
+  switch(request->operation) {
+      case AX_HTTP_GET:
+        retVal=http_send_get(request->resource, host, port, secure, response);
+      break;
+      case AX_HTTP_POST:
+        retVal=http_send_post(request->resource, host, port, secure, &request->headers, request->body, request->body_length, response);
+      break;
+      case AX_HTTP_PUT:
+              retVal=http_send_put(request->resource, host, port, secure, &request->headers, request->body, request->body_length, response);
+      break;
+      case AX_HTTP_MPOST:
+        retVal=http_send_mpost(request->resource, host, port, secure, &request->headers, request->body, request->body_length, request->mpData, 1, response);
+      break;
+      }
+
+  return retVal;
+  }
+
+int http_send_get(char *resource, char *host, int port, int secure, HTTP_Transmission *response) {
+  int retVal=AX_UNKNOWN;
+
+  if(!resource||!host||!response) { return AX_ARGNULL; }
+  if((port < 0)||(port>65536)) { return AX_NET_PORT_INVALID; }
+  int sz=strlen(HTTP_KEY[HTTPKEY_GET])+strlen(resource)+strlen(HTTP_KEY[HTTPKEY_PROTOCOL])+strlen(HTTP_HEADER[HTTPH_HOST])+strlen(host)+13+6+strlen(HTTP_HEADER[HTTPH_CONNECTION])+strlen(HTTP_HEADER[HTTPH_ACCEPT]); //+6 adds space for extra CRLFs, spaces, and null terminator
+
+  ax_socket sock;
+
+  retVal=net_socketInit(&sock);
+  retVal=net_socketOpen(&sock, host, port, secure);
+  if(retVal!=AX_OK){
+      return retVal;
+      }
+
+  char *buff=(char *)malloc(sizeof(char)*sz);
+  int wrtSz=snprintf(buff, sz, "%s %s %s%s%s%s:%d%s%s%s%s", HTTP_KEY[HTTPKEY_GET], resource, HTTP_KEY[HTTPKEY_PROTOCOL], HTTP_KEY[HTTPKEY_CRLF], HTTP_HEADER[HTTPH_HOST], host, port, HTTP_KEY[HTTPKEY_CRLF], HTTP_HEADER[HTTPH_CONNECTION], HTTP_KEY[HTTPKEY_CRLF], HTTP_KEY[HTTPKEY_CRLF]);
+
+  if(wrtSz>sz) { printDebug("http_send_get(): Buffer allocated for header was too small, truncated"); } 
+  retVal=net_socketWrite(&sock, buff, sz);
+  retVal=net_socketFlush(&sock);
+
+  retVal=http_getResponse(response, &sock);
+  if(retVal==AX_HTTP_OK) { retVal=AX_OK; }
+
+  net_socketClose(&sock);
+  free(buff);
+  return retVal;
+  }
+
+int http_send_put(char *resource, char *host, int port, int secure, HTTP_Headers *headers, char *data, int data_sz, HTTP_Transmission *response) {
+  int retVal=AX_UNKNOWN;
+
+  if(!resource||!host||!response) { return AX_ARGNULL; }
+  if((port < 0)||(port>65536)) { return AX_NET_PORT_INVALID; }
+  if((!data)&&(data_sz!=0)) { return AX_CONFLICTING_ARG; }
+
+  ax_socket sock;
+
+  retVal=net_socketInit(&sock);
+  retVal=net_socketOpen(&sock, host, port, secure);
+  if(retVal!=AX_OK){ //if a failure occurred bail out. failures here would be that a port is not free
+    return retVal;
+    }
+  printDebug("Attempting to create headers");
+   char *header=NULL;
+   header=createHeaders(header, HTTPKEY_PUT, resource, HTTPKEY_PROTOCOL, host, headers->contentType, data_sz, x_count); //returns a malloc'd string with the headers
+
+  retVal=net_socketWrite(&sock, header, strlen(header));
+  if(retVal!=AX_OK){ //if a failure occurred bail out. failures here would be that a port is not free
+      goto cleanup;
+      }
+  retVal=net_socketWrite(&sock, data, data_sz);
+  net_socketFlush(&sock);
+  if(retVal!=AX_OK) {
+     retVal=AX_NET_ERR_DATA_WRITE;
+     goto cleanup;
+     }
+
+  retVal=http_getResponse(response, &sock);
+  if(retVal==AX_HTTP_OK) { retVal=AX_OK; }
+
+  x_count++;
+   goto cleanup;
+
+
+cleanup:
+  net_socketClose(&sock);
+  free(header);
+  return retVal;
+}
+
+
+
+int http_send_post(char *resource, char *host, int port, int secure, HTTP_Headers *headers, char *data, int data_sz, HTTP_Transmission *response){
+  int retVal=AX_UNKNOWN;
+
+  if(!resource||!host||!response) { return AX_ARGNULL; }
+  if((port < 0)||(port>65536)) { return AX_NET_PORT_INVALID; }
+  if((!data)&&(data_sz!=0)) { return AX_CONFLICTING_ARG; }
+
+  ax_socket sock;
+
+  retVal=net_socketInit(&sock);
+  retVal=net_socketOpen(&sock, host, port, secure);
+  if(retVal!=AX_OK){ //if a failure occurred bail out. failures here would be that a port is not free
+    return retVal;
+    }
+  printDebug("Attempting to create headers");
+   char *header=NULL;
+   header=createHeaders(header, HTTPKEY_POST, resource, HTTPKEY_PROTOCOL, host, headers->contentType, data_sz, x_count); //returns a malloc'd string with the headers
+
+  retVal=net_socketWrite(&sock, header, strlen(header));
+  if(retVal!=AX_OK){ //if a failure occurred bail out. failures here would be that a port is not free
+      goto cleanup;
+      }
+  retVal=net_socketWrite(&sock, data, data_sz);
+  net_socketFlush(&sock);
+  x_count++;
+  printDebug("Transmission sent, Waiting for response\n");
+
+
+  retVal=http_getResponse(response, &sock);
+  if(retVal==AX_HTTP_OK) { retVal=AX_OK; }
+
+   goto cleanup;
+
+cleanup:
+  net_socketClose(&sock);
+  free(header);
+  return retVal;
+  }
+
+int http_send_mpost(char *resource, char *host, int port, int secure, HTTP_Headers *headers, char *data, int data_sz, HTTP_Part *files, int part_ct, HTTP_Transmission *response) {
+    int retVal=AX_UNKNOWN;
+ //   char xcount_tmp[10];
+//    char clength_tmp[10];
+    int boundary_sz=10;
+    int body_sz=0;
+    int hint_sz=0;
+    char *hint=NULL;
+	char *header=NULL;
+    ax_socket sock;
+
+      if(!resource||!host||!response) { return AX_ARGNULL; }
+    if((port < 0)||(port>65536)) { return AX_NET_PORT_INVALID; }
+    if((!data)&&(data_sz!=0)) { return AX_CONFLICTING_ARG; }
+ //Assemble body Parts
+    char *fileargs=NULL;
+    int file_arg_len=strlen(files->file_name)+51;
+    fileargs=(char *)malloc(sizeof(char)*file_arg_len);
+    int wrtfarg=snprintf(fileargs, file_arg_len, "name=\"file\"; filename=\"%s\"\r\nContent-Type: text/plain", files->file_name);
+    if(wrtfarg>file_arg_len) { printDebug("http_send_mpost(): Buffer allocated for file arguments was too small, truncated"); }
+    if(files->hint!=NULL)
+        {
+        hint_sz=strlen(files->hint)+18;//+1 for terminating char
+        hint=(char *)malloc(sizeof(char)*hint_sz);
+        int wrthint=snprintf(hint, hint_sz, "name=\"hint\"\r\n\r\n%s\r\n", files->hint);
+        if(wrthint>hint_sz) { printDebug("http_send_mpost(): Buffer allocated for hint was too small, truncated"); }
+        }
+//body size calculation here
+  //Boundary: --<boundary>
+    body_sz=body_sz+2+boundary_sz;   
+  //adding body ---for mparts
+    if(data_sz>0){
+      body_sz=body_sz+36+data_sz+boundary_sz;  
+      }
+  //Add Hint     Content-Disposition: form-data;name="<hint>"\r\n--<boundary>\r\n
+    if(files->hint!=NULL){
+      body_sz=body_sz+38+strlen(hint)+boundary_sz;  
+      }
+    if(files->data_sz>0) {
+      body_sz=body_sz+38+strlen(fileargs)+files->data_sz;
+      }
+    body_sz=body_sz+8+boundary_sz;
+
+//get a boundary for the multipart
+    char *boundary=(char *)malloc(sizeof(char)*boundary_sz);
+    http_getBoundary(boundary, boundary_sz);
+//convert the data size integer into a string
+ //  int wrtCLen=snprintf(clength_tmp, 10, "%d", body_sz);
+ //   if(wrtCLen>10) { printDebug("http_send_mpost(): Buffer allocated for clength_tmp was too small, truncated"); }
+    //Add operation line
+
+    printDebug("Attempting to create headers");
+
+//Create Content Type 
+	 int cTypeSz=0;
+	 cTypeSz=strlen(HTTP_HEADER[HTTPH_MULTIPART])+strlen(boundary)+1;
+	 char *contentType=(char *)calloc(cTypeSz, sizeof(char));       
+	 int wrt_ctype=snprintf(contentType, cTypeSz, "%s%s", HTTP_HEADER[HTTPH_MULTIPART], boundary);
+	 if(wrt_ctype>cTypeSz) { printDebug("http_send_mpost(): Buffer allocated for contentType was too small, truncated");}
+//Get the headers
+     header=createHeaders(header, HTTPKEY_POST, resource, HTTPKEY_PROTOCOL, host, contentType, body_sz, x_count); //returns a malloc'd string with the headers
+	 free(contentType); //we're done with the string we used to cat the values
+//Start to send the data
+	retVal=net_socketInit(&sock);
+    retVal=net_socketOpen(&sock, host, port, secure);
+    if(retVal!=AX_OK){ //if a failure occurred, bail out. A failure here would indicate that a port is closed, or the network is not available.
+      return retVal;
+      }
+//Write the headers
+    retVal=net_socketWrite(&sock, header, strlen(header));
+      if(retVal!=AX_OK){ goto cleanup; }
+
+   //write the body...
+   //start boundary
+      retVal=net_socketWrite(&sock, "--", 2);
+      retVal=net_socketWrite(&sock, boundary, boundary_sz);
+      retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2);
+   //1st section -uses the data in body if specified
+    if(data_sz>0){
+      retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_CONTENTDIS], 21);
+      retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_FORMDATA], 11);
+      retVal=net_socketWrite(&sock, data, data_sz); //Write body data, the fields and values
+      retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2);
+      retVal=net_socketWrite(&sock, "--", 2);
+      retVal=net_socketWrite(&sock, boundary, boundary_sz);
+      }
+    if(files->hint!=NULL){
+       retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_CONTENTDIS], 21);
+       retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_FORMDATA], 11);
+       retVal=net_socketWrite(&sock, hint, hint_sz); //Write body data, the fields and values
+       retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2);
+       retVal=net_socketWrite(&sock, "--", 2);
+       retVal=net_socketWrite(&sock, boundary, boundary_sz);
+       retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2);
+       }
+    if(files->data_sz>0) {
+        retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_CONTENTDIS], 21);
+        retVal=net_socketWrite(&sock, HTTP_HEADER[HTTPH_FORMDATA], 11);
+        retVal=net_socketWrite(&sock, fileargs, file_arg_len);
+        retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2);
+        retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2);
+        retVal=net_socketWrite(&sock, (char *)files->data, files->data_sz);
+        retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2);
+        }
+      retVal=net_socketWrite(&sock, "--", 2);
+      retVal=net_socketWrite(&sock, boundary, boundary_sz);
+      retVal=net_socketWrite(&sock, "--", 2);
+      retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2);
+      retVal=net_socketWrite(&sock, HTTP_KEY[HTTPKEY_CRLF], 2);
+
+
+    net_socketFlush(&sock);
+    x_count++;
+    retVal=http_getResponse(response, &sock);
+    if(retVal==AX_HTTP_OK) { retVal=AX_OK; }
+
+  cleanup:
+   net_socketClose(&sock);
+   free(header);
+   free(hint);
+   free(fileargs);
+   free(boundary);
+   return retVal;
+  }
+
+void http_add_mpart(HTTP_Transmission *request, char *file_name, unsigned char *data, int file_sz) {
+ //For future expansion.
+}
+
+//Return Handling
+HTTP_Transmission *http_parse(HTTP_Transmission *response, char *data){
+printDebug("Starting parse of ");
+printDebug(data);
+
+    char clength[7];
+    int reqSize=0;
+    char *temp=NULL;
+    char *bodyStart=NULL;
+    response->body_length=0;
+    response->response_code=-1; //indicate that no response code has been recieved(yet)
+
+    reqSize=strlen(data);
+    if(reqSize<=12) { printDebug("No Body included"); }
+    else { printDebug("body present" );
+    bodyStart=strstr(data, HTTP_RESP[HTTPR_BODY_SEP]);
+    if(bodyStart==NULL) { printDebug("D'oh! Couldn't parse for body\n"); }
+    else {
+	 int body_length=strlen(bodyStart)-4; //don't count the extra \r\n\r\n
+         response->body_length=body_length;
+         response->body=(char *)calloc(body_length+1, sizeof(char));
+         snprintf(response->body, body_length+1, "%s", bodyStart+4); //the +4 gets rid of the preceeding \r\n\r\n
+         }
+    }
+
+    temp = strtok(data, HTTP_RESP[HTTPR_DELIMS]);
+    while (temp!=NULL) {
+
+    if((strlen(temp)==3)&&(isdigit((int)temp[0]))&&(isdigit((int)temp[1]))&&(isdigit((int)temp[2]))) {
+        response->response_code=atoi(temp); //convert the text response code to an int.
+        }
+
+    if(strcmp(temp, HTTP_RESP[HTTPR_TYPE])==0) {
+        response->headers.contentType=strtok(NULL, HTTP_RESP[HTTPR_DELIMS]);
+        }
+    if(strcmp(temp, HTTP_RESP[HTTPR_LENGTH])==0){
+        temp=strtok(NULL, HTTP_RESP[HTTPR_DELIMS]);
+        strcpy(clength, temp);
+    response->body_length=atoi(clength);
+    if(response->body_length<=0) {
+         response->body_length=strlen(bodyStart+4);
+          }
+        }
+
+    temp=strtok(NULL, HTTP_RESP[HTTPR_DELIMS]);
+    }
+
+    return response;
+    }
+
+int http_getResponse(HTTP_Transmission *response, ax_socket *source){
+    int retVal=AX_UNKNOWN;
+    char *responseTxt=NULL;
+     responseTxt=(char *)calloc(AX_NET_BUFF_S, sizeof(char));
+     int length=0;
+     response->response_code=0;
+     retVal=net_socketRead(source, 300, (unsigned char *)responseTxt, AX_NET_BUFF_S, &length);
+     if(retVal==AX_OK) { printDebug("response received"); }
+     else { printDebug("Response Timeout"); return AX_NET_ERR_TIMEOUT;}
+     http_parse(response, (char *)responseTxt); 
+
+     free(responseTxt);
+    return response->response_code;
+    }
+
+
+//Supporting Methods
+/***************************************************************************************************/
+/*http_getBoundary()                                                                               */
+/*                                                                                                 */
+/*This method creates a unique string token that is used when creating mulitpart form transmissions*/
+/*in HTTP. It will return a random sequence of letters and numbers.                                */
+/***************************************************************************************************/
+char *http_getBoundary(char *buff, int length){
+    int ctr=0;
+    if(buff==NULL) { return NULL; } //if no pointer was given to us give a nothing back.
+    if(length<=0) {return NULL;} 
+       int randNum=0;
+       for(ctr=0; ctr<length; ctr++) {
+       randNum=abs(randInt()%62);
+           if(randNum<=10) {
+		if(randNum==0) { randNum++; } //avoid using slashes in boundary, char 47
+               buff[ctr]=randNum+47;
+               }
+           else if((randNum>10)&&(randNum<=36)) {
+               buff[ctr]=(randNum-10)+64;
+               }
+           else {
+               buff[ctr]=(randNum-37)+97;
+               }
+           if(buff[ctr]==32) {buff[ctr]=97; }
+       }
+
+    return buff;
+    }
+
+void zeroOutHTTPXmission(HTTP_Transmission *tgt){
+
+	    tgt->operation=-1;
+	    tgt->resource=NULL;
+	    tgt->headers.contentType=NULL;
+	    tgt->body=NULL;
+	    tgt->body_length=0;
+	    tgt->mpData=NULL;
+	    tgt->response_code=-1;
+	    tgt->secure=-1;
+}
+
+char *createHeaders(char *tgtBuff, int http_operation, char *resource, int httpver, char *hostname, char *contentType, int contentLength, int xCount) {
+  int headerSz=4;  //we account for the closing CRLF CRLF here
+  char clength_tmp[10];
+  char xCount_tmp[10];
+  int wrthdr=0;  
+//convert the ints to strings so we can get an strlen on them
+  snprintf(clength_tmp, 10, "%d", contentLength);
+  snprintf(xCount_tmp, 10, "%d", xCount);
+
+//String Size Calculations
+  headerSz=headerSz+strlen(resource)+strlen(HTTP_KEY[http_operation])+strlen(HTTP_KEY[httpver])+5; //"POST  HTTP/1.1" there are 2 spaces and a CRLF in there as well., 1 for the null
+  headerSz=headerSz+strlen(HTTP_HEADER[HTTPH_HOST])+strlen(hostname)+2;  //Host: <hostname>
+  headerSz=headerSz+strlen(HTTP_HEADER[HTTPH_TYPE])+strlen(contentType)+2;  //Content-Type: <contentType>
+  headerSz=headerSz+strlen(HTTP_HEADER[HTTPH_LENGTH])+strlen(clength_tmp)+2;  //Content-Length: <contentLength>
+  headerSz=headerSz+strlen(HTTP_HEADER[HTTPH_CONNECTION])+2; 		      //Connection: Close
+  if(xCount>0) {
+    headerSz=headerSz+strlen(HTTP_HEADER[HTTPH_XCOUNT])+strlen(xCount_tmp)+2;	
+   }
+
+//String buffer creation  
+  tgtBuff=(char *)calloc(headerSz, sizeof(char));
+//Stuffin the buffer
+  if((xCount>0)&&(http_debug==AX_TRUE)) {
+     wrthdr=snprintf(tgtBuff, headerSz, "%s %s %s\r\n%s%s\r\n%s%s\r\n%s%s\r\n%s\r\n%s%s\r\n\r\n", HTTP_KEY[http_operation], resource, HTTP_KEY[httpver], HTTP_HEADER[HTTPH_HOST], hostname, HTTP_HEADER[HTTPH_TYPE], contentType, HTTP_HEADER[HTTPH_LENGTH], clength_tmp, HTTP_HEADER[HTTPH_CONNECTION], HTTP_HEADER[HTTPH_XCOUNT], xCount_tmp);
+	}
+  else {
+     wrthdr=snprintf(tgtBuff, headerSz, "%s %s %s\r\n%s%s\r\n%s%s\r\n%s%s\r\n%s\r\n\r\n", HTTP_KEY[http_operation], resource, HTTP_KEY[httpver], HTTP_HEADER[HTTPH_HOST], hostname, HTTP_HEADER[HTTPH_TYPE], contentType, HTTP_HEADER[HTTPH_LENGTH], clength_tmp, HTTP_HEADER[HTTPH_CONNECTION]);   
+
+     }
+  if(wrthdr>headerSz) { printDebug("http_send_post(): Buffer allocated for header was too small, truncated"); }
+
+return tgtBuff;
+}
+
+
+
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axHTTP.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,76 @@
+/************************************************************************************/
+/* axHTTP.h                                                                         */
+/* �2013 Axeda Corporation                                                          */
+/*                                                                                  */
+/*Provides a basic, bare bones implementation of the HTTP 1.1 protocol spec to allow*/
+/*this library to be more platform independent. The function calls in this file     */
+/*depend on the socket calls provided in axTransport                                */
+/*                                                                                  */
+/************************************************************************************/
+#ifndef AXHTTP_H
+#define AXHTTP_H
+
+//#include "axTransport.h"
+
+#define AX_HTTP_GET    0  //Standard GET Operation
+#define AX_HTTP_POST   1  //Standard HTTP Post operation
+#define AX_HTTP_MPOST  2  //Multipart form data operation, POST
+#define AX_HTTP_PUT    3
+
+
+
+typedef struct {
+ // char *host; ..Host is passed in when sending to the
+  char *contentType;
+}HTTP_Headers;
+
+typedef struct HTTP_Part{
+   char *file_name;
+   unsigned char *data;
+   int data_sz;
+   char *hint;
+   struct HTTP_Part *next;
+}HTTP_Part;
+
+typedef struct {
+    int operation;          //POST, GET, MPOST.
+    char *resource;         //A pointer to a string that the resource is identified by
+    HTTP_Headers headers;   //a structure of headers for the request
+    char *body;             //the body of the request, used only for post and mpost
+    int body_length;        //the length of the main body, used only for post and mpost
+    HTTP_Part *mpData;      //the first link in a chain of data to be uploaded as part of a multipart post request
+    int response_code;      //For responses only, this field will be populated after calling http_parse
+    int secure;             //use SSL or TLS
+}HTTP_Transmission;
+
+//Flag used to turn on debug x-count header.
+extern int http_debug;
+
+struct ax_socket; //incomplete declaration, see axTransport.h for full declaration
+
+#ifdef __cplusplus 
+  extern "C" {
+#endif
+
+
+//Operations
+int http_send(HTTP_Transmission *request, char *host, int port, int secure, HTTP_Transmission *response);
+int http_send_get(char *resource, char *host, int port, int secure, HTTP_Transmission *response);
+int http_send_put(char *resource, char *host, int port, int secure, HTTP_Headers *headers, char *data, int data_sz, HTTP_Transmission *response);
+int http_send_post(char *resource, char *host, int port, int secure, HTTP_Headers *headers, char *data, int data_sz, HTTP_Transmission *response);
+int http_send_mpost(char *resource, char *host, int port, int secure, HTTP_Headers *headers, char *data, int data_sz, HTTP_Part *files, int part_ct, HTTP_Transmission *response);
+void http_add_mpart(HTTP_Transmission *request, char *file_name, unsigned char *data, int file_sz); //For Future expansion, do not use.
+
+//Return Handling
+HTTP_Transmission *http_parse(HTTP_Transmission *response, char *data);
+
+//Supporting Methods
+char *http_getBoundary(char *buff, int length);
+void zeroOutHTTPXmission(HTTP_Transmission *tgt);
+char *createHeaders(char *tgtBuff, int http_operation, char *resource, int httpver, char *hostname, char *contentType, int contentLength, int xCount);
+
+#ifdef __cplusplus
+  }
+#endif
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axPlatform.c	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,670 @@
+/************************************************************************************/
+/* axPlatform.c                                                                    */
+/* 2013 Axeda Corporation                                                          */
+/*                                                                                  */
+/* Defines methods for interaction with end points on the Axeda Platform.  This     */
+/* is a device independent implementation which can be applied to any device that   */
+/* supports ANSI C.                                                                 */
+/************************************************************************************/
+
+#include "axPlatform.h"
+#include "axStatusCodes.h"
+#include <string.h>
+#include <stdlib.h>
+#include "axTransport.h"
+#include "axSerializer.h"
+#include "cJSON.h"
+#include "axConstants.h"
+#include "axHTTP.h"
+
+int handleResponse(HTTP_Transmission *response);
+
+static const char *endpoints[8]= { "ammp", "data", "assets", "files", " ", "packages", "status", " "};
+static const char *contentTypes[]= { "application/json", "multipart/form-data"};
+static const char *buffer_keywords[]= {"alarms", "events", "data", "locations" };
+
+const char URL_SEP='/';
+const char ID_SEP='!';
+
+/************************************************************************************/
+/*ax_platform_upload()                                                              */
+/*                                                                                  */
+/*file(required) : a pointer to a structure that the data will be stored in.        */
+/*name (required): a file name for the file. Can be arbitrarily assigned based on   */
+/*                 source of the data.                                              */
+/*hint(required): a string that allows you to tag the file for later retrieval      */
+/*size(optional): should be populated if the data does not have an EOF character at */
+/*                the end. If sending a memory block this will define the offset.   */
+/*data(required): a pointer to the data that will be sent as this file              */
+/*                                                                                  */
+/*                                                                                  */
+/************************************************************************************/
+int ax_platform_upload(ax_platform *cloud, ax_deviceID *device, ax_file *file)
+  {
+  int retVal=AX_UNKNOWN;
+  char *url=NULL;
+  HTTP_Part fileDat;
+  HTTP_Transmission response;
+  fileDat.data=file->data;
+  fileDat.data_sz=file->size;
+  fileDat.file_name=file->name;
+  fileDat.hint=file->hint;
+  fileDat.next=NULL;
+
+  url = getFileResource(url, device);
+
+  int body_sz=strlen(file->hint)+12+strlen(file->name);
+  char *body=malloc(sizeof(char)*body_sz);
+  int bodyWrt=snprintf(body, body_sz, "name=\"hint\" \r\n %s",file->hint);
+  if(bodyWrt>body_sz) { retVal=AX_GEN_STR_TRUNC; }
+  printDebug(body);
+
+  retVal=http_send_mpost(url, cloud->hostname, cloud->port, cloud->secure, NULL, NULL, 0, &fileDat, 1, &response);
+
+  free(body);
+  free(url);
+
+return retVal;
+}
+
+/************************************************************************************/
+/* ax_platform_ping()                                                               */
+/*                                                                                  */
+/* This function creates a ping message which acts as the device heartbeat.         */
+/*                                                                                  */
+/*                                                                                  */
+/************************************************************************************/
+int ax_platform_ping(ax_platform *cloud, ax_deviceID *device)
+  {
+  int retVal=AX_UNKNOWN;
+  char *url=NULL;
+  HTTP_Headers myHeader;
+  HTTP_Transmission response;
+  myHeader.contentType=getGeneralType(myHeader.contentType);
+  url=getAgentResource(url, device);
+
+//Send the request
+  retVal= http_send_post(url, cloud->hostname, cloud->port, cloud->secure, &myHeader, NULL, 0, &response);
+//Scan the response and act if there are egress messages.
+  handleResponse(&response);
+  free(url);
+  return retVal;
+  }
+
+/************************************************************************************/
+/*ax_platform_download()                                                            */
+/*                                                                                  */
+/*This function wil request a file from the platform with a specific file ID.  The  */
+/*fileID itself will be transmitted down on a response message from the platform.   */
+/*                                                                                  */
+/*                                                                                  */
+/************************************************************************************/
+int ax_platform_download(ax_platform *cloud, ax_deviceID *device, ax_package_instruction *instr)
+  {
+    int retVal = AX_UNKNOWN;
+    char *url=NULL;
+    HTTP_Transmission response;
+    url=getFileDownloadResource(url, device, instr->file_id);
+	retVal=http_send_get(url, cloud->hostname, cloud->port, cloud->secure, &response);
+//TODO: revisit what to do with response in this case.
+    scm_file_write(instr->filename, response.body, response.body_length, instr);
+    free(url);
+    return retVal;
+  }
+
+
+/************************************************************************************/
+/* ax_platform_register()                                                           */
+/*                                                                                  */
+/*This function creates a registration message to be sent to the cloud. The message */
+/*contains information about the model, serial and expected ping interval. It also  */
+/*allows the cloud to mark the device as online. If using the standalone thread func*/
+/*-tion this method will be called automatically until successful.                  */
+/*                                                                                  */
+/*cloud(required) : a pointer to a structure that contains data about the cloud     */
+/*device(required) : a pointer to a structure that contains the model and serial    */
+/*pingRate(required) : an integer in milliseconds which tells the cloud how often to*/
+/*                     expect a heartbeat ping                                      */
+/*                                                                                  */
+/*Returns: an integer value indicating whether or not it successfully registered    */
+/************************************************************************************/
+int ax_platform_register(ax_platform *cloud, ax_deviceID *device)
+  {
+  int retVal=AX_UNKNOWN;
+  char *url=NULL;
+  char *fullRequest=NULL;
+  cJSON *requestBody;
+  HTTP_Headers myHeader;
+  HTTP_Transmission response;
+  myHeader.contentType=getGeneralType(myHeader.contentType);
+  url = getRegistrationResource(url, device);
+  int urlLength=strlen(url);
+  url[urlLength]='\0';
+
+  requestBody=getRegistrationJSON(device, cloud->ping_rate);
+
+  fullRequest=cJSON_PrintUnformatted(requestBody);
+  int contentLength=strlen(fullRequest);
+
+  retVal= http_send_post(url, cloud->hostname, cloud->port, cloud->secure, &myHeader, fullRequest, contentLength, &response);
+//Scan the response and act if there are egress messages.
+  handleResponse(&response);
+
+//Clean up after ourselves
+  free(fullRequest);
+  free(url);
+  cJSON_Delete(requestBody);
+  return retVal;
+  }
+
+/**************************************************************************************/
+/*ax_platform_send()                                                                  */
+/*                                                                                    */
+/*This function sends any data available in the structures directly to the cloud in   */
+/*an on demand fashion.                                                               */
+/*                                                                                    */
+/*                                                                                    */
+/*cloud(required) : a pointer to a structure that contains the cloud service info     */
+/*device(required) : a pointer to a structure that contains device information        */
+/*dataItems(optional) : a pointer to an array of ax_dataItem pointers that will be sent*/
+/*alarms(optional) : a pointer to an array of ax_alarm pointers that will be sent      */
+/*events(optional) : a pointer to an array of ax_event pointers that will be sent      */
+/*locations(optional) : a pointer to an array of ax_location pointers that will be sent*/
+/*                                                                                     */
+/*Returns: an integer value indicating whether or not it successfully sent the data    */
+/**************************************************************************************/
+int ax_platform_send(ax_platform *cloud, ax_deviceID *device, ax_dataSet *dataItems[], int numDataSets, ax_alarm *alarms[], int numAlarms, ax_event *events[], int numEvents, ax_location *locations[], int numLocations) {
+  
+  int retVal=AX_UNKNOWN;
+  HTTP_Headers myHeader;
+  char *url=NULL;
+  
+  char *fullRequest=NULL;
+  cJSON *rootNode=NULL;
+  rootNode=cJSON_CreateObject();
+  HTTP_Transmission response;
+  zeroOutHTTPXmission(&response);
+  //add the data in the DataItems queue if passed
+  if(dataItems){
+      cJSON_AddItemToObject(rootNode, buffer_keywords[BUFF_DATA], dataSet2JSON((ax_dataSet **)dataItems, numDataSets, terse_enable));
+      }
+  //add all alarms to the transmission if passed
+  if(alarms){
+    cJSON_AddItemToObject(rootNode, buffer_keywords[BUFF_ALARMS], AlarmsToJSON((ax_alarm **)alarms, numAlarms, terse_enable));
+    }
+  if(events){
+    cJSON_AddItemToObject(rootNode, buffer_keywords[BUFF_EVENTS], eventsToJSON((ax_event **)events, numEvents, terse_enable));
+  }
+  if(locations){
+    cJSON_AddItemToObject(rootNode, buffer_keywords[BUFF_LOCATIONS], locationsToJSON((ax_location **)locations, numLocations, terse_enable));
+  }
+
+  myHeader.contentType=getGeneralType(myHeader.contentType);
+  url = getDataResource(url, device);
+  fullRequest=cJSON_PrintUnformatted(rootNode);
+
+  retVal= http_send_post(url, cloud->hostname, cloud->port, cloud->secure, &myHeader, fullRequest, strlen(fullRequest), &response);
+//Scan the response and act if there are egress messages.
+  handleResponse(&response);
+
+  if(fullRequest) {
+  free(fullRequest);
+  }
+  free(url);
+  cJSON_Delete(rootNode);
+
+return retVal;
+  }
+
+/**************************************************************************************/
+/*ax_platform_sendData()                                                              */
+/*                                                                                    */
+/*This function immedately sends any data included in the parameter to the platform   */
+/*listed. It makes use of the ax_platform_send() function as a special case.          */
+/*                                                                                    */
+/*cloud(required) : a pointer to a structure that contains the cloud service info     */
+/*device(required) : a pointer to a structure that contains device information        */
+/*dataItems(required) : pointer to an array of ax_dataItem pointers that will be sent */
+/*                                                                                     */
+/*Returns: an integer value indicating whether or not it successfully sent the data    */
+/**************************************************************************************/
+int ax_platform_sendData(ax_platform *cloud, ax_deviceID *device, ax_dataSet *dataItems[], int numDataSets) {
+
+return ax_platform_send(cloud, device, dataItems, numDataSets, NULL, 0, NULL, 0, NULL, 0);
+
+}
+/**************************************************************************************/
+/*ax_platform_sendAlarms()                                                            */
+/*                                                                                    */
+/*This function immedately sends any alarms included in the parameter to the platform */
+/*listed. It makes use of the ax_platform_send() function as a special case.          */
+/*                                                                                    */
+/*cloud(required) : a pointer to a structure that contains the cloud service info     */
+/*device(required) : a pointer to a structure that contains device information        */
+/*alarms(required) : a pointer to an array of ax_alarm pointers that will be sent     */
+/*                                                                                     */
+/*Returns: an integer value indicating whether or not it successfully sent the data    */
+/**************************************************************************************/
+int ax_platform_sendAlarms(ax_platform *cloud, ax_deviceID *device, ax_alarm *alarms[], int numAlarms) {
+
+return ax_platform_send(cloud, device, NULL, 0, alarms, numAlarms, NULL, 0, NULL, 0);
+
+}
+/**************************************************************************************/
+/*ax_platform_sendEvents()                                                            */
+/*                                                                                    */
+/*This function immedately sends any events included in the parameter to the platform */
+/*listed. It makes use of the ax_platform_send() function as a special case.          */
+/*                                                                                    */
+/*cloud(required) : a pointer to a structure that contains the cloud service info     */
+/*device(required) : a pointer to a structure that contains device information        */
+/*events(required) : a pointer to an array of ax_event pointers that will be sent     */
+/*                                                                                     */
+/*Returns: an integer value indicating whether or not it successfully sent the data    */
+/**************************************************************************************/
+int ax_platform_sendEvents(ax_platform *cloud, ax_deviceID *device, ax_event *events[], int numEvents) {
+
+return ax_platform_send(cloud, device, NULL, 0, NULL, 0, events, numEvents, NULL, 0);
+
+}
+/**************************************************************************************/
+/*ax_platform_sendLocations()                                                         */
+/*                                                                                    */
+/*This function immedately sends any events included in the parameter to the platform */
+/*listed. It makes use of the ax_platform_send() function as a special case.          */
+/*                                                                                    */
+/*cloud(required) : a pointer to a structure that contains the cloud service info     */
+/*device(required) : a pointer to a structure that contains device information        */
+/*locations(required) : a pointer to an array of ax_location poitners that will be sent*/
+/*                                                                                     */
+/*Returns: an integer value indicating whether or not it successfully sent the data    */
+/**************************************************************************************/
+int ax_platform_sendLocations(ax_platform *cloud, ax_deviceID *device, ax_location *locations[], int numLocations) {
+
+return ax_platform_send(cloud, device, NULL, 0, NULL, 0, NULL, 0, locations, numLocations);
+
+}
+
+/**************************************************************************************/
+/*ax_platform_setPkgStatus()                                                          */
+/*                                                                                    */
+/*Tells the platform what state the package is currently in. This is necessary for the*/
+/*lifecycle of the packages in the Axeda System.                                      */
+/*                                                                                    */
+/**************************************************************************************/
+int ax_platform_setPkgStatus(ax_platform *cloud, ax_deviceID *device, char *pkgID, int status, char *errorMsg) {
+  int retVal=AX_UNKNOWN;
+  char *url=NULL;
+  char *fullRequest=NULL;
+  cJSON *requestBody=NULL; 
+  HTTP_Headers myHeader;
+  HTTP_Transmission response;
+  zeroOutHTTPXmission(&response);
+ 
+  requestBody= getPKGStatusJSON(status, errorMsg, AX_NO_PRIORITY, 0, terse_enable);  
+
+  myHeader.contentType=getGeneralType(myHeader.contentType);
+  url=getPackageUpdateResource(url, device, pkgID);
+  int urlLength=strlen(url);
+  url[urlLength]='\0'; 
+ 
+  fullRequest=cJSON_PrintUnformatted(requestBody);
+  int contentLength=strlen(fullRequest);
+//Send the status
+  retVal=http_send_put(url, cloud->hostname, cloud->port, cloud->secure, &myHeader, fullRequest, contentLength, &response);
+//Scan the response and act if there are egress messages.
+  handleResponse(&response);
+
+//Clean up after ourselves
+  free(fullRequest);
+  free(url);
+  cJSON_Delete(requestBody); 
+
+  return retVal;  
+  }
+/**************************************************************************************/
+/*ax_platform_setPkgStatus()                                                          */
+/*                                                                                    */
+/*Tells the platform what state the package is currently in. This is necessary for the*/
+/*lifecycle of the packages in the Axeda System.                                      */
+/*                                                                                    */
+/**************************************************************************************/
+int ax_platform_setPkgStatusStarted(ax_platform *cloud, ax_deviceID *device, char *pkgID) {
+  return ax_platform_setPkgStatus(cloud, device, pkgID, AX_PKG_STARTED, NULL);
+  }
+/**************************************************************************************/
+/*ax_platform_setPkgStatus()                                                          */
+/*                                                                                    */
+/*Tells the platform what state the package is currently in. This is necessary for the*/
+/*lifecycle of the packages in the Axeda System.                                      */
+/*                                                                                    */
+/**************************************************************************************/
+int ax_platform_setPkgStatusQueued(ax_platform *cloud, ax_deviceID *device, char *pkgID) {
+  return ax_platform_setPkgStatus(cloud, device, pkgID, AX_PKG_QUEUED, NULL);
+  }
+/**************************************************************************************/
+/*ax_platform_setPkgStatus()                                                          */
+/*                                                                                    */
+/*Tells the platform what state the package is currently in. This is necessary for the*/
+/*lifecycle of the packages in the Axeda System.                                      */
+/*                                                                                    */
+/**************************************************************************************/
+int ax_platform_setPkgStatusSuccess(ax_platform *cloud, ax_deviceID *device, char *pkgID) {
+  return ax_platform_setPkgStatus(cloud, device, pkgID, AX_PKG_SUCCESS, NULL);
+  }
+/**************************************************************************************/
+/*ax_platform_setPkgStatus()                                                          */
+/*                                                                                    */
+/*Tells the platform what state the package is currently in. This is necessary for the*/
+/*lifecycle of the packages in the Axeda System.                                      */
+/*                                                                                    */
+/**************************************************************************************/
+int ax_platform_setPkgStatusFailed(ax_platform *cloud, ax_deviceID *device, char *pkgID, char *errorMsg) {
+  return ax_platform_setPkgStatus(cloud, device, pkgID, AX_PKG_FAILURE, errorMsg);
+  }
+
+/***************************************************************************************************************************************************************/
+/***************************************************************************************************************************************************************/
+/*The code in the following sections should never need to be called as it will be called from the preceeding functions.                                        */
+/***************************************************************************************************************************************************************/
+/***************************************************************************************************************************************************************/
+
+/************************************************************************************/
+/* The following methods construct references to endpoints on the platform where the */
+/*respective data will be sent. For the sake of convenience they all return a pointer*/
+/*that is equal in value to that which was passed in.                                */
+/* They all take the same arguments:                                                 */
+/*                                                                                   */
+/* endPointBuff = The pointer to where the endpoint will be stored. This pointer    */
+/*                will be  allocated and populated by the function                   */
+/* ax_deviceID = A structure that contains the model and serial number of the current*/
+/*               device. This structure should be the same as the one constructed at */
+/*               startup based on the arguments.                                     */
+/************************************************************************************/
+char *getDataResource(char *endPointBuff, ax_deviceID *device)
+   {
+   return getResource(endPointBuff, device, END_DATA);
+   }
+
+char *getAgentResource(char *endPointBuff, ax_deviceID *device)
+   {
+   return getResource(endPointBuff, device, END_AGENTS);
+   }
+
+char *getFileResource(char *endPointBuff, ax_deviceID *device)
+   {
+   return getResource(endPointBuff, device, END_FILES);
+   }
+
+char *getRegistrationResource(char *endPointBuff, ax_deviceID *device)
+   {
+   return getResource(endPointBuff, device, END_REG);
+   }
+char *getFileDownloadResource(char *endPointBuff, ax_deviceID *device, char *fileID) {
+    //since this url is slightly different from all the others we need to override the standard passing.
+    char *tempBuff=NULL;
+    tempBuff=encodeAgentID(device, tempBuff);
+    int len=strlen(endpoints[END_BASE])+strlen(endpoints[END_PACKAGES])+6+strlen(tempBuff)+strlen(fileID)+strlen(endpoints[END_FILES])+1+strlen(PROTOCOL_VERSION)+1;
+    endPointBuff = (char *)calloc(len,sizeof(char));
+    // /ammp/packages/1/files/test_model!test_serial/12345
+    // , URL_SEP, endpoints[END_BASE], URL_SEP, endpoints[END_FILED], URL_SEP, PROTOCOL_VERSION, URL_SEP, endpoints[END_FILES], URL_SEP, tempBuff=encodeAgentID(device, tempBuff), URL_SEP, fileID
+    // %c%s%c%s%c%s%c%s%c%s%c%s
+    
+    snprintf(endPointBuff, len, "%c%s%c%s%c%s%c%s%c%s%c%s", URL_SEP, endpoints[END_BASE], URL_SEP, endpoints[END_PACKAGES], URL_SEP, PROTOCOL_VERSION, URL_SEP, endpoints[END_FILES], URL_SEP, tempBuff, URL_SEP, fileID );
+    free(tempBuff);
+
+    return endPointBuff;
+}
+
+char *getPackageUpdateResource(char *endPointBuff, ax_deviceID *device, char *packageID){
+    //since this url is slightly different from all the others we need to override the standard passing.
+    // "/ammp/packages/1/12345/status/test_model!test_serial"
+    char *tempBuff=NULL;
+    tempBuff=encodeAgentID(device, tempBuff);
+    int len=6+strlen(endpoints[END_BASE])+strlen(endpoints[END_PACKAGES])+strlen(PROTOCOL_VERSION)+strlen(packageID)+strlen(endpoints[END_STATUS])+strlen(tempBuff)+1;
+        endPointBuff = (char *)calloc(len, sizeof(char));
+    
+        snprintf(endPointBuff, len, "%c%s%c%s%c%s%c%s%c%s%c%s", URL_SEP, endpoints[END_BASE], URL_SEP, endpoints[END_PACKAGES], URL_SEP, PROTOCOL_VERSION, URL_SEP, packageID, URL_SEP, endpoints[END_STATUS], URL_SEP, tempBuff);
+        free(tempBuff);
+
+        return endPointBuff;
+    }
+
+char *getResource(char *endPointBuff, ax_deviceID *device, int resourceType)
+  {
+//Check for bounds on the endpoints array. END_REG is currently the highest, if it's OOB return the agent default URL
+  int resType=END_AGENTS;
+  if((resourceType<0)||(resourceType>END_REG)) { resType=END_AGENTS; }
+  else { resType=resourceType; }
+  int len = strlen(endpoints[END_BASE])+strlen(endpoints[resType])+6+strlen(device->model)+strlen(device->serial);
+       endPointBuff = (char *)calloc(len+1, sizeof(char));
+        char *tempBuff=NULL;
+         switch(resType) {
+            case END_REG:
+              snprintf(endPointBuff, len+1, "%c%s%c%s%c%s", URL_SEP, endpoints[END_BASE], URL_SEP, endpoints[END_AGENTS], URL_SEP, PROTOCOL_VERSION);
+            break;
+            case END_FILED:
+            
+            break;
+            default:
+             snprintf(endPointBuff, len+1, "%c%s%c%s%c%s%c%s", URL_SEP, endpoints[END_BASE], URL_SEP, endpoints[resType], URL_SEP, PROTOCOL_VERSION, URL_SEP, tempBuff=encodeAgentID(device, tempBuff));
+             free(tempBuff); //only used for storing the deviceID it's work is done now.
+            break;
+         }
+
+        return endPointBuff;
+  }
+
+
+/**************************************************************************************/
+/*encodeAgentID()                                                                     */
+/*                                                                                    */
+/*A utility method to convert the ax_deviceID structure into a protocol compliant     */
+/*identifier string. Used mostly by the registration message                          */
+/*                                                                                    */
+/*device(required) : a pointer to a structure that contains the model, serial, tenant*/
+/*buff(required) : a pointer to a char array where the output will be stored.         */
+/*                                                                                    */
+/*Returns: a pointer to the char array, same as *buff                                 */
+/**************************************************************************************/
+char* encodeAgentID(ax_deviceID *device, char *buff)
+ {
+    //TODO: Add logic to URL Encode output string if special characters are encountered...
+ int len=0;
+if(strlen(device->tenant)>0)
+    {
+    len=strlen(device->model)+strlen(device->serial)+strlen(device->tenant)+4;
+    buff = (char *)calloc(len, sizeof(char));
+    //buff[len]='\0';
+    snprintf(buff, len, "%s@%s!%s", device->model, device->tenant, device->serial );
+    }
+ else
+    {
+    len=strlen(device->model)+strlen(device->serial)+3;
+    buff = (char *)calloc(len, sizeof(char));
+    //buff[len]='\0';
+    snprintf(buff,len, "%s!%s", device->model, device->serial);
+    }
+
+    printDebug(buff);
+
+ return buff;
+ }
+
+/**************************************************************************************/
+/*getContentType()                                                                    */
+/*                                                                                    */
+/*A convenience method to allow different messages to have different media types. The */
+/*media types are set forth in the protocol specification. The function will return a */
+/*a pointer to a declared string, for which you do not need to free.                   */
+/*                                                                                    */
+/*                                                                                    */
+/*                                                                                    */
+/**************************************************************************************/
+
+char *getContentType(char *contentBuff, int type) {
+    switch(type) {
+        case 0:
+          contentBuff=(char *)contentTypes[MIME_JSON];
+          break;
+        default:
+          contentBuff=(char *)contentTypes[MIME_JSON];
+          break;
+        }
+    return contentBuff;
+}
+
+char *getGeneralType(char *contentBuff) {
+    return getContentType(contentBuff, TRANS_TYPE_JSON);
+    }
+
+/**************************************************************************************/
+/*int handleResponse()                                                                */
+/*                                                                                    */
+/*A catch-all method that will handle any egress messages sent from the platform back */
+/*to the device. If the JSON returned is valid it will dispatch the requests as needed*/
+/*                                                                                    */
+/*                                                                                    */
+/*                                                                                    */
+/**************************************************************************************/
+//Possible returns:
+// AX_EGR_JSON_PARSE_FAIL -- A failure to parse the JSON that was returned
+
+int handleResponse(HTTP_Transmission *response){
+    cJSON *resp_int, *temp, *temp_child, *instructions, *instruction_child;
+    int retVal=AX_OK;
+    if(response->body==NULL) {return AX_OK;} //if there's no body, there's nothing to do here.
+
+    resp_int=cJSON_Parse(response->body);
+    if(resp_int==NULL) {
+        return AX_GEN_PARSE_ERR;
+        }
+  printDebug("Creating software packages\n");
+    temp=cJSON_GetObjectItem(resp_int, "packages");
+    if(temp!=NULL) {
+        //Handle a software package here
+        int ctr=0, pkg_ctr2=0;
+        int sz=cJSON_GetArraySize(temp);
+        int instructionCt=0;
+        ax_package *temp_package;
+
+        for(ctr=0; ctr<sz; ctr++)
+            {
+            printDebug("Parsing package[]...\n");
+            temp_child=cJSON_GetArrayItem(temp, ctr);
+			//Create an ax_package for this
+            temp_package=(ax_package *)malloc(sizeof(ax_package));
+			char *pkgID=cJSON_GetObjectItem(temp_child, "id")->valuestring;
+			retVal=ax_pkg_createPackage(temp_package, pkgID, 0, AX_NO_PRIORITY);         
+            temp_package->priority=cJSON_GetObjectItem(temp_child, "priority")->valueint;
+            temp_package->time=cJSON_GetObjectItem(temp_child, "time")->valueint;
+			//add Instructions to the package
+            instructions=cJSON_GetObjectItem(temp_child, "instructions");
+            instructionCt=cJSON_GetArraySize(instructions);
+                for(pkg_ctr2=0; pkg_ctr2<instructionCt; pkg_ctr2++)
+                  {
+		  		  //int instrType=-1;
+                  printDebug("Parsing instruction\n");
+                  instruction_child=cJSON_GetArrayItem(instructions, pkg_ctr2);
+				                    
+                  if(strcmp("down", cJSON_GetObjectItem(instruction_child, "@type")->valuestring)==0) {
+				      cJSON *id=cJSON_GetObjectItem(instruction_child, "id");
+				      char *s_id = NULL;
+				      if(id != NULL){
+						s_id=id->valuestring;
+					  }
+					  char *s_path = NULL;
+					  //path is optional so we need to make sure it is here before trying to get the valuestring
+					  cJSON *path = cJSON_GetObjectItem(instruction_child, "fp");
+					  if(path != NULL){
+		  				s_path=path->valuestring;
+					  }
+					  else{
+						  path = cJSON_GetObjectItem(instruction_child, "path");
+						  if(path != NULL){
+		  					s_path=path->valuestring;
+						  }
+					  }
+					  char *s_filename = NULL;
+					  cJSON *fn = cJSON_GetObjectItem(instruction_child, "fn");
+					  if(fn != NULL){
+		       				s_filename=fn->valuestring;
+					  }
+					  else{
+						  path = cJSON_GetObjectItem(instruction_child, "filename");
+						  if(path != NULL){
+		  					s_path=path->valuestring;
+						  }
+					  }
+                      retVal=ax_pkg_addDLInstruction(temp_package, s_id, s_path, s_filename); 
+                      }
+				  //if it's not a download instruction, we don't care; down was the only one available in AMMP1.0
+                  }
+			//Now we tell someone that we've got a software package
+			scm_download_req(temp_package); 
+            }
+        }
+    int dictr1=0;
+    //Set Data Item Commands will occur here.
+     temp=NULL;
+     temp_child=NULL;
+     instructions=NULL;
+     instruction_child=NULL;
+
+     printDebug("Setting Data Item Commands\n");
+    temp=cJSON_GetObjectItem(resp_int, "set");
+    if(temp!=NULL){
+        int disz=cJSON_GetArraySize(temp);
+        for(dictr1=0; dictr1<disz; dictr1++)
+             {
+             temp_child=cJSON_GetArrayItem(temp, dictr1);
+             instructions=cJSON_GetObjectItem(temp_child, "dataItems");
+             instruction_child=instructions->child;
+             while(instruction_child!=NULL){
+                 if(instruction_child->type==cJSON_String) {
+                     data_item_write(instruction_child->string, instruction_child->valuestring, 0, AX_STRING);
+                     }
+                 else if(instruction_child->type==cJSON_False) {
+                     data_item_write(instruction_child->string, NULL, AX_FALSE, AX_DIGITAL);
+                     }
+                 else if(instruction_child->type==cJSON_True) {
+                     data_item_write(instruction_child->string, NULL, AX_TRUE, AX_DIGITAL);
+                     }
+                 else {
+                     data_item_write(instruction_child->string, NULL, instruction_child->valuedouble, AX_ANALOG);
+                     }
+                 instruction_child=instruction_child->next;
+                 }
+
+             }
+         }
+     //Data Item Poll requests here.
+    printDebug("Setting Data Item Poll requests\n");
+    int dictr2=0;
+    temp=NULL;
+    temp_child=NULL;
+    instructions=NULL;
+    instruction_child=NULL;
+     temp=cJSON_GetObjectItem(resp_int, "send");
+     if(temp!=NULL){
+        int dissz=cJSON_GetArraySize(temp);
+        for(dictr2=0; dictr2<dissz; dictr2++) {
+            temp_child=cJSON_GetArrayItem(temp, dictr2);
+            instructions=cJSON_GetObjectItem(temp_child, "dataItems");
+            instruction_child=instructions->child;
+            while(instruction_child!=NULL){
+                data_item_request(instruction_child->valuestring);
+                instruction_child=instruction_child->next;
+                }
+            }
+        }
+
+    cJSON_Delete(resp_int);//dispose of the cJSON object
+    free(response->body);
+
+
+  return retVal;
+  }
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axPlatform.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,77 @@
+/************************************************************************************/
+/* axPlatform.h                                                                    */
+/* 2013 Axeda Corporation                                                          */
+/*                                                                                  */
+/* Defines methods for interaction with end points on the Axeda Platform.  This     */
+/* is a device independent implementation which can be applied to any device that   */
+/* supports ANSI C.                                                                 */
+/************************************************************************************/
+
+#ifndef AXPLATFORM_H
+#define AXPLATFORM_H
+
+#include "axTypes.h"
+#include <stdio.h>
+
+
+#define END_BASE     0
+#define END_DATA     1
+#define END_AGENTS   2
+#define END_FILES    3
+#define END_FILED    4
+#define END_PACKAGES 5
+#define END_STATUS   6
+#define END_REG	     7
+
+#define BUFF_ALARMS     0
+#define BUFF_EVENTS     1
+#define BUFF_DATA       2
+#define BUFF_LOCATIONS  3
+
+#define PKG_QUEUED      0
+#define PKG_STARTED     1
+#define PKG_SUCCESS     2
+#define PKG_FAILURE     3
+
+#ifdef __cplusplus 
+  extern "C" {
+#endif
+
+
+int ax_platform_send(ax_platform *cloud, ax_deviceID *device, ax_dataSet *dataItems[], int numDataSets, ax_alarm *alarms[], int numAlarms, ax_event *events[], int numEvents, ax_location *locations[], int numLocations);
+int ax_platform_sendData(ax_platform *cloud, ax_deviceID *device, ax_dataSet *dataItems[], int numDataSets);
+int ax_platform_sendAlarms(ax_platform *cloud, ax_deviceID *device, ax_alarm *alarms[], int numAlarms);
+int ax_platform_sendEvents(ax_platform *cloud, ax_deviceID *device, ax_event *events[], int numEvents);
+int ax_platform_sendLocations(ax_platform *cloud, ax_deviceID *device, ax_location *locations[], int numLocations);
+
+int ax_platform_register(ax_platform *cloud, ax_deviceID *device);
+int ax_platform_ping(ax_platform *cloud, ax_deviceID *device);
+int ax_platform_upload(ax_platform *cloud, ax_deviceID *device, ax_file *file);
+int ax_platform_download(ax_platform *cloud, ax_deviceID *device, ax_package_instruction *instr);
+
+int ax_platform_setPkgStatus(ax_platform *cloud, ax_deviceID *device, char *pkgID, int status, char *errorMsg);
+int ax_platform_setPkgStatusStarted(ax_platform *cloud, ax_deviceID *device, char *pkgID);
+int ax_platform_setPkgStatusQueued(ax_platform *cloud, ax_deviceID *device, char *pkgID);
+int ax_platform_setPkgStatusSuccess(ax_platform *cloud, ax_deviceID *device, char *pkgID);
+int ax_platform_setPkgStatusFailed(ax_platform *cloud, ax_deviceID *device, char *pkgID, char *errorMsg);
+/*******************************************************************************************************/
+/*The following functions are internal only and should never need to be called outside of this library*/
+/*****************************************************************************************************/
+char *encodeAgentID(ax_deviceID *device, char *buff);
+char *getResource(char *endPointBuff, ax_deviceID *device, int resourceType);
+char *getDataResource(char *endPointBuff, ax_deviceID *device);
+char *getAgentResource(char *endPointBuff, ax_deviceID *device);
+char *getFileResource(char *endPointBuff, ax_deviceID *device);
+char *getRegistrationResource(char *endPointBuff, ax_deviceID *device);
+char *getFileDownloadResource(char *endPointBuff, ax_deviceID *device, char *fileID);
+char *getPackageUpdateResource(char *endPointBuff, ax_deviceID *device, char *packageID);
+
+char *getContentType(char *contentBuff, int type);
+char *getGeneralType(char *contentBuff);
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axSerializer.c	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,374 @@
+/************************************************************************************/
+/* axSerializer.c                                                                   */
+/* �2013 Axeda Corporation                                                          */
+/*                                                                                  */
+/* Defines methods for transposing the data in domain objects into a serialized     */
+/* encoding for transmission to the Platform Endpoint. This is a platform independent*/
+/* implementation that can be applied to any device that supports ANSI C.           */
+/*                                                                                  */
+/************************************************************************************/
+#include "axSerializer.h"
+
+#define REG_ID              0
+#define REG_MODEL_NUMBER    1
+#define REG_SERIAL_NUMBER   2
+#define REG_TENANT          3
+#define REG_PING            4
+#define REG_KEY             5
+
+#define DATA_ALERTS 0
+#define DATA_EVENTS 1
+#define DATA_DATA 2
+#define DATA_LOCATIONS 3
+#define DATA_ITEMS 4
+
+#define PKG_STATUS  0
+#define PKG_ERROR   1
+
+#define ALERT_SEV 0
+#define ALERT_CAUSE 1
+#define ALERT_REASON 2
+
+#define COM_TIME 0
+#define COM_PRIORITY 1
+#define COM_NAME 2
+#define COM_DESCRIPTION 3
+
+//#define EVENT_NAME 1
+//#define EVENT_DESC 0
+
+#define LOC_LAT 0
+#define LOC_LON 1
+#define LOC_ALT 2
+
+int terse_enable=AX_TRUE;
+
+const char *commonKW[]= {"time", "priority", "name", "description" };
+const char *dataKW[]={ "alerts", "events", "data", "locations", "dataItems"};
+const char *registrationKW[]=  {"id", "mn", "sn", "tn", "pingRate", "key"};
+const char *alertKW[]= {"severity","cause", "reason" };
+const char *locationKW[]= {"latitude", "longitude", "altitude"};
+const char *pkgStatusKW[]={"status", "error"};
+
+const char *terse_commonKW[]= {"t", "dp", "n", "d"};
+const char *terse_dataKW[]={ "alerts", "events", "data", "locations", "di"};
+const char *terse_registrationKW[]=  {"id", "mn", "sn", "tn", "pr", "k"};
+const char *terse_alertKW[]= {"sv", "cn", "cr" };
+const char *terse_locationKW[]= {"lat", "lon", "alt"};
+const char *terse_pkgStatusKW[]={"st", "err"};
+/*************************************************************************************************/
+/*dataSet2JSON()                                                                                 */
+/*                                                                                               */
+/*Takes an array of ax_dataItem structs and creates JSON to represent them in the AMMP-json      */
+/*protocol. The return type will be a CJSON pointer which can then be rendered into an actual    */
+/*JSON string. The JSON pointer will need to be free'd when you're done. If there are zero data  */
+/*items in the array then this method will return an empty JSON array pointer.                   */
+/*                                                                                               */
+/*NOTE: If any array spots are left empty they will only be ignored if theyre are NULL. Junk     */
+/*      values from left over data will cause seg faults. NULL your empty pointers!!!            */
+/*************************************************************************************************/
+//takes data item set, not longer just data item structs.
+cJSON *dataSet2JSON(ax_dataSet *di[], int len, int terse_on)
+ {
+ cJSON *root, *child_obj, *dis=NULL;
+  int ctr=0;
+
+  ax_dataSet *curr=NULL;
+  ax_dataNode *currNode;
+  root=cJSON_CreateArray();
+  
+  for(ctr=0; ctr<len; ctr++)
+      {
+      curr=di[ctr];
+      if((curr!=NULL)&&(curr->dataNode_ct>0)) //If this set doesn't have any data in it, don't include it.
+          {
+          cJSON_AddItemToArray(root, child_obj=cJSON_CreateObject());
+	if(terse_on==AX_TRUE) {
+	      cJSON_AddItemToObject(child_obj, terse_dataKW[DATA_ITEMS], dis=cJSON_CreateObject());  }
+	else {
+	     cJSON_AddItemToObject(child_obj, dataKW[DATA_ITEMS], dis=cJSON_CreateObject());  
+	     }
+	if((curr->acquisitionTime>=0)&&(curr->acquisitionTime)){
+             if(terse_on==AX_TRUE){
+                 cJSON_AddNumberToObject(child_obj, terse_commonKW[COM_TIME], curr->acquisitionTime);
+                 }
+             else {
+                  cJSON_AddNumberToObject(child_obj, commonKW[COM_TIME], curr->acquisitionTime);
+                  }
+             }
+          if((curr->priority>=1)&&(curr->priority<=100)) {
+                 if(terse_on==AX_TRUE){
+                     cJSON_AddNumberToObject(child_obj, terse_commonKW[COM_PRIORITY], curr->priority);
+                     }
+                 else {
+                     cJSON_AddNumberToObject(child_obj, commonKW[COM_PRIORITY], curr->priority);
+                     }
+             }
+          currNode=curr->data_first;
+          while(currNode!=NULL)
+              {
+              if(currNode->type==AX_DIGITAL)    {
+                  if(currNode->dValue==1)        {
+                      cJSON_AddTrueToObject(dis, currNode->name);
+                      }
+                  else {
+                      cJSON_AddFalseToObject(dis, currNode->name);
+                      }
+                  }
+                  else if(currNode->type==AX_ANALOG)  {
+                      cJSON_AddNumberToObject(dis, currNode->name, currNode->dValue);
+                      }
+                  else    {
+                      cJSON_AddStringToObject(dis, currNode->name, currNode->sValue);
+                      }
+              //advance to the next data item
+              currNode=currNode->next;
+              }
+          }
+      }
+ return root;
+ }
+/*************************************************************************************************/
+/* eventsToJSON()                                                                                */
+/*                                                                                               */
+/*Takes an array of ax_event struct pointers and creates JSON to represent them in the AMMP-json */
+/*protocol. The return type will be a CJSON pointer which can then be rendered into an actual    */
+/*JSON string. The JSON pointer will need to be free'd when you're done. If there are zero data  */
+/*items in the array then this method will return an empty JSON array pointer.                   */
+/*                                                                                               */
+/*NOTE: If any array spots are left empty they will only be ignored if theyre are NULL. Junk     */
+/*      values from left over data will cause seg faults. NULL your empty pointers!!!            */
+/*************************************************************************************************/
+cJSON *eventsToJSON(ax_event *events[], int len, int terse_on) {
+    cJSON *root, *dis;
+    ax_event *curr;
+    int ctr=0;
+    root=cJSON_CreateArray();
+    curr=events[0];
+     for(ctr=0; ctr<len; ctr++)
+          {
+		  curr=events[ctr];
+          if(curr!=NULL)
+            {
+                cJSON_AddItemToArray(root, dis=cJSON_CreateObject());
+				if(terse_on==AX_TRUE){
+	                cJSON_AddStringToObject(dis, terse_commonKW[COM_NAME], curr->name);
+					cJSON_AddStringToObject(dis, terse_commonKW[COM_DESCRIPTION], curr->description);
+					}
+				else {
+					cJSON_AddStringToObject(dis, commonKW[COM_NAME], curr->name);
+					cJSON_AddStringToObject(dis, commonKW[COM_DESCRIPTION], curr->description); 
+					}
+                
+                if((curr->dateAcquired>=0)&&(curr->dateAcquired)){
+                    if(terse_on==AX_TRUE){
+                         cJSON_AddNumberToObject(dis, terse_commonKW[COM_TIME], curr->dateAcquired);
+                         }
+                    else {
+                         cJSON_AddNumberToObject(dis, commonKW[COM_TIME], curr->dateAcquired);
+                         }
+                     }
+                if((curr->priority>=1)&&(curr->priority<=100)&&(curr->priority)) {
+                     if(terse_on==AX_TRUE){
+                         cJSON_AddNumberToObject(dis, terse_commonKW[COM_PRIORITY], curr->priority);
+                         }
+                     else {
+                          cJSON_AddNumberToObject(dis, commonKW[COM_PRIORITY], curr->priority);
+                          }
+                   }
+                }
+          }
+
+    return root;
+}
+/*************************************************************************************************/
+/* locationsToJSON()                                                                             */
+/*                                                                                               */
+/*Takes an array of ax_location struct pointers and creates JSON to represent them in the AMMP   */
+/*protocol. The return type will be a CJSON pointer which can then be rendered into an actual    */
+/*JSON string. The JSON pointer will need to be free'd when you're done. If there are zero data  */
+/*items in the array then this method will return an empty JSON array pointer.                   */
+/*                                                                                               */
+/*NOTE: If any array spots are left empty they will only be ignored if theyre are NULL. Junk     */
+/*      values from left over data will cause seg faults. NULL your empty pointers!!!            */
+/*************************************************************************************************/
+cJSON *locationsToJSON(ax_location *locations[], int len, int terse_on) {
+    cJSON *root, *dis;
+    ax_location *curr;
+    int ctr=0;
+    root=cJSON_CreateArray();
+        for(ctr=0; ctr<len; ctr++)
+              {
+              curr=locations[ctr];
+                  if(curr)
+                    {
+                    cJSON_AddItemToArray(root, dis=cJSON_CreateObject());
+                    if(terse_on==AX_TRUE){
+                        cJSON_AddNumberToObject(dis, terse_locationKW[LOC_LAT], curr->latitude);
+                        cJSON_AddNumberToObject(dis, terse_locationKW[LOC_LON], curr->longitude);
+                        cJSON_AddNumberToObject(dis, terse_locationKW[LOC_ALT], curr->altitude);
+                        }
+                    else {
+                        cJSON_AddNumberToObject(dis, locationKW[LOC_LAT], curr->latitude);
+                        cJSON_AddNumberToObject(dis, locationKW[LOC_LON], curr->longitude);
+                        cJSON_AddNumberToObject(dis, locationKW[LOC_ALT], curr->altitude);
+                        }
+                    if((curr->dateAcquired)&&(curr->dateAcquired>=0)){
+                        if(terse_on==AX_TRUE){
+                            cJSON_AddNumberToObject(dis, terse_commonKW[COM_TIME], curr->dateAcquired);
+                            }
+                        else {
+                            cJSON_AddNumberToObject(dis, commonKW[COM_TIME], curr->dateAcquired);
+                            }
+                        }
+                    if((curr->priority>=1)&&(curr->priority<=100)&&(curr->priority)) {
+                        if(terse_on==AX_TRUE){
+                            cJSON_AddNumberToObject(dis, terse_commonKW[COM_PRIORITY], curr->priority);
+                            }
+                        else {
+                            cJSON_AddNumberToObject(dis, commonKW[COM_PRIORITY], curr->priority);
+                            }
+                        }
+                    }
+              }
+
+    return root;
+}
+
+/*************************************************************************************************/
+/* alarmsToJSON()                                                                                */
+/*                                                                                               */
+/*Takes an array of ax_alarm struct pointers and creates JSON to represent them in the AMMP-json */
+/*protocol. The return type will be a CJSON pointer which can then be rendered into an actual    */
+/*JSON string. The JSON pointer will need to be free'd when you're done. If there are zero data  */
+/*items in the array then this method will return an empty JSON array pointer.                   */
+/*                                                                                               */
+/*NOTE: If any array spots are left empty they will only be ignored if theyre are NULL. Junk     */
+/*      values from left over data will cause seg faults. NULL your empty pointers!!!            */
+/*************************************************************************************************/
+cJSON *AlarmsToJSON(ax_alarm *alarms[], int len, int terse_on)
+  {
+  cJSON *root, *dis;
+     int ctr=0;
+     ax_alarm *curr;
+    root=cJSON_CreateArray();
+	curr=alarms[0];
+     for(ctr=0; ctr<len; ctr++) {
+      curr=alarms[ctr];
+      if(curr!=NULL)
+      {
+      cJSON_AddItemToArray(root, dis=cJSON_CreateObject());
+      if(terse_on==AX_TRUE){
+          cJSON_AddStringToObject(dis, terse_commonKW[COM_NAME], curr->alarmName);
+          cJSON_AddStringToObject(dis, terse_commonKW[COM_DESCRIPTION], curr->alarmDescription);
+          cJSON_AddNumberToObject(dis, terse_alertKW[ALERT_SEV], curr->alarmSeverity);
+          cJSON_AddStringToObject(dis, terse_alertKW[ALERT_CAUSE], curr->alarmCause);
+          cJSON_AddStringToObject(dis, terse_alertKW[ALERT_REASON], curr->alarmReason);
+          }
+      else {
+          cJSON_AddStringToObject(dis, commonKW[COM_NAME], curr->alarmName);
+          cJSON_AddStringToObject(dis, commonKW[COM_DESCRIPTION], curr->alarmDescription);
+          cJSON_AddNumberToObject(dis, alertKW[ALERT_SEV], curr->alarmSeverity);
+          cJSON_AddStringToObject(dis, alertKW[ALERT_CAUSE], curr->alarmCause);
+          cJSON_AddStringToObject(dis, alertKW[ALERT_REASON], curr->alarmReason);
+          }
+
+      if((curr->dateAcquired)&&(curr->dateAcquired>=0)){
+            if(terse_on==AX_TRUE){
+               cJSON_AddNumberToObject(dis, terse_commonKW[COM_TIME], curr->dateAcquired);
+               }
+            else {
+               cJSON_AddNumberToObject(dis, commonKW[COM_TIME], curr->dateAcquired);
+               }
+            }
+       if((curr->priority>=1)&&(curr->priority<=100)&&(curr->priority)) {
+            if(terse_on==AX_TRUE){
+               cJSON_AddNumberToObject(dis, terse_commonKW[COM_PRIORITY], curr->priority);
+               }
+            else {
+               cJSON_AddNumberToObject(dis, commonKW[COM_PRIORITY], curr->priority);
+               }
+           }
+        }
+     }
+
+    return root;
+  }
+/*************************************************************************************************/
+/* getRegistrationJSON()                                                                         */
+/*                                                                                               */
+/*Takes a device and ping rate and will return a cJSON structure that represents a registration  */
+/*message in the AMMP protocol.  								 */
+/*                                                                                               */
+/*************************************************************************************************/
+cJSON *getRegistrationJSON(ax_deviceID *device, int pingRate)
+  {
+   cJSON *root, *child;
+
+   root=cJSON_CreateObject();
+
+   cJSON_AddItemToObject(root, registrationKW[REG_ID], child=cJSON_CreateObject());
+   if(device->model) {
+	   cJSON_AddStringToObject(child, registrationKW[REG_MODEL_NUMBER], device->model);
+   	   }
+   if(device->serial) {
+	   cJSON_AddStringToObject(child, registrationKW[REG_SERIAL_NUMBER], device->serial);
+   	   }
+   if(strlen(device->tenant)>=1)
+    {
+    cJSON_AddStringToObject(child, registrationKW[REG_TENANT], device->tenant);
+    }
+   cJSON_AddNumberToObject(root, registrationKW[REG_PING], pingRate);
+
+
+  return root;
+  }
+
+//TODO: Fill out this description
+/*************************************************************************************************/
+/*getPKGStatusJSON()                                                                             */
+/*                                                                                               */
+/*                                                                                               */
+/*                                                                                               */
+/*************************************************************************************************/
+cJSON *getPKGStatusJSON(int status, char *error, int priority, int time, int terse_on){
+  cJSON *root;
+  root=cJSON_CreateObject();
+//STATUS
+  if(terse_on==AX_TRUE) {
+    cJSON_AddNumberToObject(root, pkgStatusKW[PKG_STATUS], status);
+	}
+  else {
+	cJSON_AddNumberToObject(root, pkgStatusKW[PKG_STATUS], status);
+	}
+//TIME
+   if(time>=0) {
+     if(terse_on==AX_TRUE) {
+		cJSON_AddNumberToObject(root, terse_commonKW[COM_TIME], time);
+		}
+	  else {
+		cJSON_AddNumberToObject(root, commonKW[COM_TIME], time);
+		}
+	  }
+//PRIORITY
+	if((priority>0)&&(priority<=100)) {
+	   if(terse_on==AX_TRUE) {
+		cJSON_AddNumberToObject(root, terse_commonKW[COM_PRIORITY], priority);
+		}
+	 else {
+		cJSON_AddNumberToObject(root, commonKW[COM_PRIORITY], priority);
+		}
+	  }
+//ERROR
+	if(error!=NULL) {
+	  if(terse_on==AX_TRUE) {
+		cJSON_AddStringToObject(root, terse_pkgStatusKW[PKG_ERROR], error);
+		}
+	  else {
+		cJSON_AddStringToObject(root, pkgStatusKW[PKG_ERROR], error);
+		}
+	}
+return root;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axSerializer.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,37 @@
+/************************************************************************************/
+/* axSerializer.c                                                                   */
+/* ©2013 Axeda Corporation                                                          */
+/*                                                                                  */
+/* Defines methods for transposing the data in domain objects into a serialized     */
+/* encoding for transmission to the Platform Endpoint. This is a platform independent*/
+/* implementation that can be applied to any device that supports ANSI C.           */
+/*                                                                                  */
+/************************************************************************************/
+
+#ifndef AXSERIALIZER_H
+#define AXSERIALIZER_H
+
+#include "cJSON.h"
+#include "axTypes.h"
+#include "axConstants.h"
+
+extern int terse_enable;
+
+#ifdef __cplusplus 
+  extern "C" {
+#endif
+
+
+cJSON *dataSet2JSON(ax_dataSet *di[], int len, int terse_on);
+cJSON *getRegistrationJSON(ax_deviceID *device, int pingRate);
+cJSON *getPKGStatusJSON(int status, char *error, int priority, int time, int terse_on);
+cJSON *AlarmsToJSON(ax_alarm *alarms[], int len, int terse_on);
+cJSON *eventsToJSON(ax_event *events[], int len, int terse_on);
+cJSON *locationsToJSON(ax_location *locations[], int len, int terse_on);
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axSettings.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,47 @@
+#ifndef _AXSETTINGS_H_
+#define _AXSETTINGS_H_
+
+/************************************************************************************/
+/* axSettings.h                                                                     */
+/* ©2013 Axeda Corporation                                                          */
+/*                                                                                  */
+/*This file defines default settings for the domain objects. Most of the time they  */
+/*define how many characters will be allocated to a particular field. The library   */
+/*automatically truncate strings that are too long and will return an error code.   */
+/************************************************************************************/
+
+//define max string sizes for certain structs
+#define AX_ALM_NAME_S 50				//Defines how many characters are allocated for an alarm name           (ax_alarm.alarmName)
+#define AX_ALM_DESC_S 50        //Defines how many characters are allocated for an alarm description    (ax_alarm.alarmDescription)
+#define AX_ALM_CAUSE_S 50       //Defines how many characters are allocated for an alarm cause          (ax_alarm.alarmCause)
+#define AX_ALM_REAS_S 50        //Defines how many characters are allocated for an alarm reason         (ax_alarm.alarmReason)
+
+#define AX_DN_NAME_S  50        //Defines how many characters are allocated for a dataNode name         (ax_dataNode.name)
+#define AX_DN_SV_S    50        //Defines how many characters are allocated for a dataNode string       (ax_datanode.sValue)
+
+#define AX_EVT_NAME_S 50        //Defines how many characters are allocated for an event name           (ax_event.name)
+#define AX_EVT_DESC_S 50        //Defines how many characters are allocated for an devent description   (ax_event.description)
+
+#define AX_MSID_DID_S 0         //Defines how many characters are allocated for a device ID             (ax_deviceID.deviceId)
+#define AX_MSID_MDL_S 50        //Defines how many characters are allocated for a model number          (ax_deviceID.model)
+#define AX_MSID_SER_S 50        //Defines how many characters are allocated for a serial number         (ax_deviceID.serial)
+#define AX_MSID_TEN_S 50        //Defines how many characters are allocated for a tenant ID             (ax_deviceID.tenant)
+
+#define AX_FILE_NAME_S 50      //Defines how many characters are allocated for a file name              (ax_file.name)
+#define AX_FILE_HINT_S 50      //Defines how many characters are allocated for a file hint              (ax_file.hint)
+#define AX_FILE_ID_S   50      //Defines how many characters are allocated for a file ID                (ax_file.fileID)
+                                                                                                       
+#define AX_PLAT_HOST_S 50      //Defines how many characters are allocated for a platform hostname      (ax_platform.hostname)
+
+#define AX_PKGI_FID_S   50     //Defines how many characters are allocated for a package file ID        (ax_package_instruction.file_id)
+#define AX_PKGI_PATH_S  50     //Defines how many characters are allocated for a package path           (ax_package_instruction.path)
+#define AX_PKGI_FNAME_S 50     //Defines how many characters are allocated for a package file name      (ax_package_instruction.filename)
+
+#define AX_PKG_ID_S   50       //Defines how many characters are allocated for a package ID             (ax_package.packageID)
+
+#define AX_NET_BUFF_S 1024     //Defines how many characters are allocated for an HTTP response         (ax_alarm.alarmReason)
+
+
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axStatusCodes.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,37 @@
+
+#ifndef _AXSTATUSCODES_H_
+#define _AXSTATUSCODES_H_
+
+#define AX_ERROR -1                         //A General Error has occurred
+#define AX_OK 0                             //Operation Success
+#define AX_UNKNOWN -42                      //An unknown error has occurred. Functions are normally initialized to return this value. The function should change it before returning it.
+#define AX_ARGNULL -999                     //An argument passed to the function was left null
+#define AX_OUT_OF_RANGE -998                //An argument passed to the function was out of range
+#define AX_DI_UNKNOWN_TYPE -997             //An argument passed to the function indicating the Data Item type was incorrect
+#define AX_CONFLICTING_ARG -996             //Two arguments passed to a function describing the same object have conflicting values e.g. arg1(string)=="fubar"; arg2(strlength)==0, The string fubar is obviously longer than 0
+#define AX_ARG_EMPTY  -995		    //An argument passed to the function was a string but it was empty eg mystring="". This will be thrown when a value is required. 
+
+#define AX_NET_ERR_UNABLE_TO_CONNECT  700       //Call to open the TCP port to IP or hostname has failed. Is the port open? IP contactable?
+#define AX_NET_ERR_PORT_WRITE         701       //The program was unable to write data onto the socket.
+#define AX_NET_ERR_HEADER_WRITE       702       //a write error occurred while writing the HTTP headers
+#define AX_NET_ERR_DATA_WRITE         703       //a write error occurred while writing the HTTP data/body
+#define AX_NET_ERR_TIMEOUT            704       //A timeout occurred while waiting for a response.
+#define AX_NET_PORT_INVALID           705       //The port that was specified is larger than 65536 or less than 0
+#define AX_NET_DNS_ERROR	      706	//There was an error resolving the name of the platform server
+#define AX_NET_MORE_WAITING	      707	//Thrown when the data written to the port does not equal the amount of data that was requested to be written.
+
+#define AX_GEN_STR_TRUNC                604     //the string to be stored was too long and has been truncated, This is a warning only.
+#define AX_EGR_JSON_PARSE_FAIL          800     //The egress message that was returned did not have the necessary fields
+#define AX_GEN_PARSE_ERR                603     //A general parsing failure has occured. Invalid JSON will cause this error. 
+
+
+//The following codes are used for an optional Queueing implementation example. 
+#define AX_GEN_QUEUE_FULL               600     //The queue is full and cannot accept more items. Item was not added. Enable overwrite or empty the queue
+#define AX_GEN_QUEUE_ITEM_EXISTS        601     //The item you tried to enqueue already exists in the queue.
+#define AX_GEN_QUEUE_EMPTY              602     //The queue is empty, thrown on a dequeue operation
+
+
+
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axToolkit.c	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,22 @@
+#include "axToolkit.h"
+#include "axTransport.h"
+
+int debugEnabled=AX_TRUE;
+
+void printDebug(char *msg) {
+    if(debugEnabled==AX_TRUE){
+    	ax_print(AX_DEBUG_MSG, msg);
+        }
+    }
+
+void printError(char *msg) {
+   ax_print(AX_ERROR_MSG, msg);
+   }
+
+int ax_debugEnabled() {
+    if(debugEnabled==1) { return AX_TRUE; }
+    else {return AX_FALSE; }
+    }
+
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axToolkit.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,49 @@
+/* ========================================================================== */
+/*                                                                            */
+/*   axToolkit.h                                                              */
+/*   ©2013 Axeda Corporation                                                  */
+/*                                                                            */
+/*   This file will include all necessary libraries and functions to use the  */
+/*   AMMP C Toolkit in your project.                                          */
+/* ========================================================================== */
+
+#ifndef AXTOOLKIT_H
+#define AXTOOLKIT_H
+
+#include "axTypes.h"
+#include "axConstants.h"
+#include "axStatusCodes.h"
+#include "axDomain.h"
+#include "axPlatform.h"
+#include "axTransport.h"
+//#include "axHTTP.h"
+
+
+typedef struct
+{
+    ax_deviceID thisDevice;         //A struct containing the device serial and model numbers  -- How the device identifies itself
+    ax_platform platform;           //A struct containing the target platform URL, port number etc. -- Where to send the data
+    int     conf_ping_rate;         //ping rate of the device in SECONDS
+    int     first_run;              //a flag to indicate if this is a first run of the agent
+    int     conf_recv_timeout;      //how long we wait to receive a response from the server
+    int     conf_queue_size;        //the size of the queues used to store data items/locations/events/alarms while operating
+    int     debug_mode;             //prints out extra messages to the standard out. Also adds HTTP header with a sequential value to all packets.
+    int     print_mem;              //prints out heap memory statistics to the standard out.
+} axToolkitConfig;
+
+extern int debugEnabled;
+
+#ifdef __cplusplus 
+  extern "C" {
+#endif
+
+int ax_debugEnabled();
+void printDebug(char *msg);
+void printError(char *msg); 
+
+
+#ifdef __cplusplus
+  }
+#endif
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axTransport.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,291 @@
+#include "axTransport.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "axStatusCodes.h"
+#include "axConstants.h"
+#include <math.h>
+#include "include_me.h"
+#include "mbed.h"
+
+using namespace mts;
+char *temp_buff;
+int buff_size;
+
+/************************************************************************************************/
+/*getEpoch()                                                                                    */
+/*                                                                                              */
+/*This method should return the number of seconds since January 1st 1970.                       */
+/*                                                                                              */
+/*returns long value in seconds                                                                 */
+/************************************************************************************************/
+long getEpoch(){
+    return (long)0;
+    }
+    
+
+/**************************************************************************************************/
+/*ax_print()                                                                                     */
+/*                                                                                               */
+/*A wrapper method that will allow you to direct the output to a particular stream. Most of the  */
+/*time the output will just go to printf/std. out. This is called by the axAgent library to print*/
+/*error and debug messages. It is reccomended to include processing for the integers AX_DEBUG_MSG*/
+/*and AX_ERROR_MSG as the library will frequently use these options if configured.               */
+/*                                                                                               */
+/* msgType: allows for an integer value to control how the data is displayed or written          */
+/* msg: a pointer to a string containing the data to be printed                                  */
+/************************************************************************************************/
+void ax_print(int msgType, char *msg){
+	switch(msgType) {
+	case AX_DEBUG_MSG:
+		 printf("DEBUG: %s\n", msg);
+		 break;
+	case AX_ERROR_MSG:
+		 printf("ERR: %s\n", msg);
+		 break;
+		}
+
+	}
+/************************************************************************************************/
+/*sys_delay()                                                                                   */
+/*                                                                                              */
+/*A wrapper method that is meant to induce a delay on the system that it running it. Used mostly*/
+/*to implement timeouts.                                                                        */
+/*                                                                                              */
+/*                                                                                              */
+/*                                                                                              */
+/************************************************************************************************/
+void sys_delay(int seconds){ 
+
+  }
+
+/************************************************************************************************/
+/*randInt()											*/
+/*												*/
+/*This function is an abstraction function and should be populated with a call to the host's    */
+/*equivalent of the random function. 								*/
+/************************************************************************************************/
+int randInt() {
+     //call to the local create random method.
+     srand(1235);
+     return rand();
+     }
+
+/************************************************************************************************/
+/*seedRand()                                                                                     */
+/*                                                                                              */
+/*This function is an abstraction function and should be populated with a call to the host's    */
+/*equivalent of the function that seeds the random number generator                             */
+/************************************************************************************************/
+int seedRand(){
+
+  
+  }
+
+/*************************************************************************************************/
+/*net_socketInit()                                                                               */
+/*                                                                                               */
+/*This method provides a convenient way to call code that initializes the socket before it is    */
+/*actually called to open. For example, it could initialize a holder object that you code or set */
+/*options on the socket itself. The return will be a code that could be passed up through the    */
+/*HTTP library and to the error stream.                                                          */
+/*************************************************************************************************/
+int net_socketInit(ax_socket *sock) {
+  int retVal=AX_UNKNOWN;
+  
+  buff_size=0;
+  temp_buff=NULL;
+  
+  return retVal;
+  }
+
+/*************************************************************************************************/
+/*net_socketOpen()                                                                               */
+/*                                                                                               */
+/*This function opens a network port and connects to it. The connection point is defined by server*/
+/*and port in the arguments. A reference to the port should be stored in the sock structure. If  */
+/*the secure flag is set to AX_TRUE your code should enable TLS or SSL at this stage.            */
+/*                                                                                               */
+/*************************************************************************************************/
+int net_socketOpen(ax_socket *sock, char *server, int port, int secure){
+  int retVal=AX_UNKNOWN;
+  
+  Wifi* wifi = Wifi::getInstance();
+ // Mode mode=TCP;
+   bool result=wifi->open(server, port, Wifi::TCP);
+//   bool result=wifi->open("192.168.1.107", port, Wifi::TCP);
+  if(result) {
+  	retVal=AX_OK;
+  	}
+  else {retVal=AX_NET_ERR_UNABLE_TO_CONNECT; }
+  
+  return retVal;
+  }
+/*************************************************************************************************/
+/*net_socketWrite()                                                                             */
+/*                                                                                               */
+/*This function is used to write the data to a network port that has already been open and is    */
+/*connected. As usual the ax_socket struct should contain a reference to your port that you wish */
+/*to write to. Data contains the data to be written and size describes how long the data is.     */
+/*                                                                                               */
+/*************************************************************************************************/
+int net_socketWrite(ax_socket *sock, char *data, int size) {
+  int retVal=AX_OK;
+//  Wifi* wifi = Wifi::getInstance();
+//  int written= wifi->write(data, size, 300);
+   int fullsz=size+1;
+   if(buff_size==0) {
+   	temp_buff=(char *)calloc(fullsz, sizeof(char));
+   	int wrt=snprintf(temp_buff, fullsz, "%s", data);
+   	buff_size++;
+   	}
+  else {
+  	int total=strlen(temp_buff)+fullsz;
+  	char *temp=(char *)calloc(total, sizeof(char));
+  	snprintf(temp, total, "%s%s", temp_buff, data);
+  	free(temp_buff);
+  	temp_buff=temp;
+    buff_size++;
+  	}
+  
+  return retVal;
+  }
+  
+
+/*************************************************************************************************/
+/*net_socketFlush()                                                                              */
+/*                                                                                               */
+/*This function will be called to handle writing all the data out to a network port. This function*/
+/*may not be needed depending on your system. The single argument is a pointer to a struct that  */
+/*should contain the necessary references to the port on your local system.                      */
+/*                                                                                               */
+/*************************************************************************************************/
+int net_socketFlush(ax_socket *sock){
+  int retVal=AX_UNKNOWN;
+  int size=strlen(temp_buff);
+  
+  Wifi* wifi = Wifi::getInstance();
+  wifi->write(temp_buff, size, 300); //Wr
+
+  free(temp_buff);
+  buff_size=0;
+  
+  return retVal;
+  }
+/*************************************************************************************************/
+/*net_socketClose()                                                                              */
+/*                                                                                               */
+/*This function will be called to handle a socket close operation. It is used primarily by the   */
+/*axHTTP part of the library. The single argument is pointer to a structure that should contain  */
+/*the necessary references to the port on your local system.                                     */
+/*Override the source in this function to close a network port                                   */
+/*************************************************************************************************/
+int net_socketClose(ax_socket *sock){
+ // int retVal=AX_UNKNOWN;
+  Wifi* wifi = Wifi::getInstance();
+  wifi->close();
+  int ctr=30;
+  
+  while((wifi->isOpen()==true)&&(ctr>0)) {
+  	wifi->close();
+  	printf("Socket Not closed, %d\n", ctr);
+  	ctr--;
+	wait(1);
+  	}
+  
+  return AX_OK;
+  }
+  
+/*************************************************************************************************/
+/*net_socketRead()                                                                               */
+/*                                                                                               */
+/*This function is a wrapper function that will read returned data from a previous web call. It  */
+/*is assumed that any connection related to ax_socket will have already been established. The    */
+/*function should read the data and store it into the *data pointer, then populate the number of */
+/*bytes read in the size variable. The axHTTP library will handle parsing and making sense of the*/
+/*data that it is provided. The timeout will be passed as a parameter but is not implemented by  */
+/*any of the calling libraries, implement it here or pass to another local function if available.*/
+/*************************************************************************************************/
+int net_socketRead(ax_socket *sock, int timeout, unsigned char *buffer, int bufferSz, int *readSz){
+  int retVal=AX_UNKNOWN;
+  Wifi* wifi = Wifi::getInstance();
+  int bytesRead = wifi->read((char *)buffer, bufferSz, 300);
+  *readSz=bytesRead;
+  if(bytesRead>0) {return AX_OK; }
+  
+  return retVal;
+  }
+
+/*************************************************************************************************/
+/*scm_download_req()                                                                              */
+/*                                                                                                */
+/*This function will be called whenever an egress message is recieved with a file download command*/
+/*You can write the code in the function to handle the file immediately OR store the structure for*/
+/*later processing. The latter is preferable as this request may prevent the network port from    */
+/*being closed in a timely fashion.                                                              */
+/*                                                                                               */
+/*************************************************************************************************/
+int scm_download_req(ax_package *request) {
+
+
+}
+
+
+/*************************************************************************************************/
+/*scm_file_write()                                                                               */
+/*                                                                                               */
+/*This function will be called whenever a file download is requested from the system. Override the*/
+/*function to direct the output to serial or a file system or other storage device of your choice*/
+/*The parameters to the function will be filled in when it's called by the toolkit               */
+/*                                                                                               */
+/*char *filename: Just what it sounds like, the file name of the file                            */
+/*char *data: The actual data of the file.                                                       */
+/*int length: The number of bytes received                                                       */
+/*ax_package_instruction *downloadInfo: a struct containing filename/path etc.                   */
+/*************************************************************************************************/
+int scm_file_write(char *filename, char *data, int length, ax_package_instruction *downloadInfo){
+
+
+    return AX_OK;
+    }
+
+
+
+/*************************************************************************************************/
+/*data_item_write()                                                                              */
+/*                                                                                               */
+/*If a set data item command is recieved from the platform server this function will be called to*/
+/*handle it each time. The function is here to allow for external data to be modified as needed. */
+/*It could toggle a pin and light up an led or set a global variable in another thread.          */
+/*                                                                                               */
+/*char *name: The name of the data item on the platform                                          */
+/*int ax_type: {AX_STRING|AX_ANALoG|AX_DIGITAL} indicates whether the sValue or dValue parameter */
+/*             will have the intended data.                                                      */
+/*char *sValue: if ax_type==AX_STRING then this will be a pointer to a valid cstring             */
+/*int dValue: if ax_type==AX_ANLOG|AX_DIGITAL this value will be populated. It should be not be  */
+/*            checked if the type is AX_STRING                                                   */
+/*************************************************************************************************/
+int data_item_write(char *name, char *sValue, int dValue, int ax_type) {
+
+return AX_OK;
+}
+
+/*************************************************************************************************/
+/*data_item_request()                                                                            */
+/*                                                                                               */
+/*This function is called whenever the platform requests a data item's current value. Since this */
+/*library is designed to run as it's own thread it does not maintain a list of data items and    */
+/*values. The implementation of this function should call an appropriate library or check an input*/
+/*then create a dataItem set and send it immediately or on the next ping.                        */
+/*                                                                                               */
+/*char *name, the name of the data item being requested                                          */
+/*                                                                                               */
+/*************************************************************************************************/
+int data_item_request(char *name) {
+
+return AX_OK;
+}
+
+/***************************************************************************************************/
+/***********************************************{Custom helper methods}****************************/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axTransport.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,50 @@
+
+
+#ifndef _AXTRANSPORT_H_
+#define _AXTRANSPORT_H_
+
+#include "axTypes.h"
+
+/************************************************************************************/
+/*Struct ax_socket                                                                  */
+/*                                                                                  */
+/*The following structure is meant as a mechanism to pass information from a network*/
+/*function to the next. It can be overridden with machine specific code, provided   */
+/* that all of the functions in this file prefixed with net_ can handle it correctly*/
+/************************************************************************************/
+typedef struct {
+    //put any structures/file descriptors here so you can access them from any of the net_ calls.
+}ax_socket;
+
+#ifdef __cplusplus 
+  extern "C" {
+#endif
+
+
+long getEpoch();
+void ax_print(int msgType, char *msg);
+int randInt();
+int seedRand();
+
+void sys_delay(int seconds);
+
+int net_socketInit(ax_socket *sock);
+int net_socketOpen(ax_socket *sock, char *server, int port, int secure);
+int net_socketWrite(ax_socket *sock, char *data, int size);
+int net_socketClose(ax_socket *sock);
+int net_socketFlush(ax_socket *sock);
+int net_socketRead(ax_socket *sock, int timeout, unsigned char *buffer, int bufferSz, int *readSz);
+
+int scm_download_req(ax_package *request);
+int scm_file_write(char *filename, char *data, int length, ax_package_instruction *downloadInfo);
+int data_item_write(char *name, char *sValue, int dValue, int ax_type);
+int data_item_request(char *name);
+
+int initializeCellular();
+#ifdef __cplusplus 
+  }
+#endif
+
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AMMPC/axTypes.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,100 @@
+#ifndef _AXTYPES_H_
+#define _AXTYPES_H_
+
+#include "axSettings.h"
+
+
+typedef struct ax_dataNode {
+    char name[AX_DN_NAME_S];   
+    char sValue[AX_DN_SV_S];
+    double  dValue;
+    int type;
+    struct ax_dataNode* next;
+}ax_dataNode;
+
+typedef struct ax_dataNode axDataNode;
+
+typedef struct {
+    int acquisitionTime;
+    int priority;
+    int created;
+    ax_dataNode *data_first;  
+    ax_dataNode *data_last;
+    int dataNode_ct;
+}ax_dataSet;
+
+typedef struct {
+  char alarmName[AX_ALM_NAME_S];
+  char alarmDescription[AX_ALM_DESC_S];
+  int alarmSeverity;
+  char alarmCause[AX_ALM_CAUSE_S];
+  char alarmReason[AX_ALM_REAS_S];
+  int dateAcquired;
+  int priority;
+}ax_alarm;   
+
+typedef struct {
+  char name[AX_EVT_NAME_S];
+  char description[AX_EVT_DESC_S];
+  int dateAcquired;
+  int priority;
+}ax_event;
+
+typedef struct {
+  double latitude;
+  double longitude;
+  double altitude;
+  int dateAcquired;
+  int priority;
+}ax_location;
+
+typedef struct { 
+    char deviceId[AX_MSID_DID_S]; //Future expansion
+    char model[AX_MSID_MDL_S];
+    char serial[AX_MSID_SER_S];
+    char tenant[AX_MSID_TEN_S];
+}ax_deviceID;
+
+//TODO: Possibly deprecate the ax_registration domain object which is just a ping rate and deviceID. Enforce it in the method.
+typedef struct {
+    ax_deviceID *device;
+    int pingRate;
+}ax_registration;
+
+typedef struct { 
+  char name[AX_FILE_NAME_S];
+  char hint[AX_FILE_HINT_S];
+  int size;
+  char fileID[AX_FILE_ID_S];
+  unsigned char *data; //array of bytes/ints/whatever
+  //int priority;
+}ax_file;
+
+typedef struct {
+    char hostname[AX_PLAT_HOST_S];
+    int ip[4];
+    int online;
+    int port;
+    int secure;         //Use SSL/TLS when talking to the platform.
+    int ping_rate;
+    int verify;         //a flag to verify the SSL/TLS certificate of the platform. The certificate MUST be stored locally and match otherwise the TLS handshake will fail.
+}ax_platform;
+
+typedef struct pkg_inst{
+   int instruction_type; //mapped to the @type
+   char file_id[AX_PKGI_FID_S];
+   char path[AX_PKGI_PATH_S];
+   char filename[AX_PKGI_FNAME_S];
+   struct pkg_inst *next;
+}ax_package_instruction;
+
+typedef struct {
+  char packageID[AX_PKG_ID_S];
+  ax_package_instruction *instructions;
+  int time;
+  int priority;
+}ax_package;
+
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FRDM_MMA8451Q.lib	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/clemente/code/FRDM_MMA8451Q/#13e2af71e2cf
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FRDM_MMA8451Q/MMA8451Q.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,541 @@
+/* Copyright (c) 2010-2011 mbed.org, MIT License
+*
+* 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 "MMA8451Q.h"
+
+#define REG_STATUS        0x00
+#define REG_WHO_AM_I      0x0D
+#define REG_CTRL_REG_1    0x2A
+#define REG_CTRL_REG_2    0x2B
+#define REG_CTRL_REG_4    0x2D
+#define REG_CTRL_REG_5    0x2E
+#define REG_INT_SRC       0x0C
+#define REG_FF_MT_CFG     0x15
+#define REG_FF_MT_SRC     0x16
+#define REG_FF_MT_THS     0x17
+#define REG_FF_MT_CNT     0x18
+#define REG_DBCNTM        0x11
+#define REG_DBNCE         0x12
+#define REG_BKFR          0x13
+#define REG_P_L_THS       0x14
+#define REG_PL_STATUS     0x10
+
+//
+#define REG_OUT_X_MSB     0x01
+#define REG_OUT_Y_MSB     0x03
+#define REG_OUT_Z_MSB     0x05
+
+#define UINT14_MAX        16383
+
+//
+#define ZYXDR           0x08
+#define ZDR             0x04
+#define YDR             0x02
+#define XDR             0x01
+
+/** Interrupt schema
+*
+* :: The FreeFall and Motion detection share the same IRQ2. 
+* 
+*   FreeFall --+                             +-- Fall_IRQ -----+
+*               \                           /                   \
+*                +-- MMA8451Q_Int2.fall ---+                     +--- MMA8451Q_usr2_fptr
+*               /                           \                   /
+*   Motion ----+                             +-- Motion_IRQ ---+
+*   
+* :: The Orientation Detect use the IRQ1
+* 
+*   Orientation Detect -- MMA8451Q_Int1.fall --- Orientation_IRQ --- MMA8451Q_usr1_fptr
+*
+*
+* :: The data ready use the IRQ2
+*
+*   Data Ready -- MMA8451Q_Int2.fall --- DataReady_IRQ --- usr2_fptr
+*
+*/
+void (*MMA8451Q_usr2_fptr)(void);               // Pointers to user function called after
+void (*MMA8451Q_usr1_fptr)(void);               // IRQ assertion.
+
+//
+InterruptIn MMA8451Q_Int1( PTA14);      // INT1
+InterruptIn MMA8451Q_Int2( PTA15);      // INT2
+
+MMA8451Q::MMA8451Q(PinName sda, PinName scl, int addr) : m_i2c(sda, scl), m_addr(addr) {
+    
+    MMA8451Q_Int1.fall( NULL);
+    MMA8451Q_Int2.fall( NULL);
+    MMA8451Q_usr2_fptr = NULL;
+    MMA8451Q_usr1_fptr = NULL;    
+
+    Reset();    
+    Active();    
+}
+
+MMA8451Q::~MMA8451Q() 
+{
+     MMA8451Q_Int1.fall( NULL);
+     MMA8451Q_Int2.fall( NULL);
+     MMA8451Q_usr2_fptr = NULL;
+     MMA8451Q_usr1_fptr = NULL;
+}
+
+void MMA8451Q::Reset( void)
+{
+    // Soft reset
+    uint8_t data[2] = {REG_CTRL_REG_2, 0x40};
+    writeRegs(data, 2);
+    wait( 0.1);
+}
+
+void MMA8451Q::FreeFallDetection( void(*fptr)(void))
+{
+    // Soft Reset
+    Reset();
+    
+    // Example Steps for Configuring Linear Freefall Detection
+    // X AND Y AND Z < 0.2g using MFF Function, 50 Hz ODR
+    // Step 1: Put the device in Standby Mode: Register 0x2A CTRL_REG1
+    unsigned char data[2] = {REG_CTRL_REG_1, 0x20};
+    writeRegs(data, 2);
+    
+    // Step 2: Configuration Register set for Freefall Detection enabling “AND” condition, OAE = 0, Enabling X,
+    // Y, Z and the Latch
+    data[0] = REG_FF_MT_CFG;
+    data[1] = 0x01;
+    writeRegs(data, 2);
+
+    // Step 3: Threshold Setting Value for the resulting acceleration < 0.2g
+    // Note: The step count is 0.063g/count
+    // • 0.2g/0.063g = 3.17 counts //Round to 3 counts
+    data[0] = REG_FF_MT_THS;
+    data[1] = 0x03;
+    writeRegs(data, 2);
+
+    // Step 4: Set the debounce counter to eliminate false positive readings for 50Hz sample rate with a
+    // requirement of 120 ms timer, assuming Normal Mode.
+    // Note: 120 ms/20 ms (steps) = 6 counts
+    data[0] = REG_FF_MT_CNT;
+    data[1] = 0x06;
+    writeRegs(data, 2);
+
+    // Step 5: Enable Motion/Freefall Interrupt Function in the System (CTRL_REG4)
+    data[0] = REG_CTRL_REG_4;
+    data[1] = 0x04;
+    writeRegs(data, 2);
+
+    // Step 6: Route the Motion/Freefall Interrupt Function to INT2 hardware pin (CTRL_REG5)
+    data[0] = REG_CTRL_REG_5;
+    data[1] = 0x00;
+    writeRegs(data, 2);
+    
+    // Step 7: Put the device in Active Mode, 50 Hz
+    data[0] = REG_CTRL_REG_1;
+    data[1] = 0x21;
+    writeRegs(data, 2);
+    
+    MMA8451Q_usr2_fptr = fptr;
+    MMA8451Q_Int2.fall( this, &MMA8451Q::Fall_IRQ);
+}
+
+void MMA8451Q::Fall_IRQ( void)
+{
+    unsigned char t;
+    
+    // Determine source of the interrupt by first reading the system interrupt
+    readRegs( REG_INT_SRC, &t, 1);
+    //
+    if ( (t & 0x04) == 0x04) {
+        // Read the Motion/Freefall Function to clear the interrupt
+        readRegs( REG_FF_MT_SRC, &t, 1);
+        // Run the user supplied function
+        MMA8451Q_usr2_fptr();
+    }
+}
+
+void MMA8451Q::MotionDetection( void(*fptr)(void))
+{
+    // Soft Reset
+    Reset();
+    
+    // 6.1 Example Steps for Configuring Motion Detection
+    // X or Y > 3g using MFF Function 4g, 100 Hz ODR, Normal Mode
+    // Step 1: Put the device into Standby Mode: Register 0x2A CTRL_REG1
+    unsigned char data[2] = {REG_CTRL_REG_1, 0x18}; // Set the device in 100 Hz ODR, Standby
+    writeRegs(data, 2);
+
+    
+    // Step 2: Set Configuration Register for Motion Detection by setting the “OR” condition OAE = 1, enabling
+    // X, Y, and the latch
+    data[0] = REG_FF_MT_CFG;
+    data[1] = 0xD8;
+    writeRegs(data, 2);
+
+    // Step 3: Threshold Setting Value for the Motion detection of > 2g
+    // Note: The step count is 0.063g/ count
+    // • 1g/0.063g = 15.8; //Round up to 16
+    data[0] = REG_FF_MT_THS;
+    data[1] = 0x10;
+    writeRegs(data, 2);
+    
+    // Step 4: Set the debounce counter to eliminate false readings for 100 Hz sample rate with a requirement
+    // of 100 ms timer.
+    // Note: 100 ms/10 ms (steps) = 10 counts
+    data[0] = REG_FF_MT_CNT;
+    data[1] = 0x0A;
+    writeRegs(data, 2);
+    
+    // Step 5: Enable Motion/Freefall Interrupt Function in the System (CTRL_REG4)
+    data[0] = REG_CTRL_REG_4;
+    data[1] = 0x04;
+    writeRegs(data, 2);
+    
+    // Step 6: Route the Motion/Freefall Interrupt Function to INT2 hardware pin (CTRL_REG5)
+    data[0] = REG_CTRL_REG_5;
+    data[1] = 0x00;
+    writeRegs(data, 2);
+    
+    // Step 7: Put the device in Active Mode
+    data[0] = REG_CTRL_REG_1;
+    data[1] = 0x19;
+    writeRegs(data, 2);
+
+    MMA8451Q_usr2_fptr = fptr;
+    MMA8451Q_Int2.fall( this, &MMA8451Q::Motion_IRQ);
+
+}
+
+void MMA8451Q::Motion_IRQ( void)
+{
+    unsigned char t;
+    
+    // Determine source of the interrupt by first reading the system interrupt
+    readRegs( REG_INT_SRC, &t, 1);
+    //
+    if ( (t & 0x04) == 0x04) {
+        // Read the Motion/Freefall Function to clear the interrupt
+        readRegs( REG_FF_MT_SRC, &t, 1);
+        // Run the user supplied function
+        MMA8451Q_usr2_fptr();
+    }
+}
+
+void MMA8451Q::OrientationDetect( void(*fptr)(void))
+{
+    OrientationDetect( fptr, Z_LOCKOUT_14, Z_BKFR_80, PL_THS_15, PL_HYS_0);
+}
+
+void MMA8451Q::OrientationDetect( void(*fptr)(void), unsigned int Z_LockOut, unsigned int Z_BkFr, unsigned int PL_Thsld, unsigned int PL_Hyst)
+{
+    unsigned char t;
+
+    // Soft Reset
+    Reset();
+        
+    // Reset orientation value.
+    OrientationState = 0;
+    OrientationStateUpdated = 0;
+    
+    // Step 1: Put the part into Standby Mode
+    Standby();
+    
+    // Step 2: Set the data rate to 50 Hz (for example, but can choose any sample rate).
+    readRegs( REG_CTRL_REG_1, &t, 1);       // Note: Can combine this step with above
+    t &= 0xC7;                             // Clear the sample rate bits
+    t |= 0x20;                             // Set the sample rate bits to 50 Hz
+    unsigned char data[2] = {REG_CTRL_REG_1, t};
+    writeRegs(data, 2);                     // Write updated value into the register.   
+    
+    
+    // Step 3: Set the PL_EN bit in Register 0x11 PL_CFG. This will enable the orientation detection.
+    readRegs( REG_DBCNTM, &t, 1);
+    data[0] = REG_DBCNTM;
+    data[1] = t | 0x40;
+    writeRegs(data, 2);
+    
+    // Step 4: Set the Back/Front Angle trip points in register 0x13 following the table in the data sheet.
+    // NOTE: This register is readable in all versions of MMA845xQ but it is only modifiable in the
+    // MMA8451Q.
+    readRegs( REG_BKFR, &t, 1);
+    t &= 0x3F;                      // Clear bit 7 and 6    
+    data[0] = REG_BKFR;
+    data[1] = t | Z_BkFr;
+    writeRegs(data, 2);             // Write in the updated Back/Front Angle
+
+    // Step 5: Set the Z-Lockout angle trip point in register 0x13 following the table in the data sheet.
+    // NOTE: This register is readable in all versions of MMA845xQ but it is only modifiable in the
+    // MMA8451Q.
+    readRegs( REG_BKFR, &t, 1);
+    t &= 0xF8;                      // Clear the last three bits of the register
+    data[0] = REG_BKFR;
+    data[1] = t | Z_LockOut;
+    writeRegs(data, 2);             // Write in the updated Z-lockout angle
+    
+    // Step 6: Set the Trip Threshold Angle
+    // NOTE: This register is readable in all versions of MMA845xQ but it is only modifiable in the
+    // MMA8451Q.
+    // Select the angle desired in the table, and,
+    // Enter in the values given in the table for the corresponding angle.
+    // Refer to Figure 7 for the reference frame of the trip angles.
+    readRegs( REG_P_L_THS, &t, 1);
+    t &= 0x07;                      // Clear the Threshold values
+    data[0] = REG_P_L_THS;
+    data[1] = t | (PL_Thsld<<3);
+    writeRegs(data, 2);             
+    
+    // Step 7: Set the Hysteresis Angle
+    // NOTE: This register is readable in all versions of MMA845xQ but it is only modifiable in the
+    // MMA8451Q.
+    // Select the hysteresis value based on the desired final trip points (threshold + hysteresis)
+    // Enter in the values given in the table for that corresponding angle.
+    // Note: Care must be taken. Review the final resulting angles. Make sure there isn’t a resulting trip value
+    // greater than 90 or less than 0.
+    // The following are the options for setting the hysteresis.
+    readRegs( REG_P_L_THS, &t, 1);
+    t &= 0xF8;                      // Clear the Hysteresis values
+    data[0] = REG_P_L_THS;
+    data[1] = t | PL_Hyst;
+    writeRegs(data, 2);             
+    
+    // Step 8: Register 0x2D, Control Register 4 configures all embedded features for interrupt
+    // detection.
+    // To set this device up to run an interrupt service routine:
+    // Program the Orientation Detection bit in Control Register 4.
+    // Set bit 4 to enable the orientation detection “INT_EN_LNDPRT”.
+    readRegs( REG_CTRL_REG_4, &t, 1);
+    data[0] = REG_CTRL_REG_4;
+    data[1] = t | 0x10;                 // Set bit 4
+    writeRegs(data, 2);             
+    
+    // Step 9: Register 0x2E is Control Register 5 which gives the option of routing the interrupt to
+    // either INT1 or INT2
+    // Depending on which interrupt pin is enabled and configured to the processor:
+    // Set bit 4 “INT_CFG_LNDPRT” to configure INT1, or,
+    // Leave the bit clear to configure INT2.
+    readRegs( REG_CTRL_REG_5, &t, 1);
+    data[0] = REG_CTRL_REG_5;
+    data[1] = t | 0x10;                 // Set bit 4 to choose the interrupt to route to INT1
+    writeRegs(data, 2);             
+    
+    // Step 10: Set the debounce counter in register 0x12
+    // This value will scale depending on the application-specific required ODR.
+    // If the device is set to go to sleep, reset the debounce counter before the device goes to sleep. This setting
+    // helps avoid long delays since the debounce will always scale with the current sample rate. The debounce
+    // can be set between 50 ms - 100 ms to avoid long delays.
+    data[0] = REG_DBNCE;
+    data[1] = 0x05;                     // This sets the debounce counter to 100 ms at 50 Hz
+    writeRegs(data, 2);             
+    
+    // Step 11: Put the device in Active Mode
+    Active();
+ 
+    MMA8451Q_usr1_fptr = fptr;
+    MMA8451Q_Int1.fall( this, &MMA8451Q::Orientation_IRQ);
+
+}
+
+void MMA8451Q::Orientation_IRQ( void)
+{
+    unsigned char t;
+    
+    // Determine source of the interrupt by first reading the system interrupt
+    readRegs( REG_INT_SRC, &t, 1);
+    //
+    if ( (t & 0x10) == 0x10) {
+        // Read the PL State from the Status Register, clear the interrupt
+        readRegs( REG_PL_STATUS, &t, 1);
+        // Set the orientation state variable
+        OrientationState = t;
+        OrientationStateUpdated = 1;
+        // Run the user supplied function
+        MMA8451Q_usr1_fptr();
+    }
+}
+
+unsigned char MMA8451Q::GetOrientationState( void)
+{
+    if ( OrientationStateUpdated) {
+        OrientationStateUpdated = 0;
+        return OrientationState;
+    }
+    //
+    return 0;
+}
+
+void MMA8451Q::DataReady( void(*fptr)(void), unsigned char ODR)
+{
+    // Soft Reset
+    Reset();
+    
+    // Step 1: Put the device into Standby Mode: Register 0x2A CTRL_REG1
+    // Set the device ODR value and Standby
+    unsigned char data[2] = {REG_CTRL_REG_1, ((ODR<<3) & 0xFE)};
+    writeRegs(data, 2);
+
+    // Step 2: Enable Data Ready Interrupt Function in the System (CTRL_REG4)
+    data[0] = REG_CTRL_REG_4;
+    data[1] = 0x01;
+    writeRegs(data, 2);
+    
+    // Step 6: Route the Data Ready Interrupt Function to INT2 hardware pin (CTRL_REG5)
+    data[0] = REG_CTRL_REG_5;
+    data[1] = 0x00;
+    writeRegs(data, 2);
+    
+    // Step 7: Put the device in Active Mode
+    data[0] = REG_CTRL_REG_1;
+    data[1] = ((ODR<<3) | 0x01);
+    writeRegs(data, 2);
+
+    MMA8451Q_usr2_fptr = fptr;
+    MMA8451Q_Int2.fall( this, &MMA8451Q::DataReady_IRQ);
+
+}
+
+void MMA8451Q::DataReady_IRQ( void)
+{
+    unsigned char t;
+    
+    // Determine source of the interrupt by first reading the system interrupt
+    readRegs( REG_INT_SRC, &t, 1);
+    //
+    if ( (t & 0x01) == 0x01) {
+        // Read the DataReady_IRQ Function to clear the interrupt
+        readRegs( REG_FF_MT_SRC, &t, 1);
+        // Run the user supplied function
+        MMA8451Q_usr2_fptr();
+    }
+}
+
+
+void MMA8451Q::Active( void)
+{
+    unsigned char t;
+    
+    // Activate the peripheral
+    readRegs(REG_CTRL_REG_1, &t, 1);
+    unsigned char data[2] = {REG_CTRL_REG_1, t|0x01};
+    writeRegs(data, 2);
+}
+
+void MMA8451Q::Standby( void)
+{
+    unsigned char t;
+    
+    // Standby
+    readRegs(REG_CTRL_REG_1, &t, 1);
+    unsigned char data[2] = {REG_CTRL_REG_1, t&0xFE};
+    writeRegs(data, 2);
+}
+
+uint8_t MMA8451Q::getWhoAmI() {
+    uint8_t who_am_i = 0;
+    readRegs(REG_WHO_AM_I, &who_am_i, 1);
+    return who_am_i;
+}
+
+float MMA8451Q::getAccX() {
+    return (float(getAccAxis(REG_OUT_X_MSB))/4096.0);
+}
+
+float MMA8451Q::getAccY() {
+    return (float(getAccAxis(REG_OUT_Y_MSB))/4096.0);
+}
+
+float MMA8451Q::getAccZ() {
+    return (float(getAccAxis(REG_OUT_Z_MSB))/4096.0);
+}
+
+void MMA8451Q::getAccAllAxis(float * res) {
+    res[0] = getAccX();
+    res[1] = getAccY();
+    res[2] = getAccZ();
+}
+
+int16_t MMA8451Q::getAccAxis(uint8_t addr) {
+    int16_t acc;
+    uint8_t res[2];
+    readRegs(addr, res, 2);
+
+    acc = (res[0] << 6) | (res[1] >> 2);
+    if (acc > UINT14_MAX/2)
+        acc -= UINT14_MAX;
+
+    return acc;
+}
+
+unsigned int MMA8451Q::getAccRawAllAxis( int16_t * res) 
+{
+    if ( isDataAvailable() & ZYXDR) 
+    {
+        getAccRawX( &res[0]);
+        getAccRawY( &res[1]);
+        getAccRawZ( &res[2]);
+        return 1;
+    } else
+        return 0;
+}
+
+int16_t MMA8451Q::getAccRawX( int16_t * res) 
+{
+    if ( isDataAvailable() & XDR) 
+    {
+        *res = getAccAxis(REG_OUT_X_MSB);
+        return 1;
+    } else
+        return 0;        
+}
+
+int16_t MMA8451Q::getAccRawY( int16_t * res) 
+{
+    if ( isDataAvailable() & YDR) 
+    {
+        *res = getAccAxis(REG_OUT_Y_MSB);
+        return 1;
+    } else
+        return 0;        
+}
+
+int16_t MMA8451Q::getAccRawZ( int16_t * res) 
+{
+    if ( isDataAvailable() & ZDR) 
+    {
+        *res = getAccAxis(REG_OUT_Z_MSB);
+        return 1;
+    } else
+        return 0;        
+}
+
+unsigned int MMA8451Q::isDataAvailable( void)
+{
+    unsigned char status;
+    
+    readRegs( REG_STATUS, &status, 1);
+
+    return (status);
+    
+}
+
+void MMA8451Q::readRegs(int addr, uint8_t * data, int len) {
+    char t[1] = {addr};
+    m_i2c.write(m_addr, t, 1, true);
+    m_i2c.read(m_addr, (char *)data, len);
+}
+
+void MMA8451Q::writeRegs(uint8_t * data, int len) {
+    m_i2c.write(m_addr, (char *)data, len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FRDM_MMA8451Q/MMA8451Q.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,267 @@
+/* Copyright (c) 2010-2011 mbed.org, MIT License
+*
+* 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 MMA8451Q_H
+#define MMA8451Q_H
+
+#include "mbed.h"
+
+/**
+* MMA8451Q accelerometer example
+*
+* @code
+* #include "mbed.h"
+* #include "MMA8451Q.h"
+* 
+* #define MMA8451_I2C_ADDRESS (0x1d<<1)
+* 
+* int main(void) {
+* 
+* MMA8451Q acc(P_E25, P_E24, MMA8451_I2C_ADDRESS);
+* PwmOut rled(LED_RED);
+* PwmOut gled(LED_GREEN);
+* PwmOut bled(LED_BLUE);
+* 
+*     while (true) {       
+*         rled = 1.0 - abs(acc.getAccX());
+*         gled = 1.0 - abs(acc.getAccY());
+*         bled = 1.0 - abs(acc.getAccZ());
+*         wait(0.1);
+*     }
+* }
+* @endcode
+*/
+
+// Z-Lock Threshold Angles
+#define Z_LOCKOUT_14    0       // Angle to 14°
+#define Z_LOCKOUT_18    1       // Angle to 18°
+#define Z_LOCKOUT_21    2       // Angle to 21°
+#define Z_LOCKOUT_25    3       // Angle to 25°
+#define Z_LOCKOUT_29    4       // Angle to 29°
+#define Z_LOCKOUT_33    5       // Angle to 33°
+#define Z_LOCKOUT_37    6       // Angle to 37°
+#define Z_LOCKOUT_42    7       // Angle to 42°
+// Back/Front Orientation Definition
+#define Z_BKFR_80       0       // Back and Front trip angle
+#define Z_BKFR_75       1       // Back and Front trip angle
+#define Z_BKFR_70       2       // Back and Front trip angle
+#define Z_BKFR_65       3       // Back and Front trip angle
+// Threshold Angle Thresholds Lookup Table
+#define PL_THS_15       0x07    // Set Threshold to 15°
+#define PL_THS_20       0x09    // Set Threshold to 20°
+#define PL_THS_30       0x0C    // Set Threshold to 30°
+#define PL_THS_35       0x0D    // Set Threshold to 35°
+#define PL_THS_40       0x0F    // Set Threshold to 40°
+#define PL_THS_45       0x10    // Set Threshold to 45°
+#define PL_THS_55       0x13    // Set Threshold to 55°
+#define PL_THS_60       0x14    // Set Threshold to 60°
+#define PL_THS_70       0x17    // Set Threshold to 70°
+#define PL_THS_75       0x19    // Set Threshold to 75°
+// Trip Angles with Hysteresis for 45° Angle
+#define PL_HYS_0        0x00    // Set Hysteresis to ±0°    
+#define PL_HYS_4        0x01    // Set Hysteresis to ±4°    
+#define PL_HYS_7        0x02    // Set Hysteresis to ±7°
+#define PL_HYS_11       0x03    // Set Hysteresis to ±11°
+#define PL_HYS_14       0x04    // Set Hysteresis to ±14°
+#define PL_HYS_17       0x05    // Set Hysteresis to ±17°
+#define PL_HYS_21       0x06    // Set Hysteresis to ±21°
+#define PL_HYS_24       0x07    // Set Hysteresis to ±24°
+// Landscape/Portrait orientation
+#define cLAPO_PU        0       // Portrait Up: Equipment standing vertically in the normal orientation
+#define cLAPO_PD        1       // Portrait Down: Equipment standing vertically in the inverted orientation
+#define cLAPO_LR        2       // Landscape Right: Equipment is in landscape mode to the right
+#define cLAPO_LL        3       // Landscape Left: Equipment is in landscape mode to the left.
+// System Output Data Rate for acceleration samples
+#define cODR_800HZ      0       // 1.25 ms
+#define cODR_400HZ      1       // 2.5 ms
+#define cODR_200HZ      2       // 5 ms
+#define cODR_100HZ      3       // 10 ms
+#define cODR_50HZ       4       // 20 ms
+#define cODR_12_5HZ     5       // 80 ms
+#define cODR_6_25HZ     6       // 160 ms
+#define cODR_1_56HZ     7       // 640 ms
+
+class MMA8451Q
+{
+public:
+    /**
+    * MMA8451Q constructor
+    *
+    * @param sda SDA pin
+    * @param sdl SCL pin
+    * @param addr addr of the I2C peripheral
+    */
+    MMA8451Q(PinName sda, PinName scl, int addr);
+    
+    /**
+    * MMA8451Q destructor
+    */
+    ~MMA8451Q();
+    
+    /**
+    * Get the value of the WHO_AM_I register
+    *
+    * @returns WHO_AM_I value
+    */
+    uint8_t getWhoAmI();
+    
+    /**
+    * Get X axis acceleration
+    *
+    * @returns X axis acceleration
+    */
+    float getAccX();
+    
+    /**
+    * Get Y axis acceleration
+    *
+    * @returns Y axis acceleration
+    */
+    float getAccY();
+    
+    /**
+    * Get Z axis acceleration
+    *
+    * @returns Z axis acceleration
+    */
+    float getAccZ();
+    
+    /**
+    * Get XYZ axis acceleration
+    *
+    * @param res array where acceleration data will be stored
+    */
+    void getAccAllAxis(float * res);
+
+    /**
+    * Get raw value for X axis acceleration
+    *
+    * @returns X axis acceleration
+    */
+    int16_t getAccRawX( int16_t * res);
+    
+    /**
+    * Get raw value for Y axis acceleration
+    *
+    * @returns Y axis acceleration
+    */
+    int16_t getAccRawY( int16_t * res);
+    
+    /**
+    * Get raw value for Z axis acceleration
+    *
+    * @returns Z axis acceleration
+    */
+    int16_t getAccRawZ( int16_t * res);
+
+    /**
+    * Get raw values for XYZ axis acceleration
+    *
+    * @param res array where acceleration data will be stored
+    */
+    unsigned int getAccRawAllAxis(int16_t * res);
+
+    /**
+    * Configure the Accelerometere for free fall detection
+    *
+    * @param pointer to the user function to execute after IRQ assertion
+    * @return none
+    */
+    void FreeFallDetection( void(*fptr)(void));
+
+    /**
+    * Configure the Accelerometere for motion detection
+    *
+    * @param pointer to the user function to execute after IRQ assertion
+    * @return none
+    */
+    void MotionDetection( void(*fptr)(void));
+    
+    /**
+    * Configure the Accelerometere for orientation detection
+    *
+    * @param pointer to the user function to execute after IRQ assertion
+    * @param Z lockout value, Z Backfront, Port/Landscape Thsld, Port/Landscape Hysteresis
+    * @return none
+    */
+    void OrientationDetect( void(*fptr)(void), unsigned int Z_LockOut, unsigned int Z_BkFr, unsigned int PL_Thsld, unsigned int PL_Hyst);
+    void OrientationDetect( void(*fptr)(void));
+       
+    /**
+    * Get the orientation state. 
+    *
+    *    0x10: PL_STATUS Register (Read Only)
+    *    Bit 7   Bit 6   Bit 5   Bit 4   Bit 3   Bit 2   Bit 1   Bit 0
+    *    NEWLP   LO      —       —       —       LAPO[1] LAPO[0] BAFRO
+    *
+    *    NEWLP
+    *        Landscape/Portrait status change flag. Default value: 0.
+    *        0: No change, 1: BAFRO and/or LAPO and/or Z-Tilt lockout value has changed
+    *    LO
+    *        Z-Tilt Angle Lockout. Default value: 0.
+    *        0: Lockout condition has not been detected.
+    *        1: Z-Tilt lockout trip angle has been exceeded. Lockout has been detected.
+    *    LAPO[1:0](*)
+    *        Landscape/Portrait orientation. Default value: 00
+    *        00: Portrait Up: Equipment standing vertically in the normal orientation
+    *        01: Portrait Down: Equipment standing vertically in the inverted orientation
+    *        10: Landscape Right: Equipment is in landscape mode to the right
+    *        11: Landscape Left: Equipment is in landscape mode to the left.
+    *        (*) The default power up state is BAFRO = 0, LAPO = 0, and LO = 0.        
+    *    BAFRO
+    *        Back or Front orientation. Default value: 0
+    *        0: Front: Equipment is in the front facing orientation.
+    *        1: Back: Equipment is in the back facing orientation.
+    */
+    unsigned char GetOrientationState( void);
+
+    /**
+    * Configure the Accelerometer to generate a IRQ when a new sample is available
+    *
+    * @param pointer to the user function to execute after IRQ assertion
+    * @return none
+    */
+    void DataReady( void(*fptr)(void), unsigned char ODR);
+    
+    unsigned int isDataAvailable( void);
+    
+    /**
+    * Soft Reset
+    * @param none
+    * @return none
+    */
+    void Reset( void);
+    
+private:
+    I2C m_i2c;
+    int m_addr;
+    void readRegs(int addr, uint8_t * data, int len);
+    void writeRegs(uint8_t * data, int len);
+    int16_t getAccAxis(uint8_t addr);
+    void Standby( void);
+    void Active( void);
+    void Fall_IRQ( void);
+    void Motion_IRQ( void);
+    void Orientation_IRQ( void);
+    void DataReady_IRQ( void);
+    //
+    unsigned char OrientationState;
+    unsigned char OrientationStateUpdated;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost.lib	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/va009039/code/KL46Z-USBHost/#6463cd1964c0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHost/IUSBEnumerator.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,36 @@
+/* mbed USBHost 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 IUSBENUMERATOR_H_
+#define IUSBENUMERATOR_H_
+
+#include "stdint.h"
+#include "USBEndpoint.h"
+
+/*
+Generic interface to implement for "smart" USB enumeration
+*/
+
+class IUSBEnumerator
+{
+public:
+    virtual void setVidPid(uint16_t vid, uint16_t pid) = 0;
+    virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) = 0; //Must return true if the interface should be parsed
+    virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) = 0; //Must return true if the endpoint will be used
+};
+
+#endif /*IUSBENUMERATOR_H_*/
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHost/USBDeviceConnected.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,125 @@
+/* mbed USBHost 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.
+ */
+
+#include "USBDeviceConnected.h"
+//#include "dbg.h"
+#define USB_DBG(...) while(0)
+
+USBDeviceConnected::USBDeviceConnected() {
+    init();
+}
+
+void USBDeviceConnected::init() {
+    hub_nb = 0;
+    port = 0;
+    vid = 0;
+    pid = 0;
+    nb_interf = 0;
+    enumerated = false;
+    //activeAddr = false;
+    //sizeControlEndpoint = 8;
+    device_class = 0;
+    device_subclass = 0;
+    proto = 0;
+    lowSpeed = false;
+    for (int i = 0; i < MAX_INTF; i++) {
+        memset((void *)&intf[i], 0, sizeof(INTERFACE));
+        intf[i].in_use = false;
+        for (int j = 0; j < MAX_ENDPOINT_PER_INTERFACE; j++) {
+            intf[i].ep[j] = NULL;
+            //strcpy(intf[i].name, "Unknown");
+        }
+    }
+    //hub_parent = NULL;
+    //hub = NULL;
+    nb_interf = 0;
+}
+
+INTERFACE * USBDeviceConnected::getInterface(uint8_t index) {
+    if (index >= MAX_INTF)
+        return NULL;
+    
+    if (intf[index].in_use)
+        return &intf[index];
+    
+    return NULL;
+}
+
+bool USBDeviceConnected::addInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) {
+    if ((intf_nb >= MAX_INTF) || (intf[intf_nb].in_use)) {
+        return false;
+    }
+    intf[intf_nb].in_use = true;
+    intf[intf_nb].intf_class = intf_class;
+    intf[intf_nb].intf_subclass = intf_subclass;
+    intf[intf_nb].intf_protocol = intf_protocol;
+    intf[intf_nb].nb_endpoint = 0;
+    return true;
+}
+
+bool USBDeviceConnected::addEndpoint(uint8_t intf_nb, USBEndpoint * ept) {
+    if ((intf_nb >= MAX_INTF) || (intf[intf_nb].in_use == false) || (intf[intf_nb].nb_endpoint >= MAX_ENDPOINT_PER_INTERFACE)) {
+        return false;
+    }
+    intf[intf_nb].nb_endpoint++;
+
+    for (int i = 0; i < MAX_ENDPOINT_PER_INTERFACE; i++) {
+        if (intf[intf_nb].ep[i] == NULL) {
+            intf[intf_nb].ep[i] = ept;
+            return true;
+        }
+    }
+    return false;
+}
+
+void USBDeviceConnected::init(uint8_t hub_, uint8_t port_, bool lowSpeed_) {
+    USB_DBG("init dev: %p", this);
+    init();
+    hub_nb = hub_;
+    port = port_;
+    lowSpeed = lowSpeed_;
+}
+
+void USBDeviceConnected::disconnect() {
+    //for(int i = 0; i < MAX_INTF; i++) {
+    //    intf[i].detach.call();
+    //}
+    //init();
+}
+
+
+USBEndpoint * USBDeviceConnected::getEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir, uint8_t index) {
+    if (intf_nb >= MAX_INTF) {
+        return NULL;
+    }
+    for (int i = 0; i < MAX_ENDPOINT_PER_INTERFACE; i++) {
+        if ((intf[intf_nb].ep[i]->getType() == type) && (intf[intf_nb].ep[i]->getDir() == dir)) {
+            if(index) {
+                index--;
+            } else {
+                return intf[intf_nb].ep[i];
+            }
+        }
+    }
+    return NULL;
+}
+
+USBEndpoint * USBDeviceConnected::getEndpoint(uint8_t intf_nb, uint8_t index) {
+    if ((intf_nb >= MAX_INTF) || (index >= MAX_ENDPOINT_PER_INTERFACE)) {
+        return NULL;
+    }
+    return intf[intf_nb].ep[index];
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHost/USBDeviceConnected.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,139 @@
+/* mbed USBHost 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.
+ */
+#pragma once
+
+#include "USBEndpoint.h"
+#include "USBHostConf.h"
+
+class USBEndpoint;
+
+typedef struct {
+    bool in_use;
+    uint8_t nb_endpoint;
+    uint8_t intf_class;
+    uint8_t intf_subclass;
+    uint8_t intf_protocol;
+    USBEndpoint * ep[MAX_ENDPOINT_PER_INTERFACE];
+    //FunctionPointer detach;
+    //char name[10];
+} INTERFACE; 
+
+/**
+* USBDeviceConnected class
+*/
+class USBDeviceConnected {
+public:
+
+    /**
+    * Constructor
+    */
+    USBDeviceConnected();
+
+    /**
+    * Attach an USBEndpoint to this device
+    *
+    * @param intf_nb interface number
+    * @param ep pointeur on the USBEndpoint which will be attached
+    * @returns true if successful, false otherwise
+    */
+    bool addEndpoint(uint8_t intf_nb, USBEndpoint * ep);
+
+    /**
+    * Retrieve an USBEndpoint by its TYPE and DIRECTION
+    *
+    * @param intf_nb the interface on which to lookup the USBEndpoint
+    * @param type type of the USBEndpoint looked for
+    * @param dir direction of the USBEndpoint looked for
+    * @param index the index of the USBEndpoint whitin the interface
+    * @returns pointer on the USBEndpoint if found, NULL otherwise
+    */
+    USBEndpoint * getEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir, uint8_t index = 0);
+
+    /**
+    * Retrieve an USBEndpoint by its index
+    *
+    * @param intf_nb interface number
+    * @param index index of the USBEndpoint
+    * @returns pointer on the USBEndpoint if found, NULL otherwise
+    */
+    USBEndpoint * getEndpoint(uint8_t intf_nb, uint8_t index);
+
+    /**
+    * Add a new interface to this device
+    *
+    * @param intf_nb interface number
+    * @param intf_class interface class
+    * @param intf_subclass interface subclass
+    * @param intf_protocol interface protocol
+    * @returns true if successful, false otherwise
+    */
+    bool addInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol);
+
+    /**
+    * Get a specific interface
+    *
+    * @param index index of the interface to be fetched
+    * @returns interface
+    */
+    INTERFACE * getInterface(uint8_t index);
+
+    /**
+    * Disconnect the device by calling a callback function registered by a driver
+    */
+    void disconnect();
+
+    void init(uint8_t hub, uint8_t _port, bool _lowSpeed);
+    void setAddress(uint8_t addr_) { addr = addr_; };
+    void setVid(uint16_t vid_) { vid = vid_; };
+    void setPid(uint16_t pid_) { pid = pid_; };
+    void setClass(uint8_t device_class_) { device_class = device_class_; }
+    void setSubClass(uint8_t device_subclass_) { device_subclass = device_subclass_; };
+    void setProtocol(uint8_t pr) { proto = pr; };
+    void setEnumerated() { enumerated = true; };
+    void setNbIntf(uint8_t nb_intf) {nb_interf = nb_intf; };
+    void setSpeed(bool _lowSpeed) { lowSpeed = _lowSpeed; }
+    void setName(const char * name_, uint8_t intf_nb) { return; };
+    void setEpCtl(USBEndpoint* ep) { ep_ctl = ep; }
+
+    static int getNewAddress() {
+        static int addr = 1;
+        return addr++;
+    }
+    uint8_t getHub() { return hub_nb; };
+    uint8_t getAddress() { return addr; };
+    uint16_t getVid() { return vid; };
+    uint16_t getPid() { return pid; };
+    uint8_t getClass() { return device_class; };
+    bool getSpeed() { return lowSpeed; }
+    bool isEnumerated() { return enumerated; };
+    USBEndpoint* getEpCtl() { return ep_ctl; }
+
+private:
+    INTERFACE intf[MAX_INTF];
+    uint8_t hub_nb;
+    uint8_t port;
+    uint16_t vid;
+    uint16_t pid;
+    uint8_t addr;
+    uint8_t device_class;
+    uint8_t device_subclass;
+    uint8_t proto;
+    bool lowSpeed;
+    bool enumerated;
+    uint8_t nb_interf;
+    USBEndpoint* ep_ctl;
+    void init();
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHost/USBEndpoint.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,54 @@
+/* mbed USBHost 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.
+ */
+ 
+#pragma once
+#include "USBHostTypes.h"
+#include "USBDeviceConnected.h"
+class USBDeviceConnected;
+
+class USBEndpoint {
+public:
+    USBEndpoint() : data01_toggle(DATA0),address(0),MaxPacketSize(8) {
+        dev = NULL;
+    }
+    void setDevice(USBDeviceConnected* _dev) { dev = _dev; }
+    void setState(uint8_t st){}; // dummy
+    void setLengthTransferred(int len) { transferred = len; };
+    void setSize(int size) { MaxPacketSize = size; }
+    void setType(ENDPOINT_TYPE _type) { type = _type; };
+    void setAddress(uint8_t addr) { address = addr; }
+    void setData01(uint8_t data01) { data01_toggle = data01; }
+
+    USBDeviceConnected* getDevice() { return dev; }
+    ENDPOINT_TYPE getType() { return type; };
+    int getLengthTransferred() { return transferred; }
+    uint8_t getAddress(){ return address; };
+    int getSize() { return MaxPacketSize; }
+    ENDPOINT_DIRECTION getDir() { return (address & 0x80) ? IN : OUT; }
+    uint8_t getData01() { return data01_toggle; }
+    void toggleData01() {
+        data01_toggle = (data01_toggle == DATA0) ? DATA1 : DATA0;
+    }
+
+private:
+    ENDPOINT_TYPE type;
+    ENDPOINT_DIRECTION dir;
+    USBDeviceConnected* dev;
+    uint8_t data01_toggle; // DATA0,DATA1
+    uint8_t address;
+    int transferred;
+    int MaxPacketSize;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHost/USBHALHost.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,412 @@
+// Simple USBHost for FRDM-KL46Z
+#include "USBHALHost.h"
+
+template <bool>struct CtAssert;
+template <>struct CtAssert<true> {};
+#define CTASSERT(A) CtAssert<A>();
+
+
+#ifdef _USB_DBG
+#define USB_DBG(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");} while(0);
+#define USB_DBG_HEX(A,B) debug_hex(A,B)
+void debug_hex(uint8_t* buf, int size);
+#else
+#define USB_DBG(...) while(0)
+#define USB_DBG_HEX(A,B) while(0)
+#endif
+
+#ifdef _USB_TEST
+#define USB_TEST_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
+#define USB_TEST_ASSERT_FALSE(A) USB_TEST_ASSERT(!(A))
+#else
+#define USB_TEST_ASSERT(A) while(0)
+#define USB_TEST_ASSERT_FALSE(A) while(0)
+#endif
+
+#define BD_OWN_MASK        (1<<7)
+#define BD_DATA01_MASK     (1<<6)
+#define BD_KEEP_MASK       (1<<5)
+#define BD_NINC_MASK       (1<<4)
+#define BD_DTS_MASK        (1<<3)
+#define BD_STALL_MASK      (1<<2)
+
+#define TX    1
+#define RX    0
+
+#define EP0_BDT_IDX(dir, odd) (((2 * dir) + (1 * odd)))
+
+#define SETUP_TOKEN    0x0D
+#define IN_TOKEN       0x09
+#define OUT_TOKEN      0x01
+
+// for each endpt: 8 bytes
+struct BDT {
+    uint8_t   info;       // BD[0:7]
+    uint8_t   dummy;      // RSVD: BD[8:15]
+    uint16_t  byte_count; // BD[16:32]
+    uint32_t  address;    // Addr
+    void setBuffer(uint8_t* buf, int size) {
+        address = (uint32_t)buf;
+        byte_count = size;
+    }
+    uint8_t getStatus() {
+        return (info>>2)&0x0f;
+    }    
+};
+
+__attribute__((__aligned__(512))) BDT bdt[64];
+
+USBHALHost* USBHALHost::instHost;
+
+USBHALHost::USBHALHost() {
+    instHost = this;
+    report.clear();
+}
+
+void USBHALHost::init() {
+    // Disable IRQ
+    NVIC_DisableIRQ(USB0_IRQn);
+
+    // choose usb src as PLL
+    SIM->SOPT2 |= (SIM_SOPT2_USBSRC_MASK | SIM_SOPT2_PLLFLLSEL_MASK);
+
+    // enable OTG clock
+    SIM->SCGC4 |= SIM_SCGC4_USBOTG_MASK;
+
+    // USB Module Configuration
+    // Reset USB Module
+    USB0->USBTRC0 |= USB_USBTRC0_USBRESET_MASK;
+    while(USB0->USBTRC0 & USB_USBTRC0_USBRESET_MASK);
+
+    // Clear interrupt flag
+    USB0->ISTAT = 0xff;
+
+    // Set BDT Base Register
+    USB0->BDTPAGE1=(uint8_t)((uint32_t)bdt>>8);
+    USB0->BDTPAGE2=(uint8_t)((uint32_t)bdt>>16);
+    USB0->BDTPAGE3=(uint8_t)((uint32_t)bdt>>24);
+
+    // Set SOF threshold
+    USB0->SOFTHLD = USB_SOFTHLD_CNT(1);
+
+    // pulldown D+ and D-
+    USB0->USBCTRL = USB_USBCTRL_PDE_MASK;
+
+    USB0->USBTRC0 |= 0x40;
+
+    // Host mode
+    USB0->CTL |= USB_CTL_HOSTMODEEN_MASK;
+    // Desable SOF packet generation
+    USB0->CTL &= ~USB_CTL_USBENSOFEN_MASK;
+
+    NVIC_SetVector(USB0_IRQn, (uint32_t)_usbisr);
+    NVIC_EnableIRQ(USB0_IRQn);
+
+    bool lowSpeed = wait_attach();
+
+    for(int retry = 2; retry > 0; retry--) {
+        // Enable RESET
+        USB0->CTL |= USB_CTL_RESET_MASK;
+        wait_ms(500);
+        USB0->CTL &= ~USB_CTL_RESET_MASK;
+    
+        // Enable SOF
+        USB0->CTL |= USB_CTL_USBENSOFEN_MASK;
+        wait_ms(100);
+
+        // token transfer initialize
+        token_transfer_init();
+
+        USB0->INTEN |= USB_INTEN_TOKDNEEN_MASK|
+                       USB_INTEN_ERROREN_MASK;
+        USB0->ERREN |= USB_ERREN_PIDERREN_MASK|
+                       USB_ERREN_CRC5EOFEN_MASK|
+                       USB_ERREN_CRC16EN_MASK|
+                       USB_ERREN_DFN8EN_MASK|
+                       USB_ERREN_BTOERREN_MASK|
+                       USB_ERREN_DMAERREN_MASK|
+                       USB_ERREN_BTSERREN_MASK;
+
+        if (addDevice(0, 0, lowSpeed)) {
+            break;
+        }
+        USB_DBG("retry=%d", retry);
+        USB_TEST_ASSERT(retry > 1);
+    }
+}
+
+bool USBHALHost::wait_attach() {
+    attach_done = false;
+    USB0->INTEN = USB_INTEN_ATTACHEN_MASK;
+    while(!attach_done);
+    wait_ms(100);
+    USB_TEST_ASSERT_FALSE(USB0->CTL & USB_CTL_SE0_MASK);
+    root_lowSpeed = (USB0->CTL & USB_CTL_JSTATE_MASK) ? false : true;
+    return root_lowSpeed;
+}
+
+void USBHALHost::setAddr(int _addr, bool _lowSpeed) {
+    USB0->ADDR = (_lowSpeed ? USB_ADDR_LSEN_MASK : 0x00) | USB_ADDR_ADDR(_addr);
+}
+
+void USBHALHost::setEndpoint() {
+    USB0->ENDPOINT[0].ENDPT = (root_lowSpeed ? USB_ENDPT_HOSTWOHUB_MASK : 0x00)|
+                              USB_ENDPT_RETRYDIS_MASK|
+                              USB_ENDPT_EPCTLDIS_MASK|
+                              USB_ENDPT_EPRXEN_MASK|
+                              USB_ENDPT_EPTXEN_MASK|
+                              USB_ENDPT_EPHSHK_MASK;
+}
+
+void USBHALHost::token_transfer_init() {
+    tx_ptr = ODD;
+    rx_ptr = ODD;
+}
+
+int USBHALHost::token_setup(USBEndpoint* ep, SETUP_PACKET* setup, uint16_t wLength) {
+    USBDeviceConnected* dev = ep->getDevice();
+    for(int retry = 0;; retry++) {
+        token_ready();
+        USB0->ENDPOINT[0].ENDPT = (root_lowSpeed ? USB_ENDPT_HOSTWOHUB_MASK : 0x00) |
+                                  USB_ENDPT_RETRYDIS_MASK|
+                                  USB_ENDPT_EPRXEN_MASK|
+                                  USB_ENDPT_EPTXEN_MASK|
+                                  USB_ENDPT_EPHSHK_MASK;
+        CTASSERT(sizeof(SETUP_PACKET) == 8);
+        setup->wLength = wLength;
+        int idx = EP0_BDT_IDX(TX, tx_ptr);
+        bdt[idx].setBuffer((uint8_t*)setup, sizeof(SETUP_PACKET));
+        bdt[idx].info = BD_OWN_MASK |
+                        BD_DTS_MASK; // always data0
+        token_done = false;
+        USB0->TOKEN = USB_TOKEN_TOKENPID(SETUP_TOKEN)|
+                      USB_TOKEN_TOKENENDPT(ep->getAddress() & 0x7f);
+        while(!token_done);
+        LastStatus = bdt[idx].getStatus();
+        if (LastStatus == ACK) {
+            if (retry > 0) {
+                USB_DBG("retry=%d %02x", retry, prev_LastStatus);
+            }
+            break;
+        } else if (LastStatus == STALL) {
+            report.stall++;
+            return STALL;
+        }
+        if (retry > 10) {
+            USB_DBG("retry=%d", retry);
+            break;
+        }
+        prev_LastStatus = LastStatus;
+        wait_ms(100 * retry);
+    }
+    ep->setData01(DATA1); // next toggle
+    return LastStatus;
+}
+
+int USBHALHost::token_in(USBEndpoint* ep, uint8_t* data, int size, int retryLimit) {
+    for(int retry = 0;; retry++) {
+        token_ready();
+        int idx = EP0_BDT_IDX(RX, rx_ptr);
+        bdt[idx].setBuffer(data, size);
+        bdt[idx].info = BD_OWN_MASK|
+                        BD_DTS_MASK|
+                        (ep->getData01() == DATA1 ? BD_DATA01_MASK : 0);
+        token_done = false;
+        USB0->TOKEN = USB_TOKEN_TOKENPID(IN_TOKEN)|
+                      USB_TOKEN_TOKENENDPT(ep->getAddress()&0x7f);
+        while(!token_done);
+        LastStatus = bdt[idx].getStatus();
+        int len = bdt[idx].byte_count;
+        if (LastStatus == DATA0 || LastStatus == DATA1) {
+            USB_TEST_ASSERT(ep->getData01() == LastStatus);
+            ep->setData01(LastStatus == DATA0 ? DATA1 : DATA0);
+            if (retry > 0) {
+                //USB_DBG("len=%d retry=%d %02x", len, retry, prev_LastStatus);
+            }
+            return len;
+        } else if (LastStatus == STALL) {
+            report.stall++;
+            return -1;
+        } else if (LastStatus == NAK) {
+            report.nak++;
+            if (retry >= retryLimit) {
+                if (retryLimit > 0) {
+                    USB_DBG("retry=%d retryLimit=%d", retry, retryLimit);
+                }
+                return -1;
+            }
+            wait_ms(100 * retry);
+        } else if (LastStatus == Bus_Timeout) {
+            if (retry >= retryLimit) {
+                if (retryLimit > 0) {
+                    USB_DBG("Bus_Timeout retry=%d retryLimit=%d", retry, retryLimit);
+                }                
+                return -1;
+            }
+            wait_ms(500 + 100 * retry);
+        } else {
+            return -1;
+        }
+        prev_LastStatus = LastStatus;
+    }
+}
+
+int USBHALHost::token_out(USBEndpoint* ep, const uint8_t* data, int size, int retryLimit) {
+    for(int retry = 0;; retry++) {
+        token_ready();
+        int idx = EP0_BDT_IDX(TX, tx_ptr);
+        bdt[idx].setBuffer((uint8_t*)data, size);
+        bdt[idx].info = BD_OWN_MASK|
+                        BD_DTS_MASK|
+                       (ep->getData01() == DATA1 ? BD_DATA01_MASK : 0);
+        token_done = false;
+        USB0->TOKEN = USB_TOKEN_TOKENPID(OUT_TOKEN)|
+                      USB_TOKEN_TOKENENDPT(ep->getAddress()&0x7f);
+        while(!token_done);
+        LastStatus = bdt[idx].getStatus();
+        int len = bdt[idx].byte_count;
+        //USB_DBG("len=%d %02x", len, LastStatus);
+        if (LastStatus == ACK) {
+            ep->toggleData01();
+            if (retry > 0) {
+                USB_DBG("len=%d retry=%d %02x", len, retry, prev_LastStatus);
+            }
+            return len;
+        } else if (LastStatus == STALL) {
+            report.stall++;
+            return -1;
+        } else if (LastStatus == NAK) {
+            report.nak++;
+            if (retry > retryLimit) {
+                USB_DBG("retry=%d retryLimit=%d", retry, retryLimit);
+                return -1;
+            }
+            wait_ms(100 * retry);
+        } else {
+            return -1;
+        }
+        prev_LastStatus = LastStatus;
+    }
+}
+
+int USBHALHost::token_iso_in(USBEndpoint* ep, uint8_t* data, int size) {
+    while(USB0->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK); // TOKEN_BUSY ?
+    USB0->ISTAT |= USB_ISTAT_SOFTOK_MASK; // Clear SOF
+    while (!(USB0->ISTAT & USB_ISTAT_SOFTOK_MASK));
+    USB0->SOFTHLD = 0; // this is needed as without this you can get errors
+    USB0->ISTAT |= USB_ISTAT_SOFTOK_MASK; // clear SOF
+
+    USB0->ENDPOINT[0].ENDPT = USB_ENDPT_EPCTLDIS_MASK|
+                              USB_ENDPT_RETRYDIS_MASK|
+                              USB_ENDPT_EPRXEN_MASK|
+                              USB_ENDPT_EPTXEN_MASK;
+    int idx = EP0_BDT_IDX(RX, rx_ptr);
+    bdt[idx].setBuffer(data, size);
+    bdt[idx].info = BD_OWN_MASK|
+                    BD_DTS_MASK; // always DATA0
+    token_done = false;
+    USB0->TOKEN = USB_TOKEN_TOKENPID(IN_TOKEN)|
+                  USB_TOKEN_TOKENENDPT(ep->getAddress()&0x7f);
+    while(!token_done);
+    LastStatus = bdt[idx].getStatus();
+    int len = bdt[idx].byte_count;
+    if (LastStatus == DATA0) {
+        return len;
+    }
+    return -1;
+}
+
+void USBHALHost::token_ready() {
+    while(USB0->CTL & USB_CTL_TXSUSPENDTOKENBUSY_MASK) { // TOKEN_BUSY ?
+        wait_ms(1);
+    }
+    USB0->ISTAT |= USB_ISTAT_SOFTOK_MASK; // Clear SOF
+    while (!(USB0->ISTAT & USB_ISTAT_SOFTOK_MASK));
+    USB0->SOFTHLD = 0; // this is needed as without this you can get errors
+    USB0->ISTAT |= USB_ISTAT_SOFTOK_MASK; // clear SOF
+}
+
+void USBHALHost::_usbisr(void) {
+    if (instHost) {
+        instHost->UsbIrqhandler();
+    }
+}
+
+void USBHALHost::UsbIrqhandler() {
+    if (USB0->ISTAT & USB_ISTAT_TOKDNE_MASK) {
+        uint8_t stat = USB0->STAT;
+        ODD_EVEN next_ptr = (stat & USB_STAT_ODD_MASK) ? ODD : EVEN;
+        if (stat & USB_STAT_TX_MASK) {
+            tx_ptr = next_ptr;
+        } else {
+            rx_ptr = next_ptr;
+        }
+        USB0->ISTAT = USB_ISTAT_TOKDNE_MASK;
+        token_done = true;
+    }
+    if (USB0->ISTAT & USB_ISTAT_ATTACH_MASK) {
+        USB0->INTEN &= ~USB_INTEN_ATTACHEN_MASK;
+        USB0->ISTAT = USB_ISTAT_ATTACH_MASK;
+        attach_done = true;
+    }
+    if (USB0->ISTAT & USB_ISTAT_ERROR_MASK) {
+        uint8_t errstat = USB0->ERRSTAT;
+        if (errstat & USB_ERRSTAT_PIDERR_MASK) {
+            report.errstat_piderr++;
+        }
+        if (errstat & USB_ERRSTAT_CRC5EOF_MASK) {
+            report.errstat_crc5eof++;
+        }
+        if (errstat & USB_ERRSTAT_CRC16_MASK) {
+            report.errstat_crc16++;
+        }
+        if (errstat & USB_ERRSTAT_DFN8_MASK) {
+            report.errstat_dfn8++;
+        }
+        if (errstat & USB_ERRSTAT_BTOERR_MASK) {
+            report.errstat_btoerr++;
+        }
+        if (errstat & USB_ERRSTAT_DMAERR_MASK) {
+            report.errstat_dmaerr++;
+        }
+        if (errstat & USB_ERRSTAT_BTSERR_MASK) {
+            report.errstat_btoerr++;
+        }
+        USB0->ERRSTAT = errstat;
+        USB0->ISTAT = USB_ISTAT_ERROR_MASK;
+    }
+}
+
+void Report::clear() {
+    errstat_piderr = 0;
+    errstat_crc5eof = 0;
+    errstat_crc16 = 0;
+    errstat_dfn8 = 0;
+    errstat_btoerr = 0;
+    errstat_dmaerr = 0;
+    errstat_bsterr = 0;
+    //
+    nak = 0;
+}
+
+void Report::print_errstat() {
+    printf("ERRSTAT PID: %d, CRC5EOF: %d, CRC16: %d, DFN8: %d, BTO: %d, DMA: %d, BST: %d\n",
+        errstat_piderr, errstat_crc5eof,
+        errstat_crc16, errstat_dfn8,
+        errstat_btoerr, errstat_dmaerr, errstat_bsterr);
+}
+
+void debug_hex(uint8_t* buf, int size) {
+    int n = 0;
+    for(int i = 0; i < size; i++) {
+        fprintf(stderr, "%02x ", buf[i]);
+        if (++n >= 16) {
+            fprintf(stderr, "\n");
+            n = 0;
+        }
+    }
+    if (n > 0) {
+        fprintf(stderr, "\n");
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHost/USBHALHost.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,71 @@
+// Simple USBHost for FRDM-KL46Z
+#pragma once
+#include "mbed.h"
+#include "USBHostTypes.h"
+#include "USBEndpoint.h"
+
+struct SETUP_PACKET {
+    uint8_t bmRequestType;
+    uint8_t bRequest;
+    uint16_t wValue;
+    uint16_t wIndex;
+    uint16_t wLength;
+};
+
+#define GET_CONFIGURATION 8
+
+// TOK_PID[5:2]
+#define Bus_Timeout 0x00
+#define Data_Error 0x0f
+
+enum ODD_EVEN {
+    ODD = 0,
+    EVEN = 1,
+};
+
+class Report {
+public:
+    void clear();
+    void print_errstat();
+    // error count
+    uint32_t errstat_piderr; // USBx_ERRSTAT[PIDERR]
+    uint32_t errstat_crc5eof;// USBx_ERRSTAT[CRC5EOF]
+    uint32_t errstat_crc16;  // USBx_ERRSTAT[CRC16]
+    uint32_t errstat_dfn8;   // USBx_ERRSTAT[DFN8]
+    uint32_t errstat_btoerr; // USBx_ERRSTAT[BTOERR]
+    uint32_t errstat_dmaerr; // USBx_ERRSTAT[DMAERR]
+    uint32_t errstat_bsterr; // USBx_ERRSTAT[BTSERR]
+    //
+    uint32_t nak;
+    uint32_t stall;
+};
+
+class USBHALHost {
+public:
+    uint8_t LastStatus;
+    uint8_t prev_LastStatus;
+    Report report;
+
+protected:
+    USBHALHost();
+    void init();
+    virtual bool addDevice(int port, int hub, bool lowSpeed) = 0;
+    void setAddr(int addr, bool lowSpeed = false);
+    void setEndpoint();
+    void token_transfer_init();
+    int token_setup(USBEndpoint* ep, SETUP_PACKET* setup, uint16_t wLength = 0);
+    int token_in(USBEndpoint* ep, uint8_t* data = NULL, int size = 0, int retryLimit = 10);
+    int token_out(USBEndpoint* ep, const uint8_t* data = NULL, int size = 0, int retryLimit = 10);
+    int token_iso_in(USBEndpoint* ep, uint8_t* data, int size);
+    void token_ready();
+private:
+    static void _usbisr(void);
+    void UsbIrqhandler();
+    __IO bool attach_done;
+    __IO bool token_done;
+    bool wait_attach();
+    bool root_lowSpeed;
+    ODD_EVEN tx_ptr;
+    ODD_EVEN rx_ptr;
+    static USBHALHost * instHost;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHost/USBHost.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,423 @@
+// Simple USBHost for FRDM-KL46Z
+#include "USBHost.h"
+#include <algorithm>
+
+template <bool>struct CtAssert;
+template <>struct CtAssert<true> {};
+#define CTASSERT(A) CtAssert<A>();
+
+
+#ifdef _USB_DBG
+#define USB_DBG(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");} while(0);
+#define USB_DBG2(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");} while(0);
+#define USB_DBG_HEX(A,B) debug_hex(A,B)
+#define USB_DBG_ERRSTAT() report.print_errstat();
+void debug_hex(uint8_t* buf, int size);
+#else
+#define USB_DBG(...) while(0)
+#define USB_DBG2(...) while(0)
+#define USB_DBG_HEX(A,B) while(0)
+#define USB_DBG_ERRSTAT() while(0)
+#endif
+
+#ifdef _USB_TEST
+#define USB_TEST_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
+#define USB_TEST_ASSERT_FALSE(A) USB_TEST_ASSERT(!(A))
+#else
+#define USB_TEST_ASSERT(A) while(0)
+#define USB_TEST_ASSERT_FALSE(A) while(0)
+#endif
+
+#define USB_INFO(...) do{fprintf(stderr,__VA_ARGS__);}while(0);
+
+USBHost* USBHost::inst = NULL;
+
+USBHost* USBHost::getHostInst()
+{
+    if (inst == NULL) {
+        inst = new USBHost();
+        inst->init();
+    }
+    return inst;
+}
+
+USBHost::USBHost() {
+    DeviceLists_count = 0;
+}
+
+/* virtual */ bool USBHost::addDevice(int hub, int port, bool lowSpeed) {
+    USBDeviceConnected* dev = new USBDeviceConnected;
+    USBEndpoint* ep = new USBEndpoint;
+    ep->setDevice(dev);
+    dev->init(hub, port, lowSpeed);
+    dev->setAddress(0);
+    dev->setEpCtl(ep);
+    uint8_t desc[18];
+    wait_ms(100);
+
+    int rc = controlRead(dev, 0x80, GET_DESCRIPTOR, 1<<8, 0, desc, 8);
+    USB_TEST_ASSERT(rc == USB_TYPE_OK);
+    USB_DBG_HEX(desc, 8);
+    DeviceDescriptor* dev_desc = reinterpret_cast<DeviceDescriptor*>(desc);
+    ep->setSize(dev_desc->bMaxPacketSize);
+
+    int new_addr = USBDeviceConnected::getNewAddress();
+    rc = controlWrite(dev, 0x00, SET_ADDRESS, new_addr, 0, NULL, 0);
+    USB_TEST_ASSERT(rc == USB_TYPE_OK);
+    dev->setAddress(new_addr);
+    wait_ms(100);
+
+    rc = controlRead(dev, 0x80, GET_DESCRIPTOR, 1<<8, 0, desc, sizeof(desc));
+    USB_TEST_ASSERT(rc == USB_TYPE_OK);
+    USB_DBG_HEX(desc, sizeof(desc));
+
+    dev->setVid(dev_desc->idVendor);
+    dev->setPid(dev_desc->idProduct);
+    dev->setClass(dev_desc->bDeviceClass);
+    USB_INFO("hub: %d port: %d speed: %s vid: %04x pid: %04x class: %02x addr: %d\n",
+        hub, port, (lowSpeed ? "low " : "full"), dev->getVid(), dev->getPid(), dev->getClass(),
+        dev->getAddress());
+
+    USB_TEST_ASSERT(DeviceLists_count < MAX_DEVICE_CONNECTED);
+    DeviceLists[DeviceLists_count++] = dev;
+
+    if (dev->getClass() == HUB_CLASS) {
+        const int config = 1;
+        int rc = controlWrite(dev, 0x00, SET_CONFIGURATION, config, 0, NULL, 0);
+        USB_TEST_ASSERT(rc == USB_TYPE_OK);
+        wait_ms(100);
+        Hub(dev);
+    }
+    return true;
+}
+
+// enumerate a device with the control USBEndpoint
+USB_TYPE USBHost::enumerate(USBDeviceConnected * dev, IUSBEnumerator* pEnumerator)
+{
+    if (dev->getClass() == HUB_CLASS) { // skip hub class
+        return USB_TYPE_OK;
+    }
+    uint8_t desc[18];
+    USB_TYPE rc = controlRead(dev, 0x80, GET_DESCRIPTOR, 1<<8, 0, desc, sizeof(desc));
+    USB_TEST_ASSERT(rc == USB_TYPE_OK);
+    USB_DBG_HEX(desc, sizeof(desc));
+    if (rc != USB_TYPE_OK) {
+        return rc;
+    }
+    DeviceDescriptor* dev_desc = reinterpret_cast<DeviceDescriptor*>(desc);
+    dev->setClass(dev_desc->bDeviceClass);
+    pEnumerator->setVidPid(dev->getVid(), dev->getPid());
+
+    rc = controlRead(dev, 0x80, GET_DESCRIPTOR, 2<<8, 0, desc, 4);
+    USB_TEST_ASSERT(rc == USB_TYPE_OK);
+    USB_DBG_HEX(desc, 4);
+
+    int TotalLength = desc[2]|desc[3]<<8;
+    uint8_t* buf = new uint8_t[TotalLength];
+    rc = controlRead(dev, 0x80, GET_DESCRIPTOR, 2<<8, 0, buf, TotalLength);
+    USB_TEST_ASSERT(rc == USB_TYPE_OK);
+    //USB_DBG_HEX(buf, TotalLength);
+
+    // Parse the configuration descriptor
+    parseConfDescr(dev, buf, TotalLength, pEnumerator);
+    delete[] buf;
+    // only set configuration if not enumerated before
+    if (!dev->isEnumerated()) {
+        USB_DBG("Set configuration 1 on dev: %p", dev);
+        // sixth step: set configuration (only 1 supported)
+        int config = 1;
+        USB_TYPE res = controlWrite(dev, 0x00, SET_CONFIGURATION, config, 0, NULL, 0);
+        if (res != USB_TYPE_OK) {
+            USB_DBG("SET CONF FAILED");
+            return res;
+        }
+        // Some devices may require this delay
+        wait_ms(100);
+        dev->setEnumerated();
+        // Now the device is enumerated!
+        USB_DBG("dev %p is enumerated", dev);
+    }
+    return USB_TYPE_OK;
+}
+
+// this method fills the USBDeviceConnected object: class,.... . It also add endpoints found in the descriptor.
+void USBHost::parseConfDescr(USBDeviceConnected * dev, uint8_t * conf_descr, uint32_t len, IUSBEnumerator* pEnumerator)
+{
+    uint32_t index = 0;
+    uint32_t len_desc = 0;
+    uint8_t id = 0;
+    int nb_endpoints_used = 0;
+    USBEndpoint * ep = NULL;
+    uint8_t intf_nb = 0;
+    bool parsing_intf = false;
+    uint8_t current_intf = 0;
+    EndpointDescriptor* ep_desc;
+
+    while (index < len) {
+        len_desc = conf_descr[index];
+        id = conf_descr[index+1];
+        switch (id) {
+            case CONFIGURATION_DESCRIPTOR:
+                USB_DBG("dev: %p has %d intf", dev, conf_descr[4]);
+                dev->setNbIntf(conf_descr[4]);
+                break;
+            case INTERFACE_DESCRIPTOR:
+                if(pEnumerator->parseInterface(conf_descr[index + 2], conf_descr[index + 5], conf_descr[index + 6], conf_descr[index + 7])) {
+                    if (intf_nb++ <= MAX_INTF) {
+                        current_intf = conf_descr[index + 2];
+                        dev->addInterface(current_intf, conf_descr[index + 5], conf_descr[index + 6], conf_descr[index + 7]);
+                        nb_endpoints_used = 0;
+                        USB_DBG("ADD INTF %d on device %p: class: %d, subclass: %d, proto: %d", current_intf, dev, conf_descr[index + 5],conf_descr[index + 6],conf_descr[index + 7]);
+                    } else {
+                        USB_DBG("Drop intf...");
+                    }
+                    parsing_intf = true;
+                } else {
+                    parsing_intf = false;
+                }
+                break;
+            case ENDPOINT_DESCRIPTOR:
+                ep_desc = reinterpret_cast<EndpointDescriptor*>(conf_descr+index);
+                if (parsing_intf && (intf_nb <= MAX_INTF) ) {
+                    if (nb_endpoints_used < MAX_ENDPOINT_PER_INTERFACE) {
+                        ENDPOINT_TYPE type = (ENDPOINT_TYPE)(ep_desc->bmAttributes & 0x03);
+                        ENDPOINT_DIRECTION dir = (ep_desc->bEndpointAddress & 0x80) ? IN : OUT;
+                        if(pEnumerator->useEndpoint(current_intf, type, dir)) {
+                            ep = new USBEndpoint;
+                            ep->setDevice(dev);
+                            ep->setType(type);
+                            ep->setAddress(ep_desc->bEndpointAddress);
+                            ep->setSize(ep_desc->wMaxPacketSize);
+                            USB_DBG("ADD USBEndpoint %p, on interf %d on device %p", ep, current_intf, dev);
+                            dev->addEndpoint(current_intf, ep);
+                            nb_endpoints_used++;
+                        }
+                    }
+                }
+                break;
+            case HID_DESCRIPTOR:
+                //lenReportDescr = conf_descr[index + 7] | (conf_descr[index + 8] << 8);
+                break;
+            default:
+                break;
+        }
+        index += len_desc;
+    }
+}
+
+USB_TYPE USBHost::controlRead(USBDeviceConnected* dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len) {
+    SETUP_PACKET setup = {requestType, request, value, index};
+    int result = ControlRead(dev, &setup, buf, len);
+    //USB_DBG2("result=%d %02x", result, LastStatus);
+    return (result >= 0) ? USB_TYPE_OK : USB_TYPE_ERROR;
+}
+
+USB_TYPE USBHost::controlWrite(USBDeviceConnected* dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len) {
+    SETUP_PACKET setup = {requestType, request, value, index};
+    int result = ControlWrite(dev, &setup, buf, len);
+    if (result >= 0) {
+        return USB_TYPE_OK;
+    }
+    USB_DBG("result=%d %02x", result, LastStatus);
+    USB_DBG_HEX(buf, len);
+    return USB_TYPE_ERROR;
+}
+
+USB_TYPE USBHost::bulkRead(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking) {
+    USB_TEST_ASSERT(blocking);
+    int result = BulkRead(ep, buf, len);
+    if (result >= 0) {
+        return USB_TYPE_OK;
+    }
+    //USB_DBG2("result=%d %02x", result, host->LastStatus);
+    return USB_TYPE_ERROR;
+}
+
+USB_TYPE USBHost::bulkWrite(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking) {
+    USB_TEST_ASSERT(blocking);
+    int result = BulkWrite(ep, buf, len);
+    if (result >= 0) {
+        return USB_TYPE_OK;
+    }
+    USB_DBG2("result=%d %02x", result, LastStatus);
+    return USB_TYPE_ERROR;
+}
+
+USB_TYPE USBHost::interruptRead(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking) {
+    int result = InterruptRead(ep, buf, len);
+    if (result >= 0) {
+        return USB_TYPE_OK;
+    }
+    return USB_TYPE_ERROR;
+}
+
+int USBHost::ControlRead(USBDeviceConnected* dev, SETUP_PACKET* setup, uint8_t* data, int size) {
+    USB_TEST_ASSERT(dev);
+    USBEndpoint* ep = dev->getEpCtl();
+    USB_TEST_ASSERT(ep);
+    setAddr(dev->getAddress(), dev->getSpeed());
+    token_setup(ep, setup, size); // setup stage
+    if (LastStatus != ACK) {
+        USB_DBG("setup %02x", LastStatus);
+        return -1;
+    }
+    int read_len = 0;
+    while(read_len < size) {
+        int size2 = std::min(size-read_len, ep->getSize());
+        int result = token_in(ep, data+read_len, size2);
+        //USB_DBG("token_in result=%d %02x", result, LastStatus);
+        if (result < 0) {
+            USB_DBG("token_in %d/%d %02x", read_len, size, LastStatus);
+            return result;
+        }
+        read_len += result;
+        if (result < ep->getSize()) {
+            break;
+        }
+    }    
+    ep->setData01(DATA1);
+    int result = token_out(ep); // status stage
+    if (result < 0) {
+        USB_DBG("status token_out %02x", LastStatus);
+        if (LastStatus == STALL) {
+            ep->setLengthTransferred(read_len);
+            return read_len;
+        }
+        return result;
+    }
+    ep->setLengthTransferred(read_len);
+    return read_len;
+}
+
+int USBHost::ControlWrite(USBDeviceConnected* dev, SETUP_PACKET* setup, uint8_t* data, int size) {
+    USB_TEST_ASSERT(dev);
+    USBEndpoint* ep = dev->getEpCtl();
+    USB_TEST_ASSERT(ep);
+    setAddr(dev->getAddress(), dev->getSpeed());
+    token_setup(ep, setup, size); // setup stage
+    if (LastStatus != ACK) {
+        USB_DBG("setup %02x", LastStatus);
+        return -1;
+    }
+    int write_len = 0;
+    if (data != NULL) {
+        write_len = token_out(ep, data, size);
+        if (write_len < 0) {
+            return -1;
+        }
+    }
+    ep->setData01(DATA1);
+    int result = token_in(ep); // status stage
+    if (result < 0) {
+        USB_DBG("result=%d %02x", result, LastStatus);
+        //return result;
+    }
+    ep->setLengthTransferred(write_len);
+    return write_len;
+}
+
+int USBHost::InterruptRead(USBEndpoint* ep, uint8_t* data, int size) {
+    USB_TEST_ASSERT(ep);
+    USBDeviceConnected* dev = ep->getDevice();
+    USB_TEST_ASSERT(dev);
+    setAddr(dev->getAddress(), dev->getSpeed());
+    setEndpoint();
+    const int retryLimit = 0;
+    int read_len = 0;
+    for(int n = 0; read_len < size; n++) {
+        int size2 = std::min(size-read_len, ep->getSize());
+        int result = token_in(ep, data+read_len, size2, retryLimit);
+        if (result < 0) {
+            if (LastStatus == NAK) {
+                if (n == 0) {
+                    return -1;
+                }
+                break;
+            }
+            //USB_DBG("token_in result=%d %02x", result, LastStatus);
+            return result;
+        }
+        read_len += result;
+        if (result < ep->getSize()) {
+            break;
+        }
+    }
+    ep->setLengthTransferred(read_len);
+    return read_len;
+}
+
+int USBHost::BulkRead(USBEndpoint* ep, uint8_t* data, int size, int timeout_ms) {
+    USB_TEST_ASSERT(ep);
+    USBDeviceConnected* dev = ep->getDevice();
+    USB_TEST_ASSERT(dev);
+    setAddr(dev->getAddress());
+    setEndpoint();
+    int retryLimit = (timeout_ms == 0) ? 0 : 10;
+    int read_len = 0;
+    Timer t;
+    for(int n = 0; read_len < size; n++) {
+        int size2 = std::min(size-read_len, ep->getSize());
+        int result = token_in(ep, data+read_len, size2, retryLimit);
+        if (result < 0) {
+            if (LastStatus == NAK) {
+                if (n == 0) {
+                    return -1;
+                }
+                break;
+            }
+            //USB_DBG("token_in result=%d %02x", result, LastStatus);
+            return result;
+        }
+        read_len += result;
+        if (result < ep->getSize()) {
+            break;
+        }
+        if (timeout_ms > 0 && t.read_ms() > timeout_ms) {
+            USB_DBG("timeout_ms: %d", timeout_ms);
+            break;
+        }
+    }
+    ep->setLengthTransferred(read_len);
+    return read_len;
+}
+
+int USBHost::BulkWrite(USBEndpoint* ep, const uint8_t* data, int size) {
+    USB_TEST_ASSERT(ep);
+    USBDeviceConnected* dev = ep->getDevice();
+    USB_TEST_ASSERT(dev);
+    setAddr(dev->getAddress());
+    setEndpoint();
+    int write_len = 0;
+    for(int n = 0; write_len < size; n++) {
+        int size2 = std::min(size-write_len, ep->getSize());
+        int result = token_out(ep, data+write_len, size2);
+        if (result < 0) {
+            if (LastStatus == NAK) {
+                if (n == 0) {
+                    return -1;
+                }
+                break;
+            }
+            USB_DBG("token_out result=%d %02x", result, LastStatus);
+            return result;
+        }
+        write_len += result;
+        if (result < ep->getSize()) {
+            break;
+        }
+    }
+    ep->setLengthTransferred(write_len);
+    return write_len;
+}
+
+int USBHost::IsochronousRead(USBEndpoint* ep, uint8_t* data, int size) {
+    USBDeviceConnected* dev = ep->getDevice();
+    USB_TEST_ASSERT(dev);
+    setAddr(dev->getAddress());
+    int result = token_iso_in(ep, data, size);
+    if (result >= 0) {
+         ep->setLengthTransferred(result);
+    }
+    return result;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHost/USBHost.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,157 @@
+// Simple USBHost for FRDM-KL46Z
+#pragma once
+#include "mbed.h"
+#include "USBHALHost.h"
+#include "USBDeviceConnected.h"
+#include "IUSBEnumerator.h"
+#include "USBHostConf.h"
+#include "USBEndpoint.h"
+
+class USBHost : public USBHALHost {
+public:
+    /**
+    * Static method to create or retrieve the single USBHost instance
+    */
+    static USBHost* getHostInst();
+
+    /**
+    * Control read: setup stage, data stage and status stage
+    *
+    * @param dev the control read will be done for this device
+    * @param requestType request type
+    * @param request request
+    * @param value value
+    * @param index index
+    * @param buf pointer on a buffer where will be store the data received
+    * @param len length of the transfer
+    *
+    * @returns status of the control read
+    */
+    USB_TYPE controlRead(USBDeviceConnected * dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len);
+
+    /**
+    * Control write: setup stage, data stage and status stage
+    *
+    * @param dev the control write will be done for this device
+    * @param requestType request type
+    * @param request request
+    * @param value value
+    * @param index index
+    * @param buf pointer on a buffer which will be written
+    * @param len length of the transfer
+    *
+    * @returns status of the control write
+    */
+    USB_TYPE controlWrite(USBDeviceConnected * dev, uint8_t requestType, uint8_t request, uint32_t value, uint32_t index, uint8_t * buf, uint32_t len);
+    /**
+    * Bulk read
+    *
+    * @param dev the bulk transfer will be done for this device
+    * @param ep USBEndpoint which will be used to read a packet
+    * @param buf pointer on a buffer where will be store the data received
+    * @param len length of the transfer
+    * @param blocking if true, the read is blocking (wait for completion)
+    *
+    * @returns status of the bulk read
+    */
+    USB_TYPE bulkRead(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true);
+
+    /**
+    * Bulk write
+    *
+    * @param dev the bulk transfer will be done for this device
+    * @param ep USBEndpoint which will be used to write a packet
+    * @param buf pointer on a buffer which will be written
+    * @param len length of the transfer
+    * @param blocking if true, the write is blocking (wait for completion)
+    *
+    * @returns status of the bulk write
+    */
+    USB_TYPE bulkWrite(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true);
+
+    /**
+    * Interrupt read
+    *
+    * @param dev the interrupt transfer will be done for this device
+    * @param ep USBEndpoint which will be used to write a packet
+    * @param buf pointer on a buffer which will be written
+    * @param len length of the transfer
+    * @param blocking if true, the read is blocking (wait for completion)
+    *
+    * @returns status of the interrupt read
+    */
+    USB_TYPE interruptRead(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true);
+
+    /**
+    * Interrupt write
+    *
+    * @param dev the interrupt transfer will be done for this device
+    * @param ep USBEndpoint which will be used to write a packet
+    * @param buf pointer on a buffer which will be written
+    * @param len length of the transfer
+    * @param blocking if true, the write is blocking (wait for completion)
+    *
+    * @returns status of the interrupt write
+    */
+    USB_TYPE interruptWrite(USBDeviceConnected * dev, USBEndpoint * ep, uint8_t * buf, uint32_t len, bool blocking = true);
+
+    /**
+    * Enumerate a device.
+    *
+    * @param dev device which will be enumerated
+    *
+    * @returns status of the enumeration
+    */
+    USB_TYPE enumerate(USBDeviceConnected * dev, IUSBEnumerator* pEnumerator);
+
+    /**
+    * Get a device
+    *
+    * @param index index of the device which will be returned
+    *
+    * @returns pointer on the "index" device
+    */
+    USBDeviceConnected * getDevice(uint8_t index) {
+        return (index < DeviceLists_count) ? DeviceLists[index] : NULL;
+    }
+
+    /**
+     *  register a driver into the host associated with a callback function called when the device is disconnected
+     *
+     *  @param dev device
+     *  @param intf interface number
+     *  @param tptr pointer to the object to call the member function on
+     *  @param mptr pointer to the member function to be called
+     */
+    template<typename T>
+    void registerDriver(USBDeviceConnected * dev, uint8_t intf, T* tptr, void (T::*mptr)(void)) {
+    }
+
+    int BulkRead(USBEndpoint*ep, uint8_t* data, int size, int timeout_ms = -1);
+    int IsochronousRead(USBEndpoint*ep, uint8_t* data, int size);
+
+private:
+    USBHost();
+    static USBHost* inst;
+    virtual bool addDevice(int hub, int port, bool lowSpeed);
+    void root_enumeration(USBDeviceConnected* dev);
+    void parseConfDescr(USBDeviceConnected* dev, uint8_t* conf_descr, uint32_t len, IUSBEnumerator* pEnumerator);
+    USBDeviceConnected* DeviceLists[MAX_DEVICE_CONNECTED];
+    int DeviceLists_count;
+
+    int ControlRead(USBDeviceConnected* dev, SETUP_PACKET* setup, uint8_t* data, int size);
+    int ControlWrite(USBDeviceConnected* dev, SETUP_PACKET* setup, uint8_t* data = NULL, int size = 0);
+    int BulkWrite(USBEndpoint*ep, const uint8_t* data, int size);
+    int InterruptRead(USBEndpoint*ep, uint8_t* data, int size);
+
+    // USB HUB
+    bool Hub(USBDeviceConnected* dev);
+    int SetPortPower(USBDeviceConnected* dev, int port);
+    int ClearPortPower(USBDeviceConnected* dev, int port);
+    int PortReset(USBDeviceConnected* dev, int port);
+    int SetPortFeature(USBDeviceConnected* dev, int feature, int index);
+    int ClearPortFeature(USBDeviceConnected* dev, int feature, int index);
+    int SetPortReset(USBDeviceConnected* dev, int port);
+    int GetPortStatus(USBDeviceConnected* dev, int port, uint32_t* status);
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHost/USBHostConf.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,86 @@
+/* mbed USBHost 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 USBHOST_CONF_H
+#define USBHOST_CONF_H
+
+/*
+* Maximum number of devices that can be connected
+* to the usb host
+*/
+#define MAX_DEVICE_CONNECTED        5
+
+/*
+* Maximum of Hub connected to the usb host
+*/
+#define MAX_HUB_NB                  2
+
+/*
+* Maximum number of ports on a USB hub
+*/
+#define MAX_HUB_PORT                4
+
+/*
+* Enable USBHostMSD
+*/
+#define USBHOST_MSD                 1
+
+/*
+* Enable USBHostKeyboard
+*/
+#define USBHOST_KEYBOARD            1
+
+/*
+* Enable USBHostMouse
+*/
+#define USBHOST_MOUSE               1
+
+/*
+* Enable USBHostSerial or USBHostMultiSerial (if set > 1)
+*/
+#define USBHOST_SERIAL              1
+
+/*
+* Enable USB3Gmodule
+*/
+#define USBHOST_3GMODULE            1 
+
+/*
+* Maximum number of interfaces of a usb device
+*/
+#define MAX_INTF                    4
+
+/*
+* Maximum number of endpoints on each interface
+*/
+#define MAX_ENDPOINT_PER_INTERFACE  3
+
+/*
+* Maximum number of endpoint descriptors that can be allocated
+*/
+#define MAX_ENDPOINT                (MAX_DEVICE_CONNECTED * MAX_INTF * MAX_ENDPOINT_PER_INTERFACE)
+
+/*
+* Maximum number of transfer descriptors that can be allocated
+*/
+#define MAX_TD                      (MAX_ENDPOINT*2)
+
+/*
+* usb_thread stack size
+*/
+#define USB_THREAD_STACK            (256*4 + MAX_HUB_NB*256*4)
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHost/USBHostHub.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,128 @@
+#include "USBHost.h"
+
+#ifdef _USB_DBG
+#define USB_DBG(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");} while(0);
+#define USB_DBG_HEX(A,B) debug_hex(A,B)
+extern void debug_hex(uint8_t* buf, int size);
+#else
+#define USB_DBG(...) while(0)
+#define USB_DBG_HEX(A,B) while(0)
+#endif
+
+#ifdef _USB_TEST
+#define USB_TEST_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
+#define USB_TEST_ASSERT_FALSE(A) USB_TEST_ASSERT(!(A))
+#else
+#define USB_TEST_ASSERT(A) while(0)
+#define USB_TEST_ASSERT_FALSE(A) while(0)
+#endif
+
+#define PORT_CONNECTION   0
+#define PORT_ENABLE       1
+#define PORT_SUSPEND      2
+#define PORT_OVER_CURRENT 3
+#define PORT_RESET        4
+#define PORT_POWER        8
+#define PORT_LOW_SPEED    9
+
+#define C_PORT_CONNECTION   16
+#define C_PORT_ENABLE       17
+#define C_PORT_SUSPEND      18
+#define C_PORT_OVER_CURRENT 19
+#define C_PORT_RESET        20
+
+bool USBHost::Hub(USBDeviceConnected* dev) {
+    HubDescriptor hubdesc;
+    // get HUB descriptor
+    int rc = controlRead(dev, 
+                        USB_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS,
+                        GET_DESCRIPTOR,
+                        0x29 << 8, 0, reinterpret_cast<uint8_t*>(&hubdesc), 
+                        sizeof(HubDescriptor));
+    USB_TEST_ASSERT(rc == USB_TYPE_OK);
+    if (rc != USB_TYPE_OK) {
+        return false;
+    }
+    USB_DBG_HEX((uint8_t*)&hubdesc, sizeof(hubdesc));
+
+    uint32_t status;
+    rc = controlRead( dev,
+                      0xa0, 0, 0, 0, reinterpret_cast<uint8_t*>(&status), 4);
+    USB_TEST_ASSERT(rc == USB_TYPE_OK);
+    if (rc != USB_TYPE_OK) {
+        return false;
+    }
+    USB_DBG("HUB STATUS: %08X\n", status);
+
+    for(int i = 1; i <= hubdesc.bNbrPorts; i++) {
+        SetPortPower(dev, i); // power on
+        wait_ms(hubdesc.bPwrOn2PwrGood*2);
+        uint32_t status;
+        GetPortStatus(dev, i, &status);
+        USB_DBG("port: %d status: %08X\n", i, status);
+        if (status & 0x010000) { // Connect Status Change, has changed
+            USB_TEST_ASSERT(status & 0x000001);
+            ClearPortFeature(dev, C_PORT_CONNECTION, i);
+            int lowSpeed = 0;
+            if (status & 0x0200) {
+                lowSpeed = 1;
+            }
+            PortReset(dev, i);
+            if (!addDevice(1, i, lowSpeed)) {
+                ClearPortPower(dev, i); // power off
+            }
+        } else {
+            ClearPortPower(dev, i); // power off
+        }
+    }
+    return false;
+}
+
+
+int USBHost::SetPortPower(USBDeviceConnected* dev, int port)
+{
+    return SetPortFeature(dev, PORT_POWER, port);
+}
+
+int USBHost::ClearPortPower(USBDeviceConnected* dev, int port)
+{
+    return ClearPortFeature(dev, PORT_POWER, port);
+}
+
+int USBHost::SetPortFeature(USBDeviceConnected* dev, int feature, int index)
+{
+    return controlWrite(dev, 0x23, SET_FEATURE,feature,index,0,0);
+}
+
+int USBHost::ClearPortFeature(USBDeviceConnected* dev, int feature, int index)
+{
+    return controlWrite(dev, 0x23, CLEAR_FEATURE,feature,index,0,0);
+}
+
+int USBHost::SetPortReset(USBDeviceConnected* dev, int port)
+{
+    return SetPortFeature(dev, PORT_RESET, port);
+}
+
+int USBHost::GetPortStatus(USBDeviceConnected* dev, int port, uint32_t* status)
+{
+    return controlRead(dev, 0xa3, GET_STATUS, 0, port, (uint8_t*)status, 4);
+}
+
+int USBHost::PortReset(USBDeviceConnected* dev, int port)
+{
+    USB_DBG("%p port=%d\n", this, port);
+    USB_TEST_ASSERT(port >= 1);
+    SetPortReset(dev, port);
+    // wait reset
+    for(int i = 0; i < 100; i++) {
+        uint32_t status;    
+        GetPortStatus(dev, port, &status);
+        USB_DBG("RESET port: %d status: %08X\n", port, status);
+        if (status & 0x100000) { // Reset change , Reset complete
+            return USB_TYPE_OK;
+        }
+        wait_ms(5);
+     }
+     return USB_TYPE_ERROR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHost/USBHostTypes.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,167 @@
+/* mbed USBHost 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 USB_INC_H
+#define USB_INC_H
+
+#include "mbed.h"
+#include "toolchain.h"
+
+enum USB_TYPE {
+    USB_TYPE_OK = 0,
+
+    // completion code
+    USB_TYPE_CRC_ERROR = 1,
+    USB_TYPE_BIT_STUFFING_ERROR = 2,
+    USB_TYPE_DATA_TOGGLE_MISMATCH_ERROR = 3,
+    USB_TYPE_STALL_ERROR = 4,
+    USB_TYPE_DEVICE_NOT_RESPONDING_ERROR = 5,
+    USB_TYPE_PID_CHECK_FAILURE_ERROR = 6,
+    USB_TYPE_UNEXPECTED_PID_ERROR = 7,
+    USB_TYPE_DATA_OVERRUN_ERROR = 8,
+    USB_TYPE_DATA_UNDERRUN_ERROR = 9,
+    USB_TYPE_RESERVED = 9,
+    USB_TYPE_RESERVED_ = 10,
+    USB_TYPE_BUFFER_OVERRUN_ERROR = 12,
+    USB_TYPE_BUFFER_UNDERRUN_ERROR = 13,
+
+    // general usb state
+    USB_TYPE_DISCONNECTED = 14,
+    USB_TYPE_FREE = 15,
+    USB_TYPE_IDLE = 16,
+    USB_TYPE_PROCESSING = 17,
+
+    USB_TYPE_ERROR = 18,
+};
+
+
+enum ENDPOINT_DIRECTION {
+    OUT = 1,
+    IN
+};
+
+enum ENDPOINT_TYPE {
+    CONTROL_ENDPOINT = 0,
+    ISOCHRONOUS_ENDPOINT,
+    BULK_ENDPOINT,
+    INTERRUPT_ENDPOINT
+};
+
+#define AUDIO_CLASS     0x01
+#define CDC_CLASS       0x02
+#define HID_CLASS       0x03
+#define MSD_CLASS       0x08
+#define HUB_CLASS       0x09
+#define SERIAL_CLASS    0x0A
+
+#define  DEVICE_DESCRIPTOR                     (1)
+#define  CONFIGURATION_DESCRIPTOR              (2)
+#define  INTERFACE_DESCRIPTOR                  (4)
+#define  ENDPOINT_DESCRIPTOR                   (5)
+#define  HID_DESCRIPTOR                        (33)
+
+//  ----------- Control RequestType Fields  ----------- 
+#define  USB_DEVICE_TO_HOST         0x80
+#define  USB_HOST_TO_DEVICE         0x00
+#define  USB_REQUEST_TYPE_CLASS     0x20
+#define  USB_REQUEST_TYPE_STANDARD  0x00
+#define  USB_RECIPIENT_DEVICE       0x00
+#define  USB_RECIPIENT_INTERFACE    0x01
+#define  USB_RECIPIENT_ENDPOINT     0x02
+
+// -------------- USB Standard Requests  -------------- 
+#define  GET_STATUS                 0x00
+#define  SET_FEATURE                0x03
+#define  SET_ADDRESS                0x05
+#define  GET_DESCRIPTOR             0x06
+#define  SET_CONFIGURATION          0x09
+#define  SET_INTERFACE              0x0b
+#define  CLEAR_FEATURE              0x01
+
+// -------------- USB Descriptor Length  -------------- 
+#define DEVICE_DESCRIPTOR_LENGTH            0x12
+#define CONFIGURATION_DESCRIPTOR_LENGTH     0x09
+
+// PID
+#define DATA0 0x03
+#define DATA1 0x0b
+#define ACK   0x02
+#define STALL 0x0e
+#define NAK   0x0a
+
+#pragma pack(push,1)
+typedef struct {
+    uint8_t bLength;            
+    uint8_t bDescriptorType;    
+    uint16_t bcdUSB;            
+    uint8_t bDeviceClass;       
+    uint8_t bDeviceSubClass;    
+    uint8_t bDeviceProtocol;    
+    uint8_t bMaxPacketSize;     
+    uint16_t idVendor;          
+    uint16_t idProduct;         
+    uint16_t bcdDevice;         
+    uint8_t iManufacturer;      
+    uint8_t iProduct;           
+    uint8_t iSerialNumber;      
+    uint8_t bNumConfigurations; 
+} PACKED DeviceDescriptor;
+
+typedef struct {
+    uint8_t bLength;               
+    uint8_t bDescriptorType;       
+    uint16_t wTotalLength;         
+    uint8_t bNumInterfaces;        
+    uint8_t bConfigurationValue;   
+    uint8_t iConfiguration;        
+    uint8_t bmAttributes;          
+    uint8_t bMaxPower;             
+} PACKED ConfigurationDescriptor; 
+
+typedef struct {
+    uint8_t bLength;                 
+    uint8_t bDescriptorType;   
+    uint8_t bInterfaceNumber;  
+    uint8_t bAlternateSetting; 
+    uint8_t bNumEndpoints;     
+    uint8_t bInterfaceClass;   
+    uint8_t bInterfaceSubClass;
+    uint8_t bInterfaceProtocol;
+    uint8_t iInterface;        
+} InterfaceDescriptor; 
+
+typedef struct {
+    uint8_t bLength;          
+    uint8_t bDescriptorType;  
+    uint8_t bEndpointAddress; 
+    uint8_t bmAttributes;     
+    uint16_t wMaxPacketSize;  
+    uint8_t bInterval;        
+} EndpointDescriptor;
+
+typedef struct {
+    uint8_t bDescLength;      
+    uint8_t bDescriptorType;  
+    uint8_t bNbrPorts;        
+    uint16_t wHubCharacteristics;
+    uint8_t bPwrOn2PwrGood;   
+    uint8_t bHubContrCurrent; 
+    uint8_t DeviceRemovable;  
+    uint8_t PortPweCtrlMak;   
+} HubDescriptor;              
+#pragma pack(pop)
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostGPS/USBHostGPS.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,100 @@
+#include "USBHostGPS.h"
+
+#ifdef _USB_DBG
+#define USB_DBG(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");} while(0);
+#define USB_DBG2(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");} while(0);
+#define USB_DBG_HEX(A,B) debug_hex(A,B)
+void debug_hex(uint8_t* buf, int size);
+#else
+#define USB_DBG(...) while(0)
+#define USB_DBG2(...) while(0)
+#define USB_DBG_HEX(A,B) while(0)
+#endif
+
+#define USB_TEST_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
+#define USB_TEST_ASSERT_FALSE(A) USB_TEST_ASSERT(!(A))
+
+#define USB_INFO(...) do{fprintf(stderr,__VA_ARGS__);}while(0);
+
+
+USBHostGPS::USBHostGPS(int baud_)
+{
+    host = USBHost::getHostInst();
+    init();
+    baud = baud_;
+}
+
+void USBHostGPS::init() {
+    dev = NULL;
+    bulk_in = NULL;
+    onUpdate = NULL;
+    dev_connected = false;
+    gps_device_found = false;
+    gps_intf = -1;
+}
+
+bool USBHostGPS::connected() {
+    return dev_connected;
+}
+
+bool USBHostGPS::connect() {
+
+    if (dev_connected) {
+        return true;
+    }
+    
+    for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) {
+        if ((dev = host->getDevice(i)) != NULL) {
+            if(host->enumerate(dev, this)) {
+                break;
+            }
+            if (gps_device_found) {
+                bulk_in = dev->getEndpoint(gps_intf, BULK_ENDPOINT, IN);
+                USB_TEST_ASSERT(bulk_in);
+                // stop bit = 1, parity = none, 8bit
+                uint8_t data[] = {baud&0xff, baud>>8, baud>>16, baud>>24, 0x00, 0x00, 0x08};
+                USB_TYPE rc = host->controlWrite(dev, 0x21, PL2303_SET_LINE_CODING, 0, 0, data, sizeof(data));
+                USB_TEST_ASSERT(rc == USB_TYPE_OK);
+                USB_INFO("New GPS device: VID:%04x PID:%04x [dev: %p - intf: %d]\n", dev->getVid(), dev->getPid(), dev, gps_intf);
+                dev_connected = true;
+                return true;
+            }
+        }
+    }
+    init();
+    return false;
+}
+
+/*virtual*/ void USBHostGPS::setVidPid(uint16_t vid, uint16_t pid)
+{
+    USB_DBG("vid:%04x pid:%04x", vid, pid);
+    if (pid == 0x2303) {
+        gps_device_found = true;
+    }
+}
+
+/*virtual*/ bool USBHostGPS::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed
+{
+    USB_DBG("intf: %d class: %02x %02x %02x", intf_nb, intf_class, intf_subclass, intf_protocol);
+    if (gps_device_found) {
+        if (gps_intf == -1) {
+            gps_intf = intf_nb;
+            return true;
+        }
+    }    
+    return false;
+}
+
+/*virtual*/ bool USBHostGPS::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used
+{
+    USB_DBG("intf_nb=%d type=%d dir=%d", intf_nb, type, dir);
+    if (gps_device_found) {
+        if (intf_nb == gps_intf) {
+            if (type == BULK_ENDPOINT && dir == IN) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostGPS/USBHostGPS.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,63 @@
+// Simple USBHost GPS Dongle for FRDM-KL46Z
+#include "USBHost.h"
+
+#define PL2303_SET_LINE_CODING 0x20
+
+class USBHostGPS : public IUSBEnumerator {
+public:
+
+    /**
+    * Constructor
+    */
+    USBHostGPS(int baud = 38400);
+
+    /**
+     * Try to connect a USB GPS device
+     *
+     * @return true if connection was successful
+     */
+    bool connect();
+
+    /**
+    * Check if a USB GPS is connected
+    *
+    * @returns true if a mouse is connected
+    */
+    bool connected();
+
+    int readNMEA(char* data, int size, int timeout_ms) {
+        int result = host->BulkRead(bulk_in, (uint8_t*)data, size, timeout_ms);
+        return (result >= 0) ? bulk_in->getLengthTransferred() : 0;
+    }
+    void attachEvent(void (*ptr)(uint8_t* data, int size)) {
+        if (ptr != NULL) {
+            onUpdate = ptr;
+        }
+    }
+    void poll() {
+        int result = host->BulkRead(bulk_in, buf, sizeof(buf), 0);
+        if (result >= 0) {
+            if (onUpdate) {
+                (*onUpdate)(buf, bulk_in->getLengthTransferred());
+            }
+        }
+    }
+
+protected:
+    //From IUSBEnumerator
+    virtual void setVidPid(uint16_t vid, uint16_t pid);
+    virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); //Must return true if the interface should be parsed
+    virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); //Must return true if the endpoint will be used
+
+private:
+    USBHost * host;
+    USBDeviceConnected* dev;
+    USBEndpoint* bulk_in;
+    bool dev_connected;
+    bool gps_device_found;
+    int gps_intf;
+    void (*onUpdate)(uint8_t* data, int size);
+    uint8_t buf[64];
+    int baud;
+    void init();
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostHID/USBHostMouse.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,175 @@
+#include "USBHostMouse.h"
+
+#ifdef _USB_DBG
+#define USB_DBG(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\n");} while(0);
+#define USB_DBG_HEX(A,B) debug_hex(A,B)
+void debug_hex(uint8_t* buf, int size);
+#else
+#define USB_DBG(...) while(0)
+#define USB_DBG_HEX(A,B) while(0)
+#endif
+
+#define USB_TEST_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
+#define USB_TEST_ASSERT_FALSE(A) USB_TEST_ASSERT(!(A))
+
+#define USB_INFO(...) do{fprintf(stderr,__VA_ARGS__);}while(0);
+
+USBHostMouse::USBHostMouse() {
+    host = USBHost::getHostInst();
+    init();
+}
+
+void USBHostMouse::init() {
+    dev = NULL;
+    int_in = NULL;
+    onUpdate = NULL;
+    onButtonUpdate = NULL;
+    onXUpdate = NULL;
+    onYUpdate = NULL;
+    onZUpdate = NULL;
+    report_id = 0;
+    dev_connected = false;
+    mouse_device_found = false;
+    mouse_intf = -1;
+    
+    buttons = 0;
+    x = 0;
+    y = 0;
+    z = 0;
+}
+
+bool USBHostMouse::connected() {
+    return dev_connected;
+}
+
+bool USBHostMouse::connect() {
+
+    if (dev_connected) {
+        return true;
+    }
+    
+    for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) {
+        if ((dev = host->getDevice(i)) != NULL) {
+
+            if(host->enumerate(dev, this))
+                break;
+            
+            if (mouse_device_found) {
+                
+                int_in = dev->getEndpoint(mouse_intf, INTERRUPT_ENDPOINT, IN);
+                USB_DBG("int_in=%p", int_in);
+                if (!int_in)
+                    break;
+                
+                USB_INFO("New Mouse device: VID:%04x PID:%04x [dev: %p - intf: %d]\n", dev->getVid(), dev->getPid(), dev, mouse_intf);
+                dev->setName("Mouse", mouse_intf);
+                host->registerDriver(dev, mouse_intf, this, &USBHostMouse::init);
+                
+                //int_in->attach(this, &USBHostMouse::rxHandler);
+                host->interruptRead(dev, int_in, report, int_in->getSize(), false);
+                
+                dev_connected = true;
+                return true;
+            }
+        }
+    }
+    init();
+    return false;
+}
+
+/*virtual*/ void USBHostMouse::setVidPid(uint16_t vid, uint16_t pid)
+{
+    USB_DBG("vid:%04x pid:%04x", vid, pid);
+    // we don't check VID/PID for mouse driver
+}
+
+/*virtual*/ bool USBHostMouse::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed
+{
+    USB_DBG("intf: %d class: %02x %02x %02x", intf_nb, intf_class, intf_subclass, intf_protocol);
+    if ((mouse_intf == -1) &&
+        (intf_class == HID_CLASS) &&
+        (intf_subclass == 0x01) &&
+        (intf_protocol == 0x02)) {
+        mouse_intf = intf_nb;
+        return true;
+    }
+    return false;
+}
+
+/*virtual*/ bool USBHostMouse::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used
+{
+    USB_DBG("intf_nb=%d type=%d dir=%d", intf_nb, type, dir);
+
+    if (intf_nb == mouse_intf) {
+        if (type == INTERRUPT_ENDPOINT && dir == IN) {
+            mouse_device_found = true;
+            return true;
+        }
+    }
+    return false;
+}
+
+#if 0
+void USBHostMouse::setup() {
+    dev = host->getDevice(0);
+    if (dev->getClass() == HUB_CLASS) {
+        for(int i = 1; ; i++) {
+            dev = host->getDevice(i);
+            if (dev == NULL) {
+                break;
+            }
+            if (enumeration(dev)) {
+                break;
+            }
+        }
+        USB_TEST_ASSERT(ep_int_in);
+    } else {
+        ep_int_in = NULL;
+    }
+}
+
+bool USBHostMouse::enumeration(USBDeviceConnected* dev) {
+    // config descriptor
+    uint8_t desc[4];
+    int rc = host->controlRead(dev, 0x80, GET_DESCRIPTOR, 2<<8, 0, desc, 4);
+    USB_TEST_ASSERT(rc == USB_TYPE_OK);
+    USB_DBG_HEX(desc, 4);
+
+    int TotalLength = desc[2]|desc[3]<<8;
+    uint8_t* buf = new uint8_t[TotalLength];
+    rc = host->controlRead(dev, 0x80, GET_DESCRIPTOR, 2<<8, 0, buf, TotalLength);
+    USB_TEST_ASSERT(rc == USB_TYPE_OK);
+    //USB_DBG_HEX(buf, TotalLength);
+    bool found = false;
+    for(int i = 0; i < TotalLength; ) {
+        int Length = buf[i];
+        uint8_t DescriptorType = buf[i+1];
+        if (DescriptorType == 0x04) { // interface
+            InterfaceDescriptor* desc = reinterpret_cast<InterfaceDescriptor*>(buf+i);
+            if (desc->bInterfaceClass == 0x03) {
+                found = true;
+            }
+        } else if (DescriptorType == 0x05) { // endpoint
+            EndpointDescriptor* desc = reinterpret_cast<EndpointDescriptor*>(buf+i);
+            if (desc->bmAttributes == 0x03 &&
+                (desc->bEndpointAddress & 0x80)) { // interrupt in
+                ep_int_in = new USBEndpoint;
+                ep_int_in->setDevice(dev);
+                ep_int_in->setAddress(desc->bEndpointAddress);
+                ep_int_in->setSize(desc->wMaxPacketSize);
+            }
+        }
+        USB_DBG_HEX(buf+i, Length);
+        i += Length;
+    }
+    delete[] buf;
+    if (!found) {
+        return false;
+    }
+    int config = 1;
+    host->controlWrite(dev, 0x00, SET_CONFIGURATION, config, 0, NULL, 0);
+    wait_ms(100);
+    return true;
+}
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostHID/USBHostMouse.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,88 @@
+/* mbed USBHost 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.
+ */
+
+#include "USBHost.h"
+
+class USBHostMouse : public IUSBEnumerator {
+public:
+
+    /**
+    * Constructor
+    */
+    USBHostMouse();
+
+    /**
+     * Try to connect a mouse device
+     *
+     * @return true if connection was successful
+     */
+    bool connect();
+
+    /**
+    * Check if a mouse is connected
+    *
+    * @returns true if a mouse is connected
+    */
+    bool connected();
+
+    int readReport(uint8_t* data) {
+        int rc = host->interruptRead(dev, int_in, data, 4);
+        return rc == USB_TYPE_OK ? 4 : 0;
+    }
+    void attachEvent(void (*ptr)(uint8_t* data, int size)) {
+        if (ptr != NULL) {
+            onUpdate = ptr;
+        }
+    }
+    void poll() {
+        USB_TYPE rc = host->interruptRead(dev, int_in, report, sizeof(report));
+        if (rc == USB_TYPE_OK) {
+            if (onUpdate) {
+                (*onUpdate)(report, int_in->getLengthTransferred());
+            }
+        }
+    }
+
+protected:
+    //From IUSBEnumerator
+    virtual void setVidPid(uint16_t vid, uint16_t pid);
+    virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); //Must return true if the interface should be parsed
+    virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); //Must return true if the endpoint will be used
+
+private:
+    USBHost * host;
+    USBDeviceConnected * dev;
+    USBEndpoint * int_in;
+    uint8_t report[4];
+    
+    bool dev_connected;
+    bool mouse_device_found;
+    int mouse_intf;
+
+    uint8_t buttons;
+    int8_t x;
+    int8_t y;
+    int8_t z;
+
+    void (*onUpdate)(uint8_t* data, int size);
+    void (*onButtonUpdate)(uint8_t buttons);
+    void (*onXUpdate)(int8_t x);
+    void (*onYUpdate)(int8_t y);
+    void (*onZUpdate)(int8_t z);
+    int report_id;
+    void init();
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem.lib	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/FATFileSystem/#b6669c987c8e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/ccsbcs.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,540 @@
+/*------------------------------------------------------------------------*/
+/* Unicode - Local code bidirectional converter  (C)ChaN, 2009            */
+/* (SBCS code pages)                                                      */
+/*------------------------------------------------------------------------*/
+/*  437   U.S. (OEM)
+/   720   Arabic (OEM)
+/   1256  Arabic (Windows)
+/   737   Greek (OEM)
+/   1253  Greek (Windows)
+/   1250  Central Europe (Windows)
+/   775   Baltic (OEM)
+/   1257  Baltic (Windows)
+/   850   Multilingual Latin 1 (OEM)
+/   852   Latin 2 (OEM)
+/   1252  Latin 1 (Windows)
+/   855   Cyrillic (OEM)
+/   1251  Cyrillic (Windows)
+/   866   Russian (OEM)
+/   857   Turkish (OEM)
+/   1254  Turkish (Windows)
+/   858   Multilingual Latin 1 + Euro (OEM)
+/   862   Hebrew (OEM)
+/   1255  Hebrew (Windows)
+/   874   Thai (OEM, Windows)
+/   1258  Vietnam (OEM, Windows)
+*/
+
+#include "ff.h"
+
+
+#if _CODE_PAGE == 437
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP437(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
+    0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+    0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 720
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP720(0x80-0xFF) to Unicode conversion table */
+    0x0000, 0x0000, 0x00E9, 0x00E2, 0x0000, 0x00E0, 0x0000, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x0651, 0x0652, 0x00F4, 0x00A4, 0x0640, 0x00FB, 0x00F9,
+    0x0621, 0x0622, 0x0623, 0x0624, 0x00A3, 0x0625, 0x0626, 0x0627,
+    0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
+    0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0x0641, 0x00B5, 0x0642,
+    0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A,
+    0x2261, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0xO650, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 737
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP737(0x80-0xFF) to Unicode conversion table */
+    0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398,
+    0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0,
+    0x03A1, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9,
+    0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
+    0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
+    0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x03C9, 0x03AC, 0x03AD, 0x03AE, 0x03CA, 0x03AF, 0x03CC, 0x03CD,
+    0x03CB, 0x03CE, 0x0386, 0x0388, 0x0389, 0x038A, 0x038C, 0x038E,
+    0x038F, 0x00B1, 0x2265, 0x2264, 0x03AA, 0x03AB, 0x00F7, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 775
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP775(0x80-0xFF) to Unicode conversion table */
+    0x0106, 0x00FC, 0x00E9, 0x0101, 0x00E4, 0x0123, 0x00E5, 0x0107,
+    0x0142, 0x0113, 0x0156, 0x0157, 0x012B, 0x0179, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x014D, 0x00F6, 0x0122, 0x00A2, 0x015A,
+    0x015B, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x00A4,
+    0x0100, 0x012A, 0x00F3, 0x017B, 0x017C, 0x017A, 0x201D, 0x00A6,
+    0x00A9, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x0141, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0104, 0x010C, 0x0118,
+    0x0116, 0x2563, 0x2551, 0x2557, 0x255D, 0x012E, 0x0160, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0172, 0x016A,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x017D,
+    0x0105, 0x010D, 0x0119, 0x0117, 0x012F, 0x0161, 0x0173, 0x016B,
+    0x017E, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x00D3, 0x00DF, 0x014C, 0x0143, 0x00F5, 0x00D5, 0x00B5, 0x0144,
+    0x0136, 0x0137, 0x013B, 0x013C, 0x0146, 0x0112, 0x0145, 0x2019,
+    0x00AD, 0x00B1, 0x201C, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x201E,
+    0x00B0, 0x2219, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 850
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP850(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
+    0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x0131, 0x00CD, 0x00CE,
+    0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
+    0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+    0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 852
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP852(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x016F, 0x0107, 0x00E7,
+    0x0142, 0x00EB, 0x0150, 0x0151, 0x00EE, 0x0179, 0x00C4, 0x0106,
+    0x00C9, 0x0139, 0x013A, 0x00F4, 0x00F6, 0x013D, 0x013E, 0x015A,
+    0x015B, 0x00D6, 0x00DC, 0x0164, 0x0165, 0x0141, 0x00D7, 0x010D,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x0104, 0x0105, 0x017D, 0x017E,
+    0x0118, 0x0119, 0x00AC, 0x017A, 0x010C, 0x015F, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x011A,
+    0x015E, 0x2563, 0x2551, 0x2557, 0x255D, 0x017B, 0x017C, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x0102, 0x0103,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x0111, 0x0110, 0x010E, 0x00CB, 0x010F, 0x0147, 0x00CD, 0x00CE,
+    0x011B, 0x2518, 0x250C, 0x2588, 0x2584, 0x0162, 0x016E, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161,
+    0x0154, 0x00DA, 0x0155, 0x0170, 0x00FD, 0x00DD, 0x0163, 0x00B4,
+    0x00AD, 0x02DD, 0x02DB, 0x02C7, 0x02D8, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x02D9, 0x0171, 0x0158, 0x0159, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 855
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP855(0x80-0xFF) to Unicode conversion table */
+    0x0452, 0x0402, 0x0453, 0x0403, 0x0451, 0x0401, 0x0454, 0x0404,
+    0x0455, 0x0405, 0x0456, 0x0406, 0x0457, 0x0407, 0x0458, 0x0408,
+    0x0459, 0x0409, 0x045A, 0x040A, 0x045B, 0x040B, 0x045C, 0x040C,
+    0x045E, 0x040E, 0x045F, 0x040F, 0x044E, 0x042E, 0x044A, 0x042A,
+    0x0430, 0x0410, 0x0431, 0x0411, 0x0446, 0x0426, 0x0434, 0x0414,
+    0x0435, 0x0415, 0x0444, 0x0424, 0x0433, 0x0413, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x0445, 0x0425, 0x0438,
+    0x0418, 0x2563, 0x2551, 0x2557, 0x255D, 0x0439, 0x0419, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x043A, 0x041A,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x043B, 0x041B, 0x043C, 0x041C, 0x043D, 0x041D, 0x043E, 0x041E,
+    0x043F, 0x2518, 0x250C, 0x2588, 0x2584, 0x041F, 0x044F, 0x2580,
+    0x042F, 0x0440, 0x0420, 0x0441, 0x0421, 0x0442, 0x0422, 0x0443,
+    0x0423, 0x0436, 0x0416, 0x0432, 0x0412, 0x044C, 0x042C, 0x2116,
+    0x00AD, 0x044B, 0x042B, 0x0437, 0x0417, 0x0448, 0x0428, 0x044D,
+    0x042D, 0x0449, 0x0429, 0x0447, 0x0427, 0x00A7, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 857
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP857(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0131, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x0130, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x015E, 0x015F,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x011E, 0x011F,
+    0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
+    0x00A9, 0x2563, 0x2551, 0x2557, 0x255D, 0x00A2, 0x00A5, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x00BA, 0x00AA, 0x00CA, 0x00CB, 0x00C8, 0x0000, 0x00CD, 0x00CE,
+    0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00A6, 0x00CC, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x0000,
+    0x00D7, 0x00DA, 0x00DB, 0x00D9, 0x00EC, 0x00FF, 0x00AF, 0x00B4,
+    0x00AD, 0x00B1, 0x0000, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 858
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP858(0x80-0xFF) to Unicode conversion table */
+    0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7,
+    0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+    0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9,
+    0x00FF, 0x00D6, 0x00DC, 0x00F8, 0x00A3, 0x00D8, 0x00D7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x00AE, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00C1, 0x00C2, 0x00C0,
+    0x00A9, 0x2563, 0x2551, 0x2557, 0x2550, 0x00A2, 0x00A5, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x00E3, 0x00C3,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x00A4,
+    0x00F0, 0x00D0, 0x00CA, 0x00CB, 0x00C8, 0x20AC, 0x00CD, 0x00CE,
+    0x00CF, 0x2518, 0x250C, 0x2588, 0x2584, 0x00C6, 0x00CC, 0x2580,
+    0x00D3, 0x00DF, 0x00D4, 0x00D2, 0x00F5, 0x00D5, 0x00B5, 0x00FE,
+    0x00DE, 0x00DA, 0x00DB, 0x00D9, 0x00FD, 0x00DD, 0x00AF, 0x00B4,
+    0x00AD, 0x00B1, 0x2017, 0x00BE, 0x00B6, 0x00A7, 0x00F7, 0x00B8,
+    0x00B0, 0x00A8, 0x00B7, 0x00B9, 0x00B3, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 862
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP862(0x80-0xFF) to Unicode conversion table */
+    0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
+    0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+    0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
+    0x05E8, 0x05E9, 0x05EA, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+    0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA,
+    0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4,
+    0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+    0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 866
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP866(0x80-0xFF) to Unicode conversion table */
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+    0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+    0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+    0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+    0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556,
+    0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+    0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F,
+    0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+    0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B,
+    0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+    0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F,
+    0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040E, 0x045E,
+    0x00B0, 0x2219, 0x00B7, 0x221A, 0x2116, 0x00A4, 0x25A0, 0x00A0
+};
+
+#elif _CODE_PAGE == 874
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP874(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x0000, 0x0000, 0x0000, 0x2026, 0x0000, 0x0000,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07,
+    0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F,
+    0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17,
+    0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F,
+    0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27,
+    0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F,
+    0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37,
+    0x0E38, 0x0E39, 0x0E3A, 0x0000, 0x0000, 0x0000, 0x0000, 0x0E3F,
+    0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47,
+    0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F,
+    0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57,
+    0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0x0000, 0x0000, 0x0000, 0x0000
+};
+
+#elif _CODE_PAGE == 1250
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP1250(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x0000, 0x2030, 0x0160, 0x2039, 0x015A, 0x0164, 0x017D, 0x0179,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2122, 0x0161, 0x203A, 0x015B, 0x0165, 0x017E, 0x017A,
+    0x00A0, 0x02C7, 0x02D8, 0x0141, 0x00A4, 0x0104, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x015E, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x017B,
+    0x00B0, 0x00B1, 0x02DB, 0x0142, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x0105, 0x015F, 0x00BB, 0x013D, 0x02DD, 0x013E, 0x017C,
+    0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7,
+    0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E,
+    0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7,
+    0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF,
+    0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7,
+    0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F,
+    0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7,
+    0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9
+};
+
+#elif _CODE_PAGE == 1251
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP1251(0x80-0xFF) to Unicode conversion table */
+    0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
+    0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2111, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
+    0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
+    0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
+    0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
+    0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457,
+    0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417,
+    0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F,
+    0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427,
+    0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F,
+    0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437,
+    0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F,
+    0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447,
+    0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F
+};
+
+#elif _CODE_PAGE == 1252
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP1252(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x017D, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x017E, 0x0178,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+    0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF,
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+    0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF
+};
+
+#elif _CODE_PAGE == 1253
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP1253(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x0000, 0x2030, 0x0000, 0x2039, 0x000C, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x00A0, 0x0385, 0x0386, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x0000, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x2015,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x00B5, 0x00B6, 0x00B7,
+    0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F,
+    0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397,
+    0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F,
+    0x03A0, 0x03A1, 0x0000, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7,
+    0x03A8, 0x03A9, 0x03AA, 0x03AD, 0x03AC, 0x03AD, 0x03AE, 0x03AF,
+    0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7,
+    0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF,
+    0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7,
+    0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0x0000
+};
+
+#elif _CODE_PAGE == 1254
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP1254(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x210A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
+    0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7,
+    0x00D8, 0x00D9, 0x00DA, 0x00BD, 0x00DC, 0x0130, 0x015E, 0x00DF,
+    0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
+    0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7,
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF
+};
+
+#elif _CODE_PAGE == 1255
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP1255(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0000, 0x2039, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0000, 0x203A, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x05B0, 0x05B1, 0x05B2, 0x05B3, 0x05B4, 0x05B5, 0x05B6, 0x05B7,
+    0x05B8, 0x05B9, 0x0000, 0x05BB, 0x05BC, 0x05BD, 0x05BE, 0x05BF,
+    0x05C0, 0x05C1, 0x05C2, 0x05C3, 0x05F0, 0x05F1, 0x05F2, 0x05F3,
+    0x05F4, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+    0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7,
+    0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF,
+    0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7,
+    0x05E8, 0x05E9, 0x05EA, 0x0000, 0x0000, 0x200E, 0x200F, 0x0000
+};
+
+#elif _CODE_PAGE == 1256
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP1256(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x067E, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688,
+    0x06AF, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x06A9, 0x2122, 0x0691, 0x203A, 0x0153, 0x200C, 0x200D, 0x06BA,
+    0x00A0, 0x060C, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x06BE, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x061B, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x061F,
+    0x06C1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627,
+    0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F,
+    0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00D7,
+    0x0637, 0x0638, 0x0639, 0x063A, 0x0640, 0x0640, 0x0642, 0x0643,
+    0x00E0, 0x0644, 0x00E2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0649, 0x064A, 0x00EE, 0x00EF,
+    0x064B, 0x064C, 0x064D, 0x064E, 0x00F4, 0x064F, 0x0650, 0x00F7,
+    0x0651, 0x00F9, 0x0652, 0x00FB, 0x00FC, 0x200E, 0x200F, 0x06D2
+}
+
+#elif _CODE_PAGE == 1257
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP1257(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0000, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x0000, 0x2030, 0x0000, 0x2039, 0x0000, 0x00A8, 0x02C7, 0x00B8,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x0000, 0x2122, 0x0000, 0x203A, 0x0000, 0x00AF, 0x02DB, 0x0000,
+    0x00A0, 0x0000, 0x00A2, 0x00A3, 0x00A4, 0x0000, 0x00A6, 0x00A7,
+    0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6,
+    0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112,
+    0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B,
+    0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7,
+    0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF,
+    0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113,
+    0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C,
+    0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7,
+    0x0173, 0x014E, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x02D9
+};
+
+#elif _CODE_PAGE == 1258
+#define _TBLDEF 1
+static
+const WCHAR Tbl[] = {   /*  CP1258(0x80-0xFF) to Unicode conversion table */
+    0x20AC, 0x0000, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021,
+    0x02C6, 0x2030, 0x0000, 0x2039, 0x0152, 0x0000, 0x0000, 0x0000,
+    0x0000, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
+    0x02DC, 0x2122, 0x0000, 0x203A, 0x0153, 0x0000, 0x0000, 0x0178,
+    0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7,
+    0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
+    0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7,
+    0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
+    0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x00C5, 0x00C6, 0x00C7,
+    0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x0300, 0x00CD, 0x00CE, 0x00CF,
+    0x0110, 0x00D1, 0x0309, 0x00D3, 0x00D4, 0x01A0, 0x00D6, 0x00D7,
+    0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x01AF, 0x0303, 0x00DF,
+    0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x00E5, 0x00E6, 0x00E7,
+    0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x0301, 0x00ED, 0x00EE, 0x00EF,
+    0x0111, 0x00F1, 0x0323, 0x00F3, 0x00F4, 0x01A1, 0x00F6, 0x00F7,
+    0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x01B0, 0x20AB, 0x00FF
+};
+
+#endif
+
+
+#if !_TBLDEF || !_USE_LFN
+#error This file is not needed in current configuration. Remove from the project.
+#endif
+
+
+WCHAR ff_convert (  /* Converted character, Returns zero on error */
+    WCHAR   src,    /* Character code to be converted */
+    UINT    dir     /* 0: Unicode to OEMCP, 1: OEMCP to Unicode */
+)
+{
+    WCHAR c;
+
+
+    if (src < 0x80) {   /* ASCII */
+        c = src;
+
+    } else {
+        if (dir) {      /* OEMCP to Unicode */
+            c = (src >= 0x100) ? 0 : Tbl[src - 0x80];
+
+        } else {        /* Unicode to OEMCP */
+            for (c = 0; c < 0x80; c++) {
+                if (src == Tbl[c]) break;
+            }
+            c = (c + 0x80) & 0xFF;
+        }
+    }
+
+    return c;
+}
+
+
+WCHAR ff_wtoupper ( /* Upper converted character */
+    WCHAR chr       /* Input character */
+)
+{
+    static const WCHAR tbl_lower[] = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xA1, 0x00A2, 0x00A3, 0x00A5, 0x00AC, 0x00AF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0x0FF, 0x101, 0x103, 0x105, 0x107, 0x109, 0x10B, 0x10D, 0x10F, 0x111, 0x113, 0x115, 0x117, 0x119, 0x11B, 0x11D, 0x11F, 0x121, 0x123, 0x125, 0x127, 0x129, 0x12B, 0x12D, 0x12F, 0x131, 0x133, 0x135, 0x137, 0x13A, 0x13C, 0x13E, 0x140, 0x142, 0x144, 0x146, 0x148, 0x14B, 0x14D, 0x14F, 0x151, 0x153, 0x155, 0x157, 0x159, 0x15B, 0x15D, 0x15F, 0x161, 0x163, 0x165, 0x167, 0x169, 0x16B, 0x16D, 0x16F, 0x171, 0x173, 0x175, 0x177, 0x17A, 0x17C, 0x17E, 0x192, 0x3B1, 0x3B2, 0x3B3, 0x3B4, 0x3B5, 0x3B6, 0x3B7, 0x3B8, 0x3B9, 0x3BA, 0x3BB, 0x3BC, 0x3BD, 0x3BE, 0x3BF, 0x3C0, 0x3C1, 0x3C3, 0x3C4, 0x3C5, 0x3C6, 0x3C7, 0x3C8, 0x3C9, 0x3CA, 0x430, 0x431, 0x432, 0x433, 0x434, 0x435, 0x436, 0x437, 0x438, 0x439, 0x43A, 0x43B, 0x43C, 0x43D, 0x43E, 0x43F, 0x440, 0x441, 0x442, 0x443, 0x444, 0x445, 0x446, 0x447, 0x448, 0x449, 0x44A, 0x44B, 0x44C, 0x44D, 0x44E, 0x44F, 0x451, 0x452, 0x453, 0x454, 0x455, 0x456, 0x457, 0x458, 0x459, 0x45A, 0x45B, 0x45C, 0x45E, 0x45F, 0x2170, 0x2171, 0x2172, 0x2173, 0x2174, 0x2175, 0x2176, 0x2177, 0x2178, 0x2179, 0x217A, 0x217B, 0x217C, 0x217D, 0x217E, 0x217F, 0xFF41, 0xFF42, 0xFF43, 0xFF44, 0xFF45, 0xFF46, 0xFF47, 0xFF48, 0xFF49, 0xFF4A, 0xFF4B, 0xFF4C, 0xFF4D, 0xFF4E, 0xFF4F, 0xFF50, 0xFF51, 0xFF52, 0xFF53, 0xFF54, 0xFF55, 0xFF56, 0xFF57, 0xFF58, 0xFF59, 0xFF5A, 0 };
+    static const WCHAR tbl_upper[] = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x21, 0xFFE0, 0xFFE1, 0xFFE5, 0xFFE2, 0xFFE3, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0x178, 0x100, 0x102, 0x104, 0x106, 0x108, 0x10A, 0x10C, 0x10E, 0x110, 0x112, 0x114, 0x116, 0x118, 0x11A, 0x11C, 0x11E, 0x120, 0x122, 0x124, 0x126, 0x128, 0x12A, 0x12C, 0x12E, 0x130, 0x132, 0x134, 0x136, 0x139, 0x13B, 0x13D, 0x13F, 0x141, 0x143, 0x145, 0x147, 0x14A, 0x14C, 0x14E, 0x150, 0x152, 0x154, 0x156, 0x158, 0x15A, 0x15C, 0x15E, 0x160, 0x162, 0x164, 0x166, 0x168, 0x16A, 0x16C, 0x16E, 0x170, 0x172, 0x174, 0x176, 0x179, 0x17B, 0x17D, 0x191, 0x391, 0x392, 0x393, 0x394, 0x395, 0x396, 0x397, 0x398, 0x399, 0x39A, 0x39B, 0x39C, 0x39D, 0x39E, 0x39F, 0x3A0, 0x3A1, 0x3A3, 0x3A4, 0x3A5, 0x3A6, 0x3A7, 0x3A8, 0x3A9, 0x3AA, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x417, 0x418, 0x419, 0x41A, 0x41B, 0x41C, 0x41D, 0x41E, 0x41F, 0x420, 0x421, 0x422, 0x423, 0x424, 0x425, 0x426, 0x427, 0x428, 0x429, 0x42A, 0x42B, 0x42C, 0x42D, 0x42E, 0x42F, 0x401, 0x402, 0x403, 0x404, 0x405, 0x406, 0x407, 0x408, 0x409, 0x40A, 0x40B, 0x40C, 0x40E, 0x40F, 0x2160, 0x2161, 0x2162, 0x2163, 0x2164, 0x2165, 0x2166, 0x2167, 0x2168, 0x2169, 0x216A, 0x216B, 0x216C, 0x216D, 0x216E, 0x216F, 0xFF21, 0xFF22, 0xFF23, 0xFF24, 0xFF25, 0xFF26, 0xFF27, 0xFF28, 0xFF29, 0xFF2A, 0xFF2B, 0xFF2C, 0xFF2D, 0xFF2E, 0xFF2F, 0xFF30, 0xFF31, 0xFF32, 0xFF33, 0xFF34, 0xFF35, 0xFF36, 0xFF37, 0xFF38, 0xFF39, 0xFF3A, 0 };
+    int i;
+
+
+    for (i = 0; tbl_lower[i] && chr != tbl_lower[i]; i++) ;
+
+    return tbl_lower[i] ? tbl_upper[i] : chr;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/diskio.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,104 @@
+/*-----------------------------------------------------------------------*/
+/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2007        */
+/*-----------------------------------------------------------------------*/
+/* This is a stub disk I/O module that acts as front end of the existing */
+/* disk I/O modules and attach it to FatFs module with common interface. */
+/*-----------------------------------------------------------------------*/
+#include "ffconf.h"
+#include "diskio.h"
+
+#include "mbed_debug.h"
+#include "FATFileSystem.h"
+
+using namespace mbed;
+
+DSTATUS disk_initialize (
+    BYTE drv                /* Physical drive nmuber (0..) */
+) 
+{
+    debug_if(FFS_DBG, "disk_initialize on drv [%d]\n", drv);
+    return (DSTATUS)FATFileSystem::_ffs[drv]->disk_initialize();
+}
+
+DSTATUS disk_status (
+    BYTE drv        /* Physical drive nmuber (0..) */
+) 
+{
+    debug_if(FFS_DBG, "disk_status on drv [%d]\n", drv);
+    return (DSTATUS)FATFileSystem::_ffs[drv]->disk_status();
+}
+
+DRESULT disk_read (
+    BYTE drv,        /* Physical drive nmuber (0..) */
+    BYTE *buff,        /* Data buffer to store read data */
+    DWORD sector,    /* Sector address (LBA) */
+    BYTE count        /* Number of sectors to read (1..255) */
+)
+{
+    debug_if(FFS_DBG, "disk_read(sector %d, count %d) on drv [%d]\n", sector, count, drv);
+    for(DWORD s=sector; s<sector+count; s++) {
+        debug_if(FFS_DBG, " disk_read(sector %d)\n", s);
+        int res = FATFileSystem::_ffs[drv]->disk_read((uint8_t*)buff, s);
+        if(res) {
+            return RES_PARERR;
+        }
+        buff += 512;
+    }
+    return RES_OK;
+}
+
+#if _READONLY == 0
+DRESULT disk_write (
+    BYTE drv,            /* Physical drive nmuber (0..) */
+    const BYTE *buff,    /* Data to be written */
+    DWORD sector,        /* Sector address (LBA) */
+    BYTE count            /* Number of sectors to write (1..255) */
+)
+{
+    debug_if(FFS_DBG, "disk_write(sector %d, count %d) on drv [%d]\n", sector, count, drv);
+    for(DWORD s = sector; s < sector + count; s++) {
+        debug_if(FFS_DBG, " disk_write(sector %d)\n", s);
+        int res = FATFileSystem::_ffs[drv]->disk_write((uint8_t*)buff, s);
+        if(res) {
+            return RES_PARERR;
+        }
+        buff += 512;
+    }
+    return RES_OK;
+}
+#endif /* _READONLY */
+
+DRESULT disk_ioctl (
+    BYTE drv,        /* Physical drive nmuber (0..) */
+    BYTE ctrl,        /* Control code */
+    void *buff        /* Buffer to send/receive control data */
+)
+{
+    debug_if(FFS_DBG, "disk_ioctl(%d)\n", ctrl);
+    switch(ctrl) {
+        case CTRL_SYNC:
+            if(FATFileSystem::_ffs[drv] == NULL) {
+                return RES_NOTRDY;
+            } else if(FATFileSystem::_ffs[drv]->disk_sync()) {
+                return RES_ERROR;
+            }
+            return RES_OK;
+        case GET_SECTOR_COUNT:
+            if(FATFileSystem::_ffs[drv] == NULL) {
+                return RES_NOTRDY;
+            } else {
+                DWORD res = FATFileSystem::_ffs[drv]->disk_sectors();
+                if(res > 0) {
+                    *((DWORD*)buff) = res; // minimum allowed
+                    return RES_OK;
+                } else {
+                    return RES_ERROR;
+                }
+            }
+        case GET_BLOCK_SIZE:
+            *((DWORD*)buff) = 1; // default when not known
+            return RES_OK;
+
+    }
+    return RES_PARERR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/diskio.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,76 @@
+//-----------------------------------------------------------------------
+//  Low level disk interface modlue include file
+//-----------------------------------------------------------------------
+
+#ifndef _DISKIO
+
+#define _READONLY    0   // 1: Remove write functions
+#define _USE_IOCTL   1   // 1: Use disk_ioctl fucntion
+
+#include "integer.h"
+
+
+// Status of Disk Functions
+typedef BYTE    DSTATUS;
+
+// Results of Disk Functions
+typedef enum {
+    RES_OK = 0, // 0: Successful
+    RES_ERROR,  // 1: R/W Error
+    RES_WRPRT,  // 2: Write Protected
+    RES_NOTRDY, // 3: Not Ready
+    RES_PARERR  // 4: Invalid Parameter
+} DRESULT;
+
+
+// Prototypes for disk control functions
+
+int assign_drives (int, int);
+DSTATUS disk_initialize (BYTE);
+DSTATUS disk_status (BYTE);
+DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);
+#if    _READONLY == 0
+DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);
+#endif
+DRESULT disk_ioctl (BYTE, BYTE, void*);
+
+
+
+// Disk Status Bits (DSTATUS)
+#define STA_NOINIT  0x01    // Drive not initialized
+#define STA_NODISK  0x02    // No medium in the drive
+#define STA_PROTECT 0x04    // Write protected
+
+
+// Command code for disk_ioctrl fucntion
+
+// Generic command (defined for FatFs)
+#define CTRL_SYNC           0    // Flush disk cache (for write functions)
+#define GET_SECTOR_COUNT    1    // Get media size (for only f_mkfs())
+#define GET_SECTOR_SIZE     2    // Get sector size (for multiple sector size (_MAX_SS >= 1024))
+#define GET_BLOCK_SIZE      3    // Get erase block size (for only f_mkfs())
+#define CTRL_ERASE_SECTOR   4    // Force erased a block of sectors (for only _USE_ERASE)
+
+// Generic command
+#define CTRL_POWER          5    // Get/Set power status
+#define CTRL_LOCK           6    // Lock/Unlock media removal
+#define CTRL_EJECT          7    // Eject media
+
+// MMC/SDC specific ioctl command
+#define MMC_GET_TYPE        10    // Get card type
+#define MMC_GET_CSD         11    // Get CSD
+#define MMC_GET_CID         12    // Get CID
+#define MMC_GET_OCR         13    // Get OCR
+#define MMC_GET_SDSTAT      14    // Get SD status
+
+// ATA/CF specific ioctl command
+#define ATA_GET_REV         20    // Get F/W revision
+#define ATA_GET_MODEL       21    // Get model name
+#define ATA_GET_SN          22    // Get serial number
+
+// NAND specific ioctl command
+#define NAND_FORMAT         30    // Create physical format
+
+
+#define _DISKIO
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/ff.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,4139 @@
+/*----------------------------------------------------------------------------/
+/  FatFs - FAT file system module  R0.09a                 (C)ChaN, 2012
+/-----------------------------------------------------------------------------/
+/ FatFs module is a generic FAT file system module for small embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following terms.
+/
+/  Copyright (C) 2012, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/   personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/-----------------------------------------------------------------------------/
+/ Feb 26,'06 R0.00  Prototype.
+/
+/ Apr 29,'06 R0.01  First stable version.
+/
+/ Jun 01,'06 R0.02  Added FAT12 support.
+/                   Removed unbuffered mode.
+/                   Fixed a problem on small (<32M) partition.
+/ Jun 10,'06 R0.02a Added a configuration option (_FS_MINIMUM).
+/
+/ Sep 22,'06 R0.03  Added f_rename().
+/                   Changed option _FS_MINIMUM to _FS_MINIMIZE.
+/ Dec 11,'06 R0.03a Improved cluster scan algorithm to write files fast.
+/                   Fixed f_mkdir() creates incorrect directory on FAT32.
+/
+/ Feb 04,'07 R0.04  Supported multiple drive system.
+/                   Changed some interfaces for multiple drive system.
+/                   Changed f_mountdrv() to f_mount().
+/                   Added f_mkfs().
+/ Apr 01,'07 R0.04a Supported multiple partitions on a physical drive.
+/                   Added a capability of extending file size to f_lseek().
+/                   Added minimization level 3.
+/                   Fixed an endian sensitive code in f_mkfs().
+/ May 05,'07 R0.04b Added a configuration option _USE_NTFLAG.
+/                   Added FSInfo support.
+/                   Fixed DBCS name can result FR_INVALID_NAME.
+/                   Fixed short seek (<= csize) collapses the file object.
+/
+/ Aug 25,'07 R0.05  Changed arguments of f_read(), f_write() and f_mkfs().
+/                   Fixed f_mkfs() on FAT32 creates incorrect FSInfo.
+/                   Fixed f_mkdir() on FAT32 creates incorrect directory.
+/ Feb 03,'08 R0.05a Added f_truncate() and f_utime().
+/                   Fixed off by one error at FAT sub-type determination.
+/                   Fixed btr in f_read() can be mistruncated.
+/                   Fixed cached sector is not flushed when create and close without write.
+/
+/ Apr 01,'08 R0.06  Added fputc(), fputs(), fprintf() and fgets().
+/                   Improved performance of f_lseek() on moving to the same or following cluster.
+/
+/ Apr 01,'09 R0.07  Merged Tiny-FatFs as a configuration option. (_FS_TINY)
+/                   Added long file name feature.
+/                   Added multiple code page feature.
+/                   Added re-entrancy for multitask operation.
+/                   Added auto cluster size selection to f_mkfs().
+/                   Added rewind option to f_readdir().
+/                   Changed result code of critical errors.
+/                   Renamed string functions to avoid name collision.
+/ Apr 14,'09 R0.07a Separated out OS dependent code on reentrant cfg.
+/                   Added multiple sector size feature.
+/ Jun 21,'09 R0.07c Fixed f_unlink() can return FR_OK on error.
+/                   Fixed wrong cache control in f_lseek().
+/                   Added relative path feature.
+/                   Added f_chdir() and f_chdrive().
+/                   Added proper case conversion to extended char.
+/ Nov 03,'09 R0.07e Separated out configuration options from ff.h to ffconf.h.
+/                   Fixed f_unlink() fails to remove a sub-dir on _FS_RPATH.
+/                   Fixed name matching error on the 13 char boundary.
+/                   Added a configuration option, _LFN_UNICODE.
+/                   Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
+/
+/ May 15,'10 R0.08  Added a memory configuration option. (_USE_LFN = 3)
+/                   Added file lock feature. (_FS_SHARE)
+/                   Added fast seek feature. (_USE_FASTSEEK)
+/                   Changed some types on the API, XCHAR->TCHAR.
+/                   Changed fname member in the FILINFO structure on Unicode cfg.
+/                   String functions support UTF-8 encoding files on Unicode cfg.
+/ Aug 16,'10 R0.08a Added f_getcwd(). (_FS_RPATH = 2)
+/                   Added sector erase feature. (_USE_ERASE)
+/                   Moved file lock semaphore table from fs object to the bss.
+/                   Fixed a wrong directory entry is created on non-LFN cfg when the given name contains ';'.
+/                   Fixed f_mkfs() creates wrong FAT32 volume.
+/ Jan 15,'11 R0.08b Fast seek feature is also applied to f_read() and f_write().
+/                   f_lseek() reports required table size on creating CLMP.
+/                   Extended format syntax of f_printf function.
+/                   Ignores duplicated directory separators in given path name.
+/
+/ Sep 06,'11 R0.09  f_mkfs() supports multiple partition to finish the multiple partition feature.
+/                   Added f_fdisk(). (_MULTI_PARTITION = 2)
+/ Aug 27,'12 R0.09a Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
+/                   Changed f_open() and f_opendir reject null object pointer to avoid crash.
+/                   Changed option name _FS_SHARE to _FS_LOCK.
+/---------------------------------------------------------------------------*/
+
+#include "ff.h"         /* FatFs configurations and declarations */
+#include "diskio.h"     /* Declarations of low level disk I/O functions */
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if _FATFS != 4004  /* Revision ID */
+#error Wrong include file (ff.h).
+#endif
+
+
+/* Definitions on sector size */
+#if _MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096
+#error Wrong sector size.
+#endif
+#if _MAX_SS != 512
+#define SS(fs)  ((fs)->ssize)   /* Variable sector size */
+#else
+#define SS(fs)  512U            /* Fixed sector size */
+#endif
+
+
+/* Reentrancy related */
+#if _FS_REENTRANT
+#if _USE_LFN == 1
+#error Static LFN work area must not be used in re-entrant configuration.
+#endif
+#define ENTER_FF(fs)        { if (!lock_fs(fs)) return FR_TIMEOUT; }
+#define LEAVE_FF(fs, res)   { unlock_fs(fs, res); return res; }
+#else
+#define ENTER_FF(fs)
+#define LEAVE_FF(fs, res)   return res
+#endif
+
+#define ABORT(fs, res)      { fp->flag |= FA__ERROR; LEAVE_FF(fs, res); }
+
+
+/* File access control feature */
+#if _FS_LOCK
+#if _FS_READONLY
+#error _FS_LOCK must be 0 on read-only cfg.
+#endif
+typedef struct {
+    FATFS *fs;              /* File ID 1, volume (NULL:blank entry) */
+    DWORD clu;              /* File ID 2, directory */
+    WORD idx;               /* File ID 3, directory index */
+    WORD ctr;               /* File open counter, 0:none, 0x01..0xFF:read open count, 0x100:write mode */
+} FILESEM;
+#endif
+
+
+
+/* DBCS code ranges and SBCS extend char conversion table */
+
+#if _CODE_PAGE == 932   /* Japanese Shift-JIS */
+#define _DF1S   0x81    /* DBC 1st byte range 1 start */
+#define _DF1E   0x9F    /* DBC 1st byte range 1 end */
+#define _DF2S   0xE0    /* DBC 1st byte range 2 start */
+#define _DF2E   0xFC    /* DBC 1st byte range 2 end */
+#define _DS1S   0x40    /* DBC 2nd byte range 1 start */
+#define _DS1E   0x7E    /* DBC 2nd byte range 1 end */
+#define _DS2S   0x80    /* DBC 2nd byte range 2 start */
+#define _DS2E   0xFC    /* DBC 2nd byte range 2 end */
+
+#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */
+#define _DF1S   0x81
+#define _DF1E   0xFE
+#define _DS1S   0x40
+#define _DS1E   0x7E
+#define _DS2S   0x80
+#define _DS2E   0xFE
+
+#elif _CODE_PAGE == 949 /* Korean */
+#define _DF1S   0x81
+#define _DF1E   0xFE
+#define _DS1S   0x41
+#define _DS1E   0x5A
+#define _DS2S   0x61
+#define _DS2E   0x7A
+#define _DS3S   0x81
+#define _DS3E   0xFE
+
+#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */
+#define _DF1S   0x81
+#define _DF1E   0xFE
+#define _DS1S   0x40
+#define _DS1E   0x7E
+#define _DS2S   0xA1
+#define _DS2E   0xFE
+
+#elif _CODE_PAGE == 437 /* U.S. (OEM) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F,0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 720 /* Arabic (OEM) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x45,0x41,0x84,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x49,0x49,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 737 /* Greek (OEM) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
+                0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xE7,0xE8,0xF1,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 775 /* Baltic (OEM) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 850 /* Multilingual Latin 1 (OEM) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 852 /* Latin 2 (OEM) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F,0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0x9F, \
+                0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
+
+#elif _CODE_PAGE == 855 /* Cyrillic (OEM) */
+#define _DF1S   0
+#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F,0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
+                0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
+                0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 857 /* Turkish (OEM) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x98,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
+                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0x59,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 858 /* Multilingual Latin 1 + Euro (OEM) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0xDE,0x8E,0x8F,0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x59,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+                0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD1,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE7,0xE9,0xEA,0xEB,0xED,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 862 /* Hebrew (OEM) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0x21,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 866 /* Russian (OEM) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0x90,0x91,0x92,0x93,0x9d,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 874 /* Thai (OEM, Windows) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1250 /* Central Europe (Windows) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xA3,0xB4,0xB5,0xB6,0xB7,0xB8,0xA5,0xAA,0xBB,0xBC,0xBD,0xBC,0xAF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
+
+#elif _CODE_PAGE == 1251 /* Cyrillic (Windows) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x82,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x80,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x8D,0x8E,0x8F, \
+                0xA0,0xA2,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB2,0xA5,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xA3,0xBD,0xBD,0xAF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF}
+
+#elif _CODE_PAGE == 1252 /* Latin 1 (Windows) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0xAd,0x9B,0x8C,0x9D,0xAE,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
+
+#elif _CODE_PAGE == 1253 /* Greek (Windows) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xA2,0xB8,0xB9,0xBA, \
+                0xE0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xFB,0xBC,0xFD,0xBF,0xFF}
+
+#elif _CODE_PAGE == 1254 /* Turkish (Windows) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x8A,0x9B,0x8C,0x9D,0x9E,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0x9F}
+
+#elif _CODE_PAGE == 1255 /* Hebrew (Windows) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF,0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1256 /* Arabic (Windows) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x8C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0x41,0xE1,0x41,0xE3,0xE4,0xE5,0xE6,0x43,0x45,0x45,0x45,0x45,0xEC,0xED,0x49,0x49,0xF0,0xF1,0xF2,0xF3,0x4F,0xF5,0xF6,0xF7,0xF8,0x55,0xFA,0x55,0x55,0xFD,0xFE,0xFF}
+
+#elif _CODE_PAGE == 1257 /* Baltic (Windows) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xA8,0xB9,0xAA,0xBB,0xBC,0xBD,0xBE,0xAF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xFF}
+
+#elif _CODE_PAGE == 1258 /* Vietnam (OEM, Windows) */
+#define _DF1S   0
+#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0xAC,0x9D,0x9E,0x9F, \
+                0xA0,0x21,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xEC,0xCD,0xCE,0xCF,0xD0,0xD1,0xF2,0xD3,0xD4,0xD5,0xD6,0xF7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xFE,0x9F}
+
+#elif _CODE_PAGE == 1   /* ASCII (for only non-LFN cfg) */
+#if _USE_LFN
+#error Cannot use LFN feature without valid code page.
+#endif
+#define _DF1S   0
+
+#else
+#error Unknown code page
+
+#endif
+
+
+/* Character code support macros */
+#define IsUpper(c)  (((c)>='A')&&((c)<='Z'))
+#define IsLower(c)  (((c)>='a')&&((c)<='z'))
+#define IsDigit(c)  (((c)>='0')&&((c)<='9'))
+
+#if _DF1S       /* Code page is DBCS */
+
+#ifdef _DF2S    /* Two 1st byte areas */
+#define IsDBCS1(c)  (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E))
+#else           /* One 1st byte area */
+#define IsDBCS1(c)  ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E)
+#endif
+
+#ifdef _DS3S    /* Three 2nd byte areas */
+#define IsDBCS2(c)  (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E))
+#else           /* Two 2nd byte areas */
+#define IsDBCS2(c)  (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E))
+#endif
+
+#else           /* Code page is SBCS */
+
+#define IsDBCS1(c)  0
+#define IsDBCS2(c)  0
+
+#endif /* _DF1S */
+
+
+/* Name status flags */
+#define NS          11      /* Index of name status byte in fn[] */
+#define NS_LOSS     0x01    /* Out of 8.3 format */
+#define NS_LFN      0x02    /* Force to create LFN entry */
+#define NS_LAST     0x04    /* Last segment */
+#define NS_BODY     0x08    /* Lower case flag (body) */
+#define NS_EXT      0x10    /* Lower case flag (ext) */
+#define NS_DOT      0x20    /* Dot entry */
+
+
+/* FAT sub-type boundaries */
+/* Note that the FAT spec by Microsoft says 4085 but Windows works with 4087! */
+#define MIN_FAT16   4086    /* Minimum number of clusters for FAT16 */
+#define MIN_FAT32   65526   /* Minimum number of clusters for FAT32 */
+
+
+/* FatFs refers the members in the FAT structures as byte array instead of
+/ structure member because the structure is not binary compatible between
+/ different platforms */
+
+#define BS_jmpBoot          0   /* Jump instruction (3) */
+#define BS_OEMName          3   /* OEM name (8) */
+#define BPB_BytsPerSec      11  /* Sector size [byte] (2) */
+#define BPB_SecPerClus      13  /* Cluster size [sector] (1) */
+#define BPB_RsvdSecCnt      14  /* Size of reserved area [sector] (2) */
+#define BPB_NumFATs         16  /* Number of FAT copies (1) */
+#define BPB_RootEntCnt      17  /* Number of root dir entries for FAT12/16 (2) */
+#define BPB_TotSec16        19  /* Volume size [sector] (2) */
+#define BPB_Media           21  /* Media descriptor (1) */
+#define BPB_FATSz16         22  /* FAT size [sector] (2) */
+#define BPB_SecPerTrk       24  /* Track size [sector] (2) */
+#define BPB_NumHeads        26  /* Number of heads (2) */
+#define BPB_HiddSec         28  /* Number of special hidden sectors (4) */
+#define BPB_TotSec32        32  /* Volume size [sector] (4) */
+#define BS_DrvNum           36  /* Physical drive number (2) */
+#define BS_BootSig          38  /* Extended boot signature (1) */
+#define BS_VolID            39  /* Volume serial number (4) */
+#define BS_VolLab           43  /* Volume label (8) */
+#define BS_FilSysType       54  /* File system type (1) */
+#define BPB_FATSz32         36  /* FAT size [sector] (4) */
+#define BPB_ExtFlags        40  /* Extended flags (2) */
+#define BPB_FSVer           42  /* File system version (2) */
+#define BPB_RootClus        44  /* Root dir first cluster (4) */
+#define BPB_FSInfo          48  /* Offset of FSInfo sector (2) */
+#define BPB_BkBootSec       50  /* Offset of backup boot sector (2) */
+#define BS_DrvNum32         64  /* Physical drive number (2) */
+#define BS_BootSig32        66  /* Extended boot signature (1) */
+#define BS_VolID32          67  /* Volume serial number (4) */
+#define BS_VolLab32         71  /* Volume label (8) */
+#define BS_FilSysType32     82  /* File system type (1) */
+#define FSI_LeadSig         0   /* FSI: Leading signature (4) */
+#define FSI_StrucSig        484 /* FSI: Structure signature (4) */
+#define FSI_Free_Count      488 /* FSI: Number of free clusters (4) */
+#define FSI_Nxt_Free        492 /* FSI: Last allocated cluster (4) */
+#define MBR_Table           446 /* MBR: Partition table offset (2) */
+#define SZ_PTE              16  /* MBR: Size of a partition table entry */
+#define BS_55AA             510 /* Boot sector signature (2) */
+
+#define DIR_Name            0   /* Short file name (11) */
+#define DIR_Attr            11  /* Attribute (1) */
+#define DIR_NTres           12  /* NT flag (1) */
+#define DIR_CrtTimeTenth    13  /* Created time sub-second (1) */
+#define DIR_CrtTime         14  /* Created time (2) */
+#define DIR_CrtDate         16  /* Created date (2) */
+#define DIR_LstAccDate      18  /* Last accessed date (2) */
+#define DIR_FstClusHI       20  /* Higher 16-bit of first cluster (2) */
+#define DIR_WrtTime         22  /* Modified time (2) */
+#define DIR_WrtDate         24  /* Modified date (2) */
+#define DIR_FstClusLO       26  /* Lower 16-bit of first cluster (2) */
+#define DIR_FileSize        28  /* File size (4) */
+#define LDIR_Ord            0   /* LFN entry order and LLE flag (1) */
+#define LDIR_Attr           11  /* LFN attribute (1) */
+#define LDIR_Type           12  /* LFN type (1) */
+#define LDIR_Chksum         13  /* Sum of corresponding SFN entry */
+#define LDIR_FstClusLO      26  /* Filled by zero (0) */
+#define SZ_DIR              32      /* Size of a directory entry */
+#define LLE                 0x40    /* Last long entry flag in LDIR_Ord */
+#define DDE                 0xE5    /* Deleted directory entry mark in DIR_Name[0] */
+#define NDDE                0x05    /* Replacement of the character collides with DDE */
+
+
+/*------------------------------------------------------------*/
+/* Module private work area                                   */
+/*------------------------------------------------------------*/
+/* Note that uninitialized variables with static duration are
+/  zeroed/nulled at start-up. If not, the compiler or start-up
+/  routine is out of ANSI-C standard.
+*/
+
+#if _VOLUMES
+static
+FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */
+#else
+#error Number of volumes must not be 0.
+#endif
+
+static
+WORD Fsid;              /* File system mount ID */
+
+#if _FS_RPATH
+static
+BYTE CurrVol;           /* Current drive */
+#endif
+
+#if _FS_LOCK
+static
+FILESEM Files[_FS_LOCK];    /* File lock semaphores */
+#endif
+
+#if _USE_LFN == 0           /* No LFN feature */
+#define DEF_NAMEBUF         BYTE sfn[12]
+#define INIT_BUF(dobj)      (dobj).fn = sfn
+#define FREE_BUF()
+
+#elif _USE_LFN == 1         /* LFN feature with static working buffer */
+static WCHAR LfnBuf[_MAX_LFN+1];
+#define DEF_NAMEBUF         BYTE sfn[12]
+#define INIT_BUF(dobj)      { (dobj).fn = sfn; (dobj).lfn = LfnBuf; }
+#define FREE_BUF()
+
+#elif _USE_LFN == 2         /* LFN feature with dynamic working buffer on the stack */
+#define DEF_NAMEBUF         BYTE sfn[12]; WCHAR lbuf[_MAX_LFN+1]
+#define INIT_BUF(dobj)      { (dobj).fn = sfn; (dobj).lfn = lbuf; }
+#define FREE_BUF()
+
+#elif _USE_LFN == 3         /* LFN feature with dynamic working buffer on the heap */
+#define DEF_NAMEBUF         BYTE sfn[12]; WCHAR *lfn
+#define INIT_BUF(dobj)      { lfn = ff_memalloc((_MAX_LFN + 1) * 2); \
+                              if (!lfn) LEAVE_FF((dobj).fs, FR_NOT_ENOUGH_CORE); \
+                              (dobj).lfn = lfn; (dobj).fn = sfn; }
+#define FREE_BUF()          ff_memfree(lfn)
+
+#else
+#error Wrong LFN configuration.
+#endif
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Functions
+
+---------------------------------------------------------------------------*/
+
+
+/*-----------------------------------------------------------------------*/
+/* String functions                                                      */
+/*-----------------------------------------------------------------------*/
+
+/* Copy memory to memory */
+static
+void mem_cpy (void* dst, const void* src, UINT cnt) {
+    BYTE *d = (BYTE*)dst;
+    const BYTE *s = (const BYTE*)src;
+
+#if _WORD_ACCESS == 1
+    while (cnt >= sizeof (int)) {
+        *(int*)d = *(int*)s;
+        d += sizeof (int); s += sizeof (int);
+        cnt -= sizeof (int);
+    }
+#endif
+    while (cnt--)
+        *d++ = *s++;
+}
+
+/* Fill memory */
+static
+void mem_set (void* dst, int val, UINT cnt) {
+    BYTE *d = (BYTE*)dst;
+
+    while (cnt--)
+        *d++ = (BYTE)val;
+}
+
+/* Compare memory to memory */
+static
+int mem_cmp (const void* dst, const void* src, UINT cnt) {
+    const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
+    int r = 0;
+
+    while (cnt-- && (r = *d++ - *s++) == 0) ;
+    return r;
+}
+
+/* Check if chr is contained in the string */
+static
+int chk_chr (const char* str, int chr) {
+    while (*str && *str != chr) str++;
+    return *str;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Request/Release grant to access the volume                            */
+/*-----------------------------------------------------------------------*/
+#if _FS_REENTRANT
+
+static
+int lock_fs (
+    FATFS *fs       /* File system object */
+)
+{
+    return ff_req_grant(fs->sobj);
+}
+
+
+static
+void unlock_fs (
+    FATFS *fs,      /* File system object */
+    FRESULT res     /* Result code to be returned */
+)
+{
+    if (fs &&
+        res != FR_NOT_ENABLED &&
+        res != FR_INVALID_DRIVE &&
+        res != FR_INVALID_OBJECT &&
+        res != FR_TIMEOUT) {
+        ff_rel_grant(fs->sobj);
+    }
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* File lock control functions                                           */
+/*-----------------------------------------------------------------------*/
+#if _FS_LOCK
+
+static
+FRESULT chk_lock (  /* Check if the file can be accessed */
+    DIR* dj,        /* Directory object pointing the file to be checked */
+    int acc         /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
+)
+{
+    UINT i, be;
+
+    /* Search file semaphore table */
+    for (i = be = 0; i < _FS_LOCK; i++) {
+        if (Files[i].fs) {  /* Existing entry */
+            if (Files[i].fs == dj->fs &&        /* Check if the file matched with an open file */
+                Files[i].clu == dj->sclust &&
+                Files[i].idx == dj->index) break;
+        } else {            /* Blank entry */
+            be++;
+        }
+    }
+    if (i == _FS_LOCK)  /* The file is not opened */
+        return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES;   /* Is there a blank entry for new file? */
+
+    /* The file has been opened. Reject any open against writing file and all write mode open */
+    return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
+}
+
+
+static
+int enq_lock (void) /* Check if an entry is available for a new file */
+{
+    UINT i;
+
+    for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;
+    return (i == _FS_LOCK) ? 0 : 1;
+}
+
+
+static
+UINT inc_lock ( /* Increment file open counter and returns its index (0:int error) */
+    DIR* dj,    /* Directory object pointing the file to register or increment */
+    int acc     /* Desired access mode (0:Read, !0:Write) */
+)
+{
+    UINT i;
+
+
+    for (i = 0; i < _FS_LOCK; i++) {    /* Find the file */
+        if (Files[i].fs == dj->fs &&
+            Files[i].clu == dj->sclust &&
+            Files[i].idx == dj->index) break;
+    }
+
+    if (i == _FS_LOCK) {                /* Not opened. Register it as new. */
+        for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ;
+        if (i == _FS_LOCK) return 0;    /* No space to register (int err) */
+        Files[i].fs = dj->fs;
+        Files[i].clu = dj->sclust;
+        Files[i].idx = dj->index;
+        Files[i].ctr = 0;
+    }
+
+    if (acc && Files[i].ctr) return 0;  /* Access violation (int err) */
+
+    Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1;  /* Set semaphore value */
+
+    return i + 1;
+}
+
+
+static
+FRESULT dec_lock (  /* Decrement file open counter */
+    UINT i          /* Semaphore index */
+)
+{
+    WORD n;
+    FRESULT res;
+
+
+    if (--i < _FS_LOCK) {
+        n = Files[i].ctr;
+        if (n == 0x100) n = 0;
+        if (n) n--;
+        Files[i].ctr = n;
+        if (!n) Files[i].fs = 0;
+        res = FR_OK;
+    } else {
+        res = FR_INT_ERR;
+    }
+    return res;
+}
+
+
+static
+void clear_lock (   /* Clear lock entries of the volume */
+    FATFS *fs
+)
+{
+    UINT i;
+
+    for (i = 0; i < _FS_LOCK; i++) {
+        if (Files[i].fs == fs) Files[i].fs = 0;
+    }
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change window offset                                                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT move_window (
+    FATFS *fs,      /* File system object */
+    DWORD sector    /* Sector number to make appearance in the fs->win[] */
+)                   /* Move to zero only writes back dirty window */
+{
+    DWORD wsect;
+
+
+    wsect = fs->winsect;
+    if (wsect != sector) {  /* Changed current window */
+#if !_FS_READONLY
+        if (fs->wflag) {    /* Write back dirty window if needed */
+            if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK)
+                return FR_DISK_ERR;
+            fs->wflag = 0;
+            if (wsect < (fs->fatbase + fs->fsize)) {    /* In FAT area */
+                BYTE nf;
+                for (nf = fs->n_fats; nf > 1; nf--) {   /* Reflect the change to all FAT copies */
+                    wsect += fs->fsize;
+                    disk_write(fs->drv, fs->win, wsect, 1);
+                }
+            }
+        }
+#endif
+        if (sector) {
+            if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK)
+                return FR_DISK_ERR;
+            fs->winsect = sector;
+        }
+    }
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Clean-up cached data                                                  */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT sync (  /* FR_OK: successful, FR_DISK_ERR: failed */
+    FATFS *fs   /* File system object */
+)
+{
+    FRESULT res;
+
+
+    res = move_window(fs, 0);
+    if (res == FR_OK) {
+        /* Update FSInfo sector if needed */
+        if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {
+            fs->winsect = 0;
+            /* Create FSInfo structure */
+            mem_set(fs->win, 0, 512);
+            ST_WORD(fs->win+BS_55AA, 0xAA55);
+            ST_DWORD(fs->win+FSI_LeadSig, 0x41615252);
+            ST_DWORD(fs->win+FSI_StrucSig, 0x61417272);
+            ST_DWORD(fs->win+FSI_Free_Count, fs->free_clust);
+            ST_DWORD(fs->win+FSI_Nxt_Free, fs->last_clust);
+            /* Write it into the FSInfo sector */
+            disk_write(fs->drv, fs->win, fs->fsi_sector, 1);
+            fs->fsi_flag = 0;
+        }
+        /* Make sure that no pending write process in the physical drive */
+        if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK)
+            res = FR_DISK_ERR;
+    }
+
+    return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get sector# from cluster#                                             */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD clust2sect (  /* !=0: Sector number, 0: Failed - invalid cluster# */
+    FATFS *fs,      /* File system object */
+    DWORD clst      /* Cluster# to be converted */
+)
+{
+    clst -= 2;
+    if (clst >= (fs->n_fatent - 2)) return 0;       /* Invalid cluster# */
+    return clst * fs->csize + fs->database;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Read value of a FAT entry                                */
+/*-----------------------------------------------------------------------*/
+
+
+DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, Else:Cluster status */
+    FATFS *fs,  /* File system object */
+    DWORD clst  /* Cluster# to get the link information */
+)
+{
+    UINT wc, bc;
+    BYTE *p;
+
+
+    if (clst < 2 || clst >= fs->n_fatent)   /* Check range */
+        return 1;
+
+    switch (fs->fs_type) {
+    case FS_FAT12 :
+        bc = (UINT)clst; bc += bc / 2;
+        if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
+        wc = fs->win[bc % SS(fs)]; bc++;
+        if (move_window(fs, fs->fatbase + (bc / SS(fs)))) break;
+        wc |= fs->win[bc % SS(fs)] << 8;
+        return (clst & 1) ? (wc >> 4) : (wc & 0xFFF);
+
+    case FS_FAT16 :
+        if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)))) break;
+        p = &fs->win[clst * 2 % SS(fs)];
+        return LD_WORD(p);
+
+    case FS_FAT32 :
+        if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)))) break;
+        p = &fs->win[clst * 4 % SS(fs)];
+        return LD_DWORD(p) & 0x0FFFFFFF;
+    }
+
+    return 0xFFFFFFFF;  /* An error occurred at the disk I/O layer */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Change value of a FAT entry                              */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+
+FRESULT put_fat (
+    FATFS *fs,  /* File system object */
+    DWORD clst, /* Cluster# to be changed in range of 2 to fs->n_fatent - 1 */
+    DWORD val   /* New value to mark the cluster */
+)
+{
+    UINT bc;
+    BYTE *p;
+    FRESULT res;
+
+
+    if (clst < 2 || clst >= fs->n_fatent) { /* Check range */
+        res = FR_INT_ERR;
+
+    } else {
+        switch (fs->fs_type) {
+        case FS_FAT12 :
+            bc = (UINT)clst; bc += bc / 2;
+            res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+            if (res != FR_OK) break;
+            p = &fs->win[bc % SS(fs)];
+            *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;
+            bc++;
+            fs->wflag = 1;
+            res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+            if (res != FR_OK) break;
+            p = &fs->win[bc % SS(fs)];
+            *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F));
+            break;
+
+        case FS_FAT16 :
+            res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
+            if (res != FR_OK) break;
+            p = &fs->win[clst * 2 % SS(fs)];
+            ST_WORD(p, (WORD)val);
+            break;
+
+        case FS_FAT32 :
+            res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
+            if (res != FR_OK) break;
+            p = &fs->win[clst * 4 % SS(fs)];
+            val |= LD_DWORD(p) & 0xF0000000;
+            ST_DWORD(p, val);
+            break;
+
+        default :
+            res = FR_INT_ERR;
+        }
+        fs->wflag = 1;
+    }
+
+    return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Remove a cluster chain                                 */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT remove_chain (
+    FATFS *fs,          /* File system object */
+    DWORD clst          /* Cluster# to remove a chain from */
+)
+{
+    FRESULT res;
+    DWORD nxt;
+#if _USE_ERASE
+    DWORD scl = clst, ecl = clst, rt[2];
+#endif
+
+    if (clst < 2 || clst >= fs->n_fatent) { /* Check range */
+        res = FR_INT_ERR;
+
+    } else {
+        res = FR_OK;
+        while (clst < fs->n_fatent) {           /* Not a last link? */
+            nxt = get_fat(fs, clst);            /* Get cluster status */
+            if (nxt == 0) break;                /* Empty cluster? */
+            if (nxt == 1) { res = FR_INT_ERR; break; }  /* Internal error? */
+            if (nxt == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }    /* Disk error? */
+            res = put_fat(fs, clst, 0);         /* Mark the cluster "empty" */
+            if (res != FR_OK) break;
+            if (fs->free_clust != 0xFFFFFFFF) { /* Update FSInfo */
+                fs->free_clust++;
+                fs->fsi_flag = 1;
+            }
+#if _USE_ERASE
+            if (ecl + 1 == nxt) {   /* Is next cluster contiguous? */
+                ecl = nxt;
+            } else {                /* End of contiguous clusters */
+                rt[0] = clust2sect(fs, scl);                    /* Start sector */
+                rt[1] = clust2sect(fs, ecl) + fs->csize - 1;    /* End sector */
+                disk_ioctl(fs->drv, CTRL_ERASE_SECTOR, rt);     /* Erase the block */
+                scl = ecl = nxt;
+            }
+#endif
+            clst = nxt; /* Next cluster */
+        }
+    }
+
+    return res;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Stretch or Create a cluster chain                      */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+DWORD create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+    FATFS *fs,          /* File system object */
+    DWORD clst          /* Cluster# to stretch. 0 means create a new chain. */
+)
+{
+    DWORD cs, ncl, scl;
+    FRESULT res;
+
+
+    if (clst == 0) {        /* Create a new chain */
+        scl = fs->last_clust;           /* Get suggested start point */
+        if (!scl || scl >= fs->n_fatent) scl = 1;
+    }
+    else {                  /* Stretch the current chain */
+        cs = get_fat(fs, clst);         /* Check the cluster status */
+        if (cs < 2) return 1;           /* It is an invalid cluster */
+        if (cs < fs->n_fatent) return cs;   /* It is already followed by next cluster */
+        scl = clst;
+    }
+
+    ncl = scl;              /* Start cluster */
+    for (;;) {
+        ncl++;                          /* Next cluster */
+        if (ncl >= fs->n_fatent) {      /* Wrap around */
+            ncl = 2;
+            if (ncl > scl) return 0;    /* No free cluster */
+        }
+        cs = get_fat(fs, ncl);          /* Get the cluster status */
+        if (cs == 0) break;             /* Found a free cluster */
+        if (cs == 0xFFFFFFFF || cs == 1)/* An error occurred */
+            return cs;
+        if (ncl == scl) return 0;       /* No free cluster */
+    }
+
+    res = put_fat(fs, ncl, 0x0FFFFFFF); /* Mark the new cluster "last link" */
+    if (res == FR_OK && clst != 0) {
+        res = put_fat(fs, clst, ncl);   /* Link it to the previous one if needed */
+    }
+    if (res == FR_OK) {
+        fs->last_clust = ncl;           /* Update FSINFO */
+        if (fs->free_clust != 0xFFFFFFFF) {
+            fs->free_clust--;
+            fs->fsi_flag = 1;
+        }
+    } else {
+        ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;
+    }
+
+    return ncl;     /* Return new cluster number or error code */
+}
+#endif /* !_FS_READONLY */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Convert offset into cluster with link map table        */
+/*-----------------------------------------------------------------------*/
+
+#if _USE_FASTSEEK
+static
+DWORD clmt_clust (  /* <2:Error, >=2:Cluster number */
+    FIL* fp,        /* Pointer to the file object */
+    DWORD ofs       /* File offset to be converted to cluster# */
+)
+{
+    DWORD cl, ncl, *tbl;
+
+
+    tbl = fp->cltbl + 1;    /* Top of CLMT */
+    cl = ofs / SS(fp->fs) / fp->fs->csize;  /* Cluster order from top of the file */
+    for (;;) {
+        ncl = *tbl++;           /* Number of cluters in the fragment */
+        if (!ncl) return 0;     /* End of table? (error) */
+        if (cl < ncl) break;    /* In this fragment? */
+        cl -= ncl; tbl++;       /* Next fragment */
+    }
+    return cl + *tbl;   /* Return the cluster number */
+}
+#endif  /* _USE_FASTSEEK */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Set directory index                              */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_sdi (
+    FATFS_DIR *dj,        /* Pointer to directory object */
+    WORD idx        /* Index of directory table */
+)
+{
+    DWORD clst;
+    WORD ic;
+
+
+    dj->index = idx;
+    clst = dj->sclust;
+    if (clst == 1 || clst >= dj->fs->n_fatent)  /* Check start cluster range */
+        return FR_INT_ERR;
+    if (!clst && dj->fs->fs_type == FS_FAT32)   /* Replace cluster# 0 with root cluster# if in FAT32 */
+        clst = dj->fs->dirbase;
+
+    if (clst == 0) {    /* Static table (root-dir in FAT12/16) */
+        dj->clust = clst;
+        if (idx >= dj->fs->n_rootdir)       /* Index is out of range */
+            return FR_INT_ERR;
+        dj->sect = dj->fs->dirbase + idx / (SS(dj->fs) / SZ_DIR);   /* Sector# */
+    }
+    else {              /* Dynamic table (sub-dirs or root-dir in FAT32) */
+        ic = SS(dj->fs) / SZ_DIR * dj->fs->csize;   /* Entries per cluster */
+        while (idx >= ic) { /* Follow cluster chain */
+            clst = get_fat(dj->fs, clst);               /* Get next cluster */
+            if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */
+            if (clst < 2 || clst >= dj->fs->n_fatent)   /* Reached to end of table or int error */
+                return FR_INT_ERR;
+            idx -= ic;
+        }
+        dj->clust = clst;
+        dj->sect = clust2sect(dj->fs, clst) + idx / (SS(dj->fs) / SZ_DIR);  /* Sector# */
+    }
+
+    dj->dir = dj->fs->win + (idx % (SS(dj->fs) / SZ_DIR)) * SZ_DIR; /* Ptr to the entry in the sector */
+
+    return FR_OK;   /* Seek succeeded */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Move directory table index next                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_next (  /* FR_OK:Succeeded, FR_NO_FILE:End of table, FR_DENIED:EOT and could not stretch */
+    FATFS_DIR *dj,        /* Pointer to directory object */
+    int stretch     /* 0: Do not stretch table, 1: Stretch table if needed */
+)
+{
+    DWORD clst;
+    WORD i;
+
+
+    stretch = stretch;      /* To suppress warning on read-only cfg. */
+    i = dj->index + 1;
+    if (!i || !dj->sect)    /* Report EOT when index has reached 65535 */
+        return FR_NO_FILE;
+
+    if (!(i % (SS(dj->fs) / SZ_DIR))) { /* Sector changed? */
+        dj->sect++;                 /* Next sector */
+
+        if (dj->clust == 0) {   /* Static table */
+            if (i >= dj->fs->n_rootdir) /* Report EOT when end of table */
+                return FR_NO_FILE;
+        }
+        else {                  /* Dynamic table */
+            if (((i / (SS(dj->fs) / SZ_DIR)) & (dj->fs->csize - 1)) == 0) { /* Cluster changed? */
+                clst = get_fat(dj->fs, dj->clust);              /* Get next cluster */
+                if (clst <= 1) return FR_INT_ERR;
+                if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+                if (clst >= dj->fs->n_fatent) {                 /* When it reached end of dynamic table */
+#if !_FS_READONLY
+                    BYTE c;
+                    if (!stretch) return FR_NO_FILE;            /* When do not stretch, report EOT */
+                    clst = create_chain(dj->fs, dj->clust);     /* Stretch cluster chain */
+                    if (clst == 0) return FR_DENIED;            /* No free cluster */
+                    if (clst == 1) return FR_INT_ERR;
+                    if (clst == 0xFFFFFFFF) return FR_DISK_ERR;
+                    /* Clean-up stretched table */
+                    if (move_window(dj->fs, 0)) return FR_DISK_ERR; /* Flush active window */
+                    mem_set(dj->fs->win, 0, SS(dj->fs));            /* Clear window buffer */
+                    dj->fs->winsect = clust2sect(dj->fs, clst); /* Cluster start sector */
+                    for (c = 0; c < dj->fs->csize; c++) {       /* Fill the new cluster with 0 */
+                        dj->fs->wflag = 1;
+                        if (move_window(dj->fs, 0)) return FR_DISK_ERR;
+                        dj->fs->winsect++;
+                    }
+                    dj->fs->winsect -= c;                       /* Rewind window address */
+#else
+                    return FR_NO_FILE;          /* Report EOT */
+#endif
+                }
+                dj->clust = clst;               /* Initialize data for new cluster */
+                dj->sect = clust2sect(dj->fs, clst);
+            }
+        }
+    }
+
+    dj->index = i;
+    dj->dir = dj->fs->win + (i % (SS(dj->fs) / SZ_DIR)) * SZ_DIR;
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Load/Store start cluster number                  */
+/*-----------------------------------------------------------------------*/
+
+static
+DWORD ld_clust (
+    FATFS *fs,  /* Pointer to the fs object */
+    BYTE *dir   /* Pointer to the directory entry */
+)
+{
+    DWORD cl;
+
+    cl = LD_WORD(dir+DIR_FstClusLO);
+    if (fs->fs_type == FS_FAT32)
+        cl |= (DWORD)LD_WORD(dir+DIR_FstClusHI) << 16;
+
+    return cl;
+}
+
+
+#if !_FS_READONLY
+static
+void st_clust (
+    BYTE *dir,  /* Pointer to the directory entry */
+    DWORD cl    /* Value to be set */
+)
+{
+    ST_WORD(dir+DIR_FstClusLO, cl);
+    ST_WORD(dir+DIR_FstClusHI, cl >> 16);
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* LFN handling - Test/Pick/Fit an LFN segment from/to directory entry   */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};  /* Offset of LFN chars in the directory entry */
+
+
+static
+int cmp_lfn (           /* 1:Matched, 0:Not matched */
+    WCHAR *lfnbuf,      /* Pointer to the LFN to be compared */
+    BYTE *dir           /* Pointer to the directory entry containing a part of LFN */
+)
+{
+    UINT i, s;
+    WCHAR wc, uc;
+
+
+    i = ((dir[LDIR_Ord] & ~LLE) - 1) * 13;  /* Get offset in the LFN buffer */
+    s = 0; wc = 1;
+    do {
+        uc = LD_WORD(dir+LfnOfs[s]);    /* Pick an LFN character from the entry */
+        if (wc) {   /* Last char has not been processed */
+            wc = ff_wtoupper(uc);       /* Convert it to upper case */
+            if (i >= _MAX_LFN || wc != ff_wtoupper(lfnbuf[i++]))    /* Compare it */
+                return 0;               /* Not matched */
+        } else {
+            if (uc != 0xFFFF) return 0; /* Check filler */
+        }
+    } while (++s < 13);             /* Repeat until all chars in the entry are checked */
+
+    if ((dir[LDIR_Ord] & LLE) && wc && lfnbuf[i])   /* Last segment matched but different length */
+        return 0;
+
+    return 1;                       /* The part of LFN matched */
+}
+
+
+
+static
+int pick_lfn (          /* 1:Succeeded, 0:Buffer overflow */
+    WCHAR *lfnbuf,      /* Pointer to the Unicode-LFN buffer */
+    BYTE *dir           /* Pointer to the directory entry */
+)
+{
+    UINT i, s;
+    WCHAR wc, uc;
+
+
+    i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;  /* Offset in the LFN buffer */
+
+    s = 0; wc = 1;
+    do {
+        uc = LD_WORD(dir+LfnOfs[s]);        /* Pick an LFN character from the entry */
+        if (wc) {   /* Last char has not been processed */
+            if (i >= _MAX_LFN) return 0;    /* Buffer overflow? */
+            lfnbuf[i++] = wc = uc;          /* Store it */
+        } else {
+            if (uc != 0xFFFF) return 0;     /* Check filler */
+        }
+    } while (++s < 13);                     /* Read all character in the entry */
+
+    if (dir[LDIR_Ord] & LLE) {              /* Put terminator if it is the last LFN part */
+        if (i >= _MAX_LFN) return 0;        /* Buffer overflow? */
+        lfnbuf[i] = 0;
+    }
+
+    return 1;
+}
+
+
+#if !_FS_READONLY
+static
+void fit_lfn (
+    const WCHAR *lfnbuf,    /* Pointer to the LFN buffer */
+    BYTE *dir,              /* Pointer to the directory entry */
+    BYTE ord,               /* LFN order (1-20) */
+    BYTE sum                /* SFN sum */
+)
+{
+    UINT i, s;
+    WCHAR wc;
+
+
+    dir[LDIR_Chksum] = sum;         /* Set check sum */
+    dir[LDIR_Attr] = AM_LFN;        /* Set attribute. LFN entry */
+    dir[LDIR_Type] = 0;
+    ST_WORD(dir+LDIR_FstClusLO, 0);
+
+    i = (ord - 1) * 13;             /* Get offset in the LFN buffer */
+    s = wc = 0;
+    do {
+        if (wc != 0xFFFF) wc = lfnbuf[i++]; /* Get an effective char */
+        ST_WORD(dir+LfnOfs[s], wc); /* Put it */
+        if (!wc) wc = 0xFFFF;       /* Padding chars following last char */
+    } while (++s < 13);
+    if (wc == 0xFFFF || !lfnbuf[i]) ord |= LLE; /* Bottom LFN part is the start of LFN sequence */
+    dir[LDIR_Ord] = ord;            /* Set the LFN order */
+}
+
+#endif
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create numbered name                                                  */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+void gen_numname (
+    BYTE *dst,          /* Pointer to generated SFN */
+    const BYTE *src,    /* Pointer to source SFN to be modified */
+    const WCHAR *lfn,   /* Pointer to LFN */
+    WORD seq            /* Sequence number */
+)
+{
+    BYTE ns[8], c;
+    UINT i, j;
+
+
+    mem_cpy(dst, src, 11);
+
+    if (seq > 5) {  /* On many collisions, generate a hash number instead of sequential number */
+        do seq = (seq >> 1) + (seq << 15) + (WORD)*lfn++; while (*lfn);
+    }
+
+    /* itoa (hexdecimal) */
+    i = 7;
+    do {
+        c = (seq % 16) + '0';
+        if (c > '9') c += 7;
+        ns[i--] = c;
+        seq /= 16;
+    } while (seq);
+    ns[i] = '~';
+
+    /* Append the number */
+    for (j = 0; j < i && dst[j] != ' '; j++) {
+        if (IsDBCS1(dst[j])) {
+            if (j == i - 1) break;
+            j++;
+        }
+    }
+    do {
+        dst[j++] = (i < 8) ? ns[i++] : ' ';
+    } while (j < 8);
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Calculate sum of an SFN                                               */
+/*-----------------------------------------------------------------------*/
+#if _USE_LFN
+static
+BYTE sum_sfn (
+    const BYTE *dir     /* Ptr to directory entry */
+)
+{
+    BYTE sum = 0;
+    UINT n = 11;
+
+    do sum = (sum >> 1) + (sum << 7) + *dir++; while (--n);
+    return sum;
+}
+#endif
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Find an object in the directory                  */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT dir_find (
+    FATFS_DIR *dj         /* Pointer to the directory object linked to the file name */
+)
+{
+    FRESULT res;
+    BYTE c, *dir;
+#if _USE_LFN
+    BYTE a, ord, sum;
+#endif
+
+    res = dir_sdi(dj, 0);           /* Rewind directory object */
+    if (res != FR_OK) return res;
+
+#if _USE_LFN
+    ord = sum = 0xFF;
+#endif
+    do {
+        res = move_window(dj->fs, dj->sect);
+        if (res != FR_OK) break;
+        dir = dj->dir;                  /* Ptr to the directory entry of current index */
+        c = dir[DIR_Name];
+        if (c == 0) { res = FR_NO_FILE; break; }    /* Reached to end of table */
+#if _USE_LFN    /* LFN configuration */
+        a = dir[DIR_Attr] & AM_MASK;
+        if (c == DDE || ((a & AM_VOL) && a != AM_LFN)) {    /* An entry without valid data */
+            ord = 0xFF;
+        } else {
+            if (a == AM_LFN) {          /* An LFN entry is found */
+                if (dj->lfn) {
+                    if (c & LLE) {      /* Is it start of LFN sequence? */
+                        sum = dir[LDIR_Chksum];
+                        c &= ~LLE; ord = c; /* LFN start order */
+                        dj->lfn_idx = dj->index;
+                    }
+                    /* Check validity of the LFN entry and compare it with given name */
+                    ord = (c == ord && sum == dir[LDIR_Chksum] && cmp_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+                }
+            } else {                    /* An SFN entry is found */
+                if (!ord && sum == sum_sfn(dir)) break; /* LFN matched? */
+                ord = 0xFF; dj->lfn_idx = 0xFFFF;   /* Reset LFN sequence */
+                if (!(dj->fn[NS] & NS_LOSS) && !mem_cmp(dir, dj->fn, 11)) break;    /* SFN matched? */
+            }
+        }
+#else       /* Non LFN configuration */
+        if (!(dir[DIR_Attr] & AM_VOL) && !mem_cmp(dir, dj->fn, 11)) /* Is it a valid entry? */
+            break;
+#endif
+        res = dir_next(dj, 0);      /* Next entry */
+    } while (res == FR_OK);
+
+    return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read an object from the directory                                     */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+static
+FRESULT dir_read (
+    FATFS_DIR *dj         /* Pointer to the directory object that pointing the entry to be read */
+)
+{
+    FRESULT res;
+    BYTE c, *dir;
+#if _USE_LFN
+    BYTE a, ord = 0xFF, sum = 0xFF;
+#endif
+
+    res = FR_NO_FILE;
+    while (dj->sect) {
+        res = move_window(dj->fs, dj->sect);
+        if (res != FR_OK) break;
+        dir = dj->dir;                  /* Ptr to the directory entry of current index */
+        c = dir[DIR_Name];
+        if (c == 0) { res = FR_NO_FILE; break; }    /* Reached to end of table */
+#if _USE_LFN    /* LFN configuration */
+        a = dir[DIR_Attr] & AM_MASK;
+        if (c == DDE || (!_FS_RPATH && c == '.') || ((a & AM_VOL) && a != AM_LFN)) {    /* An entry without valid data */
+            ord = 0xFF;
+        } else {
+            if (a == AM_LFN) {          /* An LFN entry is found */
+                if (c & LLE) {          /* Is it start of LFN sequence? */
+                    sum = dir[LDIR_Chksum];
+                    c &= ~LLE; ord = c;
+                    dj->lfn_idx = dj->index;
+                }
+                /* Check LFN validity and capture it */
+                ord = (c == ord && sum == dir[LDIR_Chksum] && pick_lfn(dj->lfn, dir)) ? ord - 1 : 0xFF;
+            } else {                    /* An SFN entry is found */
+                if (ord || sum != sum_sfn(dir)) /* Is there a valid LFN? */
+                    dj->lfn_idx = 0xFFFF;       /* It has no LFN. */
+                break;
+            }
+        }
+#else       /* Non LFN configuration */
+        if (c != DDE && (_FS_RPATH || c != '.') && !(dir[DIR_Attr] & AM_VOL))   /* Is it a valid entry? */
+            break;
+#endif
+        res = dir_next(dj, 0);              /* Next entry */
+        if (res != FR_OK) break;
+    }
+
+    if (res != FR_OK) dj->sect = 0;
+
+    return res;
+}
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Register an object to the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY
+static
+FRESULT dir_register (  /* FR_OK:Successful, FR_DENIED:No free entry or too many SFN collision, FR_DISK_ERR:Disk error */
+    FATFS_DIR *dj             /* Target directory with object name to be created */
+)
+{
+    FRESULT res;
+    BYTE c, *dir;
+#if _USE_LFN    /* LFN configuration */
+    WORD n, ne, is;
+    BYTE sn[12], *fn, sum;
+    WCHAR *lfn;
+
+
+    fn = dj->fn; lfn = dj->lfn;
+    mem_cpy(sn, fn, 12);
+
+    if (_FS_RPATH && (sn[NS] & NS_DOT))     /* Cannot create dot entry */
+        return FR_INVALID_NAME;
+
+    if (sn[NS] & NS_LOSS) {         /* When LFN is out of 8.3 format, generate a numbered name */
+        fn[NS] = 0; dj->lfn = 0;            /* Find only SFN */
+        for (n = 1; n < 100; n++) {
+            gen_numname(fn, sn, lfn, n);    /* Generate a numbered name */
+            res = dir_find(dj);             /* Check if the name collides with existing SFN */
+            if (res != FR_OK) break;
+        }
+        if (n == 100) return FR_DENIED;     /* Abort if too many collisions */
+        if (res != FR_NO_FILE) return res;  /* Abort if the result is other than 'not collided' */
+        fn[NS] = sn[NS]; dj->lfn = lfn;
+    }
+
+    if (sn[NS] & NS_LFN) {          /* When LFN is to be created, reserve an SFN + LFN entries. */
+        for (ne = 0; lfn[ne]; ne++) ;
+        ne = (ne + 25) / 13;
+    } else {                        /* Otherwise reserve only an SFN entry. */
+        ne = 1;
+    }
+
+    /* Reserve contiguous entries */
+    res = dir_sdi(dj, 0);
+    if (res != FR_OK) return res;
+    n = is = 0;
+    do {
+        res = move_window(dj->fs, dj->sect);
+        if (res != FR_OK) break;
+        c = *dj->dir;               /* Check the entry status */
+        if (c == DDE || c == 0) {   /* Is it a blank entry? */
+            if (n == 0) is = dj->index; /* First index of the contiguous entry */
+            if (++n == ne) break;   /* A contiguous entry that required count is found */
+        } else {
+            n = 0;                  /* Not a blank entry. Restart to search */
+        }
+        res = dir_next(dj, 1);      /* Next entry with table stretch */
+    } while (res == FR_OK);
+
+    if (res == FR_OK && ne > 1) {   /* Initialize LFN entry if needed */
+        res = dir_sdi(dj, is);
+        if (res == FR_OK) {
+            sum = sum_sfn(dj->fn);  /* Sum of the SFN tied to the LFN */
+            ne--;
+            do {                    /* Store LFN entries in bottom first */
+                res = move_window(dj->fs, dj->sect);
+                if (res != FR_OK) break;
+                fit_lfn(dj->lfn, dj->dir, (BYTE)ne, sum);
+                dj->fs->wflag = 1;
+                res = dir_next(dj, 0);  /* Next entry */
+            } while (res == FR_OK && --ne);
+        }
+    }
+
+#else   /* Non LFN configuration */
+    res = dir_sdi(dj, 0);
+    if (res == FR_OK) {
+        do {    /* Find a blank entry for the SFN */
+            res = move_window(dj->fs, dj->sect);
+            if (res != FR_OK) break;
+            c = *dj->dir;
+            if (c == DDE || c == 0) break;  /* Is it a blank entry? */
+            res = dir_next(dj, 1);          /* Next entry with table stretch */
+        } while (res == FR_OK);
+    }
+#endif
+
+    if (res == FR_OK) {     /* Initialize the SFN entry */
+        res = move_window(dj->fs, dj->sect);
+        if (res == FR_OK) {
+            dir = dj->dir;
+            mem_set(dir, 0, SZ_DIR);    /* Clean the entry */
+            mem_cpy(dir, dj->fn, 11);   /* Put SFN */
+#if _USE_LFN
+            dir[DIR_NTres] = *(dj->fn+NS) & (NS_BODY | NS_EXT); /* Put NT flag */
+#endif
+            dj->fs->wflag = 1;
+        }
+    }
+
+    return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Remove an object from the directory                                   */
+/*-----------------------------------------------------------------------*/
+#if !_FS_READONLY && !_FS_MINIMIZE
+static
+FRESULT dir_remove (    /* FR_OK: Successful, FR_DISK_ERR: A disk error */
+    FATFS_DIR *dj             /* Directory object pointing the entry to be removed */
+)
+{
+    FRESULT res;
+#if _USE_LFN    /* LFN configuration */
+    WORD i;
+
+    i = dj->index;  /* SFN index */
+    res = dir_sdi(dj, (WORD)((dj->lfn_idx == 0xFFFF) ? i : dj->lfn_idx));   /* Goto the SFN or top of the LFN entries */
+    if (res == FR_OK) {
+        do {
+            res = move_window(dj->fs, dj->sect);
+            if (res != FR_OK) break;
+            *dj->dir = DDE;         /* Mark the entry "deleted" */
+            dj->fs->wflag = 1;
+            if (dj->index >= i) break;  /* When reached SFN, all entries of the object has been deleted. */
+            res = dir_next(dj, 0);      /* Next entry */
+        } while (res == FR_OK);
+        if (res == FR_NO_FILE) res = FR_INT_ERR;
+    }
+
+#else           /* Non LFN configuration */
+    res = dir_sdi(dj, dj->index);
+    if (res == FR_OK) {
+        res = move_window(dj->fs, dj->sect);
+        if (res == FR_OK) {
+            *dj->dir = DDE;         /* Mark the entry "deleted" */
+            dj->fs->wflag = 1;
+        }
+    }
+#endif
+
+    return res;
+}
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pick a segment and create the object name in directory form           */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT create_name (
+    FATFS_DIR *dj,            /* Pointer to the directory object */
+    const TCHAR **path  /* Pointer to pointer to the segment in the path string */
+)
+{
+#ifdef _EXCVT
+    static const BYTE excvt[] = _EXCVT; /* Upper conversion table for extended chars */
+#endif
+
+#if _USE_LFN    /* LFN configuration */
+    BYTE b, cf;
+    WCHAR w, *lfn;
+    UINT i, ni, si, di;
+    const TCHAR *p;
+
+    /* Create LFN in Unicode */
+    for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */
+    lfn = dj->lfn;
+    si = di = 0;
+    for (;;) {
+        w = p[si++];                    /* Get a character */
+        if (w < ' ' || w == '/' || w == '\\') break;    /* Break on end of segment */
+        if (di >= _MAX_LFN)             /* Reject too long name */
+            return FR_INVALID_NAME;
+#if !_LFN_UNICODE
+        w &= 0xFF;
+        if (IsDBCS1(w)) {               /* Check if it is a DBC 1st byte (always false on SBCS cfg) */
+            b = (BYTE)p[si++];          /* Get 2nd byte */
+            if (!IsDBCS2(b))
+                return FR_INVALID_NAME; /* Reject invalid sequence */
+            w = (w << 8) + b;           /* Create a DBC */
+        }
+        w = ff_convert(w, 1);           /* Convert ANSI/OEM to Unicode */
+        if (!w) return FR_INVALID_NAME; /* Reject invalid code */
+#endif
+        if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) /* Reject illegal chars for LFN */
+            return FR_INVALID_NAME;
+        lfn[di++] = w;                  /* Store the Unicode char */
+    }
+    *path = &p[si];                     /* Return pointer to the next segment */
+    cf = (w < ' ') ? NS_LAST : 0;       /* Set last segment flag if end of path */
+#if _FS_RPATH
+    if ((di == 1 && lfn[di-1] == '.') || /* Is this a dot entry? */
+        (di == 2 && lfn[di-1] == '.' && lfn[di-2] == '.')) {
+        lfn[di] = 0;
+        for (i = 0; i < 11; i++)
+            dj->fn[i] = (i < di) ? '.' : ' ';
+        dj->fn[i] = cf | NS_DOT;        /* This is a dot entry */
+        return FR_OK;
+    }
+#endif
+    while (di) {                        /* Strip trailing spaces and dots */
+        w = lfn[di-1];
+        if (w != ' ' && w != '.') break;
+        di--;
+    }
+    if (!di) return FR_INVALID_NAME;    /* Reject nul string */
+
+    lfn[di] = 0;                        /* LFN is created */
+
+    /* Create SFN in directory form */
+    mem_set(dj->fn, ' ', 11);
+    for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ;  /* Strip leading spaces and dots */
+    if (si) cf |= NS_LOSS | NS_LFN;
+    while (di && lfn[di - 1] != '.') di--;  /* Find extension (di<=si: no extension) */
+
+    b = i = 0; ni = 8;
+    for (;;) {
+        w = lfn[si++];                  /* Get an LFN char */
+        if (!w) break;                  /* Break on end of the LFN */
+        if (w == ' ' || (w == '.' && si != di)) {   /* Remove spaces and dots */
+            cf |= NS_LOSS | NS_LFN; continue;
+        }
+
+        if (i >= ni || si == di) {      /* Extension or end of SFN */
+            if (ni == 11) {             /* Long extension */
+                cf |= NS_LOSS | NS_LFN; break;
+            }
+            if (si != di) cf |= NS_LOSS | NS_LFN;   /* Out of 8.3 format */
+            if (si > di) break;         /* No extension */
+            si = di; i = 8; ni = 11;    /* Enter extension section */
+            b <<= 2; continue;
+        }
+
+        if (w >= 0x80) {                /* Non ASCII char */
+#ifdef _EXCVT
+            w = ff_convert(w, 0);       /* Unicode -> OEM code */
+            if (w) w = excvt[w - 0x80]; /* Convert extended char to upper (SBCS) */
+#else
+            w = ff_convert(ff_wtoupper(w), 0);  /* Upper converted Unicode -> OEM code */
+#endif
+            cf |= NS_LFN;               /* Force create LFN entry */
+        }
+
+        if (_DF1S && w >= 0x100) {      /* Double byte char (always false on SBCS cfg) */
+            if (i >= ni - 1) {
+                cf |= NS_LOSS | NS_LFN; i = ni; continue;
+            }
+            dj->fn[i++] = (BYTE)(w >> 8);
+        } else {                        /* Single byte char */
+            if (!w || chk_chr("+,;=[]", w)) {   /* Replace illegal chars for SFN */
+                w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
+            } else {
+                if (IsUpper(w)) {       /* ASCII large capital */
+                    b |= 2;
+                } else {
+                    if (IsLower(w)) {   /* ASCII small capital */
+                        b |= 1; w -= 0x20;
+                    }
+                }
+            }
+        }
+        dj->fn[i++] = (BYTE)w;
+    }
+
+    if (dj->fn[0] == DDE) dj->fn[0] = NDDE; /* If the first char collides with deleted mark, replace it with 0x05 */
+
+    if (ni == 8) b <<= 2;
+    if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03)   /* Create LFN entry when there are composite capitals */
+        cf |= NS_LFN;
+    if (!(cf & NS_LFN)) {                       /* When LFN is in 8.3 format without extended char, NT flags are created */
+        if ((b & 0x03) == 0x01) cf |= NS_EXT;   /* NT flag (Extension has only small capital) */
+        if ((b & 0x0C) == 0x04) cf |= NS_BODY;  /* NT flag (Filename has only small capital) */
+    }
+
+    dj->fn[NS] = cf;    /* SFN is created */
+
+    return FR_OK;
+
+
+#else   /* Non-LFN configuration */
+    BYTE b, c, d, *sfn;
+    UINT ni, si, i;
+    const char *p;
+
+    /* Create file name in directory form */
+    for (p = *path; *p == '/' || *p == '\\'; p++) ; /* Strip duplicated separator */
+    sfn = dj->fn;
+    mem_set(sfn, ' ', 11);
+    si = i = b = 0; ni = 8;
+#if _FS_RPATH
+    if (p[si] == '.') { /* Is this a dot entry? */
+        for (;;) {
+            c = (BYTE)p[si++];
+            if (c != '.' || si >= 3) break;
+            sfn[i++] = c;
+        }
+        if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
+        *path = &p[si];                                 /* Return pointer to the next segment */
+        sfn[NS] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT;   /* Set last segment flag if end of path */
+        return FR_OK;
+    }
+#endif
+    for (;;) {
+        c = (BYTE)p[si++];
+        if (c <= ' ' || c == '/' || c == '\\') break;   /* Break on end of segment */
+        if (c == '.' || i >= ni) {
+            if (ni != 8 || c != '.') return FR_INVALID_NAME;
+            i = 8; ni = 11;
+            b <<= 2; continue;
+        }
+        if (c >= 0x80) {                /* Extended char? */
+            b |= 3;                     /* Eliminate NT flag */
+#ifdef _EXCVT
+            c = excvt[c - 0x80];        /* Upper conversion (SBCS) */
+#else
+#if !_DF1S  /* ASCII only cfg */
+            return FR_INVALID_NAME;
+#endif
+#endif
+        }
+        if (IsDBCS1(c)) {               /* Check if it is a DBC 1st byte (always false on SBCS cfg) */
+            d = (BYTE)p[si++];          /* Get 2nd byte */
+            if (!IsDBCS2(d) || i >= ni - 1) /* Reject invalid DBC */
+                return FR_INVALID_NAME;
+            sfn[i++] = c;
+            sfn[i++] = d;
+        } else {                        /* Single byte code */
+            if (chk_chr("\"*+,:;<=>\?[]|\x7F", c))  /* Reject illegal chrs for SFN */
+                return FR_INVALID_NAME;
+            if (IsUpper(c)) {           /* ASCII large capital? */
+                b |= 2;
+            } else {
+                if (IsLower(c)) {       /* ASCII small capital? */
+                    b |= 1; c -= 0x20;
+                }
+            }
+            sfn[i++] = c;
+        }
+    }
+    *path = &p[si];                     /* Return pointer to the next segment */
+    c = (c <= ' ') ? NS_LAST : 0;       /* Set last segment flag if end of path */
+
+    if (!i) return FR_INVALID_NAME;     /* Reject nul string */
+    if (sfn[0] == DDE) sfn[0] = NDDE;   /* When first char collides with DDE, replace it with 0x05 */
+
+    if (ni == 8) b <<= 2;
+    if ((b & 0x03) == 0x01) c |= NS_EXT;    /* NT flag (Name extension has only small capital) */
+    if ((b & 0x0C) == 0x04) c |= NS_BODY;   /* NT flag (Name body has only small capital) */
+
+    sfn[NS] = c;        /* Store NT flag, File name is created */
+
+    return FR_OK;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get file information from directory entry                             */
+/*-----------------------------------------------------------------------*/
+#if _FS_MINIMIZE <= 1
+static
+void get_fileinfo (     /* No return code */
+    FATFS_DIR *dj,            /* Pointer to the directory object */
+    FILINFO *fno        /* Pointer to the file information to be filled */
+)
+{
+    UINT i;
+    BYTE nt, *dir;
+    TCHAR *p, c;
+
+
+    p = fno->fname;
+    if (dj->sect) {
+        dir = dj->dir;
+        nt = dir[DIR_NTres];        /* NT flag */
+        for (i = 0; i < 8; i++) {   /* Copy name body */
+            c = dir[i];
+            if (c == ' ') break;
+            if (c == NDDE) c = (TCHAR)DDE;
+            if (_USE_LFN && (nt & NS_BODY) && IsUpper(c)) c += 0x20;
+#if _LFN_UNICODE
+            if (IsDBCS1(c) && i < 7 && IsDBCS2(dir[i+1]))
+                c = (c << 8) | dir[++i];
+            c = ff_convert(c, 1);
+            if (!c) c = '?';
+#endif
+            *p++ = c;
+        }
+        if (dir[8] != ' ') {        /* Copy name extension */
+            *p++ = '.';
+            for (i = 8; i < 11; i++) {
+                c = dir[i];
+                if (c == ' ') break;
+                if (_USE_LFN && (nt & NS_EXT) && IsUpper(c)) c += 0x20;
+#if _LFN_UNICODE
+                if (IsDBCS1(c) && i < 10 && IsDBCS2(dir[i+1]))
+                    c = (c << 8) | dir[++i];
+                c = ff_convert(c, 1);
+                if (!c) c = '?';
+#endif
+                *p++ = c;
+            }
+        }
+        fno->fattrib = dir[DIR_Attr];               /* Attribute */
+        fno->fsize = LD_DWORD(dir+DIR_FileSize);    /* Size */
+        fno->fdate = LD_WORD(dir+DIR_WrtDate);      /* Date */
+        fno->ftime = LD_WORD(dir+DIR_WrtTime);      /* Time */
+    }
+    *p = 0;     /* Terminate SFN str by a \0 */
+
+#if _USE_LFN
+    if (fno->lfname && fno->lfsize) {
+        TCHAR *tp = fno->lfname;
+        WCHAR w, *lfn;
+
+        i = 0;
+        if (dj->sect && dj->lfn_idx != 0xFFFF) {/* Get LFN if available */
+            lfn = dj->lfn;
+            while ((w = *lfn++) != 0) {         /* Get an LFN char */
+#if !_LFN_UNICODE
+                w = ff_convert(w, 0);           /* Unicode -> OEM conversion */
+                if (!w) { i = 0; break; }       /* Could not convert, no LFN */
+                if (_DF1S && w >= 0x100)        /* Put 1st byte if it is a DBC (always false on SBCS cfg) */
+                    tp[i++] = (TCHAR)(w >> 8);
+#endif
+                if (i >= fno->lfsize - 1) { i = 0; break; } /* Buffer overflow, no LFN */
+                tp[i++] = (TCHAR)w;
+            }
+        }
+        tp[i] = 0;  /* Terminate the LFN str by a \0 */
+    }
+#endif
+}
+#endif /* _FS_MINIMIZE <= 1 */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Follow a file path                                                    */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT follow_path (   /* FR_OK(0): successful, !=0: error code */
+    FATFS_DIR *dj,            /* Directory object to return last directory and found object */
+    const TCHAR *path   /* Full-path string to find a file or directory */
+)
+{
+    FRESULT res;
+    BYTE *dir, ns;
+
+
+#if _FS_RPATH
+    if (*path == '/' || *path == '\\') { /* There is a heading separator */
+        path++; dj->sclust = 0;     /* Strip it and start from the root dir */
+    } else {                            /* No heading separator */
+        dj->sclust = dj->fs->cdir;  /* Start from the current dir */
+    }
+#else
+    if (*path == '/' || *path == '\\')  /* Strip heading separator if exist */
+        path++;
+    dj->sclust = 0;                     /* Start from the root dir */
+#endif
+
+    if ((UINT)*path < ' ') {            /* Nul path means the start directory itself */
+        res = dir_sdi(dj, 0);
+        dj->dir = 0;
+    } else {                            /* Follow path */
+        for (;;) {
+            res = create_name(dj, &path);   /* Get a segment */
+            if (res != FR_OK) break;
+            res = dir_find(dj);             /* Find it */
+            ns = *(dj->fn+NS);
+            if (res != FR_OK) {             /* Failed to find the object */
+                if (res != FR_NO_FILE) break;   /* Abort if any hard error occurred */
+                /* Object not found */
+                if (_FS_RPATH && (ns & NS_DOT)) {   /* If dot entry is not exit */
+                    dj->sclust = 0; dj->dir = 0;    /* It is the root dir */
+                    res = FR_OK;
+                    if (!(ns & NS_LAST)) continue;
+                } else {                            /* Could not find the object */
+                    if (!(ns & NS_LAST)) res = FR_NO_PATH;
+                }
+                break;
+            }
+            if (ns & NS_LAST) break;            /* Last segment match. Function completed. */
+            dir = dj->dir;                      /* There is next segment. Follow the sub directory */
+            if (!(dir[DIR_Attr] & AM_DIR)) {    /* Cannot follow because it is a file */
+                res = FR_NO_PATH; break;
+            }
+            dj->sclust = ld_clust(dj->fs, dir);
+        }
+    }
+
+    return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load a sector and check if it is an FAT Volume Boot Record            */
+/*-----------------------------------------------------------------------*/
+
+static
+BYTE check_fs ( /* 0:FAT-VBR, 1:Any BR but not FAT, 2:Not a BR, 3:Disk error */
+    FATFS *fs,  /* File system object */
+    DWORD sect  /* Sector# (lba) to check if it is an FAT boot record or not */
+)
+{
+    if (disk_read(fs->drv, fs->win, sect, 1) != RES_OK) /* Load boot record */
+        return 3;
+    if (LD_WORD(&fs->win[BS_55AA]) != 0xAA55)       /* Check record signature (always placed at offset 510 even if the sector size is >512) */
+        return 2;
+
+    if ((LD_DWORD(&fs->win[BS_FilSysType]) & 0xFFFFFF) == 0x544146) /* Check "FAT" string */
+        return 0;
+    if ((LD_DWORD(&fs->win[BS_FilSysType32]) & 0xFFFFFF) == 0x544146)
+        return 0;
+
+    return 1;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file system object is valid or not                       */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT chk_mounted (   /* FR_OK(0): successful, !=0: any error occurred */
+    const TCHAR **path, /* Pointer to pointer to the path name (drive number) */
+    FATFS **rfs,        /* Pointer to pointer to the found file system object */
+    BYTE wmode          /* !=0: Check write protection for write access */
+)
+{
+    BYTE fmt, b, pi, *tbl;
+    UINT vol;
+    DSTATUS stat;
+    DWORD bsect, fasize, tsect, sysect, nclst, szbfat;
+    WORD nrsv;
+    const TCHAR *p = *path;
+    FATFS *fs;
+
+
+    /* Get logical drive number from the path name */
+    vol = p[0] - '0';                   /* Is there a drive number? */
+    if (vol <= 9 && p[1] == ':') {      /* Found a drive number, get and strip it */
+        p += 2; *path = p;              /* Return pointer to the path name */
+    } else {                            /* No drive number is given */
+#if _FS_RPATH
+        vol = CurrVol;                  /* Use current drive */
+#else
+        vol = 0;                        /* Use drive 0 */
+#endif
+    }
+
+    /* Check if the file system object is valid or not */
+    *rfs = 0;
+    if (vol >= _VOLUMES)                /* Is the drive number valid? */
+        return FR_INVALID_DRIVE;
+    fs = FatFs[vol];                    /* Get corresponding file system object */
+    if (!fs) return FR_NOT_ENABLED;     /* Is the file system object available? */
+
+    ENTER_FF(fs);                       /* Lock file system */
+
+    *rfs = fs;                          /* Return pointer to the corresponding file system object */
+    if (fs->fs_type) {                  /* If the volume has been mounted */
+        stat = disk_status(fs->drv);
+        if (!(stat & STA_NOINIT)) {     /* and the physical drive is kept initialized (has not been changed), */
+            if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check write protection if needed */
+                return FR_WRITE_PROTECTED;
+            return FR_OK;               /* The file system object is valid */
+        }
+    }
+
+    /* The file system object is not valid. */
+    /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */
+
+    fs->fs_type = 0;                    /* Clear the file system object */
+    fs->drv = LD2PD(vol);               /* Bind the logical drive and a physical drive */
+    stat = disk_initialize(fs->drv);    /* Initialize the physical drive */
+    if (stat & STA_NOINIT)              /* Check if the initialization succeeded */
+        return FR_NOT_READY;            /* Failed to initialize due to no medium or hard error */
+    if (!_FS_READONLY && wmode && (stat & STA_PROTECT)) /* Check disk write protection if needed */
+        return FR_WRITE_PROTECTED;
+#if _MAX_SS != 512                      /* Get disk sector size (variable sector size cfg only) */
+    if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &fs->ssize) != RES_OK)
+        return FR_DISK_ERR;
+#endif
+    /* Search FAT partition on the drive. Supports only generic partitions, FDISK and SFD. */
+    fmt = check_fs(fs, bsect = 0);      /* Load sector 0 and check if it is an FAT-VBR (in SFD) */
+    if (LD2PT(vol) && !fmt) fmt = 1;    /* Force non-SFD if the volume is forced partition */
+    if (fmt == 1) {                     /* Not an FAT-VBR, the physical drive can be partitioned */
+        /* Check the partition listed in the partition table */
+        pi = LD2PT(vol);
+        if (pi) pi--;
+        tbl = &fs->win[MBR_Table + pi * SZ_PTE];/* Partition table */
+        if (tbl[4]) {                       /* Is the partition existing? */
+            bsect = LD_DWORD(&tbl[8]);      /* Partition offset in LBA */
+            fmt = check_fs(fs, bsect);      /* Check the partition */
+        }
+    }
+    if (fmt == 3) return FR_DISK_ERR;
+    if (fmt) return FR_NO_FILESYSTEM;       /* No FAT volume is found */
+
+    /* An FAT volume is found. Following code initializes the file system object */
+
+    if (LD_WORD(fs->win+BPB_BytsPerSec) != SS(fs))      /* (BPB_BytsPerSec must be equal to the physical sector size) */
+        return FR_NO_FILESYSTEM;
+
+    fasize = LD_WORD(fs->win+BPB_FATSz16);              /* Number of sectors per FAT */
+    if (!fasize) fasize = LD_DWORD(fs->win+BPB_FATSz32);
+    fs->fsize = fasize;
+
+    fs->n_fats = b = fs->win[BPB_NumFATs];              /* Number of FAT copies */
+    if (b != 1 && b != 2) return FR_NO_FILESYSTEM;      /* (Must be 1 or 2) */
+    fasize *= b;                                        /* Number of sectors for FAT area */
+
+    fs->csize = b = fs->win[BPB_SecPerClus];            /* Number of sectors per cluster */
+    if (!b || (b & (b - 1))) return FR_NO_FILESYSTEM;   /* (Must be power of 2) */
+
+    fs->n_rootdir = LD_WORD(fs->win+BPB_RootEntCnt);    /* Number of root directory entries */
+    if (fs->n_rootdir % (SS(fs) / SZ_DIR)) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be sector aligned) */
+
+    tsect = LD_WORD(fs->win+BPB_TotSec16);              /* Number of sectors on the volume */
+    if (!tsect) tsect = LD_DWORD(fs->win+BPB_TotSec32);
+
+    nrsv = LD_WORD(fs->win+BPB_RsvdSecCnt);             /* Number of reserved sectors */
+    if (!nrsv) return FR_NO_FILESYSTEM;                 /* (BPB_RsvdSecCnt must not be 0) */
+
+    /* Determine the FAT sub type */
+    sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZ_DIR); /* RSV+FAT+DIR */
+    if (tsect < sysect) return FR_NO_FILESYSTEM;        /* (Invalid volume size) */
+    nclst = (tsect - sysect) / fs->csize;               /* Number of clusters */
+    if (!nclst) return FR_NO_FILESYSTEM;                /* (Invalid volume size) */
+    fmt = FS_FAT12;
+    if (nclst >= MIN_FAT16) fmt = FS_FAT16;
+    if (nclst >= MIN_FAT32) fmt = FS_FAT32;
+
+    /* Boundaries and Limits */
+    fs->n_fatent = nclst + 2;                           /* Number of FAT entries */
+    fs->database = bsect + sysect;                      /* Data start sector */
+    fs->fatbase = bsect + nrsv;                         /* FAT start sector */
+    if (fmt == FS_FAT32) {
+        if (fs->n_rootdir) return FR_NO_FILESYSTEM;     /* (BPB_RootEntCnt must be 0) */
+        fs->dirbase = LD_DWORD(fs->win+BPB_RootClus);   /* Root directory start cluster */
+        szbfat = fs->n_fatent * 4;                      /* (Required FAT size) */
+    } else {
+        if (!fs->n_rootdir) return FR_NO_FILESYSTEM;    /* (BPB_RootEntCnt must not be 0) */
+        fs->dirbase = fs->fatbase + fasize;             /* Root directory start sector */
+        szbfat = (fmt == FS_FAT16) ?                    /* (Required FAT size) */
+            fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
+    }
+    if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs))   /* (BPB_FATSz must not be less than required) */
+        return FR_NO_FILESYSTEM;
+
+#if !_FS_READONLY
+    /* Initialize cluster allocation information */
+    fs->free_clust = 0xFFFFFFFF;
+    fs->last_clust = 0;
+
+    /* Get fsinfo if available */
+    if (fmt == FS_FAT32) {
+        fs->fsi_flag = 0;
+        fs->fsi_sector = bsect + LD_WORD(fs->win+BPB_FSInfo);
+        if (disk_read(fs->drv, fs->win, fs->fsi_sector, 1) == RES_OK &&
+            LD_WORD(fs->win+BS_55AA) == 0xAA55 &&
+            LD_DWORD(fs->win+FSI_LeadSig) == 0x41615252 &&
+            LD_DWORD(fs->win+FSI_StrucSig) == 0x61417272) {
+                fs->last_clust = LD_DWORD(fs->win+FSI_Nxt_Free);
+                fs->free_clust = LD_DWORD(fs->win+FSI_Free_Count);
+        }
+    }
+#endif
+    fs->fs_type = fmt;      /* FAT sub-type */
+    fs->id = ++Fsid;        /* File system mount ID */
+    fs->winsect = 0;        /* Invalidate sector cache */
+    fs->wflag = 0;
+#if _FS_RPATH
+    fs->cdir = 0;           /* Current directory (root dir) */
+#endif
+#if _FS_LOCK                /* Clear file lock semaphores */
+    clear_lock(fs);
+#endif
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file/dir object is valid or not                          */
+/*-----------------------------------------------------------------------*/
+
+static
+FRESULT validate (  /* FR_OK(0): The object is valid, !=0: Invalid */
+    void* obj       /* Pointer to the object FIL/DIR to check validity */
+)
+{
+    FIL *fil;
+
+
+    fil = (FIL*)obj;    /* Assuming offset of fs and id in the FIL/DIR is identical */
+    if (!fil->fs || !fil->fs->fs_type || fil->fs->id != fil->id)
+        return FR_INVALID_OBJECT;
+
+    ENTER_FF(fil->fs);      /* Lock file system */
+
+    if (disk_status(fil->fs->drv) & STA_NOINIT)
+        return FR_NOT_READY;
+
+    return FR_OK;
+}
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Public Functions
+
+--------------------------------------------------------------------------*/
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Mount/Unmount a Logical Drive                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mount (
+    BYTE vol,       /* Logical drive number to be mounted/unmounted */
+    FATFS *fs       /* Pointer to new file system object (NULL for unmount)*/
+)
+{
+    FATFS *rfs;
+
+
+    if (vol >= _VOLUMES)        /* Check if the drive number is valid */
+        return FR_INVALID_DRIVE;
+    rfs = FatFs[vol];           /* Get current fs object */
+
+    if (rfs) {
+#if _FS_LOCK
+        clear_lock(rfs);
+#endif
+#if _FS_REENTRANT               /* Discard sync object of the current volume */
+        if (!ff_del_syncobj(rfs->sobj)) return FR_INT_ERR;
+#endif
+        rfs->fs_type = 0;       /* Clear old fs object */
+    }
+
+    if (fs) {
+        fs->fs_type = 0;        /* Clear new fs object */
+#if _FS_REENTRANT               /* Create sync object for the new volume */
+        if (!ff_cre_syncobj(vol, &fs->sobj)) return FR_INT_ERR;
+#endif
+    }
+    FatFs[vol] = fs;            /* Register new fs object */
+
+    return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Open or Create a File                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_open (
+    FIL *fp,            /* Pointer to the blank file object */
+    const TCHAR *path,  /* Pointer to the file name */
+    BYTE mode           /* Access mode and file open mode flags */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    BYTE *dir;
+    DEF_NAMEBUF;
+
+
+    if (!fp) return FR_INVALID_OBJECT;
+    fp->fs = 0;         /* Clear file object */
+
+#if !_FS_READONLY
+    mode &= FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW;
+    res = chk_mounted(&path, &dj.fs, (BYTE)(mode & ~FA_READ));
+#else
+    mode &= FA_READ;
+    res = chk_mounted(&path, &dj.fs, 0);
+#endif
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);   /* Follow the file path */
+        dir = dj.dir;
+#if !_FS_READONLY   /* R/W configuration */
+        if (res == FR_OK) {
+            if (!dir)   /* Current dir itself */
+                res = FR_INVALID_NAME;
+#if _FS_LOCK
+            else
+                res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
+#endif
+        }
+        /* Create or Open a file */
+        if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+            DWORD dw, cl;
+
+            if (res != FR_OK) {                 /* No file, create new */
+                if (res == FR_NO_FILE)          /* There is no file to open, create a new entry */
+#if _FS_LOCK
+                    res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
+#else
+                    res = dir_register(&dj);
+#endif
+                mode |= FA_CREATE_ALWAYS;       /* File is created */
+                dir = dj.dir;                   /* New entry */
+            }
+            else {                              /* Any object is already existing */
+                if (dir[DIR_Attr] & (AM_RDO | AM_DIR)) {    /* Cannot overwrite it (R/O or DIR) */
+                    res = FR_DENIED;
+                } else {
+                    if (mode & FA_CREATE_NEW)   /* Cannot create as new file */
+                        res = FR_EXIST;
+                }
+            }
+            if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) {    /* Truncate it if overwrite mode */
+                dw = get_fattime();                 /* Created time */
+                ST_DWORD(dir+DIR_CrtTime, dw);
+                dir[DIR_Attr] = 0;                  /* Reset attribute */
+                ST_DWORD(dir+DIR_FileSize, 0);      /* size = 0 */
+                cl = ld_clust(dj.fs, dir);          /* Get start cluster */
+                st_clust(dir, 0);                   /* cluster = 0 */
+                dj.fs->wflag = 1;
+                if (cl) {                           /* Remove the cluster chain if exist */
+                    dw = dj.fs->winsect;
+                    res = remove_chain(dj.fs, cl);
+                    if (res == FR_OK) {
+                        dj.fs->last_clust = cl - 1; /* Reuse the cluster hole */
+                        res = move_window(dj.fs, dw);
+                    }
+                }
+            }
+        }
+        else {  /* Open an existing file */
+            if (res == FR_OK) {                     /* Follow succeeded */
+                if (dir[DIR_Attr] & AM_DIR) {       /* It is a directory */
+                    res = FR_NO_FILE;
+                } else {
+                    if ((mode & FA_WRITE) && (dir[DIR_Attr] & AM_RDO)) /* R/O violation */
+                        res = FR_DENIED;
+                }
+            }
+        }
+        if (res == FR_OK) {
+            if (mode & FA_CREATE_ALWAYS)            /* Set file change flag if created or overwritten */
+                mode |= FA__WRITTEN;
+            fp->dir_sect = dj.fs->winsect;          /* Pointer to the directory entry */
+            fp->dir_ptr = dir;
+#if _FS_LOCK
+            fp->lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);
+            if (!fp->lockid) res = FR_INT_ERR;
+#endif
+        }
+
+#else               /* R/O configuration */
+        if (res == FR_OK) {                 /* Follow succeeded */
+            dir = dj.dir;
+            if (!dir) {                     /* Current dir itself */
+                res = FR_INVALID_NAME;
+            } else {
+                if (dir[DIR_Attr] & AM_DIR) /* It is a directory */
+                    res = FR_NO_FILE;
+            }
+        }
+#endif
+        FREE_BUF();
+
+        if (res == FR_OK) {
+            fp->flag = mode;                    /* File access mode */
+            fp->sclust = ld_clust(dj.fs, dir);  /* File start cluster */
+            fp->fsize = LD_DWORD(dir+DIR_FileSize); /* File size */
+            fp->fptr = 0;                       /* File pointer */
+            fp->dsect = 0;
+#if _USE_FASTSEEK
+            fp->cltbl = 0;                      /* Normal seek mode */
+#endif
+            fp->fs = dj.fs; fp->id = dj.fs->id; /* Validate file object */
+        }
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read File                                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read (
+    FIL *fp,        /* Pointer to the file object */
+    void *buff,     /* Pointer to data buffer */
+    UINT btr,       /* Number of bytes to read */
+    UINT *br        /* Pointer to number of bytes read */
+)
+{
+    FRESULT res;
+    DWORD clst, sect, remain;
+    UINT rcnt, cc;
+    BYTE csect, *rbuff = (BYTE *)buff;
+
+
+    *br = 0;    /* Clear read byte counter */
+
+    res = validate(fp);                         /* Check validity */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                   /* Aborted file? */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_READ))                  /* Check access mode */
+        LEAVE_FF(fp->fs, FR_DENIED);
+    remain = fp->fsize - fp->fptr;
+    if (btr > remain) btr = (UINT)remain;       /* Truncate btr by remaining bytes */
+
+    for ( ;  btr;                               /* Repeat until all data read */
+        rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) {
+        if ((fp->fptr % SS(fp->fs)) == 0) {     /* On the sector boundary? */
+            csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
+            if (!csect) {                       /* On the cluster boundary? */
+                if (fp->fptr == 0) {            /* On the top of the file? */
+                    clst = fp->sclust;          /* Follow from the origin */
+                } else {                        /* Middle or end of the file */
+#if _USE_FASTSEEK
+                    if (fp->cltbl)
+                        clst = clmt_clust(fp, fp->fptr);    /* Get cluster# from the CLMT */
+                    else
+#endif
+                        clst = get_fat(fp->fs, fp->clust);  /* Follow cluster chain on the FAT */
+                }
+                if (clst < 2) ABORT(fp->fs, FR_INT_ERR);
+                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                fp->clust = clst;               /* Update current cluster */
+            }
+            sect = clust2sect(fp->fs, fp->clust);   /* Get current sector */
+            if (!sect) ABORT(fp->fs, FR_INT_ERR);
+            sect += csect;
+            cc = btr / SS(fp->fs);              /* When remaining bytes >= sector size, */
+            if (cc) {                           /* Read maximum contiguous sectors directly */
+                if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */
+                    cc = fp->fs->csize - csect;
+                if (disk_read(fp->fs->drv, rbuff, sect, (BYTE)cc) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+#if !_FS_READONLY && _FS_MINIMIZE <= 2          /* Replace one of the read sectors with cached data if it contains a dirty sector */
+#if _FS_TINY
+                if (fp->fs->wflag && fp->fs->winsect - sect < cc)
+                    mem_cpy(rbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), fp->fs->win, SS(fp->fs));
+#else
+                if ((fp->flag & FA__DIRTY) && fp->dsect - sect < cc)
+                    mem_cpy(rbuff + ((fp->dsect - sect) * SS(fp->fs)), fp->buf, SS(fp->fs));
+#endif
+#endif
+                rcnt = SS(fp->fs) * cc;         /* Number of bytes transferred */
+                continue;
+            }
+#if !_FS_TINY
+            if (fp->dsect != sect) {            /* Load data sector if not in cache */
+#if !_FS_READONLY
+                if (fp->flag & FA__DIRTY) {     /* Write-back dirty sector cache */
+                    if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+                        ABORT(fp->fs, FR_DISK_ERR);
+                    fp->flag &= ~FA__DIRTY;
+                }
+#endif
+                if (disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK) /* Fill sector cache */
+                    ABORT(fp->fs, FR_DISK_ERR);
+            }
+#endif
+            fp->dsect = sect;
+        }
+        rcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));  /* Get partial sector data from sector buffer */
+        if (rcnt > btr) rcnt = btr;
+#if _FS_TINY
+        if (move_window(fp->fs, fp->dsect))     /* Move sector window */
+            ABORT(fp->fs, FR_DISK_ERR);
+        mem_cpy(rbuff, &fp->fs->win[fp->fptr % SS(fp->fs)], rcnt);  /* Pick partial sector */
+#else
+        mem_cpy(rbuff, &fp->buf[fp->fptr % SS(fp->fs)], rcnt);  /* Pick partial sector */
+#endif
+    }
+
+    LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Write File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write (
+    FIL *fp,            /* Pointer to the file object */
+    const void *buff,   /* Pointer to the data to be written */
+    UINT btw,           /* Number of bytes to write */
+    UINT *bw            /* Pointer to number of bytes written */
+)
+{
+    FRESULT res;
+    DWORD clst, sect;
+    UINT wcnt, cc;
+    const BYTE *wbuff = (const BYTE *)buff;
+    BYTE csect;
+
+
+    *bw = 0;    /* Clear write byte counter */
+
+    res = validate(fp);                     /* Check validity */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)               /* Aborted file? */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_WRITE))             /* Check access mode */
+        LEAVE_FF(fp->fs, FR_DENIED);
+    if ((DWORD)(fp->fsize + btw) < fp->fsize) btw = 0;  /* File size cannot reach 4GB */
+
+    for ( ;  btw;                           /* Repeat until all data written */
+        wbuff += wcnt, fp->fptr += wcnt, *bw += wcnt, btw -= wcnt) {
+        if ((fp->fptr % SS(fp->fs)) == 0) { /* On the sector boundary? */
+            csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
+            if (!csect) {                   /* On the cluster boundary? */
+                if (fp->fptr == 0) {        /* On the top of the file? */
+                    clst = fp->sclust;      /* Follow from the origin */
+                    if (clst == 0)          /* When no cluster is allocated, */
+                        fp->sclust = clst = create_chain(fp->fs, 0);    /* Create a new cluster chain */
+                } else {                    /* Middle or end of the file */
+#if _USE_FASTSEEK
+                    if (fp->cltbl)
+                        clst = clmt_clust(fp, fp->fptr);    /* Get cluster# from the CLMT */
+                    else
+#endif
+                        clst = create_chain(fp->fs, fp->clust); /* Follow or stretch cluster chain on the FAT */
+                }
+                if (clst == 0) break;       /* Could not allocate a new cluster (disk full) */
+                if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                fp->clust = clst;           /* Update current cluster */
+            }
+#if _FS_TINY
+            if (fp->fs->winsect == fp->dsect && move_window(fp->fs, 0)) /* Write-back sector cache */
+                ABORT(fp->fs, FR_DISK_ERR);
+#else
+            if (fp->flag & FA__DIRTY) {     /* Write-back sector cache */
+                if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+                fp->flag &= ~FA__DIRTY;
+            }
+#endif
+            sect = clust2sect(fp->fs, fp->clust);   /* Get current sector */
+            if (!sect) ABORT(fp->fs, FR_INT_ERR);
+            sect += csect;
+            cc = btw / SS(fp->fs);          /* When remaining bytes >= sector size, */
+            if (cc) {                       /* Write maximum contiguous sectors directly */
+                if (csect + cc > fp->fs->csize) /* Clip at cluster boundary */
+                    cc = fp->fs->csize - csect;
+                if (disk_write(fp->fs->drv, wbuff, sect, (BYTE)cc) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+#if _FS_TINY
+                if (fp->fs->winsect - sect < cc) {  /* Refill sector cache if it gets invalidated by the direct write */
+                    mem_cpy(fp->fs->win, wbuff + ((fp->fs->winsect - sect) * SS(fp->fs)), SS(fp->fs));
+                    fp->fs->wflag = 0;
+                }
+#else
+                if (fp->dsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
+                    mem_cpy(fp->buf, wbuff + ((fp->dsect - sect) * SS(fp->fs)), SS(fp->fs));
+                    fp->flag &= ~FA__DIRTY;
+                }
+#endif
+                wcnt = SS(fp->fs) * cc;     /* Number of bytes transferred */
+                continue;
+            }
+#if _FS_TINY
+            if (fp->fptr >= fp->fsize) {    /* Avoid silly cache filling at growing edge */
+                if (move_window(fp->fs, 0)) ABORT(fp->fs, FR_DISK_ERR);
+                fp->fs->winsect = sect;
+            }
+#else
+            if (fp->dsect != sect) {        /* Fill sector cache with file data */
+                if (fp->fptr < fp->fsize &&
+                    disk_read(fp->fs->drv, fp->buf, sect, 1) != RES_OK)
+                        ABORT(fp->fs, FR_DISK_ERR);
+            }
+#endif
+            fp->dsect = sect;
+        }
+        wcnt = SS(fp->fs) - ((UINT)fp->fptr % SS(fp->fs));/* Put partial sector into file I/O buffer */
+        if (wcnt > btw) wcnt = btw;
+#if _FS_TINY
+        if (move_window(fp->fs, fp->dsect)) /* Move sector window */
+            ABORT(fp->fs, FR_DISK_ERR);
+        mem_cpy(&fp->fs->win[fp->fptr % SS(fp->fs)], wbuff, wcnt);  /* Fit partial sector */
+        fp->fs->wflag = 1;
+#else
+        mem_cpy(&fp->buf[fp->fptr % SS(fp->fs)], wbuff, wcnt);  /* Fit partial sector */
+        fp->flag |= FA__DIRTY;
+#endif
+    }
+
+    if (fp->fptr > fp->fsize) fp->fsize = fp->fptr; /* Update file size if needed */
+    fp->flag |= FA__WRITTEN;                        /* Set file change flag */
+
+    LEAVE_FF(fp->fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File Object                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+    FIL *fp     /* Pointer to the file object */
+)
+{
+    FRESULT res;
+    DWORD tim;
+    BYTE *dir;
+
+
+    res = validate(fp);                 /* Check validity of the object */
+    if (res == FR_OK) {
+        if (fp->flag & FA__WRITTEN) {   /* Has the file been written? */
+#if !_FS_TINY   /* Write-back dirty buffer */
+            if (fp->flag & FA__DIRTY) {
+                if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+                    LEAVE_FF(fp->fs, FR_DISK_ERR);
+                fp->flag &= ~FA__DIRTY;
+            }
+#endif
+            /* Update the directory entry */
+            res = move_window(fp->fs, fp->dir_sect);
+            if (res == FR_OK) {
+                dir = fp->dir_ptr;
+                dir[DIR_Attr] |= AM_ARC;                    /* Set archive bit */
+                ST_DWORD(dir+DIR_FileSize, fp->fsize);      /* Update file size */
+                st_clust(dir, fp->sclust);                  /* Update start cluster */
+                tim = get_fattime();                        /* Update updated time */
+                ST_DWORD(dir+DIR_WrtTime, tim);
+                ST_WORD(dir+DIR_LstAccDate, 0);
+                fp->flag &= ~FA__WRITTEN;
+                fp->fs->wflag = 1;
+                res = sync(fp->fs);
+            }
+        }
+    }
+
+    LEAVE_FF(fp->fs, res);
+}
+
+#endif /* !_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_close (
+    FIL *fp     /* Pointer to the file object to be closed */
+)
+{
+    FRESULT res;
+
+
+#if _FS_READONLY
+    res = validate(fp);
+    {
+#if _FS_REENTRANT
+        FATFS *fs = fp->fs;
+#endif
+        if (res == FR_OK) fp->fs = 0;   /* Discard file object */
+        LEAVE_FF(fs, res);
+    }
+#else
+    res = f_sync(fp);       /* Flush cached data */
+#if _FS_LOCK
+    if (res == FR_OK) {     /* Decrement open counter */
+#if _FS_REENTRANT
+        FATFS *fs = fp->fs;;
+        res = validate(fp);
+        if (res == FR_OK) {
+            res = dec_lock(fp->lockid);
+            unlock_fs(fs, FR_OK);
+        }
+#else
+        res = dec_lock(fp->lockid);
+#endif
+    }
+#endif
+    if (res == FR_OK) fp->fs = 0;   /* Discard file object */
+    return res;
+#endif
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Current Drive/Directory Handlings                                     */
+/*-----------------------------------------------------------------------*/
+
+#if _FS_RPATH >= 1
+
+FRESULT f_chdrive (
+    BYTE drv        /* Drive number */
+)
+{
+    if (drv >= _VOLUMES) return FR_INVALID_DRIVE;
+
+    CurrVol = drv;
+
+    return FR_OK;
+}
+
+
+
+FRESULT f_chdir (
+    const TCHAR *path   /* Pointer to the directory path */
+)
+{
+    FRESULT res;
+    DIR dj;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 0);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);       /* Follow the path */
+        FREE_BUF();
+        if (res == FR_OK) {                 /* Follow completed */
+            if (!dj.dir) {
+                dj.fs->cdir = dj.sclust;    /* Start directory itself */
+            } else {
+                if (dj.dir[DIR_Attr] & AM_DIR)  /* Reached to the directory */
+                    dj.fs->cdir = ld_clust(dj.fs, dj.dir);
+                else
+                    res = FR_NO_PATH;       /* Reached but a file */
+            }
+        }
+        if (res == FR_NO_FILE) res = FR_NO_PATH;
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+#if _FS_RPATH >= 2
+FRESULT f_getcwd (
+    TCHAR *path,    /* Pointer to the directory path */
+    UINT sz_path    /* Size of path */
+)
+{
+    FRESULT res;
+    DIR dj;
+    UINT i, n;
+    DWORD ccl;
+    TCHAR *tp;
+    FILINFO fno;
+    DEF_NAMEBUF;
+
+
+    *path = 0;
+    res = chk_mounted((const TCHAR**)&path, &dj.fs, 0); /* Get current volume */
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        i = sz_path;        /* Bottom of buffer (dir stack base) */
+        dj.sclust = dj.fs->cdir;            /* Start to follow upper dir from current dir */
+        while ((ccl = dj.sclust) != 0) {    /* Repeat while current dir is a sub-dir */
+            res = dir_sdi(&dj, 1);          /* Get parent dir */
+            if (res != FR_OK) break;
+            res = dir_read(&dj);
+            if (res != FR_OK) break;
+            dj.sclust = ld_clust(dj.fs, dj.dir);    /* Goto parent dir */
+            res = dir_sdi(&dj, 0);
+            if (res != FR_OK) break;
+            do {                            /* Find the entry links to the child dir */
+                res = dir_read(&dj);
+                if (res != FR_OK) break;
+                if (ccl == ld_clust(dj.fs, dj.dir)) break;  /* Found the entry */
+                res = dir_next(&dj, 0);
+            } while (res == FR_OK);
+            if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
+            if (res != FR_OK) break;
+#if _USE_LFN
+            fno.lfname = path;
+            fno.lfsize = i;
+#endif
+            get_fileinfo(&dj, &fno);        /* Get the dir name and push it to the buffer */
+            tp = fno.fname;
+            if (_USE_LFN && *path) tp = path;
+            for (n = 0; tp[n]; n++) ;
+            if (i < n + 3) {
+                res = FR_NOT_ENOUGH_CORE; break;
+            }
+            while (n) path[--i] = tp[--n];
+            path[--i] = '/';
+        }
+        tp = path;
+        if (res == FR_OK) {
+            *tp++ = '0' + CurrVol;          /* Put drive number */
+            *tp++ = ':';
+            if (i == sz_path) {             /* Root-dir */
+                *tp++ = '/';
+            } else {                        /* Sub-dir */
+                do      /* Add stacked path str */
+                    *tp++ = path[i++];
+                while (i < sz_path);
+            }
+        }
+        *tp = 0;
+        FREE_BUF();
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+#endif /* _FS_RPATH >= 2 */
+#endif /* _FS_RPATH >= 1 */
+
+
+
+#if _FS_MINIMIZE <= 2
+/*-----------------------------------------------------------------------*/
+/* Seek File R/W Pointer                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_lseek (
+    FIL *fp,        /* Pointer to the file object */
+    DWORD ofs       /* File pointer from top of file */
+)
+{
+    FRESULT res;
+
+
+    res = validate(fp);                 /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)           /* Check abort flag */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+
+#if _USE_FASTSEEK
+    if (fp->cltbl) {    /* Fast seek */
+        DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;
+
+        if (ofs == CREATE_LINKMAP) {    /* Create CLMT */
+            tbl = fp->cltbl;
+            tlen = *tbl++; ulen = 2;    /* Given table size and required table size */
+            cl = fp->sclust;            /* Top of the chain */
+            if (cl) {
+                do {
+                    /* Get a fragment */
+                    tcl = cl; ncl = 0; ulen += 2;   /* Top, length and used items */
+                    do {
+                        pcl = cl; ncl++;
+                        cl = get_fat(fp->fs, cl);
+                        if (cl <= 1) ABORT(fp->fs, FR_INT_ERR);
+                        if (cl == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                    } while (cl == pcl + 1);
+                    if (ulen <= tlen) {     /* Store the length and top of the fragment */
+                        *tbl++ = ncl; *tbl++ = tcl;
+                    }
+                } while (cl < fp->fs->n_fatent);    /* Repeat until end of chain */
+            }
+            *fp->cltbl = ulen;  /* Number of items used */
+            if (ulen <= tlen)
+                *tbl = 0;       /* Terminate table */
+            else
+                res = FR_NOT_ENOUGH_CORE;   /* Given table size is smaller than required */
+
+        } else {                        /* Fast seek */
+            if (ofs > fp->fsize)        /* Clip offset at the file size */
+                ofs = fp->fsize;
+            fp->fptr = ofs;             /* Set file pointer */
+            if (ofs) {
+                fp->clust = clmt_clust(fp, ofs - 1);
+                dsc = clust2sect(fp->fs, fp->clust);
+                if (!dsc) ABORT(fp->fs, FR_INT_ERR);
+                dsc += (ofs - 1) / SS(fp->fs) & (fp->fs->csize - 1);
+                if (fp->fptr % SS(fp->fs) && dsc != fp->dsect) {    /* Refill sector cache if needed */
+#if !_FS_TINY
+#if !_FS_READONLY
+                    if (fp->flag & FA__DIRTY) {     /* Write-back dirty sector cache */
+                        if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+                            ABORT(fp->fs, FR_DISK_ERR);
+                        fp->flag &= ~FA__DIRTY;
+                    }
+#endif
+                    if (disk_read(fp->fs->drv, fp->buf, dsc, 1) != RES_OK)  /* Load current sector */
+                        ABORT(fp->fs, FR_DISK_ERR);
+#endif
+                    fp->dsect = dsc;
+                }
+            }
+        }
+    } else
+#endif
+
+    /* Normal Seek */
+    {
+        DWORD clst, bcs, nsect, ifptr;
+
+        if (ofs > fp->fsize                 /* In read-only mode, clip offset with the file size */
+#if !_FS_READONLY
+             && !(fp->flag & FA_WRITE)
+#endif
+            ) ofs = fp->fsize;
+
+        ifptr = fp->fptr;
+        fp->fptr = nsect = 0;
+        if (ofs) {
+            bcs = (DWORD)fp->fs->csize * SS(fp->fs);    /* Cluster size (byte) */
+            if (ifptr > 0 &&
+                (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
+                fp->fptr = (ifptr - 1) & ~(bcs - 1);    /* start from the current cluster */
+                ofs -= fp->fptr;
+                clst = fp->clust;
+            } else {                                    /* When seek to back cluster, */
+                clst = fp->sclust;                      /* start from the first cluster */
+#if !_FS_READONLY
+                if (clst == 0) {                        /* If no cluster chain, create a new chain */
+                    clst = create_chain(fp->fs, 0);
+                    if (clst == 1) ABORT(fp->fs, FR_INT_ERR);
+                    if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                    fp->sclust = clst;
+                }
+#endif
+                fp->clust = clst;
+            }
+            if (clst != 0) {
+                while (ofs > bcs) {                     /* Cluster following loop */
+#if !_FS_READONLY
+                    if (fp->flag & FA_WRITE) {          /* Check if in write mode or not */
+                        clst = create_chain(fp->fs, clst);  /* Force stretch if in write mode */
+                        if (clst == 0) {                /* When disk gets full, clip file size */
+                            ofs = bcs; break;
+                        }
+                    } else
+#endif
+                        clst = get_fat(fp->fs, clst);   /* Follow cluster chain if not in write mode */
+                    if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                    if (clst <= 1 || clst >= fp->fs->n_fatent) ABORT(fp->fs, FR_INT_ERR);
+                    fp->clust = clst;
+                    fp->fptr += bcs;
+                    ofs -= bcs;
+                }
+                fp->fptr += ofs;
+                if (ofs % SS(fp->fs)) {
+                    nsect = clust2sect(fp->fs, clst);   /* Current sector */
+                    if (!nsect) ABORT(fp->fs, FR_INT_ERR);
+                    nsect += ofs / SS(fp->fs);
+                }
+            }
+        }
+        if (fp->fptr % SS(fp->fs) && nsect != fp->dsect) {  /* Fill sector cache if needed */
+#if !_FS_TINY
+#if !_FS_READONLY
+            if (fp->flag & FA__DIRTY) {         /* Write-back dirty sector cache */
+                if (disk_write(fp->fs->drv, fp->buf, fp->dsect, 1) != RES_OK)
+                    ABORT(fp->fs, FR_DISK_ERR);
+                fp->flag &= ~FA__DIRTY;
+            }
+#endif
+            if (disk_read(fp->fs->drv, fp->buf, nsect, 1) != RES_OK)    /* Fill sector cache */
+                ABORT(fp->fs, FR_DISK_ERR);
+#endif
+            fp->dsect = nsect;
+        }
+#if !_FS_READONLY
+        if (fp->fptr > fp->fsize) {         /* Set file change flag if the file size is extended */
+            fp->fsize = fp->fptr;
+            fp->flag |= FA__WRITTEN;
+        }
+#endif
+    }
+
+    LEAVE_FF(fp->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Create a Directory Object                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_opendir (
+    FATFS_DIR *dj,            /* Pointer to directory object to create */
+    const TCHAR *path   /* Pointer to the directory path */
+)
+{
+    FRESULT res;
+    FATFS *fs;
+    DEF_NAMEBUF;
+
+
+    if (!dj) return FR_INVALID_OBJECT;
+
+    res = chk_mounted(&path, &dj->fs, 0);
+    fs = dj->fs;
+    if (res == FR_OK) {
+        INIT_BUF(*dj);
+        res = follow_path(dj, path);            /* Follow the path to the directory */
+        FREE_BUF();
+        if (res == FR_OK) {                     /* Follow completed */
+            if (dj->dir) {                      /* It is not the root dir */
+                if (dj->dir[DIR_Attr] & AM_DIR) {   /* The object is a directory */
+                    dj->sclust = ld_clust(fs, dj->dir);
+                } else {                        /* The object is not a directory */
+                    res = FR_NO_PATH;
+                }
+            }
+            if (res == FR_OK) {
+                dj->id = fs->id;
+                res = dir_sdi(dj, 0);           /* Rewind dir */
+            }
+        }
+        if (res == FR_NO_FILE) res = FR_NO_PATH;
+        if (res != FR_OK) dj->fs = 0;           /* Invalidate the dir object if function faild */
+    } else {
+        dj->fs = 0;
+    }
+
+    LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Directory Entry in Sequence                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_readdir (
+    FATFS_DIR *dj,            /* Pointer to the open directory object */
+    FILINFO *fno        /* Pointer to file information to return */
+)
+{
+    FRESULT res;
+    DEF_NAMEBUF;
+
+
+    res = validate(dj);                     /* Check validity of the object */
+    if (res == FR_OK) {
+        if (!fno) {
+            res = dir_sdi(dj, 0);           /* Rewind the directory object */
+        } else {
+            INIT_BUF(*dj);
+            res = dir_read(dj);             /* Read an directory item */
+            if (res == FR_NO_FILE) {        /* Reached end of dir */
+                dj->sect = 0;
+                res = FR_OK;
+            }
+            if (res == FR_OK) {             /* A valid entry is found */
+                get_fileinfo(dj, fno);      /* Get the object information */
+                res = dir_next(dj, 0);      /* Increment index for next */
+                if (res == FR_NO_FILE) {
+                    dj->sect = 0;
+                    res = FR_OK;
+                }
+            }
+            FREE_BUF();
+        }
+    }
+
+    LEAVE_FF(dj->fs, res);
+}
+
+
+
+#if _FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Get File Status                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_stat (
+    const TCHAR *path,  /* Pointer to the file path */
+    FILINFO *fno        /* Pointer to file information to return */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 0);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);   /* Follow the file path */
+        if (res == FR_OK) {             /* Follow completed */
+            if (dj.dir)     /* Found an object */
+                get_fileinfo(&dj, fno);
+            else            /* It is root dir */
+                res = FR_INVALID_NAME;
+        }
+        FREE_BUF();
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+#if !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Get Number of Free Clusters                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getfree (
+    const TCHAR *path,  /* Pointer to the logical drive number (root dir) */
+    DWORD *nclst,       /* Pointer to the variable to return number of free clusters */
+    FATFS **fatfs       /* Pointer to pointer to corresponding file system object to return */
+)
+{
+    FRESULT res;
+    FATFS *fs;
+    DWORD n, clst, sect, stat;
+    UINT i;
+    BYTE fat, *p;
+
+
+    /* Get drive number */
+    res = chk_mounted(&path, fatfs, 0);
+    fs = *fatfs;
+    if (res == FR_OK) {
+        /* If free_clust is valid, return it without full cluster scan */
+        if (fs->free_clust <= fs->n_fatent - 2) {
+            *nclst = fs->free_clust;
+        } else {
+            /* Get number of free clusters */
+            fat = fs->fs_type;
+            n = 0;
+            if (fat == FS_FAT12) {
+                clst = 2;
+                do {
+                    stat = get_fat(fs, clst);
+                    if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
+                    if (stat == 1) { res = FR_INT_ERR; break; }
+                    if (stat == 0) n++;
+                } while (++clst < fs->n_fatent);
+            } else {
+                clst = fs->n_fatent;
+                sect = fs->fatbase;
+                i = 0; p = 0;
+                do {
+                    if (!i) {
+                        res = move_window(fs, sect++);
+                        if (res != FR_OK) break;
+                        p = fs->win;
+                        i = SS(fs);
+                    }
+                    if (fat == FS_FAT16) {
+                        if (LD_WORD(p) == 0) n++;
+                        p += 2; i -= 2;
+                    } else {
+                        if ((LD_DWORD(p) & 0x0FFFFFFF) == 0) n++;
+                        p += 4; i -= 4;
+                    }
+                } while (--clst);
+            }
+            fs->free_clust = n;
+            if (fat == FS_FAT32) fs->fsi_flag = 1;
+            *nclst = n;
+        }
+    }
+    LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Truncate File                                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_truncate (
+    FIL *fp     /* Pointer to the file object */
+)
+{
+    FRESULT res;
+    DWORD ncl;
+
+
+    if (!fp) return FR_INVALID_OBJECT;
+
+    res = validate(fp);                     /* Check validity of the object */
+    if (res == FR_OK) {
+        if (fp->flag & FA__ERROR) {         /* Check abort flag */
+            res = FR_INT_ERR;
+        } else {
+            if (!(fp->flag & FA_WRITE))     /* Check access mode */
+                res = FR_DENIED;
+        }
+    }
+    if (res == FR_OK) {
+        if (fp->fsize > fp->fptr) {
+            fp->fsize = fp->fptr;   /* Set file size to current R/W point */
+            fp->flag |= FA__WRITTEN;
+            if (fp->fptr == 0) {    /* When set file size to zero, remove entire cluster chain */
+                res = remove_chain(fp->fs, fp->sclust);
+                fp->sclust = 0;
+            } else {                /* When truncate a part of the file, remove remaining clusters */
+                ncl = get_fat(fp->fs, fp->clust);
+                res = FR_OK;
+                if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+                if (ncl == 1) res = FR_INT_ERR;
+                if (res == FR_OK && ncl < fp->fs->n_fatent) {
+                    res = put_fat(fp->fs, fp->clust, 0x0FFFFFFF);
+                    if (res == FR_OK) res = remove_chain(fp->fs, ncl);
+                }
+            }
+        }
+        if (res != FR_OK) fp->flag |= FA__ERROR;
+    }
+
+    LEAVE_FF(fp->fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Delete a File or Directory                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_unlink (
+    const TCHAR *path       /* Pointer to the file or directory path */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj, sdj;
+    BYTE *dir;
+    DWORD dclst;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);       /* Follow the file path */
+        if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;          /* Cannot remove dot entry */
+#if _FS_LOCK
+        if (res == FR_OK) res = chk_lock(&dj, 2);   /* Cannot remove open file */
+#endif
+        if (res == FR_OK) {                 /* The object is accessible */
+            dir = dj.dir;
+            if (!dir) {
+                res = FR_INVALID_NAME;      /* Cannot remove the start directory */
+            } else {
+                if (dir[DIR_Attr] & AM_RDO)
+                    res = FR_DENIED;        /* Cannot remove R/O object */
+            }
+            dclst = ld_clust(dj.fs, dir);
+            if (res == FR_OK && (dir[DIR_Attr] & AM_DIR)) { /* Is it a sub-dir? */
+                if (dclst < 2) {
+                    res = FR_INT_ERR;
+                } else {
+                    mem_cpy(&sdj, &dj, sizeof (FATFS_DIR));   /* Check if the sub-dir is empty or not */
+                    sdj.sclust = dclst;
+                    res = dir_sdi(&sdj, 2);     /* Exclude dot entries */
+                    if (res == FR_OK) {
+                        res = dir_read(&sdj);
+                        if (res == FR_OK        /* Not empty dir */
+#if _FS_RPATH
+                        || dclst == dj.fs->cdir /* Current dir */
+#endif
+                        ) res = FR_DENIED;
+                        if (res == FR_NO_FILE) res = FR_OK; /* Empty */
+                    }
+                }
+            }
+            if (res == FR_OK) {
+                res = dir_remove(&dj);      /* Remove the directory entry */
+                if (res == FR_OK) {
+                    if (dclst)              /* Remove the cluster chain if exist */
+                        res = remove_chain(dj.fs, dclst);
+                    if (res == FR_OK) res = sync(dj.fs);
+                }
+            }
+        }
+        FREE_BUF();
+    }
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create a Directory                                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkdir (
+    const TCHAR *path       /* Pointer to the directory path */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    BYTE *dir, n;
+    DWORD dsc, dcl, pcl, tim = get_fattime();
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);           /* Follow the file path */
+        if (res == FR_OK) res = FR_EXIST;       /* Any object with same name is already existing */
+        if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;
+        if (res == FR_NO_FILE) {                /* Can create a new directory */
+            dcl = create_chain(dj.fs, 0);       /* Allocate a cluster for the new directory table */
+            res = FR_OK;
+            if (dcl == 0) res = FR_DENIED;      /* No space to allocate a new cluster */
+            if (dcl == 1) res = FR_INT_ERR;
+            if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;
+            if (res == FR_OK)                   /* Flush FAT */
+                res = move_window(dj.fs, 0);
+            if (res == FR_OK) {                 /* Initialize the new directory table */
+                dsc = clust2sect(dj.fs, dcl);
+                dir = dj.fs->win;
+                mem_set(dir, 0, SS(dj.fs));
+                mem_set(dir+DIR_Name, ' ', 8+3);    /* Create "." entry */
+                dir[DIR_Name] = '.';
+                dir[DIR_Attr] = AM_DIR;
+                ST_DWORD(dir+DIR_WrtTime, tim);
+                st_clust(dir, dcl);
+                mem_cpy(dir+SZ_DIR, dir, SZ_DIR);   /* Create ".." entry */
+                dir[33] = '.'; pcl = dj.sclust;
+                if (dj.fs->fs_type == FS_FAT32 && pcl == dj.fs->dirbase)
+                    pcl = 0;
+                st_clust(dir+SZ_DIR, pcl);
+                for (n = dj.fs->csize; n; n--) {    /* Write dot entries and clear following sectors */
+                    dj.fs->winsect = dsc++;
+                    dj.fs->wflag = 1;
+                    res = move_window(dj.fs, 0);
+                    if (res != FR_OK) break;
+                    mem_set(dir, 0, SS(dj.fs));
+                }
+            }
+            if (res == FR_OK) res = dir_register(&dj);  /* Register the object to the directoy */
+            if (res != FR_OK) {
+                remove_chain(dj.fs, dcl);           /* Could not register, remove cluster chain */
+            } else {
+                dir = dj.dir;
+                dir[DIR_Attr] = AM_DIR;             /* Attribute */
+                ST_DWORD(dir+DIR_WrtTime, tim);     /* Created time */
+                st_clust(dir, dcl);                 /* Table start cluster */
+                dj.fs->wflag = 1;
+                res = sync(dj.fs);
+            }
+        }
+        FREE_BUF();
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Attribute                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chmod (
+    const TCHAR *path,  /* Pointer to the file path */
+    BYTE value,         /* Attribute bits */
+    BYTE mask           /* Attribute mask to change */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    BYTE *dir;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);       /* Follow the file path */
+        FREE_BUF();
+        if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;
+        if (res == FR_OK) {
+            dir = dj.dir;
+            if (!dir) {                     /* Is it a root directory? */
+                res = FR_INVALID_NAME;
+            } else {                        /* File or sub directory */
+                mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;    /* Valid attribute mask */
+                dir[DIR_Attr] = (value & mask) | (dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */
+                dj.fs->wflag = 1;
+                res = sync(dj.fs);
+            }
+        }
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Timestamp                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_utime (
+    const TCHAR *path,  /* Pointer to the file/directory name */
+    const FILINFO *fno  /* Pointer to the time stamp to be set */
+)
+{
+    FRESULT res;
+    FATFS_DIR dj;
+    BYTE *dir;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path, &dj.fs, 1);
+    if (res == FR_OK) {
+        INIT_BUF(dj);
+        res = follow_path(&dj, path);   /* Follow the file path */
+        FREE_BUF();
+        if (_FS_RPATH && res == FR_OK && (dj.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;
+        if (res == FR_OK) {
+            dir = dj.dir;
+            if (!dir) {                 /* Root directory */
+                res = FR_INVALID_NAME;
+            } else {                    /* File or sub-directory */
+                ST_WORD(dir+DIR_WrtTime, fno->ftime);
+                ST_WORD(dir+DIR_WrtDate, fno->fdate);
+                dj.fs->wflag = 1;
+                res = sync(dj.fs);
+            }
+        }
+    }
+
+    LEAVE_FF(dj.fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Rename File/Directory                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_rename (
+    const TCHAR *path_old,  /* Pointer to the old name */
+    const TCHAR *path_new   /* Pointer to the new name */
+)
+{
+    FRESULT res;
+    FATFS_DIR djo, djn;
+    BYTE buf[21], *dir;
+    DWORD dw;
+    DEF_NAMEBUF;
+
+
+    res = chk_mounted(&path_old, &djo.fs, 1);
+    if (res == FR_OK) {
+        djn.fs = djo.fs;
+        INIT_BUF(djo);
+        res = follow_path(&djo, path_old);      /* Check old object */
+        if (_FS_RPATH && res == FR_OK && (djo.fn[NS] & NS_DOT))
+            res = FR_INVALID_NAME;
+#if _FS_LOCK
+        if (res == FR_OK) res = chk_lock(&djo, 2);
+#endif
+        if (res == FR_OK) {                     /* Old object is found */
+            if (!djo.dir) {                     /* Is root dir? */
+                res = FR_NO_FILE;
+            } else {
+                mem_cpy(buf, djo.dir+DIR_Attr, 21);     /* Save the object information except for name */
+                mem_cpy(&djn, &djo, sizeof (FATFS_DIR));      /* Check new object */
+                res = follow_path(&djn, path_new);
+                if (res == FR_OK) res = FR_EXIST;       /* The new object name is already existing */
+                if (res == FR_NO_FILE) {                /* Is it a valid path and no name collision? */
+/* Start critical section that an interruption or error can cause cross-link */
+                    res = dir_register(&djn);           /* Register the new entry */
+                    if (res == FR_OK) {
+                        dir = djn.dir;                  /* Copy object information except for name */
+                        mem_cpy(dir+13, buf+2, 19);
+                        dir[DIR_Attr] = buf[0] | AM_ARC;
+                        djo.fs->wflag = 1;
+                        if (djo.sclust != djn.sclust && (dir[DIR_Attr] & AM_DIR)) {     /* Update .. entry in the directory if needed */
+                            dw = clust2sect(djo.fs, ld_clust(djo.fs, dir));
+                            if (!dw) {
+                                res = FR_INT_ERR;
+                            } else {
+                                res = move_window(djo.fs, dw);
+                                dir = djo.fs->win+SZ_DIR;   /* .. entry */
+                                if (res == FR_OK && dir[1] == '.') {
+                                    dw = (djo.fs->fs_type == FS_FAT32 && djn.sclust == djo.fs->dirbase) ? 0 : djn.sclust;
+                                    st_clust(dir, dw);
+                                    djo.fs->wflag = 1;
+                                }
+                            }
+                        }
+                        if (res == FR_OK) {
+                            res = dir_remove(&djo);     /* Remove old entry */
+                            if (res == FR_OK)
+                                res = sync(djo.fs);
+                        }
+                    }
+/* End critical section */
+                }
+            }
+        }
+        FREE_BUF();
+    }
+    LEAVE_FF(djo.fs, res);
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _FS_MINIMIZE == 0 */
+#endif /* _FS_MINIMIZE <= 1 */
+#endif /* _FS_MINIMIZE <= 2 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Forward data to the stream directly (available on only tiny cfg)      */
+/*-----------------------------------------------------------------------*/
+#if _USE_FORWARD && _FS_TINY
+
+FRESULT f_forward (
+    FIL *fp,                        /* Pointer to the file object */
+    UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */
+    UINT btr,                       /* Number of bytes to forward */
+    UINT *bf                        /* Pointer to number of bytes forwarded */
+)
+{
+    FRESULT res;
+    DWORD remain, clst, sect;
+    UINT rcnt;
+    BYTE csect;
+
+
+    *bf = 0;    /* Clear transfer byte counter */
+
+    if (!fp) return FR_INVALID_OBJECT;
+
+    res = validate(fp);                             /* Check validity of the object */
+    if (res != FR_OK) LEAVE_FF(fp->fs, res);
+    if (fp->flag & FA__ERROR)                       /* Check error flag */
+        LEAVE_FF(fp->fs, FR_INT_ERR);
+    if (!(fp->flag & FA_READ))                      /* Check access mode */
+        LEAVE_FF(fp->fs, FR_DENIED);
+
+    remain = fp->fsize - fp->fptr;
+    if (btr > remain) btr = (UINT)remain;           /* Truncate btr by remaining bytes */
+
+    for ( ;  btr && (*func)(0, 0);                  /* Repeat until all data transferred or stream becomes busy */
+        fp->fptr += rcnt, *bf += rcnt, btr -= rcnt) {
+        csect = (BYTE)(fp->fptr / SS(fp->fs) & (fp->fs->csize - 1));    /* Sector offset in the cluster */
+        if ((fp->fptr % SS(fp->fs)) == 0) {         /* On the sector boundary? */
+            if (!csect) {                           /* On the cluster boundary? */
+                clst = (fp->fptr == 0) ?            /* On the top of the file? */
+                    fp->sclust : get_fat(fp->fs, fp->clust);
+                if (clst <= 1) ABORT(fp->fs, FR_INT_ERR);
+                if (clst == 0xFFFFFFFF) ABORT(fp->fs, FR_DISK_ERR);
+                fp->clust = clst;                   /* Update current cluster */
+            }
+        }
+        sect = clust2sect(fp->fs, fp->clust);       /* Get current data sector */
+        if (!sect) ABORT(fp->fs, FR_INT_ERR);
+        sect += csect;
+        if (move_window(fp->fs, sect))              /* Move sector window */
+            ABORT(fp->fs, FR_DISK_ERR);
+        fp->dsect = sect;
+        rcnt = SS(fp->fs) - (WORD)(fp->fptr % SS(fp->fs));  /* Forward data from sector window */
+        if (rcnt > btr) rcnt = btr;
+        rcnt = (*func)(&fp->fs->win[(WORD)fp->fptr % SS(fp->fs)], rcnt);
+        if (!rcnt) ABORT(fp->fs, FR_INT_ERR);
+    }
+
+    LEAVE_FF(fp->fs, FR_OK);
+}
+#endif /* _USE_FORWARD */
+
+
+
+#if _USE_MKFS && !_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Create File System on the Drive                                       */
+/*-----------------------------------------------------------------------*/
+#define N_ROOTDIR   512     /* Number of root dir entries for FAT12/16 */
+#define N_FATS      1       /* Number of FAT copies (1 or 2) */
+
+
+FRESULT f_mkfs (
+    BYTE drv,       /* Logical drive number */
+    BYTE sfd,       /* Partitioning rule 0:FDISK, 1:SFD */
+    UINT au         /* Allocation unit size [bytes] */
+)
+{
+    static const WORD vst[] = { 1024,   512,  256,  128,   64,    32,   16,    8,    4,    2,   0};
+    static const WORD cst[] = {32768, 16384, 8192, 4096, 2048, 16384, 8192, 4096, 2048, 1024, 512};
+    BYTE fmt, md, sys, *tbl, pdrv, part;
+    DWORD n_clst, vs, n, wsect;
+    UINT i;
+    DWORD b_vol, b_fat, b_dir, b_data;  /* LBA */
+    DWORD n_vol, n_rsv, n_fat, n_dir;   /* Size */
+    FATFS *fs;
+    DSTATUS stat;
+
+
+    /* Check mounted drive and clear work area */
+    if (drv >= _VOLUMES) return FR_INVALID_DRIVE;
+    if (sfd > 1) return FR_INVALID_PARAMETER;
+    if (au & (au - 1)) return FR_INVALID_PARAMETER;
+    fs = FatFs[drv];
+    if (!fs) return FR_NOT_ENABLED;
+    fs->fs_type = 0;
+    pdrv = LD2PD(drv);  /* Physical drive */
+    part = LD2PT(drv);  /* Partition (0:auto detect, 1-4:get from partition table)*/
+
+    /* Get disk statics */
+    stat = disk_initialize(pdrv);
+    if (stat & STA_NOINIT) return FR_NOT_READY;
+    if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+#if _MAX_SS != 512                  /* Get disk sector size */
+    if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK || SS(fs) > _MAX_SS)
+        return FR_DISK_ERR;
+#endif
+    if (_MULTI_PARTITION && part) {
+        /* Get partition information from partition table in the MBR */
+        if (disk_read(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR;
+        if (LD_WORD(fs->win+BS_55AA) != 0xAA55) return FR_MKFS_ABORTED;
+        tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
+        if (!tbl[4]) return FR_MKFS_ABORTED;    /* No partition? */
+        b_vol = LD_DWORD(tbl+8);    /* Volume start sector */
+        n_vol = LD_DWORD(tbl+12);   /* Volume size */
+    } else {
+        /* Create a partition in this function */
+        if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &n_vol) != RES_OK || n_vol < 128)
+            return FR_DISK_ERR;
+        b_vol = (sfd) ? 0 : 63;     /* Volume start sector */
+        n_vol -= b_vol;             /* Volume size */
+    }
+
+    if (!au) {              /* AU auto selection */
+        vs = n_vol / (2000 / (SS(fs) / 512));
+        for (i = 0; vs < vst[i]; i++) ;
+        au = cst[i];
+    }
+    au /= SS(fs);       /* Number of sectors per cluster */
+    if (au == 0) au = 1;
+    if (au > 128) au = 128;
+
+    /* Pre-compute number of clusters and FAT sub-type */
+    n_clst = n_vol / au;
+    fmt = FS_FAT12;
+    if (n_clst >= MIN_FAT16) fmt = FS_FAT16;
+    if (n_clst >= MIN_FAT32) fmt = FS_FAT32;
+
+    /* Determine offset and size of FAT structure */
+    if (fmt == FS_FAT32) {
+        n_fat = ((n_clst * 4) + 8 + SS(fs) - 1) / SS(fs);
+        n_rsv = 32;
+        n_dir = 0;
+    } else {
+        n_fat = (fmt == FS_FAT12) ? (n_clst * 3 + 1) / 2 + 3 : (n_clst * 2) + 4;
+        n_fat = (n_fat + SS(fs) - 1) / SS(fs);
+        n_rsv = 1;
+        n_dir = (DWORD)N_ROOTDIR * SZ_DIR / SS(fs);
+    }
+    b_fat = b_vol + n_rsv;              /* FAT area start sector */
+    b_dir = b_fat + n_fat * N_FATS;     /* Directory area start sector */
+    b_data = b_dir + n_dir;             /* Data area start sector */
+    if (n_vol < b_data + au - b_vol) return FR_MKFS_ABORTED;    /* Too small volume */
+
+    /* Align data start sector to erase block boundary (for flash memory media) */
+    if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &n) != RES_OK || !n || n > 32768) n = 1;
+    n = (b_data + n - 1) & ~(n - 1);    /* Next nearest erase block from current data start */
+    n = (n - b_data) / N_FATS;
+    if (fmt == FS_FAT32) {      /* FAT32: Move FAT offset */
+        n_rsv += n;
+        b_fat += n;
+    } else {                    /* FAT12/16: Expand FAT size */
+        n_fat += n;
+    }
+
+    /* Determine number of clusters and final check of validity of the FAT sub-type */
+    n_clst = (n_vol - n_rsv - n_fat * N_FATS - n_dir) / au;
+    if (   (fmt == FS_FAT16 && n_clst < MIN_FAT16)
+        || (fmt == FS_FAT32 && n_clst < MIN_FAT32))
+        return FR_MKFS_ABORTED;
+
+    switch (fmt) {  /* Determine system ID for partition table */
+    case FS_FAT12:  sys = 0x01; break;
+    case FS_FAT16:  sys = (n_vol < 0x10000) ? 0x04 : 0x06; break;
+    default:        sys = 0x0C;
+    }
+
+    if (_MULTI_PARTITION && part) {
+        /* Update system ID in the partition table */
+        tbl = &fs->win[MBR_Table + (part - 1) * SZ_PTE];
+        tbl[4] = sys;
+        if (disk_write(pdrv, fs->win, 0, 1) != RES_OK) return FR_DISK_ERR;
+        md = 0xF8;
+    } else {
+        if (sfd) {  /* No partition table (SFD) */
+            md = 0xF0;
+        } else {    /* Create partition table (FDISK) */
+            mem_set(fs->win, 0, SS(fs));
+            tbl = fs->win+MBR_Table;    /* Create partition table for single partition in the drive */
+            tbl[1] = 1;                     /* Partition start head */
+            tbl[2] = 1;                     /* Partition start sector */
+            tbl[3] = 0;                     /* Partition start cylinder */
+            tbl[4] = sys;                   /* System type */
+            tbl[5] = 254;                   /* Partition end head */
+            n = (b_vol + n_vol) / 63 / 255;
+            tbl[6] = (BYTE)((n >> 2) | 63); /* Partition end sector */
+            tbl[7] = (BYTE)n;               /* End cylinder */
+            ST_DWORD(tbl+8, 63);            /* Partition start in LBA */
+            ST_DWORD(tbl+12, n_vol);        /* Partition size in LBA */
+            ST_WORD(fs->win+BS_55AA, 0xAA55);   /* MBR signature */
+            if (disk_write(pdrv, fs->win, 0, 1) != RES_OK)  /* Write it to the MBR sector */
+                return FR_DISK_ERR;
+            md = 0xF8;
+        }
+    }
+
+    /* Create BPB in the VBR */
+    tbl = fs->win;                          /* Clear sector */
+    mem_set(tbl, 0, SS(fs));
+    mem_cpy(tbl, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code, OEM name */
+    i = SS(fs);                             /* Sector size */
+    ST_WORD(tbl+BPB_BytsPerSec, i);
+    tbl[BPB_SecPerClus] = (BYTE)au;         /* Sectors per cluster */
+    ST_WORD(tbl+BPB_RsvdSecCnt, n_rsv);     /* Reserved sectors */
+    tbl[BPB_NumFATs] = N_FATS;              /* Number of FATs */
+    i = (fmt == FS_FAT32) ? 0 : N_ROOTDIR;  /* Number of rootdir entries */
+    ST_WORD(tbl+BPB_RootEntCnt, i);
+    if (n_vol < 0x10000) {                  /* Number of total sectors */
+        ST_WORD(tbl+BPB_TotSec16, n_vol);
+    } else {
+        ST_DWORD(tbl+BPB_TotSec32, n_vol);
+    }
+    tbl[BPB_Media] = md;                    /* Media descriptor */
+    ST_WORD(tbl+BPB_SecPerTrk, 63);         /* Number of sectors per track */
+    ST_WORD(tbl+BPB_NumHeads, 255);         /* Number of heads */
+    ST_DWORD(tbl+BPB_HiddSec, b_vol);       /* Hidden sectors */
+    n = get_fattime();                      /* Use current time as VSN */
+    if (fmt == FS_FAT32) {
+        ST_DWORD(tbl+BS_VolID32, n);        /* VSN */
+        ST_DWORD(tbl+BPB_FATSz32, n_fat);   /* Number of sectors per FAT */
+        ST_DWORD(tbl+BPB_RootClus, 2);      /* Root directory start cluster (2) */
+        ST_WORD(tbl+BPB_FSInfo, 1);         /* FSInfo record offset (VBR+1) */
+        ST_WORD(tbl+BPB_BkBootSec, 6);      /* Backup boot record offset (VBR+6) */
+        tbl[BS_DrvNum32] = 0x80;            /* Drive number */
+        tbl[BS_BootSig32] = 0x29;           /* Extended boot signature */
+        mem_cpy(tbl+BS_VolLab32, "NO NAME    " "FAT32   ", 19); /* Volume label, FAT signature */
+    } else {
+        ST_DWORD(tbl+BS_VolID, n);          /* VSN */
+        ST_WORD(tbl+BPB_FATSz16, n_fat);    /* Number of sectors per FAT */
+        tbl[BS_DrvNum] = 0x80;              /* Drive number */
+        tbl[BS_BootSig] = 0x29;             /* Extended boot signature */
+        mem_cpy(tbl+BS_VolLab, "NO NAME    " "FAT     ", 19);   /* Volume label, FAT signature */
+    }
+    ST_WORD(tbl+BS_55AA, 0xAA55);           /* Signature (Offset is fixed here regardless of sector size) */
+    if (disk_write(pdrv, tbl, b_vol, 1) != RES_OK)  /* Write it to the VBR sector */
+        return FR_DISK_ERR;
+    if (fmt == FS_FAT32)                            /* Write backup VBR if needed (VBR+6) */
+        disk_write(pdrv, tbl, b_vol + 6, 1);
+
+    /* Initialize FAT area */
+    wsect = b_fat;
+    for (i = 0; i < N_FATS; i++) {      /* Initialize each FAT copy */
+        mem_set(tbl, 0, SS(fs));            /* 1st sector of the FAT  */
+        n = md;                             /* Media descriptor byte */
+        if (fmt != FS_FAT32) {
+            n |= (fmt == FS_FAT12) ? 0x00FFFF00 : 0xFFFFFF00;
+            ST_DWORD(tbl+0, n);             /* Reserve cluster #0-1 (FAT12/16) */
+        } else {
+            n |= 0xFFFFFF00;
+            ST_DWORD(tbl+0, n);             /* Reserve cluster #0-1 (FAT32) */
+            ST_DWORD(tbl+4, 0xFFFFFFFF);
+            ST_DWORD(tbl+8, 0x0FFFFFFF);    /* Reserve cluster #2 for root dir */
+        }
+        if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
+            return FR_DISK_ERR;
+        mem_set(tbl, 0, SS(fs));            /* Fill following FAT entries with zero */
+        for (n = 1; n < n_fat; n++) {       /* This loop may take a time on FAT32 volume due to many single sector writes */
+            if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
+                return FR_DISK_ERR;
+        }
+    }
+
+    /* Initialize root directory */
+    i = (fmt == FS_FAT32) ? au : n_dir;
+    do {
+        if (disk_write(pdrv, tbl, wsect++, 1) != RES_OK)
+            return FR_DISK_ERR;
+    } while (--i);
+
+#if _USE_ERASE  /* Erase data area if needed */
+    {
+        DWORD eb[2];
+
+        eb[0] = wsect; eb[1] = wsect + (n_clst - ((fmt == FS_FAT32) ? 1 : 0)) * au - 1;
+        disk_ioctl(pdrv, CTRL_ERASE_SECTOR, eb);
+    }
+#endif
+
+    /* Create FSInfo if needed */
+    if (fmt == FS_FAT32) {
+        ST_DWORD(tbl+FSI_LeadSig, 0x41615252);
+        ST_DWORD(tbl+FSI_StrucSig, 0x61417272);
+        ST_DWORD(tbl+FSI_Free_Count, n_clst - 1);   /* Number of free clusters */
+        ST_DWORD(tbl+FSI_Nxt_Free, 2);              /* Last allocated cluster# */
+        ST_WORD(tbl+BS_55AA, 0xAA55);
+        disk_write(pdrv, tbl, b_vol + 1, 1);    /* Write original (VBR+1) */
+        disk_write(pdrv, tbl, b_vol + 7, 1);    /* Write backup (VBR+7) */
+    }
+
+    return (disk_ioctl(pdrv, CTRL_SYNC, 0) == RES_OK) ? FR_OK : FR_DISK_ERR;
+}
+
+
+#if _MULTI_PARTITION == 2
+/*-----------------------------------------------------------------------*/
+/* Divide Physical Drive                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_fdisk (
+    BYTE pdrv,          /* Physical drive number */
+    const DWORD szt[],  /* Pointer to the size table for each partitions */
+    void* work          /* Pointer to the working buffer */
+)
+{
+    UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl;
+    BYTE s_hd, e_hd, *p, *buf = (BYTE*)work;
+    DSTATUS stat;
+    DWORD sz_disk, sz_part, s_part;
+
+
+    stat = disk_initialize(pdrv);
+    if (stat & STA_NOINIT) return FR_NOT_READY;
+    if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+    if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR;
+
+    /* Determine CHS in the table regardless of the drive geometry */
+    for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ;
+    if (n == 256) n--;
+    e_hd = n - 1;
+    sz_cyl = 63 * n;
+    tot_cyl = sz_disk / sz_cyl;
+
+    /* Create partition table */
+    mem_set(buf, 0, _MAX_SS);
+    p = buf + MBR_Table; b_cyl = 0;
+    for (i = 0; i < 4; i++, p += SZ_PTE) {
+        p_cyl = (szt[i] <= 100) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl;
+        if (!p_cyl) continue;
+        s_part = (DWORD)sz_cyl * b_cyl;
+        sz_part = (DWORD)sz_cyl * p_cyl;
+        if (i == 0) {   /* Exclude first track of cylinder 0 */
+            s_hd = 1;
+            s_part += 63; sz_part -= 63;
+        } else {
+            s_hd = 0;
+        }
+        e_cyl = b_cyl + p_cyl - 1;
+        if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER;
+
+        /* Set partition table */
+        p[1] = s_hd;                        /* Start head */
+        p[2] = (BYTE)((b_cyl >> 2) + 1);    /* Start sector */
+        p[3] = (BYTE)b_cyl;                 /* Start cylinder */
+        p[4] = 0x06;                        /* System type (temporary setting) */
+        p[5] = e_hd;                        /* End head */
+        p[6] = (BYTE)((e_cyl >> 2) + 63);   /* End sector */
+        p[7] = (BYTE)e_cyl;                 /* End cylinder */
+        ST_DWORD(p + 8, s_part);            /* Start sector in LBA */
+        ST_DWORD(p + 12, sz_part);          /* Partition size */
+
+        /* Next partition */
+        b_cyl += p_cyl;
+    }
+    ST_WORD(p, 0xAA55);
+
+    /* Write it to the MBR */
+    return (disk_write(pdrv, buf, 0, 1) || disk_ioctl(pdrv, CTRL_SYNC, 0)) ? FR_DISK_ERR : FR_OK;
+}
+
+
+#endif /* _MULTI_PARTITION == 2 */
+#endif /* _USE_MKFS && !_FS_READONLY */
+
+
+
+
+#if _USE_STRFUNC
+/*-----------------------------------------------------------------------*/
+/* Get a string from the file                                            */
+/*-----------------------------------------------------------------------*/
+TCHAR* f_gets (
+    TCHAR* buff,    /* Pointer to the string buffer to read */
+    int len,        /* Size of string buffer (characters) */
+    FIL* fil        /* Pointer to the file object */
+)
+{
+    int n = 0;
+    TCHAR c, *p = buff;
+    BYTE s[2];
+    UINT rc;
+
+
+    while (n < len - 1) {           /* Read bytes until buffer gets filled */
+        f_read(fil, s, 1, &rc);
+        if (rc != 1) break;         /* Break on EOF or error */
+        c = s[0];
+#if _LFN_UNICODE                    /* Read a character in UTF-8 encoding */
+        if (c >= 0x80) {
+            if (c < 0xC0) continue; /* Skip stray trailer */
+            if (c < 0xE0) {         /* Two-byte sequence */
+                f_read(fil, s, 1, &rc);
+                if (rc != 1) break;
+                c = ((c & 0x1F) << 6) | (s[0] & 0x3F);
+                if (c < 0x80) c = '?';
+            } else {
+                if (c < 0xF0) {     /* Three-byte sequence */
+                    f_read(fil, s, 2, &rc);
+                    if (rc != 2) break;
+                    c = (c << 12) | ((s[0] & 0x3F) << 6) | (s[1] & 0x3F);
+                    if (c < 0x800) c = '?';
+                } else {            /* Reject four-byte sequence */
+                    c = '?';
+                }
+            }
+        }
+#endif
+#if _USE_STRFUNC >= 2
+        if (c == '\r') continue;    /* Strip '\r' */
+#endif
+        *p++ = c;
+        n++;
+        if (c == '\n') break;       /* Break on EOL */
+    }
+    *p = 0;
+    return n ? buff : 0;            /* When no data read (eof or error), return with error. */
+}
+
+
+
+#if !_FS_READONLY
+#include <stdarg.h>
+/*-----------------------------------------------------------------------*/
+/* Put a character to the file                                           */
+/*-----------------------------------------------------------------------*/
+int f_putc (
+    TCHAR c,    /* A character to be output */
+    FIL* fil    /* Pointer to the file object */
+)
+{
+    UINT bw, btw;
+    BYTE s[3];
+
+
+#if _USE_STRFUNC >= 2
+    if (c == '\n') f_putc ('\r', fil);  /* LF -> CRLF conversion */
+#endif
+
+#if _LFN_UNICODE    /* Write the character in UTF-8 encoding */
+    if (c < 0x80) {         /* 7-bit */
+        s[0] = (BYTE)c;
+        btw = 1;
+    } else {
+        if (c < 0x800) {    /* 11-bit */
+            s[0] = (BYTE)(0xC0 | (c >> 6));
+            s[1] = (BYTE)(0x80 | (c & 0x3F));
+            btw = 2;
+        } else {            /* 16-bit */
+            s[0] = (BYTE)(0xE0 | (c >> 12));
+            s[1] = (BYTE)(0x80 | ((c >> 6) & 0x3F));
+            s[2] = (BYTE)(0x80 | (c & 0x3F));
+            btw = 3;
+        }
+    }
+#else               /* Write the character without conversion */
+    s[0] = (BYTE)c;
+    btw = 1;
+#endif
+    f_write(fil, s, btw, &bw);      /* Write the char to the file */
+    return (bw == btw) ? 1 : EOF;   /* Return the result */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a string to the file                                              */
+/*-----------------------------------------------------------------------*/
+int f_puts (
+    const TCHAR* str,   /* Pointer to the string to be output */
+    FIL* fil            /* Pointer to the file object */
+)
+{
+    int n;
+
+
+    for (n = 0; *str; str++, n++) {
+        if (f_putc(*str, fil) == EOF) return EOF;
+    }
+    return n;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a formatted string to the file                                    */
+/*-----------------------------------------------------------------------*/
+int f_printf (
+    FIL* fil,           /* Pointer to the file object */
+    const TCHAR* str,   /* Pointer to the format string */
+    ...                 /* Optional arguments... */
+)
+{
+    va_list arp;
+    BYTE f, r;
+    UINT i, j, w;
+    ULONG v;
+    TCHAR c, d, s[16], *p;
+    int res, chc, cc;
+
+
+    va_start(arp, str);
+
+    for (cc = res = 0; cc != EOF; res += cc) {
+        c = *str++;
+        if (c == 0) break;          /* End of string */
+        if (c != '%') {             /* Non escape character */
+            cc = f_putc(c, fil);
+            if (cc != EOF) cc = 1;
+            continue;
+        }
+        w = f = 0;
+        c = *str++;
+        if (c == '0') {             /* Flag: '0' padding */
+            f = 1; c = *str++;
+        } else {
+            if (c == '-') {         /* Flag: left justified */
+                f = 2; c = *str++;
+            }
+        }
+        while (IsDigit(c)) {        /* Precision */
+            w = w * 10 + c - '0';
+            c = *str++;
+        }
+        if (c == 'l' || c == 'L') { /* Prefix: Size is long int */
+            f |= 4; c = *str++;
+        }
+        if (!c) break;
+        d = c;
+        if (IsLower(d)) d -= 0x20;
+        switch (d) {                /* Type is... */
+        case 'S' :                  /* String */
+            p = va_arg(arp, TCHAR*);
+            for (j = 0; p[j]; j++) ;
+            chc = 0;
+            if (!(f & 2)) {
+                while (j++ < w) chc += (cc = f_putc(' ', fil));
+            }
+            chc += (cc = f_puts(p, fil));
+            while (j++ < w) chc += (cc = f_putc(' ', fil));
+            if (cc != EOF) cc = chc;
+            continue;
+        case 'C' :                  /* Character */
+            cc = f_putc((TCHAR)va_arg(arp, int), fil); continue;
+        case 'B' :                  /* Binary */
+            r = 2; break;
+        case 'O' :                  /* Octal */
+            r = 8; break;
+        case 'D' :                  /* Signed decimal */
+        case 'U' :                  /* Unsigned decimal */
+            r = 10; break;
+        case 'X' :                  /* Hexdecimal */
+            r = 16; break;
+        default:                    /* Unknown type (pass-through) */
+            cc = f_putc(c, fil); continue;
+        }
+
+        /* Get an argument and put it in numeral */
+        v = (f & 4) ? (ULONG)va_arg(arp, long) : ((d == 'D') ? (ULONG)(long)va_arg(arp, int) : (ULONG)va_arg(arp, unsigned int));
+        if (d == 'D' && (v & 0x80000000)) {
+            v = 0 - v;
+            f |= 8;
+        }
+        i = 0;
+        do {
+            d = (TCHAR)(v % r); v /= r;
+            if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
+            s[i++] = d + '0';
+        } while (v && i < sizeof s / sizeof s[0]);
+        if (f & 8) s[i++] = '-';
+        j = i; d = (f & 1) ? '0' : ' ';
+        res = 0;
+        while (!(f & 2) && j++ < w) res += (cc = f_putc(d, fil));
+        do res += (cc = f_putc(s[--i], fil)); while(i);
+        while (j++ < w) res += (cc = f_putc(' ', fil));
+        if (cc != EOF) cc = res;
+    }
+
+    va_end(arp);
+    return (cc == EOF) ? cc : res;
+}
+
+#endif /* !_FS_READONLY */
+#endif /* _USE_STRFUNC */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/ff.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,337 @@
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module include file  R0.09a    (C)ChaN, 2012
+/----------------------------------------------------------------------------/
+/ FatFs module is a generic FAT file system module for small embedded systems.
+/ This is a free software that opened for education, research and commercial
+/ developments under license policy of following terms.
+/
+/  Copyright (C) 2012, ChaN, all right reserved.
+/
+/ * The FatFs module is a free software and there is NO WARRANTY.
+/ * No restriction on use. You can use, modify and redistribute it for
+/   personal, non-profit or commercial product UNDER YOUR RESPONSIBILITY.
+/ * Redistributions of source code must retain the above copyright notice.
+/
+/----------------------------------------------------------------------------*/
+
+#ifndef _FATFS
+#define _FATFS  4004    /* Revision ID */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "integer.h"    /* Basic integer types */
+#include "ffconf.h"     /* FatFs configuration options */
+
+#if _FATFS != _FFCONF
+#error Wrong configuration file (ffconf.h).
+#endif
+
+
+
+/* Definitions of volume management */
+
+#if _MULTI_PARTITION        /* Multiple partition configuration */
+typedef struct {
+    BYTE pd;    /* Physical drive number */
+    BYTE pt;    /* Partition: 0:Auto detect, 1-4:Forced partition) */
+} PARTITION;
+extern PARTITION VolToPart[];   /* Volume - Partition resolution table */
+#define LD2PD(vol) (VolToPart[vol].pd)  /* Get physical drive number */
+#define LD2PT(vol) (VolToPart[vol].pt)  /* Get partition index */
+
+#else                           /* Single partition configuration */
+#define LD2PD(vol) (BYTE)(vol)  /* Each logical drive is bound to the same physical drive number */
+#define LD2PT(vol) 0            /* Always mounts the 1st partition or in SFD */
+
+#endif
+
+
+
+/* Type of path name strings on FatFs API */
+
+#if _LFN_UNICODE            /* Unicode string */
+#if !_USE_LFN
+#error _LFN_UNICODE must be 0 in non-LFN cfg.
+#endif
+#ifndef _INC_TCHAR
+typedef WCHAR TCHAR;
+#define _T(x) L ## x
+#define _TEXT(x) L ## x
+#endif
+
+#else                       /* ANSI/OEM string */
+#ifndef _INC_TCHAR
+typedef char TCHAR;
+#define _T(x) x
+#define _TEXT(x) x
+#endif
+
+#endif
+
+
+
+/* File system object structure (FATFS) */
+
+typedef struct {
+    BYTE    fs_type;        /* FAT sub-type (0:Not mounted) */
+    BYTE    drv;            /* Physical drive number */
+    BYTE    csize;          /* Sectors per cluster (1,2,4...128) */
+    BYTE    n_fats;         /* Number of FAT copies (1,2) */
+    BYTE    wflag;          /* win[] dirty flag (1:must be written back) */
+    BYTE    fsi_flag;       /* fsinfo dirty flag (1:must be written back) */
+    WORD    id;             /* File system mount ID */
+    WORD    n_rootdir;      /* Number of root directory entries (FAT12/16) */
+#if _MAX_SS != 512
+    WORD    ssize;          /* Bytes per sector (512, 1024, 2048 or 4096) */
+#endif
+#if _FS_REENTRANT
+    _SYNC_t sobj;           /* Identifier of sync object */
+#endif
+#if !_FS_READONLY
+    DWORD   last_clust;     /* Last allocated cluster */
+    DWORD   free_clust;     /* Number of free clusters */
+    DWORD   fsi_sector;     /* fsinfo sector (FAT32) */
+#endif
+#if _FS_RPATH
+    DWORD   cdir;           /* Current directory start cluster (0:root) */
+#endif
+    DWORD   n_fatent;       /* Number of FAT entries (= number of clusters + 2) */
+    DWORD   fsize;          /* Sectors per FAT */
+    DWORD   fatbase;        /* FAT start sector */
+    DWORD   dirbase;        /* Root directory start sector (FAT32:Cluster#) */
+    DWORD   database;       /* Data start sector */
+    DWORD   winsect;        /* Current sector appearing in the win[] */
+    BYTE    win[_MAX_SS];   /* Disk access window for Directory, FAT (and Data on tiny cfg) */
+} FATFS;
+
+
+
+/* File object structure (FIL) */
+
+typedef struct {
+    FATFS*  fs;             /* Pointer to the related file system object */
+    WORD    id;             /* File system mount ID of the related file system object */
+    BYTE    flag;           /* File status flags */
+    BYTE    pad1;
+    DWORD   fptr;           /* File read/write pointer (0ed on file open) */
+    DWORD   fsize;          /* File size */
+    DWORD   sclust;         /* File data start cluster (0:no data cluster, always 0 when fsize is 0) */
+    DWORD   clust;          /* Current cluster of fpter */
+    DWORD   dsect;          /* Current data sector of fpter */
+#if !_FS_READONLY
+    DWORD   dir_sect;       /* Sector containing the directory entry */
+    BYTE*   dir_ptr;        /* Pointer to the directory entry in the window */
+#endif
+#if _USE_FASTSEEK
+    DWORD*  cltbl;          /* Pointer to the cluster link map table (null on file open) */
+#endif
+#if _FS_LOCK
+    UINT    lockid;         /* File lock ID (index of file semaphore table Files[]) */
+#endif
+#if !_FS_TINY
+    BYTE    buf[_MAX_SS];   /* File data read/write buffer */
+#endif
+} FIL;
+
+
+
+/* Directory object structure (DIR) */
+
+typedef struct {
+    FATFS*  fs;             /* Pointer to the owner file system object */
+    WORD    id;             /* Owner file system mount ID */
+    WORD    index;          /* Current read/write index number */
+    DWORD   sclust;         /* Table start cluster (0:Root dir) */
+    DWORD   clust;          /* Current cluster */
+    DWORD   sect;           /* Current sector */
+    BYTE*   dir;            /* Pointer to the current SFN entry in the win[] */
+    BYTE*   fn;             /* Pointer to the SFN (in/out) {file[8],ext[3],status[1]} */
+#if _USE_LFN
+    WCHAR*  lfn;            /* Pointer to the LFN working buffer */
+    WORD    lfn_idx;        /* Last matched LFN index number (0xFFFF:No LFN) */
+#endif
+} FATFS_DIR;
+
+
+
+/* File status structure (FILINFO) */
+
+typedef struct {
+    DWORD   fsize;          /* File size */
+    WORD    fdate;          /* Last modified date */
+    WORD    ftime;          /* Last modified time */
+    BYTE    fattrib;        /* Attribute */
+    TCHAR   fname[13];      /* Short file name (8.3 format) */
+#if _USE_LFN
+    TCHAR*  lfname;         /* Pointer to the LFN buffer */
+    UINT    lfsize;         /* Size of LFN buffer in TCHAR */
+#endif
+} FILINFO;
+
+
+
+/* File function return code (FRESULT) */
+
+typedef enum {
+    FR_OK = 0,              /* (0) Succeeded */
+    FR_DISK_ERR,            /* (1) A hard error occurred in the low level disk I/O layer */
+    FR_INT_ERR,             /* (2) Assertion failed */
+    FR_NOT_READY,           /* (3) The physical drive cannot work */
+    FR_NO_FILE,             /* (4) Could not find the file */
+    FR_NO_PATH,             /* (5) Could not find the path */
+    FR_INVALID_NAME,        /* (6) The path name format is invalid */
+    FR_DENIED,              /* (7) Access denied due to prohibited access or directory full */
+    FR_EXIST,               /* (8) Access denied due to prohibited access */
+    FR_INVALID_OBJECT,      /* (9) The file/directory object is invalid */
+    FR_WRITE_PROTECTED,     /* (10) The physical drive is write protected */
+    FR_INVALID_DRIVE,       /* (11) The logical drive number is invalid */
+    FR_NOT_ENABLED,         /* (12) The volume has no work area */
+    FR_NO_FILESYSTEM,       /* (13) There is no valid FAT volume */
+    FR_MKFS_ABORTED,        /* (14) The f_mkfs() aborted due to any parameter error */
+    FR_TIMEOUT,             /* (15) Could not get a grant to access the volume within defined period */
+    FR_LOCKED,              /* (16) The operation is rejected according to the file sharing policy */
+    FR_NOT_ENOUGH_CORE,     /* (17) LFN working buffer could not be allocated */
+    FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_SHARE */
+    FR_INVALID_PARAMETER    /* (19) Given parameter is invalid */
+} FRESULT;
+
+
+
+/*--------------------------------------------------------------*/
+/* FatFs module application interface                           */
+
+FRESULT f_mount (BYTE, FATFS*);                     /* Mount/Unmount a logical drive */
+FRESULT f_open (FIL*, const TCHAR*, BYTE);          /* Open or create a file */
+FRESULT f_read (FIL*, void*, UINT, UINT*);          /* Read data from a file */
+FRESULT f_lseek (FIL*, DWORD);                      /* Move file pointer of a file object */
+FRESULT f_close (FIL*);                             /* Close an open file object */
+FRESULT f_opendir (FATFS_DIR*, const TCHAR*);             /* Open an existing directory */
+FRESULT f_readdir (FATFS_DIR*, FILINFO*);                 /* Read a directory item */
+FRESULT f_stat (const TCHAR*, FILINFO*);            /* Get file status */
+FRESULT f_write (FIL*, const void*, UINT, UINT*);   /* Write data to a file */
+FRESULT f_getfree (const TCHAR*, DWORD*, FATFS**);  /* Get number of free clusters on the drive */
+FRESULT f_truncate (FIL*);                          /* Truncate file */
+FRESULT f_sync (FIL*);                              /* Flush cached data of a writing file */
+FRESULT f_unlink (const TCHAR*);                    /* Delete an existing file or directory */
+FRESULT f_mkdir (const TCHAR*);                     /* Create a new directory */
+FRESULT f_chmod (const TCHAR*, BYTE, BYTE);         /* Change attribute of the file/dir */
+FRESULT f_utime (const TCHAR*, const FILINFO*);     /* Change times-tamp of the file/dir */
+FRESULT f_rename (const TCHAR*, const TCHAR*);      /* Rename/Move a file or directory */
+FRESULT f_chdrive (BYTE);                           /* Change current drive */
+FRESULT f_chdir (const TCHAR*);                     /* Change current directory */
+FRESULT f_getcwd (TCHAR*, UINT);                    /* Get current directory */
+FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*);   /* Forward data to the stream */
+FRESULT f_mkfs (BYTE, BYTE, UINT);                  /* Create a file system on the drive */
+FRESULT f_fdisk (BYTE, const DWORD[], void*);       /* Divide a physical drive into some partitions */
+int f_putc (TCHAR, FIL*);                           /* Put a character to the file */
+int f_puts (const TCHAR*, FIL*);                    /* Put a string to the file */
+int f_printf (FIL*, const TCHAR*, ...);             /* Put a formatted string to the file */
+TCHAR* f_gets (TCHAR*, int, FIL*);                  /* Get a string from the file */
+
+#define f_eof(fp) (((fp)->fptr == (fp)->fsize) ? 1 : 0)
+#define f_error(fp) (((fp)->flag & FA__ERROR) ? 1 : 0)
+#define f_tell(fp) ((fp)->fptr)
+#define f_size(fp) ((fp)->fsize)
+
+#ifndef EOF
+#define EOF (-1)
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Additional user defined functions                            */
+
+/* RTC function */
+#if !_FS_READONLY
+DWORD get_fattime (void);
+#endif
+
+/* Unicode support functions */
+#if _USE_LFN                        /* Unicode - OEM code conversion */
+WCHAR ff_convert (WCHAR, UINT);     /* OEM-Unicode bidirectional conversion */
+WCHAR ff_wtoupper (WCHAR);          /* Unicode upper-case conversion */
+#if _USE_LFN == 3                   /* Memory functions */
+void* ff_memalloc (UINT);           /* Allocate memory block */
+void ff_memfree (void*);            /* Free memory block */
+#endif
+#endif
+
+/* Sync functions */
+#if _FS_REENTRANT
+int ff_cre_syncobj (BYTE, _SYNC_t*);/* Create a sync object */
+int ff_req_grant (_SYNC_t);         /* Lock sync object */
+void ff_rel_grant (_SYNC_t);        /* Unlock sync object */
+int ff_del_syncobj (_SYNC_t);       /* Delete a sync object */
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Flags and offset address                                     */
+
+
+/* File access control and file status flags (FIL.flag) */
+
+#define FA_READ             0x01
+#define FA_OPEN_EXISTING    0x00
+#define FA__ERROR           0x80
+
+#if !_FS_READONLY
+#define FA_WRITE            0x02
+#define FA_CREATE_NEW       0x04
+#define FA_CREATE_ALWAYS    0x08
+#define FA_OPEN_ALWAYS      0x10
+#define FA__WRITTEN         0x20
+#define FA__DIRTY           0x40
+#endif
+
+
+/* FAT sub type (FATFS.fs_type) */
+
+#define FS_FAT12    1
+#define FS_FAT16    2
+#define FS_FAT32    3
+
+
+/* File attribute bits for directory entry */
+
+#define AM_RDO  0x01    /* Read only */
+#define AM_HID  0x02    /* Hidden */
+#define AM_SYS  0x04    /* System */
+#define AM_VOL  0x08    /* Volume label */
+#define AM_LFN  0x0F    /* LFN entry */
+#define AM_DIR  0x10    /* Directory */
+#define AM_ARC  0x20    /* Archive */
+#define AM_MASK 0x3F    /* Mask of defined bits */
+
+
+/* Fast seek feature */
+#define CREATE_LINKMAP  0xFFFFFFFF
+
+
+
+/*--------------------------------*/
+/* Multi-byte word access macros  */
+
+#if _WORD_ACCESS == 1   /* Enable word access to the FAT structure */
+#define LD_WORD(ptr)        (WORD)(*(WORD*)(BYTE*)(ptr))
+#define LD_DWORD(ptr)       (DWORD)(*(DWORD*)(BYTE*)(ptr))
+#define ST_WORD(ptr,val)    *(WORD*)(BYTE*)(ptr)=(WORD)(val)
+#define ST_DWORD(ptr,val)   *(DWORD*)(BYTE*)(ptr)=(DWORD)(val)
+#else                   /* Use byte-by-byte access to the FAT structure */
+#define LD_WORD(ptr)        (WORD)(((WORD)*((BYTE*)(ptr)+1)<<8)|(WORD)*(BYTE*)(ptr))
+#define LD_DWORD(ptr)       (DWORD)(((DWORD)*((BYTE*)(ptr)+3)<<24)|((DWORD)*((BYTE*)(ptr)+2)<<16)|((WORD)*((BYTE*)(ptr)+1)<<8)|*(BYTE*)(ptr))
+#define ST_WORD(ptr,val)    *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8)
+#define ST_DWORD(ptr,val)   *(BYTE*)(ptr)=(BYTE)(val); *((BYTE*)(ptr)+1)=(BYTE)((WORD)(val)>>8); *((BYTE*)(ptr)+2)=(BYTE)((DWORD)(val)>>16); *((BYTE*)(ptr)+3)=(BYTE)((DWORD)(val)>>24)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FATFS */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/ffconf.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,191 @@
+/*---------------------------------------------------------------------------/
+/  FatFs - FAT file system module configuration file  R0.09a (C)ChaN, 2012
+/----------------------------------------------------------------------------/
+/
+/ CAUTION! Do not forget to make clean the project after any changes to
+/ the configuration options.
+/
+/----------------------------------------------------------------------------*/
+#ifndef _FFCONF
+#define _FFCONF 4004    /* Revision ID */
+
+#define FFS_DBG     0
+
+/*---------------------------------------------------------------------------/
+/ Functions and Buffer Configurations
+/----------------------------------------------------------------------------*/
+
+#define _FS_TINY        0   /* 0:Normal or 1:Tiny */
+/* When _FS_TINY is set to 1, FatFs uses the sector buffer in the file system
+/  object instead of the sector buffer in the individual file object for file
+/  data transfer. This reduces memory consumption 512 bytes each file object. */
+
+
+#define _FS_READONLY    0   /* 0:Read/Write or 1:Read only */
+/* Setting _FS_READONLY to 1 defines read only configuration. This removes
+/  writing functions, f_write, f_sync, f_unlink, f_mkdir, f_chmod, f_rename,
+/  f_truncate and useless f_getfree. */
+
+
+#define _FS_MINIMIZE    0   /* 0 to 3 */
+/* The _FS_MINIMIZE option defines minimization level to remove some functions.
+/
+/   0: Full function.
+/   1: f_stat, f_getfree, f_unlink, f_mkdir, f_chmod, f_truncate and f_rename
+/      are removed.
+/   2: f_opendir and f_readdir are removed in addition to 1.
+/   3: f_lseek is removed in addition to 2. */
+
+
+#define _USE_STRFUNC    0   /* 0:Disable or 1-2:Enable */
+/* To enable string functions, set _USE_STRFUNC to 1 or 2. */
+
+
+#define _USE_MKFS       1   /* 0:Disable or 1:Enable */
+/* To enable f_mkfs function, set _USE_MKFS to 1 and set _FS_READONLY to 0 */
+
+
+#define _USE_FORWARD    0   /* 0:Disable or 1:Enable */
+/* To enable f_forward function, set _USE_FORWARD to 1 and set _FS_TINY to 1. */
+
+
+#define _USE_FASTSEEK   0   /* 0:Disable or 1:Enable */
+/* To enable fast seek feature, set _USE_FASTSEEK to 1. */
+
+
+
+/*---------------------------------------------------------------------------/
+/ Locale and Namespace Configurations
+/----------------------------------------------------------------------------*/
+
+#define _CODE_PAGE  858
+/* The _CODE_PAGE specifies the OEM code page to be used on the target system.
+/  Incorrect setting of the code page can cause a file open failure.
+/
+/   932  - Japanese Shift-JIS (DBCS, OEM, Windows)
+/   936  - Simplified Chinese GBK (DBCS, OEM, Windows)
+/   949  - Korean (DBCS, OEM, Windows)
+/   950  - Traditional Chinese Big5 (DBCS, OEM, Windows)
+/   1250 - Central Europe (Windows)
+/   1251 - Cyrillic (Windows)
+/   1252 - Latin 1 (Windows)
+/   1253 - Greek (Windows)
+/   1254 - Turkish (Windows)
+/   1255 - Hebrew (Windows)
+/   1256 - Arabic (Windows)
+/   1257 - Baltic (Windows)
+/   1258 - Vietnam (OEM, Windows)
+/   437  - U.S. (OEM)
+/   720  - Arabic (OEM)
+/   737  - Greek (OEM)
+/   775  - Baltic (OEM)
+/   850  - Multilingual Latin 1 (OEM)
+/   858  - Multilingual Latin 1 + Euro (OEM)
+/   852  - Latin 2 (OEM)
+/   855  - Cyrillic (OEM)
+/   866  - Russian (OEM)
+/   857  - Turkish (OEM)
+/   862  - Hebrew (OEM)
+/   874  - Thai (OEM, Windows)
+/   1    - ASCII only (Valid for non LFN cfg.)
+*/
+
+
+#define _USE_LFN    1       /* 0 to 3 */
+#define _MAX_LFN    255     /* Maximum LFN length to handle (12 to 255) */
+/* The _USE_LFN option switches the LFN support.
+/
+/   0: Disable LFN feature. _MAX_LFN and _LFN_UNICODE have no effect.
+/   1: Enable LFN with static working buffer on the BSS. Always NOT reentrant.
+/   2: Enable LFN with dynamic working buffer on the STACK.
+/   3: Enable LFN with dynamic working buffer on the HEAP.
+/
+/  The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. To enable LFN,
+/  Unicode handling functions ff_convert() and ff_wtoupper() must be added
+/  to the project. When enable to use heap, memory control functions
+/  ff_memalloc() and ff_memfree() must be added to the project. */
+
+
+#define _LFN_UNICODE    0   /* 0:ANSI/OEM or 1:Unicode */
+/* To switch the character code set on FatFs API to Unicode,
+/  enable LFN feature and set _LFN_UNICODE to 1. */
+
+
+#define _FS_RPATH       0   /* 0 to 2 */
+/* The _FS_RPATH option configures relative path feature.
+/
+/   0: Disable relative path feature and remove related functions.
+/   1: Enable relative path. f_chdrive() and f_chdir() are available.
+/   2: f_getcwd() is available in addition to 1.
+/
+/  Note that output of the f_readdir fnction is affected by this option. */
+
+
+
+/*---------------------------------------------------------------------------/
+/ Physical Drive Configurations
+/----------------------------------------------------------------------------*/
+
+#define _VOLUMES    1
+/* Number of volumes (logical drives) to be used. */
+
+
+#define _MAX_SS     512     /* 512, 1024, 2048 or 4096 */
+/* Maximum sector size to be handled.
+/  Always set 512 for memory card and hard disk but a larger value may be
+/  required for on-board flash memory, floppy disk and optical disk.
+/  When _MAX_SS is larger than 512, it configures FatFs to variable sector size
+/  and GET_SECTOR_SIZE command must be implememted to the disk_ioctl function. */
+
+
+#define _MULTI_PARTITION    0   /* 0:Single partition, 1/2:Enable multiple partition */
+/* When set to 0, each volume is bound to the same physical drive number and
+/ it can mount only first primaly partition. When it is set to 1, each volume
+/ is tied to the partitions listed in VolToPart[]. */
+
+
+#define _USE_ERASE  0   /* 0:Disable or 1:Enable */
+/* To enable sector erase feature, set _USE_ERASE to 1. CTRL_ERASE_SECTOR command
+/  should be added to the disk_ioctl functio. */
+
+
+
+/*---------------------------------------------------------------------------/
+/ System Configurations
+/----------------------------------------------------------------------------*/
+
+#define _WORD_ACCESS    0   /* 0 or 1 */
+/* Set 0 first and it is always compatible with all platforms. The _WORD_ACCESS
+/  option defines which access method is used to the word data on the FAT volume.
+/
+/   0: Byte-by-byte access.
+/   1: Word access. Do not choose this unless following condition is met.
+/
+/  When the byte order on the memory is big-endian or address miss-aligned word
+/  access results incorrect behavior, the _WORD_ACCESS must be set to 0.
+/  If it is not the case, the value can also be set to 1 to improve the
+/  performance and code size.
+*/
+
+
+/* A header file that defines sync object types on the O/S, such as
+/  windows.h, ucos_ii.h and semphr.h, must be included prior to ff.h. */
+
+#define _FS_REENTRANT   0       /* 0:Disable or 1:Enable */
+#define _FS_TIMEOUT     1000    /* Timeout period in unit of time ticks */
+#define _SYNC_t         HANDLE  /* O/S dependent type of sync object. e.g. HANDLE, OS_EVENT*, ID and etc.. */
+
+/* The _FS_REENTRANT option switches the reentrancy (thread safe) of the FatFs module.
+/
+/   0: Disable reentrancy. _SYNC_t and _FS_TIMEOUT have no effect.
+/   1: Enable reentrancy. Also user provided synchronization handlers,
+/      ff_req_grant, ff_rel_grant, ff_del_syncobj and ff_cre_syncobj
+/      function must be added to the project. */
+
+
+#define _FS_LOCK    0   /* 0:Disable or >=1:Enable */
+/* To enable file lock control feature, set _FS_LOCK to 1 or greater.
+   The value defines how many files can be opened simultaneously. */
+
+
+#endif /* _FFCONFIG */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/ChaN/integer.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,37 @@
+/*-------------------------------------------*/
+/* Integer type definitions for FatFs module */
+/*-------------------------------------------*/
+
+#ifndef _INTEGER
+#define _INTEGER
+
+#ifdef _WIN32    /* FatFs development platform */
+
+#include <windows.h>
+#include <tchar.h>
+
+#else            /* Embedded platform */
+
+/* These types must be 16-bit, 32-bit or larger integer */
+typedef int             INT;
+typedef unsigned int    UINT;
+
+/* These types must be 8-bit integer */
+typedef char            CHAR;
+typedef unsigned char   UCHAR;
+typedef unsigned char   BYTE;
+
+/* These types must be 16-bit integer */
+typedef short           SHORT;
+typedef unsigned short  USHORT;
+typedef unsigned short  WORD;
+typedef unsigned short  WCHAR;
+
+/* These types must be 32-bit integer */
+typedef long            LONG;
+typedef unsigned long   ULONG;
+typedef unsigned long   DWORD;
+
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/FATDirHandle.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,78 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include <string.h>
+#include "ff.h"
+#include "FATDirHandle.h"
+
+using namespace mbed;
+
+FATDirHandle::FATDirHandle(const FATFS_DIR &the_dir) {
+    dir = the_dir;
+}
+
+int FATDirHandle::closedir() {
+    delete this;
+    return 0;
+}
+
+struct dirent *FATDirHandle::readdir() {
+    FILINFO finfo;
+
+#if _USE_LFN
+    finfo.lfname = cur_entry.d_name;
+    finfo.lfsize = sizeof(cur_entry.d_name);
+#endif // _USE_LFN
+
+    FRESULT res = f_readdir(&dir, &finfo);
+
+#if _USE_LFN
+    if(res != 0 || finfo.fname[0]==0) {
+        return NULL;
+    } else {
+        if(cur_entry.d_name[0]==0) {
+            // No long filename so use short filename.
+            memcpy(cur_entry.d_name, finfo.fname, sizeof(finfo.fname));
+        }
+        return &cur_entry;
+    }
+#else
+    if(res != 0 || finfo.fname[0]==0) {
+        return NULL;
+    } else {
+        memcpy(cur_entry.d_name, finfo.fname, sizeof(finfo.fname));
+        return &cur_entry;
+    }
+#endif /* _USE_LFN */
+}
+
+void FATDirHandle::rewinddir() {
+    dir.index = 0;
+}
+
+off_t FATDirHandle::telldir() {
+    return dir.index;
+}
+
+void FATDirHandle::seekdir(off_t location) {
+    dir.index = location;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/FATDirHandle.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,45 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef MBED_FATDIRHANDLE_H
+#define MBED_FATDIRHANDLE_H
+
+#include "DirHandle.h"
+
+using namespace mbed;
+
+class FATDirHandle : public DirHandle {
+
+ public:
+    FATDirHandle(const FATFS_DIR &the_dir);
+    virtual int closedir();
+    virtual struct dirent *readdir();
+    virtual void rewinddir();
+    virtual off_t telldir();
+    virtual void seekdir(off_t location);
+
+ private:
+    FATFS_DIR dir;
+    struct dirent cur_entry;
+
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/FATFileHandle.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,90 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "ff.h"
+#include "ffconf.h"
+#include "mbed_debug.h"
+
+#include "FATFileHandle.h"
+
+FATFileHandle::FATFileHandle(FIL fh) {
+    _fh = fh;
+}
+
+int FATFileHandle::close() {
+    int retval = f_close(&_fh);
+    delete this;
+    return retval;
+}
+
+ssize_t FATFileHandle::write(const void* buffer, size_t length) {
+    UINT n;
+    FRESULT res = f_write(&_fh, buffer, length, &n);
+    if (res) { 
+        debug_if(FFS_DBG, "f_write() failed: %d", res);
+        return -1;
+    }
+    return n;
+}
+        
+ssize_t FATFileHandle::read(void* buffer, size_t length) {
+    debug_if(FFS_DBG, "read(%d)\n", length);
+    UINT n;
+    FRESULT res = f_read(&_fh, buffer, length, &n);
+    if (res) {
+        debug_if(FFS_DBG, "f_read() failed: %d\n", res);
+        return -1;
+    }
+    return n;
+}
+
+int FATFileHandle::isatty() {
+    return 0;
+}
+
+off_t FATFileHandle::lseek(off_t position, int whence) {
+    if (whence == SEEK_END) {
+        position += _fh.fsize;
+    } else if(whence==SEEK_CUR) {
+        position += _fh.fptr;
+    }
+    FRESULT res = f_lseek(&_fh, position);
+    if (res) {
+        debug_if(FFS_DBG, "lseek failed: %d\n", res);
+        return -1;
+    } else {
+        debug_if(FFS_DBG, "lseek OK, returning %i\n", _fh.fptr);
+        return _fh.fptr;
+    }
+}
+
+int FATFileHandle::fsync() {
+    FRESULT res = f_sync(&_fh);
+    if (res) {
+        debug_if(FFS_DBG, "f_sync() failed: %d\n", res);
+        return -1;
+    }
+    return 0;
+}
+
+off_t FATFileHandle::flen() {
+    return _fh.fsize;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/FATFileHandle.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,47 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef MBED_FATFILEHANDLE_H
+#define MBED_FATFILEHANDLE_H
+
+#include "FileHandle.h"
+
+using namespace mbed;
+
+class FATFileHandle : public FileHandle {
+public:
+
+    FATFileHandle(FIL fh);
+    virtual int close();
+    virtual ssize_t write(const void* buffer, size_t length);
+    virtual ssize_t read(void* buffer, size_t length);
+    virtual int isatty();
+    virtual off_t lseek(off_t position, int whence);
+    virtual int fsync();
+    virtual off_t flen();
+
+protected:
+
+    FIL _fh;
+
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/FATFileSystem.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,132 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#include "mbed.h"
+
+#include "ffconf.h"
+#include "mbed_debug.h"
+
+#include "FATFileSystem.h"
+#include "FATFileHandle.h"
+#include "FATDirHandle.h"
+
+DWORD get_fattime(void) {
+    time_t rawtime;
+    time(&rawtime);
+    struct tm *ptm = localtime(&rawtime);
+    return (DWORD)(ptm->tm_year - 80) << 25
+         | (DWORD)(ptm->tm_mon + 1  ) << 21
+         | (DWORD)(ptm->tm_mday     ) << 16
+         | (DWORD)(ptm->tm_hour     ) << 11
+         | (DWORD)(ptm->tm_min      ) << 5
+         | (DWORD)(ptm->tm_sec/2    );
+}
+
+FATFileSystem *FATFileSystem::_ffs[_VOLUMES] = {0};
+
+FATFileSystem::FATFileSystem(const char* n) : FileSystemLike(n) {
+    debug_if(FFS_DBG, "FATFileSystem(%s)\n", n);
+    for(int i=0; i<_VOLUMES; i++) {
+        if(_ffs[i] == 0) {
+            _ffs[i] = this;
+            _fsid = i;
+            debug_if(FFS_DBG, "Mounting [%s] on ffs drive [%d]\n", _name, _fsid);
+            f_mount(i, &_fs);
+            return;
+        }
+    }
+    error("Couldn't create %s in FATFileSystem::FATFileSystem\n", n);
+}
+
+FATFileSystem::~FATFileSystem() {
+    for (int i=0; i<_VOLUMES; i++) {
+        if (_ffs[i] == this) {
+            _ffs[i] = 0;
+            f_mount(i, NULL);
+        }
+    }
+}
+
+FileHandle *FATFileSystem::open(const char* name, int flags) {
+    debug_if(FFS_DBG, "open(%s) on filesystem [%s], drv [%d]\n", name, _name, _fsid);
+    char n[64];
+    sprintf(n, "%d:/%s", _fsid, name);
+    
+    /* POSIX flags -> FatFS open mode */
+    BYTE openmode;
+    if (flags & O_RDWR) {
+        openmode = FA_READ|FA_WRITE;
+    } else if(flags & O_WRONLY) {
+        openmode = FA_WRITE;
+    } else {
+        openmode = FA_READ;
+    }
+    if(flags & O_CREAT) {
+        if(flags & O_TRUNC) {
+            openmode |= FA_CREATE_ALWAYS;
+        } else {
+            openmode |= FA_OPEN_ALWAYS;
+        }
+    }
+    
+    FIL fh;
+    FRESULT res = f_open(&fh, n, openmode);
+    if (res) { 
+        debug_if(FFS_DBG, "f_open('w') failed: %d\n", res);
+        return NULL;
+    }
+    if (flags & O_APPEND) {
+        f_lseek(&fh, fh.fsize);
+    }
+    return new FATFileHandle(fh);
+}
+    
+int FATFileSystem::remove(const char *filename) {
+    FRESULT res = f_unlink(filename);
+    if (res) { 
+        debug_if(FFS_DBG, "f_unlink() failed: %d\n", res);
+        return -1;
+    }
+    return 0;
+}
+
+int FATFileSystem::format() {
+    FRESULT res = f_mkfs(_fsid, 0, 512); // Logical drive number, Partitioning rule, Allocation unit size (bytes per cluster)
+    if (res) {
+        debug_if(FFS_DBG, "f_mkfs() failed: %d\n", res);
+        return -1;
+    }
+    return 0;
+}
+
+DirHandle *FATFileSystem::opendir(const char *name) {
+    FATFS_DIR dir;
+    FRESULT res = f_opendir(&dir, name);
+    if (res != 0) {
+        return NULL;
+    }
+    return new FATDirHandle(dir);
+}
+
+int FATFileSystem::mkdir(const char *name, mode_t mode) {
+    FRESULT res = f_mkdir(name);
+    return res == 0 ? 0 : -1;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/FATFileSystem/FATFileSystem.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,57 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2012 ARM Limited
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#ifndef MBED_FATFILESYSTEM_H
+#define MBED_FATFILESYSTEM_H
+
+#include "FileSystemLike.h"
+#include "FileHandle.h"
+#include "ff.h"
+#include <stdint.h>
+
+using namespace mbed;
+
+class FATFileSystem : public FileSystemLike {
+public:
+
+    FATFileSystem(const char* n);
+    virtual ~FATFileSystem();
+
+    static FATFileSystem * _ffs[_VOLUMES];   // FATFileSystem objects, as parallel to FatFs drives array
+    FATFS _fs;                               // Work area (file system object) for logical drive
+    int _fsid;
+
+    virtual FileHandle *open(const char* name, int flags);
+    virtual int remove(const char *filename);
+    virtual int format();
+    virtual DirHandle *opendir(const char *name);
+    virtual int mkdir(const char *name, mode_t mode);
+
+    virtual int disk_initialize() { return 0; }
+    virtual int disk_status() { return 0; }
+    virtual int disk_read(uint8_t * buffer, uint64_t sector) = 0;
+    virtual int disk_write(const uint8_t * buffer, uint64_t sector) = 0;
+    virtual int disk_sync() { return 0; }
+    virtual uint64_t disk_sectors() = 0;
+
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/USBHostMSD.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,352 @@
+/* mbed USBHost 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.
+ */
+
+#include "USBHostMSD.h"
+
+#define CBW_SIGNATURE   0x43425355
+#define CSW_SIGNATURE   0x53425355
+
+#define DEVICE_TO_HOST  0x80
+#define HOST_TO_DEVICE  0x00
+
+#define GET_MAX_LUN             (0xFE)
+#define BO_MASS_STORAGE_RESET   (0xFF)
+
+USBHostMSD::USBHostMSD(const char * rootdir) : FATFileSystem(rootdir)
+{
+    host = USBHost::getHostInst();
+    init();
+}
+
+void USBHostMSD::init() {
+    dev_connected = false;
+    dev = NULL;
+    bulk_in = NULL;
+    bulk_out = NULL;
+    dev_connected = false;
+    blockSize = 0;
+    blockCount = 0;
+    msd_intf = -1;
+    msd_device_found = false;
+    disk_init = false;
+    dev_connected = false;
+    nb_ep = 0;
+}
+
+bool USBHostMSD::connected()
+{
+    return dev_connected;
+}
+
+bool USBHostMSD::connect()
+{
+    if (dev_connected) {
+        return true;
+    }
+
+    for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) {
+        if ((dev = host->getDevice(i)) != NULL) {
+            
+            //USB_DBG("Trying to connect MSD device dev=%p\n", dev);
+            
+            if(host->enumerate(dev, this))
+                break;
+
+            if (msd_device_found) {
+                bulk_in = dev->getEndpoint(msd_intf, BULK_ENDPOINT, IN);
+                bulk_out = dev->getEndpoint(msd_intf, BULK_ENDPOINT, OUT);
+                
+                if (!bulk_in || !bulk_out)
+                    continue;
+                
+                USB_INFO("New MSD device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, msd_intf);
+                dev->setName("MSD", msd_intf);
+                host->registerDriver(dev, msd_intf, this, &USBHostMSD::init);
+
+                dev_connected = true;
+                return true;
+            }
+        } //if()
+    } //for()
+    init();
+    return false;
+}
+
+/*virtual*/ void USBHostMSD::setVidPid(uint16_t vid, uint16_t pid)
+{
+    USB_DBG2("vid:%04x pid:%04x", vid, pid);
+    // we don't check VID/PID for MSD driver
+}
+
+/*virtual*/ bool USBHostMSD::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed
+{
+    USB_DBG2("intf: %d class: %02x %02x %02x", intf_nb, intf_class, intf_subclass, intf_protocol);
+
+    if ((msd_intf == -1) &&
+        (intf_class == MSD_CLASS) &&
+        (intf_subclass == 0x06) &&
+        (intf_protocol == 0x50)) {
+        msd_intf = intf_nb;
+        return true;
+    }
+    return false;
+}
+
+/*virtual*/ bool USBHostMSD::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used
+{
+    USB_DBG2("intf_nb=%d type=%d dir=%d", intf_nb, type, dir);
+    if (intf_nb == msd_intf) {
+        if (type == BULK_ENDPOINT) {
+            nb_ep++;
+            if (nb_ep == 2)
+                msd_device_found = true;
+            return true;
+        }
+    }
+    return false;
+}
+
+int USBHostMSD::testUnitReady() {
+    USB_DBG("Test unit ready");
+    return SCSITransfer(NULL, 6, DEVICE_TO_HOST, 0, 0);
+}
+
+
+int USBHostMSD::readCapacity() {
+    USB_DBG("Read capacity");
+    uint8_t cmd[10] = {0x25,0,0,0,0,0,0,0,0,0};
+    uint8_t result[8];
+    int status = SCSITransfer(cmd, 10, DEVICE_TO_HOST, result, 8);
+    if (status == 0) {
+        blockCount = (result[0] << 24) | (result[1] << 16) | (result[2] << 8) | result[3];
+        blockSize = (result[4] << 24) | (result[5] << 16) | (result[6] << 8) | result[7];
+        USB_INFO("MSD blockCount: %lld, blockSize: %d, Capacity: %lld\r\n", blockCount, blockSize, blockCount*blockSize);
+    }
+    return status;
+}
+
+
+int USBHostMSD::SCSIRequestSense() {
+    USB_DBG("Request sense");
+    uint8_t cmd[6] = {0x03,0,0,0,18,0};
+    uint8_t result[18];
+    int status = SCSITransfer(cmd, 6, DEVICE_TO_HOST, result, 18);
+    return status;
+}
+
+
+int USBHostMSD::inquiry(uint8_t lun, uint8_t page_code) {
+    USB_DBG("Inquiry");
+    uint8_t evpd = (page_code == 0) ? 0 : 1;
+    uint8_t cmd[6] = {0x12, (lun << 5) | evpd, page_code, 0, 36, 0};
+    uint8_t result[36];
+    int status = SCSITransfer(cmd, 6, DEVICE_TO_HOST, result, 36);
+    if (status == 0) {
+        char vid_pid[17];
+        memcpy(vid_pid, &result[8], 8);
+        vid_pid[8] = 0;
+        USB_INFO("MSD Vendor ID: %s", vid_pid);
+
+        memcpy(vid_pid, &result[16], 16);
+        vid_pid[16] = 0;
+        USB_INFO("MSD Product ID: %s", vid_pid);
+
+        memcpy(vid_pid, &result[32], 4);
+        vid_pid[4] = 0;
+        USB_INFO("MSD Product rev: %s", vid_pid);
+    }
+    return status;
+}
+
+int USBHostMSD::checkResult(uint8_t res, USBEndpoint * ep) {
+    // if ep stalled: send clear feature
+    if (res == USB_TYPE_STALL_ERROR) {
+        res = host->controlWrite(dev,
+                           USB_RECIPIENT_ENDPOINT | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_STANDARD,
+                           CLEAR_FEATURE,
+                           0, ep->getAddress(), NULL, 0);
+        // set state to IDLE if clear feature successful
+        if (res == USB_TYPE_OK) {
+            ep->setState(USB_TYPE_IDLE);
+        }
+    }
+
+    if (res != USB_TYPE_OK)
+        return -1;
+
+    return 0;
+}
+
+
+int USBHostMSD::SCSITransfer(uint8_t * cmd, uint8_t cmd_len, int flags, uint8_t * data, uint32_t transfer_len) {
+
+    int res = 0;
+
+    cbw.Signature = CBW_SIGNATURE;
+    cbw.Tag = 0;
+    cbw.DataLength = transfer_len;
+    cbw.Flags = flags;
+    cbw.LUN = 0;
+    cbw.CBLength = cmd_len;
+    memset(cbw.CB,0,sizeof(cbw.CB));
+    if (cmd) {
+        memcpy(cbw.CB,cmd,cmd_len);
+    }
+
+    // send the cbw
+    USB_DBG("Send CBW");
+    res = host->bulkWrite(dev, bulk_out,(uint8_t *)&cbw, 31);
+    if (checkResult(res, bulk_out))
+        return -1;
+
+    // data stage if needed
+    if (data) {
+        USB_DBG("data stage");
+        if (flags == HOST_TO_DEVICE) {
+            
+            res = host->bulkWrite(dev, bulk_out, data, transfer_len);
+            if (checkResult(res, bulk_out))
+                return -1;
+
+        } else if (flags == DEVICE_TO_HOST) {
+
+            res = host->bulkRead(dev, bulk_in, data, transfer_len);
+            if (checkResult(res, bulk_in))
+                return -1;
+        }
+    }
+
+    // status stage
+    csw.Signature = 0;
+    USB_DBG("Read CSW");
+    res = host->bulkRead(dev, bulk_in,(uint8_t *)&csw, 13);
+    if (checkResult(res, bulk_in))
+        return -1;
+
+    if (csw.Signature != CSW_SIGNATURE) {
+        return -1;
+    }
+    
+    USB_DBG("recv csw: status: %d", csw.Status);
+
+    // ModeSense?
+    if ((csw.Status == 1) && (cmd[0] != 0x03)) {
+        USB_DBG("request mode sense");
+        return SCSIRequestSense();
+    }
+    
+    // perform reset recovery
+    if ((csw.Status == 2) && (cmd[0] != 0x03)) {
+        
+        // send Bulk-Only Mass Storage Reset request
+        res = host->controlWrite(   dev,
+                              USB_RECIPIENT_INTERFACE | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS,
+                              BO_MASS_STORAGE_RESET,
+                              0, msd_intf, NULL, 0);
+        
+        // unstall both endpoints
+        res = host->controlWrite(   dev,
+                              USB_RECIPIENT_ENDPOINT | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_STANDARD,
+                              CLEAR_FEATURE,
+                              0, bulk_in->getAddress(), NULL, 0);
+        
+        res = host->controlWrite(   dev,
+                              USB_RECIPIENT_ENDPOINT | USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_STANDARD,
+                              CLEAR_FEATURE,
+                              0, bulk_out->getAddress(), NULL, 0);
+        
+    }
+
+    return csw.Status;
+}
+
+
+int USBHostMSD::dataTransfer(uint8_t * buf, uint32_t block, uint8_t nbBlock, int direction) {
+    uint8_t cmd[10];
+    memset(cmd,0,10);
+    cmd[0] = (direction == DEVICE_TO_HOST) ? 0x28 : 0x2A;
+
+    cmd[2] = (block >> 24) & 0xff;
+    cmd[3] = (block >> 16) & 0xff;
+    cmd[4] = (block >> 8) & 0xff;
+    cmd[5] =  block & 0xff;
+
+    cmd[7] = (nbBlock >> 8) & 0xff;
+    cmd[8] = nbBlock & 0xff;
+
+    return SCSITransfer(cmd, 10, direction, buf, blockSize*nbBlock);
+}
+
+int USBHostMSD::getMaxLun() {
+    uint8_t buf[1], res;
+    res = host->controlRead(    dev, USB_RECIPIENT_INTERFACE | USB_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS,
+                          0xfe, 0, msd_intf, buf, 1);
+    USB_DBG("max lun: %d", buf[0]);
+    return res;
+}
+
+int USBHostMSD::disk_initialize() {
+    USB_DBG("FILESYSTEM: init");
+    int i, timeout = 10;
+    
+    //getMaxLun();
+    
+    for (i = 0; i < timeout; i++) {
+        wait_ms(100);
+        if (!testUnitReady())
+            break;
+    }
+    
+    if (i == timeout) {
+        disk_init = false;
+        return -1;
+    }
+    
+    inquiry(0, 0);
+    disk_init = 1;
+    return readCapacity();
+}
+
+int USBHostMSD::disk_write(const uint8_t *buffer, uint64_t block_number) {
+    USB_DBG("FILESYSTEM: write block: %lld", block_number);
+    if (!disk_init) {
+        disk_initialize();
+    }
+    if (!disk_init)
+        return -1;
+    return dataTransfer((uint8_t *)buffer, block_number, 1, HOST_TO_DEVICE);
+}
+
+int USBHostMSD::disk_read(uint8_t * buffer, uint64_t block_number) {
+    USB_DBG("FILESYSTEM: read block %lld", block_number);
+    if (!disk_init) {
+        disk_initialize();
+    }
+    if (!disk_init)
+        return -1;
+    return dataTransfer((uint8_t *)buffer, block_number, 1, DEVICE_TO_HOST);
+}
+
+uint64_t USBHostMSD::disk_sectors() {
+    USB_DBG("FILESYSTEM: sectors");
+    if (!disk_init) {
+        disk_initialize();
+    }
+    if (!disk_init)
+        return 0;
+    return blockCount;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHost/USBHostMSD/USBHostMSD.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,131 @@
+/* mbed USBHost 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 USBHOSTMSD_H
+#define USBHOSTMSD_H
+
+#if _USB_DBG
+#define USB_DBG(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\r\n");} while(0);
+#else
+#define USB_DBG(...)  while(0);
+#endif
+
+#if 0
+#define USB_DBG2(...) do{fprintf(stderr,"[%s@%d] ",__PRETTY_FUNCTION__,__LINE__);fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\r\n");} while(0);
+#else
+#define USB_DBG2(...)  while(0);
+#endif
+
+#if 1
+#define USB_INFO(...) do{fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\r\n");}while(0);
+#else
+#define USB_INFO(...)  while(0);
+#endif
+
+#include "USBHostConf.h"
+#include "USBHost.h"
+#include "FATFileSystem.h"
+
+/** 
+ * A class to communicate a USB flash disk
+ */
+class USBHostMSD : public IUSBEnumerator, public FATFileSystem {
+public:
+    /**
+    * Constructor
+    *
+    * @param rootdir mount name
+    */
+    USBHostMSD(const char * rootdir);
+
+    /**
+    * Check if a MSD device is connected
+    *
+    * @return true if a MSD device is connected
+    */
+    bool connected();
+
+    /**
+     * Try to connect to a MSD device
+     *
+     * @return true if connection was successful
+     */
+    bool connect();
+
+protected:
+    //From IUSBEnumerator
+    virtual void setVidPid(uint16_t vid, uint16_t pid);
+    virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); //Must return true if the interface should be parsed
+    virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); //Must return true if the endpoint will be used
+
+    // From FATFileSystem
+    virtual int disk_initialize();
+    virtual int disk_status() {return 0;};
+    virtual int disk_read(uint8_t * buffer, uint64_t sector);
+    virtual int disk_write(const uint8_t * buffer, uint64_t sector);
+    virtual int disk_sync() {return 0;};
+    virtual uint64_t disk_sectors();
+
+private:
+    USBHost * host;
+    USBDeviceConnected * dev;
+    bool dev_connected;
+    USBEndpoint * bulk_in;
+    USBEndpoint * bulk_out;
+    uint8_t nb_ep;
+
+    // Bulk-only CBW
+    typedef __packed struct {
+        uint32_t Signature;
+        uint32_t Tag;
+        uint32_t DataLength;
+        uint8_t  Flags;
+        uint8_t  LUN;
+        uint8_t  CBLength;
+        uint8_t  CB[16];
+    } CBW;
+
+    // Bulk-only CSW
+    typedef __packed struct {
+        uint32_t Signature;
+        uint32_t Tag;
+        uint32_t DataResidue;
+        uint8_t  Status;
+    } CSW;
+
+    CBW cbw;
+    CSW csw;
+
+    int SCSITransfer(uint8_t * cmd, uint8_t cmd_len, int flags, uint8_t * data, uint32_t transfer_len);
+    int testUnitReady();
+    int readCapacity();
+    int inquiry(uint8_t lun, uint8_t page_code);
+    int SCSIRequestSense();
+    int dataTransfer(uint8_t * buf, uint32_t block, uint8_t nbBlock, int direction);
+    int checkResult(uint8_t res, USBEndpoint * ep);
+    int getMaxLun();
+
+    int blockSize;
+    uint64_t blockCount;
+
+    int msd_intf;
+    bool msd_device_found;
+    bool disk_init;
+    
+    void init();
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z_USBHostC270/BaseUvc.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,55 @@
+// BaseUvc.cpp
+#include "USBHostConf.h"
+#include "USBHost.h"
+#include "BaseUvc.h"
+
+void BaseUvc::poll()
+{
+    uint8_t buf[ep_iso_in->getSize()];
+    int result = host->IsochronousRead(ep_iso_in, buf, sizeof(buf));
+    if (result >= 0) {
+        uint16_t frame = 0;
+        onResult(frame, buf, ep_iso_in->getLengthTransferred());
+    }
+}
+
+USB_TYPE BaseUvc::Control(int req, int cs, int index, uint8_t* buf, int size)
+{
+    if (req == SET_CUR) {    
+        return host->controlWrite(dev,
+                    USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_INTERFACE, 
+                    req, cs<<8, index, buf, size);
+    }
+    return host->controlRead(dev,
+                USB_DEVICE_TO_HOST | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_INTERFACE, 
+                req, cs<<8, index, buf, size);
+}
+
+USB_TYPE BaseUvc::setInterfaceAlternate(uint8_t intf, uint8_t alt)
+{
+    return host->controlWrite(dev, USB_HOST_TO_DEVICE | USB_RECIPIENT_INTERFACE,
+                                   SET_INTERFACE, alt, intf, NULL, 0);
+}
+
+void BaseUvc::onResult(uint16_t frame, uint8_t* buf, int len)
+{
+  if(m_pCbItem && m_pCbMeth)
+    (m_pCbItem->*m_pCbMeth)(frame, buf, len);
+  else if(m_pCb)
+    m_pCb(frame, buf, len);
+}
+
+void BaseUvc::setOnResult( void (*pMethod)(uint16_t, uint8_t*, int) )
+{
+    m_pCb = pMethod;
+    m_pCbItem = NULL;
+    m_pCbMeth = NULL;
+}
+    
+void BaseUvc::clearOnResult()
+{
+    m_pCb = NULL;
+    m_pCbItem = NULL;
+    m_pCbMeth = NULL;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z_USBHostC270/BaseUvc.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,51 @@
+// BaseUvc.h
+//#include "USBIsochronous.h"
+#pragma once
+
+// --- UVC --------------------------------------------------
+#define _30FPS  333333
+#define _25FPS  400000
+#define _20FPS  500000
+#define _15FPS  666666
+#define _10FPS 1000000
+#define _5FPS  2000000
+#define _1FPS 10000000
+
+#define SET_CUR  0x01
+#define GET_CUR  0x81
+#define GET_MIN  0x82
+#define GET_MAX  0x83
+#define GET_RES  0x84
+#define GET_LEN  0x85
+#define GET_INFO 0x86
+#define GET_DEF  0x87
+
+#define VS_PROBE_CONTROL  0x01
+#define VS_COMMIT_CONTROL 0x02
+
+class BaseUvc {
+public:
+    void poll();
+    USB_TYPE Control(int req, int cs, int index, uint8_t* buf, int size);
+    USB_TYPE setInterfaceAlternate(uint8_t intf, uint8_t alt);
+    //IsochronousEp* m_isoEp;
+    // callback
+    void onResult(uint16_t frame, uint8_t* buf, int len);
+    void setOnResult( void (*pMethod)(uint16_t, uint8_t*, int) );
+    class CDummy;
+    template<class T> 
+    void setOnResult( T* pItem, void (T::*pMethod)(uint16_t, uint8_t*, int) )
+    {
+        m_pCb = NULL;
+        m_pCbItem = (CDummy*) pItem;
+        m_pCbMeth = (void (CDummy::*)(uint16_t, uint8_t*, int)) pMethod;
+    }
+    void clearOnResult();
+    CDummy* m_pCbItem;
+    void (CDummy::*m_pCbMeth)(uint16_t, uint8_t*, int);
+    void (*m_pCb)(uint16_t, uint8_t*, int);
+protected:
+    USBHost * host;
+    USBDeviceConnected * dev;
+    USBEndpoint* ep_iso_in;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z_USBHostC270/CamInfo.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,188 @@
+// CamInfo.cpp
+#include "USBHostCam.h"
+
+// Logitech C270
+#define C270_VID 0x046d
+#define C270_PID 0x0825
+#define C270_160x120 2
+#define C270_176x144 3
+#define C270_320x176 4
+#define C270_320x240 5
+#define C270_352x288 6
+#define C270_432x240 7
+#define C270_640x480 1
+#define C270_544x288 8
+#define C270_640x360 9
+#define C270_752x416 10
+#define C270_800x448 11
+#define C270_800x600 12
+
+#define C270_MJPEG 2
+#define C270_YUV2  1
+
+#define C270_EN  0x81
+#define C270_MPS  192
+#define C270_IF_ALT_192 1
+#define C270_IF_ALT(A) C270_IF_ALT_##A
+
+#define C270_INFO(SIZE) {C270_VID, C270_PID, _##SIZE, 0, \
+    "C270", \
+    C270_MJPEG, \
+    C270_##SIZE, \
+    _5FPS, \
+    C270_EN, \
+    192, \
+    C270_IF_ALT(192), \
+    }
+
+#define C210_PID 0x819
+#define C210_INFO(SIZE) {C270_VID, C210_PID, _##SIZE, 0, \
+    "C270", \
+    C270_MJPEG, \
+    C270_##SIZE, \
+    _5FPS, \
+    C270_EN, \
+    192, \
+    C270_IF_ALT(192), \
+    }
+
+// Logitech Qcam Orbit AF QCAM-200R
+#define Q200R_VID 0x046d
+#define Q200R_PID 0x0994
+#define Q200R_160x120 1
+#define Q200R_176x144 2
+#define Q200R_320x240 3
+#define Q200R_352x288 4
+#define Q200R_640x480 5
+#define Q200R_800x600 6
+
+#define Q200R_MJPEG 1
+#define Q200R_YUV2  2
+
+#define Q200R_EN  0x81
+#define Q200R_MPS  192
+#define Q200R_IF_ALT_192 1
+#define Q200R_IF_ALT_384 2
+#define Q200R_IF_ALT_512 3
+#define Q200R_IF_ALT_640 4
+#define Q200R_IF_ALT_800 5
+#define Q200R_IF_ALT_944 6
+#define Q200R_IF_ALT(A) Q200R_IF_ALT_##A
+#define Q200R_INFO(SIZE) {Q200R_VID, Q200R_PID, _##SIZE, 0, \
+    "Q200R", \
+    Q200R_MJPEG, \
+    Q200R_##SIZE, \
+    _5FPS, \
+    Q200R_EN, \
+    192, \
+    Q200R_IF_ALT(192), \
+    }
+
+//LifeCam VX700 / VX500
+#define VX700_VID 0x045e
+#define VX700_PID 0x074a
+
+#define VX700_160x120 5
+#define VX700_176x144 4
+#define VX700_320x240 3
+#define VX700_352x288 2
+#define VX700_640x480 1
+
+#define VX700_MJPEG 1
+
+#define VX700_EN  0x81
+#define VX700_MPS  128
+#define VX700_IF_ALT_128 1 
+#define VX700_IF_ALT(A) VX700_IF_ALT_##A
+#define VX700_INFO(SIZE) {VX700_VID, VX700_PID, _##SIZE, 0, \
+    "VX700", \
+    VX700_MJPEG, \
+    VX700_##SIZE, \
+    _5FPS, \
+    VX700_EN, \
+    128, \
+    VX700_IF_ALT(128), \
+    }
+
+//Sonix USB 2.0 Camera
+#define SONIX_160x120 5
+#define SONIX_176x144 4
+#define SONIX_320x240 3
+#define SONIX_352x288 2
+#define SONIX_640x480 1
+
+#define SONIX_IF_ALT_128 1
+#define SONIX_IF_ALT_256 2
+#define SONIX_IF_ALT_512 3
+#define SONIX_IF_ALT_600 4
+#define SONIX_IF_ALT_800 5
+#define SONIX_IF_ALT_956 6
+#define SONIX_IF_ALT(A) SONIX_IF_ALT_##A
+#define SONIX_INFO(SIZE) {0x0c45, 0x62c0, _##SIZE, 0, \
+    "SONIX", \
+    1, \
+    SONIX_##SIZE, \
+    _5FPS, \
+    0x81, \
+    128, \
+    SONIX_IF_ALT(128), \
+    }
+
+static const CamInfo CamInfoList[] = {
+// Logitech C270
+C270_INFO(160x120),
+C270_INFO(176x144),
+C270_INFO(320x176),
+C270_INFO(320x240),
+C270_INFO(352x288),
+C270_INFO(432x240),
+C270_INFO(640x480),
+C270_INFO(544x288),
+C270_INFO(640x360),
+C270_INFO(752x416),
+C270_INFO(800x448),
+C270_INFO(800x600),
+
+// Logitech C210
+C210_INFO(160x120),
+C210_INFO(176x144),
+C210_INFO(320x176),
+C210_INFO(320x240),
+C210_INFO(352x288),
+C210_INFO(432x240),
+C210_INFO(640x480),
+C210_INFO(544x288),
+C210_INFO(640x360),
+C210_INFO(752x416),
+C210_INFO(800x448),
+C210_INFO(800x600),
+
+// Logitech Qcam Orbit AF QCAM-200R
+Q200R_INFO(160x120),
+Q200R_INFO(176x144),
+Q200R_INFO(320x240),
+Q200R_INFO(352x288),
+Q200R_INFO(640x480),
+Q200R_INFO(800x600),
+
+// LifeCam VX700
+VX700_INFO(160x120),
+VX700_INFO(176x144),
+VX700_INFO(320x240),
+VX700_INFO(352x288),
+VX700_INFO(640x480),
+
+// Sonix USB 2.0 Camera
+SONIX_INFO(160x120),
+SONIX_INFO(176x144),
+SONIX_INFO(320x240),
+SONIX_INFO(352x288),
+SONIX_INFO(640x480),
+
+// Not found
+{0,0,0,0},
+};
+
+CamInfo* getCamInfoList() {
+    return const_cast<CamInfo*>(CamInfoList);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z_USBHostC270/USBHostCam.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,198 @@
+// USBHostCam.cpp
+#include "USBHostCam.h"
+
+#if 0
+#define CAM_DBG(x, ...) std::printf("[%s:%d]"x"\n", __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
+#else
+#define CAM_DBG(...)  while(0);
+#endif
+#define CAM_INFO(...) do{fprintf(stderr,__VA_ARGS__);}while(0);
+#define USB_INFO(...) do{fprintf(stderr,__VA_ARGS__);}while(0);
+
+CamInfo* getCamInfoList(); // CamInfo.cpp
+
+USBHostCam::USBHostCam(uint8_t size, uint8_t option, CamInfo* user_caminfo)
+{
+    CAM_DBG("size: %d, option: %d", size, option);
+    _caminfo_size = size;
+    _caminfo_option = option;
+    if (user_caminfo) {
+        CamInfoList = user_caminfo;
+    } else {
+        CamInfoList = getCamInfoList();
+    }
+    clearOnResult();
+    host = USBHost::getHostInst();
+    dev = host->getDevice(0);
+    ep_iso_in = new USBEndpoint;
+    init();
+}
+
+void USBHostCam::init()
+{
+    CAM_DBG("");
+    dev_connected = false;
+    dev = NULL;
+    cam_intf = -1;
+    device_found = false;
+    caminfo_found = false;
+}
+ 
+bool USBHostCam::connected()
+{
+    return dev_connected;
+}
+ 
+bool USBHostCam::connect()
+{
+    if (dev_connected) {
+        return true;
+    }
+ 
+    for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) {
+        if ((dev = host->getDevice(i)) != NULL) {
+            
+            CAM_DBG("Trying to connect Cam device\r\n");
+            
+            if(host->enumerate(dev, this)) {
+                break;
+            }
+            if (device_found) {
+                USB_INFO("New Cam: %s device: VID:%04x PID:%04x [dev: %p - intf: %d]\n", caminfo->name, dev->getVid(), dev->getPid(), dev, cam_intf);
+                dev->setName(caminfo->name, cam_intf);
+                //host->registerDriver(dev, cam_intf, this, &USBHostCam::onDisconnect);
+                int addr = dev->getAddress();
+                ep_iso_in->setDevice(dev);
+                ep_iso_in->setAddress(caminfo->en);
+                ep_iso_in->setSize(caminfo->mps);
+                //ep_iso_in->init(addr, caminfo->en, caminfo->mps, caminfo->frameCount, caminfo->queueLimit);
+                uint8_t buf[26];
+                memset(buf, 0, sizeof(buf));
+                buf[2] = caminfo->formatIndex;
+                buf[3] = caminfo->frameIndex;
+                *reinterpret_cast<uint32_t*>(buf+4) = caminfo->interval;
+                USB_TYPE res = Control(SET_CUR, VS_COMMIT_CONTROL, 1, buf, sizeof(buf));
+                if (res != USB_TYPE_OK) {
+                    CAM_DBG("SET_CUR VS_COMMIT_CONTROL FAILED");
+                }
+                res = setInterfaceAlternate(1, caminfo->if_alt);
+                if (res != USB_TYPE_OK) {
+                    CAM_DBG("SET_INTERFACE FAILED");
+                }
+                dev_connected = true;
+                return true;
+            }
+        }
+    }
+    init();
+    return false;
+}
+
+#if 0
+void USBHostCam::setup() {
+    caminfo = CamInfoList;
+    bool found = false;
+    while(caminfo->vid != 0) {
+        if (caminfo->vid == host->getDevice(0)->getVid() &&
+            caminfo->pid == host->getDevice(0)->getPid() && 
+            caminfo->size == _caminfo_size && caminfo->option == _caminfo_option) {
+            found = true;
+            break;
+        }
+        caminfo++;
+    }
+    if (!found) {
+        CAM_INFO("caminfo not found.\n");
+        exit(1);
+    }
+    CAM_INFO("Found: %s\n", caminfo->name);
+
+    ep_iso_in.setAddress(caminfo->en);
+    ep_iso_in.setSize(caminfo->mps);
+    uint8_t buf[26];
+    memset(buf, 0, sizeof(buf));
+    buf[2] = caminfo->formatIndex;
+    buf[3] = caminfo->frameIndex;
+    *reinterpret_cast<uint32_t*>(buf+4) = caminfo->interval;
+    USB_TYPE res = Control(SET_CUR, VS_COMMIT_CONTROL, 1, buf, sizeof(buf));
+    if (res != USB_TYPE_OK) {
+         CAM_DBG("SET_CUR VS_COMMIT_CONTROL FAILED");
+    }
+    res = setInterfaceAlternate(1, caminfo->if_alt);
+    if (res != USB_TYPE_OK) {
+         CAM_DBG("SET_INTERFACE FAILED");
+    }
+}
+#endif
+
+
+/*virtual*/ void USBHostCam::setVidPid(uint16_t vid, uint16_t pid)
+{
+    CAM_DBG("vid:%04x,pid:%04x", vid, pid);
+    caminfo = CamInfoList;
+    while(caminfo->vid != 0) {
+        if (caminfo->vid == vid && caminfo->pid == pid && 
+            caminfo->size == _caminfo_size && caminfo->option == _caminfo_option) {
+            caminfo_found = true;
+            break;
+        }
+        caminfo++;
+    }
+}
+ 
+/*virtual*/ bool USBHostCam::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed
+{
+    CAM_DBG("intf_nb=%d,intf_class=%02X,intf_subclass=%d,intf_protocol=%d", intf_nb, intf_class, intf_subclass, intf_protocol);
+    if ((cam_intf == -1) && caminfo_found) {
+        cam_intf = intf_nb;
+        device_found = true;
+        return true;
+    }
+    return false;
+}
+ 
+/*virtual*/ bool USBHostCam::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used
+{
+    CAM_DBG("intf_nb:%d,type:%d,dir:%d",intf_nb, type, dir);
+    return false;
+}
+
+#define SEQ_READ_IDOL 0
+#define SEQ_READ_EXEC 1
+#define SEQ_READ_DONE 2
+
+int USBHostCam::readJPEG(uint8_t* buf, int size, int timeout_ms) {
+    _buf = buf;
+    _pos = 0;
+    _size = size;
+    _seq = SEQ_READ_IDOL;
+    setOnResult(this, &USBHostCam::callback_motion_jpeg);
+    Timer timeout_t;
+    timeout_t.reset();
+    timeout_t.start();
+    while(timeout_t.read_ms() < timeout_ms && _seq != SEQ_READ_DONE) {
+        poll();
+    } 
+    return _pos;
+}
+
+/* virtual */ void USBHostCam::outputJPEG(uint8_t c, int status) { // from decodeMJPEG
+    if (_seq == SEQ_READ_IDOL) {
+        if (status == JPEG_START) {
+            _pos = 0;
+            _seq = SEQ_READ_EXEC;
+        }
+    }
+    if (_seq == SEQ_READ_EXEC) {
+        if (_pos < _size) {
+            _buf[_pos++] = c;  
+        }  
+        if (status == JPEG_END) {
+            _seq = SEQ_READ_DONE;
+        }
+    }
+}
+
+void USBHostCam::callback_motion_jpeg(uint16_t frame, uint8_t* buf, int len) {
+        inputPacket(buf, len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z_USBHostC270/USBHostCam.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,98 @@
+// USBHostCam.h
+#include "USBHostConf.h"
+#include "USBHost.h"
+#include "BaseUvc.h"
+#include "decodeMJPEG.h"
+#pragma once
+
+#define _160x120 2
+#define _176x144 3
+#define _320x176 4
+#define _320x240 5
+#define _352x288 6
+#define _432x240 7
+#define _640x480 1
+#define _544x288 8
+#define _640x360 9
+#define _752x416 10
+#define _800x448 11
+#define _800x600 12
+
+#define TEST_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
+
+struct CamInfo {
+    uint16_t vid;
+    uint16_t pid;
+    uint8_t size;
+    uint8_t option;
+//
+    const char* name;
+    uint8_t formatIndex;
+    uint8_t frameIndex;
+    uint32_t interval;
+    uint8_t en;
+    uint8_t mps;
+    uint8_t if_alt;
+};
+
+/** 
+ * A class to communicate a Cam
+ */
+class USBHostCam : public IUSBEnumerator, public BaseUvc, public decodeMJPEG {
+public:
+    /**
+    * Constructor
+    *
+    */
+    USBHostCam(uint8_t size = _160x120, uint8_t option = 0, CamInfo* user_caminfo = NULL);
+
+    /**
+    * Check if a Cam device is connected
+    *
+    * @return true if a Cam device is connected
+    */
+    bool connected();
+ 
+    /**
+     * Try to connect to a Cam device
+     *
+     * @return true if connection was successful
+     */
+    bool connect();
+
+    /**
+     * read jpeg image
+     *
+     * @param buf read buffer 
+     * @param size buffer size 
+     * @param timeout_ms timeout default 15sec
+     * @return jpeg size if read success else -1
+     */
+    int readJPEG(uint8_t* buf, int size, int timeout_ms = 15*1000);
+
+protected:
+    //From IUSBEnumerator
+    virtual void setVidPid(uint16_t vid, uint16_t pid);
+    virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); //Must return true if the interface should be parsed
+    virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); //Must return true if the endpoint will be used
+
+private:
+    bool dev_connected;
+ 
+    int cam_intf;
+    bool device_found;
+    bool caminfo_found;
+
+    uint8_t _seq;
+    uint8_t* _buf;
+    int _pos;
+    int _size;
+    CamInfo* CamInfoList;
+    CamInfo* caminfo;
+    uint8_t _caminfo_size;
+    uint8_t _caminfo_option;
+
+    virtual void outputJPEG(uint8_t c, int status); // from decodeMJPEG
+    void callback_motion_jpeg(uint16_t frame, uint8_t* buf, int len);
+    void init();
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z_USBHostC270/decodeMJPEG.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,180 @@
+// decodeMJPEG.cpp 2012/12/8
+// decode motion-jpeg to jpeg
+#include "mbed.h"
+#include "decodeMJPEG.h"
+
+#define MARK_SOF0 0xc0
+#define MARK_DHT  0xc4
+#define MARK_RST0 0xd0
+#define MARK_RST7 0xd7
+#define MARK_SOI  0xd8
+#define MARK_EOI  0xd9
+#define MARK_SOS  0xda
+#define MARK_DQT  0xdb
+#define MARK_DRI  0xdd
+#define MARK_APP  0xe0
+ 
+#define SEQ_INIT      0
+#define SEQ_SOI       1
+#define SEQ_FRAME     2
+#define SEQ_MARK      3
+#define SEQ_SEG_LEN   4
+#define SEQ_SEG_LEN2  5
+#define SEQ_SEG_BODY  6
+#define SEQ_SOS       7
+#define SEQ_SOS2      8
+
+static const uint8_t dht[] = {
+0xFF,0xC4,0x01,0xA2,0x00,0x00,0x01,0x05,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,
+0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,
+0x0B,0x01,0x00,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,
+0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x10,0x00,
+0x02,0x01,0x03,0x03,0x02,0x04,0x03,0x05,0x05,0x04,0x04,0x00,0x00,0x01,0x7D,0x01,
+0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,
+0x71,0x14,0x32,0x81,0x91,0xA1,0x08,0x23,0x42,0xB1,0xC1,0x15,0x52,0xD1,0xF0,0x24,
+0x33,0x62,0x72,0x82,0x09,0x0A,0x16,0x17,0x18,0x19,0x1A,0x25,0x26,0x27,0x28,0x29,
+0x2A,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,
+0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6A,
+0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,
+0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,
+0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xC2,0xC3,0xC4,0xC5,0xC6,
+0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xE1,0xE2,0xE3,
+0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,
+0xFA,0x11,0x00,0x02,0x01,0x02,0x04,0x04,0x03,0x04,0x07,0x05,0x04,0x04,0x00,0x01,
+0x02,0x77,0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,
+0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,0xA1,0xB1,0xC1,0x09,0x23,0x33,
+0x52,0xF0,0x15,0x62,0x72,0xD1,0x0A,0x16,0x24,0x34,0xE1,0x25,0xF1,0x17,0x18,0x19,
+0x1A,0x26,0x27,0x28,0x29,0x2A,0x35,0x36,0x37,0x38,0x39,0x3A,0x43,0x44,0x45,0x46,
+0x47,0x48,0x49,0x4A,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x63,0x64,0x65,0x66,
+0x67,0x68,0x69,0x6A,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7A,0x82,0x83,0x84,0x85,
+0x86,0x87,0x88,0x89,0x8A,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0xA2,0xA3,
+0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,
+0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,
+0xD9,0xDA,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xF2,0xF3,0xF4,0xF5,0xF6,
+0xF7,0xF8,0xF9,0xFA,
+};
+
+decodeMJPEG::decodeMJPEG()
+{
+    m_seq = SEQ_INIT;
+}
+
+void decodeMJPEG::inputPacket(const uint8_t* buf, int len)
+{
+    for(int i = 12; i < len; i++) {
+        input(buf[i]);
+    }    
+}
+
+void decodeMJPEG::input(uint8_t c)
+{
+    switch(m_seq) {
+        case SEQ_INIT:
+            if (c == 0xff) {
+                m_seq = SEQ_SOI;
+            }
+            break;
+        case SEQ_SOI:
+            if (c == MARK_SOI) {
+                outputJPEG(0xff, JPEG_START); // start
+                outputJPEG(c);
+                m_bDHT = false;
+                m_seq = SEQ_FRAME;
+            } else {
+                m_seq = SEQ_INIT;
+            }
+            break;
+        case SEQ_FRAME:
+            if (c == 0xff) {
+                m_seq = SEQ_MARK;
+            } else {
+                m_seq = SEQ_INIT;
+            }
+            break;
+        case SEQ_MARK:
+            if (c == MARK_SOI || c == MARK_EOI || c == 0x00) {
+                m_seq = SEQ_INIT;
+                break;
+            }
+            m_mark = c;
+            m_seq = SEQ_SEG_LEN;
+            break;
+        case SEQ_SEG_LEN:
+            m_seg_len = c;
+            m_seq = SEQ_SEG_LEN2;
+            break;
+        case SEQ_SEG_LEN2:
+            m_seg_len <<= 8;
+            m_seg_len |= c;
+            m_seg_len -= 2;
+            m_seg_pos = 0;
+            m_seq = SEQ_SEG_BODY;
+            if (m_mark == MARK_SOS) {
+                if (m_bDHT == false) {
+                    for(int i = 0; i < sizeof(dht); i++) {
+                        outputJPEG(dht[i]);
+                    }    
+                }
+                m_output_desable = false;
+            } else if (m_mark == MARK_DHT) {
+                m_bDHT = true;
+                m_output_desable = false;
+            } else {
+                m_output_desable = false;
+            }
+            if (!m_output_desable) {
+                outputJPEG(0xff);
+                outputJPEG(m_mark);
+                outputJPEG((m_seg_len+2) >> 8);
+                outputJPEG((m_seg_len+2) & 0xff);
+            } 
+            break;
+        case SEQ_SEG_BODY:
+            if (!m_output_desable) {
+                outputJPEG(c);
+            }
+            if (++m_seg_pos < m_seg_len) {
+                break;
+            }
+            if (m_mark == MARK_SOS) {
+                m_seq = SEQ_SOS;
+                break;
+            }
+            m_seq = SEQ_FRAME;
+            break;
+        case SEQ_SOS:
+            if (c == 0xff) {
+                m_seq = SEQ_SOS2;
+                break;
+            }
+            outputJPEG(c);
+            break;
+        case SEQ_SOS2:
+            if (c == 0x00) {
+                outputJPEG(0xff);
+                outputJPEG(0x00);
+                m_seq = SEQ_SOS;
+                break;
+            } else if (c >= MARK_RST0 && c <= MARK_RST7) {
+                outputJPEG(0xff);
+                outputJPEG(c);
+                m_seq = SEQ_SOS;
+                break;
+            } else if (c == MARK_EOI) {
+                outputJPEG(0xff);
+                outputJPEG(c, JPEG_END);
+                m_seq = SEQ_INIT;
+                break;
+            } else if (c == MARK_SOI) {
+                outputJPEG(0xff);
+                outputJPEG(c);
+                m_seq = SEQ_INIT;
+                break;
+            }
+            m_seq = SEQ_INIT;
+            break;
+        default:
+            m_seq = SEQ_INIT;
+            break;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z_USBHostC270/decodeMJPEG.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,25 @@
+// decodeMJPEG.h 2012/12/9
+#ifndef DECODE_MJPEG_H
+#define DECODE_MJPEG_H
+
+#define JPEG_NONE  0
+#define JPEG_START 1
+#define JPEG_END   2
+#define JPEG_ERROR 3
+
+class decodeMJPEG {
+public:
+    decodeMJPEG();
+    void inputPacket(const uint8_t* buf, int len);
+    virtual void outputJPEG(uint8_t c, int status = JPEG_NONE) = 0;
+protected:
+    void input(uint8_t c);
+    int m_seq;
+    uint8_t m_mark;
+    uint16_t m_seg_pos; 
+    uint16_t m_seg_len;
+    bool m_bDHT;
+    bool m_output_desable;
+};
+
+#endif // DECODE_MJPEG_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAG3110.lib	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mmaas/code/MAG3110/#f510561f6107
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAG3110/MAG3110.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,118 @@
+
+#include "MAG3110.h"
+#include "mbed.h"
+
+/******************************************************************************
+ * Constructors
+ ******************************************************************************/
+MAG3110::MAG3110(PinName sda, PinName scl): _i2c(sda, scl),
+    _i2c_address(0x1D), _pc(NULL), _debug(false)
+{
+    begin();
+}
+
+MAG3110::MAG3110(PinName sda, PinName scl, Serial *pc): _i2c(sda, scl),
+    _i2c_address(0x1D), _pc(pc), _debug(true)
+{
+    begin();
+}
+
+void MAG3110::begin()
+{
+    char cmd[2];
+
+    cmd[0] = MAG_CTRL_REG2;
+    cmd[1] = 0x80;
+    _i2c.write(_i2c_address, cmd, 2);
+
+    cmd[0] = MAG_CTRL_REG1;
+    cmd[1] = MAG_3110_SAMPLE80+MAG_3110_OVERSAMPLE2+MAG_3110_ACTIVE;
+    _i2c.write(_i2c_address, cmd, 2);
+
+    // No adjustment initially
+    _avgX = 0;
+    _avgY = 0;
+}
+
+// Read a single byte form 8 bit register, return as int
+int MAG3110::readReg(char regAddr)
+{
+    char cmd[1];
+
+    cmd[0] = regAddr;
+    _i2c.write(_i2c_address, cmd, 1);
+
+    cmd[0] = 0x00;
+    _i2c.read(_i2c_address, cmd, 1);
+    return (int)( cmd[0]);
+}
+
+
+// read a register per, pass first reg value, reading 2 bytes increments register
+// Reads MSB first then LSB
+int MAG3110::readVal(char regAddr)
+{
+    char cmd[2];
+
+    cmd[0] = regAddr;
+    _i2c.write(_i2c_address, cmd, 1);
+
+    cmd[0] = 0x00;
+    cmd[1] = 0x00;
+    _i2c.read(_i2c_address, cmd, 2);
+    return (int)( (cmd[1]|(cmd[0] << 8))); //concatenate the MSB and LSB
+}
+
+
+float MAG3110::getHeading()
+{
+    int xVal = readVal(MAG_OUT_X_MSB);
+    int yVal = readVal(MAG_OUT_Y_MSB);
+    return (atan2((double)(yVal - _avgY),(double)(xVal - _avgX)))*180/PI;
+}
+
+void MAG3110::getValues(int *xVal, int *yVal, int *zVal)
+{
+    *xVal = readVal(MAG_OUT_X_MSB);
+    *yVal = readVal(MAG_OUT_Y_MSB);
+    *zVal = readVal(MAG_OUT_Z_MSB);
+}
+
+
+void MAG3110::setCalibration(int minX, int maxX, int minY, int maxY )
+{
+    _avgX=(maxX+minX)/2;
+    _avgY=(maxY+minY)/2;
+}
+
+
+
+void MAG3110::calXY(PinName pin, int activeValue )
+{
+    DigitalIn calPin(pin);
+    int tempXmax, tempXmin, tempYmax, tempYmin, newX, newY;
+
+
+    // Wait for Button Press and Release before beginning calibration
+    while(calPin != activeValue) {}
+    while(calPin == activeValue) {}
+
+
+    // Read initial values of magnetomoter - read it here to create a slight delay for calPin to settle
+    tempXmax = tempXmin = readVal(MAG_OUT_X_MSB);
+    tempYmax = tempYmin = readVal(MAG_OUT_Y_MSB);
+
+    // Update min and max values until calPin asserted again
+    while(calPin != activeValue) {
+        newX = readVal(MAG_OUT_X_MSB);
+        newY = readVal(MAG_OUT_Y_MSB);
+        if (newX > tempXmax) tempXmax = newX;
+        if (newX < tempXmin) tempXmin = newX;
+        if (newY > tempYmax) tempYmax = newY;
+        if (newY < tempYmin) tempYmin = newY;
+    }
+
+    setCalibration( tempXmin, tempXmax, tempYmin, tempYmax );
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAG3110/MAG3110.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,150 @@
+/*
+ * MAG3110 Sensor Library for mbed
+ * TODO: Add proper header
+ */
+
+#ifndef MAG3110_H
+#define MAG3110_H
+
+#include "mbed.h"
+
+#define PI 3.14159265359
+
+#define MAG_ADDR 0x1D
+
+// define registers
+#define MAG_DR_STATUS 0x00
+#define MAG_OUT_X_MSB 0x01
+#define MAG_OUT_X_LSB 0x02
+#define MAG_OUT_Y_MSB 0x03
+#define MAG_OUT_Y_LSB 0x04
+#define MAG_OUT_Z_MSB 0x05
+#define MAG_OUT_Z_LSB 0x06
+#define MAG_WHO_AM_I  0x07
+#define MAG_SYSMOD    0x08
+#define MAG_OFF_X_MSB 0x09
+#define MAG_OFF_X_LSB 0x0A
+#define MAG_OFF_Y_MSB 0x0B
+#define MAG_OFF_Y_LSB 0x0C
+#define MAG_OFF_Z_MSB 0x0D
+#define MAG_OFF_Z_LSB 0x0E
+#define MAG_DIE_TEMP  0x0F
+#define MAG_CTRL_REG1 0x10
+#define MAG_CTRL_REG2 0x11
+
+// what should WHO_AM_I return?
+#define MAG_3110_WHO_AM_I_VALUE 0xC4
+
+
+// Fields in registers
+// CTRL_REG1: dr2,dr1,dr0  os1,os0  fr tm ac
+
+// Sampling rate from 80Hz down to 0.625Hz
+#define MAG_3110_SAMPLE80 0
+#define MAG_3110_SAMPLE40 0x20
+#define MAG_3110_SAMPLE20 0x40
+#define MAG_3110_SAMPLE10 0x60
+#define MAG_3110_SAMPLE5 0x80
+#define MAG_3110_SAMPLE2_5 0xA0
+#define MAG_3110_SAMPLE1_25 0xC0
+#define MAG_3110_SAMPLE0_625 0xE0
+
+// How many samples to average (lowers data rate)
+#define MAG_3110_OVERSAMPLE1 0
+#define MAG_3110_OVERSAMPLE2 0x08
+#define MAG_3110_OVERSAMPLE3 0x10
+#define MAG_3110_OVERSAMPLE4 0x18
+
+// read only 1 byte per axis
+#define MAG_3110_FASTREAD 0x04
+// do one measurement (even if in standby mode)
+#define MAG_3110_TRIGGER 0x02
+// put in active mode
+#define MAG_3110_ACTIVE 0x01
+
+// CTRL_REG2: AUTO_MRST_EN  _ RAW MAG_RST _ _ _ _ _
+// reset sensor after each reading
+#define MAG_3110_AUTO_MRST_EN 0x80
+// don't subtract user offsets
+#define MAG_3110_RAW 0x20
+// reset magnetic sensor after too-large field
+#define MAG_3110_MAG_RST 0x10
+
+// DR_STATUS Register ZYXOW ZOW YOW XOW ZYXDR ZDR YDR XDR
+#define MAG_3110_ZYXDR  0x08
+
+/**
+ * MAG3110 Class to read X/Y/Z data from the magentometer
+ *
+ */
+class MAG3110
+{
+public:
+    /**
+     * Main constructor
+     * @param sda SDA pin
+     * @param sdl SCL pin
+     * @param addr addr of the I2C peripheral
+     */
+    MAG3110(PinName sda, PinName scl);
+    /**
+     * Debug version of constructor
+     * @param sda SDA pin
+     * @param sdl SCL pin
+     * @param addr Address of the I2C peripheral
+     * @param pc Serial object to output debug messages
+     */
+    MAG3110(PinName sda, PinName scl, Serial *pc); //pass serial for debug
+    /**
+     * Setup the Magnetometer
+     *
+     */
+    void begin();
+    /**
+     * Read a register, return its value as int
+     * @param regAddr The address to read
+     * @return value in register
+     */
+    int readReg(char regAddr);
+    /**
+     * Read a value from a pair of registers, return as int
+     * @param regAddr The address to read
+     * @return Value from 2 consecutive registers
+     */
+    int readVal(char regAddr);
+    /**
+     * Calculate the heading
+     * @return heading in degrees
+     */
+    float getHeading();
+    /**
+     * Perform a read on the X, Y and Z values.
+     * @param xVal Pointer to X value
+     * @param yVal Pointer to Y value
+     * @param zVal Pointer to Z value
+     */
+    void getValues(int *xVal, int *yVal, int *zVal);
+    /**
+     * Set the calibration parameters if required.
+     * @param minX Minimum value for X range
+     * @param maxX Maximum value for X range
+     * @param minY Minimum value for Y range
+     * @param maxY maximum value for Y range
+     */
+    void setCalibration(int minX, int maxX, int minY, int maxY);
+    /**
+     * Perfrom the calibration process.
+     * @param calPin GPIO pinName to use for coordinating the board rotation
+     * @param activeValue the GPIO pin value when input asserted
+     */    
+     void calXY(PinName pin, int activeValue);
+
+private:
+    I2C _i2c;
+    int _i2c_address;
+    Serial *_pc;
+    bool _debug;
+    int _avgX, _avgY;
+
+};
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem.lib	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,1 @@
+https://mbed.org/teams/Multi-Hackers/code/SocketModem/#c7d8fe37981b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/Socket/Endpoint.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,61 @@
+/* Copyright (C) 2012 mbed.org, MIT License
+ *
+ * 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 "Socket/Socket.h"
+#include "Socket/Endpoint.h"
+#include <cstring>
+
+using std::memset;
+
+Endpoint::Endpoint()
+{
+    reset_address();
+}
+
+Endpoint::~Endpoint()
+{
+}
+
+void Endpoint::reset_address(void)
+{
+    _ipAddress[0] = '\0';
+    _port = 0;
+}
+
+int Endpoint::set_address(const char* host, const int port)
+{
+    int length = strlen(host) + 1; // size of host including terminating character
+    if (length > sizeof(_ipAddress)) {
+        printf("[ERROR] could not set address because the hostname is too long\r\n");
+        return -1;
+    } else {
+        strncpy((char*) _ipAddress, host, length);
+        _ipAddress[length] = '\0';
+        _port = port;
+    }
+    return 0;
+}
+
+char* Endpoint::get_address()
+{
+    return _ipAddress;
+}
+
+int   Endpoint::get_port()
+{
+    return _port;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/Socket/Endpoint.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,64 @@
+/* Copyright (C) 2012 mbed.org, MIT License
+ *
+ * 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 ENDPOINT_H
+#define ENDPOINT_H
+
+#include "Cellular.h"
+
+class UDPSocket;
+
+/**
+IP Endpoint (address, port)
+*/
+class Endpoint {
+    friend class UDPSocket;
+
+public:
+    /** IP Endpoint (address, port)
+     */
+    Endpoint(void);
+    
+    ~Endpoint(void);
+    
+    /** Reset the address of this endpoint
+     */
+    void reset_address(void);
+    
+    /** Set the address of this endpoint
+    \param host The endpoint address (it can either be an IP Address or a hostname that will be resolved with DNS).
+    \param port The endpoint port
+    \return 0 on success, -1 on failure (when an hostname cannot be resolved by DNS).
+     */
+    int  set_address(const char* host, const int port);
+    
+    /** Get the IP address of this endpoint
+    \return The IP address of this endpoint.
+     */
+    char* get_address(void);
+    
+    /** Get the port of this endpoint
+    \return The port of this endpoint
+     */
+    int get_port(void);
+
+protected:
+    char _ipAddress[128];
+    int _port;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/Socket/Socket.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,38 @@
+/* Copyright (C) 2012 mbed.org, MIT License
+ *
+ * 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 "Socket.h"
+#include <cstring>
+#include "Transport.h"
+
+Socket::Socket() : _blocking(true), _timeout(1500) {
+    ip = Transport::getInstance();
+}
+
+void Socket::set_blocking(bool blocking, unsigned int timeout) {
+    _blocking = blocking;
+    _timeout = timeout;
+}
+
+int Socket::close() {
+    return (ip->close()) ? 0 : -1;
+}
+
+Socket::~Socket() {
+    close(); //Don't want to leak
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/Socket/Socket.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,51 @@
+/* Copyright (C) 2012 mbed.org, MIT License
+ *
+ * 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 SOCKET_H_
+#define SOCKET_H_
+
+#include "IPStack.h"
+
+/** Socket file descriptor and select wrapper
+  */
+class Socket {
+public:
+    /** Socket
+     */
+    Socket();
+    
+    /** Set blocking or non-blocking mode of the socket and a timeout on
+        blocking socket operations
+    \param blocking  true for blocking mode, false for non-blocking mode.
+    \param timeout   timeout in ms [Default: (1500)ms].
+    */
+    void set_blocking(bool blocking, unsigned int timeout=1500);
+    
+    /** Close the socket file descriptor
+     */
+    int close();
+    
+    ~Socket();
+    
+protected:
+    bool _blocking;
+    int _timeout;
+    mts::IPStack * ip;
+};
+
+
+#endif /* SOCKET_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/Socket/TCPSocketConnection.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,99 @@
+/* Copyright (C) 2012 mbed.org, MIT License
+ *
+ * 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 "TCPSocketConnection.h"
+#include <algorithm>
+
+TCPSocketConnection::TCPSocketConnection()
+{
+}
+
+int TCPSocketConnection::connect(const char* host, const int port)
+{
+    if (!ip->open(host, port, mts::IPStack::TCP)) {
+        return -1;
+    }
+    return 0;
+}
+
+bool TCPSocketConnection::is_connected(void)
+{
+    return ip->isOpen();
+}
+
+int TCPSocketConnection::send(char* data, int length)
+{
+    Timer tmr;
+
+    if (!_blocking) {
+        tmr.start();
+        while (tmr.read_ms() < _timeout) {
+            if (ip->writeable())
+                break;
+        }
+        if (tmr.read_ms() >= _timeout) {
+            return -1;
+        }
+    }
+    return ip->write(data, length, 0);
+}
+
+// -1 if unsuccessful, else number of bytes written
+int TCPSocketConnection::send_all(char* data, int length)
+{
+    if (_blocking) {
+        return ip->write(data, length, -1);
+    } else {
+        return ip->write(data, length, _timeout);
+    }
+}
+
+// -1 if unsuccessful, else number of bytes received
+int TCPSocketConnection::receive(char* data, int length)
+{
+    Timer tmr;
+    int time = -1;
+
+    if (!_blocking) {
+        tmr.start();
+        while (time < _timeout + 20) {
+            if (ip->readable()) {
+                break;
+            }
+            time = tmr.read_ms();
+        }
+        if (time >= _timeout + 20) {
+            return -1;
+        }
+    } else {
+        while(!ip->readable());
+    }
+
+    return ip->read(data, length, 0);
+}
+
+
+// -1 if unsuccessful, else number of bytes received
+int TCPSocketConnection::receive_all(char* data, int length)
+{
+    if (_blocking) {
+        return ip->read(data, length, -1);
+    } else {
+        return ip->read(data, length, _timeout);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/Socket/TCPSocketConnection.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,76 @@
+/* Copyright (C) 2012 mbed.org, MIT License
+ *
+ * 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 TCPSOCKET_H
+#define TCPSOCKET_H
+
+#include "Socket.h"
+#include "Endpoint.h"
+
+/**
+TCP socket connection
+*/
+class TCPSocketConnection: public Socket, public Endpoint {
+    
+public:
+    /** TCP socket connection
+    */
+    TCPSocketConnection();
+    
+    /** Connects this TCP socket to the server
+    \param host The host to connect to. It can either be an IP Address or a hostname that will be resolved with DNS.
+    \param port The host's port to connect to.
+    \return 0 on success, -1 on failure.
+    */
+    int connect(const char* host, const int port);
+    
+    /** Check if the socket is connected
+    \return true if connected, false otherwise.
+    */
+    bool is_connected(void);
+    
+    /** Send data to the remote host.
+    \param data The buffer to send to the host.
+    \param length The length of the buffer to send.
+    \return the number of written bytes on success (>=0) or -1 on failure
+     */
+    int send(char* data, int length);
+    
+    /** Send all the data to the remote host.
+    \param data The buffer to send to the host.
+    \param length The length of the buffer to send.
+    \return the number of written bytes on success (>=0) or -1 on failure
+    */
+    int send_all(char* data, int length);
+    
+    /** Receive data from the remote host.
+    \param data The buffer in which to store the data received from the host.
+    \param length The maximum length of the buffer.
+    \return the number of received bytes on success (>=0) or -1 on failure
+     */
+    int receive(char* data, int length);
+    
+    /** Receive all the data from the remote host.
+    \param data The buffer in which to store the data received from the host.
+    \param length The maximum length of the buffer.
+    \return the number of received bytes on success (>=0) or -1 on failure
+    */
+    int receive_all(char* data, int length);
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/Socket/Transport.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,40 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "Transport.h"
+#include "Cellular.h"
+#include "Wifi.h"
+
+Transport::TransportType Transport::_type = Transport::NONE;
+
+void Transport::setTransport(TransportType type)
+{
+    _type = type;
+}
+
+IPStack* Transport::getInstance()
+{
+    switch (_type) {
+        case CELLULAR:
+            return (IPStack*) Cellular::getInstance();
+        case WIFI:
+            return (IPStack*) Wifi::getInstance();
+        default:
+            printf("[ERROR] Transport not set, use setTransport method.\n\r");
+            return NULL;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/Socket/Transport.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,60 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 TRANSPORT_H
+#define TRANSPORT_H
+
+#include "mbed.h"
+#include "IPStack.h"
+
+using namespace mts;
+
+/** This class has been added to the standard mbed Socket library enabling people
+* to use the Socket library interfaces for different transports that have
+* their own internal IP-Stack. Use this class prior to instantiating any of the
+* other classes in this folder to determine the underlying transport that will
+* be used by them. It is important to know that the transport classes themsleves
+* like Cellular or WiFi, must be properly initialized and connected before any
+* of the Socket package classes can be used or even instantiated.
+*/
+class Transport
+{
+public:
+    ///An enumeration that holds the supported Transport Types.
+    enum TransportType {
+        CELLULAR, WIFI, NONE
+    };
+    
+    /** This method allows you to set the transport to be used when creating other 
+    * objects from the Socket folder like TCPSocketConnection and UDPSocket.  
+    *
+    * @param type the type of underlying transport to be used. The default is NONE.
+    */
+    static void setTransport(TransportType type);
+    
+    /** This method is used within the Socket class to get the appropraite transport
+    * as an IPStack object.  In general you do not need to call this directly, but
+    * simply use the other classes in this folder. 
+    *
+    * @returns a pointer to an object that implements IPStack.
+    */
+    static IPStack* getInstance();
+    
+private:
+    static Transport::TransportType _type; // Member variable that holds the desired transport
+};
+
+#endif /* TRANSPORT_H */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/cellular/Cellular.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,910 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+
+#include "Cellular.h"
+#include "MTSText.h"
+#include "MTSSerial.h"
+
+using namespace mts;
+
+Cellular* Cellular::instance = NULL;
+
+Cellular* Cellular::getInstance()
+{
+    if(instance == NULL) {
+        instance = new Cellular(NULL);
+    }
+    return instance;
+}
+
+Cellular::Cellular(MTSBufferedIO* io)
+    : io(io)
+    , echoMode(true)
+    , pppConnected(false)
+    , mode(TCP)
+    , socketOpened(false)
+    , socketCloseable(true)
+    , local_port(0)
+    , local_address("")
+    , host_port(0)
+    , dcd(NULL)
+    , dtr(NULL)
+{
+}
+
+Cellular::~Cellular()
+{
+    if (dtr != NULL) {
+        dtr->write(1);
+    }
+
+    delete dcd;
+    delete dtr;
+}
+
+bool Cellular::init(MTSBufferedIO* io, PinName DCD, PinName DTR)
+{
+    if (io == NULL) {
+        return false;
+    }
+
+    if(dcd) {
+        delete dcd;
+        dcd = NULL;
+    }
+    if(dtr) {
+        delete dtr;
+        dtr = NULL;
+    }
+
+    if (DCD != NC) {
+        // the radio will raise and lower this line
+        dcd = new DigitalIn(DCD); //PTA4 - KL46
+    }
+    if (DTR != NC) {
+        dtr = new DigitalOut(DTR); //PTC9 - KL46
+        /* This line should be lowered when we want to talk to the radio and raised when we're done
+        for now we will lower it in the constructor and raise it in the destructor
+        */
+        dtr->write(0);
+    }
+    instance->io = io;
+    
+    test();
+    // Reset radio to make sure it's in a good state and wait for it to come back
+    reset();
+    test();
+    
+    return SUCCESS;
+}
+
+
+bool Cellular::connect()
+{
+    //Check if socket is open
+    if(socketOpened) {
+        return true;
+    }
+
+    //Check if already connected
+    if(isConnected()) {
+        return true;
+    }
+
+    Timer tmr;
+
+    //Check Registration: AT+CREG? == 0,1
+    tmr.start();
+    do {
+        Registration registration = getRegistration();
+        if(registration != REGISTERED) {
+            printf("[WARNING] Not Registered [%d] ... waiting\r\n", (int)registration);
+            wait(1);
+        } else {
+            break;
+        }
+    } while(tmr.read() < 30);
+
+    //Check RSSI: AT+CSQ
+    tmr.reset();
+    do {
+        int rssi = getSignalStrength();
+        printf("[DEBUG] Signal strength: %d\r\n", rssi);
+        if(rssi == 99) {
+            printf("[WARNING] No Signal ... waiting\r\n");
+            wait(1);
+        } else {
+            break;
+        }
+    } while(tmr.read() < 30);
+
+    //AT#CONNECTIONSTART: Make a PPP connection
+    printf("[DEBUG] Making PPP Connection Attempt. APN[%s]\r\n", apn.c_str());
+    std::string pppResult = sendCommand("AT#CONNECTIONSTART", 120000);
+    std::vector<std::string> parts = Text::split(pppResult, "\r\n");
+
+    if(pppResult.find("Ok_Info_GprsActivation") != std::string::npos) {
+        if(parts.size() >= 2) {
+            local_address = parts[1];
+        }
+        printf("[INFO] PPP Connection Established: IP[%s]\r\n", local_address.c_str());
+        pppConnected = true;
+
+    } else {
+        pppConnected = false;
+    }
+
+    return pppConnected;
+}
+
+void Cellular::disconnect()
+{
+    //AT#CONNECTIONSTOP: Close a PPP connection
+    printf("[DEBUG] Closing PPP Connection\r\n");
+
+    if(socketOpened) {
+        close();
+    }
+
+    Code code = sendBasicCommand("AT#CONNECTIONSTOP", 10000);
+    if(code == SUCCESS) {
+        printf("[DEBUG] Successfully closed PPP Connection\r\n");
+    } else {
+        printf("[ERROR] Closing PPP Connection [%d].  Continuing ...\r\n", (int)code);
+    }
+
+    pppConnected = false;
+}
+
+bool Cellular::isConnected()
+{
+    //1) Check if APN was set
+    if(apn.size() == 0) {
+        printf("[DEBUG] APN is not set\r\n");
+        return false;
+    }
+
+    //1) Check that we do not have a live connection up
+    if(socketOpened) {
+        printf("[DEBUG] Socket is opened\r\n");
+        return true;
+    }
+    //2) Query the radio
+    std::string result = sendCommand("AT#VSTATE", 3000);
+    if(result.find("CONNECTED") != std::string::npos) {
+        if(pppConnected == false) {
+            printf("[WARNING] Internal PPP state tracking differs from radio (DISCONNECTED:CONNECTED)\r\n");
+        }
+        pppConnected = true;
+    } else {
+        if(pppConnected == true) {
+            //Find out what state is
+            size_t pos = result.find("STATE:");
+            if(pos != std::string::npos) {
+                result = Text::getLine(result, pos + sizeof("STATE:"), pos);
+                printf("[WARNING] Internal PPP state tracking differs from radio (CONNECTED:%s)\r\n", result.c_str());
+            } else {
+                printf("[ERROR] Unable to parse radio state: [%s]\r\n", result.c_str());
+            }
+
+        }
+        pppConnected = false;
+    }
+
+    return pppConnected;
+}
+
+bool Cellular::bind(unsigned int port)
+{
+    if(socketOpened) {
+        printf("[ERROR] socket is open. Can not set local port\r\n");
+        return false;
+    }
+    if(port > 65535) {
+        printf("[ERROR] port out of range (0-65535)\r\n");
+        return false;
+    }
+    local_port = port;
+    return true;
+}
+
+bool Cellular::open(const std::string& address, unsigned int port, Mode mode)
+{
+    char buffer[256] = {0};
+    Code portCode, addressCode;
+
+    //1) Check that we do not have a live connection up
+    if(socketOpened) {
+        //Check that the address, port, and mode match
+        if(host_address != address || host_port != port || this->mode != mode) {
+            if(this->mode == TCP) {
+                printf("[ERROR] TCP socket already opened [%s:%d]\r\n", host_address.c_str(), host_port);
+            } else {
+                printf("[ERROR] UDP socket already opened [%s:%d]\r\n", host_address.c_str(), host_port);
+            }
+            return false;
+        }
+
+        printf("[DEBUG] Socket already opened\r\n");
+        return true;
+    }
+
+    //2) Check Parameters
+    if(port > 65535) {
+        printf("[ERROR] port out of range (0-65535)\r\n");
+        return false;
+    }
+
+    //3) Check PPP connection
+    if(!isConnected()) {
+        printf("[ERROR] PPP not established.  Attempting to connect\r\n");
+        if(!connect()) {
+            printf("[ERROR] PPP connection failed\r\n");
+            return false;
+        } else {
+            printf("[DEBUG] PPP connection established\r\n");
+        }
+    }
+
+    //Set Local Port
+    if(local_port != 0) {
+        //Attempt to set local port
+        sprintf(buffer, "AT#OUTPORT=%d", local_port);
+        Code code = sendBasicCommand(buffer, 1000);
+        if(code != SUCCESS) {
+            printf("[WARNING] Unable to set local port (%d) [%d]\r\n", local_port, (int) code);
+        }
+    }
+
+    //Set TCP/UDP parameters
+    if(mode == TCP) {
+        if(socketCloseable) {
+            Code code = sendBasicCommand("AT#DLEMODE=1,1", 1000);
+            if(code != SUCCESS) {
+                printf("[WARNING] Unable to set socket closeable [%d]\r\n", (int) code);
+            }
+        }
+        sprintf(buffer, "AT#TCPPORT=1,%d", port);
+        portCode = sendBasicCommand(buffer, 1000);
+        addressCode = sendBasicCommand("AT#TCPSERV=1,\"" + address + "\"", 1000);
+    } else {
+        if(socketCloseable) {
+            Code code = sendBasicCommand("AT#UDPDLEMODE=1", 1000);
+            if(code != SUCCESS) {
+                printf("[WARNING] Unable to set socket closeable [%d]\r\n", (int) code);
+            }
+        }
+        sprintf(buffer, "AT#UDPPORT=%d", port);
+        portCode = sendBasicCommand(buffer, 1000);
+        addressCode = sendBasicCommand("AT#UDPSERV=\"" + address + "\"", 1000);
+    }
+
+    if(portCode == SUCCESS) {
+        host_port = port;
+    } else {
+        printf("[ERROR] Host port could not be set\r\n");
+    }
+
+    if(addressCode == SUCCESS) {
+        host_address = address;
+    } else {
+        printf("[ERROR] Host address could not be set\r\n");
+    }
+
+    // Try and Connect
+    std::string sMode;
+    std::string sOpenSocketCmd;
+    if(mode == TCP) {
+        sOpenSocketCmd = "AT#OTCP=1";
+        sMode = "TCP";
+    } else {
+        sOpenSocketCmd = "AT#OUDP";
+        sMode = "UDP";
+    }
+
+    string response = sendCommand(sOpenSocketCmd, 30000);
+    if (response.find("Ok_Info_WaitingForData") != string::npos) {
+        printf("[INFO] Opened %s Socket [%s:%d]\r\n", sMode.c_str(), address.c_str(), port);
+        socketOpened = true;
+    } else {
+        printf("[WARNING] Unable to open %s Socket [%s:%d]\r\n", sMode.c_str(),  address.c_str(), port);
+        socketOpened = false;
+    }
+
+    return socketOpened;
+}
+
+bool Cellular::isOpen()
+{
+    if(io->readable()) {
+        printf("[DEBUG] Assuming open, data available to read.\n\r");
+        return true;
+    }
+    return socketOpened;
+}
+
+bool Cellular::close()
+{
+    if(io == NULL) {
+        printf("[ERROR] MTSBufferedIO not set\r\n");
+        return false;
+    }
+
+    if(!socketOpened) {
+        printf("[WARNING] Socket close() called, but socket was not open\r\n");
+        return true;
+    }
+
+    if(!socketCloseable) {
+        printf("[ERROR] Socket is not closeable\r\n");
+        return false;
+    }
+
+
+
+    if(io->write(ETX, 1000) != 1) {
+        printf("[ERROR] Timed out attempting to close socket\r\n");
+        return false;
+    }
+
+    Timer tmr;
+    int counter = 0;
+    char tmp[256];
+    tmr.start();
+    do {
+        if(socketOpened == false) {
+            break;
+        }
+        read(tmp, 256, 1000);
+    } while(counter++ < 10);
+
+    io->rxClear();
+    io->txClear();
+
+    socketOpened = false;
+    return true;
+}
+
+int Cellular::read(char* data, int max, int timeout)
+{
+    if(io == NULL) {
+        printf("[ERROR] MTSBufferedIO not set\r\n");
+        return -1;
+    }
+
+    //Check that nothing is in the rx buffer
+    if(!socketOpened && !io->readable()) {
+        printf("[ERROR] Socket is not open\r\n");
+        return -1;
+    }
+
+    int bytesRead = 0;
+
+    if(timeout >= 0) {
+        bytesRead = io->read(data, max, static_cast<unsigned int>(timeout));
+    } else {
+        bytesRead = io->read(data, max);
+    }
+
+    if(bytesRead > 0 && socketCloseable) {
+        //Remove escape characters
+        int index = 0;
+        bool escapeFlag = false;
+        for(int i = 0; i < bytesRead; i++) {
+            if(data[i] == DLE || data[i] == ETX) {
+                if(escapeFlag == true) {
+                    //This character has been escaped
+                    escapeFlag = false;
+                } else if(data[bytesRead] == DLE) {
+                    //Found escape character
+                    escapeFlag = true;
+                    continue;
+                } else {
+                    //ETX sent without escape -> Socket closed
+                    printf("[INFO] Read ETX character without DLE escape. Socket closed\r\n");
+                    socketOpened = false;
+                    continue;
+                }
+            }
+
+            if(index != i) {
+                data[index] = data[i];
+            }
+            index++;
+        }
+        bytesRead = index;
+    }
+
+    //Scan for socket closed message
+    for(size_t i = 0; i < bytesRead; i++) {
+        if(data[i] == 'O') {
+            if(strstr(&data[i], "Ok_Info_SocketClosed")) {
+                printf("[INFO] Found socket closed message. Socket closed\r\n");
+                //Close socket and Cut Off End of Message
+                socketOpened = false;
+                data[i] = '\0';
+                bytesRead = i;
+                break;
+            }
+        }
+    }
+    return bytesRead;
+}
+
+int Cellular::write(const char* data, int length, int timeout)
+{
+    if(io == NULL) {
+        printf("[ERROR] MTSBufferedIO not set\r\n");
+        return -1;
+    }
+
+    if(!socketOpened) {
+        printf("[ERROR] Socket is not open\r\n");
+        return -1;
+    }
+
+    //In order to avoid allocating another buffer, capture indices of
+    //characters to escape during write
+    int specialWritten = 0;
+    std::vector<int> vSpecial;
+    if(socketCloseable) {
+        for(int i = 0; i < length; i++) {
+            if(data[i] == ETX || data[i] == DLE) {
+                //Push back index of special characters
+                vSpecial.push_back(i);
+            }
+        }
+    }
+
+    int bytesWritten = 0;
+    if(timeout >= 0) {
+        Timer tmr;
+        tmr.start();
+        do {
+            int available = io->writeable();
+            if (available > 0) {
+                if(specialWritten < vSpecial.size()) {
+                    //Check if current index is at a special character
+                    if(bytesWritten == vSpecial[specialWritten]) {
+                        if(available < 2) {
+                            //Requires at least two bytes of space
+                            wait(0.05);
+                            continue;
+                        }
+                        //Ready to write special character
+                        if(io->write(DLE)) {
+                            specialWritten++;
+                            if(io->write(data[bytesWritten])) {
+                                bytesWritten++;
+                            }
+                        } else {
+                            //Unable to write escape character, try again next round
+                            wait(0.05);
+                        }
+                    } else {
+                        //We want to write all the way up to the next special character
+                        int relativeIndex = vSpecial[specialWritten] - bytesWritten;
+                        int size = MIN(available, relativeIndex);
+                        bytesWritten += io->write(&data[bytesWritten], size);
+                    }
+                } else {
+                    int size = MIN(available, length - bytesWritten);
+                    bytesWritten += io->write(&data[bytesWritten], size);
+                }
+            } else {
+                wait(0.05);
+            }
+        } while (tmr.read_ms() <= timeout && bytesWritten < length);
+    } else {
+        for(int i = 0; i < vSpecial.size(); i++) {
+            //Write up to the special character, then write the special character
+            int size = vSpecial[i] - bytesWritten;
+            int currentWritten = io->write(&data[bytesWritten], size);
+            bytesWritten += currentWritten;
+            if(currentWritten != size) {
+                //Failed to write up to the special character.
+                return bytesWritten;
+            }
+            if(io->write(DLE) && io->write(data[bytesWritten])) {
+                bytesWritten++;
+            } else {
+                //Failed to write the special character.
+                return bytesWritten;
+            }
+        }
+
+        bytesWritten = io->write(&data[bytesWritten], length - bytesWritten);
+    }
+
+    return bytesWritten;
+}
+
+unsigned int Cellular::readable()
+{
+    if(io == NULL) {
+        printf("[WARNING] MTSBufferedIO not set\r\n");
+        return 0;
+    }
+    if(!socketOpened && !io->readable()) {
+        printf("[WARNING] Socket is not open\r\n");
+        return 0;
+    }
+    return io->readable();
+}
+
+unsigned int Cellular::writeable()
+{
+    if(io == NULL) {
+        printf("[WARNING] MTSBufferedIO not set\r\n");
+        return 0;
+    }
+    if(!socketOpened) {
+        printf("[WARNING] Socket is not open\r\n");
+        return 0;
+    }
+
+    return io->writeable();
+}
+
+void Cellular::reset()
+{
+    disconnect();
+    Code code = sendBasicCommand("AT#RESET=0", 10000);
+    if(code != SUCCESS) {
+        printf("[ERROR] Socket Modem did not accept RESET command\n\r");
+    } else {
+        printf("[WARNING] Socket Modem is resetting, allow 30 seconds for it to come back\n\r");
+    }
+}
+
+std::string Cellular::getDeviceIP()
+{
+    return local_address;
+}
+
+Code Cellular::test()
+{
+    Code code;
+    int i = 0;
+    while (sendBasicCommand("AT", 1000) != SUCCESS) {
+        i++;
+        if (i >= 30) {
+            printf("[ERROR] Could not talk to radio after 30 tries\r\n");
+            i = 0;
+        }
+        wait(1);
+    }
+
+    return SUCCESS;
+}
+
+Code Cellular::echo(bool state)
+{
+    Code code;
+    if (state) {
+        code = sendBasicCommand("ATE0", 1000);
+        echoMode = (code == SUCCESS) ? false : echoMode;
+    } else {
+        code = sendBasicCommand("ATE1", 1000);
+        echoMode = (code == SUCCESS) ? true : echoMode;
+    }
+    return code;
+}
+
+int Cellular::getSignalStrength()
+{
+    string response = sendCommand("AT+CSQ", 1000);
+    if (response.find("OK") == string::npos) {
+        return -1;
+    }
+    int start = response.find(':');
+    int stop = response.find(',', start);
+    string signal = response.substr(start + 2, stop - start - 2);
+    int value;
+    sscanf(signal.c_str(), "%d", &value);
+    return value;
+}
+
+Cellular::Registration Cellular::getRegistration()
+{
+    string response = sendCommand("AT+CREG?", 5000);
+    if (response.find("OK") == string::npos) {
+        return UNKNOWN;
+    }
+    int start = response.find(',');
+    int stop = response.find(' ', start);
+    string regStat = response.substr(start + 1, stop - start - 1);
+    int value;
+    sscanf(regStat.c_str(), "%d", &value);
+    switch (value) {
+        case 0:
+            return NOT_REGISTERED;
+        case 1:
+            return REGISTERED;
+        case 2:
+            return SEARCHING;
+        case 3:
+            return DENIED;
+        case 4:
+            return UNKNOWN;
+        case 5:
+            return ROAMING;
+    }
+    return UNKNOWN;
+}
+
+Code Cellular::setApn(const std::string& apn)
+{
+    Code code = sendBasicCommand("AT#APNSERV=\"" + apn + "\"", 1000);
+    if (code != SUCCESS) {
+        return code;
+    }
+    this->apn = apn;
+    return code;
+}
+
+
+Code Cellular::setDns(const std::string& primary, const std::string& secondary)
+{
+    return sendBasicCommand("AT#DNS=1," + primary + "," + secondary, 1000);
+}
+
+bool Cellular::ping(const std::string& address)
+{
+    char buffer[256] = {0};
+    Code code;
+
+    code = sendBasicCommand("AT#PINGREMOTE=\"" + address + "\"", 1000);
+    if (code != SUCCESS) {
+        return false;
+    }
+
+    sprintf(buffer, "AT#PINGNUM=%d", 1);
+    code = sendBasicCommand(buffer , 1000);
+    if (code != SUCCESS) {
+        return false;
+    }
+
+    sprintf(buffer, "AT#PINGDELAY=%d", PINGDELAY);
+    code = sendBasicCommand(buffer , 1000);
+    if (code != SUCCESS) {
+        return false;
+    }
+
+    std::string response;
+    for (int i = 0; i < PINGNUM; i++) {
+        response = sendCommand("AT#PING", PINGDELAY * 1000);
+        if (response.find("alive") != std::string::npos) {
+            return true;
+        }
+    }
+    return false;
+}
+
+Code Cellular::setSocketCloseable(bool enabled)
+{
+    if(socketCloseable == enabled) {
+        return SUCCESS;
+    }
+
+    if(socketOpened) {
+        printf("[ERROR] socket is already opened. Can not set closeable\r\n");
+        return ERROR;
+    }
+
+    socketCloseable = enabled;
+
+    return SUCCESS;
+}
+
+Code Cellular::sendSMS(const Sms& sms)
+{
+    return sendSMS(sms.phoneNumber, sms.message);
+}
+
+Code Cellular::sendSMS(const std::string& phoneNumber, const std::string& message)
+{
+    Code code = sendBasicCommand("AT+CMGF=1", 1000);
+    if (code != SUCCESS) {
+        return code;
+    }
+    string cmd = "AT+CMGS=\"+";
+    cmd.append(phoneNumber);
+    cmd.append("\"");
+    string response1 = sendCommand(cmd, 1000);
+    if (response1.find('>') == string::npos) {
+        return NO_RESPONSE;
+    }
+    wait(.2);
+    string  response2 = sendCommand(message, 4000, CTRL_Z);
+    printf("SMS Response: %s\r\n", response2.c_str());
+    if (response2.find("+CMGS:") == string::npos) {
+        return FAILURE;
+    }
+    return SUCCESS;
+}
+
+std::vector<Cellular::Sms> Cellular::getReceivedSms()
+{
+    int smsNumber = 0;
+    std::vector<Sms> vSms;
+    std::string received = sendCommand("AT+CMGL=\"ALL\"", 4000);
+    size_t pos = received.find("+CMGL: ");
+
+    while (pos != std::string::npos) {
+        Cellular::Sms sms;
+        std::string line(Text::getLine(received, pos, pos));
+        //printf("[DEBUG] Top of SMS Parse Loop. LINE[%s]\r\n", line.c_str());
+        if(line.find("+CMGL: ") == std::string::npos) {
+            continue;
+        }
+
+        //Start of SMS message
+        std::vector<std::string> vSmsParts = Text::split(line, ',');
+        if(vSmsParts.size() != 6) {
+            printf("[WARNING] Expected 6 commas. SMS[%d] DATA[%s]. Continuing ...\r\n", smsNumber, line.c_str());
+            continue;
+        }
+
+        sms.phoneNumber = vSmsParts[2];
+        sms.timestamp = vSmsParts[4] + ", " + vSmsParts[5];
+
+        if(pos == std::string::npos) {
+            printf("[WARNING] Expected SMS body. SMS[%d]. Leaving ...\r\n", smsNumber);
+            break;
+        }
+        //Check for the start of the next SMS message
+        size_t bodyEnd = received.find("\r\n+CMGL: ", pos);
+        if(bodyEnd == std::string::npos) {
+            //printf("[DEBUG] Parsing Last SMS. SMS[%d]\r\n", smsNumber);
+            //This must be the last SMS message
+            bodyEnd = received.find("\r\n\r\nOK", pos);
+        }
+
+        //Safety check that we found the boundary of this current SMS message
+        if(bodyEnd != std::string::npos) {
+            sms.message = received.substr(pos, bodyEnd - pos);
+        } else {
+            sms.message = received.substr(pos);
+            printf("[WARNING] Expected to find end of SMS list. SMS[%d] DATA[%s].\r\n", smsNumber, sms.message.c_str());
+        }
+        vSms.push_back(sms);
+        pos = bodyEnd;
+        //printf("[DEBUG] Parsed SMS[%d].  Starting Next at position [%d]\r\n", smsNumber, pos);
+        smsNumber++;
+    }
+    printf("Received %d SMS\r\n", smsNumber);
+    return vSms;
+}
+
+Code Cellular::deleteOnlyReceivedReadSms()
+{
+    return sendBasicCommand("AT+CMGD=1,1", 1000);
+}
+
+Code Cellular::deleteAllReceivedSms()
+{
+    return sendBasicCommand("AT+CMGD=1,4", 1000);
+}
+
+Code Cellular::sendBasicCommand(const std::string& command, unsigned int timeoutMillis, char esc)
+{
+    if(socketOpened) {
+        printf("[ERROR] socket is open. Can not send AT commands\r\n");
+        return ERROR;
+    }
+
+    string response = sendCommand(command, timeoutMillis, esc);
+    if (response.size() == 0) {
+        return NO_RESPONSE;
+    } else if (response.find("OK") != string::npos) {
+        return SUCCESS;
+    } else if (response.find("ERROR") != string::npos) {
+        return ERROR;
+    } else {
+        return FAILURE;
+    }
+}
+
+string Cellular::sendCommand(const std::string& command, unsigned int timeoutMillis, char esc)
+{
+    if(io == NULL) {
+        printf("[ERROR] MTSBufferedIO not set\r\n");
+        return "";
+    }
+    if(socketOpened) {
+        printf("[ERROR] socket is open. Can not send AT commands\r\n");
+        return "";
+    }
+
+    io->rxClear();
+    io->txClear();
+    std::string result;
+
+    //Attempt to write command
+    if(io->write(command.data(), command.size(), timeoutMillis) != command.size()) {
+        //Failed to write command
+        if (command != "AT" && command != "at") {
+            printf("[ERROR] failed to send command to radio within %d milliseconds\r\n", timeoutMillis);
+        }
+        return "";
+    }
+
+    //Send Escape Character
+    if (esc != 0x00) {
+        if(io->write(esc, timeoutMillis) != 1) {
+            if (command != "AT" && command != "at") {
+                printf("[ERROR] failed to send character '%c' (0x%02X) to radio within %d milliseconds\r\n", esc, esc, timeoutMillis);
+            }
+            return "";
+        }
+    }
+
+    int timer = 0;
+    size_t previous = 0;
+    char tmp[256];
+    tmp[255] = 0;
+    bool started = !echoMode;
+    bool done = false;
+    do {
+        wait(0.1);
+        timer += 100;
+
+        previous = result.size();
+        //Make a non-blocking read call by passing timeout of zero
+        int size = io->read(tmp,255,0);    //1 less than allocated (timeout is instant)
+        if(size > 0) {
+            result.append(tmp, size);
+        }
+        if(!started) {
+            //In Echo Mode (Command will have echo'd + 2 characters for \r\n)
+            if(result.size() > command.size() + 2) {
+                started = true;
+            }
+        } else {
+            done =  (result.size() == previous);
+        }
+        if(timer >= timeoutMillis) {
+            if (command != "AT" && command != "at") {
+                printf("[WARNING] sendCommand [%s] timed out after %d milliseconds\r\n", command.c_str(), timeoutMillis);
+            }
+            done = true;
+        }
+    } while (!done);
+
+    return result;
+}
+
+std::string Cellular::getRegistrationNames(Registration registration)
+{
+    switch(registration) {
+        case NOT_REGISTERED:
+            return "NOT_REGISTERED";
+        case REGISTERED:
+            return "REGISTERED";
+        case SEARCHING:
+            return "SEARCHING";
+        case DENIED:
+            return "DENIED";
+        case UNKNOWN:
+            return "UNKNOWN";
+        case ROAMING:
+            return "ROAMING";
+        default:
+            return "UNKNOWN ENUM";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/cellular/Cellular.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,505 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 CELLULAR_H
+#define CELLULAR_H
+
+#include "IPStack.h"
+#include "MTSBufferedIO.h"
+#include "mbed.h"
+#include <string>
+#include <vector>
+
+namespace mts
+{
+
+/** This is a class for communicating with a Multi-Tech Systems SocketModem iCell. The
+* SocketModem iCell is a family of carrier certified embedded cellular radio modules with
+* a common hardware footprint and AT command set for built in IP-stack functionality.
+* This class supports three main types of cellular radio interactions including:
+* configuration and status AT command processing, SMS processing, and TCP Socket
+* data connections. It should be noted that the radio can not process commands or
+* SMS messages while having an open data connection at the same time. The concurrent
+* capability may be added in a future release. This class also inherits from IPStack
+* providing a common set of commands for communication devices that have an onboard
+* IP Stack. It is also integrated with the standard mbed Sockets package and can therefore
+* be used seamlessly with clients and services built on top of this interface already within
+* the mbed library.
+*
+* All of the following examples use the Pin Names for the Freedom KL46Z board coupled with
+* the SocketModem Shield Arduino compatible board. Please chage Pin Names accordingly to
+* match your hardware configuration. It also assumes the use of RTS/CTS hardware handshaking
+* using GPIOs. To disable this you will need to change settings on the radio module and
+* and use the MTSSerial class instead of MTSSerialFlowControl. The default baud rate for the
+* cellular radio is 115200 bps.
+*
+* The following set of example code demonstrates how to send and receive configuration and
+* status AT commands with the radio, create a data connection and test it:
+* @code
+* #include "mbed.h"
+* #include "Cellular.h"
+* #include "MTSSerialFlowControl.h"
+*
+* using namespace mts;
+*
+* main() {
+*   //Setup serial interface to radio
+*   MTSSerialFlowControl* serial = new MTSSerialFlowControl(PTD3, PTD2, PTA12, PTC8);
+*   serial->baud(115200);
+*
+*   //Setup Cellular class
+*   Cellular* cellular = Cellular::getInstance();
+*   cellular->init(serial, PTA4, PTC9); //DCD and DTR pins for KL46Z
+*
+*   //Run status and configuration commands
+*   printf("\n\r////Start Status and Configuration Commands////\n\r");
+*   printf("Command Test: %s\n\r", getCodeNames(cellular->test()).c_str());
+*   printf("Signal Strength: %d\n\r", cellular->getSignalStrength());
+*   printf("Registration State: %s\n\r", Cellular::getRegistrationNames(cellular->getRegistration()).c_str());
+*   printf("Send Basic Command (AT): %s\n\r", getCodeNames(cellular->sendBasicCommand("AT", 1000)).c_str());
+*   printf("Send Command (AT+CSQ): %s\n\r", cellular->sendCommand("AT+CSQ", 1000).c_str());
+*
+*   //Start Test
+*   printf("\n\r////Start Network Connectivity Test////\n\r");
+*   printf("Set APN: %s\n\r", getCodeNames(cellular->setApn("wap.cingular")).c_str()); //Use APN from service provider!!!
+*
+*   //Setup a data connection
+*   printf("Attempting to Connect, this may take some time...\n\r");
+*   while (!cellular->connect()) {
+*       printf("Failed to connect... Trying again.\n\r");
+*       wait(1);
+*   }
+*   printf("Connected to the Network!\n\r");
+*
+*   //Try pinging default server "8.8.8.8"
+*   printf("Ping Valid: %s\n\r", cellular->ping() ? "true" : "false");
+*
+*   printf("End Program\n\r");
+* }
+* @endcode
+*
+* The following set of example code demonstrates how process SMS messages:
+* @code
+* #include "mbed.h"
+* #include "Cellular.h"
+* #include "MTSSerialFlowControl.h"
+*
+* using namespace mts;
+*
+* main() {
+*   //Setup serial interface to radio
+*   MTSSerialFlowControl* serial = new MTSSerialFlowControl(PTD3, PTD2, PTA12, PTC8);
+*   serial->baud(115200);
+*
+*   //Setup Cellular class
+*   Cellular* cellular = Cellular::getInstance();
+*   cellular->init(serial, PTA4, PTC9); //DCD and DTR pins for KL46Z
+*
+*   //Start test
+*   printf("AT Test: %s\n\r", getCodeNames(cellular->test()).c_str());
+*
+*   //Waiting for network registration
+*   printf("Checking Network Registration, this may take some time...\n\r");
+*   while (cellular->getRegistration() != Cellular::REGISTERED) {
+*       printf("Still waiting... Checking again.\n\r");
+*       wait(1);
+*   }
+*   printf("Connected to the Network!\n\r");
+*
+*   //Send SMS Message
+*   Code code;
+*   std::string sMsg("Hello from Multi-Tech MBED!");
+*   std::string sPhoneNum("16128675309"); //Put your phone number here or leave Jenny's...
+*
+*   printf("Sending message [%s] to [%s]\r\n", sMsg.c_str(), sPhoneNum.c_str());
+*   code = cellular->sendSMS(sPhoneNum, sMsg);
+*
+*   if(code != SUCCESS) {
+*       printf("Error during SMS send [%s]\r\n", getCodeNames(code).c_str());
+*   } else {
+*       printf("Success!\r\n");
+*   }
+*
+*   //Try and receive SMS messages
+*   //To determine your radio's phone number send yourself an SMS and check the received #
+*   printf("Checking Received Messages\r\n");
+*   std::vector<Cellular::Sms> vSms = cellular->getReceivedSms();
+*   printf("\r\n");
+*   for(unsigned int i = 0; i < vSms.size(); i++) {
+*       printf("[%d][%s][%s][%s]\r\n", i, vSms[i].timestamp.c_str(),
+*               vSms[i].phoneNumber.c_str(), vSms[i].message.c_str());
+*   }
+*   printf("End Program\n\r");
+* }
+* @endcode
+*
+* The following set of example code demonstrates how to setup and use a TCP socket connection
+* using the native commands from this class:
+* @code
+* #include "mbed.h"
+* #include "Cellular.h"
+* #include "MTSSerialFlowControl.h"
+*
+* using namespace mts;
+*
+* main() {
+*   //Define connection parameters
+*   Code code;
+*   const int TEST_PORT = 5798;
+*   const std::string TEST_SERVER("204.26.122.96");
+*
+*   //Setup serial interface to radio
+*   MTSSerialFlowControl* serial = new MTSSerialFlowControl(PTD3, PTD2, PTA12, PTC8);
+*   serial->baud(115200);
+*
+*   //Setup Cellular class
+*   Cellular* cellular = Cellular::getInstance();
+*   cellular->init(serial, PTA4, PTC9); //DCD and DTR pins for KL46Z
+*
+*   //Start test
+*   printf("AT Test: %s\n\r", getCodeNames(cellular->test()).c_str());
+*
+*   printf("Setting APN\r\n");
+*   code = cellular->setApn("wap.cingular"); // Use from your service provider!
+*   if(code == SUCCESS) {
+*       printf("Success!\r\n");
+*   } else {
+*       printf("Error during APN setup [%s]\r\n", getCodeNames(code).c_str());
+*   }
+*
+*   //Setup a data connection
+*   printf("Attempting to Connect, this may take some time...\n\r");
+*   while (!cellular->connect()) {
+*       printf("Failed to connect... Trying again.\n\r");
+*       wait(1);
+*   }
+*   printf("Connected to the Network!\n\r");
+*
+*   printf("Opening a TCP Socket...\r\n");
+*   if(cellular->open(TEST_SERVER, TEST_PORT, IPStack::TCP)) {
+*       printf("Success!\r\n");
+*   } else {
+*       printf("Error during TCP socket open [%s:%d]\r\n", TEST_SERVER.c_str(), TEST_PORT);
+*   }
+*
+*   char data[] = "My Test Echo Message";
+*   int size = sizeof(data);
+*   printf("WRITE: [%d] [%s]\r\n", size, data);
+*   int bytesWritten = cellular->write(data, size, 10000);
+*   if(bytesWritten == size) {
+*       printf("Successfully wrote message!\r\n");
+*   } else {
+*       printf("Failed to write message!\r\n");
+*   }
+*
+*   printf("Waiting 5 seconds\r\n");
+*   wait(5);
+*
+*   printf("Reading from socket for 10 seconds\r\n");
+*   char response[size];
+*   int bytesRead = cellular->read(response, size, 10000);
+*   response[size - 1] = '\0';
+*   printf("READ: [%d] [%s]\r\n", bytesRead, response);
+*
+*   //Check to see if echo was successful
+*   if (strcmp(data, response) == 0) {
+*       printf("Echo Successful!\n\r");
+*   } else {
+*       printf("Echo failed!\n\r");
+*   }
+*
+*   //Cleaning up the connection
+*   printf("Closing socket: %s\r\n", cellular->close() ? "Success" : "Failure");
+*   printf("Disconnecting...\r\n");
+*   cellular->disconnect();
+*   printf("End Program\n\r");
+* }
+* @endcode
+*/
+
+class Cellular : virtual mts::IPStack
+{
+public:
+
+    /// An enumeration of radio registration states with a cell tower.
+    enum Registration {
+        NOT_REGISTERED, REGISTERED, SEARCHING, DENIED, UNKNOWN, ROAMING
+    };
+
+    /** This structure contains the data for an SMS message.
+    */
+    struct Sms {
+        /// Message Phone Number
+        std::string phoneNumber;
+        /// Message Body
+        std::string message;
+        /// Message Timestamp
+        std::string timestamp;
+    };
+
+    /** Destructs a Cellular object and frees all related resources.
+    */
+    ~Cellular();
+
+    /** This static function is used to create or get a reference to a
+    * Cellular object. Cellular uses the singleton pattern, which means
+    * that you can only have one existing at a time. The first time you
+    * call getInstance this method creates a new uninitialized Cellular
+    * object and returns it. All future calls to this method will return
+    * a reference to the instance created during the first call. Note that
+    * you must call init on the returned instance before mnaking any other
+    * calls. If using this class's bindings to any of the Socket package
+    * classes like TCPSocketConnection, you must call this method and the
+    * init method on the returned object first, before even creating the
+    * other objects.
+    *
+    * @returns a reference to the single Cellular obect that has been created.
+    */
+    static Cellular* getInstance();
+
+    /** This method initializes the object with the underlying radio
+    * interface to use. Note that this function MUST be called before
+    * any other calls will function correctly on a Cellular object. Also
+    * note that MTSBufferedIO is abstract, so you must use one of
+    * its inherited classes like MTSSerial or MTSSerialFlowControl.
+    *
+    * @param io the buffered io interface that is attached to the cellular
+    * radio.
+    * @param DCD this is the dcd signal from the radio. If attached the
+    * the pin must be passed in here for this class to operate correctly.
+    * The default is not connected.
+    * @param DTR this is the dtr signal from the radio. If attached the
+    * the pin must be passed in here for this class to operate correctly.
+    * The default is not connected.
+    * @returns true if the init was successful, otherwise false.
+    */
+    bool init(MTSBufferedIO* io, PinName DCD = NC, PinName DTR = NC);
+
+    // Radio link related commands
+    /** This method establishes a data connection on the cellular radio.
+    * Note that before calling you must have an activated radio and if
+    * using a SIM card set the APN using the setApn method. The APN can
+    * be obtained from your cellular service provider.
+    *
+    * @returns true if the connection was successfully established, otherwise
+    * false on an error.
+    */
+    virtual bool connect();
+
+    /** This method is used to stop a previously established cellular data connection.
+    */
+    virtual void disconnect();
+
+    /** This method is used to check if the radio currently has a data connection
+    * established.
+    *
+    * @returns true if a data connection exists, otherwise false.
+    */
+    virtual bool isConnected();
+
+    // TCP and UDP Socket related commands
+    // For behavior of the following methods refer to IPStack.h documentation
+    virtual bool bind(unsigned int port);
+    virtual bool open(const std::string& address, unsigned int port, Mode mode);
+    virtual bool isOpen();
+    virtual bool close();
+    virtual int read(char* data, int max, int timeout = -1);
+    virtual int write(const char* data, int length, int timeout = -1);
+    virtual unsigned int readable();
+    virtual unsigned int writeable();
+
+    //Other
+    /** A method to reset the Multi-Tech Socket Modem.  This command brings down the
+    * PPP link if it is up.  After this function is called, at least 30 seconds should
+    * be allowed for the cellular radio to come back up before any other Cellular
+    * functions are called.
+    */
+    virtual void reset();
+
+    //Cellular Radio Specific
+    /** A method for sending a generic AT command to the radio. Note that you cannot
+    * send commands and have a data connection at the same time.
+    *
+    * @param command the command to send to the radio without the escape character.
+    * @param timeoutMillis the time in millis to wait for a response before returning.
+    * @param esc escape character to add at the end of the command, defaults to
+    * carriage return (CR).  Does not append any character if esc == 0.
+    * @returns all data received from the radio after the command as a string.
+    */
+    std::string sendCommand(const std::string& command, unsigned int timeoutMillis, char esc = CR);
+
+    /** A method for sending a basic AT command to the radio. A basic AT command is
+    * one that simply has a response of either OK or ERROR without any other information.
+    * Note that you cannot send commands and have a data connection at the same time.
+    *
+    * @param command the command to send to the radio without the escape character.
+    * @param timeoutMillis the time in millis to wait for a response before returning.
+    * @param esc escape character to add at the end of the command, defaults to
+    * carriage return (CR).
+    * @returns the standard Code enumeration.
+    */
+    Code sendBasicCommand(const std::string& command, unsigned int timeoutMillis, char esc = CR);
+
+    /** This method is used to get the IP address of the device, which is determined
+    * via DHCP by the cellular carrier.
+    *
+    * @returns the devices IP address.
+    */
+    std::string getDeviceIP();
+    
+    /** A method for testing command access to the radio.  This method sends the
+    * command "AT" to the radio, which is a standard radio test to see if you
+    * have command access to the radio.  The function returns when it receives
+    * the expected response from the radio.
+    *
+    * @returns the standard AT Code enumeration.
+    */
+    Code test();
+
+    /** A method for configuring command ehco capability on the radio. This command
+    * sets whether sent characters are echoed back from the radio, in which case you
+    * will receive back every command you send.
+    *
+    * @param state if true echo will be turned off, otherwise it will be turned on.
+    * @returns the standard AT Code enumeration.
+    */
+    Code echo(bool state);
+
+    /** A method for getting the signal strength of the radio. This method allows you to
+    * get a value that maps to signal strength in dBm. Here 0-1 is Poor, 2-9 is Marginal,
+    * 10-14 is Ok, 15-19 is Good, and 20+ is Excellent.  If you get a result of 99 the
+    * signal strength is not known or not detectable.
+    *
+    * @returns an integer representing the signal strength.
+    */
+    int getSignalStrength();
+
+    /** This method is used to check the registration state of the radio with the cell tower.
+    * If not appropriatley registered with the tower you cannot make a cellular connection.
+    *
+    * @returns the registration state as an enumeration type.
+    */
+    Registration getRegistration();
+
+    /** This method is used to set the radios APN if using a SIM card. Note that the APN
+    * must be set correctly before you can make a data connection. The APN for your SIM
+    * can be obtained by contacting your cellular service provider.
+    *
+    * @param the APN as a string.
+    * @returns the standard AT Code enumeration.
+    */
+    Code setApn(const std::string& apn);
+
+    /** This method is used to set the DNS which enables the use of URLs instead
+    * of IP addresses when making a socket connection.
+    *
+    * @param the DNS server address as a string in form xxx.xxx.xxx.xxx.
+    * @returns the standard AT Code enumeration.
+    */
+    Code setDns(const std::string& primary, const std::string& secondary = "0.0.0.0");
+
+    /** This method is used test network connectivity by pinging a server.
+    *
+    * @param address the address of the server in format xxx.xxx.xxx.xxx.
+    * @returns true if the ping was successful, otherwise false.
+    */
+    bool ping(const std::string& address = "8.8.8.8");
+
+    /** This method can be used to trade socket functionality for performance.
+    * In order to enable a socket connection to be closed by the client side programtically,
+    * this class must process all read and write data on the socket to guard the special
+    * escape character used to close an open socket connection. It is recommened that you
+    * use the default of true unless the overhead of these operations is too significant.
+    *
+    * @param enabled set to true if you want the socket closeable, otherwise false. The default
+    * is true.
+    * @returns the standard AT Code enumeration.
+    */
+    Code setSocketCloseable(bool enabled = true);  //ETX closes socket (ETX and DLE in payload are escaped with DLE)
+
+    /** This method is used to send an SMS message. Note that you cannot send an
+    * SMS message and have a data connection open at the same time.
+    *
+    * @param phoneNumber the phone number to send the message to as a string.
+    * @param message the text message to be sent.
+    * @returns the standard AT Code enumeration.
+    */
+    Code sendSMS(const std::string& phoneNumber, const std::string& message);
+
+    /** This method is used to send an SMS message. Note that you cannot send an
+    * SMS message and have a data connection open at the same time.
+    *
+    * @param sms an Sms struct that contains all SMS transaction information.
+    * @returns the standard AT Code enumeration.
+    */
+    Code sendSMS(const Sms& sms);
+
+    /** This method retrieves all of the SMS messages currently available for
+    * this phone number.
+    *
+    * @returns a vector of existing SMS messages each as an Sms struct.
+    */
+    std::vector<Cellular::Sms> getReceivedSms();
+
+    /** This method can be used to remove/delete all received SMS messages
+    * even if they have never been retrieved or read.
+    *
+    * @returns the standard AT Code enumeration.
+    */
+    Code deleteAllReceivedSms();
+
+    /** This method can be used to remove/delete all received SMS messages
+    * that have been retrieved by the user through the getReceivedSms method.
+    * Messages that have not been retrieved yet will be unaffected.
+    *
+    * @returns the standard AT Code enumeration.
+    */
+    Code deleteOnlyReceivedReadSms();
+
+    /** A static method for getting a string representation for the Registration
+    * enumeration.
+    *
+    * @param code a Registration enumeration.
+    * @returns the enumeration name as a string.
+    */
+    static std::string getRegistrationNames(Registration registration);
+
+private:
+    static Cellular* instance; //Static pointer to the single Cellular object.
+
+    MTSBufferedIO* io; //IO interface obect that the radio is accessed through.
+    bool echoMode; //Specifies if the echo mode is currently enabled.
+
+    bool pppConnected; //Specifies if a PPP session is currently connected.
+    std::string apn; //A string that holds the APN for the radio.
+
+    Mode mode; //The current socket Mode.
+    bool socketOpened; //Specifies if a Socket is presently opened.
+    bool socketCloseable; //Specifies is a Socket can be closed.
+    unsigned int local_port; //Holds the local port for socket connections.
+    std::string local_address; //Holds the local address for socket connections.
+    unsigned int host_port; //Holds the remote port for socket connections.
+    std::string host_address; //Holds the remote address for socket connections.
+    DigitalIn* dcd; //Maps to the radios dcd signal
+    DigitalOut* dtr; //Maps to the radios dtr signal
+
+    Cellular(); //Private constructor, use the getInstance() method.
+    Cellular(MTSBufferedIO* io); //Private constructor, use the getInstance() method.
+};
+
+}
+
+#endif /* CELLULAR_H */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/include_me.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,22 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "Cellular.h"
+#include "Wifi.h"
+#include "MTSSerial.h"
+#include "MTSSerialFlowControl.h"
+#include "TCPSocketConnection.h"
+#include "Transport.h"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/io/IPStack.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,143 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 IPSTACK_H
+#define IPSTACK_H
+
+#include <string>
+
+namespace mts {
+    
+/** This class is a pure virtual class that should be inherited from when implementing
+* a communications device with an onboard IP stack.  Examples of this would be a Wi-Fi
+* or Cellular radio with a built in IP stack. Typically the IP functionality in these
+* devices is available through an AT or a similiar command interface. The inheriting
+* class should map the device commands and functionality to the pure virtual methods provided
+* here. There should also be at least one or more calls to setup the communication link
+* specific paramters as an init method for example.  This would do things like configure
+* the APN in a cellular radio or set the ssid for a WiFi device, which cannot be accounted
+* for in an abstract class like this one.
+*/
+class IPStack
+{
+public:
+    /// An enumeration for selecting the Socket Mode of TCP or UDP.
+    enum Mode {
+        TCP, UDP
+    };
+
+    /** This method is used to connect the IP layer and below for the interface. Required
+    * configurations and settings should be done in other calls or an init function.
+    *
+    * @returns true if the connection was successfully established, otherwise false.
+    */
+    virtual bool connect() = 0;
+
+    /** This method is used to disconnect the IP layer and below of the interface. This includes
+    * any cleanup required before another connection can be made.
+    */
+    virtual void disconnect() = 0;
+
+    /** This method is used to check if the IP layer of the interface is currently connected.
+    *
+    * @returns true if currently connected, otherwise false.
+    */
+    virtual bool isConnected() = 0;
+
+    /** This method is used to set the local port for the UDP or TCP socket connection.
+    * The connection can be made using the open method.
+    *
+    * @param port the local port of the socket as an int.
+    */
+    virtual bool bind(unsigned int port) = 0;
+
+    /** This method is used to open a socket connection with the given parameters.
+    * This socket connection is established using the devices built in IP stack.
+    * Currently TCP is the only supported mode.
+    *
+    * @param address is the address you want to connect to in the form of xxx.xxx.xxx.xxx
+    * or a URL. If using a URL make sure the device supports DNS and is properly configured
+    * for that mode.
+    * @param port the remote port you want to connect to.
+    * @param mode an enum that specifies whether this socket connection is type TCP or UDP.
+    * Currently only TCP is supported.
+    * @returns true if the connection was successfully opened, otherwise false.
+    */
+    virtual bool open(const std::string& address, unsigned int port, Mode mode) = 0;
+
+    /** This method is used to determine if a socket connection is currently open.
+    *
+    * @returns true if the socket is currently open, otherwise false.
+    */
+    virtual bool isOpen() = 0;
+
+    /** This method is used to close a socket connection that is currently open.
+    *
+    * @returns true if successfully closed, otherwise returns false on an error.
+    */
+    virtual bool close() = 0;
+
+    /** This method is used to read data off of a socket, assuming a valid socket
+    * connection is already open.
+    *
+    * @param data a pointer to the data buffer that will be filled with the read data.
+    * @param max the maximum number of bytes to attempt to read, typically the same as
+    * the size of the passed in data buffer.
+    * @param timeout the amount of time in milliseconds to wait in trying to read the max
+    * number of bytes. If set to -1 the call blocks until it receives the max number of bytes
+    * or encounters and error.
+    * @returns the number of bytes read and stored in the passed in data buffer. Returns
+    * -1 if there was an error in reading.
+    */
+    virtual int read(char* data, int max, int timeout = -1) = 0;
+
+    /** This method is used to write data to a socket, assuming a valid socket
+    * connection is already open.
+    *
+    * @param data a pointer to the data buffer that will be written to the socket.
+    * @param length the size of the data buffer to be written.
+    * @param timeout the amount of time in milliseconds to wait in trying to write the entire
+    * number of bytes. If set to -1 the call blocks until it writes all of the bytes or 
+    * encounters and error.
+    * @returns the number of bytes written to the socket's write buffer. Returns
+    * -1 if there was an error in writing.
+    */
+    virtual int write(const char* data, int length, int timeout = -1) = 0;
+
+
+    /** This method is used to get the number of bytes available to read off the
+    * socket.
+    *
+    * @returns the number of bytes available, 0 if there are no bytes to read.
+    */
+    virtual unsigned int readable() = 0;
+
+    /** This method is used to get the space available to write bytes to the socket.
+    *
+    * @returns the number of bytes that can be written, 0 if unable to write.
+    */
+    virtual unsigned int writeable() = 0;
+
+    /** This method is used to reset the device that provides the communications
+    * capability. Note that you may have to wait some time after reset before the
+    * device can be used.
+    */
+    virtual void reset() = 0;
+};
+
+}
+
+#endif /* IPSTACK_H */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/io/MTSBufferedIO.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,145 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "MTSBufferedIO.h"
+
+using namespace mts;
+
+MTSBufferedIO::MTSBufferedIO(int txBufferSize, int rxBufferSize)
+: txBuffer(txBufferSize)
+, rxBuffer(rxBufferSize)
+{
+
+}
+
+MTSBufferedIO::~MTSBufferedIO()
+{
+
+}
+
+int MTSBufferedIO::write(const char* data, int length, unsigned int timeoutMillis) 
+{
+    //Writes until empty or timeout is reached (different implementation planned once tx isr is working)
+    int bytesWritten = 0;
+    Timer tmr;
+    tmr.start();
+    length = MAX(0,length);
+    do {
+        int bytesWrittenSwBuffer = txBuffer.write(&data[bytesWritten], length - bytesWritten);
+        if(bytesWrittenSwBuffer > 0) {
+            handleWrite();
+            int bytesRemainingSwBuffer = txBuffer.size();
+            txBuffer.clear();
+            bytesWritten += (bytesWrittenSwBuffer - bytesRemainingSwBuffer);
+        }
+    } while(tmr.read_ms() <= timeoutMillis && bytesWritten < length);
+    return bytesWritten;
+}
+
+int MTSBufferedIO::write(const char* data, int length)
+{   
+    //Blocks until all bytes are written (different implementation planned once tx isr is working)
+    int bytesWritten = 0;
+    length = MAX(0,length);
+    do {
+        int bytesWrittenSwBuffer = txBuffer.write(&data[bytesWritten], length - bytesWritten);
+        handleWrite();
+        int bytesRemainingSwBuffer = txBuffer.size();
+        txBuffer.clear();
+        bytesWritten += bytesWrittenSwBuffer - bytesRemainingSwBuffer;
+    } while(bytesWritten < length);
+    return length;
+}
+
+int MTSBufferedIO::write(char data, unsigned int timeoutMillis) 
+{
+    return write(&data, 1, timeoutMillis);
+}
+
+int MTSBufferedIO::write(char data)
+{
+    return write(&data, 1);
+}
+
+int MTSBufferedIO::writeable() {
+    return txBuffer.remaining();   
+}
+
+int MTSBufferedIO::read(char* data, int length, unsigned int timeoutMillis) 
+{
+    int bytesRead = 0;
+    Timer tmr;
+    tmr.start();
+    length = MAX(0,length);
+    do {
+        bytesRead += rxBuffer.read(&data[bytesRead], length - bytesRead);
+    } while(tmr.read_ms() <= timeoutMillis && bytesRead < length);
+    return bytesRead;
+}
+
+int MTSBufferedIO::read(char* data, int length)
+{
+    int bytesRead = 0;
+    length = MAX(0,length);
+    while(bytesRead < length) {
+        bytesRead += rxBuffer.read(&data[bytesRead], length - bytesRead);
+    }
+    return length;
+}
+
+int MTSBufferedIO::read(char& data, unsigned int timeoutMillis) 
+{
+    return read(&data, 1, timeoutMillis);
+}
+
+int MTSBufferedIO::read(char& data)
+{
+    return rxBuffer.read(&data, 1);
+}
+
+int MTSBufferedIO::readable() {
+    return rxBuffer.size();   
+}
+
+bool MTSBufferedIO::txEmpty()
+{
+    return txBuffer.isEmpty();
+}
+
+bool MTSBufferedIO::rxEmpty()
+{
+    return rxBuffer.isEmpty();
+}
+
+bool MTSBufferedIO::txFull()
+{
+    return txBuffer.isFull();
+}
+
+bool MTSBufferedIO::rxFull()
+{
+    return rxBuffer.isFull();
+}
+
+void MTSBufferedIO::txClear()
+{
+    txBuffer.clear();
+}
+
+void MTSBufferedIO::rxClear()
+{
+    rxBuffer.clear();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/io/MTSBufferedIO.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,193 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 MTSBUFFEREDIO_H
+#define MTSBUFFEREDIO_H
+
+#include "mbed.h"
+#include "MTSCircularBuffer.h"
+
+namespace mts {
+
+/** This is an abstract class for lightweight buffered io to an underlying
+* data interface. Specifically the inheriting class will need to override
+* both the handleRead and handleWrite methods which transfer data between
+* the class's internal read and write buffers and the physical communications
+* link or its HW buffers.
+*/
+
+class MTSBufferedIO
+{
+public:
+    /** Creates a new BufferedIO object with the passed in static buffer sizes.
+    * Note that because this class is abstract you cannot construct it directly.
+    * Instead, please construct one of its derived classes like MTSSerial or
+    * MTSSerialFlowControl.
+    *
+    * @param txBufferSize the size of the Tx or write buffer in bytes. The default is
+    * 128 bytes.
+    * @param rxBufferSize the size of the Rx or read buffer in bytes. The default is
+    * 128 bytes.
+    */
+    MTSBufferedIO(int txBufferSize = 128, int rxBufferSize = 128);
+
+    /** Destructs an MTSBufferedIO object and frees all related resources, including
+    * internal buffers.
+    */
+    ~MTSBufferedIO();
+
+    /** This method enables bulk writes to the Tx or write buffer. If more data
+    * is requested to be written then space available the method writes
+    * as much data as possible within the timeout period and returns the actual amount written.
+    *
+    * @param data the byte array to be written.
+    * @param length the length of data to be written from the data parameter.
+    * @timeoutMillis amount of time in milliseconds to complete operation.
+    * @returns the number of bytes written to the buffer, which is 0 if
+    * the buffer is full.
+    */
+    int write(const char* data, int length, unsigned int timeoutMillis);
+    
+    /** This method enables bulk writes to the Tx or write buffer. If more data
+    * is requested to be written then space available the method writes
+    * as much data as possible and returns the actual amount written.
+    *
+    * @param data the byte array to be written.
+    * @param length the length of data to be written from the data parameter.
+    * @returns the number of bytes written to the buffer, which is 0 if
+    * the buffer is full.
+    */
+    int write(const char* data, int length);
+
+    /** This method attempts to write a single byte to the tx buffer
+    * within the timeout period.
+    *
+    * @param data the byte to be written as a char.
+    * @timeoutMillis amount of time in milliseconds to complete operation.
+    * @returns 1 if the byte was written or 0 if the buffer was full.
+    */
+    int write(char data, unsigned int timeoutMillis);
+    
+    /** This method writes a signle byte as a char to the Tx or write buffer.
+    *
+    * @param data the byte to be written as a char.
+    * @returns 1 if the byte was written or 0 if the buffer was full.
+    */
+    int write(char data);
+
+    /** This method is used to get the space available to write bytes to the Tx buffer.
+    *
+    * @returns the number of bytes that can be written, 0 if the buffer is full.
+    */
+    int writeable();
+
+    /** This method enables bulk reads from the Rx or read buffer.  If more data is
+    * requested then available it simply returns all remaining data within the
+    * buffer.
+    *
+    * @param data the buffer where data read will be added to.
+    * @param length the amount of data in bytes to be read into the buffer.
+    * @timeoutMillis amount of time to complete operation.
+    * @returns the total number of bytes that were read.
+    */
+    int read(char* data, int length, unsigned int timeoutMillis);
+    
+    /** This method enables bulk reads from the Rx or read buffer.  If more data is
+    * requested then available it simply returns all remaining data within the
+    * buffer.
+    *
+    * @param data the buffer where data read will be added to.
+    * @param length the amount of data in bytes to be read into the buffer.
+    * @returns the total number of bytes that were read.
+    */
+    int read(char* data, int length);
+
+    /** This method reads a single byte from the Rx or read buffer.
+    *
+    * @param data char where the read byte will be stored.
+    * @timeoutMillis amount of time to complete operation.
+    * @returns 1 if byte is read or 0 if no byte is available.
+    */
+    int read(char& data, unsigned int timeoutMillis);
+    
+    /** This method reads a single byte from the Rx or read buffer.
+    *
+    * @param data char where the read byte will be stored.
+    * @returns 1 if byte is read or 0 if no byte is available.
+    */
+    int read(char& data);
+
+    /** This method is used to get the number of bytes available to read from
+    * the Rx or read buffer.
+    *
+    * @returns the number of bytes available, 0 if there are no bytes to read.
+    */
+    int readable();
+
+    /** This method determines if the Tx or write buffer is empty.
+    *
+    * @returns true if empty, otherwise false.
+    */
+    bool txEmpty();
+
+    /** This method determines if the Rx or read buffer is empty.
+    *
+    * @returns true if empty, otherwise false.
+    */
+    bool rxEmpty();
+
+    /** This method determines if the Tx or write buffer is full.
+    *
+    * @returns true if full, otherwise false.
+    */
+    bool txFull();
+
+    /** This method determines if the Rx or read buffer is full.
+    *
+    * @returns true if full, otherwise false.
+    */
+    bool rxFull();
+
+    /** This method clears all the data from the internal Tx or write buffer.
+    */
+    virtual void txClear();
+
+    /** This method clears all the data from the internal Rx or read buffer.
+    */
+    virtual void rxClear();
+
+    /** This abstract method should be used by the deriving class to transfer
+    * data from the internal write buffer (txBuffer) to the physical interface.
+    * Note that this function is called everytime new data is written to the
+    * txBuffer though one of the write calls.
+    */
+    virtual void handleWrite() = 0;
+
+    /** This abstract method should be used by the deriving class to transfer
+    * data from the physical interface ot the internal read buffer (rxBuffer).
+    * Note that this function is never called in this class and typically should
+    * be called as part of a receive data interrupt routine.
+    */
+    virtual void handleRead() = 0;
+
+protected:
+    MTSCircularBuffer txBuffer; // Internal write or transmit circular buffer
+    MTSCircularBuffer rxBuffer; // Internal read or receieve circular buffer
+};
+
+}
+
+#endif /* MTSBUFFEREDIO_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/io/MTSSerial.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,76 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "MTSSerial.h"
+
+using namespace mts;
+
+MTSSerial::MTSSerial(PinName TXD, PinName RXD, int txBufferSize, int rxBufferSize)
+    : MTSBufferedIO(txBufferSize, rxBufferSize)
+    , serial(TXD,RXD)
+{
+    serial.attach(this, &MTSSerial::handleRead, Serial::RxIrq);
+    //serial.attach(this, &MTSSerial::handleWrite, Serial::TxIrq);
+}
+
+MTSSerial::~MTSSerial()
+{
+
+}
+
+void MTSSerial::baud(int baudrate)
+{
+    serial.baud(baudrate);
+}
+
+void MTSSerial::format(int bits, SerialBase::Parity parity, int stop_bits)
+{
+    serial.format(bits, parity, stop_bits);
+}
+
+void MTSSerial::handleRead()
+{
+    while (serial.readable()) {
+        char byte = serial.getc();
+        if(rxBuffer.write(byte) != 1) {
+            printf("[ERROR] Serial Rx Byte Dropped [%c][0x%02X]\r\n", byte, byte);
+            if(byte == 0xFF) {
+                // hack so we dont hang - fix later
+                puts("[ERR] Comm errors, must reboot");
+                fflush(stdout);
+                NVIC_SystemReset();
+            }
+            return;
+        }
+    }
+}
+
+// Currently uses Non-Irq based blocking write calls
+void MTSSerial::handleWrite()
+{
+    while(txBuffer.size() != 0) {
+        if (serial.writeable()) {
+            char byte;
+            if(txBuffer.read(byte) == 1) {
+                serial.putc(byte);
+            }
+        } else {
+            return;
+        }
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/io/MTSSerial.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,76 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 MTSSERIAL_H
+#define MTSSERIAL_H
+
+#include "mbed.h"
+#include "MTSBufferedIO.h"
+
+namespace mts
+{
+
+/** This class derives from MTSBufferedIO and provides a buffered wrapper to the
+* standard mbed Serial class. Since it depends only on the mbed Serial class for
+* accessing serial data, this class is inherently portable accross different mbed
+* platforms.
+*/
+class MTSSerial : public MTSBufferedIO
+{
+public:
+    /** Creates a new MTSSerial object that can be used to talk to an mbed serial port
+    * through internal SW buffers.
+    *
+    * @param TXD the transmit data pin on the desired mbed Serial interface.
+    * @param RXD the receive data pin on the desired mbed Serial interface.
+    * @param txBufferSize the size in bytes of the internal SW transmit buffer. The
+    * default is 64 bytes.
+    * @param rxBufferSize the size in bytes of the internal SW receive buffer. The
+    * default is 64 bytes.
+    */
+    MTSSerial(PinName TXD, PinName RXD, int txBufferSize = 256, int rxBufferSize = 256);
+
+    /** Destructs an MTSSerial object and frees all related resources, including
+    * internal buffers.
+    */
+    ~MTSSerial();
+
+    /** This method is used to the set the baud rate of the serial port.
+    *
+    * @param baudrate the baudrate in bps as an int. The default is 9600 bps.
+    */
+    void baud(int baudrate);
+
+    /** This method sets the transmission format used by the serial port.
+    *
+    * @param bits the number of bits in a word (5-8; default = 8)
+    * @param parity the parity used (SerialBase::None, SerialBase::Odd, SerialBase::Even,
+    * SerialBase::Forced1, SerialBase::Forced0; default = SerialBase::None)
+    * @param stop the number of stop bits (1 or 2; default = 1)
+    */
+    void format(int bits=8, SerialBase::Parity parity=mbed::SerialBase::None, int stop_bits=1);
+
+protected:
+    Serial serial; // Internal mbed Serial object
+
+private:
+    virtual void handleWrite(); // Method for handling data to be written
+    virtual void handleRead(); // Method for handling data to be read
+};
+
+}
+
+#endif /* MTSSERIAL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/io/MTSSerialFlowControl.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,99 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "MTSSerialFlowControl.h"
+
+using namespace mts;
+
+MTSSerialFlowControl::MTSSerialFlowControl(PinName TXD, PinName RXD, PinName RTS, PinName CTS, int txBufSize, int rxBufSize)
+    : MTSSerial(TXD, RXD, txBufSize, rxBufSize)
+    , rxReadyFlag(false)
+    , rts(RTS)
+    , cts(CTS)
+{
+    notifyStartSending();
+
+    highThreshold = MAX(rxBufSize - 10, rxBufSize * 0.85);
+    lowThreshold = rxBufSize * 0.3;
+
+    rxBuffer.attach(this, &MTSSerialFlowControl::notifyStartSending, lowThreshold, Vars::LESS);
+}
+
+MTSSerialFlowControl::~MTSSerialFlowControl()
+{
+
+}
+
+void MTSSerialFlowControl::rxClear()
+{
+    MTSBufferedIO::rxClear();
+    notifyStartSending();
+}
+
+void MTSSerialFlowControl::notifyStartSending()
+{
+    if(!rxReadyFlag) {
+        rts.write(0);
+        rxReadyFlag = true;
+        //printf("RTS LOW: READY - RX[%d/%d]\r\n", rxBuffer.size(), rxBuffer.capacity());
+    }
+}
+
+void MTSSerialFlowControl::notifyStopSending()
+{
+    if(rxReadyFlag) {
+        rts.write(1);
+        rxReadyFlag = false;
+        //printf("RTS HIGH: NOT-READY - RX[%d/%d]\r\n", rxBuffer.size(), rxBuffer.capacity());
+    }
+}
+
+void MTSSerialFlowControl::handleRead()
+{
+    while (serial.readable()) {
+        char byte = serial.getc();
+        if(rxBuffer.write(byte) != 1) {
+            rts.write(1);
+            rxReadyFlag = false;
+            printf("[ERROR] Serial Rx Byte Dropped [%c][0x%02X]\r\n", byte, byte);
+            if(byte == 0xFF) {
+                // hack so we dont hang - fix later
+                puts("[ERR] Comm errors, must reboot");
+                fflush(stdout);
+                NVIC_SystemReset();
+            }
+            return;
+        }
+        if (rxBuffer.size() > highThreshold) {
+            notifyStopSending();
+        }
+    }
+}
+
+void MTSSerialFlowControl::handleWrite()
+{
+    while(txBuffer.size() != 0) {
+        if (serial.writeable() && cts.read() == 0) {
+            char byte;
+            if(txBuffer.read(byte) == 1) {
+                serial.putc(byte);
+            }
+        } else {
+            return;
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/io/MTSSerialFlowControl.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,81 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 MTSSERIALFLOWCONTROL_H
+#define MTSSERIALFLOWCONTROL_H
+
+#include "mbed.h"
+#include "MTSSerial.h"
+
+
+namespace mts
+{
+
+/** This class derives from MTSBufferedIO/MTSSerial and provides a buffered wrapper to the
+* standard mbed Serial class along with generic RTS/CTS HW flow control. Since it
+* depends only on the mbed Serial, DigitalOut and InterruptIn classes for accessing
+* the serial data, this class is inherently portable accross different mbed platforms
+* and provides HW flow control even when not natively supported by the processors
+* serial port. If HW flow control is not needed, use MTSSerial instead. It should also
+* be noted that the RTS/CTS functionality in this class is implemented as a DTE device.
+*/
+class MTSSerialFlowControl : public MTSSerial
+{
+public:
+    /** Creates a new MTSSerialFlowControl object that can be used to talk to an mbed serial
+    * port through internal SW buffers. Note that this class also adds the ability to use
+    * RTS/CTS HW Flow Conrtol through and standard mbed DigitalIn and DigitalOut pins.
+    * The RTS and CTS functionality assumes this is a DTE device.
+    *
+    * @param TXD the transmit data pin on the desired mbed serial interface.
+    * @param RXD the receive data pin on the desired mbed serial interface.
+    * @param RTS the DigitalOut pin that RTS will be attached to. (DTE)
+    * @param CTS the DigitalIn pin that CTS will be attached to. (DTE)
+    * @param txBufferSize the size in bytes of the internal SW transmit buffer. The
+    * default is 64 bytes.
+    * @param rxBufferSize the size in bytes of the internal SW receive buffer. The
+    * default is 64 bytes.
+    * @param name an optional name for the serial port. The default is blank.
+    */
+    MTSSerialFlowControl(PinName TXD, PinName RXD, PinName RTS, PinName CTS, int txBufSize = 64, int rxBufSize = 64);
+
+    /** Destructs an MTSSerialFlowControl object and frees all related resources,
+    * including internal buffers.
+    */
+    ~MTSSerialFlowControl();
+    
+    /** This method clears all the data from the internal Rx or read buffer.
+    */
+    virtual void rxClear();
+
+private:
+    void notifyStartSending(); // Used to set cts start signal
+    void notifyStopSending(); // Used to set cts stop signal
+    
+    //This device acts as a DTE
+    bool rxReadyFlag;   //Tracks state change for rts signaling
+    DigitalOut rts; // Used to tell DCE to send or not send data
+    DigitalIn cts; // Used to check if DCE is ready for data
+    int highThreshold; // High water mark for setting cts to stop
+    int lowThreshold; // Low water mark for setting cts to start
+
+    virtual void handleRead(); // Method for handling data to be read
+    virtual void handleWrite(); // Method for handling data to be written
+};
+
+}
+
+#endif /* MTSSERIALFLOWCONTROL */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/tests/test_MTS_Circular_Buffer.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,289 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 TESTMTSCIRCULARBUFFER_H
+#define TESTMTSCIRCULARBUFFER_H
+
+#include "MTSCircularBuffer.h"
+#include "Vars.h"
+
+/* unit tests for the circular buffer class */
+
+using namespace mts;
+
+int capacity = 0;
+MTSCircularBuffer* buffer = new MTSCircularBuffer(5);
+
+void callback()
+{
+    capacity = buffer->remaining();
+}
+
+int testMTSCircularBuffer()
+{
+    printf("Testing: MTSCircularBuffer\r\n");
+    int failed = 0;
+    char byte;
+
+
+    //Test getSize method
+    if (buffer->capacity() != 5) {
+        printf("Failed: capacity()\r\n");
+        failed++;
+    }
+
+    //Test clear function
+    buffer->write("AT", 2);
+    buffer->clear();
+    if (buffer->size() != 0) {
+        printf("Failed: clear()\r\n");
+        failed++;
+    }
+
+    /* The next set of test all rely on an empty buffer!!! */
+
+    //Test isEmpty method - empty buffer
+    if (buffer->isEmpty() != true) {
+        printf("Failed: isEmpty() - empty\r\n");
+        failed++;
+    }
+
+    //Test isFull method - empty buffer
+    if (buffer->isFull() == true) {
+        printf("Failed: isFull() - empty\r\n");
+        failed++;
+    }
+
+    //Test capacity method - empty buffer
+    if (buffer->remaining() != 5) {
+        printf("Failed: remaining() - empty\r\n");
+        failed++;
+    }
+
+    //Test available method - empty buffer
+    if (buffer->size() != 0) {
+        printf("Failed: size() - empty\r\n");
+        failed++;
+    }
+
+    /* The next set of tests all rely on a full buffer */
+
+    //Test bulk write method
+    int tmp = buffer->write("Test", 5);
+    if (tmp != 5) {
+        printf("Failed: bulk write()\r\n");
+        failed++;
+    }
+
+    //Test isEmpty method - full buffer
+    if (buffer->isEmpty() == true) {
+        printf("Failed: isEmpty() - full\r\n");
+        failed++;
+    }
+
+    //Test isFull method - full buffer
+    if (buffer->isFull() == false) {
+        printf("Failed: isFull() - full\r\n");
+        failed++;
+    }
+
+    //Test capacity method - full buffer
+    if (buffer->remaining() != 0) {
+        printf("Failed: remaining() - full\r\n");
+        failed++;
+    }
+
+    //Test available method - full buffer
+    if (buffer->size() != 5) {
+        printf("Failed: size() - full\r\n");
+        failed++;
+    }
+
+    //Test single overwrite method
+    if (buffer->write('A') != 0) {
+        printf("Failed: write() - overwrite\r\n");
+        failed++;
+    }
+
+    //Test bulk overwrite method
+    if (buffer->write("Test", 5) != 0) {
+        printf("Failed: bulk write() - overflow\r\n");
+        failed++;
+    }
+
+    //Test single read method
+    if ((buffer->read(byte) < 1 && byte != 'T') || buffer->remaining() != 1) {
+        printf("Failed: single read()\r\n");
+        failed++;
+    }
+
+    //Test bulk read method
+    char data[5];
+    if (buffer->read(data, 4) != 4 || data[0] != 'e' || data[1] != 's' || data[2] != 't' || data[3] != '\0') {
+        printf("Failed: bulk read()\r\n");
+        failed++;
+    }
+
+    //Test wrap write/read methods
+    tmp = buffer->write("AT", 3);
+    if (tmp != 3 || (buffer->read(byte) < 1 && byte != 'A') || (buffer->read(byte) < 1 && byte != 'T')) {
+        printf("Failed: wrap write()/read()\r\n");
+        failed++;
+    }
+    buffer->clear();
+
+    /* The next set of test are focused all on the attach methods */
+
+    //Test attach with greater than below level
+    buffer->attach(&callback, 3, Vars::GREATER);
+    buffer->write("ABC", 3);
+    if (capacity != 0) {
+        printf("Failed: attach() - greater/below\r\n");
+        failed++;
+    }
+
+    //Test attach with greater than above level
+    buffer->write('T');
+    if (capacity != 1) {
+        printf("Failed: attach() - greater/above\r\n");
+        failed++;
+    }
+    buffer->clear();
+    capacity = 0;
+
+    //Test attach with greater equal than below level
+    buffer->attach(&callback, 3, Vars::GREATER_EQUAL);
+    buffer->write("AB", 2);
+    if (capacity != 0) {
+        printf("Failed: attach() - greater equal/below\r\n");
+        failed++;
+    }
+
+    //Test attach with greater equal than above level
+    buffer->write('T');
+    if (capacity != 2) {
+        printf("Failed: attach() - greater equal/above\r\n");
+        failed++;
+    }
+
+    //Test attach with less than above level
+    buffer->clear();
+    buffer->write("ABC", 3);
+    capacity = 0;
+    buffer->attach(&callback, 2, Vars::LESS);
+    buffer->read(byte);
+    if (capacity != 0) {
+        printf("Failed: attach() - less_equal/above\r\n");
+        failed++;
+    }
+
+    //Test attach with less than below level
+    buffer->read(byte);
+    if (capacity != 4) {
+        printf("Failed: attach() - less_equal/below%d\r\n", capacity);
+        failed++;
+    }
+
+    //Test attach with less equal than above level
+    buffer->clear();
+    buffer->write("Test", 4);
+    capacity = 0;
+    buffer->attach(&callback, 2, Vars::LESS_EQUAL);
+    buffer->read(byte);
+    if (capacity != 0) {
+        printf("Failed: attach() - less equal/above\r\n");
+        failed++;
+    }
+
+    //Test attach with less equal than below level
+    buffer->read(byte);
+    if (capacity != 3) {
+        printf("Failed: attach() - less equal/below%d\r\n", capacity);
+        failed++;
+    }
+
+    //Test attach with less equal than above level
+    buffer->clear();
+    buffer->write("Test", 4);
+    capacity = 0;
+    buffer->attach(&callback, 2, Vars::EQUAL);
+    buffer->read(byte);
+    if (capacity != 0) {
+        printf("Failed: attach() - equal/above\r\n");
+        failed++;
+    }
+
+    //Test attach with less equal than below level
+    buffer->read(byte);
+    if (capacity != 3) {
+        printf("Failed: attach() - equal/below%d\r\n", capacity);
+        failed++;
+    }
+
+    //Test Ins and Outs
+    {
+        const char inData[] = "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*";
+        const int size = sizeof(inData); 
+        char outData[size];
+        
+        int bytesWritten = 0;
+        int bytesRead = 0;
+        buffer->clear();
+        
+        Timer tmr;
+        tmr.start();
+        do {       
+            int remaining = size - bytesRead;
+            int readable = buffer->size();
+            if(remaining) {
+                if(readable) {
+                    //printf("READABLE [%d]\r\n", readable);
+                    int received = buffer->read(&outData[bytesRead], remaining);
+                    bytesRead += received;
+                    //printf("READ [%d]  TOTAL[%d]  REMAINING[%d]\r\n", received, bytesRead, size - bytesRead);
+                }
+            }
+            
+            remaining = size - bytesWritten;
+            int writeable = buffer->remaining();
+            if(remaining) {
+                if(writeable) {
+                    //printf("WRITEABLE [%d]\r\n", writeable);
+                    int written = buffer->write(&inData[bytesWritten], remaining);   
+                    bytesWritten += written;
+                    remaining = size - bytesWritten;
+                    //printf("WROTE [%d]  TOTAL[%d]  REMAINING[%d]\r\n", written, bytesWritten, size - bytesWritten);
+                }
+            }
+            
+        } while (tmr.read_ms() <= 5000 && bytesRead < size);
+        
+        printf("INPUT  [%d]: [%s]\r\n", size, inData);
+        printf("OUTPUT [%d]: [%s]\r\n", bytesRead, outData);
+        for(int i = 0; i < size - 1; i++) {
+            if(inData[i] != outData[i]) {
+                printf("Failed: Buffers do not match at index %d\r\n", i);
+                failed++;
+                break;   
+            }   
+        }
+    }
+
+    printf("Finished Testing: MTSCircularBuffer\r\n");
+    return failed;
+}
+
+#endif /* TESTMTSCIRCULARBUFFER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/tests/test_SMS.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,48 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 _TEST_SMS_H_
+#define _TEST_SMS_H_
+
+using namespace mts;
+
+void sendSms() {
+    Code code;
+    std::string sMsg("Hello from Multi-Tech MBED!");
+    std::string sPhoneNum( /* your 10-digit phone number prepended with a 1, e.g. 12228675309 */);
+    
+    printf("Sending message [%s] to [%s]\r\n", sMsg.c_str(), sPhoneNum.c_str());
+    code = Cellular::getInstance()->sendSMS(sPhoneNum, sMsg);
+    
+    if(code != SUCCESS) {
+        printf("Error during SMS send [%d]\r\n", (int)code);
+    } else {
+        printf("Success!\r\n");
+    }
+}
+
+void receiveSms() {
+    printf("Checking Received Messages\r\n");
+    std::vector<Cellular::Sms> vSms = Cellular::getInstance()->getReceivedSms();
+    printf("\r\n");
+    for(unsigned int i = 0; i < vSms.size(); i++) {
+        printf("[%d][%s][%s][%s]\r\n", i, vSms[i].timestamp.c_str(), 
+                vSms[i].phoneNumber.c_str(), vSms[i].message.c_str());
+    }
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/tests/test_TCP_Socket.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,252 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 _TEST_TCP_SOCKET_H_
+#define _TEST_TCP_SOCKET_H_
+
+// 0 for shield board with wifi
+// 1 for shield board with cellular
+#define CELL_SHIELD 0
+
+/* test TCP socket communication
+ * will keep talking to server until data doesn't match expected */
+
+using namespace mts;
+const char PATTERN_LINE1[] = "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}|";
+const char PATTERN[] =  "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}|\r\n"
+                        "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}/\r\n"
+                        "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}-\r\n"
+                        "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}\\\r\n"
+                        "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}|\r\n"
+                        "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}/\r\n"
+                        "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}-\r\n"
+                        "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}\\\r\n"
+                        "abcdefghijklmnopqrstuvwzyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()[]{}*";
+                        
+const char MENU[] =     "1       send ascii pattern until keypress\r\n"
+                        "2       send ascii pattern (numbered)\r\n"
+                        "3       send pattern and close socket\r\n"
+                        "4       send [ETX] and wait for keypress\r\n"
+                        "5       send [DLE] and wait for keypress\r\n"
+                        "6       send all hex values (00-FF)\r\n"
+                        "q       quit\r\n"
+                        ">:\r\n";
+                        
+const char WELCOME[] =  "Connected to: TCP test server";
+
+bool testTcpSocketIteration();
+
+void testTcpSocket() {
+    Code code;
+    /* this test is set up to interact with a server listening at the following address and port */
+    const int TEST_PORT = 7000;
+    const std::string TEST_SERVER("204.26.122.5";
+    
+    printf("TCP SOCKET TESTING\r\n");
+#if CELL_SHIELD
+    Transport::setTransport(Transport::CELLULAR);
+    MTSSerialFlowControl* serial = new MTSSerialFlowControl(PTD3, PTD2, PTA12, PTC8);
+    serial->baud(115200);
+    Cellular::getInstance()->init(serial);
+    
+    printf("Setting APN\r\n");
+    code = Cellular::getInstance()->setApn("wap.cingular");
+    if(code == SUCCESS) {
+        printf("Success!\r\n");
+    } else {
+        printf("Error during APN setup [%d]\r\n", (int)code);
+    }
+#else
+    for (int i = 6; i >= 0; i = i - 2) {
+        wait(2);
+        printf("Waiting %d seconds...\n\r", i);
+    }  
+    Transport::setTransport(Transport::WIFI);
+    MTSSerial* serial = new MTSSerial(PTD3, PTD2, 256, 256);
+    serial->baud(9600);
+    Wifi::getInstance()->init(serial);
+    
+    code = Wifi::getInstance()->setNetwork("your wireless network" /* SSID of wireless */, Wifi::WPA2 /* security type of wireless */, "your wireless network password" /* password for wireless */);
+    if(code == SUCCESS) {
+        printf("Success!\r\n");
+    } else {
+        printf("Error during network setup [%d]\r\n", (int)code);
+    }
+    code = Wifi::getInstance()->setDeviceIP();
+    if(code == SUCCESS) {
+        printf("Success!\r\n");
+    } else {
+        printf("Error during IP setup [%d]\r\n", (int)code);
+    }
+#endif
+
+    printf("Establishing Connection\r\n");
+#if CELL_SHIELD
+    if(Cellular::getInstance()->connect()) {
+#else
+    if(Wifi::getInstance()->connect()) {
+#endif
+        printf("Success!\r\n");
+    } else {
+        printf("Error during connection\r\n");
+    }
+       
+    printf("Opening a TCP Socket\r\n");
+#if CELL_SHIELD
+    if(Cellular::getInstance()->open(TEST_SERVER, TEST_PORT, IPStack::TCP)) {
+#else
+    if(Wifi::getInstance()->open(TEST_SERVER, TEST_PORT, IPStack::TCP)) {
+#endif
+        printf("Success!\r\n");   
+    } else {
+        printf("Error during TCP socket open [%s:%d]\r\n", TEST_SERVER.c_str(), TEST_PORT);
+    }
+    
+    //Find Welcome Message and Menu
+    
+    int count = 0;
+    while(testTcpSocketIteration()) {
+        count++;
+        printf("Successful Iterations: %d\r\n", count);
+    }
+    
+    printf("Closing socket\r\n");
+#if CELL_SHIELD
+    Cellular::getInstance()->close();
+#else
+    Wifi::getInstance()->close();
+#endif
+    
+    wait(10);
+    
+    printf("Disconnecting\r\n");
+#if CELL_SHIELD
+    Cellular::getInstance()->disconnect();   
+#else
+    Wifi::getInstance()->disconnect();
+#endif
+}
+
+bool testTcpSocketIteration() {
+    Timer tmr;
+    int bytesRead = 0;
+    const int bufferSize = 1024;
+    char buffer[bufferSize] = { 0 };
+    std::string result;
+    
+    printf("Receiving Data\r\n");
+    tmr.start();
+    do {
+#if CELL_SHIELD
+        int size = Cellular::getInstance()->read(buffer, bufferSize, 1000);
+#else
+        int size = Wifi::getInstance()->read(buffer, bufferSize, 1000);
+#endif
+        if(size != -1) {
+            result.append(buffer, size);
+        } else {
+            printf("Error reading from socket\r\n");
+            return false;
+        }
+        printf("Total bytes read %d\r\n", result.size());
+    } while (tmr.read() <= 15 && bytesRead < bufferSize);
+   
+    printf("READ: [%d] [%s]\r\n", bytesRead, result.c_str());
+    
+    size_t pos = result.find(PATTERN_LINE1);
+    if(pos != std::string::npos) {
+        //compare buffers
+        int patternSize = sizeof(PATTERN) - 1;
+        const char* ptr = &result.data()[pos];
+        bool match = true;
+        for(int i = 0; i < patternSize; i++) {
+            if(PATTERN[i] != ptr[i]) {
+                printf("1ST PATTERN DOESN'T MATCH AT [%d]\r\n", i);
+                printf("PATTERN [%02X]  BUFFER [%02X]\r\n", PATTERN[i], ptr[i]);
+                match = false;
+                break;   
+            }
+        }
+        if(match) {
+            printf("FOUND 1ST PATTERN\r\n");   
+        }
+        
+        pos = result.find(PATTERN_LINE1, pos + patternSize);
+        if(pos != std::string::npos) {
+            //compare buffers
+            ptr = &result.data()[pos];
+            match = true;
+            for(int i = 0; i < patternSize; i++) {
+                if(PATTERN[i] != ptr[i]) {
+                    printf("2ND PATTERN DOESN'T MATCH AT [%d]\r\n", i);
+                    printf("PATTERN [%02X]  BUFFER [%02X]\r\n", PATTERN[i], ptr[i]);
+                    match = false;
+                    break;   
+                }
+            }
+            if(match) {
+                printf("FOUND 2ND PATTERN\r\n");   
+            }
+        }
+    }
+    
+    result.clear();
+    
+    printf("Writing to socket: 2\r\n");
+#if CELL_SHIELD
+    if(Cellular::getInstance()->write("2\r\n", 3, 10000) == 3) {
+#else
+    if(Wifi::getInstance()->write("2\r\n", 3, 10000) == 3) {
+#endif
+        printf("Successfully wrote '2'\r\n");
+    } else {
+        printf("Failed to write '2'\r\n");   
+        return false;
+    }
+    printf("Expecting 'how many ? >:\r\n");
+#if CELL_SHIELD
+    bytesRead = Cellular::getInstance()->read(buffer, bufferSize, 10000);
+#else
+    bytesRead = Wifi::getInstance()->read(buffer, bufferSize, 10000);
+#endif
+    if(bytesRead != -1) {
+        result.append(buffer, bytesRead);
+        printf("READ: [%d] [%s]\r\n", bytesRead, result.c_str());
+        if(result.find("how many") != std::string::npos) {
+            printf("Successfully found 'how many'\r\n");   
+            printf("Writing to socket: 2\r\n");
+#if CELL_SHIELD
+            if(Cellular::getInstance()->write("2\r\n", 3, 10000) == 3) {
+#else
+            if(Wifi::getInstance()->write("2\r\n", 3, 10000) == 3) {
+#endif
+                printf("Successfully wrote '2'\r\n");
+            } else {
+                printf("Failed to write '2'\r\n");   
+                return false;
+            }
+        } else {
+            printf("Missing second option to menu item 2\r\n");
+        }
+    } else {
+        printf("Error reading from socket\r\n");
+        return false;
+    }
+    
+    return true;
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/tests/test_TCP_Socket_Echo.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,200 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 _TEST_TCP_SOCKET_ECHO_H_
+#define _TEST_TCP_SOCKET_ECHO_H_
+
+// 0 for shield board with wifi
+// 1 for shield board with cellular
+#define CELL_SHIELD 0
+
+/* test TCP socket communication
+ * designed to talk to remote echo server
+ * will talk to server until echo doesn't match sent data */
+//Setup a netcat server with command: ncat -l 5798 -k -c 'xargs -n1 --null echo'
+
+using namespace mts;
+
+bool testTcpSocketEchoLoop();
+
+void testTcpSocketEcho() {
+    Code code;
+    const int TEST_PORT = 5798;
+    const std::string TEST_SERVER( /* public IP of server running the netcat command given above */);
+    
+    printf("TCP SOCKET TESTING\r\n");
+#if CELL_SHIELD
+    Transport::setTransport(Transport::CELLULAR);
+    MTSSerialFlowControl* serial = new MTSSerialFlowControl(PTD3, PTD2, PTA12, PTC8);
+    serial->baud(115200);
+    Cellular::getInstance()->init(serial);
+    
+    printf("Setting APN\r\n");
+    code = Cellular::getInstance()->setApn("wap.cingular");
+    if(code == SUCCESS) {
+        printf("Success!\r\n");
+    } else {
+        printf("Error during APN setup [%d]\r\n", (int)code);
+    }
+#else
+    for (int i = 6; i >= 0; i = i - 2) {
+        wait(2);
+        printf("Waiting %d seconds...\n\r", i);
+    }  
+    Transport::setTransport(Transport::WIFI);
+    MTSSerial* serial = new MTSSerial(PTD3, PTD2, 256, 256);
+    serial->baud(9600);
+    Wifi::getInstance()->init(serial);
+    
+    code = Wifi::getInstance()->setNetwork("your wireless network" /* SSID of wireless */, Wifi::WPA2 /* security type of wireless */, "your wireless network password" /* password for wireless */);
+    if(code == SUCCESS) {
+        printf("Success!\r\n");
+    } else {
+        printf("Error during network setup [%d]\r\n", (int)code);
+    }
+    code = Wifi::getInstance()->setDeviceIP();
+    if(code == SUCCESS) {
+        printf("Success!\r\n");
+    } else {
+        printf("Error during IP setup [%d]\r\n", (int)code);
+    }
+#endif
+    
+    printf("Establishing Connection\r\n");
+#if CELL_SHIELD
+    if(Cellular::getInstance()->connect()) {
+#else
+    if(Wifi::getInstance()->connect()) {
+#endif
+        printf("Success!\r\n");
+    } else {
+        printf("Error during connection.  Aborting.\r\n");
+        return;
+    }
+       
+#if CELL_SHIELD
+    if(Cellular::getInstance()->open(TEST_SERVER, TEST_PORT, IPStack::TCP)) {
+#else
+    if(Wifi::getInstance()->open(TEST_SERVER, TEST_PORT, IPStack::TCP)) {
+#endif
+        printf("Success!\r\n");   
+    } else {
+        printf("Error during TCP socket open [%s:%d].  Aborting.\r\n", TEST_SERVER.c_str(), TEST_PORT);
+        return;
+    }
+    
+    int count = 0;
+    while(testTcpSocketEchoLoop()) {
+        count++;  
+        printf("Successful Echos: [%d]\r\n", count);  
+    }
+        
+    printf("Closing socket\r\n");
+#if CELL_SHIELD
+    Cellular::getInstance()->close();
+#else
+    Wifi::getInstance()->close();
+#endif
+    
+    wait(10);
+    
+    printf("Disconnecting\r\n");
+#if CELL_SHIELD
+    Cellular::getInstance()->disconnect();   
+#else
+    Wifi::getInstance()->disconnect();
+#endif  
+}
+
+bool testTcpSocketEchoLoop() {
+    using namespace mts;
+    const char buffer[] = "*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*";
+    
+    /*//Big Buffer
+    const char buffer[] = "1ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*"
+                          "2ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*"
+                          "3ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*"
+                          "4ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*"
+                          "5ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*"
+                          "6ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*"
+                          "7ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*"
+                          "8ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*"
+                          "9ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*"
+                          "0ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890*";
+    */
+                          
+    const int size = sizeof(buffer); 
+    char echoData[size];
+    
+    printf("Sending buffer\r\n");
+#if CELL_SHIELD
+    int bytesWritten = Cellular::getInstance()->write(buffer, size, 10000);
+#else
+    int bytesWritten = Wifi::getInstance()->write(buffer, size, 10000);
+#endif
+    if(bytesWritten == size) {
+        printf("Successfully sent buffer\r\n");
+    } else {
+        printf("Failed to send buffer.  Closing socket and aborting.\r\n");
+#if CELL_SHIELD
+        Cellular::getInstance()->close();
+#else
+        Wifi::getInstance()->close();
+#endif
+        return false;
+    }
+    
+    printf("Receiving echo (timeout = 15 seconds)\r\n");
+    Timer tmr;
+    int bytesRead = 0;
+    tmr.start();
+    do {
+#if CELL_SHIELD
+        int status = Cellular::getInstance()->read(&echoData[bytesRead], size - bytesRead, 1000);
+#else
+        int status = Wifi::getInstance()->read(&echoData[bytesRead], size - bytesRead, 1000);
+#endif
+        if(status != -1) {
+            bytesRead += status;
+        } else {
+            printf("Error reading from socket.  Closing socket and aborting.\r\n");
+#if CELL_SHIELD
+            Cellular::getInstance()->close();
+#else
+            Wifi::getInstance()->close();
+#endif
+            return false;
+        }
+        printf("Total bytes read %d\r\n", bytesRead);
+    } while (tmr.read_ms() <= 15000 && bytesRead < size);
+
+
+    //Safely Cap at Max Size
+    echoData[size - 1] = '\0';
+    printf("Comparing Buffers\r\n");
+    printf("SENT [%d]: [%s]\r\n", size, buffer);
+    printf("RECV [%d]: [%s]\r\n", bytesRead, echoData);
+    
+    for(int i = 0; i < size - 1; i++) {
+        if(buffer[i] != echoData[i]) {
+            printf("Buffers do not match at index %d\r\n", i);
+            return false;   
+        }   
+    }   
+    return true;
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/tests/test_main.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,59 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "mbed.h"
+#include "include_me.h"
+
+// uncomment only the header corresponding to the test you want to run
+//#include "test_ping.h"
+//#include "test_SMS.h"
+//#include "test_TCP_Socket.h"
+//#include "test_TCP_Socket_Echo.h"
+//#include "test_MTS_Circular_Buffer.h"
+
+
+//int main() {
+    // uncomment only one test at a time
+    
+    // PING TEST
+    //testPing();
+
+    /*
+    // SMS TEST
+    Transport::setTransport(Transport::CELLULAR);
+    MTSSerialFlowControl* serial = new MTSSerialFlowControl(PTD3, PTD2, PTA12, PTC8);
+    serial->baud(115200);
+    Cellular* cell = Cellular::getInstance();
+    cell->init(serial);
+    while (cell->getRegistration() != Cellular::REGISTERED);
+    while (cell->setApn("wap.cingular") != SUCCESS);
+    
+    sendSms();
+    while (true) {
+        receiveSms();
+        wait(15);
+    }
+    */
+    
+    // TCP SOCKET TEST
+    //testTcpSocket();
+    
+    // TCP SOCKET ECHO TEST
+    //testTcpSocketEcho();
+    
+    // CIRCULAR BUFFER TEST
+    //testMTSCircularBuffer();
+//}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/tests/test_ping.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,175 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 TESTPING_H
+#define TESTPING_H
+
+#include "mbed.h"
+#include "include_me.h"
+
+#define MAX_TRIES 5
+#define MAX_REGISTRATION_TRIES 10
+
+// 0 for shield board with wifi
+// 1 for shield board with cellular
+#define CELL_SHIELD 0
+
+/* tries to ping 8.8.8.8 (Google's DNS server)
+ * blinks green LED if successful, red LED if failure */
+
+using namespace mts;
+
+bool cellPingTest(const std::string& apn, const std::string& server);
+bool wifiPingTest(const std::string& server, const std::string& ssid, Wifi::SecurityType type, const std::string& key);
+void blinkLed(DigitalOut led);
+
+void testPing() {
+    DigitalOut ledG(LED1);
+    DigitalOut ledR(LED2);
+    
+    ledG = 1;
+    ledR = 1;
+    
+    std::string server = "8.8.8.8"; // Google's DNS server
+#if CELL_SHIELD
+    std::string apn = "wap.cingular"; // APN of sim card
+    if (cellPingTest(apn, server)) {
+#else
+    std::string ssid = ""; // ssid of wireless network
+    Wifi::SecurityType type = Wifi::WPA2; // NONE, WEP64, WEP128, WPA, WPA2
+    std::string key = ""; // password for network (if type is not "NONE")
+    if (wifiPingTest(server, ssid, type, key)) {
+#endif
+        printf("success!\n\r");
+        blinkLed(ledG);
+    } else {
+        printf("failure!\n\r");
+        blinkLed(ledR);
+    }
+}
+
+bool wifiPingTest(const std::string& server, const std::string& ssid, Wifi::SecurityType type, const std::string& key) {
+    int i;
+    
+    for (int i = 6; i >= 0; i = i - 2) {
+        wait(2);
+        printf("Waiting %d seconds...\n\r", i);
+    }
+    
+    Transport::setTransport(Transport::WIFI);
+    MTSSerial* serial = new MTSSerial(PTD3, PTD2, 256, 256);
+    serial->baud(9600);
+    Wifi* wifi = Wifi::getInstance();
+    wifi->init(serial);
+    
+    i = 0;
+    while (i++ < MAX_TRIES) {
+        if (wifi->setNetwork(ssid, type, key) == SUCCESS) {
+            printf("set network\r\n");
+            break;
+        } else {
+            printf("failed to set network\r\n");
+        }
+        wait(1);
+    }
+    
+    i = 0;
+    while (i++ < MAX_TRIES) {
+        if (wifi->setDeviceIP() == SUCCESS) {
+            printf("set IP\r\n");
+            break;
+        } else {
+            printf("failed to set IP\r\n");
+        }
+        wait(1);
+    }
+        
+    i = 0;
+    while (i++ < MAX_TRIES) {
+        if (wifi->connect()) {
+            printf("connected\r\n");
+            break;
+        } else {
+            printf("failed to connect\r\n");
+        }
+        wait(1);
+    }
+    
+    printf("pinging %s\n\r", server.c_str());
+    return wifi->ping(server);
+}
+
+bool cellPingTest(const std::string& apn, const std::string& server) {
+    int i; 
+    
+    Transport::setTransport(Transport::CELLULAR);
+    MTSSerialFlowControl* serial = new MTSSerialFlowControl(PTD3, PTD2, PTA12, PTC8);
+    serial->baud(115200);
+    Cellular* cell = Cellular::getInstance();
+    cell->init(serial);
+    
+    i = 0;
+    while (i++ < MAX_REGISTRATION_TRIES) {
+        if (cell->getRegistration() == Cellular::REGISTERED) {
+            printf("registered with tower\n\r");
+            break;
+        } else if (i >= MAX_REGISTRATION_TRIES) {
+            printf("failed to register with tower\n\r");
+            return false;
+        }
+        wait(3);
+    }
+    
+    i = 0;
+    printf("setting APN to %s\n\r", apn.c_str());
+    while (i++ < MAX_TRIES) {
+        if (cell->setApn(apn) == SUCCESS) {
+            printf("successfully set APN\n\r");
+            break;
+        } else if (i >= MAX_TRIES) {
+            printf("failed to set APN\n\r");
+            return false;
+        }
+        wait(1);
+    }
+    
+    i = 0;
+    printf("bringing up PPP link\n\r");
+    while (i++ < MAX_TRIES) {
+        if (cell->connect()) {
+            printf("PPP link is up\n\r");
+            break;
+        } else if (i >= MAX_TRIES) {
+            printf("failed to bring PPP link up\n\r");
+            return false;
+        }
+        wait(1);
+    }
+    
+    printf("pinging %s\n\r", server.c_str());
+    return cell->ping(server);
+}
+
+void blinkLed(DigitalOut led) {
+    led = 0;
+    
+    while (true) {
+        wait(0.25);
+        led = !led;
+    }
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/utils/MTSCircularBuffer.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,158 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "MTSCircularBuffer.h"
+
+using namespace mts;
+
+MTSCircularBuffer::MTSCircularBuffer(int bufferSize) : bufferSize(bufferSize), readIndex(0), writeIndex(0), bytes(0), _threshold(-1), _op(Vars::GREATER)
+{
+    buffer = new char[bufferSize];
+}
+
+MTSCircularBuffer::~MTSCircularBuffer()
+{
+    delete[] buffer;
+}
+
+int MTSCircularBuffer::capacity()
+{
+    return bufferSize;
+}
+
+int MTSCircularBuffer::read(char* data, int length)
+{
+    int i = 0;
+    while ((i < length) && (bytes > 0)) {
+        if (readIndex == bufferSize) {
+            readIndex = 0;
+        }
+        data[i++] = buffer[readIndex++];
+        bytes--;
+        checkThreshold();
+    }
+    return i;
+}
+
+int MTSCircularBuffer::read(char& data)
+{
+    if (bytes == 0) {
+        return 0;
+    }
+    if (readIndex == bufferSize) {
+        readIndex = 0;
+    }
+    data = buffer[readIndex++];
+    bytes--;
+    checkThreshold();
+    return 1;
+}
+
+int MTSCircularBuffer::write(const char* data, int length)
+{
+    int i = 0;
+    while((i < length) && (bytes < bufferSize)) {
+        if(writeIndex == bufferSize) {
+            writeIndex = 0;
+        }
+        buffer[writeIndex++] = data[i++];
+        bytes++;
+        checkThreshold();
+    }
+    return i;
+}
+
+int MTSCircularBuffer::write(char data)
+{
+    if (bytes == bufferSize) {
+        return 0;
+    }
+    if(writeIndex == bufferSize) {
+        writeIndex = 0;
+    }
+    buffer[writeIndex++] = data;
+    bytes++;
+    checkThreshold();
+    return 1;
+}
+
+int MTSCircularBuffer::remaining()
+{
+    return bufferSize - bytes;
+}
+
+int MTSCircularBuffer::size()
+{
+    return bytes;
+}
+
+bool MTSCircularBuffer::isFull()
+{
+    if (bytes == bufferSize) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+bool MTSCircularBuffer::isEmpty()
+{
+    if (bytes == 0) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void MTSCircularBuffer::clear()
+{
+    writeIndex = readIndex = bytes = 0;
+}
+
+void MTSCircularBuffer::checkThreshold()
+{
+    if (_threshold == -1) {
+        return;
+    }
+    switch (_op) {
+        case Vars::GREATER:
+            if (bytes > _threshold) {
+                notify.call();
+            }
+            break;
+        case Vars::LESS:
+            if (bytes < _threshold) {
+                notify.call();
+            }
+            break;
+        case Vars::GREATER_EQUAL:
+            if (bytes >= _threshold) {
+                notify.call();
+            }
+            break;
+        case Vars::LESS_EQUAL:
+            if (bytes <= _threshold) {
+                notify.call();
+            }
+            break;
+        case Vars::EQUAL:
+            if (bytes == _threshold) {
+                notify.call();
+            }
+            break;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/utils/MTSCircularBuffer.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,176 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 MTSCIRCULARBUFFER_H
+#define MTSCIRCULARBUFFER_H
+
+#include "mbed.h"
+#include "Vars.h"
+
+namespace mts {
+
+/** This class provides a circular byte buffer meant for temporary storage
+* during IO transactions.  It contains many of the common methods you
+* would expect from a circular buffer like read, write, and various
+* methods for checking the size or status.  It should be noted that
+* this class does not include any special code for thread safety like
+* a lock.  In most cases this is not problematic, but is something
+* to be aware of.
+*/
+class MTSCircularBuffer
+{
+public:
+    /** Creates an MTSCircularBuffer object with the specified static size.
+    *
+    * @prarm bufferSize size of the buffer in bytes.
+    */
+    MTSCircularBuffer(int bufferSize);
+
+    /** Destructs an MTSCircularBuffer object and frees all related resources.
+    */
+    ~MTSCircularBuffer();
+
+    /** This method enables bulk reads from the buffer.  If more data is 
+    * requested then available it simply returns all remaining data within the
+    * buffer.
+    *
+    * @param data the buffer where data read will be added to.
+    * @param length the amount of data in bytes to be read into the buffer.
+    * @returns the total number of bytes that were read.
+    */
+    int read(char* data, int length);
+
+    /** This method reads a single byte from the buffer.
+    *
+    * @param data char where the read byte will be stored.
+    * @returns 1 if byte is read or 0 if no bytes available.
+    */
+    int read(char& data);
+
+    /** This method enables bulk writes to the buffer. If more data
+    * is requested to be written then space available the method writes
+    * as much data as possible and returns the actual amount written.
+    *
+    * @param data the byte array to be written.
+    * @param length the length of data to be written from the data paramter.
+    * @returns the number of bytes written to the buffer, which is 0 if
+    * the buffer is full.
+    */
+    int write(const char* data, int length);
+
+    /** This method writes a signle byte as a char to the buffer.
+    *
+    * @param data the byte to be written as a char.
+    * @returns 1 if the byte was written or 0 if the buffer was full.
+    */
+    int write(char data);
+
+    /** This method is used to setup a callback funtion when the buffer reaches
+    * a certain threshold. The threshold condition is checked after every read
+    * and write call is completed. The condition is made up of both a threshold
+    * value and operator. An example that would trigger a callback is if the
+    * threshold was 10, the operator GREATER, and there were 12 bytes added to an
+    * empty buffer. 
+    * 
+    * @param tptr a pointer to the object to be called when the condition is met.
+    * @param mptr a pointer to the function within the object to be called when
+    * the condition is met.
+    * @param threshold the value in bytes to be used as part of the condition.
+    * @param op the operator to be used in conjunction with the threshold
+    * as part of the condition. 
+    */
+    template<typename T>
+    void attach(T *tptr, void( T::*mptr)(void), int threshold, Vars::RelationalOperator op)
+    {
+        _threshold = threshold;
+        _op = op;
+        notify.attach(tptr, mptr);
+    }
+
+    /** This method is used to setup a callback funtion when the buffer reaches
+    * a certain threshold. The threshold condition is checked after every read
+    * and write call is completed. The condition is made up of both a threshold
+    * value and operator. An example that would trigger a callback is if the
+    * threshold was 10, the operator GREATER, and there were 12 bytes added to an
+    * empty buffer. 
+    * 
+    * @param fptr a pointer to the static function to be called when the condition
+    * is met.
+    * @param threshold the value in bytes to be used as part of the condition.
+    * @param op the operator to be used in conjunction with the threshold
+    * as part of the condition. 
+    */
+    void attach(void(*fptr)(void), int threshold, Vars::RelationalOperator op)
+    {
+        _threshold = threshold;
+        _op = op;
+        notify.attach(fptr);
+    }
+
+    /** This method returns the size of the storage space currently allocated for
+    * the buffer. This value is equivalent to the one passed into the constructor.
+    * This value is equal or greater than the size() of the buffer.
+    *
+    * @returns the allocated size of the buffer in bytes.
+    */
+    int capacity();
+
+    /** This method returns the amount of space left for writing.
+    *
+    * @returns numbers of unused bytes in buffer.
+    */
+    int remaining();
+
+    /** This method returns the number of bytes available for reading.
+    *
+    * @returns number of bytes currently in buffer.
+    */
+    int size();
+
+    /** This method returns whether the buffer is empty.
+    *
+    * @returns true if empty, otherwise false.
+    */
+    bool isEmpty();
+
+    /** This method returns whether the buffer is full.
+    *
+    * @returns true if full, otherwise false.
+    */
+    bool isFull();
+    
+    /** This method clears the buffer. This is done through
+    * setting the internal read and write indexes to the same
+    * value and is therefore not an expensive operation.
+    */
+    void clear();
+
+
+private:
+    int bufferSize; // total size of the buffer
+    char* buffer; // internal byte buffer as a character buffer
+    int readIndex; // read index for circular buffer
+    int writeIndex; // write index for circular buffer
+    int bytes; // available data
+    FunctionPointer notify; // function pointer used for the internal callback notification 
+    int _threshold; // threshold for the notification
+    Vars::RelationalOperator _op; // operator that determines the direction of the threshold
+    void checkThreshold(); // private function that checks thresholds and processes notifications
+};
+
+}
+
+#endif /* MTSCIRCULARBUFFER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/utils/MTSText.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,58 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "MTSText.h"
+
+using namespace mts;
+
+std::string Text::getLine(const std::string& source, const size_t& start, size_t& cursor) {
+    char delimiters[2];
+    delimiters[0] = '\n';
+    delimiters[1] = '\r';
+    size_t end = source.find_first_of(delimiters, start, 2);
+    std::string line(source.substr(start, end - start));
+    if (end < source.size()) {
+        if (end < source.size() - 1)
+            if ((source[end] == '\n' && source[end + 1] == '\r') || (source[end] == '\r' && source[end + 1] == '\n')) {
+                //Advance an additional character in scenarios where lines end in \r\n or \n\r
+                end++;
+            }
+        end++;
+    }
+    cursor = end;
+    return line;
+}
+
+std::vector<std::string> Text::split(const std::string& str, char delimiter, int limit) {
+    return split(str, std::string(1, delimiter), limit);
+}
+
+std::vector<std::string> Text::split(const std::string& str, const std::string& delimiter, int limit) {
+    std::vector<std::string> result;
+    if(str.size() == 0) {
+        return result;
+    }
+    size_t start = 0;
+    size_t end = str.find(delimiter, start);
+    for (int i = 1; i < limit || (limit <= 0 && (end != std::string::npos)); ++i) {
+        result.push_back(str.substr(start, end - start));
+        start = end + delimiter.length();
+        end = str.find(delimiter, start);
+    }
+    result.push_back(str.substr(start));
+    return result;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/utils/MTSText.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,63 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 MTSTEXT_H_
+#define MTSTEXT_H_
+
+#include <string>
+#include <vector>
+#include <stddef.h>
+
+namespace mts
+{
+
+class Text
+{
+
+public:
+    /**
+    *
+    * @param source
+    * @param start
+    * @param cursor
+    */
+    static std::string getLine(const std::string& source, const size_t& start, size_t& cursor);
+
+    /**
+    *
+    * @param str
+    * @param delimiter
+    * @param limit
+    */
+    static std::vector<std::string> split(const std::string& str, char delimiter, int limit = 0);
+
+    /**
+    *
+    * @param str
+    * @param delimiter
+    * @param limit
+    */
+    static std::vector<std::string> split(const std::string& str, const std::string& delimiter, int limit = 0);
+
+private:
+    Text();
+    Text(const Text& other);
+    Text& operator=(const Text& other);
+};
+
+}
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/utils/Vars.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,88 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 VARS_H
+#define VARS_H
+
+#include <string>
+
+namespace mts
+{
+
+#ifndef MAX
+#define MAX(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a > _b ? _a : _b; })
+#endif
+
+#ifndef MIN
+#define MIN(a,b) ({ __typeof__ (a) _a = (a); __typeof__ (b) _b = (b); _a < _b ? _a : _b; })
+#endif
+
+
+/// An enumeration for common responses.
+enum Code {
+    SUCCESS, ERROR, FAILURE, NO_RESPONSE
+};
+
+/** A static method for getting a string representation for the Code
+* enumeration.
+*
+* @param code a Code enumeration.
+* @returns the enumeration name as a string.
+*/
+static std::string getCodeNames(Code code)
+{
+    switch(code) {
+        case SUCCESS:
+            return "SUCCESS";
+        case ERROR:
+            return "ERROR";
+        case NO_RESPONSE:
+            return "NO_RESPONSE";
+        case FAILURE:
+            return "FAILURE";
+        default:
+            return "UNKNOWN ENUM";
+    }
+}
+
+const unsigned int PINGDELAY = 3; //Time to wait on each ping for a response before timimg out (seconds)
+const unsigned int PINGNUM = 4; //Number of pings to try on ping command
+
+//Special Payload Characters
+const char ETX    = 0x03;  //Ends socket connection
+const char DLE    = 0x10;  //Escapes ETX and DLE within Payload
+const char CR     = 0x0D;
+const char NL     = 0x0A;
+const char CTRL_Z = 0x1A;
+
+
+/** This class holds several enum types and other static variables
+* that are used throughout the rest of the SDK.
+*/
+class Vars
+{
+public:
+    /// Enumeration for different cellular radio types.
+    enum Radio {NA, E1, G2, EV2, H4, EV3, H5};
+
+    enum RelationalOperator {GREATER, LESS, EQUAL, GREATER_EQUAL, LESS_EQUAL};
+};
+
+}
+
+//Test Commit!!!
+
+#endif /* VARS_H */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/wifi/Wifi.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,769 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "Wifi.h"
+#include "MTSText.h"
+
+#if 0
+//Enable debug
+#include <cstdio>
+#define DBG(x, ...) std::printf("Line: %d %s \t[Wifi : DBG]"x"\r\n", __LINE__, __FILE__, ##__VA_ARGS__);
+#else
+#define DBG(x, ...)
+#endif
+
+Wifi* Wifi::instance = NULL;
+
+Wifi* Wifi::getInstance()
+{
+    if(instance == NULL) {
+        instance = new Wifi(NULL);
+    }
+    return instance;
+}
+
+bool Wifi::sortInterfaceMode(void)
+{
+    //Check initial state of command mode
+    std::string response = sendCommand("", 1000, ">");
+    if(response.find(">") != string::npos) {
+        cmdOn = true;
+    }
+
+    //Set device into command mode
+    if (!setCmdMode(true)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool Wifi::init(MTSBufferedIO* io)
+{
+    if (io == NULL) {
+        return false;
+    }
+    instance->io = io;
+
+    // start from the same place each time
+    reset();
+
+    //Secure interface mode
+    if(!sortInterfaceMode()) {
+        return false;
+    }
+
+    //Set device to non-echo mode
+    while (sendBasicCommand("set uart mode 1", 1000) != SUCCESS) {
+        printf("[ERROR] Failed to set to non-echo mode\n\r");
+        //return false;
+    }
+    // do this twice because the module response doesnt seem to always take
+    while (sendBasicCommand("set uart mode 1", 1000) != SUCCESS) {
+        printf("[ERROR] Failed to set to non-echo mode\n\r");
+        //return false;
+    }
+
+    //Set device to manual infrastructure mode
+    if (sendBasicCommand("set wlan join 0", 1000) != SUCCESS) {
+        printf("[ERROR] Failed to set join mode\n\r");
+        return false;
+    }
+
+    //Set device to channel auto-scanning mode
+    if (sendBasicCommand("set wlan channel 0", 1000) != SUCCESS) {
+        printf("[ERROR] Failed to set auto-scanning mode\n\r");
+        return false;
+    }
+
+    //Set device so no data is transmitted immediately following a socket connection
+    if (sendBasicCommand("set comm remote 0", 1000) != SUCCESS) {
+        printf("[ERROR] Failed to set remote transmit mode\n\r");
+        return false;
+    }
+
+    //Set device into DHCP mode by default
+    if (sendBasicCommand("set ip dhcp 1", 1000) != SUCCESS) {
+        printf("[ERROR] Failed to set to default DHCP mode\n\r");
+        return false;
+    }
+
+    return true;
+}
+
+Wifi::Wifi(MTSBufferedIO* io)
+    : io(io)
+    , wifiConnected(false)
+    , _ssid("")
+    , mode(TCP)
+    , socketOpened(false)
+    , socketCloseable(true)
+    , local_port(0)
+    , local_address("")
+    , host_port(0)
+    , cmdOn(false)
+{
+
+}
+
+Wifi::~Wifi()
+{
+}
+
+bool Wifi::connect()
+{
+    //Check if socket is open
+    if(socketOpened) {
+        return true;
+    }
+
+    //Run Test first to validate a good state
+    if(isConnected()) {
+        return true;
+    }
+
+    if (_ssid.size() == 0) {
+        printf("[ERROR] No SSID has been set\n\r");
+        return false;
+    }
+
+    if(!setCmdMode(true)) {
+        return false;
+    }
+
+    //Possibly add a scan command here and look for the network....
+
+    //join my_network
+    printf("[DEBUG] Making SSID Connection Attempt. SSID[%s]\r\n", _ssid.c_str());
+    std::string result = sendCommand("join " + _ssid, 15000, "GW=");
+    //printf("Connect Status: %s\n\r", result.c_str());
+
+    //Check whether connection was successful
+    if(result.find("Associated!") != string::npos) {
+        if(result.find("Static") == string::npos) {
+            int start = result.find("IP=");
+            int stop = result.find(":", start);
+            local_address = result.substr(start + 3, stop - start - 3);
+        }
+        printf("[INFO] WiFi Connection Established: IP[%s]\r\n", local_address.c_str());
+        wifiConnected = true;
+
+        //Report Signal Strength of new connection
+        wait(1); //Needed for signal strength to be available
+        int rssi = getSignalStrength();
+        printf("[DEBUG] Signal strength (dBm): %d\r\n", rssi);
+    } else {
+        wifiConnected = false;
+    }
+
+    return wifiConnected;
+}
+
+void Wifi::disconnect()
+{
+    wait(5.0f);
+    printf("[DEBUG] Disconnecting from network\r\n");
+
+    if(socketOpened) {
+        close();
+    }
+
+    if(!setCmdMode(true)) {
+        printf("[ERROR] Failed in disconnecting from network.  Continuing ...\r\n");
+    }
+
+    std::string response = sendCommand("leave", 10000, "<4.00>");
+    response = sendCommand("show net", 5000, "Links");
+    //printf("Response: %s\n\r", response.c_str());
+    if (response.find("Assoc=FAIL") != string::npos) {
+        printf("[DEBUG] Successfully disconnected from network\r\n");
+    } else {
+        printf("[ERROR] Failed in disconnecting from network.  Continuing ...\r\n");
+    }
+
+    wifiConnected = false;
+}
+
+bool Wifi::isConnected()
+{
+    //1) Check if SSID was set
+    if(_ssid.size() == 0) {
+        printf("[DEBUG] SSID is not set\r\n");
+        return false;
+    }
+
+    //1) Check that we do not have a live connection up
+    if(isOpen()) {
+        printf("[DEBUG] Socket is opened\r\n");
+        return true;
+    }
+
+    //Check command mode.
+    if(!setCmdMode(true)) {
+        return false;
+    }
+
+    //2) Query the wifi module
+    wifiConnected = false;
+    std::string result = sendCommand("show net", 5000, "Links");
+    //printf("netResult: %s\n\r", result);
+    if(result.find("Assoc=OK") != std::string::npos) {
+        wifiConnected = true;
+    }
+
+    return wifiConnected;
+}
+
+bool Wifi::bind(unsigned int port)
+{
+    if(socketOpened) {
+        printf("[ERROR] socket is open. Can not set local port\r\n");
+        return false;
+    }
+    if(port > 65535) {
+        printf("[ERROR] port out of range (0-65535)\r\n");
+        return false;
+    }
+    local_port = port;
+    return true;
+}
+
+bool Wifi::open(const std::string& address, unsigned int port, Mode mode)
+{
+    char buffer[256] = {0};
+    printf("[DEBUG] Attempting to Open Socket\r\n");
+
+    //1) Check that we do not have a live connection up
+    if(socketOpened) {
+        //Check that the address, port, and mode match
+        if(host_address != address || host_port != port || this->mode != mode) {
+            if(this->mode == TCP) {
+                printf("[ERROR] TCP socket already opened (%s:%d)\r\n", host_address.c_str(), host_port);
+            } else {
+                printf("[ERROR] UDP socket already opened (%s:%d)\r\n", host_address.c_str(), host_port);
+            }
+            return false;
+        }
+
+        printf("[DEBUG] Socket already opened\r\n");
+        return true;
+    }
+
+    //2) Check Parameters
+    if(port > 65535) {
+        printf("[ERROR] port out of range (0-65535)\r\n");
+        return false;
+    }
+
+
+    //3) Check Wifi network connection
+    if(!isConnected()) {
+        printf("[ERROR] Wifi network not connected.  Attempting to connect\r\n");
+        if(!connect()) {
+            printf("[ERROR] Wifi network connection failed\r\n");
+            return false;
+        } else {
+            printf("[DEBUG] Wifi connection established\r\n");
+        }
+    }
+
+    //Check command mode
+    if(!setCmdMode(true)) {
+        return false;
+    }
+
+    //Set Local Port
+    if(local_port != 0) {
+        //Attempt to set local port
+        sprintf(buffer, "set ip localport %d", local_port);
+        Code code = sendBasicCommand(buffer, 1000);
+        if(code != SUCCESS) {
+            printf("[WARNING] Unable to set local port (%d) [%d]. Continuing...\r\n", local_port, (int) code);
+        }
+    }
+
+    //Set TCP/UDP parameters
+    sprintf(buffer, "set ip remote %d", port);
+    if(sendBasicCommand(buffer, 1000) == SUCCESS) {
+        host_port = port;
+    } else {
+        printf("[ERROR] Host port could not be set\r\n");
+    }
+
+    //Check if address of URL
+    std::vector<std::string> tmp = Text::split(address, '.');
+    if(tmp.size() != 4) {
+        std::string ip = getHostByName(address);
+        if(ip.size() != 0) {
+            host_address = ip;
+        } else {
+            return false;
+        }
+    } else {
+        host_address = address;
+    }
+
+    //Set Address
+    printf("[DEBUG] Host address: %s\n\r", host_address.c_str());
+    if(sendBasicCommand("set ip host " + host_address, 1000) != SUCCESS) {
+        printf("[ERROR] Host address could not be set\r\n");
+        return false;
+    }
+
+    if(sendBasicCommand("set ip protocol 8", 1000) != SUCCESS) {
+        printf("[ERROR] Failed to set TCP mode\r\n");
+        return false;
+    }
+
+    // Try and Connect
+    std::string sMode;
+    std::string sOpenSocketCmd;
+    if(mode == TCP) {
+        sOpenSocketCmd = "open";
+        sMode = "TCP";
+    } else {
+        //TODO
+        //sOpenSocketCmd = "AT#OUDP";
+        //sMode = "UDP";
+    }
+    string response = sendCommand(sOpenSocketCmd, 10000, "OPEN");
+    if (response.find("OPEN") != string::npos) {
+        printf("[INFO] Opened %s Socket [%s:%d]\r\n", sMode.c_str(), host_address.c_str(), port);
+        socketOpened = true;
+        cmdOn = false;
+    } else {
+        printf("[WARNING] Unable to open %s Socket [%s:%d]\r\n", sMode.c_str(),  host_address.c_str(), port);
+        socketOpened = false;
+    }
+
+    return socketOpened;
+}
+
+bool Wifi::isOpen()
+{
+    if(io->readable()) {
+        printf("[DEBUG] Assuming open, data available to read.\n\r");
+        return true;
+    }
+    if(!setCmdMode(true)) {
+        printf("[ERROR] Failed to properly check if TCP connection is open.\r\n");
+        return socketOpened;
+    }
+    std::string response = sendCommand("show connection", 2000, "\n");
+    int start = response.find("f");
+    if(start != string::npos && response.size() >= (start + 3)) {
+        if(response[start + 3] == '1') {
+            socketOpened = true;
+        } else {
+            socketOpened = false;
+        }
+    } else {
+        printf("[WARNING] Trouble checking TCP Connection status.\n\r");
+    }
+    return socketOpened;
+}
+
+bool Wifi::close()
+{
+    wait(1);
+    if(io == NULL) {
+        printf("[ERROR] MTSBufferedIO not set\r\n");
+        return false;
+    }
+
+    if(!socketOpened) {
+        printf("[WARNING] Socket close() called, but socket was not open\r\n");
+        return true;
+    }
+
+    if(!setCmdMode(true)) {
+        printf("[ERROR] Failed to close socket\r\n");
+        return false;
+    }
+
+    if(isOpen()) {
+        std::string response = sendCommand("close", 3000, "CLOS");
+        if(response.find("CLOS") == string::npos) {
+            printf("[WARNING] Failed to successfully close socket...\r\n");
+            return false;
+        }
+    }
+
+    wait(1); //Wait so the subsequent isOpen calls return correctly.
+    io->rxClear();
+    io->txClear();
+
+    return true;
+}
+
+int Wifi::read(char* data, int max, int timeout)
+{
+    if(io == NULL) {
+        printf("[ERROR] MTSBufferedIO not set\r\n");
+        return -1;
+    }
+
+    //Check that nothing is in the rx buffer
+    if(!socketOpened && !io->readable()) {
+        printf("[ERROR] Socket is not open\r\n");
+        return -1;
+    }
+
+    //Check for data mode
+    if(!setCmdMode(false)) {
+        printf("[ERROR] Failed to read data due to mode\r\n");
+        return -1;
+    }
+
+    int bytesRead = 0;
+
+    if(timeout >= 0) {
+        bytesRead = io->read(data, max, static_cast<unsigned int>(timeout));
+    } else {
+        bytesRead = io->read(data, max);
+    }
+
+    return bytesRead;
+}
+
+int Wifi::write(const char* data, int length, int timeout)
+{
+    if(io == NULL) {
+        printf("[ERROR] MTSBufferedIO not set\r\n");
+        return -1;
+    }
+
+    if(!socketOpened) {
+        printf("[ERROR] Socket is not open\r\n");
+        return -1;
+    }
+
+    //Check for data mode
+    if(!setCmdMode(false)) {
+        printf("[ERROR] Failed to write data due to mode\r\n");
+        return -1;
+    }
+
+    int bytesWritten = 0;
+
+    if(timeout >= 0) {
+        bytesWritten = io->write(data, length, static_cast<unsigned int>(timeout));
+    } else {
+        bytesWritten = io->write(data, length);
+    }
+
+    return bytesWritten;
+}
+
+unsigned int Wifi::readable()
+{
+    if(io == NULL) {
+        printf("[ERROR] MTSBufferedIO not set\r\n");
+        return 0;
+    }
+    if(!socketOpened) {
+        printf("[ERROR] Socket is not open\r\n");
+        return 0;
+    }
+    return io->readable();
+}
+
+unsigned int Wifi::writeable()
+{
+    if(io == NULL) {
+        printf("[ERROR] MTSBufferedIO not set\r\n");
+        return 0;
+    }
+    if(!socketOpened) {
+        printf("[ERROR] Socket is not open\r\n");
+        return 0;
+    }
+
+    return io->writeable();
+}
+
+void Wifi::reset()
+{
+    if(!sortInterfaceMode()) {
+        return;
+    }
+
+    sendCommand("factory RESET", 2000, "Set Factory Default"); // <ver> comes out about 1 sec later
+    wait(0.5f);
+    sendCommand("reboot", 2000, "*READY*");
+
+    wifiConnected = false;
+    _ssid = "";
+    mode = TCP;
+    socketOpened = false;
+    socketCloseable = true;
+    local_port = 0;
+    local_address = "";
+    host_port = 0;
+    cmdOn = false;
+    wait(1);
+//    if(!init(io)) {
+//        printf("[ERROR] Failed to reinitialize after reset.\n\r");
+//    }
+}
+
+Code Wifi::setDeviceIP(std::string address)
+{
+    //Check for command mode
+    if(!setCmdMode(true)) {
+        printf("[ERROR] Failed to set IP due to mode issue\r\n");
+        return FAILURE;
+    }
+
+    //Set to DHCP mode
+    if(address.compare("DHCP") == 0) {
+        return sendBasicCommand("set ip dhcp 1", 1000);
+    }
+
+    //Set to static mode and set address
+    Code code = sendBasicCommand("set ip address " + address, 1000);
+    if(code != SUCCESS) {
+        return code;
+    }
+    code = sendBasicCommand("set ip dhcp 0", 1000);
+    if(code != SUCCESS) {
+        return code;
+    }
+    local_address = address;
+    return SUCCESS;
+}
+
+std::string Wifi::getDeviceIP()
+{
+    return local_address;
+}
+
+Code Wifi::setNetwork(const std::string& ssid, SecurityType type, const std::string& key)
+{
+    //Check the command mode
+    if(!setCmdMode(true)) {
+        return FAILURE;
+    }
+
+    Code code;
+
+    //Set the appropraite SSID
+    code = sendBasicCommand("set wlan ssid " + ssid, 1000);
+    if (code != SUCCESS) {
+        return code;
+    }
+
+    //Set the security key
+    if (type == WEP64 || type == WEP128) {
+        //Set the WEP key if using WEP encryption
+        code = sendBasicCommand("set wlan key " + key, 1000);
+        if (code != SUCCESS) {
+            return code;
+        }
+    } else if (type == WPA || type == WPA2) {
+        //Set the WPA key if using WPA encryption
+        code = sendBasicCommand("set wlan phrase " + key, 1000);
+        if (code != SUCCESS) {
+            return code;
+        }
+    }
+
+    _ssid = ssid;
+    return SUCCESS;
+}
+
+Code Wifi::setDNS(const std::string& dnsName)
+{
+    //Check the command mode
+    if(!setCmdMode(true)) {
+        return FAILURE;
+    }
+
+    return sendBasicCommand("set dns name " + dnsName, 1000);
+}
+
+int Wifi::getSignalStrength()
+{
+    //Signal strength does not report correctly if not connected
+    if(!wifiConnected) {
+        printf("[ERROR] Could not get RSSI, Wifi network not connected.\n\r");
+        return 99;
+    }
+
+    //Check the command mode
+    if(!setCmdMode(true)) {
+        printf("[ERROR] Could not get RSSI\n\r");
+        return 99;
+    }
+
+    string response = sendCommand("show rssi", 2000, "dBm");
+    if (response.find("RSSI") == string::npos) {
+        printf("[ERROR] Could not get RSSI\n\r");
+        return 99;
+    }
+    int start = response.find('(');
+    int stop = response.find(')', start);
+    string signal = response.substr(start + 1, stop - start - 1);
+    int value;
+    sscanf(signal.c_str(), "%d", &value);
+    return value;
+}
+
+bool Wifi::ping(const std::string& address)
+{
+    //Check the command mode
+    if(!setCmdMode(true)) {
+        printf("[ERROR] Could not send ping command\n\r");
+        return false;
+    }
+
+    std::string response;
+    for (int i = 0; i < PINGNUM; i++) {
+        response = sendCommand("ping " + address, PINGDELAY * 1000, "reply");
+        if (response.find("reply") != std::string::npos) {
+            return true;
+        }
+    }
+    return false;
+}
+
+bool Wifi::setCmdMode(bool on)
+{
+    if (on) {
+        if (cmdOn) {
+            return true;
+        }
+        wait(.5);
+        std::string response = sendCommand("$$", 2000, "CMD", '$');
+        if (response.find("CMD") != string::npos) {
+            cmdOn = true;
+            wait(.5);
+            return true;
+        }
+        printf("[ERROR] Failed to enter command mode\n\r");
+        return false;
+    } else {
+        if (!cmdOn) {
+            return true;
+        }
+        std::string response = sendCommand("exit", 2000, "EXIT");
+        if (response.find("EXIT") != string::npos) {
+            cmdOn = false;
+            return true;
+        }
+        printf("[ERROR] Failed to exit command mode\n\r");
+        return false;
+    }
+}
+
+std::string Wifi::getHostByName(std::string url)
+{
+    std::string response = sendCommand("lookup " + url, 3000, "<4.00>");
+    int start = response.find("=");
+    int stop = response.find("\r");
+    if(start == string::npos || stop == string::npos) {
+        printf("[ERROR] Failed to resolve URL [%s]", response.c_str());
+        return "";
+    }
+    std::string ip = response.substr(start + 1, stop - start - 1);
+    //printf("Data: %s\n\r", ip);
+    return ip;
+}
+
+Code Wifi::sendBasicCommand(string command, int timeoutMillis, char esc)
+{
+    if(socketOpened) {
+        printf("[ERROR] socket is open. Can not send AT commands\r\n");
+        return ERROR;
+    }
+
+    string response = sendCommand(command, timeoutMillis, "AOK", esc);
+    //printf("Response: %s\n\r", response.c_str());
+    if (response.size() == 0) {
+        return NO_RESPONSE;
+    } else if (response.find("AOK") != string::npos) {
+        return SUCCESS;
+    } else if (response.find("ERR") != string::npos) {
+        return ERROR;
+    } else {
+        return FAILURE;
+    }
+}
+
+string Wifi::sendCommand(string command, int timeoutMillis, std::string response, char esc)
+{
+    if(io == NULL) {
+        printf("[ERROR] MTSBufferedIO not set\r\n");
+        return "";
+    }
+    //if(socketOpened && command.compare("$$") != 0 && command.compare("exit") != 0 && command.compare("close") != 0) {
+    //    printf("[ERROR] socket is open. Can not send AT commands\r\n");
+    //    return "";
+    //}
+
+    io->rxClear();
+    io->txClear();
+    std::string result;
+
+    //Attempt to write command
+    if(io->write(command.data(), command.size(), timeoutMillis) != command.size()) {
+        //Failed to write command
+        printf("[ERROR] failed to send command to radio within %d milliseconds\r\n", timeoutMillis);
+        return "";
+    }
+    //Send Escape Character
+    if (esc != 0x00) {
+        if(io->write(esc, timeoutMillis) != 1) {
+            printf("[ERROR] failed to send '%c' to radio within %d milliseconds\r\n", esc, timeoutMillis);
+            return "";
+        }
+    }
+    DBG("Sending: %s%c", command.data(), esc);
+
+    int timer = 0;
+    size_t previous = 0;
+    char tmp[256];
+    tmp[255] = 0;
+    bool done = false;
+    do {
+        wait(.2);
+        timer = timer + 200;
+        previous = result.size();
+        int size = io->read(tmp, 255, 0);    //1 less than allocated
+        if(size > 0) {
+            result.append(tmp, size);
+            if (response.size() != 0) {
+                if (result.find(response) != string::npos) {
+                    goto exit_func;
+                    //return result;
+                }
+            } else {
+                done =  (result.size() == previous);
+            }
+        }
+        if(timer >= timeoutMillis) {
+            if(!(command.compare("reboot") == 0 || command.compare("") == 0)) {
+                printf("[WARNING] sendCommand [%s] timed out after %d milliseconds\r\n", command.c_str(), timeoutMillis);
+            }
+            done = true;
+        }
+    } while (!done);
+
+exit_func:
+    DBG("Result: %s\n\r", result.c_str());
+    return result;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SocketModem/wifi/Wifi.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,276 @@
+/* Universal Socket Modem Interface Library
+* Copyright (c) 2013 Multi-Tech Systems
+*
+* 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 WIFI_H
+#define WIFI_H
+
+#include "IPStack.h"
+#include "MTSBufferedIO.h"
+#include "mbed.h"
+#include <string>
+#include <vector>
+
+using namespace mts;
+
+/** This is a class for communicating with a Roving Networks RN-171 Wifi module. This
+* module comes in a variety of form factors including the Multi-Tech SocketShield.
+* This class supports two main types of WiFi module interactions including:
+* configuration and status command processing and TCP Socket
+* data connections. It should be noted that while a data connection is open the module
+* must be put in command mode before commands can be sent. This is handled within the class
+* automatically for all native commands. This class also inherits from IPStack
+* providing a common set of commands for communication devices that have an onboard
+* IP Stack. It is also integrated with the standard mbed Sockets package and can therefore
+* be used seamlessly with clients and services built on top of this interface already within
+* the mbed library.
+*
+* All of the following examples use the Pin Names for the Freedom KL46Z board coupled with
+* the SocketModem Shield Arduino compatible board. Please chage Pin Names accordingly to
+* match your hardware configuration. The default baud rate for the WiFi module is 9600 bps.
+*
+* The following example shows how to connect to a WiFi netork and perform a basic ping test:
+* @code
+* #include "mbed.h"
+* #include "MTSSerial.h"
+* #include "Wifi.h"
+* using namespace mts;
+*
+* int main()
+* {
+*   std::string ssid = "Your SSID goes here";
+*   std::string securityKey = "Your secuirty key goes here";
+*   Wifi::SecurityType securityType = Wifi::WPA2;
+*
+*   //Wait for wifi module to boot up
+*   for (int i = 10; i >= 0; i = i - 2) {
+*       wait(2);
+*       printf("Waiting %d seconds...\n\r", i);
+*   }
+*
+*   //Setup serial interface to WiFi module
+*   MTSSerial* serial = new MTSSerial(PTD3, PTD2, 256, 256);
+*   serial->baud(9600);
+*
+*   //Setup Wifi class
+*   Wifi* wifi = Wifi::getInstance();
+*   printf("Init: %s\n\r", wifi->init(serial) ? "SUCCESS" : "FAILURE");
+*
+*   //Setup and check connection
+*   printf("Set Network: %s\n\r", getCodeNames(wifi->setNetwork(ssid, securityType, securityKey)).c_str());
+*   printf("Set DHCP: %s\n\r", getCodeNames(wifi->setDeviceIP("DHCP")).c_str());
+*   printf("Connect: %s\n\r", wifi->connect() ? "Success" : "Failure");
+*   printf("Is Connected: %s\n\r", wifi->isConnected() ? "True" : "False");
+*   printf("Ping Server: %s\n\r", wifi->ping("8.8.8.8") ? "Success" : "Failed");
+*
+*   //Disconnect from network
+*   printf("Disconnecting...\n\r");
+*   wifi->disconnect();
+*   printf("Is Connected: %s\n\r", wifi->isConnected() ? "True" : "False");
+*
+*   printf("End Program\n\r");
+* }
+* @endcode
+*/
+class Wifi : public IPStack
+{
+public:
+    ///An enumeration for all the supported WiFi security types.
+    enum SecurityType {
+        NONE, WEP64, WEP128, WPA, WPA2
+    };
+
+    /** Destructs a Wifi object and frees all related resources.
+    */
+    ~Wifi();
+
+    /** This static function is used to create or get a reference to a
+    * Wifi object. Wifi uses the singleton pattern, which means
+    * that you can only have one existing at a time. The first time you
+    * call getInstance this method creates a new uninitialized Wifi
+    * object and returns it. All future calls to this method will return
+    * a reference to the instance created during the first call. Note that
+    * you must call init on the returned instance before mnaking any other
+    * calls. If using this class's bindings to any of the Socket package
+    * classes like TCPSocketConnection, you must call this method and the
+    * init method on the returned object first, before even creating the
+    * other objects.
+    *
+    * @returns a reference to the single Wifi obect that has been created.
+    */
+    static Wifi* getInstance();
+
+    /** This method initializes the object with the underlying Wifi module
+    * interface to use. Note that this function MUST be called before
+    * any other calls will function correctly on a Wifi object. Also
+    * note that MTSBufferedIO is abstract, so you must use one of
+    * its inherited classes like MTSSerial or MTSSerialFlowControl.
+    *
+    * @param io the buffered io interface that is attached to the wifi
+    * radio module.
+    * @returns true if the init was successful, otherwise false.
+    */
+    bool init(MTSBufferedIO* io);
+
+    /** This method establishes a network connection on the Wif radio module.
+    * Note that before calling you NEED to first set the network information
+    * including WiFi SSID and optional security key using the setNetwork
+    * method.
+    *
+    * @returns true if the connection was successfully established, otherwise
+    * false on an error.
+    */
+    virtual bool connect();
+
+    /** This method is used to stop a previously established Wifi network connection.
+    */
+    virtual void disconnect();
+
+    /** This method is used to check if the radio currently has a Wifi network
+    * connection established.
+    *
+    * @returns true if a network connection exists, otherwise false.
+    */
+    virtual bool isConnected();
+
+    // TCP and UDP Socket related commands
+    // For behavior of the following methods refer to IPStack.h documentation
+    virtual bool bind(unsigned int port);
+    virtual bool open(const std::string& address, unsigned int port, Mode mode);
+    virtual bool isOpen();
+    virtual bool close();
+    virtual int read(char* data, int max, int timeout = -1);
+    virtual int write(const char* data, int length, int timeout = -1);
+    virtual unsigned int readable();
+    virtual unsigned int writeable();
+
+    /** This method performs a soft reboot of the device by issuing the
+    * reboot command. If the module is not able to respond to commands
+    * this will not work. This method also waits 10 seconds for the
+    * module to perform the reset and return.
+    */
+    virtual void reset();
+
+    /** A method for sending a generic text command to the radio. Note that you cannot
+    * send commands and have a socket connection at the same time, unless you first
+    * switch to command mode.
+    *
+    * @param command the command to send to the WiFi module without the escape character.
+    * @param timeoutMillis the time in millis to wait for a response before returning.
+    * @param response the text string to look for and to return immediately after finding.
+    * The default is to look for no specific response.
+    * @param esc escape character to add at the end of the command, defaults to
+    * carriage return (CR).  Does not append any character if esc == 0.
+    * @returns all data received from the radio after the command as a string.
+    */
+    std::string sendCommand(std::string command, int timeoutMillis, std::string response = "", char esc = CR);
+
+    /** A method for sending a basic command to the radio. A basic text command is
+    * one that simply has a response of either AOK or ERR without any other information.
+    * Note that you cannot send commands and have a tcp connection at the same time
+    * unless you first switch to command mode.
+    *
+    * @param command the command to send to the WiFi module without the escape character.
+    * @param timeoutMillis the time in millis to wait for a response before returning.
+    * @param esc escape character to add at the end of the command, defaults to
+    * carriage return (CR).
+    * @returns the standard Code enumeration.
+    */
+    Code sendBasicCommand(std::string command, int timeoutMillis, char esc = CR);
+
+    /** This method is used to set the network information details. This method must be
+    * called before connect, which establishes the WiFi network connection.
+    *
+    * @param ssid the SSID for the network you want to attached to.
+    * @param type the type of security used on the network. The default is NONE.
+    * @param key the security key for the network. The default is no key.
+    */
+    Code setNetwork(const std::string& ssid, SecurityType type = NONE, const std::string& key = "");
+
+    /** This method is used to set the IP address or puts the module in DHCP mode.
+    *
+    * @param address the IP address you want to use in the form of xxx.xxx.xxx.xxx or DHCP
+    * if you want to use DHCP. The default is DHCP.
+    * @returns the standard Code enumeration.
+    */
+    Code setDeviceIP(std::string address = "DHCP");
+
+    /** This method is used to get the IP address of the device, which can be
+    * set either statically or via DHCP after connecting to a network.
+    *
+    * @returns the devices IP address.
+    */
+    std::string getDeviceIP();
+
+    /** This method is used to set the DNS which enables the use of URLs instead
+    * of IP addresses when making a socket connection.
+    *
+    * @param the DNS server address as a string in form xxx.xxx.xxx.xxx.
+    * @returns the standard AT Code enumeration.
+    */
+    Code setDNS(const std::string& dnsName);
+
+    /** A method for getting the signal strength of the Wifi module. This method allows
+    * you to get the signal strength in dBm. If you get a result of 99 the signal strength
+    * is not known or there was an error in reading it. Note that you cannot read the signal
+    * strength unless you are already attached to a Wifi network.
+    *
+    * @returns an integer representing the signal strength in dBm.
+    */
+    int getSignalStrength();
+
+    /** This method is used test network connectivity by pinging a server.
+    *
+    * @param address the address of the server in format xxx.xxx.xxx.xxx.
+    * @returns true if the ping was successful, otherwise false.
+    */
+    bool ping(const std::string& address = "8.8.8.8");
+
+    /** This method is used to set whether the device is in command mode or data mode.
+    * In command mode you are able to send configuration and status commands while
+    * data mode is used for sending data when you have an open socket connection.
+    * Note that for all other methods in this class the change is handled automatically.
+    * Only use this methodif you want to send your own commands that are not already
+    * supported and need to make sure that you are in command mode.
+    *
+    * @param on if true sets to command mode, otherwise to data mode.
+    * @returns true if the change was successful, otherwise false.
+    */
+    bool setCmdMode(bool on);
+
+private:
+    static Wifi* instance; //Static pointer to the single Cellular object.
+
+    MTSBufferedIO* io; //IO interface obect that the radio is accessed through.
+
+    bool wifiConnected; //Specifies if a Wifi network session is currently connected.
+    std::string _ssid; //A string that holds the SSID for the Wifi module.
+
+    Mode mode; //The current socket Mode.
+    bool socketOpened; //Specifies if a Socket is presently opened.
+    bool socketCloseable; //Specifies is a Socket can be closed.
+    unsigned int local_port; //Holds the local port for socket connections.
+    std::string local_address; //Holds the local address for socket connections.
+    unsigned int host_port; //Holds the remote port for socket connections.
+    std::string host_address; //Holds the remote address for socket connections.
+    bool cmdOn; //Determines whether the device is in command mode or not
+
+    Wifi(); //Private constructor, use the getInstance() method.
+    Wifi(MTSBufferedIO* io); //Private constructor, use the getInstance() method.
+    bool sortInterfaceMode(void); // module gets in wierd state without IO reset
+    std::string getHostByName(std::string url); // Gets the IP address for a URL
+};
+
+#endif /* WIFI_H */
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TSI.lib	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/vsluiter/code/TSI/#4dc2f5a3a731
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TSI/TSISensor.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,254 @@
+/* Freescale Semiconductor Inc.
+ * (c) Copyright 2004-2005 Freescale Semiconductor, Inc.
+ * (c) Copyright 2001-2004 Motorola, Inc. 
+ *
+ * mbed Microcontroller Library
+ * (c) Copyright 2009-2012 ARM Limited.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "mbed.h"
+#include "TSISensor.h"
+
+#define NO_TOUCH                 0
+#define SLIDER_LENGTH           40 //LENGTH in mm
+#define TOTAL_ELECTRODE          3
+
+#define TSI0a        0
+#define TSI1         1
+#define TSI2         2
+#define TSI3         3
+#define TSI4         4
+#define TSI5         5
+#define TSI6         6
+#define TSI7         7
+#define TSI8         8
+#define TSI9         9
+#define TSI10        10
+#define TSI11        11
+#define TSI12        12
+#define TSI13        13
+#define TSI14        14
+#define TSI15        15
+
+/*Chose the correct TSI channel for the electrode number*/
+#define ELECTRODE0   TSI9
+#define ELECTRODE1   TSI10
+#define ELECTRODE2   TSI0a
+#define ELECTRODE3   TSI1
+#define ELECTRODE4   TSI2
+#define ELECTRODE5   TSI3
+#define ELECTRODE6   TSI4
+#define ELECTRODE7   TSI5
+#define ELECTRODE8   TSI6
+#define ELECTRODE9   TSI7
+#define ELECTRODE10  TSI8
+#define ELECTRODE11  TSI11
+#define ELECTRODE12  TSI12
+#define ELECTRODE13  TSI13
+#define ELECTRODE14  TSI14
+#define ELECTRODE15  TSI15
+
+#define THRESHOLD0   100
+#define THRESHOLD1   100
+#define THRESHOLD2   100
+#define THRESHOLD3   100
+#define THRESHOLD4   100
+#define THRESHOLD5   100
+#define THRESHOLD6   100
+#define THRESHOLD7   100
+#define THRESHOLD8   100
+#define THRESHOLD9   100
+#define THRESHOLD10   100
+#define THRESHOLD11   100
+#define THRESHOLD12   100
+#define THRESHOLD13   100
+#define THRESHOLD14   100
+#define THRESHOLD15   100
+
+static uint8_t total_electrode = TOTAL_ELECTRODE;
+static uint8_t elec_array[16]={ELECTRODE0,ELECTRODE1,ELECTRODE2,ELECTRODE3,ELECTRODE4,ELECTRODE5,
+                               ELECTRODE6,ELECTRODE7,ELECTRODE8,ELECTRODE9,ELECTRODE10,ELECTRODE11,
+                               ELECTRODE12,ELECTRODE13,ELECTRODE14,ELECTRODE15};
+static uint16_t gu16TSICount[16];
+static uint16_t gu16Baseline[16];
+static uint16_t gu16Threshold[16]={THRESHOLD0,THRESHOLD1,THRESHOLD2,THRESHOLD3,THRESHOLD4,THRESHOLD5,
+                                   THRESHOLD6,THRESHOLD7,THRESHOLD8,THRESHOLD9,THRESHOLD10,THRESHOLD11,
+                                   THRESHOLD12,THRESHOLD13,THRESHOLD14,THRESHOLD15};
+static uint16_t gu16Delta[16];
+static uint8_t ongoing_elec;
+static uint8_t end_flag = 1;
+
+static uint8_t SliderPercentegePosition[2] = {NO_TOUCH,NO_TOUCH};
+static uint8_t SliderDistancePosition[2] = {NO_TOUCH,NO_TOUCH};
+static uint32_t AbsolutePercentegePosition = NO_TOUCH;
+static uint32_t AbsoluteDistancePosition = NO_TOUCH;
+
+static void tsi_irq();
+
+TSISensor::TSISensor() {
+    SIM->SCGC5 |= SIM_SCGC5_PORTB_MASK;
+    SIM->SCGC5 |= SIM_SCGC5_TSI_MASK;
+
+    TSI0->GENCS |= (TSI_GENCS_ESOR_MASK
+                   | TSI_GENCS_MODE(0)
+                   | TSI_GENCS_REFCHRG(4)
+                   | TSI_GENCS_DVOLT(0)
+                   | TSI_GENCS_EXTCHRG(7)
+                   | TSI_GENCS_PS(4)
+                   | TSI_GENCS_NSCN(11)
+                   | TSI_GENCS_TSIIEN_MASK
+                   | TSI_GENCS_STPE_MASK
+                   );
+
+    TSI0->GENCS |= TSI_GENCS_TSIEN_MASK;
+
+    NVIC_SetVector(TSI0_IRQn, (uint32_t)&tsi_irq);
+    NVIC_EnableIRQ(TSI0_IRQn);
+
+    selfCalibration();
+}
+
+void TSISensor::TSISensor_reset(void) {
+    SIM->SCGC5 |= SIM_SCGC5_PORTB_MASK;
+    SIM->SCGC5 |= SIM_SCGC5_TSI_MASK;
+
+    TSI0->GENCS |= (TSI_GENCS_ESOR_MASK
+                   | TSI_GENCS_MODE(0)
+                   | TSI_GENCS_REFCHRG(4)
+                   | TSI_GENCS_DVOLT(0)
+                   | TSI_GENCS_EXTCHRG(7)
+                   | TSI_GENCS_PS(4)
+                   | TSI_GENCS_NSCN(11)
+                   | TSI_GENCS_TSIIEN_MASK
+                   | TSI_GENCS_STPE_MASK
+                   );
+
+    TSI0->GENCS |= TSI_GENCS_TSIEN_MASK;
+
+    //NVIC_SetVector(TSI0_IRQn, (uint32_t)&tsi_irq);
+    //NVIC_EnableIRQ(TSI0_IRQn);
+
+    selfCalibration();
+}
+
+void TSISensor::selfCalibration(void)
+{
+    unsigned char cnt;
+    unsigned char trigger_backup;
+
+    TSI0->GENCS |= TSI_GENCS_EOSF_MASK;      // Clear End of Scan Flag
+    TSI0->GENCS &= ~TSI_GENCS_TSIEN_MASK;    // Disable TSI module
+
+    if(TSI0->GENCS & TSI_GENCS_STM_MASK)     // Back-up TSI Trigger mode from Application
+        trigger_backup = 1;
+    else
+        trigger_backup = 0;
+
+    TSI0->GENCS &= ~TSI_GENCS_STM_MASK;      // Use SW trigger
+    TSI0->GENCS &= ~TSI_GENCS_TSIIEN_MASK;    // Enable TSI interrupts
+
+    TSI0->GENCS |= TSI_GENCS_TSIEN_MASK;     // Enable TSI module
+
+    for(cnt=0; cnt < total_electrode; cnt++)  // Get Counts when Electrode not pressed
+    {
+        TSI0->DATA = ((elec_array[cnt] << TSI_DATA_TSICH_SHIFT) );
+        TSI0->DATA |= TSI_DATA_SWTS_MASK;
+        while(!(TSI0->GENCS & TSI_GENCS_EOSF_MASK));
+        TSI0->GENCS |= TSI_GENCS_EOSF_MASK;
+        gu16Baseline[cnt] = (TSI0->DATA & TSI_DATA_TSICNT_MASK);
+    }
+
+    TSI0->GENCS &= ~TSI_GENCS_TSIEN_MASK;    // Disable TSI module
+    TSI0->GENCS |= TSI_GENCS_TSIIEN_MASK;     // Enale TSI interrupt
+    if(trigger_backup)                      // Restore trigger mode
+        TSI0->GENCS |= TSI_GENCS_STM_MASK;
+    else
+        TSI0->GENCS &= ~TSI_GENCS_STM_MASK;
+
+    TSI0->GENCS |= TSI_GENCS_TSIEN_MASK;     // Enable TSI module
+
+    TSI0->DATA = ((elec_array[0]<<TSI_DATA_TSICH_SHIFT) );
+    TSI0->DATA |= TSI_DATA_SWTS_MASK;
+}
+
+void TSISensor::sliderRead(void ) {
+    if(end_flag) {
+        end_flag = 0;
+        if((gu16Delta[0] > gu16Threshold[0])||(gu16Delta[1] > gu16Threshold[1])) {
+            SliderPercentegePosition[0] = (gu16Delta[0]*100)/(gu16Delta[0]+gu16Delta[1]);
+            SliderPercentegePosition[1] = (gu16Delta[1]*100)/(gu16Delta[0]+gu16Delta[1]);
+            SliderDistancePosition[0] = (SliderPercentegePosition[0]* SLIDER_LENGTH)/100;
+            SliderDistancePosition[1] = (SliderPercentegePosition[1]* SLIDER_LENGTH)/100;
+            AbsolutePercentegePosition = ((100 - SliderPercentegePosition[0]) + SliderPercentegePosition[1])/2;
+            AbsoluteDistancePosition = ((SLIDER_LENGTH - SliderDistancePosition[0]) + SliderDistancePosition[1])/2;
+         } else {
+            SliderPercentegePosition[0] = NO_TOUCH;
+            SliderPercentegePosition[1] = NO_TOUCH;
+            SliderDistancePosition[0] = NO_TOUCH;
+            SliderDistancePosition[1] = NO_TOUCH;
+            AbsolutePercentegePosition = NO_TOUCH;
+            AbsoluteDistancePosition = NO_TOUCH;
+         }
+    }
+}
+
+float TSISensor::readPercentage() {
+    sliderRead();
+    return (float)AbsolutePercentegePosition/100.0;
+}
+
+uint8_t TSISensor::readDistance() {
+    sliderRead();
+    return AbsoluteDistancePosition;
+}
+
+uint16_t TSISensor::readValue(uint8_t index)
+{
+    return gu16TSICount[index];
+}
+
+static void changeElectrode(void)
+{
+    int16_t u16temp_delta;
+
+    gu16TSICount[ongoing_elec] = (TSI0->DATA & TSI_DATA_TSICNT_MASK);          // Save Counts for current electrode
+    u16temp_delta = gu16TSICount[ongoing_elec] - gu16Baseline[ongoing_elec];  // Obtains Counts Delta from callibration reference
+    if(u16temp_delta < 0)
+        gu16Delta[ongoing_elec] = 0;
+    else
+        gu16Delta[ongoing_elec] = u16temp_delta;
+
+    //Change Electrode to Scan
+    if(total_electrode > 1)  
+    {
+        if((total_electrode-1) > ongoing_elec)
+            ongoing_elec++;
+        else
+            ongoing_elec = 0;
+
+        TSI0->DATA = ((elec_array[ongoing_elec]<<TSI_DATA_TSICH_SHIFT) );
+        TSI0->DATA |= TSI_DATA_SWTS_MASK;
+    }
+}
+
+void tsi_irq(void)
+{
+    end_flag = 1;
+    TSI0->GENCS |= TSI_GENCS_EOSF_MASK; // Clear End of Scan Flag
+    changeElectrode();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TSI/TSISensor.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,73 @@
+/* Freescale Semiconductor Inc.
+ * (c) Copyright 2004-2005 Freescale Semiconductor, Inc.
+ * (c) Copyright 2001-2004 Motorola, Inc. 
+ *
+ * mbed Microcontroller Library
+ * (c) Copyright 2009-2012 ARM Limited.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef TSISENSOR_H
+#define TSISENSOR_H
+
+/**
+* TSISensor example
+*
+* @code
+* #include "mbed.h"
+* #include "TSISensor.h"
+* 
+* int main(void) {
+*     PwmOut led(LED_GREEN);
+*     TSISensor tsi;
+*     
+*     while (true) {
+*         led = 1.0 - tsi.readPercentage();
+*         wait(0.1);
+*     }
+* }
+* @endcode
+*/
+class TSISensor {
+public:
+    /**
+     *   Initialize the TSI Touch Sensor
+     */
+    TSISensor();
+
+    /**
+     * Read Touch Sensor percentage value
+     *
+     * @returns percentage value between [0 ... 1]
+     */
+    float readPercentage();
+
+    /**
+     * Read Touch Sensor distance
+     *
+     * @returns distance in mm. The value is between [0 ... 40]
+     */
+    uint8_t readDistance();
+    uint16_t readValue(uint8_t);
+    void TSISensor_reset(void);
+
+private:
+    void sliderRead(void);
+    void selfCalibration(void);
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cJSON/README.txt	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,248 @@
+/*
+  Copyright (c) 2009 Dave Gamble
+
+  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.
+*/
+
+Welcome to cJSON.
+
+cJSON aims to be the dumbest possible parser that you can get your job done with.
+It's a single file of C, and a single header file.
+
+JSON is described best here: http://www.json.org/
+It's like XML, but fat-free. You use it to move data around, store things, or just
+generally represent your program's state.
+
+
+First up, how do I build?
+Add cJSON.c to your project, and put cJSON.h somewhere in the header search path.
+For example, to build the test app:
+
+gcc cJSON.c test.c -o test -lm
+./test
+
+
+As a library, cJSON exists to take away as much legwork as it can, but not get in your way.
+As a point of pragmatism (i.e. ignoring the truth), I'm going to say that you can use it
+in one of two modes: Auto and Manual. Let's have a quick run-through.
+
+
+I lifted some JSON from this page: http://www.json.org/fatfree.html
+That page inspired me to write cJSON, which is a parser that tries to share the same
+philosophy as JSON itself. Simple, dumb, out of the way.
+
+Some JSON:
+{
+    "name": "Jack (\"Bee\") Nimble", 
+    "format": {
+        "type":       "rect", 
+        "width":      1920, 
+        "height":     1080, 
+        "interlace":  false, 
+        "frame rate": 24
+    }
+}
+
+Assume that you got this from a file, a webserver, or magic JSON elves, whatever,
+you have a char * to it. Everything is a cJSON struct.
+Get it parsed:
+	cJSON *root = cJSON_Parse(my_json_string);
+
+This is an object. We're in C. We don't have objects. But we do have structs.
+What's the framerate?
+
+	cJSON *format = cJSON_GetObjectItem(root,"format");
+	int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint;
+
+
+Want to change the framerate?
+	cJSON_GetObjectItem(format,"frame rate")->valueint=25;
+	
+Back to disk?
+	char *rendered=cJSON_Print(root);
+
+Finished? Delete the root (this takes care of everything else).
+	cJSON_Delete(root);
+
+That's AUTO mode. If you're going to use Auto mode, you really ought to check pointers
+before you dereference them. If you want to see how you'd build this struct in code?
+	cJSON *root,*fmt;
+	root=cJSON_CreateObject();	
+	cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
+	cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
+	cJSON_AddStringToObject(fmt,"type",		"rect");
+	cJSON_AddNumberToObject(fmt,"width",		1920);
+	cJSON_AddNumberToObject(fmt,"height",		1080);
+	cJSON_AddFalseToObject (fmt,"interlace");
+	cJSON_AddNumberToObject(fmt,"frame rate",	24);
+
+Hopefully we can agree that's not a lot of code? There's no overhead, no unnecessary setup.
+Look at test.c for a bunch of nice examples, mostly all ripped off the json.org site, and
+a few from elsewhere.
+
+What about manual mode? First up you need some detail.
+Let's cover how the cJSON objects represent the JSON data.
+cJSON doesn't distinguish arrays from objects in handling; just type.
+Each cJSON has, potentially, a child, siblings, value, a name.
+
+The root object has: Object Type and a Child
+The Child has name "name", with value "Jack ("Bee") Nimble", and a sibling:
+Sibling has type Object, name "format", and a child.
+That child has type String, name "type", value "rect", and a sibling:
+Sibling has type Number, name "width", value 1920, and a sibling:
+Sibling has type Number, name "height", value 1080, and a sibling:
+Sibling hs type False, name "interlace", and a sibling:
+Sibling has type Number, name "frame rate", value 24
+
+Here's the structure:
+typedef struct cJSON {
+	struct cJSON *next,*prev;
+	struct cJSON *child;
+
+	int type;
+
+	char *valuestring;
+	int valueint;
+	double valuedouble;
+
+	char *string;
+} cJSON;
+
+By default all values are 0 unless set by virtue of being meaningful.
+
+next/prev is a doubly linked list of siblings. next takes you to your sibling,
+prev takes you back from your sibling to you.
+Only objects and arrays have a "child", and it's the head of the doubly linked list.
+A "child" entry will have prev==0, but next potentially points on. The last sibling has next=0.
+The type expresses Null/True/False/Number/String/Array/Object, all of which are #defined in
+cJSON.h
+
+A Number has valueint and valuedouble. If you're expecting an int, read valueint, if not read
+valuedouble.
+
+Any entry which is in the linked list which is the child of an object will have a "string"
+which is the "name" of the entry. When I said "name" in the above example, that's "string".
+"string" is the JSON name for the 'variable name' if you will.
+
+Now you can trivially walk the lists, recursively, and parse as you please.
+You can invoke cJSON_Parse to get cJSON to parse for you, and then you can take
+the root object, and traverse the structure (which is, formally, an N-tree),
+and tokenise as you please. If you wanted to build a callback style parser, this is how
+you'd do it (just an example, since these things are very specific):
+
+void parse_and_callback(cJSON *item,const char *prefix)
+{
+	while (item)
+	{
+		char *newprefix=malloc(strlen(prefix)+strlen(item->name)+2);
+		sprintf(newprefix,"%s/%s",prefix,item->name);
+		int dorecurse=callback(newprefix, item->type, item);
+		if (item->child && dorecurse) parse_and_callback(item->child,newprefix);
+		item=item->next;
+		free(newprefix);
+	}
+}
+
+The prefix process will build you a separated list, to simplify your callback handling.
+The 'dorecurse' flag would let the callback decide to handle sub-arrays on it's own, or
+let you invoke it per-item. For the item above, your callback might look like this:
+
+int callback(const char *name,int type,cJSON *item)
+{
+	if (!strcmp(name,"name"))	{ /* populate name */ }
+	else if (!strcmp(name,"format/type")	{ /* handle "rect" */ }
+	else if (!strcmp(name,"format/width")	{ /* 800 */ }
+	else if (!strcmp(name,"format/height")	{ /* 600 */ }
+	else if (!strcmp(name,"format/interlace")	{ /* false */ }
+	else if (!strcmp(name,"format/frame rate")	{ /* 24 */ }
+	return 1;
+}
+
+Alternatively, you might like to parse iteratively.
+You'd use:
+
+void parse_object(cJSON *item)
+{
+	int i; for (i=0;i<cJSON_GetArraySize(item);i++)
+	{
+		cJSON *subitem=cJSON_GetArrayItem(item,i);
+		// handle subitem.	
+	}
+}
+
+Or, for PROPER manual mode:
+
+void parse_object(cJSON *item)
+{
+	cJSON *subitem=item->child;
+	while (subitem)
+	{
+		// handle subitem
+		if (subitem->child) parse_object(subitem->child);
+		
+		subitem=subitem->next;
+	}
+}
+
+Of course, this should look familiar, since this is just a stripped-down version
+of the callback-parser.
+
+This should cover most uses you'll find for parsing. The rest should be possible
+to infer.. and if in doubt, read the source! There's not a lot of it! ;)
+
+
+In terms of constructing JSON data, the example code above is the right way to do it.
+You can, of course, hand your sub-objects to other functions to populate.
+Also, if you find a use for it, you can manually build the objects.
+For instance, suppose you wanted to build an array of objects?
+
+cJSON *objects[24];
+
+cJSON *Create_array_of_anything(cJSON **items,int num)
+{
+	int i;cJSON *prev, *root=cJSON_CreateArray();
+	for (i=0;i<24;i++)
+	{
+		if (!i)	root->child=objects[i];
+		else	prev->next=objects[i], objects[i]->prev=prev;
+		prev=objects[i];
+	}
+	return root;
+}
+	
+and simply: Create_array_of_anything(objects,24);
+
+cJSON doesn't make any assumptions about what order you create things in.
+You can attach the objects, as above, and later add children to each
+of those objects.
+
+As soon as you call cJSON_Print, it renders the structure to text.
+
+
+
+The test.c code shows how to handle a bunch of typical cases. If you uncomment
+the code, it'll load, parse and print a bunch of test files, also from json.org,
+which are more complex than I'd care to try and stash into a const char array[].
+
+
+Enjoy cJSON!
+
+
+- Dave Gamble, Aug 2009
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cJSON/cJSON.c	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,515 @@
+/*
+  Copyright (c) 2009 Dave Gamble
+
+  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.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <float.h>
+#include <limits.h>
+#include <ctype.h>
+#include "cJSON.h"
+
+static const char *ep;
+
+const char *cJSON_GetErrorPtr() {return ep;}
+
+static int cJSON_strcasecmp(const char *s1,const char *s2)
+{
+    if (!s1) return (s1==s2)?0:1;if (!s2) return 1;
+    for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)    if(*s1 == 0)    return 0;
+    return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
+}
+
+static void *(*cJSON_malloc)(size_t sz) = malloc;
+static void (*cJSON_free)(void *ptr) = free;
+
+static char* cJSON_strdup(const char* str)
+{
+      size_t len;
+      char* copy;
+
+      len = strlen(str) + 1;
+      if (!(copy = (char*)cJSON_malloc(len))) return 0;
+      memcpy(copy,str,len);
+      return copy;
+}
+
+void cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+    if (!hooks) { /* Reset hooks */
+        cJSON_malloc = malloc;
+        cJSON_free = free;
+        return;
+    }
+
+    cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
+    cJSON_free     = (hooks->free_fn)?hooks->free_fn:free;
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item()
+{
+    cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
+    if (node) memset(node,0,sizeof(cJSON));
+    return node;
+}
+
+/* Delete a cJSON structure. */
+void cJSON_Delete(cJSON *c)
+{
+    cJSON *next;
+    while (c)
+    {
+        next=c->next;
+        if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
+        if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
+        if (c->string) cJSON_free(c->string);
+        cJSON_free(c);
+        c=next;
+    }
+}
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static const char *parse_number(cJSON *item,const char *num)
+{
+    double n=0,sign=1,scale=0;int subscale=0,signsubscale=1;
+
+    /* Could use sscanf for this? */
+    if (*num=='-') sign=-1,num++;    /* Has sign? */
+    if (*num=='0') num++;            /* is zero */
+    if (*num>='1' && *num<='9')    do    n=(n*10.0)+(*num++ -'0');    while (*num>='0' && *num<='9');    /* Number? */
+    if (*num=='.' && num[1]>='0' && num[1]<='9') {num++;        do    n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');}    /* Fractional part? */
+    if (*num=='e' || *num=='E')        /* Exponent? */
+    {    num++;if (*num=='+') num++;    else if (*num=='-') signsubscale=-1,num++;        /* With sign? */
+        while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');    /* Number? */
+    }
+
+    n=sign*n*pow(10.0,(scale+subscale*signsubscale));    /* number = +/- number.fraction * 10^+/- exponent */
+
+    item->valuedouble=n;
+    item->valueint=(int)n;
+    item->type=cJSON_Number;
+    return num;
+}
+
+/* Render the number nicely from the given item into a string. */
+static char *print_number(cJSON *item)
+{
+    char *str;
+    double d=item->valuedouble;
+    if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
+    {
+        str=(char*)cJSON_malloc(21);    /* 2^64+1 can be represented in 21 chars. */
+        if (str) sprintf(str,"%d",item->valueint);
+    }
+    else
+    {
+        str=(char*)cJSON_malloc(64);    /* This is a nice tradeoff. */
+        if (str)
+        {
+            if (fabs(floor(d)-d)<=DBL_EPSILON)            sprintf(str,"%.0f",d);
+            else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9)    sprintf(str,"%e",d);
+            else                                        sprintf(str,"%f",d);
+        }
+    }
+    return str;
+}
+
+/* Parse the input text into an unescaped cstring, and populate item. */
+static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+static const char *parse_string(cJSON *item,const char *str)
+{
+    const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2;
+    if (*str!='\"') {ep=str;return 0;}    /* not a string! */
+
+    while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++;    /* Skip escaped quotes. */
+
+    out=(char*)cJSON_malloc(len+1);    /* This is how long we need for the string, roughly. */
+    if (!out) return 0;
+
+    ptr=str+1;ptr2=out;
+    while (*ptr!='\"' && *ptr)
+    {
+        if (*ptr!='\\') *ptr2++=*ptr++;
+        else
+        {
+            ptr++;
+            switch (*ptr)
+            {
+                case 'b': *ptr2++='\b';    break;
+                case 'f': *ptr2++='\f';    break;
+                case 'n': *ptr2++='\n';    break;
+                case 'r': *ptr2++='\r';    break;
+                case 't': *ptr2++='\t';    break;
+                case 'u':     /* transcode utf16 to utf8. */
+                    sscanf(ptr+1,"%4x",&uc);ptr+=4;    /* get the unicode char. */
+
+                    if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0)    break;    // check for invalid.
+
+                    if (uc>=0xD800 && uc<=0xDBFF)    // UTF16 surrogate pairs.
+                    {
+                        if (ptr[1]!='\\' || ptr[2]!='u')    break;    // missing second-half of surrogate.
+                        sscanf(ptr+3,"%4x",&uc2);ptr+=6;
+                        if (uc2<0xDC00 || uc2>0xDFFF)        break;    // invalid second-half of surrogate.
+                        uc=0x10000 | ((uc&0x3FF)<<10) | (uc2&0x3FF);
+                    }
+
+                    len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
+
+                    switch (len) {
+                        case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+                        case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+                        case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
+                        case 1: *--ptr2 =(uc | firstByteMark[len]);
+                    }
+                    ptr2+=len;
+                    break;
+                default:  *ptr2++=*ptr; break;
+            }
+            ptr++;
+        }
+    }
+    *ptr2=0;
+    if (*ptr=='\"') ptr++;
+    item->valuestring=out;
+    item->type=cJSON_String;
+    return ptr;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static char *print_string_ptr(const char *str)
+{
+    const char *ptr;char *ptr2,*out;int len=0;unsigned char token;
+
+    if (!str) return cJSON_strdup("");
+    ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;}
+
+    out=(char*)cJSON_malloc(len+3);
+    if (!out) return 0;
+
+    ptr2=out;ptr=str;
+    *ptr2++='\"';
+    while (*ptr)
+    {
+        if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++;
+        else
+        {
+            *ptr2++='\\';
+            switch (token=*ptr++)
+            {
+                case '\\':    *ptr2++='\\';    break;
+                case '\"':    *ptr2++='\"';    break;
+                case '\b':    *ptr2++='b';    break;
+                case '\f':    *ptr2++='f';    break;
+                case '\n':    *ptr2++='n';    break;
+                case '\r':    *ptr2++='r';    break;
+                case '\t':    *ptr2++='t';    break;
+                default: sprintf(ptr2,"u%04x",token);ptr2+=5;    break;    /* escape and print */
+            }
+        }
+    }
+    *ptr2++='\"';*ptr2++=0;
+    return out;
+}
+/* Invote print_string_ptr (which is useful) on an item. */
+static char *print_string(cJSON *item)    {return print_string_ptr(item->valuestring);}
+
+/* Predeclare these prototypes. */
+static const char *parse_value(cJSON *item,const char *value);
+static char *print_value(cJSON *item,int depth,int fmt);
+static const char *parse_array(cJSON *item,const char *value);
+static char *print_array(cJSON *item,int depth,int fmt);
+static const char *parse_object(cJSON *item,const char *value);
+static char *print_object(cJSON *item,int depth,int fmt);
+
+/* Utility to jump whitespace and cr/lf */
+static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
+
+/* Parse an object - create a new root, and populate. */
+cJSON *cJSON_Parse(const char *value)
+{
+    cJSON *c=cJSON_New_Item();
+    ep=0;
+    if (!c) return 0;       /* memory fail */
+
+    if (!parse_value(c,skip(value))) {cJSON_Delete(c);return 0;}
+    return c;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+char *cJSON_Print(cJSON *item)                {return print_value(item,0,1);}
+char *cJSON_PrintUnformatted(cJSON *item)    {return print_value(item,0,0);}
+
+/* Parser core - when encountering text, process appropriately. */
+static const char *parse_value(cJSON *item,const char *value)
+{
+    if (!value)                        return 0;    /* Fail on null. */
+    if (!strncmp(value,"null",4))    { item->type=cJSON_NULL;  return value+4; }
+    if (!strncmp(value,"false",5))    { item->type=cJSON_False; return value+5; }
+    if (!strncmp(value,"true",4))    { item->type=cJSON_True; item->valueint=1;    return value+4; }
+    if (*value=='\"')                { return parse_string(item,value); }
+    if (*value=='-' || (*value>='0' && *value<='9'))    { return parse_number(item,value); }
+    if (*value=='[')                { return parse_array(item,value); }
+    if (*value=='{')                { return parse_object(item,value); }
+
+    ep=value;return 0;    /* failure. */
+}
+
+/* Render a value to text. */
+static char *print_value(cJSON *item,int depth,int fmt)
+{
+    char *out=0;
+    if (!item) return 0;
+    switch ((item->type)&255)
+    {
+        case cJSON_NULL:    out=cJSON_strdup("null");    break;
+        case cJSON_False:    out=cJSON_strdup("false");break;
+        case cJSON_True:    out=cJSON_strdup("true"); break;
+        case cJSON_Number:    out=print_number(item);break;
+        case cJSON_String:    out=print_string(item);break;
+        case cJSON_Array:    out=print_array(item,depth,fmt);break;
+        case cJSON_Object:    out=print_object(item,depth,fmt);break;
+    }
+    return out;
+}
+
+/* Build an array from input text. */
+static const char *parse_array(cJSON *item,const char *value)
+{
+    cJSON *child;
+    if (*value!='[')    {ep=value;return 0;}    /* not an array! */
+
+    item->type=cJSON_Array;
+    value=skip(value+1);
+    if (*value==']') return value+1;    /* empty array. */
+
+    item->child=child=cJSON_New_Item();
+    if (!item->child) return 0;         /* memory fail */
+    value=skip(parse_value(child,skip(value)));    /* skip any spacing, get the value. */
+    if (!value) return 0;
+
+    while (*value==',')
+    {
+        cJSON *new_item;
+        if (!(new_item=cJSON_New_Item())) return 0;     /* memory fail */
+        child->next=new_item;new_item->prev=child;child=new_item;
+        value=skip(parse_value(child,skip(value+1)));
+        if (!value) return 0;    /* memory fail */
+    }
+
+    if (*value==']') return value+1;    /* end of array */
+    ep=value;return 0;    /* malformed. */
+}
+
+/* Render an array to text */
+static char *print_array(cJSON *item,int depth,int fmt)
+{
+    char **entries;
+    char *out=0,*ptr,*ret;int len=5;
+    cJSON *child=item->child;
+    int numentries=0,i=0,fail=0;
+
+    /* How many entries in the array? */
+    while (child) numentries++,child=child->next;
+    /* Allocate an array to hold the values for each */
+    entries=(char**)cJSON_malloc(numentries*sizeof(char*));
+    if (!entries) return 0;
+    memset(entries,0,numentries*sizeof(char*));
+    /* Retrieve all the results: */
+    child=item->child;
+    while (child && !fail)
+    {
+        ret=print_value(child,depth+1,fmt);
+        entries[i++]=ret;
+        if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1;
+        child=child->next;
+    }
+
+    /* If we didn't fail, try to malloc the output string */
+    if (!fail) out=(char*)cJSON_malloc(len);
+    /* If that fails, we fail. */
+    if (!out) fail=1;
+
+    /* Handle failure. */
+    if (fail)
+    {
+        for (i=0;i<numentries;i++) if (entries[i]) cJSON_free(entries[i]);
+        cJSON_free(entries);
+        return 0;
+    }
+
+    /* Compose the output array. */
+    *out='[';
+    ptr=out+1;*ptr=0;
+    for (i=0;i<numentries;i++)
+    {
+        strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
+        if (i!=numentries-1) {*ptr++=',';if(fmt)*ptr++=' ';*ptr=0;}
+        cJSON_free(entries[i]);
+    }
+    cJSON_free(entries);
+    *ptr++=']';*ptr++=0;
+    return out;
+}
+
+/* Build an object from the text. */
+static const char *parse_object(cJSON *item,const char *value)
+{
+    cJSON *child;
+    if (*value!='{')    {ep=value;return 0;}    /* not an object! */
+
+    item->type=cJSON_Object;
+    value=skip(value+1);
+    if (*value=='}') return value+1;    /* empty array. */
+
+    item->child=child=cJSON_New_Item();
+    if (!item->child) return 0;
+    value=skip(parse_string(child,skip(value)));
+    if (!value) return 0;
+    child->string=child->valuestring;child->valuestring=0;
+    if (*value!=':') {ep=value;return 0;}    /* fail! */
+    value=skip(parse_value(child,skip(value+1)));    /* skip any spacing, get the value. */
+    if (!value) return 0;
+
+    while (*value==',')
+    {
+        cJSON *new_item;
+        if (!(new_item=cJSON_New_Item()))    return 0; /* memory fail */
+        child->next=new_item;new_item->prev=child;child=new_item;
+        value=skip(parse_string(child,skip(value+1)));
+        if (!value) return 0;
+        child->string=child->valuestring;child->valuestring=0;
+        if (*value!=':') {ep=value;return 0;}    /* fail! */
+        value=skip(parse_value(child,skip(value+1)));    /* skip any spacing, get the value. */
+        if (!value) return 0;
+    }
+
+    if (*value=='}') return value+1;    /* end of array */
+    ep=value;return 0;    /* malformed. */
+}
+
+/* Render an object to text. */
+static char *print_object(cJSON *item,int depth,int fmt)
+{
+    char **entries=0,**names=0;
+    char *out=0,*ptr,*ret,*str;int len=7,i=0,j;
+    cJSON *child=item->child;
+    int numentries=0,fail=0;
+    /* Count the number of entries. */
+    while (child) numentries++,child=child->next;
+    /* Allocate space for the names and the objects */
+    entries=(char**)cJSON_malloc(numentries*sizeof(char*));
+    if (!entries) return 0;
+    names=(char**)cJSON_malloc(numentries*sizeof(char*));
+    if (!names) {cJSON_free(entries);return 0;}
+    memset(entries,0,sizeof(char*)*numentries);
+    memset(names,0,sizeof(char*)*numentries);
+
+    /* Collect all the results into our arrays: */
+    child=item->child;depth++;if (fmt) len+=depth;
+    while (child)
+    {
+        names[i]=str=print_string_ptr(child->string);
+        entries[i++]=ret=print_value(child,depth,fmt);
+        if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1;
+        child=child->next;
+    }
+
+    /* Try to allocate the output string */
+    if (!fail) out=(char*)cJSON_malloc(len);
+    if (!out) fail=1;
+
+    /* Handle failure */
+    if (fail)
+    {
+        for (i=0;i<numentries;i++) {if (names[i]) cJSON_free(names[i]);if (entries[i]) cJSON_free(entries[i]);}
+        cJSON_free(names);cJSON_free(entries);
+        return 0;
+    }
+
+    /* Compose the output: */
+    *out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0;
+    for (i=0;i<numentries;i++)
+    {
+        if (fmt) for (j=0;j<depth;j++) *ptr++='\t';
+        strcpy(ptr,names[i]);ptr+=strlen(names[i]);
+        *ptr++=':';if (fmt) *ptr++='\t';
+        strcpy(ptr,entries[i]);ptr+=strlen(entries[i]);
+        if (i!=numentries-1) *ptr++=',';
+        if (fmt) *ptr++='\n';*ptr=0;
+        cJSON_free(names[i]);cJSON_free(entries[i]);
+    }
+
+    cJSON_free(names);cJSON_free(entries);
+    if (fmt) for (i=0;i<depth-1;i++) *ptr++='\t';
+    *ptr++='}';*ptr++=0;
+    return out;
+}
+
+/* Get Array size/item / object item. */
+int    cJSON_GetArraySize(cJSON *array)                            {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;}
+cJSON *cJSON_GetArrayItem(cJSON *array,int item)                {cJSON *c=array->child;  while (c && item>0) item--,c=c->next; return c;}
+cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)    {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;}
+/* Utility for handling references. */
+static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;}
+
+/* Add item to array/object. */
+void   cJSON_AddItemToArray(cJSON *array, cJSON *item)                        {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}}
+void   cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item)    {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);}
+void    cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)                        {cJSON_AddItemToArray(array,create_reference(item));}
+void    cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item)    {cJSON_AddItemToObject(object,string,create_reference(item));}
+
+cJSON *cJSON_DetachItemFromArray(cJSON *array,int which)            {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return 0;
+    if (c->prev) c->prev->next=c->next;if (c->next) c->next->prev=c->prev;if (c==array->child) array->child=c->next;c->prev=c->next=0;return c;}
+void   cJSON_DeleteItemFromArray(cJSON *array,int which)            {cJSON_Delete(cJSON_DetachItemFromArray(array,which));}
+cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;}
+void   cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));}
+
+/* Replace array/object items with new ones. */
+void   cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem)        {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return;
+    newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem;
+    if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);}
+void   cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}}
+
+/* Create basic types: */
+cJSON *cJSON_CreateNull()                        {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
+cJSON *cJSON_CreateTrue()                        {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
+cJSON *cJSON_CreateFalse()                        {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
+cJSON *cJSON_CreateBool(int b)                    {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
+cJSON *cJSON_CreateNumber(double num)            {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
+cJSON *cJSON_CreateString(const char *string)    {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
+cJSON *cJSON_CreateArray()                        {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
+cJSON *cJSON_CreateObject()                        {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
+
+/* Create Arrays: */
+cJSON *cJSON_CreateIntArray(int *numbers,int count)                {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+cJSON *cJSON_CreateFloatArray(float *numbers,int count)            {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+cJSON *cJSON_CreateDoubleArray(double *numbers,int count)        {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+cJSON *cJSON_CreateStringArray(const char **strings,int count)    {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cJSON/cJSON.h	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,136 @@
+/*
+  Copyright (c) 2009 Dave Gamble
+
+  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 cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <float.h>
+#include <limits.h>
+#include <ctype.h>
+
+/* cJSON Types: */
+#define cJSON_False 0
+#define cJSON_True 1
+#define cJSON_NULL 2
+#define cJSON_Number 3
+#define cJSON_String 4
+#define cJSON_Array 5
+#define cJSON_Object 6
+
+#define cJSON_IsReference 256
+
+/* The cJSON structure: */
+typedef struct cJSON {
+    struct cJSON *next,*prev;    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+    struct cJSON *child;        /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+
+    int type;                    /* The type of the item, as above. */
+
+    char *valuestring;            /* The item's string, if type==cJSON_String */
+    int valueint;                /* The item's number, if type==cJSON_Number */
+    double valuedouble;            /* The item's number, if type==cJSON_Number */
+
+    char *string;                /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+} cJSON;
+
+typedef struct cJSON_Hooks {
+      void *(*malloc_fn)(size_t sz);
+      void (*free_fn)(void *ptr);
+} cJSON_Hooks;
+
+/* Supply malloc, realloc and free functions to cJSON */
+extern void cJSON_InitHooks(cJSON_Hooks* hooks);
+
+
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
+extern cJSON *cJSON_Parse(const char *value);
+/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
+extern char  *cJSON_Print(cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
+extern char  *cJSON_PrintUnformatted(cJSON *item);
+/* Delete a cJSON entity and all subentities. */
+extern void   cJSON_Delete(cJSON *c);
+
+/* Returns the number of items in an array (or object). */
+extern int      cJSON_GetArraySize(cJSON *array);
+/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
+extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
+/* Get item "string" from object. Case insensitive. */
+extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
+
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+extern const char *cJSON_GetErrorPtr();
+
+/* These calls create a cJSON item of the appropriate type. */
+extern cJSON *cJSON_CreateNull();
+extern cJSON *cJSON_CreateTrue();
+extern cJSON *cJSON_CreateFalse();
+extern cJSON *cJSON_CreateBool(int b);
+extern cJSON *cJSON_CreateNumber(double num);
+extern cJSON *cJSON_CreateString(const char *string);
+extern cJSON *cJSON_CreateArray();
+extern cJSON *cJSON_CreateObject();
+
+/* These utilities create an Array of count items. */
+extern cJSON *cJSON_CreateIntArray(int *numbers,int count);
+extern cJSON *cJSON_CreateFloatArray(float *numbers,int count);
+extern cJSON *cJSON_CreateDoubleArray(double *numbers,int count);
+extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
+
+/* Append item to the specified array/object. */
+extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
+extern void    cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+extern void    cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
+
+/* Remove/Detatch items from Arrays/Objects. */
+extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
+extern void   cJSON_DeleteItemFromArray(cJSON *array,int which);
+extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
+extern void   cJSON_DeleteItemFromObject(cJSON *object,const char *string);
+
+/* Update array items. */
+extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
+extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+
+#define cJSON_AddNullToObject(object,name)    cJSON_AddItemToObject(object, name, cJSON_CreateNull())
+#define cJSON_AddTrueToObject(object,name)    cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
+#define cJSON_AddFalseToObject(object,name)        cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
+#define cJSON_AddNumberToObject(object,name,n)    cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
+#define cJSON_AddStringToObject(object,name,s)    cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,192 @@
+#include "mbed.h"
+#include "MTSSerial.h"
+#include "Wifi.h"
+#include "TSISensor.h"
+#include "MMA8451Q.h"
+#include "MAG3110.h"
+#include "USBHostCam.h"
+#include "axToolkit.h"
+
+/*********************************************/
+/* In the following lines please input:      */
+/* on line 17: your serial number            */
+/* on line 18: your wifi SSID                */
+/* on line 19: your wifi password/key        */
+/* on line 20: your security type (WPA...    */
+/*********************************************/
+
+#define YOUR_SERIAL_NUM "your_serial_here_123"
+#define SSID "yourWifi"
+#define AP_Security "SECURITYKEYHERE"
+#define AP_Key WPA2             //security type examples: NONE, WEP64, WEP128, WPA, WPA2
+
+
+/*********************************************/
+/*Axeda connections                          */
+/*********************************************/
+
+#define MODEL           "Freescale46"                       //using Freescale as a placeholder
+#define SERVER_NAME     "toolbox-stage-connect.axeda.com" //IP = 216.34.120.53/52 stage/public URL=toolbox-stage-connect.axeda.com
+#define PORT_NUM        80
+
+
+using namespace mts;
+
+//Hardware Support Stuff
+// Include support for on-board green and red LEDs
+#define LED_ON  0
+#define LED_OFF 1
+
+DigitalOut greenLED(LED_GREEN);
+DigitalOut redLED(LED_RED);
+// Include support for onboard pushbuttons (value = 0 when pressed)
+DigitalIn  sw1(PTC3);
+DigitalIn  sw3(PTC12);
+TSISensor slider;
+AnalogIn  lightSense(PTE22);
+#define MMA8451_I2C_ADDRESS (0x1d<<1)
+MMA8451Q acc(PTE25, PTE24, MMA8451_I2C_ADDRESS);
+MAG3110 mag(PTE25, PTE24);
+//Camera support
+/*
+#if defined(TARGET_KL46Z)
+uint8_t image_buf[1024*24];
+#elif defined(TARGET_KL25Z)
+uint8_t image_buf[1024*12];
+#endif */
+
+
+int main()
+{
+    TSISensor touchSensor;
+    //float accels[3];
+    //float resting;
+    int SEVERITY;
+    //int HURT_BUTTON_STATE=0;
+    //int OK_BUTTON_STATE=0;
+    double status=0;
+    
+//Some light I/O setup:    
+    redLED=LED_OFF;
+    greenLED=LED_OFF; 
+// Turn on pull up resistors on pushbutton inputs
+    sw1.mode(PullUp);
+    sw3.mode(PullUp);
+
+//Setup the Camera: 
+ /*   int size=0;
+    USBHostCam* cam = new USBHostCam(_320x240);
+    if (!cam->connect()) {
+        error("WebCam not found.\n");
+        greenLED=LED_ON;
+        }
+    else { redLED=LED_ON; }  */
+     
+    //Set the network parameters
+    std::string ssid = SSID;
+    std::string securityKey = AP_Security;
+    Wifi::SecurityType securityType = Wifi::AP_Key;
+
+    //Wait for wifi module to boot up
+    for (int i = 10; i >= 0; i = i - 2) {
+        wait(2);
+        printf("Waiting %d seconds...\n\r", i);
+    }
+
+    //Setup serial interface to WiFi module
+    MTSSerial* serial = new MTSSerial(PTD3, PTD2, 256, 256);
+    serial->baud(9600);
+
+    //Setup Wifi class
+    Wifi* wifi = Wifi::getInstance();
+    printf("Init: %s\n\r", wifi->init(serial) ? "SUCCESS" : "FAILURE");
+    wifi->sendBasicCommand("set comm size 1460", 500, CR);
+    wifi->sendBasicCommand("set comm time 6000", 500, CR);
+  //  wifi->sendBasicCommand("set comm match "+EOT, 3, CR);
+    //Setup and check connection
+    printf("Set Network: %s\n\r", getCodeNames(wifi->setNetwork(ssid, securityType, securityKey)).c_str());
+    printf("Set DHCP: %s\n\r", getCodeNames(wifi->setDeviceIP("DHCP")).c_str());
+    while (! wifi->connect()) {
+        printf("Connect: Failure\r\n");
+        wait(1);
+    }
+    printf("Connected To Wifi!");
+    
+//Now we setup the Axeda Service:
+    ax_platform sbox;
+    ax_deviceID thisDevice;
+    ax_dataSet *ds[1];
+ //   ax_file pic;
+
+    ax_createPlatform(&sbox, SERVER_NAME, NULL, PORT_NUM); 
+    ax_data_createModelSerialDeviceId(&thisDevice, MODEL, YOUR_SERIAL_NUM, NULL);
+  //  ax_createFile(&pic, "mypic.jpg", "pic", sizeof(image_buf), (unsigned char*)&image_buf);       
+   
+    /*wait(0.7);
+    acc.getAccAllAxis( accels );
+    resting = abs(accels[0]) + abs(accels[1]) + abs(accels[2]);
+    printf("Set up resting accelerometer - %.2f\r\n", resting);
+    wait(0.1);*/
+ 
+    //Ping 
+    while (true) {
+        ds[0]=(ax_dataSet *)malloc(sizeof(ax_dataSet));
+        printf("\n\n\r ready \n\n");
+        SEVERITY=0; 
+        //ax_data_createSet(ds[0], 0, AX_NO_PRIORITY);     
+        //HURT_BUTTON_STATE=sw1.mode(PullUp);
+        //OK_BUTTON_STATE=sw3.mode(PullUp);
+        sw3.mode(PullUp);
+        sw1.mode(PullUp);
+        while(sw3&&sw1){
+            status=0;
+            greenLED=LED_ON;
+            wait(0.1);
+            greenLED=LED_OFF;
+            wait(0.1);
+                if (!sw3){
+                status=2;
+                }
+                    else{
+                        status=1;
+                    }
+            }  
+        
+        //SEND button push (hurt or ok)    
+        ax_data_addAnalogToSet(ds[0], "status", status);
+               
+        while(SEVERITY<=1){
+            SEVERITY=100*touchSensor.readPercentage();
+            redLED=LED_ON;
+            wait(0.1);
+            redLED=LED_OFF;
+            wait(0.1);
+            
+            }
+        
+        SEVERITY=100*touchSensor.readPercentage();
+
+        printf("\n\r%d\n",SEVERITY);
+        //Send data about severity (left to right position on touchpad)
+        ax_data_addAnalogToSet(ds[0], "severity",SEVERITY);    
+        
+        //Send data about orientation     
+
+        ax_data_addAnalogToSet(ds[0], "accel_x", acc.getAccX());
+        ax_data_addAnalogToSet(ds[0], "accel_y", acc.getAccY());
+        ax_data_addAnalogToSet(ds[0], "accel_z", acc.getAccZ());
+        
+        printf("\n\r%f\n", acc.getAccX());
+        printf("\n\r%f\n", acc.getAccY());
+        printf("\n\r%f\n", acc.getAccZ());
+    
+        ax_platform_sendData(&sbox, &thisDevice, ds, 1);
+        ax_data_destroySet(ds[0]);
+    /*
+        size = cam->readJPEG(image_buf, sizeof(image_buf));
+        ax_platform_upload(&sbox, &thisDevice, &pic);
+        */
+    //    printf("Ping Server: %s\n\r", wifi->ping("8.8.8.8") ? "Success" : "Failed");
+        wait(10);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Tue Jul 01 21:31:54 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/dc225afb6914
\ No newline at end of file