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