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.
Dependents: mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510
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 11:02:33 by
