/*
 * Changes from V1.0.0
 *
 + Eth 
 +    TickType_t rather than unsigned long.
 */

#include <stdlib.h>
#include <string.h>


/* Scheduler include files. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
/* winnet W6100 include files. */
#include "wizchip_conf.h"
#include "wizchip_init.h"
#include "socket.h"

/* program include files. */

#include "Ethernet_Process.h"


//#include "Comm.h"



extern QueueHandle_t xCMDQueue;

QueueHandle_t EthRxQueue;

QueueHandle_t EthTxQueue;



#define EthRX_RELATIVE_PRIORITY      ( 1 )
#define EthSTACK_SIZE                  configMINIMAL_STACK_SIZE

//#define		DIRECT_TX	


T_ETH	tEthCFG = {

		.tNetInfo.mac = {0x11, 0x11, 0x11, 0xAB, 0xCD, 0x00},
		.tNetInfo.ip = {192, 168, 1, 200},
		.tNetInfo.sn = {255, 255, 255, 0},
		.tNetInfo.gw = {192, 168, 1, 1},
		.tNetInfo.dns = {8, 8, 8, 8},
		.tNetInfo.lla={0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0},
		.tNetInfo.gua={0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0},
		.tNetInfo.sn6={0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0},
		.tNetInfo.gw6={0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0},

		.byMaxSockets = 8,
		.tSC[0].byActive = 1,
		.tSC[0].wPort = 502,
		.tSC[0].bProtocol = Sn_MR_TCP4,
		.tSC[0].bPastStatus = SOCK_CLOSED,
		
		.tSC[1].byActive = 1,
		.tSC[1].wPort = 2000,
		.tSC[1].bProtocol = Sn_MR_TCP4,	
		.tSC[1].bPastStatus = SOCK_CLOSED,

		.tSC[2].byActive = 1,
		.tSC[2].wPort = 1502,
		.tSC[2].bProtocol = Sn_MR_TCP4, 
		.tSC[2].bPastStatus = SOCK_CLOSED,

		.tSC[3].byActive = 0,
		.tSC[3].wPort = 1503,
		.tSC[3].bProtocol = Sn_MR_TCP4, 
		.tSC[3].bPastStatus = SOCK_CLOSED,

		.tSC[4].byActive = 0,
		.tSC[4].wPort = 1504,
		.tSC[4].bProtocol = Sn_MR_TCP4,
		.tSC[4].bPastStatus = SOCK_CLOSED,
		
		.tSC[5].byActive = 0,
		.tSC[5].wPort = 1505,
		.tSC[5].bProtocol = Sn_MR_TCP4, 
		.tSC[5].bPastStatus = SOCK_CLOSED,
		
		.tSC[6].byActive = 0,
		.tSC[6].wPort = 1506,
		.tSC[6].bProtocol = Sn_MR_TCP4, 
		.tSC[6].bPastStatus = SOCK_CLOSED,
		
		.tSC[7].byActive = 0,
		.tSC[7].wPort = 2000,
		.tSC[7].bProtocol = Sn_MR_UDP4,
		.tSC[7].bPastStatus = SOCK_CLOSED,


		.tNetTime.s_retry_cnt =0xA,		// default : 0x07 ///< The default retry count of SOCKETn
		.tNetTime.s_time_100us = 0x1388,		// 5000(0x1388) * 100us = 0.5s, defalut : 0x07D0(2000)	      
							///< The retransmission time of SOCKETn (unit 100us)
		.tNetTime.sl_retry_cnt= 0x5,	      	// default : 0x00 ///< The retry count of SOCKET-less
		.tNetTime.sl_time_100us =0x1388	// 5000(0x1388) * 100us = 0.5s, defalut : 0x07D0(2000)	 
							///< The retransmission time of SOCKET-less (unit 100us)
		
};


T_ETH_SEND tSend[8] = {0,};

//uint8_t		m_byTxPacket[ETH_PACKET_SIZE] = {0,};

uint8_t		m_byRxPacket[ETH_PACKET_SIZE] = {0,};
uint16_t	m_wRxPacketIdx =0;

#define		ETH_BUF_MAX	6
uint8_t		m_byData[ETH_BUF_MAX][ETH_PACKET_SIZE] = {0,};

T_COMM_PACKET	tEthPacket;

uint8_t	m_bRxSocket;



E_COMM_STEP	eEthStep = COMM_HEADER;

unsigned portBASE_TYPE uxEthPriority;

static void vEthernetTask( void * pvParameters );
static void vEthPacketRxTask( void * pvParameters );
static void vEthPacketTxTask( void * pvParameters );


static char cLog[300] = {0,};

//Net Information 
wiz_NetInfo gWIZNETINFO = { 	.mac = {0x11, 0x11, 0x11, 0xAB, 0xCD, 0x00},
				.ip = {192, 168, 1, 200},
				.sn = {255, 255, 255, 0},
				.gw = {192, 168, 1, 1},
				.dns = {8, 8, 8, 8},
				.lla={0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0},
				.gua={0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0},
				.sn6={0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0},
				.gw6={0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0}
};

uint8_t	g_bConnectSockNo = 0xFF;

uint8_t* ETH_ReturnBuf(void)
{
	static uint16_t wi = 0;
	uint8_t *pPointer;

	pPointer = &m_byData[wi++][0];
	if(wi >= ETH_BUF_MAX)
	{
		wi = 0;
	}
	return pPointer;
}
/*-----------------------------------------------------------
8 independent SOCKETs

Protocol Socket(Server Mode)
1. UDP Port
[0]	> port: 500(Config,Monitoring)
2. TCP Port
[1~6]	> port: 501~506(502: Modbus)
[7]   	> port: 507(Config,Monitoring)



*/

void vEth_Config(uint8_t	*pConfig)
{
	uint8_t 	syslock = SYS_NET_LOCK;
	uint8_t		stslock = 0;
	uint16_t	Cnt =0;
	
	uint8_t		bySC = 0;
	//memcpy((uint8_t*)&tEthCFG,pConfig,sizeof(T_ETH));
#if 1

	for(bySC=0;bySC<tEthCFG.byMaxSockets;bySC++)
	{// Socket 비활성화
		tEthCFG.tSC[bySC].bPastStatus = SOCK_CLOSED;
	}
	// 7 Soeckt is UDP Protocol  
	tEthCFG.tSC[7].bProtocol = Sn_MR_UDP4;
#endif	
	// send하기 위한 Struct 준비 
	//tSend.eSTATE = SEND_WAIT;
	//tSend.pbyData = m_byTxPacket;

	m_wRxPacketIdx = 0;

	eEthStep = COMM_HEADER;

	do{
		ctlwizchip(CW_SYS_UNLOCK,&syslock);
		__NOP();
		ctlwizchip(CW_GET_SYSLOCK,&stslock);
		Cnt++;
	}while(stslock != 0x05);

  	ctlnetwork(CN_SET_NETINFO,&tEthCFG.tNetInfo);

	// Retransmission Time/Count Setting
	ctlnetwork(CN_SET_TIMEOUT,&tEthCFG.tNetTime);
	

	tEthCFG.bPHYLinked = PHY_LINK_OFF;

	//Socket TX,RX Buffer Size �� 2Kbyte(Default��).
}

void vETH_Process_Init(void)
{
	vFSMCInit();
   	W6100Initialze();

	vEth_Config(NULL);

}


/*-----------------------------------------------------------*/
void vEthTaskCreate(unsigned portBASE_TYPE uxPriority)
{
	const unsigned portBASE_TYPE uxQueueSize = 1;//5;
    	/* Create the queue on which errors will be reported. */
	EthRxQueue = xQueueCreate( (uxQueueSize), ( unsigned portBASE_TYPE ) sizeof(T_ETH_PACKET) );	
	EthTxQueue = xQueueCreate( (uxQueueSize*2), ( unsigned portBASE_TYPE ) sizeof(T_ETH_PACKET) );

	uxEthPriority = uxPriority;
	
	xTaskCreate( vEthernetTask, "Ethernet", EthSTACK_SIZE*5, NULL, uxPriority/* + EthRX_RELATIVE_PRIORITY*/, NULL );
	xTaskCreate( vEthPacketRxTask, "EthPacketRx", EthSTACK_SIZE*5, NULL, uxPriority/* + EthRX_RELATIVE_PRIORITY*/, NULL );
	xTaskCreate( vEthPacketTxTask, "EthPacketTx", EthSTACK_SIZE, NULL, uxPriority/* + EthRX_RELATIVE_PRIORITY*/, NULL );
}
/*-----------------------------------------------------------*/
// Socket���� Server State������ ��
int32_t EthServerState(uint8_t sn,uint8_t protocol,uint16_t port,T_ETH_SEND *ptSend)
{
	int32_t ret;
	datasize_t sentsize=0;
	int8_t status = SOCK_CLOSED,interrupt;
	uint8_t tmp = 0;
	datasize_t received_size;
	uint8_t arg_tmp8;
	uint8_t* mode_msg;

	T_ETH_PACKET tPacket;

	T_ETH_UDP	tUDP;


	
	getsockopt(sn, SO_STATUS, &status);
	if(status != tEthCFG.tSC[sn].bystatus)
	{
		tEthCFG.tSC[sn].bPastStatus = tEthCFG.tSC[sn].bystatus;
	}
	tEthCFG.tSC[sn].bystatus = status;
#if 1
	switch(status)
	{
		case SOCK_CLOSED:// Start Socket...
			// interrupt Status �б�

			ctlsocket(sn,CS_GET_INTERRUPT,&interrupt);
			//if((interrupt == 0x00) || ((interrupt & Sn_IR_DISCON) == Sn_IR_DISCON))// socket 생성
			if((interrupt & Sn_IR_TIMEOUT) != Sn_IR_TIMEOUT)
			{
				if((interrupt & Sn_IR_DISCON) == Sn_IR_DISCON)
				{
					arg_tmp8 = Sn_IR_DISCON;
					ctlsocket(sn,CS_CLR_INTERRUPT,&arg_tmp8);
				
					//Log"TCP/Host Disconnect 192.168.000.001
					sprintf(cLog,"TCP/Host Disconnect(IP:%d.%d.%d.%d Port:%d)->Reader(IP:%d.%d.%d.%d Port:%d) ",
					tEthCFG.tSC[sn].tDst.byIp[0],tEthCFG.tSC[sn].tDst.byIp[1],
					tEthCFG.tSC[sn].tDst.byIp[2],tEthCFG.tSC[sn].tDst.byIp[3],
					tEthCFG.tSC[sn].tDst.wPort,
					tEthCFG.tNetInfo.ip[0],tEthCFG.tNetInfo.ip[1],
					tEthCFG.tNetInfo.ip[2],tEthCFG.tNetInfo.ip[3],
					tEthCFG.tSC[sn].wPort);

					AddLogQ(cLog, strlen(cLog));

				}
				// TCP4 socket 생성
				tmp = socket(sn, protocol, port, SOCK_IO_NONBLOCK);
				if(tmp != sn)	 /* reinitialize the socket */
				{
					return SOCKERR_SOCKNUM;
				}
				// Socket opened
				tEthCFG.tSC[sn].byOpend = 1;
				
				xQueueReset(EthRxQueue);
				xQueueReset(EthTxQueue);
				ptSend->eSTATE = SEND_WAIT;
			}
			//else if(interrupt & Sn_IR_TIMEOUT) //TIMEOUT Interrupt
			else
			{
				// TIMEOUT Interrupt;
				arg_tmp8 = Sn_IR_TIMEOUT;
                		ctlsocket(sn,CS_CLR_INTERRUPT,&arg_tmp8);

				// Timeout 처리

			}

			break;
		/*--- TCP Server Process -------------------------------------------------------------------*/
		case SOCK_INIT :
            		if( (ret = listen(sn)) != SOCK_OK) 
	    		{
				return ret;			
            		}
			break;
		case SOCK_ESTABLISHED :
			// interrupt Status �б�
			ctlsocket(sn,CS_GET_INTERRUPT,&interrupt);
			if(interrupt & Sn_IR_CON) //CONNECT Interrupt
            {

				g_bConnectSockNo = sn;
				// Client IP Information �а�, ����
				getsockopt(sn,SO_DESTIP,tEthCFG.tSC[sn].tDst.byIp);
				getsockopt(sn,SO_DESTPORT,&tEthCFG.tSC[sn].tDst.wPort);

				// Clear CONNECT Interrupt;
				arg_tmp8 = Sn_IR_CON;
                		ctlsocket(sn,CS_CLR_INTERRUPT,&arg_tmp8);

				//Log"TCP/Host Connect 192.168.000.001
				sprintf(cLog,"TCP/Host Connect(IP:%d.%d.%d.%d Port:%d)->Reader(IP:%d.%d.%d.%d Port:%d) ",
					tEthCFG.tSC[sn].tDst.byIp[0],tEthCFG.tSC[sn].tDst.byIp[1],
					tEthCFG.tSC[sn].tDst.byIp[2],tEthCFG.tSC[sn].tDst.byIp[3],
					tEthCFG.tSC[sn].tDst.wPort,
					tEthCFG.tNetInfo.ip[0],tEthCFG.tNetInfo.ip[1],
					tEthCFG.tNetInfo.ip[2],tEthCFG.tNetInfo.ip[3],
					tEthCFG.tSC[sn].wPort);
				
				AddLogQ(cLog, strlen(cLog));

			}
			else if(interrupt & Sn_IR_TIMEOUT) //TIMEOUT Interrupt
			{
				// TIMEOUT Interrupt;
				arg_tmp8 = Sn_IR_TIMEOUT;
                		ctlsocket(sn,CS_CLR_INTERRUPT,&arg_tmp8);

				sprintf(cLog,"TCP/Socket Time out");
				AddLogQ(cLog, strlen(cLog));
				// Timeout 처리

			}
			// ���� Data ���� Ȯ�� 
			getsockopt(sn,SO_RECVBUF,&received_size);
			if(received_size > ETH_PACKET_SIZE)
			{
				received_size =	ETH_PACKET_SIZE; 
			}
			if(received_size > 0)
			{
				tPacket.pbyData = ETH_ReturnBuf();
				ret = recv(sn, tPacket.pbyData  , received_size);
				if(ret <= 0) 
				{// check SOCKERR_BUSY & SOCKERR_XXX. For showing the occurrence of SOCKERR_BUSY.
					return ret;      
				}
				tPacket.wlength = (uint16_t)ret; // ���� Data ���� 
				tPacket.byScNo = sn;
				tPacket.bProtocol = Sn_MR_TCP4;
				xQueueSend( EthRxQueue, &tPacket,( portTickType ) portMAX_DELAY);
			}
		#ifndef DIRECT_TX
			// 송신 Data 처리
			// 송신 가능한 상태이고, Data 수신한 Socket으로 송신처리함
			if((ptSend->eSTATE == SEND_ENABLE)/* && (ptSend->byScNo == sn)*/)
			{
				ret = send(sn, ptSend->pbyData, (datasize_t)ptSend->wlength);
				if(ret < 0)
				{
				    close(sn);
				    return ret;
				}
				// 송신이 완료되고, 송신 대기 상태로 설정함 
				ptSend->eSTATE = SEND_WAIT;
			}
		#endif
			break;
		case SOCK_CLOSE_WAIT :
			/*
			FIN 신호를 받은 Passive Close 입장의 서버가 갖는 소켓 상태로서, Active Close에게 FIN 을 
			받은 뒤 Application의 연결 종료 프로세스가 완료되고 FIN 패킷을 보내기전 까지의 소켓 상태이다. 
			만약 소켓이 CLOSE_WAIT 상태로 계속 남아있다면 Application에 문제가 있다는 신호로 
			인식하고 적절한 조치를 취해야 한다.			
			*/
			sprintf(cLog,"TCP/Socket Close Wait");
			AddLogQ(cLog, strlen(cLog));

			xQueueReset(EthTxQueue);
			ptSend->eSTATE = SEND_WAIT;
			 if((ret = disconnect(sn)) != SOCK_OK) 
			 {
			 	return ret;
			 }
			break;
		/*---UDP Process -------------------------------------------------------------------*/
		case SOCK_UDP:
			getsockopt(sn, SO_RECVBUF, &received_size);
			if(received_size > DATA_BUF_SIZE) 
			{
				received_size = DATA_BUF_SIZE;
			}
#if 1
			if(received_size>0)
			{
				tPacket.pbyData = ETH_ReturnBuf();
				ret = recvfrom(sn, tPacket.pbyData  , received_size,tUDP.destip, &tUDP.destport,&tUDP.addr_len);
			
			    	if(ret <= 0)
			    	{
			     		return ret;
			    	}

				tPacket.wlength = (uint16_t)ret; // ���� Data ���� 
				tPacket.byScNo = sn;
				tPacket.tUDPip = tUDP;
				tPacket.bProtocol = Sn_MR_UDP4;
			//xQueueSend( EthRxQueue, &tPacket,( portTickType ) 0);
			}
#endif
			
			break;
		case SOCK_LISTEN:
		  
	  		break;
		default :
			CMD_OnLED(FALSE);	
			break;
	}
#endif
	return SOCK_OK;


}
//  Ethernet ��� ���, Packet ó�� 

uint8_t	bCheckSaveLog = FALSE;

static uint8_t EthCheck(void)
{
	uint8_t bChipOK = FALSE;
	wiz_NetTimeout	tNetTim; //
	char cLogging[40] = {0,};
	
	if((tNetTim.s_time_100us == tEthCFG.tNetTime.s_time_100us)/* &&
		(tNetTim.s_retry_cnt == tEthCFG.tNetTime.s_retry_cnt)*/)
	{// 정상 
		bChipOK = TRUE;
		bCheckSaveLog = FALSE;
	}
#if 1
	else
#else
	else if((tNetTim.s_time_100us == 2000)&&
		(tNetTim.s_retry_cnt == 7)) // default Value //retransmission time of SOCKETn (unit 100us)
#endif

	  	bChipOK = TRUE;
		bCheckSaveLog = FALSE;


	}

	return bChipOK;

}
 
static void vEthernetTask( void * pvParameters )
{
	uint8_t	bySCNo =0;	// Socket No.

	uint8_t byPHYLink = PHY_LINK_OFF;

	for( ; ; )
    	{
		if(byPHYLink == FALSE)
		{
			vTaskDelay( 10 / portTICK_PERIOD_MS); // 1000msec 
			if(ctlwizchip(CW_GET_PHYLINK, (void *)&byPHYLink) == -1)
			{
				//printf("Unknown PHY link status.\r\n");
			}
			if(byPHYLink == PHY_LINK_ON)
			{// task 시작 !
				sprintf(cLog,"TCP/Lan Link On ");
			}
		}
		else
		{
			if(EthCheck() == TRUE)
			{
				for(bySCNo=0;bySCNo<tEthCFG.byMaxSockets;bySCNo++)
				{	
					if(tEthCFG.tSC[bySCNo].byActive)
					{
						EthServerState(bySCNo,tEthCFG.tSC[bySCNo].bProtocol,tEthCFG.tSC[bySCNo].wPort,&tSend[bySCNo]);
					}
				}
			}			
			if(ctlwizchip(CW_GET_PHYLINK, (void *)&byPHYLink) == -1)
			{
				//printf("Unknown PHY link status.\r\n");
			} 
#if 0	//debugging
			byPHYLink = PHY_LINK_ON;
#endif			
			if(byPHYLink == PHY_LINK_OFF)
			{// task 중지   !
				sprintf(cLog,"TCP/Lan Link Off ");
			}
		}
		tEthCFG.bPHYLinked = byPHYLink;

	}

}
void Eth_EchoRxPacket(void)
{
  /*
	tSend.eSTATE = SEND_ENABLE;
	tSend.byScNo = tEthCFG.byRxSCNo;
	tSend.pbyData = m_byRxPacket;
	tSend.wlength = m_wRxPacketIdx;
	*/
}


// Ethernet���� ���ŵ� Data�� ���� Packet �����, Comm(Packetó��)���� ���� 
static void vEthPacketRxTask( void * pvParameters )
{
	T_ETH_PACKET tPacket;

	for(;;)
	{
		if(xQueueReceive(EthRxQueue,(void *) &tPacket, ( portTickType )portMAX_DELAY) == pdPASS )
		{
			// Data�� ������ Socket No.(�ش� Socket���� Data�� �۽���)
			
			if(tPacket.byScNo != SOC_UDP7)
			{ // TCP 처리 
				tEthCFG.byRxSCNo = tPacket.byScNo;
				if(Eth_GetPacket(&tPacket) == TRUE)
				{
					//xQueueSend( EthTxQueue, &tPacket,( portTickType ) 0);
					tEthPacket.bSocketNo = tPacket.byScNo; 
					m_bRxSocket = tPacket.byScNo;
					xQueueSend(xCMDQueue, &tEthPacket,( portTickType ) portMAX_DELAY);
				}
			}
			else
			{ // UDP 처리 

			}
		}
	}

}

static void vEthPacketTxTask( void * pvParameters )
{// 전에 송신이 완료될때까지는 Queue를 읽지 않는다..
	T_ETH_PACKET tPacket;
	int8_t status = SOCK_CLOSED;
	uint8_t bSocketNo = 0;	
	for(;;)
	{
                if(xQueueReceive(EthTxQueue,(void *) &tPacket, ( portTickType )portMAX_DELAY) == pdPASS )
		{	
			//getsockopt(tPacket.byScNo, SO_STATUS, &status);
			//if((status == SOCK_ESTABLISHED) && (tPacket.byScNo < 8))
                        tPacket.byScNo = 1;
                        if((tEthCFG.tSC[tPacket.byScNo].bystatus == SOCK_ESTABLISHED) && (tPacket.byScNo < 8))
			{
				/*
				#ifdef DIRECT_TX
					CMD_OnLED(TRUE);
					ret = send(tPacket.byScNo, tPacket.pbyData, (datasize_t)tPacket.wlength);
					if(ret <0)
					{
						close(tPacket.byScNo);
					}
					CMD_OnLED(FALSE);
				#else
				*/
				//BzOn();
				//CMD_OnLED(TRUE);
				// 전송할 Socket No.
				//tSend.byScNo = m_bRxSocket;//tPacket.byScNo;
				bSocketNo = tPacket.byScNo;
				tSend[bSocketNo].byScNo = tPacket.byScNo;
				//tSend.pbyData = ETH_ReturnBuf();
				//전송할 Data 복사 
				//memcpy(tSend.pbyData,tPacket.pbyData,tPacket.wlength);
				tSend[bSocketNo].pbyData = tPacket.pbyData;
				//전송할 Data 수량
				tSend[bSocketNo].wlength = tPacket.wlength;
				// 전송 가능 
				tSend[bSocketNo].eSTATE = SEND_ENABLE;
				do{
					vTaskDelay( 5 / portTICK_PERIOD_MS);
				}while(tSend[bSocketNo].eSTATE == SEND_ENABLE);
			}
		}
	}
}

