 /* jt1lin.c: Linux driver for Level One Communications LXT controller family  */
/*
 * NOTICE: this version tested with kernels 2.0.x and later only!
 *
 * Written 1998 by Douglas Greiman
 * Updated 1999 by Antonio Torrini
 * Maintained by Antonio Torrini (atorrini@level1.com)
 *
 * Copyright 1998 Level One Communications, Inc
 *
 * This software may be used and distributed according to the terms
 * of the GNU Public License, incorporated herein by reference.
 *
 * The maintainer may be reached as atorrini@level1.com, or C/O
 *      Level One Communications, Inc., Austin Design Center
 *      505 E. Huntland Drive
 *      Suite 550
 *      Austin, TX 78752
 *      (512) 407-2138 
 *
 * This file is a device driver for the Level One Communications
 * LXT1000 family of Gigabit Ethernet controllers.
 *
 * This code uses 'word' to mean a 16 bit value, 
 * and 'dword' to mean a 32 bit value.
 */

static const char *version = 
"jt1lin.c:v1.03 09/15/99 Level One Communications, Inc (www.level1.com)\n";
#define VERSION 0x0103
/*
				Theory of Operation

I. Board Compatibility

This device driver is designed for Level One Communications NetCelerator Family
of Gigabit Ethernet adapters.

II. Board-specific settings

PCI bus devices are configured by the system at boot time, so no jumpers
need to be set on the board.  The driver will function correctly if the
device's interrupt is shared with another device.  For maximum performance,
the system BIOS preferably should assign the PCI INTA 
signal to an otherwise
unused system IRQ line.

III. Driver operation

IIIa. I/O Modes

The NetCelerator architecture supports three different mechanisms for
I/O: 

1. Programmed I/O (PIO) mode, commonly used by low-end adapters. Note
that this mode is no longer mantained and supported.
2. Packet Descriptor List (PDL) mode, a DMA descriptor-based mechanism
similar to that used by the Intel EtherExpress Pro and other adapters.
3. Packet Descriptor List (PDC) mode, aka Packet Propulsion.

This driver supports three receive modes: PIO, PDL, and PDC.  Each
mode is mutually exclusive.  That is, all receives are done using only
PIO, or only PDL, or only PDC.

This driver supports two transmit modes: PIO, and BusMaster.  The
BusMaster transmit mode dynamically switches between PDC and PDL based
on the size of the packet to transmit.  Small packets are copied into
PDC buffers, while large packets are indirectly referenced by PDLs.

IIIb. Queues

This driver makes use of fixed-size circular queues (the JT_QUEUE
struct) for both PDL and PDC I/O modes.  Each queue contains a fixed
number of elements.  Each element is a pointer to either a PDL or a
PDC buffer, depending on the queue and the current mode of operation.
Elements are always queued at the tail and dequeued from the head.

For receive, there are two queues, RxPDxAvailableQueue and
RxPDxInProgressQueue.  The In Progress queue contains pointers to
PDL/PDC's that have been given to the controller as receive commands.
The Available queue contains PDL/PDC's which are not currently in use
by the controller.

For transmit, there are three queues, TxPDCAvailableQueue,
TxPDLAvailableQueue and TxPDxInProgressQueue.  The In Progress queue
contains pointers to PDL/PDC's that have been given to the controller
as transmit commands.  The Available queues contain either PDLs or
PDCs which are not currently in use by the controller.  Since the In
Progress queue contains a heterogeneous mixture of PDC and PDL
pointers, some means is needed to distinguish whether a given pointer
points to a PDC or a PDL.  Thus, the least significant bit
of the pointer is set to 1 for PDL pointers, and left at 0 for PDC
pointers.  Since PDLs and PDCs are always at least 32 byte aligned,
the least significant bit would otherwise always be 0.

IIIc. Synchronization

The driver can be compiled either in standard mode or in performance
mode, and the synchronization method depends on which one is used.

In standard mode, the driver runs as two independent, single-threaded 
flows of control.	One is the send-packet routine, which enforces 
single-threaded use by the 'pDev->tbusy' flag.  The other thread is 
the interrupt handler, which is single threaded by the hardware and 
other software.

The send packet thread has partial control over the Tx ring and
'pDev->tbusy' flag.  It sets the tbusy flag whenever it's queuing a Tx
packet.  While queueing the packet for transmit, the send packet
thread sets the 'pDevInstance->TxBusy' flag if it determines that the
controller cannot queue any more transmits, for any reason.  Once a
packet is queued, the send packet thread clears the 'pDev->tbusy' flag
unless the pDevInstance->TxFull flag has been set.

The interrupt handler has exclusive control over the Rx ring and
partial control over the Tx ring.  After being notified that a
transmit(s) has completed, the interrupt handler removes PDL/PDCs from
the In Progress queue and adds them to the PDC and PDL Available
queues.  If the 'pDevInstance->TxFull' flag is set, it clears both the
'TxFull' and 'tbusy' flags.

In performance mode, the driver is multi-threaded and synchronization 
is achieved by means of spin-locks inside of the send routine and the
interrupt handler.

IIId. PDL Receive

  Call Tree:
    jtDevOpen
      +--jtRxPDLCreate
		+--jtRxPDLInitialize
      |    +--jtRxPDLReplenish
      +--jtRxStart

    jtDevInterrupt
      +--jtRxPDLReceive
           +--jtRxPDLReplenish

    jtDevClose
		+--jtRxStop
	   +--jtRxPDLFlush
      +--jtRxPDLDestroy
	
jtRxPDLCreate first allocates and initializes the queues
RxPDxAvailableQueue and RxPDxInProgressQueue.  Then the function
allocates a single contiguous block of memory large enough to hold all
receive PDLs.  

jtRxPDLInitialize divides the block into individual PDLs, which are
aligned at cache line boundaries so that the controller can use PCI
Memory Write and Invalidate commands. Pointers to each PDL are
inserted into the Available queue.

jtRxPDLReplenish sends PDL receive commands to the controller.  The
commands are sent one at a time until the controller indicates that it
can't accept more commands.  Each PDL to be sent is dequeued from the
Available queue and initialized with a header indicating that the PDL
owns a single fragment.  An sk_buff large enough for a maximum size
packet is allocated.  The physical address of the sk_buff's data area
is stored in the first fragment entry of the PDL.  For bookkeeping
purposes, the virtual address of the sk_buff structure itself is
stored in the PDL's second fragment entry.  This fragment entry is
ignored by the controller.  Once initialized, the PDL is inserted into
the InProgress queue.  Finally, the address of the PDL receive command
is sent to the controller.

jtRxPDLReceive is called by the interrupt handler each time the
controller receives one or more packets.  Since the controller
receives packets in order, the function knows that the packet(s)
received have been placed into the PDLs at the head of the InProgress
queue.  The function repeatedly removes the PDL from the head of the
InProgress queue and processes it, until each received packet has been
processed.  First, the address of the sk_buff associated with a
particular PDL is extracted from the second fragment entry of that
PDL.  The sk_buff's length is adjusted to match the actual packet
length indicated by the PDL.  The packet is then given to the upper
layers for processing.  Finally, the PDL is returned to the Available
queue.  Once all of the received packets have been processed,
jtRxPDLReplenish is called to recycle PDL receive commands back into
service.

IIIe. PDC Receive

  Call Tree:
    jtDevOpen
      +--jtRxPDCCreate
		+--jtRxPDCInitialize
      |    +--jtRxPDCReplenish
      +--jtRxStart

    jtDevInterrupt
      +--jtRxPDCReceive
           +--jtRxPDCReplenish

    jtDevClose
		+--jtRxStop
		+--jtRxPDCFlush
      +--jtRxPDCDestroy
	
jtRxPDCCreate first allocates and initializes the queues
RxPDxAvailableQueue and RxPDxInProgressQueue.  Then the function
allocates a block of memory for each receive PDC.  Each PDC is at
least as large as the maximum packet size.  Currently, the memory is
allocated as a block of 1, 2 or 4 contiguous pages, considerably
larger than the normal Ethernet MTU of 1500.  A pointer to the PDC is
inserted into the Available queue.  

jtRxPDCInitialize iterates through each PDC in the Available queue.
The address of each PDC buffer is registered in the controller's PDC table
in ascending order, starting from 0.

jtRxPDCReplenish sends PDC receive commands to the controller.  The
commands are sent one at a time until the controller indicates that it
can't accept more commands.  Each PDC to be sent is dequeued from the
Available queue and inserted into the In Progress queue.  Then the
table index of the PDC is sent to the controller as a receive command.
The driver does not explicitly keep track of the PDC table index of
each PDC buffer.  Instead, the driver relies on the fact that PDCs are
always removed and inserted from the queues in order.  So the driver
simply keeps track of the table index of the PDC at the head of the
Available queue, and increments the index every time a PDC is dequeued
from the Available queue.

jtRxPDCReceive is called by the interrupt handler each time the
controller receives one or more packets.  Since the controller
receives packets in order, the function knows that the packet(s)
received have been placed into the PDCs at the head of the InProgress
queue.  The function repeatedly removes the PDC from the head of the
InProgress queue and processes it, until all received packets have
been processed.  Each PDC may have one or more packets stored inside
it.  Each packet is preceded by a PDC receive header.  The code
iterates over the PDC until it encounters a null PDC header.  For each
packet found, the packet is checked for errors.  Then, an sk_buff of
the same size as the current packet is allocated, and the packet data
is copied from PDC buffer to sk_buff.  The packet is then given to the
upper layers for processing.  Once all the packets of a PDC have been
processed, the PDC is returned to the Available queue.  Once all of
the PDCs have been processed, jtRxPDCReplenish is called to recycle
PDC receive commands back into service.

IIIf. PDx Transmit

The following description is completely accurate only for standard mode.
The differences when the driver is in performance mode will be descripted
later.

  Call Tree:
    jtDevOpen
      +--jtTxPDxCreate
		     +--jtTxPDCCreate
			  +--jtTxPDLCreate
	   +--jtTxPDxInitialize
		     +--jtTxPDCInitialize
			  +--jtTxPDLInitialize
      +--jtTxStart

    jtDevStartTransmit
      +--jtTxPDxTransmit
           +--jtTxPDLTransmit
           +--jtTxPDCGetNextAvailable
			  +--jtTxPDCAppendPacket
           +--jtTxPDCSend
	
    jtDevInterrupt
      +--jtTxPDxDone
	
    jtDevClose
	   +--jtTxStop
		+--jtTxPDxFlush
      +--jtTxPDxDestroy

jtTxPDxCreate first allocates and initializes the queues
TxPDCAvailableQueue, TxPDLAvailableQueue and TxPDxInProgressQueue.

jtTxPDCCreate allocates a block of memory for each transmit PDC.  Each
PDC is at least as large as the maximum packet size.  Currently, the
memory is allocated as a block of 1, 2 or 4 contiguous pages,
considerably larger than the normal Ethernet MTU of 1500.  A pointer
to the PDC is inserted into the PDC Available queue.  

jtTxPDLCreate allocates a single contiguous block of memory large enough
to hold all transmit PDLs.

jtTxPDCInitialize iterates through each PDC in the Available queue.
The address of each PDC buffer is registered in the controller's PDC table
in ascending order, starting from 0.

jtRxPDLInitialize divides the PDL block into individual PDLs, which are
aligned at cache line boundaries.  Pointers to each PDL are inserted into
the PDL Available queue.

jtTxPDxTransmit tries to transmit as many packets as it can find.  The
first packet is passed as a parameter to the function, as per the
standard Linux API.  To find subsequent packets, the function dredges
through the packet queues inside the interface's device structure.
The queue dredging code is only active when the driver is compiled for
kernel versions 2.0.x, since the structure of the device queues
changed substantially in kernel versions 2.1.x.

jtTxPDxTransmit loops over each packet until it runs out of packets or
until the controller can't take any more transmit commands.  If the
current packet is larger than the specified threshold, jtTxPDLTransmit
is called to send the packet via PDL.  Otherwise, the packet will be
sent via PDC.

Sending the packet via PDC is a multistep operation.  First, the code
looks for a partially built PDC from a previous iteration. If there
isn't a PDC, jtTxPDCGetNextAvailable is called to get an empty
PDC. Next, jtTxPDCAppend is called to copy the packet into the
PDC. Finally, the code peeks at the next packet.  If no more packets
are going into the PDC, either because there are no more packets, or
the next packet is too large, the code calls jtTxPDCSend to actually
send the PDC to the controller.

jtTxPDxDone is called by the interrupt handler each time the
controller completes transmit of one or more packets.  Since the
controller transmits packets in order, the function knows that the
packet(s) transmitted were from the PDCs and PDLs at the head of the
InProgress queue.  The function repeatedly removes the head element of
the InProgress queue and processes it, until each transmit has been
processed. For each element taken from the queue, he driver determines
whether the element is a PDL or PDC.  If the element is a PDL, the
function retrieves the address of the sk_buff associated with this PDL
from the second fragment entry of the PDL.  The sk_buff is freed, and
the PDL is inserted into the PDL Available queue.  If the element is a
PDC, the PDC is simply inserted into the PDC available queue.

When performance mode is enabled, jtTxPDxTransmit sends the packets and
then checks the completion status. The completion status is also checked
when the driver is out of PDCs. This implies that jtTxPDxDone can now be 
called not only from within the interrupt handler.
Second the driver doesn't ask for an interrupt on transmit completion, 
and sets a chip timer when there are PDL(s) pending. The timer will be 
removed when there are no more packets to be processed. 


IV. References

Level One Communications LXT1001 Micro-Architecture Specification,
Level One Communications, Inc.

*/

/*****************************************************************************
 * Module Include Files     
 ****************************************************************************/

#include <linux/config.h>

#include "jt1lin-def.h"

#ifdef MODULE
	#ifdef MODVERSIONS
		#include <linux/modversions.h>
	#endif
	#include <linux/module.h>
	#include <linux/version.h>
#else
	#define MOD_INC_USE_COUNT
	#define MOD_DEC_USE_COUNT
#endif

/*****************************************************************************
 * Include Files     
 ****************************************************************************/

#include <linux/types.h>       // u8, u16, etc.
#include <asm/io.h>            // inb()/outb() etc
#include <asm/signal.h>        // SA_SHIRQ etc
#include <linux/delay.h>       // mdelay()
#include <linux/if_ether.h>    // struct enet_statistics 
#include <linux/ioport.h>      // release_region() etc 
#include <linux/malloc.h>      // kmalloc, kfree 
#include <linux/netdevice.h>   // struct device and dev_xxx() 
#include <linux/pci.h>         // PCI_XXX 
#include <linux/stddef.h>      // offsetof

#include <linux/etherdevice.h> // eth_xxx()

#include <linux/module.h>

// Version compatibility
#if (LINUX_VERSION_CODE < 0x20123)
	#define test_and_set_bit(val, addr) set_bit(val, addr)
	#define test_and_clear_bit(val, addr) clear_bit(val, addr)
#endif

#if (LINUX_VERSION_CODE < 0x20100)
	#include <linux/bios32.h>      // pcibios_xxx()
#else
	#include <linux/pci.h>
#endif

#if (LINUX_VERSION_CODE < 0x20118)
	#define EXPORT_NO_SYMBOLS     register_symtab(NULL)
#endif

#if (LINUX_VERSION_CODE < 0x20100)
	#define DEV_KFREE_SKB(pSkBuff, mode) dev_kfree_skb(pSkBuff, mode)
	#define GET_FREE_PAGES(gfp, order)   __get_free_pages(gfp, order, 0)
#else
	#define DEV_KFREE_SKB(pSkBuff, mode) dev_kfree_skb(pSkBuff);
	#define GET_FREE_PAGES(gfp, order)   __get_free_pages(gfp, order);
#endif

#if (LINUX_VERSION_CODE < 0x20100)
	#define ioremap vremap
	#define iounmap vfree
#endif

#if (LINUX_VERSION_CODE < 0x20100)
	#define spinlock_t		int
	#define spin_lock_init(a)		{*(a) = 0;}
	#define spin_lock_irqsave(a,b)		{save_flags(b); cli();}
	#define spin_unlock_irqrestore(a,b)	restore_flags(b)
#endif

#if LINUX_VERSION_CODE < 0x20200
	#define spin_lock(lock)		spin_lock_irqsave(lock, Flags)
	#define spin_unlock(lock)	spin_unlock_irqrestore(lock, Flags)
	#define atomic_read(v)	(*v)
	#define atomic_set(v, a)	(*v=a)
#endif

// Why isn't this in a header file?
#ifndef TRUE
	#define TRUE 1
#endif
#ifndef FALSE
	#define FALSE 0
#endif

#define min(a,b)            (((a) < (b)) ? (a) : (b))

/*****************************************************************************
 * Defined Constants 
 ****************************************************************************/

typedef enum  {
	JT_STATUS_SUCCESS,
	JT_STATUS_FAILURE,
	JT_STATUS_RESOURCES,
	JT_STATUS_PENDING,
	JT_STATUS_SWRESET_FAILED,
	JT_STATUS_DEVICE_NOT_FOUND,
	JT_STATUS_NOT_SUPPORTED,     
	JT_STATUS_DIAGS_NOT_BUILT,
	JT_STATUS_INVALID_ADAPTER,
	JT_STATUS_INVALID_PARAMETER, 
	JT_STATUS_MISSING_PARAMETER,
	JT_STATUS_INVALID_COMMAND,
	JT_STATUS_BUFFER_TOO_SMALL,
	JT_STATUS_NO_DATA,           
	JT_STATUS_FILE_IO_FAILED,    
	JT_STATUS_INVALID_STATE,     
	JT_STATUS_IOCTL_FAILED,
	JT_STATUS_TIMEOUT
} JT_STATUS;

typedef enum  {
	JT_PHY_STATUS_LINK,
	JT_PHY_STATUS_FAILURE,
	JT_PHY_STATUS_RESTART,
} JT_PHY_STATUS;

//----------------------------------------------------------------------------
//
// Constants for the card as a whole
//
//----------------------------------------------------------------------------
#if !defined(PCI_VENDOR_ID_LEVELONE)
	#define PCI_VENDOR_ID_LEVELONE 0x1394
#endif
#if !defined(PCI_DEVICE_ID_LXT1001)
	#define PCI_DEVICE_ID_LXT1001 0x0001
#endif
#if !defined(PCI_DEVICE_ID_LXT1002)
	#define PCI_DEVICE_ID_LXT1002 0x0002
#endif

// Mercury and Matterhorn IDs
#define LEVELONE_CONTROLLER_TYPES 2
#define LXT1001 0x0000
#define LXT1002 0x0001

// Sizes for Mercury and Matterhorn
#define JT_MAX_MTU         		16*PAGE_SIZE
#define TX_PDL_TABLE_COUNT 		128
#define TX_PDC_TABLE_COUNT 		64
#define RX_PDC_TABLE_COUNT 		64

#define LXT1001_RX_FIFO_SIZE	64*1024/4

// If you change this, also need to change initializers for 
// tx_threshold, tx_flowcontrol, rx_flowcontrol etc.
#define JT_MAX_CONTROLLER_COUNT 8

// Constants for driver control structures
#define JT_MAX_LOOP_COUNT 4 

//----------------------------------------------------------------------------
//
// CSR REGISTER DEFINES
//
// The Mercury device's CSRs are arranged as a contiguous sequence of
// mostly 32 bit registers. Some of the registers are 64 bits wide. The 32
// bit registers are DWORD aligned and the 64 bit registers are QWORD
// aligned (assuming the device's base address is QWORD aligned). The
// register indices are given as ordinal values (e.g. for use as array
// indices).
//
//----------------------------------------------------------------------------

#define CSR_REG_SIZE    0x04        // All CSR reside on DWORD boundaries

#define  CSR00  (CSR_REG_SIZE*0x00)    // Mode Register - 1 
#define  CSR01  (CSR_REG_SIZE*0x01)    // Mode Register - 2 
#define  CSR02  (CSR_REG_SIZE*0x02)    // Transmit PDC Buffer Address Table Index   
#define  CSR03  (CSR_REG_SIZE*0x03)    // Product Identification Register
#define  CSR04  (CSR_REG_SIZE*0x04)    // Transmit PDC Buffer Address LSD
#define  CSR05  (CSR_REG_SIZE*0x05)    // Transmit PDC Buffer Address MSD  
#define  CSR06  (CSR_REG_SIZE*0x06)    // Receive PDC Buffer Address Table Index 
#define  CSR07  (CSR_REG_SIZE*0x07)    // Reserved
#define  CSR08  (CSR_REG_SIZE*0x08)    // Receive PDC Buffer Address LSD
#define  CSR09  (CSR_REG_SIZE*0x09)    // Receive PDC Buffer Address MSD
#define  CSR10  (CSR_REG_SIZE*0x0a)    // E2PROM   
#define  CSR11  (CSR_REG_SIZE*0x0b)    // Chip Status Register 
#define  CSR12  (CSR_REG_SIZE*0x0c)    // Transmit PDL Address Register LSD   
#define  CSR13  (CSR_REG_SIZE*0x0d)    // Transmit PDL Address Register MSD   
#define  CSR14  (CSR_REG_SIZE*0x0e)    // Receive PDL Address Register LSD 
#define  CSR15  (CSR_REG_SIZE*0x0f)    // Receive PDL Address Register MSD 
#define  CSR16  (CSR_REG_SIZE*0x10)    // Transmit PDC Register   
#define  CSR17  (CSR_REG_SIZE*0x11)    // Receive PDC Register 
#define  CSR18  (CSR_REG_SIZE*0x12)    // Interrupt Period Register  
#define  CSR19  (CSR_REG_SIZE*0x13)    // Transmit FIFO Packet Count Register 
#define  CSR20  (CSR_REG_SIZE*0x14)    // Transmit FIFO Low Watermark Register 
#define  CSR21  (CSR_REG_SIZE*0x15)    // Transmit FIFO DWORDs Free Register  
#define  CSR22  (CSR_REG_SIZE*0x16)    // Transmit FIFO Write Register  
#define  CSR23  (CSR_REG_SIZE*0x17)    // Reserved
#define  CSR24  (CSR_REG_SIZE*0x18)    // Receive FIFO Read Register 
#define  CSR25  (CSR_REG_SIZE*0x19)    // Reserved
#define  CSR26  (CSR_REG_SIZE*0x1a)    // Receive FIFO DWORD Count Register   
#define  CSR27  (CSR_REG_SIZE*0x1b)    // Receive FIFO High Watermark Register  
#define  CSR28  (CSR_REG_SIZE*0x1c)    // Receive FIFO Packet Count Register  
#define  CSR29  (CSR_REG_SIZE*0x1d)    // Command Register  
#define  CSR30  (CSR_REG_SIZE*0x1e)    // Interrupt Mask Register 
#define  CSR31  (CSR_REG_SIZE*0x1f)    // Reserved
#define  CSR32  (CSR_REG_SIZE*0x20)    // Event Status Register  
#define  CSR33  (CSR_REG_SIZE*0x21)    // Reserved
#define  CSR34  (CSR_REG_SIZE*0x22)    // Multicast Hash Table Register LSD
#define  CSR35  (CSR_REG_SIZE*0x23)    // Multicast Hash Table Register MSD
#define  CSR36  (CSR_REG_SIZE*0x24)    // LED 0 Configuration Register (LXT1001 only) 
#define  CSR37  (CSR_REG_SIZE*0x25)    // LED 1 Configuration Register (LXT1001 only) 
#define  CSR38  (CSR_REG_SIZE*0x26)    // LED 2 Configuration Register (LXT1001 only) 
#define  CSR39  (CSR_REG_SIZE*0x27)    // LED 3 Configuration Register (LXT1001 only) 
#define  CSR40  (CSR_REG_SIZE*0x28)    // Reserved  
#define  CSR41  (CSR_REG_SIZE*0x29)    // E2PROM Data Register
#define  CSR42  (CSR_REG_SIZE*0x2a)    // LAN Physical Address Register LSD
#define  CSR43  (CSR_REG_SIZE*0x2b)    // LAN Physical Address Register MSW
#define  CSR44  (CSR_REG_SIZE*0x2c)    // G/MII Address Register  
#define  CSR45  (CSR_REG_SIZE*0x2d)    // G/MII Data Register  
#define  CSR46  (CSR_REG_SIZE*0x2e)    // Statistic Index Register
#define  CSR47  (CSR_REG_SIZE*0x2f)    // Statistic Value Register 
#define  CSR48  (CSR_REG_SIZE*0x30)    // VLAN Tag Control Information Table
#define  CSR49  (CSR_REG_SIZE*0x31)    // Reserved
#define  CSR50  (CSR_REG_SIZE*0x32)    // Reserved
#define  CSR51  (CSR_REG_SIZE*0x33)    // Command Status Register
#define  CSR52  (CSR_REG_SIZE*0x34)    // Flow Control Watermark Register
#define  CSR53  (CSR_REG_SIZE*0x35)    // Transmit DMA Idle Timeout Register (LXT1002 only)
#define  CSR54  (CSR_REG_SIZE*0x36)    // Wake On LAN Index Register (LXT1002 only)
#define  CSR55  (CSR_REG_SIZE*0x37)    // Wake On LAN Data Register	(LXT1002 only)
#define  CSR56  (CSR_REG_SIZE*0x38)    // Multicast Address Index Register (LXT1002 only)
#define  CSR57  (CSR_REG_SIZE*0x39)    // Multicast Address Data Register	(LXT1002 only)
#define  CSR58  (CSR_REG_SIZE*0x3a)    // Timer 0 Count Register (LXT1001)
																			 // Status Buffer Address LSD (LXT1002)
#define  CSR59  (CSR_REG_SIZE*0x3b)    // Timer 0 Interrupt Trigger Register (LXT1001)
																			 // Status Buffer Address MSD	(LXT1002)
#define  CSR60  (CSR_REG_SIZE*0x3c)    // Timer 1 Count Register (LXT1001 only)
#define  CSR61  (CSR_REG_SIZE*0x3d)    // Timer 1 Interrupt Trigger Register (LXT1001 only)
#define  CSR62  (CSR_REG_SIZE*0x3e)    // Debug Command Register
#define  CSR63  (CSR_REG_SIZE*0x3f)    // Debug Data Register

// Set/Reset control bits
#define SERECL0                     0x00000001L
#define SERECL1                     0x00000100L
#define SERECL2                     0x00010000L
#define SERECL3                     0x01000000L

// Mode register offset and bit positions.
#define MODE_REG_1     (CSR_REG_SIZE*0x00)
	#define SWRE                    0x00000002L
	#define TXFLCTEN        				0x00000008L
	#define RXTRPR                  0x00000010L
	#define GMSTPOEN                0x00000020L
	#define TXPPEN                  0x00000040L
	#define RMPPEN                  0x00000080L
	#define TXEN                    0x00000200L
	#define RXEN                    0x00000400L
	#define MCEN                    0x00000800L
	#define BCEN                    0x00001000L
	#define POEN                    0x00002000L
	#define UCEN                    0x00004000L
	#define LGPKEN                  0x00008000L
	#define PACREN                  0x00020000L
	#define PAERPKEN                0x00040000L
	#define TXCREN                  0x00080000L
	#define MGPKEN                  0x00200000L
	#define MGMCBCEN                0x00400000L
	#define RXFLCTEN                0x00800000L
	#define VLEN                    0x02000000L
	#define VLTBEN                  0x04000000L
	#define VLRMID                  0x08000000L
	#define VLISGB                  0x10000000L
	#define USPIMD0                 0x20000000L
	#define USPIMD1                 0x40000000L
	#define LNCKEN									0x80000000L

#define MODE_REG_2               (CSR_REG_SIZE*0x01)
	#define LPBKMD_MASK              0x000000E0L
	#define LPBKMD_MAC               0x00000040L
	#define LPBKMD_PHY               0x00000080L
	#define TXIPCKEN                 0x00000200L
	#define TXTPCKEN                 0x00000400L
	#define TXUPCKEN                 0x00000800L
	#define RXIPCKEN                 0x00001000L
	#define RXTPCKEN                 0x00002000L
	#define RXUPCKEN                 0x00004000L
	#define PACKEREN                 0x00008000L
	#define LXT1002_PACTPKEN				 0x00020000L
	#define	LXT1002_MCPFEN					 0x00040000L
	#define LXT1002_TXDMSTEN				 0x02000000L
	#define LXT1002_RXDMSTEN				 0x04000000L

#define TX_PDC_BUF_ADR_TBL_IX    (CSR_REG_SIZE*0x02)
	#define TX_TBIX_MASK             0x0000003fL
#define PRODUCT_ID               (CSR_REG_SIZE*0x03)
#define TX_PDC_BUF_ADR_LO        (CSR_REG_SIZE*0x04)
#define TX_PDC_BUF_ADR_HI        (CSR_REG_SIZE*0x05)
#define RX_PDC_BUF_ADR_TBL_IX    (CSR_REG_SIZE*0x06)
	#define RX_TBIX_MASK             0x0000003fL
#define RESERVED07               (CSR_REG_SIZE*0x07)
#define RX_PDC_BUF_ADR_LO        (CSR_REG_SIZE*0x08)
#define RX_PDC_BUF_ADR_HI        (CSR_REG_SIZE*0x09)

#define EE_PROM                  (CSR_REG_SIZE*0x0a)
	#define EEPMPN                   0x00000001L
	#define EERDCM                   0x00000002L                      
	#define EEWTCM                   0x00000004L 
	#define EECKSMER                 0x00000010L
	#define EEMU                     0x00000020L
	#define EESL                     0x00000040L      
	#define EEAD                     0x00001f00L
	#define EXRMTM                   0x000f0000L
	#define FLPN                     0x00100000L
	#define FLWTEN                   0x00200000L

#define CHIP_STATUS_REG          (CSR_REG_SIZE*0x0b)
	#define TXPKAC                   0x00000001L
	#define RXPKAV                   0x00000002L
	#define FLCTST                   0x00000004L
	#define UPIST0                   0x00000008L
	#define UPIST1                   0x00000010L
	#define TXIL                     0x00000020L
	#define RXIL                     0x00000040L

#define TX_PDL_ADDR_REG_LO       (CSR_REG_SIZE*0x0c)

#define TX_PDL_ADDR_REG_HI       (CSR_REG_SIZE*0x0d)
	#define IPCKIS                   0x00200000L
	#define TPCKIS                   0x00400000L
	#define UPCKIS                   0x00800000L
	#define VLIS                     0x10000000L
	#define DMDNINRQ                 0x80000000L
	#define TX_PDL_FGCN_SHIFT        16
	#define TX_PDL_VLTBIX_SHIFT      24
	#define PDL_FGCN_MASK						 0x001f0000L

#define RX_PDL_ADDR_REG_LO       (CSR_REG_SIZE*0x0e)
#define RX_PDL_ADDR_REG_HI       (CSR_REG_SIZE*0x0f)
	#define RX_PDL_FGCN_SHIFT        16

#define TX_PDC_REG               (CSR_REG_SIZE*0x10)
	#define XFDNINRQ                 0x01000000L
	#define TX_PDC_REG_BFID_SHIFT    16
	#define TX_PDC_HDTYPE            0x00020000L

#define RX_PDC_REG               (CSR_REG_SIZE*0x11)
	#define RXINRQ                   0x80000000L
	#define RX_PDC_REG_BFID_SHIFT    16
	#define RX_PDC_HDTYPE            0x00010000L
   
#define INTR_PERIOD_REG          (CSR_REG_SIZE*0x12)
#define TX_FIFO_PKT_CNT_REG      (CSR_REG_SIZE*0x13)
#define TX_FIFO_WTR_MRK_REG      (CSR_REG_SIZE*0x14)
#define TX_FIFO_DWORDS_FREE_REG  (CSR_REG_SIZE*0x15)
#define TX_FIFO_WRITE_REG        (CSR_REG_SIZE*0x16)
#define RESERVED23               (CSR_REG_SIZE*0x17)

#define RX_FIFO_READ_REG         (CSR_REG_SIZE*0x18)
// Status Bits for the first 32 bits of Rx FIFO Header
	#define PKT_HDR_LENGTH_MASK      0x0000ffffL
	#define PKT_HDR_TYPE_MASK        0x001f0000L
	#define LXT1002_MCPFMA					 0x00200000L
	#define ERLN                     0x00400000L
	#define PHAD                     0x00800000L
	#define BCAD                     0x01000000L
	#define MCAD                     0x02000000L
	#define LGPK                     0x04000000L
	#define EROV                     0x08000000L
	#define ERCR                     0x10000000L
	#define ERRU                     0x20000000L
	#define ERAL                     0x40000000L
	#define RXER                     0x80000000L
// Status Bits for the second 32 bits of Rx FIFO Header
	#define VLTBIX_RX_MASK           0x0000000fL
	#define VLHT                     0x00000010L
	#define IPCKER                   0x00000100L
	#define TPCKER                   0x00000200L
	#define UPCKER                   0x00000400L
	#define IPHDPN                   0x00000800L
	#define TPHDPN                   0x00001000L
	#define UPHDPN                   0x00002000L
	#define	LXT1002_ADMAIX_MASK			 0x000f0000L
	#define LXT1002_ADMAIX_SHIFT		 16

#define RX_HDR1_ERROR_MASK          ( ERAL | ERRU | ERCR | EROV | ERLN )
#define RX_HDR2_ERROR_MASK          ( UPCKER | TPCKER | IPCKER )
#define RX_HDR1_STATUS_MASK         ( LGPK )
#define RX_HDR2_STATUS_MASK         ( VLTBIX_RX_MASK | VLHT | IPHDPN | TPHDPN | UPHDPN )

#define RESERVED25               (CSR_REG_SIZE*0x19)
#define RX_FIFO_DWORD_CNT_REG    (CSR_REG_SIZE*0x1a)
#define RX_FIFO_WTR_MRK_REG      (CSR_REG_SIZE*0x1b)
#define RX_FIFO_PKT_CNT_REG      (CSR_REG_SIZE*0x1c)

// Command register offset and bit positions.
#define COMMAND_REG              (CSR_REG_SIZE*0x1d)
	#define SLMDTXCM                 0x00000002L
	#define RXFISKPK                 0x00000004L
	#define DLINRQ                   0x00000008L
	#define PEINRQ                   0x00000010L
	#define LXT1001_TMEN0            0x00000020L
	#define LXT1001_TMEN1            0x00000040L 

// Interrupt mask register offset and bit positions.
#define INTR_MASK_REG            (CSR_REG_SIZE*0x1e)
	#define TXCMEMMS                 0x00000002L
	#define TXFIWMMS                 0x00000004L
	#define TXDMDNMS                 0x00000008L
	#define LXT1002_WKONLNMS				 0x00000010L
	#define TMEXMS                   0x00000040L
	#define INENMS                   0x00000080L
	#define RXCMEMMS                 0x00000200L
	#define RXFIWMMS                 0x00000400L
	#define RXMS                     0x00000800L
	#define RXPDMS                   0x00001000L
	#define PHLASTMS                 0x00002000L
	#define RXMGPKMS                 0x00004000L
	#define USPIMS0                  0x00020000L
	#define USPIMS1                  0x00040000L
	#define LXT1001_TMMS0            0x00080000L
	#define LXT1001_TMMS1            0x00100000L
	#define	LXT1002_TXDMIDMS				 0x00800000L
	#define	DMDNMSK									 0xff000000L

#define RESERVED31               (CSR_REG_SIZE*0x1f)
#define INTR_STATUS_REG          (CSR_REG_SIZE*0x20)
	#define TXCMEMIN                 0x00000002L
	#define TXFIWMIN                 0x00000004L
	#define TXDMDNIN                 0x00000008L
	#define LXT1002_WKONLNIN				 0x00000010L
	#define TMEXIN                   0x00000040L
	#define RXCMEMIN                 0x00000200L
	#define RXFIWMIN                 0x00000400L
	#define RXIN                     0x00000800L
	#define RXPDIN                   0x00001000L
	#define PHLASTIN                 0x00002000L
	#define RXMGPKIN                 0x00004000L
	#define LXT1002_WKONLN					 0x00008000L
	#define USPIIN0                  0x00020000L
	#define USPIIN1                  0x00040000L
	#define LXT1001_TMIN0            0x00080000L
	#define LXT1001_TMIN1            0x00100000L
	#define LXT1002_TXDMIDIN				 0x00200000L
	#define INTR_STATUS_RXDMDNCN     0xff000000L
	#define INTR_STATUS_RXDMDNCN_SHIFT 24
   
#define RESERVED33               (CSR_REG_SIZE*0x21)
#define MCAST_HASH_TBL_REG_LO    (CSR_REG_SIZE*0x22)
#define MCAST_HASH_TBL_REG_HI    (CSR_REG_SIZE*0x23)

// LED Register Defines
#define LXT1001_LED_00_REG       (CSR_REG_SIZE*0x24)
	#define LED_LDPL                  0x00000002L
	#define LED_LDEN                  0x00000004L
	#define LED_PUXP                  0x00000008L
	#define LED_10MB                  0x00000010L
	#define LED_100MB                 0x00000020L
	#define LED_1000MB                0x00000040L
	#define LED_FD                    0x00000100L
	#define LED_AN                    0x00000200L
	#define LED_LKST                  0x00000400L
	#define LED_ADMA                  0x00000800L
	#define LED_TX                    0x00001000L
	#define LED_RX                    0x00002000L
	#define LED_JA                    0x00004000L
	#define LED_CO                    0x00008000L
	#define LED_CA                    0x00010000L

// Selection of events that light the LEDs.
	#define JT_LED_0_MODE   LED_LDEN|LED_PUXP|LED_LKST
	#define JT_LED_1_MODE   LED_LDEN|LED_PUXP|LED_TX
	#define JT_LED_2_MODE   LED_LDEN|LED_PUXP|LED_RX
	#define JT_LED_3_MODE   LED_LDEN|LED_PUXP|LED_CO

#define LXT1001_LED_01_REG        (CSR_REG_SIZE*0x25)
#define LXT1001_LED_02_REG        (CSR_REG_SIZE*0x26)
#define LXT1001_LED_03_REG        (CSR_REG_SIZE*0x27)


#define RESERVED40               (CSR_REG_SIZE*0x21)
#define EEPROM_DATA_REG          (CSR_REG_SIZE*0x29)
#define LAN_PHY_ADR_LO           (CSR_REG_SIZE*0x2a)
#define LAN_PHY_ADR_HI           (CSR_REG_SIZE*0x2b)

#define GMII_ACCESS_REG          (CSR_REG_SIZE*0x2c)
	#define GMRRIX_MASK              0x0000001fL
	#define GMCM                     0x00000080L
	#define GMPHAD_MASK              0x00001f00L
	#define GMST                     0x00008000L
	#define GMDA                     0xffff0000L

#define GMII_MODE_REG            (CSR_REG_SIZE*0x2d)
	#define GMWRSP_MASK              0x00000003L
	#define GMWRSP_RESERVED					 0x00000003L
	#define GMWRSP_1000MB						 0x00000002L
	#define GMWRSP_100MB						 0x00000001L
	#define GMWRSP_10MB							 0x00000000L
	#define GMFD                     0x00000004L
	#define LXT1002_MDNTEN					 0x00000008L
	#define LXT1002_MDCKRT_64				 0x00000000L
	#define LXT1002_MDCKRT_32				 0x00000010L
	#define LXT1002_MDCKRT_16				 0x00000020L
	#define LXT1002_MDCKRT_8				 0x00000030L
	#define LXT1002_PASPEN					 0x00000040L
	#define GMIFPR                   0x00000100L
	#define GMPCEN                   0x00000200L
	#define GMRPP_MASK               0x60000000L
	#define RPEN                     0x80000000L

#define STAT_INDEX_REG           (CSR_REG_SIZE*0x2e)
	#define STAT_ID_TX_OK               	0
	#define STAT_ID_SINGLE_COL          	1
	#define STAT_ID_MULTI_COL           	2
	#define STAT_ID_RX_OK               	3
	#define STAT_ID_CRC_ERROR           	4
	#define STAT_ID_ALIGN_ERROR         	5
	#define STAT_ID_DROPPED             	6
	#define LXT1001_STAT_ID_RX_ERROR    	7
	#define STAT_ID_TX_ERROR            	8
	#define STAT_ID_LATE_COL            	9
	#define STAT_ID_RUNT                	10
	#define STAT_ID_TOO_LONG            	11
	#define LXT1001_STAT_ID_VLAN_OK     	12
	#define STAT_ID_VLAN_DISCARD        	13
	#define LXT1001_STAT_ID_IP_CKSUM_ERR  14
	#define LXT1001_STAT_ID_UDP_CKSUM_ERR 15
	#define STAT_ID_PKT_LENGTH_ERR      	16
	#define LXT1001_STAT_ID_TCP_CKSUM_ERR 17
	#define LXT1001_STAT_ID_IP_NOT_V4     18
	#define STAT_ID_EXCESS_COL          	19
	#define LXT1001_STAT_ID_UNICAST_OK    20
	#define LXT1001_STAT_ID_MULTICAST_OK  21
	#define LXT1001_STAT_ID_BROADCAST_OK  22
	#define STAT_ID_PAUSE_RX            	23
	#define STAT_ID_PAUSE_TX            	24
	#define STAT_ID_CONTROL_RX          	25
	#define STAT_ID_DEFERED             	26
	#define STAT_ID_EXCESS_DEFERED      	27
	#define STAT_ID_CARRIER_SENSE       	28
	#define LXT1002_RECEIVE_COLLISION_COUNT 29
	#define LXT1002_TOTAL_COLLISION_COUNT   30
#define STAT_DATA_REG            (CSR_REG_SIZE*0x2f)

#define VLAN_TCI_TBL_REG         (CSR_REG_SIZE*0x30)
	#define VLTBCM                   0x00200000L
	#define VLID_TBL_MASK            0x00000fffL
	#define VLUSPR_TBL_MASK          0x0000e000L
	#define VLTBIX_TBL_MASK          0x000f0000L

#define RESERVED49			         (CSR_REG_SIZE*0x31)
#define RESERVED50               (CSR_REG_SIZE*0x32)
#define CMD_STATUS_REG           (CSR_REG_SIZE*0x33)

#define LXT1002_FLCT_WTMK_REG    (CSR_REG_SIZE*0x34)
	#define LXT1002_FLCTLOWM_MASK		 0x0000ffffL
	#define LXT1002_FLCTHIWM_MASK		 0xffff0000L
	#define LXT1002_FLCTHIWM_SHIFT	 16

#define LXT1002_TX_DMA_ID_REG		 (CSR_REG_SIZE*0x35)

#define LXT1002_WK_ON_LN_IX_REG  (CSR_REG_SIZE*0x36)
	#define LXT1002_PTMSIX_MASK			 0x00000002L
	#define LXT1002_PTMSLN_MASK			 0x00ff0000L
	#define LXT1002_PTMSLN_SHIFT		 16
	#define LXT1002_PTMSVA				 	 0x80000000L

#define LXT1002_WK_ON_LN_DT_REG  (CSR_REG_SIZE*0x37)

#define LXT002_MLCS_AD_IX_REG    (CSR_REG_SIZE*0x38)
	#define LXT1002_MCADIX_MASK			 0x0000000fL
	#define LXT1002_MCADVA					 0x80000000L								

#define LXT1002_MLCS_AD_DT_REG   (CSR_REG_SIZE*0x39)
#define LXT1001_TM0_COUNT_REG 	 (CSR_REG_SIZE*0x3a)
#define LXT1002_ST_BUF_ADD_LSD	 (CSR_REG_SIZE*0x3a)
#define LXT1001_TM0_INT_TRIG_REG (CSR_REG_SIZE*0x3b)
#define LXT1002_ST_BUF_ADD_MSD	 (CSR_REG_SIZE*0x3b)
#define LXT1001_TM1_COUNT_REG    (CSR_REG_SIZE*0x3c)
#define LXT1001_TM1_INT_TRIG_REG (CSR_REG_SIZE*0x3d)

#define DEBUG_COMMAND_REG        (CSR_REG_SIZE*0x3e)
	#define DEBUG_BIST               0x00000001L
	#define DEBUG_DBST               0x80000000L

#define DEBUG_DATA_REG           (CSR_REG_SIZE*0x3f)

//----------------------------------------------------------------------------
// End of CSRs
//----------------------------------------------------------------------------

// Magic packet constants
#define MAGIC_PACKET_ENABLED  1
#define MAGIC_PACKET_DISABLED 0

// VLAN Constants 
#define  VLAN_TABLE_ENTRIES               16
#define  VLAN_ENTRY_IN_USE                0x80000000
#define  VLAN_ID_MASK                     0x00000fff
#define  VLAN_PRIORITY_MASK               0x0000e000
#define  VLAN_PRIORITY_SHIFT              13

#define  VLAN_FILTER_ENABLE               0x00000004
#define  VLAN_STRIP                       0x00000008
#define  VLAN_INSERT                      0x00000010

// Transmit/Receive Modes
#define TX_IOMODE_PIO                     1
#define TX_IOMODE_BUSMASTER               2

#define RX_IOMODE_PIO                     1
#define RX_IOMODE_PDL                     2
#define RX_IOMODE_PDC                     4

// Link parameters
#define DUPLEX_MODE_AUTO                  1
#define DUPLEX_MODE_HALF                  2
#define DUPLEX_MODE_FULL                  4

#define LINK_SPEED_AUTO                   0
#define LINK_SPEED_10                     10
#define LINK_SPEED_100                    100
#define LINK_SPEED_1000                   1000

//----------------------------------------------------------------------------
// PHY Indentification Values
//----------------------------------------------------------------------------

// Phy Type
#define PHY_TYPE_AUTO   1
#define PHY_TYPE_PCS    2
#define PHY_TYPE_MII    4
#define PHY_TYPE_GMII   8

// Level One PCS Phy ID
#define HG1_PCS_PHY_ID_1   0x0382
#define HG1_PCS_PHY_ID_2   0x0c00

// Level One Cheetah 10/100/1000
#define LEVEL1_CHEETAH_ID_1   0x0013
#define LEVEL1_CHEETAH_ID_2   0x7830

// Level One LXT970 10/100
#define LEVEL1_PHY_ID_1       0x7810
#define LEVEL1_PHY_ID_2       0x0000

// Level One LXT970 10/100 (Second version)
#define LEVEL1_PHY_ID_1_B      0x7810
#define LEVEL1_PHY_ID_2_B      0x0003

// National 10/100 Phy
#define NATIONAL_PHY_ID_1     0x2000
#define NATIONAL_PHY_ID_2     0x5c00

typedef enum {
	PHY_LEVEL1_PCS,
	PHY_LEVEL1_CHEETAH,
	PHY_NATIONAL,
	PHY_LEVEL1
} PHY_ID;

//----------------------------------------------------------------------------
// PCS/GMII Phy Register Definitions
//----------------------------------------------------------------------------
#define PHY_CONTROL_REG                   0
// One PCS/GMII Specific Bit Follows
	#define PHY_CTL_GSPSEL                 0x0040
	#define PHY_CTL_COLTST                 0x0080
	#define PHY_CTL_FDMD                   0x0100
	#define PHY_CTL_RSAN                   0x0200
	#define PHY_CTL_ISLT                   0x0400
	#define PHY_CTL_PWRDN                  0x0800
	#define PHY_CTL_ANEN                   0x1000
// SPSEL is PCS/GMII Specific if GSPEL = 1
	#define PHY_CTL_SPSEL                  0x2000
	#define PHY_CTL_LPBK                   0x4000
	#define PHY_CTL_RST                    0x8000

#define PHY_STATUS_REG                    1
	#define PHY_STAT_EXT_CAP               0x0001
	#define PHY_STAT_JAB_DET               0x0002
	#define PHY_STAT_LINK_OK               0x0004
	#define PHY_STAT_AN_ABLE               0x0008
	#define PHY_STAT_RFLT                  0x0010
	#define PHY_STAT_AN_DONE               0x0020
	#define PHY_STAT_MFPRESUP              0x0040
// One PCS/GMII Specific Bit Follows
	#define PHY_STAT_EXTSTS                0x0100
	#define PHY_STAT_100T2_HD              0x0200
	#define PHY_STAT_100T2_FD              0x0400
	#define PHY_STAT_10_HD                 0x0800
	#define PHY_STAT_10_FD                 0x1000
	#define PHY_STAT_100X_HD               0x2000
	#define PHY_STAT_100X_FD               0x4000
	#define PHY_STAT_100T4                 0x8000

#define PHY_ID_1_REG                      2
#define PHY_ID_2_REG                      3

#define PHY_AUTO_NEG_AD_REG               4  
	#define PHY_PCS_AD_FD                  0x0020
	#define PHY_PCS_AD_HD                  0x0040
	#define PHY_PCS_AD_PAUSE               0x0180
	#define PHY_PCS_AD_PAUSE_NO            0x0000
	#define PHY_PCS_AD_PAUSE_SYM           0x0080
	#define PHY_PCS_AD_PAUSE_ASYM          0x0100
	#define PHY_PCS_AD_PAUSE_LOCAL         0x0180
	#define PHY_PCS_AD_RFLT                0x3000
	#define PHY_PCS_AD_RFLT_OK             0x0000
	#define PHY_PCS_AD_RFLT_LNK_FAIL       0x1000
	#define PHY_PCS_AD_RFLT_OFFLINE        0x2000
	#define PHY_PCS_AD_RFLT_AN_ERROR       0x3000
	#define PHY_PCS_AD_NXPG                0x8000

	#define PHY_MII_SEL_MASK               0x001f
	#define PHY_MII_SEL_8023               0x0001
	#define PHY_MII_AD_10T                 0x0020
	#define PHY_MII_AD_10T_FD              0x0040
	#define PHY_MII_AD_100T                0x0080
	#define PHY_MII_AD_100T_FD             0x0100
	#define PHY_MII_AD_100T4               0x0200
	#define PHY_MII_AD_RFLT                0x2000
	#define PHY_MII_AD_NXPG                0x8000


#define PHY_AUTO_NEG_PART_BASE_REG        5
// See PHY_AUTO_NEG_AD_REG

#define PHY_AUTO_NEG_EXP_REG              6
	#define PHY_PCS_AN_EXP_NP_ABLE         0x0002
	#define PHY_PCS_AN_EXP_PG_RCVD         0x0004


#define PHY_AUTO_NEG_NEXT_PAGE_REG        7
	#define PHY_PCS_AN_NP_MUCF             0x03ff
	#define PHY_PCS_AN_NP_TOG              0x0800
	#define PHY_PCS_AN_NP_ACK2             0x1000
	#define PHY_PCS_AN_NP_MSPG             0x2000
	#define PHY_PCS_AN_NP_NXPG             0x8000

#define PHY_AUTO_NEG_PART_NEXT_PAGE_REG   8
// See PHY_AUTO_NEG_NEXT_PAGE_REG

#define PHY_100T_CONTROL_REG              9
#define PHY_100T_STATUS_REG               10
#define PHY_PCS_EXT_STATUS_REG            11
	#define PHY_PCS_EXT_STAT_1000T_HD      0x1000
	#define PHY_PCS_EXT_STAT_1000T_FD      0x2000
	#define PHY_PCS_EXT_STAT_1000X_HD      0x4000
	#define PHY_PCS_EXT_STAT_1000X_FD      0x8000

#define PHY_LVL1_CHIP_STAT_REG            20
	#define PHY_LVL1_CHIP_STAT_ANDONE      0x0200
	#define PHY_LVL1_CHIP_STAT_100M        0x0800
	#define PHY_LVL1_CHIP_STAT_DUP         0x1000
	#define PHY_LVL1_CHIP_STAT_LINK        0x2000

#define PHY_NAT_CHIP_STAT_REG             0x19
	#define PHY_NAT_CHIP_STAT_10M          0x0040
	#define PHY_NAT_CHIP_STAT_DUP          0x0080

#define PHY_GMII_1000T_CONTROL_REG        9
	#define PHY_GMII_MA_SL_CNF_EN      		 0x1000
	#define PHY_GMII_MULTI_PORT	      		 0x0400
	#define PHY_GMII_AD_1000T_FD		 		   0x0200
	#define PHY_GMII_AD_1000T							 0x0100

#define PHY_GMII_QUICK_STATUS_REG        	17
	#define PHY_GMII_SPEED_MASK						 0x8000
	#define PHY_GMII_100									 0x4000
	#define PHY_GMII_10									 	 0x0000
	#define PHY_GMII_1000									 0x8000
	#define PHY_GMII_100									 0x4000
	#define PHY_GMII_10									 	 0x0000
	#define PHY_GMII_LINK_OK    		  		 0x0400
	#define PHY_GMII_FD										 0x0200
	#define PHY_GMII_AN_DONE    		  		 0x0080


#define MII_TIMEOUT_MILLISECONDS 100

// PCI Constants
#define PCI_CONFIG_VENDOR_ID           	0x0000
#define PCI_CONFIG_DEVICE_ID           	0x0002
#define PCI_CONFIG_COMMAND             	0x0004
#define PCI_CONFIG_COMMAND_MWI      		0x0010
#define PCI_CONFIG_STATUS              	0x0006
#define PCI_CONFIG_REV_ID              	0x0008
#define PCI_CONFIG_CACHE_LINESIZE      	0x000C
#define PCI_CONFIG_LATENCY_TIMER       	0x000D
#define PCI_CONFIG_INTERRUPT_LINE      	0x003C
#define PCI_CONFIG_ROM_BASE            	0x0030

// Diagnostic constants
#define DIAGS_OFF 	0
#define DIAGS_ON 		1
#define DBG_DIAGNOSTICS_ENABLED   0x00000001  // Indicates the driver is in diagnostics mode.
#define DBG_RECEIVER_ENABLED      0x00000002  // Indicates the receive is enabled.  
#define DBG_TRANSMITTER_ENABLED   0x00000008  // Indicates the transmit is enabled.  
#define DBG_INTERRUPTS_ENABLED    0x00000004  // Indicates interrupts have been enabled.

/*****************************************************************************
 * Type Definitions 
 ****************************************************************************/

// Standard Level One type names 
typedef u64    UINT64, *PUINT64;
typedef u32    UINT32, *PUINT32;
typedef u16    UINT16, *PUINT16;
typedef u8     UINT8,  *PUINT8;
typedef void   VOID,   *PVOID;
typedef UINT8 BOOLEAN, *PBOOLEAN;

// Queue Management Structures
typedef struct
{
	void **         Elements;
	volatile UINT32 Head;
	volatile UINT32 Tail;
	UINT32          Capacity;
} JT_QUEUE;

// PDL Structure
#define  MAXIMUM_PDL_FRAGMENTS 31
typedef struct
{
	UINT32   FragmentAddressLow;
	UINT32   FragmentAddressHigh;
	UINT16   FragmentLength;
	UINT16   FragLengthReserved;
	UINT32   FragmentReserved;   
} PDL_FRAGMENT, *PPDL_FRAGMENT;

typedef struct
{
	UINT32         Header;                                               
	UINT32         Header2;
	PDL_FRAGMENT   Fragment[1];
} PDL, *PPDL;

// Each PDL has room for headers, 1 fragment, and 8 bytes of
// free space at the end (used for the skbuff pointer).
#define PDL_MIN_ALIGNMENT 32
#define PDL_FLAG  0x01

// PDC Header Structures
typedef struct
{
	UINT32         Header;
	UINT32         Data[0];
} TX_PDC, *PTX_PDC;

typedef struct
{  
	UINT32         Header;     
	UINT32         Header2;                                              
	UINT32         Data[0];
} RX_PDC, *PRX_PDC;

// PHY Structure
typedef struct
{
	BOOLEAN        Connected;
	PHY_ID         PhyId;
	UINT8          PhyType;
	UINT8          PhyAddress;
} PHY_ELEMENT, *PPHY_ELEMENT;

// Controller instance
typedef struct DEV_INSTANCE
{
	// Controller Type
	UINT32				 ControllerType;

	// Controller location
	UINT16         IORange;
	UINT32         ROMBaseLow;
	UINT32         ROMBaseHigh;
	UINT32         ROMRange;
	UINT8          PciBus;
	UINT8          PciDeviceFunction;
#if defined(USE_MEMORY_BASE) && LINUX_VERSION_CODE > 0x20200
	UINT32				 MapAddr;
#endif
#if defined(WORKAROUND_WRITE_COMBINE)
	UINT32				 LastPortOffset;
#endif

	// Phy fields
	PHY_ELEMENT    CurrentPhy;
	UINT16         OverrideSpeed;
	UINT8          OverrideDuplexMode;
	UINT16         Speed;
	UINT8          DuplexMode;

	// Controller options
	UINT8          NodeAddress[ETH_ALEN];
	UINT16         CacheLineSize;
	UINT32         MulticastHashHigh;
	UINT32         MulticastHashLow;
	UINT8          MagicPacketMode;

	// Transmit modes
	UINT8         TxIOMode;
	UINT32        TxPacketHeader;
	BOOLEAN       TxFlowControl;

	// Transmit methods
	JT_STATUS    (* pTxCreate)    ( struct DEV_INSTANCE * );
	JT_STATUS    (* pTxInitialize)( struct DEV_INSTANCE * );
	int          (* pTxTransmit)  ( struct DEV_INSTANCE *, struct sk_buff * );
	JT_STATUS    (* pTxFlush)     ( struct DEV_INSTANCE * );
	JT_STATUS    (* pTxDestroy)   ( struct DEV_INSTANCE * );

	// PIO transmit mode
	UINT16        TxFifoDwordFreeCount;

	// PDC transmit 
	UINT32        TxPDCCount;
	UINT32        TxPDCLength;
	UINT32        TxPDCOrder;
	UINT32        TxPDCTableCount;
	UINT32        TxPDCNextTableIndex;
	JT_QUEUE      TxPDCAvailableQueue;

	// PDL transmit 
	UINT32        TxPDLCount;
	void *				pTxPDLBase;
	UINT32        TxPDLOrder;
	JT_QUEUE      TxPDLAvailableQueue;
	UINT32        TxPDLAlignment;

#if defined (PERFORMANCE_TX)
	unsigned long	TxPDLTimerOn;
	atomic_t			TxPDLPending;
	spinlock_t		DriverLock;	/* Normal IRQ lock */
#endif

	// Busmaster transmit mode
	unsigned long TxFull; // unsigned long for atomic bitops
	UINT32        TxPDxThreshold;
	UINT8         TxPDxCommandFreeCount;
	JT_QUEUE      TxPDxInProgressQueue;

	// Receive modes
	UINT8         RxIOMode;
	BOOLEAN       RxFlowControl;

	// Receive methods
	JT_STATUS    (* pRxCreate)    ( struct DEV_INSTANCE * );
	JT_STATUS    (* pRxInitialize)( struct DEV_INSTANCE * );
	JT_STATUS    (* pRxReplenish) ( struct DEV_INSTANCE * );
	JT_STATUS    (* pRxReceive)   ( struct DEV_INSTANCE *, UINT32 );
	JT_STATUS    (* pRxFlush)     ( struct DEV_INSTANCE * );
	JT_STATUS    (* pRxDestroy)   ( struct DEV_INSTANCE * );

	// PDC receive mode
	UINT32        RxPDCCount;
	UINT32        RxPDCLength;
	UINT32        RxPDCOrder;
	UINT32        RxPDCTableCount;
	UINT32        RxPDCNextTableIndex;

	// PDL receive mode
	UINT32        RxPDLCount;
	void *        pRxPDLBase;
	UINT32        RxPDLOrder;
	UINT32        RxPDLAlignment;

	// PDC/PDL receive modes
	UINT8         RxPDxCommandFreeCount;
	JT_QUEUE      RxPDxAvailableQueue;
	JT_QUEUE      RxPDxInProgressQueue;

	// CRC check
#if defined(WORKAROUND_CRC_CHECK)
	UINT32				RxCrcCheck;
#endif

	// status buffer
	void * StatusBuffer;
	UINT8	TxOldStatusCount;

 	// Diagnostics
 	UINT32				DiagnosticsStatus;
	UINT32				Flags;
	UINT32				InterruptCount;
	JT_QUEUE      dbgRxQueue;

 	// Linux bookkeeping
	struct device * pDev;
	struct enet_statistics Statistics;

} DEV_INSTANCE, *PDEV_INSTANCE;

/*****************************************************************************
 * Global and Static Variables 
 ****************************************************************************/

#if defined(JT_DEBUG)
	#define STATIC
	#define INLINE
#else
	#define STATIC static
	#define INLINE extern inline
#endif

// Controller names
const char * jtControllerName[LEVELONE_CONTROLLER_TYPES]= {
		"LXT1001",
		"LXT1002"
	};

// List of controllers detected 
int jtControllerCount = 0;
struct device * jtControllers[JT_MAX_CONTROLLER_COUNT] = {0,};
const char jtProductName[] = "Level One Gigabit Ethernet Controller";

// User-configurable options 
STATIC int mtu           [JT_MAX_CONTROLLER_COUNT] = {0,};
STATIC int tx_mode       [JT_MAX_CONTROLLER_COUNT] = {2, 0,};
STATIC int tx_ringsize   [JT_MAX_CONTROLLER_COUNT] = {0,};
STATIC int tx_threshold  [JT_MAX_CONTROLLER_COUNT] = {-1, -1, -1, -1, -1, -1, -1, -1,};
STATIC int tx_flowcontrol[JT_MAX_CONTROLLER_COUNT] = {-1, -1, -1, -1, -1, -1, -1, -1,};
STATIC int rx_mode       [JT_MAX_CONTROLLER_COUNT] = {4, 0,}; 
STATIC int rx_ringsize   [JT_MAX_CONTROLLER_COUNT] = {0,};
STATIC int rx_flowcontrol[JT_MAX_CONTROLLER_COUNT] = {-1, -1, -1, -1, -1, -1, -1, -1,};
STATIC int speed         [JT_MAX_CONTROLLER_COUNT] = {0, 0,};
STATIC char * duplex     [JT_MAX_CONTROLLER_COUNT] = {0, 0,};
#if defined(WORKAROUND_CRC_CHECK)
STATIC int rx_crc	       [JT_MAX_CONTROLLER_COUNT] = {0,}; 
#endif

#if LINUX_VERSION_CODE > 0x20115
MODULE_DESCRIPTION("Level One LXT1000 Family Gigabit Ethernet driver");
MODULE_PARM(mtu,            "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i");
MODULE_PARM(tx_mode,        "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i");
MODULE_PARM(tx_ringsize,    "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i");
MODULE_PARM(tx_threshold,   "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i");
MODULE_PARM(tx_flowcontrol, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i");
MODULE_PARM(rx_mode,        "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i");
MODULE_PARM(rx_ringsize,    "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i");
MODULE_PARM(rx_flowcontrol, "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i");
MODULE_PARM(speed,          "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i");
MODULE_PARM(duplex,         "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "s");
#if defined(WORKAROUND_CRC_CHECK)
MODULE_PARM(rx_crc,         "1-" __MODULE_STRING(JT_MAX_CONTROLLER_COUNT) "i");
#endif
#endif

// Debugging options
#if defined(JT_DEBUG)
	int break_init  = 0; // If 1, execute breakpoint in init_module
	int sleep_init  = 0; // If non-0, sleep in init_module
	int verbose     = 1; // If 1, enable verbose debugging info
#endif

// Software breakpoints
// Assertion checking
// Debugging output
#if defined(JT_DEBUG)
	#define BreakPoint() \
		{  printk(KERN_CRIT "Breakpoint at %s function %s line %d!\n", __FILE__, __FUNCTION__, __LINE__ ); }
	#define JT_ASSERT( Condition ) if( ! (Condition) ) { jtQueuePrintAll(); BreakPoint(); }
	#define JT_ENTER if( verbose ) { printk( KERN_CRIT "Entering %s line %d\n", __FUNCTION__, __LINE__ ); }
	#define JT_LEAVE if( verbose ) { printk( KERN_CRIT "Leaving  %s line %d\n", __FUNCTION__, __LINE__ ); }
	#define JT_DPRINTK( fmt , arg... ) if( verbose ) { printk( KERN_CRIT fmt, ##arg ); }
#else
	#define BreakPoint()
	#define JT_ASSERT( Condition )
	#define JT_ENTER
	#define JT_LEAVE
	#define JT_DPRINTK( fmt , arg... )
#endif


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

// Initialization 
int init_module(void);
void cleanup_module(void);
int jt_init(struct device *);
STATIC int jtProbe(struct device *);
STATIC struct device * jtInitDevInstance( struct device * pDev,  UINT32 ControllerType, UINT8 PciBus, UINT8 PciDeviceFunction );
STATIC JT_STATUS jtDevReset(PDEV_INSTANCE pDevInstance);

// Queue management functions
INLINE JT_STATUS jtQueueCreate( JT_QUEUE * pQueue, int ElementCount );
INLINE BOOLEAN jtQueueIsEmpty( JT_QUEUE * pQueue );
INLINE BOOLEAN jtQueueIsFull( JT_QUEUE * pQueue );
INLINE void jtQueueEnqueue( JT_QUEUE * pQueue, void * pElement );
INLINE void jtQueueDequeue( JT_QUEUE * pQueue, void ** ppElement );
INLINE void jtQueueDestroy( JT_QUEUE * pQueue );

// Standard device methods 
STATIC int  jtDevOpen(struct device * pDev);
STATIC int  jtDevStartTransmit(struct sk_buff *skb, struct device * pDev);
STATIC void jtDevInterrupt(int irq, void * pDev, struct pt_regs *regs);
STATIC int  jtDevClose(struct device * pDev);
STATIC struct enet_statistics * jtDevGetStatistics(struct device * pDev);
STATIC int jtDevIoctl(struct device *pDev, struct ifreq *pIfr, int cmd);
STATIC void jtDevSetMulticastList(struct device * pDev);
STATIC int jtDevSetMacAddress(struct device * pDev, void * pNewAddr);
STATIC int jtDevChangeMtu(struct device *pDev, int NewMtu);

// Other Device related functions
STATIC void jtAddAddressToMulticastHash( DEV_INSTANCE * pDevInstance, UINT8 * pNetworkAddress );
INLINE void jtDevEnableInterrupts( PDEV_INSTANCE pDevInstance );
INLINE void jtDevDisableInterrupts( PDEV_INSTANCE pDevInstance );
STATIC void jtCsrWriteMacAddress( PDEV_INSTANCE pDevInstance, const UINT8 * pMacAddress);
STATIC void jtCsrReadMacAddress( PDEV_INSTANCE pDevInstance, UINT8 * pMacAddress);

// Phy functions 
STATIC JT_STATUS jtPhyFindPhy( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtPhyInitialize( PDEV_INSTANCE pDevInstance );
INLINE void      jtPhyDisableInterrupts( PDEV_INSTANCE pDevInstance );
INLINE void      jtPhyEnableInterrupts( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtPhyReadRegister( PDEV_INSTANCE pDevInstance, UINT8 PHYAddress, UINT8 PhyRegister, UINT16 * pData );
STATIC JT_STATUS jtPhyWriteRegister( PDEV_INSTANCE pDevInstance, UINT8 PHYAddress, UINT8 PhyRegister, UINT16 Data );
STATIC void      jtPhySetSpeed( PDEV_INSTANCE pDevInstance, UINT16 PhySpeed, PUINT16 pPhyControlReg );
STATIC JT_STATUS jtPhyHandleStatusChange( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtPhyGetLinkStatus( PDEV_INSTANCE pDevInstance, JT_PHY_STATUS *pCurrentlyConnected );
STATIC JT_STATUS jtPhyUpdateSpeedDuplex( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtPhyReadStatusRegister( PDEV_INSTANCE pDevInstance, UINT8 PHYAddress, PUINT16 pData );

// RX methods 
INLINE UINT32    jtRxGetFreeCount( PDEV_INSTANCE pDevInstance );
INLINE void      jtRxDecrementFreeCount( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtRxCreateQueues( PDEV_INSTANCE pDevInstance, int ElementCount );
STATIC JT_STATUS jtRxPDCCreate( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtRxPDLCreate( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtRxPDCInitialize( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtRxPDLInitialize( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtRxPDCReplenish( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtRxPDLReplenish( PDEV_INSTANCE pDevInstance );
STATIC void      jtRxStart( PDEV_INSTANCE pDevInstance );
INLINE JT_STATUS jtRxValidatePacketLength( PDEV_INSTANCE pDevInstance, UINT32 PacketByteCount );
INLINE JT_STATUS jtRxValidatePacketIntegrity( PDEV_INSTANCE pDevInstance, UINT32 PacketHeader1,	UINT32 PacketHeader2 );
INLINE void      jtRxHandOffPacket( DEV_INSTANCE * pDevInstance, struct sk_buff * pSkBuff,  UINT32 PacketHeader2 );
STATIC JT_STATUS jtRxPDCReceive( PDEV_INSTANCE pDevInstance, UINT32 EventStatus );
STATIC JT_STATUS jtRxPDLReceive( PDEV_INSTANCE pDevInstance, UINT32 EventStatus );
STATIC JT_STATUS jtRxPIOReceive( PDEV_INSTANCE pDevInstance, UINT32 EventStatus );
STATIC void      jtRxStop( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtRxPDCFlush( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtRxPDLFlush( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtRxPDCDestroy( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtRxPDLDestroy( PDEV_INSTANCE pDevInstance );

// TX methods 
INLINE UINT32    jtTxGetFreeCount( PDEV_INSTANCE pDevInstance );
INLINE void      jtTxDecrementFreeCount( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtTxPDxCreate( PDEV_INSTANCE pDevInstance) ;
STATIC JT_STATUS jtTxPDCCreate( PDEV_INSTANCE pDevInstance) ;
STATIC JT_STATUS jtTxPDLCreate( PDEV_INSTANCE pDevInstance) ;
STATIC JT_STATUS jtTxPDxInitialize( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtTxPDCInitialize( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtTxPDLInitialize( PDEV_INSTANCE pDevInstance );
STATIC void      jtTxStart( PDEV_INSTANCE pDevInstance );
STATIC int       jtTxPDxTransmit( PDEV_INSTANCE pDevInstance, struct sk_buff * pSkBuff );
STATIC void      jtTxPDCGetNextAvailable( DEV_INSTANCE * pDevInstance, void ** pPDC );
STATIC void      jtTxPDCAppendPacket( DEV_INSTANCE * pDevInstance, void * pPDC, UINT32 * pPDCLength,	struct sk_buff * pSkBuff );
STATIC void      jtTxPDCSend( DEV_INSTANCE * pDevInstance, TX_PDC * pPDC, UINT32 PDCLength );
STATIC void      jtTxPDLTransmit( DEV_INSTANCE * pDevInstance, struct sk_buff * pSkBuff );
STATIC int       jtTxPIOTransmit( PDEV_INSTANCE pDevInstance, struct sk_buff * pSkBuff );
STATIC JT_STATUS jtTxPDxDone( PDEV_INSTANCE pDevInstance, UINT32 TransmitDoneCount );
STATIC void      jtTxStop( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtTxPDxFlush( PDEV_INSTANCE pDevInstance );
STATIC JT_STATUS jtTxPDxDestroy( PDEV_INSTANCE pDevInstance );

// Matterhorn stuff
STATIC JT_STATUS jtStatusBufferCreate(PDEV_INSTANCE pDevInstance);
STATIC void jtStatusBufferDestroy(PDEV_INSTANCE pDevInstance);
STATIC void jtStatusBufferReset(PDEV_INSTANCE pDevInstance);

// Diagnostic stuff
#include "daltypes.h"
#include "dbgioctl.h"

STATIC void jtDevIoctlGetInfo( PDAC_INFO_IOCTL Command, struct device *pDev );
STATIC void jtDevIoctlSetInfo( PDAC_INFO_IOCTL Command, struct device *pDev );
STATIC void jtDevIoctlEnableDiagnostics (	PDAC_INFO_IOCTL Command, struct device *pDev );
STATIC void jtDevIoctlDisableDiagnostics (	PDAC_INFO_IOCTL Command, struct device *pDev );
STATIC void jtDevIoctlResetDiagnostics (	PDAC_INFO_IOCTL Command, struct device *pDev );
STATIC void jtDevIoctlTx ( PDAC_TX_IOCTL Command, struct device *pDev );
STATIC void jtDevIoctlRx ( PDAC_TX_IOCTL Command, struct device *pDev );

// Misc Methods
STATIC void jtDevResetAndRestart( PDEV_INSTANCE pDevInstance ); 
#if 0
	STATIC void      jtPrintMacAddress( const char * Message, const UINT8 * pAddress );
#endif
STATIC void      jtSizeToOrder( UINT32 ByteCount, UINT32 * pOrder, UINT32 * pActualByteCount );
STATIC JT_STATUS jtNop( PDEV_INSTANCE pDevInstance );

// Workaround Methods
UINT32 crc32buf(char *buf, long len);

/****************************************************************************
 * Low Level Routines
 ****************************************************************************/

#define jtFindNextPCIDevice( VendorId, DeviceId, Index, Bus, DevFunction ) \
     pcibios_find_device( VendorId, DeviceId, Index, Bus, DevFunction )
#define jtReadPCIConfiguration8( Bus, DevFunction, Offset, pValue ) \
     pcibios_read_config_byte( Bus, DevFunction, Offset, pValue )
#define jtReadPCIConfiguration16( Bus, DevFunction, Offset, pValue ) \
     pcibios_read_config_word( Bus, DevFunction, Offset, pValue )
#define jtReadPCIConfiguration32( Bus, DevFunction, Offset, pValue ) \
     pcibios_read_config_dword( Bus, DevFunction, Offset, pValue )
#define jtWritePCIConfiguration8( Bus, DevFunction, Offset, Value ) \
     pcibios_write_config_byte( Bus, DevFunction, Offset, Value )
#define jtWritePCIConfiguration16( Bus, DevFunction, Offset, Value ) \
     pcibios_write_config_word( Bus, DevFunction, Offset, Value )
#define jtWritePCIConfiguration32( Bus, DevFunction, Offset, Value ) \
     pcibios_write_config_dword( Bus, DevFunction, Offset, Value )
     
// This is IO mode
#if !defined(USE_MEMORY_BASE)	|| LINUX_VERSION_CODE < 0x20200

#define jtCsrIn8( pDevInstance, PortOffset, pValue ) \
     (*pValue = inb( pDevInstance->pDev->base_addr + PortOffset ))
#define jtCsrIn16( pDevInstance, PortOffset, pValue ) \
     (*pValue = inw( pDevInstance->pDev->base_addr + PortOffset ))
#define jtCsrIn32( pDevInstance, PortOffset, pValue ) \
     (*pValue = inl( pDevInstance->pDev->base_addr + PortOffset ))

#define jtCsrOut8( pDevInstance, PortOffset, Value ) \
     (outb( Value, pDevInstance->pDev->base_addr + PortOffset ))
#define jtCsrOut16( pDevInstance, PortOffset, Value ) \
     (outw( Value, pDevInstance->pDev->base_addr + PortOffset ))
#define jtCsrOut32( pDevInstance, PortOffset, Value ) \
     (outl( Value, pDevInstance->pDev->base_addr + PortOffset ))

// memory mode
#else  

// ideal case
#if TRUE //!defined(WORKAROUND_WRITE_COMBINE)

#define jtCsrIn8( pDevInstance, PortOffset, pValue ) \
	  (*pValue = readb( pDevInstance->MapAddr + PortOffset ))
#define jtCsrIn16( pDevInstance, PortOffset, pValue ) \
	  (*pValue = readw( pDevInstance->MapAddr + PortOffset ))
#define jtCsrIn32( pDevInstance, PortOffset, pValue ) \
	  (*pValue = readl( pDevInstance->MapAddr + PortOffset ))

#define jtCsrOut8( pDevInstance, PortOffset, Value ) \
		(writeb( Value, pDevInstance->MapAddr + PortOffset ))
#define jtCsrOut16( pDevInstance, PortOffset, Value ) \
		(writew( Value, pDevInstance->MapAddr + PortOffset ))
#define jtCsrOut32( pDevInstance, PortOffset, Value ) \
		(writel( Value, pDevInstance->MapAddr + PortOffset))

// workaround for write combine
#else

#define jtCsrIn8( pDevInstance, PortOffset, pValue ) \
	  {*pValue = readb( pDevInstance->MapAddr + PortOffset ); pDevInstance->LastPortOffset = 0xffff;}
#define jtCsrIn16( pDevInstance, PortOffset, pValue ) \
	  {*pValue = readw( pDevInstance->MapAddr + PortOffset );	pDevInstance->LastPortOffset = 0xffff;}
#define jtCsrIn32( pDevInstance, PortOffset, pValue ) \
	  {*pValue = readl( pDevInstance->MapAddr + PortOffset );	pDevInstance->LastPortOffset = 0xffff;}

#define jtCsrOut8( pDevInstance, PortOffset, Value ) \
		{if ( (PortOffset&~0x3) != pDevInstance->LastPortOffset && (PortOffset&~0x3) != pDevInstance->LastPortOffset+4) { \
		writeb( Value, pDevInstance->MapAddr + PortOffset ); \
		pDevInstance->LastPortOffset = PortOffset&~0x3;} \
		else { \
		outb( Value, pDevInstance->pDev->base_addr + PortOffset ); \
		pDevInstance->LastPortOffset = 0xffff; } }
#define jtCsrOut16( pDevInstance, PortOffset, Value ) \
		{if ( (PortOffset&~0x3) != pDevInstance->LastPortOffset && (PortOffset&~0x3) != pDevInstance->LastPortOffset+4) { \
		writew( Value, pDevInstance->MapAddr + PortOffset ); \
		pDevInstance->LastPortOffset = PortOffset&~0x3;} \
		else { \
		outw( Value, pDevInstance->pDev->base_addr + PortOffset ); \
		pDevInstance->LastPortOffset = 0xffff; } }
#define jtCsrOut32( pDevInstance, PortOffset, Value ) \
		{if (PortOffset != pDevInstance->LastPortOffset && PortOffset != pDevInstance->LastPortOffset+4) { \
		writel( Value, pDevInstance->MapAddr + PortOffset ); \
		pDevInstance->LastPortOffset = PortOffset; } \
		else { \
		outl( Value, pDevInstance->pDev->base_addr + PortOffset ); \
		pDevInstance->LastPortOffset = 0xffff; } }

#endif

#endif 

#define jtCsrIn32ToBuffer( pDevInstance, PortOffset, pBuffer, DwordCount ) \
     (insl( pDevInstance->pDev->base_addr + PortOffset, pBuffer, DwordCount ))
#define jtCsrOut32FromBuffer( pDevInstance, PortOffset, pBuffer, DwordCount ) \
     (outsl( pDevInstance->pDev->base_addr + PortOffset, pBuffer, DwordCount ))

/*****************************************************************************
 *
 * Queue management functions
 *
 ****************************************************************************/

#if defined(JT_DEBUG)

//----------------------------------------------------------------------------
//
// FUNCTION:    jtQueuePrint
//
// DESCRIPTION: Debugging only. 
//              Print the fields of a queue.
//
// PARAMETERS:
//  pQueue      An queue structure to be initialized.
//  Name        Arbitrary string to prefix the printout. 
//
//----------------------------------------------------------------------------

void jtQueuePrint( JT_QUEUE * pQueue, const char * Name )
{
	printk(
		KERN_CRIT "%s: Head %4d, Tail %4d, Capacity %4d, Elements %p\n",
		Name, pQueue->Head, pQueue->Tail, pQueue->Capacity, pQueue->Elements
	);
}

//----------------------------------------------------------------------------
//
// FUNCTION:    jtQueuePrintAll
//
// DESCRIPTION: Debugging only. 
//              Print the fields of all of the driver queues for the first
//              device.
//
//----------------------------------------------------------------------------

void jtQueuePrintAll(void)
{
	DEV_INSTANCE * pDevInstance = (DEV_INSTANCE*) (jtControllers[0]->priv);

	unsigned long flags = 0;
	save_flags(flags); cli();

	printk(KERN_CRIT "Dumping all queues:\n");
	jtQueuePrint( &pDevInstance->TxPDCAvailableQueue, "Tx PDC Av" );
	jtQueuePrint( &pDevInstance->TxPDLAvailableQueue, "Tx PDL Av" );
	jtQueuePrint( &pDevInstance->TxPDxInProgressQueue,"Tx PDx Pr" );
	jtQueuePrint( &pDevInstance->RxPDxAvailableQueue, "Rx PDx Av" );
	jtQueuePrint( &pDevInstance->RxPDxInProgressQueue,"Rx PDx Pr" );

	restore_flags(flags);
}

#endif

//----------------------------------------------------------------------------
//
// FUNCTION:    jtQueueCreate
//
// DESCRIPTION: Initialize a queue structure, allocating space for its
//              elements.
//
// PARAMETERS:
//  pQueue      An queue structure to be initialized.
//  MaxElementCount  The maximum number of elements that can be stored in
//                   the queue at one time.
//
//----------------------------------------------------------------------------

INLINE JT_STATUS jtQueueCreate( JT_QUEUE * pQueue, int MaxElementCount )
{
	JT_ASSERT( MaxElementCount > 0 );
	JT_ASSERT( pQueue != 0 );

	// Initialize fields
	pQueue->Elements = 0;
	pQueue->Head = 0;
	pQueue->Tail = 0;
	pQueue->Capacity = MaxElementCount+1;

	// Allocate space for element pointers
	pQueue->Elements = (void **) kmalloc( sizeof(void *) * pQueue->Capacity, GFP_KERNEL );
	if( pQueue->Elements == NULL )
	{
		return JT_STATUS_RESOURCES;
	}
	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtQueueIsEmpty
//
// DESCRIPTION: Query whether the queue is currently empty or not.
//
// PARAMETERS:
//  pQueue      The queue to inspect.
//
// RETURNS:     TRUE if the queue is empty.
//
//----------------------------------------------------------------------------

INLINE BOOLEAN jtQueueIsEmpty( JT_QUEUE * pQueue )
{
	barrier();
	JT_ASSERT( pQueue != 0 );
	JT_ASSERT( (pQueue->Head >= 0) && (pQueue->Head < pQueue->Capacity) );
	JT_ASSERT( (pQueue->Tail >= 0) && (pQueue->Tail < pQueue->Capacity) );
	return (BOOLEAN) (pQueue->Head == pQueue->Tail);
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtQueueIsFull
//
// DESCRIPTION: Query whether the queue is currently full or not.
//
// PARAMETERS:
//  pQueue      The queue to inspect.
//
// RETURNS:     TRUE if the queue is full.
//
//----------------------------------------------------------------------------

INLINE BOOLEAN jtQueueIsFull( JT_QUEUE * pQueue )
{
	barrier();
	JT_ASSERT( pQueue != 0 );
	JT_ASSERT( (pQueue->Head >= 0) && (pQueue->Head < pQueue->Capacity) );
	JT_ASSERT( (pQueue->Tail >= 0) && (pQueue->Tail < pQueue->Capacity) );
	return (BOOLEAN) (pQueue->Head == ( (pQueue->Tail + 1) % pQueue->Capacity ) );
}

//----------------------------------------------------------------------------
//
// FUNCTION:    jtQueueEnqueue
//
// DESCRIPTION: Add an element to the tail of a queue.
//
// PARAMETERS:
//  pQueue      The queue to store the element in.
//  pElement    The element to add.
//
//----------------------------------------------------------------------------

INLINE void jtQueueEnqueue( JT_QUEUE * pQueue, void * pElement )
{
	barrier();
	JT_ASSERT( pQueue != 0 );
	JT_ASSERT( ! jtQueueIsFull(pQueue) );
	JT_ASSERT( (pQueue->Head >= 0) && (pQueue->Head < pQueue->Capacity) );
	JT_ASSERT( (pQueue->Tail >= 0) && (pQueue->Tail < pQueue->Capacity) );
	pQueue->Elements[ pQueue->Tail ] = pElement;
	pQueue->Tail = (pQueue->Tail + 1) % pQueue->Capacity;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtQueueDequeue
//
// DESCRIPTION: Remove an element from the head of a queue.
//
// PARAMETERS:
//  pQueue      The queue to get the element from.
//
// RETURNS:     
//  ppElement   The element removed.
//
//----------------------------------------------------------------------------

INLINE void jtQueueDequeue( JT_QUEUE * pQueue, void ** ppElement )
{
	barrier();
	JT_ASSERT( pQueue != 0 );
	JT_ASSERT( ! jtQueueIsEmpty(pQueue) );
	JT_ASSERT( (pQueue->Head >= 0) && (pQueue->Head < pQueue->Capacity) );
	JT_ASSERT( (pQueue->Tail >= 0) && (pQueue->Tail < pQueue->Capacity) );
	*ppElement = pQueue->Elements[ pQueue->Head ];
	pQueue->Head = (pQueue->Head + 1) % pQueue->Capacity;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtQueueDestroy
//
// DESCRIPTION: Deallocate space allocated for a queue.
//
// PARAMETERS:
//  pQueue      An queue structure to be destroyed.
//
//----------------------------------------------------------------------------

INLINE void jtQueueDestroy( JT_QUEUE * pQueue )
{
	JT_ASSERT( pQueue != 0 );
	kfree( pQueue->Elements );
}


/*****************************************************************************
 *
 * Inline routines which must come before other code
 *
 ****************************************************************************/

//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevEnableInterrupts
//
// DESCRIPTION: This function will enable chip interrupts (Master Interrupts)
//              from the controller.
//
//----------------------------------------------------------------------------
INLINE void jtDevEnableInterrupts ( PDEV_INSTANCE pDevInstance )
{
	// Set Interrupt Mask Register: Bit #7 Interrupt Enable Mask
	jtCsrOut32 ( pDevInstance, INTR_MASK_REG, SERECL0 | INENMS );	
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevDisableInterrupts
//
// DESCRIPTION: This function will disable chip interrupts (Master Interrupts)
//               from the controller.
//
//----------------------------------------------------------------------------
INLINE void jtDevDisableInterrupts ( PDEV_INSTANCE pDevInstance )
{
	// Clear Interrupt Mask Register: Bit #7 Interrupt Enable Mask
	jtCsrOut32 ( pDevInstance, INTR_MASK_REG, INENMS );
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtPhyEnableInterrupts
//
// DESCRIPTION: Enable phy status interrupts from the controller. 
//
//----------------------------------------------------------------------------

INLINE void jtPhyEnableInterrupts ( PDEV_INSTANCE pDevInstance )
{
	// Set Interrupt Mask Register: Bit #13 Phy Status Interrupt
	jtCsrOut32 ( pDevInstance, INTR_MASK_REG, SERECL1 | PHLASTMS );
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtPhyDisableInterrupts
//
// DESCRIPTION: Disable phy status interrupts from the controller.
//
//----------------------------------------------------------------------------

INLINE void jtPhyDisableInterrupts ( PDEV_INSTANCE pDevInstance )
{
	// Clear Interrupt Mask Register: Bit #13 Phy Status Interrupt
	jtCsrOut32 ( pDevInstance, INTR_MASK_REG, PHLASTMS );
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxGetFreeCount
//
// DESCRIPTION: Return the number of free receive command queue entries on
//              the controller. The count of entries is cached in the
//              device instance, and refreshed as needed from the controller.
//              Thus this will usually be an undercount of the actual value.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//
// RETURNS:       The count of free entries. At least this many receive
//                commands may be sent to the controller.
//
//----------------------------------------------------------------------------

INLINE UINT32 jtRxGetFreeCount( PDEV_INSTANCE pDevInstance )
{
	// When the cached count reaches zero
	if( pDevInstance->RxPDxCommandFreeCount == 0 )
	{
		UINT8 CommandFreeCount = 0;
      
		// Reread the controller's count of how many more commands it can take
		jtCsrIn8( 
				pDevInstance, 
				CMD_STATUS_REG+3, 
				&CommandFreeCount
			);
		CommandFreeCount = CommandFreeCount & 0x3f;

		// Free commands are decremented (workaround)
		if ( CommandFreeCount )
			CommandFreeCount = CommandFreeCount - 1;

		pDevInstance->RxPDxCommandFreeCount = CommandFreeCount;
	}

	return pDevInstance->RxPDxCommandFreeCount;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxDecrementFreeCount
//
// DESCRIPTION: Decrement the cached value of the number of free receive queue 
//              entries. The cache value will be refreshed from the controller
//              when it reaches zero.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//
// RETURNS:       The count of free entries. At least this many receive
//                commands may be sent to the controller.
//
//----------------------------------------------------------------------------

INLINE void jtRxDecrementFreeCount( PDEV_INSTANCE pDevInstance )
{
	pDevInstance->RxPDxCommandFreeCount--;

	// Refresh the count if zero
	(void) jtRxGetFreeCount( pDevInstance );
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxGetFreeCount
//
// DESCRIPTION: Return the number of free transmit command queue entries on
//              the controller. The count of entries is cached in the
//              device instance, and refreshed as needed from the controller.
//              Thus this will usually be an undercount of the actual value.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//
// RETURNS:       The count of free entries. At least this many transmit
//                commands may be sent to the controller.
//
//----------------------------------------------------------------------------

INLINE UINT32 jtTxGetFreeCount( PDEV_INSTANCE pDevInstance )
{
	// When the cached count reaches zero
	if( pDevInstance->TxPDxCommandFreeCount == 0 )
	{
		UINT8 CommandFreeCount = 0;

		// Reread the controller's count of how many more commands it can take
		jtCsrIn8( 
				pDevInstance, 
				CMD_STATUS_REG+2, 
				&CommandFreeCount
			);
		CommandFreeCount = CommandFreeCount & 0x3f;

		// Decrement the value just read (workaround)
		if( CommandFreeCount )
			CommandFreeCount = CommandFreeCount-1;							

		pDevInstance->TxPDxCommandFreeCount = CommandFreeCount;

		// If controller is still full
		if( !CommandFreeCount )
		{
			 // Signal that the transmitter is now busy
			 set_bit( 0, (void *) &pDevInstance->TxFull );
		}
	}

	return pDevInstance->TxPDxCommandFreeCount;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxDecrementFreeCount
//
// DESCRIPTION: Decrement the cached value of the number of free transmit queue 
//              entries. The cache value will be refreshed from the controller
//              when it reaches zero.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//
// RETURNS:       The count of free entries. At least this many transmit
//                commands may be sent to the controller.
//
//----------------------------------------------------------------------------

INLINE void jtTxDecrementFreeCount( PDEV_INSTANCE pDevInstance )
{
	pDevInstance->TxPDxCommandFreeCount--;

	// Refresh the count if zero
	(void) jtTxGetFreeCount( pDevInstance );
		
}


/*****************************************************************************
 *
 * Initialization/Cleanup
 *
 ****************************************************************************/

#ifdef MODULE

//----------------------------------------------------------------------------
//
// FUNCTION:    init_module
//
// DESCRIPTION: This function called when the module is loaded by insmod.
//              Find and initialize installed NetCelerator controllers.
//
// RETURNS:     0 if any NetCelerator controllers were found, or
//              -ENODEV is no controllers were found.
//
//--------------------------------------------------------------------------
int init_module(void)
{
	// Export no symbols 
#if !defined(JT_DEBUG)
	EXPORT_NO_SYMBOLS;
#endif   

	// Scan for controllers 
	jtControllerCount = 0;
	jtProbe(NULL);

	// If found any controllers 
	if (jtControllerCount > 0) 
	{
		return 0;
	} 

	// Otherwise return error code 
	return -ENODEV;
}

//----------------------------------------------------------------------------
//
// FUNCTION:    cleanup_module
//
// DESCRIPTION: This function is called when the module is unloaded by rmmod.
//              Shutdown all NetCelerator controllers and release all resources.
//
//----------------------------------------------------------------------------
void cleanup_module(void)
{
	int Index;

	// Iterate over each installed controller
	for( Index=0; Index<jtControllerCount; ++Index )
	{
		struct device * pDev = jtControllers[Index];
		PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;

		// Release IO region
		release_region( pDev->base_addr, pDevInstance->IORange );

		// Unlink device from devlist
		unregister_netdev(pDev);

#if defined(USE_MEMORY_BASE) && LINUX_VERSION_CODE > 0x20200
		iounmap((PVOID)pDevInstance->MapAddr);
#endif
		// Release memory for private data
		kfree(pDevInstance);
	}

}

#else // !defined(MODULE) 

//----------------------------------------------------------------------------
//
// FUNCTION:    jt_init
//
// DESCRIPTION: Scan the PCI bus for NetCelerator controllers.
//
// PARAMETERS:
//   pFirstDev  The device structure to use for the first controller.
//
// RETURNS:     0 if any NetCelerator controllers were found, or
//              -ENODEV is no controllers were found.
//
//----------------------------------------------------------------------------

int jt_init(struct device * pFirstDev)
{
	// Scan for controllers 
	jtControllerCount = 0;
	jtProbe(pFirstDev);

	// If found any controllers 
	if (jtControllerCount > 0) 
	{
		return 0;
	} 
	// Otherwise return error code 
	return -ENODEV;
}

#endif // !defined(MODULE)

//----------------------------------------------------------------------------
//
// FUNCTION:    jtProbe
//
// DESCRIPTION: Scan the PCI bus for all NetCelerator controllers.
//
// PARAMETERS:
//  pFirstDev   This is the device structure to use for the first
//              controller detected when the driver is part of the kernel.
//              When the driver is a module, this is always NULL.
// RETURNS:     The number of controller detected.
//
//----------------------------------------------------------------------------
 
STATIC int jtProbe(struct device * pFirstDev)
{
#if defined(JT_DEBUG)
	// Should we sleep?
#if (LINUX_VERSION_CODE < 0x20100)				
	if( sleep_init )
	{
		// Sleep for 'sleep_init' seconds
		unsigned long j = jiffies + sleep_init*HZ;
		current->timeout = j;
		current->state = TASK_INTERRUPTIBLE;
		schedule();
		current->timeout = 0;
	}
#endif

	// Should we stop?
	if( break_init )
	{
		BreakPoint();
	}
#endif

#if defined(CONFIG_PCI)
	if (pcibios_present()) 
	{
		int Index = 0, MercuryAdapters = JT_MAX_CONTROLLER_COUNT;
		UINT8 PciBus = 0;
		UINT8 PciDeviceFunction = 0;

		// Iterate over all possible NetCelerator controllers (Mercury)
		for (Index = 0; Index < JT_MAX_CONTROLLER_COUNT; ++Index)
		{
			struct device * pDev;
			int Result;
	 
			// Look for the Index-th controller 
			Result = jtFindNextPCIDevice(
					PCI_VENDOR_ID_LEVELONE,
					PCI_DEVICE_ID_LXT1001,
					Index,
					&PciBus,
					&PciDeviceFunction
				);

				// If no more controllers are present
				if (Result == PCIBIOS_DEVICE_NOT_FOUND)
				{
					MercuryAdapters = Index;
					break;
				}
				// Or if an error occurred 
				else 
					if (Result != PCIBIOS_SUCCESSFUL)
					{
						printk(
								KERN_ERR __FILE__ ": Pci error probing device"
#if (LINUX_VERSION_CODE < 0x20100)				
								": %s\n",
								pcibios_strerror(Result)
#endif
							);
						break;
					}
	 
			// If this is the first device detected 
			pDev = NULL;
			if (Index == 0) 
			{
				pDev = pFirstDev;
			}
	 
			// Initialize device structure 
			pDev = jtInitDevInstance(pDev, LXT1001, PciBus, PciDeviceFunction);

			// Increment count of controllers found 
			jtControllers[jtControllerCount] = pDev;
			jtControllerCount++;
		}

		// Iterate over all possible NetCelerator controllers (matterhorn)
		for (Index = MercuryAdapters; Index < JT_MAX_CONTROLLER_COUNT; ++Index)
		{
			struct device * pDev;
			int Result;
	
			// Look for the Index-th controller
			Result = jtFindNextPCIDevice(
					PCI_VENDOR_ID_LEVELONE,
					PCI_DEVICE_ID_LXT1002,
					Index-MercuryAdapters,
					&PciBus,
					&PciDeviceFunction
				);

				// If no more controllers are present
				if (Result == PCIBIOS_DEVICE_NOT_FOUND)
				{
					break;
				}
				// Or if an error occurred
				else
					if (Result != PCIBIOS_SUCCESSFUL)
					{
						printk(
								KERN_ERR __FILE__ ": Pci error probing device"
#if (LINUX_VERSION_CODE < 0x20100)				
								": %s\n",
								pcibios_strerror(Result)
#endif
							);
						break;
					}
	
			// If this is the first device detected
			pDev = NULL;
			if (Index == 0)
			{
				pDev = pFirstDev;
			}
	
			// Initialize device structure
			pDev = jtInitDevInstance(pDev, LXT1002, PciBus, PciDeviceFunction);

			// Increment count of controllers found
			jtControllers[jtControllerCount] = pDev;
			jtControllerCount++;
		}
	} // if pcibios_present
#endif // defined(CONFIG_PCI) 
	return jtControllerCount;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtInitDevInstance
//
// DESCRIPTION: Initialize a device object for a newly detected controller.
//
// PARAMETERS:
//  pDev        A preallocated device structure, or NULL.
//	ControllerType  The kind of controller found
//  PciBus      The pci bus that the controller is attached to.
//  PciDeviceFunction  The pci device and function number of the controller.
//
// RETURNS:     The allocated device object for the controller.
//
//----------------------------------------------------------------------------

STATIC struct device * jtInitDevInstance(
		struct device * pDev,
		UINT32 ControllerType,
		UINT8 PciBus,
		UINT8 PciDeviceFunction
	)
{
	DEV_INSTANCE * pDevInstance = NULL;
	UINT32 IOBase = 0;
	UINT8 Irq = 0;
	UINT32 MemoryBaseDword = 0;
	UINT64 MemoryBase = 0;
	UINT8 CacheLineSize = 0;
	UINT16 PciCommand = 0;
	int Result = JT_STATUS_SUCCESS;
#if defined(WORKAROUND_LATENCY)
	UINT8 PciLatency;
#endif

	// Find/Allocate device structure.
	// If pDev is not NULL, it will use the given
	//   pDev device structure.
	// If pDev is NULL, it will look for an existing,
	//   correctly named device in the device chain. 
	// If this fails, it will allocate a new device structure
	//   and insert it into the device chain.
	//
	// The name of the device will be set to the next available ethN name.
	pDev = init_etherdev(pDev, 0);

	// Allocate controller private data
	pDev->priv = kmalloc(sizeof(DEV_INSTANCE), GFP_KERNEL);
	if (pDev->priv == NULL) 
	{
		return NULL;
	}
	memset(pDev->priv, 0, sizeof(DEV_INSTANCE));
	pDevInstance = (DEV_INSTANCE *) pDev->priv;
	pDevInstance->pDev = pDev;

	// Device specific
	pDevInstance->ControllerType = ControllerType;
	
	// Setup controller hardware parameters
	pDevInstance->IORange = 256;
	pDevInstance->ROMBaseLow = 0;
	pDevInstance->ROMBaseHigh = 0;
	pDevInstance->ROMRange = 0;
	pDevInstance->PciBus = PciBus;
	pDevInstance->PciDeviceFunction = PciDeviceFunction;

	// Setup Phy parameters to default
	pDevInstance->OverrideSpeed = LINK_SPEED_AUTO;
	pDevInstance->OverrideDuplexMode = DUPLEX_MODE_AUTO;
	pDevInstance->Speed = 0;
	pDevInstance->DuplexMode = 0;

	// Setup Tx parameters to default
	pDevInstance->TxIOMode = TX_IOMODE_BUSMASTER;
	if (ControllerType == LXT1002)
		pDevInstance->TxFlowControl = TRUE;
	else
		pDevInstance->TxFlowControl = FALSE;
	pDevInstance->TxPDxThreshold = 800;
	pDevInstance->TxPDCTableCount = TX_PDC_TABLE_COUNT;
	pDevInstance->TxPDCCount = TX_PDC_TABLE_COUNT;
	pDevInstance->TxPDLCount = TX_PDL_TABLE_COUNT;
	pDevInstance->TxPDLAlignment = PDL_MIN_ALIGNMENT;

	// Setup Rx parameters to default
	pDevInstance->RxIOMode = RX_IOMODE_PDC;
	if (ControllerType == LXT1002)
		pDevInstance->RxFlowControl = TRUE;
	else
		pDevInstance->RxFlowControl = FALSE;
	pDevInstance->RxPDCTableCount = RX_PDC_TABLE_COUNT;
	pDevInstance->RxPDCCount = RX_PDC_TABLE_COUNT;
	pDevInstance->RxPDLCount = RX_PDC_TABLE_COUNT;
	pDevInstance->RxPDLAlignment = PDL_MIN_ALIGNMENT;

	// Setup misc
	pDevInstance->MagicPacketMode = MAGIC_PACKET_DISABLED;

	// Diags
	pDevInstance->DiagnosticsStatus	= DIAGS_OFF;

	// Now read any user configured parameters
	// Maximum Tranfer Unit
	if( mtu[jtControllerCount] > 0 )
	{
		jtDevChangeMtu( pDev, mtu[jtControllerCount] );
	}

	// PIO or PDL/PDC Transmit mode
	switch( tx_mode[jtControllerCount] )
	{
		case TX_IOMODE_PIO:
		case TX_IOMODE_BUSMASTER:
			pDevInstance->TxIOMode = tx_mode[jtControllerCount];
			break;
	}
  
  // CRC software check
#if defined(WORKAROUND_CRC_CHECK)
  pDevInstance->RxCrcCheck = rx_crc[jtControllerCount];
#endif
   
	// PDC/PDL ring size
	if( (tx_ringsize[jtControllerCount] > 0) &&
		 (tx_ringsize[jtControllerCount] <= pDevInstance->TxPDCTableCount) )
	{
		if (tx_ringsize[jtControllerCount] <=	TX_PDC_TABLE_COUNT )
			pDevInstance->TxPDCCount = tx_ringsize[jtControllerCount];
		pDevInstance->TxPDLCount = tx_ringsize[jtControllerCount];
	}
   
	// PDC/PDL Threshold
	if( tx_threshold[jtControllerCount] >= 0 )
	{
		pDevInstance->TxPDxThreshold = tx_threshold[jtControllerCount];
	}

	// Whether to send PAUSE frames	(only for LXT1002)
	if (ControllerType == LXT1002)
		switch( tx_flowcontrol[jtControllerCount]  )
		{
			case 0:
			case 1:
				pDevInstance->TxFlowControl = tx_flowcontrol[jtControllerCount];
		}

	// PIO, PDL or PDC Receive mode
	switch( rx_mode[jtControllerCount] )
	{
		case RX_IOMODE_PIO:
		case RX_IOMODE_PDC:
		case RX_IOMODE_PDL:
			pDevInstance->RxIOMode = rx_mode[jtControllerCount];
			break;
	}
   
	// PDC/PDL ring size
	if( (rx_ringsize[jtControllerCount] > 0) &&
		 (rx_ringsize[jtControllerCount] <= pDevInstance->RxPDCTableCount) )
	{
		pDevInstance->RxPDCCount = rx_ringsize[jtControllerCount];
		pDevInstance->RxPDLCount = rx_ringsize[jtControllerCount];
	}
   
	// Whether to honor PAUSE frames send by link partner (only for LXT1002)
	if (ControllerType == LXT1002)
		switch( rx_flowcontrol[jtControllerCount]  )
		{
			case 0:
			case 1:
				pDevInstance->RxFlowControl = rx_flowcontrol[jtControllerCount];
		}
   
  pDevInstance->OverrideSpeed = speed[jtControllerCount];
    
	// Link duplex mode
	if( duplex[jtControllerCount] != NULL )
	{
		if( strcmp("half", duplex[jtControllerCount]) == 0 )
		{
			pDevInstance->OverrideDuplexMode = DUPLEX_MODE_HALF;
		} 
		else 
			if ( strcmp("full", duplex[jtControllerCount]) == 0 )
			{ 
				pDevInstance->OverrideDuplexMode = DUPLEX_MODE_FULL;
			}
	}
   
#if defined(__SMP__) && defined(PERFORMANCE_TX)
		spin_lock_init(&pDevInstance->DriverLock);
#endif

	// Read the pci interrupt used by the controller
	(void) jtReadPCIConfiguration8(
			PciBus,
			PciDeviceFunction,
			PCI_INTERRUPT_LINE,
			&Irq);
	pDev->irq = Irq;
   
	//Read the PCI IO base address and mask off the flag bits 
	(void) jtReadPCIConfiguration32(
			PciBus, 
			PciDeviceFunction,
			PCI_BASE_ADDRESS_0,
			&IOBase);
	pDev->base_addr = IOBase & ~0x03;
   
	(void) jtReadPCIConfiguration32(PciBus, PciDeviceFunction,
							  PCI_BASE_ADDRESS_1, &MemoryBaseDword);

	// We do a request_region() only to register /proc/ioports info.
	request_region(pDev->base_addr, pDevInstance->IORange, pDev->name);

	// Read the 64 bit PCI memory base address one dword at a time,
	// mash the dwords together, and mask off the flag bits
	(void) jtReadPCIConfiguration32(
			PciBus,
			PciDeviceFunction,
			PCI_BASE_ADDRESS_1,
			&MemoryBaseDword);
	MemoryBase = MemoryBaseDword & ~0x07;
	(void) jtReadPCIConfiguration32(
			PciBus,
			PciDeviceFunction,
			PCI_BASE_ADDRESS_2,
			&MemoryBaseDword);
	MemoryBase |= ((UINT64) (MemoryBaseDword & ~0x07)) << 32;
	pDev->mem_start = MemoryBase;
	pDev->mem_end = pDev->mem_start + pDevInstance->IORange;

#if defined(USE_MEMORY_BASE) && LINUX_VERSION_CODE > 0x20200
	pDevInstance->MapAddr	= (UINT32)ioremap(MemoryBase & ~0xf, pDevInstance->IORange);
#endif
   
	// Get the Cache Line Size parameter
	(void) jtReadPCIConfiguration8 (
			PciBus, PciDeviceFunction,
			PCI_CACHE_LINE_SIZE,
			&CacheLineSize
		);
	if ( CacheLineSize == 0 )
	{
		// Set the default to 4 DWORDS
		pDevInstance->CacheLineSize = 32;
		//      printk(KERN_INFO "Using default cache line size\n");
	}
	else
	{
		// Cache Line Size value from the PCI Configuration space is in DWORDS
		pDevInstance->CacheLineSize = CacheLineSize * 4;

		// Disable Memory Write and Invalidate for the controller
		(void) jtReadPCIConfiguration16 (
				PciBus, PciDeviceFunction,
				PCI_COMMAND,
				&PciCommand
			);
#if !defined(WORKAROUND_MWI)
		if ((PciCommand & PCI_COMMAND_INVALIDATE) != PCI_COMMAND_INVALIDATE)
		{
			PciCommand |= PCI_COMMAND_INVALIDATE;
			(void) jtWritePCIConfiguration16 (
					PciBus, PciDeviceFunction,
					PCI_COMMAND,
					PciCommand
				);               
		}
#else         
		if ((PciCommand & PCI_COMMAND_INVALIDATE) == PCI_COMMAND_INVALIDATE)
		{
			PciCommand &= (~PCI_COMMAND_INVALIDATE);
			(void) jtWritePCIConfiguration16 (
					PciBus, PciDeviceFunction,
					PCI_COMMAND,
					PciCommand
				);               
		}         
#endif
	}

	// Set the PDL alignment to cache line length
	if( pDevInstance->CacheLineSize > PDL_MIN_ALIGNMENT )
	{
		pDevInstance->TxPDLAlignment = pDevInstance->CacheLineSize;
		pDevInstance->RxPDLAlignment = pDevInstance->CacheLineSize;
	}

	// Read the permanent MAC address
	jtCsrReadMacAddress(pDevInstance, pDevInstance->NodeAddress);
	memcpy(pDev->dev_addr, (const UINT8 *) (pDevInstance->NodeAddress), ETH_ALEN);

	// Reset and initialize the device
	Result = jtDevReset ( pDevInstance );
	if ( Result != 0 )
	{           
		printk(KERN_WARNING "%s: Error resetting controller.\n", pDev->name);
	}
   
   // Read the ROM Address
	(void) jtReadPCIConfiguration32(
			PciBus, PciDeviceFunction,
			PCI_ROM_ADDRESS,
			&pDevInstance->ROMBaseLow
		);
	pDevInstance->ROMRange = 0x0100000;

	// Install device methods
	pDev->open = &jtDevOpen;
	pDev->hard_start_xmit = &jtDevStartTransmit;
	pDev->stop = &jtDevClose;
	pDev->get_stats = &jtDevGetStatistics;
	pDev->do_ioctl = &jtDevIoctl;
	pDev->set_multicast_list = &jtDevSetMulticastList;
	pDev->set_mac_address = &jtDevSetMacAddress;
	pDev->change_mtu = &jtDevChangeMtu;

	switch( pDevInstance->RxIOMode )
	{
		case RX_IOMODE_PDC:
			pDevInstance->pRxCreate    = &jtRxPDCCreate;
			pDevInstance->pRxInitialize= &jtRxPDCInitialize;
			pDevInstance->pRxReplenish = &jtRxPDCReplenish;
			pDevInstance->pRxReceive   = &jtRxPDCReceive;
			pDevInstance->pRxFlush     = &jtRxPDCFlush;
			pDevInstance->pRxDestroy   = &jtRxPDCDestroy;
			break;
		case RX_IOMODE_PDL:
			pDevInstance->pRxCreate    = &jtRxPDLCreate;
			pDevInstance->pRxInitialize= &jtRxPDLInitialize;
			pDevInstance->pRxReplenish = &jtRxPDLReplenish;
			pDevInstance->pRxReceive   = &jtRxPDLReceive;
			pDevInstance->pRxFlush     = &jtRxPDLFlush;
			pDevInstance->pRxDestroy   = &jtRxPDLDestroy;
			break;
		case RX_IOMODE_PIO:
			pDevInstance->pRxCreate    = &jtNop;
			pDevInstance->pRxInitialize= &jtNop;
			pDevInstance->pRxReplenish = &jtNop;
			pDevInstance->pRxReceive   = &jtRxPIOReceive;
			pDevInstance->pRxFlush     = &jtNop;
			pDevInstance->pRxDestroy   = &jtNop;
			break;
	}

	switch( pDevInstance->TxIOMode )
	{
		case TX_IOMODE_BUSMASTER:
			pDevInstance->pTxCreate    = &jtTxPDxCreate;
			pDevInstance->pTxInitialize= &jtTxPDxInitialize;
			pDevInstance->pTxTransmit  = &jtTxPDxTransmit;
			pDevInstance->pTxFlush     = &jtTxPDxFlush;
			pDevInstance->pTxDestroy   = &jtTxPDxDestroy;
			break;
		case TX_IOMODE_PIO:
			pDevInstance->pTxCreate    = &jtNop;
			pDevInstance->pTxInitialize= &jtNop;
			pDevInstance->pTxTransmit  = &jtTxPIOTransmit;
			pDevInstance->pTxFlush     = &jtNop;
			pDevInstance->pTxDestroy   = &jtNop;
			break;
	}
   
	// Register the device 
	register_netdev(pDev);

	// Print message announcing controller detection
#if !defined JT_DEBUG
	// Low priority if not in debug
	printk(KERN_INFO "%s", version);
	printk(
			KERN_INFO "%s: %s %s at IO %#3x, Mem %#lx, IRQ %d.\n",
			pDev->name,
			jtProductName,
			jtControllerName[ControllerType],
			(int)  pDev->base_addr,
			(long) pDev->mem_start,
			(int)  pDev->irq
		);
	printk(
			KERN_INFO "%s: Tx %s Mode, Rx %s Mode, Tx Threshold %d bytes.\n",
			pDev->name,
			pDevInstance->TxIOMode == TX_IOMODE_PIO ? "PIO"
			: "PDx",
			pDevInstance->RxIOMode == RX_IOMODE_PIO ? "PIO"
			: pDevInstance->RxIOMode == RX_IOMODE_PDL ? "PDL"
			: "PDC",
			pDevInstance->TxPDxThreshold
		);
#else
	JT_DPRINTK("%s", version);
	JT_DPRINTK(
			"%s: %s %s at IO %#3x, Mem %#lx, IRQ %d.\n",
			pDev->name,
			jtProductName,
			jtControllerName[ControllerType],
			(int)  pDev->base_addr,
			(long) pDev->mem_start,
			(int)  pDev->irq
		);
	JT_DPRINTK(
			"%s: Tx %s Mode, Rx %s Mode, Tx Threshold %d bytes.\n",
			pDev->name,
			pDevInstance->TxIOMode == TX_IOMODE_PIO ? "PIO"
			: "PDx",
			pDevInstance->RxIOMode == RX_IOMODE_PIO ? "PIO"
			: pDevInstance->RxIOMode == RX_IOMODE_PDL ? "PDL"
			: "PDC",
			pDevInstance->TxPDxThreshold
		);
	if( (pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER) ||
		 (pDevInstance->RxIOMode == RX_IOMODE_PDL) )
	{
		JT_DPRINTK(
				"%s: Cache Line size %d bytes.\n",
				pDev->name,
				(int)  pDevInstance->CacheLineSize
			);
	}
#endif

#if defined(WORKAROUND_LATENCY)
	jtReadPCIConfiguration8(
			PciBus, 
			PciDeviceFunction,
			PCI_LATENCY_TIMER, 
			&PciLatency
		);
	if (PciLatency < 0x40) 
	{
#if defined(JT_DEBUG)
		JT_DPRINTK(
				"%s: PCI latency timer (CFLT) is %d.  Setting to %d clocks.\n",
			  pDev->name, 
			  PciLatency, 
			  0x40
			);
#else			 
		printk(
				KERN_INFO "%s: PCI latency timer (CFLT) is %d.  Setting to %d clocks.\n",
			  pDev->name, 
			  PciLatency, 
			  0x40
			);
#endif
		jtWritePCIConfiguration8(
				PciBus, 
				PciDeviceFunction,
				PCI_LATENCY_TIMER, 
				0x40
			);
	} 
	else
#if defined(JT_DEBUG)
		JT_DPRINTK(
				"%s: PCI latency timer (CFLT) is %#x.\n",
				pDev->name, 
				PciLatency
			);
#else
		printk(
				KERN_INFO "%s: PCI latency timer (CFLT) is %#x.\n",
				pDev->name, 
				PciLatency
			);
#endif
#endif
		/*jtWritePCIConfiguration8(
				PciBus, 
				PciDeviceFunction,
				PCI_LATENCY_TIMER, 
				0x20
			);*/

	return pDev;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevReset
//
// DESCRIPTION: Do a software reset of the controller, and update all
//              local bookkeeping appropriately.
//
//----------------------------------------------------------------------------
static const int RESET_MAX_LOOP_COUNT = 999;

JT_STATUS jtDevReset ( PDEV_INSTANCE pDevInstance )
{
	UINT32 ModeRegister1;
	UINT16 i;

	// Reset our local copy of the Multicast Hast Table
	pDevInstance->MulticastHashLow = 0;
	pDevInstance->MulticastHashHigh = 0;

	// Set Set/Reset(bit #0) Control bit for Bits(7:1) and Soft Reset (bit #1)
	// in ModeRegister-1
	jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL0 | SWRE );

	// Delay feature is needed on reset
	// Wait for 20 millisecond for reset to complete
	mdelay ( 20 );

	// Poll device, waiting for 
	for ( i = 0; i < RESET_MAX_LOOP_COUNT; i++ )
	{
		// Read Bit #1 to check reset complete
		jtCsrIn32 ( pDevInstance, MODE_REG_1, &ModeRegister1 );
		if ( ( ModeRegister1 & SWRE ) == 0 )
		{
			return JT_STATUS_SUCCESS;
		}

		// Delay to give the device a chance to do work
		mdelay ( 1 ); // One millisecond
	}
   
	return ( JT_STATUS_FAILURE );
}


/*****************************************************************************
 *
 * Standard Device Methods
 *
 ****************************************************************************/

//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevOpen
//
// DESCRIPTION: Standard device method to bring the controller to a fully
//              operational state.
//
//----------------------------------------------------------------------------
STATIC int jtDevOpen(struct device * pDev)
{
	DEV_INSTANCE * pDevInstance = (DEV_INSTANCE *) pDev->priv;
	JT_STATUS Result;
	int RetCode = 0;

	// Request shared access to the controller's interrupt.
	RetCode = request_irq( 
			pDev->irq, 
			&jtDevInterrupt,
			SA_SHIRQ, // Slow, shared interrupt
			pDev->name,
			pDev
		);
	if( RetCode != 0 )
	{
		printk(
				KERN_ERR "%s: Unable to acquire interrupt %d!\n",
				pDev->name,
				pDev->irq
			);
		return -EAGAIN;
	}
   
	// Setup various options - Rx Flow Control, Tx CRC generation, 
	//    Tx Min Padding, Phy Status Polling, most are defaults but just in case...
	jtCsrOut32 ( 
			pDevInstance, 
			MODE_REG_1,  
			(SERECL0 | TXPPEN | GMSTPOEN) 
			| (SERECL1 | UCEN) 
			| (SERECL2 | TXCREN)
			| (SERECL3 | LNCKEN) 
		);

	// Packet Padding must be disabled for Mercury
	if (pDevInstance->ControllerType==LXT1002)
		jtCsrOut32(
				pDevInstance,
				MODE_REG_1,
				SERECL0 | RMPPEN
			);
	else
		jtCsrOut32(
				pDevInstance,
				MODE_REG_1,
				RMPPEN
			);
   
	// Don't receive errored packets
	jtCsrOut32( pDevInstance, MODE_REG_1, PAERPKEN );

	// If the Maximum Packet Size is greater than 1500, enable large packet support
	if ( pDev->mtu > ETH_DATA_LEN )
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL1 | LGPKEN );
	} 
	else
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, LGPKEN );
	}
   
	// Enable/Disable Transmit flow control
	if ( pDevInstance->TxFlowControl )
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL0 | TXFLCTEN );
	} 
	else
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, TXFLCTEN );
	}
   
	// Enable/Disable Receive flow control
	if ( pDevInstance->RxFlowControl )
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL2 | RXFLCTEN );
		
		// We should also set the values...
		jtCsrOut32( pDevInstance, LXT1002_FLCTLOWM_MASK, 0x500 + (0x1b00<<LXT1002_FLCTHIWM_SHIFT) );  
	} 
	else
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, RXFLCTEN );
	}

	// Workaround for CRC check
#if defined(WORKAROUND_CRC_CHECK)
	if (pDevInstance->RxCrcCheck)
		jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL2 | PACREN );
#endif 

	// Disable Magic Packet Mode
	jtCsrOut32( pDevInstance, MODE_REG_1, MGPKEN );

	// Make sure all VLAN options are disabled
	jtCsrOut32 ( pDevInstance, MODE_REG_1, (VLEN | VLTBEN | VLRMID | VLISGB) );

	// Turn on receive acceleration
	jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 1), (SERECL1 | (RXIPCKEN | RXTPCKEN | RXUPCKEN)) >> 8 );

	// Turn off transmit acceleration since first part does not support TX acceleration
	jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 1), (TXIPCKEN | TXTPCKEN | TXUPCKEN) >> 8 );

	// Set the MAC address
	jtCsrWriteMacAddress(pDevInstance, pDev->dev_addr);

	// Read the size of the Tx fifo
	jtCsrIn16( 
			pDevInstance, 
			TX_FIFO_DWORDS_FREE_REG, 
			&(pDevInstance->TxFifoDwordFreeCount)
		);

	// Decide the default options for packet transmit
	pDevInstance->TxPacketHeader = 0;

	// Use the receive watermark to detect the potential overflow condition (workaround for Mercury)
	if (pDevInstance->ControllerType == LXT1001)
	{ 
		jtCsrOut16( 
				pDevInstance, 
				RX_FIFO_WTR_MRK_REG, 
				// workaround theshold for R3C1 Silicon
				0x3fff
			);
		jtCsrOut32( pDevInstance, INTR_MASK_REG, SERECL1 | RXFIWMMS );
	}

	// Look for the attached PHY
	Result = jtPhyFindPhy ( pDevInstance );
	if( Result != JT_STATUS_SUCCESS )
	{
		printk(
				KERN_ERR "%s: Error: Did not detect PHY layer.\n",
				pDev->name 
			);
		return -EAGAIN;
	}

	// Initialize the PHY - link type, speed, duplex
	Result = jtPhyInitialize ( pDevInstance );         
	if( Result != JT_STATUS_SUCCESS )
	{
		printk(
				KERN_ERR "%s: Error: Unable to initialize PHY layer.\n", 
				pDev->name 
			);
		return -EAGAIN;
	}

	// Matterhorn specific stuff
	if (pDevInstance->ControllerType == LXT1002)
	{
		Result = jtStatusBufferCreate( pDevInstance);
		if( Result != JT_STATUS_SUCCESS )
		{
			printk(
					KERN_ERR "%s: Unable to create Status Buffer\n", 
					pDev->name 
				);

			return -EAGAIN;
		}
	}

	// Create the Receive Instance data
	Result = (*pDevInstance->pRxCreate)( pDevInstance );
	if( Result != JT_STATUS_SUCCESS )
	{
		printk(
				KERN_ERR "%s: Out of memory: Unable to create receive buffers.\n", 
				pDev->name 
			);

		// Clean up
		(*pDevInstance->pRxDestroy)( pDevInstance );
		return -EAGAIN;
	}

	// Create the Transmit Instance data
	Result = (*pDevInstance->pTxCreate)( pDevInstance );
	if( Result != JT_STATUS_SUCCESS )
	{
		printk(
				KERN_ERR "%s: Out of memory: Unable to create transmit buffers.\n", 
				pDev->name 
			);
      
		// Clean up
		(void) (*pDevInstance->pRxDestroy)( pDevInstance );
		(void) (*pDevInstance->pTxDestroy)( pDevInstance );
		return -EAGAIN;
	}
   
	// Initialize receive and transmit buffers
	(void) (*pDevInstance->pRxInitialize)( pDevInstance );
	(void) (*pDevInstance->pTxInitialize)( pDevInstance );
   
	// Start the Transmitter and Receiver
	jtTxStart( pDevInstance );
	jtRxStart( pDevInstance );

	// Read the counts of PDx commands available
	(void) jtRxGetFreeCount( pDevInstance );
	(void) jtTxGetFreeCount( pDevInstance );

	// Enable Master Interrupts
	pDevInstance->InterruptCount = 0;
	jtDevEnableInterrupts( pDevInstance );

	// Flag device as open
	pDev->start = 1;
	pDev->tbusy = 0;
	pDevInstance->TxFull = 0;
   
	// Everything worked
	MOD_INC_USE_COUNT;
	return 0;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevStartTransmit
//
// DESCRIPTION: Standard device method to transmit a single packet.
//  pSkBuff     The packet to transmit.
//  pDev        The controller to transmit from.
//
//----------------------------------------------------------------------------

STATIC int jtDevStartTransmit(struct sk_buff * pSkBuff, struct device * pDev) 
{
	DEV_INSTANCE * pDevInstance = (DEV_INSTANCE *) pDev->priv;
	int Result;
   
	// Validate arguments
#if defined(JT_DEBUG)
	if ( (pSkBuff == NULL) || (pSkBuff->len <= 0) ) 
	{
		printk(
				KERN_WARNING "%s: Obsolete driver layer request made: skbuff==NULL.\n",
				pDev->name
			);
      
		// Notify the higher layers to try again
		return 0;
	}
#endif
  
	// Check for diags
	if ( pDevInstance->DiagnosticsStatus == DIAGS_ON )
		return -EBUSY;

	// Block multiple transmits from overlapping.
	if ( test_and_set_bit(0, (void *) &pDev->tbusy ) )
	{
		//JT_DPRINTK("Busy, packet rejected\n");
		printk( KERN_CRIT "Busy, packet rejected\n");
		return -EBUSY;
	}

	// Choose the proper transmit mechanism
	Result = (*pDevInstance->pTxTransmit)( pDevInstance, pSkBuff );
   
	// Is the controller still busy?
	if( !test_bit(0, (void*) &pDevInstance->TxFull) )
		clear_bit(0, (void*) &pDev->tbusy);

#if defined(JT_DEBUG)
	else
	{
		printk(KERN_CRIT "Transmitter still busy!\n");
		printk(KERN_CRIT "Tx PDC Available = %d, PDL Available %d, In Progress = %d\n",
				(pDevInstance->TxPDCAvailableQueue.Tail - pDevInstance->TxPDCAvailableQueue.Head + pDevInstance->TxPDCAvailableQueue.Capacity)%pDevInstance->TxPDCAvailableQueue.Capacity,
				(pDevInstance->TxPDLAvailableQueue.Tail - pDevInstance->TxPDLAvailableQueue.Head + pDevInstance->TxPDLAvailableQueue.Capacity)%pDevInstance->TxPDLAvailableQueue.Capacity,
				(pDevInstance->TxPDxInProgressQueue.Tail - pDevInstance->TxPDxInProgressQueue.Head + pDevInstance->TxPDxInProgressQueue.Capacity)%pDevInstance->TxPDxInProgressQueue.Capacity
			);
	}
#endif
   
	// Mark beginning of transmit
	pDev->trans_start = jiffies;

	return Result;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevResetAndRestart
//
// DESCRIPTION: resets and reactivates the device
//
//----------------------------------------------------------------------------

STATIC void jtDevResetAndRestart( PDEV_INSTANCE pDevInstance ) 
{
	struct device * pDev = pDevInstance->pDev;
	UINT16 PhyStatus;

		// Disable Transmitter and Receiver
	jtTxStop( pDevInstance );
	jtRxStop( pDevInstance );

	//temporary
	mdelay(1);
	
	// Flush receive and transmit buffers
	(void) (*pDevInstance->pRxFlush)( pDevInstance );
	(void) (*pDevInstance->pTxFlush)( pDevInstance );

	// Read statistics before we reset the device
	(void) jtDevGetStatistics( pDev );

	// Reset the device
	jtDevReset ( pDevInstance );
    
	// Setup various options - Rx Flow Control, Tx CRC generation, 
	//    Tx Min Padding, Phy Status Polling, most are defaults but just in case...
	jtCsrOut32 ( 
			pDevInstance, 
			MODE_REG_1,  
			(SERECL0 | TXPPEN | GMSTPOEN) 
			| (SERECL1 | UCEN) 
			| (SERECL2 | TXCREN)
			| (SERECL3 | LNCKEN) 
		);
    
	// Packet Padding must be disabled for Mercury 
	// the if is probably redundant
	if (pDevInstance->ControllerType==LXT1002)
		jtCsrOut32(
				pDevInstance,
				MODE_REG_1,
				SERECL0 | RMPPEN
			);
	else
		jtCsrOut32(
				pDevInstance,
				MODE_REG_1,
				RMPPEN
			);
    
	// Don't receive errored packets
	jtCsrOut32( pDevInstance, MODE_REG_1, PAERPKEN );
    
	// If the Maximum Packet Size is greater than 1500, enable large packet support
	if ( pDev->mtu > ETH_DATA_LEN )
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL1 | LGPKEN );
	} 
	else
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, LGPKEN );
	}
    
	// Enable/Disable Transmit flow control
	if ( pDevInstance->TxFlowControl )
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL0 | TXFLCTEN );
	} 
	else
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, TXFLCTEN );
	}
   
	// Enable/Disable Receive flow control
	if ( pDevInstance->RxFlowControl )
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL2 | RXFLCTEN );
		
		// We should also set the values...
		jtCsrOut32( pDevInstance, LXT1002_FLCTLOWM_MASK, 0x500 + (0x1b00<<LXT1002_FLCTHIWM_SHIFT) );  
	} 
	else
	{
		jtCsrOut32 ( pDevInstance, MODE_REG_1, RXFLCTEN );
	}

	// CRC check
#if defined(WORKAROUND_CRC_CHECK)
	if (pDevInstance->RxCrcCheck)
		jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL2 | PACREN );
#endif 

	// Disable Magic Packet Mode
	jtCsrOut32( pDevInstance, MODE_REG_1, MGPKEN );

	// Make sure all VLAN options are disabled
	jtCsrOut32 ( pDevInstance, MODE_REG_1, (VLEN | VLTBEN | VLRMID | VLISGB) );

	// Turn on receive acceleration
	jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 1), (SERECL1 | (RXIPCKEN | RXTPCKEN | RXUPCKEN)) >> 8 );

	// Turn off transmit acceleration since first part does not support TX acceleration
	jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 1), (TXIPCKEN | TXTPCKEN | TXUPCKEN) >> 8 );

	// Set the MAC address
	jtCsrWriteMacAddress(pDevInstance, pDev->dev_addr);

	// Use the receive watermark to detect the potential overflow condition (workaround for Mercury)
	if (pDevInstance->ControllerType == LXT1001)
	{ 
		jtCsrOut16( 
				pDevInstance, 
				RX_FIFO_WTR_MRK_REG, 
				// workaround theshold for R3C1 Silicon
				0x3fff
			);
		jtCsrOut32( pDevInstance, INTR_MASK_REG, SERECL1 | RXFIWMMS );
	}

	// Dummy Phy call to turn on the LEDs
	if (pDevInstance->ControllerType == LXT1001)
	{ 
		jtPhyReadStatusRegister (
				pDevInstance,
				pDevInstance->CurrentPhy.PhyAddress,
				&PhyStatus
			);
	}

	// Re-initialize receive and transmit buffers
	(void) (*pDevInstance->pRxInitialize)( pDevInstance );
	(void) (*pDevInstance->pTxInitialize)( pDevInstance );
	
	// Enable Transmitter and Receiver
	jtTxStart( pDevInstance );
	jtRxStart( pDevInstance );

	// Redo multicast list
	jtDevSetMulticastList(pDev);

	pDevInstance->InterruptCount = 0;
}

//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevInterrupt
//
// DESCRIPTION: The interrupt service routine for the controller.
//  Irq         The number of the interrupt raised.
//  pDevHandle  The controller which raised the interrupt.
//  pReg        Saved state of the cpu.
//
//----------------------------------------------------------------------------

STATIC void jtDevInterrupt(int Irq, void * pDevHandle, struct pt_regs * pRegs) 
{
	struct device * pDev = (struct device *) pDevHandle;
	DEV_INSTANCE * pDevInstance = NULL;
	UINT32 InterruptMask = 0; 
	UINT32 EventStatus = 0;
	UINT8 LoopCount = 0;
#if defined(PERFORMANCE_TX)
	BOOLEAN TimerPopped = FALSE;
#endif
#if (LINUX_VERSION_CODE < 0x20200) && defined(PERFORMANCE_TX) && defined(SERIALIZE_TX)
	unsigned int Flags;	
#endif

	// Validate arguments
	if ( pDev == NULL) 
	{
		printk(KERN_WARNING __FILE__ ": irq %d for unknown device.\n", Irq);
		return;
	}
	
	// Get the device private data
	pDevInstance = (DEV_INSTANCE *) pDev->priv;
	
	// Check whether this is our interrupt by reading the interrupt mask.
	jtCsrIn32( pDevInstance, INTR_MASK_REG, &InterruptMask );
	InterruptMask |= DMDNMSK;
	//   JT_DPRINTK("Interrupt Mask %#010x.\n", InterruptMask);
	if( (InterruptMask & INENMS) == 0 )
	{
		// This isn't our interrupt
		return;
	}

	// Diagnostic interrupt
	if (pDevInstance->DiagnosticsStatus	== DIAGS_ON )
	{
		UINT8 TransmitDoneCount = 0;
	 
		pDevInstance->InterruptCount ++;
		jtCsrIn32( pDevInstance, INTR_STATUS_REG, &EventStatus );
		// Receive
		(void) (*pDevInstance->pRxReceive)( pDevInstance, EventStatus );
		// Transmit
		jtCsrIn8( pDevInstance, CMD_STATUS_REG, &TransmitDoneCount );
		jtTxPDxDone( pDevInstance, TransmitDoneCount );
		
		jtDevEnableInterrupts( pDevInstance );
		return;
	}

	// Mark interrupt-in-progress flag
	set_bit(0, (void *) &pDev->interrupt );

	// Read the event status 
	jtCsrIn32( pDevInstance, INTR_STATUS_REG, &EventStatus );
	
	//JT_DPRINTK( "Event Status   %#010x.\n", EventStatus);

	// Handle the active events, until no more or we get tired
	while ( (EventStatus & InterruptMask) != 0 )
	{
		// Handle Controller Reset
		if (pDevInstance->ControllerType == LXT1001)
		{ 
			if( EventStatus & RXFIWMIN )
			{
				printk ( KERN_CRIT "Fifo Overflow\n" );
				jtDevResetAndRestart( pDevInstance );
				break;
			}
		}

		(void) (*pDevInstance->pRxReceive)( pDevInstance, EventStatus );

		// Acknowledge Bus Master transmit complete
#if defined(STANDARD_TX)
		if( EventStatus & TXDMDNIN )
		{
			UINT8 TransmitDoneCount = 0;
	 
			// Read number of commands done transmitting
			jtCsrIn8( pDevInstance, CMD_STATUS_REG, &TransmitDoneCount );

			// Cleanup buffers used by transmit
			jtTxPDxDone( pDevInstance, TransmitDoneCount );
		}
#elif defined(PERFORMANCE_TX)
#if defined(PERFORMANCE_LXT1002)
		if ( pDevInstance->ControllerType == LXT1001 )
		{
#endif
			// See if the timer has expired
			if( EventStatus & TMEXMS ) 
				TimerPopped = TRUE;	 

			// See if there are any packets to process
			if( atomic_read( &pDevInstance->TxPDLPending ) || test_bit(0, (void*) &pDevInstance->TxFull) )
			{
				UINT8 TransmitDoneCount = 0;

				// Read number of commands done transmitting
				jtCsrIn8( pDevInstance, CMD_STATUS_REG, &TransmitDoneCount );

				// Cleanup buffers used by transmit
				jtTxPDxDone( pDevInstance, TransmitDoneCount );
			}
#if defined(PERFORMANCE_LXT1002)
		}
		else
		{
			UINT8 TransmitDoneCount;

			spin_lock(&pDevInstance->DriverLock);	 
			
			// Get the number of done commands
			TransmitDoneCount = *(PUINT8)pDevInstance->StatusBuffer - pDevInstance->TxOldStatusCount;

			// Cleanup buffers used by transmit
			jtTxPDxDone( pDevInstance, TransmitDoneCount );

			spin_unlock(&pDevInstance->DriverLock);	 
		}
#endif
#endif
      
		// Handle PHY interrupt
		if( EventStatus & PHLASTIN )
		{
			(void) jtPhyHandleStatusChange(pDevInstance);
		}

		// Check whether we're allowed to loop again
		++LoopCount;
		if( LoopCount >= JT_MAX_LOOP_COUNT )
		{
			// Don't check for more events, just exit
			//			JT_DPRINTK( "Max Loop Count reached.\n" );
			break;
		}
		
		// Read the event status 
		jtCsrIn32( pDevInstance, INTR_STATUS_REG, &EventStatus );
		//      JT_DPRINTK( "Event Status   %#010x.\n", EventStatus);
	}
 
#if defined(PERFORMANCE_TX)

#if defined(PERFORMANCE_LXT1002)
		if ( pDevInstance->ControllerType == LXT1001)
		{
#endif

#if defined(SERIALIZE_TX)
		spin_lock(&pDevInstance->DriverLock);	 
#endif
		// See if we need another timer
		if( TimerPopped ) 
		{
			if( atomic_read(&pDevInstance->TxPDLPending) || test_bit(0, (void*) &pDevInstance->TxFull) )
			{
#if TRUE //!defined(WORKAROUND_WRITE_COMBINE)
				jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | DLINRQ );
#else
				outl( SERECL0 | DLINRQ, pDevInstance->pDev->base_addr + COMMAND_REG );
#endif
			}
			else
				clear_bit(0, (void*) &pDevInstance->TxPDLTimerOn);
		}
		else
		{
			// See if we can stop the timer
			if( test_bit(0, (void*) &pDevInstance->TxPDLTimerOn) )
			{
				if( !atomic_read(&pDevInstance->TxPDLPending) && !test_bit(0, (void*) &pDevInstance->TxFull) )
				{
					clear_bit(0, (void*) &pDevInstance->TxPDLTimerOn);
		#if TRUE //!defined(WORKAROUND_WRITE_COMBINE)
					jtCsrOut32( pDevInstance, COMMAND_REG, DLINRQ );
		#else
					outl( DLINRQ, pDevInstance->pDev->base_addr + COMMAND_REG );
		#endif
				}
			}
		}
#if defined(SERIALIZE_TX)
		spin_unlock(&pDevInstance->DriverLock);	 
#endif

#if defined(PERFORMANCE_LXT1002)
	}
#endif

#endif

	// Interrupt done
	clear_bit(0, (void *) &pDev->interrupt );

	// Re-enable interrupts from controller
	jtDevEnableInterrupts( pDevInstance );

}

//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevClose
//
// DESCRIPTION: Standard device method to shutdown controller and cease all
//              operations.
//
//----------------------------------------------------------------------------

STATIC int jtDevClose(struct device * pDev) 
{
	PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;

	// Decrement module use count
	MOD_DEC_USE_COUNT;

	// Disable Device Interrupts
	jtDevDisableInterrupts ( pDevInstance );

	// Release interrupt
	free_irq( pDev->irq, pDev );

	// Flag device as closed
	pDev->start = 0;
	pDev->tbusy = 1;
	pDevInstance->TxFull = 1;

	// Disable Transmitter and Receiver
	jtTxStop( pDevInstance );
	jtRxStop( pDevInstance );

	// Flush receive and transmit buffers
	(void) (*pDevInstance->pRxFlush)( pDevInstance );
	(void) (*pDevInstance->pTxFlush)( pDevInstance );

	// Destroy all RX and TX buffers
	(*pDevInstance->pRxDestroy)( pDevInstance );
	(*pDevInstance->pTxDestroy)( pDevInstance );

	// Get rid of the Status Buffer
	if (pDevInstance->ControllerType == LXT1002)
		jtStatusBufferDestroy( pDevInstance);

	// Reset the device to make sure the state is known
	jtDevReset ( pDevInstance );

	return 0;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevGetStatistics
//
// DESCRIPTION: Standard device method to get statistical detail from
//              the controller.
//
//----------------------------------------------------------------------------
STATIC struct enet_statistics * jtDevGetStatistics(struct device * pDev) 
{
	PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;
	UINT32 StatValue = 0;
   
	// Read statistics from the controller and add to local statistics
	// The statistic are read in the order they occur in 
	// the enet_statistics structure.

	// rx_packets maintained locally by the driver
   
	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_TX_OK );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.tx_packets += StatValue;

	if (pDevInstance->ControllerType==LXT1001)
	{
		jtCsrOut32( pDevInstance, STAT_INDEX_REG, LXT1001_STAT_ID_RX_ERROR );
		jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
		pDevInstance->Statistics.rx_errors += StatValue;
	}

	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_TX_ERROR );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.tx_errors += StatValue;

	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_DROPPED );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.rx_dropped += StatValue;

	// tx_dropped is maintained locally by the driver

	if (pDevInstance->ControllerType==LXT1001)
	{
		jtCsrOut32( pDevInstance, STAT_INDEX_REG, LXT1001_STAT_ID_MULTICAST_OK );
		jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
		pDevInstance->Statistics.multicast += StatValue;
	}

	// Add together the number of collisioned packets
	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_SINGLE_COL );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.collisions += StatValue;
	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_MULTI_COL );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.collisions += StatValue;
	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_LATE_COL );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.collisions += StatValue;
   
	// detailed rx_errors
	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_PKT_LENGTH_ERR );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.rx_length_errors += StatValue;
	
	// rx_over_errors is maintained locally by the driver

	// Add together FCS, IP, TCP UDP CRC errors
	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_CRC_ERROR );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.rx_crc_errors += StatValue;
	
	if (pDevInstance->ControllerType==LXT1001)
	{
		jtCsrOut32( pDevInstance, STAT_INDEX_REG, LXT1001_STAT_ID_IP_CKSUM_ERR );
		jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
		pDevInstance->Statistics.rx_crc_errors += StatValue;
		jtCsrOut32( pDevInstance, STAT_INDEX_REG, LXT1001_STAT_ID_UDP_CKSUM_ERR );
		jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
		pDevInstance->Statistics.rx_crc_errors += StatValue;
		jtCsrOut32( pDevInstance, STAT_INDEX_REG, LXT1001_STAT_ID_TCP_CKSUM_ERR );
		jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
		pDevInstance->Statistics.rx_crc_errors += StatValue;
	}

	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_ALIGN_ERROR );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.rx_frame_errors += StatValue;

	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_DROPPED );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.rx_fifo_errors += StatValue;

	// rx_missed_errors has no corresponding controller statistic

	// detailed tx_errors
	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_EXCESS_COL );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.tx_aborted_errors += StatValue;

	jtCsrOut32( pDevInstance, STAT_INDEX_REG, STAT_ID_CARRIER_SENSE );
	jtCsrIn32( pDevInstance, STAT_DATA_REG, &StatValue );
	pDevInstance->Statistics.tx_carrier_errors += StatValue;

	// tx_fifo_errors, tx_heartbeat_errors, tx_window_errors
	// have no corresponding controller statistic

	return &(pDevInstance->Statistics);
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevSetMulticastList
//
// DESCRIPTION: Standard device method to set device modes.
//
//----------------------------------------------------------------------------
STATIC void jtDevSetMulticastList(struct device * pDev) 
{
	PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;

	// If promiscuous mode is requested
	if( pDev->flags & IFF_PROMISC )
	{
		// Enable controller promisc mode
		jtCsrOut32( pDevInstance, MODE_REG_1, POEN|SERECL1 );
	}
	else 
	{
		// Disable controller promisc mode
		jtCsrOut32( pDevInstance, MODE_REG_1, POEN );
	}
   
	// If all multicast requested
	if( pDev->flags & IFF_ALLMULTI )
	{
		// Set the multicast hash filter to accept all packets
		pDevInstance->MulticastHashLow = ~0;
		pDevInstance->MulticastHashHigh = ~0;
  
		// Enable multicast reception
		jtCsrOut32( pDevInstance, MCAST_HASH_TBL_REG_LO, pDevInstance->MulticastHashLow );
		jtCsrOut32( pDevInstance, MCAST_HASH_TBL_REG_HI, pDevInstance->MulticastHashHigh );
		jtCsrOut32( pDevInstance, MODE_REG_1, SERECL1 | MCEN );
	}
	// Or if a multicast list is indicated
	else 
		if( pDev->mc_count > 0 )
		{
			struct dev_mc_list * pMCEntry = pDev->mc_list;

			// Initialize the multicast hash filter to empty
			pDevInstance->MulticastHashLow = 0;
			pDevInstance->MulticastHashHigh = 0;

			// Walk the list of multicast entries, adding each one to the hash filter
			for( ; pMCEntry != NULL; pMCEntry = pMCEntry->next )
			{
				jtAddAddressToMulticastHash( pDevInstance, pMCEntry->dmi_addr );
			}

			// Write hash filter to the controller, and activate multicast filtering
			jtCsrOut32( pDevInstance, MCAST_HASH_TBL_REG_LO, pDevInstance->MulticastHashLow );
			jtCsrOut32( pDevInstance, MCAST_HASH_TBL_REG_HI, pDevInstance->MulticastHashHigh );
			jtCsrOut32( pDevInstance, MODE_REG_1, SERECL1 | MCEN );
		}
		else
		{
			// Disable controller multicast mode
			jtCsrOut32( pDevInstance, MODE_REG_1, MCEN );
		}
   
	// If broadcast address valid
	if( pDev->flags & IFF_BROADCAST )
	{
		// Enable controller broadcast mode
		jtCsrOut32( pDevInstance, MODE_REG_1, SERECL1 | BCEN );
	}
	else
	{
		// Disable controller broadcast mode
		jtCsrOut32( pDevInstance, MODE_REG_1, BCEN );
	}
   
#if defined(JT_DEBUG)
	/*
	if( verbose )
	{
		UINT32 Mode1 = 0;
		UINT32 Mode2 = 0;
		jtCsrIn32( pDevInstance, MODE_REG_1, &Mode1 );
		jtCsrIn32( pDevInstance, MODE_REG_2, &Mode2 );
		JT_DPRINTK( "Mode Reg 1: %#08x, Mode Reg 2: %#08x\n", Mode1, Mode2 );
	}
	*/
#endif
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevIoctl
//
// DESCRIPTION: Standard device method for misc operations.
//  pDev        The pertinent controller.
//  pIfr        Interface request structure.
//  cmd         The command to perform.
//
//----------------------------------------------------------------------------
STATIC int jtDevIoctl(struct device *pDev, struct ifreq *pIfr, int cmd)
{
	PDAC_IOCTL_HEADER CommandBlock = 	(PDAC_IOCTL_HEADER)pIfr->ifr_data;
	PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;
	PDAC_DIAGNOSTIC_STATISTICS_IOCTL StatCommand;
	PDAC_READ_WRITE_IOCTL ReadWriteCommand;
	UINT32 Port;
		
	// process the IOCTL command
	switch(CommandBlock->Opcode)
	{  
    case DAC_IOCTL_FIND_ADAPTERS:
			if ( pDev->start )
      	CommandBlock->Status = DAL_STATUS_SUCCESS;
			else
				CommandBlock->Status = DAL_STATUS_FAILURE;
      break;
    case DAC_IOCTL_INFO:
      jtDevIoctlGetInfo( (PDAC_INFO_IOCTL)CommandBlock, pDev );
      break;         
    case DAC_IOCTL_SET_INFO:
      jtDevIoctlSetInfo( (PDAC_INFO_IOCTL)CommandBlock, pDev );
      break;         
    case DAC_IOCTL_ENABLE_DIAGNOSTICS_MODE:
			jtDevIoctlEnableDiagnostics ((PDAC_INFO_IOCTL)CommandBlock, pDev);
			break;
    case DAC_IOCTL_DISABLE_DIAGNOSTICS_MODE:
      jtDevIoctlDisableDiagnostics ((PDAC_INFO_IOCTL)CommandBlock, pDev);
      break;
    case DAC_IOCTL_RESET_DIAGNOSTICS_MODE:
      jtDevIoctlResetDiagnostics ((PDAC_INFO_IOCTL)CommandBlock, pDev);
      break;
    case DAC_IOCTL_ENABLE_INTERRUPTS:
			if ( ( pDevInstance->DiagnosticsStatus == DIAGS_ON ) && !(pDevInstance->Flags & DBG_INTERRUPTS_ENABLED) )
			{
				jtDevEnableInterrupts ( pDevInstance );
				CommandBlock->Status = DAL_STATUS_SUCCESS;
	  		pDevInstance->Flags |= DBG_INTERRUPTS_ENABLED;
			}
			else
	  		CommandBlock->Status = DAL_STATUS_INVALID_STATE;
      break;                  
    case DAC_IOCTL_DISABLE_INTERRUPTS:
			if ( ( pDevInstance->DiagnosticsStatus == DIAGS_ON ) && (pDevInstance->Flags & DBG_INTERRUPTS_ENABLED) )
			{
				jtDevDisableInterrupts ( pDevInstance );
				CommandBlock->Status = DAL_STATUS_SUCCESS;
	  		pDevInstance->Flags &= ~DBG_INTERRUPTS_ENABLED;
			}
			else
	  		CommandBlock->Status = DAL_STATUS_INVALID_STATE;
			break;                  
    case DAC_IOCTL_GET_DIAGNOSTIC_STATISTICS:
			StatCommand = (PDAC_DIAGNOSTIC_STATISTICS_IOCTL)CommandBlock;
      StatCommand->InterruptCount = pDevInstance->InterruptCount;
			CommandBlock->Status = DAL_STATUS_SUCCESS;
      break;
    case DAC_IOCTL_ENABLE_TX:
			if ( (pDevInstance->DiagnosticsStatus == DIAGS_ON) && !(pDevInstance->Flags & DBG_TRANSMITTER_ENABLED))
			{
      	jtTxStart ( pDevInstance );
				CommandBlock->Status = DAL_STATUS_SUCCESS;
	  		pDevInstance->Flags |= DBG_TRANSMITTER_ENABLED;
			}
			else
	  		CommandBlock->Status = DAL_STATUS_INVALID_STATE;
      break;
    case DAC_IOCTL_DISABLE_TX:
			if ( (pDevInstance->DiagnosticsStatus == DIAGS_ON) && (pDevInstance->Flags & DBG_TRANSMITTER_ENABLED)) 
			{
      	jtTxStop ( pDevInstance );
				CommandBlock->Status = DAL_STATUS_SUCCESS;
	  		pDevInstance->Flags &= ~DBG_TRANSMITTER_ENABLED;
			}
			else
	  		CommandBlock->Status = DAL_STATUS_INVALID_STATE;
			break;                  
    case DAC_IOCTL_ENABLE_RX:
			if ( ( pDevInstance->DiagnosticsStatus == DIAGS_ON ) && !(pDevInstance->Flags & DBG_RECEIVER_ENABLED) )
			{
      	jtRxStart ( pDevInstance );
				CommandBlock->Status = DAL_STATUS_SUCCESS;
	  		pDevInstance->Flags |= DBG_RECEIVER_ENABLED;
			}
			else
	  		CommandBlock->Status = DAL_STATUS_INVALID_STATE;
      break;
    case DAC_IOCTL_DISABLE_RX:
			if ( ( pDevInstance->DiagnosticsStatus == DIAGS_ON ) && (pDevInstance->Flags & DBG_RECEIVER_ENABLED) )
			{
      	jtRxStop ( pDevInstance );
				CommandBlock->Status = DAL_STATUS_SUCCESS;
	  		pDevInstance->Flags &= ~DBG_RECEIVER_ENABLED;
			}
			else
	  		CommandBlock->Status = DAL_STATUS_INVALID_STATE;
      break;
    //case DAC_IOCTL_POLL_DEVICE:
    //  jtDevInterrupt( pDev->irq, pDev, NULL );
		//	CommandBlock->Status = DAL_STATUS_SUCCESS;
    //  break;
    case DAC_IOCTL_SEND_PACKET:
			jtDevIoctlTx ( (PDAC_TX_IOCTL)CommandBlock, pDev );
			break;
    case DAC_IOCTL_RECEIVE_PACKET:
			jtDevIoctlRx ( (PDAC_RX_IOCTL)CommandBlock, pDev );
			break;
		case DAC_IOCTL_IO:
			ReadWriteCommand = (PDAC_READ_WRITE_IOCTL)CommandBlock;
			Port = ReadWriteCommand->Offset;
		
			switch ( ReadWriteCommand->AccessWidth )
			{
				case DAC_BYTE_ACCESS:
					if ( ReadWriteCommand->AccessType == DAC_WRITE )
						outb(ReadWriteCommand->Value[0],Port);
					else
						ReadWriteCommand->Value[0] = inb(Port);
					break;  
				case DAC_WORD_ACCESS:
					if ( ReadWriteCommand->AccessType == DAC_WRITE )
						outw(ReadWriteCommand->Value[0],Port);
					else
						ReadWriteCommand->Value[0] = inw(Port);
					break;  
				case DAC_DWORD_ACCESS:
					if ( ReadWriteCommand->AccessType == DAC_WRITE )
						outl(ReadWriteCommand->Value[0],Port);
					else
						ReadWriteCommand->Value[0] = inl(Port);
					break;
			}
			break;
		default:
			return -EINVAL;
	}

	return 0;
}

//****************************************************************************
//
// FUNCTION:   jtDevIoctlGetInfo
//
// DESCRIPTION:
//    This function get various information about the device and driver. 
//
//****************************************************************************

STATIC void jtDevIoctlGetInfo( PDAC_INFO_IOCTL Command, struct device *pDev )
{
	PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;
	UINT8 PhyTypes, SiliconRevision;
	UINT32 Register;

	Command->DbgAdapterInfo.Flags =	0;

	// Memory and IO addresses
	Command->DbgAdapterInfo.IOBase = pDev->base_addr;
  Command->DbgAdapterInfo.IORange = 256;
      
	Command->DbgAdapterInfo.MemoryBaseLow = pDev->mem_start;
  Command->DbgAdapterInfo.MemoryRange = 256;
     
	// network node addresses
	memcpy((PUINT8)&(Command->DbgAdapterInfo.PermanentNetworkAddress),
			(PUINT8)&(pDev->dev_addr),
			sizeof ( DAL_NETWORK_ADDRESS )
		);

	/*memcpy((PUINT8)&(Command->DbgAdapterInfo.CurrentNetworkAddress),
			(PUINT8)&(pDriverData->pConfigTable->MLIDCFG_NodeAddress),
			sizeof ( DAL_NETWORK_ADDRESS )
		);*/

	// Interrupt
	Command->DbgAdapterInfo.Interrupt = pDev->irq;

	// Driver and silicon versions
	Command->DbgAdapterInfo.DriverRevision = VERSION;
	jtReadPCIConfiguration8( 
			pDevInstance->PciBus, 
			pDevInstance->PciDeviceFunction, 
			PCI_CONFIG_REV_ID, 
			&SiliconRevision 
		);
	Command->DbgAdapterInfo.SiliconRevision = SiliconRevision;

	// Phy stuff
	PhyTypes = pDevInstance->CurrentPhy.PhyType;

	if ( PhyTypes & PHY_TYPE_PCS )
	{
	  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_PHY_CAP_PCS;
	  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_PHY_CAP_1000;
	}

	if ( PhyTypes & PHY_TYPE_MII )
	{
	  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_PHY_CAP_MII;
	  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_PHY_CAP_100;
	  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_PHY_CAP_10;
	}

	if ( PhyTypes & PHY_TYPE_GMII )
	{
	  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_PHY_CAP_GMII;
	  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_PHY_CAP_1000;
	  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_PHY_CAP_100;
	  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_PHY_CAP_10;
	}


	if ((pDevInstance->OverrideSpeed == LINK_SPEED_AUTO) && (pDevInstance->OverrideDuplexMode == DUPLEX_MODE_AUTO))
	{
	  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_AUTO_NEGOTIATE;           

		if ( pDevInstance->CurrentPhy.Connected )
		  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_CONNECTED;    

		if ( pDevInstance->DuplexMode & DUPLEX_MODE_FULL )
		  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_FULL_DUPLEX;           
	 
		Command->DbgAdapterInfo.LinkSpeed = pDevInstance->Speed;
	}
	else
	{
		Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_CONNECTED;    
		
		if ( pDevInstance->OverrideDuplexMode & DUPLEX_MODE_FULL )
		  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_FULL_DUPLEX;           
		
		Command->DbgAdapterInfo.LinkSpeed = pDevInstance->OverrideSpeed;
	}
	Command->DbgAdapterInfo.CurrentPhyAddress = pDevInstance->CurrentPhy.PhyAddress;
   
	// IP acceleration
  Command->DbgAdapterInfo.Flags |=  DAC_INFO_FLAGS_IP_ENABLED;

	// Boot Rom: not beautiful, it should be changed
	jtReadPCIConfiguration32( 
			pDevInstance->PciBus, 
			pDevInstance->PciDeviceFunction, 
			PCI_CONFIG_ROM_BASE, 
			&Register 
		);
	if ( Register )
		Command->DbgAdapterInfo.Flags |= DAC_INFO_FLAGS_BOOT_ROM;
	
	// Propulsion Threshold
	Command->DbgAdapterInfo.DMAThreshold = pDevInstance->TxPDxThreshold;

	Command->Hdr.Status = DAL_STATUS_SUCCESS;
}  

//****************************************************************************
//
// FUNCTION:   jtDevIoctlSetInfo
//
// DESCRIPTION:
//    This function get various information about the device and driver. 
//
//****************************************************************************

STATIC void jtDevIoctlSetInfo( PDAC_INFO_IOCTL Command, struct device *pDev )
{
	PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;

	// See if we want to re-autonegotiate the link
	if ( Command->DbgAdapterInfo.Flags & DAC_INFO_FLAGS_AUTO_NEGOTIATE	)
	{
		if ( (pDevInstance->OverrideDuplexMode != DUPLEX_MODE_AUTO) ||
			(pDevInstance->OverrideSpeed != LINK_SPEED_AUTO) )
		{
			pDevInstance->OverrideDuplexMode = DUPLEX_MODE_AUTO;
			pDevInstance->OverrideSpeed = LINK_SPEED_AUTO;

		 	jtPhyInitialize ( pDevInstance );	
		}
	}
	//or maybe force it
	else
	{
		UINT8	OverrideDuplexMode;
		
		// Prepare the desired duplex value
		if ( Command->DbgAdapterInfo.Flags & DAC_INFO_FLAGS_FULL_DUPLEX )
			OverrideDuplexMode = DUPLEX_MODE_FULL;
		else
			OverrideDuplexMode = DUPLEX_MODE_HALF;

		// Check and set
		if ( ( OverrideDuplexMode != pDevInstance->OverrideDuplexMode ) ||
				 ( Command->DbgAdapterInfo.LinkSpeed != pDevInstance->OverrideSpeed ) )
		{
			pDevInstance->OverrideDuplexMode = OverrideDuplexMode;
			pDevInstance->OverrideSpeed = Command->DbgAdapterInfo.LinkSpeed;

		 	jtPhyInitialize ( pDevInstance );	
		}
	}
	
	// Propulsion Threshold
	pDevInstance->TxPDxThreshold = Command->DbgAdapterInfo.DMAThreshold;

	Command->Hdr.Status = DAL_STATUS_SUCCESS;
}  

//****************************************************************************
//
// FUNCTION:   jtDevIoctlEnableDiagnostics
//
// DESCRIPTION:
//    Disables the normal driver paths to allow diagnostic capabilities to
//    drive the device.
//
//****************************************************************************

STATIC void jtDevIoctlEnableDiagnostics (	PDAC_INFO_IOCTL Command, struct device *pDev )
{
	PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;
	DAL_STATUS Status = DAL_STATUS_INVALID_STATE;
  
	if ( pDevInstance->DiagnosticsStatus == DIAGS_OFF )   
	{
	  // Create the diagnostic queues    
		jtQueueCreate( &pDevInstance->dbgRxQueue, 64 );
		
		// Disable Device Interrupts
		jtDevDisableInterrupts ( pDevInstance );
		mdelay(10);
	
		// Stop packet sending
		jtDevResetAndRestart( pDevInstance );

		pDevInstance->DiagnosticsStatus = DIAGS_ON;
		pDevInstance->Flags = 0;
		Status = DAL_STATUS_SUCCESS;
	}

	Command->Hdr.Status = Status;
}

//****************************************************************************
//
// FUNCTION:   jtDevIoctlDisableDiagnostics
//
// DESCRIPTION:
//    Re-enables the normal driver paths and stops diagnostic paths
//
//****************************************************************************

STATIC void jtDevIoctlDisableDiagnostics (	PDAC_INFO_IOCTL Command, struct device *pDev )
{
	PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;
	DAL_STATUS Status = DAL_STATUS_INVALID_STATE;
	struct sk_buff * pSkBuff;
  
	if ( pDevInstance->DiagnosticsStatus == DIAGS_ON )   
	{
		// Disable Device Interrupts
		jtDevDisableInterrupts ( pDevInstance );
		mdelay(10);

		// Reset and activate the part
		jtDevResetAndRestart( pDevInstance );

		pDevInstance->DiagnosticsStatus = DIAGS_OFF;

	  // Destroy the diagnostic queues    
		while( !jtQueueIsEmpty( &pDevInstance->dbgRxQueue ) )
		{
			jtQueueDequeue( &pDevInstance->dbgRxQueue, (void **)&pSkBuff );
			DEV_KFREE_SKB( pSkBuff, FREE_READ );
		}
		jtQueueDestroy( &pDevInstance->dbgRxQueue );

		// ready to go...
		jtDevEnableInterrupts( pDevInstance );

		Status = DAL_STATUS_SUCCESS;
	}

	Command->Hdr.Status = Status;
}

//****************************************************************************
//
// FUNCTION:   jtDevIoctlResetDiagnostics
//
// DESCRIPTION:
//    Disables the normal driver paths to allow diagnostic capabilities to
//    drive the device.
//
//****************************************************************************

STATIC void jtDevIoctlResetDiagnostics (	PDAC_INFO_IOCTL Command, struct device *pDev )
{
	PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;
	DAL_STATUS Status = DAL_STATUS_INVALID_STATE;
  
	if ( pDevInstance->DiagnosticsStatus == DIAGS_ON )   
	{
		// Disable Device Interrupts
		jtDevDisableInterrupts ( pDevInstance );
		mdelay(10);
	
		// Reset and restart the device
		jtDevResetAndRestart( pDevInstance );

		pDevInstance->Flags = 0;
		Status = DAL_STATUS_SUCCESS;
	}

	Command->Hdr.Status = Status;
}

//****************************************************************************
//
// FUNCTION:   jtDevIoctlTx
//
// DESCRIPTION:
//    This function get various information about the device and driver. 
//
//****************************************************************************

STATIC void jtDevIoctlTx ( PDAC_TX_IOCTL Command, struct device *pDev )
{
	PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;
	struct sk_buff * pSkBuff = NULL;
	void * pPacketData;

	// Allocate a skbuff
	pSkBuff = dev_alloc_skb( Command->PacketSize );
	if( pSkBuff == NULL ) 
	{
		Command->Hdr.Status = DAL_STATUS_RESOURCES;
		return;
	}
	
	// Copy packet data to skbuff
	pPacketData = skb_put( pSkBuff, Command->PacketSize );
	memcpy( pPacketData, &Command->Data[0], Command->PacketSize );

	// Send the packet
	(*pDevInstance->pTxTransmit)( pDevInstance, pSkBuff );				
	
	Command->Hdr.Status = DAL_STATUS_SUCCESS;

}

//****************************************************************************
//
// FUNCTION:   jtDevIoctlTx
//
// DESCRIPTION:
//    This function get various information about the device and driver. 
//
//****************************************************************************

STATIC void jtDevIoctlRx ( PDAC_RX_IOCTL Command, struct device *pDev )
{
	PDEV_INSTANCE pDevInstance = (PDEV_INSTANCE) pDev->priv;
	struct sk_buff * pSkBuff = NULL;

	// See if there are packets in the Rx queue
	if ( jtQueueIsEmpty ( &pDevInstance->dbgRxQueue ) )
	{
		Command->Hdr.Status = DAL_STATUS_NO_DATA;
		return;    
	}

	// Get the packet
	jtQueueDequeue( &pDevInstance->dbgRxQueue, (void **)&pSkBuff );
	
	// See if the buffer is big enough and get it
	if ( Command->PacketSize < pSkBuff->len )
	{
		Command->Hdr.Status = DAL_STATUS_BUFFER_TOO_SMALL;
		DEV_KFREE_SKB( pSkBuff, FREE_READ );
		return;
	}
	
	Command->PacketSize = pSkBuff->len;
	memcpy( &Command->Data[0], pSkBuff->data, Command->PacketSize );
	DEV_KFREE_SKB( pSkBuff, FREE_READ );
		
	Command->Hdr.Status = DAL_STATUS_SUCCESS;
}

//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevSetMacAddress
//
// DESCRIPTION: Standard device method to set the MAC address.
//  pDev        The pertinent controller.
//  pNewSockAddr  Untyped pointer to the new address (type is sockaddr).
//
//----------------------------------------------------------------------------
STATIC int jtDevSetMacAddress(struct device * pDev, void * pNewSockAddr )
{
	UINT8 * pNewAddr = ((struct sockaddr *) pNewSockAddr)->sa_data;

	// Copy the new mac address into the device struct
	memcpy(pDev->dev_addr, (const UINT8 *) pNewAddr, ETH_ALEN);

	// Set the controller's mac address
	jtCsrWriteMacAddress( (PDEV_INSTANCE) pDev->priv, pDev->dev_addr);

	return 0;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtDevChangeMtu
//
// DESCRIPTION: Standard device method to change the MTU value.
//
//----------------------------------------------------------------------------
STATIC int jtDevChangeMtu(struct device *pDev, int NewMtu)
{
	// Can only change MTU before device is opened
	if( pDev->start )
	{
		return -EBUSY;
	}

	// Validate that the new MTU is reasonable
	if ( NewMtu < 60 || NewMtu > JT_MAX_MTU )
	{
		return -EINVAL;
	}

	// Set the device's mtu
	pDev->mtu = NewMtu;

	return 0;
}


/******************************************************************************
 *
 * Other device methods
 *
 ******************************************************************************/

//----------------------------------------------------------------------------
//
// FUNCTION:    jtAddAddressToMulticastHash
//
// DESCRIPTION: Compute the hash value of a network address, and OR it into
//              the multicast hash. Note that this routine does not update 
//              the controller's registers, only the saved hash in the device
//              structure.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  pNewAddress   The multicast address to add the the controller's hash.
//
//----------------------------------------------------------------------------
STATIC void jtAddAddressToMulticastHash( 
		DEV_INSTANCE * pDevInstance, 
		UINT8 * pNewAddress 
	)
{
	UINT32 CRC = -1;
	UINT8 HashTableIndex = 0;
	UINT8 ByteIndex;
	UINT32 c = 0;
	UINT8 BitIndex;

	const UINT32 CRC32_POLY = 0xedb88320;  // Ethernet CRC generator polynomial
   
	// Calculate the CRC on the address to be added to the multicast list
	for ( ByteIndex = 0; ByteIndex < ETH_ALEN; ByteIndex++ )
	{
		c = ( ( CRC & 0x0ff ) ^ pNewAddress[ByteIndex] );
		for ( BitIndex = 0; BitIndex < 8; BitIndex++ )
		{
			( c & 1 ) ? ( c = ( ( c >> 1 ) ^ CRC32_POLY ) ) : ( c >>= 1 );
		}
		CRC = ( CRC >> 8 ) ^ c;
	}
   
	// The Bit Offset into the Multicast hash table is the
	// reverse order of the last 6 bits of the CRC
	for ( BitIndex = 0; BitIndex < 6; BitIndex++ )
	{
		HashTableIndex <<= 1;
		HashTableIndex |= (CRC & 1);
		CRC >>= 1;
	}
   
	// Check to see if this bit is in the MSD or LSD of the 64 bit hash
	if ( HashTableIndex >= 32 )
	{
		pDevInstance->MulticastHashHigh |= (1 << ( HashTableIndex - 32 ));
	}
	else
	{
		pDevInstance->MulticastHashLow |= (1 << HashTableIndex);
	}
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtCsrWriteMacAddress
//
// DESCRIPTION: Set the controller's current MAC address.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  pMacAddress   The MAC address (in big endian format).
//
//----------------------------------------------------------------------------
STATIC void jtCsrWriteMacAddress(
		PDEV_INSTANCE pDevInstance,
		const UINT8 * pMacAddress
	)
{
	UINT32 MacDword0;
	UINT32 MacDword1;

	// Write the address one dword at a time 
	MacDword0 = pMacAddress[0] 
		| ((pMacAddress[1]) << 8)
		| ((pMacAddress[2]) << 16)
		| ((pMacAddress[3]) << 24);
	MacDword1 = pMacAddress[4]
		| ((pMacAddress[5] << 8));
	jtCsrOut32(pDevInstance, LAN_PHY_ADR_LO, MacDword0);
	jtCsrOut32(pDevInstance, LAN_PHY_ADR_HI, MacDword1);
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtCsrReadMacAddress
//
// DESCRIPTION: Get the controller's current MAC address.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//
// RETURNS:     
//  pMacAddress The MAC address (stored in big endian format).
//
//----------------------------------------------------------------------------
STATIC void jtCsrReadMacAddress(
		PDEV_INSTANCE pDevInstance,
		UINT8 * pMacAddress
	)
{
	UINT32 MacDword0;
	UINT32 MacDword1;

	// Read the address one dword at a time 
	jtCsrIn32(pDevInstance, LAN_PHY_ADR_LO, &MacDword0);
	jtCsrIn32(pDevInstance, LAN_PHY_ADR_HI, &MacDword1);
	pMacAddress[0] = (UINT8) (MacDword0);
	pMacAddress[1] = (UINT8) (MacDword0 >> 8);
	pMacAddress[2] = (UINT8) (MacDword0 >> 16);
	pMacAddress[3] = (UINT8) (MacDword0 >> 24);
	pMacAddress[4] = (UINT8) (MacDword1);
	pMacAddress[5] = (UINT8) (MacDword1 >> 8);
}


/******************************************************************************
 *
 * Phy methods
 *
 ******************************************************************************/

//----------------------------------------------------------------------------
//
// FUNCTION:    jtPhyFindPhy
//
// DESCRIPTION: Detect all Phys connected to the controller.
//              Choose the first, isolating all others (if any).
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtPhyFindPhy ( PDEV_INSTANCE pDevInstance )
{
	struct device * pDev = pDevInstance->pDev;
	JT_STATUS Result = JT_STATUS_SUCCESS;
	UINT32 GMIIMode = 0;
	UINT8 MaxPhyAddresses;
	BOOLEAN Found = FALSE;
	UINT8 PhyAddress;
	PHY_ID FoundPhyId = 0;
	UINT8 FoundPhyType = 0;
	UINT8 FoundPhyAddress = 0;
   
	// Determine if a PCS device is attached to this configuration
	if (pDevInstance->ControllerType == LXT1001)
	{
		jtCsrIn32 ( pDevInstance, GMII_MODE_REG, &GMIIMode );
		if (GMIIMode & GMPCEN)
		{
			MaxPhyAddresses = 1;
		}
		else 
		{
			MaxPhyAddresses = 32;
		}
  }
	else
		MaxPhyAddresses = 32;
   
	// Iterate over each possible phy
	for (PhyAddress=0; PhyAddress < MaxPhyAddresses ; PhyAddress++)
	{
		UINT16 PhyId1 = 0;
		UINT16 PhyId2 = 0;   

		// Read the first Phy identification register
		Result = jtPhyReadRegister (
				pDevInstance,
				PhyAddress,
				PHY_ID_1_REG,
				&PhyId1
			);
    if (Result != JT_STATUS_SUCCESS)
    {
			continue;
    }
      
		// Read the second Phy identification register
		Result = jtPhyReadRegister (
				pDevInstance,
				PhyAddress,
				PHY_ID_2_REG,
				&PhyId2
			);
    if (Result != JT_STATUS_SUCCESS)
    {
			continue;
    }
      
		// Look for a hardcoded list of PHY's we recognize.
		// This may be extended in the future to handle the general case
		if ((PhyId1 == HG1_PCS_PHY_ID_1) && (PhyId2 == HG1_PCS_PHY_ID_2))
		{
			Found = TRUE;
			FoundPhyType = PHY_TYPE_PCS;
			FoundPhyId = PHY_LEVEL1_PCS;
			FoundPhyAddress = 0;
			break;
    }        
    else if ((PhyId1 == LEVEL1_CHEETAH_ID_1) && (PhyId2 == LEVEL1_CHEETAH_ID_2)) 
    {
			Found = TRUE;
			FoundPhyType = PHY_TYPE_GMII;
			FoundPhyId =  PHY_LEVEL1_CHEETAH;
			FoundPhyAddress = PhyAddress;
			break;
    } 
    else if (((PhyId1 == LEVEL1_PHY_ID_1) && ((PhyId2 & 0xfffe)  == LEVEL1_PHY_ID_2)) ||
						((PhyId1 == LEVEL1_PHY_ID_1_B) && (PhyId2 == LEVEL1_PHY_ID_2_B)))
    {
			Found = TRUE;
			FoundPhyType = PHY_TYPE_MII;
			FoundPhyId =  PHY_LEVEL1;
			FoundPhyAddress = PhyAddress;
			break;
    } 
    else if ((PhyId1 == NATIONAL_PHY_ID_1) && ((PhyId2 & 0xfffe) == NATIONAL_PHY_ID_2))
    {
			Found = TRUE;
			FoundPhyType = PHY_TYPE_MII;
			FoundPhyId = PHY_NATIONAL;
			FoundPhyAddress = PhyAddress;
			break;
		}        
	}         
   
	// If didn't find a Phy
	if ( !Found )
	{
		printk(
				KERN_ERR "%s: No PHY detected.\n",
				pDev->name
			);
		return(JT_STATUS_DEVICE_NOT_FOUND);      
	} 
   
	// Announce detection
#if !defined JT_DEBUG
	printk(
			KERN_INFO "%s: %s %s PHY detected at address %d\n",
			pDev->name,
			FoundPhyId== PHY_LEVEL1_PCS ? "Level One"
			: FoundPhyId == PHY_LEVEL1 ? "Level One"
			: "National",
			FoundPhyType == PHY_TYPE_GMII ? "GMII"
			: FoundPhyType == PHY_TYPE_MII ? "MII"
			: "PCS",
			FoundPhyAddress
		);
#else
	JT_DPRINTK(
			"%s: %s %s PHY detected at address %d\n",
			pDev->name,
			FoundPhyId== PHY_LEVEL1_PCS ? "Level One"
			: FoundPhyId == PHY_LEVEL1 ? "Level One"
			: "National",
			FoundPhyType == PHY_TYPE_GMII ? "GMII"
			: FoundPhyType == PHY_TYPE_MII ? "MII"
			: "PCS",
			FoundPhyAddress
		);
#endif

	// Save PHY info
	pDevInstance->CurrentPhy.PhyId = FoundPhyId;
	pDevInstance->CurrentPhy.PhyType = FoundPhyType;
	pDevInstance->CurrentPhy.PhyAddress = FoundPhyAddress;

	// Isolate all other G/MII Phys except for the first
	for ( ; PhyAddress < MaxPhyAddresses ; PhyAddress++)
	{
		UINT16 PhyReg = 0;   

		// Isolate the PHY
		Result = jtPhyReadRegister (
				pDevInstance,
				PhyAddress,
				PHY_CONTROL_REG,
				&PhyReg
			);
		if (Result != JT_STATUS_SUCCESS)
		{
			continue;
    }
      
		// Turn on the Isolate Bit and write back to the phy
		PhyReg |= PHY_CTL_ISLT | PHY_CTL_ANEN | PHY_CTL_RSAN;
		(void) jtPhyWriteRegister (
				pDevInstance,
				PhyAddress,
				PHY_CONTROL_REG,
				PhyReg
			);
	}

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtPhyUpdateSpeedDuplex
//
// DESCRIPTION: Read the link speed and duplex from the Phy, then
//              update the controller's and our local copy of same.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtPhyUpdateSpeedDuplex( PDEV_INSTANCE pDevInstance )
{
	struct device * pDev = pDevInstance->pDev;
	JT_STATUS Result = JT_STATUS_SUCCESS;
	UINT16 PhyReg = 0;
	UINT32 GMIIMode;

	// If the Phy is the NetCelerator PCS interface
	if (pDevInstance->CurrentPhy.PhyId == PHY_LEVEL1_PCS)
	{
		// Read the Phy Status Register
		Result = jtPhyReadRegister (
				pDevInstance,
				pDevInstance->CurrentPhy.PhyAddress,
				PHY_CONTROL_REG,
				&PhyReg
			);
      
		if (Result != JT_STATUS_SUCCESS)
		{
			return Result;
		}

    // Decode the duplex
    switch (PhyReg & PHY_CTL_FDMD)
    {
      case PHY_CTL_FDMD:
				pDevInstance->DuplexMode = DUPLEX_MODE_FULL;         
				break;
      default:
				pDevInstance->DuplexMode = DUPLEX_MODE_HALF;
    }
      
		// Decode the link speed
		switch (PhyReg & ( PHY_CTL_GSPSEL | PHY_CTL_SPSEL ))
		{
			case (PHY_CTL_GSPSEL | PHY_CTL_SPSEL):
			case PHY_CTL_GSPSEL:
				pDevInstance->Speed = LINK_SPEED_1000;
				break;
			case PHY_CTL_SPSEL:
				pDevInstance->Speed = LINK_SPEED_100;
				break;
      default:
				pDevInstance->Speed = LINK_SPEED_10;
				break;
		}
	}
	// Or if the Phy is the Level One Cheetah Phy
	else if (pDevInstance->CurrentPhy.PhyId == PHY_LEVEL1_CHEETAH)
	{
		// Read the Phy Status Register
		Result = jtPhyReadRegister (
				pDevInstance,
				pDevInstance->CurrentPhy.PhyAddress,
				PHY_GMII_QUICK_STATUS_REG,
				&PhyReg
			);
    if (Result != JT_STATUS_SUCCESS)
    {
			return Result;
    }
    
    // Decode the duplex
    if (PhyReg & PHY_GMII_FD)
			pDevInstance->DuplexMode = DUPLEX_MODE_FULL;
		else         
			pDevInstance->DuplexMode = DUPLEX_MODE_HALF;
    
    // Decode the speed
    switch (PhyReg & PHY_GMII_SPEED_MASK)
    {
			case PHY_GMII_1000 :
				pDevInstance->Speed = LINK_SPEED_1000;
				break;
			case PHY_GMII_100 :
				pDevInstance->Speed = LINK_SPEED_100;
				break;
			default:
				pDevInstance->Speed = LINK_SPEED_10;
    }
	}
	// Or if the Phy is the Level One MII Phy
	else if (pDevInstance->CurrentPhy.PhyId == PHY_LEVEL1)
	{
		// Read the Phy Status Register
		Result = jtPhyReadRegister (
				pDevInstance,
				pDevInstance->CurrentPhy.PhyAddress,
				PHY_LVL1_CHIP_STAT_REG,
				&PhyReg
			);
    if (Result != JT_STATUS_SUCCESS)
    {
			return Result;
    }
    
    // Decode the duplex
    switch (PhyReg & PHY_LVL1_CHIP_STAT_DUP)
    {
			case PHY_LVL1_CHIP_STAT_DUP:
				pDevInstance->DuplexMode = DUPLEX_MODE_FULL;         
				break;
			default:
				pDevInstance->DuplexMode = DUPLEX_MODE_HALF;
		}     
    
    // Decode the speed
    switch (PhyReg & PHY_LVL1_CHIP_STAT_100M)
    {
			case PHY_LVL1_CHIP_STAT_100M:
				pDevInstance->Speed = LINK_SPEED_100;
				break;
			default:
				pDevInstance->Speed = LINK_SPEED_10;
    }
	}
	// Or if the phy is National Phy
	else if (pDevInstance->CurrentPhy.PhyId == PHY_NATIONAL)
	{
		// Read the Phy Status Register
		Result = jtPhyReadRegister (
				pDevInstance,
				pDevInstance->CurrentPhy.PhyAddress,
				PHY_NAT_CHIP_STAT_REG,
				&PhyReg
			);
    if (Result != JT_STATUS_SUCCESS)
    {
			return Result;
    }
    
    // Decode the duplex
    switch (PhyReg & PHY_NAT_CHIP_STAT_DUP)
    {
			case PHY_NAT_CHIP_STAT_DUP:
				pDevInstance->DuplexMode = DUPLEX_MODE_FULL;         
				break;
			default:
				pDevInstance->DuplexMode = DUPLEX_MODE_HALF;
    }     
    
    // Decode the speed
    switch (PhyReg & PHY_NAT_CHIP_STAT_10M)
    {
			case PHY_NAT_CHIP_STAT_10M:
				pDevInstance->Speed = LINK_SPEED_10;
				break;
			default:
				pDevInstance->Speed = LINK_SPEED_100;
    }
	}
   
	// Update the GMIIMode Register to reflect the PHY's parameters
	// .. First read the GMIIMode Register
	jtCsrIn32 ( pDevInstance, GMII_MODE_REG, &GMIIMode );
	GMIIMode &= (UINT32)~(GMWRSP_MASK | GMFD);

	// Set duplex to half or full
	if (pDevInstance->DuplexMode == DUPLEX_MODE_FULL)
	{
		GMIIMode |=  GMFD;      
	}

	// Set speed to 10, 100 or 1000
	switch (pDevInstance->Speed)
	{
		case LINK_SPEED_100:
			GMIIMode |=  GMWRSP_100MB;
			break;
		case LINK_SPEED_1000:
			GMIIMode |=  GMWRSP_1000MB;
			break;
	}
   
	// Write the Mode Register to set the proper speed/duplex
	jtCsrOut32 ( pDevInstance, GMII_MODE_REG, GMIIMode );
   
	// Announce speed
#if !defined JT_DEBUG
	printk(KERN_INFO 
			"%s: Speed %s Mb, %s Duplex\n", pDev->name,
			pDevInstance->Speed == LINK_SPEED_10 ? "10"
			: pDevInstance->Speed == LINK_SPEED_100 ? "100"
			: "1000",
			pDevInstance->DuplexMode == DUPLEX_MODE_HALF ? "Half" : "Full"
		);
#else
	JT_DPRINTK(
			"%s: Speed %s Mb, %s Duplex\n", pDev->name,
			pDevInstance->Speed == LINK_SPEED_10 ? "10"
			: pDevInstance->Speed == LINK_SPEED_100 ? "100"
			: "1000",
			pDevInstance->DuplexMode == DUPLEX_MODE_HALF ? "Half" : "Full"
		);
#endif
   
	return(Result);
}     


//----------------------------------------------------------------------------
//
// FUNCTION:    jtPhyGetLinkStatus    
//
// DESCRIPTION: Read the link status (connected/not connected) from
//              the currently active Phy
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//
// RETURNS:
//  pConnected  TRUE if the link is connected, FALSE if not.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtPhyGetLinkStatus( 
		PDEV_INSTANCE pDevInstance, 
		JT_PHY_STATUS *pConnected 
	)
{
	JT_STATUS Result = JT_STATUS_SUCCESS;
	UINT16 PhyStatus = 0;
	UINT16 LoopCount;
   
	// Initially assume link is not connected
	*pConnected = JT_PHY_STATUS_FAILURE;
   
	// If the Phy is the Level One PCS Phy
	if (pDevInstance->CurrentPhy.PhyId == PHY_LEVEL1_PCS)
	{
		// Wait 1 second for AN complete or Link, AN does not always complete
		for (LoopCount=0; LoopCount < 10 ; LoopCount++ )
		{
			// Read the Phy Status Register
			Result = jtPhyReadStatusRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					&PhyStatus
				);
			if ((Result != JT_STATUS_SUCCESS) || (PhyStatus & PHY_STAT_AN_DONE))
			{
				 break;
			}
      
			// Wait and try again
			mdelay( 100 );
		}
		if ( Result != JT_STATUS_SUCCESS )
		{
			return Result;
		}

		// Let's get the result
		if ( PhyStatus & PHY_STAT_AN_DONE )
		{
			Result = jtPhyReadRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_AUTO_NEG_PART_BASE_REG,
					&PhyStatus
				);
			// Analize and return the proper result
			if ( Result == JT_STATUS_SUCCESS )
			{ 
				PhyStatus = PhyStatus & PHY_PCS_AD_RFLT;
				switch ( PhyStatus )
				{
					case PHY_PCS_AD_RFLT_LNK_FAIL:
	        	PhyStatus = (PhyStatus & (UINT16)~PHY_PCS_AD_RFLT);               
						Result = jtPhyWriteRegister (
								pDevInstance,
								pDevInstance->CurrentPhy.PhyAddress,
								PHY_AUTO_NEG_AD_REG,
								PhyStatus
							);              
					case PHY_PCS_AD_RFLT_OK:
						if ( Result == JT_STATUS_SUCCESS )
							*pConnected = JT_PHY_STATUS_LINK;
						else
							*pConnected = JT_PHY_STATUS_FAILURE;
						break;
					case PHY_PCS_AD_RFLT_OFFLINE:
						*pConnected = JT_PHY_STATUS_FAILURE;
						break;
					case PHY_PCS_AD_RFLT_AN_ERROR:
						*pConnected = JT_PHY_STATUS_RESTART;
						break;
				}
			}
			else
				*pConnected = JT_PHY_STATUS_FAILURE;
		}
		else
			*pConnected = JT_PHY_STATUS_FAILURE;
	}
	// Or if the Phy is the Level One Cheetah Phy
	else if (pDevInstance->CurrentPhy.PhyId == PHY_LEVEL1_CHEETAH)
	{
		// Wait 1 seconds for AN complete or Link, AN does not always complete
		for (LoopCount=0; LoopCount < 10; LoopCount++ )
		{
			Result = jtPhyReadRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_GMII_QUICK_STATUS_REG,
					&PhyStatus
				);
			if ((Result != JT_STATUS_SUCCESS) || 
				(PhyStatus & PHY_GMII_AN_DONE) || 
				(PhyStatus & PHY_GMII_LINK_OK))
			{
				break;
			}
	 
			// Wait and try again
			mdelay( 100 );
		}
      
		if (Result != JT_STATUS_SUCCESS)
			return Result;

		if (PhyStatus & PHY_STAT_LINK_OK)
			*pConnected = JT_PHY_STATUS_LINK;                 
	}
	// Or if the Phy is the Level One Phy
	else if (pDevInstance->CurrentPhy.PhyId == PHY_LEVEL1)
	{
		// Wait 2 seconds for AN complete or Link, AN does not always complete
		for (LoopCount=0; LoopCount < 20 ; LoopCount++ )
		{
			Result = jtPhyReadStatusRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					&PhyStatus
				);
			if ((Result != JT_STATUS_SUCCESS) || 
				(PhyStatus & PHY_STAT_AN_DONE) || 
				(PhyStatus & PHY_STAT_LINK_OK))
			{
				break;
			}
	 
			// Wait and try again
			mdelay( 100 );
		}
      
		if (Result != JT_STATUS_SUCCESS)
		{
			return Result;
		}
      
		// Read the Phy Status Register again to make sure everything's OK
		Result = jtPhyReadStatusRegister (
				pDevInstance,
				pDevInstance->CurrentPhy.PhyAddress,
				&PhyStatus
			);
		if (Result != JT_STATUS_SUCCESS)
		{
			return Result;
		}

		if (PhyStatus & PHY_STAT_LINK_OK)
		{
			*pConnected = JT_PHY_STATUS_LINK;                 
		}
	}
	// Or if the Phy is the National Phy
	else if (pDevInstance->CurrentPhy.PhyId == PHY_NATIONAL)
	{
		// Wait 3 seconds for AN complete or Link, AN does not always complete
		for (LoopCount=0; LoopCount < 30 ; LoopCount++ )
		{
			Result = jtPhyReadStatusRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					&PhyStatus
				);
			if ((Result != JT_STATUS_SUCCESS) || 
				(PhyStatus & PHY_STAT_AN_DONE) || 
				(PhyStatus & PHY_STAT_LINK_OK))
			{
				break;
			}
	 
			// Wait and try again
			mdelay( 100 );
		}
      
		if (Result != JT_STATUS_SUCCESS)
		{
			return Result;
		}
      
		// Read the Phy Status Register again to make sure everything's OK
		Result = jtPhyReadStatusRegister (
				pDevInstance,
				pDevInstance->CurrentPhy.PhyAddress,
				&PhyStatus
			);
		if (Result != JT_STATUS_SUCCESS)
		{
			return Result;
		}

		if (PhyStatus & PHY_STAT_LINK_OK)
		{
			*pConnected = JT_PHY_STATUS_LINK;                 
		}
	}
   
	return(Result); 
}  

//----------------------------------------------------------------------------
//
// FUNCTION:    jtPhyHandleStatusChange     
//
// DESCRIPTION: Process a change in the status of the Phy (link change, etc).
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtPhyHandleStatusChange( PDEV_INSTANCE pDevInstance )
{
	struct device * pDev = pDevInstance->pDev;
	JT_STATUS Result = JT_STATUS_SUCCESS;
	JT_PHY_STATUS CurrentlyConnected;
	UINT16 PhyReg;
	
	JT_ENTER;

	// Find out if the Phy is connected
	Result = jtPhyGetLinkStatus( 
			pDevInstance, 
			&CurrentlyConnected
		);
	if (Result != JT_STATUS_SUCCESS)
	{
		JT_LEAVE;
		return Result;
	}

	// And process the result
	switch (CurrentlyConnected)
	{
		case JT_PHY_STATUS_LINK:
			pDevInstance->CurrentPhy.Connected = TRUE;
			Result = jtPhyUpdateSpeedDuplex( pDevInstance );
			Result = JT_STATUS_SUCCESS;
			break;
		case JT_PHY_STATUS_FAILURE:
			pDevInstance->CurrentPhy.Connected = FALSE;
#if !defined JT_DEBUG
			printk(KERN_INFO "%s: Not Connected.\n",	pDev->name);
#else
			JT_DPRINTK("%s: Not Connected.\n",	pDev->name);
#endif
			Result = JT_STATUS_SUCCESS;
			break;
		case JT_PHY_STATUS_RESTART:
			pDevInstance->CurrentPhy.Connected = FALSE;
			// Let's read the Control register
			Result = jtPhyReadRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_CONTROL_REG,
					&PhyReg
				);
			PhyReg |= (PHY_CTL_RSAN);
			Result = jtPhyWriteRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_CONTROL_REG,
					PhyReg
				);              
			break;
	}
			
	JT_LEAVE;
	return Result;
}  


//----------------------------------------------------------------------------
//
// FUNCTION:    jtPhySetSpeed      
//
// DESCRIPTION: Update the Phy control register to request a new speed.
// 
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  PhySpeed      The requested speed (LINKS_SPEED_10, 100 or 1000)
//  pPhyControlReg  The current value of the Phy control register.
//
// RETURNS:
//  pPhyControlReg  The updated value of the Phy control register.
//
//----------------------------------------------------------------------------
STATIC void jtPhySetSpeed(
		PDEV_INSTANCE pDevInstance,
		UINT16 PhySpeed,
		PUINT16 pPhyControlReg
	)
{
	// If the phy is PCS
	if ( pDevInstance->CurrentPhy.PhyType == PHY_TYPE_PCS )   
	{
		*pPhyControlReg &= ~(UINT16)(PHY_CTL_GSPSEL | PHY_CTL_SPSEL);
		switch (PhySpeed)
		{
      case LINK_SPEED_10:
				// Do nothing
				break;
      case LINK_SPEED_100:
				*pPhyControlReg |= PHY_CTL_SPSEL;
				break;
      default:
				*pPhyControlReg |= PHY_CTL_GSPSEL;
		}
	}
	// or it must be GMII/MII (Cheetah does not allow to force gig)
	else
	{
		if (PhySpeed == LINK_SPEED_10)
		{
			*pPhyControlReg &= ~(UINT16)PHY_CTL_SPSEL;         
    }
    else
    {
			*pPhyControlReg |= PHY_CTL_SPSEL;
		}
	}
}

//----------------------------------------------------------------------------
//
// FUNCTION:    jtPhyInitialize
//
// DESCRIPTION: Setup the phy interface based on user supplied value for
//              duplex and speed, if any. Otherwise, use auto-negotiation.
//
//----------------------------------------------------------------------------

JT_STATUS jtPhyInitialize ( PDEV_INSTANCE pDevInstance )
{
	struct device * pDev = pDevInstance->pDev;
	JT_STATUS Result;
	UINT16 PhyReg;
	UINT16 ANReg;
	UINT16 GigGMIIReg = 0;
	UINT16 SavedPhyReg = 0;
	UINT16 SavedANReg = 0;
	UINT16 SavedGigGMIIReg = 0;
	JT_PHY_STATUS CurrentlyConnected = JT_PHY_STATUS_FAILURE;
  UINT32 EventStatus;
   
	// Disable Phy Interrupts
	jtPhyDisableInterrupts ( pDevInstance );
   
	// Let's read the Control register
	Result = jtPhyReadRegister (
			pDevInstance,
			pDevInstance->CurrentPhy.PhyAddress,
			PHY_CONTROL_REG,
			&PhyReg
		);
	if ( Result != JT_STATUS_SUCCESS )
	{
		return Result;
	}
	SavedPhyReg = PhyReg;
   
	// "Un-Isolate" the Phy
	if (PhyReg & PHY_CTL_ISLT)
	{
		PhyReg &= (UINT16)~PHY_CTL_ISLT;
	}
   
	// Is the Speed and/or the Duplex forced?
	if ((pDevInstance->OverrideSpeed != LINK_SPEED_AUTO) ||
		 (pDevInstance->OverrideDuplexMode != DUPLEX_MODE_AUTO))
	{
		// Announce override
#if !defined JT_DEBUG
		printk(KERN_INFO "%s: Manual link override: Speed %s Mb, %s Duplex.\n",
				pDev->name,
				pDevInstance->OverrideSpeed == LINK_SPEED_10 ? "10"
				: pDevInstance->OverrideSpeed == LINK_SPEED_100 ? "100"
				: "1000",
				pDevInstance->OverrideDuplexMode == DUPLEX_MODE_HALF ? "Half" : "Full"
			);
#else      
		JT_DPRINTK("%s: Manual link override: Speed %s Mb, %s Duplex.\n",
				pDev->name,
				pDevInstance->OverrideSpeed == LINK_SPEED_10 ? "10"
				: pDevInstance->OverrideSpeed == LINK_SPEED_100 ? "100"
				: "1000",
				pDevInstance->OverrideDuplexMode == DUPLEX_MODE_HALF ? "Half" : "Full"
			);
#endif

		// Is AN Enabled
		if (PhyReg & PHY_CTL_ANEN)
		{
			// Disable AN
			PhyReg &= (UINT16)~PHY_CTL_ANEN;
	 
			// Write to the phy, AN must be disabled independently of setting speed/duplex
			Result = jtPhyWriteRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_CONTROL_REG,
					PhyReg
				);              
			SavedPhyReg = PhyReg;
			if (Result != JT_STATUS_SUCCESS)
			{
				return Result;
			}
    }
      
		// Is the Duplex set properly
		if ((pDevInstance->OverrideDuplexMode & 
			(DUPLEX_MODE_AUTO|DUPLEX_MODE_HALF|DUPLEX_MODE_FULL)) == DUPLEX_MODE_HALF)
		{
			// Force Half
			PhyReg &= (UINT16)~PHY_CTL_FDMD;
		}
		else
		{
			// Force Full
			PhyReg |= PHY_CTL_FDMD;
		}
      
    // Set the Speed
    jtPhySetSpeed( pDevInstance, pDevInstance->OverrideSpeed, &PhyReg );
		
		//Modify the control register if necessary
		if ( SavedPhyReg != PhyReg )
		{
			Result = jtPhyWriteRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_CONTROL_REG,
					PhyReg
				);              
			if (Result != JT_STATUS_SUCCESS)
			{
				return Result;
			}
		}
	}
	else // Use autonegotiation
	{
		// Enable AN
		PhyReg |= (UINT16)PHY_CTL_ANEN;
    // If using MII phy
    if (pDevInstance->CurrentPhy.PhyType == PHY_TYPE_MII)
    {
			// Read the Auto Negotiate register
			Result = jtPhyReadRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_AUTO_NEG_AD_REG,
					&ANReg
				);
	 
			if (Result != JT_STATUS_SUCCESS)
			{
				return Result;
			}

			SavedANReg=ANReg;

			// Set the AN Advertisement Register to indicate all capabilities
			ANReg |=  PHY_MII_AD_10T | PHY_MII_AD_10T_FD | PHY_MII_AD_100T | PHY_MII_AD_100T_FD;
			if ( SavedANReg != ANReg )
			{
				Result = jtPhyWriteRegister (
						pDevInstance,
						pDevInstance->CurrentPhy.PhyAddress,
						PHY_AUTO_NEG_AD_REG,
						ANReg
					);
			}	 
		}
    // Or using GMII phy
    else if (pDevInstance->CurrentPhy.PhyType == PHY_TYPE_GMII)
    {
			// Read the Auto Negotiate register
			Result = jtPhyReadRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_AUTO_NEG_AD_REG,
					&ANReg
				);
	 
			if (Result != JT_STATUS_SUCCESS)
			{
				return Result;
			}

			SavedANReg=ANReg;

			// Set the AN Advertisement Register to indicate all capabilities	and use next page
			ANReg |=  PHY_MII_AD_NXPG | PHY_MII_AD_10T | PHY_MII_AD_10T_FD | PHY_MII_AD_100T | PHY_MII_AD_100T_FD;
			
			// LXT1001 cannot use flow control
			if (pDevInstance->ControllerType == LXT1001)
        ANReg &= ~PHY_PCS_AD_PAUSE;
			
			if ( SavedANReg != ANReg )
			{
				Result = jtPhyWriteRegister (
						pDevInstance,
						pDevInstance->CurrentPhy.PhyAddress,
						PHY_AUTO_NEG_AD_REG,
						ANReg
					);

				if (Result != JT_STATUS_SUCCESS)
				{
					return Result;
				}
			}
			
			// Read the 1000BASET/100BASET register
			Result = jtPhyReadRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_GMII_1000T_CONTROL_REG,
					&GigGMIIReg
				);
	 
			if (Result != JT_STATUS_SUCCESS)
			{
				return Result;
			}

			SavedGigGMIIReg=GigGMIIReg;

			// Set the second AN Advertisement Register to indicate gig capabilities	
			GigGMIIReg |=  (PHY_GMII_AD_1000T_FD | PHY_GMII_AD_1000T);
			// GigGMIIReg &= ~(PHY_GMII_MA_SL_CNF_EN | PHY_GMII_MULTI_PORT);
			if ( SavedGigGMIIReg != GigGMIIReg )
			{
				Result = jtPhyWriteRegister (
						pDevInstance,
						pDevInstance->CurrentPhy.PhyAddress,
						PHY_GMII_1000T_CONTROL_REG,
						GigGMIIReg
					);
			}
		}
    // If not MII, Is this Phy PCS?
    else if (pDevInstance->CurrentPhy.PhyType == PHY_TYPE_PCS )
    {
			// Auto Negotiate with advertisement set to Link Online
			Result = jtPhyReadRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_AUTO_NEG_AD_REG,
					&ANReg
				);
	 
			if (Result != JT_STATUS_SUCCESS)
			{ 
				return Result;
			}

			// LXT1001 cannot use flow control
			if (pDevInstance->ControllerType == LXT1001)
				// Turn off the Offline Bit and the Pause Capability
				ANReg = (ANReg & (UINT16)(~PHY_PCS_AD_RFLT_OFFLINE | PHY_PCS_AD_PAUSE_NO));
			else
				// Turn off the Offline Bit 
				ANReg = (ANReg & (UINT16)~PHY_PCS_AD_RFLT_OFFLINE);

			// Now let's write to the Phy
			Result = jtPhyWriteRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_AUTO_NEG_AD_REG,
					ANReg
				);
			if (Result != JT_STATUS_SUCCESS)
			{
				return Result;
			}
		}

		//If necessary we restart the autonegotiation
		if ( SavedPhyReg != PhyReg || SavedANReg != ANReg || SavedGigGMIIReg != GigGMIIReg)
		{
			// Enable AN
#if !defined JT_DEBUG
			printk(KERN_INFO "%s: AutoNegotiate link.\n",	pDev->name);
#else
			JT_DPRINTK("%s: AutoNegotiate link.\n",	pDev->name);
#endif
			PhyReg |= (PHY_CTL_RSAN);
			Result = jtPhyWriteRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_CONTROL_REG,
					PhyReg
				);              
			if (Result != JT_STATUS_SUCCESS)
			{
				return Result;
			}
		}
	}

	// Is the link Active?
	Result = jtPhyGetLinkStatus(
			pDevInstance,
			&CurrentlyConnected 
		);           
	if (Result != JT_STATUS_SUCCESS)
	{
		return Result;
	}
   
	// And process the result
	switch (CurrentlyConnected)
	{
		case JT_PHY_STATUS_LINK:
			pDevInstance->CurrentPhy.Connected = TRUE;
			Result = jtPhyUpdateSpeedDuplex( pDevInstance );
			break;
		case JT_PHY_STATUS_FAILURE:
			pDevInstance->CurrentPhy.Connected = FALSE;
#if !defined JT_DEBUG
			printk(KERN_INFO "%s: Not Connected.\n",	pDev->name);
#else
			JT_DPRINTK("%s: Not Connected.\n",	pDev->name);
#endif
			Result = JT_STATUS_SUCCESS;
			break;
		case JT_PHY_STATUS_RESTART:
			pDevInstance->CurrentPhy.Connected = FALSE;
			PhyReg |= (PHY_CTL_RSAN);
			Result = jtPhyWriteRegister (
					pDevInstance,
					pDevInstance->CurrentPhy.PhyAddress,
					PHY_CONTROL_REG,
					PhyReg
				);              
			break;
	}

	// We need this not to generate an interrupt
	jtCsrIn32( pDevInstance, INTR_STATUS_REG, &EventStatus );
	// Enable Phy Interrupts
	jtPhyEnableInterrupts ( pDevInstance );
   
	return(Result);    
}              


//----------------------------------------------------------------------------
//
// FUNCTION:    jtPhyWriteRegister
//
// DESCRIPTION: Write a 16 bit value to the PHY register specified.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  PhyAddress    The address of the Phy to affect.
//  PhyRegister   The particular register to write to.
//  Data          The value to write to the register.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtPhyWriteRegister (
		PDEV_INSTANCE pDevInstance,
		UINT8 PHYAddress,
		UINT8 PHYRegister,
		UINT16 Data
	)
{
	UINT32 GMIIStatus;
	UINT8 Milliseconds;
	UINT32 GMIIRegisterData = 0;
   
	// Assemble a Phy write command
	GMIIRegisterData = 
		( Data << 16 ) |
		( ( PHYAddress & 0x1f ) << 8 ) |
		GMST |
		GMCM |
		( PHYRegister & 0x1f );
   
	// Indicate a Phy write to the controller
	jtCsrOut32 ( pDevInstance, GMII_ACCESS_REG, GMIIRegisterData );
   
	// Wait for ack from controller
	for (Milliseconds=0; Milliseconds < MII_TIMEOUT_MILLISECONDS ; Milliseconds++)
	{
		jtCsrIn32 ( pDevInstance, GMII_ACCESS_REG, &GMIIStatus );
		if ( (GMIIStatus & GMST) == 0 )
		{
			return JT_STATUS_SUCCESS;
    }      
      
		// Wait a while longer
		mdelay( 1 ); // 1 millisecond
	}  
   
	JT_DPRINTK( "jyPhyWriteRegister returns JT_STATUS_TIMEOUT\n");
	return(JT_STATUS_TIMEOUT);   
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtPhyReadRegister
//
// DESCRIPTION: Read a 16 bit value from the PHY register specified.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  PhyAddress    The address of the Phy to affect.
//  PhyRegister   The particular register to read from.
//
// RETURNS:
//  pData         Updated with the value read from the register.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtPhyReadRegister (
		PDEV_INSTANCE pDevInstance,
		UINT8 PHYAddress,
		UINT8 PHYRegister,
		PUINT16 pData
	)
{
	UINT32 GMIIStatus;
	UINT8 Milliseconds;
	UINT32 GMIIRegisterData;
   
	// Assemble a Phy read command
	GMIIRegisterData = 
		( ( PHYAddress & 0x1f ) << 8 ) |
		GMST | 
		( PHYRegister & 0x1f );
   
	// Indicate a Phy read to the controller
	jtCsrOut32 ( pDevInstance, GMII_ACCESS_REG, GMIIRegisterData );
   
	// Wait for ack
	for (Milliseconds=0; Milliseconds < MII_TIMEOUT_MILLISECONDS ; Milliseconds++)
	{
		jtCsrIn32 ( pDevInstance, GMII_ACCESS_REG, &GMIIStatus );
		if ( (GMIIStatus & GMST) == 0 )
    {
			*pData = (UINT16)(GMIIStatus>>16);
			return JT_STATUS_SUCCESS;
		}      
      
		// Wait a while longer
		mdelay( 1 ); // 1 millisecond
	}
   
	JT_DPRINTK( "jyPhyReadRegister returns JT_STATUS_TIMEOUT\n");
	return(JT_STATUS_TIMEOUT);   
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtPhyReadStatusRegister
//
// DESCRIPTION: Read the value of the PHY's Status register.
//              Because of a bug/quirk in certain PHY's, the value is
//              read multiple times until two consecutive reads yield the
//              same value.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  PhyAddress    The address of the Phy to affect.
//
// RETURNS:
//  pPhyStatus    Updated with the value read from the status register.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtPhyReadStatusRegister (
		PDEV_INSTANCE pDevInstance,
		UINT8 PhyAddress,
		PUINT16 pPhyStatus
	)
{
	JT_STATUS Result = JT_STATUS_SUCCESS;
	UINT8 Milliseconds;
	UINT16 PreviousPhyStatus = -1;

	// If the last two values read don't match, loop waiting for it to stabilize
	for( Milliseconds = 0; Milliseconds < MII_TIMEOUT_MILLISECONDS ; Milliseconds++)
	{
		// Read the status again
		Result = jtPhyReadRegister (
				pDevInstance,
				PhyAddress,
				PHY_STATUS_REG,
				pPhyStatus
			);
    if ( Result != JT_STATUS_SUCCESS )
    {
			JT_DPRINTK( "jtPhyReadStatusRegister returns %d\n", Result);
			return Result;
    }
      
		// Has value stabilized?
		//      JT_DPRINTK( "Phy Status = %10x, Previous Status = %10x\n", *pPhyStatus, PreviousPhyStatus );
		if ( *pPhyStatus == PreviousPhyStatus )
		{
			return JT_STATUS_SUCCESS;
		}
      
		// Save the last read status
		PreviousPhyStatus = *pPhyStatus;

		// Wait a while longer
		mdelay( 1 ); // 1 millisecond
	}

	JT_DPRINTK( "jtPhyReadStatusRegister returns JT_STATUS_TIMEOUT\n");
	return(JT_STATUS_TIMEOUT);   
}


/*****************************************************************************
 * RX Routines
 ****************************************************************************/

//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxCreateQueues
//
// DESCRIPTION: Allocate the queues required for PDL and PDC receive modes.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  ElementCount  The maximum number of elements each queue will hold.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxCreateQueues(
		PDEV_INSTANCE pDevInstance,
		int ElementCount
	)
{
	JT_STATUS Result1, Result2;

	JT_ASSERT( pDevInstance->RxIOMode != RX_IOMODE_PIO );
   
	// Initialize the Available and InProgress queues
	Result1 = jtQueueCreate( 
			&pDevInstance->RxPDxAvailableQueue, 
			ElementCount
		);
	Result2 = jtQueueCreate(
			&pDevInstance->RxPDxInProgressQueue,
			ElementCount
		);
	if( Result1 != JT_STATUS_SUCCESS || Result2 != JT_STATUS_SUCCESS )
	{
		return JT_STATUS_RESOURCES;
	}
   
	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPDCCreate
//
// DESCRIPTION: Allocate the PDC buffers required for PDC receive mode.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxPDCCreate(PDEV_INSTANCE pDevInstance)
{
	JT_STATUS Result = JT_STATUS_SUCCESS;
	int PDCIndex;
	int MinPDCSize;
#if !defined WORKAROUND_RECEIVE_BUFFERS
	struct device * pDev = pDevInstance->pDev;
#endif

	JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDC );
   
	// Create the queues
	Result = jtRxCreateQueues( pDevInstance, pDevInstance->RxPDCCount );
	if( Result != JT_STATUS_SUCCESS )
	{
		return Result;
	}

	// Each PDC must be at least large enough to hold one Rx PDC header,
	// one full-size packet, and a trailing null header.
#if defined (WORKAROUND_RECEIVE_BUFFERS)
	MinPDCSize = 64 * 1024;
#else
	MinPDCSize = pDev->mtu + ETH_HLEN + 3 * sizeof(UINT32); 
#if defined(WORKAROUND_CRC_CHECK)
	if (pDevInstance->RxCrcCheck)
		MinPDCSize += ( 4*ETH_HLEN/60); 
#endif
#endif

	jtSizeToOrder(
			MinPDCSize,
			&pDevInstance->RxPDCOrder,
			&pDevInstance->RxPDCLength
		);
   
	// Interate over each available Rx PDC table entry.
	for( PDCIndex = 0; PDCIndex < pDevInstance->RxPDCCount; ++PDCIndex )
	{
		RX_PDC * pPDC;

		// Allocate PDC buffer
		pPDC = (RX_PDC *) GET_FREE_PAGES( GFP_KERNEL, pDevInstance->RxPDCOrder );
		//			pPDC = (RX_PDC *) kmalloc( pDevInstance->RxPDCLength, GFP_KERNEL );
		if( pPDC == NULL )
		{
			return JT_STATUS_RESOURCES;
		}
      
    // Put buffer on the Available queue
    jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDC );
	}

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPDLCreate
//
// DESCRIPTION: Allocate the PDL buffers required for PDL receive mode.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxPDLCreate(PDEV_INSTANCE pDevInstance)
{
	JT_STATUS Result = JT_STATUS_SUCCESS;
	int MinPDLSize;
	int ActualPDLSize = 0;
	int PDLIndex;
	
	JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDL );
   
	// Create the queues
	Result = jtRxCreateQueues( pDevInstance, pDevInstance->RxPDLCount );
	if( Result != JT_STATUS_SUCCESS )
	{
		return Result;
	}
   
	// Block must be at least large enough to hold all the PDLs
	// with cache-line alignment
	MinPDLSize = pDevInstance->RxPDLCount * pDevInstance->RxPDLAlignment;

	jtSizeToOrder(
			MinPDLSize,
			&pDevInstance->RxPDLOrder,
			&ActualPDLSize
		);
   
	// First allocate a big contiguous block
	pDevInstance->pRxPDLBase = (void *) GET_FREE_PAGES( GFP_KERNEL, pDevInstance->RxPDLOrder );
	if( pDevInstance->pRxPDLBase == NULL )
	{
		return JT_STATUS_RESOURCES;
	}
   
	// Carve the block into individual PDL buffers and put them on the queue
	for( PDLIndex = 0; PDLIndex < pDevInstance->RxPDLCount; ++PDLIndex )
	{
		// Each PDL buffer has room for exactly one fragment
		// plus 8 bytes free at the end
		void * pPDL = pDevInstance->pRxPDLBase + pDevInstance->RxPDLAlignment * PDLIndex;
  
		// Put buffer on the Available queue
		jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL );
	}
   
	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPDCInitialize
//
// DESCRIPTION: Initialize the PDC buffers required for PDC receive mode.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxPDCInitialize(PDEV_INSTANCE pDevInstance)
{
	int PDCIndex;

	JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDC );

	// Initialize Rx PDC Table Index
	pDevInstance->RxPDCNextTableIndex = 0;

	// Interate over each PDC in the Rx Available queue.
	for( PDCIndex = 0; PDCIndex < pDevInstance->RxPDCCount; ++PDCIndex )
	{
		RX_PDC * pPDC;
		UINT64 BusAddr;

		jtCsrOut32( pDevInstance, RX_PDC_BUF_ADR_TBL_IX, PDCIndex );

		// Take PDC from the head the Available queue
		jtQueueDequeue( &pDevInstance->RxPDxAvailableQueue, (void**) &pPDC );

		// Register the buffer with the controller
		BusAddr = virt_to_bus( pPDC );
		
		// Order dependency! The high dword must be transferred first!
		jtCsrOut32( pDevInstance, RX_PDC_BUF_ADR_HI, (UINT32) (BusAddr >> 32) );
		jtCsrOut32( pDevInstance, RX_PDC_BUF_ADR_LO, (UINT32) (BusAddr) );

		// Put PDC back on the tail the Available queue
		jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDC );
	}
   
	// Now send receive commands to controller
	jtRxPDCReplenish( pDevInstance );

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPDLInitialize
//
// DESCRIPTION: Initialize the PDL buffers required for PDL receive mode.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxPDLInitialize(PDEV_INSTANCE pDevInstance)
{
	JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDL );

	// Now send receive commands to controller
	(void) jtRxPDLReplenish( pDevInstance );

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPDCReplenish
//
// DESCRIPTION: Send down PDC receive commands to the controller until
//              can take no more.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxPDCReplenish( PDEV_INSTANCE pDevInstance )
{
	JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDC );

	// Send receive commands to the controller until it's full
	while( !jtQueueIsEmpty( &pDevInstance->RxPDxAvailableQueue )
	&& (jtRxGetFreeCount( pDevInstance ) > 0) )
	{
		void * pPDC = NULL;
		UINT32 RxPDCCommand;

		// Get buffer from the Available queue
		jtQueueDequeue( &pDevInstance->RxPDxAvailableQueue, (void**) &pPDC );

		// Put buffer on the In Progress queue
		jtQueueEnqueue( &pDevInstance->RxPDxInProgressQueue, pPDC );
      
		// Signal controller to use this buffer for receive
		RxPDCCommand = pDevInstance->RxPDCLength
			| ( pDevInstance->RxPDCNextTableIndex << RX_PDC_REG_BFID_SHIFT )
			| RXINRQ ;
		
		#if !defined(WORKAROUND_WRITE_COMBINE)
			jtCsrOut32( pDevInstance, RX_PDC_REG, RxPDCCommand );
		#else
			outl( RxPDCCommand, pDevInstance->pDev->base_addr + RX_PDC_REG );
		#endif
      
		// Advance to next table entry
		pDevInstance->RxPDCNextTableIndex = 
			(pDevInstance->RxPDCNextTableIndex + 1) % pDevInstance->RxPDCCount;
      
    // Update count
    jtRxDecrementFreeCount( pDevInstance );
	} 
   
	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPDLReplenish
//
// DESCRIPTION: Send down PDL receive commands to the controller until
//              can take no more.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxPDLReplenish( PDEV_INSTANCE pDevInstance )
{
	struct device * pDev = pDevInstance->pDev;
   
	JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDL );
   
	// Send receive commands to the controller until it's full
	while( !jtQueueIsEmpty( &pDevInstance->RxPDxAvailableQueue )
		&& (jtRxGetFreeCount( pDevInstance ) > 0) )
	{
		PDL * pPDL = NULL;
		struct sk_buff * pSkBuff = NULL;
		UINT64 SkBuffBusAddr;
		UINT64 SkBuffVirtAddr;
		UINT64 PDLBusAddr;
		UINT32 PacketLength;
      
    // Set the packet length to the maximum frame size
#if defined(WORKAROUND_RECEIVE_BUFFERS)
    PacketLength = 64 * 1024 - 2;
#else
    PacketLength = pDev->mtu + ETH_HLEN;
#if defined(WORKAROUND_CRC_CHECK)
		if (pDevInstance->RxCrcCheck)
    	PacketLength += 4;
#endif
#endif
    
    // Allocate receive packet
    pSkBuff = dev_alloc_skb( PacketLength + 2 );
    if( pSkBuff == NULL ) 
    {
			JT_DPRINTK(
					"%s: Out of memory.\n",
					pDev->name
				);
	 
			// Exit without trying to construct any more receive commands.
			return JT_STATUS_RESOURCES;
    }
      
		// Align the IP header on a  16 byte boundary
		skb_reserve(pSkBuff, 2);

		// Get PDL buffer from the Available queue
		jtQueueDequeue( &pDevInstance->RxPDxAvailableQueue, (void**) &pPDL );
      
		// Create header in PDL buffer
		pPDL->Header = PacketLength
			| (1 << RX_PDL_FGCN_SHIFT) // Fragment count
			| RXINRQ;
      
    // Point first PDL fragment to the skbuff
    SkBuffBusAddr = virt_to_bus( pSkBuff->data );

    pPDL->Fragment[0].FragmentAddressLow =  (UINT32) (SkBuffBusAddr);
    pPDL->Fragment[0].FragmentAddressHigh = (UINT32) (SkBuffBusAddr >> 32);
    pPDL->Fragment[0].FragmentLength = PacketLength;
    
    // Save away the sk_buff address in second fragment
    SkBuffVirtAddr = (unsigned long) pSkBuff;
    pPDL->Fragment[1].FragmentAddressLow = (UINT32) (SkBuffVirtAddr);
    pPDL->Fragment[1].FragmentAddressHigh = (UINT32) (SkBuffVirtAddr >> 32);
    
    // Put buffer on the In Progress queue
    jtQueueEnqueue( &pDevInstance->RxPDxInProgressQueue, pPDL );
    
    // Send receive command to the controller
    PDLBusAddr = virt_to_bus( pPDL );

		// Order dependency! The high dword must be transferred first!
		if ( (UINT32)(PDLBusAddr >> 32) ) 
			jtCsrOut32( pDevInstance, RX_PDL_ADDR_REG_HI, (UINT32) (PDLBusAddr >> 32) );
		jtCsrOut32( pDevInstance, RX_PDL_ADDR_REG_LO, (UINT32) (PDLBusAddr) );
      
    // Update count
    jtRxDecrementFreeCount( pDevInstance );
	} 

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxStart
//
// DESCRIPTION: Direct the controller to begin receiving packets and
//              to raise interrupts for packets received.
//
//----------------------------------------------------------------------------

STATIC void jtRxStart( PDEV_INSTANCE pDevInstance)
{
	// If in PIO receive mode
	if ( pDevInstance->RxIOMode == RX_IOMODE_PIO )
	{
		// Enable interrupts for packet received
		jtCsrOut32( pDevInstance, INTR_MASK_REG, SERECL1 | RXMS );
	}
	else // .. in Bus Master mode
	{
		jtCsrOut32( pDevInstance, RX_PDL_ADDR_REG_HI, 0 );

		// Enable interrupts for PDC or PDL DMA complete
		jtCsrOut32( pDevInstance, INTR_MASK_REG, SERECL1 | RXPDMS );
	}

	// Enable packet reception on the controller
	jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL1 | RXEN );
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxValidatePacketLength
//
// DESCRIPTION: Verify that a receive packet has an acceptable length.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  PacketByteCount  Length of the received packet.
//
//----------------------------------------------------------------------------
INLINE JT_STATUS jtRxValidatePacketLength(
		PDEV_INSTANCE pDevInstance,
		UINT32 PacketByteCount
	)
{
	struct device * pDev = pDevInstance->pDev;

	// Validate the length
	if( (PacketByteCount == 0) || (PacketByteCount > (pDev->mtu + ETH_HLEN)) )
	{
		// Discard this packet
		pDevInstance->Statistics.rx_errors++;
		JT_DPRINTK(
				"Discarding %s packet %d\n",
				PacketByteCount ? "oversize" : "zero-length", 
				PacketByteCount
			);
		return JT_STATUS_FAILURE;
	}

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxValidatePacketIntegrity
//
// DESCRIPTION: Verify that a receive packet has no errors.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  PacketHeader1 The first dword of packet headers from the controller.
//  PacketHeader2 The second dword.
//
//----------------------------------------------------------------------------
INLINE JT_STATUS jtRxValidatePacketIntegrity(
		PDEV_INSTANCE pDevInstance,
		UINT32 PacketHeader1,
		UINT32 PacketHeader2
	)
{
	// Check for errors in the packet 
#if defined WORKAROUND_ERROR_HEADERS
	if( (PacketHeader1 & RXER) != 0 
			 || (PacketHeader2 & (IPCKER | IPHDPN)) == (IPCKER | IPHDPN) 
			 || (PacketHeader2 & (TPCKER | TPHDPN)) == (TPCKER | TPHDPN)
			 || (PacketHeader2 & (UPCKER | UPHDPN)) == (UPCKER | UPHDPN)
		)
#else
	if( (PacketHeader1 & RXER) != 0 
		|| (PacketHeader2 & (IPCKER | TPCKER | UPCKER)) )
#endif
	{
		// Discard this packet
		pDevInstance->Statistics.rx_errors++;
		JT_DPRINTK( "Discarding errored packet %X.\n", PacketHeader1);
		return JT_STATUS_FAILURE;
	}

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxHandOffPacket
//
// DESCRIPTION: Give a received packet to higher protocol layers.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  pSkBuff       The received packet.
//  PacketHeader2 The second dword of the packet headers from the controller.
//
//----------------------------------------------------------------------------
INLINE void jtRxHandOffPacket(
		DEV_INSTANCE * pDevInstance,
		struct sk_buff * pSkBuff,
		UINT32 PacketHeader2
	)
{
	struct device * pDev = pDevInstance->pDev;

	if ( pDevInstance->DiagnosticsStatus == DIAGS_OFF )
	{
		// Setup skbuff fields
		pSkBuff->dev = pDev;
		pSkBuff->protocol = eth_type_trans(pSkBuff, pDev);

		// Hardware checksumming for IP/TCP/UDP packets 
		if( PacketHeader2 & IPHDPN )
		{
			if ( (PacketHeader2 & TPHDPN) || (PacketHeader2 & UPHDPN) )
				pSkBuff->ip_summed = CHECKSUM_UNNECESSARY;
			else
				pSkBuff->ip_summed = CHECKSUM_HW;
		}
		
		// Update statistics
		pDevInstance->Statistics.rx_packets++;
#if LINUX_VERSION_CODE >= 0x20125
		pDevInstance->Statistics.rx_bytes += pSkBuff->len;
#endif

		// Hand off packet to protocol stack
		netif_rx(pSkBuff);
	}
	else
	{
		if ( !jtQueueIsFull(&pDevInstance->dbgRxQueue) )
			jtQueueEnqueue( &pDevInstance->dbgRxQueue, (void *)pSkBuff );
		else
			DEV_KFREE_SKB( pSkBuff, FREE_READ );
	}
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPDCReceive
//
// DESCRIPTION: Process packets received by the controller.
//              Reclaim buffers used by packets that have been received.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  ReceiveDoneCount  The number of PDC receive commands that have completed.
//
//----------------------------------------------------------------------------
STATIC JT_STATUS jtRxPDCReceive(
		PDEV_INSTANCE pDevInstance,
		UINT32 EventStatus 
	)
{
	struct device * pDev = pDevInstance->pDev;
	UINT32 ReceiveDoneCount = 0;
	int ReceiveIndex;

	JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDC );

	// Update RXDMDNCN
	ReceiveDoneCount = (EventStatus & INTR_STATUS_RXDMDNCN) >> INTR_STATUS_RXDMDNCN_SHIFT;
		
	// Iterate over each completed receive
	for( ReceiveIndex = 0; ReceiveIndex < ReceiveDoneCount; ++ReceiveIndex )
	{
		void * pPDC;
		RX_PDC * pPDCCurrPacket;

    // If no buffers are on the In Progress queue
    if( jtQueueIsEmpty( &pDevInstance->RxPDxInProgressQueue ) )
    {
			// Serious error in our bookeeping
			printk(
					 KERN_ERR "%s: Controller indicates nonexistent receives.\n",
					 pDev->name
				);
#if defined(JT_DEBUG)
			BreakPoint();
#endif
			return JT_STATUS_FAILURE;
		}

		// Take the receive buffer off the In Progress queue
		jtQueueDequeue( &pDevInstance->RxPDxInProgressQueue, (void **) &pPDC );
		
		// Iterate over each packet in the buffer
		pPDCCurrPacket = (RX_PDC *) pPDC;

		while( TRUE )
		{
			UINT32 PacketHeader1 = 0;
			UINT32 PacketHeader2 = 0;
			UINT32 PacketByteCount;
			JT_STATUS Result;
			struct sk_buff * pSkBuff;
			void * pPacketData;

			// Check for NULL header
			PacketHeader1 = pPDCCurrPacket->Header;
			PacketHeader2 = pPDCCurrPacket->Header2;
			if( (PacketHeader1 & RX_PDC_HDTYPE) == 0 )
			{
				break;
			}

			// Extract length of packet and round up to integral number of dwords
			PacketByteCount = (PacketHeader1 & PKT_HDR_LENGTH_MASK);

#if defined(WORKAROUND_CRC_CHECK)
			if (pDevInstance->RxCrcCheck)
			{
				UINT32 SoftCrc, HardCrc;

				PacketByteCount -= 4;
				
				SoftCrc = crc32buf( (char *)pPDCCurrPacket->Data, PacketByteCount );
				HardCrc = *(PUINT32)(((char *)pPDCCurrPacket->Data+PacketByteCount));
				if(	SoftCrc != HardCrc )
	    	{
					//JT_DPRINTK("Mismatch: Hard CRC %x, Soft CRC %x\n", HardCrc, SoftCrc);
					printk(KERN_CRIT "Mismatch: Hard CRC %x, Soft CRC %x\n", HardCrc, SoftCrc);
					break;
				}
			}
#endif

			// Validate the length
			Result = jtRxValidatePacketLength( pDevInstance, PacketByteCount );
			if( Result != JT_STATUS_SUCCESS )
			{
				break;
			}

			// Check for errors in the packet
			Result = jtRxValidatePacketIntegrity( 
					pDevInstance,
					PacketHeader1,
					PacketHeader2 
				);
			if( Result != JT_STATUS_SUCCESS )
			{
				// Continue to next packet
#if defined(WORKAROUND_CRC_CHECK)
				if (pDevInstance->RxCrcCheck)
					pPDCCurrPacket += (PacketByteCount + 4 - 1)/8 + 2; 
				else
#endif
					pPDCCurrPacket += (PacketByteCount - 1)/8 + 2; 
				continue;
			}
    
			// Allocate buffer for packet
			pSkBuff = dev_alloc_skb( PacketByteCount + 2 );
			if( pSkBuff == NULL ) 
			{
				// Drop this packet
				pDevInstance->Statistics.rx_dropped++;
				printk(
						KERN_NOTICE "%s: Out of memory. Dropping packet.\n",
						pDev->name
					);
	 
				// Exit without trying to receive any more packets
				break;
			}

			// Align the IP header on a  16 byte boundary
			skb_reserve(pSkBuff, 2);

			// Copy packet data to skbuff
			pPacketData = skb_put( pSkBuff, PacketByteCount );
			memcpy( pPacketData, (const void *) pPDCCurrPacket->Data, PacketByteCount );

			// Give packet to next layer
			jtRxHandOffPacket( pDevInstance, pSkBuff, PacketHeader2 );
			pSkBuff = NULL;
  
			// Advance to next packet
#if defined(WORKAROUND_CRC_CHECK)
			if (pDevInstance->RxCrcCheck)
				pPDCCurrPacket += (PacketByteCount + 4 - 1)/8 + 2; 
			else
#endif
				pPDCCurrPacket += (PacketByteCount - 1)/8 + 2; 
    }

    // Put the receive buffer on the Available queue
    jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDC );
	}
   
	jtRxPDCReplenish( pDevInstance );

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPDLReceive
//
// DESCRIPTION: Process packets received by the controller.
//              Reclaim buffers used by packets that have been received.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  ReceiveDoneCount  The number of PDL receive commands that have completed.
//
//----------------------------------------------------------------------------
STATIC JT_STATUS jtRxPDLReceive(
		PDEV_INSTANCE pDevInstance, 
		UINT32 EventStatus 
	)
{
	struct device * pDev = pDevInstance->pDev;
	UINT32 ReceiveDoneCount = 0;
	int ReceiveIndex;

	JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDL );

	// Update RXDMDNCN
	ReceiveDoneCount = (EventStatus & INTR_STATUS_RXDMDNCN) >> INTR_STATUS_RXDMDNCN_SHIFT;
		
	// Iterate over each completed receive
	for( ReceiveIndex = 0; ReceiveIndex < ReceiveDoneCount; ++ReceiveIndex )
	{
		PDL * pPDL = NULL;
		struct sk_buff * pSkBuff = NULL;
		UINT64 SkBuffVirtAddr;
		UINT32 PacketHeader1 = 0;
		UINT32 PacketHeader2 = 0;
		UINT32 PacketByteCount;
		JT_STATUS Result;

		// If no buffers are on the In Progress queue
		if( jtQueueIsEmpty( &pDevInstance->RxPDxInProgressQueue ) )
		{
			// Serious error in our bookeeping
			printk(
					KERN_ERR "%s: Controller indicates nonexistent receives.\n",
					pDev->name
				);
			return JT_STATUS_FAILURE;
    }

    // Take the receive buffer off the In Progress queue
    jtQueueDequeue( &pDevInstance->RxPDxInProgressQueue, (void **) &pPDL );
		//pDevInstance->RxPDxCommandFreeCount ++;
      
		// Get the sk_buff packet
		SkBuffVirtAddr = (((UINT64) pPDL->Fragment[1].FragmentAddressHigh) << 32)
			| pPDL->Fragment[1].FragmentAddressLow;
		pSkBuff = (void *) (unsigned long) SkBuffVirtAddr;

#if defined(JT_DEBUG)
		pPDL->Fragment[1].FragmentAddressLow = 0;
		pPDL->Fragment[1].FragmentAddressHigh = 0;
#endif

		// Get packet headers
		PacketHeader1 = pPDL->Header;
		PacketHeader2 = pPDL->Header2;

		// Extract length of packet and round up
		PacketByteCount = (PacketHeader1 & PKT_HDR_LENGTH_MASK);
		
#if defined(WORKAROUND_CRC_CHECK)
		if (pDevInstance->RxCrcCheck)
		{
			UINT32 SoftCrc, HardCrc;

			UINT32 Offset;
			
			PacketByteCount -= 4;
			SoftCrc = crc32buf( pSkBuff->data, PacketByteCount );
			HardCrc = (UINT32)*(PUINT32)((pSkBuff->data+PacketByteCount));
			//if ( HardCrc == (UINT32)*(PUINT32)(pSkBuff->data+PacketByteCount-8) )
			if(	SoftCrc != HardCrc )
	    {
				SoftCrc = crc32buf( pSkBuff->data, PacketByteCount );

				//JT_DPRINTK("Mismatch: Hard CRC %x, Soft CRC %x\n", HardCrc, SoftCrc);
				printk(KERN_CRIT "Mismatch: Hard CRC %x, Soft CRC %x\n", HardCrc, SoftCrc);
				switch ( (PacketByteCount+6) % 8 )
				{ 
					case 0:
						Offset=6;
						break;
					case 7:
						Offset=5;
						break;
					case 6:
						Offset=4;
						break;
					case 5:
						Offset=3;
						break;
					case 4:
						Offset=2;
						break;
					default:
						Offset=1;
						break;
				}

				//Offset = min(6, (PacketByteCount+6) % 8 );
				printk(KERN_CRIT "Offset %d, Packet %d\n", Offset, PacketByteCount); 
				if ( !memcmp( 
						pSkBuff->data+PacketByteCount+4-Offset-8, 
						pSkBuff->data+PacketByteCount+4-Offset, 
						Offset )
					)
					printk(KERN_CRIT "Got it!\n");
				else 
					
				jtCsrOut32( pDevInstance, CSR57, (UINT32)pSkBuff->data );

				DEV_KFREE_SKB( pSkBuff, FREE_WRITE );

				// Put the PDL back on the available queue
				jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL );
				continue;

			}
		}
#endif 

		// Validate the length
		Result = jtRxValidatePacketLength( pDevInstance, PacketByteCount );		
		
		if( Result != JT_STATUS_SUCCESS )
    {
			DEV_KFREE_SKB( pSkBuff, FREE_WRITE );

			// Put the PDL back on the available queue
			jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL );
			JT_DPRINTK("Packet has wrong length!\n");
			continue;
		}

		// Check for errors in the packet
		Result = jtRxValidatePacketIntegrity( 
				pDevInstance,
				PacketHeader1,
				PacketHeader2 
			);
		if( Result != JT_STATUS_SUCCESS )
		{
			DEV_KFREE_SKB( pSkBuff, FREE_WRITE );

			// Put the PDL back on the available queue
			jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL );
			JT_DPRINTK("Packet has errors!\n");
			continue;
    }

		// Setup skbuff fields
		skb_put( pSkBuff, PacketByteCount );

		// Give packet to next layer
		jtRxHandOffPacket( pDevInstance, pSkBuff, PacketHeader2 );
		pSkBuff = NULL;
      
    // Put the buffer back on the available queue
    jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL );
	}

	(void) jtRxPDLReplenish( pDevInstance );

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPIOReceive
//
// DESCRIPTION: Obtain a received packet from the controller using 
//              the PIO mechanism.
//              This function expects to be called from the interrupt handler.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxPIOReceive( 
		DEV_INSTANCE * pDevInstance, 
		UINT32 EventStatus 
	)
{
	struct device * pDev = pDevInstance->pDev;
	UINT16 PacketAvailCount = 0;
	UINT16 PacketDoneCount = 0;

	// Any packets received?
	if( !(EventStatus & RXIN) )
	{
		return JT_STATUS_SUCCESS;
	}

	// Read the number of receive packets available
	jtCsrIn16( pDevInstance, RX_FIFO_PKT_CNT_REG, &PacketAvailCount );

	// Iterate over each available packet
	for( ; PacketAvailCount > 0; --PacketAvailCount, ++PacketDoneCount )
	{
		UINT32 PacketHeader1 = 0;
		UINT32 PacketHeader2 = 0;
		UINT32 PacketByteCount;
		UINT32 PacketFullDwordCount;
		JT_STATUS Result;
		struct sk_buff * pSkBuff;
		void * pPacketData;

    // Read the 8 bytes of packet headers
    jtCsrIn32( pDevInstance, RX_FIFO_READ_REG, &PacketHeader1 );
    jtCsrIn32( pDevInstance, RX_FIFO_READ_REG, &PacketHeader2 );

    // Extract length of packet and number of complete dwords
    PacketByteCount = (PacketHeader1 & PKT_HDR_LENGTH_MASK);
    PacketFullDwordCount = PacketByteCount / 4;
 
    // Validate the length
    Result = jtRxValidatePacketLength( pDevInstance, PacketByteCount );
    if( Result != JT_STATUS_SUCCESS )
    {
			// Discard this packet
			jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | RXFISKPK );
			continue;
    }

		// Check for errors in the packet
		Result = jtRxValidatePacketIntegrity( 
				pDevInstance,
				PacketHeader1,
				PacketHeader2 
			);
    if( Result != JT_STATUS_SUCCESS )
    {
			// Discard this packet
			jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | RXFISKPK );
			continue;
    }
    
		// Allocate buffer for packet
		pSkBuff = dev_alloc_skb( PacketByteCount + 2 );
		if( pSkBuff == NULL ) 
		{
			// Drop this packet
			jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | RXFISKPK );
			pDevInstance->Statistics.rx_dropped++;
			printk(
					KERN_NOTICE "%s: Out of memory. Dropping packet.\n",
					pDev->name
				);
	 
			// Exit without trying to receive any more packets
			return JT_STATUS_RESOURCES;
    }

		// Align the IP header on a  16 byte boundary
		skb_reserve(pSkBuff, 2);

		// Read each dword of packet data
		pPacketData = skb_put( pSkBuff, PacketByteCount );
		jtCsrIn32ToBuffer( 
				pDevInstance,
				RX_FIFO_READ_REG,
				pPacketData, 
				PacketFullDwordCount 
			);
    pPacketData += PacketFullDwordCount * sizeof(UINT32);
	      
		// Receive remaining halfword, if any
		if( (PacketByteCount & 2) == 2 )
		{
			jtCsrIn16( pDevInstance, RX_FIFO_READ_REG, (UINT16 *) pPacketData );
			pPacketData += sizeof(UINT16);   
    }
      
    // Receive remaining byte, if any
    if( (PacketByteCount & 1) == 1 )
    {
			jtCsrIn8( pDevInstance, RX_FIFO_READ_REG, (UINT8 *) pPacketData );
			pPacketData += sizeof(UINT8);   
    }

		// Give packet to next layer
		jtRxHandOffPacket( pDevInstance, pSkBuff, PacketHeader2 );
		pSkBuff = NULL;

		// Notify controller to advance to next packet
		jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | RXFISKPK );
	}

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:   jtRxStop
//
// DESCRIPTION: Direct the controller to cease receiving packets and
//              to not raise interrupts for packets received.
//
//----------------------------------------------------------------------------

STATIC void jtRxStop( PDEV_INSTANCE pDevInstance )
{
	// Disable packet reception on the controller
	jtCsrOut32( pDevInstance, MODE_REG_1, RXEN );

	// Disable interrupts for any receive events
	jtCsrOut32( pDevInstance, INTR_MASK_REG, RXCMEMMS | RXFIWMMS | RXMS | RXPDMS );
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPDCFlush
//
// DESCRIPTION: Flush all PDCs currently in progress.
//              jtRxStop must have been called before this function.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxPDCFlush( PDEV_INSTANCE pDevInstance )
{
	// Drain the InProgress queue
	while( !jtQueueIsEmpty( &pDevInstance->RxPDxInProgressQueue ) )
	{
		void * pPDC = NULL;
		jtQueueDequeue( &pDevInstance->RxPDxInProgressQueue, &pPDC );
		jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDC );
	}

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPDLFlush
//
// DESCRIPTION: Flush all PDLs currently in progress.
//              jtRxStop must have been called before this function.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxPDLFlush( PDEV_INSTANCE pDevInstance )
{
	// Drain the InProgress queue
	while( !jtQueueIsEmpty( &pDevInstance->RxPDxInProgressQueue ) )
	{
		PDL * pPDL = NULL;
		UINT64 SkBuffVirtAddr;
		struct sk_buff * pSkBuff = NULL;

    // Get the PDL off the InProgress queue
    jtQueueDequeue( &pDevInstance->RxPDxInProgressQueue, (void**) &pPDL );

    // Get the sk_buff packet
    SkBuffVirtAddr = (((UINT64) pPDL->Fragment[1].FragmentAddressHigh) << 32)
		 | pPDL->Fragment[1].FragmentAddressLow;
    pSkBuff = (void *) (unsigned long) SkBuffVirtAddr;

#if defined(JT_DEBUG)
    pPDL->Fragment[1].FragmentAddressLow = 0;
    pPDL->Fragment[1].FragmentAddressHigh = 0;
#endif

    // Send PDL back to available queue
    jtQueueEnqueue( &pDevInstance->RxPDxAvailableQueue, pPDL );
	}
	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxDestroy
//
// DESCRIPTION: Deallocate all queues and buffers required for PDC
//              receive mode. 
//              jtRxStop must have been called before this function.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxPDCDestroy( PDEV_INSTANCE pDevInstance )
{
	JT_ASSERT( pDevInstance->RxIOMode == RX_IOMODE_PDC );

	// Interate over each used Rx PDC table entry.
	while( ! jtQueueIsEmpty( &pDevInstance->RxPDxAvailableQueue ) )
	{
		RX_PDC * pPDC;
		
    // Dequeue buffer on the Available queue
    jtQueueDequeue( &pDevInstance->RxPDxAvailableQueue, (void **) &pPDC );
		
		// Deallocate PDC buffer
		free_pages( (unsigned long) pPDC, pDevInstance->RxPDCOrder );
	}

	// Deallocate Rx queues
	jtQueueDestroy( &pDevInstance->RxPDxAvailableQueue );
	jtQueueDestroy( &pDevInstance->RxPDxInProgressQueue );

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtRxPDLDestroy
//
// DESCRIPTION: Deallocate all queues and buffers required for busmaster
//              receive modes. 
//              jtRxStop must have been called before this function.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtRxPDLDestroy( PDEV_INSTANCE pDevInstance )
{
	if( pDevInstance->pRxPDLBase != NULL )
	{
    // Deallocate PDL buffers block
    free_pages( (unsigned long) pDevInstance->pRxPDLBase, pDevInstance->RxPDLOrder );
	}

	// Deallocate Rx queues
	jtQueueDestroy( &pDevInstance->RxPDxAvailableQueue );
	jtQueueDestroy( &pDevInstance->RxPDxInProgressQueue );

	return JT_STATUS_SUCCESS;
}


/*****************************************************************************
 * TX Routines
 ****************************************************************************/

//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDxCreate
//
// DESCRIPTION: Allocate and initialize all queues and buffers required
//              for busmaster transmit modes.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtTxPDxCreate(PDEV_INSTANCE pDevInstance)
{
	JT_STATUS Result, Result1, Result2, Result3;

	JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER );

	// Initialize Tx queues
	Result1 = jtQueueCreate( 
			&pDevInstance->TxPDCAvailableQueue, 
			pDevInstance->TxPDCCount
		);
	Result2 = jtQueueCreate( 
			&pDevInstance->TxPDLAvailableQueue, 
			pDevInstance->TxPDLCount
		);
	Result3 = jtQueueCreate(
			&pDevInstance->TxPDxInProgressQueue,
			pDevInstance->TxPDCCount + pDevInstance->TxPDLCount
		);
	if( Result1 != JT_STATUS_SUCCESS 
		 || Result2 != JT_STATUS_SUCCESS
		 || Result3 != JT_STATUS_SUCCESS )
	{
		return JT_STATUS_RESOURCES;
	}

	// Create PDC buffers
	Result = jtTxPDCCreate( pDevInstance );
	if( Result != JT_STATUS_SUCCESS )
	{
		return Result;
	}
	
	// Create PDL buffers
	Result = jtTxPDLCreate( pDevInstance );
	if( Result != JT_STATUS_SUCCESS )
	{
		return Result;
	}
#if defined(PERFORMANCE_TX)
	atomic_set ( &pDevInstance->TxPDLPending, 0 );
#endif
	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDCCreate
//
// DESCRIPTION: Allocate and initialize all queues and buffers required
//              for PDC transmit.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtTxPDCCreate(PDEV_INSTANCE pDevInstance)
{
	struct device * pDev = pDevInstance->pDev;
	int MinPDCSize;
	int PDCIndex;

	JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER );

	// PDC must be at least large enough to hold one Tx PDC header
	// and one full-size packet.
	MinPDCSize = pDev->mtu + ETH_HLEN + sizeof(UINT32);
	jtSizeToOrder(
			MinPDCSize,
			&pDevInstance->TxPDCOrder,
			&pDevInstance->TxPDCLength
		);
	
	// Interate over each Tx PDC buffer to create.
	for( PDCIndex = 0; PDCIndex < pDevInstance->TxPDCCount; ++PDCIndex )
	{
		TX_PDC * pPDC;

		// Allocate PDC buffer
		pPDC = (TX_PDC *) GET_FREE_PAGES( GFP_KERNEL, pDevInstance->TxPDCOrder );
		//			pPDC = (TX_PDC *) kmalloc( pDevInstance->TxPDCLength, GFP_KERNEL );
		if( pPDC == NULL )
		{
			return JT_STATUS_RESOURCES;
		}

		// Put buffer on the Available queue
		jtQueueEnqueue( &pDevInstance->TxPDCAvailableQueue, pPDC );
	}

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDLCreate
//
// DESCRIPTION: Allocate and initialize all queues and buffers required
//              for PDL transmit.
//
//----------------------------------------------------------------------------

JT_STATUS jtTxPDLCreate( DEV_INSTANCE * pDevInstance )
{
	int MinPDLSize;
	int ActualPDLSize = 0;
	int PDLIndex;

	JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER );

	// Block must be at least large enough to hold all the PDLs
	// with cache-line alignment
	MinPDLSize = pDevInstance->TxPDLCount * pDevInstance->TxPDLAlignment;
	jtSizeToOrder(
			MinPDLSize,
			&pDevInstance->TxPDLOrder,
			&ActualPDLSize
		);
	
	// First allocate a big contiguous block
	pDevInstance->pTxPDLBase = (void *) GET_FREE_PAGES( GFP_KERNEL, pDevInstance->TxPDLOrder );
	if( pDevInstance->pTxPDLBase == NULL )
	{
		return JT_STATUS_RESOURCES;
	}
	
	// Carve the block into individual PDL buffers and put them on the queue
	for( PDLIndex = 0; PDLIndex < pDevInstance->TxPDLCount; ++PDLIndex )
	{
		// Each PDL buffer has room for exactly one fragment
		// plus 8 bytes free at the end
		void * pPDL = pDevInstance->pTxPDLBase + pDevInstance->TxPDLAlignment * PDLIndex;

		// Put buffer on the Available queue
		jtQueueEnqueue( &pDevInstance->TxPDLAvailableQueue, pPDL );
	}

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDxInitialize
//
// DESCRIPTION: Initialize all buffers required
//              for busmaster transmit modes.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtTxPDxInitialize(PDEV_INSTANCE pDevInstance)
{
	JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER );

	// Initialize PDC buffers
	(void) jtTxPDCInitialize( pDevInstance );

	// Initialize PDL buffers
	(void) jtTxPDLInitialize( pDevInstance );

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDCInitialize
//
// DESCRIPTION: Allocate and initialize all queues and buffers required
//              for PDC transmit.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtTxPDCInitialize(PDEV_INSTANCE pDevInstance)
{
	int PDCIndex;

	JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER );

	// Initialize Tx PDC Table Index
	pDevInstance->TxPDCNextTableIndex = 0;

	// Interate over each Tx PDC buffer to create.
	for( PDCIndex = 0; PDCIndex < pDevInstance->TxPDCCount; ++PDCIndex )
	{
		TX_PDC * pPDC;
		UINT64 BusAddr;

		jtCsrOut32( pDevInstance, TX_PDC_BUF_ADR_TBL_IX, PDCIndex );

		// Take PDC from the Available queue
		jtQueueDequeue( &pDevInstance->TxPDCAvailableQueue, (void **) &pPDC );

		// Register the buffer with the controller
		BusAddr = virt_to_bus( pPDC );

		// Order dependency! The high dword must be transferred first!
		jtCsrOut32( pDevInstance, TX_PDC_BUF_ADR_HI, (UINT32) (BusAddr >> 32) );
		jtCsrOut32( pDevInstance, TX_PDC_BUF_ADR_LO, (UINT32) (BusAddr) );

		// Put PDC back on tail of Available queue
		jtQueueEnqueue( &pDevInstance->TxPDCAvailableQueue, pPDC );
	}

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDLInitialize
//
// DESCRIPTION: Allocate and initialize all queues and buffers required
//              for PDL transmit.
//
//----------------------------------------------------------------------------

JT_STATUS jtTxPDLInitialize( DEV_INSTANCE * pDevInstance )
{
	JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER );

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxStart
//
// DESCRIPTION: Direct the controller to begin transmitting packets and
//              to raise interrupts for PDC/PDL packets transferred to
//              the controller.
//
//----------------------------------------------------------------------------

STATIC void jtTxStart(PDEV_INSTANCE pDevInstance)
{
	jtCsrOut32( pDevInstance, TX_PDL_ADDR_REG_HI, 0 );

	// Enable interrupts for PDC or PDL DMA complete
	jtCsrOut32( pDevInstance, INTR_MASK_REG, SERECL0 | TXDMDNMS | TMEXMS );

#if defined(PERFORMANCE_TX)

#if defined(PERFORMANCE_LXT1002)
	if ( pDevInstance->ControllerType == LXT1001 )
	{
#endif
		// Prepare the timer and write the time lapse to the chip   
		pDevInstance->TxPDLTimerOn = 0;
		jtCsrOut32( pDevInstance, INTR_PERIOD_REG, 24000 );

#if defined(PERFORMANCE_LXT1002)
	}
	else
	{
		// Enable the usage of the idle interrupt and write the constant 
		jtCsrOut32( pDevInstance, INTR_MASK_REG, SERECL1 | LXT1002_TXDMIDMS );
		jtCsrOut32( pDevInstance, LXT1002_TX_DMA_ID_REG, 0 );
	}
#endif

#endif

	// Enable packet transmission on the controller
	jtCsrOut32 ( pDevInstance, MODE_REG_1, SERECL1 | TXEN );
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDxTransmit
//
// DESCRIPTION: Give the controller a packet to transmit,
//              using the PDC/PDL mechanism.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  pSkBuff       The packet to transmit.
//
//----------------------------------------------------------------------------
STATIC int jtTxPDxTransmit(
		PDEV_INSTANCE pDevInstance, 
		struct sk_buff * pSkBuff 
	)
{
	void * pPartialPDC;
	UINT32 PartialPDCLength;
	struct sk_buff * pCurrentSkBuff, * pNextSkBuff = NULL;
	int Done;
#if defined(PERFORMANCE_TX)	&& defined(SERIALIZE_TX)
	unsigned long Flags;
#endif

#if ( (LINUX_VERSION_CODE < 0x20100) && MULTIPLE_PACKETS)
	struct device * pDev = pDevInstance->pDev;

	struct sk_buff_head * pSkBuffQueue = pDev->buffs;
#endif

	// Hold partially filled PDC
	pPartialPDC = NULL;
	PartialPDCLength = 0;

	// While there remain packets in the device queue
	pCurrentSkBuff = pSkBuff;
	Done = FALSE;

	while( (!Done) && (!test_bit(0, (void*) &pDevInstance->TxFull)) )
	{
#if ( (LINUX_VERSION_CODE < 0x20100) && MULTIPLE_PACKETS)
		// Want to stuff multiple packets into a single PDC, if possible
		// Scan the device packet queues until we find a packet
		while( skb_queue_empty( pSkBuffQueue ) )
		{
			// Look in the next queue
			pSkBuffQueue++;

			// If there are no more queues
			if( pSkBuffQueue == &pDev->buffs[DEV_NUMBUFFS] )
			{
					// No more packets
				Done = TRUE;
				break;
			}
    }

		// Get the next packet to process
		if( ! Done )
		{
			pNextSkBuff = skb_dequeue( pSkBuffQueue );
			JT_DPRINTK("Taking packet from device queue.\n");
    }
#else
    Done = TRUE;
#endif

		// If a partially filled PDC exists and we're done with it
		if( (pPartialPDC != NULL) &&
		( Done || 
		(pCurrentSkBuff->len >= pDevInstance->TxPDxThreshold) ||
			(pCurrentSkBuff->len > pDevInstance->TxPDCLength - PartialPDCLength - 8) ) )
		{
			// Send it
			jtTxPDCSend( pDevInstance, pPartialPDC, PartialPDCLength );
			pPartialPDC = NULL;
			PartialPDCLength = 0;
		}

    // If this packet is small enough to transfer using PDC mode
    if( pCurrentSkBuff->len < pDevInstance->TxPDxThreshold )
    {
			// If no partially filled PDC buffer exists
			if( pPartialPDC == NULL )
			{
				// Start a new one
				jtTxPDCGetNextAvailable( pDevInstance, &pPartialPDC );
				PartialPDCLength = 0;
			}

			// Append packet into current PDC buffer
			jtTxPDCAppendPacket( 
					pDevInstance, 
					pPartialPDC, 
					&PartialPDCLength, 
					pCurrentSkBuff 
				);
		}
    else
    {
			// Transmit packet using PDL
			jtTxPDLTransmit( pDevInstance, pCurrentSkBuff );
		}

		// Done with this packet
		pCurrentSkBuff = pNextSkBuff;

	} // end while

	// If a partially filled PDC exists
	if( (pPartialPDC != NULL) )
	{
		// Send it			 
		jtTxPDCSend( pDevInstance, pPartialPDC, PartialPDCLength );
		pPartialPDC = NULL;
		PartialPDCLength = 0;
	}

	JT_ASSERT( pPartialPDC == NULL );

#if defined(PERFORMANCE_TX)

#if defined(PERFORMANCE_LXT1002)
	if ( pDevInstance->ControllerType == LXT1001 )
	{
#endif

		// Look for outstanding an existing timer
#if defined(SERIALIZE_TX)
		spin_lock_irqsave(&pDevInstance->DriverLock, Flags);
#endif

		if( !test_bit(0, (void*) &pDevInstance->TxPDLTimerOn) )
		{
			// Is there outstanding stuff
			if( atomic_read( &pDevInstance->TxPDLPending ) || test_bit(0, (void*) &pDevInstance->TxFull) )
			{
				// We need to fire a timer
				jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | DLINRQ );
				set_bit( 0, (void*) &pDevInstance->TxPDLTimerOn );
			}
		}

#if defined(SERIALIZE_TX)
		spin_unlock_irqrestore(&pDevInstance->DriverLock, Flags);
#endif

#if defined(PERFORMANCE_LXT1002)
	}
	else
	{
		UINT8 TransmitDoneCount;
		
		// Protect from the ISR
		spin_lock_irqsave(&pDevInstance->DriverLock, Flags);
			
		// Get the number of done commands
		TransmitDoneCount = *(PUINT8)pDevInstance->StatusBuffer - pDevInstance->TxOldStatusCount;

		// Cleanup buffers used by transmit
		jtTxPDxDone( pDevInstance, TransmitDoneCount );
		
		// Release protection
		spin_unlock_irqrestore(&pDevInstance->DriverLock, Flags);
	}
#endif

#endif

	return 0;
}
      

//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDGetNextAvailable
//
// DESCRIPTION: Take the next available Tx PDC buffer from the queue,
//              and check for queue exhaustion.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
// RETURNS:
//  ppPDC         The buffer taken off the available queue.
//
//----------------------------------------------------------------------------

STATIC void jtTxPDCGetNextAvailable( DEV_INSTANCE * pDevInstance, void ** ppPDC )
{
	// Take a PDC buffer from the available queue
	jtQueueDequeue( &pDevInstance->TxPDCAvailableQueue, ppPDC );

	// If the available queue is now exhausted
	if( jtQueueIsEmpty( &pDevInstance->TxPDCAvailableQueue ) )
	{
		// Signal that the transmitter is busy
		set_bit( 0, (void *) &pDevInstance->TxFull );
		JT_DPRINTK("Out of PDCs\n");
	}
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDCAppendPacket
//
// DESCRIPTION: Append a packet to a PDC buffer. The buffer is assumed to 
//              be large enough to fit in the PDC.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  pPDC          The buffer to append to.
//  PDCLength     Number of bytes currently used in the PDC buffer.
//  pSkBuff       The packet to append
// RETURNS:
//  PDCLength     Number of bytes used, including newly appended packet.
//
//----------------------------------------------------------------------------

STATIC void jtTxPDCAppendPacket(
		DEV_INSTANCE * pDevInstance,
		void * pPDC, 
		UINT32 * pPDCLength,
		struct sk_buff * pSkBuff
	)
{
	TX_PDC * pPDCCurrPacket;

	// Round packet length up to next 8 byte boundary
	*pPDCLength = (*pPDCLength + 7) & ~7U;

	// Create packet header in PDC buffer
	pPDCCurrPacket = (TX_PDC *)(pPDC + *pPDCLength);
	pPDCCurrPacket->Header = pSkBuff->len | 
		( 0x20000 ) | // Make this a #define
		pDevInstance->TxPacketHeader ;
	*pPDCLength += sizeof(UINT32);
   
	// Copy packet into current PDC buffer
	memcpy( pPDCCurrPacket->Data, (const void *) pSkBuff->data, pSkBuff->len );
	*pPDCLength += pSkBuff->len;
   
	// Increment stats
#if LINUX_VERSION_CODE >= 0x20125
	pDevInstance->Statistics.tx_bytes += pSkBuff->len;
#endif

	// Release packet
	DEV_KFREE_SKB( pSkBuff, FREE_WRITE );
}
      

//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDCSend
//
// DESCRIPTION: Signal the controller to transmit a PDC buffer.
//              The PDC buffer is assumed to be fully constructed, with
//              one or more packets.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  pPDC          The buffer to transmit.
//  PDCLength     Number of bytes used in the PDC buffer.
//
//----------------------------------------------------------------------------

STATIC void jtTxPDCSend(
		DEV_INSTANCE * pDevInstance,
		TX_PDC * pPDC, 
		UINT32 PDCLength
	)
{
	UINT32 TxPDCCommand;

	JT_ASSERT( pPDC != NULL );
	JT_ASSERT( PDCLength > 0 );

	// Initiate transmit of existing PDC
	// Put PDC buffer on the In Progress queue
	jtQueueEnqueue( &pDevInstance->TxPDxInProgressQueue, pPDC );
   
	// Decrement count of PDx commands available
	jtTxDecrementFreeCount( pDevInstance );

	// Signal controller to begin transmit
#if defined(STANDARD_TX)
	TxPDCCommand = PDCLength 
		| ( pDevInstance->TxPDCNextTableIndex << TX_PDC_REG_BFID_SHIFT )
		| XFDNINRQ ;
#else
	TxPDCCommand = PDCLength 
		| ( pDevInstance->TxPDCNextTableIndex << TX_PDC_REG_BFID_SHIFT );
#endif

//#if !defined(WORKAROUND_WRITE_COMBINE)
	jtCsrOut32( pDevInstance, TX_PDC_REG, TxPDCCommand );
//#else
//	outl( TxPDCCommand, pDevInstance->pDev->base_addr + TX_PDC_REG );
//#endif

	// Advance to next table entry
	pDevInstance->TxPDCNextTableIndex = 
		(pDevInstance->TxPDCNextTableIndex + 1) % pDevInstance->TxPDCCount;

}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDLTransmit
//
// DESCRIPTION: Give the controller a packet to transmit,
//              using the PDL mechanism.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  pSkBuff     The packet to transmit.
//
//----------------------------------------------------------------------------

STATIC void jtTxPDLTransmit( 
		DEV_INSTANCE * pDevInstance, 
		struct sk_buff * pSkBuff 
	)
{
	PDL * pPDL = NULL;
	void * pPDx = NULL;
	UINT64 SkBuffBusAddr;
	UINT64 SkBuffVirtAddr;
	UINT64 PDLBusAddr;

	// Take PDL buffer off the Available queue
	jtQueueDequeue( &pDevInstance->TxPDLAvailableQueue, (void**) &pPDL );
  
	// If PDL available queue is now exhausted
	if( jtQueueIsEmpty( &pDevInstance->TxPDLAvailableQueue ) )
	{
		// Signal that the transmitter is busy
		JT_DPRINTK("Out of PDLs\n");
		set_bit( 0, (void *) &pDevInstance->TxFull );
	}
      
	// Create packet header in PDL buffer
#if defined(STANDARD_TX)
	pPDL->Header = 
		pSkBuff->len
		| ( 1 << TX_PDL_FGCN_SHIFT )   // Fragment count
		| pDevInstance->TxPacketHeader 
		| DMDNINRQ;
#elif defined(PERFORMANCE_TX)
	pPDL->Header = 
		pSkBuff->len
		| ( 1 << TX_PDL_FGCN_SHIFT )   // Fragment count
		| pDevInstance->TxPacketHeader; 
#endif
	      
	// Point PDL fragment 1 to the skbuff
	SkBuffBusAddr = virt_to_bus( pSkBuff->data );
	pPDL->Fragment[0].FragmentAddressLow =  (UINT32) (SkBuffBusAddr);
	pPDL->Fragment[0].FragmentAddressHigh = (UINT32) (SkBuffBusAddr >> 32);
	pPDL->Fragment[0].FragmentLength = pSkBuff->len;

	// Save away the sk_buff address
	SkBuffVirtAddr = (unsigned long) pSkBuff;
	pPDL->Fragment[1].FragmentAddressLow = (UINT32) (SkBuffVirtAddr);
	pPDL->Fragment[1].FragmentAddressHigh = (UINT32) (SkBuffVirtAddr >> 32);

	// Mark as PDL type and put buffer on the In Progress queue
	pPDx = (void *) ((unsigned long) pPDL | PDL_FLAG);
	jtQueueEnqueue( &pDevInstance->TxPDxInProgressQueue, pPDx );

	// Decrement count of PDx commands available
	jtTxDecrementFreeCount( pDevInstance );

	// Signal controller to begin transmit
	PDLBusAddr = virt_to_bus( pPDL );

	// Order dependency! The high dword must be transferred first!
	if ( (UINT32)(PDLBusAddr >> 32) )
		jtCsrOut32( pDevInstance, TX_PDL_ADDR_REG_HI, (UINT32) (PDLBusAddr >> 32) );
	jtCsrOut32( pDevInstance, TX_PDL_ADDR_REG_LO, (UINT32) (PDLBusAddr) );

#if defined(PERFORMANCE_TX)
#if defined(PERFORMANCE_LXT1002)
	if ( pDevInstance->ControllerType == LXT1001 )
	{
#endif
		atomic_inc ( &pDevInstance->TxPDLPending );
#if defined(PERFORMANCE_LXT1002)
	}
#endif
#endif

	// Update statistics
#if LINUX_VERSION_CODE >= 0x20125
	pDevInstance->Statistics.tx_bytes += pSkBuff->len;
#endif
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPIOTransmit
//
// DESCRIPTION: Give the controller a packet to transmit,
//              using the PIO mechanism.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  pSkBuff       The packet to transmit.
//
//----------------------------------------------------------------------------
STATIC int jtTxPIOTransmit(
		PDEV_INSTANCE pDevInstance, 
		struct sk_buff * pSkBuff 
	)
{
	struct device * pDev = pDevInstance->pDev;

	// Number of bytes of data, not including Tx header
	UINT32 PacketByteCount = pSkBuff->len;

	// Number of 4 byte dwords, rounded up (not including one for Tx header)
	UINT32 PacketDwordCount = (PacketByteCount - 1) / 4 + 1;

	// Number of complete 4 byte dwords in packet
	UINT32 PacketFullDwordCount = PacketByteCount / 4;

	// Transmit header for the controller
	UINT32 PacketHeader = pDevInstance->TxPacketHeader | PacketByteCount;

	// Moving pointer to current data element
	void * pPacketData = pSkBuff->data;

	// Validate packet length
	if( PacketByteCount > (pDev->mtu + ETH_HLEN) ) 
	{
		// The packet is too large - discard
		JT_DPRINTK( "Discarding oversize packet.\n");
  
		// Release buffer
		DEV_KFREE_SKB( pSkBuff, FREE_WRITE );

		// Update statistics
		pDevInstance->Statistics.tx_errors++;

		return 0;
	}

	// Check whether there is room in the Transmit FIFO for this packet
	// First compare against the saved value
	if( pDevInstance->TxFifoDwordFreeCount < (PacketDwordCount + 1) )
	{
		// Update our saved value and check again
		jtCsrIn16( 
				pDevInstance, 
				TX_FIFO_DWORDS_FREE_REG, 
				&(pDevInstance->TxFifoDwordFreeCount)
			);
      
    // If there is still not enough space available
    if( pDevInstance->TxFifoDwordFreeCount < (PacketDwordCount + 1) )
    {
			// Drop the packet
			printk(
					KERN_NOTICE "%s: Transmit buffer is full. Dropping packet.\n",
					pDev->name
				);

			// Release buffer
			DEV_KFREE_SKB( pSkBuff, FREE_WRITE );

			// Update statistics
			pDevInstance->Statistics.tx_dropped++;

			return 0;
    }
	}

	// Update saved value of Fifo usage
	pDevInstance->TxFifoDwordFreeCount -= (PacketDwordCount + 1);

	// Transmit the header
	jtCsrOut32( pDevInstance, TX_FIFO_WRITE_REG, PacketHeader );

	// Transmit the packet itself
	// Write each dword of packet data
	pPacketData = pSkBuff->data;
	jtCsrOut32FromBuffer( 
			pDevInstance,
			TX_FIFO_WRITE_REG,
			pPacketData, 
			PacketFullDwordCount 
		);
	pPacketData += PacketFullDwordCount * sizeof(UINT32);
	
	// Transmit remaining halfword, if any
	if( (PacketByteCount & 2) == 2 )
	{
		jtCsrOut16( pDevInstance, TX_FIFO_WRITE_REG, * (UINT16 *) pPacketData );
		pPacketData += sizeof(UINT16);   
	}

	// Transmit remaining byte, if any
	if( (PacketByteCount & 1) == 1 )
	{
		jtCsrOut16( pDevInstance, TX_FIFO_WRITE_REG, * (UINT8 *) pPacketData );
		pPacketData += sizeof(UINT8);   
	}

	// Notify the controller to begin transmit
	jtCsrOut32( pDevInstance, COMMAND_REG, SERECL0 | SLMDTXCM );

	// Update statistics
#if LINUX_VERSION_CODE >= 0x20125
	pDevInstance->Statistics.tx_bytes += pSkBuff->len;
#endif

	// Release buffer
	DEV_KFREE_SKB( pSkBuff, FREE_WRITE );

	return 0;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDxDone
//
// DESCRIPTION: Reclaim buffers used by packets that have completed transmit.
//
// PARAMETERS:
//  pDevInstance  The pertinent controller instance.
//  TransmitDoneCount  The number of PDC/PDL transmits that have completed.
//
//----------------------------------------------------------------------------
STATIC JT_STATUS jtTxPDxDone(
		PDEV_INSTANCE pDevInstance,
		UINT32 TransmitDoneCount 
	)
{
	struct device * pDev = pDevInstance->pDev;
	int TransmitIndex;

	// Iterate over each completed transmit
	for( TransmitIndex = 0; TransmitIndex < TransmitDoneCount; ++TransmitIndex )
	{
		void * pPDx = NULL;

		// If no buffers are on the In Progress queue
		if( jtQueueIsEmpty( &pDevInstance->TxPDxInProgressQueue ) )
		{
			// Serious error in our bookeeping
			printk(
					KERN_ERR "%s: Controller indicates nonexistent transmits.\n",
					pDev->name
				);
			return JT_STATUS_FAILURE;
		}

    // Take the transmit buffer off the In Progress queue
    jtQueueDequeue( &pDevInstance->TxPDxInProgressQueue, (void **) &pPDx );
    
    // Use least significant bit to determine PDC or PDL buffer
    if( ((unsigned long) pPDx) & PDL_FLAG )
    {
			// The buffer is a PDL
			PDL * pPDL  = (void *) ((unsigned long) pPDx & ~PDL_FLAG);
			UINT64 SkBuffVirtAddr = 0;
			struct sk_buff * pSkBuff = NULL;

			JT_ASSERT( ((pPDL->Header & PDL_FGCN_MASK) >> TX_PDL_FGCN_SHIFT) == 1 );

			// Release the skbuff holding the packet data
			SkBuffVirtAddr = 
				(((UINT64) pPDL->Fragment[1].FragmentAddressHigh) << 32)
				| pPDL->Fragment[1].FragmentAddressLow;
			pSkBuff = (void *) (unsigned long) SkBuffVirtAddr;
			DEV_KFREE_SKB( pSkBuff, FREE_WRITE );
#if defined(JT_DEBUG)
			pPDL->Fragment[1].FragmentAddressLow = 0;
			pPDL->Fragment[1].FragmentAddressHigh = 0;
#endif
			
			// Put the buffer back on the available queue
			jtQueueEnqueue( &pDevInstance->TxPDLAvailableQueue, pPDL );

#if defined(PERFORMANCE_TX)
#if defined(PERFORMANCE_LXT1002)
			if ( pDevInstance->ControllerType == LXT1001 )
			{
#endif
				atomic_dec ( &pDevInstance->TxPDLPending );
#if defined(PERFORMANCE_LXT1002)
			}
#endif
#endif
		}
		else  // PDC
		{
			JT_ASSERT( ((((TX_PDC *)pPDx)->Header & PDL_FGCN_MASK) >> TX_PDL_FGCN_SHIFT) == 2 );
			// Put the buffer back on the available queue
			jtQueueEnqueue( &pDevInstance->TxPDCAvailableQueue, pPDx );
		}
	}

	// Decide whether the transmitter is still busy
	if( test_bit(0, (void *) &pDevInstance->TxFull) )
	{
		// Have a PDC available
		BOOLEAN PDCNotFull = (!jtQueueIsEmpty( &pDevInstance->TxPDCAvailableQueue ));

		// Likewise, have a PDL available
		BOOLEAN PDLNotFull = (!jtQueueIsEmpty( &pDevInstance->TxPDLAvailableQueue ));

		if( PDCNotFull && PDLNotFull )
		{
			// Re-read the number of commands that the chip can accept
			// If the chip is ready for more commands
			if( jtTxGetFreeCount( pDevInstance ) != 0 )
			{		
				// Mark the controller as not busy
				clear_bit(0, (void *) &pDevInstance->TxFull);
				clear_bit(0, (void *) &pDev->tbusy );
				mark_bh(NET_BH);
			}
    }
	}

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxStop
//
// DESCRIPTION: Direct the controller to cease transmitting packets.
//
//----------------------------------------------------------------------------
STATIC void jtTxStop(PDEV_INSTANCE pDevInstance)
{
	// Disable packet reception on the controller
	jtCsrOut32( pDevInstance, MODE_REG_1, TXEN );

#if defined(PERFORMANCE_TX)

#if defined(PERFORMANCE_LXT1002)
	if ( pDevInstance->ControllerType == LXT1001 )
	{
#endif
		// Disable Timer
		pDevInstance->TxPDLTimerOn = 0;
		jtCsrOut32( pDevInstance, INTR_PERIOD_REG, 0 );
		jtCsrOut32( pDevInstance, COMMAND_REG, DLINRQ );
#if defined(PERFORMANCE_LXT1002)
	}
	else
	{
		// Disable the usage of the idle interrupt 
		jtCsrOut32( pDevInstance, INTR_MASK_REG, LXT1002_TXDMIDMS );
	}
#endif

#endif
	
	// Disable interrupts for any transmit events
	jtCsrOut32( pDevInstance, INTR_MASK_REG, TXDMDNMS | TXCMEMMS | TXFIWMMS | TMEXMS );
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDxFlush
//
// DESCRIPTION: Flush all PDCs and PDLs in progress.
//              jtTxStop must have been called prior to this function.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtTxPDxFlush(PDEV_INSTANCE pDevInstance)
{
	JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER );

	// Drain the InProgress queue
	while( !jtQueueIsEmpty( &pDevInstance->TxPDxInProgressQueue ) )
	{
		jtTxPDxDone( pDevInstance, 1 );
	}

#if defined(PERFORMANCE_TX)
	atomic_set ( &pDevInstance->TxPDLPending, 0 );
#endif

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtTxPDxDestroy
//
// DESCRIPTION: Deallocate all queues and buffers required
//              for busmaster transmit modes.
//              jtTxStop must have been called prior to this function.
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtTxPDxDestroy(PDEV_INSTANCE pDevInstance)
{
	JT_ASSERT( pDevInstance->TxIOMode == TX_IOMODE_BUSMASTER );

	// Interate over each allocated Tx PDC buffer.
	while( ! jtQueueIsEmpty( &pDevInstance->TxPDCAvailableQueue ) )
	{
		void * pPDC = NULL;

		// Dequeue buffer on the Available queue
		jtQueueDequeue( &pDevInstance->TxPDCAvailableQueue, (void **) &pPDC );
		
		// Deallocate PDC buffer
		free_pages( (unsigned long) pPDC, pDevInstance->TxPDCOrder );
	}

	// Deallocate Tx PDLs
	if( pDevInstance->pTxPDLBase != NULL )
	{
    // Deallocate PDL buffer block
    free_pages( (unsigned long) pDevInstance->pTxPDLBase, pDevInstance->TxPDLOrder ); 
	}
	
	// Deallocate Tx queues
	jtQueueDestroy( &pDevInstance->TxPDCAvailableQueue );
	jtQueueDestroy( &pDevInstance->TxPDLAvailableQueue );
	jtQueueDestroy( &pDevInstance->TxPDxInProgressQueue );

	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtStatusBufferCreate
//
// DESCRIPTION: Creates and processes the status buffer for Matterhorn
//
//----------------------------------------------------------------------------

STATIC JT_STATUS jtStatusBufferCreate(PDEV_INSTANCE pDevInstance)
{
	UINT64 BusAddress;

	// Allocate the memory
	pDevInstance->StatusBuffer = (void *) kmalloc( 128, GFP_KERNEL );

	if( !pDevInstance->StatusBuffer )
		return JT_STATUS_FAILURE;

	memset (pDevInstance->StatusBuffer, 0, 128);

	// Convert it to a physical address
	BusAddress = virt_to_bus( pDevInstance->StatusBuffer );

	// Give it to the chip	
	jtCsrOut32( pDevInstance, LXT1002_ST_BUF_ADD_MSD, (UINT32) (BusAddress >> 32) );
	jtCsrOut32( pDevInstance, LXT1002_ST_BUF_ADD_LSD, (UINT32) (BusAddress) );

	jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 3), (SERECL3 | LXT1002_TXDMSTEN) >> 24 );

	return JT_STATUS_SUCCESS;
}

//----------------------------------------------------------------------------
//
// FUNCTION:    jtStatusBufferDestroy
//
// DESCRIPTION: destroys the status buffer for Matterhorn
//
//----------------------------------------------------------------------------

STATIC void jtStatusBufferDestroy(PDEV_INSTANCE pDevInstance)
{
	jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 3), LXT1002_TXDMSTEN >> 24 );

	kfree( pDevInstance->StatusBuffer  );
}

//----------------------------------------------------------------------------
//
// FUNCTION:    jtStatusBufferReset
//
// DESCRIPTION: resets the status buffer for Matterhorn
//
//----------------------------------------------------------------------------

STATIC void jtStatusBufferReset(PDEV_INSTANCE pDevInstance)
{
	jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 3), LXT1002_TXDMSTEN >> 24 );

	memset (pDevInstance->StatusBuffer, 0, 128);

	jtCsrOut8 ( pDevInstance, (MODE_REG_2 + 3), (SERECL3 | LXT1002_TXDMSTEN) >> 24 );
}

/*****************************************************************************
 * Misc Routines
 ****************************************************************************/

#if 0
//----------------------------------------------------------------------------
//
// FUNCTION:    jtPrintMacAddress
//
// DESCRIPTION: Print out the colon-separated octets of a mac address.
//
// PARAMETERS:
//   Message     A string to print before the address.
//   pAddress    The Mac address to print.
//
//----------------------------------------------------------------------------
STATIC void jtPrintMacAddress(
		const char * Message, 
		const UINT8 * pAddress 
	)
{
	printk(
		KERN_INFO "%s %02x:%02x:%02x:%02x:%02x:%02x\n",
		Message,
		(int) pAddress[0],
		(int) pAddress[1],
		(int) pAddress[2],
		(int) pAddress[3],
		(int) pAddress[4],
		(int) pAddress[5]
	);
}
#endif

//----------------------------------------------------------------------------
//
// FUNCTION:    jtNop
//
// DESCRIPTION: Do absolutely nothing.
//
//----------------------------------------------------------------------------
STATIC JT_STATUS jtNop( DEV_INSTANCE * pDevInstance )
{
	return JT_STATUS_SUCCESS;
}


//----------------------------------------------------------------------------
//
// FUNCTION:    jtSizeToOrder
//
// DESCRIPTION: Compute the binary logarithm of an (byte count / PAGE_SIZE).
//              We want to allocate 'ByteCount' bytes, but the function
//              __get_free_pages requires an 'Order' parameter, where
//              2**Order is the number of pages that will be allocated
// 
// PARAMETERS:
//  ByteCount   Just that
//
// RETURNS:
//  pOrder           The smallest X such that (2**X) * PAGE_SIZE >= ByteCount
//  pActualByteCount The number (2**Order) * PAGE_SIZE
//
//----------------------------------------------------------------------------
STATIC void jtSizeToOrder(
		UINT32 ByteCount,
		UINT32 * pOrder,
		UINT32 * pActualByteCount
	)
{
	// Find actual number of pages
	*pOrder = 0;
	*pActualByteCount = PAGE_SIZE;
	while( *pActualByteCount < ByteCount  )
	{
		(*pActualByteCount) *= 2;
		(*pOrder)++;
	}
}

//------------------------------------------------------------------------------
// First, the polynomial itself and its table of feedback terms.  The  
// polynomial is                                                       
// X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 
// Note that we take it "backwards" and put the highest-order term in  
// the lowest-order bit.  The X^32 term is "implied"; the LSB is the   
// X^31 term, etc.  The X^0 term (usually shown as "+1") results in    
// the MSB being 1.                                                    
//								       
// Note that the usual hardware shift register implementation, which   
// is what we're using (we're merely optimizing it by doing eight-bit  
// chunks at a time) shifts bits into the lowest-order term.  In our   
// implementation, that means shifting towards the right.  Why do we   
// do it this way?  Because the calculated CRC must be transmitted in  
// order from highest-order term to lowest-order term.  UARTs transmit 
// characters in order from LSB to MSB.  By storing the CRC this way,  
// we hand it to the UART in the order low-byte to high-byte; the UART 
// sends each low-bit to hight-bit; and the result is transmission bit 
// by bit from highest- to lowest-order term without requiring any bit 
// shuffling on our part.  Reception works similarly.                  
//								       
// The feedback terms table consists of 256, 32-bit entries.  Notes:   
//                                                                     
//  1. The table can be generated at runtime if desired; code to do so 
//     is shown later.  It might not be obvious, but the feedback      
//     terms simply represent the results of eight shift/xor opera-    
//     tions for all combinations of data and CRC register values.     
//                                                                     
//  2. The CRC accumulation logic is the same for all CRC polynomials, 
//     be they sixteen or thirty-two bits wide.  You simply choose the 
//     appropriate table.  Alternatively, because the table can be     
//     generated at runtime, you can start by generating the table for 
//     the polynomial in question and use exactly the same "updcrc",   
//     if your application needn't simultaneously handle two CRC       
//     polynomials.  (Note, however, that XMODEM is strange.)          
//                                                                     
//  3. For 16-bit CRCs, the table entries need be only 16 bits wide;   
//     of course, 32-bit entries work OK if the high 16 bits are zero. 
//                                                                     
//  4. The values must be right-shifted by eight bits by the "updcrc"  
//     logic; the shift must be unsigned (bring in zeroes).  On some   
//     hardware you could probably optimize the shift in assembler by  
//     using byte-swap instructions.                                   
//------------------------------------------------------------------------------

static unsigned long crc_32_tab[] = { // CRC polynomial 0xedb88320 
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};

#define UPDC32(octet,crc) (crc_32_tab[((crc) ^ ((unsigned char)octet)) & 0xff] ^ ((crc) >> 8))

UINT32 crc32buf(char *buf, long len)
{
    register unsigned long oldcrc32;
    oldcrc32 = 0xFFFFFFFF;
    for ( ; len; --len, ++buf) {
        oldcrc32 = UPDC32(*buf, oldcrc32);
    }
    return ~oldcrc32;
}

/*
 * Local Variables:
 *  compile-command: "gcc -DMODULE -D__KERNEL__ -O2 -Wall -I../../include -c jt1lin.c"
 * End:
 */
