blob: 3a69ad9bf309fa0e8b2d72c911d99c17073ec6f9 [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.liu7ca612c2025-04-25 09:23:36 +080041static pthread_t s_tid_reader[ATPORTID_NUM];
42static int s_at_fd[ATPORTID_NUM] = {-1}; /* fd of the AT channel */
43static int s_uart_fd[MBTK_SIM_NUM] = {-1}; /* fd of the UART channel */
b.liu87afc4c2024-08-14 17:33:45 +080044
b.liu7ca612c2025-04-25 09:23:36 +080045static ATUnsolHandler s_unsolHandler[MBTK_SIM_NUM];
b.liu87afc4c2024-08-14 17:33:45 +080046
47/* for input buffering */
48
b.liu7ca612c2025-04-25 09:23:36 +080049static char s_ATBuffer[ATPORTID_NUM][MAX_AT_RESPONSE+1];
50static char *s_ATBufferCur[ATPORTID_NUM] = {s_ATBuffer[ATPORTID_SIM1_0], s_ATBuffer[ATPORTID_SIM1_0], s_ATBuffer[ATPORTID_SIM1_0],
51 s_ATBuffer[ATPORTID_SIM2_0], s_ATBuffer[ATPORTID_SIM2_0], s_ATBuffer[ATPORTID_SIM2_0]};
52static char s_UartBuffer[MBTK_SIM_NUM][MAX_AT_RESPONSE+1];
53static char *s_UartBufferCur[MBTK_SIM_NUM] = {s_UartBuffer[MBTK_SIM_1], s_UartBuffer[MBTK_SIM_2]};
b.liu87afc4c2024-08-14 17:33:45 +080054
b.liu7ca612c2025-04-25 09:23:36 +080055static mbtk_ril_at_state_enum at_state[ATPORTID_NUM] = {RIL_AT_STATE_CLOSED};
b.liu87afc4c2024-08-14 17:33:45 +080056
57#if AT_DEBUG
58void AT_DUMP(const char* prefix, const char* buff, int len)
59{
60 if (len < 0)
61 len = strlen(buff);
62 LOGD("%.*s", len, buff);
63}
64#endif
65
66/*
67 * There is one reader thread |s_tid_reader| and potentially multiple writer
68 * threads. |s_commandmutex| and |s_commandcond| are used to maintain the
69 * condition that the writer thread will not read from |sp_response| until the
70 * reader thread has signaled itself is finished, etc. |s_writeMutex| is used to
71 * prevent multiple writer threads from calling at_send_command_full_nolock
72 * function at the same time.
73 */
74
75// "Wait" when AT process...
b.liu7ca612c2025-04-25 09:23:36 +080076static pthread_mutex_t s_commandmutex[ATPORTID_NUM] = {PTHREAD_MUTEX_INITIALIZER};
77static pthread_cond_t s_commandcond[ATPORTID_NUM] = {PTHREAD_COND_INITIALIZER};
b.liu87afc4c2024-08-14 17:33:45 +080078
b.liu7ca612c2025-04-25 09:23:36 +080079static ATCommandType s_type[ATPORTID_NUM];
80static const char *s_responsePrefix[ATPORTID_NUM] = {NULL};
81static const char *s_smsPDU[ATPORTID_NUM] = {NULL};
82static ATResponse *sp_response[ATPORTID_NUM] = {NULL};
83static char s_curr_at[ATPORTID_NUM][AT_BUFF_MAX];
b.liu87afc4c2024-08-14 17:33:45 +080084
85static void (*s_onTimeout)(void) = NULL;
86static void (*s_onReaderClosed)(void) = NULL;
87static int s_readerClosed;
88
b.liu7ca612c2025-04-25 09:23:36 +080089static void onReaderClosed(ATPortId_enum port);
90static int writeCtrlZ (ATPortId_enum port, const char *s);
91static int writeline (ATPortId_enum port, const char *s);
b.liu87afc4c2024-08-14 17:33:45 +080092
93typedef struct
94{
95 char *at_command;
96 long long timeout; // ms
97 bool timeout_close; // Close AT or not while AT response timeout.
98} at_timeout_t;
99
100static at_timeout_t at_timeout_list[] =
101{
102 {"AT+CRSM", 10000, false},
103// {"AT+COPS", 60000, false}
104};
105
106#define NS_PER_S 1000000000
107static void setTimespecRelative(struct timespec *p_ts, long long msec)
108{
109 struct timeval tv;
110
111 gettimeofday(&tv, (struct timezone *) NULL);
112
113 p_ts->tv_sec = tv.tv_sec + (msec / 1000);
114 p_ts->tv_nsec = (tv.tv_usec + (msec % 1000) * 1000L ) * 1000L;
115 /* assuming tv.tv_usec < 10^6 */
116 if (p_ts->tv_nsec >= NS_PER_S)
117 {
118 p_ts->tv_sec++;
119 p_ts->tv_nsec -= NS_PER_S;
120 }
121}
122
123static void sleepMsec(long long msec)
124{
125 struct timespec ts;
126 int err;
127
128 ts.tv_sec = (msec / 1000);
129 ts.tv_nsec = (msec % 1000) * 1000 * 1000;
130
131 do
132 {
133 err = nanosleep (&ts, &ts);
134 }
135 while (err < 0 && errno == EINTR);
136}
137
138
139
140/** add an intermediate response to sp_response*/
b.liu7ca612c2025-04-25 09:23:36 +0800141static void addIntermediate(ATPortId_enum port, const char *line)
b.liu87afc4c2024-08-14 17:33:45 +0800142{
143 ATLine *p_new;
144
145 p_new = (ATLine *) malloc(sizeof(ATLine));
146
147 p_new->line = strdup(line);
148
149// LOGD("line:%s", line);
150// LOGD("line-1:%s", p_new->line);
151
152 /* note: this adds to the head of the list, so the list
153 will be in reverse order of lines received. the order is flipped
154 again before passing on to the command issuer */
b.liub171c9a2024-11-12 19:23:29 +0800155 p_new->p_next = sp_response[port]->p_intermediates;
156 sp_response[port]->p_intermediates = p_new;
b.liu87afc4c2024-08-14 17:33:45 +0800157}
158
159
160/**
161 * returns 1 if line is a final response indicating error
162 * See 27.007 annex B
163 * WARNING: NO CARRIER and others are sometimes unsolicited
164 */
165static const char * s_finalResponsesError[] =
166{
167 "ERROR",
168 "+CMS ERROR:",
169 "+CME ERROR:",
170// "NO CARRIER", /* sometimes! */ // Only for ATD ?
171 "NO ANSWER",
172 "NO DIALTONE",
173};
b.liu7ca612c2025-04-25 09:23:36 +0800174static int isFinalResponseError(ATPortId_enum port, const char *line)
b.liu87afc4c2024-08-14 17:33:45 +0800175{
176 size_t i;
177
178 for (i = 0 ; i < ARRAY_SIZE(s_finalResponsesError) ; i++)
179 {
180 if (strStartsWith(line, s_finalResponsesError[i]))
181 {
182 return 1;
183 }
184 }
185
b.liub171c9a2024-11-12 19:23:29 +0800186 if(!strncasecmp(s_curr_at[port], "ATD", 3) && strStartsWith(line, "NO CARRIER"))
b.liu87afc4c2024-08-14 17:33:45 +0800187 {
188 return 1;
189 }
190
191 return 0;
192}
193
194/**
195 * returns 1 if line is a final response indicating success
196 * See 27.007 annex B
197 * WARNING: NO CARRIER and others are sometimes unsolicited
198 */
199static const char * s_finalResponsesSuccess[] =
200{
201 "OK",
202// "CONNECT" /* some stacks start up data on another channel */
203};
204static int isFinalResponseSuccess(const char *line)
205{
206 size_t i;
207
208 for (i = 0 ; i < ARRAY_SIZE(s_finalResponsesSuccess) ; i++)
209 {
210 if (strStartsWith(line, s_finalResponsesSuccess[i]))
211 {
212 return 1;
213 }
214 }
215
216 return 0;
217}
218
219/**
220 * returns 1 if line is a final response, either error or success
221 * See 27.007 annex B
222 * WARNING: NO CARRIER and others are sometimes unsolicited
223 */
b.liu7ca612c2025-04-25 09:23:36 +0800224static int isFinalResponse(ATPortId_enum port, const char *line)
b.liu87afc4c2024-08-14 17:33:45 +0800225{
b.liub171c9a2024-11-12 19:23:29 +0800226 return isFinalResponseSuccess(line) || isFinalResponseError(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800227}
228
229/**
230 * returns 1 if line is the first line in (what will be) a two-line
231 * SMS unsolicited response
232 */
233static const char * s_smsUnsoliciteds[] =
234{
235 "+CMT:",
236 "+CDS:",
237 "+CBM:"
238};
239static int isSMSUnsolicited(const char *line)
240{
241 size_t i;
242
243 for (i = 0 ; i < ARRAY_SIZE(s_smsUnsoliciteds) ; i++)
244 {
245 if (strStartsWith(line, s_smsUnsoliciteds[i]))
246 {
247 return 1;
248 }
249 }
250
251 return 0;
252}
253
254
255/** assumes s_commandmutex is held */
b.liu7ca612c2025-04-25 09:23:36 +0800256static void handleFinalResponse(ATPortId_enum port, const char *line)
b.liu87afc4c2024-08-14 17:33:45 +0800257{
b.liub171c9a2024-11-12 19:23:29 +0800258 sp_response[port]->finalResponse = strdup(line);
b.liu87afc4c2024-08-14 17:33:45 +0800259
260 //LOGD("AT complete (pthread_cond_signal): %s",line);
b.liub171c9a2024-11-12 19:23:29 +0800261 pthread_cond_signal(&s_commandcond[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800262}
263
b.liu7ca612c2025-04-25 09:23:36 +0800264static void handleUnsolicited(ATPortId_enum port, const char *line)
b.liu87afc4c2024-08-14 17:33:45 +0800265{
b.liu7ca612c2025-04-25 09:23:36 +0800266 if (s_unsolHandler[port < ATPORTID_SIM2_0 ? MBTK_SIM_1 : MBTK_SIM_2] != NULL)
b.liu87afc4c2024-08-14 17:33:45 +0800267 {
b.liu7ca612c2025-04-25 09:23:36 +0800268 s_unsolHandler[port < ATPORTID_SIM2_0 ? MBTK_SIM_1 : MBTK_SIM_2](line, NULL);
b.liu87afc4c2024-08-14 17:33:45 +0800269 }
270}
271
b.liu7ca612c2025-04-25 09:23:36 +0800272static void processLine(ATPortId_enum port, const char *line)
b.liu87afc4c2024-08-14 17:33:45 +0800273{
b.liub171c9a2024-11-12 19:23:29 +0800274 pthread_mutex_lock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800275// LOGD("LINE : %s", line);
b.liub171c9a2024-11-12 19:23:29 +0800276 if (sp_response[port] == NULL)
b.liu87afc4c2024-08-14 17:33:45 +0800277 {
278 /* no command pending */
b.liu7ca612c2025-04-25 09:23:36 +0800279 handleUnsolicited(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800280 }
281 else if (isFinalResponseSuccess(line))
282 {
b.liub171c9a2024-11-12 19:23:29 +0800283 sp_response[port]->success = 1;
284 handleFinalResponse(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800285 }
b.liub171c9a2024-11-12 19:23:29 +0800286 else if (isFinalResponseError(port, line))
b.liu87afc4c2024-08-14 17:33:45 +0800287 {
b.liub171c9a2024-11-12 19:23:29 +0800288 sp_response[port]->success = 0;
289 handleFinalResponse(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800290 }
b.liub171c9a2024-11-12 19:23:29 +0800291 else if (s_smsPDU[port] != NULL && 0 == strcmp(line, "> "))
b.liu87afc4c2024-08-14 17:33:45 +0800292 {
293 // See eg. TS 27.005 4.3
294 // Commands like AT+CMGS have a "> " prompt
b.liub171c9a2024-11-12 19:23:29 +0800295 writeCtrlZ(port, s_smsPDU[port]);
296 s_smsPDU[port] = NULL;
b.liu87afc4c2024-08-14 17:33:45 +0800297 }
b.liub171c9a2024-11-12 19:23:29 +0800298 else switch (s_type[port])
b.liu87afc4c2024-08-14 17:33:45 +0800299 {
300 case NO_RESULT:
b.liu7ca612c2025-04-25 09:23:36 +0800301 handleUnsolicited(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800302 break;
303 case NUMERIC:
b.liub171c9a2024-11-12 19:23:29 +0800304 if (sp_response[port]->p_intermediates == NULL
b.liu87afc4c2024-08-14 17:33:45 +0800305 && isdigit(line[0])
306 )
307 {
b.liub171c9a2024-11-12 19:23:29 +0800308 addIntermediate(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800309 }
310 else
311 {
312 /* either we already have an intermediate response or
313 the line doesn't begin with a digit */
b.liu7ca612c2025-04-25 09:23:36 +0800314 handleUnsolicited(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800315 }
316 break;
317 case SINGLELINE:
b.liub171c9a2024-11-12 19:23:29 +0800318 if (sp_response[port]->p_intermediates == NULL
319 && strStartsWith (line, s_responsePrefix[port])
b.liu87afc4c2024-08-14 17:33:45 +0800320 )
321 {
322 if(*line == '"')
323 {
324 char *line_temp = strdup(line);
b.liudeb8e422024-12-14 17:36:56 +0800325 char *free_ptr = line_temp;
b.liu87afc4c2024-08-14 17:33:45 +0800326 line_temp++;
327 if(strlen(line_temp) > 0)
328 {
329 char *ptr = line_temp + strlen(line_temp) - 1;
330 while(ptr >= line_temp && *ptr == '"')
331 {
332 *ptr = '\0';
333 ptr--;
334 }
335 }
b.liub171c9a2024-11-12 19:23:29 +0800336 addIntermediate(port, line_temp);
b.liudeb8e422024-12-14 17:36:56 +0800337 free(free_ptr);
b.liu87afc4c2024-08-14 17:33:45 +0800338 }
339 else
340 {
b.liub171c9a2024-11-12 19:23:29 +0800341 addIntermediate(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800342 }
343 }
344 else
345 {
346 /* we already have an intermediate response */
b.liu7ca612c2025-04-25 09:23:36 +0800347 handleUnsolicited(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800348 }
349 break;
350 case MULTILINE:
b.liub171c9a2024-11-12 19:23:29 +0800351 if (strStartsWith (line, s_responsePrefix[port]))
b.liu87afc4c2024-08-14 17:33:45 +0800352 {
b.liub171c9a2024-11-12 19:23:29 +0800353 addIntermediate(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800354 }
355 else
356 {
b.liu7ca612c2025-04-25 09:23:36 +0800357 handleUnsolicited(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800358 }
359 break;
360
361 default: /* this should never be reached */
b.liub171c9a2024-11-12 19:23:29 +0800362 LOGE("Unsupported AT command type %d\n", s_type[port]);
b.liu7ca612c2025-04-25 09:23:36 +0800363 handleUnsolicited(port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800364 break;
365 }
366
b.liub171c9a2024-11-12 19:23:29 +0800367 pthread_mutex_unlock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800368}
369
370
371/**
372 * Returns a pointer to the end of the next line
373 * special-cases the "> " SMS prompt
374 *
375 * returns NULL if there is no complete line
376 */
377static char * findNextEOL(char *cur)
378{
379 if (cur[0] == '>' && cur[1] == ' ' && cur[2] == '\0')
380 {
381 /* SMS prompt character...not \r terminated */
382 return cur+2;
383 }
384
385 // Find next newline
386 while (*cur != '\0' && *cur != '\r' && *cur != '\n') cur++;
387
388 return *cur == '\0' ? NULL : cur;
389}
390
391
392/**
393 * Reads a line from the AT channel, returns NULL on timeout.
394 * Assumes it has exclusive read access to the FD
395 *
396 * This line is valid only until the next call to readline
397 *
398 * This function exists because as of writing, android libc does not
399 * have buffered stdio.
400 */
401
b.liu7ca612c2025-04-25 09:23:36 +0800402static const char *readline(ATPortId_enum port)
b.liu87afc4c2024-08-14 17:33:45 +0800403{
404 ssize_t count;
405
406 char *p_read = NULL;
407 char *p_eol = NULL;
408 char *ret;
409
410 /* this is a little odd. I use *s_ATBufferCur == 0 to
411 * mean "buffer consumed completely". If it points to a character, than
412 * the buffer continues until a \0
413 */
b.liub171c9a2024-11-12 19:23:29 +0800414 if (*s_ATBufferCur[port] == '\0')
b.liu87afc4c2024-08-14 17:33:45 +0800415 {
416 /* empty buffer */
b.liub171c9a2024-11-12 19:23:29 +0800417 s_ATBufferCur[port] = s_ATBuffer[port];
418 *s_ATBufferCur[port] = '\0';
419 p_read = s_ATBuffer[port];
b.liu87afc4c2024-08-14 17:33:45 +0800420 }
421 else /* *s_ATBufferCur != '\0' */
422 {
423 /* there's data in the buffer from the last read */
424
425 // skip over leading newlines
b.liub171c9a2024-11-12 19:23:29 +0800426 while (*s_ATBufferCur[port] == '\r' || *s_ATBufferCur[port] == '\n')
427 s_ATBufferCur[port]++;
b.liu87afc4c2024-08-14 17:33:45 +0800428
b.liub171c9a2024-11-12 19:23:29 +0800429 p_eol = findNextEOL(s_ATBufferCur[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800430
431 if (p_eol == NULL)
432 {
433 /* a partial line. move it up and prepare to read more */
434 size_t len;
435
b.liub171c9a2024-11-12 19:23:29 +0800436 len = strlen(s_ATBufferCur[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800437
b.liub171c9a2024-11-12 19:23:29 +0800438 memmove(s_ATBuffer[port], s_ATBufferCur[port], len + 1);
439 p_read = s_ATBuffer[port] + len;
440 s_ATBufferCur[port] = s_ATBuffer[port];
b.liu87afc4c2024-08-14 17:33:45 +0800441 }
442 /* Otherwise, (p_eol !- NULL) there is a complete line */
443 /* that will be returned the while () loop below */
444 }
445
446 while (p_eol == NULL)
447 {
b.liub171c9a2024-11-12 19:23:29 +0800448 if (0 == MAX_AT_RESPONSE - (p_read - s_ATBuffer[port]))
b.liu87afc4c2024-08-14 17:33:45 +0800449 {
450 LOGE("ERROR: Input line exceeded buffer\n");
451 /* ditch buffer and start over again */
b.liub171c9a2024-11-12 19:23:29 +0800452 s_ATBufferCur[port] = s_ATBuffer[port];
453 *s_ATBufferCur[port] = '\0';
454 p_read = s_ATBuffer[port];
b.liu87afc4c2024-08-14 17:33:45 +0800455 }
456
457 do
458 {
b.liub171c9a2024-11-12 19:23:29 +0800459 count = read(s_at_fd[port], p_read,
460 MAX_AT_RESPONSE - (p_read - s_ATBuffer[port]));
b.liu87afc4c2024-08-14 17:33:45 +0800461 usleep(10000);
462 }
463 while (count < 0 && errno == EINTR);
464
465 if (count > 0)
466 {
467 AT_DUMP( "<< ", p_read, count );
468
469 p_read[count] = '\0';
470
471 // skip over leading newlines
b.liub171c9a2024-11-12 19:23:29 +0800472 while (*s_ATBufferCur[port] == '\r' || *s_ATBufferCur[port] == '\n')
473 s_ATBufferCur[port]++;
b.liu87afc4c2024-08-14 17:33:45 +0800474
b.liub171c9a2024-11-12 19:23:29 +0800475 p_eol = findNextEOL(s_ATBufferCur[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800476 p_read += count;
477 }
478 else if (count <= 0)
479 {
480 /* read error encountered or EOF reached */
481 if(count == 0)
482 {
b.liufd87baf2024-11-15 15:30:38 +0800483 LOGD("atchannel[Port-%d]: EOF reached", port);
b.liu87afc4c2024-08-14 17:33:45 +0800484 }
485 else
486 {
b.liufd87baf2024-11-15 15:30:38 +0800487 LOGD("atchannel[Port-%d]: read error %s", port, strerror(errno));
b.liu87afc4c2024-08-14 17:33:45 +0800488 }
489 return NULL;
490 }
491 }
492
493 /* a full line in the buffer. Place a \0 over the \r and return */
494
b.liub171c9a2024-11-12 19:23:29 +0800495 ret = s_ATBufferCur[port];
b.liu87afc4c2024-08-14 17:33:45 +0800496 *p_eol = '\0';
b.liub171c9a2024-11-12 19:23:29 +0800497 s_ATBufferCur[port] = p_eol + 1; /* this will always be <= p_read, */
b.liu87afc4c2024-08-14 17:33:45 +0800498 /* and there will be a \0 at *p_read */
499
b.liufd87baf2024-11-15 15:30:38 +0800500 LOGD("[Port-%d]AT< %s", port, ret);
b.liu87afc4c2024-08-14 17:33:45 +0800501 return ret;
502}
503
b.liu7ca612c2025-04-25 09:23:36 +0800504static const char *readlineUrc(ATPortId_enum port)
b.liu87afc4c2024-08-14 17:33:45 +0800505{
506 ssize_t count;
507
508 char *p_read = NULL;
509 char *p_eol = NULL;
510 char *ret;
511
b.liu7ca612c2025-04-25 09:23:36 +0800512 mbtk_sim_type_enum sim_id = port < ATPORTID_SIM2_0 ? MBTK_SIM_1 : MBTK_SIM_2;
513
514
b.liu87afc4c2024-08-14 17:33:45 +0800515 /* this is a little odd. I use *s_ATBufferCur == 0 to
516 * mean "buffer consumed completely". If it points to a character, than
517 * the buffer continues until a \0
518 */
b.liu7ca612c2025-04-25 09:23:36 +0800519 if (*s_UartBufferCur[sim_id] == '\0')
b.liu87afc4c2024-08-14 17:33:45 +0800520 {
521 /* empty buffer */
b.liu7ca612c2025-04-25 09:23:36 +0800522 s_UartBufferCur[sim_id] = s_UartBuffer[sim_id];
523 *s_UartBufferCur[sim_id] = '\0';
524 p_read = s_UartBuffer[sim_id];
b.liu87afc4c2024-08-14 17:33:45 +0800525 }
526 else /* *s_ATBufferCur != '\0' */
527 {
528 /* there's data in the buffer from the last read */
529
530 // skip over leading newlines
b.liu7ca612c2025-04-25 09:23:36 +0800531 while (*s_UartBufferCur[sim_id] == '\r' || *s_UartBufferCur[sim_id] == '\n')
532 s_UartBufferCur[sim_id]++;
b.liu87afc4c2024-08-14 17:33:45 +0800533
b.liu7ca612c2025-04-25 09:23:36 +0800534 p_eol = findNextEOL(s_UartBufferCur[sim_id]);
b.liu87afc4c2024-08-14 17:33:45 +0800535
536 if (p_eol == NULL)
537 {
538 /* a partial line. move it up and prepare to read more */
539 size_t len;
540
b.liu7ca612c2025-04-25 09:23:36 +0800541 len = strlen(s_UartBufferCur[sim_id]);
b.liu87afc4c2024-08-14 17:33:45 +0800542
b.liu7ca612c2025-04-25 09:23:36 +0800543 memmove(s_UartBuffer[sim_id], s_UartBufferCur[sim_id], len + 1);
544 p_read = s_UartBuffer[sim_id] + len;
545 s_UartBufferCur[sim_id] = s_UartBuffer[sim_id];
b.liu87afc4c2024-08-14 17:33:45 +0800546 }
547 /* Otherwise, (p_eol !- NULL) there is a complete line */
548 /* that will be returned the while () loop below */
549 }
550
551 while (p_eol == NULL)
552 {
b.liu7ca612c2025-04-25 09:23:36 +0800553 if (0 == MAX_AT_RESPONSE - (p_read - s_UartBuffer[sim_id]))
b.liu87afc4c2024-08-14 17:33:45 +0800554 {
555 LOGE("ERROR: Input line exceeded buffer\n");
556 /* ditch buffer and start over again */
b.liu7ca612c2025-04-25 09:23:36 +0800557 s_UartBufferCur[sim_id] = s_UartBuffer[sim_id];
558 *s_UartBufferCur[sim_id] = '\0';
559 p_read = s_UartBuffer[sim_id];
b.liu87afc4c2024-08-14 17:33:45 +0800560 }
561
562 do
563 {
b.liu7ca612c2025-04-25 09:23:36 +0800564 count = read(s_uart_fd[sim_id], p_read,
565 MAX_AT_RESPONSE - (p_read - s_UartBuffer[sim_id]));
b.liu87afc4c2024-08-14 17:33:45 +0800566 usleep(10000);
567 }
568 while (count < 0 && errno == EINTR);
569
570 if (count > 0)
571 {
572 AT_DUMP( "<< ", p_read, count );
573
574 p_read[count] = '\0';
575
576 // skip over leading newlines
b.liu7ca612c2025-04-25 09:23:36 +0800577 while (*s_UartBufferCur[sim_id] == '\r' || *s_UartBufferCur[sim_id] == '\n')
578 s_UartBufferCur[sim_id]++;
b.liu87afc4c2024-08-14 17:33:45 +0800579
b.liu7ca612c2025-04-25 09:23:36 +0800580 p_eol = findNextEOL(s_UartBufferCur[sim_id]);
b.liu87afc4c2024-08-14 17:33:45 +0800581 p_read += count;
582 }
583 else if (count <= 0)
584 {
585 /* read error encountered or EOF reached */
586 if(count == 0)
587 {
588 LOGD("atchannel: EOF reached");
589 }
590 else
591 {
592 LOGD("atchannel: read error %s", strerror(errno));
593 }
594 return NULL;
595 }
596 }
597
598 /* a full line in the buffer. Place a \0 over the \r and return */
599
b.liu7ca612c2025-04-25 09:23:36 +0800600 ret = s_UartBufferCur[sim_id];
b.liu87afc4c2024-08-14 17:33:45 +0800601 *p_eol = '\0';
b.liu7ca612c2025-04-25 09:23:36 +0800602 s_UartBufferCur[sim_id] = p_eol + 1; /* this will always be <= p_read, */
b.liu87afc4c2024-08-14 17:33:45 +0800603 /* and there will be a \0 at *p_read */
604
b.liu7ca612c2025-04-25 09:23:36 +0800605 LOGD("[Sim-%d,Port-%d]URC< %s", sim_id, port, ret);
b.liu87afc4c2024-08-14 17:33:45 +0800606 return ret;
607}
608
609
b.liu7ca612c2025-04-25 09:23:36 +0800610static void onReaderClosed(ATPortId_enum port)
b.liu87afc4c2024-08-14 17:33:45 +0800611{
612 LOGD("onReaderClosed()");
613 if (s_onReaderClosed != NULL && s_readerClosed == 0)
614 {
615
b.liub171c9a2024-11-12 19:23:29 +0800616 pthread_mutex_lock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800617
618 s_readerClosed = 1;
619
b.liub171c9a2024-11-12 19:23:29 +0800620 pthread_cond_signal(&s_commandcond[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800621
b.liub171c9a2024-11-12 19:23:29 +0800622 pthread_mutex_unlock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800623
624 s_onReaderClosed();
625 }
626}
627
b.liu06559f62024-11-01 18:48:22 +0800628typedef struct
629{
630 int cid;
631 bool act;
632 bool waitting;
633} info_cgact_wait_t;
634extern info_cgact_wait_t cgact_wait;
635
b.liu87afc4c2024-08-14 17:33:45 +0800636static void *readerLoop(void *arg)
637{
638 UNUSED(arg);
b.liu7ca612c2025-04-25 09:23:36 +0800639 ATPortId_enum *port = (ATPortId_enum*)arg;
b.liu87afc4c2024-08-14 17:33:45 +0800640 for (;;)
641 {
642 const char * line;
643
b.liub171c9a2024-11-12 19:23:29 +0800644 line = readline(*port);
b.liu87afc4c2024-08-14 17:33:45 +0800645
646 if (line == NULL)
647 {
b.liufd87baf2024-11-15 15:30:38 +0800648 //usleep(50000);
649 //continue;
b.liu87afc4c2024-08-14 17:33:45 +0800650 break;
651 }
652
653 if(strStartsWith(line, "MBTK_AT_READY")) {
654 //handleUnsolicited(line);
655 continue;
b.liu06559f62024-11-01 18:48:22 +0800656 } else if(strStartsWith(line, "CONNECT")) {
657 if(cgact_wait.waitting && cgact_wait.act) {
658 cgact_wait.waitting = false;
659 }
b.liu87afc4c2024-08-14 17:33:45 +0800660 }
661
662 if(isSMSUnsolicited(line))
663 {
664 char *line1;
665 const char *line2;
666
667 // The scope of string returned by 'readline()' is valid only
668 // till next call to 'readline()' hence making a copy of line
669 // before calling readline again.
670 line1 = strdup(line);
b.liub171c9a2024-11-12 19:23:29 +0800671 line2 = readline(*port);
b.liu87afc4c2024-08-14 17:33:45 +0800672
673 if (line2 == NULL)
674 {
675 free(line1);
676 break;
677 }
678
b.liu7ca612c2025-04-25 09:23:36 +0800679 if (s_unsolHandler[*port < ATPORTID_SIM2_0 ? MBTK_SIM_1 : MBTK_SIM_2] != NULL)
b.liu87afc4c2024-08-14 17:33:45 +0800680 {
b.liu7ca612c2025-04-25 09:23:36 +0800681 s_unsolHandler[*port < ATPORTID_SIM2_0 ? MBTK_SIM_1 : MBTK_SIM_2] (line1, line2);
b.liu87afc4c2024-08-14 17:33:45 +0800682 }
683 free(line1);
684 }
685 else
686 {
b.liub171c9a2024-11-12 19:23:29 +0800687 processLine(*port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800688 }
689 }
690
b.liub171c9a2024-11-12 19:23:29 +0800691 onReaderClosed(*port);
692
693 free(port);
b.liu87afc4c2024-08-14 17:33:45 +0800694
695 return NULL;
696}
697
698static void *readerUrcLoop(void *arg)
699{
700 UNUSED(arg);
b.liu7ca612c2025-04-25 09:23:36 +0800701 ATPortId_enum *port = (ATPortId_enum*)arg;
b.liu87afc4c2024-08-14 17:33:45 +0800702 for (;;)
703 {
704 const char *line;
705
b.liufd87baf2024-11-15 15:30:38 +0800706 line = readlineUrc(*port);
b.liu87afc4c2024-08-14 17:33:45 +0800707
708 if (line == NULL)
709 {
710 break;
711 }
712
b.liu7ca612c2025-04-25 09:23:36 +0800713 handleUnsolicited(*port, line);
b.liu87afc4c2024-08-14 17:33:45 +0800714 }
715
b.liub171c9a2024-11-12 19:23:29 +0800716 onReaderClosed(*port);
717
718 free(port);
b.liu87afc4c2024-08-14 17:33:45 +0800719
720 return NULL;
721}
722
723
724/**
725 * Sends string s to the radio with a \r appended.
726 * Returns AT_ERROR_* on error, 0 on success
727 *
728 * This function exists because as of writing, android libc does not
729 * have buffered stdio.
730 */
b.liu7ca612c2025-04-25 09:23:36 +0800731static int writeline (ATPortId_enum port, const char *s)
b.liu87afc4c2024-08-14 17:33:45 +0800732{
733 size_t cur = 0;
734 size_t len = strlen(s);
735 ssize_t written;
736
b.liub171c9a2024-11-12 19:23:29 +0800737 if (s_at_fd[port] < 0 || s_readerClosed > 0)
b.liu87afc4c2024-08-14 17:33:45 +0800738 {
739 return AT_ERROR_CHANNEL_CLOSED;
740 }
741
b.liufd87baf2024-11-15 15:30:38 +0800742 LOGD("[Port-%d]AT> %s", port, s);
b.liu87afc4c2024-08-14 17:33:45 +0800743
744 AT_DUMP( ">> ", s, strlen(s) );
745
b.liub171c9a2024-11-12 19:23:29 +0800746 memset(s_curr_at[port], 0x0, AT_BUFF_MAX);
747 memcpy(s_curr_at[port], s, strlen(s));
b.liu87afc4c2024-08-14 17:33:45 +0800748
749 /* the main string */
750 while (cur < len)
751 {
752 do
753 {
b.liub171c9a2024-11-12 19:23:29 +0800754 written = write (s_at_fd[port], s + cur, len - cur);
b.liu87afc4c2024-08-14 17:33:45 +0800755 }
756 while (written < 0 && errno == EINTR);
757
758 if (written < 0)
759 {
760 return AT_ERROR_GENERIC;
761 }
762
763 cur += written;
764 }
765
766 /* the \r */
767
768 do
769 {
b.liub171c9a2024-11-12 19:23:29 +0800770 written = write (s_at_fd[port], "\r", 1);
b.liu87afc4c2024-08-14 17:33:45 +0800771 }
772 while ((written < 0 && errno == EINTR) || (written == 0));
773
774 if (written < 0)
775 {
776 return AT_ERROR_GENERIC;
777 }
778
779 return 0;
780}
781
b.liu7ca612c2025-04-25 09:23:36 +0800782static int writeCtrlZ (ATPortId_enum port, const char *s)
b.liu87afc4c2024-08-14 17:33:45 +0800783{
784 size_t cur = 0;
785 size_t len = strlen(s);
786 ssize_t written;
787
b.liub171c9a2024-11-12 19:23:29 +0800788 if (s_at_fd[port] < 0 || s_readerClosed > 0)
b.liu87afc4c2024-08-14 17:33:45 +0800789 {
790 return AT_ERROR_CHANNEL_CLOSED;
791 }
792
793 LOGD("AT> %s^Z\n", s);
794
795 AT_DUMP( ">* ", s, strlen(s) );
796
797 /* the main string */
798 while (cur < len)
799 {
800 do
801 {
b.liub171c9a2024-11-12 19:23:29 +0800802 written = write (s_at_fd[port], s + cur, len - cur);
b.liu87afc4c2024-08-14 17:33:45 +0800803 }
804 while (written < 0 && errno == EINTR);
805
806 if (written < 0)
807 {
808 return AT_ERROR_GENERIC;
809 }
810
811 cur += written;
812 }
813
814 /* the ^Z */
815
816 do
817 {
b.liub171c9a2024-11-12 19:23:29 +0800818 written = write (s_at_fd[port], "\032", 1);
b.liu87afc4c2024-08-14 17:33:45 +0800819 }
820 while ((written < 0 && errno == EINTR) || (written == 0));
821
822 if (written < 0)
823 {
824 return AT_ERROR_GENERIC;
825 }
826
827 return 0;
828}
829
b.liu7ca612c2025-04-25 09:23:36 +0800830static void clearPendingCommand(ATPortId_enum port)
b.liu87afc4c2024-08-14 17:33:45 +0800831{
b.liub171c9a2024-11-12 19:23:29 +0800832 if (sp_response[port] != NULL)
b.liu87afc4c2024-08-14 17:33:45 +0800833 {
b.liub171c9a2024-11-12 19:23:29 +0800834 at_response_free(sp_response[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800835 }
836
b.liub171c9a2024-11-12 19:23:29 +0800837 sp_response[port] = NULL;
838 s_responsePrefix[port] = NULL;
839 s_smsPDU[port] = NULL;
b.liu87afc4c2024-08-14 17:33:45 +0800840}
841
842
843/**
844 * Starts AT handler on stream "fd'
845 * returns 0 on success, -1 on error
846 */
b.liu7ca612c2025-04-25 09:23:36 +0800847int at_open(ATPortId_enum port, int at_fd, int uart_fd, ATUnsolHandler h)
b.liu87afc4c2024-08-14 17:33:45 +0800848{
849 int ret;
850 pthread_attr_t attr;
851
b.liub171c9a2024-11-12 19:23:29 +0800852 s_at_fd[port] = at_fd;
b.liu7ca612c2025-04-25 09:23:36 +0800853 s_uart_fd[port < ATPORTID_SIM2_0 ? MBTK_SIM_1 : MBTK_SIM_2] = uart_fd;
854 s_unsolHandler[port < ATPORTID_SIM2_0 ? MBTK_SIM_1 : MBTK_SIM_2] = h;
b.liu87afc4c2024-08-14 17:33:45 +0800855 s_readerClosed = 0;
b.liub171c9a2024-11-12 19:23:29 +0800856 s_responsePrefix[port] = NULL;
857 s_smsPDU[port] = NULL;
858 sp_response[port] = NULL;
859
b.liu7ca612c2025-04-25 09:23:36 +0800860 ATPortId_enum *at_port_ptr = (ATPortId_enum*)malloc(sizeof(ATPortId_enum));
861 ATPortId_enum *urc_port_ptr = (ATPortId_enum*)malloc(sizeof(ATPortId_enum));
b.liub171c9a2024-11-12 19:23:29 +0800862 *at_port_ptr = port;
863 *urc_port_ptr = port;
b.liu87afc4c2024-08-14 17:33:45 +0800864
865 pthread_attr_init (&attr);
866 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
b.liub171c9a2024-11-12 19:23:29 +0800867 ret = pthread_create(&s_tid_reader[port], &attr, readerLoop, at_port_ptr);
b.liu87afc4c2024-08-14 17:33:45 +0800868 if (ret < 0)
869 {
870 LOGE("AT thread create fail.");
871 return -1;
872 }
873
b.liu7ca612c2025-04-25 09:23:36 +0800874 if(port == ATPORTID_SIM1_0 || port == ATPORTID_SIM2_0) { // URC only for ATPORTTYPE_0
b.liufd87baf2024-11-15 15:30:38 +0800875 pthread_t uart_tid_reader;
876 ret = pthread_create(&uart_tid_reader, &attr, readerUrcLoop, urc_port_ptr);
877 if (ret < 0)
878 {
879 LOGE("Uart thread create fail.");
880 return -1;
881 }
b.liu87afc4c2024-08-14 17:33:45 +0800882 }
883
884 return 0;
885}
886
887/* FIXME is it ok to call this from the reader and the command thread? */
b.liu7ca612c2025-04-25 09:23:36 +0800888void at_close(ATPortId_enum port)
b.liu87afc4c2024-08-14 17:33:45 +0800889{
890 LOGD("at_close()");
b.liub171c9a2024-11-12 19:23:29 +0800891 if (s_at_fd[port] >= 0)
b.liu87afc4c2024-08-14 17:33:45 +0800892 {
b.liub171c9a2024-11-12 19:23:29 +0800893 close(s_at_fd[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800894 }
b.liu7ca612c2025-04-25 09:23:36 +0800895 if (s_uart_fd[port < ATPORTID_SIM2_0 ? MBTK_SIM_1 : MBTK_SIM_2] >= 0)
b.liu87afc4c2024-08-14 17:33:45 +0800896 {
b.liu7ca612c2025-04-25 09:23:36 +0800897 close(s_uart_fd[port < ATPORTID_SIM2_0 ? MBTK_SIM_1 : MBTK_SIM_2]);
b.liu87afc4c2024-08-14 17:33:45 +0800898 }
b.liub171c9a2024-11-12 19:23:29 +0800899 s_at_fd[port] = -1;
b.liu7ca612c2025-04-25 09:23:36 +0800900 s_uart_fd[port < ATPORTID_SIM2_0 ? MBTK_SIM_1 : MBTK_SIM_2] = -1;
b.liu87afc4c2024-08-14 17:33:45 +0800901
b.liub171c9a2024-11-12 19:23:29 +0800902 pthread_mutex_lock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800903 s_readerClosed = 1;
b.liub171c9a2024-11-12 19:23:29 +0800904 pthread_cond_signal(&s_commandcond[port]);
905 pthread_mutex_unlock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +0800906 /* the reader thread should eventually die */
907
b.liub171c9a2024-11-12 19:23:29 +0800908 at_state[port] = RIL_AT_STATE_CLOSED;
b.liu87afc4c2024-08-14 17:33:45 +0800909}
910
911static ATResponse * at_response_new()
912{
913 return (ATResponse *) calloc(1, sizeof(ATResponse));
914}
915
916void at_response_free(ATResponse *p_response)
917{
918 ATLine *p_line;
919
920 if (p_response == NULL) return;
921
922 p_line = p_response->p_intermediates;
923
924 while (p_line != NULL)
925 {
926 ATLine *p_toFree;
927
928 p_toFree = p_line;
929 p_line = p_line->p_next;
930
931 free(p_toFree->line);
932 free(p_toFree);
933 }
934
935 free (p_response->finalResponse);
936 free (p_response);
937}
938
939/**
940 * The line reader places the intermediate responses in reverse order
941 * here we flip them back
942 */
943static void reverseIntermediates(ATResponse *p_response)
944{
945 ATLine *pcur,*pnext;
946
947 pcur = p_response->p_intermediates;
948 p_response->p_intermediates = NULL;
949
950 while (pcur != NULL)
951 {
952 pnext = pcur->p_next;
953 pcur->p_next = p_response->p_intermediates;
954 p_response->p_intermediates = pcur;
955 pcur = pnext;
956 }
957}
958
959static long long at_timeout_get(const char *at_command, bool *timeout_close)
960{
961 long long timeout = 0;
962 int i;
963 for(i = 0; i < ARRAY_SIZE(at_timeout_list); i++)
964 {
965 if(!strncasecmp(at_command, at_timeout_list[i].at_command, strlen(at_timeout_list[i].at_command)))
966 {
967 timeout = at_timeout_list[i].timeout;
968 *timeout_close = at_timeout_list[i].timeout_close;
969 break;
970 }
971 }
972
973 return timeout;
974}
975
976/**
977 * Internal send_command implementation
978 * Doesn't lock or call the timeout callback
979 *
980 * timeoutMsec == 0 means infinite timeout
981 */
b.liu7ca612c2025-04-25 09:23:36 +0800982static int at_send_command_full_nolock (ATPortId_enum port, const char *command, ATCommandType type,
b.liu87afc4c2024-08-14 17:33:45 +0800983 const char *responsePrefix, const char *smspdu,
984 long long timeoutMsec, ATResponse **pp_outResponse)
985{
986 int err = 0;
987 bool tiemout_close = true;
988 struct timespec ts;
b.liub171c9a2024-11-12 19:23:29 +0800989 if(at_state[port] == RIL_AT_STATE_READY)
990 at_state[port] = RIL_AT_STATE_BUSY;
b.liu87afc4c2024-08-14 17:33:45 +0800991
b.liub171c9a2024-11-12 19:23:29 +0800992 if(sp_response[port] != NULL)
b.liu87afc4c2024-08-14 17:33:45 +0800993 {
994 err = AT_ERROR_COMMAND_PENDING;
995 goto error;
996 }
997
b.liub171c9a2024-11-12 19:23:29 +0800998 err = writeline (port, command);
b.liu87afc4c2024-08-14 17:33:45 +0800999
1000 if (err < 0)
1001 {
1002 goto error;
1003 }
1004
b.liub171c9a2024-11-12 19:23:29 +08001005 s_type[port] = type;
1006 s_responsePrefix[port] = responsePrefix;
1007 s_smsPDU[port] = smspdu;
1008 sp_response[port] = at_response_new();
b.liu87afc4c2024-08-14 17:33:45 +08001009
1010 if(timeoutMsec == 0)
1011 {
1012 timeoutMsec = at_timeout_get(command, &tiemout_close);
1013 }
1014
1015 if (timeoutMsec != 0)
1016 {
1017 setTimespecRelative(&ts, timeoutMsec);
1018 }
1019
b.liub171c9a2024-11-12 19:23:29 +08001020 while (sp_response[port]->finalResponse == NULL && s_readerClosed == 0)
b.liu87afc4c2024-08-14 17:33:45 +08001021 {
1022 //LOGD("AT wait time:%lld",timeoutMsec);
1023 if (timeoutMsec != 0)
1024 {
b.liub171c9a2024-11-12 19:23:29 +08001025 err = pthread_cond_timedwait(&s_commandcond[port], &s_commandmutex[port], &ts);
b.liu87afc4c2024-08-14 17:33:45 +08001026 }
1027 else
1028 {
b.liub171c9a2024-11-12 19:23:29 +08001029 err = pthread_cond_wait(&s_commandcond[port], &s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001030 }
1031
1032 //LOGD("AT continue:err - %d",err);
1033 if (err == ETIMEDOUT)
1034 {
1035 if(tiemout_close)
1036 {
1037 err = AT_ERROR_TIMEOUT_CLOSE;
1038 }
1039 else
1040 {
1041 err = AT_ERROR_TIMEOUT;
1042 }
1043 goto error;
1044 }
1045 }
1046
1047 if (pp_outResponse == NULL)
1048 {
b.liub171c9a2024-11-12 19:23:29 +08001049 at_response_free(sp_response[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001050 }
1051 else
1052 {
1053 /* line reader stores intermediate responses in reverse order */
b.liub171c9a2024-11-12 19:23:29 +08001054 reverseIntermediates(sp_response[port]);
1055 *pp_outResponse = sp_response[port];
b.liu87afc4c2024-08-14 17:33:45 +08001056 }
1057
b.liub171c9a2024-11-12 19:23:29 +08001058 sp_response[port] = NULL;
b.liu87afc4c2024-08-14 17:33:45 +08001059
1060 if(s_readerClosed > 0)
1061 {
1062 err = AT_ERROR_CHANNEL_CLOSED;
1063 goto error;
1064 }
1065
1066 err = 0;
1067error:
b.liub171c9a2024-11-12 19:23:29 +08001068 if(at_state[port] == RIL_AT_STATE_BUSY)
1069 at_state[port] = RIL_AT_STATE_READY;
1070 clearPendingCommand(port);
b.liu87afc4c2024-08-14 17:33:45 +08001071
1072 return err;
1073}
1074
1075/**
1076 * Internal send_command implementation
1077 *
1078 * timeoutMsec == 0 means infinite timeout
1079 */
b.liu7ca612c2025-04-25 09:23:36 +08001080static int at_send_command_full (ATPortId_enum port, const char *command, ATCommandType type,
b.liu87afc4c2024-08-14 17:33:45 +08001081 const char *responsePrefix, const char *smspdu,
1082 long long timeoutMsec, ATResponse **pp_outResponse)
1083{
1084 int err;
1085
b.liub171c9a2024-11-12 19:23:29 +08001086 if (0 != pthread_equal(s_tid_reader[port], pthread_self()))
b.liu87afc4c2024-08-14 17:33:45 +08001087 {
1088 /* cannot be called from reader thread */
1089 LOGE("cannot be called from reader thread.");
1090 return AT_ERROR_INVALID_THREAD;
1091 }
1092
1093 // Waitting for previous AT complete.
b.liub171c9a2024-11-12 19:23:29 +08001094 while(at_state[port] == RIL_AT_STATE_BUSY)
b.liu87afc4c2024-08-14 17:33:45 +08001095 {
1096 usleep(10000);
1097 }
1098
b.liub171c9a2024-11-12 19:23:29 +08001099 pthread_mutex_lock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001100
b.liub171c9a2024-11-12 19:23:29 +08001101 err = at_send_command_full_nolock(port, command, type,
b.liu87afc4c2024-08-14 17:33:45 +08001102 responsePrefix, smspdu,
1103 timeoutMsec, pp_outResponse);
1104
b.liub171c9a2024-11-12 19:23:29 +08001105 pthread_mutex_unlock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001106
1107 if (err == AT_ERROR_TIMEOUT_CLOSE && s_onTimeout != NULL)
1108 {
1109 s_onTimeout();
1110 }
1111
1112 return err;
1113}
1114
1115
1116/**
1117 * Issue a single normal AT command with no intermediate response expected
1118 *
1119 * "command" should not include \r
1120 * pp_outResponse can be NULL
1121 *
1122 * if non-NULL, the resulting ATResponse * must be eventually freed with
1123 * at_response_free
1124 */
b.liu7ca612c2025-04-25 09:23:36 +08001125int at_send_command (ATPortId_enum port, const char *command, ATResponse **pp_outResponse)
b.liu87afc4c2024-08-14 17:33:45 +08001126{
b.liub171c9a2024-11-12 19:23:29 +08001127 return at_send_command_full (port, command, NO_RESULT, NULL,
b.liu87afc4c2024-08-14 17:33:45 +08001128 NULL, 0, pp_outResponse);
1129}
1130
1131
b.liu7ca612c2025-04-25 09:23:36 +08001132int at_send_command_singleline (ATPortId_enum port, const char *command,
b.liu87afc4c2024-08-14 17:33:45 +08001133 const char *responsePrefix,
1134 ATResponse **pp_outResponse)
1135{
1136 int err;
1137
b.liub171c9a2024-11-12 19:23:29 +08001138 err = at_send_command_full (port, command, SINGLELINE, responsePrefix,
b.liu87afc4c2024-08-14 17:33:45 +08001139 NULL, 0, pp_outResponse);
1140
1141 if (err == 0 && pp_outResponse != NULL
1142 && (*pp_outResponse)->success > 0
1143 && (*pp_outResponse)->p_intermediates == NULL
1144 )
1145 {
1146 /* successful command must have an intermediate response */
1147 at_response_free(*pp_outResponse);
1148 *pp_outResponse = NULL;
1149 return AT_ERROR_INVALID_RESPONSE;
1150 }
1151
1152 return err;
1153}
1154
b.liu7ca612c2025-04-25 09:23:36 +08001155int at_send_command_singleline_with_timeout (ATPortId_enum port, const char *command,
b.liu87afc4c2024-08-14 17:33:45 +08001156 const char *responsePrefix,
1157 ATResponse **pp_outResponse,long long timeoutMsec)
1158{
1159 int err;
1160
b.liub171c9a2024-11-12 19:23:29 +08001161 err = at_send_command_full (port, command, SINGLELINE, responsePrefix,
b.liu87afc4c2024-08-14 17:33:45 +08001162 NULL, timeoutMsec, pp_outResponse);
1163
1164 if (err == 0 && pp_outResponse != NULL
1165 && (*pp_outResponse)->success > 0
1166 && (*pp_outResponse)->p_intermediates == NULL
1167 )
1168 {
1169 /* successful command must have an intermediate response */
1170 at_response_free(*pp_outResponse);
1171 *pp_outResponse = NULL;
1172 return AT_ERROR_INVALID_RESPONSE;
1173 }
1174
1175 return err;
1176}
1177
1178
1179
b.liu7ca612c2025-04-25 09:23:36 +08001180int at_send_command_numeric (ATPortId_enum port, const char *command,
b.liu87afc4c2024-08-14 17:33:45 +08001181 ATResponse **pp_outResponse)
1182{
1183 int err;
1184
b.liub171c9a2024-11-12 19:23:29 +08001185 err = at_send_command_full (port, command, NUMERIC, NULL,
b.liu87afc4c2024-08-14 17:33:45 +08001186 NULL, 0, pp_outResponse);
1187
1188 if (err == 0 && pp_outResponse != NULL
1189 && (*pp_outResponse)->success > 0
1190 && (*pp_outResponse)->p_intermediates == NULL
1191 )
1192 {
1193 /* successful command must have an intermediate response */
1194 at_response_free(*pp_outResponse);
1195 *pp_outResponse = NULL;
1196 return AT_ERROR_INVALID_RESPONSE;
1197 }
1198
1199 return err;
1200}
1201
1202
b.liu7ca612c2025-04-25 09:23:36 +08001203int at_send_command_sms (ATPortId_enum port, const char *command,
b.liu87afc4c2024-08-14 17:33:45 +08001204 const char *pdu,
1205 const char *responsePrefix,
1206 ATResponse **pp_outResponse)
1207{
1208 int err;
1209
b.liub171c9a2024-11-12 19:23:29 +08001210 err = at_send_command_full (port, command, SINGLELINE, responsePrefix,
b.liu87afc4c2024-08-14 17:33:45 +08001211 pdu, 0, pp_outResponse);
1212
1213 if (err == 0 && pp_outResponse != NULL
1214 && (*pp_outResponse)->success > 0
1215 && (*pp_outResponse)->p_intermediates == NULL
1216 )
1217 {
1218 /* successful command must have an intermediate response */
1219 at_response_free(*pp_outResponse);
1220 *pp_outResponse = NULL;
1221 return AT_ERROR_INVALID_RESPONSE;
1222 }
1223
1224 return err;
1225}
1226
1227
b.liu7ca612c2025-04-25 09:23:36 +08001228int at_send_command_multiline (ATPortId_enum port, const char *command,
b.liu87afc4c2024-08-14 17:33:45 +08001229 const char *responsePrefix,
1230 ATResponse **pp_outResponse)
1231{
1232 int err;
1233
b.liub171c9a2024-11-12 19:23:29 +08001234 err = at_send_command_full (port, command, MULTILINE, responsePrefix,
b.liu87afc4c2024-08-14 17:33:45 +08001235 NULL, 0, pp_outResponse);
1236
1237 return err;
1238}
1239
1240
1241/** This callback is invoked on the command thread */
1242void at_set_on_timeout(void (*onTimeout)(void))
1243{
1244 s_onTimeout = onTimeout;
1245}
1246
1247/**
1248 * This callback is invoked on the reader thread (like ATUnsolHandler)
1249 * when the input stream closes before you call at_close
1250 * (not when you call at_close())
1251 * You should still call at_close()
1252 */
1253
1254void at_set_on_reader_closed(void (*onClose)(void))
1255{
1256 s_onReaderClosed = onClose;
1257}
1258
1259
1260/**
1261 * Periodically issue an AT command and wait for a response.
1262 * Used to ensure channel has start up and is active
1263 */
b.liu7ca612c2025-04-25 09:23:36 +08001264int at_handshake(ATPortId_enum port)
b.liu87afc4c2024-08-14 17:33:45 +08001265{
b.liu62240ee2024-11-07 17:52:45 +08001266// int i;
b.liu87afc4c2024-08-14 17:33:45 +08001267 int err = 0;
1268
b.liub171c9a2024-11-12 19:23:29 +08001269 if (0 != pthread_equal(s_tid_reader[port], pthread_self()))
b.liu87afc4c2024-08-14 17:33:45 +08001270 {
1271 /* cannot be called from reader thread */
1272 return AT_ERROR_INVALID_THREAD;
1273 }
b.liub171c9a2024-11-12 19:23:29 +08001274 pthread_mutex_lock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001275
1276#if 0
1277 for (i = 0 ; i < HANDSHAKE_RETRY_COUNT ; i++)
1278 {
1279 /* some stacks start with verbose off */
1280 err = at_send_command_full_nolock("ATE0Q0V1", NO_RESULT,
1281 NULL, NULL, HANDSHAKE_TIMEOUT_MSEC, NULL);
1282
1283 if (err == 0)
1284 {
1285 break;
1286 }
1287 }
1288#else
b.liub171c9a2024-11-12 19:23:29 +08001289 err = at_send_command_full_nolock(port, "ATE0Q0V1", NO_RESULT,
b.liu87afc4c2024-08-14 17:33:45 +08001290 NULL, NULL, 0, NULL);
1291#endif
1292
1293 if (err == 0)
1294 {
1295 /* pause for a bit to let the input buffer drain any unmatched OK's
1296 (they will appear as extraneous unsolicited responses) */
1297 sleepMsec(HANDSHAKE_TIMEOUT_MSEC);
1298 }
1299
b.liub171c9a2024-11-12 19:23:29 +08001300 pthread_mutex_unlock(&s_commandmutex[port]);
b.liu87afc4c2024-08-14 17:33:45 +08001301
1302
1303 return err;
1304}
1305
1306/**
1307 * Returns error code from response
1308 * Assumes AT+CMEE=1 (numeric) mode
1309 */
b.liudeb8e422024-12-14 17:36:56 +08001310AT_CME_Error at_get_cme_error(const ATResponse *p_response)
b.liu87afc4c2024-08-14 17:33:45 +08001311{
1312 int ret;
1313 int err;
1314 char *p_cur;
1315
b.liub6180902025-07-02 18:48:23 +08001316 if (p_response == NULL)
1317 {
1318 return CME_ERROR_NON_CME;
1319 }
1320
b.liu87afc4c2024-08-14 17:33:45 +08001321 if (p_response->success > 0)
1322 {
1323 return CME_SUCCESS;
1324 }
1325
1326 if (p_response->finalResponse == NULL
1327 || !strStartsWith(p_response->finalResponse, "+CME ERROR:")
1328 )
1329 {
1330 return CME_ERROR_NON_CME;
1331 }
1332
1333 p_cur = p_response->finalResponse;
1334 err = at_tok_start(&p_cur);
1335
1336 if (err < 0)
1337 {
1338 return CME_ERROR_NON_CME;
1339 }
1340
1341 err = at_tok_nextint(&p_cur, &ret);
1342
1343 if (err < 0)
1344 {
1345 return CME_ERROR_NON_CME;
1346 }
1347
1348 return ret;
1349}
1350
b.liu7ca612c2025-04-25 09:23:36 +08001351mbtk_ril_at_state_enum at_state_get(ATPortId_enum port)
b.liu87afc4c2024-08-14 17:33:45 +08001352{
b.liub171c9a2024-11-12 19:23:29 +08001353 return at_state[port];
b.liu87afc4c2024-08-14 17:33:45 +08001354}
1355
b.liu7ca612c2025-04-25 09:23:36 +08001356void at_state_set(ATPortId_enum port, mbtk_ril_at_state_enum state)
b.liu87afc4c2024-08-14 17:33:45 +08001357{
b.liub171c9a2024-11-12 19:23:29 +08001358 at_state[port] = state;
b.liu87afc4c2024-08-14 17:33:45 +08001359}
1360
1361bool at_rsp_check(ATResponse *p_response)
1362{
1363 if(!p_response || !p_response->success)
1364 return false;
1365
1366 return true;
1367}
1368
b.liu7ca612c2025-04-25 09:23:36 +08001369void unused_func(ATPortId_enum port)
b.liu87afc4c2024-08-14 17:33:45 +08001370{
b.liub171c9a2024-11-12 19:23:29 +08001371 isFinalResponse(port, NULL);
b.liu87afc4c2024-08-14 17:33:45 +08001372}
1373