ON Semiconductor / mbed-os

Dependents:   mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ATCommandsInterface.cpp Source File

ATCommandsInterface.cpp

00001 /* ATCommandsInterface.cpp */
00002 /* Copyright (C) 2012 mbed.org, MIT License
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
00005  * and associated documentation files (the "Software"), to deal in the Software without restriction,
00006  * including without limitation the rights to use, copy, modify, merge, publish, distribute,
00007  * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
00008  * furnished to do so, subject to the following conditions:
00009  *
00010  * The above copyright notice and this permission notice shall be included in all copies or
00011  * substantial portions of the Software.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
00014  * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00015  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
00016  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00017  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00018  */
00019 
00020 #define __DEBUG__  2 //ERR+WARN
00021 #ifndef __MODULE__
00022 #define __MODULE__ "ATCommandsInterface.cpp"
00023 #endif
00024 
00025 #include "core/fwk.h"
00026 
00027 #include <cstdio>
00028 #include <cstring> //For memset, strstr...
00029 
00030 using std::memmove;
00031 
00032 #include "ATCommandsInterface.h"
00033 
00034 ATCommandsInterface::ATCommandsInterface(IOStream* pStream) :
00035    m_pStream(pStream), m_open(false), m_transactionState(IDLE), m_env2AT(), m_AT2Env(), m_processingMtx(),
00036    m_processingThread(&ATCommandsInterface::staticCallback, this, (osPriority)AT_THREAD_PRIORITY, 4*192),
00037    m_eventsMgmtMtx(), m_eventsProcessingMtx()
00038 {
00039   memset(m_eventsHandlers, 0, MAX_AT_EVENTS_HANDLERS * sizeof(IATEventsHandler*));
00040 
00041   m_processingMtx.lock();
00042 }
00043 
00044 //Open connection to AT Interface in order to execute command & register/unregister events
00045 int ATCommandsInterface::open()
00046 {
00047   if( m_open )
00048   {
00049     WARN("AT interface is already open");
00050     return OK;
00051   }
00052   DBG("Opening AT interface");
00053   //Start processing
00054   m_processingThread.signal_set(AT_SIG_PROCESSING_START);
00055 
00056   m_processingMtx.unlock();
00057 
00058   m_open = true;
00059 
00060   DBG("AT interface opened");
00061   
00062   return OK;
00063 }
00064 
00065 //Initialize AT link & start events processing
00066 int ATCommandsInterface::init(bool reset /* = true*/)
00067 {
00068   
00069   //Lock transaction mutex
00070   m_transactionMtx.lock();
00071   
00072   if (reset)
00073   {
00074     DBG("Sending ATZ E1 V1");
00075     //Should we flush m_pStream at this point ???
00076     int err;
00077     int tries = 5;
00078     do
00079     {
00080       err = executeInternal("ATZ E1 V1", this, NULL, 3000); //Enable echo and verbosity
00081       if(err && tries)
00082       {
00083         WARN("No response, trying again");
00084         Thread::wait(1000); //Give dongle time to recover
00085       }
00086     } while(err && tries--);
00087     if( err )
00088     {
00089       ERR("Sending ATZ E1 V1 returned with err code %d", err);
00090       m_transactionMtx.unlock();
00091       return err;
00092     }
00093   }
00094   
00095   //Enable events handling and execute events enabling commands
00096   enableEvents();
00097 
00098   DBG("AT interface initialized");
00099   
00100   //Unlock transaction mutex
00101   m_transactionMtx.unlock();
00102 
00103   return OK;
00104 }
00105 
00106 //Close connection
00107 int ATCommandsInterface::close()
00108 {
00109   if( !m_open )
00110   {
00111     WARN("AT interface is already closed");
00112     return OK;
00113   }
00114 
00115   DBG("Closing AT interface");
00116   
00117   //Lock transaction mutex
00118   m_transactionMtx.lock();
00119   
00120   //Disable events handling and advertize this to the events handlers
00121   disableEvents();
00122 
00123   //Stop processing
00124   m_processingThread.signal_set(AT_SIG_PROCESSING_STOP);
00125   //m_stopSphre.release();
00126 
00127   int* msg = m_env2AT.alloc(osWaitForever);
00128   *msg = AT_STOP;
00129   m_env2AT.put(msg); //Used to unstall the process if needed
00130 
00131   //Unlock process routine (abort read)
00132   m_pStream->abortRead(); //This is thread-safe
00133   m_processingMtx.lock();
00134   m_open = false;
00135   
00136   //Unlock transaction mutex
00137   m_transactionMtx.unlock();
00138 
00139   DBG("AT interface closed");
00140   return OK;
00141 }
00142 
00143 bool ATCommandsInterface::isOpen()
00144 {
00145   return m_open;
00146 }
00147 
00148 int ATCommandsInterface::executeSimple(const char* command, ATResult* pResult, uint32_t timeout/*=1000*/)
00149 {
00150   return execute(command, this, pResult, timeout);
00151 }
00152 
00153 int ATCommandsInterface::execute(const char* command, IATCommandsProcessor* pProcessor, ATResult* pResult, uint32_t timeout/*=1000*/)
00154 {
00155   if(!m_open)
00156   {
00157     WARN("Interface is not open!");
00158     return NET_INVALID;
00159   }
00160 
00161   //Lock transaction mutex
00162   m_transactionMtx.lock();
00163   
00164   disableEvents(); //Disable unsollicited result codes
00165   int ret = executeInternal(command, pProcessor, pResult, timeout);
00166   enableEvents(); //Re-enable unsollicited result codes whatever the result of the command is
00167   
00168   //Unlock transaction mutex
00169   m_transactionMtx.unlock();
00170   
00171   return ret;
00172 }
00173 
00174 int ATCommandsInterface::registerEventsHandler(IATEventsHandler* pHdlr)
00175 {
00176   m_eventsMgmtMtx.lock();
00177   m_eventsProcessingMtx.lock();
00178   for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
00179   {
00180     if( m_eventsHandlers[i] == NULL )
00181     {
00182       m_eventsHandlers[i] = pHdlr;
00183       m_eventsProcessingMtx.unlock();
00184       m_eventsMgmtMtx.unlock();
00185       return OK;
00186     }
00187   }
00188   m_eventsProcessingMtx.unlock();
00189   m_eventsMgmtMtx.unlock();
00190   return NET_OOM; //No room left
00191 }
00192 
00193 int ATCommandsInterface::deregisterEventsHandler(IATEventsHandler* pHdlr)
00194 {
00195   m_eventsMgmtMtx.lock();
00196   m_eventsProcessingMtx.lock();
00197   for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find handler in list
00198   {
00199     if( m_eventsHandlers[i] == pHdlr )
00200     {
00201       m_eventsHandlers[i] = NULL;
00202       m_eventsProcessingMtx.unlock();
00203       m_eventsMgmtMtx.unlock();
00204       return OK;
00205     }
00206   }
00207   m_eventsProcessingMtx.unlock();
00208   m_eventsMgmtMtx.unlock();
00209   return NET_NOTFOUND; //Not found
00210 }
00211 
00212 //Private methods
00213 
00214 int ATCommandsInterface::executeInternal(const char* command, IATCommandsProcessor* pProcessor, ATResult* pResult, uint32_t timeout/*=1000*/)
00215 {
00216   DBG("Executing command %s", command);
00217 
00218   //Discard previous result if it arrived too late
00219   osEvent evt = m_AT2Env.get(0);
00220 
00221   if(evt.status == osEventMail)
00222   {
00223     m_AT2Env.free((int*)evt.value.p);
00224     WARN("Previous result discarded");
00225   }
00226 
00227   //Send params to the process routine
00228   m_transactionCommand = command;
00229   if(pProcessor != NULL)
00230   {
00231     m_pTransactionProcessor = pProcessor;
00232   }
00233   else
00234   {
00235     m_pTransactionProcessor = this; //Use default behaviour
00236   }
00237 
00238   DBG("Sending command ready signal to AT thread & aborting current blocking read operation");
00239 
00240   //Produce command ready signal
00241   int* msg = m_env2AT.alloc(osWaitForever);
00242   *msg = AT_CMD_READY;
00243   m_env2AT.put(msg);
00244 
00245   DBG("Trying to enter abortRead()");
00246   //Unlock process routine (abort read)
00247   m_pStream->abortRead(); //This is thread-safe
00248 
00249   //Wait for a result (get result message)
00250   evt = m_AT2Env.get(timeout);
00251 
00252   if(evt.status != osEventMail)
00253   {
00254     //Cancel request
00255     msg = m_env2AT.alloc(osWaitForever);
00256     *msg = AT_TIMEOUT;
00257     m_env2AT.put(msg);
00258 
00259     DBG("Trying to enter abortRead()");
00260     //Unlock process routine (abort read)
00261     m_pStream->abortRead(); //This is thread-safe
00262     
00263     //Wait for acknowledge
00264     int msgResult;
00265     do
00266     {
00267       evt = m_AT2Env.get(osWaitForever);
00268       msgResult = *((int*) evt.value.p);
00269       m_AT2Env.free((int*)evt.value.p);
00270     } while(msgResult != AT_TIMEOUT);  
00271 
00272     WARN("Command returned no message");
00273     WARN("Command \"%s\" returned no message", command);
00274     return NET_TIMEOUT;
00275   }
00276   DBG("Command returned with message %d", *msg);
00277 
00278   m_AT2Env.free((int*)evt.value.p);
00279 
00280   if(pResult != NULL)
00281   {
00282     *pResult = m_transactionResult;
00283   }
00284 
00285   int ret = ATResultToReturnCode(m_transactionResult);
00286   if(ret != OK)
00287   {
00288     WARN("Command returned AT result %d with code %d", m_transactionResult.result, m_transactionResult.code);
00289     WARN("Command \"%s\" returned AT result %d with code %d", command, m_transactionResult.result, m_transactionResult.code);
00290   }
00291 
00292   DBG("Command returned successfully");
00293 
00294   return ret;
00295 }
00296 
00297 int ATCommandsInterface::tryReadLine()
00298 {
00299   static bool lineDetected = false;
00300 
00301   //Block on serial read or incoming command
00302   DBG("Trying to read a new line from stream");
00303   int ret = m_pStream->waitAvailable(); //This can be aborted
00304   size_t readLen = 0;
00305   if(ret == OK)
00306   {
00307     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
00308   }
00309   if(ret == OK)
00310   {
00311     m_inputPos+=readLen;
00312     m_inputBuf[m_inputPos] = '\0'; //Add null terminating character to ease the use of str* functions
00313     DBG("In buffer: [%s]", m_inputBuf);
00314   }
00315 
00316   if( ret == NET_INTERRUPTED ) //It is worth checking readLen as data might have been read even though the read was interrupted
00317   {
00318     DBG("Read was interrupted");
00319     return NET_INTERRUPTED; //0 chars were read
00320   }
00321   else if(readLen == 0)
00322   {
00323     DBG("Nothing read");
00324     return OK; //0 chars were read
00325   }
00326 
00327   DBG("Trying to process incoming line");
00328   bool lineProcessed = false;
00329 
00330   do
00331   {
00332     lineProcessed = false; //Reset flag
00333 
00334     DBG("New iteration");
00335 
00336     //Look for a new line
00337     if(!lineDetected)
00338     {
00339       DBG("No line detected yet");
00340       //Try to look for a starting CRLF
00341       char* crPtr = strchr(m_inputBuf, CR);
00342       /*
00343       Different cases at this point:
00344       - CRLF%c sequence: this is the start of a line
00345       - CRLFCR(LF) sequence: this is the end of a line (followed by the beginning of the next one)
00346       - LF: this is the trailing LF char of the previous line, discard
00347       - CR / CRLF incomplete sequence: more data is needed to determine which action to take
00348       - %c ... CR sequence: this should be the echo of the previous sequence
00349       - %c sequence: This might be the echo of the previous command; more data is needed to determine which action to take
00350 
00351       In every case, move mem at the beginning
00352       */
00353       if(crPtr != NULL)
00354       {
00355         DBG("CR char found");
00356 
00357 #if 0
00358         //Discard all preceding characters (can do nothing if m_inputBuf == crPtr)
00359         memmove(m_inputBuf, crPtr, (m_inputPos + 1) - (crPtr-m_inputBuf)); //Move null-terminating char as well
00360         m_inputPos = m_inputPos - (crPtr-m_inputBuf); //Adjust m_inputPos
00361 #endif
00362 
00363         //If the line starts with CR, this should be a result code
00364         if( crPtr == m_inputBuf )
00365         {
00366           //To determine the sequence we need at least 3 chars
00367           if(m_inputPos >= 3)
00368           {
00369             //Look for a LF char next to the CR char
00370             if(m_inputBuf[1] == LF)
00371             {
00372               //At this point we can check whether this is the end of a preceding line or the beginning of a new one
00373               if(m_inputBuf[2] != CR)
00374               {
00375                 DBG("Beginning of new line found");
00376                 //Beginning of a line
00377                 lineDetected = true; //Move to next state-machine step
00378               }
00379               else
00380               {
00381                 //End of an unprocessed line
00382                 WARN("End of unprocessed line");
00383               }
00384               //In both cases discard CRLF
00385               DBG("Discarding CRLF");
00386               memmove(m_inputBuf, m_inputBuf + 2, (m_inputPos + 1) - 2); //Move null-terminating char as well
00387               m_inputPos = m_inputPos - 2; //Adjust m_inputPos
00388             }
00389             else
00390             {
00391               //This is completely unexpected, discard the CR char to try to recover good state
00392               WARN("Unexpected %c char (%02d code) found after CR char", m_inputBuf[1]);
00393               memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well
00394               m_inputPos = m_inputPos - 1; //Adjust m_inputPos
00395             }
00396           }
00397         }
00398         //if the line does NOT begin with CR, this can be an echo of the previous command, process it
00399         else
00400         {
00401           int crPos = crPtr - m_inputBuf;
00402           int lfOff = 0; //Offset for LF if present
00403           DBG("New line found (possible echo of command)");
00404           //This is the end of line
00405           //Replace m_inputBuf[crPos] with null-terminating char
00406           m_inputBuf[crPos] = '\0';
00407           //Check if there is a LF char afterwards
00408           if(m_inputPos - crPos >= 1)
00409           {
00410             if(m_inputBuf[crPos+1] == LF)
00411             {
00412               lfOff++; //We will discard LF char as well
00413             }
00414           }
00415           //Process line
00416           int ret = processReadLine();
00417           if(ret)
00418           {
00419             m_inputPos = 0;
00420             m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00421             lineDetected = false;
00422             return ret;
00423           }
00424 
00425           //If sendData has been called, all incoming data has been discarded
00426           if(m_inputPos > 0)
00427           {
00428             memmove(m_inputBuf, m_inputBuf + crPos + lfOff + 1, (m_inputPos + 1) - (crPos + lfOff + 1)); //Move null-terminating char as well
00429             m_inputPos = m_inputPos - (crPos + lfOff + 1); //Adjust m_inputPos
00430           }
00431           DBG("One line was successfully processed");
00432           lineProcessed = true; //Line was processed with success
00433           lineDetected = false; //Search now for a new line
00434         }
00435       }
00436       else if(m_inputBuf[0] == LF) //If there is a remaining LF char from the previous line, discard it
00437       {
00438         DBG("Discarding single LF char");
00439         memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well
00440         m_inputPos = m_inputPos - 1; //Adjust m_inputPos
00441       }
00442     }
00443 
00444     //Look for the end of line
00445     if(lineDetected)
00446     {
00447       DBG("Looking for end of line");
00448       //Try to look for a terminating CRLF
00449       char* crPtr = strchr(m_inputBuf, CR);
00450       /*
00451       Different cases at this point:
00452       - CRLF sequence: this is the end of the line
00453       - CR%c sequence : unexpected
00454       - CR incomplete sequence: more data is needed to determine which action to take
00455       */
00456 
00457       //Try to look for a '>' (greater than character) that marks an entry prompt
00458       char* greaterThanPtr = strchr(m_inputBuf, GD);
00459       /*
00460       This character must be detected as there is no CRLF sequence at the end of an entry prompt
00461        */
00462 
00463       if(crPtr != NULL)
00464       {
00465         DBG("CR char found");
00466         int crPos = crPtr - m_inputBuf;
00467         //To determine the sequence we need at least 2 chars
00468         if(m_inputPos - crPos >= 2)
00469         {
00470           //Look for a LF char next to the CR char
00471           if(m_inputBuf[crPos + 1] == LF)
00472           {
00473             DBG("End of new line found");
00474             //This is the end of line
00475             //Replace m_inputBuf[crPos] with null-terminating char
00476             m_inputBuf[crPos] = '\0';
00477             //Process line
00478             int ret = processReadLine();
00479             if(ret)
00480             {
00481               m_inputPos = 0;
00482               m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00483               lineDetected = false;
00484               return ret;
00485             }
00486 
00487             //If sendData has been called, all incoming data has been discarded
00488             if(m_inputPos > 0)
00489             {
00490               //Shift remaining data to beginning of buffer
00491               memmove(m_inputBuf, m_inputBuf + crPos + 2, (m_inputPos + 1) - (crPos + 2)); //Move null-terminating char as well
00492               m_inputPos = m_inputPos - (crPos + 2); //Adjust m_inputPos
00493             }
00494 
00495             DBG("One line was successfully processed");
00496             lineProcessed = true; //Line was processed with success
00497           }
00498           else
00499           {
00500             //This is completely unexpected, discard all chars till the CR char to try to recover good state
00501             WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[crPos + 1]);
00502             memmove(m_inputBuf, m_inputBuf + crPos + 1, (m_inputPos + 1) - (crPos + 1)); //Move null-terminating char as well
00503             m_inputPos = m_inputPos - (crPos + 1); //Adjust m_inputPos
00504           }
00505           lineDetected = false; //In both case search now for a new line
00506         }
00507       }
00508       else if(greaterThanPtr != NULL)
00509       {
00510         DBG("> char found");
00511         int gdPos = greaterThanPtr - m_inputBuf;
00512         //To determine the sequence we need at least 2 chars
00513         if(m_inputPos - gdPos >= 2)
00514         {
00515           //Look for a space char next to the GD char
00516           if(m_inputBuf[gdPos + 1] == ' ')
00517           {
00518             //This is an entry prompt
00519             //Replace m_inputBuf[gdPos] with null-terminating char
00520             m_inputBuf[gdPos] = '\0';
00521 
00522             //Shift remaining data to beginning of buffer
00523             memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well
00524             m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos
00525 
00526             //Process prompt
00527             ret = processEntryPrompt();
00528             if(ret)
00529             {
00530               m_inputPos = 0;
00531               m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00532               lineDetected = false;
00533               return ret;
00534             }
00535 
00536             DBG("One line was successfully processed");
00537             lineProcessed = true; //Line was processed with success
00538           }
00539           else
00540           {
00541             //This is completely unexpected, discard all chars till the GD char to try to recover good state
00542             WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[gdPos + 1]);
00543             memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well
00544             m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos
00545           }
00546           lineDetected = false; //In both case search now for a new line
00547         }
00548       }
00549     }
00550   } while(lineProcessed); //If one complete line was processed there might be other incoming lines that can also be processed without reading the buffer again
00551 
00552   //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)
00553   if(m_inputPos == AT_INPUT_BUF_SIZE - 1)
00554   {
00555     //Discard everything
00556     m_inputPos = 0;
00557     m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00558     WARN("Incoming buffer is too short to process incoming line");
00559     //Look for a new line
00560     lineDetected = false;
00561   }
00562 
00563   DBG("Processed every full incoming lines");
00564 
00565   return OK;
00566 }
00567 
00568 int ATCommandsInterface::trySendCommand()
00569 {
00570   osEvent evt = m_env2AT.get(0);
00571   DBG("status = %d, msg = %d", evt.status, evt.value.p);
00572   if(evt.status == osEventMail)
00573   {
00574     int* msg = (int*) evt.value.p;
00575     if( *msg == AT_CMD_READY ) //Command pending
00576     {
00577       if(m_transactionState != IDLE)
00578       {
00579         WARN("Previous command not processed!");
00580       }
00581       DBG("Sending pending command");
00582       m_pStream->write((uint8_t*)m_transactionCommand, strlen(m_transactionCommand), osWaitForever);
00583       char cr = CR;
00584       m_pStream->write((uint8_t*)&cr, 1, osWaitForever); //Carriage return line terminator
00585       m_transactionState = COMMAND_SENT;
00586     }
00587     else //Timeout
00588     {
00589       //Acknowledge
00590       int* msg = m_AT2Env.alloc(osWaitForever);
00591       *msg = AT_TIMEOUT;
00592       m_AT2Env.put(msg); //Command has timed out
00593       m_transactionState = IDLE; //State-machine reset
00594     }
00595     m_env2AT.free(msg);
00596   }
00597   return OK;
00598 }
00599 
00600 int ATCommandsInterface::processReadLine()
00601 {
00602   DBG("Processing read line [%s]", m_inputBuf);
00603   //The line is stored in m_inputBuf
00604   if(m_transactionState == COMMAND_SENT)
00605   {
00606     //If the command has been sent, checks echo to see if it has been received properly
00607     if( strcmp(m_transactionCommand, m_inputBuf) == 0 )
00608     {
00609       DBG("Command echo received");
00610       //If so, it means that the following lines will only be solicited results
00611       m_transactionState = READING_RESULT;
00612       return OK;
00613     }
00614   }
00615   if(m_transactionState == IDLE || m_transactionState == COMMAND_SENT)
00616   {
00617     bool found = false;
00618     char* pSemicol = strchr(m_inputBuf, ':');
00619     char* pData = NULL;
00620     if( pSemicol != NULL ) //Split the identifier & the result code (if it exists)
00621     {
00622       *pSemicol = '\0';
00623       pData = pSemicol + 1;
00624       if(pData[0]==' ')
00625       {
00626         pData++; //Suppress whitespace
00627       }
00628     }
00629     //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
00630     m_eventsProcessingMtx.lock();
00631     //Go through the list
00632     for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
00633     {
00634       if( m_eventsHandlers[i] != NULL )
00635       {
00636         if( m_eventsHandlers[i]->isATCodeHandled(m_inputBuf) )
00637         {
00638           m_eventsHandlers[i]->onEvent(m_inputBuf, pData);
00639           found = true; //Do not break here as there might be multiple handlers for one event type
00640         }
00641       }
00642     }
00643     m_eventsProcessingMtx.unlock();
00644     if(found)
00645     {
00646       return OK;
00647     }
00648   }
00649   if(m_transactionState == READING_RESULT)
00650   {
00651     //The following lines can either be a command response or a result code (OK / ERROR / CONNECT / +CME ERROR: %s / +CMS ERROR: %s)
00652     if(strcmp("OK", m_inputBuf) == 0)
00653     {
00654       DBG("OK result received");
00655       m_transactionResult.code = 0;
00656       m_transactionResult.result = ATResult::AT_OK;
00657       m_transactionState = IDLE;
00658       int* msg = m_AT2Env.alloc(osWaitForever);
00659       *msg = AT_RESULT_READY;
00660       m_AT2Env.put(msg); //Command has been processed
00661       return OK;
00662     }
00663     else if(strcmp("ERROR", m_inputBuf) == 0)
00664     {
00665       DBG("ERROR result received");
00666       m_transactionResult.code = 0;
00667       m_transactionResult.result = ATResult::AT_ERROR;
00668       m_transactionState = IDLE;
00669       int* msg = m_AT2Env.alloc(osWaitForever);
00670       *msg = AT_RESULT_READY;
00671       m_AT2Env.put(msg); //Command has been processed
00672       return OK;
00673     }
00674     else if(strncmp("CONNECT", m_inputBuf, 7 /*=strlen("CONNECT")*/) == 0) //Result can be "CONNECT" or "CONNECT %d", indicating baudrate
00675     {
00676       DBG("CONNECT result received");
00677       m_transactionResult.code = 0;
00678       m_transactionResult.result = ATResult::AT_CONNECT;
00679       m_transactionState = IDLE;
00680       int* msg = m_AT2Env.alloc(osWaitForever);
00681       *msg = AT_RESULT_READY;
00682       m_AT2Env.put(msg); //Command has been processed
00683       return OK;
00684     }
00685     else if(strcmp("COMMAND NOT SUPPORT", m_inputBuf) == 0) //Huawei-specific, not normalized
00686     {
00687       DBG("COMMAND NOT SUPPORT result received");
00688       m_transactionResult.code = 0;
00689       m_transactionResult.result = ATResult::AT_ERROR;
00690       m_transactionState = IDLE;
00691       int* msg = m_AT2Env.alloc(osWaitForever);
00692       *msg = AT_RESULT_READY;
00693       m_AT2Env.put(msg); //Command has been processed
00694       return OK;
00695     }
00696     else if(strstr(m_inputBuf, "+CME ERROR:") == m_inputBuf) //Mobile Equipment Error
00697     {
00698       std::sscanf(m_inputBuf + 12 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code);
00699       DBG("+CME ERROR: %d result received", m_transactionResult.code);
00700       m_transactionResult.result = ATResult::AT_CME_ERROR;
00701       m_transactionState = IDLE;
00702       int* msg = m_AT2Env.alloc(osWaitForever);
00703       *msg = AT_RESULT_READY;
00704       m_AT2Env.put(msg); //Command has been processed
00705       return OK;
00706     }
00707     else if(strstr(m_inputBuf, "+CMS ERROR:") == m_inputBuf) //SIM Error
00708     {
00709       std::sscanf(m_inputBuf + 13 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code);
00710       DBG("+CMS ERROR: %d result received", m_transactionResult.code);
00711       m_transactionResult.result = ATResult::AT_CMS_ERROR;
00712       m_transactionState = IDLE;
00713       int* msg = m_AT2Env.alloc(osWaitForever);
00714       *msg = AT_RESULT_READY;
00715       m_AT2Env.put(msg); //Command has been processed
00716       return OK;
00717     }
00718     else
00719     {
00720       DBG("Unprocessed result received: '%s'", m_inputBuf);
00721       //Must call transaction processor to complete line processing
00722       int ret = m_pTransactionProcessor->onNewATResponseLine(this, m_inputBuf); //Here sendData can be called
00723       return ret;
00724     }
00725   }
00726 
00727   return OK;
00728 }
00729 
00730 int ATCommandsInterface::processEntryPrompt()
00731 {
00732   DBG("Calling prompt handler");
00733   int ret = m_pTransactionProcessor->onNewEntryPrompt(this); //Here sendData can be called
00734 
00735   if( ret != NET_MOREINFO ) //A new prompt is expected
00736   {
00737     DBG("Sending break character");
00738     //Send CTRL+Z (break sequence) to exit prompt
00739     char seq[2] = {BRK, 0x00};
00740     sendData(seq);
00741   }
00742   return OK;
00743 }
00744 
00745 //This will be called on initialization & after the execution of a command
00746 void ATCommandsInterface::enableEvents()
00747 {
00748   //Advertize this to events handlers
00749   m_eventsMgmtMtx.lock();
00750   for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
00751   {
00752     if( m_eventsHandlers[i] != NULL )
00753     {
00754       m_eventsHandlers[i]->onDispatchStart();
00755       //Enable this kind of events
00756       const char* cmd  = m_eventsHandlers[i]->getEventsEnableCommand();
00757       if(cmd != NULL)
00758       {
00759         int ret = executeInternal(cmd, this, NULL); //Execute enable command
00760         if(ret)
00761         {
00762           WARN("Events enabling command \"%s\" failed", cmd);
00763         }
00764       }
00765     }
00766   }
00767   m_eventsMgmtMtx.unlock();
00768 }
00769 
00770 //This will be called on de-initialization & before the execution of a command to prevent unsollicited result codes from polluting the results
00771 void ATCommandsInterface::disableEvents()
00772 {
00773   //Advertize this to events handlers
00774   m_eventsMgmtMtx.lock();
00775   for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
00776   {
00777     if( m_eventsHandlers[i] != NULL )
00778     {
00779       m_eventsHandlers[i]->onDispatchStart();
00780       //Disable this kind of events
00781       const char* cmd = m_eventsHandlers[i]->getEventsDisableCommand();
00782       if(cmd != NULL)
00783       {
00784         int ret = executeInternal(cmd, this, NULL); //Execute disable command
00785         if(ret)
00786         {
00787           WARN("Events disabling command \"%s\" failed", cmd);
00788         }
00789       }
00790     }
00791   }
00792   m_eventsMgmtMtx.unlock();
00793 }
00794 
00795 //Commands that can be called during onNewATResponseLine callback, additionally to close()
00796 //Access to this method is protected (can ONLY be called on processing thread during IATCommandsProcessor::onNewATResponseLine execution)
00797 int ATCommandsInterface::sendData(const char* data)
00798 {
00799   //m_inputBuf is cleared at this point (and MUST therefore be empty)
00800   int dataLen = strlen(data);
00801   DBG("Sending raw string of length %d", dataLen);
00802   int ret = m_pStream->write((uint8_t*)data, dataLen, osWaitForever);
00803   if(ret)
00804   {
00805     WARN("Could not write to stream (returned %d)", ret);
00806     return ret;
00807   }
00808 
00809   int dataPos = 0;
00810   do
00811   {
00812     //Read echo
00813     size_t readLen;
00814     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
00815     if(ret)
00816     {
00817       WARN("Could not read from stream (returned %d)", ret);
00818       m_inputPos = 0; //Reset input buffer state
00819       m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00820       return ret;
00821     }
00822 
00823     if( memcmp(m_inputBuf, data + dataPos, readLen) != 0 )
00824     {
00825       //Echo does not match output
00826       m_inputBuf[readLen] = '\0';
00827       WARN("Echo does not match output, got '%s' instead", m_inputBuf);
00828       m_inputPos = 0; //Reset input buffer state
00829       m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00830       return NET_DIFF;
00831     }
00832 
00833     dataPos += readLen;
00834     //If all characters have not been read yet
00835 
00836   } while(dataPos < dataLen);
00837 
00838   DBG("String sent successfully");
00839 
00840   m_inputPos = 0; //Reset input buffer state
00841   m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00842 
00843   return OK;
00844 }
00845 
00846 /*static*/ void ATCommandsInterface::staticCallback(void const* p)
00847 {
00848   ((ATCommandsInterface*)p)->process();
00849 }
00850 
00851 int ATCommandsInterface::ATResultToReturnCode(ATResult result) //Helper
00852 {
00853   if(result.result == ATResult::AT_OK)
00854   {
00855     return OK;
00856   }
00857   else
00858   {
00859     return NET_MOREINFO;
00860   }
00861 }
00862 
00863 /*virtual*/ int ATCommandsInterface::onNewATResponseLine(ATCommandsInterface* pInst, const char* line) //Default implementation for simple commands handling
00864 {
00865   return OK;
00866 }
00867 
00868 /*virtual*/ int ATCommandsInterface::onNewEntryPrompt(ATCommandsInterface* pInst) //Default implementation (just sends Ctrl+Z to exit the prompt by returning OK right-away)
00869 {
00870   return OK;
00871 }
00872 
00873 void ATCommandsInterface::process() //Processing thread
00874 {
00875   DBG("AT Thread started");
00876   while(true)
00877   {
00878     DBG("AT Processing on hold");
00879     m_processingThread.signal_wait(AT_SIG_PROCESSING_START); //Block until the process is started
00880 
00881     m_processingMtx.lock();
00882     DBG("AT Processing started");
00883     //First of all discard buffer
00884     int ret;
00885     size_t readLen;
00886     do //Drop everything
00887     {
00888       ret = m_pStream->read((uint8_t*)m_inputBuf, &readLen, AT_INPUT_BUF_SIZE - 1, 0); //Do NOT wait at this point
00889     } while(ret == OK);
00890     m_inputPos = 0; //Clear input buffer
00891     do
00892     {
00893       DBG("Trying to send a pending command");
00894       trySendCommand(); //This must be tried first as we discarded the buffer before and therefore would be blocking though there is a pending command
00895       DBG("Trying to read a new line");
00896       tryReadLine();
00897     } while( m_processingThread.signal_wait(AT_SIG_PROCESSING_STOP, 0).status != osEventSignal ); //Loop until the process is interrupted
00898     m_processingMtx.unlock();
00899     DBG("AT Processing stopped");
00900   }
00901 }
00902