Vodafone K3770/K3772-Z modems driver & networking library
Dependencies: Socket USBHostWANDongle lwip-sys lwip
Dependents: VodafoneUSBModemHTTPClientTest VodafoneUSBModemNTPClientTest VodafoneUSBModemSMSTest VodafoneUSBModemUSSDTest ... more
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 #if __DEBUG__ >= 4 00322 DBGX("In buffer: ["); 00323 if __DEBUG__ 00324 for(int i=0; i<strlen(m_inputBuf); i++) { 00325 if(m_inputBuf[i]=='\r') { 00326 DBGX("<CR>"); 00327 } else if(m_inputBuf[i]=='\n') { 00328 DBGX("<LF>"); 00329 } else { 00330 DBGX("%c",m_inputBuf[i]); 00331 } 00332 } 00333 DBGX("]\r\n"); 00334 #endif 00335 } 00336 00337 if( ret == NET_INTERRUPTED ) //It is worth checking readLen as data might have been read even though the read was interrupted 00338 { 00339 DBG("Read was interrupted with %d chars read",readLen); 00340 return NET_INTERRUPTED; //0 chars were read 00341 } 00342 else if(readLen == 0) 00343 { 00344 DBG("Nothing read"); 00345 return OK; //0 chars were read 00346 } 00347 00348 DBG("Trying to process incoming line"); 00349 bool lineProcessed = false; 00350 00351 do 00352 { 00353 lineProcessed = false; //Reset flag 00354 00355 DBG("New iteration"); 00356 00357 //Look for a new line 00358 if(!lineDetected) 00359 { 00360 DBG("No line detected yet"); 00361 //Try to look for a starting CRLF 00362 char* crPtr = strchr(m_inputBuf, CR); 00363 /* 00364 Different cases at this point: 00365 - CRLF%c sequence: this is the start of a line 00366 - CRLFCR(LF) sequence: this is the end of a line (followed by the beginning of the next one) 00367 - LF: this is the trailing LF char of the previous line, discard 00368 - CR / CRLF incomplete sequence: more data is needed to determine which action to take 00369 - %c ... CR sequence: this should be the echo of the previous sequence 00370 - %c sequence: This might be the echo of the previous command; more data is needed to determine which action to take 00371 00372 In every case, move mem at the beginning 00373 */ 00374 if(crPtr != NULL) 00375 { 00376 DBG("CR char found"); 00377 00378 #if 0 00379 //Discard all preceding characters (can do nothing if m_inputBuf == crPtr) 00380 memmove(m_inputBuf, crPtr, (m_inputPos + 1) - (crPtr-m_inputBuf)); //Move null-terminating char as well 00381 m_inputPos = m_inputPos - (crPtr-m_inputBuf); //Adjust m_inputPos 00382 #endif 00383 00384 //If the line starts with CR, this should be a result code 00385 if( crPtr == m_inputBuf ) 00386 { 00387 //To determine the sequence we need at least 3 chars 00388 if(m_inputPos >= 3) 00389 { 00390 //Look for a LF char next to the CR char 00391 if(m_inputBuf[1] == LF) 00392 { 00393 //At this point we can check whether this is the end of a preceding line or the beginning of a new one 00394 if(m_inputBuf[2] != CR) 00395 { 00396 DBG("Beginning of new line found"); 00397 //Beginning of a line 00398 lineDetected = true; //Move to next state-machine step 00399 } 00400 else 00401 { 00402 //End of an unprocessed line 00403 WARN("End of unprocessed line"); 00404 } 00405 //In both cases discard CRLF 00406 DBG("Discarding CRLF"); 00407 memmove(m_inputBuf, m_inputBuf + 2, (m_inputPos + 1) - 2); //Move null-terminating char as well 00408 m_inputPos = m_inputPos - 2; //Adjust m_inputPos 00409 } 00410 else 00411 { 00412 //This is completely unexpected, discard the CR char to try to recover good state 00413 WARN("Unexpected %c char (%02d code) found after CR char", m_inputBuf[1]); 00414 memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well 00415 m_inputPos = m_inputPos - 1; //Adjust m_inputPos 00416 } 00417 } 00418 } 00419 //if the line does NOT begin with CR, this can be an echo of the previous command, process it 00420 else 00421 { 00422 int crPos = crPtr - m_inputBuf; 00423 int lfOff = 0; //Offset for LF if present 00424 DBG("New line found (possible echo of command)"); 00425 //This is the end of line 00426 //Replace m_inputBuf[crPos] with null-terminating char 00427 m_inputBuf[crPos] = '\0'; 00428 //Check if there is a LF char afterwards 00429 if(m_inputPos - crPos >= 1) 00430 { 00431 if(m_inputBuf[crPos+1] == LF) 00432 { 00433 lfOff++; //We will discard LF char as well 00434 } 00435 } 00436 //Process line 00437 int ret = processReadLine(); 00438 if(ret) 00439 { 00440 m_inputPos = 0; 00441 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer 00442 lineDetected = false; 00443 return ret; 00444 } 00445 00446 //If sendData has been called, all incoming data has been discarded 00447 if(m_inputPos > 0) 00448 { 00449 memmove(m_inputBuf, m_inputBuf + crPos + lfOff + 1, (m_inputPos + 1) - (crPos + lfOff + 1)); //Move null-terminating char as well 00450 m_inputPos = m_inputPos - (crPos + lfOff + 1); //Adjust m_inputPos 00451 } 00452 DBG("One line was successfully processed"); 00453 lineProcessed = true; //Line was processed with success 00454 lineDetected = false; //Search now for a new line 00455 } 00456 } 00457 else if(m_inputBuf[0] == LF) //If there is a remaining LF char from the previous line, discard it 00458 { 00459 DBG("Discarding single LF char"); 00460 memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well 00461 m_inputPos = m_inputPos - 1; //Adjust m_inputPos 00462 } 00463 } 00464 00465 //Look for the end of line 00466 if(lineDetected) 00467 { 00468 DBG("Looking for end of line"); 00469 //Try to look for a terminating CRLF 00470 char* crPtr = strchr(m_inputBuf, CR); 00471 /* 00472 Different cases at this point: 00473 - CRLF sequence: this is the end of the line 00474 - CR%c sequence : unexpected 00475 - CR incomplete sequence: more data is needed to determine which action to take 00476 */ 00477 00478 //Try to look for a '>' (greater than character) that marks an entry prompt 00479 char* greaterThanPtr = strchr(m_inputBuf, GD); 00480 /* 00481 This character must be detected as there is no CRLF sequence at the end of an entry prompt 00482 */ 00483 00484 if(crPtr != NULL) 00485 { 00486 DBG("CR char found"); 00487 int crPos = crPtr - m_inputBuf; 00488 //To determine the sequence we need at least 2 chars 00489 if(m_inputPos - crPos >= 2) 00490 { 00491 //Look for a LF char next to the CR char 00492 if(m_inputBuf[crPos + 1] == LF) 00493 { 00494 DBG("End of new line found"); 00495 //This is the end of line 00496 //Replace m_inputBuf[crPos] with null-terminating char 00497 m_inputBuf[crPos] = '\0'; 00498 //Process line 00499 int ret = processReadLine(); 00500 // ret is non-zero in case of error 00501 if(ret) 00502 { 00503 m_inputPos = 0; 00504 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer 00505 lineDetected = false; 00506 return ret; 00507 } 00508 00509 //If sendData has been called, all incoming data has been discarded 00510 if(m_inputPos > 0) 00511 { 00512 //Shift remaining data to beginning of buffer 00513 memmove(m_inputBuf, m_inputBuf + crPos + 2, (m_inputPos + 1) - (crPos + 2)); //Move null-terminating char as well 00514 m_inputPos = m_inputPos - (crPos + 2); //Adjust m_inputPos 00515 } 00516 00517 DBG("One line was successfully processed"); 00518 lineProcessed = true; //Line was processed with success 00519 } 00520 else 00521 { 00522 //This is completely unexpected, discard all chars till the CR char to try to recover good state 00523 WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[crPos + 1]); 00524 memmove(m_inputBuf, m_inputBuf + crPos + 1, (m_inputPos + 1) - (crPos + 1)); //Move null-terminating char as well 00525 m_inputPos = m_inputPos - (crPos + 1); //Adjust m_inputPos 00526 } 00527 lineDetected = false; //In both case search now for a new line 00528 } 00529 } 00530 else if(greaterThanPtr != NULL) 00531 { 00532 DBG("> char found"); 00533 int gdPos = greaterThanPtr - m_inputBuf; 00534 //To determine the sequence we need at least 2 chars 00535 if(m_inputPos - gdPos >= 2) 00536 { 00537 //Look for a space char next to the GD char 00538 if(m_inputBuf[gdPos + 1] == ' ') 00539 { 00540 //This is an entry prompt 00541 //Replace m_inputBuf[gdPos] with null-terminating char 00542 m_inputBuf[gdPos] = '\0'; 00543 00544 //Shift remaining data to beginning of buffer 00545 memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well 00546 m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos 00547 00548 //Process prompt 00549 ret = processEntryPrompt(); 00550 if(ret) 00551 { 00552 m_inputPos = 0; 00553 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer 00554 lineDetected = false; 00555 return ret; 00556 } 00557 00558 DBG("One line was successfully processed"); 00559 lineProcessed = true; //Line was processed with success 00560 } 00561 else 00562 { 00563 //This is completely unexpected, discard all chars till the GD char to try to recover good state 00564 WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[gdPos + 1]); 00565 memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well 00566 m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos 00567 } 00568 lineDetected = false; //In both case search now for a new line 00569 } 00570 } 00571 } 00572 } while(lineProcessed); //If one complete line was processed there might be other incoming lines that can also be processed without reading the buffer again 00573 00574 //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) 00575 if(m_inputPos == AT_INPUT_BUF_SIZE - 1) 00576 { 00577 //Discard everything 00578 m_inputPos = 0; 00579 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer 00580 WARN("Incoming buffer is too short to process incoming line"); 00581 //Look for a new line 00582 lineDetected = false; 00583 } 00584 00585 DBG("Processed every full incoming lines"); 00586 00587 return OK; 00588 } 00589 00590 int ATCommandsInterface::trySendCommand() 00591 { 00592 osEvent evt = m_env2AT.get(0); 00593 DBG("status = %d, msg = %d", evt.status, evt.value.p); 00594 if(evt.status == osEventMail) 00595 { 00596 int* msg = (int*) evt.value.p; 00597 if( *msg == AT_CMD_READY ) //Command pending 00598 { 00599 if(m_transactionState != IDLE) 00600 { 00601 WARN("Previous command not processed!"); 00602 } 00603 DBG("Sending pending command %s (%d)",m_transactionCommand,strlen(m_transactionCommand)); 00604 m_pStream->write((uint8_t*)m_transactionCommand, strlen(m_transactionCommand), osWaitForever); 00605 char cr = CR; 00606 m_pStream->write((uint8_t*)&cr, 1, osWaitForever); //Carriage return line terminator 00607 m_transactionState = COMMAND_SENT; 00608 } 00609 else //Timeout 00610 { 00611 DBG("Timeout message received"); 00612 //Acknowledge 00613 int* msg = m_AT2Env.alloc(osWaitForever); 00614 *msg = AT_TIMEOUT; 00615 m_AT2Env.put(msg); //Command has timed out 00616 m_transactionState = IDLE; //State-machine reset 00617 } 00618 m_env2AT.free(msg); 00619 } 00620 return OK; 00621 } 00622 00623 int ATCommandsInterface::processReadLine() 00624 { 00625 DBG("Processing read line [%s]", m_inputBuf); 00626 //The line is stored in m_inputBuf 00627 if(m_transactionState == COMMAND_SENT) 00628 { 00629 //If the command has been sent, checks echo to see if it has been received properly 00630 if( strcmp(m_transactionCommand, m_inputBuf) == 0 ) 00631 { 00632 DBG("Command echo received"); 00633 //If so, it means that the following lines will only be solicited results 00634 m_transactionState = READING_RESULT; 00635 return OK; 00636 } 00637 } 00638 if(m_transactionState == IDLE || m_transactionState == COMMAND_SENT) 00639 { 00640 bool found = false; 00641 char* pSemicol = strchr(m_inputBuf, ':'); 00642 char* pData = NULL; 00643 if( pSemicol != NULL ) //Split the identifier & the result code (if it exists) 00644 { 00645 *pSemicol = '\0'; 00646 pData = pSemicol + 1; 00647 if(pData[0]==' ') 00648 { 00649 pData++; //Suppress whitespace 00650 } 00651 } 00652 //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 00653 m_eventsProcessingMtx.lock(); 00654 //Go through the list 00655 for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot 00656 { 00657 if( m_eventsHandlers[i] != NULL ) 00658 { 00659 if( m_eventsHandlers[i]->isATCodeHandled(m_inputBuf) ) 00660 { 00661 m_eventsHandlers[i]->onEvent(m_inputBuf, pData); 00662 found = true; //Do not break here as there might be multiple handlers for one event type 00663 } 00664 } 00665 } 00666 m_eventsProcessingMtx.unlock(); 00667 if(found) 00668 { 00669 return OK; 00670 } 00671 } 00672 if(m_transactionState == READING_RESULT) 00673 { 00674 //The following lines can either be a command response or a result code (OK / ERROR / CONNECT / +CME ERROR: %s / +CMS ERROR: %s) 00675 if(strcmp("OK", m_inputBuf) == 0) 00676 { 00677 DBG("OK result received"); 00678 m_transactionResult.code = 0; 00679 m_transactionResult.result = ATResult::AT_OK; 00680 m_transactionState = IDLE; 00681 int* msg = m_AT2Env.alloc(osWaitForever); 00682 *msg = AT_RESULT_READY; 00683 m_AT2Env.put(msg); //Command has been processed 00684 return OK; 00685 } 00686 else if(strcmp("ERROR", m_inputBuf) == 0) 00687 { 00688 DBG("ERROR result received"); 00689 m_transactionResult.code = 0; 00690 m_transactionResult.result = ATResult::AT_ERROR; 00691 m_transactionState = IDLE; 00692 int* msg = m_AT2Env.alloc(osWaitForever); 00693 *msg = AT_RESULT_READY; 00694 m_AT2Env.put(msg); //Command has been processed 00695 return OK; 00696 } 00697 else if(strncmp("CONNECT", m_inputBuf, 7 /*=strlen("CONNECT")*/) == 0) //Result can be "CONNECT" or "CONNECT %d", indicating baudrate 00698 { 00699 DBG("CONNECT result received"); 00700 m_transactionResult.code = 0; 00701 m_transactionResult.result = ATResult::AT_CONNECT; 00702 m_transactionState = IDLE; 00703 int* msg = m_AT2Env.alloc(osWaitForever); 00704 *msg = AT_RESULT_READY; 00705 m_AT2Env.put(msg); //Command has been processed 00706 return OK; 00707 } 00708 else if(strcmp("COMMAND NOT SUPPORT", m_inputBuf) == 0) //Huawei-specific, not normalized 00709 { 00710 DBG("COMMAND NOT SUPPORT result received"); 00711 m_transactionResult.code = 0; 00712 m_transactionResult.result = ATResult::AT_ERROR; 00713 m_transactionState = IDLE; 00714 int* msg = m_AT2Env.alloc(osWaitForever); 00715 *msg = AT_RESULT_READY; 00716 m_AT2Env.put(msg); //Command has been processed 00717 return OK; 00718 } 00719 else if(strstr(m_inputBuf, "+CME ERROR:") == m_inputBuf) //Mobile Equipment Error 00720 { 00721 std::sscanf(m_inputBuf + 12 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code); 00722 DBG("+CME ERROR: %d result received", m_transactionResult.code); 00723 m_transactionResult.result = ATResult::AT_CME_ERROR; 00724 m_transactionState = IDLE; 00725 int* msg = m_AT2Env.alloc(osWaitForever); 00726 *msg = AT_RESULT_READY; 00727 m_AT2Env.put(msg); //Command has been processed 00728 return OK; 00729 } 00730 else if(strstr(m_inputBuf, "+CMS ERROR:") == m_inputBuf) //SIM Error 00731 { 00732 std::sscanf(m_inputBuf + 13 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code); 00733 DBG("+CMS ERROR: %d result received", m_transactionResult.code); 00734 m_transactionResult.result = ATResult::AT_CMS_ERROR; 00735 m_transactionState = IDLE; 00736 int* msg = m_AT2Env.alloc(osWaitForever); 00737 *msg = AT_RESULT_READY; 00738 m_AT2Env.put(msg); //Command has been processed 00739 return OK; 00740 } 00741 else 00742 { 00743 DBG("Unprocessed result received: '%s'", m_inputBuf); 00744 //Must call transaction processor to complete line processing 00745 int ret = m_pTransactionProcessor->onNewATResponseLine(this, m_inputBuf); //Here sendData can be called 00746 return ret; 00747 } 00748 } 00749 00750 return OK; 00751 } 00752 00753 int ATCommandsInterface::processEntryPrompt() 00754 { 00755 DBG("Calling prompt handler"); 00756 int ret = m_pTransactionProcessor->onNewEntryPrompt(this); //Here sendData can be called 00757 00758 if( ret != NET_MOREINFO ) //A new prompt is expected 00759 { 00760 DBG("Sending break character"); 00761 //Send CTRL+Z (break sequence) to exit prompt 00762 char seq[2] = {BRK, 0x00}; 00763 sendData(seq); 00764 } 00765 return OK; 00766 } 00767 00768 //This will be called on initialization & after the execution of a command 00769 void ATCommandsInterface::enableEvents() 00770 { 00771 DBG("Trying to enable events"); 00772 //Advertize this to events handlers 00773 m_eventsMgmtMtx.lock(); 00774 for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot 00775 { 00776 if( m_eventsHandlers[i] != NULL ) 00777 { 00778 m_eventsHandlers[i]->onDispatchStart(); 00779 //Enable this kind of events 00780 if(m_eventsHandlers[i]->getEventsEnableCommand() != NULL) 00781 { 00782 DBG("Enabling events for handler %d",i); 00783 int ret = executeInternal(m_eventsHandlers[i]->getEventsEnableCommand(), this, NULL); //Execute enable command 00784 if(ret) 00785 { 00786 WARN("Events enabling command failed: %d",ret); 00787 } else { 00788 DBG("Enabled events"); 00789 } 00790 } 00791 } 00792 } 00793 m_eventsMgmtMtx.unlock(); 00794 } 00795 00796 //This will be called on de-initialization & before the execution of a command to prevent unsollicited result codes from polluting the results 00797 void ATCommandsInterface::disableEvents() 00798 { 00799 //Advertize this to events handlers 00800 m_eventsMgmtMtx.lock(); 00801 for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot 00802 { 00803 if( m_eventsHandlers[i] != NULL ) 00804 { 00805 m_eventsHandlers[i]->onDispatchStart(); 00806 //Disable this kind of events 00807 if(m_eventsHandlers[i]->getEventsDisableCommand() != NULL) 00808 { 00809 int ret = executeInternal(m_eventsHandlers[i]->getEventsDisableCommand(), this, NULL); //Execute disable command 00810 if(ret) 00811 { 00812 WARN("Events disabling command failed"); 00813 } 00814 } 00815 } 00816 } 00817 m_eventsMgmtMtx.unlock(); 00818 } 00819 00820 //Commands that can be called during onNewATResponseLine callback, additionally to close() 00821 //Access to this method is protected (can ONLY be called on processing thread during IATCommandsProcessor::onNewATResponseLine execution) 00822 int ATCommandsInterface::sendData(const char* data) 00823 { 00824 //m_inputBuf is cleared at this point (and MUST therefore be empty) 00825 int dataLen = strlen(data); 00826 DBG("Sending raw string of length %d", dataLen); 00827 int ret = m_pStream->write((uint8_t*)data, dataLen, osWaitForever); 00828 if(ret) 00829 { 00830 WARN("Could not write to stream (returned %d)", ret); 00831 return ret; 00832 } 00833 00834 int dataPos = 0; 00835 do 00836 { 00837 //Read echo 00838 size_t readLen; 00839 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 00840 if(ret) 00841 { 00842 WARN("Could not read from stream (returned %d)", ret); 00843 m_inputPos = 0; //Reset input buffer state 00844 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer 00845 return ret; 00846 } 00847 00848 if( memcmp(m_inputBuf, data + dataPos, readLen) != 0 ) 00849 { 00850 //Echo does not match output 00851 m_inputBuf[readLen] = '\0'; 00852 WARN("Echo does not match output, got '%s' instead", m_inputBuf); 00853 m_inputPos = 0; //Reset input buffer state 00854 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer 00855 return NET_DIFF; 00856 } 00857 00858 dataPos += readLen; 00859 //If all characters have not been read yet 00860 00861 } while(dataPos < dataLen); 00862 00863 DBG("String sent successfully"); 00864 00865 m_inputPos = 0; //Reset input buffer state 00866 m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer 00867 00868 return OK; 00869 } 00870 00871 /*static*/ void ATCommandsInterface::staticCallback(void const* p) 00872 { 00873 ((ATCommandsInterface*)p)->process(); 00874 } 00875 00876 int ATCommandsInterface::ATResultToReturnCode(ATResult result) //Helper 00877 { 00878 if(result.result == ATResult::AT_OK) 00879 { 00880 return OK; 00881 } 00882 else 00883 { 00884 return NET_MOREINFO; 00885 } 00886 } 00887 00888 /*virtual*/ int ATCommandsInterface::onNewATResponseLine(ATCommandsInterface* pInst, const char* line) //Default implementation for simple commands handling 00889 { 00890 return OK; 00891 } 00892 00893 /*virtual*/ int ATCommandsInterface::onNewEntryPrompt(ATCommandsInterface* pInst) //Default implementation (just sends Ctrl+Z to exit the prompt by returning OK right-away) 00894 { 00895 return OK; 00896 } 00897 00898 void ATCommandsInterface::pause() { 00899 DBG("pausing at commands interface"); 00900 m_pStream->abortRead(); 00901 m_processingThread.signal_set(AT_SIG_PROCESSING_STOP); 00902 } 00903 00904 void ATCommandsInterface::restart() { 00905 DBG("restarting AT commands interface"); 00906 m_processingThread.signal_set(AT_SIG_PROCESSING_START); 00907 } 00908 00909 void ATCommandsInterface::process() //Processing thread 00910 { 00911 DBG("AT Thread started"); 00912 while(true) 00913 { 00914 DBG("AT Processing on hold"); 00915 m_processingThread.signal_wait(AT_SIG_PROCESSING_START); //Block until the process is started 00916 00917 m_processingMtx.lock(); 00918 DBG("AT Processing started"); 00919 //First of all discard buffer 00920 int ret; 00921 size_t readLen; 00922 do //Drop everything 00923 { 00924 ret = m_pStream->read((uint8_t*)m_inputBuf, &readLen, AT_INPUT_BUF_SIZE - 1, 0); //Do NOT wait at this point 00925 } while(ret == OK); 00926 m_inputPos = 0; //Clear input buffer 00927 do 00928 { 00929 DBG("Trying to send a pending command"); 00930 trySendCommand(); //This must be tried first as we discarded the buffer before and therefore would be blocking though there is a pending command 00931 DBG("Trying to read a new line"); 00932 tryReadLine(); 00933 } while( m_processingThread.signal_wait(AT_SIG_PROCESSING_STOP, 0).status != osEventSignal ); //Loop until the process is interrupted 00934 m_processingMtx.unlock(); 00935 DBG("AT Processing stopped"); 00936 } 00937 } 00938
Generated on Tue Jul 12 2022 12:11:27 by 1.7.2