blob: 407a204ef54ac35dddfd301b7d80ec71bfe134e6 [file] [log] [blame]
bohu952dde92018-06-09 08:20:14 -07001/* //device/system/reference-ril/atchannel.c
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "atchannel.h"
19#include "at_tok.h"
20
21#include <stdio.h>
22#include <string.h>
23#include <pthread.h>
24#include <ctype.h>
Bjoern Johanssondf74cb42018-08-23 16:43:53 -070025#include <poll.h>
bohu952dde92018-06-09 08:20:14 -070026#include <stdlib.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <sys/time.h>
30#include <time.h>
31#include <unistd.h>
32
33#define LOG_NDEBUG 0
34#define LOG_TAG "AT"
35#include <utils/Log.h>
36
37#include "misc.h"
38
39
40#define NUM_ELEMS(x) (sizeof(x)/sizeof((x)[0]))
41
42#define MAX_AT_RESPONSE (8 * 1024)
43#define HANDSHAKE_RETRY_COUNT 8
44#define HANDSHAKE_TIMEOUT_MSEC 250
45
46static pthread_t s_tid_reader;
47static int s_fd = -1; /* fd of the AT channel */
48static ATUnsolHandler s_unsolHandler;
49
50/* for input buffering */
51
52static char s_ATBuffer[MAX_AT_RESPONSE+1];
53static char *s_ATBufferCur = s_ATBuffer;
54
55#if AT_DEBUG
56void AT_DUMP(const char* prefix, const char* buff, int len)
57{
58 if (len < 0)
59 len = strlen(buff);
60 RLOGD("%.*s", len, buff);
61}
62#endif
63
64/*
65 * There is one reader thread |s_tid_reader| and potentially multiple writer
66 * threads. |s_commandmutex| and |s_commandcond| are used to maintain the
67 * condition that the writer thread will not read from |sp_response| until the
68 * reader thread has signaled itself is finished, etc. |s_writeMutex| is used to
69 * prevent multiple writer threads from calling at_send_command_full_nolock
70 * function at the same time.
71 */
72
73static pthread_mutex_t s_commandmutex = PTHREAD_MUTEX_INITIALIZER;
74static pthread_cond_t s_commandcond = PTHREAD_COND_INITIALIZER;
75static pthread_mutex_t s_writeMutex = PTHREAD_MUTEX_INITIALIZER;
76
77static ATCommandType s_type;
78static const char *s_responsePrefix = NULL;
79static const char *s_smsPDU = NULL;
80static ATResponse *sp_response = NULL;
81
82static void (*s_onTimeout)(void) = NULL;
83static void (*s_onReaderClosed)(void) = NULL;
84static int s_readerClosed;
85
86static void onReaderClosed();
87static int writeCtrlZ (const char *s);
88static int writeline (const char *s);
89
90#define NS_PER_S 1000000000
91static void setTimespecRelative(struct timespec *p_ts, long long msec)
92{
93 struct timeval tv;
94
95 gettimeofday(&tv, (struct timezone *) NULL);
96
97 p_ts->tv_sec = tv.tv_sec + (msec / 1000);
98 p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000L ) * 1000L;
99 /* assuming tv.tv_usec < 10^6 */
100 if (p_ts->tv_nsec >= NS_PER_S) {
101 p_ts->tv_sec++;
102 p_ts->tv_nsec -= NS_PER_S;
103 }
104}
105
106static void sleepMsec(long long msec)
107{
108 struct timespec ts;
109 int err;
110
111 ts.tv_sec = (msec / 1000);
112 ts.tv_nsec = (msec % 1000) * 1000 * 1000;
113
114 do {
115 err = nanosleep (&ts, &ts);
116 } while (err < 0 && errno == EINTR);
117}
118
119
120
121/** add an intermediate response to sp_response*/
122static void addIntermediate(const char *line)
123{
124 ATLine *p_new;
125
126 p_new = (ATLine *) malloc(sizeof(ATLine));
127
128 p_new->line = strdup(line);
129
130 /* note: this adds to the head of the list, so the list
131 will be in reverse order of lines received. the order is flipped
132 again before passing on to the command issuer */
133 p_new->p_next = sp_response->p_intermediates;
134 sp_response->p_intermediates = p_new;
135}
136
137
138/**
139 * returns 1 if line is a final response indicating error
140 * See 27.007 annex B
141 * WARNING: NO CARRIER and others are sometimes unsolicited
142 */
143static const char * s_finalResponsesError[] = {
144 "ERROR",
145 "+CMS ERROR:",
146 "+CME ERROR:",
147 "NO CARRIER", /* sometimes! */
148 "NO ANSWER",
149 "NO DIALTONE",
150};
151static int isFinalResponseError(const char *line)
152{
153 size_t i;
154
155 for (i = 0 ; i < NUM_ELEMS(s_finalResponsesError) ; i++) {
156 if (strStartsWith(line, s_finalResponsesError[i])) {
157 return 1;
158 }
159 }
160
161 return 0;
162}
163
164/**
165 * returns 1 if line is a final response indicating success
166 * See 27.007 annex B
167 * WARNING: NO CARRIER and others are sometimes unsolicited
168 */
169static const char * s_finalResponsesSuccess[] = {
170 "OK",
171 "CONNECT" /* some stacks start up data on another channel */
172};
173static int isFinalResponseSuccess(const char *line)
174{
175 size_t i;
176
177 for (i = 0 ; i < NUM_ELEMS(s_finalResponsesSuccess) ; i++) {
178 if (strStartsWith(line, s_finalResponsesSuccess[i])) {
179 return 1;
180 }
181 }
182
183 return 0;
184}
185
186/**
187 * returns 1 if line is a final response, either error or success
188 * See 27.007 annex B
189 * WARNING: NO CARRIER and others are sometimes unsolicited
190 */
191static int isFinalResponse(const char *line)
192{
193 return isFinalResponseSuccess(line) || isFinalResponseError(line);
194}
195
196
197/**
198 * returns 1 if line is the first line in (what will be) a two-line
199 * SMS unsolicited response
200 */
201static const char * s_smsUnsoliciteds[] = {
202 "+CMT:",
203 "+CDS:",
204 "+CBM:"
205};
206static int isSMSUnsolicited(const char *line)
207{
208 size_t i;
209
210 for (i = 0 ; i < NUM_ELEMS(s_smsUnsoliciteds) ; i++) {
211 if (strStartsWith(line, s_smsUnsoliciteds[i])) {
212 return 1;
213 }
214 }
215
216 return 0;
217}
218
219
220/** assumes s_commandmutex is held */
221static void handleFinalResponse(const char *line)
222{
223 sp_response->finalResponse = strdup(line);
224
225 pthread_cond_signal(&s_commandcond);
226}
227
228static void handleUnsolicited(const char *line)
229{
230 if (s_unsolHandler != NULL) {
231 s_unsolHandler(line, NULL);
232 }
233}
234
235static void processLine(const char *line)
236{
237 pthread_mutex_lock(&s_commandmutex);
238
239 if (sp_response == NULL) {
240 /* no command pending */
241 handleUnsolicited(line);
242 } else if (isFinalResponseSuccess(line)) {
243 sp_response->success = 1;
244 handleFinalResponse(line);
245 } else if (isFinalResponseError(line)) {
246 sp_response->success = 0;
247 handleFinalResponse(line);
248 } else if (s_smsPDU != NULL && 0 == strcmp(line, "> ")) {
249 // See eg. TS 27.005 4.3
250 // Commands like AT+CMGS have a "> " prompt
251 writeCtrlZ(s_smsPDU);
252 s_smsPDU = NULL;
253 } else switch (s_type) {
254 case NO_RESULT:
255 handleUnsolicited(line);
256 break;
257 case NUMERIC:
258 if (sp_response->p_intermediates == NULL
259 && isdigit(line[0])
260 ) {
261 addIntermediate(line);
262 } else {
263 /* either we already have an intermediate response or
264 the line doesn't begin with a digit */
265 handleUnsolicited(line);
266 }
267 break;
268 case SINGLELINE:
269 if (sp_response->p_intermediates == NULL
270 && strStartsWith (line, s_responsePrefix)
271 ) {
272 addIntermediate(line);
273 } else {
274 /* we already have an intermediate response */
275 handleUnsolicited(line);
276 }
277 break;
278 case MULTILINE:
279 if (strStartsWith (line, s_responsePrefix)) {
280 addIntermediate(line);
281 } else {
282 handleUnsolicited(line);
283 }
284 break;
285
286 default: /* this should never be reached */
287 RLOGE("Unsupported AT command type %d\n", s_type);
288 handleUnsolicited(line);
289 break;
290 }
291
292 pthread_mutex_unlock(&s_commandmutex);
293}
294
295
296/**
297 * Returns a pointer to the end of the next line
298 * special-cases the "> " SMS prompt
299 *
300 * returns NULL if there is no complete line
301 */
302static char * findNextEOL(char *cur)
303{
304 if (cur[0] == '>' && cur[1] == ' ' && cur[2] == '\0') {
305 /* SMS prompt character...not \r terminated */
306 return cur+2;
307 }
308
309 // Find next newline
310 while (*cur != '\0' && *cur != '\r' && *cur != '\n') cur++;
311
312 return *cur == '\0' ? NULL : cur;
313}
314
315
316/**
317 * Reads a line from the AT channel, returns NULL on timeout.
318 * Assumes it has exclusive read access to the FD
319 *
320 * This line is valid only until the next call to readline
321 *
322 * This function exists because as of writing, android libc does not
323 * have buffered stdio.
324 */
325
326static const char *readline()
327{
328 ssize_t count;
329
330 char *p_read = NULL;
331 char *p_eol = NULL;
332 char *ret;
333
334 /* this is a little odd. I use *s_ATBufferCur == 0 to
335 * mean "buffer consumed completely". If it points to a character, than
336 * the buffer continues until a \0
337 */
338 if (*s_ATBufferCur == '\0') {
339 /* empty buffer */
340 s_ATBufferCur = s_ATBuffer;
341 *s_ATBufferCur = '\0';
342 p_read = s_ATBuffer;
343 } else { /* *s_ATBufferCur != '\0' */
344 /* there's data in the buffer from the last read */
345
346 // skip over leading newlines
347 while (*s_ATBufferCur == '\r' || *s_ATBufferCur == '\n')
348 s_ATBufferCur++;
349
350 p_eol = findNextEOL(s_ATBufferCur);
351
352 if (p_eol == NULL) {
353 /* a partial line. move it up and prepare to read more */
354 size_t len;
355
356 len = strlen(s_ATBufferCur);
357
358 memmove(s_ATBuffer, s_ATBufferCur, len + 1);
359 p_read = s_ATBuffer + len;
360 s_ATBufferCur = s_ATBuffer;
361 }
362 /* Otherwise, (p_eol !- NULL) there is a complete line */
363 /* that will be returned the while () loop below */
364 }
365
366 while (p_eol == NULL) {
367 if (0 == MAX_AT_RESPONSE - (p_read - s_ATBuffer)) {
368 RLOGE("ERROR: Input line exceeded buffer\n");
369 /* ditch buffer and start over again */
370 s_ATBufferCur = s_ATBuffer;
371 *s_ATBufferCur = '\0';
372 p_read = s_ATBuffer;
373 }
374
375 do {
376 count = read(s_fd, p_read,
377 MAX_AT_RESPONSE - (p_read - s_ATBuffer));
378 } while (count < 0 && errno == EINTR);
379
380 if (count > 0) {
381 AT_DUMP( "<< ", p_read, count );
382
383 p_read[count] = '\0';
384
385 // skip over leading newlines
386 while (*s_ATBufferCur == '\r' || *s_ATBufferCur == '\n')
387 s_ATBufferCur++;
388
389 p_eol = findNextEOL(s_ATBufferCur);
390 p_read += count;
391 } else if (count <= 0) {
392 /* read error encountered or EOF reached */
393 if(count == 0) {
394 RLOGD("atchannel: EOF reached");
395 } else {
396 RLOGD("atchannel: read error %s", strerror(errno));
397 }
398 return NULL;
399 }
400 }
401
402 /* a full line in the buffer. Place a \0 over the \r and return */
403
404 ret = s_ATBufferCur;
405 *p_eol = '\0';
406 s_ATBufferCur = p_eol + 1; /* this will always be <= p_read, */
407 /* and there will be a \0 at *p_read */
408
409 RLOGD("AT< %s\n", ret);
410 return ret;
411}
412
413
414static void onReaderClosed()
415{
416 if (s_onReaderClosed != NULL && s_readerClosed == 0) {
417
418 pthread_mutex_lock(&s_commandmutex);
419
420 s_readerClosed = 1;
421
422 pthread_cond_signal(&s_commandcond);
423
424 pthread_mutex_unlock(&s_commandmutex);
425
426 s_onReaderClosed();
427 }
428}
429
430
431static void *readerLoop(void *arg __unused)
432{
433 for (;;) {
434 const char * line;
435
436 line = readline();
437
438 if (line == NULL) {
439 break;
440 }
441
442 if(isSMSUnsolicited(line)) {
443 char *line1;
444 const char *line2;
445
446 // The scope of string returned by 'readline()' is valid only
447 // till next call to 'readline()' hence making a copy of line
448 // before calling readline again.
449 line1 = strdup(line);
450 line2 = readline();
451
452 if (line2 == NULL) {
453 free(line1);
454 break;
455 }
456
457 if (s_unsolHandler != NULL) {
458 s_unsolHandler (line1, line2);
459 }
460 free(line1);
461 } else {
462 processLine(line);
463 }
464 }
465
466 onReaderClosed();
467
468 return NULL;
469}
470
471/**
472 * Sends string s to the radio with a \r appended.
473 * Returns AT_ERROR_* on error, 0 on success
474 *
475 * This function exists because as of writing, android libc does not
476 * have buffered stdio.
477 */
478static int writeline (const char *s)
479{
480 size_t cur = 0;
481 size_t len = strlen(s);
482 ssize_t written;
483
484 if (s_fd < 0 || s_readerClosed > 0) {
485 return AT_ERROR_CHANNEL_CLOSED;
486 }
487
488 RLOGD("AT> %s\n", s);
489
490 AT_DUMP( ">> ", s, strlen(s) );
491
492 /* the main string */
493 while (cur < len) {
494 do {
495 written = write (s_fd, s + cur, len - cur);
496 } while (written < 0 && errno == EINTR);
497
498 if (written < 0) {
499 return AT_ERROR_GENERIC;
500 }
501
502 cur += written;
503 }
504
505 /* the \r */
506
507 do {
508 written = write (s_fd, "\r" , 1);
509 } while ((written < 0 && errno == EINTR) || (written == 0));
510
511 if (written < 0) {
512 return AT_ERROR_GENERIC;
513 }
514
515 return 0;
516}
517static int writeCtrlZ (const char *s)
518{
519 size_t cur = 0;
520 size_t len = strlen(s);
521 ssize_t written;
522
523 if (s_fd < 0 || s_readerClosed > 0) {
524 return AT_ERROR_CHANNEL_CLOSED;
525 }
526
527 RLOGD("AT> %s^Z\n", s);
528
529 AT_DUMP( ">* ", s, strlen(s) );
530
531 /* the main string */
532 while (cur < len) {
533 do {
534 written = write (s_fd, s + cur, len - cur);
535 } while (written < 0 && errno == EINTR);
536
537 if (written < 0) {
538 return AT_ERROR_GENERIC;
539 }
540
541 cur += written;
542 }
543
544 /* the ^Z */
545
546 do {
547 written = write (s_fd, "\032" , 1);
548 } while ((written < 0 && errno == EINTR) || (written == 0));
549
550 if (written < 0) {
551 return AT_ERROR_GENERIC;
552 }
553
554 return 0;
555}
556
557static void clearPendingCommand()
558{
559 if (sp_response != NULL) {
560 at_response_free(sp_response);
561 }
562
563 sp_response = NULL;
564 s_responsePrefix = NULL;
565 s_smsPDU = NULL;
566}
567
568
569/**
570 * Starts AT handler on stream "fd'
571 * returns 0 on success, -1 on error
572 */
573int at_open(int fd, ATUnsolHandler h)
574{
575 int ret;
576 pthread_t tid;
577 pthread_attr_t attr;
578
579 s_fd = fd;
580 s_unsolHandler = h;
581 s_readerClosed = 0;
582
583 s_responsePrefix = NULL;
584 s_smsPDU = NULL;
585 sp_response = NULL;
586
587 pthread_attr_init (&attr);
588 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
589
590 ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);
591
592 if (ret < 0) {
593 perror ("pthread_create");
594 return -1;
595 }
596
597
598 return 0;
599}
600
601/* FIXME is it ok to call this from the reader and the command thread? */
602void at_close()
603{
604 if (s_fd >= 0) {
605 close(s_fd);
606 }
607 s_fd = -1;
608
609 pthread_mutex_lock(&s_commandmutex);
610
611 s_readerClosed = 1;
612
613 pthread_cond_signal(&s_commandcond);
614
615 pthread_mutex_unlock(&s_commandmutex);
616
617 /* the reader thread should eventually die */
618}
619
620static ATResponse * at_response_new()
621{
622 return (ATResponse *) calloc(1, sizeof(ATResponse));
623}
624
625void at_response_free(ATResponse *p_response)
626{
627 ATLine *p_line;
628
629 if (p_response == NULL) return;
630
631 p_line = p_response->p_intermediates;
632
633 while (p_line != NULL) {
634 ATLine *p_toFree;
635
636 p_toFree = p_line;
637 p_line = p_line->p_next;
638
639 free(p_toFree->line);
640 free(p_toFree);
641 }
642
643 free (p_response->finalResponse);
644 free (p_response);
645}
646
647/**
648 * The line reader places the intermediate responses in reverse order
649 * here we flip them back
650 */
651static void reverseIntermediates(ATResponse *p_response)
652{
653 ATLine *pcur,*pnext;
654
655 pcur = p_response->p_intermediates;
656 p_response->p_intermediates = NULL;
657
658 while (pcur != NULL) {
659 pnext = pcur->p_next;
660 pcur->p_next = p_response->p_intermediates;
661 p_response->p_intermediates = pcur;
662 pcur = pnext;
663 }
664}
665
666/**
667 * Internal send_command implementation
668 * Doesn't lock or call the timeout callback
669 *
670 * timeoutMsec == 0 means infinite timeout
671 */
672
673static int at_send_command_full_nolock (const char *command, ATCommandType type,
674 const char *responsePrefix, const char *smspdu,
675 long long timeoutMsec, ATResponse **pp_outResponse)
676{
677 int err = 0;
678 struct timespec ts;
679
680 if(sp_response != NULL) {
681 err = AT_ERROR_COMMAND_PENDING;
682 goto error;
683 }
684
685 err = writeline (command);
686
687 if (err < 0) {
688 goto error;
689 }
690
691 s_type = type;
692 s_responsePrefix = responsePrefix;
693 s_smsPDU = smspdu;
694 sp_response = at_response_new();
695
696 if (timeoutMsec != 0) {
697 setTimespecRelative(&ts, timeoutMsec);
698 }
699
700 while (sp_response->finalResponse == NULL && s_readerClosed == 0) {
701 if (timeoutMsec != 0) {
702 err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts);
703 } else {
704 err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
705 }
706
707 if (err == ETIMEDOUT) {
708 err = AT_ERROR_TIMEOUT;
709 goto error;
710 }
711 }
712
713 if (pp_outResponse == NULL) {
714 at_response_free(sp_response);
715 } else {
716 /* line reader stores intermediate responses in reverse order */
717 reverseIntermediates(sp_response);
718 *pp_outResponse = sp_response;
719 }
720
721 sp_response = NULL;
722
723 if(s_readerClosed > 0) {
724 err = AT_ERROR_CHANNEL_CLOSED;
725 goto error;
726 }
727
728 err = 0;
729error:
730 clearPendingCommand();
731
732 return err;
733}
734
735/**
736 * Internal send_command implementation
737 *
738 * timeoutMsec == 0 means infinite timeout
739 */
740static int at_send_command_full (const char *command, ATCommandType type,
741 const char *responsePrefix, const char *smspdu,
742 long long timeoutMsec, ATResponse **pp_outResponse)
743{
744 int err;
745 bool inEmulator;
746
747 if (0 != pthread_equal(s_tid_reader, pthread_self())) {
748 /* cannot be called from reader thread */
749 return AT_ERROR_INVALID_THREAD;
750 }
751 inEmulator = isInEmulator();
752 if (inEmulator) {
753 pthread_mutex_lock(&s_writeMutex);
754 }
755 pthread_mutex_lock(&s_commandmutex);
756
757 err = at_send_command_full_nolock(command, type,
758 responsePrefix, smspdu,
759 timeoutMsec, pp_outResponse);
760
761 pthread_mutex_unlock(&s_commandmutex);
762 if (inEmulator) {
763 pthread_mutex_unlock(&s_writeMutex);
764 }
765
766 if (err == AT_ERROR_TIMEOUT && s_onTimeout != NULL) {
767 s_onTimeout();
768 }
769
770 return err;
771}
772
773
774/**
775 * Issue a single normal AT command with no intermediate response expected
776 *
777 * "command" should not include \r
778 * pp_outResponse can be NULL
779 *
780 * if non-NULL, the resulting ATResponse * must be eventually freed with
781 * at_response_free
782 */
783int at_send_command (const char *command, ATResponse **pp_outResponse)
784{
785 int err;
786
787 err = at_send_command_full (command, NO_RESULT, NULL,
788 NULL, 0, pp_outResponse);
789
790 return err;
791}
792
793
794int at_send_command_singleline (const char *command,
795 const char *responsePrefix,
796 ATResponse **pp_outResponse)
797{
798 int err;
799
800 err = at_send_command_full (command, SINGLELINE, responsePrefix,
801 NULL, 0, pp_outResponse);
802
803 if (err == 0 && pp_outResponse != NULL
804 && (*pp_outResponse)->success > 0
805 && (*pp_outResponse)->p_intermediates == NULL
806 ) {
807 /* successful command must have an intermediate response */
808 at_response_free(*pp_outResponse);
809 *pp_outResponse = NULL;
810 return AT_ERROR_INVALID_RESPONSE;
811 }
812
813 return err;
814}
815
816
817int at_send_command_numeric (const char *command,
818 ATResponse **pp_outResponse)
819{
820 int err;
821
822 err = at_send_command_full (command, NUMERIC, NULL,
823 NULL, 0, pp_outResponse);
824
825 if (err == 0 && pp_outResponse != NULL
826 && (*pp_outResponse)->success > 0
827 && (*pp_outResponse)->p_intermediates == NULL
828 ) {
829 /* successful command must have an intermediate response */
830 at_response_free(*pp_outResponse);
831 *pp_outResponse = NULL;
832 return AT_ERROR_INVALID_RESPONSE;
833 }
834
835 return err;
836}
837
838
839int at_send_command_sms (const char *command,
840 const char *pdu,
841 const char *responsePrefix,
842 ATResponse **pp_outResponse)
843{
844 int err;
845
846 err = at_send_command_full (command, SINGLELINE, responsePrefix,
847 pdu, 0, pp_outResponse);
848
849 if (err == 0 && pp_outResponse != NULL
850 && (*pp_outResponse)->success > 0
851 && (*pp_outResponse)->p_intermediates == NULL
852 ) {
853 /* successful command must have an intermediate response */
854 at_response_free(*pp_outResponse);
855 *pp_outResponse = NULL;
856 return AT_ERROR_INVALID_RESPONSE;
857 }
858
859 return err;
860}
861
862
863int at_send_command_multiline (const char *command,
864 const char *responsePrefix,
865 ATResponse **pp_outResponse)
866{
867 int err;
868
869 err = at_send_command_full (command, MULTILINE, responsePrefix,
870 NULL, 0, pp_outResponse);
871
872 return err;
873}
874
875
876/** This callback is invoked on the command thread */
877void at_set_on_timeout(void (*onTimeout)(void))
878{
879 s_onTimeout = onTimeout;
880}
881
882/**
883 * This callback is invoked on the reader thread (like ATUnsolHandler)
884 * when the input stream closes before you call at_close
885 * (not when you call at_close())
886 * You should still call at_close()
887 */
888
889void at_set_on_reader_closed(void (*onClose)(void))
890{
891 s_onReaderClosed = onClose;
892}
893
894
895/**
896 * Periodically issue an AT command and wait for a response.
897 * Used to ensure channel has start up and is active
898 */
899
900int at_handshake()
901{
902 int i;
903 int err = 0;
904 bool inEmulator;
905
906 if (0 != pthread_equal(s_tid_reader, pthread_self())) {
907 /* cannot be called from reader thread */
908 return AT_ERROR_INVALID_THREAD;
909 }
910 inEmulator = isInEmulator();
911 if (inEmulator) {
912 pthread_mutex_lock(&s_writeMutex);
913 }
914 pthread_mutex_lock(&s_commandmutex);
915
916 for (i = 0 ; i < HANDSHAKE_RETRY_COUNT ; i++) {
917 /* some stacks start with verbose off */
918 err = at_send_command_full_nolock ("ATE0Q0V1", NO_RESULT,
919 NULL, NULL, HANDSHAKE_TIMEOUT_MSEC, NULL);
920
921 if (err == 0) {
922 break;
923 }
924 }
925
926 if (err == 0) {
927 /* pause for a bit to let the input buffer drain any unmatched OK's
928 (they will appear as extraneous unsolicited responses) */
929
930 sleepMsec(HANDSHAKE_TIMEOUT_MSEC);
931 }
932
933 pthread_mutex_unlock(&s_commandmutex);
934 if (inEmulator) {
935 pthread_mutex_unlock(&s_writeMutex);
936 }
937
938 return err;
939}
940
941/**
942 * Returns error code from response
943 * Assumes AT+CMEE=1 (numeric) mode
944 */
945AT_CME_Error at_get_cme_error(const ATResponse *p_response)
946{
947 int ret;
948 int err;
949 char *p_cur;
950
951 if (p_response->success > 0) {
952 return CME_SUCCESS;
953 }
954
955 if (p_response->finalResponse == NULL
956 || !strStartsWith(p_response->finalResponse, "+CME ERROR:")
957 ) {
958 return CME_ERROR_NON_CME;
959 }
960
961 p_cur = p_response->finalResponse;
962 err = at_tok_start(&p_cur);
963
964 if (err < 0) {
965 return CME_ERROR_NON_CME;
966 }
967
968 err = at_tok_nextint(&p_cur, &ret);
969
970 if (err < 0) {
971 return CME_ERROR_NON_CME;
972 }
973
974 return (AT_CME_Error) ret;
975}
976