Vodafone K3770/K3772-Z modems driver & networking library

Dependencies:   Socket USBHostWANDongle lwip-sys lwip

Dependents:   VodafoneUSBModemHTTPClientTest VodafoneUSBModemNTPClientTest VodafoneUSBModemSMSTest VodafoneUSBModemUSSDTest ... more

Fork of VodafoneUSBModem_bleedingedge by Donatien Garnier

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