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