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