mbed official / CellularModem

Dependencies:   Socket lwip-sys lwip

Dependents:   CellularUSBModem CellularUSBModem

Files at this revision

API Documentation at this revision

Comitter:
bogdanm
Date:
Thu Oct 17 12:29:05 2013 +0300
Parent:
0:bb116b4ed7b7
Child:
2:03b089430085
Commit message:
Initial release of the CellularModem library

Changed in this revision

at/ATCommandsInterface.cpp Show annotated file Show diff for this revision Revisions of this file
at/ATCommandsInterface.h Show annotated file Show diff for this revision Revisions of this file
core/IOStream.h Show annotated file Show diff for this revision Revisions of this file
core/MtxCircBuffer.h Show annotated file Show diff for this revision Revisions of this file
core/config.h Show annotated file Show diff for this revision Revisions of this file
core/dbg.cpp Show annotated file Show diff for this revision Revisions of this file
core/dbg.h Show annotated file Show diff for this revision Revisions of this file
core/errors.h Show annotated file Show diff for this revision Revisions of this file
core/fwk.h Show annotated file Show diff for this revision Revisions of this file
ip/IPInterface.cpp Show annotated file Show diff for this revision Revisions of this file
ip/IPInterface.h Show annotated file Show diff for this revision Revisions of this file
ip/LwIPInterface.cpp Show annotated file Show diff for this revision Revisions of this file
ip/LwIPInterface.h Show annotated file Show diff for this revision Revisions of this file
ip/PPPIPInterface.cpp Show annotated file Show diff for this revision Revisions of this file
ip/PPPIPInterface.h Show annotated file Show diff for this revision Revisions of this file
link/LinkMonitor.cpp Show annotated file Show diff for this revision Revisions of this file
link/LinkMonitor.h Show annotated file Show diff for this revision Revisions of this file
lwipopts_conf.h Show annotated file Show diff for this revision Revisions of this file
sms/CDMASMSInterface.cpp Show annotated file Show diff for this revision Revisions of this file
sms/CDMASMSInterface.h Show annotated file Show diff for this revision Revisions of this file
sms/GSMSMSInterface.cpp Show annotated file Show diff for this revision Revisions of this file
sms/GSMSMSInterface.h Show annotated file Show diff for this revision Revisions of this file
sms/SMSInterface.h Show annotated file Show diff for this revision Revisions of this file
ussd/USSDInterface.cpp Show annotated file Show diff for this revision Revisions of this file
ussd/USSDInterface.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/at/ATCommandsInterface.cpp	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,895 @@
+/* ATCommandsInterface.cpp */
+/* 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.
+ */
+
+#define __DEBUG__  2 //ERR+WARN
+#ifndef __MODULE__
+#define __MODULE__ "ATCommandsInterface.cpp"
+#endif
+
+#include "core/fwk.h"
+
+#include <cstdio>
+#include <cstring> //For memset, strstr...
+
+using std::memmove;
+
+#include "ATCommandsInterface.h"
+
+ATCommandsInterface::ATCommandsInterface(IOStream* pStream) :
+   m_pStream(pStream), m_open(false), m_env2AT(), m_AT2Env(), m_processingMtx(),
+   m_processingThread(&ATCommandsInterface::staticCallback, this, (osPriority)AT_THREAD_PRIORITY, 4*192),
+   m_eventsMgmtMtx(), m_eventsProcessingMtx()
+{
+  memset(m_eventsHandlers, 0, MAX_AT_EVENTS_HANDLERS * sizeof(IATEventsHandler*));
+
+  m_processingMtx.lock();
+}
+
+//Open connection to AT Interface in order to execute command & register/unregister events
+int ATCommandsInterface::open()
+{
+  if( m_open )
+  {
+    WARN("AT interface is already open");
+    return OK;
+  }
+  DBG("Opening AT interface");
+  //Start processing
+  m_processingThread.signal_set(AT_SIG_PROCESSING_START);
+
+  m_processingMtx.unlock();
+
+  m_open = true;
+
+  DBG("AT interface opened");
+  
+  return OK;
+}
+
+//Initialize AT link & start events processing
+int ATCommandsInterface::init()
+{
+  DBG("Sending ATZ E1 V1");
+  
+  //Lock transaction mutex
+  m_transactionMtx.lock();
+  
+  //Should we flush m_pStream at this point ???
+  int err;
+  int tries = 5;
+  do
+  {
+    err = executeInternal("ATZ E1 V1", this, NULL, 3000); //Enable echo and verbosity
+    if(err && tries)
+    {
+      WARN("No response, trying again");
+      Thread::wait(1000); //Give dongle time to recover
+    }
+  } while(err && tries--);
+  if( err )
+  {
+    ERR("Sending ATZ E1 V1 returned with err code %d", err);
+    m_transactionMtx.unlock();
+    return err;
+  }
+  
+  //Enable events handling and execute events enabling commands
+  enableEvents();
+
+  DBG("AT interface initialized");
+  
+  //Unlock transaction mutex
+  m_transactionMtx.unlock();
+
+  return OK;
+}
+
+//Close connection
+int ATCommandsInterface::close()
+{
+  if( !m_open )
+  {
+    WARN("AT interface is already closed");
+    return OK;
+  }
+
+  DBG("Closing AT interface");
+  
+  //Lock transaction mutex
+  m_transactionMtx.lock();
+  
+  //Disable events handling and advertize this to the events handlers
+  disableEvents();
+
+  //Stop processing
+  m_processingThread.signal_set(AT_SIG_PROCESSING_STOP);
+  //m_stopSphre.release();
+
+  int* msg = m_env2AT.alloc(osWaitForever);
+  *msg = AT_STOP;
+  m_env2AT.put(msg); //Used to unstall the process if needed
+
+  //Unlock process routine (abort read)
+  m_pStream->abortRead(); //This is thread-safe
+  m_processingMtx.lock();
+  m_open = false;
+  
+  //Unlock transaction mutex
+  m_transactionMtx.unlock();
+
+  DBG("AT interface closed");
+  return OK;
+}
+
+bool ATCommandsInterface::isOpen()
+{
+  return m_open;
+}
+
+int ATCommandsInterface::executeSimple(const char* command, ATResult* pResult, uint32_t timeout/*=1000*/)
+{
+  return execute(command, this, pResult, timeout);
+}
+
+int ATCommandsInterface::execute(const char* command, IATCommandsProcessor* pProcessor, ATResult* pResult, uint32_t timeout/*=1000*/)
+{
+  if(!m_open)
+  {
+    WARN("Interface is not open!");
+    return NET_INVALID;
+  }
+
+  //Lock transaction mutex
+  m_transactionMtx.lock();
+  
+  disableEvents(); //Disable unsollicited result codes
+  int ret = executeInternal(command, pProcessor, pResult, timeout);
+  enableEvents(); //Re-enable unsollicited result codes whatever the result of the command is
+  
+  //Unlock transaction mutex
+  m_transactionMtx.unlock();
+  
+  return ret;
+}
+
+int ATCommandsInterface::registerEventsHandler(IATEventsHandler* pHdlr)
+{
+  m_eventsMgmtMtx.lock();
+  m_eventsProcessingMtx.lock();
+  for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
+  {
+    if( m_eventsHandlers[i] == NULL )
+    {
+      m_eventsHandlers[i] = pHdlr;
+      m_eventsProcessingMtx.unlock();
+      m_eventsMgmtMtx.unlock();
+      return OK;
+    }
+  }
+  m_eventsProcessingMtx.unlock();
+  m_eventsMgmtMtx.unlock();
+  return NET_OOM; //No room left
+}
+
+int ATCommandsInterface::deregisterEventsHandler(IATEventsHandler* pHdlr)
+{
+  m_eventsMgmtMtx.lock();
+  m_eventsProcessingMtx.lock();
+  for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find handler in list
+  {
+    if( m_eventsHandlers[i] == pHdlr )
+    {
+      m_eventsHandlers[i] = NULL;
+      m_eventsProcessingMtx.unlock();
+      m_eventsMgmtMtx.unlock();
+      return OK;
+    }
+  }
+  m_eventsProcessingMtx.unlock();
+  m_eventsMgmtMtx.unlock();
+  return NET_NOTFOUND; //Not found
+}
+
+//Private methods
+
+int ATCommandsInterface::executeInternal(const char* command, IATCommandsProcessor* pProcessor, ATResult* pResult, uint32_t timeout/*=1000*/)
+{
+  DBG("Executing command %s", command);
+
+  //Discard previous result if it arrived too late
+  osEvent evt = m_AT2Env.get(0);
+
+  if(evt.status == osEventMail)
+  {
+    m_AT2Env.free((int*)evt.value.p);
+    WARN("Previous result discarded");
+  }
+
+  //Send params to the process routine
+  m_transactionCommand = command;
+  if(pProcessor != NULL)
+  {
+    m_pTransactionProcessor = pProcessor;
+  }
+  else
+  {
+    m_pTransactionProcessor = this; //Use default behaviour
+  }
+
+  DBG("Sending command ready signal to AT thread & aborting current blocking read operation");
+
+  //Produce command ready signal
+  int* msg = m_env2AT.alloc(osWaitForever);
+  *msg = AT_CMD_READY;
+  m_env2AT.put(msg);
+
+  DBG("Trying to enter abortRead()");
+  //Unlock process routine (abort read)
+  m_pStream->abortRead(); //This is thread-safe
+
+  //Wait for a result (get result message)
+  evt = m_AT2Env.get(timeout);
+
+  if(evt.status != osEventMail)
+  {
+    //Cancel request
+    msg = m_env2AT.alloc(osWaitForever);
+    *msg = AT_TIMEOUT;
+    m_env2AT.put(msg);
+
+    DBG("Trying to enter abortRead()");
+    //Unlock process routine (abort read)
+    m_pStream->abortRead(); //This is thread-safe
+    
+    //Wait for acknowledge
+    int msgResult;
+    do
+    {
+      evt = m_AT2Env.get(osWaitForever);
+      msgResult = *((int*) evt.value.p);
+      m_AT2Env.free((int*)evt.value.p);
+    } while(msgResult != AT_TIMEOUT);  
+
+    WARN("Command returned no message");
+    return NET_TIMEOUT;
+  }
+  DBG("Command returned with message %d", *msg);
+
+  m_AT2Env.free((int*)evt.value.p);
+
+  if(pResult != NULL)
+  {
+    *pResult = m_transactionResult;
+  }
+
+  int ret = ATResultToReturnCode(m_transactionResult);
+  if(ret != OK)
+  {
+    WARN("Command returned AT result %d with code %d", m_transactionResult.result, m_transactionResult.code);
+  }
+
+  DBG("Command returned successfully");
+
+  return ret;
+}
+
+int ATCommandsInterface::tryReadLine()
+{
+  static bool lineDetected = false;
+
+  //Block on serial read or incoming command
+  DBG("Trying to read a new line from stream");
+  int ret = m_pStream->waitAvailable(); //This can be aborted
+  size_t readLen = 0;
+  if(ret == OK)
+  {
+    ret = m_pStream->read((uint8_t*)m_inputBuf + m_inputPos, &readLen, AT_INPUT_BUF_SIZE - 1 - m_inputPos, 0); //Do NOT wait at this point
+  }
+  if(ret == OK)
+  {
+    m_inputPos+=readLen;
+    m_inputBuf[m_inputPos] = '\0'; //Add null terminating character to ease the use of str* functions
+    DBG("In buffer: [%s]", m_inputBuf);
+  }
+
+  if( ret == NET_INTERRUPTED ) //It is worth checking readLen as data might have been read even though the read was interrupted
+  {
+    DBG("Read was interrupted");
+    return NET_INTERRUPTED; //0 chars were read
+  }
+  else if(readLen == 0)
+  {
+    DBG("Nothing read");
+    return OK; //0 chars were read
+  }
+
+  DBG("Trying to process incoming line");
+  bool lineProcessed = false;
+
+  do
+  {
+    lineProcessed = false; //Reset flag
+
+    DBG("New iteration");
+
+    //Look for a new line
+    if(!lineDetected)
+    {
+      DBG("No line detected yet");
+      //Try to look for a starting CRLF
+      char* crPtr = strchr(m_inputBuf, CR);
+      /*
+      Different cases at this point:
+      - CRLF%c sequence: this is the start of a line
+      - CRLFCR(LF) sequence: this is the end of a line (followed by the beginning of the next one)
+      - LF: this is the trailing LF char of the previous line, discard
+      - CR / CRLF incomplete sequence: more data is needed to determine which action to take
+      - %c ... CR sequence: this should be the echo of the previous sequence
+      - %c sequence: This might be the echo of the previous command; more data is needed to determine which action to take
+
+      In every case, move mem at the beginning
+      */
+      if(crPtr != NULL)
+      {
+        DBG("CR char found");
+
+#if 0
+        //Discard all preceding characters (can do nothing if m_inputBuf == crPtr)
+        memmove(m_inputBuf, crPtr, (m_inputPos + 1) - (crPtr-m_inputBuf)); //Move null-terminating char as well
+        m_inputPos = m_inputPos - (crPtr-m_inputBuf); //Adjust m_inputPos
+#endif
+
+        //If the line starts with CR, this should be a result code
+        if( crPtr == m_inputBuf )
+        {
+          //To determine the sequence we need at least 3 chars
+          if(m_inputPos >= 3)
+          {
+            //Look for a LF char next to the CR char
+            if(m_inputBuf[1] == LF)
+            {
+              //At this point we can check whether this is the end of a preceding line or the beginning of a new one
+              if(m_inputBuf[2] != CR)
+              {
+                DBG("Beginning of new line found");
+                //Beginning of a line
+                lineDetected = true; //Move to next state-machine step
+              }
+              else
+              {
+                //End of an unprocessed line
+                WARN("End of unprocessed line");
+              }
+              //In both cases discard CRLF
+              DBG("Discarding CRLF");
+              memmove(m_inputBuf, m_inputBuf + 2, (m_inputPos + 1) - 2); //Move null-terminating char as well
+              m_inputPos = m_inputPos - 2; //Adjust m_inputPos
+            }
+            else
+            {
+              //This is completely unexpected, discard the CR char to try to recover good state
+              WARN("Unexpected %c char (%02d code) found after CR char", m_inputBuf[1]);
+              memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well
+              m_inputPos = m_inputPos - 1; //Adjust m_inputPos
+            }
+          }
+        }
+        //if the line does NOT begin with CR, this can be an echo of the previous command, process it
+        else
+        {
+          int crPos = crPtr - m_inputBuf;
+          int lfOff = 0; //Offset for LF if present
+          DBG("New line found (possible echo of command)");
+          //This is the end of line
+          //Replace m_inputBuf[crPos] with null-terminating char
+          m_inputBuf[crPos] = '\0';
+          //Check if there is a LF char afterwards
+          if(m_inputPos - crPos >= 1)
+          {
+            if(m_inputBuf[crPos+1] == LF)
+            {
+              lfOff++; //We will discard LF char as well
+            }
+          }
+          //Process line
+          int ret = processReadLine();
+          if(ret)
+          {
+            m_inputPos = 0;
+            m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
+            lineDetected = false;
+            return ret;
+          }
+
+          //If sendData has been called, all incoming data has been discarded
+          if(m_inputPos > 0)
+          {
+            memmove(m_inputBuf, m_inputBuf + crPos + lfOff + 1, (m_inputPos + 1) - (crPos + lfOff + 1)); //Move null-terminating char as well
+            m_inputPos = m_inputPos - (crPos + lfOff + 1); //Adjust m_inputPos
+          }
+          DBG("One line was successfully processed");
+          lineProcessed = true; //Line was processed with success
+          lineDetected = false; //Search now for a new line
+        }
+      }
+      else if(m_inputBuf[0] == LF) //If there is a remaining LF char from the previous line, discard it
+      {
+        DBG("Discarding single LF char");
+        memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well
+        m_inputPos = m_inputPos - 1; //Adjust m_inputPos
+      }
+    }
+
+    //Look for the end of line
+    if(lineDetected)
+    {
+      DBG("Looking for end of line");
+      //Try to look for a terminating CRLF
+      char* crPtr = strchr(m_inputBuf, CR);
+      /*
+      Different cases at this point:
+      - CRLF sequence: this is the end of the line
+      - CR%c sequence : unexpected
+      - CR incomplete sequence: more data is needed to determine which action to take
+      */
+
+      //Try to look for a '>' (greater than character) that marks an entry prompt
+      char* greaterThanPtr = strchr(m_inputBuf, GD);
+      /*
+      This character must be detected as there is no CRLF sequence at the end of an entry prompt
+       */
+
+      if(crPtr != NULL)
+      {
+        DBG("CR char found");
+        int crPos = crPtr - m_inputBuf;
+        //To determine the sequence we need at least 2 chars
+        if(m_inputPos - crPos >= 2)
+        {
+          //Look for a LF char next to the CR char
+          if(m_inputBuf[crPos + 1] == LF)
+          {
+            DBG("End of new line found");
+            //This is the end of line
+            //Replace m_inputBuf[crPos] with null-terminating char
+            m_inputBuf[crPos] = '\0';
+            //Process line
+            int ret = processReadLine();
+            if(ret)
+            {
+              m_inputPos = 0;
+              m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
+              lineDetected = false;
+              return ret;
+            }
+
+            //If sendData has been called, all incoming data has been discarded
+            if(m_inputPos > 0)
+            {
+              //Shift remaining data to beginning of buffer
+              memmove(m_inputBuf, m_inputBuf + crPos + 2, (m_inputPos + 1) - (crPos + 2)); //Move null-terminating char as well
+              m_inputPos = m_inputPos - (crPos + 2); //Adjust m_inputPos
+            }
+
+            DBG("One line was successfully processed");
+            lineProcessed = true; //Line was processed with success
+          }
+          else
+          {
+            //This is completely unexpected, discard all chars till the CR char to try to recover good state
+            WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[crPos + 1]);
+            memmove(m_inputBuf, m_inputBuf + crPos + 1, (m_inputPos + 1) - (crPos + 1)); //Move null-terminating char as well
+            m_inputPos = m_inputPos - (crPos + 1); //Adjust m_inputPos
+          }
+          lineDetected = false; //In both case search now for a new line
+        }
+      }
+      else if(greaterThanPtr != NULL)
+      {
+        DBG("> char found");
+        int gdPos = greaterThanPtr - m_inputBuf;
+        //To determine the sequence we need at least 2 chars
+        if(m_inputPos - gdPos >= 2)
+        {
+          //Look for a space char next to the GD char
+          if(m_inputBuf[gdPos + 1] == ' ')
+          {
+            //This is an entry prompt
+            //Replace m_inputBuf[gdPos] with null-terminating char
+            m_inputBuf[gdPos] = '\0';
+
+            //Shift remaining data to beginning of buffer
+            memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well
+            m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos
+
+            //Process prompt
+            ret = processEntryPrompt();
+            if(ret)
+            {
+              m_inputPos = 0;
+              m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
+              lineDetected = false;
+              return ret;
+            }
+
+            DBG("One line was successfully processed");
+            lineProcessed = true; //Line was processed with success
+          }
+          else
+          {
+            //This is completely unexpected, discard all chars till the GD char to try to recover good state
+            WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[gdPos + 1]);
+            memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well
+            m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos
+          }
+          lineDetected = false; //In both case search now for a new line
+        }
+      }
+    }
+  } while(lineProcessed); //If one complete line was processed there might be other incoming lines that can also be processed without reading the buffer again
+
+  //If the line could not be processed AND buffer is full, it means that we won't ever be able to process it (buffer too short)
+  if(m_inputPos == AT_INPUT_BUF_SIZE - 1)
+  {
+    //Discard everything
+    m_inputPos = 0;
+    m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
+    WARN("Incoming buffer is too short to process incoming line");
+    //Look for a new line
+    lineDetected = false;
+  }
+
+  DBG("Processed every full incoming lines");
+
+  return OK;
+}
+
+int ATCommandsInterface::trySendCommand()
+{
+  osEvent evt = m_env2AT.get(0);
+  DBG("status = %d, msg = %d", evt.status, evt.value.p);
+  if(evt.status == osEventMail)
+  {
+    int* msg = (int*) evt.value.p;
+    if( *msg == AT_CMD_READY ) //Command pending
+    {
+      if(m_transactionState != IDLE)
+      {
+        WARN("Previous command not processed!");
+      }
+      DBG("Sending pending command");
+      m_pStream->write((uint8_t*)m_transactionCommand, strlen(m_transactionCommand), osWaitForever);
+      char cr = CR;
+      m_pStream->write((uint8_t*)&cr, 1, osWaitForever); //Carriage return line terminator
+      m_transactionState = COMMAND_SENT;
+    }
+    else //Timeout
+    {
+      //Acknowledge
+      int* msg = m_AT2Env.alloc(osWaitForever);
+      *msg = AT_TIMEOUT;
+      m_AT2Env.put(msg); //Command has timed out
+      m_transactionState = IDLE; //State-machine reset
+    }
+    m_env2AT.free(msg);
+  }
+  return OK;
+}
+
+int ATCommandsInterface::processReadLine()
+{
+  DBG("Processing read line [%s]", m_inputBuf);
+  //The line is stored in m_inputBuf
+  if(m_transactionState == COMMAND_SENT)
+  {
+    //If the command has been sent, checks echo to see if it has been received properly
+    if( strcmp(m_transactionCommand, m_inputBuf) == 0 )
+    {
+      DBG("Command echo received");
+      //If so, it means that the following lines will only be solicited results
+      m_transactionState = READING_RESULT;
+      return OK;
+    }
+  }
+  if(m_transactionState == IDLE || m_transactionState == COMMAND_SENT)
+  {
+    bool found = false;
+    char* pSemicol = strchr(m_inputBuf, ':');
+    char* pData = NULL;
+    if( pSemicol != NULL ) //Split the identifier & the result code (if it exists)
+    {
+      *pSemicol = '\0';
+      pData = pSemicol + 1;
+      if(pData[0]==' ')
+      {
+        pData++; //Suppress whitespace
+      }
+    }
+    //Looks for a unsolicited result code; we can have m_transactionState == COMMAND_SENT as the code may have arrived just before we sent the command
+    m_eventsProcessingMtx.lock();
+    //Go through the list
+    for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
+    {
+      if( m_eventsHandlers[i] != NULL )
+      {
+        if( m_eventsHandlers[i]->isATCodeHandled(m_inputBuf) )
+        {
+          m_eventsHandlers[i]->onEvent(m_inputBuf, pData);
+          found = true; //Do not break here as there might be multiple handlers for one event type
+        }
+      }
+    }
+    m_eventsProcessingMtx.unlock();
+    if(found)
+    {
+      return OK;
+    }
+  }
+  if(m_transactionState == READING_RESULT)
+  {
+    //The following lines can either be a command response or a result code (OK / ERROR / CONNECT / +CME ERROR: %s / +CMS ERROR: %s)
+    if(strcmp("OK", m_inputBuf) == 0)
+    {
+      DBG("OK result received");
+      m_transactionResult.code = 0;
+      m_transactionResult.result = ATResult::AT_OK;
+      m_transactionState = IDLE;
+      int* msg = m_AT2Env.alloc(osWaitForever);
+      *msg = AT_RESULT_READY;
+      m_AT2Env.put(msg); //Command has been processed
+      return OK;
+    }
+    else if(strcmp("ERROR", m_inputBuf) == 0)
+    {
+      DBG("ERROR result received");
+      m_transactionResult.code = 0;
+      m_transactionResult.result = ATResult::AT_ERROR;
+      m_transactionState = IDLE;
+      int* msg = m_AT2Env.alloc(osWaitForever);
+      *msg = AT_RESULT_READY;
+      m_AT2Env.put(msg); //Command has been processed
+      return OK;
+    }
+    else if(strncmp("CONNECT", m_inputBuf, 7 /*=strlen("CONNECT")*/) == 0) //Result can be "CONNECT" or "CONNECT %d", indicating baudrate
+    {
+      DBG("CONNECT result received");
+      m_transactionResult.code = 0;
+      m_transactionResult.result = ATResult::AT_CONNECT;
+      m_transactionState = IDLE;
+      int* msg = m_AT2Env.alloc(osWaitForever);
+      *msg = AT_RESULT_READY;
+      m_AT2Env.put(msg); //Command has been processed
+      return OK;
+    }
+    else if(strcmp("COMMAND NOT SUPPORT", m_inputBuf) == 0) //Huawei-specific, not normalized
+    {
+      DBG("COMMAND NOT SUPPORT result received");
+      m_transactionResult.code = 0;
+      m_transactionResult.result = ATResult::AT_ERROR;
+      m_transactionState = IDLE;
+      int* msg = m_AT2Env.alloc(osWaitForever);
+      *msg = AT_RESULT_READY;
+      m_AT2Env.put(msg); //Command has been processed
+      return OK;
+    }
+    else if(strstr(m_inputBuf, "+CME ERROR:") == m_inputBuf) //Mobile Equipment Error
+    {
+      std::sscanf(m_inputBuf + 12 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code);
+      DBG("+CME ERROR: %d result received", m_transactionResult.code);
+      m_transactionResult.result = ATResult::AT_CME_ERROR;
+      m_transactionState = IDLE;
+      int* msg = m_AT2Env.alloc(osWaitForever);
+      *msg = AT_RESULT_READY;
+      m_AT2Env.put(msg); //Command has been processed
+      return OK;
+    }
+    else if(strstr(m_inputBuf, "+CMS ERROR:") == m_inputBuf) //SIM Error
+    {
+      std::sscanf(m_inputBuf + 13 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code);
+      DBG("+CMS ERROR: %d result received", m_transactionResult.code);
+      m_transactionResult.result = ATResult::AT_CMS_ERROR;
+      m_transactionState = IDLE;
+      int* msg = m_AT2Env.alloc(osWaitForever);
+      *msg = AT_RESULT_READY;
+      m_AT2Env.put(msg); //Command has been processed
+      return OK;
+    }
+    else
+    {
+      DBG("Unprocessed result received: '%s'", m_inputBuf);
+      //Must call transaction processor to complete line processing
+      int ret = m_pTransactionProcessor->onNewATResponseLine(this, m_inputBuf); //Here sendData can be called
+      return ret;
+    }
+  }
+
+  return OK;
+}
+
+int ATCommandsInterface::processEntryPrompt()
+{
+  DBG("Calling prompt handler");
+  int ret = m_pTransactionProcessor->onNewEntryPrompt(this); //Here sendData can be called
+
+  if( ret != NET_MOREINFO ) //A new prompt is expected
+  {
+    DBG("Sending break character");
+    //Send CTRL+Z (break sequence) to exit prompt
+    char seq[2] = {BRK, 0x00};
+    sendData(seq);
+  }
+  return OK;
+}
+
+//This will be called on initialization & after the execution of a command
+void ATCommandsInterface::enableEvents()
+{
+  //Advertize this to events handlers
+  m_eventsMgmtMtx.lock();
+  for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
+  {
+    if( m_eventsHandlers[i] != NULL )
+    {
+      m_eventsHandlers[i]->onDispatchStart();
+      //Enable this kind of events
+      if(m_eventsHandlers[i]->getEventsEnableCommand() != NULL)
+      {
+        int ret = executeInternal(m_eventsHandlers[i]->getEventsEnableCommand(), this, NULL); //Execute enable command
+        if(ret)
+        {
+          WARN("Events enabling command failed");
+        }
+      }
+    }
+  }
+  m_eventsMgmtMtx.unlock();
+}
+
+//This will be called on de-initialization & before the execution of a command to prevent unsollicited result codes from polluting the results
+void ATCommandsInterface::disableEvents()
+{
+  //Advertize this to events handlers
+  m_eventsMgmtMtx.lock();
+  for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
+  {
+    if( m_eventsHandlers[i] != NULL )
+    {
+      m_eventsHandlers[i]->onDispatchStart();
+      //Disable this kind of events
+      if(m_eventsHandlers[i]->getEventsDisableCommand() != NULL)
+      {
+        int ret = executeInternal(m_eventsHandlers[i]->getEventsDisableCommand(), this, NULL); //Execute disable command
+        if(ret)
+        {
+          WARN("Events disabling command failed");
+        }
+      }
+    }
+  }
+  m_eventsMgmtMtx.unlock();
+}
+
+//Commands that can be called during onNewATResponseLine callback, additionally to close()
+//Access to this method is protected (can ONLY be called on processing thread during IATCommandsProcessor::onNewATResponseLine execution)
+int ATCommandsInterface::sendData(const char* data)
+{
+  //m_inputBuf is cleared at this point (and MUST therefore be empty)
+  int dataLen = strlen(data);
+  DBG("Sending raw string of length %d", dataLen);
+  int ret = m_pStream->write((uint8_t*)data, dataLen, osWaitForever);
+  if(ret)
+  {
+    WARN("Could not write to stream (returned %d)", ret);
+    return ret;
+  }
+
+  int dataPos = 0;
+  do
+  {
+    //Read echo
+    size_t readLen;
+    int ret = m_pStream->read((uint8_t*)m_inputBuf, &readLen, MIN(dataLen - dataPos, AT_INPUT_BUF_SIZE - 1), osWaitForever); //Make sure we do not read more than needed otherwise it could break the parser
+    if(ret)
+    {
+      WARN("Could not read from stream (returned %d)", ret);
+      m_inputPos = 0; //Reset input buffer state
+      m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
+      return ret;
+    }
+
+    if( memcmp(m_inputBuf, data + dataPos, readLen) != 0 )
+    {
+      //Echo does not match output
+      m_inputBuf[readLen] = '\0';
+      WARN("Echo does not match output, got '%s' instead", m_inputBuf);
+      m_inputPos = 0; //Reset input buffer state
+      m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
+      return NET_DIFF;
+    }
+
+    dataPos += readLen;
+    //If all characters have not been read yet
+
+  } while(dataPos < dataLen);
+
+  DBG("String sent successfully");
+
+  m_inputPos = 0; //Reset input buffer state
+  m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
+
+  return OK;
+}
+
+/*static*/ void ATCommandsInterface::staticCallback(void const* p)
+{
+  ((ATCommandsInterface*)p)->process();
+}
+
+int ATCommandsInterface::ATResultToReturnCode(ATResult result) //Helper
+{
+  if(result.result == ATResult::AT_OK)
+  {
+    return OK;
+  }
+  else
+  {
+    return NET_MOREINFO;
+  }
+}
+
+/*virtual*/ int ATCommandsInterface::onNewATResponseLine(ATCommandsInterface* pInst, const char* line) //Default implementation for simple commands handling
+{
+  return OK;
+}
+
+/*virtual*/ int ATCommandsInterface::onNewEntryPrompt(ATCommandsInterface* pInst) //Default implementation (just sends Ctrl+Z to exit the prompt by returning OK right-away)
+{
+  return OK;
+}
+
+void ATCommandsInterface::process() //Processing thread
+{
+  DBG("AT Thread started");
+  while(true)
+  {
+    DBG("AT Processing on hold");
+    m_processingThread.signal_wait(AT_SIG_PROCESSING_START); //Block until the process is started
+
+    m_processingMtx.lock();
+    DBG("AT Processing started");
+    //First of all discard buffer
+    int ret;
+    size_t readLen;
+    do //Drop everything
+    {
+      ret = m_pStream->read((uint8_t*)m_inputBuf, &readLen, AT_INPUT_BUF_SIZE - 1, 0); //Do NOT wait at this point
+    } while(ret == OK);
+    m_inputPos = 0; //Clear input buffer
+    do
+    {
+      DBG("Trying to send a pending command");
+      trySendCommand(); //This must be tried first as we discarded the buffer before and therefore would be blocking though there is a pending command
+      DBG("Trying to read a new line");
+      tryReadLine();
+    } while( m_processingThread.signal_wait(AT_SIG_PROCESSING_STOP, 0).status != osEventSignal ); //Loop until the process is interrupted
+    m_processingMtx.unlock();
+    DBG("AT Processing stopped");
+  }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/at/ATCommandsInterface.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,153 @@
+/* ATCommandsInterface.h */
+/* 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 ATCOMMANDSINTERFACE_H_
+#define ATCOMMANDSINTERFACE_H_
+
+#include "core/fwk.h"
+#include "rtos.h"
+
+#define MAX_AT_EVENTS_HANDLERS 4
+
+class ATCommandsInterface;
+
+/** Interface implemented by components handling AT events
+ *
+ */
+class IATEventsHandler
+{
+protected:
+  virtual bool isATCodeHandled(const char* atCode) = 0; //Is this AT code handled
+  virtual void onDispatchStart() = 0;
+  virtual void onDispatchStop() = 0;
+  virtual char* getEventsEnableCommand() = 0;
+  virtual char* getEventsDisableCommand() = 0;
+  virtual void onEvent(const char* atCode, const char* evt) = 0;
+  friend class ATCommandsInterface;
+};
+
+/** Interface implemented by components executing complex AT commands
+ *
+ */
+class IATCommandsProcessor
+{
+protected:
+  virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line) = 0;
+  virtual int onNewEntryPrompt(ATCommandsInterface* pInst) = 0;
+  friend class ATCommandsInterface;
+};
+
+#define AT_INPUT_BUF_SIZE 192//64
+
+//Signals to be sent to the processing thread
+#define AT_SIG_PROCESSING_START 1
+#define AT_SIG_PROCESSING_STOP 2
+//Messages to be sent to the processing thread
+#define AT_CMD_READY 1
+#define AT_TIMEOUT 2
+#define AT_STOP 3
+//Messages to be sent from the processing thread
+#define AT_RESULT_READY 1
+
+/** AT Commands interface class
+ *
+ */
+class ATCommandsInterface : protected IATCommandsProcessor
+{
+public:
+  ATCommandsInterface(IOStream* pStream);
+
+  //Open connection to AT Interface in order to execute command & register/unregister events
+  int open();
+
+  //Initialize AT link
+  int init();
+
+  //Close connection
+  int close();
+  
+  bool isOpen();
+
+  class ATResult
+  {
+  public:
+    enum { AT_OK, AT_ERROR, AT_CONNECT, AT_CMS_ERROR, AT_CME_ERROR } result;
+    int code;
+  };
+
+  int executeSimple(const char* command, ATResult* pResult, uint32_t timeout=1000);
+  int execute(const char* command, IATCommandsProcessor* pProcessor, ATResult* pResult, uint32_t timeout=1000);
+  
+  int registerEventsHandler(IATEventsHandler* pHdlr);
+  int deregisterEventsHandler(IATEventsHandler* pHdlr);
+
+  //Commands that can be called during onNewATResponseLine callback, additionally to close()
+  //Access to this method is protected (can ONLY be called on processing thread during IATCommandsProcessor::onNewATResponseLine execution)
+  int sendData(const char* data);
+
+  static void staticCallback(void const* p);
+private:
+  int executeInternal(const char* command, IATCommandsProcessor* pProcessor, ATResult* pResult, uint32_t timeout=1000);
+  
+  int tryReadLine();
+  int trySendCommand();
+  int processReadLine();
+  int processEntryPrompt();
+  
+  void enableEvents();
+  void disableEvents();
+
+  int ATResultToReturnCode(ATResult result); //Helper
+
+  virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line); //Default implementation for simple commands handling
+  virtual int onNewEntryPrompt(ATCommandsInterface* pInst); //Default implementation (just sends Ctrl+Z to exit the prompt)
+
+  void process(); //Processing thread
+
+  IOStream* m_pStream;
+  
+  bool m_open; //< TRUE when the AT interface is open, and FALSE when it is not.
+
+  const char* m_transactionCommand;
+  const char* m_transactionData;
+
+  IATCommandsProcessor* m_pTransactionProcessor;
+  ATResult m_transactionResult;
+
+  enum { IDLE, COMMAND_SENT, READING_RESULT, ABORTED } m_transactionState;
+
+  char m_inputBuf[AT_INPUT_BUF_SIZE]; // Stores characters received from the modem.
+  int m_inputPos; // Current position of fill pointer in the input buffer.
+
+  Mutex m_transactionMtx;
+
+  // These are RTOS queues, concurrent access protected. In this case both only contain an integer.
+  Mail<int,1> m_env2AT; // used by calling function to inform processing thread of events
+  Mail<int,1> m_AT2Env; // used by processing thread to inform calling function of events
+
+  IATEventsHandler* m_eventsHandlers[MAX_AT_EVENTS_HANDLERS]; // all registered events handlers
+
+  Mutex m_processingMtx;
+  Thread m_processingThread;
+
+  Mutex m_eventsMgmtMtx; //Lock events use within the calling thread
+  Mutex m_eventsProcessingMtx; //Lock events use within the processing thread
+};
+
+#endif /* ATCOMMANDSINTERFACE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/IOStream.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,59 @@
+/* IOStream.h */
+/* 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 IOSTREAM_H_
+#define IOSTREAM_H_
+
+#include "rtos.h"
+
+class IStream
+{
+public:
+  //IStream();
+  //virtual ~IStream();
+
+    //0 for non-blocking (returns immediately), osWaitForever for infinite blocking
+    virtual int read(uint8_t* buf, size_t* pLength, size_t maxLength, uint32_t timeout=osWaitForever) = 0;
+    virtual size_t available() = 0;
+    virtual int waitAvailable(uint32_t timeout=osWaitForever) = 0; //Wait for data to be available
+    virtual int abortRead() = 0; //Abort current reading (or waiting) operation
+};
+
+class OStream
+{
+public:
+  //OStream();
+  //virtual ~OStream();
+
+    //0 for non-blocking (returns immediately), osWaitForever for infinite blocking
+    virtual int write(uint8_t* buf, size_t length, uint32_t timeout=osWaitForever) = 0;
+    virtual size_t space() = 0;
+    virtual int waitSpace(uint32_t timeout=osWaitForever) = 0; //Wait for space to be available
+    virtual int abortWrite() = 0; //Abort current writing (or waiting) operation
+};
+
+class IOStream : public IStream, public OStream
+{
+public:
+  //IOStream();
+  //virtual ~IOStream();
+};
+
+
+#endif /* IOSTREAM_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/MtxCircBuffer.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,95 @@
+/* MtxCircBuf.h */
+/* 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 MTXCIRCBUFFER_H
+#define MTXCIRCBUFFER_H
+
+#include "rtos.h"
+
+//Mutex protected circualr buffer
+template<typename T, int size>
+class MtxCircBuffer
+{
+public:
+  MtxCircBuffer() //:
+      //mtx()
+  {
+    write = 0;
+    read = 0;
+  }
+
+  bool isFull()
+  {
+    mtx.lock();
+    bool r = (((write + 1) % size) == read);
+    mtx.unlock();
+    return r;
+  }
+
+  bool isEmpty()
+  {
+    mtx.lock();
+    bool r = (read == write);
+    mtx.unlock();
+    return r;
+  }
+
+  void queue(T k)
+  {
+    mtx.lock();
+    buf[write++] = k;
+    write %= size;
+    if (isFull())
+    {
+        read++;
+        read %= size;
+    }
+    mtx.unlock();
+  }
+
+  uint16_t available()
+  {
+    mtx.lock();
+    uint16_t a = (write >= read) ? (write - read) : (size - read + write);
+    mtx.unlock();
+    return a;
+  }
+
+  bool dequeue(T * c)
+  {
+    mtx.lock();
+    bool empty = (read == write);
+    if (!empty)
+    {
+      *c = buf[read++];
+      read %= size;
+    }
+    mtx.unlock();
+    return (!empty);
+  }
+
+private:
+  volatile uint16_t write;
+  volatile uint16_t read;
+  volatile T buf[size];
+  Mutex mtx;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/config.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,27 @@
+/* config.h */
+/* 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 CONFIG_H_
+#define CONFIG_H_
+
+
+//Configuration
+#define AT_THREAD_PRIORITY 0
+
+
+#endif /* CONFIG_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/dbg.cpp	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,127 @@
+/* dbg.cpp */
+/* 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 "dbg.h"
+
+#include "mbed.h"
+#include "rtos.h"
+
+#include <cstdio>
+#include <cstdarg>
+
+using namespace std;
+
+static Serial debug_pc(USBTX, USBRX);
+
+static char debug_newline[3];
+
+static void debug_lock(bool set)
+{
+  static Mutex* mtx = new Mutex(); //Singleton runtime initialisation to avoid static initialisation chaos problems
+  static bool init = false;
+  if(set)
+  {
+    mtx->lock();
+    if(!init)
+    {
+      strncpy( debug_newline, "\n", 2 );
+      printf("[START]\n");
+      fflush(stdout);
+      init = true;
+    }
+  }
+  else
+  {
+    mtx->unlock();
+  }
+}
+
+void debug_init()
+{
+  debug_lock(true); //Force init
+  debug_lock(false);
+}
+
+void debug_set_newline(const char* newline)
+{
+  debug_lock(true);
+  strncpy( debug_newline, newline, 2 );
+  debug_newline[2] = '\0';
+  debug_lock(false);
+}
+
+void debug_set_speed(int speed)
+{
+  debug_pc.baud(speed);
+}
+
+void debug(int level, const char* module, int line, const char* fmt, ...)
+{
+  debug_lock(true);
+  switch(level)
+  {
+  default:
+  case 1:
+    printf("[ERROR]");
+    break;
+  case 2:
+    printf("[WARN]");
+    break;
+  case 3:
+    printf("[INFO]");
+    break;
+  case 4:
+    printf("[DBG]");
+    break;
+  }
+
+  printf(" Module %s - Line %d: ", module, line);
+
+  va_list argp;
+
+  va_start(argp, fmt);
+  vprintf(fmt, argp);
+  va_end(argp);
+
+  printf(debug_newline);
+
+  fflush(stdout);
+
+  debug_lock(false);
+
+}
+
+void debug_error(const char* module, int line, int ret)
+{
+  debug_lock(true);
+  printf("[RC] Module %s - Line %d : Error %d\n", module, line, ret);
+  fflush(stdout);
+  debug_lock(false);
+}
+
+void debug_exact(const char* fmt, ...)
+{
+  debug_lock(true);
+  va_list argp;
+
+  va_start(argp, fmt);
+  vprintf(fmt, argp);
+  va_end(argp);
+  debug_lock(false);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/dbg.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,79 @@
+/* dbg.h */
+/* 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 DBG_H_
+#define DBG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+void debug_init(void);
+void debug(int level, const char* module, int line, const char* fmt, ...);
+void debug_set_newline(const char* newline);
+void debug_set_speed(int speed);
+void debug_error(const char* module, int line, int ret);
+void debug_exact(const char* fmt, ...);
+
+#define DBG_INIT() do{ debug_init(); }while(0)
+
+#define DBG_SET_NEWLINE( x ) do{ debug_set_newline(x); }while(0)
+
+#define DBG_SET_SPEED( x ) do{ debug_set_speed(x); }while(0)
+
+#if __DEBUG__ > 0
+#ifndef __MODULE__
+#error "__MODULE__ must be defined"
+#endif
+#endif
+
+#if __DEBUG__ >= 1
+#define ERR(...) do{ debug(1, __MODULE__, __LINE__, __VA_ARGS__); }while(0)
+#else
+#define ERR(...) do{ }while(0)
+#endif
+
+#if __DEBUG__ >= 2
+#define WARN(...) do{ debug(2, __MODULE__, __LINE__, __VA_ARGS__); }while(0)
+#else
+#define WARN(...) do{ }while(0)
+#endif
+
+#if __DEBUG__ >= 3
+#define INFO(...) do{ debug(3, __MODULE__, __LINE__, __VA_ARGS__); }while(0)
+#define CHECK(ret) do{ if(ret){ debug_error(__MODULE__, __LINE__, ret); } }while(0)
+#else
+#define INFO(...) do{ }while(0)
+#define CHECK(ret) do{ }while(0)
+#endif
+
+#if __DEBUG__ >= 4
+#define DBG(...) do{ debug(4, __MODULE__, __LINE__, __VA_ARGS__); }while(0)
+#define DBGX(...) do{ debug_exact(__VA_ARGS__); }while(0)
+#else
+#define DBG(...) do{ }while(0)
+#define DBGX(...) do{ }while(0)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* DBG_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/errors.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,47 @@
+/* errors.h */
+/* 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 ERRORS_H_
+#define ERRORS_H_
+
+/** \page Network-related errors */
+
+#define OK 0 //No error
+
+#define NET_FULL 1 //>All available resources are already used
+#define NET_EMPTY 2 //>No resource
+#define NET_NOTFOUND 3 //>Element cannot be found
+#define NET_INVALID 4 //>Invalid
+#define NET_CONTEXT 5 //>Called in a wrong context (eg during an interrupt)
+#define NET_TIMEOUT 6 //>Timeout
+#define NET_UNKNOWN 7 //>Unknown error
+#define NET_OVERFLOW 8 //>Overflow
+#define NET_PROCESSING 9 //>Command is processing
+#define NET_INTERRUPTED 10 //>Current operation has been interrupted
+#define NET_MOREINFO 11 //>More info on this error can be retrieved elsewhere (eg in a parameter passed as ptr)
+#define NET_ABORT 12 //>Current operation must be aborted
+#define NET_DIFF 13 //>Items that should match are different
+#define NET_AUTH 14 //>Authentication failed
+#define NET_PROTOCOL 15 //>Protocol error
+#define NET_OOM 16 //>Out of memory
+#define NET_CONN 17 //>Connection error
+#define NET_CLOSED 18 //>Connection was closed by remote end
+#define NET_TOOSMALL 19 //>Buffer is too small
+
+#endif /* ERRORS_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/fwk.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,61 @@
+/* fwk.h */
+/* 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 FWK_H_
+#define FWK_H_
+
+#include "config.h"
+
+#include "string.h"
+//using namespace std;
+
+#include "stdint.h"
+typedef unsigned int size_t;
+
+#ifndef __cplusplus
+//boolean type compatibility
+typedef byte bool;
+#define true 1
+#define false 0
+#endif
+
+#ifndef NULL
+#define NULL ((void*)0)
+#endif
+
+#define CR '\x0D'
+#define LF '\x0A'
+#define GD '\x3E'
+#define BRK '\x1A'
+
+//Custom utility classes
+#include "IOStream.h"
+//#include "String.h"
+
+//Error codes
+#include "errors.h"
+
+//Debug
+#include "dbg.h"
+
+//Utility macros
+#define MIN(x,y) (((x)<(y))?(x):(y))
+#define MAX(x,y) (((x)>(y))?(x):(y))
+
+#endif /* FWK_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ip/IPInterface.cpp	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,81 @@
+/* IPInterface.cpp */
+/* 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 "core/fwk.h"
+
+#include "IPInterface.h"
+
+#include <cstring> //For strcpy
+
+
+IPInterface::IPInterface() : m_connected(false)
+{
+
+}
+
+/*virtual*/ IPInterface::~IPInterface()
+{
+
+}
+
+void IPInterface::registerAsDefaultInterface() //First come, first served
+{
+  s_pDefaultInterface = this;
+}
+
+void IPInterface::unregisterAsDefaultInterface() //Must be called before inst is destroyed to avoid invalid ptr fault
+{
+  s_pDefaultInterface = NULL;
+}
+
+/*static*/ IPInterface* IPInterface::getDefaultInterface() //For use by TCP, UDP sockets library
+{
+  return s_pDefaultInterface;
+}
+
+/*static*/ IPInterface* IPInterface::s_pDefaultInterface = NULL;
+
+
+char* IPInterface::getIPAddress() //Get IP Address as a string ('a.b.c.d')
+{
+  if(isConnected())
+  {
+    return m_ipAddr;
+  }
+  else
+  {
+    return NULL;
+  }
+}
+
+bool IPInterface::isConnected() //Is the interface connected?
+{
+  return m_connected;
+}
+
+void IPInterface::setIPAddress(char* ipAddr)
+{
+  std::strcpy(m_ipAddr, ipAddr); //Let's trust the derived class not to buffer overflow us
+}
+
+void IPInterface::setConnected(bool connected)
+{
+  m_connected = connected;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ip/IPInterface.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,60 @@
+/* IPInterface.h */
+/* 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 IPINTERFACE_H_
+#define IPINTERFACE_H_
+
+#include "core/fwk.h"
+
+/** Generic IP-based network interface
+ *
+ */
+class IPInterface
+{
+public:
+    IPInterface();
+    virtual ~IPInterface();
+
+    //int init(); //Initialize interface; no connection should be performed at this stage
+    virtual int connect() = 0; //Do connect the interface
+    virtual int disconnect() = 0;
+    //It is encouraged that the derived class implement a "setup(...)" function to configure the interface before the connection
+    
+    char* getIPAddress(); //Get IP Address as a string ('a.b.c.d')
+    bool isConnected(); //Is the interface connected?
+
+    static IPInterface* getDefaultInterface(); //For use by TCP, UDP sockets library
+
+    //WARN: Implementation will have to be more careful in case of multiple interfaces (or implement a routing protocol based on local IP addresses differentiation)
+    void registerAsDefaultInterface(); //First come, first served
+    void unregisterAsDefaultInterface(); //Must be called before inst is destroyed to avoid invalid ptr fault
+
+protected:
+    //Must be called by subclasses
+    void setIPAddress(char* ipAddr);
+    void setConnected(bool connected);
+
+private:
+    char m_ipAddr[16];
+    bool m_connected;
+
+    static IPInterface* s_pDefaultInterface;
+};
+
+#endif /* IPINTERFACE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ip/LwIPInterface.cpp	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,52 @@
+/* LwIPInterface.cpp */
+/* 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 "core/fwk.h"
+
+#include "LwIPInterface.h"
+
+extern "C" {
+#include "lwip/init.h"
+#include "lwip/tcpip.h"
+}
+
+LwIPInterface::LwIPInterface() : IPInterface(), m_rdySphre(1)
+{
+  m_rdySphre.wait();
+}
+
+LwIPInterface::~LwIPInterface()
+{
+
+}
+
+int LwIPInterface::init() //Init LwIP-specific stuff, create the right bindings, etc
+{
+  //lwip_init(); //All LwIP initialisation functions called on a per-module basis (according to lwipopts.h)
+  tcpip_init(LwIPInterface::tcpipRdyCb, this); //Start TCP/IP processing thread
+  m_rdySphre.wait(); //Wait for callback to produce resource
+  return OK;
+}
+
+/*static*/ void LwIPInterface::tcpipRdyCb(void* ctx) //Result of TCP/IP thread launch
+{
+  LwIPInterface* pIf = (LwIPInterface*) ctx;
+  pIf->m_rdySphre.release();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ip/LwIPInterface.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,44 @@
+/* LwIPInterface.h */
+/* 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 LWIPINTERFACE_H_
+#define LWIPINTERFACE_H_
+
+#include "core/fwk.h"
+#include "IPInterface.h"
+
+#include "rtos.h"
+
+/** LwIP-based network interface
+ *
+ */
+class LwIPInterface : public IPInterface
+{
+public:
+    LwIPInterface();
+    virtual ~LwIPInterface();
+
+    int init(); //Init LwIP-specific stuff, create the right bindings, etc
+    
+private:
+    static void tcpipRdyCb(void* ctx); //Result of TCP/IP thread launch
+    Semaphore m_rdySphre;
+};
+
+#endif /* LWIPINTERFACE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ip/PPPIPInterface.cpp	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,466 @@
+/* PPPIPInterface.cpp */
+/* 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.
+ */
+
+#define __DEBUG__ 0
+#ifndef __MODULE__
+#define __MODULE__ "PPPIPInterface.cpp"
+#endif
+
+#include "core/fwk.h"
+#include "rtos.h"
+
+#include <cstdio>
+using std::sscanf;
+
+#include "PPPIPInterface.h"
+
+#define MSISDN "*99#"
+
+#define CONNECT_CMD_PREFIX "ATD "
+#define CONNECT_CMD_SUFFIX "\x0D"
+#define EXPECTED_RESP_SUFFIX "\x0D" "\x0A" "CONNECT" "\x0D" "\x0A"
+#define EXPECTED_RESP_DATARATE_SUFFIX "\x0D" "\x0A" "CONNECT %d" "\x0D" "\x0A"
+#define EXPECTED_RESP_MIN_LEN 20
+#define OK_RESP "\x0D" "\x0A" "OK" "\x0D" "\x0A"
+#define ESCAPE_SEQ "+++"
+#define HANGUP_CMD "ATH" "\x0D"
+#define NO_CARRIER_RESP "\x0D" "\x0A" "NO CARRIER" "\x0D" "\x0A"
+extern "C" {
+#include "lwip/ip_addr.h"
+#include "lwip/inet.h"
+#include "lwip/err.h"
+#include "lwip/dns.h"
+
+#include "netif/ppp/ppp.h"
+}
+
+PPPIPInterface::PPPIPInterface(IOStream* pStream, const char* msisdn) : LwIPInterface(), m_linkStatusSphre(1), m_pppErrCode(0), m_pStream(pStream), m_streamAvail(true), m_pppd(-1)
+{
+  m_connectCmd = new char[strlen(CONNECT_CMD_PREFIX) + strlen(msisdn) + strlen(CONNECT_CMD_SUFFIX) + 1];
+  sprintf(m_connectCmd, "%s%s%s", CONNECT_CMD_PREFIX, msisdn, CONNECT_CMD_SUFFIX);
+  m_expectedResp = new char[strlen(m_connectCmd) + strlen(EXPECTED_RESP_SUFFIX) + 1];
+  sprintf(m_expectedResp, "%s%s", m_connectCmd, EXPECTED_RESP_SUFFIX);
+  m_expectedRespDatarate = new char[strlen(m_connectCmd) + strlen(EXPECTED_RESP_DATARATE_SUFFIX) + 1];
+  sprintf(m_expectedRespDatarate, "%s%s", m_connectCmd, EXPECTED_RESP_DATARATE_SUFFIX);
+  m_linkStatusSphre.wait();
+}
+
+/*virtual*/ PPPIPInterface::~PPPIPInterface()
+{
+  delete m_connectCmd;
+  delete m_expectedResp;
+  delete m_expectedRespDatarate;
+}
+
+/*virtual*/ int PPPIPInterface::init() //Init PPP-specific stuff, create the right bindings, etc
+{
+  DBG("Initializing LwIP");
+  LwIPInterface::init(); //Init LwIP, NOT including PPP
+  DBG("Initializing PPP");
+  pppInit();
+  DBG("Done");
+  return OK;
+}
+
+int PPPIPInterface::setup(const char* user, const char* pw)
+{
+  DBG("Configuring PPP authentication method");
+  pppSetAuth(PPPAUTHTYPE_ANY, user, pw);
+  DBG("Done");
+  return OK;
+}
+
+/*virtual*/ int PPPIPInterface::connect()
+{
+  int ret;
+  char buf[32];
+  size_t len;
+  DBG("Trying to connect with PPP");
+  
+  cleanupLink();
+  
+  DBG("Sending %s", m_connectCmd);
+  
+  ret = m_pStream->write((uint8_t*)m_connectCmd, strlen(m_connectCmd), osWaitForever);
+  if( ret != OK )
+  {
+    return NET_UNKNOWN;
+  }
+  
+  DBG("Expect %s", m_expectedResp);
+    
+  len = 0;
+  size_t readLen;
+  ret = m_pStream->read((uint8_t*)buf + len, &readLen, EXPECTED_RESP_MIN_LEN, 10000);
+  if( ret != OK )
+  {
+    return NET_UNKNOWN;
+  }
+  len += readLen;
+  while( (len < EXPECTED_RESP_MIN_LEN) || (buf[len-1] != LF) )
+  {
+    ret = m_pStream->read((uint8_t*)buf + len, &readLen, 1, 10000);
+    if( ret != OK )
+    {
+      return NET_UNKNOWN;
+    }
+    len += readLen;
+  }
+  
+  buf[len]=0;
+  
+  DBG("Got %s[len %d]", buf, len);
+  
+  int datarate = 0;
+  if( (sscanf(buf, m_expectedRespDatarate, &datarate ) != 1) && (strcmp(m_expectedResp, buf) != 0) )
+  {
+    //Discard buffer
+    do //Clear buf
+    {
+      ret = m_pStream->read((uint8_t*)buf, &len, 32, 0);
+    } while( (ret == OK) && (len > 0) );
+    return NET_CONN;
+  }
+      
+  DBG("Transport link open");
+  if(datarate != 0)
+  {
+    DBG("Datarate: %d bps", datarate);
+  }
+  m_linkStatusSphre.wait(0);
+  if((m_pppd != -1) && (m_pppErrCode == 0)) //Already connected
+  {
+    return NET_INVALID;
+  }
+  
+  ret = pppOverSerialOpen(this, PPPIPInterface::linkStatusCb, this);
+  if(ret < 0)
+  {
+    switch(ret)
+    {
+    case PPPERR_OPEN:
+    default:
+      return NET_FULL; //All available resources are already used
+    }
+  }
+  m_pppd = ret; //PPP descriptor
+  m_linkStatusSphre.wait(); //Block indefinitely; there should be a timeout there
+  if(m_pppErrCode != PPPERR_NONE)
+  {
+    m_pppd = -1;
+  }
+  switch(m_pppErrCode)
+  {
+  case PPPERR_NONE: //Connected OK
+    return OK;
+  case PPPERR_CONNECT: //Connection lost
+    return NET_INTERRUPTED;
+  case PPPERR_AUTHFAIL: //Authentication failed
+    return NET_AUTH;
+  case PPPERR_PROTOCOL: //Protocol error
+    return NET_PROTOCOL;
+  default:
+    return NET_UNKNOWN;
+  }
+}
+
+/*virtual*/ int PPPIPInterface::disconnect()
+{
+  int ret = m_linkStatusSphre.wait(0);
+  if(ret > 0) //Already disconnected?
+  {
+    m_pppd = -1; //Discard PPP descriptor
+    switch(m_pppErrCode)
+      {
+      case PPPERR_CONNECT: //Connection terminated
+      case PPPERR_AUTHFAIL: //Authentication failed
+      case PPPERR_PROTOCOL: //Protocol error
+      case PPPERR_USER:
+        return OK;
+      default:
+        return NET_UNKNOWN;
+      }
+  }
+  else
+  {
+    if(m_pppd == -1)
+    {
+      return NET_INVALID;
+    }
+    pppClose(m_pppd);
+    do
+    {
+      m_linkStatusSphre.wait(); //Block indefinitely; there should be a timeout there
+      DBG("Received PPP err code %d", m_pppErrCode);
+    } while(m_pppErrCode != PPPERR_USER);
+    m_pppd = -1; //Discard PPP descriptor
+  }
+  
+  DBG("Sending %s", ESCAPE_SEQ);
+  
+  ret = m_pStream->write((uint8_t*)ESCAPE_SEQ, strlen(ESCAPE_SEQ), osWaitForever);
+  if( ret != OK )
+  {
+    return NET_UNKNOWN;
+  }
+  
+  cleanupLink();
+  
+  return OK;
+}
+
+
+int PPPIPInterface::cleanupLink()
+{
+  int ret;
+  char buf[32];
+  size_t len;
+  
+  do //Clear buf
+  {
+    ret = m_pStream->read((uint8_t*)buf, &len, 32, 100);
+    if(ret == OK)
+    {
+      buf[len] = '\0';
+      DBG("Got %s", buf);
+    }
+  } while( (ret == OK) && (len > 0) );
+  
+  DBG("Sending %s", HANGUP_CMD);
+  
+  ret = m_pStream->write((uint8_t*)HANGUP_CMD, strlen(HANGUP_CMD), osWaitForever);
+  if( ret != OK )
+  {
+    return NET_UNKNOWN;
+  }
+     
+  size_t readLen;
+  
+  //Hangup
+  DBG("Expect %s", HANGUP_CMD);
+
+  len = 0;
+  while( len < strlen(HANGUP_CMD) )
+  {
+    ret = m_pStream->read((uint8_t*)buf + len, &readLen, strlen(HANGUP_CMD) - len, 100);
+    if( ret != OK )
+    {
+      break;
+    }
+    len += readLen;
+    /////
+    buf[len]=0;
+    DBG("Got %s", buf);
+  }
+  
+  buf[len]=0;
+  
+  DBG("Got %s[len %d]", buf, len);
+  
+  //OK response
+  DBG("Expect %s", OK_RESP);
+
+  len = 0;
+  while( len < strlen(OK_RESP) )
+  {
+    ret = m_pStream->read((uint8_t*)buf + len, &readLen, strlen(OK_RESP) - len, 100);
+    if( ret != OK )
+    {
+      break;
+    }
+    len += readLen;
+    /////
+    buf[len]=0;
+    DBG("Got %s", buf);
+  }
+  
+  buf[len]=0;
+  
+  DBG("Got %s[len %d]", buf, len);
+  
+  //NO CARRIER event
+  DBG("Expect %s", NO_CARRIER_RESP);
+
+  len = 0;
+  while( len < strlen(NO_CARRIER_RESP) )
+  {
+    ret = m_pStream->read((uint8_t*)buf + len, &readLen, strlen(NO_CARRIER_RESP) - len, 100);
+    if( ret != OK )
+    {
+      break;
+    }
+    len += readLen;
+    /////
+    buf[len]=0;
+    DBG("Got %s", buf);
+  }
+  
+  buf[len]=0;
+  
+  DBG("Got %s[len %d]", buf, len);
+  
+  do //Clear buf
+  {
+    ret = m_pStream->read((uint8_t*)buf, &len, 32, 100);
+    if(ret == OK)
+    {
+      buf[len] = '\0';
+      DBG("Got %s", buf);
+    }
+  } while( (ret == OK) && (len > 0) );
+  
+  
+  return OK;
+}
+
+/*static*/ void PPPIPInterface::linkStatusCb(void *ctx, int errCode, void *arg) //PPP link status
+{
+  PPPIPInterface* pIf = (PPPIPInterface*)ctx;
+  struct ppp_addrs* addrs = (struct ppp_addrs*) arg;
+
+  switch(errCode)
+  {
+  case PPPERR_NONE:
+    WARN("Connected via PPP.");
+    DBG("Local IP address: %s", inet_ntoa(addrs->our_ipaddr));
+    DBG("Netmask: %s", inet_ntoa(addrs->netmask));
+    DBG("Remote IP address: %s", inet_ntoa(addrs->his_ipaddr));
+    DBG("Primary DNS: %s", inet_ntoa(addrs->dns1));
+    DBG("Secondary DNS: %s", inet_ntoa(addrs->dns2));
+    //Setup DNS
+    if (addrs->dns1.addr != 0)
+    {
+      dns_setserver(0, (struct ip_addr*)&(addrs->dns1));
+    }
+    if (addrs->dns2.addr != 0)
+    {
+      dns_setserver(1, (struct ip_addr*)&(addrs->dns1));
+    }
+        
+    pIf->setConnected(true);
+    pIf->setIPAddress(inet_ntoa(addrs->our_ipaddr));
+    break;
+  case PPPERR_CONNECT: //Connection lost
+    WARN("Connection lost/terminated");
+    pIf->setConnected(false);
+    break;
+  case PPPERR_AUTHFAIL: //Authentication failed
+    WARN("Authentication failed");
+    pIf->setConnected(false);
+    break;
+  case PPPERR_PROTOCOL: //Protocol error
+    WARN("Protocol error");
+    pIf->setConnected(false);
+    break;
+  case PPPERR_USER:
+    WARN("Disconnected by user");
+    pIf->setConnected(false);
+    break;
+  default:
+    WARN("Unknown error (%d)", errCode);
+    pIf->setConnected(false);
+    break;
+  }
+
+  pIf->m_linkStatusSphre.wait(0); //If previous event has not been handled, "delete" it now
+  pIf->m_pppErrCode = errCode;
+  pIf->m_linkStatusSphre.release();
+}
+
+//LwIP PPP implementation
+extern "C"
+{
+
+/**
+ * Writes to the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data to send
+ * @param len length (in bytes) of data to send
+ * @return number of bytes actually sent
+ *
+ * @note This function will block until all data can be sent.
+ */
+u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len)
+{
+  DBG("sio_write");
+  PPPIPInterface* pIf = (PPPIPInterface*)fd;
+  int ret;
+  if(!pIf->m_streamAvail) //If stream is not available (it is a shared resource) don't go further
+  {
+    return 0;
+  }
+  ret = pIf->m_pStream->write(data, len, osWaitForever); //Blocks until all data is sent or an error happens
+  if(ret != OK)
+  {
+    return 0;
+  }
+  return len;
+}
+
+/**
+ * Reads from the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @return number of bytes actually received - may be 0 if aborted by sio_read_abort
+ *
+ * @note This function will block until data can be received. The blocking
+ * can be cancelled by calling sio_read_abort().
+ */
+u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len)
+{
+  DBG("sio_read");
+  PPPIPInterface* pIf = (PPPIPInterface*)fd;
+  int ret;
+  size_t readLen;
+  if(!pIf->m_streamAvail) //If stream is not available (it is a shared resource) don't go further
+  {
+    WARN("EXIT NOT AVAIL");
+    return 0;
+  }
+  ret = pIf->m_pStream->read(data, &readLen, len, osWaitForever); //Blocks until some data is received or an error happens
+  if(ret != OK)
+  {
+    return 0;
+  }
+  DBG("ret");
+  return readLen;
+}
+
+/**
+ * Aborts a blocking sio_read() call.
+ *
+ * @param fd serial device handle
+ */
+void sio_read_abort(sio_fd_t fd)
+{
+  DBG("sio_read_abort");
+  PPPIPInterface* pIf = (PPPIPInterface*)fd;
+  if(!pIf->m_streamAvail) //If stream is not available (it is a shared resource) don't go further
+  {
+    return;
+  }
+  pIf->m_pStream->abortRead();
+  DBG("ret");
+}
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ip/PPPIPInterface.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,72 @@
+/* PPPIPInterface.h */
+/* 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 PPPIPINTERFACE_H_
+#define PPPIPINTERFACE_H_
+
+#include "core/fwk.h"
+
+#include "LwIPInterface.h"
+
+#include "lwip/sio.h"
+
+namespace rtos {
+class Semaphore;
+}
+using namespace rtos;
+
+#define DEFAULT_MSISDN_GSM    "*99#"
+#define DEFAULT_MSISDN_CDMA   "#777"
+
+/** Interface using PPP to connect to an IP-based network
+ *
+ */
+class PPPIPInterface : public LwIPInterface
+{
+public:
+    PPPIPInterface(IOStream* pStream, const char* msisdn);
+    virtual ~PPPIPInterface();
+
+    int init(); //Init PPP-specific stuff, create the right bindings, etc
+    int setup(const char* user, const char* pw); //Setup authentication
+    virtual int connect();
+    virtual int disconnect();
+
+private:
+    int cleanupLink();
+
+    static void linkStatusCb(void *ctx, int errCode, void *arg); //PPP link status
+    Semaphore m_linkStatusSphre;
+    int m_pppErrCode;
+
+    IOStream* m_pStream; //Serial stream
+    bool m_streamAvail;
+
+    int m_pppd;
+
+    friend u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len);
+    friend u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len);
+    friend void sio_read_abort(sio_fd_t fd);
+
+    char* m_connectCmd;
+    char* m_expectedResp;
+    char* m_expectedRespDatarate;
+};
+
+#endif /* PPPIPINTERFACE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/link/LinkMonitor.cpp	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,148 @@
+/* LinkMonitor.cpp */
+/* 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.
+ */
+
+#define __DEBUG__ 0
+#ifndef __MODULE__
+#define __MODULE__ "LinkMonitor.cpp"
+#endif
+
+#include "core/fwk.h"
+
+#include "LinkMonitor.h"
+
+#include <cstdio>
+using std::sscanf;
+
+#define DEFAULT_TIMEOUT 10000
+
+LinkMonitor::LinkMonitor(ATCommandsInterface* pIf) : m_pIf(pIf), m_rssi(0), m_registrationState(REGISTRATION_STATE_UNKNOWN), m_bearer(BEARER_UNKNOWN)
+{
+
+}
+
+int LinkMonitor::init()
+{
+  // we need to make sure that we setup the operator selection to be in 'numeric' format.
+  // i.e. it is made up of a network and country code when returned by the modem e.g. Operator = 23415. This allows easy logic parsing for
+  // setting up other network parameters in future.
+  DBG("LinkMonitor::init() being called. This should only happen once: executinging AT+COPS=0,2");  
+  int ret = m_pIf->executeSimple("AT+COPS=0,2", NULL, DEFAULT_TIMEOUT); //Configure to set the operator string to Country Code and mobile network code
+  if(ret != OK)
+  {
+    WARN(" NET_PROTOCOL error from sending the AT+COPS command to the modem. ");
+    return NET_PROTOCOL;
+  }
+  return OK;
+}
+
+/*virtual*/ int LinkMonitor::onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
+{
+  DBG("Line is %s", line);
+  int v;
+  if( sscanf(line, "+CREG: %*d,%d", &v) >= 1 ) //Reg state is valid
+  {
+    DBG("+CREG %d", v);
+    switch( v )
+    {
+      case 0:
+        m_registrationState = REGISTRATION_STATE_UNKNOWN;
+        break;
+      case 1:
+        m_registrationState = REGISTRATION_STATE_HOME_NETWORK;
+        break;
+      case 2:
+        m_registrationState = REGISTRATION_STATE_REGISTERING;
+        break;
+      case 3:
+        m_registrationState = REGISTRATION_STATE_DENIED;  
+        break;
+      case 4:
+        m_registrationState = REGISTRATION_STATE_NO_SIGNAL;  
+        break;        
+      case 5:
+        m_registrationState = REGISTRATION_STATE_ROAMING;  
+        break;
+      default:
+        m_registrationState = REGISTRATION_STATE_UNKNOWN;  
+        break;
+    }
+  }
+  else if( sscanf(line, "+COPS: %*d,%*d,\"%*[^\"]\",%d", &v) >= 1 )
+  {
+    DBG("+COPS %d", v);
+    switch( v )
+    {
+      case 0:
+        m_bearer = BEARER_GSM;
+        break;
+      case 2:
+        m_bearer = BEARER_UMTS;
+        break;
+      case 3:
+        m_bearer = BEARER_EDGE;
+        break;
+      case 4: //HSDPA
+      case 5: //HSUPA
+      case 6: //HSDPA + HSUPA
+        m_bearer = BEARER_HSPA;
+        break;
+      case 7:
+        m_bearer = BEARER_LTE;
+        break;  
+      case 1: //GSM Compact
+      default:
+        m_bearer = BEARER_UNKNOWN;
+        break;
+    }
+  }
+  else if( sscanf(line, "+CSQ: %d,%*d", &v) >= 1 )
+  {
+    DBG("+CSQ %d", v);
+    if(v == 99) //Unknown
+    {
+      m_rssi = 0; //Unknown
+    }
+    else
+    {
+      m_rssi = -113 + 2*v;
+    }
+  }
+  return OK;
+}
+
+/*virtual*/ int LinkMonitor::onNewEntryPrompt(ATCommandsInterface* pInst)
+{
+  return OK;
+}
+
+int LinkMonitor::getState(int* pRssi, REGISTRATION_STATE* pRegistrationState, BEARER* pBearer)
+{
+  m_rssi = 0;
+  m_registrationState = REGISTRATION_STATE_UNKNOWN;
+  m_bearer = BEARER_UNKNOWN;
+  int ret = m_pIf->execute("AT+CREG?;+COPS?;+CSQ", this, NULL, DEFAULT_TIMEOUT); //Configure to get registration info & get it; get signal quality
+  if(ret != OK)
+  {
+    return NET_PROTOCOL;
+  }
+  *pRssi = m_rssi;
+  *pRegistrationState = m_registrationState;
+  *pBearer = m_bearer;
+  return OK;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/link/LinkMonitor.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,90 @@
+/* LinkMonitor.h */
+/* 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 LINKMONITOR_H_
+#define LINKMONITOR_H_
+
+#include "core/fwk.h"
+
+#include "rtos.h"
+
+#include "at/ATCommandsInterface.h"
+
+/** Component to monitor link quality
+ *
+ */
+class LinkMonitor : protected IATCommandsProcessor
+{
+public:
+  /** Create LinkMonitor instance
+     @param pIf Pointer to the ATCommandsInterface instance to use
+   */
+  LinkMonitor(ATCommandsInterface* pIf);
+
+  /** Initialize monitor
+   */
+  int init();
+
+  /** Registration State
+  */  
+  enum REGISTRATION_STATE
+  {
+    REGISTRATION_STATE_UNKNOWN, //!< Unknown
+    REGISTRATION_STATE_REGISTERING, //!< Registering
+    REGISTRATION_STATE_DENIED, //!< Denied
+    REGISTRATION_STATE_NO_SIGNAL, //!< No Signal
+    REGISTRATION_STATE_HOME_NETWORK, //!< Registered on home network
+    REGISTRATION_STATE_ROAMING //!< Registered on roaming network
+  };
+
+  /** Bearer type
+  */
+  enum BEARER
+  {
+    BEARER_UNKNOWN, //!< Unknown
+    BEARER_GSM, //!< GSM (2G)
+    BEARER_EDGE, //!< EDGE (2.5G)
+    BEARER_UMTS, //!< UMTS (3G)
+    BEARER_HSPA, //!< HSPA (3G+)
+    BEARER_LTE //!< LTE (4G)
+  };
+  
+  /** Get link state
+    @param pRssi pointer to store the current RSSI in dBm, between -51 dBm and -113 dBm if known; -51 dBm means -51 dBm or more; -113 dBm means -113 dBm or less; 0 if unknown
+    @param pRegistrationState pointer to store the current registration state
+    @param pBearer pointer to store the current bearer
+    @return 0 on success, error code on failure
+  */
+  int getState(int* pRssi, REGISTRATION_STATE* pRegistrationState, BEARER* pBearer);
+  
+protected:
+  //IATCommandsProcessor
+  virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line);
+  virtual int onNewEntryPrompt(ATCommandsInterface* pInst);
+  
+private:
+  ATCommandsInterface* m_pIf;
+  
+  int m_rssi;
+  REGISTRATION_STATE m_registrationState;
+  BEARER m_bearer;
+
+};
+
+#endif /* LINKMONITOR_H_ */
--- a/lwipopts_conf.h	Thu Oct 17 09:14:07 2013 +0000
+++ b/lwipopts_conf.h	Thu Oct 17 12:29:05 2013 +0300
@@ -24,4 +24,3 @@
 
 #endif /* LWIPOPTS_CONF_H_ */
 
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sms/CDMASMSInterface.cpp	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,348 @@
+/* CDMASMSInterface.cpp */
+/* 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.
+ */
+
+#define __DEBUG__ 0
+#ifndef __MODULE__
+#define __MODULE__ "CDMASMSInterface.cpp"
+#endif
+
+#include "core/fwk.h"
+
+#include "CDMASMSInterface.h"
+
+#include <cstdio>
+#include <cstring>
+
+#define DEFAULT_TIMEOUT 10000
+
+CDMASMSInterface::CDMASMSInterface(ATCommandsInterface* pIf) : m_pIf(pIf), m_msg(NULL), m_maxMsgLength(0), m_msisdn(NULL)
+{
+}
+
+int CDMASMSInterface::init()
+{
+  m_state = SMS_IDLE;
+
+  DBG("Get number of messages in the different inboxes");
+  int ret = updateInbox();
+  if(ret)
+  {
+    return NET_PROTOCOL;
+  }
+  
+  DBG("Initialization done");
+  return OK;
+}
+
+int CDMASMSInterface::send(const char* number, const char* message)
+{
+  if( strlen(number) > 16 )
+  {
+    return NET_INVALID; //Number too long
+  }
+
+  int ret;
+
+  //Prepare infos
+  m_state = SMS_SEND_CMD_SENT;
+  
+  bool intlNumber=(number[0]=='+'); //If the number starts with the + sign, replace it with 011 instead (int'l dialing code in the US)
+
+  DBG("Send SM");
+  //Send command
+  char cmd[32+strlen(message)];
+  std::sprintf(cmd, "AT!SSMS=0,%s%s,,\"%s\"",intlNumber?"011":"", intlNumber?(number+1):number, message); //Send with normal priority
+  ret = m_pIf->execute(cmd, this, NULL, DEFAULT_TIMEOUT);
+
+  if(ret != OK)
+  {
+    WARN("ret %d", ret);
+    m_state = SMS_IDLE;
+    return NET_PROTOCOL;
+  }
+
+  DBG("Check status");
+  m_txState = SMS_PENDING;
+  
+  int tries = 10;
+  while(tries--)
+  {
+    m_state = SMS_GET_TX_STATUS_CMD_SENT;
+    ret = m_pIf->execute("AT!SSMS?", this, NULL, DEFAULT_TIMEOUT);
+    if(ret)
+    {
+      m_state = SMS_IDLE;
+      return ret;
+    }
+    m_state = SMS_IDLE;
+    if(m_txState == SMS_PENDING) //Wait more
+    {
+      Thread::wait(1000);
+      continue;
+    }
+    else if(m_txState == SMS_FAILED)
+    {
+      ERR("The modem could not send the SM");
+      return NET_CONN; //Probably a conenction issue, the user can retry
+    }
+    else
+    {
+      break;
+    }
+  }
+  if(!tries)
+  {
+    ERR("The is still trying to send the SM");
+    return NET_TIMEOUT;
+  }
+  return OK;
+}
+
+
+int CDMASMSInterface::get(char* number, char* message, size_t maxLength)
+{
+  if( maxLength < 1  )
+  {
+    return NET_INVALID; //Buffer too short
+  }
+
+  int ret;
+
+  DBG("Get next message");
+  if( (m_msgInListsCount[0] + m_msgInListsCount[1] + m_msgInListsCount[2]) == 0)
+  {
+    DBG("Message list count is 0 and needs updating. Running updateInbox.");
+    ret = updateInbox();
+    if (ret)
+    {
+      return ret;
+    }
+  }
+
+  if( (m_msgInListsCount[0] + m_msgInListsCount[1] + m_msgInListsCount[2]) == 0)
+  {
+    DBG("Message list count is 0");
+    return NET_EMPTY; //No message to read
+  }
+  
+  //Determine which index to use : 3 (read), then 1 (urgent), then 2 (regular)
+  int index;
+  if(m_msgInListsCount[2])
+  {
+    index = 3;
+  }
+  else if(m_msgInListsCount[0])
+  {
+    index = 1;
+  }
+  else //if(m_msgInListsCount[1])
+  {
+    index = 2;
+  }
+
+  //Prepare infos
+  m_state = SMS_GET_CMD_SENT;
+  m_msisdn = (char*) number;
+  m_msg = (char*) message;
+  m_maxMsgLength = maxLength;
+  m_headersToRead = 3;
+  
+  m_msisdn[0] = '\0';
+
+  DBG("Get SMS");
+  //Read command
+  char cmd[32];
+  std::sprintf(cmd, "AT!GSMS?%d,1", index); //1 is the oldest message
+  ret = m_pIf->execute(cmd, this, NULL, DEFAULT_TIMEOUT);
+  if( ret != OK )
+  {
+    WARN("AT!GSMS returned %d", ret);
+    m_state = SMS_IDLE;
+    return NET_PROTOCOL;
+  }
+  
+  //If message is not read, it will be put at the end of the read list
+  int item;
+  if( index != 3 )
+  {
+    //Decrement count in relevant list
+    m_msgInListsCount[index-1]--;  
+    //Increment count in read list
+    m_msgInListsCount[3-1]++;  
+    item = m_msgInListsCount[3-1];
+    //Normally item should be equal to 1 as we'd have read any older messages first
+    if( item != 1 )
+    {
+      WARN("Still some older messages pending in the read inbox");
+    }
+  }
+  else
+  {
+    //The item is still the oldest one
+    item = 1;
+  }
+  
+  DBG("Deleting message");
+  //Delete message from inbox
+  std::sprintf(cmd, "AT!DSMS=3"/*,%d", item*/); //FIXME why doesn't that work when specifying the index??
+  ret = m_pIf->executeSimple(cmd, NULL, DEFAULT_TIMEOUT);
+  if(ret != OK)
+  {
+    ERR("Could not delete message");
+  }
+  else
+  {
+    //Now we can decrease the number of read messages
+    m_msgInListsCount[3-1]--; 
+  }
+  
+  if (m_state != SMS_CMD_PROCESSED)
+  {
+    WARN("Message could not be retrieved properly");
+    m_state = SMS_IDLE;
+    return NET_EMPTY;
+  }
+  
+  m_state = SMS_IDLE;
+
+  return OK;
+}
+
+
+int CDMASMSInterface::getCount(size_t* pCount)
+{
+  int ret = updateInbox();
+  if(ret)
+  {
+      return NET_PROTOCOL;
+  }
+
+  *pCount = m_msgInListsCount[0] + m_msgInListsCount[1] + m_msgInListsCount[2]; //Urgent messages + regular messages + read messages
+
+  return OK;
+}
+
+
+/*virtual*/ int CDMASMSInterface::onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
+{
+  if(m_state == SMS_SEND_CMD_SENT)
+  {
+    DBG("SMS Send: %s", line);
+  }
+  else if(m_state == SMS_GET_TX_STATUS_CMD_SENT)
+  {
+    if(!strcmp(line, "sent"))
+    {
+      m_txState = SMS_SENT;
+      m_state = SMS_CMD_PROCESSED;
+    }
+    else if(!strcmp(line, "failed"))
+    {
+      m_txState = SMS_FAILED;
+      m_state = SMS_CMD_PROCESSED;
+    }
+    else if(!strcmp(line, "none"))
+    {
+      m_txState = SMS_NONE;
+      m_state = SMS_CMD_PROCESSED;
+    }
+    else if(!strcmp(line, "pending"))
+    {
+      m_txState = SMS_PENDING;
+      m_state = SMS_CMD_PROCESSED;
+    }
+  }
+  else if(m_state == SMS_GET_CMD_SENT)
+  {
+    DBG("Header: %s", line);
+    
+    if(m_msisdn[0]=='\0')
+    {
+      sscanf(line, "From: %16s", m_msisdn);
+    }
+    
+    m_headersToRead--;
+    
+    if(m_headersToRead==0) //End of headers
+    {
+      if(m_msisdn[0]!='\0') //Checks that the incoming number has been retrieved
+      {
+        m_state = SMS_GET_HDR_RECEIVED;
+      }
+      else
+      {
+        m_state = SMS_IDLE; //Error, signal it
+      }
+    }
+  }
+  else if(m_state == SMS_GET_HDR_RECEIVED)
+  {
+    DBG("Message: %s", line);
+    size_t cpyLen = MIN( std::strlen(line), m_maxMsgLength - 1 );
+    std::memcpy( m_msg, line, cpyLen );
+    m_msg[cpyLen] = '\0';
+    m_state = SMS_CMD_PROCESSED;
+  }
+  else if(m_state == SMS_GET_COUNT_CMD_SENT)
+  {
+    DBG("Inbox: %s", line);
+    int index;
+    size_t count;
+    if((strlen(line) > 16) && sscanf(line + 16, "{Index = %d}: %d", &index, &count) == 2)
+    {
+      if((index > 0) && (index <=4))
+      {
+        m_msgInListsCount[index-1] = count;
+      }
+      if(index == 4)
+      {
+        m_state = SMS_CMD_PROCESSED;
+      }
+    }
+  }
+  return OK;
+}
+
+/*virtual*/ int CDMASMSInterface::onNewEntryPrompt(ATCommandsInterface* pInst)
+{
+  return OK;
+}
+
+
+int CDMASMSInterface::updateInbox()
+{
+  //Get number of unread/read messages
+
+  DBG("Updating inbox");
+  m_msgInListsCount[0] = m_msgInListsCount[1] = m_msgInListsCount[2] = m_msgInListsCount[3] = 0; //Reset counts
+
+  //Get counts
+  m_state = SMS_GET_COUNT_CMD_SENT;
+  int ret = m_pIf->execute("AT!CNTSMS", this, NULL, DEFAULT_TIMEOUT);
+  if( ret != OK )
+  {
+    WARN("AT!CNTSMS returned %d", ret);
+    m_msgInListsCount[0] = m_msgInListsCount[1] = m_msgInListsCount[2] = m_msgInListsCount[3] = 0; //Invalidate counts
+    m_state = SMS_IDLE;
+    return NET_PROTOCOL;
+  }
+
+  return OK;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sms/CDMASMSInterface.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,90 @@
+/* SMSInterface.h */
+/* 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 CDMASMSINTERFACE_H_
+#define CDMASMSINTERFACE_H_
+
+#include "SMSInterface.h"
+
+#define MAX_SM 8
+
+/** Component to use the Short Messages Service (SMS)
+ *
+ */
+class CDMASMSInterface : public ISMSInterface, protected IATCommandsProcessor
+{
+public:
+  /** Create SMSInterface instance
+     @param pIf Pointer to the ATCommandsInterface instance to use
+   */
+  CDMASMSInterface(ATCommandsInterface* pIf);
+
+  /** Initialize interface
+    Configure SMS commands & register for SMS-related unsolicited result codes
+   */
+  virtual int init();
+
+  /** Send a SM
+     @param number The receiver's phone number
+     @param message The message to send
+     @return 0 on success, error code on failure
+   */
+  virtual int send(const char* number, const char* message);
+
+
+  /** Receive a SM
+     @param number Pointer to a buffer to store the sender's phone number (must be at least 17 characters-long, including the space for the null-terminating char)
+     @param message Pointer to a buffer to store the the incoming message
+     @param maxLength Maximum message length that can be stored in buffer (including null-terminating character)
+     @return 0 on success, error code on failure
+   */
+  virtual int get(char* number, char* message, size_t maxLength);
+
+
+  /** Get the number of SMs in the incoming box
+     @param pCount pointer to store the number of unprocessed SMs on
+     @return 0 on success, error code on failure
+   */
+  virtual int getCount(size_t* pCount);
+
+protected:
+  //IATCommandsProcessor
+  virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line);
+  virtual int onNewEntryPrompt(ATCommandsInterface* pInst);
+
+  int updateInbox(); //Update messages count in the different inboxes
+
+private:
+  ATCommandsInterface* m_pIf;
+
+  //Current message
+  char* m_msg;
+  size_t m_maxMsgLength;
+  char* m_msisdn;
+
+  //Messages list
+  size_t m_msgInListsCount[4]; //4 lists
+  
+  size_t m_headersToRead;
+  
+  enum { SMS_NONE, SMS_SENT, SMS_PENDING, SMS_FAILED } m_txState;
+  enum { SMS_IDLE, SMS_SEND_CMD_SENT, SMS_GET_TX_STATUS_CMD_SENT, SMS_GET_CMD_SENT, SMS_GET_HDR_RECEIVED, SMS_GET_COUNT_CMD_SENT, SMS_CMD_PROCESSED } m_state;
+};
+
+#endif /* CDMASMSINTERFACE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sms/GSMSMSInterface.cpp	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,423 @@
+/* GSMSMSInterface.cpp */
+/* 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.
+ */
+
+#define __DEBUG__ 2
+#ifndef __MODULE__
+#define __MODULE__ "GSMSMSInterface.cpp"
+#endif
+
+#include "core/fwk.h"
+
+#include "GSMSMSInterface.h"
+
+#include <cstdio>
+#include <cstring>
+
+#define DEFAULT_TIMEOUT 10000
+
+GSMSMSInterface::GSMSMSInterface(ATCommandsInterface* pIf) : m_pIf(pIf), m_msg(NULL), m_maxMsgLength(0), m_msisdn(NULL)
+{
+  m_pIf->registerEventsHandler(this); //Add us to the unsolicited result codes handlers
+}
+
+int GSMSMSInterface::init()
+{
+  m_msgRefListCount = 0;
+  m_needsUpdate = true;
+  m_state = SMS_IDLE;
+
+  DBG("Set format");
+  //Set Text mode format
+  int ret = m_pIf->executeSimple("AT+CMGF=1", NULL, DEFAULT_TIMEOUT);
+  if(ret != OK)
+  {
+    return NET_PROTOCOL;
+  }
+
+  DBG("Setup new messages indication");
+  //Setup new messages indication
+  ret = m_pIf->executeSimple("AT+CNMI=2,1,0,0,0", NULL, DEFAULT_TIMEOUT);
+  if(ret != OK)
+  {
+    return NET_PROTOCOL;
+  }
+
+  DBG("Try to fetch inbox");
+  m_inboxMtx.lock();
+  if( m_needsUpdate )
+  {
+    ret = updateInbox(); //Fetch existing messages references
+    if(ret)
+    {
+      m_inboxMtx.unlock();
+      return NET_PROTOCOL;
+    }
+  }
+  m_inboxMtx.unlock();
+
+  DBG("Initialization done");
+  return OK;
+}
+
+int GSMSMSInterface::send(const char* number, const char* message)
+{
+  if( strlen(number) > 16 )
+  {
+    return NET_INVALID; //Number too long to match 3GPP spec
+  }
+
+  int ret;
+
+  //Prepare infos
+  m_state = SMS_SEND_CMD_SENT;
+  m_msg = (char*) message;
+
+  DBG("Send SM");
+  //Send command
+  char cmd[32];
+  std::sprintf(cmd, "AT+CMGS=\"%s\"", number);
+  ret = m_pIf->execute(cmd, this, NULL, DEFAULT_TIMEOUT);
+
+  if( (ret != OK) || (m_state != SMS_CMD_PROCESSED) )
+  {
+    WARN("ret %d, state %d", ret, m_state);
+    m_state = SMS_IDLE;
+    return NET_PROTOCOL;
+  }
+
+  DBG("SM sent");
+  m_state = SMS_IDLE;
+  return OK;
+}
+
+
+int GSMSMSInterface::get(char* number, char* message, size_t maxLength)
+{
+  if( maxLength < 1  )
+  {
+    return NET_INVALID; //Buffer too short
+  }
+
+  int ret;
+
+  DBG("Get next message");
+  m_inboxMtx.lock();
+  if( ((m_msgRefListCount == 0) && m_needsUpdate) || ((m_msgRefListCount > 0) && (m_msgRefList[0] == -1)) )
+  {
+    DBG("Message list count is 0 and needs updating or next index is unknown, calling updateInbox()");
+    ret = updateInbox();
+    
+    if (ret)
+    {
+      m_inboxMtx.unlock();
+      return ret;
+    }
+  }
+  
+  DBG("%d messages to read", m_msgRefListCount);
+
+  if(m_msgRefListCount == 0)
+  {
+    m_inboxMtx.unlock();
+    DBG("Message list count is 0, I think it's empty and returning.");
+    return NET_EMPTY; //No message to read
+  }
+
+  //Prepare infos
+  m_state = SMS_GET_CMD_SENT;
+  m_msisdn = (char*) number;
+  m_msg = (char*) message;
+  m_maxMsgLength = maxLength;
+
+  DBG("Get SMS");
+  //List command
+  char cmd[32];
+  std::sprintf(cmd, "AT+CMGR=%d", m_msgRefList[0]);
+  ret = m_pIf->execute(cmd, this, NULL, DEFAULT_TIMEOUT);
+  if( ret != OK )
+  {
+    WARN("AT+CMGR returned %d", ret);
+    m_state = SMS_IDLE;
+    m_inboxMtx.unlock();
+    return NET_PROTOCOL;
+  }
+
+  if (m_state != SMS_CMD_PROCESSED)
+  {
+    WARN("State variable is not 'SMS_CMD_PROCESSED' - returning 'NET_EMPTY'");
+  }
+
+  DBG("Deleting message from index number: %d", m_msgRefList[0] );
+  //Delete message from outbox
+  std::sprintf(cmd, "AT+CMGD=%d", m_msgRefList[0]);
+  ret = m_pIf->executeSimple(cmd, NULL, DEFAULT_TIMEOUT);
+  if(ret != OK)
+  {
+    ERR("Could not delete message");
+  }
+  //Remove message from list
+  std::memmove(&m_msgRefList[0], &m_msgRefList[1], MIN(m_msgRefListCount-1,MAX_SM-1)*sizeof(m_msgRefList[0]));
+  m_msgRefListCount--;
+  
+  if(m_msgRefListCount > MAX_SM - 1) //Last message index is unknown, so put -1 to tell the lib to fetch it when needed
+  {
+    DBG("Last message index is unknown, will need to be updated");
+    m_msgRefList[MAX_SM - 1] = -1;
+  }
+  
+  DBG("%d messages to read", m_msgRefListCount);
+  
+  if (m_state != SMS_CMD_PROCESSED)
+  {
+    m_state = SMS_IDLE;
+    m_inboxMtx.unlock();
+    return NET_EMPTY;
+  }
+  
+  m_state = SMS_IDLE;
+  m_inboxMtx.unlock();
+
+  return OK;
+}
+
+
+int GSMSMSInterface::getCount(size_t* pCount)
+{
+  int ret;
+
+  m_inboxMtx.lock();
+  if( m_needsUpdate )
+  {
+    ret = updateInbox();
+    if(ret)
+    {
+      m_inboxMtx.unlock();
+      return NET_PROTOCOL;
+    }
+  }
+
+  *pCount = m_msgRefListCount;
+  m_inboxMtx.unlock();
+
+  return OK;
+}
+
+
+/*virtual*/ int GSMSMSInterface::onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
+{
+  if(m_state == SMS_SEND_CMD_SENT)
+  {
+    if( std::sscanf(line, "+CMGS: %*d") == 0 )
+    {
+      DBG("SM sent");
+      m_state = SMS_CMD_PROCESSED;
+    }
+  }
+  else if(m_state == SMS_GET_CMD_SENT)
+  {
+    DBG("Header: %s", line);
+    if( std::sscanf(line, "+CMGR: %*[^,],\"%16[^\"]\"", m_msisdn) == 1 ) //Get message ref
+    {
+      m_state = SMS_GET_HDR_RECEIVED;
+    }
+  }
+  else if(m_state == SMS_GET_HDR_RECEIVED)
+  {
+    DBG("Message: %s", line);
+    size_t cpyLen = MIN( std::strlen(line), m_maxMsgLength - 1 );
+    std::memcpy( m_msg, line, cpyLen );
+    m_msg[cpyLen] = '\0';
+    m_state = SMS_CMD_PROCESSED;
+  }
+  else if(m_state == SMS_GET_COUNT_CMD_SENT)
+  {
+    DBG("Header: %s", line);
+    int msgRef;
+    if( std::sscanf(line, "+CMGL: %d,\"REC", &msgRef) == 1 ) //Filter on REC READ and REC UNREAD messages 
+    {
+      m_state = SMS_GET_COUNT_HDR_RECEIVED;
+      //Add message to list
+      if(m_msgRefListCount < MAX_SM)
+      {
+        m_msgRefList[m_msgRefListCount] = msgRef;
+      }
+      m_msgRefListCount++; //Always count message
+      DBG("m_msgRefListCount=%d",m_msgRefListCount);
+    }
+  }
+  else if(m_state == SMS_GET_COUNT_HDR_RECEIVED)
+  {
+    DBG("Message (debug only): %s", line); //For debug only
+    m_state = SMS_GET_COUNT_CMD_SENT;
+  }
+  return OK;
+}
+
+/*virtual*/ int GSMSMSInterface::onNewEntryPrompt(ATCommandsInterface* pInst)
+{
+  if(m_state == SMS_SEND_CMD_SENT)
+  {
+    char* crPtr = strchr(m_msg, CR);
+    if(crPtr != NULL)
+    {
+      int crPos = crPtr - m_msg;
+      //Replace m_inputBuf[crPos] with null-terminating char
+      m_msg[crPos] = '\x0';
+
+      //If there is a CR char, split message there
+
+      //Do print the message
+      int ret = pInst->sendData(m_msg);
+      if(ret)
+      {
+        return ret;
+      }
+
+      char cr[2] = {CR, '\0'};
+      ret = pInst->sendData(cr);
+      if(ret)
+      {
+        return ret;
+      }
+
+      m_msg += crPos;
+
+      if(m_msg[0] == LF)
+      {
+        m_msg++; //Discard LF char as well
+      }
+
+      return NET_MOREINFO;
+    }
+    else
+    {
+      //Do print the message
+      pInst->sendData(m_msg);
+      return OK;
+    }
+  }
+
+  return OK;
+}
+
+/*virtual*/ bool GSMSMSInterface::isATCodeHandled(const char* atCode) //Is this AT code handled
+{
+  DBG("AT code is %s", atCode);
+  if( strcmp("+CMTI", atCode) == 0 )
+  {
+    return true;
+  }
+
+  DBG("Not handled");
+  return false;
+}
+
+/*virtual*/ void GSMSMSInterface::onDispatchStart()
+{
+    
+}
+
+/*virtual*/ void GSMSMSInterface::onDispatchStop()
+{
+    
+}
+
+/*virtual*/ char* GSMSMSInterface::getEventsEnableCommand()
+{
+  return "AT+CNMI=2,1,0,0,0";
+}
+
+/*virtual*/ char* GSMSMSInterface::getEventsDisableCommand()
+{
+  return "AT+CNMI=0,0,0,0,0"; //Indications will be buffered within the modem and flushed back when the former command is executed
+}
+
+/*virtual*/ void GSMSMSInterface::onEvent(const char* atCode, const char* evt)
+{
+  if( strcmp("+CMTI", atCode) != 0 )
+  {
+    return; //Not supported
+  }
+
+  DBG("Unsollicited result code: %s - %s", atCode, evt);
+
+  //Get index
+  int msgRef;
+  if(( std::sscanf(evt, "\"SM\",%d", &msgRef) == 1 ) || 
+     ( std::sscanf(evt, "\"ME\",%d", &msgRef) == 1 ))
+  {
+    DBG("Adding message to list (ref %d)", msgRef);
+    if(m_inboxMtx.trylock())
+    {
+      //Add message to list
+      if(m_msgRefListCount < MAX_SM)
+      {
+        m_msgRefList[m_msgRefListCount] = msgRef;
+      }
+      m_msgRefListCount++; //Always count message
+      m_inboxMtx.unlock();
+    }
+    else
+    {
+      WARN("Could not get lock");
+      m_needsUpdate = true;
+    }
+  }
+}
+
+int GSMSMSInterface::updateInbox()
+{
+  //Get memory indexes of unread messages
+
+  DBG("Updating inbox");
+  m_msgRefListCount = 0; //Reset list
+  m_needsUpdate = false; //Assume we won't need update after this routine (can be set to true by an incoming SM event)
+
+  //First list the "REC READ" messages that were not processed in the previous session
+  m_state = SMS_GET_COUNT_CMD_SENT;
+  int ret = m_pIf->execute("AT+CMGL=\"REC READ\"", this, NULL, DEFAULT_TIMEOUT);
+  if( ret != OK )
+  {
+    WARN("AT+CMGL returned %d", ret);
+    m_state = SMS_IDLE;
+    m_msgRefListCount = 0; //List could be invalid
+    m_needsUpdate = true;
+    return NET_PROTOCOL;
+  }
+  
+  //Now list the "REC UNREAD" messages that were received by the modem since
+  m_state = SMS_GET_COUNT_CMD_SENT;
+  ret = m_pIf->execute("AT+CMGL=\"REC UNREAD\"", this, NULL, DEFAULT_TIMEOUT);
+  if( ret != OK )
+  {
+    WARN("AT+CMGL returned %d", ret);
+    m_state = SMS_IDLE;
+    m_msgRefListCount = 0; //List could be invalid
+    m_needsUpdate = true;
+    return NET_PROTOCOL;
+  }
+
+  DBG("%d incoming messages in inbox", m_msgRefListCount);
+
+  m_state = SMS_IDLE;
+
+  return OK;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sms/GSMSMSInterface.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,97 @@
+/* SMSInterface.h */
+/* 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 GSMSMSINTERFACE_H_
+#define GSMSMSINTERFACE_H_
+
+#include "SMSInterface.h"
+
+/** Component to use the Short Messages Service (SMS)
+ *
+ */
+class GSMSMSInterface : public ISMSInterface, protected IATCommandsProcessor, IATEventsHandler
+{
+public:
+  /** Create SMSInterface instance
+     @param pIf Pointer to the ATCommandsInterface instance to use
+   */
+  GSMSMSInterface(ATCommandsInterface* pIf);
+
+  /** Initialize interface
+    Configure SMS commands & register for SMS-related unsolicited result codes
+   */
+  virtual int init();
+
+  /** Send a SM
+     @param number The receiver's phone number
+     @param message The message to send
+     @return 0 on success, error code on failure
+   */
+  virtual int send(const char* number, const char* message);
+
+
+  /** Receive a SM
+     @param number Pointer to a buffer to store the sender's phone number (must be at least 17 characters-long, including the space for the null-terminating char)
+     @param message Pointer to a buffer to store the the incoming message
+     @param maxLength Maximum message length that can be stored in buffer (including null-terminating character)
+     @return 0 on success, error code on failure
+   */
+  virtual int get(char* number, char* message, size_t maxLength);
+
+
+  /** Get the number of SMs in the incoming box
+     @param pCount pointer to store the number of unprocessed SMs on
+     @return 0 on success, error code on failure
+   */
+  virtual int getCount(size_t* pCount);
+
+protected:
+  //IATCommandsProcessor
+  virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line);
+  virtual int onNewEntryPrompt(ATCommandsInterface* pInst);
+
+  //IATEventsHandler
+  virtual bool isATCodeHandled(const char* atCode); //Is this AT code handled
+  virtual void onDispatchStart();
+  virtual void onDispatchStop();
+  virtual char* getEventsEnableCommand();
+  virtual char* getEventsDisableCommand();
+  virtual void onEvent(const char* atCode, const char* evt);
+
+  //Updates messages count/references
+  int updateInbox();
+
+private:
+  ATCommandsInterface* m_pIf;
+
+  //Current message
+  char* m_msg;
+  size_t m_maxMsgLength;
+  char* m_msisdn;
+
+  //Messages list
+  int m_msgRefList[MAX_SM];
+  size_t m_msgRefListCount;
+  bool m_needsUpdate;
+  Mutex m_inboxMtx; //To protect concurrent accesses btw the user's thread and the AT thread
+
+  enum { SMS_IDLE, SMS_SEND_CMD_SENT, SMS_GET_CMD_SENT, SMS_GET_HDR_RECEIVED, SMS_GET_COUNT_CMD_SENT, SMS_GET_COUNT_HDR_RECEIVED, SMS_CMD_PROCESSED } m_state;
+};
+
+#endif /* GSMSMSINTERFACE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sms/SMSInterface.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,67 @@
+/* SMSInterface.h */
+/* 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 ISMSINTERFACE_H_
+#define ISMSINTERFACE_H_
+
+#include "core/fwk.h"
+
+#include "rtos.h"
+
+#include "at/ATCommandsInterface.h"
+
+#define MAX_SM 8
+
+/** Component to use the Short Messages Service (SMS)
+ *
+ */
+class ISMSInterface
+{
+public:
+  /** Initialize interface
+    Configure SMS commands & register for SMS-related unsolicited result codes
+   */
+  virtual int init() = 0;
+
+
+  /** Send a SM
+     @param number The receiver's phone number
+     @param message The message to send
+     @return 0 on success, error code on failure
+   */
+  virtual int send(const char* number, const char* message) = 0;
+
+
+  /** Receive a SM
+     @param number Pointer to a buffer to store the sender's phone number (must be at least 17 characters-long, including the space for the null-terminating char)
+     @param message Pointer to a buffer to store the the incoming message
+     @param maxLength Maximum message length that can be stored in buffer (including null-terminating character)
+     @return 0 on success, error code on failure
+   */
+  virtual int get(char* number, char* message, size_t maxLength) = 0;
+
+
+  /** Get the number of SMs in the incoming box
+     @param pCount pointer to store the number of unprocessed SMs on
+     @return 0 on success, error code on failure
+   */
+  virtual int getCount(size_t* pCount) = 0;
+};
+
+#endif /* ISMSINTERFACE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ussd/USSDInterface.cpp	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,196 @@
+/* USSDInterface.cpp */
+/* 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.
+ */
+
+#define __DEBUG__ 0
+#ifndef __MODULE__
+#define __MODULE__ "USSDInterface.cpp"
+#endif
+
+#include "core/fwk.h"
+
+#include "USSDInterface.h"
+
+#include <cstdio>
+
+#define DEFAULT_TIMEOUT 10000
+#define USSD_TIMEOUT 15000
+
+USSDInterface::USSDInterface(ATCommandsInterface* pIf) : m_pIf(pIf), m_responseMtx(), m_responseSphre(1), m_result(NULL), m_maxResultLength(0)
+{
+  m_responseSphre.wait(0); //Take ownership of the semaphore
+  m_pIf->registerEventsHandler(this); //Add us to the unsolicited result codes handlers
+}
+
+int USSDInterface::init()
+{
+  DBG("Initialization done");
+  return OK;
+}
+
+int USSDInterface::send(const char* command, char* result, size_t maxLength)
+{
+  if (strlen(command) > 20) //Prevent buffer overflow
+  {
+    return NET_TOOSMALL;
+  }
+
+  m_responseMtx.lock();
+  m_result = result;
+  m_maxResultLength = maxLength;
+  m_responseMtx.unlock();
+
+  m_responseSphre.wait(0); //Make sure there is not a pending result that needs to be discarded
+
+  DBG("Send USSD command & register for unsolicited result codes");
+  //Send USSD command to the network
+  char cmd[32];
+  std::sprintf(cmd, "AT+CUSD=1,\"%s\"", command);
+  int ret = m_pIf->execute(cmd, this, NULL, DEFAULT_TIMEOUT);
+  if( ret != OK )
+  {
+    return NET_PROTOCOL;
+  }
+  
+  //Did we already get a response (3GPP rev < 6) ?
+
+  //Now wait for response
+  int res = m_responseSphre.wait(USSD_TIMEOUT);
+
+  //Reset data
+  m_responseMtx.lock();
+  m_result = NULL;
+  m_maxResultLength = 0;
+  m_responseMtx.unlock();
+
+  if(res <= 0)
+  {
+    DBG("No result received");
+    ret = m_pIf->executeSimple("AT+CUSD=2", NULL, DEFAULT_TIMEOUT); //Cancel command
+    if( ret != OK )
+    {
+      return NET_PROTOCOL;
+    }
+    return NET_TIMEOUT;
+  }
+
+  DBG("Result received: %s", result);
+
+  return OK;
+}
+
+/*virtual*/ int USSDInterface::onNewATResponseLine(ATCommandsInterface* pInst, const char* line)
+{
+  const char* pSemicol = strchr(line, ':');
+  if( ( (pSemicol - line) != strlen("+CUSD") ) || ( memcmp(line, "+CUSD", strlen("+CUSD")) != 0) )
+  {
+    WARN("Unknown code");
+    return OK;
+  }
+
+  const char* pData = NULL;
+  if( pSemicol != NULL ) //Split the identifier & the result code (if it exists)
+  {
+    pData = pSemicol + 1;
+    if(pData[0]==' ')
+    {
+      pData++; //Suppress whitespace
+    }
+    processResult(pData);
+  }
+  return OK;
+}
+
+/*virtual*/ int USSDInterface::onNewEntryPrompt(ATCommandsInterface* pInst)
+{
+  return OK;
+}
+
+/*virtual*/ bool USSDInterface::isATCodeHandled(const char* atCode) //Is this AT code handled
+{
+  DBG("AT code is %s", atCode);
+  if( strcmp("+CUSD", atCode) == 0 )
+  {
+    return true;
+  }
+
+  DBG("Not handled");
+  return false;
+}
+
+/*virtual*/ void USSDInterface::onDispatchStart()
+{
+
+
+}
+
+/*virtual*/ void USSDInterface::onDispatchStop()
+{
+
+}
+
+/*virtual*/ char* USSDInterface::getEventsEnableCommand()
+{
+  return NULL; //No need to disable events here
+}
+
+/*virtual*/ char* USSDInterface::getEventsDisableCommand()
+{
+  return NULL; //No need to re-enable events here
+}
+
+/*virtual*/ void USSDInterface::onEvent(const char* atCode, const char* evt)
+{
+  if( strcmp("+CUSD", atCode) != 0 )
+  {
+    WARN("Wrong AT Code");
+    return; //Not supported
+  }
+
+  processResult(evt);
+}
+
+void USSDInterface::processResult(const char* data)
+{
+  char* pStart = (char*) strchr(data,'\"');
+  if(pStart==NULL)
+  {
+    WARN("Could not find opening quote");
+    return; //Invalid/incomplete response
+  }
+  pStart++; //Point to first char of response
+  char* pEnd = (char*) strchr(pStart,'\"');
+  if(pEnd==NULL)
+  {
+    WARN("Could not find closing quote");
+    return; //Invalid/incomplete response
+  }
+  m_responseMtx.lock();
+  if(m_maxResultLength == 0) //No pending command
+  {
+    WARN("No pending command");
+    m_responseMtx.unlock();
+    return;
+  }
+  size_t cpyLen = MIN( pEnd - pStart, m_maxResultLength - 1 );
+  memcpy((void*)m_result, pStart, cpyLen);
+  m_result[cpyLen] = '\0';
+  DBG("Got USSD response: %s", m_result);
+  m_responseMtx.unlock();
+  m_responseSphre.release(); //Signal user thread that response is ready
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ussd/USSDInterface.h	Thu Oct 17 12:29:05 2013 +0300
@@ -0,0 +1,80 @@
+/* USSDInterface.h */
+/* 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 USSDINTERFACE_H_
+#define USSDINTERFACE_H_
+
+#include "core/fwk.h"
+
+#include "rtos.h"
+
+#include "at/ATCommandsInterface.h"
+
+/** Component to send/receive Unstructured Supplementary Service Data (USSD)
+ *
+ */
+class USSDInterface : protected IATCommandsProcessor, IATEventsHandler
+{
+public:
+  /** Create USSDInterface instance
+     @param pIf Pointer to the ATCommandsInterface instance to use
+   */
+  USSDInterface(ATCommandsInterface* pIf);
+
+  /** Initialize interface
+   Configure USSD commands & register for USSD-related unsolicited result codes
+  */
+  int init();
+
+  /** Send a USSD command & wait for its result
+    @param command The command to send
+    @param result Buffer in which to store the result
+    @param maxLength Maximum result length that can be stored in buffer (including null-terminating character)
+    @return 0 on success, error code on failure
+  */
+  int send(const char* command, char* result, size_t maxLength);
+
+protected:
+  //IATCommandsProcessor, needed for implementations of 3GGP standard < r06
+  virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line);
+  virtual int onNewEntryPrompt(ATCommandsInterface* pInst);
+  
+  //IATEventsHandler, needed for implementations of 3GGP standard >= r06
+  virtual bool isATCodeHandled(const char* atCode); //Is this AT code handled
+  virtual void onDispatchStart();
+  virtual void onDispatchStop();
+  virtual char* getEventsEnableCommand();
+  virtual char* getEventsDisableCommand();
+  virtual void onEvent(const char* atCode, const char* evt);
+
+private:
+  void processResult(const char* data);
+
+  ATCommandsInterface* m_pIf;
+  Mutex m_responseMtx; //To protect concurrent accesses btw the user's thread and the AT thread
+  Semaphore m_responseSphre;
+
+  //Result
+  volatile char* m_result;
+  volatile size_t m_maxResultLength;
+
+};
+
+
+#endif /* USSDINTERFACE_H_ */