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