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