2025-04-10 17:31:33 +08:00

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;
}