/******************************************************************************
 *
 * Name:        sky2.c
 * Project:     Yukon2 specific functions and implementations
 * Version:     $Revision: 1.35.2.28 $
 * Date:        $Date: 2005/03/22 13:51:13 $
 * Purpose:     The main driver source module
 *
 *****************************************************************************/

/******************************************************************************
 *
 *	(C)Copyright 1998-2002 SysKonnect GmbH.
 *	(C)Copyright 2002-2004 Marvell.
 *
 *	Driver for Marvell Yukon/2 chipset and SysKonnect Gigabit Ethernet 
 *      Server Adapters.
 *
 *	Author: Ralph Roesler (rroesler@syskonnect.de)
 *	        Mirko Lindner (mlindner@syskonnect.de)
 *
 *	Address all question to: linux@syskonnect.de
 *
 *	The technical manual for the adapters is available from SysKonnect's
 *	web pages: www.syskonnect.com
 *	
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	The information in this file is provided "AS IS" without warranty.
 *
 *****************************************************************************/

#include "h/skdrv1st.h"
#include "h/skdrv2nd.h"
#include <linux/tcp.h>

/******************************************************************************
 *
 * Local Function Prototypes
 *
 *****************************************************************************/

static void InitPacketQueues(SK_AC *pAC,int Port);
static void GiveTxBufferToHw(SK_AC *pAC,SK_IOC IoC,int Port);
static void GiveRxBufferToHw(SK_AC *pAC,SK_IOC IoC,int Port,SK_PACKET *pPacket);
static void FillReceiveTableYukon2(SK_AC *pAC,SK_IOC IoC,int Port);
static SK_BOOL HandleReceives(SK_AC *pAC,int Port,SK_U16 Len,SK_U32 FrameStatus,SK_U16 Tcp1,SK_U16 Tcp2,SK_U32 Tist,SK_U16 Vlan);
static void CheckForSendComplete(SK_AC *pAC,SK_IOC IoC,int Port,SK_PKT_QUEUE *pPQ,SK_LE_TABLE *pLETab,unsigned int Done);
static void UnmapAndFreeTxPktBuffer(SK_AC *pAC,SK_PACKET *pSkPacket,int TxPort);
static SK_BOOL AllocateAndInitLETables(SK_AC *pAC);
static SK_BOOL AllocatePacketBuffersYukon2(SK_AC *pAC);
static void FreeLETables(SK_AC *pAC);
static void FreePacketBuffers(SK_AC *pAC);
static SK_BOOL AllocAndMapRxBuffer(SK_AC *pAC,SK_PACKET *pSkPacket,int Port);
#ifdef CONFIG_SK98LIN_NAPI
static SK_BOOL HandleStatusLEs(SK_AC *pAC,int *WorkDone,int WorkToDo);
#else
static SK_BOOL HandleStatusLEs(SK_AC *pAC);
#endif

extern void	SkGeCheckTimer		(DEV_NET *pNet);
extern void	SkLocalEventQueue(	SK_AC *pAC,
					SK_U32 Class,
					SK_U32 Event,
					SK_U32 Param1,
					SK_U32 Param2,
					SK_BOOL Flag);
extern void	SkLocalEventQueue64(	SK_AC *pAC,
					SK_U32 Class,
					SK_U32 Event,
					SK_U64 Param,
					SK_BOOL Flag);

/******************************************************************************
 *
 * Local Variables
 *
 *****************************************************************************/

#define MAX_NBR_RX_BUFFERS_IN_HW	0x15
static SK_U8 NbrRxBuffersInHW;

#if defined(__i386__) || defined(__x86_64__)
#if defined(__x86_64__)
#define FLUSH_OPC(le)
/* #define FLUSH_OPC(le)			\ 	*/
/*	cache0 = ((long *)(le))[0]; 		\	*/
/*	cache1 = ((long *)(le))[1];		\	*/
/*	((volatile long *)(le))[0] = cache0;	\	*/
/*	((volatile long *)(le))[1] = cache1;		*/
#else
#define FLUSH_OPC(le) 
#endif
#else
#define FLUSH_OPC(le) 
#endif

/******************************************************************************
 *
 * Global Functions
 *
 *****************************************************************************/

int SkY2Xmit( struct sk_buff *skb, struct SK_NET_DEVICE *dev); 

/*****************************************************************************
 *
 * 	SkY2RestartStatusUnit - restarts teh status unit
 *
 * Description:
 *	Reenables the status unit after any De-Init (e.g. when altering 
 *	the sie of the MTU via 'ifconfig a.b.c.d mtu xxx')
 *
 * Returns:	N/A
 */
void SkY2RestartStatusUnit(
SK_AC  *pAC)  /* pointer to adapter control context */
{
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("==> SkY2RestartStatusUnit\n"));

	/*
	** It might be that the TX timer is not started. Therefore
	** it is initialized here -> to be more investigated!
	*/
	SK_OUT32(pAC->IoBase, STAT_TX_TIMER_INI, HW_MS_TO_TICKS(pAC,10));

	pAC->StatusLETable.Done  = 0;
	pAC->StatusLETable.Put   = 0;
	pAC->StatusLETable.HwPut = 0;
	SkGeY2InitStatBmu(pAC, pAC->IoBase, &pAC->StatusLETable);

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("<== SkY2RestartStatusUnit\n"));
}

/*****************************************************************************
 *
 * 	SkY2RlmtSend - sends out a single RLMT notification
 *
 * Description:
 *	This function sends out an RLMT frame
 *
 * Returns:	
 *	> 0 - on succes: the number of bytes in the message
 *	= 0 - on resource shortage: this frame sent or dropped, now
 *	      the ring is full ( -> set tbusy)
 *	< 0 - on failure: other problems ( -> return failure to upper layers)
 */
int SkY2RlmtSend (
SK_AC          *pAC,       /* pointer to adapter control context           */
int             PortNr,    /* index of port the packet(s) shall be send to */
struct sk_buff *pMessage)  /* pointer to send-message                      */
{
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("=== SkY2RlmtSend\n"));
#if 0
	return -1;   // temporarily do not send out RLMT frames
#endif
	skb_shinfo(pMessage)->nr_frags = (2*MAX_SKB_FRAGS) + PortNr;
	return(SkY2Xmit(pMessage, pAC->dev[PortNr])); // SkY2Xmit needs device
}

/*****************************************************************************
 *
 * 	SkY2AllocateResources - Allocates all required resources for Yukon2
 *
 * Description:
 *	This function allocates all memory needed for the Yukon2. 
 *	It maps also RX buffers to the LETables and initializes the
 *	status list element table.
 *
 * Returns:	
 *	SK_TRUE, if all resources could be allocated and setup succeeded
 *	SK_FALSE, if an error 
 */
SK_BOOL SkY2AllocateResources (
SK_AC  *pAC)  /* pointer to adapter control context */
{
	int CurrMac;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
		("==> SkY2AllocateResources\n"));

	/*
	** Initialize the packet queue variables first
	*/
	for (CurrMac = 0; CurrMac < pAC->GIni.GIMacsFound; CurrMac++) {
		InitPacketQueues(pAC, CurrMac);
	}

	/* 
	** Get sufficient memory for the LETables
	*/
	if (!AllocateAndInitLETables(pAC)) {
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
			SK_DBGCAT_INIT | SK_DBGCAT_DRV_ERROR,
			("No memory for LETable.\n"));
		return(SK_FALSE);
	}

	/*
	** Allocate and intialize memory for both RX and TX 
	** packet and fragment buffers. On an error, free 
	** previously allocated LETable memory and quit.
	*/
	if (!AllocatePacketBuffersYukon2(pAC)) {
		FreeLETables(pAC);
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
			SK_DBGCAT_INIT | SK_DBGCAT_DRV_ERROR,
			("No memory for Packetbuffers.\n"));
		return(SK_FALSE);
	}

	/* 
	** Rx and Tx LE tables will be initialized in SkGeOpen() 
	**
	** It might be that the TX timer is not started. Therefore
	** it is initialized here -> to be more investigated!
	*/
	SK_OUT32(pAC->IoBase, STAT_TX_TIMER_INI, HW_MS_TO_TICKS(pAC,10));
	SkGeY2InitStatBmu(pAC, pAC->IoBase, &pAC->StatusLETable);

	pAC->MaxUnusedRxLeWorking = MAX_UNUSED_RX_LE_WORKING;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
		("<== SkY2AllocateResources\n"));

	return (SK_TRUE);
}

/*****************************************************************************
 *
 * 	SkY2FreeResources - Frees previously allocated resources of Yukon2
 *
 * Description:
 *	This function frees all previously allocated memory of the Yukon2. 
 *
 * Returns: N/A
 */
void SkY2FreeResources (
SK_AC  *pAC)  /* pointer to adapter control context */
{
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("==> SkY2FreeResources\n"));

	FreeLETables(pAC);
	FreePacketBuffers(pAC);

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("<== SkY2FreeResources\n"));
}

/*****************************************************************************
 *
 * 	SkY2AllocateRxBuffers - Allocates the receive buffers for a port
 *
 * Description:
 *	This function allocated all the RX buffers of the Yukon2. 
 *
 * Returns: N/A
 */
void SkY2AllocateRxBuffers (
SK_AC    *pAC,   /* pointer to adapter control context */
SK_IOC    IoC,	 /* I/O control context                */
int       Port)	 /* port index of RX                   */
{
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
		("==> SkY2AllocateRxBuffers (Port %c)\n", Port));

	FillReceiveTableYukon2(pAC, IoC, Port);

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
		("<== SkY2AllocateRxBuffers\n"));
}

/*****************************************************************************
 *
 * 	SkY2FreeRxBuffers - Free's all allocates RX buffers of
 *
 * Description:
 *	This function frees all RX buffers of the Yukon2 for a single port
 *
 * Returns: N/A
 */
void SkY2FreeRxBuffers (
SK_AC    *pAC,   /* pointer to adapter control context */
SK_IOC    IoC,	 /* I/O control context                */
int       Port)	 /* port index of RX                   */
{
	SK_PACKET     *pSkPacket;
	unsigned long  Flags;   /* for POP/PUSH macros */

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("==> SkY2FreeRxBuffers (Port %c)\n", Port));

	if (pAC->RxPort[Port].ReceivePacketTable   != NULL) {
		POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_working, pSkPacket);
		while (pSkPacket != NULL) {
			if ((pSkPacket->pFrag) != NULL) {
				pci_unmap_page(pAC->PciDev,
				(dma_addr_t) pSkPacket->pFrag->pPhys,
				pSkPacket->pFrag->FragLen - 2,
				PCI_DMA_FROMDEVICE);

				DEV_KFREE_SKB_ANY(pSkPacket->pMBuf);
				pSkPacket->pMBuf        = NULL;
				pSkPacket->pFrag->pPhys = (SK_U64) 0;
				pSkPacket->pFrag->pVirt = NULL;
			}
			PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pSkPacket);
			POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_working, pSkPacket);
		}
	}

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("<== SkY2FreeRxBuffers\n"));
}

/*****************************************************************************
 *
 * 	SkY2FreeTxBuffers - Free's any currently maintained Tx buffer
 *
 * Description:
 *	This function frees the TX buffers of the Yukon2 for a single port
 *	which might be in use by a transmit action
 *
 * Returns: N/A
 */
void SkY2FreeTxBuffers (
SK_AC    *pAC,   /* pointer to adapter control context */
SK_IOC    IoC,	 /* I/O control context                */
int       Port)	 /* port index of TX                   */
{
	SK_PACKET      *pSkPacket;
	SK_FRAG        *pSkFrag;
	unsigned long   Flags;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("==> SkY2FreeTxBuffers (Port %c)\n", Port));
 
	if (pAC->TxPort[Port][0].TransmitPacketTable != NULL) {
		POP_FIRST_PKT_FROM_QUEUE(&pAC->TxPort[Port][0].TxAQ_working, pSkPacket);
		while (pSkPacket != NULL) {
			if ((pSkFrag = pSkPacket->pFrag) != NULL) {
				UnmapAndFreeTxPktBuffer(pAC, pSkPacket, Port);
			}
			PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->TxPort[Port][0].TxQ_free, pSkPacket);
			POP_FIRST_PKT_FROM_QUEUE(&pAC->TxPort[Port][0].TxAQ_working, pSkPacket);
		}
#if USE_SYNC_TX_QUEUE
		POP_FIRST_PKT_FROM_QUEUE(&pAC->TxPort[Port][0].TxSQ_working, pSkPacket);
		while (pSkPacket != NULL) {
			if ((pSkFrag = pSkPacket->pFrag) != NULL) {
				UnmapAndFreeTxPktBuffer(pAC, pSkPacket, Port);
			}
			PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->TxPort[Port][0].TxQ_free, pSkPacket);
			POP_FIRST_PKT_FROM_QUEUE(&pAC->TxPort[Port][0].TxSQ_working, pSkPacket);
		}
#endif
	}

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("<== SkY2FreeTxBuffers\n"));
}

/*****************************************************************************
 *
 * 	SkY2Isr - handle a receive IRQ for all yukon2 cards
 *
 * Description:
 *	This function is called when a receive IRQ is set. (only for yukon2)
 *	HandleReceives does the deferred processing of all outstanding
 *	interrupt operations.
 *
 * Returns:	N/A
 */
SkIsrRetVar SkY2Isr (
int              irq,     /* the irq we have received (might be shared!) */
void            *dev_id,  /* current device id                           */
struct  pt_regs *ptregs)  /* not used by our driver                      */
{
	struct SK_NET_DEVICE  *dev  = (struct SK_NET_DEVICE *)dev_id;
	DEV_NET               *pNet = (DEV_NET*) dev->priv;
	SK_AC                 *pAC  = pNet->pAC;
	SK_U32                 IntSrc;
	unsigned long          Flags;
#ifndef CONFIG_SK98LIN_NAPI
	SK_BOOL                handledStatLE = SK_FALSE;
#endif

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
		("==> SkY2Isr\n"));

	SK_IN32(pAC->IoBase, B0_Y2_SP_ISRC2, &IntSrc);

	if (IntSrc == 0) {
		SK_OUT32(pAC->IoBase, B0_Y2_SP_ICR, 2);
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
			("No Interrupt\n ==> SkY2Isr\n"));
		return;

	}

#ifdef Y2_RECOVERY
	if (pNet->InRecover) {
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
			("Already in recover\n ==> SkY2Isr\n"));
		SK_OUT32(pAC->IoBase, B0_Y2_SP_ICR, 2);
		return;
	}
#endif

#ifdef CONFIG_SK98LIN_NAPI
	if (netif_rx_schedule_prep(dev)) {
		pAC->GIni.GIValIrqMask &= ~(Y2_IS_STAT_BMU);
		SK_OUT32(pAC->IoBase, B0_IMSK, pAC->GIni.GIValIrqMask);
		__netif_rx_schedule(dev);
	}
#else
	handledStatLE = HandleStatusLEs(pAC);
#endif

	/* 
	** Check for Special Interrupts 
	*/
	if ((IntSrc & ~Y2_IS_STAT_BMU) || pAC->CheckQueue || pNet->TimerExpired) {
		pAC->CheckQueue = SK_FALSE;
		spin_lock_irqsave(&pAC->SlowPathLock, Flags);
#ifdef Y2_RECOVERY
		if (pNet->TimerExpired) {
			SkGeCheckTimer(pNet);
		}
#endif
		SkGeSirqIsr(pAC, pAC->IoBase, IntSrc);
		SkEventDispatcher(pAC, pAC->IoBase);
		spin_unlock_irqrestore(&pAC->SlowPathLock, Flags);
	}

	/* Speed enhancement for a2 chipsets */
	if (HW_FEATURE(pAC, HWF_WA_DEV_42)) {
		spin_lock_irqsave(&pAC->SetPutIndexLock, Flags);
		SkGeY2SetPutIndex(pAC, pAC->IoBase, Y2_PREF_Q_ADDR(Q_XA1,0), &pAC->TxPort[0][0].TxALET);
		SkGeY2SetPutIndex(pAC, pAC->IoBase, Y2_PREF_Q_ADDR(Q_R1,0), &pAC->RxPort[0].RxLET);
		spin_unlock_irqrestore(&pAC->SetPutIndexLock, Flags);
	}

	/* 
	** Reenable interrupts and signal end of ISR 
	*/
	SK_OUT32(pAC->IoBase, B0_Y2_SP_ICR, 2);
			
	/*
	** Stop and restart TX timer in case a Status LE was handled
	*/
#ifndef CONFIG_SK98LIN_NAPI
	if ((HW_FEATURE(pAC, HWF_WA_DEV_43_418)) && (handledStatLE)) {
		SK_OUT8(pAC->IoBase, STAT_TX_TIMER_CTRL, TIM_STOP);
		SK_OUT8(pAC->IoBase, STAT_TX_TIMER_CTRL, TIM_START);
	}
#endif

	if (!(IS_Q_EMPTY(&(pAC->TxPort[0][TX_PRIO_LOW].TxAQ_waiting)))) {
		GiveTxBufferToHw(pAC, pAC->IoBase, 0);
	}
	if (!(IS_Q_EMPTY(&(pAC->TxPort[1][TX_PRIO_LOW].TxAQ_waiting)))) {
		GiveTxBufferToHw(pAC, pAC->IoBase, 1);
	}

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
		("<== SkY2Isr\n"));

	return;
}	/* SkY2Isr */

/*****************************************************************************
 *
 *	SkY2Xmit - Linux frame transmit function for Yukon2
 *
 * Description:
 *	The system calls this function to send frames onto the wire.
 *	It puts the frame in the tx descriptor ring. If the ring is
 *	full then, the 'tbusy' flag is set.
 *
 * Returns:
 *	0, if everything is ok
 *	!=0, on error
 *
 * WARNING: 
 *	returning 1 in 'tbusy' case caused system crashes (double
 *	allocated skb's) !!!
 */
int SkY2Xmit(
struct sk_buff       *skb,  /* socket buffer to be sent */
struct SK_NET_DEVICE *dev)  /* via which device?        */
{
	DEV_NET         *pNet    = (DEV_NET*) dev->priv;
	SK_AC           *pAC     = pNet->pAC;
	SK_U8            FragIdx = 0;
	SK_PACKET       *pSkPacket;
	SK_FRAG         *PrevFrag;
	SK_FRAG         *CurrFrag;
	SK_PKT_QUEUE    *pWorkQueue;  /* corresponding TX queue */
	SK_PKT_QUEUE    *pWaitQueue; 
	SK_PKT_QUEUE    *pFreeQueue; 
	SK_LE_TABLE     *pLETab;      /* corresponding LETable  */ 
	skb_frag_t      *sk_frag;
	SK_U64           PhysAddr;
	unsigned long    Flags;
	unsigned int     Port;
	int              CurrFragCtr;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
		("==> SkY2Xmit\n"));

	/*
	** Get port and return if no free packet is available 
	*/
	if (skb_shinfo(skb)->nr_frags > MAX_SKB_FRAGS) {
		Port = skb_shinfo(skb)->nr_frags - (2*MAX_SKB_FRAGS);
		skb_shinfo(skb)->nr_frags = 0;
	} else {
		Port = (pAC->RlmtNets == 2) ? pNet->PortNr : pAC->ActivePort;
	}

	if (IS_Q_EMPTY(&(pAC->TxPort[Port][TX_PRIO_LOW].TxQ_free))) {
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
			SK_DBGCAT_DRV_TX_PROGRESS | SK_DBGCAT_DRV_ERROR,
			("Not free packets available for send\n"));
		return 1; /* zero bytes sent! */
	}

	/*
	** Put any new packet to be sent in the waiting queue and 
	** handle also any possible fragment of that packet.
	*/
	pWorkQueue = &(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_working);
	pWaitQueue = &(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting);
	pFreeQueue = &(pAC->TxPort[Port][TX_PRIO_LOW].TxQ_free);
	pLETab     = &(pAC->TxPort[Port][TX_PRIO_LOW].TxALET);

	/*
	** Normal send operations require only one fragment, because 
	** only one sk_buff data area is passed. 
	** In contradiction to this, scatter-gather (zerocopy) send
	** operations might pass one or more additional fragments 
	** where each fragment needs a separate fragment info packet.
	*/
	if (((skb_shinfo(skb)->nr_frags + 1) * MAX_FRAG_OVERHEAD) > 
					NUM_FREE_LE_IN_TABLE(pLETab)) {
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
			SK_DBGCAT_DRV_TX_PROGRESS | SK_DBGCAT_DRV_ERROR,
			("Not enough LE available for send\n"));
		return 1; /* zero bytes sent! */
	}
	
	if ((skb_shinfo(skb)->nr_frags + 1) > MAX_NUM_FRAGS) {
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
			SK_DBGCAT_DRV_TX_PROGRESS | SK_DBGCAT_DRV_ERROR,
			("Not even one fragment available for send\n"));
		return 1; /* zero bytes sent! */
	}

	/*
	** Get first packet from free packet queue
	*/
	POP_FIRST_PKT_FROM_QUEUE(pFreeQueue, pSkPacket);
	if(pSkPacket == NULL) {
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
			SK_DBGCAT_DRV_TX_PROGRESS | SK_DBGCAT_DRV_ERROR,
			("Could not obtain free packet used for xmit\n"));
		return 1; /* zero bytes sent! */
	}

	pSkPacket->pFrag = &(pSkPacket->FragArray[FragIdx]);

	/* 
	** map the sk_buff to be available for the adapter 
	*/
	PhysAddr = (SK_U64) pci_map_page(pAC->PciDev,
			virt_to_page(skb->data),
			((unsigned long) skb->data & ~PAGE_MASK),
			skb_headlen(skb),
			PCI_DMA_TODEVICE);
	pSkPacket->pMBuf	  = skb;
	pSkPacket->pFrag->pPhys   = PhysAddr;
	pSkPacket->pFrag->FragLen = skb_headlen(skb);
	pSkPacket->pFrag->pNext   = NULL; /* initial has no next default */
	pSkPacket->NumFrags	  = skb_shinfo(skb)->nr_frags + 1;

	PrevFrag = pSkPacket->pFrag;

	/*
	** Each scatter-gather fragment need to be mapped...
	*/
        for (	CurrFragCtr = 0; 
		CurrFragCtr < skb_shinfo(skb)->nr_frags;
		CurrFragCtr++) {
		FragIdx++;
		sk_frag = &skb_shinfo(skb)->frags[CurrFragCtr];
		CurrFrag = &(pSkPacket->FragArray[FragIdx]);

		/* 
		** map the sk_buff to be available for the adapter 
		*/
		PhysAddr = (SK_U64) pci_map_page(pAC->PciDev,
				sk_frag->page,
		 		sk_frag->page_offset,
		 		sk_frag->size,
		 		PCI_DMA_TODEVICE);

		CurrFrag->pPhys   = PhysAddr;
 		CurrFrag->FragLen = sk_frag->size;
 		CurrFrag->pNext   = NULL;

		/*
		** Add the new fragment to the list of fragments
		*/
		PrevFrag->pNext = CurrFrag;
		PrevFrag = CurrFrag;
	}

	/* 
	** Add packet to waiting packets queue 
	*/
	PUSH_PKT_AS_LAST_IN_QUEUE(pWaitQueue, pSkPacket);
	GiveTxBufferToHw(pAC, pAC->IoBase, Port);
	dev->trans_start = jiffies;
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
		("<== SkY2Xmit(return 0)\n"));
	return (0);
}	/* SkY2Xmit */

#ifdef CONFIG_SK98LIN_NAPI
/*****************************************************************************
 *
 *	SkY2Poll - NAPI Rx polling callback for Yukon2 chipsets
 *
 * Description:
 *	Called by the Linux system in case NAPI polling is activated
 *
 * Returns
 *	The number of work data still to be handled
 *
 * Notes
 *	The slowpath lock needs to be set because HW accesses may
 *	interfere with slowpath events (e.g. TWSI)
 */
int SkY2Poll(
struct net_device *dev,     /* device that needs to be polled */
int               *budget)  /* how many budget do we have?    */
{
	SK_AC	       *pAC           = ((DEV_NET*)(dev->priv))->pAC;
	int	        WorkToDo      = min(*budget, dev->quota);
	int	        WorkDone      = 0;
	SK_BOOL         handledStatLE = SK_FALSE;
	unsigned long   Flags;       

	spin_lock_irqsave(&pAC->SlowPathLock, Flags);
	handledStatLE = HandleStatusLEs(pAC, &WorkDone, WorkToDo);

	*budget -= WorkDone;
	dev->quota -= WorkDone;

	if(WorkDone < WorkToDo) {
		netif_rx_complete(dev);
		pAC->GIni.GIValIrqMask |= (Y2_IS_STAT_BMU);
		SK_OUT32(pAC->IoBase, B0_IMSK, pAC->GIni.GIValIrqMask);
		if ((HW_FEATURE(pAC, HWF_WA_DEV_43_418)) && (handledStatLE)) {
			SK_OUT8(pAC->IoBase, STAT_TX_TIMER_CTRL, TIM_STOP);
			SK_OUT8(pAC->IoBase, STAT_TX_TIMER_CTRL, TIM_START);
		}
	}
	spin_unlock_irqrestore(&pAC->SlowPathLock, Flags);
	return (WorkDone >= WorkToDo);
}	/* SkY2Poll */
#endif

/******************************************************************************
 *
 *	SkY2PortStop - stop a port on Yukon2
 *
 * Description:
 *	This function stops a port of the Yukon2 chip. This stop 
 *	stop needs to be performed in a specific order:
 * 
 *	a) Stop the Prefetch unit
 *	b) Stop the Port (MAC, PHY etc.)
 *
 * Returns: N/A
 */
void SkY2PortStop(
SK_AC   *pAC,      /* adapter control context                             */
SK_IOC   IoC,      /* I/O control context (address of adapter registers)  */
int      Port,     /* port to stop (MAC_1 + n)                            */
int      Dir,      /* StopDirection (SK_STOP_RX, SK_STOP_TX, SK_STOP_ALL) */
int      RstMode)  /* Reset Mode (SK_SOFT_RST, SK_HARD_RST)               */
{
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("==> SkY2PortStop (Port %c)\n", 'A' + Port));

	/*
	** Stop the HW
	*/
	SkGeStopPort(pAC, IoC, Port, Dir, RstMode);

	/*
	** Move any TX packet from work queues into the free queue again
	** and initialize the TX LETable variables
	*/
	SkY2FreeTxBuffers(pAC, pAC->IoBase, Port);
	pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Bmu.RxTx.TcpWp    = 0;
	pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Bmu.RxTx.MssValue = 0;
	pAC->TxPort[Port][TX_PRIO_LOW].TxALET.BufHighAddr       = 0;
	pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Done              = 0;    
	pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Put               = 0;
	// pAC->GIni.GP[Port].PState = SK_PRT_STOP;

	/*
	** Move any RX packet from work queue into the waiting queue
	** and initialize the RX LETable variables
	*/
	SkY2FreeRxBuffers(pAC, pAC->IoBase, Port);
	pAC->RxPort[Port].RxLET.BufHighAddr = 0;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("<== SkY2PortStop()\n"));
}

/******************************************************************************
 *
 *	SkY2PortStart - start a port on Yukon2
 *
 * Description:
 *	This function starts a port of the Yukon2 chip. This start 
 *	action needs to be performed in a specific order:
 * 
 *	a) Initialize the LET indices (PUT/GET to 0)
 *	b) Initialize the LET in HW (enables also prefetch unit)
 *	c) Move all RX buffers from waiting queue to working queue
 *	   which involves also setting up of RX list elements
 *	d) Initialize the FIFO settings of Yukon2 (Watermark etc.)
 *	e) Initialize the Port (MAC, PHY etc.)
 *	f) Initialize the MC addresses
 *
 * Returns:	N/A
 */
void SkY2PortStart(
SK_AC   *pAC,   /* adapter control context                            */
SK_IOC   IoC,   /* I/O control context (address of adapter registers) */
int      Port)  /* port to start                                      */
{
	// SK_GEPORT *pPrt = &pAC->GIni.GP[Port];
	SK_HWLE   *pLE;
	SK_U32     DWord;
	SK_U32     PrefetchReg; /* register for Put index */
#if defined(__x86_64__)
	long       cache0, cache1;
#endif

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("==> SkY2PortStart (Port %c)\n", 'A' + Port));

	/*
	** Initialize the LET indices
	*/
	pAC->RxPort[Port].RxLET.Done                = 0; 
	pAC->RxPort[Port].RxLET.Put                 = 0;
	pAC->RxPort[Port].RxLET.HwPut               = 0;
	pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Done  = 0;    
	pAC->TxPort[Port][TX_PRIO_LOW].TxALET.Put   = 0;
	pAC->TxPort[Port][TX_PRIO_LOW].TxALET.HwPut = 0;
	if (HW_SYNC_TX_SUPPORTED(pAC)) {
		pAC->TxPort[Port][TX_PRIO_LOW].TxSLET.Done  = 0;    
		pAC->TxPort[Port][TX_PRIO_LOW].TxSLET.Put   = 0;
		pAC->TxPort[Port][TX_PRIO_LOW].TxSLET.HwPut = 0;
	}
	
	if (HW_FEATURE(pAC, HWF_WA_DEV_420)) {
		/*
		** It might be that we have to limit the RX buffers 
		** effectively passed to HW. Initialize the start
		** value in that case...
		*/
		NbrRxBuffersInHW = 0;
	}

	/*
	** TODO on dual net adapters we need to check if
	** StatusLETable need to be set...
	** 
	** pAC->StatusLETable.Done  = 0;
	** pAC->StatusLETable.Put   = 0;
	** pAC->StatusLETable.HwPut = 0;
	** SkGeY2InitPrefetchUnit(pAC, pAC->IoBase, Q_ST, &pAC->StatusLETable);
	*/

	/*
	** Initialize the LET in HW (enables also prefetch unit)
	*/
	SkGeY2InitPrefetchUnit(pAC, IoC,(Port == 0) ? Q_R1 : Q_R2,
			&pAC->RxPort[Port].RxLET);
	SkGeY2InitPrefetchUnit( pAC, IoC,(Port == 0) ? Q_XA1 : Q_XA2, 
			&pAC->TxPort[Port][TX_PRIO_LOW].TxALET);
	if (HW_SYNC_TX_SUPPORTED(pAC)) {
		SkGeY2InitPrefetchUnit( pAC, IoC, (Port == 0) ? Q_XS1 : Q_XS2,
				&pAC->TxPort[Port][TX_PRIO_HIGH].TxSLET);
	}


	/*
	** Using new values for the watermarks and the timer for
	** low latency optimization
	*/
	if (pAC->LowLatency) {
		SK_OUT8(IoC, STAT_FIFO_WM, 1);
		SK_OUT8(IoC, STAT_FIFO_ISR_WM, 1);
		SK_OUT32(IoC, STAT_LEV_TIMER_INI, 50);
		SK_OUT32(IoC, STAT_ISR_TIMER_INI, 10);
	}


	/*
	** Initialize the Port (MAC, PHY etc.)
	*/
	if (SkGeInitPort(pAC, IoC, Port)) {
		if (Port == 0) {
			printk("%s: SkGeInitPort A failed.\n",pAC->dev[0]->name);
		} else {
			printk("%s: SkGeInitPort B failed.\n",pAC->dev[1]->name);
		}
	}
	
	if (IS_GMAC(pAC)) {
		/* disable Rx GMAC FIFO Flush Mode */
		SK_OUT8(IoC, MR_ADDR(Port, RX_GMF_CTRL_T), (SK_U8) GMF_RX_F_FL_OFF);
	}

	/*
	** Initialize the MC addresses
	*/
	SkAddrMcUpdate(pAC,IoC, Port);

	SkMacRxTxEnable(pAC, IoC,Port);
				
	if (pAC->RxPort[Port].UseRxCsum) {
		SkGeRxCsum(pAC, IoC, Port, SK_TRUE);
	
		GET_RX_LE(pLE, &pAC->RxPort[Port].RxLET);
		RXLE_SET_STACS1(pLE, pAC->CsOfs1);
		RXLE_SET_STACS2(pLE, pAC->CsOfs2);
		RXLE_SET_CTRL(pLE, 0);

		RXLE_SET_OPC(pLE, OP_TCPSTART | HW_OWNER);
		FLUSH_OPC(pLE);
		if (Port == 0) {
			PrefetchReg=Y2_PREF_Q_ADDR(Q_R1,PREF_UNIT_PUT_IDX_REG);
		} else {
			PrefetchReg=Y2_PREF_Q_ADDR(Q_R2,PREF_UNIT_PUT_IDX_REG);
		}
		DWord = GET_PUT_IDX(&pAC->RxPort[Port].RxLET);
		SK_OUT32(IoC, PrefetchReg, DWord);
		UPDATE_HWPUT_IDX(&pAC->RxPort[Port].RxLET);
	}

	pAC->GIni.GP[Port].PState = SK_PRT_RUN;
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("<== SkY2PortStart()\n"));
}

/******************************************************************************
 *
 * Local Functions
 *
 *****************************************************************************/

/*****************************************************************************
 *
 *	InitPacketQueues - initialize SW settings of packet queues
 *
 * Description:
 *	This function will initialize the packet queues for a port.
 *
 * Returns: N/A
 */
static void InitPacketQueues(
SK_AC  *pAC,   /* pointer to adapter control context */
int     Port)  /* index of port to be initialized    */
{
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
		("==> InitPacketQueues(Port %c)\n", 'A' + Port));
	
	pAC->RxPort[Port].RxQ_working.pHead = NULL;
	pAC->RxPort[Port].RxQ_working.pTail = NULL;
	spin_lock_init(&pAC->RxPort[Port].RxQ_working.QueueLock);
	
	pAC->RxPort[Port].RxQ_waiting.pHead = NULL;
	pAC->RxPort[Port].RxQ_waiting.pTail = NULL;
	spin_lock_init(&pAC->RxPort[Port].RxQ_waiting.QueueLock);
	
	pAC->TxPort[Port][TX_PRIO_LOW].TxQ_free.pHead = NULL;
	pAC->TxPort[Port][TX_PRIO_LOW].TxQ_free.pTail = NULL;
	spin_lock_init(&pAC->TxPort[Port][TX_PRIO_LOW].TxQ_free.QueueLock);

	pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_working.pHead = NULL;
	pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_working.pTail = NULL;
	spin_lock_init(&pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_working.QueueLock);
	
	pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting.pHead = NULL;
	pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting.pTail = NULL;
	spin_lock_init(&pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting.QueueLock);
	
#if USE_SYNC_TX_QUEUE
	pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_working.pHead = NULL;
	pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_working.pTail = NULL;
	spin_lock_init(&pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_working.QueueLock);

	pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_waiting.pHead = NULL;
	pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_waiting.pTail = NULL;
	spin_lock_init(&pAC->TxPort[Port][TX_PRIO_LOW].TxSQ_waiting.QueueLock);
#endif
	
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
		("<== InitPacketQueues(Port %c)\n", 'A' + Port));
}	/* InitPacketQueues */

/*****************************************************************************
 *
 *	GiveTxBufferToHw - commits a previously allocated DMA area to HW
 *
 * Description:
 *	This functions gives transmit buffers to HW. If no list elements
 *	are available the buffers will be queued. 
 *
 * Notes:
 *       This function can run only once in a system at one time.
 *
 * Returns: N/A
 */
static void GiveTxBufferToHw(
SK_AC   *pAC,   /* pointer to adapter control context         */
SK_IOC   IoC,   /* I/O control context (address of registers) */
int      Port)  /* port index for which the buffer is used    */
{
	SK_HWLE         *pLE;
	SK_PACKET       *pSkPacket;
	SK_FRAG         *pFrag;
	SK_PKT_QUEUE    *pWorkQueue;   /* corresponding TX queue */
	SK_PKT_QUEUE    *pWaitQueue; 
	SK_LE_TABLE     *pLETab;       /* corresponding LETable  */ 
	SK_BOOL          SetOpcodePacketFlag;
	SK_U32           HighAddress;
	SK_U32           LowAddress;
	SK_U16           TcpSumStart; 
	SK_U16           TcpSumWrite;
	SK_U8            OpCode;
	SK_U8            Ctrl;
	unsigned long    Flags;
	unsigned long    LockFlag;
	int              Protocol;
#ifdef NETIF_F_TSO
	SK_U16           Mss;
	int              TcpOptLen;
	int              IpTcpLen;
#endif
#if defined(__x86_64__)
	long             cache0, cache1;
#endif

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
		("==> GiveTxBufferToHw\n"));

	if (IS_Q_EMPTY(&(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting))) {
		return;
	}

	spin_lock_irqsave(&pAC->TxQueueLock, LockFlag);

	/*
	** Initialize queue settings
	*/
	pWorkQueue = &(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_working);
	pWaitQueue = &(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting);
	pLETab     = &(pAC->TxPort[Port][TX_PRIO_LOW].TxALET);

	POP_FIRST_PKT_FROM_QUEUE(pWaitQueue, pSkPacket);
	while (pSkPacket != NULL) {
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
			("\tWe have a packet to send %p\n", pSkPacket));

		/* 
		** the first frag of a packet gets opcode OP_PACKET 
		*/
		SetOpcodePacketFlag	= SK_TRUE;
		pFrag			= pSkPacket->pFrag;

		/* 
		** fill list elements with data from fragments 
		*/
		while (pFrag != NULL) {
			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
				("\tGet LE\n"));
#ifdef NETIF_F_TSO
			Mss = skb_shinfo(pSkPacket->pMBuf)->tso_size;
			if (Mss) {
				TcpOptLen = ((pSkPacket->pMBuf->h.th->doff - 5) * 4);
				IpTcpLen  = ((pSkPacket->pMBuf->nh.iph->ihl * 4) + 
						sizeof(struct tcphdr));
				Mss += (TcpOptLen + IpTcpLen + C_LEN_ETHERMAC_HEADER);
			}
			if (pLETab->Bmu.RxTx.MssValue != Mss) {
				pLETab->Bmu.RxTx.MssValue = Mss;
				/* Take a new LE for TSO from the table */
				GET_TX_LE(pLE, pLETab);

#if 0
				if(pSkPacket->VlanId) {
					TXLE_SET_OPC(pLE, OP_LRGLENVLAN | HW_OWNER);
					TXLE_SET_VLAN(pLE, pSkPacket->VlanId);
					pSkPacket->VlanId = 0;
					Ctrl |= INS_VLAN;
				} else {
#endif
					TXLE_SET_OPC(pLE, OP_LRGLEN | HW_OWNER);
#if 0
				}
#endif
				/* set maximum segment size for new packet */
				TXLE_SET_LSLEN(pLE, pLETab->Bmu.RxTx.MssValue);
				FLUSH_OPC(pLE) ;
			}
#endif
			GET_TX_LE(pLE, pLETab);
			Ctrl = 0;

			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
				("\tGot empty LE %p idx %d\n", pLE, GET_PUT_IDX(pLETab)));

			SK_DBG_DUMP_TX_LE(pLE);

			LowAddress  = (SK_U32) (pFrag->pPhys & 0xffffffff);
			HighAddress = (SK_U32) (pFrag->pPhys >> 32);
			if (HighAddress != pLETab->BufHighAddr) {
				/* set opcode high part of the address in one LE */
				OpCode = OP_ADDR64 | HW_OWNER;
	
				/* Set now the 32 high bits of the address */
				TXLE_SET_ADDR( pLE, HighAddress);
	
				/* Set the opcode into the LE */
				TXLE_SET_OPC(pLE, OpCode);
	
				/* Flush the LE to memory */
				FLUSH_OPC(pLE);
	
				/* remember the HighAddress we gave to the Hardware */
				pLETab->BufHighAddr = HighAddress;
				
				/* get a new LE because we filled one with high address */
				GET_TX_LE(pLE, pLETab);
			}
	
			/*
			** TCP checksum offload
			*/
			if ((pSkPacket->pMBuf->ip_summed == CHECKSUM_HW) && 
			    (SetOpcodePacketFlag         == SK_TRUE)) {
				Protocol = ((SK_U8)pSkPacket->pMBuf->data[C_OFFSET_IPPROTO] & 0xff);
				/* if (Protocol & C_PROTO_ID_IP) { Ctrl = 0; } */ 
				if (Protocol & C_PROTO_ID_TCP) {
					Ctrl = CALSUM | WR_SUM | INIT_SUM | LOCK_SUM;
					/* TCP Checksum Calculation Start Position */
					TcpSumStart = C_LEN_ETHERMAC_HEADER + IP_HDR_LEN;
					/* TCP Checksum Write Position */
					TcpSumWrite = TcpSumStart + TCP_CSUM_OFFS;
				} else {
					Ctrl = UDPTCP | CALSUM | WR_SUM | INIT_SUM | LOCK_SUM;
					/* TCP Checksum Calculation Start Position */
					TcpSumStart = ETHER_MAC_HDR_LEN + IP_HDR_LEN;
					/* UDP Checksum Write Position */
					TcpSumWrite = TcpSumStart + UDP_CSUM_OFFS;
				}
	
				if ((Ctrl) && (pLETab->Bmu.RxTx.TcpWp != TcpSumWrite)) {
					/* Update the last value of the write position */
					pLETab->Bmu.RxTx.TcpWp = TcpSumWrite;
	
					/* Set the Lock field for this LE: */
					/* Checksum calculation for one packet only */
					TXLE_SET_LCKCS(pLE, 1);
	
					/* Set the start position for checksum. */
					TXLE_SET_STACS(pLE, TcpSumStart);
	
					/* Set the position where the checksum will be writen */
					TXLE_SET_WRICS(pLE, TcpSumWrite);
	
					/* Set the initial value for checksum */
					/* PseudoHeader CS passed from Linux -> 0! */
					TXLE_SET_INICS(pLE, 0);
	
					/* Set the opcode for tcp checksum */
					TXLE_SET_OPC(pLE, OP_TCPLISW | HW_OWNER);
	
					/* Flush the LE to memory */
					FLUSH_OPC(pLE);
	
					/* get a new LE because we filled one with data for checksum */
					GET_TX_LE(pLE, pLETab);
				}
			} /* end TCP offload handling */
	
			TXLE_SET_ADDR(pLE, LowAddress);
			TXLE_SET_LEN(pLE, pFrag->FragLen);
	
			if (SetOpcodePacketFlag){
#ifdef NETIF_F_TSO
				if (Mss) {
					OpCode = OP_LARGESEND | HW_OWNER;
				} else {
#endif
					OpCode = OP_PACKET| HW_OWNER;
#ifdef NETIF_F_TSO
				}
#endif
				SetOpcodePacketFlag = SK_FALSE;
			} else {
				/* Follow packet in a sequence has always OP_BUFFER */
				OpCode = OP_BUFFER | HW_OWNER;
			}
	
			pFrag = pFrag->pNext;
			if (pFrag == NULL) {
				/* mark last fragment */
				Ctrl |= EOP;
			}
			TXLE_SET_CTRL(pLE, Ctrl);
			TXLE_SET_OPC(pLE, OpCode);
			FLUSH_OPC(pLE);
			SK_DBG_DUMP_TX_LE(pLE);
		}
	
		/* 
		** Remember next LE for tx complete 
		*/
		pSkPacket->NextLE = GET_PUT_IDX(pLETab);
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
			("\tNext LE for pkt %p is %d\n", pSkPacket, pSkPacket->NextLE));

		/* 
		** Add packet to working packets queue 
		*/
		PUSH_PKT_AS_LAST_IN_QUEUE(pWorkQueue, pSkPacket);

		/* 
		** give transmit start command
		*/
		if (HW_FEATURE(pAC, HWF_WA_DEV_42)) {
			spin_lock(&pAC->SetPutIndexLock);
			SkGeY2SetPutIndex(pAC, pAC->IoBase, Y2_PREF_Q_ADDR(Q_XA1,0), &pAC->TxPort[0][0].TxALET);
			spin_unlock(&pAC->SetPutIndexLock);
		} else {
			/* write put index */
			if (Port == 0) { 
				SK_OUT32(pAC->IoBase, 
					Y2_PREF_Q_ADDR(Q_XA1,PREF_UNIT_PUT_IDX_REG), 
					GET_PUT_IDX(&pAC->TxPort[0][0].TxALET)); 
				UPDATE_HWPUT_IDX(&pAC->TxPort[0][0].TxALET);
			} else {
				SK_OUT32(pAC->IoBase, 
					Y2_PREF_Q_ADDR(Q_XA2, PREF_UNIT_PUT_IDX_REG), 
					GET_PUT_IDX(&pAC->TxPort[1][0].TxALET)); 
				UPDATE_HWPUT_IDX(&pAC->TxPort[1][0].TxALET);
			}
		}
	
		if (IS_Q_EMPTY(&(pAC->TxPort[Port][TX_PRIO_LOW].TxAQ_waiting))) {
			break; /* get out of while */
		}
		POP_FIRST_PKT_FROM_QUEUE(pWaitQueue, pSkPacket);
	} /* while (pSkPacket != NULL) */

	spin_unlock_irqrestore(&pAC->TxQueueLock, LockFlag);

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
		("<== GiveTxBufferToHw\n"));
	return;
}	/* GiveTxBufferToHw */

/***********************************************************************
 *
 *	GiveRxBufferToHw - commits a previously allocated DMA area to HW
 *
 * Description:
 *	This functions gives receive buffers to HW. If no list elements
 *	are available the buffers will be queued. 
 *
 * Notes:
 *       This function can run only once in a system at one time.
 *
 * Returns: N/A
 */
static void GiveRxBufferToHw(
SK_AC      *pAC,      /* pointer to adapter control context         */
SK_IOC      IoC,      /* I/O control context (address of registers) */
int         Port,     /* port index for which the buffer is used    */
SK_PACKET  *pPacket)  /* receive buffer(s)                          */
{
	SK_HWLE         *pLE;
	SK_LE_TABLE     *pLETab;
	SK_BOOL         Done = SK_FALSE;  /* at least on LE changed? */
	SK_U32          LowAddress;
	SK_U32          HighAddress;
	SK_U32          PrefetchReg;      /* register for Put index  */
	unsigned        NumFree;
	unsigned        Required;
	unsigned long   Flags;
#if defined(__x86_64__)
	long            cache0, cache1;
#endif

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
	("==> GiveRxBufferToHw(Port %c, Packet %p)\n", 'A' + Port, pPacket));

	pLETab	= &pAC->RxPort[Port].RxLET;

	if (Port == 0) {
		PrefetchReg = Y2_PREF_Q_ADDR(Q_R1, PREF_UNIT_PUT_IDX_REG);
	} else {
		PrefetchReg = Y2_PREF_Q_ADDR(Q_R2, PREF_UNIT_PUT_IDX_REG);
	} 

	if (pPacket != NULL) {
		/*
		** For the time being, we have only one packet passed
		** to this function which might be changed in future!
		*/
		PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
	}

	/* 
	** now pPacket contains the very first waiting packet
	*/
	POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
	while (pPacket != NULL) {
		if (HW_FEATURE(pAC, HWF_WA_DEV_420)) {
			if (NbrRxBuffersInHW >= MAX_NBR_RX_BUFFERS_IN_HW) {
				PUSH_PKT_AS_FIRST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
				SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
					("<== GiveRxBufferToHw()\n"));
				return;
			} 
			NbrRxBuffersInHW++;
		}

		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
			("Try to add packet %p\n", pPacket));

		/* 
		** Check whether we have enough listelements:
		**
		** we have to take into account that each fragment 
		** may need an additional list element for the high 
		** part of the address here I simplified it by 
		** using MAX_FRAG_OVERHEAD maybe it's worth to split 
		** this constant for Rx and Tx or to calculate the
		** real number of needed LE's
		*/
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
			("\tNum %d Put %d Done %d Free %d %d\n",
			pLETab->Num, pLETab->Put, pLETab->Done,
			NUM_FREE_LE_IN_TABLE(pLETab),
			(NUM_FREE_LE_IN_TABLE(pLETab))));

		Required = pPacket->NumFrags + MAX_FRAG_OVERHEAD;
		NumFree = NUM_FREE_LE_IN_TABLE(pLETab);
		if (NumFree) {
			NumFree--;
		}

		if (Required > NumFree ) {
			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
				SK_DBGCAT_DRV_RX_PROGRESS | SK_DBGCAT_DRV_ERROR,
				("\tOut of LEs have %d need %d\n",
				NumFree, Required));

			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
				("\tWaitQueue starts with packet %p\n", pPacket));
			PUSH_PKT_AS_FIRST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
			if (Done) {
				/*
				** write Put index to BMU or Polling Unit and make the LE's
				** available for the hardware
				*/
				SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
					("\tWrite new Put Idx\n"));

				SK_OUT32(IoC, PrefetchReg, GET_PUT_IDX(pLETab));
				UPDATE_HWPUT_IDX(pLETab);
			}
			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
				("<== GiveRxBufferToHw()\n"));
			return;
		} else {
			if (!AllocAndMapRxBuffer(pAC, pPacket, Port)) {
				/*
				** Failure while allocating sk_buff might
				** be due to temporary short of resources
				** Maybe next time buffers are available.
				** Until this, the packet remains in the 
				** RX waiting queue...
				*/
				SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
					SK_DBGCAT_DRV_RX_PROGRESS | SK_DBGCAT_DRV_ERROR,
					("Failed to allocate Rx buffer\n"));

				SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
					("WaitQueue starts with packet %p\n", pPacket));
				PUSH_PKT_AS_FIRST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
				if (Done) {
					/*
					** write Put index to BMU or Polling 
					** Unit and make the LE's
					** available for the hardware
					*/
					SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
						("\tWrite new Put Idx\n"));
	
					SK_OUT32(IoC, PrefetchReg, GET_PUT_IDX(pLETab));
					UPDATE_HWPUT_IDX(pLETab);
				}
				SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
					("<== GiveRxBufferToHw()\n"));
				return;
			}
		}
		Done = SK_TRUE;

		LowAddress = (SK_U32) (pPacket->pFrag->pPhys & 0xffffffff);
		HighAddress = (SK_U32) (pPacket->pFrag->pPhys >> 32);
		if (HighAddress != pLETab->BufHighAddr) {
			/* get a new LE for high address */
			GET_RX_LE(pLE, pLETab);

			/* Set now the 32 high bits of the address */
			RXLE_SET_ADDR(pLE, HighAddress);

			/* Set the control bits of the address */
			RXLE_SET_CTRL(pLE, 0);

			/* Set the opcode into the LE */
			RXLE_SET_OPC(pLE, (OP_ADDR64 | HW_OWNER));

			/* Flush the LE to memory */
			FLUSH_OPC(pLE);

			/* remember the HighAddress we gave to the Hardware */
			pLETab->BufHighAddr = HighAddress;
		}

		/*
		** Fill data into listelement
		*/
		GET_RX_LE(pLE, pLETab);
		RXLE_SET_ADDR(pLE, LowAddress);
		RXLE_SET_LEN(pLE, pPacket->pFrag->FragLen);
		RXLE_SET_CTRL(pLE, 0);
		RXLE_SET_OPC(pLE, (OP_PACKET | HW_OWNER));
		FLUSH_OPC(pLE);

		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
			("=== LE filled\n"));

		SK_DBG_DUMP_RX_LE(pLE);

		/* 
		** Remember next LE for rx complete 
		*/
		pPacket->NextLE = GET_PUT_IDX(pLETab);

		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
			("\tPackets Next LE is %d\n", pPacket->NextLE));

		/* 
		** Add packet to working receive buffer queue and get
		** any next packet out of the waiting queue
		*/
		PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_working, pPacket);
		if (IS_Q_EMPTY(&(pAC->RxPort[Port].RxQ_waiting))) {
			break; /* get out of while processing */
		}
		POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pPacket);
	}

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
		("\tWaitQueue is empty\n"));

	if (Done) {
		/*
		** write Put index to BMU or Polling Unit and make the LE's
		** available for the hardware
		*/
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
			("\tWrite new Put Idx\n"));

		/* Speed enhancement for a2 chipsets */
		if (HW_FEATURE(pAC, HWF_WA_DEV_42)) {
			spin_lock_irqsave(&pAC->SetPutIndexLock, Flags);
			SkGeY2SetPutIndex(pAC, pAC->IoBase, Y2_PREF_Q_ADDR(Q_R1,0), pLETab);
			spin_unlock_irqrestore(&pAC->SetPutIndexLock, Flags);
		} else {
			/* write put index */
			if (Port == 0) { 
				SK_OUT32(IoC, 
					Y2_PREF_Q_ADDR(Q_R1, PREF_UNIT_PUT_IDX_REG), 
					GET_PUT_IDX(pLETab)); 
			} else {
				SK_OUT32(IoC, 
					Y2_PREF_Q_ADDR(Q_R2, PREF_UNIT_PUT_IDX_REG), 
					GET_PUT_IDX(pLETab)); 
			}

			/* Update put index */
			UPDATE_HWPUT_IDX(pLETab);
		}
	}

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
		("<== GiveRxBufferToHw()\n"));
}       /* GiveRxBufferToHw */

/***********************************************************************
 *
 *	FillReceiveTableYukon2 - map any waiting RX buffers to HW
 *
 * Description:
 *	If the list element table contains more empty elements than 
 *	specified this function tries to refill them.
 *
 * Notes:
 *       This function can run only once per port in a system at one time.
 *
 * Returns: N/A
 */
static void FillReceiveTableYukon2(
SK_AC   *pAC,   /* pointer to adapter control context */
SK_IOC   IoC,   /* I/O control context                */
int      Port)  /* port index of RX                   */
{
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
		("==> FillReceiveTableYukon2 (Port %c)\n", 'A' + Port));

	if (NUM_FREE_LE_IN_TABLE(&pAC->RxPort[Port].RxLET) >
		pAC->MaxUnusedRxLeWorking) {

		/* 
		** Give alle waiting receive buffers down 
		** The queue holds all RX packets that
		** need a fresh allocation of the sk_buff.
		*/
		if (pAC->RxPort[Port].RxQ_waiting.pHead != NULL) {
			SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
			("Waiting queue is not empty -> give it to HW"));
			GiveRxBufferToHw(pAC, IoC, Port, NULL);
		}
	}

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
		("<== FillReceiveTableYukon2 ()\n"));
}	/* FillReceiveTableYukon2 */

/******************************************************************************
 *
 *
 *	HandleReceives - will pass any ready RX packet to kernel
 *
 * Description:
 *	This functions handles a received packet. It checks wether it is
 *	valid, updates the receive list element table and gives the receive
 *	buffer to Linux
 *
 * Notes:
 *	This function can run only once per port at one time in the system.
 *
 * Returns: N/A
 */
static SK_BOOL HandleReceives(
SK_AC  *pAC,          /* adapter control context                     */
int     Port,         /* port on which a packet has been received    */
SK_U16  Len,          /* number of bytes which was actually received */
SK_U32  FrameStatus,  /* MAC frame status word                       */
SK_U16  Tcp1,         /* first hw checksum                           */
SK_U16  Tcp2,         /* second hw checksum                          */
SK_U32  Tist,         /* timestamp                                   */
SK_U16  Vlan)         /* Vlan Id                                     */
{

	SK_PACKET       *pSkPacket;
	SK_LE_TABLE     *pLETab;
	SK_MBUF         *pRlmtMbuf;  /* buffer for giving RLMT frame */
	struct sk_buff  *pMsg;       /* ptr to message holding frame */
#ifdef __ia64__
	struct sk_buff  *pNewMsg;    /* used when IP aligning        */
#endif
		
#ifdef CONFIG_SK98LIN_NAPI
	SK_BOOL         SlowPathLock = SK_FALSE;
#else
	SK_BOOL         SlowPathLock = SK_TRUE;
#endif
	SK_BOOL         IsGoodPkt;
	SK_BOOL         IsBc;
	SK_BOOL         IsMc;
	SK_EVPARA       EvPara;      /* an event parameter union     */
	SK_I16          LenToFree;   /* must be signed integer       */

	unsigned long   Flags;       /* for spin lock                */
	unsigned int    RlmtNotifier;
	unsigned short  Type;
	int             IpFrameLength;
	int             FrameLength; /* total length of recvd frame  */
	int             HeaderLength;
	int             NumBytes; 
	int             Result;
	int             Offset = 0;

#ifdef Y2_SYNC_CHECK
	SK_U16		MyTcp;
#endif

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
		("==> HandleReceives (Port %c)\n", 'A' + Port));

	/* 
	** initialize vars for selected port 
	*/
	pLETab = &pAC->RxPort[Port].RxLET;

	/* 
	** check whether we want to receive this packet 
	*/
	SK_Y2_RXSTAT_CHECK_PKT(Len, FrameStatus, IsGoodPkt);

	/*
	** Remember length to free (in case of RxBuffer overruns;
	** unlikely, but might happen once in a while)
	*/
	LenToFree = (SK_I16) Len;

	/* 
	** maybe we put these two checks into the SK_RXDESC_CHECK_PKT macro too 
	*/
	if (Len > pAC->RxBufSize) {
		IsGoodPkt = SK_FALSE;
	}

	/*
	** take first receive buffer out of working queue 
	*/
	POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_working, pSkPacket);
	if (pSkPacket == NULL) {
 		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
			SK_DBGCAT_DRV_ERROR,
			("Packet not available. NULL pointer.\n"));
		return(SK_TRUE);
	}

	if (HW_FEATURE(pAC, HWF_WA_DEV_420)) {
		NbrRxBuffersInHW--;
	}

	/* 
	** Verify the received length of the frame! Note that having 
	** multiple RxBuffers being aware of one single receive packet
	** (one packet spread over multiple RxBuffers) is not supported 
	** by this driver!
	*/
	if ((Len > pAC->RxBufSize) || (Len > (SK_U16) pSkPacket->PacketLen)) {
		IsGoodPkt = SK_FALSE;
	}

	/* 
	** Reset own bit in LE's between old and new Done index
	** This is not really necessary but makes debugging easier 
	*/
	CLEAR_LE_OWN_FROM_DONE_TO(pLETab, pSkPacket->NextLE);

	/* 
	** Free the list elements for new Rx buffers 
	*/
	SET_DONE_INDEX(pLETab, pSkPacket->NextLE);
	pMsg = pSkPacket->pMBuf;
	FrameLength = Len;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
		("Received frame of length %d on port %d\n",FrameLength, Port));

	if (!IsGoodPkt) {
		/* 
		** release the DMA mapping 
		*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5)
 		pci_dma_sync_single(pAC->PciDev,
				(dma_addr_t) pSkPacket->pFrag->pPhys,
				pSkPacket->pFrag->FragLen,
				PCI_DMA_FROMDEVICE);

#else
		pci_dma_sync_single_for_cpu(pAC->PciDev,
				(dma_addr_t) pSkPacket->pFrag->pPhys,
				pSkPacket->pFrag->FragLen,
				PCI_DMA_FROMDEVICE);
#endif

		DEV_KFREE_SKB_ANY(pSkPacket->pMBuf);
		PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pSkPacket);
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
			("<== HandleReceives (Port %c)\n", 'A' + Port));

		/*
		** Sanity check for RxBuffer overruns...
		*/
		LenToFree = LenToFree - (pSkPacket->pFrag->FragLen);
		while (LenToFree > 0) {
			POP_FIRST_PKT_FROM_QUEUE(&pAC->RxPort[Port].RxQ_working, pSkPacket);
			if (HW_FEATURE(pAC, HWF_WA_DEV_420)) {
				NbrRxBuffersInHW--;
			}
			CLEAR_LE_OWN_FROM_DONE_TO(pLETab, pSkPacket->NextLE);
			SET_DONE_INDEX(pLETab, pSkPacket->NextLE);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5)
			pci_dma_sync_single(pAC->PciDev,
					(dma_addr_t) pSkPacket->pFrag->pPhys,
					pSkPacket->pFrag->FragLen,
					PCI_DMA_FROMDEVICE);
#else
			pci_dma_sync_single_for_device(pAC->PciDev,
					(dma_addr_t) pSkPacket->pFrag->pPhys,
					pSkPacket->pFrag->FragLen,
					PCI_DMA_FROMDEVICE); 
#endif

			DEV_KFREE_SKB_ANY(pSkPacket->pMBuf);
			PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pSkPacket);
			LenToFree = LenToFree - ((SK_I16)(pSkPacket->pFrag->FragLen));
			
			SK_DBG_MSG(pAC, SK_DBGMOD_DRV,
				SK_DBGCAT_DRV_RX_PROGRESS | SK_DBGCAT_DRV_ERROR,
				("<==HandleReceives (Port %c) drop faulty len pkt(2)\n",'A'+Port));
		}
		return(SK_TRUE);
	} else {
		/* 
		** Release the DMA mapping 
		*/
		pci_unmap_single(pAC->PciDev,
				 pSkPacket->pFrag->pPhys,
				 pAC->RxBufSize,
				 PCI_DMA_FROMDEVICE);

		skb_put(pMsg, FrameLength);		/* set message len */
		pMsg->ip_summed = CHECKSUM_NONE;	/* initial default */

#ifdef Y2_SYNC_CHECK
		pAC->FramesWithoutSyncCheck++;
		if (pAC->FramesWithoutSyncCheck > Y2_RESYNC_WATERMARK) {
			if ((Tcp1 != 1) && (Tcp2 != 0)) {
				pAC->FramesWithoutSyncCheck = 0;
				MyTcp = (SK_U16) SkCsCalculateChecksum(
						&pMsg->data[14],
						FrameLength - 14);
				if (MyTcp != Tcp1) {
					/* Queue port reset event */
					SkLocalEventQueue(pAC, SKGE_DRV,
					SK_DRV_RECOVER,Port,-1,SK_FALSE);
				}
			}
		}
#endif

		if (pAC->RxPort[Port].UseRxCsum) {
			Type = ntohs(*((short*)&pMsg->data[12]));
			if (Type == 0x800) {
				*((char *)&(IpFrameLength)) = pMsg->data[16];
				*(((char *)&(IpFrameLength))+1) = pMsg->data[17];
				IpFrameLength = ntohs(IpFrameLength);
				HeaderLength  = FrameLength - IpFrameLength;
				if (HeaderLength == 0xe) {
					Result = 
					    SkCsGetReceiveInfo(pAC,&pMsg->data[14],Tcp1,Tcp2, Port);
					if ((Result == SKCS_STATUS_IP_FRAGMENT) ||
					    (Result == SKCS_STATUS_IP_CSUM_OK)  ||
					    (Result == SKCS_STATUS_TCP_CSUM_OK) ||
					    (Result == SKCS_STATUS_UDP_CSUM_OK)) {
						pMsg->ip_summed = CHECKSUM_UNNECESSARY;
					} else if ((Result == SKCS_STATUS_TCP_CSUM_ERROR)    ||
					           (Result == SKCS_STATUS_UDP_CSUM_ERROR)    ||
					           (Result == SKCS_STATUS_IP_CSUM_ERROR_UDP) ||
					           (Result == SKCS_STATUS_IP_CSUM_ERROR_TCP) ||
					           (Result == SKCS_STATUS_IP_CSUM_ERROR)) {
						SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
							SK_DBGCAT_DRV_RX_PROGRESS | SK_DBGCAT_DRV_ERROR,
							("skge: CRC error. Frame dropped!\n"));
						DEV_KFREE_SKB_ANY(pMsg);
						PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pSkPacket);
						SK_DBG_MSG(pAC,SK_DBGMOD_DRV,SK_DBGCAT_DRV_RX_PROGRESS,
							("<==HandleReceives(Port %c)\n",'A'+Port));
						return(SK_TRUE);
					} else {
						pMsg->ip_summed = CHECKSUM_NONE;
					}
				} /* end if (HeaderLength == valid) */
			} /* end if (Type == 0x800) -> IP frame */
		} /* end if (pRxPort->UseRxCsum) */
		
		SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
			SK_DBGCAT_DRV_RX_PROGRESS,("V"));
		RlmtNotifier = SK_RLMT_RX_PROTOCOL;

		IsBc = (FrameStatus & GMR_FS_BC) ? SK_TRUE : SK_FALSE;
		SK_RLMT_PRE_LOOKAHEAD(pAC,Port,FrameLength,
					IsBc,&Offset,&NumBytes);
		if (NumBytes != 0) {
			IsMc = (FrameStatus & GMR_FS_MC) ? SK_TRUE : SK_FALSE;
			SK_RLMT_LOOKAHEAD(pAC,Port,&pMsg->data[Offset],
						IsBc,IsMc,&RlmtNotifier);
		}

		if (RlmtNotifier == SK_RLMT_RX_PROTOCOL) {
			SK_DBG_MSG(NULL,SK_DBGMOD_DRV,
				SK_DBGCAT_DRV_RX_PROGRESS,("W"));
			if ((Port == pAC->ActivePort)||(pAC->RlmtNets == 2)) {
				/* send up only frames from active port */
				SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
					SK_DBGCAT_DRV_RX_PROGRESS,("U"));
#ifdef xDEBUG
				DumpMsg(pMsg, "Rx");
#endif
				SK_PNMI_CNT_RX_OCTETS_DELIVERED(pAC,
					FrameLength, Port);
#ifdef __ia64__
				pNewMsg = alloc_skb(pMsg->len, GFP_ATOMIC);
				skb_reserve(pNewMsg, 2); /* to align IP */
				SK_MEMCPY(pNewMsg->data,pMsg->data,pMsg->len);
				pNewMsg->ip_summed = pMsg->ip_summed;
				pNewMsg->len = pMsg->len;
				DEV_KFREE_SKB_ANY(pMsg);
				pMsg = pNewMsg;
#endif
				pMsg->dev = pAC->dev[Port];
				pMsg->protocol = eth_type_trans(pMsg,
					pAC->dev[Port]);
				netif_rx(pMsg);
				pAC->dev[Port]->last_rx = jiffies;
			} else { /* drop frame */
				SK_DBG_MSG(NULL,SK_DBGMOD_DRV,
					SK_DBGCAT_DRV_RX_PROGRESS,("D"));
				DEV_KFREE_SKB_ANY(pMsg);
			}
		} else { /* This is an RLMT-packet! */
			SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
				SK_DBGCAT_DRV_RX_PROGRESS,("R"));
			pRlmtMbuf = SkDrvAllocRlmtMbuf(pAC,
				pAC->IoBase, FrameLength);
			if (pRlmtMbuf != NULL) {
				pRlmtMbuf->pNext = NULL;
				pRlmtMbuf->Length = FrameLength;
				pRlmtMbuf->PortIdx = Port;
				EvPara.pParaPtr = pRlmtMbuf;
				SK_MEMCPY((char*)(pRlmtMbuf->pData),
				          (char*)(pMsg->data),FrameLength);

				if (SlowPathLock == SK_TRUE) {
					spin_lock_irqsave(&pAC->SlowPathLock, Flags);
					SkEventQueue(pAC, SKGE_RLMT,
						SK_RLMT_PACKET_RECEIVED,
						EvPara);
					pAC->CheckQueue = SK_TRUE;
					spin_unlock_irqrestore(&pAC->SlowPathLock, Flags);
				} else {
					SkEventQueue(pAC, SKGE_RLMT,
						SK_RLMT_PACKET_RECEIVED,
						EvPara);
					pAC->CheckQueue = SK_TRUE;
				}

				SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
					SK_DBGCAT_DRV_RX_PROGRESS,("Q"));
			}
			if (pAC->dev[Port]->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
#ifdef __ia64__
				pNewMsg = alloc_skb(pMsg->len, GFP_ATOMIC);
				skb_reserve(pNewMsg, 2); /* to align IP */
				SK_MEMCPY(pNewMsg->data,pMsg->data,pMsg->len);
				pNewMsg->ip_summed = pMsg->ip_summed;
				pNewMsg->len = pMsg->len;
				DEV_KFREE_SKB_ANY(pMsg);
				pMsg = pNewMsg;
#endif
				pMsg->dev = pAC->dev[Port];
				pMsg->protocol = eth_type_trans(pMsg,pAC->dev[Port]);
				netif_rx(pMsg);
				pAC->dev[Port]->last_rx = jiffies;
			} else {
				DEV_KFREE_SKB_ANY(pMsg);
			}
		} /* if packet for rlmt */
		PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[Port].RxQ_waiting, pSkPacket);
	} /* end if-else (IsGoodPkt) */

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
		("<== HandleReceives (Port %c)\n", 'A' + Port));
	return(SK_TRUE);

}	/* HandleReceives */

/***********************************************************************
 *
 * 	CheckForSendComplete - Frees any freeable Tx bufffer 
 *
 * Description:
 *	This function checks the queues of a port for completed send
 *	packets and returns these packets back to the OS.
 *
 * Notes:
 *	This function can run simultaneously for both ports if
 *	the OS function OSReturnPacket() can handle this,
 *
 *	Such a send complete does not mean, that the packet is really
 *	out on the wire. We just know that the adapter has copied it
 *	into its internal memory and the buffer in the systems memory
 *	is no longer needed.
 *
 * Returns: N/A
 */
static void CheckForSendComplete(
SK_AC         *pAC,     /* pointer to adapter control context  */
SK_IOC         IoC,     /* I/O control context                 */
int            Port,    /* port index                          */
SK_PKT_QUEUE  *pPQ,     /* tx working packet queue to check    */
SK_LE_TABLE   *pLETab,  /* corresponding list element table    */
unsigned int   Done)    /* done index reported for this LET    */
{
	SK_PACKET       *pSkPacket;
	SK_PKT_QUEUE     SendCmplPktQ = { NULL, NULL, SPIN_LOCK_UNLOCKED };
	SK_BOOL          DoWakeQueue  = SK_FALSE;
	unsigned long    Flags;
	unsigned         Put;
	
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
		("==> CheckForSendComplete(Port %c)\n", 'A' + Port));

	/* 
	** Reset own bit in LE's between old and new Done index
	** This is not really necessairy but makes debugging easier 
	*/
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
		("Clear Own Bits in TxTable from %d to %d\n",
		pLETab->Done, (Done == 0) ?
		NUM_LE_IN_TABLE(pLETab) :
		(Done - 1)));

	spin_lock_irqsave(&(pPQ->QueueLock), Flags);

	CLEAR_LE_OWN_FROM_DONE_TO(pLETab, Done);

	Put = GET_PUT_IDX(pLETab);

	/* 
	** Check whether some packets have been completed 
	*/
	PLAIN_POP_FIRST_PKT_FROM_QUEUE(pPQ, pSkPacket);
	while (pSkPacket != NULL) {
		
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
			("Check Completion of Tx packet %p\n", pSkPacket));
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
			("Put %d NewDone %d NextLe of Packet %d\n", Put, Done,
			pSkPacket->NextLE));

		if ((Put > Done) &&
			((pSkPacket->NextLE > Put) || (pSkPacket->NextLE <= Done))) {
			PLAIN_PUSH_PKT_AS_LAST_IN_QUEUE(&SendCmplPktQ, pSkPacket);
			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
				("Packet finished (a)\n"));
		} else if ((Done > Put) &&
			(pSkPacket->NextLE > Put) && (pSkPacket->NextLE <= Done)) {
			PLAIN_PUSH_PKT_AS_LAST_IN_QUEUE(&SendCmplPktQ, pSkPacket);
			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
				("Packet finished (b)\n"));
		} else if ((Done == TXA_MAX_LE-1) && (Put == 0) && (pSkPacket->NextLE == 0)) {
			PLAIN_PUSH_PKT_AS_LAST_IN_QUEUE(&SendCmplPktQ, pSkPacket);
			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
				("Packet finished (b)\n"));
			DoWakeQueue = SK_TRUE;
		} else if (Done == Put) {
			/* all packets have been sent */
			PLAIN_PUSH_PKT_AS_LAST_IN_QUEUE(&SendCmplPktQ, pSkPacket);
			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
				("Packet finished (c)\n"));
		} else {
			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
				("Packet not yet finished\n"));
			PLAIN_PUSH_PKT_AS_FIRST_IN_QUEUE(pPQ, pSkPacket);
			break;
		}
		PLAIN_POP_FIRST_PKT_FROM_QUEUE(pPQ, pSkPacket);
	}
	spin_unlock_irqrestore(&(pPQ->QueueLock), Flags);

	/* 
	** Set new done index in list element table
	*/
	SET_DONE_INDEX(pLETab, Done);
	 
	/*
	** All TX packets that are send complete should be added to
	** the free queue again for new sents to come
	*/
	pSkPacket = SendCmplPktQ.pHead;
	while (pSkPacket != NULL) {
		while (pSkPacket->pFrag != NULL) {
			pci_unmap_page(pAC->PciDev,
					(dma_addr_t) pSkPacket->pFrag->pPhys,
					pSkPacket->pFrag->FragLen,
					PCI_DMA_FROMDEVICE);
			pSkPacket->pFrag = pSkPacket->pFrag->pNext;
		}

		DEV_KFREE_SKB_ANY(pSkPacket->pMBuf);
		pSkPacket->pMBuf	= NULL;
		pSkPacket = pSkPacket->pNext; /* get next packet */
	}

	/*
	** Append the available TX packets back to free queue
	*/
	if (SendCmplPktQ.pHead != NULL) { 
		spin_lock_irqsave(&(pAC->TxPort[Port][0].TxQ_free.QueueLock), Flags);
		if (pAC->TxPort[Port][0].TxQ_free.pTail != NULL) {
			pAC->TxPort[Port][0].TxQ_free.pTail->pNext = SendCmplPktQ.pHead;
			pAC->TxPort[Port][0].TxQ_free.pTail        = SendCmplPktQ.pTail;
			if (pAC->TxPort[Port][0].TxQ_free.pHead->pNext == NULL) {
				netif_wake_queue(pAC->dev[Port]);
			}
		} else {
			pAC->TxPort[Port][0].TxQ_free.pHead = SendCmplPktQ.pHead;
			pAC->TxPort[Port][0].TxQ_free.pTail = SendCmplPktQ.pTail; 
			netif_wake_queue(pAC->dev[Port]);
		}
		if (Done == Put) {
			netif_wake_queue(pAC->dev[Port]);
		}
		if (DoWakeQueue) {
			netif_wake_queue(pAC->dev[Port]);
			DoWakeQueue = SK_FALSE;
		}
		spin_unlock_irqrestore(&pAC->TxPort[Port][0].TxQ_free.QueueLock, Flags);
	}

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
		("<== CheckForSendComplete()\n"));

	return;
}	/* CheckForSendComplete */

/*****************************************************************************
 *
 *	UnmapAndFreeTxPktBuffer
 *
 * Description:
 *      This function free any allocated space of receive buffers
 *
 * Arguments:
 *      pAC - A pointer to the adapter context struct.
 *
 */
static void UnmapAndFreeTxPktBuffer(
SK_AC       *pAC,       /* pointer to adapter context             */
SK_PACKET   *pSkPacket,	/* pointer to port struct of ring to fill */
int          TxPort)    /* TX port index                          */
{
	SK_FRAG	 *pFrag = pSkPacket->pFrag;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
		("--> UnmapAndFreeTxPktBuffer\n"));

	while (pFrag != NULL) {
		pci_unmap_page(pAC->PciDev,
				(dma_addr_t) pFrag->pPhys,
				pFrag->FragLen,
				PCI_DMA_FROMDEVICE);
		pFrag = pFrag->pNext;
	}

	DEV_KFREE_SKB_ANY(pSkPacket->pMBuf);
	pSkPacket->pMBuf	= NULL;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_TX_PROGRESS,
		("<-- UnmapAndFreeTxPktBuffer\n"));
}

/*****************************************************************************
 *
 * 	HandleStatusLEs
 *
 * Description:
 *	This function checks for any new status LEs that may have been 
  *	received. Those status LEs may either be Rx or Tx ones.
 *
 * Returns:	N/A
 */
static SK_BOOL HandleStatusLEs(
#ifdef CONFIG_SK98LIN_NAPI
SK_AC *pAC,       /* pointer to adapter context   */
int   *WorkDone,  /* Done counter needed for NAPI */
int    WorkToDo)  /* ToDo counter for NAPI        */
#else
SK_AC *pAC)       /* pointer to adapter context   */
#endif
{
	int       DoneTxA[SK_MAX_MACS];
	int       DoneTxS[SK_MAX_MACS];
	int       Port;
	SK_BOOL   handledStatLE = SK_FALSE;
	SK_BOOL   NewDone       = SK_FALSE;
	SK_HWLE  *pLE;
	SK_U16    HighVal;
	SK_U32    LowVal;
	SK_U8     OpCode;
	int       i;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
		("==> HandleStatusLEs\n"));

	do {
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
			("Check next Own Bit of ST-LE[%d]: 0x%li \n",
			(pAC->StatusLETable.Done + 1) % NUM_LE_IN_TABLE(&pAC->StatusLETable),
			 OWN_OF_FIRST_LE(&pAC->StatusLETable)));

		while (OWN_OF_FIRST_LE(&pAC->StatusLETable) == HW_OWNER) {
			GET_ST_LE(pLE, &pAC->StatusLETable);
			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
				("Working on finished status LE[%d]:\n",
				GET_DONE_INDEX(&pAC->StatusLETable)));
			SK_DBG_DUMP_ST_LE(pLE);
			handledStatLE = SK_TRUE;
			OpCode = STLE_GET_OPC(pLE) & ~HW_OWNER;
			Port = STLE_GET_LINK(pLE);

#ifdef USE_TIST_FOR_RESET
			if (SK_ADAPTER_WAITING_FOR_TIST(pAC)) {
				/* do we just have a tist LE ? */
				if ((OpCode & OP_RXTIMESTAMP) == OP_RXTIMESTAMP) {
					for (i = 0; i < pAC->GIni.GIMacsFound; i++) {
						if (SK_PORT_WAITING_FOR_ANY_TIST(pAC, i)) {
							/* if a port is waiting for any tist it is done */
							SK_CLR_STATE_FOR_PORT(pAC, i);
							SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
								("Got any Tist on port %c (now 0x%X!!!)\n",
								'A' + i, pAC->AdapterResetState));
						}
						if (SK_PORT_WAITING_FOR_SPECIFIC_TIST(pAC, i)) {
							Y2_GET_TIST_LOW_VAL(pAC->IoBase, &LowVal);
							if ((pAC->MinTistHi != pAC->GIni.GITimeStampCnt) ||
								(pAC->MinTistLo < LowVal)) {
								/* time is up now */
								SK_CLR_STATE_FOR_PORT(pAC, i);
								SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
									("Got expected Tist on Port %c (now 0x%X)!!!\n",
									'A' + i, pAC->AdapterResetState));
#ifdef Y2_SYNC_CHECK
								pAC->FramesWithoutSyncCheck =
								Y2_RESYNC_WATERMARK;						
#endif
							} else {
								SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
									("Got Tist %l:%l on Port %c but still waiting\n",
									pAC->GIni.GITimeStampCnt, pAC->MinTistLo,
									'A' + i));
							}
						}
					}
#ifndef Y2_RECOVERY
					if (!SK_ADAPTER_WAITING_FOR_TIST(pAC)) {
						/* nobody needs tist anymore - turn it off */
						Y2_DISABLE_TIST(pAC->IoBase);
						SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
						("Turn off Tist !!!\n"));
					}
#endif
				} else if (OpCode == OP_TXINDEXLE) {
					/*
					 * change OpCode to notify the folowing code
					 * to ignore the done index from this LE
					 * unfortunately tist LEs will be generated only
					 * for RxStat LEs
					 * so in order to get a safe Done index for a
					 * port currently waiting for a tist we have to
					 * get the done index directly from the BMU
					 */
					OpCode = OP_MOD_TXINDEX;
					SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
						("Mark unusable TX_INDEX LE!!!\n"));
				} else {
					if (SK_PORT_WAITING_FOR_TIST(pAC, Port)) {
						SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP, 
							("Ignore LE 0x%X on Port %c!!!\n",
							OpCode, 'A' + Port));
						OpCode = OP_MOD_LE;
#ifdef Y2_LE_CHECK
						/* mark entries invalid */
						pAC->LastOpc = 0xFF;
						pAC->LastPort = 3;
#endif
					}
				}
			} /* if (SK_ADAPTER_WAITING_FOR_TIST(pAC)) */
#endif





#ifdef Y2_LE_CHECK
			if (pAC->LastOpc != 0xFF) {
				/* last opc is valid
				 * check if current opcode follows last opcode
				 */
				if ((((OpCode & OP_RXTIMESTAMP) == OP_RXTIMESTAMP) && (pAC->LastOpc != OP_RXSTAT)) ||
				    (((OpCode & OP_RXCHKS) == OP_RXCHKS) && (pAC->LastOpc != OP_RXTIMESTAMP)) ||
				    ((OpCode == OP_RXSTAT) && (pAC->LastOpc != OP_RXCHKS))) {

					/* opcode sequence broken
					 * current LE is invalid
					 */

					if (pAC->LastOpc == OP_RXTIMESTAMP) {
						/* force invalid checksum */
						pLE->St.StUn.StRxTCPCSum.RxTCPSum1 = 1;
						pLE->St.StUn.StRxTCPCSum.RxTCPSum2 = 0;
						OpCode = pAC->LastOpc = OP_RXCHKS;
						Port = pAC->LastPort;
					} else if (pAC->LastOpc == OP_RXCHKS) {
						/* force invalid frame */
						Port = pAC->LastPort;
						pLE->St.Stat.BufLen = 64;
						pLE->St.StUn.StRxStatWord = GMR_FS_CRC_ERR;
						OpCode = pAC->LastOpc = OP_RXSTAT;
#ifdef Y2_SYNC_CHECK
						/* force rx sync check */
						pAC->FramesWithoutSyncCheck = Y2_RESYNC_WATERMARK;
#endif
					} else if (pAC->LastOpc == OP_RXSTAT) {
						/* create dont care tist */
						pLE->St.StUn.StRxTimeStamp = 0;
						OpCode = pAC->LastOpc = OP_RXTIMESTAMP;
						/* dont know the port yet */
					} else {
#ifdef DEBUG
						SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
							("Unknown LastOpc %X for Timestamp on port %c.\n",
							pAC->LastOpc, Port));
#endif
					}
				}
			}
#endif

			switch (OpCode) {
			case OP_RXSTAT:
#ifdef Y2_RECOVERY
				pAC->LastOpc = OP_RXSTAT;
#endif
				/* 
				** This is always the last Status LE belonging
				** to a received packet -> handle it...
				*/
				if ((Port != 0) && (Port != 1)) {
				/* Unknown port */
					panic("sk98lin: Unknown port %d\n",
					Port);
				}

				HandleReceives(
					pAC,
					Port,
					STLE_GET_LEN(pLE),
					STLE_GET_FRSTATUS(pLE),
					pAC->StatusLETable.Bmu.Stat.TcpSum1,
					pAC->StatusLETable.Bmu.Stat.TcpSum2,
					pAC->StatusLETable.Bmu.Stat.RxTimeStamp,
					pAC->StatusLETable.Bmu.Stat.VlanId);
#ifdef CONFIG_SK98LIN_NAPI
				if (*WorkDone >= WorkToDo) {
					break;
				}
				(*WorkDone)++;
#endif
				break;
			case OP_RXVLAN:
				/* this value will be used for next RXSTAT */
				pAC->StatusLETable.Bmu.Stat.VlanId = STLE_GET_VLAN(pLE);
				break;
			case OP_RXTIMEVLAN:
				/* this value will be used for next RXSTAT */
				pAC->StatusLETable.Bmu.Stat.VlanId = STLE_GET_VLAN(pLE);
				/* fall through */
			case OP_RXTIMESTAMP:
				/* this value will be used for next RXSTAT */
				pAC->StatusLETable.Bmu.Stat.RxTimeStamp = STLE_GET_TIST(pLE);
#ifdef Y2_RECOVERY
				pAC->LastOpc = OP_RXTIMESTAMP;
				pAC->LastPort = Port;
#endif
				break;
			case OP_RXCHKSVLAN:
				/* this value will be used for next RXSTAT */
				pAC->StatusLETable.Bmu.Stat.VlanId = STLE_GET_VLAN(pLE);
				/* fall through */
			case OP_RXCHKS:
				/* this value will be used for next RXSTAT */
				pAC->StatusLETable.Bmu.Stat.TcpSum1 = STLE_GET_TCP1(pLE);
				pAC->StatusLETable.Bmu.Stat.TcpSum2 = STLE_GET_TCP2(pLE);
#ifdef Y2_RECOVERY
				pAC->LastPort = Port;
				pAC->LastOpc = OP_RXCHKS;
#endif
				break;
			case OP_RSS_HASH:
				/* this value will be used for next RXSTAT */
#if 0
				pAC->StatusLETable.Bmu.Stat.RssHashValue = STLE_GET_RSS(pLE);
#endif
				break;
			case OP_TXINDEXLE:
				/*
				** :;:; TODO
				** it would be possible to check for which queues
				** the index has been changed and call 
				** CheckForSendComplete() only for such queues
				*/
				STLE_GET_DONE_IDX(pLE,LowVal,HighVal);
				SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
					("LowVal: 0x%x HighVal: 0x%x\n", LowVal, HighVal));

				/*
				** It would be possible to check whether we really
				** need the values for second port or sync queue, 
				** but I think checking whether we need them is 
				** more expensive than the calculation
				*/
				DoneTxA[0] = STLE_GET_DONE_IDX_TXA1(LowVal,HighVal);
				DoneTxS[0] = STLE_GET_DONE_IDX_TXS1(LowVal,HighVal);
				DoneTxA[1] = STLE_GET_DONE_IDX_TXA2(LowVal,HighVal);
				DoneTxS[1] = STLE_GET_DONE_IDX_TXS2(LowVal,HighVal);

				SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
					("DoneTxa1 0x%x DoneTxS1: 0x%x DoneTxa2 0x%x DoneTxS2: 0x%x\n",
					DoneTxA[0], DoneTxS[0], DoneTxA[1], DoneTxS[1]));

				NewDone = SK_TRUE;
				break;
#ifdef USE_TIST_FOR_RESET
			case OP_MOD_TXINDEX:
				SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
					("OP_MOD_TXINDEX\n"));
				SK_IN16(pAC->IoBase, Q_ADDR(Q_XA1, Q_DONE), &DoneTxA[0]);
				if (pAC->GIni.GIMacsFound > 1) {
					SK_IN16(pAC->IoBase, Q_ADDR(Q_XA2, Q_DONE), &DoneTxA[1]);
				}
				NewDone = SK_TRUE;
				break;
			case OP_MOD_LE:
				SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DUMP,
				("Ignore marked LE on port in Reset\n"));
				break;
#endif

			default:
				/* 
				** Have to handle the illegal Opcode in Status LE 
				*/
				SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
					("Unexpected OpCode\n"));
				break;
			}

#ifdef Y2_RECOVERY
			OpCode = STLE_GET_OPC(pLE) & ~HW_OWNER;
			STLE_SET_OPC(pLE, OpCode);
#else
			/* 
			** Reset own bit we have to do this in order to detect a overflow 
			*/
			STLE_SET_OPC(pLE, SW_OWNER);
#endif
		} /* while (OWN_OF_FIRST_LE(&pAC->StatusLETable) == HW_OWNER) */

		/* 
		** Now handle any new transmit complete 
		*/
		if (NewDone) {
			SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
				("Done Index for Tx BMU has been changed\n"));
			for (Port = 0; Port < pAC->GIni.GIMacsFound; Port++) {
				/* 
				** Do we have a new Done idx ? 
				*/
				if (DoneTxA[Port] != GET_DONE_INDEX(&pAC->TxPort[Port][0].TxALET)) {
					SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
						("Check TxA%d\n", Port + 1));
					CheckForSendComplete(pAC, pAC->IoBase, Port,
						&(pAC->TxPort[Port][0].TxAQ_working),
						&pAC->TxPort[Port][0].TxALET,
						DoneTxA[Port]);
				} else {
					SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
						("No changes for TxA%d\n", Port + 1));
				}
#if USE_SYNC_TX_QUEUE
				if (HW_SYNC_TX_SUPPORTED(pAC)) {
					/* 
					** Do we have a new Done idx ? 
					*/
					if (DoneTxS[Port] !=
						GET_DONE_INDEX(&pAC->TxPort[Port][0].TxSLET)) {
						SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
							SK_DBGCAT_DRV_INT_SRC,
							("Check TxS%d\n", Port));
						CheckForSendComplete(pAC, pAC->IoBase, Port,
							&(pAC->TxPort[Port][0].TxSQ_working),
							&pAC->TxPort[Port][0].TxSLET,
							DoneTxS[Port]);
					} else {
						SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
							SK_DBGCAT_DRV_INT_SRC,
							("No changes for TxS%d\n", Port));
					}
				}
#endif
			}
		}
		NewDone = SK_FALSE;

		/* 
		** Check whether we have to refill our RX table  
		*/
		if (HW_FEATURE(pAC, HWF_WA_DEV_420)) {
			if (NbrRxBuffersInHW < MAX_NBR_RX_BUFFERS_IN_HW) {
				for (Port = 0; Port < pAC->GIni.GIMacsFound; Port++) {
					SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
						("Check for refill of RxBuffers on Port %c\n", 'A' + Port));
					FillReceiveTableYukon2(pAC, pAC->IoBase, Port);
				}
			}
		} else {
			for (Port = 0; Port < pAC->GIni.GIMacsFound; Port++) {
				SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_INT_SRC,
					("Check for refill of RxBuffers on Port %c\n", 'A' + Port));
				if (NUM_FREE_LE_IN_TABLE(&pAC->RxPort[Port].RxLET) >= 64) {
					FillReceiveTableYukon2(pAC, pAC->IoBase, Port);
				}
			}
		}
#ifdef CONFIG_SK98LIN_NAPI
		if (*WorkDone >= WorkToDo) {
			break;
		}
#endif
	} while (OWN_OF_FIRST_LE(&pAC->StatusLETable) == HW_OWNER);

	/* 
	** Clear status BMU 
	*/
	SK_OUT32(pAC->IoBase, STAT_CTRL, SC_STAT_CLR_IRQ);

	return(handledStatLE);
}	/* HandleStatusLEs */

/*****************************************************************************
 *
 *	AllocateAndInitLETables - allocate memory for the LETable and init
 *
 * Description:
 *	This function will allocate space for the LETable and will also  
 *	initialize them. The size of the tables must have been specified 
 *	before.
 *
 * Arguments:
 *	pAC - A pointer to the adapter context struct.
 *
 * Returns:
 *	SK_TRUE  - all LETables initialized
 *	SK_FALSE - failed
 */
static SK_BOOL AllocateAndInitLETables(
SK_AC *pAC)  /* pointer to adapter context */
{
	char           *pVirtMemAddr;
	dma_addr_t     pPhysMemAddr = 0;
	SK_U32         CurrMac;
	unsigned       Size;
	unsigned       Aligned;
	unsigned       Alignment;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
		("==> AllocateAndInitLETables()\n"));

	/*
	** Determine how much memory we need with respect to alignment
	*/
	Alignment = MAX_LEN_OF_LE_TAB;
	Size = 0;
	for (CurrMac = 0; CurrMac < pAC->GIni.GIMacsFound; CurrMac++) {
		SK_ALIGN_SIZE(LE_TAB_SIZE(RX_MAX_LE), Alignment, Aligned);
		Size += Aligned;
		SK_ALIGN_SIZE(LE_TAB_SIZE(TXA_MAX_LE), Alignment, Aligned);
		Size += Aligned;
		SK_ALIGN_SIZE(LE_TAB_SIZE(TXS_MAX_LE), Alignment, Aligned);
		Size += Aligned;
	}
	SK_ALIGN_SIZE(LE_TAB_SIZE(ST_MAX_LE), Alignment, Aligned);
	Size += Aligned;
	Size += Alignment;
	pAC->SizeOfAlignedLETables = Size;
	
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT, 
			("Need %08x bytes in total\n", Size));
	
	/*
	** Allocate the memory
	*/
	pVirtMemAddr = pci_alloc_consistent(pAC->PciDev, Size, &pPhysMemAddr);
	if (pVirtMemAddr == NULL) {
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, 
			SK_DBGCAT_INIT | SK_DBGCAT_DRV_ERROR,
			("AllocateAndInitLETables: kernel malloc failed!\n"));
		return (SK_FALSE); 
	}

	/* 
	** Initialize the memory
	*/
	SK_MEMSET(pVirtMemAddr, 0, Size);
	ALIGN_ADDR(pVirtMemAddr, Alignment); /* Macro defined in skgew.h */
	
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
		("Virtual address of LETab is %8p!\n", pVirtMemAddr));
	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
		("Phys address of LETab is %8p!\n", (void *) pPhysMemAddr));

	for (CurrMac = 0; CurrMac < pAC->GIni.GIMacsFound; CurrMac++) {
		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
			("RxLeTable for Port %c", 'A' + CurrMac));
		SkGeY2InitSingleLETable(
			pAC,
			&pAC->RxPort[CurrMac].RxLET,
			RX_MAX_LE,
			pVirtMemAddr,
			(SK_U32) (pPhysMemAddr & 0xffffffff),
			(SK_U32) (((SK_U64) pPhysMemAddr) >> 32));

		SK_ALIGN_SIZE(LE_TAB_SIZE(RX_MAX_LE), Alignment, Aligned);
		pVirtMemAddr += Aligned;
		pPhysMemAddr += Aligned;

		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
			("TxALeTable for Port %c", 'A' + CurrMac));
		SkGeY2InitSingleLETable(
			pAC,
			&pAC->TxPort[CurrMac][0].TxALET,
			TXA_MAX_LE,
			pVirtMemAddr,
			(SK_U32) (pPhysMemAddr & 0xffffffff),
			(SK_U32) (((SK_U64) pPhysMemAddr) >> 32));

		SK_ALIGN_SIZE(LE_TAB_SIZE(TXA_MAX_LE), Alignment, Aligned);
		pVirtMemAddr += Aligned;
		pPhysMemAddr += Aligned;

		SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
			("TxSLeTable for Port %c", 'A' + CurrMac));
		SkGeY2InitSingleLETable(
			pAC,
			&pAC->TxPort[CurrMac][0].TxSLET,
			TXS_MAX_LE,
			pVirtMemAddr,
			(SK_U32) (pPhysMemAddr & 0xffffffff),
			(SK_U32) (((SK_U64) pPhysMemAddr) >> 32));

		SK_ALIGN_SIZE(LE_TAB_SIZE(TXS_MAX_LE), Alignment, Aligned);
		pVirtMemAddr += Aligned;
		pPhysMemAddr += Aligned;
	}

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,("StLeTable"));

	SkGeY2InitSingleLETable(
		pAC,
		&pAC->StatusLETable,
		ST_MAX_LE,
		pVirtMemAddr,
		(SK_U32) (pPhysMemAddr & 0xffffffff),
		(SK_U32) (((SK_U64) pPhysMemAddr) >> 32));

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT, 
		("<== AllocateAndInitLETables(OK)\n"));
	return(SK_TRUE);
}	/* AllocateAndInitLETables */

/*****************************************************************************
 *
 *	AllocatePacketBuffersYukon2 - allocate packet and fragment buffers
 *
 * Description:
 *      This function will allocate space for the packets and fragments
 *
 * Arguments:
 *      pAC - A pointer to the adapter context struct.
 *
 * Returns:
 *      SK_TRUE  - Memory was allocated correctly
 *      SK_FALSE - An error occured
 */
static SK_BOOL AllocatePacketBuffersYukon2(
SK_AC *pAC)  /* pointer to adapter context */
{
	SK_PACKET       *pRxPacket;
	SK_PACKET       *pTxPacket;
	SK_U32           CurrBuff;
	SK_U32           CurrMac;
	unsigned long    Flags; /* needed for POP/PUSH functions */

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
		("==> AllocatePacketBuffersYukon2()"));

	for (CurrMac = 0; CurrMac < pAC->GIni.GIMacsFound; CurrMac++) {
		/* 
		** Allocate RX packet space, initialize the packets and
		** add them to the RX waiting queue. Waiting queue means 
		** that packet and fragment are initialized, but no sk_buff
		** has been assigned to it yet.
		*/
		pAC->RxPort[CurrMac].ReceivePacketTable = 
			kmalloc((RX_MAX_NBR_BUFFERS * sizeof(SK_PACKET)), GFP_KERNEL);

		if (pAC->RxPort[CurrMac].ReceivePacketTable == NULL) {
			SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_INIT | SK_DBGCAT_DRV_ERROR,
				("AllocatePacketBuffersYukon2: no mem RxPkts (port %i)",CurrMac));
			break;
		} else {
			SK_MEMSET(pAC->RxPort[CurrMac].ReceivePacketTable, 0, 
				(RX_MAX_NBR_BUFFERS * sizeof(SK_PACKET)));

			pRxPacket = pAC->RxPort[CurrMac].ReceivePacketTable;

			for (CurrBuff=0;CurrBuff<RX_MAX_NBR_BUFFERS;CurrBuff++) {
				pRxPacket->pFrag = &(pRxPacket->FragArray[0]);
				pRxPacket->NumFrags = 1;
				PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->RxPort[CurrMac].RxQ_waiting, pRxPacket);
				pRxPacket++;
			}
		}

		/*
		** Allocate TX packet space, initialize the packets and
		** add them to the TX free queue. Free queue means that
		** packet is available and initialized, but no fragment
		** has been assigned to it. (Must be done at TX side)
		*/
		pAC->TxPort[CurrMac][0].TransmitPacketTable = 
			kmalloc((TX_MAX_NBR_BUFFERS * sizeof(SK_PACKET)), GFP_KERNEL);

		if (pAC->TxPort[CurrMac][0].TransmitPacketTable == NULL) {
			SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_INIT | SK_DBGCAT_DRV_ERROR,
				("AllocatePacketBuffersYukon2: no mem TxPkts (port %i)",CurrMac));
			kfree(pAC->RxPort[CurrMac].ReceivePacketTable);
			return(SK_FALSE);
		} else {
			SK_MEMSET(pAC->TxPort[CurrMac][0].TransmitPacketTable, 0, 
				(TX_MAX_NBR_BUFFERS * sizeof(SK_PACKET)));
		
			pTxPacket = pAC->TxPort[CurrMac][0].TransmitPacketTable;

			for (CurrBuff=0;CurrBuff<TX_MAX_NBR_BUFFERS;CurrBuff++) {
				PUSH_PKT_AS_LAST_IN_QUEUE(&pAC->TxPort[CurrMac][0].TxQ_free, pTxPacket);
				pTxPacket++;
			}
		}
	} /* end for (CurrMac = 0; CurrMac < pAC->GIni.GIMacsFound; CurrMac++) */

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_INIT,
		("<== AllocatePacketBuffersYukon2 (OK)\n"));
	return(SK_TRUE);

}	/* AllocatePacketBuffersYukon2 */

/*****************************************************************************
 *
 *	FreeLETables - release allocated memory of LETables
 *
 * Description:
 *      This function will free all resources of the LETables
 *
 * Arguments:
 *      pAC - A pointer to the adapter context struct.
 *
 * Returns: N/A
 */
static void FreeLETables(
SK_AC *pAC)  /* pointer to adapter control context */
{
	dma_addr_t	pPhysMemAddr;
	char		*pVirtMemAddr;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("==> FreeLETables()\n"));
	
	/*
	** The RxLETable is the first of all LET. 
	** Therefore we can use its address for the input 
	** of the free function.
	*/
	pVirtMemAddr = (char *) pAC->RxPort[0].RxLET.pLETab;
	pPhysMemAddr = (((SK_U64) pAC->RxPort[0].RxLET.pPhyLETABHigh << (SK_U64) 32) | 
			((SK_U64) pAC->RxPort[0].RxLET.pPhyLETABLow));

	/* free continuous memory */
	pci_free_consistent(pAC->PciDev, pAC->SizeOfAlignedLETables,
			    pVirtMemAddr, pPhysMemAddr);

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("<== FreeLETables()\n"));
}	/* FreeLETables */

/*****************************************************************************
 *
 *	FreePacketBuffers - free's all packet buffers of an adapter
 *
 * Description:
 *      This function will free all previously allocated memory of the 
 *	packet buffers.
 *
 * Arguments:
 *      pAC - A pointer to the adapter context struct.
 *
 * Returns: N/A
 */
static void FreePacketBuffers(
SK_AC *pAC)  /* pointer to adapter control context */
{
	int Port;

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("==> FreePacketBuffers()\n"));
	
	for (Port = 0; Port < pAC->GIni.GIMacsFound; Port++) {
		kfree(pAC->RxPort[Port].ReceivePacketTable);
		kfree(pAC->TxPort[Port][0].TransmitPacketTable);
	}

	SK_DBG_MSG(pAC, SK_DBGMOD_DRV, SK_DBGCAT_DRV_MSG,
		("<== FreePacketBuffers()\n"));
}	/* FreePacketBuffers */

/*****************************************************************************
 *
 * 	AllocAndMapRxBuffer - fill one buffer into the receive packet/fragment
 *
 * Description:
 *	The function allocates a new receive buffer and assigns it to the
 *	the passsed receive packet/fragment
 *
 * Returns:
 *	SK_TRUE - a buffer was allocated and assigned
 *	SK_FALSE - a buffer could not be added
 */
static SK_BOOL AllocAndMapRxBuffer(
SK_AC      *pAC,        /* pointer to the adapter control context */
SK_PACKET  *pSkPacket,  /* pointer to packet that is to fill      */
int         Port)       /* port the packet belongs to             */
{
	struct sk_buff *pMsgBlock;  /* pointer to a new message block  */
	SK_U64          PhysAddr;   /* physical address of a rx buffer */

	SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
		("--> AllocAndMapRxBuffer (Port: %i)\n", Port));

	pMsgBlock = alloc_skb(pAC->RxBufSize, GFP_ATOMIC);
	if (pMsgBlock == NULL) {
		SK_DBG_MSG(NULL, SK_DBGMOD_DRV,
			SK_DBGCAT_DRV_RX_PROGRESS | SK_DBGCAT_DRV_ERROR,
			("%s: Allocation of rx buffer failed !\n",
			pAC->dev[Port]->name));
		SK_PNMI_CNT_NO_RX_BUF(pAC, pAC->RxPort[Port].PortIndex);
		return(SK_FALSE);
	}
	skb_reserve(pMsgBlock, 8);

	PhysAddr = (SK_U64) pci_map_page(pAC->PciDev,
		virt_to_page(pMsgBlock->data),
		((unsigned long) pMsgBlock->data &
		~PAGE_MASK),
		pAC->RxBufSize,
		PCI_DMA_FROMDEVICE);

	pSkPacket->pFrag->pVirt   = pMsgBlock->data;
	pSkPacket->pFrag->pPhys   = PhysAddr;
	pSkPacket->pFrag->FragLen = pAC->RxBufSize; /* for correct unmap */
	pSkPacket->pMBuf          = pMsgBlock;	
	pSkPacket->PacketLen      = pAC->RxBufSize;

	SK_DBG_MSG(NULL, SK_DBGMOD_DRV, SK_DBGCAT_DRV_RX_PROGRESS,
		("<-- AllocAndMapRxBuffer\n"));

	return (SK_TRUE);
}	/* AllocAndMapRxBuffer */

/*******************************************************************************
 *
 * End of file
 *
 ******************************************************************************/