/**
* @file
* Ethernet Interface Skeleton
*
*/

/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
*    this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
*    derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
*
*/

/*
* This file is a skeleton for developing Ethernet network interface
* drivers for lwIP. Add code to the low_level functions and do a
* search-and-replace for the word "ethernetif" to replace it with
* something that better describes your network interface.
*/

#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
#include "lwip/tcpip.h"
#include "lwip/icmp.h"
#include "lwip/lwip_timers.h"
#include "netif/etharp.h"
#include "err.h"
#include "ethernetif.h"
#include "queue.h"

#include "lwip/ethip6.h" //Add for ipv6

#include "main.h"	// for the definition of CONFIG_WLAN


#if !CONFIG_WLAN
#include "stm32f2x7_eth.h"
#else
#include <lwip_intf.h>
#endif
#include <platform/platform_stdlib.h>

#include <string.h>

#ifdef CONFIG_DONT_CARE_TP
#define netifMTU							(576)
#else
#define netifMTU							(1500)
#endif
#define netifINTERFACE_TASK_STACK_SIZE		( 350 )
#define netifINTERFACE_TASK_PRIORITY		( configMAX_PRIORITIES - 1 )
#define netifGUARD_BLOCK_TIME			( 250 )
/* The time to block waiting for input. */
#define emacBLOCK_TIME_WAITING_FOR_INPUT	( ( portTickType ) 100 )
#define FAKE_PING_REPLY						0

#if !CONFIG_WLAN
/* Define those to better describe your network interface. */
#define IFNAME0 's'
#define IFNAME1 't'

static struct netif *s_pxNetIf = NULL;
xSemaphoreHandle s_xSemaphore = NULL;
          

/* Ethernet Rx & Tx DMA Descriptors */
extern ETH_DMADESCTypeDef  DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB];

/* Ethernet Receive buffers  */
extern uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE]; 

/* Ethernet Transmit buffers */
extern uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE]; 

/* Global pointers to track current transmit and receive descriptors */
extern ETH_DMADESCTypeDef  *DMATxDescToSet;
extern ETH_DMADESCTypeDef  *DMARxDescToGet;

/* Global pointer for last received frame infos */
extern ETH_DMA_Rx_Frame_infos *DMA_RX_FRAME_infos;

static void ethernetif_input( void * pvParameters );
#endif

static void arp_timer(void *arg);


/**
* In this function, the hardware should be initialized.
* Called from ethernetif_init().
*
* @param netif the already initialized lwip network interface structure
*        for this ethernetif
*/
static void low_level_init(struct netif *netif)
{
  uint32_t i;

  /* set netif MAC hardware address length */
  netif->hwaddr_len = ETHARP_HWADDR_LEN;

#if !CONFIG_WLAN
  /* set netif MAC hardware address */
  netif->hwaddr[0] =  MAC_ADDR0;
  netif->hwaddr[1] =  MAC_ADDR1;
  netif->hwaddr[2] =  MAC_ADDR2;
  netif->hwaddr[3] =  MAC_ADDR3;
  netif->hwaddr[4] =  MAC_ADDR4;
  netif->hwaddr[5] =  MAC_ADDR5;
#endif
  
  /* set netif maximum transfer unit */
  netif->mtu = netifMTU;

  /* Accept broadcast address and ARP traffic */
  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;

#if !CONFIG_WLAN
  s_pxNetIf =netif;

  /* create binary semaphore used for informing ethernetif of frame reception */
  if (s_xSemaphore == NULL)
  {
    s_xSemaphore= xSemaphoreCreateCounting(20,0);
  }

  /* initialize MAC address in ethernet MAC */ 
  ETH_MACAddressConfig(ETH_MAC_Address0, netif->hwaddr); 

  /* Initialize Tx Descriptors list: Chain Mode */
  ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB);
  /* Initialize Rx Descriptors list: Chain Mode  */
  ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB);

  /* Enable Ethernet Rx interrrupt */
  { 
    for(i=0; i<ETH_RXBUFNB; i++)
    {
      ETH_DMARxDescReceiveITConfig(&DMARxDscrTab[i], ENABLE);
    }
  }

#ifdef CHECKSUM_BY_HARDWARE
  /* Enable the checksum insertion for the Tx frames */
  {
    for(i=0; i<ETH_TXBUFNB; i++)
    {
      ETH_DMATxDescChecksumInsertionConfig(&DMATxDscrTab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull);
    }
  } 
#endif

  /* create the task that handles the ETH_MAC */
  xTaskCreate(ethernetif_input, (signed char*) "Eth_if", netifINTERFACE_TASK_STACK_SIZE, NULL,
              netifINTERFACE_TASK_PRIORITY,NULL);
  
  /* Enable MAC and DMA transmission and reception */
  ETH_Start();   
#else	//#if !CONFIG_WLAN
  /* WLAN interface is initialized later */
#endif
}

#if FAKE_PING_REPLY
void fake_arp_reply(struct netif *netif, struct pbuf *p)
{
	struct pbuf *q, *tq, *tp;
	int q_len, p_len;
	struct eth_hdr *p_eth, *q_eth;
	struct etharp_hdr *p_arp, *q_arp;
	struct eth_addr fake_src_mac = {0xb0, 0x48, 0x7a, 0xfe, 0xea, 0xe0};

	// Allocate buffer to store received packet
	q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_POOL);
	if (p == NULL) {
		printf("\n\rCannot allocate pbuf to receive packet");
		return;
	}
	for (tq = q, tp = p, q_len = tq->len; tp != NULL ; tp = tp->next) 
	{
		p_len = tp->len;
		while(p_len)
		{
			if(q_len > p_len) {
				memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), p_len);
				q_len -= p_len;
				p_len = 0;
			}else{
				memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), q_len);
				p_len -= q_len;
				tq = tq->next;
				q_len = tq->len;
			}
		}
	}
	p_eth = (struct eth_hdr *)p->payload;
	q_eth = (struct eth_hdr *)q->payload;
	p_arp = (struct etharp_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr));
	q_arp = (struct etharp_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr));

	q_eth->dest = p_eth->src;
	q_eth->src = fake_src_mac;
	q_arp->opcode = htons(ARP_REPLY);
	q_arp->shwaddr = fake_src_mac;
	q_arp->sipaddr = p_arp->dipaddr;
	q_arp->dhwaddr = p_eth->src;
	q_arp->dipaddr = p_arp->sipaddr;

	if(0){
		int i;
		char *buf = q->payload;
		printf("\n\r");
		for(i=0;i<q->tot_len;i++)
			printf("0x%02x, ", buf[i]);
		printf("\n\r");
	}
	if (ERR_OK != netif->input(q, netif)){
		printf("\n\rfake_arp_reply input error");
		pbuf_free(q);
	}else
		printf("\n\rfake arp reply \n\r");
}

void fake_echo_reply(struct netif *netif, struct pbuf *p)
{
	struct pbuf *q, *tq, *tp;
	int q_len, p_len;
	struct eth_hdr *p_eth, *q_eth;
	struct ip_hdr *p_ip, *q_ip;
	struct icmp_echo_hdr *p_echo, *q_echo;

	// Allocate buffer to store received packet
	q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_POOL);
	if (p == NULL) {
		printf("\n\rCannot allocate pbuf to receive packet");
		return;
	}
	for (tq = q, tp = p, q_len = tq->len; tp != NULL ; tp = tp->next) 
	{
		p_len = tp->len;
		while(p_len)
		{
			if(q_len > p_len) {
				memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), p_len);
				q_len -= p_len;
				p_len = 0;
			}else{
				memcpy((char*)tq->payload + (tq->len - q_len), (char*)tp->payload + (tp->len - p_len), q_len);
				p_len -= q_len;
				tq = tq->next;
				q_len = tq->len;
			}
		}
	}
	p_eth = (struct eth_hdr *)p->payload;
	q_eth = (struct eth_hdr *)q->payload;
	p_ip = (struct ip_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr));
	q_ip = (struct ip_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr));
	p_echo = (struct icmp_echo_hdr *)((char*)(p->payload) + sizeof(struct eth_hdr) + sizeof(struct ip_hdr));
	q_echo = (struct icmp_echo_hdr *)((char*)(q->payload) + sizeof(struct eth_hdr) + sizeof(struct ip_hdr));

	q_eth->dest = p_eth->src;
	q_eth->src = p_eth->dest;
	q_ip->src.addr = p_ip->dest.addr;
	q_ip->dest.addr = p_ip->src.addr;
	q_ip->_chksum = 0;
	q_ip->_chksum = inet_chksum(q_ip, sizeof(struct ip_hdr));
	q_echo->type = ICMP_ER;
	q_echo->code = 0;
	q_echo->chksum = 0;
	q_echo->chksum = inet_chksum(q_echo, q->tot_len - sizeof(struct eth_hdr) - sizeof(struct ip_hdr));

	if(0){
		int i;
		char *buf = q->payload;
		printf("\n\r");
		for(i=0;i<q->tot_len;i++)
			printf("0x%02x, ", buf[i]);
		printf("\n\r");
	}
	if (ERR_OK != netif->input(q, netif)){
		printf("\n\rfake_echo_reply input error");
		pbuf_free(q);
	}else
		printf("\n\rfake echo reply \n\r");
}
#endif // #if FAKE_PING_REPLY

/**
* This function should do the actual transmission of the packet. The packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
*
* @param netif the lwip network interface structure for this ethernetif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
* @return ERR_OK if the packet could be sent
*         an err_t value if the packet couldn't be sent
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
*       strange results. You might consider waiting for space in the DMA queue
*       to become availale since the stack doesn't retry to send a packet
*       dropped because of memory failure (except for the TCP timers).
*/

static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
#if !CONFIG_WLAN
	static xSemaphoreHandle xTxSemaphore = NULL;
	struct pbuf *q;
	uint32_t l = 0;
	u8 *buffer ;
  
	if (xTxSemaphore == NULL)
	{
		vSemaphoreCreateBinary (xTxSemaphore);
	} 

	if (xSemaphoreTake(xTxSemaphore, netifGUARD_BLOCK_TIME))
	{
		buffer =  (u8 *)(DMATxDescToSet->Buffer1Addr);
		for(q = p; q != NULL; q = q->next) 
		{
		  memcpy((u8_t*)&buffer[l], q->payload, q->len);
		  l = l + q->len;
		}
		ETH_Prepare_Transmit_Descriptors(l);
		xSemaphoreGive(xTxSemaphore);
	}
#else
	/* Refer to eCos lwip eth_drv_send() */
	struct eth_drv_sg sg_list[MAX_ETH_DRV_SG];
	int sg_len = 0;
	struct pbuf *q;

	if(!rltk_wlan_running(netif_get_idx(netif)))
		return ERR_IF;

	for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) {
		sg_list[sg_len].buf = (unsigned int) q->payload;
		sg_list[sg_len++].len = q->len;
	}

#if FAKE_PING_REPLY
	{
		char *header = p->payload;
		if(header[12] == 0x08 && header[13] == 0x06)
		{ // arp packet
			if(header[21] == 0x01)
			{ // arp request packet
				printf("\n\rfake_ping: arp request packet.");
				if(0)
				{
					int i;
					printf("\n\r");
					for (q = p; q != NULL; q = q->next) 
					{
						char *buf = q->payload;
						for(i=0;i<q->len;i++)
							printf("0x%02x, ", buf[i]);
					}
					printf("\n\r");
				}
				fake_arp_reply(netif, p);
				return ERR_OK;
			}
		}else if(header[12] == 0x08 && header[13] == 0x00)
		{ // ip packet
			if(header[15] == 0x00 && header[23] == 0x01)
			{ // icmp packet
				printf("\n\rfake_ping: icmp packet.");
				if(0){
					int i;
					printf("\n\r");
					for (q = p; q != NULL; q = q->next) 
					{
						char *buf = q->payload;
						for(i=0;i<q->len;i++)
							printf("0x%02x, ", buf[i]);
					}
					printf("\n\r");
				}
				fake_echo_reply(netif, p);
				return ERR_OK;
			}
		}
	}
#endif // #if FAKE_PING_REPLY
	if (sg_len)
		rltk_wlan_send(netif_get_idx(netif), sg_list, sg_len, p->tot_len);
#endif // #if !CONFIG_WLAN

  	return ERR_OK;
}

  	
#if !CONFIG_WLAN
/**
* Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
*         NULL on memory error
*/
static struct pbuf * low_level_input(struct netif *netif)
{
  struct pbuf *p, *q;
  u16_t len;
  uint32_t l=0,i =0;
  FrameTypeDef frame;
  u8 *buffer;
  __IO ETH_DMADESCTypeDef *DMARxNextDesc;
  
  p = NULL;
  
  /* Get received frame */
  frame = ETH_Get_Received_Frame_interrupt();
  
  /* check that frame has no error */
  if ((frame.descriptor->Status & ETH_DMARxDesc_ES) == (uint32_t)RESET)
  {
    
    /* Obtain the size of the packet and put it into the "len" variable. */
    len = frame.length;
    buffer = (u8 *)frame.buffer;

    /* We allocate a pbuf chain of pbufs from the pool. */
    p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
 
    /* Copy received frame from ethernet driver buffer to stack buffer */
    if (p != NULL)
    { 
      for (q = p; q != NULL; q = q->next)
      {
        memcpy((u8_t*)q->payload, (u8_t*)&buffer[l], q->len);
        l = l + q->len;
      } 
    }
  }
  
  /* Release descriptors to DMA */
  /* Check if received frame with multiple DMA buffer segments */
  if (DMA_RX_FRAME_infos->Seg_Count > 1)
  {
    DMARxNextDesc = DMA_RX_FRAME_infos->FS_Rx_Desc;
  }
  else
  {
    DMARxNextDesc = frame.descriptor;
  }
  
  /* Set Own bit in Rx descriptors: gives the buffers back to DMA */
  for (i=0; i<DMA_RX_FRAME_infos->Seg_Count; i++)
  {  
    DMARxNextDesc->Status = ETH_DMARxDesc_OWN;
    DMARxNextDesc = (ETH_DMADESCTypeDef *)(DMARxNextDesc->Buffer2NextDescAddr);
  }
  
  /* Clear Segment_Count */
  DMA_RX_FRAME_infos->Seg_Count =0;
  
  
  /* When Rx Buffer unavailable flag is set: clear it and resume reception */
  if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)  
  {
    /* Clear RBUS ETHERNET DMA flag */
    ETH->DMASR = ETH_DMASR_RBUS;
      
    /* Resume DMA reception */
    ETH->DMARPDR = 0;
  }
  return p;
}


/**
* This function is the ethernetif_input task, it is processed when a packet 
* is ready to be read from the interface. It uses the function low_level_input() 
* that should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif the lwip network interface structure for this ethernetif
*/
void ethernetif_input( void * pvParameters )
{
  struct pbuf *p;
  
	for( ;; )
	{
		if (xSemaphoreTake( s_xSemaphore, emacBLOCK_TIME_WAITING_FOR_INPUT)==pdTRUE)
		{
			p = low_level_input( s_pxNetIf );
			if (ERR_OK != s_pxNetIf->input( p, s_pxNetIf))
			{
				pbuf_free(p);
				p=NULL;
			}
		}
	}
}
#endif

/* Refer to eCos eth_drv_recv to do similarly in ethernetif_input */
void ethernetif_recv(struct netif *netif, int total_len)
{
#if CONFIG_WLAN
	struct eth_drv_sg sg_list[MAX_ETH_DRV_SG];
	struct pbuf *p, *q;
	int sg_len = 0;

	if(!rltk_wlan_running(netif_get_idx(netif)))
		return;

	if ((total_len > MAX_ETH_MSG) || (total_len < 0))
		total_len = MAX_ETH_MSG;

	// Allocate buffer to store received packet
	p = pbuf_alloc(PBUF_RAW, total_len, PBUF_POOL);
	if (p == NULL) {
		printf("\n\rCannot allocate pbuf to receive packet");
		return;
	}

	// Create scatter list
	for (q = p; q != NULL && sg_len < MAX_ETH_DRV_SG; q = q->next) {
   		sg_list[sg_len].buf = (unsigned int) q->payload;
		sg_list[sg_len++].len = q->len;
	}

	// Copy received packet to scatter list from wrapper rx skb
  	//printf("\n\rwlan:%c: Recv sg_len: %d, tot_len:%d", netif->name[1],sg_len, total_len);
	rltk_wlan_recv(netif_get_idx(netif), sg_list, sg_len);

	// Pass received packet to the interface
	if (ERR_OK != netif->input(p, netif))
		pbuf_free(p);
#endif
}

/**
* Should be called at the beginning of the program to set up the
* network interface. It calls the function low_level_init() to do the
* actual setup of the hardware.
*
* This function should be passed as a parameter to netif_add().
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if the loopif is initialized
*         ERR_MEM if private data couldn't be allocated
*         any other err_t on error
*/
err_t ethernetif_init(struct netif *netif)
{
  LWIP_ASSERT("netif != NULL", (netif != NULL));

#if !CONFIG_WLAN
	/* ethernet */
	netif->name[0] = IFNAME0;
	netif->name[1] = IFNAME1;
#else 
	/* wlan interface*/
#if LWIP_NETIF_HOSTNAME
	/* Initialize interface hostname */
	if(netif->name[1] == '0')
		netif->hostname = "lwip0";
	else if(netif->name[1] == '1')
		netif->hostname = "lwip1";
#endif /* LWIP_NETIF_HOSTNAME */

#endif // WLAN

  netif->output = etharp_output;
  netif->output_ip6 = ethip6_output ;//Add for ipv6
  netif->linkoutput = low_level_output;

  /* initialize the hardware */
  low_level_init(netif);

  etharp_init();
  sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);

  return ERR_OK;
}


static void arp_timer(void *arg)
{
  etharp_tmr();
  sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
}

/*
 * For FreeRTOS tickless
 */
int lwip_tickless_used = 0;


int arp_timeout_exist(void)
{
	struct sys_timeouts *timeouts;
	struct sys_timeo *t;

	timeouts = sys_arch_timeouts();

	for(t = timeouts->next; t != NULL;t = t->next)
		if(t->h == arp_timer)
			return 1;

	return 0;
}

//Called by rltk_wlan_PRE_SLEEP_PROCESSING()
void lwip_PRE_SLEEP_PROCESSING(void)
{
	if(arp_timeout_exist()) {
		tcpip_untimeout(arp_timer, NULL);
	}
	lwip_tickless_used = 1;
}

//Called in ips_leave() path, support tickless when wifi power wakeup due to ioctl or deinit
void lwip_POST_SLEEP_PROCESSING(void)
{
	if(lwip_tickless_used) {
		tcpip_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
	}
}
