1
0
Fork 0

Squashed 'tmk_core/' changes from 7967731..b9e0ea0

b9e0ea0 Merge commit '7fa9d8bdea3773d1195b04d98fcf27cf48ddd81d' as 'tool/mbed/mbed-sdk'
7fa9d8b Squashed 'tool/mbed/mbed-sdk/' content from commit 7c21ce5

git-subtree-dir: tmk_core
git-subtree-split: b9e0ea08cb940de20b3610ecdda18e9d8cd7c552
This commit is contained in:
Jun Wako 2015-04-24 16:26:14 +09:00
parent a20ef7052c
commit 1fe4406f37
4198 changed files with 2016457 additions and 0 deletions

View file

@ -0,0 +1,78 @@
/* CellularModem.h */
/* Copyright (C) 2013 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 CELLULARMODEM_H_
#define CELLULARMODEM_H_
#include "core/fwk.h"
#include "at/ATCommandsInterface.h"
class CellularModem
{
public:
//Internet-related functions
/** Open a 3G internet connection
@return 0 on success, error code on failure
*/
virtual int connect(const char* apn = NULL, const char* user = NULL, const char* password = NULL) = 0;
/** Close the internet connection
@return 0 on success, error code on failure
*/
virtual int disconnect() = 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 sendSM(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 sapce 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 getSM(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 getSMCount(size_t* pCount) = 0;
/** Get the ATCommandsInterface instance
@return Pointer to the ATCommandsInterface instance
*/
virtual ATCommandsInterface* getATCommandsInterface() = 0;
/** Switch power on or off
In order to use this function, a pin name must have been entered in the constructor
@param enable true to switch the dongle on, false to switch it off
@return 0 on success, error code on failure
*/
virtual int power(bool enable) = 0;
};
#endif /* CELLULARMODEM_H_ */

View file

@ -0,0 +1,902 @@
/* 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_transactionState(IDLE), 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(bool reset /* = true*/)
{
//Lock transaction mutex
m_transactionMtx.lock();
if (reset)
{
DBG("Sending ATZ E1 V1");
//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");
WARN("Command \"%s\" returned no message", command);
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);
WARN("Command \"%s\" returned AT result %d with code %d", command, 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
const char* cmd = m_eventsHandlers[i]->getEventsEnableCommand();
if(cmd != NULL)
{
int ret = executeInternal(cmd, this, NULL); //Execute enable command
if(ret)
{
WARN("Events enabling command \"%s\" failed", cmd);
}
}
}
}
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
const char* cmd = m_eventsHandlers[i]->getEventsDisableCommand();
if(cmd != NULL)
{
int ret = executeInternal(cmd, this, NULL); //Execute disable command
if(ret)
{
WARN("Events disabling command \"%s\" failed", cmd);
}
}
}
}
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");
}
}

View file

@ -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(bool reset = true);
//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_ */

View file

@ -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_ */

View file

@ -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

View file

@ -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_ */

View file

@ -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);
}

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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_ */

View file

@ -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();
}

View file

@ -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_ */

View file

@ -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;
using std::sprintf;
#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) : LwIPInterface(), m_linkStatusSphre(1), m_pppErrCode(0), m_pStream(pStream), m_streamAvail(true), m_pppd(-1)
{
m_linkStatusSphre.wait();
}
/*virtual*/ PPPIPInterface::~PPPIPInterface()
{
}
/*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, const char* msisdn)
{
DBG("Configuring PPP authentication method");
pppSetAuth(PPPAUTHTYPE_ANY, user, pw);
m_msisdn = msisdn;
DBG("Done");
return OK;
}
/*virtual*/ int PPPIPInterface::connect()
{
int ret;
char cmd[32];
int cmdLen;
char buf[32];
size_t len;
DBG("Trying to connect with PPP");
cleanupLink();
cmdLen = sprintf(cmd, "%s%s%s", CONNECT_CMD_PREFIX, m_msisdn, CONNECT_CMD_SUFFIX);
DBG("Sending %s", cmd);
ret = m_pStream->write((uint8_t*)cmd, cmdLen, osWaitForever);
if( ret != OK )
{
return NET_UNKNOWN;
}
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;
strcpy(&cmd[cmdLen], EXPECTED_RESP_DATARATE_SUFFIX);
if( (sscanf(buf, cmd, &datarate ) != 1))
{
strcpy(&cmd[cmdLen], EXPECTED_RESP_SUFFIX);
if (strcmp(cmd, 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");
}
}

View file

@ -0,0 +1,69 @@
/* 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);
virtual ~PPPIPInterface();
int init(); //Init PPP-specific stuff, create the right bindings, etc
int setup(const char* user, const char* pw, const char* msisdn); //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;
const char* m_msisdn;
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);
};
#endif /* PPPIPINTERFACE_H_ */

View file

@ -0,0 +1,175 @@
/* 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)
{
m_gsm = true;
}
int LinkMonitor::init(bool gsm)
{
m_gsm = gsm;
if (m_gsm)
{
// 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);
char n[32] = "";
char s[32] = "";
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;
}
}
else if ( (sscanf(line, "+CNUM: \"%[^\"]\",\"%[^\"]\",%d", n, s, &v) == 3) ||
(sscanf(line, "+CNUM: \"\",\"%[^\"]\",%d", s, &v) == 2) )
{
if (*s && ((v == 145/*number includes + */) || (v == 129/*otherwise*/))) {
strcpy(m_phoneNumber, s);
}
}
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(m_gsm ? "AT+CREG?;+COPS?;+CSQ" : "AT+CREG?;+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;
}
int LinkMonitor::getPhoneNumber(char* phoneNumber)
{
*m_phoneNumber = '\0';
if (m_gsm) {
int ret = m_pIf->execute("AT+CNUM", this, NULL, DEFAULT_TIMEOUT);
if(ret != OK)
{
return NET_PROTOCOL;
}
}
strcpy(phoneNumber, m_phoneNumber);
return OK;
}

View file

@ -0,0 +1,96 @@
/* 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(bool gsm = true);
/** 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);
/** Get my phone number
@param phoneNumber pointer to store the current phoneNumber
@return 0 on success, error code on failure
*/
int getPhoneNumber(char* phoneNumber);
protected:
//IATCommandsProcessor
virtual int onNewATResponseLine(ATCommandsInterface* pInst, const char* line);
virtual int onNewEntryPrompt(ATCommandsInterface* pInst);
private:
ATCommandsInterface* m_pIf;
int m_rssi;
bool m_gsm;
REGISTRATION_STATE m_registrationState;
BEARER m_bearer;
char m_phoneNumber[16];
};
#endif /* LINKMONITOR_H_ */

View file

@ -0,0 +1,26 @@
/* lwipopts.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 LWIPOPTS_CONF_H_
#define LWIPOPTS_CONF_H_
#define LWIP_TRANSPORT_PPP 1
#endif /* LWIPOPTS_CONF_H_ */

View file

@ -0,0 +1,350 @@
/* 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>
using std::sscanf;
#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;
}

View file

@ -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_ */

View file

@ -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;
}

View file

@ -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_ */

View file

@ -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_ */

View file

@ -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
}

View file

@ -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_ */