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