local fork

Dependencies:   Socket USBHostWANDongle_bleedingedge lwip-sys lwip

Dependents:   Encrypted

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     DBG("In buffer: [%s]", m_inputBuf);
00322   }
00323 
00324   if( ret == NET_INTERRUPTED ) //It is worth checking readLen as data might have been read even though the read was interrupted
00325   {
00326     DBG("Read was interrupted with %d chars read",readLen);
00327     return NET_INTERRUPTED; //0 chars were read
00328   }
00329   else if(readLen == 0)
00330   {
00331     DBG("Nothing read");
00332     return OK; //0 chars were read
00333   }
00334 
00335   DBG("Trying to process incoming line");
00336   bool lineProcessed = false;
00337 
00338   do
00339   {
00340     lineProcessed = false; //Reset flag
00341 
00342     DBG("New iteration");
00343 
00344     //Look for a new line
00345     if(!lineDetected)
00346     {
00347       DBG("No line detected yet");
00348       //Try to look for a starting CRLF
00349       char* crPtr = strchr(m_inputBuf, CR);
00350       /*
00351       Different cases at this point:
00352       - CRLF%c sequence: this is the start of a line
00353       - CRLFCR(LF) sequence: this is the end of a line (followed by the beginning of the next one)
00354       - LF: this is the trailing LF char of the previous line, discard
00355       - CR / CRLF incomplete sequence: more data is needed to determine which action to take
00356       - %c ... CR sequence: this should be the echo of the previous sequence
00357       - %c sequence: This might be the echo of the previous command; more data is needed to determine which action to take
00358 
00359       In every case, move mem at the beginning
00360       */
00361       if(crPtr != NULL)
00362       {
00363         DBG("CR char found");
00364 
00365 #if 0
00366         //Discard all preceding characters (can do nothing if m_inputBuf == crPtr)
00367         memmove(m_inputBuf, crPtr, (m_inputPos + 1) - (crPtr-m_inputBuf)); //Move null-terminating char as well
00368         m_inputPos = m_inputPos - (crPtr-m_inputBuf); //Adjust m_inputPos
00369 #endif
00370 
00371         //If the line starts with CR, this should be a result code
00372         if( crPtr == m_inputBuf )
00373         {
00374           //To determine the sequence we need at least 3 chars
00375           if(m_inputPos >= 3)
00376           {
00377             //Look for a LF char next to the CR char
00378             if(m_inputBuf[1] == LF)
00379             {
00380               //At this point we can check whether this is the end of a preceding line or the beginning of a new one
00381               if(m_inputBuf[2] != CR)
00382               {
00383                 DBG("Beginning of new line found");
00384                 //Beginning of a line
00385                 lineDetected = true; //Move to next state-machine step
00386               }
00387               else
00388               {
00389                 //End of an unprocessed line
00390                 WARN("End of unprocessed line");
00391               }
00392               //In both cases discard CRLF
00393               DBG("Discarding CRLF");
00394               memmove(m_inputBuf, m_inputBuf + 2, (m_inputPos + 1) - 2); //Move null-terminating char as well
00395               m_inputPos = m_inputPos - 2; //Adjust m_inputPos
00396             }
00397             else
00398             {
00399               //This is completely unexpected, discard the CR char to try to recover good state
00400               WARN("Unexpected %c char (%02d code) found after CR char", m_inputBuf[1]);
00401               memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well
00402               m_inputPos = m_inputPos - 1; //Adjust m_inputPos
00403             }
00404           }
00405         }
00406         //if the line does NOT begin with CR, this can be an echo of the previous command, process it
00407         else
00408         {
00409           int crPos = crPtr - m_inputBuf;
00410           int lfOff = 0; //Offset for LF if present
00411           DBG("New line found (possible echo of command)");
00412           //This is the end of line
00413           //Replace m_inputBuf[crPos] with null-terminating char
00414           m_inputBuf[crPos] = '\0';
00415           //Check if there is a LF char afterwards
00416           if(m_inputPos - crPos >= 1)
00417           {
00418             if(m_inputBuf[crPos+1] == LF)
00419             {
00420               lfOff++; //We will discard LF char as well
00421             }
00422           }
00423           //Process line
00424           int ret = processReadLine();
00425           if(ret)
00426           {
00427             m_inputPos = 0;
00428             m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00429             lineDetected = false;
00430             return ret;
00431           }
00432 
00433           //If sendData has been called, all incoming data has been discarded
00434           if(m_inputPos > 0)
00435           {
00436             memmove(m_inputBuf, m_inputBuf + crPos + lfOff + 1, (m_inputPos + 1) - (crPos + lfOff + 1)); //Move null-terminating char as well
00437             m_inputPos = m_inputPos - (crPos + lfOff + 1); //Adjust m_inputPos
00438           }
00439           DBG("One line was successfully processed");
00440           lineProcessed = true; //Line was processed with success
00441           lineDetected = false; //Search now for a new line
00442         }
00443       }
00444       else if(m_inputBuf[0] == LF) //If there is a remaining LF char from the previous line, discard it
00445       {
00446         DBG("Discarding single LF char");
00447         memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well
00448         m_inputPos = m_inputPos - 1; //Adjust m_inputPos
00449       }
00450     }
00451 
00452     //Look for the end of line
00453     if(lineDetected)
00454     {
00455       DBG("Looking for end of line");
00456       //Try to look for a terminating CRLF
00457       char* crPtr = strchr(m_inputBuf, CR);
00458       /*
00459       Different cases at this point:
00460       - CRLF sequence: this is the end of the line
00461       - CR%c sequence : unexpected
00462       - CR incomplete sequence: more data is needed to determine which action to take
00463       */
00464 
00465       //Try to look for a '>' (greater than character) that marks an entry prompt
00466       char* greaterThanPtr = strchr(m_inputBuf, GD);
00467       /*
00468       This character must be detected as there is no CRLF sequence at the end of an entry prompt
00469        */
00470 
00471       if(crPtr != NULL)
00472       {
00473         DBG("CR char found");
00474         int crPos = crPtr - m_inputBuf;
00475         //To determine the sequence we need at least 2 chars
00476         if(m_inputPos - crPos >= 2)
00477         {
00478           //Look for a LF char next to the CR char
00479           if(m_inputBuf[crPos + 1] == LF)
00480           {
00481             DBG("End of new line found");
00482             //This is the end of line
00483             //Replace m_inputBuf[crPos] with null-terminating char
00484             m_inputBuf[crPos] = '\0';
00485             //Process line
00486             int ret = processReadLine();
00487             if(ret)
00488             {
00489               m_inputPos = 0;
00490               m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00491               lineDetected = false;
00492               return ret;
00493             }
00494 
00495             //If sendData has been called, all incoming data has been discarded
00496             if(m_inputPos > 0)
00497             {
00498               //Shift remaining data to beginning of buffer
00499               memmove(m_inputBuf, m_inputBuf + crPos + 2, (m_inputPos + 1) - (crPos + 2)); //Move null-terminating char as well
00500               m_inputPos = m_inputPos - (crPos + 2); //Adjust m_inputPos
00501             }
00502 
00503             DBG("One line was successfully processed");
00504             lineProcessed = true; //Line was processed with success
00505           }
00506           else
00507           {
00508             //This is completely unexpected, discard all chars till the CR char to try to recover good state
00509             WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[crPos + 1]);
00510             memmove(m_inputBuf, m_inputBuf + crPos + 1, (m_inputPos + 1) - (crPos + 1)); //Move null-terminating char as well
00511             m_inputPos = m_inputPos - (crPos + 1); //Adjust m_inputPos
00512           }
00513           lineDetected = false; //In both case search now for a new line
00514         }
00515       }
00516       else if(greaterThanPtr != NULL)
00517       {
00518         DBG("> char found");
00519         int gdPos = greaterThanPtr - m_inputBuf;
00520         //To determine the sequence we need at least 2 chars
00521         if(m_inputPos - gdPos >= 2)
00522         {
00523           //Look for a space char next to the GD char
00524           if(m_inputBuf[gdPos + 1] == ' ')
00525           {
00526             //This is an entry prompt
00527             //Replace m_inputBuf[gdPos] with null-terminating char
00528             m_inputBuf[gdPos] = '\0';
00529 
00530             //Shift remaining data to beginning of buffer
00531             memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well
00532             m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos
00533 
00534             //Process prompt
00535             ret = processEntryPrompt();
00536             if(ret)
00537             {
00538               m_inputPos = 0;
00539               m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00540               lineDetected = false;
00541               return ret;
00542             }
00543 
00544             DBG("One line was successfully processed");
00545             lineProcessed = true; //Line was processed with success
00546           }
00547           else
00548           {
00549             //This is completely unexpected, discard all chars till the GD char to try to recover good state
00550             WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[gdPos + 1]);
00551             memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well
00552             m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos
00553           }
00554           lineDetected = false; //In both case search now for a new line
00555         }
00556       }
00557     }
00558   } while(lineProcessed); //If one complete line was processed there might be other incoming lines that can also be processed without reading the buffer again
00559 
00560   //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)
00561   if(m_inputPos == AT_INPUT_BUF_SIZE - 1)
00562   {
00563     //Discard everything
00564     m_inputPos = 0;
00565     m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00566     WARN("Incoming buffer is too short to process incoming line");
00567     //Look for a new line
00568     lineDetected = false;
00569   }
00570 
00571   DBG("Processed every full incoming lines");
00572 
00573   return OK;
00574 }
00575 
00576 int ATCommandsInterface::trySendCommand()
00577 {
00578   osEvent evt = m_env2AT.get(0);
00579   DBG("status = %d, msg = %d", evt.status, evt.value.p);
00580   if(evt.status == osEventMail)
00581   {
00582     int* msg = (int*) evt.value.p;
00583     if( *msg == AT_CMD_READY ) //Command pending
00584     {
00585       if(m_transactionState != IDLE)
00586       {
00587         WARN("Previous command not processed!");
00588       }
00589       DBG("Sending pending command %s (%d)",m_transactionCommand,strlen(m_transactionCommand));
00590       m_pStream->write((uint8_t*)m_transactionCommand, strlen(m_transactionCommand), osWaitForever);
00591       char cr = CR;
00592       m_pStream->write((uint8_t*)&cr, 1, osWaitForever); //Carriage return line terminator
00593       m_transactionState = COMMAND_SENT;
00594     }
00595     else //Timeout
00596     {
00597       DBG("Timeout message received");
00598       //Acknowledge
00599       int* msg = m_AT2Env.alloc(osWaitForever);
00600       *msg = AT_TIMEOUT;
00601       m_AT2Env.put(msg); //Command has timed out
00602       m_transactionState = IDLE; //State-machine reset
00603     }
00604     m_env2AT.free(msg);
00605   }
00606   return OK;
00607 }
00608 
00609 int ATCommandsInterface::processReadLine()
00610 {
00611   DBG("Processing read line [%s]", m_inputBuf);
00612   //The line is stored in m_inputBuf
00613   if(m_transactionState == COMMAND_SENT)
00614   {
00615     //If the command has been sent, checks echo to see if it has been received properly
00616     if( strcmp(m_transactionCommand, m_inputBuf) == 0 )
00617     {
00618       DBG("Command echo received");
00619       //If so, it means that the following lines will only be solicited results
00620       m_transactionState = READING_RESULT;
00621       return OK;
00622     }
00623   }
00624   if(m_transactionState == IDLE || m_transactionState == COMMAND_SENT)
00625   {
00626     bool found = false;
00627     char* pSemicol = strchr(m_inputBuf, ':');
00628     char* pData = NULL;
00629     if( pSemicol != NULL ) //Split the identifier & the result code (if it exists)
00630     {
00631       *pSemicol = '\0';
00632       pData = pSemicol + 1;
00633       if(pData[0]==' ')
00634       {
00635         pData++; //Suppress whitespace
00636       }
00637     }
00638     //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
00639     m_eventsProcessingMtx.lock();
00640     //Go through the list
00641     for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
00642     {
00643       if( m_eventsHandlers[i] != NULL )
00644       {
00645         if( m_eventsHandlers[i]->isATCodeHandled(m_inputBuf) )
00646         {
00647           m_eventsHandlers[i]->onEvent(m_inputBuf, pData);
00648           found = true; //Do not break here as there might be multiple handlers for one event type
00649         }
00650       }
00651     }
00652     m_eventsProcessingMtx.unlock();
00653     if(found)
00654     {
00655       return OK;
00656     }
00657   }
00658   if(m_transactionState == READING_RESULT)
00659   {
00660     //The following lines can either be a command response or a result code (OK / ERROR / CONNECT / +CME ERROR: %s / +CMS ERROR: %s)
00661     if(strcmp("OK", m_inputBuf) == 0)
00662     {
00663       DBG("OK result received");
00664       m_transactionResult.code = 0;
00665       m_transactionResult.result = ATResult::AT_OK;
00666       m_transactionState = IDLE;
00667       int* msg = m_AT2Env.alloc(osWaitForever);
00668       *msg = AT_RESULT_READY;
00669       m_AT2Env.put(msg); //Command has been processed
00670       return OK;
00671     }
00672     else if(strcmp("ERROR", m_inputBuf) == 0)
00673     {
00674       DBG("ERROR result received");
00675       m_transactionResult.code = 0;
00676       m_transactionResult.result = ATResult::AT_ERROR;
00677       m_transactionState = IDLE;
00678       int* msg = m_AT2Env.alloc(osWaitForever);
00679       *msg = AT_RESULT_READY;
00680       m_AT2Env.put(msg); //Command has been processed
00681       return OK;
00682     }
00683     else if(strncmp("CONNECT", m_inputBuf, 7 /*=strlen("CONNECT")*/) == 0) //Result can be "CONNECT" or "CONNECT %d", indicating baudrate
00684     {
00685       DBG("CONNECT result received");
00686       m_transactionResult.code = 0;
00687       m_transactionResult.result = ATResult::AT_CONNECT;
00688       m_transactionState = IDLE;
00689       int* msg = m_AT2Env.alloc(osWaitForever);
00690       *msg = AT_RESULT_READY;
00691       m_AT2Env.put(msg); //Command has been processed
00692       return OK;
00693     }
00694     else if(strcmp("COMMAND NOT SUPPORT", m_inputBuf) == 0) //Huawei-specific, not normalized
00695     {
00696       DBG("COMMAND NOT SUPPORT result received");
00697       m_transactionResult.code = 0;
00698       m_transactionResult.result = ATResult::AT_ERROR;
00699       m_transactionState = IDLE;
00700       int* msg = m_AT2Env.alloc(osWaitForever);
00701       *msg = AT_RESULT_READY;
00702       m_AT2Env.put(msg); //Command has been processed
00703       return OK;
00704     }
00705     else if(strstr(m_inputBuf, "+CME ERROR:") == m_inputBuf) //Mobile Equipment Error
00706     {
00707       std::sscanf(m_inputBuf + 12 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code);
00708       DBG("+CME ERROR: %d result received", m_transactionResult.code);
00709       m_transactionResult.result = ATResult::AT_CME_ERROR;
00710       m_transactionState = IDLE;
00711       int* msg = m_AT2Env.alloc(osWaitForever);
00712       *msg = AT_RESULT_READY;
00713       m_AT2Env.put(msg); //Command has been processed
00714       return OK;
00715     }
00716     else if(strstr(m_inputBuf, "+CMS ERROR:") == m_inputBuf) //SIM Error
00717     {
00718       std::sscanf(m_inputBuf + 13 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code);
00719       DBG("+CMS ERROR: %d result received", m_transactionResult.code);
00720       m_transactionResult.result = ATResult::AT_CMS_ERROR;
00721       m_transactionState = IDLE;
00722       int* msg = m_AT2Env.alloc(osWaitForever);
00723       *msg = AT_RESULT_READY;
00724       m_AT2Env.put(msg); //Command has been processed
00725       return OK;
00726     }
00727     else
00728     {
00729       DBG("Unprocessed result received: '%s'", m_inputBuf);
00730       //Must call transaction processor to complete line processing
00731       int ret = m_pTransactionProcessor->onNewATResponseLine(this, m_inputBuf); //Here sendData can be called
00732       return ret;
00733     }
00734   }
00735 
00736   return OK;
00737 }
00738 
00739 int ATCommandsInterface::processEntryPrompt()
00740 {
00741   DBG("Calling prompt handler");
00742   int ret = m_pTransactionProcessor->onNewEntryPrompt(this); //Here sendData can be called
00743 
00744   if( ret != NET_MOREINFO ) //A new prompt is expected
00745   {
00746     DBG("Sending break character");
00747     //Send CTRL+Z (break sequence) to exit prompt
00748     char seq[2] = {BRK, 0x00};
00749     sendData(seq);
00750   }
00751   return OK;
00752 }
00753 
00754 //This will be called on initialization & after the execution of a command
00755 void ATCommandsInterface::enableEvents()
00756 {
00757   DBG("Trying to enable events");
00758   //Advertize this to events handlers
00759   m_eventsMgmtMtx.lock();
00760   for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
00761   {
00762     if( m_eventsHandlers[i] != NULL )
00763     {
00764       m_eventsHandlers[i]->onDispatchStart();
00765       //Enable this kind of events
00766       if(m_eventsHandlers[i]->getEventsEnableCommand() != NULL)
00767       {
00768         DBG("Enabling events for handler %d",i);
00769         int ret = executeInternal(m_eventsHandlers[i]->getEventsEnableCommand(), this, NULL); //Execute enable command
00770         if(ret)
00771         {
00772           WARN("Events enabling command failed: %d",ret);
00773         } else {
00774            DBG("Enabled events");
00775         }
00776       }
00777     }
00778   }
00779   m_eventsMgmtMtx.unlock();
00780 }
00781 
00782 //This will be called on de-initialization & before the execution of a command to prevent unsollicited result codes from polluting the results
00783 void ATCommandsInterface::disableEvents()
00784 {
00785   //Advertize this to events handlers
00786   m_eventsMgmtMtx.lock();
00787   for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
00788   {
00789     if( m_eventsHandlers[i] != NULL )
00790     {
00791       m_eventsHandlers[i]->onDispatchStart();
00792       //Disable this kind of events
00793       if(m_eventsHandlers[i]->getEventsDisableCommand() != NULL)
00794       {
00795         int ret = executeInternal(m_eventsHandlers[i]->getEventsDisableCommand(), this, NULL); //Execute disable command
00796         if(ret)
00797         {
00798           WARN("Events disabling command failed");
00799         }
00800       }
00801     }
00802   }
00803   m_eventsMgmtMtx.unlock();
00804 }
00805 
00806 //Commands that can be called during onNewATResponseLine callback, additionally to close()
00807 //Access to this method is protected (can ONLY be called on processing thread during IATCommandsProcessor::onNewATResponseLine execution)
00808 int ATCommandsInterface::sendData(const char* data)
00809 {
00810   //m_inputBuf is cleared at this point (and MUST therefore be empty)
00811   int dataLen = strlen(data);
00812   DBG("Sending raw string of length %d", dataLen);
00813   int ret = m_pStream->write((uint8_t*)data, dataLen, osWaitForever);
00814   if(ret)
00815   {
00816     WARN("Could not write to stream (returned %d)", ret);
00817     return ret;
00818   }
00819 
00820   int dataPos = 0;
00821   do
00822   {
00823     //Read echo
00824     size_t readLen;
00825     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
00826     if(ret)
00827     {
00828       WARN("Could not read from stream (returned %d)", ret);
00829       m_inputPos = 0; //Reset input buffer state
00830       m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00831       return ret;
00832     }
00833 
00834     if( memcmp(m_inputBuf, data + dataPos, readLen) != 0 )
00835     {
00836       //Echo does not match output
00837       m_inputBuf[readLen] = '\0';
00838       WARN("Echo does not match output, got '%s' instead", m_inputBuf);
00839       m_inputPos = 0; //Reset input buffer state
00840       m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00841       return NET_DIFF;
00842     }
00843 
00844     dataPos += readLen;
00845     //If all characters have not been read yet
00846 
00847   } while(dataPos < dataLen);
00848 
00849   DBG("String sent successfully");
00850 
00851   m_inputPos = 0; //Reset input buffer state
00852   m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
00853 
00854   return OK;
00855 }
00856 
00857 /*static*/ void ATCommandsInterface::staticCallback(void const* p)
00858 {
00859   ((ATCommandsInterface*)p)->process();
00860 }
00861 
00862 int ATCommandsInterface::ATResultToReturnCode(ATResult result) //Helper
00863 {
00864   if(result.result == ATResult::AT_OK)
00865   {
00866     return OK;
00867   }
00868   else
00869   {
00870     return NET_MOREINFO;
00871   }
00872 }
00873 
00874 /*virtual*/ int ATCommandsInterface::onNewATResponseLine(ATCommandsInterface* pInst, const char* line) //Default implementation for simple commands handling
00875 {
00876   return OK;
00877 }
00878 
00879 /*virtual*/ int ATCommandsInterface::onNewEntryPrompt(ATCommandsInterface* pInst) //Default implementation (just sends Ctrl+Z to exit the prompt by returning OK right-away)
00880 {
00881   return OK;
00882 }
00883 
00884 void ATCommandsInterface::pause() {
00885    DBG("pausing at commands interface");
00886    m_pStream->abortRead();
00887    m_processingThread.signal_set(AT_SIG_PROCESSING_STOP);
00888 }
00889 
00890 void ATCommandsInterface::restart() {
00891    DBG("restarting AT commands interface");
00892    m_processingThread.signal_set(AT_SIG_PROCESSING_START);
00893 }
00894 
00895 void ATCommandsInterface::process() //Processing thread
00896 {
00897   DBG("AT Thread started");
00898   while(true)
00899   {
00900     DBG("AT Processing on hold");
00901     m_processingThread.signal_wait(AT_SIG_PROCESSING_START); //Block until the process is started
00902 
00903     m_processingMtx.lock();
00904     DBG("AT Processing started");
00905     //First of all discard buffer
00906     int ret;
00907     size_t readLen;
00908     do //Drop everything
00909     {
00910       ret = m_pStream->read((uint8_t*)m_inputBuf, &readLen, AT_INPUT_BUF_SIZE - 1, 0); //Do NOT wait at this point
00911     } while(ret == OK);
00912     m_inputPos = 0; //Clear input buffer
00913     do
00914     {
00915       DBG("Trying to send a pending command");
00916       trySendCommand(); //This must be tried first as we discarded the buffer before and therefore would be blocking though there is a pending command
00917       DBG("Trying to read a new line");
00918       tryReadLine();
00919     } while( m_processingThread.signal_wait(AT_SIG_PROCESSING_STOP, 0).status != osEventSignal ); //Loop until the process is interrupted
00920     m_processingMtx.unlock();
00921     DBG("AT Processing stopped");
00922   }
00923 }
00924