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