#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "tests_body.h"
/**********************************************************************/
/************************ TEST SCENARIOS ******************************/

struct smb_test wrst;
const char* TLIST[7] = {"2.1.1", "2.1.2", "2.2.1", "2.2.2", "2.2.5", "2.2.6", "2.2.7"};

void init_wr_test(int num)
{
  unsigned int vtag;

  wrst.ptx[0].fsize    = 64;
  wrst.ptx[0].fload    = 1;
  wrst.ptx[0].fnum     = T_2XX_FNUM; //1000;
  wrst.ptx[1].fsize    = 64;
  wrst.ptx[1].fload    = 1;
  wrst.ptx[1].fnum     = T_2XX_FNUM; //1000;
  wrst.ptx[2].fsize    = 64;
  wrst.ptx[2].fload    = 1;
  wrst.ptx[2].fnum     = T_2XX_FNUM; //1000;
  wrst.ptx[3].fsize    = 64;
  wrst.ptx[3].fload    = 1;
  wrst.ptx[3].fnum     = T_2XX_FNUM; //1000;

  switch(num) {
    case TEST_2_1_1:
      /* 2.1.1  Unicast - one-to-one stream */
      /* TX:      p0->p2; p2 -> p0; 802.1d
       * RX_TRIG: p0; p2 -> smac 
       * RX:      p1; p3 */
      wrst.ptx[0].d_port = 2;
      wrst.ptx[2].d_port = 0;
      wrst.ptx[0].q_vid  = VID_NONE;
      wrst.ptx[2].q_vid  = VID_NONE;
      wrst.ptx[1].d_port = DPORT_NONE;
      wrst.ptx[3].d_port = DPORT_NONE;

      wrst.prx[0].s_port = 2;
      wrst.prx[2].s_port = 0;
      wrst.prx[1].s_port = RX_PALL;
      wrst.prx[3].s_port = RX_PALL;
      wrst.prx[0].trig2_len = 0;
      wrst.prx[1].trig2_len = 0;
      wrst.prx[2].trig2_len = 0;
      wrst.prx[3].trig2_len = 0;
      break;

    case TEST_2_1_2:
      /* 2.1.2 Unicast - many-to-many stream */
      /* TX:      p0->p1; p1->p0; p2->p3; p3->p2; 802.1d
       * RX_TRIG: p0; p1; p2; p3 */
      wrst.ptx[0].d_port = 1;
      wrst.ptx[1].d_port = 0;
      wrst.ptx[2].d_port = 3;
      wrst.ptx[3].d_port = 2;
      wrst.ptx[0].q_vid  = VID_NONE;
      wrst.ptx[1].q_vid  = VID_NONE;
      wrst.ptx[2].q_vid  = VID_NONE;
      wrst.ptx[3].q_vid  = VID_NONE;

      wrst.prx[0].s_port = 1;
      wrst.prx[1].s_port = 0;
      wrst.prx[2].s_port = 3;
      wrst.prx[3].s_port = 2;
      break;

    case TEST_2_2_1:
      /* 2.2.1 VLAN-limited forwarding */
      /* TX:      p0->p2; p2->p0; 802.1q
       * RX_TRIG: p0; p2 -> smac; vid; prio
       * RX:      p1; p3 */
      wrst.ptx[0].d_port = 2;
      wrst.ptx[0].q_vid  = 1;
      wrst.ptx[0].q_pri  = 1;
      wrst.ptx[2].d_port = 0;
      wrst.ptx[2].q_vid  = 1;
      wrst.ptx[2].q_pri  = 1;
      wrst.ptx[1].d_port = DPORT_NONE;
      wrst.ptx[3].d_port = DPORT_NONE;

      wrst.prx[0].s_port = 2;
      wrst.prx[2].s_port = 0;
      wrst.prx[1].s_port = RX_PALL;
      wrst.prx[3].s_port = RX_PALL;
      wrst.prx[0].trig2_len = 4;
      wrst.prx[2].trig2_len = 4;
      wrst.prx[1].trig2_len = 0;
      wrst.prx[3].trig2_len = 0;
      vtag = VLAN_TAG(1, 1);
      memcpy( wrst.prx[0].trig2_pat, &vtag, 4 );
      memcpy( wrst.prx[2].trig2_pat, &vtag, 4 );
      break;

    case TEST_2_2_2:
      /* 2.2.2 VLAN separation of traffic */
      /* TX:      p0; p2; 802.1q; broadcast
       * RX_TRIG: p0; p2 -> smac; vid; prio
       * RX:      p1; p3 */
      wrst.ptx[0].d_port = TX_PALL;
      wrst.ptx[0].q_vid  = 1;
      wrst.ptx[0].q_pri  = 0;
      wrst.ptx[2].d_port = TX_PALL;
      wrst.ptx[2].q_vid  = 1;
      wrst.ptx[2].q_pri  = 0;
      wrst.ptx[1].d_port = DPORT_NONE;
      wrst.ptx[3].d_port = DPORT_NONE;

      wrst.prx[0].s_port = 2;
      wrst.prx[2].s_port = 0;
      wrst.prx[1].s_port = RX_PALL;
      wrst.prx[3].s_port = RX_PALL;
      wrst.prx[0].trig2_len = 4;
      wrst.prx[2].trig2_len = 4;
      wrst.prx[1].trig2_len = 0;
      wrst.prx[3].trig2_len = 0;
      vtag = VLAN_TAG(1, 0);
      memcpy( wrst.prx[0].trig2_pat, &vtag, 4 );
      memcpy( wrst.prx[2].trig2_pat, &vtag, 4 );
      break;

    case TEST_2_2_5:
      /* 2.2.5 Tagging */
      /* TX:      p0->p2; p2->p0; 802.1d
       * RX_TRIG: p0; p2 -> smac; vid; prio
       * RX:      p1; p3 */
      wrst.ptx[0].d_port = 2;
      wrst.ptx[2].d_port = 0;
      wrst.ptx[0].q_vid  = VID_NONE;
      wrst.ptx[2].q_vid  = VID_NONE;
      wrst.ptx[1].d_port = DPORT_NONE;
      wrst.ptx[3].d_port = DPORT_NONE;

      wrst.prx[0].s_port = 2;
      wrst.prx[2].s_port = 0;
      wrst.prx[1].s_port = RX_PALL;
      wrst.prx[3].s_port = RX_PALL;
      wrst.prx[0].trig2_len = 4;
      wrst.prx[2].trig2_len = 4;
      wrst.prx[1].trig2_len = 0;
      wrst.prx[3].trig2_len = 0;
      vtag = VLAN_TAG(1, 3);
      memcpy( wrst.prx[0].trig2_pat, &vtag, 4 );
      memcpy( wrst.prx[2].trig2_pat, &vtag, 4 );
      break;

    case TEST_2_2_6:
      /* 2.2.6 Untagging */
      /* TX:      p0->p2; p2->p0; 802.1q
       * RX_TRIG: p0; p2 -> smac; ethtype
       * RX:      p1; p3 */
      wrst.ptx[0].d_port = 2;
      wrst.ptx[2].d_port = 0;
      wrst.ptx[0].q_vid  = 1;
      wrst.ptx[0].q_pri  = 0;
      wrst.ptx[2].q_vid  = 1;
      wrst.ptx[2].q_pri  = 0;
      wrst.ptx[1].d_port = DPORT_NONE;
      wrst.ptx[3].d_port = DPORT_NONE;

      wrst.prx[0].s_port = 2;
      wrst.prx[2].s_port = 0;
      wrst.prx[1].s_port = RX_PALL;
      wrst.prx[3].s_port = RX_PALL;
      wrst.prx[0].trig2_len = 2;
      wrst.prx[2].trig2_len = 2;
      wrst.prx[1].trig2_len = 0;
      wrst.prx[3].trig2_len = 0;
      vtag = NOVLAN_TAG;
      memcpy( wrst.prx[0].trig2_pat, &vtag, 2 );
      memcpy( wrst.prx[2].trig2_pat, &vtag, 2 );
      break;

    case TEST_2_2_7:
      /* 2.2.7 Snake test */
      /* TX:  p0->p1; p1->p0; p2->p3; p3->p2
       * RX_TRIG: p0; p1; p2; p3 -> smac; ethtype */
      wrst.ptx[0].d_port = 1;
      wrst.ptx[1].d_port = 0;
      wrst.ptx[2].d_port = 3;
      wrst.ptx[3].d_port = 2;
      wrst.ptx[0].q_vid  = VID_NONE;
      wrst.ptx[1].q_vid  = VID_NONE;
      wrst.ptx[2].q_vid  = VID_NONE;
      wrst.ptx[3].q_vid  = VID_NONE;

      wrst.prx[0].s_port = 1;
      wrst.prx[1].s_port = 0;
      wrst.prx[2].s_port = 3;
      wrst.prx[3].s_port = 2;
      wrst.prx[0].trig2_len = 2;
      wrst.prx[1].trig2_len = 2;
      wrst.prx[2].trig2_len = 2;
      wrst.prx[3].trig2_len = 2;
      vtag = NOVLAN_TAG;
      memcpy( wrst.prx[0].trig2_pat, &vtag, 2 );
      memcpy( wrst.prx[1].trig2_pat, &vtag, 2 );
      memcpy( wrst.prx[2].trig2_pat, &vtag, 2 );
      memcpy( wrst.prx[3].trig2_pat, &vtag, 2 );
      break;
  }
}

int run_test( HTCountStructure *cntrs )
{
  int i, rx_pmask = 0, tx_pmask = 0;
  struct smb_port_tx *p_tx = NULL;
  struct smb_port_rx *p_rx = NULL;
  char mac[SMB_PORTS+1][6] = {{0x3c, 0x47, 0x0e, 0x04, 0x05, 0x06},
                              {0x3c, 0x47, 0x0e, 0x14, 0x15, 0x16},
                              {0x3c, 0x47, 0x0e, 0x24, 0x25, 0x26},
                              {0x3c, 0x47, 0x0e, 0x34, 0x35, 0x36},
                              {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
  
  reset_ports(SMB_P0 | SMB_P1 | SMB_P2 | SMB_P3);

  /* setup required streams */
  for (i=0; i<SMB_PORTS; ++i) {
    p_tx = &(wrst.ptx[i]);

    if (p_tx->d_port != DPORT_NONE) {
      tx_pmask |= PORT_TO_PMASK(i);
      setup_stream(i, mac[p_tx->d_port], mac[i], p_tx->fsize, p_tx->fnum,
          p_tx->fload/100.0, p_tx->q_pri, p_tx->q_vid);
    }
  }

  /* setup required triggers */
  for (i=0; i<SMB_PORTS; ++i) {
    p_rx = &(wrst.prx[i]);

    /* TRIG1 - SMAC */
    if (p_rx->s_port != RX_PALL)
      smb_set_trigger( mac[p_rx->s_port], i, 6, 6, HTTRIGGER_1 );
    /* TRIG2 - Ethtype */
    if (p_rx->trig2_len > 0)
      smb_set_trigger( p_rx->trig2_pat, i, 12, p_rx->trig2_len, HTTRIGGER_2);
  }

  clear_allcntrs(SMB_PALL);

  ////wait for link up
  rx_pmask = 0;
  for (i=0; i<SMB_PORTS; ++i) {
    p_rx = &(wrst.prx[i]);
    if ((p_rx->s_port >= 0 && p_rx->s_port <=3) || p_rx->s_port == RX_PALL)
      rx_pmask |= PORT_TO_PMASK(i);
  }
  wait_linkup(rx_pmask);

  //generate traffic
  run_traffic(tx_pmask, TRAFFIC_START);
  NSDelay(1);

  wait_test_done(tx_pmask, 0);

  get_allcntrs(SMB_PALL, cntrs);

  return 0;
}

int anl_result( HTCountStructure *cntrs, int testno)
{
  switch (testno) {
    case TEST_2_1_1:
      /* All frames sent from p0 received on p2
       * All frames sent from p2 received on p0
       * Limited number of frames may be received on p1; p3 due to learning */
      if ( (cntrs[0].TmtPkt == T_2XX_FNUM && cntrs[2].RcvPkt == T_2XX_FNUM && cntrs[2].RcvTrig == T_2XX_FNUM) &&
           (cntrs[2].TmtPkt == T_2XX_FNUM && cntrs[0].RcvPkt == T_2XX_FNUM && cntrs[0].RcvTrig == T_2XX_FNUM) &&
           (cntrs[1].RcvPkt < T_2XX_UNRECOG_THR) && (cntrs[3].RcvPkt < T_2XX_UNRECOG_THR))
        return TEST_PASSED;

      if ( (cntrs[0].TmtPkt == T_2XX_FNUM && cntrs[2].RcvPkt == T_2XX_FNUM && cntrs[2].RcvTrig == T_2XX_FNUM) &&
           (cntrs[2].TmtPkt == T_2XX_FNUM && cntrs[0].RcvPkt == T_2XX_FNUM && cntrs[0].RcvTrig == T_2XX_FNUM) )
        return TEST_WARNING;

      break;
      
    case TEST_2_1_2:
    case TEST_2_2_7:
      /* All frames sent from p0 received on p1
       * All frames sent from p1 received on p0
       * All frames sent from p2 received on p3
       * All frames sent from p3 received on p2 */
      if ( (cntrs[0].TmtPkt == T_2XX_FNUM && cntrs[1].RcvPkt == T_2XX_FNUM && cntrs[1].RcvTrig == T_2XX_FNUM) &&
           (cntrs[1].TmtPkt == T_2XX_FNUM && cntrs[0].RcvPkt == T_2XX_FNUM && cntrs[0].RcvTrig == T_2XX_FNUM) &&
           (cntrs[2].TmtPkt == T_2XX_FNUM && cntrs[3].RcvPkt == T_2XX_FNUM && cntrs[3].RcvTrig == T_2XX_FNUM) &&
           (cntrs[3].TmtPkt == T_2XX_FNUM && cntrs[2].RcvPkt == T_2XX_FNUM && cntrs[2].RcvTrig == T_2XX_FNUM) )
        return TEST_PASSED;

      if ( (cntrs[0].TmtPkt == T_2XX_FNUM && cntrs[1].RcvPkt >= T_2XX_FNUM && cntrs[1].RcvTrig == T_2XX_FNUM) &&
           (cntrs[1].TmtPkt == T_2XX_FNUM && cntrs[0].RcvPkt >= T_2XX_FNUM && cntrs[0].RcvTrig == T_2XX_FNUM) &&
           (cntrs[2].TmtPkt == T_2XX_FNUM && cntrs[3].RcvPkt >= T_2XX_FNUM && cntrs[3].RcvTrig == T_2XX_FNUM) &&
           (cntrs[3].TmtPkt == T_2XX_FNUM && cntrs[2].RcvPkt >= T_2XX_FNUM && cntrs[2].RcvTrig == T_2XX_FNUM) )
        return TEST_WARNING;

      break;

    case TEST_2_2_1:
    case TEST_2_2_5:
    case TEST_2_2_6:
      /* All frames sent from p0 received on p2
       * All frames sent from p2 received on p0
       * No frames received on ports other than p0; p2 */
      if ( (cntrs[0].TmtPkt == T_2XX_FNUM && cntrs[2].RcvPkt == T_2XX_FNUM && cntrs[2].RcvTrig == T_2XX_FNUM) &&
           (cntrs[2].TmtPkt == T_2XX_FNUM && cntrs[0].RcvPkt == T_2XX_FNUM && cntrs[0].RcvTrig == T_2XX_FNUM) &&
           (cntrs[1].RcvPkt == 0 && cntrs[3].RcvPkt == 0) )
        return TEST_PASSED;
      break;

    case TEST_2_2_2:
      /* All frames sent from p0 dropped
       * All frames sent from p2 received only on p0
       * No frames received on ports other than p0 */
      if ( (cntrs[0].TmtPkt == T_2XX_FNUM && cntrs[2].RcvPkt == 0 && cntrs[2].RcvTrig == 0) &&
           (cntrs[2].TmtPkt == T_2XX_FNUM && cntrs[0].RcvPkt == T_2XX_FNUM && cntrs[0].RcvTrig == T_2XX_FNUM) &&
           (cntrs[1].RcvPkt == 0 && cntrs[2].RcvPkt == 0 && cntrs[3].RcvPkt == 0) )
        return TEST_PASSED;
      break;
  }

  return TEST_FAILED;
}
