Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH 00/23] per device dirty throttling -v8
From: Ray Lee @ 2007-08-04 16:01 UTC (permalink / raw)
  To: david@lang.hm
  Cc: Ingo Molnar, Linus Torvalds, Peter Zijlstra, linux-mm,
	linux-kernel, miklos, akpm, neilb, dgc, tomoki.sekiyama.qu,
	nikita, trond.myklebust, yingchao.zhou, richard, netdev
In-Reply-To: <Pine.LNX.4.64.0708040032570.6905@asgard.lang.hm>

(adding netdev cc:)

On 8/4/07, david@lang.hm <david@lang.hm> wrote:
> On Sat, 4 Aug 2007, Ingo Molnar wrote:
>
> > * Ingo Molnar <mingo@elte.hu> wrote:
> >
> >> There are positive reports in the never-ending "my system crawls like
> >> an XT when copying large files" bugzilla entry:
> >>
> >>  http://bugzilla.kernel.org/show_bug.cgi?id=7372
> >
> > i forgot this entry:
> >
> > " We recently upgraded our office to gigabit Ethernet and got some big
> >   AMD64 / 3ware boxes for file and vmware servers... only to find them
> >   almost useless under any kind of real load. I've built some patched
> >   2.6.21.6 kernels (using the bdi throttling patch you mentioned) to
> >   see if our various Debian Etch boxes run better. So far my testing
> >   shows a *great* improvement over the stock Debian 2.6.18 kernel on
> >   our configurations. "
> >
> > and bdi has been in -mm in the past i think, so we also know (to a
> > certain degree) that it does not hurt those workloads that are fine
> > either.
> >
> > [ my personal interest in this is the following regression: every time i
> >  start a large kernel build with DEBUG_INFO on a quad-core 4GB RAM box,
> >  i get up to 30 seconds complete pauses in Vim (and most other tasks),
> >  during plain editing of the source code. (which happens when Vim tries
> >  to write() to its swap/undo-file.) ]
>
> I have an issue that sounds like it's related.
>
> I've got a syslog server that's got two Opteron 246 cpu's, 16G ram, 2x140G
> 15k rpm drives (fusion MPT hardware mirroring), 16x500G 7200rpm SATA
> drives on 3ware 9500 cards (software raid6) running 2.6.20.3 with hz set
> at default and preempt turned off.
>
> I have syslog doing buffered writes to the SCSI drives and every 5 min a
> cron job copies the data to the raid array.
>
> I've found that if I do anything significant on the large raid array that
> the system looses a significant amount of the UDP syslog traffic, even
> though there should be pleanty of ram and cpu (and the spindles involved
> in the writes are not being touched), even a grep can cause up to 40%
> losses in the syslog traffic. I've experimented with nice levels (nicing
> down the grep and nicing up the syslogd) without a noticable effect on the
> losses.
>
> I've been planning to try a new kernel with hz=1000 to see if that would
> help, and after that experiment with the various preempt settings, but it
> sounds like the per-device queues may actually be more relavent to the
> problem.
>
> what would you suggest I test, and in what order and combination?

At least on a surface level, your report has some similarities to
http://lkml.org/lkml/2007/5/21/84 . In that message, John Miller
mentions several things he tried without effect:

< - I increased the max allowed receive buffer through
< proc/sys/net/core/rmem_max and the application calls the right
< syscall. "netstat -su" does not show any "packet receive errors".
<
< - After getting "kernel: swapper: page allocation failure.
< order:0, mode:0x20", I increased /proc/sys/vm/min_free_kbytes
<
< - ixgb.txt in kernel network documentation suggests to increase
< net.core.netdev_max_backlog to 300000. This did not help.
<
< - I also had to increase net.core.optmem_max, because the default
< value was too small for 700 multicast groups.

As they're all pretty simple to test, it may be worthwhile to give
them a shot just to rule things out.

Ray

^ permalink raw reply

* Re: [RFC 0/2][BNX2]: Add iSCSI support to BNX2 devices.
From: Jeff Garzik @ 2007-08-04  8:47 UTC (permalink / raw)
  To: Michael Chan
  Cc: davem, mchristi, netdev, open-iscsi, anilgv, talm, lusinsky, uri
In-Reply-To: <1186200229.18322.97.camel@dell>

Michael Chan wrote:
> [BNX2]: Add iSCSI support to BNX2 devices.
> 
> Modify bnx2 and add a cnic driver to support some offload functions
> needed by iSCSI.
> 
> Add a new open-iscsi driver to support iSCSI offload on bnx2 devices.
> 
> Signed-off-by: Anil Veerabhadrappa <anilgv@broadcom.com>
> Signed-off-by: Michael Chan <mchan@broadcom.com>
> 
> --
> 
> The complete patch is in:
> 
> ftp://Net_sys_anon@ftp1.broadcom.com/0001-BNX2-Add-iSCSI-support-to-BNX2-devices.patch
> 
> I broke this into 2 patches and omitted the firmware blob in the next 2
> emails for review.

patch #2/2 did not make it (to me personally nor to 
http://marc.info/?l=linux-netdev)

	Jeff




^ permalink raw reply

* Re: [PATCH] ixgbe: New driver for Pci-Express 10GbE 82598 support
From: Kok, Auke @ 2007-08-04  4:35 UTC (permalink / raw)
  To: jeff, netdev
  Cc: Auke Kok, ayyappan.veeraiyan, akpm, arjan, hch, shemminger,
	nhorman, inaky, mb, john.ronciak
In-Reply-To: <20070804040502.D9133F2C0F@doppio.foo-projects.org>

Auke Kok wrote:
> This patch adds support for the Intel 82598 PCI-Express 10GbE
> chipset. Devices will be available on the market soon.

Also available through http and git:

  http://foo-projects.org/~sofar/ixgbe-20070803-submission.patch
  http://foo-projects.org/~sofar/ixgbe-20070803-submission.patch.bz2

  git://lost.foo-projects.org/~ahkok/linux-2.6#ixgbe-20070803-submission

Cheers,

Auke

^ permalink raw reply

* [PATCH] ixgbe: New driver for Pci-Express 10GbE 82598 support
From: Auke Kok @ 2007-08-04  4:04 UTC (permalink / raw)
  To: jeff, netdev
  Cc: ayyappan.veeraiyan, akpm, arjan, hch, shemminger, nhorman, inaky,
	mb, john.ronciak

This patch adds support for the Intel 82598 PCI-Express 10GbE
chipset. Devices will be available on the market soon.

This version of the driver is largely the same as the last release:

* Driver uses a single RX and single TX queue, each using 1 MSI-X
  irq vector.
* Driver runs in NAPI mode only
* Driver is largely multiqueue-ready (TM)

Changes since last version:

* Removed ixgbe_param.c completely:
  - removed rx parameter (unused)
  - flowcontrol uses hardcoded defaults
  - flowcontrol can be enabled as usual per ethtool
  - LLI code was retired for now (it was disabled by default)
  - buffermode setting uses the optimal case (PS for jumbo,
    single buffers for 1500MTU's and below)
  - irq moderation settings were implemented using the ethtool
    coalesce settings:
    ethtool -c ethX - displays rx and tx irq delay moderation
    ethtool -C ethX rx-usecs N tx-usecs M - set irq delay moderation
    times for rx, tx.
    (in msi or legacy irq mode it will only use rx-usecs for both
    rx and tx)
* removed no longer existing ethtool perm_addr ops
* Namespace cleanup
* Sparse address space annotations
* Removed a bunch of dead/unused code
* DEBUG_SHIRQ panic fix when going ifdn

Signed-off-by: Auke Kok <auke-jan.h.kok@intel.com>
Signed-off-by: Ayyappan Veeraiyan <ayyappan.veeraiyan@intel.com>
---
 Documentation/networking/ixgbe.txt |   72 +
 MAINTAINERS                        |   10 +-
 drivers/net/Kconfig                |   22 +
 drivers/net/Makefile               |    1 +
 drivers/net/ixgbe/Makefile         |   36 +
 drivers/net/ixgbe/ixgbe.h          |  243 ++++
 drivers/net/ixgbe/ixgbe_82598.c    |  598 ++++++++
 drivers/net/ixgbe/ixgbe_api.c      |  553 +++++++
 drivers/net/ixgbe/ixgbe_api.h      |   80 ++
 drivers/net/ixgbe/ixgbe_common.c   | 1679 ++++++++++++++++++++++
 drivers/net/ixgbe/ixgbe_common.h   |   43 +
 drivers/net/ixgbe/ixgbe_ethtool.c  |  931 ++++++++++++
 drivers/net/ixgbe/ixgbe_main.c     | 2765 ++++++++++++++++++++++++++++++++++++
 drivers/net/ixgbe/ixgbe_osdep.h    |   69 +
 drivers/net/ixgbe/ixgbe_phy.c      |  601 ++++++++
 drivers/net/ixgbe/ixgbe_phy.h      |   40 +
 drivers/net/ixgbe/ixgbe_type.h     | 1390 ++++++++++++++++++
 17 files changed, 9127 insertions(+), 6 deletions(-)
 create mode 100644 Documentation/networking/ixgbe.txt
 create mode 100644 drivers/net/ixgbe/Makefile
 create mode 100644 drivers/net/ixgbe/ixgbe.h
 create mode 100644 drivers/net/ixgbe/ixgbe_82598.c
 create mode 100644 drivers/net/ixgbe/ixgbe_api.c
 create mode 100644 drivers/net/ixgbe/ixgbe_api.h
 create mode 100644 drivers/net/ixgbe/ixgbe_common.c
 create mode 100644 drivers/net/ixgbe/ixgbe_common.h
 create mode 100644 drivers/net/ixgbe/ixgbe_ethtool.c
 create mode 100644 drivers/net/ixgbe/ixgbe_main.c
 create mode 100644 drivers/net/ixgbe/ixgbe_osdep.h
 create mode 100644 drivers/net/ixgbe/ixgbe_phy.c
 create mode 100644 drivers/net/ixgbe/ixgbe_phy.h
 create mode 100644 drivers/net/ixgbe/ixgbe_type.h

diff --git a/Documentation/networking/ixgbe.txt b/Documentation/networking/ixgbe.txt
new file mode 100644
index 0000000..823d69c
--- /dev/null
+++ b/Documentation/networking/ixgbe.txt
@@ -0,0 +1,72 @@
+Linux* Base Driver for the 10 Gigabit Family of Adapters
+================================================================
+
+July 09, 2007
+
+
+Contents
+========
+
+- In This Release
+- Identifying Your Adapter
+- Command Line Parameters
+- Support
+
+In This Release
+===============
+
+This file describes the Linux* Base Driver for the 10 Gigabit PCI Express
+Family of Adapters.  This driver supports the 2.6.x kernel. This driver
+includes support for Itanium(R)2-based systems.
+
+The following features are now available in supported kernels:
+ - Native VLANs
+ - Channel Bonding (teaming)
+ - SNMP
+
+Channel Bonding documentation can be found in the Linux kernel source:
+/Documentation/networking/bonding.txt
+
+Instructions on updating ethtool can be found in the section "Additional
+Configurations" later in this document.
+
+
+Identifying Your Adapter
+========================
+
+The following Intel network adapters are compatible with the drivers in this
+release:
+
+Controller  Adapter Name                 Physical Layer
+----------  ------------                 --------------
+82598       Intel(R) 10GbE-LR/LRM/SR
+            Server Adapters              10G Base -SR (850 nm optical fiber)
+                                         10G Base -LRM (850 nm optical fiber)
+                                         10G Base -LR (1310 nm optical fiber)
+
+For more information on how to identify your adapter, go to the Adapter &
+Driver ID Guide at:
+
+    http://support.intel.com/support/network/sb/CS-012904.htm
+
+For the latest Intel network drivers for Linux, refer to the following
+website.  In the search field, enter your adapter name or type, or use the
+networking link on the left to search for your adapter:
+
+    http://downloadfinder.intel.com/scripts-df/support_intel.asp
+
+
+Support
+=======
+
+For general information, go to the Intel support website at:
+
+    http://support.intel.com
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+    http://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on the supported
+kernel with a supported adapter, email the specific information related
+to the issue to e1000-devel@lists.sf.net
diff --git a/MAINTAINERS b/MAINTAINERS
index e65e96a..66ebc66 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1998,16 +1998,14 @@ W:	http://sourceforge.net/projects/e1000/
 S:	Supported
 
 INTEL PRO/10GbE SUPPORT
-P:	Jeff Kirsher
-M:	jeffrey.t.kirsher@intel.com
 P:	Ayyappan Veeraiyan
 M:	ayyappan.veeraiyan@intel.com
-P:	John Ronciak
-M:	john.ronciak@intel.com
-P:	Jesse Brandeburg
-M:	jesse.brandeburg@intel.com
 P:	Auke Kok
 M:	auke-jan.h.kok@intel.com
+P:	Jesse Brandeburg
+M:	jesse.brandeburg@intel.com
+P:	John Ronciak
+M:	john.ronciak@intel.com
 L:	e1000-devel@lists.sourceforge.net
 W:	http://sourceforge.net/projects/e1000/
 S:	Supported
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 81ef81c..bbd25c7 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2405,6 +2405,28 @@ config EHEA
 	  To compile the driver as a module, choose M here. The module
 	  will be called ehea.
 
+config IXGBE
+	tristate "Intel(R) PRO/10GbE PCI Express adapters support"
+	depends on PCI
+	---help---
+	  This driver supports Intel(R) PRO/10GbE PCI Express family of
+	  adapters.  For more information on how to identify your adapter, go
+	  to the Adapter & Driver ID Guide at:
+
+	  <http://support.intel.com/support/network/adapter/pro100/21397.htm>
+
+	  For general information and support, go to the Intel support
+	  website at:
+
+	  <http://support.intel.com>
+
+	  More specific information on configuring the driver is in
+	  <file:Documentation/networking/ixgbe.txt>.
+
+	  To compile this driver as a module, choose M here and read
+	  <file:Documentation/networking/net-modules.txt>.  The module
+	  will be called ixgbe.
+
 config IXGB
 	tristate "Intel(R) PRO/10GbE support"
 	depends on PCI
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index e684212..7529c8f 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_E1000) += e1000/
 obj-$(CONFIG_IBM_EMAC) += ibm_emac/
+obj-$(CONFIG_IXGBE) += ixgbe/
 obj-$(CONFIG_IXGB) += ixgb/
 obj-$(CONFIG_CHELSIO_T1) += chelsio/
 obj-$(CONFIG_CHELSIO_T3) += cxgb3/
diff --git a/drivers/net/ixgbe/Makefile b/drivers/net/ixgbe/Makefile
new file mode 100644
index 0000000..61526f1
--- /dev/null
+++ b/drivers/net/ixgbe/Makefile
@@ -0,0 +1,36 @@
+################################################################################
+#
+# Intel PRO/10GbE PCI Express Linux driver
+# Copyright(c) 1999 - 2007 Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+# more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# The full GNU General Public License is included in this distribution in
+# the file called "COPYING".
+#
+# Contact Information:
+# Linux NICS <linux.nics@intel.com>
+# e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+# Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+#
+################################################################################
+
+#
+# Makefile for the Intel(R) PRO/10GbE PCI Express ethernet driver
+#
+
+obj-$(CONFIG_IXGBE) += ixgbe.o
+
+ixgbe-objs := ixgbe_main.o ixgbe_common.o ixgbe_api.o ixgbe_ethtool.o \
+              ixgbe_82598.o ixgbe_phy.o
diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h
new file mode 100644
index 0000000..74ddd6a
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe.h
@@ -0,0 +1,243 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _IXGBE_H_
+#define _IXGBE_H_
+
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/vmalloc.h>
+
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+
+#include "ixgbe_api.h"
+
+#define IXGBE_ERR(args...) printk(KERN_ERR "ixgbe: " args)
+
+#define PFX "ixgbe: "
+#define DPRINTK(nlevel, klevel, fmt, args...) \
+	(void)((NETIF_MSG_##nlevel & adapter->msg_enable) && \
+	printk(KERN_##klevel PFX "%s: %s: " fmt, adapter->netdev->name, \
+		__FUNCTION__ , ## args))
+
+/* TX/RX descriptor defines */
+#define IXGBE_DEFAULT_TXD			   1024
+#define IXGBE_MAX_TXD				   4096
+#define IXGBE_MIN_TXD				     64
+
+#define IXGBE_DEFAULT_RXD			   1024
+#define IXGBE_MAX_RXD				   4096
+#define IXGBE_MIN_RXD				     64
+
+#define IXGBE_DEFAULT_RXQ			   1
+#define IXGBE_MAX_RXQ				   1
+#define IXGBE_MIN_RXQ				   1
+
+/* flow control */
+#define DEFAULT_FCRTL				0x10000
+#define MIN_FCRTL				      0
+#define MAX_FCRTL				0x7FF80
+#define DEFAULT_FCRTH				0x20000
+#define MIN_FCRTH				      0
+#define MAX_FCRTH				0x7FFF0
+#define DEFAULT_FCPAUSE				 0x6800  /* may be too long */
+#define MIN_FCPAUSE				      0
+#define MAX_FCPAUSE				 0xFFFF
+
+/* interrupt throttling */
+#define IXGBE_ITR_DEFAULT_USECS		  125  /* 8000 irqs per second */
+#define IXGBE_ITR_MIN_USECS		    2  /* 500k */
+#define IXGBE_ITR_MAX_USECS		10000  /* 100 */
+
+
+#define MAXIMUM_ETHERNET_VLAN_SIZE (ETH_FRAME_LEN + ETH_FCS_LEN + VLAN_HLEN)
+
+/* Used for packet split */
+#define IXGBE_RXBUFFER_64    64
+#define IXGBE_RXBUFFER_128   128
+#define IXGBE_RXBUFFER_256   256
+/* Supported Rx Buffer Sizes */
+#define IXGBE_RXBUFFER_2048  2048
+
+#define IXGBE_RX_HDR_SIZE IXGBE_RXBUFFER_256
+
+/* How many Tx Descriptors do we need to call netif_wake_queue? */
+#define IXGBE_TX_QUEUE_WAKE 16
+
+/* How many Rx Buffers do we bundle into one write to the hardware ? */
+#define IXGBE_RX_BUFFER_WRITE	16	/* Must be power of 2 */
+#define IXGBE_TX_FLAGS_CSUM		(u32)(1)
+#define IXGBE_TX_FLAGS_VLAN		(u32)(1 << 1)
+#define IXGBE_TX_FLAGS_TSO		(u32)(1 << 2)
+#define IXGBE_TX_FLAGS_IPV4		(u32)(1 << 3)
+#define IXGBE_TX_FLAGS_VLAN_MASK	0xffff0000
+#define IXGBE_TX_FLAGS_VLAN_SHIFT	16
+
+/* wrapper around a pointer to a socket buffer,
+ * so a DMA handle can be stored along with the buffer */
+struct ixgbe_tx_buffer {
+	struct sk_buff *skb;
+	dma_addr_t dma;
+	unsigned long time_stamp;
+	u16 length;
+	u16 next_to_watch;
+};
+
+struct ixgbe_rx_buffer {
+	struct sk_buff *skb;
+	dma_addr_t dma;
+	struct page *page;
+	dma_addr_t page_dma;
+};
+
+struct ixgbe_queue_stats {
+	u64 packets;
+	u64 bytes;
+};
+
+struct ixgbe_ring {
+	struct ixgbe_adapter *adapter;	/* backlink */
+	void *desc;			/* descriptor ring memory */
+	dma_addr_t dma;			/* phys. address of descriptor ring */
+	unsigned int size;		/* length in bytes */
+	unsigned int count;		/* amount of descriptors */
+	unsigned int next_to_use;
+	unsigned int next_to_clean;
+
+	union {
+		struct ixgbe_tx_buffer *tx_buffer_info;
+		struct ixgbe_rx_buffer *rx_buffer_info;
+	};
+
+	u16 head;
+	u16 tail;
+
+	/* To protect race between sender and clean_tx_irq */
+	spinlock_t tx_lock;
+
+	struct ixgbe_queue_stats stats;
+
+	u32 eims_value;
+	u32 itr_val;
+	u16 itr_range;
+	u16 itr_register;
+
+	char name[IFNAMSIZ + 5];
+};
+
+#define IXGBE_DESC_UNUSED(R) \
+	((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->count) + \
+	(R)->next_to_clean - (R)->next_to_use - 1)
+
+#define IXGBE_RX_DESC_ADV(R, i)	    \
+	(&(((union ixgbe_adv_rx_desc *)((R).desc))[i]))
+#define IXGBE_TX_DESC_ADV(R, i)	    \
+	(&(((union ixgbe_adv_tx_desc *)((R).desc))[i]))
+#define IXGBE_TX_CTXTDESC_ADV(R, i)	    \
+	(&(((struct ixgbe_adv_tx_context_desc *)((R).desc))[i]))
+
+#define IXGBE_MAX_JUMBO_FRAME_SIZE	16128
+
+/* board specific private data structure */
+struct ixgbe_adapter {
+	struct timer_list watchdog_timer;
+	struct vlan_group *vlgrp;
+	u16 bd_number;
+	u16 rx_buf_len;
+	atomic_t irq_sem;
+	struct work_struct reset_task;
+
+	/* TX */
+	struct ixgbe_ring *tx_ring;	/* One per active queue */
+	u64 restart_queue;
+	u64 lsc_int;
+	u64 hw_tso_ctxt;
+	u64 hw_tso6_ctxt;
+	u32 tx_timeout_count;
+	bool detect_tx_hung;
+
+	/* RX */
+	struct ixgbe_ring *rx_ring;	/* One per active queue */
+	u64 hw_csum_tx_good;
+	u64 hw_csum_rx_error;
+	u64 hw_csum_rx_good;
+	u64 non_eop_descs;
+	int num_tx_queues;
+	int num_rx_queues;
+	struct msix_entry *msix_entries;
+
+	u64 rx_hdr_split;
+	u32 alloc_rx_page_failed;
+	u32 alloc_rx_buff_failed;
+	u32 flags;
+#define IXGBE_FLAG_RX_CSUM_ENABLED		(u32)(1)
+#define IXGBE_FLAG_MSI_ENABLED			(u32)(1 << 1)
+#define IXGBE_FLAG_MSIX_ENABLED			(u32)(1 << 2)
+#define IXGBE_FLAG_RX_PS_ENABLED		(u32)(1 << 3)
+#define IXGBE_FLAG_IN_NETPOLL			(u32)(1 << 4)
+
+	/* Interrupt Throttle Rate */
+	u32 rx_eitr;
+	u32 tx_eitr;
+
+	/* OS defined structs */
+	struct net_device *netdev;
+	struct pci_dev *pdev;
+	struct net_device_stats net_stats;
+
+	/* structs defined in ixgbe_hw.h */
+	struct ixgbe_hw hw;
+	u16 msg_enable;
+	struct ixgbe_hw_stats stats;
+	char lsc_name[IFNAMSIZ + 5];
+
+	u64 tx_busy;
+};
+
+extern char ixgbe_driver_name[];
+extern char ixgbe_driver_version[];
+
+extern int ixgbe_up(struct ixgbe_adapter *adapter);
+extern void ixgbe_down(struct ixgbe_adapter *adapter);
+extern void ixgbe_reset(struct ixgbe_adapter *adapter);
+extern int ixgbe_setup_rx_resources(struct ixgbe_adapter *adapter,
+				    struct ixgbe_ring *rxdr);
+extern int ixgbe_setup_tx_resources(struct ixgbe_adapter *adapter,
+				    struct ixgbe_ring *txdr);
+extern void ixgbe_free_rx_resources(struct ixgbe_adapter *adapter,
+				    struct ixgbe_ring *rx_ring);
+extern void ixgbe_free_tx_resources(struct ixgbe_adapter *adapter,
+				    struct ixgbe_ring *tx_ring);
+extern void ixgbe_update_stats(struct ixgbe_adapter *adapter);
+
+extern void ixgbe_set_ethtool_ops(struct net_device *netdev);
+extern s32 ixgbe_mac_addr_valid(u8 *mac_addr);
+extern void ixgbe_mta_set(struct ixgbe_hw *hw, u8 *mc_addr);
+extern void ixgbe_mc_addr_add(struct ixgbe_hw *hw, u8 *mc_addr);
+#endif /* _IXGBE_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_82598.c b/drivers/net/ixgbe/ixgbe_82598.c
new file mode 100644
index 0000000..917e6b7
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_82598.c
@@ -0,0 +1,598 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "ixgbe_type.h"
+#include "ixgbe_api.h"
+#include "ixgbe_common.h"
+#include "ixgbe_phy.h"
+
+#define IXGBE_82598_MAX_TX_QUEUES 32
+#define IXGBE_82598_MAX_RX_QUEUES 64
+#define IXGBE_82598_RAR_ENTRIES   16
+
+s32 ixgbe_init_shared_code_82598(struct ixgbe_hw *hw);
+
+static s32 ixgbe_assign_func_pointers_82598(struct ixgbe_hw *hw);
+static s32 ixgbe_get_link_settings_82598(struct ixgbe_hw *hw, u32 *speed,
+					 bool *autoneg);
+static s32 ixgbe_get_copper_link_settings_82598(struct ixgbe_hw *hw,
+						u32 *speed, bool *autoneg);
+static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw);
+static u32 ixgbe_get_num_of_tx_queues_82598(struct ixgbe_hw *hw);
+static u32 ixgbe_get_num_of_rx_queues_82598(struct ixgbe_hw *hw);
+static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw);
+static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, u32 *speed,
+				      bool *link_up);
+static s32 ixgbe_setup_mac_link_speed_82598(struct ixgbe_hw *hw, u32 speed,
+					    bool autoneg,
+					    bool autoneg_wait_to_complete);
+static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw);
+static s32 ixgbe_check_copper_link_82598(struct ixgbe_hw *hw, u32 *speed,
+					 bool *link_up);
+static s32 ixgbe_setup_copper_link_speed_82598(struct ixgbe_hw *hw, u32 speed,
+					       bool autoneg,
+					       bool autoneg_wait_to_complete);
+static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw);
+static u32 ixgbe_get_num_rx_addrs_82598(struct ixgbe_hw *hw);
+
+
+/**
+ *  ixgbe_init_shared_code_82598 - Inits func ptrs and MAC type
+ *  @hw: pointer to hardware structure
+ *
+ *  Initialize the shared code for 82598. This will assign function pointers
+ *  and assign the MAC type.  Does not touch the hardware.
+ **/
+s32 ixgbe_init_shared_code_82598(struct ixgbe_hw *hw)
+{
+	/* Set MAC type */
+	hw->mac.type = ixgbe_mac_82598EB;
+
+	/* Assign function pointers */
+	ixgbe_assign_func_pointers_82598(hw);
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_assign_func_pointers_82598 - Assigns 82598-specific funtion pointers
+ *  @hw: pointer to hardware structure
+ *
+ *  Note - Generic function pointers have already been assigned, so the
+ *  function pointers set here are only for 82598-specific functions.
+ **/
+static s32 ixgbe_assign_func_pointers_82598(struct ixgbe_hw *hw)
+{
+
+	hw->func.ixgbe_func_get_media_type =
+			       &ixgbe_get_media_type_82598;
+	hw->func.ixgbe_func_get_num_of_tx_queues =
+			       &ixgbe_get_num_of_tx_queues_82598;
+	hw->func.ixgbe_func_get_num_of_rx_queues =
+			       &ixgbe_get_num_of_rx_queues_82598;
+	hw->func.ixgbe_func_reset_hw = &ixgbe_reset_hw_82598;
+
+	hw->func.ixgbe_func_get_num_rx_addrs =
+			       &ixgbe_get_num_rx_addrs_82598;
+
+	/* Link */
+	if (ixgbe_get_media_type(hw) == ixgbe_media_type_copper) {
+		hw->func.ixgbe_func_setup_link =
+			       &ixgbe_setup_copper_link_82598;
+		hw->func.ixgbe_func_check_link =
+			       &ixgbe_check_copper_link_82598;
+		hw->func.ixgbe_func_setup_link_speed =
+			       &ixgbe_setup_copper_link_speed_82598;
+		hw->func.ixgbe_func_get_link_settings =
+			       &ixgbe_get_copper_link_settings_82598;
+	} else {
+		hw->func.ixgbe_func_setup_link =
+			       &ixgbe_setup_mac_link_82598;
+		hw->func.ixgbe_func_check_link =
+			       &ixgbe_check_mac_link_82598;
+		hw->func.ixgbe_func_setup_link_speed =
+			       &ixgbe_setup_mac_link_speed_82598;
+		hw->func.ixgbe_func_get_link_settings =
+			       &ixgbe_get_link_settings_82598;
+	}
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_get_link_settings_82598 - Determines default link settings
+ *  @hw: pointer to hardware structure
+ *  @speed: pointer to link speed
+ *  @autoneg: boolean auto-negotiation value
+ *
+ *  Determines the default link settings by reading the AUTOC register.
+ **/
+static s32 ixgbe_get_link_settings_82598(struct ixgbe_hw *hw, u32 *speed,
+					 bool *autoneg)
+{
+	s32 status = IXGBE_SUCCESS;
+	s32 autoc_reg;
+
+	autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+
+	if (hw->mac.link_settings_loaded) {
+		autoc_reg &= ~IXGBE_AUTOC_LMS_ATTACH_TYPE;
+		autoc_reg &= ~IXGBE_AUTOC_LMS_MASK;
+		autoc_reg |= hw->mac.link_attach_type;
+		autoc_reg |= hw->mac.link_mode_select;
+	}
+
+	switch (autoc_reg & IXGBE_AUTOC_LMS_MASK) {
+	case IXGBE_AUTOC_LMS_1G_LINK_NO_AN:
+		*speed = IXGBE_LINK_SPEED_1GB_FULL;
+		*autoneg = false;
+		break;
+
+	case IXGBE_AUTOC_LMS_10G_LINK_NO_AN:
+		*speed = IXGBE_LINK_SPEED_10GB_FULL;
+		*autoneg = false;
+		break;
+
+	case IXGBE_AUTOC_LMS_1G_AN:
+		*speed = IXGBE_LINK_SPEED_1GB_FULL;
+		*autoneg = true;
+		break;
+
+	case IXGBE_AUTOC_LMS_KX4_AN:
+	case IXGBE_AUTOC_LMS_KX4_AN_1G_AN:
+		*speed = IXGBE_LINK_SPEED_UNKNOWN;
+		if (autoc_reg & IXGBE_AUTOC_KX4_SUPP) {
+			*speed |= IXGBE_LINK_SPEED_10GB_FULL;
+		}
+		if (autoc_reg & IXGBE_AUTOC_KX_SUPP) {
+			*speed |= IXGBE_LINK_SPEED_1GB_FULL;
+		}
+		*autoneg = true;
+		break;
+
+	default:
+		status = IXGBE_ERR_LINK_SETUP;
+		break;
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_get_copper_link_settings_82598 - Determines default link settings
+ *  @hw: pointer to hardware structure
+ *  @speed: pointer to link speed
+ *  @autoneg: boolean auto-negotiation value
+ *
+ *  Determines the default link settings by reading the AUTOC register.
+ **/
+static s32 ixgbe_get_copper_link_settings_82598(struct ixgbe_hw *hw,
+					 	u32 *speed, bool *autoneg)
+{
+	s32 status = IXGBE_ERR_LINK_SETUP;
+	u16 speed_ability;
+
+	*speed = 0;
+	*autoneg = true;
+
+	status = ixgbe_read_phy_reg(hw, IXGBE_MDIO_PHY_SPEED_ABILITY,
+				    IXGBE_MDIO_PMA_PMD_DEV_TYPE,
+				    &speed_ability);
+
+	if (status == IXGBE_SUCCESS) {
+		if (speed_ability & IXGBE_MDIO_PHY_SPEED_10G)
+		    *speed |= IXGBE_LINK_SPEED_10GB_FULL;
+		if (speed_ability & IXGBE_MDIO_PHY_SPEED_1G)
+		    *speed |= IXGBE_LINK_SPEED_1GB_FULL;
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_get_media_type_82598 - Determines media type
+ *  @hw: pointer to hardware structure
+ *
+ *  Returns the media type (fiber, copper, backplane)
+ **/
+static enum ixgbe_media_type ixgbe_get_media_type_82598(struct ixgbe_hw *hw)
+{
+	enum ixgbe_media_type media_type;
+
+	/* Media type for I82598 is based on device ID */
+	switch (hw->device_id) {
+	case IXGBE_DEV_ID_82598:
+		/* Default device ID is mezzanine card KX/KX4 */
+		media_type = ixgbe_media_type_backplane;
+		break;
+	case IXGBE_DEV_ID_82598AF_DUAL_PORT:
+	case IXGBE_DEV_ID_82598AF_SINGLE_PORT:
+		media_type = ixgbe_media_type_fiber;
+		break;
+	default:
+		media_type = ixgbe_media_type_unknown;
+		break;
+	}
+
+	return media_type;
+}
+
+/**
+ *  ixgbe_get_num_of_tx_queues_82598 - Get number of TX queues
+ *  @hw: pointer to hardware structure
+ *
+ *  Returns the number of transmit queues for the given adapter.
+ **/
+static u32 ixgbe_get_num_of_tx_queues_82598(struct ixgbe_hw *hw)
+{
+	return IXGBE_82598_MAX_TX_QUEUES;
+}
+
+/**
+ *  ixgbe_get_num_of_rx_queues_82598 - Get number of RX queues
+ *  @hw: pointer to hardware structure
+ *
+ *  Returns the number of receive queues for the given adapter.
+ **/
+static u32 ixgbe_get_num_of_rx_queues_82598(struct ixgbe_hw *hw)
+{
+	return IXGBE_82598_MAX_RX_QUEUES;
+}
+
+/**
+ *  ixgbe_setup_mac_link_82598 - Configures MAC link settings
+ *  @hw: pointer to hardware structure
+ *
+ *  Configures link settings based on values in the ixgbe_hw struct.
+ *  Restarts the link.  Performs autonegotiation if needed.
+ **/
+static s32 ixgbe_setup_mac_link_82598(struct ixgbe_hw *hw)
+{
+	u32 autoc_reg;
+	u32 links_reg;
+	u32 i;
+	s32 status = IXGBE_SUCCESS;
+
+	autoc_reg = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+
+	if (hw->mac.link_settings_loaded) {
+		autoc_reg &= ~IXGBE_AUTOC_LMS_ATTACH_TYPE;
+		autoc_reg &= ~IXGBE_AUTOC_LMS_MASK;
+		autoc_reg |= hw->mac.link_attach_type;
+		autoc_reg |= hw->mac.link_mode_select;
+
+		IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc_reg);
+		msleep(50);
+	}
+
+	/* Restart link */
+	autoc_reg |= IXGBE_AUTOC_AN_RESTART;
+	IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc_reg);
+
+	/* Only poll for autoneg to complete if specified to do so */
+	if (hw->phy.autoneg_wait_to_complete) {
+		if (hw->mac.link_mode_select == IXGBE_AUTOC_LMS_KX4_AN ||
+		    hw->mac.link_mode_select == IXGBE_AUTOC_LMS_KX4_AN_1G_AN) {
+			links_reg = 0; /* Just in case Autoneg time = 0 */
+			for (i = 0; i < IXGBE_AUTO_NEG_TIME; i++) {
+				links_reg = IXGBE_READ_REG(hw, IXGBE_LINKS);
+				if (links_reg & IXGBE_LINKS_KX_AN_COMP)
+					break;
+				msleep(100);
+			}
+			if (!(links_reg & IXGBE_LINKS_KX_AN_COMP)) {
+				status = IXGBE_ERR_AUTONEG_NOT_COMPLETE;
+				DEBUGOUT("Autonegotiation did not complete.\n");
+			}
+		}
+	}
+
+	/* Add delay to filter out noises during initial link setup */
+	msleep(50);
+
+	return status;
+}
+
+/**
+ *  ixgbe_check_mac_link_82598 - Get link/speed status
+ *  @hw: pointer to hardware structure
+ *  @speed: pointer to link speed
+ *  @link_up: true is link is up, false otherwise
+ *
+ *  Reads the links register to determine if link is up and the current speed
+ **/
+static s32 ixgbe_check_mac_link_82598(struct ixgbe_hw *hw, u32 *speed,
+				      bool *link_up)
+{
+	u32 links_reg;
+
+	links_reg = IXGBE_READ_REG(hw, IXGBE_LINKS);
+
+	if (links_reg & IXGBE_LINKS_UP)
+		*link_up = true;
+	else
+		*link_up = false;
+
+	if (links_reg & IXGBE_LINKS_SPEED)
+		*speed = IXGBE_LINK_SPEED_10GB_FULL;
+	else
+		*speed = IXGBE_LINK_SPEED_1GB_FULL;
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_setup_mac_link_speed_82598 - Set MAC link speed
+ *  @hw: pointer to hardware structure
+ *  @speed: new link speed
+ *  @autoneg: true if auto-negotiation enabled
+ *  @autoneg_wait_to_complete: true if waiting is needed to complete
+ *
+ *  Set the link speed in the AUTOC register and restarts link.
+ **/
+static s32 ixgbe_setup_mac_link_speed_82598(struct ixgbe_hw *hw,
+					    u32 speed, bool autoneg,
+					    bool autoneg_wait_to_complete)
+{
+	s32 status = IXGBE_SUCCESS;
+
+	/* If speed is 10G, then check for CX4 or XAUI. */
+	if ((speed == IXGBE_LINK_SPEED_10GB_FULL) &&
+	    (!(hw->mac.link_attach_type & IXGBE_AUTOC_10G_KX4)))
+		hw->mac.link_mode_select = IXGBE_AUTOC_LMS_10G_LINK_NO_AN;
+	else if ((speed == IXGBE_LINK_SPEED_1GB_FULL) && (!autoneg))
+		hw->mac.link_mode_select = IXGBE_AUTOC_LMS_1G_LINK_NO_AN;
+	else if (autoneg) {
+		/* BX mode - Autonegotiate 1G */
+		if (!(hw->mac.link_attach_type & IXGBE_AUTOC_1G_PMA_PMD))
+			hw->mac.link_mode_select = IXGBE_AUTOC_LMS_1G_AN;
+		else /* KX/KX4 mode */
+			hw->mac.link_mode_select = IXGBE_AUTOC_LMS_KX4_AN_1G_AN;
+	} else {
+		status = IXGBE_ERR_LINK_SETUP;
+	}
+
+	if (status == IXGBE_SUCCESS) {
+		hw->phy.autoneg_wait_to_complete = autoneg_wait_to_complete;
+
+		hw->mac.link_settings_loaded = true;
+		/*
+		 * Setup and restart the link based on the new values in
+		 * ixgbe_hw This will write the AUTOC register based on the new
+		 * stored values
+		 */
+		ixgbe_setup_mac_link_82598(hw);
+	}
+
+	return status;
+}
+
+
+/**
+ *  ixgbe_setup_copper_link_82598 - Setup copper link settings
+ *  @hw: pointer to hardware structure
+ *
+ *  Configures link settings based on values in the ixgbe_hw struct.
+ *  Restarts the link.  Performs autonegotiation if needed.  Restart
+ *  phy and wait for autonegotiate to finish.  Then synchronize the
+ *  MAC and PHY.
+ **/
+static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw)
+{
+	s32 status;
+	u32 speed = 0;
+	bool link_up = false;
+
+	/* Set up MAC */
+	ixgbe_setup_mac_link_82598(hw);
+
+	/* Restart autonegotiation on PHY */
+	status = ixgbe_setup_phy_link(hw);
+
+	/* Synchronize MAC to PHY speed */
+	if (status == IXGBE_SUCCESS)
+		status = ixgbe_check_link(hw, &speed, &link_up);
+
+	return status;
+}
+
+/**
+ *  ixgbe_check_copper_link_82598 - Syncs MAC & PHY link settings
+ *  @hw: pointer to hardware structure
+ *  @speed: pointer to link speed
+ *  @link_up: true if link is up, false otherwise
+ *
+ *  Reads the mac link, phy link, and synchronizes the MAC to PHY.
+ **/
+static s32 ixgbe_check_copper_link_82598(struct ixgbe_hw *hw, u32 *speed,
+					 bool *link_up)
+{
+	s32 status;
+	u32 phy_speed = 0;
+	bool phy_link = false;
+
+	/* This is the speed and link the MAC is set at */
+	ixgbe_check_mac_link_82598(hw, speed, link_up);
+
+	/*
+	 * Check current speed and link status of the PHY register.
+	 * This is a vendor specific register and may have to
+	 * be changed for other copper PHYs.
+	 */
+	status = ixgbe_check_phy_link(hw, &phy_speed, &phy_link);
+
+	if ((status == IXGBE_SUCCESS) && (phy_link)) {
+		/*
+		 * Check current link status of the MACs link's register
+		 * matches that of the speed in the PHY register
+		 */
+		if (*speed != phy_speed) {
+			/*
+			 * The copper PHY requires 82598 attach type to be XAUI
+			 * for 10G and BX for 1G
+			 */
+			hw->mac.link_attach_type =
+				(IXGBE_AUTOC_10G_XAUI | IXGBE_AUTOC_1G_BX);
+
+			/* Synchronize the MAC speed to the PHY speed */
+			status = ixgbe_setup_mac_link_speed_82598(hw, phy_speed,
+								  false, false);
+			if (status == IXGBE_SUCCESS)
+				ixgbe_check_mac_link_82598(hw, speed, link_up);
+			else
+				status = IXGBE_ERR_LINK_SETUP;
+		}
+	} else {
+		*link_up = phy_link;
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_setup_copper_link_speed_82598 - Set the PHY autoneg advertised field
+ *  @hw: pointer to hardware structure
+ *  @speed: new link speed
+ *  @autoneg: true if autonegotiation enabled
+ *  @autoneg_wait_to_complete: true if waiting is needed to complete
+ *
+ *  Sets the link speed in the AUTOC register in the MAC and restarts link.
+ **/
+static s32 ixgbe_setup_copper_link_speed_82598(struct ixgbe_hw *hw, u32 speed,
+					       bool autoneg,
+					       bool autoneg_wait_to_complete)
+{
+	s32 status;
+	bool link_up = 0;
+
+	/* Setup the PHY according to input speed */
+	status = ixgbe_setup_phy_link_speed(hw, speed, autoneg,
+					    autoneg_wait_to_complete);
+
+	/* Synchronize MAC to PHY speed */
+	if (status == IXGBE_SUCCESS)
+		status = ixgbe_check_link(hw, &speed, &link_up);
+
+	return status;
+}
+
+/**
+ *  ixgbe_reset_hw_82598 - Performs hardware reset
+ *  @hw: pointer to hardware structure
+ *
+ *  Resets the hardware by reseting the transmit and receive units, masks and
+ *  clears all interrupts, performing a PHY reset, and performing a link (MAC)
+ *  reset.
+ **/
+static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw)
+{
+	s32 status = IXGBE_SUCCESS;
+	u32 ctrl;
+	u32 gheccr;
+	u32 i;
+	u32 autoc;
+
+	/* Call adapter stop to disable tx/rx and clear interrupts */
+	ixgbe_stop_adapter(hw);
+
+	/* Reset PHY */
+	ixgbe_reset_phy(hw);
+
+	/*
+	 * Prevent the PCI-E bus from from hanging by disabling PCI-E master
+	 * access and verify no pending requests before reset
+	 */
+	if (ixgbe_disable_pcie_master(hw) != IXGBE_SUCCESS) {
+		status = IXGBE_ERR_MASTER_REQUESTS_PENDING;
+		DEBUGOUT("PCI-E Master disable polling has failed.\n");
+	}
+
+	/*
+	 * Issue global reset to the MAC.  This needs to be a SW reset.
+	 * If link reset is used, it might reset the MAC when mng is using it
+	 */
+	ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
+	IXGBE_WRITE_REG(hw, IXGBE_CTRL, (ctrl | IXGBE_CTRL_RST));
+	IXGBE_WRITE_FLUSH(hw);
+
+	/* Poll for reset bit to self-clear indicating reset is complete */
+	for (i = 0; i < 10; i++) {
+		udelay(1);
+		ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
+		if (!(ctrl & IXGBE_CTRL_RST))
+			break;
+	}
+	if (ctrl & IXGBE_CTRL_RST) {
+		status = IXGBE_ERR_RESET_FAILED;
+		DEBUGOUT("Reset polling failed to complete.\n");
+	}
+
+	msleep(50);
+
+	gheccr = IXGBE_READ_REG(hw, IXGBE_GHECCR);
+	gheccr &= ~((1 << 21) | (1 << 18) | (1 << 9) | (1 << 6));
+	IXGBE_WRITE_REG(hw, IXGBE_GHECCR, gheccr);
+
+	/*
+	 * AUTOC register which stores link settings gets cleared
+	 * and reloaded from EEPROM after reset. We need to restore
+	 * our stored value from init in case SW changed the attach
+	 * type or speed.  If this is the first time and link settings
+	 * have not been stored, store default settings from AUTOC.
+	 */
+	autoc = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+	if (hw->mac.link_settings_loaded) {
+		autoc &= ~(IXGBE_AUTOC_LMS_ATTACH_TYPE);
+		autoc &= ~(IXGBE_AUTOC_LMS_MASK);
+		autoc |= hw->mac.link_attach_type;
+		autoc |= hw->mac.link_mode_select;
+		IXGBE_WRITE_REG(hw, IXGBE_AUTOC, autoc);
+	} else {
+		hw->mac.link_attach_type =
+					 (autoc & IXGBE_AUTOC_LMS_ATTACH_TYPE);
+		hw->mac.link_mode_select = (autoc & IXGBE_AUTOC_LMS_MASK);
+		hw->mac.link_settings_loaded = true;
+	}
+
+	/* Store the permanent mac address */
+	ixgbe_get_mac_addr(hw, hw->mac.perm_addr);
+
+	return status;
+}
+
+/**
+ *  ixgbe_get_num_rx_addrs_82598 - Get RX address registers
+ *  @hw: pointer to hardware structure
+ *
+ *  Returns the of RAR entries for the given adapter.
+ **/
+static u32 ixgbe_get_num_rx_addrs_82598(struct ixgbe_hw *hw)
+{
+	return IXGBE_82598_RAR_ENTRIES;
+}
+
diff --git a/drivers/net/ixgbe/ixgbe_api.c b/drivers/net/ixgbe/ixgbe_api.c
new file mode 100644
index 0000000..0694fdf
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_api.c
@@ -0,0 +1,553 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "ixgbe_api.h"
+#include "ixgbe_common.h"
+
+extern s32 ixgbe_init_shared_code_82598(struct ixgbe_hw *hw);
+extern s32 ixgbe_init_shared_code_phy(struct ixgbe_hw *hw);
+
+/**
+ *  ixgbe_init_shared_code - Initialize the shared code
+ *  @hw: pointer to hardware structure
+ *
+ *  This will assign function pointers and assign the MAC type and PHY code.
+ *  Does not touch the hardware. This function must be called prior to any
+ *  other function in the shared code. The ixgbe_hw structure should be
+ *  memset to 0 prior to calling this function.  The following fields in
+ *  hw structure should be filled in prior to calling this function:
+ *  hw_addr, back, device_id, vendor_id, subsystem_device_id,
+ *   subsystem_vendor_id, and revision_id
+ **/
+s32 ixgbe_init_shared_code(struct ixgbe_hw *hw)
+{
+	s32 status = IXGBE_ERR_DEVICE_NOT_SUPPORTED;
+
+	/*
+	 * Assign generic function pointers before entering adapter-specific
+	 * init
+	 */
+	ixgbe_assign_func_pointers_generic(hw);
+
+	if (hw->vendor_id == IXGBE_INTEL_VENDOR_ID) {
+		switch (hw->device_id) {
+		case IXGBE_DEV_ID_82598:
+		case IXGBE_DEV_ID_82598AF_SINGLE_PORT:
+		case IXGBE_DEV_ID_82598AF_DUAL_PORT:
+			status = ixgbe_init_shared_code_82598(hw);
+			status = ixgbe_init_shared_code_phy(hw);
+			break;
+		default:
+			status = IXGBE_ERR_DEVICE_NOT_SUPPORTED;
+			break;
+		}
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_init_hw - Initialize the hardware
+ *  @hw: pointer to hardware structure
+ *
+ *  Initialize the hardware by resetting and then starting the hardware
+ **/
+s32 ixgbe_init_hw(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_init_hw, (hw),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_reset_hw - Performs a hardware reset
+ *  @hw: pointer to hardware structure
+ *
+ *  Resets the hardware by resetting the transmit and receive units, masks and
+ *  clears all interrupts, performs a PHY reset, and performs a MAC reset
+ **/
+s32 ixgbe_reset_hw(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_reset_hw, (hw),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_start_hw - Prepares hardware for TX/TX
+ *  @hw: pointer to hardware structure
+ *
+ *  Starts the hardware by filling the bus info structure and media type,
+ *  clears all on chip counters, initializes receive address registers,
+ *  multicast table, VLAN filter table, calls routine to setup link and
+ *  flow control settings, and leaves transmit and receive units disabled
+ *  and uninitialized.
+ **/
+s32 ixgbe_start_hw(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_start_hw, (hw),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_clear_hw_cntrs - Clear hardware counters
+ *  @hw: pointer to hardware structure
+ *
+ *  Clears all hardware statistics counters by reading them from the hardware
+ *  Statistics counters are clear on read.
+ **/
+s32 ixgbe_clear_hw_cntrs(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_clear_hw_cntrs, (hw),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_get_media_type - Get media type
+ *  @hw: pointer to hardware structure
+ *
+ *  Returns the media type (fiber, copper, backplane)
+ **/
+enum ixgbe_media_type ixgbe_get_media_type(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_get_media_type, (hw),
+			       ixgbe_media_type_unknown);
+}
+
+/**
+ *  ixgbe_get_mac_addr - Get MAC address
+ *  @hw: pointer to hardware structure
+ *  @mac_addr: Adapter MAC address
+ *
+ *  Reads the adapter's MAC address from the first Receive Address Register
+ *  (RAR0) A reset of the adapter must have been performed prior to calling this
+ *  function in order for the MAC address to have been loaded from the EEPROM
+ *  into RAR0
+ **/
+s32 ixgbe_get_mac_addr(struct ixgbe_hw *hw, u8 *mac_addr)
+{
+	return ixgbe_call_func(hw, ixgbe_func_get_mac_addr,
+			       (hw, mac_addr), IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_get_bus_info - Set PCI bus info
+ *  @hw: pointer to hardware structure
+ *
+ *  Sets the PCI bus info (speed, width, type) within the ixgbe_hw structure
+ **/
+s32 ixgbe_get_bus_info(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_get_bus_info, (hw),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_get_num_of_tx_queues - Get TX queues
+ *  @hw: pointer to hardware structure
+ *
+ *  Returns the number of transmit queues for the given adapter.
+ **/
+u32 ixgbe_get_num_of_tx_queues(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_get_num_of_tx_queues,
+			       (hw), 0);
+}
+
+/**
+ *  ixgbe_stop_adapter - Disable TX/TX units
+ *  @hw: pointer to hardware structure
+ *
+ *  Sets the adapter_stopped flag within ixgbe_hw struct. Clears interrupts,
+ *  disables transmit and receive units. The adapter_stopped flag is used by
+ *  the shared code and drivers to determine if the adapter is in a stopped
+ *  state and should not touch the hardware.
+ **/
+s32 ixgbe_stop_adapter(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_stop_adapter, (hw),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_identify_phy - Get PHY type
+ *  @hw: pointer to hardware structure
+ *
+ *  Determines the physical layer module found on the current adapter.
+ **/
+s32 ixgbe_identify_phy(struct ixgbe_hw *hw)
+{
+	s32 status = IXGBE_SUCCESS;
+
+	if (hw->phy.type == ixgbe_phy_unknown) {
+		status = ixgbe_call_func(hw,
+					 ixgbe_func_identify_phy,
+					 (hw),
+					 IXGBE_NOT_IMPLEMENTED);
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_reset_phy - Perform a PHY reset
+ *  @hw: pointer to hardware structure
+ **/
+s32 ixgbe_reset_phy(struct ixgbe_hw *hw)
+{
+	s32 status = IXGBE_SUCCESS;
+
+	if (hw->phy.type == ixgbe_phy_unknown) {
+		if (ixgbe_identify_phy(hw) != IXGBE_SUCCESS) {
+		    status = IXGBE_ERR_PHY;
+		}
+	}
+
+	if (status == IXGBE_SUCCESS) {
+		status = ixgbe_call_func(hw,
+					 ixgbe_func_reset_phy,
+					 (hw),
+					 IXGBE_NOT_IMPLEMENTED);
+	}
+	return status;
+}
+
+/**
+ *  ixgbe_read_phy_reg - Read PHY register
+ *  @hw: pointer to hardware structure
+ *  @reg_addr: 32 bit address of PHY register to read
+ *  @phy_data: Pointer to read data from PHY register
+ *
+ *  Reads a value from a specified PHY register
+ **/
+s32 ixgbe_read_phy_reg(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type,
+		       u16 *phy_data)
+{
+	s32 status = IXGBE_SUCCESS;
+
+	if (hw->phy.type == ixgbe_phy_unknown) {
+		if (ixgbe_identify_phy(hw) != IXGBE_SUCCESS) {
+		    status = IXGBE_ERR_PHY;
+		}
+	}
+
+	if (status == IXGBE_SUCCESS) {
+		status = ixgbe_call_func(hw,
+					 ixgbe_func_read_phy_reg,
+					 (hw, reg_addr, device_type, phy_data),
+					 IXGBE_NOT_IMPLEMENTED);
+	}
+	return status;
+}
+
+/**
+ *  ixgbe_write_phy_reg - Write PHY register
+ *  @hw: pointer to hardware structure
+ *  @reg_addr: 32 bit PHY register to write
+ *  @phy_data: Data to write to the PHY register
+ *
+ *  Writes a value to specified PHY register
+ **/
+s32 ixgbe_write_phy_reg(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type,
+			u16 phy_data)
+{
+	s32 status = IXGBE_SUCCESS;
+
+	if (hw->phy.type == ixgbe_phy_unknown) {
+		if (ixgbe_identify_phy(hw) != IXGBE_SUCCESS) {
+		    status = IXGBE_ERR_PHY;
+		}
+	}
+
+	if (status == IXGBE_SUCCESS) {
+		status = ixgbe_call_func(hw,
+					 ixgbe_func_write_phy_reg,
+					 (hw, reg_addr, device_type, phy_data),
+					 IXGBE_NOT_IMPLEMENTED);
+	}
+	return status;
+}
+
+/**
+ *  ixgbe_setup_link - Configure link settings
+ *  @hw: pointer to hardware structure
+ *
+ *  Configures link settings based on values in the ixgbe_hw struct.
+ *  Restarts the link.  Performs autonegotiation if needed.
+ **/
+s32 ixgbe_setup_link(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_setup_link, (hw),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_check_link - Get link and speed status
+ *  @hw: pointer to hardware structure
+ *
+ *  Reads the links register to determine if link is up and the current speed
+ **/
+s32 ixgbe_check_link(struct ixgbe_hw *hw, u32 *speed,
+		     bool *link_up)
+{
+	return ixgbe_call_func(hw, ixgbe_func_check_link, (hw, speed, link_up),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_setup_link_speed - Set link speed
+ *  @hw: pointer to hardware structure
+ *  @speed: new link speed
+ *  @autoneg: true if autonegotiation enabled
+ *
+ *  Set the link speed and restarts the link.
+ **/
+s32 ixgbe_setup_link_speed(struct ixgbe_hw *hw, u32 speed,
+			   bool autoneg,
+			   bool autoneg_wait_to_complete)
+{
+	return ixgbe_call_func(hw, ixgbe_func_setup_link_speed, (hw, speed,
+			       autoneg, autoneg_wait_to_complete),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_led_on - Turn on LED's
+ *  @hw: pointer to hardware structure
+ *  @index: led number to turn on
+ *
+ *  Turns on the software controllable LEDs.
+ **/
+s32 ixgbe_led_on(struct ixgbe_hw *hw, u32 index)
+{
+	return ixgbe_call_func(hw, ixgbe_func_led_on, (hw, index),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_led_off - Turn off LED's
+ *  @hw: pointer to hardware structure
+ *  @index: led number to turn off
+ *
+ *  Turns off the software controllable LEDs.
+ **/
+s32 ixgbe_led_off(struct ixgbe_hw *hw, u32 index)
+{
+	return ixgbe_call_func(hw, ixgbe_func_led_off, (hw, index),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_init_eeprom_params - Initialiaze EEPROM parameters
+ *  @hw: pointer to hardware structure
+ *
+ *  Initializes the EEPROM parameters ixgbe_eeprom_info within the
+ *  ixgbe_hw struct in order to set up EEPROM access.
+ **/
+s32 ixgbe_init_eeprom_params(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_init_eeprom_params, (hw),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+
+/**
+ *  ixgbe_write_eeprom - Write word to EEPROM
+ *  @hw: pointer to hardware structure
+ *  @offset: offset within the EEPROM to be written to
+ *  @data: 16 bit word to be written to the EEPROM
+ *
+ *  Writes 16 bit value to EEPROM. If ixgbe_eeprom_update_checksum is not
+ *  called after this function, the EEPROM will most likely contain an
+ *  invalid checksum.
+ **/
+s32 ixgbe_write_eeprom(struct ixgbe_hw *hw, u16 offset, u16 data)
+{
+	s32 status;
+
+	/*
+	 * Initialize EEPROM parameters.  This will not do anything if the
+	 * EEPROM structure has already been initialized
+	 */
+	ixgbe_init_eeprom_params(hw);
+
+	/* Check for invalid offset */
+	if (offset >= hw->eeprom.word_size) {
+		status = IXGBE_ERR_EEPROM;
+	} else {
+		status = ixgbe_call_func(hw,
+					 ixgbe_func_write_eeprom,
+					 (hw, offset, data),
+					 IXGBE_NOT_IMPLEMENTED);
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_read_eeprom - Read word from EEPROM
+ *  @hw: pointer to hardware structure
+ *  @offset: offset within the EEPROM to be read
+ *  @data: read 16 bit value from EEPROM
+ *
+ *  Reads 16 bit value from EEPROM
+ **/
+s32 ixgbe_read_eeprom(struct ixgbe_hw *hw, u16 offset, u16 *data)
+{
+	s32 status;
+
+	/*
+	 * Initialize EEPROM parameters.  This will not do anything if the
+	 * EEPROM structure has already been initialized
+	 */
+	ixgbe_init_eeprom_params(hw);
+
+	/* Check for invalid offset */
+	if (offset >= hw->eeprom.word_size) {
+		status = IXGBE_ERR_EEPROM;
+	} else {
+		status = ixgbe_call_func(hw,
+					 ixgbe_func_read_eeprom,
+					 (hw, offset, data),
+					 IXGBE_NOT_IMPLEMENTED);
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_validate_eeprom_checksum - Validate EEPROM checksum
+ *  @hw: pointer to hardware structure
+ *  @checksum_val: calculated checksum
+ *
+ *  Performs checksum calculation and validates the EEPROM checksum
+ **/
+s32 ixgbe_validate_eeprom_checksum(struct ixgbe_hw *hw, u16 *checksum_val)
+{
+	return ixgbe_call_func(hw, ixgbe_func_validate_eeprom_checksum,
+			       (hw, checksum_val), IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_set_rar - Set RX address register
+ *  @hw: pointer to hardware structure
+ *  @addr: Address to put into receive address register
+ *  @index: Receive address register to write
+ *  @vind: Vind to set RAR to
+ *  @enable_addr: set flag that address is active
+ *
+ *  Puts an ethernet address into a receive address register.
+ **/
+s32 ixgbe_set_rar(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vind,
+		  u32 enable_addr)
+{
+	return ixgbe_call_func(hw, ixgbe_func_set_rar, (hw, index, addr, vind,
+			       enable_addr), IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_init_rx_addrs - Initializes receive address filters.
+ *  @hw: pointer to hardware structure
+ *
+ *  Places the MAC address in receive address register 0 and clears the rest
+ *  of the receive addresss registers. Clears the multicast table. Assumes
+ *  the receiver is in reset when the routine is called.
+ **/
+s32 ixgbe_init_rx_addrs(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_init_rx_addrs, (hw),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_get_num_rx_addrs - Returns the number of RAR entries.
+ *  @hw: pointer to hardware structure
+ **/
+u32 ixgbe_get_num_rx_addrs(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_get_num_rx_addrs, (hw), 0);
+}
+
+/**
+ *  ixgbe_update_mc_addr_list - Updates the MAC's list of multicast addresses
+ *  @hw: pointer to hardware structure
+ *  @mc_addr_list: the list of new multicast addresses
+ *  @mc_addr_count: number of addresses
+ *  @pad: number of bytes between addresses in the list
+ *
+ *  The given list replaces any existing list. Clears the MC addrs from receive
+ *  address registers and the multicast table. Uses unsed receive address
+ *  registers for the first multicast addresses, and hashes the rest into the
+ *  multicast table.
+ **/
+s32 ixgbe_update_mc_addr_list(struct ixgbe_hw *hw, u8 *mc_addr_list,
+			      u32 mc_addr_count, u32 pad)
+{
+	return ixgbe_call_func(hw, ixgbe_func_update_mc_addr_list, (hw,
+			       mc_addr_list, mc_addr_count,  pad),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_clear_vfta - Clear VLAN filter table
+ *  @hw: pointer to hardware structure
+ *
+ *  Clears the VLAN filer table, and the VMDq index associated with the filter
+ **/
+s32 ixgbe_clear_vfta(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_clear_vfta, (hw),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_set_vfta - Set VLAN filter table
+ *  @hw: pointer to hardware structure
+ *  @vlan: VLAN id to write to VLAN filter
+ *  @vind: VMDq output index that maps queue to VLAN id in VFTA
+ *  @vlan_on: boolean flag to turn on/off VLAN in VFTA
+ *
+ *  Turn on/off specified VLAN in the VLAN filter table.
+ **/
+s32 ixgbe_set_vfta(struct ixgbe_hw *hw, u32 vlan, u32 vind, bool vlan_on)
+{
+	return ixgbe_call_func(hw, ixgbe_func_set_vfta, (hw, vlan, vind,
+			       vlan_on), IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_setup_fc - Set flow control
+ *  @hw: pointer to hardware structure
+ *  @packetbuf_num: packet buffer number (0-7)
+ *
+ *  Configures the flow control settings based on SW configuration.
+ **/
+s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num)
+{
+	return ixgbe_call_func(hw, ixgbe_func_setup_fc, (hw, packetbuf_num),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
diff --git a/drivers/net/ixgbe/ixgbe_api.h b/drivers/net/ixgbe/ixgbe_api.h
new file mode 100644
index 0000000..ddd9b67
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_api.h
@@ -0,0 +1,80 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _IXGBE_API_H_
+#define _IXGBE_API_H_
+
+#include "ixgbe_type.h"
+
+s32 ixgbe_init_shared_code(struct ixgbe_hw *hw);
+
+s32 ixgbe_init_hw(struct ixgbe_hw *hw);
+s32 ixgbe_reset_hw(struct ixgbe_hw *hw);
+s32 ixgbe_start_hw(struct ixgbe_hw *hw);
+s32 ixgbe_clear_hw_cntrs(struct ixgbe_hw *hw);
+enum ixgbe_media_type ixgbe_get_media_type(struct ixgbe_hw *hw);
+s32 ixgbe_get_mac_addr(struct ixgbe_hw *hw, u8 *mac_addr);
+s32 ixgbe_get_bus_info(struct ixgbe_hw *hw);
+u32 ixgbe_get_num_of_tx_queues(struct ixgbe_hw *hw);
+s32 ixgbe_stop_adapter(struct ixgbe_hw *hw);
+
+s32 ixgbe_identify_phy(struct ixgbe_hw *hw);
+s32 ixgbe_reset_phy(struct ixgbe_hw *hw);
+s32 ixgbe_read_phy_reg(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type,
+		       u16 *phy_data);
+s32 ixgbe_write_phy_reg(struct ixgbe_hw *hw, u32 reg_addr, u32 device_type,
+			u16 phy_data);
+
+s32 ixgbe_setup_link(struct ixgbe_hw *hw);
+s32 ixgbe_setup_link_speed(struct ixgbe_hw *hw, u32 speed,
+			   bool autoneg, bool autoneg_wait_to_complete);
+s32 ixgbe_check_link(struct ixgbe_hw *hw, u32 *speed,
+		     bool *link_up);
+s32 ixgbe_led_on(struct ixgbe_hw *hw, u32 index);
+s32 ixgbe_led_off(struct ixgbe_hw *hw, u32 index);
+
+s32 ixgbe_init_eeprom_params(struct ixgbe_hw *hw);
+s32 ixgbe_write_eeprom(struct ixgbe_hw *hw, u16 offset, u16 data);
+s32 ixgbe_read_eeprom(struct ixgbe_hw *hw, u16 offset, u16 *data);
+s32 ixgbe_validate_eeprom_checksum(struct ixgbe_hw *hw, u16 *checksum_val);
+
+s32 ixgbe_set_rar(struct ixgbe_hw *hw, u32 index, u8 *addr,
+		      u32 vind, u32 enable_addr);
+s32 ixgbe_init_rx_addrs(struct ixgbe_hw *hw);
+u32 ixgbe_get_num_rx_addrs(struct ixgbe_hw *hw);
+s32 ixgbe_update_mc_addr_list(struct ixgbe_hw *hw, u8 *mc_addr_list,
+				  u32 mc_addr_count, u32 pad);
+s32 ixgbe_clear_vfta(struct ixgbe_hw *hw);
+s32 ixgbe_set_vfta(struct ixgbe_hw *hw, u32 vlan,
+		   u32 vind, bool vlan_on);
+
+s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num);
+
+s32 ixgbe_validate_mac_addr(u8 *mac_addr);
+
+#endif /* _IXGBE_API_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_common.c b/drivers/net/ixgbe/ixgbe_common.c
new file mode 100644
index 0000000..d882b4a
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_common.c
@@ -0,0 +1,1679 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "ixgbe_common.h"
+#include "ixgbe_api.h"
+
+static s32 ixgbe_acquire_eeprom(struct ixgbe_hw *hw);
+static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw);
+static void ixgbe_release_eeprom_semaphore(struct ixgbe_hw *hw);
+static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw);
+static void ixgbe_standby_eeprom(struct ixgbe_hw *hw);
+static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data,
+					u16 count);
+static u16 ixgbe_shift_in_eeprom_bits(struct ixgbe_hw *hw, u16 count);
+static void ixgbe_raise_eeprom_clk(struct ixgbe_hw *hw, u32 *eec);
+static void ixgbe_lower_eeprom_clk(struct ixgbe_hw *hw, u32 *eec);
+static void ixgbe_release_eeprom(struct ixgbe_hw *hw);
+static u16 ixgbe_calc_eeprom_checksum(struct ixgbe_hw *hw);
+
+static void ixgbe_enable_rar(struct ixgbe_hw *hw, u32 index);
+static void ixgbe_disable_rar(struct ixgbe_hw *hw, u32 index);
+static s32 ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr);
+static void ixgbe_add_mc_addr(struct ixgbe_hw *hw, u8 *mc_addr);
+
+static s32 ixgbe_init_hw_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr);
+static s32 ixgbe_get_bus_info_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw);
+
+static s32 ixgbe_led_on_generic(struct ixgbe_hw *hw, u32 index);
+static s32 ixgbe_led_off_generic(struct ixgbe_hw *hw, u32 index);
+
+static s32 ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_write_eeprom_generic(struct ixgbe_hw *hw, u16 offset,
+				      u16 data);
+static s32 ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset,
+					      u16 *data);
+static s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw,
+						  u16 *checksum_val);
+static s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw);
+
+static s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr,
+				 u32 vind, u32 enable_addr);
+static s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw,
+					     u8 *mc_addr_list,
+					     u32 mc_addr_count, u32 pad);
+static s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan,
+				  u32 vind, bool vlan_on);
+
+static s32 ixgbe_setup_fc_generic(struct ixgbe_hw *hw, s32 packtetbuf_num);
+static void ixgbe_set_mta(struct ixgbe_hw *hw, u8 *mc_addr);
+/**
+ *  ixgbe_assign_func_pointers_generic - Set generic func ptrs
+ *  @hw: pointer to hardware structure
+ *
+ *  Assigns generic function pointers.  Adapter-specific functions can
+ *  override the assignment of generic function pointers by assigning
+ *  their own adapter-specific function pointers.
+ **/
+s32 ixgbe_assign_func_pointers_generic(struct ixgbe_hw *hw)
+{
+	struct ixgbe_functions *f = &hw->func;
+
+	f->ixgbe_func_init_hw = &ixgbe_init_hw_generic;
+	f->ixgbe_func_start_hw = &ixgbe_start_hw_generic;
+	f->ixgbe_func_clear_hw_cntrs = &ixgbe_clear_hw_cntrs_generic;
+	f->ixgbe_func_get_mac_addr = &ixgbe_get_mac_addr_generic;
+	f->ixgbe_func_stop_adapter = &ixgbe_stop_adapter_generic;
+	f->ixgbe_func_get_bus_info = &ixgbe_get_bus_info_generic;
+	/* LED */
+	f->ixgbe_func_led_on = &ixgbe_led_on_generic;
+	f->ixgbe_func_led_off = &ixgbe_led_off_generic;
+	/* EEPROM */
+	f->ixgbe_func_init_eeprom_params = &ixgbe_init_eeprom_params_generic;
+	f->ixgbe_func_read_eeprom = &ixgbe_read_eeprom_bit_bang_generic;
+	f->ixgbe_func_write_eeprom = &ixgbe_write_eeprom_generic;
+	f->ixgbe_func_validate_eeprom_checksum =
+			       &ixgbe_validate_eeprom_checksum_generic;
+	f->ixgbe_func_update_eeprom_checksum =
+			       &ixgbe_update_eeprom_checksum_generic;
+	/* RAR, Multicast, VLAN */
+	f->ixgbe_func_set_rar = &ixgbe_set_rar_generic;
+	f->ixgbe_func_init_rx_addrs = &ixgbe_init_rx_addrs_generic;
+	f->ixgbe_func_update_mc_addr_list = &ixgbe_update_mc_addr_list_generic;
+	f->ixgbe_func_enable_mc = &ixgbe_enable_mc_generic;
+	f->ixgbe_func_disable_mc = &ixgbe_disable_mc_generic;
+	f->ixgbe_func_clear_vfta = &ixgbe_clear_vfta_generic;
+	f->ixgbe_func_set_vfta = &ixgbe_set_vfta_generic;
+	f->ixgbe_func_setup_fc = &ixgbe_setup_fc_generic;
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_start_hw_generic - Prepare hardware for TX/RX
+ *  @hw: pointer to hardware structure
+ *
+ *  Starts the hardware by filling the bus info structure and media type, clears
+ *  all on chip counters, initializes receive address registers, multicast
+ *  table, VLAN filter table, calls routine to set up link and flow control
+ *  settings, and leaves transmit and receive units disabled and uninitialized
+ **/
+static s32 ixgbe_start_hw_generic(struct ixgbe_hw *hw)
+{
+	u32 ctrl_ext;
+
+	/* Set the media type */
+	hw->phy.media_type = ixgbe_get_media_type(hw);
+
+	/* Set bus info */
+	ixgbe_get_bus_info(hw);
+
+	/* Identify the PHY */
+	ixgbe_identify_phy(hw);
+
+	/*
+	 * Store MAC address from RAR0, clear receive address registers, and
+	 * clear the multicast table
+	 */
+	ixgbe_init_rx_addrs(hw);
+
+	/* Clear the VLAN filter table */
+	ixgbe_clear_vfta(hw);
+
+	/* Set up link */
+	ixgbe_setup_link(hw);
+
+	/* Clear statistics registers */
+	ixgbe_clear_hw_cntrs(hw);
+
+	/* Set up flow control */
+	ixgbe_setup_fc(hw, 0);
+
+	/* Set No Snoop Disable */
+	ctrl_ext = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT);
+	ctrl_ext |= IXGBE_CTRL_EXT_NS_DIS;
+	IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, ctrl_ext);
+
+	/* Clear adapter stopped flag */
+	hw->adapter_stopped = false;
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_init_hw_generic - Generic hardware initialization
+ *  @hw: pointer to hardware structure
+ *
+ *  Initialize the hardware by reseting the hardware, filling the bus info
+ *  structure and media type, clears all on chip counters, initializes receive
+ *  address registers, multicast table, VLAN filter table, calls routine to set
+ *  up link and flow control settings, and leaves transmit and receive units
+ *  disabled and uninitialized
+ **/
+static s32 ixgbe_init_hw_generic(struct ixgbe_hw *hw)
+{
+	/* Reset the hardware */
+	ixgbe_reset_hw(hw);
+
+	/* Start the HW */
+	ixgbe_start_hw(hw);
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_clear_hw_cntrs_generic - Generic clear hardware counters
+ *  @hw: pointer to hardware structure
+ *
+ *  Clears all hardware statistics counters by reading them from the hardware
+ *  Statistics counters are clear on read.
+ **/
+static s32 ixgbe_clear_hw_cntrs_generic(struct ixgbe_hw *hw)
+{
+	u16 i = 0;
+
+	IXGBE_READ_REG(hw, IXGBE_CRCERRS);
+	IXGBE_READ_REG(hw, IXGBE_ILLERRC);
+	IXGBE_READ_REG(hw, IXGBE_ERRBC);
+	IXGBE_READ_REG(hw, IXGBE_MSPDC);
+	for (i = 0; i < 8; i++)
+		IXGBE_READ_REG(hw, IXGBE_MPC(i));
+
+	IXGBE_READ_REG(hw, IXGBE_MLFC);
+	IXGBE_READ_REG(hw, IXGBE_MRFC);
+	IXGBE_READ_REG(hw, IXGBE_RLEC);
+	IXGBE_READ_REG(hw, IXGBE_LXONTXC);
+	IXGBE_READ_REG(hw, IXGBE_LXONRXC);
+	IXGBE_READ_REG(hw, IXGBE_LXOFFTXC);
+	IXGBE_READ_REG(hw, IXGBE_LXOFFRXC);
+
+	for (i = 0; i < 8; i++) {
+		IXGBE_READ_REG(hw, IXGBE_PXONTXC(i));
+		IXGBE_READ_REG(hw, IXGBE_PXONRXC(i));
+		IXGBE_READ_REG(hw, IXGBE_PXOFFTXC(i));
+		IXGBE_READ_REG(hw, IXGBE_PXOFFRXC(i));
+	}
+
+	IXGBE_READ_REG(hw, IXGBE_PRC64);
+	IXGBE_READ_REG(hw, IXGBE_PRC127);
+	IXGBE_READ_REG(hw, IXGBE_PRC255);
+	IXGBE_READ_REG(hw, IXGBE_PRC511);
+	IXGBE_READ_REG(hw, IXGBE_PRC1023);
+	IXGBE_READ_REG(hw, IXGBE_PRC1522);
+	IXGBE_READ_REG(hw, IXGBE_GPRC);
+	IXGBE_READ_REG(hw, IXGBE_BPRC);
+	IXGBE_READ_REG(hw, IXGBE_MPRC);
+	IXGBE_READ_REG(hw, IXGBE_GPTC);
+	IXGBE_READ_REG(hw, IXGBE_GORCL);
+	IXGBE_READ_REG(hw, IXGBE_GORCH);
+	IXGBE_READ_REG(hw, IXGBE_GOTCL);
+	IXGBE_READ_REG(hw, IXGBE_GOTCH);
+	for (i = 0; i < 8; i++)
+		IXGBE_READ_REG(hw, IXGBE_RNBC(i));
+	IXGBE_READ_REG(hw, IXGBE_RUC);
+	IXGBE_READ_REG(hw, IXGBE_RFC);
+	IXGBE_READ_REG(hw, IXGBE_ROC);
+	IXGBE_READ_REG(hw, IXGBE_RJC);
+	IXGBE_READ_REG(hw, IXGBE_MNGPRC);
+	IXGBE_READ_REG(hw, IXGBE_MNGPDC);
+	IXGBE_READ_REG(hw, IXGBE_MNGPTC);
+	IXGBE_READ_REG(hw, IXGBE_TORL);
+	IXGBE_READ_REG(hw, IXGBE_TORH);
+	IXGBE_READ_REG(hw, IXGBE_TPR);
+	IXGBE_READ_REG(hw, IXGBE_TPT);
+	IXGBE_READ_REG(hw, IXGBE_PTC64);
+	IXGBE_READ_REG(hw, IXGBE_PTC127);
+	IXGBE_READ_REG(hw, IXGBE_PTC255);
+	IXGBE_READ_REG(hw, IXGBE_PTC511);
+	IXGBE_READ_REG(hw, IXGBE_PTC1023);
+	IXGBE_READ_REG(hw, IXGBE_PTC1522);
+	IXGBE_READ_REG(hw, IXGBE_MPTC);
+	IXGBE_READ_REG(hw, IXGBE_BPTC);
+	for (i = 0; i < 16; i++) {
+		IXGBE_READ_REG(hw, IXGBE_QPRC(i));
+		IXGBE_READ_REG(hw, IXGBE_QBRC(i));
+		IXGBE_READ_REG(hw, IXGBE_QPTC(i));
+		IXGBE_READ_REG(hw, IXGBE_QBTC(i));
+	}
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_get_mac_addr_generic - Generic get MAC address
+ *  @hw: pointer to hardware structure
+ *  @mac_addr: Adapter MAC address
+ *
+ *  Reads the adapter's MAC address from first Receive Address Register (RAR0)
+ *  A reset of the adapter must be performed prior to calling this function
+ *  in order for the MAC address to have been loaded from the EEPROM into RAR0
+ **/
+static s32 ixgbe_get_mac_addr_generic(struct ixgbe_hw *hw, u8 *mac_addr)
+{
+	u32 rar_high;
+	u32 rar_low;
+	u16 i;
+
+	rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(0));
+	rar_low = IXGBE_READ_REG(hw, IXGBE_RAL(0));
+
+	for (i = 0; i < 4; i++)
+		mac_addr[i] = (u8)(rar_low >> (i*8));
+
+	for (i = 0; i < 2; i++)
+		mac_addr[i+4] = (u8)(rar_high >> (i*8));
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_get_bus_info_generic - Generic set PCI bus info
+ *  @hw: pointer to hardware structure
+ *
+ *  Sets the PCI bus info (speed, width, type) within the ixgbe_hw structure
+ **/
+static s32 ixgbe_get_bus_info_generic(struct ixgbe_hw *hw)
+{
+	u16 link_status;
+
+	hw->bus.type = ixgbe_bus_type_pci_express;
+
+	/* Get the negotiated link width and speed from PCI config space */
+	link_status = IXGBE_READ_PCIE_WORD(hw, IXGBE_PCI_LINK_STATUS);
+
+	switch (link_status & IXGBE_PCI_LINK_WIDTH) {
+	case IXGBE_PCI_LINK_WIDTH_1:
+		hw->bus.width = ixgbe_bus_width_pcie_x1;
+		break;
+	case IXGBE_PCI_LINK_WIDTH_2:
+		hw->bus.width = ixgbe_bus_width_pcie_x2;
+		break;
+	case IXGBE_PCI_LINK_WIDTH_4:
+		hw->bus.width = ixgbe_bus_width_pcie_x4;
+		break;
+	case IXGBE_PCI_LINK_WIDTH_8:
+		hw->bus.width = ixgbe_bus_width_pcie_x8;
+		break;
+	default:
+		hw->bus.width = ixgbe_bus_width_unknown;
+		break;
+	}
+
+	switch (link_status & IXGBE_PCI_LINK_SPEED) {
+	case IXGBE_PCI_LINK_SPEED_2500:
+		hw->bus.speed = ixgbe_bus_speed_2500;
+		break;
+	case IXGBE_PCI_LINK_SPEED_5000:
+		hw->bus.speed = ixgbe_bus_speed_5000;
+		break;
+	default:
+		hw->bus.speed = ixgbe_bus_speed_unknown;
+		break;
+	}
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_stop_adapter_generic - Generic stop TX/RX units
+ *  @hw: pointer to hardware structure
+ *
+ *  Sets the adapter_stopped flag within ixgbe_hw struct. Clears interrupts,
+ *  disables transmit and receive units. The adapter_stopped flag is used by
+ *  the shared code and drivers to determine if the adapter is in a stopped
+ *  state and should not touch the hardware.
+ **/
+static s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw)
+{
+	u32 number_of_queues;
+	u32 reg_val;
+	u16 i;
+
+	/*
+	 * Set the adapter_stopped flag so other driver functions stop touching
+	 * the hardware
+	 */
+	hw->adapter_stopped = true;
+
+	/* Disable the receive unit */
+	reg_val = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
+	reg_val &= ~(IXGBE_RXCTRL_RXEN);
+	IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, reg_val);
+	msleep(2);
+
+	/* Clear interrupt mask to stop from interrupts being generated */
+	IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_IRQ_CLEAR_MASK);
+
+	/* Clear any pending interrupts */
+	IXGBE_READ_REG(hw, IXGBE_EICR);
+
+	/* Disable the transmit unit.  Each queue must be disabled. */
+	number_of_queues = ixgbe_get_num_of_tx_queues(hw);
+	for (i = 0; i < number_of_queues; i++) {
+		reg_val = IXGBE_READ_REG(hw, IXGBE_TXDCTL(i));
+		if (reg_val & IXGBE_TXDCTL_ENABLE) {
+			reg_val &= ~IXGBE_TXDCTL_ENABLE;
+			IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(i), reg_val);
+		}
+	}
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_led_on_generic - Turns on the software controllable LEDs.
+ *  @hw: pointer to hardware structure
+ *  @index: led number to turn on
+ **/
+static s32 ixgbe_led_on_generic(struct ixgbe_hw *hw, u32 index)
+{
+	u32 led_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL);
+
+	/* To turn on the LED, set mode to ON. */
+	led_reg &= ~IXGBE_LED_MODE_MASK(index);
+	led_reg |= IXGBE_LED_ON << IXGBE_LED_MODE_SHIFT(index);
+	IXGBE_WRITE_REG(hw, IXGBE_LEDCTL, led_reg);
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_led_off_generic - Turns off the software controllable LEDs.
+ *  @hw: pointer to hardware structure
+ *  @index: led number to turn off
+ **/
+static s32 ixgbe_led_off_generic(struct ixgbe_hw *hw, u32 index)
+{
+	u32 led_reg = IXGBE_READ_REG(hw, IXGBE_LEDCTL);
+
+	/* To turn off the LED, set mode to OFF. */
+	led_reg &= ~IXGBE_LED_MODE_MASK(index);
+	led_reg |= IXGBE_LED_OFF << IXGBE_LED_MODE_SHIFT(index);
+	IXGBE_WRITE_REG(hw, IXGBE_LEDCTL, led_reg);
+
+	return IXGBE_SUCCESS;
+}
+
+
+/**
+ *  ixgbe_init_eeprom_params_generic - Initialize EEPROM params
+ *  @hw: pointer to hardware structure
+ *
+ *  Initializes the EEPROM parameters ixgbe_eeprom_info within the
+ *  ixgbe_hw struct in order to set up EEPROM access.
+ **/
+static s32 ixgbe_init_eeprom_params_generic(struct ixgbe_hw *hw)
+{
+	struct ixgbe_eeprom_info *eeprom = &hw->eeprom;
+	u32 eec;
+	u16 eeprom_size;
+
+	if (eeprom->type == ixgbe_eeprom_uninitialized) {
+		eeprom->type = ixgbe_eeprom_none;
+
+		/*
+		 * Check for EEPROM present first.
+		 * If not present leave as none
+		 */
+		eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+		if (eec & IXGBE_EEC_PRES) {
+			eeprom->type = ixgbe_eeprom_spi;
+
+			/*
+			 * SPI EEPROM is assumed here.  This code would need to
+			 * change if a future EEPROM is not SPI.
+			 */
+			eeprom_size = (u16)((eec & IXGBE_EEC_SIZE) >>
+					    IXGBE_EEC_SIZE_SHIFT);
+			eeprom->word_size = 1 << (eeprom_size +
+						  IXGBE_EEPROM_WORD_SIZE_SHIFT);
+		}
+
+		if (eec & IXGBE_EEC_ADDR_SIZE)
+			eeprom->address_bits = 16;
+		else
+			eeprom->address_bits = 8;
+		DEBUGOUT3("Eeprom params: type = %d, size = %d, address bits: "
+			  "%d\n", eeprom->type, eeprom->word_size,
+			  eeprom->address_bits);
+	}
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_write_eeprom_generic - Writes 16 bit value to EEPROM
+ *  @hw: pointer to hardware structure
+ *  @offset: offset within the EEPROM to be written to
+ *  @data: 16 bit word to be written to the EEPROM
+ *
+ *  If ixgbe_eeprom_update_checksum is not called after this function, the
+ *  EEPROM will most likely contain an invalid checksum.
+ **/
+static s32 ixgbe_write_eeprom_generic(struct ixgbe_hw *hw, u16 offset,
+				      u16 data)
+{
+	s32 status;
+	u8 write_opcode = IXGBE_EEPROM_WRITE_OPCODE_SPI;
+
+	/* Prepare the EEPROM for writing  */
+	status = ixgbe_acquire_eeprom(hw);
+
+	if (status == IXGBE_SUCCESS) {
+		if (ixgbe_ready_eeprom(hw) != IXGBE_SUCCESS) {
+			ixgbe_release_eeprom(hw);
+			status = IXGBE_ERR_EEPROM;
+		}
+	}
+
+	if (status == IXGBE_SUCCESS) {
+		ixgbe_standby_eeprom(hw);
+
+		/*  Send the WRITE ENABLE command (8 bit opcode )  */
+		ixgbe_shift_out_eeprom_bits(hw, IXGBE_EEPROM_WREN_OPCODE_SPI,
+					    IXGBE_EEPROM_OPCODE_BITS);
+
+		ixgbe_standby_eeprom(hw);
+
+		/*
+		 * Some SPI eeproms use the 8th address bit embedded in the
+		 * opcode
+		 */
+		if ((hw->eeprom.address_bits == 8) && (offset >= 128))
+			write_opcode |= IXGBE_EEPROM_A8_OPCODE_SPI;
+
+		/* Send the Write command (8-bit opcode + addr) */
+		ixgbe_shift_out_eeprom_bits(hw, write_opcode,
+					    IXGBE_EEPROM_OPCODE_BITS);
+		ixgbe_shift_out_eeprom_bits(hw, (u16)(offset*2),
+					    hw->eeprom.address_bits);
+
+		/* Send the data */
+		data = (data >> 8) | (data << 8);
+		ixgbe_shift_out_eeprom_bits(hw, data, 16);
+		ixgbe_standby_eeprom(hw);
+
+		msleep(10);
+
+		/* Done with writing - release the EEPROM */
+		ixgbe_release_eeprom(hw);
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_read_eeprom_bit_bang_generic - Read EEPROM word using bit-bang
+ *  @hw: pointer to hardware structure
+ *  @offset: offset within the EEPROM to be read
+ *  @data: read 16 bit value from EEPROM
+ *
+ *  Reads 16 bit value from EEPROM through bit-bang method
+ **/
+static s32 ixgbe_read_eeprom_bit_bang_generic(struct ixgbe_hw *hw, u16 offset,
+					      u16 *data)
+{
+	s32 status;
+	u16 word_in;
+	u8 read_opcode = IXGBE_EEPROM_READ_OPCODE_SPI;
+
+	/* Prepare the EEPROM for reading  */
+	status = ixgbe_acquire_eeprom(hw);
+
+	if (status == IXGBE_SUCCESS) {
+		if (ixgbe_ready_eeprom(hw) != IXGBE_SUCCESS) {
+			ixgbe_release_eeprom(hw);
+			status = IXGBE_ERR_EEPROM;
+		}
+	}
+
+	if (status == IXGBE_SUCCESS) {
+		ixgbe_standby_eeprom(hw);
+
+		/*
+		 * Some SPI eeproms use the 8th address bit embedded in the
+		 * opcode
+		 */
+		if ((hw->eeprom.address_bits == 8) && (offset >= 128))
+			read_opcode |= IXGBE_EEPROM_A8_OPCODE_SPI;
+
+		/* Send the READ command (opcode + addr) */
+		ixgbe_shift_out_eeprom_bits(hw, read_opcode,
+					    IXGBE_EEPROM_OPCODE_BITS);
+		ixgbe_shift_out_eeprom_bits(hw, (u16)(offset*2),
+					    hw->eeprom.address_bits);
+
+		/* Read the data. */
+		word_in = ixgbe_shift_in_eeprom_bits(hw, 16);
+		*data = (word_in >> 8) | (word_in << 8);
+
+		/* End this read operation */
+		ixgbe_release_eeprom(hw);
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_acquire_eeprom - Acquire EEPROM using bit-bang
+ *  @hw: pointer to hardware structure
+ *
+ *  Prepares EEPROM for access using bit-bang method. This function should
+ *  be called before issuing a command to the EEPROM.
+ **/
+static s32 ixgbe_acquire_eeprom(struct ixgbe_hw *hw)
+{
+	s32 status = IXGBE_SUCCESS;
+	u32 eec;
+	u32 i;
+
+	if (ixgbe_acquire_swfw_sync(hw, IXGBE_GSSR_EEP_SM) != IXGBE_SUCCESS)
+		status = IXGBE_ERR_SWFW_SYNC;
+
+	if (status == IXGBE_SUCCESS) {
+		eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+		/* Request EEPROM Access */
+		eec |= IXGBE_EEC_REQ;
+		IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+
+		for (i = 0; i < IXGBE_EEPROM_GRANT_ATTEMPTS; i++) {
+			eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+			if (eec & IXGBE_EEC_GNT)
+				break;
+			udelay(5);
+		}
+
+		/* Release if grant not aquired */
+		if (!(eec & IXGBE_EEC_GNT)) {
+			eec &= ~IXGBE_EEC_REQ;
+			IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+			DEBUGOUT("Could not acquire EEPROM grant\n");
+
+			ixgbe_release_swfw_sync(hw, IXGBE_GSSR_EEP_SM);
+			status = IXGBE_ERR_EEPROM;
+		}
+	}
+
+	/* Setup EEPROM for Read/Write */
+	if (status == IXGBE_SUCCESS) {
+		/* Clear CS and SK */
+		eec &= ~(IXGBE_EEC_CS | IXGBE_EEC_SK);
+		IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+		IXGBE_WRITE_FLUSH(hw);
+		udelay(1);
+	}
+	return status;
+}
+
+/**
+ *  ixgbe_get_eeprom_semaphore - Get hardware semaphore
+ *  @hw: pointer to hardware structure
+ *
+ *  Sets the hardware semaphores so EEPROM access can occur for bit-bang method
+ **/
+static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw)
+{
+	s32 status = IXGBE_ERR_EEPROM;
+	u32 timeout;
+	u32 i;
+	u32 swsm;
+
+	/* Set timeout value based on size of EEPROM */
+	timeout = hw->eeprom.word_size + 1;
+
+	/* Get SMBI software semaphore between device drivers first */
+	for (i = 0; i < timeout; i++) {
+		/*
+		 * If the SMBI bit is 0 when we read it, then the bit will be
+		 * set and we have the semaphore
+		 */
+		swsm = IXGBE_READ_REG(hw, IXGBE_SWSM);
+		if (!(swsm & IXGBE_SWSM_SMBI)) {
+			status = IXGBE_SUCCESS;
+			break;
+		}
+		msleep(1);
+	}
+
+	/* Now get the semaphore between SW/FW through the SWESMBI bit */
+	if (status == IXGBE_SUCCESS) {
+		for (i = 0; i < timeout; i++) {
+			swsm = IXGBE_READ_REG(hw, IXGBE_SWSM);
+
+			/* Set the SW EEPROM semaphore bit to request access */
+			swsm |= IXGBE_SWSM_SWESMBI;
+			IXGBE_WRITE_REG(hw, IXGBE_SWSM, swsm);
+
+			/*
+			 * If we set the bit successfully then we got the
+			 * semaphore.
+			 */
+			swsm = IXGBE_READ_REG(hw, IXGBE_SWSM);
+			if (swsm & IXGBE_SWSM_SWESMBI)
+				break;
+
+			udelay(50);
+		}
+
+		/*
+		 * Release semaphores and return error if SW EEPROM semaphore
+		 * was not granted because we don't have access to the EEPROM
+		 */
+		if (i >= timeout) {
+			DEBUGOUT("Driver can't access the Eeprom - Semaphore "
+				 "not granted.\n");
+			ixgbe_release_eeprom_semaphore(hw);
+			status = IXGBE_ERR_EEPROM;
+		}
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_release_eeprom_semaphore - Release hardware semaphore
+ *  @hw: pointer to hardware structure
+ *
+ *  This function clears hardware semaphore bits.
+ **/
+static void ixgbe_release_eeprom_semaphore(struct ixgbe_hw *hw)
+{
+	u32 swsm;
+
+	swsm = IXGBE_READ_REG(hw, IXGBE_SWSM);
+
+	/* Release both semaphores by writing 0 to the bits SWESMBI and SMBI */
+	swsm &= ~(IXGBE_SWSM_SWESMBI | IXGBE_SWSM_SMBI);
+	IXGBE_WRITE_REG(hw, IXGBE_SWSM, swsm);
+}
+
+/**
+ *  ixgbe_ready_eeprom - Polls for EEPROM ready
+ *  @hw: pointer to hardware structure
+ **/
+static s32 ixgbe_ready_eeprom(struct ixgbe_hw *hw)
+{
+	s32 status = IXGBE_SUCCESS;
+	u16 i;
+	u8 spi_stat_reg;
+
+	/*
+	 * Read "Status Register" repeatedly until the LSB is cleared.  The
+	 * EEPROM will signal that the command has been completed by clearing
+	 * bit 0 of the internal status register.  If it's not cleared within
+	 * 5 milliseconds, then error out.
+	 */
+	for (i = 0; i < IXGBE_EEPROM_MAX_RETRY_SPI; i += 5) {
+		ixgbe_shift_out_eeprom_bits(hw, IXGBE_EEPROM_RDSR_OPCODE_SPI,
+					    IXGBE_EEPROM_OPCODE_BITS);
+		spi_stat_reg = (u8)ixgbe_shift_in_eeprom_bits(hw, 8);
+		if (!(spi_stat_reg & IXGBE_EEPROM_STATUS_RDY_SPI))
+			break;
+
+		udelay(5);
+		ixgbe_standby_eeprom(hw);
+	};
+
+	/*
+	 * On some parts, SPI write time could vary from 0-20mSec on 3.3V
+	 * devices (and only 0-5mSec on 5V devices)
+	 */
+	if (i >= IXGBE_EEPROM_MAX_RETRY_SPI) {
+		DEBUGOUT("SPI EEPROM Status error\n");
+		status = IXGBE_ERR_EEPROM;
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_standby_eeprom - Returns EEPROM to a "standby" state
+ *  @hw: pointer to hardware structure
+ **/
+static void ixgbe_standby_eeprom(struct ixgbe_hw *hw)
+{
+	u32 eec;
+
+	eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+	/* Toggle CS to flush commands */
+	eec |= IXGBE_EEC_CS;
+	IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+	IXGBE_WRITE_FLUSH(hw);
+	udelay(1);
+	eec &= ~IXGBE_EEC_CS;
+	IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+	IXGBE_WRITE_FLUSH(hw);
+	udelay(1);
+}
+
+/**
+ *  ixgbe_shift_out_eeprom_bits - Shift data bits out to the EEPROM.
+ *  @hw: pointer to hardware structure
+ *  @data: data to send to the EEPROM
+ *  @count: number of bits to shift out
+ **/
+static void ixgbe_shift_out_eeprom_bits(struct ixgbe_hw *hw, u16 data,
+					u16 count)
+{
+	u32 eec;
+	u32 mask;
+	u32 i;
+
+	eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+	/*
+	 * Mask is used to shift "count" bits of "data" out to the EEPROM
+	 * one bit at a time.  Determine the starting bit based on count
+	 */
+	mask = 0x01 << (count - 1);
+
+	for (i = 0; i < count; i++) {
+		/*
+		 * A "1" is shifted out to the EEPROM by setting bit "DI" to a
+		 * "1", and then raising and then lowering the clock (the SK
+		 * bit controls the clock input to the EEPROM).  A "0" is
+		 * shifted out to the EEPROM by setting "DI" to "0" and then
+		 * raising and then lowering the clock.
+		 */
+		if (data & mask)
+			eec |= IXGBE_EEC_DI;
+		else
+			eec &= ~IXGBE_EEC_DI;
+
+		IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+		IXGBE_WRITE_FLUSH(hw);
+
+		udelay(1);
+
+		ixgbe_raise_eeprom_clk(hw, &eec);
+		ixgbe_lower_eeprom_clk(hw, &eec);
+
+		/*
+		 * Shift mask to signify next bit of data to shift in to the
+		 * EEPROM
+		 */
+		mask = mask >> 1;
+	};
+
+	/* We leave the "DI" bit set to "0" when we leave this routine. */
+	eec &= ~IXGBE_EEC_DI;
+	IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+	IXGBE_WRITE_FLUSH(hw);
+}
+
+/**
+ *  ixgbe_shift_in_eeprom_bits - Shift data bits in from the EEPROM
+ *  @hw: pointer to hardware structure
+ **/
+static u16 ixgbe_shift_in_eeprom_bits(struct ixgbe_hw *hw, u16 count)
+{
+	u32 eec;
+	u32 i;
+	u16 data = 0;
+
+	/*
+	 * In order to read a register from the EEPROM, we need to shift
+	 * 'count' bits in from the EEPROM. Bits are "shifted in" by raising
+	 * the clock input to the EEPROM (setting the SK bit), and then reading
+	 * the value of the "DO" bit.  During this "shifting in" process the
+	 * "DI" bit should always be clear.
+	 */
+	eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+	eec &= ~(IXGBE_EEC_DO | IXGBE_EEC_DI);
+
+	for (i = 0; i < count; i++) {
+		data = data << 1;
+		ixgbe_raise_eeprom_clk(hw, &eec);
+
+		eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+		eec &= ~(IXGBE_EEC_DI);
+		if (eec & IXGBE_EEC_DO)
+			data |= 1;
+
+		ixgbe_lower_eeprom_clk(hw, &eec);
+	}
+
+	return data;
+}
+
+/**
+ *  ixgbe_raise_eeprom_clk - Raises the EEPROM's clock input.
+ *  @hw: pointer to hardware structure
+ *  @eec: EEC register's current value
+ **/
+static void ixgbe_raise_eeprom_clk(struct ixgbe_hw *hw, u32 *eec)
+{
+	/*
+	 * Raise the clock input to the EEPROM
+	 * (setting the SK bit), then delay
+	 */
+	*eec = *eec | IXGBE_EEC_SK;
+	IXGBE_WRITE_REG(hw, IXGBE_EEC, *eec);
+	IXGBE_WRITE_FLUSH(hw);
+	udelay(1);
+}
+
+/**
+ *  ixgbe_lower_eeprom_clk - Lowers the EEPROM's clock input.
+ *  @hw: pointer to hardware structure
+ *  @eecd: EECD's current value
+ **/
+static void ixgbe_lower_eeprom_clk(struct ixgbe_hw *hw, u32 *eec)
+{
+	/*
+	 * Lower the clock input to the EEPROM (clearing the SK bit), then
+	 * delay
+	 */
+	*eec = *eec & ~IXGBE_EEC_SK;
+	IXGBE_WRITE_REG(hw, IXGBE_EEC, *eec);
+	IXGBE_WRITE_FLUSH(hw);
+	udelay(1);
+}
+
+/**
+ *  ixgbe_release_eeprom - Release EEPROM, release semaphores
+ *  @hw: pointer to hardware structure
+ **/
+static void ixgbe_release_eeprom(struct ixgbe_hw *hw)
+{
+	u32 eec;
+
+	eec = IXGBE_READ_REG(hw, IXGBE_EEC);
+
+	eec |= IXGBE_EEC_CS;  /* Pull CS high */
+	eec &= ~IXGBE_EEC_SK; /* Lower SCK */
+
+	IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+	IXGBE_WRITE_FLUSH(hw);
+
+	udelay(1);
+
+	/* Stop requesting EEPROM access */
+	eec &= ~IXGBE_EEC_REQ;
+	IXGBE_WRITE_REG(hw, IXGBE_EEC, eec);
+
+	ixgbe_release_swfw_sync(hw, IXGBE_GSSR_EEP_SM);
+}
+
+/**
+ *  ixgbe_calc_eeprom_checksum - Calculates and returns the checksum
+ *  @hw: pointer to hardware structure
+ **/
+static u16 ixgbe_calc_eeprom_checksum(struct ixgbe_hw *hw)
+{
+	u16 i;
+	u16 j;
+	u16 checksum = 0;
+	u16 length = 0;
+	u16 pointer = 0;
+	u16 word = 0;
+
+	/* Include 0x0-0x3F in the checksum */
+	for (i = 0; i < IXGBE_EEPROM_CHECKSUM; i++) {
+		if (ixgbe_read_eeprom(hw, i, &word) != IXGBE_SUCCESS) {
+			DEBUGOUT("EEPROM read failed\n");
+			break;
+		}
+		checksum += word;
+	}
+
+	/* Include all data from pointers except for the fw pointer */
+	for (i = IXGBE_PCIE_ANALOG_PTR; i < IXGBE_FW_PTR; i++) {
+		ixgbe_read_eeprom(hw, i, &pointer);
+
+		/* Make sure the pointer seems valid */
+		if (pointer != 0xFFFF && pointer != 0) {
+			ixgbe_read_eeprom(hw, pointer, &length);
+
+			if (length != 0xFFFF && length != 0) {
+				for (j = pointer+1; j <= pointer+length; j++) {
+					ixgbe_read_eeprom(hw, j, &word);
+					checksum += word;
+				}
+			}
+		}
+	}
+
+	checksum = (u16)IXGBE_EEPROM_SUM - checksum;
+
+	return checksum;
+}
+
+/**
+ *  ixgbe_validate_eeprom_checksum_generic - Validate EEPROM checksum
+ *  @hw: pointer to hardware structure
+ *  @checksum_val: calculated checksum
+ *
+ *  Performs checksum calculation and validates the EEPROM checksum.  If the
+ *  caller does not need checksum_val, the value can be NULL.
+ **/
+static s32 ixgbe_validate_eeprom_checksum_generic(struct ixgbe_hw *hw,
+						  u16 *checksum_val)
+{
+	s32 status;
+	u16 checksum;
+	u16 read_checksum = 0;
+
+	/*
+	 * Read the first word from the EEPROM. If this times out or fails, do
+	 * not continue or we could be in for a very long wait while every
+	 * EEPROM read fails
+	 */
+	status = ixgbe_read_eeprom(hw, 0, &checksum);
+
+	if (status == IXGBE_SUCCESS) {
+		checksum = ixgbe_calc_eeprom_checksum(hw);
+
+		ixgbe_read_eeprom(hw, IXGBE_EEPROM_CHECKSUM, &read_checksum);
+
+		/*
+		 * Verify read checksum from EEPROM is the same as
+		 * calculated checksum
+		 */
+		if (read_checksum != checksum) {
+			status = IXGBE_ERR_EEPROM_CHECKSUM;
+		}
+
+		/* If the user cares, return the calculated checksum */
+		if (checksum_val) {
+			*checksum_val = checksum;
+		}
+	} else {
+		DEBUGOUT("EEPROM read failed\n");
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_update_eeprom_checksum_generic - Updates the EEPROM checksm
+ *  @hw: pointer to hardware structure
+ **/
+static s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw)
+{
+	s32 status;
+	u16 checksum;
+
+	/*
+	 * Read the first word from the EEPROM. If this times out or fails, do
+	 * not continue or we could be in for a very long wait while every
+	 * EEPROM read fails
+	 */
+	status = ixgbe_read_eeprom(hw, 0, &checksum);
+
+	if (status == IXGBE_SUCCESS) {
+		checksum = ixgbe_calc_eeprom_checksum(hw);
+		status = ixgbe_write_eeprom(hw, IXGBE_EEPROM_CHECKSUM,
+					    checksum);
+	} else {
+		DEBUGOUT("EEPROM read failed\n");
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_validate_mac_addr - Validate MAC address
+ *  @mac_addr: pointer to MAC address.
+ *
+ *  Tests a MAC address to ensure it is a valid Individual Address
+ **/
+s32 ixgbe_validate_mac_addr(u8 *mac_addr)
+{
+	s32 status = IXGBE_SUCCESS;
+
+	/* Make sure it is not a multicast address */
+	if (IXGBE_IS_MULTICAST(mac_addr)) {
+		DEBUGOUT("MAC address is multicast\n");
+		status = IXGBE_ERR_INVALID_MAC_ADDR;
+	/* Not a broadcast address */
+	} else if (IXGBE_IS_BROADCAST(mac_addr)) {
+		DEBUGOUT("MAC address is broadcast\n");
+		status = IXGBE_ERR_INVALID_MAC_ADDR;
+	/* Reject the zero address */
+	} else if (mac_addr[0] == 0 && mac_addr[1] == 0 && mac_addr[2] == 0 &&
+		 mac_addr[3] == 0 && mac_addr[4] == 0 && mac_addr[5] == 0) {
+		DEBUGOUT("MAC address is all zeros\n");
+		status = IXGBE_ERR_INVALID_MAC_ADDR;
+	}
+	return status;
+}
+
+/**
+ *  ixgbe_set_rar_generic - Set RX address register
+ *  @hw: pointer to hardware structure
+ *  @addr: Address to put into receive address register
+ *  @index: Receive address register to write
+ *  @vind: Vind to set RAR to
+ *  @enable_addr: set flag that address is active
+ *
+ *  Puts an ethernet address into a receive address register.
+ **/
+static s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr,
+				 u32 vind, u32 enable_addr)
+{
+	u32 rar_low, rar_high;
+
+	/*
+	 * HW expects these in little endian so we reverse the byte order from
+	 * network order (big endian) to little endian
+	 */
+	rar_low = ((u32)addr[0] |
+		   ((u32)addr[1] << 8) |
+		   ((u32)addr[2] << 16) |
+		   ((u32)addr[3] << 24));
+
+	rar_high = ((u32)addr[4] |
+		    ((u32)addr[5] << 8) |
+		    ((vind << IXGBE_RAH_VIND_SHIFT) & IXGBE_RAH_VIND_MASK));
+
+	if (enable_addr != 0)
+		rar_high |= IXGBE_RAH_AV;
+
+	IXGBE_WRITE_REG(hw, IXGBE_RAL(index), rar_low);
+	IXGBE_WRITE_REG(hw, IXGBE_RAH(index), rar_high);
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_enable_rar - Enable RX address register
+ *  @hw: pointer to hardware structure
+ *  @index: index into the RAR table
+ *
+ *  Enables the select receive address register.
+ **/
+static void ixgbe_enable_rar(struct ixgbe_hw *hw, u32 index)
+{
+	u32 rar_high;
+
+	rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(index));
+	rar_high |= IXGBE_RAH_AV;
+	IXGBE_WRITE_REG(hw, IXGBE_RAH(index), rar_high);
+}
+
+/**
+ *  ixgbe_disable_rar - Disable RX address register
+ *  @hw: pointer to hardware structure
+ *  @index: index into the RAR table
+ *
+ *  Disables the select receive address register.
+ **/
+static void ixgbe_disable_rar(struct ixgbe_hw *hw, u32 index)
+{
+	u32 rar_high;
+
+	rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(index));
+	rar_high &= (~IXGBE_RAH_AV);
+	IXGBE_WRITE_REG(hw, IXGBE_RAH(index), rar_high);
+}
+
+/**
+ *  ixgbe_init_rx_addrs_generic - Initializes receive address filters.
+ *  @hw: pointer to hardware structure
+ *
+ *  Places the MAC address in receive address register 0 and clears the rest
+ *  of the receive addresss registers. Clears the multicast table. Assumes
+ *  the receiver is in reset when the routine is called.
+ **/
+static s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw)
+{
+	u32 i;
+	u32 rar_entries = ixgbe_get_num_rx_addrs(hw);
+
+	/*
+	 * If the current mac address is valid, assume it is a software override
+	 * to the permanent address.
+	 * Otherwise, use the permanent address from the eeprom.
+	 */
+	if (ixgbe_validate_mac_addr(hw->mac.addr) ==
+	    IXGBE_ERR_INVALID_MAC_ADDR) {
+		/* Get the MAC address from the RAR0 for later reference */
+		ixgbe_get_mac_addr(hw, hw->mac.addr);
+
+		DEBUGOUT3(" Keeping Current RAR0 Addr =%.2X %.2X %.2X ",
+			  hw->mac.addr[0], hw->mac.addr[1],
+			  hw->mac.addr[2]);
+		DEBUGOUT3("%.2X %.2X %.2X\n", hw->mac.addr[3],
+			  hw->mac.addr[4], hw->mac.addr[5]);
+	} else {
+		/* Setup the receive address. */
+		DEBUGOUT("Overriding MAC Address in RAR[0]\n");
+		DEBUGOUT3(" New MAC Addr =%.2X %.2X %.2X ",
+			  hw->mac.addr[0], hw->mac.addr[1],
+			  hw->mac.addr[2]);
+		DEBUGOUT3("%.2X %.2X %.2X\n", hw->mac.addr[3],
+			  hw->mac.addr[4], hw->mac.addr[5]);
+
+		ixgbe_set_rar(hw, 0, hw->mac.addr, 0, IXGBE_RAH_AV);
+	}
+
+	hw->addr_ctrl.rar_used_count = 1;
+
+	/* Zero out the other receive addresses. */
+	DEBUGOUT("Clearing RAR[1-15]\n");
+	for (i = 1; i < rar_entries; i++) {
+		IXGBE_WRITE_REG(hw, IXGBE_RAL(i), 0);
+		IXGBE_WRITE_REG(hw, IXGBE_RAH(i), 0);
+	}
+
+	/* Clear the MTA */
+	hw->addr_ctrl.mc_addr_in_rar_count = 0;
+	hw->addr_ctrl.mta_in_use = 0;
+	IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, hw->mac.mc_filter_type);
+
+	DEBUGOUT(" Clearing MTA\n");
+	for (i = 0; i < IXGBE_MC_TBL_SIZE; i++)
+		IXGBE_WRITE_REG(hw, IXGBE_MTA(i), 0);
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_mta_vector - Determines bit-vector in multicast table to set
+ *  @hw: pointer to hardware structure
+ *  @mc_addr: the multicast address
+ *
+ *  Extracts the 12 bits, from a multicast address, to determine which
+ *  bit-vector to set in the multicast table. The hardware uses 12 bits, from
+ *  incoming rx multicast addresses, to determine the bit-vector to check in
+ *  the MTA. Which of the 4 combination, of 12-bits, the hardware uses is set
+ *  by the MO field of the MCSTCTRL. The MO field is set during initalization
+ *  to mc_filter_type.
+ **/
+static s32 ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr)
+{
+	u32 vector = 0;
+
+	switch (hw->mac.mc_filter_type) {
+	case 0:	  /* use bits [47:36] of the address */
+		vector = ((mc_addr[4] >> 4) | (((u16)mc_addr[5]) << 4));
+		break;
+	case 1:	  /* use bits [46:35] of the address */
+		vector = ((mc_addr[4] >> 3) | (((u16)mc_addr[5]) << 5));
+		break;
+	case 2:	  /* use bits [45:34] of the address */
+		vector = ((mc_addr[4] >> 2) | (((u16)mc_addr[5]) << 6));
+		break;
+	case 3:	  /* use bits [43:32] of the address */
+		vector = ((mc_addr[4]) | (((u16)mc_addr[5]) << 8));
+		break;
+	default:	 /* Invalid mc_filter_type */
+		DEBUGOUT("MC filter type param set incorrectly\n");
+		break;
+	}
+
+	/* vector can only be 12-bits or boundary will be exceeded */
+	vector &= 0xFFF;
+	return vector;
+}
+
+/**
+ *  ixgbe_set_mta - Set bit-vector in multicast table
+ *  @hw: pointer to hardware structure
+ *  @hash_value: Multicast address hash value
+ *
+ *  Sets the bit-vector in the multicast table.
+ **/
+static void ixgbe_set_mta(struct ixgbe_hw *hw, u8 *mc_addr)
+{
+	u32 vector;
+	u32 vector_bit;
+	u32 vector_reg;
+	u32 mta_reg;
+
+	hw->addr_ctrl.mta_in_use++;
+
+	vector = ixgbe_mta_vector(hw, mc_addr);
+	DEBUGOUT1(" bit-vector = 0x%03X\n", vector);
+
+	/*
+	 * The MTA is a register array of 128 32-bit registers. It is treated
+	 * like an array of 4096 bits.  We want to set bit
+	 * BitArray[vector_value]. So we figure out what register the bit is
+	 * in, read it, OR in the new bit, then write back the new value.  The
+	 * register is determined by the upper 7 bits of the vector value and
+	 * the bit within that register are determined by the lower 5 bits of
+	 * the value.
+	 */
+	vector_reg = (vector >> 5) & 0x7F;
+	vector_bit = vector & 0x1F;
+	mta_reg = IXGBE_READ_REG(hw, IXGBE_MTA(vector_reg));
+	mta_reg |= (1 << vector_bit);
+	IXGBE_WRITE_REG(hw, IXGBE_MTA(vector_reg), mta_reg);
+}
+
+/**
+ *  ixgbe_add_mc_addr - Adds a multicast address.
+ *  @hw: pointer to hardware structure
+ *  @mc_addr: new multicast address
+ *
+ *  Adds it to unused receive address register or to the multicast table.
+ **/
+static void ixgbe_add_mc_addr(struct ixgbe_hw *hw, u8 *mc_addr)
+{
+	u32 rar_entries = ixgbe_get_num_rx_addrs(hw);
+
+	DEBUGOUT6(" MC Addr =%.2X %.2X %.2X %.2X %.2X %.2X\n",
+		  mc_addr[0], mc_addr[1], mc_addr[2],
+		  mc_addr[3], mc_addr[4], mc_addr[5]);
+
+	/*
+	 * Place this multicast address in the RAR if there is room,
+	 * else put it in the MTA
+	 */
+	if (hw->addr_ctrl.rar_used_count < rar_entries) {
+		ixgbe_set_rar(hw, hw->addr_ctrl.rar_used_count,
+			      mc_addr, 0, IXGBE_RAH_AV);
+		DEBUGOUT1("Added a multicast address to RAR[%d]\n",
+			  hw->addr_ctrl.rar_used_count);
+		hw->addr_ctrl.rar_used_count++;
+		hw->addr_ctrl.mc_addr_in_rar_count++;
+	} else {
+		ixgbe_set_mta(hw, mc_addr);
+	}
+
+	DEBUGOUT("ixgbe_add_mc_addr Complete\n");
+}
+
+/**
+ *  ixgbe_update_mc_addr_list_generic - Updates MAC list of multicast addresses
+ *  @hw: pointer to hardware structure
+ *  @mc_addr_list: the list of new multicast addresses
+ *  @mc_addr_count: number of addresses
+ *  @pad: number of bytes between addresses in the list
+ *
+ *  The given list replaces any existing list. Clears the MC addrs from receive
+ *  address registers and the multicast table. Uses unsed receive address
+ *  registers for the first multicast addresses, and hashes the rest into the
+ *  multicast table.
+ **/
+static s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw,
+					     u8 *mc_addr_list,
+					     u32 mc_addr_count, u32 pad)
+{
+	u32 i;
+	u32 rar_entries = ixgbe_get_num_rx_addrs(hw);
+
+	/*
+	 * Set the new number of MC addresses that we are being requested to
+	 * use.
+	 */
+	hw->addr_ctrl.num_mc_addrs = mc_addr_count;
+	hw->addr_ctrl.rar_used_count -= hw->addr_ctrl.mc_addr_in_rar_count;
+	hw->addr_ctrl.mc_addr_in_rar_count = 0;
+	hw->addr_ctrl.mta_in_use = 0;
+
+	/* Zero out the other receive addresses. */
+	DEBUGOUT("Clearing RAR[1-15]\n");
+	for (i = hw->addr_ctrl.rar_used_count; i < rar_entries; i++) {
+		IXGBE_WRITE_REG(hw, IXGBE_RAL(i), 0);
+		IXGBE_WRITE_REG(hw, IXGBE_RAH(i), 0);
+	}
+
+	/* Clear the MTA */
+	DEBUGOUT(" Clearing MTA\n");
+	for (i = 0; i < IXGBE_MC_TBL_SIZE; i++)
+		IXGBE_WRITE_REG(hw, IXGBE_MTA(i), 0);
+
+	/* Add the new addresses */
+	for (i = 0; i < mc_addr_count; i++) {
+		DEBUGOUT(" Adding the multicast addresses:\n");
+		ixgbe_add_mc_addr(hw, mc_addr_list +
+				  (i * (IXGBE_ETH_LENGTH_OF_ADDRESS + pad)));
+	}
+
+	/* Enable mta */
+	if (hw->addr_ctrl.mta_in_use > 0)
+		IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL,
+				IXGBE_MCSTCTRL_MFE | hw->mac.mc_filter_type);
+
+	DEBUGOUT("ixgbe_update_mc_addr_list_generic Complete\n");
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_enable_mc_generic - Enable multicast address in RAR
+ *  @hw: pointer to hardware structure
+ *
+ *  Enables multicast address in RAR and the use of the multicast hash table.
+ **/
+static s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw)
+{
+	u32 i;
+	struct ixgbe_addr_filter_info *a = &hw->addr_ctrl;
+
+	if (a->mc_addr_in_rar_count > 0)
+		for (i = (a->rar_used_count - a->mc_addr_in_rar_count);
+		     i < a->rar_used_count; i++)
+			ixgbe_enable_rar(hw, i);
+
+	if (a->mta_in_use > 0)
+		IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, IXGBE_MCSTCTRL_MFE |
+				hw->mac.mc_filter_type);
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_disable_mc_generic - Disable mutlicast address in RAR
+ *  @hw: pointer to hardware structure
+ *
+ *  Disables multicast address in RAR and the use of the multicast hash table.
+ **/
+static s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw)
+{
+	u32 i;
+	struct ixgbe_addr_filter_info *a = &hw->addr_ctrl;
+
+	if (a->mc_addr_in_rar_count > 0)
+		for (i = (a->rar_used_count - a->mc_addr_in_rar_count);
+		     i < a->rar_used_count; i++)
+			ixgbe_disable_rar(hw, i);
+
+	if (a->mta_in_use > 0)
+		IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, hw->mac.mc_filter_type);
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_clear_vfta_generic - Clear VLAN filter table
+ *  @hw: pointer to hardware structure
+ *
+ *  Clears the VLAN filer table, and the VMDq index associated with the filter
+ **/
+static s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw)
+{
+	u32 offset;
+	u32 vlanbyte;
+
+	for (offset = 0; offset < IXGBE_VLAN_FILTER_TBL_SIZE; offset++)
+		IXGBE_WRITE_REG(hw, IXGBE_VFTA(offset), 0);
+
+	for (vlanbyte = 0; vlanbyte < 4; vlanbyte++)
+		for (offset = 0; offset < IXGBE_VLAN_FILTER_TBL_SIZE; offset++)
+			IXGBE_WRITE_REG(hw, IXGBE_VFTAVIND(vlanbyte, offset),
+					0);
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_set_vfta_generic - Set VLAN filter table
+ *  @hw: pointer to hardware structure
+ *  @vlan: VLAN id to write to VLAN filter
+ *  @vind: VMDq output index that maps queue to VLAN id in VFTA
+ *  @vlan_on: boolean flag to turn on/off VLAN in VFTA
+ *
+ *  Turn on/off specified VLAN in the VLAN filter table.
+ **/
+static s32 ixgbe_set_vfta_generic(struct ixgbe_hw *hw, u32 vlan, u32 vind,
+				  bool vlan_on)
+{
+	u32 VftaIndex;
+	u32 BitOffset;
+	u32 VftaReg;
+	u32 VftaByte;
+
+	/* Determine 32-bit word position in array */
+	VftaIndex = (vlan >> 5) & 0x7F;   /* upper seven bits */
+
+	/* Determine the location of the (VMD) queue index */
+	VftaByte =  ((vlan >> 3) & 0x03); /* bits (4:3) indicating byte array */
+	BitOffset = (vlan & 0x7) << 2;    /* lower 3 bits indicate nibble */
+
+	/* Set the nibble for VMD queue index */
+	VftaReg = IXGBE_READ_REG(hw, IXGBE_VFTAVIND(VftaByte, VftaIndex));
+	VftaReg &= (~(0x0F << BitOffset));
+	VftaReg |= (vind << BitOffset);
+	IXGBE_WRITE_REG(hw, IXGBE_VFTAVIND(VftaByte, VftaIndex), VftaReg);
+
+	/* Determine the location of the bit for this VLAN id */
+	BitOffset = vlan & 0x1F;	   /* lower five bits */
+
+	VftaReg = IXGBE_READ_REG(hw, IXGBE_VFTA(VftaIndex));
+	if (vlan_on)
+		/* Turn on this VLAN id */
+		VftaReg |= (1 << BitOffset);
+	else
+		/* Turn off this VLAN id */
+		VftaReg &= ~(1 << BitOffset);
+	IXGBE_WRITE_REG(hw, IXGBE_VFTA(VftaIndex), VftaReg);
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_setup_fc_generic - Configure flow control settings
+ *  @hw: pointer to hardware structure
+ *  @packetbuf_num: packet buffer number (0-7)
+ *
+ *  Configures the flow control settings based on SW configuration.
+ *  This function is used for 802.3x flow control configuration only.
+ **/
+static s32 ixgbe_setup_fc_generic(struct ixgbe_hw *hw, s32 packetbuf_num)
+{
+	u32 frctl_reg;
+	u32 rmcs_reg;
+
+	if (packetbuf_num < 0 || packetbuf_num > 7) {
+		DEBUGOUT1("Invalid packet buffer number [%d], expected range is"
+			  " 0-7\n", packetbuf_num);
+	}
+
+	frctl_reg = IXGBE_READ_REG(hw, IXGBE_FCTRL);
+	frctl_reg &= ~(IXGBE_FCTRL_RFCE | IXGBE_FCTRL_RPFCE);
+
+	rmcs_reg = IXGBE_READ_REG(hw, IXGBE_RMCS);
+	rmcs_reg &= ~(IXGBE_RMCS_TFCE_PRIORITY | IXGBE_RMCS_TFCE_802_3X);
+
+	/*
+	 * We want to save off the original Flow Control configuration just in
+	 * case we get disconnected and then reconnected into a different hub
+	 * or switch with different Flow Control capabilities.
+	 */
+	hw->fc.type = hw->fc.original_type;
+
+	/*
+	 * The possible values of the "flow_control" parameter are:
+	 * 0: Flow control is completely disabled
+	 * 1: Rx flow control is enabled (we can receive pause frames but not
+	 *    send pause frames).
+	 * 2: Tx flow control is enabled (we can send pause frames but we do not
+	 *    support receiving pause frames)
+	 * 3: Both Rx and TX flow control (symmetric) are enabled.
+	 * other: Invalid.
+	 */
+	switch (hw->fc.type) {
+	case ixgbe_fc_none:
+		break;
+	case ixgbe_fc_rx_pause:
+		/*
+		 * RX Flow control is enabled,
+		 * and TX Flow control is disabled.
+		 */
+		frctl_reg |= IXGBE_FCTRL_RFCE;
+		break;
+	case ixgbe_fc_tx_pause:
+		/*
+		 * TX Flow control is enabled, and RX Flow control is disabled,
+		 * by a software over-ride.
+		 */
+		rmcs_reg |= IXGBE_RMCS_TFCE_802_3X;
+		break;
+	case ixgbe_fc_full:
+		/*
+		 * Flow control (both RX and TX) is enabled by a software
+		 * over-ride.
+		 */
+		frctl_reg |= IXGBE_FCTRL_RFCE;
+		rmcs_reg |= IXGBE_RMCS_TFCE_802_3X;
+		break;
+	default:
+		/* We should never get here.  The value should be 0-3. */
+		DEBUGOUT("Flow control param set incorrectly\n");
+		break;
+	}
+
+	/* Enable 802.3x based flow control settings. */
+	IXGBE_WRITE_REG(hw, IXGBE_FCTRL, frctl_reg);
+	IXGBE_WRITE_REG(hw, IXGBE_RMCS, rmcs_reg);
+
+	/*
+	 * We need to set up the Receive Threshold high and low water
+	 * marks as well as (optionally) enabling the transmission of
+	 * XON frames.
+	 */
+	if (hw->fc.type & ixgbe_fc_tx_pause) {
+		if (hw->fc.send_xon) {
+			IXGBE_WRITE_REG(hw, IXGBE_FCRTL(packetbuf_num),
+					(hw->fc.low_water | IXGBE_FCRTL_XONE));
+		} else {
+			IXGBE_WRITE_REG(hw, IXGBE_FCRTL(packetbuf_num),
+					hw->fc.low_water);
+		}
+		IXGBE_WRITE_REG(hw, IXGBE_FCRTH(packetbuf_num),
+				(hw->fc.high_water)|IXGBE_FCRTH_FCEN);
+	}
+
+	IXGBE_WRITE_REG(hw, IXGBE_FCTTV(0), hw->fc.pause_time);
+	IXGBE_WRITE_REG(hw, IXGBE_FCRTV, (hw->fc.pause_time >> 1));
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_disable_pcie_master - Disable PCI-express master access
+ *  @hw: pointer to hardware structure
+ *
+ *  Disables PCI-Express master access and verifies there are no pending
+ *  requests. IXGBE_ERR_MASTER_REQUESTS_PENDING is returned if master disable
+ *  bit hasn't caused the master requests to be disabled, else IXGBE_SUCCESS
+ *  is returned signifying master requests disabled.
+ **/
+s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw)
+{
+	u32 ctrl;
+	s32 i;
+	s32 status = IXGBE_ERR_MASTER_REQUESTS_PENDING;
+
+	ctrl = IXGBE_READ_REG(hw, IXGBE_CTRL);
+	ctrl |= IXGBE_CTRL_GIO_DIS;
+	IXGBE_WRITE_REG(hw, IXGBE_CTRL, ctrl);
+
+	for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) {
+		if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO)) {
+			status = IXGBE_SUCCESS;
+			break;
+		}
+		udelay(100);
+	}
+
+	return status;
+}
+
+
+/**
+ *  ixgbe_acquire_swfw_sync - Aquire SWFW semaphore
+ *  @hw: pointer to hardware structure
+ *  @mask: Mask to specify wich semaphore to acquire
+ *
+ *  Aquires the SWFW semaphore throught the GSSR register for the specified
+ *  function (CSR, PHY0, PHY1, EEPROM, Flash)
+ **/
+s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask)
+{
+	u32 gssr;
+	u32 swmask = mask;
+	u32 fwmask = mask << 5;
+	s32 timeout = 200;
+
+	while (timeout) {
+		if (ixgbe_get_eeprom_semaphore(hw))
+			return -IXGBE_ERR_SWFW_SYNC;
+
+		gssr = IXGBE_READ_REG(hw, IXGBE_GSSR);
+		if (!(gssr & (fwmask | swmask)))
+			break;
+
+		/*
+		 * Firmware currently using resource (fwmask) or other software
+		 * thread currently using resource (swmask)
+		 */
+		ixgbe_release_eeprom_semaphore(hw);
+		msleep(5);
+		timeout--;
+	}
+
+	if (!timeout) {
+		DEBUGOUT("Driver can't access resource, GSSR timeout.\n");
+		return -IXGBE_ERR_SWFW_SYNC;
+	}
+
+	gssr |= swmask;
+	IXGBE_WRITE_REG(hw, IXGBE_GSSR, gssr);
+
+	ixgbe_release_eeprom_semaphore(hw);
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_release_swfw_sync - Release SWFW semaphore
+ *  @hw: pointer to hardware structure
+ *  @mask: Mask to specify wich semaphore to release
+ *
+ *  Releases the SWFW semaphore throught the GSSR register for the specified
+ *  function (CSR, PHY0, PHY1, EEPROM, Flash)
+ **/
+void ixgbe_release_swfw_sync(struct ixgbe_hw *hw, u16 mask)
+{
+	u32 gssr;
+	u32 swmask = mask;
+
+	ixgbe_get_eeprom_semaphore(hw);
+
+	gssr = IXGBE_READ_REG(hw, IXGBE_GSSR);
+	gssr &= ~swmask;
+	IXGBE_WRITE_REG(hw, IXGBE_GSSR, gssr);
+
+	ixgbe_release_eeprom_semaphore(hw);
+}
+
diff --git a/drivers/net/ixgbe/ixgbe_common.h b/drivers/net/ixgbe/ixgbe_common.h
new file mode 100644
index 0000000..d3de048
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_common.h
@@ -0,0 +1,43 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _IXGBE_COMMON_H_
+#define _IXGBE_COMMON_H_
+
+#include "ixgbe_type.h"
+
+s32 ixgbe_assign_func_pointers_generic(struct ixgbe_hw *hw);
+
+s32 ixgbe_blink_led_start_generic(struct ixgbe_hw *hw, u32 index);
+s32 ixgbe_blink_led_stop_generic(struct ixgbe_hw *hw, u32 index);
+
+s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask);
+void ixgbe_release_swfw_sync(struct ixgbe_hw *hw, u16 mask);
+s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw);
+
+#endif /* IXGBE_COMMON */
diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c
new file mode 100644
index 0000000..3f632f5
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_ethtool.c
@@ -0,0 +1,931 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+/* ethtool support for ixgbe */
+
+#include "ixgbe.h"
+
+#include <linux/uaccess.h>
+
+#define IXGBE_ALL_RAR_ENTRIES 16
+
+struct ixgbe_stats {
+	char stat_string[ETH_GSTRING_LEN];
+	int sizeof_stat;
+	int stat_offset;
+};
+
+#define IXGBE_STAT(m) sizeof(((struct ixgbe_adapter *)0)->m), \
+		      offsetof(struct ixgbe_adapter, m)
+static struct ixgbe_stats ixgbe_gstrings_stats[] = {
+	{"rx_packets", IXGBE_STAT(net_stats.rx_packets)},
+	{"tx_packets", IXGBE_STAT(net_stats.tx_packets)},
+	{"rx_bytes", IXGBE_STAT(net_stats.rx_bytes)},
+	{"tx_bytes", IXGBE_STAT(net_stats.tx_bytes)},
+	{"lsc_int", IXGBE_STAT(lsc_int)},
+	{"tx_busy", IXGBE_STAT(tx_busy)},
+	{"non_eop_descs", IXGBE_STAT(non_eop_descs)},
+	{"rx_errors", IXGBE_STAT(net_stats.rx_errors)},
+	{"tx_errors", IXGBE_STAT(net_stats.tx_errors)},
+	{"rx_dropped", IXGBE_STAT(net_stats.rx_dropped)},
+	{"tx_dropped", IXGBE_STAT(net_stats.tx_dropped)},
+	{"multicast", IXGBE_STAT(net_stats.multicast)},
+	{"broadcast", IXGBE_STAT(stats.bprc)},
+	{"rx_no_buffer_count", IXGBE_STAT(stats.rnbc[0]) },
+	{"collisions", IXGBE_STAT(net_stats.collisions)},
+	{"rx_over_errors", IXGBE_STAT(net_stats.rx_over_errors)},
+	{"rx_crc_errors", IXGBE_STAT(net_stats.rx_crc_errors)},
+	{"rx_frame_errors", IXGBE_STAT(net_stats.rx_frame_errors)},
+	{"rx_fifo_errors", IXGBE_STAT(net_stats.rx_fifo_errors)},
+	{"rx_missed_errors", IXGBE_STAT(net_stats.rx_missed_errors)},
+	{"tx_aborted_errors", IXGBE_STAT(net_stats.tx_aborted_errors)},
+	{"tx_carrier_errors", IXGBE_STAT(net_stats.tx_carrier_errors)},
+	{"tx_fifo_errors", IXGBE_STAT(net_stats.tx_fifo_errors)},
+	{"tx_heartbeat_errors", IXGBE_STAT(net_stats.tx_heartbeat_errors)},
+	{"tx_timeout_count", IXGBE_STAT(tx_timeout_count)},
+	{"tx_restart_queue", IXGBE_STAT(restart_queue)},
+	{"rx_long_length_errors", IXGBE_STAT(stats.roc)},
+	{"rx_short_length_errors", IXGBE_STAT(stats.ruc)},
+	{"tx_tcp4_seg_ctxt", IXGBE_STAT(hw_tso_ctxt)},
+	{"tx_tcp6_seg_ctxt", IXGBE_STAT(hw_tso6_ctxt)},
+	{"tx_flow_control_xon", IXGBE_STAT(stats.lxontxc)},
+	{"rx_flow_control_xon", IXGBE_STAT(stats.lxonrxc)},
+	{"tx_flow_control_xoff", IXGBE_STAT(stats.lxofftxc)},
+	{"rx_flow_control_xoff", IXGBE_STAT(stats.lxoffrxc)},
+	{"rx_csum_offload_good", IXGBE_STAT(hw_csum_rx_good)},
+	{"rx_csum_offload_errors", IXGBE_STAT(hw_csum_rx_error)},
+	{"tx_csum_offload_ctxt", IXGBE_STAT(hw_csum_tx_good)},
+	{"rx_header_split", IXGBE_STAT(rx_hdr_split)},
+	{"alloc_rx_page_failed", IXGBE_STAT(alloc_rx_page_failed)},
+	{"alloc_rx_buff_failed", IXGBE_STAT(alloc_rx_buff_failed)},
+};
+
+#define IXGBE_QUEUE_STATS_LEN ( \
+		(((struct ixgbe_adapter *)netdev->priv)->num_tx_queues + \
+		((struct ixgbe_adapter *)netdev->priv)->num_rx_queues) * \
+		(sizeof(struct ixgbe_queue_stats) / sizeof(u64)) \
+	)
+#define IXGBE_GLOBAL_STATS_LEN	\
+	sizeof(ixgbe_gstrings_stats) / sizeof(struct ixgbe_stats)
+#define IXGBE_STATS_LEN (IXGBE_GLOBAL_STATS_LEN + IXGBE_QUEUE_STATS_LEN)
+
+static int ixgbe_get_settings(struct net_device *netdev,
+			      struct ethtool_cmd *ecmd)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	ecmd->supported = (SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE);
+	ecmd->advertising = (ADVERTISED_10000baseT_Full | ADVERTISED_FIBRE);
+	ecmd->port = PORT_FIBRE;
+	ecmd->transceiver = XCVR_EXTERNAL;
+
+	if (netif_carrier_ok(adapter->netdev)) {
+		ecmd->speed = SPEED_10000;
+		ecmd->duplex = DUPLEX_FULL;
+	} else {
+		ecmd->speed = -1;
+		ecmd->duplex = -1;
+	}
+
+	ecmd->autoneg = AUTONEG_DISABLE;
+	return 0;
+}
+
+static int ixgbe_set_settings(struct net_device *netdev,
+			      struct ethtool_cmd *ecmd)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	if (ecmd->autoneg == AUTONEG_ENABLE ||
+	    ecmd->speed + ecmd->duplex != SPEED_10000 + DUPLEX_FULL)
+		return -EINVAL;
+
+	if (netif_running(adapter->netdev)) {
+		ixgbe_down(adapter);
+		ixgbe_reset(adapter);
+		ixgbe_up(adapter);
+	} else {
+		ixgbe_reset(adapter);
+	}
+
+	return 0;
+}
+
+static void ixgbe_get_pauseparam(struct net_device *netdev,
+				 struct ethtool_pauseparam *pause)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct ixgbe_hw *hw = &adapter->hw;
+
+	pause->autoneg = AUTONEG_DISABLE;
+
+	if (hw->fc.type == ixgbe_fc_rx_pause) {
+		pause->rx_pause = 1;
+	} else if (hw->fc.type == ixgbe_fc_tx_pause) {
+		pause->tx_pause = 1;
+	} else if (hw->fc.type == ixgbe_fc_full) {
+		pause->rx_pause = 1;
+		pause->tx_pause = 1;
+	}
+}
+
+static int ixgbe_set_pauseparam(struct net_device *netdev,
+				struct ethtool_pauseparam *pause)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct ixgbe_hw *hw = &adapter->hw;
+
+	if (pause->autoneg == AUTONEG_ENABLE)
+		return -EINVAL;
+
+	if (pause->rx_pause && pause->tx_pause)
+		hw->fc.type = ixgbe_fc_full;
+	else if (pause->rx_pause && !pause->tx_pause)
+		hw->fc.type = ixgbe_fc_rx_pause;
+	else if (!pause->rx_pause && pause->tx_pause)
+		hw->fc.type = ixgbe_fc_tx_pause;
+	else if (!pause->rx_pause && !pause->tx_pause)
+		hw->fc.type = ixgbe_fc_none;
+
+	hw->fc.original_type = hw->fc.type;
+
+	if (netif_running(adapter->netdev)) {
+		ixgbe_down(adapter);
+		ixgbe_up(adapter);
+	} else {
+		ixgbe_reset(adapter);
+	}
+
+	return 0;
+}
+
+static u32 ixgbe_get_rx_csum(struct net_device *netdev)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	return (adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED);
+}
+
+static int ixgbe_set_rx_csum(struct net_device *netdev, u32 data)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	if (data)
+		adapter->flags |= IXGBE_FLAG_RX_CSUM_ENABLED;
+	else
+		adapter->flags &= ~IXGBE_FLAG_RX_CSUM_ENABLED;
+
+	if (netif_running(netdev)) {
+		ixgbe_down(adapter);
+		ixgbe_up(adapter);
+	} else {
+		ixgbe_reset(adapter);
+	}
+
+	return 0;
+}
+
+static u32 ixgbe_get_tx_csum(struct net_device *netdev)
+{
+	return (netdev->features & NETIF_F_HW_CSUM) != 0;
+}
+
+static int ixgbe_set_tx_csum(struct net_device *netdev, u32 data)
+{
+	if (data)
+		netdev->features |= NETIF_F_HW_CSUM;
+	else
+		netdev->features &= ~NETIF_F_HW_CSUM;
+
+	return 0;
+}
+
+static int ixgbe_set_tso(struct net_device *netdev, u32 data)
+{
+
+	if (data) {
+		netdev->features |= NETIF_F_TSO;
+		netdev->features |= NETIF_F_TSO6;
+	} else {
+		netdev->features &= ~NETIF_F_TSO;
+		netdev->features &= ~NETIF_F_TSO6;
+	}
+	return 0;
+}
+
+static int ixgbe_get_coalesce(struct net_device *netdev,
+			      struct ethtool_coalesce *ecmd)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	if (adapter->rx_eitr == 0)
+		ecmd->rx_coalesce_usecs = 0;
+	else
+		ecmd->rx_coalesce_usecs = 1000000 / adapter->rx_eitr;
+	
+	if (adapter->tx_eitr == 0)
+		ecmd->tx_coalesce_usecs = 0;
+	else
+		ecmd->tx_coalesce_usecs = 1000000 / adapter->tx_eitr;
+
+	return 0;
+}
+
+static int ixgbe_set_coalesce(struct net_device *netdev,
+			      struct ethtool_coalesce *ecmd)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	if ((ecmd->rx_coalesce_usecs > IXGBE_ITR_MAX_USECS) ||
+	    ((ecmd->rx_coalesce_usecs > 0) &&
+	     (ecmd->rx_coalesce_usecs < IXGBE_ITR_MIN_USECS)))
+		return -EINVAL;
+	if ((ecmd->tx_coalesce_usecs > IXGBE_ITR_MAX_USECS) ||
+	    ((ecmd->tx_coalesce_usecs > 0) &&
+	     (ecmd->tx_coalesce_usecs < IXGBE_ITR_MIN_USECS)))
+		return -EINVAL;
+	
+	/* convert to rate of irq's per second */
+	if (ecmd->rx_coalesce_usecs == 0)
+		adapter->rx_eitr = 0;
+	else
+		adapter->rx_eitr = (1000000 / ecmd->rx_coalesce_usecs);
+	
+	if (ecmd->tx_coalesce_usecs == 0)
+		adapter->tx_eitr = 0;
+	else
+		adapter->tx_eitr = (1000000 / ecmd->tx_coalesce_usecs);
+
+	if (netif_running(netdev)) {
+		ixgbe_down(adapter);
+		ixgbe_up(adapter);
+	}
+
+	return 0;
+}
+
+static u32 ixgbe_get_msglevel(struct net_device *netdev)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	return adapter->msg_enable;
+}
+
+static void ixgbe_set_msglevel(struct net_device *netdev, u32 data)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	adapter->msg_enable = data;
+}
+
+static int ixgbe_get_regs_len(struct net_device *netdev)
+{
+#define IXGBE_REGS_LEN  1127*sizeof(u32)
+	return IXGBE_REGS_LEN;
+}
+
+#define IXGBE_GET_STAT(_A_, _R_) _A_->stats._R_
+
+static void ixgbe_get_regs(struct net_device *netdev,
+			   struct ethtool_regs *regs, void *p)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct ixgbe_hw *hw = &adapter->hw;
+	u32 *regs_buff = p;
+	u8 i;
+
+	memset(p, 0, IXGBE_REGS_LEN * sizeof(u32));
+
+	regs->version = (1 << 24) | hw->revision_id << 16 | hw->device_id;
+
+	/* General Registers */
+	regs_buff[0] = IXGBE_READ_REG(hw, IXGBE_CTRL);
+	regs_buff[1] = IXGBE_READ_REG(hw, IXGBE_STATUS);
+	regs_buff[2] = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT);
+	regs_buff[3] = IXGBE_READ_REG(hw, IXGBE_ESDP);
+	regs_buff[4] = IXGBE_READ_REG(hw, IXGBE_EODSDP);
+	regs_buff[5] = IXGBE_READ_REG(hw, IXGBE_LEDCTL);
+	regs_buff[6] = IXGBE_READ_REG(hw, IXGBE_FRTIMER);
+	regs_buff[7] = IXGBE_READ_REG(hw, IXGBE_TCPTIMER);
+
+	/* NVM Register */
+	regs_buff[8] = IXGBE_READ_REG(hw, IXGBE_EEC);
+	regs_buff[9] = IXGBE_READ_REG(hw, IXGBE_EERD);
+	regs_buff[10] = IXGBE_READ_REG(hw, IXGBE_FLA);
+	regs_buff[11] = IXGBE_READ_REG(hw, IXGBE_EEMNGCTL);
+	regs_buff[12] = IXGBE_READ_REG(hw, IXGBE_EEMNGDATA);
+	regs_buff[13] = IXGBE_READ_REG(hw, IXGBE_FLMNGCTL);
+	regs_buff[14] = IXGBE_READ_REG(hw, IXGBE_FLMNGDATA);
+	regs_buff[15] = IXGBE_READ_REG(hw, IXGBE_FLMNGCNT);
+	regs_buff[16] = IXGBE_READ_REG(hw, IXGBE_FLOP);
+	regs_buff[17] = IXGBE_READ_REG(hw, IXGBE_GRC);
+
+	/* Interrupt */
+	regs_buff[18] = IXGBE_READ_REG(hw, IXGBE_EICR);
+	regs_buff[19] = IXGBE_READ_REG(hw, IXGBE_EICS);
+	regs_buff[20] = IXGBE_READ_REG(hw, IXGBE_EIMS);
+	regs_buff[21] = IXGBE_READ_REG(hw, IXGBE_EIMC);
+	regs_buff[22] = IXGBE_READ_REG(hw, IXGBE_EIAC);
+	regs_buff[23] = IXGBE_READ_REG(hw, IXGBE_EIAM);
+	regs_buff[24] = IXGBE_READ_REG(hw, IXGBE_EITR(0));
+	regs_buff[25] = IXGBE_READ_REG(hw, IXGBE_IVAR(0));
+	regs_buff[26] = IXGBE_READ_REG(hw, IXGBE_MSIXT);
+	regs_buff[27] = IXGBE_READ_REG(hw, IXGBE_MSIXPBA);
+	regs_buff[28] = IXGBE_READ_REG(hw, IXGBE_PBACL);
+	regs_buff[29] = IXGBE_READ_REG(hw, IXGBE_GPIE);
+
+	/* Flow Control */
+	regs_buff[30] = IXGBE_READ_REG(hw, IXGBE_PFCTOP);
+	regs_buff[31] = IXGBE_READ_REG(hw, IXGBE_FCTTV(0));
+	regs_buff[32] = IXGBE_READ_REG(hw, IXGBE_FCTTV(1));
+	regs_buff[33] = IXGBE_READ_REG(hw, IXGBE_FCTTV(2));
+	regs_buff[34] = IXGBE_READ_REG(hw, IXGBE_FCTTV(3));
+	for (i = 0; i < 8; i++)
+		regs_buff[35 + i] = IXGBE_READ_REG(hw, IXGBE_FCRTL(i));
+	for (i = 0; i < 8; i++)
+		regs_buff[43 + i] = IXGBE_READ_REG(hw, IXGBE_FCRTH(i));
+	regs_buff[51] = IXGBE_READ_REG(hw, IXGBE_FCRTV);
+	regs_buff[52] = IXGBE_READ_REG(hw, IXGBE_TFCS);
+
+	/* Receive DMA */
+	for (i = 0; i < 64; i++)
+		regs_buff[53 + i] = IXGBE_READ_REG(hw, IXGBE_RDBAL(i));
+	for (i = 0; i < 64; i++)
+		regs_buff[117 + i] = IXGBE_READ_REG(hw, IXGBE_RDBAH(i));
+	for (i = 0; i < 64; i++)
+		regs_buff[181 + i] = IXGBE_READ_REG(hw, IXGBE_RDLEN(i));
+	for (i = 0; i < 64; i++)
+		regs_buff[245 + i] = IXGBE_READ_REG(hw, IXGBE_RDH(i));
+	for (i = 0; i < 64; i++)
+		regs_buff[309 + i] = IXGBE_READ_REG(hw, IXGBE_RDT(i));
+	for (i = 0; i < 64; i++)
+		regs_buff[373 + i] = IXGBE_READ_REG(hw, IXGBE_RXDCTL(i));
+	for (i = 0; i < 16; i++)
+		regs_buff[437 + i] = IXGBE_READ_REG(hw, IXGBE_SRRCTL(i));
+	for (i = 0; i < 16; i++)
+		regs_buff[453 + i] = IXGBE_READ_REG(hw, IXGBE_DCA_RXCTRL(i));
+	regs_buff[469] = IXGBE_READ_REG(hw, IXGBE_RDRXCTL);
+	for (i = 0; i < 8; i++ )
+		regs_buff[470 + i] = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(i));
+	regs_buff[478] = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
+	regs_buff[479] = IXGBE_READ_REG(hw, IXGBE_DROPEN);
+
+	/* Receive */
+	regs_buff[480] = IXGBE_READ_REG(hw, IXGBE_RXCSUM);
+	regs_buff[481] = IXGBE_READ_REG(hw, IXGBE_RFCTL);
+	for (i = 0; i < 16; i++)
+		regs_buff[482 + i] = IXGBE_READ_REG(hw, IXGBE_RAL(i));
+	for (i = 0; i < 16; i++)
+		regs_buff[498 + i] = IXGBE_READ_REG(hw, IXGBE_RAH(i));
+	regs_buff[514] = IXGBE_READ_REG(hw, IXGBE_PSRTYPE);
+	regs_buff[515] = IXGBE_READ_REG(hw, IXGBE_FCTRL);
+	regs_buff[516] = IXGBE_READ_REG(hw, IXGBE_VLNCTRL);
+	regs_buff[517] = IXGBE_READ_REG(hw, IXGBE_MCSTCTRL);
+	regs_buff[518] = IXGBE_READ_REG(hw, IXGBE_MRQC);
+	regs_buff[519] = IXGBE_READ_REG(hw, IXGBE_VMD_CTL);
+	for (i = 0; i < 8; i++)
+		regs_buff[520 + i] = IXGBE_READ_REG(hw, IXGBE_IMIR(i));
+	for (i = 0; i < 8; i++)
+		regs_buff[528 + i] = IXGBE_READ_REG(hw, IXGBE_IMIREXT(i));
+	regs_buff[536] = IXGBE_READ_REG(hw, IXGBE_IMIRVP);
+
+	/* Transmit */
+	for (i = 0; i < 32; i++)
+		regs_buff[537 + i] = IXGBE_READ_REG(hw, IXGBE_TDBAL(i));
+	for (i = 0; i < 32; i++)
+		regs_buff[569 + i] = IXGBE_READ_REG(hw, IXGBE_TDBAH(i));
+	for (i = 0; i < 32; i++)
+		regs_buff[601 + i] = IXGBE_READ_REG(hw, IXGBE_TDLEN(i));
+	for (i = 0; i < 32; i++)
+		regs_buff[633 + i] = IXGBE_READ_REG(hw, IXGBE_TDH(i));
+	for (i = 0; i < 32; i++)
+		regs_buff[665 + i] = IXGBE_READ_REG(hw, IXGBE_TDT(i));
+	for (i = 0; i < 32; i++)
+		regs_buff[697 + i] = IXGBE_READ_REG(hw, IXGBE_TXDCTL(i));
+	for (i = 0; i < 32; i++)
+		regs_buff[729 + i] = IXGBE_READ_REG(hw, IXGBE_TDWBAL(i));
+	for (i = 0; i < 32; i++)
+		regs_buff[761 + i] = IXGBE_READ_REG(hw, IXGBE_TDWBAH(i));
+	regs_buff[793] = IXGBE_READ_REG(hw, IXGBE_DTXCTL);
+	for (i = 0; i < 16; i++)
+		regs_buff[794 + i] = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL(i));
+	regs_buff[810] = IXGBE_READ_REG(hw, IXGBE_TIPG);
+	for (i = 0; i < 8; i++)
+		regs_buff[811 + i] = IXGBE_READ_REG(hw, IXGBE_TXPBSIZE(i));
+	regs_buff[819] = IXGBE_READ_REG(hw, IXGBE_MNGTXMAP);
+
+	/* Wake Up */
+	regs_buff[820] = IXGBE_READ_REG(hw, IXGBE_WUC);
+	regs_buff[821] = IXGBE_READ_REG(hw, IXGBE_WUFC);
+	regs_buff[822] = IXGBE_READ_REG(hw, IXGBE_WUS);
+	regs_buff[823] = IXGBE_READ_REG(hw, IXGBE_IPAV);
+	regs_buff[824] = IXGBE_READ_REG(hw, IXGBE_IP4AT);
+	regs_buff[825] = IXGBE_READ_REG(hw, IXGBE_IP6AT);
+	regs_buff[826] = IXGBE_READ_REG(hw, IXGBE_WUPL);
+	regs_buff[827] = IXGBE_READ_REG(hw, IXGBE_WUPM);
+	regs_buff[828] = IXGBE_READ_REG(hw, IXGBE_FHFT);
+
+	/* DCE */
+	regs_buff[829] = IXGBE_READ_REG(hw, IXGBE_RMCS);
+	regs_buff[830] = IXGBE_READ_REG(hw, IXGBE_DPMCS);
+	regs_buff[831] = IXGBE_READ_REG(hw, IXGBE_PDPMCS);
+	regs_buff[832] = IXGBE_READ_REG(hw, IXGBE_RUPPBMR);
+	for (i = 0; i < 8; i++)
+		regs_buff[833 + i] = IXGBE_READ_REG(hw, IXGBE_RT2CR(i));
+	for (i = 0; i < 8; i++)
+		regs_buff[841 + i] = IXGBE_READ_REG(hw, IXGBE_RT2SR(i));
+	for (i = 0; i < 8; i++)
+		regs_buff[849 + i] = IXGBE_READ_REG(hw, IXGBE_TDTQ2TCCR(i));
+	for (i = 0; i < 8; i++)
+		regs_buff[857 + i] = IXGBE_READ_REG(hw, IXGBE_TDTQ2TCSR(i));
+	for (i = 0; i < 8; i++)
+		regs_buff[865 + i] = IXGBE_READ_REG(hw, IXGBE_TDPT2TCCR(i));
+	for (i = 0; i < 8; i++)
+		regs_buff[873 + i] = IXGBE_READ_REG(hw, IXGBE_TDPT2TCSR(i));
+
+	/* Statistics */
+	regs_buff[881] = IXGBE_GET_STAT(adapter, crcerrs);
+	regs_buff[882] = IXGBE_GET_STAT(adapter, illerrc);
+	regs_buff[883] = IXGBE_GET_STAT(adapter, errbc);
+	regs_buff[884] = IXGBE_GET_STAT(adapter, mspdc);
+	for (i = 0; i < 8; i++)
+		regs_buff[885 + i] = IXGBE_GET_STAT(adapter, mpc[i]);
+	regs_buff[893] = IXGBE_GET_STAT(adapter, mlfc);
+	regs_buff[894] = IXGBE_GET_STAT(adapter, mrfc);
+	regs_buff[895] = IXGBE_GET_STAT(adapter, rlec);
+	regs_buff[896] = IXGBE_GET_STAT(adapter, lxontxc);
+	regs_buff[897] = IXGBE_GET_STAT(adapter, lxonrxc);
+	regs_buff[898] = IXGBE_GET_STAT(adapter, lxofftxc);
+	regs_buff[899] = IXGBE_GET_STAT(adapter, lxoffrxc);
+	for (i = 0; i < 8; i++)
+		regs_buff[900 + i] = IXGBE_GET_STAT(adapter, pxontxc[i]);
+	for (i = 0; i < 8; i++)
+		regs_buff[908 + i] = IXGBE_GET_STAT(adapter, pxonrxc[i]);
+	for (i = 0; i < 8; i++)
+		regs_buff[916 + i] = IXGBE_GET_STAT(adapter, pxofftxc[i]);
+	for (i = 0; i < 8; i++)
+		regs_buff[924 + i] = IXGBE_GET_STAT(adapter, pxoffrxc[i]);
+	regs_buff[932] = IXGBE_GET_STAT(adapter, prc64);
+	regs_buff[933] = IXGBE_GET_STAT(adapter, prc127);
+	regs_buff[934] = IXGBE_GET_STAT(adapter, prc255);
+	regs_buff[935] = IXGBE_GET_STAT(adapter, prc511);
+	regs_buff[936] = IXGBE_GET_STAT(adapter, prc1023);
+	regs_buff[937] = IXGBE_GET_STAT(adapter, prc1522);
+	regs_buff[938] = IXGBE_GET_STAT(adapter, gprc);
+	regs_buff[939] = IXGBE_GET_STAT(adapter, bprc);
+	regs_buff[940] = IXGBE_GET_STAT(adapter, mprc);
+	regs_buff[941] = IXGBE_GET_STAT(adapter, gptc);
+	regs_buff[942] = IXGBE_GET_STAT(adapter, gorc);
+	regs_buff[944] = IXGBE_GET_STAT(adapter, gotc);
+	for (i = 0; i < 8; i++)
+		regs_buff[946 + i] = IXGBE_GET_STAT(adapter, rnbc[i]);
+	regs_buff[954] = IXGBE_GET_STAT(adapter, ruc);
+	regs_buff[955] = IXGBE_GET_STAT(adapter, rfc);
+	regs_buff[956] = IXGBE_GET_STAT(adapter, roc);
+	regs_buff[957] = IXGBE_GET_STAT(adapter, rjc);
+	regs_buff[958] = IXGBE_GET_STAT(adapter, mngprc);
+	regs_buff[959] = IXGBE_GET_STAT(adapter, mngpdc);
+	regs_buff[960] = IXGBE_GET_STAT(adapter, mngptc);
+	regs_buff[961] = IXGBE_GET_STAT(adapter, tor);
+	regs_buff[963] = IXGBE_GET_STAT(adapter, tpr);
+	regs_buff[964] = IXGBE_GET_STAT(adapter, tpt);
+	regs_buff[965] = IXGBE_GET_STAT(adapter, ptc64);
+	regs_buff[966] = IXGBE_GET_STAT(adapter, ptc127);
+	regs_buff[967] = IXGBE_GET_STAT(adapter, ptc255);
+	regs_buff[968] = IXGBE_GET_STAT(adapter, ptc511);
+	regs_buff[969] = IXGBE_GET_STAT(adapter, ptc1023);
+	regs_buff[970] = IXGBE_GET_STAT(adapter, ptc1522);
+	regs_buff[971] = IXGBE_GET_STAT(adapter, mptc);
+	regs_buff[972] = IXGBE_GET_STAT(adapter, bptc);
+	regs_buff[973] = IXGBE_GET_STAT(adapter, xec);
+	for (i = 0; i < 16; i++)
+		regs_buff[974 + i] = IXGBE_GET_STAT(adapter, qprc[i]);
+	for (i = 0; i < 16; i++)
+		regs_buff[990 + i] = IXGBE_GET_STAT(adapter, qptc[i]);
+	for (i = 0; i < 16; i++)
+		regs_buff[1006 + i] = IXGBE_GET_STAT(adapter, qbrc[i]);
+	for (i = 0; i < 16; i++)
+		regs_buff[1022 + i] = IXGBE_GET_STAT(adapter, qbtc[i]);
+
+	/* MAC */
+	regs_buff[1038] = IXGBE_READ_REG(hw, IXGBE_PCS1GCFIG);
+	regs_buff[1039] = IXGBE_READ_REG(hw, IXGBE_PCS1GLCTL);
+	regs_buff[1040] = IXGBE_READ_REG(hw, IXGBE_PCS1GLSTA);
+	regs_buff[1041] = IXGBE_READ_REG(hw, IXGBE_PCS1GDBG0);
+	regs_buff[1042] = IXGBE_READ_REG(hw, IXGBE_PCS1GDBG1);
+	regs_buff[1043] = IXGBE_READ_REG(hw, IXGBE_PCS1GANA);
+	regs_buff[1044] = IXGBE_READ_REG(hw, IXGBE_PCS1GANLP);
+	regs_buff[1045] = IXGBE_READ_REG(hw, IXGBE_PCS1GANNP);
+	regs_buff[1046] = IXGBE_READ_REG(hw, IXGBE_PCS1GANLPNP);
+	regs_buff[1047] = IXGBE_READ_REG(hw, IXGBE_HLREG0);
+	regs_buff[1048] = IXGBE_READ_REG(hw, IXGBE_HLREG1);
+	regs_buff[1049] = IXGBE_READ_REG(hw, IXGBE_PAP);
+	regs_buff[1050] = IXGBE_READ_REG(hw, IXGBE_MACA);
+	regs_buff[1051] = IXGBE_READ_REG(hw, IXGBE_APAE);
+	regs_buff[1052] = IXGBE_READ_REG(hw, IXGBE_ARD);
+	regs_buff[1053] = IXGBE_READ_REG(hw, IXGBE_AIS);
+	regs_buff[1054] = IXGBE_READ_REG(hw, IXGBE_MSCA);
+	regs_buff[1055] = IXGBE_READ_REG(hw, IXGBE_MSRWD);
+	regs_buff[1056] = IXGBE_READ_REG(hw, IXGBE_MLADD);
+	regs_buff[1057] = IXGBE_READ_REG(hw, IXGBE_MHADD);
+	regs_buff[1058] = IXGBE_READ_REG(hw, IXGBE_TREG);
+	regs_buff[1059] = IXGBE_READ_REG(hw, IXGBE_PCSS1);
+	regs_buff[1060] = IXGBE_READ_REG(hw, IXGBE_PCSS2);
+	regs_buff[1061] = IXGBE_READ_REG(hw, IXGBE_XPCSS);
+	regs_buff[1062] = IXGBE_READ_REG(hw, IXGBE_SERDESC);
+	regs_buff[1063] = IXGBE_READ_REG(hw, IXGBE_MACS);
+	regs_buff[1064] = IXGBE_READ_REG(hw, IXGBE_AUTOC);
+	regs_buff[1065] = IXGBE_READ_REG(hw, IXGBE_LINKS);
+	regs_buff[1066] = IXGBE_READ_REG(hw, IXGBE_AUTOC2);
+	regs_buff[1067] = IXGBE_READ_REG(hw, IXGBE_AUTOC3);
+	regs_buff[1068] = IXGBE_READ_REG(hw, IXGBE_ANLP1);
+	regs_buff[1069] = IXGBE_READ_REG(hw, IXGBE_ANLP2);
+	regs_buff[1070] = IXGBE_READ_REG(hw, IXGBE_ATLASCTL);
+
+	/* Diagnostic */
+	regs_buff[1071] = IXGBE_READ_REG(hw, IXGBE_RDSTATCTL);
+	for (i = 0; i < 8; i++)
+		regs_buff[1072] = IXGBE_READ_REG(hw, IXGBE_RDSTAT(i));
+	regs_buff[1080] = IXGBE_READ_REG(hw, IXGBE_RDHMPN);
+	regs_buff[1081] = IXGBE_READ_REG(hw, IXGBE_RIC_DW0);
+	regs_buff[1082] = IXGBE_READ_REG(hw, IXGBE_RIC_DW1);
+	regs_buff[1083] = IXGBE_READ_REG(hw, IXGBE_RIC_DW2);
+	regs_buff[1084] = IXGBE_READ_REG(hw, IXGBE_RIC_DW3);
+	regs_buff[1085] = IXGBE_READ_REG(hw, IXGBE_RDPROBE);
+	regs_buff[1086] = IXGBE_READ_REG(hw, IXGBE_TDSTATCTL);
+	for (i = 0; i < 8; i++)
+		regs_buff[1087] = IXGBE_READ_REG(hw, IXGBE_TDSTAT(i));
+	regs_buff[1095] = IXGBE_READ_REG(hw, IXGBE_TDHMPN);
+	regs_buff[1096] = IXGBE_READ_REG(hw, IXGBE_TIC_DW0);
+	regs_buff[1097] = IXGBE_READ_REG(hw, IXGBE_TIC_DW1);
+	regs_buff[1098] = IXGBE_READ_REG(hw, IXGBE_TIC_DW2);
+	regs_buff[1099] = IXGBE_READ_REG(hw, IXGBE_TIC_DW3);
+	regs_buff[1100] = IXGBE_READ_REG(hw, IXGBE_TDPROBE);
+	regs_buff[1101] = IXGBE_READ_REG(hw, IXGBE_TXBUFCTRL);
+	regs_buff[1102] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA0);
+	regs_buff[1103] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA1);
+	regs_buff[1104] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA2);
+	regs_buff[1105] = IXGBE_READ_REG(hw, IXGBE_TXBUFDATA3);
+	regs_buff[1106] = IXGBE_READ_REG(hw, IXGBE_RXBUFCTRL);
+	regs_buff[1107] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA0);
+	regs_buff[1108] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA1);
+	regs_buff[1109] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA2);
+	regs_buff[1110] = IXGBE_READ_REG(hw, IXGBE_RXBUFDATA3);
+	for (i = 0; i < 8; i++)
+		regs_buff[1111] = IXGBE_READ_REG(hw, IXGBE_PCIE_DIAG(i));
+	regs_buff[1119] = IXGBE_READ_REG(hw, IXGBE_RFVAL);
+	regs_buff[1120] = IXGBE_READ_REG(hw, IXGBE_MDFTC1);
+	regs_buff[1121] = IXGBE_READ_REG(hw, IXGBE_MDFTC2);
+	regs_buff[1122] = IXGBE_READ_REG(hw, IXGBE_MDFTFIFO1);
+	regs_buff[1123] = IXGBE_READ_REG(hw, IXGBE_MDFTFIFO2);
+	regs_buff[1124] = IXGBE_READ_REG(hw, IXGBE_MDFTS);
+	regs_buff[1125] = IXGBE_READ_REG(hw, IXGBE_PCIEECCCTL);
+	regs_buff[1126] = IXGBE_READ_REG(hw, IXGBE_PBTXECC);
+	regs_buff[1127] = IXGBE_READ_REG(hw, IXGBE_PBRXECC);
+}
+
+static int ixgbe_get_eeprom_len(struct net_device *netdev)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	return adapter->hw.eeprom.word_size * 2;
+}
+
+static int ixgbe_get_eeprom(struct net_device *netdev,
+			    struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct ixgbe_hw *hw = &adapter->hw;
+	u16 *eeprom_buff;
+	int first_word, last_word, eeprom_len;
+	int ret_val = 0;
+	u16 i;
+
+	if (eeprom->len == 0)
+		return -EINVAL;
+
+	eeprom->magic = hw->vendor_id | (hw->device_id << 16);
+
+	first_word = eeprom->offset >> 1;
+	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+	eeprom_len = last_word - first_word + 1;
+
+	eeprom_buff = kmalloc(sizeof(u16) * eeprom_len, GFP_KERNEL);
+	if (!eeprom_buff)
+		return -ENOMEM;
+
+	for (i = 0; i < eeprom_len; i++) {
+		if ((ret_val = ixgbe_read_eeprom(hw, first_word + i,
+						 &eeprom_buff[i])))
+			break;
+	}
+
+	/* Device's eeprom is always little-endian, word addressable */
+	for (i = 0; i < eeprom_len; i++)
+		le16_to_cpus(&eeprom_buff[i]);
+
+	memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+	kfree(eeprom_buff);
+
+	return ret_val;
+}
+
+static void ixgbe_get_drvinfo(struct net_device *netdev,
+			      struct ethtool_drvinfo *drvinfo)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	strncpy(drvinfo->driver, ixgbe_driver_name, 32);
+	strncpy(drvinfo->version, ixgbe_driver_version, 32);
+	strncpy(drvinfo->fw_version, "N/A", 32);
+	strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32);
+	drvinfo->n_stats = IXGBE_STATS_LEN;
+	drvinfo->regdump_len = ixgbe_get_regs_len(netdev);
+}
+
+static void ixgbe_get_ringparam(struct net_device *netdev,
+				struct ethtool_ringparam *ring)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct ixgbe_ring *tx_ring = adapter->tx_ring;
+	struct ixgbe_ring *rx_ring = adapter->rx_ring;
+
+	ring->rx_max_pending = IXGBE_MAX_RXD;
+	ring->tx_max_pending = IXGBE_MAX_TXD;
+	ring->rx_mini_max_pending = 0;
+	ring->rx_jumbo_max_pending = 0;
+	ring->rx_pending = rx_ring->count;
+	ring->tx_pending = tx_ring->count;
+	ring->rx_mini_pending = 0;
+	ring->rx_jumbo_pending = 0;
+}
+
+static int ixgbe_set_ringparam(struct net_device *netdev,
+			       struct ethtool_ringparam *ring)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct ixgbe_tx_buffer *old_buf;
+	struct ixgbe_rx_buffer *old_rx_buf;
+	void *old_desc;
+	int i, err;
+	u32 new_rx_count, new_tx_count, old_size;
+	dma_addr_t old_dma;
+
+	if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
+		return -EINVAL;
+
+	new_rx_count = max(ring->rx_pending, (u32)IXGBE_MIN_RXD);
+	new_rx_count = min(new_rx_count, (u32)IXGBE_MAX_RXD);
+	new_rx_count = ALIGN(new_rx_count, IXGBE_REQ_RX_DESCRIPTOR_MULTIPLE);
+
+	new_tx_count = max(ring->tx_pending, (u32)IXGBE_MIN_TXD);
+	new_tx_count = min(new_tx_count, (u32)IXGBE_MAX_TXD);
+	new_tx_count = ALIGN(new_tx_count, IXGBE_REQ_TX_DESCRIPTOR_MULTIPLE);
+
+	if ((new_tx_count == adapter->tx_ring->count) &&
+	    (new_rx_count == adapter->rx_ring->count)) {
+		/* nothing to do */
+		return 0;
+	}
+
+	if (netif_running(adapter->netdev))
+		ixgbe_down(adapter);
+
+	/*
+	 * We can't just free everything and then setup again,
+	 * because the ISRs in MSI-X mode get passed pointers
+	 * to the tx and rx ring structs.
+	 */
+	if (new_tx_count != adapter->tx_ring->count) {
+		for (i = 0; i < adapter->num_tx_queues; i++) {
+			/* Save existing descriptor ring */
+			old_buf = adapter->tx_ring[i].tx_buffer_info;
+			old_desc = adapter->tx_ring[i].desc;
+			old_size = adapter->tx_ring[i].size;
+			old_dma = adapter->tx_ring[i].dma;
+			/* Try to allocate a new one */
+			adapter->tx_ring[i].tx_buffer_info = NULL;
+			adapter->tx_ring[i].desc = NULL;
+			adapter->tx_ring[i].count = new_tx_count;
+			err = ixgbe_setup_tx_resources(adapter,
+						       &adapter->tx_ring[i]);
+			if (err) {
+				/* Restore the old one so at least
+				   the adapter still works, even if
+				   we failed the request */
+				adapter->tx_ring[i].tx_buffer_info = old_buf;
+				adapter->tx_ring[i].desc = old_desc;
+				adapter->tx_ring[i].size = old_size;
+				adapter->tx_ring[i].dma = old_dma;
+				goto err_setup;
+			}
+			/* Free the old buffer manually */
+			vfree(old_buf);
+			pci_free_consistent(adapter->pdev, old_size,
+					    old_desc, old_dma);
+		}
+	}
+
+	if (new_rx_count != adapter->rx_ring->count) {
+		for (i = 0; i < adapter->num_rx_queues; i++) {
+
+			old_rx_buf = adapter->rx_ring[i].rx_buffer_info;
+			old_desc = adapter->rx_ring[i].desc;
+			old_size = adapter->rx_ring[i].size;
+			old_dma = adapter->rx_ring[i].dma;
+
+			adapter->rx_ring[i].rx_buffer_info = NULL;
+			adapter->rx_ring[i].desc = NULL;
+			adapter->rx_ring[i].dma = 0;
+			adapter->rx_ring[i].count = new_rx_count;
+			err = ixgbe_setup_rx_resources(adapter,
+						       &adapter->rx_ring[i]);
+			if (err) {
+				adapter->rx_ring[i].rx_buffer_info = old_rx_buf;
+				adapter->rx_ring[i].desc = old_desc;
+				adapter->rx_ring[i].size = old_size;
+				adapter->rx_ring[i].dma = old_dma;
+				goto err_setup;
+			}
+
+			vfree(old_rx_buf);
+			pci_free_consistent(adapter->pdev, old_size, old_desc,
+					    old_dma);
+		}
+	}
+
+	err = 0;
+err_setup:
+	if (netif_running(adapter->netdev))
+		ixgbe_up(adapter);
+
+	return err;
+}
+
+static int ixgbe_get_stats_count(struct net_device *netdev)
+{
+	return IXGBE_STATS_LEN;
+}
+
+static void ixgbe_get_ethtool_stats(struct net_device *netdev,
+				    struct ethtool_stats *stats, u64 *data)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	u64 *queue_stat;
+	int stat_count = sizeof(struct ixgbe_queue_stats) / sizeof(u64);
+	int j, k;
+	int i;
+
+	ixgbe_update_stats(adapter);
+	for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) {
+		char *p = (char *)adapter + ixgbe_gstrings_stats[i].stat_offset;
+		data[i] = (ixgbe_gstrings_stats[i].sizeof_stat ==
+			   sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+	}
+	for (j = 0; j < adapter->num_tx_queues; j++) {
+		queue_stat = (u64 *)&adapter->tx_ring[j].stats;
+		for (k = 0; k < stat_count; k++)
+			data[i + k] = queue_stat[k];
+		i += k;
+	}
+	for (j = 0; j < adapter->num_rx_queues; j++) {
+		queue_stat = (u64 *)&adapter->rx_ring[j].stats;
+		for (k = 0; k < stat_count; k++)
+			data[i + k] = queue_stat[k];
+		i += k;
+	}
+}
+
+static void ixgbe_get_strings(struct net_device *netdev, u32 stringset,
+			      u8 *data)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	u8 *p = data;
+	int i;
+
+	switch (stringset) {
+	case ETH_SS_STATS:
+		for (i = 0; i < IXGBE_GLOBAL_STATS_LEN; i++) {
+			memcpy(p, ixgbe_gstrings_stats[i].stat_string,
+			       ETH_GSTRING_LEN);
+			p += ETH_GSTRING_LEN;
+		}
+		for (i = 0; i < adapter->num_tx_queues; i++) {
+			sprintf(p, "tx_queue_%u_packets", i);
+			p += ETH_GSTRING_LEN;
+			sprintf(p, "tx_queue_%u_bytes", i);
+			p += ETH_GSTRING_LEN;
+		}
+		for (i = 0; i < adapter->num_rx_queues; i++) {
+			sprintf(p, "rx_queue_%u_packets", i);
+			p += ETH_GSTRING_LEN;
+			sprintf(p, "rx_queue_%u_bytes", i);
+			p += ETH_GSTRING_LEN;
+		}
+/*		BUG_ON(p - data != IXGBE_STATS_LEN * ETH_GSTRING_LEN); */
+		break;
+	}
+}
+
+
+static void ixgbe_get_wol(struct net_device *netdev,
+			  struct ethtool_wolinfo *wol)
+{
+	wol->supported = 0;
+	wol->wolopts = 0;
+
+	return;
+}
+
+static int ixgbe_nway_reset(struct net_device *netdev)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	if (netif_running(netdev)) {
+		ixgbe_down(adapter);
+		ixgbe_reset(adapter);
+		ixgbe_up(adapter);
+	}
+
+	return 0;
+}
+
+static int ixgbe_phys_id(struct net_device *netdev, u32 data)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	u32 led_reg = IXGBE_READ_REG(&adapter->hw, IXGBE_LEDCTL);
+	u32 i;
+
+	if (!data || data > 300)
+		data = 300;
+
+	for (i = 0; i < (data * 1000); i += 400) {
+		ixgbe_led_on(&adapter->hw, IXGBE_LED_ON);
+		msleep_interruptible(200);
+		ixgbe_led_off(&adapter->hw, IXGBE_LED_ON);
+		msleep_interruptible(200);
+	}
+
+	/* Restore LED settings */
+	IXGBE_WRITE_REG(&adapter->hw, IXGBE_LEDCTL, led_reg);
+
+	return IXGBE_SUCCESS;
+}
+
+static struct ethtool_ops ixgbe_ethtool_ops = {
+	.get_settings           = ixgbe_get_settings,
+	.set_settings           = ixgbe_set_settings,
+	.get_drvinfo            = ixgbe_get_drvinfo,
+	.get_regs_len           = ixgbe_get_regs_len,
+	.get_regs               = ixgbe_get_regs,
+	.get_wol                = ixgbe_get_wol,
+	.nway_reset             = ixgbe_nway_reset,
+	.get_link               = ethtool_op_get_link,
+	.get_eeprom_len         = ixgbe_get_eeprom_len,
+	.get_eeprom             = ixgbe_get_eeprom,
+	.get_ringparam          = ixgbe_get_ringparam,
+	.set_ringparam          = ixgbe_set_ringparam,
+	.get_pauseparam         = ixgbe_get_pauseparam,
+	.set_pauseparam         = ixgbe_set_pauseparam,
+	.get_rx_csum            = ixgbe_get_rx_csum,
+	.set_rx_csum            = ixgbe_set_rx_csum,
+	.get_tx_csum            = ixgbe_get_tx_csum,
+	.set_tx_csum            = ixgbe_set_tx_csum,
+	.get_sg                 = ethtool_op_get_sg,
+	.set_sg                 = ethtool_op_set_sg,
+	.get_msglevel           = ixgbe_get_msglevel,
+	.set_msglevel           = ixgbe_set_msglevel,
+	.get_tso                = ethtool_op_get_tso,
+	.set_tso                = ixgbe_set_tso,
+	.get_coalesce           = ixgbe_get_coalesce,
+	.set_coalesce           = ixgbe_set_coalesce,
+	.get_strings            = ixgbe_get_strings,
+	.phys_id                = ixgbe_phys_id,
+	.get_stats_count        = ixgbe_get_stats_count,
+	.get_ethtool_stats      = ixgbe_get_ethtool_stats,
+};
+
+void ixgbe_set_ethtool_ops(struct net_device *netdev)
+{
+	SET_ETHTOOL_OPS(netdev, &ixgbe_ethtool_ops);
+}
diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c
new file mode 100644
index 0000000..7d5d0be
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_main.c
@@ -0,0 +1,2765 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/ipv6.h>
+#include <net/checksum.h>
+#include <net/ip6_checksum.h>
+
+#include "ixgbe.h"
+
+char ixgbe_driver_name[] = "ixgbe";
+static char ixgbe_driver_string[] =
+			"Intel(R) PRO/10GbE PCI Express Network Driver";
+
+#define DRIVERNAPI "-NAPI"
+#define DRV_VERSION "1.1.9"DRIVERNAPI
+char ixgbe_driver_version[] = DRV_VERSION;
+static char ixgbe_copyright[] = "Copyright (c) 1999-2007 Intel Corporation.";
+
+/* ixgbe_pci_tbl - PCI Device ID Table
+ *
+ * Wildcard entries (PCI_ANY_ID) should come last
+ * Last entry must be all 0s
+ *
+ * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
+ *   Class, Class Mask, private data (not used) }
+ */
+static struct pci_device_id ixgbe_pci_tbl[] = {
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, IXGBE_DEV_ID_82598)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, IXGBE_DEV_ID_82598AF_DUAL_PORT)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, IXGBE_DEV_ID_82598AF_SINGLE_PORT)},
+
+	/* required last entry */
+	{0, }
+};
+MODULE_DEVICE_TABLE(pci, ixgbe_pci_tbl);
+
+MODULE_AUTHOR("Intel Corporation, <linux.nics@intel.com>");
+MODULE_DESCRIPTION("Intel(R) PRO/10GbE PCI Express Network Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
+
+#define DEFAULT_DEBUG_LEVEL_SHIFT 3
+
+static void ixgbe_set_ivar(struct ixgbe_adapter *adapter, u16 int_alloc_entry,
+			   u8 msix_vector)
+{
+	u32 ivar, index;
+
+	msix_vector |= IXGBE_IVAR_ALLOC_VAL;
+	index = (int_alloc_entry >> 2) & 0x1F;
+	ivar = IXGBE_READ_REG_ARRAY(&adapter->hw, IXGBE_IVAR(0), index);
+	ivar |= (msix_vector << (8 * (int_alloc_entry & 0x3)));
+	IXGBE_WRITE_REG_ARRAY(&adapter->hw, IXGBE_IVAR(0), index, ivar);
+}
+
+static void ixgbe_unmap_and_free_tx_resource(struct ixgbe_adapter *adapter,
+					     struct ixgbe_tx_buffer
+					     *tx_buffer_info)
+{
+	if (tx_buffer_info->dma) {
+		pci_unmap_page(adapter->pdev,
+			       tx_buffer_info->dma,
+			       tx_buffer_info->length, PCI_DMA_TODEVICE);
+		tx_buffer_info->dma = 0;
+	}
+	if (tx_buffer_info->skb) {
+		dev_kfree_skb_any(tx_buffer_info->skb);
+		tx_buffer_info->skb = NULL;
+	}
+	/* tx_buffer_info must be completely set up in the transmit path */
+}
+
+static inline bool ixgbe_check_tx_hang(struct ixgbe_adapter *adapter,
+					    struct ixgbe_ring *tx_ring,
+					    unsigned int eop,
+					    union ixgbe_adv_tx_desc *eop_desc)
+{
+	/* Detect a transmit hang in hardware, this serializes the
+	 * check with the clearing of time_stamp and movement of i */
+	adapter->detect_tx_hung = false;
+	if (tx_ring->tx_buffer_info[eop].dma &&
+		     time_after(jiffies,
+				tx_ring->tx_buffer_info[eop].time_stamp
+				+ HZ)
+		     && !(IXGBE_READ_REG(&adapter->hw, IXGBE_TFCS) &
+			  IXGBE_TFCS_TXOFF)) {
+
+		/* detected Tx unit hang */
+		DPRINTK(DRV, ERR, "Detected Tx Unit Hang\n"
+			"  TDH                  <%x>\n"
+			"  TDT                  <%x>\n"
+			"  next_to_use          <%x>\n"
+			"  next_to_clean        <%x>\n"
+			"tx_buffer_info[next_to_clean]\n"
+			"  time_stamp           <%lx>\n"
+			"  next_to_watch        <%x>\n"
+			"  jiffies              <%lx>\n"
+			"  next_to_watch.status <%x>\n",
+			readl(adapter->hw.hw_addr + tx_ring->head),
+			readl(adapter->hw.hw_addr + tx_ring->tail),
+			tx_ring->next_to_use,
+			tx_ring->next_to_clean,
+			tx_ring->tx_buffer_info[eop].time_stamp,
+			eop, jiffies, eop_desc->wb.status);
+		return true;
+	}
+
+	return false;
+}
+
+/**
+ * ixgbe_clean_tx_irq - Reclaim resources after transmit completes
+ * @adapter: board private structure
+ **/
+static bool ixgbe_clean_tx_irq(struct ixgbe_adapter *adapter,
+				    struct ixgbe_ring *tx_ring)
+{
+	struct net_device *netdev = adapter->netdev;
+	union ixgbe_adv_tx_desc *tx_desc, *eop_desc;
+	struct ixgbe_tx_buffer *tx_buffer_info;
+	unsigned int i, eop;
+	bool cleaned = false;
+
+	i = tx_ring->next_to_clean;
+	eop = tx_ring->tx_buffer_info[i].next_to_watch;
+	eop_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop);
+	while (eop_desc->wb.status & cpu_to_le32(IXGBE_TXD_STAT_DD)) {
+		for (cleaned = false; !cleaned; ) {
+			tx_desc = IXGBE_TX_DESC_ADV(*tx_ring, i);
+			tx_buffer_info = &tx_ring->tx_buffer_info[i];
+			cleaned = (i == eop);
+
+			tx_ring->stats.bytes += tx_buffer_info->length;
+			ixgbe_unmap_and_free_tx_resource(adapter,
+							 tx_buffer_info);
+			tx_desc->wb.status = 0;
+
+			i++;
+			if (i == tx_ring->count)
+				i = 0;
+		}
+
+		tx_ring->stats.packets++;
+
+		eop = tx_ring->tx_buffer_info[i].next_to_watch;
+		eop_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop);
+	}
+
+	tx_ring->next_to_clean = i;
+
+#define TX_WAKE_THRESHOLD 32
+	spin_lock(&tx_ring->tx_lock);
+
+	if (cleaned && netif_carrier_ok(netdev) &&
+		     IXGBE_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD)
+		netif_wake_queue(netdev);
+
+	spin_unlock(&tx_ring->tx_lock);
+
+	if (adapter->detect_tx_hung)
+		if (ixgbe_check_tx_hang(adapter, tx_ring, eop, eop_desc))
+			netif_stop_queue(netdev);
+
+	return cleaned;
+}
+
+/**
+ * ixgbe_receive_skb - Send a completed packet up the stack
+ * @adapter: board private structure
+ * @skb: packet to send up
+ * @is_vlan: packet has a VLAN tag
+ * @tag: VLAN tag from descriptor
+ **/
+static void ixgbe_receive_skb(struct ixgbe_adapter *adapter,
+			      struct sk_buff *skb, bool is_vlan,
+			      u16 tag)
+{
+	if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) {
+		if (adapter->vlgrp && is_vlan)
+			vlan_hwaccel_receive_skb(skb, adapter->vlgrp, tag);
+		else
+			netif_receive_skb(skb);
+	} else {
+
+		if (adapter->vlgrp && is_vlan)
+			vlan_hwaccel_rx(skb, adapter->vlgrp, tag);
+		else
+			netif_rx(skb);
+	}
+}
+
+static inline void ixgbe_rx_checksum(struct ixgbe_adapter *adapter,
+					 u32 status_err,
+					 struct sk_buff *skb)
+{
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* Ignore Checksum bit is set */
+	if ((status_err & IXGBE_RXD_STAT_IXSM) ||
+		     !(adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED))
+		return;
+	/* TCP/UDP checksum error bit is set */
+	if (status_err & (IXGBE_RXDADV_ERR_TCPE | IXGBE_RXDADV_ERR_IPE)) {
+		/* let the stack verify checksum errors */
+		adapter->hw_csum_rx_error++;
+		return;
+	}
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if (status_err & (IXGBE_RXD_STAT_L4CS | IXGBE_RXD_STAT_UDPCS))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	adapter->hw_csum_rx_good++;
+}
+
+/**
+ * ixgbe_alloc_rx_buffers - Replace used receive buffers; packet split
+ * @adapter: address of board private structure
+ **/
+static void ixgbe_alloc_rx_buffers(struct ixgbe_adapter *adapter,
+				       struct ixgbe_ring *rx_ring,
+				       int cleaned_count)
+{
+	struct net_device *netdev = adapter->netdev;
+	struct pci_dev *pdev = adapter->pdev;
+	union ixgbe_adv_rx_desc *rx_desc;
+	struct ixgbe_rx_buffer *rx_buffer_info;
+	struct sk_buff *skb;
+	unsigned int i;
+	unsigned int bufsz = adapter->rx_buf_len + NET_IP_ALIGN;
+
+	i = rx_ring->next_to_use;
+	rx_buffer_info = &rx_ring->rx_buffer_info[i];
+
+	while (cleaned_count--) {
+		rx_desc = IXGBE_RX_DESC_ADV(*rx_ring, i);
+
+		if (!rx_buffer_info->page &&
+				(adapter->flags & IXGBE_FLAG_RX_PS_ENABLED)) {
+			rx_buffer_info->page = alloc_page(GFP_ATOMIC);
+			if (!rx_buffer_info->page) {
+				adapter->alloc_rx_page_failed++;
+				goto no_buffers;
+			}
+			rx_buffer_info->page_dma =
+			    pci_map_page(pdev, rx_buffer_info->page,
+					 0, PAGE_SIZE, PCI_DMA_FROMDEVICE);
+		}
+
+		if (!rx_buffer_info->skb) {
+			skb = netdev_alloc_skb(netdev, bufsz);
+
+			if (!skb) {
+				adapter->alloc_rx_buff_failed++;
+				goto no_buffers;
+			}
+
+			/*
+			 * Make buffer alignment 2 beyond a 16 byte boundary
+			 * this will result in a 16 byte aligned IP header after
+			 * the 14 byte MAC header is removed
+			 */
+			skb_reserve(skb, NET_IP_ALIGN);
+
+			rx_buffer_info->skb = skb;
+			rx_buffer_info->dma = pci_map_single(pdev, skb->data,
+							  bufsz,
+							  PCI_DMA_FROMDEVICE);
+		}
+		/* Refresh the desc even if buffer_addrs didn't change because
+		 * each write-back erases this info. */
+		if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+			rx_desc->read.pkt_addr =
+			    cpu_to_le64(rx_buffer_info->page_dma);
+			rx_desc->read.hdr_addr =
+					cpu_to_le64(rx_buffer_info->dma);
+		} else {
+			rx_desc->read.pkt_addr =
+					cpu_to_le64(rx_buffer_info->dma);
+		}
+
+		i++;
+		if (i == rx_ring->count)
+			i = 0;
+		rx_buffer_info = &rx_ring->rx_buffer_info[i];
+	}
+no_buffers:
+	if (rx_ring->next_to_use != i) {
+		rx_ring->next_to_use = i;
+		if (i-- == 0)
+			i = (rx_ring->count - 1);
+
+		/*
+		 * Force memory writes to complete before letting h/w
+		 * know there are new descriptors to fetch.  (Only
+		 * applicable for weak-ordered memory model archs,
+		 * such as IA-64).
+		 */
+		wmb();
+		writel(i, adapter->hw.hw_addr + rx_ring->tail);
+	}
+}
+
+static bool ixgbe_clean_rx_irq(struct ixgbe_adapter *adapter,
+					struct ixgbe_ring *rx_ring,
+					int *work_done, int work_to_do)
+{
+	struct net_device *netdev = adapter->netdev;
+	struct pci_dev *pdev = adapter->pdev;
+	union ixgbe_adv_rx_desc *rx_desc, *next_rxd;
+	struct ixgbe_rx_buffer *rx_buffer_info, *next_buffer;
+	struct sk_buff *skb;
+	unsigned int i;
+	u32 upper_len, len, staterr;
+	u16 hdr_info;
+	bool cleaned = false;
+	int cleaned_count = 0;
+
+	i = rx_ring->next_to_clean;
+	upper_len = 0;
+	rx_desc = IXGBE_RX_DESC_ADV(*rx_ring, i);
+	staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
+	rx_buffer_info = &rx_ring->rx_buffer_info[i];
+
+	while (staterr & IXGBE_RXD_STAT_DD) {
+		if (*work_done >= work_to_do)
+			break;
+		(*work_done)++;
+
+		if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+			hdr_info =
+			    le16_to_cpu(rx_desc->wb.lower.lo_dword.hdr_info);
+			len =
+			    ((hdr_info & IXGBE_RXDADV_HDRBUFLEN_MASK) >>
+			     IXGBE_RXDADV_HDRBUFLEN_SHIFT);
+			if (hdr_info & IXGBE_RXDADV_SPH)
+				adapter->rx_hdr_split++;
+			if (len > IXGBE_RX_HDR_SIZE)
+				len = IXGBE_RX_HDR_SIZE;
+			upper_len = le16_to_cpu(rx_desc->wb.upper.length);
+		} else
+			len = le16_to_cpu(rx_desc->wb.upper.length);
+
+		cleaned = true;
+		skb = rx_buffer_info->skb;
+		prefetch(skb->data - NET_IP_ALIGN);
+		rx_buffer_info->skb = NULL;
+
+		if (len && !skb_shinfo(skb)->nr_frags) {
+			pci_unmap_single(pdev, rx_buffer_info->dma,
+					 adapter->rx_buf_len + NET_IP_ALIGN,
+					 PCI_DMA_FROMDEVICE);
+			skb_put(skb, len);
+		}
+
+		if (upper_len) {
+			pci_unmap_page(pdev, rx_buffer_info->page_dma,
+				       PAGE_SIZE, PCI_DMA_FROMDEVICE);
+			rx_buffer_info->page_dma = 0;
+			skb_fill_page_desc(skb, skb_shinfo(skb)->nr_frags,
+					   rx_buffer_info->page, 0, upper_len);
+			rx_buffer_info->page = NULL;
+
+			skb->len += upper_len;
+			skb->data_len += upper_len;
+			skb->truesize += upper_len;
+		}
+
+		i++;
+		if (i == rx_ring->count)
+			i = 0;
+		next_buffer = &rx_ring->rx_buffer_info[i];
+
+		next_rxd = IXGBE_RX_DESC_ADV(*rx_ring, i);
+		prefetch(next_rxd);
+
+		cleaned_count++;
+		if (staterr & IXGBE_RXD_STAT_EOP) {
+			rx_ring->stats.packets++;
+			rx_ring->stats.bytes += skb->len;
+		} else {
+			rx_buffer_info->skb = next_buffer->skb;
+			rx_buffer_info->dma = next_buffer->dma;
+			next_buffer->skb = skb;
+			adapter->non_eop_descs++;
+			goto next_desc;
+		}
+
+		if (staterr & IXGBE_RXDADV_ERR_FRAME_ERR_MASK) {
+			dev_kfree_skb_irq(skb);
+			goto next_desc;
+		}
+
+		ixgbe_rx_checksum(adapter, staterr, skb);
+		skb->protocol = eth_type_trans(skb, netdev);
+		ixgbe_receive_skb(adapter, skb, (staterr & IXGBE_RXD_STAT_VP),
+				  le16_to_cpu(rx_desc->wb.upper.vlan));
+		netdev->last_rx = jiffies;
+
+next_desc:
+		rx_desc->wb.upper.status_error = 0;
+
+		/* return some buffers to hardware, one at a time is too slow */
+		if (cleaned_count >= IXGBE_RX_BUFFER_WRITE) {
+			ixgbe_alloc_rx_buffers(adapter, rx_ring,
+						   cleaned_count);
+			cleaned_count = 0;
+		}
+
+		/* use prefetched values */
+		rx_desc = next_rxd;
+		rx_buffer_info = next_buffer;
+
+		staterr = le32_to_cpu(rx_desc->wb.upper.status_error);
+	}
+
+	rx_ring->next_to_clean = i;
+	cleaned_count = IXGBE_DESC_UNUSED(rx_ring);
+
+	if (cleaned_count)
+		ixgbe_alloc_rx_buffers(adapter, rx_ring, cleaned_count);
+
+	return cleaned;
+}
+
+#define IXGBE_MAX_INTR 10
+/**
+ * ixgbe_configure_msix - Configure MSI-X hardware
+ * @adapter: board private structure
+ *
+ * ixgbe_configure_msix sets up the hardware to properly generate MSI-X
+ * interrupts.
+ **/
+static void ixgbe_configure_msix(struct ixgbe_adapter *adapter)
+{
+	int i, vector = 0;
+
+	for (i = 0; i < adapter->num_tx_queues; i++) {
+		ixgbe_set_ivar(adapter, IXGBE_IVAR_TX_QUEUE(i),
+			       IXGBE_MSIX_VECTOR(vector));
+		if (adapter->tx_eitr != 0)
+			writel(1000000000 / (adapter->tx_eitr * 256),
+			       adapter->hw.hw_addr +
+			       adapter->tx_ring[i].itr_register);
+		else
+			writel(0, adapter->hw.hw_addr +
+			       adapter->tx_ring[i].itr_register);
+		vector++;
+	}
+
+	for (i = 0; i < adapter->num_rx_queues; i++) {
+		ixgbe_set_ivar(adapter, IXGBE_IVAR_RX_QUEUE(i),
+			       IXGBE_MSIX_VECTOR(vector));
+		if (adapter->rx_eitr != 0)
+			writel(1000000000 / (adapter->rx_eitr * 256),
+			       adapter->hw.hw_addr +
+			       adapter->rx_ring[i].itr_register);
+		else
+			writel(0, adapter->hw.hw_addr +
+			       adapter->rx_ring[i].itr_register);
+		vector++;
+	}
+
+	vector = adapter->num_tx_queues + adapter->num_rx_queues;
+	ixgbe_set_ivar(adapter, IXGBE_IVAR_OTHER_CAUSES_INDEX,
+		       IXGBE_MSIX_VECTOR(vector));
+	IXGBE_WRITE_REG(&adapter->hw, IXGBE_EITR(vector), 1950);
+}
+
+static irqreturn_t ixgbe_msix_lsc(int irq, void *data)
+{
+	struct net_device *netdev = data;
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct ixgbe_hw *hw = &adapter->hw;
+	u32 eicr = IXGBE_READ_REG(hw, IXGBE_EICR);
+
+	if (eicr & IXGBE_EICR_LSC) {
+		adapter->lsc_int++;
+		mod_timer(&adapter->watchdog_timer, jiffies);
+	}
+	IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, IXGBE_EIMS_OTHER);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ixgbe_msix_clean_tx(int irq, void *data)
+{
+	struct ixgbe_ring *txr = data;
+	struct ixgbe_adapter *adapter = txr->adapter;
+	int i;
+
+	for (i = 0; i < IXGBE_MAX_INTR; i++) {
+		if (!ixgbe_clean_tx_irq(adapter, txr))
+			break;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ixgbe_msix_clean_rx(int irq, void *data)
+{
+	struct ixgbe_ring *rxr = data;
+	struct ixgbe_adapter *adapter = rxr->adapter;
+
+	IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, rxr->eims_value);
+	netif_rx_schedule(adapter->netdev);
+	return IRQ_HANDLED;
+}
+
+static int ixgbe_clean_rxonly(struct net_device *netdev, int *budget)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	int work_to_do = min(*budget, netdev->quota);
+	int work_done = 0;
+	struct ixgbe_ring *rxr = adapter->rx_ring;
+
+	/* Keep link state information with original netdev */
+	if (!netif_carrier_ok(netdev))
+		goto quit_polling;
+
+	ixgbe_clean_rx_irq(adapter, rxr, &work_done, work_to_do);
+
+	*budget -= work_done;
+	netdev->quota -= work_done;
+
+	/* If no Tx and not enough Rx work done, exit the polling mode */
+	if ((work_done == 0) || !netif_running(netdev)) {
+quit_polling:
+		netif_rx_complete(netdev);
+		IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS, rxr->eims_value);
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * ixgbe_setup_msix - Initialize MSI-X interrupts
+ *
+ * ixgbe_setup_msix allocates MSI-X vectors and requests
+ * interrutps from the kernel.
+ **/
+static int ixgbe_setup_msix(struct ixgbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	int i, int_vector = 0, err = 0;
+	int max_msix_count;
+
+	/* +1 for the LSC interrupt */
+	max_msix_count = adapter->num_rx_queues + adapter->num_tx_queues + 1;
+	adapter->msix_entries = kcalloc(max_msix_count,
+					sizeof(struct msix_entry), GFP_KERNEL);
+	if (!adapter->msix_entries)
+		return -ENOMEM;
+
+	for (i = 0; i < max_msix_count; i++)
+		adapter->msix_entries[i].entry = i;
+
+	err = pci_enable_msix(adapter->pdev, adapter->msix_entries,
+			      max_msix_count);
+	if (err)
+		goto out;
+
+	for (i = 0; i < adapter->num_tx_queues; i++) {
+		sprintf(adapter->tx_ring[i].name, "%s-tx%d", netdev->name, i);
+		err = request_irq(adapter->msix_entries[int_vector].vector,
+				  &ixgbe_msix_clean_tx,
+				  0,
+				  adapter->tx_ring[i].name,
+				  &(adapter->tx_ring[i]));
+		if (err) {
+			DPRINTK(PROBE, ERR,
+				"request_irq failed for MSIX interrupt "
+				"Error: %d\n", err);
+			goto release_irqs;
+		}
+		adapter->tx_ring[i].eims_value =
+		    (1 << IXGBE_MSIX_VECTOR(int_vector));
+		adapter->tx_ring[i].itr_register = IXGBE_EITR(int_vector);
+		adapter->tx_ring[i].itr_val = adapter->tx_eitr;
+		int_vector++;
+	}
+
+	for (i = 0; i < adapter->num_rx_queues; i++) {
+		if (strlen(netdev->name) < (IFNAMSIZ - 5))
+			sprintf(adapter->rx_ring[i].name,
+				"%s-rx%d", netdev->name, i);
+		else
+			memcpy(adapter->rx_ring[i].name,
+			       netdev->name, IFNAMSIZ);
+		err = request_irq(adapter->msix_entries[int_vector].vector,
+				  &ixgbe_msix_clean_rx, 0,
+				  adapter->rx_ring[i].name,
+				  &(adapter->rx_ring[i]));
+		if (err) {
+			DPRINTK(PROBE, ERR,
+				"request_irq failed for MSIX interrupt "
+				"Error: %d\n", err);
+			goto release_irqs;
+		}
+
+		adapter->rx_ring[i].eims_value =
+		    (1 << IXGBE_MSIX_VECTOR(int_vector));
+		adapter->rx_ring[i].itr_register = IXGBE_EITR(int_vector);
+		adapter->rx_ring[i].itr_val = adapter->rx_eitr;
+		int_vector++;
+	}
+
+	sprintf(adapter->lsc_name, "%s-lsc", netdev->name);
+	err = request_irq(adapter->msix_entries[int_vector].vector,
+			  &ixgbe_msix_lsc, 0, adapter->lsc_name, netdev);
+	if (err) {
+		DPRINTK(PROBE, ERR,
+			"request_irq for msix_lsc failed: %d\n", err);
+		goto release_irqs;
+	}
+
+	adapter->netdev->poll = ixgbe_clean_rxonly;
+	adapter->flags |= IXGBE_FLAG_MSIX_ENABLED;
+	return 0;
+
+release_irqs:
+	int_vector--;
+	for (; int_vector >= adapter->num_tx_queues; int_vector--)
+		free_irq(adapter->msix_entries[int_vector].vector,
+			 &(adapter->rx_ring[int_vector -
+					    adapter->num_tx_queues]));
+
+	for (; int_vector >= 0; int_vector--)
+		free_irq(adapter->msix_entries[int_vector].vector,
+			 &(adapter->tx_ring[int_vector]));
+out:
+	kfree(adapter->msix_entries);
+	adapter->msix_entries = NULL;
+	adapter->flags &= ~IXGBE_FLAG_MSIX_ENABLED;
+	return err;
+}
+
+/**
+ * ixgbe_intr - Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a network interface device structure
+ * @pt_regs: CPU registers structure
+ **/
+static irqreturn_t ixgbe_intr(int irq, void *data)
+{
+	struct net_device *netdev = data;
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct ixgbe_hw *hw = &adapter->hw;
+	u32 eicr;
+
+	eicr = IXGBE_READ_REG(hw, IXGBE_EICR);
+
+	if (!eicr)
+		return IRQ_NONE;	/* Not our interrupt */
+
+	if (eicr & IXGBE_EICR_LSC) {
+		adapter->lsc_int++;
+		mod_timer(&adapter->watchdog_timer, jiffies);
+	}
+	if (netif_rx_schedule_prep(netdev)) {
+		/* Disable interrupts and register for poll. The flush of the
+		 * posted write is intentionally left out. */
+		atomic_inc(&adapter->irq_sem);
+		IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, ~0);
+		__netif_rx_schedule(netdev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * ixgbe_request_irq - initialize interrupts
+ * @adapter: board private structure
+ *
+ * Attempts to configure interrupts using the best available
+ * capabilities of the hardware and kernel.
+ **/
+static int ixgbe_request_irq(struct ixgbe_adapter *adapter, u32 *num_rx_queues)
+{
+	struct net_device *netdev = adapter->netdev;
+	int flags, err;
+	irqreturn_t(*handler) (int, void *) = &ixgbe_intr;
+
+	flags = IRQF_SHARED;
+
+	err = ixgbe_setup_msix(adapter);
+	if (!err)
+		goto request_done;
+
+	/*
+	 * if we can't do MSI-X, fall through and try MSI
+	 * No need to reallocate memory since we're decreasing the number of
+	 * queues. We just won't use the other ones, also it is freed correctly
+	 * on ixgbe_remove.
+	 */
+	*num_rx_queues = 1;
+
+	/* do MSI */
+	err = pci_enable_msi(adapter->pdev);
+	if (!err) {
+		adapter->flags |= IXGBE_FLAG_MSI_ENABLED;
+		flags &= ~IRQF_SHARED;
+		handler = &ixgbe_intr;
+	}
+
+	err = request_irq(adapter->pdev->irq, handler, flags,
+			  netdev->name, netdev);
+	if (err)
+		DPRINTK(PROBE, ERR, "request_irq failed, Error %d\n", err);
+
+request_done:
+	return err;
+}
+
+static void ixgbe_free_irq(struct ixgbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+
+	if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
+		int i;
+
+		for (i = 0; i < adapter->num_tx_queues; i++)
+			free_irq(adapter->msix_entries[i].vector,
+				 &(adapter->tx_ring[i]));
+		for (i = 0; i < adapter->num_rx_queues; i++)
+			free_irq(adapter->msix_entries[i +
+						adapter->num_tx_queues].vector,
+				&(adapter->rx_ring[i]));
+		i = adapter->num_rx_queues + adapter->num_tx_queues;
+		free_irq(adapter->msix_entries[i].vector, netdev);
+		pci_disable_msix(adapter->pdev);
+		kfree(adapter->msix_entries);
+		adapter->msix_entries = NULL;
+		adapter->flags &= ~IXGBE_FLAG_MSIX_ENABLED;
+		return;
+	}
+
+	free_irq(adapter->pdev->irq, netdev);
+	if (adapter->flags & IXGBE_FLAG_MSI_ENABLED) {
+		pci_disable_msi(adapter->pdev);
+		adapter->flags &= ~IXGBE_FLAG_MSI_ENABLED;
+	}
+}
+
+/**
+ * ixgbe_irq_disable - Mask off interrupt generation on the NIC
+ * @adapter: board private structure
+ **/
+static inline void ixgbe_irq_disable(struct ixgbe_adapter *adapter)
+{
+	atomic_inc(&adapter->irq_sem);
+	IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMC, ~0);
+	IXGBE_WRITE_FLUSH(&adapter->hw);
+	synchronize_irq(adapter->pdev->irq);
+}
+
+/**
+ * ixgbe_irq_enable - Enable default interrupt generation settings
+ * @adapter: board private structure
+ **/
+static inline void ixgbe_irq_enable(struct ixgbe_adapter *adapter)
+{
+	if (atomic_dec_and_test(&adapter->irq_sem)) {
+		if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED) {
+			IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIAC,
+					(IXGBE_EIMS_ENABLE_MASK &
+					 ~(IXGBE_EIMS_OTHER | IXGBE_EIMS_LSC)));
+		}
+		IXGBE_WRITE_REG(&adapter->hw, IXGBE_EIMS,
+				IXGBE_EIMS_ENABLE_MASK);
+		IXGBE_WRITE_FLUSH(&adapter->hw);
+	}
+}
+
+/**
+ * ixgbe_configure_msi_and_legacy - Initialize PIN (INTA...) and MSI interrupts
+ *
+ **/
+static void ixgbe_configure_msi_and_legacy(struct ixgbe_adapter *adapter)
+{
+	int i;
+	struct ixgbe_hw *hw = &adapter->hw;
+
+	if (adapter->rx_eitr)
+		IXGBE_WRITE_REG(hw, IXGBE_EITR(0),
+				1000000000 / (adapter->rx_eitr * 256));
+
+	for (i = 0; i < adapter->num_rx_queues; i++) {
+		ixgbe_set_ivar(adapter, IXGBE_IVAR_RX_QUEUE(i),
+			       IXGBE_MSIX_VECTOR(i));
+	}
+	for (i = 0; i < adapter->num_tx_queues; i++) {
+		ixgbe_set_ivar(adapter, IXGBE_IVAR_TX_QUEUE(i),
+			       IXGBE_MSIX_VECTOR(i));
+	}
+}
+
+/**
+ * ixgbe_configure_tx - Configure 8254x Transmit Unit after Reset
+ * @adapter: board private structure
+ *
+ * Configure the Tx unit of the MAC after a reset.
+ **/
+static void ixgbe_configure_tx(struct ixgbe_adapter *adapter)
+{
+	u64 tdba;
+	struct ixgbe_hw *hw = &adapter->hw;
+	u32 i, tdlen;
+
+	/* Setup the HW Tx Head and Tail descriptor pointers */
+	for (i = 0; i < adapter->num_tx_queues; i++) {
+		tdba = adapter->tx_ring[i].dma;
+		tdlen = adapter->tx_ring[i].count *
+		    sizeof(union ixgbe_adv_tx_desc);
+		IXGBE_WRITE_REG(hw, IXGBE_TDBAL(i), (tdba & DMA_32BIT_MASK));
+		IXGBE_WRITE_REG(hw, IXGBE_TDBAH(i), (tdba >> 32));
+		IXGBE_WRITE_REG(hw, IXGBE_TDLEN(i), tdlen);
+		IXGBE_WRITE_REG(hw, IXGBE_TDH(i), 0);
+		IXGBE_WRITE_REG(hw, IXGBE_TDT(i), 0);
+		adapter->tx_ring[i].head = IXGBE_TDH(i);
+		adapter->tx_ring[i].tail = IXGBE_TDT(i);
+	}
+
+	IXGBE_WRITE_REG(hw, IXGBE_TIPG, IXGBE_TIPG_FIBER_DEFAULT);
+}
+
+#define PAGE_USE_COUNT(S) (((S) >> PAGE_SHIFT) + \
+			(((S) & (PAGE_SIZE - 1)) ? 1 : 0))
+
+#define IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT			2
+/**
+ * ixgbe_configure_rx - Configure 8254x Receive Unit after Reset
+ * @adapter: board private structure
+ *
+ * Configure the Rx unit of the MAC after a reset.
+ **/
+static void ixgbe_configure_rx(struct ixgbe_adapter *adapter)
+{
+	u64 rdba;
+	struct ixgbe_hw *hw = &adapter->hw;
+	struct net_device *netdev = adapter->netdev;
+	int max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN;
+	u32 rdlen, rxctrl, rxcsum;
+	u32 random[10];
+	u32 reta, mrqc;
+	int i;
+	u32 fctrl, hlreg0;
+	u32 srrctl;
+	u32 pages;
+
+	/* Decide whether to use packet split mode or not */
+	if (netdev->mtu > ETH_DATA_LEN)
+		adapter->flags |= IXGBE_FLAG_RX_PS_ENABLED;
+	else
+		adapter->flags &= ~IXGBE_FLAG_RX_PS_ENABLED;
+
+	/* Set the RX buffer length according to the mode */
+	if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+		adapter->rx_buf_len = IXGBE_RX_HDR_SIZE;
+	} else {
+		if (netdev->mtu <= ETH_DATA_LEN)
+			adapter->rx_buf_len = MAXIMUM_ETHERNET_VLAN_SIZE;
+		else
+			adapter->rx_buf_len = ALIGN(max_frame, 1024);
+	}
+
+	fctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_FCTRL);
+	fctrl |= IXGBE_FCTRL_BAM;
+	IXGBE_WRITE_REG(&adapter->hw, IXGBE_FCTRL, fctrl);
+
+	hlreg0 = IXGBE_READ_REG(hw, IXGBE_HLREG0);
+	if (adapter->netdev->mtu <= ETH_DATA_LEN)
+		hlreg0 &= ~IXGBE_HLREG0_JUMBOEN;
+	else
+		hlreg0 |= IXGBE_HLREG0_JUMBOEN;
+	IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg0);
+
+	pages = PAGE_USE_COUNT(adapter->netdev->mtu);
+
+	srrctl = IXGBE_READ_REG(&adapter->hw, IXGBE_SRRCTL(0));
+	srrctl &= ~IXGBE_SRRCTL_BSIZEHDR_MASK;
+	srrctl &= ~IXGBE_SRRCTL_BSIZEPKT_MASK;
+
+	if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) {
+		srrctl |= PAGE_SIZE >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
+		srrctl |= IXGBE_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS;
+		srrctl |= ((IXGBE_RX_HDR_SIZE <<
+			    IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT) &
+			   IXGBE_SRRCTL_BSIZEHDR_MASK);
+	} else {
+		srrctl |= IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF;
+
+		if (adapter->rx_buf_len == MAXIMUM_ETHERNET_VLAN_SIZE)
+			srrctl |=
+			     IXGBE_RXBUFFER_2048 >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
+		else
+			srrctl |=
+			     adapter->rx_buf_len >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
+	}
+	IXGBE_WRITE_REG(&adapter->hw, IXGBE_SRRCTL(0), srrctl);
+
+	rdlen = adapter->rx_ring[0].count * sizeof(union ixgbe_adv_rx_desc);
+	/* disable receives while setting up the descriptors */
+	rxctrl = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
+	IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, rxctrl & ~IXGBE_RXCTRL_RXEN);
+	rxctrl |= (IXGBE_RXCTRL_DMBYPS | IXGBE_RXCTRL_RXEN);
+
+	/* Setup the HW Rx Head and Tail Descriptor Pointers and
+	 * the Base and Length of the Rx Descriptor Ring */
+	for (i = 0; i < adapter->num_rx_queues; i++) {
+		rdba = adapter->rx_ring[i].dma;
+		IXGBE_WRITE_REG(hw, IXGBE_RDBAL(i), (rdba & DMA_32BIT_MASK));
+		IXGBE_WRITE_REG(hw, IXGBE_RDBAH(i), (rdba >> 32));
+		IXGBE_WRITE_REG(hw, IXGBE_RDLEN(i), rdlen);
+		IXGBE_WRITE_REG(hw, IXGBE_RDH(i), 0);
+		IXGBE_WRITE_REG(hw, IXGBE_RDT(i), 0);
+		adapter->rx_ring[i].head = IXGBE_RDH(i);
+		adapter->rx_ring[i].tail = IXGBE_RDT(i);
+	}
+
+	if (adapter->num_rx_queues > 1) {
+		/* Random 40bytes used as random key in RSS hash function */
+		get_random_bytes(&random[0], 40);
+
+		switch (adapter->num_rx_queues) {
+		case 8:
+		case 4:
+			/* Bits [3:0] in each byte refers the Rx queue no */
+			reta = 0x00010203;
+			break;
+		case 2:
+			reta = 0x00010001;
+			break;
+		default:
+			reta = 0x00000000;
+			break;
+		}
+
+		/* Fill out redirection table */
+		for (i = 0; i < 32; i++) {
+			IXGBE_WRITE_REG_ARRAY(hw, IXGBE_RETA(0), i, reta);
+			if (adapter->num_rx_queues > 4) {
+				i++;
+				IXGBE_WRITE_REG_ARRAY(hw, IXGBE_RETA(0), i,
+						      0x04050607);
+			}
+		}
+
+		/* Fill out hash function seeds */
+		for (i = 0; i < 10; i++)
+			IXGBE_WRITE_REG_ARRAY(hw, IXGBE_RSSRK(0), i, random[i]);
+
+		mrqc = IXGBE_MRQC_RSSEN
+		    /* Perform hash on these packet types */
+		    | IXGBE_MRQC_RSS_FIELD_IPV4
+		    | IXGBE_MRQC_RSS_FIELD_IPV4_TCP
+		    | IXGBE_MRQC_RSS_FIELD_IPV4_UDP
+		    | IXGBE_MRQC_RSS_FIELD_IPV6_EX_TCP
+		    | IXGBE_MRQC_RSS_FIELD_IPV6_EX
+		    | IXGBE_MRQC_RSS_FIELD_IPV6
+		    | IXGBE_MRQC_RSS_FIELD_IPV6_TCP
+		    | IXGBE_MRQC_RSS_FIELD_IPV6_UDP
+		    | IXGBE_MRQC_RSS_FIELD_IPV6_EX_UDP;
+		IXGBE_WRITE_REG(hw, IXGBE_MRQC, mrqc);
+
+		/* Multiqueue and packet checksumming are mutually exclusive. */
+		rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM);
+		rxcsum |= IXGBE_RXCSUM_PCSD;
+		IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum);
+	} else {
+		/* Enable Receive Checksum Offload for TCP and UDP */
+		rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM);
+		if (adapter->flags & IXGBE_FLAG_RX_CSUM_ENABLED) {
+			/* Enable IPv4 payload checksum for UDP fragments
+			 * Must be used in conjunction with packet-split. */
+			rxcsum |= IXGBE_RXCSUM_IPPCSE;
+		} else {
+			/* don't need to clear IPPCSE as it defaults to 0 */
+		}
+		IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum);
+	}
+	/* Enable Receives */
+	IXGBE_WRITE_REG(hw, IXGBE_RXCTRL, rxctrl);
+	rxctrl = IXGBE_READ_REG(hw, IXGBE_RXCTRL);
+}
+
+static void ixgbe_vlan_rx_register(struct net_device *netdev,
+				   struct vlan_group *grp)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	u32 ctrl;
+
+	ixgbe_irq_disable(adapter);
+	adapter->vlgrp = grp;
+
+	if (grp) {
+		/* enable VLAN tag insert/strip */
+		ctrl = IXGBE_READ_REG(&adapter->hw, IXGBE_VLNCTRL);
+		ctrl |= IXGBE_VLNCTRL_VME | IXGBE_VLNCTRL_VFE;
+		ctrl &= ~IXGBE_VLNCTRL_CFIEN;
+		IXGBE_WRITE_REG(&adapter->hw, IXGBE_VLNCTRL, ctrl);
+	}
+
+	ixgbe_irq_enable(adapter);
+}
+
+static void ixgbe_vlan_rx_add_vid(struct net_device *netdev, u16 vid)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	/* add VID to filter table */
+	ixgbe_set_vfta(&adapter->hw, vid, 0, true);
+}
+
+static void ixgbe_vlan_rx_kill_vid(struct net_device *netdev, u16 vid)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	ixgbe_irq_disable(adapter);
+	vlan_group_set_device(adapter->vlgrp, vid, NULL);
+	ixgbe_irq_enable(adapter);
+
+	/* remove VID from filter table */
+	ixgbe_set_vfta(&adapter->hw, vid, 0, false);
+}
+
+static void ixgbe_restore_vlan(struct ixgbe_adapter *adapter)
+{
+	ixgbe_vlan_rx_register(adapter->netdev, adapter->vlgrp);
+
+	if (adapter->vlgrp) {
+		u16 vid;
+		for (vid = 0; vid < VLAN_GROUP_ARRAY_LEN; vid++) {
+			if (!vlan_group_get_device(adapter->vlgrp, vid))
+				continue;
+			ixgbe_vlan_rx_add_vid(adapter->netdev, vid);
+		}
+	}
+}
+
+/**
+ * ixgbe_set_multi - Multicast and Promiscuous mode set
+ * @netdev: network interface device structure
+ *
+ * The set_multi entry point is called whenever the multicast address
+ * list or the network interface flags are updated.  This routine is
+ * responsible for configuring the hardware for proper multicast,
+ * promiscuous mode, and all-multi behavior.
+ **/
+static void ixgbe_set_multi(struct net_device *netdev)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct ixgbe_hw *hw = &adapter->hw;
+	struct dev_mc_list *mc_ptr;
+	u8 *mta_list;
+	u32 fctrl;
+	int i;
+
+	/* Check for Promiscuous and All Multicast modes */
+
+	fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL);
+
+	if (netdev->flags & IFF_PROMISC) {
+		fctrl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
+	} else if (netdev->flags & IFF_ALLMULTI) {
+		fctrl |= IXGBE_FCTRL_MPE;
+		fctrl &= ~IXGBE_FCTRL_UPE;
+	} else {
+		fctrl &= ~(IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE);
+	}
+
+	IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl);
+
+	if (netdev->mc_count) {
+		mta_list = kcalloc(netdev->mc_count, ETH_ALEN, GFP_ATOMIC);
+		if (!mta_list)
+			return;
+
+		/* Shared function expects packed array of only addresses. */
+		mc_ptr = netdev->mc_list;
+
+		for (i = 0; i < netdev->mc_count; i++) {
+			if (!mc_ptr)
+				break;
+			memcpy(mta_list + (i * ETH_ALEN), mc_ptr->dmi_addr,
+			       ETH_ALEN);
+			mc_ptr = mc_ptr->next;
+		}
+
+		ixgbe_update_mc_addr_list(hw, mta_list, i, 0);
+		kfree(mta_list);
+	} else {
+		ixgbe_update_mc_addr_list(hw, NULL, 0, 0);
+	}
+
+}
+
+int ixgbe_up(struct ixgbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+	int i;
+	u32 gpie;
+	struct ixgbe_hw *hw = &adapter->hw;
+	u32 txdctl, rxdctl, mhadd;
+	int max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN;
+
+	/* hardware has been reset, we need to reload some things */
+	ixgbe_set_multi(netdev);
+
+	ixgbe_restore_vlan(adapter);
+
+	ixgbe_configure_tx(adapter);
+	ixgbe_configure_rx(adapter);
+	for (i = 0; i < adapter->num_rx_queues; i++)
+		ixgbe_alloc_rx_buffers(adapter, &adapter->rx_ring[i],
+					   (adapter->rx_ring[i].count - 1));
+
+	if ((adapter->flags & IXGBE_FLAG_MSIX_ENABLED) ||
+			(adapter->flags & IXGBE_FLAG_MSI_ENABLED)) {
+		gpie = IXGBE_READ_REG(&adapter->hw, IXGBE_GPIE);
+		if (!(adapter->flags & IXGBE_FLAG_MSI_ENABLED))
+			gpie |= IXGBE_GPIE_MSIX_MODE;
+		IXGBE_WRITE_REG(&adapter->hw, IXGBE_GPIE,
+				(gpie | IXGBE_GPIE_EIAME |
+				 IXGBE_GPIE_PBA_SUPPORT | IXGBE_GPIE_OCD));
+		gpie = IXGBE_READ_REG(&adapter->hw, IXGBE_GPIE);
+
+	}
+
+	mhadd = IXGBE_READ_REG(hw, IXGBE_MHADD);
+
+	if (max_frame != (mhadd >> IXGBE_MHADD_MFS_SHIFT)) {
+		mhadd &= ~IXGBE_MHADD_MFS_MASK;
+		mhadd |= max_frame << IXGBE_MHADD_MFS_SHIFT;
+
+		IXGBE_WRITE_REG(hw, IXGBE_MHADD, mhadd);
+	}
+
+	for (i = 0; i < adapter->num_tx_queues; i++) {
+		txdctl = IXGBE_READ_REG(&adapter->hw, IXGBE_TXDCTL(i));
+		txdctl |= IXGBE_TXDCTL_ENABLE;
+		IXGBE_WRITE_REG(&adapter->hw, IXGBE_TXDCTL(i), txdctl);
+	}
+
+	for (i = 0; i < adapter->num_rx_queues; i++) {
+		rxdctl = IXGBE_READ_REG(&adapter->hw, IXGBE_RXDCTL(i));
+		rxdctl |= IXGBE_RXDCTL_ENABLE;
+		IXGBE_WRITE_REG(&adapter->hw, IXGBE_RXDCTL(i), rxdctl);
+	}
+
+	mod_timer(&adapter->watchdog_timer, jiffies);
+
+	netif_poll_enable(netdev);
+	if (adapter->flags & IXGBE_FLAG_MSIX_ENABLED)
+		ixgbe_configure_msix(adapter);
+	else
+		ixgbe_configure_msi_and_legacy(adapter);
+	ixgbe_irq_enable(adapter);
+
+	return 0;
+}
+
+void ixgbe_reset(struct ixgbe_adapter *adapter)
+{
+	/* Allow time for pending master requests to run */
+	ixgbe_reset_hw(&adapter->hw);
+
+	if (ixgbe_init_hw(&adapter->hw))
+		DPRINTK(PROBE, ERR, "Hardware Error\n");
+
+	/* reprogram the RAR[0] in case user changed it. */
+	ixgbe_set_rar(&adapter->hw, 0, adapter->hw.mac.addr, 0, IXGBE_RAH_AV);
+
+}
+
+#ifdef CONFIG_PM
+static int ixgbe_resume(struct pci_dev *pdev)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	u32 err, num_rx_queues = adapter->num_rx_queues;
+
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+	err = pci_enable_device(pdev);
+	if (err) {
+		printk(KERN_ERR "ixgbe: Cannot enable PCI device from " \
+				"suspend\n");
+		return err;
+	}
+	pci_set_master(pdev);
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	pci_enable_wake(pdev, PCI_D3cold, 0);
+
+	if (netif_running(netdev)) {
+		err = ixgbe_request_irq(adapter, &num_rx_queues);
+		if (err)
+			return err;
+	}
+
+	ixgbe_reset(adapter);
+
+	if (netif_running(netdev))
+		ixgbe_up(adapter);
+
+	netif_device_attach(netdev);
+
+	return 0;
+}
+#endif
+
+/**
+ * ixgbe_clean_rx_ring - Free Rx Buffers per Queue
+ * @adapter: board private structure
+ * @rx_ring: ring to free buffers from
+ **/
+static void ixgbe_clean_rx_ring(struct ixgbe_adapter *adapter,
+				struct ixgbe_ring *rx_ring)
+{
+	struct pci_dev *pdev = adapter->pdev;
+	unsigned long size;
+	unsigned int i;
+
+	/* Free all the Rx ring sk_buffs */
+
+	for (i = 0; i < rx_ring->count; i++) {
+		struct ixgbe_rx_buffer *rx_buffer_info;
+
+		rx_buffer_info = &rx_ring->rx_buffer_info[i];
+		if (rx_buffer_info->dma) {
+			pci_unmap_single(pdev, rx_buffer_info->dma,
+					 adapter->rx_buf_len,
+					 PCI_DMA_FROMDEVICE);
+			rx_buffer_info->dma = 0;
+		}
+		if (rx_buffer_info->skb) {
+			dev_kfree_skb(rx_buffer_info->skb);
+			rx_buffer_info->skb = NULL;
+		}
+		if (!rx_buffer_info->page)
+			continue;
+		pci_unmap_page(pdev, rx_buffer_info->page_dma, PAGE_SIZE,
+			       PCI_DMA_FROMDEVICE);
+		rx_buffer_info->page_dma = 0;
+
+		put_page(rx_buffer_info->page);
+		rx_buffer_info->page = NULL;
+	}
+
+	size = sizeof(struct ixgbe_rx_buffer) * rx_ring->count;
+	memset(rx_ring->rx_buffer_info, 0, size);
+
+	/* Zero out the descriptor ring */
+	memset(rx_ring->desc, 0, rx_ring->size);
+
+	rx_ring->next_to_clean = 0;
+	rx_ring->next_to_use = 0;
+
+	writel(0, adapter->hw.hw_addr + rx_ring->head);
+	writel(0, adapter->hw.hw_addr + rx_ring->tail);
+}
+
+/**
+ * ixgbe_clean_tx_ring - Free Tx Buffers
+ * @adapter: board private structure
+ * @tx_ring: ring to be cleaned
+ **/
+static void ixgbe_clean_tx_ring(struct ixgbe_adapter *adapter,
+				struct ixgbe_ring *tx_ring)
+{
+	struct ixgbe_tx_buffer *tx_buffer_info;
+	unsigned long size;
+	unsigned int i;
+
+	/* Free all the Tx ring sk_buffs */
+
+	for (i = 0; i < tx_ring->count; i++) {
+		tx_buffer_info = &tx_ring->tx_buffer_info[i];
+		ixgbe_unmap_and_free_tx_resource(adapter, tx_buffer_info);
+	}
+
+	size = sizeof(struct ixgbe_tx_buffer) * tx_ring->count;
+	memset(tx_ring->tx_buffer_info, 0, size);
+
+	/* Zero out the descriptor ring */
+	memset(tx_ring->desc, 0, tx_ring->size);
+
+	tx_ring->next_to_use = 0;
+	tx_ring->next_to_clean = 0;
+
+	writel(0, adapter->hw.hw_addr + tx_ring->head);
+	writel(0, adapter->hw.hw_addr + tx_ring->tail);
+}
+
+/**
+ * ixgbe_clean_all_tx_rings - Free Tx Buffers for all queues
+ * @adapter: board private structure
+ **/
+static void ixgbe_clean_all_tx_rings(struct ixgbe_adapter *adapter)
+{
+	int i;
+
+	for (i = 0; i < adapter->num_tx_queues; i++)
+		ixgbe_clean_tx_ring(adapter, &adapter->tx_ring[i]);
+}
+
+/**
+ * ixgbe_clean_all_rx_rings - Free Rx Buffers for all queues
+ * @adapter: board private structure
+ **/
+static void ixgbe_clean_all_rx_rings(struct ixgbe_adapter *adapter)
+{
+	int i;
+
+	for (i = 0; i < adapter->num_rx_queues; i++)
+		ixgbe_clean_rx_ring(adapter, &adapter->rx_ring[i]);
+}
+
+void ixgbe_down(struct ixgbe_adapter *adapter)
+{
+	struct net_device *netdev = adapter->netdev;
+
+	netif_poll_disable(netdev);
+
+	ixgbe_irq_disable(adapter);
+
+	del_timer_sync(&adapter->watchdog_timer);
+
+	netif_carrier_off(netdev);
+	netif_stop_queue(netdev);
+
+	ixgbe_reset(adapter);
+	ixgbe_clean_all_tx_rings(adapter);
+	ixgbe_clean_all_rx_rings(adapter);
+
+}
+
+static int ixgbe_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+#ifdef CONFIG_PM
+	int retval = 0;
+#endif
+
+	netif_device_detach(netdev);
+
+	if (netif_running(netdev)) {
+		ixgbe_down(adapter);
+		ixgbe_free_irq(adapter);
+	}
+
+#ifdef CONFIG_PM
+	retval = pci_save_state(pdev);
+	if (retval)
+		return retval;
+#endif
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	pci_enable_wake(pdev, PCI_D3cold, 0);
+
+	pci_disable_device(pdev);
+
+	pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+	return 0;
+}
+
+static void ixgbe_shutdown(struct pci_dev *pdev)
+{
+	ixgbe_suspend(pdev, PMSG_SUSPEND);
+}
+
+/**
+ * ixgbe_clean - NAPI Rx polling callback
+ * @adapter: board private structure
+ **/
+static int ixgbe_clean(struct net_device *netdev, int *budget)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	int work_to_do = min(*budget, netdev->quota);
+	int tx_cleaned, work_done = 0;
+
+	/* Keep link state information with original netdev */
+	if (!netif_carrier_ok(adapter->netdev))
+		goto quit_polling;
+
+	/* In non-MSIX case, there is no multi-Tx/Rx queue */
+	tx_cleaned = ixgbe_clean_tx_irq(adapter, adapter->tx_ring);
+	ixgbe_clean_rx_irq(adapter, adapter->rx_ring, &work_done, work_to_do);
+
+	*budget -= work_done;
+	netdev->quota -= work_done;
+
+	/* If no Tx and not enough Rx work done, exit the polling mode */
+	if ((!tx_cleaned && (work_done == 0)) ||
+	    !netif_running(adapter->netdev)) {
+quit_polling:
+		netif_rx_complete(netdev);
+		ixgbe_irq_enable(adapter);
+		return 0;
+	}
+
+	return 1;
+}
+
+/**
+ * ixgbe_tx_timeout - Respond to a Tx Hang
+ * @netdev: network interface device structure
+ **/
+static void ixgbe_tx_timeout(struct net_device *netdev)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	/* Do the reset outside of interrupt context */
+	schedule_work(&adapter->reset_task);
+}
+
+static void ixgbe_reset_task(struct work_struct *work)
+{
+	struct ixgbe_adapter *adapter;
+	adapter = container_of(work, struct ixgbe_adapter, reset_task);
+
+	adapter->tx_timeout_count++;
+
+	ixgbe_down(adapter);
+	ixgbe_up(adapter);
+}
+
+/**
+ * ixgbe_alloc_queues - Allocate memory for all rings
+ * @adapter: board private structure to initialize
+ *
+ * We allocate one ring per queue at run-time since we don't know the
+ * number of queues at compile-time.  The polling_netdev array is
+ * intended for Multiqueue, but should work fine with a single queue.
+ **/
+static int __devinit ixgbe_alloc_queues(struct ixgbe_adapter *adapter)
+{
+	int i;
+
+	adapter->tx_ring = kcalloc(adapter->num_tx_queues,
+				   sizeof(struct ixgbe_ring), GFP_KERNEL);
+	if (!adapter->tx_ring)
+		return -ENOMEM;
+
+	for (i = 0; i < adapter->num_tx_queues; i++)
+		adapter->tx_ring[i].count = IXGBE_DEFAULT_TXD;
+
+	adapter->rx_ring = kcalloc(adapter->num_rx_queues,
+				   sizeof(struct ixgbe_ring), GFP_KERNEL);
+	if (!adapter->rx_ring) {
+		kfree(adapter->tx_ring);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < adapter->num_rx_queues; i++) {
+		adapter->rx_ring[i].adapter = adapter;
+		adapter->rx_ring[i].itr_register = IXGBE_EITR(i);
+		adapter->rx_ring[i].count = IXGBE_DEFAULT_RXD;
+	}
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_sw_init - Initialize general software structures (struct ixgbe_adapter)
+ * @adapter: board private structure to initialize
+ *
+ * ixgbe_sw_init initializes the Adapter private data structure.
+ * Fields are initialized based on PCI device information and
+ * OS network device settings (MTU size).
+ **/
+static int __devinit ixgbe_sw_init(struct ixgbe_adapter *adapter)
+{
+	struct ixgbe_hw *hw = &adapter->hw;
+	struct pci_dev *pdev = adapter->pdev;
+
+	/* PCI config space info */
+
+	hw->vendor_id = pdev->vendor;
+	hw->device_id = pdev->device;
+	hw->subsystem_vendor_id = pdev->subsystem_vendor;
+	hw->subsystem_device_id = pdev->subsystem_device;
+
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &hw->revision_id);
+
+	if (ixgbe_init_shared_code(hw)) {
+		DPRINTK(PROBE, ERR, "Init_shared_code failed\n");
+		return -EIO;
+	}
+
+	hw->mac.link_mode_select = IXGBE_AUTOC_LMS_10G_LINK_NO_AN;
+	if (ixgbe_init_hw(hw)) {
+		DPRINTK(PROBE, ERR, "HW Init failed\n");
+		return -EIO;
+	}
+	if (ixgbe_setup_link_speed(hw, IXGBE_LINK_SPEED_10GB_FULL, true,
+								false)) {
+		DPRINTK(PROBE, ERR, "link_setup_speed FAILED\n");
+		return -EIO;
+	}
+
+	/* initialize eeprom parameters */
+	if (ixgbe_init_eeprom_params(hw)) {
+		IXGBE_ERR("EEPROM initialization failed\n");
+		return -EIO;
+	}
+
+	/* Set the default values */
+	adapter->num_rx_queues = IXGBE_DEFAULT_RXQ;
+	adapter->num_tx_queues = 1;
+	adapter->flags |= IXGBE_FLAG_RX_CSUM_ENABLED;
+
+	if (ixgbe_alloc_queues(adapter)) {
+		DPRINTK(PROBE, ERR, "Unable to allocate memory for queues\n");
+		return -ENOMEM;
+	}
+
+	atomic_set(&adapter->irq_sem, 1);
+
+	return 0;
+}
+
+/**
+ * ixgbe_setup_tx_resources - allocate Tx resources (Descriptors)
+ * @adapter: board private structure
+ * @txdr:    tx descriptor ring (for a specific queue) to setup
+ *
+ * Return 0 on success, negative on failure
+ **/
+int ixgbe_setup_tx_resources(struct ixgbe_adapter *adapter,
+			     struct ixgbe_ring *txdr)
+{
+	struct pci_dev *pdev = adapter->pdev;
+	int size;
+
+	size = sizeof(struct ixgbe_tx_buffer) * txdr->count;
+	txdr->tx_buffer_info = vmalloc(size);
+	if (!txdr->tx_buffer_info) {
+		DPRINTK(PROBE, ERR,
+		"Unable to allocate memory for the transmit descriptor ring\n");
+		return -ENOMEM;
+	}
+	memset(txdr->tx_buffer_info, 0, size);
+
+	/* round up to nearest 4K */
+	txdr->size = txdr->count * sizeof(union ixgbe_adv_tx_desc);
+	txdr->size = ALIGN(txdr->size, 4096);
+
+	txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma);
+	if (!txdr->desc) {
+		vfree(txdr->tx_buffer_info);
+		DPRINTK(PROBE, ERR,
+			"Memory allocation failed for the tx desc ring\n");
+		return -ENOMEM;
+	}
+
+	txdr->adapter = adapter;
+	txdr->next_to_use = 0;
+	txdr->next_to_clean = 0;
+	spin_lock_init(&txdr->tx_lock);
+
+	return 0;
+}
+
+/**
+ * ixgbe_setup_rx_resources - allocate Rx resources (Descriptors)
+ * @adapter: board private structure
+ * @rxdr:    rx descriptor ring (for a specific queue) to setup
+ *
+ * Returns 0 on success, negative on failure
+ **/
+int ixgbe_setup_rx_resources(struct ixgbe_adapter *adapter,
+			     struct ixgbe_ring *rxdr)
+{
+	struct pci_dev *pdev = adapter->pdev;
+	int size, desc_len;
+
+	size = sizeof(struct ixgbe_rx_buffer) * rxdr->count;
+	rxdr->rx_buffer_info = vmalloc(size);
+	if (!rxdr->rx_buffer_info) {
+		DPRINTK(PROBE, ERR,
+			"vmalloc allocation failed for the rx desc ring\n");
+		return -ENOMEM;
+	}
+	memset(rxdr->rx_buffer_info, 0, size);
+
+	desc_len = sizeof(union ixgbe_adv_rx_desc);
+
+	/* Round up to nearest 4K */
+	rxdr->size = rxdr->count * desc_len;
+	rxdr->size = ALIGN(rxdr->size, 4096);
+
+	rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma);
+
+	if (!rxdr->desc) {
+		DPRINTK(PROBE, ERR,
+			"Memory allocation failed for the rx desc ring\n");
+		vfree(rxdr->rx_buffer_info);
+		return -ENOMEM;
+	}
+
+	rxdr->next_to_clean = 0;
+	rxdr->next_to_use = 0;
+	rxdr->adapter = adapter;
+
+	return 0;
+}
+
+/**
+ * ixgbe_free_tx_resources - Free Tx Resources per Queue
+ * @adapter: board private structure
+ * @tx_ring: Tx descriptor ring for a specific queue
+ *
+ * Free all transmit software resources
+ **/
+void ixgbe_free_tx_resources(struct ixgbe_adapter *adapter,
+			     struct ixgbe_ring *tx_ring)
+{
+	struct pci_dev *pdev = adapter->pdev;
+
+	ixgbe_clean_tx_ring(adapter, tx_ring);
+
+	vfree(tx_ring->tx_buffer_info);
+	tx_ring->tx_buffer_info = NULL;
+
+	pci_free_consistent(pdev, tx_ring->size, tx_ring->desc, tx_ring->dma);
+
+	tx_ring->desc = NULL;
+}
+
+/**
+ * ixgbe_free_all_tx_resources - Free Tx Resources for All Queues
+ * @adapter: board private structure
+ *
+ * Free all transmit software resources
+ **/
+static void ixgbe_free_all_tx_resources(struct ixgbe_adapter *adapter)
+{
+	int i;
+
+	for (i = 0; i < adapter->num_tx_queues; i++)
+		ixgbe_free_tx_resources(adapter, &adapter->tx_ring[i]);
+}
+
+/**
+ * ixgbe_free_rx_resources - Free Rx Resources
+ * @adapter: board private structure
+ * @rx_ring: ring to clean the resources from
+ *
+ * Free all receive software resources
+ **/
+void ixgbe_free_rx_resources(struct ixgbe_adapter *adapter,
+			     struct ixgbe_ring *rx_ring)
+{
+	struct pci_dev *pdev = adapter->pdev;
+
+
+	ixgbe_clean_rx_ring(adapter, rx_ring);
+
+	vfree(rx_ring->rx_buffer_info);
+	rx_ring->rx_buffer_info = NULL;
+
+	pci_free_consistent(pdev, rx_ring->size, rx_ring->desc, rx_ring->dma);
+
+	rx_ring->desc = NULL;
+}
+
+/**
+ * ixgbe_free_all_rx_resources - Free Rx Resources for All Queues
+ * @adapter: board private structure
+ *
+ * Free all receive software resources
+ **/
+static void ixgbe_free_all_rx_resources(struct ixgbe_adapter *adapter)
+{
+	int i;
+
+	for (i = 0; i < adapter->num_rx_queues; i++)
+		ixgbe_free_rx_resources(adapter, &adapter->rx_ring[i]);
+}
+
+/**
+ * ixgbe_setup_all_tx_resources - wrapper to allocate Tx resources
+ *				  (Descriptors) for all queues
+ * @adapter: board private structure
+ *
+ * If this function returns with an error, then it's possible one or
+ * more of the rings is populated (while the rest are not).  It is the
+ * callers duty to clean those orphaned rings.
+ *
+ * Return 0 on success, negative on failure
+ **/
+static int ixgbe_setup_all_tx_resources(struct ixgbe_adapter *adapter)
+{
+	int i, err = 0;
+
+	for (i = 0; i < adapter->num_tx_queues; i++) {
+		err = ixgbe_setup_tx_resources(adapter, &adapter->tx_ring[i]);
+		if (err) {
+			DPRINTK(PROBE, ERR,
+				"Allocation for Tx Queue %u failed\n", i);
+			break;
+		}
+	}
+
+	return err;
+}
+
+/**
+ * ixgbe_setup_all_rx_resources - wrapper to allocate Rx resources
+ *				  (Descriptors) for all queues
+ * @adapter: board private structure
+ *
+ * If this function returns with an error, then it's possible one or
+ * more of the rings is populated (while the rest are not).  It is the
+ * callers duty to clean those orphaned rings.
+ *
+ * Return 0 on success, negative on failure
+ **/
+
+static int ixgbe_setup_all_rx_resources(struct ixgbe_adapter *adapter)
+{
+	int i, err = 0;
+
+	for (i = 0; i < adapter->num_rx_queues; i++) {
+		err = ixgbe_setup_rx_resources(adapter, &adapter->rx_ring[i]);
+		if (err) {
+			DPRINTK(PROBE, ERR,
+				"Allocation for Rx Queue %u failed\n", i);
+			break;
+		}
+	}
+
+	return err;
+}
+
+/**
+ * ixgbe_change_mtu - Change the Maximum Transfer Unit
+ * @netdev: network interface device structure
+ * @new_mtu: new value for maximum frame size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int ixgbe_change_mtu(struct net_device *netdev, int new_mtu)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	int max_frame = new_mtu + ETH_HLEN + ETH_FCS_LEN;
+
+	if ((max_frame < (ETH_ZLEN + ETH_FCS_LEN)) ||
+	    (max_frame > IXGBE_MAX_JUMBO_FRAME_SIZE))
+		return -EINVAL;
+
+	netdev->mtu = new_mtu;
+
+	if (netif_running(netdev)) {
+		ixgbe_down(adapter);
+		ixgbe_up(adapter);
+	}
+
+	return 0;
+}
+
+/**
+ * ixgbe_open - Called when a network interface is made active
+ * @netdev: network interface device structure
+ *
+ * Returns 0 on success, negative value on failure
+ *
+ * The open entry point is called when a network interface is made
+ * active by the system (IFF_UP).  At this point all resources needed
+ * for transmit and receive operations are allocated, the interrupt
+ * handler is registered with the OS, the watchdog timer is started,
+ * and the stack is notified that the interface is ready.
+ **/
+static int ixgbe_open(struct net_device *netdev)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	int err, i;
+	u32 ctrl_ext, num_rx_queues = adapter->num_rx_queues;
+
+	/* Let firmware know the driver has taken over */
+	ctrl_ext = IXGBE_READ_REG(&adapter->hw, IXGBE_CTRL_EXT);
+	IXGBE_WRITE_REG(&adapter->hw, IXGBE_CTRL_EXT,
+			ctrl_ext | IXGBE_CTRL_EXT_DRV_LOAD);
+
+	/* allocate transmit descriptors */
+	err = ixgbe_setup_all_tx_resources(adapter);
+	if (err)
+		goto err_setup_tx;
+
+	/* allocate receive descriptors */
+	err = ixgbe_setup_all_rx_resources(adapter);
+	if (err)
+		goto err_setup_rx;
+
+	err = ixgbe_request_irq(adapter, &num_rx_queues);
+	if (err)
+		goto err_req_irq;
+
+	/* ixgbe_request might have reduced num_rx_queues */
+	if (num_rx_queues < adapter->num_rx_queues) {
+		/* released the remaining rx queue resource */
+		for (i = num_rx_queues; i < adapter->num_rx_queues; i++)
+			ixgbe_free_rx_resources(adapter, &adapter->rx_ring[i]);
+		adapter->num_rx_queues = num_rx_queues;
+	}
+
+	err = ixgbe_up(adapter);
+	if (err)
+		goto err_up;
+
+	return IXGBE_SUCCESS;
+
+err_up:
+	ixgbe_free_irq(adapter);
+err_req_irq:
+	ixgbe_free_all_rx_resources(adapter);
+err_setup_rx:
+	ixgbe_free_all_tx_resources(adapter);
+err_setup_tx:
+	ixgbe_reset(adapter);
+
+	return err;
+}
+
+/**
+ * ixgbe_close - Disables a network interface
+ * @netdev: network interface device structure
+ *
+ * Returns 0, this is not allowed to fail
+ *
+ * The close entry point is called when an interface is de-activated
+ * by the OS.  The hardware is still under the drivers control, but
+ * needs to be disabled.  A global MAC reset is issued to stop the
+ * hardware, and all transmit and receive resources are freed.
+ **/
+static int ixgbe_close(struct net_device *netdev)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	u32 ctrl_ext;
+
+	ixgbe_down(adapter);
+	ixgbe_free_irq(adapter);
+
+	ixgbe_free_all_tx_resources(adapter);
+	ixgbe_free_all_rx_resources(adapter);
+
+	ctrl_ext = IXGBE_READ_REG(&adapter->hw, IXGBE_CTRL_EXT);
+	IXGBE_WRITE_REG(&adapter->hw, IXGBE_CTRL_EXT,
+			ctrl_ext & ~IXGBE_CTRL_EXT_DRV_LOAD);
+
+	return 0;
+}
+
+/**
+ * ixgbe_update_stats - Update the board statistics counters.
+ * @adapter: board private structure
+ **/
+void ixgbe_update_stats(struct ixgbe_adapter *adapter)
+{
+	struct ixgbe_hw *hw = &adapter->hw;
+	u64 good_rx, missed_rx, bprc;
+
+	adapter->stats.crcerrs += IXGBE_READ_REG(hw, IXGBE_CRCERRS);
+	good_rx = IXGBE_READ_REG(hw, IXGBE_GPRC);
+	missed_rx = IXGBE_READ_REG(hw, IXGBE_MPC(0));
+	missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(1));
+	missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(2));
+	missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(3));
+	missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(4));
+	missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(5));
+	missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(6));
+	missed_rx += IXGBE_READ_REG(hw, IXGBE_MPC(7));
+	adapter->stats.gprc += (good_rx - missed_rx);
+
+	adapter->stats.mpc[0] += missed_rx;
+	adapter->stats.gorc += IXGBE_READ_REG(hw, IXGBE_GORCH);
+	bprc = IXGBE_READ_REG(hw, IXGBE_BPRC);
+	adapter->stats.bprc += bprc;
+	adapter->stats.mprc += IXGBE_READ_REG(hw, IXGBE_MPRC);
+	adapter->stats.mprc -= bprc;
+	adapter->stats.roc += IXGBE_READ_REG(hw, IXGBE_ROC);
+	adapter->stats.prc64 += IXGBE_READ_REG(hw, IXGBE_PRC64);
+	adapter->stats.prc127 += IXGBE_READ_REG(hw, IXGBE_PRC127);
+	adapter->stats.prc255 += IXGBE_READ_REG(hw, IXGBE_PRC255);
+	adapter->stats.prc511 += IXGBE_READ_REG(hw, IXGBE_PRC511);
+	adapter->stats.prc1023 += IXGBE_READ_REG(hw, IXGBE_PRC1023);
+	adapter->stats.prc1522 += IXGBE_READ_REG(hw, IXGBE_PRC1522);
+
+	adapter->stats.rlec += IXGBE_READ_REG(hw, IXGBE_RLEC);
+	adapter->stats.lxonrxc += IXGBE_READ_REG(hw, IXGBE_LXONRXC);
+	adapter->stats.lxontxc += IXGBE_READ_REG(hw, IXGBE_LXONTXC);
+	adapter->stats.lxoffrxc += IXGBE_READ_REG(hw, IXGBE_LXOFFRXC);
+	adapter->stats.lxofftxc += IXGBE_READ_REG(hw, IXGBE_LXOFFTXC);
+	adapter->stats.ruc += IXGBE_READ_REG(hw, IXGBE_RUC);
+	adapter->stats.gptc += IXGBE_READ_REG(hw, IXGBE_GPTC);
+	adapter->stats.gotc += IXGBE_READ_REG(hw, IXGBE_GOTCH);
+	adapter->stats.rnbc[0] += IXGBE_READ_REG(hw, IXGBE_RNBC(0));
+	adapter->stats.ruc += IXGBE_READ_REG(hw, IXGBE_RUC);
+	adapter->stats.rfc += IXGBE_READ_REG(hw, IXGBE_RFC);
+	adapter->stats.rjc += IXGBE_READ_REG(hw, IXGBE_RJC);
+	adapter->stats.tor += IXGBE_READ_REG(hw, IXGBE_TORH);
+	adapter->stats.tpr += IXGBE_READ_REG(hw, IXGBE_TPR);
+	adapter->stats.ptc64 += IXGBE_READ_REG(hw, IXGBE_PTC64);
+	adapter->stats.ptc127 += IXGBE_READ_REG(hw, IXGBE_PTC127);
+	adapter->stats.ptc255 += IXGBE_READ_REG(hw, IXGBE_PTC255);
+	adapter->stats.ptc511 += IXGBE_READ_REG(hw, IXGBE_PTC511);
+	adapter->stats.ptc1023 += IXGBE_READ_REG(hw, IXGBE_PTC1023);
+	adapter->stats.ptc1522 += IXGBE_READ_REG(hw, IXGBE_PTC1522);
+	adapter->stats.mptc += IXGBE_READ_REG(hw, IXGBE_MPTC);
+	adapter->stats.bptc += IXGBE_READ_REG(hw, IXGBE_BPTC);
+
+	/* Fill out the OS statistics structure */
+	adapter->net_stats.rx_packets = adapter->stats.gprc;
+	adapter->net_stats.tx_packets = adapter->stats.gptc;
+	adapter->net_stats.rx_bytes = adapter->stats.gorc;
+	adapter->net_stats.tx_bytes = adapter->stats.gotc;
+	adapter->net_stats.multicast = adapter->stats.mprc;
+
+	/* Rx Errors */
+	adapter->net_stats.rx_errors = adapter->stats.crcerrs +
+						adapter->stats.rlec;
+	adapter->net_stats.rx_dropped = 0;
+	adapter->net_stats.rx_length_errors = adapter->stats.rlec;
+	adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs;
+	adapter->net_stats.rx_missed_errors = adapter->stats.mpc[0];
+
+}
+
+/**
+ * ixgbe_watchdog - Timer Call-back
+ * @data: pointer to adapter cast into an unsigned long
+ **/
+static void ixgbe_watchdog(unsigned long data)
+{
+	struct ixgbe_adapter *adapter = (struct ixgbe_adapter *)data;
+	struct net_device *netdev = adapter->netdev;
+	bool link_up;
+	u32 link_speed = 0;
+
+	ixgbe_check_link(&adapter->hw, &(link_speed), &link_up);
+
+	if (link_up) {
+		if (!netif_carrier_ok(netdev)) {
+			DPRINTK(LINK, INFO,
+				"NIC Link is Up %d Gbps Full Duplex\n",
+				((link_speed ==
+				  IXGBE_LINK_SPEED_10GB_FULL) ? 10 : 1));
+
+			netif_carrier_on(netdev);
+			netif_wake_queue(netdev);
+		} else {
+			/* Force detection of hung controller */
+			adapter->detect_tx_hung = true;
+		}
+	} else {
+		if (netif_carrier_ok(netdev)) {
+			DPRINTK(LINK, INFO, "NIC Link is Down\n");
+			netif_carrier_off(netdev);
+			netif_stop_queue(netdev);
+		}
+	}
+
+	ixgbe_update_stats(adapter);
+
+	/* Reset the timer */
+	mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 2 * HZ));
+}
+
+#define IXGBE_MAX_TXD_PWR	14
+#define IXGBE_MAX_DATA_PER_TXD	(1 << IXGBE_MAX_TXD_PWR)
+
+/* Tx Descriptors needed, worst case */
+#define TXD_USE_COUNT(S) (((S) >> IXGBE_MAX_TXD_PWR) + \
+			 (((S) & (IXGBE_MAX_DATA_PER_TXD - 1)) ? 1 : 0))
+#define DESC_NEEDED TXD_USE_COUNT(IXGBE_MAX_DATA_PER_TXD) /* skb->data */ + \
+	MAX_SKB_FRAGS * TXD_USE_COUNT(PAGE_SIZE) + 1	/* for context */
+
+static int ixgbe_tso(struct ixgbe_adapter *adapter,
+			 struct ixgbe_ring *tx_ring, struct sk_buff *skb,
+			 u32 tx_flags, u8 *hdr_len)
+{
+	struct ixgbe_adv_tx_context_desc *context_desc;
+	unsigned int i;
+	int err;
+	struct ixgbe_tx_buffer *tx_buffer_info;
+	u32 vlan_macip_lens = 0, type_tucmd_mlhl = 0;
+	u32 mss_l4len_idx = 0, l4len;
+	*hdr_len = 0;
+
+	if (skb_is_gso(skb)) {
+		if (skb_header_cloned(skb)) {
+			err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC);
+			if (err)
+				return err;
+		}
+		l4len = tcp_hdrlen(skb);
+		*hdr_len += l4len;
+
+		if (skb->protocol == ntohs(ETH_P_IP)) {
+			struct iphdr *iph = ip_hdr(skb);
+			iph->tot_len = 0;
+			iph->check = 0;
+			tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr,
+								 iph->daddr, 0,
+								 IPPROTO_TCP,
+								 0);
+			adapter->hw_tso_ctxt++;
+		} else if (skb_shinfo(skb)->gso_type == SKB_GSO_TCPV6) {
+			ipv6_hdr(skb)->payload_len = 0;
+			tcp_hdr(skb)->check =
+			    ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+					     &ipv6_hdr(skb)->daddr,
+					     0, IPPROTO_TCP, 0);
+			adapter->hw_tso6_ctxt++;
+		}
+
+		i = tx_ring->next_to_use;
+
+		tx_buffer_info = &tx_ring->tx_buffer_info[i];
+		context_desc = IXGBE_TX_CTXTDESC_ADV(*tx_ring, i);
+
+		/* VLAN MACLEN IPLEN */
+		if (tx_flags & IXGBE_TX_FLAGS_VLAN)
+			vlan_macip_lens |=
+			    (tx_flags & IXGBE_TX_FLAGS_VLAN_MASK);
+		vlan_macip_lens |= ((skb_network_offset(skb)) <<
+				    IXGBE_ADVTXD_MACLEN_SHIFT);
+		*hdr_len += skb_network_offset(skb);
+		vlan_macip_lens |=
+		    (skb_transport_header(skb) - skb_network_header(skb));
+		*hdr_len +=
+		    (skb_transport_header(skb) - skb_network_header(skb));
+		context_desc->vlan_macip_lens = cpu_to_le32(vlan_macip_lens);
+		context_desc->seqnum_seed = 0;
+
+		/* ADV DTYP TUCMD MKRLOC/ISCSIHEDLEN */
+		type_tucmd_mlhl |= (IXGBE_TXD_CMD_DEXT |
+				    IXGBE_ADVTXD_DTYP_CTXT);
+
+		if (skb->protocol == ntohs(ETH_P_IP))
+			type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV4;
+		type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_TCP;
+		context_desc->type_tucmd_mlhl = cpu_to_le32(type_tucmd_mlhl);
+
+		/* MSS L4LEN IDX */
+		mss_l4len_idx |=
+		    (skb_shinfo(skb)->gso_size << IXGBE_ADVTXD_MSS_SHIFT);
+		mss_l4len_idx |= (l4len << IXGBE_ADVTXD_L4LEN_SHIFT);
+		context_desc->mss_l4len_idx = cpu_to_le32(mss_l4len_idx);
+
+		tx_buffer_info->time_stamp = jiffies;
+		tx_buffer_info->next_to_watch = i;
+
+		i++;
+		if (i == tx_ring->count)
+			i = 0;
+		tx_ring->next_to_use = i;
+
+		return true;
+	}
+	return false;
+}
+
+static bool ixgbe_tx_csum(struct ixgbe_adapter *adapter,
+				   struct ixgbe_ring *tx_ring,
+				   struct sk_buff *skb, u32 tx_flags)
+{
+	struct ixgbe_adv_tx_context_desc *context_desc;
+	unsigned int i;
+	struct ixgbe_tx_buffer *tx_buffer_info;
+	u32 vlan_macip_lens = 0, type_tucmd_mlhl = 0;
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL ||
+	    (tx_flags & IXGBE_TX_FLAGS_VLAN)) {
+		i = tx_ring->next_to_use;
+		tx_buffer_info = &tx_ring->tx_buffer_info[i];
+		context_desc = IXGBE_TX_CTXTDESC_ADV(*tx_ring, i);
+
+		if (tx_flags & IXGBE_TX_FLAGS_VLAN)
+			vlan_macip_lens |=
+			    (tx_flags & IXGBE_TX_FLAGS_VLAN_MASK);
+		vlan_macip_lens |= (skb_network_offset(skb) <<
+				    IXGBE_ADVTXD_MACLEN_SHIFT);
+		if (skb->ip_summed == CHECKSUM_PARTIAL)
+			vlan_macip_lens |= (skb_transport_header(skb) -
+					    skb_network_header(skb));
+
+		context_desc->vlan_macip_lens = cpu_to_le32(vlan_macip_lens);
+		context_desc->seqnum_seed = 0;
+
+		type_tucmd_mlhl |= (IXGBE_TXD_CMD_DEXT |
+				    IXGBE_ADVTXD_DTYP_CTXT);
+
+		if (skb->ip_summed == CHECKSUM_PARTIAL) {
+			if (skb->protocol == ntohs(ETH_P_IP))
+				type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_IPV4;
+
+			if (skb->sk->sk_protocol == IPPROTO_TCP)
+				type_tucmd_mlhl |= IXGBE_ADVTXD_TUCMD_L4T_TCP;
+		}
+
+		context_desc->type_tucmd_mlhl = cpu_to_le32(type_tucmd_mlhl);
+		context_desc->mss_l4len_idx = 0;
+
+		tx_buffer_info->time_stamp = jiffies;
+		tx_buffer_info->next_to_watch = i;
+		adapter->hw_csum_tx_good++;
+		i++;
+		if (i == tx_ring->count)
+			i = 0;
+		tx_ring->next_to_use = i;
+
+		return true;
+	}
+	return false;
+}
+
+static int ixgbe_tx_map(struct ixgbe_adapter *adapter,
+			struct ixgbe_ring *tx_ring,
+			struct sk_buff *skb, unsigned int first)
+{
+	struct ixgbe_tx_buffer *tx_buffer_info;
+	unsigned int len = skb->len;
+	unsigned int offset = 0, size, count = 0, i;
+	unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
+	unsigned int f;
+
+	len -= skb->data_len;
+
+	i = tx_ring->next_to_use;
+
+	while (len) {
+		tx_buffer_info = &tx_ring->tx_buffer_info[i];
+		size = min(len, (uint)IXGBE_MAX_DATA_PER_TXD);
+
+		tx_buffer_info->length = size;
+		tx_buffer_info->dma = pci_map_single(adapter->pdev,
+						  skb->data + offset,
+						  size, PCI_DMA_TODEVICE);
+		tx_buffer_info->time_stamp = jiffies;
+		tx_buffer_info->next_to_watch = i;
+
+		len -= size;
+		offset += size;
+		count++;
+		i++;
+		if (i == tx_ring->count)
+			i = 0;
+	}
+
+	for (f = 0; f < nr_frags; f++) {
+		struct skb_frag_struct *frag;
+
+		frag = &skb_shinfo(skb)->frags[f];
+		len = frag->size;
+		offset = frag->page_offset;
+
+		while (len) {
+			tx_buffer_info = &tx_ring->tx_buffer_info[i];
+			size = min(len, (uint)IXGBE_MAX_DATA_PER_TXD);
+
+			tx_buffer_info->length = size;
+			tx_buffer_info->dma = pci_map_page(adapter->pdev,
+							frag->page,
+							offset,
+							size, PCI_DMA_TODEVICE);
+			tx_buffer_info->time_stamp = jiffies;
+			tx_buffer_info->next_to_watch = i;
+
+			len -= size;
+			offset += size;
+			count++;
+			i++;
+			if (i == tx_ring->count)
+				i = 0;
+		}
+	}
+	if (i == 0)
+		i = tx_ring->count - 1;
+	else
+		i = i - 1;
+	tx_ring->tx_buffer_info[i].skb = skb;
+	tx_ring->tx_buffer_info[first].next_to_watch = i;
+
+	return count;
+}
+
+static void ixgbe_tx_queue(struct ixgbe_adapter *adapter,
+			       struct ixgbe_ring *tx_ring,
+			       int tx_flags, int count, u32 paylen, u8 hdr_len)
+{
+	union ixgbe_adv_tx_desc *tx_desc = NULL;
+	struct ixgbe_tx_buffer *tx_buffer_info;
+	u32 olinfo_status = 0, cmd_type_len = 0;
+	unsigned int i;
+	u32 txd_cmd = IXGBE_TXD_CMD_EOP | IXGBE_TXD_CMD_RS | IXGBE_TXD_CMD_IFCS;
+
+	cmd_type_len |= IXGBE_ADVTXD_DTYP_DATA;
+
+	cmd_type_len |= IXGBE_ADVTXD_DCMD_IFCS | IXGBE_ADVTXD_DCMD_DEXT;
+
+	if (tx_flags & IXGBE_TX_FLAGS_VLAN)
+		cmd_type_len |= IXGBE_ADVTXD_DCMD_VLE;
+
+	if (tx_flags & IXGBE_TX_FLAGS_TSO) {
+		cmd_type_len |= IXGBE_ADVTXD_DCMD_TSE;
+
+		olinfo_status |= IXGBE_TXD_POPTS_TXSM <<
+						IXGBE_ADVTXD_POPTS_SHIFT;
+
+		if (tx_flags & IXGBE_TX_FLAGS_IPV4)
+			olinfo_status |= IXGBE_TXD_POPTS_IXSM <<
+						IXGBE_ADVTXD_POPTS_SHIFT;
+
+	} else if (tx_flags & IXGBE_TX_FLAGS_CSUM)
+		olinfo_status |= IXGBE_TXD_POPTS_TXSM <<
+						IXGBE_ADVTXD_POPTS_SHIFT;
+
+	olinfo_status |= ((paylen - hdr_len) << IXGBE_ADVTXD_PAYLEN_SHIFT);
+
+	i = tx_ring->next_to_use;
+	while (count--) {
+		tx_buffer_info = &tx_ring->tx_buffer_info[i];
+		tx_desc = IXGBE_TX_DESC_ADV(*tx_ring, i);
+		tx_desc->read.buffer_addr = cpu_to_le64(tx_buffer_info->dma);
+		tx_desc->read.cmd_type_len =
+			cpu_to_le32(cmd_type_len | tx_buffer_info->length);
+		tx_desc->read.olinfo_status = cpu_to_le32(olinfo_status);
+
+		i++;
+		if (i == tx_ring->count)
+			i = 0;
+	}
+
+	tx_desc->read.cmd_type_len |= cpu_to_le32(txd_cmd);
+
+	/*
+	 * Force memory writes to complete before letting h/w
+	 * know there are new descriptors to fetch.  (Only
+	 * applicable for weak-ordered memory model archs,
+	 * such as IA-64).
+	 */
+	wmb();
+
+	tx_ring->next_to_use = i;
+	writel(i, adapter->hw.hw_addr + tx_ring->tail);
+}
+
+static int ixgbe_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct ixgbe_ring *tx_ring;
+	unsigned int len = skb->len;
+	unsigned int first;
+	unsigned int tx_flags = 0;
+	unsigned long flags = 0;
+	u8 hdr_len;
+	int tso;
+	unsigned int mss = 0;
+	int count = 0;
+	unsigned int f;
+	unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
+	len -= skb->data_len;
+
+	tx_ring = adapter->tx_ring;
+
+	if (skb->len <= 0) {
+		dev_kfree_skb(skb);
+		return NETDEV_TX_OK;
+	}
+	mss = skb_shinfo(skb)->gso_size;
+
+	if (mss)
+		count++;
+	else if (skb->ip_summed == CHECKSUM_PARTIAL)
+		count++;
+
+	count += TXD_USE_COUNT(len);
+	for (f = 0; f < nr_frags; f++)
+		count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size);
+
+	spin_lock_irqsave(&tx_ring->tx_lock, flags);
+	if (IXGBE_DESC_UNUSED(tx_ring) < (count + 2)) {
+		adapter->tx_busy++;
+		netif_stop_queue(netdev);
+		spin_unlock_irqrestore(&tx_ring->tx_lock, flags);
+		return NETDEV_TX_BUSY;
+	}
+	spin_unlock_irqrestore(&tx_ring->tx_lock, flags);
+	if (adapter->vlgrp && vlan_tx_tag_present(skb)) {
+		tx_flags |= IXGBE_TX_FLAGS_VLAN;
+		tx_flags |= (vlan_tx_tag_get(skb) << IXGBE_TX_FLAGS_VLAN_SHIFT);
+	}
+
+	if (skb->protocol == ntohs(ETH_P_IP))
+		tx_flags |= IXGBE_TX_FLAGS_IPV4;
+	first = tx_ring->next_to_use;
+	tso = ixgbe_tso(adapter, tx_ring, skb, tx_flags, &hdr_len);
+	if (tso < 0) {
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
+	}
+
+	if (tso)
+		tx_flags |= IXGBE_TX_FLAGS_TSO;
+	else if (ixgbe_tx_csum(adapter, tx_ring, skb, tx_flags) &&
+		 (skb->ip_summed == CHECKSUM_PARTIAL))
+		tx_flags |= IXGBE_TX_FLAGS_CSUM;
+
+	ixgbe_tx_queue(adapter, tx_ring, tx_flags,
+			   ixgbe_tx_map(adapter, tx_ring, skb, first),
+			   skb->len, hdr_len);
+
+	netdev->trans_start = jiffies;
+
+	spin_lock_irqsave(&tx_ring->tx_lock, flags);
+	/* Make sure there is space in the ring for the next send. */
+	if (IXGBE_DESC_UNUSED(tx_ring) < DESC_NEEDED)
+		netif_stop_queue(netdev);
+	spin_unlock_irqrestore(&tx_ring->tx_lock, flags);
+
+	return NETDEV_TX_OK;
+}
+
+/**
+ * ixgbe_get_stats - Get System Network Statistics
+ * @netdev: network interface device structure
+ *
+ * Returns the address of the device statistics structure.
+ * The statistics are actually updated from the timer callback.
+ **/
+static struct net_device_stats *ixgbe_get_stats(struct net_device *netdev)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	/* only return the current stats */
+	return &adapter->net_stats;
+}
+
+/**
+ * ixgbe_set_mac - Change the Ethernet Address of the NIC
+ * @netdev: network interface device structure
+ * @p: pointer to an address structure
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int ixgbe_set_mac(struct net_device *netdev, void *p)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+	struct sockaddr *addr = p;
+
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+	memcpy(adapter->hw.mac.addr, addr->sa_data, netdev->addr_len);
+
+	ixgbe_set_rar(&adapter->hw, 0, adapter->hw.mac.addr, 0, IXGBE_RAH_AV);
+
+	return 0;
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+/*
+ * Polling 'interrupt' - used by things like netconsole to send skbs
+ * without having to re-enable interrupts. It's not called while
+ * the interrupt routine is executing.
+ */
+static void ixgbe_netpoll(struct net_device *netdev)
+{
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	disable_irq(adapter->pdev->irq);
+	adapter->flags |= IXGBE_FLAG_IN_NETPOLL;
+	ixgbe_intr(adapter->pdev->irq, netdev);
+	adapter->flags &= ~IXGBE_FLAG_IN_NETPOLL;
+	enable_irq(adapter->pdev->irq);
+}
+#endif
+
+/**
+ * ixgbe_probe - Device Initialization Routine
+ * @pdev: PCI device information struct
+ * @ent: entry in ixgbe_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ *
+ * ixgbe_probe initializes an adapter identified by a pci_dev structure.
+ * The OS initialization, configuring of the adapter private structure,
+ * and a hardware reset occur.
+ **/
+static int __devinit ixgbe_probe(struct pci_dev *pdev,
+				 const struct pci_device_id *ent)
+{
+	struct net_device *netdev;
+	struct ixgbe_adapter *adapter = NULL;
+	unsigned long mmio_start, mmio_len;
+	static int cards_found;
+	int i, err, pci_using_dac;
+	struct ixgbe_hw *hw;
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+
+	if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK) &&
+	    !pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK)) {
+		pci_using_dac = 1;
+	} else {
+		err = pci_set_dma_mask(pdev, DMA_32BIT_MASK);
+		if (err) {
+			err = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK);
+			if (err) {
+				IXGBE_ERR("No usable DMA configuration,"
+					  "aborting\n");
+				goto err_dma;
+			}
+		}
+		pci_using_dac = 0;
+	}
+
+	err = pci_request_regions(pdev, ixgbe_driver_name);
+	if (err) {
+		IXGBE_ERR("pci_request_regions failed 0x%x\n", err);
+		goto err_pci_reg;
+	}
+
+	pci_set_master(pdev);
+
+	netdev = alloc_etherdev(sizeof(struct ixgbe_adapter));
+	if (!netdev) {
+		err = -ENOMEM;
+		goto err_alloc_etherdev;
+	}
+
+	SET_MODULE_OWNER(netdev);
+	SET_NETDEV_DEV(netdev, &pdev->dev);
+
+	pci_set_drvdata(pdev, netdev);
+	adapter = netdev_priv(netdev);
+
+	adapter->netdev = netdev;
+	adapter->pdev = pdev;
+	hw = &adapter->hw;
+	hw->back = adapter;
+	adapter->msg_enable = (1 << DEFAULT_DEBUG_LEVEL_SHIFT) - 1;
+
+	mmio_start = pci_resource_start(pdev, 0);
+	mmio_len = pci_resource_len(pdev, 0);
+
+	hw->hw_addr = ioremap(mmio_start, mmio_len);
+	if (!hw->hw_addr) {
+		err = -EIO;
+		goto err_ioremap;
+	}
+
+	for (i = 1; i <= 5; i++) {
+		if (pci_resource_len(pdev, i) == 0)
+			continue;
+	}
+
+	netdev->open = &ixgbe_open;
+	netdev->stop = &ixgbe_close;
+	netdev->hard_start_xmit = &ixgbe_xmit_frame;
+	netdev->get_stats = &ixgbe_get_stats;
+	netdev->set_multicast_list = &ixgbe_set_multi;
+	netdev->set_mac_address = &ixgbe_set_mac;
+	netdev->change_mtu = &ixgbe_change_mtu;
+	ixgbe_set_ethtool_ops(netdev);
+	netdev->tx_timeout = &ixgbe_tx_timeout;
+	netdev->watchdog_timeo = 5 * HZ;
+	netdev->poll = &ixgbe_clean;
+	netdev->weight = 64;
+	netdev->vlan_rx_register = ixgbe_vlan_rx_register;
+	netdev->vlan_rx_add_vid = ixgbe_vlan_rx_add_vid;
+	netdev->vlan_rx_kill_vid = ixgbe_vlan_rx_kill_vid;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	netdev->poll_controller = ixgbe_netpoll;
+#endif
+	strcpy(netdev->name, pci_name(pdev));
+
+	netdev->mem_start = mmio_start;
+	netdev->mem_end = mmio_start + mmio_len;
+
+	adapter->bd_number = cards_found;
+
+	/* setup the private structure */
+	err = ixgbe_sw_init(adapter);
+	if (err)
+		goto err_sw_init;
+
+	netdev->features = NETIF_F_SG |
+			   NETIF_F_HW_CSUM |
+			   NETIF_F_HW_VLAN_TX |
+			   NETIF_F_HW_VLAN_RX |
+			   NETIF_F_HW_VLAN_FILTER;
+
+	netdev->features |= NETIF_F_TSO;
+
+	netdev->features |= NETIF_F_TSO6;
+	if (pci_using_dac)
+		netdev->features |= NETIF_F_HIGHDMA;
+
+
+	/* make sure the EEPROM is good */
+	if (ixgbe_validate_eeprom_checksum(hw, NULL) < 0) {
+		DPRINTK(PROBE, ERR, "The EEPROM Checksum Is Not Valid\n");
+		err = -EIO;
+		goto err_eeprom;
+	}
+
+	memcpy(netdev->dev_addr, hw->mac.perm_addr, netdev->addr_len);
+	memcpy(netdev->perm_addr, hw->mac.perm_addr, netdev->addr_len);
+
+	if (ixgbe_validate_mac_addr(netdev->perm_addr)) {
+		err = -EIO;
+		goto err_eeprom;
+	}
+
+	ixgbe_get_bus_info(hw);
+
+	init_timer(&adapter->watchdog_timer);
+	adapter->watchdog_timer.function = &ixgbe_watchdog;
+	adapter->watchdog_timer.data = (unsigned long)adapter;
+
+	INIT_WORK(&adapter->reset_task, ixgbe_reset_task);
+
+	/* initialize default flow control settings */
+	hw->fc.original_type = ixgbe_fc_full;
+	hw->fc.type = ixgbe_fc_full;
+	hw->fc.high_water = DEFAULT_FCRTH;
+	hw->fc.low_water = DEFAULT_FCRTL;
+	hw->fc.pause_time = DEFAULT_FCPAUSE;
+
+	/* interrupt throttling */
+	adapter->rx_eitr = (1000000 / IXGBE_ITR_DEFAULT_USECS);
+	adapter->tx_eitr = (1000000 / IXGBE_ITR_DEFAULT_USECS);
+
+	/* print bus type/speed/width info */
+	DPRINTK(PROBE, INFO, "(PCI Express:%s:%s) \n",
+		((hw->bus.speed == ixgbe_bus_speed_2500) ? "2.5Gb/s":"Unknown"),
+		 (hw->bus.width == ixgbe_bus_width_pcie_x8) ? "Width x8" :
+		 (hw->bus.width == ixgbe_bus_width_pcie_x4) ? "Width x4" :
+		 (hw->bus.width == ixgbe_bus_width_pcie_x1) ? "Width x1" :
+		 ("Unknown"));
+
+	/* reset the hardware with the new settings */
+	ixgbe_reset(adapter);
+
+	netif_carrier_off(netdev);
+	netif_stop_queue(netdev);
+	netif_poll_disable(netdev);
+
+	strcpy(netdev->name, "eth%d");
+	err = register_netdev(netdev);
+	if (err)
+		goto err_register;
+
+
+	cards_found++;
+	return 0;
+
+err_register:
+err_sw_init:
+err_eeprom:
+	iounmap(hw->hw_addr);
+err_ioremap:
+	free_netdev(netdev);
+err_alloc_etherdev:
+	pci_release_regions(pdev);
+err_pci_reg:
+err_dma:
+	pci_disable_device(pdev);
+	return err;
+}
+
+/**
+ * ixgbe_remove - Device Removal Routine
+ * @pdev: PCI device information struct
+ *
+ * ixgbe_remove is called by the PCI subsystem to alert the driver
+ * that it should release a PCI device.  The could be caused by a
+ * Hot-Plug event, or because the driver is going to be removed from
+ * memory.
+ **/
+static void __devexit ixgbe_remove(struct pci_dev *pdev)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+	unregister_netdev(netdev);
+
+	kfree(adapter->tx_ring);
+	kfree(adapter->rx_ring);
+
+	iounmap(adapter->hw.hw_addr);
+	pci_release_regions(pdev);
+
+	free_netdev(netdev);
+}
+
+u16 ixgbe_read_pci_cfg_word(struct ixgbe_hw *hw, u32 reg)
+{
+	u16 value;
+	struct ixgbe_adapter *adapter = hw->back;
+
+	pci_read_config_word(adapter->pdev, reg, &value);
+	return value;
+}
+
+/**
+ * ixgbe_io_error_detected - called when PCI error is detected
+ * @pdev: Pointer to PCI device
+ * @state: The current pci connection state
+ *
+ * This function is called after a PCI bus error affecting
+ * this device has been detected.
+ */
+static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
+						pci_channel_state_t state)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct ixgbe_adapter *adapter = netdev->priv;
+
+	netif_device_detach(netdev);
+
+	if (netif_running(netdev))
+		ixgbe_down(adapter);
+	pci_disable_device(pdev);
+
+	/* Request a slot slot reset. */
+	return PCI_ERS_RESULT_NEED_RESET;
+}
+
+/**
+ * ixgbe_io_slot_reset - called after the pci bus has been reset.
+ * @pdev: Pointer to PCI device
+ *
+ * Restart the card from scratch, as if from a cold-boot.
+ */
+static pci_ers_result_t ixgbe_io_slot_reset(struct pci_dev *pdev)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct ixgbe_adapter *adapter = netdev->priv;
+
+	if (pci_enable_device(pdev)) {
+		DPRINTK(PROBE, ERR,
+			"Cannot re-enable PCI device after reset.\n");
+		return PCI_ERS_RESULT_DISCONNECT;
+	}
+	pci_set_master(pdev);
+
+	pci_enable_wake(pdev, PCI_D3hot, 0);
+	pci_enable_wake(pdev, PCI_D3cold, 0);
+
+	ixgbe_reset(adapter);
+
+	return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * ixgbe_io_resume - called when traffic can start flowing again.
+ * @pdev: Pointer to PCI device
+ *
+ * This callback is called when the error recovery driver tells us that
+ * its OK to resume normal operation.
+ */
+static void ixgbe_io_resume(struct pci_dev *pdev)
+{
+	struct net_device *netdev = pci_get_drvdata(pdev);
+	struct ixgbe_adapter *adapter = netdev->priv;
+
+	if (netif_running(netdev)) {
+		if (ixgbe_up(adapter)) {
+			DPRINTK(PROBE, INFO, "ixgbe_up failed after reset\n");
+			return;
+		}
+	}
+
+	netif_device_attach(netdev);
+
+}
+
+static struct pci_error_handlers ixgbe_err_handler = {
+	.error_detected = ixgbe_io_error_detected,
+	.slot_reset = ixgbe_io_slot_reset,
+	.resume = ixgbe_io_resume,
+};
+
+static struct pci_driver ixgbe_driver = {
+	.name     = ixgbe_driver_name,
+	.id_table = ixgbe_pci_tbl,
+	.probe    = ixgbe_probe,
+	.remove   = __devexit_p(ixgbe_remove),
+#ifdef CONFIG_PM
+	.suspend  = ixgbe_suspend,
+	.resume   = ixgbe_resume,
+#endif
+	.shutdown = ixgbe_shutdown,
+	.err_handler = &ixgbe_err_handler
+};
+
+/**
+ * ixgbe_init_module - Driver Registration Routine
+ *
+ * ixgbe_init_module is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ **/
+static int __init ixgbe_init_module(void)
+{
+	int ret;
+	printk(KERN_INFO "%s - version %s\n", ixgbe_driver_string,
+	       ixgbe_driver_version);
+
+	printk(KERN_INFO "%s\n", ixgbe_copyright);
+
+	ret = pci_register_driver(&ixgbe_driver);
+	return ret;
+}
+module_init(ixgbe_init_module);
+
+/**
+ * ixgbe_exit_module - Driver Exit Cleanup Routine
+ *
+ * ixgbe_exit_module is called just before the driver is removed
+ * from memory.
+ **/
+static void __exit ixgbe_exit_module(void)
+{
+	pci_unregister_driver(&ixgbe_driver);
+}
+module_exit(ixgbe_exit_module);
+
+/* ixgbe_main.c */
diff --git a/drivers/net/ixgbe/ixgbe_osdep.h b/drivers/net/ixgbe/ixgbe_osdep.h
new file mode 100644
index 0000000..1f81c93
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_osdep.h
@@ -0,0 +1,69 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+
+/* glue for the OS independent part of ixgbe
+ * includes register access macros
+ */
+
+#ifndef _IXGBE_OSDEP_H_
+#define _IXGBE_OSDEP_H_
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+
+#include <linux/sched.h>
+
+#define DEBUGOUT(S)             do {} while (0)
+#define DEBUGOUT1(S, A...)      do {} while (0)
+
+#define DEBUGFUNC(F) DEBUGOUT(F)
+#define DEBUGOUT2 DEBUGOUT1
+#define DEBUGOUT3 DEBUGOUT2
+#define DEBUGOUT6 DEBUGOUT3
+#define DEBUGOUT7 DEBUGOUT6
+
+#define IXGBE_WRITE_REG(a, reg, value) writel((value), ((a)->hw_addr + (reg)))
+
+#define IXGBE_READ_REG(a, reg) readl((a)->hw_addr + (reg))
+
+#define IXGBE_WRITE_REG_ARRAY(a, reg, offset, value) ( \
+    writel((value), ((a)->hw_addr + (reg) + ((offset) << 2))))
+
+#define IXGBE_READ_REG_ARRAY(a, reg, offset) ( \
+    readl((a)->hw_addr + (reg) + ((offset) << 2)))
+
+#define IXGBE_WRITE_FLUSH(a) IXGBE_READ_REG(a, IXGBE_STATUS)
+struct ixgbe_hw;
+extern u16 ixgbe_read_pci_cfg_word(struct ixgbe_hw *hw, u32 reg);
+#define IXGBE_READ_PCIE_WORD ixgbe_read_pci_cfg_word
+#define IXGBE_EEPROM_GRANT_ATTEMPS 100
+
+#endif /* _IXGBE_OSDEP_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_phy.c b/drivers/net/ixgbe/ixgbe_phy.c
new file mode 100644
index 0000000..b06235b
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_phy.c
@@ -0,0 +1,601 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#include "ixgbe_api.h"
+#include "ixgbe_common.h"
+#include "ixgbe_phy.h"
+
+static s32 ixgbe_get_phy_id(struct ixgbe_hw *hw);
+static s32 ixgbe_assign_func_pointers_phy(struct ixgbe_hw *hw);
+static bool ixgbe_validate_phy_addr(struct ixgbe_hw *hw, u32 phy_addr);
+static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id);
+static s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw);
+static s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
+				      u32 device_type, u16 *phy_data);
+static s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
+				       u32 device_type, u16 phy_data);
+
+/* PHY specific */
+static s32 ixgbe_setup_tnx_phy_link(struct ixgbe_hw *hw);
+static s32 ixgbe_check_tnx_phy_link(struct ixgbe_hw *hw, u32 *speed,
+				    bool *link_up);
+static s32 ixgbe_setup_tnx_phy_link_speed(struct ixgbe_hw *hw, u32 speed,
+					  bool autoneg,
+					  bool autoneg_wait_to_complete);
+/**
+ *  ixgbe_init_shared_code_phy - Initialize PHY shared code
+ *  @hw: pointer to hardware structure
+ **/
+s32 ixgbe_init_shared_code_phy(struct ixgbe_hw *hw)
+{
+	/* Assign function pointers */
+	ixgbe_assign_func_pointers_phy(hw);
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_assign_func_pointers_phy -  Assigns PHY-specific function pointers
+ *  @hw: pointer to hardware structure
+ *
+ *  Note, generic function pointers have already been assigned, so the
+ *  function pointers set here are only for PHY-specific functions.
+ **/
+static s32 ixgbe_assign_func_pointers_phy(struct ixgbe_hw *hw)
+{
+	hw->func.ixgbe_func_reset_phy =
+			    &ixgbe_reset_phy_generic;
+	hw->func.ixgbe_func_read_phy_reg =
+			    &ixgbe_read_phy_reg_generic;
+	hw->func.ixgbe_func_write_phy_reg =
+			    &ixgbe_write_phy_reg_generic;
+	hw->func.ixgbe_func_identify_phy =
+			    &ixgbe_identify_phy_generic;
+
+	if (ixgbe_get_media_type(hw) == ixgbe_media_type_copper) {
+		/* Call PHY identify routine to get the phy type */
+		ixgbe_identify_phy(hw);
+
+		switch (hw->phy.type) {
+		case ixgbe_phy_tn:
+			hw->func.ixgbe_func_setup_phy_link =
+					&ixgbe_setup_tnx_phy_link;
+			hw->func.ixgbe_func_check_phy_link =
+					&ixgbe_check_tnx_phy_link;
+			hw->func.ixgbe_func_setup_phy_link_speed =
+					&ixgbe_setup_tnx_phy_link_speed;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return IXGBE_SUCCESS;
+}
+
+/**
+ *  ixgbe_identify_phy_generic - Get physical layer module
+ *  @hw: pointer to hardware structure
+ *
+ *  Determines the physical layer module found on the current adapter.
+ **/
+static s32 ixgbe_identify_phy_generic(struct ixgbe_hw *hw)
+{
+	s32 status = IXGBE_ERR_PHY_ADDR_INVALID;
+	u32 phy_addr;
+
+	for (phy_addr = 0; phy_addr < IXGBE_MAX_PHY_ADDR; phy_addr++) {
+		if (ixgbe_validate_phy_addr(hw, phy_addr)) {
+			hw->phy.addr = phy_addr;
+			ixgbe_get_phy_id(hw);
+			hw->phy.type = ixgbe_get_phy_type_from_id(hw->phy.id);
+			status = IXGBE_SUCCESS;
+			break;
+		}
+	}
+	return status;
+}
+
+/**
+ *  ixgbe_validate_phy_addr - Determines phy address is valid
+ *  @hw: pointer to hardware structure
+ *
+ **/
+static bool ixgbe_validate_phy_addr(struct ixgbe_hw *hw, u32 phy_addr)
+{
+	u16 phy_id = 0;
+	bool valid = false;
+
+	hw->phy.addr = phy_addr;
+	ixgbe_read_phy_reg_generic(hw,
+				   IXGBE_MDIO_PHY_ID_HIGH,
+				   IXGBE_MDIO_PMA_PMD_DEV_TYPE,
+				   &phy_id);
+
+	if (phy_id != 0xFFFF && phy_id != 0x0)
+		valid = true;
+
+	return valid;
+}
+
+/**
+ *  ixgbe_get_phy_id - Get the phy type
+ *  @hw: pointer to hardware structure
+ *
+ **/
+static s32 ixgbe_get_phy_id(struct ixgbe_hw *hw)
+{
+	u32 status;
+	u16 phy_id_high = 0;
+	u16 phy_id_low = 0;
+
+	status = ixgbe_read_phy_reg_generic(hw,
+				   IXGBE_MDIO_PHY_ID_HIGH,
+				   IXGBE_MDIO_PMA_PMD_DEV_TYPE,
+				   &phy_id_high);
+
+	if (status == IXGBE_SUCCESS) {
+		hw->phy.id = (u32)(phy_id_high << 16);
+		status = ixgbe_read_phy_reg_generic(hw,
+					   IXGBE_MDIO_PHY_ID_LOW,
+					   IXGBE_MDIO_PMA_PMD_DEV_TYPE,
+					   &phy_id_low);
+		hw->phy.id |= (u32)(phy_id_low & IXGBE_PHY_REVISION_MASK);
+		hw->phy.revision = (u32)(phy_id_low & ~IXGBE_PHY_REVISION_MASK);
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_get_phy_type_from_id - Get the phy type
+ *  @hw: pointer to hardware structure
+ *
+ **/
+static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id)
+{
+	enum ixgbe_phy_type phy_type;
+
+	switch (phy_id) {
+	case TN1010_PHY_ID:
+		phy_type = ixgbe_phy_tn;
+		break;
+
+	case QT2022_PHY_ID:
+		phy_type = ixgbe_phy_qt;
+		break;
+
+	default:
+		phy_type = ixgbe_phy_unknown;
+		break;
+	}
+
+	return phy_type;
+}
+
+/**
+ *  ixgbe_reset_phy_generic - Performs a PHY reset
+ *  @hw: pointer to hardware structure
+ **/
+static s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw)
+{
+	/*
+	 * Perform soft PHY reset to the PHY_XS.
+	 * This will cause a soft reset to the PHY
+	 */
+	return ixgbe_write_phy_reg(hw, IXGBE_MDIO_PHY_XS_CONTROL,
+				   IXGBE_MDIO_PHY_XS_DEV_TYPE,
+				   IXGBE_MDIO_PHY_XS_RESET);
+}
+
+/**
+ *  ixgbe_read_phy_reg_generic - Reads a value from a specified PHY register
+ *  @hw: pointer to hardware structure
+ *  @reg_addr: 32 bit address of PHY register to read
+ *  @phy_data: Pointer to read data from PHY register
+ **/
+static s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
+				      u32 device_type, u16 *phy_data)
+{
+	u32 command;
+	u32 i;
+	u32 timeout = 10;
+	u32 data;
+	s32 status = IXGBE_SUCCESS;
+	u16 gssr;
+
+	if (IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_LAN_ID_1)
+		gssr = IXGBE_GSSR_PHY1_SM;
+	else
+		gssr = IXGBE_GSSR_PHY0_SM;
+
+	if (ixgbe_acquire_swfw_sync(hw, gssr) != IXGBE_SUCCESS)
+		status = IXGBE_ERR_SWFW_SYNC;
+
+	if (status == IXGBE_SUCCESS) {
+		/* Setup and write the address cycle command */
+		command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT)  |
+			   (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
+			   (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
+			   (IXGBE_MSCA_ADDR_CYCLE | IXGBE_MSCA_MDI_COMMAND));
+
+		IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
+
+		/*
+		 * Check every 10 usec to see if the address cycle completed.
+		 * The MDI Command bit will clear when the operation is
+		 * complete
+		 */
+		for (i = 0; i < timeout; i++) {
+			udelay(10);
+
+			command = IXGBE_READ_REG(hw, IXGBE_MSCA);
+
+			if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) {
+				break;
+			}
+		}
+
+		if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) {
+			DEBUGFUNC("PHY address command did not complete.\n");
+			status = IXGBE_ERR_PHY;
+		}
+
+		if (status == IXGBE_SUCCESS) {
+			/*
+			 * Address cycle complete, setup and write the read
+			 * command
+			 */
+			command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT)  |
+				   (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
+				   (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
+				   (IXGBE_MSCA_READ | IXGBE_MSCA_MDI_COMMAND));
+
+			IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
+
+			/*
+			 * Check every 10 usec to see if the address cycle
+			 * completed. The MDI Command bit will clear when the
+			 * operation is complete
+			 */
+			for (i = 0; i < timeout; i++) {
+				udelay(10);
+
+				command = IXGBE_READ_REG(hw, IXGBE_MSCA);
+
+				if ((command & IXGBE_MSCA_MDI_COMMAND) == 0)
+					break;
+			}
+
+			if ((command & IXGBE_MSCA_MDI_COMMAND) != 0) {
+				DEBUGFUNC("PHY read command didn't complete\n");
+				status = IXGBE_ERR_PHY;
+			} else {
+				/*
+				 * Read operation is complete.  Get the data
+				 * from MSRWD
+				 */
+				data = IXGBE_READ_REG(hw, IXGBE_MSRWD);
+				data >>= IXGBE_MSRWD_READ_DATA_SHIFT;
+				*phy_data = (u16)(data);
+			}
+		}
+
+		ixgbe_release_swfw_sync(hw, gssr);
+	}
+	return status;
+}
+
+/**
+ *  ixgbe_write_phy_reg_generic - Writes a value to specified PHY register
+ *  @hw: pointer to hardware structure
+ *  @reg_addr: 32 bit PHY register to write
+ *  @device_type: 5 bit device type
+ *  @phy_data: Data to write to the PHY register
+ **/
+static s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr,
+				       u32 device_type, u16 phy_data)
+{
+	u32 command;
+	u32 i;
+	u32 timeout = 10;
+	s32 status = IXGBE_SUCCESS;
+	u16 gssr;
+
+	if (IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_LAN_ID_1)
+		gssr = IXGBE_GSSR_PHY1_SM;
+	else
+		gssr = IXGBE_GSSR_PHY0_SM;
+
+	if (ixgbe_acquire_swfw_sync(hw, gssr) != IXGBE_SUCCESS)
+		status = IXGBE_ERR_SWFW_SYNC;
+
+	if (status == IXGBE_SUCCESS) {
+		/* Put the data in the MDI single read and write data register*/
+		IXGBE_WRITE_REG(hw, IXGBE_MSRWD, (u32)phy_data);
+
+		/* Setup and write the address cycle command */
+		command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT)  |
+			   (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
+			   (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
+			   (IXGBE_MSCA_ADDR_CYCLE | IXGBE_MSCA_MDI_COMMAND));
+
+		IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
+
+		/*
+		 * Check every 10 usec to see if the address cycle completed.
+		 * The MDI Command bit will clear when the operation is
+		 * complete
+		 */
+		for (i = 0; i < timeout; i++) {
+			udelay(10);
+
+			command = IXGBE_READ_REG(hw, IXGBE_MSCA);
+
+			if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) {
+				DEBUGFUNC("PHY address cmd didn't complete\n");
+				break;
+			}
+		}
+
+		if ((command & IXGBE_MSCA_MDI_COMMAND) != 0)
+			status = IXGBE_ERR_PHY;
+
+		if (status == IXGBE_SUCCESS) {
+			/*
+			 * Address cycle complete, setup and write the write
+			 * command
+			 */
+			command = ((reg_addr << IXGBE_MSCA_NP_ADDR_SHIFT)  |
+				   (device_type << IXGBE_MSCA_DEV_TYPE_SHIFT) |
+				   (hw->phy.addr << IXGBE_MSCA_PHY_ADDR_SHIFT) |
+				   (IXGBE_MSCA_WRITE | IXGBE_MSCA_MDI_COMMAND));
+
+			IXGBE_WRITE_REG(hw, IXGBE_MSCA, command);
+
+			/*
+			 * Check every 10 usec to see if the address cycle
+			 * completed. The MDI Command bit will clear when the
+			 * operation is complete
+			 */
+			for (i = 0; i < timeout; i++) {
+				udelay(10);
+
+				command = IXGBE_READ_REG(hw, IXGBE_MSCA);
+
+				if ((command & IXGBE_MSCA_MDI_COMMAND) == 0) {
+					DEBUGFUNC("PHY write command did not "
+						  "complete.\n");
+					break;
+				}
+			}
+
+			if ((command & IXGBE_MSCA_MDI_COMMAND) != 0)
+				status = IXGBE_ERR_PHY;
+		}
+
+		ixgbe_release_swfw_sync(hw, gssr);
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_setup_phy_link - Restart PHY autoneg
+ *  @hw: pointer to hardware structure
+ *
+ *  Restart autonegotiation and PHY and waits for completion.
+ **/
+s32 ixgbe_setup_phy_link(struct ixgbe_hw *hw)
+{
+	return ixgbe_call_func(hw, ixgbe_func_setup_phy_link, (hw),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_check_phy_link - Determine link and speed status
+ *  @hw: pointer to hardware structure
+ *
+ *  Reads a PHY register to determine if link is up and the current speed for
+ *  the PHY.
+ **/
+s32 ixgbe_check_phy_link(struct ixgbe_hw *hw, u32 *speed,
+			 bool *link_up)
+{
+	return ixgbe_call_func(hw, ixgbe_func_check_phy_link, (hw, speed,
+			       link_up), IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_setup_phy_link_speed - Set auto advertise
+ *  @hw: pointer to hardware structure
+ *  @speed: new link speed
+ *  @autoneg: true if autonegotiation enabled
+ *
+ *  Sets the auto advertised capabilities
+ **/
+s32 ixgbe_setup_phy_link_speed(struct ixgbe_hw *hw, u32 speed,
+			       bool autoneg,
+			       bool autoneg_wait_to_complete)
+{
+	return ixgbe_call_func(hw, ixgbe_func_setup_phy_link_speed, (hw, speed,
+			       autoneg, autoneg_wait_to_complete),
+			       IXGBE_NOT_IMPLEMENTED);
+}
+
+/**
+ *  ixgbe_setup_tnx_phy_link - Set and restart autoneg
+ *  @hw: pointer to hardware structure
+ *
+ *  Restart autonegotiation and PHY and waits for completion.
+ **/
+static s32 ixgbe_setup_tnx_phy_link(struct ixgbe_hw *hw)
+{
+	s32 status = IXGBE_NOT_IMPLEMENTED;
+	u32 time_out;
+	u32 max_time_out = 10;
+	u16 autoneg_speed_selection_register = 0x10;
+	u16 autoneg_restart_mask = 0x0200;
+	u16 autoneg_complete_mask = 0x0020;
+	u16 autoneg_reg = 0;
+
+	/*
+	 * Set advertisement settings in PHY based on autoneg_advertised
+	 * settings. If autoneg_advertised = 0, then advertise default values
+	 * txn devices cannot be "forced" to a autoneg 10G and fail.  But can
+	 * for a 1G.
+	 */
+	ixgbe_read_phy_reg(hw,
+		  autoneg_speed_selection_register,
+		  IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+		  &autoneg_reg);
+
+	if (hw->phy.autoneg_advertised == IXGBE_LINK_SPEED_1GB_FULL)
+		autoneg_reg &= 0xEFFF; /* 0 in bit 12 is 1G operation */
+	else
+		autoneg_reg |= 0x1000; /* 1 in bit 12 is 10G/1G operation */
+
+	ixgbe_write_phy_reg(hw,
+		  autoneg_speed_selection_register,
+		  IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+		  autoneg_reg);
+
+
+	/* Restart PHY autonegotiation and wait for completion */
+	ixgbe_read_phy_reg(hw,
+		  IXGBE_MDIO_AUTO_NEG_CONTROL,
+		  IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+		  &autoneg_reg);
+
+	autoneg_reg |= autoneg_restart_mask;
+
+	ixgbe_write_phy_reg(hw,
+		  IXGBE_MDIO_AUTO_NEG_CONTROL,
+		  IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+		  autoneg_reg);
+
+	/* Wait for autonegotiation to finish */
+	for (time_out = 0; time_out < max_time_out; time_out++) {
+		udelay(10);
+		/* Restart PHY autonegotiation and wait for completion */
+		status = ixgbe_read_phy_reg(hw,
+					    IXGBE_MDIO_AUTO_NEG_STATUS,
+					    IXGBE_MDIO_AUTO_NEG_DEV_TYPE,
+					    &autoneg_reg);
+
+		autoneg_reg &= autoneg_complete_mask;
+		if (autoneg_reg == autoneg_complete_mask) {
+			status = IXGBE_SUCCESS;
+			break;
+		}
+	}
+
+	if (time_out == max_time_out)
+		status = IXGBE_ERR_LINK_SETUP;
+
+	return status;
+}
+
+/**
+ *  ixgbe_check_tnx_phy_link - Determine link and speed status
+ *  @hw: pointer to hardware structure
+ *
+ *  Reads the VS1 register to determine if link is up and the current speed for
+ *  the PHY.
+ **/
+static s32 ixgbe_check_tnx_phy_link(struct ixgbe_hw *hw, u32 *speed,
+				    bool *link_up)
+{
+	s32 status = IXGBE_SUCCESS;
+	u32 time_out;
+	u32 max_time_out = 10;
+	u16 phy_link = 0;
+	u16 phy_speed = 0;
+	u16 phy_data = 0;
+
+	/* Initialize speed and link to default case */
+	*link_up = false;
+	*speed = IXGBE_LINK_SPEED_10GB_FULL;
+
+	/*
+	 * Check current speed and link status of the PHY register.
+	 * This is a vendor specific register and may have to
+	 * be changed for other copper PHYs.
+	 */
+	for (time_out = 0; time_out < max_time_out; time_out++) {
+		udelay(10);
+		if (phy_link == IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS) {
+			*link_up = true;
+			if (phy_speed ==
+			    IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS)
+				*speed = IXGBE_LINK_SPEED_1GB_FULL;
+			break;
+		} else {
+			status = ixgbe_read_phy_reg(hw,
+				     IXGBE_MDIO_VENDOR_SPECIFIC_1_STATUS,
+				     IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE,
+				     &phy_data);
+			phy_link = phy_data &
+				IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS;
+			phy_speed = phy_data &
+				IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS;
+		}
+	}
+
+	return status;
+}
+
+/**
+ *  ixgbe_setup_tnx_phy_link_speed - Sets the auto advertised capabilities
+ *  @hw: pointer to hardware structure
+ *  @speed: new link speed
+ *  @autoneg: true if autonegotiation enabled
+ **/
+static s32 ixgbe_setup_tnx_phy_link_speed(struct ixgbe_hw *hw, u32 speed,
+					  bool autoneg,
+					  bool autoneg_wait_to_complete)
+{
+	/*
+	 * Clear autoneg_advertised and set new values based on input link
+	 * speed.
+	 */
+	hw->phy.autoneg_advertised = 0;
+
+	if (speed & IXGBE_LINK_SPEED_10GB_FULL) {
+		hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_10GB_FULL;
+	}
+	if (speed & IXGBE_LINK_SPEED_1GB_FULL) {
+		hw->phy.autoneg_advertised |= IXGBE_LINK_SPEED_1GB_FULL;
+	}
+
+	/* Setup link based on the new speed settings */
+	ixgbe_setup_tnx_phy_link(hw);
+
+	return IXGBE_SUCCESS;
+}
diff --git a/drivers/net/ixgbe/ixgbe_phy.h b/drivers/net/ixgbe/ixgbe_phy.h
new file mode 100644
index 0000000..ada510a
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_phy.h
@@ -0,0 +1,40 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _IXGBE_PHY_H_
+#define _IXGBE_PHY_H_
+
+#include "ixgbe_type.h"
+
+s32 ixgbe_init_shared_code_phy(struct ixgbe_hw *hw);
+s32 ixgbe_setup_phy_link(struct ixgbe_hw *hw);
+s32 ixgbe_check_phy_link(struct ixgbe_hw *hw, u32 *speed, bool *link_up);
+s32 ixgbe_setup_phy_link_speed(struct ixgbe_hw *hw, u32 speed, bool autoneg,
+			       bool autoneg_wait_to_complete);
+
+#endif /* _IXGBE_PHY_H_ */
diff --git a/drivers/net/ixgbe/ixgbe_type.h b/drivers/net/ixgbe/ixgbe_type.h
new file mode 100644
index 0000000..0d7db6c
--- /dev/null
+++ b/drivers/net/ixgbe/ixgbe_type.h
@@ -0,0 +1,1390 @@
+/*******************************************************************************
+
+  Intel PRO/10GbE PCI Express Linux driver
+  Copyright(c) 1999 - 2007 Intel Corporation.
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms and conditions of the GNU General Public License,
+  version 2, as published by the Free Software Foundation.
+
+  This program is distributed in the hope it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+  The full GNU General Public License is included in this distribution in
+  the file called "COPYING".
+
+  Contact Information:
+  Linux NICS <linux.nics@intel.com>
+  e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
+  Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+
+*******************************************************************************/
+
+#ifndef _IXGBE_TYPE_H_
+#define _IXGBE_TYPE_H_
+
+#include "ixgbe_osdep.h"
+
+/* Vendor ID */
+#define IXGBE_INTEL_VENDOR_ID   0x8086
+
+/* Device IDs */
+#define IXGBE_DEV_ID_82598               0x10B6
+#define IXGBE_DEV_ID_82598AF_DUAL_PORT   0x10C6
+#define IXGBE_DEV_ID_82598AF_SINGLE_PORT 0x10C7
+
+/* General Registers */
+#define IXGBE_CTRL      0x00000
+#define IXGBE_STATUS    0x00008
+#define IXGBE_CTRL_EXT  0x00018
+#define IXGBE_ESDP      0x00020
+#define IXGBE_EODSDP    0x00028
+#define IXGBE_LEDCTL    0x00200
+#define IXGBE_FRTIMER   0x00048
+#define IXGBE_TCPTIMER  0x0004C
+
+/* NVM Registers */
+#define IXGBE_EEC       0x10010
+#define IXGBE_EERD      0x10014
+#define IXGBE_FLA       0x1001C
+#define IXGBE_EEMNGCTL  0x10110
+#define IXGBE_EEMNGDATA 0x10114
+#define IXGBE_FLMNGCTL  0x10118
+#define IXGBE_FLMNGDATA 0x1011C
+#define IXGBE_FLMNGCNT  0x10120
+#define IXGBE_FLOP      0x1013C
+#define IXGBE_GRC       0x10200
+
+/* Interrupt Registers */
+#define IXGBE_EICR      0x00800
+#define IXGBE_EICS      0x00808
+#define IXGBE_EIMS      0x00880
+#define IXGBE_EIMC      0x00888
+#define IXGBE_EIAC      0x00810
+#define IXGBE_EIAM      0x00890
+#define IXGBE_EITR(_i) (0x00820 + ((_i) * 4)) /* 0x820-0x86c */
+#define IXGBE_IVAR(_i) (0x00900 + ((_i) * 4)) /* 24 at 0x900-0x960 */
+#define IXGBE_MSIXT     0x00000 /* MSI-X Table. 0x0000 - 0x01C */
+#define IXGBE_MSIXPBA   0x02000 /* MSI-X Pending bit array */
+#define IXGBE_PBACL     0x11068
+#define IXGBE_GPIE      0x00898
+
+/* Flow Control Registers */
+#define IXGBE_PFCTOP    0x03008
+#define IXGBE_FCTTV(_i) (0x03200 + ((_i) * 4)) /* 4 of these (0-3) */
+#define IXGBE_FCRTL(_i) (0x03220 + ((_i) * 8)) /* 8 of these (0-7) */
+#define IXGBE_FCRTH(_i) (0x03260 + ((_i) * 8)) /* 8 of these (0-7) */
+#define IXGBE_FCRTV     0x032A0
+#define IXGBE_TFCS      0x0CE00
+
+/* Receive DMA Registers */
+#define IXGBE_RDBAL(_i) (0x01000 + ((_i) * 0x40)) /* 64 of each (0-63)*/
+#define IXGBE_RDBAH(_i) (0x01004 + ((_i) * 0x40))
+#define IXGBE_RDLEN(_i) (0x01008 + ((_i) * 0x40))
+#define IXGBE_RDH(_i)   (0x01010 + ((_i) * 0x40))
+#define IXGBE_RDT(_i)   (0x01018 + ((_i) * 0x40))
+#define IXGBE_RXDCTL(_i) (0x01028 + ((_i) * 0x40))
+#define IXGBE_SRRCTL(_i) (0x02100 + ((_i) * 4))
+					     /* array of 16 (0x02100-0x0213C) */
+#define IXGBE_DCA_RXCTRL(_i)    (0x02200 + ((_i) * 4))
+					     /* array of 16 (0x02200-0x0223C) */
+#define IXGBE_RDRXCTL    0x02F00
+#define IXGBE_RXPBSIZE(_i)      (0x03C00 + ((_i) * 4))
+					     /* 8 of these 0x03C00 - 0x03C1C */
+#define IXGBE_RXCTRL    0x03000
+#define IXGBE_DROPEN    0x03D04
+#define IXGBE_RXPBSIZE_SHIFT 10
+
+/* Receive Registers */
+#define IXGBE_RXCSUM    0x05000
+#define IXGBE_RFCTL     0x05008
+#define IXGBE_MTA(_i)   (0x05200 + ((_i) * 4))
+				   /* Multicast Table Array - 128 entries */
+#define IXGBE_RAL(_i)   (0x05400 + ((_i) * 8)) /* 16 of these (0-15) */
+#define IXGBE_RAH(_i)   (0x05404 + ((_i) * 8)) /* 16 of these (0-15) */
+#define IXGBE_PSRTYPE   0x05480
+				   /* 0x5480-0x54BC Packet split receive type */
+#define IXGBE_VFTA(_i)  (0x0A000 + ((_i) * 4))
+					 /* array of 4096 1-bit vlan filters */
+#define IXGBE_VFTAVIND(_j, _i)  (0x0A200 + ((_j) * 0x200) + ((_i) * 4))
+				     /*array of 4096 4-bit vlan vmdq indicies */
+#define IXGBE_FCTRL     0x05080
+#define IXGBE_VLNCTRL   0x05088
+#define IXGBE_MCSTCTRL  0x05090
+#define IXGBE_MRQC      0x05818
+#define IXGBE_VMD_CTL   0x0581C
+#define IXGBE_IMIR(_i)  (0x05A80 + ((_i) * 4))  /* 8 of these (0-7) */
+#define IXGBE_IMIREXT(_i)       (0x05AA0 + ((_i) * 4))  /* 8 of these (0-7) */
+#define IXGBE_IMIRVP    0x05AC0
+#define IXGBE_RETA(_i)  (0x05C00 + ((_i) * 4))  /* 32 of these (0-31) */
+#define IXGBE_RSSRK(_i) (0x05C80 + ((_i) * 4))  /* 10 of these (0-9) */
+
+/* Transmit DMA registers */
+#define IXGBE_TDBAL(_i) (0x06000 + ((_i) * 0x40))/* 32 of these (0-31)*/
+#define IXGBE_TDBAH(_i) (0x06004 + ((_i) * 0x40))
+#define IXGBE_TDLEN(_i) (0x06008 + ((_i) * 0x40))
+#define IXGBE_TDH(_i)   (0x06010 + ((_i) * 0x40))
+#define IXGBE_TDT(_i)   (0x06018 + ((_i) * 0x40))
+#define IXGBE_TXDCTL(_i) (0x06028 + ((_i) * 0x40))
+#define IXGBE_TDWBAL(_i) (0x06038 + ((_i) * 0x40))
+#define IXGBE_TDWBAH(_i) (0x0603C + ((_i) * 0x40))
+#define IXGBE_DTXCTL    0x07E00
+#define IXGBE_DCA_TXCTRL(_i)    (0x07200 + ((_i) * 4))
+					      /* there are 16 of these (0-15) */
+#define IXGBE_TIPG      0x0CB00
+#define IXGBE_TXPBSIZE(_i)      (0x0CC00 + ((_i) *0x04))
+						      /* there are 8 of these */
+#define IXGBE_MNGTXMAP  0x0CD10
+#define IXGBE_TIPG_FIBER_DEFAULT 3
+#define IXGBE_TXPBSIZE_SHIFT    10
+
+/* Wake up registers */
+#define IXGBE_WUC       0x05800
+#define IXGBE_WUFC      0x05808
+#define IXGBE_WUS       0x05810
+#define IXGBE_IPAV      0x05838
+#define IXGBE_IP4AT     0x05840 /* IPv4 table 0x5840-0x5858 */
+#define IXGBE_IP6AT     0x05880 /* IPv6 table 0x5880-0x588F */
+#define IXGBE_WUPL      0x05900
+#define IXGBE_WUPM      0x05A00 /* wake up pkt memory 0x5A00-0x5A7C */
+#define IXGBE_FHFT      0x09000 /* Flex host filter table 9000-93FC */
+
+/* Music registers */
+#define IXGBE_RMCS      0x03D00
+#define IXGBE_DPMCS     0x07F40
+#define IXGBE_PDPMCS    0x0CD00
+#define IXGBE_RUPPBMR   0x050A0
+#define IXGBE_RT2CR(_i) (0x03C20 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_RT2SR(_i) (0x03C40 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_TDTQ2TCCR(_i)     (0x0602C + ((_i) * 0x40)) /* 8 of these (0-7) */
+#define IXGBE_TDTQ2TCSR(_i)     (0x0622C + ((_i) * 0x40)) /* 8 of these (0-7) */
+#define IXGBE_TDPT2TCCR(_i)     (0x0CD20 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_TDPT2TCSR(_i)     (0x0CD40 + ((_i) * 4)) /* 8 of these (0-7) */
+
+/* Stats registers */
+#define IXGBE_CRCERRS   0x04000
+#define IXGBE_ILLERRC   0x04004
+#define IXGBE_ERRBC     0x04008
+#define IXGBE_MSPDC     0x04010
+#define IXGBE_MPC(_i)   (0x03FA0 + ((_i) * 4)) /* 8 of these 3FA0-3FBC*/
+#define IXGBE_MLFC      0x04034
+#define IXGBE_MRFC      0x04038
+#define IXGBE_RLEC      0x04040
+#define IXGBE_LXONTXC   0x03F60
+#define IXGBE_LXONRXC   0x0CF60
+#define IXGBE_LXOFFTXC  0x03F68
+#define IXGBE_LXOFFRXC  0x0CF68
+#define IXGBE_PXONTXC(_i)       (0x03F00 + ((_i) * 4)) /* 8 of these 3F00-3F1C*/
+#define IXGBE_PXONRXC(_i)       (0x0CF00 + ((_i) * 4)) /* 8 of these CF00-CF1C*/
+#define IXGBE_PXOFFTXC(_i)      (0x03F20 + ((_i) * 4)) /* 8 of these 3F20-3F3C*/
+#define IXGBE_PXOFFRXC(_i)      (0x0CF20 + ((_i) * 4)) /* 8 of these CF20-CF3C*/
+#define IXGBE_PRC64     0x0405C
+#define IXGBE_PRC127    0x04060
+#define IXGBE_PRC255    0x04064
+#define IXGBE_PRC511    0x04068
+#define IXGBE_PRC1023   0x0406C
+#define IXGBE_PRC1522   0x04070
+#define IXGBE_GPRC      0x04074
+#define IXGBE_BPRC      0x04078
+#define IXGBE_MPRC      0x0407C
+#define IXGBE_GPTC      0x04080
+#define IXGBE_GORCL     0x04088
+#define IXGBE_GORCH     0x0408C
+#define IXGBE_GOTCL     0x04090
+#define IXGBE_GOTCH     0x04094
+#define IXGBE_RNBC(_i)  (0x03FC0 + ((_i) * 4)) /* 8 of these 3FC0-3FDC*/
+#define IXGBE_RUC       0x040A4
+#define IXGBE_RFC       0x040A8
+#define IXGBE_ROC       0x040AC
+#define IXGBE_RJC       0x040B0
+#define IXGBE_MNGPRC    0x040B4
+#define IXGBE_MNGPDC    0x040B8
+#define IXGBE_MNGPTC    0x0CF90
+#define IXGBE_TORL      0x040C0
+#define IXGBE_TORH      0x040C4
+#define IXGBE_TPR       0x040D0
+#define IXGBE_TPT       0x040D4
+#define IXGBE_PTC64     0x040D8
+#define IXGBE_PTC127    0x040DC
+#define IXGBE_PTC255    0x040E0
+#define IXGBE_PTC511    0x040E4
+#define IXGBE_PTC1023   0x040E8
+#define IXGBE_PTC1522   0x040EC
+#define IXGBE_MPTC      0x040F0
+#define IXGBE_BPTC      0x040F4
+#define IXGBE_XEC       0x04120
+
+#define IXGBE_RQSMR(_i) (0x02300 + ((_i) * 4)) /* 16 of these */
+#define IXGBE_TQSMR(_i) (0x07300 + ((_i) * 4)) /* 8 of these */
+
+#define IXGBE_QPRC(_i) (0x01030 + ((_i) * 0x40)) /* 16 of these */
+#define IXGBE_QPTC(_i) (0x06030 + ((_i) * 0x40)) /* 16 of these */
+#define IXGBE_QBRC(_i) (0x01034 + ((_i) * 0x40)) /* 16 of these */
+#define IXGBE_QBTC(_i) (0x06034 + ((_i) * 0x40)) /* 16 of these */
+
+/* Management */
+#define IXGBE_MAVTV(_i) (0x05010 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_MFUTP(_i) (0x05030 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_MANC      0x05820
+#define IXGBE_MFVAL     0x05824
+#define IXGBE_MANC2H    0x05860
+#define IXGBE_MDEF(_i)  (0x05890 + ((_i) * 4)) /* 8 of these (0-7) */
+#define IXGBE_MIPAF     0x058B0
+#define IXGBE_MMAL(_i)  (0x05910 + ((_i) * 8)) /* 4 of these (0-3) */
+#define IXGBE_MMAH(_i)  (0x05914 + ((_i) * 8)) /* 4 of these (0-3) */
+#define IXGBE_FTFT      0x09400 /* 0x9400-0x97FC */
+
+/* ARC Subsystem registers */
+#define IXGBE_HICR      0x15F00
+#define IXGBE_FWSTS     0x15F0C
+#define IXGBE_HSMC0R    0x15F04
+#define IXGBE_HSMC1R    0x15F08
+#define IXGBE_SWSR      0x15F10
+#define IXGBE_HFDR      0x15FE8
+#define IXGBE_FLEX_MNG  0x15800 /* 0x15800 - 0x15EFC */
+
+/* PCI-E registers */
+#define IXGBE_GCR       0x11000
+#define IXGBE_GTV       0x11004
+#define IXGBE_FUNCTAG   0x11008
+#define IXGBE_GLT       0x1100C
+#define IXGBE_GSCL_1    0x11010
+#define IXGBE_GSCL_2    0x11014
+#define IXGBE_GSCL_3    0x11018
+#define IXGBE_GSCL_4    0x1101C
+#define IXGBE_GSCN_0    0x11020
+#define IXGBE_GSCN_1    0x11024
+#define IXGBE_GSCN_2    0x11028
+#define IXGBE_GSCN_3    0x1102C
+#define IXGBE_FACTPS    0x10150
+#define IXGBE_PCIEANACTL  0x11040
+#define IXGBE_SWSM      0x10140
+#define IXGBE_FWSM      0x10148
+#define IXGBE_GSSR      0x10160
+#define IXGBE_MREVID    0x11064
+#define IXGBE_DCA_ID    0x11070
+#define IXGBE_DCA_CTRL  0x11074
+
+/* Diagnostic Registers */
+#define IXGBE_RDSTATCTL 0x02C20
+#define IXGBE_RDSTAT(_i) (0x02C00 + ((_i) * 4)) /* 0x02C00-0x02C1C */
+#define IXGBE_RDHMPN    0x02F08
+#define IXGBE_RIC_DW0   0x02F10
+#define IXGBE_RIC_DW1   0x02F14
+#define IXGBE_RIC_DW2   0x02F18
+#define IXGBE_RIC_DW3   0x02F1C
+#define IXGBE_RDPROBE   0x02F20
+#define IXGBE_TDSTATCTL 0x07C20
+#define IXGBE_TDSTAT(_i) (0x07C00 + ((_i) * 4)) /* 0x07C00 - 0x07C1C */
+#define IXGBE_TDHMPN    0x07F08
+#define IXGBE_TIC_DW0   0x07F10
+#define IXGBE_TIC_DW1   0x07F14
+#define IXGBE_TIC_DW2   0x07F18
+#define IXGBE_TIC_DW3   0x07F1C
+#define IXGBE_TDPROBE   0x07F20
+#define IXGBE_TXBUFCTRL 0x0C600
+#define IXGBE_TXBUFDATA0  0x0C610
+#define IXGBE_TXBUFDATA1  0x0C614
+#define IXGBE_TXBUFDATA2  0x0C618
+#define IXGBE_TXBUFDATA3  0x0C61C
+#define IXGBE_RXBUFCTRL   0x03600
+#define IXGBE_RXBUFDATA0  0x03610
+#define IXGBE_RXBUFDATA1  0x03614
+#define IXGBE_RXBUFDATA2  0x03618
+#define IXGBE_RXBUFDATA3  0x0361C
+#define IXGBE_PCIE_DIAG(_i)     (0x11090 + ((_i) * 4)) /* 8 of these */
+#define IXGBE_RFVAL     0x050A4
+#define IXGBE_MDFTC1    0x042B8
+#define IXGBE_MDFTC2    0x042C0
+#define IXGBE_MDFTFIFO1 0x042C4
+#define IXGBE_MDFTFIFO2 0x042C8
+#define IXGBE_MDFTS     0x042CC
+#define IXGBE_RXDATAWRPTR(_i)   (0x03700 + ((_i) * 4)) /* 8 of these 3700-370C*/
+#define IXGBE_RXDESCWRPTR(_i)   (0x03710 + ((_i) * 4)) /* 8 of these 3710-371C*/
+#define IXGBE_RXDATARDPTR(_i)   (0x03720 + ((_i) * 4)) /* 8 of these 3720-372C*/
+#define IXGBE_RXDESCRDPTR(_i)   (0x03730 + ((_i) * 4)) /* 8 of these 3730-373C*/
+#define IXGBE_TXDATAWRPTR(_i)   (0x0C700 + ((_i) * 4)) /* 8 of these C700-C70C*/
+#define IXGBE_TXDESCWRPTR(_i)   (0x0C710 + ((_i) * 4)) /* 8 of these C710-C71C*/
+#define IXGBE_TXDATARDPTR(_i)   (0x0C720 + ((_i) * 4)) /* 8 of these C720-C72C*/
+#define IXGBE_TXDESCRDPTR(_i)   (0x0C730 + ((_i) * 4)) /* 8 of these C730-C73C*/
+#define IXGBE_PCIEECCCTL 0x1106C
+#define IXGBE_PBTXECC   0x0C300
+#define IXGBE_PBRXECC   0x03300
+#define IXGBE_GHECCR    0x110B0
+
+/* MAC Registers */
+#define IXGBE_PCS1GCFIG 0x04200
+#define IXGBE_PCS1GLCTL 0x04208
+#define IXGBE_PCS1GLSTA 0x0420C
+#define IXGBE_PCS1GDBG0 0x04210
+#define IXGBE_PCS1GDBG1 0x04214
+#define IXGBE_PCS1GANA  0x04218
+#define IXGBE_PCS1GANLP 0x0421C
+#define IXGBE_PCS1GANNP 0x04220
+#define IXGBE_PCS1GANLPNP 0x04224
+#define IXGBE_HLREG0    0x04240
+#define IXGBE_HLREG1    0x04244
+#define IXGBE_PAP       0x04248
+#define IXGBE_MACA      0x0424C
+#define IXGBE_APAE      0x04250
+#define IXGBE_ARD       0x04254
+#define IXGBE_AIS       0x04258
+#define IXGBE_MSCA      0x0425C
+#define IXGBE_MSRWD     0x04260
+#define IXGBE_MLADD     0x04264
+#define IXGBE_MHADD     0x04268
+#define IXGBE_TREG      0x0426C
+#define IXGBE_PCSS1     0x04288
+#define IXGBE_PCSS2     0x0428C
+#define IXGBE_XPCSS     0x04290
+#define IXGBE_SERDESC   0x04298
+#define IXGBE_MACS      0x0429C
+#define IXGBE_AUTOC     0x042A0
+#define IXGBE_LINKS     0x042A4
+#define IXGBE_AUTOC2    0x042A8
+#define IXGBE_AUTOC3    0x042AC
+#define IXGBE_ANLP1     0x042B0
+#define IXGBE_ANLP2     0x042B4
+#define IXGBE_ATLASCTL  0x04800
+
+/* CTRL Bit Masks */
+#define IXGBE_CTRL_GIO_DIS      0x00000004 /* Global IO Master Disable bit */
+#define IXGBE_CTRL_LNK_RST      0x00000008 /* Link Reset. Resets everything. */
+#define IXGBE_CTRL_RST          0x04000000 /* Reset (SW) */
+
+/* FACTPS */
+#define IXGBE_FACTPS_LFS        0x40000000 /* LAN Function Select */
+
+/* MHADD Bit Masks */
+#define IXGBE_MHADD_MFS_MASK    0xFFFF0000
+#define IXGBE_MHADD_MFS_SHIFT   16
+
+/* Extended Device Control */
+#define IXGBE_CTRL_EXT_NS_DIS   0x00010000 /* No Snoop disable */
+#define IXGBE_CTRL_EXT_RO_DIS   0x00020000 /* Relaxed Ordering disable */
+#define IXGBE_CTRL_EXT_DRV_LOAD 0x10000000 /* Driver loaded bit for FW */
+
+/* Direct Cache Access (DCA) definitions */
+#define IXGBE_DCA_CTRL_DCA_ENABLE  0x00000000 /* DCA Enable */
+#define IXGBE_DCA_CTRL_DCA_DISABLE 0x00000001 /* DCA Disable */
+
+#define IXGBE_DCA_CTRL_DCA_MODE_CB1 0x00 /* DCA Mode CB1 */
+#define IXGBE_DCA_CTRL_DCA_MODE_CB2 0x02 /* DCA Mode CB2 */
+
+#define IXGBE_DCA_RXCTRL_CPUID_MASK 0x0000001F /* Rx CPUID Mask */
+#define IXGBE_DCA_RXCTRL_DESC_DCA_EN (1 << 5) /* DCA Rx Desc enable */
+#define IXGBE_DCA_RXCTRL_HEAD_DCA_EN (1 << 6) /* DCA Rx Desc header enable */
+#define IXGBE_DCA_RXCTRL_DATA_DCA_EN (1 << 7) /* DCA Rx Desc payload enable */
+
+#define IXGBE_DCA_TXCTRL_CPUID_MASK 0x0000001F /* Tx CPUID Mask */
+#define IXGBE_DCA_TXCTRL_DESC_DCA_EN (1 << 5) /* DCA Tx Desc enable */
+
+/* MSCA Bit Masks */
+#define IXGBE_MSCA_NP_ADDR_MASK      0x0000FFFF /* MDI Address (new protocol) */
+#define IXGBE_MSCA_NP_ADDR_SHIFT     0
+#define IXGBE_MSCA_DEV_TYPE_MASK     0x001F0000 /* Device Type (new protocol) */
+#define IXGBE_MSCA_DEV_TYPE_SHIFT    16 /* Register Address (old protocol */
+#define IXGBE_MSCA_PHY_ADDR_MASK     0x03E00000 /* PHY Address mask */
+#define IXGBE_MSCA_PHY_ADDR_SHIFT    21 /* PHY Address shift*/
+#define IXGBE_MSCA_OP_CODE_MASK      0x0C000000 /* OP CODE mask */
+#define IXGBE_MSCA_OP_CODE_SHIFT     26 /* OP CODE shift */
+#define IXGBE_MSCA_ADDR_CYCLE        0x00000000 /* OP CODE 00 (addr cycle) */
+#define IXGBE_MSCA_WRITE             0x04000000 /* OP CODE 01 (write) */
+#define IXGBE_MSCA_READ              0x08000000 /* OP CODE 10 (read) */
+#define IXGBE_MSCA_READ_AUTOINC      0x0C000000 /* OP CODE 11 (read, auto inc)*/
+#define IXGBE_MSCA_ST_CODE_MASK      0x30000000 /* ST Code mask */
+#define IXGBE_MSCA_ST_CODE_SHIFT     28 /* ST Code shift */
+#define IXGBE_MSCA_NEW_PROTOCOL      0x00000000 /* ST CODE 00 (new protocol) */
+#define IXGBE_MSCA_OLD_PROTOCOL      0x10000000 /* ST CODE 01 (old protocol) */
+#define IXGBE_MSCA_MDI_COMMAND       0x40000000 /* Initiate MDI command */
+#define IXGBE_MSCA_MDI_IN_PROG_EN    0x80000000 /* MDI in progress enable */
+
+/* MSRWD bit masks */
+#define IXGBE_MSRWD_WRITE_DATA_MASK  0x0000FFFF
+#define IXGBE_MSRWD_WRITE_DATA_SHIFT 0
+#define IXGBE_MSRWD_READ_DATA_MASK   0xFFFF0000
+#define IXGBE_MSRWD_READ_DATA_SHIFT  16
+
+/* Device Type definitions for new protocol MDIO commands */
+#define IXGBE_MDIO_PMA_PMD_DEV_TYPE               0x1
+#define IXGBE_MDIO_PCS_DEV_TYPE                   0x3
+#define IXGBE_MDIO_PHY_XS_DEV_TYPE                0x4
+#define IXGBE_MDIO_AUTO_NEG_DEV_TYPE              0x7
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE     0x1E   /* Device 30 */
+
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_CONTROL      0x0    /* VS1 Control Reg */
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_STATUS       0x1    /* VS1 Status Reg */
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_LINK_STATUS  0x0008 /* 1 = Link Up */
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_SPEED_STATUS 0x0010 /* 0 - 10G, 1 - 1G */
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_10G_SPEED    0x0018
+#define IXGBE_MDIO_VENDOR_SPECIFIC_1_1G_SPEED     0x0010
+
+#define IXGBE_MDIO_AUTO_NEG_CONTROL    0x0 /* AUTO_NEG Control Reg */
+#define IXGBE_MDIO_AUTO_NEG_STATUS     0x1 /* AUTO_NEG Status Reg */
+#define IXGBE_MDIO_PHY_XS_CONTROL      0x0 /* PHY_XS Control Reg */
+#define IXGBE_MDIO_PHY_XS_RESET        0x8000 /* PHY_XS Reset */
+#define IXGBE_MDIO_PHY_ID_HIGH         0x2 /* PHY ID High Reg*/
+#define IXGBE_MDIO_PHY_ID_LOW          0x3 /* PHY ID Low Reg*/
+#define IXGBE_MDIO_PHY_SPEED_ABILITY   0x4 /* Speed Abilty Reg */
+#define IXGBE_MDIO_PHY_SPEED_10G       0x0001 /* 10G capable */
+#define IXGBE_MDIO_PHY_SPEED_1G        0x0010 /* 1G capable */
+
+#define IXGBE_PHY_REVISION_MASK        0xFFFFFFF0
+#define IXGBE_MAX_PHY_ADDR             32
+
+/* PHY IDs*/
+#define TN1010_PHY_ID    0x00A19410
+#define QT2022_PHY_ID    0x0043A400
+
+/* General purpose Interrupt Enable */
+#define IXGBE_GPIE_MSIX_MODE      0x00000010 /* MSI-X mode */
+#define IXGBE_GPIE_OCD            0x00000020 /* Other Clear Disable */
+#define IXGBE_GPIE_EIMEN          0x00000040 /* Immediate Interrupt Enable */
+#define IXGBE_GPIE_EIAME          0x40000000
+#define IXGBE_GPIE_PBA_SUPPORT    0x80000000
+
+/* Transmit Flow Control status */
+#define IXGBE_TFCS_TXOFF         0x00000001
+#define IXGBE_TFCS_TXOFF0        0x00000100
+#define IXGBE_TFCS_TXOFF1        0x00000200
+#define IXGBE_TFCS_TXOFF2        0x00000400
+#define IXGBE_TFCS_TXOFF3        0x00000800
+#define IXGBE_TFCS_TXOFF4        0x00001000
+#define IXGBE_TFCS_TXOFF5        0x00002000
+#define IXGBE_TFCS_TXOFF6        0x00004000
+#define IXGBE_TFCS_TXOFF7        0x00008000
+
+/* TCP Timer */
+#define IXGBE_TCPTIMER_KS            0x00000100
+#define IXGBE_TCPTIMER_COUNT_ENABLE  0x00000200
+#define IXGBE_TCPTIMER_COUNT_FINISH  0x00000400
+#define IXGBE_TCPTIMER_LOOP          0x00000800
+#define IXGBE_TCPTIMER_DURATION_MASK 0x000000FF
+
+/* HLREG0 Bit Masks */
+#define IXGBE_HLREG0_TXCRCEN      0x00000001   /* bit  0 */
+#define IXGBE_HLREG0_RXCRCSTRP    0x00000002   /* bit  1 */
+#define IXGBE_HLREG0_JUMBOEN      0x00000004   /* bit  2 */
+#define IXGBE_HLREG0_TXPADEN      0x00000400   /* bit 10 */
+#define IXGBE_HLREG0_TXPAUSEEN    0x00001000   /* bit 12 */
+#define IXGBE_HLREG0_RXPAUSEEN    0x00004000   /* bit 14 */
+#define IXGBE_HLREG0_LPBK         0x00008000   /* bit 15 */
+#define IXGBE_HLREG0_MDCSPD       0x00010000   /* bit 16 */
+#define IXGBE_HLREG0_CONTMDC      0x00020000   /* bit 17 */
+#define IXGBE_HLREG0_CTRLFLTR     0x00040000   /* bit 18 */
+#define IXGBE_HLREG0_PREPEND      0x00F00000   /* bits 20-23 */
+#define IXGBE_HLREG0_PRIPAUSEEN   0x01000000   /* bit 24 */
+#define IXGBE_HLREG0_RXPAUSERECDA 0x06000000   /* bits 25-26 */
+#define IXGBE_HLREG0_RXLNGTHERREN 0x08000000   /* bit 27 */
+#define IXGBE_HLREG0_RXPADSTRIPEN 0x10000000   /* bit 28 */
+
+/* VMD_CTL bitmasks */
+#define IXGBE_VMD_CTL_VMDQ_EN     0x00000001
+#define IXGBE_VMD_CTL_VMDQ_FILTER 0x00000002
+
+/* RDHMPN and TDHMPN bitmasks */
+#define IXGBE_RDHMPN_RDICADDR       0x007FF800
+#define IXGBE_RDHMPN_RDICRDREQ      0x00800000
+#define IXGBE_RDHMPN_RDICADDR_SHIFT 11
+#define IXGBE_TDHMPN_TDICADDR       0x003FF800
+#define IXGBE_TDHMPN_TDICRDREQ      0x00800000
+#define IXGBE_TDHMPN_TDICADDR_SHIFT 11
+
+/* Receive Checksum Control */
+#define IXGBE_RXCSUM_IPPCSE     0x00001000   /* IP payload checksum enable */
+#define IXGBE_RXCSUM_PCSD       0x00002000   /* packet checksum disabled */
+
+/* FCRTL Bit Masks */
+#define IXGBE_FCRTL_XONE        0x80000000  /* bit 31, XON enable */
+#define IXGBE_FCRTH_FCEN        0x80000000  /* Rx Flow control enable */
+
+/* PAP bit masks*/
+#define IXGBE_PAP_TXPAUSECNT_MASK   0x0000FFFF /* Pause counter mask */
+
+/* RMCS Bit Masks */
+#define IXGBE_RMCS_RRM          0x00000002 /* Receive Recylce Mode enable */
+/* Receive Arbitration Control: 0 Round Robin, 1 DFP */
+#define IXGBE_RMCS_RAC          0x00000004
+#define IXGBE_RMCS_DFP          IXGBE_RMCS_RAC /* Deficit Fixed Priority ena */
+#define IXGBE_RMCS_TFCE_802_3X  0x00000008 /* Tx Priority flow control ena */
+#define IXGBE_RMCS_TFCE_PRIORITY 0x00000010 /* Tx Priority flow control ena */
+#define IXGBE_RMCS_ARBDIS       0x00000040 /* Arbitration disable bit */
+
+/* Interrupt register bitmasks */
+
+/* Extended Interrupt Cause Read */
+#define IXGBE_EICR_RTX_QUEUE    0x0000FFFF /* RTx Queue Interrupt */
+#define IXGBE_EICR_LSC          0x00100000 /* Link Status Change */
+#define IXGBE_EICR_MNG          0x00400000 /* Managability Event Interrupt */
+#define IXGBE_EICR_PBUR         0x10000000 /* Packet Buffer Handler Error */
+#define IXGBE_EICR_DHER         0x20000000 /* Descriptor Handler Error */
+#define IXGBE_EICR_TCP_TIMER    0x40000000 /* TCP Timer */
+#define IXGBE_EICR_OTHER        0x80000000 /* Interrupt Cause Active */
+
+/* Extended Interrupt Cause Set */
+#define IXGBE_EICS_RTX_QUEUE    IXGBE_EICR_RTX_QUEUE /* RTx Queue Interrupt */
+#define IXGBE_EICS_LSC          IXGBE_EICR_LSC /* Link Status Change */
+#define IXGBE_EICR_GPI_SDP0     0x01000000 /* Gen Purpose Interrupt on SDP0 */
+#define IXGBE_EICS_MNG          IXGBE_EICR_MNG /* MNG Event Interrupt */
+#define IXGBE_EICS_PBUR         IXGBE_EICR_PBUR /* Pkt Buf Handler Error */
+#define IXGBE_EICS_DHER         IXGBE_EICR_DHER /* Desc Handler Error */
+#define IXGBE_EICS_TCP_TIMER    IXGBE_EICR_TCP_TIMER /* TCP Timer */
+#define IXGBE_EICS_OTHER        IXGBE_EICR_OTHER     /* INT Cause Active */
+
+/* Extended Interrupt Mask Set */
+#define IXGBE_EIMS_RTX_QUEUE    IXGBE_EICR_RTX_QUEUE /* RTx Queue Interrupt */
+#define IXGBE_EIMS_LSC          IXGBE_EICR_LSC       /* Link Status Change */
+#define IXGBE_EIMS_MNG          IXGBE_EICR_MNG       /* MNG Event Interrupt */
+#define IXGBE_EIMS_PBUR         IXGBE_EICR_PBUR      /* Pkt Buf Handler Error */
+#define IXGBE_EIMS_DHER         IXGBE_EICR_DHER      /* Descr Handler Error */
+#define IXGBE_EIMS_TCP_TIMER    IXGBE_EICR_TCP_TIMER /* TCP Timer */
+#define IXGBE_EIMS_OTHER        IXGBE_EICR_OTHER     /* INT Cause Active */
+
+/* Extended Interrupt Mask Clear */
+#define IXGBE_EIMC_RTX_QUEUE    IXGBE_EICR_RTX_QUEUE /* RTx Queue Interrupt */
+#define IXGBE_EIMC_LSC          IXGBE_EICR_LSC       /* Link Status Change */
+#define IXGBE_EIMC_MNG          IXGBE_EICR_MNG       /* MNG Event Interrupt */
+#define IXGBE_EIMC_PBUR         IXGBE_EICR_PBUR      /* Pkt Buf Handler Error */
+#define IXGBE_EIMC_DHER         IXGBE_EICR_DHER      /* Desc Handler Error */
+#define IXGBE_EIMC_TCP_TIMER    IXGBE_EICR_TCP_TIMER /* TCP Timer */
+#define IXGBE_EIMC_OTHER        IXGBE_EICR_OTHER     /* INT Cause Active */
+
+#define IXGBE_EIMS_ENABLE_MASK ( \
+				IXGBE_EIMS_RTX_QUEUE       | \
+				IXGBE_EIMS_LSC             | \
+				IXGBE_EIMS_TCP_TIMER       | \
+				IXGBE_EIMS_OTHER)
+
+/* Immediate Interrupt RX (A.K.A. Low Latency Interrupt) */
+#define IXGBE_IMIR_PORT_IM_EN     0x00010000  /* TCP port enable */
+#define IXGBE_IMIR_PORT_BP        0x00020000  /* TCP port check bypass */
+#define IXGBE_IMIREXT_SIZE_BP     0x00001000  /* Packet size bypass */
+#define IXGBE_IMIREXT_CTRL_URG    0x00002000  /* Check URG bit in header */
+#define IXGBE_IMIREXT_CTRL_ACK    0x00004000  /* Check ACK bit in header */
+#define IXGBE_IMIREXT_CTRL_PSH    0x00008000  /* Check PSH bit in header */
+#define IXGBE_IMIREXT_CTRL_RST    0x00010000  /* Check RST bit in header */
+#define IXGBE_IMIREXT_CTRL_SYN    0x00020000  /* Check SYN bit in header */
+#define IXGBE_IMIREXT_CTRL_FIN    0x00040000  /* Check FIN bit in header */
+#define IXGBE_IMIREXT_CTRL_BP     0x00080000  /* Bypass check of control bits */
+
+/* Interrupt clear mask */
+#define IXGBE_IRQ_CLEAR_MASK    0xFFFFFFFF
+
+/* Interrupt Vector Allocation Registers */
+#define IXGBE_IVAR_REG_NUM      25
+#define IXGBE_IVAR_TXRX_ENTRY   96
+#define IXGBE_IVAR_RX_ENTRY     64
+#define IXGBE_IVAR_RX_QUEUE(_i)    (0 + (_i))
+#define IXGBE_IVAR_TX_QUEUE(_i)    (64 + (_i))
+#define IXGBE_IVAR_TX_ENTRY     32
+
+#define IXGBE_IVAR_TCP_TIMER_INDEX       96 /* 0 based index */
+#define IXGBE_IVAR_OTHER_CAUSES_INDEX    97 /* 0 based index */
+
+#define IXGBE_MSIX_VECTOR(_i)   (0 + (_i))
+
+#define IXGBE_IVAR_ALLOC_VAL    0x80 /* Interrupt Allocation valid */
+
+/* VLAN Control Bit Masks */
+#define IXGBE_VLNCTRL_VET       0x0000FFFF  /* bits 0-15 */
+#define IXGBE_VLNCTRL_CFI       0x10000000  /* bit 28 */
+#define IXGBE_VLNCTRL_CFIEN     0x20000000  /* bit 29 */
+#define IXGBE_VLNCTRL_VFE       0x40000000  /* bit 30 */
+#define IXGBE_VLNCTRL_VME       0x80000000  /* bit 31 */
+
+#define IXGBE_ETHERNET_IEEE_VLAN_TYPE 0x8100  /* 802.1q protocol */
+
+/* STATUS Bit Masks */
+#define IXGBE_STATUS_LAN_ID     0x0000000C /* LAN ID */
+#define IXGBE_STATUS_GIO        0x00080000 /* GIO Master Enable Status */
+
+#define IXGBE_STATUS_LAN_ID_0   0x00000000 /* LAN ID 0 */
+#define IXGBE_STATUS_LAN_ID_1   0x00000004 /* LAN ID 1 */
+
+/* ESDP Bit Masks */
+#define IXGBE_ESDP_SDP4 0x00000001 /* SDP4 Data Value */
+#define IXGBE_ESDP_SDP5 0x00000002 /* SDP5 Data Value */
+#define IXGBE_ESDP_SDP4_DIR     0x00000004 /* SDP4 IO direction */
+#define IXGBE_ESDP_SDP5_DIR     0x00000008 /* SDP5 IO direction */
+
+/* LEDCTL Bit Masks */
+#define IXGBE_LED_IVRT_BASE      0x00000040
+#define IXGBE_LED_BLINK_BASE     0x00000080
+#define IXGBE_LED_MODE_MASK_BASE 0x0000000F
+#define IXGBE_LED_OFFSET(_base, _i) (_base << (8 * (_i)))
+#define IXGBE_LED_MODE_SHIFT(_i) (8*(_i))
+#define IXGBE_LED_IVRT(_i)       IXGBE_LED_OFFSET(IXGBE_LED_IVRT_BASE, _i)
+#define IXGBE_LED_BLINK(_i)      IXGBE_LED_OFFSET(IXGBE_LED_BLINK_BASE, _i)
+#define IXGBE_LED_MODE_MASK(_i)  IXGBE_LED_OFFSET(IXGBE_LED_MODE_MASK_BASE, _i)
+
+/* LED modes */
+#define IXGBE_LED_LINK_UP       0x0
+#define IXGBE_LED_LINK_10G      0x1
+#define IXGBE_LED_MAC           0x2
+#define IXGBE_LED_FILTER        0x3
+#define IXGBE_LED_LINK_ACTIVE   0x4
+#define IXGBE_LED_LINK_1G       0x5
+#define IXGBE_LED_ON            0xE
+#define IXGBE_LED_OFF           0xF
+
+/* AUTOC Bit Masks */
+#define IXGBE_AUTOC_KX4_SUPP    0x80000000
+#define IXGBE_AUTOC_KX_SUPP     0x40000000
+#define IXGBE_AUTOC_PAUSE       0x30000000
+#define IXGBE_AUTOC_RF          0x08000000
+#define IXGBE_AUTOC_PD_TMR      0x06000000
+#define IXGBE_AUTOC_AN_RX_LOOSE 0x01000000
+#define IXGBE_AUTOC_AN_RX_DRIFT 0x00800000
+#define IXGBE_AUTOC_AN_RX_ALIGN 0x007C0000
+#define IXGBE_AUTOC_AN_RESTART  0x00001000
+#define IXGBE_AUTOC_FLU         0x00000001
+#define IXGBE_AUTOC_LMS_SHIFT   13
+#define IXGBE_AUTOC_LMS_MASK   (0x7 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_1G_LINK_NO_AN  (0x0 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_10G_LINK_NO_AN (0x1 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_1G_AN  (0x2 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_KX4_AN (0x4 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_KX4_AN_1G_AN   (0x6 << IXGBE_AUTOC_LMS_SHIFT)
+#define IXGBE_AUTOC_LMS_ATTACH_TYPE    (0x7 << IXGBE_AUTOC_10G_PMA_PMD_SHIFT)
+
+#define IXGBE_AUTOC_1G_PMA_PMD      0x00000200
+#define IXGBE_AUTOC_10G_PMA_PMD     0x00000180
+#define IXGBE_AUTOC_10G_PMA_PMD_SHIFT 7
+#define IXGBE_AUTOC_1G_PMA_PMD_SHIFT 9
+#define IXGBE_AUTOC_10G_XAUI   (0x0 << IXGBE_AUTOC_10G_PMA_PMD_SHIFT)
+#define IXGBE_AUTOC_10G_KX4    (0x1 << IXGBE_AUTOC_10G_PMA_PMD_SHIFT)
+#define IXGBE_AUTOC_10G_CX4    (0x2 << IXGBE_AUTOC_10G_PMA_PMD_SHIFT)
+#define IXGBE_AUTOC_1G_BX      (0x0 << IXGBE_AUTOC_1G_PMA_PMD_SHIFT)
+#define IXGBE_AUTOC_1G_KX      (0x1 << IXGBE_AUTOC_1G_PMA_PMD_SHIFT)
+
+/* LINKS Bit Masks */
+#define IXGBE_LINKS_KX_AN_COMP  0x80000000
+#define IXGBE_LINKS_UP          0x40000000
+#define IXGBE_LINKS_SPEED       0x20000000
+#define IXGBE_LINKS_MODE        0x18000000
+#define IXGBE_LINKS_RX_MODE     0x06000000
+#define IXGBE_LINKS_TX_MODE     0x01800000
+#define IXGBE_LINKS_XGXS_EN     0x00400000
+#define IXGBE_LINKS_PCS_1G_EN   0x00200000
+#define IXGBE_LINKS_1G_AN_EN    0x00100000
+#define IXGBE_LINKS_KX_AN_IDLE  0x00080000
+#define IXGBE_LINKS_1G_SYNC     0x00040000
+#define IXGBE_LINKS_10G_ALIGN   0x00020000
+#define IXGBE_LINKS_10G_LANE_SYNC 0x00017000
+#define IXGBE_LINKS_TL_FAULT    0x00001000
+#define IXGBE_LINKS_SIGNAL      0x00000F00
+
+#define IXGBE_AUTO_NEG_TIME     45  /* 4.5 Seconds */
+
+/* SW Semaphore Register bitmasks */
+#define IXGBE_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */
+#define IXGBE_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */
+#define IXGBE_SWSM_WMNG 0x00000004 /* Wake MNG Clock */
+
+/* GSSR definitions */
+#define IXGBE_GSSR_EEP_SM     0x0001
+#define IXGBE_GSSR_PHY0_SM    0x0002
+#define IXGBE_GSSR_PHY1_SM    0x0004
+#define IXGBE_GSSR_MAC_CSR_SM 0x0008
+#define IXGBE_GSSR_FLASH_SM   0x0010
+
+/* EEC Register */
+#define IXGBE_EEC_SK        0x00000001 /* EEPROM Clock */
+#define IXGBE_EEC_CS        0x00000002 /* EEPROM Chip Select */
+#define IXGBE_EEC_DI        0x00000004 /* EEPROM Data In */
+#define IXGBE_EEC_DO        0x00000008 /* EEPROM Data Out */
+#define IXGBE_EEC_FWE_MASK  0x00000030 /* FLASH Write Enable */
+#define IXGBE_EEC_FWE_DIS   0x00000010 /* Disable FLASH writes */
+#define IXGBE_EEC_FWE_EN    0x00000020 /* Enable FLASH writes */
+#define IXGBE_EEC_FWE_SHIFT 4
+#define IXGBE_EEC_REQ       0x00000040 /* EEPROM Access Request */
+#define IXGBE_EEC_GNT       0x00000080 /* EEPROM Access Grant */
+#define IXGBE_EEC_PRES      0x00000100 /* EEPROM Present */
+#define IXGBE_EEC_ARD       0x00000200 /* EEPROM Auto Read Done */
+/* EEPROM Addressing bits based on type (0-small, 1-large) */
+#define IXGBE_EEC_ADDR_SIZE 0x00000400
+#define IXGBE_EEC_SIZE      0x00007800 /* EEPROM Size */
+
+#define IXGBE_EEC_SIZE_SHIFT          11
+#define IXGBE_EEPROM_WORD_SIZE_SHIFT  6
+#define IXGBE_EEPROM_OPCODE_BITS      8
+
+/* Checksum and EEPROM pointers */
+#define IXGBE_EEPROM_CHECKSUM   0x3F
+#define IXGBE_EEPROM_SUM        0xBABA
+#define IXGBE_PCIE_ANALOG_PTR   0x03
+#define IXGBE_ATLAS0_CONFIG_PTR 0x04
+#define IXGBE_ATLAS1_CONFIG_PTR 0x05
+#define IXGBE_PCIE_GENERAL_PTR  0x06
+#define IXGBE_PCIE_CONFIG0_PTR  0x07
+#define IXGBE_PCIE_CONFIG1_PTR  0x08
+#define IXGBE_CORE0_PTR         0x09
+#define IXGBE_CORE1_PTR         0x0A
+#define IXGBE_MAC0_PTR          0x0B
+#define IXGBE_MAC1_PTR          0x0C
+#define IXGBE_CSR0_CONFIG_PTR   0x0D
+#define IXGBE_CSR1_CONFIG_PTR   0x0E
+#define IXGBE_FW_PTR            0x0F
+
+/* EEPROM Commands - SPI */
+#define IXGBE_EEPROM_MAX_RETRY_SPI      5000 /* Max wait 5ms for RDY signal */
+#define IXGBE_EEPROM_STATUS_RDY_SPI     0x01
+#define IXGBE_EEPROM_READ_OPCODE_SPI    0x03  /* EEPROM read opcode */
+#define IXGBE_EEPROM_WRITE_OPCODE_SPI   0x02  /* EEPROM write opcode */
+#define IXGBE_EEPROM_A8_OPCODE_SPI      0x08  /* opcode bit-3 = addr bit-8 */
+#define IXGBE_EEPROM_WREN_OPCODE_SPI    0x06  /* EEPROM set Write Ena latch */
+/* EEPROM reset Write Enbale latch */
+#define IXGBE_EEPROM_WRDI_OPCODE_SPI    0x04
+#define IXGBE_EEPROM_RDSR_OPCODE_SPI    0x05  /* EEPROM read Status reg */
+#define IXGBE_EEPROM_WRSR_OPCODE_SPI    0x01  /* EEPROM write Status reg */
+#define IXGBE_EEPROM_ERASE4K_OPCODE_SPI 0x20  /* EEPROM ERASE 4KB */
+#define IXGBE_EEPROM_ERASE64K_OPCODE_SPI  0xD8  /* EEPROM ERASE 64KB */
+#define IXGBE_EEPROM_ERASE256_OPCODE_SPI  0xDB  /* EEPROM ERASE 256B */
+
+/* EEPROM Read Register */
+#define IXGBE_EEPROM_READ_REG_DATA   16   /* data offset in EEPROM read reg */
+#define IXGBE_EEPROM_READ_REG_DONE   2    /* Offset to READ done bit */
+#define IXGBE_EEPROM_READ_REG_START  1    /* First bit to start operation */
+#define IXGBE_EEPROM_READ_ADDR_SHIFT 2    /* Shift to the address bits */
+
+#define IXGBE_ETH_LENGTH_OF_ADDRESS   6
+
+#ifndef IXGBE_EEPROM_GRANT_ATTEMPTS
+#define IXGBE_EEPROM_GRANT_ATTEMPTS 1000 /* EEPROM # attempts to gain grant */
+#endif
+
+#ifndef IXGBE_EERD_ATTEMPTS
+/* Number of 5 microseconds we wait for EERD read to complete */
+#define IXGBE_EERD_ATTEMPTS 100000
+#endif
+
+/* PCI Bus Info */
+#define IXGBE_PCI_LINK_STATUS     0xB2
+#define IXGBE_PCI_LINK_WIDTH      0x3F0
+#define IXGBE_PCI_LINK_WIDTH_1    0x10
+#define IXGBE_PCI_LINK_WIDTH_2    0x20
+#define IXGBE_PCI_LINK_WIDTH_4    0x40
+#define IXGBE_PCI_LINK_WIDTH_8    0x80
+#define IXGBE_PCI_LINK_SPEED      0xF
+#define IXGBE_PCI_LINK_SPEED_2500 0x1
+#define IXGBE_PCI_LINK_SPEED_5000 0x2
+
+/* Number of 100 microseconds we wait for PCI Express master disable */
+#define IXGBE_PCI_MASTER_DISABLE_TIMEOUT 800
+
+/* PHY Types */
+#define IXGBE_M88E1145_E_PHY_ID  0x01410CD0
+
+/* Check whether address is multicast.  This is little-endian specific check.*/
+#define IXGBE_IS_MULTICAST(Address) \
+		(bool)(((u8 *)(Address))[0] & ((u8)0x01))
+
+/* Check whether an address is broadcast. */
+#define IXGBE_IS_BROADCAST(Address)                      \
+		((((u8 *)(Address))[0] == ((u8)0xff)) && \
+		(((u8 *)(Address))[1] == ((u8)0xff)))
+
+/* RAH */
+#define IXGBE_RAH_VIND_MASK     0x003C0000
+#define IXGBE_RAH_VIND_SHIFT    18
+#define IXGBE_RAH_AV            0x80000000
+
+/* Filters */
+#define IXGBE_MC_TBL_SIZE       128  /* Multicast Filter Table (4096 bits) */
+#define IXGBE_VLAN_FILTER_TBL_SIZE 128  /* VLAN Filter Table (4096 bits) */
+
+/* Header split receive */
+#define IXGBE_RFCTL_ISCSI_DIS       0x00000001
+#define IXGBE_RFCTL_ISCSI_DWC_MASK  0x0000003E
+#define IXGBE_RFCTL_ISCSI_DWC_SHIFT 1
+#define IXGBE_RFCTL_NFSW_DIS        0x00000040
+#define IXGBE_RFCTL_NFSR_DIS        0x00000080
+#define IXGBE_RFCTL_NFS_VER_MASK    0x00000300
+#define IXGBE_RFCTL_NFS_VER_SHIFT   8
+#define IXGBE_RFCTL_NFS_VER_2       0
+#define IXGBE_RFCTL_NFS_VER_3       1
+#define IXGBE_RFCTL_NFS_VER_4       2
+#define IXGBE_RFCTL_IPV6_DIS        0x00000400
+#define IXGBE_RFCTL_IPV6_XSUM_DIS   0x00000800
+#define IXGBE_RFCTL_IPFRSP_DIS      0x00004000
+#define IXGBE_RFCTL_IPV6_EX_DIS     0x00010000
+#define IXGBE_RFCTL_NEW_IPV6_EXT_DIS 0x00020000
+
+/* Transmit Config masks */
+#define IXGBE_TXDCTL_ENABLE     0x02000000 /* Enable specific Tx Queue */
+#define IXGBE_TXDCTL_SWFLSH     0x04000000 /* Tx Desc. write-back flushing */
+/* Enable short packet padding to 64 bytes */
+#define IXGBE_TX_PAD_ENABLE     0x00000400
+#define IXGBE_JUMBO_FRAME_ENABLE 0x00000004  /* Allow jumbo frames */
+/* This allows for 16K packets + 4k for vlan */
+#define IXGBE_MAX_FRAME_SZ      0x40040000
+
+#define IXGBE_TDWBAL_HEAD_WB_ENABLE   0x1      /* Tx head write-back enable */
+#define IXGBE_TDWBAL_SEQNUM_WB_ENABLE 0x2      /* Tx seq. # write-back enable */
+
+/* Receive Config masks */
+#define IXGBE_RXCTRL_RXEN       0x00000001  /* Enable Receiver */
+#define IXGBE_RXCTRL_DMBYPS     0x00000002  /* Descriptor Monitor Bypass */
+#define IXGBE_RXDCTL_ENABLE     0x02000000  /* Enable specific Rx Queue */
+
+#define IXGBE_FCTRL_SBP 0x00000002 /* Store Bad Packet */
+#define IXGBE_FCTRL_MPE 0x00000100 /* Multicast Promiscuous Ena*/
+#define IXGBE_FCTRL_UPE 0x00000200 /* Unicast Promiscuous Ena */
+#define IXGBE_FCTRL_BAM 0x00000400 /* Broadcast Accept Mode */
+#define IXGBE_FCTRL_PMCF 0x00001000 /* Pass MAC Control Frames */
+#define IXGBE_FCTRL_DPF 0x00002000 /* Discard Pause Frame */
+/* Receive Priority Flow Control Enbale */
+#define IXGBE_FCTRL_RPFCE 0x00004000
+#define IXGBE_FCTRL_RFCE 0x00008000 /* Receive Flow Control Ena */
+
+/* Multiple Receive Queue Control */
+#define IXGBE_MRQC_RSSEN                 0x00000001  /* RSS Enable */
+#define IXGBE_MRQC_RSS_FIELD_MASK        0xFFFF0000
+#define IXGBE_MRQC_RSS_FIELD_IPV4_TCP    0x00010000
+#define IXGBE_MRQC_RSS_FIELD_IPV4        0x00020000
+#define IXGBE_MRQC_RSS_FIELD_IPV6_EX_TCP 0x00040000
+#define IXGBE_MRQC_RSS_FIELD_IPV6_EX     0x00080000
+#define IXGBE_MRQC_RSS_FIELD_IPV6        0x00100000
+#define IXGBE_MRQC_RSS_FIELD_IPV6_TCP    0x00200000
+#define IXGBE_MRQC_RSS_FIELD_IPV4_UDP    0x00400000
+#define IXGBE_MRQC_RSS_FIELD_IPV6_UDP    0x00800000
+#define IXGBE_MRQC_RSS_FIELD_IPV6_EX_UDP 0x01000000
+
+#define IXGBE_TXD_POPTS_IXSM 0x01       /* Insert IP checksum */
+#define IXGBE_TXD_POPTS_TXSM 0x02       /* Insert TCP/UDP checksum */
+#define IXGBE_TXD_CMD_EOP    0x01000000 /* End of Packet */
+#define IXGBE_TXD_CMD_IFCS   0x02000000 /* Insert FCS (Ethernet CRC) */
+#define IXGBE_TXD_CMD_IC     0x04000000 /* Insert Checksum */
+#define IXGBE_TXD_CMD_RS     0x08000000 /* Report Status */
+#define IXGBE_TXD_CMD_DEXT   0x20000000 /* Descriptor extension (0 = legacy) */
+#define IXGBE_TXD_CMD_VLE    0x40000000 /* Add VLAN tag */
+#define IXGBE_TXD_STAT_DD    0x00000001 /* Descriptor Done */
+
+/* Receive Descriptor bit definitions */
+#define IXGBE_RXD_STAT_DD       0x01    /* Descriptor Done */
+#define IXGBE_RXD_STAT_EOP      0x02    /* End of Packet */
+#define IXGBE_RXD_STAT_IXSM     0x04    /* Ignore checksum */
+#define IXGBE_RXD_STAT_VP       0x08    /* IEEE VLAN Packet */
+#define IXGBE_RXD_STAT_UDPCS    0x10    /* UDP xsum caculated */
+#define IXGBE_RXD_STAT_L4CS     0x20    /* L4 xsum calculated */
+#define IXGBE_RXD_STAT_IPCS     0x40    /* IP xsum calculated */
+#define IXGBE_RXD_STAT_PIF      0x80    /* passed in-exact filter */
+#define IXGBE_RXD_STAT_CRCV     0x100   /* Speculative CRC Valid */
+#define IXGBE_RXD_STAT_VEXT     0x200   /* 1st VLAN found */
+#define IXGBE_RXD_STAT_UDPV     0x400   /* Valid UDP checksum */
+#define IXGBE_RXD_STAT_DYNINT   0x800   /* Pkt caused INT via DYNINT */
+#define IXGBE_RXD_STAT_ACK      0x8000  /* ACK Packet indication */
+#define IXGBE_RXD_ERR_CE        0x01    /* CRC Error */
+#define IXGBE_RXD_ERR_LE        0x02    /* Length Error */
+#define IXGBE_RXD_ERR_PE        0x08    /* Packet Error */
+#define IXGBE_RXD_ERR_OSE       0x10    /* Oversize Error */
+#define IXGBE_RXD_ERR_USE       0x20    /* Undersize Error */
+#define IXGBE_RXD_ERR_TCPE      0x40    /* TCP/UDP Checksum Error */
+#define IXGBE_RXD_ERR_IPE       0x80    /* IP Checksum Error */
+#define IXGBE_RXDADV_HBO        0x00800000
+#define IXGBE_RXDADV_ERR_CE     0x01000000 /* CRC Error */
+#define IXGBE_RXDADV_ERR_LE     0x02000000 /* Length Error */
+#define IXGBE_RXDADV_ERR_PE     0x08000000 /* Packet Error */
+#define IXGBE_RXDADV_ERR_OSE    0x10000000 /* Oversize Error */
+#define IXGBE_RXDADV_ERR_USE    0x20000000 /* Undersize Error */
+#define IXGBE_RXDADV_ERR_TCPE   0x40000000 /* TCP/UDP Checksum Error */
+#define IXGBE_RXDADV_ERR_IPE    0x80000000 /* IP Checksum Error */
+#define IXGBE_RXD_VLAN_ID_MASK  0x0FFF  /* VLAN ID is in lower 12 bits */
+#define IXGBE_RXD_PRI_MASK      0xE000  /* Priority is in upper 3 bits */
+#define IXGBE_RXD_PRI_SHIFT     13
+#define IXGBE_RXD_CFI_MASK      0x1000  /* CFI is bit 12 */
+#define IXGBE_RXD_CFI_SHIFT     12
+
+/* SRRCTL bit definitions */
+#define IXGBE_SRRCTL_BSIZEPKT_SHIFT 10     /* so many KBs */
+#define IXGBE_SRRCTL_BSIZEPKT_MASK  0x0000007F
+#define IXGBE_SRRCTL_BSIZEHDR_MASK  0x00003F00
+#define IXGBE_SRRCTL_DESCTYPE_LEGACY 0x00000000
+#define IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF 0x02000000
+#define IXGBE_SRRCTL_DESCTYPE_HDR_SPLIT  0x04000000
+#define IXGBE_SRRCTL_DESCTYPE_HDR_REPLICATION_LARGE_PKT 0x08000000
+#define IXGBE_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS 0x0A000000
+
+#define IXGBE_RXDPS_HDRSTAT_HDRSP       0x00008000
+#define IXGBE_RXDPS_HDRSTAT_HDRLEN_MASK 0x000003FF
+
+#define IXGBE_RXDADV_RSSTYPE_MASK       0x0000000F
+#define IXGBE_RXDADV_PKTTYPE_MASK       0x0000FFF0
+#define IXGBE_RXDADV_HDRBUFLEN_MASK     0x00007FE0
+#define IXGBE_RXDADV_HDRBUFLEN_SHIFT    5
+#define IXGBE_RXDADV_SPLITHEADER_EN     0x00001000
+#define IXGBE_RXDADV_SPH                0x8000
+
+/* RSS Hash results */
+#define IXGBE_RXDADV_RSSTYPE_NONE       0x00000000
+#define IXGBE_RXDADV_RSSTYPE_IPV4_TCP   0x00000001
+#define IXGBE_RXDADV_RSSTYPE_IPV4       0x00000002
+#define IXGBE_RXDADV_RSSTYPE_IPV6_TCP   0x00000003
+#define IXGBE_RXDADV_RSSTYPE_IPV6_EX    0x00000004
+#define IXGBE_RXDADV_RSSTYPE_IPV6       0x00000005
+#define IXGBE_RXDADV_RSSTYPE_IPV6_TCP_EX 0x00000006
+#define IXGBE_RXDADV_RSSTYPE_IPV4_UDP   0x00000007
+#define IXGBE_RXDADV_RSSTYPE_IPV6_UDP   0x00000008
+#define IXGBE_RXDADV_RSSTYPE_IPV6_UDP_EX 0x00000009
+
+/* RSS Packet Types as indicated in the receive descriptor. */
+#define IXGBE_RXDADV_PKTTYPE_NONE       0x00000000
+#define IXGBE_RXDADV_PKTTYPE_IPV4       0x00000010 /* IPv4 hdr present */
+#define IXGBE_RXDADV_PKTTYPE_IPV4_EX    0x00000020 /* IPv4 hdr + extensions */
+#define IXGBE_RXDADV_PKTTYPE_IPV6       0x00000040 /* IPv6 hdr present */
+#define IXGBE_RXDADV_PKTTYPE_IPV6_EX    0x00000080 /* IPv6 hdr + extensions */
+#define IXGBE_RXDADV_PKTTYPE_TCP        0x00000100 /* TCP hdr present */
+#define IXGBE_RXDADV_PKTTYPE_UDP        0x00000200 /* UDP hdr present */
+#define IXGBE_RXDADV_PKTTYPE_SCTP       0x00000400 /* SCTP hdr present */
+#define IXGBE_RXDADV_PKTTYPE_NFS        0x00000800 /* NFS hdr present */
+
+/* Masks to determine if packets should be dropped due to frame errors */
+#define IXGBE_RXD_ERR_FRAME_ERR_MASK ( \
+				      IXGBE_RXD_ERR_CE | \
+				      IXGBE_RXD_ERR_LE | \
+				      IXGBE_RXD_ERR_PE | \
+				      IXGBE_RXD_ERR_OSE | \
+				      IXGBE_RXD_ERR_USE)
+
+#define IXGBE_RXDADV_ERR_FRAME_ERR_MASK ( \
+				      IXGBE_RXDADV_ERR_CE | \
+				      IXGBE_RXDADV_ERR_LE | \
+				      IXGBE_RXDADV_ERR_PE | \
+				      IXGBE_RXDADV_ERR_OSE | \
+				      IXGBE_RXDADV_ERR_USE)
+
+/* Multicast bit mask */
+#define IXGBE_MCSTCTRL_MFE      0x4
+
+/* Number of Transmit and Receive Descriptors must be a multiple of 8 */
+#define IXGBE_REQ_TX_DESCRIPTOR_MULTIPLE  8
+#define IXGBE_REQ_RX_DESCRIPTOR_MULTIPLE  8
+#define IXGBE_REQ_TX_BUFFER_GRANULARITY   1024
+
+/* Vlan-specific macros */
+#define IXGBE_RX_DESC_SPECIAL_VLAN_MASK  0x0FFF /* VLAN ID in lower 12 bits */
+#define IXGBE_RX_DESC_SPECIAL_PRI_MASK   0xE000 /* Priority in upper 3 bits */
+#define IXGBE_RX_DESC_SPECIAL_PRI_SHIFT  0x000D /* Priority in upper 3 of 16 */
+#define IXGBE_TX_DESC_SPECIAL_PRI_SHIFT  IXGBE_RX_DESC_SPECIAL_PRI_SHIFT
+
+/* Transmit Descriptor - Legacy */
+struct ixgbe_legacy_tx_desc {
+	u64 buffer_addr;       /* Address of the descriptor's data buffer */
+	union {
+		u32 data;
+		struct {
+			u16 length;    /* Data buffer length */
+			u8 cso; /* Checksum offset */
+			u8 cmd; /* Descriptor control */
+		} flags;
+	} lower;
+	union {
+		u32 data;
+		struct {
+			u8 status;     /* Descriptor status */
+			u8 css; /* Checksum start */
+			u16 vlan;
+		} fields;
+	} upper;
+};
+
+/* Transmit Descriptor - Advanced */
+union ixgbe_adv_tx_desc {
+	struct {
+		u64 buffer_addr;       /* Address of descriptor's data buf */
+		u32 cmd_type_len;
+		u32 olinfo_status;
+	} read;
+	struct {
+		u64 rsvd;       /* Reserved */
+		u32 nxtseq_seed;
+		u32 status;
+	} wb;
+};
+
+/* Receive Descriptor - Legacy */
+struct ixgbe_legacy_rx_desc {
+	u64 buffer_addr; /* Address of the descriptor's data buffer */
+	u16 length;      /* Length of data DMAed into data buffer */
+	u16 csum;        /* Packet checksum */
+	u8 status;       /* Descriptor status */
+	u8 errors;       /* Descriptor Errors */
+	u16 vlan;
+};
+
+/* Receive Descriptor - Advanced */
+union ixgbe_adv_rx_desc {
+	struct {
+		u64 pkt_addr; /* Packet buffer address */
+		u64 hdr_addr; /* Header buffer address */
+	} read;
+	struct {
+		struct {
+			struct {
+				u16 pkt_info; /* RSS type, Packet type */
+				u16 hdr_info; /* Split Header, header len */
+			} lo_dword;
+			union {
+				u32 rss; /* RSS Hash */
+				struct {
+					u16 ip_id; /* IP id */
+					u16 csum; /* Packet Checksum */
+				} csum_ip;
+			} hi_dword;
+		} lower;
+		struct {
+			u32 status_error; /* ext status/error */
+			u16 length; /* Packet length */
+			u16 vlan; /* VLAN tag */
+		} upper;
+	} wb;  /* writeback */
+};
+
+/* Context descriptors */
+struct ixgbe_adv_tx_context_desc {
+	u32 vlan_macip_lens;
+	u32 seqnum_seed;
+	u32 type_tucmd_mlhl;
+	u32 mss_l4len_idx;
+};
+
+/* Adv Transmit Descriptor Config Masks */
+#define IXGBE_ADVTXD_DTALEN_MASK      0x0000FFFF /* Data buffer length(bytes) */
+#define IXGBE_ADVTXD_DTYP_MASK  0x00F00000 /* DTYP mask */
+#define IXGBE_ADVTXD_DTYP_CTXT  0x00200000 /* Advanced Context Desc */
+#define IXGBE_ADVTXD_DTYP_DATA  0x00300000 /* Advanced Data Descriptor */
+#define IXGBE_ADVTXD_DCMD_EOP   IXGBE_TXD_CMD_EOP  /* End of Packet */
+#define IXGBE_ADVTXD_DCMD_IFCS  IXGBE_TXD_CMD_IFCS /* Insert FCS */
+#define IXGBE_ADVTXD_DCMD_RDMA  0x04000000 /* RDMA */
+#define IXGBE_ADVTXD_DCMD_RS    IXGBE_TXD_CMD_RS   /* Report Status */
+#define IXGBE_ADVTXD_DCMD_DDTYP_ISCSI 0x10000000     /* DDP hdr type or iSCSI */
+#define IXGBE_ADVTXD_DCMD_DEXT  IXGBE_TXD_CMD_DEXT /* Desc ext (1=Adv) */
+#define IXGBE_ADVTXD_DCMD_VLE   IXGBE_TXD_CMD_VLE  /* VLAN pkt enable */
+#define IXGBE_ADVTXD_DCMD_TSE   0x80000000 /* TCP Seg enable */
+#define IXGBE_ADVTXD_STAT_DD    IXGBE_TXD_STAT_DD  /* Descriptor Done */
+#define IXGBE_ADVTXD_STAT_SN_CRC      0x00000002 /* NXTSEQ/SEED present in WB */
+#define IXGBE_ADVTXD_STAT_RSV   0x0000000C /* STA Reserved */
+#define IXGBE_ADVTXD_IDX_SHIFT  4 /* Adv desc Index shift */
+#define IXGBE_ADVTXD_POPTS_SHIFT      8  /* Adv desc POPTS shift */
+#define IXGBE_ADVTXD_POPTS_IXSM (IXGBE_TXD_POPTS_IXSM << \
+				IXGBE_ADVTXD_POPTS_SHIFT)
+#define IXGBE_ADVTXD_POPTS_TXSM (IXGBE_TXD_POPTS_TXSM << \
+				IXGBE_ADVTXD_POPTS_SHIFT)
+#define IXGBE_ADVTXD_POPTS_EOM  0x00000400 /* Enable L bit-RDMA DDP hdr */
+#define IXGBE_ADVTXD_POPTS_ISCO_1ST   0x00000000 /* 1st TSO of iSCSI PDU */
+#define IXGBE_ADVTXD_POPTS_ISCO_MDL   0x00000800 /* Middle TSO of iSCSI PDU */
+#define IXGBE_ADVTXD_POPTS_ISCO_LAST  0x00001000 /* Last TSO of iSCSI PDU */
+#define IXGBE_ADVTXD_POPTS_ISCO_FULL 0x00001800 /* 1st&Last TSO-full iSCSI PDU*/
+#define IXGBE_ADVTXD_POPTS_RSV  0x00002000 /* POPTS Reserved */
+#define IXGBE_ADVTXD_PAYLEN_SHIFT  14 /* Adv desc PAYLEN shift */
+#define IXGBE_ADVTXD_MACLEN_SHIFT  9  /* Adv ctxt desc mac len shift */
+#define IXGBE_ADVTXD_VLAN_SHIFT    16  /* Adv ctxt vlan tag shift */
+#define IXGBE_ADVTXD_TUCMD_IPV4    0x00000400  /* IP Packet Type: 1=IPv4 */
+#define IXGBE_ADVTXD_TUCMD_IPV6    0x00000000  /* IP Packet Type: 0=IPv6 */
+#define IXGBE_ADVTXD_TUCMD_L4T_UDP 0x00000000  /* L4 Packet TYPE of UDP */
+#define IXGBE_ADVTXD_TUCMD_L4T_TCP 0x00000800  /* L4 Packet TYPE of TCP */
+#define IXGBE_ADVTXD_TUCMD_MKRREQ  0x00002000 /* Req requires Markers and CRC */
+#define IXGBE_ADVTXD_L4LEN_SHIFT   8  /* Adv ctxt L4LEN shift */
+#define IXGBE_ADVTXD_MSS_SHIFT     16  /* Adv ctxt MSS shift */
+
+#define IXGBE_LINK_SPEED_UNKNOWN   0
+#define IXGBE_LINK_SPEED_1GB_FULL  0x0020
+#define IXGBE_LINK_SPEED_10GB_FULL 0x0080
+
+
+enum ixgbe_eeprom_type {
+	ixgbe_eeprom_uninitialized = 0,
+	ixgbe_eeprom_spi,
+	ixgbe_eeprom_none /* No NVM support */
+};
+
+enum ixgbe_mac_type {
+	ixgbe_mac_unknown = 0,
+	ixgbe_mac_82598EB,
+	ixgbe_num_macs
+};
+
+enum ixgbe_phy_type {
+	ixgbe_phy_unknown = 0,
+	ixgbe_phy_tn,
+	ixgbe_phy_qt,
+	ixgbe_phy_xaui
+};
+
+enum ixgbe_media_type {
+	ixgbe_media_type_unknown = 0,
+	ixgbe_media_type_fiber,
+	ixgbe_media_type_copper,
+	ixgbe_media_type_backplane
+};
+
+/* Flow Control Settings */
+enum ixgbe_fc_type {
+	ixgbe_fc_none = 0,
+	ixgbe_fc_rx_pause,
+	ixgbe_fc_tx_pause,
+	ixgbe_fc_full,
+	ixgbe_fc_default
+};
+
+/* PCI bus types */
+enum ixgbe_bus_type {
+	ixgbe_bus_type_unknown = 0,
+	ixgbe_bus_type_pci,
+	ixgbe_bus_type_pcix,
+	ixgbe_bus_type_pci_express,
+	ixgbe_bus_type_reserved
+};
+
+/* PCI bus speeds */
+enum ixgbe_bus_speed {
+	ixgbe_bus_speed_unknown = 0,
+	ixgbe_bus_speed_33,
+	ixgbe_bus_speed_66,
+	ixgbe_bus_speed_100,
+	ixgbe_bus_speed_120,
+	ixgbe_bus_speed_133,
+	ixgbe_bus_speed_2500,
+	ixgbe_bus_speed_5000,
+	ixgbe_bus_speed_reserved
+};
+
+/* PCI bus widths */
+enum ixgbe_bus_width {
+	ixgbe_bus_width_unknown = 0,
+	ixgbe_bus_width_pcie_x1,
+	ixgbe_bus_width_pcie_x2,
+	ixgbe_bus_width_pcie_x4,
+	ixgbe_bus_width_pcie_x8,
+	ixgbe_bus_width_32,
+	ixgbe_bus_width_64,
+	ixgbe_bus_width_reserved
+};
+
+struct ixgbe_eeprom_info {
+	enum ixgbe_eeprom_type type;
+	u16 word_size;
+	u16 address_bits;
+};
+
+struct ixgbe_addr_filter_info {
+	u32 num_mc_addrs;
+	u32 rar_used_count;
+	u32 mc_addr_in_rar_count;
+	u32 mta_in_use;
+};
+
+/* Bus parameters */
+struct ixgbe_bus_info {
+	enum ixgbe_bus_speed speed;
+	enum ixgbe_bus_width width;
+	enum ixgbe_bus_type type;
+};
+
+/* Flow control parameters */
+struct ixgbe_fc_info {
+	u32 high_water; /* Flow Control High-water */
+	u32 low_water; /* Flow Control Low-water */
+	u16 pause_time; /* Flow Control Pause timer */
+	bool send_xon; /* Flow control send XON */
+	bool strict_ieee; /* Strict IEEE mode */
+	enum ixgbe_fc_type type; /* Type of flow control */
+	enum ixgbe_fc_type original_type;
+};
+
+/* Statistics counters collected by the MAC */
+struct ixgbe_hw_stats {
+	u64 crcerrs;
+	u64 illerrc;
+	u64 errbc;
+	u64 mspdc;
+	u64 mpctotal;
+	u64 mpc[8];
+	u64 mlfc;
+	u64 mrfc;
+	u64 rlec;
+	u64 lxontxc;
+	u64 lxonrxc;
+	u64 lxofftxc;
+	u64 lxoffrxc;
+	u64 pxontxc[8];
+	u64 pxonrxc[8];
+	u64 pxofftxc[8];
+	u64 pxoffrxc[8];
+	u64 prc64;
+	u64 prc127;
+	u64 prc255;
+	u64 prc511;
+	u64 prc1023;
+	u64 prc1522;
+	u64 gprc;
+	u64 bprc;
+	u64 mprc;
+	u64 gptc;
+	u64 gorc;
+	u64 gotc;
+	u64 rnbc[8];
+	u64 ruc;
+	u64 rfc;
+	u64 roc;
+	u64 rjc;
+	u64 mngprc;
+	u64 mngpdc;
+	u64 mngptc;
+	u64 tor;
+	u64 tpr;
+	u64 tpt;
+	u64 ptc64;
+	u64 ptc127;
+	u64 ptc255;
+	u64 ptc511;
+	u64 ptc1023;
+	u64 ptc1522;
+	u64 mptc;
+	u64 bptc;
+	u64 xec;
+	u64 rqsmr[16];
+	u64 tqsmr[8];
+	u64 qprc[16];
+	u64 qptc[16];
+	u64 qbrc[16];
+	u64 qbtc[16];
+};
+
+/* forward declaration */
+struct ixgbe_hw;
+
+/* Function pointer table */
+struct ixgbe_functions {
+	s32 (*ixgbe_func_init_hw)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_reset_hw)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_start_hw)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_clear_hw_cntrs)(struct ixgbe_hw *);
+	enum ixgbe_media_type (*ixgbe_func_get_media_type)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_get_mac_addr)(struct ixgbe_hw *, u8 *);
+	u32 (*ixgbe_func_get_num_of_tx_queues)(struct ixgbe_hw *);
+	u32 (*ixgbe_func_get_num_of_rx_queues)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_stop_adapter)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_get_bus_info)(struct ixgbe_hw *);
+
+	/* PHY */
+	s32 (*ixgbe_func_identify_phy)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_reset_phy)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_read_phy_reg)(struct ixgbe_hw *, u32, u32, u16 *);
+	s32 (*ixgbe_func_write_phy_reg)(struct ixgbe_hw *, u32, u32, u16);
+	s32 (*ixgbe_func_setup_phy_link)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_setup_phy_link_speed)(struct ixgbe_hw *,
+					       u32, bool, bool);
+	s32 (*ixgbe_func_check_phy_link)(struct ixgbe_hw *, u32 *,
+					 bool *);
+
+	/* Link */
+	s32 (*ixgbe_func_setup_link)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_setup_link_speed)(struct ixgbe_hw *, u32,
+					   bool, bool);
+	s32 (*ixgbe_func_check_link)(struct ixgbe_hw *, u32 *,
+				     bool *);
+	s32 (*ixgbe_func_get_link_settings)(struct ixgbe_hw *,
+					    u32 *,
+					    bool *);
+
+	/* LED */
+	s32 (*ixgbe_func_led_on)(struct ixgbe_hw *, u32);
+	s32 (*ixgbe_func_led_off)(struct ixgbe_hw *, u32);
+	s32 (*ixgbe_func_blink_led_start)(struct ixgbe_hw *, u32);
+	s32 (*ixgbe_func_blink_led_stop)(struct ixgbe_hw *, u32);
+
+	/* EEPROM */
+	s32 (*ixgbe_func_init_eeprom_params)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_read_eeprom)(struct ixgbe_hw *, u16, u16 *);
+	s32 (*ixgbe_func_write_eeprom)(struct ixgbe_hw *, u16, u16);
+	s32 (*ixgbe_func_validate_eeprom_checksum)(struct ixgbe_hw *, u16 *);
+	s32 (*ixgbe_func_update_eeprom_checksum)(struct ixgbe_hw *);
+
+	/* RAR, Multicast, VLAN */
+	s32 (*ixgbe_func_set_rar)(struct ixgbe_hw *, u32, u8 *, u32 , u32);
+	s32 (*ixgbe_func_init_rx_addrs)(struct ixgbe_hw *);
+	u32 (*ixgbe_func_get_num_rx_addrs)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_update_mc_addr_list)(struct ixgbe_hw *, u8 *, u32,
+					      u32);
+	s32 (*ixgbe_func_enable_mc)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_disable_mc)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_clear_vfta)(struct ixgbe_hw *);
+	s32 (*ixgbe_func_set_vfta)(struct ixgbe_hw *, u32, u32, bool);
+
+	/* Flow Control */
+	s32 (*ixgbe_func_setup_fc)(struct ixgbe_hw *, s32);
+};
+
+struct ixgbe_mac_info {
+	enum ixgbe_mac_type type;
+	u8                  addr[IXGBE_ETH_LENGTH_OF_ADDRESS];
+	u8                  perm_addr[IXGBE_ETH_LENGTH_OF_ADDRESS];
+	s32                 mc_filter_type;
+	u32                 link_attach_type;
+	u32                 link_mode_select;
+	bool                link_settings_loaded;
+};
+
+struct ixgbe_phy_info {
+	enum ixgbe_phy_type             type;
+	u32                             addr;
+	u32                             id;
+	u32                             revision;
+	enum ixgbe_media_type           media_type;
+	u32				autoneg_advertised;
+	bool                            autoneg_wait_to_complete;
+};
+
+struct ixgbe_hw {
+	u8 __iomem                      *hw_addr;
+	void                            *back;
+	struct ixgbe_functions          func;
+	struct ixgbe_mac_info           mac;
+	struct ixgbe_addr_filter_info   addr_ctrl;
+	struct ixgbe_fc_info            fc;
+	struct ixgbe_phy_info           phy;
+	struct ixgbe_eeprom_info        eeprom;
+	struct ixgbe_bus_info           bus;
+	u16                             device_id;
+	u16                             vendor_id;
+	u16                             subsystem_device_id;
+	u16                             subsystem_vendor_id;
+	u8                              revision_id;
+	bool                            adapter_stopped;
+};
+
+#define ixgbe_func_from_hw_struct(hw, _func) hw->func._func
+
+#define ixgbe_call_func(hw, func, params, error) \
+		(ixgbe_func_from_hw_struct(hw, func) != NULL) ? \
+		ixgbe_func_from_hw_struct(hw, func) params: error
+
+/* Error Codes */
+#define IXGBE_SUCCESS                           0
+#define IXGBE_ERR_EEPROM                        -1
+#define IXGBE_ERR_EEPROM_CHECKSUM               -2
+#define IXGBE_ERR_PHY                           -3
+#define IXGBE_ERR_CONFIG                        -4
+#define IXGBE_ERR_PARAM                         -5
+#define IXGBE_ERR_MAC_TYPE                      -6
+#define IXGBE_ERR_UNKNOWN_PHY                   -7
+#define IXGBE_ERR_LINK_SETUP                    -8
+#define IXGBE_ERR_ADAPTER_STOPPED               -9
+#define IXGBE_ERR_INVALID_MAC_ADDR              -10
+#define IXGBE_ERR_DEVICE_NOT_SUPPORTED          -11
+#define IXGBE_ERR_MASTER_REQUESTS_PENDING       -12
+#define IXGBE_ERR_INVALID_LINK_SETTINGS         -13
+#define IXGBE_ERR_AUTONEG_NOT_COMPLETE          -14
+#define IXGBE_ERR_RESET_FAILED                  -15
+#define IXGBE_ERR_SWFW_SYNC                     -16
+#define IXGBE_ERR_PHY_ADDR_INVALID              -17
+#define IXGBE_NOT_IMPLEMENTED                   0x7FFFFFFF
+
+#endif /* _IXGBE_TYPE_H_ */
-- 
1.5.0.6


^ permalink raw reply related

* Re: [BNX2]: Fix suspend/resume problem.
From: David Miller @ 2007-08-04  3:57 UTC (permalink / raw)
  To: mchan; +Cc: netdev
In-Reply-To: <1186180354.18322.79.camel@dell>

From: "Michael Chan" <mchan@broadcom.com>
Date: Fri, 03 Aug 2007 15:32:34 -0700

> [BNX2]: Fix suspend/resume problem.
> 
> The device would not resume properly if it was shutdown before the system
> was suspended.  In such scenario where the netif_running state is 0,
> bnx2_suspend() would not save the PCI state and so the memory enable bit
> and bus master enable bit would be lost.
> 
> We fix this by always saving and restoring the PCI state in
> bnx2_suspend() and bnx2_resume() regardless of netif_running() state.
> 
> Update version to 1.6.4.
> 
> Signed-off-by: Michael Chan <mchan@broadcom.com>

Also applied, thanks Michael.

^ permalink raw reply

* Re: [REGRESSION] tg3 dead after s2ram
From: David Miller @ 2007-08-04  3:57 UTC (permalink / raw)
  To: mchan
  Cc: joachim.deguara, akpm, linux-kernel, michal.k.k.piotrowski,
	netdev, linux-acpi
In-Reply-To: <1186081829.18322.20.camel@dell>

From: "Michael Chan" <mchan@broadcom.com>
Date: Thu, 02 Aug 2007 12:10:29 -0700

> [TG3]: Fix suspend/resume problem.
> 
> Joachim Deguara <joachim.deguara@amd.com> reported that tg3 devices
> would not resume properly if the device was shutdown before the system
> was suspended.  In such scenario where the netif_running state is 0,
> tg3_suspend() would not save the PCI state and so the memory enable bit
> and bus master enable bit would be lost.
> 
> We fix this by always saving and restoring the PCI state in
> tg3_suspend() and tg3_resume() regardless of netif_running() state.
> 
> Signed-off-by: Michael Chan <mchan@broadcom.com>

Patch applied.

^ permalink raw reply

* Re: Distributed storage.
From: Manu Abraham @ 2007-08-04  3:44 UTC (permalink / raw)
  To: Dave Dillow; +Cc: Evgeniy Polyakov, netdev, linux-kernel, linux-fsdevel
In-Reply-To: <1186195868.16431.5.camel@obelisk.thedillows.org>

On 8/4/07, Dave Dillow <dave@thedillows.org> wrote:
> On Fri, 2007-08-03 at 09:04 +0400, Manu Abraham wrote:
> > On 7/31/07, Evgeniy Polyakov <johnpol@2ka.mipt.ru> wrote:
> >
> > > TODO list currently includes following main items:
> > >     * redundancy algorithm (drop me a request of your own, but it is highly
> > >         unlikley that Reed-Solomon based will ever be used - it is too slow
> > >         for distributed RAID, I consider WEAVER codes)
> >
> >
> > LDPC codes[1][2] have been replacing Turbo code[3] with regards to
> > communication links and we have been seeing that transition. (maybe
> > helpful, came to mind seeing the mention of Turbo code) Don't know how
> > weaver compares to LDPC, though found some comparisons [4][5] But
> > looking at fault tolerance figures, i guess Weaver is much better.
> >
> > [1] http://www.ldpc-codes.com/
> > [2] http://portal.acm.org/citation.cfm?id=1240497
> > [3] http://en.wikipedia.org/wiki/Turbo_code
> > [4] http://domino.research.ibm.com/library/cyberdig.nsf/papers/BD559022A190D41C85257212006CEC11/$File/rj10391.pdf
> > [5] http://hplabs.hp.com/personal/Jay_Wylie/publications/wylie_dsn2007.pdf
>
> Searching Google for Dr. Plank's work at the University of TN turns up
> some analysis of using LDPC codes in storage systems.
>
> http://www.google.com/search?hl=en&q=plank+ldpc&btnG=Google+Search
>
> Patents are an issue to watch out for around the use of Tornado/Raptor
> codes. I've not researched it, but I believe there be dragons there.
>

We don't use the code in the driver straight away [2] (in the case
that i mentioned), since that happens in the hardware (demodulator
chip) [1], but we have an interface for selecting the code-rate [2]
(LDPC/BCH) for DVB-S2 and the new papers for DVB-T2 looks geared that
the base decision is to use LDPC.

Though i now see a patent application for it [3]. Not sure whether it
is a registered patent, i am under an agreement of Non-Disclosure with
STM. Will ask the relevant person there, whether they have it
registered. (Most probably they may have it registered).

There are a few people from STM on LK, if not they can possibly
confirm whether the patent is regsitered or not.

[1] http://www2.dac.com/data2/42nd/42acceptedpapers.nsf/0c4c09c6ffa905c487256b7b007afb72/998f93e4b29e99fa87256fc400714617/$FILE/33_1.pdf

[2] http://linuxtv.org/hg/~manu/stb0899-c5/file/760cb230695c/linux/include/linux/dvb/frontend.h

[3] http://www.freepatentsonline.com/20060206779.html
http://www.freepatentsonline.com/20060206778.html

^ permalink raw reply

* [RFC 1/2][BNX2]: Add iSCSI support to BNX2 devices.
From: Michael Chan @ 2007-08-04  4:04 UTC (permalink / raw)
  To: davem, mchristi, netdev, open-iscsi; +Cc: anilgv, talm, lusinsky, uri

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 81ef81c..b23c142 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2237,6 +2237,16 @@ config BNX2
 	  To compile this driver as a module, choose M here: the module
 	  will be called bnx2.  This is recommended.
 
+config CNIC
+	tristate "Broadcom CNIC support"
+	depends on BNX2
+	help
+	  This driver supports offload features of Broadcom NetXtremeII
+	  gigabit Ethernet cards.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called cnic.  This is recommended.
+
 config SPIDER_NET
 	tristate "Spider Gigabit Ethernet driver"
 	depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB)
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index e684212..3b5f057 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_STNIC) += stnic.o 8390.o
 obj-$(CONFIG_FEALNX) += fealnx.o
 obj-$(CONFIG_TIGON3) += tg3.o
 obj-$(CONFIG_BNX2) += bnx2.o
+obj-$(CONFIG_CNIC) += cnic.o
 spidernet-y += spider_net.o spider_net_ethtool.o
 obj-$(CONFIG_SPIDER_NET) += spidernet.o sungem_phy.o
 obj-$(CONFIG_GELIC_NET) += ps3_gelic.o
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index d53dfc5..a0a0a83 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -242,7 +242,7 @@ static inline u32 bnx2_tx_avail(struct bnx2 *bp)
 	return (bp->tx_ring_size - diff);
 }
 
-static u32
+u32
 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset)
 {
 	u32 val;
@@ -254,7 +254,7 @@ bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset)
 	return val;
 }
 
-static void
+void
 bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val)
 {
 	spin_lock_bh(&bp->indirect_lock);
@@ -263,7 +263,7 @@ bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val)
 	spin_unlock_bh(&bp->indirect_lock);
 }
 
-static void
+void
 bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val)
 {
 	offset += cid_addr;
@@ -447,6 +447,26 @@ bnx2_netif_start(struct bnx2 *bp)
 }
 
 static void
+bnx2_cnic_stop(struct bnx2 *bp)
+{
+	struct cnic_ops *c_ops;
+
+	c_ops = rcu_dereference(bp->cnic_ops);
+	if (c_ops)
+		c_ops->cnic_stop(bp->cnic_data);
+}
+
+static void
+bnx2_cnic_start(struct bnx2 *bp)
+{
+	struct cnic_ops *c_ops;
+
+	c_ops = rcu_dereference(bp->cnic_ops);
+	if (c_ops)
+		c_ops->cnic_start(bp->cnic_data);
+}
+
+static void
 bnx2_free_mem(struct bnx2 *bp)
 {
 	int i;
@@ -2628,6 +2648,9 @@ bnx2_has_work(struct bnx2 *bp)
 	    (sblk->status_attn_bits_ack & STATUS_ATTN_EVENTS))
 		return 1;
 
+	if (rcu_dereference(bp->cnic_ops) && (bp->cnic_tag != sblk->status_idx))
+		return 1;
+
 	return 0;
 }
 
@@ -2638,6 +2661,7 @@ bnx2_poll(struct net_device *dev, int *budget)
 	struct status_block *sblk = bp->status_blk;
 	u32 status_attn_bits = sblk->status_attn_bits;
 	u32 status_attn_bits_ack = sblk->status_attn_bits_ack;
+	struct cnic_ops *c_ops;
 
 	if ((status_attn_bits & STATUS_ATTN_EVENTS) !=
 	    (status_attn_bits_ack & STATUS_ATTN_EVENTS)) {
@@ -2667,6 +2691,13 @@ bnx2_poll(struct net_device *dev, int *budget)
 		dev->quota -= work_done;
 	}
 
+	rcu_read_lock();
+	c_ops = rcu_dereference(bp->cnic_ops);
+	if (c_ops)
+		bp->cnic_tag = c_ops->cnic_handler(bp->cnic_data,
+						   bp->status_blk);
+	rcu_read_unlock();
+
 	bp->last_status_idx = bp->status_blk->status_idx;
 	rmb();
 
@@ -2692,6 +2723,53 @@ bnx2_poll(struct net_device *dev, int *budget)
 	return 1;
 }
 
+int bnx2_register_cnic(struct net_device *dev, struct cnic_ops *ops, void *data)
+{
+	struct bnx2 *bp = netdev_priv(dev);
+
+	if (ops == NULL)
+		return -EINVAL;
+
+	if (!try_module_get(ops->cnic_owner))
+		return -EBUSY;
+
+	bp->cnic_data = data;
+	rcu_assign_pointer(bp->cnic_ops, ops);
+
+	if (netif_running(bp->dev)) {
+		struct status_block *status_blk = bp->status_blk;
+		int i = 0;
+
+		while (status_blk->status_completion_producer_index && i < 10) {
+			REG_WR(bp, BNX2_HC_COMMAND,
+			       bp->hc_cmd | BNX2_HC_COMMAND_COAL_NOW_WO_INT);
+			REG_RD(bp, BNX2_HC_COMMAND);
+			udelay(10);
+			i++;
+			/* Tell compiler status_blk fields can change. */
+			barrier();
+		}
+		if (status_blk->status_completion_producer_index) {
+			printk(KERN_ERR PFX "%s: "
+			       "KCQ index not resetting to 0.\n",
+			       bp->dev->name);
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+int bnx2_unregister_cnic(struct net_device *dev)
+{
+	struct bnx2 *bp = netdev_priv(dev);
+
+	module_put(bp->cnic_ops->cnic_owner);
+	rcu_assign_pointer(bp->cnic_ops, NULL);
+	synchronize_rcu();
+	return 0;
+}
+
 /* Called with rtnl_lock from vlan functions and also netif_tx_lock
  * from set_multicast.
  */
@@ -2767,7 +2845,7 @@ bnx2_set_rx_mode(struct net_device *dev)
 	spin_unlock_bh(&bp->phy_lock);
 }
 
-#define FW_BUF_SIZE	0x8000
+#define FW_BUF_SIZE	0x10000
 
 static int
 bnx2_gunzip_init(struct bnx2 *bp)
@@ -3107,13 +3185,13 @@ bnx2_init_cpus(struct bnx2 *bp)
 	cpu_reg.spad_base = BNX2_CP_SCRATCH;
 	cpu_reg.mips_view_base = 0x8000000;
 
-	if (CHIP_NUM(bp) == CHIP_NUM_5709) {
+	if (CHIP_NUM(bp) == CHIP_NUM_5709)
 		fw = &bnx2_cp_fw_09;
+	else
+		fw = &bnx2_cp_fw_06;
+
+	rc = load_cpu_fw(bp, &cpu_reg, fw);
 
-		rc = load_cpu_fw(bp, &cpu_reg, fw);
-		if (rc)
-			goto init_cpu_err;
-	}
 init_cpu_err:
 	bnx2_gunzip_end(bp);
 	return rc;
@@ -5112,12 +5190,14 @@ bnx2_reset_task(struct work_struct *work)
 		return;
 
 	bp->in_reset_task = 1;
+	bnx2_cnic_stop(bp);
 	bnx2_netif_stop(bp);
 
 	bnx2_init_nic(bp);
 
 	atomic_set(&bp->intr_sem, 1);
 	bnx2_netif_start(bp);
+	bnx2_cnic_start(bp);
 	bp->in_reset_task = 0;
 }
 
@@ -5803,9 +5883,11 @@ bnx2_set_coalesce(struct net_device *dev, struct ethtool_coalesce *coal)
 	bp->stats_ticks &= BNX2_HC_STATS_TICKS_HC_STAT_TICKS;
 
 	if (netif_running(bp->dev)) {
+		bnx2_cnic_stop(bp);
 		bnx2_netif_stop(bp);
 		bnx2_init_nic(bp);
 		bnx2_netif_start(bp);
+		bnx2_cnic_start(bp);
 	}
 
 	return 0;
@@ -5839,7 +5921,9 @@ bnx2_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering)
 
 		return -EINVAL;
 	}
+
 	if (netif_running(bp->dev)) {
+		bnx2_cnic_stop(bp);
 		bnx2_netif_stop(bp);
 		bnx2_reset_chip(bp, BNX2_DRV_MSG_CODE_RESET);
 		bnx2_free_skbs(bp);
@@ -5857,6 +5941,7 @@ bnx2_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering)
 			return rc;
 		bnx2_init_nic(bp);
 		bnx2_netif_start(bp);
+		bnx2_cnic_start(bp);
 	}
 
 	return 0;
@@ -6082,6 +6167,7 @@ bnx2_self_test(struct net_device *dev, struct ethtool_test *etest, u64 *buf)
 	if (etest->flags & ETH_TEST_FL_OFFLINE) {
 		int i;
 
+		bnx2_cnic_stop(bp);
 		bnx2_netif_stop(bp);
 		bnx2_reset_chip(bp, BNX2_DRV_MSG_CODE_DIAG);
 		bnx2_free_skbs(bp);
@@ -6103,6 +6189,7 @@ bnx2_self_test(struct net_device *dev, struct ethtool_test *etest, u64 *buf)
 		else {
 			bnx2_init_nic(bp);
 			bnx2_netif_start(bp);
+			bnx2_cnic_start(bp);
 		}
 
 		/* wait for link up */
@@ -6354,11 +6441,13 @@ bnx2_change_mtu(struct net_device *dev, int new_mtu)
 
 	dev->mtu = new_mtu;
 	if (netif_running(dev)) {
+		bnx2_cnic_stop(bp);
 		bnx2_netif_stop(bp);
 
 		bnx2_init_nic(bp);
 
 		bnx2_netif_start(bp);
+		bnx2_cnic_start(bp);
 	}
 	return 0;
 }
@@ -6941,6 +7030,7 @@ bnx2_suspend(struct pci_dev *pdev, pm_message_t state)
 		return 0;
 
 	flush_scheduled_work();
+	bnx2_cnic_stop(bp);
 	bnx2_netif_stop(bp);
 	netif_device_detach(dev);
 	del_timer_sync(&bp->timer);
@@ -6971,6 +7061,7 @@ bnx2_resume(struct pci_dev *pdev)
 	netif_device_attach(dev);
 	bnx2_init_nic(bp);
 	bnx2_netif_start(bp);
+	bnx2_cnic_start(bp);
 	return 0;
 }
 
@@ -6996,5 +7087,8 @@ static void __exit bnx2_cleanup(void)
 module_init(bnx2_init);
 module_exit(bnx2_cleanup);
 
-
-
+EXPORT_SYMBOL(bnx2_register_cnic);
+EXPORT_SYMBOL(bnx2_unregister_cnic);
+EXPORT_SYMBOL(bnx2_reg_rd_ind);
+EXPORT_SYMBOL(bnx2_reg_wr_ind);
+EXPORT_SYMBOL(bnx2_ctx_wr);
diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h
index 102adfe..75929c7 100644
--- a/drivers/net/bnx2.h
+++ b/drivers/net/bnx2.h
@@ -6465,6 +6465,16 @@ struct flash_spec {
 	u8  *name;
 };
 
+struct cnic_ops {
+	struct module		*cnic_owner;
+	void			(*cnic_stop)(void *);
+	void			(*cnic_start)(void *);
+	int			(*cnic_handler)(void *, struct status_block *);
+};
+
+extern int bnx2_register_cnic(struct net_device *, struct cnic_ops *, void *);
+extern int bnx2_unregister_cnic(struct net_device *);
+
 struct bnx2 {
 	/* Fields used in the tx and intr/napi performance paths are grouped */
 	/* together in the beginning of the structure. */
@@ -6524,6 +6534,10 @@ struct bnx2 {
 	int		tx_ring_size;
 	u32		tx_wake_thresh;
 
+	struct cnic_ops		*cnic_ops;
+	void			*cnic_data;
+	int			cnic_tag;
+
 	/* End of fields used in the performance code paths. */
 
 	char			*name;
@@ -6635,6 +6649,7 @@ struct bnx2 {
 
 	u16			req_line_speed;
 	u8			req_duplex;
+	u8			req_port;
 
 	u8			phy_port;
 	u8			link_up;
@@ -6684,8 +6699,10 @@ struct bnx2 {
 	void			*gunzip_buf;
 };
 
-static u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset);
-static void bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val);
+extern u32 bnx2_reg_rd_ind(struct bnx2 *bp, u32 offset);
+extern void bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val);
+extern u32 bnx2_reg_rd_ind_locked(struct bnx2 *bp, u32 offset);
+extern void bnx2_reg_wr_ind_locked(struct bnx2 *bp, u32 offset, u32 val);
 
 #define REG_RD(bp, offset)					\
 	readl(bp->regview + offset)
@@ -6704,7 +6721,9 @@ static void bnx2_reg_wr_ind(struct bnx2 *bp, u32 offset, u32 val);
 
 /* Indirect context access.  Unlike the MBQ_WR, these macros will not
  * trigger a chip event. */
-static void bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val);
+extern void bnx2_ctx_wr(struct bnx2 *bp, u32 cid_addr, u32 offset, u32 val);
+extern void bnx2_ctx_wr_locked(struct bnx2 *bp, u32 cid_addr, u32 offset,
+			       u32 val);
 
 #define CTX_WR(bp, cid_addr, offset, val)			\
 	bnx2_ctx_wr(bp, cid_addr, offset, val)
diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c
new file mode 100644
index 0000000..207fd19
--- /dev/null
+++ b/drivers/net/cnic.c
@@ -0,0 +1,1885 @@
+/* cnic.c: Broadcom CNIC core network driver.
+ *
+ * Copyright (c) 2006-2007 Broadcom Corporation
+ *
+ * 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.
+ *
+ * Written by: John(Zongxi) Chen (zongxi@broadcom.com)
+ * Modified and maintained by: Michael Chan <mchan@broadcom.com>
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/inetdevice.h>
+#include <linux/in.h>
+#include <linux/dma-mapping.h>
+#include <asm/byteorder.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/if_vlan.h>
+#define BCM_VLAN 1
+#include <net/ip.h>
+#include <net/tcp.h>
+#include <linux/workqueue.h>
+#include <net/arp.h>
+#include <net/neighbour.h>
+#include <net/route.h>
+#include <net/netevent.h>
+
+#include "bnx2.h"
+#include "cnic_if.h"
+#include "cnic.h"
+#include "cnic_cm.h"
+
+#define DRV_MODULE_NAME 	"cnic"
+#define PFX DRV_MODULE_NAME ": "
+
+static char version[] __devinitdata =
+	"Broadcom NetXtreme II CNIC Driver " DRV_MODULE_NAME " v" CNIC_MODULE_VERSION " (" CNIC_MODULE_RELDATE ")\n";
+
+MODULE_AUTHOR("John(Zongxi) Chen <zongxic@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom NetXtreme II CNIC Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(CNIC_MODULE_VERSION);
+
+static LIST_HEAD(cnic_dev_list);
+static DEFINE_RWLOCK(cnic_dev_lock);
+static DEFINE_MUTEX(cnic_lock);
+
+struct cnic_ulp_ops *cnic_ulp_tbl[MAX_CNIC_ULP_TYPE];
+
+static int cnic_service_kq(void *, struct status_block *);
+static void cnic_service_stop(void *);
+static void cnic_service_start(void *);
+
+static struct cnic_ops my_cnic_ops = {
+	.cnic_owner	= THIS_MODULE,
+	.cnic_handler	= cnic_service_kq,
+	.cnic_stop	= cnic_service_stop,
+	.cnic_start	= cnic_service_start,
+};
+
+static inline void cnic_hold(struct cnic_dev *dev)
+{
+	atomic_inc(&dev->ref_count);
+}
+
+static inline void cnic_put(struct cnic_dev *dev)
+{
+	atomic_dec(&dev->ref_count);
+}
+
+int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops)
+{
+	struct cnic_dev *dev;
+
+	if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+		printk(KERN_ERR PFX "cnic_register_driver: Bad type %d\n",
+		       ulp_type);
+		return -EINVAL;
+	}
+	mutex_lock(&cnic_lock);
+	if (cnic_ulp_tbl[ulp_type]) {
+		printk(KERN_ERR PFX "cnic_register_driver: Type %d has already "
+				    "been registered\n", ulp_type);
+		mutex_unlock(&cnic_lock);
+		return -EBUSY;
+	}
+
+	read_lock(&cnic_dev_lock);
+	list_for_each_entry(dev, &cnic_dev_list, list) {
+		struct cnic_local *cp = dev->cnic_priv;
+
+		clear_bit(ULP_F_INIT, &cp->ulp_flags[ulp_type]);
+	}
+	read_unlock(&cnic_dev_lock);
+
+	rcu_assign_pointer(cnic_ulp_tbl[ulp_type], ulp_ops);
+	mutex_unlock(&cnic_lock);
+
+	read_lock(&cnic_dev_lock);
+	list_for_each_entry(dev, &cnic_dev_list, list) {
+		struct cnic_local *cp = dev->cnic_priv;
+
+		/* Prevent race conditions with netdev_event */
+		rtnl_lock();
+		if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[ulp_type]))
+			ulp_ops->cnic_init(dev);
+		rtnl_unlock();
+	}
+	read_unlock(&cnic_dev_lock);
+
+	return 0;
+}
+
+int cnic_unregister_driver(int ulp_type)
+{
+	struct cnic_dev *dev;
+
+	if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+		printk(KERN_ERR PFX "cnic_unregister_driver: Bad type %d\n",
+		       ulp_type);
+		return -EINVAL;
+	}
+	mutex_lock(&cnic_lock);
+	if (!cnic_ulp_tbl[ulp_type]) {
+		printk(KERN_ERR PFX "cnic_unregister_driver: Type %d has not "
+				    "been registered\n", ulp_type);
+		goto out_unlock;
+	}
+	read_lock(&cnic_dev_lock);
+	list_for_each_entry(dev, &cnic_dev_list, list) {
+		struct cnic_local *cp = dev->cnic_priv;
+		
+		if (rcu_dereference(cp->ulp_ops[ulp_type])) {
+			printk(KERN_ERR PFX "cnic_unregister_driver: Type %d "
+			       "still has devices registered\n", ulp_type);
+			read_unlock(&cnic_dev_lock);
+			goto out_unlock;
+		}
+	}
+	read_unlock(&cnic_dev_lock);
+
+	rcu_assign_pointer(cnic_ulp_tbl[ulp_type], NULL);
+
+	mutex_unlock(&cnic_lock);
+	synchronize_rcu();
+	return 0;
+
+out_unlock:
+	mutex_unlock(&cnic_lock);
+	return -EINVAL;
+}
+
+EXPORT_SYMBOL(cnic_register_driver);
+EXPORT_SYMBOL(cnic_unregister_driver);
+
+static int cnic_start_hw(struct cnic_dev *);
+static void cnic_stop_hw(struct cnic_dev *);
+
+static int cnic_register_device(struct cnic_dev *dev, int ulp_type,
+				void *ulp_ctx)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	struct cnic_ulp_ops *ulp_ops;
+
+	if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+		printk(KERN_ERR PFX "cnic_register_device: Bad type %d\n",
+		       ulp_type);
+		return -EINVAL;
+	}
+	mutex_lock(&cnic_lock);
+	if (cnic_ulp_tbl[ulp_type] == NULL) {
+		printk(KERN_ERR PFX "cnic_register_device: Driver with type %d "
+				    "has not been registered\n", ulp_type);
+		mutex_unlock(&cnic_lock);
+		return -EAGAIN;
+	}
+	if (rcu_dereference(cp->ulp_ops[ulp_type])) {
+		printk(KERN_ERR PFX "cnic_register_device: Type %d has already "
+		       "been registered to this device\n", ulp_type);
+		mutex_unlock(&cnic_lock);
+		return -EBUSY;
+	}
+	if (!try_module_get(cnic_ulp_tbl[ulp_type]->owner)) {
+		mutex_unlock(&cnic_lock);
+		return -EBUSY;
+	}
+
+	clear_bit(ULP_F_START, &cp->ulp_flags[ulp_type]);
+	cp->ulp_handle[ulp_type] = ulp_ctx;
+	ulp_ops = cnic_ulp_tbl[ulp_type];
+	rcu_assign_pointer(cp->ulp_ops[ulp_type], ulp_ops);
+	cnic_hold(dev);
+	dev->use_count++;
+
+	if (dev->use_count == 1) {
+		if (test_bit(CNIC_F_IF_UP, &dev->flags))
+			cnic_start_hw(dev);
+	}
+
+	if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
+		if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[ulp_type]))
+			ulp_ops->cnic_start(cp->ulp_handle[ulp_type]);
+
+	mutex_unlock(&cnic_lock);
+
+	return 0;
+
+}
+
+static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+
+	if (ulp_type >= MAX_CNIC_ULP_TYPE) {
+		printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n",
+		       ulp_type);
+		return -EINVAL;
+	}
+	mutex_lock(&cnic_lock);
+	if (rcu_dereference(cp->ulp_ops[ulp_type])) {
+		dev->use_count--;
+		module_put(cp->ulp_ops[ulp_type]->owner);
+		rcu_assign_pointer(cp->ulp_ops[ulp_type], NULL);
+		if (dev->use_count == 0)
+			cnic_stop_hw(dev);
+		cnic_put(dev);
+	} else {
+		printk(KERN_ERR PFX "cnic_unregister_device: device not "
+		       "registered to this ulp type %d\n", ulp_type);
+		mutex_unlock(&cnic_lock);
+		return -EINVAL;
+	}
+	mutex_unlock(&cnic_lock);
+
+	synchronize_rcu();
+
+	return 0;
+}
+
+#ifdef CNIC_SYSFS
+static ssize_t show_pci_bar(struct class_device *cdev, char *buf)
+{
+	struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev);
+
+	return sprintf(buf, "0x%.8x\n", (u32)pci_resource_start(dev->pcidev, 0));
+}
+
+static ssize_t show_kq_intr_coal(struct class_device *cdev, char *buf)
+{
+	struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev);
+
+	return sprintf(buf, "0x%.8x\n", REG_RD(dev, BNX2_HC_COMP_PROD_TRIP));
+}
+
+static ssize_t set_kq_intr_coal(struct class_device *cdev, const char *buf,
+			      size_t count)
+{
+	struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev);
+	u32 val;
+
+	if (sscanf(buf, " 0x%x ", &val) > 0)
+		REG_WR(dev, BNX2_HC_COMP_PROD_TRIP, val);
+	return count;
+}
+
+static ssize_t show_kq_com_ticks(struct class_device *cdev, char *buf)
+{
+	struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev);
+
+	return sprintf(buf, "0x%.8x\n", REG_RD(dev, BNX2_HC_COM_TICKS));
+}
+
+static ssize_t set_kq_com_ticks(struct class_device *cdev, const char *buf,
+			      size_t count)
+{
+	struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev);
+	u32 val;
+
+	if (sscanf(buf, " 0x%x ", &val) > 0)
+		REG_WR(dev, BNX2_HC_COM_TICKS, val);
+	return count;
+}
+
+static ssize_t show_kq_cmd_ticks(struct class_device *cdev, char *buf)
+{
+	struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev);
+
+	return sprintf(buf, "0x%.8x\n", REG_RD(dev, BNX2_HC_CMD_TICKS));
+}
+
+static ssize_t set_kq_cmd_ticks(struct class_device *cdev, const char *buf,
+			      size_t count)
+{
+	struct cnic_dev *dev = container_of(cdev, struct cnic_dev, class_dev);
+	u32 val;
+
+	if (sscanf(buf, " 0x%x ", &val) > 0)
+		REG_WR(dev, BNX2_HC_CMD_TICKS, val);
+	return count;
+}
+
+static CLASS_DEVICE_ATTR(pci_bar, S_IRUGO, show_pci_bar, NULL);
+static CLASS_DEVICE_ATTR(kq_intr_coal, S_IRUGO | S_IWUSR, show_kq_intr_coal,
+			 set_kq_intr_coal);
+static CLASS_DEVICE_ATTR(kq_com_ticks, S_IRUGO | S_IWUSR, show_kq_com_ticks,
+			 set_kq_com_ticks);
+static CLASS_DEVICE_ATTR(kq_cmd_ticks, S_IRUGO | S_IWUSR, show_kq_cmd_ticks,
+			 set_kq_cmd_ticks);
+
+static struct class_device_attribute *cnic_class_attributes[] = {
+	&class_device_attr_pci_bar,
+	&class_device_attr_kq_intr_coal,
+	&class_device_attr_kq_com_ticks,
+	&class_device_attr_kq_cmd_ticks
+};
+
+static void cnic_sysfs_release(struct class_device *class_dev)
+{
+}
+
+static struct class cnic_class = {
+	.name    = "cnic",
+	.release = cnic_sysfs_release,
+};
+
+static int cnic_register_sysfs(struct cnic_dev *device)
+{
+	struct class_device *class_dev = &device->class_dev;
+	char dev_name[BUS_ID_SIZE];
+	int ret;
+	int i;
+
+	class_dev->class      = &cnic_class;
+	class_dev->class_data = device;
+	snprintf(dev_name, BUS_ID_SIZE, "%.2x:%.2x.%.1x",
+		 device->pcidev->bus->number, PCI_SLOT(device->pcidev->devfn),
+		 PCI_FUNC(device->pcidev->devfn));
+	strlcpy(class_dev->class_id, dev_name, BUS_ID_SIZE);
+
+	ret = class_device_register(class_dev);
+	if (ret)
+		goto err;
+
+	for (i = 0; i < ARRAY_SIZE(cnic_class_attributes); ++i) {
+		ret = class_device_create_file(class_dev,
+					       cnic_class_attributes[i]);
+		if (ret)
+			goto err_unregister;
+	}
+
+	return 0;
+
+err_unregister:
+	class_device_unregister(class_dev);
+err:
+	return ret;
+}
+
+static void cnic_unregister_sysfs(struct cnic_dev *device)
+{
+	class_device_unregister(&device->class_dev);
+}
+
+static int cnic_sysfs_setup(void)
+{
+	return class_register(&cnic_class);
+}
+
+static void cnic_sysfs_cleanup(void)
+{
+	class_unregister(&cnic_class);
+}
+#else
+static void cnic_unregister_sysfs(struct cnic_dev *device)
+{
+}
+
+static int cnic_register_sysfs(struct cnic_dev *device)
+{
+	return 0;
+}
+
+static int cnic_sysfs_setup(void)
+{
+	return 0;
+}
+
+static void cnic_sysfs_cleanup(void)
+{
+}
+
+#endif
+
+static void cnic_free_resc(struct cnic_dev *dev)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	int i;
+
+	if (cp->kwq_pgtbl) {
+		pci_free_consistent(dev->pcidev, cp->kwq_pgtbl_size,
+				    cp->kwq_pgtbl, cp->kwq_pgtbl_mapping);
+		cp->kwq_pgtbl = NULL;
+	}
+	for (i = 0; i < KWQ_PAGE_CNT; i++) {
+		if (cp->kwq[i]) {
+			pci_free_consistent(dev->pcidev, BCM_PAGE_SIZE,
+					    cp->kwq[i], cp->kwq_mapping[i]);
+			cp->kwq[i] = NULL;
+		}
+	}
+	if (cp->kcq_pgtbl) {
+		pci_free_consistent(dev->pcidev, cp->kcq_pgtbl_size,
+				    cp->kcq_pgtbl, cp->kcq_pgtbl_mapping);
+		cp->kcq_pgtbl = NULL;
+	}
+	for (i = 0; i < KCQ_PAGE_CNT; i++) {
+		if (cp->kcq[i]) {
+			pci_free_consistent(dev->pcidev, BCM_PAGE_SIZE,
+					    cp->kcq[i], cp->kcq_mapping[i]);
+			cp->kcq[i] = NULL;
+		}
+	}
+
+	return;
+}
+
+static int cnic_alloc_resc(struct cnic_dev *dev)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	int i;
+
+	cp->kwq_pgtbl_size = ((KWQ_PAGE_CNT * 8) + BCM_PAGE_SIZE - 1) &
+			      (BCM_PAGE_SIZE - 1);
+	cp->kwq_pgtbl = pci_alloc_consistent(dev->pcidev, cp->kwq_pgtbl_size,
+					     &cp->kwq_pgtbl_mapping);
+	if (cp->kwq_pgtbl == NULL)
+		goto error;
+
+	for (i = 0; i < KWQ_PAGE_CNT; i++) {
+		cp->kwq[i] = pci_alloc_consistent(dev->pcidev, BCM_PAGE_SIZE,
+						  &cp->kwq_mapping[i]);
+		if (cp->kwq[i] == NULL)
+			goto error;
+	}
+
+	cp->kcq_pgtbl_size = ((KCQ_PAGE_CNT * 8) + BCM_PAGE_SIZE - 1) &
+			     (BCM_PAGE_SIZE - 1);
+	cp->kcq_pgtbl = pci_alloc_consistent(dev->pcidev, cp->kcq_pgtbl_size,
+					     &cp->kcq_pgtbl_mapping);
+	if (cp->kcq_pgtbl == NULL)
+		goto error;
+
+	for (i = 0; i < KCQ_PAGE_CNT; i++) {
+		cp->kcq[i] = pci_alloc_consistent(dev->pcidev, BCM_PAGE_SIZE,
+						  &cp->kcq_mapping[i]);
+		if (cp->kcq[i] == NULL)
+			goto error;
+	}
+
+	return 0;
+	
+error:
+	cnic_free_resc(dev);
+	return -ENOMEM;
+}
+
+static void cnic_setup_page_tbl(u32 *page_table, u32 page_cnt,
+				dma_addr_t base_mapping[])
+{
+	int i;
+
+	for (i = 0; i < page_cnt; i++) {
+		/* Each entry needs to be in big endian format. */
+		*page_table = (u32) ((u64) base_mapping[i] >> 32);
+		page_table++;
+		*page_table = (u32) base_mapping[i];
+		page_table++;
+	}
+}
+
+static inline u32 cnic_kwq_avail(struct cnic_local *cp)
+{
+	return (MAX_KWQ_IDX -
+		((cp->kwq_prod_idx - cp->kwq_con_idx) & MAX_KWQ_IDX));
+}
+
+static int cnic_submit_kernel_wqes(struct cnic_dev *dev, struct kwqe *wqes[],
+			    u32 num_wqes)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	struct kwqe *prod_qe;
+	u16 prod, sw_prod, i;
+
+	if (!test_bit(CNIC_F_CNIC_UP, &dev->flags))
+		return -EAGAIN;		/* bnx2 is down */
+
+	spin_lock_bh(&cp->cnic_ulp_lock);
+	if (num_wqes > cnic_kwq_avail(cp)) {
+		spin_unlock_bh(&cp->cnic_ulp_lock);
+		return -EAGAIN;
+	}
+
+	prod = cp->kwq_prod_idx;
+	sw_prod = prod & MAX_KWQ_IDX;
+	for (i = 0; i < num_wqes; i++) {
+		prod_qe = &cp->kwq[KWQ_PG(sw_prod)][KWQ_IDX(sw_prod)];
+		memcpy(prod_qe, wqes[i], sizeof(struct kwqe));
+		prod++;
+		sw_prod = prod & MAX_KWQ_IDX;
+	}
+	cp->kwq_prod_idx = prod;
+
+	REG_WR16(dev, cp->kwq_io_addr, cp->kwq_prod_idx);
+	/* Proper ordering of REG_WR16 from different CPUs. */
+	mmiowb();
+
+	spin_unlock_bh(&cp->cnic_ulp_lock);
+	return 0;
+}
+
+static void service_kcqes(struct cnic_dev *dev, int num_cqes)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	int i, j;
+
+	i = 0;
+	j = 1;
+	while (num_cqes) {
+		struct cnic_ulp_ops *ulp_ops;
+		int ulp_type;
+		u32 kcqe_op_flag = cp->completed_kcq[i]->kcqe_op_flag &
+				   KCQE_FLAGS_LAYER_MASK;
+
+		while (j < num_cqes) {
+			if ((cp->completed_kcq[i + j]->kcqe_op_flag &
+			     KCQE_FLAGS_LAYER_MASK) != kcqe_op_flag) {
+				break;
+			}
+			j++;
+		}
+
+		if (kcqe_op_flag == KCQE_FLAGS_LAYER_MASK_L5_RDMA)
+			ulp_type = CNIC_ULP_RDMA;
+		else if (kcqe_op_flag == KCQE_FLAGS_LAYER_MASK_L5_ISCSI)
+			ulp_type = CNIC_ULP_ISCSI;
+		else if (kcqe_op_flag == KCQE_FLAGS_LAYER_MASK_L4)
+			ulp_type = CNIC_ULP_L4;
+		else {
+			printk(KERN_ERR PFX "%s: Unknown type of KCQE(0x%x)\n",
+			       dev->netdev->name, kcqe_op_flag);
+			goto end;
+		}
+
+		rcu_read_lock();
+		ulp_ops = rcu_dereference(cp->ulp_ops[ulp_type]);
+		if (likely(ulp_ops)) {
+			ulp_ops->indicate_kcqes(cp->ulp_handle[ulp_type],
+						  cp->completed_kcq + i, j);
+		}
+		rcu_read_unlock();
+end:
+		num_cqes -= j;
+		i += j;
+		j = 1;
+	}
+	return;
+}
+
+static int cnic_service_kq(void *data, struct status_block *status_blk)
+{
+	struct cnic_dev *dev = data;
+	struct cnic_local *cp = dev->cnic_priv;
+	u32 status_idx = status_blk->status_idx;
+	u16 hw_prod, sw_prod;
+
+	cp->kwq_con_idx = status_blk->status_rx_quick_consumer_index15;
+
+	hw_prod = status_blk->status_completion_producer_index;
+	sw_prod = cp->kcq_prod_idx;
+	while (sw_prod != hw_prod) {
+		u16 i, max;
+		struct kcqe *kcqe;
+		int kcqe_cnt = 0;
+
+		i = sw_prod & MAX_KCQ_IDX;
+		max = hw_prod & MAX_KCQ_IDX;
+		while ((i != max) && (kcqe_cnt < MAX_COMPLETED_KCQE)) {
+			cp->completed_kcq[kcqe_cnt++] =
+				&cp->kcq[KCQ_PG(i)][KCQ_IDX(i)];
+			i = (i + 1) & MAX_KCQ_IDX;
+		}
+
+		kcqe = cp->completed_kcq[kcqe_cnt - 1];
+		while (kcqe->kcqe_op_flag & KCQE_FLAGS_NEXT) {
+			kcqe_cnt--;
+			if (kcqe_cnt == 0)
+				goto done;
+			kcqe = cp->completed_kcq[kcqe_cnt - 1];
+		}
+		sw_prod += kcqe_cnt;
+
+		service_kcqes(dev, kcqe_cnt);
+
+		REG_WR16(dev, cp->kcq_io_addr, sw_prod);
+
+		if (hw_prod != status_blk->status_completion_producer_index)
+			hw_prod = status_blk->status_completion_producer_index;
+		else
+			break;
+	}
+done:
+	/* Proper ordering of REG_WR16 from different CPUs. */
+	mmiowb();
+	cp->kcq_prod_idx = sw_prod;
+	return status_idx;
+}
+
+static void cnic_ulp_stop(struct cnic_dev *dev)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	int if_type;
+
+	rcu_read_lock();
+	for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
+		struct cnic_ulp_ops *ulp_ops;
+
+		ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
+		if (!ulp_ops)
+			continue;
+
+		if (test_and_clear_bit(ULP_F_START, &cp->ulp_flags[if_type]))
+			ulp_ops->cnic_stop(cp->ulp_handle[if_type]);
+	}
+	rcu_read_unlock();
+}
+
+static void cnic_ulp_start(struct cnic_dev *dev)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	int if_type;
+
+	rcu_read_lock();
+	for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
+		struct cnic_ulp_ops *ulp_ops;
+
+		ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
+		if (!ulp_ops)
+			continue;
+
+		if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[if_type]))
+			ulp_ops->cnic_start(cp->ulp_handle[if_type]);
+	}
+	rcu_read_unlock();
+}
+
+static void cnic_service_start(void *data)
+{
+	struct cnic_dev *dev = data;
+
+	cnic_hold(dev);
+	mutex_lock(&cnic_lock);
+	set_bit(CNIC_F_IF_UP, &dev->flags);
+	if (dev->use_count) {
+		if (!cnic_start_hw(dev))
+			cnic_ulp_start(dev);
+	}
+	mutex_unlock(&cnic_lock);
+	cnic_put(dev);
+}
+
+static void cnic_service_stop(void *data)
+{
+	struct cnic_dev *dev = data;
+
+	cnic_hold(dev);
+	mutex_lock(&cnic_lock);
+	clear_bit(CNIC_F_IF_UP, &dev->flags);
+	cnic_ulp_stop(dev);
+	cnic_stop_hw(dev);
+	mutex_unlock(&cnic_lock);
+	cnic_put(dev);
+}
+
+static void cnic_ulp_init(struct cnic_dev *dev)
+{
+	int i;
+	struct cnic_local *cp = dev->cnic_priv;
+
+	rcu_read_lock();
+	for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) {
+		struct cnic_ulp_ops *ulp_ops;
+
+		ulp_ops = rcu_dereference(cnic_ulp_tbl[i]);
+		if (!ulp_ops || !try_module_get(ulp_ops->owner))
+			continue;
+
+		if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[i]))
+			ulp_ops->cnic_init(dev);
+
+		module_put(ulp_ops->owner);
+	}
+	rcu_read_unlock();
+}
+
+static void cnic_ulp_exit(struct cnic_dev *dev)
+{
+	int i;
+	struct cnic_local *cp = dev->cnic_priv;
+
+	rcu_read_lock();
+	for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) {
+		struct cnic_ulp_ops *ulp_ops;
+
+		ulp_ops = rcu_dereference(cnic_ulp_tbl[i]);
+		if (!ulp_ops || !try_module_get(ulp_ops->owner))
+			continue;
+
+		if (test_and_clear_bit(ULP_F_INIT, &cp->ulp_flags[i]))
+			ulp_ops->cnic_exit(dev);
+
+		module_put(ulp_ops->owner);
+	}
+	rcu_read_unlock();
+}
+
+static int cnic_queue_work(struct cnic_local *cp, u32 work_type, void *data)
+{
+	struct cnic_work_node *node;
+	int bytes = sizeof(u32 *);
+
+	spin_lock_bh(&cp->cm_lock);
+
+	node = &cp->cnic_work_ring[cp->cnic_wr_prod];
+	node->work_type = work_type;
+	if (work_type == WORK_TYPE_KCQE)
+		bytes = sizeof(struct kcqe);
+	if (work_type == WORK_TYPE_REDIRECT)
+		bytes = sizeof(struct cnic_redirect_entry);
+	memcpy(&node->work_data, data, bytes);
+	cp->cnic_wr_prod++;
+	cp->cnic_wr_prod &= WORK_RING_SIZE_MASK;
+
+	spin_unlock_bh(&cp->cm_lock);
+	return 0;
+}
+
+static int cnic_cm_offload_pg(struct cnic_sock *csk)
+{
+	struct cnic_dev *dev = csk->dev;
+	struct l4_kwq_offload_pg *l4kwqe;
+	struct kwqe *wqes[1];
+	struct neighbour *neigh = csk->dst->neighbour;
+	struct net_device *netdev = neigh->dev;
+
+	l4kwqe = (struct l4_kwq_offload_pg *) &csk->kwqe1;
+	memset(l4kwqe, 0, sizeof(*l4kwqe));
+	wqes[0] = (struct kwqe *) l4kwqe;
+
+	l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_OFFLOAD_PG;
+	l4kwqe->flags =
+		L4_LAYER_CODE << L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT;
+	l4kwqe->l2hdr_nbytes = ETH_HLEN;
+	l4kwqe->da0 = neigh->ha[0];
+	l4kwqe->da1 = neigh->ha[1];
+	l4kwqe->da2 = neigh->ha[2];
+	l4kwqe->da3 = neigh->ha[3];
+	l4kwqe->da4 = neigh->ha[4];
+	l4kwqe->da5 = neigh->ha[5];
+
+	l4kwqe->sa0 = netdev->dev_addr[0];
+	l4kwqe->sa1 = netdev->dev_addr[1];
+	l4kwqe->sa2 = netdev->dev_addr[2];
+	l4kwqe->sa3 = netdev->dev_addr[3];
+	l4kwqe->sa4 = netdev->dev_addr[4];
+	l4kwqe->sa5 = netdev->dev_addr[5];
+
+	l4kwqe->etype = ETH_P_IP;
+	l4kwqe->ipid_count = DEF_IPID_COUNT;
+	l4kwqe->host_opaque = csk->l5_cid;
+
+	if (csk->vlan_id) {
+		l4kwqe->pg_flags |= L4_KWQ_OFFLOAD_PG_VLAN_TAGGING;
+		l4kwqe->vlan_tag = csk->vlan_id;
+		l4kwqe->l2hdr_nbytes += 4;
+	}
+
+	return (dev->submit_kwqes(dev, wqes, 1));
+}
+
+static int cnic_cm_update_pg(struct cnic_sock *csk)
+{
+	struct cnic_dev *dev = csk->dev;
+	struct l4_kwq_update_pg *l4kwqe;
+	struct kwqe *wqes[1];
+	struct neighbour *neigh = csk->dst->neighbour;
+
+	l4kwqe = (struct l4_kwq_update_pg *) &csk->kwqe1;
+	memset(l4kwqe, 0, sizeof(*l4kwqe));
+	wqes[0] = (struct kwqe *) l4kwqe;
+
+	l4kwqe->opcode = L4_KWQE_OPCODE_VALUE_UPDATE_PG;
+	l4kwqe->flags =
+		L4_LAYER_CODE << L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT;
+	l4kwqe->pg_cid = csk->pg_cid;
+	l4kwqe->da0 = neigh->ha[0];
+	l4kwqe->da1 = neigh->ha[1];
+	l4kwqe->da2 = neigh->ha[2];
+	l4kwqe->da3 = neigh->ha[3];
+	l4kwqe->da4 = neigh->ha[4];
+	l4kwqe->da5 = neigh->ha[5];
+
+	l4kwqe->pg_host_opaque = 0;
+	l4kwqe->pg_valids = L4_KWQ_UPDATE_PG_VALIDS_DA;
+
+	return (dev->submit_kwqes(dev, wqes, 1));
+}
+
+static int cnic_cm_upload_pg(struct cnic_sock *csk)
+{
+	struct cnic_dev *dev = csk->dev;
+	struct l4_kwq_upload *l4kwqe;
+	struct kwqe *wqes[1];
+
+	l4kwqe = (struct l4_kwq_upload *) &csk->kwqe1;
+	memset(l4kwqe, 0, sizeof(*l4kwqe));
+	wqes[0] = (struct kwqe *) l4kwqe;
+
+	l4kwqe->opcode = L4_KWQE_OPCODE_VALUE_UPLOAD_PG;
+	l4kwqe->flags =
+		L4_LAYER_CODE << L4_KWQ_UPLOAD_LAYER_CODE_SHIFT;
+	l4kwqe->cid = csk->pg_cid;
+
+	return (dev->submit_kwqes(dev, wqes, 1));
+}
+
+static void cnic_redirect(struct cnic_local *cp, struct dst_entry *new,
+			  struct dst_entry *old)
+{
+	int i, found = 0;
+
+	spin_lock_bh(&cp->cm_lock);
+	for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) {
+		struct cnic_sock *csk;
+
+		csk = &cp->csk_tbl[i];
+		if (test_bit(SK_F_INUSE, &csk->flags) && csk->dst == old)
+			found = 1;
+	}
+	spin_unlock_bh(&cp->cm_lock);
+
+	if (found) {
+		struct cnic_redirect_entry cnic_redir;
+
+		dst_hold(new);
+		cnic_redir.old_dst = old;
+		cnic_redir.new_dst = new;
+		cnic_queue_work(cp, WORK_TYPE_REDIRECT, &cnic_redir);
+		schedule_work(&cp->cnic_task);
+	}
+}
+
+static void cnic_update_neigh(struct cnic_local *cp, struct neighbour *neigh)
+{
+	int i, found = 0;
+
+	spin_lock_bh(&cp->cm_lock);
+	for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) {
+		struct cnic_sock *csk;
+
+		csk = &cp->csk_tbl[i];
+		if (test_bit(SK_F_INUSE, &csk->flags) && csk->dst) {
+			if (csk->dst->neighbour == neigh)
+				found = 1;
+		}
+	}
+	spin_unlock_bh(&cp->cm_lock);
+
+	if (!found)
+		return;
+
+	neigh_hold(neigh);
+
+	cnic_queue_work(cp, WORK_TYPE_NEIGH_UPDATE, &neigh);
+	schedule_work(&cp->cnic_task);
+}
+
+static int cnic_net_callback(struct notifier_block *this, unsigned long event,
+	void *ptr)
+{
+	struct cnic_local *cp = container_of(this, struct cnic_local, cm_nb);
+
+	if (event == NETEVENT_NEIGH_UPDATE) {
+		struct neighbour *neigh = ptr;
+
+		cnic_update_neigh(cp, neigh);
+
+	} else if (event == NETEVENT_REDIRECT) {
+		struct netevent_redirect *netevent = ptr;
+		struct dst_entry *old_dst = netevent->old;
+		struct dst_entry *new_dst = netevent->new;
+
+		cnic_redirect(cp, new_dst, old_dst);
+	}
+	return 0;
+}
+
+static int cnic_ok_to_connect(struct cnic_sock *csk)
+{
+	if (test_bit(SK_F_INUSE, &csk->flags) &&
+	    !test_bit(SK_F_OFFLD_PENDING, &csk->flags) &&
+	    !test_bit(SK_F_OFFLD_COMPLETE, &csk->flags) &&
+	    test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags))
+		return 1;
+	return 0;
+}
+
+static int cnic_cm_conn_req(struct cnic_sock *csk)
+{
+	struct cnic_dev *dev = csk->dev;
+	struct l4_kwq_connect_req1 *l4kwqe1;
+	struct l4_kwq_connect_req3 *l4kwqe3;
+	struct kwqe *wqes[2];
+	u8 tcp_flags = 0;
+
+	l4kwqe1 = (struct l4_kwq_connect_req1 *) &csk->kwqe2;
+	l4kwqe3 = (struct l4_kwq_connect_req3 *) &csk->kwqe3;
+	memset(l4kwqe1, 0, sizeof(*l4kwqe1));
+	memset(l4kwqe3, 0, sizeof(*l4kwqe3));
+	wqes[0] = (struct kwqe *) l4kwqe1;
+	wqes[1] = (struct kwqe *) l4kwqe3;
+
+	l4kwqe1->op_code = L4_KWQE_OPCODE_VALUE_CONNECT1;
+	l4kwqe1->flags =
+		(L4_LAYER_CODE << L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT) |
+		 L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT;
+	l4kwqe1->cid = csk->cid;
+	l4kwqe1->pg_cid = csk->pg_cid;
+	l4kwqe1->src_ip = be32_to_cpu(csk->src_ip);
+	l4kwqe1->dst_ip = be32_to_cpu(csk->dst_ip);
+	l4kwqe1->src_port = be16_to_cpu(csk->src_port);
+	l4kwqe1->dst_port = be16_to_cpu(csk->dst_port);
+	if (test_bit(SK_TCP_NO_DELAY_ACK, &csk->tcp_flags))
+		tcp_flags |= L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK;
+	if (test_bit(SK_TCP_KEEP_ALIVE, &csk->tcp_flags))
+		tcp_flags |= L4_KWQ_CONNECT_REQ1_KEEP_ALIVE;
+	if (test_bit(SK_TCP_NAGLE, &csk->tcp_flags))
+		tcp_flags |= L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE;
+	if (test_bit(SK_TCP_TIMESTAMP, &csk->tcp_flags))
+		tcp_flags |= L4_KWQ_CONNECT_REQ1_TIME_STAMP;
+	if (test_bit(SK_TCP_SACK, &csk->tcp_flags))
+		tcp_flags |= L4_KWQ_CONNECT_REQ1_SACK;
+	if (test_bit(SK_TCP_SEG_SCALING, &csk->tcp_flags))
+		tcp_flags |= L4_KWQ_CONNECT_REQ1_SEG_SCALING;
+	
+	l4kwqe1->tcp_flags = tcp_flags;
+
+	l4kwqe3->op_code = L4_KWQE_OPCODE_VALUE_CONNECT3;
+	l4kwqe3->flags =
+		L4_LAYER_CODE << L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT;
+	l4kwqe3->ka_timeout = csk->ka_timeout;
+	l4kwqe3->ka_interval = csk->ka_interval;
+	l4kwqe3->ka_max_probe_count = csk->ka_max_probe_count;
+	l4kwqe3->tos = csk->tos;
+	l4kwqe3->ttl = csk->ttl;
+	l4kwqe3->snd_seq_scale = csk->snd_seq_scale;
+	l4kwqe3->pmtu = dst_mtu(csk->dst);
+	l4kwqe3->mss = l4kwqe3->pmtu - 40;
+	l4kwqe3->rcv_buf = csk->rcv_buf;
+	l4kwqe3->snd_buf = csk->snd_buf;
+	l4kwqe3->seed = csk->seed;
+
+	return (dev->submit_kwqes(dev, wqes, 2));
+}
+
+static int cnic_cm_close_req(struct cnic_sock *csk)
+{
+	struct cnic_dev *dev = csk->dev;
+	struct l4_kwq_close_req *l4kwqe;
+	struct kwqe *wqes[1];
+
+	l4kwqe = (struct l4_kwq_close_req *) &csk->kwqe2;
+	memset(l4kwqe, 0, sizeof(*l4kwqe));
+	wqes[0] = (struct kwqe *) l4kwqe;
+
+	l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_CLOSE;
+	l4kwqe->flags = L4_LAYER_CODE << L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT;
+	l4kwqe->cid = csk->cid;
+
+	return (dev->submit_kwqes(dev, wqes, 1));
+}
+
+static int cnic_cm_abort_req(struct cnic_sock *csk)
+{
+	struct cnic_dev *dev = csk->dev;
+	struct l4_kwq_reset_req *l4kwqe;
+	struct kwqe *wqes[1];
+
+	l4kwqe = (struct l4_kwq_reset_req *) &csk->kwqe2;
+	memset(l4kwqe, 0, sizeof(*l4kwqe));
+	wqes[0] = (struct kwqe *) l4kwqe;
+
+	l4kwqe->op_code = L4_KWQE_OPCODE_VALUE_RESET;
+	l4kwqe->flags = L4_LAYER_CODE << L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT;
+	l4kwqe->cid = csk->cid;
+
+	return (dev->submit_kwqes(dev, wqes, 1));
+}
+
+static int cnic_cm_create(struct cnic_dev *dev, int ulp_type, u32 cid,
+			  u32 l5_cid, struct cnic_sock **csk, void *context)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	struct cnic_sock *csk1;
+
+	if (l5_cid >= MAX_CM_SK_TBL_SZ)
+		return -EINVAL;
+		
+	csk1 = &cp->csk_tbl[l5_cid];
+	if (test_and_set_bit(SK_F_INUSE, &csk1->flags))
+		return -EINVAL;
+
+	csk1->dev = dev;
+	csk1->cid = cid;
+	csk1->l5_cid = l5_cid;
+	csk1->ulp_type = ulp_type;
+	csk1->context = context;
+
+	csk1->ka_timeout = DEF_KA_TIMEOUT;
+	csk1->ka_interval = DEF_KA_INTERVAL;
+	csk1->ka_max_probe_count = DEF_KA_MAX_PROBE_COUNT;
+	csk1->tos = DEF_TOS;
+	csk1->ttl = DEF_TTL;
+	csk1->snd_seq_scale = DEF_SND_SEQ_SCALE;
+	csk1->rcv_buf = DEF_RCV_BUF;
+	csk1->snd_buf = DEF_SND_BUF;
+	csk1->seed = DEF_SEED;
+
+	*csk = csk1;
+
+	return 0;
+}
+
+static int cnic_cm_destroy(struct cnic_sock *csk)
+{
+	struct cnic_local *cp = csk->dev->cnic_priv;
+
+	if (!test_bit(SK_F_INUSE, &csk->flags))
+		return -EINVAL;
+
+	spin_lock_bh(&cp->cm_lock);
+	if (csk->dst) {
+		if (csk->dst->neighbour)
+			neigh_release(csk->dst->neighbour);
+		dst_release(csk->dst);
+		csk->dst = NULL;
+	}
+	csk->flags = 0;
+	spin_unlock_bh(&cp->cm_lock);
+	return 0;
+}
+
+static inline struct net_device *get_real_netdev(struct net_device *netdev)
+{
+	return netdev->priv_flags & IFF_802_1Q_VLAN ?
+		VLAN_DEV_INFO(netdev)->real_dev : netdev;
+}
+
+static struct cnic_dev *cnic_cm_select_dev(struct sockaddr_in *dst_addr,
+					   int ulp_type)
+{
+	u32 dst_ip = dst_addr->sin_addr.s_addr;
+	struct flowi fl;
+	struct rtable *rt;
+	struct net_device *netdev;
+	struct cnic_dev *dev;
+	int err, found;
+
+	memset(&fl, 0, sizeof(fl));
+	fl.nl_u.ip4_u.daddr = dst_ip;
+
+	err = ip_route_output_key(&rt, &fl);
+	if (err)
+		return NULL;
+
+	netdev = get_real_netdev(rt->idev->dev);
+
+	found = 0;
+	read_lock(&cnic_dev_lock);
+	list_for_each_entry(dev, &cnic_dev_list, list) {
+		if (netdev == dev->netdev) {
+			found = 1;
+			cnic_hold(dev);
+			break;
+		}
+	}
+	read_unlock(&cnic_dev_lock);
+
+	ip_rt_put(rt);
+
+	if (found) {
+		struct cnic_local *cp = dev->cnic_priv;
+
+		if (rcu_dereference(cp->ulp_ops[ulp_type])) {
+			cnic_put(dev);
+			return dev;
+		}
+		cnic_put(dev);
+	}
+
+	return NULL;
+}
+
+static int cnic_cm_connect(struct cnic_sock *csk, struct cnic_sockaddr *saddr)
+{
+	struct cnic_dev *dev = csk->dev;
+	struct net_device *realdev;
+	u32 dst_ip = saddr->remote_addr.sin_addr.s_addr;
+	u32 src_ip = saddr->local_addr.sin_addr.s_addr;
+	struct flowi fl;
+	struct rtable *rt;
+	struct neighbour *neigh;
+	int err = 0, retry = 0;
+
+	if (!test_bit(SK_F_INUSE, &csk->flags))
+		return -EINVAL;
+
+	memset(&fl, 0, sizeof(fl));
+	fl.nl_u.ip4_u.daddr = dst_ip;
+	fl.nl_u.ip4_u.saddr = src_ip;
+	err = ip_route_output_key(&rt, &fl);
+	if (err)
+		return err;
+
+	realdev = get_real_netdev(rt->idev->dev);
+	if (realdev != dev->netdev)
+		goto err;
+
+	if (src_ip == 0)
+		src_ip = inet_select_addr(rt->idev->dev, dst_ip, RT_SCOPE_LINK);
+
+	csk->dst = &rt->u.dst;
+	csk->src_ip = src_ip;
+	csk->dst_ip = dst_ip;
+	csk->src_port = saddr->local_addr.sin_port;
+	csk->dst_port = saddr->remote_addr.sin_port;
+
+	neigh = csk->dst->neighbour;
+	if (!csk->dst->neighbour)
+		goto err;
+
+	neigh_hold(neigh);
+
+	if (realdev != rt->idev->dev)
+		csk->vlan_id = VLAN_DEV_INFO(rt->idev->dev)->vlan_id;
+	else
+		csk->vlan_id = 0;
+
+
+	if (neigh->nud_state & NUD_VALID)
+		err = cnic_cm_offload_pg(csk);
+
+	while (!(neigh->nud_state & NUD_VALID) && (retry < 3)) {
+		arp_send(ARPOP_REQUEST, ETH_P_ARP, rt->rt_gateway,
+			 rt->idev->dev, rt->rt_src, NULL,
+			 rt->idev->dev->dev_addr, NULL);
+		msleep(1000);
+		retry++;
+	}
+	if (!(neigh->nud_state & NUD_VALID))
+		err = -ENODATA;
+
+	if (!err)
+		return 0;
+
+	neigh_release(neigh);
+
+err:
+	csk->dst = NULL;
+
+	ip_rt_put(rt);
+	return err;
+}
+
+static int cnic_cm_abort(struct cnic_sock *csk)
+{
+	if (!test_bit(SK_F_INUSE, &csk->flags))
+		return -EINVAL;
+
+	return (cnic_cm_abort_req(csk));
+}
+
+static int cnic_cm_close(struct cnic_sock *csk)
+{
+	if (!test_bit(SK_F_INUSE, &csk->flags))
+		return -EINVAL;
+
+	return (cnic_cm_close_req(csk));
+}
+
+static void cnic_cm_process_neigh(struct cnic_dev *dev, struct neighbour *neigh)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	int i;
+
+	for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) {
+		struct cnic_sock *csk;
+
+		csk = &cp->csk_tbl[i];
+		spin_lock_bh(&cp->cm_lock);
+		if (test_bit(SK_F_INUSE, &csk->flags) && csk->dst &&
+		    csk->dst->neighbour == neigh) {
+			if (test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags))
+				cnic_cm_update_pg(csk);
+			else
+				cnic_cm_offload_pg(csk);
+		}
+		spin_unlock_bh(&cp->cm_lock);
+	}
+	neigh_release(neigh);
+}
+
+static void cnic_cm_process_redirect(struct cnic_dev *dev,
+				     struct cnic_redirect_entry *redir)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	int i;
+
+	for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) {
+		struct cnic_sock *csk;
+
+		spin_lock_bh(&cp->cm_lock);
+		csk = &cp->csk_tbl[i];
+		if (test_bit(SK_F_INUSE, &csk->flags) &&
+		    csk->dst == redir->old_dst) {
+			csk->dst = redir->new_dst;
+			dst_hold(csk->dst);
+			neigh_hold(csk->dst->neighbour);
+			if (redir->old_dst->neighbour);
+				neigh_release(redir->old_dst->neighbour);
+			dst_release(redir->old_dst);
+			if (test_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags))
+				cnic_cm_update_pg(csk);
+			else
+				cnic_cm_offload_pg(csk);
+		}
+		spin_unlock_bh(&cp->cm_lock);
+	}
+
+	dst_release(redir->new_dst);
+}
+
+static void cnic_cm_upcall(struct cnic_local *cp, struct cnic_sock *csk,
+			   u8 opcode)
+{
+	struct cnic_ulp_ops *ulp_ops;
+	int ulp_type = csk->ulp_type;
+
+	rcu_read_lock();
+	ulp_ops = rcu_dereference(cp->ulp_ops[ulp_type]);
+	if (ulp_ops) {
+		if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE)
+			ulp_ops->cm_connect_complete(csk);
+		else if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_CLOSE)
+			ulp_ops->cm_close_complete(csk);
+		else if (opcode == L4_KCQE_OPCODE_VALUE_RESET_RECEIVED)
+			ulp_ops->cm_remote_abort(csk);
+		else if (opcode == L4_KCQE_OPCODE_VALUE_RESET_COMP)
+			ulp_ops->cm_abort_complete(csk);
+		else if (opcode == L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED)
+			ulp_ops->cm_remote_close(csk);
+	}
+	rcu_read_unlock();
+}
+
+static void cnic_cm_process_kcqe(struct cnic_dev *dev, struct kcqe *kcqe)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	struct l4_kcq *l4kcqe = (struct l4_kcq *) kcqe;
+	u8 opcode = l4kcqe->op_code;
+
+	if (opcode == L4_KCQE_OPCODE_VALUE_OFFLOAD_PG) {
+		u32 l5_cid = l4kcqe->pg_host_opaque;
+		struct cnic_sock *csk = &cp->csk_tbl[l5_cid];
+
+		if (!test_bit(SK_F_INUSE, &csk->flags))
+			return;
+
+		csk->pg_cid = l4kcqe->pg_cid;
+		set_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags);
+		if (cnic_ok_to_connect(csk)) {
+			set_bit(SK_F_OFFLD_PENDING, &csk->flags);
+			cnic_cm_conn_req(csk);
+		}
+
+	} else if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_RESP) {
+
+	} else if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE) {
+		u32 l5_cid = l4kcqe->conn_id;
+		struct cnic_sock *csk = &cp->csk_tbl[l5_cid];
+
+		if (test_bit(SK_F_INUSE, &csk->flags)) {
+			if (l4kcqe->status == 0)
+				set_bit(SK_F_OFFLD_COMPLETE, &csk->flags);
+			clear_bit(SK_F_OFFLD_PENDING, &csk->flags);
+			cnic_cm_upcall(cp, csk, opcode);
+		}
+
+	} else if (opcode == L4_KCQE_OPCODE_VALUE_CONNECT_CLOSE ||
+		   opcode == L4_KCQE_OPCODE_VALUE_RESET_RECEIVED ||
+		   opcode == L4_KCQE_OPCODE_VALUE_RESET_COMP) {
+		u32 l5_cid = l4kcqe->conn_id;
+		struct cnic_sock *csk = &cp->csk_tbl[l5_cid];
+
+		if (test_bit(SK_F_INUSE, &csk->flags)) {
+			clear_bit(SK_F_OFFLD_COMPLETE, &csk->flags);
+			clear_bit(SK_F_OFFLD_PENDING, &csk->flags);
+			cnic_cm_upload_pg(csk);
+			clear_bit(SK_F_PG_OFFLD_COMPLETE, &csk->flags);
+			cnic_cm_upcall(cp, csk, opcode);
+		}
+	} else if (opcode == L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED) {
+		u32 l5_cid = l4kcqe->conn_id;
+		struct cnic_sock *csk = &cp->csk_tbl[l5_cid];
+
+		if (test_bit(SK_F_INUSE, &csk->flags))
+			cnic_cm_upcall(cp, csk, opcode);
+	}
+}
+
+static void cnic_cm_indicate_kcqe(void *data, struct kcqe *kcqe[], u32 num_cqe)
+{
+	struct cnic_dev *dev = data;
+	int i;
+	struct cnic_local *cp = dev->cnic_priv;
+
+	for (i = 0; i < num_cqe; i++)
+		cnic_queue_work(cp, WORK_TYPE_KCQE, kcqe[i]);
+
+	schedule_work(&cp->cnic_task);
+}
+
+static void cnic_cm_indicate_event(void *data, unsigned long event)
+{
+}
+
+static void cnic_cm_dummy(void *data)
+{
+}
+
+static struct cnic_ulp_ops cm_ulp_ops = {
+	.cnic_start		= cnic_cm_dummy,
+	.cnic_stop		= cnic_cm_dummy,
+	.indicate_kcqes		= cnic_cm_indicate_kcqe,
+	.indicate_netevent	= cnic_cm_indicate_event,
+	.indicate_inetevent	= cnic_cm_indicate_event,
+};
+
+static void cnic_task(struct work_struct *work)
+{
+	struct cnic_local *cp =
+		container_of(work, struct cnic_local, cnic_task);
+	struct cnic_dev *dev = cp->dev;
+	u32 cons = cp->cnic_wr_cons;
+	u32 prod = cp->cnic_wr_prod;
+
+	while (cons != prod) {
+		struct cnic_work_node *node;
+
+		node = &cp->cnic_work_ring[cons];
+		if (node->work_type == WORK_TYPE_KCQE)
+			cnic_cm_process_kcqe(dev, &node->work_data.kcqe);
+		else if (node->work_type == WORK_TYPE_NEIGH_UPDATE)
+			cnic_cm_process_neigh(dev, node->work_data.neigh);
+		else if (node->work_type == WORK_TYPE_REDIRECT)
+			cnic_cm_process_redirect(dev,
+				&node->work_data.cnic_redir);
+		cons++;
+		cons &= WORK_RING_SIZE_MASK;
+	}
+	cp->cnic_wr_cons = cons;
+}
+
+static void cnic_free_dev(struct cnic_dev *dev)
+{
+	cnic_free_resc(dev);
+	cnic_unregister_sysfs(dev);
+	pci_dev_put(dev->pcidev);
+	dev_put(dev->netdev);
+	kfree(dev);
+}
+
+static void cnic_cm_free_mem(struct cnic_dev *dev)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+
+	kfree(cp->csk_tbl);
+	cp->csk_tbl = NULL;
+}
+
+static int cnic_cm_alloc_mem(struct cnic_dev *dev)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+
+	cp->csk_tbl = kmalloc(sizeof(struct cnic_sock) * MAX_CM_SK_TBL_SZ,
+			      GFP_KERNEL);
+	if (!cp->csk_tbl)
+		return -ENOMEM;
+	memset(cp->csk_tbl, 0, sizeof(struct cnic_sock) * MAX_CM_SK_TBL_SZ);
+	return 0;
+}
+
+static int cnic_cm_open(struct cnic_dev *dev)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	u32 seed;
+	int err;
+
+	get_random_bytes(&seed, 4);
+	bnx2_ctx_wr(dev->bp, 45, 0, seed);
+
+	err = cnic_cm_alloc_mem(dev);
+	if (err) {
+		cnic_cm_free_mem(dev);
+		return err;
+	}
+
+	spin_lock_init(&cp->cm_lock);
+
+	INIT_WORK(&cp->cnic_task, cnic_task);
+
+	cp->cm_nb.notifier_call = cnic_net_callback;
+	register_netevent_notifier(&cp->cm_nb);
+
+	dev->cm_create = cnic_cm_create;
+	dev->cm_destroy = cnic_cm_destroy;
+	dev->cm_connect = cnic_cm_connect;
+	dev->cm_abort = cnic_cm_abort;
+	dev->cm_close = cnic_cm_close;
+	dev->cm_select_dev = cnic_cm_select_dev;
+
+	cp->ulp_handle[CNIC_ULP_L4] = dev;
+	rcu_assign_pointer(cp->ulp_ops[CNIC_ULP_L4], &cm_ulp_ops);
+	return 0;
+}
+
+static void cnic_cm_cleanup(struct cnic_sock *csk)
+{
+	clear_bit(SK_F_INUSE, &csk->flags);
+	if (csk->dst) {
+		if (csk->dst->neighbour)
+			neigh_release(csk->dst->neighbour);
+		dst_release(csk->dst);
+		csk->dst = NULL;
+	}
+}
+
+static int cnic_cm_shutdown(struct cnic_dev *dev)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	int i;
+
+	unregister_netevent_notifier(&cp->cm_nb);
+
+	cancel_work_sync(&cp->cnic_task);
+
+	if (!cp->csk_tbl)
+		return 0;
+
+	for (i = 0; i < MAX_CM_SK_TBL_SZ; i++) {
+		struct cnic_sock *csk = &cp->csk_tbl[i];
+
+		cnic_cm_cleanup(csk);
+	}
+	cnic_cm_free_mem(dev);
+
+	return 0;
+}
+
+static void cnic_init_context(struct cnic_dev *dev, u32 cid)
+{
+	u32 cid_addr;
+	int i;
+
+	cid_addr = GET_CID_ADDR(cid);
+
+	for (i = 0; i < CTX_SIZE; i += 4)
+		bnx2_ctx_wr(dev->bp, cid_addr, i, 0);
+}
+
+static int cnic_start_hw(struct cnic_dev *dev)
+{
+	struct cnic_local *cp = dev->cnic_priv;
+	u32 val;
+	int err;
+
+	if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
+		return -EALREADY;
+
+	val = REG_RD(dev, BNX2_MQ_CONFIG);
+	val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE;
+	if (BCM_PAGE_BITS > 12)
+		val |= (12 - 8)  << 4;
+	else
+		val |= (BCM_PAGE_BITS - 8)  << 4;
+
+	REG_WR(dev, BNX2_MQ_CONFIG, val);
+
+	REG_WR(dev, BNX2_HC_COMP_PROD_TRIP, (2 << 16) | 8);
+	REG_WR(dev, BNX2_HC_COM_TICKS, (64 << 16) | 220);
+	REG_WR(dev, BNX2_HC_CMD_TICKS, (64 << 16) | 220);
+
+	cnic_init_context(dev, KWQ_CID);
+	cnic_init_context(dev, KCQ_CID);
+
+	cp->kwq_cid_addr = GET_CID_ADDR(KWQ_CID);
+	cp->kwq_io_addr = MB_GET_CID_ADDR(KWQ_CID) + L5_KRNLQ_HOST_QIDX;
+
+	cnic_setup_page_tbl(cp->kwq_pgtbl, KWQ_PAGE_CNT, cp->kwq_mapping);
+	
+	cp->kwq_prod_idx = 0;
+	cp->kwq_con_idx = 0;
+	
+	/* Initialize the kernel work queue context. */
+	val = KRNLQ_TYPE_TYPE_KRNLQ | KRNLQ_SIZE_TYPE_SIZE |
+	      (BCM_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ;
+	bnx2_ctx_wr(dev->bp, cp->kwq_cid_addr, L5_KRNLQ_TYPE, val);
+	
+	val = (PAGE_SIZE / sizeof(struct kwqe) - 1) << 16;
+	bnx2_ctx_wr(dev->bp, cp->kwq_cid_addr, L5_KRNLQ_QE_SELF_SEQ_MAX, val);
+	
+	val = ((PAGE_SIZE / sizeof(struct kwqe)) << 16) | KWQ_PAGE_CNT;
+	bnx2_ctx_wr(dev->bp, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_NPAGES, val);
+	
+	val = (u32) ((u64) cp->kwq_pgtbl_mapping >> 32);
+	bnx2_ctx_wr(dev->bp, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_HADDR_HI, val);
+	
+	val = (u32) cp->kwq_pgtbl_mapping;
+	bnx2_ctx_wr(dev->bp, cp->kwq_cid_addr, L5_KRNLQ_PGTBL_HADDR_LO, val);
+
+	cp->kcq_cid_addr = GET_CID_ADDR(KCQ_CID);
+	cp->kcq_io_addr = MB_GET_CID_ADDR(KCQ_CID) + L5_KRNLQ_HOST_QIDX;
+	
+	cnic_setup_page_tbl(cp->kcq_pgtbl, KCQ_PAGE_CNT, cp->kcq_mapping);
+	
+	cp->kcq_prod_idx = 0;
+	
+	/* Initialize the kernel complete queue context. */
+	val = KRNLQ_TYPE_TYPE_KRNLQ | KRNLQ_SIZE_TYPE_SIZE |
+	      (BCM_PAGE_BITS - 8) | KRNLQ_FLAGS_QE_SELF_SEQ;
+	bnx2_ctx_wr(dev->bp, cp->kcq_cid_addr, L5_KRNLQ_TYPE, val);
+	
+	val = (BCM_PAGE_SIZE / sizeof(struct kcqe) - 1) << 16;
+	bnx2_ctx_wr(dev->bp, cp->kcq_cid_addr, L5_KRNLQ_QE_SELF_SEQ_MAX, val);
+	
+	val = ((BCM_PAGE_SIZE / sizeof(struct kcqe)) << 16) | KCQ_PAGE_CNT;
+	bnx2_ctx_wr(dev->bp, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_NPAGES, val);
+	
+	val = (u32) ((u64) cp->kcq_pgtbl_mapping >> 32);
+	bnx2_ctx_wr(dev->bp, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_HADDR_HI, val);
+	
+	val = (u32) cp->kcq_pgtbl_mapping;
+	bnx2_ctx_wr(dev->bp, cp->kcq_cid_addr, L5_KRNLQ_PGTBL_HADDR_LO, val);
+
+	/* Enable Commnad Scheduler notification when we write to the
+	 * host producer index of the kernel contexts. */
+	REG_WR(dev, BNX2_MQ_KNL_CMD_MASK1, 2);
+
+	/* Enable Command Scheduler notification when we write to either
+	 * the Send Queue or Receive Queue producer indexes of the kernel
+	 * bypass contexts. */
+	REG_WR(dev, BNX2_MQ_KNL_BYP_CMD_MASK1, 7);
+	REG_WR(dev, BNX2_MQ_KNL_BYP_WRITE_MASK1, 7);
+
+	/* Notify COM when the driver post an application buffer. */
+	REG_WR(dev, BNX2_MQ_KNL_RX_V2P_MASK2, 0x2000);
+
+	/* Set the CP and COM doorbells.  These two processors polls the
+	 * doorbell for a non zero value before running.  This must be done
+	 * after setting up the kernel queue contexts. */
+	bnx2_reg_wr_ind(dev->bp, BNX2_CP_SCRATCH + 0x20, 1);
+	bnx2_reg_wr_ind(dev->bp, BNX2_COM_SCRATCH + 0x20, 1);
+
+	err = bnx2_register_cnic(dev->netdev, &my_cnic_ops, dev);
+	if (err) {
+		printk(KERN_ERR PFX "%s: bnx2_register_cnic failed\n",
+		       dev->netdev->name);
+		return -EBUSY;
+	}
+
+	set_bit(CNIC_F_CNIC_UP, &dev->flags);
+	cnic_cm_open(dev);
+
+	return 0;
+}
+
+static void cnic_stop_hw(struct cnic_dev *dev)
+{
+	if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) {
+		struct cnic_local *cp = dev->cnic_priv;
+
+		cnic_cm_shutdown(dev);
+		bnx2_unregister_cnic(dev->netdev);
+		rcu_assign_pointer(cp->ulp_ops[CNIC_ULP_L4], NULL);
+		clear_bit(CNIC_F_CNIC_UP, &dev->flags);
+		synchronize_rcu();
+
+		bnx2_reg_wr_ind(dev->bp, BNX2_CP_SCRATCH + 0x20, 0);
+		bnx2_reg_wr_ind(dev->bp, BNX2_COM_SCRATCH + 0x20, 0);
+
+		cnic_init_context(dev, KWQ_CID);
+		cnic_init_context(dev, KCQ_CID);
+	}
+}
+
+static struct cnic_dev *init_cnic(struct net_device *dev)
+{
+	struct cnic_dev *cdev;
+	struct cnic_local *bp;
+	struct bnx2 *bnx2_bp = netdev_priv(dev);
+	int alloc_size;
+
+	alloc_size = sizeof(struct cnic_dev) + sizeof(struct cnic_local);
+
+	cdev = kmalloc(alloc_size , GFP_KERNEL);
+	if (cdev == NULL) {
+		printk(KERN_ERR PFX "%s: allocate dev struct failure\n",
+		       dev->name);
+		return cdev;
+	}
+	memset(cdev, 0, alloc_size);
+
+	cdev->netdev = dev;
+	cdev->bp = bnx2_bp;
+	cdev->pcidev = bnx2_bp->pdev;
+	cdev->regview = bnx2_bp->regview;
+	cdev->cnic_priv = (char *)cdev + sizeof(struct cnic_dev);
+	cdev->submit_kwqes = cnic_submit_kernel_wqes;
+	cdev->register_device = cnic_register_device;
+	cdev->unregister_device = cnic_unregister_device;
+
+	if (cnic_alloc_resc(cdev)) {
+		printk(KERN_ERR PFX "%s: allocate resource failure\n",
+		       dev->name);
+		kfree(cdev);
+		return NULL;
+	}
+	bp = cdev->cnic_priv;
+	bp->dev = cdev;
+	spin_lock_init(&bp->cnic_ulp_lock);
+	dev_hold(cdev->netdev);
+	pci_dev_get(cdev->pcidev);
+
+	return cdev;
+}
+
+static struct cnic_dev *is_cnic_dev(struct net_device *dev)
+{
+	struct ethtool_drvinfo drvinfo;
+	struct cnic_dev *cdev = NULL;
+
+	if (dev->ethtool_ops && dev->ethtool_ops->get_drvinfo) {
+		memset(&drvinfo, 0, sizeof(drvinfo));
+		dev->ethtool_ops->get_drvinfo(dev, &drvinfo);
+		
+		if (!strcmp(drvinfo.driver, "bnx2")) {
+			cdev = init_cnic(dev);
+			if (cdev) {
+				cnic_register_sysfs(cdev);
+				write_lock(&cnic_dev_lock);
+				list_add(&cdev->list, &cnic_dev_list);
+				write_unlock(&cnic_dev_lock);
+			}
+		}
+	}
+	return cdev;
+}
+
+/**
+ * IP event handler
+ */
+static int cnic_ip_event(struct notifier_block *this, unsigned long event,
+						 void *ptr)
+{
+	struct in_ifaddr *ifa = (struct in_ifaddr *) ptr;
+	struct net_device *netdev = (struct net_device *) ifa->ifa_dev->dev;
+	struct cnic_dev *dev;
+	int if_type;
+	u32 my_dev = 0;
+
+	read_lock(&cnic_dev_lock);
+	list_for_each_entry(dev, &cnic_dev_list, list) {
+		if (netdev == dev->netdev) {
+			my_dev = 1;
+			cnic_hold(dev);
+			break;
+		}
+	}
+	read_unlock(&cnic_dev_lock);
+
+	if (my_dev) {
+		struct cnic_local *cp = dev->cnic_priv;
+
+		rcu_read_lock();
+		for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
+			struct cnic_ulp_ops *ulp_ops;
+
+			ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
+			if (ulp_ops) {
+				void *ctx = cp->ulp_handle[if_type];
+
+				ulp_ops->indicate_inetevent(ctx, event);
+			}
+		}
+		rcu_read_unlock();
+
+		cnic_put(dev);
+	}
+
+	return NOTIFY_DONE;
+}
+
+/**
+ * netdev event handler
+ */
+static int cnic_netdev_event(struct notifier_block *this, unsigned long event,
+							 void *ptr)
+{
+	struct net_device *netdev = ptr;
+	struct cnic_dev *dev;
+	int if_type;
+	u32 my_dev = 0;
+
+	read_lock(&cnic_dev_lock);
+	list_for_each_entry(dev, &cnic_dev_list, list) {
+		if (netdev == dev->netdev) {
+			my_dev = 1;
+			cnic_hold(dev);
+			break;
+		}
+	}
+	read_unlock(&cnic_dev_lock);
+
+	if (!my_dev && event == NETDEV_REGISTER) {
+		/* Check for the hot-plug device */
+		dev = is_cnic_dev(netdev);
+		if (dev) {
+			my_dev = 1;
+			cnic_hold(dev);
+		}
+	}
+	if (my_dev) {
+		struct cnic_local *cp = dev->cnic_priv;
+
+		if (event == NETDEV_REGISTER)
+			cnic_ulp_init(dev);
+		else if (event == NETDEV_UNREGISTER)
+			cnic_ulp_exit(dev);
+		else if (event == NETDEV_UP) {
+			mutex_lock(&cnic_lock);
+			set_bit(CNIC_F_IF_UP, &dev->flags);
+			if (dev->use_count) {
+				if (!cnic_start_hw(dev))
+					cnic_ulp_start(dev);
+			}
+			mutex_unlock(&cnic_lock);
+		}
+
+		rcu_read_lock();
+		for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) {
+			struct cnic_ulp_ops *ulp_ops;
+			void *ctx;
+
+			ulp_ops = rcu_dereference(cp->ulp_ops[if_type]);
+			if (!ulp_ops)
+				continue;
+
+			ctx = cp->ulp_handle[if_type];
+
+			ulp_ops->indicate_netevent(ctx, event);
+		}
+		rcu_read_unlock();
+
+		if (event == NETDEV_GOING_DOWN) {
+			mutex_lock(&cnic_lock);
+			clear_bit(CNIC_F_IF_UP, &dev->flags);
+			cnic_ulp_stop(dev);
+			cnic_stop_hw(dev);
+			mutex_unlock(&cnic_lock);
+		} else if (event == NETDEV_UNREGISTER) {
+			int i = 0;
+
+			write_lock(&cnic_dev_lock);
+			list_del_init(&dev->list);
+			write_unlock(&cnic_dev_lock);
+			while ((atomic_read(&dev->ref_count) != 1) &&
+			       i < 10) {
+				msleep(100);
+				i++;
+			}
+			if (atomic_read(&dev->ref_count) != 1)
+				printk(KERN_ERR PFX "%s: Failed waiting"
+				       " for ref count to go zero\n",
+				       dev->netdev->name);
+			cnic_free_dev(dev);
+			goto done;
+		}
+		cnic_put(dev);
+	}
+done:
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block cnic_ip_notifier = {
+	cnic_ip_event,
+	0
+};
+
+static struct notifier_block cnic_netdev_notifier = {
+	cnic_netdev_event,
+	0
+};
+
+static void cnic_release(void)
+{
+	struct cnic_dev *dev;
+
+	while (!list_empty(&cnic_dev_list)) {
+		dev = list_entry(cnic_dev_list.next, struct cnic_dev, list);
+		if (test_bit(CNIC_F_CNIC_UP, &dev->flags))
+			cnic_stop_hw(dev);
+
+		list_del_init(&dev->list);
+		cnic_free_dev(dev);
+	}
+}
+
+static int __init cnic_init(void)
+{
+	int rc = 0;
+	struct net_device *dev;
+
+	printk(KERN_INFO "%s", version);
+
+	cnic_sysfs_setup();
+
+	read_lock(&dev_base_lock);
+	/* Find Teton devices */
+	for_each_netdev(dev)
+		is_cnic_dev(dev);
+
+	read_unlock(&dev_base_lock);
+
+	rc = register_inetaddr_notifier(&cnic_ip_notifier);
+	if (rc)
+		cnic_release();
+	rc = register_netdevice_notifier(&cnic_netdev_notifier);
+	if (rc) {
+		unregister_inetaddr_notifier(&cnic_ip_notifier);
+		cnic_release();
+	}
+	return rc;
+}
+
+static void __exit cnic_exit(void)
+{
+	unregister_inetaddr_notifier(&cnic_ip_notifier);
+	unregister_netdevice_notifier(&cnic_netdev_notifier);
+	cnic_release();
+	cnic_sysfs_cleanup();
+
+	return;
+}
+
+module_init(cnic_init);
+module_exit(cnic_exit);
diff --git a/drivers/net/cnic.h b/drivers/net/cnic.h
new file mode 100644
index 0000000..6ba1f4e
--- /dev/null
+++ b/drivers/net/cnic.h
@@ -0,0 +1,163 @@
+/* cnic.h: Broadcom CNIC core network driver.
+ *
+ * Copyright (c) 2006 Broadcom Corporation
+ *
+ * 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.
+ *
+ * Written by: John(Zongxi) Chen (zongxic@broadcom.com)
+ */
+
+
+#ifndef CNIC_H
+#define CNIC_H
+
+#if !defined(__LITTLE_ENDIAN) && !defined(__BIG_ENDIAN)
+	#error "Missing either LITTLE_ENDIAN or BIG_ENDIAN definition."
+#endif
+
+#define KWQ_PAGE_CNT	4
+#define KCQ_PAGE_CNT	16
+
+#define KWQ_CID 		24
+#define KCQ_CID 		25
+
+/*
+ *	krnlq_context definition
+ */
+#define L5_KRNLQ_FLAGS	0x00000000
+#define L5_KRNLQ_SIZE	0x00000000
+#define L5_KRNLQ_TYPE	0x00000000
+#define KRNLQ_FLAGS_PG_SZ					(0xf<<0)
+#define KRNLQ_FLAGS_PG_SZ_256					(0<<0)
+#define KRNLQ_FLAGS_PG_SZ_512					(1<<0)
+#define KRNLQ_FLAGS_PG_SZ_1K					(2<<0)
+#define KRNLQ_FLAGS_PG_SZ_2K					(3<<0)
+#define KRNLQ_FLAGS_PG_SZ_4K					(4<<0)
+#define KRNLQ_FLAGS_PG_SZ_8K					(5<<0)
+#define KRNLQ_FLAGS_PG_SZ_16K					(6<<0)
+#define KRNLQ_FLAGS_PG_SZ_32K					(7<<0)
+#define KRNLQ_FLAGS_PG_SZ_64K					(8<<0)
+#define KRNLQ_FLAGS_PG_SZ_128K					(9<<0)
+#define KRNLQ_FLAGS_PG_SZ_256K					(10<<0)
+#define KRNLQ_FLAGS_PG_SZ_512K					(11<<0)
+#define KRNLQ_FLAGS_PG_SZ_1M					(12<<0)
+#define KRNLQ_FLAGS_PG_SZ_2M					(13<<0)
+#define KRNLQ_FLAGS_QE_SELF_SEQ					(1<<15)
+#define KRNLQ_SIZE_TYPE_SIZE	((((0x28 + 0x1f) & ~0x1f) / 0x20) << 16)
+#define KRNLQ_TYPE_TYPE						(0xf<<28)
+#define KRNLQ_TYPE_TYPE_EMPTY					(0<<28)
+#define KRNLQ_TYPE_TYPE_KRNLQ					(6<<28)
+
+#define L5_KRNLQ_HOST_QIDX		0x00000004
+#define L5_KRNLQ_HOST_FW_QIDX		0x00000008
+#define L5_KRNLQ_NX_QE_SELF_SEQ 	0x0000000c
+#define L5_KRNLQ_QE_SELF_SEQ_MAX	0x0000000c
+#define L5_KRNLQ_NX_QE_HADDR_HI 	0x00000010
+#define L5_KRNLQ_NX_QE_HADDR_LO 	0x00000014
+#define L5_KRNLQ_PGTBL_PGIDX		0x00000018
+#define L5_KRNLQ_NX_PG_QIDX 		0x00000018
+#define L5_KRNLQ_PGTBL_NPAGES		0x0000001c
+#define L5_KRNLQ_QIDX_INCR		0x0000001c
+#define L5_KRNLQ_PGTBL_HADDR_HI 	0x00000020
+#define L5_KRNLQ_PGTBL_HADDR_LO 	0x00000024
+
+struct cnic_redirect_entry {
+	struct dst_entry *old_dst;
+	struct dst_entry *new_dst;
+};
+
+struct cnic_work_node {
+	u32			work_type;
+#define	WORK_TYPE_KCQE			1
+#define	WORK_TYPE_NEIGH_UPDATE		2
+#define	WORK_TYPE_REDIRECT		3
+	union {
+		struct kcqe		kcqe;
+		struct neighbour	*neigh;
+		struct cnic_redirect_entry cnic_redir;
+	} work_data;
+};
+
+#define WORK_RING_SIZE		128
+#define WORK_RING_SIZE_MASK	127
+#define MAX_CM_SK_TBL_SZ	128
+#define MAX_COMPLETED_KCQE	64
+
+#define KWQE_CNT (BCM_PAGE_SIZE / sizeof(struct kwqe))
+#define KCQE_CNT (BCM_PAGE_SIZE / sizeof(struct kcqe))
+#define MAX_KWQE_CNT (KWQE_CNT - 1)
+#define MAX_KCQE_CNT (KCQE_CNT - 1)
+
+#define MAX_KWQ_IDX	((KWQ_PAGE_CNT * KWQE_CNT) - 1)
+#define MAX_KCQ_IDX	((KCQ_PAGE_CNT * KCQE_CNT) - 1)
+
+#define KWQ_PG(x) (((x) & ~MAX_KWQE_CNT) >> (BCM_PAGE_BITS - 5))
+#define KWQ_IDX(x) ((x) & MAX_KWQE_CNT)
+
+#define KCQ_PG(x) (((x) & ~MAX_KCQE_CNT) >> (BCM_PAGE_BITS - 5))
+#define KCQ_IDX(x) ((x) & MAX_KCQE_CNT)
+
+#define DEF_IPID_COUNT		0xc001
+
+#define DEF_KA_TIMEOUT		10000
+#define DEF_KA_INTERVAL		300000
+#define DEF_KA_MAX_PROBE_COUNT	3
+#define DEF_TOS			0
+#define DEF_TTL			0xfe
+#define DEF_SND_SEQ_SCALE	0
+#define DEF_RCV_BUF		0xffff
+#define DEF_SND_BUF		0xffff
+#define DEF_SEED		0
+
+struct cnic_local {
+
+	spinlock_t cnic_ulp_lock;
+	void *ulp_handle[MAX_CNIC_ULP_TYPE];
+	unsigned long ulp_flags[MAX_CNIC_ULP_TYPE];
+#define ULP_F_INIT	0
+#define ULP_F_START	1
+	struct cnic_ulp_ops *ulp_ops[MAX_CNIC_ULP_TYPE];
+
+	struct cnic_dev *dev;
+
+	u32 kwq_cid_addr;
+	u32 kcq_cid_addr;
+
+	struct kwqe	*kwq[KWQ_PAGE_CNT];
+	dma_addr_t	kwq_mapping[KWQ_PAGE_CNT];
+	u16		kwq_prod_idx;
+	u32		kwq_io_addr;
+
+	u16		kwq_con_idx;
+
+	void		*kwq_pgtbl;
+	dma_addr_t	kwq_pgtbl_mapping;
+	int		kwq_pgtbl_size;
+
+	struct kcqe	*kcq[KCQ_PAGE_CNT];
+	dma_addr_t	kcq_mapping[KCQ_PAGE_CNT];
+	u16		kcq_prod_idx;
+	u32		kcq_io_addr;
+
+	void		*kcq_pgtbl;
+	dma_addr_t	kcq_pgtbl_mapping;
+	int		kcq_pgtbl_size;
+
+	struct kcqe	*completed_kcq[MAX_COMPLETED_KCQE];
+
+	struct cnic_sock *csk_tbl;
+	spinlock_t	cm_lock;
+
+	struct notifier_block cm_nb;
+
+	struct cnic_work_node	cnic_work_ring[WORK_RING_SIZE];
+	int			cnic_wr_cons;
+	int			cnic_wr_prod;
+
+	struct work_struct	cnic_task;
+
+};
+
+#endif
diff --git a/drivers/net/cnic_cm.h b/drivers/net/cnic_cm.h
new file mode 100644
index 0000000..d6ea01d
--- /dev/null
+++ b/drivers/net/cnic_cm.h
@@ -0,0 +1,555 @@
+#ifndef __57XX_L5CM_HSI_LINUX_LE__
+#define __57XX_L5CM_HSI_LINUX_LE__
+
+/* KWQ (kernel work queue) request op codes */
+#define L4_KWQE_OPCODE_VALUE_CONNECT1               (50)
+#define L4_KWQE_OPCODE_VALUE_CONNECT2               (51)
+#define L4_KWQE_OPCODE_VALUE_CONNECT3               (52)
+#define L4_KWQE_OPCODE_VALUE_RESET                  (53)
+#define L4_KWQE_OPCODE_VALUE_CLOSE                  (54)
+#define L4_KWQE_OPCODE_VALUE_UPDATE_SECRET          (60)
+#define L4_KWQE_OPCODE_VALUE_INIT_ULP               (61)
+
+#define L4_KWQE_OPCODE_VALUE_OFFLOAD_PG             (1)
+#define L4_KWQE_OPCODE_VALUE_UPDATE_PG              (9)
+#define L4_KWQE_OPCODE_VALUE_UPLOAD_PG              (14)
+
+/* KCQ (kernel completion queue) response op codes */
+#define L4_KCQE_OPCODE_VALUE_CONNECT_RESP           (52)
+#define L4_KCQE_OPCODE_VALUE_CLOSE_COMP             (53)
+#define L4_KCQE_OPCODE_VALUE_RESET_COMP             (54)
+#define L4_KCQE_OPCODE_VALUE_FW_TCP_UPDATE          (55)
+#define L4_KCQE_OPCODE_VALUE_CONNECT_COMPLETE       (56)
+#define L4_KCQE_OPCODE_VALUE_RESET_RECEIVED         (57)
+#define L4_KCQE_OPCODE_VALUE_CLOSE_RECEIVED         (58)
+#define L4_KCQE_OPCODE_VALUE_CONNECT_CLOSE	        (59)
+#define L4_KCQE_OPCODE_VALUE_CONNECT_RESET          (60)
+#define L4_KCQE_OPCODE_VALUE_INIT_ULP               (61)
+
+#define L4_KCQE_OPCODE_VALUE_OFFLOAD_PG             (1)
+#define L4_KCQE_OPCODE_VALUE_UPLOAD_PG              (14)
+
+/* KCQ (kernel completion queue) completion status */
+#define L4_KCQE_COMPLETION_STATUS_SUCCESS		    (0)
+#define L4_KCQE_COMPLETION_STATUS_TIMEOUT        (0x93)
+
+#define L4_LAYER_CODE (4)
+
+/*
+ * L4 KCQ CQE
+ */
+struct l4_kcq {
+	u32 cid;
+	u32 pg_cid;
+	u32 conn_id;
+	u32 pg_host_opaque;
+#if defined(__BIG_ENDIAN)
+	u16 status;
+	u16 reserved1;
+#elif defined(__LITTLE_ENDIAN)
+	u16 reserved1;
+	u16 status;
+#endif
+	u32 reserved2[2];
+#if defined(__BIG_ENDIAN)
+	u8 flags;
+#define L4_KCQ_RESERVED3 (0xF<<0)
+#define L4_KCQ_RESERVED3_SHIFT 0
+#define L4_KCQ_LAYER_CODE (0x7<<4)
+#define L4_KCQ_LAYER_CODE_SHIFT 4
+#define L4_KCQ_RESERVED4 (0x1<<7)
+#define L4_KCQ_RESERVED4_SHIFT 7
+	u8 op_code;
+	u16 qe_self_seq;
+#elif defined(__LITTLE_ENDIAN)
+	u16 qe_self_seq;
+	u8 op_code;
+	u8 flags;
+#define L4_KCQ_RESERVED3 (0xF<<0)
+#define L4_KCQ_RESERVED3_SHIFT 0
+#define L4_KCQ_LAYER_CODE (0x7<<4)
+#define L4_KCQ_LAYER_CODE_SHIFT 4
+#define L4_KCQ_RESERVED4 (0x1<<7)
+#define L4_KCQ_RESERVED4_SHIFT 7
+#endif
+};
+
+
+/*
+ * L4 KCQ CQE PG upload
+ */
+struct l4_kcq_upload_pg {
+	u32 pg_cid;
+#if defined(__BIG_ENDIAN)
+	u16 pg_status;
+	u16 pg_ipid_count;
+#elif defined(__LITTLE_ENDIAN)
+	u16 pg_ipid_count;
+	u16 pg_status;
+#endif
+	u32 reserved1[5];
+#if defined(__BIG_ENDIAN)
+	u8 flags;
+#define L4_KCQ_UPLOAD_PG_RESERVED3 (0xF<<0)
+#define L4_KCQ_UPLOAD_PG_RESERVED3_SHIFT 0
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KCQ_UPLOAD_PG_RESERVED4 (0x1<<7)
+#define L4_KCQ_UPLOAD_PG_RESERVED4_SHIFT 7
+	u8 op_code;
+	u16 qe_self_seq;
+#elif defined(__LITTLE_ENDIAN)
+	u16 qe_self_seq;
+	u8 op_code;
+	u8 flags;
+#define L4_KCQ_UPLOAD_PG_RESERVED3 (0xF<<0)
+#define L4_KCQ_UPLOAD_PG_RESERVED3_SHIFT 0
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KCQ_UPLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KCQ_UPLOAD_PG_RESERVED4 (0x1<<7)
+#define L4_KCQ_UPLOAD_PG_RESERVED4_SHIFT 7
+#endif
+};
+
+
+/*
+ * Gracefully close the connection request
+ */
+struct l4_kwq_close_req {
+#if defined(__BIG_ENDIAN)
+	u8 flags;
+#define L4_KWQ_CLOSE_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_CLOSE_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT_SHIFT 7
+	u8 op_code;
+	u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+	u16 reserved0;
+	u8 op_code;
+	u8 flags;
+#define L4_KWQ_CLOSE_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_CLOSE_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CLOSE_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CLOSE_REQ_LINKED_WITH_NEXT_SHIFT 7
+#endif
+	u32 cid;
+	u32 reserved2[6];
+};
+
+
+/*
+ * The first request to be passed in order to establish connection in option2
+ */
+struct l4_kwq_connect_req1 {
+#if defined(__BIG_ENDIAN)
+	u8 flags;
+#define L4_KWQ_CONNECT_REQ1_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ1_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT_SHIFT 7
+	u8 op_code;
+	u8 reserved0;
+	u8 conn_flags;
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_IP_V6 (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_IP_V6_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_RSRV (0x1F<<3)
+#define L4_KWQ_CONNECT_REQ1_RSRV_SHIFT 3
+#elif defined(__LITTLE_ENDIAN)
+	u8 conn_flags;
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_IS_PG_HOST_OPAQUE_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_IP_V6 (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_IP_V6_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_PASSIVE_FLAG_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_RSRV (0x1F<<3)
+#define L4_KWQ_CONNECT_REQ1_RSRV_SHIFT 3
+	u8 reserved0;
+	u8 op_code;
+	u8 flags;
+#define L4_KWQ_CONNECT_REQ1_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ1_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ1_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ1_LINKED_WITH_NEXT_SHIFT 7
+#endif
+	u32 cid;
+	u32 pg_cid;
+	u32 src_ip;
+	u32 dst_ip;
+#if defined(__BIG_ENDIAN)
+	u16 dst_port;
+	u16 src_port;
+#elif defined(__LITTLE_ENDIAN)
+	u16 src_port;
+	u16 dst_port;
+#endif
+#if defined(__BIG_ENDIAN)
+	u8 rsrv1[3];
+	u8 tcp_flags;
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP (0x1<<3)
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP_SHIFT 3
+#define L4_KWQ_CONNECT_REQ1_SACK (0x1<<4)
+#define L4_KWQ_CONNECT_REQ1_SACK_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING (0x1<<5)
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING_SHIFT 5
+#define L4_KWQ_CONNECT_REQ1_RESERVED2 (0x3<<6)
+#define L4_KWQ_CONNECT_REQ1_RESERVED2_SHIFT 6
+#elif defined(__LITTLE_ENDIAN)
+	u8 tcp_flags;
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK (0x1<<0)
+#define L4_KWQ_CONNECT_REQ1_NO_DELAY_ACK_SHIFT 0
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE (0x1<<1)
+#define L4_KWQ_CONNECT_REQ1_KEEP_ALIVE_SHIFT 1
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE (0x1<<2)
+#define L4_KWQ_CONNECT_REQ1_NAGLE_ENABLE_SHIFT 2
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP (0x1<<3)
+#define L4_KWQ_CONNECT_REQ1_TIME_STAMP_SHIFT 3
+#define L4_KWQ_CONNECT_REQ1_SACK (0x1<<4)
+#define L4_KWQ_CONNECT_REQ1_SACK_SHIFT 4
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING (0x1<<5)
+#define L4_KWQ_CONNECT_REQ1_SEG_SCALING_SHIFT 5
+#define L4_KWQ_CONNECT_REQ1_RESERVED2 (0x3<<6)
+#define L4_KWQ_CONNECT_REQ1_RESERVED2_SHIFT 6
+	u8 rsrv1[3];
+#endif
+	u32 rsrv2;
+};
+
+
+/*
+ * The second ( optional )request to be passed in order to establish connection in option2 - for IPv6 only
+ */
+struct l4_kwq_connect_req2 {
+#if defined(__BIG_ENDIAN)
+	u8 flags;
+#define L4_KWQ_CONNECT_REQ2_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ2_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT_SHIFT 7
+	u8 op_code;
+	u8 reserved0;
+	u8 rsrv;
+#elif defined(__LITTLE_ENDIAN)
+	u8 rsrv;
+	u8 reserved0;
+	u8 op_code;
+	u8 flags;
+#define L4_KWQ_CONNECT_REQ2_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ2_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ2_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ2_LINKED_WITH_NEXT_SHIFT 7
+#endif
+	u32 reserved2;
+	u32 src_ip_v6_2;
+	u32 src_ip_v6_3;
+	u32 src_ip_v6_4;
+	u32 dst_ip_v6_2;
+	u32 dst_ip_v6_3;
+	u32 dst_ip_v6_4;
+};
+
+
+/*
+ * The third ( and last )request to be passed in order to establish connection in option2
+ */
+struct l4_kwq_connect_req3 {
+#if defined(__BIG_ENDIAN)
+	u8 flags;
+#define L4_KWQ_CONNECT_REQ3_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ3_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT_SHIFT 7
+	u8 op_code;
+	u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+	u16 reserved0;
+	u8 op_code;
+	u8 flags;
+#define L4_KWQ_CONNECT_REQ3_RESERVED1 (0xF<<0)
+#define L4_KWQ_CONNECT_REQ3_RESERVED1_SHIFT 0
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE (0x7<<4)
+#define L4_KWQ_CONNECT_REQ3_LAYER_CODE_SHIFT 4
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_CONNECT_REQ3_LINKED_WITH_NEXT_SHIFT 7
+#endif
+	u32 ka_timeout;
+	u32 ka_interval ;
+#if defined(__BIG_ENDIAN)
+	u8 snd_seq_scale;
+	u8 ttl;
+	u8 tos;
+	u8 ka_max_probe_count;
+#elif defined(__LITTLE_ENDIAN)
+	u8 ka_max_probe_count;
+	u8 tos;
+	u8 ttl;
+	u8 snd_seq_scale;
+#endif
+#if defined(__BIG_ENDIAN)
+	u16 pmtu;
+	u16 mss;
+#elif defined(__LITTLE_ENDIAN)
+	u16 mss;
+	u16 pmtu;
+#endif
+	u32 rcv_buf;
+	u32 snd_buf;
+	u32 seed;
+};
+
+
+/*
+ * a KWQE request to offload a PG connection
+ */
+struct l4_kwq_offload_pg {
+#if defined(__BIG_ENDIAN)
+	u8 flags;
+#define L4_KWQ_OFFLOAD_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_OFFLOAD_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT_SHIFT 7
+	u8 op_code;
+	u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+	u16 reserved0;
+	u8 op_code;
+	u8 flags;
+#define L4_KWQ_OFFLOAD_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_OFFLOAD_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_OFFLOAD_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_OFFLOAD_PG_LINKED_WITH_NEXT_SHIFT 7
+#endif
+#if defined(__BIG_ENDIAN)
+	u8 l2hdr_nbytes;
+	u8 pg_flags;
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP (0x1<<0)
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING (0x1<<1)
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING_SHIFT 1
+#define L4_KWQ_OFFLOAD_PG_RESERVED2 (0x3F<<2)
+#define L4_KWQ_OFFLOAD_PG_RESERVED2_SHIFT 2
+	u8 da0;
+	u8 da1;
+#elif defined(__LITTLE_ENDIAN)
+	u8 da1;
+	u8 da0;
+	u8 pg_flags;
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP (0x1<<0)
+#define L4_KWQ_OFFLOAD_PG_SNAP_ENCAP_SHIFT 0
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING (0x1<<1)
+#define L4_KWQ_OFFLOAD_PG_VLAN_TAGGING_SHIFT 1
+#define L4_KWQ_OFFLOAD_PG_RESERVED2 (0x3F<<2)
+#define L4_KWQ_OFFLOAD_PG_RESERVED2_SHIFT 2
+	u8 l2hdr_nbytes;
+#endif
+#if defined(__BIG_ENDIAN)
+	u8 da2;
+	u8 da3;
+	u8 da4;
+	u8 da5;
+#elif defined(__LITTLE_ENDIAN)
+	u8 da5;
+	u8 da4;
+	u8 da3;
+	u8 da2;
+#endif
+#if defined(__BIG_ENDIAN)
+	u8 sa0;
+	u8 sa1;
+	u8 sa2;
+	u8 sa3;
+#elif defined(__LITTLE_ENDIAN)
+	u8 sa3;
+	u8 sa2;
+	u8 sa1;
+	u8 sa0;
+#endif
+#if defined(__BIG_ENDIAN)
+	u8 sa4;
+	u8 sa5;
+	u16 etype;
+#elif defined(__LITTLE_ENDIAN)
+	u16 etype;
+	u8 sa5;
+	u8 sa4;
+#endif
+#if defined(__BIG_ENDIAN)
+	u16 vlan_tag;
+	u16 ipid_start;
+#elif defined(__LITTLE_ENDIAN)
+	u16 ipid_start;
+	u16 vlan_tag;
+#endif
+#if defined(__BIG_ENDIAN)
+	u16 ipid_count;
+	u16 reserved3;
+#elif defined(__LITTLE_ENDIAN)
+	u16 reserved3;
+	u16 ipid_count;
+#endif
+	u32 host_opaque;
+};
+
+
+/*
+ * Abortively close the connection request
+ */
+struct l4_kwq_reset_req {
+#if defined(__BIG_ENDIAN)
+	u8 flags;
+#define L4_KWQ_RESET_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_RESET_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_RESET_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT_SHIFT 7
+	u8 op_code;
+	u16 reserved0;
+#elif defined(__LITTLE_ENDIAN)
+	u16 reserved0;
+	u8 op_code;
+	u8 flags;
+#define L4_KWQ_RESET_REQ_RESERVED1 (0xF<<0)
+#define L4_KWQ_RESET_REQ_RESERVED1_SHIFT 0
+#define L4_KWQ_RESET_REQ_LAYER_CODE (0x7<<4)
+#define L4_KWQ_RESET_REQ_LAYER_CODE_SHIFT 4
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_RESET_REQ_LINKED_WITH_NEXT_SHIFT 7
+#endif
+	u32 cid;
+	u32 reserved2[6];
+};
+
+
+/*
+ * a KWQE request to update a PG connection
+ */
+struct l4_kwq_update_pg {
+#if defined(__BIG_ENDIAN)
+	u8 flags;
+#define L4_KWQ_UPDATE_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPDATE_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_UPDATE_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT_SHIFT 7
+	u8 opcode;
+	u16 oper16;
+#elif defined(__LITTLE_ENDIAN)
+	u16 oper16;
+	u8 opcode;
+	u8 flags;
+#define L4_KWQ_UPDATE_PG_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPDATE_PG_RESERVED1_SHIFT 0
+#define L4_KWQ_UPDATE_PG_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPDATE_PG_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPDATE_PG_LINKED_WITH_NEXT_SHIFT 7
+#endif
+	u32 pg_cid;
+	u32 pg_host_opaque;
+#if defined(__BIG_ENDIAN)
+	u8 pg_valids;
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT (0x1<<0)
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT_SHIFT 0
+#define L4_KWQ_UPDATE_PG_VALIDS_DA (0x1<<1)
+#define L4_KWQ_UPDATE_PG_VALIDS_DA_SHIFT 1
+#define L4_KWQ_UPDATE_PG_RESERVERD2 (0x3F<<2)
+#define L4_KWQ_UPDATE_PG_RESERVERD2_SHIFT 2
+	u8 pg_unused_a;
+	u16 pg_ipid_count;
+#elif defined(__LITTLE_ENDIAN)
+	u16 pg_ipid_count;
+	u8 pg_unused_a;
+	u8 pg_valids;
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT (0x1<<0)
+#define L4_KWQ_UPDATE_PG_VALIDS_IPID_COUNT_SHIFT 0
+#define L4_KWQ_UPDATE_PG_VALIDS_DA (0x1<<1)
+#define L4_KWQ_UPDATE_PG_VALIDS_DA_SHIFT 1
+#define L4_KWQ_UPDATE_PG_RESERVERD2 (0x3F<<2)
+#define L4_KWQ_UPDATE_PG_RESERVERD2_SHIFT 2
+#endif
+#if defined(__BIG_ENDIAN)
+	u16 reserverd3;
+	u8 da0;
+	u8 da1;
+#elif defined(__LITTLE_ENDIAN)
+	u8 da1;
+	u8 da0;
+	u16 reserverd3;
+#endif
+#if defined(__BIG_ENDIAN)
+	u8 da2;
+	u8 da3;
+	u8 da4;
+	u8 da5;
+#elif defined(__LITTLE_ENDIAN)
+	u8 da5;
+	u8 da4;
+	u8 da3;
+	u8 da2;
+#endif
+	u32 reserved4;
+	u32 reserved5;
+};
+
+
+/*
+ * a KWQE request to upload a PG or L4 context
+ */
+struct l4_kwq_upload {
+#if defined(__BIG_ENDIAN)
+	u8 flags;
+#define L4_KWQ_UPLOAD_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPLOAD_RESERVED1_SHIFT 0
+#define L4_KWQ_UPLOAD_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPLOAD_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT_SHIFT 7
+	u8 opcode;
+	u16 oper16;
+#elif defined(__LITTLE_ENDIAN)
+	u16 oper16;
+	u8 opcode;
+	u8 flags;
+#define L4_KWQ_UPLOAD_RESERVED1 (0xF<<0)
+#define L4_KWQ_UPLOAD_RESERVED1_SHIFT 0
+#define L4_KWQ_UPLOAD_LAYER_CODE (0x7<<4)
+#define L4_KWQ_UPLOAD_LAYER_CODE_SHIFT 4
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT (0x1<<7)
+#define L4_KWQ_UPLOAD_LINKED_WITH_NEXT_SHIFT 7
+#endif
+	u32 cid;
+	u32 reserved2[6];
+};
+
+#endif /* __57XX_L5CM_HSI_LINUX_LE__ */
diff --git a/drivers/net/cnic_if.h b/drivers/net/cnic_if.h
new file mode 100644
index 0000000..2cce89e
--- /dev/null
+++ b/drivers/net/cnic_if.h
@@ -0,0 +1,152 @@
+/* cnic_if.h: Broadcom CNIC core network driver.
+ *
+ * Copyright (c) 2006 Broadcom Corporation
+ *
+ * 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.
+ *
+ * Written by: John(Zongxi) Chen  (zongxic@broadcom.com)
+ */
+
+
+#ifndef CNIC_IF_H
+#define CNIC_IF_H
+
+#define CNIC_MODULE_VERSION	"1.1.10"
+#define CNIC_MODULE_RELDATE	"July 30, 2007"
+
+#define CNIC_ULP_RDMA		0
+#define CNIC_ULP_ISCSI		1
+#define CNIC_ULP_L4		2
+#define MAX_CNIC_ULP_TYPE_EXT	2
+#define MAX_CNIC_ULP_TYPE	3
+
+struct kwqe {
+	u32 kwqe_op_flag;
+
+	u32 kwqe_info0;
+	u32 kwqe_info1;
+	u32 kwqe_info2;
+	u32 kwqe_info3;
+	u32 kwqe_info4;
+	u32 kwqe_info5;
+	u32 kwqe_info6;
+};
+
+struct kcqe {
+	u32 kcqe_info0;
+	u32 kcqe_info1;
+	u32 kcqe_info2;
+	u32 kcqe_info3;
+	u32 kcqe_info4;
+	u32 kcqe_info5;
+	u32 kcqe_info6;
+	u32 kcqe_op_flag;
+		#define KCQE_FLAGS_LAYER_MASK		(0x7<<28)
+		#define KCQE_FLAGS_LAYER_MASK_MISC	(0<<28)
+		#define KCQE_FLAGS_LAYER_MASK_L2	(2<<28)
+		#define KCQE_FLAGS_LAYER_MASK_L3	(3<<28)
+		#define KCQE_FLAGS_LAYER_MASK_L4	(4<<28)
+		#define KCQE_FLAGS_LAYER_MASK_L5_RDMA	(5<<28)
+		#define KCQE_FLAGS_LAYER_MASK_L5_ISCSI	(6<<28)
+		#define KCQE_FLAGS_NEXT 		(1<<31)
+};
+
+struct cnic_sockaddr {
+	struct sockaddr_in	local_addr;
+	struct sockaddr_in	remote_addr;
+};
+
+struct cnic_sock {
+	struct cnic_dev *dev;
+	void	*context;
+	u32	src_ip;
+	u32	dst_ip;
+	u16	src_port;
+	u16	dst_port;
+	u16	vlan_id;
+	struct dst_entry *dst;
+	u32	cid;
+	u32	l5_cid;
+	u32	pg_cid;
+	int	ulp_type;
+
+	u32	ka_timeout;
+	u32	ka_interval;
+	u8	ka_max_probe_count;
+	u8	tos;
+	u8	ttl;
+	u8	snd_seq_scale;
+	u32	rcv_buf;
+	u32	snd_buf;
+	u32	seed;
+
+	unsigned long	tcp_flags;
+#define SK_TCP_NO_DELAY_ACK	0
+#define SK_TCP_KEEP_ALIVE	1
+#define SK_TCP_NAGLE		2
+#define SK_TCP_TIMESTAMP	3
+#define SK_TCP_SACK		4
+#define SK_TCP_SEG_SCALING	5
+	unsigned long	flags;
+#define SK_F_INUSE		0
+#define SK_F_OFFLD_COMPLETE	1
+#define SK_F_OFFLD_PENDING	2
+#define SK_F_PG_OFFLD_COMPLETE	3
+	struct kwqe kwqe1;
+	struct kwqe kwqe2;
+	struct kwqe kwqe3;
+};
+
+struct cnic_dev {
+	struct net_device	*netdev;
+	struct bnx2		*bp;
+	struct pci_dev		*pcidev;
+	void __iomem		*regview;
+	struct list_head	list;
+	struct class_device	class_dev;
+
+	int (*register_device)(struct cnic_dev *dev, int ulp_type,
+			       void *ulp_ctx);
+	int (*unregister_device)(struct cnic_dev *dev, int ulp_type);
+	int (*submit_kwqes)(struct cnic_dev *dev, struct kwqe *wqes[],
+				u32 num_wqes);
+
+	int (*cm_create)(struct cnic_dev *, int, u32, u32, struct cnic_sock **,
+			 void *);
+	int (*cm_destroy)(struct cnic_sock *);
+	int (*cm_connect)(struct cnic_sock *, struct cnic_sockaddr *);
+	int (*cm_abort)(struct cnic_sock *);
+	int (*cm_close)(struct cnic_sock *);
+	struct cnic_dev *(*cm_select_dev)(struct sockaddr_in *, int ulp_type);
+	unsigned long flags;
+#define CNIC_F_IF_UP		0
+#define CNIC_F_CNIC_UP		1
+	atomic_t ref_count;
+	int use_count;
+	void *cnic_priv;
+};
+
+struct cnic_ulp_ops {
+	void (*cnic_init)(struct cnic_dev *dev);
+	void (*cnic_exit)(struct cnic_dev *dev);
+	void (*cnic_start)(void *ulp_ctx);
+	void (*cnic_stop)(void *ulp_ctx);
+	void (*indicate_kcqes)(void *ulp_ctx, struct kcqe *cqes[],
+				u32 num_cqes);
+	void (*indicate_netevent)(void *ulp_ctx, unsigned long event);
+	void (*indicate_inetevent)(void *ulp_ctx, unsigned long event);
+	void (*cm_connect_complete)(struct cnic_sock *);
+	void (*cm_close_complete)(struct cnic_sock *);
+	void (*cm_abort_complete)(struct cnic_sock *);
+	void (*cm_remote_close)(struct cnic_sock *);
+	void (*cm_remote_abort)(struct cnic_sock *);
+	struct module *owner;
+};
+
+extern int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops);
+
+extern int cnic_unregister_driver(int ulp_type);
+
+#endif



^ permalink raw reply related

* [RFC 0/2][BNX2]: Add iSCSI support to BNX2 devices.
From: Michael Chan @ 2007-08-04  4:03 UTC (permalink / raw)
  To: davem, mchristi, netdev, open-iscsi; +Cc: anilgv, talm, lusinsky, uri

[BNX2]: Add iSCSI support to BNX2 devices.

Modify bnx2 and add a cnic driver to support some offload functions
needed by iSCSI.

Add a new open-iscsi driver to support iSCSI offload on bnx2 devices.

Signed-off-by: Anil Veerabhadrappa <anilgv@broadcom.com>
Signed-off-by: Michael Chan <mchan@broadcom.com>

--

The complete patch is in:

ftp://Net_sys_anon@ftp1.broadcom.com/0001-BNX2-Add-iSCSI-support-to-BNX2-devices.patch

I broke this into 2 patches and omitted the firmware blob in the next 2
emails for review.

---
 drivers/net/Kconfig                       |   10 +
 drivers/net/Makefile                      |    1 +
 drivers/net/bnx2.c                        |  116 +-
 drivers/net/bnx2.h                        |   25 +-
 drivers/net/bnx2_fw.h                     | 7036 ++++++++++++++++++-----------
 drivers/net/cnic.c                        | 1885 ++++++++
 drivers/net/cnic.diff                     |  363 ++
 drivers/net/cnic.h                        |  163 +
 drivers/net/cnic_cm.h                     |  555 +++
 drivers/net/cnic_if.h                     |  152 +
 drivers/scsi/Kconfig                      |    2 +
 drivers/scsi/Makefile                     |    1 +
 drivers/scsi/bnx2i/57xx_iscsi_constants.h |  212 +
 drivers/scsi/bnx2i/57xx_iscsi_hsi.h       | 1501 ++++++
 drivers/scsi/bnx2i/Kconfig                |    7 +
 drivers/scsi/bnx2i/Makefile               |    4 +
 drivers/scsi/bnx2i/bnx2i.h                |  828 ++++
 drivers/scsi/bnx2i/bnx2i_hwi.c            | 1993 ++++++++
 drivers/scsi/bnx2i/bnx2i_init.c           |  393 ++
 drivers/scsi/bnx2i/bnx2i_iscsi.c          | 3718 +++++++++++++++
 drivers/scsi/bnx2i/bnx2i_sysfs.c          |  616 +++



^ permalink raw reply

* Re: Distributed storage.
From: Dave Dillow @ 2007-08-04  2:51 UTC (permalink / raw)
  To: Manu Abraham; +Cc: Evgeniy Polyakov, netdev, linux-kernel, linux-fsdevel
In-Reply-To: <1a297b360708022204u4fc7603pb6baebe2bdf28618@mail.gmail.com>

On Fri, 2007-08-03 at 09:04 +0400, Manu Abraham wrote:
> On 7/31/07, Evgeniy Polyakov <johnpol@2ka.mipt.ru> wrote:
> 
> > TODO list currently includes following main items:
> >     * redundancy algorithm (drop me a request of your own, but it is highly
> >         unlikley that Reed-Solomon based will ever be used - it is too slow
> >         for distributed RAID, I consider WEAVER codes)
> 
> 
> LDPC codes[1][2] have been replacing Turbo code[3] with regards to
> communication links and we have been seeing that transition. (maybe
> helpful, came to mind seeing the mention of Turbo code) Don't know how
> weaver compares to LDPC, though found some comparisons [4][5] But
> looking at fault tolerance figures, i guess Weaver is much better.
> 
> [1] http://www.ldpc-codes.com/
> [2] http://portal.acm.org/citation.cfm?id=1240497
> [3] http://en.wikipedia.org/wiki/Turbo_code
> [4] http://domino.research.ibm.com/library/cyberdig.nsf/papers/BD559022A190D41C85257212006CEC11/$File/rj10391.pdf
> [5] http://hplabs.hp.com/personal/Jay_Wylie/publications/wylie_dsn2007.pdf

Searching Google for Dr. Plank's work at the University of TN turns up
some analysis of using LDPC codes in storage systems.

http://www.google.com/search?hl=en&q=plank+ldpc&btnG=Google+Search

Patents are an issue to watch out for around the use of Tornado/Raptor
codes. I've not researched it, but I believe there be dragons there.

^ permalink raw reply

* [patch 4/7] CAN: Add broadcast manager (bcm) protocol
From: Urs Thuermann @ 2007-08-04  2:07 UTC (permalink / raw)
  To: netdev; +Cc: urs, oliver, davem, kaber, urs.thuermann, oliver.hartkopp
In-Reply-To: <20070804020657.25572.0@janus.isnogud.escape.de>

[-- Attachment #1: 04-can-bcm-proto.diff --]
[-- Type: text/plain, Size: 49996 bytes --]

This patch adds the CAN broadcast manager (bcm) protocol.

Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>

---
 include/linux/can/bcm.h |   65 +
 net/can/Kconfig         |   28 
 net/can/Makefile        |    3 
 net/can/bcm.c           | 1755 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 1851 insertions(+)

Index: net-2.6/include/linux/can/bcm.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/include/linux/can/bcm.h	2007-08-03 11:21:51.000000000 +0200
@@ -0,0 +1,65 @@
+/*
+ * linux/can/bcm.h
+ *
+ * Definitions for CAN Broadcast Manager (BCM)
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_BCM_H
+#define CAN_BCM_H
+
+/**
+ * struct bcm_msg_head - head of messages to/from the broadcast manager
+ * @opcode:    opcode, see enum below.
+ * @flags:     special flags, see below.
+ * @count:     number of frames to send before changing interval.
+ * @ival1:     interval for the first @count frames.
+ * @ival2:     interval for the following frames.
+ * @can_id:    CAN ID of frames to be sent or received.
+ * @nframes:   number of frames appended to the message head.
+ * @frames:    array of CAN frames.
+ */
+struct bcm_msg_head {
+	int opcode;
+	int flags;
+	int count;
+	struct timeval ival1, ival2;
+	canid_t can_id;
+	int nframes;
+	struct can_frame frames[0];
+};
+
+enum {
+	TX_SETUP = 1,	/* create (cyclic) transmission task */
+	TX_DELETE,	/* remove (cyclic) transmission task */
+	TX_READ,	/* read properties of (cyclic) transmission task */
+	TX_SEND,	/* send one CAN frame */
+	RX_SETUP,	/* create RX content filter subscription */
+	RX_DELETE,	/* remove RX content filter subscription */
+	RX_READ,	/* read properties of RX content filter subscription */
+	TX_STATUS,	/* reply to TX_READ request */
+	TX_EXPIRED,	/* notification on performed transmissions (count=0) */
+	RX_STATUS,	/* reply to RX_READ request */
+	RX_TIMEOUT,	/* cyclic message is absent */
+	RX_CHANGED	/* updated CAN frame (detected content change) */
+};
+
+#define SETTIMER            0x0001
+#define STARTTIMER          0x0002
+#define TX_COUNTEVT         0x0004
+#define TX_ANNOUNCE         0x0008
+#define TX_CP_CAN_ID        0x0010
+#define RX_FILTER_ID        0x0020
+#define RX_CHECK_DLC        0x0040
+#define RX_NO_AUTOTIMER     0x0080
+#define RX_ANNOUNCE_RESUME  0x0100
+#define TX_RESET_MULTI_IDX  0x0200
+#define RX_RTR_FRAME        0x0400
+
+#endif /* CAN_BCM_H */
Index: net-2.6/net/can/Kconfig
===================================================================
--- net-2.6.orig/net/can/Kconfig	2007-08-03 11:21:48.000000000 +0200
+++ net-2.6/net/can/Kconfig	2007-08-03 11:21:51.000000000 +0200
@@ -42,6 +42,34 @@
 	  Say Y here if you want non-root users to be able to access CAN_RAW
 	  sockets.
 
+config CAN_BCM
+	tristate "Broadcast Manager CAN Protocol (with content filtering)"
+	depends on CAN
+	default N
+	---help---
+	  The Broadcast Manager offers content filtering, timeout monitoring,
+	  sending of RTR-frames and cyclic CAN messages without permanent user
+	  interaction. The BCM can be 'programmed' via the BSD socket API and
+	  informs you on demand e.g. only on content updates / timeouts.
+	  You probably want to use the bcm socket in most cases where cyclic
+	  CAN messages are used on the bus (e.g. in automotive environments).
+	  To use the Broadcast Manager, use AF_CAN with protocol CAN_BCM.
+
+config CAN_BCM_USER
+	bool "Allow non-root users to access CAN broadcast manager sockets"
+	depends on CAN_BCM
+	default N
+	---help---
+	  The Controller Area Network is a local field bus transmitting only
+	  broadcast messages without any routing and security concepts.
+	  In the majority of cases the user application has to deal with
+	  raw CAN frames. Therefore it might be reasonable NOT to restrict
+	  the CAN access only to the user root, as known from other networks.
+	  Since CAN_BCM sockets can only send and receive frames to/from CAN
+	  interfaces this does not affect security of others networks.
+	  Say Y here if you want non-root users to be able to access CAN_BCM
+	  sockets.
+
 config CAN_DEBUG_CORE
 	bool "CAN Core debugging messages"
 	depends on CAN
Index: net-2.6/net/can/Makefile
===================================================================
--- net-2.6.orig/net/can/Makefile	2007-08-03 11:21:48.000000000 +0200
+++ net-2.6/net/can/Makefile	2007-08-03 11:21:51.000000000 +0200
@@ -7,3 +7,6 @@
 
 obj-$(CONFIG_CAN_RAW)	+= can-raw.o
 can-raw-objs		:= raw.o
+
+obj-$(CONFIG_CAN_BCM)	+= can-bcm.o
+can-bcm-objs		:= bcm.o
Index: net-2.6/net/can/bcm.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/bcm.c	2007-08-03 11:21:51.000000000 +0200
@@ -0,0 +1,1755 @@
+/*
+ * bcm.c - Broadcast Manager to filter/send (cyclic) CAN content
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, the following disclaimer and
+ *    the referenced file 'COPYING'.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/uio.h>
+#include <linux/poll.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <linux/can/bcm.h>
+#include <net/sock.h>
+
+/* use of last_frames[index].can_dlc */
+#define RX_RECV    0x40 /* received data for this element */
+#define RX_THR     0x80 /* element not been sent due to throttle feature */
+#define BCM_CAN_DLC_MASK 0x0F /* clean private flags in can_dlc by masking */
+
+/* get best masking value for can_rx_register() for a given single can_id */
+#define REGMASK(id) ((id & CAN_RTR_FLAG) | ((id & CAN_EFF_FLAG) ? \
+			(CAN_EFF_MASK | CAN_EFF_FLAG) : CAN_SFF_MASK))
+
+#define IDENT "bcm"
+#define CAN_BCM_VERSION CAN_VERSION
+static __initdata const char banner[] = KERN_INFO
+	"can: broadcast manager protocol (rev " CAN_BCM_VERSION ")\n";
+
+MODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+/* easy access to can_frame payload */
+static inline u64 GET_U64(const struct can_frame *cp)
+{
+	return *(u64 *)cp->data;
+}
+
+struct bcm_op {
+	struct list_head list;
+	int ifindex;
+	canid_t can_id;
+	int flags;
+	unsigned long j_ival1, j_ival2, j_lastmsg;
+	unsigned long frames_abs, frames_filtered;
+	struct timer_list timer, thrtimer;
+	struct timeval ival1, ival2;
+	ktime_t rx_stamp;
+	int rx_ifindex;
+	int count;
+	int nframes;
+	int currframe;
+	struct can_frame *frames;
+	struct can_frame *last_frames;
+	struct can_frame sframe;
+	struct can_frame last_sframe;
+	struct sock *sk;
+	struct net_device *rx_reg_dev;
+};
+
+static struct proc_dir_entry *proc_dir;
+
+#ifdef CONFIG_CAN_BCM_USER
+#define BCM_CAP (-1)
+#else
+#define BCM_CAP CAP_NET_RAW
+#endif
+
+struct bcm_sock {
+	struct sock sk;
+	int bound;
+	int ifindex;
+	struct notifier_block notifier;
+	struct list_head rx_ops;
+	struct list_head tx_ops;
+	unsigned long dropped_usr_msgs;
+	struct proc_dir_entry *bcm_proc_read;
+	char procname [9]; /* pointer printed in ASCII with \0 */
+};
+
+static inline struct bcm_sock *bcm_sk(const struct sock *sk)
+{
+	return (struct bcm_sock *)sk;
+}
+
+#define CFSIZ sizeof(struct can_frame)
+#define OPSIZ sizeof(struct bcm_op)
+#define MHSIZ sizeof(struct bcm_msg_head)
+
+/*
+ * rounded_tv2jif - calculate jiffies from timeval including optional up
+ * @tv: pointer to timeval
+ *
+ * Description:
+ * In opposite to timeval_to_jiffies() provided in include/linux/jiffies.h this
+ * function is intentionally more relaxed on precise timer ticks to get exact
+ * one jiffy for requested 1000us on a 1000HZ machine.
+ * This code is to be removed when upgrading to kernel hrtimer.
+ *
+ * Return:
+ *  calculated jiffies (max: ULONG_MAX)
+ */
+static unsigned long rounded_tv2jif(const struct timeval *tv)
+{
+	unsigned long sec  = tv->tv_sec;
+	unsigned long usec = tv->tv_usec;
+	unsigned long jif;
+
+	if (sec > ULONG_MAX / HZ)
+		return ULONG_MAX;
+
+	/* round up to get at least the requested time */
+	usec += 1000000 / HZ - 1;
+
+	jif  = usec / (1000000 / HZ);
+
+	if (sec * HZ > ULONG_MAX - jif)
+		return ULONG_MAX;
+
+	return jif + sec * HZ;
+}
+
+/*
+ * procfs functions
+ */
+static char *bcm_proc_getifname(int ifindex)
+{
+	struct net_device *dev;
+
+	if (!ifindex)
+		return "any";
+
+	dev = __dev_get_by_index(ifindex); /* no usage counting */
+	if (dev)
+		return dev->name;
+
+	return "???";
+}
+
+static int bcm_read_proc(char *page, char **start, off_t off,
+			 int count, int *eof, void *data)
+{
+	int len = 0;
+	struct sock *sk = (struct sock *)data;
+	struct bcm_sock *bo = bcm_sk(sk);
+	struct bcm_op *op;
+
+	len += snprintf(page + len, PAGE_SIZE - len, ">>> socket %p",
+			sk->sk_socket);
+	len += snprintf(page + len, PAGE_SIZE - len, " / sk %p", sk);
+	len += snprintf(page + len, PAGE_SIZE - len, " / bo %p", bo);
+	len += snprintf(page + len, PAGE_SIZE - len, " / dropped %lu",
+			bo->dropped_usr_msgs);
+	len += snprintf(page + len, PAGE_SIZE - len, " / bound %s",
+			bcm_proc_getifname(bo->ifindex));
+	len += snprintf(page + len, PAGE_SIZE - len, " <<<\n");
+
+	list_for_each_entry(op, &bo->rx_ops, list) {
+
+		unsigned long reduction;
+
+		/* print only active entries & prevent division by zero */
+		if (!op->frames_abs)
+			continue;
+
+		len += snprintf(page + len, PAGE_SIZE - len,
+				"rx_op: %03X %-5s ",
+				op->can_id, bcm_proc_getifname(op->ifindex));
+		len += snprintf(page + len, PAGE_SIZE - len, "[%d]%c ",
+				op->nframes,
+				(op->flags & RX_CHECK_DLC)?'d':' ');
+		if (op->j_ival1)
+			len += snprintf(page + len, PAGE_SIZE - len,
+					"timeo=%ld ", op->j_ival1);
+
+		if (op->j_ival2)
+			len += snprintf(page + len, PAGE_SIZE - len,
+					"thr=%ld ", op->j_ival2);
+
+		len += snprintf(page + len, PAGE_SIZE - len,
+				"# recv %ld (%ld) => reduction: ",
+				op->frames_filtered, op->frames_abs);
+
+		reduction = 100 - (op->frames_filtered * 100) / op->frames_abs;
+
+		len += snprintf(page + len, PAGE_SIZE - len, "%s%ld%%\n",
+				(reduction == 100)?"near ":"", reduction);
+
+		if (len > PAGE_SIZE - 200) {
+			/* mark output cut off */
+			len += snprintf(page + len, PAGE_SIZE - len, "(..)\n");
+			break;
+		}
+	}
+
+	list_for_each_entry(op, &bo->tx_ops, list) {
+
+		len += snprintf(page + len, PAGE_SIZE - len,
+				"tx_op: %03X %s [%d] ",
+				op->can_id, bcm_proc_getifname(op->ifindex),
+				op->nframes);
+		if (op->j_ival1)
+			len += snprintf(page + len, PAGE_SIZE - len, "t1=%ld ",
+					op->j_ival1);
+
+		if (op->j_ival2)
+			len += snprintf(page + len, PAGE_SIZE - len, "t2=%ld ",
+					op->j_ival2);
+
+		len += snprintf(page + len, PAGE_SIZE - len, "# sent %ld\n",
+				op->frames_abs);
+
+		if (len > PAGE_SIZE - 100) {
+			/* mark output cut off */
+			len += snprintf(page + len, PAGE_SIZE - len, "(..)\n");
+			break;
+		}
+	}
+
+	len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+	*eof = 1;
+	return len;
+}
+
+/*
+ * bcm_can_tx - send the (next) CAN frame to the appropriate CAN interface
+ *              of the given bcm tx op
+ */
+static void bcm_can_tx(struct bcm_op *op)
+{
+	struct sk_buff *skb;
+	struct net_device *dev;
+	struct can_frame *cf = &op->frames[op->currframe];
+
+	DBG_FRAME("BCM: bcm_can_tx: sending frame", cf);
+
+	/* no target device? => exit */
+	if (!op->ifindex)
+		return;
+
+	dev = dev_get_by_index(op->ifindex);
+
+	if (!dev) {
+		/* RFC: should this bcm_op remove itself here? */
+		return;
+	}
+
+	skb = alloc_skb(CFSIZ,
+			in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+
+	if (!skb)
+		goto out;
+
+	memcpy(skb_put(skb, CFSIZ), cf, CFSIZ);
+
+	/* send with loopback */
+	skb->dev = dev;
+	skb->sk = op->sk;
+	can_send(skb, 1);
+
+	/* update statistics */
+	op->currframe++;
+	op->frames_abs++;
+
+	/* reached last frame? */
+	if (op->currframe >= op->nframes)
+		op->currframe = 0;
+ out:
+	dev_put(dev);
+}
+
+/*
+ * bcm_send_to_user - send a BCM message to the userspace
+ *                    (consisting of bcm_msg_head + x CAN frames)
+ */
+static void bcm_send_to_user(struct bcm_op *op, struct bcm_msg_head *head,
+			     struct can_frame *frames, int has_timestamp)
+{
+	struct sk_buff *skb;
+	struct can_frame *firstframe;
+	struct sockaddr_can *addr;
+	struct sock *sk = op->sk;
+	int datalen = head->nframes * CFSIZ;
+	int err;
+
+	skb = alloc_skb(sizeof(*head) + datalen,
+			in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+	if (!skb)
+		return;
+
+	memcpy(skb_put(skb, sizeof(*head)), head, sizeof(*head));
+
+	if (head->nframes) {
+		/* can_frames starting here */
+		firstframe = (struct can_frame *) skb_tail_pointer(skb);
+
+		memcpy(skb_put(skb, datalen), frames, datalen);
+
+		/*
+		 * the BCM uses the can_dlc-element of the can_frame
+		 * structure for internal purposes. This is only
+		 * relevant for updates that are generated by the
+		 * BCM, where nframes is 1
+		 */
+		if (head->nframes == 1)
+			firstframe->can_dlc &= BCM_CAN_DLC_MASK;
+	}
+
+	if (has_timestamp) {
+		/* restore rx timestamp */
+		skb->tstamp = op->rx_stamp;
+	}
+
+	/* restore originator for recvfrom() */
+	addr = (struct sockaddr_can *)skb->cb;
+	memset(addr, 0, sizeof(*addr));
+	addr->can_family  = AF_CAN;
+	addr->can_ifindex = op->rx_ifindex;
+
+	err = sock_queue_rcv_skb(sk, skb);
+	if (err < 0) {
+		struct bcm_sock *bo = bcm_sk(sk);
+
+		DBG("sock_queue_rcv_skb failed: %d\n", err);
+		kfree_skb(skb);
+		/* don't care about overflows in this statistic */
+		bo->dropped_usr_msgs++;
+	}
+}
+
+/*
+ * bcm_tx_timeout_handler - performes cyclic CAN frame transmissions
+ */
+static void bcm_tx_timeout_handler(unsigned long data)
+{
+	struct bcm_op *op = (struct bcm_op *)data;
+
+	DBG("Called with bcm_op %p\n", op);
+
+	if (op->j_ival1 && (op->count > 0)) {
+
+		op->count--;
+		if (!op->count && (op->flags & TX_COUNTEVT)) {
+			struct bcm_msg_head msg_head;
+
+			/* create notification to user */
+			DBG("sending TX_EXPIRED for can_id %03X\n",
+			    op->can_id);
+
+			msg_head.opcode  = TX_EXPIRED;
+			msg_head.flags   = op->flags;
+			msg_head.count   = op->count;
+			msg_head.ival1   = op->ival1;
+			msg_head.ival2   = op->ival2;
+			msg_head.can_id  = op->can_id;
+			msg_head.nframes = 0;
+
+			bcm_send_to_user(op, &msg_head, NULL, 0);
+		}
+	}
+
+	DBG("count=%d j_ival1=%ld j_ival2=%ld\n",
+	    op->count, op->j_ival1, op->j_ival2);
+
+	if (op->j_ival1 && (op->count > 0)) {
+
+		op->timer.expires = jiffies + op->j_ival1;
+		add_timer(&op->timer);
+
+		DBG("adding timer ival1. func=%p data=%p exp=0x%08X\n",
+		    op->timer.function,
+		    (char *) op->timer.data,
+		    (unsigned int) op->timer.expires);
+
+		/* send (next) frame */
+		bcm_can_tx(op);
+
+	} else {
+		if (op->j_ival2) {
+			op->timer.expires = jiffies + op->j_ival2;
+			add_timer(&op->timer);
+
+			DBG("adding timer ival2. func=%p data=%p exp=0x%08X\n",
+			    op->timer.function,
+			    (char *) op->timer.data,
+			    (unsigned int) op->timer.expires);
+
+			/* send (next) frame */
+			bcm_can_tx(op);
+
+		} else
+			DBG("no timer restart\n");
+	}
+
+	return;
+}
+
+/*
+ * bcm_rx_changed - create a RX_CHANGED notification due to changed content
+ */
+static void bcm_rx_changed(struct bcm_op *op, struct can_frame *data)
+{
+	struct bcm_msg_head head;
+
+	op->j_lastmsg = jiffies;
+
+	/* update statistics */
+	op->frames_filtered++;
+
+	/* prevent statistics overflow */
+	if (op->frames_filtered > ULONG_MAX/100)
+		op->frames_filtered = op->frames_abs = 0;
+
+	DBG("setting j_lastmsg to 0x%08X for rx_op %p\n",
+	    (unsigned int) op->j_lastmsg, op);
+	DBG("sending notification\n");
+
+	head.opcode  = RX_CHANGED;
+	head.flags   = op->flags;
+	head.count   = op->count;
+	head.ival1   = op->ival1;
+	head.ival2   = op->ival2;
+	head.can_id  = op->can_id;
+	head.nframes = 1;
+
+	bcm_send_to_user(op, &head, data, 1);
+}
+
+/*
+ * bcm_rx_update_and_send - process a detected relevant receive content change
+ *                          1. update the last received data
+ *                          2. send a notification to the user (if possible)
+ */
+static void bcm_rx_update_and_send(struct bcm_op *op,
+				   struct can_frame *lastdata,
+				   struct can_frame *rxdata)
+{
+	unsigned long nexttx = op->j_lastmsg + op->j_ival2;
+
+	memcpy(lastdata, rxdata, CFSIZ);
+
+	/* mark as used */
+	lastdata->can_dlc |= RX_RECV;
+
+	/* throttle bcm_rx_changed ? */
+	if ((op->thrtimer.expires) ||
+	    ((op->j_ival2) && (nexttx > jiffies))) {
+		/* we are already waiting OR we have to start waiting */
+
+		/* mark as 'throttled' */
+		lastdata->can_dlc |= RX_THR;
+
+		if (!(op->thrtimer.expires)) {
+			/* start the timer only the first time */
+			op->thrtimer.expires = nexttx;
+			add_timer(&op->thrtimer);
+
+			DBG("adding thrtimer. func=%p data=%p exp=0x%08X\n",
+			    op->thrtimer.function,
+			    (char *) op->thrtimer.data,
+			    (unsigned int) op->thrtimer.expires);
+		}
+
+	} else {
+		/* send RX_CHANGED to the user immediately */
+		bcm_rx_changed(op, rxdata);
+	}
+}
+
+/*
+ * bcm_rx_cmp_to_index - (bit)compares the currently received data to formerly
+ *                       received data stored in op->last_frames[]
+ */
+static void bcm_rx_cmp_to_index(struct bcm_op *op, int index,
+				struct can_frame *rxdata)
+{
+	/*
+	 * no one uses the MSBs of can_dlc for comparation,
+	 * so we use it here to detect the first time of reception
+	 */
+
+	if (!(op->last_frames[index].can_dlc & RX_RECV)) {
+		/* received data for the first time => send update to user */
+		DBG("first time :)\n");
+		bcm_rx_update_and_send(op, &op->last_frames[index], rxdata);
+		return;
+	}
+
+	/* do a real check in can_frame data section */
+
+	DBG("op->frames[index].data = 0x%016llx\n",
+	    GET_U64(&op->frames[index]));
+	DBG("op->last_frames[index].data = 0x%016llx\n",
+	    GET_U64(&op->last_frames[index]));
+	DBG("rxdata->data = 0x%016llx\n", GET_U64(rxdata));
+
+	if ((GET_U64(&op->frames[index]) & GET_U64(rxdata)) !=
+	    (GET_U64(&op->frames[index]) & GET_U64(&op->last_frames[index]))) {
+		DBG("relevant data change :)\n");
+		bcm_rx_update_and_send(op, &op->last_frames[index], rxdata);
+		return;
+	}
+
+	if (op->flags & RX_CHECK_DLC) {
+		/* do a real check in can_frame dlc */
+		if (rxdata->can_dlc != (op->last_frames[index].can_dlc &
+					BCM_CAN_DLC_MASK)) {
+			DBG("dlc change :)\n");
+			bcm_rx_update_and_send(op, &op->last_frames[index],
+					       rxdata);
+			return;
+		}
+	}
+	DBG("no relevant change :(\n");
+}
+
+/*
+ * bcm_rx_starttimer - enable timeout monitoring for CAN frame receiption
+ */
+static void bcm_rx_starttimer(struct bcm_op *op)
+{
+	if (op->flags & RX_NO_AUTOTIMER)
+		return;
+
+	if (op->j_ival1) {
+		op->timer.expires = jiffies + op->j_ival1;
+
+		DBG("adding rx timeout timer ival1. func=%p data=%p "
+		    "exp=0x%08X\n",
+		    op->timer.function,
+		    (char *) op->timer.data,
+		    (unsigned int) op->timer.expires);
+
+		add_timer(&op->timer);
+	}
+}
+
+/*
+ * bcm_rx_timeout_handler - when the (cyclic) CAN frame receiption timed out
+ */
+static void bcm_rx_timeout_handler(unsigned long data)
+{
+	struct bcm_op *op = (struct bcm_op *)data;
+	struct bcm_msg_head msg_head;
+
+	DBG("sending RX_TIMEOUT for can_id %03X. op is %p\n", op->can_id, op);
+
+	msg_head.opcode  = RX_TIMEOUT;
+	msg_head.flags   = op->flags;
+	msg_head.count   = op->count;
+	msg_head.ival1   = op->ival1;
+	msg_head.ival2   = op->ival2;
+	msg_head.can_id  = op->can_id;
+	msg_head.nframes = 0;
+
+	bcm_send_to_user(op, &msg_head, NULL, 0);
+
+	/* no restart of the timer is done here! */
+
+	/* if user wants to be informed, when cyclic CAN-Messages come back */
+	if ((op->flags & RX_ANNOUNCE_RESUME) && op->last_frames) {
+		/* clear received can_frames to indicate 'nothing received' */
+		memset(op->last_frames, 0, op->nframes * CFSIZ);
+		DBG("RX_ANNOUNCE_RESTART\n");
+	}
+}
+
+/*
+ * bcm_rx_thr_handler - the time for blocked content updates is over now:
+ *                      Check for throttled data and send it to the userspace
+ */
+static void bcm_rx_thr_handler(unsigned long data)
+{
+	struct bcm_op *op = (struct bcm_op *)data;
+	int i = 0;
+
+	/* mark disabled / consumed timer */
+	op->thrtimer.expires = 0;
+
+	if (op->nframes > 1) {
+		DBG("sending MUX RX_CHANGED for can_id %03X. op is %p\n",
+		    op->can_id, op);
+		/* for MUX filter we start at index 1 */
+		for (i = 1; i < op->nframes; i++) {
+			if ((op->last_frames) &&
+			    (op->last_frames[i].can_dlc & RX_THR)) {
+				op->last_frames[i].can_dlc &= ~RX_THR;
+				bcm_rx_changed(op, &op->last_frames[i]);
+			}
+		}
+
+	} else {
+		DBG("sending simple RX_CHANGED for can_id %03X. op is %p\n",
+		    op->can_id, op);
+		/* for RX_FILTER_ID and simple filter */
+		if (op->last_frames && (op->last_frames[0].can_dlc & RX_THR)) {
+			op->last_frames[0].can_dlc &= ~RX_THR;
+			bcm_rx_changed(op, &op->last_frames[0]);
+		}
+	}
+}
+
+/*
+ * bcm_rx_handler - handle a CAN frame receiption
+ */
+static void bcm_rx_handler(struct sk_buff *skb, void *data)
+{
+	struct bcm_op *op = (struct bcm_op *)data;
+	struct can_frame rxframe;
+	int i;
+
+	/* disable timeout */
+	del_timer(&op->timer);
+
+	DBG("Called with bcm_op %p\n", op);
+
+	if (skb->len == sizeof(rxframe)) {
+		memcpy(&rxframe, skb->data, sizeof(rxframe));
+		/* save rx timestamp */
+		op->rx_stamp = skb->tstamp;
+		/* save originator for recvfrom() */
+		op->rx_ifindex = skb->dev->ifindex;
+		/* update statistics */
+		op->frames_abs++;
+		kfree_skb(skb);
+		DBG("got can_frame with can_id %03X\n", rxframe.can_id);
+
+	} else {
+		DBG("Wrong skb->len = %d\n", skb->len);
+		kfree_skb(skb);
+		return;
+	}
+
+	DBG_FRAME("BCM: bcm_rx_handler: CAN frame", &rxframe);
+
+	if (op->can_id != rxframe.can_id) {
+		DBG("ERROR! Got wrong can_id %03X! Expected %03X.\n",
+		    rxframe.can_id, op->can_id);
+		return;
+	}
+
+	if (op->flags & RX_RTR_FRAME) {
+		/* send reply for RTR-request */
+		DBG("RTR-request\n");
+
+		/* send op->frames[0] to CAN device */
+		bcm_can_tx(op);
+		return;
+	}
+
+	if (op->flags & RX_FILTER_ID) {
+		/* the easiest case */
+		DBG("Easy does it with RX_FILTER_ID\n");
+
+		bcm_rx_update_and_send(op, &op->last_frames[0], &rxframe);
+		bcm_rx_starttimer(op);
+		return;
+	}
+
+	if (op->nframes == 1) {
+		/* simple compare with index 0 */
+		DBG("Simple compare\n");
+
+		bcm_rx_cmp_to_index(op, 0, &rxframe);
+		bcm_rx_starttimer(op);
+		return;
+	}
+
+	if (op->nframes > 1) {
+		/* multiplex compare */
+		DBG("Multiplex compare\n");
+
+		/*
+		 * find the first multiplex mask that fits.
+		 * Remark: The MUX-mask is stored in index 0
+		 */
+
+		for (i = 1; i < op->nframes; i++) {
+			if ((GET_U64(&op->frames[0]) & GET_U64(&rxframe)) ==
+			    (GET_U64(&op->frames[0]) &
+			     GET_U64(&op->frames[i]))) {
+				DBG("found MUX index %d\n", i);
+				bcm_rx_cmp_to_index(op, i, &rxframe);
+				break;
+			}
+		}
+		bcm_rx_starttimer(op);
+	}
+}
+
+/*
+ * helpers for bcm_op handling: find & delete bcm [rx|tx] op elements
+ */
+static struct bcm_op *bcm_find_op(struct list_head *ops, canid_t can_id,
+				  int ifindex)
+{
+	struct bcm_op *op;
+
+	list_for_each_entry(op, ops, list) {
+		if ((op->can_id == can_id) && (op->ifindex == ifindex))
+			return op;
+	}
+
+	return NULL;
+}
+
+static void bcm_remove_op(struct bcm_op *op)
+{
+	del_timer(&op->timer);
+	del_timer(&op->thrtimer);
+
+	if ((op->frames) && (op->frames != &op->sframe))
+		kfree(op->frames);
+
+	if ((op->last_frames) && (op->last_frames != &op->last_sframe))
+		kfree(op->last_frames);
+
+	kfree(op);
+
+	return;
+}
+
+static void bcm_rx_unreg(struct net_device *dev, struct bcm_op *op)
+{
+	if (op->rx_reg_dev == dev) {
+		can_rx_unregister(dev, op->can_id, REGMASK(op->can_id),
+				  bcm_rx_handler, op);
+
+		/* mark as removed subscription */
+		op->rx_reg_dev = NULL;
+	} else
+		printk(KERN_ERR "can-bcm: bcm_rx_unreg: registered device "
+		       "mismatch %p %p\n", op->rx_reg_dev, dev);
+}
+
+/*
+ * bcm_delete_rx_op - find and remove a rx op (returns number of removed ops)
+ */
+static int bcm_delete_rx_op(struct list_head *ops, canid_t can_id, int ifindex)
+{
+	struct bcm_op *op, *n;
+
+	list_for_each_entry_safe(op, n, ops, list) {
+		if ((op->can_id == can_id) && (op->ifindex == ifindex)) {
+			DBG("removing rx_op %p for can_id %03X\n",
+			    op, op->can_id);
+
+			/*
+			 * Don't care if we're bound or not (due to netdev
+			 * problems) can_rx_unregister() is always a save
+			 * thing to do here.
+			 */
+			if (op->ifindex) {
+				/*
+				 * Only remove subscriptions that had not
+				 * been removed due to NETDEV_UNREGISTER
+				 * in bcm_notifier()
+				 */
+				if (op->rx_reg_dev) {
+					struct net_device *dev;
+
+					dev = dev_get_by_index(op->ifindex);
+					if (dev) {
+						bcm_rx_unreg(dev, op);
+						dev_put(dev);
+					}
+				}
+			} else
+				can_rx_unregister(NULL, op->can_id,
+						  REGMASK(op->can_id),
+						  bcm_rx_handler, op);
+
+			list_del(&op->list);
+			bcm_remove_op(op);
+			return 1; /* done */
+		}
+	}
+
+	return 0; /* not found */
+}
+
+/*
+ * bcm_delete_tx_op - find and remove a tx op (returns number of removed ops)
+ */
+static int bcm_delete_tx_op(struct list_head *ops, canid_t can_id, int ifindex)
+{
+	struct bcm_op *op, *n;
+
+	list_for_each_entry_safe(op, n, ops, list) {
+		if ((op->can_id == can_id) && (op->ifindex == ifindex)) {
+			DBG("removing rx_op %p for can_id %03X\n",
+			    op, op->can_id);
+			list_del(&op->list);
+			bcm_remove_op(op);
+			return 1; /* done */
+		}
+	}
+
+	return 0; /* not found */
+}
+
+/*
+ * bcm_read_op - read out a bcm_op and send it to the user (for bcm_sendmsg)
+ */
+static int bcm_read_op(struct list_head *ops, struct bcm_msg_head *msg_head,
+		       int ifindex)
+{
+	struct bcm_op *op = bcm_find_op(ops, msg_head->can_id, ifindex);
+
+	if (!op) {
+		DBG("TRX_READ: did not find op for can_id %03X\n",
+		    msg_head->can_id);
+		return -EINVAL;
+	}
+
+	DBG("TRX_READ: sending status for can_id %03X\n",
+	    msg_head->can_id);
+	/* put current values into msg_head */
+	msg_head->flags   = op->flags;
+	msg_head->count   = op->count;
+	msg_head->ival1   = op->ival1;
+	msg_head->ival2   = op->ival2;
+	msg_head->nframes = op->nframes;
+
+	bcm_send_to_user(op, msg_head, op->frames, 0);
+
+	return MHSIZ;
+}
+
+/*
+ * bcm_tx_setup - create or update a bcm tx op (for bcm_sendmsg)
+ */
+static int bcm_tx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+			int ifindex, struct sock *sk)
+{
+	struct bcm_sock *bo = bcm_sk(sk);
+	struct bcm_op *op;
+	int i, err;
+
+	/* we need a real device to send frames */
+	if (!ifindex)
+		return -ENODEV;
+
+	/* we need at least one can_frame */
+	if (msg_head->nframes < 1)
+		return -EINVAL;
+
+	/* check the given can_id */
+	op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex);
+
+	if (op) {
+		/* update existing BCM operation */
+
+		DBG("TX_SETUP: modifying existing tx_op %p for can_id %03X\n",
+		    op, msg_head->can_id);
+
+		/*
+		 * Do we need more space for the can_frames than currently
+		 * allocated? -> This is a _really_ unusual use-case and
+		 * therefore (complexity / locking) it is not supported.
+		 */
+		if (msg_head->nframes > op->nframes)
+			return -E2BIG;
+
+		/* update can_frames content */
+		for (i = 0; i < msg_head->nframes; i++) {
+			err = memcpy_fromiovec((u8 *)&op->frames[i],
+					       msg->msg_iov, CFSIZ);
+			if (err < 0)
+				return err;
+
+			if (msg_head->flags & TX_CP_CAN_ID) {
+				/* copy can_id into frame */
+				op->frames[i].can_id = msg_head->can_id;
+			}
+		}
+
+	} else {
+		/* insert new BCM operation for the given can_id */
+
+		op = kzalloc(OPSIZ, GFP_KERNEL);
+		if (!op)
+			return -ENOMEM;
+
+		DBG("TX_SETUP: creating new tx_op %p for can_id %03X\n",
+		    op, msg_head->can_id);
+
+		op->can_id    = msg_head->can_id;
+
+		/* create array for can_frames and copy the data */
+		if (msg_head->nframes > 1) {
+			op->frames = kmalloc(msg_head->nframes * CFSIZ,
+					     GFP_KERNEL);
+			if (!op->frames) {
+				kfree(op);
+				return -ENOMEM;
+			}
+		} else
+			op->frames = &op->sframe;
+
+		for (i = 0; i < msg_head->nframes; i++) {
+			err = memcpy_fromiovec((u8 *)&op->frames[i],
+					       msg->msg_iov, CFSIZ);
+			if (err < 0) {
+				if (op->frames != &op->sframe)
+					kfree(op->frames);
+				kfree(op);
+				return err;
+			}
+
+			if (msg_head->flags & TX_CP_CAN_ID) {
+				/* copy can_id into frame */
+				op->frames[i].can_id = msg_head->can_id;
+			}
+		}
+
+		/* tx_ops never compare with previous received messages */
+		op->last_frames = NULL;
+
+		/* bcm_can_tx / bcm_tx_timeout_handler needs this */
+		op->sk = sk;
+
+		op->ifindex = ifindex;
+
+		/* initialize uninitialized (kmalloc) structure */
+		init_timer(&op->timer);
+
+		/* currently unused in tx_ops */
+		init_timer(&op->thrtimer);
+
+		/* handler for tx_ops */
+		op->timer.function = bcm_tx_timeout_handler;
+
+		/* timer.data points to this op-structure */
+		op->timer.data = (unsigned long)op;
+
+		/* add this bcm_op to the list of the tx_ops */
+		list_add(&op->list, &bo->tx_ops);
+
+	} /* if ((op = bcm_find_op(&bo->tx_ops, msg_head->can_id, ifindex))) */
+
+	if (op->nframes != msg_head->nframes) {
+		op->nframes   = msg_head->nframes;
+		/* start multiple frame transmission with index 0 */
+		op->currframe = 0;
+	}
+
+	/* check flags */
+
+	op->flags = msg_head->flags;
+
+	if (op->flags & TX_RESET_MULTI_IDX) {
+		/* start multiple frame transmission with index 0 */
+		op->currframe = 0;
+	}
+
+	if (op->flags & SETTIMER) {
+		/* set timer values */
+
+		op->count = msg_head->count;
+		op->ival1 = msg_head->ival1;
+		op->ival2 = msg_head->ival2;
+		op->j_ival1 = rounded_tv2jif(&msg_head->ival1);
+		op->j_ival2 = rounded_tv2jif(&msg_head->ival2);
+
+		DBG("TX_SETUP: SETTIMER count=%d j_ival1=%ld j_ival2=%ld\n",
+		    op->count, op->j_ival1, op->j_ival2);
+
+		/* disable an active timer due to zero values? */
+		if (!op->j_ival1 && !op->j_ival2) {
+			del_timer(&op->timer);
+			DBG("TX_SETUP: SETTIMER disabled timer.\n");
+		}
+	}
+
+	if ((op->flags & STARTTIMER) &&
+	    ((op->j_ival1 && op->count) || op->j_ival2)) {
+
+		del_timer(&op->timer);
+
+		/* spec: send can_frame when starting timer */
+		op->flags |= TX_ANNOUNCE;
+
+		if (op->j_ival1 && (op->count > 0)) {
+			op->timer.expires = jiffies + op->j_ival1;
+			/* op->count-- is done in bcm_tx_timeout_handler */
+			DBG("TX_SETUP: adding timer ival1. func=%p data=%p "
+			    "exp=0x%08X\n",
+			    op->timer.function,
+			    (char *) op->timer.data,
+			    (unsigned int) op->timer.expires);
+
+		} else {
+			op->timer.expires = jiffies + op->j_ival2;
+			DBG("TX_SETUP: adding timer ival2. func=%p data=%p "
+			    "exp=0x%08X\n",
+			    op->timer.function,
+			    (char *) op->timer.data,
+			    (unsigned int) op->timer.expires);
+		}
+
+		add_timer(&op->timer);
+	}
+
+	if (op->flags & TX_ANNOUNCE)
+		bcm_can_tx(op);
+
+	return msg_head->nframes * CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_rx_setup - create or update a bcm rx op (for bcm_sendmsg)
+ */
+static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg,
+			int ifindex, struct sock *sk)
+{
+	struct bcm_sock *bo = bcm_sk(sk);
+	struct bcm_op *op;
+	int do_rx_register;
+	int err;
+
+	if ((msg_head->flags & RX_FILTER_ID) || (!(msg_head->nframes))) {
+		/* be robust against wrong usage ... */
+		msg_head->flags |= RX_FILTER_ID;
+		msg_head->nframes = 0; /* ignore trailing garbage */
+	}
+
+	if ((msg_head->flags & RX_RTR_FRAME) &&
+	    ((msg_head->nframes != 1) ||
+	     (!(msg_head->can_id & CAN_RTR_FLAG)))) {
+
+		DBG("RX_SETUP: bad RX_RTR_FRAME setup!\n");
+		return -EINVAL;
+	}
+
+	/* check the given can_id */
+	op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex);
+	if (op) {
+		/* update existing BCM operation */
+
+		DBG("RX_SETUP: modifying existing rx_op %p for can_id %03X\n",
+		    op, msg_head->can_id);
+
+		/*
+		 * Do we need more space for the can_frames than currently
+		 * allocated? -> This is a _really_ unusual use-case and
+		 * therefore (complexity / locking) it is not supported.
+		 */
+		if (msg_head->nframes > op->nframes)
+			return -E2BIG;
+
+		if (msg_head->nframes) {
+			/* update can_frames content */
+			err = memcpy_fromiovec((u8 *)op->frames,
+					       msg->msg_iov,
+					       msg_head->nframes * CFSIZ);
+			if (err < 0)
+				return err;
+
+			/* clear last_frames to indicate 'nothing received' */
+			memset(op->last_frames, 0, msg_head->nframes * CFSIZ);
+		}
+
+		op->nframes = msg_head->nframes;
+
+		/* Only an update -> do not call can_rx_register() */
+		do_rx_register = 0;
+
+	} else {
+		/* insert new BCM operation for the given can_id */
+
+		op = kzalloc(OPSIZ, GFP_KERNEL);
+		if (!op)
+			return -ENOMEM;
+
+		DBG("RX_SETUP: creating new rx_op %p for can_id %03X\n",
+		    op, msg_head->can_id);
+
+		op->can_id    = msg_head->can_id;
+		op->nframes   = msg_head->nframes;
+
+		if (msg_head->nframes > 1) {
+			/* create array for can_frames and copy the data */
+			op->frames = kmalloc(msg_head->nframes * CFSIZ,
+					     GFP_KERNEL);
+			if (!op->frames) {
+				kfree(op);
+				return -ENOMEM;
+			}
+
+			/* create and init array for received can_frames */
+			op->last_frames = kzalloc(msg_head->nframes * CFSIZ,
+						  GFP_KERNEL);
+			if (!op->last_frames) {
+				kfree(op->frames);
+				kfree(op);
+				return -ENOMEM;
+			}
+
+		} else {
+			op->frames = &op->sframe;
+			op->last_frames = &op->last_sframe;
+		}
+
+		if (msg_head->nframes) {
+			err = memcpy_fromiovec((u8 *)op->frames, msg->msg_iov,
+					       msg_head->nframes * CFSIZ);
+			if (err < 0) {
+				if (op->frames != &op->sframe)
+					kfree(op->frames);
+				if (op->last_frames != &op->last_sframe)
+					kfree(op->last_frames);
+				kfree(op);
+				return err;
+			}
+		}
+
+		op->sk = sk;
+		op->ifindex = ifindex;
+
+		/* initialize uninitialized (kzalloc) structure */
+		init_timer(&op->timer);
+
+		/* init throttle timer for RX_CHANGED */
+		init_timer(&op->thrtimer);
+
+		/* handler for rx timeouts */
+		op->timer.function = bcm_rx_timeout_handler;
+
+		/* timer.data points to this op-structure */
+		op->timer.data = (unsigned long)op;
+
+		/* handler for RX_CHANGED throttle timeouts */
+		op->thrtimer.function = bcm_rx_thr_handler;
+
+		/* timer.data points to this op-structure */
+		op->thrtimer.data = (unsigned long)op;
+
+		/* mark disabled timer */
+		op->thrtimer.expires = 0;
+
+		/* add this bcm_op to the list of the tx_ops */
+		list_add(&op->list, &bo->rx_ops);
+
+		/* call can_rx_register() */
+		do_rx_register = 1;
+
+	} /* if ((op = bcm_find_op(&bo->rx_ops, msg_head->can_id, ifindex))) */
+
+	/* check flags */
+	op->flags = msg_head->flags;
+
+	if (op->flags & RX_RTR_FRAME) {
+
+		/* no timers in RTR-mode */
+		del_timer(&op->thrtimer);
+		del_timer(&op->timer);
+
+		/*
+		 * funny feature in RX(!)_SETUP only for RTR-mode:
+		 * copy can_id into frame BUT without RTR-flag to
+		 * prevent a full-load-loopback-test ... ;-]
+		 */
+		if ((op->flags & TX_CP_CAN_ID) ||
+		    (op->frames[0].can_id == op->can_id))
+			op->frames[0].can_id = op->can_id & ~CAN_RTR_FLAG;
+
+	} else {
+		if (op->flags & SETTIMER) {
+
+			/* set timer value */
+			op->ival1 = msg_head->ival1;
+			op->ival2 = msg_head->ival2;
+			op->j_ival1 = rounded_tv2jif(&msg_head->ival1);
+			op->j_ival2 = rounded_tv2jif(&msg_head->ival2);
+
+			DBG("RX_SETUP: SETTIMER j_ival1=%ld j_ival2=%ld\n",
+			    op->j_ival1, op->j_ival2);
+
+			/* disable an active timer due to zero value? */
+			if (!op->j_ival1) {
+				del_timer(&op->timer);
+				DBG("RX_SETUP: disabled timer rx timeouts.\n");
+			}
+
+			/* free currently blocked msgs ? */
+			if (op->thrtimer.expires) {
+				DBG("RX_SETUP: unblocking throttled msgs.\n");
+				del_timer(&op->thrtimer);
+				/* send blocked msgs hereafter */
+				op->thrtimer.expires = jiffies + 2;
+				add_timer(&op->thrtimer);
+			}
+			/*
+			 * if (op->j_ival2) is zero, no (new) throttling
+			 * will happen. For details see functions
+			 * bcm_rx_update_and_send() and bcm_rx_thr_handler()
+			 */
+		}
+
+		if ((op->flags & STARTTIMER) && op->j_ival1) {
+
+			del_timer(&op->timer);
+			op->timer.expires = jiffies + op->j_ival1;
+
+			DBG("RX_SETUP: adding timer ival1. func=%p data=%p"
+			    " exp=0x%08X\n",
+			    (char *) op->timer.function,
+			    (char *) op->timer.data,
+			    (unsigned int) op->timer.expires);
+
+			add_timer(&op->timer);
+		}
+	}
+
+	/* now we can register for can_ids, if we added a new bcm_op */
+	if (do_rx_register) {
+		DBG("RX_SETUP: can_rx_register() for can_id %03X. "
+		    "rx_op is %p\n", op->can_id, op);
+
+		if (ifindex) {
+			struct net_device *dev = dev_get_by_index(ifindex);
+
+			if (dev) {
+				can_rx_register(dev, op->can_id,
+						REGMASK(op->can_id),
+						bcm_rx_handler, op, IDENT);
+				op->rx_reg_dev = dev;
+				dev_put(dev);
+			}
+
+		} else
+			can_rx_register(NULL, op->can_id, REGMASK(op->can_id),
+					bcm_rx_handler, op, IDENT);
+	}
+
+	return msg_head->nframes * CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_tx_send - send a single CAN frame to the CAN interface (for bcm_sendmsg)
+ */
+static int bcm_tx_send(struct msghdr *msg, int ifindex, struct sock *sk)
+{
+	struct sk_buff *skb;
+	struct net_device *dev;
+	int err;
+
+	/* just copy and send one can_frame */
+
+	if (!ifindex) /* we need a real device to send frames */
+		return -ENODEV;
+
+	skb = alloc_skb(CFSIZ, GFP_KERNEL);
+
+	if (!skb)
+		return -ENOMEM;
+
+	err = memcpy_fromiovec(skb_put(skb, CFSIZ), msg->msg_iov, CFSIZ);
+	if (err < 0) {
+		kfree_skb(skb);
+		return err;
+	}
+
+	DBG_FRAME("BCM: TX_SEND: sending frame",
+		  (struct can_frame *)skb->data);
+
+	dev = dev_get_by_index(ifindex);
+	if (!dev) {
+		kfree_skb(skb);
+		return -ENODEV;
+	}
+
+	skb->dev = dev;
+	skb->sk  = sk;
+	can_send(skb, 1); /* send with loopback */
+	dev_put(dev);
+
+	return CFSIZ + MHSIZ;
+}
+
+/*
+ * bcm_sendmsg - process BCM commands (opcodes) from the userspace
+ */
+static int bcm_sendmsg(struct kiocb *iocb, struct socket *sock,
+		       struct msghdr *msg, size_t size)
+{
+	struct sock *sk = sock->sk;
+	struct bcm_sock *bo = bcm_sk(sk);
+	int ifindex = bo->ifindex; /* default ifindex for this bcm_op */
+	struct bcm_msg_head msg_head;
+	int ret; /* read bytes or error codes as return value */
+
+	if (!bo->bound) {
+		DBG("sock %p not bound\n", sk);
+		return -ENOTCONN;
+	}
+
+	/* check for alternative ifindex for this bcm_op */
+
+	if (!ifindex && msg->msg_name) {
+		/* no bound device as default => check msg_name */
+		struct sockaddr_can *addr =
+			(struct sockaddr_can *)msg->msg_name;
+
+		if (addr->can_family != AF_CAN)
+			return -EINVAL;
+
+		ifindex = addr->can_ifindex; /* ifindex from sendto() */
+
+		if (ifindex) {
+			struct net_device *dev = dev_get_by_index(ifindex);
+
+			if (!dev) {
+				DBG("device %d not found\n", ifindex);
+				return -ENODEV;
+			}
+
+			if (dev->type != ARPHRD_CAN) {
+				DBG("device %d no CAN device\n", ifindex);
+				dev_put(dev);
+				return -ENODEV;
+			}
+
+			dev_put(dev);
+		}
+	}
+
+	/* read message head information */
+
+	ret = memcpy_fromiovec((u8 *)&msg_head, msg->msg_iov, MHSIZ);
+	if (ret < 0)
+		return ret;
+
+	DBG("opcode %d for can_id %03X\n", msg_head.opcode, msg_head.can_id);
+
+	lock_sock(sk);
+
+	switch (msg_head.opcode) {
+
+	case TX_SETUP:
+		ret = bcm_tx_setup(&msg_head, msg, ifindex, sk);
+		break;
+
+	case RX_SETUP:
+		ret = bcm_rx_setup(&msg_head, msg, ifindex, sk);
+		break;
+
+	case TX_DELETE:
+		if (bcm_delete_tx_op(&bo->tx_ops, msg_head.can_id, ifindex))
+			ret = MHSIZ;
+		else
+			ret = -EINVAL;
+		break;
+
+	case RX_DELETE:
+		if (bcm_delete_rx_op(&bo->rx_ops, msg_head.can_id, ifindex))
+			ret = MHSIZ;
+		else
+			ret = -EINVAL;
+		break;
+
+	case TX_READ:
+		/* reuse msg_head for the reply to TX_READ */
+		msg_head.opcode  = TX_STATUS;
+		ret = bcm_read_op(&bo->tx_ops, &msg_head, ifindex);
+		break;
+
+	case RX_READ:
+		/* reuse msg_head for the reply to RX_READ */
+		msg_head.opcode  = RX_STATUS;
+		ret = bcm_read_op(&bo->rx_ops, &msg_head, ifindex);
+		break;
+
+	case TX_SEND:
+		/* we need at least one can_frame */
+		if (msg_head.nframes < 1)
+			ret = -EINVAL;
+		else
+			ret = bcm_tx_send(msg, ifindex, sk);
+		break;
+
+	default:
+		DBG("Unknown opcode %d\n", msg_head.opcode);
+		ret = -EINVAL;
+		break;
+	}
+
+	release_sock(sk);
+
+	return ret;
+}
+
+/*
+ * notification handler for netdevice status changes
+ */
+static int bcm_notifier(struct notifier_block *nb, unsigned long msg,
+			void *data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct bcm_sock *bo = container_of(nb, struct bcm_sock, notifier);
+	struct sock *sk = &bo->sk;
+	struct bcm_op *op;
+	int notify_enodev = 0;
+
+	DBG("msg %ld for dev %p (%s idx %d) sk %p bo->ifindex %d\n",
+	    msg, dev, dev->name, dev->ifindex, sk, bo->ifindex);
+
+	if (dev->type != ARPHRD_CAN)
+		return NOTIFY_DONE;
+
+	switch (msg) {
+
+	case NETDEV_UNREGISTER:
+		lock_sock(sk);
+
+		/* remove device specific receive entries */
+		list_for_each_entry(op, &bo->rx_ops, list)
+			if (op->rx_reg_dev == dev)
+				bcm_rx_unreg(dev, op);
+
+		/* remove device reference, if this is our bound device */
+		if (bo->bound && bo->ifindex == dev->ifindex) {
+			bo->bound   = 0;
+			bo->ifindex = 0;
+			notify_enodev = 1;
+		}
+
+		release_sock(sk);
+
+		if (notify_enodev) {
+			sk->sk_err = ENODEV;
+			if (!sock_flag(sk, SOCK_DEAD))
+				sk->sk_error_report(sk);
+		}
+		break;
+
+	case NETDEV_DOWN:
+		if (bo->bound && bo->ifindex == dev->ifindex) {
+			sk->sk_err = ENETDOWN;
+			if (!sock_flag(sk, SOCK_DEAD))
+				sk->sk_error_report(sk);
+		}
+	}
+
+	return NOTIFY_DONE;
+}
+
+/*
+ * initial settings for all BCM sockets to be set at socket creation time
+ */
+static int bcm_init(struct sock *sk)
+{
+	struct bcm_sock *bo = bcm_sk(sk);
+
+	bo->bound            = 0;
+	bo->ifindex          = 0;
+	bo->dropped_usr_msgs = 0;
+	bo->bcm_proc_read    = NULL;
+
+	INIT_LIST_HEAD(&bo->tx_ops);
+	INIT_LIST_HEAD(&bo->rx_ops);
+
+	/* set notifier */
+	bo->notifier.notifier_call = bcm_notifier;
+
+	register_netdevice_notifier(&bo->notifier);
+
+	return 0;
+}
+
+/*
+ * standard socket functions
+ */
+static int bcm_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	struct bcm_sock *bo = bcm_sk(sk);
+	struct bcm_op *op, *next;
+
+	DBG("socket %p, sk %p\n", sock, sk);
+
+	/* remove bcm_ops, timer, rx_unregister(), etc. */
+
+	unregister_netdevice_notifier(&bo->notifier);
+
+	lock_sock(sk);
+
+	list_for_each_entry_safe(op, next, &bo->tx_ops, list) {
+		DBG("removing tx_op %p for can_id %03X\n", op, op->can_id);
+		bcm_remove_op(op);
+	}
+
+	list_for_each_entry_safe(op, next, &bo->rx_ops, list) {
+		DBG("removing rx_op %p for can_id %03X\n", op, op->can_id);
+
+		/*
+		 * Don't care if we're bound or not (due to netdev problems)
+		 * can_rx_unregister() is always a save thing to do here.
+		 */
+		if (op->ifindex) {
+			/*
+			 * Only remove subscriptions that had not
+			 * been removed due to NETDEV_UNREGISTER
+			 * in bcm_notifier()
+			 */
+			if (op->rx_reg_dev) {
+				struct net_device *dev;
+
+				dev = dev_get_by_index(op->ifindex);
+				if (dev) {
+					bcm_rx_unreg(dev, op);
+					dev_put(dev);
+				}
+			}
+		} else
+			can_rx_unregister(NULL, op->can_id,
+					  REGMASK(op->can_id),
+					  bcm_rx_handler, op);
+
+		bcm_remove_op(op);
+	}
+
+	/* remove procfs entry */
+	if (proc_dir && bo->bcm_proc_read)
+		remove_proc_entry(bo->procname, proc_dir);
+
+	/* remove device reference */
+	if (bo->bound) {
+		bo->bound   = 0;
+		bo->ifindex = 0;
+	}
+
+	release_sock(sk);
+	sock_put(sk);
+
+	return 0;
+}
+
+static int bcm_connect(struct socket *sock, struct sockaddr *uaddr, int len,
+		       int flags)
+{
+	struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+	struct sock *sk = sock->sk;
+	struct bcm_sock *bo = bcm_sk(sk);
+
+	if (bo->bound)
+		return -EISCONN;
+
+	/* bind a device to this socket */
+	if (addr->can_ifindex) {
+		struct net_device *dev = dev_get_by_index(addr->can_ifindex);
+
+		if (!dev) {
+			DBG("could not find device index %d\n",
+			    addr->can_ifindex);
+			return -ENODEV;
+		}
+
+		if (dev->type != ARPHRD_CAN) {
+			DBG("device %d no CAN device\n", addr->can_ifindex);
+			dev_put(dev);
+			return -ENODEV;
+		}
+
+		bo->ifindex = dev->ifindex;
+		dev_put(dev);
+
+		DBG("socket %p bound to device %s (idx %d)\n",
+		    sock, dev->name, dev->ifindex);
+
+	} else {
+		/* no interface reference for ifindex = 0 ('any' CAN device) */
+		bo->ifindex = 0;
+	}
+
+	bo->bound = 1;
+
+	if (proc_dir) {
+		/* unique socket address as filename */
+		sprintf(bo->procname, "%p", sock);
+		bo->bcm_proc_read = create_proc_read_entry(bo->procname, 0644,
+							   proc_dir,
+							   bcm_read_proc, sk);
+	}
+
+	return 0;
+}
+
+static int bcm_recvmsg(struct kiocb *iocb, struct socket *sock,
+		       struct msghdr *msg, size_t size, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+	int error = 0;
+	int noblock;
+	int err;
+
+	DBG("socket %p, sk %p\n", sock, sk);
+
+	noblock =  flags & MSG_DONTWAIT;
+	flags   &= ~MSG_DONTWAIT;
+	skb = skb_recv_datagram(sk, flags, noblock, &error);
+	if (!skb)
+		return error;
+
+	DBG("delivering skbuff %p\n", skb);
+	DBG_SKB(skb);
+
+	if (skb->len < size)
+		size = skb->len;
+
+	err = memcpy_toiovec(msg->msg_iov, skb->data, size);
+	if (err < 0) {
+		skb_free_datagram(sk, skb);
+		return err;
+	}
+
+	sock_recv_timestamp(msg, sk, skb);
+
+	if (msg->msg_name) {
+		msg->msg_namelen = sizeof(struct sockaddr_can);
+		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+	}
+
+	DBG("freeing sock %p, skbuff %p\n", sk, skb);
+	skb_free_datagram(sk, skb);
+
+	return size;
+}
+
+static unsigned int bcm_poll(struct file *file, struct socket *sock,
+			     poll_table *wait)
+{
+	unsigned int mask = 0;
+
+	DBG("socket %p\n", sock);
+
+	mask = datagram_poll(file, sock, wait);
+	return mask;
+}
+
+static struct proto_ops bcm_ops = {
+	.family        = PF_CAN,
+	.release       = bcm_release,
+	.bind          = sock_no_bind,
+	.connect       = bcm_connect,
+	.socketpair    = sock_no_socketpair,
+	.accept        = sock_no_accept,
+	.getname       = sock_no_getname,
+	.poll          = bcm_poll,
+	.ioctl         = NULL,		/* use can_ioctl() from af_can.c */
+	.listen        = sock_no_listen,
+	.shutdown      = sock_no_shutdown,
+	.setsockopt    = sock_no_setsockopt,
+	.getsockopt    = sock_no_getsockopt,
+	.sendmsg       = bcm_sendmsg,
+	.recvmsg       = bcm_recvmsg,
+	.mmap          = sock_no_mmap,
+	.sendpage      = sock_no_sendpage,
+};
+
+static struct proto bcm_proto = {
+	.name       = "CAN_BCM",
+	.owner      = THIS_MODULE,
+	.obj_size   = sizeof(struct bcm_sock),
+	.init       = bcm_init,
+};
+
+static struct can_proto bcm_can_proto = {
+	.type       = SOCK_DGRAM,
+	.protocol   = CAN_BCM,
+	.capability = BCM_CAP,
+	.ops        = &bcm_ops,
+	.prot       = &bcm_proto,
+};
+
+static int __init bcm_module_init(void)
+{
+	printk(banner);
+
+	can_proto_register(&bcm_can_proto);
+
+	/* create /proc/net/can/bcm directory */
+	proc_dir = proc_mkdir(CAN_PROC_DIR"/"IDENT, NULL);
+
+	if (proc_dir)
+		proc_dir->owner = THIS_MODULE;
+
+	return 0;
+}
+
+static void __exit bcm_module_exit(void)
+{
+	can_proto_unregister(&bcm_can_proto);
+
+	if (proc_dir)
+		remove_proc_entry(CAN_PROC_DIR"/"IDENT, NULL);
+}
+
+module_init(bcm_module_init);
+module_exit(bcm_module_exit);

--

^ permalink raw reply

* [patch 7/7] CAN: Add documentation
From: Urs Thuermann @ 2007-08-04  2:07 UTC (permalink / raw)
  To: netdev; +Cc: urs, oliver, davem, kaber, urs.thuermann, oliver.hartkopp
In-Reply-To: <20070804020657.25572.0@janus.isnogud.escape.de>

[-- Attachment #1: 07-can-doc.diff --]
[-- Type: text/plain, Size: 29374 bytes --]

This patch adds documentation for the PF_CAN protocol family.

Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>

---
 Documentation/networking/00-INDEX |    2 
 Documentation/networking/can.txt  |  635 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 637 insertions(+)

Index: net-2.6/Documentation/networking/can.txt
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/Documentation/networking/can.txt	2007-08-03 11:21:58.000000000 +0200
@@ -0,0 +1,635 @@
+============================================================================
+
+can.txt
+
+Readme file for the Controller Area Network Protocol Family (aka Socket CAN)
+
+This file contains
+
+  1 Overview / What is Socket CAN
+
+  2 Motivation / Why using the socket API
+
+  3 Socket CAN concept
+    3.1 receive lists
+    3.2 loopback
+    3.3 network security issues (capabilities)
+    3.4 network problem notifications
+
+  4 How to use Socket CAN
+    4.1 RAW protocol sockets with can_filters (SOCK_RAW)
+      4.1.1 RAW socket option CAN_RAW_FILTER
+      4.1.2 RAW socket option CAN_RAW_ERR_FILTER
+      4.1.3 RAW socket option CAN_RAW_LOOPBACK
+      4.1.4 RAW socket option CAN_RAW_RECV_OWN_MSGS
+    4.2 Broadcast Manager protocol sockets (SOCK_DGRAM)
+    4.3 connected transport protocols (SOCK_SEQPACKET)
+    4.4 unconnected transport protocols (SOCK_DGRAM)
+
+  5 Socket CAN core module
+    5.1 can.ko module params
+    5.2 procfs content
+    5.3 writing own CAN protocol modules
+
+  6 CAN network drivers
+    6.1 general settings
+    6.2 loopback
+    6.3 CAN controller hardware filters
+    6.4 currently supported CAN hardware
+    6.5 todo
+
+  7 Credits
+
+============================================================================
+
+1. Overview / What is Socket CAN
+--------------------------------
+
+The socketcan package is an implementation of CAN protocols
+(Controller Area Network) for Linux.  CAN is a networking technology
+which has wide-spread use in automation, embedded devices, and
+automotive fields.  While there have been other CAN implementations
+for Linux based on character devices, Socket CAN uses the Berkeley
+socket API, the Linux network stack and implements the CAN device
+drivers as network interfaces.  The CAN socket API has been designed
+as similar as possible to the TCP/IP protocols to allow programmers,
+familiar with network programming, to easily learn how to use CAN
+sockets.
+
+2. Motivation / Why using the socket API
+----------------------------------------
+
+There have been CAN implementations for Linux before Socket CAN so the
+question arises, why we have started another project.  Most existing
+implementations come as a device driver for some CAN hardware, they
+are based on character devices and provide comparatively little
+functionality.  Usually, there is only a hardware-specific device
+driver which provides a character device interface to send and
+receive raw CAN frames, directly to/from the controller hardware.
+Queueing of frames and higher-level transport protocols like ISO-TP
+have to be implemented in user space applications.  Also, most
+character-device implementations support only one single process to
+open the device at a time, similar to a serial interface.  Exchanging
+the CAN controller requires employment of another device driver and
+often the need for adaption of large parts of the application to the
+new driver's API.
+
+Socket CAN was designed to overcome all of these limitations.  A new
+protocol family has been implemented which provides a socket interface
+to user space applications and which builds upon the Linux network
+layer, so to use all of the provided queueing functionality.  Device
+drivers for CAN controller hardware register itself with the Linux
+network layer as a network device, so that CAN frames from the
+controller can be passed up to the network layer and on to the CAN
+protocol family module and also vice-versa.  Also, the protocol family
+module provides an API for transport protocol modules to register, so
+that any number of transport protocols can be loaded or unloaded
+dynamically.  In fact, the can core module alone does not provide any
+protocol and can not be used without loading at least one additional
+protocol module.  Multiple sockets can be opened at the same time,
+on different or the same protocol module and they can listen/send
+frames on different or the same CAN IDs.  Several sockets listening on
+the same interface for frames with the same CAN ID are all passed the
+same received matching CAN frames.  An application wishing to
+communicate using a specific transport protocol, e.g. ISO-TP, just
+selects that protocol when opening the socket, and then can read and
+write application data byte streams, without having to deal with
+CAN-IDs, frames, etc.
+
+Similar functionality visible from user-space could be provided by a
+character decive, too, but this would lead to a technically inelegant
+solution for a couple of reasons:
+
+* Intricate usage.  Instead of passing a protocol argument to
+  socket(2) and using bind(2) to select a CAN interface and CAN ID, an
+  application would have to do all these operations using ioctl(2)s.
+
+* Code duplication.  A character device cannot make use of the Linux
+  network queueing code, so all that code would have to be duplicated
+  for CAN networking.
+
+* Abstraction.  In most existing character-device implementations, the
+  hardware-specific device driver for a CAN controller directly
+  provides the character device for the application to work with.
+  This is at least very unusual in Unix systems, for both, char and
+  block devices.  For example you don't have a character device for a
+  certain UART of a serial interface, a certain sound chip in your
+  computer, a SCSI or IDE controller providing access to your hard
+  disk or tape streamer device.  Instead, you have abstraction layers
+  which provide a unified character or block device interface to the
+  application on the one hand, and a interface for hardware-specific
+  device drivers on the other hand.  These abstractions are provided
+  by subsystems like the tty layer, the audio subsystem or the SCSI
+  and IDE subsystems for the devices mentioned above.
+
+  The easiest way to implement a CAN device driver is as a character
+  without such a (complete) abstraction layer, as is done by most
+  existing drivers.  The right way, however, would be to add such a
+  layer with all the functionality like registering for certain CAN
+  IDs, supporting several open file descriptors and (de)multplexing
+  CAN frames between them, (sophisticated) queueing of CAN frames, and
+  providing an API for device driver to register with.  However, then
+  it would be no more difficult, or may be even easier, to use the
+  networking framework provided by the Linux kernel, and this is what
+  Socket CAN does.
+
+  The use of the networking framework of the Linux kernel is just the
+  natural and most appropriate way to implement CAN for Linux.
+
+3. Socket CAN concept
+---------------------
+
+  As described in chapter 2 it is the main goal of Socket CAN to
+  provide a socket interface to user space applications which builds
+  upon the Linux networklayer. In opposite to the commonly known
+  TCP/IP and ethernet networking the CAN bus is a broadcast-only(!)
+  medium that has no MAC-layer adressing like ethernet. The CAN-identifier
+  (can_id) is used for arbitration on the CAN-bus. Therefore the CAN-IDs
+  have to be choosen unique on the bus. When designing a CAN-ECU
+  network the CAN-IDs are mapped to be sent by a specific ECU.
+  For this reason a CAN-ID can be treatened best as a kind of source address.
+
+  3.1 receive lists
+
+  The network transparent access of multiple applications leads to the
+  problem that different applications may be interrested in the same
+  CAN-IDs from the same CAN network interface. The Socket CAN core
+  module - which implements the protocol family CAN - provides several
+  high efficient receive lists for this reason. If e.g. a user space
+  application opens a CAN RAW socket, the raw protocol module itself
+  requests the (range of) CAN-IDs from the Socket CAN core that are
+  requested by the user. The subscription and unsubscription of
+  CAN-IDs can be done for specific CAN interfaces or for all(!) known
+  CAN interfaces with the can_rx_(un)register() functions provided to
+  CAN protocol modules by the SocketCAN core (see chapter 5).
+  To optimize the CPU usage at runtime the receive lists are split up
+  into several specific lists per device that match the requested
+  filter complexity for a given use-case.
+
+  3.2 loopback
+
+  As known from other networking concepts the data exchanging
+  applications may run on the same or different nodes without any
+  change (except if the according addressing information):
+
+         ___   ___   ___                   _______   ___
+        | _ | | _ | | _ |                 | _   _ | | _ |
+        ||A|| ||B|| ||C||                 ||A| |B|| ||C||
+        |___| |___| |___|                 |_______| |___|
+          |     |     |                       |       |
+        -----------------(1)- CAN bus -(2)---------------
+
+  To ensure that application A receives the same information in the
+  expample (2) as it would receive in example (1) there is need for
+  some kind of local loopback on the appropriate node.
+
+  The Linux network devices (by default) just can handle the
+  transmission and receiption of media dependend frames. Due to the
+  arbritration on the CAN bus the transmission of a low prio CAN-ID
+  may be delayed from the receipition of a high prio CAN frame. To
+  reflect the correct* traffic on the node the loopback of the sent
+  data has to be performed right after a successful transmission. If
+  the CAN network interface is not capable to perform the loopback for
+  some reason the SocketCAN core can do this task as a fallback solution.
+  See chapter 6.2 for details (recommended).
+
+  The loopback functionality is enabled by default to reflect standard
+  networking behaviour for CAN applications. Due to some requests from
+  the RT-SocketCAN group the loopback optionally may be disabled for each
+  seperate socket. See sockopts from the CAN RAW sockets in chapter 4.1 .
+
+  * = you really like to have this when you're running analyser tools
+      like 'candump' or 'cansniffer' on the (same) node.
+
+  3.3 network security issues (capabilities)
+
+  The Controller Area Network is a local field bus transmitting only
+  broadcast messages without any routing and security concepts.
+  In the majority of cases the user application has to deal with
+  raw CAN frames. Therefore it might be reasonable NOT to restrict
+  the CAN access only to the user root, as known from other networks.
+  Since the currently implemented CAN_RAW and CAN_BCM sockets can only
+  send and receive frames to/from CAN interfaces it does not affect
+  security of others networks to allow all users to access the CAN.
+  To enable non-root users to access CAN_RAW and CAN_BCM protocol
+  sockets the Kconfig options CAN_RAW_USER and/or CAN_BCM_USER may be
+  selected at kernel compile time.
+
+  3.4 network problem notifications
+
+  The use of the CAN bus may lead to several problems on the physical
+  and media access control layer. Detecting and logging of these lower
+  layer problems is a vital requirement for CAN users to identify
+  hardware issues on the physical transceiver layer as well as
+  arbitration problems and error frames caused by the different
+  ECUs. The occurance of detected errors are important for diagnosis
+  and have to be logged together with the exact timestamp. For this
+  reason the CAN interface driver can generate so called Error Frames
+  that can optionally be passed to the user application on the same
+  way like other CAN frames. Whenever an error on the physical layer
+  or the MAC layer is detected (e.g. by the CAN controller) the driver
+  creates an appropriate error frame. Error frames can be requested by
+  the user application using the common CAN filter mechanisms. Inside
+  this filter definition the (interrested) type of errors may be
+  selected. The receiption of error frames is disabled by default.
+
+4. How to use Socket CAN
+------------------------
+
+  Like TCP/IP, you first need to open a socket for communicating over a
+  CAN network. Since Socket CAN implements a new protocol family, you
+  need to pass PF_CAN as the first argument to the socket(2) system
+  call. Currently, there are two CAN protocols to choose from, the raw
+  socket protocol and the broadcast manager (BCM). So to open a socket,
+  you would write
+
+    s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
+
+  and
+
+    s = socket(PF_CAN, SOCK_DGRAM, CAN_BCM);
+
+  respectively.  After the successful creation of the socket, you would
+  normally use the bind(2) system call to bind the socket to a CAN
+  interface (which is different to TCP/IP due to different addressing
+  - see chapter 3). After binding (CAN_RAW) or connecting (CAN_BCM)
+  the socket, you can read(2) and write(2) from/to the socket or use
+  send(2), sendto(2), sendmsg(2) and the recv* counterpart operations
+  on the socket as usual. There are also CAN specific socket options
+  described below.
+
+  The basic CAN frame structure and the sockaddr structure are defined
+  in include/linux/can.h:
+
+    struct can_frame {
+            canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+            __u8    can_dlc; /* data length code: 0 .. 8 */
+            __u8    data[8] __attribute__((aligned(8)));
+    };
+
+  The alignment of the (linear) payload data[] to a 64bit boundary
+  allows the user to define own structs and unions to easily access the
+  CAN payload. There is no given byteorder on the CAN bus by
+  default. A read(2) system call on a CAN_RAW socket transfers a
+  struct can_frame to the user space.
+
+  The sockaddr_can structure has an interface index analogue to the
+  PF_PACKET socket, that also binds to a specific interface:
+
+    struct sockaddr_can {
+            sa_family_t can_family;
+            int         can_ifindex;
+            union {
+                    struct { canid_t rx_id, tx_id; } tp16;
+                    struct { canid_t rx_id, tx_id; } tp20;
+                    struct { canid_t rx_id, tx_id; } mcnet;
+                    struct { canid_t rx_id, tx_id; } isotp;
+                    struct { int     lcu,   type;  } bap;
+            } can_addr;
+    };
+
+  To determine the interface index the an appropriate ioctl() has to
+  be used (example for CAN_RAW sockets without error checking):
+
+    int s;
+    struct sockaddr_can addr;
+    struct ifreq ifr;
+
+    s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
+
+    strcpy(ifr.ifr_name, "can0" );
+    ioctl(s, SIOCGIFINDEX, &ifr);
+
+    addr.can_family = AF_CAN;
+    addr.can_ifindex = ifr.ifr_ifindex;
+
+    bind(s, (struct sockaddr *)&addr, sizeof(addr));
+
+    (..)
+
+  To bind a socket to all(!) CAN interfaces the interface index might
+  be 0 (zero). In this case the socket receives CAN frames from every
+  enabled CAN interface. To determine the originating CAN interface
+  the system call recvfrom(2) may be used instead of read(2). To send
+  on a socket that is bound to 'any' interface sendto(2) is needed to
+  specify the outgoing interface.
+
+  Reading CAN frames from a bound CAN_RAW socket (see above) consists
+  of reading a struct can_frame:
+
+    struct can_frame frame;
+
+    nbytes = read(s, &frame, sizeof(struct can_frame));
+
+    if (nbytes < 0) {
+            perror("can raw socket read");
+            return 1;
+    }
+
+    /* paraniod check ... */
+    if (nbytes < sizeof(struct can_frame)) {
+            fprintf(stderr, "read: incomplete CAN frame\n");
+            return 1;
+    }
+
+    /* do something with the received CAN frame */
+
+  Writing CAN frames can be done analogue with the write(2) system call:
+
+    nbytes = write(s, &frame, sizeof(struct can_frame));
+
+  When the CAN interface is bound to 'any' existing CAN interface
+  (addr.can_ifindex = 0) it is recommended to use recvfrom(2) if the
+  information about the originating CAN interface is needed:
+
+    struct sockaddr_can addr;
+    struct ifreq ifr;
+    socklen_t len = sizeof(addr);
+    struct can_frame frame;
+
+    nbytes = recvfrom(s, &frame, sizeof(struct can_frame),
+                      0, (struct sockaddr*)&addr, &len);
+
+    /* get interface name of the received CAN frame */
+    ifr.ifr_ifindex = addr.can_ifindex;
+    ioctl(s, SIOCGIFNAME, &ifr);
+    printf("Received a CAN frame from interface %s", ifr.ifr_name);
+
+  To write CAN frames on sockets bound to 'any' CAN interface the
+  outgoing interface has to be defined certainly.
+
+    strcpy(ifr.ifr_name, "can0");
+    ioctl(s, SIOCGIFINDEX, &ifr);
+    addr.can_ifindex = ifr.ifr_ifindex;
+    addr.can_family  = AF_CAN;
+
+    nbytes = sendto(s, &frame, sizeof(struct can_frame),
+                    0, (struct sockaddr*)&addr, sizeof(addr));
+
+  4.1 RAW protocol sockets with can_filters (SOCK_RAW)
+
+  Using CAN_RAW sockets is extensively comparable to the commonly
+  known access to CAN character devices. To meet the new possibilities
+  provided by the multi user SocketCAN approach, some reasonable
+  defaults are set at RAW socket bindung time:
+
+  - The filters are set to exactly one filter receiving everything
+  - The socket only receives valid data frames (=> no error frames)
+  - The loopback of sent CAN frames is enabled (see chapter 3.2)
+  - The socket does not receive it's own sent frames (in loopback mode)
+
+  These default settings may be changed before or after binding the socket.
+  To use the referenced definitions of the socket options for CAN_RAW
+  sockets include linux/can/raw.h .
+
+  4.1.1 RAW socket option CAN_RAW_FILTER
+
+  The receiption of CAN frames using CAN_RAW sockets can be controlled
+  by defining 0 .. n filters with the CAN_RAW_FILTER socket option.
+
+  The CAN filter structure is defined in include/linux/can.h:
+
+    struct can_filter {
+            canid_t can_id;
+            canid_t can_mask;
+    };
+
+  A filter matches, when
+
+    <received_can_id> & mask == can_id & mask
+
+  which is analogue to known CAN controllers hardware filter semantics.
+  The filter can be inverted in this semantic, when the CAN_INV_FILTER
+  bit is set in can_id element of the can_filter structure. In
+  opposite to CAN controller hardware filters the user may set 0 .. n
+  receive filters for each open socket separately:
+
+    struct can_filter rfilter[2];
+
+    rfilter[0].can_id   = 0x123;
+    rfilter[0].can_mask = CAN_SFF_MASK;
+    rfilter[1].can_id   = 0x200;
+    rfilter[1].can_mask = 0x700;
+
+    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, sizeof(rfilter));
+
+  To disable the receiption of CAN frames on the selected CAN_RAW socket:
+
+    setsockopt(s, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
+
+  To set the filters to zero filters is quite obsolete as not readed
+  data causes the raw socket to discard the received CAN frames. But
+  having this 'send only' use-case we may remove the receive list in the
+  Kernel to save a little (really a very little!) CPU usage.
+
+  4.1.2 RAW socket option CAN_RAW_ERR_FILTER
+
+  As described in chapter 3.4 the CAN interface driver can generate so
+  called Error Frames that can optionally be passed to the user
+  application on the same way like other CAN frames. The possible
+  errors are devided into different error classes that may be filtered
+  using the appropriate error mask. To register for every possible
+  error condition CAN_ERR_MASK can be used as value for the error mask.
+  The values for the error mask are defined in linux/can/error.h .
+
+    can_err_mask_t err_mask = ( CAN_ERR_TX_TIMEOUT | CAN_ERR_BUSOFF );
+
+    setsockopt(s, SOL_CAN_RAW, CAN_RAW_ERR_FILTER,
+               &err_mask, sizeof(err_mask));
+
+  4.1.3 RAW socket option CAN_RAW_LOOPBACK
+
+  To meet multi user needs the local loopback is enabled by default
+  (see chapter 3.2 for details). But in some embedded use-cases
+  (e.g. when only one application uses the CAN bus) this loopback
+  functionality can be disabled (separately for each socket):
+
+    int loopback = 0; /* 0 = disabled, 1 = enabled (default) */
+
+    setsockopt(s, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));
+
+  4.1.4 RAW socket option CAN_RAW_RECV_OWN_MSGS
+
+  When the local loopback is enabled, all the sent CAN frames are
+  looped back to the open CAN sockets that registered for the CAN
+  frames' CAN-ID on this given interface to meet the multi user
+  needs. The receiption of the CAN frames on the same socket that was
+  sending the CAN frame is assumed to be unwanted and therefore
+  disabled by default. This default behaviour may be changed on
+  demand:
+
+    int set_recv_own_msgs = 1; /* 0 = disabled (default), 1 = enabled */
+
+    setsockopt(s, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS,
+               &recv_own_msgs, sizeof(recv_own_msgs));
+
+  4.2 Broadcast Manager protocol sockets (SOCK_DGRAM)
+  4.3 connected transport protocols (SOCK_SEQPACKET)
+  4.4 unconnected transport protocols (SOCK_DGRAM)
+
+
+5. Socket CAN core module
+-------------------------
+
+  The Socket CAN core module implements the protocol family
+  PF_CAN. CAN protocol modules are loaded by the core module at
+  runtime. The core module provides an interface for CAN protocol
+  modules to subscribe needed CAN IDs (see chapter 3.1).
+
+  5.1 can.ko module params
+
+  - stats_timer: To calculate the Socket CAN core statistics
+    (e.g. current/maximum frames per second) this 1 second timer is
+    invoked at can.ko module start time by default. This timer can be
+    disabled giving stattimer=0 on the module comandline.
+
+  - debug: When the Kconfig option CONFIG_CAN_DEBUG_CORE is set at
+    compile time, the debug output code is compiled into the module.
+    debug = 0x01 => print general debug information
+    debug = 0x02 => print content of processed CAN frames
+    debug = 0x04 => print content of processed socket buffers
+
+    It is possible or have ORed values e.g. 3 or 7 for an output off
+    all available debug information. Using 0x02 and 0x04 may flood
+    your kernel log - so be careful.
+
+  5.2 procfs content
+
+  As described in chapter 3.1 the Socket CAN core uses several filter
+  lists to deliver received CAN frames to CAN protocol modules. These
+  receive lists, their filters and the count of filter matches can be
+  checked in the appropriate receive list. All entries contain the
+  device and a protocol module identifier:
+
+    foo@bar:~$ cat /proc/net/can/rcvlist_all
+
+    receive list 'rx_all':
+      (vcan3: no entry)
+      (vcan2: no entry)
+      (vcan1: no entry)
+      device   can_id   can_mask  function  userdata   matches  ident
+       vcan0     000    00000000  f88e6370  f6c6f400         0  raw
+      (any: no entry)
+
+  In this example an application requests any CAN traffic from vcan0.
+
+    rcvlist_all - list for unfiltered entries (no filter operations)
+    rcvlist_eff - list for single extended frame (EFF) entries
+    rcvlist_err - list for error frames masks
+    rcvlist_fil - list for mask/value filters
+    rcvlist_inv - list for mask/value filters (inverse semantic)
+    rcvlist_sff - list for single standard frame (SFF) entries
+
+  Additional procfs files in /proc/net/can
+
+    stats       - Socket CAN core statistics (rx/tx frames, match ratios, ...)
+    reset_stats - manual statistic reset
+    version     - prints the Socket CAN core version and the ABI version
+
+  5.3 writing own CAN protocol modules
+
+  To implement a new protocol in the protocol family PF_CAN a new
+  protocol has to be defined in include/linux/can.h .
+  The prototypes and definitions to use the Socket CAN core can be
+  accessed by including include/linux/can/core.h .
+  Additionally to functions that register the CAN protocol and the
+  CAN device notifier chain there are functions to subscribe CAN
+  frames received by CAN interfaces and to send CAN frames:
+
+    can_rx_register   - subscribe CAN frames from a specific interface
+    can_rx_unregister - unsubscribe CAN frames from a specific interface
+    can_send          - transmit a CAN frame (optional with local loopback)
+
+  For details see the kerneldoc documentation in net/can/af_can.c or
+  the source code of net/can/raw.c or net/can/bcm.c .
+
+6. CAN network drivers
+----------------------
+
+  Writing a CAN network device driver is much easier than writing a
+  CAN character device driver. Analogue to other know network device
+  drivers you mainly have to deal with:
+
+  - TX: Put the CAN frame from the socket buffer to the CAN controller.
+  - RX: Put the CAN frame from the CAN controller to the socket buffer.
+
+  See e.g. at Documentation/networking/netdevices.txt . The differences
+  for writing CAN network device driver are described below:
+
+  6.1 general settings
+
+    dev->type  = ARPHRD_CAN; /* the netdevice hardware type */
+    dev->flags = IFF_NOARP;  /* CAN has no arp */
+
+    dev->mtu   = sizeof(struct can_frame);
+
+  The struct can_frame is the payload of each socket buffer in the
+  protocol family PF_CAN.
+
+  6.2 loopback
+
+  As described in chapter 3.2 the CAN network device driver should
+  support a local loopback functionality. If so the driver flag
+  IFF_LOOPBACK has to be set to omit the PF_CAN core to perform the
+  loopback as fallback solution:
+
+    dev->flags = (IFF_NOARP | IFF_LOOPBACK);
+
+  6.3 CAN controller hardware filters
+
+  To reduce the interrupt load on deep embedded systems some CAN
+  controllers support the filtering of CAN IDs or ranges of CAN IDs.
+  These hardware filter capabilities vary from controller to
+  controller and have to be identified as not feasible in a multi-user
+  networking approach. The use of the very controller specific
+  hardware filters could make sense in a very dedicated use-case, as a
+  filter on driver level would affect all users in the multi-user
+  system. The high efficient filter sets inside the PF_CAN core allow
+  to set different multiple filters for each socket separately.
+  Therefore the use of hardware filters goes to the category 'handmade
+  tuning on deep embedded systems'. The author is running a MPC603e
+  @133MHz with four SJA1000 CAN controllers from 2002 under heavy bus
+  load without any problems ...
+
+  6.4 currently supported CAN hardware (May 2007)
+
+  On the project website http://developer.berlios.de/projects/socketcan
+  there are different drivers available:
+
+    vcan:    Virtual CAN interface driver (if no real hardware is available)
+    sja1000: Philips SJA1000 CAN controller (recommended)
+    i82527:  Intel i82527 CAN controller
+    mscan:   Motorola/Freescale CAN controller (e.g. inside SOC MPC5200)
+    slcan:   For a bunch of CAN adaptors that are attached via a
+             serial line ASCII protocol (for serial / USB adaptors)
+
+  Additionally the different CAN adaptors (ISA/PCI/PCMCIA/USB/Parport)
+  from PEAK Systemtechnik support the CAN netdevice driver modell
+  since Linux driver v6.0: http://www.peak-system.com/linux/index.htm
+
+  Please check the Mailing Lists on the berlios OSS project website.
+
+  6.5 todo (May 2007)
+
+  The configuration interface for CAN network drivers is still an open
+  issue that has not been finalized in the socketcan project. Also the
+  idea of having a library module (candev.ko) that holds functions
+  that are needed by all CAN netdevices is not ready to ship.
+  Your contribution is welcome.
+
+7. Credits
+----------
+
+  Oliver Hartkopp (PF_CAN core, filters, drivers, bcm)
+  Urs Thuermann (PF_CAN core, kernel integration, socket interfaces, raw, vcan)
+  Jan Kizka (RT-SocketCAN core, Socket-API reconciliation)
+  Wolfgang Grandegger (RT-SocketCAN core & drivers, Raw Socket-API reviews)
+  Robert Schwebel (design reviews, PTXdist integration)
+  Marc Kleine-Budde (design reviews, Kernel 2.6 cleanups, drivers)
+  Benedikt Spranger (reviews)
+  Thomas Gleixner (LKML reviews, coding style, posting hints)
+  Andrey Volkov (kernel subtree structure, ioctls, mscan driver)
+  Matthias Brukner (first SJA1000 CAN netdevice implementation Q2/2003)
+  Klaus Hitschler (PEAK driver integration)
+  Uwe Koppe (CAN netdevices with PF_PACKET approach)
+  Michael Schulze (driver layer loopback requirement, RT CAN drivers review)
Index: net-2.6/Documentation/networking/00-INDEX
===================================================================
--- net-2.6.orig/Documentation/networking/00-INDEX	2007-08-03 11:21:31.000000000 +0200
+++ net-2.6/Documentation/networking/00-INDEX	2007-08-03 11:21:58.000000000 +0200
@@ -26,6 +26,8 @@
 	- info on the driver for Baycom style amateur radio modems
 bridge.txt
 	- where to get user space programs for ethernet bridging with Linux.
+can.txt
+	- documentation on CAN protocol family.
 comx.txt
 	- info on drivers for COMX line of synchronous serial adapters.
 cops.txt

--

^ permalink raw reply

* [patch 2/7] CAN: Add PF_CAN core module
From: Urs Thuermann @ 2007-08-04  2:06 UTC (permalink / raw)
  To: netdev; +Cc: urs, oliver, davem, kaber, urs.thuermann, oliver.hartkopp
In-Reply-To: <20070804020657.25572.0@janus.isnogud.escape.de>

[-- Attachment #1: 02-can-core.diff --]
[-- Type: text/plain, Size: 60570 bytes --]

This patch adds the CAN core functionality but no protocols or drivers.
No protocol implementations are included here.  They come as separate
patches.  Protocol numbers are already in include/linux/can.h.

Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>

---
 include/linux/can.h       |  113 +++++
 include/linux/can/core.h  |   80 +++
 include/linux/can/error.h |   93 ++++
 net/Kconfig               |    1 
 net/Makefile              |    1 
 net/can/Kconfig           |   25 +
 net/can/Makefile          |    6 
 net/can/af_can.c          |  995 ++++++++++++++++++++++++++++++++++++++++++++++
 net/can/af_can.h          |  121 +++++
 net/can/proc.c            |  531 ++++++++++++++++++++++++
 10 files changed, 1966 insertions(+)

Index: net-2.6/include/linux/can.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/include/linux/can.h	2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,113 @@
+/*
+ * linux/can.h
+ *
+ * Definitions for CAN networklayer (socket addr / CAN frame / CAN filter)
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ *          Urs Thuermann   <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_H
+#define CAN_H
+
+#include <linux/types.h>
+#include <linux/socket.h>
+
+/* controller area network (CAN) kernel definitions */
+
+/* special address description flags for the CAN_ID */
+#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */
+#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */
+#define CAN_ERR_FLAG 0x20000000U /* error frame */
+
+/* valid bits in CAN ID for frame formats */
+#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */
+#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */
+#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */
+
+/*
+ * Controller Area Network Identifier structure
+ *
+ * bit 0-28	: CAN identifier (11/29 bit)
+ * bit 29	: error frame flag (0 = data frame, 1 = error frame)
+ * bit 30	: remote transmission request flag (1 = rtr frame)
+ * bit 31	: frame format flag (0 = standard 11 bit, 1 = extended 29 bit)
+ */
+typedef __u32 canid_t;
+
+/*
+ * Controller Area Network Error Frame Mask structure
+ *
+ * bit 0-28	: error class mask (see include/linux/can/error.h)
+ * bit 29-31	: set to zero
+ */
+typedef __u32 can_err_mask_t;
+
+/**
+ * struct can_frame - basic CAN frame structure
+ * @can_id:  the CAN ID of the frame and CAN_*_FLAG flags, see above.
+ * @can_dlc: the data length field of the CAN frame
+ * @data:    the CAN frame payload.
+ */
+struct can_frame {
+	canid_t can_id;  /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+	__u8    can_dlc; /* data length code: 0 .. 8 */
+	__u8    data[8] __attribute__((aligned(8)));
+};
+
+/* particular protocols of the protocol family PF_CAN */
+#define CAN_RAW		1 /* RAW sockets */
+#define CAN_BCM		2 /* Broadcast Manager */
+#define CAN_TP16	3 /* VAG Transport Protocol v1.6 */
+#define CAN_TP20	4 /* VAG Transport Protocol v2.0 */
+#define CAN_MCNET	5 /* Bosch MCNet */
+#define CAN_ISOTP	6 /* ISO 15765-2 Transport Protocol */
+#define CAN_BAP		7 /* VAG Bedien- und Anzeigeprotokoll */
+#define CAN_NPROTO	8
+
+#define SOL_CAN_BASE 100
+
+/**
+ * struct sockaddr_can - the sockaddr structure for CAN sockets
+ * @can_family:  address family number AF_CAN.
+ * @can_ifindex: CAN network interface index.
+ * @can_addr:    transport protocol specific address, mostly CAN IDs.
+ */
+struct sockaddr_can {
+	sa_family_t can_family;
+	int         can_ifindex;
+	union {
+		struct { canid_t rx_id, tx_id; } tp16;
+		struct { canid_t rx_id, tx_id; } tp20;
+		struct { canid_t rx_id, tx_id; } mcnet;
+		struct { canid_t rx_id, tx_id; } isotp;
+		struct { int     lcu,   type;  } bap;
+	} can_addr;
+};
+
+/**
+ * struct can_filter - CAN ID based filter in can_register().
+ * @can_id:   relevant bits of CAN ID which are not masked out.
+ * @can_mask: CAN mask (see description)
+ *
+ * Description:
+ * A filter matches, when
+ *
+ *          <received_can_id> & mask == can_id & mask
+ *
+ * The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ * filter for error frames (CAN_ERR_FLAG bit set in mask).
+ */
+struct can_filter {
+	canid_t can_id;
+	canid_t can_mask;
+};
+
+#define CAN_INV_FILTER 0x20000000U /* to be set in can_filter.can_id */
+
+#endif /* CAN_H */
Index: net-2.6/include/linux/can/core.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/include/linux/can/core.h	2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,80 @@
+/*
+ * linux/can/core.h
+ *
+ * Protoypes and definitions for CAN protocol modules using the PF_CAN core
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ *          Urs Thuermann   <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_CORE_H
+#define CAN_CORE_H
+
+#include <linux/can.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define CAN_VERSION "20070802"
+
+/* increment this number each time you change some user-space interface */
+#define CAN_ABI_VERSION "8"
+
+#define CAN_VERSION_STRING "rev " CAN_VERSION " abi " CAN_ABI_VERSION
+
+#define DNAME(dev) ((dev) ? (dev)->name : "any")
+
+#define CAN_PROC_DIR "net/can" /* /proc/... */
+
+/**
+ * struct can_proto - CAN protocol structure
+ * @type:       type argument in socket() syscall, e.g. SOCK_DGRAM.
+ * @protocol:   protocol number in socket() syscall.
+ * @capability: capability needed to open the socket, or -1 for no restriction.
+ * @ops:        pointer to struct proto_ops for sock->ops.
+ * @prot:       pointer to struct proto structure.
+ */
+struct can_proto {
+	int              type;
+	int              protocol;
+	int              capability;
+	struct proto_ops *ops;
+	struct proto     *prot;
+};
+
+/* function prototypes for the CAN networklayer core (af_can.c) */
+
+extern int can_proto_register(struct can_proto *cp);
+extern int can_proto_unregister(struct can_proto *cp);
+
+extern int can_rx_register(struct net_device *dev, canid_t can_id,
+			   canid_t mask,
+			   void (*func)(struct sk_buff *, void *),
+			   void *data, char *ident);
+
+extern int can_rx_unregister(struct net_device *dev, canid_t can_id,
+			     canid_t mask,
+			     void (*func)(struct sk_buff *, void *),
+			     void *data);
+
+extern int can_send(struct sk_buff *skb, int loop);
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+extern void can_debug_skb(struct sk_buff *skb);
+extern void can_debug_cframe(const char *msg, struct can_frame *cframe, ...);
+#define DBG(args...)       (debug & 1 ? \
+			       (printk(KERN_DEBUG "can-%s %s: ", \
+				IDENT, __func__), printk(args)) : 0)
+#define DBG_FRAME(args...) (debug & 2 ? can_debug_cframe(args) : 0)
+#define DBG_SKB(skb)       (debug & 4 ? can_debug_skb(skb) : 0)
+#else
+#define DBG(args...)
+#define DBG_FRAME(args...)
+#define DBG_SKB(skb)
+#endif
+
+#endif /* CAN_CORE_H */
Index: net-2.6/net/Kconfig
===================================================================
--- net-2.6.orig/net/Kconfig	2007-08-03 11:21:32.000000000 +0200
+++ net-2.6/net/Kconfig	2007-08-03 11:21:46.000000000 +0200
@@ -210,6 +210,7 @@
 endmenu
 
 source "net/ax25/Kconfig"
+source "net/can/Kconfig"
 source "net/irda/Kconfig"
 source "net/bluetooth/Kconfig"
 source "net/rxrpc/Kconfig"
Index: net-2.6/net/Makefile
===================================================================
--- net-2.6.orig/net/Makefile	2007-08-03 11:21:32.000000000 +0200
+++ net-2.6/net/Makefile	2007-08-03 11:21:46.000000000 +0200
@@ -34,6 +34,7 @@
 obj-$(CONFIG_NETROM)		+= netrom/
 obj-$(CONFIG_ROSE)		+= rose/
 obj-$(CONFIG_AX25)		+= ax25/
+obj-$(CONFIG_CAN)		+= can/
 obj-$(CONFIG_IRDA)		+= irda/
 obj-$(CONFIG_BT)		+= bluetooth/
 obj-$(CONFIG_SUNRPC)		+= sunrpc/
Index: net-2.6/net/can/Kconfig
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/Kconfig	2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,25 @@
+#
+# Controller Area Network (CAN) network layer core configuration
+#
+
+menuconfig CAN
+	depends on NET
+	tristate "CAN bus subsystem support"
+	---help---
+	  Controller Area Network (CAN) is a slow (up to 1Mbit/s) serial
+	  communications protocol, which was developed by Bosch at
+	  1991 mainly for automotive, but now widely used in marine
+	  (NMEA2000), industrial and medical applications.
+	  More information on the CAN network protocol family PF_CAN
+	  is contained in <Documentation/networking/can.txt>.
+
+	  If you want CAN support, you should say Y here and also to the
+	  specific driver for your controller(s) below.
+
+config CAN_DEBUG_CORE
+	bool "CAN Core debugging messages"
+	depends on CAN
+	---help---
+	  Say Y here if you want the CAN core to produce a bunch of debug
+	  messages to the system log.  Select this if you are having a
+	  problem with CAN support and want to see more of what is going on.
Index: net-2.6/net/can/Makefile
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/Makefile	2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,6 @@
+#
+#  Makefile for the Linux Controller Area Network core.
+#
+
+obj-$(CONFIG_CAN)	+= can.o
+can-objs		:= af_can.o proc.o
Index: net-2.6/net/can/af_can.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/af_can.c	2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,995 @@
+/*
+ * af_can.c - Protocol family CAN core module
+ *            (used by different CAN protocol modules)
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, the following disclaimer and
+ *    the referenced file 'COPYING'.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#include <linux/uaccess.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <net/sock.h>
+
+#include "af_can.h"
+
+#define IDENT "core"
+static __initdata const char banner[] = KERN_INFO
+	"can: controller area network core (" CAN_VERSION_STRING ")\n";
+
+MODULE_DESCRIPTION("Controller Area Network PF_CAN core");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>, "
+	      "Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+
+MODULE_ALIAS_NETPROTO(PF_CAN);
+
+int stats_timer = 1; /* default: on */
+module_param(stats_timer, int, S_IRUGO);
+MODULE_PARM_DESC(stats_timer, "enable timer for statistics (default:on)");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+HLIST_HEAD(rx_dev_list);
+static struct dev_rcv_lists rx_alldev_list;
+static DEFINE_SPINLOCK(rcv_lists_lock);
+
+static struct kmem_cache *rcv_cache __read_mostly;
+
+/* table of registered CAN protocols */
+static struct can_proto *proto_tab[CAN_NPROTO];
+
+struct timer_list stattimer; /* timer for statistics update */
+struct s_stats  stats;       /* packet statistics */
+struct s_pstats pstats;      /* receive list statistics */
+
+/*
+ * af_can socket functions
+ */
+
+static int can_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+
+	switch (cmd) {
+
+	case SIOCGSTAMP:
+		return sock_get_timestamp(sk, (struct timeval __user *)arg);
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+static void can_sock_destruct(struct sock *sk)
+{
+	DBG("called for sock %p\n", sk);
+
+	skb_queue_purge(&sk->sk_receive_queue);
+	if (sk->sk_protinfo)
+		kfree(sk->sk_protinfo);
+}
+
+static int can_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+	struct can_proto *cp;
+	char module_name[sizeof("can-proto-000")];
+	int ret = 0;
+
+	DBG("socket %p, type %d, proto %d\n", sock, sock->type, protocol);
+
+	sock->state = SS_UNCONNECTED;
+
+	if (protocol < 0 || protocol >= CAN_NPROTO)
+		return -EINVAL;
+
+	DBG("looking up proto %d in proto_tab[]\n", protocol);
+
+	/* try to load protocol module, when CONFIG_KMOD is defined */
+	if (!proto_tab[protocol]) {
+		sprintf(module_name, "can-proto-%d", protocol);
+		ret = request_module(module_name);
+
+		/*
+		 * In case of error we only print a message but don't
+		 * return the error code immediately.  Below we will
+		 * return -EPROTONOSUPPORT
+		 */
+		if (ret == -ENOSYS)
+			printk(KERN_INFO "can: request_module(%s) not"
+			       " implemented.\n", module_name);
+		else if (ret)
+			printk(KERN_ERR "can: request_module(%s) failed\n",
+			       module_name);
+	}
+
+	/* check for success and correct type */
+	cp = proto_tab[protocol];
+	if (!cp || cp->type != sock->type)
+		return -EPROTONOSUPPORT;
+
+	if (cp->capability >= 0 && !capable(cp->capability))
+		return -EPERM;
+
+	sock->ops = cp->ops;
+
+	sk = sk_alloc(PF_CAN, GFP_KERNEL, cp->prot, 1);
+	if (!sk)
+		return -ENOMEM;
+
+	sock_init_data(sock, sk);
+	sk->sk_destruct = can_sock_destruct;
+
+	DBG("created sock: %p\n", sk);
+
+	if (sk->sk_prot->init)
+		ret = sk->sk_prot->init(sk);
+
+	if (ret) {
+		/* release sk on errors */
+		sock_orphan(sk);
+		sock_put(sk);
+	}
+
+	return ret;
+}
+
+/*
+ * af_can tx path
+ */
+
+/**
+ * can_send - transmit a CAN frame (optional with local loopback)
+ * @skb: pointer to socket buffer with CAN frame in data section
+ * @loop: loopback for listeners on local CAN sockets (recommended default!)
+ *
+ * Return:
+ *  0 on success
+ *  -ENETDOWN when the selected interface is down
+ *  -ENOBUFS on full driver queue (see net_xmit_errno())
+ *  -ENOMEM when local loopback failed at calling skb_clone()
+ */
+int can_send(struct sk_buff *skb, int loop)
+{
+	int err;
+
+	if (skb->dev->type != ARPHRD_CAN) {
+		kfree_skb(skb);
+		return -EPERM;
+	}
+
+	if (!(skb->dev->flags & IFF_UP)) {
+		kfree_skb(skb);
+		return -ENETDOWN;
+	}
+
+	skb->protocol = htons(ETH_P_CAN);
+
+	if (loop) {
+		/* local loopback of sent CAN frames */
+
+		/* indication for the CAN driver: do loopback */
+		skb->pkt_type = PACKET_LOOPBACK;
+
+		/*
+		 * The reference to the originating sock may be required
+		 * by the receiving socket to check whether the frame is
+		 * its own. Example: can_raw sockopt CAN_RAW_RECV_OWN_MSGS
+		 * Therefore we have to ensure that skb->sk remains the
+		 * reference to the originating sock by restoring skb->sk
+		 * after each skb_clone() or skb_orphan() usage.
+		 */
+
+		if (!(skb->dev->flags & IFF_LOOPBACK)) {
+			/*
+			 * If the interface is not capable to do loopback
+			 * itself, we do it here.
+			 */
+			struct sk_buff *newskb = skb_clone(skb, GFP_ATOMIC);
+
+			if (!newskb) {
+				kfree_skb(skb);
+				return -ENOMEM;
+			}
+
+			newskb->sk = skb->sk;
+			newskb->ip_summed = CHECKSUM_UNNECESSARY;
+			newskb->pkt_type = PACKET_BROADCAST;
+			netif_rx(newskb);
+		}
+	} else {
+		/* indication for the CAN driver: no loopback required */
+		skb->pkt_type = PACKET_HOST;
+	}
+
+	/* send to netdevice */
+	err = dev_queue_xmit(skb);
+	if (err > 0)
+		err = net_xmit_errno(err);
+
+	/* update statistics */
+	stats.tx_frames++;
+	stats.tx_frames_delta++;
+
+	return err;
+}
+EXPORT_SYMBOL(can_send);
+
+/*
+ * af_can rx path
+ */
+
+static struct dev_rcv_lists *find_dev_rcv_lists(struct net_device *dev)
+{
+	struct dev_rcv_lists *d;
+	struct hlist_node *n;
+
+	/*
+	 * find receive list for this device
+	 *
+	 * The hlist_for_each_entry*() macros curse through the list
+	 * using the pointer variable n and set d to the containing
+	 * struct in each list iteration.  Therefore, after list
+	 * iteration, d is unmodified when the list is empty, and it
+	 * points to last list element, when the list is non-empty
+	 * but no match in the loop body is found.  I.e. d is *not*
+	 * NULL when no match is found.  We can, however, use the
+	 * cursor variable n to decide if a match was found.
+	 */
+
+	hlist_for_each_entry(d, n, &rx_dev_list, list) {
+		if (d->dev == dev)
+			break;
+	}
+
+	return n ? d : NULL;
+}
+
+static struct hlist_head *find_rcv_list(canid_t *can_id, canid_t *mask,
+					struct dev_rcv_lists *d)
+{
+	canid_t inv = *can_id & CAN_INV_FILTER; /* save flag before masking */
+
+	/* filter error frames */
+	if (*mask & CAN_ERR_FLAG) {
+		/* clear CAN_ERR_FLAG in list entry */
+		*mask &= CAN_ERR_MASK;
+		return &d->rx[RX_ERR];
+	}
+
+	/* ensure valid values in can_mask */
+	if (*mask & CAN_EFF_FLAG)
+		*mask &= (CAN_EFF_MASK | CAN_EFF_FLAG | CAN_RTR_FLAG);
+	else
+		*mask &= (CAN_SFF_MASK | CAN_RTR_FLAG);
+
+	/* reduce condition testing at receive time */
+	*can_id &= *mask;
+
+	/* inverse can_id/can_mask filter */
+	if (inv)
+		return &d->rx[RX_INV];
+
+	/* mask == 0 => no condition testing at receive time */
+	if (!(*mask))
+		return &d->rx[RX_ALL];
+
+	/* use extra filterset for the subscription of exactly *ONE* can_id */
+	if (*can_id & CAN_EFF_FLAG) {
+		if (*mask == (CAN_EFF_MASK | CAN_EFF_FLAG)) {
+			/* RFC: a use-case for hash-tables in the future? */
+			return &d->rx[RX_EFF];
+		}
+	} else {
+		if (*mask == CAN_SFF_MASK)
+			return &d->rx_sff[*can_id];
+	}
+
+	/* default: filter via can_id/can_mask */
+	return &d->rx[RX_FIL];
+}
+
+/**
+ * can_rx_register - subscribe CAN frames from a specific interface
+ * @dev: pointer to netdevice (NULL => subcribe from 'all' CAN devices list)
+ * @can_id: CAN identifier (see description)
+ * @mask: CAN mask (see description)
+ * @func: callback function on filter match
+ * @data: returned parameter for callback function
+ * @ident: string for calling module indentification
+ *
+ * Description:
+ *  Invokes the callback function with the received sk_buff and the given
+ *  parameter 'data' on a matching receive filter. A filter matches, when
+ *
+ *          <received_can_id> & mask == can_id & mask
+ *
+ *  The filter can be inverted (CAN_INV_FILTER bit set in can_id) or it can
+ *  filter for error frames (CAN_ERR_FLAG bit set in mask).
+ *
+ * Return:
+ *  0 on success
+ *  -ENOMEM on missing cache mem to create subscription entry
+ *  -ENODEV unknown device
+ */
+int can_rx_register(struct net_device *dev, canid_t can_id, canid_t mask,
+		    void (*func)(struct sk_buff *, void *), void *data,
+		    char *ident)
+{
+	struct receiver *r;
+	struct hlist_head *rl;
+	struct dev_rcv_lists *d;
+	int ret = 0;
+
+	/* insert new receiver  (dev,canid,mask) -> (func,data) */
+
+	DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p, "
+	    "ident %s\n", dev, DNAME(dev), can_id, mask, func, data, ident);
+
+	r = kmem_cache_alloc(rcv_cache, GFP_KERNEL);
+	if (!r)
+		return -ENOMEM;
+
+	spin_lock_bh(&rcv_lists_lock);
+
+	d = find_dev_rcv_lists(dev);
+	if (d) {
+		rl = find_rcv_list(&can_id, &mask, d);
+
+		r->can_id  = can_id;
+		r->mask    = mask;
+		r->matches = 0;
+		r->func    = func;
+		r->data    = data;
+		r->ident   = ident;
+
+		hlist_add_head_rcu(&r->list, rl);
+		d->entries++;
+
+		pstats.rcv_entries++;
+		if (pstats.rcv_entries_max < pstats.rcv_entries)
+			pstats.rcv_entries_max = pstats.rcv_entries;
+	} else {
+		DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+		    DNAME(dev), can_id, mask);
+		kmem_cache_free(rcv_cache, r);
+		ret = -ENODEV;
+	}
+
+	spin_unlock_bh(&rcv_lists_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(can_rx_register);
+
+/*
+ * can_rx_delete_device - rcu callback for dev_rcv_lists structure removal
+ */
+static void can_rx_delete_device(struct rcu_head *rp)
+{
+	struct dev_rcv_lists *d = container_of(rp, struct dev_rcv_lists, rcu);
+
+	DBG("removing dev_rcv_list at %p\n", d);
+	kfree(d);
+}
+
+/*
+ * can_rx_delete_receiver - rcu callback for single receiver entry removal
+ */
+static void can_rx_delete_receiver(struct rcu_head *rp)
+{
+	struct receiver *r = container_of(rp, struct receiver, rcu);
+
+	DBG("removing receiver at %p\n", r);
+	kmem_cache_free(rcv_cache, r);
+}
+
+/**
+ * can_rx_unregister - unsubscribe CAN frames from a specific interface
+ * @dev: pointer to netdevice (NULL => unsubcribe from 'all' CAN devices list)
+ * @can_id: CAN identifier
+ * @mask: CAN mask
+ * @func: callback function on filter match
+ * @data: returned parameter for callback function
+ *
+ * Description:
+ *  Removes subscription entry depending on given (subscription) values.
+ *
+ * Return:
+ *  0 on success
+ *  -EINVAL on missing subscription entry
+ *  -ENODEV unknown device
+ */
+int can_rx_unregister(struct net_device *dev, canid_t can_id, canid_t mask,
+		      void (*func)(struct sk_buff *, void *), void *data)
+{
+	struct receiver *r = NULL;
+	struct hlist_head *rl;
+	struct hlist_node *next;
+	struct dev_rcv_lists *d;
+	int ret = 0;
+
+	DBG("dev %p (%s), id %03X, mask %03X, callback %p, data %p\n",
+	    dev, DNAME(dev), can_id, mask, func, data);
+
+	spin_lock_bh(&rcv_lists_lock);
+
+	d = find_dev_rcv_lists(dev);
+	if (!d) {
+		DBG("receive list not found for dev %s, id %03X, mask %03X\n",
+		    DNAME(dev), can_id, mask);
+		ret = -ENODEV;
+		goto out;
+	}
+
+	rl = find_rcv_list(&can_id, &mask, d);
+
+	/*
+	 * Search the receiver list for the item to delete.  This should
+	 * exist, since no receiver may be unregistered that hasn't
+	 * been registered before.
+	 */
+
+	hlist_for_each_entry(r, next, rl, list) {
+		if (r->can_id == can_id && r->mask == mask
+		    && r->func == func && r->data == data)
+			break;
+	}
+
+	/*
+	 * Check for bug in CAN protocol implementations:
+	 * If no matching list item was found, the list cursor variable next
+	 * will be NULL, while r will point to the last item of the list.
+	 */
+
+	if (!next) {
+		DBG("receive list entry not found for "
+		    "dev %s, id %03X, mask %03X\n", DNAME(dev), can_id, mask);
+		ret = -EINVAL;
+		r = NULL;
+		d = NULL;
+		goto out;
+	}
+
+	hlist_del_rcu(&r->list);
+	d->entries--;
+
+	if (pstats.rcv_entries > 0)
+		pstats.rcv_entries--;
+
+	/* remove device structure requested by NETDEV_UNREGISTER */
+	if (d->remove_on_zero_entries && !d->entries) {
+		DBG("removing dev_rcv_list for %s on zero entries\n",
+		    dev->name);
+		hlist_del_rcu(&d->list);
+	} else
+		d = NULL;
+
+ out:
+	spin_unlock_bh(&rcv_lists_lock);
+
+	/* schedule the receiver item for deletion */
+	if (r)
+		call_rcu(&r->rcu, can_rx_delete_receiver);
+
+	/* schedule the device structure for deletion */
+	if (d)
+		call_rcu(&d->rcu, can_rx_delete_device);
+
+	return ret;
+}
+EXPORT_SYMBOL(can_rx_unregister);
+
+static inline void deliver(struct sk_buff *skb, struct receiver *r)
+{
+	struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC);
+
+	DBG("skbuff %p cloned to %p\n", skb, clone);
+	if (clone) {
+		clone->sk = skb->sk;
+		r->func(clone, r->data);
+		r->matches++;
+	}
+}
+
+static int can_rcv_filter(struct dev_rcv_lists *d, struct sk_buff *skb)
+{
+	struct receiver *r;
+	struct hlist_node *n;
+	int matches = 0;
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	canid_t can_id = cf->can_id;
+
+	if (d->entries == 0)
+		return 0;
+
+	if (can_id & CAN_ERR_FLAG) {
+		/* check for error frame entries only */
+		hlist_for_each_entry_rcu(r, n, &d->rx[RX_ERR], list) {
+			if (can_id & r->mask) {
+				DBG("match on rx_err skbuff %p\n", skb);
+				deliver(skb, r);
+				matches++;
+			}
+		}
+		return matches;
+	}
+
+	/* check for unfiltered entries */
+	hlist_for_each_entry_rcu(r, n, &d->rx[RX_ALL], list) {
+		DBG("match on rx_all skbuff %p\n", skb);
+		deliver(skb, r);
+		matches++;
+	}
+
+	/* check for can_id/mask entries */
+	hlist_for_each_entry_rcu(r, n, &d->rx[RX_FIL], list) {
+		if ((can_id & r->mask) == r->can_id) {
+			DBG("match on rx_fil skbuff %p\n", skb);
+			deliver(skb, r);
+			matches++;
+		}
+	}
+
+	/* check for inverted can_id/mask entries */
+	hlist_for_each_entry_rcu(r, n, &d->rx[RX_INV], list) {
+		if ((can_id & r->mask) != r->can_id) {
+			DBG("match on rx_inv skbuff %p\n", skb);
+			deliver(skb, r);
+			matches++;
+		}
+	}
+
+	/* check CAN_ID specific entries */
+	if (can_id & CAN_EFF_FLAG) {
+		hlist_for_each_entry_rcu(r, n, &d->rx[RX_EFF], list) {
+			if (r->can_id == can_id) {
+				DBG("match on rx_eff skbuff %p\n", skb);
+				deliver(skb, r);
+				matches++;
+			}
+		}
+	} else {
+		can_id &= CAN_SFF_MASK;
+		hlist_for_each_entry_rcu(r, n, &d->rx_sff[can_id], list) {
+			DBG("match on rx_sff skbuff %p\n", skb);
+			deliver(skb, r);
+			matches++;
+		}
+	}
+
+	return matches;
+}
+
+static int can_rcv(struct sk_buff *skb, struct net_device *dev,
+		   struct packet_type *pt, struct net_device *orig_dev)
+{
+	struct dev_rcv_lists *d;
+	int matches;
+
+	DBG("received skbuff on device %s, ptype %04x\n",
+	    dev->name, ntohs(pt->type));
+	DBG_SKB(skb);
+	DBG_FRAME("af_can: can_rcv: received CAN frame",
+		  (struct can_frame *)skb->data);
+
+	if (dev->type != ARPHRD_CAN) {
+		kfree_skb(skb);
+		return 0;
+	}
+
+	/* update statistics */
+	stats.rx_frames++;
+	stats.rx_frames_delta++;
+
+	rcu_read_lock();
+
+	/* deliver the packet to sockets listening on all devices */
+	matches = can_rcv_filter(&rx_alldev_list, skb);
+
+	/* find receive list for this device */
+	d = find_dev_rcv_lists(dev);
+	if (d)
+		matches += can_rcv_filter(d, skb);
+
+	rcu_read_unlock();
+
+	/* free the skbuff allocated by the netdevice driver */
+	DBG("freeing skbuff %p\n", skb);
+	kfree_skb(skb);
+
+	if (matches > 0) {
+		stats.matches++;
+		stats.matches_delta++;
+	}
+
+	return 0;
+}
+
+/*
+ * af_can protocol functions
+ */
+
+/**
+ * can_proto_register - register CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ *
+ * Return:
+ *  0 on success
+ *  -EINVAL invalid (out of range) protocol number
+ *  -EBUSY  protocol already in use
+ *  -ENOBUF if proto_register() fails
+ */
+int can_proto_register(struct can_proto *cp)
+{
+	int proto = cp->protocol;
+	int err = 0;
+
+	if (proto < 0 || proto >= CAN_NPROTO) {
+		printk(KERN_ERR "can: protocol number %d out of range\n",
+		       proto);
+		return -EINVAL;
+	}
+	if (proto_tab[proto]) {
+		printk(KERN_ERR "can: protocol %d already registered\n",
+		       proto);
+		return -EBUSY;
+	}
+
+	err = proto_register(cp->prot, 0);
+	if (err < 0)
+		return err;
+
+	proto_tab[proto] = cp;
+
+	/* use generic ioctl function if the module doesn't bring its own */
+	if (!cp->ops->ioctl)
+		cp->ops->ioctl = can_ioctl;
+
+	return err;
+}
+EXPORT_SYMBOL(can_proto_register);
+
+/**
+ * can_proto_unregister - unregister CAN transport protocol
+ * @cp: pointer to CAN protocol structure
+ *
+ * Return:
+ *  0 on success
+ *  -ESRCH protocol number was not registered
+ */
+int can_proto_unregister(struct can_proto *cp)
+{
+	int proto = cp->protocol;
+
+	if (!proto_tab[proto]) {
+		printk(KERN_ERR "can: protocol %d is not registered\n", proto);
+		return -ESRCH;
+	}
+	proto_unregister(cp->prot);
+	proto_tab[proto] = NULL;
+
+	return 0;
+}
+EXPORT_SYMBOL(can_proto_unregister);
+
+/*
+ * af_can notifier to create/remove CAN netdevice specific structs
+ */
+static int can_notifier(struct notifier_block *nb, unsigned long msg,
+			void *data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct dev_rcv_lists *d;
+
+	DBG("msg %ld for dev %p (%s idx %d)\n",
+	    msg, dev, dev->name, dev->ifindex);
+
+	if (dev->type != ARPHRD_CAN)
+		return NOTIFY_DONE;
+
+	switch (msg) {
+
+	case NETDEV_REGISTER:
+
+		/*
+		 * create new dev_rcv_lists for this device
+		 *
+		 * N.B. zeroing the struct is the correct initialization
+		 * for the embedded hlist_head structs.
+		 * Another list type, e.g. list_head, would require
+		 * explicit initialization.
+		 */
+
+		DBG("creating new dev_rcv_lists for %s\n", dev->name);
+
+		d = kzalloc(sizeof(*d),
+			    in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+		if (!d) {
+			printk(KERN_ERR
+			       "can: allocation of receive list failed\n");
+			return NOTIFY_DONE;
+		}
+		d->dev = dev;
+
+		spin_lock_bh(&rcv_lists_lock);
+		hlist_add_head_rcu(&d->list, &rx_dev_list);
+		spin_unlock_bh(&rcv_lists_lock);
+
+		break;
+
+	case NETDEV_UNREGISTER:
+		spin_lock_bh(&rcv_lists_lock);
+
+		d = find_dev_rcv_lists(dev);
+		if (d) {
+			DBG("remove dev_rcv_list for %s (%d entries)\n",
+			    dev->name, d->entries);
+
+			if (d->entries) {
+				d->remove_on_zero_entries = 1;
+				d = NULL;
+			} else
+				hlist_del_rcu(&d->list);
+		} else
+			printk(KERN_ERR "can: notifier: receive list not "
+			       "found for dev %s\n", dev->name);
+
+		spin_unlock_bh(&rcv_lists_lock);
+
+		if (d)
+			call_rcu(&d->rcu, can_rx_delete_device);
+
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+/*
+ * af_can debugging stuff
+ */
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+
+#define DBG_BSIZE 1024
+
+/**
+ * can_debug_cframe - print CAN frame
+ * @msg: pointer to message printed before the given CAN frame
+ * @cf: pointer to CAN frame
+ */
+void can_debug_cframe(const char *msg, struct can_frame *cf, ...)
+{
+	va_list ap;
+	int len;
+	int dlc, i;
+	char *buf;
+
+	buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+	if (!buf)
+		return;
+
+	len = sprintf(buf, KERN_DEBUG);
+	va_start(ap, cf);
+	len += snprintf(buf + len, DBG_BSIZE - 64, msg, ap);
+	buf[len++] = ':';
+	buf[len++] = ' ';
+	va_end(ap);
+
+	dlc = cf->can_dlc;
+	if (dlc > 8)
+		dlc = 8;
+
+	if (cf->can_id & CAN_EFF_FLAG)
+		len += sprintf(buf + len, "<%08X> [%X] ",
+			       cf->can_id & CAN_EFF_MASK, dlc);
+	else
+		len += sprintf(buf + len, "<%03X> [%X] ",
+			       cf->can_id & CAN_SFF_MASK, dlc);
+
+	for (i = 0; i < dlc; i++)
+		len += sprintf(buf + len, "%02X ", cf->data[i]);
+
+	if (cf->can_id & CAN_RTR_FLAG)
+		len += sprintf(buf + len, "(RTR)");
+
+	buf[len++] = '\n';
+	buf[len]   = '\0';
+	printk(buf);
+	kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_cframe);
+
+/**
+ * can_debug_skb - print socket buffer content to kernel log
+ * @skb: pointer to socket buffer
+ */
+void can_debug_skb(struct sk_buff *skb)
+{
+	int len, nbytes, i;
+	char *buf;
+
+	buf = kmalloc(DBG_BSIZE, GFP_ATOMIC);
+	if (!buf)
+		return;
+
+	len = sprintf(buf,
+		      KERN_DEBUG "  skbuff at %p, dev: %d, proto: %04x\n"
+		      KERN_DEBUG "  users: %d, dataref: %d, nr_frags: %d, "
+		      "h,d,t,e,l: %p %+d %+d %+d, %d",
+		      skb, skb->dev ? skb->dev->ifindex : -1,
+		      ntohs(skb->protocol),
+		      atomic_read(&skb->users),
+		      atomic_read(&(skb_shinfo(skb)->dataref)),
+		      skb_shinfo(skb)->nr_frags,
+		      skb->head, skb->data - skb->head,
+		      skb->tail - skb->head, skb->end - skb->head, skb->len);
+	nbytes = skb->end - skb->head;
+	for (i = 0; i < nbytes; i++) {
+		if (i % 16 == 0)
+			len += sprintf(buf + len, "\n" KERN_DEBUG "  ");
+		if (len < DBG_BSIZE - 16) {
+			len += sprintf(buf + len, " %02x", skb->head[i]);
+		} else {
+			len += sprintf(buf + len, "...");
+			break;
+		}
+	}
+	buf[len++] = '\n';
+	buf[len]   = '\0';
+	printk(buf);
+	kfree(buf);
+}
+EXPORT_SYMBOL(can_debug_skb);
+
+#endif
+
+/*
+ * af_can module init/exit functions
+ */
+
+static struct packet_type can_packet = {
+	.type = __constant_htons(ETH_P_CAN),
+	.dev  = NULL,
+	.func = can_rcv,
+};
+
+static struct net_proto_family can_family_ops = {
+	.family = PF_CAN,
+	.create = can_create,
+	.owner  = THIS_MODULE,
+};
+
+/* notifier block for netdevice event */
+static struct notifier_block can_netdev_notifier = {
+	.notifier_call = can_notifier,
+};
+
+static __init int can_init(void)
+{
+	printk(banner);
+
+	rcv_cache = kmem_cache_create("can_receiver", sizeof(struct receiver),
+				      0, 0, NULL);
+	if (!rcv_cache)
+		return -ENOMEM;
+
+	/*
+	 * Insert rx_alldev_list for reception on all devices.
+	 * This struct is zero initialized which is correct for the
+	 * embedded hlist heads, the dev pointer, and the entries counter.
+	 */
+
+	spin_lock_bh(&rcv_lists_lock);
+	hlist_add_head_rcu(&rx_alldev_list.list, &rx_dev_list);
+	spin_unlock_bh(&rcv_lists_lock);
+
+	if (stats_timer) {
+		/* the statistics are updated every second (timer triggered) */
+		init_timer(&stattimer);
+		stattimer.function = can_stat_update;
+		stattimer.data = 0;
+		/* update every second */
+		stattimer.expires = jiffies + HZ;
+		/* start statistics timer */
+		add_timer(&stattimer);
+	} else
+		stattimer.function = NULL;
+
+	/* procfs init */
+	can_init_proc();
+
+	/* protocol register */
+	sock_register(&can_family_ops);
+	register_netdevice_notifier(&can_netdev_notifier);
+	dev_add_pack(&can_packet);
+
+	return 0;
+}
+
+static __exit void can_exit(void)
+{
+	struct dev_rcv_lists *d;
+	struct hlist_node *n, *next;
+
+	if (stats_timer)
+		del_timer(&stattimer);
+
+	/* procfs remove */
+	can_remove_proc();
+
+	/* protocol unregister */
+	dev_remove_pack(&can_packet);
+	unregister_netdevice_notifier(&can_netdev_notifier);
+	sock_unregister(PF_CAN);
+
+	/* remove rx_dev_list */
+	spin_lock_bh(&rcv_lists_lock);
+	hlist_del(&rx_alldev_list.list);
+	hlist_for_each_entry_safe(d, n, next, &rx_dev_list, list) {
+		hlist_del(&d->list);
+		kfree(d);
+	}
+	spin_unlock_bh(&rcv_lists_lock);
+
+	kmem_cache_destroy(rcv_cache);
+}
+
+module_init(can_init);
+module_exit(can_exit);
Index: net-2.6/net/can/af_can.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/af_can.h	2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, the following disclaimer and
+ *    the referenced file 'COPYING'.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef AF_CAN_H
+#define AF_CAN_H
+
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <linux/can.h>
+
+/* af_can rx dispatcher structures */
+
+struct receiver {
+	struct hlist_node list;
+	struct rcu_head rcu;
+	canid_t can_id;
+	canid_t mask;
+	unsigned long matches;
+	void (*func)(struct sk_buff *, void *);
+	void *data;
+	char *ident;
+};
+
+enum { RX_ERR, RX_ALL, RX_FIL, RX_INV, RX_EFF, RX_MAX };
+
+struct dev_rcv_lists {
+	struct hlist_node list;
+	struct rcu_head rcu;
+	struct net_device *dev;
+	struct hlist_head rx[RX_MAX];
+	struct hlist_head rx_sff[0x800];
+	int remove_on_zero_entries;
+	int entries;
+};
+
+/* statistic structures */
+
+struct s_stats {
+	unsigned long jiffies_init;
+
+	unsigned long rx_frames;
+	unsigned long tx_frames;
+	unsigned long matches;
+
+	unsigned long total_rx_rate;
+	unsigned long total_tx_rate;
+	unsigned long total_rx_match_ratio;
+
+	unsigned long current_rx_rate;
+	unsigned long current_tx_rate;
+	unsigned long current_rx_match_ratio;
+
+	unsigned long max_rx_rate;
+	unsigned long max_tx_rate;
+	unsigned long max_rx_match_ratio;
+
+	unsigned long rx_frames_delta;
+	unsigned long tx_frames_delta;
+	unsigned long matches_delta;
+}; /* can be reset e.g. by can_init_stats() */
+
+struct s_pstats {
+	unsigned long stats_reset;
+	unsigned long user_reset;
+	unsigned long rcv_entries;
+	unsigned long rcv_entries_max;
+}; /* persistent statistics */
+
+/* function prototypes for the CAN networklayer procfs (proc.c) */
+extern void can_init_proc(void);
+extern void can_remove_proc(void);
+extern void can_stat_update(unsigned long data);
+
+/* structures and variables from af_can.c needed in proc.c for reading */
+extern struct timer_list stattimer;	/* timer for statistics update */
+extern struct s_stats  stats;		/* packet statistics */
+extern struct s_pstats pstats;		/* receive list statistics */
+extern struct hlist_head rx_dev_list;	/* rx dispatcher structures */
+
+#endif /* AF_CAN_H */
Index: net-2.6/net/can/proc.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/proc.c	2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,531 @@
+/*
+ * proc.c - procfs support for Protocol family CAN core module
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, the following disclaimer and
+ *    the referenced file 'COPYING'.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <linux/rcupdate.h>
+#include <linux/can/core.h>
+
+#include "af_can.h"
+
+/*
+ * proc filenames for the PF_CAN core
+ */
+
+#define CAN_PROC_VERSION     "version"
+#define CAN_PROC_STATS       "stats"
+#define CAN_PROC_RESET_STATS "reset_stats"
+#define CAN_PROC_RCVLIST_ALL "rcvlist_all"
+#define CAN_PROC_RCVLIST_FIL "rcvlist_fil"
+#define CAN_PROC_RCVLIST_INV "rcvlist_inv"
+#define CAN_PROC_RCVLIST_SFF "rcvlist_sff"
+#define CAN_PROC_RCVLIST_EFF "rcvlist_eff"
+#define CAN_PROC_RCVLIST_ERR "rcvlist_err"
+
+static struct proc_dir_entry *can_dir;
+static struct proc_dir_entry *pde_version;
+static struct proc_dir_entry *pde_stats;
+static struct proc_dir_entry *pde_reset_stats;
+static struct proc_dir_entry *pde_rcvlist_all;
+static struct proc_dir_entry *pde_rcvlist_fil;
+static struct proc_dir_entry *pde_rcvlist_inv;
+static struct proc_dir_entry *pde_rcvlist_sff;
+static struct proc_dir_entry *pde_rcvlist_eff;
+static struct proc_dir_entry *pde_rcvlist_err;
+
+static int user_reset;
+
+static const char *rx_list_name[] = {
+	[RX_ERR] = "rx_err",
+	[RX_ALL] = "rx_all",
+	[RX_FIL] = "rx_fil",
+	[RX_INV] = "rx_inv",
+	[RX_EFF] = "rx_eff",
+};
+
+/*
+ * af_can statistics stuff
+ */
+
+static void can_init_stats(void)
+{
+	/*
+	 * This memset function is called from a timer context (when
+	 * stattimer is active which is the default) OR in a process
+	 * context (reading the proc_fs when stattimer is disabled).
+	 */
+	memset(&stats, 0, sizeof(stats));
+	stats.jiffies_init = jiffies;
+
+	pstats.stats_reset++;
+
+	if (user_reset) {
+		user_reset = 0;
+		pstats.user_reset++;
+	}
+}
+
+static unsigned long calc_rate(unsigned long oldjif, unsigned long newjif,
+			       unsigned long count)
+{
+	unsigned long ret = 0;
+
+	if (oldjif == newjif)
+		return 0;
+
+	/* see can_rcv() - this should NEVER happen! */
+	if (count > (ULONG_MAX / HZ)) {
+		printk(KERN_ERR "can: calc_rate: count exceeded! %ld\n",
+		       count);
+		return 99999999;
+	}
+
+	ret = (count * HZ) / (newjif - oldjif);
+
+	return ret;
+}
+
+void can_stat_update(unsigned long data)
+{
+	unsigned long j = jiffies; /* snapshot */
+
+	/* restart counting in timer context on user request */
+	if (user_reset)
+		can_init_stats();
+
+	/* restart counting on jiffies overflow */
+	if (j < stats.jiffies_init)
+		can_init_stats();
+
+	/* stats.rx_frames is the definitively max. statistic value */
+
+	/* prevent overflow in calc_rate() */
+	if (stats.rx_frames > (ULONG_MAX / HZ))
+		can_init_stats();
+
+	/* matches overflow - very improbable */
+	if (stats.matches > (ULONG_MAX / 100))
+		can_init_stats();
+
+	/* calc total values */
+	if (stats.rx_frames)
+		stats.total_rx_match_ratio = (stats.matches * 100) /
+						stats.rx_frames;
+
+	stats.total_tx_rate = calc_rate(stats.jiffies_init, j,
+					stats.tx_frames);
+	stats.total_rx_rate = calc_rate(stats.jiffies_init, j,
+					stats.rx_frames);
+
+	/* calc current values */
+	if (stats.rx_frames_delta)
+		stats.current_rx_match_ratio =
+			(stats.matches_delta * 100) / stats.rx_frames_delta;
+
+	stats.current_tx_rate = calc_rate(0, HZ, stats.tx_frames_delta);
+	stats.current_rx_rate = calc_rate(0, HZ, stats.rx_frames_delta);
+
+	/* check / update maximum values */
+	if (stats.max_tx_rate < stats.current_tx_rate)
+		stats.max_tx_rate = stats.current_tx_rate;
+
+	if (stats.max_rx_rate < stats.current_rx_rate)
+		stats.max_rx_rate = stats.current_rx_rate;
+
+	if (stats.max_rx_match_ratio < stats.current_rx_match_ratio)
+		stats.max_rx_match_ratio = stats.current_rx_match_ratio;
+
+	/* clear values for 'current rate' calculation */
+	stats.tx_frames_delta = 0;
+	stats.rx_frames_delta = 0;
+	stats.matches_delta   = 0;
+
+	/* restart timer (one second) */
+	stattimer.expires = jiffies + HZ;
+	add_timer(&stattimer);
+}
+
+/*
+ * proc read functions
+ *
+ * From known use-cases we expect about 10 entries in a receive list to be
+ * printed in the proc_fs. So PAGE_SIZE is definitely enough space here.
+ *
+ */
+
+static int can_print_rcvlist(char *page, int len, struct hlist_head *rx_list,
+			     struct net_device *dev)
+{
+	struct receiver *r;
+	struct hlist_node *n;
+
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(r, n, rx_list, list) {
+		char *fmt = (r->can_id & CAN_EFF_FLAG)?
+			"   %-5s  %08X  %08x  %08x  %08x  %8ld  %s\n" :
+			"   %-5s     %03X    %08x  %08lx  %08lx  %8ld  %s\n";
+
+		len += snprintf(page + len, PAGE_SIZE - len, fmt,
+				DNAME(dev), r->can_id, r->mask,
+				(unsigned long)r->func, (unsigned long)r->data,
+				r->matches, r->ident);
+
+		/* does a typical line fit into the current buffer? */
+
+		/* 100 Bytes before end of buffer */
+		if (len > PAGE_SIZE - 100) {
+			/* mark output cut off */
+			len += snprintf(page + len, PAGE_SIZE - len,
+					"   (..)\n");
+			break;
+		}
+	}
+	rcu_read_unlock();
+
+	return len;
+}
+
+static int can_print_recv_banner(char *page, int len)
+{
+	/*
+	 *                  can1.  00000000  00000000  00000000
+	 *                 .......          0  tp20
+	 */
+	len += snprintf(page + len, PAGE_SIZE - len,
+			"  device   can_id   can_mask  function"
+			"  userdata   matches  ident\n");
+
+	return len;
+}
+
+static int can_proc_read_stats(char *page, char **start, off_t off,
+			       int count, int *eof, void *data)
+{
+	int len = 0;
+
+	len += snprintf(page + len, PAGE_SIZE - len, "\n");
+	len += snprintf(page + len, PAGE_SIZE - len,
+			" %8ld transmitted frames (TXF)\n", stats.tx_frames);
+	len += snprintf(page + len, PAGE_SIZE - len,
+			" %8ld received frames (RXF)\n", stats.rx_frames);
+	len += snprintf(page + len, PAGE_SIZE - len,
+			" %8ld matched frames (RXMF)\n", stats.matches);
+
+	len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+	if (stattimer.function == can_stat_update) {
+		len += snprintf(page + len, PAGE_SIZE - len,
+				" %8ld %% total match ratio (RXMR)\n",
+				stats.total_rx_match_ratio);
+
+		len += snprintf(page + len, PAGE_SIZE - len,
+				" %8ld frames/s total tx rate (TXR)\n",
+				stats.total_tx_rate);
+		len += snprintf(page + len, PAGE_SIZE - len,
+				" %8ld frames/s total rx rate (RXR)\n",
+				stats.total_rx_rate);
+
+		len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+		len += snprintf(page + len, PAGE_SIZE - len,
+				" %8ld %% current match ratio (CRXMR)\n",
+				stats.current_rx_match_ratio);
+
+		len += snprintf(page + len, PAGE_SIZE - len,
+				" %8ld frames/s current tx rate (CTXR)\n",
+				stats.current_tx_rate);
+		len += snprintf(page + len, PAGE_SIZE - len,
+				" %8ld frames/s current rx rate (CRXR)\n",
+				stats.current_rx_rate);
+
+		len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+		len += snprintf(page + len, PAGE_SIZE - len,
+				" %8ld %% max match ratio (MRXMR)\n",
+				stats.max_rx_match_ratio);
+
+		len += snprintf(page + len, PAGE_SIZE - len,
+				" %8ld frames/s max tx rate (MTXR)\n",
+				stats.max_tx_rate);
+		len += snprintf(page + len, PAGE_SIZE - len,
+				" %8ld frames/s max rx rate (MRXR)\n",
+				stats.max_rx_rate);
+
+		len += snprintf(page + len, PAGE_SIZE - len, "\n");
+	}
+
+	len += snprintf(page + len, PAGE_SIZE - len,
+			" %8ld current receive list entries (CRCV)\n",
+			pstats.rcv_entries);
+	len += snprintf(page + len, PAGE_SIZE - len,
+			" %8ld maximum receive list entries (MRCV)\n",
+			pstats.rcv_entries_max);
+
+	if (pstats.stats_reset)
+		len += snprintf(page + len, PAGE_SIZE - len,
+				"\n %8ld statistic resets (STR)\n",
+				pstats.stats_reset);
+
+	if (pstats.user_reset)
+		len += snprintf(page + len, PAGE_SIZE - len,
+				" %8ld user statistic resets (USTR)\n",
+				pstats.user_reset);
+
+	len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+	*eof = 1;
+	return len;
+}
+
+static int can_proc_read_reset_stats(char *page, char **start, off_t off,
+				     int count, int *eof, void *data)
+{
+	int len = 0;
+
+	user_reset = 1;
+
+	if (stattimer.function == can_stat_update) {
+		len += snprintf(page + len, PAGE_SIZE - len,
+				"Scheduled statistic reset #%ld.\n",
+				pstats.stats_reset + 1);
+
+	} else {
+		if (stats.jiffies_init != jiffies)
+			can_init_stats();
+
+		len += snprintf(page + len, PAGE_SIZE - len,
+				"Performed statistic reset #%ld.\n",
+				pstats.stats_reset);
+	}
+
+	*eof = 1;
+	return len;
+}
+
+static int can_proc_read_version(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	int len = 0;
+
+	len += snprintf(page + len, PAGE_SIZE - len, "%s\n",
+			CAN_VERSION_STRING);
+	*eof = 1;
+	return len;
+}
+
+static int can_proc_read_rcvlist(char *page, char **start, off_t off,
+				 int count, int *eof, void *data)
+{
+	/* double cast to prevent GCC warning */
+	int idx = (int)(long)data;
+	int len = 0;
+	struct dev_rcv_lists *d;
+	struct hlist_node *n;
+
+	len += snprintf(page + len, PAGE_SIZE - len,
+			"\nreceive list '%s':\n", rx_list_name[idx]);
+
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+
+		if (!hlist_empty(&d->rx[idx])) {
+			len = can_print_recv_banner(page, len);
+			len = can_print_rcvlist(page, len, &d->rx[idx], d->dev);
+		} else
+			len += snprintf(page + len, PAGE_SIZE - len,
+					"  (%s: no entry)\n", DNAME(d->dev));
+
+		/* exit on end of buffer? */
+		if (len > PAGE_SIZE - 100)
+			break;
+	}
+	rcu_read_unlock();
+
+	len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+	*eof = 1;
+	return len;
+}
+
+static int can_proc_read_rcvlist_sff(char *page, char **start, off_t off,
+				     int count, int *eof, void *data)
+{
+	int len = 0;
+	struct dev_rcv_lists *d;
+	struct hlist_node *n;
+
+	/* RX_SFF */
+	len += snprintf(page + len, PAGE_SIZE - len,
+			"\nreceive list 'rx_sff':\n");
+
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(d, n, &rx_dev_list, list) {
+		int i, all_empty = 1;
+		/* check wether at least one list is non-empty */
+		for (i = 0; i < 0x800; i++)
+			if (!hlist_empty(&d->rx_sff[i])) {
+				all_empty = 0;
+				break;
+			}
+
+		if (!all_empty) {
+			len = can_print_recv_banner(page, len);
+			for (i = 0; i < 0x800; i++) {
+				if (!hlist_empty(&d->rx_sff[i]) &&
+				    len < PAGE_SIZE - 100)
+					len = can_print_rcvlist(page, len,
+								&d->rx_sff[i],
+								d->dev);
+			}
+		} else
+			len += snprintf(page + len, PAGE_SIZE - len,
+					"  (%s: no entry)\n", DNAME(d->dev));
+
+		/* exit on end of buffer? */
+		if (len > PAGE_SIZE - 100)
+			break;
+	}
+	rcu_read_unlock();
+
+	len += snprintf(page + len, PAGE_SIZE - len, "\n");
+
+	*eof = 1;
+	return len;
+}
+
+/*
+ * proc utility functions
+ */
+
+static struct proc_dir_entry *can_create_proc_readentry(const char *name,
+							mode_t mode,
+							read_proc_t *read_proc,
+							void *data)
+{
+	if (can_dir)
+		return create_proc_read_entry(name, mode, can_dir, read_proc,
+					      data);
+	else
+		return NULL;
+}
+
+static void can_remove_proc_readentry(const char *name)
+{
+	if (can_dir)
+		remove_proc_entry(name, can_dir);
+}
+
+/*
+ * can_init_proc - create main CAN proc directory and procfs entries
+ */
+void can_init_proc(void)
+{
+	/* create /proc/net/can directory */
+	can_dir = proc_mkdir(CAN_PROC_DIR, NULL);
+
+	if (!can_dir) {
+		printk(KERN_INFO "can: failed to create /proc/%s . "
+		       "CONFIG_PROC_FS missing?\n", CAN_PROC_DIR);
+		return;
+	}
+
+	can_dir->owner = THIS_MODULE;
+
+	/* own procfs entries from the AF_CAN core */
+	pde_version     = can_create_proc_readentry(CAN_PROC_VERSION, 0644,
+					can_proc_read_version, NULL);
+	pde_stats       = can_create_proc_readentry(CAN_PROC_STATS, 0644,
+					can_proc_read_stats, NULL);
+	pde_reset_stats = can_create_proc_readentry(CAN_PROC_RESET_STATS, 0644,
+					can_proc_read_reset_stats, NULL);
+	pde_rcvlist_err = can_create_proc_readentry(CAN_PROC_RCVLIST_ERR, 0644,
+					can_proc_read_rcvlist, (void *)RX_ERR);
+	pde_rcvlist_all = can_create_proc_readentry(CAN_PROC_RCVLIST_ALL, 0644,
+					can_proc_read_rcvlist, (void *)RX_ALL);
+	pde_rcvlist_fil = can_create_proc_readentry(CAN_PROC_RCVLIST_FIL, 0644,
+					can_proc_read_rcvlist, (void *)RX_FIL);
+	pde_rcvlist_inv = can_create_proc_readentry(CAN_PROC_RCVLIST_INV, 0644,
+					can_proc_read_rcvlist, (void *)RX_INV);
+	pde_rcvlist_eff = can_create_proc_readentry(CAN_PROC_RCVLIST_EFF, 0644,
+					can_proc_read_rcvlist, (void *)RX_EFF);
+	pde_rcvlist_sff = can_create_proc_readentry(CAN_PROC_RCVLIST_SFF, 0644,
+					can_proc_read_rcvlist_sff, NULL);
+}
+
+/*
+ * can_remove_proc - remove procfs entries and main CAN proc directory
+ */
+void can_remove_proc(void)
+{
+	if (pde_version)
+		can_remove_proc_readentry(CAN_PROC_VERSION);
+
+	if (pde_stats)
+		can_remove_proc_readentry(CAN_PROC_STATS);
+
+	if (pde_reset_stats)
+		can_remove_proc_readentry(CAN_PROC_RESET_STATS);
+
+	if (pde_rcvlist_err)
+		can_remove_proc_readentry(CAN_PROC_RCVLIST_ERR);
+
+	if (pde_rcvlist_all)
+		can_remove_proc_readentry(CAN_PROC_RCVLIST_ALL);
+
+	if (pde_rcvlist_fil)
+		can_remove_proc_readentry(CAN_PROC_RCVLIST_FIL);
+
+	if (pde_rcvlist_inv)
+		can_remove_proc_readentry(CAN_PROC_RCVLIST_INV);
+
+	if (pde_rcvlist_eff)
+		can_remove_proc_readentry(CAN_PROC_RCVLIST_EFF);
+
+	if (pde_rcvlist_sff)
+		can_remove_proc_readentry(CAN_PROC_RCVLIST_SFF);
+
+	if (can_dir)
+		remove_proc_entry(CAN_PROC_DIR, NULL);
+}
Index: net-2.6/include/linux/can/error.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/include/linux/can/error.h	2007-08-03 11:21:46.000000000 +0200
@@ -0,0 +1,93 @@
+/*
+ * linux/can/error.h
+ *
+ * Definitions of the CAN error frame to be filtered and passed to the user.
+ *
+ * Author: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_ERROR_H
+#define CAN_ERROR_H
+
+#define CAN_ERR_DLC 8 /* dlc for error frames */
+
+/* error class (mask) in can_id */
+#define CAN_ERR_TX_TIMEOUT   0x00000001U /* TX timeout (by netdevice driver) */
+#define CAN_ERR_LOSTARB      0x00000002U /* lost arbitration    / data[0]    */
+#define CAN_ERR_CRTL         0x00000004U /* controller problems / data[1]    */
+#define CAN_ERR_PROT         0x00000008U /* protocol violations / data[2..3] */
+#define CAN_ERR_TRX          0x00000010U /* transceiver status  / data[4]    */
+#define CAN_ERR_ACK          0x00000020U /* received no ACK on transmission */
+#define CAN_ERR_BUSOFF       0x00000040U /* bus off */
+#define CAN_ERR_BUSERROR     0x00000080U /* bus error (may flood!) */
+#define CAN_ERR_RESTARTED    0x00000100U /* controller restarted */
+
+/* arbitration lost in bit ... / data[0] */
+#define CAN_ERR_LOSTARB_UNSPEC   0x00 /* unspecified */
+				      /* else bit number in bitstream */
+
+/* error status of CAN-controller / data[1] */
+#define CAN_ERR_CRTL_UNSPEC      0x00 /* unspecified */
+#define CAN_ERR_CRTL_RX_OVERFLOW 0x01 /* RX buffer overflow */
+#define CAN_ERR_CRTL_TX_OVERFLOW 0x02 /* TX buffer overflow */
+#define CAN_ERR_CRTL_RX_WARNING  0x04 /* reached warning level for RX errors */
+#define CAN_ERR_CRTL_TX_WARNING  0x08 /* reached warning level for TX errors */
+#define CAN_ERR_CRTL_RX_PASSIVE  0x10 /* reached error passive status RX */
+#define CAN_ERR_CRTL_TX_PASSIVE  0x20 /* reached error passive status TX */
+				      /* (at least one error counter exceeds */
+				      /* the protocol-defined level of 127)  */
+
+/* error in CAN protocol (type) / data[2] */
+#define CAN_ERR_PROT_UNSPEC      0x00 /* unspecified */
+#define CAN_ERR_PROT_BIT         0x01 /* single bit error */
+#define CAN_ERR_PROT_FORM        0x02 /* frame format error */
+#define CAN_ERR_PROT_STUFF       0x04 /* bit stuffing error */
+#define CAN_ERR_PROT_BIT0        0x08 /* unable to send dominant bit */
+#define CAN_ERR_PROT_BIT1        0x10 /* unable to send recessive bit */
+#define CAN_ERR_PROT_OVERLOAD    0x20 /* bus overload */
+#define CAN_ERR_PROT_ACTIVE      0x40 /* active error announcement */
+#define CAN_ERR_PROT_TX          0x80 /* error occured on transmission */
+
+/* error in CAN protocol (location) / data[3] */
+#define CAN_ERR_PROT_LOC_UNSPEC  0x00 /* unspecified */
+#define CAN_ERR_PROT_LOC_SOF     0x03 /* start of frame */
+#define CAN_ERR_PROT_LOC_ID28_21 0x02 /* ID bits 28 - 21 (SFF: 10 - 3) */
+#define CAN_ERR_PROT_LOC_ID20_18 0x06 /* ID bits 20 - 18 (SFF: 2 - 0 )*/
+#define CAN_ERR_PROT_LOC_SRTR    0x04 /* substitute RTR (SFF: RTR) */
+#define CAN_ERR_PROT_LOC_IDE     0x05 /* identifier extension */
+#define CAN_ERR_PROT_LOC_ID17_13 0x07 /* ID bits 17-13 */
+#define CAN_ERR_PROT_LOC_ID12_05 0x0F /* ID bits 12-5 */
+#define CAN_ERR_PROT_LOC_ID04_00 0x0E /* ID bits 4-0 */
+#define CAN_ERR_PROT_LOC_RTR     0x0C /* RTR */
+#define CAN_ERR_PROT_LOC_RES1    0x0D /* reserved bit 1 */
+#define CAN_ERR_PROT_LOC_RES0    0x09 /* reserved bit 0 */
+#define CAN_ERR_PROT_LOC_DLC     0x0B /* data length code */
+#define CAN_ERR_PROT_LOC_DATA    0x0A /* data section */
+#define CAN_ERR_PROT_LOC_CRC_SEQ 0x08 /* CRC sequence */
+#define CAN_ERR_PROT_LOC_CRC_DEL 0x18 /* CRC delimiter */
+#define CAN_ERR_PROT_LOC_ACK     0x19 /* ACK slot */
+#define CAN_ERR_PROT_LOC_ACK_DEL 0x1B /* ACK delimiter */
+#define CAN_ERR_PROT_LOC_EOF     0x1A /* end of frame */
+#define CAN_ERR_PROT_LOC_INTERM  0x12 /* intermission */
+
+/* error status of CAN-transceiver / data[4] */
+/*                                             CANH CANL */
+#define CAN_ERR_TRX_UNSPEC             0x00 /* 0000 0000 */
+#define CAN_ERR_TRX_CANH_NO_WIRE       0x04 /* 0000 0100 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_BAT  0x05 /* 0000 0101 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_VCC  0x06 /* 0000 0110 */
+#define CAN_ERR_TRX_CANH_SHORT_TO_GND  0x07 /* 0000 0111 */
+#define CAN_ERR_TRX_CANL_NO_WIRE       0x40 /* 0100 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_BAT  0x50 /* 0101 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_VCC  0x60 /* 0110 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_GND  0x70 /* 0111 0000 */
+#define CAN_ERR_TRX_CANL_SHORT_TO_CANH 0x80 /* 1000 0000 */
+
+/* controller specific additional information / data[5..7] */
+
+#endif /* CAN_ERROR_H */

--

^ permalink raw reply

* [patch 1/7] CAN: Allocate protocol numbers for PF_CAN
From: Urs Thuermann @ 2007-08-04  2:06 UTC (permalink / raw)
  To: netdev; +Cc: urs, oliver, davem, kaber, urs.thuermann, oliver.hartkopp
In-Reply-To: <20070804020657.25572.0@janus.isnogud.escape.de>

[-- Attachment #1: 01-can-proto-numbers.diff --]
[-- Type: text/plain, Size: 4694 bytes --]

This patch adds a protocol/address family number, ARP hardware type,
ethernet packet type, and a line discipline number for the SocketCAN
implementation.

Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>

---
 include/linux/if_arp.h   |    1 +
 include/linux/if_ether.h |    1 +
 include/linux/socket.h   |    2 ++
 include/linux/tty.h      |    3 ++-
 net/core/sock.c          |    4 ++--
 5 files changed, 8 insertions(+), 3 deletions(-)

Index: net-2.6/include/linux/if_arp.h
===================================================================
--- net-2.6.orig/include/linux/if_arp.h	2007-08-03 11:21:32.000000000 +0200
+++ net-2.6/include/linux/if_arp.h	2007-08-03 11:21:42.000000000 +0200
@@ -52,6 +52,7 @@
 #define ARPHRD_ROSE	270
 #define ARPHRD_X25	271		/* CCITT X.25			*/
 #define ARPHRD_HWX25	272		/* Boards with X.25 in firmware	*/
+#define ARPHRD_CAN	280		/* Controller Area Network      */
 #define ARPHRD_PPP	512
 #define ARPHRD_CISCO	513		/* Cisco HDLC	 		*/
 #define ARPHRD_HDLC	ARPHRD_CISCO
Index: net-2.6/include/linux/if_ether.h
===================================================================
--- net-2.6.orig/include/linux/if_ether.h	2007-08-03 11:21:32.000000000 +0200
+++ net-2.6/include/linux/if_ether.h	2007-08-03 11:21:42.000000000 +0200
@@ -90,6 +90,7 @@
 #define ETH_P_WAN_PPP   0x0007          /* Dummy type for WAN PPP frames*/
 #define ETH_P_PPP_MP    0x0008          /* Dummy type for PPP MP frames */
 #define ETH_P_LOCALTALK 0x0009		/* Localtalk pseudo type 	*/
+#define ETH_P_CAN	0x000C		/* Controller Area Network      */
 #define ETH_P_PPPTALK	0x0010		/* Dummy type for Atalk over PPP*/
 #define ETH_P_TR_802_2	0x0011		/* 802.2 frames 		*/
 #define ETH_P_MOBITEX	0x0015		/* Mobitex (kaz@cafe.net)	*/
Index: net-2.6/include/linux/socket.h
===================================================================
--- net-2.6.orig/include/linux/socket.h	2007-08-03 11:21:32.000000000 +0200
+++ net-2.6/include/linux/socket.h	2007-08-03 11:21:42.000000000 +0200
@@ -185,6 +185,7 @@
 #define AF_PPPOX	24	/* PPPoX sockets		*/
 #define AF_WANPIPE	25	/* Wanpipe API Sockets */
 #define AF_LLC		26	/* Linux LLC			*/
+#define AF_CAN		29	/* Controller Area Network      */
 #define AF_TIPC		30	/* TIPC sockets			*/
 #define AF_BLUETOOTH	31	/* Bluetooth sockets 		*/
 #define AF_IUCV		32	/* IUCV sockets			*/
@@ -220,6 +221,7 @@
 #define PF_PPPOX	AF_PPPOX
 #define PF_WANPIPE	AF_WANPIPE
 #define PF_LLC		AF_LLC
+#define PF_CAN		AF_CAN
 #define PF_TIPC		AF_TIPC
 #define PF_BLUETOOTH	AF_BLUETOOTH
 #define PF_IUCV		AF_IUCV
Index: net-2.6/include/linux/tty.h
===================================================================
--- net-2.6.orig/include/linux/tty.h	2007-08-03 11:21:32.000000000 +0200
+++ net-2.6/include/linux/tty.h	2007-08-03 11:21:42.000000000 +0200
@@ -24,7 +24,7 @@
 #define NR_PTYS	CONFIG_LEGACY_PTY_COUNT   /* Number of legacy ptys */
 #define NR_UNIX98_PTY_DEFAULT	4096      /* Default maximum for Unix98 ptys */
 #define NR_UNIX98_PTY_MAX	(1 << MINORBITS) /* Absolute limit */
-#define NR_LDISCS		17
+#define NR_LDISCS		18
 
 /* line disciplines */
 #define N_TTY		0
@@ -45,6 +45,7 @@
 #define N_SYNC_PPP	14	/* synchronous PPP */
 #define N_HCI		15	/* Bluetooth HCI UART */
 #define N_GIGASET_M101	16	/* Siemens Gigaset M101 serial DECT adapter */
+#define N_SLCAN		17	/* Serial / USB serial CAN Adaptors */
 
 /*
  * This character is the same as _POSIX_VDISABLE: it cannot be used as
Index: net-2.6/net/core/sock.c
===================================================================
--- net-2.6.orig/net/core/sock.c	2007-08-03 11:21:32.000000000 +0200
+++ net-2.6/net/core/sock.c	2007-08-03 11:21:42.000000000 +0200
@@ -153,7 +153,7 @@
   "sk_lock-AF_ASH"   , "sk_lock-AF_ECONET"   , "sk_lock-AF_ATMSVC"   ,
   "sk_lock-21"       , "sk_lock-AF_SNA"      , "sk_lock-AF_IRDA"     ,
   "sk_lock-AF_PPPOX" , "sk_lock-AF_WANPIPE"  , "sk_lock-AF_LLC"      ,
-  "sk_lock-27"       , "sk_lock-28"          , "sk_lock-29"          ,
+  "sk_lock-27"       , "sk_lock-28"          , "sk_lock-AF_CAN"      ,
   "sk_lock-AF_TIPC"  , "sk_lock-AF_BLUETOOTH", "sk_lock-IUCV"        ,
   "sk_lock-AF_RXRPC" , "sk_lock-AF_MAX"
 };
@@ -167,7 +167,7 @@
   "slock-AF_ASH"   , "slock-AF_ECONET"   , "slock-AF_ATMSVC"   ,
   "slock-21"       , "slock-AF_SNA"      , "slock-AF_IRDA"     ,
   "slock-AF_PPPOX" , "slock-AF_WANPIPE"  , "slock-AF_LLC"      ,
-  "slock-27"       , "slock-28"          , "slock-29"          ,
+  "slock-27"       , "slock-28"          , "slock-AF_CAN"      ,
   "slock-AF_TIPC"  , "slock-AF_BLUETOOTH", "slock-AF_IUCV"     ,
   "slock-AF_RXRPC" , "slock-AF_MAX"
 };

--

^ permalink raw reply

* [patch 3/7] CAN: Add raw protocol
From: Urs Thuermann @ 2007-08-04  2:07 UTC (permalink / raw)
  To: netdev; +Cc: urs, oliver, davem, kaber, urs.thuermann, oliver.hartkopp
In-Reply-To: <20070804020657.25572.0@janus.isnogud.escape.de>

[-- Attachment #1: 03-can-raw-proto.diff --]
[-- Type: text/plain, Size: 21646 bytes --]

This patch adds the CAN raw protocol.

Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>

---
 include/linux/can/raw.h |   31 +
 net/can/Kconfig         |   26 +
 net/can/Makefile        |    3 
 net/can/raw.c           |  757 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 817 insertions(+)

Index: net-2.6/include/linux/can/raw.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/include/linux/can/raw.h	2007-08-03 11:21:48.000000000 +0200
@@ -0,0 +1,31 @@
+/*
+ * linux/can/raw.h
+ *
+ * Definitions for raw CAN sockets
+ *
+ * Authors: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
+ *          Urs Thuermann   <urs.thuermann@volkswagen.de>
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#ifndef CAN_RAW_H
+#define CAN_RAW_H
+
+#include <linux/can.h>
+
+#define SOL_CAN_RAW (SOL_CAN_BASE + CAN_RAW)
+
+/* for socket options affecting the socket (not the global system) */
+
+enum {
+	CAN_RAW_FILTER = 1,	/* set 0 .. n can_filter(s)          */
+	CAN_RAW_ERR_FILTER,	/* set filter for error frames       */
+	CAN_RAW_LOOPBACK,	/* local loopback (default:on)       */
+	CAN_RAW_RECV_OWN_MSGS	/* receive my own msgs (default:off) */
+};
+
+#endif
Index: net-2.6/net/can/Kconfig
===================================================================
--- net-2.6.orig/net/can/Kconfig	2007-08-03 11:21:46.000000000 +0200
+++ net-2.6/net/can/Kconfig	2007-08-03 11:21:48.000000000 +0200
@@ -16,6 +16,32 @@
 	  If you want CAN support, you should say Y here and also to the
 	  specific driver for your controller(s) below.
 
+config CAN_RAW
+	tristate "Raw CAN Protocol (raw access with CAN-ID filtering)"
+	depends on CAN
+	default N
+	---help---
+	  The Raw CAN protocol option offers access to the CAN bus via
+	  the BSD socket API. You probably want to use the raw socket in
+	  most cases where no higher level protocol is being used. The raw
+	  socket has several filter options e.g. ID-Masking / Errorframes.
+	  To receive/send raw CAN messages, use AF_CAN with protocol CAN_RAW.
+
+config CAN_RAW_USER
+	bool "Allow non-root users to access Raw CAN Protocol sockets"
+	depends on CAN_RAW
+	default N
+	---help---
+	  The Controller Area Network is a local field bus transmitting only
+	  broadcast messages without any routing and security concepts.
+	  In the majority of cases the user application has to deal with
+	  raw CAN frames. Therefore it might be reasonable NOT to restrict
+	  the CAN access only to the user root, as known from other networks.
+	  Since CAN_RAW sockets can only send and receive frames to/from CAN
+	  interfaces this does not affect security of others networks.
+	  Say Y here if you want non-root users to be able to access CAN_RAW
+	  sockets.
+
 config CAN_DEBUG_CORE
 	bool "CAN Core debugging messages"
 	depends on CAN
Index: net-2.6/net/can/Makefile
===================================================================
--- net-2.6.orig/net/can/Makefile	2007-08-03 11:21:46.000000000 +0200
+++ net-2.6/net/can/Makefile	2007-08-03 11:21:48.000000000 +0200
@@ -4,3 +4,6 @@
 
 obj-$(CONFIG_CAN)	+= can.o
 can-objs		:= af_can.o proc.o
+
+obj-$(CONFIG_CAN_RAW)	+= can-raw.o
+can-raw-objs		:= raw.o
Index: net-2.6/net/can/raw.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/net/can/raw.c	2007-08-03 11:21:48.000000000 +0200
@@ -0,0 +1,757 @@
+/*
+ * raw.c - Raw sockets for protocol family CAN
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, the following disclaimer and
+ *    the referenced file 'COPYING'.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/uio.h>
+#include <linux/poll.h>
+#include <linux/net.h>
+#include <linux/netdevice.h>
+#include <linux/socket.h>
+#include <linux/if_arp.h>
+#include <linux/skbuff.h>
+#include <linux/can.h>
+#include <linux/can/core.h>
+#include <linux/can/raw.h>
+#include <net/sock.h>
+
+#define IDENT "raw"
+#define CAN_RAW_VERSION CAN_VERSION
+static __initdata const char banner[] =
+	KERN_INFO "can: raw protocol (rev " CAN_RAW_VERSION ")\n";
+
+MODULE_DESCRIPTION("PF_CAN raw protocol");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
+
+#ifdef CONFIG_CAN_DEBUG_CORE
+static int debug;
+module_param(debug, int, S_IRUGO);
+MODULE_PARM_DESC(debug, "debug print mask: 1:debug, 2:frames, 4:skbs");
+#endif
+
+#ifdef CONFIG_CAN_RAW_USER
+#define RAW_CAP (-1)
+#else
+#define RAW_CAP CAP_NET_RAW
+#endif
+
+#define MASK_ALL 0
+
+/*
+ * A raw socket has a list of can_filters attached to it, each receiving
+ * the CAN frames matching that filter.  If the filter list is empty,
+ * no CAN frames will be received by the socket.  The default after
+ * opening the socket, is to have one filter which receives all frames.
+ * The filter list is allocated dynamically with the exception of the
+ * list containing only one item.  This common case is optimized by
+ * storing the single filter in dfilter, to avoid using dynamic memory.
+ */
+
+struct raw_sock {
+	struct sock sk;
+	int bound;
+	int ifindex;
+	struct notifier_block notifier;
+	int loopback;
+	int recv_own_msgs;
+	int count;                 /* number of active filters */
+	struct can_filter dfilter; /* default/single filter */
+	struct can_filter *filter; /* pointer to filter(s) */
+	can_err_mask_t err_mask;
+};
+
+static inline struct raw_sock *raw_sk(const struct sock *sk)
+{
+	return (struct raw_sock *)sk;
+}
+
+static void raw_rcv(struct sk_buff *skb, void *data)
+{
+	struct sock *sk = (struct sock *)data;
+	struct raw_sock *ro = raw_sk(sk);
+	struct sockaddr_can *addr;
+	int error;
+
+	DBG("received skbuff %p, sk %p\n", skb, sk);
+	DBG_SKB(skb);
+
+	if (!ro->recv_own_msgs) {
+		/* check the received tx sock reference */
+		if (skb->sk == sk) {
+			DBG("trashed own tx msg\n");
+			kfree_skb(skb);
+			return;
+		}
+	}
+
+	addr = (struct sockaddr_can *)skb->cb;
+	memset(addr, 0, sizeof(*addr));
+	addr->can_family  = AF_CAN;
+	addr->can_ifindex = skb->dev->ifindex;
+
+	error = sock_queue_rcv_skb(sk, skb);
+	if (error < 0) {
+		DBG("sock_queue_rcv_skb failed: %d\n", error);
+		DBG("freeing skbuff %p\n", skb);
+		kfree_skb(skb);
+	}
+}
+
+static void raw_enable_filters(struct net_device *dev, struct sock *sk)
+{
+	struct raw_sock *ro = raw_sk(sk);
+	struct can_filter *filter = ro->filter;
+	int i;
+
+	for (i = 0; i < ro->count; i++) {
+		DBG("filter can_id %08X, can_mask %08X%s, sk %p\n",
+		    filter[i].can_id, filter[i].can_mask,
+		    filter[i].can_id & CAN_INV_FILTER ? " (inv)" : "", sk);
+
+		can_rx_register(dev, filter[i].can_id, filter[i].can_mask,
+				raw_rcv, sk, IDENT);
+	}
+}
+
+static void raw_enable_errfilter(struct net_device *dev, struct sock *sk)
+{
+	struct raw_sock *ro = raw_sk(sk);
+
+	if (ro->err_mask)
+		can_rx_register(dev, 0, ro->err_mask | CAN_ERR_FLAG,
+				raw_rcv, sk, IDENT);
+}
+
+static void raw_disable_filters(struct net_device *dev, struct sock *sk)
+{
+	struct raw_sock *ro = raw_sk(sk);
+	struct can_filter *filter = ro->filter;
+	int i;
+
+	for (i = 0; i < ro->count; i++) {
+		DBG("filter can_id %08X, can_mask %08X%s, sk %p\n",
+		    filter[i].can_id, filter[i].can_mask,
+		    filter[i].can_id & CAN_INV_FILTER ? " (inv)" : "", sk);
+
+		can_rx_unregister(dev, filter[i].can_id, filter[i].can_mask,
+				  raw_rcv, sk);
+	}
+}
+
+static void raw_disable_errfilter(struct net_device *dev, struct sock *sk)
+{
+	struct raw_sock *ro = raw_sk(sk);
+
+	if (ro->err_mask)
+		can_rx_unregister(dev, 0, ro->err_mask | CAN_ERR_FLAG,
+				  raw_rcv, sk);
+}
+
+static int raw_notifier(struct notifier_block *nb,
+			unsigned long msg, void *data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct raw_sock *ro = container_of(nb, struct raw_sock, notifier);
+	struct sock *sk = &ro->sk;
+
+	DBG("msg %ld for dev %p (%s idx %d) sk %p ro->ifindex %d\n",
+	    msg, dev, dev->name, dev->ifindex, sk, ro->ifindex);
+
+	if (dev->type != ARPHRD_CAN)
+		return NOTIFY_DONE;
+
+	if (ro->ifindex != dev->ifindex)
+		return NOTIFY_DONE;
+
+	switch (msg) {
+
+	case NETDEV_UNREGISTER:
+		lock_sock(sk);
+		/* remove current filters & unregister */
+		if (ro->bound) {
+			raw_disable_filters(dev, sk);
+			raw_disable_errfilter(dev, sk);
+		}
+
+		if (ro->count > 1)
+			kfree(ro->filter);
+
+		ro->ifindex = 0;
+		ro->bound   = 0;
+		ro->count   = 0;
+		release_sock(sk);
+
+		sk->sk_err = ENODEV;
+		if (!sock_flag(sk, SOCK_DEAD))
+			sk->sk_error_report(sk);
+		break;
+
+	case NETDEV_DOWN:
+		sk->sk_err = ENETDOWN;
+		if (!sock_flag(sk, SOCK_DEAD))
+			sk->sk_error_report(sk);
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static int raw_init(struct sock *sk)
+{
+	struct raw_sock *ro = raw_sk(sk);
+
+	ro->bound            = 0;
+	ro->ifindex          = 0;
+
+	/* set default filter to single entry dfilter */
+	ro->dfilter.can_id   = 0;
+	ro->dfilter.can_mask = MASK_ALL;
+	ro->filter           = &ro->dfilter;
+	ro->count            = 1;
+
+	/* set default loopback behaviour */
+	ro->loopback         = 1;
+	ro->recv_own_msgs    = 0;
+
+	/* set notifier */
+	ro->notifier.notifier_call = raw_notifier;
+
+	register_netdevice_notifier(&ro->notifier);
+
+	return 0;
+}
+
+static int raw_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+	struct raw_sock *ro = raw_sk(sk);
+
+	DBG("socket %p, sk %p, refcnt %d\n", sock, sk,
+	    atomic_read(&sk->sk_refcnt));
+
+	unregister_netdevice_notifier(&ro->notifier);
+
+	lock_sock(sk);
+
+	/* remove current filters & unregister */
+	if (ro->bound) {
+		if (ro->ifindex) {
+			struct net_device *dev = dev_get_by_index(ro->ifindex);
+			if (dev) {
+				raw_disable_filters(dev, sk);
+				raw_disable_errfilter(dev, sk);
+				dev_put(dev);
+			}
+		} else {
+			raw_disable_filters(NULL, sk);
+			raw_disable_errfilter(NULL, sk);
+		}
+	}
+
+	if (ro->count > 1)
+		kfree(ro->filter);
+
+	ro->ifindex = 0;
+	ro->bound   = 0;
+	ro->count   = 0;
+
+	release_sock(sk);
+	sock_put(sk);
+
+	return 0;
+}
+
+static int raw_bind(struct socket *sock, struct sockaddr *uaddr, int len)
+{
+	struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+	struct sock *sk = sock->sk;
+	struct raw_sock *ro = raw_sk(sk);
+	int err = 0;
+	int notify_enetdown = 0;
+
+	DBG("socket %p to device %d\n", sock, addr->can_ifindex);
+
+	if (len < sizeof(*addr))
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (ro->bound) {
+		/* unregister current filters for this device */
+		if (ro->ifindex) {
+			struct net_device *dev = dev_get_by_index(ro->ifindex);
+			if (dev) {
+				raw_disable_filters(dev, sk);
+				raw_disable_errfilter(dev, sk);
+				dev_put(dev);
+			}
+			ro->ifindex = 0;
+
+		} else {
+			raw_disable_filters(NULL, sk);
+			raw_disable_errfilter(NULL, sk);
+		}
+
+		ro->bound = 0;
+	}
+
+	if (addr->can_ifindex) {
+		struct net_device *dev = dev_get_by_index(addr->can_ifindex);
+		if (!dev) {
+			DBG("could not find device %d\n", addr->can_ifindex);
+			err = -ENODEV;
+			goto out;
+		}
+		if (dev->type != ARPHRD_CAN) {
+			DBG("device %d no CAN device\n", addr->can_ifindex);
+			dev_put(dev);
+			err = -ENODEV;
+			goto out;
+		}
+		if (!(dev->flags & IFF_UP))
+			notify_enetdown = 1;
+
+		ro->ifindex = dev->ifindex;
+
+		/* filters set by default/setsockopt */
+		raw_enable_filters(dev, sk);
+		raw_enable_errfilter(dev, sk);
+		dev_put(dev);
+
+	} else {
+		ro->ifindex = 0;
+
+		/* filters set by default/setsockopt */
+		raw_enable_filters(NULL, sk);
+		raw_enable_errfilter(NULL, sk);
+	}
+
+	ro->bound = 1;
+
+ out:
+	release_sock(sk);
+
+	if (notify_enetdown) {
+		sk->sk_err = ENETDOWN;
+		if (!sock_flag(sk, SOCK_DEAD))
+			sk->sk_error_report(sk);
+	}
+
+	return err;
+}
+
+static int raw_getname(struct socket *sock, struct sockaddr *uaddr,
+		       int *len, int peer)
+{
+	struct sockaddr_can *addr = (struct sockaddr_can *)uaddr;
+	struct sock *sk = sock->sk;
+	struct raw_sock *ro = raw_sk(sk);
+
+	if (peer)
+		return -EOPNOTSUPP;
+
+	addr->can_family  = AF_CAN;
+	addr->can_ifindex = ro->ifindex;
+
+	*len = sizeof(*addr);
+
+	return 0;
+}
+
+static unsigned int raw_poll(struct file *file, struct socket *sock,
+			     poll_table *wait)
+{
+	unsigned int mask = 0;
+
+	DBG("socket %p\n", sock);
+
+	mask = datagram_poll(file, sock, wait);
+	return mask;
+}
+
+static int raw_setsockopt(struct socket *sock, int level, int optname,
+			  char __user *optval, int optlen)
+{
+	struct sock *sk = sock->sk;
+	struct raw_sock *ro = raw_sk(sk);
+	struct can_filter *filter = NULL;  /* dyn. alloc'ed filters */
+	struct can_filter sfilter;         /* single filter */
+	struct net_device *dev = NULL;
+	can_err_mask_t err_mask = 0;
+	int count = 0;
+	int err;
+
+	if (level != SOL_CAN_RAW)
+		return -EINVAL;
+	if (optlen < 0)
+		return -EINVAL;
+
+	switch (optname) {
+
+	case CAN_RAW_FILTER:
+		if (optlen % sizeof(struct can_filter) != 0)
+			return -EINVAL;
+
+		count = optlen / sizeof(struct can_filter);
+
+		if (count > 1) {
+			/* filter does not fit into dfilter => alloc space */
+			filter = kmalloc(optlen, GFP_KERNEL);
+			if (!filter)
+				return -ENOMEM;
+
+			err = copy_from_user(filter, optval, optlen);
+			if (err) {
+				kfree(filter);
+				return err;
+			}
+		} else if (count == 1) {
+			err = copy_from_user(&sfilter, optval, optlen);
+			if (err)
+				return err;
+		}
+
+		lock_sock(sk);
+
+		if (ro->bound && ro->ifindex)
+			dev = dev_get_by_index(ro->ifindex);
+
+		/* remove current filters & unregister */
+		if (ro->bound)
+			raw_disable_filters(dev, sk);
+
+		if (ro->count > 1)
+			kfree(ro->filter);
+
+		if (count == 1) {
+			/* copy filter data for single filter */
+			ro->dfilter = sfilter;
+			filter = &ro->dfilter;
+		}
+
+		/* add new filters & register */
+		ro->filter = filter;
+		ro->count  = count;
+		if (ro->bound)
+			raw_enable_filters(dev, sk);
+
+		if (dev)
+			dev_put(dev);
+
+		release_sock(sk);
+
+		break;
+
+	case CAN_RAW_ERR_FILTER:
+		if (optlen != sizeof(err_mask))
+			return -EINVAL;
+
+		err = copy_from_user(&err_mask, optval, optlen);
+		if (err)
+			return err;
+
+		err_mask &= CAN_ERR_MASK;
+
+		lock_sock(sk);
+
+		if (ro->bound && ro->ifindex)
+			dev = dev_get_by_index(ro->ifindex);
+
+		/* remove current error mask */
+		if (ro->bound)
+			raw_disable_errfilter(dev, sk);
+
+		ro->err_mask = err_mask;
+
+		/* add new error mask */
+		if (ro->bound)
+			raw_enable_errfilter(dev, sk);
+
+		if (dev)
+			dev_put(dev);
+
+		release_sock(sk);
+
+		break;
+
+	case CAN_RAW_LOOPBACK:
+		if (optlen != sizeof(ro->loopback))
+			return -EINVAL;
+
+		err = copy_from_user(&ro->loopback, optval, optlen);
+		if (err)
+			return err;
+
+		break;
+
+	case CAN_RAW_RECV_OWN_MSGS:
+		if (optlen != sizeof(ro->recv_own_msgs))
+			return -EINVAL;
+
+		err = copy_from_user(&ro->recv_own_msgs, optval, optlen);
+		if (err)
+			return err;
+
+		break;
+
+	default:
+		return -ENOPROTOOPT;
+	}
+	return 0;
+}
+
+static int raw_getsockopt(struct socket *sock, int level, int optname,
+			  char __user *optval, int __user *optlen)
+{
+	struct sock *sk = sock->sk;
+	struct raw_sock *ro = raw_sk(sk);
+	int len;
+	void *val;
+	int err = 0;
+
+	if (level != SOL_CAN_RAW)
+		return -EINVAL;
+	if (get_user(len, optlen))
+		return -EFAULT;
+	if (len < 0)
+		return -EINVAL;
+
+	switch (optname) {
+
+	case CAN_RAW_FILTER:
+		lock_sock(sk);
+		if (ro->count > 0) {
+			int fsize = ro->count * sizeof(struct can_filter);
+			if (len > fsize)
+				len = fsize;
+			err = copy_to_user(optval, ro->filter, len);
+		} else
+			len = 0;
+		release_sock(sk);
+
+		if (!err)
+			err = put_user(len, optlen);
+		return err;
+
+	case CAN_RAW_ERR_FILTER:
+		if (len > sizeof(can_err_mask_t))
+			len = sizeof(can_err_mask_t);
+		val = &ro->err_mask;
+		break;
+
+	case CAN_RAW_LOOPBACK:
+		if (len > sizeof(int))
+			len = sizeof(int);
+		val = &ro->loopback;
+		break;
+
+	case CAN_RAW_RECV_OWN_MSGS:
+		if (len > sizeof(int))
+			len = sizeof(int);
+		val = &ro->recv_own_msgs;
+		break;
+
+	default:
+		return -ENOPROTOOPT;
+	}
+
+	if (put_user(len, optlen))
+		return -EFAULT;
+	if (copy_to_user(optval, val, len))
+		return -EFAULT;
+	return 0;
+}
+
+static int raw_sendmsg(struct kiocb *iocb, struct socket *sock,
+		       struct msghdr *msg, size_t size)
+{
+	struct sock *sk = sock->sk;
+	struct raw_sock *ro = raw_sk(sk);
+	struct sk_buff *skb;
+	struct net_device *dev;
+	int ifindex;
+	int err;
+
+	DBG("socket %p, sk %p\n", sock, sk);
+
+	if (msg->msg_name) {
+		struct sockaddr_can *addr =
+			(struct sockaddr_can *)msg->msg_name;
+
+		if (addr->can_family != AF_CAN)
+			return -EINVAL;
+
+		ifindex = addr->can_ifindex;
+	} else
+		ifindex = ro->ifindex;
+
+	dev = dev_get_by_index(ifindex);
+	if (!dev) {
+		DBG("device %d not found\n", ifindex);
+		return -ENXIO;
+	}
+
+	skb = alloc_skb(size, GFP_KERNEL);
+	if (!skb) {
+		dev_put(dev);
+		return -ENOMEM;
+	}
+
+	err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+	if (err < 0) {
+		kfree_skb(skb);
+		dev_put(dev);
+		return err;
+	}
+	skb->dev = dev;
+	skb->sk  = sk;
+
+	DBG("sending skbuff to interface %d\n", ifindex);
+	DBG_SKB(skb);
+
+	err = can_send(skb, ro->loopback);
+
+	dev_put(dev);
+
+	if (err)
+		return err;
+
+	return size;
+}
+
+static int raw_recvmsg(struct kiocb *iocb, struct socket *sock,
+		       struct msghdr *msg, size_t size, int flags)
+{
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+	int error = 0;
+	int noblock;
+
+	DBG("socket %p, sk %p\n", sock, sk);
+
+	noblock =  flags & MSG_DONTWAIT;
+	flags   &= ~MSG_DONTWAIT;
+
+	skb = skb_recv_datagram(sk, flags, noblock, &error);
+	if (!skb)
+		return error;
+
+	DBG("delivering skbuff %p\n", skb);
+	DBG_SKB(skb);
+
+	if (size < skb->len)
+		msg->msg_flags |= MSG_TRUNC;
+	else
+		size = skb->len;
+
+	error = memcpy_toiovec(msg->msg_iov, skb->data, size);
+	if (error < 0) {
+		skb_free_datagram(sk, skb);
+		return error;
+	}
+
+	sock_recv_timestamp(msg, sk, skb);
+
+	if (msg->msg_name) {
+		msg->msg_namelen = sizeof(struct sockaddr_can);
+		memcpy(msg->msg_name, skb->cb, msg->msg_namelen);
+	}
+
+	DBG("freeing sock %p, skbuff %p\n", sk, skb);
+	skb_free_datagram(sk, skb);
+
+	return size;
+}
+
+static struct proto_ops raw_ops = {
+	.family        = PF_CAN,
+	.release       = raw_release,
+	.bind          = raw_bind,
+	.connect       = sock_no_connect,
+	.socketpair    = sock_no_socketpair,
+	.accept        = sock_no_accept,
+	.getname       = raw_getname,
+	.poll          = raw_poll,
+	.ioctl         = NULL,		/* use can_ioctl() from af_can.c */
+	.listen        = sock_no_listen,
+	.shutdown      = sock_no_shutdown,
+	.setsockopt    = raw_setsockopt,
+	.getsockopt    = raw_getsockopt,
+	.sendmsg       = raw_sendmsg,
+	.recvmsg       = raw_recvmsg,
+	.mmap          = sock_no_mmap,
+	.sendpage      = sock_no_sendpage,
+};
+
+static struct proto raw_proto = {
+	.name       = "CAN_RAW",
+	.owner      = THIS_MODULE,
+	.obj_size   = sizeof(struct raw_sock),
+	.init       = raw_init,
+};
+
+static struct can_proto raw_can_proto = {
+	.type       = SOCK_RAW,
+	.protocol   = CAN_RAW,
+	.capability = RAW_CAP,
+	.ops        = &raw_ops,
+	.prot       = &raw_proto,
+};
+
+static __init int raw_module_init(void)
+{
+	printk(banner);
+
+	can_proto_register(&raw_can_proto);
+	return 0;
+}
+
+static __exit void raw_module_exit(void)
+{
+	can_proto_unregister(&raw_can_proto);
+}
+
+module_init(raw_module_init);
+module_exit(raw_module_exit);

--

^ permalink raw reply

* [patch 5/7] CAN: Add virtual CAN netdevice driver
From: Urs Thuermann @ 2007-08-04  2:07 UTC (permalink / raw)
  To: netdev; +Cc: urs, oliver, davem, kaber, urs.thuermann, oliver.hartkopp
In-Reply-To: <20070804020657.25572.0@janus.isnogud.escape.de>

[-- Attachment #1: 05-can-vcan-driver.diff --]
[-- Type: text/plain, Size: 10311 bytes --]

This patch adds the virtual CAN bus (vcan) network driver.
The vcan device is just a loopback device for CAN frames, no
real CAN hardware is involved.

Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>

---
 drivers/net/Makefile     |    1 
 drivers/net/can/Kconfig  |   25 ++++
 drivers/net/can/Makefile |    5 
 drivers/net/can/vcan.c   |  261 +++++++++++++++++++++++++++++++++++++++++++++++
 net/can/Kconfig          |    3 
 5 files changed, 295 insertions(+)

Index: net-2.6/drivers/net/Makefile
===================================================================
--- net-2.6.orig/drivers/net/Makefile	2007-08-03 11:21:31.000000000 +0200
+++ net-2.6/drivers/net/Makefile	2007-08-03 11:21:54.000000000 +0200
@@ -8,6 +8,7 @@
 obj-$(CONFIG_CHELSIO_T1) += chelsio/
 obj-$(CONFIG_CHELSIO_T3) += cxgb3/
 obj-$(CONFIG_EHEA) += ehea/
+obj-$(CONFIG_CAN) += can/
 obj-$(CONFIG_BONDING) += bonding/
 obj-$(CONFIG_ATL1) += atl1/
 obj-$(CONFIG_GIANFAR) += gianfar_driver.o
Index: net-2.6/drivers/net/can/Kconfig
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/drivers/net/can/Kconfig	2007-08-03 11:21:54.000000000 +0200
@@ -0,0 +1,25 @@
+menu "CAN Device Drivers"
+	depends on CAN
+
+config CAN_VCAN
+	tristate "Virtual Local CAN Interface (vcan)"
+ 	depends on CAN
+	default N
+ 	---help---
+	  Similar to the network loopback devices, vcan offers a
+	  virtual local CAN interface.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called vcan.
+
+config CAN_DEBUG_DEVICES
+	bool "CAN devices debugging messages"
+	depends on CAN
+	default N
+	---help---
+	  Say Y here if you want the CAN device drivers to produce a bunch of
+	  debug messages to the system log.  Select this if you are having
+	  a problem with CAN support and want to see more of what is going
+	  on.
+
+endmenu
Index: net-2.6/drivers/net/can/Makefile
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/drivers/net/can/Makefile	2007-08-03 11:21:54.000000000 +0200
@@ -0,0 +1,5 @@
+#
+#  Makefile for the Linux Controller Area Network drivers.
+#
+
+obj-$(CONFIG_CAN_VCAN)		+= vcan.o
Index: net-2.6/drivers/net/can/vcan.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ net-2.6/drivers/net/can/vcan.c	2007-08-03 11:21:54.000000000 +0200
@@ -0,0 +1,261 @@
+/*
+ * vcan.c - Virtual CAN interface
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, the following disclaimer and
+ *    the referenced file 'COPYING'.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2 as distributed in the 'COPYING'
+ * file from the main directory of the linux kernel source.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/can.h>
+#include <net/rtnetlink.h>
+
+static __initdata const char banner[] =
+	KERN_INFO "vcan: Virtual CAN interface driver\n";
+
+MODULE_DESCRIPTION("virtual CAN interface");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static int debug;
+module_param(debug, int, S_IRUGO);
+#endif
+
+/* To be moved to linux/can/dev.h */
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...)       (debug & 1 ? \
+			       (printk(KERN_DEBUG "vcan %s: ", __func__), \
+				printk(args)) : 0)
+#else
+#define DBG(args...)
+#endif
+
+
+/*
+ * CAN test feature:
+ * Enable the loopback on driver level for testing the CAN core loopback modes.
+ * See Documentation/networking/can.txt for details.
+ */
+
+static int loopback; /* loopback testing. Default: 0 (Off) */
+module_param(loopback, int, S_IRUGO);
+MODULE_PARM_DESC(loopback, "Loop back frames (for testing). Default: 0 (Off)");
+
+struct vcan_priv {
+	struct net_device *dev;
+	struct list_head list;
+};
+static LIST_HEAD(vcan_devs);
+
+static int vcan_open(struct net_device *dev)
+{
+	DBG("%s: interface up\n", dev->name);
+
+	netif_start_queue(dev);
+	return 0;
+}
+
+static int vcan_stop(struct net_device *dev)
+{
+	DBG("%s: interface down\n", dev->name);
+
+	netif_stop_queue(dev);
+	return 0;
+}
+
+static void vcan_rx(struct sk_buff *skb, struct net_device *dev)
+{
+	struct net_device_stats *stats = &dev->stats;
+
+	stats->rx_packets++;
+	stats->rx_bytes += skb->len;
+
+	skb->protocol  = htons(ETH_P_CAN);
+	skb->pkt_type  = PACKET_BROADCAST;
+	skb->dev       = dev;
+	skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+	DBG("received skbuff on interface %d\n", dev->ifindex);
+
+	netif_rx(skb);
+}
+
+static int vcan_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	struct net_device_stats *stats = &dev->stats;
+	int loop;
+
+	DBG("sending skbuff on interface %s\n", dev->name);
+
+	stats->tx_packets++;
+	stats->tx_bytes += skb->len;
+
+	/* set flag whether this packet has to be looped back */
+	loop = skb->pkt_type == PACKET_LOOPBACK;
+
+	if (!loopback) {
+		/* no loopback handling available inside this driver */
+
+		if (loop) {
+			/*
+			 * only count the packets here, because the
+			 * CAN core already did the loopback for us
+			 */
+			stats->rx_packets++;
+			stats->rx_bytes += skb->len;
+		}
+		kfree_skb(skb);
+		return 0;
+	}
+
+	/* perform standard loopback handling for CAN network interfaces */
+
+	if (loop) {
+		struct sock *srcsk = skb->sk;
+
+		if (atomic_read(&skb->users) != 1) {
+			struct sk_buff *old_skb = skb;
+
+			skb = skb_clone(old_skb, GFP_ATOMIC);
+			DBG(KERN_INFO "%s: %s: freeing old skbuff %p, "
+			    "using new skbuff %p\n",
+			    dev->name, __FUNCTION__, old_skb, skb);
+			kfree_skb(old_skb);
+			if (!skb)
+				return 0;
+		} else
+			skb_orphan(skb);
+
+		/* receive with packet counting */
+		skb->sk = srcsk;
+		vcan_rx(skb, dev);
+	} else {
+		/* no looped packets => no counting */
+		kfree_skb(skb);
+	}
+	return 0;
+}
+
+static void vcan_setup(struct net_device *dev)
+{
+	DBG("dev %s\n", dev->name);
+
+	dev->type              = ARPHRD_CAN;
+	dev->mtu               = sizeof(struct can_frame);
+	dev->hard_header_len   = 0;
+	dev->addr_len          = 0;
+	dev->tx_queue_len      = 0;
+	dev->flags             = IFF_NOARP;
+
+	/* set flags according to driver capabilities */
+	if (loopback)
+		dev->flags |= IFF_LOOPBACK;
+
+	dev->open              = vcan_open;
+	dev->stop              = vcan_stop;
+	dev->hard_start_xmit   = vcan_tx;
+	dev->destructor        = free_netdev;
+
+	SET_MODULE_OWNER(dev);
+}
+
+static int vcan_newlink(struct net_device *dev,
+			struct nlattr *tb[], struct nlattr *data[])
+{
+	struct vcan_priv *priv = netdev_priv(dev);
+	int err;
+
+	err = register_netdevice(dev);
+	if (err < 0)
+		return err;
+
+	priv->dev = dev;
+	list_add_tail(&priv->list, &vcan_devs);
+	return 0;
+}
+
+static void vcan_dellink(struct net_device *dev)
+{
+	struct vcan_priv *priv = netdev_priv(dev);
+
+	list_del(&priv->list);
+	unregister_netdevice(dev);
+}
+
+static struct rtnl_link_ops vcan_link_ops __read_mostly = {
+       .kind           = "vcan",
+       .priv_size      = sizeof(struct vcan_priv),
+       .setup          = vcan_setup,
+       .newlink        = vcan_newlink,
+       .dellink        = vcan_dellink,
+};
+
+static __init int vcan_init_module(void)
+{
+	int err;
+
+	printk(banner);
+
+	if (loopback)
+		printk(KERN_INFO "vcan: enabled loopback on driver level.\n");
+
+	rtnl_lock();
+	err = __rtnl_link_register(&vcan_link_ops);
+	rtnl_unlock();
+	return err;
+}
+
+static __exit void vcan_cleanup_module(void)
+{
+	struct vcan_priv *priv, *n;
+
+	rtnl_lock();
+	list_for_each_entry_safe(priv, n, &vcan_devs, list)
+		vcan_dellink(priv->dev);
+	__rtnl_link_unregister(&vcan_link_ops);
+	rtnl_unlock();
+}
+
+module_init(vcan_init_module);
+module_exit(vcan_cleanup_module);
Index: net-2.6/net/can/Kconfig
===================================================================
--- net-2.6.orig/net/can/Kconfig	2007-08-03 11:21:51.000000000 +0200
+++ net-2.6/net/can/Kconfig	2007-08-03 11:21:54.000000000 +0200
@@ -77,3 +77,6 @@
 	  Say Y here if you want the CAN core to produce a bunch of debug
 	  messages to the system log.  Select this if you are having a
 	  problem with CAN support and want to see more of what is going on.
+
+
+source "drivers/net/can/Kconfig"

--

^ permalink raw reply

* [patch 0/7] CAN: Add new PF_CAN protocol family, try #5
From: Urs Thuermann @ 2007-08-04  2:06 UTC (permalink / raw)
  To: netdev; +Cc: urs, oliver, davem, kaber, urs.thuermann, oliver.hartkopp

Hello Dave,

this is the fifth post of the patch series that adds the PF_CAN
protocol family for the Controller Area Network.

Since our last post we have changed the following:

* Remove slab destructor from calls to kmem_cache_alloc().
* Add comments about types defined in can.h.
* Update comment on vcan loopback module parameter.
* Fix typo in documentation.

The changes in try #4 were:

* Change vcan network driver to use the new RTNL API, as suggested by
  Patrick.
* Revert our change to use skb->iif instead of skb->cb.  After
  discussion with Patrick and Jamal it turned out, our first
  implementation was correct.
* Use skb_tail_pointer() instead of skb->tail directly.
* Coding style changes to satisfy linux/scripts/checkpatch.pl.
* Minor changes for 64-bit-cleanliness.
* Minor cleanup of #include's

The changes in try #3 were:

* Use sbk->sk and skb->pkt_type instead of skb->cb to pass loopback
  flags and originating socket down to the driver and back to the
  receiving socket.  Thanks to Patrick McHardy for pointing out our
  wrong use of sbk->cb.
* Use skb->iif instead of skb->cb to pass receiving interface from
  raw_rcv() and bcm_rcv() up to raw_recvmsg() and bcm_recvmsg().
* Set skb->protocol when sending CAN frames to netdevices.
* Removed struct raw_opt and struct bcm_opt and integrated these
  directly into struct raw_sock and bcm_sock resp., like most other
  proto implementations do.
* We have found and fixed race conditions between raw_bind(),
  raw_{set,get}sockopt() and raw_notifier().  This resulted in
  - complete removal of our own notifier list infrastructure in
    af_can.c.  raw.c and bcm.c now use normal netdevice notifiers.
  - removal of ro->lock spinlock.  We use lock_sock(sk) now.
  - changed deletion of dev_rcv_lists, which are now marked for
    deletion in the netdevice notifier in af_can.c and are actually
    deleted when all entries have been deleted using can_rx_unregister().
* Follow changes in 2.6.22 (e.g. ktime_t timestamps in skb).
* Removed obsolete code from vcan.c, as pointed out by Stephen Hemminger.

The changes in try #2 were:

* reduced RCU callback overhead when deleting receiver lists (thx to
  feedback from Paul E. McKenney).
* eliminated some code duplication in net/can/proc.c.
* renamed slock-29 and sk_lock-29 to slock-AF_CAN and sk_lock-AF_CAN in
  net/core/sock.c
* added entry for can.txt in Documentation/networking/00-INDEX
* added error frame definitions in include/linux/can/error.h, which are to
  be used by CAN network drivers.


This patch series applies against net-2.6 and is derived from Subversion
revision r455 of http://svn.berlios.de/svnroot/repos/socketcan.
It can be found in the directory
http://svn.berlios.de/svnroot/repos/socketcan/trunk/patch-series/<version>.

This patch doesn't touch anything in the kernel except for the allocation
of a couple of numbers for protocol, arp hw type, and a line discipline.

Please review this patch series for integration into your tree.

Thanks very much for your work!

Best regards,

Urs Thuermann
Oliver Hartkopp

--

^ permalink raw reply

* [patch 6/7] CAN: Add maintainer entries
From: Urs Thuermann @ 2007-08-04  2:07 UTC (permalink / raw)
  To: netdev; +Cc: urs, oliver, davem, kaber, urs.thuermann, oliver.hartkopp
In-Reply-To: <20070804020657.25572.0@janus.isnogud.escape.de>

[-- Attachment #1: 06-can-maintainers.diff --]
[-- Type: text/plain, Size: 1716 bytes --]

This patch adds entries in the CREDITS and MAINTAINERS file for CAN.

Signed-off-by: Oliver Hartkopp <oliver.hartkopp@volkswagen.de>
Signed-off-by: Urs Thuermann <urs.thuermann@volkswagen.de>

---
 CREDITS     |   16 ++++++++++++++++
 MAINTAINERS |    9 +++++++++
 2 files changed, 25 insertions(+)

Index: net-2.6/CREDITS
===================================================================
--- net-2.6.orig/CREDITS	2007-08-03 11:21:31.000000000 +0200
+++ net-2.6/CREDITS	2007-08-03 11:21:56.000000000 +0200
@@ -1331,6 +1331,14 @@
 S: 5623 HZ Eindhoven
 S: The Netherlands
 
+N: Oliver Hartkopp
+E: oliver.hartkopp@volkswagen.de
+W: http://www.volkswagen.de
+D: Controller Area Network (network layer core)
+S: Brieffach 1776
+S: 38436 Wolfsburg
+S: Germany
+
 N: Andrew Haylett
 E: ajh@primag.co.uk
 D: Selection mechanism
@@ -3284,6 +3292,14 @@
 S: F-35042 Rennes Cedex
 S: France
 
+N: Urs Thuermann
+E: urs.thuermann@volkswagen.de
+W: http://www.volkswagen.de
+D: Controller Area Network (network layer core)
+S: Brieffach 1776
+S: 38436 Wolfsburg
+S: Germany
+
 N: Jon Tombs
 E: jon@gte.esi.us.es
 W: http://www.esi.us.es/~jon
Index: net-2.6/MAINTAINERS
===================================================================
--- net-2.6.orig/MAINTAINERS	2007-08-03 11:21:31.000000000 +0200
+++ net-2.6/MAINTAINERS	2007-08-03 11:21:56.000000000 +0200
@@ -951,6 +951,15 @@
 L:	video4linux-list@redhat.com
 S:	Maintained
 
+CAN NETWORK LAYER
+P:	Urs Thuermann
+M:	urs.thuermann@volkswagen.de
+P:	Oliver Hartkopp
+M:	oliver.hartkopp@volkswagen.de
+L:	socketcan-core@lists.berlios.de
+W:	http://developer.berlios.de/projects/socketcan/
+S:	Maintained
+
 CALGARY x86-64 IOMMU
 P:	Muli Ben-Yehuda
 M:	muli@il.ibm.com

--

^ permalink raw reply

* Re: Distributed storage.
From: Daniel Phillips @ 2007-08-04  1:19 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: netdev, linux-kernel, linux-fsdevel, Peter Zijlstra
In-Reply-To: <20070803102629.GB10089@2ka.mipt.ru>

On Friday 03 August 2007 03:26, Evgeniy Polyakov wrote:
> On Thu, Aug 02, 2007 at 02:08:24PM -0700, I wrote:
> > I see bits that worry me, e.g.:
> >
> > +		req = mempool_alloc(st->w->req_pool, GFP_NOIO);
> >
> > which seems to be callable in response to a local request, just the
> > case where NBD deadlocks.  Your mempool strategy can work reliably
> > only if you can prove that the pool allocations of the maximum
> > number of requests you can have in flight do not exceed the size of
> > the pool.  In other words, if you ever take the pool's fallback
> > path to normal allocation, you risk deadlock.
>
> mempool should be allocated to be able to catch up with maximum
> in-flight requests, in my tests I was unable to force block layer to
> put more than 31 pages in sync, but in one bio. Each request is
> essentially dealyed bio processing, so this must handle maximum
> number of in-flight bios (if they do not cover multiple nodes, if
> they do, then each node requires own request).

It depends on the characteristics of the physical and virtual block 
devices involved.  Slow block devices can produce surprising effects.  
Ddsnap still qualifies as "slow" under certain circumstances (big 
linear write immediately following a new snapshot). Before we added 
throttling we would see as many as 800,000 bios in flight.  Nice to 
know the system can actually survive this... mostly.  But memory 
deadlock is a clear and present danger under those conditions and we 
did hit it (not to mention that read latency sucked beyond belief). 

Anyway, we added a simple counting semaphore to throttle the bio traffic 
to a reasonable number and behavior became much nicer, but most 
importantly, this satisfies one of the primary requirements for 
avoiding block device memory deadlock: a strictly bounded amount of bio 
traffic in flight.  In fact, we allow some bounded number of 
non-memalloc bios *plus* however much traffic the mm wants to throw at 
us in memalloc mode, on the assumption that the mm knows what it is 
doing and imposes its own bound of in flight bios per device.   This 
needs auditing obviously, but the mm either does that or is buggy.  In 
practice, with this throttling in place we never saw more than 2,000 in 
flight no matter how hard we hit it, which is about the number we were 
aiming at.  Since we draw our reserve from the main memalloc pool, we 
can easily handle 2,000 bios in flight, even under extreme conditions.

See:
    http://zumastor.googlecode.com/svn/trunk/ddsnap/kernel/dm-ddsnap.c
    down(&info->throttle_sem);

To be sure, I am not very proud of this throttling mechanism for various 
reasons, but the thing is, _any_ throttling mechanism no matter how 
sucky solves the deadlock problem.  Over time I want to move the 
throttling up into bio submission proper, or perhaps incorporate it in 
device mapper's queue function, not quite as high up the food chain.  
Only some stupid little logistical issues stopped me from doing it one 
of those ways right from the start.   I think Peter has also tried some 
things in this area.  Anyway, that part is not pressing because the 
throttling can be done in the virtual device itself as we do it, even 
if it is not very pretty there.  The point is: you have to throttle the 
bio traffic.  The alternative is to die a horrible death under 
conditions that may be rare, but _will_ hit somebody.

Regards,

Daniel

^ permalink raw reply

* Re: Distributed storage.
From: Daniel Phillips @ 2007-08-04  0:49 UTC (permalink / raw)
  To: Mike Snitzer; +Cc: Evgeniy Polyakov, netdev, linux-kernel, linux-fsdevel
In-Reply-To: <170fa0d20708022109s60ebb85aqe68ec1033634ef27@mail.gmail.com>

Hi Mike,

On Thursday 02 August 2007 21:09, Mike Snitzer wrote:
> But NBD's synchronous nature is actually an asset when coupled with
> MD raid1 as it provides guarantees that the data has _really_ been
> mirrored remotely.

And bio completion doesn't?

Regards,

Daniel

^ permalink raw reply

* Re: Distributed storage.
From: Daniel Phillips @ 2007-08-04  0:41 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: netdev, linux-kernel, linux-fsdevel
In-Reply-To: <20070731171347.GA14267@2ka.mipt.ru>

Hi Evgeniy,

Nit alert:

On Tuesday 31 July 2007 10:13, Evgeniy Polyakov wrote:
>     * storage can be formed on top of remote nodes and be exported
> 	simultaneously (iSCSI is peer-to-peer only, NBD requires device
> 	mapper and is synchronous)

In fact, NBD has nothing to do with device mapper.  I use it as a 
physical target underneath ddraid (a device mapper plugin) just like I 
would use your DST if it proves out.

Regards,

Daniel

^ permalink raw reply

* Re: netdevice queueing / sendmsg issue?
From: Krzysztof Halasa @ 2007-08-03 22:18 UTC (permalink / raw)
  To: David Miller; +Cc: netdev
In-Reply-To: <20070728.224959.74562336.davem@davemloft.net>

David Miller <davem@davemloft.net> writes:

> Software interrupts might be getting lost, dev_kfree_skb_irq() has to
> queue the kfree_skb() to soft IRQ.
>
> Therefore, dev_kfree_skb_irq() will only work properly from hardware
> interrupt context, where we will return and thus run the scheduled
> software interrupt.

Problem solved, stupid user mistake.
I was using netif_start_queue() instead of netif_wake_queue().
-- 
Krzysztof Halasa

^ permalink raw reply

* [BNX2]: Fix suspend/resume problem.
From: Michael Chan @ 2007-08-03 22:32 UTC (permalink / raw)
  To: davem; +Cc: netdev

[BNX2]: Fix suspend/resume problem.

The device would not resume properly if it was shutdown before the system
was suspended.  In such scenario where the netif_running state is 0,
bnx2_suspend() would not save the PCI state and so the memory enable bit
and bus master enable bit would be lost.

We fix this by always saving and restoring the PCI state in
bnx2_suspend() and bnx2_resume() regardless of netif_running() state.

Update version to 1.6.4.

Signed-off-by: Michael Chan <mchan@broadcom.com>

diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index d53dfc5..24e7f9a 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -54,8 +54,8 @@
 
 #define DRV_MODULE_NAME		"bnx2"
 #define PFX DRV_MODULE_NAME	": "
-#define DRV_MODULE_VERSION	"1.6.3"
-#define DRV_MODULE_RELDATE	"July 16, 2007"
+#define DRV_MODULE_VERSION	"1.6.4"
+#define DRV_MODULE_RELDATE	"August 3, 2007"
 
 #define RUN_AT(x) (jiffies + (x))
 
@@ -6937,6 +6937,11 @@ bnx2_suspend(struct pci_dev *pdev, pm_message_t state)
 	struct bnx2 *bp = netdev_priv(dev);
 	u32 reset_code;
 
+	/* PCI register 4 needs to be saved whether netif_running() or not.
+	 * MSI address and data need to be saved if using MSI and
+	 * netif_running().
+	 */
+	pci_save_state(pdev);
 	if (!netif_running(dev))
 		return 0;
 
@@ -6952,7 +6957,6 @@ bnx2_suspend(struct pci_dev *pdev, pm_message_t state)
 		reset_code = BNX2_DRV_MSG_CODE_SUSPEND_NO_WOL;
 	bnx2_reset_chip(bp, reset_code);
 	bnx2_free_skbs(bp);
-	pci_save_state(pdev);
 	bnx2_set_power_state(bp, pci_choose_state(pdev, state));
 	return 0;
 }
@@ -6963,10 +6967,10 @@ bnx2_resume(struct pci_dev *pdev)
 	struct net_device *dev = pci_get_drvdata(pdev);
 	struct bnx2 *bp = netdev_priv(dev);
 
+	pci_restore_state(pdev);
 	if (!netif_running(dev))
 		return 0;
 
-	pci_restore_state(pdev);
 	bnx2_set_power_state(bp, PCI_D0);
 	netif_device_attach(dev);
 	bnx2_init_nic(bp);



^ permalink raw reply related

* Re: strange tcp behavior
From: David Miller @ 2007-08-03 21:17 UTC (permalink / raw)
  To: johnpol; +Cc: simon, john, netdev
In-Reply-To: <20070803082242.GA25582@2ka.mipt.ru>

From: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Date: Fri, 3 Aug 2007 12:22:42 +0400

> Maybe recvmsg should be changed too for symmetry?

I took a look at this, and it's not %100 trivial.

Let's do this later, and only sendmsg for now in order to
fix the bug in the stable branches.

^ permalink raw reply

* Re: [PATCH] lro: myri10ge example how to use LRO
From: Andrew Gallatin @ 2007-08-03 21:00 UTC (permalink / raw)
  To: Kok, Auke
  Cc: netdev, raisch, themann, linux-kernel, linuxppc-dev, meder,
	tklein, stefan.roscher, jeff, davem
In-Reply-To: <46B39063.6070301@intel.com>

Kok, Auke wrote:
> Andrew Gallatin wrote:
>> To follow up on Jan-Bernd Themann's LRO patch earlier today,
>> this patch shows how the generic LRO interface can be used for
>> page based drivers.
>>
>> Again, many thanks to Jan-Bernd Themann for leading this effort.
>>
>> Drew
>>
>> Singed off by: Andrew Gallatin <gallatin@myri.com>
>>
> 
> 
> please take a look at my lro patch for ethtool and see if it works for 
> you, instead of adding another generic module parameter that doesn't 
> need to be there.

That looks very nice, and will indeed work for me.

Thanks,

Drew

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox