local fork

Dependencies:   Socket USBHostWANDongle_bleedingedge lwip-sys lwip

Dependents:   Encrypted

Fork of VodafoneUSBModem_bleedingedge by Donatien Garnier

main/if/sms/SMSInterface.cpp

Committer:
donatien
Date:
2012-05-24
Revision:
0:3b2f052c333b

File content as of revision 0:3b2f052c333b:

/* SMSInterface.cpp */
/*
Copyright (C) 2012 ARM Limited.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#define __DEBUG__ 4
#ifndef __MODULE__
#define __MODULE__ "SMSInterface.cpp"
#endif

#include "core/fwk.h"

#include "SMSInterface.h"

#include <cstdio>

#define DEFAULT_TIMEOUT 10000

SMSInterface::SMSInterface(ATCommandsInterface* pIf) : m_pIf(pIf), m_msg(NULL), m_maxMsgLength(0), m_msisdn(NULL), /*m_msgCount(0),*/
m_msgRefListCount(0), m_needsUpdate(true), m_state(SMS_IDLE)
{
  m_pIf->registerEventsHandler(this); //Add us to the unsolicited result codes handlers
}

int SMSInterface::init()
{
  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 SMSInterface::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 SMSInterface::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)
  {
    ret = updateInbox();
    if (ret)
    {
      m_inboxMtx.unlock();
      return ret;
    }
  }

  if(m_msgRefListCount == 0)
  {
    m_inboxMtx.unlock();
    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)
  {
    m_state = SMS_IDLE;
    m_inboxMtx.unlock();
    return NET_EMPTY;
  }

  m_state = SMS_IDLE;

  DBG("Deleting message");
  //Delete message from outbox
  std::sprintf(cmd, "AT+CMGD=%d", m_msgRefList[0]);
  ret = m_pIf->executeSimple(cmd, NULL, DEFAULT_TIMEOUT);
  if(ret != OK)
  {
    m_inboxMtx.unlock();
    WARN("Could not delete message");
  }

  //Remove message from list
  std::memmove(m_msgRefList, m_msgRefList+1, m_msgRefListCount-1);
  m_msgRefListCount--;

  m_inboxMtx.unlock();

  return OK;
}


int SMSInterface::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 SMSInterface::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", &msgRef) == 1 )
    {
      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 SMSInterface::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 SMSInterface::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 SMSInterface::onDispatchStart()
{


}

/*virtual*/ void SMSInterface::onDispatchStop()
{

}

/*virtual*/ void SMSInterface::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 )
  {
    DBG("Adding message to list (ref %d)", msgRef);
    m_inboxMtx.lock();
    //Add message to list
    if(m_msgRefListCount < MAX_SM)
    {
      m_msgRefList[m_msgRefListCount] = msgRef;
    }
    else
    {
      m_needsUpdate = true;
    }
    m_msgRefListCount++; //Always count message
    m_inboxMtx.unlock();
  }
}

int SMSInterface::updateInbox()
{
  //Get memory indexes of unread messages
  m_state = SMS_GET_COUNT_CMD_SENT;

  DBG("Updating inbox");
  m_msgRefListCount = 0; //Reset list

  int 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);

  if( m_msgRefListCount > MAX_SM )
  {
    m_needsUpdate = true;
  }
  else
  {
    m_needsUpdate = false;
  }

  m_state = SMS_IDLE;

  return OK;
}