blob: 80f8e6865c4c13dc44c0468f639c080c9e7221db [file] [log] [blame]
b.liu87afc4c2024-08-14 17:33:45 +08001/* //device/system/reference-ril/atchannel.c
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9** http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#include "atchannel.h"
19#include "at_tok.h"
20
21#include <stdio.h>
22#include <string.h>
23#include <pthread.h>
24#include <ctype.h>
25#include <stdlib.h>
26#include <errno.h>
27#include <fcntl.h>
28#include <sys/time.h>
29#include <time.h>
30#include <unistd.h>
31
32#include "mbtk_log.h"
33#include "mbtk_type.h"
34#include "mbtk_utils.h"
35
36#define MAX_AT_RESPONSE (8 * 1024)
37#define HANDSHAKE_RETRY_COUNT 20
38#define HANDSHAKE_TIMEOUT_MSEC 500
b.liu15f456b2024-10-31 20:16:06 +080039#define AT_BUFF_MAX 1024
b.liu87afc4c2024-08-14 17:33:45 +080040
b.liub171c9a2024-11-12 19:23:29 +080041static pthread_t s_tid_reader[ATPORTTYPE_NUM];
42static int s_at_fd[ATPORTTYPE_NUM] = {-1}; /* fd of the AT channel */
b.liu87afc4c2024-08-14 17:33:45 +080043static int s_uart_fd = -1; /* fd of the UART channel */
44
45static ATUnsolHandler s_unsolHandler;
46
47/* for input buffering */
48
b.liub171c9a2024-11-12 19:23:29 +080049static char s_ATBuffer[ATPORTTYPE_NUM][MAX_AT_RESPONSE+1];
50static char *s_ATBufferCur[ATPORTTYPE_NUM] = {s_ATBuffer[ATPORTTYPE_0], s_ATBuffer[ATPORTTYPE_1], s_ATBuffer[ATPORTTYPE_2]};
b.liu87afc4c2024-08-14 17:33:45 +080051static char s_UartBuffer[MAX_AT_RESPONSE+1];
52static char *s_UartBufferCur = s_UartBuffer;
53
b.liub171c9a2024-11-12 19:23:29 +080054static mbtk_ril_at_state_enum at_state[ATPORTTYPE_NUM] = {RIL_AT_STATE_CLOSED};
b.liu87afc4c2024-08-14 17:33:45 +080055
56#if AT_DEBUG
57void AT_DUMP(const char* prefix, const char* buff, int len)
58{
59 if (len < 0)
60 len = strlen(buff);
61 LOGD("%.*s", len, buff);
62}
63#endif
64
65/*
66 * There is one reader thread |s_tid_reader| and potentially multiple writer
67 * threads. |s_commandmutex| and |s_commandcond| are used to maintain the
68 * condition that the writer thread will not read from |sp_response| until the
69 * reader thread has signaled itself is finished, etc. |s_writeMutex| is used to
70 * prevent multiple writer threads from calling at_send_command_full_nolock
71 * function at the same time.
72 */
73
74// "Wait" when AT process...
b.liub171c9a2024-11-12 19:23:29 +080075static pthread_mutex_t s_commandmutex[ATPORTTYPE_NUM] = {PTHREAD_MUTEX_INITIALIZER};
76static pthread_cond_t s_commandcond[ATPORTTYPE_NUM] = {PTHREAD_COND_INITIALIZER};
b.liu87afc4c2024-08-14 17:33:45 +080077
b.liub171c9a2024-11-12 19:23:29 +080078static ATCommandType s_type[ATPORTTYPE_NUM];
79static const char *s_responsePrefix[ATPORTTYPE_NUM] = {NULL};
80static const char *s_smsPDU[ATPORTTYPE_NUM] = {NULL};
81static ATResponse *sp_response[ATPORTTYPE_NUM] = {NULL};
82static char s_curr_at[ATPORTTYPE_NUM][AT_BUFF_MAX];
b.liu87afc4c2024-08-14 17:33:45 +080083
84static void (*s_onTimeout)(void) = NULL;
85static void (*s_onReaderClosed)(void) = NULL;
86static int s_readerClosed;
87
b.liub171c9a2024-11-12 19:23:29 +080088static void onReaderClosed(ATPortType_enum port);
89static int writeCtrlZ (ATPortType_enum port, const char *s);
90static int writeline (ATPortType_enum port, const char *s);
b.liu87afc4c2024-08-14 17:33:45 +080091
92typedef struct
93{
94 char *at_command;
95 long long timeout; // ms
96 bool timeout_close; // Close AT or not while AT response timeout.
97} at_timeout_t;
98
99static at_timeout_t at_timeout_list[] =
100{
101 {"AT+CRSM", 10000, false},
102// {"AT+COPS", 60000, false}
103};
104
105#define NS_PER_S 1000000000
106static void setTimespecRelative(struct timespec *p_ts, long long msec)
107{
108 struct timeval tv;
109
110 gettimeofday(&tv, (struct timezone *) NULL);
111
112 p_ts->tv_sec = tv.tv_sec + (msec / 1000);
113 p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000L ) * 1000L;
114 /* assuming tv.tv_usec < 10^6 */
115 if (p_ts->tv_nsec >= NS_PER_S)
116 {
117 p_ts->tv_sec++;
118 p_ts->tv_nsec -= NS_PER_S;
119 }
120}
121
122static void sleepMsec(long long msec)
123{
124 struct timespec ts;
125 int err;
126
127 ts.tv_sec = (msec / 1000);
128 ts.tv_nsec = (msec % 1000) * 1000 * 1000;
129
130 do
131 {
132 err = nanosleep (&ts, &ts);
133 }
134 while (err < 0 && errno == EINTR);
135}
136
137
138
139/** add an intermediate response to sp_response*/
b.liub171c9a2024-11-12 19:23:29 +0800140static void addIntermediate(ATPortType_enum port, const char *line)
b.liu87afc4c2024-08-14 17:33:45 +0800141{
142 ATLine *p_new;
143
144 p_new = (ATLine *) malloc(sizeof(ATLine));
145
146 p_new->line = strdup(line);
147
148// LOGD("line:%s", line);
149// LOGD("line-1:%s", p_new->line);
150
151 /* note: this adds to the head of the list, so the list
152 will be in reverse order of lines received. the order is flipped
153 again before passing on to the command issuer */
b.liub171c9a2024-11-12 19:23:29 +0800154 p_new->p_next = sp_response[port]->p_intermediates;
155 sp_response[port]->p_intermediates = p_new;
b.liu87afc4c2024-08-14 17:33:45 +0800156}
157
158
159/**
160 * returns 1 if line is a final response indicating error
161 * See 27.007 annex B
162 * WARNING: NO CARRIER and others are sometimes unsolicited
163 */
164static const char * s_finalResponsesError[] =
165{
166 "ERROR",
167 "+CMS ERROR:",
168 "+CME ERROR:",
169// "NO CARRIER", /* sometimes! */ // Only for ATD ?
170 "NO ANSWER",
171 "NO DIALTONE",
172};
b.liub171c9a2024-11-12 19:23:29 +0800173static int isFinalResponseError(ATPortType_enum port, const char *line)
b.liu87afc4c2024-08-14 17:33:45 +0800174{
175 size_t i;
176
177 for (i = 0 ; i < ARRAY_SIZE(s_finalResponsesError) ; i++)
178 {
179 if (strStartsWith(line, s_finalResponsesError[i]))
180 {
181 return 1;
182 }
183 }
184
b.liub171c9a2024-11-12 19:23:29 +0800185 if(!strncasecmp(s_curr_at[port], "ATD", 3) && strStartsWith(line, "NO CARRIER"))
b.liu87afc4c2024-08-14 17:33:45 +0800186 {
187 return 1;
188 }
189
190 return 0;
191}
192
193/**
194 * returns 1 if line is a final response indicating success
195 * See 27.007 annex B
196 * WARNING: NO CARRIER and others are sometimes unsolicited
197 */
198static const char * s_finalResponsesSuccess[] =
199{
200 "OK",
201// "CONNECT" /* some stacks start up data on another channel */
202};
203static int isFinalResponseSuccess(const char *line)
204{
205 size_t i;
206
207 for (i = 0 ; i < ARRAY_SIZE(s_finalResponsesSuccess) ; i++)
208 {
209 if (strStartsWith(line, s_finalResponsesSuccess[i]))
210 {
211 return 1;
212 }
213 }
214
215 return 0;
216}
217
218/**
219 * returns 1 if line is a final response, either error or success
220 * See 27.007 annex B
221 * WARNING: NO CARRIER and others are sometimes unsolicited
222 */
b.liub171c9a2024-11-12 19:23:29 +0800223static int isFinalResponse(ATPortType_enum port, const char *line)
b.liu87afc4c2024-08-14 17:33:45 +0800224{
b.liub171c9a2024-11-12 19:23:29 +0800225 return isFinalResponseSuccess(line) || isFinalResponseError(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800226}
227
228/**
229 * returns 1 if line is the first line in (what will be) a two-line
230 * SMS unsolicited response
231 */
232static const char * s_smsUnsoliciteds[] =
233{
234 "+CMT:",
235 "+CDS:",
236 "+CBM:"
237};
238static int isSMSUnsolicited(const char *line)
239{
240 size_t i;
241
242 for (i = 0 ; i < ARRAY_SIZE(s_smsUnsoliciteds) ; i++)
243 {
244 if (strStartsWith(line, s_smsUnsoliciteds[i]))
245 {
246 return 1;
247 }
248 }
249
250 return 0;
251}
252
253
254/** assumes s_commandmutex is held */
b.liub171c9a2024-11-12 19:23:29 +0800255static void handleFinalResponse(ATPortType_enum port, const char *line)
b.liu87afc4c2024-08-14 17:33:45 +0800256{
b.liub171c9a2024-11-12 19:23:29 +0800257 sp_response[port]->finalResponse = strdup(line);
b.liu87afc4c2024-08-14 17:33:45 +0800258
259 //LOGD("AT complete (pthread_cond_signal): %s",line);
b.liub171c9a2024-11-12 19:23:29 +0800260 pthread_cond_signal(&s_commandcond[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800261}
262
263static void handleUnsolicited(const char *line)
264{
265 if (s_unsolHandler != NULL)
266 {
267 s_unsolHandler(line, NULL);
268 }
269}
270
b.liub171c9a2024-11-12 19:23:29 +0800271static void processLine(ATPortType_enum port, const char *line)
b.liu87afc4c2024-08-14 17:33:45 +0800272{
b.liub171c9a2024-11-12 19:23:29 +0800273 pthread_mutex_lock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800274// LOGD("LINE : %s", line);
b.liub171c9a2024-11-12 19:23:29 +0800275 if (sp_response[port] == NULL)
b.liu87afc4c2024-08-14 17:33:45 +0800276 {
277 /* no command pending */
278 handleUnsolicited(line);
279 }
280 else if (isFinalResponseSuccess(line))
281 {
b.liub171c9a2024-11-12 19:23:29 +0800282 sp_response[port]->success = 1;
283 handleFinalResponse(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800284 }
b.liub171c9a2024-11-12 19:23:29 +0800285 else if (isFinalResponseError(port, line))
b.liu87afc4c2024-08-14 17:33:45 +0800286 {
b.liub171c9a2024-11-12 19:23:29 +0800287 sp_response[port]->success = 0;
288 handleFinalResponse(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800289 }
b.liub171c9a2024-11-12 19:23:29 +0800290 else if (s_smsPDU[port] != NULL && 0 == strcmp(line, "> "))
b.liu87afc4c2024-08-14 17:33:45 +0800291 {
292 // See eg. TS 27.005 4.3
293 // Commands like AT+CMGS have a "> " prompt
b.liub171c9a2024-11-12 19:23:29 +0800294 writeCtrlZ(port, s_smsPDU[port]);
295 s_smsPDU[port] = NULL;
b.liu87afc4c2024-08-14 17:33:45 +0800296 }
b.liub171c9a2024-11-12 19:23:29 +0800297 else switch (s_type[port])
b.liu87afc4c2024-08-14 17:33:45 +0800298 {
299 case NO_RESULT:
300 handleUnsolicited(line);
301 break;
302 case NUMERIC:
b.liub171c9a2024-11-12 19:23:29 +0800303 if (sp_response[port]->p_intermediates == NULL
b.liu87afc4c2024-08-14 17:33:45 +0800304 && isdigit(line[0])
305 )
306 {
b.liub171c9a2024-11-12 19:23:29 +0800307 addIntermediate(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800308 }
309 else
310 {
311 /* either we already have an intermediate response or
312 the line doesn't begin with a digit */
313 handleUnsolicited(line);
314 }
315 break;
316 case SINGLELINE:
b.liub171c9a2024-11-12 19:23:29 +0800317 if (sp_response[port]->p_intermediates == NULL
318 && strStartsWith (line, s_responsePrefix[port])
b.liu87afc4c2024-08-14 17:33:45 +0800319 )
320 {
321 if(*line == '"')
322 {
323 char *line_temp = strdup(line);
b.liudeb8e422024-12-14 17:36:56 +0800324 char *free_ptr = line_temp;
b.liu87afc4c2024-08-14 17:33:45 +0800325 line_temp++;
326 if(strlen(line_temp) > 0)
327 {
328 char *ptr = line_temp + strlen(line_temp) - 1;
329 while(ptr >= line_temp && *ptr == '"')
330 {
331 *ptr = '\0';
332 ptr--;
333 }
334 }
b.liub171c9a2024-11-12 19:23:29 +0800335 addIntermediate(port, line_temp);
b.liudeb8e422024-12-14 17:36:56 +0800336 free(free_ptr);
b.liu87afc4c2024-08-14 17:33:45 +0800337 }
338 else
339 {
b.liub171c9a2024-11-12 19:23:29 +0800340 addIntermediate(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800341 }
342 }
343 else
344 {
345 /* we already have an intermediate response */
346 handleUnsolicited(line);
347 }
348 break;
349 case MULTILINE:
b.liub171c9a2024-11-12 19:23:29 +0800350 if (strStartsWith (line, s_responsePrefix[port]))
b.liu87afc4c2024-08-14 17:33:45 +0800351 {
b.liub171c9a2024-11-12 19:23:29 +0800352 addIntermediate(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800353 }
354 else
355 {
356 handleUnsolicited(line);
357 }
358 break;
359
360 default: /* this should never be reached */
b.liub171c9a2024-11-12 19:23:29 +0800361 LOGE("Unsupported AT command type %d\n", s_type[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800362 handleUnsolicited(line);
363 break;
364 }
365
b.liub171c9a2024-11-12 19:23:29 +0800366 pthread_mutex_unlock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800367}
368
369
370/**
371 * Returns a pointer to the end of the next line
372 * special-cases the "> " SMS prompt
373 *
374 * returns NULL if there is no complete line
375 */
376static char * findNextEOL(char *cur)
377{
378 if (cur[0] == '>' && cur[1] == ' ' && cur[2] == '\0')
379 {
380 /* SMS prompt character...not \r terminated */
381 return cur+2;
382 }
383
384 // Find next newline
385 while (*cur != '\0' && *cur != '\r' && *cur != '\n') cur++;
386
387 return *cur == '\0' ? NULL : cur;
388}
389
390
391/**
392 * Reads a line from the AT channel, returns NULL on timeout.
393 * Assumes it has exclusive read access to the FD
394 *
395 * This line is valid only until the next call to readline
396 *
397 * This function exists because as of writing, android libc does not
398 * have buffered stdio.
399 */
400
b.liub171c9a2024-11-12 19:23:29 +0800401static const char *readline(ATPortType_enum port)
b.liu87afc4c2024-08-14 17:33:45 +0800402{
403 ssize_t count;
404
405 char *p_read = NULL;
406 char *p_eol = NULL;
407 char *ret;
408
409 /* this is a little odd. I use *s_ATBufferCur == 0 to
410 * mean "buffer consumed completely". If it points to a character, than
411 * the buffer continues until a \0
412 */
b.liub171c9a2024-11-12 19:23:29 +0800413 if (*s_ATBufferCur[port] == '\0')
b.liu87afc4c2024-08-14 17:33:45 +0800414 {
415 /* empty buffer */
b.liub171c9a2024-11-12 19:23:29 +0800416 s_ATBufferCur[port] = s_ATBuffer[port];
417 *s_ATBufferCur[port] = '\0';
418 p_read = s_ATBuffer[port];
b.liu87afc4c2024-08-14 17:33:45 +0800419 }
420 else /* *s_ATBufferCur != '\0' */
421 {
422 /* there's data in the buffer from the last read */
423
424 // skip over leading newlines
b.liub171c9a2024-11-12 19:23:29 +0800425 while (*s_ATBufferCur[port] == '\r' || *s_ATBufferCur[port] == '\n')
426 s_ATBufferCur[port]++;
b.liu87afc4c2024-08-14 17:33:45 +0800427
b.liub171c9a2024-11-12 19:23:29 +0800428 p_eol = findNextEOL(s_ATBufferCur[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800429
430 if (p_eol == NULL)
431 {
432 /* a partial line. move it up and prepare to read more */
433 size_t len;
434
b.liub171c9a2024-11-12 19:23:29 +0800435 len = strlen(s_ATBufferCur[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800436
b.liub171c9a2024-11-12 19:23:29 +0800437 memmove(s_ATBuffer[port], s_ATBufferCur[port], len + 1);
438 p_read = s_ATBuffer[port] + len;
439 s_ATBufferCur[port] = s_ATBuffer[port];
b.liu87afc4c2024-08-14 17:33:45 +0800440 }
441 /* Otherwise, (p_eol !- NULL) there is a complete line */
442 /* that will be returned the while () loop below */
443 }
444
445 while (p_eol == NULL)
446 {
b.liub171c9a2024-11-12 19:23:29 +0800447 if (0 == MAX_AT_RESPONSE - (p_read - s_ATBuffer[port]))
b.liu87afc4c2024-08-14 17:33:45 +0800448 {
449 LOGE("ERROR: Input line exceeded buffer\n");
450 /* ditch buffer and start over again */
b.liub171c9a2024-11-12 19:23:29 +0800451 s_ATBufferCur[port] = s_ATBuffer[port];
452 *s_ATBufferCur[port] = '\0';
453 p_read = s_ATBuffer[port];
b.liu87afc4c2024-08-14 17:33:45 +0800454 }
455
456 do
457 {
b.liub171c9a2024-11-12 19:23:29 +0800458 count = read(s_at_fd[port], p_read,
459 MAX_AT_RESPONSE - (p_read - s_ATBuffer[port]));
b.liu87afc4c2024-08-14 17:33:45 +0800460 usleep(10000);
461 }
462 while (count < 0 && errno == EINTR);
463
464 if (count > 0)
465 {
466 AT_DUMP( "<< ", p_read, count );
467
468 p_read[count] = '\0';
469
470 // skip over leading newlines
b.liub171c9a2024-11-12 19:23:29 +0800471 while (*s_ATBufferCur[port] == '\r' || *s_ATBufferCur[port] == '\n')
472 s_ATBufferCur[port]++;
b.liu87afc4c2024-08-14 17:33:45 +0800473
b.liub171c9a2024-11-12 19:23:29 +0800474 p_eol = findNextEOL(s_ATBufferCur[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800475 p_read += count;
476 }
477 else if (count <= 0)
478 {
479 /* read error encountered or EOF reached */
480 if(count == 0)
481 {
b.liufd87baf2024-11-15 15:30:38 +0800482 LOGD("atchannel[Port-%d]: EOF reached", port);
b.liu87afc4c2024-08-14 17:33:45 +0800483 }
484 else
485 {
b.liufd87baf2024-11-15 15:30:38 +0800486 LOGD("atchannel[Port-%d]: read error %s", port, strerror(errno));
b.liu87afc4c2024-08-14 17:33:45 +0800487 }
488 return NULL;
489 }
490 }
491
492 /* a full line in the buffer. Place a \0 over the \r and return */
493
b.liub171c9a2024-11-12 19:23:29 +0800494 ret = s_ATBufferCur[port];
b.liu87afc4c2024-08-14 17:33:45 +0800495 *p_eol = '\0';
b.liub171c9a2024-11-12 19:23:29 +0800496 s_ATBufferCur[port] = p_eol + 1; /* this will always be <= p_read, */
b.liu87afc4c2024-08-14 17:33:45 +0800497 /* and there will be a \0 at *p_read */
498
b.liufd87baf2024-11-15 15:30:38 +0800499 LOGD("[Port-%d]AT< %s", port, ret);
b.liu87afc4c2024-08-14 17:33:45 +0800500 return ret;
501}
502
b.liufd87baf2024-11-15 15:30:38 +0800503static const char *readlineUrc(ATPortType_enum port)
b.liu87afc4c2024-08-14 17:33:45 +0800504{
505 ssize_t count;
506
507 char *p_read = NULL;
508 char *p_eol = NULL;
509 char *ret;
510
511 /* this is a little odd. I use *s_ATBufferCur == 0 to
512 * mean "buffer consumed completely". If it points to a character, than
513 * the buffer continues until a \0
514 */
515 if (*s_UartBufferCur == '\0')
516 {
517 /* empty buffer */
518 s_UartBufferCur = s_UartBuffer;
519 *s_UartBufferCur = '\0';
520 p_read = s_UartBuffer;
521 }
522 else /* *s_ATBufferCur != '\0' */
523 {
524 /* there's data in the buffer from the last read */
525
526 // skip over leading newlines
527 while (*s_UartBufferCur == '\r' || *s_UartBufferCur == '\n')
528 s_UartBufferCur++;
529
530 p_eol = findNextEOL(s_UartBufferCur);
531
532 if (p_eol == NULL)
533 {
534 /* a partial line. move it up and prepare to read more */
535 size_t len;
536
537 len = strlen(s_UartBufferCur);
538
539 memmove(s_UartBuffer, s_UartBufferCur, len + 1);
540 p_read = s_UartBuffer + len;
541 s_UartBufferCur = s_UartBuffer;
542 }
543 /* Otherwise, (p_eol !- NULL) there is a complete line */
544 /* that will be returned the while () loop below */
545 }
546
547 while (p_eol == NULL)
548 {
549 if (0 == MAX_AT_RESPONSE - (p_read - s_UartBuffer))
550 {
551 LOGE("ERROR: Input line exceeded buffer\n");
552 /* ditch buffer and start over again */
553 s_UartBufferCur = s_UartBuffer;
554 *s_UartBufferCur = '\0';
555 p_read = s_UartBuffer;
556 }
557
558 do
559 {
560 count = read(s_uart_fd, p_read,
561 MAX_AT_RESPONSE - (p_read - s_UartBuffer));
562 usleep(10000);
563 }
564 while (count < 0 && errno == EINTR);
565
566 if (count > 0)
567 {
568 AT_DUMP( "<< ", p_read, count );
569
570 p_read[count] = '\0';
571
572 // skip over leading newlines
573 while (*s_UartBufferCur == '\r' || *s_UartBufferCur == '\n')
574 s_UartBufferCur++;
575
576 p_eol = findNextEOL(s_UartBufferCur);
577 p_read += count;
578 }
579 else if (count <= 0)
580 {
581 /* read error encountered or EOF reached */
582 if(count == 0)
583 {
584 LOGD("atchannel: EOF reached");
585 }
586 else
587 {
588 LOGD("atchannel: read error %s", strerror(errno));
589 }
590 return NULL;
591 }
592 }
593
594 /* a full line in the buffer. Place a \0 over the \r and return */
595
596 ret = s_UartBufferCur;
597 *p_eol = '\0';
598 s_UartBufferCur = p_eol + 1; /* this will always be <= p_read, */
599 /* and there will be a \0 at *p_read */
600
b.liufd87baf2024-11-15 15:30:38 +0800601 LOGD("[Port-%d]URC< %s", port, ret);
b.liu87afc4c2024-08-14 17:33:45 +0800602 return ret;
603}
604
605
606
b.liub171c9a2024-11-12 19:23:29 +0800607static void onReaderClosed(ATPortType_enum port)
b.liu87afc4c2024-08-14 17:33:45 +0800608{
609 LOGD("onReaderClosed()");
610 if (s_onReaderClosed != NULL && s_readerClosed == 0)
611 {
612
b.liub171c9a2024-11-12 19:23:29 +0800613 pthread_mutex_lock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800614
615 s_readerClosed = 1;
616
b.liub171c9a2024-11-12 19:23:29 +0800617 pthread_cond_signal(&s_commandcond[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800618
b.liub171c9a2024-11-12 19:23:29 +0800619 pthread_mutex_unlock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800620
621 s_onReaderClosed();
622 }
623}
624
b.liu06559f62024-11-01 18:48:22 +0800625typedef struct
626{
627 int cid;
628 bool act;
629 bool waitting;
630} info_cgact_wait_t;
631extern info_cgact_wait_t cgact_wait;
632
b.liu87afc4c2024-08-14 17:33:45 +0800633static void *readerLoop(void *arg)
634{
635 UNUSED(arg);
b.liub171c9a2024-11-12 19:23:29 +0800636 ATPortType_enum *port = (ATPortType_enum*)arg;
b.liu87afc4c2024-08-14 17:33:45 +0800637 for (;;)
638 {
639 const char * line;
640
b.liub171c9a2024-11-12 19:23:29 +0800641 line = readline(*port);
b.liu87afc4c2024-08-14 17:33:45 +0800642
643 if (line == NULL)
644 {
b.liufd87baf2024-11-15 15:30:38 +0800645 //usleep(50000);
646 //continue;
b.liu87afc4c2024-08-14 17:33:45 +0800647 break;
648 }
649
650 if(strStartsWith(line, "MBTK_AT_READY")) {
651 //handleUnsolicited(line);
652 continue;
b.liu06559f62024-11-01 18:48:22 +0800653 } else if(strStartsWith(line, "CONNECT")) {
654 if(cgact_wait.waitting && cgact_wait.act) {
655 cgact_wait.waitting = false;
656 }
b.liu87afc4c2024-08-14 17:33:45 +0800657 }
658
659 if(isSMSUnsolicited(line))
660 {
661 char *line1;
662 const char *line2;
663
664 // The scope of string returned by 'readline()' is valid only
665 // till next call to 'readline()' hence making a copy of line
666 // before calling readline again.
667 line1 = strdup(line);
b.liub171c9a2024-11-12 19:23:29 +0800668 line2 = readline(*port);
b.liu87afc4c2024-08-14 17:33:45 +0800669
670 if (line2 == NULL)
671 {
672 free(line1);
673 break;
674 }
675
676 if (s_unsolHandler != NULL)
677 {
678 s_unsolHandler (line1, line2);
679 }
680 free(line1);
681 }
682 else
683 {
b.liub171c9a2024-11-12 19:23:29 +0800684 processLine(*port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800685 }
686 }
687
b.liub171c9a2024-11-12 19:23:29 +0800688 onReaderClosed(*port);
689
690 free(port);
b.liu87afc4c2024-08-14 17:33:45 +0800691
692 return NULL;
693}
694
695static void *readerUrcLoop(void *arg)
696{
697 UNUSED(arg);
b.liub171c9a2024-11-12 19:23:29 +0800698 ATPortType_enum *port = (ATPortType_enum*)arg;
b.liu87afc4c2024-08-14 17:33:45 +0800699 for (;;)
700 {
701 const char *line;
702
b.liufd87baf2024-11-15 15:30:38 +0800703 line = readlineUrc(*port);
b.liu87afc4c2024-08-14 17:33:45 +0800704
705 if (line == NULL)
706 {
707 break;
708 }
709
710 handleUnsolicited(line);
711 }
712
b.liub171c9a2024-11-12 19:23:29 +0800713 onReaderClosed(*port);
714
715 free(port);
b.liu87afc4c2024-08-14 17:33:45 +0800716
717 return NULL;
718}
719
720
721/**
722 * Sends string s to the radio with a \r appended.
723 * Returns AT_ERROR_* on error, 0 on success
724 *
725 * This function exists because as of writing, android libc does not
726 * have buffered stdio.
727 */
b.liub171c9a2024-11-12 19:23:29 +0800728static int writeline (ATPortType_enum port, const char *s)
b.liu87afc4c2024-08-14 17:33:45 +0800729{
730 size_t cur = 0;
731 size_t len = strlen(s);
732 ssize_t written;
733
b.liub171c9a2024-11-12 19:23:29 +0800734 if (s_at_fd[port] < 0 || s_readerClosed > 0)
b.liu87afc4c2024-08-14 17:33:45 +0800735 {
736 return AT_ERROR_CHANNEL_CLOSED;
737 }
738
b.liufd87baf2024-11-15 15:30:38 +0800739 LOGD("[Port-%d]AT> %s", port, s);
b.liu87afc4c2024-08-14 17:33:45 +0800740
741 AT_DUMP( ">> ", s, strlen(s) );
742
b.liub171c9a2024-11-12 19:23:29 +0800743 memset(s_curr_at[port], 0x0, AT_BUFF_MAX);
744 memcpy(s_curr_at[port], s, strlen(s));
b.liu87afc4c2024-08-14 17:33:45 +0800745
746 /* the main string */
747 while (cur < len)
748 {
749 do
750 {
b.liub171c9a2024-11-12 19:23:29 +0800751 written = write (s_at_fd[port], s + cur, len - cur);
b.liu87afc4c2024-08-14 17:33:45 +0800752 }
753 while (written < 0 && errno == EINTR);
754
755 if (written < 0)
756 {
757 return AT_ERROR_GENERIC;
758 }
759
760 cur += written;
761 }
762
763 /* the \r */
764
765 do
766 {
b.liub171c9a2024-11-12 19:23:29 +0800767 written = write (s_at_fd[port], "\r", 1);
b.liu87afc4c2024-08-14 17:33:45 +0800768 }
769 while ((written < 0 && errno == EINTR) || (written == 0));
770
771 if (written < 0)
772 {
773 return AT_ERROR_GENERIC;
774 }
775
776 return 0;
777}
778
b.liub171c9a2024-11-12 19:23:29 +0800779static int writeCtrlZ (ATPortType_enum port, const char *s)
b.liu87afc4c2024-08-14 17:33:45 +0800780{
781 size_t cur = 0;
782 size_t len = strlen(s);
783 ssize_t written;
784
b.liub171c9a2024-11-12 19:23:29 +0800785 if (s_at_fd[port] < 0 || s_readerClosed > 0)
b.liu87afc4c2024-08-14 17:33:45 +0800786 {
787 return AT_ERROR_CHANNEL_CLOSED;
788 }
789
790 LOGD("AT> %s^Z\n", s);
791
792 AT_DUMP( ">* ", s, strlen(s) );
793
794 /* the main string */
795 while (cur < len)
796 {
797 do
798 {
b.liub171c9a2024-11-12 19:23:29 +0800799 written = write (s_at_fd[port], s + cur, len - cur);
b.liu87afc4c2024-08-14 17:33:45 +0800800 }
801 while (written < 0 && errno == EINTR);
802
803 if (written < 0)
804 {
805 return AT_ERROR_GENERIC;
806 }
807
808 cur += written;
809 }
810
811 /* the ^Z */
812
813 do
814 {
b.liub171c9a2024-11-12 19:23:29 +0800815 written = write (s_at_fd[port], "\032", 1);
b.liu87afc4c2024-08-14 17:33:45 +0800816 }
817 while ((written < 0 && errno == EINTR) || (written == 0));
818
819 if (written < 0)
820 {
821 return AT_ERROR_GENERIC;
822 }
823
824 return 0;
825}
826
b.liub171c9a2024-11-12 19:23:29 +0800827static void clearPendingCommand(ATPortType_enum port)
b.liu87afc4c2024-08-14 17:33:45 +0800828{
b.liub171c9a2024-11-12 19:23:29 +0800829 if (sp_response[port] != NULL)
b.liu87afc4c2024-08-14 17:33:45 +0800830 {
b.liub171c9a2024-11-12 19:23:29 +0800831 at_response_free(sp_response[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800832 }
833
b.liub171c9a2024-11-12 19:23:29 +0800834 sp_response[port] = NULL;
835 s_responsePrefix[port] = NULL;
836 s_smsPDU[port] = NULL;
b.liu87afc4c2024-08-14 17:33:45 +0800837}
838
839
840/**
841 * Starts AT handler on stream "fd'
842 * returns 0 on success, -1 on error
843 */
b.liub171c9a2024-11-12 19:23:29 +0800844int at_open(ATPortType_enum port, int at_fd, int uart_fd, ATUnsolHandler h)
b.liu87afc4c2024-08-14 17:33:45 +0800845{
846 int ret;
847 pthread_attr_t attr;
848
b.liub171c9a2024-11-12 19:23:29 +0800849 s_at_fd[port] = at_fd;
b.liu87afc4c2024-08-14 17:33:45 +0800850 s_uart_fd = uart_fd;
851 s_unsolHandler = h;
852 s_readerClosed = 0;
b.liub171c9a2024-11-12 19:23:29 +0800853 s_responsePrefix[port] = NULL;
854 s_smsPDU[port] = NULL;
855 sp_response[port] = NULL;
856
857 ATPortType_enum *at_port_ptr = (ATPortType_enum*)malloc(sizeof(ATPortType_enum));
858 ATPortType_enum *urc_port_ptr = (ATPortType_enum*)malloc(sizeof(ATPortType_enum));
859 *at_port_ptr = port;
860 *urc_port_ptr = port;
b.liu87afc4c2024-08-14 17:33:45 +0800861
862 pthread_attr_init (&attr);
863 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
b.liub171c9a2024-11-12 19:23:29 +0800864 ret = pthread_create(&s_tid_reader[port], &attr, readerLoop, at_port_ptr);
b.liu87afc4c2024-08-14 17:33:45 +0800865 if (ret < 0)
866 {
867 LOGE("AT thread create fail.");
868 return -1;
869 }
870
b.liufd87baf2024-11-15 15:30:38 +0800871 if(port == ATPORTTYPE_0) { // URC only for ATPORTTYPE_0
872 pthread_t uart_tid_reader;
873 ret = pthread_create(&uart_tid_reader, &attr, readerUrcLoop, urc_port_ptr);
874 if (ret < 0)
875 {
876 LOGE("Uart thread create fail.");
877 return -1;
878 }
b.liu87afc4c2024-08-14 17:33:45 +0800879 }
880
881 return 0;
882}
883
884/* FIXME is it ok to call this from the reader and the command thread? */
b.liub171c9a2024-11-12 19:23:29 +0800885void at_close(ATPortType_enum port)
b.liu87afc4c2024-08-14 17:33:45 +0800886{
887 LOGD("at_close()");
b.liub171c9a2024-11-12 19:23:29 +0800888 if (s_at_fd[port] >= 0)
b.liu87afc4c2024-08-14 17:33:45 +0800889 {
b.liub171c9a2024-11-12 19:23:29 +0800890 close(s_at_fd[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800891 }
892 if (s_uart_fd >= 0)
893 {
894 close(s_uart_fd);
895 }
b.liub171c9a2024-11-12 19:23:29 +0800896 s_at_fd[port] = -1;
b.liu87afc4c2024-08-14 17:33:45 +0800897 s_uart_fd = -1;
898
b.liub171c9a2024-11-12 19:23:29 +0800899 pthread_mutex_lock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800900 s_readerClosed = 1;
b.liub171c9a2024-11-12 19:23:29 +0800901 pthread_cond_signal(&s_commandcond[port]);
902 pthread_mutex_unlock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800903 /* the reader thread should eventually die */
904
b.liub171c9a2024-11-12 19:23:29 +0800905 at_state[port] = RIL_AT_STATE_CLOSED;
b.liu87afc4c2024-08-14 17:33:45 +0800906}
907
908static ATResponse * at_response_new()
909{
910 return (ATResponse *) calloc(1, sizeof(ATResponse));
911}
912
913void at_response_free(ATResponse *p_response)
914{
915 ATLine *p_line;
916
917 if (p_response == NULL) return;
918
919 p_line = p_response->p_intermediates;
920
921 while (p_line != NULL)
922 {
923 ATLine *p_toFree;
924
925 p_toFree = p_line;
926 p_line = p_line->p_next;
927
928 free(p_toFree->line);
929 free(p_toFree);
930 }
931
932 free (p_response->finalResponse);
933 free (p_response);
934}
935
936/**
937 * The line reader places the intermediate responses in reverse order
938 * here we flip them back
939 */
940static void reverseIntermediates(ATResponse *p_response)
941{
942 ATLine *pcur,*pnext;
943
944 pcur = p_response->p_intermediates;
945 p_response->p_intermediates = NULL;
946
947 while (pcur != NULL)
948 {
949 pnext = pcur->p_next;
950 pcur->p_next = p_response->p_intermediates;
951 p_response->p_intermediates = pcur;
952 pcur = pnext;
953 }
954}
955
956static long long at_timeout_get(const char *at_command, bool *timeout_close)
957{
958 long long timeout = 0;
959 int i;
960 for(i = 0; i < ARRAY_SIZE(at_timeout_list); i++)
961 {
962 if(!strncasecmp(at_command, at_timeout_list[i].at_command, strlen(at_timeout_list[i].at_command)))
963 {
964 timeout = at_timeout_list[i].timeout;
965 *timeout_close = at_timeout_list[i].timeout_close;
966 break;
967 }
968 }
969
970 return timeout;
971}
972
973/**
974 * Internal send_command implementation
975 * Doesn't lock or call the timeout callback
976 *
977 * timeoutMsec == 0 means infinite timeout
978 */
b.liub171c9a2024-11-12 19:23:29 +0800979static int at_send_command_full_nolock (ATPortType_enum port, const char *command, ATCommandType type,
b.liu87afc4c2024-08-14 17:33:45 +0800980 const char *responsePrefix, const char *smspdu,
981 long long timeoutMsec, ATResponse **pp_outResponse)
982{
983 int err = 0;
984 bool tiemout_close = true;
985 struct timespec ts;
b.liub171c9a2024-11-12 19:23:29 +0800986 if(at_state[port] == RIL_AT_STATE_READY)
987 at_state[port] = RIL_AT_STATE_BUSY;
b.liu87afc4c2024-08-14 17:33:45 +0800988
b.liub171c9a2024-11-12 19:23:29 +0800989 if(sp_response[port] != NULL)
b.liu87afc4c2024-08-14 17:33:45 +0800990 {
991 err = AT_ERROR_COMMAND_PENDING;
992 goto error;
993 }
994
b.liub171c9a2024-11-12 19:23:29 +0800995 err = writeline (port, command);
b.liu87afc4c2024-08-14 17:33:45 +0800996
997 if (err < 0)
998 {
999 goto error;
1000 }
1001
b.liub171c9a2024-11-12 19:23:29 +08001002 s_type[port] = type;
1003 s_responsePrefix[port] = responsePrefix;
1004 s_smsPDU[port] = smspdu;
1005 sp_response[port] = at_response_new();
b.liu87afc4c2024-08-14 17:33:45 +08001006
1007 if(timeoutMsec == 0)
1008 {
1009 timeoutMsec = at_timeout_get(command, &tiemout_close);
1010 }
1011
1012 if (timeoutMsec != 0)
1013 {
1014 setTimespecRelative(&ts, timeoutMsec);
1015 }
1016
b.liub171c9a2024-11-12 19:23:29 +08001017 while (sp_response[port]->finalResponse == NULL && s_readerClosed == 0)
b.liu87afc4c2024-08-14 17:33:45 +08001018 {
1019 //LOGD("AT wait time:%lld",timeoutMsec);
1020 if (timeoutMsec != 0)
1021 {
b.liub171c9a2024-11-12 19:23:29 +08001022 err = pthread_cond_timedwait(&s_commandcond[port], &s_commandmutex[port], &ts);
b.liu87afc4c2024-08-14 17:33:45 +08001023 }
1024 else
1025 {
b.liub171c9a2024-11-12 19:23:29 +08001026 err = pthread_cond_wait(&s_commandcond[port], &s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001027 }
1028
1029 //LOGD("AT continue:err - %d",err);
1030 if (err == ETIMEDOUT)
1031 {
1032 if(tiemout_close)
1033 {
1034 err = AT_ERROR_TIMEOUT_CLOSE;
1035 }
1036 else
1037 {
1038 err = AT_ERROR_TIMEOUT;
1039 }
1040 goto error;
1041 }
1042 }
1043
1044 if (pp_outResponse == NULL)
1045 {
b.liub171c9a2024-11-12 19:23:29 +08001046 at_response_free(sp_response[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001047 }
1048 else
1049 {
1050 /* line reader stores intermediate responses in reverse order */
b.liub171c9a2024-11-12 19:23:29 +08001051 reverseIntermediates(sp_response[port]);
1052 *pp_outResponse = sp_response[port];
b.liu87afc4c2024-08-14 17:33:45 +08001053 }
1054
b.liub171c9a2024-11-12 19:23:29 +08001055 sp_response[port] = NULL;
b.liu87afc4c2024-08-14 17:33:45 +08001056
1057 if(s_readerClosed > 0)
1058 {
1059 err = AT_ERROR_CHANNEL_CLOSED;
1060 goto error;
1061 }
1062
1063 err = 0;
1064error:
b.liub171c9a2024-11-12 19:23:29 +08001065 if(at_state[port] == RIL_AT_STATE_BUSY)
1066 at_state[port] = RIL_AT_STATE_READY;
1067 clearPendingCommand(port);
b.liu87afc4c2024-08-14 17:33:45 +08001068
1069 return err;
1070}
1071
1072/**
1073 * Internal send_command implementation
1074 *
1075 * timeoutMsec == 0 means infinite timeout
1076 */
b.liub171c9a2024-11-12 19:23:29 +08001077static int at_send_command_full (ATPortType_enum port, const char *command, ATCommandType type,
b.liu87afc4c2024-08-14 17:33:45 +08001078 const char *responsePrefix, const char *smspdu,
1079 long long timeoutMsec, ATResponse **pp_outResponse)
1080{
1081 int err;
1082
b.liub171c9a2024-11-12 19:23:29 +08001083 if (0 != pthread_equal(s_tid_reader[port], pthread_self()))
b.liu87afc4c2024-08-14 17:33:45 +08001084 {
1085 /* cannot be called from reader thread */
1086 LOGE("cannot be called from reader thread.");
1087 return AT_ERROR_INVALID_THREAD;
1088 }
1089
1090 // Waitting for previous AT complete.
b.liub171c9a2024-11-12 19:23:29 +08001091 while(at_state[port] == RIL_AT_STATE_BUSY)
b.liu87afc4c2024-08-14 17:33:45 +08001092 {
1093 usleep(10000);
1094 }
1095
b.liub171c9a2024-11-12 19:23:29 +08001096 pthread_mutex_lock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001097
b.liub171c9a2024-11-12 19:23:29 +08001098 err = at_send_command_full_nolock(port, command, type,
b.liu87afc4c2024-08-14 17:33:45 +08001099 responsePrefix, smspdu,
1100 timeoutMsec, pp_outResponse);
1101
b.liub171c9a2024-11-12 19:23:29 +08001102 pthread_mutex_unlock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001103
1104 if (err == AT_ERROR_TIMEOUT_CLOSE && s_onTimeout != NULL)
1105 {
1106 s_onTimeout();
1107 }
1108
1109 return err;
1110}
1111
1112
1113/**
1114 * Issue a single normal AT command with no intermediate response expected
1115 *
1116 * "command" should not include \r
1117 * pp_outResponse can be NULL
1118 *
1119 * if non-NULL, the resulting ATResponse * must be eventually freed with
1120 * at_response_free
1121 */
b.liub171c9a2024-11-12 19:23:29 +08001122int at_send_command (ATPortType_enum port, const char *command, ATResponse **pp_outResponse)
b.liu87afc4c2024-08-14 17:33:45 +08001123{
b.liub171c9a2024-11-12 19:23:29 +08001124 return at_send_command_full (port, command, NO_RESULT, NULL,
b.liu87afc4c2024-08-14 17:33:45 +08001125 NULL, 0, pp_outResponse);
1126}
1127
1128
b.liub171c9a2024-11-12 19:23:29 +08001129int at_send_command_singleline (ATPortType_enum port, const char *command,
b.liu87afc4c2024-08-14 17:33:45 +08001130 const char *responsePrefix,
1131 ATResponse **pp_outResponse)
1132{
1133 int err;
1134
b.liub171c9a2024-11-12 19:23:29 +08001135 err = at_send_command_full (port, command, SINGLELINE, responsePrefix,
b.liu87afc4c2024-08-14 17:33:45 +08001136 NULL, 0, pp_outResponse);
1137
1138 if (err == 0 && pp_outResponse != NULL
1139 && (*pp_outResponse)->success > 0
1140 && (*pp_outResponse)->p_intermediates == NULL
1141 )
1142 {
1143 /* successful command must have an intermediate response */
1144 at_response_free(*pp_outResponse);
1145 *pp_outResponse = NULL;
1146 return AT_ERROR_INVALID_RESPONSE;
1147 }
1148
1149 return err;
1150}
1151
b.liub171c9a2024-11-12 19:23:29 +08001152int at_send_command_singleline_with_timeout (ATPortType_enum port, const char *command,
b.liu87afc4c2024-08-14 17:33:45 +08001153 const char *responsePrefix,
1154 ATResponse **pp_outResponse,long long timeoutMsec)
1155{
1156 int err;
1157
b.liub171c9a2024-11-12 19:23:29 +08001158 err = at_send_command_full (port, command, SINGLELINE, responsePrefix,
b.liu87afc4c2024-08-14 17:33:45 +08001159 NULL, timeoutMsec, pp_outResponse);
1160
1161 if (err == 0 && pp_outResponse != NULL
1162 && (*pp_outResponse)->success > 0
1163 && (*pp_outResponse)->p_intermediates == NULL
1164 )
1165 {
1166 /* successful command must have an intermediate response */
1167 at_response_free(*pp_outResponse);
1168 *pp_outResponse = NULL;
1169 return AT_ERROR_INVALID_RESPONSE;
1170 }
1171
1172 return err;
1173}
1174
1175
1176
b.liub171c9a2024-11-12 19:23:29 +08001177int at_send_command_numeric (ATPortType_enum port, const char *command,
b.liu87afc4c2024-08-14 17:33:45 +08001178 ATResponse **pp_outResponse)
1179{
1180 int err;
1181
b.liub171c9a2024-11-12 19:23:29 +08001182 err = at_send_command_full (port, command, NUMERIC, NULL,
b.liu87afc4c2024-08-14 17:33:45 +08001183 NULL, 0, pp_outResponse);
1184
1185 if (err == 0 && pp_outResponse != NULL
1186 && (*pp_outResponse)->success > 0
1187 && (*pp_outResponse)->p_intermediates == NULL
1188 )
1189 {
1190 /* successful command must have an intermediate response */
1191 at_response_free(*pp_outResponse);
1192 *pp_outResponse = NULL;
1193 return AT_ERROR_INVALID_RESPONSE;
1194 }
1195
1196 return err;
1197}
1198
1199
b.liub171c9a2024-11-12 19:23:29 +08001200int at_send_command_sms (ATPortType_enum port, const char *command,
b.liu87afc4c2024-08-14 17:33:45 +08001201 const char *pdu,
1202 const char *responsePrefix,
1203 ATResponse **pp_outResponse)
1204{
1205 int err;
1206
b.liub171c9a2024-11-12 19:23:29 +08001207 err = at_send_command_full (port, command, SINGLELINE, responsePrefix,
b.liu87afc4c2024-08-14 17:33:45 +08001208 pdu, 0, pp_outResponse);
1209
1210 if (err == 0 && pp_outResponse != NULL
1211 && (*pp_outResponse)->success > 0
1212 && (*pp_outResponse)->p_intermediates == NULL
1213 )
1214 {
1215 /* successful command must have an intermediate response */
1216 at_response_free(*pp_outResponse);
1217 *pp_outResponse = NULL;
1218 return AT_ERROR_INVALID_RESPONSE;
1219 }
1220
1221 return err;
1222}
1223
1224
b.liub171c9a2024-11-12 19:23:29 +08001225int at_send_command_multiline (ATPortType_enum port, const char *command,
b.liu87afc4c2024-08-14 17:33:45 +08001226 const char *responsePrefix,
1227 ATResponse **pp_outResponse)
1228{
1229 int err;
1230
b.liub171c9a2024-11-12 19:23:29 +08001231 err = at_send_command_full (port, command, MULTILINE, responsePrefix,
b.liu87afc4c2024-08-14 17:33:45 +08001232 NULL, 0, pp_outResponse);
1233
1234 return err;
1235}
1236
1237
1238/** This callback is invoked on the command thread */
1239void at_set_on_timeout(void (*onTimeout)(void))
1240{
1241 s_onTimeout = onTimeout;
1242}
1243
1244/**
1245 * This callback is invoked on the reader thread (like ATUnsolHandler)
1246 * when the input stream closes before you call at_close
1247 * (not when you call at_close())
1248 * You should still call at_close()
1249 */
1250
1251void at_set_on_reader_closed(void (*onClose)(void))
1252{
1253 s_onReaderClosed = onClose;
1254}
1255
1256
1257/**
1258 * Periodically issue an AT command and wait for a response.
1259 * Used to ensure channel has start up and is active
1260 */
b.liub171c9a2024-11-12 19:23:29 +08001261int at_handshake(ATPortType_enum port)
b.liu87afc4c2024-08-14 17:33:45 +08001262{
b.liu62240ee2024-11-07 17:52:45 +08001263// int i;
b.liu87afc4c2024-08-14 17:33:45 +08001264 int err = 0;
1265
b.liub171c9a2024-11-12 19:23:29 +08001266 if (0 != pthread_equal(s_tid_reader[port], pthread_self()))
b.liu87afc4c2024-08-14 17:33:45 +08001267 {
1268 /* cannot be called from reader thread */
1269 return AT_ERROR_INVALID_THREAD;
1270 }
b.liub171c9a2024-11-12 19:23:29 +08001271 pthread_mutex_lock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001272
1273#if 0
1274 for (i = 0 ; i < HANDSHAKE_RETRY_COUNT ; i++)
1275 {
1276 /* some stacks start with verbose off */
1277 err = at_send_command_full_nolock("ATE0Q0V1", NO_RESULT,
1278 NULL, NULL, HANDSHAKE_TIMEOUT_MSEC, NULL);
1279
1280 if (err == 0)
1281 {
1282 break;
1283 }
1284 }
1285#else
b.liub171c9a2024-11-12 19:23:29 +08001286 err = at_send_command_full_nolock(port, "ATE0Q0V1", NO_RESULT,
b.liu87afc4c2024-08-14 17:33:45 +08001287 NULL, NULL, 0, NULL);
1288#endif
1289
1290 if (err == 0)
1291 {
1292 /* pause for a bit to let the input buffer drain any unmatched OK's
1293 (they will appear as extraneous unsolicited responses) */
1294 sleepMsec(HANDSHAKE_TIMEOUT_MSEC);
1295 }
1296
b.liub171c9a2024-11-12 19:23:29 +08001297 pthread_mutex_unlock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001298
1299
1300 return err;
1301}
1302
1303/**
1304 * Returns error code from response
1305 * Assumes AT+CMEE=1 (numeric) mode
1306 */
b.liudeb8e422024-12-14 17:36:56 +08001307AT_CME_Error at_get_cme_error(const ATResponse *p_response)
b.liu87afc4c2024-08-14 17:33:45 +08001308{
1309 int ret;
1310 int err;
1311 char *p_cur;
1312
1313 if (p_response->success > 0)
1314 {
1315 return CME_SUCCESS;
1316 }
1317
1318 if (p_response->finalResponse == NULL
1319 || !strStartsWith(p_response->finalResponse, "+CME ERROR:")
1320 )
1321 {
1322 return CME_ERROR_NON_CME;
1323 }
1324
1325 p_cur = p_response->finalResponse;
1326 err = at_tok_start(&p_cur);
1327
1328 if (err < 0)
1329 {
1330 return CME_ERROR_NON_CME;
1331 }
1332
1333 err = at_tok_nextint(&p_cur, &ret);
1334
1335 if (err < 0)
1336 {
1337 return CME_ERROR_NON_CME;
1338 }
1339
1340 return ret;
1341}
1342
b.liub171c9a2024-11-12 19:23:29 +08001343mbtk_ril_at_state_enum at_state_get(ATPortType_enum port)
b.liu87afc4c2024-08-14 17:33:45 +08001344{
b.liub171c9a2024-11-12 19:23:29 +08001345 return at_state[port];
b.liu87afc4c2024-08-14 17:33:45 +08001346}
1347
b.liub171c9a2024-11-12 19:23:29 +08001348void at_state_set(ATPortType_enum port, mbtk_ril_at_state_enum state)
b.liu87afc4c2024-08-14 17:33:45 +08001349{
b.liub171c9a2024-11-12 19:23:29 +08001350 at_state[port] = state;
b.liu87afc4c2024-08-14 17:33:45 +08001351}
1352
1353bool at_rsp_check(ATResponse *p_response)
1354{
1355 if(!p_response || !p_response->success)
1356 return false;
1357
1358 return true;
1359}
1360
b.liub171c9a2024-11-12 19:23:29 +08001361void unused_func(ATPortType_enum port)
b.liu87afc4c2024-08-14 17:33:45 +08001362{
b.liub171c9a2024-11-12 19:23:29 +08001363 isFinalResponse(port, NULL);
b.liu87afc4c2024-08-14 17:33:45 +08001364}
1365