OSDN Git Service

am eb90dbe8: Merge "Remove references to <linux/omap_csmi.h>."
[android-x86/hardware-ril.git] / reference-ril / atchannel.c
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_ANDROID_OS
39 #define USE_NP 1
40 #endif /* HAVE_ANDROID_OS */
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
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
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
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*/
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 };
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 };
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  */
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 };
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 */
218 static void handleFinalResponse(const char *line)
219 {
220     sp_response->finalResponse = strdup(line);
221
222     pthread_cond_signal(&s_commandcond);
223 }
224
225 static void handleUnsolicited(const char *line)
226 {
227     if (s_unsolHandler != NULL) {
228         s_unsolHandler(line, NULL);
229     }
230 }
231
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  */
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
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
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
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  */
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 }
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
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  */
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? */
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
616 static ATResponse * at_response_new()
617 {
618     return (ATResponse *) calloc(1, sizeof(ATResponse));
619 }
620
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  */
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
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  */
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  */
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
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
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
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
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 */
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
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
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  */
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