OSDN Git Service

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