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

#ifdef CONFIG_INCLUDE_ZARLINK

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

/*
 * when sending a request, use the current value of cmdFrameIndex. The reply received from 
 * the switch should match this index. If so, increment the index circularly. 
 */

static zarlinkPortStats_t portStatsCumulative[10];
static zarlinkPortStats_t portStatsCurrent[10];

static union {
  unsigned char requestFrameData8[8];
  unsigned short requestFrameData16[4];
  } request;

static int getStatsHandler(int argc, char *argv[], int flags);
static int showStatsHandler(int argc, char *argv[], int flags);

static int ctrlRequestStats(int port, int octet)
{
  int i;
  unsigned char tmpStatus;
  unsigned int baseAddr;

  // initialize command frame data
  for(i=0;i<8;i++)
    request.requestFrameData8[i] = 0x00;

  request.requestFrameData8[0] = 0x18; // read request
  request.requestFrameData8[1] = 0x00; // statistic counter memory

  /*
   * calculate base address in internal memory
   *
   * NOTE: internal memory is addressed in 8-byte chunks called octets, 
   * NOT bytes! Each port has eight octets of RMON data so, for example, 
   * port 1 base address is 0x8, port 7 base address is 0x38 (56).
   */

  baseAddr = (unsigned int)(((port * 0x8) + octet) & 0x0FFF);

  request.requestFrameData8[2] = (unsigned char) (baseAddr        & 0xff);
  request.requestFrameData8[3] = (unsigned char)((baseAddr >>  8) & 0xff);
  request.requestFrameData8[4] = (unsigned char)((baseAddr >> 16) & 0xff);

  request.requestFrameData8[5] = 0x01; // number of 8-byte octets
  request.requestFrameData8[6] = (unsigned char)((port*8) + octet);

  // poll status reg bit 0
  for(i=0;i<10000;i++) {
    tmpStatus = zarlinkStatusRead();
    if ((tmpStatus & 0x01) != 0)
      break;
    }

  if ((tmpStatus & 0x01) == 0)
    return -1;

  for(i=0;i<8/2;i++)
    zarlinkCtrlBuffer1Write(request.requestFrameData16[i]);

  zarlinkCommandWrite(0x01);

  return 0;
}

static int ctrlClearStats(int port, int octet)
{
  int i;
  unsigned char tmpStatus;
  unsigned int baseAddr;

  // initialize command frame data
  for(i=0;i<8;i++)
    request.requestFrameData8[i] = 0x00;

  request.requestFrameData8[0] = 0x28; // write request
  request.requestFrameData8[1] = 0x00; // statistic counter memory

  /*
   * calculate base address in internal memory
   *
   * NOTE: internal memory is addressed in 8-byte chunks called octets, 
   * NOT bytes! Each port has eight octets of RMON data so, for example, 
   * port 1 base address is 0x8, port 7 base address is 0x38 (56).
   */

  baseAddr = (unsigned int)(((port * 0x8) + octet) & 0x0FFF);

  request.requestFrameData8[2] = (unsigned char) (baseAddr        & 0xff);
  request.requestFrameData8[3] = (unsigned char)((baseAddr >>  8) & 0xff);
  request.requestFrameData8[4] = (unsigned char)((baseAddr >> 16) & 0xff);

  request.requestFrameData8[5] = 0x01; // number of 8-byte octets
  request.requestFrameData8[6] = (unsigned char)((port*8) + octet);

  // poll status reg bit 0
  for(i=0;i<10000;i++) {
    tmpStatus = zarlinkStatusRead();
    if ((tmpStatus & 0x01) != 0)
      break;
    }

  if ((tmpStatus & 0x01) == 0)
    return -1;

  for(i=0;i<8/2;i++)
    zarlinkCtrlBuffer1Write(request.requestFrameData16[i]);

  for(i=0;i<8/2;i++)
    zarlinkCtrlBuffer1Write((unsigned short)0);

  zarlinkCommandWrite(0x01);

  return 0;
}

static int ctrlClearPort(int port)
{
  int octet;
  int retStat = 0;

  for(octet=0;((octet<8) && (retStat == 0)); octet++) {
    retStat = ctrlClearStats(port, octet);
    }

  return retStat;
}

static int ctrlClearAll(void)
{
  int port;
  int retStat = 0;

  for(port=0;((port<10) && (retStat == 0)); port++) {
    retStat = ctrlClearPort(port);
    }

  return retStat;
}

static octet0 * pOctet0;
static octet1 * pOctet1;
static octet2 * pOctet2;
static octet3 * pOctet3;
static octet4 * pOctet4;
static octet5 * pOctet5;
static octet6 * pOctet6;
static octet7 * pOctet7;

static int decodeStats(unsigned short *pRawOctetData16, int port, int octet)
{
  int retVal = -1;
  unsigned char * pRawOctetData = (unsigned char *)pRawOctetData16;

  if (pRawOctetData == NULL)
    return retVal;

  if ((port < 0) || (port > 10))
    return retVal;

  if ((octet < 0) || (octet > 8))
    return retVal;

  /*
   * for completed read, the first byte (byte 0) of data, bits[7:4] should be 0b0101 for
   * completed read. The frame # (byte 7) should also be examined, and also the 
   * address at bytes 3 and 4. The actual data begins at byte 8.
   */

  switch(octet) {
    case 0:
      pOctet0 = (octet0 *)pRawOctetData;
      portStatsCurrent[port].bytesSent = pOctet0->bytesSent;
      portStatsCurrent[port].unicastFrameSent = pOctet0->unicastFrameSent;
      portStatsCurrent[port].frameSentFail = pOctet0->frameSentFail;
      retVal = 0;
      break;
    case 1:
      pOctet1 = (octet1 *)pRawOctetData;
      portStatsCurrent[port].flowCtrlSent = pOctet1->flowCtrlSent;
      portStatsCurrent[port].nonUnicastFrameSent = pOctet1->nonUnicastFrameSent;
      portStatsCurrent[port].bytesReceived= pOctet1->bytesReceived;
      retVal = 0;
      break;
    case 2:
      pOctet2 = (octet2 *)pRawOctetData;
      portStatsCurrent[port].frameReceived  = pOctet2->frameReceived;
      portStatsCurrent[port].totalBytesReceived = pOctet2->totalBytesReceived;
      retVal = 0;
      break;
    case 3:
      pOctet3 = (octet3 *)pRawOctetData;
      portStatsCurrent[port].totalFramesReceived = pOctet3->totalFramesReceived;
      portStatsCurrent[port].flowCtrlFramesReceived = pOctet3->flowCtrlFramesReceived;
      portStatsCurrent[port].mcastFramesReceived = pOctet3->mcastFramesReceived;
      portStatsCurrent[port].bcastFramesReceived = pOctet3->bcastFramesReceived;

      retVal = 0;
      break;
    case 4:
      pOctet4 = (octet4 *)pRawOctetData;
      portStatsCurrent[port].len64Frames = pOctet4->len64Frames;
      portStatsCurrent[port].jabberFrames = pOctet4->jabberFrames;
      portStatsCurrent[port].len65To127Frames = pOctet4->len65To127Frames;
      portStatsCurrent[port].oversizeFrames = pOctet4->oversizeFrames;
      retVal = 0;
      break;
    case 5:
      pOctet5 = (octet5 *)pRawOctetData;
      portStatsCurrent[port].len128To255Frames = pOctet5->len128To255Frames;
      portStatsCurrent[port].len256To511Frames = pOctet5->len256To511Frames;
      portStatsCurrent[port].len512To1023Frames = pOctet5->len512To1023Frames;
      portStatsCurrent[port].len1024To1522Frames = pOctet5->len1024To1522Frames;
      retVal = 0;
      break;
    case 6:
      pOctet6 = (octet6 *)pRawOctetData;
      portStatsCurrent[port].fragment = pOctet6->fragment;
      portStatsCurrent[port].alignError  = pOctet6->alignError;
      portStatsCurrent[port].runtFrames = pOctet6->runtFrames;
      portStatsCurrent[port].crcErrors = pOctet6->crcErrors;
      portStatsCurrent[port].shortEvent = pOctet6->shortEvent;
      retVal = 0;
      break;
    case 7:
      pOctet7 = (octet7 *)pRawOctetData;
      portStatsCurrent[port].collision = pOctet7->collision;
      portStatsCurrent[port].droppedFrames = pOctet7->droppedFrames;
      portStatsCurrent[port].filteringCounter = pOctet7->filteringCounter;
      portStatsCurrent[port].delayExceedDiscardCounter = pOctet7->delayExceedDiscardCounter;
      portStatsCurrent[port].lateCollision = pOctet7->lateCollision;
      retVal = 0;
      break;
    default:
      break;
    }

  return retVal;
}

int zarlinkRmonGet(int port)
{
  int i;
  int retVal = 0;

  for(i=0; ((i<8) && (retVal == 0)); i++) {
    retVal = ctrlRequestStats(port, i);
    if (retVal != 0) {
       return -1;
      }
    }
  return 0;
}

static int getStatsHandler(int argc, char *argv[], int flags)
{
  int retVal = -1;
  int bufLen;
  char * pOutBuf;
  int port;

  if (argc < 2)
    return retVal;

  port = (int)strtol(argv[1], (char **)NULL, 10);
  return zarlinkRmonGet(port);
}

static int showStatsHandler(int argc, char *argv[], int flags)
{
  int retVal = -1;
  int bufLen;
  char * pOutBuf;
  int port;

  if (argc < 2)
    return retVal;

  pOutBuf = clicharGetOutputBuffer(&bufLen);
  port = (int)strtol(argv[1], (char **)NULL, 10);

  if (retVal == 0) {
    sprintf(pOutBuf, "--- Zarlink port stats for port [%d] ---\r\n", port);
    clicharSendOutputBuffer(pOutBuf, bufLen);    
    
    sprintf(pOutBuf, "  TX: bytes[%u %u]  unicastFrame[%u]  frameTxFail[%u]\r\n",
      portStatsCumulative[port].bytesSent, // high order 32-bits
      portStatsCurrent[port].bytesSent, 
      (portStatsCurrent[port].unicastFrameSent + 
       portStatsCumulative[port].unicastFrameSent), 
      (portStatsCurrent[port].frameSentFail +
       portStatsCumulative[port].frameSentFail)
       );
    clicharSendOutputBuffer(pOutBuf, bufLen);    

    sprintf(pOutBuf, "  RX: bytes[%u %u]  frames[%u %u]  totalBytes[%u %u]  totalFrames[%u]\r\n",
      portStatsCumulative[port].bytesReceived, // high order 32-bits
      portStatsCurrent[port].bytesReceived, 
      portStatsCumulative[port].frameReceived, // high order 32-bits
      portStatsCurrent[port].frameReceived, 
      portStatsCumulative[port].totalBytesReceived, // high order 32-bits
      portStatsCurrent[port].totalBytesReceived, 
      (portStatsCurrent[port].totalFramesReceived +
       portStatsCumulative[port].totalFramesReceived)
      );
    clicharSendOutputBuffer(pOutBuf, bufLen);    

    sprintf(pOutBuf, "  RX: mcastFrames[%u]  bcastFrames[%u]  flowCtrlFrames[%u]\r\n",
      (portStatsCurrent[port].mcastFramesReceived +
       portStatsCumulative[port].mcastFramesReceived), 
      (portStatsCurrent[port].bcastFramesReceived +
       portStatsCumulative[port].bcastFramesReceived), 
      (portStatsCurrent[port].flowCtrlFramesReceived +
       portStatsCurrent[port].flowCtrlFramesReceived)
       );
    clicharSendOutputBuffer(pOutBuf, bufLen);    

    sprintf(pOutBuf, " ERR: align[%u]  runt[%u]  crc[%u]  shortEvent[%u]\r\n",
      (portStatsCurrent[port].alignError +
       portStatsCumulative[port].alignError), 
      (portStatsCurrent[port].runtFrames +
       portStatsCumulative[port].runtFrames), 
      (portStatsCurrent[port].crcErrors + 
       portStatsCumulative[port].crcErrors), 
      (portStatsCurrent[port].shortEvent +
       portStatsCumulative[port].shortEvent)
       );
    clicharSendOutputBuffer(pOutBuf, bufLen);    

    sprintf(pOutBuf, "FLOW: flowCtrlFramesRecv[%u]  flowCtrlSent[%u]\r\n",
      (portStatsCurrent[port].flowCtrlFramesReceived +
       portStatsCumulative[port].flowCtrlFramesReceived),
      (portStatsCurrent[port].flowCtrlSent +
       portStatsCumulative[port].flowCtrlSent)
       );
    clicharSendOutputBuffer(pOutBuf, bufLen);    

    sprintf(pOutBuf, " PKT: len64[%u]  len65-127[%u]  jabber[%u]  oversize[%u]\r\n",
      (portStatsCurrent[port].len64Frames +
       portStatsCumulative[port].len64Frames),
      (portStatsCurrent[port].len65To127Frames +
       portStatsCumulative[port].len65To127Frames),
      (portStatsCurrent[port].jabberFrames +
       portStatsCumulative[port].jabberFrames),
      (portStatsCurrent[port].oversizeFrames +
       portStatsCumulative[port].oversizeFrames)
       );
    clicharSendOutputBuffer(pOutBuf, bufLen);    
      
    sprintf(pOutBuf, " LEN: 128-255[%u]  256-511[%u]  512-1023[%u]  1024-1522[%u]\r\n",
      (portStatsCurrent[port].len128To255Frames +
       portStatsCumulative[port].len128To255Frames),
      (portStatsCurrent[port].len256To511Frames +
       portStatsCumulative[port].len256To511Frames),
      (portStatsCurrent[port].len512To1023Frames +
       portStatsCumulative[port].len512To1023Frames),
      (portStatsCurrent[port].len1024To1522Frames +
       portStatsCumulative[port].len1024To1522Frames)
       );
    clicharSendOutputBuffer(pOutBuf, bufLen);    

    sprintf(pOutBuf, "OOPS: fragment[%u]  dropped[%u]  filterCount[%u]  delayExceedDiscard[%u]\r\n",
      (portStatsCurrent[port].fragment +
       portStatsCumulative[port].fragment),
      (portStatsCurrent[port].droppedFrames +
       portStatsCumulative[port].droppedFrames),
      (portStatsCurrent[port].filteringCounter +
       portStatsCumulative[port].filteringCounter),
      (portStatsCurrent[port].delayExceedDiscardCounter +
       portStatsCumulative[port].delayExceedDiscardCounter)
       );
    clicharSendOutputBuffer(pOutBuf, bufLen);    
    }

  return retVal;
}

static void clearLocalStatsPort(int port)
{

  if ((port < 0) || (port > 9))
    return;

  portStatsCurrent[port].bytesSent = 0;
  portStatsCurrent[port].unicastFrameSent = 0;
  portStatsCurrent[port].frameSentFail = 0;
  portStatsCurrent[port].flowCtrlSent = 0;
  portStatsCurrent[port].nonUnicastFrameSent = 0;
  portStatsCurrent[port].bytesReceived = 0;
  portStatsCurrent[port].frameReceived = 0;
  portStatsCurrent[port].totalBytesReceived = 0;
  portStatsCurrent[port].totalFramesReceived = 0;
  portStatsCurrent[port].flowCtrlFramesReceived = 0;
  portStatsCurrent[port].mcastFramesReceived = 0;
  portStatsCurrent[port].bcastFramesReceived = 0;
  portStatsCurrent[port].len64Frames = 0;
  portStatsCurrent[port].jabberFrames = 0;
  portStatsCurrent[port].len65To127Frames = 0;
  portStatsCurrent[port].oversizeFrames = 0;
  portStatsCurrent[port].len128To255Frames = 0;
  portStatsCurrent[port].len256To511Frames = 0;
  portStatsCurrent[port].len512To1023Frames = 0;
  portStatsCurrent[port].len1024To1522Frames = 0;
  portStatsCurrent[port].fragment = 0;
  portStatsCurrent[port].alignError = 0;
  portStatsCurrent[port].runtFrames = 0;
  portStatsCurrent[port].crcErrors = 0;
  portStatsCurrent[port].shortEvent = 0;
  portStatsCurrent[port].collision = 0;
  portStatsCurrent[port].droppedFrames = 0;
  portStatsCurrent[port].filteringCounter = 0;
  portStatsCurrent[port].delayExceedDiscardCounter = 0;
  portStatsCurrent[port].lateCollision = 0;

  portStatsCumulative[port].bytesSent = 0;
  portStatsCumulative[port].unicastFrameSent = 0;
  portStatsCumulative[port].frameSentFail = 0;
  portStatsCumulative[port].flowCtrlSent = 0;
  portStatsCumulative[port].nonUnicastFrameSent = 0;
  portStatsCumulative[port].bytesReceived = 0;
  portStatsCumulative[port].frameReceived = 0;
  portStatsCumulative[port].totalBytesReceived = 0;
  portStatsCumulative[port].totalFramesReceived = 0;
  portStatsCumulative[port].flowCtrlFramesReceived = 0;
  portStatsCumulative[port].mcastFramesReceived = 0;
  portStatsCumulative[port].bcastFramesReceived = 0;
  portStatsCumulative[port].len64Frames = 0;
  portStatsCumulative[port].jabberFrames = 0;
  portStatsCumulative[port].len65To127Frames = 0;
  portStatsCumulative[port].oversizeFrames = 0;
  portStatsCumulative[port].len128To255Frames = 0;
  portStatsCumulative[port].len256To511Frames = 0;
  portStatsCumulative[port].len512To1023Frames = 0;
  portStatsCumulative[port].len1024To1522Frames = 0;
  portStatsCumulative[port].fragment = 0;
  portStatsCumulative[port].alignError = 0;
  portStatsCumulative[port].runtFrames = 0;
  portStatsCumulative[port].crcErrors = 0;
  portStatsCumulative[port].shortEvent = 0;
  portStatsCumulative[port].collision = 0;
  portStatsCumulative[port].droppedFrames = 0;
  portStatsCumulative[port].filteringCounter = 0;
  portStatsCumulative[port].delayExceedDiscardCounter = 0;
  portStatsCumulative[port].lateCollision = 0;
}

static void clearLocalStatsAll(void)
{
  int i;

  for (i=0;i<10;i++) {
    clearLocalStatsPort(i);
    }
}

static int clearHandler(int argc, char *argv[], int flags)
{
  int retVal = -1;
  int bufLen;
  char * pOutBuf;
  int port;

  if (argc == 2) {
    port = (int)strtol(argv[1], (char **)NULL, 10);
    clearLocalStatsPort(port);
    retVal = ctrlClearPort(port);
    }
  else if (argc == 1) {
    clearLocalStatsAll();
    retVal = ctrlClearAll();
    }

  return retVal;
}


int zarlinkRmonInit(void)
{
  cliRegisterCommand("getstats", getStatsHandler, "getstats [port] - retrieve port statistics from switch");
  cliRegisterCommand("showstats", showStatsHandler, "showstats [port] - display port statistics");
  cliRegisterCommand("clear", clearHandler, "clear [port] - clear port statistics");
  
  clearLocalStatsAll();
  // ctrlClearAll();
  return 0;
}

// called by CTRL2 handler 
int zarlinkRmonUpdateStats(unsigned char * pRmonData)
{
  int port;
  int octet;
  union {
    unsigned char u8[4];
    unsigned long u32;
    } u;

  if (pRmonData == NULL)
    return -1;
  
  // CMD0[3:0] = 0b1000 for statistics counter memory
  // CMD0[7:4] = 0b0101 for completed read
  // CMD1[7:0] = 0x00 for statistics counter memory
  if ((pRmonData[0] != 0x58) || (pRmonData[1] != 0x00))
    return -1;

  // first, extract port and octet from address (bytes 3,4)
  u.u8[0] = pRmonData[2];
  u.u8[1] = pRmonData[3];
  u.u8[2] = pRmonData[4];
  u.u8[3] = 0x00;

  /*
   * calculate port & octet from base address in memory
   *
   * NOTE: internal memory is addressed in 8-byte chunks called octets, 
   * NOT bytes! Each port has eight octets of RMON data so, for example, 
   * port 1 base address is 0x8, port 7 base address is 0x38 (56).
   */

  octet = (int)(u.u32 & 0x00000007);
  port  = (int)((u.u32 >> 3) & 0xF);

  return decodeStats((unsigned short *)(&pRmonData[8]), port, octet);
}

void zarlinkRmonCounterOverflow(unsigned char intPort, unsigned long rmonOverflowBits)
{
  // There's no easy way to do this, so do it the right way...

  if (rmonOverflowBits & 0x00000001 != 0)
    portStatsCumulative[intPort].bytesSent += 1; // this is the high 32 bits

  if (rmonOverflowBits & 0x00000002 != 0)
    portStatsCumulative[intPort].unicastFrameSent += 0x01000000;
  
  if (rmonOverflowBits & 0x00000004 != 0)
    portStatsCumulative[intPort].frameSentFail += 0x100;
  
  if (rmonOverflowBits & 0x00000008 != 0)
    portStatsCumulative[intPort].flowCtrlSent += 0x10000;
  
  if (rmonOverflowBits & 0x00000010 != 0)
    portStatsCumulative[intPort].nonUnicastFrameSent += 0x10000;
  
  if (rmonOverflowBits & 0x00000020 != 0)
    portStatsCumulative[intPort].bytesReceived += 1; // this is the high 32 bits
  
  if (rmonOverflowBits & 0x00000040 != 0)
    portStatsCumulative[intPort].frameReceived += 1; // this is the high 32 bits
  
  if (rmonOverflowBits & 0x00000080 != 0)
    portStatsCumulative[intPort].totalBytesReceived += 1; // this is the high 32
  
  if (rmonOverflowBits & 0x00000100 != 0)
    portStatsCumulative[intPort].totalFramesReceived += 0x1000000;
  
  if (rmonOverflowBits & 0x00000200 != 0)
    portStatsCumulative[intPort].flowCtrlFramesReceived += 0x100;
  
  if (rmonOverflowBits & 0x00000400 != 0)
    portStatsCumulative[intPort].mcastFramesReceived += 0x10000;
  
  if (rmonOverflowBits & 0x00000800 != 0)
    portStatsCumulative[intPort].bcastFramesReceived += 0x10000;
  
  if (rmonOverflowBits & 0x00001000 != 0)
    portStatsCumulative[intPort].len64Frames += 0x1000000;
  
  if (rmonOverflowBits & 0x00002000 != 0)
    portStatsCumulative[intPort].jabberFrames += 0x100;
  
  if (rmonOverflowBits & 0x00004000 != 0)
    portStatsCumulative[intPort].len65To127Frames += 0x1000000;
  
  if (rmonOverflowBits & 0x00008000 != 0)
    portStatsCumulative[intPort].oversizeFrames += 0x100;
  
  if (rmonOverflowBits & 0x00010000 != 0)
    portStatsCumulative[intPort].len128To255Frames += 0x10000;
  
  if (rmonOverflowBits & 0x00020000 != 0)
    portStatsCumulative[intPort].len256To511Frames += 0x10000;
  
  if (rmonOverflowBits & 0x00040000 != 0)
    portStatsCumulative[intPort].len512To1023Frames += 0x10000;
  
  if (rmonOverflowBits & 0x00080000 != 0)
    portStatsCumulative[intPort].len1024To1522Frames += 0x10000;
  
  if (rmonOverflowBits & 0x00100000 != 0)
    portStatsCumulative[intPort].fragment += 0x10000;
  
  if (rmonOverflowBits & 0x00200000 != 0)
    portStatsCumulative[intPort].alignError += 0x100;
  
  if (rmonOverflowBits & 0x00400000 != 0)
    portStatsCumulative[intPort].runtFrames += 0x100;
  
  if (rmonOverflowBits & 0x00800000 != 0)
    portStatsCumulative[intPort].crcErrors += 0x10000;
  
  if (rmonOverflowBits & 0x01000000 != 0)
    portStatsCumulative[intPort].shortEvent += 0x10000;
  
  if (rmonOverflowBits & 0x02000000 != 0)
    portStatsCumulative[intPort].collision += 0x10000;
  
  if (rmonOverflowBits & 0x04000000 != 0)
    portStatsCumulative[intPort].droppedFrames += 0x10000;
  
  if (rmonOverflowBits & 0x08000000 != 0)
    portStatsCumulative[intPort].filteringCounter += 0x10000;
  
  if (rmonOverflowBits & 0x10000000 != 0)
    portStatsCumulative[intPort].delayExceedDiscardCounter += 0x100;
  
  if (rmonOverflowBits & 0x20000000 != 0)
    portStatsCumulative[intPort].lateCollision += 0x100;
    
  return;
}

#endif

