/*
 * (c)COPYRIGHT
 * ALL RIGHT RESERVED
 *
 * FileName : socket.c
 * Revision History :
 * ----------  -------  -----------  -----------------------------------------------------
 * Date        version  Name         Description
 * ----------  -------  -----------  -----------------------------------------------------
 * 08/28/2006  1.0.1    Bong         support W3150A+
 * ----------  -------  -----------  -----------------------------------------------------
 * 11/10/2006  1.0.2    Jung         modify interrupt part of send(), sendto(), igmpsend()
 * ---------------------------------------------------------------------------------------
 * 11/20/2006  1.0.3    Jung         modify interrupt part of send(), sendto(), igmpsend()
 * ---------------------------------------------------------------------------------------
 * 12/27/2006  1.0.4    Hyung        modify interrupt part of send(), sendto(), igmpsend()
 * ---------------------------------------------------------------------------------------
 *
 */
#include "socket.h"

uint16 local_port;
/**
* Internal Functions
*/

/*
*********************************************************************
This Socket function initialize the channel in perticular mode, and set the port
and wait for W3150A+ done it.
And the Parameters "s" is for socket number, "protocol" is for socket protocol,
port is the source port for the socket and flag is the option for the socket.
This function return 1 for sucess else 0.
*********************************************************************
*/

uint8 socket(SOCKET s, uint8 protocol, uint16 port, uint8 flag)
{
	uint8 ret;
#ifdef __DEF_IINCHIP_DBG__
	printf("socket()\r\n");
#endif
	if ((protocol == Sn_MR_TCP) || (protocol == Sn_MR_UDP) || (protocol == Sn_MR_IPRAW) || (protocol == Sn_MR_MACRAW) || (protocol == Sn_MR_PPPOE))
	{
		close(s);
		IINCHIP_WRITE(Sn_MR(s),protocol | flag);
		if (port != 0) {
			IINCHIP_WRITE(Sn_PORT0(s),(uint8)((port & 0xff00) >> 8));
			IINCHIP_WRITE((Sn_PORT0(s) + 1),(uint8)(port & 0x00ff));
		} else {
			local_port++; // if don't set the source port, set local_port number.
			IINCHIP_WRITE(Sn_PORT0(s),(uint8)((local_port & 0xff00) >> 8));
			IINCHIP_WRITE((Sn_PORT0(s) + 1),(uint8)(local_port & 0x00ff));
		}
		IINCHIP_WRITE(Sn_CR(s),Sn_CR_OPEN); // run sockinit Sn_CR
		ret = 1;
	}
	else
	{
		ret = 0;
	}
#ifdef __DEF_IINCHIP_DBG__
	printf("Sn_SR = %.2x , Protocol = %.2x\r\n", IINCHIP_READ(Sn_SR(s)), IINCHIP_READ(Sn_MR(s)));
#endif
	return ret;
}


/*
*********************************************************************
This function close the socket and parameter is "s" which represent the socket number
*********************************************************************
*/

void close(SOCKET s)
{
#ifdef __DEF_IINCHIP_DBG__
	printf("close()\r\n");
#endif
	IINCHIP_WRITE(Sn_CR(s),Sn_CR_CLOSE);
}

/*
*********************************************************************
This function established  the connection for the channel in passive (server) mode.
This function waits for the request from the peer. The parameter "s" is the socket number
This function return 1 for success else 0.
*********************************************************************
*/

uint8 listen(SOCKET s)
{
	uint8 ret;
#ifdef __DEF_IINCHIP_DBG__
	printf("listen()\r\n");
#endif
	if (IINCHIP_READ(Sn_SR(s)) == SOCK_INIT)
	{
		IINCHIP_WRITE(Sn_CR(s),Sn_CR_LISTEN);
		ret = 1;
	}
	else
	{
		ret = 0;
#ifdef __DEF_IINCHIP_DBG__
	printf("Fail[invalid ip,port]\r\n");
#endif
	}
	return ret;
}


/*
*********************************************************************
This function established  the connection for the channel in Active (client) mode. 
This function waits for the untill the connection is established. The parameter "s" is the socket number
This function return 1 for success else 0.
*********************************************************************
*/

uint8 connect(SOCKET s, uint8 * addr, uint16 port)
{
	uint8 ret;
#ifdef __DEF_IINCHIP_DBG__
	printf("connect()\r\n");
#endif
	if 
		(
			((addr[0] == 0xFF) && (addr[1] == 0xFF) && (addr[2] == 0xFF) && (addr[3] == 0xFF)) ||
		 	((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) ||
		 	(port == 0x00) 
		) 
 	{
 		ret = 0;
#ifdef __DEF_IINCHIP_DBG__
	printf("Fail[invalid ip,port]\r\n");
#endif
	}
	else
	{

		ret = 1;
		// set destination IP
		IINCHIP_WRITE(Sn_DIPR0(s),addr[0]);
		IINCHIP_WRITE((Sn_DIPR0(s) + 1),addr[1]);
		IINCHIP_WRITE((Sn_DIPR0(s) + 2),addr[2]);
		IINCHIP_WRITE((Sn_DIPR0(s) + 3),addr[3]);
		IINCHIP_WRITE(Sn_DPORT0(s),(uint8)((port & 0xff00) >> 8));
		IINCHIP_WRITE((Sn_DPORT0(s) + 1),(uint8)(port & 0x00ff));
		IINCHIP_WRITE(Sn_CR(s),Sn_CR_CONNECT);
		// wait for completion
		while (IINCHIP_READ(Sn_CR(s)))
		{
			if (IINCHIP_READ(Sn_SR(s)) == SOCK_CLOSED)
			{
#ifdef __DEF_IINCHIP_DBG__
			printf("SOCK_CLOSED.\r\n");
#endif
				ret = 0; break;
			}
		}
	}

	return ret;
}


/*
*********************************************************************
This function used for disconnect the socket and parameter is "s" which represent the socket number
*********************************************************************
*/

void disconnect(SOCKET s)
{
#ifdef __DEF_IINCHIP_DBG__
	printf("disconnect()\r\n");
#endif
	IINCHIP_WRITE(Sn_CR(s),Sn_CR_DISCON);
}

/*
*********************************************************************
This function used to send the data in TCP mode and the parameter "s" represents
the socket number and "buf" a pointer to data and "len" is the data size to be send.
This function return 1 for success else 0.
*********************************************************************
*/

uint16 send(SOCKET s, const uint8 * buf, uint16 len)
{
	uint8 status=0;
	uint16 ret=0;
	uint16 freesize=0;
#ifdef __DEF_IINCHIP_DBG__
	printf("send()\r\n");
#endif

	if (len > getIINCHIP_TxMAX(s)) ret = getIINCHIP_TxMAX(s); // check size not to exceed MAX size.
	else ret = len;

	// if freebuf is available, start.
	do 
	{
		freesize = getSn_TX_FSR(s);
		status = IINCHIP_READ(Sn_SR(s));
		if ((status != SOCK_ESTABLISHED) && (status != SOCK_CLOSE_WAIT)) {ret = 0; break;}
#ifdef __DEF_IINCHIP_DBG__
		printf("socket %d freesize(%d) empty or error\r\n", s, freesize);
#endif
	} while (freesize < ret);

	// copy data
	send_data_processing(s, (uint8 *)buf, ret);
	IINCHIP_WRITE(Sn_CR(s),Sn_CR_SEND);
	while ( (IINCHIP_READ(Sn_IR(s)) & Sn_IR_SEND_OK) != Sn_IR_SEND_OK )
	{
		status = IINCHIP_READ(Sn_SR(s));
		if (status == SOCK_CLOSED)
		{
#ifdef __DEF_IINCHIP_DBG__
			printf("SOCK_CLOSED.\r\n");
#endif
			setISR(s, getISR(s) & (Sn_IR_RECV | Sn_IR_DISCON | Sn_IR_CON));		// clear SEND_OK & TIMEOUT in I_STATUS[s]
			setSn_IR(s, (Sn_IR_SEND_OK | Sn_IR_TIMEOUT));						// clear SEND_OK & TIMEOUT in Sn_IR(s)
			return 0;
		}
	}
	setISR(s, getISR(s) & (~Sn_IR_SEND_OK));
	setSn_IR(s, Sn_IR_SEND_OK);

	return ret;
}


/*
*********************************************************************
This function is an application I/F function which is used to receive the data in TCP mode.
It continues to wait for data as much as the application wants to receive and the 
parameter "s" represents the socket number and "buf" a pointer to copy the data
to be received and "len" is the data size to be read. This function return received data size
for success else -1.
*********************************************************************
*/

uint16 recv(SOCKET s, uint8 * buf, uint16 len)
{
	uint16 ret=0;
#ifdef __DEF_IINCHIP_DBG__
	printf("recv()\r\n");
#endif


	if ( len > 0 )
	{
		recv_data_processing(s, buf, len);
		IINCHIP_WRITE(Sn_CR(s),Sn_CR_RECV);
		ret = len;
	}
	return ret;
}

/*
*********************************************************************
This function is an application I/F function which is used to send the data for other then
TCP mode. Unlike TCP transmission, The peer's destination address and the port is needed.
and the parameter "s" represents the socket number and "buf" a pointer to the data 
and "len" is the data size to send and addr is the peer's Destination IP address and port is
the peer's destination port number. This function return send data size for success else -1.
*********************************************************************
*/

uint16 sendto(SOCKET s, const uint8 * buf, uint16 len, uint8 * addr, uint16 port)
{
	uint8 status=0;
	uint8 isr=0;
	uint16 ret=0;
	
#ifdef __DEF_IINCHIP_DBG__
	printf("sendto()\r\n");
#endif
   if (len > getIINCHIP_TxMAX(s)) ret = getIINCHIP_TxMAX(s); // check size not to exceed MAX size.
   else ret = len;

	if
		(
		 	((addr[0] == 0x00) && (addr[1] == 0x00) && (addr[2] == 0x00) && (addr[3] == 0x00)) ||
		 	((port == 0x00)) ||(ret == 0)
		) 
 	{
 	   ;
#ifdef __DEF_IINCHIP_DBG__
	printf("%d Fail[%.2x.%.2x.%.2x.%.2x, %.d, %d]\r\n",s, addr[0], addr[1], addr[2], addr[3] , port, len);
	printf("Fail[invalid ip,port]\r\n");
#endif
	}
	else
	{
		IINCHIP_WRITE(Sn_DIPR0(s),addr[0]);
		IINCHIP_WRITE((Sn_DIPR0(s) + 1),addr[1]);
		IINCHIP_WRITE((Sn_DIPR0(s) + 2),addr[2]);
		IINCHIP_WRITE((Sn_DIPR0(s) + 3),addr[3]);
		IINCHIP_WRITE(Sn_DPORT0(s),(uint8)((port & 0xff00) >> 8));
		IINCHIP_WRITE((Sn_DPORT0(s) + 1),(uint8)(port & 0x00ff));

		// copy data
		send_data_processing(s, (uint8 *)buf, ret);
		IINCHIP_WRITE(Sn_CR(s),Sn_CR_SEND);
    	while ( (IINCHIP_READ(Sn_IR(s)) & Sn_IR_SEND_OK) != Sn_IR_SEND_OK )
		{
			status = IINCHIP_READ(Sn_SR(s));
#ifndef __DEF_IINCHIP_INT__
			isr = IINCHIP_READ(Sn_IR(s));
#endif
			if ( (isr & Sn_IR_TIMEOUT) || (getISR(s) & Sn_IR_TIMEOUT) )
			{
#ifdef __DEF_IINCHIP_DBG__
				printf("send fail.\r\n");
#endif
				setISR(s, getISR(s) & (Sn_IR_RECV | Sn_IR_DISCON | Sn_IR_CON));		// clear SEND_OK & TIMEOUT in I_STATUS[s]
				setSn_IR(s, (Sn_IR_SEND_OK | Sn_IR_TIMEOUT));						// clear SEND_OK & TIMEOUT in Sn_IR(s)
				return 0;
			}
		}
		setISR(s, getISR(s) & (~Sn_IR_SEND_OK));
	 	setSn_IR(s, Sn_IR_SEND_OK);
	}
	return ret;
}


/*
*********************************************************************
This function is an application I/F function which is used to receive the data in other then
TCP mode. This function is used to receive UDP, IP_RAW and MAC_RAW mode, and handle 
the header as well. 
The parameter "s" represents the socket number and "buf" a pointer to copy the data to be 
received and "len" is the data size to read and and addr is a pointer to store the peer's 
IP address and port is a pointer to store the peer's port number.
This function return received data size for success else -1.
*********************************************************************
*/

uint16 recvfrom(SOCKET s, uint8 * buf, uint16 len, uint8 * addr, uint16 *port)
{
	uint8 head[8];
	uint16 data_len=0;
	uint16 ptr=0;
#ifdef __DEF_IINCHIP_DBG__
	printf("recvfrom()\r\n");
#endif

	if ( len > 0 )
	{
   	ptr = IINCHIP_READ(Sn_RX_RD0(s));
   	ptr = ((ptr & 0x00ff) << 8) + IINCHIP_READ(Sn_RX_RD0(s) + 1);
#ifdef __DEF_IINCHIP_DBG__
   	printf("ISR_RX: rd_ptr : %.4x\r\n", ptr);
#endif
   	switch (IINCHIP_READ(Sn_MR(s)) & 0x07)
   	{
   	case Sn_MR_UDP :
   			read_data(s, (uint8 *)ptr, head, 0x08);
   			ptr += 8;
   			// read peer's IP address, port number.
    			addr[0] = head[0];
   			addr[1] = head[1];
   			addr[2] = head[2];
   			addr[3] = head[3];
   			*port = head[4];
   			*port = (*port << 8) + head[5];
   			data_len = head[6];
   			data_len = (data_len << 8) + head[7];
   			
#ifdef __DEF_IINCHIP_DBG__
   			printf("UDP msg arrived\r\n");
   			printf("source Port : %d\r\n", *port);
   			printf("source IP : %d.%d.%d.%d\r\n", addr[0], addr[1], addr[2], addr[3]);
#endif

				read_data(s, (uint8 *)ptr, buf, data_len); // data copy.
				ptr += data_len;

            IINCHIP_WRITE(Sn_RX_RD0(s),(uint8)((ptr & 0xff00) >> 8));
            IINCHIP_WRITE((Sn_RX_RD0(s) + 1),(uint8)(ptr & 0x00ff));
   			break;
   
   	case Sn_MR_IPRAW :
   			read_data(s, (uint8 *)ptr, head, 0x06);
   			ptr += 6;
   
   			addr[0] = head[0];
   			addr[1] = head[1];
   			addr[2] = head[2];
   			addr[3] = head[3];
   			data_len = head[4];
   			data_len = (data_len << 8) + head[5];
   	
#ifdef __DEF_IINCHIP_DBG__
   			printf("IP RAW msg arrived\r\n");
   			printf("source IP : %d.%d.%d.%d\r\n", addr[0], addr[1], addr[2], addr[3]);
#endif
				read_data(s, (uint8 *)ptr, buf, data_len); // data copy.
				ptr += data_len;

            IINCHIP_WRITE(Sn_RX_RD0(s),(uint8)((ptr & 0xff00) >> 8));
            IINCHIP_WRITE((Sn_RX_RD0(s) + 1),(uint8)(ptr & 0x00ff));
   			break;
   	case Sn_MR_MACRAW :
   			read_data(s,(uint8*)ptr,head,2);
   			ptr+=2;
   			data_len = head[0];
   			data_len = (data_len<<8) + head[1] - 2;

   			read_data(s,(uint8*) ptr,buf,data_len);
   			ptr += data_len;
   			IINCHIP_WRITE(Sn_RX_RD0(s),(uint8)((ptr & 0xff00) >> 8));
   			IINCHIP_WRITE((Sn_RX_RD0(s) + 1),(uint8)(ptr & 0x00ff));
   			
#ifdef __DEF_IINCHIP_DGB__
			printf("MAC RAW msg arrived\r\n");
			printf("dest mac=%.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",buf[0],buf[1],buf[2],buf[3],buf[4],buf[5]);
			printf("src  mac=%.2X.%.2X.%.2X.%.2X.%.2X.%.2X\r\n",buf[6],buf[7],buf[8],buf[9],buf[10],buf[11]);
			printf("type    =%.2X%.2X\r\n",buf[12],buf[13]); 
#endif			
			break;

   	default :
   			break;
   	}
		IINCHIP_WRITE(Sn_CR(s),Sn_CR_RECV);
	}
#ifdef __DEF_IINCHIP_DBG__
	printf("recvfrom() end ..\r\n");
#endif
 	return data_len;
}

uint16 igmpsend(SOCKET s, const uint8 * buf, uint16 len)
{
	uint8 status=0;
	uint8 isr=0;
	uint16 ret=0;
	
#ifdef __DEF_IINCHIP_DBG__
	printf("igmpsend()\r\n");
#endif
   if (len > getIINCHIP_TxMAX(s)) ret = getIINCHIP_TxMAX(s); // check size not to exceed MAX size.
   else ret = len;

	if	(ret == 0) 
 	{
 	   
#ifdef __DEF_IINCHIP_DBG__
	printf("%d Fail[%d]\r\n",len);
#endif
	}
	else
	{
		// copy data
      send_data_processing(s, (uint8 *)buf, ret);
		  IINCHIP_WRITE(Sn_CR(s),Sn_CR_SEND);
      while ( (IINCHIP_READ(Sn_IR(s)) & Sn_IR_SEND_OK) != Sn_IR_SEND_OK )
      {
      	status = IINCHIP_READ(Sn_SR(s));
#ifndef __DEF_IINCHIP_INT__
			isr = IINCHIP_READ(Sn_IR(s));
#endif
			if ( (isr & Sn_IR_TIMEOUT) || (getISR(s) & Sn_IR_TIMEOUT) )
			{
#ifdef __DEF_IINCHIP_DBG__
				printf("igmp send fail.\r\n");
#endif
				setISR(s, getISR(s) & (Sn_IR_RECV | Sn_IR_DISCON | Sn_IR_CON));		// clear SEND_OK & TIMEOUT in I_STATUS[s]
				setSn_IR(s, (Sn_IR_SEND_OK | Sn_IR_TIMEOUT));						// clear SEND_OK & TIMEOUT in Sn_IR(s)
				return 0;
			}
		}
		setISR(s, getISR(s) & (~Sn_IR_SEND_OK));
	 	setSn_IR(s, Sn_IR_SEND_OK);
	}
	return ret;
}
