blob: 238ab163f298260d4e0c9c23d1c3645c5b77cba7 [file] [log] [blame]
/******************************************************************************
*(C) Copyright 2011 Marvell International Ltd.
* All Rights Reserved
******************************************************************************/
/*--------------------------------------------------------------------------------------------------------------------
* -------------------------------------------------------------------------------------------------------------------
*
* Filename: tcp_api.c
*
* Description: The APIs to handle TCP operations.
*
* History:
* Aug, 13 2012 - Haili Wang(hlw@marvell.com) Creation of file
*
* Notes:
*
******************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <arpa/inet.h>
#include "linux_types.h"
#include "utlEventHandler.h"
#include "pxa_dbg.h"
#include "tcp_api.h"
#include "tcp_state_machine.h"
#include "sulog_lib.h"
static struct sulog_data *p_usr_data = NULL;
void SetUserData(void *data)
{
p_usr_data = (struct sulog_data *) data;
}
static int tcpfd = -1;
static int sockfd = -1;
static utlEventHandlerId_T tcpHandler;
static utlEventHandlerId_T acceptedHandler;
//static DIAG_COM_RX_Packet_Info RxPacket;
static unsigned char RxBuffer[TTY_MAX_LENGTH];
static int RxOffset = 0;
int ifc_init(void);
int ifc_init(void) { return 0;}
void ifc_close(void);
void ifc_close(void) {}
int ifc_up(const char *name);
int ifc_up(const char *name __attribute__ ((unused))) { return 0; }
int ifc_set_addr(const char *name, unsigned addr);
int ifc_set_addr(const char *name __attribute__ ((unused)), unsigned addr __attribute__ ((unused))) { return 0; }
int ifc_set_mask(const char *name, unsigned mask);
int ifc_set_mask(const char *name __attribute__ ((unused)), unsigned mask __attribute__ ((unused))) { return 0; }
static utlReturnCode_T ReceiveDataFromTCP(const utlEventHandlerType_T handler_type UNUSED,
const utlEventHandlerType_T event_type UNUSED,
const int fd,
const utlRelativeTime_P2c period_p UNUSED,
void *arg_p UNUSED)
{
int dwBytesTransferred, offset;
UINT8* transferBuffer = RxBuffer;
offset = RxOffset;
if ((dwBytesTransferred = read(fd, transferBuffer + offset, sizeof(RxBuffer) - offset)) > 0)
{
DBGMSG("----Data received from TCP:%d, len:%d\n", fd, dwBytesTransferred);
handle_sulog_cmd_data(p_usr_data, transferBuffer, &offset, dwBytesTransferred);
if ((unsigned) offset + 4 > sizeof(RxBuffer))
{
DBGMSG("----offset %d too large, reset to 0\n", offset);
offset = 0;
}
RxOffset = offset;
return utlSUCCESS;
}
else if(dwBytesTransferred == 0)
{
DBGMSG("***** TCP Connection closed by peer *****\r\n");
TCPDisconnect();
return utlSUCCESS;
}
else
{
if(errno == ECONNRESET)
{
DBGMSG("***** TCP Connection reset by peer *****\r\n");
TCPDisconnect();
return utlSUCCESS;
}
ERRMSG("***** TCP Read failed:%d,%s. *****\r\n", dwBytesTransferred,strerror(errno));
return utlFAILED;
}
return utlSUCCESS;
}
int createListenSocket(void)
{
int addr_reuse = 1;
F_ENTER();
while(sockfd < 0)
{
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
ERRMSG("create tcp socket error:%s\n", strerror(errno));
}
}
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &addr_reuse, sizeof(addr_reuse)) < 0)
{
ERRMSG("setsockopt : %s", strerror(errno));
}
F_LEAVE();
return sockfd;
}
int configInterface(void)
{
// ifconfig rndis0 192.168.1.101 netmask 255.255.255.0 up
in_addr_t addr, netmask;
if(p_usr_data == NULL || p_usr_data->network_settings.is_local)
return 0;
addr = inet_addr(p_usr_data->network_settings.diag_ip);
netmask = inet_addr(p_usr_data->network_settings.diag_netmask);
ifc_init();
if(ifc_up(p_usr_data->network_settings.net_dev))
{
ERRMSG("failed to turn on interface");
ifc_close();
return -1;
}
if(ifc_set_addr(p_usr_data->network_settings.net_dev, addr))
{
ERRMSG("failed to set ipaddr");
ifc_close();
return -1;
}
if(ifc_set_mask(p_usr_data->network_settings.net_dev, netmask))
{
ERRMSG("failed to set netmask");
ifc_close();
return -1;
}
ifc_close();
return 0;
}
static int acceptTCPSocket(void)
{
struct sockaddr_in peer_addr;
int addr_size = 0;
int ret;
int flags;
ret = accept(sockfd, (struct sockaddr *)&peer_addr, (socklen_t*)&addr_size);
if( ret < 0)
{
ERRMSG("accept tcp socket on %s:%d error:%s\n", p_usr_data ? p_usr_data->network_settings.diag_ip : NULL, p_usr_data ? p_usr_data->network_settings.port : 0, strerror(errno));
}
else
{
DBGMSG("client %s:%d is accepted!\n", inet_ntoa(peer_addr.sin_addr), ntohs(peer_addr.sin_port));
tcpfd = ret;
memset(RxBuffer, 0, sizeof(RxBuffer));
RxOffset = 0;
tcpHandler = utlSetFdEventHandler(utlEVENT_HANDLER_TYPE_READ, utlEVENT_HANDLER_PRIORITY_MEDIUM, tcpfd, ReceiveDataFromTCP, NULL);
/* Since blocking write behavior is preferred,
* undo O_NONBLOCK flag set in utlSetFdEventHandler
*/
if ((flags = fcntl(tcpfd, F_GETFL)) != -1)
{
flags &= ~O_NONBLOCK;
if(fcntl(tcpfd, F_SETFL, flags) == -1)
{
ERRMSG("F_SETFL error: %s", strerror(errno));
}
}
else
{
ERRMSG("F_GETFL error: %s", strerror(errno));
}
}
return ret;
}
static utlReturnCode_T AcceptClient(const utlEventHandlerType_T handler_type UNUSED,
const utlEventHandlerType_T event_type UNUSED,
const int fd UNUSED,
const utlRelativeTime_P2c period_p UNUSED,
void *arg_p UNUSED)
{
if (acceptTCPSocket() < 0)
{
return utlFAILED;
}
else
{
TCPAccept();
return utlSUCCESS;
}
}
int prepareListenSocket(void)
{
struct sockaddr_in serv_addr;
int ret;
if (p_usr_data == NULL)
{
return -1;
}
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(p_usr_data->network_settings.port);
serv_addr.sin_addr.s_addr = inet_addr(p_usr_data->network_settings.diag_ip);
ret = bind(sockfd,(const struct sockaddr*)&serv_addr,sizeof(serv_addr));
if(ret < 0)
{
ERRMSG("bind tcp socket on %s:%d error:%s\n", p_usr_data->network_settings.diag_ip, p_usr_data->network_settings.port,strerror(errno));
}
else
{
ret = listen(sockfd, 1);
if(ret < 0)
{
ERRMSG("listen to tcp socket on %s:%d error:%s\n", p_usr_data->network_settings.diag_ip, p_usr_data->network_settings.port,strerror(errno));
}
else
{
acceptedHandler = utlSetFdEventHandler(utlEVENT_HANDLER_TYPE_READ, utlEVENT_HANDLER_PRIORITY_MEDIUM, sockfd, AcceptClient, NULL);
}
}
return ret;
}
void closeListenSocket(void)
{
F_ENTER();
utlDeleteEventHandler(acceptedHandler);
close(sockfd);
sockfd = -1;
}
void closeDataSocket(void)
{
F_ENTER();
utlDeleteEventHandler(tcpHandler);
close(tcpfd);
tcpfd = -1;
#if 0
free(RxPacket.buffer);
#endif
}
int SendTCPdata(const unsigned char * data, unsigned len)
{
if(tcpfd > 0)
{
ssize_t count = len;
int ret, retry = 5;
while (count > 0)
{
ret = send(tcpfd, data, count, MSG_NOSIGNAL);
if (ret == count)
{
count = 0;
break; /* send all bytes successfully */
}
else if (ret >= 0)
{
data += ret;
count -= ret;
if(--retry == 0)
{
ERRMSG("tcp send incomplete: %d of %d bytes sent\n", len - count, len);
break;
}
continue;
}
else if (ret < 0)
{
ERRMSG("tcp send error: %s\n", strerror(errno));
return -1;
}
}
return len - count;
}
else
return -1;
}
void InitTCP(void)
{
F_ENTER();
InitTCPMachine();
F_LEAVE();
}
static int IsNetIfUp(void)
{
struct ifreq ifr;
int s, err;
if ((s = socket( AF_INET, SOCK_DGRAM, 0)) < 0)
{
err = errno;
ERRMSG("%s:socket error:%s", __func__, strerror(err));
return -1;
}
ifr.ifr_name[0] = '\0';
if (p_usr_data)
{
strncat(ifr.ifr_name, p_usr_data->network_settings.net_dev, IFNAMSIZ - 1);
}
if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
{
err = errno;
ERRMSG("%s:%s", __func__, strerror(err));
close(s);
return -1;
}
close(s);
s = ifr.ifr_flags & IFF_UP ? 1 : 0;
DBGMSG("net interface %s is %s", ifr.ifr_name, s ? "up" : "down");
return s;
}
void handleTCPUevent(int status)
{
if (p_usr_data && !p_usr_data->network_settings.is_local && status == 0) //remove
{
if(IsNetIfUp() == 0)
{
TCPDisconnect();
}
}
}