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