Nicholas Herriot / VodafoneK3770Lib
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ATCommandsInterface.cpp Source File

ATCommandsInterface.cpp

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