Files
LFLede/RM520N-5G/文件/source/atc.c
T
2026-08-21 18:28:33 +08:00

1057 lines
33 KiB
C

/*
Copyright 2025 Quectel Wireless Solutions Co.,Ltd
Quectel hereby grants customers of Quectel a license to use, modify,
distribute and publish the Software in binary form provided that
customers shall have no right to reverse engineer, reverse assemble,
decompile or reduce to source code form any portion of the Software.
Under no circumstances may customers modify, demonstrate, use, deliver
or disclose any portion of the Software in source code form.
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <stddef.h>
#include <pthread.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <getopt.h>
#include <poll.h>
#include <sys/time.h>
#include <endian.h>
#include <time.h>
#include <sys/types.h>
#include <limits.h>
#include <inttypes.h>
extern int asprintf(char **s, const char *fmt, ...);
#include "QMIThread.h"
#include "atchannel.h"
#include "at_tok.h"
static int asr_style_atc = 0;
static int s_pdp;
#define safe_free(__x) do { if (__x) { free((void *)__x); __x = NULL;}} while(0)
#define safe_at_response_free(__x) { if (__x) { at_response_free(__x); __x = NULL;}}
#define at_response_error(err, p_response) \
(err \
|| p_response == NULL \
|| p_response->finalResponse == NULL \
|| p_response->success == 0)
static int atc_init(PROFILE_T *profile) {
int err;
char *cmd;
ATResponse *p_response = NULL;
if (profile->proxy[0]) {
s_pdp = profile->pdp;
err = at_send_command_singleline("AT+QNETDEVSTATUS=?", "+QNETDEVSTATUS:", &p_response);
if (at_response_error(err, p_response))
asr_style_atc = 1; //EC200T/EC100Y do not support this AT, but RG801/RG500U support
safe_at_response_free(p_response);
return err;
}
err = at_handshake();
if (err) {
dbg_time("handshake fail, TODO ... ");
goto exit;
}
s_pdp = profile->pdp;
at_send_command_singleline("AT+QCFG=\"usbnet\"", "+QCFG:", NULL);
at_send_command_multiline("AT+QNETDEVCTL=?", "+QNETDEVCTL:", NULL);
at_send_command("AT+CGREG=2", NULL); //GPRS Network Registration Status
at_send_command("AT+CEREG=2", NULL); //EPS Network Registration Status
at_send_command("AT+C5GREG=2", NULL); //5GS Network Registration Status
err = at_send_command_singleline("AT+QNETDEVSTATUS=?", "+QNETDEVSTATUS:", &p_response);
if (at_response_error(err, p_response))
asr_style_atc = 1; //EC200T/EC100Y do not support this AT, but RG801/RG500U support
safe_at_response_free(p_response);
if(profile->usb_dev.idProduct == 0x6007)
asr_style_atc = 1;
err = at_send_command_singleline("AT+QCFG=\"NAT\"", "+QCFG:", &p_response);
if (!at_response_error(err, p_response)) {
int old_nat, new_nat = asr_style_atc ? 1 : 0;
err = at_tok_scanf(p_response->p_intermediates->line, "%s%d", NULL, &old_nat);
if (err == 2 && old_nat != new_nat) {
safe_at_response_free(p_response);
asprintf(&cmd, "AT+QCFG=\"NAT\",%d", new_nat);
err = at_send_command(cmd, &p_response);
safe_free(cmd);
if (!at_response_error(err, p_response)) {
err = at_send_command("at+cfun=1,1",NULL);
if (!err)
g_donot_exit_when_modem_hangup = 1;
//reboot to take effect
}
safe_at_response_free(p_response);
}
err = 0;
}
safe_at_response_free(p_response);
exit:
return err;
}
static int atc_deinit(void) {
return 0;
}
/**
* Called by atchannel when an unsolicited line appears
* This is called on atchannel's reader thread. AT commands may
* not be issued here
*/
static void onUnsolicited (const char *s, const char *sms_pdu)
{
(void)sms_pdu;
if (strStartsWith(s, "+QNETDEVSTATUS:")) {
qmidevice_send_event_to_main(RIL_UNSOL_DATA_CALL_LIST_CHANGED);
}
else if (strStartsWith(s, "+CGREG:")
|| strStartsWith(s, "+CEREG:")
|| strStartsWith(s, "+C5GREG:")) {
qmidevice_send_event_to_main(RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED);
}
}
static void onTimeout(void) {
dbg_time("%s", __func__);
//TODO
}
static void onClose(void) {
dbg_time("%s", __func__);
}
static void * atc_read_thread(void *param) {
PROFILE_T *profile = (PROFILE_T *)param;
const char *cdc_wdm = (const char *)profile->qmichannel;
int wait_for_request_quit = 0;
int atc_fd;
atc_fd = cm_open_dev(cdc_wdm);
if (atc_fd <= 0) {
dbg_time("fail to open (%s), errno: %d (%s)", cdc_wdm, errno, strerror(errno));
goto __quit;
}
dbg_time("atc_fd = %d", atc_fd);
if (at_open(atc_fd, onUnsolicited, 0))
goto __quit;
at_set_on_timeout(onTimeout);
at_set_on_reader_closed(onClose);
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_CONNECTED);
while (atc_fd > 0) {
struct pollfd pollfds[] = {{atc_fd, POLLIN, 0}, {qmidevice_control_fd[1], POLLIN, 0}};
int ne, ret, nevents = 2;
ret = poll(pollfds, nevents, wait_for_request_quit ? 1000 : -1);
if (ret == 0 && wait_for_request_quit) {
break;
}
if (ret < 0) {
dbg_time("%s poll=%d, errno: %d (%s)", __func__, ret, errno, strerror(errno));
break;
}
for (ne = 0; ne < nevents; ne++) {
int fd = pollfds[ne].fd;
short revents = pollfds[ne].revents;
if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
dbg_time("%s poll err/hup/inval", __func__);
dbg_time("epoll fd = %d, events = 0x%04x", fd, revents);
if (revents & (POLLERR | POLLHUP | POLLNVAL))
goto __quit;
}
if ((revents & POLLIN) == 0)
continue;
if (atc_fd == fd) {
usleep(10*1000); //let atchannel.c read at response.
}
else if (fd == qmidevice_control_fd[1]) {
int triger_event;
if (read(fd, &triger_event, sizeof(triger_event)) == sizeof(triger_event)) {
//dbg_time("triger_event = 0x%x", triger_event);
switch (triger_event) {
case RIL_REQUEST_QUIT:
goto __quit;
break;
case SIG_EVENT_STOP:
wait_for_request_quit = 1;
break;
default:
break;
}
}
}
}
}
__quit:
at_close();
qmidevice_send_event_to_main(RIL_INDICATE_DEVICE_DISCONNECTED);
dbg_time("%s exit", __func__);
return NULL;
}
const struct qmi_device_ops atc_dev_ops = {
.init = atc_init,
.deinit = atc_deinit,
.read = atc_read_thread,
};
static int requestBaseBandVersion(PROFILE_T *profile) {
int retVal = -1;
int err;
ATResponse *p_response = NULL;
(void)profile;
err = at_send_command_multiline("AT+CGMR", "\0", &p_response);
if (at_response_error(err, p_response))
goto exit;
if (p_response->p_intermediates && p_response->p_intermediates->line) {
strncpy(profile->BaseBandVersion, p_response->p_intermediates->line, sizeof(profile->BaseBandVersion) - 1);
retVal = 0;
}
exit:
safe_at_response_free(p_response);
return retVal;
}
static int requestGetSIMStatus(SIM_Status *pSIMStatus)
{
int err;
ATResponse *p_response = NULL;
char *cpinLine;
char *cpinResult;
int ret = SIM_NOT_READY;
err = at_send_command_singleline("AT+CPIN?", "+CPIN:", &p_response);
if (at_response_error(err, p_response))
goto done;
switch (at_get_cme_error(p_response))
{
case CME_SUCCESS:
break;
case CME_SIM_NOT_INSERTED:
case CME_OPERATION_NOT_ALLOWED:
case CME_FAILURE:
ret = SIM_ABSENT;
goto done;
default:
ret = SIM_NOT_READY;
goto done;
}
cpinLine = p_response->p_intermediates->line;
err = at_tok_start (&cpinLine);
if (err < 0)
{
ret = SIM_NOT_READY;
goto done;
}
err = at_tok_nextstr(&cpinLine, &cpinResult);
if (err < 0)
{
ret = SIM_NOT_READY;
goto done;
}
if (0 == strcmp (cpinResult, "SIM PIN"))
{
ret = SIM_PIN;
goto done;
}
else if (0 == strcmp (cpinResult, "SIM PUK"))
{
ret = SIM_PUK;
goto done;
}
else if (0 == strcmp (cpinResult, "PH-NET PIN"))
{
return SIM_NETWORK_PERSONALIZATION;
}
else if (0 != strcmp (cpinResult, "READY"))
{
/* we're treating unsupported lock types as "sim absent" */
ret = SIM_ABSENT;
goto done;
}
ret = SIM_READY;
done:
safe_at_response_free(p_response);
*pSIMStatus = ret;
return err;
}
static int requestEnterSimPin(const char *pPinCode) {
int retVal = -1;
int err;
ATResponse *p_response = NULL;
char *cmd = NULL;
asprintf(&cmd, "AT+CPIN=%s", pPinCode);
err = at_send_command(cmd, NULL);
safe_free(cmd);
if (!at_response_error(err, p_response)) {
retVal = 0;
}
safe_at_response_free(p_response);
return retVal;
}
static int requestSetProfile(PROFILE_T *profile) {
int err;
ATResponse *p_response = NULL;
char *cmd = NULL;
const char *new_apn = profile->apn ? profile->apn : "";
const char *new_user = profile->user ? profile->user : "";
const char *new_pd = profile->pd ? profile->pd : "";
const char *ipStr[] = {"NULL", "IPV4", "IPV6", "IPV4V6"};
dbg_time("%s[%d] %s/%s/%s/%d/%s", __func__,
profile->pdp, profile->apn, profile->user, profile->pd,
profile->auth,ipStr[profile->iptype]);
if ( !strcmp(profile->old_apn, new_apn) && !strcmp(profile->old_user, new_user)
&& !strcmp(profile->old_pd, new_pd)
&& profile->old_iptype == profile->iptype
&& profile->old_auth == profile->auth)
{
dbg_time("no need to set skip the rest");
return 0;
}
asprintf(&cmd, "AT+QICSGP=%d,%d,\"%s\",\"%s\",\"%s\",%d",
profile->pdp, profile->iptype, new_apn, new_user, new_pd, profile->auth);
err = at_send_command(cmd, &p_response);
safe_free(cmd);
if (at_response_error(err, p_response)) {
safe_at_response_free(p_response);
asprintf(&cmd, "AT+CGDCONT=%d,\"%s\",\"%s\"", profile->pdp, ipStr[profile->iptype], new_apn);
err = at_send_command(cmd, &p_response);
safe_free(cmd);
}
safe_at_response_free(p_response);
return 1;
}
static int requestGetProfile(PROFILE_T *profile) {
int retVal = -1;
int err;
ATResponse *p_response = NULL;
char *cmd = NULL;
int pdp;
int old_iptype = 1; // 1 ~ IPV4, 2 ~ IPV6, 3 ~ IPV4V6
char *old_apn = "", *old_user = "", *old_pd = "";
int old_auth = 0;
const char *ipStr[] = {"NULL", "IPV4", "IPV6", "IPV4V6"};
if (profile->enable_ipv4 && profile->enable_ipv6)
profile->iptype = 3;
else if (profile->enable_ipv6)
profile->iptype = 2;
else
profile->iptype = 1;
_re_check:
asprintf(&cmd, "AT+QICSGP=%d", profile->pdp);
err = at_send_command_singleline(cmd, "+QICSGP:", &p_response);
safe_free(cmd);
if (err == AT_ERROR_INVALID_RESPONSE && p_response == NULL) {
//bug of RG801H
safe_at_response_free(p_response);
asprintf(&cmd, "AT+QICSGP=%d,%d,\"\",\"\",\"\",0", profile->pdp, profile->iptype);
err = at_send_command(cmd, &p_response);
safe_free(cmd);
if (!at_response_error(err, p_response)) {
safe_at_response_free(p_response);
goto _re_check;
}
}
if (!at_response_error(err, p_response)) {
err = at_tok_scanf(p_response->p_intermediates->line,
"%d%s%s%s%d", &old_iptype, &old_apn, &old_user, &old_pd, &old_auth);
if (err != 4 || pdp != profile->pdp)
goto _error;
}
else {
ATLine *atLine = NULL;
char *cgdcont_iptype = NULL;
safe_at_response_free(p_response);
err = at_send_command_multiline("AT+CGDCONT?", "+CGDCONT:", &p_response);
if (at_response_error(err, p_response))
goto _error;
atLine = p_response->p_intermediates;
while (atLine) {
err = at_tok_scanf(atLine->line, "%d%s%s", &pdp, &cgdcont_iptype, &old_apn);
if (err == 3 && pdp == profile->pdp) {
if (!strcasecmp(cgdcont_iptype, ipStr[3]))
old_iptype = 3;
else if (!strcasecmp(cgdcont_iptype, ipStr[2]))
old_iptype = 2;
else
old_iptype = 1;
break;
}
old_apn = NULL;
atLine = atLine->p_next;
}
}
retVal = 0;
_error:
if (!old_apn) old_apn = "";
if (!old_user) old_user = "";
if (!old_pd) old_pd = "";
strncpy(profile->old_apn, old_apn, sizeof(profile->old_apn));
strncpy(profile->old_user, old_user, sizeof(profile->old_user));
strncpy(profile->old_pd, old_pd, sizeof(profile->old_pd));
profile->old_auth = old_auth;
profile->old_iptype = old_iptype;
dbg_time("%s[%d] %s/%s/%s/%d/%s", __func__,
profile->pdp, profile->old_apn, profile->old_user, profile->old_pd,
profile->old_auth, ipStr[profile->old_iptype]);
safe_at_response_free(p_response);
return retVal;
}
static int requestRegistrationState(UCHAR *pPSAttachedState) {
int retVal = -1;
int err;
ATResponse *p_response = NULL;
ATLine *p_cur;
int i;
int cops_act = -1;
int state = 0, lac = 0, cid = 0, act = 0;
int commas;
char *line;
*pPSAttachedState = 0;
err = at_send_command_multiline(
"AT+COPS=3,0;+COPS?;+COPS=3,1;+COPS?;+COPS=3,2;+COPS?",
"+COPS:", &p_response);
if (at_response_error(err, p_response))
goto error;
/*
AT< +COPS: 0,0,"CHINA MOBILE",13
AT< +COPS: 0,1,"CMCC",13
AT< +COPS: 0,2,"46000",13
AT< OK
*/
retVal = 0;
for (i = 0, p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next, i++) {
err = at_tok_scanf(p_cur->line, "%d%d%s%d", NULL, NULL, NULL, &cops_act);
if (err != 4) goto error;
break;
}
safe_at_response_free(p_response);
switch (cops_act) {
case 2: //UTRAN
case 3: //GSM W/EGPRS
case 4: //UTRAN W/HSDPA
case 5: //UTRAN W/HSUPA
case 6: //UTRAN W/HSDPA and HSUPA
//AT+CGREG GPRS Network Registration Status
err = at_send_command_singleline("AT+CGREG?", "+CGREG:", &p_response);
break;
case 7: //E-UTRAN
case 13: //E-UTRAN-NR dual connectivity
//AT+CEREG EPS Network Registration Status
err = at_send_command_singleline("AT+CEREG?", "+CEREG:", &p_response);
break;
case 10: //E-UTRAN connected to a 5GCN
case 11: //NR connected to a 5GCN
case 12: //NG-RAN
//AT+C5GREG 5GS Network Registration Status
err = at_send_command_singleline("AT+C5GREG?", "+C5GREG:", &p_response);
break;
default:
goto error;
break;
}
if (at_response_error(err, p_response))
goto error;
if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto error;
line = p_response->p_intermediates->line;
commas = at_tok_count(line);
switch (commas) {
case 0: /* +CREG: <stat> */
err = at_tok_nextint(&line, &state);
if (err < 0) goto error;
break;
case 1: /* +CREG: <n>, <stat> */
err = at_tok_scanf(line, "%d%d", NULL, &state);
if (err != 2) goto error;
break;
case 2: /* +CREG: <stat>, <lac>, <cid> */
err = at_tok_scanf(line, "%d%x%x", NULL, &state, &lac, &cid);
if (err != 3) goto error;
break;
case 3: /* +CREG: <n>, <stat>, <lac>, <cid> */
err = at_tok_scanf(line, "%d%d%x%x", NULL, &state, &lac, &cid);
if (err != 4) goto error;
break;
case 4: //<n>, <stat>, <lac>, <cid>, <networkType> */
case 5:
case 6:
case 7:
err = at_tok_scanf(line, "%d%d%x%x%d", NULL, &state, &lac, &cid, &act);
if (err != 5) goto error;
break;
default:
goto error;
}
//dbg_time("state=%d", state);
if (state == 1 || state == 5) { //Registered, home network / roaming
*pPSAttachedState = 1;
}
error:
safe_at_response_free(p_response);
return retVal;
}
static int requestSetupDataCall(PROFILE_T *profile, int curIpFamily) {
int err;
ATResponse *p_response = NULL;
char *cmd = NULL;
ATLine *p_cur = NULL;
int pdp = profile->pdp;
int state = 0;
(void)curIpFamily;
if (strStartsWith(profile->BaseBandVersion, "RG801H") || strStartsWith(profile->BaseBandVersion, "EC200H")) {
//RG801H will miss USB_CDC_NOTIFY_NETWORK_CONNECTION
asprintf(&cmd, "ifconfig %s up", profile->usbnet_adapter);
if (system(cmd)) {};
safe_free(cmd);
}
if (asr_style_atc) {
err = at_send_command_multiline("AT+CGACT?", "+CGACT:", &p_response);
if (at_response_error(err, p_response))
goto _error;
for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next) {
int cid = 0;
state = 0;
err = at_tok_scanf(p_cur->line, "%d%d", &cid, &state);
if (cid == pdp)
break;
else if(state)
state = 0;
}
safe_at_response_free(p_response);
if (state == 0) {
asprintf(&cmd, "AT+CGACT=1,%d", pdp);
err = at_send_command(cmd, &p_response);
safe_free(cmd);
if (at_response_error(err, p_response))
goto _error;
}
}
if(asr_style_atc)
asprintf(&cmd, "AT+QNETDEVCTL=1,%d,%d", pdp, 1);
else
asprintf(&cmd, "AT+QNETDEVCTL=%d,1,%d", pdp, 1);
err = at_send_command(cmd, &p_response);
safe_free(cmd);
if (at_response_error(err, p_response))
goto _error;
if (!asr_style_atc) { //TODO some modems do not sync return setup call resule
int t = 0;
while (t++ < 15) {
asprintf(&cmd, "AT+QNETDEVSTATUS=%d", pdp);
err = at_send_command_singleline(cmd, "+QNETDEVSTATUS", &p_response);
safe_free(cmd);
if (err) goto _error;
if (!at_response_error(err, p_response)) {
break;
}
safe_at_response_free(p_response);
sleep(1);
}
}
//some modem do not report URC
qmidevice_send_event_to_main(RIL_UNSOL_DATA_CALL_LIST_CHANGED);
_error:
safe_at_response_free(p_response);
//dbg_time("%s err=%d", __func__, err);
return err;
}
static int at_netdevstatus(int pdp, unsigned int *pV4Addr) {
int err;
ATResponse *p_response = NULL;
char *cmd = NULL;
char *ipv4_address = NULL;
char *ipv4_gate = NULL;
char *ipv4_DHCP = NULL;
char *ipv4_pDNS = NULL;
char *ipv4_sDNS = NULL;
char *ipv6_address = NULL;
char *ipv6_gate = NULL;
char *ipv6_DHCP = NULL;
char *ipv6_pDNS = NULL;
char *ipv6_sDNS = NULL;
*pV4Addr = 0;
asprintf(&cmd, "AT+QNETDEVSTATUS=%d", pdp);
err = at_send_command_singleline(cmd, "+QNETDEVSTATUS", &p_response);
safe_free(cmd);
if (at_response_error(err, p_response)) goto _error;
if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto _error;
err = at_tok_scanf(p_response->p_intermediates->line, "%s%s%s%s%s%s%s%s%s%s",
&ipv4_address, &ipv4_gate, &ipv4_DHCP, &ipv4_pDNS, &ipv4_sDNS,
&ipv6_address, &ipv6_gate, &ipv6_DHCP, &ipv6_pDNS, &ipv6_sDNS);
if (err > 0) {
#if 0
dbg_time("%s,%s,%s,%s,%s,%s,%s,%s,%s,%s",
ipv4_address, ipv4_gate, ipv4_DHCP, ipv4_pDNS, ipv4_sDNS,
ipv6_address, ipv6_gate, ipv6_DHCP, ipv6_pDNS, ipv6_sDNS);
#endif
if (ipv4_address && ipv4_address[0]) {
int addr[4] = {0, 0, 0, 0};
if (strstr(ipv4_address, ".")) {
sscanf(ipv4_address, "%d.%d.%d.%d", &addr[0], &addr[1], &addr[2], &addr[3]);
}
else {
sscanf(ipv4_address, "%02X%02X%02X%02X", &addr[3], &addr[2], &addr[1], &addr[0]);
}
*pV4Addr = (addr[0]) | (addr[1]<<8) | (addr[2]<<16) | (addr[3]<<24);
}
}
_error:
safe_at_response_free(p_response);
return 0;
}
static int requestQueryDataCall(UCHAR *pConnectionStatus, int curIpFamily) {
int err;
ATResponse *p_response = NULL;
ATLine *p_cur = NULL;
int state = 0;
int bind = 0;
int cid;
int pdp = s_pdp;
unsigned int v4Addr = 0;
(void)curIpFamily;
*pConnectionStatus = QWDS_PKT_DATA_DISCONNECTED;
if (!asr_style_atc) {
err = at_netdevstatus(pdp, &v4Addr);
if (!err && v4Addr) {
*pConnectionStatus = QWDS_PKT_DATA_CONNECTED;
//if (profile->ipv4.Address == v4Addr) {} //TODO
}
return err;
}
err = at_send_command_multiline("AT+QNETDEVCTL?", "+QNETDEVCTL:", &p_response);
if (at_response_error(err, p_response))
goto _error;
for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next)
{
//+QNETDECTL:<op>,<cid>,<urc_en>,<state>
err = at_tok_scanf(p_cur->line, "%d%d%d%d", &bind, &cid, NULL, &state);
if (err != 4 || cid != pdp)
continue;
if (bind != 1)
bind = 0;
}
safe_at_response_free(p_response);
if (bind == 0 || state == 0)
goto _error;
err = at_send_command_multiline("AT+CGACT?", "+CGACT:", &p_response);
if (at_response_error(err, p_response))
goto _error;
for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next)
{
state = 0;
err = at_tok_scanf(p_cur->line, "%d%d", &cid, &state);
if (cid == pdp)
break;
else if(state)
state = 0;
}
safe_at_response_free(p_response);
if (bind && state)
*pConnectionStatus = QWDS_PKT_DATA_CONNECTED;
_error:
safe_at_response_free(p_response);
//dbg_time("%s err=%d, call_state=%d", __func__, err, *pConnectionStatus);
return 0;
}
static int requestDeactivateDefaultPDP(PROFILE_T *profile, int curIpFamily) {
char *cmd = NULL;
int pdp = profile->pdp;
(void)curIpFamily;
if (asr_style_atc)
asprintf(&cmd, "AT+QNETDEVCTL=0,%d,%d", pdp, 0);
else
asprintf(&cmd, "AT+QNETDEVCTL=%d,0,%d", pdp, 0);
at_send_command(cmd, NULL);
safe_free(cmd);
//dbg_time("%s err=%d", __func__, err);
return 0;
}
static int requestGetIPAddress(PROFILE_T *profile, int curIpFamily) {
int err;
ATResponse *p_response = NULL;
char *cmd = NULL;
ATLine *p_cur = NULL;
int pdp = profile->pdp;
unsigned int v4Addr = 0;
unsigned char *v4 = (unsigned char *)&v4Addr;
(void)curIpFamily;
if (!asr_style_atc) {
err = at_netdevstatus(pdp, &v4Addr);
goto _error;
}
asprintf(&cmd, "AT+CGPADDR=%d", profile->pdp);
err = at_send_command_singleline(cmd, "+CGPADDR:", &p_response);
safe_free(cmd);
if (at_response_error(err, p_response))
goto _error;
//+CGPADDR: 1,"10.201.80.91","2409:8930:4B3:41C7:F9B8:3D9B:A2F7:CA96"
for (p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next)
{
char *ipv4 = NULL;
char *ipv6 = NULL;
err = at_tok_scanf(p_cur->line, "%d%s%s", &pdp, &ipv4, &ipv6);
if (err < 2 || pdp != profile->pdp)
continue;
if (ipv4) {
int addr[4] = {0, 0, 0, 0};
sscanf(ipv4, "%d.%d.%d.%d", &addr[0], &addr[1], &addr[2], &addr[3]);
v4[0] = addr[0];
v4[1] = addr[1];
v4[2] = addr[2];
v4[3] = addr[3];
break;
}
}
_error:
if (v4Addr && profile->ipv4.Address != v4Addr) {
profile->ipv4.Address = v4Addr;
dbg_time("%s %d.%d.%d.%d", __func__, v4[0], v4[1], v4[2], v4[3]);
}
//dbg_time("%s err=%d", __func__, err);
return v4Addr ? 0 : -1;
}
static int requestGetSignalInfo(void) {
int retVal = -1;
int err;
ATResponse *p_response = NULL;
int i;
ATLine *p_cur = NULL;
char *rat = NULL;
int cops_act = 0;
int is_nr5g_nsa = 0, nr5g_sa = 0;
int verbose = 0;
err = at_send_command_singleline("at+cops?", "+COPS:", &p_response);
if (at_response_error(err, p_response)) goto _error;
if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto _error;
retVal = 0;
err = at_tok_scanf(p_response->p_intermediates->line, "%d%d%s%d", NULL, NULL, NULL, &cops_act);
if (err != 4) goto _error;
nr5g_sa = (cops_act == 11);
safe_at_response_free(p_response);
err = at_send_command_multiline("at+qeng=\"servingcell\"", "+QENG:", &p_response);
if (at_response_error(err, p_response))
goto _error;
for (i = 0, p_cur = p_response->p_intermediates; p_cur != NULL; p_cur = p_cur->p_next, i++) {
char *type, *state;
err = at_tok_scanf(p_cur->line, "%s%s", &type, &state);
if (err != 2 || strcmp(type, "servingcell"))
continue;
if (!strcmp(state, "SEARCH") || !strcmp(state, "LIMSRV"))
continue;
if (!strcmp(state, "NOCONN") || !strcmp(state, "CONNECT")) {
err = at_tok_scanf(p_cur->line, "%s%s%s", &type, &state, &rat);
if (err != 3)
continue;
}
else {
rat = state;
}
if (!strcmp(rat, "NR5G-SA"))
{
//+QENG: "servingcell",<state>,"NR5G-SA",<duplex_mode>,<MCC>,<MNC>,<cellID>,<PCID>,<TAC>,<ARFCN>,<band>,<NR_DL_bandwidth>,<RSRP>,<RSRQ>,<SINR>,<tx_power>,<srxlev>
//+QENG: "servingcell","NOCONN","NR5G-SA","TDD", 454,12,0,21,4ED,636576,78,3,-85,-11,32,0,5184
struct qeng_servingcell_nr5g_sa {
char *cell_type, *state, *rat, *is_tdd;
int MCC, MNC, cellID/*hex*/;
int PCID, TAC/*hex*/, ARFCN;
int band, NR_DL_bandwidth;
int RSRP, RSRQ, RSSI, SINR;
};
struct qeng_servingcell_nr5g_sa nr5g_sa;
memset(&nr5g_sa, 0, sizeof(nr5g_sa));
err = at_tok_scanf(p_cur->line, "%s,%s,%s,%s,%d,%d,%x,%d,%x,%d,%d,%d,%d,%d,%d,%d",
&nr5g_sa.cell_type, &nr5g_sa.state, &nr5g_sa.rat, &nr5g_sa.is_tdd,
&nr5g_sa.MCC, &nr5g_sa.MNC, &nr5g_sa.cellID, &nr5g_sa.PCID, &nr5g_sa.TAC,
&nr5g_sa.ARFCN, &nr5g_sa.band, &nr5g_sa.NR_DL_bandwidth,
&nr5g_sa.RSRP, &nr5g_sa.RSRQ, &nr5g_sa.RSSI, &nr5g_sa.SINR);
if (err >= 13 && verbose) {
dbg_time("%s,%s,%s,%s,%d,%d,%x,%d,%x,%d,%d,%d,%d,%d,%d,%d",
nr5g_sa.cell_type, nr5g_sa.state, nr5g_sa.rat, nr5g_sa.is_tdd,
nr5g_sa.MCC, nr5g_sa.MNC, nr5g_sa.cellID, nr5g_sa.PCID, nr5g_sa.TAC,
nr5g_sa.ARFCN, nr5g_sa.band, nr5g_sa.NR_DL_bandwidth,
nr5g_sa.RSRP, nr5g_sa.RSRQ, nr5g_sa.RSSI, nr5g_sa.SINR);
}
}
else if (!strcmp(rat, "NR5G-NSA"))
{
//+QENG: "NR5G-NSA",<MCC>,<MNC>,<PCID>,<RSRP>,< SINR>,<RSRQ>,<ARFCN>,<band>
struct qeng_servingcell_nr5g_nsa {
char *mcc, *mnc;
int pcid, rsrp, sinr, rsrq;
};
struct qeng_servingcell_nr5g_nsa nr5g_nsa;
memset(&nr5g_nsa, 0, sizeof(nr5g_nsa));
err = at_tok_scanf(p_cur->line, "%s%s%s%s%d%d%d%dd",
NULL, NULL, &nr5g_nsa.mcc, &nr5g_nsa.mnc, &nr5g_nsa.pcid, &nr5g_nsa.rsrp, &nr5g_nsa.sinr, &nr5g_nsa.rsrq);
if (err == 8 && verbose)
{
dbg_time("mcc=%s, mnc=%s, pcid=%d, rsrp=%d, sinr=%d, rsrq=%d",
nr5g_nsa.mcc, nr5g_nsa.mnc, nr5g_nsa.pcid, nr5g_nsa.rsrp, nr5g_nsa.sinr, nr5g_nsa.rsrq);
}
is_nr5g_nsa = 1;
}
else if (!strcmp(rat, "LTE"))
{
//+QENG: "LTE",<is_tdd>,<MCC>,<MNC>,<cellID>,<PCID>,<earfcn>,<freq_band_ind>,<UL_bandwidth>,<DL_bandwidth>,<TAC>,<RSRP>,<RSRQ>,<RSSI>,<SINR>,<CQI>,<tx_power>,<srxlev>
struct qeng_servingcell_lte {
char *is_tdd, *mcc, *mnc;
int cellID/*hex*/, pcid, earfcn, freq_band_ind, ul_bandwidth, dl_bandwidth;
int tac/*hex*/, rsrp, rsrq, rssi, sinr, cqi,tx_power,srxlev;
};
struct qeng_servingcell_lte lte;
memset(&lte, 0, sizeof(lte));
if (!strcmp(rat, state))
err = at_tok_scanf(p_cur->line, "%s%s%s%s%s%x%d%d%d%d%d%x%d%d%d%d%d%d%d",
NULL, NULL, &lte.is_tdd, &lte.mcc, &lte.mnc,
&lte.cellID, &lte.pcid, &lte.earfcn, &lte.freq_band_ind, &lte.ul_bandwidth, &lte.dl_bandwidth,
&lte.tac, &lte.rsrp, &lte.rsrq, &lte.rssi, &lte.sinr, &lte.cqi, &lte.tx_power, &lte.srxlev);
else
err = at_tok_scanf(p_cur->line, "%s%s%s%s%s%s%x%d%d%d%d%d%x%d%d%d%d%d%d%d",
NULL, NULL, NULL, &lte.is_tdd, &lte.mcc, &lte.mnc,
&lte.cellID, &lte.pcid, &lte.earfcn, &lte.freq_band_ind, &lte.ul_bandwidth, &lte.dl_bandwidth,
&lte.tac, &lte.rsrp, &lte.rsrq, &lte.rssi, &lte.sinr, &lte.cqi, &lte.tx_power, &lte.srxlev);
if (err >= 18 && verbose)
{
dbg_time("is_tdd=%s, mcc=%s, mnc=%s", lte.is_tdd, lte.mcc, lte.mnc);
dbg_time("cellID=%x, pcid=%d, earfcn=%d", lte.cellID, lte.pcid, lte.earfcn);
dbg_time("freq_band_ind=%d, ul_bandwidth=%d, dl_bandwidth=%d", lte.freq_band_ind, lte.ul_bandwidth, lte.dl_bandwidth);
dbg_time("tac=%x, rsrp=%d, rsrq=%d, rssi=%d, sinr=%d", lte.tac, lte.rsrp, lte.rsrq, lte.rssi, lte.sinr);
dbg_time("cqi=%d, tx_power=%d, earfcn=%d", lte.cqi, lte.tx_power, lte.srxlev);
}
}
}
if (is_nr5g_nsa) {
int endc_avl, plmn_info_list_r15_avl, endc_rstr, nr5g_basic;
is_nr5g_nsa = 0;
safe_at_response_free(p_response);
err = at_send_command_multiline("at+qendc", "+QENDC:", &p_response);
if (at_response_error(err, p_response)) goto _error;
if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto _error;
err = at_tok_scanf(p_response->p_intermediates->line, "%d%d%d%d",
&endc_avl, &plmn_info_list_r15_avl, &endc_rstr, &nr5g_basic);
if (err == 4 && nr5g_basic) {
is_nr5g_nsa = 1;
}
}
if (verbose)
dbg_time("cops_act=%d, nr5g_nsa=%d, nr5g_sa=%d", cops_act, is_nr5g_nsa, nr5g_sa);
_error:
safe_at_response_free(p_response);
return retVal;
}
static int requestGetICCID(void) {
int retVal = -1;
int err;
ATResponse *p_response = NULL;
char *iccid;
err = at_send_command_singleline("AT+QCCID", "+QCCID:", &p_response);
if (at_response_error(err, p_response)) goto _error;
if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto _error;
err = at_tok_scanf(p_response->p_intermediates->line, "%s", &iccid);
if (err != 1) goto _error;
if (iccid && iccid[0]) {
dbg_time("%s %s", __func__, iccid);
retVal = 0;
}
_error:
safe_at_response_free(p_response);
return retVal;
}
static int requestGetIMSI(void) {
int retVal = -1;
int err;
ATResponse *p_response = NULL;
char *imsi;
err = at_send_command_numeric("AT+CIMI", &p_response);
if (at_response_error(err, p_response)) goto _error;
if (!p_response->p_intermediates || !p_response->p_intermediates->line) goto _error;
imsi = p_response->p_intermediates->line;
if (imsi) {
dbg_time("%s %s", __func__, imsi);
retVal = 0;
}
_error:
safe_at_response_free(p_response);
return retVal;
}
const struct request_ops atc_request_ops = {
.requestBaseBandVersion = requestBaseBandVersion,
.requestGetSIMStatus = requestGetSIMStatus,
.requestEnterSimPin = requestEnterSimPin,
.requestSetProfile = requestSetProfile,
.requestGetProfile = requestGetProfile,
.requestRegistrationState = requestRegistrationState,
.requestSetupDataCall = requestSetupDataCall,
.requestQueryDataCall = requestQueryDataCall,
.requestDeactivateDefaultPDP = requestDeactivateDefaultPDP,
.requestGetIPAddress = requestGetIPAddress,
.requestGetSignalInfo = requestGetSignalInfo,
.requestGetICCID = requestGetICCID,
.requestGetIMSI = requestGetIMSI,
};