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