blob: 00418364ee2704077d67bc9e983e0147e7fbd7da [file] [log] [blame]
The Android Open Source Project00f06fc2009-03-03 19:32:15 -08001/* //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
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080036#include "misc.h"
37
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080038
Chih-Hung Hsieh434554c2016-05-12 10:01:16 -070039#define NUM_ELEMS(x) (sizeof(x)/sizeof((x)[0]))
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080040
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
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080054#if AT_DEBUG
55void AT_DUMP(const char* prefix, const char* buff, int len)
56{
57 if (len < 0)
58 len = strlen(buff);
Wink Saville4dcab4f2012-11-19 16:05:13 -080059 RLOGD("%.*s", len, buff);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080060}
61#endif
62
63/*
Weilun Du9f471e22017-02-07 10:47:19 -080064 * 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.
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080070 */
71
72static pthread_mutex_t s_commandmutex = PTHREAD_MUTEX_INITIALIZER;
73static pthread_cond_t s_commandcond = PTHREAD_COND_INITIALIZER;
Weilun Du9f471e22017-02-07 10:47:19 -080074static pthread_mutex_t s_writeMutex = PTHREAD_MUTEX_INITIALIZER;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080075
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
Jinhui Li11476212016-03-24 08:55:10 +080089#define NS_PER_S 1000000000
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080090static void setTimespecRelative(struct timespec *p_ts, long long msec)
91{
92 struct timeval tv;
93
94 gettimeofday(&tv, (struct timezone *) NULL);
95
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080096 p_ts->tv_sec = tv.tv_sec + (msec / 1000);
97 p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000L ) * 1000L;
Jinhui Li11476212016-03-24 08:55:10 +080098 /* 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 }
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800103}
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800104
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 */
Wink Saville4dcab4f2012-11-19 16:05:13 -0800286 RLOGE("Unsupported AT command type %d\n", s_type);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800287 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)) {
Wink Saville4dcab4f2012-11-19 16:05:13 -0800367 RLOGE("ERROR: Input line exceeded buffer\n");
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800368 /* 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 );
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800381
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) {
Wink Saville4dcab4f2012-11-19 16:05:13 -0800393 RLOGD("atchannel: EOF reached");
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800394 } else {
Wink Saville4dcab4f2012-11-19 16:05:13 -0800395 RLOGD("atchannel: read error %s", strerror(errno));
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800396 }
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
Wink Saville4dcab4f2012-11-19 16:05:13 -0800408 RLOGD("AT< %s\n", ret);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800409 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
Sanket Padawef0c8ca72016-06-30 15:01:08 -0700430static void *readerLoop(void *arg __unused)
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800431{
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) {
Sanket Padawe1fbb3822016-03-09 17:51:57 -0800452 free(line1);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800453 break;
454 }
455
456 if (s_unsolHandler != NULL) {
457 s_unsolHandler (line1, line2);
458 }
459 free(line1);
460 } else {
461 processLine(line);
462 }
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800463 }
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
Wink Saville4dcab4f2012-11-19 16:05:13 -0800487 RLOGD("AT> %s\n", s);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800488
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
Wink Saville4dcab4f2012-11-19 16:05:13 -0800526 RLOGD("AT> %s^Z\n", s);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800527
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
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800586 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;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800677 struct timespec ts;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800678
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
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800695 if (timeoutMsec != 0) {
696 setTimespecRelative(&ts, timeoutMsec);
697 }
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800698
699 while (sp_response->finalResponse == NULL && s_readerClosed == 0) {
700 if (timeoutMsec != 0) {
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800701 err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800702 } 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;
Weilun Du9f471e22017-02-07 10:47:19 -0800744 bool inEmulator;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800745
746 if (0 != pthread_equal(s_tid_reader, pthread_self())) {
747 /* cannot be called from reader thread */
748 return AT_ERROR_INVALID_THREAD;
749 }
Weilun Du9f471e22017-02-07 10:47:19 -0800750 inEmulator = isInEmulator();
751 if (inEmulator) {
752 pthread_mutex_lock(&s_writeMutex);
753 }
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800754 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);
Weilun Du9f471e22017-02-07 10:47:19 -0800761 if (inEmulator) {
762 pthread_mutex_unlock(&s_writeMutex);
763 }
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800764
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;
Weilun Du9f471e22017-02-07 10:47:19 -0800903 bool inEmulator;
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800904
905 if (0 != pthread_equal(s_tid_reader, pthread_self())) {
906 /* cannot be called from reader thread */
907 return AT_ERROR_INVALID_THREAD;
908 }
Weilun Du9f471e22017-02-07 10:47:19 -0800909 inEmulator = isInEmulator();
910 if (inEmulator) {
911 pthread_mutex_lock(&s_writeMutex);
912 }
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800913 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);
Weilun Du9f471e22017-02-07 10:47:19 -0800933 if (inEmulator) {
934 pthread_mutex_unlock(&s_writeMutex);
935 }
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800936
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