/**
 * @file
 * Ethernet Interface for RMII Devices
 */

/*
 * Skeleton Author: Adam Dunkels <adam@sics.se>
 */

#include "lwip/opt.h"

#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "netif/etharp.h"
#include "netif/ppp_oe.h"

#include "netif/rmiiif.h"
#include "LPC23xx.h"
#include "LPC2300_HAL.h"

using namespace LPC2300;

/* Define those to better describe your network interface. */
#define IFNAME0 'e'
#define IFNAME1 'n'

/**
 * Helper struct to hold private data used to operate your ethernet interface.
 * Keeping the ethernet address of the MAC in this struct is not necessary
 * as it is already kept in the struct netif.
 * But this is only an example, anyway...
 */
struct emacif {
  struct eth_addr *ethaddr;
  /* Add whatever per-interface state that is needed here. */
  unsigned long    duplex;
  unsigned long    speed;
};

/**
 * This function writes a specific value in a register of the PHY device.
 *
 * @param reg The specific register.
 * @param data The value to be stored.
 */
static void PHY_write(unsigned long reg, unsigned long data) {
  MAC_MCMD = 0x0000;
  MAC_MADR = PHY_ADDR | reg;
  MAC_MWTD = data;
  while(MAC_MIND!=0);
}

/**
 * This function reads a value from a register of the PHY device.
 *
 * @param reg The specific register.
 * @return The readed value.
 */
static unsigned long PHY_read(unsigned long reg) {
  MAC_MCMD = 0x0001;
  MAC_MADR = PHY_ADDR | reg;
  while((MAC_MIND&0x05) != 0);
  MAC_MCMD = 0x0000;
  return MAC_MRDD;
}

/**
 * This function starts up the PHY device
 *
 * @return It returns TRUE if everything is fine.
 */
static int PHY_init(struct emacif *emac) {
  unsigned long timeout =  MII_BMSR_TIMEOUT * 4;
  unsigned long regValue;

  MAC_MCFG = 0x8018;
  wait(0.2); // Short
  MAC_MCFG = 0x0018;
  MAC_MCMD = 0;
  MAC_COMMAND |=  0x0200; // RMII, &=~0x0200 for MII
  MAC_SUPP = 0x0100;      // RMII, 0x0000 for 10MBit
  wait(0.2); // Short
  PHY_write(PHY_BMCR, BMCR_RESET);
  wait(0.2); // Short
  
  while(timeout--) {
    regValue = PHY_read(PHY_BMCR);
    if((regValue & BMCR_RESET)== 0x0) {
      break;
    }
  }
  if(!timeout) {
    return FALSE;
  }
  
  regValue = PHY_read(PHY_PHYIDR1);
  if((regValue & 0x2000) != 0x2000) {
    return FALSE;
  }
    
  regValue = PHY_read(PHY_PHYIDR2);
  if((regValue & 0x5C90) != 0x5C90) {
    return FALSE;
  }
  
  PHY_write(PHY_BMCR, BMCR_AN | BMCR_RE_AN);
  /* auto negotiation, restart AN */
  /* RE_AN should be self cleared */


  timeout = MII_BMSR_TIMEOUT;
  while(timeout--) {
    regValue = PHY_read(PHY_PHYSTS);
    if((regValue & 0x0011) == 0x0011) {
      break;
    }
  } 
  if(!timeout) {
    return FALSE;
  }
  
  // Link should be established from here.
  if(regValue&0x4) {
    emac->duplex = FULL_DUPLEX;
  } else {
    emac->duplex = HALF_DUPLEX;
  }
  
  if(regValue&0x2) {
    emac->speed = SPEED_10;
  } else {
    emac->speed = SPEED_100;
  }
  
  return TRUE;
}

/**
 * In this function, the Tx descriptor queue is set up for use with the DMA.
 */
void EMACTxDescriptor_init() {
  unsigned long i;
  unsigned long *tx_desc_addr;
  unsigned long *tx_status_addr;
   
  for(i=0; i<EMAC_TX_DESCRIPTOR_COUNT; ++i) {
	tx_desc_addr      = (unsigned long *)(TX_DESCRIPTOR_ADDR + i*8);	             // two words at a time, packet and control
	*tx_desc_addr     = (unsigned long)  (EMAC_TX_BUFFER_ADDR + i*EMAC_BLOCK_SIZE);
	*(tx_desc_addr+1) = (unsigned long)  (EMAC_TX_DESC_INT | (EMAC_BLOCK_SIZE - 1)); // set size only

	tx_status_addr    = (unsigned long *)(TX_STATUS_ADDR + i*4);	                 // TX status, one word only, status info.
	*tx_status_addr   = (unsigned long)  0;		                                     // initially, set status info to 0
  }
    
  MAC_TXDESCRIPTOR    = TX_DESCRIPTOR_ADDR;	                                         // Base addr of tx descriptor array
  MAC_TXSTATUS        = TX_STATUS_ADDR;		                                         // Base addr of tx status
  MAC_TXDESCRIPTORNUM = EMAC_TX_DESCRIPTOR_COUNT - 1;	                             // number of tx descriptors, 16

  MAC_TXPRODUCEINDEX  = 0x0;	                                                     // TX descriptors point to zero
}

/**
 * In this function, the Rx descriptor queue is set up for use with the DMA.
 */
void EMACRxDescriptor_init() {
  unsigned long i;
  unsigned long *rx_desc_addr;
  unsigned long *rx_status_addr;
   
  for (i=0; i<EMAC_RX_DESCRIPTOR_COUNT; ++i) {
	rx_desc_addr        = (unsigned long *)(RX_DESCRIPTOR_ADDR + i*8); 	               // two words at a time, packet and control
	*rx_desc_addr       = (unsigned long)  (EMAC_RX_BUFFER_ADDR + i*EMAC_BLOCK_SIZE);
	*(rx_desc_addr+1)   = (unsigned long)  (EMAC_RX_DESC_INT | (EMAC_BLOCK_SIZE - 1)); // set size only
	
	rx_status_addr      = (unsigned long *)(RX_STATUS_ADDR + i * 8);	               // RX status, two words, status info. and status hash CRC.
	*rx_status_addr     = (unsigned long)0;	                                           // initially, set both status info and hash CRC to 0
	*(rx_status_addr+1) = (unsigned long)0; 
   }

  MAC_RXDESCRIPTOR      = RX_DESCRIPTOR_ADDR;	                                       // Base addr of rx descriptor array
  MAC_RXSTATUS          = RX_STATUS_ADDR;			                                   // Base addr of rx status
  MAC_RXDESCRIPTORNUM   = EMAC_RX_DESCRIPTOR_COUNT - 1;		                           // number of rx descriptors, 16

   MAC_RXCONSUMEINDEX   = 0x0;	                                                       // RX descriptor points to zero
}

/**
 * In this function, the hardware will be initialized.
 * Called from ethernetif_init().
 *
 * @param netif the already initialized lwip network interface structure
 *        for this ethernetif
 */
static int emac_hw_init(struct netif *netif) {
  struct emacif *emac = static_cast<struct emacif *>(netif->state);
//  unsigned long timeout =  MII_BMSR_TIMEOUT * 4;
  unsigned long regValue;
  
  /* maximum transfer unit */
  netif->mtu = EMAC_BLOCK_SIZE;
  
  /* device capabilities */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
 
  regValue = PCONP;
  regValue |= PCONP_EMAC_CLOCK;
  PCONP = regValue;
  
  wait(0.2); //Short
  
  // vv RMII only
  regValue = MAC_MODULEID;
  if(regValue==PHILIPS_EMAC_MODULE_ID) {
    /* This is the rev."-" ID for the existing MCB2300 board,
	   on rev. A, regVal should NOT equal to PHILIPS_EMAC_MODULE_ID,
	   P1.6 should NOT be set. */
	PINSEL2 = 0x50151105;	/* selects P1[0,1,4,6,8,9,10,14,15] */
  } else {
    PINSEL2 = 0x50150105;	/* selects P1[0,1,4,8,9,10,14,15] */
  }
  PINSEL3 = 0x00000005;	/* selects P1[17:16] */
  // ^^ RMII only
  // PINSEL2 = 0x55555555;	/* selects P1[15:0] */   MII alternative
  // PINSEL3 = 0x00000005;	/* selects P1[17:16] */  MII alternative
 
  MAC_MAC1    = 0xCF00;
  MAC_COMMAND = 0x0038;
  
  wait(0.2); //Short
  
  MAC_MAC1     =  0x0000;
  MAC_COMMAND &= ~0x0003; // Disable Rx/Tx-Enable
  MAC_MAC2     =  0x0000;
  MAC_IPGR     =  0x0012;
  MAC_CLRT     =  0x370F;
  MAC_MAXF     =  0x0600;
  
  if(!PHY_init(emac)) {
    return FALSE;
  };

  MAC_SA0 = ((netif->hwaddr[1] << 8) & 0x0000FF00) | ((netif->hwaddr[0]) & 0x000000FF);
  MAC_SA1 = ((netif->hwaddr[3] << 8) & 0x0000FF00) | ((netif->hwaddr[2]) & 0x000000FF);
  MAC_SA2 = ((netif->hwaddr[5] << 8) & 0x0000FF00) | ((netif->hwaddr[4]) & 0x000000FF);

  // TODO: Please optimize
  if((emac->speed==SPEED_10)&&(emac->duplex==HALF_DUPLEX)) {
	MAC_MAC2 = 0x30;         //  half duplex, CRC and PAD enabled.
	MAC_SUPP &= ~0x0100;     //  RMII Support Reg. speed is set to 10M
	MAC_COMMAND |= 0x0200;
	//MAC_COMMAND |= 0x0040; // [10]-half duplex,[9]-MII mode,[6]-Pass runt frame, [5]-RxReset
	MAC_IPGT = 0x0012;		 // IPG setting in half duplex mode
	
  } else if((emac->speed==SPEED_100)&&(emac->duplex == HALF_DUPLEX)) {
	MAC_MAC2 = 0x30;		 // half duplex, CRC and PAD enabled.
	MAC_SUPP |= 0x0100;		 // RMII Support Reg. speed is set to 100M
	MAC_COMMAND |= 0x0200;
	//MAC_COMMAND |= 0x0040; // [10]-half duplex,[9]-MII mode,[6]-Pass runt frame, [5]-RxReset
	MAC_IPGT = 0x0012;		 // IPG setting in half duplex mode
	
  } else if((emac->speed == SPEED_10)&&(emac->duplex == FULL_DUPLEX)) {
	MAC_MAC2 = 0x31;		 // full duplex, CRC and PAD enabled.
	MAC_SUPP &= ~0x0100;	 // RMII Support Reg. speed is set to 10M
	MAC_COMMAND |= 0x0600;
	//MAC_COMMAND |= 0x0440; //[10]-full duplex,[9]-MII mode,[6]-Pass runt frame, [5]-RxReset
	MAC_IPGT = 0x0015;		 // IPG setting in full duplex mode

  } else if((emac->speed==SPEED_100)&&(emac->duplex==FULL_DUPLEX)) {
  	MAC_MAC2 = 0x31;		 // full duplex, CRC and PAD enabled.
	MAC_SUPP |= 0x0100;		 // RMII Support Reg. speed is set to 100M
	MAC_COMMAND |= 0x0600;
	//MAC_COMMAND |= 0x0440; // [10]-full duplex,[9]-MII mode,[6]-Pass runt frame, [5]-RxReset
	MAC_IPGT = 0x0015;		 // IPG setting in full duplex mode
  }

  // TxDescriptor init (msg queues)
  EMACTxDescriptor_init();
  
  // RxDescriptor init (msg queues)
  EMACRxDescriptor_init();
  
  
  MAC_COMMAND |= (0x03);  // Tx/Rx-Enable
  MAC_MAC1 |= 0x0001;	// [1]-Pass All Rx Frame

  /* Set up RX filter, accept broadcast and perfect station */
  MAC_RXFILTERCTRL = ACC_UNICAST | ACC_BROADCAST | ACC_MATCH | ACC_MULTICAST;// | ACC_HASH;

  MAC_INTCLEAR = 0xFFFF; // clear all MAC interrupts
  
  /* MAC interrupt related register setting */
//  vic_vector(EMAC_INT, EMAC_handler, HIGHEST_PRIORITY);

  // Wakeup on Lan
  // MAC_RXFILTERWOLCLR = 0xFFFF; // set all bits to clear receive filter WOLs
  // MAC_RXFILTERCTRL |= 0x2000;	 // enable Rx Magic Packet and RxFilter Enable WOL
  // MAC_INTENABLE = 0x2000;	     // only enable WOL interrupt
  // else
  //MAC_INTENABLE = 0x00FF; // Enable all interrupts except SOFTINT and WOL
  return TRUE;
}

/**
 * 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) {
  LWIP_DEBUGF(NETIF_DEBUG, ("TXPRODUCEINDEX: %d, TXCONSUMEINDEX: %d\n", MAC_TXPRODUCEINDEX, MAC_TXCONSUMEINDEX));
//  struct emacif *emac = static_cast<struct emacif *>(netif->state);
  struct pbuf *q;

  unsigned long *tx_desc_addr;
  unsigned long TxProduceIndex;
  unsigned long TxConsumeIndex;

  TxProduceIndex = MAC_TXPRODUCEINDEX;
  TxConsumeIndex = MAC_TXCONSUMEINDEX;

  if((TxProduceIndex == TxConsumeIndex-1) || (TxProduceIndex == 0 && TxConsumeIndex == EMAC_TX_DESCRIPTOR_COUNT-1)) {
	/* reach the limit, that probably should never happen */
	/* To be tested */
	LWIP_DEBUGF(NETIF_DEBUG, ("low_level_output: No MEM.\n"));
	LWIP_DEBUGF(NETIF_DEBUG, ("low_level_output end.\n"));
	return ERR_MEM;
  }

  #if ETH_PAD_SIZE
    pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
  #endif

  LWIP_DEBUGF(NETIF_DEBUG, ("Oldpacket: %d, Oldpayload: %x, Oldstatus: %x.\n", TxProduceIndex, EMAC_TX_BUFFER(TxProduceIndex), TX_STATUS_INFO(TxProduceIndex)));
  for(q = p; q != NULL; q = q->next) {
    LWIP_DEBUGF(NETIF_DEBUG, ("Try ro enqueue a new packet in the dma tx list.\n"));
    /* Send the data from the pbuf to the interface, one pbuf at a
       time. The size of the data in each pbuf is kept in the ->len
       variable. */
       
    // TODO: Insert TX_BLOCK_ADDR instead q->payload (memcpy)
	tx_desc_addr = (unsigned long *)(TX_DESCRIPTOR_ADDR + TxProduceIndex * 8);  // two words at a time, packet and control
//	mem_free(*tx_desc_addr);
    LWIP_DEBUGF(NETIF_DEBUG, ("TxDest:  %02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX.\n", (char)*((char *)q->payload+0), (char)*((char *)q->payload+1), (char)*((char *)q->payload+2), (char)*((char *)q->payload+3), (char)*((char *)q->payload+4), (char)*((char *)q->payload+5)));
    LWIP_DEBUGF(NETIF_DEBUG, ("TxSourc: %02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX.\n", (char)*((char *)q->payload+6), (char)*((char *)q->payload+7), (char)*((char *)q->payload+8), (char)*((char *)q->payload+9), (char)*((char *)q->payload+10), (char)*((char *)q->payload+11)));


    memcpy((void *)*tx_desc_addr,q->payload, q->len);                                // full block
	*(tx_desc_addr+1) = (unsigned)(EMAC_TX_DESC_INT | ((!q->next)? EMAC_TX_DESC_LAST : 0) | (q->len -1)); // set TX descriptor control field
	TxProduceIndex++;
	LWIP_DEBUGF(NETIF_DEBUG, ("Packet: %d, Payload: %x, Status: %x.\n", TxProduceIndex-1, EMAC_TX_BUFFER(TxProduceIndex-1), TX_STATUS_INFO(TxProduceIndex-1)));
		
	if(TxProduceIndex == EMAC_TX_DESCRIPTOR_COUNT) {
	  TxProduceIndex = 0;
	}
	
	MAC_TXPRODUCEINDEX = TxProduceIndex;
  }

  #if ETH_PAD_SIZE
    pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
  #endif
  
  LINK_STATS_INC(link.xmit);
  LWIP_DEBUGF(NETIF_DEBUG, ("low_level_output end.\n"));
  return ERR_OK;
}
/**
 * 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;
  unsigned long packet;
  u16_t len;
  LWIP_DEBUGF(NETIF_DEBUG, ("RXPRODUCEINDEX: %d, RXCONSUMEINDEX: %d.\n", MAC_RXPRODUCEINDEX, MAC_RXCONSUMEINDEX));
  
  if(MAC_RXPRODUCEINDEX == MAC_RXCONSUMEINDEX) {
    // No Packet is received
    return NULL;
  }
  packet = MAC_RXCONSUMEINDEX;
  
  /* Obtain the size of the packet and put it into the "len"
     variable. */
  len = (RX_STATUS_INFO(packet) & DESC_SIZE_MASK) -1;
  LWIP_DEBUGF(NETIF_DEBUG, ("Trying to receive packet no %d from dma with length %d.\n", packet, len));
  LWIP_DEBUGF(NETIF_DEBUG, ("Packet status: %x\n", RX_STATUS_INFO(packet)));

  #if ETH_PAD_SIZE
    len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
  #endif
  
  /* We allocate a pbuf chain of pbufs from the pool. */
  p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
  
  if(p != NULL) {
    #if ETH_PAD_SIZE
      pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
    #endif

    /* We iterate over the pbuf chain until we have read the entire
     * packet into the pbuf. */
    unsigned long rest = 0;
    for(q = p; q != NULL; q = q->next) {
      /* Read enough bytes to fill this pbuf in the chain. The
       * available data in the pbuf is given by the q->len
       * variable. */
      memcpy(q->payload,(void *)(RX_DESCRIPTOR_PACKET(packet)+rest),(q->len<len-rest)?q->len:(len-rest));
      rest += q->len;
      LWIP_DEBUGF(NETIF_DEBUG, ("Buffer length: %d\n", q->len));
    }
    if(++packet==EMAC_RX_DESCRIPTOR_COUNT) {
      packet = 0;
    }
    
    MAC_RXCONSUMEINDEX = packet;

    #if ETH_PAD_SIZE
      pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */ 
    #endif

    LINK_STATS_INC(link.recv);
  } else {
    MAC_RXCONSUMEINDEX++;
    LINK_STATS_INC(link.memerr);
    LINK_STATS_INC(link.drop);
  }
  return p;  
}

/**
 * This function should be called 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 emac_input(struct netif *netif) {
  //struct emacif *emac;
  struct eth_hdr *ethhdr;
  struct pbuf *p;

  /* move received packet into a new pbuf */
  p = low_level_input(netif);
  /* no packet could be read, silently ignore this */
  if (p == NULL) return;
  /* points to packet payload, which starts with an Ethernet header */
  ethhdr = (struct eth_hdr *)(p->payload);

  switch(htons(ethhdr->type)) {
    /* IP or ARP packet? */
    case ETHTYPE_IP:
      etharp_ip_input(netif, p);
      pbuf_header(p, -((s16_t) sizeof(struct eth_hdr)));
      if(netif->input(p, netif)!=ERR_OK) {
        LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
      }
      break;
    case ETHTYPE_ARP:
      etharp_arp_input(netif, (struct eth_addr *)&(netif->hwaddr[0]), p);
      break;
#if PPPOE_SUPPORT
    /* PPPoE packet? */
    case ETHTYPE_PPPOEDISC:
    case ETHTYPE_PPPOE:
#endif /* PPPOE_SUPPORT */
    /* full packet send to tcpip_thread to process */
    default:
      break;
  }
  
  pbuf_free(p);
  p = NULL;
}

/**
 * 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 emac_init(struct netif *netif) {
  struct emacif *emac;

  LWIP_ASSERT("netif != NULL", (netif != NULL));
    
  emac = static_cast<struct emacif *>(mem_malloc(sizeof(struct emacif)));
  if (emac == NULL) {
    return ERR_MEM;
  }

  /*
   * Initialize the snmp variables and counters inside the struct netif.
   * The last argument should be replaced with your link speed, in units
   * of bits per second.
   */
  NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 1500); // <-- TODO: Put the right speed in

  netif->state = emac;
  netif->name[0] = IFNAME0;
  netif->name[1] = IFNAME1;
  /* We directly use etharp_output() here to save a function call.
   * You can instead declare your own function an call etharp_output()
   * from it if you have to do some checks before sending (e.g. if link
   * is available...) */
  netif->output = etharp_output;
  netif->linkoutput = low_level_output;
  
  emac->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);
  emac->duplex = FULL_DUPLEX;
  emac->speed = SPEED_100;
  
  /* initialize the hardware */
  emac_hw_init(netif);

  return ERR_OK;
}
