
// YOU SHOULD CAREFULLY READ THE FOLLOWING TERMS AND CONDITIONS BEFORE 
// INSTALLING AND USING THIS PRODUCT, 
// THE USE OF WHICH IS LICENSED BY 3COM CORPORATION ("3COM") 
// and others as set forth below for your USE ONLY AS SET FORTH BELOW. 
// DOWNLOADING, INSTALLING OR OTHERWISE USING ANY PART OF THE SOFTWARE OR 
// DOCUMENTATION INDICATES THAT YOU ACCEPT THESE TERMS AND CONDITIONS.  
// IF YOU DO NOT AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT, 
// DO NOT DOWNLOAD, INSTALL OR OTHERWISE USE THE SOFTWARE OR DOCUMENTATION. 
// AND IF YOU HAVE RECEIVED THE SOFTWARE AND DOCUMENTATION ON PHYSICAL MEDIA, 
// RETURN THE ENTIRE PRODUCT WITH THE SOFTWARE AND DOCUMENTATION UNUSED TO THE
// SUPPLIER WHERE YOU OBTAINED IT.

// This driver (3c990.c) has been written to work with the 3cr990 product line
// of network cards, manufactured by 3Com Corp.
// This driver is not intended for any other product line, including the 3c59x 
// or 3C90x product lines (although drivers with both of these names,
// and for both of these product lines, are available ). 

// This is a Beta version of the driver (note the version number 1.0.0a).
// It does not work with the 2.3 kernel; only the 2.0 and 2.2 at this point.
// There has also been very little work (~none) on performance as of yet.
// The driver contains no support now for architectures other than IA-32 bit.
// If you want a better version, try back later.  

// To force the media selection, use the command line argument force=X, 
// where X denotes the selection as follows:
// 0=10(megabit)Half(Duplex); 1=10Full; 2=100Half; 3=100Full; 4=auto (default)
// e.g. insmod 3c990.o force=1   
// ... should give you 10 megabit full duplex forced action.

// You may contact 3Com for updates and information (regarding this driver) at:
// http://support.3com.com/infodeli/tools/nic/3c990.htm

// You may request or submit driver modifications at:
// http://support.3com.com/infodeli/tools/nic/linuxrequest.htm

// If you would like more information about compiling the driver than is 
// available at the bottom of this file, you may choose to reference:
// http://cesdis.gsfc.nasa.gov/linux/misc/modules.html
// and/or you may reference the readme file for the 3c90x driver.

// License to "tc990image"
// The binary image "tc990image" is licensed to users by 3Com Corporation, 
// with intended use for 3Com's Network Interface Card models 3CR990-x.
// (Where x indicates any extension model number.)
// LICENSE:  3Com grants you a nonexclusive, nontransferable 
// (except as specified herein) license to use the tc990image in conjunction 
// with your use of 3Com's Network Interface Card models 3CR990-x as specified 
// above. You are not permitted to lease, rent, distribute or sublicense 
// (except as specified herein) the tc990image or to use the tc990image or in 
// any other unauthorized manner.  Further, no license is granted to you in the
// human readable code of the Software (source code).  Except as provided 
// below, this Agreement does not grant you any rights to patents, copyrights, 
// trade secrets, trademarks, or any other rights with respect to the Software 
// or Documentation.
// Subject to the restrictions set forth herein, the tc990image is licensed to 
// be used on any workstation or any network server owned by or leased to you, 
// for your internal use, provided that the tc990image is used only in 
// connection with this 3Com product.  You may reproduce and provide one (1) 
// copy of the tc990image for each such workstation or network server on which // the Software is used as permitted hereunder.  Otherwise, the Software and 
// Documentation may be copied only as essential for backup or archive purposes
// in support of your use of the Software as permitted hereunder.  Each copy 
// of the tc990image must contain 3Com's and its licensors' proprietary rights 
// and copyright notices in the same form as on the original.  You agree not to
// remove or deface any portion of any legend provided on any licensed program 
// or documentation delivered to you under this Agreement.
// ASSIGNMENT;   You may transfer the tc990image and the licenses granted 
// herein to another party in the same country in which you obtained it if the 
// other party agrees in writing to accept and be bound by the terms and 
// conditions of this Agreement. If you transfer the tc990image, you must at 
// the same time either transfer all copies of the Software and Documentation 
// to the party or you must destroy any copies not transferred. Except as set 
// forth above, you may not assign or transfer your rights.

// NO REVERSE ENGINEERING:  Modification, reverse engineering, reverse 
// compiling, or disassembly of the tc990image is expressly prohibited. 
// However, if you are a European Union ("EU") resident, information necessary 
// to achieve interoperability of the tc990image with other programs within the
// meaning of the EU Directive on the Legal Protection of Computer Programs is 
// available to you from 3Com upon written request.
// TRADE SECRETS; TITLE:  You acknowledge and agree that the structure, 
// sequence and organization of the tc990image are the valuable trade secrets 
// of 3Com and its suppliers. You agree to hold such trade secrets in 
// confidence. You further acknowledge and agree that ownership of, and title 
// to, the tc990image and all subsequent copies thereof regardless of the form // or media are held by 3Com.

// License to this driver
// Some material in this driver has been adopted from pci-skeleton.c, 
// a Linux PCI network adapter skeleton device driver. 
// pci-skeleton.c written in 1998-1999 by Donald Becker.
// The author of pci-skeleton.c may be reached as becker@scyld.com.
// More information about Becker is available at http://www.scyld.com/

// Except as otherwise provided, This software and pci-skeleton.c, may be used 
// and distributed according to the terms of the GNU Public License (GPL), 
// incorporated herein by reference.

// UNITED STATES GOVERNMENT LEGENDS:  The portions of this Driver developed by 
// 3Com, and the tc990image ("Software") is commercial in nature and developed 
// solely at private expense.  The Software is delivered as 
// "Commercial Computer Software" as defined in DFARS 252.227-7014 (June 1995) 
// or as a commercial item as defined in FAR 2.101(a) and as such is provided 
// with only such rights as are provided herein.  Technical data is provided 
// with limited rights only as provided in DFAR 252.227-7015 (Nov. 1995) or 
// FAR 52.227-14 (June 1987), whichever is applicable.

// TERM AND TERMINATION:  The licenses to the Software granted hereunder are 
// perpetual unless terminated earlier as specified below or otherwise 
// provided. You may terminate the licenses and this Agreement at any time by 
// destroying the Software and Documentation together with all copies and 
// merged portions in any form. The licenses and this Agreement will also 
// terminate immediately if you fail to comply with any term or condition of 
// this Agreement. Upon such termination you agree to destroy the Software and 
// Documentation, together with all copies and merged portions in any form.

// LIMITED WARRANTIES AND LIMITATION OF LIABILITY: This driver and  tc990image 
// are provided with no warranty, including warranty of fitness for any 
// particular purpose.  3Com assumes no liability for any damages or injuries 
// resulting from use of the driver or tc990image file.
// GOVERNING LAW:   This License shall be governed by the laws of the State of 
// California, U.S.A. excluding its conflicts of laws principles and excluding 
// the United Nations Convention on Contracts for the 
// International Sale of Goods.
// SEVERABILITY:  In the event any provision of this License is found to be 
// invalid, illegal or unenforceable, the validity, legality and enforceability
// of any of the remaining provisions shall not in any way be affected or 
// impaired and a valid, legal and enforceable provision of similar intent and 
// economic impact shall be substituted therefor.
// ENTIRE AGREEMENT:  This Agreement sets forth the entire understanding and 
// agreement between you and 3Com and supersedes all prior agreements, whether 
// written or oral, with respect to the Software and Documentation, and may be 
// amended only in a writing signed by both parties.
// Should you have any questions concerning this Agreement or if you desire to 
// contact 3Com for any reason, please contact the 3Com subsidiary serving your
// country, or write: 
// 3Com Corporation, Customer Support Information, 
// 5400 Bayfront Plaza, Santa Clara, CA  95052

static const char *version = "3Com  3c990.c  v1.0.0a  8/2000  \n";
static int min_pci_latency = 64;
static int force = 4;

#if !defined(__OPTIMIZE__)  ||  !defined(__KERNEL__)
#warning  You must compile this file with the correct options!
#warning  See the last lines of the source file.
#error You must compile this driver with "-O".
#endif

#define UPDATE_INDEX( index, size, entries ) { index += size; if ( index == (entries * size ) )  index = 0; }

// Include files to support kernel versions 2.0.x and 2.2.x.  
#include <linux/config.h>
#include <linux/version.h>
#ifdef MODULE
#ifdef MODVERSIONS
#include <linux/modversions.h>
#endif
#include <linux/module.h>
#else
#define MOD_INC_USE_COUNT
#define MOD_DEC_USE_COUNT
#endif

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/malloc.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <asm/processor.h>		// * Processor type for cache alignment. *
#include <asm/bitops.h>
#include <asm/io.h>

#include <asm/spinlock.h>
#include <linux/delay.h>    

#include "3c990img.h"

#if (LINUX_VERSION_CODE >= 0x20100)
char kernel_version[] = UTS_RELEASE;
#else
#ifndef __alpha__
#define ioremap vremap
#define iounmap vfree
#endif
#endif

#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115
MODULE_AUTHOR("David P. McLean  <davidpmclean@yahoo.com>");
MODULE_DESCRIPTION("Driver for 3Com EtherLink 10/100 PCI NIC with 3XP Processor");
MODULE_PARM(min_pci_latency, "i");
MODULE_PARM(force, "i");

#endif
#if LINUX_VERSION_CODE < 0x20123
#define test_and_set_bit(val, addr) set_bit(val, addr)
#endif
#if LINUX_VERSION_CODE <= 0x20139
#define	net_device_stats enet_statistics
#else
#define NETSTATS_VER2
#endif
#if LINUX_VERSION_CODE < 0x20155
#include <linux/bios32.h>
#define PCI_SUPPORT_VER1
#else
#define PCI_SUPPORT_VER2
#endif
#if LINUX_VERSION_CODE < 0x20159
#define dev_free_skb(skb) dev_kfree_skb(skb, FREE_WRITE);
#else
#define dev_free_skb(skb) dev_kfree_skb(skb);
#endif
#if ! defined(CAP_NET_ADMIN)
#define capable(CAP_XXX) (suser())
#endif

#define tc990_IMAGE_SIZE 0x10000
#define tc990_VENDOR_ID 0x10B7

#define TX_ENTRIES        128
#define RX_ENTRIES        128    
#define CMD_ENTRIES        64
#define RESPONSE_ENTRIES   64

// 3cr990 Commands.
#define tc990_CMD_TX_ENABLE             0x1
#define tc990_CMD_TX_DISABLE            0x2
#define tc990_CMD_RX_ENABLE             0x3
#define tc990_CMD_RX_DISABLE            0x4
#define tc990_CMD_RX_FILT_WRITE         0x5
#define tc990_CMD_READ_STATS            0x7
#define tc990_CMD_XCVR_SELECT           0x13
#define tc990_CMD_MAX_PKT_SIZE_WRITE    0x1A
#define tc990_CMD_MCAST_HASH_MASK_WRITE 0x25
#define tc990_CMD_STATION_ADR_READ      0x27

// Descriptor Type
#define CMD_DSC_TYPE_CMD_FRAME       0x2
#define CMD_DSC_FRAME_VALID          ( 1 << 7 ) 
#define CMD_DSC_RESPONSE_NEEDED      ( 1 << 6 )
#define CMD_DSC_RESPONSE_NOT_NEEDED  0 

#define RESPONSE_DSC_ERROR_SET       ( 1 << 6 )
#define tc990_STATUS_CMD_FOUND	     2

// 3cr990 Registers
#define tc990_SOFT_RESET_REG         0x0
#define tc990_INT_STATUS_REG         0x4
#define tc990_INT_ENABLE_REG         0x8
#define tc990_INT_MASK_REG           0xC
#define tc990_HostTo3XP_COMM_5_REG   0x1C
#define tc990_HostTo3XP_COMM_4_REG   0x20
#define tc990_HostTo3XP_COMM_3_REG   0x24
#define tc990_HostTo3XP_COMM_2_REG   0x28
#define tc990_HostTo3XP_COMM_1_REG   0x2C
#define tc990_HostTo3XP_COMM_0_REG   0x30
#define tc990_3XP2HOST_COMM_0_REG    0x40

#define tc990_MASK_ALL_INT        0xFFFFFFFF
#define tc990_UNMASK_ALL_INT      0x0
#define tc990_ENABLE_ALL_INT      0xFFFFFFEF
#define tc990_RESET_ALL           0x7F

// 3cr990 commands for handshake with firmware.
#define tc990_NULL_CMD                  0x00
#define tc990_BOOTCMD_REG_BOOT_RECORD   0x0FF
#define tc990_BOOTCMD_RUNTIME_IMAGE     0xFD
#define tc990_BOOTCMD_DOWNLOAD_COMPLETE 0xFB
#define tc990_BOOTCMD_SEGMENT_AVAILABLE 0xFC

// Waiting for boot signature
#define tc990_WAITING_FOR_BOOT          0x7
#define tc990_WAITING_FOR_HOST_REQUEST  0xD
#define tc990_WAITING_FOR_SEGMENT       0x10

#define tc990_3XP_COMM_INT0  0x2

// Delay in microseconds to wait for the reset to be over
#define tc990_WAIT_COUNTER        100000

#define ETHERNET_MAXIMUM_FRAME_SIZE 1514
//#define ETHERNET_ADDRESS_SIZE 6 //#define ETHERNET_HEADER_SIZE 14

#define tc990_RX_FILT_DIRECTED          0x1
#define tc990_RX_FILT_ALL_MULTICAST     0x2
#define tc990_RX_FILT_BROADCAST         0x4
#define tc990_RX_FILT_PROMISCUOUS       0x8
#define tc990_RX_FILT_HASH_MULTICAST    0x10

#define tc990_MULTICAST_BITS               0x3f

// Slot information.
struct SLOT {
	u32 PhysicalAddressLo;
	u32 PhysicalAddressHi;
	u32 VirtualAddressLo;
	u32 VirtualAddressHi;
	u32 BufferLength;
	u32 skb;
};

typedef struct _tc990_RING {
	u32 RingBase;          // ring address (sharedmemory)
	u32 NoEntries;         // number of entries in ring
	u32 LastWriteUL;	     // send: last insert
} tc990_RING;

typedef struct _TX_RING {
	u32 RingBase;          // ring address (sharedmemory)
	u32 NoEntries;         // number of entries in ring
	u32 LastWriteUL;	     // send: last insert
	u32 LastReadUL;	     // cleanup: last read - current read
	u32 WriteRegister;     // register used to single NIC
	u32 PacketPendingNo;   // Number of packets sent - not completed
} TX_RING;

// This structure is updated by the driver and read by the firmware.
typedef struct _HOST_WRITE_INDEXES {
	volatile u32 regRxHiReadUL;
	volatile u32 regRxLoReadUL;
	volatile u32 regRxBuffWriteUL;
	volatile u32 regRespReadUL;
} HOST_WRITE_INDEXES;

// This structure is updated by firmware and read by the driver.
typedef struct _HOST_READ_INDEXES {
	volatile u32 regTxLoReadUL;
	volatile u32 regTxHiReadUL;
	volatile u32 regRxLoWriteUL;
	volatile u32 regRxBuffReadUL;
	volatile u32 regCmdReadUL;
	volatile u32 regRespWriteUL;
	volatile u32 regRxHiWriteUL;
} HOST_READ_INDEXES;

// main variable structure 
typedef struct _HOST_VAR_S {
	HOST_WRITE_INDEXES hvWriteS;
	HOST_READ_INDEXES hvReadS;
} HOST_VAR_S;

typedef struct _HOST_INIT_S {
	HOST_VAR_S  *hostVarsPS;         // points to host variable data struct 
	u32 hostVarsHiUL;
	u32 hostTxLoStartUL;    // Tx Lo priority ring 
	u32 hostTxLoStartHiUL;
	u32 hostTxLoSizeUL;
	u32 hostTxHiStartUL;    // Tx Hi priority ring 
	u32 hostTxHiStartHiUL;
	u32 hostTxHiSizeUL;
	u32 hostRxLoStartUL;    // Rx Lo priority ring 
	u32 hostRxLoStartHiUL;
	u32 hostRxLoSizeUL;
	u32 hostRxFreeStartUL;  // Rx free buffer ring 
	u32 hostRxFreeStartHiUL;
	u32 hostRxFreeSizeUL;
	u32 hostCtrlStartUL;    // Comand ring 
	u32 hostCtrlStartHiUL;
	u32 hostCtrlSizeUL;
	u32 hostRespStartUL;    // Command Response ring 
	u32 hostRespStartHiUL;
	u32 hostRespSizeUL;
	u32 hostZeroWordUL;     // (lo) physical address of zero word 
	u32 hostZeroWordHiUL;   // (Hi) used for dma 
	u32 hostRxHiStartUL;    // Rx Hi priority ring 
	u32 hostRxHiStartHiUL;
	u32 hostRxHiSizeUL;
} HOST_INIT_S;

// Receive Descriptor. This structure is updated by the firmware.
typedef struct _RX_DSC {
	u32 Flags:8;
	u32 NumDescriptor:8;
	u32 FrameLength:16;
	u32 VirtualAddressLo;
	u32 VirtualAddressHi;
	u32 RxStatus;
	u16 FilterResults;
	u16 res1;
	u32 res2;
} RX_DSC;

// Receive Free Descriptor. This structure is filled by the driver to give a
// buffer to the firmware.
typedef struct _RX_FREE_DSC {
	u32 PhysicalAddressLo;
	u32 PhysicalAddressHi;
	u32 VirtualAddressLo;
	u32 VirtualAddressHi;
} RX_FREE_DSC;

// used for both commands and responses 
typedef struct {
	u32 Flags:8;
	u32 NumDescriptor:8;
	u32 Command:16;
	u32 SequenceNo:16;
	u32 Parameter1:16;
	u32 Parameter2;
	u32 Parameter3;
} CMD_DSC;

// stats response labels follow (until RESPONSE_DSC union)
typedef struct {
	u32 reserved;
	u32 reservedB;
	u32 pkt;
	u32 byte;
}rtnTx;

typedef struct {
	u32 reserved;
	u32 deferred;
	u32 lateCollision;
	u32 collisions;
}rtnTxMore;

typedef struct {
	u32 carrierLost;
	u32 multCollision;
	u32 reserved;
	u32 reservedB;
}rtnTxCrit;

typedef struct {
	u32 reserved;
	u32 txFiltered;
	u32 rxPkt;
	u32 rxByte;
}rtnTxRx;

typedef struct {
	u32 reserved;
	u32 fifoOverrun;
	u32 badSSD;
	u32 crcError;
}rtnRx;

typedef struct {
	u32 oversize;
	u32 reserved;
	u32 reservedB;
	u32 reservedC;
}rtnRxGeneral;

typedef struct {
	u32 filtered;
	u32 reserved;
	u32 reservedB;
	u32 reservedC;
}rtnRxEtc;

typedef union {
	CMD_DSC nrml;                 //"normal" response values
	rtnTx tx;
	rtnTxMore txMore;
	rtnTxCrit txCrit;
	rtnTxRx txRx;
	rtnRx rx;
	rtnRxGeneral rxGeneral;
	rtnRxEtc rxEtc;
} RESPONSE_DSC;

// individual descriptors for each packet 
typedef struct _TX_DSC {
	u8  pktFlagsUC;       
	u8  pktReservedUC;
	u16 pktLenUW;          // # of bytes in this packet 
	u32 pktHostAddrLoUL;   // PCI address of packet data
	u32 pktHostAddrHiUL;   // PCI address of packet data
	u32 pktSpareUL;        // not used 
} TX_DSC;

typedef struct _tc990_FILE_HEADER {
    u8 tagID[8];
    u32 Version;
    u32 NumSections;
    u32 ExecuteAddress;
} tc990_FILE_HEADER; 

typedef struct _tc990_FILE_SECTION {
    u32 NumBytes;
    u16 Checksum;
    u16 Reserved;
    u32 tc3XPStartAddress;
} tc990_FILE_SECTION;

struct pci_id_info {
	const char *name;
	u16 device_id;
};

static struct pci_id_info pci_tbl[] = {
	{"3Com EtherLink 10/100 PCI NIC with 3XP Processor (3CR990-TX-95)",0x9902},
	{"3Com EtherLink 10/100 PCI NIC with 3XP Processor (3CR990-TX-97)",0x9903},
	{"3Com EtherLink Server 10/100 PCI NIC with 3XP Processor (3CR990SVR95)", 
	 0x9908},
  	{"3Com EtherLink Server 10/100 PCI NIC with 3XP Processor (3CR990SVR97)", 
	 0x9909},
	{0,},						                // 0 terminated list. 
};

struct tc990phys {
	HOST_INIT_S init; 
	u32 slack;
	HOST_VAR_S var; 
	TX_DSC txLo[TX_ENTRIES], txHi[TX_ENTRIES];
	RX_FREE_DSC rxFree[RX_ENTRIES];
	RX_DSC rxLo[RX_ENTRIES], rxHi[RX_ENTRIES]; 
	CMD_DSC cmd[CMD_ENTRIES];
	RESPONSE_DSC rsp[RESPONSE_ENTRIES];
};

struct tc990virt {
    tc990_RING RxHiRing, RxLoRing, RxBuffRing;  // receive information 
    TX_RING TxHiRing, TxLoRing; 	            // send information 
    tc990_RING CmdRing, RspRing;	            // command ring information
};

struct tc990_private { 
	struct tc990phys * pys;   
	struct tc990virt * vrt;     
	void * DownloadPageVirtual;
	u32 DownloadPagePhysical;
	u32 ringPhysical;
	u32 BufferPhysical;
	struct SLOT* Slot;
	struct device *next_module;			// Link for devices of this type. 
	const char *product_name;
	struct net_device_stats stats;
	struct wait_queue *statsWait;   
	u16 chip_id;
	unsigned char pci_bus, pci_devfn;
	u8 interruptUp;			
	u32 pad[4];							// Used for 32-byte alignment 
};

static struct device *tc990probe(u16 pci_bus, u16 pci_devfn, 
		   struct device *dev, long ioaddr, u16 irq, u16 chp_idx, u16 fnd_cnt);
static int  tc990_open(struct device *dev);                    
static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);  
static void stats_handler(struct tc990_private *np);
static int  tc990_close(struct device *dev);                   
static int  start_tx(struct sk_buff *skb, struct device *dev);  
static struct net_device_stats *get_stats(struct device *dev);  
static void set_rx_mode(struct device *dev);                    
static void ReleaseBufferToFirmware( struct device *dev, u32 SlotIndex );
static void ProcessReceive(struct device *dev, tc990_RING *pRxRing,
					volatile u32 *regRxReadUL, volatile u32 *regRxWriteUL);
static u16 CalculateBufferChecksum(u16 *Buffer,  u32 Count);
static u16 Downloadbootrecord(struct device *dev, u16 process);
static u16 DownloadRunTimeImage(struct device *dev);
static u16 issueCommand(struct device *dev, u16 Command, u16 Parameter1, 
	      u32 Parameter2, u32 Parameter3, u16 *Return1,
	      u32 *Return2, u32 *Return3, u8 WaitForResponse );
static u8 ProcessResponse( struct tc990_private *np, 
					u32 responseReadIndex, u16 Command );
static void bailOutNow ( struct device *dev, u16 state );



static u16 issueCommand( struct device *dev, u16 Command, u16 Parameter1,
			 u32 Parameter2, u32 Parameter3, u16 *Return1,
			 u32 *Return2, u32 *Return3, u8 WaitForResponse )
{
  struct tc990_private *np = (struct tc990_private *)dev->priv;
  CMD_DSC*      commandDescriptor;
  RESPONSE_DSC* responseDescriptor;
  u32 count, responseStatus;
  long ioaddr = dev->base_addr; 

  commandDescriptor = (CMD_DSC*) (np->vrt->CmdRing.RingBase + 
										 np->vrt->CmdRing.LastWriteUL );
  memset(commandDescriptor, 0, sizeof( CMD_DSC )); 

  if ( WaitForResponse )   // Check if wait for response is needed.
    // Set the flag indicating the need for a response to the command.
    responseStatus = CMD_DSC_RESPONSE_NEEDED;
  else
    responseStatus = CMD_DSC_RESPONSE_NOT_NEEDED;

  commandDescriptor->Command       = Command;
  commandDescriptor->NumDescriptor = 0;
  commandDescriptor->Parameter1    = Parameter1;
  commandDescriptor->Parameter2    = Parameter2;
  commandDescriptor->Parameter3    = Parameter3;
  commandDescriptor->Flags         = CMD_DSC_FRAME_VALID 
	  | responseStatus | CMD_DSC_TYPE_CMD_FRAME ; 
  commandDescriptor->SequenceNo    = 0x11;

  UPDATE_INDEX( np->vrt->CmdRing.LastWriteUL, sizeof( CMD_DSC ), 
				CMD_ENTRIES) ; 

  writel(np->vrt->CmdRing.LastWriteUL, ioaddr + tc990_HostTo3XP_COMM_2_REG);

  if ( WaitForResponse ) {    // Check for response
	  for ( count = 0; count < tc990_WAIT_COUNTER; count++ ){
		  // If firmware has posted the response then break from loop
		  if ( np->pys->var.hvWriteS.regRespReadUL != 
			   np->pys->var.hvReadS.regRespWriteUL ){
			  if(tc990_CMD_READ_STATS==commandDescriptor->Command){
				  ProcessResponse (np, np->pys->var.hvWriteS.regRespReadUL, 0);
				  return 0;
			  }
			  if (tc990_STATUS_CMD_FOUND == ProcessResponse 
				  (np, np->pys->var.hvWriteS.regRespReadUL, 
				   (u16)commandDescriptor->Command))	           break;
		  }
		  udelay(50);  
	  }
	  // If no response for tc990_WAIT_COUNTER, return failure.
	  if ( count >= tc990_WAIT_COUNTER ) 
		  return 1;
	
    responseDescriptor = (RESPONSE_DSC*) (np->vrt->RspRing.RingBase + 
				   					      np->pys->var.hvWriteS.regRespReadUL);
    if ( responseDescriptor->nrml.Flags & RESPONSE_DSC_ERROR_SET ) 
      return 1;
    // Command is successful, pass the response results back.
    if ( Return1 ) *Return1 = (u16)responseDescriptor->nrml.Parameter1;
    if ( Return2 ) *Return2 = responseDescriptor->nrml.Parameter2;
    if ( Return3 ) *Return3 = responseDescriptor->nrml.Parameter3;
    UPDATE_INDEX(np->pys->var.hvWriteS.regRespReadUL, 
				 sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES);
  }
  return 0;
}


//Description:
//    This routine calculates the checksum of the count bytes in the buffer.
//Arguments:
//    Buffer  - Pointer to the buffer.
//    Count   - Count of bytes for which to compute the checksum.
//Return Value:
//    Checksum of the block.
static u16 CalculateBufferChecksum(u16 *Buffer,  u32 Count)
{
    u32   checksum = 0;
    while ( Count > 1 ){
		// This is the inner loop.
		checksum    += *Buffer++; 
		Count       -= 2;
		// Fold 32-bit checksum to 16 bits.
		while ( checksum >> 16 )
			checksum = ( checksum & 0xffff ) + ( checksum >> 16 );
    }	
    if ( Count > 0 ) 
		checksum += *Buffer;
    while ( checksum >> 16 )
		checksum = ( checksum & 0xffff ) + ( checksum >> 16 );
    return (u16)(~checksum );
}

static u16 ResetAdapter(struct device *dev)
{
    u32  count;
	long ioaddr = dev->base_addr;

    writel(tc990_RESET_ALL, ioaddr + tc990_SOFT_RESET_REG);
	udelay(10);	 // Give the board some time to adjust
	writel(0,    ioaddr + tc990_SOFT_RESET_REG);   
    for ( count = 0; count < tc990_WAIT_COUNTER; count++ ) {
        if ( readl(ioaddr + tc990_3XP2HOST_COMM_0_REG)
               == tc990_WAITING_FOR_HOST_REQUEST ) break;
		udelay(50); 
    }
    if ( count >= tc990_WAIT_COUNTER )  return 1;
    return 0;
}

//    This routine verifies that the adpater is present and fills in the 
//    adapter specific data
static u16 Downloadbootrecord(struct device *dev, u16 process)
{
	struct tc990_private *np = (struct tc990_private *)dev->priv;
	u32  val, counter;
	long ioaddr = dev->base_addr;

	// Check if the adapter status is waiting for boot.
	for ( counter = 0; counter < tc990_WAIT_COUNTER; counter++ ) {
		val = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG); 
		if ( val == process ) break;
		udelay(50);  	
	}

	if ( tc990_WAIT_COUNTER == counter  ) return 21;
	
	// register the ring with the firmware
	writel(0, ioaddr +  tc990_HostTo3XP_COMM_2_REG);
	writel( np->ringPhysical, 
		   ioaddr + tc990_HostTo3XP_COMM_1_REG );

	writel(tc990_BOOTCMD_REG_BOOT_RECORD, 
		   ioaddr + tc990_HostTo3XP_COMM_0_REG);
		
	// wait for the firmware to pick up the command.
	for ( counter = 0; counter < tc990_WAIT_COUNTER; counter++ ) {
		val = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG);
		if ( val != process )
			break;
		udelay(50);
	}
	
	if ( tc990_WAIT_COUNTER == counter ) return 22;
	
	// clear the tx and cmd ring write registers
	writel(tc990_NULL_CMD, ioaddr + tc990_HostTo3XP_COMM_1_REG);
	writel(tc990_NULL_CMD, ioaddr + tc990_HostTo3XP_COMM_2_REG);
	writel(tc990_NULL_CMD, ioaddr + tc990_HostTo3XP_COMM_3_REG);
	writel(tc990_NULL_CMD, ioaddr + tc990_HostTo3XP_COMM_0_REG);
	
	return 0;
}

//Returns 0 if the image could be downloaded (else other than 0).
static u16 DownloadRunTimeImage(struct device *dev)
{
	struct tc990_private *np = (struct tc990_private *)dev->priv;

	tc990_FILE_HEADER *fileHeader;
	u32 index;
	tc990_FILE_SECTION *fileSection;
	u32 count, value, oldIntMask, oldIntEnable;
	u32 tempLength, thisSectionHeaderLocation, thisSectionDataLocation;
	void *sectionDataBuffer; 
	u32 tc3XPStartAddress, numBytesInThisSection, checksum;
	u32  sections;
	long ioaddr = dev->base_addr; 

	(void *)np->DownloadPageVirtual = kmalloc(0xfc00, GFP_KERNEL); 
	if (np->DownloadPageVirtual == NULL) return 40;

	fileHeader = (tc990_FILE_HEADER*)tc990image; 

	if((fileHeader->tagID[0] != 'T') || (fileHeader->tagID[1] != 'Y') ||
	   (fileHeader->tagID[2] != 'P') || (fileHeader->tagID[3] != 'H') ||
	   (fileHeader->tagID[4] != 'O') || (fileHeader->tagID[5] != 'O') ||
	   (fileHeader->tagID[6] != 'N')) 
		{ 
			kfree(np->DownloadPageVirtual);		
			return 42;
		}
	if ( ResetAdapter(dev) ) {
			kfree(np->DownloadPageVirtual);		
			return 43;
	}
	udelay(5000);	
	oldIntEnable = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG); 

	// Mask the interrupts and enable the interrupts.
	oldIntEnable = readl(ioaddr + tc990_INT_ENABLE_REG); // zeros
	writel(oldIntEnable | tc990_3XP_COMM_INT0, ioaddr + tc990_INT_ENABLE_REG);
	oldIntMask = readl(ioaddr + tc990_INT_MASK_REG);
	writel(oldIntMask | tc990_3XP_COMM_INT0, ioaddr + tc990_INT_MASK_REG );

	for ( count = 0; count < tc990_WAIT_COUNTER ; count++ ) {
		value   = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG);
		if ( value == tc990_WAITING_FOR_HOST_REQUEST ) break;
		udelay(50);	
	}

	if ( count == tc990_WAIT_COUNTER ) {
		kfree(np->DownloadPageVirtual);		
		return 44;
	}
	udelay(5000);	
	// Acknowledge the status.
	writel(tc990_3XP_COMM_INT0, ioaddr + tc990_INT_STATUS_REG);
	writel(fileHeader->ExecuteAddress, ioaddr + tc990_HostTo3XP_COMM_1_REG);
	writel(tc990_BOOTCMD_RUNTIME_IMAGE, ioaddr + tc990_HostTo3XP_COMM_0_REG); 

	thisSectionHeaderLocation = sizeof(struct _tc990_FILE_HEADER )/4; 

	sections = fileHeader->NumSections;   

	for ( index = 0; index < sections; index++ ) {   
		fileSection = 
			(tc990_FILE_SECTION*)(tc990image +  thisSectionHeaderLocation );
		numBytesInThisSection = fileSection->NumBytes;
		tc3XPStartAddress = fileSection->tc3XPStartAddress;
		thisSectionDataLocation = sizeof( tc990_FILE_SECTION )/4;

		while( numBytesInThisSection ) {
			// Wait for 3cr990 to be ready to accept this section.
			for ( count = 0; count < tc990_WAIT_COUNTER; count++ ) {
				value = readl(ioaddr + tc990_INT_STATUS_REG );
				if ( value & tc990_3XP_COMM_INT0 ) break;
				udelay(50);
			}
			
			if ( count == tc990_WAIT_COUNTER ) {
				kfree(np->DownloadPageVirtual);		
				return 46;
			}
			udelay(5000);	
			writel(tc990_3XP_COMM_INT0, ioaddr + tc990_INT_STATUS_REG);
			value = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG);
			if ( value != tc990_WAITING_FOR_SEGMENT ){
				udelay(100); 
				kfree(np->DownloadPageVirtual);		
				return 47;
			} 

			// Calculate the location of the data.
			sectionDataBuffer = (u8*)( tc990image +  
										  thisSectionHeaderLocation +
										  thisSectionDataLocation );
			// 3cr990 is ready to accept section. Prepare the section.
			// Download entire segment if it fits
			tempLength  = numBytesInThisSection;
			np->DownloadPagePhysical = virt_to_bus(np->DownloadPageVirtual);
			memcpy(np->DownloadPageVirtual, sectionDataBuffer, tempLength);
			checksum = CalculateBufferChecksum(np->DownloadPageVirtual, tempLength);
			writel(tempLength, ioaddr + tc990_HostTo3XP_COMM_1_REG);
			writel(checksum, ioaddr + tc990_HostTo3XP_COMM_2_REG);
			writel(tc3XPStartAddress, ioaddr + tc990_HostTo3XP_COMM_3_REG);
			writel(0, ioaddr + tc990_HostTo3XP_COMM_4_REG);
			writel(np->DownloadPagePhysical, 
				   ioaddr + tc990_HostTo3XP_COMM_5_REG);

			// Tell 3cr990 it is ready.
			writel(tc990_BOOTCMD_SEGMENT_AVAILABLE, 
				   ioaddr + tc990_HostTo3XP_COMM_0_REG);

			// Update length and load address for next pass.
			numBytesInThisSection -= tempLength;
			tc3XPStartAddress += tempLength;
			
			// Update the section data location.
			thisSectionDataLocation += tempLength/4;
		}  

		// Update the section header location.
		numBytesInThisSection = fileSection->NumBytes;

		thisSectionHeaderLocation += ( numBytesInThisSection +
			sizeof( tc990_FILE_SECTION ) )/4;
	}

	udelay(5000);		
	// We have been able to download all the sections successfully.
	// Tell 3cr990 we are done and wait for a boot message.
		
	writel(tc990_BOOTCMD_DOWNLOAD_COMPLETE, ioaddr+tc990_HostTo3XP_COMM_0_REG);
	for ( count = 0; count < tc990_WAIT_COUNTER; count++ ) {
		value = readl(ioaddr + tc990_3XP2HOST_COMM_0_REG);
		if ( value == tc990_WAITING_FOR_BOOT )
			break;
		udelay(50);
	}
	if ( count == tc990_WAIT_COUNTER ) {
		kfree(np->DownloadPageVirtual);		
		return 49;
	}
	udelay(100); 
	// Program the original values back on IntEnable and IntMask
	writel(oldIntMask, ioaddr + tc990_INT_MASK_REG);
	writel(oldIntEnable, ioaddr + tc990_INT_ENABLE_REG);
	
	kfree(np->DownloadPageVirtual);		
	return 0;
}

// * A list of our installed devices, for removing the driver module. *
static struct device *root_net_dev = NULL;

// * we detect the cards we know about in slot order. *
static u16 pci_etherdev_probe(struct device *dev, struct pci_id_info pci_tbl[])
{
   	struct pci_dev *pdev;
	u16 cards_found = 0, pci_index = 0;
	unsigned char pci_bus, pci_device_fn, cacheSz;
	u8 pci_latency;

	if ( ! pcibios_present())
		return -ENODEV;

	for (;pci_index < 0xff; pci_index++) {
		u16 vendor, device = 0, pci_command;
		u16 chip_idx, irq = 0;
		u8 rev;
		long pciaddr = 0;
		long ioaddr;
		if (pcibios_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
							   &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
			break;

		pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor);

		if (vendor != tc990_VENDOR_ID) continue;
		pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device);

		for (chip_idx = 0; pci_tbl[chip_idx].device_id; chip_idx++)
			if (device == pci_tbl[chip_idx].device_id) break;
			
		if (pci_tbl[chip_idx].device_id == 0) continue; // * Compiled out! *

		pcibios_read_config_byte(pci_bus, pci_device_fn,
								 PCI_CLASS_REVISION, &rev); 

		pdev = pci_find_slot(pci_bus, pci_device_fn);
		pciaddr = pdev->base_address[1];
		
		irq = pdev->irq;

		if ((ioaddr = (long)ioremap(pciaddr & PCI_BASE_ADDRESS_MEM_MASK,
									0x200)) == 0) {
			printk(KERN_INFO "Failed to map PCI address %#lx.\n", pciaddr);
			continue;
		}

        // Read the cache line size from the PCI space.
		pcibios_read_config_byte(pci_bus, pci_device_fn,
								 PCI_CACHE_LINE_SIZE, &cacheSz);

        // Cache line size is in DWORDS, calculate bytes. 
        cacheSz *= 4;

        // Check the cache line size.
        if ((cacheSz % 0x10) || (!cacheSz)) {
			printk(KERN_INFO "Cacheline size not proper.\n");
            cacheSz = 0x20;	//default:Use the paragraph boundary for alignment.
        }  

		// Here the Command reg is read and modified if nec.
		pcibios_read_config_word(pci_bus, pci_device_fn,
								 PCI_COMMAND, &pci_command);
		if ( (pci_command & (PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE) )
			 != (PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE) ) {
			printk(KERN_INFO "The PCI BIOS has not enabled the"
				   " device at %d/%d.  Updating PCI command %4.4x->"
				   " | (PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE).\n",
				   pci_bus, pci_device_fn, pci_command);
			pci_command |= (PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE);
			pcibios_write_config_word(pci_bus, pci_device_fn,
									  PCI_COMMAND, pci_command);
		}

		dev = tc990probe(pci_bus, pci_device_fn, dev, ioaddr,
									   irq, chip_idx, cards_found);

		if (!dev) break;

		//PCI latency check (set)
		pcibios_read_config_byte(pci_bus, pci_device_fn,
								 PCI_LATENCY_TIMER, &pci_latency);
		if (pci_latency < min_pci_latency) {
			printk(KERN_INFO "  PCI latency timer (CFLT) is "
				   "unreasonably low at %d.  Setting to %d clocks.\n",
				   pci_latency, min_pci_latency);
			pcibios_write_config_byte(pci_bus, pci_device_fn,
									  PCI_LATENCY_TIMER, min_pci_latency);
		}
		dev = 0;
		cards_found++;
	}
	return cards_found ? 0 : -ENODEV;
}

#ifndef MODULE
u16 skel_tc990_probe(struct device *dev)
{
	printk(KERN_INFO "%s", version);
	return pci_etherdev_probe(dev, pci_tbl);
}
#endif

//Initialize boot record ring area
static u16 initRingZone( struct device *dev )
{
	struct tc990_private *np = (struct tc990_private *)dev->priv;
    u32 physicalLow = virt_to_bus(&np->pys->init); 
	u16 index;
	RX_FREE_DSC *rxFreeDescriptor; 
    u16 rxBufferSize =  ETHERNET_MAXIMUM_FRAME_SIZE + 4 + 2; 
	struct sk_buff *skb;

	np->ringPhysical = physicalLow;   

    // initialize the BootRecord and HostRingReadWrite registers

    np->pys->init.hostZeroWordUL = physicalLow + sizeof(HOST_INIT_S); 
    np->pys->init.hostZeroWordHiUL = 0;

    physicalLow += sizeof(HOST_INIT_S) + sizeof(u32); 
    np->pys->init.hostVarsPS = (HOST_VAR_S*)physicalLow;
    np->pys->init.hostVarsHiUL = 0;

    physicalLow += sizeof(HOST_VAR_S);
    // Save the address of Tx descriptor rings.
	np->vrt->TxLoRing.RingBase = (u32)bus_to_virt(physicalLow); 
    np->pys->init.hostTxLoStartUL =  physicalLow;
    np->pys->init.hostTxLoStartHiUL = 0;
    np->pys->init.hostTxLoSizeUL = TX_ENTRIES * sizeof(TX_DSC);
    
    physicalLow += np->pys->init.hostTxLoSizeUL;
	np->vrt->TxHiRing.RingBase = (u32)bus_to_virt(physicalLow); 
    np->pys->init.hostTxHiStartUL =  physicalLow;
    np->pys->init.hostTxHiStartHiUL = 0;
    np->pys->init.hostTxHiSizeUL = TX_ENTRIES * sizeof(TX_DSC);
    
    physicalLow += np->pys->init.hostTxHiSizeUL;
    // Save the address of RxLo descriptor ring. This ring is filled by f/w.
	np->vrt->RxLoRing.RingBase = (u32)bus_to_virt(physicalLow); 
    np->pys->init.hostRxLoStartUL = physicalLow;
    np->pys->init.hostRxLoStartHiUL = 0;
    np->pys->init.hostRxLoSizeUL = RX_ENTRIES * sizeof(RX_DSC);

    physicalLow += np->pys->init.hostRxLoSizeUL;
    // Save the address of RxHi descriptor ring. This ring is filled by f/w.
	np->vrt->RxHiRing.RingBase = (u32)bus_to_virt(physicalLow); 
    np->pys->init.hostRxHiStartUL = physicalLow;
    np->pys->init.hostRxHiStartHiUL = 0;
    np->pys->init.hostRxHiSizeUL = RX_ENTRIES * sizeof(RX_DSC);

    physicalLow += np->pys->init.hostRxHiSizeUL;
    // Save the address of Rx buffer ring. This ring is filled by driver.
	np->vrt->RxBuffRing.RingBase = (u32)bus_to_virt(physicalLow); 
    np->pys->init.hostRxFreeStartUL = physicalLow;
    np->pys->init.hostRxFreeStartHiUL = 0;
    np->pys->init.hostRxFreeSizeUL = RX_ENTRIES * sizeof(RX_FREE_DSC);

    physicalLow += np->pys->init.hostRxFreeSizeUL;
    // Save the command and response buffers
	np->vrt->CmdRing.RingBase = (u32)bus_to_virt(physicalLow); 
    np->pys->init.hostCtrlStartUL = physicalLow;      
    np->pys->init.hostCtrlStartHiUL = 0;
    np->pys->init.hostCtrlSizeUL = CMD_ENTRIES * sizeof(CMD_DSC);

    physicalLow += np->pys->init.hostCtrlSizeUL;
	np->vrt->RspRing.RingBase = (u32)bus_to_virt(physicalLow); 
    np->pys->init.hostRespStartUL = physicalLow;
    np->pys->init.hostRespStartHiUL = 0;
    np->pys->init.hostRespSizeUL = RESPONSE_ENTRIES * 
                                          sizeof(RESPONSE_DSC);

	np->pys->var.hvWriteS.regRxBuffWriteUL =
	     ( RX_ENTRIES - 1 ) * sizeof(RX_FREE_DSC);

    // Initialize counters  (from ResetRingStructures) 
    np->vrt->RxLoRing.LastWriteUL = 0;        
    np->vrt->RxHiRing.LastWriteUL = 0;        
    np->vrt->RxBuffRing.LastWriteUL = 0;
    np->vrt->TxLoRing.LastWriteUL = 0;        
    np->vrt->TxHiRing.LastWriteUL = 0;        
    np->vrt->CmdRing.LastWriteUL = 0;

    np->vrt->TxLoRing.LastReadUL = 0;        
    np->vrt->TxHiRing.LastReadUL = 0;        
    np->vrt->TxLoRing.WriteRegister = tc990_HostTo3XP_COMM_3_REG;   
    np->vrt->TxHiRing.WriteRegister = tc990_HostTo3XP_COMM_1_REG; 
    np->vrt->TxLoRing.PacketPendingNo = 0;
    np->vrt->TxHiRing.PacketPendingNo = 0;

    np->vrt->TxLoRing.NoEntries = TX_ENTRIES;
    np->vrt->TxHiRing.NoEntries = TX_ENTRIES;
    np->vrt->CmdRing.NoEntries = CMD_ENTRIES;
    np->vrt->RspRing.NoEntries = RESPONSE_ENTRIES;

	np->Slot = kmalloc (((RX_ENTRIES-1)*(sizeof(struct SLOT))), GFP_KERNEL);
    if (!np->Slot) return 50;
	memset( np->Slot, 0, ((RX_ENTRIES -1)*(sizeof(struct SLOT)) ) );  

	/// Alloc slots
	// Put the addresses of all the receive buffers in the receive buffer
    // descriptor ring.
    for ( index = 0 ; index < RX_ENTRIES - 1; index++ ) {
		if ((skb = dev_alloc_skb(rxBufferSize)) == NULL) {
			printk(KERN_INFO "3c990: Allocate rx skb failed.  Not enough memory.  Try reducing RX_ENRIES value and recompiling this module. \n"); 
			return 52; 
		}
		skb->dev = dev;
		skb_reserve(skb, 4);	// 32 bit align the IP header 
        // Save the information in the slot.
        np->Slot[index].VirtualAddressLo = (u32)skb->tail;
        np->Slot[index].VirtualAddressHi = 0;
        np->Slot[index].skb = (u32)skb;
        np->Slot[index].PhysicalAddressLo = 
			(u32)virt_to_bus((void*)np->Slot[index].VirtualAddressLo); 
        np->Slot[index].PhysicalAddressHi = 0;
        np->Slot[index].BufferLength = rxBufferSize;
    }
    // Indicate all the buffers are free. 
    rxFreeDescriptor = (RX_FREE_DSC*)np->vrt->RxBuffRing.RingBase; 
    for(index = 0; index < RX_ENTRIES - 1; index++ ) {
        rxFreeDescriptor->PhysicalAddressLo = 
                np->Slot[index].PhysicalAddressLo;
        rxFreeDescriptor->PhysicalAddressHi =
                np->Slot[index].PhysicalAddressHi;
		rxFreeDescriptor->VirtualAddressLo = index;
        rxFreeDescriptor->VirtualAddressHi = 0;
        rxFreeDescriptor++;
    }
	return 0;
}

static struct device *tc990probe ( u16 pci_bus, u16 pci_devfn, 
		  struct device *dev, long ioaddr, u16 irq, u16 chip_id, u16 card_idx )
{
	struct tc990_private *np;
	u16 errVal;
    u32 stationAddressLo = 0;
    u16 stationAddressHi = 0;

	dev = init_etherdev(dev, sizeof(struct tc990_private));  

	dev->base_addr = ioaddr;
	dev->irq = irq;

	// * Make certain the descriptor lists are aligned. *
	np = (void *)(((long)kmalloc (sizeof(*np), GFP_KERNEL) + 15) & ~15);
	if (!np) { bailOutNow(dev, 5); return NULL; }
	memset( np, 0, sizeof(*np) );  
	dev->priv = np;

	np->pys = 
		(struct tc990phys *) kmalloc (sizeof(struct tc990phys), GFP_KERNEL);
	np->vrt =
		(struct tc990virt *) kmalloc (sizeof(struct tc990virt), GFP_KERNEL); 

	if ( !np->pys || !np->vrt ) { bailOutNow(dev, 15); return NULL; }

	memset( np->pys, 0, sizeof(struct tc990phys) );  
	memset( np->vrt, 0, sizeof(struct tc990virt) );  
	memset( &np->stats, 0, sizeof(struct net_device_stats) );  

	errVal = initRingZone(dev); 
	if (errVal) { bailOutNow(dev, 25); return NULL; }

	np->next_module = root_net_dev;
	root_net_dev = dev;
	np->pci_bus = pci_bus;
	np->pci_devfn = pci_devfn;
	np->chip_id = chip_id;

	// * The chip-specific entries in the device structure. *
	dev->open = &tc990_open;
	dev->hard_start_xmit = &start_tx;
	dev->stop = &tc990_close;
	dev->get_stats = &get_stats;
	dev->set_multicast_list = &set_rx_mode;

	errVal = DownloadRunTimeImage(dev);
	if (errVal) { bailOutNow(dev, 50); return NULL; }
   	errVal = Downloadbootrecord(dev, tc990_WAITING_FOR_BOOT);
	if (errVal) { bailOutNow(dev, 70); return NULL; }

    // Set the maximum packet size for the firmware 
    errVal = issueCommand( dev, tc990_CMD_MAX_PKT_SIZE_WRITE,
					  0x800, 0, 0, NULL, NULL, NULL, 1);
	if (errVal) { bailOutNow(dev, 77); return NULL; }

    // Read the station address from the adapter.
    errVal = issueCommand( dev, tc990_CMD_STATION_ADR_READ,
					  0, 0, 0, &stationAddressHi, &stationAddressLo, NULL, 1);
	if (errVal) { bailOutNow(dev, 78); return NULL; }
    dev->dev_addr[0] = ((u8*)(&stationAddressHi))[1];
    dev->dev_addr[1] = ((u8*)(&stationAddressHi))[0];
    dev->dev_addr[2] = ((u8*)(&stationAddressLo))[3];
	dev->dev_addr[3] = ((u8*)(&stationAddressLo))[2];
	dev->dev_addr[4] = ((u8*)(&stationAddressLo))[1];
	dev->dev_addr[5] = ((u8*)(&stationAddressLo))[0];

	return dev;
}



static int tc990_open(struct device *dev)
{
	struct tc990_private *np = (struct tc990_private *)dev->priv;
	long ioaddr = dev->base_addr;
	u16 retVal = 0;

	if (request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev))
		return -EAGAIN;  
	MOD_INC_USE_COUNT;
	dev->tbusy = 0;
	dev->interrupt = 0;
	np->interruptUp = 1;
	dev->start = 1;
  
	if ( (force < 0) || (force > 4) ) 
		printk (KERN_WARNING "3c990: Bad value -- ""force=%x"".  Autonegotiation default used instead. \n", force);
	else
		retVal = issueCommand(dev, tc990_CMD_XCVR_SELECT, 
						 force, 0, 0, NULL, NULL, NULL, 0);
	retVal |= issueCommand(dev, tc990_CMD_TX_ENABLE, //enableTransmitOnAdapter
					 0, 0, 0, NULL, NULL, NULL, 1);
    retVal |= issueCommand(dev, tc990_CMD_RX_ENABLE, //same for receive
	  0, 0, 0, NULL, NULL, NULL, 1);
	if (retVal) return -EAGAIN;  //If something didn't work, bail out.
    writel(tc990_ENABLE_ALL_INT, ioaddr + tc990_INT_ENABLE_REG); //go
    writel(tc990_UNMASK_ALL_INT, ioaddr + tc990_INT_MASK_REG );
	return 0;
}

static int start_tx(struct sk_buff *skb, struct device *dev)
{
   	struct tc990_private *np = (struct tc990_private *)dev->priv;
	unsigned entry, processed, available;
	unsigned long flags;
	TX_RING *pTxRing = &np->vrt->TxLoRing;	

	   // * Block a timer-based transmit from overlapping.  This could better 
	   //be done with atomic_swap(1, dev->tbusy), but set_bit() works as well.*
	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
		return 1;
	}
	save_flags(flags);
	cli();

	// * Calculate the next Tx descriptor entry. *
	entry = (np->vrt->TxLoRing.LastWriteUL / (sizeof (TX_DSC))); 
	processed = (np->vrt->TxLoRing.LastReadUL / (sizeof (TX_DSC)));

	if (processed > entry)
		available = processed - entry;
	else
		available = TX_ENTRIES - (entry - processed);

	//Fill Frame fields 
	np->pys->txLo[entry].pktFlagsUC = 0x1;     
	np->pys->txLo[entry].pktReservedUC = 0x1;  
	np->pys->txLo[entry].pktLenUW = 0;         
	np->pys->txLo[entry].pktHostAddrLoUL = (u32)skb; 
	np->pys->txLo[entry].pktHostAddrHiUL = 0;
	np->pys->txLo[entry].pktSpareUL = 0;

	np->pys->txLo[entry+1].pktHostAddrLoUL = virt_to_bus(skb->data); 
	np->pys->txLo[entry+1].pktLenUW = skb->len; //skb->len in bytes 

	np->vrt->TxLoRing.LastWriteUL += sizeof(TX_DSC);
	if (np->vrt->TxLoRing.LastWriteUL >= (TX_ENTRIES * sizeof(TX_DSC)))  
		np->vrt->TxLoRing.LastWriteUL = 0;

	np->vrt->TxLoRing.LastWriteUL += sizeof(TX_DSC);
	if (np->vrt->TxLoRing.LastWriteUL >= (TX_ENTRIES * sizeof(TX_DSC)))  
		np->vrt->TxLoRing.LastWriteUL = 0;

	restore_flags(flags);

    // Tell 3cr990: do some work now
	writel(np->vrt->TxLoRing.LastWriteUL, dev->base_addr + pTxRing->WriteRegister);

	if (available < 4) 	return 0;

	clear_bit(0, (void*)&dev->tbusy);		// * Typical path *
	return 0;
}

// * The interrupt handler does all of the Rx thread work and cleans up
//   after the Tx thread. * //
static void intr_handler(int irq, void *dev_instance, struct pt_regs *rgs)
{
	struct device *dev = (struct device *)dev_instance;
	struct tc990_private *np;
	volatile u32 intr_status; 
	volatile u32 *rxReadIndex;
	volatile u32 *rxWriteIndex;
	long ioaddr; 
	u16 count;	
	u16 entry = 0, processed, available;	
	ioaddr = dev->base_addr;
	np = (struct tc990_private *)dev->priv;

#if defined(__i386__)  
	// * A lock to prevent simultaneous entry bug on Intel SMP machines. *
	if (test_and_set_bit(0, (void*)&dev->interrupt)) {
		printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
			   dev->name);
		dev->interrupt = 0;	// * Avoid halting machine. *
		return;
	}
#else
	if (dev->interrupt) {
		printk(KERN_ERR "%s: Re-entering the interrupt handler.\n", dev->name);
		return;
	}
	dev->interrupt = 1;
#endif

	// Mask all the interrupts, so that we don't get an interrupt
	writel(tc990_MASK_ALL_INT, ioaddr + tc990_INT_MASK_REG);

	intr_status = readl(ioaddr + tc990_INT_STATUS_REG);//wasIntrStatus

	do {
		// * Acknowledge all of the current interrupt sources ASAP. *
		writel(intr_status, ioaddr + tc990_INT_STATUS_REG);

		/// Check for new Receive Hi data
		rxReadIndex =  &np->pys->var.hvWriteS.regRxHiReadUL;
        rxWriteIndex = &np->pys->var.hvReadS.regRxHiWriteUL;
        if(*rxReadIndex != *rxWriteIndex)
			ProcessReceive( dev, &np->vrt->RxHiRing, rxReadIndex, rxWriteIndex );
		/// Check for new Receive Lo data
		rxReadIndex =  &np->pys->var.hvWriteS.regRxLoReadUL;
        rxWriteIndex = &np->pys->var.hvReadS.regRxLoWriteUL;		
		if (*rxReadIndex != *rxWriteIndex)
			ProcessReceive( dev, &np->vrt->RxLoRing, rxReadIndex, rxWriteIndex );
		
        /// Check send complete Lo queue
		entry = (np->vrt->TxLoRing.LastWriteUL / (sizeof(TX_DSC)));
		processed = (np->vrt->TxLoRing.LastReadUL / (sizeof (TX_DSC)));
		if (processed > entry)
			available = processed - entry;
		else
			available = TX_ENTRIES - (entry - processed);
		count = TX_ENTRIES - available - 4; //Buffer by a couple for elbow room
		if (count > 32) count = 32;
		available = available + count;

		while ( (count>0) ){
			if (np->pys->txLo[processed].pktFlagsUC){
				// * Free the original skb. * 
				dev_free_skb((void*)np->pys->txLo[processed].pktHostAddrLoUL);
				np->pys->txLo[processed].pktHostAddrLoUL = 0;
			}
			//updateTxRingIndex  
			np->vrt->TxLoRing.LastReadUL += sizeof(TX_DSC);
			if (np->vrt->TxLoRing.LastReadUL >= (TX_ENTRIES * sizeof(TX_DSC)))  
				np->vrt->TxLoRing.LastReadUL = 0;
			count--;
			processed = (np->vrt->TxLoRing.LastReadUL / (sizeof (TX_DSC)));
		}
		// * Note the hysteresis in marking the queue non-full. *
		if (dev->tbusy){
			if (available > 30){ // * The ring is no longer full, clear tbusy.*
				clear_bit(0, (void*)&dev->tbusy);
				mark_bh(NET_BH);
			}
		}
        /// Check if the response write index has changed.
        if ( np->pys->var.hvWriteS.regRespReadUL != 
			 np->pys->var.hvReadS.regRespWriteUL ) 
			ProcessResponse( np, np->pys->var.hvWriteS.regRespReadUL, 0 );
		intr_status = readl(ioaddr + tc990_INT_STATUS_REG);
	} while (intr_status);
	
    writel(tc990_UNMASK_ALL_INT, ioaddr + tc990_INT_MASK_REG );	
#if defined(__i386__)
	clear_bit(0, (void*)&dev->interrupt);
#else
	dev->interrupt = 0;
#endif
	return;
}

static void stats_handler( struct tc990_private  *np )
{	
	RESPONSE_DSC* responseDescriptor;
	
	responseDescriptor = (RESPONSE_DSC*) //tx
		(np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL);
	if ( responseDescriptor->nrml.Flags & RESPONSE_DSC_ERROR_SET ) {
		printk(KERN_WARNING "3c990: Stats error flag set.\n" ); 
		return;
	}
	np->stats.tx_packets = responseDescriptor->tx.pkt; 
#if LINUX_VERSION_CODE > 0x20024
	np->stats.tx_bytes = responseDescriptor->tx.byte;
#endif

	UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, 
				  sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES );
	responseDescriptor = (RESPONSE_DSC*) //txMore
		(np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL );

	UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, 
				  sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES );
	responseDescriptor = (RESPONSE_DSC*) //txCrit
		(np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL );
	np->stats.tx_carrier_errors = responseDescriptor->txCrit.carrierLost;
	np->stats.collisions = responseDescriptor->txCrit.multCollision;
	np->stats.tx_errors	= responseDescriptor->txCrit.carrierLost;		
	
	UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, 
				  sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES );
	responseDescriptor = (RESPONSE_DSC*) //txRx
		(np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL );
	np->stats.rx_packets = responseDescriptor->txRx.rxPkt;
#if LINUX_VERSION_CODE > 0x20024
	np->stats.rx_bytes = responseDescriptor->txRx.rxByte;
#endif
	
	UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, 
				  sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES );
	responseDescriptor = (RESPONSE_DSC*) //rx
		(np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL );	
	np->stats.rx_fifo_errors = responseDescriptor->rx.fifoOverrun;
	np->stats.rx_errors	= responseDescriptor->rx.fifoOverrun;
	np->stats.rx_errors	+= responseDescriptor->rx.badSSD;
	np->stats.rx_errors	+= responseDescriptor->rx.crcError;
	np->stats.rx_crc_errors	= responseDescriptor->rx.crcError;
	
	UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, 
				  sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES );
	responseDescriptor = (RESPONSE_DSC*) //rxGeneral
		(np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL );
	np->stats.rx_length_errors = responseDescriptor->rxGeneral.oversize;	

	UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, 
				  sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES );
	responseDescriptor = (RESPONSE_DSC*) //rxEtc
		(np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL );
	
	UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL, 
				  sizeof( RESPONSE_DSC ), RESPONSE_ENTRIES );
	responseDescriptor = (RESPONSE_DSC*) //dpm7
		(np->vrt->RspRing.RingBase + np->pys->var.hvWriteS.regRespReadUL );

	if (np->interruptUp == 1) wake_up_interruptible( &np->statsWait );
}

static struct enet_statistics *get_stats(struct device *dev)
{
	u16 errVal = 0;  //general use variable
	struct tc990_private *np = (struct tc990_private *)dev->priv;

	if (np->interruptUp == 1) {
		errVal = issueCommand( dev, (u16)tc990_CMD_READ_STATS,
							   0, 0, 0, NULL, NULL, NULL, 0 ); 
		if (errVal) printk(KERN_CRIT "3c990: Stats command gone BAD." );
		interruptible_sleep_on( &np->statsWait );
	}
	else {
		errVal = issueCommand( dev, (u16)tc990_CMD_READ_STATS,
							   0, 0, 0, NULL,  NULL,  NULL,  1 ); 
		if (errVal) printk(KERN_CRIT "3c990:Stats command gone BAD." );
	}
	return &np->stats;
}

#define POLYNOMIAL 0x04c11db7 
static void set_rx_mode(struct device *dev)
{
	u32 filter = 0;
	u32 Crc, Carry;
	u16 i, j;
	u8 ThisByte;
	u8 Address[6];
	u32 hashFilterArray[(tc990_MULTICAST_BITS + 1) / 32];
	u32 hashBit, count;

	if (dev->flags & IFF_PROMISC) {	   // * Set promiscuous. *  log net taps. *
		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", dev->name);
        filter |= tc990_RX_FILT_PROMISCUOUS;
	} 
	else if (dev->flags & IFF_ALLMULTI) {
	//	   ||  (dev->flags & IFF_ALLMULTI)) {       
		filter |= tc990_RX_FILT_DIRECTED; 
		filter |= tc990_RX_FILT_ALL_MULTICAST;
		filter |= tc990_RX_FILT_BROADCAST;
	}
	else if (dev->flags & IFF_MULTICAST) {
		filter |= tc990_RX_FILT_DIRECTED;
		filter |= tc990_RX_FILT_HASH_MULTICAST;
		filter |= tc990_RX_FILT_BROADCAST;
		///SetMulticastAddressList on the adapter.
		// Clear all bits   
		memset( hashFilterArray, 0, sizeof( hashFilterArray ) ); 
		// Calculate hash bit for eash address and stuff in bit array
		for ( count = 0; count < dev->mc_count; count++ ) {			
			//CalculateHashBits
			// Compute CRC for the address value. 
			Crc = 0xffffffff;  // initial value 
			// For each byte of the address
			for ( i = 0; i < 6; i++ ) {
				ThisByte = Address[i];
				// For each bit in the byte
				for ( j = 0; j < 8; j++ ) {
					Carry = ((Crc & 0x80000000) ? 1 : 0) ^ (ThisByte & 0x01);
					Crc  <<= 1;
					ThisByte >>= 1;
					if ( Carry )
						Crc = (Crc ^ POLYNOMIAL) | Carry;
				}
			}
			// filter bit position. 
			hashBit = (u16)(Crc & tc990_MULTICAST_BITS) ;			
			hashFilterArray[ hashBit / 32 ] |=  ( 1 << hashBit % 32 ) ;
		}
		i = issueCommand( dev, (u16)tc990_CMD_MCAST_HASH_MASK_WRITE,
						  (u16)2, hashFilterArray[0], hashFilterArray[1], 
						  NULL,  NULL,  NULL,  0 ); 
	}
	else {
		filter |= tc990_RX_FILT_DIRECTED;
		filter |= tc990_RX_FILT_BROADCAST;
	}
	i = issueCommand(dev, (u16)tc990_CMD_RX_FILT_WRITE,
					 (u16)filter, 0, 0, NULL, NULL, NULL, 0); 
}

static int tc990_close(struct device *dev)
{
	u16 errVal; 
	long ioaddr = dev->base_addr; 
	struct tc990_private *np = (struct tc990_private *)dev->priv;

	dev->start = 0;
	dev->tbusy = 1;
	np->interruptUp = 0;

	// * Disable interrupts by clearing the interrupt mask. *
	writel(tc990_MASK_ALL_INT, ioaddr + tc990_INT_MASK_REG);
	// * Stop the chip's Tx and Rx processes. *
	errVal = issueCommand(dev, tc990_CMD_TX_DISABLE,
						  0, 0, 0, NULL, NULL, NULL, 1);
    errVal |= issueCommand(dev, tc990_CMD_RX_DISABLE,
						   0, 0, 0, NULL, NULL, NULL, 1);
	if (errVal) printk(KERN_CRIT 
	  "3c990: Unaccounted for failure of command to network adapter. DANGER!");
	free_irq(dev->irq, dev);
	MOD_DEC_USE_COUNT;
	return 0;
}



#ifdef MODULE
u16 init_module(void)
{
	printk(KERN_INFO "%s", version);
	return pci_etherdev_probe(NULL, pci_tbl);
}

void cleanup_module(void)
{
	struct device *next_dev;
	u16 index;
	u16 processed; 

	// * No need to check MOD_IN_USE, as sys_delete_module() checks. *
	while (root_net_dev) {
		struct tc990_private *np=(struct tc990_private *)root_net_dev->priv;
		while (np->vrt->TxLoRing.LastReadUL != 
			   np->pys->var.hvReadS.regTxLoReadUL) {
			processed = (np->vrt->TxLoRing.LastReadUL / (sizeof (TX_DSC)));
			if (np->pys->txLo[processed].pktFlagsUC){
				dev_free_skb((void*)np->pys->txLo[processed].pktHostAddrLoUL);
				np->pys->txLo[processed].pktHostAddrLoUL = 0;
			}
			np->vrt->TxLoRing.LastReadUL += sizeof(TX_DSC);
			if (np->vrt->TxLoRing.LastReadUL >= (TX_ENTRIES * sizeof(TX_DSC)))
				np->vrt->TxLoRing.LastReadUL = 0;
		}

		next_dev = np->next_module;
		unregister_netdev(root_net_dev);
		iounmap((char *)root_net_dev->base_addr);

		// Free all the receive buffers in the receive buffer descriptor ring.
		for  ( index = 0 ; index < RX_ENTRIES - 1; index++ ) {
			if (np->Slot[index].skb != (u32)NULL) {
				dev_free_skb((void*)np->Slot[index].skb);
			}
			np->Slot[index].VirtualAddressLo = 0;
			np->Slot[index].PhysicalAddressLo = 0;
		}

		kfree(np->Slot);		
		kfree(np->pys);
		kfree(np->vrt);
		kfree(np);
		kfree(root_net_dev);

		root_net_dev = next_dev;
	}
}
#endif  // * MODULE *

static void bailOutNow (struct device *dev, u16 state)
{
	u16 index;
	struct tc990_private *np=(struct tc990_private *)dev->priv;

	printk(KERN_INFO"3c990: There appear to be inadequate resources at this time and on this machine for this driver.  Killing further initialization. \n" );

	if (dev->flags & IFF_UP) { // * Some kernel versions close automatically. *
		tc990_close(dev);
		dev->flags &= ~(IFF_UP|IFF_RUNNING);
	}
	if (state > 25) root_net_dev = np->next_module;
	unregister_netdev(dev);
	iounmap((char *)dev->base_addr);

	if (state > 10){
		// Free all the receive buffers.
		if (np->Slot){
			for( index = 0 ; index < RX_ENTRIES - 1; index++ ) {
				if (np->Slot[index].skb != (u32)NULL) {
					dev_free_skb((void*)np->Slot[index].skb);
				}
				np->Slot[index].VirtualAddressLo = 0;
				np->Slot[index].PhysicalAddressLo = 0;
			}
		}
		kfree(np->Slot);		
		kfree(np->pys);
		kfree(np->vrt);	
		kfree(np);
	}
	kfree(dev);
	return;
}

static void ProcessReceive(struct device *dev, tc990_RING *pRxRing,
					volatile u32 *regRxReadUL, volatile u32 *regRxWriteUL)
{
	struct sk_buff *skb;
	RX_DSC  *rxDescriptor ;
	u32 slotIndex, rxReadIndex = *regRxReadUL, writeVal = *regRxWriteUL;
    u16 rxBufferSize =  ETHERNET_MAXIMUM_FRAME_SIZE + 4 + 2; 
	struct tc990_private *np = (struct tc990_private *)dev->priv;

	while ( rxReadIndex != writeVal ){
		rxDescriptor = (RX_DSC*)(pRxRing->RingBase + rxReadIndex);
		slotIndex   = rxDescriptor->VirtualAddressLo;

		// Handle rx
		if ( rxDescriptor->RxStatus ) {
			printk (KERN_INFO "3c990: rx_error \n"); 
			continue; 
		}
		else {
			skb = (struct sk_buff*)np->Slot[slotIndex].skb;
			if (skb){  
				skb_put(skb, rxDescriptor->FrameLength);
				skb->protocol = eth_type_trans(skb, dev);
				netif_rx(skb);
				dev->last_rx = jiffies;
			}
		}
		UPDATE_INDEX( rxReadIndex, sizeof( RX_DSC ), RX_ENTRIES );
		writeVal = *regRxWriteUL;
	}
	// Refill buffer slots.
	rxReadIndex = *regRxReadUL;
	while ( rxReadIndex != writeVal ) {
		/// Give this receive descriptor back to firmware.
		rxDescriptor = (RX_DSC*)(pRxRing->RingBase + rxReadIndex);
		slotIndex   = rxDescriptor->VirtualAddressLo;

		UPDATE_INDEX( rxReadIndex, sizeof( RX_DSC ), RX_ENTRIES );
		*regRxReadUL = rxReadIndex ;

		if ((skb = dev_alloc_skb(rxBufferSize)) == NULL) {
			printk(KERN_CRIT "3c990: Memory squeeze (alloc rx buff failed) Danger!\n"); 
			return; // That hurts.
		}
		skb->dev = dev;
		skb_reserve(skb, 4);

        // Save the information in the slot.
        np->Slot[slotIndex].VirtualAddressLo = (u32)skb->tail;
        np->Slot[slotIndex].VirtualAddressHi = 0;
        np->Slot[slotIndex].skb = (u32)skb;
        np->Slot[slotIndex].PhysicalAddressLo = 
			virt_to_bus((void*)np->Slot[slotIndex].VirtualAddressLo); 
        np->Slot[slotIndex].PhysicalAddressHi = 0;
        np->Slot[slotIndex].BufferLength = rxBufferSize;
	    ReleaseBufferToFirmware(dev, slotIndex );
    }
} 

//Routine Description: Returns the buffer at SlotIndex to the firmware.
static void ReleaseBufferToFirmware( struct device *dev, u32 SlotIndex )
{
    RX_FREE_DSC *rxFreeDescriptor;
	HOST_VAR_S *pHostVar;
	struct tc990_private *np = (struct tc990_private *)dev->priv;    
	
	pHostVar = (HOST_VAR_S*)bus_to_virt((u32)np->pys->init.hostVarsPS);
	
    rxFreeDescriptor = (RX_FREE_DSC*) 
		(np->vrt->RxBuffRing.RingBase + pHostVar->hvWriteS.regRxBuffWriteUL);
	
    rxFreeDescriptor->PhysicalAddressLo =np->Slot[SlotIndex].PhysicalAddressLo;
    rxFreeDescriptor->PhysicalAddressHi =np->Slot[SlotIndex].PhysicalAddressHi;
    rxFreeDescriptor->VirtualAddressLo = SlotIndex;
    rxFreeDescriptor->VirtualAddressHi = 0; 
    
    UPDATE_INDEX( pHostVar->hvWriteS.regRxBuffWriteUL, 
				  sizeof( RX_FREE_DSC ), RX_ENTRIES );
}

static u8 ProcessResponse( struct tc990_private *np, u32 responseReadIndex, 
						   u16 Command )
{
    RESPONSE_DSC* responseDesc;
	
    while ( responseReadIndex !=  np->pys->var.hvReadS.regRespWriteUL ){
		responseDesc = (RESPONSE_DSC*)
			(np->vrt->RspRing.RingBase + responseReadIndex);
		
		if ( Command == responseDesc->nrml.Command ) 
			return tc990_STATUS_CMD_FOUND; 
		if (responseDesc->nrml.Command == tc990_CMD_READ_STATS){ 
			stats_handler( np );
			responseReadIndex =  np->pys->var.hvWriteS.regRespReadUL;
			return 0;
		}
		else {
			if ( responseDesc->nrml.Flags & RESPONSE_DSC_ERROR_SET ) 
				printk(KERN_WARNING "tc990: Response error bit set \n");
			UPDATE_INDEX( np->pys->var.hvWriteS.regRespReadUL,
						  sizeof(RESPONSE_DSC), RESPONSE_ENTRIES );
			responseReadIndex =  np->pys->var.hvWriteS.regRespReadUL;
		}
    }
	return 0;
}

/*
 * Local variables:
 *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -I/usr/src/linux/include -O6 -c 3c990.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
 *  SMP-compile-command: "gcc -D__SMP__ -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -I/usr/src/linux/include -O6 -c 3c990.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
 *  c-indent-level: 4
 *  c-basic-offset: 4
 *  tab-width: 4
 * End:
 */


