blob: 410ad0e92253c08a473228aead59980d4d489bbf [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
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);
Steve Block20cd1142011-12-20 20:47:51 +000072 ALOGD("%.*s", len, buff);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -080073}
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 LOGE("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 LOGE("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) {
Steve Block20cd1142011-12-20 20:47:51 +0000401 ALOGD("atchannel: EOF reached");
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800402 } else {
Steve Block20cd1142011-12-20 20:47:51 +0000403 ALOGD("atchannel: read error %s", strerror(errno));
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800404 }
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
Steve Block20cd1142011-12-20 20:47:51 +0000416 ALOGD("AT< %s\n", ret);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800417 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
Steve Block20cd1142011-12-20 20:47:51 +0000502 ALOGD("AT> %s\n", s);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800503
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
Steve Block20cd1142011-12-20 20:47:51 +0000541 ALOGD("AT> %s^Z\n", s);
The Android Open Source Project00f06fc2009-03-03 19:32:15 -0800542
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