/*
 * Copyright (c) 2014, 2016, 2018, The Linux Foundation. All rights reserved.
 * Permission to use, copy, modify, and/or distribute this software for
 * any purpose with or without fee is hereby granted, provided that the
 * above copyright notice and this permission notice appear in all copies.
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */


/**
 * @defgroup dess_acl DESS_ACL
 * @{
 */
#include "sw.h"
#include "hsl.h"
#include "hsl_dev.h"
#include "hsl_acl.h"
#include "dess_acl.h"
#include "dess_reg.h"
#include "dess_acl_prv.h"

//#define DESS_ACL_DEBUG
//#define DESS_SW_ENTRY
#define DESS_HW_ENTRY

static dess_acl_list_t *sw_list_ent[SW_MAX_NR_DEV];
static dess_acl_rule_t *sw_rule_ent[SW_MAX_NR_DEV];

static dess_acl_rule_t *sw_rule_tmp[SW_MAX_NR_DEV];
static dess_acl_rule_t *hw_rule_tmp[SW_MAX_NR_DEV];
#ifdef DESS_SW_ENTRY
static a_uint8_t *sw_filter_mem = NULL;
#endif

static sw_error_t
_dess_filter_valid_set(a_uint32_t dev_id, a_uint32_t flt_idx, a_uint32_t flag);

#ifndef DESS_SW_ENTRY
#ifndef DESS_HW_ENTRY
static sw_error_t
_dess_filter_ports_bind(a_uint32_t dev_id, a_uint32_t flt_idx,
                        a_uint32_t ports);

static sw_error_t
_dess_filter_write(a_uint32_t dev_id, a_uint32_t reg[], a_uint32_t flt_idx,
                   a_uint32_t op);
#endif
#endif

//static sw_error_t
//_dess_filter_read(a_uint32_t dev_id, a_uint32_t reg[], a_uint32_t flt_idx,
//                  a_uint32_t op);

static sw_error_t
_dess_filter_down_to_hw(a_uint32_t dev_id, hw_filter_t * filter,
                        a_uint32_t flt_idx);

static sw_error_t
_dess_filter_up_to_sw(a_uint32_t dev_id, hw_filter_t * filter,
                      a_uint32_t flt_idx);

static sw_error_t
_dess_acl_rule_src_filter_sts_set(a_uint32_t dev_id,
                                  a_uint32_t rule_id, a_bool_t enable);

static void
_dess_acl_list_dump(a_uint32_t dev_id)
{
    a_uint32_t i;
    dess_acl_list_t *sw_list;

    aos_printk("\ndev_id=%d  list control infomation:", dev_id);
    for (i = 0; i < DESS_MAX_FILTER; i++)
    {
        sw_list = &(sw_list_ent[dev_id][i]);
        if (ENT_USED & sw_list->status)
        {
            aos_printk
            ("\nlist_id=%02d  list_pri=%02d  rule_nr=%02d  [pts_map]:0x%02x  idx=%02d ",
             sw_list->list_id, sw_list->list_pri, sw_list->rule_nr,
             sw_list->bind_pts, i);
        }
    }
    aos_printk("\n");
}

static void
_dess_acl_sw_rule_dump(char *info, dess_acl_rule_t * sw_rule)
{
#ifdef DESS_ACL_DEBUG
    a_uint32_t flt_idx, i;

    aos_printk("\n%s", info);
    for (flt_idx = 0; flt_idx < DESS_MAX_FILTER; flt_idx++)
    {
        aos_printk("\n%d software filter:", flt_idx);
        aos_printk("\nact:");
        for (i = 0; i < 3; i++)
        {
            aos_printk("%08x  ", sw_rule[flt_idx].filter.act[i]);
        }

        aos_printk("\nvlu:");
        for (i = 0; i < 5; i++)
        {
            aos_printk("%08x  ", sw_rule[flt_idx].filter.vlu[i]);
        }

        aos_printk("\nmsk:");
        for (i = 0; i < 5; i++)
        {
            aos_printk("%08x  ", sw_rule[flt_idx].filter.msk[i]);
        }

        aos_printk("\nctl:status[%02d] list_id[%02d] rule_id[%02d]",
                   sw_rule[flt_idx].status,
                   sw_rule[flt_idx].list_id, sw_rule[flt_idx].rule_id);

        aos_printk("\n\n");
    }
#else
    return;
#endif
}

static dess_acl_list_t *
_dess_acl_list_loc(a_uint32_t dev_id, a_uint32_t list_id)
{
    a_uint32_t i;

    for (i = 0; i < DESS_MAX_FILTER; i++)
    {
        if ((ENT_USED & sw_list_ent[dev_id][i].status)
                && (list_id == sw_list_ent[dev_id][i].list_id))
        {
            return &(sw_list_ent[dev_id][i]);
        }
    }
    return NULL;
}

static sw_error_t
_dess_filter_valid_set(a_uint32_t dev_id, a_uint32_t flt_idx, a_uint32_t flag)
{
#ifdef DESS_SW_ENTRY
    hw_filter_t filter;

    _dess_filter_up_to_sw(dev_id, &filter, flt_idx);

    filter.msk[4] &= 0xfffffff8;
    filter.msk[4] |= (flag & 0x7);

    _dess_filter_down_to_hw(dev_id, &filter, flt_idx);

    return SW_OK;
#else
#ifdef DESS_HW_ENTRY
    hw_filter_t filter;

    filter = sw_rule_ent[dev_id][flt_idx].filter;

    filter.msk[4] &= 0xfffffff8;
    filter.msk[4] |= (flag & 0x7);

    _dess_filter_down_to_hw(dev_id, &filter, flt_idx);
    return SW_OK;
#else
    sw_error_t rv;
    a_uint32_t addr, data = 0;

    /* read filter mask at first */
    addr = DESS_RULE_FUNC_ADDR;
    data = (flt_idx & 0x7f) | (0x1 << 8) | (0x1 << 10) | (0x1 << 31);
    HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                          (a_uint8_t *) (&data), sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    /* get filter mask and modify it */
    addr = DESS_RULE_FUNC_ADDR + 20;
    HSL_REG_ENTRY_GEN_GET(rv, dev_id, addr, sizeof (a_uint32_t),
                          (a_uint8_t *) (&data), sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    data &= 0xfffffff8;
    data |= (flag & 0x7);
    HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                          (a_uint8_t *) (&data), sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    /* write back filter mask */
    addr = DESS_RULE_FUNC_ADDR;
    data = (flt_idx & 0x7f) | (0x1 << 8) | (0x1 << 31);
    HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                          (a_uint8_t *) (&data), sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    return SW_OK;
#endif
#endif
}

static sw_error_t
_dess_filter_ports_bind(a_uint32_t dev_id, a_uint32_t flt_idx, a_uint32_t ports)
{
#ifdef DESS_SW_ENTRY
    hw_filter_t filter;

    _dess_filter_up_to_sw(dev_id, &filter, flt_idx);

    filter.vlu[4] &= 0xffffff80;
    filter.vlu[4] |= (ports & 0x7f);

    _dess_filter_down_to_hw(dev_id, &filter, flt_idx);

    return SW_OK;
#else
#ifdef DESS_HW_ENTRY
    hw_filter_t filter;

    filter = sw_rule_ent[dev_id][flt_idx].filter;

    filter.vlu[4] &= 0xffffff80;
    filter.vlu[4] |= (ports & 0x7f);

    _dess_filter_down_to_hw(dev_id, &filter, flt_idx);

    return SW_OK;
#else
    sw_error_t rv;
    a_uint32_t addr, data;

    /* read filter value at first */
    addr = DESS_RULE_FUNC_ADDR;
    data = (flt_idx & 0x7f) | (0x1 << 10) | (0x1 << 31);
    HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                          (a_uint8_t *) (&data), sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    /* get filter value and modify it */
    addr = DESS_RULE_FUNC_ADDR + 20;
    HSL_REG_ENTRY_GEN_GET(rv, dev_id, addr, sizeof (a_uint32_t),
                          (a_uint8_t *) (&data), sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    data &= 0xffffff80;
    data |= (ports & 0x7f);
    HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                          (a_uint8_t *) (&data), sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    /* write back filter value */
    addr = DESS_RULE_FUNC_ADDR;
    data = (flt_idx & 0x7f) | (0x1 << 31);
    HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                          (a_uint8_t *) (&data), sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    return SW_OK;
#endif
#endif
}

#ifndef DESS_SW_ENTRY
#ifndef DESS_HW_ENTRY
static sw_error_t
_dess_filter_write(a_uint32_t dev_id, a_uint32_t reg[], a_uint32_t flt_idx,
                   a_uint32_t op)
{
    a_uint32_t i, addr, data, idx = 6;
    sw_error_t rv;

    if (DESS_FILTER_ACT_OP == op)
    {
        idx = 4;
    }

    for (i = 1; i < idx; i++)
    {
        addr = DESS_RULE_FUNC_ADDR + (i << 2);
        HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                              (a_uint8_t *) (&(reg[i - 1])),
                              sizeof (a_uint32_t));
        SW_RTN_ON_ERROR(rv);
    }

    addr = DESS_RULE_FUNC_ADDR;
    data = (flt_idx & 0x7f) | (op << 8) | (0x1 << 31);
    HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                          (a_uint8_t *) (&data), sizeof (a_uint32_t));

    return rv;
}

static sw_error_t
_dess_filter_read(a_uint32_t dev_id, a_uint32_t reg[], a_uint32_t flt_idx,
                  a_uint32_t op)
{
    a_uint32_t i, addr, data, idx = 6;
    sw_error_t rv;

    addr = DESS_RULE_FUNC_ADDR;
    data = (flt_idx & 0x7f) | (op << 8) | (0x1 << 10) | (0x1 << 31);
    HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                          (a_uint8_t *) (&data), sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    if (DESS_FILTER_ACT_OP == op)
    {
        idx = 4;
    }

    for (i = 1; i < idx; i++)
    {
        addr = DESS_RULE_FUNC_ADDR + (i << 2);
        HSL_REG_ENTRY_GEN_GET(rv, dev_id, addr, sizeof (a_uint32_t),
                              (a_uint8_t *) (&(reg[i - 1])),
                              sizeof (a_uint32_t));
        SW_RTN_ON_ERROR(rv);
    }

    return SW_OK;
}
#endif
#endif

static sw_error_t
_dess_filter_down_to_hw(a_uint32_t dev_id, hw_filter_t * filter,
                        a_uint32_t flt_idx)
{
#ifdef DESS_SW_ENTRY
    a_uint8_t *tbl = sw_filter_mem + sizeof (hw_filter_t) * flt_idx;

    aos_mem_copy(tbl, filter, sizeof (hw_filter_t));
#else
#ifdef DESS_HW_ENTRY
    sw_error_t rv;
    a_uint32_t i, base, addr;

    base = DESS_FILTER_ACT_ADDR + (flt_idx << 4);
    for (i = 0; i < 3; i++)
    {
        addr = base + (i << 2);
        HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                              (a_uint8_t *) (&(filter->act[i])),
                              sizeof (a_uint32_t));
        SW_RTN_ON_ERROR(rv);
    }

    base = DESS_FILTER_VLU_ADDR + (flt_idx << 5);
    for (i = 0; i < 5; i++)
    {
        addr = base + (i << 2);
        HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                              (a_uint8_t *) (&(filter->vlu[i])),
                              sizeof (a_uint32_t));
        SW_RTN_ON_ERROR(rv);
    }

    base = DESS_FILTER_MSK_ADDR + (flt_idx << 5);
    for (i = 0; i < 5; i++)
    {
        addr = base + (i << 2);
        HSL_REG_ENTRY_GEN_SET(rv, dev_id, addr, sizeof (a_uint32_t),
                              (a_uint8_t *) (&(filter->msk[i])),
                              sizeof (a_uint32_t));
        SW_RTN_ON_ERROR(rv);
    }
#else
    sw_error_t rv;

    rv = _dess_filter_write(dev_id, &(filter->act[0]), flt_idx,
                            DESS_FILTER_ACT_OP);
    SW_RTN_ON_ERROR(rv);

    rv = _dess_filter_write(dev_id, &(filter->vlu[0]), flt_idx,
                            DESS_FILTER_VLU_OP);
    SW_RTN_ON_ERROR(rv);

    rv = _dess_filter_write(dev_id, &(filter->msk[0]), flt_idx,
                            DESS_FILTER_MSK_OP);
    SW_RTN_ON_ERROR(rv);
#endif
#endif

    return SW_OK;
}

static sw_error_t
_dess_filter_up_to_sw(a_uint32_t dev_id, hw_filter_t * filter,
                      a_uint32_t flt_idx)
{
#ifdef DESS_SW_ENTRY
    a_uint8_t *tbl = sw_filter_mem + sizeof (hw_filter_t) * flt_idx;

    aos_mem_copy(filter, tbl, sizeof (hw_filter_t));
#else
#ifdef DESS_HW_ENTRY
    sw_error_t rv;
    a_uint32_t i, base, addr;

    base = DESS_FILTER_VLU_ADDR + (flt_idx << 5);
    for (i = 0; i < 5; i++)
    {
        addr = base + (i << 2);
        HSL_REG_ENTRY_GEN_GET(rv, dev_id, addr, sizeof (a_uint32_t),
                              (a_uint8_t *) (&(filter->vlu[i])),
                              sizeof (a_uint32_t));
        SW_RTN_ON_ERROR(rv);
    }

    base = DESS_FILTER_MSK_ADDR + (flt_idx << 5);
    for (i = 0; i < 5; i++)
    {
        addr = base + (i << 2);
        HSL_REG_ENTRY_GEN_GET(rv, dev_id, addr, sizeof (a_uint32_t),
                              (a_uint8_t *) (&(filter->msk[i])),
                              sizeof (a_uint32_t));
        SW_RTN_ON_ERROR(rv);
    }

    base = DESS_FILTER_ACT_ADDR + (flt_idx << 4);
    for (i = 0; i < 3; i++)
    {
        addr = base + (i << 2);
        HSL_REG_ENTRY_GEN_GET(rv, dev_id, addr, sizeof (a_uint32_t),
                              (a_uint8_t *) (&(filter->act[i])),
                              sizeof (a_uint32_t));
        SW_RTN_ON_ERROR(rv);
    }
#else
    sw_error_t rv;

    rv = _dess_filter_read(dev_id, &(filter->vlu[0]), flt_idx,
                           DESS_FILTER_VLU_OP);
    SW_RTN_ON_ERROR(rv);

    rv = _dess_filter_read(dev_id, &(filter->msk[0]), flt_idx,
                           DESS_FILTER_MSK_OP);
    SW_RTN_ON_ERROR(rv);

    rv = _dess_filter_read(dev_id, &(filter->act[0]), flt_idx,
                           DESS_FILTER_ACT_OP);
    SW_RTN_ON_ERROR(rv);
#endif
#endif

    return SW_OK;
}

static sw_error_t
_dess_acl_list_insert(a_uint32_t dev_id, a_uint32_t * src_idx,
                      a_uint32_t * dst_idx, dess_acl_rule_t * src_rule,
                      dess_acl_rule_t * dst_rule)
{
    a_uint32_t i, data, rule_id, list_id, list_pri;

    rule_id = 0;
    list_id = src_rule[*src_idx].list_id;
    list_pri = src_rule[*src_idx].list_pri;

    for (i = *src_idx; i < DESS_MAX_FILTER; i++)
    {
        if (!(ENT_USED & src_rule[i].status))
        {
            continue; // was: break;
        }

        if (src_rule[i].list_id != list_id)
        {
            break;
        }

        SW_GET_FIELD_BY_REG(MAC_RUL_M4, RULE_TYP, data,
                            src_rule[i].filter.msk[4]);
        if (!data)
        {
            continue;
        }

        if (DESS_MAX_FILTER <= *dst_idx)
        {
            return SW_NO_RESOURCE;
        }

        if (ENT_USED & dst_rule[*dst_idx].status)
        {
            return SW_NO_RESOURCE;
        }

        SW_GET_FIELD_BY_REG(MAC_RUL_M4, RULE_VALID, data,
                            src_rule[i].filter.msk[4]);
        if ((FLT_START == data) && (*dst_idx % 2))
        {
            if (*src_idx != i)
            {
                dst_rule[*dst_idx].src_flt_dis = src_rule[i].src_flt_dis;
                dst_rule[*dst_idx].list_id = list_id;
                dst_rule[*dst_idx].list_pri = list_pri;
                dst_rule[*dst_idx].rule_id = rule_id - 1;
                dst_rule[*dst_idx].status |= ENT_USED;
            }

            (*dst_idx)++;
            if (DESS_MAX_FILTER <= *dst_idx)
            {
                return SW_NO_RESOURCE;
            }

            if (ENT_USED & dst_rule[*dst_idx].status)
            {
                return SW_NO_RESOURCE;
            }
        }

        aos_mem_copy(&(dst_rule[*dst_idx].filter), &(src_rule[i].filter),
                     sizeof (hw_filter_t));
        dst_rule[*dst_idx].src_flt_dis = src_rule[i].src_flt_dis;
        dst_rule[*dst_idx].list_id = list_id;
        dst_rule[*dst_idx].list_pri = list_pri;
        dst_rule[*dst_idx].rule_id = rule_id;
        dst_rule[*dst_idx].status |= ENT_USED;
        if (ENT_DEACTIVE & src_rule[i].status)
        {
            dst_rule[*dst_idx].status |= ENT_DEACTIVE;
        }
        (*dst_idx)++;

        if ((FLT_END == data) && (*dst_idx % 2))
        {
            if (DESS_MAX_FILTER > *dst_idx)
            {
                dst_rule[*dst_idx].src_flt_dis = src_rule[i].src_flt_dis;
                dst_rule[*dst_idx].list_id = list_id;
                dst_rule[*dst_idx].list_pri = list_pri;
                dst_rule[*dst_idx].rule_id = rule_id;
                dst_rule[*dst_idx].status |= ENT_USED;
                (*dst_idx)++;
            }
        }

        if ((FLT_END == data) || (FLT_STARTEND == data))
        {
            rule_id++;
        }
    }

    *src_idx = i;
    return SW_OK;
}

static sw_error_t
_dess_acl_rule_alloc(a_uint32_t dev_id, dess_acl_list_t * sw_list,
                     a_uint32_t filter_nr)
{
    a_uint32_t free_flt_nr, load_idx, begin_idx, start_idx, end_idx, i;
    a_uint32_t largest_nr, largest_idx;
    sw_error_t rv;

    /* calculate the proper location, [start_idx, end_idx) */
    start_idx = 0;
    end_idx = DESS_MAX_FILTER;
    for (i = 0; i < DESS_MAX_FILTER; i++)
    {
        if (ENT_USED & sw_rule_ent[dev_id][i].status)
        {
            if (sw_rule_ent[dev_id][i].list_pri < sw_list->list_pri)
            {
                start_idx = i + 1;
            }
            else if (sw_rule_ent[dev_id][i].list_pri > sw_list->list_pri)
            {
                end_idx = i;
                break;
            }
        }
    }

    /* find the larget free filters block */
    largest_nr = 0;
    largest_idx = 0;
    free_flt_nr = 0;
    begin_idx = start_idx;
    for (i = start_idx; i < end_idx; i++)
    {
        if (!(ENT_USED & sw_rule_ent[dev_id][i].status))
        {
            free_flt_nr++;
        }
        else
        {
            if (free_flt_nr > largest_nr)
            {
                largest_nr = free_flt_nr;
                largest_idx = begin_idx;
            }
            free_flt_nr = 0;
            begin_idx = i + 1;
        }
    }

    if (free_flt_nr > largest_nr)
    {
        largest_nr = free_flt_nr;
        largest_idx = begin_idx;
    }

    if ((!largest_nr) || ((largest_nr + 1) < filter_nr))
    {
        return SW_NO_RESOURCE;
    }

    for (i = 0; i < DESS_MAX_FILTER; i++)
    {
        if (ENT_USED & sw_rule_ent[dev_id][i].status)
        {
            aos_mem_copy(&(sw_rule_tmp[dev_id][i]), &(sw_rule_ent[dev_id][i]),
                         sizeof (dess_acl_rule_t));
        }
    }

    begin_idx = 0;
    load_idx = largest_idx;
    rv = _dess_acl_list_insert(dev_id, &begin_idx, &load_idx,
                               hw_rule_tmp[dev_id], sw_rule_tmp[dev_id]);
    return rv;
}

static sw_error_t
_dess_acl_rule_reorder(a_uint32_t dev_id, dess_acl_list_t * sw_list)
{
    a_uint32_t i, src_idx, dst_idx;
    sw_error_t rv;

    dst_idx = 0;
    for (i = 0; i < DESS_MAX_FILTER;)
    {
        if (ENT_USED & sw_rule_ent[dev_id][i].status)
        {
            if (sw_rule_ent[dev_id][i].list_pri <= sw_list->list_pri)
            {
                rv = _dess_acl_list_insert(dev_id, &i, &dst_idx,
                                           sw_rule_ent[dev_id],
                                           sw_rule_tmp[dev_id]);
                SW_RTN_ON_ERROR(rv);
            }
            else
            {
                break;
            }
        }
        else
        {
            i++;
        }
    }

    src_idx = 0;
    rv = _dess_acl_list_insert(dev_id, &src_idx, &dst_idx, hw_rule_tmp[dev_id],
                               sw_rule_tmp[dev_id]);
    SW_RTN_ON_ERROR(rv);

    for (; i < DESS_MAX_FILTER;)
    {
        if (ENT_USED & sw_rule_ent[dev_id][i].status)
        {
            rv = _dess_acl_list_insert(dev_id, &i, &dst_idx,
                                       sw_rule_ent[dev_id],
                                       sw_rule_tmp[dev_id]);
            SW_RTN_ON_ERROR(rv);
        }
        else
        {
            i++;
        }
    }

    return SW_OK;
}

static void
_dess_acl_rule_sync(a_uint32_t dev_id, a_uint32_t flt_idx, a_uint32_t flt_nr)
{
    a_uint32_t i, data;

    for (i = flt_idx; i < (flt_idx + flt_nr); i++)
    {
        if (aos_mem_cmp
                (&(sw_rule_ent[dev_id][i]), &(sw_rule_tmp[dev_id][i]),
                 sizeof (dess_acl_rule_t)))
        {
            SW_GET_FIELD_BY_REG(MAC_RUL_M4, RULE_TYP, data,
                                sw_rule_tmp[dev_id][i].filter.msk[4]);
            if (data)
            {
                _dess_filter_down_to_hw(dev_id,
                                        &(sw_rule_tmp[dev_id][i].filter), i);
            }
            else
            {
                _dess_filter_valid_set(dev_id, i, 0);
            }

            aos_mem_copy(&(sw_rule_ent[dev_id][i]), &(sw_rule_tmp[dev_id][i]),
                         sizeof (dess_acl_rule_t));
            _dess_acl_rule_src_filter_sts_set(dev_id, i,
                                              !sw_rule_tmp[dev_id][i].src_flt_dis);
        }
    }
}

static sw_error_t
_dess_acl_list_creat(a_uint32_t dev_id, a_uint32_t list_id, a_uint32_t list_pri)
{
    a_uint32_t i, loc = DESS_MAX_FILTER;
    dess_acl_list_t *sw_list;

    HSL_DEV_ID_CHECK(dev_id);

    if ((DESS_MAX_LIST_ID < list_id) || (DESS_MAX_LIST_PRI < list_pri))
    {
        return SW_NOT_SUPPORTED;
    }

    for (i = 0; i < DESS_MAX_FILTER; i++)
    {
        sw_list = &(sw_list_ent[dev_id][i]);
        if (ENT_USED & sw_list->status)
        {
            if (list_id == sw_list->list_id)
            {
                return SW_ALREADY_EXIST;
            }
        }
        else
        {
            loc = i;
        }
    }

    if (DESS_MAX_FILTER == loc)
    {
        return SW_NO_RESOURCE;
    }

    sw_list = &(sw_list_ent[dev_id][loc]);
    aos_mem_zero(sw_list, sizeof (dess_acl_list_t));
    sw_list->list_id = list_id;
    sw_list->list_pri = list_pri;
    sw_list->status |= ENT_USED;
    return SW_OK;
}

static sw_error_t
_dess_acl_list_destroy(a_uint32_t dev_id, a_uint32_t list_id)
{
    dess_acl_list_t *sw_list;

    HSL_DEV_ID_CHECK(dev_id);

    if (DESS_MAX_LIST_ID < list_id)
    {
        return SW_NOT_SUPPORTED;
    }

    sw_list = _dess_acl_list_loc(dev_id, list_id);
    if (NULL == sw_list)
    {
        return SW_NOT_FOUND;
    }

    if (0 != sw_list->bind_pts)
    {
        return SW_NOT_SUPPORTED;
    }

    if (0 != sw_list->rule_nr)
    {
        return SW_NOT_SUPPORTED;
    }

    aos_mem_zero(sw_list, sizeof (dess_acl_list_t));
    return SW_OK;
}

static sw_error_t
_dess_acl_rule_add(a_uint32_t dev_id, a_uint32_t list_id,
                   a_uint32_t rule_id, a_uint32_t rule_nr,
                   fal_acl_rule_t * rule)
{
    sw_error_t rv;
    dess_acl_list_t *sw_list;
    dess_acl_rule_t *sw_rule;
    a_uint32_t i, free_flt_nr, old_flt_nr, old_flt_idx, new_flt_nr, bind_pts;

    HSL_DEV_ID_CHECK(dev_id);

    if (DESS_MAX_LIST_ID < list_id)
    {
        return SW_NOT_SUPPORTED;
    }

    if ((0 == rule_nr) || (NULL == rule))
    {
        return SW_BAD_PARAM;
    }

    sw_list = _dess_acl_list_loc(dev_id, list_id);
    if (NULL == sw_list)
    {
        return SW_NOT_FOUND;
    }

    if (rule_id != sw_list->rule_nr)
    {
        return SW_BAD_PARAM;
    }

    old_flt_idx = 0;
    old_flt_nr = 0;
    free_flt_nr = 0;
    aos_mem_zero(hw_rule_tmp[dev_id],
                 DESS_HW_RULE_TMP_CNT * sizeof (dess_acl_rule_t));
    aos_mem_zero(sw_rule_tmp[dev_id],
                 DESS_MAX_FILTER * sizeof (dess_acl_rule_t));
    for (i = 0; i < DESS_MAX_FILTER; i++)
    {
        sw_rule = &(sw_rule_ent[dev_id][i]);
        if (ENT_USED & sw_rule->status)
        {
            if (sw_rule->list_id == sw_list->list_id)
            {
                aos_mem_copy(&(hw_rule_tmp[dev_id][old_flt_nr]), sw_rule,
                             sizeof (dess_acl_rule_t));
                if (!old_flt_nr)
                {
                    old_flt_idx = i;
                }
                old_flt_nr++;
            }
        }
        else
        {
            free_flt_nr++;
        }
    }

    if (!free_flt_nr)
    {
        return SW_NO_RESOURCE;
    }

    /* parse rule entry and alloc rule resource */
    new_flt_nr = old_flt_nr;
    for (i = 0; i < rule_nr; i++)
    {
        rv = _dess_acl_rule_sw_to_hw(dev_id, &rule[i], hw_rule_tmp[dev_id],
                                     &new_flt_nr);
        SW_RTN_ON_ERROR(rv);
    }

    if (free_flt_nr < (new_flt_nr - old_flt_nr))
    {
        return SW_NO_RESOURCE;
    }

    for (i = old_flt_nr; i < new_flt_nr; i++)
    {
        hw_rule_tmp[dev_id][i].status |= ENT_USED;
        hw_rule_tmp[dev_id][i].list_id = sw_list->list_id;
        hw_rule_tmp[dev_id][i].list_pri = sw_list->list_pri;
        bind_pts = sw_list->bind_pts;
        SW_SET_REG_BY_FIELD(MAC_RUL_V4, SRC_PT, bind_pts,
                            (hw_rule_tmp[dev_id][i].filter.vlu[4]));
    }

    for (i = 0; i < old_flt_nr; i++)
    {
        sw_rule = &(sw_rule_ent[dev_id][old_flt_idx + i]);
        sw_rule->status &= (~ENT_USED);
        sw_rule->status |= (ENT_TMP);
    }

    rv = _dess_acl_rule_alloc(dev_id, sw_list, new_flt_nr);
    if (SW_OK != rv)
    {
        aos_mem_zero(sw_rule_tmp[dev_id],
                     DESS_MAX_FILTER * sizeof (dess_acl_rule_t));
        rv = _dess_acl_rule_reorder(dev_id, sw_list);
    }

    for (i = 0; i < old_flt_nr; i++)
    {
        sw_rule = &(sw_rule_ent[dev_id][i + old_flt_idx]);
        sw_rule->status |= (ENT_USED);
        sw_rule->status &= (~ENT_TMP);
    }
    SW_RTN_ON_ERROR(rv);

    _dess_acl_rule_sync(dev_id, 0, DESS_MAX_FILTER);
    sw_list->rule_nr += rule_nr;

    _dess_acl_sw_rule_dump("sw rule after add", sw_rule_ent[dev_id]);
    return SW_OK;
}

static sw_error_t
_dess_acl_rule_delete(a_uint32_t dev_id, a_uint32_t list_id,
                      a_uint32_t rule_id, a_uint32_t rule_nr)
{
    dess_acl_rule_t *sw_rule;
    dess_acl_list_t *sw_list;
    a_uint32_t i, flt_idx = 0, src_idx, dst_idx, del_nr = 0, flt_nr = 0;
    sw_error_t rv;

    HSL_DEV_ID_CHECK(dev_id);

    if (DESS_MAX_LIST_ID < list_id)
    {
        return SW_NOT_SUPPORTED;
    }

    sw_list = _dess_acl_list_loc(dev_id, list_id);
    if (NULL == sw_list)
    {
        return SW_NOT_FOUND;
    }

    if (sw_list->rule_nr < (rule_id + rule_nr))
    {
        return SW_BAD_PARAM;
    }

    aos_mem_zero(hw_rule_tmp[dev_id],
                 DESS_HW_RULE_TMP_CNT * sizeof (dess_acl_rule_t));
    aos_mem_zero(sw_rule_tmp[dev_id],
                 DESS_MAX_FILTER * sizeof (dess_acl_rule_t));

    for (i = 0; i < DESS_MAX_FILTER; i++)
    {
        sw_rule = &(sw_rule_ent[dev_id][i]);
        if ((ENT_USED & sw_rule->status) && (sw_rule->list_id == list_id))
        {
            if (!flt_nr)
            {
                flt_idx = i;
            }

            if ((sw_rule->rule_id >= rule_id)
                    && (sw_rule->rule_id < (rule_id + rule_nr)))
            {
                del_nr++;
            }
            else
            {
                aos_mem_copy(&(hw_rule_tmp[dev_id][flt_idx + flt_nr]), sw_rule,
                             sizeof (dess_acl_rule_t));
            }
            flt_nr++;
        }
    }

    if (!del_nr)
    {
        return SW_NOT_FOUND;
    }

    _dess_acl_sw_rule_dump("hw rule before del", hw_rule_tmp[dev_id]);

    for (i = 0; i < flt_nr; i++)
    {
        sw_rule = &(hw_rule_tmp[dev_id][flt_idx + i]);
        if (ENT_USED & sw_rule->status)
        {
            break;
        }
    }

    if (i != flt_nr)
    {
        src_idx = flt_idx + i;
        dst_idx = flt_idx;
        rv = _dess_acl_list_insert(dev_id, &src_idx, &dst_idx,
                                   hw_rule_tmp[dev_id], sw_rule_tmp[dev_id]);
        SW_RTN_ON_ERROR(rv);
    }

    _dess_acl_rule_sync(dev_id, flt_idx, flt_nr);
    sw_list->rule_nr -= rule_nr;

    _dess_acl_sw_rule_dump("sw rule after del", sw_rule_ent[dev_id]);
    return SW_OK;
}

static sw_error_t
_dess_acl_rule_query(a_uint32_t dev_id, a_uint32_t list_id,
                     a_uint32_t rule_id, fal_acl_rule_t * rule)
{
    sw_error_t rv;
    dess_acl_rule_t *sw_rule;
    a_uint32_t flt_nr = 0, i;

    HSL_DEV_ID_CHECK(dev_id);

    if (DESS_MAX_LIST_ID < list_id)
    {
        return SW_NOT_SUPPORTED;
    }

    aos_mem_zero(hw_rule_tmp[dev_id],
                 DESS_HW_RULE_TMP_CNT * sizeof (dess_acl_rule_t));
    for (i = 0; i < DESS_MAX_FILTER; i++)
    {
        sw_rule = &(sw_rule_ent[dev_id][i]);
        if (ENT_USED & sw_rule->status)
        {
            if ((sw_rule->list_id == list_id) && (sw_rule->rule_id == rule_id))
            {
                aos_mem_copy(&(hw_rule_tmp[dev_id][flt_nr]), sw_rule,
                             sizeof (dess_acl_rule_t));
                flt_nr++;
            }
        }
    }

    if (!flt_nr)
    {
        return SW_NOT_FOUND;
    }

    aos_mem_zero(rule, sizeof (fal_acl_rule_t));
    rv = _dess_acl_rule_hw_to_sw(dev_id, rule, hw_rule_tmp[dev_id], 0, flt_nr);
    return rv;
}

static sw_error_t
_dess_acl_rule_bind(a_uint32_t dev_id, a_uint32_t list_id, a_uint32_t ports)
{
    sw_error_t rv;
    a_uint32_t i;
    dess_acl_rule_t *sw_rule;

    for (i = 0; i < DESS_MAX_FILTER; i++)
    {
        sw_rule = &(sw_rule_ent[dev_id][i]);

        if ((ENT_USED & sw_rule->status)
                && (list_id == sw_rule->list_id)
                && (!(ENT_DEACTIVE & sw_rule->status)))
        {
            rv = _dess_filter_ports_bind(dev_id, i, ports);
            SW_RTN_ON_ERROR(rv);

            SW_SET_REG_BY_FIELD(MAC_RUL_V4, SRC_PT, ports,
                                (sw_rule->filter.vlu[4]));
        }
    }

    return SW_OK;
}

static sw_error_t
_dess_acl_list_bind(a_uint32_t dev_id, a_uint32_t list_id,
                    fal_acl_direc_t direc, fal_acl_bind_obj_t obj_t,
                    a_uint32_t obj_idx)
{
    sw_error_t rv;
    a_uint32_t ports;
    dess_acl_list_t *sw_list;

    HSL_DEV_ID_CHECK(dev_id);

    if (DESS_MAX_LIST_ID < list_id)
    {
        return SW_NOT_SUPPORTED;
    }

    if (FAL_ACL_DIREC_IN != direc)
    {
        return SW_NOT_SUPPORTED;
    }

    sw_list = _dess_acl_list_loc(dev_id, list_id);
    if (NULL == sw_list)
    {
        return SW_NOT_FOUND;
    }

    if (FAL_ACL_BIND_PORT == obj_t)
    {
        ports = (sw_list->bind_pts) | (0x1 << obj_idx);
    }
    else if (FAL_ACL_BIND_PORTBITMAP == obj_t)
    {
        ports = (sw_list->bind_pts) | obj_idx;
    }
    else
    {
        return SW_NOT_SUPPORTED;
    }

    rv = _dess_acl_rule_bind(dev_id, list_id, ports);
    SW_RTN_ON_ERROR(rv);

    sw_list->bind_pts = ports;
    return SW_OK;
}

static sw_error_t
_dess_acl_list_unbind(a_uint32_t dev_id, a_uint32_t list_id,
                      fal_acl_direc_t direc, fal_acl_bind_obj_t obj_t,
                      a_uint32_t obj_idx)
{
    sw_error_t rv;
    a_uint32_t ports;
    dess_acl_list_t *sw_list;

    HSL_DEV_ID_CHECK(dev_id);

    if (DESS_MAX_LIST_ID < list_id)
    {
        return SW_NOT_SUPPORTED;
    }

    if (FAL_ACL_DIREC_IN != direc)
    {
        return SW_NOT_SUPPORTED;
    }

    sw_list = _dess_acl_list_loc(dev_id, list_id);
    if (NULL == sw_list)
    {
        return SW_NOT_FOUND;
    }

    if (FAL_ACL_BIND_PORT == obj_t)
    {
        ports = (sw_list->bind_pts) & (~(0x1UL << obj_idx));
    }
    else if (FAL_ACL_BIND_PORTBITMAP == obj_t)
    {
        ports = (sw_list->bind_pts) & (~obj_idx);
    }
    else
    {
        return SW_NOT_SUPPORTED;
    }

    rv = _dess_acl_rule_bind(dev_id, list_id, ports);
    SW_RTN_ON_ERROR(rv);

    sw_list->bind_pts = ports;
    return SW_OK;
}

static sw_error_t
_dess_acl_status_set(a_uint32_t dev_id, a_bool_t enable)
{
    sw_error_t rv;
    a_uint32_t val;

    HSL_DEV_ID_CHECK(dev_id);

    if (A_TRUE == enable)
    {
        val = 1;
    }
    else if (A_FALSE == enable)
    {
        val = 0;
    }
    else
    {
        return SW_BAD_PARAM;
    }

    HSL_REG_FIELD_SET(rv, dev_id, MOD_ENABLE, 0, ACL_EN, (a_uint8_t *) (&val),
                      sizeof (a_uint32_t));
    return rv;
}

static sw_error_t
_dess_acl_status_get(a_uint32_t dev_id, a_bool_t * enable)
{
    sw_error_t rv;
    a_uint32_t val = 0;

    HSL_DEV_ID_CHECK(dev_id);

    HSL_REG_FIELD_GET(rv, dev_id, MOD_ENABLE, 0, ACL_EN, (a_uint8_t *) (&val),
                      sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    if (val)
    {
        *enable = A_TRUE;
    }
    else
    {
        *enable = A_FALSE;
    }

    return SW_OK;
}

static sw_error_t
_dess_acl_port_udf_profile_set(a_uint32_t dev_id, fal_port_t port_id,
                               fal_acl_udf_type_t udf_type, a_uint32_t offset,
                               a_uint32_t length)
{
    sw_error_t rv;
    a_uint32_t reg = 0;

    HSL_DEV_ID_CHECK(dev_id);

    if (DESS_UDF_MAX_OFFSET < offset)
    {
        return SW_BAD_PARAM;
    }

    if (DESS_UDF_MAX_OFFSET < length)
    {
        return SW_BAD_PARAM;
    }

    if ((FAL_ACL_UDF_TYPE_L2_SNAP == udf_type)
            || (FAL_ACL_UDF_TYPE_L3_PLUS == udf_type))
    {
        HSL_REG_ENTRY_GET(rv, dev_id, WIN_RULE_CTL1, port_id,
                          (a_uint8_t *) (&reg), sizeof (a_uint32_t));
    }
    else
    {
        HSL_REG_ENTRY_GET(rv, dev_id, WIN_RULE_CTL0, port_id,
                          (a_uint8_t *) (&reg), sizeof (a_uint32_t));
    }
    SW_RTN_ON_ERROR(rv);

    switch (udf_type)
    {
        case FAL_ACL_UDF_TYPE_L2:
            SW_SET_REG_BY_FIELD(WIN_RULE_CTL0, L2_OFFSET, offset, reg);
            SW_SET_REG_BY_FIELD(WIN_RULE_CTL0, L2_LENGTH, length, reg);
            break;
        case FAL_ACL_UDF_TYPE_L3:
            SW_SET_REG_BY_FIELD(WIN_RULE_CTL0, L3_OFFSET, offset, reg);
            SW_SET_REG_BY_FIELD(WIN_RULE_CTL0, L3_LENGTH, length, reg);
            break;
        case FAL_ACL_UDF_TYPE_L4:
            SW_SET_REG_BY_FIELD(WIN_RULE_CTL0, L4_OFFSET, offset, reg);
            SW_SET_REG_BY_FIELD(WIN_RULE_CTL0, L4_LENGTH, length, reg);
            break;
        case FAL_ACL_UDF_TYPE_L2_SNAP:
            SW_SET_REG_BY_FIELD(WIN_RULE_CTL1, L2S_OFFSET, offset, reg);
            SW_SET_REG_BY_FIELD(WIN_RULE_CTL1, L2S_LENGTH, length, reg);
            break;
        case FAL_ACL_UDF_TYPE_L3_PLUS:
            SW_SET_REG_BY_FIELD(WIN_RULE_CTL1, L3P_OFFSET, offset, reg);
            SW_SET_REG_BY_FIELD(WIN_RULE_CTL1, L3P_LENGTH, length, reg);
            break;
        default:
            return SW_BAD_PARAM;
    }

    if ((FAL_ACL_UDF_TYPE_L2_SNAP == udf_type)
            || (FAL_ACL_UDF_TYPE_L3_PLUS == udf_type))
    {
        HSL_REG_ENTRY_SET(rv, dev_id, WIN_RULE_CTL1, port_id,
                          (a_uint8_t *) (&reg), sizeof (a_uint32_t));
    }
    else
    {
        HSL_REG_ENTRY_SET(rv, dev_id, WIN_RULE_CTL0, port_id,
                          (a_uint8_t *) (&reg), sizeof (a_uint32_t));
    }

    return rv;
}

static sw_error_t
_dess_acl_port_udf_profile_get(a_uint32_t dev_id, fal_port_t port_id,
                               fal_acl_udf_type_t udf_type, a_uint32_t * offset,
                               a_uint32_t * length)
{
    sw_error_t rv;
    a_uint32_t reg = 0;

    HSL_DEV_ID_CHECK(dev_id);

    if ((FAL_ACL_UDF_TYPE_L2_SNAP == udf_type)
            || (FAL_ACL_UDF_TYPE_L3_PLUS == udf_type))
    {
        HSL_REG_ENTRY_GET(rv, dev_id, WIN_RULE_CTL1, port_id,
                          (a_uint8_t *) (&reg), sizeof (a_uint32_t));
    }
    else
    {
        HSL_REG_ENTRY_GET(rv, dev_id, WIN_RULE_CTL0, port_id,
                          (a_uint8_t *) (&reg), sizeof (a_uint32_t));
    }
    SW_RTN_ON_ERROR(rv);

    switch (udf_type)
    {
        case FAL_ACL_UDF_TYPE_L2:
            SW_GET_FIELD_BY_REG(WIN_RULE_CTL0, L2_OFFSET, (*offset), reg);
            SW_GET_FIELD_BY_REG(WIN_RULE_CTL0, L2_LENGTH, (*length), reg);
            break;
        case FAL_ACL_UDF_TYPE_L3:
            SW_GET_FIELD_BY_REG(WIN_RULE_CTL0, L3_OFFSET, (*offset), reg);
            SW_GET_FIELD_BY_REG(WIN_RULE_CTL0, L3_LENGTH, (*length), reg);
            break;
        case FAL_ACL_UDF_TYPE_L4:
            SW_GET_FIELD_BY_REG(WIN_RULE_CTL0, L4_OFFSET, (*offset), reg);
            SW_GET_FIELD_BY_REG(WIN_RULE_CTL0, L4_LENGTH, (*length), reg);
            break;
        case FAL_ACL_UDF_TYPE_L2_SNAP:
            SW_GET_FIELD_BY_REG(WIN_RULE_CTL1, L2S_OFFSET, (*offset), reg);
            SW_GET_FIELD_BY_REG(WIN_RULE_CTL1, L2S_LENGTH, (*length), reg);
            break;
        case FAL_ACL_UDF_TYPE_L3_PLUS:
            SW_GET_FIELD_BY_REG(WIN_RULE_CTL1, L3P_OFFSET, (*offset), reg);
            SW_GET_FIELD_BY_REG(WIN_RULE_CTL1, L3P_LENGTH, (*length), reg);
            break;
        default:
            return SW_BAD_PARAM;
    }

    return SW_OK;
}

static sw_error_t
_dess_acl_rule_active(a_uint32_t dev_id, a_uint32_t list_id,
                      a_uint32_t rule_id, a_uint32_t rule_nr, a_bool_t active)
{
    sw_error_t rv;
    a_uint32_t i, ports;
    dess_acl_list_t *sw_list;
    dess_acl_rule_t *sw_rule;

    HSL_DEV_ID_CHECK(dev_id);

    sw_list = _dess_acl_list_loc(dev_id, list_id);
    if (NULL == sw_list)
    {
        return SW_NOT_FOUND;
    }

    if (sw_list->rule_nr < (rule_id + rule_nr))
    {
        return SW_BAD_PARAM;
    }

    if (A_TRUE == active)
    {
        ports = (sw_list->bind_pts);
    }
    else
    {
        ports = 0;
    }

    for (i = 0; i < DESS_MAX_FILTER; i++)
    {
        sw_rule = &(sw_rule_ent[dev_id][i]);

        if ((ENT_USED & sw_rule->status)
                && (list_id == sw_rule->list_id)
                && (rule_id <= sw_rule->rule_id)
                && ((rule_id + rule_nr) > sw_rule->rule_id))
        {
            rv = _dess_filter_ports_bind(dev_id, i, ports);
            SW_RTN_ON_ERROR(rv);

            SW_SET_REG_BY_FIELD(MAC_RUL_V4, SRC_PT, ports,
                                (sw_rule->filter.vlu[4]));

            if (A_TRUE == active)
            {
                sw_rule->status &= (~ENT_DEACTIVE);
            }
            else
            {
                sw_rule->status |= (ENT_DEACTIVE);
            }
        }
    }

    return SW_OK;
}

static sw_error_t
_dess_acl_rule_src_filter_sts_set(a_uint32_t dev_id,
                                  a_uint32_t rule_id, a_bool_t enable)
{
    sw_error_t rv;
    a_uint32_t reg = 0, regIdx;
    dess_acl_rule_t *sw_rule;

    HSL_DEV_ID_CHECK(dev_id);

    sw_rule = &sw_rule_ent[dev_id][rule_id];
    if (!(ENT_USED & sw_rule->status))
    {
        return SW_NOT_FOUND;
    }

    regIdx = rule_id >> 5;

    HSL_REG_ENTRY_GET(rv, dev_id, ACL_FWD_SRC_FILTER_CTL0, regIdx,
                      (a_uint8_t *) (&reg), sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    if (A_TRUE == enable)
    {
        reg |= (0x1 << (rule_id & 0x1F));
        sw_rule->src_flt_dis = 0;
    }
    else if (A_FALSE == enable)
    {
        reg &= ~(0x1 << (rule_id & 0x1F));
        sw_rule->src_flt_dis = 1;
    }
    else
    {
        return SW_BAD_PARAM;
    }

    HSL_REG_ENTRY_SET(rv, dev_id, ACL_FWD_SRC_FILTER_CTL0, regIdx,
                      (a_uint8_t *) (&reg), sizeof (a_uint32_t));

    return rv;
}

static sw_error_t
_dess_acl_rule_src_filter_sts_get(a_uint32_t dev_id,
                                  a_uint32_t rule_id, a_bool_t* enable)
{
    sw_error_t rv;
    a_uint32_t reg = 0, regIdx;
    dess_acl_rule_t *sw_rule;

    HSL_DEV_ID_CHECK(dev_id);

    sw_rule = &sw_rule_ent[dev_id][rule_id];
    if (!(ENT_USED & sw_rule->status))
    {
        return SW_NOT_FOUND;
    }

    regIdx = rule_id >> 5;

    HSL_REG_ENTRY_GET(rv, dev_id, ACL_FWD_SRC_FILTER_CTL0, regIdx,
                      (a_uint8_t *) (&reg), sizeof (a_uint32_t));
    SW_RTN_ON_ERROR(rv);

    if (reg & (0x1 << (rule_id & 0x1F)))
    {
        *enable = A_TRUE;
    }
    else
    {
        *enable = A_FALSE;
    }

    return rv;
}


HSL_LOCAL sw_error_t
dess_acl_list_dump(a_uint32_t dev_id)
{
    _dess_acl_list_dump(dev_id);
    return SW_OK;
}

HSL_LOCAL sw_error_t
dess_acl_rule_dump(a_uint32_t dev_id)
{
    a_uint32_t flt_idx, i;
    sw_error_t rv;
    hw_filter_t filter;

    aos_printk("\ndess_acl_rule_dump:\n");

    for (flt_idx = 0; flt_idx < DESS_MAX_FILTER; flt_idx++)
    {
        aos_mem_zero(&filter, sizeof (hw_filter_t));

        rv = _dess_filter_up_to_sw(dev_id, &filter, flt_idx);
        if (SW_OK != rv)
        {
            continue;
        }

        aos_printk("\n%d filter dump:", flt_idx);

        aos_printk("\nhardware content:");
        aos_printk("\nact:");
        for (i = 0; i < (sizeof (filter.act) / sizeof (a_uint32_t)); i++)
        {
            aos_printk("%08x  ", filter.act[i]);
        }

        aos_printk("\nvlu:");
        for (i = 0; i < (sizeof (filter.vlu) / sizeof (a_uint32_t)); i++)
        {
            aos_printk("%08x  ", filter.vlu[i]);
        }

        aos_printk("\nmsk:");
        for (i = 0; i < (sizeof (filter.msk) / sizeof (a_uint32_t)); i++)
        {
            aos_printk("%08x  ", filter.msk[i]);
        }

        aos_printk("\nsoftware content:");
        aos_printk("\nact:");
        for (i = 0; i < (sizeof (filter.act) / sizeof (a_uint32_t)); i++)
        {
            aos_printk("%08x  ", sw_rule_ent[dev_id][flt_idx].filter.act[i]);
        }

        aos_printk("\nvlu:");
        for (i = 0; i < (sizeof (filter.vlu) / sizeof (a_uint32_t)); i++)
        {
            aos_printk("%08x  ", sw_rule_ent[dev_id][flt_idx].filter.vlu[i]);
        }

        aos_printk("\nmsk:");
        for (i = 0; i < (sizeof (filter.msk) / sizeof (a_uint32_t)); i++)
        {
            aos_printk("%08x  ", sw_rule_ent[dev_id][flt_idx].filter.msk[i]);
        }

        aos_printk("\nctl:status[%02d] list_id[%02d] rule_id[%02d] src_flt_dis[%02d]",
                   sw_rule_ent[dev_id][flt_idx].status,
                   sw_rule_ent[dev_id][flt_idx].list_id,
                   sw_rule_ent[dev_id][flt_idx].rule_id,
                   sw_rule_ent[dev_id][flt_idx].src_flt_dis);

        aos_printk("\n\n");
    }

    return SW_OK;
}

sw_error_t
dess_acl_reset(a_uint32_t dev_id)
{
    HSL_DEV_ID_CHECK(dev_id);

    aos_mem_zero(sw_list_ent[dev_id],
                 DESS_MAX_FILTER * sizeof (dess_acl_list_t));

    aos_mem_zero(sw_rule_ent[dev_id],
                 DESS_MAX_FILTER * sizeof (dess_acl_rule_t));

    return SW_OK;
}

/**
 * @brief Creat an acl list
 * @details  Comments:
  *     If the value of list_pri is more small then the priority is more high,
 *     that means the list could be first matched.
 * @param[in] dev_id device id
 * @param[in] list_id acl list id
 * @param[in] list_pri acl list priority
 * @return SW_OK or error code
 */
sw_error_t
dess_acl_list_creat(a_uint32_t dev_id, a_uint32_t list_id, a_uint32_t list_pri)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_list_creat(dev_id, list_id, list_pri);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief Destroy an acl list
 * @param[in] dev_id device id
 * @param[in] list_id acl list id
 * @return SW_OK or error code
 */
HSL_LOCAL sw_error_t
dess_acl_list_destroy(a_uint32_t dev_id, a_uint32_t list_id)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_list_destroy(dev_id, list_id);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief Add one rule or more rules to an existing acl list
 * @param[in] dev_id device id
 * @param[in] list_id acl list id
 * @param[in] rule_id first rule id of this adding operation in list
 * @param[in] rule_nr rule number of this adding operation
 * @param[in] rule rules content of this adding operation
 * @return SW_OK or error code
 */
sw_error_t
dess_acl_rule_add(a_uint32_t dev_id, a_uint32_t list_id,
                  a_uint32_t rule_id, a_uint32_t rule_nr, fal_acl_rule_t * rule)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_rule_add(dev_id, list_id, rule_id, rule_nr, rule);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief Delete one rule or more rules from an existing acl list
 * @param[in] dev_id device id
 * @param[in] list_id acl list id
 * @param[in] rule_id first rule id of this deleteing operation in list
 * @param[in] rule_nr rule number of this deleteing operation
 * @return SW_OK or error code
 */
sw_error_t
dess_acl_rule_delete(a_uint32_t dev_id, a_uint32_t list_id,
                     a_uint32_t rule_id, a_uint32_t rule_nr)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_rule_delete(dev_id, list_id, rule_id, rule_nr);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief Query one particular rule in a particular acl list
 * @param[in] dev_id device id
 * @param[in] list_id acl list id
 * @param[in] rule_id first rule id of this deleteing operation in list
 * @param[out] rule rule content of this operation
 * @return SW_OK or error code
 */
sw_error_t
dess_acl_rule_query(a_uint32_t dev_id, a_uint32_t list_id,
                    a_uint32_t rule_id, fal_acl_rule_t * rule)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_rule_query(dev_id, list_id, rule_id, rule);
    HSL_API_UNLOCK;
    return rv;
}

a_uint32_t
dess_acl_rule_get_offset(a_uint32_t dev_id, a_uint32_t list_id, a_uint32_t rule_id)
{
    a_uint32_t i, pos=0;
    dess_acl_rule_t *sw_rule;

    for (i = 0; i < DESS_MAX_FILTER; i++)
    {
        sw_rule = &(sw_rule_ent[dev_id][i]);

        if ((ENT_USED & sw_rule->status)
                && (list_id == sw_rule->list_id) && (sw_rule->rule_id == rule_id)
                && (!(ENT_DEACTIVE & sw_rule->status)))
        {
            pos = i;
            break;

        }
    }

    return pos;
}


sw_error_t
dess_acl_rule_sync_multi_portmap(a_uint32_t dev_id, a_uint32_t pos, a_uint32_t *act)
{

    HSL_DEV_ID_CHECK(dev_id);

    if (DESS_MAX_LIST_ID < pos)
    {
        return SW_NOT_SUPPORTED;
    }

    sw_rule_ent[dev_id][pos].filter.act[1] = act[1];
    sw_rule_ent[dev_id][pos].filter.act[2] = act[2];

    sw_rule_tmp[dev_id][pos].filter.act[1] = act[1];
    sw_rule_tmp[dev_id][pos].filter.act[2] = act[2];

    hw_rule_tmp[dev_id][pos].filter.act[1] = act[1];
    hw_rule_tmp[dev_id][pos].filter.act[2] = act[2];


    return SW_OK;
}

/**
 * @brief Bind an acl list to a particular object
 * @details  Comments:
 *    If obj_t equals FAL_ACL_BIND_PORT then obj_idx means port id
 * @param[in] dev_id device id
 * @param[in] list_id acl list id
 * @param[in] direc direction of this binding operation
 * @param[in] obj_t object type of this binding operation
 * @param[in] obj_idx object index of this binding operation
 * @return SW_OK or error code
 */
sw_error_t
dess_acl_list_bind(a_uint32_t dev_id, a_uint32_t list_id,
                   fal_acl_direc_t direc, fal_acl_bind_obj_t obj_t,
                   a_uint32_t obj_idx)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_list_bind(dev_id, list_id, direc, obj_t, obj_idx);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief Unbind an acl list from a particular object
 * @details  Comments:
  *    If obj_t equals FAL_ACL_BIND_PORT then obj_idx means port id
 * @param[in] dev_id device id
 * @param[in] list_id acl list id
 * @param[in] direc direction of this unbinding operation
 * @param[in] obj_t object type of this unbinding operation
 * @param[in] obj_idx object index of this unbinding operation
 * @return SW_OK or error code
 */
sw_error_t
dess_acl_list_unbind(a_uint32_t dev_id, a_uint32_t list_id,
                     fal_acl_direc_t direc, fal_acl_bind_obj_t obj_t,
                     a_uint32_t obj_idx)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_list_unbind(dev_id, list_id, direc, obj_t, obj_idx);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief Set working status of ACL engine on a particular device
 * @param[in] dev_id device id
 * @param[in] enable A_TRUE or A_FALSE
 * @return SW_OK or error code
 */
sw_error_t
dess_acl_status_set(a_uint32_t dev_id, a_bool_t enable)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_status_set(dev_id, enable);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief Get working status of ACL engine on a particular device
 * @param[in] dev_id device id
 * @param[out] enable A_TRUE or A_FALSE
 * @return SW_OK or error code
 */
HSL_LOCAL sw_error_t
dess_acl_status_get(a_uint32_t dev_id, a_bool_t * enable)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_status_get(dev_id, enable);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief Set user define fields profile on a particular port
 * @param[in] dev_id device id
 * @param[in] port_id port id
 * @param[in] udf_type udf type
 * @param[in] offset udf offset
 * @param[in] length udf length
 * @return SW_OK or error code
 */
HSL_LOCAL sw_error_t
dess_acl_port_udf_profile_set(a_uint32_t dev_id, fal_port_t port_id,
                              fal_acl_udf_type_t udf_type, a_uint32_t offset,
                              a_uint32_t length)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_port_udf_profile_set(dev_id, port_id, udf_type, offset,
                                        length);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief Get user define fields profile on a particular port
 * @param[in] dev_id device id
 * @param[in] port_id port id
 * @param[in] udf_type udf type
 * @param[out] offset udf offset
 * @param[out] length udf length
 * @return SW_OK or error code
 */
HSL_LOCAL sw_error_t
dess_acl_port_udf_profile_get(a_uint32_t dev_id, fal_port_t port_id,
                              fal_acl_udf_type_t udf_type, a_uint32_t * offset,
                              a_uint32_t * length)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_port_udf_profile_get(dev_id, port_id, udf_type, offset,
                                        length);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief Active one or more rules in an existing acl list
 * @param[in] dev_id device id
 * @param[in] list_id acl list id
 * @param[in] rule_id first rule id of this deactive operation in list
 * @param[in] rule_nr rule number of this deactive operation
 * @return SW_OK or error code
 */
HSL_LOCAL sw_error_t
dess_acl_rule_active(a_uint32_t dev_id, a_uint32_t list_id,
                     a_uint32_t rule_id, a_uint32_t rule_nr)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_rule_active(dev_id, list_id, rule_id, rule_nr, A_TRUE);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief Deactive one or more rules in an existing acl list
 * @param[in] dev_id device id
 * @param[in] list_id acl list id
 * @param[in] rule_id first rule id of this deactive operation in list
 * @param[in] rule_nr rule number of this deactive operation
 * @return SW_OK or error code
 */
HSL_LOCAL sw_error_t
dess_acl_rule_deactive(a_uint32_t dev_id, a_uint32_t list_id,
                       a_uint32_t rule_id, a_uint32_t rule_nr)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_rule_active(dev_id, list_id, rule_id, rule_nr, A_FALSE);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief enable acl forward source filter of one rule.
 * @param[in] dev_id device id
 * @param[in] rule_id first rule id of this deactive operation in list
 * @param[in] enable A_TRUE or A_FALSE
 * @return SW_OK or error code
 */
HSL_LOCAL sw_error_t
dess_acl_rule_src_filter_sts_set(a_uint32_t dev_id,
                                 a_uint32_t rule_id, a_bool_t enable)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_rule_src_filter_sts_set(dev_id, rule_id, enable);
    HSL_API_UNLOCK;
    return rv;
}

/**
 * @brief get the status of acl forward source filter of one rule.
 * @param[in] dev_id device id
 * @param[in] rule_id first rule id of this deactive operation in list
 * @param[out] enable A_TRUE or A_FALSE
 * @return SW_OK or error code
 */
HSL_LOCAL sw_error_t
dess_acl_rule_src_filter_sts_get(a_uint32_t dev_id,
                                 a_uint32_t rule_id, a_bool_t* enable)
{
    sw_error_t rv;

    HSL_API_LOCK;
    rv = _dess_acl_rule_src_filter_sts_get(dev_id, rule_id, enable);
    HSL_API_UNLOCK;
    return rv;
}



sw_error_t
dess_acl_init(a_uint32_t dev_id)
{
    HSL_DEV_ID_CHECK(dev_id);

    sw_list_ent[dev_id] =
        (dess_acl_list_t *) aos_mem_alloc(DESS_MAX_FILTER *
                                          sizeof (dess_acl_list_t));
    if (NULL == sw_list_ent[dev_id])
    {
        return SW_NO_RESOURCE;
    }
    aos_mem_zero(sw_list_ent[dev_id],
                 DESS_MAX_FILTER * sizeof (dess_acl_list_t));

    sw_rule_ent[dev_id] =
        (dess_acl_rule_t *) aos_mem_alloc(DESS_MAX_FILTER *
                                          sizeof (dess_acl_rule_t));
    if (NULL == sw_rule_ent[dev_id])
    {
        return SW_NO_RESOURCE;
    }
    aos_mem_zero(sw_rule_ent[dev_id],
                 DESS_MAX_FILTER * sizeof (dess_acl_rule_t));

    hw_rule_tmp[dev_id] =
        (dess_acl_rule_t *) aos_mem_alloc(DESS_HW_RULE_TMP_CNT *
                                          sizeof (dess_acl_rule_t));
    if (NULL == hw_rule_tmp[dev_id])
    {
        return SW_NO_RESOURCE;
    }

    sw_rule_tmp[dev_id] =
        (dess_acl_rule_t *) aos_mem_alloc(DESS_MAX_FILTER *
                                          sizeof (dess_acl_rule_t));
    if (NULL == sw_rule_tmp[dev_id])
    {
        return SW_NO_RESOURCE;
    }
#ifdef DESS_SW_ENTRY
    sw_filter_mem = aos_mem_alloc(DESS_MAX_FILTER * sizeof (hw_filter_t));
    if (NULL == sw_filter_mem)
    {
        return SW_NO_RESOURCE;
    }
    aos_mem_zero(sw_filter_mem, DESS_MAX_FILTER * sizeof (hw_filter_t));
#endif

#ifndef HSL_STANDALONG
    {
        hsl_api_t *p_api;

        SW_RTN_ON_NULL(p_api = hsl_api_ptr_get(dev_id));

        p_api->acl_list_creat = dess_acl_list_creat;
        p_api->acl_list_destroy = dess_acl_list_destroy;
        p_api->acl_list_bind = dess_acl_list_bind;
        p_api->acl_list_unbind = dess_acl_list_unbind;
        p_api->acl_rule_add = dess_acl_rule_add;
        p_api->acl_rule_delete = dess_acl_rule_delete;
        p_api->acl_rule_query = dess_acl_rule_query;
        p_api->acl_status_set = dess_acl_status_set;
        p_api->acl_status_get = dess_acl_status_get;
        p_api->acl_list_dump = dess_acl_list_dump;
        p_api->acl_rule_dump = dess_acl_rule_dump;
        p_api->acl_port_udf_profile_set = dess_acl_port_udf_profile_set;
        p_api->acl_port_udf_profile_get = dess_acl_port_udf_profile_get;
        p_api->acl_rule_active = dess_acl_rule_active;
        p_api->acl_rule_deactive = dess_acl_rule_deactive;
        p_api->acl_rule_src_filter_sts_set = dess_acl_rule_src_filter_sts_set;
        p_api->acl_rule_src_filter_sts_get = dess_acl_rule_src_filter_sts_get;
        p_api->acl_rule_get_offset = dess_acl_rule_get_offset;
        p_api->acl_rule_sync_multi_portmap = dess_acl_rule_sync_multi_portmap;
    }
#endif

    return SW_OK;
}

sw_error_t
dess_acl_cleanup(a_uint32_t dev_id)
{
    HSL_DEV_ID_CHECK(dev_id);

    if (NULL != sw_list_ent[dev_id])
    {
        aos_mem_free(sw_list_ent[dev_id]);
    }

    if (NULL != sw_rule_ent[dev_id])
    {
        aos_mem_free(sw_rule_ent[dev_id]);
    }

    if (NULL != hw_rule_tmp[dev_id])
    {
        aos_mem_free(hw_rule_tmp[dev_id]);
    }

    if (NULL != sw_rule_tmp[dev_id])
    {
        aos_mem_free(sw_rule_tmp[dev_id]);
    }
#ifdef DESS_SW_ENTRY
    if (NULL != sw_filter_mem)
    {
        aos_mem_free(sw_filter_mem);
    }
#endif

    return SW_OK;
}

/**
 * @}
 */

