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