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