ATCommandsInterface.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  1. /* ATCommandsInterface.cpp */
  2. /* Copyright (C) 2012 mbed.org, MIT License
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
  5. * and associated documentation files (the "Software"), to deal in the Software without restriction,
  6. * including without limitation the rights to use, copy, modify, merge, publish, distribute,
  7. * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in all copies or
  11. * substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
  14. * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  15. * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  16. * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  18. */
  19. #define __DEBUG__ 2 //ERR+WARN
  20. #ifndef __MODULE__
  21. #define __MODULE__ "ATCommandsInterface.cpp"
  22. #endif
  23. #include "core/fwk.h"
  24. #include <cstdio>
  25. #include <cstring> //For memset, strstr...
  26. using std::memmove;
  27. #include "ATCommandsInterface.h"
  28. ATCommandsInterface::ATCommandsInterface(IOStream* pStream) :
  29. m_pStream(pStream), m_open(false), m_transactionState(IDLE), m_env2AT(), m_AT2Env(), m_processingMtx(),
  30. m_processingThread(&ATCommandsInterface::staticCallback, this, (osPriority)AT_THREAD_PRIORITY, 4*192),
  31. m_eventsMgmtMtx(), m_eventsProcessingMtx()
  32. {
  33. memset(m_eventsHandlers, 0, MAX_AT_EVENTS_HANDLERS * sizeof(IATEventsHandler*));
  34. m_processingMtx.lock();
  35. }
  36. //Open connection to AT Interface in order to execute command & register/unregister events
  37. int ATCommandsInterface::open()
  38. {
  39. if( m_open )
  40. {
  41. WARN("AT interface is already open");
  42. return OK;
  43. }
  44. DBG("Opening AT interface");
  45. //Start processing
  46. m_processingThread.signal_set(AT_SIG_PROCESSING_START);
  47. m_processingMtx.unlock();
  48. m_open = true;
  49. DBG("AT interface opened");
  50. return OK;
  51. }
  52. //Initialize AT link & start events processing
  53. int ATCommandsInterface::init(bool reset /* = true*/)
  54. {
  55. //Lock transaction mutex
  56. m_transactionMtx.lock();
  57. if (reset)
  58. {
  59. DBG("Sending ATZ E1 V1");
  60. //Should we flush m_pStream at this point ???
  61. int err;
  62. int tries = 5;
  63. do
  64. {
  65. err = executeInternal("ATZ E1 V1", this, NULL, 3000); //Enable echo and verbosity
  66. if(err && tries)
  67. {
  68. WARN("No response, trying again");
  69. Thread::wait(1000); //Give dongle time to recover
  70. }
  71. } while(err && tries--);
  72. if( err )
  73. {
  74. ERR("Sending ATZ E1 V1 returned with err code %d", err);
  75. m_transactionMtx.unlock();
  76. return err;
  77. }
  78. }
  79. //Enable events handling and execute events enabling commands
  80. enableEvents();
  81. DBG("AT interface initialized");
  82. //Unlock transaction mutex
  83. m_transactionMtx.unlock();
  84. return OK;
  85. }
  86. //Close connection
  87. int ATCommandsInterface::close()
  88. {
  89. if( !m_open )
  90. {
  91. WARN("AT interface is already closed");
  92. return OK;
  93. }
  94. DBG("Closing AT interface");
  95. //Lock transaction mutex
  96. m_transactionMtx.lock();
  97. //Disable events handling and advertize this to the events handlers
  98. disableEvents();
  99. //Stop processing
  100. m_processingThread.signal_set(AT_SIG_PROCESSING_STOP);
  101. //m_stopSphre.release();
  102. int* msg = m_env2AT.alloc(osWaitForever);
  103. *msg = AT_STOP;
  104. m_env2AT.put(msg); //Used to unstall the process if needed
  105. //Unlock process routine (abort read)
  106. m_pStream->abortRead(); //This is thread-safe
  107. m_processingMtx.lock();
  108. m_open = false;
  109. //Unlock transaction mutex
  110. m_transactionMtx.unlock();
  111. DBG("AT interface closed");
  112. return OK;
  113. }
  114. bool ATCommandsInterface::isOpen()
  115. {
  116. return m_open;
  117. }
  118. int ATCommandsInterface::executeSimple(const char* command, ATResult* pResult, uint32_t timeout/*=1000*/)
  119. {
  120. return execute(command, this, pResult, timeout);
  121. }
  122. int ATCommandsInterface::execute(const char* command, IATCommandsProcessor* pProcessor, ATResult* pResult, uint32_t timeout/*=1000*/)
  123. {
  124. if(!m_open)
  125. {
  126. WARN("Interface is not open!");
  127. return NET_INVALID;
  128. }
  129. //Lock transaction mutex
  130. m_transactionMtx.lock();
  131. disableEvents(); //Disable unsollicited result codes
  132. int ret = executeInternal(command, pProcessor, pResult, timeout);
  133. enableEvents(); //Re-enable unsollicited result codes whatever the result of the command is
  134. //Unlock transaction mutex
  135. m_transactionMtx.unlock();
  136. return ret;
  137. }
  138. int ATCommandsInterface::registerEventsHandler(IATEventsHandler* pHdlr)
  139. {
  140. m_eventsMgmtMtx.lock();
  141. m_eventsProcessingMtx.lock();
  142. for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
  143. {
  144. if( m_eventsHandlers[i] == NULL )
  145. {
  146. m_eventsHandlers[i] = pHdlr;
  147. m_eventsProcessingMtx.unlock();
  148. m_eventsMgmtMtx.unlock();
  149. return OK;
  150. }
  151. }
  152. m_eventsProcessingMtx.unlock();
  153. m_eventsMgmtMtx.unlock();
  154. return NET_OOM; //No room left
  155. }
  156. int ATCommandsInterface::deregisterEventsHandler(IATEventsHandler* pHdlr)
  157. {
  158. m_eventsMgmtMtx.lock();
  159. m_eventsProcessingMtx.lock();
  160. for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find handler in list
  161. {
  162. if( m_eventsHandlers[i] == pHdlr )
  163. {
  164. m_eventsHandlers[i] = NULL;
  165. m_eventsProcessingMtx.unlock();
  166. m_eventsMgmtMtx.unlock();
  167. return OK;
  168. }
  169. }
  170. m_eventsProcessingMtx.unlock();
  171. m_eventsMgmtMtx.unlock();
  172. return NET_NOTFOUND; //Not found
  173. }
  174. //Private methods
  175. int ATCommandsInterface::executeInternal(const char* command, IATCommandsProcessor* pProcessor, ATResult* pResult, uint32_t timeout/*=1000*/)
  176. {
  177. DBG("Executing command %s", command);
  178. //Discard previous result if it arrived too late
  179. osEvent evt = m_AT2Env.get(0);
  180. if(evt.status == osEventMail)
  181. {
  182. m_AT2Env.free((int*)evt.value.p);
  183. WARN("Previous result discarded");
  184. }
  185. //Send params to the process routine
  186. m_transactionCommand = command;
  187. if(pProcessor != NULL)
  188. {
  189. m_pTransactionProcessor = pProcessor;
  190. }
  191. else
  192. {
  193. m_pTransactionProcessor = this; //Use default behaviour
  194. }
  195. DBG("Sending command ready signal to AT thread & aborting current blocking read operation");
  196. //Produce command ready signal
  197. int* msg = m_env2AT.alloc(osWaitForever);
  198. *msg = AT_CMD_READY;
  199. m_env2AT.put(msg);
  200. DBG("Trying to enter abortRead()");
  201. //Unlock process routine (abort read)
  202. m_pStream->abortRead(); //This is thread-safe
  203. //Wait for a result (get result message)
  204. evt = m_AT2Env.get(timeout);
  205. if(evt.status != osEventMail)
  206. {
  207. //Cancel request
  208. msg = m_env2AT.alloc(osWaitForever);
  209. *msg = AT_TIMEOUT;
  210. m_env2AT.put(msg);
  211. DBG("Trying to enter abortRead()");
  212. //Unlock process routine (abort read)
  213. m_pStream->abortRead(); //This is thread-safe
  214. //Wait for acknowledge
  215. int msgResult;
  216. do
  217. {
  218. evt = m_AT2Env.get(osWaitForever);
  219. msgResult = *((int*) evt.value.p);
  220. m_AT2Env.free((int*)evt.value.p);
  221. } while(msgResult != AT_TIMEOUT);
  222. WARN("Command returned no message");
  223. WARN("Command \"%s\" returned no message", command);
  224. return NET_TIMEOUT;
  225. }
  226. DBG("Command returned with message %d", *msg);
  227. m_AT2Env.free((int*)evt.value.p);
  228. if(pResult != NULL)
  229. {
  230. *pResult = m_transactionResult;
  231. }
  232. int ret = ATResultToReturnCode(m_transactionResult);
  233. if(ret != OK)
  234. {
  235. WARN("Command returned AT result %d with code %d", m_transactionResult.result, m_transactionResult.code);
  236. WARN("Command \"%s\" returned AT result %d with code %d", command, m_transactionResult.result, m_transactionResult.code);
  237. }
  238. DBG("Command returned successfully");
  239. return ret;
  240. }
  241. int ATCommandsInterface::tryReadLine()
  242. {
  243. static bool lineDetected = false;
  244. //Block on serial read or incoming command
  245. DBG("Trying to read a new line from stream");
  246. int ret = m_pStream->waitAvailable(); //This can be aborted
  247. size_t readLen = 0;
  248. if(ret == OK)
  249. {
  250. 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
  251. }
  252. if(ret == OK)
  253. {
  254. m_inputPos+=readLen;
  255. m_inputBuf[m_inputPos] = '\0'; //Add null terminating character to ease the use of str* functions
  256. DBG("In buffer: [%s]", m_inputBuf);
  257. }
  258. if( ret == NET_INTERRUPTED ) //It is worth checking readLen as data might have been read even though the read was interrupted
  259. {
  260. DBG("Read was interrupted");
  261. return NET_INTERRUPTED; //0 chars were read
  262. }
  263. else if(readLen == 0)
  264. {
  265. DBG("Nothing read");
  266. return OK; //0 chars were read
  267. }
  268. DBG("Trying to process incoming line");
  269. bool lineProcessed = false;
  270. do
  271. {
  272. lineProcessed = false; //Reset flag
  273. DBG("New iteration");
  274. //Look for a new line
  275. if(!lineDetected)
  276. {
  277. DBG("No line detected yet");
  278. //Try to look for a starting CRLF
  279. char* crPtr = strchr(m_inputBuf, CR);
  280. /*
  281. Different cases at this point:
  282. - CRLF%c sequence: this is the start of a line
  283. - CRLFCR(LF) sequence: this is the end of a line (followed by the beginning of the next one)
  284. - LF: this is the trailing LF char of the previous line, discard
  285. - CR / CRLF incomplete sequence: more data is needed to determine which action to take
  286. - %c ... CR sequence: this should be the echo of the previous sequence
  287. - %c sequence: This might be the echo of the previous command; more data is needed to determine which action to take
  288. In every case, move mem at the beginning
  289. */
  290. if(crPtr != NULL)
  291. {
  292. DBG("CR char found");
  293. #if 0
  294. //Discard all preceding characters (can do nothing if m_inputBuf == crPtr)
  295. memmove(m_inputBuf, crPtr, (m_inputPos + 1) - (crPtr-m_inputBuf)); //Move null-terminating char as well
  296. m_inputPos = m_inputPos - (crPtr-m_inputBuf); //Adjust m_inputPos
  297. #endif
  298. //If the line starts with CR, this should be a result code
  299. if( crPtr == m_inputBuf )
  300. {
  301. //To determine the sequence we need at least 3 chars
  302. if(m_inputPos >= 3)
  303. {
  304. //Look for a LF char next to the CR char
  305. if(m_inputBuf[1] == LF)
  306. {
  307. //At this point we can check whether this is the end of a preceding line or the beginning of a new one
  308. if(m_inputBuf[2] != CR)
  309. {
  310. DBG("Beginning of new line found");
  311. //Beginning of a line
  312. lineDetected = true; //Move to next state-machine step
  313. }
  314. else
  315. {
  316. //End of an unprocessed line
  317. WARN("End of unprocessed line");
  318. }
  319. //In both cases discard CRLF
  320. DBG("Discarding CRLF");
  321. memmove(m_inputBuf, m_inputBuf + 2, (m_inputPos + 1) - 2); //Move null-terminating char as well
  322. m_inputPos = m_inputPos - 2; //Adjust m_inputPos
  323. }
  324. else
  325. {
  326. //This is completely unexpected, discard the CR char to try to recover good state
  327. WARN("Unexpected %c char (%02d code) found after CR char", m_inputBuf[1]);
  328. memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well
  329. m_inputPos = m_inputPos - 1; //Adjust m_inputPos
  330. }
  331. }
  332. }
  333. //if the line does NOT begin with CR, this can be an echo of the previous command, process it
  334. else
  335. {
  336. int crPos = crPtr - m_inputBuf;
  337. int lfOff = 0; //Offset for LF if present
  338. DBG("New line found (possible echo of command)");
  339. //This is the end of line
  340. //Replace m_inputBuf[crPos] with null-terminating char
  341. m_inputBuf[crPos] = '\0';
  342. //Check if there is a LF char afterwards
  343. if(m_inputPos - crPos >= 1)
  344. {
  345. if(m_inputBuf[crPos+1] == LF)
  346. {
  347. lfOff++; //We will discard LF char as well
  348. }
  349. }
  350. //Process line
  351. int ret = processReadLine();
  352. if(ret)
  353. {
  354. m_inputPos = 0;
  355. m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
  356. lineDetected = false;
  357. return ret;
  358. }
  359. //If sendData has been called, all incoming data has been discarded
  360. if(m_inputPos > 0)
  361. {
  362. memmove(m_inputBuf, m_inputBuf + crPos + lfOff + 1, (m_inputPos + 1) - (crPos + lfOff + 1)); //Move null-terminating char as well
  363. m_inputPos = m_inputPos - (crPos + lfOff + 1); //Adjust m_inputPos
  364. }
  365. DBG("One line was successfully processed");
  366. lineProcessed = true; //Line was processed with success
  367. lineDetected = false; //Search now for a new line
  368. }
  369. }
  370. else if(m_inputBuf[0] == LF) //If there is a remaining LF char from the previous line, discard it
  371. {
  372. DBG("Discarding single LF char");
  373. memmove(m_inputBuf, m_inputBuf + 1, (m_inputPos + 1) - 1); //Move null-terminating char as well
  374. m_inputPos = m_inputPos - 1; //Adjust m_inputPos
  375. }
  376. }
  377. //Look for the end of line
  378. if(lineDetected)
  379. {
  380. DBG("Looking for end of line");
  381. //Try to look for a terminating CRLF
  382. char* crPtr = strchr(m_inputBuf, CR);
  383. /*
  384. Different cases at this point:
  385. - CRLF sequence: this is the end of the line
  386. - CR%c sequence : unexpected
  387. - CR incomplete sequence: more data is needed to determine which action to take
  388. */
  389. //Try to look for a '>' (greater than character) that marks an entry prompt
  390. char* greaterThanPtr = strchr(m_inputBuf, GD);
  391. /*
  392. This character must be detected as there is no CRLF sequence at the end of an entry prompt
  393. */
  394. if(crPtr != NULL)
  395. {
  396. DBG("CR char found");
  397. int crPos = crPtr - m_inputBuf;
  398. //To determine the sequence we need at least 2 chars
  399. if(m_inputPos - crPos >= 2)
  400. {
  401. //Look for a LF char next to the CR char
  402. if(m_inputBuf[crPos + 1] == LF)
  403. {
  404. DBG("End of new line found");
  405. //This is the end of line
  406. //Replace m_inputBuf[crPos] with null-terminating char
  407. m_inputBuf[crPos] = '\0';
  408. //Process line
  409. int ret = processReadLine();
  410. if(ret)
  411. {
  412. m_inputPos = 0;
  413. m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
  414. lineDetected = false;
  415. return ret;
  416. }
  417. //If sendData has been called, all incoming data has been discarded
  418. if(m_inputPos > 0)
  419. {
  420. //Shift remaining data to beginning of buffer
  421. memmove(m_inputBuf, m_inputBuf + crPos + 2, (m_inputPos + 1) - (crPos + 2)); //Move null-terminating char as well
  422. m_inputPos = m_inputPos - (crPos + 2); //Adjust m_inputPos
  423. }
  424. DBG("One line was successfully processed");
  425. lineProcessed = true; //Line was processed with success
  426. }
  427. else
  428. {
  429. //This is completely unexpected, discard all chars till the CR char to try to recover good state
  430. WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[crPos + 1]);
  431. memmove(m_inputBuf, m_inputBuf + crPos + 1, (m_inputPos + 1) - (crPos + 1)); //Move null-terminating char as well
  432. m_inputPos = m_inputPos - (crPos + 1); //Adjust m_inputPos
  433. }
  434. lineDetected = false; //In both case search now for a new line
  435. }
  436. }
  437. else if(greaterThanPtr != NULL)
  438. {
  439. DBG("> char found");
  440. int gdPos = greaterThanPtr - m_inputBuf;
  441. //To determine the sequence we need at least 2 chars
  442. if(m_inputPos - gdPos >= 2)
  443. {
  444. //Look for a space char next to the GD char
  445. if(m_inputBuf[gdPos + 1] == ' ')
  446. {
  447. //This is an entry prompt
  448. //Replace m_inputBuf[gdPos] with null-terminating char
  449. m_inputBuf[gdPos] = '\0';
  450. //Shift remaining data to beginning of buffer
  451. memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well
  452. m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos
  453. //Process prompt
  454. ret = processEntryPrompt();
  455. if(ret)
  456. {
  457. m_inputPos = 0;
  458. m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
  459. lineDetected = false;
  460. return ret;
  461. }
  462. DBG("One line was successfully processed");
  463. lineProcessed = true; //Line was processed with success
  464. }
  465. else
  466. {
  467. //This is completely unexpected, discard all chars till the GD char to try to recover good state
  468. WARN("Unexpected %c char (%02d code) found in incoming line", m_inputBuf[gdPos + 1]);
  469. memmove(m_inputBuf, m_inputBuf + gdPos + 1, (m_inputPos + 1) - (gdPos + 1)); //Move null-terminating char as well
  470. m_inputPos = m_inputPos - (gdPos + 1); //Adjust m_inputPos
  471. }
  472. lineDetected = false; //In both case search now for a new line
  473. }
  474. }
  475. }
  476. } while(lineProcessed); //If one complete line was processed there might be other incoming lines that can also be processed without reading the buffer again
  477. //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)
  478. if(m_inputPos == AT_INPUT_BUF_SIZE - 1)
  479. {
  480. //Discard everything
  481. m_inputPos = 0;
  482. m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
  483. WARN("Incoming buffer is too short to process incoming line");
  484. //Look for a new line
  485. lineDetected = false;
  486. }
  487. DBG("Processed every full incoming lines");
  488. return OK;
  489. }
  490. int ATCommandsInterface::trySendCommand()
  491. {
  492. osEvent evt = m_env2AT.get(0);
  493. DBG("status = %d, msg = %d", evt.status, evt.value.p);
  494. if(evt.status == osEventMail)
  495. {
  496. int* msg = (int*) evt.value.p;
  497. if( *msg == AT_CMD_READY ) //Command pending
  498. {
  499. if(m_transactionState != IDLE)
  500. {
  501. WARN("Previous command not processed!");
  502. }
  503. DBG("Sending pending command");
  504. m_pStream->write((uint8_t*)m_transactionCommand, strlen(m_transactionCommand), osWaitForever);
  505. char cr = CR;
  506. m_pStream->write((uint8_t*)&cr, 1, osWaitForever); //Carriage return line terminator
  507. m_transactionState = COMMAND_SENT;
  508. }
  509. else //Timeout
  510. {
  511. //Acknowledge
  512. int* msg = m_AT2Env.alloc(osWaitForever);
  513. *msg = AT_TIMEOUT;
  514. m_AT2Env.put(msg); //Command has timed out
  515. m_transactionState = IDLE; //State-machine reset
  516. }
  517. m_env2AT.free(msg);
  518. }
  519. return OK;
  520. }
  521. int ATCommandsInterface::processReadLine()
  522. {
  523. DBG("Processing read line [%s]", m_inputBuf);
  524. //The line is stored in m_inputBuf
  525. if(m_transactionState == COMMAND_SENT)
  526. {
  527. //If the command has been sent, checks echo to see if it has been received properly
  528. if( strcmp(m_transactionCommand, m_inputBuf) == 0 )
  529. {
  530. DBG("Command echo received");
  531. //If so, it means that the following lines will only be solicited results
  532. m_transactionState = READING_RESULT;
  533. return OK;
  534. }
  535. }
  536. if(m_transactionState == IDLE || m_transactionState == COMMAND_SENT)
  537. {
  538. bool found = false;
  539. char* pSemicol = strchr(m_inputBuf, ':');
  540. char* pData = NULL;
  541. if( pSemicol != NULL ) //Split the identifier & the result code (if it exists)
  542. {
  543. *pSemicol = '\0';
  544. pData = pSemicol + 1;
  545. if(pData[0]==' ')
  546. {
  547. pData++; //Suppress whitespace
  548. }
  549. }
  550. //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
  551. m_eventsProcessingMtx.lock();
  552. //Go through the list
  553. for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
  554. {
  555. if( m_eventsHandlers[i] != NULL )
  556. {
  557. if( m_eventsHandlers[i]->isATCodeHandled(m_inputBuf) )
  558. {
  559. m_eventsHandlers[i]->onEvent(m_inputBuf, pData);
  560. found = true; //Do not break here as there might be multiple handlers for one event type
  561. }
  562. }
  563. }
  564. m_eventsProcessingMtx.unlock();
  565. if(found)
  566. {
  567. return OK;
  568. }
  569. }
  570. if(m_transactionState == READING_RESULT)
  571. {
  572. //The following lines can either be a command response or a result code (OK / ERROR / CONNECT / +CME ERROR: %s / +CMS ERROR: %s)
  573. if(strcmp("OK", m_inputBuf) == 0)
  574. {
  575. DBG("OK result received");
  576. m_transactionResult.code = 0;
  577. m_transactionResult.result = ATResult::AT_OK;
  578. m_transactionState = IDLE;
  579. int* msg = m_AT2Env.alloc(osWaitForever);
  580. *msg = AT_RESULT_READY;
  581. m_AT2Env.put(msg); //Command has been processed
  582. return OK;
  583. }
  584. else if(strcmp("ERROR", m_inputBuf) == 0)
  585. {
  586. DBG("ERROR result received");
  587. m_transactionResult.code = 0;
  588. m_transactionResult.result = ATResult::AT_ERROR;
  589. m_transactionState = IDLE;
  590. int* msg = m_AT2Env.alloc(osWaitForever);
  591. *msg = AT_RESULT_READY;
  592. m_AT2Env.put(msg); //Command has been processed
  593. return OK;
  594. }
  595. else if(strncmp("CONNECT", m_inputBuf, 7 /*=strlen("CONNECT")*/) == 0) //Result can be "CONNECT" or "CONNECT %d", indicating baudrate
  596. {
  597. DBG("CONNECT result received");
  598. m_transactionResult.code = 0;
  599. m_transactionResult.result = ATResult::AT_CONNECT;
  600. m_transactionState = IDLE;
  601. int* msg = m_AT2Env.alloc(osWaitForever);
  602. *msg = AT_RESULT_READY;
  603. m_AT2Env.put(msg); //Command has been processed
  604. return OK;
  605. }
  606. else if(strcmp("COMMAND NOT SUPPORT", m_inputBuf) == 0) //Huawei-specific, not normalized
  607. {
  608. DBG("COMMAND NOT SUPPORT result received");
  609. m_transactionResult.code = 0;
  610. m_transactionResult.result = ATResult::AT_ERROR;
  611. m_transactionState = IDLE;
  612. int* msg = m_AT2Env.alloc(osWaitForever);
  613. *msg = AT_RESULT_READY;
  614. m_AT2Env.put(msg); //Command has been processed
  615. return OK;
  616. }
  617. else if(strstr(m_inputBuf, "+CME ERROR:") == m_inputBuf) //Mobile Equipment Error
  618. {
  619. std::sscanf(m_inputBuf + 12 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code);
  620. DBG("+CME ERROR: %d result received", m_transactionResult.code);
  621. m_transactionResult.result = ATResult::AT_CME_ERROR;
  622. m_transactionState = IDLE;
  623. int* msg = m_AT2Env.alloc(osWaitForever);
  624. *msg = AT_RESULT_READY;
  625. m_AT2Env.put(msg); //Command has been processed
  626. return OK;
  627. }
  628. else if(strstr(m_inputBuf, "+CMS ERROR:") == m_inputBuf) //SIM Error
  629. {
  630. std::sscanf(m_inputBuf + 13 /* =strlen("+CME ERROR: ") */, "%d", &m_transactionResult.code);
  631. DBG("+CMS ERROR: %d result received", m_transactionResult.code);
  632. m_transactionResult.result = ATResult::AT_CMS_ERROR;
  633. m_transactionState = IDLE;
  634. int* msg = m_AT2Env.alloc(osWaitForever);
  635. *msg = AT_RESULT_READY;
  636. m_AT2Env.put(msg); //Command has been processed
  637. return OK;
  638. }
  639. else
  640. {
  641. DBG("Unprocessed result received: '%s'", m_inputBuf);
  642. //Must call transaction processor to complete line processing
  643. int ret = m_pTransactionProcessor->onNewATResponseLine(this, m_inputBuf); //Here sendData can be called
  644. return ret;
  645. }
  646. }
  647. return OK;
  648. }
  649. int ATCommandsInterface::processEntryPrompt()
  650. {
  651. DBG("Calling prompt handler");
  652. int ret = m_pTransactionProcessor->onNewEntryPrompt(this); //Here sendData can be called
  653. if( ret != NET_MOREINFO ) //A new prompt is expected
  654. {
  655. DBG("Sending break character");
  656. //Send CTRL+Z (break sequence) to exit prompt
  657. char seq[2] = {BRK, 0x00};
  658. sendData(seq);
  659. }
  660. return OK;
  661. }
  662. //This will be called on initialization & after the execution of a command
  663. void ATCommandsInterface::enableEvents()
  664. {
  665. //Advertize this to events handlers
  666. m_eventsMgmtMtx.lock();
  667. for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
  668. {
  669. if( m_eventsHandlers[i] != NULL )
  670. {
  671. m_eventsHandlers[i]->onDispatchStart();
  672. //Enable this kind of events
  673. const char* cmd = m_eventsHandlers[i]->getEventsEnableCommand();
  674. if(cmd != NULL)
  675. {
  676. int ret = executeInternal(cmd, this, NULL); //Execute enable command
  677. if(ret)
  678. {
  679. WARN("Events enabling command \"%s\" failed", cmd);
  680. }
  681. }
  682. }
  683. }
  684. m_eventsMgmtMtx.unlock();
  685. }
  686. //This will be called on de-initialization & before the execution of a command to prevent unsollicited result codes from polluting the results
  687. void ATCommandsInterface::disableEvents()
  688. {
  689. //Advertize this to events handlers
  690. m_eventsMgmtMtx.lock();
  691. for(int i = 0; i < MAX_AT_EVENTS_HANDLERS; i++) //Find a free slot
  692. {
  693. if( m_eventsHandlers[i] != NULL )
  694. {
  695. m_eventsHandlers[i]->onDispatchStart();
  696. //Disable this kind of events
  697. const char* cmd = m_eventsHandlers[i]->getEventsDisableCommand();
  698. if(cmd != NULL)
  699. {
  700. int ret = executeInternal(cmd, this, NULL); //Execute disable command
  701. if(ret)
  702. {
  703. WARN("Events disabling command \"%s\" failed", cmd);
  704. }
  705. }
  706. }
  707. }
  708. m_eventsMgmtMtx.unlock();
  709. }
  710. //Commands that can be called during onNewATResponseLine callback, additionally to close()
  711. //Access to this method is protected (can ONLY be called on processing thread during IATCommandsProcessor::onNewATResponseLine execution)
  712. int ATCommandsInterface::sendData(const char* data)
  713. {
  714. //m_inputBuf is cleared at this point (and MUST therefore be empty)
  715. int dataLen = strlen(data);
  716. DBG("Sending raw string of length %d", dataLen);
  717. int ret = m_pStream->write((uint8_t*)data, dataLen, osWaitForever);
  718. if(ret)
  719. {
  720. WARN("Could not write to stream (returned %d)", ret);
  721. return ret;
  722. }
  723. int dataPos = 0;
  724. do
  725. {
  726. //Read echo
  727. size_t readLen;
  728. 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
  729. if(ret)
  730. {
  731. WARN("Could not read from stream (returned %d)", ret);
  732. m_inputPos = 0; //Reset input buffer state
  733. m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
  734. return ret;
  735. }
  736. if( memcmp(m_inputBuf, data + dataPos, readLen) != 0 )
  737. {
  738. //Echo does not match output
  739. m_inputBuf[readLen] = '\0';
  740. WARN("Echo does not match output, got '%s' instead", m_inputBuf);
  741. m_inputPos = 0; //Reset input buffer state
  742. m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
  743. return NET_DIFF;
  744. }
  745. dataPos += readLen;
  746. //If all characters have not been read yet
  747. } while(dataPos < dataLen);
  748. DBG("String sent successfully");
  749. m_inputPos = 0; //Reset input buffer state
  750. m_inputBuf[0] = '\0'; //Always have a null-terminating char at start of buffer
  751. return OK;
  752. }
  753. /*static*/ void ATCommandsInterface::staticCallback(void const* p)
  754. {
  755. ((ATCommandsInterface*)p)->process();
  756. }
  757. int ATCommandsInterface::ATResultToReturnCode(ATResult result) //Helper
  758. {
  759. if(result.result == ATResult::AT_OK)
  760. {
  761. return OK;
  762. }
  763. else
  764. {
  765. return NET_MOREINFO;
  766. }
  767. }
  768. /*virtual*/ int ATCommandsInterface::onNewATResponseLine(ATCommandsInterface* pInst, const char* line) //Default implementation for simple commands handling
  769. {
  770. return OK;
  771. }
  772. /*virtual*/ int ATCommandsInterface::onNewEntryPrompt(ATCommandsInterface* pInst) //Default implementation (just sends Ctrl+Z to exit the prompt by returning OK right-away)
  773. {
  774. return OK;
  775. }
  776. void ATCommandsInterface::process() //Processing thread
  777. {
  778. DBG("AT Thread started");
  779. while(true)
  780. {
  781. DBG("AT Processing on hold");
  782. m_processingThread.signal_wait(AT_SIG_PROCESSING_START); //Block until the process is started
  783. m_processingMtx.lock();
  784. DBG("AT Processing started");
  785. //First of all discard buffer
  786. int ret;
  787. size_t readLen;
  788. do //Drop everything
  789. {
  790. ret = m_pStream->read((uint8_t*)m_inputBuf, &readLen, AT_INPUT_BUF_SIZE - 1, 0); //Do NOT wait at this point
  791. } while(ret == OK);
  792. m_inputPos = 0; //Clear input buffer
  793. do
  794. {
  795. DBG("Trying to send a pending command");
  796. trySendCommand(); //This must be tried first as we discarded the buffer before and therefore would be blocking though there is a pending command
  797. DBG("Trying to read a new line");
  798. tryReadLine();
  799. } while( m_processingThread.signal_wait(AT_SIG_PROCESSING_STOP, 0).status != osEventSignal ); //Loop until the process is interrupted
  800. m_processingMtx.unlock();
  801. DBG("AT Processing stopped");
  802. }
  803. }