533 lines
15 KiB
C
533 lines
15 KiB
C
/****************************************************************************
|
|
*
|
|
* Copy right: 2019-, Copyrigths of EigenComm Ltd.
|
|
* File name: ctw_tcp.c
|
|
* Description: EC618 CTWING http access source file
|
|
* History: Rev1.0 2022-03-01
|
|
*
|
|
****************************************************************************/
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include DEBUG_LOG_HEADER_FILE
|
|
#include "cmsis_os2.h"
|
|
#include "at_util.h"
|
|
#include "ctw_tcp.h"
|
|
#include "ps_event_callback.h"
|
|
#include "ps_lib_api.h"
|
|
#include "netdb.h"
|
|
#include "sockets.h"
|
|
|
|
#define TCP_HOST "tcp.ctwing.cn"
|
|
#define TCP_PORT "8996"
|
|
|
|
#define HEARTBEAT_TIME (300)
|
|
|
|
bool ctwTimeExpired(uint32_t time)
|
|
{
|
|
uint32_t now = 0;
|
|
now = osKernelGetTickCount()/1000;
|
|
if(now > time)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
static BOOL prepareBuffer(UINT8* outBuf, UINT16* cursor, char* inbuf, UINT8 inbuflen, UINT16 outBufSize)
|
|
{
|
|
UINT8 copyLen;
|
|
UINT16 outbufCursor = *cursor;
|
|
|
|
if(inbuflen == 0){
|
|
return FALSE;
|
|
}
|
|
|
|
outBuf[outbufCursor++] = 0;
|
|
outBuf[outbufCursor++] = inbuflen;
|
|
do {
|
|
if((outBufSize - outbufCursor) >= inbuflen) {
|
|
copyLen = inbuflen;
|
|
}else{
|
|
return FALSE;
|
|
}
|
|
memcpy(outBuf + outbufCursor, inbuf, copyLen);
|
|
outbufCursor += copyLen;
|
|
inbuflen -= copyLen;
|
|
}while(inbuflen);
|
|
|
|
*cursor = outbufCursor;
|
|
return TRUE;
|
|
}
|
|
|
|
static void ctwTcpGetRegParam(ctwTcpRegParam_t* pRegParam)
|
|
{
|
|
UeExtStatusInfo statusInfo;
|
|
CmsRetId ret = CMS_RET_SUCC;
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpGetRegParam_1, P_INFO, "get imei and iccid");
|
|
OsaGetImeiNumSync(pRegParam->imei);
|
|
appGetIccidNumSync(pRegParam->iccid);
|
|
appGetImsiNumSync(pRegParam->imsi);
|
|
ret = appGetUeExtStatusInfoSync(UE_EXT_STATUS_PHY, &statusInfo);
|
|
if(ret == CMS_RET_SUCC){
|
|
pRegParam->phyCellId = statusInfo.phyStatus.phyCellId;
|
|
pRegParam->rsrp = statusInfo.phyStatus.sRsrp/100;
|
|
pRegParam->snr = statusInfo.phyStatus.snr;
|
|
pRegParam->txPower = statusInfo.phyStatus.txPower;
|
|
}
|
|
}
|
|
|
|
static int ctwTcpConnectTimeout(int32_t connectFd, uint32_t timeout)
|
|
{
|
|
fd_set writeSet;
|
|
fd_set errorSet;
|
|
FD_ZERO(&writeSet);
|
|
FD_ZERO(&errorSet);
|
|
FD_SET(connectFd,&writeSet);
|
|
FD_SET(connectFd,&errorSet);
|
|
struct timeval tv;
|
|
tv.tv_sec = timeout;
|
|
tv.tv_usec = 0;
|
|
int ret;
|
|
|
|
ret = select(connectFd+1, NULL, &writeSet, &errorSet, &tv);
|
|
|
|
if(ret <= 0)
|
|
{
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
if(FD_ISSET(connectFd, &errorSet))
|
|
{
|
|
int mErr = sock_get_errno(connectFd);
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpConnectTimeout_2, P_INFO, "select error fd set get errno=%d", mErr);
|
|
if(mErr)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
else if(FD_ISSET(connectFd, &writeSet))
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpConnectTimeout_3, P_INFO, "errno=115(EINPROGRESS) connect success in time(25s)");
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int ctwTcpConnectSocket(int32_t socket, struct sockaddr *addr, int32_t addrlen)
|
|
{
|
|
|
|
int ret = 0;
|
|
int32_t errCode;
|
|
|
|
if(connect(socket,addr,addrlen) == 0)
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpConnectSocket_1, P_INFO, "httpConnectSocket connect success");
|
|
}
|
|
else
|
|
{
|
|
errCode = sock_get_errno(socket);
|
|
if(errCode == EINPROGRESS)
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpConnectSocket_2, P_INFO, "httpConnectSocket connect is ongoing");
|
|
|
|
ret = ctwTcpConnectTimeout(socket, 25);
|
|
if(ret == 0)
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpConnectSocket_3, P_INFO, "httpConnectSocket connect success");
|
|
}
|
|
else
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpConnectSocket_4, P_INFO, "httpConnectSocket connect fail,error code %d", errCode);
|
|
if(socket_error_is_fatal(errCode))
|
|
{
|
|
ret = -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpConnectSocket_5, P_INFO, "httpConnectSocket connect fail %d",errCode);
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int ctwTcpConnect(char* host, char* port)
|
|
{
|
|
struct timeval timeout_recv;
|
|
struct addrinfo hints, *addr_list, *p;
|
|
//char portStr[10] = {0};
|
|
int socket = -1;
|
|
|
|
timeout_recv.tv_sec = 1;
|
|
timeout_recv.tv_usec = 0;
|
|
|
|
memset( &hints, 0, sizeof( hints ) );
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
|
|
//snprintf(portStr, sizeof(port), "%d", port) ;
|
|
|
|
if (getaddrinfo( host, port , &hints, &addr_list ) != 0 ) {
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpConnect_1, P_INFO, "unresolved dns");
|
|
return -1;
|
|
}
|
|
|
|
//try address one by one until sucess
|
|
for ( p = addr_list; p != NULL; p = p->ai_next ) {
|
|
socket = (int) socket( p->ai_family, p->ai_socktype,p->ai_protocol);
|
|
if ( socket < 0 ) {
|
|
continue;//try new one
|
|
}
|
|
|
|
setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &timeout_recv, sizeof(timeout_recv));;
|
|
|
|
int32_t flags = fcntl( socket, F_GETFL, 0);
|
|
if(flags < 0)
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpConnect_2, P_INFO, "CreateSocket get file cntrl flags fail");
|
|
close(socket);
|
|
socket = -1;
|
|
continue;//try new one
|
|
}
|
|
|
|
fcntl(socket, F_SETFL, flags|O_NONBLOCK); //set socket as nonblock for connect timeout
|
|
|
|
if ( ctwTcpConnectSocket(socket, p->ai_addr, (int32_t)p->ai_addrlen ) == 0 ) {
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpConnect_3, P_INFO, "connect success");
|
|
fcntl(socket, F_SETFL, flags&~O_NONBLOCK); //connect success recover to block mode
|
|
break;
|
|
}
|
|
|
|
fcntl(socket, F_SETFL, flags&~O_NONBLOCK); //connect fail recover to block mode
|
|
|
|
close(socket );
|
|
socket = -1;
|
|
}
|
|
|
|
freeaddrinfo( addr_list );
|
|
|
|
return socket;
|
|
|
|
}
|
|
|
|
bool ctwTcpEncodeLoginData(uint8_t** ptr, uint16_t* ptrLen, MWNvmCfgCtwTcpParam* pCtwTcpParam, ctwTcpRegParam_t* pRegParam)
|
|
{
|
|
bool ret = true;
|
|
uint16_t i = 0;
|
|
uint8_t deviceIdLen = 0, passwordLen = 0, moduleLen = 0, chiptypeLen = 0, softversionLen = 0, iccidLen = 0;
|
|
char str[10] = {0};
|
|
uint8_t* ptrStream = NULL;
|
|
MWNvmCfgCtwParamCfg ctwParamCfg = {0};
|
|
mwNvmCfgGetCtwParamConfig(&ctwParamCfg);
|
|
deviceIdLen = strlen(pCtwTcpParam->deviceId);
|
|
passwordLen = strlen(pCtwTcpParam->password);
|
|
moduleLen = strlen(ctwParamCfg.module);
|
|
chiptypeLen = strlen(ctwParamCfg.chipType);
|
|
softversionLen = strlen(ctwParamCfg.softVersion);
|
|
iccidLen = strlen(pRegParam->iccid);
|
|
|
|
*ptrLen = 27 + deviceIdLen + passwordLen + 3 + moduleLen + chiptypeLen + softversionLen + 65; //version=2.0 15*2+20+3*3+6 imsi/imei iccid rsrp/sinr/txpower cellid
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpEncodeLoginData_1, P_INFO, "estimate login data len:%d", *ptrLen);
|
|
|
|
ptrStream = (uint8_t*)mallocEc(*ptrLen);
|
|
if(ptrStream == NULL)
|
|
{
|
|
ret = false;
|
|
goto exit;
|
|
}
|
|
*ptr = ptrStream;
|
|
//message type
|
|
ptrStream[0] = LOGIN_MESSAGE;
|
|
i++;
|
|
|
|
//deviceId
|
|
prepareBuffer(ptrStream, &i, pCtwTcpParam->deviceId, deviceIdLen, *ptrLen);
|
|
|
|
//password
|
|
prepareBuffer(ptrStream, &i, pCtwTcpParam->password, passwordLen, *ptrLen);
|
|
|
|
//version
|
|
prepareBuffer(ptrStream, &i, "2.0", 3, *ptrLen);
|
|
|
|
//module
|
|
prepareBuffer(ptrStream, &i, ctwParamCfg.module, moduleLen, *ptrLen);
|
|
|
|
//chiptype
|
|
prepareBuffer(ptrStream, &i, ctwParamCfg.chipType, chiptypeLen, *ptrLen);
|
|
|
|
//softversion
|
|
prepareBuffer(ptrStream, &i, ctwParamCfg.softVersion, softversionLen, *ptrLen);
|
|
|
|
//imei
|
|
prepareBuffer(ptrStream, &i, pRegParam->imei, 15, *ptrLen);
|
|
|
|
//iccid
|
|
prepareBuffer(ptrStream, &i, pRegParam->iccid, iccidLen, *ptrLen);
|
|
|
|
//imsi
|
|
prepareBuffer(ptrStream, &i, pRegParam->imsi, 15, *ptrLen);
|
|
|
|
//rsrp
|
|
snprintf(str, 10, "%d", pRegParam->rsrp) ;
|
|
prepareBuffer(ptrStream, &i, str, strlen(str), *ptrLen);
|
|
|
|
//sinr
|
|
snprintf(str, 10, "%d", pRegParam->snr) ;
|
|
prepareBuffer(ptrStream, &i, str, strlen(str), *ptrLen);
|
|
|
|
//txpower
|
|
snprintf(str, 10, "%d", pRegParam->txPower) ;
|
|
prepareBuffer(ptrStream, &i, str, strlen(str), *ptrLen);
|
|
|
|
//cellid
|
|
snprintf(str, 10, "%d", pRegParam->phyCellId) ;
|
|
prepareBuffer(ptrStream, &i, str, strlen(str), *ptrLen);
|
|
|
|
*ptrLen = i;
|
|
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpEncodeLoginData_2, P_INFO, "login data len:%d", *ptrLen);
|
|
exit:
|
|
return ret;
|
|
}
|
|
|
|
bool ctwTcpSendPacket(ctwTcpContext_t* pContext, uint8_t* buf, int len)
|
|
{
|
|
int32_t waitToSend = len;
|
|
int32_t hasSend = 0;
|
|
fd_set writeFs;
|
|
struct timeval tv;
|
|
uint32_t preSelTime = 0,passedTime=0;
|
|
uint8_t ret=0;
|
|
|
|
tv.tv_sec = 2;
|
|
tv.tv_usec = 0;
|
|
|
|
FD_ZERO(&writeFs);
|
|
if(pContext->socket >= 0)
|
|
FD_SET(pContext->socket, &writeFs);
|
|
|
|
|
|
do
|
|
{
|
|
tv.tv_usec -= (passedTime*1000);
|
|
preSelTime=osKernelGetTickCount()/portTICK_PERIOD_MS;
|
|
ret = select(pContext->socket + 1, NULL, &writeFs, NULL, &tv);
|
|
if(ret>0)
|
|
{
|
|
hasSend = send(pContext->socket, (buf + len - waitToSend), waitToSend, MSG_DONTWAIT);
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpSendPacket_1, P_INFO, "%d bytes data has sent to ctwing", hasSend);
|
|
if(hasSend > 0)
|
|
{
|
|
waitToSend -= hasSend;
|
|
}
|
|
else if(hasSend == 0)
|
|
{
|
|
pContext->lastSendExpireTime = osKernelGetTickCount()/1000 + HEARTBEAT_TIME - 20;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpSendPacket_2, P_INFO, "send failed (errno:%d)", errno);
|
|
return false;
|
|
}
|
|
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpSendPacket_3, P_INFO, "preSelTime: %d,current tick %d", preSelTime,osKernelGetTickCount());
|
|
passedTime = (osKernelGetTickCount()-preSelTime)>0?(osKernelGetTickCount() - preSelTime)/portTICK_PERIOD_MS:(0xFFFFFFFF - preSelTime + osKernelGetTickCount())/portTICK_PERIOD_MS;
|
|
|
|
}
|
|
else//slelct return <=0 select timeout or error
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
}while(waitToSend>0);
|
|
|
|
return true;
|
|
}
|
|
|
|
static int32_t ctwTcpRecv(int32_t socket, uint8_t* buf, uint16_t len)
|
|
{
|
|
int32_t readLen = 0;
|
|
int32_t ret,mErr;
|
|
while (readLen < len)
|
|
{
|
|
ret = recv(socket, buf+readLen, len-readLen, 0);
|
|
mErr = sock_get_errno(socket);
|
|
if (ret <= 0)
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpRecv_1, P_INFO, "recv [blocking] return:-%d", -ret);
|
|
if(socket_error_is_fatal(mErr))//maybe closed or reset by peer
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpRecv_1_1, P_INFO, "fatal error");
|
|
return ret;
|
|
}
|
|
else
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpRecv_1_2, P_INFO, "not fatal error");
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpRecv_3, P_INFO, "recv %d bytes", ret);
|
|
readLen += ret;
|
|
}
|
|
}
|
|
return readLen;
|
|
}
|
|
|
|
|
|
int32_t ctwTcpReadPacket(ctwTcpContext_t* pContext)
|
|
{
|
|
uint16_t len = 0, resultCode = 0;
|
|
uint8_t type = 0, buffer[3] = {0};
|
|
uint8_t *buf = NULL;
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpReadPacket_0, P_INFO, "enter");
|
|
message_pack_t message;
|
|
int32_t rc = ctwTcpRecv(pContext->socket, &type, 1);
|
|
if (rc < 0) return rc;
|
|
else if(rc == 1)
|
|
{
|
|
pContext->lastRecvExpireTime = osKernelGetTickCount()/1000 + HEARTBEAT_TIME - 20;
|
|
switch(type)
|
|
{
|
|
case DOWNSTREAM_MESSAGE:
|
|
{
|
|
rc = ctwTcpRecv(pContext->socket, buffer, 2);
|
|
if(rc != 2)
|
|
{
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
len = (((buffer[0] | len) << 8) | (buffer[1] | 0x0000));
|
|
buf = mallocEc(len + 1);
|
|
if(buf == NULL)
|
|
{
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
memset(buf, 0x00, len + 1);
|
|
rc = ctwTcpRecv(pContext->socket, buf, len);
|
|
if(rc != len)
|
|
{
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
message.message = buf;
|
|
message.msgLen = len;
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpReadPacket_1, P_INFO, "recive dwon mesage:%d", len);
|
|
pContext->messageHandlerCb(&message);
|
|
|
|
rc = 0;
|
|
break;
|
|
}
|
|
case LOGIN_ACK:
|
|
{
|
|
if(2 != ctwTcpRecv(pContext->socket,buffer,2))
|
|
{
|
|
rc = -1;
|
|
goto exit;
|
|
}
|
|
|
|
resultCode = ((buffer[0] << 8) | buffer[1]);
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpReadPacket_2, P_INFO, "receive login ack resultCode:%d", resultCode);
|
|
rc = resultCode;
|
|
goto exit;
|
|
break;
|
|
}
|
|
case HEARTBEAT_ACK:
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpReadPacket_3, P_INFO, "Ping ack Received!");
|
|
pContext->waitPingAck = 0;
|
|
rc = 1;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpReadPacket_4, P_INFO, "receive unknown msg!");
|
|
rc = -1;
|
|
goto exit;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
bool ctwTcpUpdata(ctwTcpContext_t* pContext, uint8_t* buf, uint16_t len)
|
|
{
|
|
uint8_t *buffer = NULL;
|
|
bool ret = false;
|
|
|
|
buffer = mallocEc(len+3);
|
|
buffer[0] = UPSTREAM_MESSAGE;
|
|
buffer[1] = len>>8;
|
|
buffer[2] = len>>0;
|
|
memcpy(buffer+3, buf, len);
|
|
if(false == ctwTcpSendPacket(pContext, buffer, len+3))
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpUpdata_1, P_INFO, "send up data fail");
|
|
ret = false;
|
|
}
|
|
else
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpUpdata_2, P_INFO, "send up data success");
|
|
ret = true;
|
|
}
|
|
freeEc(buf);
|
|
freeEc(buffer);
|
|
return ret;
|
|
}
|
|
|
|
|
|
bool ctwTcpLogin(MWNvmCfgCtwTcpParam* pCtwTcpParam, ctwTcpContext_t* pContext)
|
|
{
|
|
uint8_t *buffer = NULL;
|
|
uint16_t bufLen = 0;
|
|
int32_t rc = 0;
|
|
ctwTcpRegParam_t pRegParam = {0};
|
|
int32_t socket = -1;
|
|
|
|
ctwTcpGetRegParam(&pRegParam);
|
|
if(false == ctwTcpEncodeLoginData(&buffer, &bufLen, pCtwTcpParam, &pRegParam))
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpLogin_2, P_INFO, "encode login data fail");
|
|
return false;
|
|
}
|
|
|
|
socket = ctwTcpConnect(TCP_HOST, TCP_PORT);
|
|
if(socket < 0)
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpLogin_1, P_INFO, "tcp connect fail");
|
|
freeEc(buffer);
|
|
return false;
|
|
}
|
|
pContext->socket = socket;
|
|
|
|
if(false == ctwTcpSendPacket(pContext, buffer, bufLen))
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpLogin_3, P_INFO, "send packet fail");
|
|
freeEc(buffer);
|
|
return false;
|
|
}
|
|
freeEc(buffer);
|
|
rc = ctwTcpReadPacket(pContext);
|
|
|
|
if(rc != 0)
|
|
{
|
|
ECPLAT_PRINTF(UNILOG_CTWING, ctwTcpLogin_4, P_INFO, "login ack fail,rc:%d",rc);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|