| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902 |
- /* 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");
- }
- }
|