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