OSDN Git Service

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