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