#include "archinc.h"
#include "cli.h"
#include "strutil.h"
#include "zarlink.h"
#include "zl_bus.h"
#include "zl_util.h"
#include "nvstore.h"
#include "zlregs.h"
#include "zl_cpumac.h"

#include "uip/uip.h"
#include "uip/uip_arp.h"

#include <stdio.h>
#include <stdlib.h>

#ifdef CONFIG_INCLUDE_ZARLINK

#define NVSTORE_CPU_MACADDR_NAME "macaddr"

static char cpuMacAddr[6] = {0x00, 0x0c, 0x6a, 0x01, 0x02, 0x02};

/*
 * The structure of an ethernet header is: 
 * | Source MAC (6 bytes)  | Dest MAC (6 bytes) | type (2 bytes) |
 *
 * A type of 0x0800 indicates an IP packet
 *
 * The ethernet trailer is a 32-bit CRC
 */

int zarlinkCpuMacAddrSet(unsigned char *setMacAddr)
{
  int i;
  unsigned char mac5;

  if (setMacAddr == NULL) {
    return -1;
    }

  for(i=0;i<6;i++) {
    zarlinkIndexedWrite(CPUMAC(i), setMacAddr[i]);
    }

  // gig MAC last byte value
  zarlinkIndexedWrite(MACB5G, setMacAddr[5]);

  // setup the 'mask' for byte5[0:3] addr for ports 0-7
  mac5 = (setMacAddr[5] & 0x07);
  mac5 |= (mac5 << 4);
  
  zarlinkIndexedWrite(MACB01, mac5); // increment MAC port 0,1 byte5[0:3] addr
  zarlinkIndexedWrite(MACB23, mac5); // increment MAC port 2,3 byte5[0:3] addr
  zarlinkIndexedWrite(MACB45, mac5); // increment MAC port 4,5 byte5[0:3] addr
  zarlinkIndexedWrite(MACB67, mac5); // increment MAC port 6,7 byte5[0:3] addr

  return 0;
}

static struct uip_eth_addr uipMacAddr;

static int macAddrSetUip(unsigned char *setMacAddr)
{
  int i;

  if (setMacAddr == NULL) {
    return -1;
    }

  for(i=0;i<6;i++) {
    uipMacAddr.addr[i] = setMacAddr[i];
    }

  uip_setethaddr(uipMacAddr);
  return 0;
}

int zarlinkCpuMacAddrGet(unsigned char *getMacAddr)
{
  int i;

  if (getMacAddr == NULL) {
    return -1;
    }

  for(i=0;i<6;i++)
    getMacAddr[i] = zarlinkIndexedRead(CPUMAC(i));

  return 0;
}

static int setmacHandler(int argc, char *argv[], int flags)
{
  int retVal;
  int bufLen;
  char * pOutBuf;
  char length;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  if (argc == 4) {
    cpuMacAddr[0] = 0x00;
    cpuMacAddr[1] = 0x0c;
    cpuMacAddr[2] = 0x6a;
    cpuMacAddr[3] = (unsigned char)strtol(argv[1], (char **)NULL, 16);
    cpuMacAddr[4] = (unsigned char)strtol(argv[2], (char **)NULL, 16);
    cpuMacAddr[5]  = (unsigned char)strtol(argv[3], (char **)NULL, 16);
    sprintf(pOutBuf, "%s: setting cpu MAC address to %02x:%02x:%02x:%02x:%02x:%02x\r\n",
      argv[0], cpuMacAddr[0], cpuMacAddr[1], cpuMacAddr[1], cpuMacAddr[3], cpuMacAddr[4], cpuMacAddr[5]);
    length = 6;
    retVal = nvstoreWrite(NVSTORE_CPU_MACADDR_NAME, cpuMacAddr, length);
    zarlinkCpuMacAddrSet(cpuMacAddr);
    macAddrSetUip(cpuMacAddr);
    }
  else if (argc == 1) {
    zarlinkCpuMacAddrGet(cpuMacAddr);
    sprintf(pOutBuf, "%s: cpu MAC address is %02x:%02x:%02x:%02x:%02x:%02x\r\n",
      argv[0], cpuMacAddr[0], cpuMacAddr[1], cpuMacAddr[2], cpuMacAddr[3], cpuMacAddr[4], cpuMacAddr[5]);
      retVal = 0;
    }
  else {
    retVal = -1;
    }

  clicharSendOutputBuffer(pOutBuf, bufLen);

  return retVal;
}

int zarlinkCpuMacInit(void)
{
  int bufLen;
  char * pOutBuf;
  char length;

  pOutBuf = clicharGetOutputBuffer(&bufLen);

  length = 6;
  if (nvstoreRead(NVSTORE_CPU_MACADDR_NAME, cpuMacAddr, &length) == 0) {
    zarlinkCpuMacAddrSet(cpuMacAddr);
    }
  else {
    sprintf(pOutBuf, "! no CPU MAC address is stored -- use setmac to set one\r\n");
    }

  cliRegisterCommand("setmac", setmacHandler, "setmac [m2] [m1] [m0] - sets CPU MAC address to 00:0c:6a:m2:m1:m0");
  cliRegisterCommand("getmac", setmacHandler, "getmac - displays the current CPU MAC address ");

  clicharSendOutputBuffer(pOutBuf, bufLen);

  return 0;
}

int zarlinkCpuMacAddrSetUip(void)
{
    macAddrSetUip(cpuMacAddr);
    return 0;
}

#endif /* CONFIG_INCLUDE_ZARLINK */

