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