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
54static pthread_t s_tid_reader;
55static int s_fd = -1;    /* fd of the AT channel */
56static ATUnsolHandler s_unsolHandler;
57
58/* for input buffering */
59
60static char s_ATBuffer[MAX_AT_RESPONSE+1];
61static char *s_ATBufferCur = s_ATBuffer;
62
63static int s_ackPowerIoctl; /* true if TTY has android byte-count
64                                handshake for low power*/
65static int s_readCount = 0;
66
67#if AT_DEBUG
68void  AT_DUMP(const char*  prefix, const char*  buff, int  len)
69{
70    if (len < 0)
71        len = strlen(buff);
72    RLOGD("%.*s", len, buff);
73}
74#endif
75
76/*
77 * for current pending command
78 * these are protected by s_commandmutex
79 */
80
81static pthread_mutex_t s_commandmutex = PTHREAD_MUTEX_INITIALIZER;
82static pthread_cond_t s_commandcond = PTHREAD_COND_INITIALIZER;
83
84static ATCommandType s_type;
85static const char *s_responsePrefix = NULL;
86static const char *s_smsPDU = NULL;
87static ATResponse *sp_response = NULL;
88
89static void (*s_onTimeout)(void) = NULL;
90static void (*s_onReaderClosed)(void) = NULL;
91static int s_readerClosed;
92
93static void onReaderClosed();
94static int writeCtrlZ (const char *s);
95static int writeline (const char *s);
96
97#ifndef USE_NP
98static 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
112static 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*/
128static 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 */
149static const char * s_finalResponsesError[] = {
150    "ERROR",
151    "+CMS ERROR:",
152    "+CME ERROR:",
153    "NO CARRIER", /* sometimes! */
154    "NO ANSWER",
155    "NO DIALTONE",
156};
157static 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 */
175static const char * s_finalResponsesSuccess[] = {
176    "OK",
177    "CONNECT"       /* some stacks start up data on another channel */
178};
179static 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 */
197static 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 */
207static const char * s_smsUnsoliciteds[] = {
208    "+CMT:",
209    "+CDS:",
210    "+CBM:"
211};
212static 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 */
227static void handleFinalResponse(const char *line)
228{
229    sp_response->finalResponse = strdup(line);
230
231    pthread_cond_signal(&s_commandcond);
232}
233
234static void handleUnsolicited(const char *line)
235{
236    if (s_unsolHandler != NULL) {
237        s_unsolHandler(line, NULL);
238    }
239}
240
241static 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            RLOGE("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 */
308static 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
332static 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            RLOGE("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                RLOGD("atchannel: EOF reached");
402            } else {
403                RLOGD("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    RLOGD("AT< %s\n", ret);
417    return ret;
418}
419
420
421static 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
438static 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 */
492static 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    RLOGD("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}
531static 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    RLOGD("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
571static 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 */
587int 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? */
650void 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
668static ATResponse * at_response_new()
669{
670    return (ATResponse *) calloc(1, sizeof(ATResponse));
671}
672
673void 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 */
699static 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
721static 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;
785error:
786    clearPendingCommand();
787
788    return err;
789}
790
791/**
792 * Internal send_command implementation
793 *
794 * timeoutMsec == 0 means infinite timeout
795 */
796static 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 */
832int 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
843int 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
866int 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
888int 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
912int 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 */
926void 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
938void 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
949int 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 */
987AT_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