Newer
Older
mbed-os / targets / TARGET_Cypress / TARGET_PSOC6 / mtb-pdl-cat1 / drivers / source / edd_rx.c
@Dustin Crossman Dustin Crossman on 4 Jun 2021 79 KB Fix file modes.
/******************************************************************************
 * copyright (C) 2014-2015 Cadence Design Systems
 * All rights reserved.
 ******************************************************************************
 * edd_rx.c
 * Ethernet DMA MAC Driver
 *
 * Rx-related functions source file
 *****************************************************************************/

#include "cy_device.h"

#if defined (CY_IP_MXETH)

#include "cdn_stdint.h"
#include "cdn_errno.h"
#include "log.h"
#include "cps_v2.h"
#include "emac_regs.h"
#include "cedi.h"
#include "edd_int.h"

#ifdef __cplusplus
extern "C" {
#endif
/******************************************************************************
 * Driver API functions
 *****************************************************************************/

/* Identify max Rx pkt size for queues - determined by size of Rx packet buffer
 * (if using full store & forward mode), and the current maximum frame size,
 * e.g. 1518, 1536 or jumbo frame.
 * @param pD - driver private state info specific to this instance
 * @param maxSize - pointer for returning max frame size, same for each Rx queue
 * @return 0 if successful
 * @return EINVAL if invalid parameters
 */
uint32_t emacCalcMaxRxFrameSize(void *pD, uint32_t *maxSize) {
    uint16_t ram_word_size, ram_addr_bits;
    uint32_t ram_size, max_size, tmp;
    uint8_t enabled = 0;
    uint16_t length;

    if ((pD==NULL) || (maxSize==NULL)) return EINVAL;

    if (0!=emacGetJumboFramesRx(pD,&enabled))
        return EINVAL;

    if (enabled) {
        if (0!=emacGetJumboFrameRxMaxLen(pD, &length))
            return EINVAL;
        max_size = length;
    } else {
        if (0!=emacGet1536ByteFramesRx(pD,&enabled))
            return EINVAL;
        if (enabled)
            max_size = 1536;
        else
            max_size = 1518;
    }

    if (0!=emacGetRxPartialStFwd(pD, &tmp, &enabled))
        return EINVAL;

    if ((!enabled) && CEDI_PdVar(hwCfg).rx_pkt_buffer)
    {
        // What is word size of SRAM in bytes
        ram_word_size = (CEDI_PdVar(hwCfg).rx_pbuf_data >> 1)+1;
        //vDbgMsg(DBG_GEN_MSG, 10, "RAM word size = %u (x32 bits)\n", CEDI_PdVar(hwCfg).rx_pbuf_data);
        ram_addr_bits = CEDI_PdVar(hwCfg).rx_pbuf_addr;
        //vDbgMsg(DBG_GEN_MSG, 10, "RAM Rx addr bits = %u\n", ram_addr_bits);

        ram_size = (1<<(ram_addr_bits + ram_word_size + 1)) - 96;
        vDbgMsg(DBG_GEN_MSG, 10, "RAM size = %u\n", ram_size);

        if (ram_size<max_size)
            max_size = ram_size;
    }
    vDbgMsg(DBG_GEN_MSG, 10, "Max Rx frame size = %u\n", max_size);

    *maxSize = max_size;
    return 0;
}

/* Add a buffer (size determined by rxBufLength in CEDI_Config) to the end of
 * the receive buffer queue.  This function is intended to be used during
 * setting up the receive buffers, and should not be called while Rx is
 * enabled or unread data remains in the queue.
 * @param pD - driver private state info specific to this instance
 * @param queueNum - number of the Rx queue (range 0 to rxQs-1)
 * @param buf - pointer to struct for virtual and physical addresses of buffer.
 *      Physical address checked for word-alignment in 64/128-bit width cases.
 * @param init - if <>0 then initialise the buffer data to all zeros
 * @return 0 if successful, EINVAL if invalid queueNum, buffer alignment, or
 *    bufStart pointer/addresses
 */
uint32_t emacAddRxBuf(void *pD, uint8_t queueNum, CEDI_BuffAddr *buf, uint8_t init)
{
    uint32_t tmp, bufLenWords;
    rxQueue_t *rxQ;

    if (!pD) return EINVAL;

    if (queueNum>=(CEDI_PdVar(cfg)).rxQs) {
        vDbgMsg(DBG_GEN_MSG, 5, "Error: Invalid Rx queue number: %u\n", queueNum);
        return EINVAL;
    }

    if ((buf==0) || (buf->vAddr==0) || (buf->pAddr==0)) {
        vDbgMsg(DBG_GEN_MSG, 5, "%s\n", "Error: NULL buf parameter");
        return EINVAL;
    }
//    vDbgMsg(DBG_GEN_MSG, 10, "%s entered (regBase %08X) bufV=0x%08X bufP=0x%08X\n", __func__,
//            CEDI_PdVar(cfg).regBase, buf->vAddr, buf->pAddr);

    rxQ = &(CEDI_PdVar(rxQueue[queueNum]));

    if (rxQ->numRxBufs>=((CEDI_PdVar(cfg)).rxQLen[queueNum])) {
        vDbgMsg(DBG_GEN_MSG, 5, "%s\n", "Error: Rx descriptor list full");
        return EINVAL;
    }

    /* alignment checking */
    switch (CEDI_PdVar(cfg).dmaBusWidth) {
    case CEDI_DMA_BUS_WIDTH_32:
        tmp = 4; break;
    case CEDI_DMA_BUS_WIDTH_64:
        tmp = 8; break;
    case CEDI_DMA_BUS_WIDTH_128:
        tmp = 16; break;
    default: tmp = 4; break;
    }
    if ((buf->pAddr)%tmp) {
        vDbgMsg(DBG_GEN_MSG, 5, "%s\n", "Error: Rx buffer not word-aligned");
        return EINVAL;
    }

    /* save virtual address */
    *(rxQ->rxEndVA) = buf->vAddr;

    bufLenWords = (CEDI_PdVar(cfg).rxBufLength[queueNum])<<4;
    if (init)
        for (tmp=0; tmp<bufLenWords; tmp++)
            CPS_UncachedWrite32((uint32_t *)(buf->vAddr)+tmp, 0);

    /* clear wrap & used on old end, add new buffer */
    CPS_UncachedWrite32(&(rxQ->rxDescEnd->word[0]),
                            buf->pAddr & CEDI_RXD_ADDR_MASK);
    /* upper 32 bits if 64 bit addressing */
    if (CEDI_PdVar(cfg).dmaAddrBusWidth) {
#ifdef CEDI_64B_COMPILE
        /* 64-bit addressing */
        CPS_UncachedWrite32(&(rxQ->rxDescEnd->word[2]),
                             (buf->pAddr & 0xFFFFFFFF00000000)>>32);
#else
        /* 32-bit addressing */
        CPS_UncachedWrite32(&(rxQ->rxDescEnd->word[2]), 0x00000000);
#endif
    }

    /* put known pattern into word[1] for debugging */
    CPS_UncachedWrite32(&(rxQ->rxDescEnd->word[1]), CEDI_RXD_EMPTY);

    /* inc end & stop pointer */
    rxQ->rxDescEnd = (rxDesc *)(((uintptr_t)rxQ->rxDescEnd) +
            (CEDI_PdVar(rxDescriptorSize)));
    rxQ->rxDescStop = rxQ->rxDescEnd;

    /* inc VA end & stop pointers & buffer count */
    rxQ->rxEndVA++;
    rxQ->rxStopVA++;
    *(rxQ->rxStopVA) = 0;
    rxQ->numRxBufs++;

    /* write new end(-stop) descriptor */
    CPS_UncachedWrite32(&(rxQ->rxDescEnd->word[0]),
                             CEDI_RXD_WRAP | CEDI_RXD_USED );

    return 0;
}

/* Get the number of useable buffers/descriptors present in the specified
 * Rx queue, excluding the end-stop descriptor.
 * @param pD - driver private state info specific to this instance
 * @param queueNum - number of the Rx queue (range 0 to rxQs-1)
 * @param numBufs - pointer for returning number of descriptors
 * @return 0 if successful
 * @return EINVAL if invalid parameter
 */
uint32_t emacNumRxBufs(void *pD, uint8_t queueNum, uint16_t *numBufs)
{
    if ((pD==NULL) || (numBufs==NULL) || (queueNum>=(CEDI_PdVar(cfg)).rxQs))
        return EINVAL;

    *numBufs = (CEDI_PdVar(rxQueue[queueNum])).numRxBufs;
    return 0;
}

/* Get the number of buffers/descriptors marked "used" in the specified Rx
 *   queue (excluding unuseable end-stop), i.e. those holding unread data.
 * @param pD - driver private state info specific to this instance
 * @param queueNum - number of the Rx queue (range 0 to rxQs-1)
 * @return number of used buffers
 * @return 0 if invalid parameter
 */
uint32_t emacNumRxUsed(void *pD, uint8_t queueNum)
{
    uint32_t tmp, thisWd, count=0;
    rxDesc *thisDesc;
    rxQueue_t *rxQ;

    if ((pD==NULL) || (queueNum>=(CEDI_PdVar(cfg)).rxQs))
        return 0;

    rxQ = &(CEDI_PdVar(rxQueue[queueNum]));
    /* count forward from tail, until used not set */
    thisDesc =  rxQ->rxDescTail;
    for (tmp = 0; tmp<rxQ->numRxBufs; tmp++)
    {
        thisWd = CPS_UncachedRead32(&(thisDesc->word[0]));
        if (thisWd & CEDI_RXD_USED)
            count++;
        else
            break;
        if (thisWd & CEDI_RXD_WRAP)
            thisDesc = rxQ->rxDescStart;
        else
            thisDesc = (rxDesc *)(((uintptr_t)(thisDesc))
                                    + (CEDI_PdVar(rxDescriptorSize)));
    }
    return count;
}

/**
 * Read first unread descriptor (at tail of queue): if new data is available
 * it swaps out the buffer and replaces it with a new one, clears the
 * descriptor for re-use, then updates the driver queue-pointer.
 * Checks for Start Of Frame (SOF) and End Of Frame (EOF) flags in the
 * descriptors, passing back in return value.
 * If EOF set, the descriptor status is returned via rxDescStat.
 * @param[in] pD driver private state info specific to this instance
 * @param[in] queueNum
 *   number of the Rx queue
 * @param[in,out] buf pointer to address of memory for new buffer to add to Rx
 *   descriptor queue, if data is available the buffer addresses for this are
 *   returned in buf, else if no data available the new buffer can be re-used.
 *   Physical address of buffer is checked for word-alignment in 64/128-bit
 *   width cases.
 * @param[in] init if >0 then initialise the (new) buffer data to all zeros.
 *    Ignored if no data available.
 * @param[out] descData pointer for returning status & descriptor data
 *   Struct fields:
 *
 *    uint32_t rxDescStat  - Rx descriptor status word
 *
 *    uint8_t status    - Rx data status, one of the following values:
 *      CEDI_RXDATA_SOF_EOF  :data available, single-buffer frame (SOF & EOF
 *                            set)
 *      CEDI_RXDATA_SOF_ONLY :data available, start of multi-buffer frame
 *      CEDI_RXDATA_NO_FLAG  :data available, intermediate buffer of multi-
 *                            buffer frame
 *      CEDI_RXDATA_EOF_ONLY :data available, end of multi-buffer frame
 *      CEDI_RXDATA_NODATA   :no data available
 *
 *    CEDI_TimeStampData rxTsData - Rx descriptor timestamp when valid
 *                                  (rxTsData->tsValid will be set to 1)
 *
 * @return 0 if successful,
 * @return EINVAL if invalid queueNum, buf, rxDescStat or
 *    status parameters
 */
uint32_t emacReadRxBuf(void *pD, uint8_t queueNum, CEDI_BuffAddr *buf,
                        uint8_t init, CEDI_RxDescData *descData)
{
    uint32_t tmp, bufLenWords, descWd0;
    CEDI_BuffAddr oldbuf;
    uint8_t wdNum, tailWrap;
    uint32_t tsLowerWd, tsUpperWd;
    rxQueue_t *rxQ;

    if (!pD) return EINVAL;

//    vDbgMsg(DBG_GEN_MSG, 10, "%s entered (regBase %08lX)\n", __func__,
//                CEDI_PdVar(cfg).regBase);

    if (buf==NULL) {
        vDbgMsg(DBG_GEN_MSG, 5, "%s\n", "Error: NULL buf parameter");
        return EINVAL;
    }

    if (descData==NULL) {
        vDbgMsg(DBG_GEN_MSG, 5, "%s\n", "Error: NULL descData parameter");
        return EINVAL;
    }

    if (queueNum>=(CEDI_PdVar(cfg)).rxQs) {
        vDbgMsg(DBG_GEN_MSG, 5, "Error: Invalid Rx queue number - %u\n", queueNum);
        return EINVAL;
    }

    if ((buf->vAddr==0) || (buf->pAddr==0)) {
        vDbgMsg(DBG_GEN_MSG, 5, "%s\n", "Error: NULL buf address");
        return EINVAL;
    }

    /* alignment checking for new buffer */
    switch (CEDI_PdVar(cfg).dmaBusWidth) {
    case CEDI_DMA_BUS_WIDTH_32:
        tmp = 4; break;
    case CEDI_DMA_BUS_WIDTH_64:
        tmp = 8; break;
    case CEDI_DMA_BUS_WIDTH_128:
        tmp = 16; break;
    default: tmp = 4; break;
    }
    if ((buf->pAddr)%tmp) {
        vDbgMsg(DBG_GEN_MSG, 5, "%s\n", "Error: Rx buffer not word-aligned");
        return EINVAL;
    }

    rxQ = &(CEDI_PdVar(rxQueue[queueNum]));

    /* get first descriptor & test used bit */
    descWd0 = CPS_UncachedRead32(&(rxQ->rxDescTail->word[0]));

    if (descWd0 & CEDI_RXD_USED) {
        /* new data received - read & process descriptor */

        /* get old physical address */
        oldbuf.pAddr = descWd0 & CEDI_RXD_ADDR_MASK;

#ifdef CEDI_64B_COMPILE
        /* upper 32 bits if 64 bit addressing */
        if (CEDI_PdVar(cfg).dmaAddrBusWidth)
            oldbuf.pAddr |= ((uint64_t)CPS_UncachedRead32(
                                        &(rxQ->rxDescTail->word[2])))<<32;
#endif

        /* get old virtual address & clear from list */
        oldbuf.vAddr = *(rxQ->rxTailVA);
        *(rxQ->rxTailVA) = 0;

        /* save new virtual address */
        *(rxQ->rxStopVA) = buf->vAddr;

        bufLenWords = (CEDI_PdVar(cfg).rxBufLength[queueNum])<<4;
        if (init)
            for (tmp=0; tmp<bufLenWords; tmp++)
                CPS_UncachedWrite32((uint32_t *)(buf->vAddr+4*tmp), 0);

        /* read Rx status */
        descData->rxDescStat = CPS_UncachedRead32(&(rxQ->rxDescTail->word[1]));

        /* extract timestamp if available */
        if ((CEDI_PdVar(cfg).enRxExtBD) && (descWd0 & CEDI_RXD_TS_VALID)) {
        uint32_t reg;
            descData->rxTsData.tsValid = 1;
                        // position depends on 32/64 bit addr
            wdNum = (CEDI_PdVar(cfg).dmaAddrBusWidth)?4:2;

            tsLowerWd = CPS_UncachedRead32(&(rxQ->rxDescTail->word[wdNum]));
            tsUpperWd = CPS_UncachedRead32(&(rxQ->rxDescTail->word[wdNum+1]));

            descData->rxTsData.tsNanoSec = tsLowerWd & CEDI_TS_NANO_SEC_MASK;
            descData->rxTsData.tsSecs =
                    (((tsUpperWd & CEDI_TS_SEC1_MASK)<<CEDI_TS_SEC1_POS_SHIFT)
                        | (tsLowerWd >> CEDI_TS_SEC0_SHIFT));

        /* The timestamp only contains lower few bits of seconds, so add value from 1588 timer */
        reg =  CPS_UncachedRead32(CEDI_RegAddr(tsu_timer_sec));
        /* If the top bit is set in the timestamp, but not in 1588 timer, it has rolled over, so subtract max size */
        if ((descData->rxTsData.tsSecs & (CEDI_TS_SEC_TOP>>1)) && !(reg & (CEDI_TS_SEC_TOP>>1))) {
        descData->rxTsData.tsSecs -= (CEDI_TS_SEC_TOP<<1);

        }
        descData->rxTsData.tsSecs += ((~CEDI_TS_SEC_MASK) & EMAC_REGS__TSU_TIMER_SEC__TIMER__READ(reg));
    }
    else 
    {
        descData->rxTsData.tsValid = 0;
    }

        /* save this for later */
        tailWrap = descWd0 & CEDI_RXD_WRAP;

                /* write back to descriptors */
        CPS_UncachedWrite32(&(rxQ->rxDescTail->word[1]), CEDI_RXD_EMPTY);
        /* zero buf physical address & set used - this will be new end-stop */
        CPS_UncachedWrite32(&(rxQ->rxDescTail->word[0]),
                                CEDI_RXD_USED | (tailWrap?CEDI_RXD_WRAP:0));

        /* handle old "stop" descriptor now */
        /* insert new buf physical address & clear used */
        descWd0 = CPS_UncachedRead32(&(rxQ->rxDescStop->word[0]));
        descWd0 = ((buf->pAddr) & CEDI_RXD_ADDR_MASK) |
                    (descWd0 & CEDI_RXD_WRAP);
        CPS_UncachedWrite32(&(rxQ->rxDescStop->word[0]), descWd0);
        /* upper 32 bits if 64 bit addressing */
        if (CEDI_PdVar(cfg).dmaAddrBusWidth) {
#ifdef CEDI_64B_COMPILE
            /* 64-bit addressing */
            CPS_UncachedWrite32(&(rxQ->rxDescStop->word[2]),
                             (buf->pAddr & 0xFFFFFFFF00000000)>>32);
#else
            /* 32-bit addressing */
            CPS_UncachedWrite32(&(rxQ->rxDescStop->word[2]), 0x00000000);
#endif
        }

        /* update pointers */
        rxQ->rxDescStop = rxQ->rxDescTail;
        rxQ->rxStopVA = rxQ->rxTailVA;
        if (tailWrap) {
            rxQ->rxDescTail = rxQ->rxDescStart;
            rxQ->rxTailVA = rxQ->rxBufVAddr;
        }
        else {
            rxQ->rxDescTail = (rxDesc *)(((uintptr_t)(rxQ->rxDescTail))
                                        + (CEDI_PdVar(rxDescriptorSize)));
            rxQ->rxTailVA++;
        }

        /* return old buffer addresses */
        buf->pAddr = oldbuf.pAddr;
        buf->vAddr = oldbuf.vAddr;
/*        vDbgMsg(DBG_GEN_MSG, 10, "%s vAddr=%p pAddr=%p\n",
                      __func__, (void *)buf->vAddr, (void *)buf->pAddr);*/


        /* work out read frame status */
        if ((descData->rxDescStat) & CEDI_RXD_SOF) {
            if ((descData->rxDescStat) & CEDI_RXD_EOF)
                descData->status = CEDI_RXDATA_SOF_EOF;
            else
                descData->status = CEDI_RXDATA_SOF_ONLY;
        }
        else
        {
            if ((descData->rxDescStat) & CEDI_RXD_EOF)
                descData->status = CEDI_RXDATA_EOF_ONLY;
            else
                descData->status = CEDI_RXDATA_NO_FLAG;
        }
    }
    else
        descData->status = CEDI_RXDATA_NODATA;

    return 0;
}

/* Decode the Rx descriptor status into a bit-field struct
 * @param pD - driver private state info specific to this instance
 * @param rxDStatWord - Rx descriptor status word
 * @param rxDStat - pointer to bit-field struct for decoded status fields
 */
void emacGetRxDescStat(void *pD, uint32_t rxDStatWord, CEDI_RxDescStat *rxDStat)
{
    uint32_t reg, wd1;

    if ((NULL==pD) || (NULL==rxDStat)) return;

    reg = CPS_UncachedRead32(CEDI_RegAddr(network_config));

    wd1 = rxDStatWord;
    rxDStat->bufLen = wd1 & CEDI_RXD_LEN_MASK;
    if (EMAC_REGS__NETWORK_CONFIG__JUMBO_FRAMES__READ(reg) ||
        (EMAC_REGS__NETWORK_CONFIG__IGNORE_RX_FCS__READ(reg)==0)) {
        rxDStat->bufLen |= wd1 & CEDI_RXD_LEN13_FCS_STAT;
        rxDStat->fcsStatus = 0;
    }
    else
        rxDStat->fcsStatus = (wd1 & CEDI_RXD_LEN13_FCS_STAT)?1:0;

    rxDStat->sof = (wd1 & CEDI_RXD_SOF)?1:0;
    rxDStat->eof = (wd1 & CEDI_RXD_EOF)?1:0;
    rxDStat->header = (!rxDStat->eof && (wd1 & CEDI_RXD_HDR))?1:0;
    rxDStat->eoh = (!rxDStat->eof && (wd1 & CEDI_RXD_EOH))?1:0;
    rxDStat->vlanTagDet = (wd1 & CEDI_RXD_VLAN_TAG)?1:0;
    rxDStat->cfi = ((wd1 & CEDI_RXD_CFI) && rxDStat->vlanTagDet)?1:0;
    if (rxDStat->vlanTagDet)
        rxDStat->vlanPri =
                (wd1 & CEDI_RXD_VLAN_PRI_MASK)>>CEDI_RXD_VLAN_PRI_SHIFT;
    else
        rxDStat->vlanPri = 0;
    rxDStat->priTagDet = (wd1 & CEDI_RXD_PRI_TAG)?1:0;
    if (EMAC_REGS__NETWORK_CONFIG__RECEIVE_CHECKSUM_OFFLOAD_ENABLE__READ(reg)) {
        rxDStat->chkOffStat = (wd1 & CEDI_RXD_TYP_IDR_CHK_STA_MASK)\
                                    >>CEDI_RXD_TYP_IDR_CHK_STA_SHIFT;
        rxDStat->snapNoVlanCfi = (wd1 & CEDI_RXD_TYP_MAT_SNP_NCFI)?1:0;
        rxDStat->typeMatchReg = 0;
        rxDStat->typeIdMatch = 0;
    }
    else {
        rxDStat->chkOffStat = 0;
        rxDStat->snapNoVlanCfi = 0;
        rxDStat->typeMatchReg = (wd1 & CEDI_RXD_TYP_IDR_CHK_STA_MASK)\
                                    >>CEDI_RXD_TYP_IDR_CHK_STA_SHIFT;
        rxDStat->typeIdMatch = (wd1 & CEDI_RXD_TYP_MAT_SNP_NCFI)?1:0;
    }

    rxDStat->specAddReg = (wd1 & CEDI_RXD_SPEC_REG_MASK)\
                                >>CEDI_RXD_SPEC_REG_SHIFT;
    if (CEDI_PdVar(hwCfg).rx_pkt_buffer &&
            (CEDI_PdVar(hwCfg).num_spec_add_filters>4))
    {   /* extra spec. addr matching variation */
        rxDStat->specAddReg += ((wd1 & CEDI_RXD_SPEC_ADD_MAT)?1:0) << 2;
        rxDStat->specAddMatch = (wd1 & CEDI_RXD_EXT_ADD_MAT)?1:0;
        rxDStat->extAddrMatch = 0;
    }
    else
    {
        rxDStat->specAddMatch = (wd1 & CEDI_RXD_SPEC_ADD_MAT)?1:0;
        rxDStat->extAddrMatch = (wd1 & CEDI_RXD_EXT_ADD_MAT)?1:0;
    }
    rxDStat->uniHashMatch = (wd1 & CEDI_RXD_UNI_HASH_MAT)?1:0;
    rxDStat->multiHashMatch = (wd1 & CEDI_RXD_MULTI_HASH_MAT)?1:0;
    rxDStat->broadcast = (wd1 & (uint32_t)CEDI_RXD_BROADCAST_DET)?1:0;
}

/* Provide the size of descriptor calculated for the current configuration.
 * @param pD - driver private state info specific to this instance
 * @param rxDescSize - pointer to Rx descriptor Size
 */
void emacGetRxDescSize(void *pD, uint32_t *rxDescSize)
{
    if ((pD==NULL)||(rxDescSize==NULL))
        return;
    *rxDescSize = CEDI_PdVar(rxDescriptorSize);
}

/* Get state of receiver
 * @param pD - driver private state info specific to this instance
 * @return 1 if enabled
 * @return 0 if disabled or pD==NULL
 */
uint32_t emacRxEnabled(void *pD)
{
    uint32_t reg;
    if (pD==NULL) return 0;
    reg = CPS_UncachedRead32(CEDI_RegAddr(network_control));
    return EMAC_REGS__NETWORK_CONTROL__ENABLE_RECEIVE__READ(reg);
}

/* Enable the receive circuit.
 * @param pD - driver private state info specific to this instance
 */
void emacEnableRx(void *pD)
{
    uint32_t reg;
    if (!pD) return;
    reg = CPS_UncachedRead32(CEDI_RegAddr(network_control));
    EMAC_REGS__NETWORK_CONTROL__ENABLE_RECEIVE__SET(reg);
    CPS_UncachedWrite32(CEDI_RegAddr(network_control), reg);
}

/* Disable the receive circuit.
 * @param pD - driver private state info specific to this instance
 */
void emacDisableRx(void *pD)
{
    uint32_t reg;
    if (!pD) return;
    reg = CPS_UncachedRead32(CEDI_RegAddr(network_control));
    EMAC_REGS__NETWORK_CONTROL__ENABLE_RECEIVE__CLR(reg);
    CPS_UncachedWrite32(CEDI_RegAddr(network_control), reg);
}

/* Remove a buffer from the end of the receive buffer queue.  This function is
 * intended to be used when shutting down the driver, prior to deallocating the
 * receive buffers, and should not be called while Rx is enabled or unread
 * data remains in the queue.
 * @param pD - driver private state info specific to this instance
 * @param queueNum - number of the Rx queue (range 0 to rxQs-1)
 * @param buf - pointer to struct for returning virtual and physical addresses
 *  of buffer.
 * @return 0 if successful
 * @return EINVAL if invalid queueNum, ENOENT if no buffers left to free
 */
uint32_t emacRemoveRxBuf(void *pD, uint8_t queueNum, CEDI_BuffAddr *buf)
{
    uint32_t tmp;
    rxQueue_t *rxQ;

    if (!pD) return EINVAL;

    if (queueNum>=(CEDI_PdVar(cfg)).rxQs) {
        vDbgMsg(DBG_GEN_MSG, 5, "Error: Invalid Rx queue number: %u\n", queueNum);
        return EINVAL;
    }

    if (buf==0) {
        vDbgMsg(DBG_GEN_MSG, 5, "%s\n", "Error: NULL buf parameter");
        return EINVAL;
    }

    rxQ = &(CEDI_PdVar(rxQueue[queueNum]));

    if (0==rxQ->numRxBufs)
        return ENOENT;

    /* skip "stop" descriptor since no buffer there */
    if ((rxQ->rxDescEnd==rxQ->rxDescStop) && (rxQ->rxDescEnd!=rxQ->rxDescStart))
    {
        rxQ->rxDescEnd = (rxDesc*)
                ((uintptr_t)(rxQ->rxDescEnd) - CEDI_PdVar(rxDescriptorSize));
    rxQ->rxEndVA--;
    }

    /* get physical address */
    buf->pAddr = CPS_UncachedRead32(&(rxQ->rxDescEnd->word[0]))
                                            & CEDI_RXD_ADDR_MASK;
    /* get virtual address */
    buf->vAddr = *(rxQ->rxEndVA);

    /* dec end/tail pointers unless already at start of list */
    if (rxQ->rxDescEnd!=rxQ->rxDescStart) {
        rxQ->rxDescEnd = (rxDesc*)
                ((uintptr_t)(rxQ->rxDescEnd) - CEDI_PdVar(rxDescriptorSize));
        rxQ->rxEndVA--;

        /* set wrap on new end descriptor */
        tmp = CPS_UncachedRead32(&(rxQ->rxDescEnd->word[0]));
        CPS_UncachedWrite32(&(rxQ->rxDescEnd->word[0]), tmp | CEDI_RXD_WRAP);
    }
    rxQ->numRxBufs--;

    return 0;
}

void emacFindQBaseAddr(void *pD, uint8_t queueNum, rxQueue_t *rxQ,
                        uint32_t *pAddr, uintptr_t *vAddr) {
    uint8_t q = 0;
    /* find start addresses for this rxQ */
    *vAddr = CEDI_PdVar(cfg).rxQAddr;
    *pAddr = CEDI_PdVar(cfg).rxQPhyAddr;

    if (queueNum>0)
        rxQ->rxBufVAddr = (CEDI_PdVar(rxQueue[0]).rxBufVAddr);
    while (q<queueNum) {
        *vAddr += (rxQ->numRxDesc)*(CEDI_PdVar(rxDescriptorSize));//sizeof(rxDesc);
        *pAddr += (rxQ->numRxDesc)*(CEDI_PdVar(rxDescriptorSize));//sizeof(rxDesc);
        rxQ->rxBufVAddr += rxQ->numRxDesc;
        q++;
    }
    vDbgMsg(DBG_GEN_MSG, 10, "%s: base address Q%u virt=%08lX phys=%08X vAddrList=%p\n",
            __func__, queueNum, *vAddr, *pAddr, rxQ->rxBufVAddr);
}
/* Reset Rx buffer descriptor list/ buffer virtual address list to initial
 * empty state, clearing all descriptors.  For use by init or after a fatal
 * error. Disables receive circuit.
 * @param pD - driver private state info specific to this instance
 * @param queueNum - number of the Rx queue (range 0 to rxQs-1)
 * @param ptrsOnly - if =1, then reset pointers and clearing used bits only
 *          after a link down/up event (assume buffers already assigned)
 *          if =0, initialise all list fields for this queue, including
 *          clearing buffer addresses
 * @return 0 if successful
 * @return EINVAL if invalid parameter
 */
uint32_t emacResetRxQ(void *pD, uint8_t queueNum, uint8_t ptrsOnly)
{
#define CEDI_WR_RXQ_PTR_REG_N_CASE(Q) case Q:\
        CPS_UncachedWrite32(CEDI_RegAddr(receive_q##Q##_ptr), regTmp);\
        break;

    uint32_t regTmp;
    uint16_t i;
    uint32_t pAddr;
    uintptr_t vAddr;
    rxDesc* descPtr;
    rxQueue_t *rxQ;

    if ((pD==NULL) || (queueNum>=CEDI_PdVar(cfg).rxQs) || (ptrsOnly>1))
        return EINVAL;

    emacDisableRx(pD);

    rxQ = &(CEDI_PdVar(rxQueue[queueNum]));
    emacFindQBaseAddr(pD, queueNum, rxQ, &pAddr, &vAddr);

    /* want the virtual addresses here: */
    if (ptrsOnly) {
        if (rxQ->rxDescStop!=rxQ->rxDescEnd) {
        /* copy buffer addresses from new "stop" descriptor to old one,
         * before reset pointers */
            CPS_UncachedWrite32((uint32_t *)&(rxQ->rxDescStop->word[0]),
                CPS_UncachedRead32((uint32_t *)&(rxQ->rxDescEnd->word[0])));
            *(rxQ->rxStopVA) = *(rxQ->rxEndVA);
        }
    }
    else
    {
    rxQ->rxDescStart = (rxDesc *)vAddr;
        rxQ->rxDescEnd = (rxDesc *)vAddr;
    }
    rxQ->rxDescStop = (rxDesc *)vAddr;
    rxQ->rxDescTail = (rxDesc *)vAddr;
    rxQ->rxTailVA = rxQ->rxBufVAddr;
    rxQ->rxStopVA = rxQ->rxBufVAddr;
    if (!ptrsOnly) {
        rxQ->rxEndVA = rxQ->rxBufVAddr;
        *(rxQ->rxStopVA) = 0;
        rxQ->numRxBufs = 0;
    }

    /* full reset: clear used flags except stop & set wrap flag, only expand
     * available size as buffers are added - if ptrsOnly, then buffers already
     * in ring, preserve addresses & only clear used bits/wd1  */
    descPtr = rxQ->rxDescStart;
    for (i = 0; i<rxQ->numRxDesc; i++) {
        if (ptrsOnly) {
            if (rxQ->rxDescStop==rxQ->rxDescEnd) {
                CPS_UncachedWrite32((uint32_t *)&(rxQ->rxDescStop->word[0]),
                                          CEDI_RXD_WRAP|CEDI_RXD_USED );
                CPS_UncachedWrite32(&(rxQ->rxDescStop->word[1]), CEDI_RXD_EMPTY);
                *(rxQ->rxStopVA) = 0;
            }
            else
            {
                pAddr = CPS_UncachedRead32((uint32_t *)&(rxQ->rxDescStop->word[0]));
                CPS_UncachedWrite32((uint32_t *)&(rxQ->rxDescStop->word[0]),
                                    pAddr & ~(CEDI_RXD_WRAP|CEDI_RXD_USED));
                CPS_UncachedWrite32(&(rxQ->rxDescStop->word[1]), CEDI_RXD_EMPTY);
                /* inc stop pointer */
                rxQ->rxDescStop = (rxDesc *)(((uintptr_t)rxQ->rxDescStop) +
                                    (CEDI_PdVar(rxDescriptorSize)));
                /* inc VA stop pointer */
                rxQ->rxStopVA++;
            }
        }
        else {
        CPS_UncachedWrite32((uint32_t *)
                &(descPtr->word[0]), i?0:CEDI_RXD_WRAP|CEDI_RXD_USED);
        CPS_UncachedWrite32((uint32_t *)
                &(descPtr->word[1]), CEDI_RXD_EMPTY);
            descPtr = (rxDesc*) (((uintptr_t)(descPtr)) +
                (CEDI_PdVar(rxDescriptorSize)));
        }
    }

    if (!ptrsOnly) {
        /* write hardware base address register */
        regTmp = 0;
        EMAC_REGS__RECEIVE_Q_PTR__DMA_RX_Q_PTR__MODIFY(regTmp, pAddr>>2);
        switch (queueNum) {
        case 0:
            CPS_UncachedWrite32(CEDI_RegAddr(receive_q_ptr), regTmp);
            break;
        CEDI_WR_RXQ_PTR_REG_N_CASE(1);
        CEDI_WR_RXQ_PTR_REG_N_CASE(2);
        CEDI_WR_RXQ_PTR_REG_N_CASE(3);
        CEDI_WR_RXQ_PTR_REG_N_CASE(4);
        CEDI_WR_RXQ_PTR_REG_N_CASE(5);
        CEDI_WR_RXQ_PTR_REG_N_CASE(6);
        CEDI_WR_RXQ_PTR_REG_N_CASE(7);
        CEDI_WR_RXQ_PTR_REG_N_CASE(8);
        CEDI_WR_RXQ_PTR_REG_N_CASE(9);
        CEDI_WR_RXQ_PTR_REG_N_CASE(10);
        CEDI_WR_RXQ_PTR_REG_N_CASE(11);
        CEDI_WR_RXQ_PTR_REG_N_CASE(12);
        CEDI_WR_RXQ_PTR_REG_N_CASE(13);
        CEDI_WR_RXQ_PTR_REG_N_CASE(14);
        CEDI_WR_RXQ_PTR_REG_N_CASE(15);
        }
    }
    return 0;
}

/* Return the content of EMAC receive status register
 * @param pD - driver private state info specific to this instance
 * @param status - pointer to struct with fields for each flag
 * @return =1 if any flags set, =0 if not or status=NULL.
 */
uint32_t emacGetRxStatus(void *pD, CEDI_RxStatus *status)
{
    uint32_t reg;
    if ((pD==NULL)||(status==NULL))
        return 0;

    reg = CPS_UncachedRead32(CEDI_RegAddr(receive_status));

    status->buffNotAvail =
            EMAC_REGS__RECEIVE_STATUS__BUFFER_NOT_AVAILABLE__READ(reg);
    status->frameRx =
            EMAC_REGS__RECEIVE_STATUS__FRAME_RECEIVED__READ(reg);
    status->rxOverrun =
            EMAC_REGS__RECEIVE_STATUS__RECEIVE_OVERRUN__READ(reg);
    status->hRespNotOk =
            EMAC_REGS__RECEIVE_STATUS__RESP_NOT_OK__READ(reg);

    return reg?1:0;
}

/* Reset the bits of EMAC receive status register as selected in resetStatus
 * @param pD - driver private state info specific to this instance
 * @param resetStatus - OR'd combination of CEDI_RXS_ bit-fields
 */
void emacClearRxStatus(void *pD, uint32_t resetStatus)
{
    uint32_t reg = 0;
    if (!pD) return;

    if (resetStatus & CEDI_RXS_NO_BUFF)
        reg |= CEDI_RXS_NO_BUFF;

    if (resetStatus & CEDI_RXS_FRAME_RX)
        reg |= CEDI_RXS_FRAME_RX;

    if (resetStatus & CEDI_RXS_OVERRUN)
        reg |= CEDI_RXS_OVERRUN;

    if (resetStatus & CEDI_RXS_HRESP_ERR)
        reg |= CEDI_RXS_HRESP_ERR;

    CPS_UncachedWrite32(CEDI_RegAddr(receive_status), reg);
}

/**
 *  Enable/disable header-data split feature.
 *  When enabled, frame L2/L3/L4 headers will written to separate
 *  buffer, before data starts in a second buffer (if not zero payload)
 */
uint32_t emacSetHdrDataSplit(void *pD, uint8_t enable) {

    uint32_t reg;
    if ((pD==NULL) || (enable>1))
        return EINVAL;
    if (CEDI_PdVar(hwCfg).hdr_split==0)
        return ENOTSUP;

    reg = CPS_UncachedRead32(CEDI_RegAddr(dma_config));
    if (enable)
        EMAC_REGS__DMA_CONFIG__HDR_DATA_SPLITTING_EN__SET(reg);
    else
        EMAC_REGS__DMA_CONFIG__HDR_DATA_SPLITTING_EN__CLR(reg);

    CPS_UncachedWrite32(CEDI_RegAddr(dma_config), reg);
    return EOK;
}

/**
 * Read enable/disable status for header-data split feature
 */
uint32_t emacGetHdrDataSplit(void *pD, uint8_t *enable) {

    if ((pD==NULL) || (enable==NULL))
        return EINVAL;
    if (CEDI_PdVar(hwCfg).hdr_split==0)
        return ENOTSUP;

    *enable = EMAC_REGS__DMA_CONFIG__HDR_DATA_SPLITTING_EN__READ(
                CPS_UncachedRead32(CEDI_RegAddr(dma_config)));

    return EOK;
}

/**
 *  Enable/disable Receive Segment Coalescing function.
 *  When enabled, consecutive TCP/IP frames on a priority queue
 *  will be combined to form a single large frame
 */
uint32_t emacSetRscEnable(void *pD, uint8_t queue, uint8_t enable) {

    uint32_t reg, enableField;
    if (pD==NULL)
        return EINVAL;
    if (CEDI_PdVar(hwCfg).pbuf_rsc==0)
        return ENOTSUP;
    if ((queue<1) || (queue>=(CEDI_PdVar(cfg)).rxQs) || (enable>1))
        return EINVAL;

    reg = CPS_UncachedRead32(CEDI_RegAddr(rsc_control));
    enableField = EMAC_REGS__RSC_CONTROL__RSC_CONTROL__READ(reg);

    if (enable)
        enableField |= (1 << (queue-1));
    else
        enableField &= ~(1 << (queue-1));

    EMAC_REGS__RSC_CONTROL__RSC_CONTROL__MODIFY(reg, enableField);
    CPS_UncachedWrite32(CEDI_RegAddr(rsc_control), reg);

    return EOK;
}

/**
 * Read enabled status of RSC on a specified priority queue
 */
uint32_t emacGetRscEnable(void *pD, uint8_t queue, uint8_t *enable) {

    uint32_t reg;
    if ((pD==NULL) || (enable==NULL))
        return EINVAL;
    if ((queue<1)||(queue>=(CEDI_PdVar(cfg)).rxQs))
        return EINVAL;
    if (CEDI_PdVar(hwCfg).pbuf_rsc==0)
        return ENOTSUP;

    reg = CPS_UncachedRead32(CEDI_RegAddr(rsc_control));
    *enable = (EMAC_REGS__RSC_CONTROL__RSC_CONTROL__READ(reg)
                            & (1<<(queue-1)))?1:0;

    return EOK;
}

/**
 *  Set/Clear Mask of Receive Segment Coalescing disabling.
 *  When mask is set and RSC is enabled, the RSC operation is not
 *  disabled by receipt of frame with an end-coalesce flag set
 *  (SYN/FIN/RST/URG)
 */
uint32_t emacSetRscClearMask(void *pD, uint8_t setMask) {

    uint32_t reg;
    if ((pD==NULL) || (setMask>1))
        return EINVAL;
    if (CEDI_PdVar(hwCfg).pbuf_rsc==0)
        return ENOTSUP;

    reg = CPS_UncachedRead32(CEDI_RegAddr(rsc_control));
    if (setMask)
        reg |= (1<<16);
    else
        reg &= ~(1<<16);
    CPS_UncachedWrite32(CEDI_RegAddr(rsc_control), reg);

    return EOK;
}

uint32_t emacSetRxPartialStFwd(void *pD, uint32_t watermark, uint8_t enable)
{
    uint32_t reg;
    if (!pD) return EINVAL;
    if (CEDI_PdVar(hwCfg).rx_pkt_buffer==0)
        return ENOTSUP;
    if (enable>1) return EINVAL;
//    if ((enable) && (!CEDI_PdVar(hwCfg).rx_pkt_buffer))
//        return EINVAL;
    if (watermark>((1<<CEDI_PdVar(hwCfg).rx_pbuf_addr)-1))
        return EINVAL;

    reg = CPS_UncachedRead32(CEDI_RegAddr(pbuf_rxcutthru));
    if (enable) {
        EMAC_REGS__PBUF_RXCUTTHRU__DMA_RX_CUTTHRU_THRESHOLD__MODIFY(reg,
                watermark);
        EMAC_REGS__PBUF_RXCUTTHRU__DMA_RX_CUTTHRU__SET(reg);
    }
    else
        EMAC_REGS__PBUF_RXCUTTHRU__DMA_RX_CUTTHRU__CLR(reg);

    CPS_UncachedWrite32(CEDI_RegAddr(pbuf_rxcutthru), reg);

    return 0;
}

uint32_t emacGetRxPartialStFwd(void *pD, uint32_t *watermark, uint8_t *enable)
{
    uint32_t reg;
    if ((pD==0)||(enable==0)||(watermark==0))
        return EINVAL;
    if (CEDI_PdVar(hwCfg).rx_pkt_buffer==0)
        return ENOTSUP;

    reg = CPS_UncachedRead32(CEDI_RegAddr(pbuf_rxcutthru));
    (*enable) = EMAC_REGS__PBUF_RXCUTTHRU__DMA_RX_CUTTHRU__READ(reg);

    if (*enable)
        *watermark = EMAC_REGS__PBUF_RXCUTTHRU__DMA_RX_CUTTHRU_THRESHOLD__READ(reg);

    return 0;
}

/******************************** Rx Filtering ******************************/

  /**
   * Set specific address register to the given address value
   * @param[in] pD driver private state info specific to this instance
   * @param[in] addrNum number of specific address filters,
   *                in range 1 - num_spec_add_filters.
   *    $RANGE $FROM 1 $TO CEDI_DesignCfg.num_spec_add_filters$
   * @param[in] addr pointer to the 6-byte MAC address value to write
   * @param[in] specFilterType flag specifying whether to use MAC source or
   *    destination address to be compared for filtering. Source filter when =1.
   *    $RANGE $FROM 0 $TO 1 $
   * @param[in] byteMask  Bits masking out bytes of specific address from
   *    comparison.  When high, the associated address byte will be ignored.
   *    e.g. LSB of byteMask=1 implies first byte received should not be compared
   *    Ignored if addrNum=1, full bit masking available (SpecificAddr1Mask)
   *    $RANGE $FROM 0 $TO 0x3F $TEST_SUBSET 4 $
   * @return 0 if successful,
   * @return EINVAL if pD, addrNum, specFilterType or byteMask invalid
   * @return ENOTSUP if CEDI_DesignCfg.num_spec_add_filters==0
   */
uint32_t emacSetSpecificAddr(void *pD, uint8_t addrNum, CEDI_MacAddress *addr,
                            uint8_t specFilterType, uint8_t byteMask)
{
#define CEDI_WR_SPEC_ADDR_CASE(reg) \
          case(reg):EMAC_REGS__SPEC_ADD_BOTTOM__ADDRESS__MODIFY(regVal,\
                      addr->byte[0] + (addr->byte[1]<<8) + \
                      (addr->byte[2]<<16) + (addr->byte[3]<<24));\
                    CPS_UncachedWrite32((CEDI_RegAddr(spec_add##reg##_bottom)),\
                                          regVal);\
                    regVal = 0;\
                    if (reg==1) {\
                      EMAC_REGS__SPEC_ADD_TOP_NO_MASK__ADDRESS__MODIFY(regVal,\
                        addr->byte[4] + (addr->byte[5]<<8));\
                      EMAC_REGS__SPEC_ADD_TOP_NO_MASK__FILTER_TYPE__MODIFY( \
                           regVal,specFilterType);\
                    } else {\
                      EMAC_REGS__SPEC_ADD_TOP__ADDRESS__MODIFY(regVal,\
                        addr->byte[4] + (addr->byte[5]<<8));\
                      EMAC_REGS__SPEC_ADD_TOP__FILTER_TYPE__MODIFY( \
                        regVal,specFilterType);\
                      EMAC_REGS__SPEC_ADD_TOP__FILTER_BYTE_MASK__MODIFY( \
                        regVal,byteMask);\
                    }\
                    CPS_UncachedWrite32((CEDI_RegAddr(spec_add##reg##_top)),\
                                          regVal); break;

    uint32_t regVal;
    if ((!pD)||(addr==NULL)) return EINVAL;
    if ((!addrNum) || (addrNum>(CEDI_PdVar(hwCfg).num_spec_add_filters)))
        return EINVAL;
    if ((specFilterType>1) || (byteMask>0x3F))
        return EINVAL;

    if (CEDI_PdVar(hwCfg).num_spec_add_filters==0)
        return ENOTSUP;

    regVal = 0;
    switch(addrNum) {
            CEDI_WR_SPEC_ADDR_CASE(1)
            CEDI_WR_SPEC_ADDR_CASE(2)
            CEDI_WR_SPEC_ADDR_CASE(3)
            CEDI_WR_SPEC_ADDR_CASE(4)
            CEDI_WR_SPEC_ADDR_CASE(5)
            CEDI_WR_SPEC_ADDR_CASE(6)
            CEDI_WR_SPEC_ADDR_CASE(7)
            CEDI_WR_SPEC_ADDR_CASE(8)
            CEDI_WR_SPEC_ADDR_CASE(9)
            CEDI_WR_SPEC_ADDR_CASE(10)
            CEDI_WR_SPEC_ADDR_CASE(11)
            CEDI_WR_SPEC_ADDR_CASE(12)
            CEDI_WR_SPEC_ADDR_CASE(13)
            CEDI_WR_SPEC_ADDR_CASE(14)
            CEDI_WR_SPEC_ADDR_CASE(15)
            CEDI_WR_SPEC_ADDR_CASE(16)
            CEDI_WR_SPEC_ADDR_CASE(17)
            CEDI_WR_SPEC_ADDR_CASE(18)
            CEDI_WR_SPEC_ADDR_CASE(19)
            CEDI_WR_SPEC_ADDR_CASE(20)
            CEDI_WR_SPEC_ADDR_CASE(21)
            CEDI_WR_SPEC_ADDR_CASE(22)
            CEDI_WR_SPEC_ADDR_CASE(23)
            CEDI_WR_SPEC_ADDR_CASE(24)
            CEDI_WR_SPEC_ADDR_CASE(25)
            CEDI_WR_SPEC_ADDR_CASE(26)
            CEDI_WR_SPEC_ADDR_CASE(27)
            CEDI_WR_SPEC_ADDR_CASE(28)
            CEDI_WR_SPEC_ADDR_CASE(29)
            CEDI_WR_SPEC_ADDR_CASE(30)
            CEDI_WR_SPEC_ADDR_CASE(31)
            CEDI_WR_SPEC_ADDR_CASE(32)
}

    return EOK;
}

  /**
   * Get the value of a specific address register
   * @param[in] pD driver private state info specific to this instance
   * @param[in] addrNum number of specific address filters, in
   *                range 1 - num_spec_add_filters
   * @param[out] specFilterType flag specifying whether to use MAC source or
   *    destination address for filtering. When set to 1 use source address.
   * @param[out] byteMask Bits masking out bytes of specific address from
   *    comparison.  When high, the associated address byte will be ignored.
   *    e.g. LSB of byteMask=1 implies first byte received should not be compared
   *    Ignored if addrNum=1, full bit masking available (SpecificAddr1Mask)
   * @param[out] addr pointer to a 6-byte MAC address struct for returning the
   *    address value
   * @return 0 if successful
   * @return EINVAL if pD, addrNum, specFilterType or byteMask invalid
   * @return ENOTSUP if CEDI_DesignCfg.num_spec_add_filters==0
   */
uint32_t emacGetSpecificAddr(void *pD, uint8_t addrNum, CEDI_MacAddress *addr,
                        uint8_t *specFilterType, uint8_t *byteMask)
{

#define CEDI_RD_SPEC_ADDR_CASE(reg) \
              case(reg):\
              regAddrBottom = EMAC_REGS__SPEC_ADD_BOTTOM__ADDRESS__READ(\
                 CPS_UncachedRead32(CEDI_RegAddr(spec_add##reg##_bottom)));\
              regTopVal= CPS_UncachedRead32(CEDI_RegAddr(spec_add##reg##_top));\
              if (reg==1) {\
                regAddrTop = EMAC_REGS__SPEC_ADD_TOP_NO_MASK__ADDRESS__READ(\
                            regTopVal);\
                *specFilterType = \
                  EMAC_REGS__SPEC_ADD_TOP_NO_MASK__FILTER_TYPE__READ(regTopVal);\
                *byteMask = 0;\
              } else {\
                regAddrTop = EMAC_REGS__SPEC_ADD_TOP__ADDRESS__READ(regTopVal);\
                *specFilterType = \
                  EMAC_REGS__SPEC_ADD_TOP__FILTER_TYPE__READ(regTopVal);\
                *byteMask = \
                  EMAC_REGS__SPEC_ADD_TOP__FILTER_BYTE_MASK__READ(regTopVal);}\
              break;

    uint32_t regAddrTop, regAddrBottom, regTopVal;
    if ((pD==NULL)||(addr==NULL))
        return EINVAL;

    if ((specFilterType==NULL)||(byteMask==NULL))
        return EINVAL;

    if (CEDI_PdVar(hwCfg).num_spec_add_filters==0)
        return ENOTSUP;
    if ((!addrNum) || (addrNum>(CEDI_PdVar(hwCfg).num_spec_add_filters)))
        return EINVAL;
    regAddrTop = 0;
    regAddrBottom = 0;
    switch(addrNum) {
                CEDI_RD_SPEC_ADDR_CASE(1)
                CEDI_RD_SPEC_ADDR_CASE(2)
                CEDI_RD_SPEC_ADDR_CASE(3)
                CEDI_RD_SPEC_ADDR_CASE(4)
                CEDI_RD_SPEC_ADDR_CASE(5)
                CEDI_RD_SPEC_ADDR_CASE(6)
                CEDI_RD_SPEC_ADDR_CASE(7)
                CEDI_RD_SPEC_ADDR_CASE(8)
                CEDI_RD_SPEC_ADDR_CASE(9)
                CEDI_RD_SPEC_ADDR_CASE(10)
                CEDI_RD_SPEC_ADDR_CASE(11)
                CEDI_RD_SPEC_ADDR_CASE(12)
                CEDI_RD_SPEC_ADDR_CASE(13)
                CEDI_RD_SPEC_ADDR_CASE(14)
                CEDI_RD_SPEC_ADDR_CASE(15)
                CEDI_RD_SPEC_ADDR_CASE(16)
                CEDI_RD_SPEC_ADDR_CASE(17)
                CEDI_RD_SPEC_ADDR_CASE(18)
                CEDI_RD_SPEC_ADDR_CASE(19)
                CEDI_RD_SPEC_ADDR_CASE(20)
                CEDI_RD_SPEC_ADDR_CASE(21)
                CEDI_RD_SPEC_ADDR_CASE(22)
                CEDI_RD_SPEC_ADDR_CASE(23)
                CEDI_RD_SPEC_ADDR_CASE(24)
                CEDI_RD_SPEC_ADDR_CASE(25)
                CEDI_RD_SPEC_ADDR_CASE(26)
                CEDI_RD_SPEC_ADDR_CASE(27)
                CEDI_RD_SPEC_ADDR_CASE(28)
                CEDI_RD_SPEC_ADDR_CASE(29)
                CEDI_RD_SPEC_ADDR_CASE(30)
                CEDI_RD_SPEC_ADDR_CASE(31)
                CEDI_RD_SPEC_ADDR_CASE(32)
    }

//    vDbgMsg(DBG_GEN_MSG, 10, "top=%08X  bottom=%08X\n",
//                    regAddrTop, regAddrBottom);
    addr->byte[0] = (regAddrBottom & 0xFF);
    addr->byte[1] = ((regAddrBottom>>8) & 0xFF);
    addr->byte[2] = ((regAddrBottom>>16) & 0xFF);
    addr->byte[3] = ((regAddrBottom>>24) & 0xFF);
    addr->byte[4] = (regAddrTop & 0xFF);
    addr->byte[5] = ((regAddrTop>>8) & 0xFF);
    return EOK;
}

/* Set the specific address 1 mask register to the given value, allowing
 * address matching against a portion of the specific address 1 register
 * @param pD - driver private state info specific to this instance
 * @param mask - pointer to the address mask value to write
 * @return 0 if successful
 * @return EINVAL if mask=NULL
 * @return ENOTSUP if CEDI_DesignCfg.num_spec_add_filters==0
 */
uint32_t emacSetSpecificAddr1Mask(void *pD, CEDI_MacAddress *mask)
{
    uint32_t reg;

    if ((pD==NULL) || (mask==NULL))
        return EINVAL;
    if (CEDI_PdVar(hwCfg).num_spec_add_filters==0)
        return ENOTSUP;

    reg = 0;
    EMAC_REGS__MASK_ADD1_BOTTOM__ADDRESS_MASK__MODIFY(reg,
            mask->byte[0] + (mask->byte[1]<<8) + (mask->byte[2]<<16)
                        + (mask->byte[3]<<24));
    CPS_UncachedWrite32((CEDI_RegAddr(mask_add1_bottom)), reg);
    reg = 0;
    EMAC_REGS__MASK_ADD1_TOP__ADDRESS_MASK__MODIFY(reg,
            mask->byte[4] + (mask->byte[5]<<8));
    CPS_UncachedWrite32((CEDI_RegAddr(mask_add1_top)), reg);
    return EOK;
}

/* Get the value of the specific address 1 mask register
 * @param pD - driver private state info specific to this instance
 * @param mask - pointer to a 6-byte MAC address struct for returning the
 *    mask value
 * @return 0 if successful, EINVAL if addrNum invalid
 * @return ENOTSUP if CEDI_DesignCfg.num_spec_add_filters==0
 */
uint32_t emacGetSpecificAddr1Mask(void *pD, CEDI_MacAddress *mask)
{
    int reg1, reg2;
    if ((pD==NULL)||(mask==NULL)) return EINVAL;
    if (CEDI_PdVar(hwCfg).num_spec_add_filters==0)
        return ENOTSUP;

    reg1 = EMAC_REGS__MASK_ADD1_BOTTOM__ADDRESS_MASK__READ(
            CPS_UncachedRead32(CEDI_RegAddr(mask_add1_bottom)));
    reg2 = EMAC_REGS__MASK_ADD1_TOP__ADDRESS_MASK__READ(
            CPS_UncachedRead32(CEDI_RegAddr(mask_add1_top)));

//      vDbgMsg(DBG_GEN_MSG, 10, "top=%08X  bottom=%08X\n", reg2, reg1);
    mask->byte[0] = (reg1 & 0xFF);
    mask->byte[1] = ((reg1>>8) & 0xFF);
    mask->byte[2] = ((reg1>>16) & 0xFF);
    mask->byte[3] = ((reg1>>24) & 0xFF);
    mask->byte[4] = (reg2 & 0xFF);
    mask->byte[5] = ((reg2>>8) & 0xFF);
    return EOK;
}

/* Disable the specific address match stored at given register, by writing 0
 * to lower address register
 * @param pD - driver private state info specific to this instance
 * @param addrNum -
 *    number of specific address filters, in range 1 - num_spec_add_filters
 *    $RANGE $FROM 1 $TO CEDI_DesignCfg.num_spec_add_filters$
 * @return 0 if successful
 * @return EINVAL if invalid parameter
 * @return ENOTSUP if CEDI_DesignCfg.num_spec_add_filters==0
 */
uint32_t emacDisableSpecAddr(void *pD, uint8_t addrNum)
{
#define CEDI_DIS_SPEC_ADDR_CASE(reg) \
    case(reg): \
        CPS_UncachedWrite32((CEDI_RegAddr(spec_add##reg##_bottom)), 0); break;

    if (!pD) return EINVAL;
    if (CEDI_PdVar(hwCfg).num_spec_add_filters==0)
        return ENOTSUP;
    if ((!addrNum) || (addrNum>(CEDI_PdVar(hwCfg).num_spec_add_filters)))
        return EINVAL;

    switch(addrNum) {
                CEDI_DIS_SPEC_ADDR_CASE(1)
                CEDI_DIS_SPEC_ADDR_CASE(2)
                CEDI_DIS_SPEC_ADDR_CASE(3)
                CEDI_DIS_SPEC_ADDR_CASE(4)
                CEDI_DIS_SPEC_ADDR_CASE(5)
                CEDI_DIS_SPEC_ADDR_CASE(6)
                CEDI_DIS_SPEC_ADDR_CASE(7)
                CEDI_DIS_SPEC_ADDR_CASE(8)
                CEDI_DIS_SPEC_ADDR_CASE(9)
                CEDI_DIS_SPEC_ADDR_CASE(10)
                CEDI_DIS_SPEC_ADDR_CASE(11)
                CEDI_DIS_SPEC_ADDR_CASE(12)
                CEDI_DIS_SPEC_ADDR_CASE(13)
                CEDI_DIS_SPEC_ADDR_CASE(14)
                CEDI_DIS_SPEC_ADDR_CASE(15)
                CEDI_DIS_SPEC_ADDR_CASE(16)
                CEDI_DIS_SPEC_ADDR_CASE(17)
                CEDI_DIS_SPEC_ADDR_CASE(18)
                CEDI_DIS_SPEC_ADDR_CASE(19)
                CEDI_DIS_SPEC_ADDR_CASE(20)
                CEDI_DIS_SPEC_ADDR_CASE(21)
                CEDI_DIS_SPEC_ADDR_CASE(22)
                CEDI_DIS_SPEC_ADDR_CASE(23)
                CEDI_DIS_SPEC_ADDR_CASE(24)
                CEDI_DIS_SPEC_ADDR_CASE(25)
                CEDI_DIS_SPEC_ADDR_CASE(26)
                CEDI_DIS_SPEC_ADDR_CASE(27)
                CEDI_DIS_SPEC_ADDR_CASE(28)
                CEDI_DIS_SPEC_ADDR_CASE(29)
                CEDI_DIS_SPEC_ADDR_CASE(30)
                CEDI_DIS_SPEC_ADDR_CASE(31)
                CEDI_DIS_SPEC_ADDR_CASE(32)
    }

    return 0;
}

/**
 * En/Disable Type ID match field of the specified register, and set
 * type Id value if enabling
 * @param[in] pD driver private state info specific to this instance
 * @param[in] matchSel number of TypeID Match register, range 1 - 4
 *    $RANGE $FROM 1 $TO 4$
 * @param[in] typeId the Type ID match value to write,
 *    ignored if enable equal 0
 * @param[in] enable if equal 1 enables the type matching for this ID,
 *    if 0 then disables type matching for this ID
 *    $RANGE $FROM 0 $TO 1$
 * @return 0 if successful,
 * @return EINVAL if matchSel invalid
 * $VALIDFAIL if ((enable==0)&&((matchSel<1)||(matchSel>4)))
 *  $EXPECT_RETURN EINVAL $
 */
uint32_t emacSetTypeIdMatch(void *pD, uint8_t matchSel, uint16_t typeId,
        uint8_t enable)
{
    uint32_t regVal = 0;
    if ((pD==NULL) || (matchSel<1) || (matchSel>4)) return EINVAL;
    if (enable>1) return EINVAL;

    switch (matchSel) {
    case 1:
        if (enable) {
            EMAC_REGS__SPEC_TYPE1__ENABLE_COPY__SET(regVal);
            EMAC_REGS__SPEC_TYPE1__MATCH__MODIFY(regVal, typeId);
        }
        CPS_UncachedWrite32(CEDI_RegAddr(spec_type1),regVal);
        break;
    case 2:
        if (enable) {
            EMAC_REGS__SPEC_TYPE2__ENABLE_COPY__SET(regVal);
            EMAC_REGS__SPEC_TYPE2__MATCH__MODIFY(regVal, typeId);
        }
        CPS_UncachedWrite32(CEDI_RegAddr(spec_type2),regVal);
        break;
    case 3:
        if (enable) {
            EMAC_REGS__SPEC_TYPE3__ENABLE_COPY__SET(regVal);
            EMAC_REGS__SPEC_TYPE3__MATCH__MODIFY(regVal, typeId);
        }
        CPS_UncachedWrite32(CEDI_RegAddr(spec_type3),regVal);
        break;
    case 4:
        if (enable) {
            EMAC_REGS__SPEC_TYPE4__ENABLE_COPY__SET(regVal);
            EMAC_REGS__SPEC_TYPE4__MATCH__MODIFY(regVal, typeId);
        }
        CPS_UncachedWrite32(CEDI_RegAddr(spec_type4),regVal);
        break;
    }

    return 0;
}

/* Read the specified Type ID match register settings
 * @param pD - driver private state info specific to this instance
 * @param matchSel  - number of TypeID Match register, range 1 - 4
 * @param typeId - pointer for returning the Type ID match value read,
 *              ignored if disabled
 * @param enabled - pointer for returning enabled status: if value returned <>0
 *             then typeId matching is enabled for this register, else disabled
 * @return 0 if successful, EINVAL if invalid parameter
 */
uint32_t emacGetTypeIdMatch(void *pD, uint8_t matchSel, uint16_t *typeId,
        uint8_t *enabled)
{
    uint32_t regVal = 0;
    if (pD==NULL) return EINVAL;
    if ((matchSel<1) || (matchSel>4) || (enabled==NULL)) return EINVAL;
    if (*enabled && (typeId==NULL)) return EINVAL;

    switch (matchSel) {
    case 1:
        regVal = CPS_UncachedRead32(CEDI_RegAddr(spec_type1));
        *enabled = EMAC_REGS__SPEC_TYPE1__ENABLE_COPY__READ(regVal);
        *typeId = EMAC_REGS__SPEC_TYPE1__MATCH__READ(regVal);
        break;
    case 2:
        regVal = CPS_UncachedRead32(CEDI_RegAddr(spec_type2));
        *enabled = EMAC_REGS__SPEC_TYPE2__ENABLE_COPY__READ(regVal);
        *typeId = EMAC_REGS__SPEC_TYPE2__MATCH__READ(regVal);
        break;
    case 3:
        regVal = CPS_UncachedRead32(CEDI_RegAddr(spec_type3));
        *enabled = EMAC_REGS__SPEC_TYPE3__ENABLE_COPY__READ(regVal);
        *typeId = EMAC_REGS__SPEC_TYPE3__MATCH__READ(regVal);
        break;
    case 4:
        regVal = CPS_UncachedRead32(CEDI_RegAddr(spec_type4));
        *enabled = EMAC_REGS__SPEC_TYPE4__ENABLE_COPY__READ(regVal);
        *typeId = EMAC_REGS__SPEC_TYPE4__MATCH__READ(regVal);
        break;
    }

    return 0;
}

/* En/disable reception of unicast frames when hash register matched
 * @param pD - driver private state info specific to this instance
 * @param enable if<>0, enables reception, else disables
 */
void emacSetUnicastEnable(void *pD, uint8_t enable)
{
    uint32_t reg;
    if (!pD) return;
    if (enable>1) return;
    reg = CPS_UncachedRead32(CEDI_RegAddr(network_config));
    if (enable)
        EMAC_REGS__NETWORK_CONFIG__UNICAST_HASH_ENABLE__SET(reg);
    else
        EMAC_REGS__NETWORK_CONFIG__UNICAST_HASH_ENABLE__CLR(reg);
    CPS_UncachedWrite32(CEDI_RegAddr(network_config), reg);
}

/* Return state of unicast frame matching
 * @param pD - driver private state info specific to this instance
 * @return  =0 if disabled, =1 if enabled
 */
uint32_t emacGetUnicastEnable(void *pD, uint8_t *enable)
{
    if ((pD==0)||(enable==0))
      return EINVAL;
    *enable= EMAC_REGS__NETWORK_CONFIG__UNICAST_HASH_ENABLE__READ(
            CPS_UncachedRead32(CEDI_RegAddr(network_config)));

    return 0;
}

/* En/disable reception of multicast frames when hash register matched
 * @param pD - driver private state info specific to this instance
 * @param enable if<>0, enables, else disables
 */
void emacSetMulticastEnable(void *pD, uint8_t enable)
{
    uint32_t reg;
    if (!pD) return;
    if (enable>1) return;
    reg = CPS_UncachedRead32(CEDI_RegAddr(network_config));
    if (enable)
        EMAC_REGS__NETWORK_CONFIG__MULTICAST_HASH_ENABLE__SET(reg);
    else
        EMAC_REGS__NETWORK_CONFIG__MULTICAST_HASH_ENABLE__CLR(reg);
    CPS_UncachedWrite32(CEDI_RegAddr(network_config), reg);
}

/* Return state of multicast frame matching
 * @param pD - driver private state info specific to this instance
 * @return =0 if disabled, =1 if enabled
 */
uint32_t emacGetMulticastEnable(void *pD, uint8_t *enable)
{
    if ((pD==0)||(enable==0))
      return EINVAL;
    *enable= EMAC_REGS__NETWORK_CONFIG__MULTICAST_HASH_ENABLE__READ(
            CPS_UncachedRead32(CEDI_RegAddr(network_config)));

    return 0;
}

/* Dis/Enable receipt of broadcast frames
 * @param pD - driver private state info specific to this instance
 * @param reject if =0 broadcasts are accepted, else they are rejected.
 */
void emacSetNoBroadcast(void *pD, uint8_t reject)
{
    uint32_t reg;
    if (!pD) return;
    reg = CPS_UncachedRead32(CEDI_RegAddr(network_config));
    if (reject)
        EMAC_REGS__NETWORK_CONFIG__NO_BROADCAST__SET(reg);
    else
        EMAC_REGS__NETWORK_CONFIG__NO_BROADCAST__CLR(reg);
    CPS_UncachedWrite32(CEDI_RegAddr(network_config), reg);
}

/* Return broadcast rejection setting
 * @param pD - driver private state info specific to this instance
 * @return if =0, broadcasts being accepted, else rejected
 */
uint32_t emacGetNoBroadcast(void *pD, uint8_t *reject)
{
    if ((pD==0)||(reject==0))
      return EINVAL;
    *reject= EMAC_REGS__NETWORK_CONFIG__NO_BROADCAST__READ(
            CPS_UncachedRead32(CEDI_RegAddr(network_config)));

    return 0;
}

/* En/Disable receipt of only frames which have been VLAN tagged
 * @param pD - driver private state info specific to this instance
 * @param enable<>0 to reject non-VLAN-tagged frames.
 */
void emacSetVlanOnly(void *pD, uint8_t enable)
{
    uint32_t reg;
    if (!pD) return;
    if (enable>1) return;
    reg = CPS_UncachedRead32(CEDI_RegAddr(network_config));
    if (enable)
        EMAC_REGS__NETWORK_CONFIG__DISCARD_NON_VLAN_FRAMES__SET(reg);
    else
        EMAC_REGS__NETWORK_CONFIG__DISCARD_NON_VLAN_FRAMES__CLR(reg);
    CPS_UncachedWrite32(CEDI_RegAddr(network_config), reg);
}

/* Return VLAN-tagged filter setting
 * @param pD - driver private state info specific to this instance
 * @return <>0 if VLAN-only, else accept non-VLAN tagged frames
 */
uint32_t emacGetVlanOnly(void *pD, uint8_t *enable)
{
    if ((pD==0)||(enable==0))
      return EINVAL;
    *enable= EMAC_REGS__NETWORK_CONFIG__DISCARD_NON_VLAN_FRAMES__READ(
            CPS_UncachedRead32(CEDI_RegAddr(network_config)));

    return 0;
}

/* En/Disable stacked VLAN processing mode.
 * @param pD - driver private state info specific to this instance
 * @param enable - if <>0 enables stacked VLAN processing, if =0 disables it
 * @param vlanType - sets user defined VLAN type for matching first VLAN tag.
 *    Ignored if enable =0.
 */
void emacSetStackedVlanReg(void *pD, uint8_t enable, uint16_t vlanType)
{
    uint32_t reg;
    if (!pD) return;
    if (enable>1) return;

    reg = CPS_UncachedRead32(CEDI_RegAddr(stacked_vlan));
    if (enable) {
        EMAC_REGS__STACKED_VLAN__ENABLE_PROCESSING__SET(reg);
        EMAC_REGS__STACKED_VLAN__MATCH__MODIFY(reg, vlanType);
    }
    else
        EMAC_REGS__STACKED_VLAN__ENABLE_PROCESSING__CLR(reg);
    CPS_UncachedWrite32(CEDI_RegAddr(stacked_vlan), reg);
}

/* Reads stacked VLAN register settings.
 * @param pD - driver private state info specific to this instance
 * @param enable - pointer for returning Enabled field: =1 if enabled, =0 if
 *    disabled.
 * @param vlanType - pointer for returning VLAN type field
 *
 */
void emacGetStackedVlanReg(void *pD, uint8_t *enable, uint16_t *vlanType)
{
    uint32_t reg;
    if ((pD==NULL)||(enable==NULL) || (vlanType==NULL)) return;

    reg = CPS_UncachedRead32(CEDI_RegAddr(stacked_vlan));
    *enable = EMAC_REGS__STACKED_VLAN__ENABLE_PROCESSING__READ(reg);
    *vlanType = EMAC_REGS__STACKED_VLAN__MATCH__READ(reg);
}

/* En/Disable copy all frames mode
 * @param pD - driver private state info specific to this instance
 * @param enable - if <>0, enables copy all frames mode, else this is
 *    disabled
 */
void emacSetCopyAllFrames(void *pD, uint8_t enable)
{
    uint32_t reg;
    if (!pD) return;
    if (enable>1) return;
    reg = CPS_UncachedRead32(CEDI_RegAddr(network_config));
    if (enable)
        EMAC_REGS__NETWORK_CONFIG__COPY_ALL_FRAMES__SET(reg);
    else
        EMAC_REGS__NETWORK_CONFIG__COPY_ALL_FRAMES__CLR(reg);
    CPS_UncachedWrite32(CEDI_RegAddr(network_config), reg);
}

/* Get "copy all" setting
 * @param pD - driver private state info specific to this instance
 * @return =0 if disabled, =1 if enabled
 */
uint32_t emacGetCopyAllFrames(void *pD, uint8_t *enable)
{
    if ((pD==0)||(enable==0))
      return EINVAL;
    *enable= EMAC_REGS__NETWORK_CONFIG__COPY_ALL_FRAMES__READ(
            CPS_UncachedRead32(CEDI_RegAddr(network_config)));

    return 0;
}

/* Set the hash address register.
 * @param pD - driver private state info specific to this instance
 * @param hAddrTop  -  most significant 32 bits of hash register
 * @param hAddrBot  - least significant 32 bits of hash register
 * @return EINVAL if pD=NULL, else 0.
 */
uint32_t emacSetHashAddr(void *pD, uint32_t hAddrTop, uint32_t hAddrBot)
{
    if (pD==NULL) return EINVAL;
    CPS_UncachedWrite32(CEDI_RegAddr(hash_bottom),
            EMAC_REGS__HASH_BOTTOM__ADDRESS__WRITE(hAddrBot));
    CPS_UncachedWrite32(CEDI_RegAddr(hash_top),
            EMAC_REGS__HASH_TOP__ADDRESS__WRITE(hAddrTop));
    return 0;
}

/* Read the hash address register.
 * @param pD - driver private state info specific to this instance
 * @param hAddrTop  -  pointer for returning most significant 32 bits of
 *    hash register
 * @param hAddrBot  - pointer for returning least significant 32 bits of
 *    hash register
 * @return EINVAL if any parameter =NULL, else 0.
 */
uint32_t emacGetHashAddr(void *pD, uint32_t *hAddrTop, uint32_t *hAddrBot)
{
    if ((pD==NULL) || (hAddrTop==NULL) || (hAddrBot==NULL)) return EINVAL;
    *hAddrBot = EMAC_REGS__HASH_BOTTOM__ADDRESS__READ(
                CPS_UncachedRead32(CEDI_RegAddr(hash_bottom)));
    *hAddrTop = EMAC_REGS__HASH_TOP__ADDRESS__READ(
                CPS_UncachedRead32(CEDI_RegAddr(hash_top)));
    return 0;
}

/* Enable/disable discard of frames with length shorter than given in length
 * field
 * @param pD - driver private state info specific to this instance
 * @param enable - if <>1 then enable, else disable.
 */
void emacSetLenErrDiscard(void *pD, uint8_t enable)
{
    uint32_t reg;
    if (!pD) return;
    if (enable>1) return;
    reg = CPS_UncachedRead32(CEDI_RegAddr(network_config));
    if (enable)
        EMAC_REGS__NETWORK_CONFIG__LENGTH_FIELD_ERROR_FRAME_DISCARD__SET(reg);
    else
        EMAC_REGS__NETWORK_CONFIG__LENGTH_FIELD_ERROR_FRAME_DISCARD__CLR(reg);
    CPS_UncachedWrite32(CEDI_RegAddr(network_config), reg);
}

/* Read enable/disable status for discard of frames with length shorter than
 * given in length field.
 * @param pD - driver private state info specific to this instance
 * @return 1 if enabled, 0 if disabled.
 */
uint32_t emacGetLenErrDiscard(void *pD, uint8_t *enable)
{
    if ((pD==0)||(enable==0))
      return EINVAL;
    *enable= EMAC_REGS__NETWORK_CONFIG__LENGTH_FIELD_ERROR_FRAME_DISCARD__READ(
            CPS_UncachedRead32(CEDI_RegAddr(network_config)));

    return 0;
}

/******************************** Rx Priority Queues ******************************/

/* Return the numbers of screener, ethtype & compare registers present
 * @param pD - driver private state info specific to this instance
 * @param regNums - points to a CEDI_NumScreeners struct with the match parameters
 *    to be written
 * @return 0 if successful, EINVAL if parameter invalid
 */
uint32_t emacGetNumScreenRegs(void *pD, CEDI_NumScreeners *regNums)
{
    if ((pD==NULL) || (regNums==NULL))
        return EINVAL;

    regNums->type1ScrRegs = CEDI_PdVar(hwCfg).num_type1_screeners;
    regNums->type2ScrRegs = CEDI_PdVar(hwCfg).num_type2_screeners;
    regNums->ethtypeRegs = CEDI_PdVar(hwCfg).num_scr2_ethtype_regs;
    regNums->compareRegs = CEDI_PdVar(hwCfg).num_scr2_compare_regs;
    return 0;
}


/* Write Rx frame matching values to a Type 1 screening register, for allocating
 * to a priority queue.
 * @param pD - driver private state info specific to this instance
 * @param regNum - the Type 1 register number, range 0 to num_type1_screeners-1
 * @param regVals - points to a CEDI_T1Screen struct with the match parameters
 *    to be written
 * @return 0 if successful, EINVAL if parameter invalid
 */
uint32_t emacSetType1ScreenReg(void *pD, uint8_t regNum, CEDI_T1Screen *regVals)
{
#define CEDI_WR_SCRN_TYPE1_REG_CASE(rNum) \
    case(rNum):\
        CPS_UncachedWrite32(CEDI_RegAddr(screening_type_1_register_##rNum),reg);\
        break;

    uint32_t reg;
    if ((pD==NULL) || (regVals==NULL))
        return EINVAL;

    if (CEDI_PdVar(hwCfg).num_type1_screeners==0)
        return ENOTSUP;

    if ((regNum>=CEDI_PdVar(hwCfg).num_type1_screeners) ||
        (regVals->qNum>=CEDI_PdVar(cfg).rxQs) ||
        (regVals->udpEnable>1) || (regVals->dstcEnable>1))
        return EINVAL;

    reg = 0;
    EMAC_REGS__SCREENING_TYPE_1_REGISTER__QUEUE_NUMBER__MODIFY(reg,
                                                            regVals->qNum);
    EMAC_REGS__SCREENING_TYPE_1_REGISTER__DSTC_ENABLE__MODIFY(reg,
                                                        regVals->dstcEnable);
    EMAC_REGS__SCREENING_TYPE_1_REGISTER__DSTC_MATCH__MODIFY(reg,
                                                        regVals->dstcMatch);
    EMAC_REGS__SCREENING_TYPE_1_REGISTER__UDP_PORT_MATCH_ENABLE__MODIFY(reg,
                                                    regVals->udpEnable);
    EMAC_REGS__SCREENING_TYPE_1_REGISTER__UDP_PORT_MATCH__MODIFY(reg,
                                                        regVals->udpPort);

    switch (regNum) {
        CEDI_WR_SCRN_TYPE1_REG_CASE(0)
        CEDI_WR_SCRN_TYPE1_REG_CASE(1)
        CEDI_WR_SCRN_TYPE1_REG_CASE(2)
        CEDI_WR_SCRN_TYPE1_REG_CASE(3)
        CEDI_WR_SCRN_TYPE1_REG_CASE(4)
        CEDI_WR_SCRN_TYPE1_REG_CASE(5)
        CEDI_WR_SCRN_TYPE1_REG_CASE(6)
        CEDI_WR_SCRN_TYPE1_REG_CASE(7)
        CEDI_WR_SCRN_TYPE1_REG_CASE(8)
        CEDI_WR_SCRN_TYPE1_REG_CASE(9)
        CEDI_WR_SCRN_TYPE1_REG_CASE(10)
        CEDI_WR_SCRN_TYPE1_REG_CASE(11)
        CEDI_WR_SCRN_TYPE1_REG_CASE(12)
        CEDI_WR_SCRN_TYPE1_REG_CASE(13)
        CEDI_WR_SCRN_TYPE1_REG_CASE(14)
        CEDI_WR_SCRN_TYPE1_REG_CASE(15)
    }
    return 0;
}

/* Read Rx frame matching values from a Type1 screening register
 * @param pD - driver private state info specific to this instance
 * @param regNum - the Type 1 register number, range 0 to num_type1_screeners-1
 * @param regVals - points to a CEDI_T1Screen struct for returning the match
 *    parameters
 * @return 0 if successful, EINVAL if parameter invalid
 */
uint32_t emacGetType1ScreenReg(void *pD, uint8_t regNum, CEDI_T1Screen *regVals)
{
#define CEDI_RD_SCRN_TYPE1_REG_CASE(rNum) \
    case(rNum):\
        reg=CPS_UncachedRead32(CEDI_RegAddr(screening_type_1_register_##rNum));\
        break;

    uint32_t reg = 0;

    if ((pD==NULL) || (regVals==NULL))
        return EINVAL;

    if (CEDI_PdVar(hwCfg).num_type1_screeners==0)
        return ENOTSUP;

    if (regNum>=CEDI_PdVar(hwCfg).num_type1_screeners)
        return EINVAL;

    switch (regNum) {
        CEDI_RD_SCRN_TYPE1_REG_CASE(0)
        CEDI_RD_SCRN_TYPE1_REG_CASE(1)
        CEDI_RD_SCRN_TYPE1_REG_CASE(2)
        CEDI_RD_SCRN_TYPE1_REG_CASE(3)
        CEDI_RD_SCRN_TYPE1_REG_CASE(4)
        CEDI_RD_SCRN_TYPE1_REG_CASE(5)
        CEDI_RD_SCRN_TYPE1_REG_CASE(6)
        CEDI_RD_SCRN_TYPE1_REG_CASE(7)
        CEDI_RD_SCRN_TYPE1_REG_CASE(8)
        CEDI_RD_SCRN_TYPE1_REG_CASE(9)
        CEDI_RD_SCRN_TYPE1_REG_CASE(10)
        CEDI_RD_SCRN_TYPE1_REG_CASE(11)
        CEDI_RD_SCRN_TYPE1_REG_CASE(12)
        CEDI_RD_SCRN_TYPE1_REG_CASE(13)
        CEDI_RD_SCRN_TYPE1_REG_CASE(14)
        CEDI_RD_SCRN_TYPE1_REG_CASE(15)
    }
    regVals->qNum =
            EMAC_REGS__SCREENING_TYPE_1_REGISTER__QUEUE_NUMBER__READ(reg);
    regVals->dstcMatch =
            EMAC_REGS__SCREENING_TYPE_1_REGISTER__DSTC_MATCH__READ(reg);
    regVals->udpPort =
            EMAC_REGS__SCREENING_TYPE_1_REGISTER__UDP_PORT_MATCH__READ(reg);
    regVals->dstcEnable =
            EMAC_REGS__SCREENING_TYPE_1_REGISTER__DSTC_ENABLE__READ(reg);
    regVals->udpEnable =
            EMAC_REGS__SCREENING_TYPE_1_REGISTER__UDP_PORT_MATCH_ENABLE__READ(
                    reg);
    return 0;
}

/* Write Rx frame matching values to a Type 2 screening register, for
 * allocating to a priority queue.
 * @param pD - driver private state info specific to this instance
 * @param regNum - the Type 2 register number, range 0 to num_type2_screeners-1
 * @param regVals - points to a CEDI_T2Screen struct with the match
 *    parameters to be written
 * @return 0 if successful, EINVAL if parameter invalid
 */
uint32_t emacSetType2ScreenReg(void *pD, uint8_t regNum, CEDI_T2Screen *regVals)
{
#define CEDI_WR_SCRN_TYPE2_REG_CASE(rNum) \
    case(rNum):\
        CPS_UncachedWrite32(CEDI_RegAddr(screening_type_2_register_##rNum),\
            reg); break;

    uint32_t reg;

    if ((pD==NULL) || (regVals==NULL))
        return EINVAL;

    if (CEDI_PdVar(hwCfg).num_type2_screeners==0)
        return ENOTSUP;

    if ((regNum>=CEDI_PdVar(hwCfg).num_type2_screeners) ||
        (regVals->qNum>=CEDI_PdVar(cfg).rxQs) ||
        (regVals->vlanEnable>1) ||
        (regVals->vlanEnable && (regVals->vlanPriority>=8)) ||
        (regVals->eTypeEnable>1) ||
        ((regVals->eTypeEnable) && (regVals->ethTypeIndex>=8)) ||
            ((regVals->eTypeEnable) &&
              (regVals->ethTypeIndex>=CEDI_PdVar(hwCfg).num_scr2_ethtype_regs)) ||
        (regVals->compAEnable>1) ||
        ((regVals->compAEnable) && (regVals->compAIndex>=32)) ||
            ((regVals->compAEnable) &&
              (regVals->compAIndex>=CEDI_PdVar(hwCfg).num_scr2_compare_regs)) ||
        (regVals->compBEnable>1) ||
        ((regVals->compBEnable) && (regVals->compBIndex>=32)) ||
            ((regVals->compBEnable) &&
              (regVals->compBIndex>=CEDI_PdVar(hwCfg).num_scr2_compare_regs)) ||
        (regVals->compCEnable>1) ||
        ((regVals->compCEnable) && (regVals->compCIndex>=32)) ||
            ((regVals->compCEnable) &&
              (regVals->compCIndex>=CEDI_PdVar(hwCfg).num_scr2_compare_regs)))
        return EINVAL;

    reg = 0;
    EMAC_REGS__SCREENING_TYPE_2_REGISTER__QUEUE_NUMBER__MODIFY(reg,
                                                            regVals->qNum);
    EMAC_REGS__SCREENING_TYPE_2_REGISTER__VLAN_ENABLE__MODIFY(reg,
                                                    regVals->vlanEnable);
    EMAC_REGS__SCREENING_TYPE_2_REGISTER__VLAN_PRIORITY__MODIFY(reg,
                                                    regVals->vlanPriority);
    EMAC_REGS__SCREENING_TYPE_2_REGISTER__ETHERTYPE_ENABLE__MODIFY(reg,
                                                    regVals->eTypeEnable);
    EMAC_REGS__SCREENING_TYPE_2_REGISTER__INDEX__MODIFY(reg,
                                                    regVals->ethTypeIndex);
    EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_A_ENABLE__MODIFY(reg,
                                                    regVals->compAEnable);
    EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_A__MODIFY(reg,
                                                    regVals->compAIndex);
    EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_B_ENABLE__MODIFY(reg,
                                                    regVals->compBEnable);
    EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_B__MODIFY(reg,
                                                    regVals->compBIndex);
    EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_C_ENABLE__MODIFY(reg,
                                                    regVals->compCEnable);
    EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_C__MODIFY(reg,
                                                    regVals->compCIndex);

    switch (regNum) {
        CEDI_WR_SCRN_TYPE2_REG_CASE(0)
        CEDI_WR_SCRN_TYPE2_REG_CASE(1)
        CEDI_WR_SCRN_TYPE2_REG_CASE(2)
        CEDI_WR_SCRN_TYPE2_REG_CASE(3)
        CEDI_WR_SCRN_TYPE2_REG_CASE(4)
        CEDI_WR_SCRN_TYPE2_REG_CASE(5)
        CEDI_WR_SCRN_TYPE2_REG_CASE(6)
        CEDI_WR_SCRN_TYPE2_REG_CASE(7)
        CEDI_WR_SCRN_TYPE2_REG_CASE(8)
        CEDI_WR_SCRN_TYPE2_REG_CASE(9)
        CEDI_WR_SCRN_TYPE2_REG_CASE(10)
        CEDI_WR_SCRN_TYPE2_REG_CASE(11)
        CEDI_WR_SCRN_TYPE2_REG_CASE(12)
        CEDI_WR_SCRN_TYPE2_REG_CASE(13)
        CEDI_WR_SCRN_TYPE2_REG_CASE(14)
        CEDI_WR_SCRN_TYPE2_REG_CASE(15)
    }
    return 0;
}

/* Read Rx frame matching values from a Type 2 screening register
 * @param pD - driver private state info specific to this instance
 * @param regNum - the Type 2 register number, range 0 to num_type2_screeners-1
 * @param regVals - points to a CEDI_T2Screen struct for returning the match
 *    parameters
 * @return 0 if successful, EINVAL if parameter invalid
 */
uint32_t emacGetType2ScreenReg(void *pD, uint8_t regNum, CEDI_T2Screen *regVals)
{
#define CEDI_RD_SCRN_TYPE2_REG_CASE(rNum) \
    case(rNum):\
        reg=CPS_UncachedRead32(CEDI_RegAddr(screening_type_2_register_##rNum));\
        break;

    uint32_t reg = 0;

    if ((pD==0)||(regVals==0))
        return EINVAL;

    if (CEDI_PdVar(hwCfg).num_type2_screeners==0)
        return ENOTSUP;

    if (regNum>=CEDI_PdVar(hwCfg).num_type2_screeners)
        return EINVAL;

    switch (regNum) {
        CEDI_RD_SCRN_TYPE2_REG_CASE(0)
        CEDI_RD_SCRN_TYPE2_REG_CASE(1)
        CEDI_RD_SCRN_TYPE2_REG_CASE(2)
        CEDI_RD_SCRN_TYPE2_REG_CASE(3)
        CEDI_RD_SCRN_TYPE2_REG_CASE(4)
        CEDI_RD_SCRN_TYPE2_REG_CASE(5)
        CEDI_RD_SCRN_TYPE2_REG_CASE(6)
        CEDI_RD_SCRN_TYPE2_REG_CASE(7)
        CEDI_RD_SCRN_TYPE2_REG_CASE(8)
        CEDI_RD_SCRN_TYPE2_REG_CASE(9)
        CEDI_RD_SCRN_TYPE2_REG_CASE(10)
        CEDI_RD_SCRN_TYPE2_REG_CASE(11)
        CEDI_RD_SCRN_TYPE2_REG_CASE(12)
        CEDI_RD_SCRN_TYPE2_REG_CASE(13)
        CEDI_RD_SCRN_TYPE2_REG_CASE(14)
        CEDI_RD_SCRN_TYPE2_REG_CASE(15)
    }
    regVals->qNum =
            EMAC_REGS__SCREENING_TYPE_2_REGISTER__QUEUE_NUMBER__READ(reg);
    regVals->vlanPriority =
            EMAC_REGS__SCREENING_TYPE_2_REGISTER__VLAN_PRIORITY__READ(reg);
    regVals->vlanEnable =
            EMAC_REGS__SCREENING_TYPE_2_REGISTER__VLAN_ENABLE__READ(reg);
    regVals->ethTypeIndex =
            EMAC_REGS__SCREENING_TYPE_2_REGISTER__INDEX__READ(reg);
    regVals->eTypeEnable =
            EMAC_REGS__SCREENING_TYPE_2_REGISTER__ETHERTYPE_ENABLE__READ(reg);
    regVals->compAIndex =
            EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_A__READ(reg);
    regVals->compAEnable =
            EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_A_ENABLE__READ(reg);
    regVals->compBIndex =
            EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_B__READ(reg);
    regVals->compBEnable =
            EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_B_ENABLE__READ(reg);
    regVals->compCIndex =
            EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_C__READ(reg);
    regVals->compCEnable =
            EMAC_REGS__SCREENING_TYPE_2_REGISTER__COMPARE_C_ENABLE__READ(reg);
    return 0;
}

/* Write the ethertype compare value at the given index in the Ethertype
 * registers
 * @param pD - driver private state info specific to this instance
 * @param index - number of screener Type 2 Ethertype compare register to
 *    write, range 0 to num_scr2_ethtype_regs-1
 * @param eTypeVal - Ethertype compare value to write
 * @return 0 if successful, EINVAL if parameter invalid
 */
uint32_t emacSetType2EthertypeReg(void *pD, uint8_t index, uint16_t eTypeVal)
{
#define CEDI_WR_SCRN_TYPE2_ETHTYPE_REG_CASE(rNum) \
    case(rNum):\
        CPS_UncachedWrite32(CEDI_RegAddr(screening_type_2_ethertype_reg_##rNum),reg);\
        break;

    uint32_t reg;

    if (pD==NULL)
        return EINVAL;

    if ((CEDI_PdVar(hwCfg).num_type2_screeners==0) ||
            (CEDI_PdVar(hwCfg).num_scr2_ethtype_regs==0))
    return ENOTSUP;

    if (index>=CEDI_PdVar(hwCfg).num_scr2_ethtype_regs)
        return EINVAL;

    reg = 0;
    EMAC_REGS__SCREENING_TYPE_2_ETHERTYPE_REG__COMPARE_VALUE__MODIFY(reg,
                                                                    eTypeVal);
    switch (index) {
        CEDI_WR_SCRN_TYPE2_ETHTYPE_REG_CASE(0)
        CEDI_WR_SCRN_TYPE2_ETHTYPE_REG_CASE(1)
        CEDI_WR_SCRN_TYPE2_ETHTYPE_REG_CASE(2)
        CEDI_WR_SCRN_TYPE2_ETHTYPE_REG_CASE(3)
        CEDI_WR_SCRN_TYPE2_ETHTYPE_REG_CASE(4)
        CEDI_WR_SCRN_TYPE2_ETHTYPE_REG_CASE(5)
        CEDI_WR_SCRN_TYPE2_ETHTYPE_REG_CASE(6)
        CEDI_WR_SCRN_TYPE2_ETHTYPE_REG_CASE(7)
    }
    return 0;
}

/* Read the ethertype compare value at the given index in the Ethertype
 * registers
 * @param pD - driver private state info specific to this instance
 * @param index - number of screener Type 2 Ethertype compare register to
 *    read, range 0 to num_scr2_ethtype_regs-1
 * @param eTypeVal - pointer for returning the Ethertype compare value
 * @return 0 if successful, EINVAL if parameter invalid
 */
uint32_t emacGetType2EthertypeReg(void *pD, uint8_t index, uint16_t *eTypeVal)
{
#define CEDI_RD_SCRN_TYPE2_ETHTYPE_REG_CASE(rNum) \
    case(rNum):\
        reg = CPS_UncachedRead32(CEDI_RegAddr(screening_type_2_ethertype_reg_##rNum));\
        break;

    uint32_t reg = 0;

    if ((pD==NULL)||(eTypeVal==NULL))
        return EINVAL;

    if ((CEDI_PdVar(hwCfg).num_type2_screeners==0) ||
            (CEDI_PdVar(hwCfg).num_scr2_ethtype_regs==0))
    return ENOTSUP;

    if (index>=CEDI_PdVar(hwCfg).num_scr2_ethtype_regs)
        return EINVAL;

    switch (index) {
        CEDI_RD_SCRN_TYPE2_ETHTYPE_REG_CASE(0)
        CEDI_RD_SCRN_TYPE2_ETHTYPE_REG_CASE(1)
        CEDI_RD_SCRN_TYPE2_ETHTYPE_REG_CASE(2)
        CEDI_RD_SCRN_TYPE2_ETHTYPE_REG_CASE(3)
        CEDI_RD_SCRN_TYPE2_ETHTYPE_REG_CASE(4)
        CEDI_RD_SCRN_TYPE2_ETHTYPE_REG_CASE(5)
        CEDI_RD_SCRN_TYPE2_ETHTYPE_REG_CASE(6)
        CEDI_RD_SCRN_TYPE2_ETHTYPE_REG_CASE(7)
    }
    *eTypeVal = (uint16_t)(
            EMAC_REGS__SCREENING_TYPE_2_ETHERTYPE_REG__COMPARE_VALUE__READ(reg));
    return 0;
}

/* Write the compare value at the given index in the Type 2 compare register
 * @param pD - driver private state info specific to this instance
 * @param index - number of the Type 2 compare register to write, range 0 to
 *    num_scr2_compare_regs-1
 * @param regVals - points to a CEDI_T2Compare struct with the compare
 *    parameters to be written
 * @return 0 if successful, EINVAL if parameter invalid
 */
uint32_t emacSetType2CompareReg(void *pD, uint8_t index, CEDI_T2Compare *regVals)
{
#define CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(rNum) \
    case(rNum):\
        CPS_UncachedWrite32(CEDI_RegAddr(type2_compare_##rNum##_word_0),reg0);\
        CPS_UncachedWrite32(CEDI_RegAddr(type2_compare_##rNum##_word_1),reg1);\
        break;

    uint32_t reg0, reg1;

    if ((pD==NULL)||(regVals==NULL))
        return EINVAL;

    if ((CEDI_PdVar(hwCfg).num_type2_screeners==0) ||
            (CEDI_PdVar(hwCfg).num_scr2_compare_regs==0))
    return ENOTSUP;

    if ((index>=CEDI_PdVar(hwCfg).num_scr2_compare_regs)
        || (regVals->offsetVal>0x3F)
        || (regVals->offsetPosition>CEDI_T2COMP_OFF_TCPUDP)
        || (regVals->disableMask>1))
        return EINVAL;

    reg0 = 0;
    reg1 = 0;
    EMAC_REGS__TYPE2_COMPARE_WORD_0__MASK_VALUE__MODIFY(reg0,
                                                        regVals->compMask);
    EMAC_REGS__TYPE2_COMPARE_WORD_0__COMPARE_VALUE__MODIFY(reg0,
                                                        regVals->compValue);
    EMAC_REGS__TYPE2_COMPARE_WORD_1__OFFSET_VALUE__MODIFY(reg1,
                                                        regVals->offsetVal);
    EMAC_REGS__TYPE2_COMPARE_WORD_1__COMPARE_OFFSET__MODIFY(reg1,
                                                    regVals->offsetPosition);
    EMAC_REGS__TYPE2_COMPARE_WORD_1__DISABLE_MASK__MODIFY(reg1,
                                                    regVals->disableMask);

    switch (index) {
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(0)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(1)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(2)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(3)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(4)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(5)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(6)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(7)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(8)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(9)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(10)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(11)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(12)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(13)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(14)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(15)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(16)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(17)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(18)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(19)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(20)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(21)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(22)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(23)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(24)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(25)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(26)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(27)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(28)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(29)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(30)
        CEDI_WR_SCRN_TYPE2_COMPARE_REG_CASE(31)
    }
    return 0;
}

/* Read the compare value at the given index in the Type 2 compare register
 * @param pD - driver private state info specific to this instance
 * @param index - number of the Type 2 compare register to read, range 0 to
 *    num_scr2_compare_regs-1
 * @param regVals - points to a CEDI_T2Compare struct for returning the
 *    compare parameters
 * @return 0 if successful, EINVAL if parameter invalid
 */
uint32_t emacGetType2CompareReg(void *pD, uint8_t index, CEDI_T2Compare *regVals)
{
#define CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(rNum) \
    case(rNum):\
        reg0=CPS_UncachedRead32(CEDI_RegAddr(type2_compare_##rNum##_word_0));\
        reg1=CPS_UncachedRead32(CEDI_RegAddr(type2_compare_##rNum##_word_1));\
        break;

    uint32_t reg0 = 0, reg1 = 0;

    if ((pD==0)||(regVals==0))
      return EINVAL;

    if ((CEDI_PdVar(hwCfg).num_type2_screeners==0) ||
            (CEDI_PdVar(hwCfg).num_scr2_compare_regs==0))
    return ENOTSUP;

    if (index>=CEDI_PdVar(hwCfg).num_scr2_compare_regs)
        return EINVAL;

    switch (index) {
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(0)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(1)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(2)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(3)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(4)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(5)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(6)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(7)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(8)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(9)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(10)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(11)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(12)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(13)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(14)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(15)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(16)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(17)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(18)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(19)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(20)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(21)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(22)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(23)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(24)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(25)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(26)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(27)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(28)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(29)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(30)
        CEDI_RD_SCRN_TYPE2_COMPARE_REG_CASE(31)
    }

    regVals->compMask =(uint16_t)(
        EMAC_REGS__TYPE2_COMPARE_WORD_0__MASK_VALUE__READ(reg0));
    regVals->compValue =(uint16_t)(
        EMAC_REGS__TYPE2_COMPARE_WORD_0__COMPARE_VALUE__READ(reg0));
    regVals->offsetVal =(uint8_t)(
        EMAC_REGS__TYPE2_COMPARE_WORD_1__OFFSET_VALUE__READ(reg1));
    regVals->offsetPosition =(CEDI_T2Offset)(
        EMAC_REGS__TYPE2_COMPARE_WORD_1__COMPARE_OFFSET__READ(reg1));
    regVals->disableMask = (uint8_t)(
        EMAC_REGS__TYPE2_COMPARE_WORD_1__DISABLE_MASK__READ(reg1));

    return 0;
}

#ifdef __cplusplus
}
#endif

#endif /* CY_IP_MXETH */