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
AT_DUMP(const char * prefix,const char * buff,int len)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
setTimespecRelative(struct timespec * p_ts,long long msec)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
sleepMsec(long long msec)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*/
addIntermediate(const char * line)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 };
isFinalResponseError(const char * line)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 };
isFinalResponseSuccess(const char * line)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 */
isFinalResponse(const char * line)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 };
isSMSUnsolicited(const char * line)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 */
handleFinalResponse(const char * line)218 static void handleFinalResponse(const char *line)
219 {
220 sp_response->finalResponse = strdup(line);
221
222 pthread_cond_signal(&s_commandcond);
223 }
224
handleUnsolicited(const char * line)225 static void handleUnsolicited(const char *line)
226 {
227 if (s_unsolHandler != NULL) {
228 s_unsolHandler(line, NULL);
229 }
230 }
231
processLine(const char * line)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 */
findNextEOL(char * cur)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
readline()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
onReaderClosed()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
readerLoop(void * arg)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 */
writeline(const char * s)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 }
writeCtrlZ(const char * s)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
clearPendingCommand()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 */
at_open(int fd,ATUnsolHandler h)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? */
at_close()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
at_response_new()616 static ATResponse * at_response_new()
617 {
618 return (ATResponse *) calloc(1, sizeof(ATResponse));
619 }
620
at_response_free(ATResponse * p_response)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 */
reverseIntermediates(ATResponse * p_response)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
at_send_command_full_nolock(const char * command,ATCommandType type,const char * responsePrefix,const char * smspdu,long long timeoutMsec,ATResponse ** pp_outResponse)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 */
at_send_command_full(const char * command,ATCommandType type,const char * responsePrefix,const char * smspdu,long long timeoutMsec,ATResponse ** pp_outResponse)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 */
at_send_command(const char * command,ATResponse ** pp_outResponse)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
at_send_command_singleline(const char * command,const char * responsePrefix,ATResponse ** pp_outResponse)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
at_send_command_numeric(const char * command,ATResponse ** pp_outResponse)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
at_send_command_sms(const char * command,const char * pdu,const char * responsePrefix,ATResponse ** pp_outResponse)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
at_send_command_multiline(const char * command,const char * responsePrefix,ATResponse ** pp_outResponse)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 */
at_set_on_timeout(void (* onTimeout)(void))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
at_set_on_reader_closed(void (* onClose)(void))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
at_handshake()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 */
at_get_cme_error(const ATResponse * p_response)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