#include "archinc.h"
#include "cli.h"
#include "strutil.h"
#include "zarlink.h"

#ifdef CONFIG_INCLUDE_ZARLINK

#include <stdio.h>
#include <stdlib.h>

/* for 'ingressEgress', 0=mirror disabled, 1=ingress, 2=egress */

static struct {
  int port;
  int ctrlReg;
  int ingressEgress;
  int reflectPort;
  } mirrors[2];

static int addMirror(int port, int ingressEgress, int reflectPort)
{
  int i;
  unsigned char mirrorReg;

  if (!((ingressEgress == 1) || (ingressEgress == 2)))
    return -1;

  for(i=0;i<2;i++) {
    if (mirrors[i].ingressEgress == 0)
      break;
    }

  if (i==2) 
    return -1;

  mirrorReg = (unsigned char)0x80; // start by activating mirror
  mirrorReg |= (unsigned char)(port & 0x07);
  mirrorReg |= (unsigned char)((reflectPort & 0x07) << 4);

  if (ingressEgress == 1)
    mirrorReg &= ~(0x08);
  else if (ingressEgress == 2)
    mirrorReg |= 0x08;

  mirrors[i].port = port;
  mirrors[i].ingressEgress = ingressEgress;
  mirrors[i].reflectPort = reflectPort;

  zarlinkIndexedWrite(mirrors[i].ctrlReg, mirrorReg);

  return 0;
}

static int removeMirror(int port, int ingressEgress)
{
  int i;

  if (!((ingressEgress == 1) || (ingressEgress == 2)))
    return -1;

  for(i=0;i<2;i++) {
    if ((mirrors[i].port == port) && (mirrors[i].ingressEgress == ingressEgress)) {
      mirrors[i].ingressEgress = 0;
      break;
      }
    }

  if (i==2) 
    return -1;

  zarlinkIndexedWrite(mirrors[i].ctrlReg, (unsigned char)0);

  return 0;
}

static void printAllMirrors(void)
{
  int bufLen;
  char * pOutBuf;
  int i;

  pOutBuf = clicharGetOutputBuffer(&bufLen);
  
  for(i=0;i<2;i++) {
    if (mirrors[i].ingressEgress != 0) {
      sprintf(pOutBuf, "mirror[%d]: port[%d] %s is reflected to port[%d]\r\n",
        i, 
        mirrors[i].port, 
        ((mirrors[i].ingressEgress == 1) ? "ingress" : "egress"), 
        mirrors[i].reflectPort);
      }
    else {
      sprintf(pOutBuf, "mirror[%d] is not enabled\r\n", i);
      }
    // output buffer
    clicharSendOutputBuffer(pOutBuf, bufLen);
    } // for loop
}

static int mirrorHandler(int argc, char * argv[], int flags)
{
  int retVal = -1;
  int foo;
  int port, reflect;
  int ingrEgr;
  int i;


  if (argc == 1) {
    retVal = 0;
    printAllMirrors();
    }
  else if (argc == 4) {
    // grab the 'subject' port
    port = (int)strtol(argv[1], (char **)NULL, 10);

    // figure out ingress or egress
    switch(*argv[2]) {
      case 'i':
      case 'I':
        ingrEgr = 1;
        break;
      case 'e':
      case 'E':
        ingrEgr = 2;
        break;
      default:
        return -1;
        break;
      }

    if ((*argv[3] == 'd') || (*argv[3] == 'D')) {
      if (port < 8) {
        retVal = removeMirror(port, ingrEgr);
        printAllMirrors();
        }
      }
    else {
      reflect = (int)strtol(argv[3], (char **)NULL, 10);
      if ((port < 8) && (reflect < 8)) {
        retVal = addMirror(port, ingrEgr, reflect);
        printAllMirrors();
        }
      }
    }

  return retVal;
}


int zarlinkMirrorInit(void)
{
  mirrors[0].ctrlReg = 0x710;
  mirrors[0].ingressEgress = 0;

  mirrors[1].ctrlReg = 0x711;
  mirrors[1].ingressEgress = 0;

  // disable the mirrors to start with
  zarlinkIndexedWrite(mirrors[0].ctrlReg, (unsigned char)0);
  zarlinkIndexedWrite(mirrors[1].ctrlReg, (unsigned char)0);

  cliRegisterCommand("mirror", mirrorHandler, "mirror [port] [ingress | egress] [reflectionPort | (d)isable] - replicate port traffic");
  return 0;
}

#endif

