/* Copyright (c) 2008-2011 Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Freescale Semiconductor nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 *
 * ALTERNATIVELY, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") as published by the Free Software
 * Foundation, either version 2 of that License or (at your option) any
 * later version.
 *
 * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/******************************************************************************
 @File          fm_kg.c

 @Description   FM PCD ...
*//***************************************************************************/
#include "std_ext.h"
#include "error_ext.h"
#include "string_ext.h"
#include "debug_ext.h"
#include "net_ext.h"
#include "fm_port_ext.h"

#include "fm_common.h"
#include "fm_pcd.h"
#include "fm_hc.h"

#include "fm_pcd_ipc.h"


static t_Error WriteKgarWait(t_FmPcd *p_FmPcd, uint32_t kgar)
{
    WRITE_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->kgar, kgar);
    /* Wait for GO to be idle and read error */
    while ((kgar = GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->kgar)) & FM_PCD_KG_KGAR_GO) ;
    if (kgar & FM_PCD_KG_KGAR_ERR)
        RETURN_ERROR(MINOR, E_INVALID_STATE, ("Keygen scheme access violation"));
    return E_OK;
}

static e_FmPcdKgExtractDfltSelect GetGenericSwDefault(t_FmPcdKgExtractDflt swDefaults[], uint8_t numOfSwDefaults, uint8_t code)
{
    int i;

    switch(code)
    {
        case( KG_SCH_GEN_PARSE_RESULT_N_FQID):
        case( KG_SCH_GEN_DEFAULT):
        case( KG_SCH_GEN_NEXTHDR):
            for(i=0 ; i<numOfSwDefaults ; i++)
                if(swDefaults[i].type == e_FM_PCD_KG_GENERIC_NOT_FROM_DATA)
                    return swDefaults[i].dfltSelect;
            ASSERT_COND(FALSE);
        case( KG_SCH_GEN_SHIM1):
        case( KG_SCH_GEN_SHIM2):
        case( KG_SCH_GEN_IP_PID_NO_V):
        case( KG_SCH_GEN_ETH_NO_V):
        case( KG_SCH_GEN_SNAP_NO_V):
        case( KG_SCH_GEN_VLAN1_NO_V):
        case( KG_SCH_GEN_VLAN2_NO_V):
        case( KG_SCH_GEN_ETH_TYPE_NO_V):
        case( KG_SCH_GEN_PPP_NO_V):
        case( KG_SCH_GEN_MPLS1_NO_V):
        case( KG_SCH_GEN_MPLS_LAST_NO_V):
        case( KG_SCH_GEN_L3_NO_V):
        case( KG_SCH_GEN_IP2_NO_V):
        case( KG_SCH_GEN_GRE_NO_V):
        case( KG_SCH_GEN_L4_NO_V):
            for(i=0 ; i<numOfSwDefaults ; i++)
                if(swDefaults[i].type == e_FM_PCD_KG_GENERIC_FROM_DATA_NO_V)
                    return swDefaults[i].dfltSelect;

        case( KG_SCH_GEN_START_OF_FRM):
        case( KG_SCH_GEN_ETH):
        case( KG_SCH_GEN_SNAP):
        case( KG_SCH_GEN_VLAN1):
        case( KG_SCH_GEN_VLAN2):
        case( KG_SCH_GEN_ETH_TYPE):
        case( KG_SCH_GEN_PPP):
        case( KG_SCH_GEN_MPLS1):
        case( KG_SCH_GEN_MPLS2):
        case( KG_SCH_GEN_MPLS3):
        case( KG_SCH_GEN_MPLS_LAST):
        case( KG_SCH_GEN_IPV4):
        case( KG_SCH_GEN_IPV6):
        case( KG_SCH_GEN_IPV4_TUNNELED):
        case( KG_SCH_GEN_IPV6_TUNNELED):
        case( KG_SCH_GEN_MIN_ENCAP):
        case( KG_SCH_GEN_GRE):
        case( KG_SCH_GEN_TCP):
        case( KG_SCH_GEN_UDP):
        case( KG_SCH_GEN_IPSEC_AH):
        case( KG_SCH_GEN_SCTP):
        case( KG_SCH_GEN_DCCP):
        case( KG_SCH_GEN_IPSEC_ESP):
            for(i=0 ; i<numOfSwDefaults ; i++)
                if(swDefaults[i].type == e_FM_PCD_KG_GENERIC_FROM_DATA)
                    return swDefaults[i].dfltSelect;
        default:
            return e_FM_PCD_KG_DFLT_ILLEGAL;
    }
}

static uint8_t GetGenCode(e_FmPcdExtractFrom src, uint8_t *p_Offset)
{
    *p_Offset = 0;

    switch(src)
    {
        case(e_FM_PCD_EXTRACT_FROM_FRAME_START):
            return KG_SCH_GEN_START_OF_FRM;
        case(e_FM_PCD_EXTRACT_FROM_DFLT_VALUE):
            return KG_SCH_GEN_DEFAULT;
        case(e_FM_PCD_EXTRACT_FROM_PARSE_RESULT):
            return KG_SCH_GEN_PARSE_RESULT_N_FQID;
        case(e_FM_PCD_EXTRACT_FROM_ENQ_FQID):
            *p_Offset = 32;
            return KG_SCH_GEN_PARSE_RESULT_N_FQID;
        case(e_FM_PCD_EXTRACT_FROM_CURR_END_OF_PARSE):
            return KG_SCH_GEN_NEXTHDR;
        default:
            REPORT_ERROR(MAJOR, E_INVALID_VALUE, ("Illegal 'extract from' src"));
            return 0;
    }
}

static uint8_t GetGenHdrCode(e_NetHeaderType hdr, e_FmPcdHdrIndex hdrIndex, bool ignoreProtocolValidation)
{
    if(!ignoreProtocolValidation)
        switch(hdr)
        {
            case(HEADER_TYPE_NONE):
                ASSERT_COND(FALSE);
            case(HEADER_TYPE_ETH):
                return KG_SCH_GEN_ETH;
            case(HEADER_TYPE_LLC_SNAP):
                return KG_SCH_GEN_SNAP;
            case(HEADER_TYPE_PPPoE):
                return KG_SCH_GEN_PPP;
            case(HEADER_TYPE_MPLS):
                if((hdrIndex == e_FM_PCD_HDR_INDEX_NONE) || (hdrIndex == e_FM_PCD_HDR_INDEX_1))
                    return KG_SCH_GEN_MPLS1;
                if(hdrIndex == e_FM_PCD_HDR_INDEX_2)
                    return KG_SCH_GEN_MPLS2;
                if(hdrIndex == e_FM_PCD_HDR_INDEX_3)
                    return KG_SCH_GEN_MPLS3;
                if(hdrIndex == e_FM_PCD_HDR_INDEX_LAST)
                    return KG_SCH_GEN_MPLS_LAST;
                REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal MPLS header index"));
                return 0;
            case(HEADER_TYPE_IPv4):
                if((hdrIndex == e_FM_PCD_HDR_INDEX_NONE) || (hdrIndex == e_FM_PCD_HDR_INDEX_1))
                    return KG_SCH_GEN_IPV4;
                if(hdrIndex == e_FM_PCD_HDR_INDEX_2)
                    return KG_SCH_GEN_IPV4_TUNNELED;
                REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal IPv4 header index"));
                return 0;
            case(HEADER_TYPE_IPv6):
                if((hdrIndex == e_FM_PCD_HDR_INDEX_NONE) || (hdrIndex == e_FM_PCD_HDR_INDEX_1))
                    return KG_SCH_GEN_IPV6;
                if(hdrIndex == e_FM_PCD_HDR_INDEX_2)
                    return KG_SCH_GEN_IPV6_TUNNELED;
                REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal IPv6 header index"));
                return 0;
            case(HEADER_TYPE_GRE):
                return KG_SCH_GEN_GRE;
            case(HEADER_TYPE_TCP):
                return KG_SCH_GEN_TCP;
            case(HEADER_TYPE_UDP):
                return KG_SCH_GEN_UDP;
            case(HEADER_TYPE_IPSEC_AH):
                return KG_SCH_GEN_IPSEC_AH;
            case(HEADER_TYPE_IPSEC_ESP):
                return KG_SCH_GEN_IPSEC_ESP;
            case(HEADER_TYPE_SCTP):
                return KG_SCH_GEN_SCTP;
            case(HEADER_TYPE_DCCP):
                return KG_SCH_GEN_DCCP;
            default:
                REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                return 0;
        }
    else
        switch(hdr)
        {
            case(HEADER_TYPE_NONE):
                ASSERT_COND(FALSE);
            case(HEADER_TYPE_ETH):
                return KG_SCH_GEN_ETH_NO_V;
            case(HEADER_TYPE_LLC_SNAP):
                return KG_SCH_GEN_SNAP_NO_V;
            case(HEADER_TYPE_PPPoE):
                return KG_SCH_GEN_PPP_NO_V;
            case(HEADER_TYPE_MPLS):
                 if((hdrIndex == e_FM_PCD_HDR_INDEX_NONE) || (hdrIndex == e_FM_PCD_HDR_INDEX_1))
                    return KG_SCH_GEN_MPLS1_NO_V;
                if(hdrIndex == e_FM_PCD_HDR_INDEX_LAST)
                    return KG_SCH_GEN_MPLS_LAST_NO_V;
                if((hdrIndex == e_FM_PCD_HDR_INDEX_2) || (hdrIndex == e_FM_PCD_HDR_INDEX_3) )
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Indexed MPLS Extraction not supported"));
                else
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal MPLS header index"));
                return 0;
            case(HEADER_TYPE_IPv4):
            case(HEADER_TYPE_IPv6):
              if((hdrIndex == e_FM_PCD_HDR_INDEX_NONE) || (hdrIndex == e_FM_PCD_HDR_INDEX_1))
                    return KG_SCH_GEN_L3_NO_V;
                if(hdrIndex == e_FM_PCD_HDR_INDEX_2)
                    return KG_SCH_GEN_IP2_NO_V;
                REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal IP header index"));
            case(HEADER_TYPE_MINENCAP):
                return KG_SCH_GEN_IP2_NO_V;
            case(HEADER_TYPE_USER_DEFINED_L3):
                return KG_SCH_GEN_L3_NO_V;
            case(HEADER_TYPE_GRE):
                return KG_SCH_GEN_GRE_NO_V;
            case(HEADER_TYPE_TCP):
            case(HEADER_TYPE_UDP):
            case(HEADER_TYPE_IPSEC_AH):
            case(HEADER_TYPE_IPSEC_ESP):
            case(HEADER_TYPE_SCTP):
            case(HEADER_TYPE_DCCP):
                return KG_SCH_GEN_L4_NO_V;
            case(HEADER_TYPE_USER_DEFINED_SHIM1):
                return KG_SCH_GEN_SHIM1;
            case(HEADER_TYPE_USER_DEFINED_SHIM2):
                return KG_SCH_GEN_SHIM2;
            default:
                REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                return 0;
        }
}
static t_GenericCodes GetGenFieldCode(e_NetHeaderType hdr, t_FmPcdFields field, bool ignoreProtocolValidation, e_FmPcdHdrIndex hdrIndex)
{
    if (!ignoreProtocolValidation)
        switch(hdr)
        {
            case(HEADER_TYPE_NONE):
                ASSERT_COND(FALSE);
            case(HEADER_TYPE_ETH):
                switch(field.eth)
                {
                    case(NET_HEADER_FIELD_ETH_TYPE):
                        return KG_SCH_GEN_ETH_TYPE;
                    default:
                        REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                        return 0;
                }
            case(HEADER_TYPE_VLAN):
                switch(field.vlan)
                {
                    case(NET_HEADER_FIELD_VLAN_TCI):
                        if((hdrIndex == e_FM_PCD_HDR_INDEX_NONE) || (hdrIndex == e_FM_PCD_HDR_INDEX_1))
                            return KG_SCH_GEN_VLAN1;
                        if(hdrIndex == e_FM_PCD_HDR_INDEX_LAST)
                            return KG_SCH_GEN_VLAN2;
                        REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal VLAN header index"));
                        return 0;
                }
            case(HEADER_TYPE_MPLS):
            case(HEADER_TYPE_IPSEC_AH):
            case(HEADER_TYPE_IPSEC_ESP):
            case(HEADER_TYPE_LLC_SNAP):
            case(HEADER_TYPE_PPPoE):
            case(HEADER_TYPE_IPv4):
            case(HEADER_TYPE_IPv6):
            case(HEADER_TYPE_GRE):
            case(HEADER_TYPE_MINENCAP):
            case(HEADER_TYPE_USER_DEFINED_L3):
            case(HEADER_TYPE_TCP):
            case(HEADER_TYPE_UDP):
            case(HEADER_TYPE_SCTP):
            case(HEADER_TYPE_DCCP):
            case(HEADER_TYPE_USER_DEFINED_L4):
                REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
            default:
                REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Header not supported"));
                return 0;
        }
        else
            switch(hdr)
            {
                case(HEADER_TYPE_NONE):
                    ASSERT_COND(FALSE);
                case(HEADER_TYPE_ETH):
                switch(field.eth)
                {
                    case(NET_HEADER_FIELD_ETH_TYPE):
                        return KG_SCH_GEN_ETH_TYPE_NO_V;
                    default:
                        REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                        return 0;
                }
                case(HEADER_TYPE_VLAN):
                    switch(field.vlan)
                    {
                        case(NET_HEADER_FIELD_VLAN_TCI) :
                            if((hdrIndex == e_FM_PCD_HDR_INDEX_NONE) || (hdrIndex == e_FM_PCD_HDR_INDEX_1))
                                return KG_SCH_GEN_VLAN1_NO_V;
                            if(hdrIndex == e_FM_PCD_HDR_INDEX_LAST)
                                return KG_SCH_GEN_VLAN2_NO_V;
                            REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal VLAN header index"));
                            return 0;
                    }
                case(HEADER_TYPE_IPv4):
                    switch(field.ipv4)
                    {
                        case(NET_HEADER_FIELD_IPv4_PROTO):
                            return KG_SCH_GEN_IP_PID_NO_V;
                        default:
                            REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                            return 0;
                    }
                case(HEADER_TYPE_IPv6):
                   switch(field.ipv6)
                    {
                        case(NET_HEADER_FIELD_IPv6_NEXT_HDR):
                            return KG_SCH_GEN_IP_PID_NO_V;
                        default:
                            REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                            return 0;
                    }
                case(HEADER_TYPE_MPLS):
                case(HEADER_TYPE_LLC_SNAP):
                case(HEADER_TYPE_PPPoE):
                case(HEADER_TYPE_GRE):
                case(HEADER_TYPE_MINENCAP):
                case(HEADER_TYPE_USER_DEFINED_L3):
                case(HEADER_TYPE_TCP):
                case(HEADER_TYPE_UDP):
                case(HEADER_TYPE_IPSEC_AH):
                case(HEADER_TYPE_IPSEC_ESP):
                case(HEADER_TYPE_SCTP):
                case(HEADER_TYPE_DCCP):
                case(HEADER_TYPE_USER_DEFINED_L4):
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Header not supported"));
                    return 0;
            }
}

static t_KnownFieldsMasks GetKnownProtMask(e_NetHeaderType hdr, e_FmPcdHdrIndex index, t_FmPcdFields field)
{
    switch(hdr)
    {
        case(HEADER_TYPE_NONE):
            ASSERT_COND(FALSE);
        case(HEADER_TYPE_ETH):
            switch(field.eth)
            {
                case(NET_HEADER_FIELD_ETH_DA):
                    return KG_SCH_KN_MACDST;
                case(NET_HEADER_FIELD_ETH_SA):
                    return KG_SCH_KN_MACSRC;
                case(NET_HEADER_FIELD_ETH_TYPE):
                    return KG_SCH_KN_ETYPE;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
           }
        case(HEADER_TYPE_LLC_SNAP):
            switch(field.llcSnap)
            {
                case(NET_HEADER_FIELD_LLC_SNAP_TYPE):
                    return KG_SCH_KN_ETYPE;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
           }
        case(HEADER_TYPE_VLAN):
            switch(field.vlan)
            {
                case(NET_HEADER_FIELD_VLAN_TCI):
                    if((index == e_FM_PCD_HDR_INDEX_NONE) || (index == e_FM_PCD_HDR_INDEX_1))
                        return KG_SCH_KN_TCI1;
                    if(index == e_FM_PCD_HDR_INDEX_LAST)
                        return KG_SCH_KN_TCI2;
                    else
                    {
                        REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                        return 0;
                    }
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
            }
        case(HEADER_TYPE_MPLS):
            switch(field.mpls)
            {
                case(NET_HEADER_FIELD_MPLS_LABEL_STACK):
                    if((index == e_FM_PCD_HDR_INDEX_NONE) || (index == e_FM_PCD_HDR_INDEX_1))
                        return KG_SCH_KN_MPLS1;
                    if(index == e_FM_PCD_HDR_INDEX_2)
                        return KG_SCH_KN_MPLS2;
                    if(index == e_FM_PCD_HDR_INDEX_LAST)
                        return KG_SCH_KN_MPLS_LAST;
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal MPLS index"));
                    return 0;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
            }
        case(HEADER_TYPE_IPv4):
            switch(field.ipv4)
            {
                case(NET_HEADER_FIELD_IPv4_SRC_IP):
                    if((index == e_FM_PCD_HDR_INDEX_NONE) || (index == e_FM_PCD_HDR_INDEX_1))
                        return KG_SCH_KN_IPSRC1;
                    if(index == e_FM_PCD_HDR_INDEX_2)
                        return KG_SCH_KN_IPSRC2;
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal IPv4 index"));
                    return 0;
                case(NET_HEADER_FIELD_IPv4_DST_IP):
                    if((index == e_FM_PCD_HDR_INDEX_NONE) || (index == e_FM_PCD_HDR_INDEX_1))
                        return KG_SCH_KN_IPDST1;
                    if(index == e_FM_PCD_HDR_INDEX_2)
                        return KG_SCH_KN_IPDST2;
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal IPv4 index"));
                    return 0;
                case(NET_HEADER_FIELD_IPv4_PROTO):
                    if((index == e_FM_PCD_HDR_INDEX_NONE) || (index == e_FM_PCD_HDR_INDEX_1))
                        return KG_SCH_KN_PTYPE1;
                    if(index == e_FM_PCD_HDR_INDEX_2)
                        return KG_SCH_KN_PTYPE2;
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal IPv4 index"));
                    return 0;
                case(NET_HEADER_FIELD_IPv4_TOS):
                    if((index == e_FM_PCD_HDR_INDEX_NONE) || (index == e_FM_PCD_HDR_INDEX_1))
                        return KG_SCH_KN_IPTOS_TC1;
                    if(index == e_FM_PCD_HDR_INDEX_2)
                        return KG_SCH_KN_IPTOS_TC2;
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal IPv4 index"));
                    return 0;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
            }
        case(HEADER_TYPE_IPv6):
             switch(field.ipv6)
            {
                case(NET_HEADER_FIELD_IPv6_SRC_IP):
                    if((index == e_FM_PCD_HDR_INDEX_NONE) || (index == e_FM_PCD_HDR_INDEX_1))
                        return KG_SCH_KN_IPSRC1;
                    if(index == e_FM_PCD_HDR_INDEX_2)
                        return KG_SCH_KN_IPSRC2;
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal IPv6 index"));
                    return 0;
                case(NET_HEADER_FIELD_IPv6_DST_IP):
                    if((index == e_FM_PCD_HDR_INDEX_NONE) || (index == e_FM_PCD_HDR_INDEX_1))
                        return KG_SCH_KN_IPDST1;
                    if(index == e_FM_PCD_HDR_INDEX_2)
                        return KG_SCH_KN_IPDST2;
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal IPv6 index"));
                    return 0;
                case(NET_HEADER_FIELD_IPv6_NEXT_HDR):
                    if((index == e_FM_PCD_HDR_INDEX_NONE) || (index == e_FM_PCD_HDR_INDEX_1))
                        return KG_SCH_KN_PTYPE1;
                    if(index == e_FM_PCD_HDR_INDEX_2)
                        return KG_SCH_KN_PTYPE2;
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal IPv6 index"));
                    return 0;
                case(NET_HEADER_FIELD_IPv6_VER | NET_HEADER_FIELD_IPv6_FL | NET_HEADER_FIELD_IPv6_TC):
                    if((index == e_FM_PCD_HDR_INDEX_NONE) || (index == e_FM_PCD_HDR_INDEX_1))
                        return (KG_SCH_KN_IPV6FL1 | KG_SCH_KN_IPTOS_TC1);
                    if(index == e_FM_PCD_HDR_INDEX_2)
                        return (KG_SCH_KN_IPV6FL2 | KG_SCH_KN_IPTOS_TC2);
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal IPv6 index"));
                    return 0;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
            }
        case(HEADER_TYPE_GRE):
            switch(field.gre)
            {
                case(NET_HEADER_FIELD_GRE_TYPE):
                    return KG_SCH_KN_GREPTYPE;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
           }
        case(HEADER_TYPE_MINENCAP):
            switch(field.minencap)
            {
                case(NET_HEADER_FIELD_MINENCAP_SRC_IP):
                    return KG_SCH_KN_IPSRC2;
                case(NET_HEADER_FIELD_MINENCAP_DST_IP):
                    return KG_SCH_KN_IPDST2;
                case(NET_HEADER_FIELD_MINENCAP_TYPE):
                    return KG_SCH_KN_PTYPE2;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
           }
        case(HEADER_TYPE_TCP):
            switch(field.tcp)
            {
                case(NET_HEADER_FIELD_TCP_PORT_SRC):
                    return KG_SCH_KN_L4PSRC;
                case(NET_HEADER_FIELD_TCP_PORT_DST):
                    return KG_SCH_KN_L4PDST;
                case(NET_HEADER_FIELD_TCP_FLAGS):
                    return KG_SCH_KN_TFLG;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
            }
        case(HEADER_TYPE_UDP):
            switch(field.udp)
            {
                case(NET_HEADER_FIELD_UDP_PORT_SRC):
                    return KG_SCH_KN_L4PSRC;
                case(NET_HEADER_FIELD_UDP_PORT_DST):
                    return KG_SCH_KN_L4PDST;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
            }
        case(HEADER_TYPE_IPSEC_AH):
            switch(field.ipsecAh)
            {
                case(NET_HEADER_FIELD_IPSEC_AH_SPI):
                    return KG_SCH_KN_IPSEC_SPI;
                case(NET_HEADER_FIELD_IPSEC_AH_NH):
                    return KG_SCH_KN_IPSEC_NH;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
            }
        case(HEADER_TYPE_IPSEC_ESP):
            switch(field.ipsecEsp)
            {
                case(NET_HEADER_FIELD_IPSEC_ESP_SPI):
                    return KG_SCH_KN_IPSEC_SPI;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
            }
        case(HEADER_TYPE_SCTP):
            switch(field.sctp)
            {
                case(NET_HEADER_FIELD_SCTP_PORT_SRC):
                    return KG_SCH_KN_L4PSRC;
                case(NET_HEADER_FIELD_SCTP_PORT_DST):
                    return KG_SCH_KN_L4PDST;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
            }
        case(HEADER_TYPE_DCCP):
            switch(field.dccp)
            {
                case(NET_HEADER_FIELD_DCCP_PORT_SRC):
                    return KG_SCH_KN_L4PSRC;
                case(NET_HEADER_FIELD_DCCP_PORT_DST):
                    return KG_SCH_KN_L4PDST;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
            }
        case(HEADER_TYPE_PPPoE):
            switch(field.pppoe)
            {
                case(NET_HEADER_FIELD_PPPoE_PID):
                    return KG_SCH_KN_PPPID;
                case(NET_HEADER_FIELD_PPPoE_SID):
                    return KG_SCH_KN_PPPSID;
                default:
                    REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
                    return 0;
            }
        default:
            REPORT_ERROR(MAJOR, E_NOT_SUPPORTED, ("Extraction not supported"));
            return 0;
    }
}


static uint8_t GetKnownFieldId(uint32_t bitMask)
{
    uint8_t cnt = 0;

    while (bitMask)
        if(bitMask & 0x80000000)
            break;
        else
        {
            cnt++;
            bitMask <<= 1;
        }
    return cnt;

}

static uint8_t GetExtractedOrMask(uint8_t bitOffset, bool fqid)
{
    uint8_t i, mask, numOfOnesToClear, walking1Mask = 1;

    /* bitOffset 1-7 --> mask 0x1-0x7F */
    if(bitOffset<8)
    {
        mask = 0;
        for(i = 0 ; i < bitOffset ; i++, walking1Mask <<= 1)
            mask |= walking1Mask;
    }
    else
    {
       mask = 0xFF;
       numOfOnesToClear = 0;
       if(fqid && bitOffset>24)
           /* bitOffset 25-31 --> mask 0xFE-0x80 */
           numOfOnesToClear = (uint8_t)(bitOffset-24);
       else
          /* bitOffset 9-15 --> mask 0xFE-0x80 */
          if(!fqid && bitOffset>8)
               numOfOnesToClear = (uint8_t)(bitOffset-8);
       for(i = 0 ; i < numOfOnesToClear ; i++, walking1Mask <<= 1)
           mask &= ~walking1Mask;
       /* bitOffset 8-24 for FQID, 8 for PP --> no mask (0xFF)*/
    }
    return mask;
}


t_Error FmPcdKgBuildClsPlanGrp(t_Handle h_FmPcd, t_FmPcdKgInterModuleClsPlanGrpParams *p_Grp, t_FmPcdKgInterModuleClsPlanSet *p_ClsPlanSet)
{
    t_FmPcd                         *p_FmPcd = (t_FmPcd*)h_FmPcd;
    t_FmPcdKgClsPlanGrp             *p_ClsPlanGrp;
    t_FmPcdIpcKgClsPlanParams       kgAlloc;
    t_Error                         err = E_OK;
    uint32_t                        oredVectors = 0;
    uint32_t                        intFlags;
    int                             i, j;

    if (p_Grp->numOfOptions >= FM_PCD_MAX_NUM_OF_OPTIONS(FM_PCD_MAX_NUM_OF_CLS_PLANS))
        RETURN_ERROR(MAJOR, E_INVALID_VALUE,("Too many classification plan basic options selected."));

    intFlags = FmPcdLock(p_FmPcd);

    /* find a new clsPlan group */
    for(i = 0;i<FM_MAX_NUM_OF_PORTS;i++)
        if(!p_FmPcd->p_FmPcdKg->clsPlanGrps[i].used)
            break;
    if(i== FM_MAX_NUM_OF_PORTS)
    {
        FmPcdUnlock(p_FmPcd, intFlags);
        RETURN_ERROR(MAJOR, E_FULL,("No classification plan groups available."));
    }
    p_FmPcd->p_FmPcdKg->clsPlanGrps[i].used = TRUE;
    p_Grp->clsPlanGrpId = (uint8_t)i;

    if(p_Grp->numOfOptions == 0)
        p_FmPcd->p_FmPcdKg->emptyClsPlanGrpId = (uint8_t)i;

    if (!TRY_LOCK(NULL, &p_FmPcd->p_FmPcdKg->clsPlanGrps[p_Grp->clsPlanGrpId].lock))
    {
        FmPcdUnlock(p_FmPcd, intFlags);
        return ERROR_CODE(E_BUSY);
    }
    FmPcdUnlock(p_FmPcd, intFlags);

    p_ClsPlanGrp = &p_FmPcd->p_FmPcdKg->clsPlanGrps[i];
    p_ClsPlanGrp->netEnvId = p_Grp->netEnvId;
    p_ClsPlanGrp->owners = 0;
    FmPcdSetClsPlanGrpId(p_FmPcd, p_Grp->netEnvId, p_Grp->clsPlanGrpId);
    FmPcdIncNetEnvOwners(p_FmPcd, p_Grp->netEnvId);

    p_ClsPlanGrp->sizeOfGrp = (uint16_t)(1<<p_Grp->numOfOptions);
    /* a minimal group of 8 is required */
    if(p_ClsPlanGrp->sizeOfGrp < CLS_PLAN_NUM_PER_GRP)
        p_ClsPlanGrp->sizeOfGrp = CLS_PLAN_NUM_PER_GRP;
    if(p_FmPcd->guestId == NCSW_MASTER_ID)
    {
        err = KgAllocClsPlanEntries(h_FmPcd, p_ClsPlanGrp->sizeOfGrp, p_FmPcd->guestId, &p_ClsPlanGrp->baseEntry);

        if(err)
        {
            RELEASE_LOCK(p_FmPcd->p_FmPcdKg->clsPlanGrps[p_Grp->clsPlanGrpId].lock);
            RETURN_ERROR(MINOR, E_INVALID_STATE, NO_MSG);
        }

    }
    else
    {
        t_FmPcdIpcMsg   msg;
        uint32_t        replyLength;
        t_FmPcdIpcReply reply;

        /* in GUEST_PARTITION, we use the IPC, to also set a private driver group if required */
        memset(&reply, 0, sizeof(reply));
        memset(&msg, 0, sizeof(msg));
        memset(&kgAlloc, 0, sizeof(kgAlloc));
        kgAlloc.guestId = p_FmPcd->guestId;
        kgAlloc.numOfClsPlanEntries = p_ClsPlanGrp->sizeOfGrp;
        msg.msgId = FM_PCD_ALLOC_KG_CLSPLAN;
        memcpy(msg.msgBody, &kgAlloc, sizeof(kgAlloc));
        replyLength = (sizeof(uint32_t) + sizeof(p_ClsPlanGrp->baseEntry));
        if ((err = XX_IpcSendMessage(p_FmPcd->h_IpcSession,
                                     (uint8_t*)&msg,
                                     sizeof(msg.msgId) + sizeof(kgAlloc),
                                     (uint8_t*)&reply,
                                     &replyLength,
                                     NULL,
                                     NULL)) != E_OK)
        {
            RELEASE_LOCK(p_FmPcd->p_FmPcdKg->clsPlanGrps[p_Grp->clsPlanGrpId].lock);
            RETURN_ERROR(MAJOR, err, NO_MSG);
        }

        if (replyLength != (sizeof(uint32_t) + sizeof(p_ClsPlanGrp->baseEntry)))
        {
            RELEASE_LOCK(p_FmPcd->p_FmPcdKg->clsPlanGrps[p_Grp->clsPlanGrpId].lock);
            RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("IPC reply length mismatch"));
        }
        if ((t_Error)reply.error != E_OK)
        {
            RELEASE_LOCK(p_FmPcd->p_FmPcdKg->clsPlanGrps[p_Grp->clsPlanGrpId].lock);
            RETURN_ERROR(MINOR, (t_Error)reply.error, NO_MSG);
        }

        p_ClsPlanGrp->baseEntry = *(uint8_t*)(reply.replyBody);
    }

    /* build classification plan entries parameters */
    p_ClsPlanSet->baseEntry = p_ClsPlanGrp->baseEntry;
    p_ClsPlanSet->numOfClsPlanEntries = p_ClsPlanGrp->sizeOfGrp;

    oredVectors = 0;
    for(i = 0; i<p_Grp->numOfOptions; i++)
    {
        oredVectors |= p_Grp->optVectors[i];
        /* save an array of used options - the indexes represent the power of 2 index */
        p_ClsPlanGrp->optArray[i] = p_Grp->options[i];
    }
    /* set the classification plan relevant entries so that all bits
     * relevant to the list of options is cleared
     */
    for(j = 0; j<p_ClsPlanGrp->sizeOfGrp; j++)
        p_ClsPlanSet->vectors[j] = ~oredVectors;

    for(i = 0; i<p_Grp->numOfOptions; i++)
    {
       /* option i got the place 2^i in the clsPlan array. all entries that
         * have bit i set, should have the vector bit cleared. So each option
         * has one location that it is exclusive (1,2,4,8...) and represent the
         * presence of that option only, and other locations that represent a
         * combination of options.
         * e.g:
         * If ethernet-BC is option 1 it gets entry 2 in the table. Entry 2
         * now represents a frame with ethernet-BC header - so the bit
         * representing ethernet-BC should be set and all other option bits
         * should be cleared.
         * Entries 2,3,6,7,10... also have ethernet-BC and therefore have bit
         * vector[1] set, but they also have other bits set:
         * 3=1+2, options 0 and 1
         * 6=2+4, options 1 and 2
         * 7=1+2+4, options 0,1,and 2
         * 10=2+8, options 1 and 3
         * etc.
         * */

        /* now for each option (i), we set their bits in all entries (j)
         * that contain bit 2^i.
         */
        for(j = 0; j<p_ClsPlanGrp->sizeOfGrp; j++)
        {
            if(j & (1<<i))
                p_ClsPlanSet->vectors[j] |= p_Grp->optVectors[i];
        }
    }

    RELEASE_LOCK(p_FmPcd->p_FmPcdKg->clsPlanGrps[p_Grp->clsPlanGrpId].lock);

    return E_OK;
}

void FmPcdKgDestroyClsPlanGrp(t_Handle h_FmPcd, uint8_t grpId)
{
    t_FmPcd                         *p_FmPcd = (t_FmPcd*)h_FmPcd;
    t_FmPcdIpcKgClsPlanParams       kgAlloc;
    t_Error                         err;
    t_FmPcdIpcMsg                   msg;
    uint32_t                        replyLength;
    t_FmPcdIpcReply                 reply;

    /* check that no port is bound to this clsPlan */
    if(p_FmPcd->p_FmPcdKg->clsPlanGrps[grpId].owners)
    {
        REPORT_ERROR(MINOR, E_INVALID_STATE, ("Trying to delete a clsPlan grp that has ports bound to"));
        return;
    }

    FmPcdDecNetEnvOwners(p_FmPcd, p_FmPcd->p_FmPcdKg->clsPlanGrps[grpId].netEnvId);

    /* free blocks */
    if(p_FmPcd->guestId == NCSW_MASTER_ID)
    {
        KgFreeClsPlanEntries(h_FmPcd,
                             p_FmPcd->p_FmPcdKg->clsPlanGrps[grpId].sizeOfGrp,
                             p_FmPcd->guestId,
                             p_FmPcd->p_FmPcdKg->clsPlanGrps[grpId].baseEntry);
    }
    else    /* in GUEST_PARTITION, we use the IPC, to also set a private driver group if required */
    {
        memset(&reply, 0, sizeof(reply));
        memset(&msg, 0, sizeof(msg));
        kgAlloc.guestId = p_FmPcd->guestId;
        kgAlloc.numOfClsPlanEntries = p_FmPcd->p_FmPcdKg->clsPlanGrps[grpId].sizeOfGrp;
        kgAlloc.clsPlanBase = p_FmPcd->p_FmPcdKg->clsPlanGrps[grpId].baseEntry;
        msg.msgId = FM_PCD_FREE_KG_CLSPLAN;
        memcpy(msg.msgBody, &kgAlloc, sizeof(kgAlloc));
        replyLength = sizeof(uint32_t);
        if ((err = XX_IpcSendMessage(p_FmPcd->h_IpcSession,
                                     (uint8_t*)&msg,
                                     sizeof(msg.msgId) + sizeof(kgAlloc),
                                     (uint8_t*)&reply,
                                     &replyLength,
                                     NULL,
                                     NULL)) != E_OK)
        {
            REPORT_ERROR(MINOR, err, NO_MSG);
            return;
        }
        if (replyLength != sizeof(uint32_t))
        {
            REPORT_ERROR(MAJOR, E_INVALID_VALUE, ("IPC reply length mismatch"));
            return;
        }
        if((t_Error)reply.error != E_OK)
        {
            REPORT_ERROR(MAJOR, E_INVALID_STATE, ("Free KG clsPlan failed"));
            return;
        }
    }

    if(grpId == p_FmPcd->p_FmPcdKg->emptyClsPlanGrpId)
        p_FmPcd->p_FmPcdKg->emptyClsPlanGrpId = ILLEGAL_CLS_PLAN;
    /* clear clsPlan driver structure */
    memset(&p_FmPcd->p_FmPcdKg->clsPlanGrps[grpId], 0, sizeof(t_FmPcdKgClsPlanGrp));
}

t_Error FmPcdKgBuildBindPortToSchemes(t_Handle h_FmPcd , t_FmPcdKgInterModuleBindPortToSchemes *p_BindPort, uint32_t *p_SpReg, bool add)
{
    t_FmPcd                 *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint32_t                j, schemesPerPortVector = 0;
    t_FmPcdKgScheme         *p_Scheme;
    uint8_t                 i, relativeSchemeId;
    uint32_t                tmp, walking1Mask;
    uint8_t                 swPortIndex = 0;

    SANITY_CHECK_RETURN_ERROR(p_FmPcd, E_INVALID_HANDLE);
    SANITY_CHECK_RETURN_ERROR(p_FmPcd->p_FmPcdKg, E_INVALID_HANDLE);
    SANITY_CHECK_RETURN_ERROR(!p_FmPcd->p_FmPcdDriverParam, E_INVALID_STATE);

    /* for each scheme */
    for(i = 0; i<p_BindPort->numOfSchemes; i++)
    {
        relativeSchemeId = FmPcdKgGetRelativeSchemeId(p_FmPcd, p_BindPort->schemesIds[i]);
        if(relativeSchemeId >= FM_PCD_KG_NUM_OF_SCHEMES)
            RETURN_ERROR(MAJOR, E_NOT_IN_RANGE, NO_MSG);

        if(add)
        {
            if (!FmPcdKgIsSchemeValidSw(h_FmPcd, relativeSchemeId))
                RETURN_ERROR(MAJOR, E_INVALID_STATE, ("Requested scheme is invalid."));

            p_Scheme = &p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId];
            /* check netEnvId  of the port against the scheme netEnvId */
            if((p_Scheme->netEnvId != p_BindPort->netEnvId) && (p_Scheme->netEnvId != ILLEGAL_NETENV))
                RETURN_ERROR(MAJOR, E_INVALID_STATE, ("Port may not be bound to requested scheme - differ in netEnvId"));

            /* if next engine is private port policer profile, we need to check that it is valid */
            HW_PORT_ID_TO_SW_PORT_INDX(swPortIndex, p_BindPort->hardwarePortId);
            if(p_Scheme->nextRelativePlcrProfile)
            {
                for(j = 0;j<p_Scheme->numOfProfiles;j++)
                {
                    ASSERT_COND(p_FmPcd->p_FmPcdPlcr->portsMapping[swPortIndex].h_FmPort);
                    if(p_Scheme->relativeProfileId+j >= p_FmPcd->p_FmPcdPlcr->portsMapping[swPortIndex].numOfProfiles)
                        RETURN_ERROR(MAJOR, E_INVALID_STATE, ("Relative profile not in range"));
                     if(!FmPcdPlcrIsProfileValid(p_FmPcd, (uint16_t)(p_FmPcd->p_FmPcdPlcr->portsMapping[swPortIndex].profilesBase + p_Scheme->relativeProfileId + j)))
                        RETURN_ERROR(MINOR, E_INVALID_STATE, ("Relative profile not valid."));
                }
            }
            if(!p_BindPort->useClsPlan)
            {
                /* if this port does not use clsPlan, it may not be bound to schemes with units that contain
                cls plan options. Schemes that are used only directly, should not be checked.
                it also may not be bound to schemes that go to CC with units that are options  - so we OR
                the match vector and the grpBits (= ccUnits) */
                if ((p_Scheme->matchVector != SCHEME_ALWAYS_DIRECT) || p_Scheme->ccUnits)
                {
                    walking1Mask = 0x80000000;
                    tmp = (p_Scheme->matchVector == SCHEME_ALWAYS_DIRECT)? 0:p_Scheme->matchVector;
                    tmp |= p_Scheme->ccUnits;
                    while (tmp)
                    {
                        if(tmp & walking1Mask)
                        {
                            tmp &= ~walking1Mask;
                            if(!PcdNetEnvIsUnitWithoutOpts(p_FmPcd, p_Scheme->netEnvId, walking1Mask))
                                RETURN_ERROR(MAJOR, E_INVALID_STATE, ("Port (without clsPlan) may not be bound to requested scheme - uses clsPlan options"));
                        }
                        walking1Mask >>= 1;
                    }
                }
            }
        }
        /* build vector */
        schemesPerPortVector |= 1 << (31 - p_BindPort->schemesIds[i]);
    }

    *p_SpReg = schemesPerPortVector;

    return E_OK;
}

void FmPcdKgIncSchemeOwners(t_Handle h_FmPcd , t_FmPcdKgInterModuleBindPortToSchemes *p_BindPort)
{
    t_FmPcd             *p_FmPcd = (t_FmPcd*)h_FmPcd;
    int             i;
    t_FmPcdKgScheme *p_Scheme;

    /* for each scheme - update owners counters */
    for(i = 0; i<p_BindPort->numOfSchemes; i++)
    {
        p_Scheme = &p_FmPcd->p_FmPcdKg->schemes[p_BindPort->schemesIds[i]];

        /* increment owners number */
        p_Scheme->owners++;
    }
}

void FmPcdKgDecSchemeOwners(t_Handle h_FmPcd , t_FmPcdKgInterModuleBindPortToSchemes *p_BindPort)
{
    t_FmPcd             *p_FmPcd = (t_FmPcd*)h_FmPcd;
    int             i;
    t_FmPcdKgScheme *p_Scheme;

    /* for each scheme - update owners counters */
    for(i = 0; i<p_BindPort->numOfSchemes; i++)
    {
        p_Scheme = &p_FmPcd->p_FmPcdKg->schemes[p_BindPort->schemesIds[i]];

        /* increment owners number */
        ASSERT_COND(p_Scheme->owners);
        p_Scheme->owners--;
    }
}

static t_Error KgWriteSp(t_FmPcd *p_FmPcd, uint8_t hardwarePortId, uint32_t spReg, bool add)
{
    t_FmPcdKgPortConfigRegs *p_FmPcdKgPortRegs;
    uint32_t                tmpKgarReg = 0, tmpKgpeSp, intFlags;
    t_Error                 err = E_OK;

    if (p_FmPcd->h_Hc)
        return FmHcKgWriteSp(p_FmPcd->h_Hc, hardwarePortId, spReg, add);

    p_FmPcdKgPortRegs = &p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.portRegs;

    tmpKgarReg = FmPcdKgBuildReadPortSchemeBindActionReg(hardwarePortId);
    intFlags = FmPcdLock(p_FmPcd);
    err = WriteKgarWait(p_FmPcd, tmpKgarReg);
    if(err)
    {
        FmPcdUnlock(p_FmPcd, intFlags);
        RETURN_ERROR(MINOR, err, NO_MSG);
    }

    tmpKgpeSp = GET_UINT32(p_FmPcdKgPortRegs->kgoe_sp);

    if(add)
        tmpKgpeSp |= spReg;
    else /* clear */
        tmpKgpeSp &= ~spReg;

    WRITE_UINT32(p_FmPcdKgPortRegs->kgoe_sp, tmpKgpeSp);

    tmpKgarReg = FmPcdKgBuildWritePortSchemeBindActionReg(hardwarePortId);

    err = WriteKgarWait(p_FmPcd, tmpKgarReg);
    FmPcdUnlock(p_FmPcd, intFlags);
    return err;
}

static t_Error KgWriteCpp(t_FmPcd *p_FmPcd, uint8_t hardwarePortId, uint32_t cppReg)
{
    t_FmPcdKgPortConfigRegs *p_FmPcdKgPortRegs;
    uint32_t                tmpKgarReg, intFlags;
    t_Error                 err;

    if (p_FmPcd->h_Hc)
        return FmHcKgWriteCpp(p_FmPcd->h_Hc, hardwarePortId, cppReg);

    p_FmPcdKgPortRegs = &p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.portRegs;
    intFlags = FmPcdLock(p_FmPcd);
    WRITE_UINT32(p_FmPcdKgPortRegs->kgoe_cpp, cppReg);

    tmpKgarReg = FmPcdKgBuildWritePortClsPlanBindActionReg(hardwarePortId);
    err = WriteKgarWait(p_FmPcd, tmpKgarReg);
    FmPcdUnlock(p_FmPcd, intFlags);

    return err;
}

static void FmPcdKgUnbindPortToClsPlanGrp(t_FmPcd *p_FmPcd, uint8_t hardwarePortId)
{
    KgWriteCpp(p_FmPcd, hardwarePortId, 0);
}

static t_Error KgBindPortToClsPlanGrp(t_FmPcd *p_FmPcd, uint8_t hardwarePortId, uint8_t clsPlanGrpId)
{
    uint32_t                tmpKgpeCpp = 0;

    tmpKgpeCpp = FmPcdKgBuildCppReg(p_FmPcd, clsPlanGrpId);
    return KgWriteCpp(p_FmPcd, hardwarePortId, tmpKgpeCpp);
}

t_Error FmPcdKgBindPortToSchemes(t_Handle h_FmPcd , t_FmPcdKgInterModuleBindPortToSchemes  *p_SchemeBind)
{
    t_FmPcd                 *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint32_t                spReg;
    t_Error                 err = E_OK;

    err = FmPcdKgBuildBindPortToSchemes(h_FmPcd, p_SchemeBind, &spReg, TRUE);
    if(err)
        RETURN_ERROR(MAJOR, err, NO_MSG);

    err = KgWriteSp(p_FmPcd, p_SchemeBind->hardwarePortId, spReg, TRUE);
    if(err)
        RETURN_ERROR(MAJOR, err, NO_MSG);

    FmPcdKgIncSchemeOwners(h_FmPcd, p_SchemeBind);

    return E_OK;
}

t_Error FmPcdKgUnbindPortToSchemes(t_Handle h_FmPcd ,  t_FmPcdKgInterModuleBindPortToSchemes *p_SchemeBind)
{
    t_FmPcd                 *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint32_t                spReg;
    t_Error                 err = E_OK;

    err = FmPcdKgBuildBindPortToSchemes(h_FmPcd, p_SchemeBind, &spReg, FALSE);
    if(err)
        RETURN_ERROR(MAJOR, err, NO_MSG);

    err = KgWriteSp(p_FmPcd, p_SchemeBind->hardwarePortId, spReg, FALSE);
    if(err)
        RETURN_ERROR(MAJOR, err, NO_MSG);

    FmPcdKgDecSchemeOwners(h_FmPcd, p_SchemeBind);

    return E_OK;
}

bool     FmPcdKgIsSchemeValidSw(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

    return p_FmPcd->p_FmPcdKg->schemes[schemeId].valid;
}

bool     KgIsSchemeAlwaysDirect(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

    if(p_FmPcd->p_FmPcdKg->schemes[schemeId].matchVector == SCHEME_ALWAYS_DIRECT)
        return TRUE;
    else
        return FALSE;
}

t_Error  FmPcdKgAllocSchemes(t_Handle h_FmPcd, uint8_t numOfSchemes, uint8_t guestId, uint8_t *p_SchemesIds)
{
    t_FmPcd             *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint32_t            intFlags;
    uint8_t             i,j;

    SANITY_CHECK_RETURN_ERROR(p_FmPcd, E_INVALID_HANDLE);
    SANITY_CHECK_RETURN_ERROR(p_FmPcd->p_FmPcdKg, E_INVALID_HANDLE);

    intFlags = FmPcdLock(p_FmPcd);
    for(j=0,i=0;i<FM_PCD_KG_NUM_OF_SCHEMES && j<numOfSchemes;i++)
    {
        if(!p_FmPcd->p_FmPcdKg->schemesMng[i].allocated)
        {
            p_FmPcd->p_FmPcdKg->schemesMng[i].allocated = TRUE;
            p_FmPcd->p_FmPcdKg->schemesMng[i].ownerId = guestId;
            p_SchemesIds[j] = i;
            j++;
        }
    }

    if (j != numOfSchemes)
    {
        /* roll back */
        for(j--; j; j--)
        {
            p_FmPcd->p_FmPcdKg->schemesMng[p_SchemesIds[j]].allocated = FALSE;
            p_FmPcd->p_FmPcdKg->schemesMng[p_SchemesIds[j]].ownerId = 0;
            p_SchemesIds[j] = 0;
        }
        FmPcdUnlock(p_FmPcd, intFlags);
        RETURN_ERROR(MAJOR, E_NOT_AVAILABLE, ("No schemes found"));
    }
    FmPcdUnlock(p_FmPcd, intFlags);

    return E_OK;
}

t_Error  FmPcdKgFreeSchemes(t_Handle h_FmPcd, uint8_t numOfSchemes, uint8_t guestId, uint8_t *p_SchemesIds)
{
    t_FmPcd             *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint32_t            intFlags;
    uint8_t             i;

    SANITY_CHECK_RETURN_ERROR(p_FmPcd, E_INVALID_HANDLE);
    SANITY_CHECK_RETURN_ERROR(p_FmPcd->p_FmPcdKg, E_INVALID_HANDLE);

    intFlags = FmPcdLock(p_FmPcd);

    for(i=0;i<numOfSchemes;i++)
    {
        if(!p_FmPcd->p_FmPcdKg->schemesMng[p_SchemesIds[i]].allocated)
        {
            FmPcdUnlock(p_FmPcd, intFlags);
            RETURN_ERROR(MAJOR, E_INVALID_STATE, ("Scheme was not previously allocated"));
        }
        if(p_FmPcd->p_FmPcdKg->schemesMng[p_SchemesIds[i]].ownerId != guestId)
        {
            FmPcdUnlock(p_FmPcd, intFlags);
            RETURN_ERROR(MAJOR, E_INVALID_STATE, ("Scheme is not owned by caller. "));
        }
        p_FmPcd->p_FmPcdKg->schemesMng[p_SchemesIds[i]].allocated = FALSE;
        p_FmPcd->p_FmPcdKg->schemesMng[p_SchemesIds[i]].ownerId = 0;
    }

    FmPcdUnlock(p_FmPcd, intFlags);
    return E_OK;
}

t_Error  KgAllocClsPlanEntries(t_Handle h_FmPcd, uint16_t numOfClsPlanEntries, uint8_t guestId, uint8_t *p_First)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint32_t    intFlags;
    uint8_t     numOfBlocks, blocksFound=0, first=0;
    uint8_t     i, j;

    intFlags = FmPcdLock(p_FmPcd);

    if(!numOfClsPlanEntries)
    {
        FmPcdUnlock(p_FmPcd, intFlags);
        return E_OK;
    }

    if ((numOfClsPlanEntries % CLS_PLAN_NUM_PER_GRP) || (!POWER_OF_2(numOfClsPlanEntries)))
    {
        FmPcdUnlock(p_FmPcd, intFlags);
        RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("numOfClsPlanEntries must be a power of 2 and divisible by 8"));
    }

    numOfBlocks =  (uint8_t)(numOfClsPlanEntries/CLS_PLAN_NUM_PER_GRP);

    /* try to find consequent blocks */
    first = 0;
    for(i=0;i<FM_PCD_MAX_NUM_OF_CLS_PLANS/CLS_PLAN_NUM_PER_GRP;)
    {
        if(!p_FmPcd->p_FmPcdKg->clsPlanBlocksMng[i].allocated)
        {
            blocksFound++;
            i++;
            if(blocksFound == numOfBlocks)
                break;
        }
        else
        {
            blocksFound = 0;
            /* advance i to the next aligned address */
            first = i = (uint8_t)(first + numOfBlocks);
        }
    }

    if(blocksFound == numOfBlocks)
    {
        *p_First = (uint8_t)(first*CLS_PLAN_NUM_PER_GRP);
        for(j = first; j<first + numOfBlocks; j++)
        {
            p_FmPcd->p_FmPcdKg->clsPlanBlocksMng[j].allocated = TRUE;
            p_FmPcd->p_FmPcdKg->clsPlanBlocksMng[j].ownerId = guestId;
        }
        FmPcdUnlock(p_FmPcd, intFlags);

        return E_OK;
    }
    else
    {
        FmPcdUnlock(p_FmPcd, intFlags);
        RETURN_ERROR(MINOR, E_FULL, ("No recources for clsPlan"));
    }
}

void  KgFreeClsPlanEntries(t_Handle h_FmPcd, uint16_t numOfClsPlanEntries, uint8_t guestId, uint8_t base)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint32_t    intFlags;
    uint8_t     numOfBlocks;
    uint8_t     i, baseBlock;

    UNUSED( guestId);

    intFlags = FmPcdLock(p_FmPcd);

    numOfBlocks =  (uint8_t)(numOfClsPlanEntries/CLS_PLAN_NUM_PER_GRP);
    ASSERT_COND(!(base%CLS_PLAN_NUM_PER_GRP));

    baseBlock = (uint8_t)(base/CLS_PLAN_NUM_PER_GRP);
    for(i=baseBlock;i<baseBlock+numOfBlocks;i++)
    {
        ASSERT_COND(p_FmPcd->p_FmPcdKg->clsPlanBlocksMng[i].allocated);
        ASSERT_COND(guestId == p_FmPcd->p_FmPcdKg->clsPlanBlocksMng[i].ownerId);
        p_FmPcd->p_FmPcdKg->clsPlanBlocksMng[i].allocated = FALSE;
        p_FmPcd->p_FmPcdKg->clsPlanBlocksMng[i].ownerId = 0;
    }
    FmPcdUnlock(p_FmPcd, intFlags);
}

void KgEnable(t_FmPcd *p_FmPcd)
{
    t_FmPcdKgRegs               *p_Regs = p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs;

    ASSERT_COND(FmIsMaster(p_FmPcd->h_Fm));
    WRITE_UINT32(p_Regs->kggcr,GET_UINT32(p_Regs->kggcr) | FM_PCD_KG_KGGCR_EN);
}

void KgDisable(t_FmPcd *p_FmPcd)
{
    t_FmPcdKgRegs               *p_Regs = p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs;

    ASSERT_COND(FmIsMaster(p_FmPcd->h_Fm));
    WRITE_UINT32(p_Regs->kggcr,GET_UINT32(p_Regs->kggcr) & ~FM_PCD_KG_KGGCR_EN);
}

void KgSetClsPlan(t_Handle h_FmPcd, t_FmPcdKgInterModuleClsPlanSet *p_Set)
{
    t_FmPcd                 *p_FmPcd = (t_FmPcd*)h_FmPcd;
    t_FmPcdKgClsPlanRegs    *p_FmPcdKgPortRegs;
    uint32_t                tmpKgarReg=0, intFlags;
    uint16_t                i, j;

    SANITY_CHECK_RETURN(p_FmPcd, E_INVALID_HANDLE);
    SANITY_CHECK_RETURN(p_FmPcd->p_FmPcdKg, E_INVALID_HANDLE);

    ASSERT_COND(FmIsMaster(p_FmPcd->h_Fm));
    p_FmPcdKgPortRegs = &p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.clsPlanRegs;

    intFlags = FmPcdLock(p_FmPcd);
    for(i=p_Set->baseEntry;i<p_Set->baseEntry+p_Set->numOfClsPlanEntries;i+=8)
    {
        tmpKgarReg = FmPcdKgBuildWriteClsPlanBlockActionReg((uint8_t)(i / CLS_PLAN_NUM_PER_GRP));

        for (j = i; j < i+8; j++)
        {
            ASSERT_COND(IN_RANGE(0, (j - p_Set->baseEntry), FM_PCD_MAX_NUM_OF_CLS_PLANS-1));
            WRITE_UINT32(p_FmPcdKgPortRegs->kgcpe[j % CLS_PLAN_NUM_PER_GRP],p_Set->vectors[j - p_Set->baseEntry]);
        }

        if(WriteKgarWait(p_FmPcd, tmpKgarReg) != E_OK)
        {
            REPORT_ERROR(MAJOR, E_INVALID_STATE, ("WriteKgarWait FAILED"));
            return;
        }
    }
    FmPcdUnlock(p_FmPcd, intFlags);
}

static void PcdKgErrorException(t_Handle h_FmPcd)
{
    t_FmPcd                 *p_FmPcd = (t_FmPcd *)h_FmPcd;
    uint32_t                event, force, schemeIndexes = 0,index = 0, mask = 0;

    ASSERT_COND(FmIsMaster(p_FmPcd->h_Fm));
    event = GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->kgeer);
    mask = GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->kgeeer);

    schemeIndexes = GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->kgseer);
    schemeIndexes &= GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->kgseeer);

    event &= mask;

    /* clear the forced events */
    force = GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->kgfeer);
    if(force & event)
        WRITE_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->kgfeer, force & ~event);

    WRITE_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->kgeer, event);
    WRITE_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->kgseer, schemeIndexes);

    if(event & FM_PCD_KG_DOUBLE_ECC)
        p_FmPcd->f_Exception(p_FmPcd->h_App,e_FM_PCD_KG_EXCEPTION_DOUBLE_ECC);
    if(event & FM_PCD_KG_KEYSIZE_OVERFLOW)
    {
        if(schemeIndexes)
        {
            while(schemeIndexes)
            {
                if(schemeIndexes & 0x1)
                    p_FmPcd->f_FmPcdIndexedException(p_FmPcd->h_App,e_FM_PCD_KG_EXCEPTION_KEYSIZE_OVERFLOW, (uint16_t)(31 - index));
                schemeIndexes >>= 1;
                index+=1;
            }
        }
        else /* this should happen only when interrupt is forced. */
            p_FmPcd->f_Exception(p_FmPcd->h_App,e_FM_PCD_KG_EXCEPTION_KEYSIZE_OVERFLOW);
    }
}

static t_Error KgInitGuest(t_FmPcd *p_FmPcd)
{
    t_Error                     err = E_OK;
    t_FmPcdIpcKgSchemesParams   kgAlloc;
    uint32_t                    replyLength;
    t_FmPcdIpcReply             reply;
    t_FmPcdIpcMsg               msg;

    ASSERT_COND(p_FmPcd->guestId != NCSW_MASTER_ID);

    /* in GUEST_PARTITION, we use the IPC  */
    memset(&reply, 0, sizeof(reply));
    memset(&msg, 0, sizeof(msg));
    memset(&kgAlloc, 0, sizeof(t_FmPcdIpcKgSchemesParams));
    kgAlloc.numOfSchemes = p_FmPcd->p_FmPcdKg->numOfSchemes;
    kgAlloc.guestId = p_FmPcd->guestId;
    msg.msgId = FM_PCD_ALLOC_KG_SCHEMES;
    memcpy(msg.msgBody, &kgAlloc, sizeof(kgAlloc));
    replyLength = sizeof(uint32_t) + p_FmPcd->p_FmPcdKg->numOfSchemes*sizeof(uint8_t);
    if ((err = XX_IpcSendMessage(p_FmPcd->h_IpcSession,
                                 (uint8_t*)&msg,
                                 sizeof(msg.msgId) + sizeof(kgAlloc),
                                 (uint8_t*)&reply,
                                 &replyLength,
                                 NULL,
                                 NULL)) != E_OK)
        RETURN_ERROR(MAJOR, err, NO_MSG);
    if(replyLength != (sizeof(uint32_t) + p_FmPcd->p_FmPcdKg->numOfSchemes*sizeof(uint8_t)))
        RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("IPC reply length mismatch"));
    memcpy(p_FmPcd->p_FmPcdKg->schemesIds, (uint8_t*)(reply.replyBody),p_FmPcd->p_FmPcdKg->numOfSchemes*sizeof(uint8_t));

    return (t_Error)reply.error;
}

static t_Error KgInitMaster(t_FmPcd *p_FmPcd)
{
    t_Error                     err = E_OK;
    t_FmPcdKgRegs               *p_Regs = p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs;
    int                         i;
    uint8_t                     hardwarePortId = 0;
    uint32_t                    tmpReg;

    ASSERT_COND(p_FmPcd->guestId == NCSW_MASTER_ID);

    /**********************KGEER******************/
    WRITE_UINT32(p_Regs->kgeer, (FM_PCD_KG_DOUBLE_ECC | FM_PCD_KG_KEYSIZE_OVERFLOW));
    /**********************KGEER******************/

    /**********************KGEEER******************/
    tmpReg = 0;
    if(p_FmPcd->exceptions & FM_PCD_EX_KG_DOUBLE_ECC)
    {
        FmEnableRamsEcc(p_FmPcd->h_Fm);
        tmpReg |= FM_PCD_KG_DOUBLE_ECC;
    }
    if(p_FmPcd->exceptions & FM_PCD_EX_KG_KEYSIZE_OVERFLOW)
        tmpReg |= FM_PCD_KG_KEYSIZE_OVERFLOW;
    WRITE_UINT32(p_Regs->kgeeer,tmpReg);
    /**********************KGEEER******************/

    /**********************KGFDOR******************/
    WRITE_UINT32(p_Regs->kgfdor,0);
    /**********************KGFDOR******************/

    /**********************KGGDV0R******************/
    WRITE_UINT32(p_Regs->kggdv0r,0);
    /**********************KGGDV0R******************/

    /**********************KGGDV1R******************/
    WRITE_UINT32(p_Regs->kggdv1r,0);
    /**********************KGGDV1R******************/

    /**********************KGGCR******************/
    WRITE_UINT32(p_Regs->kggcr, NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME);
    /**********************KGGCR******************/

    /* register even if no interrupts enabled, to allow future enablement */
    FmRegisterIntr(p_FmPcd->h_Fm, e_FM_MOD_KG, 0, e_FM_INTR_TYPE_ERR, PcdKgErrorException, p_FmPcd);

    /* clear binding between ports to schemes so that all ports are not bound to any schemes */
    for (i=0;i<FM_MAX_NUM_OF_PORTS;i++)
    {
        SW_PORT_INDX_TO_HW_PORT_ID(hardwarePortId, i);

        err = KgWriteSp(p_FmPcd, hardwarePortId, 0xffffffff, FALSE);
        if(err)
            RETURN_ERROR(MINOR, err, NO_MSG);

        err = KgWriteCpp(p_FmPcd, hardwarePortId, 0);
        if(err)
            RETURN_ERROR(MINOR, err, NO_MSG);
    }

    /* enable and enable all scheme interrupts */
    WRITE_UINT32(p_Regs->kgseer, 0xFFFFFFFF);
    WRITE_UINT32(p_Regs->kgseeer, 0xFFFFFFFF);

    if(p_FmPcd->p_FmPcdKg->numOfSchemes)
    {
        err = FmPcdKgAllocSchemes(p_FmPcd,
                                  p_FmPcd->p_FmPcdKg->numOfSchemes,
                                  p_FmPcd->guestId,
                                  p_FmPcd->p_FmPcdKg->schemesIds);
        if(err)
            RETURN_ERROR(MINOR, err, NO_MSG);
    }

    return E_OK;
}


/****************************************/
/*  API routines                        */
/****************************************/
t_Error FM_PCD_KgSetAdditionalDataAfterParsing(t_Handle h_FmPcd, uint8_t payloadOffset)
{
   t_FmPcd              *p_FmPcd = (t_FmPcd*)h_FmPcd;
   t_FmPcdKgRegs        *p_Regs;

    SANITY_CHECK_RETURN_ERROR(p_FmPcd, E_INVALID_HANDLE);
    SANITY_CHECK_RETURN_ERROR(!p_FmPcd->p_FmPcdDriverParam, E_NULL_POINTER);
    SANITY_CHECK_RETURN_ERROR(p_FmPcd->p_FmPcdKg, E_NULL_POINTER);
    SANITY_CHECK_RETURN_ERROR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs, E_NULL_POINTER);

    p_Regs = p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs;
    if(!FmIsMaster(p_FmPcd->h_Fm))
        RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, ("FM_PCD_KgSetAdditionalDataAfterParsing - guest mode!"));

/* not needed
    if(payloadOffset > 256)
        RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("data exatraction offset from parseing end can not be more than 256"));
*/

    WRITE_UINT32(p_Regs->kgfdor,payloadOffset);

    return E_OK;
}

t_Error FM_PCD_KgSetDfltValue(t_Handle h_FmPcd, uint8_t valueId, uint32_t value)
{
   t_FmPcd              *p_FmPcd = (t_FmPcd*)h_FmPcd;
   t_FmPcdKgRegs        *p_Regs;

    SANITY_CHECK_RETURN_ERROR(p_FmPcd, E_INVALID_HANDLE);
    SANITY_CHECK_RETURN_ERROR(((valueId == 0) || (valueId == 1)), E_INVALID_VALUE);
    SANITY_CHECK_RETURN_ERROR(!p_FmPcd->p_FmPcdDriverParam, E_NULL_POINTER);
    SANITY_CHECK_RETURN_ERROR(p_FmPcd->p_FmPcdKg, E_NULL_POINTER);
    SANITY_CHECK_RETURN_ERROR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs, E_NULL_POINTER);

    p_Regs = p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs;

    if(!FmIsMaster(p_FmPcd->h_Fm))
        RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, ("FM_PCD_KgSetDfltValue - guest mode!"));

    if(valueId == 0)
        WRITE_UINT32(p_Regs->kggdv0r,value);
    else
        WRITE_UINT32(p_Regs->kggdv1r,value);
    return E_OK;
}

#if (defined(DEBUG_ERRORS) && (DEBUG_ERRORS > 0))
t_Error FM_PCD_KgDumpRegs(t_Handle h_FmPcd)
{
    t_FmPcd             *p_FmPcd = (t_FmPcd*)h_FmPcd;
    int                 i = 0, j = 0;
    uint8_t             hardwarePortId = 0;
    uint32_t            tmpKgarReg, intFlags;
    t_Error             err = E_OK;
    t_FmPcdIpcMsg       msg;

    DECLARE_DUMP;

    SANITY_CHECK_RETURN_ERROR(p_FmPcd, E_INVALID_HANDLE);
    SANITY_CHECK_RETURN_ERROR(p_FmPcd->p_FmPcdKg, E_INVALID_HANDLE);
    SANITY_CHECK_RETURN_ERROR(!p_FmPcd->p_FmPcdDriverParam, E_INVALID_STATE);

    if(p_FmPcd->guestId != NCSW_MASTER_ID)
    {
        memset(&msg, 0, sizeof(msg));
        msg.msgId = FM_PCD_KG_DUMP_REGS;
        return XX_IpcSendMessage(p_FmPcd->h_IpcSession,
                                 (uint8_t*)&msg,
                                 sizeof(msg.msgId),
                                 NULL,
                                 NULL,
                                 NULL,
                                 NULL);
    }
    DUMP_SUBTITLE(("\n"));
    DUMP_TITLE(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs, ("FmPcdKgRegs Regs"));

    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kggcr);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kgeer);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kgeeer);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kgseer);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kgseeer);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kggsr);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kgtpc);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kgserc);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kgfdor);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kggdv0r);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kggdv1r);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kgfer);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kgfeer);
    DUMP_VAR(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs,kgar);

    DUMP_SUBTITLE(("\n"));
    intFlags = FmPcdLock(p_FmPcd);
    for(j = 0;j<FM_PCD_KG_NUM_OF_SCHEMES;j++)
    {
        tmpKgarReg = FmPcdKgBuildReadSchemeActionReg((uint8_t)j);
        if(WriteKgarWait(p_FmPcd, tmpKgarReg) != E_OK)
            RETURN_ERROR(MAJOR, E_INVALID_STATE, NO_MSG);

        DUMP_TITLE(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs, ("FmPcdKgIndirectAccessSchemeRegs Scheme %d Regs", j));

        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_mode);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_ekfc);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_ekdv);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_bmch);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_bmcl);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_fqb);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_hc);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_ppc);

        DUMP_TITLE(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs.kgse_gec, ("kgse_gec"));
        DUMP_SUBSTRUCT_ARRAY(i, FM_PCD_KG_NUM_OF_GENERIC_REGS)
        {
            DUMP_MEMORY(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs.kgse_gec[i], sizeof(uint32_t));
        }

        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_spc);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_dv0);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_dv1);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_ccbs);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs,kgse_mv);
    }
    DUMP_SUBTITLE(("\n"));

    for (i=0;i<FM_MAX_NUM_OF_PORTS;i++)
    {
        SW_PORT_INDX_TO_HW_PORT_ID(hardwarePortId, i);

        tmpKgarReg = FmPcdKgBuildReadPortSchemeBindActionReg(hardwarePortId);

        err = WriteKgarWait(p_FmPcd, tmpKgarReg);
        if(err)
            RETURN_ERROR(MINOR, err, NO_MSG);

        DUMP_TITLE(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.portRegs, ("FmPcdKgIndirectAccessPortRegs PCD Port %d regs", hardwarePortId));

        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.portRegs, kgoe_sp);
        DUMP_VAR(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.portRegs, kgoe_cpp);
    }

    DUMP_SUBTITLE(("\n"));
    for(j=0;j<FM_PCD_MAX_NUM_OF_CLS_PLANS/CLS_PLAN_NUM_PER_GRP;j++)
    {
        DUMP_TITLE(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.clsPlanRegs, ("FmPcdKgIndirectAccessClsPlanRegs Regs group %d", j));
        DUMP_TITLE(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.clsPlanRegs.kgcpe, ("kgcpe"));

        tmpKgarReg = FmPcdKgBuildReadClsPlanBlockActionReg((uint8_t)j);
        err = WriteKgarWait(p_FmPcd, tmpKgarReg);
        if(err)
            RETURN_ERROR(MINOR, err, NO_MSG);
        DUMP_SUBSTRUCT_ARRAY(i, 8)
            DUMP_MEMORY(&p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.clsPlanRegs.kgcpe[i], sizeof(uint32_t));
    }
    FmPcdUnlock(p_FmPcd, intFlags);

    return E_OK;
}
#endif /* (defined(DEBUG_ERRORS) && ... */

t_Handle KgConfig( t_FmPcd *p_FmPcd, t_FmPcdParams *p_FmPcdParams)
{
    t_FmPcdKg   *p_FmPcdKg;

    UNUSED(p_FmPcd);

    if (p_FmPcdParams->numOfSchemes > FM_PCD_KG_NUM_OF_SCHEMES)
    {
        REPORT_ERROR(MAJOR, E_INVALID_VALUE,
                     ("numOfSchemes should not exceed %d", FM_PCD_KG_NUM_OF_SCHEMES));
        return NULL;
    }

    p_FmPcdKg = (t_FmPcdKg *)XX_Malloc(sizeof(t_FmPcdKg));
    if (!p_FmPcdKg)
    {
        REPORT_ERROR(MAJOR, E_NO_MEMORY, ("FM Keygen allocation FAILED"));
        return NULL;
    }
    memset(p_FmPcdKg, 0, sizeof(t_FmPcdKg));

    if(FmIsMaster(p_FmPcd->h_Fm))
    {
        p_FmPcdKg->p_FmPcdKgRegs  = (t_FmPcdKgRegs *)UINT_TO_PTR(FmGetPcdKgBaseAddr(p_FmPcdParams->h_Fm));
        p_FmPcd->exceptions |= DEFAULT_fmPcdKgErrorExceptions;
    }

    p_FmPcdKg->numOfSchemes = p_FmPcdParams->numOfSchemes;
    if((p_FmPcd->guestId == NCSW_MASTER_ID) && !p_FmPcdKg->numOfSchemes)
    {
        p_FmPcdKg->numOfSchemes = FM_PCD_KG_NUM_OF_SCHEMES;
        DBG(WARNING, ("numOfSchemes was defined 0 by user, re-defined by driver to FM_PCD_KG_NUM_OF_SCHEMES"));
    }

    p_FmPcdKg->emptyClsPlanGrpId = ILLEGAL_CLS_PLAN;

    return p_FmPcdKg;
}

t_Error KgInit(t_FmPcd *p_FmPcd)
{
    if (p_FmPcd->guestId == NCSW_MASTER_ID)
        return KgInitMaster(p_FmPcd);
    else
        return KgInitGuest(p_FmPcd);
}

t_Error KgFree(t_FmPcd *p_FmPcd)
{
    t_FmPcdIpcKgSchemesParams       kgAlloc;
    t_Error                         err = E_OK;
    t_FmPcdIpcMsg                   msg;
    uint32_t                        replyLength;
    t_FmPcdIpcReply                 reply;

    FmUnregisterIntr(p_FmPcd->h_Fm, e_FM_MOD_KG, 0, e_FM_INTR_TYPE_ERR);

    if(p_FmPcd->guestId == NCSW_MASTER_ID)
        return FmPcdKgFreeSchemes(p_FmPcd,
                                    p_FmPcd->p_FmPcdKg->numOfSchemes,
                                    p_FmPcd->guestId,
                                    p_FmPcd->p_FmPcdKg->schemesIds);

    /* guest */
    memset(&reply, 0, sizeof(reply));
    memset(&msg, 0, sizeof(msg));
    kgAlloc.numOfSchemes = p_FmPcd->p_FmPcdKg->numOfSchemes;
    kgAlloc.guestId = p_FmPcd->guestId;
    ASSERT_COND(kgAlloc.numOfSchemes < FM_PCD_KG_NUM_OF_SCHEMES);
    memcpy(kgAlloc.schemesIds, p_FmPcd->p_FmPcdKg->schemesIds , (sizeof(uint8_t))*kgAlloc.numOfSchemes);
    msg.msgId = FM_PCD_FREE_KG_SCHEMES;
    memcpy(msg.msgBody, &kgAlloc, sizeof(kgAlloc));
    replyLength = sizeof(uint32_t);
    if ((err = XX_IpcSendMessage(p_FmPcd->h_IpcSession,
                                 (uint8_t*)&msg,
                                 sizeof(msg.msgId) + sizeof(kgAlloc),
                                 (uint8_t*)&reply,
                                 &replyLength,
                                 NULL,
                                 NULL)) != E_OK)
        RETURN_ERROR(MAJOR, err, NO_MSG);
    if (replyLength != sizeof(uint32_t))
        RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("IPC reply length mismatch"));

    return (t_Error)reply.error;
}

t_Error FmPcdKgSetOrBindToClsPlanGrp(t_Handle h_FmPcd, uint8_t hardwarePortId, uint8_t netEnvId, protocolOpt_t *p_OptArray, uint8_t *p_ClsPlanGrpId, bool *p_IsEmptyClsPlanGrp)
{
    t_FmPcd                                 *p_FmPcd = (t_FmPcd *)h_FmPcd;
    t_FmPcdKgInterModuleClsPlanGrpParams    grpParams, *p_GrpParams;
    t_FmPcdKgClsPlanGrp                     *p_ClsPlanGrp;
    t_FmPcdKgInterModuleClsPlanSet          *p_ClsPlanSet;
    t_Error                                 err;

    memset(&grpParams, 0, sizeof(grpParams));
    grpParams.clsPlanGrpId = ILLEGAL_CLS_PLAN;
    p_GrpParams = &grpParams;

    p_GrpParams->netEnvId = netEnvId;
    err = PcdGetClsPlanGrpParams(h_FmPcd, p_GrpParams);
    if(err)
        RETURN_ERROR(MINOR,err,NO_MSG);
    if(p_GrpParams->grpExists)
        *p_ClsPlanGrpId = p_GrpParams->clsPlanGrpId;
    else
    {
        p_ClsPlanSet = (t_FmPcdKgInterModuleClsPlanSet *)XX_Malloc(sizeof(t_FmPcdKgInterModuleClsPlanSet));
        if (!p_ClsPlanSet)
            RETURN_ERROR(MAJOR, E_NO_MEMORY, ("memory allocation failed for p_ClsPlanSet"));
        memset(p_ClsPlanSet, 0, sizeof(t_FmPcdKgInterModuleClsPlanSet));
        err = FmPcdKgBuildClsPlanGrp(h_FmPcd, p_GrpParams, p_ClsPlanSet);
        if (err)
        {
            XX_Free(p_ClsPlanSet);
            RETURN_ERROR(MINOR,err,NO_MSG);
        }
        *p_ClsPlanGrpId = p_GrpParams->clsPlanGrpId;

        if (p_FmPcd->h_Hc)
        {
            /* write clsPlan entries to memory */
            err = FmHcPcdKgSetClsPlan(p_FmPcd->h_Hc, p_ClsPlanSet);
            if (err)
            {
                XX_Free(p_ClsPlanSet);
                RETURN_ERROR(MAJOR, err, NO_MSG);
            }
        }
        else
            /* write clsPlan entries to memory */
            KgSetClsPlan(p_FmPcd, p_ClsPlanSet);

        XX_Free(p_ClsPlanSet);
    }

    /* mark if this is an empty classification group */
    if(*p_ClsPlanGrpId == p_FmPcd->p_FmPcdKg->emptyClsPlanGrpId)
        *p_IsEmptyClsPlanGrp = TRUE;
    else
        *p_IsEmptyClsPlanGrp = FALSE;

    p_ClsPlanGrp = &p_FmPcd->p_FmPcdKg->clsPlanGrps[*p_ClsPlanGrpId];

    /* increment owners number */
    p_ClsPlanGrp->owners++;

    /* copy options array for port */
    memcpy(p_OptArray, &p_FmPcd->p_FmPcdKg->clsPlanGrps[*p_ClsPlanGrpId].optArray, FM_PCD_MAX_NUM_OF_OPTIONS(FM_PCD_MAX_NUM_OF_CLS_PLANS)*sizeof(protocolOpt_t));

    /* bind port to the new or existing group */
    err = KgBindPortToClsPlanGrp(p_FmPcd, hardwarePortId, p_GrpParams->clsPlanGrpId);
    if(err)
        RETURN_ERROR(MINOR, err, NO_MSG);

    return E_OK;
}

t_Error FmPcdKgDeleteOrUnbindPortToClsPlanGrp(t_Handle h_FmPcd, uint8_t hardwarePortId, uint8_t clsPlanGrpId)
{
    t_FmPcd                         *p_FmPcd = (t_FmPcd *)h_FmPcd;
    t_FmPcdKgClsPlanGrp             *p_ClsPlanGrp = &p_FmPcd->p_FmPcdKg->clsPlanGrps[clsPlanGrpId];
    t_FmPcdKgInterModuleClsPlanSet  *p_ClsPlanSet;

    FmPcdKgUnbindPortToClsPlanGrp(p_FmPcd, hardwarePortId);

    /* decrement owners number */
    ASSERT_COND(p_ClsPlanGrp->owners);
    p_ClsPlanGrp->owners--;

    if(!p_ClsPlanGrp->owners)
    {
        if (p_FmPcd->h_Hc)
            return FmHcPcdKgDeleteClsPlan(p_FmPcd->h_Hc, clsPlanGrpId);
        else
        {
            /* clear clsPlan entries in memory */
            p_ClsPlanSet = (t_FmPcdKgInterModuleClsPlanSet *)XX_Malloc(sizeof(t_FmPcdKgInterModuleClsPlanSet));
            if (!p_ClsPlanSet)
                RETURN_ERROR(MAJOR, E_NO_MEMORY, ("memory allocation failed for p_ClsPlanSet"));
            memset(p_ClsPlanSet, 0, sizeof(t_FmPcdKgInterModuleClsPlanSet));
            p_ClsPlanSet->baseEntry = p_FmPcd->p_FmPcdKg->clsPlanGrps[clsPlanGrpId].baseEntry;
            p_ClsPlanSet->numOfClsPlanEntries = p_FmPcd->p_FmPcdKg->clsPlanGrps[clsPlanGrpId].sizeOfGrp;
            KgSetClsPlan(p_FmPcd, p_ClsPlanSet);
            XX_Free(p_ClsPlanSet);
            FmPcdKgDestroyClsPlanGrp(h_FmPcd, clsPlanGrpId);
        }
    }
    return E_OK;
}

t_Error FmPcdKgBuildScheme(t_Handle h_FmPcd,  t_FmPcdKgSchemeParams *p_Scheme, t_FmPcdKgInterModuleSchemeRegs *p_SchemeRegs)
{
    t_FmPcd                             *p_FmPcd = (t_FmPcd *)h_FmPcd;
    uint32_t                            grpBits = 0;
    uint8_t                             grpBase;
    bool                                direct=TRUE, absolute=FALSE;
    uint16_t                            profileId=0, numOfProfiles=0, relativeProfileId;
    t_Error                             err = E_OK;
    int                                 i = 0;
    t_NetEnvParams                      netEnvParams;
    uint32_t                            tmpReg, fqbTmp = 0, ppcTmp = 0, selectTmp, maskTmp, knownTmp, genTmp;
    t_FmPcdKgKeyExtractAndHashParams    *p_KeyAndHash = NULL;
    uint8_t                             j, curr, idx;
    uint8_t                             id, shift=0, code=0, offset=0, size=0;
    t_FmPcdExtractEntry                 *p_Extract = NULL;
    t_FmPcdKgExtractedOrParams          *p_ExtractOr;
    bool                                generic = FALSE;
    t_KnownFieldsMasks                  bitMask;
    e_FmPcdKgExtractDfltSelect          swDefault = (e_FmPcdKgExtractDfltSelect)0;
    t_FmPcdKgSchemesExtracts            *p_LocalExtractsArray;
    uint8_t                             numOfSwDefaults = 0;
    t_FmPcdKgExtractDflt                swDefaults[NUM_OF_SW_DEFAULTS];
    uint8_t                             currGenId = 0, relativeSchemeId;

    if(!p_Scheme->modify)
        relativeSchemeId = p_Scheme->id.relativeSchemeId;
    else
        relativeSchemeId = FmPcdKgGetRelativeSchemeId(p_FmPcd, (uint8_t)(PTR_TO_UINT(p_Scheme->id.h_Scheme)-1));

    memset(&p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId], 0, sizeof(t_FmPcdKgScheme));
    memset(swDefaults, 0, NUM_OF_SW_DEFAULTS*sizeof(t_FmPcdKgExtractDflt));
    memset(p_SchemeRegs, 0, sizeof(t_FmPcdKgInterModuleSchemeRegs));

    if (p_Scheme->netEnvParams.numOfDistinctionUnits > FM_PCD_MAX_NUM_OF_DISTINCTION_UNITS)
        RETURN_ERROR(MAJOR, E_INVALID_VALUE,
                     ("numOfDistinctionUnits should not exceed %d", FM_PCD_MAX_NUM_OF_DISTINCTION_UNITS));

    /* by netEnv parameters, get match vector */
    if(!p_Scheme->alwaysDirect)
    {
        p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].netEnvId =
            (uint8_t)(PTR_TO_UINT(p_Scheme->netEnvParams.h_NetEnv)-1);
        netEnvParams.netEnvId = p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].netEnvId;
        netEnvParams.numOfDistinctionUnits = p_Scheme->netEnvParams.numOfDistinctionUnits;
        memcpy(netEnvParams.unitIds, p_Scheme->netEnvParams.unitIds, (sizeof(uint8_t))*p_Scheme->netEnvParams.numOfDistinctionUnits);
        err = PcdGetUnitsVector(p_FmPcd, &netEnvParams);
        if(err)
            RETURN_ERROR(MAJOR, E_INVALID_STATE, NO_MSG);
        p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].matchVector = netEnvParams.vector;
    }
    else
    {
        p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].matchVector = SCHEME_ALWAYS_DIRECT;
        p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].netEnvId = ILLEGAL_NETENV;
    }

    if(p_Scheme->nextEngine == e_FM_PCD_INVALID)
        RETURN_ERROR(MAJOR, E_INVALID_STATE, ("Next Engine of the scheme is not Valid"));

    if(p_Scheme->bypassFqidGeneration)
    {
#ifdef FM_KG_NO_BYPASS_FQID_GEN
        {
            t_FmRevisionInfo    revInfo;

            FM_GetRevision(p_FmPcd->h_Fm, &revInfo);
            if (revInfo.majorRev != 4)
                RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, ("bypassFqidGeneration."));
        }
#endif /* FM_KG_NO_BYPASS_FQID_GEN */
        if(p_Scheme->baseFqid)
            RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("baseFqid set for a scheme that does not generate an FQID"));
    }
    else
        if(!p_Scheme->baseFqid)
            DBG(WARNING, ("baseFqid is 0."));

    if(p_Scheme->nextEngine == e_FM_PCD_PLCR)
    {
        direct = p_Scheme->kgNextEngineParams.plcrProfile.direct;
        p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].directPlcr = direct;
        absolute = (bool)(p_Scheme->kgNextEngineParams.plcrProfile.sharedProfile ? TRUE : FALSE);
        if(!direct && absolute)
            RETURN_ERROR(MAJOR, E_INVALID_STATE, ("Indirect policing is not available when profile is shared."));

        if(direct)
        {
            profileId = p_Scheme->kgNextEngineParams.plcrProfile.profileSelect.directRelativeProfileId;
            numOfProfiles = 1;
        }
        else
        {
            profileId = p_Scheme->kgNextEngineParams.plcrProfile.profileSelect.indirectProfile.fqidOffsetRelativeProfileIdBase;
            shift = p_Scheme->kgNextEngineParams.plcrProfile.profileSelect.indirectProfile.fqidOffsetShift;
            numOfProfiles = p_Scheme->kgNextEngineParams.plcrProfile.profileSelect.indirectProfile.numOfProfiles;
        }
    }

    if(p_Scheme->nextEngine == e_FM_PCD_CC)
    {
#ifdef FM_KG_NO_BYPASS_PLCR_PROFILE_GEN
        if((p_Scheme->kgNextEngineParams.cc.plcrNext) && (p_Scheme->kgNextEngineParams.cc.bypassPlcrProfileGeneration))
        {
            t_FmRevisionInfo    revInfo;

            FM_GetRevision(p_FmPcd->h_Fm, &revInfo);
            if (revInfo.majorRev != 4)
                RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, ("bypassPlcrProfileGeneration."));
        }
#endif /* FM_KG_NO_BYPASS_PLCR_PROFILE_GEN */

        err = FmPcdCcGetGrpParams(p_Scheme->kgNextEngineParams.cc.h_CcTree,
                             p_Scheme->kgNextEngineParams.cc.grpId,
                             &grpBits,
                             &grpBase);
        if(err)
            RETURN_ERROR(MAJOR, err, NO_MSG);
        p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].ccUnits = grpBits;

        if((p_Scheme->kgNextEngineParams.cc.plcrNext) &&
           (!p_Scheme->kgNextEngineParams.cc.bypassPlcrProfileGeneration))
        {
                if(p_Scheme->kgNextEngineParams.cc.plcrProfile.sharedProfile)
                    RETURN_ERROR(MAJOR, E_INVALID_STATE, ("Shared profile may not be used after Coarse classification."));
                absolute = FALSE;
                direct = p_Scheme->kgNextEngineParams.cc.plcrProfile.direct;
                if(direct)
                {
                    profileId = p_Scheme->kgNextEngineParams.cc.plcrProfile.profileSelect.directRelativeProfileId;
                    numOfProfiles = 1;
                }
                else
                {
                    profileId = p_Scheme->kgNextEngineParams.cc.plcrProfile.profileSelect.indirectProfile.fqidOffsetRelativeProfileIdBase;
                    shift = p_Scheme->kgNextEngineParams.cc.plcrProfile.profileSelect.indirectProfile.fqidOffsetShift;
                    numOfProfiles = p_Scheme->kgNextEngineParams.cc.plcrProfile.profileSelect.indirectProfile.numOfProfiles;
                }
        }
    }

    /* if policer is used directly after KG, or after CC */
    if((p_Scheme->nextEngine == e_FM_PCD_PLCR)  ||
       ((p_Scheme->nextEngine == e_FM_PCD_CC) &&
        (p_Scheme->kgNextEngineParams.cc.plcrNext) &&
        (!p_Scheme->kgNextEngineParams.cc.bypassPlcrProfileGeneration)))
    {
        /* if private policer profile, it may be uninitialized yet, therefore no checks are done at this stage */
        if(absolute)
        {
            /* for absolute direct policy only, */
            relativeProfileId = profileId;
            err = FmPcdPlcrGetAbsoluteProfileId(h_FmPcd,e_FM_PCD_PLCR_SHARED,NULL, relativeProfileId, &profileId);
            if(err)
                RETURN_ERROR(MAJOR, err, ("Shared profile not valid offset"));
            if(!FmPcdPlcrIsProfileValid(p_FmPcd, profileId))
                RETURN_ERROR(MINOR, E_INVALID_STATE, ("Shared profile not valid."));
            p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].relativeProfileId = profileId;
        }
        else
        {
            /* save relative profile id's for later check */
            p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].nextRelativePlcrProfile = TRUE;
            p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].relativeProfileId = profileId;
            p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].numOfProfiles = numOfProfiles;
        }
    }
    else
    {
        /* if policer is NOT going to be used after KG at all than if bypassFqidGeneration
        is set, we do not need numOfUsedExtractedOrs and hashDistributionNumOfFqids */
        if(p_Scheme->bypassFqidGeneration && p_Scheme->numOfUsedExtractedOrs)
            RETURN_ERROR(MAJOR, E_INVALID_STATE,
                    ("numOfUsedExtractedOrs is set in a scheme that does not generate FQID or policer profile ID"));
        if(p_Scheme->bypassFqidGeneration &&
                p_Scheme->useHash &&
                p_Scheme->keyExtractAndHashParams.hashDistributionNumOfFqids)
            RETURN_ERROR(MAJOR, E_INVALID_STATE,
                    ("hashDistributionNumOfFqids is set in a scheme that does not generate FQID or policer profile ID"));
    }

    /* configure all 21 scheme registers */
    tmpReg =  KG_SCH_MODE_EN;
    switch(p_Scheme->nextEngine)
    {
        case(e_FM_PCD_PLCR):
            /* add to mode register - NIA */
            tmpReg |= KG_SCH_MODE_NIA_PLCR;
            tmpReg |= NIA_ENG_PLCR;
            tmpReg |= (uint32_t)(p_Scheme->kgNextEngineParams.plcrProfile.sharedProfile ? NIA_PLCR_ABSOLUTE:0);
            /* initialize policer profile command - */
            /*  configure kgse_ppc  */
            if(direct)
            /* use profileId as base, other fields are 0 */
                p_SchemeRegs->kgse_ppc = (uint32_t)profileId;
            else
            {
                if(shift > MAX_PP_SHIFT)
                    RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("fqidOffsetShift may not be larger than %d", MAX_PP_SHIFT));

                if(!numOfProfiles || !POWER_OF_2(numOfProfiles))
                    RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("numOfProfiles must not be 0 and must be a power of 2"));

                ppcTmp = ((uint32_t)shift << KG_SCH_PP_SHIFT_HIGH_SHIFT) & KG_SCH_PP_SHIFT_HIGH;
                ppcTmp |= ((uint32_t)shift << KG_SCH_PP_SHIFT_LOW_SHIFT) & KG_SCH_PP_SHIFT_LOW;
                ppcTmp |= ((uint32_t)(numOfProfiles-1) << KG_SCH_PP_MASK_SHIFT);
                ppcTmp |= (uint32_t)profileId;

                p_SchemeRegs->kgse_ppc = ppcTmp;
            }
            break;
        case(e_FM_PCD_CC):
            /* mode reg - define NIA */
            tmpReg |= (NIA_ENG_FM_CTL | NIA_FM_CTL_AC_CC);

            p_SchemeRegs->kgse_ccbs = grpBits;
            tmpReg |= (uint32_t)(grpBase << KG_SCH_MODE_CCOBASE_SHIFT);

            if(p_Scheme->kgNextEngineParams.cc.plcrNext)
            {
                if(!p_Scheme->kgNextEngineParams.cc.bypassPlcrProfileGeneration)
                {
                    /* find out if absolute or relative */
                    if(absolute)
                         RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("It is illegal to request a shared profile in a scheme that is in a KG->CC->PLCR flow"));
                    if(direct)
                    {
                        /* mask = 0, base = directProfileId */
                        p_SchemeRegs->kgse_ppc = (uint32_t)profileId;
                    }
                    else
                    {
                        if(shift > MAX_PP_SHIFT)
                            RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("fqidOffsetShift may not be larger than %d", MAX_PP_SHIFT));
                        if(!numOfProfiles || !POWER_OF_2(numOfProfiles))
                            RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("numOfProfiles must not be 0 and must be a power of 2"));

                        ppcTmp = ((uint32_t)shift << KG_SCH_PP_SHIFT_HIGH_SHIFT) & KG_SCH_PP_SHIFT_HIGH;
                        ppcTmp |= ((uint32_t)shift << KG_SCH_PP_SHIFT_LOW_SHIFT) & KG_SCH_PP_SHIFT_LOW;
                        ppcTmp |= ((uint32_t)(numOfProfiles-1) << KG_SCH_PP_MASK_SHIFT);
                        ppcTmp |= (uint32_t)profileId;

                        p_SchemeRegs->kgse_ppc = ppcTmp;
                    }
                }
                else
                    ppcTmp = KG_SCH_PP_NO_GEN;
            }
            break;
        case(e_FM_PCD_DONE):
            if(p_Scheme->kgNextEngineParams.doneAction == e_FM_PCD_DROP_FRAME)
                tmpReg |= (NIA_ENG_BMI | NIA_BMI_AC_DISCARD);
            else
                tmpReg |= (NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME);
            break;
        default:
             RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, ("Next engine not supported"));
    }
    p_SchemeRegs->kgse_mode = tmpReg;

    p_SchemeRegs->kgse_mv = p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].matchVector;

    if(p_Scheme->useHash)
    {
        p_KeyAndHash = &p_Scheme->keyExtractAndHashParams;

        if (p_KeyAndHash->numOfUsedExtracts >= FM_PCD_KG_MAX_NUM_OF_EXTRACTS_PER_KEY)
             RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("numOfUsedExtracts out of range"));

        /*  configure kgse_dv0  */
        p_SchemeRegs->kgse_dv0 = p_KeyAndHash->privateDflt0;

        /*  configure kgse_dv1  */
        p_SchemeRegs->kgse_dv1 = p_KeyAndHash->privateDflt1;

        if(!p_Scheme->bypassFqidGeneration)
        {
            if(!p_KeyAndHash->hashDistributionNumOfFqids || !POWER_OF_2(p_KeyAndHash->hashDistributionNumOfFqids))
                RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("hashDistributionNumOfFqids must not be 0 and must be a power of 2"));
            if((p_KeyAndHash->hashDistributionNumOfFqids-1) & p_Scheme->baseFqid)
                DBG(WARNING, ("baseFqid unaligned. Distribution may result in less than hashDistributionNumOfFqids queues."));
        }

        /*  configure kgse_ekdv  */
        tmpReg = 0;
        for( i=0 ;i<p_KeyAndHash->numOfUsedDflts ; i++)
        {
            switch(p_KeyAndHash->dflts[i].type)
            {
                case(e_FM_PCD_KG_MAC_ADDR):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_MAC_ADDR_SHIFT);
                    break;
                case(e_FM_PCD_KG_TCI):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_TCI_SHIFT);
                    break;
                case(e_FM_PCD_KG_ENET_TYPE):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_ENET_TYPE_SHIFT);
                    break;
                case(e_FM_PCD_KG_PPP_SESSION_ID):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_PPP_SESSION_ID_SHIFT);
                    break;
                case(e_FM_PCD_KG_PPP_PROTOCOL_ID):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_PPP_PROTOCOL_ID_SHIFT);
                    break;
                case(e_FM_PCD_KG_MPLS_LABEL):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_MPLS_LABEL_SHIFT);
                    break;
                case(e_FM_PCD_KG_IP_ADDR):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_IP_ADDR_SHIFT);
                    break;
                case(e_FM_PCD_KG_PROTOCOL_TYPE):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_PROTOCOL_TYPE_SHIFT);
                    break;
                case(e_FM_PCD_KG_IP_TOS_TC):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_IP_TOS_TC_SHIFT);
                    break;
                case(e_FM_PCD_KG_IPV6_FLOW_LABEL):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_L4_PORT_SHIFT);
                    break;
                case(e_FM_PCD_KG_IPSEC_SPI):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_IPSEC_SPI_SHIFT);
                    break;
                case(e_FM_PCD_KG_L4_PORT):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_L4_PORT_SHIFT);
                    break;
                case(e_FM_PCD_KG_TCP_FLAG):
                    tmpReg |= (p_KeyAndHash->dflts[i].dfltSelect << KG_SCH_DEF_TCP_FLAG_SHIFT);
                    break;
                case(e_FM_PCD_KG_GENERIC_FROM_DATA):
                    swDefaults[numOfSwDefaults].type = e_FM_PCD_KG_GENERIC_FROM_DATA;
                    swDefaults[numOfSwDefaults].dfltSelect = p_KeyAndHash->dflts[i].dfltSelect;
                    numOfSwDefaults ++;
                    break;
                case(e_FM_PCD_KG_GENERIC_FROM_DATA_NO_V):
                    swDefaults[numOfSwDefaults].type = e_FM_PCD_KG_GENERIC_FROM_DATA_NO_V;
                    swDefaults[numOfSwDefaults].dfltSelect = p_KeyAndHash->dflts[i].dfltSelect;
                    numOfSwDefaults ++;
                    break;
                case(e_FM_PCD_KG_GENERIC_NOT_FROM_DATA):
                    swDefaults[numOfSwDefaults].type = e_FM_PCD_KG_GENERIC_NOT_FROM_DATA;
                    swDefaults[numOfSwDefaults].dfltSelect = p_KeyAndHash->dflts[i].dfltSelect;
                    numOfSwDefaults ++;
                   break;
                default:
                    RETURN_ERROR(MAJOR, E_INVALID_SELECTION, NO_MSG);
            }
        }
        p_SchemeRegs->kgse_ekdv = tmpReg;

        p_LocalExtractsArray = (t_FmPcdKgSchemesExtracts *)XX_Malloc(sizeof(t_FmPcdKgSchemesExtracts));
        if(!p_LocalExtractsArray)
            RETURN_ERROR(MAJOR, E_NO_MEMORY, ("No memory"));

        /*  configure kgse_ekfc and  kgse_gec */
        knownTmp = 0;
        for( i=0 ;i<p_KeyAndHash->numOfUsedExtracts ; i++)
        {
            p_Extract = &p_KeyAndHash->extractArray[i];
            switch(p_Extract->type)
            {
                case(e_FM_PCD_KG_EXTRACT_PORT_PRIVATE_INFO):
                    knownTmp |= KG_SCH_KN_PORT_ID;
                    /* save in driver structure */
                    p_LocalExtractsArray->extractsArray[i].id = GetKnownFieldId(KG_SCH_KN_PORT_ID);
                    p_LocalExtractsArray->extractsArray[i].known = TRUE;
                    break;
                case(e_FM_PCD_EXTRACT_BY_HDR):
                    switch(p_Extract->extractByHdr.hdr)
                    {
                        case(HEADER_TYPE_UDP_ENCAP_ESP):
                            switch(p_Extract->extractByHdr.type)
                            {
                                case(e_FM_PCD_EXTRACT_FROM_HDR):
                                    /* case where extraction from ESP only */
                                    if (p_Extract->extractByHdr.extractByHdrType.fromHdr.offset >= UDP_HEADER_SIZE)
                                    {
                                        p_Extract->extractByHdr.hdr = FmPcdGetAliasHdr(p_FmPcd, p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].netEnvId, HEADER_TYPE_UDP_ENCAP_ESP);
                                        p_Extract->extractByHdr.extractByHdrType.fromHdr.offset -= UDP_HEADER_SIZE;
                                        p_Extract->extractByHdr.ignoreProtocolValidation = TRUE;
                                    }
                                    else
                                    {
                                        p_Extract->extractByHdr.hdr = HEADER_TYPE_UDP;
                                        p_Extract->extractByHdr.ignoreProtocolValidation = FALSE;
                                    }
                                    break;
                                case(e_FM_PCD_EXTRACT_FROM_FIELD):
                                    switch(p_Extract->extractByHdr.extractByHdrType.fromField.field.udpEncapEsp)
                                    {
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_PORT_SRC):
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_PORT_DST):
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_LEN):
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_CKSUM):
                                            p_Extract->extractByHdr.hdr = HEADER_TYPE_UDP;
                                            break;
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_SPI):
                                            p_Extract->extractByHdr.type = e_FM_PCD_EXTRACT_FROM_HDR;
                                            p_Extract->extractByHdr.hdr = FmPcdGetAliasHdr(p_FmPcd, p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].netEnvId, HEADER_TYPE_UDP_ENCAP_ESP);
                                            p_Extract->extractByHdr.extractByHdrType.fromField.size = p_Extract->extractByHdr.extractByHdrType.fromField.size;
                                            /*p_Extract->extractByHdr.extractByHdrType.fromField.offset += ESP_SPI_OFFSET;*/
                                            p_Extract->extractByHdr.ignoreProtocolValidation = TRUE;
                                            break;
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_SEQUENCE_NUM):
                                            p_Extract->extractByHdr.type = e_FM_PCD_EXTRACT_FROM_HDR;
                                            p_Extract->extractByHdr.hdr = FmPcdGetAliasHdr(p_FmPcd, p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].netEnvId, HEADER_TYPE_UDP_ENCAP_ESP);
                                            p_Extract->extractByHdr.extractByHdrType.fromField.size = p_Extract->extractByHdr.extractByHdrType.fromField.size;
                                            p_Extract->extractByHdr.extractByHdrType.fromField.offset += ESP_SEQ_NUM_OFFSET;
                                            p_Extract->extractByHdr.ignoreProtocolValidation = TRUE;
                                            break;
                                    }
                                    break;
                                case(e_FM_PCD_EXTRACT_FULL_FIELD):
                                    switch(p_Extract->extractByHdr.extractByHdrType.fullField.udpEncapEsp)
                                    {
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_PORT_SRC):
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_PORT_DST):
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_LEN):
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_CKSUM):
                                            p_Extract->extractByHdr.hdr = HEADER_TYPE_UDP;
                                            break;
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_SPI):
                                            p_Extract->extractByHdr.type = e_FM_PCD_EXTRACT_FROM_HDR;
                                            p_Extract->extractByHdr.hdr = FmPcdGetAliasHdr(p_FmPcd, p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].netEnvId, HEADER_TYPE_UDP_ENCAP_ESP);
                                            p_Extract->extractByHdr.extractByHdrType.fromHdr.size = ESP_SPI_SIZE;
                                            p_Extract->extractByHdr.extractByHdrType.fromHdr.offset = ESP_SPI_OFFSET;
                                            p_Extract->extractByHdr.ignoreProtocolValidation = TRUE;
                                            break;
                                        case(NET_HEADER_FIELD_UDP_ENCAP_ESP_SEQUENCE_NUM):
                                            p_Extract->extractByHdr.type = e_FM_PCD_EXTRACT_FROM_HDR;
                                            p_Extract->extractByHdr.hdr = FmPcdGetAliasHdr(p_FmPcd, p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].netEnvId, HEADER_TYPE_UDP_ENCAP_ESP);
                                            p_Extract->extractByHdr.extractByHdrType.fromHdr.size = ESP_SEQ_NUM_SIZE;
                                            p_Extract->extractByHdr.extractByHdrType.fromHdr.offset = ESP_SEQ_NUM_OFFSET;
                                            p_Extract->extractByHdr.ignoreProtocolValidation = TRUE;
                                            break;
                                    }
                                    break;
                            }
                            break;
                        default:
                            break;
                    }
                    switch(p_Extract->extractByHdr.type)
                    {
                        case(e_FM_PCD_EXTRACT_FROM_HDR):
                            generic = TRUE;
                            /* get the header code for the generic extract */
                            code = GetGenHdrCode(p_Extract->extractByHdr.hdr, p_Extract->extractByHdr.hdrIndex, p_Extract->extractByHdr.ignoreProtocolValidation);
                            /* set generic register fields */
                            offset = p_Extract->extractByHdr.extractByHdrType.fromHdr.offset;
                            size = p_Extract->extractByHdr.extractByHdrType.fromHdr.size;
                            break;
                        case(e_FM_PCD_EXTRACT_FROM_FIELD):
                            generic = TRUE;
                            /* get the field code for the generic extract */
                            code = GetGenFieldCode(p_Extract->extractByHdr.hdr,
                                        p_Extract->extractByHdr.extractByHdrType.fromField.field, p_Extract->extractByHdr.ignoreProtocolValidation,p_Extract->extractByHdr.hdrIndex);
                            offset = p_Extract->extractByHdr.extractByHdrType.fromField.offset;
                            size = p_Extract->extractByHdr.extractByHdrType.fromField.size;
                            break;
                        case(e_FM_PCD_EXTRACT_FULL_FIELD):
                            if(!p_Extract->extractByHdr.ignoreProtocolValidation)
                            {
                                /* if we have a known field for it - use it, otherwise use generic */
                                bitMask = GetKnownProtMask(p_Extract->extractByHdr.hdr, p_Extract->extractByHdr.hdrIndex,
                                            p_Extract->extractByHdr.extractByHdrType.fullField);
                                if(bitMask)
                                {
                                    knownTmp |= bitMask;
                                    /* save in driver structure */
                                    p_LocalExtractsArray->extractsArray[i].id = GetKnownFieldId(bitMask);
                                    p_LocalExtractsArray->extractsArray[i].known = TRUE;
                                }
                                else
                                    generic = TRUE;

                            }
                            else
                                generic = TRUE;
                            if(generic)
                            {
                                /* tmp - till we cover more headers under generic */
                                RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, ("Full header selection not supported"));
                            }
                            break;
                        default:
                            RETURN_ERROR(MAJOR, E_INVALID_SELECTION, NO_MSG);
                    }
                    break;
                case(e_FM_PCD_EXTRACT_NON_HDR):
                    /* use generic */
                    generic = TRUE;
                    offset = 0;
                    /* get the field code for the generic extract */
                    code = GetGenCode(p_Extract->extractNonHdr.src, &offset);
                    offset += p_Extract->extractNonHdr.offset;
                    size = p_Extract->extractNonHdr.size;
                    break;
                default:
                    RETURN_ERROR(MAJOR, E_INVALID_SELECTION, NO_MSG);
            }

            if(generic)
            {
                /* set generic register fields */
                if(currGenId >= FM_PCD_KG_NUM_OF_GENERIC_REGS)
                    RETURN_ERROR(MAJOR, E_FULL, ("Generic registers are fully used"));
                if(!code)
                    RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, NO_MSG);

                genTmp = KG_SCH_GEN_VALID;
                genTmp |= (uint32_t)(code << KG_SCH_GEN_HT_SHIFT);
                genTmp |= offset;
                if((size > MAX_KG_SCH_SIZE) || (size < 1))
                      RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, ("Illegal extraction (size out of range)"));
                genTmp |= (uint32_t)((size - 1) << KG_SCH_GEN_SIZE_SHIFT);
                swDefault = GetGenericSwDefault(swDefaults, numOfSwDefaults, code);
                if(swDefault == e_FM_PCD_KG_DFLT_ILLEGAL)
                    DBG(WARNING, ("No sw default configured"));

                genTmp |= swDefault << KG_SCH_GEN_DEF_SHIFT;
                genTmp |= KG_SCH_GEN_MASK;
                p_SchemeRegs->kgse_gec[currGenId] = genTmp;
                /* save in driver structure */
                p_LocalExtractsArray->extractsArray[i].id = currGenId++;
                p_LocalExtractsArray->extractsArray[i].known = FALSE;
                generic = FALSE;
            }
        }
        p_SchemeRegs->kgse_ekfc = knownTmp;

        selectTmp = 0;
        maskTmp = 0xFFFFFFFF;
        /*  configure kgse_bmch, kgse_bmcl and kgse_fqb */

        if(p_KeyAndHash->numOfUsedMasks >= FM_PCD_KG_NUM_OF_EXTRACT_MASKS)
            RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("Only %d masks supported", FM_PCD_KG_NUM_OF_EXTRACT_MASKS));
        for( i=0 ;i<p_KeyAndHash->numOfUsedMasks ; i++)
        {
            /* Get the relative id of the extract (for known 0-0x1f, for generic 0-7) */
            id = p_LocalExtractsArray->extractsArray[p_KeyAndHash->masks[i].extractArrayIndex].id;
            /* Get the shift of the select field (depending on i) */
            GET_MASK_SEL_SHIFT(shift,i);
            if (p_LocalExtractsArray->extractsArray[p_KeyAndHash->masks[i].extractArrayIndex].known)
                selectTmp |= id << shift;
            else
                selectTmp |= (id + MASK_FOR_GENERIC_BASE_ID) << shift;

            /* Get the shift of the offset field (depending on i) - may
               be in  kgse_bmch or in kgse_fqb (depending on i) */
            GET_MASK_OFFSET_SHIFT(shift,i);
            if (i<=1)
                selectTmp |= p_KeyAndHash->masks[i].offset << shift;
            else
                fqbTmp |= p_KeyAndHash->masks[i].offset << shift;

            /* Get the shift of the mask field (depending on i) */
            GET_MASK_SHIFT(shift,i);
            /* pass all bits */
            maskTmp |= KG_SCH_BITMASK_MASK << shift;
            /* clear bits that need masking */
            maskTmp &= ~(0xFF << shift) ;
            /* set mask bits */
            maskTmp |= (p_KeyAndHash->masks[i].mask << shift) ;
        }
        p_SchemeRegs->kgse_bmch = selectTmp;
        p_SchemeRegs->kgse_bmcl = maskTmp;
        /* kgse_fqb will be written t the end of the routine */

        /*  configure kgse_hc  */
        if(p_KeyAndHash->hashShift > MAX_HASH_SHIFT)
             RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("hashShift must not be larger than %d", MAX_HASH_SHIFT));
        if(p_KeyAndHash->hashDistributionFqidsShift > MAX_DIST_FQID_SHIFT)
             RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("hashDistributionFqidsShift must not be larger than %d", MAX_DIST_FQID_SHIFT));

        tmpReg = 0;

        tmpReg |= ((p_KeyAndHash->hashDistributionNumOfFqids - 1) << p_KeyAndHash->hashDistributionFqidsShift);
        tmpReg |= p_KeyAndHash->hashShift << KG_SCH_HASH_CONFIG_SHIFT_SHIFT;

        if(p_KeyAndHash->symmetricHash)
        {
            if((!!(p_SchemeRegs->kgse_ekfc & KG_SCH_KN_MACSRC) != !!(p_SchemeRegs->kgse_ekfc & KG_SCH_KN_MACDST)) ||
                    (!!(p_SchemeRegs->kgse_ekfc & KG_SCH_KN_IPSRC1) != !!(p_SchemeRegs->kgse_ekfc & KG_SCH_KN_IPDST1)) ||
                    (!!(p_SchemeRegs->kgse_ekfc & KG_SCH_KN_IPSRC2) != !!(p_SchemeRegs->kgse_ekfc & KG_SCH_KN_IPDST2)) ||
                    (!!(p_SchemeRegs->kgse_ekfc & KG_SCH_KN_L4PSRC) != !!(p_SchemeRegs->kgse_ekfc & KG_SCH_KN_L4PDST)))
                RETURN_ERROR(MAJOR, E_INVALID_STATE, ("symmetricHash set but src/dest extractions missing"));
            tmpReg |= KG_SCH_HASH_CONFIG_SYM;
        }
        p_SchemeRegs->kgse_hc = tmpReg;

        /* build the return array describing the order of the extractions */

        /* the last currGenId places of the array
           are for generic extracts that are always last.
           We now sort for the calculation of the order of the known
           extractions we sort the known extracts between orderedArray[0] and
           orderedArray[p_KeyAndHash->numOfUsedExtracts - currGenId - 1].
           for the calculation of the order of the generic extractions we use:
           num_of_generic - currGenId
           num_of_known - p_KeyAndHash->numOfUsedExtracts - currGenId
           first_generic_index = num_of_known */
        curr = 0;
        for (i=0;i<p_KeyAndHash->numOfUsedExtracts ; i++)
        {
            if(p_LocalExtractsArray->extractsArray[i].known)
            {
                ASSERT_COND(curr<(p_KeyAndHash->numOfUsedExtracts - currGenId));
                j = curr;
                /* id is the extract id (port id = 0, mac src = 1 etc.). the value in the array is the original
                index in the user's extractions array */
                /* we compare the id of the current extract with the id of the extract in the orderedArray[j-1]
                location */
                while((j > 0) && (p_LocalExtractsArray->extractsArray[i].id <
                      p_LocalExtractsArray->extractsArray[p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].orderedArray[j-1]].id))
                {
                    p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].orderedArray[j] =
                        p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].orderedArray[j-1];
                    j--;
                }
                p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].orderedArray[j] = (uint8_t)i;
                curr++;
            }
            else
            {
                /* index is first_generic_index + generic index (id) */
                idx = (uint8_t)(p_KeyAndHash->numOfUsedExtracts - currGenId + p_LocalExtractsArray->extractsArray[i].id);
                ASSERT_COND(idx < FM_PCD_KG_MAX_NUM_OF_EXTRACTS_PER_KEY);
                p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].orderedArray[idx]= (uint8_t)i;
            }
        }
        XX_Free(p_LocalExtractsArray);
        p_LocalExtractsArray = NULL;

    }
    else
    {
        /* clear all unused registers: */
        p_SchemeRegs->kgse_ekfc = 0;
        p_SchemeRegs->kgse_ekdv = 0;
        p_SchemeRegs->kgse_bmch = 0;
        p_SchemeRegs->kgse_bmcl = 0;
        p_SchemeRegs->kgse_hc = 0;
        p_SchemeRegs->kgse_dv0 = 0;
        p_SchemeRegs->kgse_dv1 = 0;
    }

    if(p_Scheme->bypassFqidGeneration)
        p_SchemeRegs->kgse_hc |= KG_SCH_HASH_CONFIG_NO_FQID;

    /*  configure kgse_spc  */
    if( p_Scheme->schemeCounter.update)
        p_SchemeRegs->kgse_spc = p_Scheme->schemeCounter.value;


    /* check that are enough generic registers */
    if(p_Scheme->numOfUsedExtractedOrs + currGenId > FM_PCD_KG_NUM_OF_GENERIC_REGS)
        RETURN_ERROR(MAJOR, E_FULL, ("Generic registers are fully used"));

    /* extracted OR mask on Qid */
    for( i=0 ;i<p_Scheme->numOfUsedExtractedOrs ; i++)
    {

        p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].extractedOrs = TRUE;
        /*  configure kgse_gec[i]  */
        p_ExtractOr = &p_Scheme->extractedOrs[i];
        switch(p_ExtractOr->type)
        {
            case(e_FM_PCD_KG_EXTRACT_PORT_PRIVATE_INFO):
                code = KG_SCH_GEN_PARSE_RESULT_N_FQID;
                offset = 0;
                break;
            case(e_FM_PCD_EXTRACT_BY_HDR):
                /* get the header code for the generic extract */
                code = GetGenHdrCode(p_ExtractOr->extractByHdr.hdr, p_ExtractOr->extractByHdr.hdrIndex, p_ExtractOr->extractByHdr.ignoreProtocolValidation);
                /* set generic register fields */
                offset = p_ExtractOr->extractionOffset;
                break;
            case(e_FM_PCD_EXTRACT_NON_HDR):
                /* get the field code for the generic extract */
                offset = 0;
                code = GetGenCode(p_ExtractOr->src, &offset);
                offset += p_ExtractOr->extractionOffset;
                break;
            default:
                RETURN_ERROR(MAJOR, E_INVALID_SELECTION, NO_MSG);
        }

        /* set generic register fields */
        if(!code)
            RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, NO_MSG);
        genTmp = KG_SCH_GEN_EXTRACT_TYPE | KG_SCH_GEN_VALID;
        genTmp |= (uint32_t)(code << KG_SCH_GEN_HT_SHIFT);
        genTmp |= offset;
        if(!!p_ExtractOr->bitOffsetInFqid == !!p_ExtractOr->bitOffsetInPlcrProfile)
            RETURN_ERROR(MAJOR, E_INVALID_VALUE, (" extracted byte must effect either FQID or Policer profile"));

        /************************************************************************************
            bitOffsetInFqid and bitOffsetInPolicerProfile are translated to rotate parameter
            in the following way:

            Driver API and implementation:
            ==============================
            FQID: extracted OR byte may be shifted right 1-31 bits to effect parts of the FQID.
            if shifted less than 8 bits, or more than 24 bits a mask is set on the bits that
            are not overlapping FQID.
                     ------------------------
                    |      FQID (24)         |
                     ------------------------
            --------
           |        |  extracted OR byte
            --------

            Policer Profile: extracted OR byte may be shifted right 1-15 bits to effect parts of the
            PP id. Unless shifted exactly 8 bits to overlap the PP id, a mask is set on the bits that
            are not overlapping PP id.

                     --------
                    | PP (8) |
                     --------
            --------
           |        |  extracted OR byte
            --------

            HW implementation
            =================
            FQID and PP construct a 32 bit word in the way describe below. Extracted byte is located
            as the highest byte of that word and may be rotated to effect any part os the FQID or
            the PP.
             ------------------------  --------
            |      FQID (24)         || PP (8) |
             ------------------------  --------
             --------
            |        |  extracted OR byte
             --------

        ************************************************************************************/

        if(p_ExtractOr->bitOffsetInFqid)
        {
            if(p_ExtractOr->bitOffsetInFqid > MAX_KG_SCH_FQID_BIT_OFFSET )
              RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("Illegal extraction (bitOffsetInFqid out of range)"));
            if(p_ExtractOr->bitOffsetInFqid<8)
                genTmp |= (uint32_t)((p_ExtractOr->bitOffsetInFqid+24) << KG_SCH_GEN_SIZE_SHIFT);
            else
                genTmp |= (uint32_t)((p_ExtractOr->bitOffsetInFqid-8) << KG_SCH_GEN_SIZE_SHIFT);
            p_ExtractOr->mask &= GetExtractedOrMask(p_ExtractOr->bitOffsetInFqid, TRUE);
        }
        else /* effect policer profile */
        {
            if(p_ExtractOr->bitOffsetInPlcrProfile > MAX_KG_SCH_PP_BIT_OFFSET )
              RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("Illegal extraction (bitOffsetInPlcrProfile out of range)"));
            p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].bitOffsetInPlcrProfile = p_ExtractOr->bitOffsetInPlcrProfile;
            genTmp |= (uint32_t)((p_ExtractOr->bitOffsetInPlcrProfile+16) << KG_SCH_GEN_SIZE_SHIFT);
            p_ExtractOr->mask &= GetExtractedOrMask(p_ExtractOr->bitOffsetInPlcrProfile, FALSE);
        }

        genTmp |= (uint32_t)(p_ExtractOr->extractionOffset << KG_SCH_GEN_DEF_SHIFT);
        /* clear bits that need masking */
        genTmp &= ~KG_SCH_GEN_MASK ;
        /* set mask bits */
        genTmp |= (uint32_t)(p_ExtractOr->mask << KG_SCH_GEN_MASK_SHIFT);
        p_SchemeRegs->kgse_gec[currGenId++] = genTmp;

    }
    /* clear all unused GEC registers */
    for( i=currGenId ;i<FM_PCD_KG_NUM_OF_GENERIC_REGS ; i++)
        p_SchemeRegs->kgse_gec[i] = 0;

    /* add base Qid for this scheme */
    /* add configuration for kgse_fqb */
    if(p_Scheme->baseFqid & ~0x00FFFFFF)
        RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("baseFqid must be between 1 and 2^24-1"));

    fqbTmp |= p_Scheme->baseFqid;
    p_SchemeRegs->kgse_fqb = fqbTmp;

    p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].nextEngine = p_Scheme->nextEngine;
    p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].doneAction = p_Scheme->kgNextEngineParams.doneAction;
    return E_OK;
}

void  FmPcdKgValidateSchemeSw(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

    ASSERT_COND(!p_FmPcd->p_FmPcdKg->schemes[schemeId].valid);

    if(p_FmPcd->p_FmPcdKg->schemes[schemeId].netEnvId != ILLEGAL_NETENV)
        FmPcdIncNetEnvOwners(p_FmPcd, p_FmPcd->p_FmPcdKg->schemes[schemeId].netEnvId);
    p_FmPcd->p_FmPcdKg->schemes[schemeId].valid = TRUE;
}

void  FmPcdKgInvalidateSchemeSw(t_Handle h_FmPcd, uint8_t schemeId)
{

    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

    if(p_FmPcd->p_FmPcdKg->schemes[schemeId].netEnvId != ILLEGAL_NETENV)
        FmPcdDecNetEnvOwners(h_FmPcd, p_FmPcd->p_FmPcdKg->schemes[schemeId].netEnvId);
    p_FmPcd->p_FmPcdKg->schemes[schemeId].valid = FALSE;
}

uint32_t FmPcdKgGetRequiredAction(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;
    ASSERT_COND(p_FmPcd->p_FmPcdKg->schemes[schemeId].valid);

    return p_FmPcd->p_FmPcdKg->schemes[schemeId].requiredAction;
}

uint32_t FmPcdKgGetPointedOwners(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

   ASSERT_COND(p_FmPcd->p_FmPcdKg->schemes[schemeId].valid);

    return p_FmPcd->p_FmPcdKg->schemes[schemeId].pointedOwners;
}

bool FmPcdKgIsDirectPlcr(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

   ASSERT_COND(p_FmPcd->p_FmPcdKg->schemes[schemeId].valid);

    return p_FmPcd->p_FmPcdKg->schemes[schemeId].directPlcr;
}


uint16_t FmPcdKgGetRelativeProfileId(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

   ASSERT_COND(p_FmPcd->p_FmPcdKg->schemes[schemeId].valid);

    return p_FmPcd->p_FmPcdKg->schemes[schemeId].relativeProfileId;
}


bool FmPcdKgIsDistrOnPlcrProfile(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

   ASSERT_COND(p_FmPcd->p_FmPcdKg->schemes[schemeId].valid);

    if((p_FmPcd->p_FmPcdKg->schemes[schemeId].extractedOrs &&
        p_FmPcd->p_FmPcdKg->schemes[schemeId].bitOffsetInPlcrProfile) ||
        p_FmPcd->p_FmPcdKg->schemes[schemeId].nextRelativePlcrProfile)
        return TRUE;
    else
        return FALSE;

}
void FmPcdKgUpatePointedOwner(t_Handle h_FmPcd, uint8_t schemeId, bool add)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

   ASSERT_COND(p_FmPcd->p_FmPcdKg->schemes[schemeId].valid);

    if(add)
        p_FmPcd->p_FmPcdKg->schemes[schemeId].pointedOwners++;
    else
        p_FmPcd->p_FmPcdKg->schemes[schemeId].pointedOwners--;
}

e_FmPcdEngine FmPcdKgGetNextEngine(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

    ASSERT_COND(p_FmPcd->p_FmPcdKg->schemes[schemeId].valid);

    return p_FmPcd->p_FmPcdKg->schemes[schemeId].nextEngine;
}

e_FmPcdDoneAction FmPcdKgGetDoneAction(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

    ASSERT_COND(p_FmPcd->p_FmPcdKg->schemes[schemeId].valid);

    return p_FmPcd->p_FmPcdKg->schemes[schemeId].doneAction;
}

void FmPcdKgUpdateRequiredAction(t_Handle h_FmPcd, uint8_t schemeId, uint32_t requiredAction)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

    ASSERT_COND(p_FmPcd->p_FmPcdKg->schemes[schemeId].valid);

    p_FmPcd->p_FmPcdKg->schemes[schemeId].requiredAction = requiredAction;
}

t_Error FmPcdKgCheckInvalidateSchemeSw(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

    if(schemeId >= FM_PCD_KG_NUM_OF_SCHEMES)
        REPORT_ERROR(MAJOR, E_NOT_IN_RANGE, NO_MSG);

   /* check that no port is bound to this scheme */
    if(p_FmPcd->p_FmPcdKg->schemes[schemeId].owners)
       RETURN_ERROR(MINOR, E_INVALID_STATE, ("Trying to delete a scheme that has ports bound to"));
    if(!p_FmPcd->p_FmPcdKg->schemes[schemeId].valid)
       RETURN_ERROR(MINOR, E_INVALID_STATE, ("Trying to delete an invalid scheme"));
    return E_OK;
}

uint32_t FmPcdKgBuildCppReg(t_Handle h_FmPcd, uint8_t clsPlanGrpId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint32_t    tmpKgpeCpp;

    tmpKgpeCpp = (uint32_t)(p_FmPcd->p_FmPcdKg->clsPlanGrps[clsPlanGrpId].baseEntry / 8);
    tmpKgpeCpp |= (uint32_t)(((p_FmPcd->p_FmPcdKg->clsPlanGrps[clsPlanGrpId].sizeOfGrp / 8) - 1) << FM_PCD_KG_PE_CPP_MASK_SHIFT);
    return tmpKgpeCpp;
}

bool    FmPcdKgHwSchemeIsValid(uint32_t schemeModeReg)
{

    if(schemeModeReg & KG_SCH_MODE_EN)
        return TRUE;
    else
        return FALSE;
}

uint32_t    FmPcdKgBuildWriteSchemeActionReg(uint8_t schemeId, bool updateCounter)
{
    return     (uint32_t)(((uint32_t)schemeId << FM_PCD_KG_KGAR_NUM_SHIFT)|
                            FM_PCD_KG_KGAR_GO |
                            FM_PCD_KG_KGAR_WRITE |
                            FM_PCD_KG_KGAR_SEL_SCHEME_ENTRY |
                            DUMMY_PORT_ID |
                            (updateCounter ? FM_PCD_KG_KGAR_SCHEME_WSEL_UPDATE_CNT:0));

}

uint32_t    FmPcdKgBuildReadSchemeActionReg(uint8_t schemeId)
{
    return     (uint32_t)(((uint32_t)schemeId << FM_PCD_KG_KGAR_NUM_SHIFT)|
                            FM_PCD_KG_KGAR_GO |
                            FM_PCD_KG_KGAR_READ |
                            FM_PCD_KG_KGAR_SEL_SCHEME_ENTRY |
                            DUMMY_PORT_ID |
                            FM_PCD_KG_KGAR_SCHEME_WSEL_UPDATE_CNT);

}


uint32_t    FmPcdKgBuildWriteClsPlanBlockActionReg(uint8_t grpId)
{
    return (uint32_t)(FM_PCD_KG_KGAR_GO |
                        FM_PCD_KG_KGAR_WRITE |
                        FM_PCD_KG_KGAR_SEL_CLS_PLAN_ENTRY |
                        DUMMY_PORT_ID |
                        ((uint32_t)grpId << FM_PCD_KG_KGAR_NUM_SHIFT) |
                        FM_PCD_KG_KGAR_WSEL_MASK);


        /* if we ever want to write 1 by 1, use:
        sel = (uint8_t)(0x01 << (7- (entryId % CLS_PLAN_NUM_PER_GRP)));*/
}

uint32_t    FmPcdKgBuildReadClsPlanBlockActionReg(uint8_t grpId)
{
    return (uint32_t)(FM_PCD_KG_KGAR_GO |
                        FM_PCD_KG_KGAR_READ |
                        FM_PCD_KG_KGAR_SEL_CLS_PLAN_ENTRY |
                        DUMMY_PORT_ID |
                        ((uint32_t)grpId << FM_PCD_KG_KGAR_NUM_SHIFT) |
                        FM_PCD_KG_KGAR_WSEL_MASK);


        /* if we ever want to write 1 by 1, use:
        sel = (uint8_t)(0x01 << (7- (entryId % CLS_PLAN_NUM_PER_GRP)));*/
}

uint32_t        FmPcdKgBuildWritePortSchemeBindActionReg(uint8_t hardwarePortId)
{

    return (uint32_t)(FM_PCD_KG_KGAR_GO |
                        FM_PCD_KG_KGAR_WRITE |
                        FM_PCD_KG_KGAR_SEL_PORT_ENTRY |
                        hardwarePortId |
                        FM_PCD_KG_KGAR_SEL_PORT_WSEL_SP);
}

uint32_t        FmPcdKgBuildReadPortSchemeBindActionReg(uint8_t hardwarePortId)
{

    return (uint32_t)(FM_PCD_KG_KGAR_GO |
                        FM_PCD_KG_KGAR_READ |
                        FM_PCD_KG_KGAR_SEL_PORT_ENTRY |
                        hardwarePortId |
                        FM_PCD_KG_KGAR_SEL_PORT_WSEL_SP);
}
uint32_t        FmPcdKgBuildWritePortClsPlanBindActionReg(uint8_t hardwarePortId)
{

    return (uint32_t)(FM_PCD_KG_KGAR_GO |
                        FM_PCD_KG_KGAR_WRITE |
                        FM_PCD_KG_KGAR_SEL_PORT_ENTRY |
                        hardwarePortId |
                        FM_PCD_KG_KGAR_SEL_PORT_WSEL_CPP);
}

uint8_t FmPcdKgGetClsPlanGrpBase(t_Handle h_FmPcd, uint8_t clsPlanGrp)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

    return p_FmPcd->p_FmPcdKg->clsPlanGrps[clsPlanGrp].baseEntry;
}

uint16_t FmPcdKgGetClsPlanGrpSize(t_Handle h_FmPcd, uint8_t clsPlanGrp)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;

    return p_FmPcd->p_FmPcdKg->clsPlanGrps[clsPlanGrp].sizeOfGrp;
}

uint8_t FmPcdKgGetSchemeSwId(t_Handle h_FmPcd, uint8_t schemeHwId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint8_t     i;

    for(i=0;i<p_FmPcd->p_FmPcdKg->numOfSchemes;i++)
        if(p_FmPcd->p_FmPcdKg->schemesIds[i] == schemeHwId)
            return i;
    ASSERT_COND(i!=p_FmPcd->p_FmPcdKg->numOfSchemes);
    return FM_PCD_KG_NUM_OF_SCHEMES;
}

uint8_t FmPcdKgGetNumOfPartitionSchemes(t_Handle h_FmPcd)
{
    return ((t_FmPcd*)h_FmPcd)->p_FmPcdKg->numOfSchemes;
}

uint8_t FmPcdKgGetPhysicalSchemeId(t_Handle h_FmPcd, uint8_t relativeSchemeId)
{
    return ((t_FmPcd*)h_FmPcd)->p_FmPcdKg->schemesIds[relativeSchemeId];
}

uint8_t FmPcdKgGetRelativeSchemeId(t_Handle h_FmPcd, uint8_t schemeId)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint8_t     i;

    for(i = 0;i<p_FmPcd->p_FmPcdKg->numOfSchemes;i++)
        if(p_FmPcd->p_FmPcdKg->schemesIds[i] == schemeId)
            return i;

    if(i == p_FmPcd->p_FmPcdKg->numOfSchemes)
        REPORT_ERROR(MAJOR, E_NOT_IN_RANGE, ("Scheme is out of partition range"));

    return FM_PCD_KG_NUM_OF_SCHEMES;
}

t_Error FmPcdKgCcGetSetParams(t_Handle h_FmPcd, t_Handle  h_Scheme, uint32_t requiredAction)
{
    t_FmPcd             *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint8_t             relativeSchemeId, physicalSchemeId;
    uint32_t            tmpKgarReg, tmpReg32 = 0, intFlags;
    t_Error             err;

    SANITY_CHECK_RETURN_VALUE(h_FmPcd, E_INVALID_HANDLE, 0);
    SANITY_CHECK_RETURN_VALUE(p_FmPcd->p_FmPcdKg, E_INVALID_HANDLE, 0);
    SANITY_CHECK_RETURN_VALUE(!p_FmPcd->p_FmPcdDriverParam, E_INVALID_STATE, 0);

    if (p_FmPcd->h_Hc)
        return FmHcPcdKgCcGetSetParams(p_FmPcd->h_Hc,  h_Scheme,  requiredAction);

    physicalSchemeId = (uint8_t)(PTR_TO_UINT(h_Scheme)-1);

    relativeSchemeId = FmPcdKgGetRelativeSchemeId(p_FmPcd, physicalSchemeId);
    if(relativeSchemeId >= FM_PCD_KG_NUM_OF_SCHEMES)
        RETURN_ERROR(MAJOR, E_NOT_IN_RANGE, NO_MSG);

    if (FmPcdKgSchemeTryLock(p_FmPcd, relativeSchemeId, FALSE))
        RETURN_ERROR(MAJOR, E_INVALID_STATE, ("Lock of the scheme FAILED"));

    if(!p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].pointedOwners ||
       !(p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].requiredAction & requiredAction))
    {
        if(requiredAction & UPDATE_NIA_ENQ_WITHOUT_DMA)
        {
            switch(p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].nextEngine)
            {
                case(e_FM_PCD_DONE):
                    if(p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].doneAction == e_FM_PCD_ENQ_FRAME)
                    {
                        tmpKgarReg = FmPcdKgBuildReadSchemeActionReg(physicalSchemeId);
                        intFlags = FmPcdLock(p_FmPcd);
                        WriteKgarWait(p_FmPcd, tmpKgarReg);
                        if (!(GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs.kgse_mode) & KG_SCH_MODE_EN))
                        {
                            RELEASE_LOCK(p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].lock);
                            RETURN_ERROR(MAJOR, E_ALREADY_EXISTS, ("Scheme is Invalid"));
                        }
                        tmpReg32 = GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs.kgse_mode);
                        ASSERT_COND(tmpReg32 & (NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME));
                        WRITE_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs.kgse_mode, tmpReg32 | NIA_BMI_AC_ENQ_FRAME_WITHOUT_DMA);
                        /* call indirect command for scheme write */
                        tmpKgarReg = FmPcdKgBuildWriteSchemeActionReg(physicalSchemeId, FALSE);
                        WriteKgarWait(p_FmPcd, tmpKgarReg);
                        FmPcdUnlock(p_FmPcd, intFlags);
                    }
                break;
                case(e_FM_PCD_PLCR):
                    if(!p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].directPlcr ||
                       (p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].extractedOrs &&
                        p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].bitOffsetInPlcrProfile) ||
                        p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].nextRelativePlcrProfile)
                        {
                            RELEASE_LOCK(p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].lock);
                            RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, ("In this situation PP can not be with distribution and has to be shared"));
                        }
                        err = FmPcdPlcrCcGetSetParams(h_FmPcd, p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].relativeProfileId, requiredAction);
                        if(err)
                        {
                            RELEASE_LOCK(p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].lock);
                            RETURN_ERROR(MAJOR, err, NO_MSG);
                        }
               break;
               default:
                    RETURN_ERROR(MAJOR, E_INVALID_VALUE,("in this situation the next engine after scheme can be or PLCR or ENQ_FRAME"));
            }
        }
    }
    p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].pointedOwners += 1;
    p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].requiredAction |= requiredAction;

    RELEASE_LOCK(p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].lock);
    return E_OK;
}

t_Error FmPcdKgSchemeTryLock(t_Handle h_FmPcd, uint8_t schemeId, bool intr)
{
    t_FmPcd     *p_FmPcd = (t_FmPcd*)h_FmPcd;
    bool        ans;

    if (intr)
        ans = TRY_LOCK(NULL, &p_FmPcd->p_FmPcdKg->schemes[schemeId].lock);
    else
        ans = TRY_LOCK(p_FmPcd->h_Spinlock, &p_FmPcd->p_FmPcdKg->schemes[schemeId].lock);
    if (ans)
        return E_OK;
    return ERROR_CODE(E_BUSY);
}

void FmPcdKgReleaseSchemeLock(t_Handle h_FmPcd, uint8_t schemeId)
{
    RELEASE_LOCK(((t_FmPcd*)h_FmPcd)->p_FmPcdKg->schemes[schemeId].lock);
}

t_Handle FM_PCD_KgSetScheme(t_Handle h_FmPcd,  t_FmPcdKgSchemeParams *p_Scheme)
{
    t_FmPcd                             *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint32_t                            tmpReg;
    t_FmPcdKgInterModuleSchemeRegs      schemeRegs;
    t_FmPcdKgInterModuleSchemeRegs      *p_MemRegs;
    uint8_t                             i;
    t_Error                             err = E_OK;
    uint32_t                            tmpKgarReg;
    uint32_t                            intFlags;
    uint8_t                             physicalSchemeId, relativeSchemeId;

    SANITY_CHECK_RETURN_VALUE(p_FmPcd, E_INVALID_HANDLE, NULL);
    SANITY_CHECK_RETURN_VALUE(p_FmPcd->p_FmPcdKg, E_INVALID_HANDLE, NULL);
    SANITY_CHECK_RETURN_VALUE(!p_FmPcd->p_FmPcdDriverParam, E_INVALID_STATE, NULL);

    if (p_FmPcd->h_Hc)
        return FmHcPcdKgSetScheme(p_FmPcd->h_Hc, p_Scheme);

    /* if not called for modification, check first that this scheme is unused */
    if(!p_Scheme->modify)
    {
        /* check that schemeId is in range */
        if(p_Scheme->id.relativeSchemeId >= p_FmPcd->p_FmPcdKg->numOfSchemes)
        {
            REPORT_ERROR(MAJOR, E_NOT_IN_RANGE, ("Scheme is out of range"));
            return NULL;
        }
        relativeSchemeId = p_Scheme->id.relativeSchemeId;

        if (FmPcdKgSchemeTryLock(p_FmPcd, relativeSchemeId, FALSE))
            return NULL;

        physicalSchemeId = p_FmPcd->p_FmPcdKg->schemesIds[relativeSchemeId];

        /* read specified scheme into scheme registers */
        tmpKgarReg = FmPcdKgBuildReadSchemeActionReg(physicalSchemeId);
        intFlags = FmPcdLock(p_FmPcd);
        WriteKgarWait(p_FmPcd, tmpKgarReg);
        tmpReg = GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs.kgse_mode);
        FmPcdUnlock(p_FmPcd, intFlags);

        if (tmpReg & KG_SCH_MODE_EN)
        {
            REPORT_ERROR(MAJOR, E_ALREADY_EXISTS,
                         ("Scheme %d(phys %d) is already used", relativeSchemeId, physicalSchemeId));
            RELEASE_LOCK(p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].lock);
            return NULL;
        }
    }
    else
    {
        SANITY_CHECK_RETURN_VALUE(p_Scheme->id.h_Scheme, E_INVALID_HANDLE, NULL);

        intFlags = FmPcdLock(p_FmPcd);
        physicalSchemeId = (uint8_t)(PTR_TO_UINT(p_Scheme->id.h_Scheme)-1);
        relativeSchemeId = FmPcdKgGetRelativeSchemeId(p_FmPcd, physicalSchemeId);

        /* check that schemeId is in range */
        if(relativeSchemeId >= FM_PCD_KG_NUM_OF_SCHEMES)
        {
            REPORT_ERROR(MAJOR, E_NOT_IN_RANGE, NO_MSG);
            FmPcdUnlock(p_FmPcd, intFlags);
            return NULL;
        }

        err = FmPcdKgSchemeTryLock(p_FmPcd, relativeSchemeId, TRUE);
        FmPcdUnlock(p_FmPcd, intFlags);
        if (err)
            return NULL;
    }

    err = FmPcdKgBuildScheme(h_FmPcd, p_Scheme, &schemeRegs);
    if(err)
    {
        REPORT_ERROR(MAJOR, err, NO_MSG);
        FmPcdKgInvalidateSchemeSw(h_FmPcd, relativeSchemeId);
        RELEASE_LOCK(p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].lock);
        return NULL;
    }

    /* configure all 21 scheme registers */
    p_MemRegs = &p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs;
    intFlags = FmPcdLock(p_FmPcd);
    WRITE_UINT32(p_MemRegs->kgse_ppc, schemeRegs.kgse_ppc);
    WRITE_UINT32(p_MemRegs->kgse_ccbs, schemeRegs.kgse_ccbs);
    WRITE_UINT32(p_MemRegs->kgse_mode, schemeRegs.kgse_mode);
    WRITE_UINT32(p_MemRegs->kgse_mv, schemeRegs.kgse_mv);
    WRITE_UINT32(p_MemRegs->kgse_dv0, schemeRegs.kgse_dv0);
    WRITE_UINT32(p_MemRegs->kgse_dv1, schemeRegs.kgse_dv1);
    WRITE_UINT32(p_MemRegs->kgse_ekdv, schemeRegs.kgse_ekdv);
    WRITE_UINT32(p_MemRegs->kgse_ekfc, schemeRegs.kgse_ekfc);
    WRITE_UINT32(p_MemRegs->kgse_bmch, schemeRegs.kgse_bmch);
    WRITE_UINT32(p_MemRegs->kgse_bmcl, schemeRegs.kgse_bmcl);
    WRITE_UINT32(p_MemRegs->kgse_hc, schemeRegs.kgse_hc);
    WRITE_UINT32(p_MemRegs->kgse_spc, schemeRegs.kgse_spc);
    WRITE_UINT32(p_MemRegs->kgse_fqb, schemeRegs.kgse_fqb);
    for(i=0 ; i<FM_PCD_KG_NUM_OF_GENERIC_REGS ; i++)
        WRITE_UINT32(p_MemRegs->kgse_gec[i], schemeRegs.kgse_gec[i]);

    /* call indirect command for scheme write */
    tmpKgarReg = FmPcdKgBuildWriteSchemeActionReg(physicalSchemeId, p_Scheme->schemeCounter.update);

    WriteKgarWait(p_FmPcd, tmpKgarReg);
    FmPcdUnlock(p_FmPcd, intFlags);

    FmPcdKgValidateSchemeSw(h_FmPcd, relativeSchemeId);

    RELEASE_LOCK(p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].lock);

    return UINT_TO_PTR((uint64_t)physicalSchemeId+1);
}

t_Error  FM_PCD_KgDeleteScheme(t_Handle h_FmPcd, t_Handle h_Scheme)
{
    t_FmPcd             *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint8_t             physicalSchemeId;
    uint32_t            tmpKgarReg, intFlags;
    t_Error             err = E_OK;
    uint8_t             relativeSchemeId;

    SANITY_CHECK_RETURN_ERROR(p_FmPcd, E_INVALID_HANDLE);
    SANITY_CHECK_RETURN_ERROR(p_FmPcd->p_FmPcdKg, E_INVALID_HANDLE);
    SANITY_CHECK_RETURN_ERROR(!p_FmPcd->p_FmPcdDriverParam, E_INVALID_STATE);

    if (p_FmPcd->h_Hc)
        return FmHcPcdKgDeleteScheme(p_FmPcd->h_Hc, h_Scheme);

    physicalSchemeId = (uint8_t)(PTR_TO_UINT(h_Scheme)-1);
    relativeSchemeId = FmPcdKgGetRelativeSchemeId(p_FmPcd, physicalSchemeId);

    if(relativeSchemeId >= FM_PCD_KG_NUM_OF_SCHEMES)
        RETURN_ERROR(MAJOR, E_NOT_IN_RANGE, NO_MSG);

    if ((err = FmPcdKgSchemeTryLock(p_FmPcd, relativeSchemeId, FALSE)) != E_OK)
       RETURN_ERROR(MINOR, err, NO_MSG);

    /* check that no port is bound to this scheme */
    err = FmPcdKgCheckInvalidateSchemeSw(h_FmPcd, relativeSchemeId);
    if(err)
       RETURN_ERROR(MINOR, err, NO_MSG);

    intFlags = FmPcdLock(p_FmPcd);
    /* clear mode register, including enable bit */
    WRITE_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs.kgse_mode, 0);

    /* call indirect command for scheme write */
    tmpKgarReg = FmPcdKgBuildWriteSchemeActionReg(physicalSchemeId, FALSE);

    WriteKgarWait(p_FmPcd, tmpKgarReg);
    FmPcdUnlock(p_FmPcd, intFlags);

    FmPcdKgInvalidateSchemeSw(h_FmPcd, relativeSchemeId);

    RELEASE_LOCK(p_FmPcd->p_FmPcdKg->schemes[relativeSchemeId].lock);

    return E_OK;
}

uint32_t  FM_PCD_KgGetSchemeCounter(t_Handle h_FmPcd, t_Handle h_Scheme)
{
    t_FmPcd             *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint32_t            tmpKgarReg, spc, intFlags;
    uint8_t             physicalSchemeId;

    SANITY_CHECK_RETURN_VALUE(h_FmPcd, E_INVALID_HANDLE, 0);
    SANITY_CHECK_RETURN_VALUE(p_FmPcd->p_FmPcdKg, E_INVALID_HANDLE, 0);
    SANITY_CHECK_RETURN_VALUE(!p_FmPcd->p_FmPcdDriverParam, E_INVALID_STATE, 0);

    if (p_FmPcd->h_Hc)
        return FmHcPcdKgGetSchemeCounter(p_FmPcd->h_Hc, h_Scheme);

    physicalSchemeId = (uint8_t)(PTR_TO_UINT(h_Scheme)-1);

    if(FmPcdKgGetRelativeSchemeId(p_FmPcd, physicalSchemeId) == FM_PCD_KG_NUM_OF_SCHEMES)
        REPORT_ERROR(MAJOR, E_NOT_IN_RANGE, NO_MSG);

    tmpKgarReg = FmPcdKgBuildReadSchemeActionReg(physicalSchemeId);
    intFlags = FmPcdLock(p_FmPcd);
    WriteKgarWait(p_FmPcd, tmpKgarReg);
    if (!(GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs.kgse_mode) & KG_SCH_MODE_EN))
       REPORT_ERROR(MAJOR, E_ALREADY_EXISTS, ("Scheme is Invalid"));
    spc = GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs.kgse_spc);
    FmPcdUnlock(p_FmPcd, intFlags);

    return spc;
}

t_Error  FM_PCD_KgSetSchemeCounter(t_Handle h_FmPcd, t_Handle h_Scheme, uint32_t value)
{
    t_FmPcd             *p_FmPcd = (t_FmPcd*)h_FmPcd;
    uint32_t            tmpKgarReg, intFlags;
    uint8_t             physicalSchemeId;

    SANITY_CHECK_RETURN_VALUE(h_FmPcd, E_INVALID_HANDLE, 0);
    SANITY_CHECK_RETURN_VALUE(p_FmPcd->p_FmPcdKg, E_INVALID_HANDLE, 0);
    SANITY_CHECK_RETURN_VALUE(!p_FmPcd->p_FmPcdDriverParam, E_INVALID_STATE, 0);

    if (p_FmPcd->h_Hc)
        return FmHcPcdKgSetSchemeCounter(p_FmPcd->h_Hc, h_Scheme, value);

    physicalSchemeId = (uint8_t)(PTR_TO_UINT(h_Scheme)-1);
    /* check that schemeId is in range */
    if(FmPcdKgGetRelativeSchemeId(p_FmPcd, physicalSchemeId) == FM_PCD_KG_NUM_OF_SCHEMES)
        REPORT_ERROR(MAJOR, E_NOT_IN_RANGE, NO_MSG);

    /* read specified scheme into scheme registers */
    tmpKgarReg = FmPcdKgBuildReadSchemeActionReg(physicalSchemeId);
    intFlags = FmPcdLock(p_FmPcd);
    WriteKgarWait(p_FmPcd, tmpKgarReg);
    if (!(GET_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs.kgse_mode) & KG_SCH_MODE_EN))
    {
       FmPcdUnlock(p_FmPcd, intFlags);
       RETURN_ERROR(MAJOR, E_ALREADY_EXISTS, ("Scheme is Invalid"));
    }

    /* change counter value */
    WRITE_UINT32(p_FmPcd->p_FmPcdKg->p_FmPcdKgRegs->indirectAccessRegs.schemeRegs.kgse_spc, value);

    /* call indirect command for scheme write */
    tmpKgarReg = FmPcdKgBuildWriteSchemeActionReg(physicalSchemeId, TRUE);

    WriteKgarWait(p_FmPcd, tmpKgarReg);
    FmPcdUnlock(p_FmPcd, intFlags);

    return E_OK;
}