Logo Search packages:      
Sourcecode: u-boot-linaro version File versions  Download package

IxEthAccDataPlane.c

/**
 * @file IxEthDataPlane.c
 *
 * @author Intel Corporation
 * @date 12-Feb-2002
 *
 * @brief This file contains the implementation of the IXPxxx
 * Ethernet Access Data plane component
 *
 * Design Notes:
 *
 * @par
 * IXP400 SW Release version 2.0
 *
 * -- Copyright Notice --
 *
 * @par
 * Copyright 2001-2005, Intel Corporation.
 * All rights reserved.
 *
 * @par
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. 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.
 * 3. Neither the name of the Intel Corporation nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * @par
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``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 THE COPYRIGHT OWNER OR CONTRIBUTORS 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.
 *
 * @par
 * -- End of Copyright Notice --
 */

#include "IxNpeMh.h"
#include "IxEthAcc.h"
#include "IxEthDB.h"
#include "IxOsal.h"
#include "IxEthDBPortDefs.h"
#include "IxFeatureCtrl.h"
#include "IxEthAcc_p.h"
#include "IxEthAccQueueAssign_p.h"

extern PUBLIC IxEthAccMacState ixEthAccMacState[];
extern PUBLIC UINT32 ixEthAccNewSrcMask;

/**
 * private functions prototype
 */
PRIVATE IX_OSAL_MBUF *
ixEthAccEntryFromQConvert(UINT32 qEntry, UINT32 mask);

PRIVATE UINT32
ixEthAccMbufRxQPrepare(IX_OSAL_MBUF *mbuf);

PRIVATE UINT32
ixEthAccMbufTxQPrepare(IX_OSAL_MBUF *mbuf);

PRIVATE IxEthAccStatus
ixEthAccTxSwQHighestPriorityGet(IxEthAccPortId portId,
                        IxEthAccTxPriority *priorityPtr);

PRIVATE IxEthAccStatus
ixEthAccTxFromSwQ(IxEthAccPortId portId,
              IxEthAccTxPriority priority);

PRIVATE IxEthAccStatus
ixEthAccRxFreeFromSwQ(IxEthAccPortId portId);

PRIVATE void
ixEthAccMbufFromTxQ(IX_OSAL_MBUF *mbuf);

PRIVATE void
ixEthAccMbufFromRxQ(IX_OSAL_MBUF *mbuf);

PRIVATE IX_STATUS
ixEthAccQmgrLockTxWrite(IxEthAccPortId portId,
                  UINT32 qBuffer);

PRIVATE IX_STATUS
ixEthAccQmgrLockRxWrite(IxEthAccPortId portId,
                  UINT32 qBuffer);

PRIVATE IX_STATUS
ixEthAccQmgrTxWrite(IxEthAccPortId portId,
                UINT32 qBuffer,
                UINT32 priority);

/**
 * @addtogroup IxEthAccPri
 *@{
 */

/* increment a counter only when stats are enabled */
#define TX_STATS_INC(port,field) \
        IX_ETH_ACC_STATS_INC(ixEthAccPortData[port].ixEthAccTxData.stats.field)
#define RX_STATS_INC(port,field) \
        IX_ETH_ACC_STATS_INC(ixEthAccPortData[port].ixEthAccRxData.stats.field)

/* always increment the counter (mainly used for unexpected errors) */
#define TX_INC(port,field) \
        ixEthAccPortData[port].ixEthAccTxData.stats.field++
#define RX_INC(port,field) \
        ixEthAccPortData[port].ixEthAccRxData.stats.field++

PRIVATE IxEthAccDataPlaneStats     ixEthAccDataStats;

extern IxEthAccPortDataInfo   ixEthAccPortData[];
extern IxEthAccInfo   ixEthAccDataInfo;

PRIVATE IxOsalFastMutex txWriteMutex[IX_ETH_ACC_NUMBER_OF_PORTS];
PRIVATE IxOsalFastMutex rxWriteMutex[IX_ETH_ACC_NUMBER_OF_PORTS];

/**
 *
 * @brief Mbuf header conversion macros : they implement the
 *  different conversions using a temporary value. They also double-check
 *  that the parameters can be converted to/from NPE format.
 *
 */
#if defined(__wince) && !defined(IN_KERNEL)
#define PTR_VIRT2NPE(ptrSrc,dst) \
  do { UINT32 temp; \
      IX_OSAL_ENSURE(sizeof(ptrSrc) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(dst) == sizeof(UINT32), "Wrong parameter type"); \
      temp = (UINT32)IX_OSAL_MBUF_MBUF_VIRTUAL_TO_PHYSICAL_TRANSLATION((IX_OSAL_MBUF*)ptrSrc); \
      (dst) = IX_OSAL_SWAP_BE_SHARED_LONG(temp); } \
  while(0)

#define PTR_NPE2VIRT(type,src,ptrDst) \
  do { void *temp; \
      IX_OSAL_ENSURE(sizeof(type) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(src) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(ptrDst) == sizeof(UINT32), "Wrong parameter type"); \
      temp = (void *)IX_OSAL_SWAP_BE_SHARED_LONG(src); \
      (ptrDst) = (type)IX_OSAL_MBUF_MBUF_PHYSICAL_TO_VIRTUAL_TRANSLATION(temp); } \
  while(0)
#else
00155 #define PTR_VIRT2NPE(ptrSrc,dst) \
  do { UINT32 temp; \
      IX_OSAL_ENSURE(sizeof(ptrSrc) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(dst) == sizeof(UINT32), "Wrong parameter type"); \
      temp = (UINT32)IX_OSAL_MMU_VIRT_TO_PHYS(ptrSrc); \
      (dst) = IX_OSAL_SWAP_BE_SHARED_LONG(temp); } \
  while(0)

#define PTR_NPE2VIRT(type,src,ptrDst) \
  do { void *temp; \
      IX_OSAL_ENSURE(sizeof(type) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(src) == sizeof(UINT32), "Wrong parameter type"); \
      IX_OSAL_ENSURE(sizeof(ptrDst) == sizeof(UINT32), "Wrong parameter type"); \
      temp = (void *)IX_OSAL_SWAP_BE_SHARED_LONG(src); \
      (ptrDst) = (type)IX_OSAL_MMU_PHYS_TO_VIRT(temp); } \
  while(0)
#endif

/**
 *
 * @brief Mbuf payload pointer conversion macros : Wince has its own
 *  method to convert the buffer pointers
 */
#if defined(__wince) && !defined(IN_KERNEL)
#define DATAPTR_VIRT2NPE(ptrSrc,dst) \
  do { UINT32 temp; \
      temp = (UINT32)IX_OSAL_MBUF_DATA_VIRTUAL_TO_PHYSICAL_TRANSLATION(ptrSrc); \
      (dst) = IX_OSAL_SWAP_BE_SHARED_LONG(temp); } \
  while(0)

#else
00186 #define DATAPTR_VIRT2NPE(ptrSrc,dst) PTR_VIRT2NPE(IX_OSAL_MBUF_MDATA(ptrSrc),dst)
#endif


/* Flush the shared part of the mbuf header */
#define IX_ETHACC_NE_CACHE_FLUSH(mbufPtr) \
  do { \
      IX_OSAL_CACHE_FLUSH(IX_ETHACC_NE_SHARED(mbufPtr), \
                        sizeof(IxEthAccNe)); \
    } \
  while(0)

/* Invalidate the shared part of the mbuf header */
#define IX_ETHACC_NE_CACHE_INVALIDATE(mbufPtr) \
  do { \
      IX_OSAL_CACHE_INVALIDATE(IX_ETHACC_NE_SHARED(mbufPtr), \
                           sizeof(IxEthAccNe)); \
    } \
  while(0)

/* Preload one cache line (shared mbuf headers are aligned
 * and their size is 1 cache line)
 *
 * IX_OSAL_CACHED  is defined when the mbuf headers are
 * allocated from cached memory.
 *
 * Other processor on emulation environment may not implement
 * preload function
 */
#ifdef IX_OSAL_CACHED
      #if (CPU!=SIMSPARCSOLARIS) && !defined (__wince)
            #define IX_ACC_DATA_CACHE_PRELOAD(ptr) \
            do { /* preload a cache line (Xscale Processor) */ \
                  __asm__ (" pld [%0]\n": : "r" (ptr)); \
            } \
            while(0)
      #else
            /* preload not implemented on different processor */
            #define IX_ACC_DATA_CACHE_PRELOAD(mbufPtr) \
            do { /* nothing */ } while (0)
      #endif
#else
      /* preload not needed if cache is not enabled */
      #define IX_ACC_DATA_CACHE_PRELOAD(mbufPtr) \
      do { /* nothing */ } while (0)
#endif

/**
 *
 * @brief function to retrieve the correct pointer from
 * a queue entry posted by the NPE
 *
 * @param qEntry : entry from qmgr queue
 *        mask : applicable mask for this queue
 *        (4 most significant bits are used for additional informations)
 *
 * @return IX_OSAL_MBUF * pointer to mbuf header
 *
 * @internal
 */
PRIVATE IX_OSAL_MBUF *
00247 ixEthAccEntryFromQConvert(UINT32 qEntry, UINT32 mask)
{
    IX_OSAL_MBUF *mbufPtr;

    if (qEntry != 0)
    {
        /* mask NPE bits (e.g. priority, port ...) */
        qEntry &= mask;

#if IX_ACC_DRAM_PHYS_OFFSET != 0
        /* restore the original address pointer (if PHYS_OFFSET is not 0) */
        qEntry |= (IX_ACC_DRAM_PHYS_OFFSET & ~IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);
#endif
        /* get the mbuf pointer address from the npe-shared address */
        qEntry -= offsetof(IX_OSAL_MBUF,ix_ne);

        /* phys2virt mbuf */
        mbufPtr = (IX_OSAL_MBUF *)IX_OSAL_MMU_PHYS_TO_VIRT(qEntry);

        /* preload the cacheline shared with NPE */
        IX_ACC_DATA_CACHE_PRELOAD(IX_ETHACC_NE_SHARED(mbufPtr));

        /* preload the cacheline used by xscale */
        IX_ACC_DATA_CACHE_PRELOAD(mbufPtr);
    }
    else
    {
      mbufPtr = NULL;
    }

    return mbufPtr;
}

/* Convert the mbuf header for NPE transmission */
PRIVATE UINT32
ixEthAccMbufTxQPrepare(IX_OSAL_MBUF *mbuf)
{
    UINT32 qbuf;
    UINT32 len;

    /* endianess swap for tci and flags
       note: this is done only once, even for chained buffers */
    IX_ETHACC_NE_FLAGS(mbuf)   = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_FLAGS(mbuf));
    IX_ETHACC_NE_VLANTCI(mbuf) = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_VLANTCI(mbuf));

    /* test for unchained mbufs */
    if (IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(mbuf) == NULL)
    {
      /* "best case" scenario : unchained mbufs */
      IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedTxMBufs);

      /* payload pointer conversion */
      DATAPTR_VIRT2NPE(mbuf, IX_ETHACC_NE_DATA(mbuf));

      /* unchained mbufs : the frame length is the mbuf length
       * and the 2 identical lengths are stored in the same
       * word.
       */
      len = IX_OSAL_MBUF_MLEN(mbuf);

      /* set the length in both length and pktLen 16-bits fields */
      len |= (len << IX_ETHNPE_ACC_LENGTH_OFFSET);
      IX_ETHACC_NE_LEN(mbuf) = IX_OSAL_SWAP_BE_SHARED_LONG(len);

      /* unchained mbufs : next contains 0 */
      IX_ETHACC_NE_NEXT(mbuf) = 0;

      /* flush shared header after all address conversions */
      IX_ETHACC_NE_CACHE_FLUSH(mbuf);
    }
    else
    {
      /* chained mbufs */
      IX_OSAL_MBUF *ptr = mbuf;
      IX_OSAL_MBUF *nextPtr;
      UINT32 frmLen;

      /* get the frame length from the header of the first buffer */
      frmLen = IX_OSAL_MBUF_PKT_LEN(mbuf);

      do
      {
          IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedTxMBufs);

          /* payload pointer */
          DATAPTR_VIRT2NPE(ptr,IX_ETHACC_NE_DATA(ptr));
          /* Buffer length and frame length are stored in the same word */
          len = IX_OSAL_MBUF_MLEN(ptr);
          len = frmLen | (len << IX_ETHNPE_ACC_LENGTH_OFFSET);
          IX_ETHACC_NE_LEN(ptr) = IX_OSAL_SWAP_BE_SHARED_LONG(len);

          /* get the virtual next chain pointer */
          nextPtr = IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr);
          if (nextPtr != NULL)
          {
            /* shared pointer of the next buffer is chained */
            PTR_VIRT2NPE(IX_ETHACC_NE_SHARED(nextPtr),
                       IX_ETHACC_NE_NEXT(ptr));
          }
          else
          {
            IX_ETHACC_NE_NEXT(ptr) = 0;
          }

          /* flush shared header after all address conversions */
          IX_ETHACC_NE_CACHE_FLUSH(ptr);

          /* move to next buffer */
          ptr = nextPtr;

          /* the frame length field is set only in the first buffer
           * and is zeroed in the next buffers
           */
          frmLen = 0;
      }
      while(ptr != NULL);

    }

    /* virt2phys mbuf itself */
    qbuf = (UINT32)IX_OSAL_MMU_VIRT_TO_PHYS(
              IX_ETHACC_NE_SHARED(mbuf));

    /* Ensure the bits which are reserved to exchange information with
     * the NPE are cleared
     *
     * If the mbuf address is not correctly aligned, or from an
     * incompatible memory range, there is no point to continue
     */
    IX_OSAL_ENSURE(((qbuf & ~IX_ETHNPE_QM_Q_TXENET_ADDR_MASK) == 0),
            "Invalid address range");

    return qbuf;
}

/* Convert the mbuf header for NPE reception */
PRIVATE UINT32
ixEthAccMbufRxQPrepare(IX_OSAL_MBUF *mbuf)
{
    UINT32 len;
    UINT32 qbuf;

    if (IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(mbuf) == NULL)
    {
      /* "best case" scenario : unchained mbufs */
      IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedRxFreeMBufs);

      /* unchained mbufs : payload pointer */
      DATAPTR_VIRT2NPE(mbuf, IX_ETHACC_NE_DATA(mbuf));

      /* unchained mbufs : set the buffer length
      * and the frame length field is zeroed
      */
      len = (IX_OSAL_MBUF_MLEN(mbuf) << IX_ETHNPE_ACC_LENGTH_OFFSET);
      IX_ETHACC_NE_LEN(mbuf) = IX_OSAL_SWAP_BE_SHARED_LONG(len);

      /* unchained mbufs : next pointer is null */
      IX_ETHACC_NE_NEXT(mbuf) = 0;

      /* flush shared header after all address conversions */
      IX_ETHACC_NE_CACHE_FLUSH(mbuf);

      /* remove shared header cache line */
      IX_ETHACC_NE_CACHE_INVALIDATE(mbuf);
    }
    else
    {
      /* chained mbufs */
      IX_OSAL_MBUF *ptr = mbuf;
      IX_OSAL_MBUF *nextPtr;

      do
      {
          /* chained mbufs */
          IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedRxFreeMBufs);

          /* we must save virtual next chain pointer */
          nextPtr = IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr);

          if (nextPtr != NULL)
          {
            /* chaining pointer for NPE */
            PTR_VIRT2NPE(IX_ETHACC_NE_SHARED(nextPtr),
                       IX_ETHACC_NE_NEXT(ptr));
          }
          else
          {
            IX_ETHACC_NE_NEXT(ptr) = 0;
          }

          /* payload pointer */
          DATAPTR_VIRT2NPE(ptr,IX_ETHACC_NE_DATA(ptr));

          /* buffer length */
          len = (IX_OSAL_MBUF_MLEN(ptr) << IX_ETHNPE_ACC_LENGTH_OFFSET);
          IX_ETHACC_NE_LEN(ptr) = IX_OSAL_SWAP_BE_SHARED_LONG(len);

          /* flush shared header after all address conversions */
          IX_ETHACC_NE_CACHE_FLUSH(ptr);

          /* remove shared header cache line */
          IX_ETHACC_NE_CACHE_INVALIDATE(ptr);

          /* next mbuf in the chain */
          ptr = nextPtr;
      }
      while(ptr != NULL);
    }

    /* virt2phys mbuf itself */
    qbuf = (UINT32)IX_OSAL_MMU_VIRT_TO_PHYS(
              IX_ETHACC_NE_SHARED(mbuf));

    /* Ensure the bits which are reserved to exchange information with
     * the NPE are cleared
     *
     * If the mbuf address is not correctly aligned, or from an
     * incompatible memory range, there is no point to continue
     */
    IX_OSAL_ENSURE(((qbuf & ~IX_ETHNPE_QM_Q_RXENET_ADDR_MASK) == 0),
            "Invalid address range");

    return qbuf;
}

/* Convert the mbuf header after NPE transmission
 * Since there is nothing changed by the NPE, there is no need
 * to process anything but the update of internal stats
 * when they are enabled
*/
PRIVATE void
ixEthAccMbufFromTxQ(IX_OSAL_MBUF *mbuf)
{
#ifndef NDEBUG
    /* test for unchained mbufs */
    if (IX_ETHACC_NE_NEXT(mbuf) == 0)
    {
      /* unchained mbufs : update the stats */
      IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedTxDoneMBufs);
    }
    else
    {
      /* chained mbufs : walk the chain and update the stats */
      IX_OSAL_MBUF *ptr = mbuf;

      do
      {
          IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedTxDoneMBufs);
          ptr = IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr);
      }
      while (ptr != NULL);
    }
#endif
}

/* Convert the mbuf header after NPE reception */
PRIVATE void
ixEthAccMbufFromRxQ(IX_OSAL_MBUF *mbuf)
{
    UINT32 len;

    /* endianess swap for tci and flags
       note: this is done only once, even for chained buffers */
    IX_ETHACC_NE_FLAGS(mbuf)   = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_FLAGS(mbuf));
    IX_ETHACC_NE_VLANTCI(mbuf) = IX_OSAL_SWAP_BE_SHARED_SHORT(IX_ETHACC_NE_VLANTCI(mbuf));

    /* test for unchained mbufs */
    if (IX_ETHACC_NE_NEXT(mbuf) == 0)
    {
      /* unchained mbufs */
      IX_ETH_ACC_STATS_INC(ixEthAccDataStats.unchainedRxMBufs);

      /* get the frame length. it is the same than the buffer length */
      len = IX_OSAL_SWAP_BE_SHARED_LONG(IX_ETHACC_NE_LEN(mbuf));
      len &= IX_ETHNPE_ACC_PKTLENGTH_MASK;
      IX_OSAL_MBUF_PKT_LEN(mbuf) = IX_OSAL_MBUF_MLEN(mbuf) = len;

        /* clears the next packet field */
      IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(mbuf) = NULL;
    }
    else
    {
      IX_OSAL_MBUF *ptr = mbuf;
      IX_OSAL_MBUF *nextPtr;
      UINT32 frmLen;

      /* convert the frame length */
      frmLen = IX_OSAL_SWAP_BE_SHARED_LONG(IX_ETHACC_NE_LEN(mbuf));
      IX_OSAL_MBUF_PKT_LEN(mbuf) = (frmLen & IX_ETHNPE_ACC_PKTLENGTH_MASK);

        /* chained mbufs */
      do
      {
          IX_ETH_ACC_STATS_INC(ixEthAccDataStats.chainedRxMBufs);

          /* convert the length */
          len = IX_OSAL_SWAP_BE_SHARED_LONG(IX_ETHACC_NE_LEN(ptr));
          IX_OSAL_MBUF_MLEN(ptr) = (len >> IX_ETHNPE_ACC_LENGTH_OFFSET);

            /* get the next pointer */
          PTR_NPE2VIRT(IX_OSAL_MBUF *,IX_ETHACC_NE_NEXT(ptr), nextPtr);
          if (nextPtr != NULL)
          {
            nextPtr = (IX_OSAL_MBUF *)((UINT8 *)nextPtr - offsetof(IX_OSAL_MBUF,ix_ne));
          }
          /* set the next pointer */
          IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(ptr) = nextPtr;

          /* move to the next buffer */
          ptr = nextPtr;
      }
      while (ptr != NULL);
    }
}

/* write to qmgr if possible and report an overflow if not possible
 * Use a fast lock to protect the queue write.
 * This way, the tx feature is reentrant.
 */
PRIVATE IX_STATUS
ixEthAccQmgrLockTxWrite(IxEthAccPortId portId, UINT32 qBuffer)
{
    IX_STATUS qStatus;
    if (ixOsalFastMutexTryLock(&txWriteMutex[portId]) == IX_SUCCESS)
    {
      qStatus = ixQMgrQWrite(
             IX_ETH_ACC_PORT_TO_TX_Q_ID(portId),
             &qBuffer);
#ifndef NDEBUG
      if (qStatus != IX_SUCCESS)
      {
          TX_STATS_INC(portId, txOverflow);
      }
#endif
      ixOsalFastMutexUnlock(&txWriteMutex[portId]);
    }
    else
    {
      TX_STATS_INC(portId, txLock);
      qStatus = IX_QMGR_Q_OVERFLOW;
    }
    return qStatus;
}

/* write to qmgr if possible and report an overflow if not possible
 * Use a fast lock to protect the queue write.
 * This way, the Rx feature is reentrant.
 */
PRIVATE IX_STATUS
ixEthAccQmgrLockRxWrite(IxEthAccPortId portId, UINT32 qBuffer)
{
    IX_STATUS qStatus;
    if (ixOsalFastMutexTryLock(&rxWriteMutex[portId]) == IX_SUCCESS)
    {
      qStatus = ixQMgrQWrite(
             IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId),
             &qBuffer);
#ifndef NDEBUG
      if (qStatus != IX_SUCCESS)
      {
          RX_STATS_INC(portId, rxFreeOverflow);
      }
#endif
      ixOsalFastMutexUnlock(&rxWriteMutex[portId]);
    }
    else
    {
      RX_STATS_INC(portId, rxFreeLock);
      qStatus = IX_QMGR_Q_OVERFLOW;
    }
    return qStatus;
}

/*
 * Set the priority and write to a qmgr queue.
 */
PRIVATE IX_STATUS
ixEthAccQmgrTxWrite(IxEthAccPortId portId, UINT32 qBuffer, UINT32 priority)
{
    /* fill the priority field */
    qBuffer |= (priority << IX_ETHNPE_QM_Q_FIELD_PRIOR_R);

    return ixEthAccQmgrLockTxWrite(portId, qBuffer);
}

/**
 *
 * @brief This function will discover the highest priority S/W Tx Q that
 *        has entries in it
 *
 * @param portId - (in) the id of the port whose S/W Tx queues are to be searched
 *        priorityPtr - (out) the priority of the highest priority occupied q will be written
 *                      here
 *
 * @return IX_ETH_ACC_SUCCESS if an occupied Q is found
 *         IX_ETH_ACC_FAIL if no Q has entries
 *
 * @internal
 */
PRIVATE IxEthAccStatus
00647 ixEthAccTxSwQHighestPriorityGet(IxEthAccPortId portId,
                        IxEthAccTxPriority *priorityPtr)
{
    if (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline
      == FIFO_NO_PRIORITY)
    {
      if(IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId].
             ixEthAccTxData.txQ[IX_ETH_ACC_TX_DEFAULT_PRIORITY]))
      {
          return IX_ETH_ACC_FAIL;
      }
      else
      {
          *priorityPtr = IX_ETH_ACC_TX_DEFAULT_PRIORITY;
          TX_STATS_INC(portId,txPriority[*priorityPtr]);
          return IX_ETH_ACC_SUCCESS;
      }
    }
    else
    {
      IxEthAccTxPriority highestPriority = IX_ETH_ACC_TX_PRIORITY_7;
      while(1)
      {
          if(!IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId].
             ixEthAccTxData.txQ[highestPriority]))
          {

            *priorityPtr = highestPriority;
            TX_STATS_INC(portId,txPriority[highestPriority]);
            return IX_ETH_ACC_SUCCESS;

          }
          if (highestPriority == IX_ETH_ACC_TX_PRIORITY_0)
          {
            return IX_ETH_ACC_FAIL;
          }
          highestPriority--;
      }
    }
}

/**
 *
 * @brief This function will take a buffer from a TX S/W Q and attempt
 *        to add it to the relevant TX H/W Q
 *
 * @param portId - the port whose TX queue is to be written to
 *        priority - identifies the queue from which the entry is to be read
 *
 * @internal
 */
PRIVATE IxEthAccStatus
00699 ixEthAccTxFromSwQ(IxEthAccPortId portId,
              IxEthAccTxPriority priority)
{
    IX_OSAL_MBUF        *mbuf;
    IX_STATUS        qStatus;

    IX_OSAL_ENSURE((UINT32)priority <= (UINT32)7, "Invalid priority");

    IX_ETH_ACC_DATAPLANE_REMOVE_MBUF_FROM_Q_HEAD(
      ixEthAccPortData[portId].ixEthAccTxData.txQ[priority],
      mbuf);

    if (mbuf != NULL)
    {
      /*
       * Add the Tx buffer to the H/W Tx Q
       * We do not need to flush here as it is already done
       * in TxFrameSubmit().
       */
      qStatus = ixEthAccQmgrTxWrite(
            portId,
            IX_OSAL_MMU_VIRT_TO_PHYS((UINT32)IX_ETHACC_NE_SHARED(mbuf)),
            priority);

      if (qStatus == IX_SUCCESS)
      {
          TX_STATS_INC(portId,txFromSwQOK);
          return IX_SUCCESS;
      }
      else if (qStatus == IX_QMGR_Q_OVERFLOW)
      {
          /*
           * H/W Q overflow, need to save the buffer
           * back on the s/w Q.
           * we must put it back on the head of the q to avoid
           * reordering packet tx
           */
          TX_STATS_INC(portId,txFromSwQDelayed);
          IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD(
            ixEthAccPortData[portId].ixEthAccTxData.txQ[priority],
            mbuf);

          /*enable Q notification*/
          qStatus = ixQMgrNotificationEnable(
            IX_ETH_ACC_PORT_TO_TX_Q_ID(portId),
            IX_ETH_ACC_PORT_TO_TX_Q_SOURCE(portId));

            if (qStatus != IX_SUCCESS && qStatus != IX_QMGR_WARNING)
            {
            TX_INC(portId,txUnexpectedError);
            IX_ETH_ACC_FATAL_LOG(
                  "ixEthAccTxFromSwQ:Unexpected Error: %u\n",
                  qStatus, 0, 0, 0, 0, 0);
            }
      }
      else
      {
          TX_INC(portId,txUnexpectedError);

          /* recovery attempt */
          IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD(
            ixEthAccPortData[portId].ixEthAccTxData.txQ[priority],
            mbuf);

          IX_ETH_ACC_FATAL_LOG(
            "ixEthAccTxFromSwQ:Error: unexpected QM status 0x%08X\n",
            qStatus, 0, 0, 0, 0, 0);
      }
    }
    else
    {
      /* sw queue is empty */
    }
    return IX_ETH_ACC_FAIL;
}

/**
 *
 * @brief This function will take a buffer from a RXfree S/W Q and attempt
 *        to add it to the relevant RxFree H/W Q
 *
 * @param portId - the port whose RXFree queue is to be written to
 *
 * @internal
 */
PRIVATE IxEthAccStatus
00785 ixEthAccRxFreeFromSwQ(IxEthAccPortId portId)
{
    IX_OSAL_MBUF        *mbuf;
    IX_STATUS        qStatus = IX_SUCCESS;

    IX_ETH_ACC_DATAPLANE_REMOVE_MBUF_FROM_Q_HEAD(
        ixEthAccPortData[portId].ixEthAccRxData.freeBufferList,
        mbuf);
    if (mbuf != NULL)
    {
      /*
       * Add The Rx Buffer to the H/W Free buffer Q if possible
       */
      qStatus = ixEthAccQmgrLockRxWrite(portId,
              IX_OSAL_MMU_VIRT_TO_PHYS(
                   (UINT32)IX_ETHACC_NE_SHARED(mbuf)));

      if (qStatus == IX_SUCCESS)
      {
          RX_STATS_INC(portId,rxFreeRepFromSwQOK);
          /*
           * Buffer added to h/w Q.
           */
          return IX_SUCCESS;
      }
      else if (qStatus == IX_QMGR_Q_OVERFLOW)
      {
          /*
           * H/W Q overflow, need to save the buffer back on the s/w Q.
           */
          RX_STATS_INC(portId,rxFreeRepFromSwQDelayed);

          IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD(
               ixEthAccPortData[portId].ixEthAccRxData.freeBufferList,
               mbuf);
      }
      else
      {
          /* unexpected qmgr error */
          RX_INC(portId,rxUnexpectedError);

          IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_HEAD(
                ixEthAccPortData[portId].ixEthAccRxData.freeBufferList,
                mbuf);

          IX_ETH_ACC_FATAL_LOG("IxEthAccRxFreeFromSwQ:Error: unexpected QM status 0x%08X\n",
                         qStatus, 0, 0, 0, 0, 0);
      }
    }
    else
    {
      /* sw queue is empty */
    }
    return IX_ETH_ACC_FAIL;
}


IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccInitDataPlane()
{
    UINT32 portId;

    /*
     * Initialize the service and register callback to other services.
     */

    IX_ETH_ACC_MEMSET(&ixEthAccDataStats,
                  0,
                  sizeof(ixEthAccDataStats));

    for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
      ixOsalFastMutexInit(&txWriteMutex[portId]);
      ixOsalFastMutexInit(&rxWriteMutex[portId]);

      IX_ETH_ACC_MEMSET(&ixEthAccPortData[portId],
                    0,
                    sizeof(ixEthAccPortData[portId]));

      ixEthAccPortData[portId].ixEthAccTxData.schDiscipline = FIFO_NO_PRIORITY;
    }

    return (IX_ETH_ACC_SUCCESS);
}


IX_ETH_ACC_PUBLIC
00872 IxEthAccStatus ixEthAccPortTxDoneCallbackRegister(IxEthAccPortId portId,
                                      IxEthAccPortTxDoneCallback
                                      txCallbackFn,
                                      UINT32 callbackTag)
{
    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
      return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
      return (IX_ETH_ACC_INVALID_PORT);
    }

/* HACK: removing this code to enable NPE-A preliminary testing
 *    if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId))
 *    {
 *        IX_ETH_ACC_WARNING_LOG("ixEthAccPortTxDoneCallbackRegister: Unavailable Eth %d: Cannot register TxDone Callback.\n",(INT32)portId,0,0,0,0,0);
 *        return IX_ETH_ACC_SUCCESS ;
 *    }
 */

    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
      return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }
    if (txCallbackFn == 0)
      /* Check for null function pointer here. */
    {
      return (IX_ETH_ACC_INVALID_ARG);
    }
    ixEthAccPortData[portId].ixEthAccTxData.txBufferDoneCallbackFn = txCallbackFn;
    ixEthAccPortData[portId].ixEthAccTxData.txCallbackTag = callbackTag;
    return (IX_ETH_ACC_SUCCESS);
}


IX_ETH_ACC_PUBLIC
00910 IxEthAccStatus ixEthAccPortRxCallbackRegister(IxEthAccPortId portId,
                                    IxEthAccPortRxCallback
                                    rxCallbackFn,
                                    UINT32 callbackTag)
{
    IxEthAccPortId port;

    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
      return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
      return (IX_ETH_ACC_INVALID_PORT);
    }

    if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId))
    {
        IX_ETH_ACC_WARNING_LOG("ixEthAccPortRxCallbackRegister: Unavailable Eth %d: Cannot register Rx Callback.\n",(INT32)portId,0,0,0,0,0);
        return IX_ETH_ACC_SUCCESS ;
    }

    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
      return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }

    /* Check for null function pointer here. */
    if (rxCallbackFn == NULL)
    {
      return (IX_ETH_ACC_INVALID_ARG);
    }

    /* Check the user is not changing the callback type
     * when the port is enabled.
    */
    if (ixEthAccMacState[portId].portDisableState == ACTIVE)
    {
      for (port = 0; port < IX_ETH_ACC_NUMBER_OF_PORTS; port++)
      {
          if ((ixEthAccMacState[port].portDisableState == ACTIVE)
            && (ixEthAccPortData[port].ixEthAccRxData.rxMultiBufferCallbackInUse == TRUE))
          {
            /* one of the active ports has a different rx callback type.
             * Changing the callback type when the port is enabled
             * is not safe
             */
            return (IX_ETH_ACC_INVALID_ARG);
          }
      }
    }

    /* update the callback pointer : this is done before
     * registering the new qmgr callback
     */
    ixEthAccPortData[portId].ixEthAccRxData.rxCallbackFn = rxCallbackFn;
    ixEthAccPortData[portId].ixEthAccRxData.rxCallbackTag = callbackTag;

    /* update the qmgr callback for rx queues */
    if (ixEthAccQMgrRxCallbacksRegister(ixEthRxFrameQMCallback)
      != IX_ETH_ACC_SUCCESS)
    {
      /* unexpected qmgr error */
        IX_ETH_ACC_FATAL_LOG("ixEthAccPortRxCallbackRegister: unexpected QMgr error, " \
            "could not register Rx single-buffer callback\n", 0, 0, 0, 0, 0, 0);

      RX_INC(portId,rxUnexpectedError);
      return (IX_ETH_ACC_INVALID_ARG);
    }

    ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackInUse = FALSE;

    return (IX_ETH_ACC_SUCCESS);
}

IX_ETH_ACC_PUBLIC
00986 IxEthAccStatus ixEthAccPortMultiBufferRxCallbackRegister(
                   IxEthAccPortId portId,
                   IxEthAccPortMultiBufferRxCallback
                   rxCallbackFn,
                   UINT32 callbackTag)
{
    IxEthAccPortId port;

    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
      return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
      return (IX_ETH_ACC_INVALID_PORT);
    }

    if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId))
    {
        IX_ETH_ACC_WARNING_LOG("ixEthAccPortMultiBufferRxCallbackRegister: Unavailable Eth %d: Cannot register Rx Callback.\n",(INT32)portId,0,0,0,0,0);
        return IX_ETH_ACC_SUCCESS ;
    }

    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
      return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }

    /* Check for null function pointer here. */
    if (rxCallbackFn == NULL)
    {
      return (IX_ETH_ACC_INVALID_ARG);
    }

    /* Check the user is not changing the callback type
     * when the port is enabled.
    */
    if (ixEthAccMacState[portId].portDisableState == ACTIVE)
    {
      for (port = 0; port < IX_ETH_ACC_NUMBER_OF_PORTS; port++)
      {
          if ((ixEthAccMacState[port].portDisableState == ACTIVE)
            && (ixEthAccPortData[port].ixEthAccRxData.rxMultiBufferCallbackInUse == FALSE))
          {
            /* one of the active ports has a different rx callback type.
             * Changing the callback type when the port is enabled
             * is not safe
             */
            return (IX_ETH_ACC_INVALID_ARG);
          }
      }
    }

    /* update the callback pointer : this is done before
     * registering the new qmgr callback
     */
    ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackFn = rxCallbackFn;
    ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackTag = callbackTag;

    /* update the qmgr callback for rx queues */
    if (ixEthAccQMgrRxCallbacksRegister(ixEthRxMultiBufferQMCallback)
      != IX_ETH_ACC_SUCCESS)
    {
      /* unexpected qmgr error */
      RX_INC(portId,rxUnexpectedError);

        IX_ETH_ACC_FATAL_LOG("ixEthAccPortMultiBufferRxCallbackRegister: unexpected QMgr error, " \
            "could not register Rx multi-buffer callback\n", 0, 0, 0, 0, 0, 0);

      return (IX_ETH_ACC_INVALID_ARG);
    }

    ixEthAccPortData[portId].ixEthAccRxData.rxMultiBufferCallbackInUse = TRUE;

    return (IX_ETH_ACC_SUCCESS);
}

IX_ETH_ACC_PUBLIC
01064 IxEthAccStatus ixEthAccPortTxFrameSubmit(IxEthAccPortId portId,
                               IX_OSAL_MBUF *buffer,
                               IxEthAccTxPriority priority)
{
    IX_STATUS     qStatus = IX_SUCCESS;
    UINT32      qBuffer;
    IxEthAccTxPriority highestPriority;
    IxQMgrQStatus txQStatus;

#ifndef NDEBUG
    if (buffer == NULL)
    {
      return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
      return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
      return (IX_ETH_ACC_INVALID_PORT);
    }

    if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId))
    {
        IX_ETH_ACC_FATAL_LOG("ixEthAccPortTxFrameSubmit: Unavailable Eth %d: Cannot submit Tx Frame.\n",
                       (INT32)portId,0,0,0,0,0);
        return IX_ETH_ACC_PORT_UNINITIALIZED ;
    }

    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
      return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }
    if ((UINT32)priority > (UINT32)IX_ETH_ACC_TX_PRIORITY_7)
    {
      return (IX_ETH_ACC_INVALID_ARG);
    }
#endif

    /*
     * Need to Flush the MBUF and its contents (data) as it may be
     * read from the NPE. Convert virtual addresses to physical addresses also.
     */
    qBuffer = ixEthAccMbufTxQPrepare(buffer);

    /*
     * If no fifo priority set on Xscale ...
     */
    if (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline ==
      FIFO_NO_PRIORITY)
    {
      /*
       * Add The Tx Buffer to the H/W Tx Q if possible
       * (the priority is passed to the NPE, because
       * the NPE is able to reorder the frames
       * before transmission to the underlying hardware)
       */
      qStatus = ixEthAccQmgrTxWrite(portId,
                              qBuffer,
                              IX_ETH_ACC_TX_DEFAULT_PRIORITY);

      if (qStatus == IX_SUCCESS)
      {
          TX_STATS_INC(portId,txQOK);

          /*
           * "best case" scenario : Buffer added to h/w Q.
           */
          return (IX_SUCCESS);
      }
      else if (qStatus == IX_QMGR_Q_OVERFLOW)
      {
          /*
           * We were unable to write the buffer to the
           * appropriate H/W Q,  Save it in the sw Q.
           * (use the default priority queue regardless of
           * input parameter)
           */
          priority = IX_ETH_ACC_TX_DEFAULT_PRIORITY;
      }
      else
      {
          /* unexpected qmgr error */
          TX_INC(portId,txUnexpectedError);
          IX_ETH_ACC_FATAL_LOG(
            "ixEthAccPortTxFrameSubmit:Error: qStatus = %u\n",
            (UINT32)qStatus, 0, 0, 0, 0, 0);
          return (IX_ETH_ACC_FAIL);
      }
    }
    else if (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline ==
           FIFO_PRIORITY)
    {

      /*
       * For priority transmission, put the frame directly on the H/W queue
       * if the H/W queue is empty, otherwise, put it in a S/W Q
       */
      ixQMgrQStatusGet(IX_ETH_ACC_PORT_TO_TX_Q_ID(portId), &txQStatus);
      if((txQStatus & IX_QMGR_Q_STATUS_E_BIT_MASK) != 0)
      {
          /*The tx queue is empty, check whether there are buffers on the s/w queues*/
          if(ixEthAccTxSwQHighestPriorityGet(portId,  &highestPriority)
             !=IX_ETH_ACC_FAIL)
          {
            /*there are buffers on the s/w queues, submit them*/
            ixEthAccTxFromSwQ(portId, highestPriority);

            /* the queue was empty, 1 buffer is already supplied
             * but is likely to be immediately transmitted and the
             * hw queue is likely to be empty again, so submit
             * more from the sw queues
             */
            if(ixEthAccTxSwQHighestPriorityGet(portId,  &highestPriority)
               !=IX_ETH_ACC_FAIL)
            {
                ixEthAccTxFromSwQ(portId, highestPriority);
                /*
                 * and force the buffer supplied to be placed
                 * on a priority queue
                 */
                qStatus = IX_QMGR_Q_OVERFLOW;
            }
            else
            {
                /*there are no buffers in the s/w queues, submit directly*/
                qStatus = ixEthAccQmgrTxWrite(portId, qBuffer, priority);
            }
          }
          else
          {
            /*there are no buffers in the s/w queues, submit directly*/
            qStatus = ixEthAccQmgrTxWrite(portId, qBuffer, priority);
          }
      }
      else
      {
          qStatus = IX_QMGR_Q_OVERFLOW;
      }
    }
    else
    {
      TX_INC(portId,txUnexpectedError);
      IX_ETH_ACC_FATAL_LOG(
          "ixEthAccPortTxFrameSubmit:Error: wrong schedule discipline setup\n",
          0, 0, 0, 0, 0, 0);
      return (IX_ETH_ACC_FAIL);
    }

    if(qStatus == IX_SUCCESS )
    {
      TX_STATS_INC(portId,txQOK);
      return IX_ETH_ACC_SUCCESS;
    }
    else if(qStatus == IX_QMGR_Q_OVERFLOW)
    {
      TX_STATS_INC(portId,txQDelayed);
      /*
       * We were unable to write the buffer to the
       * appropriate H/W Q,  Save it in a s/w Q.
       */
      IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_TAIL(
            ixEthAccPortData[portId].
            ixEthAccTxData.txQ[priority],
            buffer);

      qStatus = ixQMgrNotificationEnable(
            IX_ETH_ACC_PORT_TO_TX_Q_ID(portId),
            IX_ETH_ACC_PORT_TO_TX_Q_SOURCE(portId));

        if (qStatus != IX_SUCCESS)
      {
          if (qStatus == IX_QMGR_WARNING)
          {
            /* notification is enabled for a queue
             * which is already empty (the condition is already met)
             * and there will be no more queue event to drain the sw queue
             */
            TX_STATS_INC(portId,txLateNotificationEnabled);

            /* pull a buffer from the sw queue */
            if(ixEthAccTxSwQHighestPriorityGet(portId,  &highestPriority)
               !=IX_ETH_ACC_FAIL)
            {
                /*there are buffers on the s/w queues, submit from them*/
                ixEthAccTxFromSwQ(portId, highestPriority);
            }
          }
          else
          {
            TX_INC(portId,txUnexpectedError);
            IX_ETH_ACC_FATAL_LOG(
                 "ixEthAccPortTxFrameSubmit: unexpected Error: %u\n",
                 qStatus, 0, 0, 0, 0, 0);
          }
        }
    }
    else
    {
      TX_INC(portId,txUnexpectedError);
      IX_ETH_ACC_FATAL_LOG(
           "ixEthAccPortTxFrameSubmit: unexpected Error: %u\n",
           qStatus, 0, 0, 0, 0, 0);
      return (IX_ETH_ACC_FAIL);
    }

    return (IX_ETH_ACC_SUCCESS);
}


/**
 *
 * @brief replenish: convert a chain of mbufs to the format
 *        expected by the NPE
 *
  */

IX_ETH_ACC_PUBLIC
01283 IxEthAccStatus ixEthAccPortRxFreeReplenish(IxEthAccPortId portId,
                                 IX_OSAL_MBUF *buffer)
{
    IX_STATUS     qStatus = IX_SUCCESS;
    UINT32      qBuffer;

    /*
     * Check buffer is valid.
     */

#ifndef NDEBUG
    /* check parameter value */
    if (buffer == 0)
    {
      return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
      return (IX_ETH_ACC_FAIL);
    }
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
      return (IX_ETH_ACC_INVALID_PORT);
    }

    /* check initialisation is done */
    if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId))
    {
        IX_ETH_ACC_FATAL_LOG(" ixEthAccPortRxFreeReplenish: Unavailable Eth %d: Cannot replenish Rx Free Q.\n",(INT32)portId,0,0,0,0,0);
        return IX_ETH_ACC_PORT_UNINITIALIZED ;
    }

    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
      return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }
    /* check boundaries and constraints */
    if (IX_OSAL_MBUF_MLEN(buffer) < IX_ETHNPE_ACC_RXFREE_BUFFER_LENGTH_MIN)
    {
      return (IX_ETH_ACC_FAIL);
    }
#endif

    qBuffer = ixEthAccMbufRxQPrepare(buffer);

    /*
     * Add The Rx Buffer to the H/W Free buffer Q if possible
     */
    qStatus = ixEthAccQmgrLockRxWrite(portId, qBuffer);

    if (qStatus == IX_SUCCESS)
    {
      RX_STATS_INC(portId,rxFreeRepOK);
      /*
       * Buffer added to h/w Q.
       */
      return (IX_SUCCESS);
    }
    else if (qStatus == IX_QMGR_Q_OVERFLOW)
    {
      RX_STATS_INC(portId,rxFreeRepDelayed);
      /*
       * We were unable to write the buffer to the approprate H/W Q,
       * Save it in a s/w Q.
       */
      IX_ETH_ACC_DATAPLANE_ADD_MBUF_TO_Q_TAIL(
          ixEthAccPortData[portId].ixEthAccRxData.freeBufferList,
          buffer);

      qStatus = ixQMgrNotificationEnable(
          IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId),
          IX_ETH_ACC_PORT_TO_RX_FREE_Q_SOURCE(portId));

        if (qStatus != IX_SUCCESS)
      {
          if (qStatus == IX_QMGR_WARNING)
          {
            /* notification is enabled for a queue
             * which is already empty (the condition is already met)
             * and there will be no more queue event to drain the sw queue
             * move an entry from the sw queue to the hw queue */
            RX_STATS_INC(portId,rxFreeLateNotificationEnabled);
            ixEthAccRxFreeFromSwQ(portId);
          }
          else
          {
            RX_INC(portId,rxUnexpectedError);
            IX_ETH_ACC_FATAL_LOG(
                 "ixEthAccRxPortFreeReplenish:Error: %u\n",
                 qStatus, 0, 0, 0, 0, 0);
          }
        }
    }
    else
    {
      RX_INC(portId,rxUnexpectedError);
      IX_ETH_ACC_FATAL_LOG(
          "ixEthAccRxPortFreeReplenish:Error: qStatus = %u\n",
          (UINT32)qStatus, 0, 0, 0, 0, 0);
        return(IX_ETH_ACC_FAIL);
    }
    return (IX_ETH_ACC_SUCCESS);
}


IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccTxSchedulingDisciplineSetPriv(IxEthAccPortId portId,
                                     IxEthAccSchedulerDiscipline
                                     sched)
{
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
      return (IX_ETH_ACC_INVALID_PORT);
    }

    if (IX_ETH_ACC_SUCCESS != ixEthAccSingleEthNpeCheck(portId))
    {
        IX_ETH_ACC_WARNING_LOG("ixEthAccTxSchedulingDisciplineSet: Unavailable Eth %d: Cannot set Tx Scheduling Discipline.\n",(INT32)portId,0,0,0,0,0);
        return IX_ETH_ACC_SUCCESS ;
    }

    if (!IX_ETH_IS_PORT_INITIALIZED(portId))
    {
      return (IX_ETH_ACC_PORT_UNINITIALIZED);
    }

    if (sched != FIFO_PRIORITY && sched != FIFO_NO_PRIORITY)
    {
      return (IX_ETH_ACC_INVALID_ARG);
    }

    ixEthAccPortData[portId].ixEthAccTxData.schDiscipline = sched;
    return (IX_ETH_ACC_SUCCESS);
}

IX_ETH_ACC_PUBLIC
IxEthAccStatus ixEthAccRxSchedulingDisciplineSetPriv(IxEthAccSchedulerDiscipline
                                     sched)
{
    if (sched != FIFO_PRIORITY && sched != FIFO_NO_PRIORITY)
    {
      return (IX_ETH_ACC_INVALID_ARG);
    }

    ixEthAccDataInfo.schDiscipline = sched;

    return (IX_ETH_ACC_SUCCESS);
}


/**
 * @fn ixEthRxFrameProcess(IxEthAccPortId portId, IX_OSAL_MBUF *mbufPtr)
 *
 * @brief process incoming frame :
 *
 * @param @ref IxQMgrCallback IxQMgrMultiBufferCallback
 *
 * @return none
 *
 * @internal
 *
 */
IX_ETH_ACC_PRIVATE BOOL
01446 ixEthRxFrameProcess(IxEthAccPortId portId, IX_OSAL_MBUF *mbufPtr)
{
    UINT32 flags;
    IxEthDBStatus result;

#ifndef NDEBUG
    /* Prudent to at least check the port is within range */
    if (portId >= IX_ETH_ACC_NUMBER_OF_PORTS)
    {
      ixEthAccDataStats.unexpectedError++;
      IX_ETH_ACC_FATAL_LOG(
           "ixEthRxFrameProcess: Illegal port: %u\n",
           (UINT32)portId, 0, 0, 0, 0, 0);
      return FALSE;
    }
#endif

    /* convert fields from mbuf header */
    ixEthAccMbufFromRxQ(mbufPtr);

    /* check about any special processing for this frame */
    flags = IX_ETHACC_NE_FLAGS(mbufPtr);
    if ((flags & (IX_ETHACC_NE_FILTERMASK | IX_ETHACC_NE_NEWSRCMASK)) == 0)
    {
      /* "best case" scenario : nothing special to do for this frame */
      return TRUE;
    }

#ifdef CONFIG_IXP425_COMPONENT_ETHDB
    /* if a new source MAC address is detected by the NPE,
     * update IxEthDB with the portId and the MAC address.
     */
    if ((flags & IX_ETHACC_NE_NEWSRCMASK & ixEthAccNewSrcMask) != 0)
    {
        result = ixEthDBFilteringDynamicEntryProvision(portId,
                    (IxEthDBMacAddr *) IX_ETHACC_NE_SOURCEMAC(mbufPtr));

      if (result != IX_ETH_DB_SUCCESS && result != IX_ETH_DB_FEATURE_UNAVAILABLE)
      {
            if ((ixEthAccMacState[portId].portDisableState == ACTIVE) && (result != IX_ETH_DB_BUSY))
            {
              RX_STATS_INC(portId, rxUnexpectedError);
                IX_ETH_ACC_FATAL_LOG("ixEthRxFrameProcess: Failed to add source MAC \
                                    to the Learning/Filtering database\n", 0, 0, 0, 0, 0, 0);
            }
            else
            {
                /* we expect this to fail during PortDisable, as EthDB is disabled for
                 * that port and will refuse to learn new addresses
             */
            }
      }
      else
      {
          RX_STATS_INC(portId, rxUnlearnedMacAddress);
      }
    }
#endif

    /* check if this frame should have been filtered
     * by the NPE and take the appropriate action
     */
    if (((flags & IX_ETHACC_NE_FILTERMASK) != 0)
        && (ixEthAccMacState[portId].portDisableState == ACTIVE))
    {
        /* If the mbuf was allocated with a small data size, or the current data pointer is not
         * within the allocated data area, then the buffer is non-standard and has to be
         * replenished with the minimum size only
         */
        if( (IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(mbufPtr) < IX_ETHNPE_ACC_RXFREE_BUFFER_LENGTH_MIN)
           || ((UINT8 *)IX_OSAL_MBUF_ALLOCATED_BUFF_DATA(mbufPtr) > IX_OSAL_MBUF_MDATA(mbufPtr))
           || ((UINT8 *)(IX_OSAL_MBUF_ALLOCATED_BUFF_DATA(mbufPtr) +
              IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(mbufPtr))
               < IX_OSAL_MBUF_MDATA(mbufPtr)) )
        {
            /* set to minimum length */
            IX_OSAL_MBUF_MLEN(mbufPtr) = IX_OSAL_MBUF_PKT_LEN(mbufPtr) =
                IX_ETHNPE_ACC_RXFREE_BUFFER_LENGTH_MIN;
        }
        else
        {
            /* restore original length */
            IX_OSAL_MBUF_MLEN(mbufPtr) = IX_OSAL_MBUF_PKT_LEN(mbufPtr) =
                ( IX_OSAL_MBUF_ALLOCATED_BUFF_LEN(mbufPtr) -
                 (IX_OSAL_MBUF_MDATA(mbufPtr) - (UINT8 *)IX_OSAL_MBUF_ALLOCATED_BUFF_DATA(mbufPtr)) );
        }

        /* replenish from here */
        if (ixEthAccPortRxFreeReplenish(portId, mbufPtr) != IX_ETH_ACC_SUCCESS)
        {
                IX_ETH_ACC_FATAL_LOG("ixEthRxFrameProcess: Failed to replenish with filtered frame\
                                      on port %d\n", portId, 0, 0, 0, 0, 0);
        }

        RX_STATS_INC(portId, rxFiltered);

        /* indicate that frame should not be subjected to further processing */
        return FALSE;
    }

    return TRUE;
}


/**
 * @fn ixEthRxFrameQMCallback
 *
 * @brief receive callback for Frame receive Q from NPE
 *
 * Frames are passed one-at-a-time to the user
 *
 * @param @ref IxQMgrCallback
 *
 * @return none
 *
 * @internal
 *
 * Design note : while processing the entry X, entry X+1 is preloaded
 * into memory to reduce the number of stall cycles
 *
 */
void ixEthRxFrameQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId)
{
    IX_OSAL_MBUF    *mbufPtr;
    IX_OSAL_MBUF    *nextMbufPtr;
    UINT32     qEntry;
    UINT32     nextQEntry;
    UINT32     *qEntryPtr;
    UINT32     portId;
    UINT32     destPortId;
    UINT32     npeId;
    UINT32     rxQReadStatus;

    /*
     * Design note : entries are read in a buffer, This buffer contains
     * an extra zeroed entry so the loop will
     * always terminate on a null entry, whatever the result of Burst read is.
     */
    UINT32 rxQEntry[IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK + 1];

    /*
     * Indication of the number of times the callback is used.
     */
    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackCounter);

    do
    {
      /*
       * Indication of the number of times the queue is drained
       */
      IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackBurstRead);

      /* ensure the last entry of the array contains a zeroed value */
      qEntryPtr = rxQEntry;
      qEntryPtr[IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK] = 0;

      rxQReadStatus = ixQMgrQBurstRead(qId,
             IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK,
             qEntryPtr);

#ifndef NDEBUG
      if ((rxQReadStatus != IX_QMGR_Q_UNDERFLOW)
          && (rxQReadStatus != IX_SUCCESS))
      {
          ixEthAccDataStats.unexpectedError++;
          /*major error*/
          IX_ETH_ACC_FATAL_LOG(
            "ixEthRxFrameQMCallback:Error: %u\n",
            (UINT32)rxQReadStatus, 0, 0, 0, 0, 0);
          return;
      }
#endif

      /* convert and preload the next entry
       * (the conversion function takes care about null pointers which
       * are used to mark the end of the loop)
       */
      nextQEntry = *qEntryPtr;
      nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry,
                    IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

      while(nextQEntry != 0)
      {
          /* get the next entry */
          qEntry = nextQEntry;
          mbufPtr = nextMbufPtr;

#ifndef NDEBUG
          if (mbufPtr == NULL)
          {
            ixEthAccDataStats.unexpectedError++;
            IX_ETH_ACC_FATAL_LOG(
                "ixEthRxFrameQMCallback: Null Mbuf Ptr\n",
                0, 0, 0, 0, 0, 0);
            return;
          }
#endif

          /* convert the next entry
           * (the conversion function takes care about null pointers which
           * are used to mark the end of the loop)
           */
          nextQEntry = *(++qEntryPtr);
          nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry,
                        IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

          /*
           * Get Port and Npe ID from message.
           */
          npeId = ((IX_ETHNPE_QM_Q_RXENET_NPEID_MASK &
                  qEntry) >> IX_ETHNPE_QM_Q_FIELD_NPEID_R);
          portId = IX_ETH_ACC_NPE_TO_PORT_ID(npeId);

          /* process frame, check the return code and skip the remaining of
           * the loop if the frame is to be filtered out
           */
            if (ixEthRxFrameProcess(portId, mbufPtr))
            {
              /* destination portId for this packet */
              destPortId = IX_ETHACC_NE_DESTPORTID(mbufPtr);

                if (destPortId != IX_ETH_DB_UNKNOWN_PORT)
                {
                    destPortId = IX_ETH_DB_NPE_LOGICAL_ID_TO_PORT_ID(destPortId);
                }

              /* test if QoS is enabled in ethAcc
              */
              if (ixEthAccDataInfo.schDiscipline == FIFO_PRIORITY)
              {
                /* check if there is a higher priority queue
                * which may require processing and then process it.
                */
                if (ixEthAccDataInfo.higherPriorityQueue[qId] < IX_QMGR_MAX_NUM_QUEUES)
                {
                    ixEthRxFrameQMCallback(ixEthAccDataInfo.higherPriorityQueue[qId],
                                  callbackId);
                }
              }

              /*
              * increment priority stats
              */
              RX_STATS_INC(portId,rxPriority[IX_ETHACC_NE_QOS(mbufPtr)]);

              /*
              * increment callback count stats
              */
              RX_STATS_INC(portId,rxFrameClientCallback);

              /*
              * Call user level callback.
              */
              ixEthAccPortData[portId].ixEthAccRxData.rxCallbackFn(
                ixEthAccPortData[portId].ixEthAccRxData.rxCallbackTag,
                mbufPtr,
                destPortId);
            }
      }
    } while (rxQReadStatus == IX_SUCCESS);
}

/**
 * @fn ixEthRxMultiBufferQMCallback
 *
 * @brief receive callback for Frame receive Q from NPE
 *
 * Frames are passed as an array to the user
 *
 * @param @ref IxQMgrCallback
 *
 * @return none
 *
 * @internal
 *
 * Design note : while processing the entry X, entry X+1 is preloaded
 * into memory to reduce the number of stall cycles
 *
 */
void ixEthRxMultiBufferQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId)
{
    IX_OSAL_MBUF    *mbufPtr;
    IX_OSAL_MBUF    *nextMbufPtr;
    UINT32     qEntry;
    UINT32     nextQEntry;
    UINT32     *qEntryPtr;
    UINT32     portId;
    UINT32     npeId;
    UINT32     rxQReadStatus;
    /*
     * Design note : entries are read in a static buffer, This buffer contains
     * an extra zeroed entry so the loop will
     * always terminate on a null entry, whatever the result of Burst read is.
     */
    static UINT32 rxQEntry[IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK + 1];
    static IX_OSAL_MBUF *rxMbufPortArray[IX_ETH_ACC_NUMBER_OF_PORTS][IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK + 1];
    IX_OSAL_MBUF **rxMbufPtr[IX_ETH_ACC_NUMBER_OF_PORTS];

    for (portId = 0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
      rxMbufPtr[portId] = rxMbufPortArray[portId];
    }

    /*
     * Indication of the number of times the callback is used.
     */
    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackCounter);

    do
    {
      /*
       * Indication of the number of times the queue is drained
       */
      IX_ETH_ACC_STATS_INC(ixEthAccDataStats.rxCallbackBurstRead);

      /* ensure the last entry of the array contains a zeroed value */
      qEntryPtr = rxQEntry;
      qEntryPtr[IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK] = 0;

      rxQReadStatus = ixQMgrQBurstRead(qId,
             IX_ETH_ACC_MAX_RX_FRAME_CONSUME_PER_CALLBACK,
             qEntryPtr);

#ifndef NDEBUG
      if ((rxQReadStatus != IX_QMGR_Q_UNDERFLOW)
          && (rxQReadStatus != IX_SUCCESS))
      {
          ixEthAccDataStats.unexpectedError++;
          /*major error*/
          IX_ETH_ACC_FATAL_LOG(
            "ixEthRxFrameMultiBufferQMCallback:Error: %u\n",
            (UINT32)rxQReadStatus, 0, 0, 0, 0, 0);
          return;
      }
#endif

      /* convert and preload the next entry
       * (the conversion function takes care about null pointers which
       * are used to mark the end of the loop)
       */
      nextQEntry = *qEntryPtr;
      nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry,
                    IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

      while(nextQEntry != 0)
      {
          /* get the next entry */
          qEntry = nextQEntry;
          mbufPtr = nextMbufPtr;

#ifndef NDEBUG
          if (mbufPtr == NULL)
          {
            ixEthAccDataStats.unexpectedError++;
            IX_ETH_ACC_FATAL_LOG(
                "ixEthRxFrameMultiBufferQMCallback:Error: Null Mbuf Ptr\n",
                0, 0, 0, 0, 0, 0);
            return;
          }
#endif

          /* convert the next entry
           * (the conversion function takes care about null pointers which
           * are used to mark the end of the loop)
           */
          nextQEntry = *(++qEntryPtr);
          nextMbufPtr = ixEthAccEntryFromQConvert(nextQEntry,
                        IX_ETHNPE_QM_Q_RXENET_ADDR_MASK);

          /*
           * Get Port and Npe ID from message.
           */
          npeId = ((IX_ETHNPE_QM_Q_RXENET_NPEID_MASK &
                  qEntry) >>
                 IX_ETHNPE_QM_Q_FIELD_NPEID_R);
          portId = IX_ETH_ACC_NPE_TO_PORT_ID(npeId);

          /* skip the remaining of the loop if the frame is
           * to be filtered out
           */
          if (ixEthRxFrameProcess(portId, mbufPtr))
          {
            /* store a mbuf pointer in an array */
            *rxMbufPtr[portId]++ = mbufPtr;

            /*
             * increment priority stats
             */
            RX_STATS_INC(portId,rxPriority[IX_ETHACC_NE_QOS(mbufPtr)]);
          }

          /* test for QoS enabled in ethAcc */
          if (ixEthAccDataInfo.schDiscipline == FIFO_PRIORITY)
          {
            /* check if there is a higher priority queue
             * which may require processing and then process it.
             */
            if (ixEthAccDataInfo.higherPriorityQueue[qId] < IX_QMGR_MAX_NUM_QUEUES)
            {
                ixEthRxMultiBufferQMCallback(ixEthAccDataInfo.higherPriorityQueue[qId],
                                     callbackId);
            }
          }
      }

      /* check if any of the the arrays contains any entry */
      for (portId = 0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
      {
          if (rxMbufPtr[portId] != rxMbufPortArray[portId])
          {
            /* add a last NULL pointer at the end of the
             * array of mbuf pointers
             */
            *rxMbufPtr[portId] = NULL;

            /*
             * increment callback count stats
             */
            RX_STATS_INC(portId,rxFrameClientCallback);

            /*
             * Call user level callback with an array of
             * buffers (NULL terminated)
             */
            ixEthAccPortData[portId].ixEthAccRxData.
                rxMultiBufferCallbackFn(
                      ixEthAccPortData[portId].ixEthAccRxData.
                             rxMultiBufferCallbackTag,
                      rxMbufPortArray[portId]);

            /* reset the buffer pointer to the beginning of
             * the array
             */
            rxMbufPtr[portId] = rxMbufPortArray[portId];
          }
      }

    } while (rxQReadStatus == IX_SUCCESS);
}


/**
 * @brief  rxFree low event handler
 *
 */
01891 void ixEthRxFreeQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId)
{
    IxEthAccPortId      portId = (IxEthAccPortId) callbackId;
    int                   lockVal;
    UINT32        maxQWritesToPerform = IX_ETH_ACC_MAX_RX_FREE_BUFFERS_LOAD;
    IX_STATUS             qStatus = IX_SUCCESS;

    /*
     * We have reached a low threshold on one of the Rx Free Qs
     */

    /*note that due to the fact that we are working off an Empty threshold, this callback
      need only write a single entry to the Rx Free queue in order to re-arm the notification
    */

    RX_STATS_INC(portId,rxFreeLowCallback);

    /*
     * Get buffers from approprite S/W Rx freeBufferList Q.
     */

#ifndef NDEBUG
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
      ixEthAccDataStats.unexpectedError++;
      IX_ETH_ACC_FATAL_LOG(
          "ixEthRxFreeQMCallback:Error: Invalid Port 0x%08X\n",
          portId, 0, 0, 0, 0, 0);
      return;
    }
#endif
    IX_ETH_ACC_DATA_PLANE_LOCK(lockVal);
    if (IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId].
                              ixEthAccRxData.freeBufferList))
    {
      /*
       * Turn off Q callback notification for Q in Question.
       */
      qStatus = ixQMgrNotificationDisable(
          IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId));


      IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal);

      if (qStatus != IX_SUCCESS)
      {
          RX_INC(portId,rxUnexpectedError);
          IX_ETH_ACC_FATAL_LOG(
            "ixEthRxFreeQMCallback:Error: unexpected QM status 0x%08X\n",
            qStatus, 0, 0, 0, 0, 0);
          return;
      }
    }
    else
    {
      IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal);
      /*
       * Load the H/W Q with buffers from the s/w Q.
       */

      do
      {
          /*
           * Consume Q entries. - Note Q contains Physical addresss,
           * and have already been flushed to memory,
           * And endianess converted if required.
           */
          if (ixEthAccRxFreeFromSwQ(portId) != IX_SUCCESS)
          {
            /*
             * No more entries in s/w Q.
             * Turn off Q callback indication
             */

            IX_ETH_ACC_DATA_PLANE_LOCK(lockVal);
            if (IX_ETH_ACC_DATAPLANE_IS_Q_EMPTY(ixEthAccPortData[portId].
                ixEthAccRxData.freeBufferList))
            {
                qStatus = ixQMgrNotificationDisable(
                  IX_ETH_ACC_PORT_TO_RX_FREE_Q_ID(portId));
            }
            IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal);
            break;
          }
      }
      while (--maxQWritesToPerform);
    }
}
/**
 * @fn Tx queue low event handler
 *
 */
void
ixEthTxFrameQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId)
{
    IxEthAccPortId portId = (IxEthAccPortId) callbackId;
    int              lockVal;
    UINT32     maxQWritesToPerform = IX_ETH_ACC_MAX_TX_FRAME_TX_CONSUME_PER_CALLBACK;
    IX_STATUS        qStatus = IX_SUCCESS;
    IxEthAccTxPriority highestPriority;


    /*
     * We have reached a low threshold on the Tx Q, and are being asked to
     * supply a buffer for transmission from our S/W TX queues
     */
    TX_STATS_INC(portId,txLowThreshCallback);

    /*
     * Get buffers from approprite Q.
     */

#ifndef NDEBUG
    if (!IX_ETH_ACC_IS_PORT_VALID(portId))
    {
      ixEthAccDataStats.unexpectedError++;
      IX_ETH_ACC_FATAL_LOG(
          "ixEthTxFrameQMCallback:Error: Invalid Port 0x%08X\n",
          portId, 0, 0, 0, 0, 0);
      return;
    }
#endif

    do
    {
      /*
       * Consume Q entries. - Note Q contains Physical addresss,
       * and have already been flushed to memory,
       * and endianess already sone if required.
       */

      IX_ETH_ACC_DATA_PLANE_LOCK(lockVal);

      if(ixEthAccTxSwQHighestPriorityGet(portId, &highestPriority) ==
         IX_ETH_ACC_FAIL)
      {
          /*
           * No more entries in s/w Q.
           * Turn off Q callback indication
           */
          qStatus = ixQMgrNotificationDisable(
            IX_ETH_ACC_PORT_TO_TX_Q_ID(portId));

          IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal);

          if (qStatus != IX_SUCCESS)
          {
            ixEthAccDataStats.unexpectedError++;
            IX_ETH_ACC_FATAL_LOG(
                "ixEthTxFrameQMCallback:Error: unexpected QM status 0x%08X\n",
                qStatus, 0, 0, 0, 0, 0);
          }

          return;
      }
      else
      {
          IX_ETH_ACC_DATA_PLANE_UNLOCK(lockVal);
          if (ixEthAccTxFromSwQ(portId,highestPriority)!=IX_SUCCESS)
          {
                /* nothing left in the sw queue or the hw queues are
                * full. There is no point to continue to drain the
                * sw queues
                */
            return;
          }
      }
    }
    while (--maxQWritesToPerform);
}

/**
 * @brief TxDone event handler
 *
 * Design note : while processing the entry X, entry X+1 is preloaded
 * into memory to reduce the number of stall cycles
 *
 */

void
02071 ixEthTxFrameDoneQMCallback(IxQMgrQId qId, IxQMgrCallbackId callbackId)
{
    IX_OSAL_MBUF    *mbufPtr;
    UINT32     qEntry;
    UINT32     *qEntryPtr;
    UINT32     txDoneQReadStatus;
    UINT32     portId;
    UINT32     npeId;

    /*
     * Design note : entries are read in a static buffer, This buffer contains
     * an extra entyry (which is zeroed by the compiler), so the loop will
     * always terminate on a null entry, whatever the result of Burst read is.
     */
    static UINT32 txDoneQEntry[IX_ETH_ACC_MAX_TX_FRAME_DONE_CONSUME_PER_CALLBACK + 1];

    /*
     * Indication that Tx frames have been transmitted from the NPE.
     */

    IX_ETH_ACC_STATS_INC(ixEthAccDataStats.txDoneCallbackCounter);

    do{
      qEntryPtr = txDoneQEntry;
      txDoneQReadStatus = ixQMgrQBurstRead(IX_ETH_ACC_TX_FRAME_DONE_ETH_Q,
                 IX_ETH_ACC_MAX_TX_FRAME_DONE_CONSUME_PER_CALLBACK,
                 qEntryPtr);

#ifndef NDEBUG
      if (txDoneQReadStatus != IX_QMGR_Q_UNDERFLOW
          && (txDoneQReadStatus != IX_SUCCESS))
      {
          /*major error*/
          ixEthAccDataStats.unexpectedError++;
          IX_ETH_ACC_FATAL_LOG(
            "ixEthTxFrameDoneQMCallback:Error: %u\n",
            (UINT32)txDoneQReadStatus, 0, 0, 0, 0, 0);
          return;
      }
#endif

      qEntry = *qEntryPtr;

      while(qEntry != 0)
      {
          mbufPtr = ixEthAccEntryFromQConvert(qEntry,
                  IX_ETHNPE_QM_Q_TXENET_ADDR_MASK);

#ifndef NDEBUG
          if (mbufPtr == NULL)
          {
            ixEthAccDataStats.unexpectedError++;
            IX_ETH_ACC_FATAL_LOG(
                "ixEthTxFrameDoneQMCallback:Error: Null Mbuf Ptr\n",
                0, 0, 0, 0, 0, 0);
            return;
          }
#endif

          /* endianness conversions and stats updates */
          ixEthAccMbufFromTxQ(mbufPtr);

          /*
           * Get NPE id from message, then convert to portId.
           */
          npeId = ((IX_ETHNPE_QM_Q_TXENETDONE_NPEID_MASK &
                   qEntry) >>
                  IX_ETHNPE_QM_Q_FIELD_NPEID_R);
          portId = IX_ETH_ACC_NPE_TO_PORT_ID(npeId);

#ifndef NDEBUG
          /* Prudent to at least check the port is within range */
          if (portId >= IX_ETH_ACC_NUMBER_OF_PORTS)
          {
            ixEthAccDataStats.unexpectedError++;
            IX_ETH_ACC_FATAL_LOG(
                "ixEthTxFrameDoneQMCallback: Illegal port: %u\n",
                (UINT32)portId, 0, 0, 0, 0, 0);
            return;
          }
#endif

          TX_STATS_INC(portId,txDoneClientCallback);

          /*
           * Call user level callback.
           */
          ixEthAccPortData[portId].ixEthAccTxData.txBufferDoneCallbackFn(
            ixEthAccPortData[portId].ixEthAccTxData.txCallbackTag,
            mbufPtr);

          /* move to next queue entry */
          qEntry = *(++qEntryPtr);

      }
    } while( txDoneQReadStatus == IX_SUCCESS );
}

IX_ETH_ACC_PUBLIC
02170 void ixEthAccDataPlaneShow(void)
{
    UINT32 numTx0Entries;
    UINT32 numTx1Entries;
    UINT32 numTxDoneEntries;
    UINT32 numRxEntries;
    UINT32 numRxFree0Entries;
    UINT32 numRxFree1Entries;
    UINT32 portId;
#ifdef __ixp46X
    UINT32 numTx2Entries;
    UINT32 numRxFree2Entries;
#endif
#ifndef NDEBUG
    UINT32 priority;
    UINT32 numBuffersInRx=0;
    UINT32 numBuffersInTx=0;
    UINT32 numBuffersInSwQ=0;
    UINT32 totalBuffers=0;
    UINT32 rxFreeCallbackCounter = 0;
    UINT32 txCallbackCounter = 0;
#endif
    UINT32 key;

    /* snapshot of stats */
    IxEthAccTxDataStats tx[IX_ETH_ACC_NUMBER_OF_PORTS];
    IxEthAccRxDataStats rx[IX_ETH_ACC_NUMBER_OF_PORTS];
    IxEthAccDataPlaneStats stats;

    if (!IX_ETH_ACC_IS_SERVICE_INITIALIZED())
    {
      return;
    }

    /* get a reliable snapshot */
    key = ixOsalIrqLock();

    numTx0Entries = 0;
    ixQMgrQNumEntriesGet(IX_ETH_ACC_TX_FRAME_ENET0_Q, &numTx0Entries);
    numTx1Entries = 0;
    ixQMgrQNumEntriesGet(IX_ETH_ACC_TX_FRAME_ENET1_Q, &numTx1Entries);
    numTxDoneEntries = 0;
    ixQMgrQNumEntriesGet( IX_ETH_ACC_TX_FRAME_DONE_ETH_Q, &numTxDoneEntries);
    numRxEntries = 0;
    ixEthAccQMgrRxQEntryGet(&numRxEntries);
    numRxFree0Entries = 0;
    ixQMgrQNumEntriesGet(IX_ETH_ACC_RX_FREE_BUFF_ENET0_Q, &numRxFree0Entries);
    numRxFree1Entries = 0;
    ixQMgrQNumEntriesGet(IX_ETH_ACC_RX_FREE_BUFF_ENET1_Q, &numRxFree1Entries);

#ifdef __ixp46X
    numTx2Entries = 0;
    ixQMgrQNumEntriesGet(IX_ETH_ACC_TX_FRAME_ENET2_Q, &numTx2Entries);
    numRxFree2Entries = 0;
    ixQMgrQNumEntriesGet(IX_ETH_ACC_RX_FREE_BUFF_ENET2_Q, &numRxFree2Entries);
#endif

    for(portId=IX_ETH_PORT_1; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
      memcpy(&tx[portId],
             &ixEthAccPortData[portId].ixEthAccTxData.stats,
             sizeof(tx[portId]));
      memcpy(&rx[portId],
             &ixEthAccPortData[portId].ixEthAccRxData.stats,
             sizeof(rx[portId]));
    }
    memcpy(&stats, &ixEthAccDataStats, sizeof(stats));

    ixOsalIrqUnlock(key);

#ifdef NDEBUG
    printf("Detailed statistics collection not supported in this load\n");
#endif

    /* print snapshot */
    for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
        /* If not IXP42X A0 stepping, proceed to check for existence of coprocessors */
        if ((IX_FEATURE_CTRL_SILICON_TYPE_A0 !=
           (ixFeatureCtrlProductIdRead() & IX_FEATURE_CTRL_SILICON_STEPPING_MASK))
          || (IX_FEATURE_CTRL_DEVICE_TYPE_IXP42X != ixFeatureCtrlDeviceRead ()))
        {
                if ((IX_ETH_PORT_1 == portId) &&
                    (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_ETH0) ==
                     IX_FEATURE_CTRL_COMPONENT_DISABLED))
                {
                   continue ;
                }
                if ((IX_ETH_PORT_2 == portId) &&
                    (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_ETH1) ==
                     IX_FEATURE_CTRL_COMPONENT_DISABLED))
                {
                    continue ;
                }
                if ((IX_ETH_PORT_3 == portId) &&
                    (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_NPEA_ETH) ==
                     IX_FEATURE_CTRL_COMPONENT_DISABLED))
                {
                    continue ;
                }
        }

      printf("PORT %u --------------------------------\n",
             portId);
#ifndef NDEBUG
      printf("Tx Done Frames                : %u\n",
             tx[portId].txDoneClientCallback +
             tx[portId].txDoneSwQDuringDisable +
             tx[portId].txDoneDuringDisable);
      printf("Tx Frames                     : %u\n",
             tx[portId].txQOK + tx[portId].txQDelayed);
      printf("Tx H/W Q Added OK             : %u\n",
             tx[portId].txQOK);
      printf("Tx H/W Q Delayed              : %u\n",
             tx[portId].txQDelayed);
      printf("Tx From S/W Q Added OK        : %u\n",
             tx[portId].txFromSwQOK);
      printf("Tx From S/W Q Delayed         : %u\n",
             tx[portId].txFromSwQDelayed);
      printf("Tx Overflow                   : %u\n",
             tx[portId].txOverflow);
      printf("Tx Mutual Lock                : %u\n",
             tx[portId].txLock);
      printf("Tx Late Ntf Enabled           : %u\n",
             tx[portId].txLateNotificationEnabled);
      printf("Tx Low Thresh CB              : %u\n",
             tx[portId].txLowThreshCallback);
      printf("Tx Done from H/W Q (Disable)  : %u\n",
             tx[portId].txDoneDuringDisable);
      printf("Tx Done from S/W Q (Disable)  : %u\n",
             tx[portId].txDoneSwQDuringDisable);
      for (priority = IX_ETH_ACC_TX_PRIORITY_0;
           priority <= IX_ETH_ACC_TX_PRIORITY_7;
           priority++)
      {
          if (tx[portId].txPriority[priority])
          {
            printf("Tx Priority %u                 : %u\n",
                   priority,
                   tx[portId].txPriority[priority]);
          }
      }
#endif
      printf("Tx unexpected errors          : %u (should be 0)\n",
             tx[portId].txUnexpectedError);

#ifndef NDEBUG
      printf("Rx Frames                     : %u\n",
             rx[portId].rxFrameClientCallback +
             rx[portId].rxSwQDuringDisable+
             rx[portId].rxDuringDisable);
      printf("Rx Free Replenish             : %u\n",
             rx[portId].rxFreeRepOK + rx[portId].rxFreeRepDelayed);
      printf("Rx Free H/W Q Added OK        : %u\n",
             rx[portId].rxFreeRepOK);
      printf("Rx Free H/W Q Delayed         : %u\n",
             rx[portId].rxFreeRepDelayed);
      printf("Rx Free From S/W Q Added OK   : %u\n",
             rx[portId].rxFreeRepFromSwQOK);
      printf("Rx Free From S/W Q Delayed    : %u\n",
             rx[portId].rxFreeRepFromSwQDelayed);
      printf("Rx Free Overflow              : %u\n",
             rx[portId].rxFreeOverflow);
      printf("Rx Free Mutual Lock           : %u\n",
             rx[portId].rxFreeLock);
      printf("Rx Free Late Ntf Enabled      : %u\n",
             rx[portId].rxFreeLateNotificationEnabled);
      printf("Rx Free Low CB                : %u\n",
             rx[portId].rxFreeLowCallback);
      printf("Rx From H/W Q (Disable)       : %u\n",
             rx[portId].rxDuringDisable);
      printf("Rx From S/W Q (Disable)       : %u\n",
             rx[portId].rxSwQDuringDisable);
      printf("Rx unlearned Mac Address      : %u\n",
             rx[portId].rxUnlearnedMacAddress);
        printf("Rx Filtered (Rx => RxFree)    : %u\n",
            rx[portId].rxFiltered);

      for (priority = IX_ETH_ACC_TX_PRIORITY_0;
           priority <= IX_ETH_ACC_TX_PRIORITY_7;
           priority++)
      {
          if (rx[portId].rxPriority[priority])
          {
            printf("Rx Priority %u                 : %u\n",
                   priority,
                   rx[portId].rxPriority[priority]);
          }
      }
#endif
      printf("Rx unexpected errors          : %u (should be 0)\n",
             rx[portId].rxUnexpectedError);

#ifndef NDEBUG
      numBuffersInTx = tx[portId].txQOK +
          tx[portId].txQDelayed -
          tx[portId].txDoneClientCallback -
          tx[portId].txDoneSwQDuringDisable -
          tx[portId].txDoneDuringDisable;

      printf("# Tx Buffers currently for transmission : %u\n",
             numBuffersInTx);

      numBuffersInRx = rx[portId].rxFreeRepOK +
          rx[portId].rxFreeRepDelayed -
          rx[portId].rxFrameClientCallback -
          rx[portId].rxSwQDuringDisable -
          rx[portId].rxDuringDisable;

      printf("# Rx Buffers currently for reception    : %u\n",
             numBuffersInRx);

      totalBuffers += numBuffersInRx + numBuffersInTx;
#endif
    }

    printf("---------------------------------------\n");

#ifndef NDEBUG
    printf("\n");
    printf("Mbufs :\n");
    printf("Tx Unchained mbufs            : %u\n",
         stats.unchainedTxMBufs);
    printf("Tx Chained bufs               : %u\n",
         stats.chainedTxMBufs);
    printf("TxDone Unchained mbufs        : %u\n",
         stats.unchainedTxDoneMBufs);
    printf("TxDone Chained bufs           : %u\n",
         stats.chainedTxDoneMBufs);
    printf("RxFree Unchained mbufs        : %u\n",
         stats.unchainedRxFreeMBufs);
    printf("RxFree Chained bufs           : %u\n",
         stats.chainedRxFreeMBufs);
    printf("Rx Unchained mbufs            : %u\n",
         stats.unchainedRxMBufs);
    printf("Rx Chained bufs               : %u\n",
         stats.chainedRxMBufs);

    printf("\n");
    printf("Software queue usage :\n");
    printf("Buffers added to S/W Q        : %u\n",
         stats.addToSwQ);
    printf("Buffers removed from S/W Q    : %u\n",
         stats.removeFromSwQ);

    printf("\n");
    printf("Hardware queues callbacks :\n");

    for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
      rxFreeCallbackCounter += rx[portId].rxFreeLowCallback;
      txCallbackCounter += tx[portId].txLowThreshCallback;
    }
    printf("Tx Done QM Callback invoked   : %u\n",
         stats.txDoneCallbackCounter);
    printf("Tx QM Callback invoked        : %u\n",
         txCallbackCounter);
    printf("Rx QM Callback invoked        : %u\n",
         stats.rxCallbackCounter);
    printf("Rx QM Callback burst read     : %u\n",
         stats.rxCallbackBurstRead);
    printf("Rx Free QM Callback invoked   : %u\n",
         rxFreeCallbackCounter);
#endif
    printf("Unexpected errors in CB       : %u (should be 0)\n",
         stats.unexpectedError);
    printf("\n");

    printf("Hardware queues levels :\n");
    printf("Transmit Port 1 Q             : %u \n",numTx0Entries);
    printf("Transmit Port 2 Q             : %u \n",numTx1Entries);
#ifdef __ixp46X
    printf("Transmit Port 3 Q             : %u \n",numTx2Entries);
#endif
    printf("Transmit Done Q               : %u \n",numTxDoneEntries);
    printf("Receive Q                     : %u \n",numRxEntries);
    printf("Receive Free Port 1 Q         : %u \n",numRxFree0Entries);
    printf("Receive Free Port 2 Q         : %u \n",numRxFree1Entries);
#ifdef __ixp46X
    printf("Receive Free Port 3 Q         : %u \n",numRxFree2Entries);
#endif

#ifndef NDEBUG
    printf("\n");
    printf("# Total Buffers accounted for : %u\n",
         totalBuffers);

    numBuffersInSwQ = ixEthAccDataStats.addToSwQ -
      ixEthAccDataStats.removeFromSwQ;

    printf("    Buffers in S/W Qs         : %u\n",
         numBuffersInSwQ);
    printf("    Buffers in H/W Qs or NPEs : %u\n",
         totalBuffers - numBuffersInSwQ);
#endif

    printf("Rx QoS Discipline             : %s\n",
         (ixEthAccDataInfo.schDiscipline ==
          FIFO_PRIORITY ) ? "Enabled" : "Disabled");

    for(portId=0; portId < IX_ETH_ACC_NUMBER_OF_PORTS; portId++)
    {
      printf("Tx QoS Discipline port %u      : %s\n",
             portId,
             (ixEthAccPortData[portId].ixEthAccTxData.schDiscipline ==
            FIFO_PRIORITY ) ? "Enabled" : "Disabled");
    }
    printf("\n");
}






Generated by  Doxygen 1.6.0   Back to index