/******************************************************************************/
/*                                                                            */
/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 - 2003 Broadcom  */
/* Corporation.                                                               */
/* All rights reserved.                                                       */
/*                                                                            */
/* This program is free software; you can redistribute it and/or modify       */
/* it under the terms of the GNU General Public License as published by       */
/* the Free Software Foundation, located in the file LICENSE.                 */
/*                                                                            */
/******************************************************************************/


char bcm5700_driver[] = "bcm5700";
char bcm5700_version[] = "7.1.22";
char bcm5700_date[] = "(01/07/04)";

#define B57UM
#include "mm.h"

/* A few user-configurable values. */

#define MAX_UNITS 16
/* Used to pass the full-duplex flag, etc. */
static int line_speed[MAX_UNITS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
static int auto_speed[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
static int full_duplex[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
static int rx_flow_control[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
static int tx_flow_control[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
static int auto_flow_control[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
static int mtu[MAX_UNITS] = {1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500,1500};	/* Jumbo MTU for interfaces. */
#endif
static int tx_checksum[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
static int rx_checksum[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
static int scatter_gather[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};

#define TX_DESC_CNT DEFAULT_TX_PACKET_DESC_COUNT
static unsigned int tx_pkt_desc_cnt[MAX_UNITS] =
	{TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,
	TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,
	TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,TX_DESC_CNT,
	TX_DESC_CNT};

#define RX_DESC_CNT DEFAULT_STD_RCV_DESC_COUNT
static unsigned int rx_std_desc_cnt[MAX_UNITS] =
	{RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,
	RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,
	RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,RX_DESC_CNT,
	RX_DESC_CNT };

#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
#define JBO_DESC_CNT DEFAULT_JUMBO_RCV_DESC_COUNT
static unsigned int rx_jumbo_desc_cnt[MAX_UNITS] =
	{JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,
	JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,
	JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,JBO_DESC_CNT,
	JBO_DESC_CNT };
#endif

#ifdef BCM_INT_COAL
#ifdef BCM_NAPI_RXPOLL
static unsigned int adaptive_coalesce[MAX_UNITS] =
	{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
#else
static unsigned int adaptive_coalesce[MAX_UNITS] =
	{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
#endif

#define RX_COAL_TK DEFAULT_RX_COALESCING_TICKS
static unsigned int rx_coalesce_ticks[MAX_UNITS] =
	{RX_COAL_TK,RX_COAL_TK,RX_COAL_TK,RX_COAL_TK,RX_COAL_TK,
	RX_COAL_TK, RX_COAL_TK,RX_COAL_TK,RX_COAL_TK,RX_COAL_TK,
	RX_COAL_TK,RX_COAL_TK, RX_COAL_TK,RX_COAL_TK,RX_COAL_TK,
	RX_COAL_TK};

#define RX_COAL_FM DEFAULT_RX_MAX_COALESCED_FRAMES
static unsigned int rx_max_coalesce_frames[MAX_UNITS] =
	{RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,
	RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,
	RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,RX_COAL_FM,
	RX_COAL_FM};

#define TX_COAL_TK DEFAULT_TX_COALESCING_TICKS
static unsigned int tx_coalesce_ticks[MAX_UNITS] =
	{TX_COAL_TK,TX_COAL_TK,TX_COAL_TK,TX_COAL_TK,TX_COAL_TK,
	TX_COAL_TK, TX_COAL_TK,TX_COAL_TK,TX_COAL_TK,TX_COAL_TK,
	TX_COAL_TK,TX_COAL_TK, TX_COAL_TK,TX_COAL_TK,TX_COAL_TK,
	TX_COAL_TK};

#define TX_COAL_FM DEFAULT_TX_MAX_COALESCED_FRAMES
static unsigned int tx_max_coalesce_frames[MAX_UNITS] =
	{TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,
	TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,
	TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,TX_COAL_FM,
	TX_COAL_FM};

#define ST_COAL_TK DEFAULT_STATS_COALESCING_TICKS
static unsigned int stats_coalesce_ticks[MAX_UNITS] =
	{ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,
	ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,
	ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,ST_COAL_TK,
	ST_COAL_TK,};

#endif
#ifdef BCM_WOL
static int enable_wol[MAX_UNITS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
#endif
#ifdef BCM_TSO
static int enable_tso[MAX_UNITS] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
#endif

#ifdef BCM_NIC_SEND_BD
static int nic_tx_bd[MAX_UNITS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
#endif

#ifdef BCM_ASF
static int vlan_tag_mode[MAX_UNITS] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
#endif

/* Operational parameters that usually are not changed. */
/* Time in jiffies before concluding the transmitter is hung. */
#define TX_TIMEOUT  (2*HZ)

#if (LINUX_VERSION_CODE < 0x02030d)
#define pci_resource_start(dev, bar)	(dev->base_address[bar] & PCI_BASE_ADDRESS_MEM_MASK)
#elif (LINUX_VERSION_CODE < 0x02032b)
#define pci_resource_start(dev, bar)	(dev->resource[bar] & PCI_BASE_ADDRESS_MEM_MASK)
#endif

#if (LINUX_VERSION_CODE < 0x02032b)
#define dev_kfree_skb_irq(skb)  dev_kfree_skb(skb)
#define netif_wake_queue(dev)	clear_bit(0, &dev->tbusy); mark_bh(NET_BH)
#define netif_stop_queue(dev)	set_bit(0, &dev->tbusy)

static inline void netif_start_queue(struct net_device *dev)
{
	dev->tbusy = 0;
	dev->interrupt = 0;
	dev->start = 1;
}

#define netif_queue_stopped(dev)	dev->tbusy
#define netif_running(dev)		dev->start

static inline void tasklet_schedule(struct tasklet_struct *tasklet)
{
	queue_task(tasklet, &tq_immediate);
	mark_bh(IMMEDIATE_BH);
}

static inline void tasklet_init(struct tasklet_struct *tasklet,
				void (*func)(unsigned long),
				unsigned long data)
{
        tasklet->next = NULL;
        tasklet->sync = 0;
        tasklet->routine = (void (*)(void *))func;
        tasklet->data = (void *)data;
}

#define tasklet_kill(tasklet)

#endif

#if (LINUX_VERSION_CODE < 0x020300)
struct pci_device_id {
	unsigned int vendor, device;		/* Vendor and device ID or PCI_ANY_ID */
	unsigned int subvendor, subdevice;	/* Subsystem ID's or PCI_ANY_ID */
	unsigned int class, class_mask;		/* (class,subclass,prog-if) triplet */
	unsigned long driver_data;		/* Data private to the driver */
};

#define PCI_ANY_ID		0

#define pci_set_drvdata(pdev, dev)
#define pci_get_drvdata(pdev) 0

#define pci_enable_device(pdev) 0

#define __devinit		__init
#define __devinitdata		__initdata
#define __devexit

#define SET_MODULE_OWNER(dev)
#define MODULE_DEVICE_TABLE(pci, pci_tbl)

#endif

#if (LINUX_VERSION_CODE < 0x020411)
#ifndef __devexit_p
#define __devexit_p(x)	x
#endif
#endif

#ifndef MODULE_LICENSE
#define MODULE_LICENSE(license)
#endif

#ifndef IRQ_RETVAL
typedef void irqreturn_t;
#define IRQ_RETVAL(x)
#endif

#if (LINUX_VERSION_CODE < 0x02032a)
static inline void *pci_alloc_consistent(struct pci_dev *pdev, size_t size,
					 dma_addr_t *dma_handle)
{
	void *virt_ptr;

	/* Maximum in slab.c */
	if (size > 131072)
		return 0;

	virt_ptr = kmalloc(size, GFP_KERNEL);
	*dma_handle = virt_to_bus(virt_ptr);
	return virt_ptr;
}
#define pci_free_consistent(dev, size, ptr, dma_ptr)	kfree(ptr)

#endif /*#if (LINUX_VERSION_CODE < 0x02032a) */


#if (LINUX_VERSION_CODE < 0x02040d)

#if (LINUX_VERSION_CODE >= 0x020409) && defined(RED_HAT_LINUX_KERNEL)

#define BCM_32BIT_DMA_MASK ((u64) 0x00000000ffffffff)
#define BCM_64BIT_DMA_MASK ((u64) 0xffffffffffffffff)

#else
/* pci_set_dma_mask is using dma_addr_t */

#define BCM_32BIT_DMA_MASK ((dma_addr_t) 0xffffffff)
#define BCM_64BIT_DMA_MASK ((dma_addr_t) 0xffffffff)

#endif

#else /* (LINUX_VERSION_CODE < 0x02040d) */

#define BCM_32BIT_DMA_MASK ((u64) 0x00000000ffffffff)
#define BCM_64BIT_DMA_MASK ((u64) 0xffffffffffffffff)
#endif

#if (LINUX_VERSION_CODE < 0x020329)
#define pci_set_dma_mask(pdev, mask) (0)
#else
#if (LINUX_VERSION_CODE < 0x020403)
int
pci_set_dma_mask(struct pci_dev *dev, dma_addr_t mask)
{
    if(! pci_dma_supported(dev, mask))
        return -EIO;

    dev->dma_mask = mask;

    return 0;
}
#endif
#endif

#if (LINUX_VERSION_CODE < 0x020547)
#define pci_set_consistent_dma_mask(pdev, mask)
#endif

#if (LINUX_VERSION_CODE < 0x020402)
#define pci_request_regions(pdev, name) (0)
#define pci_release_regions(pdev)
#endif

#if ! defined(spin_is_locked)
#define spin_is_locked(lock)    (test_bit(0,(lock)))
#endif

#define BCM5700_LOCK(pUmDevice, flags)					\
	if ((pUmDevice)->do_global_lock) {				\
		spin_lock_irqsave(&(pUmDevice)->global_lock, flags);	\
	}

#define BCM5700_UNLOCK(pUmDevice, flags)				\
	if ((pUmDevice)->do_global_lock) {				\
		spin_unlock_irqrestore(&(pUmDevice)->global_lock, flags);\
	}

inline void
bcm5700_intr_lock(PUM_DEVICE_BLOCK pUmDevice)
{
	if (pUmDevice->do_global_lock) {
		spin_lock(&pUmDevice->global_lock);
	}
}

inline void
bcm5700_intr_unlock(PUM_DEVICE_BLOCK pUmDevice)
{
	if (pUmDevice->do_global_lock) {
		spin_unlock(&pUmDevice->global_lock);
	}
}

void
bcm5700_intr_off(PUM_DEVICE_BLOCK pUmDevice)
{
	atomic_inc(&pUmDevice->intr_sem);
	LM_DisableInterrupt(&pUmDevice->lm_dev);
#if (LINUX_VERSION_CODE >= 0x2051c)
	synchronize_irq(pUmDevice->dev->irq);
#else
	synchronize_irq();
#endif
	LM_DisableInterrupt(&pUmDevice->lm_dev);
}

void
bcm5700_intr_on(PUM_DEVICE_BLOCK pUmDevice)
{
	if (atomic_dec_and_test(&pUmDevice->intr_sem)) {
		LM_EnableInterrupt(&pUmDevice->lm_dev);
	}
}

/*
 * Broadcom NIC Extension support
 * -ffan
 */
#ifdef NICE_SUPPORT
#include "nicext.h"

typedef struct {
	ushort  tag;
	ushort  signature;
} vlan_tag_t;

#endif /* NICE_SUPPORT */

int MM_Packet_Desc_Size = sizeof(UM_PACKET);

#if defined(MODULE)
MODULE_AUTHOR("Michael Chan <mchan@broadcom.com>");
MODULE_DESCRIPTION("BCM5700 Driver");
MODULE_LICENSE("GPL");
MODULE_PARM(debug, "i");
MODULE_PARM(line_speed, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(auto_speed, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(rx_flow_control, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(tx_flow_control, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(auto_flow_control, "1-" __MODULE_STRING(MAX_UNITS) "i");
#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
MODULE_PARM(mtu, "1-" __MODULE_STRING(MAX_UNITS) "i");
#endif
MODULE_PARM(tx_checksum, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(rx_checksum, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(scatter_gather, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(tx_pkt_desc_cnt, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(rx_std_desc_cnt, "1-" __MODULE_STRING(MAX_UNITS) "i");
#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
MODULE_PARM(rx_jumbo_desc_cnt, "1-" __MODULE_STRING(MAX_UNITS) "i");
#endif
#ifdef BCM_INT_COAL
MODULE_PARM(adaptive_coalesce, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(rx_coalesce_ticks, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(rx_max_coalesce_frames, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(tx_coalesce_ticks, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(tx_max_coalesce_frames, "1-" __MODULE_STRING(MAX_UNITS) "i");
MODULE_PARM(stats_coalesce_ticks, "1-" __MODULE_STRING(MAX_UNITS) "i");
#endif
#ifdef BCM_WOL
MODULE_PARM(enable_wol, "1-" __MODULE_STRING(MAX_UNITS) "i");
#endif
#ifdef BCM_TSO
MODULE_PARM(enable_tso, "1-" __MODULE_STRING(MAX_UNITS) "i");
#endif
#ifdef BCM_NIC_SEND_BD
MODULE_PARM(nic_tx_bd, "1-" __MODULE_STRING(MAX_UNITS) "i");
#endif
#ifdef BCM_ASF
MODULE_PARM(vlan_tag_mode, "1-" __MODULE_STRING(MAX_UNITS) "i");
#endif
#endif

#define RUN_AT(x) (jiffies + (x))

char kernel_version[] = UTS_RELEASE;

#define PCI_SUPPORT_VER2

#if ! defined(CAP_NET_ADMIN)
#define capable(CAP_XXX) (suser())
#endif

#define tigon3_debug debug
#if TIGON3_DEBUG
static int tigon3_debug = TIGON3_DEBUG;
#else
static int tigon3_debug = 0;
#endif


STATIC int bcm5700_open(struct net_device *dev);
STATIC void bcm5700_timer(unsigned long data);
STATIC void bcm5700_reset(struct net_device *dev);
STATIC int bcm5700_start_xmit(struct sk_buff *skb, struct net_device *dev);
STATIC irqreturn_t bcm5700_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
#ifdef BCM_TASKLET
STATIC void bcm5700_tasklet(unsigned long data);
#endif
STATIC int bcm5700_close(struct net_device *dev);
STATIC struct net_device_stats *bcm5700_get_stats(struct net_device *dev);
STATIC int bcm5700_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
STATIC void bcm5700_set_rx_mode(struct net_device *dev);
STATIC int bcm5700_set_mac_addr(struct net_device *dev, void *p);
#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
STATIC int bcm5700_change_mtu(struct net_device *dev, int new_mtu);
#endif
#ifdef BCM_NAPI_RXPOLL
STATIC int bcm5700_poll(struct net_device *dev, int *budget);
#endif
STATIC int replenish_rx_buffers(PUM_DEVICE_BLOCK pUmDevice, int max);
STATIC int bcm5700_freemem(struct net_device *dev);
#ifdef NICE_SUPPORT
STATIC int bcm5700_freemem2(UM_DEVICE_BLOCK *pUmDevice, int index);
#endif
#ifdef BCM_INT_COAL
#ifndef BCM_NAPI_RXPOLL
STATIC int bcm5700_adapt_coalesce(PUM_DEVICE_BLOCK pUmDevice);
#endif
#endif
STATIC void bcm5700_set_vlan_mode(UM_DEVICE_BLOCK *pUmDevice);
STATIC int bcm5700_init_counters(PUM_DEVICE_BLOCK pUmDevice);
#ifdef BCM_VLAN
STATIC void bcm5700_vlan_rx_register(struct net_device *dev, struct vlan_group *vlgrp);
STATIC void bcm5700_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid);
#endif
void bcm5700_shutdown(UM_DEVICE_BLOCK *pUmDevice);
void bcm5700_free_remaining_rx_bufs(UM_DEVICE_BLOCK *pUmDevice);
void bcm5700_validate_param_range(UM_DEVICE_BLOCK *pUmDevice, int *param,
	char *param_name, int min, int max, int deflt);

#ifdef HAVE_POLL_CONTROLLER
STATIC void poll_bcm5700(struct net_device *dev);
#endif

/* A list of all installed bcm5700 devices. */
static struct net_device *root_tigon3_dev = NULL;

#if defined(CONFIG_SPARC64) || defined(CONFIG_X86_64) ||defined(CONFIG_PPC64)

#ifdef NICE_SUPPORT
#if (LINUX_VERSION_CODE < 0x20500)
extern int register_ioctl32_conversion(unsigned int cmd,
	int (*handler)(unsigned int, unsigned int, unsigned long,
	struct file *));
int unregister_ioctl32_conversion(unsigned int cmd);
#else
#include <linux/ioctl32.h>
#endif

#define BCM_IOCTL32 1

atomic_t bcm5700_load_count = ATOMIC_INIT(0);

static int
bcm5700_ioctl32(unsigned int fd, unsigned int cmd, unsigned long arg,
	struct file *filep)
{
	struct ifreq rq;
	struct net_device *tmp_dev = root_tigon3_dev;
	int ret;
	struct nice_req* nrq;
	struct ifreq_nice32 {
		char ifnr_name[16];
		__u32 cmd;
		__u32 nrq1;
		__u32 nrq2;
		__u32 nrq3;
	} nrq32;

	if (!capable(CAP_NET_ADMIN))
		return -EPERM;

	if (copy_from_user(&nrq32, (char *) arg, 32))
		return -EFAULT;

	memcpy(rq.ifr_name, nrq32.ifnr_name, 16);

	nrq = (struct nice_req*) &rq.ifr_ifru;
	nrq->cmd = nrq32.cmd;
	if (nrq->cmd == NICE_CMD_GET_STATS_BLOCK) {
		nrq->nrq_stats_useraddr = (void *) ((__u64) nrq32.nrq1);
		nrq->nrq_stats_size = nrq32.nrq2;
	}
	else {
		memcpy(&nrq->nrq_speed, &nrq32.nrq1, 12);
	}
	while (tmp_dev) {
		if (strcmp(rq.ifr_name, tmp_dev->name) == 0) {
			ret = bcm5700_ioctl(tmp_dev, &rq, cmd);
			if (ret == 0) {
				if (nrq->cmd == NICE_CMD_GET_STATS_BLOCK)
					return ret;

				memcpy(&nrq32.nrq1, &nrq->nrq_speed, 12);
				if (copy_to_user((char *) arg, &nrq32, 32))
					return -EFAULT;
			}
			return ret;
		}
		tmp_dev = ((UM_DEVICE_BLOCK *)(tmp_dev->priv))->next_module;
	}
	return -ENODEV;
}
#endif /* NICE_SUPPORT */
#endif

typedef enum {
	BCM5700A6 = 0,
	BCM5700T6,
	BCM5700A9,
	BCM5700T9,
	BCM5700,
	BCM5701A5,
	BCM5701T1,
	BCM5701T8,
	BCM5701A7,
	BCM5701A10,
	BCM5701A12,
	BCM5701,
	BCM5702,
	BCM5703,
	BCM5703A31,
	BCM5703ARBUCKLE,
	TC996T,
	TC996ST,
	TC996SSX,
	TC996SX,
	TC996BT,
	TC997T,
	TC997SX,
	TC1000T,
	TC1000BT,
	TC940BR01,
	TC942BR01,
	TC998T,
	TC998SX,
	TC999T,
	NC6770,
	NC1020,
	NC7760,
	NC7761,
	NC7770,
	NC7771,
	NC7780,
	NC7781,
	NC7772,
	NC7782,
	NC7783,
	BCM5704CIOBE,
	BCM5704,
	BCM5704S,
	BCM5705,
	BCM5705M,
	BCM5705F,
	BCM5901,
	BCM5782,
	BCM5788
} board_t;


/* indexed by board_t, above */
static struct {
	char *name;
} board_info[] __devinitdata = {
	{ "Broadcom BCM5700 1000Base-T" },
	{ "Broadcom BCM5700 1000Base-SX" },
	{ "Broadcom BCM5700 1000Base-SX" },
	{ "Broadcom BCM5700 1000Base-T" },
	{ "Broadcom BCM5700" },
	{ "Broadcom BCM5701 1000Base-T" },
	{ "Broadcom BCM5701 1000Base-T" },
	{ "Broadcom BCM5701 1000Base-T" },
	{ "Broadcom BCM5701 1000Base-SX" },
	{ "Broadcom BCM5701 1000Base-T" },
	{ "Broadcom BCM5701 1000Base-T" },
	{ "Broadcom BCM5701" },
	{ "Broadcom BCM5702 1000Base-T" },
	{ "Broadcom BCM5703 1000Base-T" },
	{ "Broadcom BCM5703 1000Base-SX" },
	{ "Broadcom B5703 1000Base-SX" },
	{ "3Com 3C996 10/100/1000 Server NIC" },
	{ "3Com 3C996 10/100/1000 Server NIC" },
	{ "3Com 3C996 Gigabit Fiber-SX Server NIC" },
	{ "3Com 3C996 Gigabit Fiber-SX Server NIC" },
	{ "3Com 3C996B Gigabit Server NIC" },
	{ "3Com 3C997 Gigabit Server NIC" },
	{ "3Com 3C997 Gigabit Fiber-SX Server NIC" },
	{ "3Com 3C1000 Gigabit NIC" },
	{ "3Com 3C1000B-T 10/100/1000 PCI" },
	{ "3Com 3C940 Gigabit LOM (21X21)" },
	{ "3Com 3C942 Gigabit LOM (31X31)" },
	{ "3Com 3C998-T Dual Port 10/100/1000 PCI-X Server NIC" },
	{ "3Com 3C998-SX Dual Port 1000-SX PCI-X Server NIC" },
	{ "3Com 3C999-T Quad Port 10/100/1000 PCI-X Server NIC" },
	{ "HP NC6770 Gigabit Server Adapter" },
	{ "NC1020 HP ProLiant Gigabit Server Adapter 32 PCI" },
	{ "HP NC7760 Gigabit Server Adapter" },
	{ "HP NC7761 Gigabit Server Adapter" },
	{ "HP NC7770 Gigabit Server Adapter" },
	{ "HP NC7771 Gigabit Server Adapter" },
	{ "HP NC7780 Gigabit Server Adapter" },
	{ "HP NC7781 Gigabit Server Adapter" },
	{ "HP NC7772 Gigabit Server Adapter" },
	{ "HP NC7782 Gigabit Server Adapter" },
	{ "HP NC7783 Gigabit Server Adapter" },
	{ "Broadcom BCM5704 CIOB-E 1000Base-T" },
	{ "Broadcom BCM5704 1000Base-T" },
	{ "Broadcom BCM5704 1000Base-SX" },
	{ "Broadcom BCM5705 1000Base-T" },
	{ "Broadcom BCM5705M 1000Base-T" },
	{ "Broadcom 570x 10/100 Integrated Controller" },
	{ "Broadcom BCM5901 100Base-TX" },
	{ "Broadcom NetXtreme Gigabit Ethernet for hp" },
	{ "Broadcom BCM5788 1000Base-T" },
	{ 0 },
	};

static struct pci_device_id bcm5700_pci_tbl[] __devinitdata = {
	{0x14e4, 0x1644, 0x14e4, 0x1644, 0, 0, BCM5700A6 },
	{0x14e4, 0x1644, 0x14e4, 0x2, 0, 0, BCM5700T6 },
	{0x14e4, 0x1644, 0x14e4, 0x3, 0, 0, BCM5700A9 },
	{0x14e4, 0x1644, 0x14e4, 0x4, 0, 0, BCM5700T9 },
	{0x14e4, 0x1644, 0x1028, 0xd1, 0, 0, BCM5700 },
	{0x14e4, 0x1644, 0x1028, 0x0106, 0, 0, BCM5700 },
	{0x14e4, 0x1644, 0x1028, 0x0109, 0, 0, BCM5700 },
	{0x14e4, 0x1644, 0x1028, 0x010a, 0, 0, BCM5700 },
	{0x14e4, 0x1644, 0x10b7, 0x1000, 0, 0, TC996T },
	{0x14e4, 0x1644, 0x10b7, 0x1001, 0, 0, TC996ST },
	{0x14e4, 0x1644, 0x10b7, 0x1002, 0, 0, TC996SSX },
	{0x14e4, 0x1644, 0x10b7, 0x1003, 0, 0, TC997T },
	{0x14e4, 0x1644, 0x10b7, 0x1005, 0, 0, TC997SX },
	{0x14e4, 0x1644, 0x10b7, 0x1008, 0, 0, TC942BR01 },
	{0x14e4, 0x1644, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5700 },
	{0x14e4, 0x1645, 0x14e4, 1, 0, 0, BCM5701A5 },
	{0x14e4, 0x1645, 0x14e4, 5, 0, 0, BCM5701T1 },
	{0x14e4, 0x1645, 0x14e4, 6, 0, 0, BCM5701T8 },
	{0x14e4, 0x1645, 0x14e4, 7, 0, 0, BCM5701A7 },
	{0x14e4, 0x1645, 0x14e4, 8, 0, 0, BCM5701A10 },
	{0x14e4, 0x1645, 0x14e4, 0x8008, 0, 0, BCM5701A12 },
	{0x14e4, 0x1645, 0x0e11, 0xc1, 0, 0, NC6770 },
	{0x14e4, 0x1645, 0x0e11, 0x7c, 0, 0, NC7770 },
	{0x14e4, 0x1645, 0x0e11, 0x85, 0, 0, NC7780 },
	{0x14e4, 0x1645, 0x1028, 0x0121, 0, 0, BCM5701 },
	{0x14e4, 0x1645, 0x10b7, 0x1004, 0, 0, TC996SX },
	{0x14e4, 0x1645, 0x10b7, 0x1006, 0, 0, TC996BT },
	{0x14e4, 0x1645, 0x10b7, 0x1007, 0, 0, TC1000T },
	{0x14e4, 0x1645, 0x10b7, 0x1008, 0, 0, TC940BR01 },
	{0x14e4, 0x1645, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5701 },
	{0x14e4, 0x1646, 0x14e4, 0x8009, 0, 0, BCM5702 },
	{0x14e4, 0x1646, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5702 },
	{0x14e4, 0x16a6, 0x14e4, 0x8009, 0, 0, BCM5702 },
	{0x14e4, 0x16a6, 0x14e4, 0x000c, 0, 0, BCM5702 },
	{0x14e4, 0x16a6, 0x0e11, 0xbb, 0, 0, NC7760 },
	{0x14e4, 0x16a6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5702 },
	{0x14e4, 0x16c6, 0x10b7, 0x1100, 0, 0, TC1000BT },
	{0x14e4, 0x16c6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5702 },
	{0x14e4, 0x1647, 0x14e4, 0x0009, 0, 0, BCM5703 },
	{0x14e4, 0x1647, 0x14e4, 0x000a, 0, 0, BCM5703A31 },
	{0x14e4, 0x1647, 0x14e4, 0x000b, 0, 0, BCM5703 },
	{0x14e4, 0x1647, 0x14e4, 0x800a, 0, 0, BCM5703 },
	{0x14e4, 0x1647, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5703 },
	{0x14e4, 0x16a7, 0x14e4, 0x0009, 0, 0, BCM5703 },
	{0x14e4, 0x16a7, 0x14e4, 0x000a, 0, 0, BCM5703A31 },
	{0x14e4, 0x16a7, 0x14e4, 0x000b, 0, 0, BCM5703 },
	{0x14e4, 0x16a7, 0x14e4, 0x800a, 0, 0, BCM5703 },
	{0x14e4, 0x16a7, 0x0e11, 0xca, 0, 0, NC7771 },
	{0x14e4, 0x16a7, 0x0e11, 0xcb, 0, 0, NC7781 },
	{0x14e4, 0x16a7, 0x1014, 0x0281, 0, 0, BCM5703ARBUCKLE },
	{0x14e4, 0x16a7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5703 },
	{0x14e4, 0x16c7, 0x14e4, 0x000a, 0, 0, BCM5703A31 },
	{0x14e4, 0x16c7, 0x0e11, 0xca, 0, 0, NC7771 },
	{0x14e4, 0x16c7, 0x0e11, 0xcb, 0, 0, NC7781 },
	{0x14e4, 0x16c7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5703 },
	{0x14e4, 0x1648, 0x0e11, 0xcf, 0, 0, NC7772 },
	{0x14e4, 0x1648, 0x0e11, 0xd0, 0, 0, NC7782 },
	{0x14e4, 0x1648, 0x0e11, 0xd1, 0, 0, NC7783 },
	{0x14e4, 0x1648, 0x10b7, 0x2000, 0, 0, TC998T },
	{0x14e4, 0x1648, 0x10b7, 0x3000, 0, 0, TC999T },
	{0x14e4, 0x1648, 0x1166, 0x1648, 0, 0, BCM5704CIOBE },
	{0x14e4, 0x1648, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5704 },
	{0x14e4, 0x1649, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5704S },
	{0x14e4, 0x16a8, 0x14e4, 0x16a8, 0, 0, BCM5704S },
	{0x14e4, 0x16a8, 0x10b7, 0x2001, 0, 0, TC998SX },
	{0x14e4, 0x16a8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5704S },
	{0x14e4, 0x1653, 0x0e11, 0x00e3, 0, 0, NC7761 },
	{0x14e4, 0x1653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5705 },
	{0x14e4, 0x1654, 0x0e11, 0x00e3, 0, 0, NC7761 },
	{0x14e4, 0x1654, 0x103c, 0x3100, 0, 0, NC1020 },
	{0x14e4, 0x1654, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5705 },
	{0x14e4, 0x165d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5705M },
	{0x14e4, 0x165e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5705M },
	{0x14e4, 0x166e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5705F },
	{0x14e4, 0x1696, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5782 },
	{0x14e4, 0x169c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5788 },
	{0x14e4, 0x170d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5901 },
	{0x14e4, 0x170e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BCM5901 },
	{0,}
};

MODULE_DEVICE_TABLE(pci, bcm5700_pci_tbl);

#ifdef BCM_PROC_FS
extern int bcm5700_proc_create(void);
extern int bcm5700_proc_create_dev(struct net_device *dev);
extern int bcm5700_proc_remove_dev(struct net_device *dev);
#endif

static int __devinit bcm5700_init_board(struct pci_dev *pdev,
					struct net_device **dev_out,
					int board_idx)
{
	struct net_device *dev;
	PUM_DEVICE_BLOCK pUmDevice;
	PLM_DEVICE_BLOCK pDevice;
	int rc;

	*dev_out = NULL;

	/* dev zeroed in init_etherdev */
#if (LINUX_VERSION_CODE >= 0x20600)
	dev = alloc_etherdev(sizeof(*pUmDevice));
#else
	dev = init_etherdev(NULL, sizeof(*pUmDevice));
#endif
	if (dev == NULL) {
		printk (KERN_ERR "%s: unable to alloc new ethernet\n",
			bcm5700_driver);
		return -ENOMEM;
	}
	SET_MODULE_OWNER(dev);
#if (LINUX_VERSION_CODE >= 0x20600)
	SET_NETDEV_DEV(dev, &pdev->dev);
#endif
	pUmDevice = (PUM_DEVICE_BLOCK) dev->priv;

	/* enable device (incl. PCI PM wakeup), and bus-mastering */
	rc = pci_enable_device (pdev);
	if (rc)
		goto err_out;

	rc = pci_request_regions(pdev, bcm5700_driver);
	if (rc)
		goto err_out;

	pci_set_master(pdev);

	if (pci_set_dma_mask(pdev, BCM_64BIT_DMA_MASK) == 0) {
		pUmDevice->using_dac = 1;
		pci_set_consistent_dma_mask(pdev, BCM_64BIT_DMA_MASK);
	}
	else if (pci_set_dma_mask(pdev, BCM_32BIT_DMA_MASK) == 0) {
		pUmDevice->using_dac = 0;
	}
	else {
		printk(KERN_ERR "System does not support DMA\n");
		pci_release_regions(pdev);
		goto err_out;
	}

	pUmDevice->dev = dev;
	pUmDevice->pdev = pdev;
	pUmDevice->mem_list_num = 0;
	pUmDevice->next_module = root_tigon3_dev;
	pUmDevice->index = board_idx;
	root_tigon3_dev = dev;

	spin_lock_init(&pUmDevice->global_lock);

	spin_lock_init(&pUmDevice->undi_lock);

	spin_lock_init(&pUmDevice->phy_lock);

	pDevice = (PLM_DEVICE_BLOCK) pUmDevice;
	pDevice->FunctNum = PCI_FUNC(pUmDevice->pdev->devfn);

#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
	bcm5700_validate_param_range(pUmDevice, &mtu[board_idx], "mtu", 1500, 9000, 1500);
	dev->mtu = mtu[board_idx];
#endif

	if (pci_find_device(0x8086, 0x2418, NULL) ||
		pci_find_device(0x8086, 0x2428, NULL) ||
		pci_find_device(0x8086, 0x244e, NULL) ||
		pci_find_device(0x8086, 0x2448, NULL)) {

		/* Found ICH, ICH0, or ICH2 */
		pDevice->UndiFix = 1;
	}

	if (LM_GetAdapterInfo(pDevice) != LM_STATUS_SUCCESS) {
		printk(KERN_ERR "Get Adapter info failed\n");
		rc = -ENODEV;
		goto err_out_unmap;
	}

	if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5705) {
		if (dev->mtu > 1500) {
			dev->mtu = 1500;
			printk(KERN_WARNING "%s-%d: Jumbo mtu sizes not supported, using mtu=1500\n", bcm5700_driver, pUmDevice->index);
		}
	}

	if (pci_find_device(0x1022, 0x700c, NULL)) {
		/* AMD762 writes I/O out of order */
		/* Setting bit 1 in 762's register 0x4C still doesn't work */
		/* in all cases */
		pDevice->FlushPostedWrites = TRUE;
		pDevice->NicSendBd = FALSE;
	}
	pUmDevice->do_global_lock = 0;
	if (T3_ASIC_REV(pUmDevice->lm_dev.ChipRevId) == T3_ASIC_REV_5700) {
		/* The 5700 chip works best without interleaved register */
		/* accesses on certain machines. */
		pUmDevice->do_global_lock = 1;
	}
	if ((T3_ASIC_REV(pUmDevice->lm_dev.ChipRevId) == T3_ASIC_REV_5701) &&
		((pDevice->PciState & T3_PCI_STATE_NOT_PCI_X_BUS) == 0)) {

		pUmDevice->rx_buf_align = 0;
	}
	else {
		pUmDevice->rx_buf_align = 2;
	}
/*	dev->base_addr = pci_resource_start(pdev, 0);*/
	dev->mem_start = pci_resource_start(pdev, 0);
	dev->mem_end = dev->mem_start + sizeof(T3_STD_MEM_MAP); 
	dev->irq = pdev->irq;

	*dev_out = dev;
	return 0;

err_out_unmap:
	pci_release_regions(pdev);
	bcm5700_freemem(dev);

err_out:
#if (LINUX_VERSION_CODE < 0x020600)
	unregister_netdev(dev);
	kfree(dev);
#else
	free_netdev(dev);
#endif
	return rc;
}

static int __devinit
bcm5700_print_ver(void)
{
	printk(KERN_INFO "Broadcom Gigabit Ethernet Driver %s ",
		bcm5700_driver);
#ifdef NICE_SUPPORT
	printk("with Broadcom NIC Extension (NICE) ");
#endif
	printk("ver. %s %s\n", bcm5700_version, bcm5700_date);
	return 0;
}

static int __devinit
bcm5700_init_one(struct pci_dev *pdev,
				       const struct pci_device_id *ent)
{
	struct net_device *dev = NULL;
	PUM_DEVICE_BLOCK pUmDevice;
	PLM_DEVICE_BLOCK pDevice;
	int i;
	static int board_idx = -1;
	static int printed_version = 0;
	struct pci_dev *amd_dev;

	board_idx++;

	if (!printed_version) {
		bcm5700_print_ver();
#ifdef BCM_PROC_FS
		bcm5700_proc_create();
#endif
		printed_version = 1;
	}

	i = bcm5700_init_board(pdev, &dev, board_idx);
	if (i < 0) {
		return i;
	}

	if (dev == NULL)
		return -ENOMEM;

#ifdef BCM_IOCTL32
	if (atomic_read(&bcm5700_load_count) == 0) {
		register_ioctl32_conversion(SIOCNICE, bcm5700_ioctl32);
	}
	atomic_inc(&bcm5700_load_count);
#endif
	dev->open = bcm5700_open;
	dev->hard_start_xmit = bcm5700_start_xmit;
	dev->stop = bcm5700_close;
	dev->get_stats = bcm5700_get_stats;
	dev->set_multicast_list = bcm5700_set_rx_mode;
	dev->do_ioctl = bcm5700_ioctl;
	dev->set_mac_address = &bcm5700_set_mac_addr;
#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
	dev->change_mtu = &bcm5700_change_mtu;
#endif
#if (LINUX_VERSION_CODE >= 0x20400)
	dev->tx_timeout = bcm5700_reset;
	dev->watchdog_timeo = TX_TIMEOUT;
#endif
#ifdef BCM_VLAN
	dev->vlan_rx_register = &bcm5700_vlan_rx_register;
	dev->vlan_rx_kill_vid = &bcm5700_vlan_rx_kill_vid;
#endif
#ifdef BCM_NAPI_RXPOLL
	dev->poll = bcm5700_poll;
	dev->weight = 64;
#endif

	pUmDevice = (PUM_DEVICE_BLOCK) dev->priv;
	pDevice = (PLM_DEVICE_BLOCK) pUmDevice;

	dev->base_addr = pci_resource_start(pdev, 0);
	dev->irq = pdev->irq;
#ifdef HAVE_POLL_CONTROLLER
	dev->poll_controller = poll_bcm5700;
#endif

#if (LINUX_VERSION_CODE >= 0x20600)
	if ((i = register_netdev(dev))) {
		printk(KERN_ERR "%s: Cannot register net device\n",
			bcm5700_driver);
		if (pUmDevice->lm_dev.pMappedMemBase)
			iounmap(pUmDevice->lm_dev.pMappedMemBase);
		pci_release_regions(pdev);
		bcm5700_freemem(dev);
		free_netdev(dev);
		return i;
	}
#endif

	pci_set_drvdata(pdev, dev);

	memcpy(dev->dev_addr, pDevice->NodeAddress, 6);
	pUmDevice->name = board_info[ent->driver_data].name,
	printk(KERN_INFO "%s: %s found at mem %lx, IRQ %d, ",
		dev->name, pUmDevice->name, dev->base_addr,
		dev->irq);
	printk("node addr ");
	for (i = 0; i < 6; i++) {
		printk("%2.2x", dev->dev_addr[i]);
	}
	printk("\n");

	printk(KERN_INFO "%s: ", dev->name);
	if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5400_PHY_ID)
		printk("Broadcom BCM5400 Copper ");
	else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5401_PHY_ID)
		printk("Broadcom BCM5401 Copper ");
	else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5411_PHY_ID)
		printk("Broadcom BCM5411 Copper ");
	else if (((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5701_PHY_ID) &&
		!pDevice->EnableTbi) {
		printk("Broadcom BCM5701 Integrated Copper ");
	}
	else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5703_PHY_ID) {
		printk("Broadcom BCM5703 Integrated ");
		if (pDevice->EnableTbi)
			printk("SerDes ");
		else
			printk("Copper ");
	}
	else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5704_PHY_ID) {
		printk("Broadcom BCM5704 Integrated ");
		if (pDevice->EnableTbi)
			printk("SerDes ");
		else
			printk("Copper ");
	}
	else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM5705_PHY_ID)
		printk("Broadcom BCM5705 Integrated Copper ");
	else if ((pDevice->PhyId & PHY_ID_MASK) == PHY_BCM8002_PHY_ID)
		printk("Broadcom BCM8002 SerDes ");
	else if (pDevice->EnableTbi) {
		if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5703) {
			printk("Broadcom BCM5703 Integrated SerDes ");
		}
		else if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5704) {
			printk("Broadcom BCM5704 Integrated SerDes ");
		}
		else {
			printk("Agilent HDMP-1636 SerDes ");
		}
	}
	else {
		printk("Unknown ");
	}
	printk("transceiver found\n");

	printk(KERN_INFO "%s: ", dev->name);
#if (LINUX_VERSION_CODE >= 0x20400)
	if (scatter_gather[board_idx]) {
		dev->features |= NETIF_F_SG;
		if (pUmDevice->using_dac && !pDevice->Bcm5788)
			dev->features |= NETIF_F_HIGHDMA;
	}
	if ((pDevice->TaskOffloadCap & LM_TASK_OFFLOAD_TX_TCP_CHECKSUM) &&
		tx_checksum[board_idx]) {
		dev->features |= NETIF_F_IP_CSUM;
	}
#ifdef BCM_VLAN
	dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
#endif
#ifdef BCM_TSO
	if ((pDevice->TaskToOffload & LM_TASK_OFFLOAD_TCP_SEGMENTATION) &&
		enable_tso[board_idx]) {
		dev->features |= NETIF_F_TSO;
	}
#endif

	printk("Scatter-gather %s, 64-bit DMA %s, Tx Checksum %s, ",
		(char *) ((dev->features & NETIF_F_SG) ? "ON" : "OFF"),
		(char *) ((dev->features & NETIF_F_HIGHDMA) ? "ON" : "OFF"),
		(char *) ((dev->features & NETIF_F_IP_CSUM) ? "ON" : "OFF"));
#endif
	if ((pDevice->ChipRevId != T3_CHIP_ID_5700_B0) &&
		rx_checksum[board_idx])
		printk("Rx Checksum ON");
	else
		printk("Rx Checksum OFF");
#ifdef BCM_VLAN
	printk(", 802.1Q VLAN ON");
#endif
#ifdef BCM_TSO
	if (dev->features & NETIF_F_TSO) {
		printk(", TSO ON");
	}
	else
#endif
#ifdef BCM_NAPI_RXPOLL
	printk(", NAPI ON");
#endif
	printk("\n");

#ifdef BCM_PROC_FS
	bcm5700_proc_create_dev(dev);
#endif
#ifdef BCM_TASKLET
	tasklet_init(&pUmDevice->tasklet, bcm5700_tasklet,
		(unsigned long) pUmDevice);
#endif
	if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5704) {
		if ((REG_RD(pDevice, PciCfg.DualMacCtrl) &
			T3_DUAL_MAC_CH_CTRL_MASK) == 3) {

			printk(KERN_WARNING "%s: Device is configured for Hardware Based Teaming which is not supported with this operating system. Please consult the user diagnostic guide to disable Turbo Teaming.\n", dev->name);
		}
	}

	if ((amd_dev = pci_find_device(0x1022, 0x700c, NULL))) {
		u32 val;

		/* Found AMD 762 North bridge */
		pci_read_config_dword(amd_dev, 0x4c, &val);
		if ((val & 0x02) == 0) {
			pci_write_config_dword(amd_dev, 0x4c, val | 0x02);
			printk(KERN_INFO "%s: Setting AMD762 Northbridge to enable PCI ordering compliance\n", bcm5700_driver);
		}
	}
	return 0;

}


static void __devexit
bcm5700_remove_one (struct pci_dev *pdev)
{
	struct net_device *dev = pci_get_drvdata (pdev);
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;

#ifdef BCM_PROC_FS
	bcm5700_proc_remove_dev(dev); 
#endif
#ifdef BCM_IOCTL32
	atomic_dec(&bcm5700_load_count);
	if (atomic_read(&bcm5700_load_count) == 0)
		unregister_ioctl32_conversion(SIOCNICE);
#endif
	unregister_netdev(dev);

	if (pUmDevice->lm_dev.pMappedMemBase)
		iounmap(pUmDevice->lm_dev.pMappedMemBase);

	pci_release_regions(pdev);

#if (LINUX_VERSION_CODE < 0x020600)
	kfree(dev);
#else
	free_netdev(dev);
#endif

	pci_set_drvdata(pdev, NULL);

/*	pci_power_off(pdev, -1);*/

}



STATIC int
bcm5700_open(struct net_device *dev)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice;

	if (pUmDevice->suspended)
		return -EAGAIN;

	/* delay for 6 seconds */
	pUmDevice->delayed_link_ind = (6 * HZ) / pUmDevice->timer_interval;

#ifdef BCM_INT_COAL
#ifndef BCM_NAPI_RXPOLL
	pUmDevice->adaptive_expiry = HZ / pUmDevice->timer_interval;
#endif
#endif

#if INCLUDE_TBI_SUPPORT
	if (pDevice->EnableTbi && pDevice->PollTbiLink) {
		pUmDevice->poll_tbi_interval = HZ / pUmDevice->timer_interval;
		if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5703) {
			pUmDevice->poll_tbi_interval /= 4;
		}
		pUmDevice->poll_tbi_expiry = pUmDevice->poll_tbi_interval;
	}
#endif

	pUmDevice->asf_heartbeat = (120 * HZ) / pUmDevice->timer_interval;

	/* Sometimes we get spurious ints. after reset when link is down. */
	/* This field tells the isr to service the int. even if there is */
	/* no status block update. */
	if (pDevice->LedMode != LED_MODE_LINK10) {
		pUmDevice->adapter_just_inited = (3 * HZ) /
			pUmDevice->timer_interval;
	}
	else {
		pUmDevice->adapter_just_inited = 0;
	}

	if (request_irq(dev->irq, &bcm5700_interrupt, SA_SHIRQ, dev->name, dev)) {
		return -EAGAIN;
	}

	pUmDevice->opened = 1;
	if (LM_InitializeAdapter(pDevice) != LM_STATUS_SUCCESS) {
		pUmDevice->opened = 0;
		free_irq(dev->irq, dev);
		bcm5700_freemem(dev);
		return -EAGAIN;
	}

	bcm5700_set_vlan_mode(pUmDevice);
	bcm5700_init_counters(pUmDevice);

	if (pDevice->UndiFix) {
		printk(KERN_INFO "%s: Using indirect register access\n", dev->name);
	}

	if (memcmp(dev->dev_addr, pDevice->NodeAddress, 6)) {
		LM_SetMacAddress(pDevice, dev->dev_addr);
	}

	if (tigon3_debug > 1)
		printk(KERN_DEBUG "%s: tigon3_open() irq %d.\n", dev->name, dev->irq);

	QQ_InitQueue(&pUmDevice->rx_out_of_buf_q.Container,
        MAX_RX_PACKET_DESC_COUNT);
	netif_start_queue(dev);

#if (LINUX_VERSION_CODE < 0x020300)
	MOD_INC_USE_COUNT;
#endif

	init_timer(&pUmDevice->timer);
	pUmDevice->timer.expires = RUN_AT(pUmDevice->timer_interval);
	pUmDevice->timer.data = (unsigned long)dev;
	pUmDevice->timer.function = &bcm5700_timer;
	add_timer(&pUmDevice->timer);

	atomic_set(&pUmDevice->intr_sem, 0);
	LM_EnableInterrupt(pDevice);

	return 0;
}


STATIC void
bcm5700_timer(unsigned long data)
{
	struct net_device *dev = (struct net_device *)data;
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice;
	unsigned long flags = 0;
	LM_UINT32 value32;

	if (!pUmDevice->opened)
		return;

	if (atomic_read(&pUmDevice->intr_sem) || pUmDevice->suspended) {
		pUmDevice->timer.expires = RUN_AT(pUmDevice->timer_interval);
		add_timer(&pUmDevice->timer);
		return;
	}

	if ((T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5705) &&
		(pDevice->LinkStatus == LM_STATUS_LINK_ACTIVE)) {

		LM_GetStats(pDevice);
	}

#if INCLUDE_TBI_SUPPORT
	if(pDevice->PollTbiLink && (--pUmDevice->poll_tbi_expiry <= 0)) {
		BCM5700_PHY_LOCK(pUmDevice, flags);
		value32 = REG_RD(pDevice, MacCtrl.Status);
		if (((pDevice->LinkStatus == LM_STATUS_LINK_ACTIVE) &&
			((value32 & (MAC_STATUS_LINK_STATE_CHANGED |
				MAC_STATUS_CFG_CHANGED)) ||
			!(value32 & MAC_STATUS_PCS_SYNCED)))
			||
			((pDevice->LinkStatus != LM_STATUS_LINK_ACTIVE) &&
			(value32 & (MAC_STATUS_PCS_SYNCED |
				MAC_STATUS_SIGNAL_DETECTED))))
		{
			LM_SetupPhy(pDevice);
		}
		BCM5700_PHY_UNLOCK(pUmDevice, flags);
		pUmDevice->poll_tbi_expiry = pUmDevice->poll_tbi_interval;
		
        }
#endif

	if (pUmDevice->delayed_link_ind > 0) {
		if (pUmDevice->delayed_link_ind == 1)
			MM_IndicateStatus(pDevice, pDevice->LinkStatus);
		else
			pUmDevice->delayed_link_ind--;
	}
	if (pUmDevice->adapter_just_inited > 0) {
		pUmDevice->adapter_just_inited--;
	}

	if (pUmDevice->crc_counter_expiry > 0)
		pUmDevice->crc_counter_expiry--;

	if (!pUmDevice->interrupt) {
		if (!pDevice->UseTaggedStatus) {
			BCM5700_LOCK(pUmDevice, flags);
			if (pDevice->pStatusBlkVirt->Status & STATUS_BLOCK_UPDATED) {
				/* This will generate an interrupt */
				REG_WR(pDevice, Grc.LocalCtrl,
					pDevice->GrcLocalCtrl |
					GRC_MISC_LOCAL_CTRL_SET_INT);
			}
			else {
				REG_WR(pDevice, HostCoalesce.Mode,
					pDevice->CoalesceMode |
					HOST_COALESCE_ENABLE |
					HOST_COALESCE_NOW);
			}
			if (!(REG_RD(pDevice, DmaWrite.Mode) &
				DMA_WRITE_MODE_ENABLE)) {
				BCM5700_UNLOCK(pUmDevice, flags);
				bcm5700_reset(dev);
			}
			else {
				BCM5700_UNLOCK(pUmDevice, flags);
			}
			if (pUmDevice->tx_queued) {
				pUmDevice->tx_queued = 0;
				netif_wake_queue(dev);
			}
		}
#if (LINUX_VERSION_CODE < 0x02032b)
		if ((QQ_GetEntryCnt(&pDevice->TxPacketFreeQ.Container) !=
			pDevice->TxPacketDescCnt) &&
			((jiffies - dev->trans_start) > TX_TIMEOUT)) {

			printk(KERN_WARNING "%s: Tx hung\n", dev->name);
			bcm5700_reset(dev);
		}
#endif
	}
#ifdef BCM_INT_COAL
#ifndef BCM_NAPI_RXPOLL
	if (pUmDevice->adaptive_coalesce) {
		pUmDevice->adaptive_expiry--;
		if (pUmDevice->adaptive_expiry == 0) {	
			pUmDevice->adaptive_expiry = HZ /
				pUmDevice->timer_interval;
			bcm5700_adapt_coalesce(pUmDevice);
		}
	}
#endif
#endif
	if (QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container) >
		(unsigned int) pUmDevice->rx_buf_repl_panic_thresh) {
		/* Generate interrupt and let isr allocate buffers */
		REG_WR(pDevice, HostCoalesce.Mode, pDevice->CoalesceMode |
			HOST_COALESCE_ENABLE | HOST_COALESCE_NOW);
	}

#ifdef BCM_ASF
	if (pDevice->EnableAsf) {
		pUmDevice->asf_heartbeat--;
		if (pUmDevice->asf_heartbeat == 0) {
			MEM_WR_OFFSET(pDevice, T3_CMD_MAILBOX,
				T3_CMD_NICDRV_ALIVE);
			MEM_WR_OFFSET(pDevice, T3_CMD_LENGTH_MAILBOX, 4);
			MEM_WR_OFFSET(pDevice, T3_CMD_DATA_MAILBOX, 3);
			value32 = REG_RD(pDevice, Grc.RxCpuEvent);
			REG_WR(pDevice, Grc.RxCpuEvent, value32 | BIT_14);
			pUmDevice->asf_heartbeat = (120 * HZ) /
				pUmDevice->timer_interval;
		}
	}
#endif

	pUmDevice->timer.expires = RUN_AT(pUmDevice->timer_interval);
	add_timer(&pUmDevice->timer);
}

STATIC int
bcm5700_init_counters(PUM_DEVICE_BLOCK pUmDevice)
{
	PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev;

#ifdef BCM_INT_COAL
#ifndef BCM_NAPI_RXPOLL
	pUmDevice->rx_curr_coalesce_frames = pDevice->RxMaxCoalescedFrames;
	pUmDevice->rx_curr_coalesce_ticks = pDevice->RxCoalescingTicks;
	pUmDevice->tx_curr_coalesce_frames = pDevice->TxMaxCoalescedFrames;
	pUmDevice->rx_last_cnt = 0;
	pUmDevice->tx_last_cnt = 0;
#endif
#endif
	pDevice->PhyCrcCount = 0;
#if TIGON3_DEBUG
	pUmDevice->tx_zc_count = 0;
	pUmDevice->tx_chksum_count = 0;
	pUmDevice->tx_himem_count = 0;
	pUmDevice->rx_good_chksum_count = 0;
	pUmDevice->rx_bad_chksum_count = 0;
#endif
#ifdef BCM_TSO
	pUmDevice->tso_pkt_count = 0;
#endif
	return 0;
}

#ifdef BCM_INT_COAL
#ifndef BCM_NAPI_RXPOLL
STATIC int
bcm5700_do_adapt_coalesce(PUM_DEVICE_BLOCK pUmDevice,
	int rx_frames, int rx_ticks, int tx_frames, int rx_frames_intr)
{
	unsigned long flags = 0;
	LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev;

	if (pUmDevice->do_global_lock) {
		if (spin_is_locked(&pUmDevice->global_lock))
			return 0;
		spin_lock_irqsave(&pUmDevice->global_lock, flags);
	}
	pUmDevice->rx_curr_coalesce_frames = rx_frames;
	pUmDevice->rx_curr_coalesce_ticks = rx_ticks;
	pUmDevice->tx_curr_coalesce_frames = tx_frames;
	pUmDevice->rx_curr_coalesce_frames_intr = rx_frames_intr;
	REG_WR(pDevice, HostCoalesce.RxMaxCoalescedFrames, rx_frames);

	REG_WR(pDevice, HostCoalesce.RxCoalescingTicks, rx_ticks);

	REG_WR(pDevice, HostCoalesce.TxMaxCoalescedFrames, tx_frames);

	REG_WR(pDevice, HostCoalesce.RxMaxCoalescedFramesDuringInt,
		rx_frames_intr);

	BCM5700_UNLOCK(pUmDevice, flags);
	return 0;
}

STATIC int
bcm5700_adapt_coalesce(PUM_DEVICE_BLOCK pUmDevice)
{
	PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev;
	uint rx_curr_cnt, tx_curr_cnt, rx_delta, tx_delta, total_delta;

	rx_curr_cnt = pDevice->pStatsBlkVirt->ifHCInUcastPkts.Low;
	tx_curr_cnt = pDevice->pStatsBlkVirt->ifHCOutUcastPkts.Low;
	if ((rx_curr_cnt <= pUmDevice->rx_last_cnt) ||
		(tx_curr_cnt < pUmDevice->tx_last_cnt)) {

		/* skip if there is counter rollover */
		pUmDevice->rx_last_cnt = rx_curr_cnt;
		pUmDevice->tx_last_cnt = tx_curr_cnt;
		return 0;
	}

	rx_delta = rx_curr_cnt - pUmDevice->rx_last_cnt;
	tx_delta = tx_curr_cnt - pUmDevice->tx_last_cnt;
	total_delta = (((rx_delta + rx_delta) + tx_delta) / 3) << 1;

	pUmDevice->rx_last_cnt = rx_curr_cnt;
	pUmDevice->tx_last_cnt = tx_curr_cnt;

	if (total_delta < ADAPTIVE_LO_PKT_THRESH) {
		if (pUmDevice->rx_curr_coalesce_frames !=
			ADAPTIVE_LO_RX_MAX_COALESCED_FRAMES) {

			bcm5700_do_adapt_coalesce(pUmDevice,
				ADAPTIVE_LO_RX_MAX_COALESCED_FRAMES,
				ADAPTIVE_LO_RX_COALESCING_TICKS,
				ADAPTIVE_LO_TX_MAX_COALESCED_FRAMES,
				ADAPTIVE_LO_RX_MAX_COALESCED_FRAMES_DURING_INT);
		}
	}
	else if (total_delta < ADAPTIVE_HI_PKT_THRESH) {
		if (pUmDevice->rx_curr_coalesce_frames !=
			DEFAULT_RX_MAX_COALESCED_FRAMES) {

			bcm5700_do_adapt_coalesce(pUmDevice,
				DEFAULT_RX_MAX_COALESCED_FRAMES,
				DEFAULT_RX_COALESCING_TICKS,
				DEFAULT_TX_MAX_COALESCED_FRAMES,
				DEFAULT_RX_MAX_COALESCED_FRAMES_DURING_INT);
		}
	}
	else {
		if (pUmDevice->rx_curr_coalesce_frames !=
			ADAPTIVE_HI_RX_MAX_COALESCED_FRAMES) {

			bcm5700_do_adapt_coalesce(pUmDevice,
				ADAPTIVE_HI_RX_MAX_COALESCED_FRAMES,
				ADAPTIVE_HI_RX_COALESCING_TICKS,
				ADAPTIVE_HI_TX_MAX_COALESCED_FRAMES,
				ADAPTIVE_HI_RX_MAX_COALESCED_FRAMES_DURING_INT);
		}
	}
	return 0;
}
#endif
#endif

STATIC void
bcm5700_reset(struct net_device *dev)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice;
	unsigned long flags;

	netif_stop_queue(dev);
	bcm5700_intr_off(pUmDevice);
	BCM5700_PHY_LOCK(pUmDevice, flags);
	LM_ResetAdapter(pDevice);	
	bcm5700_set_vlan_mode(pUmDevice);
	bcm5700_init_counters(pUmDevice);
	if (memcmp(dev->dev_addr, pDevice->NodeAddress, 6)) {
		LM_SetMacAddress(pDevice, dev->dev_addr);
	}
	BCM5700_PHY_UNLOCK(pUmDevice, flags);
	atomic_set(&pUmDevice->intr_sem, 1);
	bcm5700_intr_on(pUmDevice);
	netif_wake_queue(dev);
}

STATIC void
bcm5700_set_vlan_mode(UM_DEVICE_BLOCK *pUmDevice)
{
	LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev;
	LM_UINT32 ReceiveMask = pDevice->ReceiveMask;
	int vlan_tag_mode = pUmDevice->vlan_tag_mode;

	if (vlan_tag_mode == VLAN_TAG_MODE_AUTO_STRIP) {
		if (pDevice->EnableAsf) {
			vlan_tag_mode = VLAN_TAG_MODE_FORCED_STRIP;
		}
		else {
			vlan_tag_mode = VLAN_TAG_MODE_NORMAL_STRIP;
		}
	}
	if (vlan_tag_mode == VLAN_TAG_MODE_NORMAL_STRIP) {
		ReceiveMask |= LM_KEEP_VLAN_TAG;
#ifdef BCM_VLAN
		if (pUmDevice->vlgrp)
			ReceiveMask &= ~LM_KEEP_VLAN_TAG;
#endif
#ifdef NICE_SUPPORT
		if (pUmDevice->nice_rx)
			ReceiveMask &= ~LM_KEEP_VLAN_TAG;
#endif
	}
	else if (vlan_tag_mode == VLAN_TAG_MODE_FORCED_STRIP) {
		ReceiveMask &= ~LM_KEEP_VLAN_TAG;
	}
	if (ReceiveMask != pDevice->ReceiveMask)
		LM_SetReceiveMask(pDevice, ReceiveMask);
}

#ifdef BCM_VLAN
STATIC void
bcm5700_vlan_rx_register(struct net_device *dev, struct vlan_group *vlgrp)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) dev->priv;

	bcm5700_intr_off(pUmDevice);
	pUmDevice->vlgrp = vlgrp;
	bcm5700_set_vlan_mode(pUmDevice);
	bcm5700_intr_on(pUmDevice);
}

STATIC void
bcm5700_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) dev->priv;

	bcm5700_intr_off(pUmDevice);
	if (pUmDevice->vlgrp) {
		pUmDevice->vlgrp->vlan_devices[vid] = NULL;
	}
	bcm5700_intr_on(pUmDevice);
}
#endif

STATIC int
bcm5700_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice;
	PLM_PACKET pPacket;
	PUM_PACKET pUmPacket;
	unsigned long flags = 0;
	int frag_no;
#ifdef NICE_SUPPORT
	vlan_tag_t *vlan_tag;
#endif
#ifdef BCM_TSO
	LM_UINT32 mss;
	uint16_t ip_tcp_len, tcp_opt_len, tcp_seg_flags;
#endif

	if ((pDevice->LinkStatus == LM_STATUS_LINK_DOWN) ||
		!pDevice->InitDone || pUmDevice->suspended)
	{
		dev_kfree_skb(skb);
		return 0;
	}
	
#if (LINUX_VERSION_CODE < 0x02032b)
	if (test_and_set_bit(0, &dev->tbusy)) {
		return 1;
	}
#endif

	if (pUmDevice->do_global_lock && pUmDevice->interrupt) {
		netif_stop_queue(dev);
		pUmDevice->tx_queued = 1;
		if (!pUmDevice->interrupt) {
			netif_wake_queue(dev);
			pUmDevice->tx_queued = 0;
		}
		return 1;
	}

	pPacket = (PLM_PACKET)
		QQ_PopHead(&pDevice->TxPacketFreeQ.Container);
	if (pPacket == 0) {
		netif_stop_queue(dev);
		pUmDevice->tx_full = 1;
		if (QQ_GetEntryCnt(&pDevice->TxPacketFreeQ.Container)) {
			netif_wake_queue(dev);
			pUmDevice->tx_full = 0;
		}
		return 1;
	}
	pUmPacket = (PUM_PACKET) pPacket;
	pUmPacket->skbuff = skb;

	if (skb->ip_summed == CHECKSUM_HW) {
		pPacket->Flags = SND_BD_FLAG_TCP_UDP_CKSUM;
#if TIGON3_DEBUG
		pUmDevice->tx_chksum_count++;
#endif
	}
	else {
		pPacket->Flags = 0;
	}
#if MAX_SKB_FRAGS
	frag_no = skb_shinfo(skb)->nr_frags;
#else
	frag_no = 0;
#endif
	if (atomic_read(&pDevice->SendBdLeft) < (frag_no + 1)) {
		netif_stop_queue(dev);
		pUmDevice->tx_full = 1;
		QQ_PushHead(&pDevice->TxPacketFreeQ.Container, pPacket);
		if (atomic_read(&pDevice->SendBdLeft) >= (frag_no + 1)) {
			netif_wake_queue(dev);
			pUmDevice->tx_full = 0;
		}
		return 1;
	}

	pPacket->u.Tx.FragCount = frag_no + 1;
#if TIGON3_DEBUG
	if (pPacket->u.Tx.FragCount > 1)
		pUmDevice->tx_zc_count++;
#endif

#ifdef BCM_VLAN
	if (pUmDevice->vlgrp && vlan_tx_tag_present(skb)) {
		pPacket->VlanTag = vlan_tx_tag_get(skb);
		pPacket->Flags |= SND_BD_FLAG_VLAN_TAG;
	}
#endif
#ifdef NICE_SUPPORT
	vlan_tag = (vlan_tag_t *) &skb->cb[0];
	if (vlan_tag->signature == 0x5555) {
		pPacket->VlanTag = vlan_tag->tag;
		pPacket->Flags |= SND_BD_FLAG_VLAN_TAG;
		vlan_tag->signature = 0;
	}
#endif

#ifdef BCM_TSO
	if ((mss = (LM_UINT32) skb_shinfo(skb)->tso_size) &&
		(skb->len > pDevice->TxMtu)) {

		pUmDevice->tso_pkt_count++;

		pPacket->Flags |= SND_BD_FLAG_CPU_PRE_DMA |
			SND_BD_FLAG_CPU_POST_DMA;

		tcp_opt_len = 0;
		if (skb->h.th->doff > 5) {
			tcp_opt_len = (skb->h.th->doff - 5) << 2;
		}
		ip_tcp_len = (skb->nh.iph->ihl << 2) + sizeof(struct tcphdr);
		skb->nh.iph->check = 0;
		skb->h.th->check = ~csum_tcpudp_magic(skb->nh.iph->saddr,
			skb->nh.iph->daddr, 0, IPPROTO_TCP, 0);

		skb->nh.iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len);
		tcp_seg_flags = 0;
		if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5705) {
			if (tcp_opt_len || (skb->nh.iph->ihl > 5)) {
				tcp_seg_flags =
					((skb->nh.iph->ihl - 5) +
					(tcp_opt_len >> 2)) << 11;
			}
		}
		else {
			if (tcp_opt_len || (skb->nh.iph->ihl > 5)) {
				pPacket->Flags |=
					((skb->nh.iph->ihl - 5) +
					(tcp_opt_len >> 2)) << 12;
			}
		}
		pPacket->u.Tx.MaxSegmentSize = mss | tcp_seg_flags;
	}
	else
	{
		pPacket->u.Tx.MaxSegmentSize = 0;
	}
#endif
	BCM5700_LOCK(pUmDevice, flags);
	LM_SendPacket(pDevice, pPacket);
	BCM5700_UNLOCK(pUmDevice, flags);

#if (LINUX_VERSION_CODE < 0x02032b)
	netif_wake_queue(dev);
#endif
	dev->trans_start = jiffies;
	return 0;
}

#ifdef BCM_NAPI_RXPOLL
STATIC int
bcm5700_poll(struct net_device *dev, int *budget)
{
	int orig_budget = *budget;
	int work_done;
	UM_DEVICE_BLOCK *pUmDevice = (UM_DEVICE_BLOCK *) dev->priv;
	LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev;

	if (orig_budget > dev->quota)
		orig_budget = dev->quota;

	work_done = LM_ServiceRxPoll(pDevice, orig_budget);
	*budget -= work_done;
	dev->quota -= work_done;

	if (QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container)) {
		replenish_rx_buffers(pUmDevice, 0);
	}
	if (work_done) {
		MM_IndicateRxPackets(pDevice);
		LM_QueueRxPackets(pDevice);
	}
	if ((work_done < orig_budget) || atomic_read(&pUmDevice->intr_sem) ||
		pUmDevice->suspended) {

		netif_rx_complete(dev);
		REG_WR(pDevice, Grc.Mode, pDevice->GrcMode);
		pDevice->RxPoll = FALSE;
		if ((pDevice->pStatusBlkVirt->Status & STATUS_BLOCK_UPDATED) ||
			(pDevice->pStatusBlkVirt->Idx[0].RcvProdIdx !=
			pDevice->RcvRetConIdx)) {

			REG_WR(pDevice, HostCoalesce.Mode,
				pDevice->CoalesceMode | HOST_COALESCE_ENABLE |
				HOST_COALESCE_NOW);
		}
		return 0;
	}
	return 1;
}
#endif /* BCM_NAPI_RXPOLL */

STATIC irqreturn_t
bcm5700_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
{
	struct net_device *dev = (struct net_device *)dev_instance;
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice;
	LM_UINT32 oldtag, newtag;
	int i, max_intr_loop;
#ifdef BCM_TASKLET
	int repl_buf_count;
#endif
	unsigned int handled = 1;

	if (!pDevice->InitDone) {
		handled = 0;
		return IRQ_RETVAL(handled);
	}

	if (atomic_read(&pUmDevice->intr_sem)) {
		MB_REG_WR(pDevice, Mailbox.Interrupt[0].Low, 1);
		handled = 0;
		return IRQ_RETVAL(handled);
	}

	bcm5700_intr_lock(pUmDevice);
	if (test_and_set_bit(0, (void*)&pUmDevice->interrupt)) {
		printk(KERN_ERR "%s: Duplicate entry of the interrupt handler by "
			   "processor %d.\n",
			   dev->name, hard_smp_processor_id());
		bcm5700_intr_unlock(pUmDevice);
		handled = 0;
		return IRQ_RETVAL(handled);
	}

	if ((pDevice->pStatusBlkVirt->Status & STATUS_BLOCK_UPDATED) ||
#ifdef NICE_SUPPORT
		pUmDevice->adapter_just_inited || pUmDevice->intr_test)
#else
		pUmDevice->adapter_just_inited)
#endif
	{

#ifdef NICE_SUPPORT
		if (pUmDevice->intr_test) {
			if (!(REG_RD(pDevice, PciCfg.PciState) &
				T3_PCI_STATE_INTERRUPT_NOT_ACTIVE)) {
				pUmDevice->intr_test_result = 1;
			}
			pUmDevice->intr_test = 0;
		}
#endif
#ifdef BCM_NAPI_RXPOLL
		max_intr_loop = 1;
#else
		max_intr_loop = 50;
#endif
		if (pDevice->UseTaggedStatus) {
			MB_REG_WR(pDevice, Mailbox.Interrupt[0].Low, 1);
			oldtag = pDevice->pStatusBlkVirt->StatusTag;

			for (i = 0; ; i++) {
   				pDevice->pStatusBlkVirt->Status &=
					~STATUS_BLOCK_UPDATED;

				LM_ServiceInterrupts(pDevice);
				newtag = pDevice->pStatusBlkVirt->StatusTag;
				if ((newtag == oldtag) || (i > max_intr_loop)) {
					MB_REG_WR(pDevice,
						Mailbox.Interrupt[0].Low,
						oldtag << 24);
					pDevice->LastTag = oldtag;
					if (pDevice->UndiFix) {
						REG_WR(pDevice, Grc.LocalCtrl,
						pDevice->GrcLocalCtrl | 0x2);
					}
					break;
				}
				oldtag = newtag;
			}
		}
		else {
			i = 0;
			do {
				uint dummy;

				MB_REG_WR(pDevice, Mailbox.Interrupt[0].Low, 1);
   				pDevice->pStatusBlkVirt->Status &=
					~STATUS_BLOCK_UPDATED;
				LM_ServiceInterrupts(pDevice);
				MB_REG_WR(pDevice, Mailbox.Interrupt[0].Low, 0);
				dummy = MB_REG_RD(pDevice,
					Mailbox.Interrupt[0].Low);
				i++;
			}
			while ((pDevice->pStatusBlkVirt->Status &
				STATUS_BLOCK_UPDATED) && (i < max_intr_loop));
			if (pDevice->UndiFix) {
				REG_WR(pDevice, Grc.LocalCtrl,
					pDevice->GrcLocalCtrl | 0x2);
			}
		}
	}
	else {
		handled = 0;
	}
#ifdef BCM_TASKLET
	repl_buf_count = QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container);
	if (((repl_buf_count > pUmDevice->rx_buf_repl_panic_thresh) ||
		pDevice->QueueAgain) &&
		(!test_and_set_bit(0, &pUmDevice->tasklet_busy))) {

		replenish_rx_buffers(pUmDevice,
			pUmDevice->rx_buf_repl_isr_limit);
		clear_bit(0, (void*)&pUmDevice->tasklet_busy);
	}
	else if ((repl_buf_count > pUmDevice->rx_buf_repl_thresh) &&
		!pUmDevice->tasklet_pending) {

		pUmDevice->tasklet_pending = 1;
		tasklet_schedule(&pUmDevice->tasklet);
	}
#else
#ifdef BCM_NAPI_RXPOLL
	if (!pDevice->RxPoll &&
		QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container)) {
		pDevice->RxPoll = 1;
		MM_ScheduleRxPoll(pDevice);
	}
#else
	if (QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container)) {
		replenish_rx_buffers(pUmDevice, 0);
	}

	if (QQ_GetEntryCnt(&pDevice->RxPacketFreeQ.Container) ||
		pDevice->QueueAgain) {

		LM_QueueRxPackets(pDevice);
	}
#endif
#endif

	clear_bit(0, (void*)&pUmDevice->interrupt);
	bcm5700_intr_unlock(pUmDevice);
	if (pUmDevice->tx_queued) {
		pUmDevice->tx_queued = 0;
		netif_wake_queue(dev);
	}
	return IRQ_RETVAL(handled);
}


#ifdef BCM_TASKLET
STATIC void
bcm5700_tasklet(unsigned long data)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)data;

	/* RH 7.2 Beta 3 tasklets are reentrant */
	if (test_and_set_bit(0, &pUmDevice->tasklet_busy)) {
		pUmDevice->tasklet_pending = 0;
		return;
	}

	pUmDevice->tasklet_pending = 0;
	if (pUmDevice->opened && !pUmDevice->suspended)
		replenish_rx_buffers(pUmDevice, 0);

	clear_bit(0, &pUmDevice->tasklet_busy);
}
#endif

STATIC int
bcm5700_close(struct net_device *dev)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice;

#if (LINUX_VERSION_CODE < 0x02032b)
	dev->start = 0;
#endif
	netif_stop_queue(dev);
	pUmDevice->opened = 0;

	if (tigon3_debug > 1)
		printk(KERN_DEBUG "%s: Shutting down Tigon3\n",
			   dev->name);

	LM_MulticastClear(pDevice);
	bcm5700_shutdown(pUmDevice);
	del_timer(&pUmDevice->timer);

	free_irq(dev->irq, dev);
#if (LINUX_VERSION_CODE < 0x020300)
	MOD_DEC_USE_COUNT;
#endif
	LM_SetPowerState(pDevice, LM_POWER_STATE_D3);
	bcm5700_freemem(dev);

	return 0;
}

STATIC int
bcm5700_freemem(struct net_device *dev)
{
	int i;
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;

	for (i = 0; i < pUmDevice->mem_list_num; i++) {
		if (pUmDevice->mem_size_list[i] == 0) {
			kfree(pUmDevice->mem_list[i]);
		}
		else {
			pci_free_consistent(pUmDevice->pdev,
				(size_t) pUmDevice->mem_size_list[i],
				pUmDevice->mem_list[i],
				pUmDevice->dma_list[i]);
		}
	}
	pUmDevice->mem_list_num = 0;

#ifdef NICE_SUPPORT
	if (!pUmDevice->opened) {
		for (i = 0; i < MAX_MEM2; i++) {
			if (pUmDevice->mem_size_list2[i]) {
				bcm5700_freemem2(pUmDevice, i);
			}
		}
	}
#endif
	return 0;
}

#ifdef NICE_SUPPORT
/* Frees consistent memory allocated through ioctl */
/* The memory to be freed is in mem_list2[index] */
STATIC int
bcm5700_freemem2(UM_DEVICE_BLOCK *pUmDevice, int index)
{
#if (LINUX_VERSION_CODE >= 0x020400)
	void *ptr;
	struct page *pg, *last_pg;

	/* Probably won't work on some architectures */
	ptr = pUmDevice->mem_list2[index],
	pg = virt_to_page(ptr);
	last_pg = virt_to_page(ptr + pUmDevice->mem_size_list2[index] - 1);
	for (; ; pg++) {
#if (LINUX_VERSION_CODE > 0x020500)
		ClearPageReserved(pg);
#else
		mem_map_unreserve(pg);
#endif
		if (pg == last_pg)
			break;
	}
	pci_free_consistent(pUmDevice->pdev,
		(size_t) pUmDevice->mem_size_list2[index],
		pUmDevice->mem_list2[index],
		pUmDevice->dma_list2[index]);
	pUmDevice->mem_size_list2[index] = 0;
#endif
	return 0;
}
#endif

unsigned long
bcm5700_crc_count(PUM_DEVICE_BLOCK pUmDevice)
{
	PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev;
	LM_UINT32 Value32;
	PT3_STATS_BLOCK pStats = (PT3_STATS_BLOCK) pDevice->pStatsBlkVirt;
	unsigned long flags;

	if ((T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5700 ||
		T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5701) &&
		!pDevice->EnableTbi) { 

		if (!pUmDevice->opened || !pDevice->InitDone ||
			pUmDevice->adapter_just_inited) {

			return 0;
		}

		/* regulate MDIO access during run time */
		if (pUmDevice->crc_counter_expiry > 0)
			return pDevice->PhyCrcCount;

		pUmDevice->crc_counter_expiry = (5 * HZ) /
			pUmDevice->timer_interval;

		BCM5700_PHY_LOCK(pUmDevice, flags);
		LM_ReadPhy(pDevice, 0x1e, &Value32);
		if ((Value32 & 0x8000) == 0)
			LM_WritePhy(pDevice, 0x1e, Value32 | 0x8000);
		LM_ReadPhy(pDevice, 0x14, &Value32);
		BCM5700_PHY_UNLOCK(pUmDevice, flags);
		/* Sometimes data on the MDIO bus can be corrupted */
		if (Value32 != 0xffff)
			pDevice->PhyCrcCount += Value32;
		return pDevice->PhyCrcCount;
	}
	else if (pStats == 0) {
		return 0;
	}
	else {
		return (MM_GETSTATS(pStats->dot3StatsFCSErrors));
	}
}

STATIC struct net_device_stats *
bcm5700_get_stats(struct net_device *dev)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice;
	PT3_STATS_BLOCK pStats = (PT3_STATS_BLOCK) pDevice->pStatsBlkVirt;
	struct net_device_stats *p_netstats = &pUmDevice->stats;

	if (pStats == 0)
		return p_netstats;

	/* Get stats from LM */
	p_netstats->rx_packets =
		MM_GETSTATS(pStats->ifHCInUcastPkts) +
		MM_GETSTATS(pStats->ifHCInMulticastPkts) +
		MM_GETSTATS(pStats->ifHCInBroadcastPkts);
	p_netstats->tx_packets = 
		MM_GETSTATS(pStats->ifHCOutUcastPkts) +
		MM_GETSTATS(pStats->ifHCOutMulticastPkts) +
		MM_GETSTATS(pStats->ifHCOutBroadcastPkts);
	p_netstats->rx_bytes = MM_GETSTATS(pStats->ifHCInOctets);
	p_netstats->tx_bytes = MM_GETSTATS(pStats->ifHCOutOctets);
	p_netstats->tx_errors =
		MM_GETSTATS(pStats->dot3StatsInternalMacTransmitErrors) +
		MM_GETSTATS(pStats->dot3StatsCarrierSenseErrors) +
		MM_GETSTATS(pStats->ifOutDiscards) +
		MM_GETSTATS(pStats->ifOutErrors);
	p_netstats->multicast = MM_GETSTATS(pStats->ifHCInMulticastPkts);
	p_netstats->collisions = MM_GETSTATS(pStats->etherStatsCollisions);
	p_netstats->rx_length_errors =
		MM_GETSTATS(pStats->dot3StatsFramesTooLong) +
		MM_GETSTATS(pStats->etherStatsUndersizePkts);
	p_netstats->rx_over_errors = MM_GETSTATS(pStats->nicNoMoreRxBDs);
	p_netstats->rx_frame_errors =
		MM_GETSTATS(pStats->dot3StatsAlignmentErrors);
	p_netstats->rx_crc_errors = bcm5700_crc_count(pUmDevice);
	p_netstats->rx_errors =
		p_netstats->rx_length_errors +
		p_netstats->rx_over_errors +
		p_netstats->rx_frame_errors +
		p_netstats->rx_crc_errors +
		MM_GETSTATS(pStats->etherStatsFragments) +
		MM_GETSTATS(pStats->etherStatsJabbers);
	
	p_netstats->tx_aborted_errors = MM_GETSTATS(pStats->ifOutDiscards);
	p_netstats->tx_carrier_errors =
		MM_GETSTATS(pStats->dot3StatsCarrierSenseErrors);

	return p_netstats;
}

#ifdef SIOCETHTOOL

#ifdef ETHTOOL_GSTRINGS

#define ETH_NUM_STATS 26

struct {
	char string[ETH_GSTRING_LEN];
} bcm5700_stats_str_arr[ETH_NUM_STATS] = {
	{ "rx_unicast_packets" },
	{ "rx_multicast_packets" },
	{ "rx_broadcast_packets" },
	{ "rx_bytes" },
	{ "rx_fragments" },
	{ "rx_crc_errors" },
	{ "rx_align_errors" },
	{ "rx_xon_frames" },
	{ "rx_xoff_frames" },
	{ "rx_long_frames" },
	{ "rx_short_frames" },
	{ "rx_discards" },
	{ "rx_errors" },
	{ "tx_unicast_packets" },
	{ "tx_multicast_packets" },
	{ "tx_broadcast_packets" },
	{ "tx_bytes" },
	{ "tx_collisions" },
	{ "tx_deferred" },
	{ "tx_excess_collisions" },
	{ "tx_late_collisions" },
	{ "tx_xon_frames" },
	{ "tx_xoff_frames" },
	{ "tx_internal_mac_errors" },
	{ "tx_carrier_errors" },
	{ "tx_errors" },
};

#define STATS_OFFSET(offset_name) ((OFFSETOF(T3_STATS_BLOCK, offset_name)) / sizeof(uint64_t))

#ifdef __BIG_ENDIAN
#define SWAP_DWORD_64(x) (x)
#else
#define SWAP_DWORD_64(x) ((x << 32) | (x >> 32))
#endif

unsigned long bcm5700_stats_offset_arr[ETH_NUM_STATS] = {
	STATS_OFFSET(ifHCInUcastPkts),
	STATS_OFFSET(ifHCInMulticastPkts),
	STATS_OFFSET(ifHCInBroadcastPkts),
	STATS_OFFSET(ifHCInOctets),
	STATS_OFFSET(etherStatsFragments),
	STATS_OFFSET(dot3StatsFCSErrors),
	STATS_OFFSET(dot3StatsAlignmentErrors),
	STATS_OFFSET(xonPauseFramesReceived),
	STATS_OFFSET(xoffPauseFramesReceived),
	STATS_OFFSET(dot3StatsFramesTooLong),
	STATS_OFFSET(etherStatsUndersizePkts),
	STATS_OFFSET(ifInDiscards),
	STATS_OFFSET(ifInErrors),
	STATS_OFFSET(ifHCOutUcastPkts),
	STATS_OFFSET(ifHCOutMulticastPkts),
	STATS_OFFSET(ifHCOutBroadcastPkts),
	STATS_OFFSET(ifHCOutOctets),
	STATS_OFFSET(etherStatsCollisions),
	STATS_OFFSET(dot3StatsDeferredTransmissions),
	STATS_OFFSET(dot3StatsExcessiveCollisions),
	STATS_OFFSET(dot3StatsLateCollisions),
	STATS_OFFSET(outXonSent),
	STATS_OFFSET(outXoffSent),
	STATS_OFFSET(dot3StatsInternalMacTransmitErrors),
	STATS_OFFSET(dot3StatsCarrierSenseErrors),
	STATS_OFFSET(ifOutErrors),
};

#endif /* ETHTOOL_GSTRINGS */

static int netdev_ethtool_ioctl(struct net_device *dev, void *useraddr)
{
	struct ethtool_cmd ethcmd;
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice;
		
	if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
		return -EFAULT;

        switch (ethcmd.cmd) {
#ifdef ETHTOOL_GDRVINFO
        case ETHTOOL_GDRVINFO: {
		struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};

		strcpy(info.driver,  bcm5700_driver);
#if INCLUDE_5701_AX_FIX
		if(pDevice->ChipRevId == T3_CHIP_ID_5701_A0) {
			extern int t3FwReleaseMajor;
			extern int t3FwReleaseMinor;
			extern int t3FwReleaseFix;

			sprintf(info.fw_version, "%i.%i.%i",
				t3FwReleaseMajor, t3FwReleaseMinor, 
				t3FwReleaseFix);
		}
#endif
		strcpy(info.version, bcm5700_version);
		strcpy(info.bus_info, pUmDevice->pdev->slot_name);
#ifdef ETHTOOL_GEEPROM
		BCM_EEDUMP_LEN(&info, pDevice->NvramSize);
#endif
#ifdef ETHTOOL_GREGS
		/* dump everything, including holes in the register space */
		info.regdump_len = 0x6c00;
#endif
#ifdef ETHTOOL_GSTATS
		info.n_stats = ETH_NUM_STATS;
#endif
		if (copy_to_user(useraddr, &info, sizeof(info)))
			return -EFAULT;
		return 0;
	}
#endif
        case ETHTOOL_GSET: {
		if (pDevice->EnableTbi) {
			ethcmd.supported =
				(SUPPORTED_1000baseT_Full |
				SUPPORTED_Autoneg);
			ethcmd.supported |= SUPPORTED_FIBRE;
			ethcmd.port = PORT_FIBRE;
		}
		else {
			ethcmd.supported =
				(SUPPORTED_10baseT_Half |
				SUPPORTED_10baseT_Full |
				SUPPORTED_100baseT_Half |
				SUPPORTED_100baseT_Full |
				SUPPORTED_1000baseT_Half |
				SUPPORTED_1000baseT_Full |
				SUPPORTED_Autoneg);
			ethcmd.supported |= SUPPORTED_TP;
			ethcmd.port = PORT_TP;
		}

		ethcmd.transceiver = XCVR_INTERNAL;
		ethcmd.phy_address = 0;

		if (pUmDevice->line_speed == 1000)
			ethcmd.speed = SPEED_1000;
		else if (pUmDevice->line_speed == 100)
			ethcmd.speed = SPEED_100;
		else if (pUmDevice->line_speed == 10)
			ethcmd.speed = SPEED_10;
		else
			ethcmd.speed = 0;

		if (pDevice->DuplexMode == LM_DUPLEX_MODE_FULL)
			ethcmd.duplex = DUPLEX_FULL;
		else
			ethcmd.duplex = DUPLEX_HALF;

		if (pDevice->DisableAutoNeg == FALSE) {
			ethcmd.autoneg = AUTONEG_ENABLE;
			ethcmd.advertising = ADVERTISED_Autoneg;
			if (pDevice->EnableTbi) {
				ethcmd.advertising |=
					ADVERTISED_1000baseT_Full |
					ADVERTISED_FIBRE;
			}
			else {
				ethcmd.advertising |=
					ADVERTISED_TP;
				if (pDevice->advertising &
					PHY_AN_AD_10BASET_HALF) {

					ethcmd.advertising |=
						ADVERTISED_10baseT_Half;
				}
				if (pDevice->advertising &
					PHY_AN_AD_10BASET_FULL) {

					ethcmd.advertising |=
						ADVERTISED_10baseT_Full;
				}
				if (pDevice->advertising &
					PHY_AN_AD_100BASETX_HALF) {

					ethcmd.advertising |=
						ADVERTISED_100baseT_Half;
				}
				if (pDevice->advertising &
					PHY_AN_AD_100BASETX_FULL) {

					ethcmd.advertising |=
						ADVERTISED_100baseT_Full;
				}
				if (pDevice->advertising1000 &
					BCM540X_AN_AD_1000BASET_HALF) {

					ethcmd.advertising |=
						ADVERTISED_1000baseT_Half;
				}
				if (pDevice->advertising1000 &
					BCM540X_AN_AD_1000BASET_FULL) {

					ethcmd.advertising |=
						ADVERTISED_1000baseT_Full;
				}
			}
		}
		else {
			ethcmd.autoneg = AUTONEG_DISABLE;
			ethcmd.advertising = 0;
		}

		ethcmd.maxtxpkt = pDevice->TxMaxCoalescedFrames;
		ethcmd.maxrxpkt = pDevice->RxMaxCoalescedFrames;

		if(copy_to_user(useraddr, &ethcmd, sizeof(ethcmd)))
			return -EFAULT;
		return 0;
	}
	case ETHTOOL_SSET: {
		unsigned long flags;

		if(!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (ethcmd.autoneg == AUTONEG_ENABLE) {
			pDevice->RequestedLineSpeed = LM_LINE_SPEED_AUTO;
			pDevice->RequestedDuplexMode = LM_DUPLEX_MODE_UNKNOWN;
			pDevice->DisableAutoNeg = FALSE;
		}
		else {
			if (ethcmd.speed == SPEED_1000) {
				if (!pDevice->EnableTbi || pDevice->No1000)
					return -EINVAL;

				pDevice->RequestedLineSpeed =
					LM_LINE_SPEED_1000MBPS;

				pDevice->RequestedDuplexMode =
					LM_DUPLEX_MODE_FULL;
			}
			else if (ethcmd.speed == SPEED_100) {
				pDevice->RequestedLineSpeed =
					LM_LINE_SPEED_100MBPS;
			}
			else if (ethcmd.speed == SPEED_10) {
				pDevice->RequestedLineSpeed =
					LM_LINE_SPEED_10MBPS;
			}
			else {
				return -EINVAL;
			}

			pDevice->DisableAutoNeg = TRUE;
			if (ethcmd.duplex == DUPLEX_FULL) {
				pDevice->RequestedDuplexMode =
					LM_DUPLEX_MODE_FULL;
			}
			else {
				pDevice->RequestedDuplexMode =
					LM_DUPLEX_MODE_HALF;
			}
		}
		BCM5700_PHY_LOCK(pUmDevice, flags);
		LM_SetupPhy(pDevice);
		BCM5700_PHY_UNLOCK(pUmDevice, flags);
		return 0;
	}
#ifdef ETHTOOL_GWOL
#ifdef BCM_WOL
	case ETHTOOL_GWOL: {
		struct ethtool_wolinfo wol = {ETHTOOL_GWOL};

		if (pDevice->EnableTbi && !pDevice->FiberWolCapable) {
			wol.supported = 0;
			wol.wolopts = 0;
		}
		else {
			wol.supported = WAKE_MAGIC;
			if (pDevice->WakeUpMode == LM_WAKE_UP_MODE_MAGIC_PACKET)
			{
				wol.wolopts = WAKE_MAGIC;
			}
			else {
				wol.wolopts = 0;
			}
		}
		if (copy_to_user(useraddr, &wol, sizeof(wol)))
			return -EFAULT;
		return 0;
	}
	case ETHTOOL_SWOL: {
		struct ethtool_wolinfo wol;

		if(!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (copy_from_user(&wol, useraddr, sizeof(wol)))
			return -EFAULT;
		if (pDevice->EnableTbi && !pDevice->FiberWolCapable &&
			wol.wolopts) {
			return -EINVAL;
		}
		
		if ((wol.wolopts & ~WAKE_MAGIC) != 0) {
			return -EINVAL;
		}
		if (wol.wolopts & WAKE_MAGIC) {
			pDevice->WakeUpModeCap = LM_WAKE_UP_MODE_MAGIC_PACKET;
			pDevice->WakeUpMode = LM_WAKE_UP_MODE_MAGIC_PACKET;
		}
		else {
			pDevice->WakeUpModeCap = LM_WAKE_UP_MODE_NONE;
			pDevice->WakeUpMode = LM_WAKE_UP_MODE_NONE;
		}
		return 0;
        }
#endif
#endif
#ifdef ETHTOOL_GLINK
	case ETHTOOL_GLINK: {
		struct ethtool_value edata = {ETHTOOL_GLINK};

		if (pUmDevice->delayed_link_ind > 0)
			return -EOPNOTSUPP;

		if (pDevice->LinkStatus == LM_STATUS_LINK_ACTIVE) {
			edata.data =  1;
		}
		else {
			edata.data =  0;
		}
		if (copy_to_user(useraddr, &edata, sizeof(edata)))
			return -EFAULT;
		return 0;
	}
#endif
#ifdef ETHTOOL_NWAY_RST
	case ETHTOOL_NWAY_RST: {
		LM_UINT32 phyctrl;
		unsigned long flags;

		if(!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (pDevice->DisableAutoNeg) {
			return -EINVAL;
		}
		BCM5700_PHY_LOCK(pUmDevice, flags);
		if (pDevice->EnableTbi) {
			pDevice->RequestedLineSpeed = LM_LINE_SPEED_1000MBPS;
			pDevice->DisableAutoNeg = TRUE;
			LM_SetupPhy(pDevice);

			pDevice->RequestedLineSpeed = LM_LINE_SPEED_AUTO;
			pDevice->DisableAutoNeg = FALSE;
			LM_SetupPhy(pDevice);
		}
		else {
			if ((T3_ASIC_REV(pDevice->ChipRevId) ==
					T3_ASIC_REV_5703) ||
				(T3_ASIC_REV(pDevice->ChipRevId) ==
					T3_ASIC_REV_5704) ||
				(T3_ASIC_REV(pDevice->ChipRevId) ==
					T3_ASIC_REV_5705))
			{
				LM_ResetPhy(pDevice);
				LM_SetupPhy(pDevice);
			}
			LM_ReadPhy(pDevice, PHY_CTRL_REG, &phyctrl);
			LM_WritePhy(pDevice, PHY_CTRL_REG, phyctrl |
				PHY_CTRL_AUTO_NEG_ENABLE |
				PHY_CTRL_RESTART_AUTO_NEG);
		}
		BCM5700_PHY_UNLOCK(pUmDevice, flags);
		return 0;
	}
#endif
#ifdef ETHTOOL_GEEPROM
	case ETHTOOL_GEEPROM: {
		struct ethtool_eeprom eeprom;
		LM_UINT32 *buf = 0;
		LM_UINT32 buf1[64/4];
		int i, j, offset, len;

		if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
			return -EFAULT;
		
		if (eeprom.offset >= pDevice->NvramSize)
			return -EFAULT;

		/* maximum data limited */
		/* to read more, call again with a different offset */
		if (eeprom.len > 0x800) {
			eeprom.len = 0x800;
			if (copy_to_user(useraddr, &eeprom, sizeof(eeprom)))
				return -EFAULT;
		}

		if (eeprom.len > 64) {
			buf = kmalloc(eeprom.len, GFP_KERNEL);
			if (!buf)
				return -ENOMEM;
		}
		else {
			buf = buf1;
		}
		useraddr += offsetof(struct ethtool_eeprom, data);

		offset = eeprom.offset;
		len = eeprom.len;
		if (offset & 3) {
			offset &= 0xfffffffc;
			len += (offset & 3);
		}
		len = (len + 3) & 0xfffffffc;
		for (i = 0, j = 0; j < len; i++, j += 4) {
			if (LM_NvramRead(pDevice, offset + j, buf + i) !=
				LM_STATUS_SUCCESS) {
				break;
			}
		}
		if (j >= len) {
			buf += (eeprom.offset & 3);
			i = copy_to_user(useraddr, buf, eeprom.len);
		}
		if (eeprom.len > 64) {
			kfree(buf);
		}
		if ((j < len) || i)
			return -EFAULT;
		return 0;
	}
	case ETHTOOL_SEEPROM: {
		struct ethtool_eeprom eeprom;
		LM_UINT32 buf[64/4];
		int i, offset, len;

		if(!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
			return -EFAULT;
		
		if ((eeprom.offset & 3) || (eeprom.len & 3) ||
			(eeprom.offset >= pDevice->NvramSize)) {
			return -EFAULT;
		}

		if ((eeprom.offset + eeprom.len) >= pDevice->NvramSize) {
			eeprom.len = pDevice->NvramSize - eeprom.offset;
		}

		useraddr += offsetof(struct ethtool_eeprom, data);

		len = eeprom.len;
		offset = eeprom.offset;
		for (; len > 0; ) {
			if (len < 64)
				i = len;
			else
				i = 64;
			if (copy_from_user(&buf, useraddr, i))
				return -EFAULT;

			bcm5700_intr_off(pUmDevice);
#ifdef BCM_NAPI_RXPOLL
			/* Prevent race condition on Grc.Mode register */
			while (pDevice->RxPoll)
				yield();
#endif
			if (LM_NvramWriteBlock(pDevice, offset, buf, i/4) !=
				LM_STATUS_SUCCESS) {
				bcm5700_intr_on(pUmDevice);
				return -EFAULT;
			}
			bcm5700_intr_on(pUmDevice);
			len -= i;
			offset += i;
			useraddr += i;
		}
		return 0;
	}
#endif
#ifdef ETHTOOL_GREGS
#if (LINUX_VERSION_CODE >= 0x02040f)
	case ETHTOOL_GREGS: {
		struct ethtool_regs eregs;
		LM_UINT32 *buf, *buf1;
		unsigned int i;

		if(!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (pDevice->UndiFix)
			return -EOPNOTSUPP;
		if (copy_from_user(&eregs, useraddr, sizeof(eregs)))
			return -EFAULT;
		if (eregs.len > 0x6c00)
			eregs.len = 0x6c00;
		eregs.version = 0x0;
		if (copy_to_user(useraddr, &eregs, sizeof(eregs)))
			return -EFAULT;
		buf = buf1 = kmalloc(eregs.len, GFP_KERNEL);
		if (!buf)
			return -ENOMEM;
		for (i = 0; i < eregs.len; i = i + 4) {
			if ((i >= 0x5c00) && (i < 0x6000)) {
				/* skip FTQ register block */
				*buf1++ = 0;
				continue;
			}
			*buf1++ = REG_RD_OFFSET(pDevice, i);
		}
		i = copy_to_user(useraddr + sizeof(eregs), buf, eregs.len);
		kfree(buf);
		if (i)
			return -EFAULT;
		return 0;
	}
#endif
#endif
#ifdef ETHTOOL_GPAUSEPARAM
	case ETHTOOL_GPAUSEPARAM: {
		struct ethtool_pauseparam epause = { ETHTOOL_GPAUSEPARAM };

		epause.autoneg = !pDevice->DisableAutoNeg;
		epause.rx_pause = 
			(pDevice->FlowControlCap &
			LM_FLOW_CONTROL_RECEIVE_PAUSE) != 0;
		epause.tx_pause = 
			(pDevice->FlowControlCap &
			LM_FLOW_CONTROL_TRANSMIT_PAUSE) != 0;
		if (copy_to_user(useraddr, &epause, sizeof(epause)))
			return -EFAULT;

		return 0;
	}
	case ETHTOOL_SPAUSEPARAM: {
		struct ethtool_pauseparam epause;
		unsigned long flags;

		if(!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (copy_from_user(&epause, useraddr, sizeof(epause)))
			return -EFAULT;
		pDevice->FlowControlCap = 0;
		if (epause.autoneg && !pDevice->DisableAutoNeg) {
			pDevice->FlowControlCap |= LM_FLOW_CONTROL_AUTO_PAUSE;
		}
		if (epause.rx_pause)  {
			pDevice->FlowControlCap |=
				LM_FLOW_CONTROL_RECEIVE_PAUSE;
		}
		if (epause.tx_pause)  {
			pDevice->FlowControlCap |=
				LM_FLOW_CONTROL_TRANSMIT_PAUSE;
		}
		BCM5700_PHY_LOCK(pUmDevice, flags);
		LM_SetupPhy(pDevice);
		BCM5700_PHY_UNLOCK(pUmDevice, flags);

		return 0;
	}
#endif
#ifdef ETHTOOL_GRXCSUM
	case ETHTOOL_GRXCSUM: {
		struct ethtool_value edata = { ETHTOOL_GRXCSUM };

		edata.data = 
			(pDevice->TaskToOffload &
			LM_TASK_OFFLOAD_RX_TCP_CHECKSUM) != 0;
		if (copy_to_user(useraddr, &edata, sizeof(edata)))
			return -EFAULT;

		return 0;
	}
	case ETHTOOL_SRXCSUM: {
		struct ethtool_value edata;

		if(!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (copy_from_user(&edata, useraddr, sizeof(edata)))
			return -EFAULT;
		if (edata.data) {
			if (!(pDevice->TaskOffloadCap &
				LM_TASK_OFFLOAD_TX_TCP_CHECKSUM)) {

				return -EINVAL;
			}
			pDevice->TaskToOffload |=
				LM_TASK_OFFLOAD_RX_TCP_CHECKSUM |
				LM_TASK_OFFLOAD_RX_UDP_CHECKSUM;
		}
		else {
			pDevice->TaskToOffload &=
				~(LM_TASK_OFFLOAD_RX_TCP_CHECKSUM |
				LM_TASK_OFFLOAD_RX_UDP_CHECKSUM);
		}
		return 0;
	}
	case ETHTOOL_GTXCSUM: {
		struct ethtool_value edata = { ETHTOOL_GTXCSUM };

		edata.data = 
			(dev->features & NETIF_F_IP_CSUM) != 0;
		if (copy_to_user(useraddr, &edata, sizeof(edata)))
			return -EFAULT;

		return 0;
	}
	case ETHTOOL_STXCSUM: {
		struct ethtool_value edata;

		if(!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (copy_from_user(&edata, useraddr, sizeof(edata)))
			return -EFAULT;
		if (edata.data) {
			if (!(pDevice->TaskOffloadCap &
				LM_TASK_OFFLOAD_TX_TCP_CHECKSUM)) {

				return -EINVAL;
			}
			dev->features |= NETIF_F_IP_CSUM;
			pDevice->TaskToOffload |=
				LM_TASK_OFFLOAD_TX_TCP_CHECKSUM |
				LM_TASK_OFFLOAD_TX_UDP_CHECKSUM;
		}
		else {
			dev->features &= ~NETIF_F_IP_CSUM;
			pDevice->TaskToOffload &=
				~(LM_TASK_OFFLOAD_TX_TCP_CHECKSUM |
				LM_TASK_OFFLOAD_TX_UDP_CHECKSUM);
		}
		return 0;
	}
	case ETHTOOL_GSG: {
		struct ethtool_value edata = { ETHTOOL_GSG };

		edata.data = 
			(dev->features & NETIF_F_SG) != 0;
		if (copy_to_user(useraddr, &edata, sizeof(edata)))
			return -EFAULT;
		return 0;
	}
	case ETHTOOL_SSG: {
		struct ethtool_value edata;

		if(!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (copy_from_user(&edata, useraddr, sizeof(edata)))
			return -EFAULT;
		if (edata.data) {
			dev->features |= NETIF_F_SG;
		}
		else {
			dev->features &= ~NETIF_F_SG;
		}
		return 0;
	}
#endif
#ifdef ETHTOOL_GRINGPARAM
	case ETHTOOL_GRINGPARAM: {
		struct ethtool_ringparam ering = { ETHTOOL_GRINGPARAM };

		ering.rx_max_pending = T3_STD_RCV_RCB_ENTRY_COUNT - 1;
		ering.rx_pending = pDevice->RxStdDescCnt;
		ering.rx_mini_max_pending = 0;
		ering.rx_mini_pending = 0;
#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
		ering.rx_jumbo_max_pending = T3_JUMBO_RCV_RCB_ENTRY_COUNT - 1;
		ering.rx_jumbo_pending = pDevice->RxJumboDescCnt;
#else
		ering.rx_jumbo_max_pending = 0;
		ering.rx_jumbo_pending = 0;
#endif
		ering.tx_max_pending = MAX_TX_PACKET_DESC_COUNT - 1;
		ering.tx_pending = pDevice->TxPacketDescCnt;
		if (copy_to_user(useraddr, &ering, sizeof(ering)))
			return -EFAULT;
		return 0;
	}
#endif
#ifdef ETHTOOL_PHYS_ID
	case ETHTOOL_PHYS_ID: {
		struct ethtool_value edata;

		if(!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (copy_from_user(&edata, useraddr, sizeof(edata)))
			return -EFAULT;
		if (LM_BlinkLED(pDevice, edata.data) == LM_STATUS_SUCCESS)
			return 0;
		return -EINTR;
	}
#endif
#ifdef ETHTOOL_GSTRINGS
	case ETHTOOL_GSTRINGS: {
		struct ethtool_gstrings egstr = { ETHTOOL_GSTRINGS };

		if (copy_from_user(&egstr, useraddr, sizeof(egstr)))
			return -EFAULT;
		if (egstr.string_set != ETH_SS_STATS)
			return -EINVAL;
		egstr.len = ETH_NUM_STATS;
		if (copy_to_user(useraddr, &egstr, sizeof(egstr)))
			return -EFAULT;
		if (copy_to_user(useraddr + sizeof(egstr), 
			bcm5700_stats_str_arr, sizeof(bcm5700_stats_str_arr)))
			return -EFAULT;
		return 0;
	}
#endif
#ifdef ETHTOOL_GSTATS
	case ETHTOOL_GSTATS: {
		struct ethtool_stats estats = { ETHTOOL_GSTATS };
		uint64_t stats[ETH_NUM_STATS];
		int i;
		uint64_t *pStats =
			(uint64_t *) pDevice->pStatsBlkVirt;

		if (copy_to_user(useraddr, &estats, sizeof(estats)))
			return -EFAULT;

		estats.n_stats = ETH_NUM_STATS;
		if (pStats == 0) {
			memset(stats, 0, sizeof(stats));
		}
		else {
			for (i = 0; i < ETH_NUM_STATS; i++) {
				stats[i] = SWAP_DWORD_64(*(pStats +
					bcm5700_stats_offset_arr[i]));
			}
		}
		if (copy_to_user(useraddr + sizeof(estats), &stats,
			sizeof(stats)))
			return -EFAULT;
		return 0;
	}
#endif
#ifdef BCM_TSO
#ifdef ETHTOOL_GTSO
	case ETHTOOL_GTSO: {
		struct ethtool_value edata = { ETHTOOL_GTSO };

		edata.data = 
			(dev->features & NETIF_F_TSO) != 0;
		if (copy_to_user(useraddr, &edata, sizeof(edata)))
			return -EFAULT;
		return 0;
	}
#endif
#ifdef ETHTOOL_STSO
	case ETHTOOL_STSO: {
		struct ethtool_value edata;

		if(!capable(CAP_NET_ADMIN))
			return -EPERM;
		if (copy_from_user(&edata, useraddr, sizeof(edata)))
			return -EFAULT;
		if (!(pDevice->TaskToOffload &
			LM_TASK_OFFLOAD_TCP_SEGMENTATION))
		{
			return -EINVAL;
		}
		if (edata.data) {
			dev->features |= NETIF_F_TSO;
		}
		else {
			dev->features &= ~NETIF_F_TSO;
		}
		return 0;
	}
#endif
#endif /* #ifdef BCM_TSO */
	}

	return -EOPNOTSUPP;
}
#endif /* #ifdef SIOCETHTOOL */

/* Provide ioctl() calls to examine the MII xcvr state. */
STATIC int bcm5700_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice;
	u16 *data = (u16 *)&rq->ifr_data;
	u32 value;
	unsigned long flags;

	switch(cmd) {
#ifdef SIOCGMIIPHY
	case SIOCGMIIPHY:
#endif
	case SIOCDEVPRIVATE:		/* Get the address of the PHY in use. */
		data[0] = pDevice->PhyAddr;

#ifdef SIOCGMIIREG
	case SIOCGMIIREG:
#endif
	case SIOCDEVPRIVATE+1:		/* Read the specified MII register. */
		if (pDevice->EnableTbi)
			return -EOPNOTSUPP;

		if (pUmDevice->delayed_link_ind > 0) {
			return -EOPNOTSUPP;
		}

		BCM5700_PHY_LOCK(pUmDevice, flags);
		LM_ReadPhy(pDevice, data[1] & 0x1f, (LM_UINT32 *) &value);
		BCM5700_PHY_UNLOCK(pUmDevice, flags);
		data[3] = value & 0xffff;
		return 0;

#ifdef SIOCSMIIREG
	case SIOCSMIIREG:
#endif
	case SIOCDEVPRIVATE+2:		/* Write the specified MII register */
		if (!capable(CAP_NET_ADMIN))
			return -EPERM;

		if (pDevice->EnableTbi)
			return -EOPNOTSUPP;

		BCM5700_PHY_LOCK(pUmDevice, flags);
		LM_WritePhy(pDevice, data[1] & 0x1f, data[2]);
		BCM5700_PHY_UNLOCK(pUmDevice, flags);
		return 0;

#ifdef NICE_SUPPORT
	case SIOCNICE:
	{
		struct nice_req* nrq;

		if (!capable(CAP_NET_ADMIN))
			return -EPERM;

		nrq = (struct nice_req*)&rq->ifr_ifru;
		if( nrq->cmd == NICE_CMD_QUERY_SUPPORT ) {
			nrq->nrq_magic = NICE_DEVICE_MAGIC;
			nrq->nrq_support_rx = 1;
			nrq->nrq_support_vlan = 1;
			nrq->nrq_support_get_speed = 1;
#ifdef BCM_NAPI_RXPOLL
			nrq->nrq_support_rx_napi = 1;
#endif
			return 0;
		}
#ifdef BCM_NAPI_RXPOLL
		else if( nrq->cmd == NICE_CMD_SET_RX_NAPI )
#else
		else if( nrq->cmd == NICE_CMD_SET_RX )
#endif
		{
			pUmDevice->nice_rx = nrq->nrq_rx;
			pUmDevice->nice_ctx = nrq->nrq_ctx;
			bcm5700_set_vlan_mode(pUmDevice);
			return 0;
		}
#ifdef BCM_NAPI_RXPOLL
		else if( nrq->cmd == NICE_CMD_GET_RX_NAPI )
#else
		else if( nrq->cmd == NICE_CMD_GET_RX )
#endif
		{
			nrq->nrq_rx = pUmDevice->nice_rx;
			nrq->nrq_ctx = pUmDevice->nice_ctx;
			return 0;
		}
		else if( nrq->cmd == NICE_CMD_GET_SPEED ) {
			nrq->nrq_speed = pUmDevice->line_speed;
			return 0;
		}
		else {
			int j;

			if (!pUmDevice->opened)
				return -EINVAL;

			switch (nrq->cmd) {
			case NICE_CMD_BLINK_LED:
				if (LM_BlinkLED(pDevice, nrq->nrq_blink_time) ==
					LM_STATUS_SUCCESS) {
					return 0;
				}
				return -EINTR;

			case NICE_CMD_DIAG_SUSPEND:
				bcm5700_intr_off(pUmDevice);
				pUmDevice->suspended = 1;
				netif_carrier_off(dev);
				netif_stop_queue(dev);
#ifdef BCM_TASKLET
				tasklet_kill(&pUmDevice->tasklet);
#endif
#ifdef BCM_NAPI_RXPOLL
				while (pDevice->RxPoll)
					yield();
#endif
				LM_Abort(pDevice);
				LM_ResetChip(pDevice);
				return 0;

			case NICE_CMD_DIAG_RESUME:
				if (pUmDevice->suspended) {
					pUmDevice->suspended = 0;
					bcm5700_reset(dev);
				}
				return 0;

			case NICE_CMD_REG_READ:
				if (nrq->nrq_offset >= 0x10000) {
					nrq->nrq_data = LM_RegRdInd(pDevice,
						nrq->nrq_offset);
				}
				else {
					nrq->nrq_data = LM_RegRd(pDevice,
						nrq->nrq_offset);
				}
				return 0;

			case NICE_CMD_REG_WRITE:
				if (nrq->nrq_offset >= 0x10000) {
					LM_RegWrInd(pDevice, nrq->nrq_offset,
						nrq->nrq_data);
				}
				else {
					LM_RegWr(pDevice, nrq->nrq_offset,
						nrq->nrq_data, FALSE);
				}
				return 0;

			case NICE_CMD_REG_READ_DIRECT:
			case NICE_CMD_REG_WRITE_DIRECT:
				if ((nrq->nrq_offset >= 0x10000) ||
					pDevice->UndiFix) {
					return -EINVAL;
				}

				if (nrq->cmd == NICE_CMD_REG_READ_DIRECT) {
					nrq->nrq_data = REG_RD_OFFSET(pDevice,
						nrq->nrq_offset);
				}
				else {
					REG_WR_OFFSET(pDevice, nrq->nrq_offset,
							nrq->nrq_data);
				}
				return 0;

			case NICE_CMD_MEM_READ:
				nrq->nrq_data = LM_MemRdInd(pDevice,
					nrq->nrq_offset);
				return 0;

			case NICE_CMD_MEM_WRITE:
				LM_MemWrInd(pDevice, nrq->nrq_offset,
					nrq->nrq_data);
				return 0;

			case NICE_CMD_CFG_READ32:
				pci_read_config_dword(pUmDevice->pdev,
					nrq->nrq_offset, (u32 *)&nrq->nrq_data);
				return 0;

			case NICE_CMD_CFG_READ16:
				pci_read_config_word(pUmDevice->pdev,
					nrq->nrq_offset, (u16 *)&nrq->nrq_data);
				return 0;

			case NICE_CMD_CFG_READ8:
				pci_read_config_byte(pUmDevice->pdev,
					nrq->nrq_offset, (u8 *)&nrq->nrq_data);
				return 0;

			case NICE_CMD_CFG_WRITE32:
				pci_write_config_dword(pUmDevice->pdev,
					nrq->nrq_offset, (u32)nrq->nrq_data);
				return 0;

			case NICE_CMD_CFG_WRITE16:
				pci_write_config_word(pUmDevice->pdev,
					nrq->nrq_offset, (u16)nrq->nrq_data);
				return 0;

			case NICE_CMD_CFG_WRITE8:
				pci_write_config_byte(pUmDevice->pdev,
					nrq->nrq_offset, (u8)nrq->nrq_data);
				return 0;

			case NICE_CMD_RESET:
				bcm5700_reset(dev);
				return 0;

			case NICE_CMD_ENABLE_MAC_LOOPBACK:
				if (pDevice->LoopBackMode != 0) {
					return -EINVAL;
				}

				BCM5700_PHY_LOCK(pUmDevice, flags);
				LM_EnableMacLoopBack(pDevice);
				BCM5700_PHY_UNLOCK(pUmDevice, flags);
				return 0;

			case NICE_CMD_DISABLE_MAC_LOOPBACK:
				if (pDevice->LoopBackMode !=
					LM_MAC_LOOP_BACK_MODE) {
					return -EINVAL;
				}

				BCM5700_PHY_LOCK(pUmDevice, flags);
				LM_DisableMacLoopBack(pDevice);
				BCM5700_PHY_UNLOCK(pUmDevice, flags);
				return 0;

			case NICE_CMD_ENABLE_PHY_LOOPBACK:
				if (pDevice->LoopBackMode != 0) {
					return -EINVAL;
				}

				BCM5700_PHY_LOCK(pUmDevice, flags);
				LM_EnablePhyLoopBack(pDevice);
				BCM5700_PHY_UNLOCK(pUmDevice, flags);
				return 0;

			case NICE_CMD_DISABLE_PHY_LOOPBACK:
				if (pDevice->LoopBackMode !=
					LM_PHY_LOOP_BACK_MODE) {
					return -EINVAL;
				}

				BCM5700_PHY_LOCK(pUmDevice, flags);
				LM_DisablePhyLoopBack(pDevice);
				BCM5700_PHY_UNLOCK(pUmDevice, flags);
				return 0;

			case NICE_CMD_ENABLE_EXT_LOOPBACK:
				if (pDevice->LoopBackMode != 0) {
					return -EINVAL;
				}

				if (pDevice->EnableTbi) {
					if (nrq->nrq_speed != 1000)
						return -EINVAL;
				}
				else {
					if ((nrq->nrq_speed != 1000) &&
						(nrq->nrq_speed != 100) &&
						(nrq->nrq_speed != 10)) {
						return -EINVAL;
					}
				}
				BCM5700_PHY_LOCK(pUmDevice, flags);
				LM_EnableExtLoopBack(pDevice, nrq->nrq_speed);
				BCM5700_PHY_UNLOCK(pUmDevice, flags);
				return 0;

			case NICE_CMD_DISABLE_EXT_LOOPBACK:
				if (pDevice->LoopBackMode !=
					LM_EXT_LOOP_BACK_MODE) {
					return -EINVAL;
				}

				BCM5700_PHY_LOCK(pUmDevice, flags);
				LM_DisableExtLoopBack(pDevice);
				BCM5700_PHY_UNLOCK(pUmDevice, flags);
				return 0;

			case NICE_CMD_INTERRUPT_TEST:
				pUmDevice->intr_test_result = 0;
				pUmDevice->intr_test = 1;
				REG_WR(pDevice, Grc.LocalCtrl,
					pDevice->GrcLocalCtrl |
					GRC_MISC_LOCAL_CTRL_SET_INT);
				for (j = 0; j < 10; j++) {
					if (pUmDevice->intr_test_result)
						break;
					REG_WR(pDevice, Grc.LocalCtrl,
						pDevice->GrcLocalCtrl |
						GRC_MISC_LOCAL_CTRL_SET_INT);
					MM_Sleep(pDevice, 1);
				}
				nrq->nrq_intr_test_result =
					pUmDevice->intr_test_result;
				return 0;

			case NICE_CMD_KMALLOC_PHYS: {
#if (LINUX_VERSION_CODE >= 0x020400)
				dma_addr_t mapping;
				__u64 cpu_pa;
				void *ptr;
				int i;
				struct page *pg, *last_pg;

				for (i = 0; i < MAX_MEM2; i++) {
					if (pUmDevice->mem_size_list2[i] == 0)
						break;
				}
				if (i >= MAX_MEM2)
					return -EFAULT;
				ptr = pci_alloc_consistent(pUmDevice->pdev,
					nrq->nrq_size, &mapping);
				if (!ptr) {
					return -EFAULT;
				}
				pUmDevice->mem_size_list2[i] = nrq->nrq_size;
				pUmDevice->mem_list2[i] = ptr;
				pUmDevice->dma_list2[i] = mapping;

				/* put pci mapping at the beginning of buffer */
				*((__u64 *) ptr) = (__u64) mapping;

				/* Probably won't work on some architectures */
				/* get CPU mapping */
				cpu_pa = (__u64) virt_to_phys(ptr);
				pUmDevice->cpu_pa_list2[i] = cpu_pa;
				nrq->nrq_phys_addr_lo = (__u32) cpu_pa;
				nrq->nrq_phys_addr_hi = (__u32) (cpu_pa >> 32);

				pg = virt_to_page(ptr);
				last_pg = virt_to_page(ptr + nrq->nrq_size - 1);
				for (; ; pg++) {
#if (LINUX_VERSION_CODE > 0x020500)
					SetPageReserved(pg);
#else
					mem_map_reserve(pg);
#endif
					if (pg == last_pg)
						break;
				}
				return 0;
#else
				return -EOPNOTSUPP;
#endif
			}
			case NICE_CMD_KFREE_PHYS: {
				int i;
				__u64 cpu_pa;

				cpu_pa = (__u64) nrq->nrq_phys_addr_lo +
					((__u64) nrq->nrq_phys_addr_hi << 32);
				for (i = 0; i < MAX_MEM2; i++) {
					if (pUmDevice->cpu_pa_list2[i] ==
						cpu_pa)
					{
						break;
					}
				}
				if (i >= MAX_MEM2)
					return -EFAULT;

				bcm5700_freemem2(pUmDevice, i);
				return 0;
			}
			case NICE_CMD_SET_WRITE_PROTECT:
				pDevice->EepromWp = nrq->nrq_write_protect;
				return 0;
			case NICE_CMD_GET_STATS_BLOCK: {
				PT3_STATS_BLOCK pStats =
					(PT3_STATS_BLOCK)pDevice->pStatsBlkVirt;
				if (copy_to_user(nrq->nrq_stats_useraddr,
					pStats, nrq->nrq_stats_size)) {
					return -EFAULT;
				}
				return 0;
			}
			case NICE_CMD_CLR_STATS_BLOCK: {
				int j;
				PT3_STATS_BLOCK pStats =
					(PT3_STATS_BLOCK)pDevice->pStatsBlkVirt;

				memset(pStats, 0, sizeof(T3_STATS_BLOCK));
				if (T3_ASIC_REV(pDevice->ChipRevId) ==
					T3_ASIC_REV_5705) {
					return 0;
				}
				for(j = 0x0300; j < 0x0b00; j = j + 4) {
					MEM_WR_OFFSET(pDevice, j, 0);
				}

				return 0;
			}
			}
		}
		return -EOPNOTSUPP;
	}
#endif /* NICE_SUPPORT */
#ifdef SIOCETHTOOL
	case SIOCETHTOOL:
		return netdev_ethtool_ioctl(dev, (void *) rq->ifr_data);
#endif
	default:
		return -EOPNOTSUPP;
	}
	return -EOPNOTSUPP;
}

STATIC void bcm5700_set_rx_mode(struct net_device *dev)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice;
	int i;
	struct dev_mc_list *mclist;

	LM_MulticastClear(pDevice);
	for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
			 i++, mclist = mclist->next) {
		LM_MulticastAdd(pDevice, (PLM_UINT8) &mclist->dmi_addr);
	}
	if (dev->flags & IFF_ALLMULTI) {
		if (!(pDevice->ReceiveMask & LM_ACCEPT_ALL_MULTICAST)) {
			LM_SetReceiveMask(pDevice,
				pDevice->ReceiveMask | LM_ACCEPT_ALL_MULTICAST);
		}
	}
	else if (pDevice->ReceiveMask & LM_ACCEPT_ALL_MULTICAST) {
		LM_SetReceiveMask(pDevice,
			pDevice->ReceiveMask & ~LM_ACCEPT_ALL_MULTICAST);
	}
	if (dev->flags & IFF_PROMISC) {
		if (!(pDevice->ReceiveMask & LM_PROMISCUOUS_MODE)) {
			LM_SetReceiveMask(pDevice,
				pDevice->ReceiveMask | LM_PROMISCUOUS_MODE);
		}
	}
	else if (pDevice->ReceiveMask & LM_PROMISCUOUS_MODE) {
		LM_SetReceiveMask(pDevice,
			pDevice->ReceiveMask & ~LM_PROMISCUOUS_MODE);
	}
}

/*
 * Set the hardware MAC address.
 */
STATIC int bcm5700_set_mac_addr(struct net_device *dev, void *p)
{
	struct sockaddr *addr=p;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) dev->priv;
	UM_DEVICE_BLOCK *pUmDevice = (UM_DEVICE_BLOCK *) pDevice;

	memcpy(dev->dev_addr, addr->sa_data,dev->addr_len);
	if (pUmDevice->opened)
		LM_SetMacAddress(pDevice, dev->dev_addr);
	return 0;
}

#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
STATIC int bcm5700_change_mtu(struct net_device *dev, int new_mtu)
{
	int pkt_size = new_mtu + ETHERNET_PACKET_HEADER_SIZE;
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK)dev->priv;
	PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev;
	unsigned long flags;
	int reinit = 0;

	if ((pkt_size < MIN_ETHERNET_PACKET_SIZE_NO_CRC) || 
		(pkt_size > MAX_ETHERNET_JUMBO_PACKET_SIZE_NO_CRC)) {

		return -EINVAL;
	}
	if ((T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5705) &&
		(pkt_size > MAX_ETHERNET_PACKET_SIZE_NO_CRC)) {

		return -EINVAL;
	}
	if (pUmDevice->suspended)
		return -EAGAIN;

	if (pUmDevice->opened && (new_mtu != dev->mtu) &&
		(T3_ASIC_REV(pDevice->ChipRevId) != T3_ASIC_REV_5705)) {
		reinit = 1;
	}

	BCM5700_PHY_LOCK(pUmDevice, flags);
	if (reinit) {
		netif_stop_queue(dev);
		bcm5700_shutdown(pUmDevice);
		bcm5700_freemem(dev);
	}

	dev->mtu = new_mtu;
	if (pkt_size < MAX_ETHERNET_PACKET_SIZE_NO_CRC) {
		pDevice->RxMtu = pDevice->TxMtu =
			MAX_ETHERNET_PACKET_SIZE_NO_CRC;
	}
	else {
		pDevice->RxMtu = pDevice->TxMtu = pkt_size;
	}

	if (dev->mtu <= 1514) {
		pDevice->RxJumboDescCnt = 0;
	}
	else {
		pDevice->RxJumboDescCnt =
			rx_jumbo_desc_cnt[pUmDevice->index];
	}
	pDevice->RxPacketDescCnt = pDevice->RxJumboDescCnt +
		pDevice->RxStdDescCnt;

	pDevice->RxJumboBufferSize = (pDevice->RxMtu + 8 /* CRC + VLAN */ +
		COMMON_CACHE_LINE_SIZE-1) & ~COMMON_CACHE_LINE_MASK;

	if (reinit) {
		LM_InitializeAdapter(pDevice);
		bcm5700_set_vlan_mode(pUmDevice);
		bcm5700_init_counters(pUmDevice);
		if (memcmp(dev->dev_addr, pDevice->NodeAddress, 6)) {
			LM_SetMacAddress(pDevice, dev->dev_addr);
		}
		netif_start_queue(dev);
		bcm5700_intr_on(pUmDevice);
	}
	BCM5700_PHY_UNLOCK(pUmDevice, flags);

	return 0;
}
#endif


#if (LINUX_VERSION_CODE < 0x020300)

int
bcm5700_probe(struct net_device *dev)
{
	int cards_found = 0;
	struct pci_dev *pdev = NULL;
	struct pci_device_id *pci_tbl;
	u16 ssvid, ssid;

	if ( ! pci_present())
		return -ENODEV;

	pci_tbl = bcm5700_pci_tbl;
	while ((pdev = pci_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pdev))) {
		int idx;

		pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &ssvid);
		pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &ssid);
		for (idx = 0; pci_tbl[idx].vendor; idx++) {
			if ((pci_tbl[idx].vendor == PCI_ANY_ID ||
				pci_tbl[idx].vendor == pdev->vendor) &&
				(pci_tbl[idx].device == PCI_ANY_ID ||
				pci_tbl[idx].device == pdev->device) &&
				(pci_tbl[idx].subvendor == PCI_ANY_ID ||
				pci_tbl[idx].subvendor == ssvid) &&
				(pci_tbl[idx].subdevice == PCI_ANY_ID ||
				pci_tbl[idx].subdevice == ssid))
			{

				break;
			}
		}
		if (pci_tbl[idx].vendor == 0)
			continue;


		if (bcm5700_init_one(pdev, &pci_tbl[idx]) == 0)
			cards_found++;
	}

	return cards_found ? 0 : -ENODEV;
}

#ifdef MODULE
int init_module(void)
{
	return bcm5700_probe(NULL);
}

void cleanup_module(void)
{
	struct net_device *next_dev;
	PUM_DEVICE_BLOCK pUmDevice;

	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
	while (root_tigon3_dev) {
		pUmDevice = (PUM_DEVICE_BLOCK)root_tigon3_dev->priv;
#ifdef BCM_PROC_FS
		bcm5700_proc_remove_dev(root_tigon3_dev); 
#endif
		next_dev = pUmDevice->next_module;
		unregister_netdev(root_tigon3_dev);
		if (pUmDevice->lm_dev.pMappedMemBase)
			iounmap(pUmDevice->lm_dev.pMappedMemBase);
#if (LINUX_VERSION_CODE < 0x020600)
		kfree(root_tigon3_dev);
#else
		free_netdev(root_tigon3_dev);
#endif
		root_tigon3_dev = next_dev;
	}
#ifdef BCM_IOCTL32
	unregister_ioctl32_conversion(SIOCNICE);
#endif
}

#endif  /* MODULE */
#else	/* LINUX_VERSION_CODE < 0x020300 */

#if (LINUX_VERSION_CODE >= 0x020406)
static int bcm5700_suspend (struct pci_dev *pdev, u32 state)
#else
static void bcm5700_suspend (struct pci_dev *pdev)
#endif
{
	struct net_device *dev = (struct net_device *) pci_get_drvdata(pdev);
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) dev->priv;
	PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev;

	if (!netif_running(dev))
#if (LINUX_VERSION_CODE >= 0x020406)
		return 0;
#else
		return;
#endif

	netif_device_detach (dev);
	bcm5700_shutdown(pUmDevice);
	LM_SetPowerState(pDevice, LM_POWER_STATE_D3);

/*	pci_power_off(pdev, -1);*/
#if (LINUX_VERSION_CODE >= 0x020406)
	return 0;
#endif
}


#if (LINUX_VERSION_CODE >= 0x020406)
static int bcm5700_resume(struct pci_dev *pdev)
#else
static void bcm5700_resume(struct pci_dev *pdev)
#endif
{
	struct net_device *dev = (struct net_device *) pci_get_drvdata(pdev);
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) dev->priv;
	PLM_DEVICE_BLOCK pDevice = &pUmDevice->lm_dev;

	if (!netif_running(dev))
#if (LINUX_VERSION_CODE >= 0x020406)
		return 0;
#else
		return;
#endif
/*	pci_power_on(pdev);*/
	netif_device_attach(dev);
	LM_SetPowerState(pDevice, LM_POWER_STATE_D0);
	MM_InitializeUmPackets(pDevice);
	bcm5700_reset(dev);
#if (LINUX_VERSION_CODE >= 0x020406)
	return 0;
#endif
}


static struct pci_driver bcm5700_pci_driver = {
	name:		bcm5700_driver,
	id_table:	bcm5700_pci_tbl,
	probe:		bcm5700_init_one,
	remove:		__devexit_p(bcm5700_remove_one),
	suspend:	bcm5700_suspend,
	resume:		bcm5700_resume,
};


static int __init bcm5700_init_module (void)
{
	return pci_module_init(&bcm5700_pci_driver);
}


static void __exit bcm5700_cleanup_module (void)
{
	pci_unregister_driver(&bcm5700_pci_driver);
}


module_init(bcm5700_init_module);
module_exit(bcm5700_cleanup_module);
#endif

/*
 * Middle Module
 *
 */


#ifdef BCM_NAPI_RXPOLL
LM_STATUS
MM_ScheduleRxPoll(LM_DEVICE_BLOCK *pDevice)
{
	struct net_device *dev = ((UM_DEVICE_BLOCK *) pDevice)->dev;

	if (netif_rx_schedule_prep(dev)) {
		__netif_rx_schedule(dev);
		return LM_STATUS_SUCCESS;
	}
	return LM_STATUS_FAILURE;
}
#endif

LM_STATUS
MM_ReadConfig16(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset,
	LM_UINT16 *pValue16)
{
	UM_DEVICE_BLOCK *pUmDevice;

	pUmDevice = (UM_DEVICE_BLOCK *) pDevice;
	pci_read_config_word(pUmDevice->pdev, Offset, (u16 *) pValue16);
	return LM_STATUS_SUCCESS;
}

LM_STATUS
MM_ReadConfig32(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset,
	LM_UINT32 *pValue32)
{
	UM_DEVICE_BLOCK *pUmDevice;

	pUmDevice = (UM_DEVICE_BLOCK *) pDevice;
	pci_read_config_dword(pUmDevice->pdev, Offset, (u32 *) pValue32);
	return LM_STATUS_SUCCESS;
}

LM_STATUS
MM_WriteConfig16(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset,
	LM_UINT16 Value16)
{
	UM_DEVICE_BLOCK *pUmDevice;

	pUmDevice = (UM_DEVICE_BLOCK *) pDevice;
	pci_write_config_word(pUmDevice->pdev, Offset, Value16);
	return LM_STATUS_SUCCESS;
}

LM_STATUS
MM_WriteConfig32(PLM_DEVICE_BLOCK pDevice, LM_UINT32 Offset,
	LM_UINT32 Value32)
{
	UM_DEVICE_BLOCK *pUmDevice;

	pUmDevice = (UM_DEVICE_BLOCK *) pDevice;
	pci_write_config_dword(pUmDevice->pdev, Offset, Value32);
	return LM_STATUS_SUCCESS;
}

LM_STATUS
MM_AllocateSharedMemory(PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlockSize,
	PLM_VOID *pMemoryBlockVirt, PLM_PHYSICAL_ADDRESS pMemoryBlockPhy,
	LM_BOOL Cached)
{
	PLM_VOID pvirt;
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice;
	dma_addr_t mapping;

	pvirt = pci_alloc_consistent(pUmDevice->pdev, BlockSize,
					       &mapping);
	if (!pvirt) {
		return LM_STATUS_FAILURE;
	}
	pUmDevice->mem_list[pUmDevice->mem_list_num] = pvirt;
	pUmDevice->dma_list[pUmDevice->mem_list_num] = mapping;
	pUmDevice->mem_size_list[pUmDevice->mem_list_num++] = BlockSize;
	memset(pvirt, 0, BlockSize);
	*pMemoryBlockVirt = (PLM_VOID) pvirt;
	MM_SetAddr(pMemoryBlockPhy, mapping);
	return LM_STATUS_SUCCESS;
}

LM_STATUS
MM_AllocateMemory(PLM_DEVICE_BLOCK pDevice, LM_UINT32 BlockSize,
	PLM_VOID *pMemoryBlockVirt)
{
	PLM_VOID pvirt;
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice;


	/* Maximum in slab.c */
	if (BlockSize > 131072) {
		goto MM_Alloc_error;
	}

	pvirt = kmalloc(BlockSize, GFP_KERNEL);
	if (!pvirt) {
		goto MM_Alloc_error;
	}
	pUmDevice->mem_list[pUmDevice->mem_list_num] = pvirt;
	pUmDevice->dma_list[pUmDevice->mem_list_num] = 0;
	pUmDevice->mem_size_list[pUmDevice->mem_list_num++] = 0;
	/* mem_size_list[i] == 0 indicates that the memory should be freed */
	/* using kfree */
	memset(pvirt, 0, BlockSize);
	*pMemoryBlockVirt = pvirt;
	return LM_STATUS_SUCCESS;

MM_Alloc_error:
	printk(KERN_WARNING "%s: Memory allocation failed - buffer parameters may be set too high\n", pUmDevice->dev->name);
	return LM_STATUS_FAILURE;
}

LM_STATUS
MM_MapMemBase(PLM_DEVICE_BLOCK pDevice)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice;

	pDevice->pMappedMemBase = ioremap_nocache(
		pci_resource_start(pUmDevice->pdev, 0), sizeof(T3_STD_MEM_MAP));
	return LM_STATUS_SUCCESS;
}

LM_STATUS
MM_InitializeUmPackets(PLM_DEVICE_BLOCK pDevice)
{
	unsigned int i;
	struct sk_buff *skb;
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice;
	PUM_PACKET pUmPacket;
	PLM_PACKET pPacket;

	for (i = 0; i < pDevice->RxPacketDescCnt; i++) {
		pPacket = QQ_PopHead(&pDevice->RxPacketFreeQ.Container);
		pUmPacket = (PUM_PACKET) pPacket;
		if (pPacket == 0) {
			printk(KERN_DEBUG "Bad RxPacketFreeQ\n");
		}
		if (pUmPacket->skbuff == 0) {
			skb = dev_alloc_skb(pPacket->u.Rx.RxBufferSize + 2);
			if (skb == 0) {
				pUmPacket->skbuff = 0;
				QQ_PushTail(
					&pUmDevice->rx_out_of_buf_q.Container,
					pPacket);
				continue;
			}
			pUmPacket->skbuff = skb;
			skb->dev = pUmDevice->dev;
			skb_reserve(skb, pUmDevice->rx_buf_align);
		}
		QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket);
	}
	if (T3_ASIC_REV(pUmDevice->lm_dev.ChipRevId) == T3_ASIC_REV_5700) {
		/* reallocate buffers in the ISR */
		pUmDevice->rx_buf_repl_thresh = 0;
		pUmDevice->rx_buf_repl_panic_thresh = 0;
		pUmDevice->rx_buf_repl_isr_limit = 0;
	}
	else {
		pUmDevice->rx_buf_repl_thresh = pDevice->RxPacketDescCnt / 8;
		pUmDevice->rx_buf_repl_panic_thresh =
			pDevice->RxPacketDescCnt  * 7 / 8;

		/* This limits the time spent in the ISR when the receiver */
		/* is in a steady state of being overrun. */
		pUmDevice->rx_buf_repl_isr_limit = pDevice->RxPacketDescCnt / 8;

#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
		if (pDevice->RxJumboDescCnt != 0) {
			if (pUmDevice->rx_buf_repl_thresh >=
				pDevice->RxJumboDescCnt) {

				pUmDevice->rx_buf_repl_thresh = 
				pUmDevice->rx_buf_repl_panic_thresh = 
					pDevice->RxJumboDescCnt - 1;
			}
			if (pUmDevice->rx_buf_repl_thresh >=
				pDevice->RxStdDescCnt) {

				pUmDevice->rx_buf_repl_thresh = 
				pUmDevice->rx_buf_repl_panic_thresh = 
					pDevice->RxStdDescCnt - 1;
			}
		}
#endif
	}
	return LM_STATUS_SUCCESS;
}

LM_STATUS
MM_GetConfig(PLM_DEVICE_BLOCK pDevice)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice;
	int index = pUmDevice->index;
	struct net_device *dev = pUmDevice->dev;

	bcm5700_validate_param_range(pUmDevice, &auto_speed[index], "auto_speed",
		0, 1, 1);
	if (auto_speed[index] == 0)
		pDevice->DisableAutoNeg = TRUE;
	else
		pDevice->DisableAutoNeg = FALSE;

	if (line_speed[index] == 0) {
		pDevice->RequestedLineSpeed = LM_LINE_SPEED_AUTO;
		pDevice->DisableAutoNeg = FALSE;
	}
	else {
		bcm5700_validate_param_range(pUmDevice, &full_duplex[index],
			"full_duplex", 0, 1, 1);
		if (full_duplex[index]) {
			pDevice->RequestedDuplexMode = LM_DUPLEX_MODE_FULL;
		}
		else {
			pDevice->RequestedDuplexMode = LM_DUPLEX_MODE_HALF;
		}

		if (line_speed[index] == 1000) {
			pDevice->RequestedLineSpeed = LM_LINE_SPEED_1000MBPS;
			if (pDevice->No1000) {
				pDevice->RequestedLineSpeed =
					LM_LINE_SPEED_100MBPS;
				printk(KERN_WARNING "%s-%d: Invalid line_speed parameter (1000), using 100\n", bcm5700_driver, index);
			}
			else {
				if (pDevice->EnableTbi && !full_duplex[index]) {

					printk(KERN_WARNING "%s-%d: Invalid full_duplex parameter (0) for fiber, using 1\n", bcm5700_driver, index);
					pDevice->RequestedDuplexMode =
						LM_DUPLEX_MODE_FULL;
				}

				if (!pDevice->EnableTbi && !auto_speed[index]) {
					printk(KERN_WARNING "%s-%d: Invalid auto_speed parameter (0) for copper, using 1\n", bcm5700_driver, index);
					pDevice->DisableAutoNeg = FALSE;
				}
			}
		}
		else if (line_speed[index] == 100) {
			pDevice->RequestedLineSpeed = LM_LINE_SPEED_100MBPS;
		}
		else if (line_speed[index] == 10) {
			pDevice->RequestedLineSpeed = LM_LINE_SPEED_10MBPS;
		}
		else {
			pDevice->RequestedLineSpeed = LM_LINE_SPEED_AUTO;
			pDevice->DisableAutoNeg = FALSE;
			printk(KERN_WARNING "%s-%d: Invalid line_speed parameter (%d), using 0\n", bcm5700_driver, index, line_speed[index]);
		}

	}
	pDevice->FlowControlCap = 0;
	bcm5700_validate_param_range(pUmDevice, &rx_flow_control[index],
		"rx_flow_control", 0, 1, 0);
	if (rx_flow_control[index] != 0) {
		pDevice->FlowControlCap |= LM_FLOW_CONTROL_RECEIVE_PAUSE;
	}
	bcm5700_validate_param_range(pUmDevice, &tx_flow_control[index],
		"tx_flow_control", 0, 1, 0);
	if (tx_flow_control[index] != 0) {
		pDevice->FlowControlCap |= LM_FLOW_CONTROL_TRANSMIT_PAUSE;
	}
	bcm5700_validate_param_range(pUmDevice, &auto_flow_control[index],
		"auto_flow_control", 0, 1, 0);
	if (auto_flow_control[index] != 0) {
		if (pDevice->DisableAutoNeg == FALSE) {

			pDevice->FlowControlCap |= LM_FLOW_CONTROL_AUTO_PAUSE;
			if ((tx_flow_control[index] == 0) &&
				(rx_flow_control[index] == 0)) {

				pDevice->FlowControlCap |=
					LM_FLOW_CONTROL_TRANSMIT_PAUSE |
					LM_FLOW_CONTROL_RECEIVE_PAUSE;
			}
		}
	}

	if (dev->mtu > 1500) {
		pDevice->RxMtu = dev->mtu + 14;
	}

	if (T3_ASIC_REV(pDevice->ChipRevId) != T3_ASIC_REV_5700) {
		pDevice->UseTaggedStatus = TRUE;
		pUmDevice->timer_interval = HZ;
		if ((T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5703) &&
			pDevice->EnableTbi) {
			pUmDevice->timer_interval = HZ/4;
		}
	}
	else {
		pUmDevice->timer_interval = HZ/10;
	}

	bcm5700_validate_param_range(pUmDevice, &tx_pkt_desc_cnt[index],
		"tx_pkt_desc_cnt", 1, MAX_TX_PACKET_DESC_COUNT-1, TX_DESC_CNT);
	pDevice->TxPacketDescCnt = tx_pkt_desc_cnt[index];
	bcm5700_validate_param_range(pUmDevice, &rx_std_desc_cnt[index],
		"rx_std_desc_cnt", 1, T3_STD_RCV_RCB_ENTRY_COUNT-1,
		RX_DESC_CNT);
	pDevice->RxStdDescCnt = rx_std_desc_cnt[index];

#if T3_JUMBO_RCV_RCB_ENTRY_COUNT
	bcm5700_validate_param_range(pUmDevice, &rx_jumbo_desc_cnt[index],
		"rx_jumbo_desc_cnt", 1, T3_JUMBO_RCV_RCB_ENTRY_COUNT-1,
		JBO_DESC_CNT);

	if (mtu[index] <= 1514)
		pDevice->RxJumboDescCnt = 0;
	else
		pDevice->RxJumboDescCnt = rx_jumbo_desc_cnt[index];
#endif

#ifdef BCM_INT_COAL
	bcm5700_validate_param_range(pUmDevice, &adaptive_coalesce[index],
		"adaptive_coalesce", 0, 1, 1);
#ifdef BCM_NAPI_RXPOLL
	if (adaptive_coalesce[index]) {
		printk(KERN_WARNING "%s-%d: adaptive_coalesce not used in NAPI mode\n", bcm5700_driver, index);
		adaptive_coalesce[index] = 0;

	}
#endif
	pUmDevice->adaptive_coalesce = adaptive_coalesce[index];
	if (!pUmDevice->adaptive_coalesce) {
		bcm5700_validate_param_range(pUmDevice,
			&rx_coalesce_ticks[index], "rx_coalesce_ticks", 0,
			MAX_RX_COALESCING_TICKS, RX_COAL_TK);
		if ((rx_coalesce_ticks[index] == 0) &&
			(rx_max_coalesce_frames[index] == 0)) {

			printk(KERN_WARNING "%s-%d: Conflicting rx_coalesce_ticks (0) and rx_max_coalesce_frames (0) parameters, using %d and %d respectively\n",
				bcm5700_driver, index, RX_COAL_TK, RX_COAL_FM);

			rx_coalesce_ticks[index] = RX_COAL_TK;
			rx_max_coalesce_frames[index] = RX_COAL_FM;
		}
		pDevice->RxCoalescingTicks = rx_coalesce_ticks[index];
		pUmDevice->rx_curr_coalesce_ticks = pDevice->RxCoalescingTicks;

		bcm5700_validate_param_range(pUmDevice,
			&rx_max_coalesce_frames[index],
			"rx_max_coalesce_frames", 0,
			MAX_RX_MAX_COALESCED_FRAMES, RX_COAL_FM);

		pDevice->RxMaxCoalescedFrames = rx_max_coalesce_frames[index];
		pUmDevice->rx_curr_coalesce_frames =
			pDevice->RxMaxCoalescedFrames;

		bcm5700_validate_param_range(pUmDevice,
			&tx_coalesce_ticks[index], "tx_coalesce_ticks", 0,
			MAX_TX_COALESCING_TICKS, TX_COAL_TK);
		if ((tx_coalesce_ticks[index] == 0) &&
			(tx_max_coalesce_frames[index] == 0)) {

			printk(KERN_WARNING "%s-%d: Conflicting tx_coalesce_ticks (0) and tx_max_coalesce_frames (0) parameters, using %d and %d respectively\n",
				bcm5700_driver, index, TX_COAL_TK, TX_COAL_FM);

			tx_coalesce_ticks[index] = TX_COAL_TK;
			tx_max_coalesce_frames[index] = TX_COAL_FM;
		}
		pDevice->TxCoalescingTicks = tx_coalesce_ticks[index];
		bcm5700_validate_param_range(pUmDevice,
			&tx_max_coalesce_frames[index],
			"tx_max_coalesce_frames", 0,
			MAX_TX_MAX_COALESCED_FRAMES, TX_COAL_FM);
		pDevice->TxMaxCoalescedFrames = tx_max_coalesce_frames[index];
		pUmDevice->tx_curr_coalesce_frames =
			pDevice->TxMaxCoalescedFrames;

		bcm5700_validate_param_range(pUmDevice,
			&stats_coalesce_ticks[index], "stats_coalesce_ticks",
			0, MAX_STATS_COALESCING_TICKS, ST_COAL_TK);
		if ((stats_coalesce_ticks[index] > 0) &&
			(stats_coalesce_ticks[index] < 100)) {
			printk(KERN_WARNING "%s-%d: Invalid stats_coalesce_ticks parameter (%u), using 100\n", bcm5700_driver, index, (unsigned int) stats_coalesce_ticks[index]);
			stats_coalesce_ticks[index] = 100;
		}
		pDevice->StatsCoalescingTicks = stats_coalesce_ticks[index];
		pDevice->StatsCoalescingTicks = stats_coalesce_ticks[index];
	}
	else {
		pUmDevice->rx_curr_coalesce_frames = RX_COAL_FM;
		pUmDevice->rx_curr_coalesce_ticks = RX_COAL_TK;
		pUmDevice->tx_curr_coalesce_frames = TX_COAL_FM;
	}
#endif

#ifdef BCM_WOL
	bcm5700_validate_param_range(pUmDevice, &enable_wol[index],
		"enable_wol", 0, 1, 0);
	if (enable_wol[index]) {
		pDevice->WakeUpModeCap = LM_WAKE_UP_MODE_MAGIC_PACKET;
		pDevice->WakeUpMode = LM_WAKE_UP_MODE_MAGIC_PACKET;
	}
#endif
#ifdef BCM_NIC_SEND_BD
	bcm5700_validate_param_range(pUmDevice, &nic_tx_bd[index], "nic_tx_bd",
		0, 1, 0);
	pDevice->NicSendBd = nic_tx_bd[index];
	if ((pDevice->EnablePciXFix) ||
		(T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5705)) {
		if (pDevice->NicSendBd) {
			pDevice->NicSendBd = FALSE;
			printk(KERN_WARNING "%s-%d: Nic Send BDs not available on this NIC or not possible on this system\n", bcm5700_driver, index);
		}
	}
#endif
#if INCLUDE_TBI_SUPPORT
	pDevice->PollTbiLink = TRUE;
	if (((T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5704) ||
	    (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5703))
		 && pDevice->EnableTbi)
	{
		/* just poll since we have hardware autoneg. in 5704 */
		pDevice->NoTbiInterrupt = TRUE;
	}
#endif
	bcm5700_validate_param_range(pUmDevice, &scatter_gather[index],
		"scatter_gather", 0, 1, 1);
	bcm5700_validate_param_range(pUmDevice, &tx_checksum[index],
		"tx_checksum", 0, 1, 1);
	bcm5700_validate_param_range(pUmDevice, &rx_checksum[index],
		"rx_checksum", 0, 1, 1);
	if (!(pDevice->TaskOffloadCap & LM_TASK_OFFLOAD_TX_TCP_CHECKSUM)) {
		if (tx_checksum[index] || rx_checksum[index]) {

			pDevice->TaskToOffload = LM_TASK_OFFLOAD_NONE; 
			printk(KERN_WARNING "%s-%d: Checksum offload not available on this NIC\n", bcm5700_driver, index);
		}
	}
	else {
		if (rx_checksum[index]) {
			pDevice->TaskToOffload |=
				LM_TASK_OFFLOAD_RX_TCP_CHECKSUM |
				LM_TASK_OFFLOAD_RX_UDP_CHECKSUM;
		}
		if (tx_checksum[index]) {
			pDevice->TaskToOffload |=
				LM_TASK_OFFLOAD_TX_TCP_CHECKSUM |
				LM_TASK_OFFLOAD_TX_UDP_CHECKSUM;
			pDevice->NoTxPseudoHdrChksum = TRUE;
		}
	}
#ifdef BCM_TSO
	bcm5700_validate_param_range(pUmDevice, &enable_tso[index],
		"enable_tso", 0, 1, 1);

	/* Always enable TSO firmware if supported */
	/* This way we can turn it on or off on the fly */
	if (pDevice->TaskOffloadCap & LM_TASK_OFFLOAD_TCP_SEGMENTATION)
	{
		pDevice->TaskToOffload |=
			LM_TASK_OFFLOAD_TCP_SEGMENTATION;
	}
	if (enable_tso[index] &&
		!(pDevice->TaskToOffload & LM_TASK_OFFLOAD_TCP_SEGMENTATION))
	{
		printk(KERN_WARNING "%s-%d: TSO not available on this NIC\n", bcm5700_driver, index);
	}
#endif
#ifdef BCM_ASF
	bcm5700_validate_param_range(pUmDevice, &vlan_tag_mode[index],
		"vlan_strip_mode", 0, 2, 0);
	pUmDevice->vlan_tag_mode = vlan_tag_mode[index];
#else
	pUmDevice->vlan_tag_mode = VLAN_TAG_MODE_NORMAL_STRIP;
#endif
	return LM_STATUS_SUCCESS;
}

LM_STATUS
MM_IndicateRxPackets(PLM_DEVICE_BLOCK pDevice)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice;
	PLM_PACKET pPacket;
	PUM_PACKET pUmPacket;
	struct sk_buff *skb;
	int size;
	int vlan_tag_size = 0;

	if (pDevice->ReceiveMask & LM_KEEP_VLAN_TAG)
		vlan_tag_size = 4;

	while (1) {
		pPacket = (PLM_PACKET)
			QQ_PopHead(&pDevice->RxPacketReceivedQ.Container);
		if (pPacket == 0)
			break;
		pUmPacket = (PUM_PACKET) pPacket;
#if ! defined(NO_PCI_UNMAP)
		pci_unmap_single(pUmDevice->pdev,
				pci_unmap_addr(pUmPacket, map[0]),
				pPacket->u.Rx.RxBufferSize,
				PCI_DMA_FROMDEVICE);
#endif
		if ((pPacket->PacketStatus != LM_STATUS_SUCCESS) ||
			((size = pPacket->PacketSize) >
			(pDevice->RxMtu + vlan_tag_size))) {

			/* reuse skb */
#ifdef BCM_TASKLET
			QQ_PushTail(&pUmDevice->rx_out_of_buf_q.Container, pPacket);
#else
			QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket);
#endif
			pUmDevice->rx_misc_errors++;
			continue;
		}
		skb = pUmPacket->skbuff;
		skb_put(skb, size);
		skb->pkt_type = 0;
		skb->protocol = eth_type_trans(skb, skb->dev);
		if (size > pDevice->RxMtu) {
			/* Make sure we have a valid VLAN tag */
			if (htons(skb->protocol) != 0x8100) {
				dev_kfree_skb_irq(skb);
				pUmDevice->rx_misc_errors++;
				goto drop_rx;
			}
		}
		if ((pPacket->Flags & RCV_BD_FLAG_TCP_UDP_CHKSUM_FIELD) &&
			(pDevice->TaskToOffload &
				LM_TASK_OFFLOAD_RX_TCP_CHECKSUM)) {
			if (pPacket->u.Rx.TcpUdpChecksum == 0xffff) {

				skb->ip_summed = CHECKSUM_UNNECESSARY;
#if TIGON3_DEBUG
				pUmDevice->rx_good_chksum_count++;
#endif
			}
			else {
				skb->ip_summed = CHECKSUM_NONE;
				pUmDevice->rx_bad_chksum_count++;
			}
		}
		else {
			skb->ip_summed = CHECKSUM_NONE;
		}
#ifdef NICE_SUPPORT
		if( pUmDevice->nice_rx ) {
			vlan_tag_t *vlan_tag;

			vlan_tag = (vlan_tag_t *) &skb->cb[0];
			if (pPacket->Flags & RCV_BD_FLAG_VLAN_TAG) {
				vlan_tag->signature = 0x7777;
				vlan_tag->tag = pPacket->VlanTag;
			}
			else {
				vlan_tag->signature = 0;
			}
			pUmDevice->nice_rx(skb, pUmDevice->nice_ctx);
		}
		else
#endif
		{
#ifdef BCM_VLAN
			if (pUmDevice->vlgrp &&
				(pPacket->Flags & RCV_BD_FLAG_VLAN_TAG)) {

#ifdef BCM_NAPI_RXPOLL
				vlan_hwaccel_receive_skb(skb, pUmDevice->vlgrp,
					pPacket->VlanTag);
#else
				vlan_hwaccel_rx(skb, pUmDevice->vlgrp,
					pPacket->VlanTag);
#endif
			}
			else
#endif
			{
#ifdef BCM_NAPI_RXPOLL
				netif_receive_skb(skb);
#else
				netif_rx(skb);
#endif
			}
		}
		pUmDevice->dev->last_rx = jiffies;

drop_rx:
#ifdef BCM_TASKLET
		pUmPacket->skbuff = 0;
		QQ_PushTail(&pUmDevice->rx_out_of_buf_q.Container, pPacket);
#else
		skb = dev_alloc_skb(pPacket->u.Rx.RxBufferSize + 2);
		if (skb == 0) {
			pUmPacket->skbuff = 0;
			QQ_PushTail(&pUmDevice->rx_out_of_buf_q.Container, pPacket);
		}
		else {
			pUmPacket->skbuff = skb;
			skb->dev = pUmDevice->dev;
			skb_reserve(skb, pUmDevice->rx_buf_align);
			QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket);
		}
#endif
	}
	return LM_STATUS_SUCCESS;
}

LM_STATUS
MM_CoalesceTxBuffer(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket)
{
	PUM_PACKET pUmPacket = (PUM_PACKET) pPacket;
	struct sk_buff *skb = pUmPacket->skbuff;
	struct sk_buff *nskb;
#if ! defined(NO_PCI_UNMAP)
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice;

	pci_unmap_single(pUmDevice->pdev,
			pci_unmap_addr(pUmPacket, map[0]),
			pci_unmap_len(pUmPacket, map_len[0]),
			PCI_DMA_TODEVICE);
#if MAX_SKB_FRAGS
	{
		int i;

		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
			pci_unmap_page(pUmDevice->pdev,
				pci_unmap_addr(pUmPacket, map[i + 1]),
				pci_unmap_len(pUmPacket, map_len[i + 1]),
				PCI_DMA_TODEVICE);
		}
	}
#endif
#endif
	if ((nskb = skb_copy(skb, GFP_ATOMIC))) {
		pUmPacket->lm_packet.u.Tx.FragCount = 1;
		dev_kfree_skb(skb);
		pUmPacket->skbuff = nskb;
		return LM_STATUS_SUCCESS;
	}
	dev_kfree_skb(skb);
	pUmPacket->skbuff = 0;
	return LM_STATUS_FAILURE;
}

/* Returns 1 if not all buffers are allocated */
STATIC int
replenish_rx_buffers(PUM_DEVICE_BLOCK pUmDevice, int max)
{
	PLM_PACKET pPacket;
	PUM_PACKET pUmPacket;
	PLM_DEVICE_BLOCK pDevice = (PLM_DEVICE_BLOCK) pUmDevice;
	struct sk_buff *skb;
	int queue_rx = 0;
	int alloc_cnt = 0;
	int ret = 0;

	while ((pUmPacket = (PUM_PACKET)
		QQ_PopHead(&pUmDevice->rx_out_of_buf_q.Container)) != 0) {
		pPacket = (PLM_PACKET) pUmPacket;
		if (pUmPacket->skbuff) {
			/* reuse an old skb */
			QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket);
			queue_rx = 1;
			continue;
		}
		if ((skb = dev_alloc_skb(pPacket->u.Rx.RxBufferSize + 2)) == 0) {
			QQ_PushHead(&pUmDevice->rx_out_of_buf_q.Container,
				pPacket);
			ret = 1;
			break;
		}
		pUmPacket->skbuff = skb;
		skb->dev = pUmDevice->dev;
		skb_reserve(skb, pUmDevice->rx_buf_align);
		QQ_PushTail(&pDevice->RxPacketFreeQ.Container, pPacket);
		queue_rx = 1;
		if (max > 0) {
			alloc_cnt++;
			if (alloc_cnt >= max)
				break;
		}
	}
	if (queue_rx || pDevice->QueueAgain) {
		LM_QueueRxPackets(pDevice);
	}
	return ret;
}

LM_STATUS
MM_IndicateTxPackets(PLM_DEVICE_BLOCK pDevice)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice;
	PLM_PACKET pPacket;
	PUM_PACKET pUmPacket;
	struct sk_buff *skb;
#if ! defined(NO_PCI_UNMAP) && MAX_SKB_FRAGS
	int i;
#endif

	while (1) {
		pPacket = (PLM_PACKET)
			QQ_PopHead(&pDevice->TxPacketXmittedQ.Container);
		if (pPacket == 0)
			break;
		pUmPacket = (PUM_PACKET) pPacket;
		skb = pUmPacket->skbuff;
#if ! defined(NO_PCI_UNMAP)
		pci_unmap_single(pUmDevice->pdev,
				pci_unmap_addr(pUmPacket, map[0]),
				pci_unmap_len(pUmPacket, map_len[0]),
				PCI_DMA_TODEVICE);
#if MAX_SKB_FRAGS
		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
			pci_unmap_page(pUmDevice->pdev,
				pci_unmap_addr(pUmPacket, map[i + 1]),
				pci_unmap_len(pUmPacket, map_len[i + 1]),
				PCI_DMA_TODEVICE);
		}
#endif
#endif
		dev_kfree_skb_irq(skb);
		pUmPacket->skbuff = 0;
		QQ_PushTail(&pDevice->TxPacketFreeQ.Container, pPacket);
	}
	if (pUmDevice->tx_full) {
		if (QQ_GetEntryCnt(&pDevice->TxPacketFreeQ.Container) >=
			(pDevice->TxPacketDescCnt >> 1)) {

			pUmDevice->tx_full = 0;
			netif_wake_queue(pUmDevice->dev);
		}
	}
	return LM_STATUS_SUCCESS;
}

LM_STATUS
MM_IndicateStatus(PLM_DEVICE_BLOCK pDevice, LM_STATUS Status)
{
	PUM_DEVICE_BLOCK pUmDevice = (PUM_DEVICE_BLOCK) pDevice;
	struct net_device *dev = pUmDevice->dev;
	LM_FLOW_CONTROL flow_control;

	if (!pUmDevice->opened)
		return LM_STATUS_SUCCESS;

	if (pUmDevice->delayed_link_ind > 0) {
		pUmDevice->delayed_link_ind = 0;
		if (Status == LM_STATUS_LINK_DOWN) {
			pUmDevice->line_speed = 0;
			netif_carrier_off(dev);
			printk(KERN_ERR "%s: %s NIC Link is DOWN\n", bcm5700_driver, dev->name);
		}
		else if (Status == LM_STATUS_LINK_ACTIVE) {
			netif_carrier_on(dev);
			printk(KERN_INFO "%s: %s NIC Link is UP, ", bcm5700_driver, dev->name);
		}
	}
	else {
		if (Status == LM_STATUS_LINK_DOWN) {
			pUmDevice->line_speed = 0;
			netif_carrier_off(dev);
			printk(KERN_ERR "%s: %s NIC Link is Down\n", bcm5700_driver, dev->name);
		}
		else if (Status == LM_STATUS_LINK_ACTIVE) {
			netif_carrier_on(dev);
			printk(KERN_INFO "%s: %s NIC Link is Up, ", bcm5700_driver, dev->name);
		}
	}

	if (Status == LM_STATUS_LINK_ACTIVE) {
		if (pDevice->LineSpeed == LM_LINE_SPEED_1000MBPS)
			pUmDevice->line_speed = 1000;
		else if (pDevice->LineSpeed == LM_LINE_SPEED_100MBPS)
			pUmDevice->line_speed = 100;
		else if (pDevice->LineSpeed == LM_LINE_SPEED_10MBPS)
			pUmDevice->line_speed = 10;

		printk("%d Mbps ", pUmDevice->line_speed);

		if (pDevice->DuplexMode == LM_DUPLEX_MODE_FULL)
			printk("full duplex");
		else
			printk("half duplex");

		flow_control = pDevice->FlowControl &
			(LM_FLOW_CONTROL_RECEIVE_PAUSE |
			LM_FLOW_CONTROL_TRANSMIT_PAUSE);
		if (flow_control) {
			if (flow_control & LM_FLOW_CONTROL_RECEIVE_PAUSE) {
				printk(", receive ");
				if (flow_control & LM_FLOW_CONTROL_TRANSMIT_PAUSE)
					printk("& transmit ");
			}
			else {
				printk(", transmit ");
			}
			printk("flow control ON");
		}
		printk("\n");
	}
	return LM_STATUS_SUCCESS;
}

void
MM_UnmapRxDma(LM_DEVICE_BLOCK *pDevice, LM_PACKET *pPacket)
{
#if ! defined(NO_PCI_UNMAP)
	UM_DEVICE_BLOCK *pUmDevice = (UM_DEVICE_BLOCK *) pDevice;
	UM_PACKET *pUmPacket = (UM_PACKET *) pPacket;

	if (!pUmPacket->skbuff)
		return;

	pci_unmap_single(pUmDevice->pdev,
			pci_unmap_addr(pUmPacket, map[0]),
			pPacket->u.Rx.RxBufferSize,
			PCI_DMA_FROMDEVICE);
#endif
}

LM_STATUS
MM_FreeRxBuffer(PLM_DEVICE_BLOCK pDevice, PLM_PACKET pPacket)
{
	PUM_PACKET pUmPacket;
	struct sk_buff *skb;

	if (pPacket == 0)
		return LM_STATUS_SUCCESS;
	pUmPacket = (PUM_PACKET) pPacket;
	if ((skb = pUmPacket->skbuff)) {
		/* DMA address already unmapped */
		dev_kfree_skb(skb);
	}
	pUmPacket->skbuff = 0;
	return LM_STATUS_SUCCESS;
}

LM_STATUS
MM_Sleep(LM_DEVICE_BLOCK *pDevice, LM_UINT32 msec)
{
	current->state = TASK_INTERRUPTIBLE;
	if (schedule_timeout(HZ * msec / 1000) != 0) {
		return LM_STATUS_FAILURE;
	}
	if (signal_pending(current))
		return LM_STATUS_FAILURE;

	return LM_STATUS_SUCCESS;
}

void
bcm5700_shutdown(UM_DEVICE_BLOCK *pUmDevice)
{
	LM_DEVICE_BLOCK *pDevice = (LM_DEVICE_BLOCK *) pUmDevice;

	bcm5700_intr_off(pUmDevice);
	netif_carrier_off(pUmDevice->dev);
#ifdef BCM_TASKLET
	tasklet_kill(&pUmDevice->tasklet);
#endif
	LM_Halt(pDevice);
	pDevice->InitDone = 0;
	bcm5700_free_remaining_rx_bufs(pUmDevice);
}

void
bcm5700_free_remaining_rx_bufs(UM_DEVICE_BLOCK *pUmDevice)
{
	LM_DEVICE_BLOCK *pDevice = &pUmDevice->lm_dev;
	UM_PACKET *pUmPacket;
	int cnt, i;

	cnt = QQ_GetEntryCnt(&pUmDevice->rx_out_of_buf_q.Container);
	for (i = 0; i < cnt; i++) {
		if ((pUmPacket =
			QQ_PopHead(&pUmDevice->rx_out_of_buf_q.Container))
			!= 0) {

			MM_UnmapRxDma(pDevice, (LM_PACKET *) pUmPacket);
			MM_FreeRxBuffer(pDevice, &pUmPacket->lm_packet);
			QQ_PushTail(&pDevice->RxPacketFreeQ.Container,
				pUmPacket);
		}
	}
}

void
bcm5700_validate_param_range(UM_DEVICE_BLOCK *pUmDevice, int *param,
	char *param_name, int min, int max, int deflt)
{
	if (((unsigned int) *param < (unsigned int) min) ||
		((unsigned int) *param > (unsigned int) max)) {

		printk(KERN_WARNING "%s-%d: Invalid %s parameter (%u), using %u\n", bcm5700_driver, pUmDevice->index, param_name, (unsigned int) *param, (unsigned int) deflt);
		*param = deflt;
	}
}

struct net_device *
bcm5700_find_peer(struct net_device *dev)
{
	struct net_device *tmp_dev;
	UM_DEVICE_BLOCK *pUmDevice, *pUmTmp;
	LM_DEVICE_BLOCK *pDevice;

	tmp_dev = 0;
	pUmDevice = (UM_DEVICE_BLOCK *) dev->priv;
	pDevice = &pUmDevice->lm_dev;
	if (T3_ASIC_REV(pDevice->ChipRevId) == T3_ASIC_REV_5704) {
		tmp_dev = root_tigon3_dev;
		while (tmp_dev) {
			pUmTmp = (PUM_DEVICE_BLOCK) tmp_dev->priv;
			if ((tmp_dev != dev) &&
				(pUmDevice->pdev->bus->number ==
				pUmTmp->pdev->bus->number) &&
				PCI_SLOT(pUmDevice->pdev->devfn) ==
				PCI_SLOT(pUmTmp->pdev->devfn)) {

				break;
			}
			tmp_dev = pUmTmp->next_module;
		}
	}
	return tmp_dev;
}

LM_DEVICE_BLOCK *
MM_FindPeerDev(LM_DEVICE_BLOCK *pDevice)
{
	UM_DEVICE_BLOCK *pUmDevice = (UM_DEVICE_BLOCK *) pDevice;
	struct net_device *dev = pUmDevice->dev;
	struct net_device *peer_dev;

	peer_dev = bcm5700_find_peer(dev);
	if (!peer_dev)
		return 0;
	return ((LM_DEVICE_BLOCK *) peer_dev->priv);
}

#ifdef HAVE_POLL_CONTROLLER
STATIC void
poll_bcm5700(struct net_device *dev)
{
#ifdef RED_HAT_LINUX_KERNEL
	if (!netdump_mode)
#endif
		disable_irq(dev->irq);
	bcm5700_interrupt(dev->irq, dev, NULL);
#ifdef RED_HAT_LINUX_KERNEL
	if (!netdump_mode)
#endif
		enable_irq(dev->irq);
}
#endif
