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