/*************************************************************************
 *  TNETDxxxx Software Support
 *  Copyright (c) 2002,2003 Texas Instruments Incorporated. All Rights Reserved.
 *
 *  FILE: cppi.c  
 *
 *  DESCRIPTION:
 *      This file contains shared code for all CPPI modules.
 *
 *  HISTORY:
 *   7Aug02 Greg RC1.00  Original Version created.
 *  27Sep02 Mick RC1.01  Merged for use by CPMAC/CPSAR
 *  16Oct02 Mick RC1.02  Performance Tweaks (see cppihist.txt)
 *  12Nov02 Mick RC1.02  Updated to use cpmac_reg.h
 *  09Jan03 Mick RC3.01  Removed modification to RxBuffer ptr
 *  28Mar03 Mick 1.03    cpswHalEswitchRxReturn now returns error if Malloc Fails
 *  10Apr03 Mick 1.03.02 Added Needs Buffer Support
 *  11Jun03 Mick 1.06.02 Send() errors corrected
 *  07Jul03 Menaka       Updated for USB20
 *
 *  @author  Greg Guyotte
 *  @version 1.00
 *  @date    7-Aug-2002
 *****************************************************************************/
#ifndef _CPHAL                       /* +GSG 030320 */
#include "cpswhaleswitchprivate.h"               /* +GSG 030320 */
#define static                       /* +GSG 030320 */
#endif                               /* +GSG 030320 */

HAL_CONTROL_KEY hcVersion_CPPI_cpswHalEswitch[]="CPPI 02.00.00 "__DATE__" "__TIME__;

#define CB_SOF_BIT         (1<<31)
#define CB_EOF_BIT         (1<<30)
#define CB_OWNERSHIP_BIT   (1<<29)
#define CB_EOQ_BIT         (1<<28)
#define CB_SIZE_MASK       0x0000ffff
#define CB_OFFSET_MASK     0xffff0000
#define TEARDOWN_VAL       0xfffffffc

#ifdef _CPHAL_AAL5
/*
 *  This function takes a vpi/vci pair and computes the 4 byte atm header
 *  (minus the HEC).
 *
 *  @param  vpi  Virtual Path Identifier.
 *  @param  vci  Virtual Channel Identifier.
 *
 *  @return  A properly formatted ATM header, without the HEC.
 */ 
static int atmheader(int gfc, int vpi, int vci, int pti, int clp)
  {
   int itmp;

   itmp=0;
  
   /* UNI Mode uses the GFC field */
   itmp |= ((gfc & 0xF) << 28);
   itmp |= ((vpi & 0xFF) << 20);
    
   /* if NNI Mode, no gfc and larger VPI */
   /*itmp |= ((vpi & 0xFFF) << 20);*/

   itmp|=((vci & 0xFFFF) << 4);
   itmp|=((pti & 0x7) << 1);
   itmp|=(clp & 0x1);
   return(itmp);
  }
#endif

static void cpswHalEswitchFreeRx(HAL_DEVICE *HalDev, int Ch) 
  {
   HAL_RCB *rcb_ptr;                                                /*+GSG 030303*/
   int rcbSize = (sizeof(HAL_RCB)+0xf)&~0xf;                        /*+GSG 030303*/
   int Num = HalDev->ChData[Ch].RxNumBuffers, i;                    /*+GSG 030303*/
   cppi_rxcntl_s *pRxCtl=&HalDev->RxCtlBlk[Ch];                     /*+GSG 030320*/

   dbgprint(1<<0,"cpswHalEswitchFreeRx(HalDev:%08x, Ch:%d)\n", (bit32u)HalDev, Ch); /*~GSG 030320 */

   /* Free Rx data buffers attached to descriptors, if necessary */
   if (pRxCtl->RcbStart != 0)                                       /*~GSG 030320*/
     {                                                              /*+GSG 030303*/
      for(i=0;i<Num;i++)                                            /*+GSG 030303*/
        {                                                           /*+GSG 030303*/
         rcb_ptr = (HAL_RCB *)(pRxCtl->RcbStart + (i*rcbSize));     /*+GSG 030303*/

         /* free the data buffer */
         if (rcb_ptr->DatPtr != 0)
	   {
            dbgprint(0xa,"[os]FreeRxBuffer(MemPtr:%08x)\n", (bit32u)rcb_ptr->DatPtr);
            dbgprint(1<<6,"  Freeing Data Buffer, Ch %d\n", Ch);    /*+GSG 030303 */

	    HalDev->OsFunc->FreeRxBuffer((void *)rcb_ptr->OsInfo, (void *)rcb_ptr->DatPtr);
            rcb_ptr->OsInfo=0;                                        /*MJH+030522*/
            rcb_ptr->DatPtr=0;                                        /*MJH+030522*/
	   }
	}                                                           /*+GSG 030303*/
     }                                                              /*+GSG 030303*/

   dbgprint(0xa,"[os]Free(MemPtr:%08x)\n", (bit32u)pRxCtl->RcbStart); /*~GSG 030320 */
   dbgprint(1<<6,"  Freeing Rx buffer descriptors, Ch %d\n", Ch);     /*~GSG 030320 */

   /* free up all desciptors at once */
   HalDev->OsFunc->FreeDmaXfer(pRxCtl->RcbStart);

   /* mark buffers as freed */
   pRxCtl->RcbStart = 0;
  }

static void cpswHalEswitchFreeTx(HAL_DEVICE *HalDev, int Ch, int Queue)
  {
   cppi_txcntl_s *pTxCtl=&HalDev->TxCtlBlk[Ch][Queue]; /* +GSG 030321 */

   dbgprint(1,"[cppi]cpswHalEswitchFreeTx(HalDev:%08x, Ch:%d, Queue:%d)\n", (bit32u)HalDev, Ch, Queue); /* +GSG 030321 */

   dbgprint(0xa,"[cppi->os]FreeDmaXfer(MemPtr:%08x)\n", (bit32u)pTxCtl->TcbStart); /* +GSG 030321 */
   dbgprint(1<<6,"  [cppi cpswHalEswitchFreeTx()]Freeing Tx buffer descriptors, Ch %d, Queue %d\n", Ch, Queue); /* +GSG 030321 */

   /* free all descriptors at once */
   HalDev->OsFunc->FreeDmaXfer(pTxCtl->TcbStart); /* ~GSG 030321 */

   pTxCtl->TcbStart = 0; /* ~GSG 030321 */
  }

/* return of 0 means that this code executed, -1 means the interrupt was not 
   a teardown interrupt */
static int cpswHalEswitchRxTeardownInt(HAL_DEVICE *HalDev, int Ch)
  { 
   cppi_rxcntl_s *pRxCtl=&HalDev->RxCtlBlk[Ch]; /* +GSG 030321 */

#ifdef _CPHAL_AAL5
   int i;
   volatile bit32u *pTmp;
#endif

   dbgprint(1,"[cppi]cpswHalEswitchRxTeardownInt(HalDev:%08x, Ch:%d)\n", (bit32u)HalDev, Ch); /* ~GSG 030321 */

   /* check to see if the interrupt is a teardown interrupt */
   if ((RX_DMA_STATE_CP(HalDev->dev_base,Ch) & TEARDOWN_VAL) == TEARDOWN_VAL)
     {
      /* finish channel teardown */

      /* Free channel resources on a FULL teardown */
      if (HalDev->RxTeardownPending[Ch] & FULL_TEARDOWN)
        {
         cpswHalEswitchFreeRx(HalDev, Ch);
        }

      /* bug fix - clear Rx channel pointers on teardown */
      pRxCtl->RcbPool         = 0;     /* ~GSG 030321 */
      pRxCtl->RxActQueueHead  = 0;     /* ~GSG 030321 */
      pRxCtl->RxActQueueCount = 0;     /* ~GSG 030321 */
      pRxCtl->RxActive        = FALSE; /* ~GSG 030321 */

      /* write completion pointer */
      RX_DMA_STATE_CP(HalDev->dev_base,Ch) = TEARDOWN_VAL;

      /* use direction bit as a teardown pending bit! May be able to
         use only one teardown pending integer in HalDev */

      HalDev->RxTeardownPending[Ch] &= ~RX_TEARDOWN;

      HalDev->ChIsOpen[Ch][DIRECTION_RX] = 0;

#ifdef _CPHAL_AAL5
      /* call OS Teardown Complete (if TX is also done) */
      if ((HalDev->TxTeardownPending[Ch] & TX_TEARDOWN) == 0)
	{
	 /* mark channel as closed */
         HalDev->ChIsOpen[Ch][DIRECTION_TX] = 0;
        
	 /* disable channel interrupt */
	 SAR_TX_MASK_CLR(HalDev->dev_base) = (1<<Ch);
         SAR_TX_MASK_CLR(HalDev->dev_base) = (1<<(Ch+16)); /* +GSG 030307 */
	 SAR_RX_MASK_CLR(HalDev->dev_base) = (1<<Ch);

         /* Clear PDSP Channel State RAM */
         pTmp = (pPDSP_BLOCK_0(HalDev->dev_base)+(Ch*64));
         for (i=0; i<NUM_PDSP_AAL5_STATE_WORDS; i++)
           *pTmp++ = 0;

	 if ((HalDev->RxTeardownPending[Ch] & BLOCKING_TEARDOWN) == 0)
	   {
            dbgprint(2,"[cppi->os]TeardownComplete(OsDev:%08x, Ch:%d, Direction:%d)\n", 
                     (bit32u)HalDev->OsDev, Ch, DIRECTION_TX|DIRECTION_RX); /* ~GSG 030321 */

	    HalDev->OsFunc->TeardownComplete(HalDev->OsDev, Ch, DIRECTION_TX|DIRECTION_RX);
	   }
	 /* clear all teardown pending information for this channel */
	 HalDev->RxTeardownPending[Ch] = 0;
	 HalDev->TxTeardownPending[Ch] = 0;
	}
#endif
 
#ifdef _CPHAL_CPMAC
      HalDev->ChIsOpen[Ch][DIRECTION_RX] = 0;
      CPMAC_RX_INTMASK_CLEAR(HalDev->dev_base) = (1<<Ch);
      if ((HalDev->RxTeardownPending[Ch] & BLOCKING_TEARDOWN) == 0)
	{
         dbgprint(2,"[cppi->os]TeardownComplete(OsDev:%08x, Ch:%d, Direction:%d)\n", 
                  (bit32u)HalDev->OsDev, Ch, DIRECTION_RX);

	 HalDev->OsFunc->TeardownComplete(HalDev->OsDev, Ch, DIRECTION_RX);
	}
      HalDev->RxTeardownPending[Ch] = 0;
#endif

      return (EC_NO_ERRORS);
     }
   return (-1);
  }

/* return of 0 means that this code executed, -1 means the interrupt was not 
   a teardown interrupt */
static int cpswHalEswitchTxTeardownInt(HAL_DEVICE *HalDev, int Ch, int Queue)
  {
   HAL_TCB *Last, *Curr, *First;                                    /*+GSG 030303*/
   cppi_txcntl_s *pTxCtl=&HalDev->TxCtlBlk[Ch][Queue];              /*+GSG 030321*/

#ifdef _CPHAL_AAL5
   int i;
   volatile bit32u *pTmp;
#endif

   dbgprint(1,"[cppi]cpswHalEswitchTxTeardownInt(HalDev:%08x, Ch:%d, Queue:%d)\n", (bit32u)HalDev, Ch, Queue);

   if ((TX_DMA_STATE_CP(HalDev->dev_base,Ch,Queue) & TEARDOWN_VAL) == TEARDOWN_VAL)
     {
      /* return outstanding buffers to OS                             +RC3.02*/
      Curr = pTxCtl->TxActQueueHead;                                /*+GSG 030303*/
      First = Curr;                                                 /*+GSG 030303*/
      while (Curr)                                                  /*+GSG 030303*/
        {                                                           /*+GSG 030303*/
         /* Pop TCB(s) for packet from the stack */                 /*+GSG 030303*/
         Last = Curr->Eop;                                          /*+GSG 030303*/
         pTxCtl->TxActQueueHead = Last->Next;                       /*+GSG 030303*/
                                                                    /*+GSG 030303*/
         /* return to OS */                                         /*+GSG 030303*/
         Curr->SendComplete(Curr->OsInfo); /* HAL 2.0 */
                                                                    /*+GSG 030303*/
         /* Push Tcb(s) back onto the stack */                      /*+GSG 030303*/
         Curr = Last->Next;                                         /*+GSG 030303*/
         Last->Next = pTxCtl->TcbPool;                              /*+GSG 030303*/
         pTxCtl->TcbPool = First;                                   /*+GSG 030303*/
                                                                    /*+GSG 030303*/
         /* set the first(SOP) pointer for the next packet */       /*+GSG 030303*/
         First = Curr;                                              /*+GSG 030303*/
        }                                                           /*+GSG 030303*/
      
      /* finish channel teardown */
     
      /* save the OsInfo to pass to upper layer
         THIS WAS CRASHING - because it's possible that I get the teardown
         notification and the TcbHPool is null.  In this case, the buffers
         to free can be found in the TxHActiveQueue.  If I need to get OsInfo
         in the future, I can get it from one of those buffers.
         OsInfo = pTxCtl->TcbHPool->OsInfo; */

      if (HalDev->TxTeardownPending[Ch] & FULL_TEARDOWN)
        { 
	 cpswHalEswitchFreeTx(HalDev, Ch, Queue);
	} /* if FULL teardown */

      /* bug fix - clear Tx channel pointers on teardown */
      pTxCtl->TcbPool = 0;             /* ~GSG 030321 */
      pTxCtl->TxActQueueHead  = 0;     /* ~GSG 030321 */
      pTxCtl->TxActQueueCount = 0;     /* ~GSG 030321 */
      pTxCtl->TxActive        = FALSE; /* ~GSG 030321 */

      /* write completion pointer */
      TX_DMA_STATE_CP(HalDev->dev_base,Ch,Queue) = TEARDOWN_VAL;     

      /* no longer pending teardown */
      HalDev->TxTeardownPending[Ch] &= ~TX_TEARDOWN;

      HalDev->ChIsOpen[Ch][DIRECTION_TX] = 0;

#ifdef _CPHAL_AAL5
      /* call OS Teardown Complete (if Rx is also done) */
      if ((HalDev->RxTeardownPending[Ch] & RX_TEARDOWN) == 0)
	{
	 /* mark channel as closed */
	 HalDev->ChIsOpen[Ch][DIRECTION_RX] = 0;

	 /* disable channel interrupt */
	 SAR_TX_MASK_CLR(HalDev->dev_base) = (1<<Ch);
         SAR_TX_MASK_CLR(HalDev->dev_base) = (1<<(Ch+16)); /* +GSG 030307 */
	 SAR_RX_MASK_CLR(HalDev->dev_base) = (1<<Ch);

         /* Clear PDSP Channel State RAM */
         pTmp = (pPDSP_BLOCK_0(HalDev->dev_base)+(Ch*64));
         for (i=0; i<NUM_PDSP_AAL5_STATE_WORDS; i++)
           *pTmp++ = 0;

	 if ((HalDev->TxTeardownPending[Ch] & BLOCKING_TEARDOWN) == 0)
	   {
            dbgprint(2,"[cppi->os]TeardownComplete(OsDev:%08x, Ch:%d, Direction:%d)\n", 
                     (bit32u)HalDev->OsDev, Ch, DIRECTION_TX|DIRECTION_RX); /* ~GSG 030321 */

	    HalDev->OsFunc->TeardownComplete(HalDev->OsDev, Ch, DIRECTION_TX|DIRECTION_RX);
	   }

	 /* clear all teardown pending information for this channel */
	 HalDev->RxTeardownPending[Ch] = 0;
	 HalDev->TxTeardownPending[Ch] = 0;
	}
#endif

#ifdef _CPHAL_CPMAC
      HalDev->ChIsOpen[Ch][DIRECTION_TX] = 0;
      CPMAC_TX_INTMASK_CLEAR(HalDev->dev_base) = (1<<Ch);
      if ((HalDev->TxTeardownPending[Ch] & BLOCKING_TEARDOWN) == 0)
	{
         dbgprint(2,"[cppi->os]TeardownComplete(OsDev:%08x, Ch:%d, Direction:%d)\n", 
		      (bit32u)HalDev->OsDev, Ch, DIRECTION_TX);

	 HalDev->OsFunc->TeardownComplete(HalDev->OsDev, Ch, DIRECTION_TX);
	}
      HalDev->TxTeardownPending[Ch] = 0;
#endif

      return (EC_NO_ERRORS);
     }
   return (-1);
  }

/* +GSG 030421 */
static void cpswHalEswitchAddToRxQueue(HAL_DEVICE *HalDev, HAL_RCB *FirstRcb, HAL_RCB *LastRcb, int FragCount, int Ch)
  {
  cppi_rxcntl_s *pRxCtl=&HalDev->RxCtlBlk[Ch]; /* +GSG 030321 */

  if (pRxCtl->RxActQueueHead==0)
    {
    dbgprint(1<<4,"[cppi]cpswHalEswitchRxReturn: Adding Rcb %08x to head, %08x is tail,
             RxActive[%d]=%d\n", (bit32u)FirstRcb, (bit32u)LastRcb,
             Ch, pRxCtl->RxActive);
        
    pRxCtl->RxActQueueHead=FirstRcb;
    pRxCtl->RxActQueueTail=LastRcb;
    if (!pRxCtl->RxActive)
      {
      /* write Rx Queue Head Descriptor Pointer */
      RX_DMA_STATE_HDP(HalDev->dev_base,Ch) = VirtToPhys(FirstRcb) - HalDev->offset;
      pRxCtl->RxActive=TRUE;
      }
    }
   else
    {
    register HAL_RCB *OldTailRcb;
    register bit32u rmode;

    dbgprint(1<<4,"[cppi]cpswHalEswitchRxReturn: Adding Rcb %08x to head, %08x is tail,
             RxActive[%d]=%d\n", (bit32u)FirstRcb, (bit32u)LastRcb,
             Ch, pRxCtl->RxActive);
        
    OldTailRcb=pRxCtl->RxActQueueTail;
    OldTailRcb->Next=(void *)FirstRcb;

    /* The previous tail need to be updated from noncached memory, The Emerald would write
       the whole cache line which could coincide with a HW write, one would be lost */
 
    OldTailRcb=VirtToVirtNoCache(OldTailRcb);
    OldTailRcb->HNext=VirtToPhys(FirstRcb) - HalDev->offset;
    pRxCtl->RxActQueueTail=LastRcb;
    HalDev->OsFunc->CriticalOn();
    rmode=OldTailRcb->mode;
    if (rmode&CB_EOQ_BIT)
      {
      (RX_DMA_STATE_HDP( HalDev->dev_base , Ch ))  = VirtToPhys(FirstRcb) - HalDev->offset;
      rmode&=~CB_EOQ_BIT;
      OldTailRcb->mode=rmode;
      }   
    HalDev->OsFunc->CriticalOff();
    }
  pRxCtl->RxActQueueCount+=FragCount;

#ifdef _CPHAL_CPUSB20
  USB20_RXCH_FREEBUFCNT(HalDev->dev_base,Ch)=FragCount;
#endif
#ifdef _CPHAL_HDMA
  HDMA_FREE_BUFFER_COUNT(HalDev->dev_base,Ch)=FragCount;
#endif
  }

/**
 *  @ingroup CPHAL_Functions
 *  This function is called to indicate to the CPHAL that the upper layer
 *  software has finished processing the receive data (given to it by
 *  osReceive()).  The CPHAL will then return the appropriate receive buffers
 *  and buffer descriptors to the available pool.
 *
 *  @param   HalReceiveInfo   Start of receive buffer descriptor chain returned to
 *                      CPHAL.
 *  @param   StripFlag  Flag indicating whether the upper layer software has
 *                      retained ownership of the receive data buffers.
 *<BR>
 * 'FALSE' means  that the CPHAL can reuse the receive data buffers.
 *<BR>
 * 'TRUE' : indicates the data buffers were retained by the OS
 *<BR>
 * NOTE:  If StripFlag is TRUE, it is the responsibility of the upper layer software to free the buffers when they are no longer needed.
 *
 *  @return EC_NO_ERRORS (ok). <BR>
 *          Possible Error Codes:<BR>
 *          @ref EC_VAL_INVALID_STATE "EC_VAL_INVALID_STATE"<BR>
 *          @ref EC_VAL_RCB_NEEDS_BUFFER "EC_VAL_RCB_NEEDS_BUFFER"<BR>
 *          @ref EC_VAL_RCB_DROPPED "EC_VAL_RCB_DROPPED"<BR>
 */
static int cpswHalEswitchRxReturn(HAL_RECEIVEINFO *HalReceiveInfo, int StripFlag)
  { 
  int Ch=HalReceiveInfo->Ch;
  HAL_RCB *LastRcb; 
  HAL_DEVICE *HalDev = HalReceiveInfo->HalDev;
  int RcbSize;
  int FragCount = HalReceiveInfo->FragCount;

  if (HalDev->State != enOpened)
    return(MODULE_ERROR|EC_FUNC_RXRETURN|EC_VAL_INVALID_STATE);
 
  dbgprint(1,"[cppi]cpswHalEswitchRxReturn(HalReceiveInfo:%08x, StripFlag:%d)\n",
           (bit32u)HalReceiveInfo, StripFlag); /* ~GSG 030321 */

  LastRcb=(HAL_RCB *)HalReceiveInfo->Eop;
  LastRcb->HNext=0;
  LastRcb->Next=0;

  RcbSize = HalDev->ChData[Ch].RxBufSize;
  if (FragCount>1)
    {
    LastRcb->Off_BLen=RcbSize;
    LastRcb->mode=CB_OWNERSHIP_BIT;
    }

  HalReceiveInfo->Off_BLen=RcbSize;
  HalReceiveInfo->mode=CB_OWNERSHIP_BIT;
  
  /* If OS has kept the buffers for this packet, attempt to alloc new buffers */
  if (StripFlag)
    {
    HAL_RCB *TempRcb;
    HAL_RCB *CurrHeadRcb = HalReceiveInfo, *LastGoodRcb=0;         /* +GSG 030421 */
    char *pBuf;
    int rc=0,i;                                                      /*MJH+030417*/
    int GoodCount=0;                                               /*GSG+030421*/

    TempRcb = HalReceiveInfo;
    for (i=0; i<FragCount; i++)
      {
      if (TempRcb == 0)
        {
        dbgPrintf("Rx Return error while allocating new buffers\n");
        dbgPrintf("Rcb = %08x, Rcb->Eop = %08x, FragCount = %d:%d\n",
                  (bit32u)HalReceiveInfo, (bit32u)HalReceiveInfo->Eop, FragCount,i);
        osfuncSioFlush();

        return(EC_CPPI|EC_FUNC_RXRETURN|EC_VAL_CORRUPT_RCB_CHAIN);
        }

      dbgprint(0xa,"[cppi->os]MallocRxBuffer(Size:%d, MemBase:%08x, MemRange:%08x, OsSetup:%08x, HalReceiveInfo:%08x, OsReceiveInfo:%08x, OsDev:%08x\n",
               RcbSize,0,0xf,(bit32u)HalDev->ChData[Ch].OsSetup, (bit32u)TempRcb,
              (bit32u)&TempRcb->OsInfo, (bit32u)HalDev->OsDev);

      /* size = ((RcbSize+15) & ~15) + 15;*/                      /*-3.01b*/
      /*size = RcbSize + 15;*/                                               /* -GSG 030421 */
      pBuf= (char *) HalDev->OsFunc->MallocRxBuffer(RcbSize,0,
                                                    0xF,HalDev->RxCtlBlk[Ch].OsSetup,
                                                    (void *)TempRcb,
                                                    (void *)&TempRcb->OsInfo,
                                                    (void *) HalDev->OsDev);
      if (!pBuf)
        {
        /* malloc failed, add this RCB to Needs Buffer List */
        TempRcb->FragCount = 1;                                             /*MJH+030417*/
        (HAL_RCB *)TempRcb->Eop = TempRcb;                                  /* GSG +030430 */

        if(HalDev->NeedsCount < MAX_NEEDS)                                  /* +MJH 030410 */
          {                                                                 /* +MJH 030410 */
          HalDev->Needs[HalDev->NeedsCount] = (HAL_RECEIVEINFO *) TempRcb; /* +MJH 030410 */
          HalDev->NeedsCount++;                                            /* +MJH 030410 */
          rc = (EC_CPPI|EC_FUNC_RXRETURN|EC_VAL_RCB_NEEDS_BUFFER);         /* ~MJH 030417 */
          }                                                                 /* +MJH 030410 */
         else                                                               /* +MJH 030410 */
          rc = (EC_CPPI|EC_FUNC_RXRETURN|EC_VAL_RCB_DROPPED);              /* ~MJH 030417 */

        /* requeue any previous RCB's that were ready to go before this one */
        if (GoodCount > 0)                                                  /* +GSG 030421 */
          {                                                                 /* +GSG 030421 */
          LastGoodRcb->HNext=0;                                            /* +GSG 030430 */
          LastGoodRcb->Next=0;                                             /* +GSG 030430 */
          osfuncDataCacheHitWritebackAndInvalidate((void *)LastGoodRcb, 16);            /* +GSG 030430 */

          cpswHalEswitchAddToRxQueue(HalDev, CurrHeadRcb, LastGoodRcb, GoodCount, Ch);   /* +GSG 030421 */
          GoodCount = 0;                                                   /* +GSG 030421 */ 
          }                                                                 /* +GSG 030421 */ 

        CurrHeadRcb = TempRcb->Next;                                        /* +GSG 030421 */
        }
       else                                                                  /* +GSG 030421 */
        {                                                                    /* +GSG 030421 */
        /* malloc succeeded, requeue the RCB to the hardware */
        TempRcb->BufPtr=VirtToPhys(pBuf) - HalDev->offset;
        TempRcb->DatPtr=pBuf;
        /* Emerald fix 10/29 */
        osfuncDataCacheHitWritebackAndInvalidate((void *)TempRcb, 16);
 
        /* i store the last good RCB in case the malloc fails for the
           next fragment.  This ensures that I can go ahead and return
           a partial chain of RCB's to the hardware */
        LastGoodRcb = TempRcb;                                              /* +GSG 030421 */
        GoodCount++;                                                        /* +GSG 030421 */
        }                                                                    /* +GSG 030421 */
      TempRcb = TempRcb->Next;
      } /*  end of Frag loop */
    /* if there any good RCB's to requeue, do so here */
    if (GoodCount > 0)                                                        /* +GSG 030421 */
      {
      cpswHalEswitchAddToRxQueue(HalDev, CurrHeadRcb, LastGoodRcb, GoodCount, Ch);          /* +GSG 030421 */
      }
    return(rc);                                                               /* ~GSG 030421 */
    }
   else
    {
    /*  Not Stripping */
    /* Emerald */
    /* Write Back SOP and last RCB */
    osfuncDataCacheHitWritebackAndInvalidate((void *)HalReceiveInfo, 16);

    if (FragCount > 1)
      {
      osfuncDataCacheHitWritebackAndInvalidate((void *)LastRcb, 16);
      }
    /* if not stripping buffers, always add to queue */
    cpswHalEswitchAddToRxQueue(HalDev, HalReceiveInfo, LastRcb, FragCount, Ch); /*MJH~030520*/
    }
  return(EC_NO_ERRORS);
  }

/* +MJH 030410
   Trys to liberate an RCB until liberation fails.
   Note: If liberation fails then cpswHalEswitchRxReturn will re-add the RCB to the
         Needs list.
*/

static void cpswHalEswitchNeedsCheck(HAL_DEVICE *HalDev)
{

    HalDev->OsFunc->CriticalOn();
    while(HalDev->NeedsCount)
      {
      int rc;
      HAL_RECEIVEINFO* HalRcb;

      HalDev->NeedsCount--;
      HalRcb = HalDev->Needs[HalDev->NeedsCount];
      rc =  cpswHalEswitchRxReturn(HalRcb, 1);
      /* short circuit if cpswHalEswitchRxReturn starts to fail */
      if (rc != 0)
        break;
      }
    HalDev->OsFunc->CriticalOff();
}

/*
 *  This function allocates transmit buffer descriptors (internal CPHAL function).
 *  It creates a high priority transmit queue by default for a single Tx
 *  channel.  If QoS is enabled for the given CPHAL device, this function
 *  will also allocate a low priority transmit queue.
 *
 *  @param   HalDev   CPHAL module instance. (set by cphalInitModule())  
 *  @param   Ch       Channel number.
 *
 *  @return  0 OK, Non-Zero Not OK
 */
static int cpswHalEswitchInitTcb(HAL_DEVICE *HalDev, int Ch)
  {
   int i, Num = HalDev->ChData[Ch].TxNumBuffers;
   HAL_TCB *pTcb=0;
   char *AllTcb;
   int  tcbSize, Queue;
   int SizeMalloc;

   dbgprint(1,"[cppi]cpswHalEswitchInitTcb(HalDev:%08x, Ch:%d)\n", (bit32u)HalDev, Ch); /* ~GSG 030321 */
   dbgprint(1<<6,"  [cppi cpswHalEswitchInitTcb()]TxNumBuffers:%d\n", Num);             /* ~GSG 030321 */

   tcbSize = (sizeof(HAL_TCB)+0xf)&~0xf;
   SizeMalloc = (tcbSize*Num)+0xf;

   for (Queue=0; Queue < HalDev->ChData[Ch].TxNumQueues; Queue++)
     {
      cppi_txcntl_s *pTxCtl=&HalDev->TxCtlBlk[Ch][Queue]; /* +GSG 030321 */

      if (pTxCtl->TcbStart == 0) /* ~GSG 030321 */
        {
         dbgprint(1<<6,"  [cppi cpswHalEswitchInitTcb()]Allocate Tx buffer descriptors\n"); /* ~GSG 030321 */
         dbgprint(0xa,"[cppi->os]MallocDmaXfer(Size:%d, MemBase:%08x, MemRange:%08x)\n", SizeMalloc,0,0xffffffff); /* ~GSG 030321 */

         /* malloc all TCBs at once */
         AllTcb = (char *)HalDev->OsFunc->MallocDmaXfer(SizeMalloc,0,0xffffffff);
         if (!AllTcb) 
           {
            return(EC_CPPI|EC_FUNC_HAL_INIT|EC_VAL_TCB_MALLOC_FAILED); 
           }

         dbgprint(1<<6,"  [cppi cpswHalEswitchInitTcb()]Clear Tx buffer descriptors\n"); /* ~GSG 030321 */

         cpswHalCommonmemset(AllTcb, 0, SizeMalloc); 

         /* keep this address for freeing later */
         pTxCtl->TcbStart = AllTcb;                      /* ~GSG 030321 */
        }
       else
        {
         /* if the memory has already been allocated, simply reuse it! */
         AllTcb = pTxCtl->TcbStart;                      /* ~GSG 030321 */
        }

      /* align to cache line */
      AllTcb = (char *)(((bit32u)AllTcb + 0xf) &~ 0xf); /*PITS #143  MJH~030522*/

      /* default High priority transmit queue */
      pTxCtl->TcbPool=0;                                 /* ~GSG 030321 */
      for(i=0;i<Num;i++)
        {
         /*pTcb=(HAL_TCB *) OsFunc->MallocDmaXfer(sizeof(HAL_TCB),0,0xffffffff); */
         pTcb= (HAL_TCB *)(AllTcb + (i*tcbSize));
         pTcb->mode=0;
         pTcb->BufPtr=0;
         pTcb->Next=pTxCtl->TcbPool;                     /* ~GSG 030321 */
         pTcb->Off_BLen=0;
         pTxCtl->TcbPool=pTcb;                           /* ~GSG 030321 */
        }
     }

   return(EC_NO_ERRORS);
  }

/* 
 *  This function allocates receive buffer descriptors (internal CPHAL function).
 *  After allocation, the function 'queues' (gives to the hardware) the newly 
 *  created receive buffers to enable packet reception.
 *
 *  @param   HalDev   CPHAL module instance. (set by cphalInitModule())
 *  @param   Ch    Channel number.
 *
 *  @return  0 OK, Non-Zero Not OK
 */
static int cpswHalEswitchInitRcb(HAL_DEVICE *HalDev, int Ch)
  {
   int i, Num = HalDev->ChData[Ch].RxNumBuffers;
   int Size = HalDev->ChData[Ch].RxBufSize;
   HAL_RCB *pRcb;
   char *pBuf;
   char *AllRcb;
   int  rcbSize;
   int  DoMalloc = 0;
   int SizeMalloc;
   int MallocSize;
   cppi_rxcntl_s *pRxCtl=&HalDev->RxCtlBlk[Ch];  /* +GSG 030321 */
   
   dbgprint(1,"[cppi]cpswHalEswitchInitRcb(HalDev:%08x, Ch:%d)\n", (bit32u)HalDev, Ch); /* ~GSG 030321 */
   dbgprint(1<<6,"  [cppi cpswHalEswitchInitRcb()]RxNumBuffers:%d\n", Num);             /* ~GSG 030321 */
   dbgprint(1<<6,"  [cppi cpswHalEswitchInitRcb()]RxBufSize   :%d\n", Size);            /* ~GSG 030321 */

   rcbSize = (sizeof(HAL_RCB)+0xf)&~0xf;
   SizeMalloc = (rcbSize*Num)+0xf;

   if (pRxCtl->RcbStart == 0) /* ~GSG 030321 */
     {
      DoMalloc = 1;
  
      dbgprint(1<<6,"  [cppi Ch %d cpswHalEswitchInitRcb()]Allocate Rx buffer descriptors\n", Ch); /* ~GSG 030321 */
      dbgprint(0xa,"[cppi->os]MallocDmaXfer(Size:%d, MemBase:%08x, MemRange:%08x)\n",SizeMalloc,0,0xffffffff); /* ~GSG 030321 */

      /* malloc all RCBs at once */
      AllRcb= (char *)HalDev->OsFunc->MallocDmaXfer(SizeMalloc,0,0xffffffff);
      if (!AllRcb) 
        {
         return(EC_CPPI|EC_FUNC_HAL_INIT|EC_VAL_RCB_MALLOC_FAILED);
        }

      dbgprint(1<<6,"  [cppi Ch %d cpswHalEswitchInitRcb()]Clear Rx buffer descriptors\n", Ch); /* ~GSG 030321 */

      cpswHalCommonmemset(AllRcb, 0, SizeMalloc);

      /* keep this address for freeing later */
      pRxCtl->RcbStart = AllRcb;     /* ~GSG 030321 */
     }
    else
     {
      /* if the memory has already been allocated, simply reuse it! */
      AllRcb = pRxCtl->RcbStart;     /* ~GSG 030321 */
     }

   /* align to cache line */
   AllRcb = (char *)(((bit32u)AllRcb + 0xf)&~0xf);  /*PITS #143  MJH~030522*/

   pRxCtl->RcbPool=0;                /* ~GSG 030321 */
   for(i=0;i<Num;i++)
     {
      pRcb = (HAL_RCB *)(AllRcb + (i*rcbSize));

      if (DoMalloc == 1)
        {
         dbgprint(0xa,"[cppi->os]MallocRxBuffer(Size:%d, MemBase:%08x, MemRange:%08x, OsSetup:%08x, HalReceiveInfo:%08x, OsReceiveInfo:%08x, OsDev:%08x)\n", 
		      Size,0,0xf,(bit32u)HalDev->ChData[Ch].OsSetup, (bit32u)pRcb,(bit32u)&pRcb->OsInfo, (bit32u)HalDev->OsDev);

         MallocSize = Size;                                       /*~3.01 */
         pBuf= (char *) HalDev->OsFunc->MallocRxBuffer(MallocSize,0,0xF,HalDev->RxCtlBlk[Ch].OsSetup, (void *)pRcb, (void *)&pRcb->OsInfo, (void *) HalDev->OsDev);
         if (!pBuf) 
	   {
            return(EC_CPPI|EC_FUNC_HAL_INIT|EC_VAL_RX_BUFFER_MALLOC_FAILED);
	   }
	 /* -RC3.01 pBuf = (char *)(((bit32u)pBuf+0xF) & ~0xF); */
         pRcb->BufPtr=VirtToPhys(pBuf) - HalDev->offset;
         pRcb->DatPtr=pBuf;
         /*pRcb->BufSize=Size;*/
        }
      pRcb->mode=0;
      pRcb->Ch=Ch;
      pRcb->Next=(void *)pRxCtl->RcbPool; /* ~GSG 030321 */
      pRcb->Off_BLen=0;
      pRcb->HalDev = HalDev;
      pRxCtl->RcbPool=pRcb;               /* ~GSG 030321 */
     }

   /* Give all of the Rx buffers to hardware */

   dbgprint(1<<16,"  [cppi Ch %d cpswHalEswitchInitRcb()]First free buffer: %08x\n", Ch, (bit32u)pRxCtl->RcbPool); /* ~GSG 030321 */
   
   while(pRxCtl->RcbPool)                 /* ~GSG 030321 */
     {
      pRcb=pRxCtl->RcbPool;               /* ~GSG 030321 */
      pRxCtl->RcbPool=pRcb->Next;         /* ~GSG 030321 */
      pRcb->Eop=(void*)pRcb;
      pRcb->FragCount=1;
      cpswHalEswitchRxReturn((HAL_RECEIVEINFO *)pRcb, 0); /* ~GSG 030321 */
     }

   return(EC_NO_ERRORS);
  }

/** 
 *  @ingroup CPHAL_Functions
 *  This function transmits the data in FragList using available transmit
 *  buffer descriptors.  More information on the use of the Mode parameter
 *  is available in the module-specific appendices.  Note:  The OS should
 *  not call Send() for a channel that has been requested to be torndown.
 *
 *  @param   HalDev      CPHAL module instance. (set by cphalInitModule())  
 *  @param   FragList    Fragment List structure.
 *  @param   FragCount   Number of fragments in FragList.
 *  @param   PacketSize  Number of bytes to transmit.
 *  @param   OsSendInfo  OS Send Information structure. <BR>
 *  @param   SendComplete Function pointer for per-packet SendComplete() function. <BR>
 *  @param   Mode        32-bit value with the following bit fields: <BR>
 *                       31-16: Mode  (used for module specific data). <BR>
 *                       15-08: Queue (transmit queue to send on). <BR>
 *                       07-00: Channel (channel number to send on).
 *
 *  @return  EC_NO_ERRORS (ok). <BR>
 *           Possible Error Codes:<BR>
 *           @ref EC_VAL_INVALID_STATE "EC_VAL_INVALID_STATE"<BR>
 *           @ref EC_VAL_NOT_LINKED "EC_VAL_NOT_LINKED"<BR>
 *           @ref EC_VAL_INVALID_CH "EC_VAL_INVALID_CH"<BR>
 *           @ref EC_VAL_OUT_OF_TCBS "EC_VAL_OUT_OF_TCBS"<BR>
 *           @ref EC_VAL_NO_TCBS "EC_VAL_NO_TCBS"<BR>
 */
static int cpswHalEswitchSend(HAL_DEVICE *HalDev,FRAGLIST *FragList,
                      int FragCount,int PacketSize, OS_SENDINFO *OsSendInfo,
                      int   (*SendComplete)(OS_SENDINFO *), bit32u Mode)
  {
  HAL_TCB *tcb_ptr, *head;
  int i;
  int rc = EC_NO_ERRORS;
  int Ch = Mode & 0xFF;

#ifdef _CPHAL_CPUSB20
  int Queue =0;   /* USB does not have Tx Q's so that field is used to piggy-back zero byte enable */
#else
  int Queue = (Mode>>8)&0xFF;
#endif
  cppi_txcntl_s *pTxCtl=&HalDev->TxCtlBlk[Ch][Queue]; /* +GSG 030321 */

#ifdef _CPHAL_AAL5
  int  WaitFlag = (Mode>>30)&1; /* This is for AAL5 testing only */                     /* ~GSG 030508 */
   int PktType = (Mode>>16)&3; /* 0=AAL5, 1=Null AAL, 2=OAM, 3=Transparent */           /* +GSG 030508 */
   int AtmHeaderInData = (Mode>>31)&1;                                                 /* +GSG 030508 */
#endif

  dbgprint(1,"[cppi]cpswHalEswitchSend(HalDev:%08x, FragList:%08x, FragCount:%d, PacketSize:%d, OsSendInfo:%08x, Mode:%08x)\n",
           (bit32u)HalDev, (bit32u)FragList, FragCount, PacketSize, (bit32u)OsSendInfo, Mode);

  if (HalDev->State != enOpened)
     return(EC_CPPI|EC_FUNC_SEND|EC_VAL_INVALID_STATE);

#ifdef _CPHAL_CPMAC
  if (!HalDev->Linked)
    {
     rc = EC_CPPI|EC_FUNC_SEND|EC_VAL_NOT_LINKED;
     return(rc);
    }
#endif

   if (HalDev->ChIsOpen[Ch][DIRECTION_TX] == 0)                    /*MJH~030611*/ /*PITS 148*/
    return(MODULE_ERROR|EC_FUNC_SEND|EC_VAL_INVALID_CH);              /*+GSG 030303*/

  dbgprint(0x6,"[cppi->os]CriticalOn()\n");                           /* ~GSG 030321 */

  HalDev->OsFunc->CriticalOn();

#ifdef _CPHAL_CPMAC  
  /* Setup Tx mode and size */
  if (PacketSize<60) 
    {                  
      FragList[FragCount-1].len += (60 - PacketSize);              /*MJH~030506*//*PITS 132*/
      PacketSize = 60;                                             /*MJH~030506*/
    }
  Mode &= CB_PASSCRC_BIT;
#endif
#ifdef _CPHAL_HDMA
  /* Setup Tx mode and size */
  if (PacketSize<60)
    {
      FragList[FragCount-1].len += (60 - PacketSize);              /*MJH~030506*//*PITS 132*/
      PacketSize = 60;                                             /*MJH~030506*/
    }
  Mode &= ~0x0ffff;
#endif
#ifdef _CPHAL_CPUSB20
  FragList[0].len |= Mode & ~0x0ffff;
  Mode = ((Mode>>8)&0x1)<<23;                /* Put the Zero byte enable in bit23 */
#endif
#ifdef _CPHAL_AAL5
  Mode = 0;
#endif

  tcb_ptr = head = pTxCtl->TcbPool;                                   /* ~GSG 030321 */

  if (tcb_ptr)
    {
#ifdef _CPHAL_AAL5
    int FragIndex;
#endif

    Mode|=PacketSize|CB_SOF_BIT|CB_OWNERSHIP_BIT;

#ifdef _CPHAL_AAL5
      /* these two TCB words are only valid on SOP */
      if (AtmHeaderInData == 1)
        {
         tcb_ptr->AtmHeader = 0;  /* bug fix for transparent mode PTI problem */
         /* Expect AtmHeader in the data */
         tcb_ptr->AtmHeader |= *((bit8u *)FragList[0].data++) << 24;
         tcb_ptr->AtmHeader |= *((bit8u *)FragList[0].data++) << 16;
         tcb_ptr->AtmHeader |= *((bit8u *)FragList[0].data++) << 8;
         tcb_ptr->AtmHeader |= *((bit8u *)FragList[0].data++);

         /* decrement data buffer length accordingly */
         FragList[0].len -= ATM_HEADER_SIZE;

         /* if the first fragment was ATM Header only, go to next fragment for loop */
         FragIndex=(FragList[0].len == 0)?1:0;

         /* No CPCS_UU/CPI if not AAL5 */
         tcb_ptr->Word5 = ((PktType & 0x3)<<16);
        }
       else
        {
         FragIndex=0;
         /* calculate AtmHeader from fields */
         tcb_ptr->AtmHeader = atmheader(HalDev->ChData[Ch].Gfc, /* ~GSG 030306 */
           HalDev->ChData[Ch].Vpi, HalDev->ChData[Ch].Vci,
           HalDev->ChData[Ch].Pti, HalDev->ChData[Ch].Clp);

         tcb_ptr->Word5 = HalDev->ChData[Ch].CpcsUU | ((HalDev->ChData[Ch].PktType &0x3)<<16);
        }

      for (i=FragIndex; i<FragCount; i++)
#else
      for (i=0; i<FragCount; i++)
#endif
       {      
        /* Setup Tx mode and size */
        tcb_ptr->Off_BLen = FragList[i].len;
        tcb_ptr->mode     = Mode;
        tcb_ptr->BufPtr   = VirtToPhys((bit32 *)FragList[i].data) - HalDev->offset;
        tcb_ptr->OsInfo = OsSendInfo;
        tcb_ptr->SendComplete = SendComplete;         /* HAL 2.0 */

        if (i == (FragCount - 1))
          {
           /* last fragment */
           tcb_ptr->mode |= CB_EOF_BIT; 

           /* since this is the last fragment, set the TcbPool pointer before
              nulling out the Next pointers */

           pTxCtl->TcbPool = tcb_ptr->Next; /* ~GSG 030321 */

           tcb_ptr->Next = 0;
           tcb_ptr->HNext = 0;

           /* In the Tx Interrupt handler, we will need to know which TCB is EOP,
              so we can save that information in the SOP */
           head->Eop = tcb_ptr;

           /* Emerald fix 10/29 */
	   osfuncDataCacheHitWritebackAndInvalidate((void *)tcb_ptr, 16);

#ifdef _CPHAL_AAL5
	   osfuncDataCacheHitWritebackAndInvalidate((void *)((bit32u)tcb_ptr + 16), 16);
#endif
          }
         else
          {
          tcb_ptr->HNext    = VirtToPhys((bit32 *)tcb_ptr->Next) - HalDev->offset;
          Mode=CB_OWNERSHIP_BIT;

	  /* Emerald fix 10/29 */
          osfuncDataCacheHitWritebackAndInvalidate((void *)tcb_ptr, 16);
#ifdef _CPHAL_AAL5
	  osfuncDataCacheHitWritebackAndInvalidate((void *)((bit32u)tcb_ptr + 16), 16);
#endif
          tcb_ptr = tcb_ptr->Next;  /* what about the end of TCB list?? */

          if (tcb_ptr == 0)
            {
	    rc = EC_CPPI|EC_FUNC_SEND|EC_VAL_OUT_OF_TCBS;
            goto ExitSend;
            }
	  }
       } /* for */

     /* put it on the high priority queue */
     if (pTxCtl->TxActQueueHead == 0)                       /*~GSG 030321*/
       {
        pTxCtl->TxActQueueHead=head;                        /*~GSG 030321*/
        pTxCtl->TxActQueueTail=tcb_ptr;                     /*~GSG 030321*/
        if (!pTxCtl->TxActive)                              /*~GSG 030321*/
          {
#ifdef _CPHAL_AAL5
           if (!WaitFlag)
	     {
#endif
	      /* write CPPI TX HDP */
              TX_DMA_STATE_HDP(HalDev->dev_base,Ch,Queue) = VirtToPhys(head)  - HalDev->offset;
              pTxCtl->TxActive=TRUE;                        /*~GSG 030321*/

#ifdef _CPHAL_AAL5
	     }
#endif           
          }
       }
      else
       {
        register volatile HAL_TCB *pTailTcb;   
        register bit32u tmode;
        register bit32u pCurrentTcb;
           
        pTailTcb=pTxCtl->TxActQueueTail;
        pTailTcb->Next=head;
      
        /* The previous tail need to be updated from noncached memory, The Emerald would write
           the whole cache line which could coincide with a HW write, one would be lost */
      
        pTailTcb=VirtToVirtNoCache(pTailTcb);

        pCurrentTcb=VirtToPhys(head)  - HalDev->offset;
        pTailTcb->HNext=pCurrentTcb;
        pTxCtl->TxActQueueTail=tcb_ptr;
        tmode=pTailTcb->mode;
        if (tmode&CB_EOQ_BIT)
          {
          (TX_DMA_STATE_HDP(HalDev->dev_base,Ch,Queue))  = pCurrentTcb;
          tmode&=~CB_EOQ_BIT;
          pTailTcb->mode=tmode;
          }
       }
#ifdef __CPHAL_DEBUG                                        /*+GSG 030303*/
        pTxCtl->TxActQueueCount+=FragCount;                 /*+GSG 030303*/
#endif                                                      /*+GSG 030303*/

     rc = EC_NO_ERRORS;
     goto ExitSend;
    } /* if (tcb_ptr) */
   else
    {
     rc = EC_CPPI|EC_FUNC_SEND|EC_VAL_NO_TCBS;
     goto ExitSend;
    }
ExitSend:
   dbgprint(0x6,"[cppi->os]CriticalOff()\n");                /* ~GSG 030321 */

   HalDev->OsFunc->CriticalOff();
   return(rc);     
  }

/*
 *  This function processes receive interrupts.  It traverses the receive
 *  buffer queue, extracting the data and passing it to the upper layer software via 
 *  osReceive().  It handles all error conditions and fragments without valid data by
 *  immediately returning the RCB's to the RCB pool.
 *
 *  @param   HalDev   CPHAL module instance. (set by cphalInitModule())  
 *  @param   Ch       Channel Number.
 *  @param   MoreWork Flag that indicates that there is more work to do when set to 1.
 *
 *  @return  0 if OK, non-zero otherwise.
 */
static int cpswHalEswitchRxInt(HAL_DEVICE *HalDev, int Ch, int *MoreWork)
  {
  HAL_RCB *CurrentRcb, *LastRcb, *SopRcb, *EofRcb, *EopRcb;
  bit32u RxBufStatus,PacketsServiced, RxPktLen, RxSopStatus, FrmFrags, TotalFrags, FrmLen;
  cppi_rxcntl_s *pRxCtl=&HalDev->RxCtlBlk[Ch];  /*+GSG 030321*/
  int FragIndex; /* +GSG 030508 */
  int EarlyReturn = 0; /* +GSG 030521 */

#ifdef _CPHAL_AAL5  
  bit32u PktType, ExpDmaSize, Cells, CurrDmaLen;
  int PassHeader=0;
#endif
  int mode;
#ifdef __CPHAL_CPPI_OFFSET
  bit32u SopOffset;
#endif  

  dbgprint(0x1,"[cppi]cpswHalEswitchRxInt(HalDev:%08x, Ch:%d, MoreWork:%08x)\n", (bit32u)HalDev, Ch, (bit32u)MoreWork); /*~GSG 030321*/

  if(HalDev->NeedsCount) /* +MJH 030410 */
    cpswHalEswitchNeedsCheck(HalDev);  /* +MJH 030410 */

  /* Handle case of teardown interrupt */
  if (HalDev->RxTeardownPending[Ch] != 0)
    {
    int Ret;

     Ret = cpswHalEswitchRxTeardownInt(HalDev, Ch);
     if (Ret == 0)
       {                                                          /*+GSG 030303*/
        *MoreWork = 0; /* bug fix 1/6 */                          /*+GSG 030303*/
        return (EC_NO_ERRORS);
       }                                                          /*+GSG 030303*/
    }

  CurrentRcb=pRxCtl->RxActQueueHead;                              /*~GSG 030321 */
#ifdef _CPPI_TEST
  if (CurrentRcb==0) 
    {
     return(EC_CPPI|EC_FUNC_RXINT|EC_VAL_NULL_RCB);
    }
#endif

  osfuncDataCacheHitInvalidate((void*)CurrentRcb, 16);

  RxBufStatus=CurrentRcb->mode;
#ifdef _CPHAL_AAL5
  /* I think I need to do this to ensure that i read UuCpi properly,
     which is on the second cache line of the Rcb */
  osfuncDataCacheHitInvalidate((void*)((bit32u)CurrentRcb+16), 16);
#endif
  PacketsServiced=0;
  
  while((CurrentRcb)&&((RxBufStatus&CB_OWNERSHIP_BIT)==0)&&
	(PacketsServiced<pRxCtl->RxServiceMax)) /*~GSG 030307*/
    {
     dbgprint(1<<16,"  [cppi Ch %d cpswHalEswitchRxInt()]Processing SOP descriptor = %08x\n", 
		    Ch, (bit32u)CurrentRcb);    /*~GSG 030321*/

    PacketsServiced++;                          /*~GSG 030307*/
    SopRcb=CurrentRcb;
    RxSopStatus=RxBufStatus;
#ifdef _CPPI_TEST
    if((RxBufStatus&CB_SOF_BIT)==0)
      {
       return(EC_CPPI|EC_FUNC_RXINT|EC_VAL_SOP_ERROR);
      }
#endif
    RxPktLen = RxSopStatus&CB_SIZE_MASK;
    /* Not sure what MAC needs to do for next block */
#ifdef _CPHAL_AAL5
    PktType=((SopRcb->UuCpi & 0x00030000) >> 16);                             /* GSG ~030508 */
    /* Calculate the expected DMA length */
    if (RxPktLen != 0)
      {
       Cells=RxPktLen/48;
       if ((RxPktLen%48) > 40)
         Cells++;
       if (PktType == PKT_TYPE_AAL5)                                          /* ~GSG 030508 */
         Cells++;
       ExpDmaSize=Cells*48;
      }
     else
      {
       ExpDmaSize=0;
      }
#endif

#ifdef __CPHAL_CPPI_OFFSET
    SopOffset=(SopRcb->Off_BLen&CB_OFFSET_MASK)>>16;
#endif
    FrmFrags=0;
    TotalFrags=0;
    FragIndex=0;
    FrmLen=0;
    EofRcb=0;

#ifdef _CPHAL_AAL5                                                              /* +GSG 030508 */
    CurrDmaLen=0;
      if ((PktType == PKT_TYPE_OAM) || (PktType == PKT_TYPE_TRANS))              /* +GSG 030508 */
        {                                                                        /* +GSG 030508 */
         /* first frag is ATM Header */                                          /* +GSG 030508 */
         PassHeader = 1;                                                         /* +GSG 030508 */
         HalDev->fraglist[0].data = (void *)&SopRcb->AtmHeader;          /* +GSG 030508 */
         HalDev->fraglist[0].len = 4;                                    /* +GSG 030508 */
         HalDev->fraglist[0].OsInfo = SopRcb->OsInfo;                    /* +GSG 030508 */
         FragIndex++;                                                            /* +GSG 030508 */
        }                                                                        /* +GSG 030508 */
#endif                                                                          /* +GSG 030508 */

    do
      {
      bit32u DmaLen;

#ifdef __CPHAL_CPPI_OFFSET
      DmaLen=CurrentRcb->Off_BLen&CB_SIZE_MASK;
#else
      DmaLen=CurrentRcb->Off_BLen;
#endif
#ifdef _CPHAL_AAL5                                                              /* +GSG 030508 */
      CurrDmaLen+=DmaLen;
#endif                                                                          /* +GSG 030508 */
      FrmLen+=DmaLen;
      TotalFrags++;
      if (!EofRcb)
        {
        HalDev->fraglist[FragIndex].data=((char *)CurrentRcb->DatPtr);       /* ~GSG 030508 */
#ifdef __CPHAL_CPPI_OFFSET
        HalDev->fraglist[FragIndex].data+=((FrmFrags==0)?SopOffset:0);       /* ~GSG 030508 */
#endif
        HalDev->fraglist[FragIndex].len=DmaLen;                              /* ~GSG 030508 */

        /* GSG 12/9 */
        HalDev->fraglist[FragIndex].OsInfo = CurrentRcb->OsInfo;             /* ~GSG 030508 */

	/* Upper layer must do the data invalidate */
       
	FrmFrags++;
        FragIndex++;                                                         /* ~GSG 030508 */
        if (FrmLen>=RxPktLen)
          EofRcb=CurrentRcb;
        }
      LastRcb=CurrentRcb;
      CurrentRcb=LastRcb->Next;
      if (CurrentRcb)
	osfuncDataCacheHitInvalidate((void*)CurrentRcb,16);
      }while(((LastRcb->mode&CB_EOF_BIT)==0)&&(CurrentRcb));

#ifdef _CPPI_TEST
    if ((LastRcb->mode&CB_EOF_BIT)==0)
      {
       return(EC_CPPI|EC_FUNC_RXINT|EC_VAL_EOP_ERROR);
      }
#endif
    /* New location for interrupt acknowledge */
    /* Write the completion pointer */
    RX_DMA_STATE_CP(HalDev->dev_base,Ch) = VirtToPhys(LastRcb) - HalDev->offset;

    EopRcb=LastRcb;
    pRxCtl->RxActQueueHead=CurrentRcb;      /*~GSG 030321*/
    pRxCtl->RxActQueueCount-=TotalFrags;    /*~GSG 030321*/

    if (LastRcb->mode&CB_EOQ_BIT)
      {
       if (CurrentRcb)
         {
#ifdef _CPHAL_AAL5
          HalDev->Stats.RxMisQCnt[Ch]++;
#endif
#ifdef _CPHAL_CPUSB20
           HalDev->Stats.RxMisQCnt[Ch]++;
#endif
          RX_DMA_STATE_HDP(HalDev->dev_base,Ch) = LastRcb->HNext;
         }
        else
	 {
#ifdef _CPHAL_AAL5
	  /* Rx EOQ */
	  HalDev->Stats.RxMisQCnt[Ch]++;
#endif
#ifdef _CPHAL_CPUSB20
           /* Rx EOQ */
           HalDev->Stats.RxMisQCnt[Ch]++;
#endif        
          pRxCtl->RxActive=FALSE;          /*~GSG 030321*/
	 }
      }

    EopRcb->Next=0;

     /* setup SopRcb for the packet */
     SopRcb->Eop=(void*)EopRcb;
     SopRcb->FragCount=TotalFrags;

#ifdef _CPHAL_AAL5    
    if ((ExpDmaSize!=CurrDmaLen)||(RxSopStatus&RX_ERROR_MASK))
      {
       /* check for Rx errors (only valid on SOP) */
       if (RxSopStatus & RX_ERROR_MASK)
         {
          if (RxSopStatus & CRC_ERROR_MASK)
            HalDev->Stats.CrcErrors[Ch]++;
       
          if (RxSopStatus & LENGTH_ERROR_MASK) 
	    HalDev->Stats.LenErrors[Ch]++;
         
          if (RxSopStatus & ABORT_ERROR_MASK)
	    HalDev->Stats.AbortErrors[Ch]++;

          if (RxSopStatus & STARV_ERROR_MASK)
	    HalDev->Stats.StarvErrors[Ch]++; 
         }
        else
	 {
	  HalDev->Stats.DmaLenErrors[Ch]++; /* different type of length error */
	 }

        dbgprint(1<<16,"  [cppi Ch %d RxInt()]Returning errored packet = %08x, mode = %08x\n",
             Ch, (bit32u)SopRcb, RxSopStatus);
        EarlyReturn = 1;
      }
#endif

     /* do not pass up the packet if we're out of RCB's (or have an errored packet)*/
     if ((CurrentRcb == 0) || (EarlyReturn == 1))
       {
        cpswHalEswitchRxReturn((HAL_RECEIVEINFO *)SopRcb,0);
       }
      else
       {
#ifdef _CPHAL_AAL5
        if (EopRcb!=EofRcb)
          {
           HAL_RCB *FirstEmptyRcb;
 
           FirstEmptyRcb = EofRcb->Next;
           FirstEmptyRcb->Eop = (void*)EopRcb;
           FirstEmptyRcb->FragCount = TotalFrags-FrmFrags;

           dbgprint(1<<16,"  [cppi Ch %d cpswHalEswitchRxInt()]Returning empty packet = %08x\n", 
                      Ch, (bit32u)FirstEmptyRcb); /*~GSG 030321*/

           cpswHalEswitchRxReturn((HAL_RECEIVEINFO *)FirstEmptyRcb,0);
           SopRcb->Eop=(void*)EofRcb;
           SopRcb->FragCount=FrmFrags;
           EofRcb->Next=0;  /* Optional */
          } 

        mode = Ch | (PktType << 16) | (PassHeader << 31);   /* ~GSG 030508 */
#endif
#ifdef _CPHAL_CPMAC
      mode = (SopRcb->mode & 0xFFFF0000) | Ch;
#endif
#ifdef _CPHAL_HDMA
      mode = SopRcb->mode;
#endif
#ifdef _CPHAL_CPUSB20
      mode = Ch&0xff;
#endif

      dbgprint(0x10002,"[cppi->os]Receive(OsDev:%08x, FragList:%08x, FragCount:%d, PacketSize:%d, HalReceiveInfo:%08x, Mode:%d\n",
		  (bit32u)HalDev->OsDev, (bit32u)HalDev->fraglist,FragIndex,RxPktLen,(bit32u)SopRcb,mode);

      HalDev->ChData[Ch].Receive(HalDev->OsDev,HalDev->fraglist,FragIndex,RxPktLen,
                                 (HAL_RECEIVEINFO *)SopRcb,mode);                     /* HAL 2.0 */

      }	

     if (CurrentRcb)                                                                  /*MJH+030522*/
       RxBufStatus=CurrentRcb->mode;
    } /* while */

  if ((CurrentRcb)&&((RxBufStatus&CB_OWNERSHIP_BIT)==0)) /*~GSG 030307*/
    { 
     *MoreWork = 1; 
    }
   else
    { 
     *MoreWork = 0;
    }

  if (PacketsServiced == 0)
    {
     dbgprint(1<<16,"  [cppi cpswHalEswitchRxInt()]No Rx packets serviced on int!\n"); /*~GSG 030321*/
     dbgprint(1<<16,"                RxActQueueHead[%d] = %08x\n", Ch, (bit32u)pRxCtl->RxActQueueHead);/*~GSG 030321*/
    }
  
#ifdef _CPHAL_AAL5
  /* update stats */
  HalDev->Stats.RxPacketsServiced[Ch] += PacketsServiced;
  HalDev->Stats.RxTotal += PacketsServiced;
  if (HalDev->Stats.RxMaxServiced < PacketsServiced)
    HalDev->Stats.RxMaxServiced = PacketsServiced;
#endif

#ifdef _CPHAL_CPUSB20
  /* update stats */
  HalDev->Stats.RxInt[Ch] += PacketsServiced;
  HalDev->Stats.RxServicedPerInt[Ch] += PacketsServiced;
  HalDev->Stats.RxTotal += PacketsServiced;
  if (HalDev->Stats.RxMaxServiced[Ch] < PacketsServiced)
    HalDev->Stats.RxMaxServiced[Ch] = PacketsServiced;
#endif

  return (EC_NO_ERRORS);
  }

/*
 *  This function processes transmit interrupts.  It traverses the
 *  transmit buffer queue, detecting sent data buffers and notifying the upper 
 *  layer software via osSendComplete().  (for SAR, i originally had this split
 *  into two functions, one for each queue, but joined them on 8/8/02)
 *
 *  @param   HalDev   CPHAL module instance. (set by cphalInitModule())  
 *  @param   Queue    Queue number to service (always 0 for MAC, Choose 1 for SAR to service low priority queue)
 *  @param   MoreWork Flag that indicates that there is more work to do when set to 1.
 *
 *  @return  0 if OK, non-zero otherwise.
 */
static int cpswHalEswitchTxInt(HAL_DEVICE *HalDev, int Ch, int Queue, int *MoreWork)
  {
  HAL_TCB *CurrentTcb,*LastTcbProcessed,*FirstTcbProcessed;
  int PacketsServiced;
  bit32u TxFrameStatus; 
  cppi_txcntl_s *pTxCtl=&HalDev->TxCtlBlk[Ch][Queue];                 /*+GSG 030321*/
  OS_FUNCTIONS *OsFunc = HalDev->OsFunc;

#ifdef _CPHAL_AAL5
   int OtherQueue = Queue^1;
#endif

#ifdef __CPHAL_DEBUG                                                  /*+GSG 030303*/
  HAL_TCB *DbgTcb;                                                    /*+GSG 030303*/
#endif                                                                /*+GSG 030303*/

  dbgprint(1,"[cppi]cpswHalEswitchTxInt(HalDev:%08x, Ch:%d, Queue:%d, MoreWork:%08x)\n", (bit32u)HalDev, Ch,
           Queue, (bit32u)MoreWork); /*~GSG 030321*/

  /* Handle case of teardown interrupt.  This must be checked at
     the top of the function rather than the bottom, because
     the normal data processing can wipe out the completion
     pointer which is used to determine teardown complete. */
  if (HalDev->TxTeardownPending[Ch] != 0)
    {
    int Ret;

     Ret = cpswHalEswitchTxTeardownInt(HalDev, Ch, Queue);
     if (Ret == 0)
       {                                                              /*+GSG 030303*/
        *MoreWork = 0; /* bug fix 1/6 */                              /*+GSG 030303*/
        return (EC_NO_ERRORS);
       }                                                              /*+GSG 030303*/
    }

  CurrentTcb = pTxCtl->TxActQueueHead;                                /*~GSG 030321*/
  FirstTcbProcessed=CurrentTcb;

  if (CurrentTcb==0) 
    {
      /* @todo If this happens on the SAR, should report an error via halStatus */

      TX_DMA_STATE_CP(HalDev->dev_base,Ch,Queue) = VirtToPhys(pTxCtl->LastTcbProcessed) - HalDev->offset;
      return(EC_NO_ERRORS);
    }

  osfuncDataCacheHitInvalidate((void *)CurrentTcb, 16);
  TxFrameStatus=CurrentTcb->mode;
  PacketsServiced=0;

  /* should the ownership bit check be inside of the loop?? could make it a 
     while-do loop and take this check away */
  if ((TxFrameStatus&CB_OWNERSHIP_BIT)==0)
    {
    OsFunc->CriticalOn();                                     /*+GSG 030307*/
    do 
      {
      /* Pop TCB(s) for packet from the stack */
      LastTcbProcessed=CurrentTcb->Eop;

      /* new location for acknowledge */
      /* Write the completion pointer */
      TX_DMA_STATE_CP(HalDev->dev_base,Ch,Queue) = VirtToPhys(LastTcbProcessed) - HalDev->offset;

      pTxCtl->TxActQueueHead = LastTcbProcessed->Next;

#ifdef __CPHAL_DEBUG                                                  /*+GSG 030303*/
      /* simply count the number of TCB's processed - TxActQueueCount 
         only valid for debug! */                                     /*+GSG 030303*/
      DbgTcb = CurrentTcb;                                            /*+GSG 030303*/
      while (DbgTcb != LastTcbProcessed)                              /*+GSG 030303*/
	{                                                             /*+GSG 030303*/
         pTxCtl->TxActQueueCount--;                                   /*+GSG 030303*/
         DbgTcb = DbgTcb->Next;                                       /*+GSG 030303*/
	}                                                             /*+GSG 030303*/
      pTxCtl->TxActQueueCount--; /* count the last one*/              /*+GSG 030303*/
#endif                                                                /*+GSG 030303*/

      if (LastTcbProcessed != CurrentTcb)
        osfuncDataCacheHitInvalidate((void *)LastTcbProcessed, 16);

      if (LastTcbProcessed->mode&CB_EOQ_BIT)
        {
        if (LastTcbProcessed->Next)
          {
	   /* Misqueued packet */
#ifdef _CPHAL_AAL5
           HalDev->Stats.TxMisQCnt[Ch][Queue]++; 
#endif
#ifdef _CPHAL_CPUSB20
               HalDev->Stats.TxMisQCnt[Ch]++;
#endif    
           TX_DMA_STATE_HDP(HalDev->dev_base,Ch,Queue) = LastTcbProcessed->HNext;
          }
         else
          {
	   /* Tx End of Queue */
#ifdef _CPHAL_AAL5
	   HalDev->Stats.TxEOQCnt[Ch][Queue]++;
#endif 
#ifdef _CPHAL_CPUSB20
               HalDev->Stats.TxEOQCnt[Ch]++;
#endif
	   pTxCtl->TxActive=FALSE;
          }
        }

      dbgprint(0x22,"[cppi->os]SendComplete(OsSendInfo:%08x)\n", (bit32u)CurrentTcb->OsInfo);

      CurrentTcb->SendComplete(CurrentTcb->OsInfo); /* HAL 2.0 */

      /* Push Tcb(s) back onto the stack */
      CurrentTcb = LastTcbProcessed->Next;
      LastTcbProcessed->Next=pTxCtl->TcbPool;
      pTxCtl->TcbPool=FirstTcbProcessed;
        
      PacketsServiced++;
  
      TxFrameStatus=CB_OWNERSHIP_BIT; 
      /* set the first(SOP) pointer for the next packet */
      FirstTcbProcessed = CurrentTcb;
      if (CurrentTcb)
        {
	 osfuncDataCacheHitInvalidate((void *)CurrentTcb, 16);
         TxFrameStatus=CurrentTcb->mode;
        }

      }while(((TxFrameStatus&CB_OWNERSHIP_BIT)==0)
             &&(PacketsServiced<pTxCtl->TxServiceMax));

      pTxCtl->LastTcbProcessed = LastTcbProcessed;

#ifdef _CPHAL_AAL5
      {
      cppi_txcntl_s *pTxCtl2=&HalDev->TxCtlBlk[Ch][OtherQueue];

      /* this fixes the SAR TurboDSL hardware bug (multiple queue failure) */
      if (pTxCtl2->TxActive)
        if (pTxCtl2->TxActQueueHead)
          if (TX_DMA_STATE_HDP(HalDev->dev_base,Ch,OtherQueue) == 0)
            {
             osfuncDataCacheHitInvalidate(pTxCtl2->TxActQueueHead,16);
             if ((pTxCtl2->TxActQueueHead->mode) & CB_OWNERSHIP_BIT)
               { 
                HalDev->TurboDslErrors++;
                TX_DMA_STATE_HDP(HalDev->dev_base,Ch,OtherQueue) = 
                  VirtToPhys(pTxCtl2->TxActQueueHead) - HalDev->offset;
               }
            }
      }
#endif

    OsFunc->CriticalOff();  /*+GSG 030307*/

    if ((TxFrameStatus&CB_OWNERSHIP_BIT)==0)
      {
       *MoreWork = 1;
      }
     else
      {
       *MoreWork = 0;
      }
    }

#ifdef __CPHAL_DEBUG
  if (PacketsServiced == 0)
    {
     dbgprint(1<<16,"  [cppi Ch %d cpswHalEswitchTxInt()]No Tx packets serviced on int!\n", Ch);
    }
#endif

#ifdef _CPHAL_CPUSB20
   /* update stats */
   HalDev->Stats.TxServicedPerInt[Ch] += PacketsServiced;
   HalDev->Stats.TxInt[Ch] += PacketsServiced;
   HalDev->Stats.TxTotal += PacketsServiced;
   if (HalDev->Stats.TxMaxServiced[Ch] < PacketsServiced)
     HalDev->Stats.TxMaxServiced[Ch] = PacketsServiced;
#endif
#ifdef _CPHAL_AAL5
  /* update stats */
  HalDev->Stats.TxPacketsServiced[Ch][Queue] += PacketsServiced;
  HalDev->Stats.TxTotal += PacketsServiced;
  if (HalDev->Stats.TxMaxServiced[Ch][Queue] < PacketsServiced)
    HalDev->Stats.TxMaxServiced[Ch][Queue] = PacketsServiced;
#endif

  return(EC_NO_ERRORS);
  }

static int cpswHalEswitchChannelTeardown(HAL_DEVICE *HalDev, int Ch, bit32 Mode);

/**
 *  @ingroup CPHAL_Functions
 *  This function closes the CPHAL module.  The module will be reset.
 *  The Mode parameter should be used to determine the actions taken by 
 *  Close().
 *
 *  @param   HalDev   CPHAL module instance. (set by xxxInitModule())  
 *  @param   Mode     Indicates actions to take on close.  The following integer
 *                    values are valid: <BR>
 *                    1:  Does not free buffer resources, init parameters remain
 *                        intact.  User can then call Open() without calling Init()
 *                        to attempt to reset the device and bring it back to the
 *                        last known state.<BR>
 *                    2:  Frees the buffer resources, but keeps init parameters.  This
 *                        option is a more aggressive means of attempting a device reset.
 *                    3:  Frees the buffer resources, and clears all init parameters. <BR>
 *                        At this point, the caller would have to call to completely
 *                        reinitialize the device (Init()) before being able to call 
 *                        Open().  Use this mode if you are shutting down the module
 *                        and do not plan to restart.
 *
 *  @return  EC_NO_ERRORS (ok).<BR>
 *           Possible Error Codes:<BR>
 *           @ref EC_VAL_INVALID_STATE "EC_VAL_INVALID_STATE"<BR>
 *           Any error code from cpswHalEswitchChannelTeardown().<BR>
 */
static int cpswHalEswitchClose(HAL_DEVICE *HalDev, bit32 Mode)
  {
   int Ch, Inst, Ret;
   OS_DEVICE *TmpOsDev;
   OS_FUNCTIONS *TmpOsFunc;
   HAL_FUNCTIONS *TmpHalFunc;
   char *TmpDeviceInfo;

#ifdef _CPHAL_AAL5
   CPSAR_FUNCTIONS *TmpSarFunc;
   CPSAR_DEVICE *TmpSarDev; 
#endif

#ifdef _CPHAL_CPMAC
   int Ticks;                                                     /*MJH~030306*/
#endif

#ifdef _CPHAL_HDMA
   int i;
   int Ticks;
#endif

   dbgprint(1,"[cppi]cpswHalEswitchClose(HalDev:%08x, Mode:%d)\n", (bit32u)HalDev, Mode);

#ifdef _CPHAL_CPUSB20
   /* Verify that the channels can be closed */

#endif

   /* Verify proper device state */
   if (HalDev->State != enOpened)
     return (MODULE_ERROR | EC_FUNC_CLOSE|EC_VAL_INVALID_STATE);

   /* Teardown all open channels */ 
   for (Ch = 0; Ch <= HIGHEST_CHANNEL; Ch++)
     {
      if (HalDev->ChIsOpen[Ch][DIRECTION_TX] == TRUE)
        {
         if (Mode == 1)
	   {
            Ret = cpswHalEswitchChannelTeardown(HalDev, Ch, TX_TEARDOWN | PARTIAL_TEARDOWN | BLOCKING_TEARDOWN);
            if (Ret) return (Ret);
	   }
          else
	   {
            Ret = cpswHalEswitchChannelTeardown(HalDev, Ch, TX_TEARDOWN | FULL_TEARDOWN | BLOCKING_TEARDOWN);
            if (Ret) return (Ret);
	   }
        }

      if (HalDev->ChIsOpen[Ch][DIRECTION_RX] == TRUE)
        {
         if (Mode == 1)
	   {
            Ret = cpswHalEswitchChannelTeardown(HalDev, Ch, RX_TEARDOWN | PARTIAL_TEARDOWN | BLOCKING_TEARDOWN);
            if (Ret) return (Ret);
	   }
          else
	   {
            Ret = cpswHalEswitchChannelTeardown(HalDev, Ch, RX_TEARDOWN | FULL_TEARDOWN | BLOCKING_TEARDOWN);
            if (Ret) return (Ret);
	   }
        }
     }

#ifndef _CPHAL_HDMA
   /* free fraglist in HalDev */
   HalDev->OsFunc->Free(HalDev->fraglist);
   HalDev->fraglist = 0;
#endif

#ifdef _CPHAL_CPUSB20
   /* unregister the interrupt */
   HalDev->OsFunc->IsrUnRegister(HalDev->OsDev, HalDev->intr_usb);
   HalDev->OsFunc->IsrUnRegister(HalDev->OsDev, HalDev->intr_usb);
#else
   /* unregister the interrupt */
   HalDev->OsFunc->IsrUnRegister(HalDev->OsDev, HalDev->interrupt);
#endif

#ifdef _CPHAL_AAL5
   /* Disable the Tx CPPI DMA */
   TX_CPPI_CTL_REG(HalDev->dev_base) = 0;

   /* Disable the Rx CPPI DMA */
   RX_CPPI_CTL_REG(HalDev->dev_base) = 0;

   /* Close the SAR hardware - puts the device in reset if this module is the
      "last one out" */
   HalDev->SarFunc->Close(HalDev->SarDev, Mode);
#endif

#ifdef _CPHAL_CPUSB20
   /* Disable the CPPI DMAs */
   USB20_TXCTL(HalDev->dev_base) = 0;
   USB20_RXCTL(HalDev->dev_base) = 0;
 
   /* Disable the Cell DMA */
   USB20_CELLDMA_EN(HalDev->dev_base) = 0;
   USB20_CELLDMA_EN(HalDev->dev_base) = 0;

   /* Disable all the CPPI Interrupts */
   USB20_TXINTREN_SET(HalDev->dev_base) =  0;
   USB20_RXINTREN_SET(HalDev->dev_base) =  0;

   /* Clear USB control register */
   USB20_USB_CTRL(HalDev->dev_base)=0;
#endif

#ifdef _CPHAL_CPMAC
   Ticks = 0;    /* Disable Tick Timer */                           /*MJH+030306*/
   HalDev->OsFunc->Control(HalDev->OsDev, hcTick, haCLEAR, &Ticks); /*MJH+030306*/

   /* Free the Phy Information Structure */
   if(HalDev->PhyDev)
     {
      HalDev->OsFunc->Free(HalDev->PhyDev);                         /*MJH+030513*/
      HalDev->PhyDev = 0;                                           /*MJH+030522*/
     }

   /* Perform CPMAC specific closing functions */
   CPMAC_MACCONTROL(HalDev->dev_base) &= ~MII_EN;
   CPMAC_TX_CONTROL(HalDev->dev_base) &= ~TX_EN;
   CPMAC_RX_CONTROL(HalDev->dev_base) &= ~RX_EN;
  
   /* put device back into reset */
   (*(volatile bit32u *)(HalDev->ResetBase)) &=~ (1<<HalDev->ResetBit);
   Ticks = 64;                                                    /*MJH~030306*/
   osfuncSleep(&Ticks);
#endif

#ifdef _CPHAL_HDMA
  Ticks=0;
  HalDev->OsFunc->Control(HalDev->OsDev,hcTick,hcSet,&Ticks);
  cpswHalEswitchDisablePort(HalDev,0);
  cpswHalEswitchDisablePort(HalDev,1);
  cpswHalEswitchDisablePort(HalDev,2);
  Ticks=HalDev->OneSec/100;  /* Wait a 'while' */
  osfuncSleep(&Ticks);

  /* Park the PDSP  */
  cpswHalEswitchPDSPCommand(HalDev, MIPS_PAUSE_PDSP, 0, 0, 0, 0);

  CPPI_MAC_CONTROL(HalDev->Cpmac0Base) &= ~MII_EN;
  CPPI_TX_CONTROL(HalDev->Cpmac0Base) &= ~TX_EN;
  CPPI_RX_CONTROL(HalDev->Cpmac0Base) &= ~RX_EN;

  CPPI_MAC_CONTROL(HalDev->Cpmac1Base) &= ~MII_EN;
  CPPI_TX_CONTROL(HalDev->Cpmac1Base) &= ~TX_EN;
  CPPI_RX_CONTROL(HalDev->Cpmac1Base) &= ~RX_EN;

  CPPI_TX_CONTROL(HalDev->HdmaBase) &= ~TX_EN;
  CPPI_RX_CONTROL(HalDev->HdmaBase) &= ~RX_EN;

  cpswHalUtlReset(HalDev->ResetBase,HalDev->HdmaResetBit);
  cpswHalUtlReset(HalDev->ResetBase,HalDev->Phy0ResetBit);
  cpswHalUtlReset(HalDev->ResetBase,HalDev->Phy1ResetBit);
  cpswHalUtlReset(HalDev->ResetBase,HalDev->MdioResetBit);
  cpswHalUtlReset(HalDev->ResetBase,HalDev->Cpmac0ResetBit);
  cpswHalUtlReset(HalDev->ResetBase,HalDev->Cpmac1ResetBit);
  cpswHalUtlReset(HalDev->ResetBase,HalDev->EswitchResetBit);
  for(i=0;i<MAX_CHAN;i++)
    {
    HAL_TCB *Curr;

    cpswHalEswitchFreeRx(HalDev,i);
    HalDev->ChIsOpen[i][DIRECTION_RX] = 0;
    Curr = HalDev->TxCtlBlk[i][0].TxActQueueHead;
    while(Curr)
      {
      HAL_TCB *Last;

      Curr->SendComplete(Curr->OsInfo);
      Last=Curr->Eop;
      Curr=Last->Next;
      }
    cpswHalEswitchFreeTx(HalDev,i,0);
    HalDev->ChIsOpen[i][DIRECTION_TX] = 0;
    }
  HalDev->OsFunc->FreeDmaXfer(HalDev->EswitchOverFlowArea);
  HalDev->OsFunc->FreeDmaXfer(HalDev->EswitchCposArea);
  HalDev->State = enInitialized;
  return(EC_NO_ERRORS);

#endif

   /* If mode is 3, than clear the HalDev and set next state to DevFound*/
   if (Mode == 3)
     {
      /* I need to keep the HalDev parameters that were setup in InitModule */
      TmpOsDev = HalDev->OsDev;
      TmpOsFunc = HalDev->OsFunc;
      TmpDeviceInfo = HalDev->DeviceInfo;
#ifdef _CPHAL_AAL5
      TmpSarFunc = HalDev->SarFunc;
      TmpSarDev = HalDev->SarDev;
#endif
      TmpHalFunc = HalDev->HalFunc;
      Inst = HalDev->Inst;
  
      /* Clear HalDev */
      dbgprint(1<<6,"  [cppi cpswHalEswitchClose()]Clear HalDev\n");
      cpswHalCommonmemset(HalDev, 0, sizeof(HAL_DEVICE));

      /* Restore key parameters */
      HalDev->OsDev = TmpOsDev;
      HalDev->OsFunc = TmpOsFunc;
      HalDev->DeviceInfo = TmpDeviceInfo;
#ifdef _CPHAL_AAL5
      HalDev->SarFunc = TmpSarFunc;
      HalDev->SarDev = TmpSarDev;
#endif
      HalDev->HalFunc = TmpHalFunc;
      HalDev->Inst = Inst;

      HalDev->State = enDevFound;
     }
    else
     {
      HalDev->State = enInitialized;
     }

   return(EC_NO_ERRORS);
  }

/**
 *  @ingroup CPHAL_Functions
 *  This function performs a teardown for the given channel.  The value of the
 *  Mode parameter controls the operation of the function, as documented below. 
 *
 *  Note: If bit 3 of Mode is set, this call is blocking, and will not return 
 *  until the teardown interrupt has occurred and been processed. While waiting 
 *  for a blocking teardown to complete, cpswHalEswitchChannelTeardown() will signal the OS
 *  (via Control(.."Sleep"..)) to allow the OS to perform other tasks if 
 *  necessary.  If and only if bit 3 of Mode is clear, the CPHAL will call the
 *  OS TeardownComplete() function to indicate that the teardown has completed.  
 *  
 *  @param   HalDev  CPHAL module instance. (set by xxxInitModule())  
 *  @param   Ch      Channel number.
 *  @param   Mode    Bit 0 (LSB): Perform Tx teardown (if set).<BR>
 *                   Bit 1: Perform Rx teardown (if set). <BR>
 *                   Bit 2: If set, perform full teardown (free buffers/descriptors). 
 *                          If clear, perform partial teardown (keep buffers). <BR>
 *                   Bit 3 (MSB): If set, call is blocking.
 *                            If clear, call is non-blocking.
 *
 *  @return  EC_NO_ERRORS (ok). <BR>
 *           Possible Error Codes:<BR>
 *           @ref EC_VAL_INVALID_STATE "EC_VAL_INVALID_STATE"<BR>
 *           @ref EC_VAL_INVALID_CH "EC_VAL_INVALID_CH"<BR>
 *           @ref EC_VAL_TX_TEARDOWN_ALREADY_PEND "EC_VAL_TX_TEARDOWN_ALREADY_PEND"<BR>
 *           @ref EC_VAL_RX_TEARDOWN_ALREADY_PEND "EC_VAL_RX_TEARDOWN_ALREADY_PEND"<BR>
 *           @ref EC_VAL_TX_CH_ALREADY_TORNDOWN "EC_VAL_TX_CH_ALREADY_TORNDOWN"<BR>
 *           @ref EC_VAL_RX_CH_ALREADY_TORNDOWN "EC_VAL_RX_CH_ALREADY_TORNDOWN"<BR>
 *           @ref EC_VAL_TX_TEARDOWN_TIMEOUT "EC_VAL_TX_TEARDOWN_TIMEOUT"<BR>
 *           @ref EC_VAL_RX_TEARDOWN_TIMEOUT "EC_VAL_RX_TEARDOWN_TIMEOUT"<BR>
 *           @ref EC_VAL_LUT_NOT_READY "EC_VAL_LUT_NOT_READY"<BR>
 */
static int cpswHalEswitchChannelTeardown(HAL_DEVICE *HalDev, int Ch, bit32 Mode)
  {
   int DoTx, DoRx, Sleep=2048, timeout=0;                         /*MJH~030306*/

/* Set the module, used for error returns */
#ifdef _CPHAL_AAL5
   int Ret;
#endif
   /* dummy loop variable */ /* 030707+MV */
#ifdef _CPHAL_CPUSB20
   int tmp=0;
#endif

   dbgprint(1,"[cppi]cpswHalEswitchChannelTeardown(HalDev:%08x, Ch:%d, Mode:%d)\n", 
            (bit32u)HalDev, Ch, Mode);

#ifdef _CPHAL_AAL5
   /* AAL5 only supports tearing down both sides at once (currently)*/
   Mode = (Mode | TX_TEARDOWN | RX_TEARDOWN);
#endif

#ifdef _CPHAL_CPUSB20
   /* USB20 only supports tearing down all channels at once and only supports full teardown*/
   Mode = (Mode | TX_TEARDOWN | RX_TEARDOWN |FULL_TEARDOWN );
   /* Make sure that the CPPI DMAs are in IDLE state before tearing down the channels */
   while ( USB20_TEARDOWN_REG(0xa4020000) != 0 ){ tmp++;}
   /* Disable all the interrupts */
   USB20_TXINTREN_CLR(HalDev->dev_base) = HalDev->VbusTxIntrEnMask;
   USB20_RXINTREN_CLR(HalDev->dev_base) = HalDev->VbusRxIntrEnMask;
#endif

   DoTx = (Mode & TX_TEARDOWN);
   DoRx = (Mode & RX_TEARDOWN);

   if (HalDev->State < enInitialized)
     return(MODULE_ERROR|EC_FUNC_CHTEARDOWN|EC_VAL_INVALID_STATE);

   if ((Ch < 0) || (Ch > HIGHEST_CHANNEL)) 
     {
     return(MODULE_ERROR|EC_FUNC_CHTEARDOWN|EC_VAL_INVALID_CH);
     }

   /* set teardown pending bits before performing the teardown, because they 
      will be used in the int handler (this is done for AAL5) */
   if (DoTx)
     {
      if (HalDev->TxTeardownPending[Ch] != 0)
        return(MODULE_ERROR|EC_FUNC_CHTEARDOWN|EC_VAL_TX_TEARDOWN_ALREADY_PEND);

      /* If a full teardown, this also means that the user must
      setup all channels again to use them */
      if (Mode & FULL_TEARDOWN)
        HalDev->ChIsSetup[Ch][DIRECTION_TX] = 0;

      if (HalDev->State < enOpened)
        {
         /* if the hardware has never been opened, the channel has never actually
         been setup in the hardware, so I just need to reset the software flag
         and leave */
         HalDev->ChIsSetup[Ch][DIRECTION_TX] = 0;
         return (EC_NO_ERRORS);
	}
       else
	{
         if (HalDev->ChIsOpen[Ch][DIRECTION_TX] == 0)
	   {
            return(MODULE_ERROR|EC_FUNC_CHTEARDOWN|EC_VAL_TX_CH_ALREADY_TORNDOWN);
	   }

         /* set teardown flag */
         HalDev->TxTeardownPending[Ch] = Mode;
	}
     }

   if (DoRx)
     {
      if (HalDev->RxTeardownPending[Ch] != 0)
        return(MODULE_ERROR|EC_FUNC_CHTEARDOWN|EC_VAL_RX_TEARDOWN_ALREADY_PEND);

      if (Mode & FULL_TEARDOWN)
        HalDev->ChIsSetup[Ch][DIRECTION_RX] = 0;

      if (HalDev->State < enOpened)
        {
         HalDev->ChIsSetup[Ch][DIRECTION_RX] = 0;
         return (EC_NO_ERRORS);
	}
       else
	{
         if (HalDev->ChIsOpen[Ch][DIRECTION_RX] == 0)
           return(MODULE_ERROR|EC_FUNC_CHTEARDOWN|EC_VAL_RX_CH_ALREADY_TORNDOWN);
      
         HalDev->RxTeardownPending[Ch] = Mode;
	}
     }

#ifdef _CPHAL_HDMA
   HalDev->TxTeardownPending[Ch] = 0;
   HalDev->RxTeardownPending[Ch] = 0;
   return (EC_NO_ERRORS);
#endif

   /* Perform Tx Teardown Duties */
   if ((DoTx) && (HalDev->State == enOpened))
     {
      /* Request TX channel teardown */
      TRANSMIT_PORT_TEARDOWN_REG(HalDev->dev_base) = Ch;      

      /* wait until teardown has completed */
      if (Mode & BLOCKING_TEARDOWN)
        {
	 timeout = 0;
         while (HalDev->ChIsOpen[Ch][DIRECTION_TX] == TRUE)
	   {
            osfuncSleep(&Sleep);

            timeout++;
	    if (timeout > 100000)
	      {
               return(MODULE_ERROR|EC_FUNC_CHTEARDOWN|EC_VAL_TX_TEARDOWN_TIMEOUT);     
	      }
	   }
	}
     } /* if DoTx */

   /* Perform Rx Teardown Duties */
   if ((DoRx) && (HalDev->State == enOpened))
     {
#ifdef _CPHAL_AAL5
      /* call main teardown routine for Rx */
      Ret = HalDev->SarFunc->ChannelTeardown(HalDev->SarDev, Ch, Mode);
      if (Ret) return (Ret);
#endif

#ifdef _CPHAL_CPMAC
      /* perform CPMAC specific RX channel teardown */
      CPMAC_RX_TEARDOWN(HalDev->dev_base) = Ch;
#endif
      if (Mode & BLOCKING_TEARDOWN)
        {
	 timeout = 0;
         while (HalDev->ChIsOpen[Ch][DIRECTION_RX] == TRUE)
	   {
            osfuncSleep(&Sleep);

            timeout++;
	    if (timeout > 100000)
	      {
               return(MODULE_ERROR|EC_FUNC_CHTEARDOWN|EC_VAL_RX_TEARDOWN_TIMEOUT);     
	      }
	   }
        }
     } /* if DoRx */

   return (EC_NO_ERRORS);
  }
