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