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