Sync froyo ril blob from hyc's codebase.
[xdandroid:rootfs.git] / atchannel.c
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
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
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
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*/
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 };
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 };
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  */
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 };
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 */
227 static void handleFinalResponse(const char *line)
228 {
229     sp_response->finalResponse = strdup(line);
230
231     pthread_cond_signal(&s_commandcond);
232 }
233
234 static void handleUnsolicited(const char *line)
235 {
236     if (s_unsolHandler != NULL) {
237         s_unsolHandler(line, NULL);
238     }
239 }
240
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  */
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
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
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
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  */
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 }
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
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  */
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? */
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
668 static ATResponse * at_response_new()
669 {
670     return (ATResponse *) calloc(1, sizeof(ATResponse));
671 }
672
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  */
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
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  */
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  */
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
843 int at_send_command_timeout (const char *command, long long timeout, ATResponse **pp_outResponse)
844 {
845     int err;
846
847     err = at_send_command_full (command, NO_RESULT, NULL,
848                                     NULL, timeout, pp_outResponse);
849
850     return err;
851 }
852
853
854 int at_send_command_singleline (const char *command,
855                                 const char *responsePrefix,
856                                  ATResponse **pp_outResponse)
857 {
858     int err;
859
860     err = at_send_command_full (command, SINGLELINE, responsePrefix,
861                                     NULL, 0, pp_outResponse);
862
863     if (err == 0 && pp_outResponse != NULL
864         && (*pp_outResponse)->success > 0
865         && (*pp_outResponse)->p_intermediates == NULL
866     ) {
867         /* successful command must have an intermediate response */
868         at_response_free(*pp_outResponse);
869         *pp_outResponse = NULL;
870         return AT_ERROR_INVALID_RESPONSE;
871     }
872
873     return err;
874 }
875
876
877 int at_send_command_numeric (const char *command,
878                                  ATResponse **pp_outResponse)
879 {
880     int err;
881
882     err = at_send_command_full (command, NUMERIC, NULL,
883                                     NULL, 0, pp_outResponse);
884
885     if (err == 0 && pp_outResponse != NULL
886         && (*pp_outResponse)->success > 0
887         && (*pp_outResponse)->p_intermediates == NULL
888     ) {
889         /* successful command must have an intermediate response */
890         at_response_free(*pp_outResponse);
891         *pp_outResponse = NULL;
892         return AT_ERROR_INVALID_RESPONSE;
893     }
894
895     return err;
896 }
897
898
899 int at_send_command_sms (const char *command,
900                                 const char *pdu,
901                                 const char *responsePrefix,
902                                  ATResponse **pp_outResponse)
903 {
904     int err;
905
906     err = at_send_command_full (command, SINGLELINE, responsePrefix,
907                                     pdu, 0, pp_outResponse);
908
909     if (err == 0 && pp_outResponse != NULL
910         && (*pp_outResponse)->success > 0
911         && (*pp_outResponse)->p_intermediates == NULL
912     ) {
913         /* successful command must have an intermediate response */
914         at_response_free(*pp_outResponse);
915         *pp_outResponse = NULL;
916         return AT_ERROR_INVALID_RESPONSE;
917     }
918
919     return err;
920 }
921
922
923 int at_send_command_multiline (const char *command,
924                                 const char *responsePrefix,
925                                  ATResponse **pp_outResponse)
926 {
927     int err;
928
929     err = at_send_command_full (command, MULTILINE, responsePrefix,
930                                     NULL, 0, pp_outResponse);
931
932     return err;
933 }
934
935
936 /** This callback is invoked on the command thread */
937 void at_set_on_timeout(void (*onTimeout)(void))
938 {
939     s_onTimeout = onTimeout;
940 }
941
942 /**
943  *  This callback is invoked on the reader thread (like ATUnsolHandler)
944  *  when the input stream closes before you call at_close
945  *  (not when you call at_close())
946  *  You should still call at_close()
947  */
948
949 void at_set_on_reader_closed(void (*onClose)(void))
950 {
951     s_onReaderClosed = onClose;
952 }
953
954
955 /**
956  * Periodically issue an AT command and wait for a response.
957  * Used to ensure channel has start up and is active
958  */
959
960 int at_handshake()
961 {
962     int i;
963     int err = 0;
964
965     if (0 != pthread_equal(s_tid_reader, pthread_self())) {
966         /* cannot be called from reader thread */
967         return AT_ERROR_INVALID_THREAD;
968     }
969
970     pthread_mutex_lock(&s_commandmutex);
971
972     for (i = 0 ; i < HANDSHAKE_RETRY_COUNT ; i++) {
973         /* some stacks start with verbose off */
974         err = at_send_command_full_nolock ("ATE0Q0V1", NO_RESULT,
975                     NULL, NULL, HANDSHAKE_TIMEOUT_MSEC, NULL);
976
977         if (err == 0) {
978             break;
979         }
980     }
981
982     if (err == 0) {
983         /* pause for a bit to let the input buffer drain any unmatched OK's
984            (they will appear as extraneous unsolicited responses) */
985
986         sleepMsec(HANDSHAKE_TIMEOUT_MSEC);
987     }
988
989     pthread_mutex_unlock(&s_commandmutex);
990
991     return err;
992 }
993
994 /**
995  * Returns error code from response
996  * Assumes AT+CMEE=1 (numeric) mode
997  */
998 AT_CME_Error at_get_cme_error(const ATResponse *p_response)
999 {
1000     int ret;
1001     int err;
1002     char *p_cur;
1003
1004     if (p_response->success > 0) {
1005         return CME_SUCCESS;
1006     }
1007
1008     if (p_response->finalResponse == NULL
1009         || !strStartsWith(p_response->finalResponse, "+CME ERROR:")
1010     ) {
1011         return CME_ERROR_NON_CME;
1012     }
1013
1014     p_cur = p_response->finalResponse;
1015     err = at_tok_start(&p_cur);
1016
1017     if (err < 0) {
1018         return CME_ERROR_NON_CME;
1019     }
1020
1021     err = at_tok_nextint(&p_cur, &ret);
1022
1023     if (err < 0) {
1024         return CME_ERROR_NON_CME;
1025     }
1026
1027     return (AT_CME_Error) ret;
1028 }
1029