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