/**************************************************************************** * * 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 #include #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; }