/*
@func ret_t | rtl8370_getAsicVlan4kEntry | Get VID mapped entry to 4K VLAN table. 
@parm rtl8370_user_vlan4kentry* | ptr_vlan4kEntry | VLAN entry seting for 4K table. There is VID field in entry structure and  entry is directly mapping to 4K table location (1 to 1).
@rvalue RT_ERR_OK | Success.
@rvalue RT_ERR_SMI | SMI access error.
@rvalue RT_ERR_INPUT | Invalid input parameter.
@rvalue RT_ERR_VLAN_VID | Invalid VID parameter (0~4095).
@comm
    The API can get entry of 4k VLAN table. Software must prepare the retrieving VID first at writing data and used control word to access desired VLAN entry.
    
*/
ret_t rtl8370_getAsicVlan4kEntry(rtl8370_user_vlan4kentry *ptr_vlan4kEntry )
{
#if defined(DISABLE_VLAN_SHADOW)
    rtl8370_vlan4kentrysmi vlan_4k_entry;
    uint32                    page_idx;
    uint16                    *tableAddr;
    ret_t                     retVal;
    uint32                     regData;

    if(NULL == ptr_vlan4kEntry)     
        return RT_ERR_INPUT;

    /* Write Address (VLAN_ID) */
    regData = ptr_vlan4kEntry->vid;
    retVal = rtl8370_setAsicReg(RTL8370_TABLE_ACCESS_ADDR_REG, regData);
    if(retVal !=  RT_ERR_OK)
        return retVal;

    /* Read Command */
    retVal = rtl8370_setAsicReg(RTL8370_TABLE_ACCESS_CTRL_REG, RTL8370_TABLE_ACCESS_REG_DATA(TB_OP_READ,TB_TARGET_CVLAN));
    if(retVal !=  RT_ERR_OK)
        return retVal;

    /* Check ASIC Command */

    /* Read VLAN data from register */
    tableAddr = (uint16 *)&vlan_4k_entry;
    for(page_idx = 0; page_idx < (sizeof(rtl8370_vlan4kentrysmi) / 2); page_idx++)
    {
        retVal = rtl8370_getAsicReg(RTL8370_TABLE_ACCESS_DATA_BASE + page_idx, &regData);
        if(retVal !=  RT_ERR_OK)
            return retVal;

        *tableAddr = regData;
        tableAddr++;
    }

    _rtl8370_Vlan4kStSmi2User(&vlan_4k_entry, ptr_vlan4kEntry);

#else

    uint16  vid;

    if(ptr_vlan4kEntry->vid > RTL8370_VIDMAX)
        return RT_ERR_VLAN_VID;

    vid = ptr_vlan4kEntry->vid;
    memcpy(ptr_vlan4kEntry, &user_4kvlan[ptr_vlan4kEntry->vid], sizeof(rtl8370_user_vlan4kentry));
    ptr_vlan4kEntry->vid = vid;

#endif

#if defined(CONFIG_RTL8370_ASICDRV_TEST)
    _rtl8370_Vlan4kStSmi2User(&Rtl8370sVirtualVlanTable[ptr_vlan4kEntry->vid], ptr_vlan4kEntry);
#endif

    return RT_ERR_OK;
}
ret_t rtl8370_getAsicAclAct( uint32 index, rtl8370_acl_act_t *aclAct)
{
    rtl8370_acl_act_smi_t aclActSmi;
    ret_t retVal;
    uint32 regAddr, regData;
    uint16* tableAddr;
    uint32 i;
    
    if(index > RTL8370_ACLRULEMAX)
        return RT_ERR_FILTER_ENTRYIDX;
    
    memset(&aclActSmi,0x00,sizeof(rtl8370_acl_act_smi_t));


    /* Write ACS_ADR register for data bits */
    regAddr = RTL8370_TABLE_ACCESS_ADDR_REG;
    regData = index;
    retVal = rtl8370_setAsicReg(regAddr, regData);
    if(retVal !=RT_ERR_OK)
        return retVal;
    

    /* Write ACS_CMD register */
    regAddr = RTL8370_TABLE_ACCESS_CTRL_REG;
    regData = RTL8370_TABLE_ACCESS_REG_DATA(TB_OP_READ, TB_TARGET_ACLACT);
    retVal = rtl8370_setAsicReg(regAddr, regData);
    if(retVal !=RT_ERR_OK)
        return retVal;
    

    /* Read Data Bits */
    regAddr = RTL8370_TABLE_ACCESS_DATA_BASE;
    tableAddr = (uint16*)&aclActSmi;
    for(i=0;i<RTL8370_ACLACTTBLEN;i++)
    {
        retVal = rtl8370_getAsicReg(regAddr, &regData);
        if(retVal !=RT_ERR_OK)
            return retVal;

        *tableAddr = regData;
        
        regAddr ++;
        tableAddr ++;
    }

     _rtl8370_aclActStSmi2User(aclAct, &aclActSmi);
     
#ifdef CONFIG_RTL8370_ASICDRV_TEST
    Rtl8370sVirtualAclActTable[index] = aclActSmi;
#endif

    return RT_ERR_OK;
}
ret_t rtl8370_getAsicAclRule( uint32 index, rtl8370_acl_rule_t *aclRule)
{
    rtl8370_acl_rule_smi_t aclRuleSmi;    
    uint32 regAddr, regData;
    ret_t retVal;
    uint16* tableAddr;
    uint32 i;

    if(index > RTL8370_ACLRULEMAX)
        return RT_ERR_FILTER_ENTRYIDX;

    memset(&aclRuleSmi,0x00,sizeof(rtl8370_acl_rule_smi_t));

    /* Write ACS_ADR register for data bits */
    regAddr = RTL8370_TABLE_ACCESS_ADDR_REG;
    regData = RTL8370_ACLRULETBADDR(DATABITS, index);
    retVal = rtl8370_setAsicReg(regAddr, regData);
    if(retVal !=RT_ERR_OK)
        return retVal;
    

    /* Write ACS_CMD register */
    regAddr = RTL8370_TABLE_ACCESS_CTRL_REG;
    regData = RTL8370_TABLE_ACCESS_REG_DATA(TB_OP_READ, TB_TARGET_ACLRULE);
    retVal = rtl8370_setAsicReg(regAddr, regData);
    if(retVal !=RT_ERR_OK)
        return retVal;
    

    /* Read Data Bits */
    regAddr = RTL8370_TABLE_ACCESS_DATA_BASE;
    tableAddr = (uint16*)&aclRuleSmi.data_bits;
    for(i=0;i<RTL8370_ACLRULETBLEN;i++)
    {
        retVal = rtl8370_getAsicReg(regAddr, &regData);
        if(retVal !=RT_ERR_OK)
            return retVal;

        *tableAddr = regData;
        
        regAddr ++;
        tableAddr ++;
    }
    
    /* Read Valid Bit */
    retVal = rtl8370_getAsicReg(RTL8370_TABLE_ACCESS_DATA_REG(RTL8370_ACLRULETBLEN), &regData);
    if(retVal !=RT_ERR_OK)
        return retVal;
    
    aclRuleSmi.valid = regData & 0x1;

    /* Write ACS_ADR register for carebits*/
    regAddr = RTL8370_TABLE_ACCESS_ADDR_REG;
    regData = RTL8370_ACLRULETBADDR(CAREBITS, index);
    retVal = rtl8370_setAsicReg(regAddr, regData);
    if(retVal !=RT_ERR_OK)
        return retVal;

    /* Write ACS_CMD register */
    regAddr = RTL8370_TABLE_ACCESS_CTRL_REG;
    regData = RTL8370_TABLE_ACCESS_REG_DATA(TB_OP_READ, TB_TARGET_ACLRULE);
    retVal = rtl8370_setAsicReg(regAddr, regData);
    if(retVal !=RT_ERR_OK)
        return retVal;

    /* Read Care Bits */
    regAddr = RTL8370_TABLE_ACCESS_DATA_BASE;
    tableAddr = (uint16*)&aclRuleSmi.care_bits;    
    for(i=0;i<RTL8370_ACLRULETBLEN;i++)
    {
        retVal = rtl8370_getAsicReg(regAddr, &regData);
        if(retVal !=RT_ERR_OK)
            return retVal;

        *tableAddr = regData;
        
        regAddr ++;
        tableAddr ++;
    }
    

#ifdef CONFIG_RTL8370_ASICDRV_TEST
    aclRuleSmi = Rtl8370sVirtualAclRuleTable[index];
#endif

     _rtl8370_aclRuleStSmi2User(aclRule, &aclRuleSmi);

    return RT_ERR_OK;
}
/*
@func ret_t | rtl8370_setAsicAclRule | Set acl rule content.
@parm uint32 | index | ACL rule index (0-63) of 64 ACL rules.
@parm rtl8370_acltrule | aclRule | ACL rule stucture for setting.
@rvalue RT_ERR_OK | Success.
@rvalue RT_ERR_SMI | SMI access error. 
@rvalue RT_ERR_INPUT | Invalid input parameter.
@rvalue RT_ERR_FILTER_ENTRYIDX | Invalid ACL rule index (0-63).
@comm
    The API can set ACL rule
    
    System supported 64 shared 289-bit ACL ingress rule. Index was available at range 0-63 only. If software want to
    modify ACL rule, the ACL function should be disable at first or unspecify acl action will be executed. 

    One ACL rule structure has three parts setting:
    Bit 0-143        Data Bits of this Rule
    Bit    144        Valid Bit
    Bit 145-288    Care Bits of this Rule

    There are four kinds of field in Data Bits and Care Bits: Active Portmask, Type, Tag Exist, and 7 fields

    Reading/writing to a ACL Rule is achieved by writing/reading registers at 0x0500 ~ 0x050A
*/
ret_t rtl8370_setAsicAclRule(uint32 index, rtl8370_acl_rule_t *aclRule)
{
    rtl8370_acl_rule_smi_t aclRuleSmi;
    uint16* tableAddr;
    uint32 regAddr;
    uint32    regData;
    uint32 i;
    ret_t retVal;
    
    if(index > RTL8370_ACLRULEMAX)
        return RT_ERR_FILTER_ENTRYIDX;
    
    memset(&aclRuleSmi,0x00,sizeof(rtl8370_acl_rule_smi_t));
     _rtl8370_aclRuleStUser2Smi(aclRule, &aclRuleSmi);

    /* Write ACS_ADR register for data bits */
    regAddr = RTL8370_TABLE_ACCESS_ADDR_REG;
    regData = RTL8370_ACLRULETBADDR(DATABITS, index);
    retVal = rtl8370_setAsicReg(regAddr,regData);
    if(retVal !=RT_ERR_OK)
        return retVal;

    /* Write Data Bits to ACS_DATA registers */
     tableAddr = (uint16*)&aclRuleSmi.data_bits;
     regAddr = RTL8370_TABLE_ACCESS_DATA_BASE;

    for(i=0;i<RTL8370_ACLRULETBLEN;i++)
    {
        regData = *tableAddr;
        retVal = rtl8370_setAsicReg(regAddr,regData);
        if(retVal !=RT_ERR_OK)
            return retVal;

        regAddr++;
        tableAddr++;
    }

    /* Write Valid Bit */
    retVal = rtl8370_setAsicRegBits(RTL8370_TABLE_ACCESS_DATA_REG(RTL8370_ACLRULETBLEN), 0x1, aclRuleSmi.valid);
    if(retVal !=RT_ERR_OK)
        return retVal;

    /* Write ACS_CMD register for care bits*/
    regAddr = RTL8370_TABLE_ACCESS_CTRL_REG;
    regData = RTL8370_TABLE_ACCESS_REG_DATA(TB_OP_WRITE, TB_TARGET_ACLRULE);
    retVal = rtl8370_setAsicReg(regAddr, regData);
    if(retVal !=RT_ERR_OK)
        return retVal;
    

    /* Write ACS_ADR register */
    regAddr = RTL8370_TABLE_ACCESS_ADDR_REG;
    regData = RTL8370_ACLRULETBADDR(CAREBITS, index);
    retVal = rtl8370_setAsicReg(regAddr, regData);
    if(retVal !=RT_ERR_OK)
        return retVal;
    
    
    retVal = rtl8370_setAsicReg(regAddr,regData);
    if(retVal !=RT_ERR_OK)
        return retVal;

    /* Write Care Bits to ACS_DATA registers */
     tableAddr = (uint16*)&aclRuleSmi.care_bits;
     regAddr = RTL8370_TABLE_ACCESS_DATA_BASE;

    for(i=0;i<RTL8370_ACLRULETBLEN;i++)
    {
        regData = *tableAddr;
        retVal = rtl8370_setAsicReg(regAddr,regData);
        if(retVal !=RT_ERR_OK)
            return retVal;

        regAddr++;
        tableAddr++;
    }
    
    /* Write ACS_CMD register */
    regAddr = RTL8370_TABLE_ACCESS_CTRL_REG;
    regData = RTL8370_TABLE_ACCESS_REG_DATA(TB_OP_WRITE, TB_TARGET_ACLRULE);
    retVal = rtl8370_setAsicReg(regAddr, regData);
    if(retVal !=RT_ERR_OK)
        return retVal;

#ifdef CONFIG_RTL8370_ASICDRV_TEST
    Rtl8370sVirtualAclRuleTable[index] = aclRuleSmi;
#endif

    return RT_ERR_OK;
}
/*
@func ret_t | rtl8370_setAsicVlan4kEntry | Set VID mapped entry to 4K VLAN table.
@parm rtl8370_user_vlan4kentry* | ptr_vlan4kEntry | VLAN entry seting for 4K table. There is VID field in entry structure and  entry is directly mapping to 4K table location (1 to 1).
@rvalue RT_ERR_OK | Success.
@rvalue RT_ERR_SMI | SMI access error.
@rvalue RT_ERR_INPUT | Invalid input parameter.
@rvalue RT_ERR_L2_FID | Invalid FID (0~4095).
@rvalue RT_ERR_PORT_MASK | Invalid port mask (0x00~0x3F).
@rvalue RT_ERR_VLAN_VID | Invalid VID parameter (0~4095).
@comm
    VID field of C-tag is 12-bits and available VID range is 0~4095. In 802.1q spec. , null VID (0x000) means tag header contain priority information
    only and VID 0xFFF is reserved for implementtation usage. But ASIC still retrieved these VID entries in 4K VLAN table if VID is decided from 16
    member configurations. It has no available VID 0x000 and 0xFFF from C-tag. ASIC will retrieve these two non-standard VIDs (0x000 and 0xFFF) from 
    member configuration indirectly referenced by Port based, Protocol-and-Port based VLAN and 802.1x functions.
    
*/
ret_t rtl8370_setAsicVlan4kEntry(rtl8370_user_vlan4kentry *ptr_vlan4kEntry )
{
    rtl8370_vlan4kentrysmi vlan_4k_entry;
    uint32                    page_idx;
    uint16                    *tableAddr;
    ret_t                     retVal;
    uint32                     regData;

    if(NULL == ptr_vlan4kEntry)     
        return RT_ERR_INPUT;

    if(ptr_vlan4kEntry->vid > RTL8370_VIDMAX) 
        return RT_ERR_VLAN_VID;

    if((ptr_vlan4kEntry->lurep != TRUE) && (ptr_vlan4kEntry->lurep != FALSE) )
        return RT_ERR_INPUT;

    if(ptr_vlan4kEntry->fid > RTL8370_FIDMAX)
        return RT_ERR_L2_FID;

    if(ptr_vlan4kEntry->msti > RTL8370_MSTIMAX)
        return RT_ERR_MSTI;

    if( (ptr_vlan4kEntry->envlanpol != TRUE) && (ptr_vlan4kEntry->envlanpol != FALSE) )
        return RT_ERR_INPUT;

    if(ptr_vlan4kEntry->meteridx > RTL8370_METERIDXMAX)
        return RT_ERR_FILTER_METER_ID;

    if( (ptr_vlan4kEntry->vbpen != TRUE) && (ptr_vlan4kEntry->vbpen != FALSE) )
        return RT_ERR_INPUT;

    if(ptr_vlan4kEntry->vbpri > RTL8370_PRIMAX)
        return RT_ERR_VLAN_PRIORITY;

    memset(&vlan_4k_entry, 0x00, sizeof(rtl8370_vlan4kentrysmi));
    _rtl8370_Vlan4kStUser2Smi(ptr_vlan4kEntry, &vlan_4k_entry);

    /* Prepare Data */
    tableAddr = (uint16 *)&vlan_4k_entry;
    for(page_idx = 0; page_idx < (sizeof(rtl8370_vlan4kentrysmi) / 2); page_idx++)
    {
        regData = *tableAddr;
        retVal = rtl8370_setAsicReg(RTL8370_TABLE_ACCESS_DATA_BASE + page_idx, regData);
        if(retVal !=  RT_ERR_OK)
            return retVal;

        tableAddr++;
    }

    /* Write Address (VLAN_ID) */
    regData = ptr_vlan4kEntry->vid;
    retVal = rtl8370_setAsicReg(RTL8370_TABLE_ACCESS_ADDR_REG, regData);
    if(retVal !=  RT_ERR_OK)
        return retVal;

    /* Write Command */
    retVal = rtl8370_setAsicReg(RTL8370_TABLE_ACCESS_CTRL_REG, RTL8370_TABLE_ACCESS_REG_DATA(TB_OP_WRITE,TB_TARGET_CVLAN));
    if(retVal !=  RT_ERR_OK)
        return retVal;

#if defined(CONFIG_RTL8370_ASICDRV_TEST)
    memcpy(&Rtl8370sVirtualVlanTable[ptr_vlan4kEntry->vid], &vlan_4k_entry, sizeof(rtl8370_vlan4kentrysmi));
#endif

#if !defined(DISABLE_VLAN_SHADOW)
    memcpy(&user_4kvlan[ptr_vlan4kEntry->vid], ptr_vlan4kEntry, sizeof(rtl8370_user_vlan4kentry));
#endif

    return RT_ERR_OK;
}