1 /******************************************************************************
2 @file atc.c
3 @brief at command.
4
5 DESCRIPTION
6 Connectivity Management Tool for USB network adapter of Quectel wireless cellular modules.
7
8 INITIALIZATION AND SEQUENCING REQUIREMENTS
9 None.
10
11 ---------------------------------------------------------------------------
12 Copyright (c) 2016 - 2020 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
13 Quectel Wireless Solution Proprietary and Confidential.
14 ---------------------------------------------------------------------------
15 ******************************************************************************/
16 #include <unistd.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <ctype.h>
21 #include <fcntl.h>
22 #include <stddef.h>
23 #include <pthread.h>
24 #include <errno.h>
25 #include <time.h>
26 #include <signal.h>
27 #include <getopt.h>
28 #include <poll.h>
29 #include <sys/time.h>
30 #include <endian.h>
31 #include <time.h>
32 #include <sys/types.h>
33 #include <limits.h>
34 #include <inttypes.h>
35
36 extern int asprintf(char **s, const char *fmt, ...);
37
38 #include "QMIThread.h"
39
40 #include "atchannel.h"
41 #include "at_tok.h"
42
43 static int asr_style_atc = 0;
44 #define safe_free(__x) do { if (__x) { free((void *)__x); __x = NULL;}} while(0)
45 #define safe_at_response_free(__x) { if (__x) { at_response_free(__x); __x = NULL;}}
46
47 #define at_response_error(err, p_response) \
48 (err \
49 || p_response == NULL \
50 || p_response->finalResponse == NULL \
51 || p_response->success == 0)
52
atc_init(PROFILE_T * profile)53 static int atc_init(PROFILE_T *profile) {
54 int err;
55 ATResponse *p_response = NULL;
56
57 (void)profile;
58
59 err = at_handshake();
60 if (err) {
61 dbg_time("handshake fail, TODO ... ");
62 goto exit;
63 }
64
65 at_send_command("AT+QCFG=\"NAT\",1", NULL);
66 at_send_command_singleline("AT+QCFG=\"usbnet\"", "+QCFG:", NULL);
67 at_send_command_multiline("AT+QNETDEVCTL=?", "+QNETDEVCTL:", NULL);
68 at_send_command("AT+CGREG=2", NULL);
69
70 err = at_send_command_singleline("AT+QNETDEVSTATUS=?", "+QNETDEVSTATUS:", &p_response);
71 if (at_response_error(err, p_response))
72 asr_style_atc = 1; //EC200T/EC100Y do not support this AT, but RG801/RG500U support
73 safe_at_response_free(p_response);
74
75 exit:
76 return err;
77 }
78
atc_deinit(void)79 static int atc_deinit(void) {
80 return 0;
81 }
82
83 /**
84 * Called by atchannel when an unsolicited line appears
85 * This is called on atchannel's reader thread. AT commands may
86 * not be issued here
87 */
onUnsolicited(const char * s,const char * sms_pdu)88 static void onUnsolicited (const char *s, const char *sms_pdu)
89 {
90 (void)sms_pdu;
91
92 if (strStartsWith(s, "+QNETDEVSTATUS:")) {
93 qmidevice_send_event_to_main(RIL_UNSOL_DATA_CALL_LIST_CHANGED);
94 }
95 else if (strStartsWith(s, "+CGREG:") || strStartsWith(s, "+C5GREG:")) {
96 qmidevice_send_event_to_main(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED);
97 }
98 }
99
atc_read_thread(void * param)100 static void * atc_read_thread(void *param) {
101 PROFILE_T *profile = (PROFILE_T *)param;
102 const char *cdc_wdm = (const char *)profile->qmichannel;
103 int wait_for_request_quit = 0;
104 int atc_fd;
105
106 atc_fd = cm_open_dev(cdc_wdm);
107 if (atc_fd <= 0) {
108 dbg_time("fail to open (%s), errno: %d (%s)", cdc_wdm, errno, strerror(errno));
109 goto __quit;
110 }
111
112 dbg_time("atc_fd = %d", atc_fd);
113
114 if (at_open(atc_fd, onUnsolicited))
115 goto __quit;
116
117 qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
118
119 while (atc_fd > 0) {
120 struct pollfd pollfds[] = {{atc_fd, POLLIN, 0}, {qmidevice_control_fd[1], POLLIN, 0}};
121 int ne, ret, nevents = 2;
122
123 ret = poll(pollfds, nevents, wait_for_request_quit ? 1000 : -1);
124
125 if (ret == 0 && wait_for_request_quit) {
126 break;
127 }
128
129 if (ret < 0) {
130 dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
131 break;
132 }
133
134 for (ne = 0; ne < nevents; ne++) {
135 int fd = pollfds[ne].fd;
136 short revents = pollfds[ne].revents;
137
138 if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
139 dbg_time("%s poll err/hup/inval", __func__);
140 dbg_time("epoll fd = %d, events = 0x%04x", fd, revents);
141 if (revents & (POLLERR | POLLHUP | POLLNVAL))
142 goto __quit;
143 }
144
145 if ((revents & POLLIN) == 0)
146 continue;
147
148 if (atc_fd == fd) {
149 usleep(10*1000); //let atchannel.c read at response.
150 }
151 else if (fd == qmidevice_control_fd[1]) {
152 int triger_event;
153 if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) {
154 //dbg_time("triger_event = 0x%x", triger_event);
155 switch (triger_event) {
156 case RIL_REQUEST_QUIT:
157 goto __quit;
158 break;
159 case SIG_EVENT_STOP:
160 wait_for_request_quit = 1;
161 break;
162 default:
163 break;
164 }
165 }
166 }
167 }
168 }
169
170 __quit:
171 at_close();
172 qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
173 dbg_time("%s exit", __func__);
174
175 return NULL;
176 }
177
178 const struct qmi_device_ops atc_dev_ops = {
179 .init = atc_init,
180 .deinit = atc_deinit,
181 .read = atc_read_thread,
182 };
183
requestBaseBandVersion(PROFILE_T * profile)184 static int requestBaseBandVersion(PROFILE_T *profile) {
185 int err;
186 ATResponse *p_response = NULL;
187
188 (void)profile;
189
190 err = at_send_command_singleline("AT+CGMR", "\0", &p_response);
191 if (at_response_error(err, p_response))
192 goto exit;
193
194 exit:
195 safe_at_response_free(p_response);
196 return err;
197 }
198
requestGetSIMStatus(SIM_Status * pSIMStatus)199 static int requestGetSIMStatus(SIM_Status *pSIMStatus)
200 {
201 int err;
202 ATResponse *p_response = NULL;
203 char *cpinLine;
204 char *cpinResult;
205 int ret = SIM_NOT_READY;
206
207 err = at_send_command_singleline("AT+CPIN?", "+CPIN:", &p_response);
208 if (at_response_error(err, p_response))
209 goto done;
210
211 switch (at_get_cme_error(p_response))
212 {
213 case CME_SUCCESS:
214 break;
215
216 case CME_SIM_NOT_INSERTED:
217 case CME_OPERATION_NOT_ALLOWED:
218 case CME_FAILURE:
219 ret = SIM_ABSENT;
220 goto done;
221
222 default:
223 ret = SIM_NOT_READY;
224 goto done;
225 }
226
227 cpinLine = p_response->p_intermediates->line;
228 err = at_tok_start (&cpinLine);
229
230 if (err < 0)
231 {
232 ret = SIM_NOT_READY;
233 goto done;
234 }
235
236 err = at_tok_nextstr(&cpinLine, &cpinResult);
237
238 if (err < 0)
239 {
240 ret = SIM_NOT_READY;
241 goto done;
242 }
243
244 if (0 == strcmp (cpinResult, "SIM PIN"))
245 {
246 ret = SIM_PIN;
247 goto done;
248 }
249 else if (0 == strcmp (cpinResult, "SIM PUK"))
250 {
251 ret = SIM_PUK;
252 goto done;
253 }
254 else if (0 == strcmp (cpinResult, "PH-NET PIN"))
255 {
256 return SIM_NETWORK_PERSONALIZATION;
257 }
258 else if (0 != strcmp (cpinResult, "READY"))
259 {
260 /* we're treating unsupported lock types as "sim absent" */
261 ret = SIM_ABSENT;
262 goto done;
263 }
264
265 ret = SIM_READY;
266
267 done:
268 safe_at_response_free(p_response);
269 *pSIMStatus = ret;
270 return err;
271 }
272
requestRegistrationState(UCHAR * pPSAttachedState)273 static int requestRegistrationState(UCHAR *pPSAttachedState) {
274 int err;
275 ATResponse *p_response = NULL;
276 ATLine *p_cur;
277 int i;
278 int cops_atc = -1;
279 char *response[3] = {NULL, NULL, NULL};
280
281 *pPSAttachedState = 0;
282
283 err = at_send_command_multiline(
284 "AT+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?",
285 "+COPS:", &p_response);
286 if (at_response_error(err, p_response))
287 goto error;
288
289 for ( i = 0, p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next, i++) {
290 int skip;
291 char *line = p_cur->line;
292
293 err = at_tok_start(&line);
294 if (err < 0) goto error;
295
296 err = at_tok_nextint(&line, &skip);
297 if (err < 0) goto error;
298
299 if (!at_tok_hasmore(&line))
300 continue;
301
302 err = at_tok_nextint(&line, &skip);
303 if (err < 0) goto error;
304
305 if (!at_tok_hasmore(&line))
306 continue;
307
308 err = at_tok_nextstr(&line, &(response[i]));
309 if (err < 0) goto error;
310
311 if (!at_tok_hasmore(&line))
312 continue;
313
314 err = at_tok_nextint(&line, &cops_atc);
315 if (err < 0) goto error;
316 }
317
318 if (cops_atc != -1) {
319 *pPSAttachedState = 1;
320 }
321
322 error:
323 safe_at_response_free(p_response);
324
325 return err;
326 }
327
requestSetupDataCall(PROFILE_T * profile,int curIpFamily)328 static int requestSetupDataCall(PROFILE_T *profile, int curIpFamily) {
329 int err;
330 ATResponse *p_response = NULL;
331 char *cmd = NULL;
332 ATLine *p_cur = NULL;
333 char *line = NULL;
334 int pdp = profile->pdp;
335 int state = 0;
336
337 (void)curIpFamily;
338
339 if (asr_style_atc) {
340 err = at_send_command_multiline("AT+CGACT?", "+CGACT:", &p_response);
341 if (at_response_error(err, p_response))
342 goto _error;
343
344 for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next) {
345 int cid = 0;
346 line = p_cur->line;
347
348 err = at_tok_start(&line);
349 if (err < 0) goto _error;
350
351 err = at_tok_nextint(&line, &cid);
352 if (err < 0) goto _error;
353
354 if (cid != pdp)
355 continue;
356
357 err = at_tok_nextint(&line, &state);
358 if (err < 0) goto _error;
359 }
360 safe_at_response_free(p_response);
361
362 if (state == 0) {
363 asprintf(&cmd, "AT+CGACT=1,%d", pdp);
364 err = at_send_command(cmd, &p_response);
365 safe_free(cmd);
366 if (at_response_error(err, p_response))
367 goto _error;
368 }
369 }
370
371 if(asr_style_atc)
372 asprintf(&cmd, "AT+QNETDEVCTL=1,%d,%d", pdp, 1);
373 else
374 asprintf(&cmd, "AT+QNETDEVCTL=%d,1,%d", pdp, 0);
375 err = at_send_command(cmd, &p_response);
376 safe_free(cmd);
377
378 if (at_response_error(err, p_response))
379 goto _error;
380
381 if (!asr_style_atc) { //TODO some modems do not sync return setup call resule
382 int t = 0;
383
384 asprintf(&cmd, "AT+QNETDEVSTATUS=%d", pdp);
385 while (t++ < 30) {
386 err = at_send_command_singleline(cmd, "+QNETDEVSTATUS", &p_response);
387
388 if (!at_response_error(err, p_response)) {
389 safe_at_response_free(p_response);
390 break;
391 }
392 safe_at_response_free(p_response);
393 sleep(1);
394 }
395 safe_free(cmd);
396 if (t > 15)
397 goto _error;
398 }
399 //some modem do not report URC
400 qmidevice_send_event_to_main(RIL_UNSOL_DATA_CALL_LIST_CHANGED);
401
402 _error:
403 safe_at_response_free(p_response);
404 dbg_time("%s err=%d", __func__, err);
405 return err;
406 }
407
at_netdevstatus(int pdp,unsigned int * pV4Addr)408 static int at_netdevstatus(int pdp, unsigned int *pV4Addr) {
409 int err;
410 ATResponse *p_response = NULL;
411 char *cmd = NULL;
412 char *line;
413 char *ipv4 = NULL;
414
415 *pV4Addr = 0;
416
417 asprintf(&cmd, "AT+QNETDEVSTATUS=%d", pdp);
418 err = at_send_command_singleline(cmd, "+QNETDEVSTATUS", &p_response);
419 safe_free(cmd);
420 if (at_response_error(err, p_response))
421 goto _error;
422
423 line = p_response->p_intermediates->line;
424 err = at_tok_start(&line);
425 if (err < 0)
426 goto _error;
427
428 err = at_tok_nextstr(&line, &ipv4);
429 if (err < 0) goto _error;
430
431 if (ipv4) {
432 int addr[4] = {0, 0, 0, 0};
433
434 sscanf(ipv4, "%d.%d.%d.%d", &addr[0], &addr[1], &addr[2], &addr[3]);
435 *pV4Addr = (addr[0]) | (addr[1]<<8) | (addr[2]<<16) | (addr[3]<<24);
436 }
437
438 _error:
439 return err;
440 }
441
requestQueryDataCall(UCHAR * pConnectionStatus,int curIpFamily)442 static int requestQueryDataCall(UCHAR *pConnectionStatus, int curIpFamily) {
443 int err;
444 ATResponse *p_response = NULL;
445 ATLine *p_cur = NULL;
446 char *line = NULL;
447 int state = 0;
448 int bind = 0;
449 int cid;
450 int pdp = 1;
451 unsigned int v4Addr = 0;
452
453 (void)curIpFamily;
454
455 *pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED;
456
457 if (asr_style_atc)
458 goto _asr_style_atc;
459
460 err = at_netdevstatus(pdp, &v4Addr);
461 if (!err && v4Addr) {
462 *pConnectionStatus = QWDS_PKT_DATA_CONNECTED;
463 //if (profile->ipv4.Address == 0) {} //TODO
464 }
465 goto _error;
466
467 _asr_style_atc:
468 err = at_send_command_multiline("AT+QNETDEVCTL?", "+QNETDEVCTL:", &p_response);
469 if (at_response_error(err, p_response))
470 goto _error;
471
472 for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next)
473 {
474 int tmp;
475 //+QNETDECTL:<op>,<cid>,<urc_en>,<state>
476
477 line = p_cur->line;
478 err = at_tok_start(&line);
479 if (err < 0)
480 goto _error;
481
482 err = at_tok_nextint(&line, &bind);
483 if (err < 0)
484 goto _error;
485
486 err = at_tok_nextint(&line, &cid);
487 if (err < 0)
488 goto _error;
489
490 if (cid != pdp)
491 continue;
492
493 err = at_tok_nextint(&line, &tmp);
494 if(err < 0)
495 goto _error;
496
497 err = at_tok_nextint(&line, &state);
498 if(err < 0)
499 goto _error;
500 }
501 safe_at_response_free(p_response);
502
503 if (bind == 0 || state == 0)
504 goto _error;
505
506 err = at_send_command_multiline("AT+CGACT?", "+CGACT:", &p_response);
507 if (at_response_error(err, p_response))
508 goto _error;
509
510 for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next)
511 {
512 line = p_cur->line;
513 err = at_tok_start(&line);
514 if (err < 0)
515 goto _error;
516
517 err = at_tok_nextint(&line, &cid);
518 if (err < 0)
519 goto _error;
520
521 if (cid != pdp)
522 continue;
523
524 err = at_tok_nextint(&line, &state);
525 if (err < 0)
526 goto _error;
527 }
528 safe_at_response_free(p_response);
529
530 if (bind && state)
531 *pConnectionStatus = QWDS_PKT_DATA_CONNECTED;
532
533 _error:
534 safe_at_response_free(p_response);
535 dbg_time("%s err=%d, call_state=%d", __func__, err, *pConnectionStatus);
536 return err;
537 }
538
requestDeactivateDefaultPDP(PROFILE_T * profile,int curIpFamily)539 static int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily) {
540 int err;
541 char *cmd = NULL;
542 int pdp = profile->pdp;
543
544 (void)curIpFamily;
545
546 if (asr_style_atc)
547 asprintf(&cmd, "AT+QNETDEVCTL=0,%d,%d", pdp, 0);
548 else
549 asprintf(&cmd, "AT+QNETDEVCTL=%d,0,%d", pdp, 0);
550 err = at_send_command(cmd, NULL);
551 safe_free(cmd);
552
553 dbg_time("%s err=%d", __func__, err);
554 return err;
555 }
556
requestGetIPAddress(PROFILE_T * profile,int curIpFamily)557 static int requestGetIPAddress(PROFILE_T *profile, int curIpFamily) {
558 int err;
559 ATResponse *p_response = NULL;
560 char *cmd = NULL;
561 ATLine *p_cur = NULL;
562 char *line = NULL;
563 int pdp = profile->pdp;
564 unsigned int v4Addr = 0;
565
566 (void)curIpFamily;
567
568 if (asr_style_atc)
569 goto _asr_style_atc;
570
571 err = at_netdevstatus(pdp, &v4Addr);
572 if (err < 0) goto _error;
573
574 goto _error;
575
576 _asr_style_atc:
577 asprintf(&cmd, "AT+CGPADDR=%d", profile->pdp);
578 err = at_send_command_singleline(cmd, "+CGPADDR:", &p_response);
579 safe_free(cmd);
580 if (at_response_error(err, p_response))
581 goto _error;
582
583 //+CGPADDR: 1,"10.201.80.91","2409:8930:4B3:41C7:F9B8:3D9B:A2F7:CA96"
584 for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next)
585 {
586 char *ipv4 = NULL;
587
588 line = p_cur->line;
589 err = at_tok_start(&line);
590 if (err < 0)
591 goto _error;
592
593 err = at_tok_nextint(&line, &pdp);
594 if (err < 0)
595 goto _error;
596
597 if (pdp != profile->pdp)
598 continue;
599
600 if (!at_tok_hasmore(&line))
601 continue;
602
603 err = at_tok_nextstr(&line, &ipv4);
604 if (err < 0) goto _error;
605
606 if (ipv4) {
607 int addr[4] = {0, 0, 0, 0};
608
609 sscanf(ipv4, "%d.%d.%d.%d", &addr[0], &addr[1], &addr[2], &addr[3]);
610 v4Addr = (addr[0]) | (addr[1]<<8) | (addr[2]<<16) | (addr[3]<<24);
611 break;
612 }
613 }
614
615 _error:
616 if (!v4Addr && !err) {
617 err = -1;
618 }
619 if (profile->ipv4.Address != v4Addr) {
620 profile->ipv4.Address = v4Addr;
621 if (v4Addr) {
622 unsigned char *v4 = (unsigned char *)&v4Addr;
623 dbg_time("%s %d.%d.%d.%d", __func__, v4[0], v4[1], v4[2], v4[3]);
624 }
625 }
626
627 dbg_time("%s err=%d", __func__, err);
628 return err;
629 }
630
requestGetICCID(void)631 static int requestGetICCID(void) {
632 int err;
633 ATResponse *p_response = NULL;
634 char *line;
635 char *iccid;
636
637 err = at_send_command_singleline("AT+QCCID", "+QCCID:", &p_response);
638 if (at_response_error(err, p_response))
639 goto _error;
640
641 line = p_response->p_intermediates->line;
642 err = at_tok_start(&line);
643 if (err < 0)
644 goto _error;
645
646 err = at_tok_nextstr(&line, &iccid);
647 if (err < 0)
648 goto _error;
649
650 dbg_time("%s %s", __func__, iccid);
651
652 _error:
653 safe_at_response_free(p_response);
654 return err;
655 }
656
requestGetIMSI(void)657 static int requestGetIMSI(void) {
658 int err;
659 ATResponse *p_response = NULL;
660 char *imsi;
661
662 err = at_send_command_numeric("AT+CIMI", &p_response);
663 if (at_response_error(err, p_response))
664 goto exit;
665
666 imsi = p_response->p_intermediates->line;
667 if (imsi) {
668 dbg_time("%s %s", __func__, imsi);
669 }
670
671 exit:
672 safe_at_response_free(p_response);
673 return err;
674 }
675
676 const struct request_ops atc_request_ops = {
677 .requestBaseBandVersion = requestBaseBandVersion,
678 .requestGetSIMStatus = requestGetSIMStatus,
679 .requestRegistrationState = requestRegistrationState,
680 .requestSetupDataCall = requestSetupDataCall,
681 .requestQueryDataCall = requestQueryDataCall,
682 .requestDeactivateDefaultPDP = requestDeactivateDefaultPDP,
683 .requestGetIPAddress = requestGetIPAddress,
684 .requestGetICCID = requestGetICCID,
685 .requestGetIMSI = requestGetIMSI,
686 };
687
688