Home | History | Annotate | Download | only in reference-ril
      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 
     49 static pthread_t s_tid_reader;
     50 static int s_fd = -1;    /* fd of the AT channel */
     51 static ATUnsolHandler s_unsolHandler;
     52 
     53 /* for input buffering */
     54 
     55 static char s_ATBuffer[MAX_AT_RESPONSE+1];
     56 static char *s_ATBufferCur = s_ATBuffer;
     57 
     58 #if AT_DEBUG
     59 void  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 
     72 static pthread_mutex_t s_commandmutex = PTHREAD_MUTEX_INITIALIZER;
     73 static pthread_cond_t s_commandcond = PTHREAD_COND_INITIALIZER;
     74 
     75 static ATCommandType s_type;
     76 static const char *s_responsePrefix = NULL;
     77 static const char *s_smsPDU = NULL;
     78 static ATResponse *sp_response = NULL;
     79 
     80 static void (*s_onTimeout)(void) = NULL;
     81 static void (*s_onReaderClosed)(void) = NULL;
     82 static int s_readerClosed;
     83 
     84 static void onReaderClosed();
     85 static int writeCtrlZ (const char *s);
     86 static int writeline (const char *s);
     87 
     88 #ifndef USE_NP
     89 static 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 
    103 static 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*/
    119 static 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  */
    140 static const char * s_finalResponsesError[] = {
    141     "ERROR",
    142     "+CMS ERROR:",
    143     "+CME ERROR:",
    144     "NO CARRIER", /* sometimes! */
    145     "NO ANSWER",
    146     "NO DIALTONE",
    147 };
    148 static 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  */
    166 static const char * s_finalResponsesSuccess[] = {
    167     "OK",
    168     "CONNECT"       /* some stacks start up data on another channel */
    169 };
    170 static 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  */
    188 static 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  */
    198 static const char * s_smsUnsoliciteds[] = {
    199     "+CMT:",
    200     "+CDS:",
    201     "+CBM:"
    202 };
    203 static 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 */
    218 static void handleFinalResponse(const char *line)
    219 {
    220     sp_response->finalResponse = strdup(line);
    221 
    222     pthread_cond_signal(&s_commandcond);
    223 }
    224 
    225 static void handleUnsolicited(const char *line)
    226 {
    227     if (s_unsolHandler != NULL) {
    228         s_unsolHandler(line, NULL);
    229     }
    230 }
    231 
    232 static 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  */
    299 static 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 
    323 static 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 
    411 static 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 
    428 static 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  */
    474 static 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 }
    513 static 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 
    553 static 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  */
    569 int 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? */
    598 void 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 
    616 static ATResponse * at_response_new()
    617 {
    618     return (ATResponse *) calloc(1, sizeof(ATResponse));
    619 }
    620 
    621 void 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  */
    647 static 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 
    669 static 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;
    733 error:
    734     clearPendingCommand();
    735 
    736     return err;
    737 }
    738 
    739 /**
    740  * Internal send_command implementation
    741  *
    742  * timeoutMsec == 0 means infinite timeout
    743  */
    744 static 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  */
    780 int 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 
    791 int 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 
    814 int 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 
    836 int 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 
    860 int 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 */
    874 void 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 
    886 void 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 
    897 int 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  */
    935 AT_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