local fork
Dependencies: Socket USBHostWANDongle_bleedingedge lwip-sys lwip
Fork of VodafoneUSBModem_bleedingedge by
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
Generated on Tue Jul 12 2022 21:41:40 by 1.7.2