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