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 45static pthread_t s_tid_reader; 46static int s_fd = -1; /* fd of the AT channel */ 47static ATUnsolHandler s_unsolHandler; 48 49/* for input buffering */ 50 51static char s_ATBuffer[MAX_AT_RESPONSE+1]; 52static char *s_ATBufferCur = s_ATBuffer; 53 54#if AT_DEBUG 55void 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 72static pthread_mutex_t s_commandmutex = PTHREAD_MUTEX_INITIALIZER; 73static pthread_cond_t s_commandcond = PTHREAD_COND_INITIALIZER; 74static pthread_mutex_t s_writeMutex = PTHREAD_MUTEX_INITIALIZER; 75 76static ATCommandType s_type; 77static const char *s_responsePrefix = NULL; 78static const char *s_smsPDU = NULL; 79static ATResponse *sp_response = NULL; 80 81static void (*s_onTimeout)(void) = NULL; 82static void (*s_onReaderClosed)(void) = NULL; 83static int s_readerClosed; 84 85static void onReaderClosed(); 86static int writeCtrlZ (const char *s); 87static int writeline (const char *s); 88 89#define NS_PER_S 1000000000 90static 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 105static 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*/ 121static 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 */ 142static const char * s_finalResponsesError[] = { 143 "ERROR", 144 "+CMS ERROR:", 145 "+CME ERROR:", 146 "NO CARRIER", /* sometimes! */ 147 "NO ANSWER", 148 "NO DIALTONE", 149}; 150static 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 */ 168static const char * s_finalResponsesSuccess[] = { 169 "OK", 170 "CONNECT" /* some stacks start up data on another channel */ 171}; 172static 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 */ 190static 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 */ 200static const char * s_smsUnsoliciteds[] = { 201 "+CMT:", 202 "+CDS:", 203 "+CBM:" 204}; 205static 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 */ 220static void handleFinalResponse(const char *line) 221{ 222 sp_response->finalResponse = strdup(line); 223 224 pthread_cond_signal(&s_commandcond); 225} 226 227static void handleUnsolicited(const char *line) 228{ 229 if (s_unsolHandler != NULL) { 230 s_unsolHandler(line, NULL); 231 } 232} 233 234static 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 */ 301static 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 325static 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 413static 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 430static 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 */ 477static 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} 516static 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 556static 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 */ 572int 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? */ 601void 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 619static ATResponse * at_response_new() 620{ 621 return (ATResponse *) calloc(1, sizeof(ATResponse)); 622} 623 624void 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 */ 650static 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 672static 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; 728error: 729 clearPendingCommand(); 730 731 return err; 732} 733 734/** 735 * Internal send_command implementation 736 * 737 * timeoutMsec == 0 means infinite timeout 738 */ 739static 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 bool inEmulator; 745 746 if (0 != pthread_equal(s_tid_reader, pthread_self())) { 747 /* cannot be called from reader thread */ 748 return AT_ERROR_INVALID_THREAD; 749 } 750 inEmulator = isInEmulator(); 751 if (inEmulator) { 752 pthread_mutex_lock(&s_writeMutex); 753 } 754 pthread_mutex_lock(&s_commandmutex); 755 756 err = at_send_command_full_nolock(command, type, 757 responsePrefix, smspdu, 758 timeoutMsec, pp_outResponse); 759 760 pthread_mutex_unlock(&s_commandmutex); 761 if (inEmulator) { 762 pthread_mutex_unlock(&s_writeMutex); 763 } 764 765 if (err == AT_ERROR_TIMEOUT && s_onTimeout != NULL) { 766 s_onTimeout(); 767 } 768 769 return err; 770} 771 772 773/** 774 * Issue a single normal AT command with no intermediate response expected 775 * 776 * "command" should not include \r 777 * pp_outResponse can be NULL 778 * 779 * if non-NULL, the resulting ATResponse * must be eventually freed with 780 * at_response_free 781 */ 782int at_send_command (const char *command, ATResponse **pp_outResponse) 783{ 784 int err; 785 786 err = at_send_command_full (command, NO_RESULT, NULL, 787 NULL, 0, pp_outResponse); 788 789 return err; 790} 791 792 793int at_send_command_singleline (const char *command, 794 const char *responsePrefix, 795 ATResponse **pp_outResponse) 796{ 797 int err; 798 799 err = at_send_command_full (command, SINGLELINE, responsePrefix, 800 NULL, 0, pp_outResponse); 801 802 if (err == 0 && pp_outResponse != NULL 803 && (*pp_outResponse)->success > 0 804 && (*pp_outResponse)->p_intermediates == NULL 805 ) { 806 /* successful command must have an intermediate response */ 807 at_response_free(*pp_outResponse); 808 *pp_outResponse = NULL; 809 return AT_ERROR_INVALID_RESPONSE; 810 } 811 812 return err; 813} 814 815 816int at_send_command_numeric (const char *command, 817 ATResponse **pp_outResponse) 818{ 819 int err; 820 821 err = at_send_command_full (command, NUMERIC, NULL, 822 NULL, 0, pp_outResponse); 823 824 if (err == 0 && pp_outResponse != NULL 825 && (*pp_outResponse)->success > 0 826 && (*pp_outResponse)->p_intermediates == NULL 827 ) { 828 /* successful command must have an intermediate response */ 829 at_response_free(*pp_outResponse); 830 *pp_outResponse = NULL; 831 return AT_ERROR_INVALID_RESPONSE; 832 } 833 834 return err; 835} 836 837 838int at_send_command_sms (const char *command, 839 const char *pdu, 840 const char *responsePrefix, 841 ATResponse **pp_outResponse) 842{ 843 int err; 844 845 err = at_send_command_full (command, SINGLELINE, responsePrefix, 846 pdu, 0, pp_outResponse); 847 848 if (err == 0 && pp_outResponse != NULL 849 && (*pp_outResponse)->success > 0 850 && (*pp_outResponse)->p_intermediates == NULL 851 ) { 852 /* successful command must have an intermediate response */ 853 at_response_free(*pp_outResponse); 854 *pp_outResponse = NULL; 855 return AT_ERROR_INVALID_RESPONSE; 856 } 857 858 return err; 859} 860 861 862int at_send_command_multiline (const char *command, 863 const char *responsePrefix, 864 ATResponse **pp_outResponse) 865{ 866 int err; 867 868 err = at_send_command_full (command, MULTILINE, responsePrefix, 869 NULL, 0, pp_outResponse); 870 871 return err; 872} 873 874 875/** This callback is invoked on the command thread */ 876void at_set_on_timeout(void (*onTimeout)(void)) 877{ 878 s_onTimeout = onTimeout; 879} 880 881/** 882 * This callback is invoked on the reader thread (like ATUnsolHandler) 883 * when the input stream closes before you call at_close 884 * (not when you call at_close()) 885 * You should still call at_close() 886 */ 887 888void at_set_on_reader_closed(void (*onClose)(void)) 889{ 890 s_onReaderClosed = onClose; 891} 892 893 894/** 895 * Periodically issue an AT command and wait for a response. 896 * Used to ensure channel has start up and is active 897 */ 898 899int at_handshake() 900{ 901 int i; 902 int err = 0; 903 bool inEmulator; 904 905 if (0 != pthread_equal(s_tid_reader, pthread_self())) { 906 /* cannot be called from reader thread */ 907 return AT_ERROR_INVALID_THREAD; 908 } 909 inEmulator = isInEmulator(); 910 if (inEmulator) { 911 pthread_mutex_lock(&s_writeMutex); 912 } 913 pthread_mutex_lock(&s_commandmutex); 914 915 for (i = 0 ; i < HANDSHAKE_RETRY_COUNT ; i++) { 916 /* some stacks start with verbose off */ 917 err = at_send_command_full_nolock ("ATE0Q0V1", NO_RESULT, 918 NULL, NULL, HANDSHAKE_TIMEOUT_MSEC, NULL); 919 920 if (err == 0) { 921 break; 922 } 923 } 924 925 if (err == 0) { 926 /* pause for a bit to let the input buffer drain any unmatched OK's 927 (they will appear as extraneous unsolicited responses) */ 928 929 sleepMsec(HANDSHAKE_TIMEOUT_MSEC); 930 } 931 932 pthread_mutex_unlock(&s_commandmutex); 933 if (inEmulator) { 934 pthread_mutex_unlock(&s_writeMutex); 935 } 936 937 return err; 938} 939 940/** 941 * Returns error code from response 942 * Assumes AT+CMEE=1 (numeric) mode 943 */ 944AT_CME_Error at_get_cme_error(const ATResponse *p_response) 945{ 946 int ret; 947 int err; 948 char *p_cur; 949 950 if (p_response->success > 0) { 951 return CME_SUCCESS; 952 } 953 954 if (p_response->finalResponse == NULL 955 || !strStartsWith(p_response->finalResponse, "+CME ERROR:") 956 ) { 957 return CME_ERROR_NON_CME; 958 } 959 960 p_cur = p_response->finalResponse; 961 err = at_tok_start(&p_cur); 962 963 if (err < 0) { 964 return CME_ERROR_NON_CME; 965 } 966 967 err = at_tok_nextint(&p_cur, &ret); 968 969 if (err < 0) { 970 return CME_ERROR_NON_CME; 971 } 972 973 return (AT_CME_Error) ret; 974} 975 976