fork of VodafoneUSBModem with updated USBHost library

Dependencies:   Socket USBHost lwip-sys lwip

Dependents:   VodafoneUSBModemSMSTest

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