#include <linux/in.h>
#include "rtl865xc_testModel.h"
#include "rtl865xc_testModel_L34_test.h"
#include "rtl8198c_IPv6_test.h"
#include "../common/rtl_utils.h"
#include "packetGen/pktGen.h"
#include <net/rtl/rtl865x_multicast.h>



#include "../AsicDriver/rtl865x_asicCom.h"
#include "../AsicDriver/rtl865x_asicL4.h"
#include "../AsicDriver/rtl865x_asicL3.h"
#include "../AsicDriver/rtl865x_asicL2.h"
#include "../AsicDriver/rtl865x_asicBasic.h"
#include "../l3Driver/rtl865x_arp.h"



#define DBG_TESTMODEL_L34_TEST
#ifdef DBG_TESTMODEL_L34_TEST
#define DBG_TESTMODEL_L34_TEST_PRK printk
#else
#define DBG_TESTMODEL_L34_TEST_PRK(format, args...)
#endif

static uint32  strtoip(ipaddr_t *ip, int8 *str)
{
    int32 t1, t2, t3, t4;
    sscanf((char*)str, "%d.%d.%d.%d", &t1, &t2, &t3, &t4);
    *ip = (t1<<24)|(t2<<16)|(t3<<8)|t4;
    return *(uint32*)ip;
}

typedef uint32 in_addr_t;
static int inet_aton(const char *cp, struct in_addr *addrptr)
{
    in_addr_t addr;
    int value;
    int part;

    addr = 0;
    for (part = 1; part <= 4; part++)
    {

        if (!isdigit(*cp))
            return 0;

        value = 0;
        while (isdigit(*cp))
        {
            value *= 10;
            value += *cp++ - '0';
            if (value > 255)
                return 0;
        }

        if (part < 4)
        {
            if (*cp++ != '.')
                return 0;
        }
        else
        {
            char c = *cp++;
            if (c != '\0' && !isspace(c))
                return 0;
        }

        addr <<= 8;
        addr |= value;
    }

    /*  W. Richard Stevens in his book UNIX Network Programming,
     *  Volume 1, second edition, on page 71 says:
     *
     *  An undocumented feature of inet_aton is that if addrptr is
     *  a null pointer, the function still performs it validation
     *  of the input string, but does not store the result.
     */
    if (addrptr)
    {
        addrptr->s_addr = htonl(addr);
    }

    return 1;
}



static uint32 inet_addr(const int8 *cp)
{

    struct in_addr a;

    if (!inet_aton(cp, &a))
        return -1;
    else
        return a.s_addr;
}



int32 rtl8198c_dumpRegs(void)
{
    uint32 reg_swctrl0 = READ_MEM32(0xbb804418);
    rtlglue_printf("---- Dump Regs ----\n");
    rtlglue_printf("SWCTRL0[0x%08x]:0x%08x\n",0xbb804418,reg_swctrl0);
    rtlglue_printf("SWCTRL1[0x%08x]:0x%08x\n",0xbb80441c,READ_MEM32(0xbb80441c));

    return 	SUCCESS;
}



int32 rtl8198C_testFillTable(void)
{
	rtl8198C_tblAsic_v6NexthopTable_t   entry_in;
	rtl8198C_tblAsic_v6NexthopTable_t   entry_out;
	rtl8198C_tblAsic_v6NexthopTable_t   *entry_in_p;
	rtl8198C_tblAsic_v6NexthopTable_t   *entry_out_p;
	uint32 tableNum=0;
	uint32 entryNum=0;
	uint32 bitNumber=0;
	uint32 temp=0;
	uint32 i=0;
	uint32 failtimes=0;
	uint32 successiTmes=0;

	
	int8 *str[] = { "TYPE_L2_SWITCH_TABLE", "TYPE_ARP_TABLE", "TYPE_L3_ROUTING_TABLE", "TYPE_MULTICAST_TABLE", "TYPE_NETINTERFACE_TABLE",
		"TYPE_EXT_INT_IP_TABLE", "TYPE_VLAN_TABLE", "TYPE_VLAN1_TABLE","non_Table","TYPE_L4_TCP_UDP_TABLE",
		"non_Table","TYPE_PPPOE_TABLE","TYPE_ACL_RULE_TABLE","TYPE_NEXT_HOP_TABLE","TYPE_RATE_LIMIT_TABLE","non_Table",
		"TYPE_DS_LITE_TABLE","TYPE_6RD_TABLE","TYPE_IPv6_ROUTING_TABLE","TYPE_IPv6_NEXTHOP_TABLE","TYPE_IPV6_NEIGH_TABLE","TYPE_IPv6_MULTICAST_TABLE"};

	uint32 tableEntryNumSize[]={
		/*TYPE_L2_SWITCH_TABLE*/1024,
		/*TYPE_ARP_TABLE*/512,
		/*TYPE_L3_ROUTING_TABLE*/8,
		/*TYPE_MULTICAST_TABLE*/256,
		/*TYPE_NETINTERFACE_TABLE*/8,
		/*TYPE_EXT_INT_IP_TABLE*/16,
		/*TYPE_VLAN_TABLE*/2048,
		/*TYPE_VLAN1_TABLE*/2048,
		/*non_Table*/8,
		/*TYPE_L4_TCP_UDP_TABLE*/1024,
		/*non_Table*/8,
		/*TYPE_PPPOE_TABLE*/8,
		/*TYPE_ACL_RULE_TABLE*/256,
		/*TYPE_NEXT_HOP_TABLE*/32,
		/*TYPE_RATE_LIMIT_TABLE*/32,
		/*non_Table*/8,
		/*TYPE_DS_LITE_TABLE*/8,
		/*TYPE_6RD_TABLE*/8,
		/*TYPE_IPv6_ROUTING_TABLE*/8,
		/*TYPE_IPv6_NEXTHOP_TABLE*/32,
		/*TYPE_IPV6_ARP_TABLE*/256,
		/*TYPE_IPv6_MULTICAST_TABLE*/256};

#if defined(CONFIG_RTL_8685S_HWNAT)
	uint32 tableEntryBits[]={
		/*TYPE_L2_SWITCH_TABLE*/58,
		/*TYPE_ARP_TABLE*/11,
		/*TYPE_L3_ROUTING_TABLE*/65,
		/*TYPE_MULTICAST_TABLE*/82,
		/*TYPE_NETINTERFACE_TABLE*/128,
		/*TYPE_EXT_INT_IP_TABLE*/72,
		/*TYPE_VLAN_TABLE*/20,
		/*TYPE_VLAN1_TABLE*/20,
		/*non_Table*/0,
		/*TYPE_L4_TCP_UDP_TABLE*/94,
		/*non_Table*/0,
		/*TYPE_PPPOE_TABLE*/19,
		/*TYPE_ACL_RULE_TABLE*/336,
		/*TYPE_NEXT_HOP_TABLE*/22,
		/*TYPE_RATE_LIMIT_TABLE*/84,
		/*non_Table*/0,
		/*TYPE_DS_LITE_TABLE*/286,
		/*TYPE_6RD_TABLE*/161,
		/*TYPE_IPv6_ROUTING_TABLE*/164,
		/*TYPE_IPv6_NEXTHOP_TABLE*/18,
		/*TYPE_IPV6_ARP_TABLE*/84,
		/*TYPE_IPv6_MULTICAST_TABLE*/274};
#else

	uint32 tableEntryBits[]={
		/*TYPE_L2_SWITCH_TABLE*/58,
		/*TYPE_ARP_TABLE*/11,
		/*TYPE_L3_ROUTING_TABLE*/61,
		/*TYPE_MULTICAST_TABLE*/82,
		/*TYPE_NETINTERFACE_TABLE*/128,
		/*TYPE_EXT_INT_IP_TABLE*/72,
		/*TYPE_VLAN_TABLE*/20,
		/*TYPE_VLAN1_TABLE*/20,
		/*non_Table*/0,
		/*TYPE_L4_TCP_UDP_TABLE*/94,
		/*non_Table*/0,
		/*TYPE_PPPOE_TABLE*/19,
		/*TYPE_ACL_RULE_TABLE*/336,
		/*TYPE_NEXT_HOP_TABLE*/21,
		/*TYPE_RATE_LIMIT_TABLE*/84,
		/*non_Table*/0,
		/*TYPE_DS_LITE_TABLE*/286,
		/*TYPE_6RD_TABLE*/161,
		/*TYPE_IPv6_ROUTING_TABLE*/164,
		/*TYPE_IPv6_NEXTHOP_TABLE*/17,
		/*TYPE_IPV6_ARP_TABLE*/83,
		/*TYPE_IPv6_MULTICAST_TABLE*/274};
#endif
	entry_in_p =&entry_in;
	entry_out_p=&entry_out;
	memset(entry_in_p,0xff,sizeof(entry_in));
	memset(entry_out_p,0,sizeof(entry_out));

	rtlglue_printf("=======================Start testing table ======================\n");

	for(tableNum=0 ; tableNum<22 ; tableNum++)
	{

		//ingore non_table
		if(str[tableNum] == "non_Table" )
			continue;
		
		rtlglue_printf("**  Testing   Table %s  ..................\n",str[tableNum]);
		failtimes=0;
		successiTmes=0;
		for(entryNum=0 ; entryNum < tableEntryNumSize[tableNum];entryNum++)
		{

			_rtl8651_forceAddAsicEntry(tableNum, entryNum, entry_in_p);
			memset(entry_out_p,0,sizeof(entry_out));
			_rtl8651_readAsicEntry(tableNum, entryNum,entry_out_p);

//			memComp(entry_in_p,entry_out_p,sizeof(entry_in),"");
			bitNumber=0;
			i=0;
			temp = *( ( (uint32 *)(entry_out_p) ) + i );

			while(1)
			{

				if(temp&1)
				{
					bitNumber++;
					temp=temp >> 1; 
				}else if((bitNumber %32) ==0){ //a word end
					i++;
					temp = *( ( (uint32 *)(entry_out_p) ) + i );
//					rtlglue_printf("temp = %x \n",temp);
					if(i>=11) //entry max
						break;


				}else{	//find zero
					break;
				}
			}


			if(bitNumber >= tableEntryBits[tableNum])
			{
				successiTmes++;
				//all table entry OK!
				if(successiTmes == tableEntryNumSize[tableNum])
				{
					printk("Table %s total entry:%d test all OK! \nExpect_access:%d  Exact_access:%d \n\n",str[tableNum],
						tableEntryNumSize[tableNum],tableEntryBits[tableNum],bitNumber);

				}
//				rtlglue_printf("%s idx=%d check success\n",str[tableNum],entryNum);
			}else{
				printk("\033[31;43m %s idx=%d check fail Expect_access:%d  Exact_access:%d \033[m \n",str[tableNum],entryNum,tableEntryBits[tableNum],bitNumber);
				failtimes++;
				if(failtimes > 8)
				{
//					rtlglue_printf("!!!!!!!!!!!!!!FAIL TIME > 8 \n\n");
//					break;
				}else{

				}
			}

			
		}

	}

	return SUCCESS;

}

extern int32 _rtl865x_clearHWL2Table(void);
extern uint32 getL2TableRowFromMacStr(int8 *str);

int32 rtl8198C_testL2learnLimit(void)
{
	rtl865x_tblAsicDrv_l2Param_t l2t;

	extern int	BSP_MHZ;
	extern int  BSP_SYSCLK;
	_rtl865x_clearHWL2Table();
	/* lan HOST2_MAC */
	strtomac(&l2t.macAddr, "01-00-5e-00-00-40");
	l2t.ageSec=3;
	l2t.isStatic=1;
	l2t.nhFlag = 1;
	l2t.memberPortMask = (1<<1);
	rtl8651_setAsicL2Table(getL2TableRowFromMacStr("01-00-5e-00-00-40"), 0, &l2t);
	/* BSP_MHZ:200  BSP_SYSCLK:200000000 */
	printk("BSP_MHZ:%d  BSP_SYSCLK:%d \n",BSP_MHZ,BSP_SYSCLK);
	return SUCCESS;
	
}


extern int32 rtl8651_clearAsicPvid(void);
extern uint32 getL2TableRowFromMacStr(int8 *str);
extern int32 rtl865xc_netIfIndex(int vid)  ;