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