PHS module SMA-01 library. see: https://developer.mbed.org/users/phsfan/notebook/abitusbmodem/

Dependencies:   Socket lwip-sys lwip

Dependents:   AbitUSBModem_HTTPTest AbitUSBModem_MQTTTest AbitUSBModem_WebsocketTest AbitUSBModem_SMSTest

Fork of VodafoneUSBModem by mbed official

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ATCommandsInterface.cpp Source File

ATCommandsInterface.cpp

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