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