Netdev List
 help / color / mirror / Atom feed
* [PATCH 3/4] batman-adv: Remove vis info element in free_info
From: Sven Eckelmann @ 2011-01-30 12:11 UTC (permalink / raw)
  To: davem; +Cc: netdev, Sven Eckelmann
In-Reply-To: <1296389504-11208-1-git-send-email-sven@narfation.org>

The free_info function will be called when no reference to the info
object exists anymore. It must be ensured that the allocated memory
gets freed and not only the elements which are managed by the info
object.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/vis.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index 0be55be..988296c 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -64,6 +64,7 @@ static void free_info(struct kref *ref)
 
 	spin_unlock_bh(&bat_priv->vis_list_lock);
 	kfree_skb(info->skb_packet);
+	kfree(info);
 }
 
 /* Compare two vis packets, used by the hashing algorithm */
-- 
1.7.2.3


^ permalink raw reply related

* [PATCH 2/4] batman-adv: Remove vis info on hashing errors
From: Sven Eckelmann @ 2011-01-30 12:11 UTC (permalink / raw)
  To: davem; +Cc: netdev, Sven Eckelmann
In-Reply-To: <1296389504-11208-1-git-send-email-sven@narfation.org>

A newly created vis info object must be removed when it couldn't be
added to the hash. The old_info which has to be replaced was already
removed and isn't related to the hash anymore.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/vis.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index f69a374..0be55be 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -444,7 +444,7 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv,
 			      info);
 	if (hash_added < 0) {
 		/* did not work (for some reason) */
-		kref_put(&old_info->refcount, free_info);
+		kref_put(&info->refcount, free_info);
 		info = NULL;
 	}
 
-- 
1.7.2.3


^ permalink raw reply related

* [PATCH 1/4] batman-adv: Fix kernel panic when fetching vis data on a vis server
From: Sven Eckelmann @ 2011-01-30 12:11 UTC (permalink / raw)
  To: davem; +Cc: netdev, Linus Lüssing, Sven Eckelmann
In-Reply-To: <1296389504-11208-1-git-send-email-sven@narfation.org>

From: Linus Lüssing <linus.luessing@web.de>

The hash_iterate removal introduced a bug leading to a kernel panic when
fetching the vis data on a vis server. That commit forgot to rename one
variable name, which this commit fixes now.

Reported-by: Russell Senior <russell@personaltelco.net>
Signed-off-by: Linus Lüssing <linus.luessing@web.de>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/vis.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index cd4c423..f69a374 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -268,10 +268,10 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
 				buff_pos += sprintf(buff + buff_pos, "%pM,",
 						entry->addr);
 
-				for (i = 0; i < packet->entries; i++)
+				for (j = 0; j < packet->entries; j++)
 					buff_pos += vis_data_read_entry(
 							buff + buff_pos,
-							&entries[i],
+							&entries[j],
 							entry->addr,
 							entry->primary);
 
-- 
1.7.2.3


^ permalink raw reply related

* pull request: batman-adv 2011-01-30
From: Sven Eckelmann @ 2011-01-30 12:11 UTC (permalink / raw)
  To: davem; +Cc: netdev

Hi,

I would like to propose following patches for net-2.6.git/2.6.38. These are
important fixes for kernel oopses/memory leaks. I already send this exact same
request in 201101301036.57928.sven@narfation.org were you requested a new patch
series posting.

I cannot reduce the number of fixes any more because it makes no sense to fix
one Oops and leave the other one open. I've already removed the fixes for the
protocol implementation errors from the first patchset.

The following changes since commit aa0adb1a85e159cf57f0e11282bc6c9e3606a5f3:

  batman-adv: Use "__attribute__" shortcut macros (2011-01-16 03:25:19 +0100)

are available in the git repository at:
  git://git.open-mesh.org/ecsv/linux-merge.git batman-adv/merge-oopsonly

Linus Lüssing (1):
      batman-adv: Fix kernel panic when fetching vis data on a vis server

Sven Eckelmann (3):
      batman-adv: Remove vis info on hashing errors
      batman-adv: Remove vis info element in free_info
      batman-adv: Make vis info stack traversal threadsafe

 net/batman-adv/vis.c |   14 ++++++++------
 1 files changed, 8 insertions(+), 6 deletions(-)

 Thanks,
 	Sven

^ permalink raw reply

* Re: [PATCH]netdev: add driver for enc424j600 ethernet chip on SPI bus
From: David Miller @ 2011-01-30 12:01 UTC (permalink / raw)
  To: romieu
  Cc: balaji.v, netdev, mirqus, mohan, blue.cube, lanconelli.claudio,
	sriram, vbalaji.acs
In-Reply-To: <20110130115516.GA8392@electric-eye.fr.zoreil.com>

From: Francois Romieu <romieu@fr.zoreil.com>
Date: Sun, 30 Jan 2011 12:55:16 +0100

> Balaji Venkatachalam <balaji.v@thotakaa.com> :
>> From: Balaji Venkatachalam <balaji.v@thotakaa.com>
>> Updated patch [V1.29] for Microchip enc424j600 ethernet chip controlled via SPI.
>> 
>> I tested it on my custom board with ARM9 (Freescale i.MX233) with
>> Kernel 2.6.31.14.
> [...]
>> 4. mapped enc424j600_dump_regs to eth_ops get_regs
> 
> There is some kind of misunderstanding.
> 
> See include/linux/ethtool.h
> [...]
> struct ethtool_ops {
>         int     (*get_settings)(struct net_device *, struct ethtool_cmd *);
>         int     (*set_settings)(struct net_device *, struct ethtool_cmd *);
>         void    (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
>         int     (*get_regs_len)(struct net_device *);
>         void    (*get_regs)(struct net_device *, struct ethtool_regs *, void *);
> 
> It should not even compile.

Balaji, please take care of your changes and do not rush this review
process like you seem to be doing.

If you're not even watching for compiler warnings and errors, I can
expect that you did exaclty zero functional testing of the changed
code.

That's simply unacceptable.

^ permalink raw reply

* Re: [PATCH]netdev: add driver for enc424j600 ethernet chip on SPI bus
From: Francois Romieu @ 2011-01-30 11:55 UTC (permalink / raw)
  To: Balaji Venkatachalam
  Cc: netdev, mirqus, mohan, blue.cube, lanconelli.claudio, sriram,
	David Miller, vbalaji.acs
In-Reply-To: <AANLkTi=h52CMtS_05XW8JgWQOSf8pnezH6R6_VVS5DtQ@mail.gmail.com>

Balaji Venkatachalam <balaji.v@thotakaa.com> :
> From: Balaji Venkatachalam <balaji.v@thotakaa.com>
> Updated patch [V1.29] for Microchip enc424j600 ethernet chip controlled via SPI.
> 
> I tested it on my custom board with ARM9 (Freescale i.MX233) with
> Kernel 2.6.31.14.
[...]
> 4. mapped enc424j600_dump_regs to eth_ops get_regs

There is some kind of misunderstanding.

See include/linux/ethtool.h
[...]
struct ethtool_ops {
        int     (*get_settings)(struct net_device *, struct ethtool_cmd *);
        int     (*set_settings)(struct net_device *, struct ethtool_cmd *);
        void    (*get_drvinfo)(struct net_device *, struct ethtool_drvinfo *);
        int     (*get_regs_len)(struct net_device *);
        void    (*get_regs)(struct net_device *, struct ethtool_regs *, void *);

It should not even compile.

-- 
Ueimor

^ permalink raw reply

* Re: pull request: batman-adv 2011-01-29
From: David Miller @ 2011-01-30 11:56 UTC (permalink / raw)
  To: sven-KaDOiPu9UxWEi8DpZVb4nw
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	b.a.t.m.a.n-ZwoEplunGu2X36UT3dwllkB+6BGkLq7r
In-Reply-To: <201101301036.57928.sven-KaDOiPu9UxWEi8DpZVb4nw@public.gmane.org>

From: Sven Eckelmann <sven-KaDOiPu9UxWEi8DpZVb4nw@public.gmane.org>
Date: Sun, 30 Jan 2011 10:36:54 +0100

> On Sunday 30 January 2011 04:08:15 David Miller wrote:
>> I'm not taking 13 patches in one big bunch at this point post-merge-window,
>> now matter what they are.
> 
> Thats why I tried to provide the 6 bug fixes only version without polishing - 
> I could also try to reduce it to a 4 critical version (oops/memleak patches 
> only without protocol implementation failures).
> 
> Or is the problem that I tried to get them in as pull request?

Please make a new patch series posting and pull request with only the most
important fixes included.

Once I accept those, you can send me a second patch series and pull
request with the cleanups done on top.  Just mention explicitly in
this second pull request that there is a dependency upon the initial
bug fixes.  In this way I will know to pull net-2.6 into net-next-2.6
first, and then pull from your tree into net-next-2.6

Thanks.

^ permalink raw reply

* Re: [PATCH]netdev: add driver for enc424j600 ethernet chip on SPI bus
From: Balaji Venkatachalam @ 2011-01-30 10:14 UTC (permalink / raw)
  To: romieu
  Cc: netdev, mirqus, mohan, blue.cube, lanconelli.claudio, sriram,
	David Miller, vbalaji.acs
In-Reply-To: <AANLkTiksJpnx3M=D=WPF1wGej4POEQG4No-S3efb_gNM@mail.gmail.com>

From: Balaji Venkatachalam <balaji.v@thotakaa.com>
Updated patch [V1.29] for Microchip enc424j600 ethernet chip controlled via SPI.

I tested it on my custom board with ARM9 (Freescale i.MX233) with
Kernel 2.6.31.14.

Changes done since V1.28 to V1.29
1. enc424j600_spi_trans function implementation optimized
2. Unnecessary comments and Improper Comments removed
3. Added an empty line after variable declaration in all functions
4. mapped enc424j600_dump_regs to eth_ops get_regs
5. removed the hardcoding of return type in enc424j600_probe function

Changes done since V1.27 to V1.28
1. did some code formatting at line no 110

Changes done since V1.24 to V1.27
1. Timeout Mechanism implemented for enc424j600_soft_reset function
2. Timeout Mechanism implemented for enc424j600_wait_for_autoneg function
3. Window Naming changed to enum
4. Removed WRITEVERIFY functionality

Todo List:
1. Low Power Mode Functionality implementation
2. Provide Support for On-Chip DMA.
3. Remove mutex_lock wherever not required
Any comments are welcome.

Signed-off-by: Balaji Venkatachalam <balaji.v@thotakaa.com>
---
diff -uprN -X a/Documentation/dontdiff a/drivers/net/enc424j600.c
b/drivers/net/enc424j600.c
--- a/drivers/net/enc424j600.c	1970-01-01 05:30:00.000000000 +0530
+++ b/drivers/net/enc424j600.c	2011-01-30 15:30:09.000000000 +0530
@@ -0,0 +1,1722 @@
+/*
+ * Microchip ENC424J600 ethernet driver (MAC + PHY) on SPI bus
+ *
+ * Copyright (C) 2011 Thotaka Technologies Pvt Ltd
+ * Author: Balaji Venkatachalam <balaji.v@thotakaa.com>
+ * based on enc424j600.c written by Kuba Marek
+ * based on enc28j60.c written by Claudio Lanconelli
+ *
+ * Changes done since V1.28 to V1.29
+ * 1. enc424j600_spi_trans function implementation optimized
+ * 2. Unnecessary comments and Improper Comments removed
+ * 3. Added an empty line after variable declaration in all functions
+ * 4. mapped enc424j600_dump_regs to eth_ops get_regs
+ * 5. removed the hardcoding of return type in enc424j600_probe function
+ *
+ * Changes done since V1.27 to V1.28
+ * 1. did some code formatting
+ *
+ * Changes done since V1.24 to V1.27
+ * 1. Timeout Mechanism implemented for enc424j600_soft_reset function
+ * 2. Timeout Mechanism implemented for enc424j600_wait_for_autoneg function
+ * 3. Window Naming changed to enum
+ * 4. Removed WRITEVERIFY functionality
+ *
+ * Todo List:
+ * 1. Low Power Mode Functionality implementation
+ * 2. Provide Support for On-Chip DMA
+ * 3. Remove mutex_lock wherever not required
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/tcp.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+
+#include "enc424j600_hw.h"
+
+#define DRV_NAME "enc424j600"
+#define DRV_VERSION "1.29"
+
+#define ENC424J600_MSG_DEFAULT \
+	(NETIF_MSG_PROBE | NETIF_MSG_IFUP | NETIF_MSG_IFDOWN | NETIF_MSG_LINK)
+
+#define SPI_TRANSFER_BUF_LEN (4 + MAX_FRAMELEN)
+#define TX_TIMEOUT (4 * HZ)
+
+/* Max TX retries in case of collision as suggested by errata datasheet */
+#define MAX_TX_RETRYCOUNT 16
+
+#define SPI_OPLEN	1
+
+#define SRAMSIZE		0x6000
+#define TXSTART			0x0000
+#define RXSTART			0x1600
+
+static int enc424j600_enable_dma;	/* Enable SPI DMA. Default: 0 (Off) */
+
+enum {
+	RXFILTER_NORMAL,
+	RXFILTER_MULTI,
+	RXFILTER_PROMISC
+};
+enum {
+	RXWINDOW,
+	USERWINDOW,
+	GPWINDOW
+};
+
+/* Driver local data */
+struct enc424j600_net {
+	struct net_device *netdev;
+	struct spi_device *spi;
+	struct mutex lock;
+	struct sk_buff *tx_skb;
+	struct work_struct tx_work;
+	struct work_struct irq_work;
+	struct work_struct setrx_work;
+	struct work_struct restart_work;
+	u8 bank;		/* current register bank selected */
+	u16 next_pk_ptr;	/* next packet pointer within FIFO */
+	u16 max_pk_counter;	/* statistics: max packet counter */
+	u16 tx_retry_count;
+	bool hw_enable;
+	bool full_duplex;
+	bool autoneg;
+	bool speed100;
+	int rxfilter;
+	u32 msg_enable;
+
+	u8 *spi_rx_buf;
+	u8 *spi_tx_buf;
+	dma_addr_t spi_tx_dma;
+	dma_addr_t spi_rx_dma;
+};
+
+/* use ethtool to change the level for any given device */
+static struct {
+	u32 msg_enable;
+} debug = { -1 };
+
+static int enc424j600_spi_trans(struct enc424j600_net *priv, int len)
+{
+	/*modified to suit half duplexed spi */
+	struct spi_transfer tt = {
+		.tx_buf = priv->spi_tx_buf,
+		.len = SPI_OPLEN,
+	};
+	struct spi_transfer tr = {
+		.rx_buf = priv->spi_rx_buf,
+		.len = len,
+	};
+	struct spi_message m;
+	int ret;
+
+	spi_message_init(&m);
+
+	spi_message_add_tail(&tt, &m);
+	spi_message_add_tail(&tr, &m);
+
+	ret = spi_sync(priv->spi, &m);
+
+	if (ret) {
+		dev_err(&priv->spi->dev,
+			"spi transfer failed: ret = %d\n", ret);
+		goto out;
+	}
+
+	memcpy(priv->spi_rx_buf, tr.rx_buf, len);
+
+out:
+	return ret;
+}
+
+/*
+ * Read data from chip SRAM.
+ */
+static int enc424j600_read_sram(struct enc424j600_net *priv,
+				u8 *dst, int len, u16 srcaddr, int window)
+{
+	int ret;
+
+	if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
+		return -EINVAL;
+
+	/* First set the write pointer as per selected window */
+	if (window == RXWINDOW)
+		priv->spi_tx_buf[0] = WRXRDPT;
+	else if (window == USERWINDOW)
+		priv->spi_tx_buf[0] = WUDARDPT;
+	else if (window == GPWINDOW)
+		priv->spi_tx_buf[0] = WGPRDPT;
+
+	priv->spi_tx_buf[1] = srcaddr & 0xFF;
+	priv->spi_tx_buf[2] = srcaddr >> 8;
+	ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
+
+	/* Transfer the data */
+	if (window == RXWINDOW)
+		priv->spi_tx_buf[0] = RRXDATA;
+	else if (window == USERWINDOW)
+		priv->spi_tx_buf[0] = RUDADATA;
+	else if (window == GPWINDOW)
+		priv->spi_tx_buf[0] = RGPDATA;
+
+	ret = enc424j600_spi_trans(priv, len + 1);
+
+	/* Copy the data from the rx buffer */
+	memcpy(dst, &priv->spi_rx_buf[0], len);
+
+	return ret;
+}
+
+/*
+ * Write data to chip SRAM.
+ */
+static int enc424j600_write_sram(struct enc424j600_net *priv,
+				 const u8 *src, int len, u16 dstaddr,
+				 int window)
+{
+	int ret;
+
+	if (len > SPI_TRANSFER_BUF_LEN - 1 || len <= 0)
+		return -EINVAL;
+
+	/* First set the general purpose write pointer */
+	if (window == RXWINDOW)
+		priv->spi_tx_buf[0] = WRXWRPT;
+	else if (window == USERWINDOW)
+		priv->spi_tx_buf[0] = WUDAWRPT;
+	else if (window == GPWINDOW)
+		priv->spi_tx_buf[0] = WGPWRPT;
+
+	priv->spi_tx_buf[1] = dstaddr & 0xFF;
+	priv->spi_tx_buf[2] = dstaddr >> 8;
+	ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
+
+	/* Copy the data to the tx buffer */
+	memcpy(&priv->spi_tx_buf[1], src, len);
+
+	/* Transfer the data */
+	if (window == RXWINDOW)
+		priv->spi_tx_buf[0] = WRXDATA;
+	else if (window == USERWINDOW)
+		priv->spi_tx_buf[0] = WUDADATA;
+	else if (window == GPWINDOW)
+		priv->spi_tx_buf[0] = WGPDATA;
+
+	ret = spi_write(priv->spi, priv->spi_tx_buf, len + 1);
+
+	return ret;
+}
+
+/*
+ * Select the current register bank if necessary to be able to read @addr.
+ */
+static void enc424j600_set_bank(struct enc424j600_net *priv, u8 addr)
+{
+	u8 b = (addr & BANK_MASK) >> BANK_SHIFT;
+
+	/* These registers are present in all banks, no need to switch bank */
+	if (addr >= EUDASTL && addr <= ECON1H)
+		return;
+	if (priv->bank == b)
+		return;
+
+	priv->spi_tx_buf[0] = BXSEL(b);
+
+	enc424j600_spi_trans(priv, 1);
+	priv->bank = b;
+}
+
+/*
+ * Sets and Clears addr register
+ */
+static void enc424j600_write_bits(struct enc424j600_net *priv, u8
addr, u8 bits,
+				  u8 mask)
+{
+	enc424j600_set_bank(priv, addr);
+	priv->spi_tx_buf[0] = bits;
+	priv->spi_tx_buf[1] = mask;
+	spi_write(priv->spi, priv->spi_tx_buf, 2);
+}
+
+/*
+ * Write a 8bit special function register.
+ * The @sfr parameters takes address of the register.
+ * Uses banked write instruction.
+ */
+static int enc424j600_write_8b_sfr(struct enc424j600_net *priv, u8
sfr, u8 data)
+{
+	int ret;
+
+	enc424j600_set_bank(priv, sfr);
+
+	priv->spi_tx_buf[0] = WCR(sfr & ADDR_MASK);
+	priv->spi_tx_buf[1] = data & 0xFF;
+	ret = spi_write(priv->spi, priv->spi_tx_buf, 2);
+
+	return ret;
+}
+
+/*
+ * Read a 8bit special function register.
+ * The @sfr parameters takes address of the register.
+ * Uses banked read instruction.
+ */
+static int enc424j600_read_8b_sfr(struct enc424j600_net *priv,
+				  u8 sfr, u8 *data)
+{
+	int ret;
+
+	enc424j600_set_bank(priv, sfr);
+	priv->spi_tx_buf[0] = RCR(sfr & ADDR_MASK);
+	ret = enc424j600_spi_trans(priv, 2);
+	*data = priv->spi_rx_buf[0];
+
+	return ret;
+}
+
+/*
+ * Write a 16bit special function register.
+ * The @sfr parameters takes address of the low byte of the register.
+ * Takes care of the endiannes & buffers.
+ * Uses banked write instruction.
+ */
+
+static int enc424j600_write_16b_sfr(struct enc424j600_net *priv,
+				    u8 sfr, u16 data)
+{
+	int ret;
+
+	enc424j600_set_bank(priv, sfr);
+
+	priv->spi_tx_buf[0] = WCR(sfr & ADDR_MASK);
+	priv->spi_tx_buf[1] = data & 0xFF;
+	priv->spi_tx_buf[2] = data >> 8;
+	ret = spi_write(priv->spi, priv->spi_tx_buf, 3);
+	if (ret && netif_msg_drv(priv))
+		printk(KERN_DEBUG DRV_NAME ": %s() failed: ret = %d\n",
+		       __func__, ret);
+
+	return ret;
+}
+
+/*
+ * Read a 16bit special function register.
+ * The @sfr parameters takes address of the low byte of the register.
+ * Takes care of the endiannes & buffers.
+ * Uses banked read instruction.
+ */
+static int enc424j600_read_16b_sfr(struct enc424j600_net *priv,
+				   u8 sfr, u16 *data)
+{
+	int ret;
+
+	enc424j600_set_bank(priv, sfr);
+
+	priv->spi_tx_buf[0] = RCR(sfr & ADDR_MASK);
+	priv->spi_tx_buf[1] = 0;
+	priv->spi_tx_buf[2] = 0;
+	priv->spi_tx_buf[3] = 0;
+	ret = enc424j600_spi_trans(priv, 3);
+	*data = priv->spi_rx_buf[0] | priv->spi_rx_buf[1] << (u16) 8;
+
+	return ret;
+}
+
+static unsigned long msec20_to_jiffies;
+
+/*
+ * Wait for bits in register to become equal to @readyMask, but at most 20ms.
+ */
+static int checktimeout_16bit(struct enc424j600_net *priv,
+			      u8 reg, u16 mask, u16 readyMask)
+{
+	unsigned long timeout = jiffies + msec20_to_jiffies;
+	u16 value;
+
+	/* 20 msec timeout read */
+	enc424j600_read_16b_sfr(priv, reg, &value);
+	while ((value & mask) != readyMask) {
+		if (time_after(jiffies, timeout)) {
+			if (netif_msg_drv(priv))
+				dev_dbg(&priv->spi->dev,
+					"reg %02x ready timeout!\n", reg);
+			return -ETIMEDOUT;
+		}
+		cpu_relax();
+		enc424j600_read_16b_sfr(priv, reg, &value);
+	}
+
+	return 0;
+}
+
+static int checktimeout_8bit(struct enc424j600_net *priv,
+			     u8 reg, u8 mask, u8 readyMask)
+{
+	unsigned long timeout = jiffies + msec20_to_jiffies;
+	u8 value;
+
+	/* 20 msec timeout read */
+	enc424j600_read_8b_sfr(priv, reg, &value);
+	while ((value & mask) != readyMask) {
+		if (time_after(jiffies, timeout)) {
+			if (netif_msg_drv(priv))
+				dev_dbg(&priv->spi->dev,
+					"reg %02x ready timeout!\n", reg);
+			return -ETIMEDOUT;
+		}
+		cpu_relax();
+		enc424j600_read_8b_sfr(priv, reg, &value);
+	}
+
+	return 0;
+}
+
+/*
+ * Reset the enc424j600.
+ */
+static int enc424j600_soft_reset(struct enc424j600_net *priv)
+{
+	int ret;
+	u16 eudast;
+
+	if (netif_msg_hw(priv))
+		printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+	enc424j600_write_16b_sfr(priv, EUDASTL, EUDAST_TEST_VAL);
+	ret = checktimeout_16bit(priv, EUDASTL, 0xFFFF, EUDAST_TEST_VAL);
+	if (ret != 0)
+		return ret;
+	ret = checktimeout_16bit(priv, ESTATH, CLKRDY, CLKRDY);
+	if (ret != 0)
+		return ret;
+
+	priv->spi_tx_buf[0] = SETETHRST;
+	enc424j600_spi_trans(priv, 1);
+	/*inline with the datasheet */
+	udelay(25);
+
+	enc424j600_read_16b_sfr(priv, EUDASTL, &eudast);
+	if (netif_msg_hw(priv) && eudast != 0)
+		printk(KERN_DEBUG DRV_NAME
+		       ": %s() EUDASTL is not zero!\n", __func__);
+	/*inline with the datasheet */
+	/*datasheet says to wait for 256 usec atleast */
+	udelay(300);
+	return 0;
+}
+
+/*
+ * PHY register read
+ * PHY registers are not accessed directly, but through the MII
+ */
+static int enc424j600_phy_read(struct enc424j600_net *priv,
+			       u16 address, u16 *data)
+{
+	int ret;
+
+	enc424j600_write_16b_sfr(priv, MIREGADRL,
+				 address | (MIREGADRH_VAL << 8));
+	enc424j600_write_16b_sfr(priv, MICMDL, MIIRD);
+	udelay(26);
+	ret = !checktimeout_8bit(priv, MISTATL, BUSY, 0);
+	enc424j600_write_16b_sfr(priv, MICMDL, 0);
+	enc424j600_read_16b_sfr(priv, MIRDL, data);
+	return ret;
+}
+
+static int enc424j600_phy_write(struct enc424j600_net *priv, u16 address,
+				u16 data)
+{
+	enc424j600_write_16b_sfr(priv, MIREGADRL,
+				 address | (MIREGADRH_VAL << 8));
+	enc424j600_write_16b_sfr(priv, MIWRL, data);
+	udelay(26);
+	return !checktimeout_8bit(priv, MISTATL, BUSY, 0);
+}
+
+/*
+ * Read the hardware MAC address to dev->dev_addr.
+ */
+static int enc424j600_get_hw_macaddr(struct net_device *ndev)
+{
+	struct enc424j600_net *priv = netdev_priv(ndev);
+	u16 maadr1, maadr2, maadr3;
+
+	mutex_lock(&priv->lock);
+
+	if (netif_msg_drv(priv))
+		printk(KERN_INFO DRV_NAME
+		       ": %s: Setting MAC address to %pM\n",
+		       ndev->name, ndev->dev_addr);
+
+	enc424j600_read_16b_sfr(priv, MAADR3L, &maadr3);
+	ndev->dev_addr[5] = maadr3 >> 8;
+	ndev->dev_addr[4] = maadr3 & 0xff;
+	enc424j600_read_16b_sfr(priv, MAADR2L, &maadr2);
+	ndev->dev_addr[3] = maadr2 >> 8;
+	ndev->dev_addr[2] = maadr2 & 0xff;
+	enc424j600_read_16b_sfr(priv, MAADR1L, &maadr1);
+	ndev->dev_addr[1] = maadr1 >> 8;
+	ndev->dev_addr[0] = maadr1 & 0xff;
+
+	mutex_unlock(&priv->lock);
+
+	return 0;
+}
+
+/*
+ * Program the hardware MAC address from dev->dev_addr.
+ */
+static int enc424j600_set_hw_macaddr(struct net_device *ndev)
+{
+	struct enc424j600_net *priv = netdev_priv(ndev);
+
+	mutex_lock(&priv->lock);
+
+	if (priv->hw_enable) {
+		if (netif_msg_drv(priv))
+			printk(KERN_DEBUG DRV_NAME
+			       ": %s() Hardware must be disabled to set "
+			       "Mac address\n", __func__);
+		mutex_unlock(&priv->lock);
+		return -EBUSY;
+	}
+
+	if (netif_msg_drv(priv))
+		printk(KERN_INFO DRV_NAME
+		       ": %s: Setting MAC address to %pM\n",
+		       ndev->name, ndev->dev_addr);
+
+	enc424j600_write_16b_sfr(priv, MAADR3L,
+				 ndev->dev_addr[4] | ndev->dev_addr[5] << 8);
+	enc424j600_write_16b_sfr(priv, MAADR2L,
+				 ndev->dev_addr[2] | ndev->dev_addr[3] << 8);
+	enc424j600_write_16b_sfr(priv, MAADR1L,
+				 ndev->dev_addr[0] | ndev->dev_addr[1] << 8);
+
+	mutex_unlock(&priv->lock);
+
+	return 0;
+}
+
+/*
+ * Store the new hardware address in dev->dev_addr, and update the MAC.
+ */
+static int enc424j600_set_mac_address(struct net_device *dev, void *addr)
+{
+	struct sockaddr *address = addr;
+
+	if (netif_running(dev))
+		return -EBUSY;
+	if (!is_valid_ether_addr(address->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
+	return enc424j600_set_hw_macaddr(dev);
+}
+
+u8 nolock_regb_read(struct enc424j600_net *priv, u8 address)
+{
+	u8 data;
+
+	enc424j600_read_8b_sfr(priv, address, &data);
+	return data;
+}
+
+u16 nolock_regw_read(struct enc424j600_net *priv, u8 address)
+{
+	u16 data;
+
+	enc424j600_read_16b_sfr(priv, address, &data);
+	return data;
+}
+
+/*Debug routine to dump useful register contents*/
+static void enc424j600_dump_regs(struct enc424j600_net *priv, const char *msg)
+{
+	mutex_lock(&priv->lock);
+	printk(KERN_DEBUG DRV_NAME " %s\n"
+	       "Cntrl: ECON1H ECON1L ECON2H ECON2L ESTATH ESTATL  EIRH  "
+	       "EIRL  EIEH  EIEL\n"
+	       "       0x%02x   0x%02x   0x%02x   0x%02x   0x%02x   0x%02x    "
+	       "0x%02x  0x%02x  0x%02x  0x%02x\n"
+	       "MAC  : MACON1 MACON2\n"
+	       "       0x%04x 0x%04x\n"
+	       "Rx   : ERXST  ERXTAIL ERXHEAD  ERXWRPT ERXRDPT ERXFCON MAMXFL\n"
+	       "       0x%04x 0x%04x  0x%04x   0x%04x  0x%04x  0x%04x  0x%04x\n"
+	       "Tx   : ETXST  ETXLEN  MACLCON1 \n"
+	       "       0x%04x 0x%04x  0x%02x\n",
+	       msg,
+	       nolock_regb_read(priv, ECON1H), nolock_regb_read(priv, ECON1L),
+	       nolock_regb_read(priv, ECON2H), nolock_regb_read(priv, ECON2L),
+	       nolock_regb_read(priv, ESTATH), nolock_regb_read(priv, ESTATL),
+	       nolock_regb_read(priv, EIRH), nolock_regb_read(priv, EIRL),
+	       nolock_regb_read(priv, EIEH), nolock_regb_read(priv, EIEL),
+	       nolock_regw_read(priv, MACON1L), nolock_regw_read(priv, MACON2L),
+	       nolock_regw_read(priv, ERXSTL), nolock_regw_read(priv, ERXTAILL),
+	       nolock_regw_read(priv, ERXHEADL),
+	       nolock_regw_read(priv, ERXWRPTL), nolock_regw_read(priv,
+								  ERXRDPTL),
+	       nolock_regw_read(priv, ERXFCONL), nolock_regw_read(priv,
+								  MAMXFLL),
+	       nolock_regw_read(priv, ETXSTL), nolock_regw_read(priv, ETXLENL),
+	       nolock_regw_read(priv, MACLCONL));
+	mutex_unlock(&priv->lock);
+}
+
+/*
+ * TODO: Check the functionality
+ * Low power mode shrinks power consumption about 100x, so we'd like
+ * the chip to be in that mode whenever it's inactive. (However, we
+ * can't stay in lowpower mode during suspend with WOL active.)
+ */
+static void enc424j600_lowpower(struct enc424j600_net *priv, bool is_low)
+{
+
+	if (netif_msg_drv(priv))
+		dev_dbg(&priv->spi->dev, "%s power...\n",
+			is_low ? "low" : "high");
+
+#if 0
+	mutex_lock(&priv->lock);
+	if (is_low) {
+		nolock_reg_bfclr(priv, ECON1, ECON1_RXEN);
+		checktimeout_8bit(priv, ESTAT, ESTAT_RXBUSY, 0);
+		checktimeout_8bit(priv, ECON1, ECON1_TXRTS, 0);
+		/* ECON2_VRPS was set during initialization */
+		nolock_reg_bfset(priv, ECON2, ECON2_PWRSV);
+	} else {
+		nolock_reg_bfclr(priv, ECON2, ECON2_PWRSV);
+		checktimeout_8bit(priv, ESTAT, ESTAT_CLKRDY, ESTAT_CLKRDY);
+		/* caller sets ECON1_RXEN */
+	}
+	mutex_unlock(&priv->lock);
+#endif
+}
+
+static unsigned long msec2000_to_jiffies;
+/* Waits for autonegotiation to complete. */
+static int enc424j600_wait_for_autoneg(struct enc424j600_net *priv)
+{
+	unsigned long timeout = jiffies + msec2000_to_jiffies;
+	u16 value;
+
+	/* 20 msec timeout read */
+	enc424j600_phy_read(priv, PHSTAT1, &value);
+	while ((value & ANDONE) == 0) {
+		if (time_after(jiffies, timeout)) {
+			if (netif_msg_drv(priv))
+				dev_dbg(&priv->spi->dev,
+					"reg %02x ready timeout!\n", PHSTAT1);
+			return -ETIMEDOUT;
+		}
+		cpu_relax();
+		enc424j600_phy_read(priv, PHSTAT1, &value);
+	}
+	return 0;
+
+}
+
+/*
+ * Reset and initialize the chip, but don't enable interrupts and don't
+ * start receiving yet.
+ */
+static int enc424j600_hw_init(struct enc424j600_net *priv)
+{
+	u8 eidledl;
+	u16 phcon1;
+	u16 macon2;
+	u16 econ1l;
+	/*priv->autoneg = AUTONEG_ENABLE; */
+
+	if (netif_msg_drv(priv))
+		printk(KERN_DEBUG DRV_NAME ": %s() - %s\n", __func__,
+		       priv->autoneg ? "Autoneg" : (priv->
+						    full_duplex ? "FullDuplex" :
+						    "HalfDuplex"));
+
+	mutex_lock(&priv->lock);
+
+	priv->bank = 0;
+	priv->hw_enable = false;
+	priv->tx_retry_count = 0;
+	priv->max_pk_counter = 0;
+	priv->rxfilter = RXFILTER_NORMAL;
+
+	if (enc424j600_soft_reset(priv) != 0)
+		return 0;
+
+	/*
+	 * Check the device id and silicon revision id.
+	 */
+	enc424j600_read_8b_sfr(priv, EIDLEDL, &eidledl);
+
+	if ((eidledl & DEVID_MASK) >> DEVID_SHIFT != ENC424J600_DEV_ID) {
+		if (netif_msg_drv(priv))
+			printk(KERN_DEBUG DRV_NAME
+			       ": %s() Invalid device ID: %d\n", __func__,
+			       (eidledl & DEVID_MASK) >> DEVID_SHIFT);
+		return 0;
+	}
+
+	if (netif_msg_drv(priv))
+		printk(KERN_INFO DRV_NAME ": Silicon revision ID: 0x%02x\n",
+		       (eidledl & REVID_MASK) >> REVID_SHIFT);
+
+	enc424j600_write_16b_sfr(priv, ETXSTL, TXSTART);
+	enc424j600_write_16b_sfr(priv, ERXSTL, RXSTART);
+
+	priv->next_pk_ptr = RXSTART;
+	enc424j600_write_16b_sfr(priv, ERXTAILL, SRAMSIZE - 2);
+	enc424j600_write_16b_sfr(priv, ERXFCONL, UCEN | BCEN | CRCEN | RUNTEN);
+
+	enc424j600_phy_write(priv, PHANA, PHANA_DEFAULT);
+
+	/* PHCON1 */
+	phcon1 = 0;
+	if (priv->autoneg) {
+		/* Enable autonegotiation and renegotiate */
+		phcon1 |= ANEN | RENEG;
+	} else {
+		if (priv->speed100)
+			phcon1 |= SPD100;
+		if (priv->full_duplex)
+			phcon1 |= PFULDPX;
+	}
+	enc424j600_phy_write(priv, PHCON1, phcon1);
+
+	/* MACON2
+	 * defer transmission if collision occurs (only for half duplex)
+	 * pad to 60 or 64 bytes and append CRC
+	 * enable receiving huge frames (instead of limiting packet size) */
+	macon2 = MACON2_DEFER | PADCFG2 | PADCFG0 | TXCRCEN | HFRMEN;
+
+	/* If autonegotiation is enabled, we have to wait untill it finishes
+	 * and set the PHYDPX bit in MACON2 correctly */
+	if (priv->autoneg) {
+		u8 estath;
+		if (!enc424j600_wait_for_autoneg(priv)) {
+			/* read the PHYDPX bit in ESTAT and set FULDPX in
+			   MACON2 accordingly */
+			enc424j600_read_8b_sfr(priv, ESTATH, &estath);
+			if (estath & PHYDPX)
+				macon2 |= FULDPX;
+		} else		/*if timedout, just disable autoneg */
+			priv->autoneg = AUTONEG_DISABLE;
+	} else if (priv->full_duplex)
+		macon2 |= FULDPX;
+
+	enc424j600_write_16b_sfr(priv, MACON2L, macon2);
+
+	/* MAIPGL
+	 * Recomended values for inter packet gaps */
+	if (!priv->autoneg) {
+		enc424j600_write_16b_sfr(priv, MAIPGL,
+					 MAIPGL_VAL | (MAIPGH_VAL << 8));
+	}
+
+	/*
+	 * Select enabled interrupts, but don't set the global
+	 * interrupt enable flag.
+	 */
+
+	enc424j600_write_16b_sfr(priv, EIEL,
+				 LINKIE << 8 | PKTIE | DMAIE | TXIE | TXABTIE |
+				 RXABTIE);
+
+	enc424j600_read_16b_sfr(priv, ECON1L, &econ1l);
+	econ1l |= (RXEN);
+	enc424j600_write_16b_sfr(priv, ECON1L, econ1l);
+
+	mutex_unlock(&priv->lock);
+
+	if (netif_msg_hw(priv))
+		enc424j600_dump_regs(priv, "Hw initialized.");
+
+	return 1;
+}
+
+static void enc424j600_hw_enable(struct enc424j600_net *priv)
+{
+	if (netif_msg_hw(priv))
+		printk(KERN_DEBUG DRV_NAME ": %s() enabling interrupts.\n",
+		       __func__);
+
+	mutex_lock(&priv->lock);
+
+	/* Clear any pending interrupts */
+	enc424j600_write_16b_sfr(priv, EIRL, 0);
+
+	/* Enable global interrupt flag */
+	enc424j600_write_bits(priv, EIEH, BFS(EIEH), INTIE);
+
+	/* enable receive logic */
+	enc424j600_write_bits(priv, ECON1L, BFS(ECON1L), RXEN);
+	priv->hw_enable = true;
+	mutex_unlock(&priv->lock);
+}
+
+static void enc424j600_hw_disable(struct enc424j600_net *priv)
+{
+	if (netif_msg_hw(priv))
+		printk(KERN_DEBUG DRV_NAME ": %s() disabling interrupts.\n",
+		       __func__);
+
+	mutex_lock(&priv->lock);
+
+	/* disable receive logic */
+	enc424j600_write_bits(priv, ECON1L, BFC(ECON1L), RXEN);
+
+	/* Disable global interrupt flag */
+	enc424j600_write_bits(priv, EIEH, BFC(EIEH), INTIE);
+
+	priv->hw_enable = false;
+
+	mutex_unlock(&priv->lock);
+}
+
+static int
+enc424j600_setlink(struct net_device *ndev, u8 autoneg, u16 speed, u8 duplex)
+{
+	struct enc424j600_net *priv = netdev_priv(ndev);
+	int ret = 0;
+
+	if (!priv->hw_enable) {
+		/* link is in low power mode now; duplex setting
+		 * will take effect on next enc424j600_hw_init().
+		 */
+		if (speed == SPEED_10 || speed == SPEED_100) {
+			priv->autoneg = (autoneg == AUTONEG_ENABLE);
+			priv->full_duplex = (duplex == DUPLEX_FULL);
+			priv->speed100 = (speed == SPEED_100);
+		} else {
+			if (netif_msg_link(priv))
+				dev_warn(&ndev->dev,
+					 "unsupported link setting\n");
+			/*speeds other than SPEED_10 and SPEED_100 */
+			/*are not supported by chip */
+			ret = -EOPNOTSUPP;
+		}
+	} else {
+		if (netif_msg_link(priv))
+			dev_warn(&ndev->dev, "Warning: hw must be disabled "
+				 "to set link mode\n");
+		ret = -EBUSY;
+	}
+	return ret;
+}
+
+/*
+ * Receive Status vector
+ */
+static void enc424j600_dump_rsv(struct enc424j600_net *priv, const char *msg,
+				u16 pk_ptr, int len, u16 sts)
+{
+	printk(KERN_DEBUG DRV_NAME ": %s - NextPk: 0x%04x - RSV:\n",
+	       msg, pk_ptr);
+	printk(KERN_DEBUG DRV_NAME ": ByteCount: %d, DribbleNibble: %d\n", len,
+	       RSV_GETBIT(sts, RSV_DRIBBLENIBBLE));
+	printk(KERN_DEBUG DRV_NAME ": RxOK: %d, CRCErr:%d, LenChkErr: %d,"
+	       " LenOutOfRange: %d\n", RSV_GETBIT(sts, RSV_RXOK),
+	       RSV_GETBIT(sts, RSV_CRCERROR),
+	       RSV_GETBIT(sts, RSV_LENCHECKERR),
+	       RSV_GETBIT(sts, RSV_LENOUTOFRANGE));
+	printk(KERN_DEBUG DRV_NAME ": Multicast: %d, Broadcast: %d, "
+	       "LongDropEvent: %d, CarrierEvent: %d\n",
+	       RSV_GETBIT(sts, RSV_RXMULTICAST),
+	       RSV_GETBIT(sts, RSV_RXBROADCAST),
+	       RSV_GETBIT(sts, RSV_RXLONGEVDROPEV),
+	       RSV_GETBIT(sts, RSV_CARRIEREV));
+	printk(KERN_DEBUG DRV_NAME ": ControlFrame: %d, PauseFrame: %d,"
+	       " UnknownOp: %d, VLanTagFrame: %d\n",
+	       RSV_GETBIT(sts, RSV_RXCONTROLFRAME),
+	       RSV_GETBIT(sts, RSV_RXPAUSEFRAME),
+	       RSV_GETBIT(sts, RSV_RXUNKNOWNOPCODE),
+	       RSV_GETBIT(sts, RSV_RXTYPEVLAN));
+}
+
+static void dump_packet(const char *msg, int len, const char *data)
+{
+
+	printk(KERN_ALERT ": %s - packet len:%d\n", msg, len);
+	print_hex_dump(KERN_ALERT, "pk data: ", DUMP_PREFIX_OFFSET, 16, 1,
+		       data, len, true);
+}
+
+/*
+ * Calculate wrap around when reading beyond the end of the RX buffer
+ */
+static u16 rx_packet_start(u16 ptr)
+{
+	if (ptr + RSV_SIZE > RXEND_INIT)
+		return (ptr + RSV_SIZE) - (RXEND_INIT - RXSTART + 1);
+	else
+		return ptr + RSV_SIZE;
+}
+
+/*
+ * ERXRDPT need to be set always at odd addresses, refer to errata datasheet
+ */
+static u16 erxrdpt_workaround(u16 next_packet_ptr, u16 start, u16 end)
+{
+	u16 erxrdpt;
+
+	if ((next_packet_ptr - 1 < start) || (next_packet_ptr - 1 > end))
+		erxrdpt = end;
+	else
+		erxrdpt = next_packet_ptr - 1;
+
+	return erxrdpt;
+}
+
+static void nolock_rxfifo_init(struct enc424j600_net *priv, u16 start, u16 end)
+{
+	u16 erxrdpt;
+
+	if (start > 0x5FFF || end > 0x5FFF || start > end) {
+		if (netif_msg_drv(priv))
+			printk(KERN_ERR DRV_NAME ": %s(%d, %d) RXFIFO "
+			       "bad parameters!\n", __func__, start, end);
+		return;
+	}
+	/* set receive buffer start + end */
+	priv->next_pk_ptr = start;
+	enc424j600_write_16b_sfr(priv, ERXSTL, start);
+	erxrdpt = erxrdpt_workaround(priv->next_pk_ptr, start, end);
+	enc424j600_write_16b_sfr(priv, ERXRDPTL, erxrdpt);
+	enc424j600_write_16b_sfr(priv, ERXTAILL, end);
+}
+
+/*
+ * Hardware receive function.
+ * Read the buffer memory, update the FIFO pointer to free the buffer,
+ * check the status vector and decrement the packet counter.
+ */
+static void enc424j600_hw_rx(struct net_device *ndev)
+{
+	struct enc424j600_net *priv = netdev_priv(ndev);
+	struct sk_buff *skb = NULL;
+	u16 erxrdpt, next_packet, rxstat;
+	u8 pkcnt;
+	u16 head, tail;
+	u8 rsv[RSV_SIZE];
+	u16 newrxtail;
+	int len;
+
+	if (netif_msg_rx_status(priv))
+		printk(KERN_DEBUG DRV_NAME ": RX pk_addr:0x%04x\n",
+		       priv->next_pk_ptr);
+	if (unlikely(priv->next_pk_ptr > RXEND_INIT)) {
+		if (netif_msg_rx_err(priv))
+			dev_err(&ndev->dev,
+				"%s() Invalid packet address!! 0x%04x\n",
+				__func__, priv->next_pk_ptr);
+		mutex_lock(&priv->lock);
+		enc424j600_write_bits(priv, ECON1L, BFC(ECON1L), RXEN);
+		enc424j600_write_bits(priv, ECON2L, BFS(ECON2L), RXRST);
+		enc424j600_write_bits(priv, ECON2L, BFC(ECON2L), RXRST);
+		nolock_rxfifo_init(priv, RXSTART, RXEND_INIT);
+		enc424j600_write_bits(priv, EIRL, BFC(EIRL), RXABTIF);
+		enc424j600_write_bits(priv, ECON1L, BFS(ECON1L), RXEN);
+		mutex_unlock(&priv->lock);
+		ndev->stats.rx_errors++;
+		return;
+	}
+
+	/* Read next packet pointer and rx status vector */
+	enc424j600_read_sram(priv, rsv, sizeof(rsv), priv->next_pk_ptr,
+			     RXWINDOW);
+
+	next_packet = rsv[1];
+	next_packet <<= 8;
+	next_packet |= rsv[0];
+
+	len = rsv[3];
+	len <<= 8;
+	len |= rsv[2];
+
+	rxstat = rsv[5];
+	rxstat <<= 8;
+	rxstat |= rsv[4];
+
+	if (netif_msg_rx_status(priv))
+		enc424j600_dump_rsv(priv, __func__, next_packet, len, rxstat);
+
+	if (!RSV_GETBIT(rxstat, RSV_RXOK) || len > MAX_FRAMELEN) {
+		if (netif_msg_rx_err(priv))
+			dev_err(&ndev->dev, "Rx Error (%04x)\n", rxstat);
+		ndev->stats.rx_errors++;
+		if (RSV_GETBIT(rxstat, RSV_CRCERROR))
+			ndev->stats.rx_crc_errors++;
+		if (RSV_GETBIT(rxstat, RSV_LENCHECKERR))
+			ndev->stats.rx_frame_errors++;
+		if (len > MAX_FRAMELEN)
+			ndev->stats.rx_over_errors++;
+	} else {
+		skb = dev_alloc_skb(len + NET_IP_ALIGN);
+		if (!skb) {
+			if (netif_msg_rx_err(priv))
+				dev_err(&ndev->dev,
+					"out of memory for Rx'd frame\n");
+			ndev->stats.rx_dropped++;
+		} else {
+			skb->dev = ndev;
+			skb_reserve(skb, NET_IP_ALIGN);
+
+			/* copy the packet from the receive buffer */
+			enc424j600_read_sram(priv, skb_put(skb, len), len,
+					     rx_packet_start(priv->next_pk_ptr),
+					     RXWINDOW);
+
+			if (netif_msg_pktdata(priv))
+				dump_packet(__func__, skb->len, skb->data);
+			skb->protocol = eth_type_trans(skb, ndev);
+			/* update statistics */
+			ndev->stats.rx_packets++;
+			ndev->stats.rx_bytes += len;
+			netif_rx_ni(skb);
+		}
+	}
+	newrxtail = next_packet - 2;
+	if (next_packet == RXSTART)
+		newrxtail = SRAMSIZE - 2;
+
+	enc424j600_write_16b_sfr(priv, ERXTAILL, newrxtail);
+	/*
+	 * Move the RX read pointer to the start of the next
+	 * received packet.
+	 * This frees the memory we just read out
+	 */
+	erxrdpt = erxrdpt_workaround(next_packet, RXSTART, RXEND_INIT);
+	if (netif_msg_hw(priv))
+		printk(KERN_DEBUG DRV_NAME ": %s() ERXRDPT:0x%04x\n", __func__,
+		       erxrdpt);
+
+	/*TODO: remove mutex_lock wherever not required */
+	mutex_lock(&priv->lock);
+	enc424j600_write_16b_sfr(priv, ERXRDPTL, erxrdpt);
+
+	priv->next_pk_ptr = next_packet;
+	enc424j600_read_8b_sfr(priv, ESTATL, &pkcnt);
+	enc424j600_read_16b_sfr(priv, ERXHEADL, &head);
+	enc424j600_read_16b_sfr(priv, ERXTAILL, &tail);
+	/* we are done with this packet, decrement the packet counter */
+	enc424j600_write_bits(priv, ECON1H, BFS(ECON1H), PKTDEC);
+
+	mutex_unlock(&priv->lock);
+}
+
+/*
+ * Access the PHY to determine link status
+ */
+static void enc424j600_check_link_status(struct enc424j600_net *priv)
+{
+	u8 estath;
+	u16 macon2;
+
+	enc424j600_read_8b_sfr(priv, ESTATH, &estath);
+	if (estath & PHYLNK) {
+		if (priv->autoneg) {
+			if (!enc424j600_wait_for_autoneg(priv)) {
+				if (estath & PHYDPX) {
+					printk(KERN_ALERT "Full Duplex");
+					enc424j600_read_16b_sfr(priv, MACON2L,
+								&macon2);
+					macon2 |= FULDPX;
+					enc424j600_write_16b_sfr(priv, MACON2L,
+								 macon2);
+				}
+			} else	/*if timed out, disable autoneg and continue */
+				priv->autoneg = AUTONEG_DISABLE;
+		}
+		netif_carrier_on(priv->netdev);
+		if (netif_msg_ifup(priv))
+			dev_info(&(priv->netdev->dev), "link up\n");
+	} else {
+		if (netif_msg_ifdown(priv))
+			dev_info(&(priv->netdev->dev), "link down\n");
+		netif_carrier_off(priv->netdev);
+	}
+}
+
+static void enc424j600_tx_clear(struct enc424j600_net *priv, bool err)
+{
+	struct net_device *ndev = priv->netdev;
+
+	if (err)
+		ndev->stats.tx_errors++;
+	else
+		ndev->stats.tx_packets++;
+
+	if (priv->tx_skb) {
+		if (!err)
+			ndev->stats.tx_bytes += priv->tx_skb->len;
+		dev_kfree_skb(priv->tx_skb);
+		priv->tx_skb = NULL;
+	}
+
+	netif_wake_queue(ndev);
+}
+
+static int enc424j600_int_rx_abbort_handler(struct enc424j600_net *priv,
+					    int loop)
+{
+	loop++;
+	if (netif_msg_intr(priv))
+		printk(KERN_DEBUG DRV_NAME ": intRXAbt(%d)\n", loop);
+	mutex_lock(&priv->lock);
+	priv->netdev->stats.rx_dropped++;
+	enc424j600_write_bits(priv, EIRL, BFC(EIRL), RXABTIF);
+	mutex_unlock(&priv->lock);
+
+	return loop;
+}
+
+static int enc424j600_int_link_handler(struct enc424j600_net *priv, int loop)
+{
+	loop++;
+	if (netif_msg_intr(priv))
+		printk(KERN_DEBUG DRV_NAME ": intLINK(%d)\n", loop);
+
+	/* we check more than is necessary here --
+	 * only PHYLNK would be needed. */
+	enc424j600_check_link_status(priv);
+
+	return loop;
+}
+
+static int enc424j600_int_tx_handler(struct enc424j600_net *priv, int loop)
+{
+	loop++;
+	if (netif_msg_intr(priv))
+		printk(KERN_DEBUG DRV_NAME ": intTX(%d)\n", loop);
+
+	mutex_lock(&priv->lock);
+	enc424j600_tx_clear(priv, false);
+	enc424j600_write_bits(priv, EIRL, BFC(EIRL), TXIF);
+	mutex_unlock(&priv->lock);
+
+	return loop;
+}
+
+static int enc424j600_int_tx_err_handler(struct enc424j600_net *priv, int loop)
+{
+	u8 etxstat;
+	loop++;
+
+	if (netif_msg_intr(priv))
+		printk(KERN_DEBUG DRV_NAME ": intTXErr(%d)\n", loop);
+
+	mutex_lock(&priv->lock);
+
+	enc424j600_read_8b_sfr(priv, ETXSTATH, &etxstat);
+
+	if (etxstat & LATECOL) {
+		if (netif_msg_tx_err(priv))
+			printk(KERN_DEBUG DRV_NAME
+			       ": Late collision TXErr (%d)\n",
+			       priv->tx_retry_count);
+		if (priv->tx_retry_count++ < MAX_TX_RETRYCOUNT)
+			enc424j600_write_bits(priv, ECON1L, BFS(ECON1L), TXRTS);
+		else
+			enc424j600_tx_clear(priv, true);
+	} else if (etxstat & MAXCOL) {
+		if (netif_msg_tx_err(priv))
+			printk(KERN_DEBUG DRV_NAME ": Max collisions TXErr\n");
+		enc424j600_tx_clear(priv, true);
+	} else {
+		enc424j600_tx_clear(priv, true);
+	}
+
+	mutex_unlock(&priv->lock);
+
+	return loop;
+}
+
+static int enc424j600_int_received_packet_handler(struct enc424j600_net *priv)
+{
+	uint8_t pk_counter;
+	int ret;
+
+	enc424j600_read_8b_sfr(priv, ESTATL, &pk_counter);
+	if (pk_counter && netif_msg_intr(priv))
+		printk(KERN_DEBUG DRV_NAME ": intRX, pk_cnt: %d\n", pk_counter);
+	if (pk_counter > priv->max_pk_counter) {
+		/* update statistics */
+		priv->max_pk_counter = pk_counter;
+		if (netif_msg_rx_status(priv) && priv->max_pk_counter > 1)
+			printk(KERN_DEBUG DRV_NAME ": RX max_pk_cnt: %d\n",
+			       priv->max_pk_counter);
+	}
+	ret = pk_counter;
+	while (pk_counter-- > 0)
+		enc424j600_hw_rx(priv->netdev);
+	return ret;
+}
+
+static void enc424j600_irq_work_handler(struct work_struct *work)
+{
+
+	struct enc424j600_net *priv =
+	    container_of(work, struct enc424j600_net, irq_work);
+	int loop;
+
+	if (netif_msg_intr(priv))
+		printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+	/* disable further interrupts */
+	enc424j600_write_bits(priv, EIEH, BFC(EIEH), INTIE);
+
+	do {
+		u16 intflags;
+		enc424j600_read_16b_sfr(priv, EIRL, &intflags);
+		loop = 0;
+
+		/* LINK changed handler */
+		if ((intflags & LINKIF) != 0)
+			loop = enc424j600_int_link_handler(priv, loop);
+
+		/* TX complete handler */
+		if ((intflags & TXIF) != 0)
+			loop = enc424j600_int_tx_handler(priv, loop);
+
+		/* TX Error handler */
+		if ((intflags & TXABTIF) != 0) {
+			printk(KERN_ALERT "ABORTING TRANSMITTING PACKET");
+			loop = enc424j600_int_tx_err_handler(priv, loop);
+		}
+		/* RX Error handler */
+		if ((intflags & RXABTIF) != 0) {
+			printk(KERN_ALERT "ABORTING RECEIVED PACKET");
+			loop = enc424j600_int_rx_abbort_handler(priv, loop);
+		}
+		/* RX handler */
+		if ((intflags & PKTIF) != 0)
+			loop = enc424j600_int_received_packet_handler(priv);
+		enc424j600_write_bits(priv, EIRL, BFC(EIRL), intflags && 0xff);
+		enc424j600_write_bits(priv, EIRH, BFC(EIRH), intflags >> 8);
+	} while (loop);
+	/* re-enable interrupts */
+	enc424j600_write_bits(priv, EIEH, BFS(EIEH), INTIE);
+
+	if (netif_msg_intr(priv))
+		printk(KERN_DEBUG DRV_NAME ": %s() exit\n", __func__);
+}
+
+void locked_reg_bfset(struct enc424j600_net *priv, u8 addr, u8 mask)
+{
+	mutex_lock(&priv->lock);
+	enc424j600_write_bits(priv, addr, BFS(addr), mask);
+	mutex_unlock(&priv->lock);
+}
+
+/*
+ * Hardware transmit function.
+ * Fill the buffer memory and send the contents of the transmit buffer
+ * onto the network
+ */
+static void enc424j600_hw_tx(struct enc424j600_net *priv)
+{
+	if (!priv->tx_skb) {
+		enc424j600_tx_clear(priv, false);
+		return;
+	}
+
+	if (netif_msg_tx_queued(priv))
+		printk(KERN_DEBUG DRV_NAME ": Tx Packet Len:%d\n",
+		       priv->tx_skb->len);
+
+	if (netif_msg_pktdata(priv))
+		dump_packet(__func__, priv->tx_skb->len, priv->tx_skb->data);
+
+	enc424j600_write_sram(priv, priv->tx_skb->data, priv->tx_skb->len,
+			      TXSTART, GPWINDOW);
+
+	/* Set the tx pointer to start of general purpose SRAM area */
+	enc424j600_write_16b_sfr(priv, ETXSTL, TXSTART);
+
+	/* Write the transfer length */
+	enc424j600_write_16b_sfr(priv, ETXLENL, priv->tx_skb->len);
+
+	/* set TX request flag */
+	locked_reg_bfset(priv, ECON1L, TXRTS);
+}
+
+static int enc424j600_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	struct enc424j600_net *priv = netdev_priv(dev);
+
+	if (netif_msg_tx_queued(priv))
+		printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+	/* If some error occurs while trying to transmit this
+	 * packet, you should return '1' from this function.
+	 * In such a case you _may not_ do anything to the
+	 * SKB, it is still owned by the network queueing
+	 * layer when an error is returned. This means you
+	 * may not modify any SKB fields, you may not free
+	 * the SKB, etc.
+	 */
+	netif_stop_queue(dev);
+
+	/* save the timestamp */
+	priv->netdev->trans_start = jiffies;
+	/* Remember the skb for deferred processing */
+	priv->tx_skb = skb;
+	schedule_work(&priv->tx_work);
+
+	return NETDEV_TX_OK;
+}
+
+static void enc424j600_tx_work_handler(struct work_struct *work)
+{
+	struct enc424j600_net *priv =
+	    container_of(work, struct enc424j600_net, tx_work);
+
+	/* actual delivery of data */
+	enc424j600_hw_tx(priv);
+}
+
+static irqreturn_t enc424j600_irq(int irq, void *dev_id)
+{
+	struct enc424j600_net *priv = dev_id;
+
+	/*
+	 * Can't do anything in interrupt context because we need to
+	 * block (spi_sync() is blocking) so fire of the interrupt
+	 * handling workqueue.
+	 * Remember that we access enc424j600 registers through SPI bus
+	 * via spi_sync() call.
+	 */
+	schedule_work(&priv->irq_work);
+
+	return IRQ_HANDLED;
+}
+
+static void enc424j600_tx_timeout(struct net_device *ndev)
+{
+	struct enc424j600_net *priv = netdev_priv(ndev);
+
+	if (netif_msg_timer(priv))
+		dev_err(&ndev->dev, DRV_NAME " tx timeout\n");
+
+	ndev->stats.tx_errors++;
+	/* can't restart safely under softirq */
+	schedule_work(&priv->restart_work);
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+static int enc424j600_net_open(struct net_device *dev)
+{
+	struct enc424j600_net *priv = netdev_priv(dev);
+
+	if (netif_msg_drv(priv))
+		printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+	if (!is_valid_ether_addr(dev->dev_addr)) {
+		if (netif_msg_ifup(priv))
+			dev_err(&dev->dev, "invalid MAC address %pM\n",
+				dev->dev_addr);
+		return -EADDRNOTAVAIL;
+	}
+	/* Reset the hardware here (and take it out of low power mode) */
+	enc424j600_lowpower(priv, false);
+	enc424j600_hw_disable(priv);
+	if (!enc424j600_hw_init(priv)) {
+		if (netif_msg_ifup(priv))
+			dev_err(&dev->dev, "hw_reset() failed\n");
+		return -EINVAL;
+	}
+	/* Update the MAC address (in case user has changed it) */
+	enc424j600_set_hw_macaddr(dev);
+	/* Enable interrupts */
+	enc424j600_hw_enable(priv);
+	/* check link status */
+	enc424j600_check_link_status(priv);
+	/* We are now ready to accept transmit requests from
+	 * the queueing layer of the networking.
+	 */
+	netif_start_queue(dev);
+
+	return 0;
+}
+
+/* The inverse routine to net_open(). */
+static int enc424j600_net_close(struct net_device *dev)
+{
+	struct enc424j600_net *priv = netdev_priv(dev);
+
+	if (netif_msg_drv(priv))
+		printk(KERN_DEBUG DRV_NAME ": %s() enter\n", __func__);
+
+	enc424j600_hw_disable(priv);
+	enc424j600_lowpower(priv, true);
+	netif_stop_queue(dev);
+
+	return 0;
+}
+
+/*
+ * Set or clear the multicast filter for this adapter
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, filter out multicast packets
+ * num_addrs > 0 Multicast mode, receive normal and MC packets
+ */
+static void enc424j600_set_multicast_list(struct net_device *dev)
+{
+	struct enc424j600_net *priv = netdev_priv(dev);
+	int oldfilter = priv->rxfilter;
+
+	if (dev->flags & IFF_PROMISC) {
+		if (netif_msg_link(priv))
+			dev_info(&dev->dev, "promiscuous mode\n");
+		priv->rxfilter = RXFILTER_PROMISC;
+	} else if ((dev->flags & IFF_ALLMULTI) || dev->mc_count) {
+		if (netif_msg_link(priv))
+			dev_info(&dev->dev, "%smulticast mode\n",
+				 (dev->flags & IFF_ALLMULTI) ? "all-" : "");
+		priv->rxfilter = RXFILTER_MULTI;
+	} else {
+		if (netif_msg_link(priv))
+			dev_info(&dev->dev, "normal mode\n");
+		priv->rxfilter = RXFILTER_NORMAL;
+	}
+
+	if (oldfilter != priv->rxfilter)
+		schedule_work(&priv->setrx_work);
+}
+
+void locked_regb_write(struct enc424j600_net *priv, u8 address, u8 data)
+{
+	mutex_lock(&priv->lock);
+	enc424j600_write_8b_sfr(priv, address, data);
+	mutex_unlock(&priv->lock);
+}
+
+static void enc424j600_setrx_work_handler(struct work_struct *work)
+{
+	u16 macon1;
+	struct enc424j600_net *priv =
+	    container_of(work, struct enc424j600_net, setrx_work);
+
+	if (priv->rxfilter == RXFILTER_PROMISC) {
+		if (netif_msg_drv(priv))
+			printk(KERN_DEBUG DRV_NAME ": promiscuous mode\n");
+		enc424j600_read_16b_sfr(priv, MACON1L, &macon1);
+		macon1 = macon1 | PASSALL;
+		enc424j600_write_16b_sfr(priv, MACON1L, macon1);
+		locked_regb_write(priv, ERXFCONL, UCEN | MCEN | NOTMEEN);
+	} else if (priv->rxfilter == RXFILTER_MULTI) {
+		if (netif_msg_drv(priv))
+			printk(KERN_DEBUG DRV_NAME ": multicast mode\n");
+		locked_regb_write(priv, ERXFCONL, UCEN | CRCEN | BCEN | MCEN);
+
+	} else {
+		if (netif_msg_drv(priv))
+			printk(KERN_DEBUG DRV_NAME ": normal mode\n");
+		locked_regb_write(priv, ERXFCONL, UCEN | CRCEN | BCEN);
+
+	}
+}
+
+static void enc424j600_restart_work_handler(struct work_struct *work)
+{
+	struct enc424j600_net *priv =
+	    container_of(work, struct enc424j600_net, restart_work);
+	struct net_device *ndev = priv->netdev;
+	int ret;
+
+	rtnl_lock();
+	if (netif_running(ndev)) {
+		enc424j600_net_close(ndev);
+		ret = enc424j600_net_open(ndev);
+		if (unlikely(ret)) {
+			dev_info(&ndev->dev, " could not restart %d\n", ret);
+			dev_close(ndev);
+		}
+	}
+	rtnl_unlock();
+}
+
+/* ......................... ETHTOOL SUPPORT ........................... */
+
+static void
+enc424j600_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+	strlcpy(info->driver, DRV_NAME, sizeof(info->driver));
+	strlcpy(info->version, DRV_VERSION, sizeof(info->version));
+	strlcpy(info->bus_info,
+		dev_name(dev->dev.parent), sizeof(info->bus_info));
+}
+
+static int
+enc424j600_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct enc424j600_net *priv = netdev_priv(dev);
+
+	cmd->transceiver = XCVR_INTERNAL;
+	cmd->supported = SUPPORTED_10baseT_Half
+	    | SUPPORTED_10baseT_Full
+	    | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_TP;
+
+	cmd->speed = priv->speed100 ? SPEED_100 : SPEED_10;
+	cmd->duplex = priv->full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
+	cmd->port = PORT_TP;
+	cmd->autoneg = priv->autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE;
+
+	return 0;
+}
+
+static int
+enc424j600_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	return enc424j600_setlink(dev, cmd->autoneg, cmd->speed, cmd->duplex);
+}
+
+static u32 enc424j600_get_msglevel(struct net_device *dev)
+{
+	struct enc424j600_net *priv = netdev_priv(dev);
+
+	return priv->msg_enable;
+}
+
+static void enc424j600_set_msglevel(struct net_device *dev, u32 val)
+{
+	struct enc424j600_net *priv = netdev_priv(dev);
+
+	priv->msg_enable = val;
+}
+
+static const struct ethtool_ops enc424j600_ethtool_ops = {
+	.get_settings = enc424j600_get_settings,
+	.set_settings = enc424j600_set_settings,
+	.get_drvinfo = enc424j600_get_drvinfo,
+	.get_msglevel = enc424j600_get_msglevel,
+	.set_msglevel = enc424j600_set_msglevel,
+	.get_regs = enc424j600_dump_regs,
+};
+
+static int enc424j600_chipset_init(struct net_device *dev)
+{
+	struct enc424j600_net *priv = netdev_priv(dev);
+
+	enc424j600_get_hw_macaddr(dev);
+	return enc424j600_hw_init(priv);
+}
+
+static const struct net_device_ops enc424j600_netdev_ops = {
+	.ndo_open = enc424j600_net_open,
+	.ndo_stop = enc424j600_net_close,
+	.ndo_start_xmit = enc424j600_send_packet,
+	.ndo_set_multicast_list = enc424j600_set_multicast_list,
+	.ndo_set_mac_address = enc424j600_set_mac_address,
+	.ndo_tx_timeout = enc424j600_tx_timeout,
+	.ndo_change_mtu = eth_change_mtu,
+	.ndo_validate_addr = eth_validate_addr,
+};
+
+static int __devinit enc424j600_probe(struct spi_device *spi)
+{
+	struct net_device *dev;
+	struct enc424j600_net *priv;
+	int ret = 0;
+
+	if (netif_msg_drv(&debug))
+		dev_info(&spi->dev, DRV_NAME " Ethernet driver %s loaded\n",
+			 DRV_VERSION);
+
+	dev = alloc_etherdev(sizeof(struct enc424j600_net));
+	if (!dev) {
+		if (netif_msg_drv(&debug))
+			dev_err(&spi->dev, DRV_NAME
+				": unable to alloc new ethernet\n");
+		ret = -ENOMEM;
+		goto error_alloc;
+	}
+	priv = netdev_priv(dev);
+
+	priv->netdev = dev;
+	priv->spi = spi;
+	priv->msg_enable = netif_msg_init(debug.msg_enable,
+					  ENC424J600_MSG_DEFAULT);
+	mutex_init(&priv->lock);
+	INIT_WORK(&priv->tx_work, enc424j600_tx_work_handler);
+	INIT_WORK(&priv->setrx_work, enc424j600_setrx_work_handler);
+	INIT_WORK(&priv->irq_work, enc424j600_irq_work_handler);
+	INIT_WORK(&priv->restart_work, enc424j600_restart_work_handler);
+	dev_set_drvdata(&spi->dev, priv);
+	SET_NETDEV_DEV(dev, &spi->dev);
+	/*TODO: chip DMA features to be utilized */
+	/* If requested, allocate DMA buffers */
+	if (enc424j600_enable_dma) {
+		spi->dev.coherent_dma_mask = ~0;
+
+		/*
+		 * Minimum coherent DMA allocation is PAGE_SIZE, so allocate
+		 * that much and share it between Tx and Rx DMA buffers.
+		 */
+#if SPI_TRANSFER_BUF_LEN > PAGE_SIZE / 2
+#error "A problem in DMA buffer allocation"
+#endif
+		priv->spi_tx_buf = dma_alloc_coherent(&spi->dev,
+						      PAGE_SIZE,
+						      &priv->spi_tx_dma,
+						      GFP_DMA);
+
+		if (priv->spi_tx_buf) {
+			priv->spi_rx_buf =
+			    (u8 *) (priv->spi_tx_buf + (PAGE_SIZE / 2));
+			priv->spi_rx_dma = (dma_addr_t) (priv->spi_tx_dma +
+							 (PAGE_SIZE / 2));
+		} else {
+			/* Fall back to non-DMA */
+			enc424j600_enable_dma = 0;
+		}
+	}
+
+	/* Allocate non-DMA buffers */
+	if (!enc424j600_enable_dma) {
+		priv->spi_tx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+		if (!priv->spi_tx_buf) {
+			ret = -ENOMEM;
+			goto error_tx_buf;
+		}
+		priv->spi_rx_buf = kmalloc(SPI_TRANSFER_BUF_LEN, GFP_KERNEL);
+		if (!priv->spi_rx_buf) {
+			ret = -ENOMEM;
+			goto error_rx_buf;
+		}
+	}
+
+	if (!enc424j600_chipset_init(dev)) {
+		if (netif_msg_probe(priv))
+			dev_info(&spi->dev, DRV_NAME " chip not found\n");
+		ret = -EIO;
+		goto error_irq;
+	}
+
+	/* Board setup must set the relevant edge trigger type;
+	 * level triggers won't currently work.
+	 */
+	ret = request_irq(spi->irq, enc424j600_irq, 0, DRV_NAME, priv);
+	if (ret < 0) {
+		if (netif_msg_probe(priv))
+			dev_err(&spi->dev, DRV_NAME ": request irq %d failed "
+				"(ret = %d)\n", spi->irq, ret);
+		goto error_irq;
+	}
+
+	dev->if_port = IF_PORT_10BASET;
+	dev->irq = spi->irq;
+	dev->netdev_ops = &enc424j600_netdev_ops;
+	dev->watchdog_timeo = TX_TIMEOUT;
+	SET_ETHTOOL_OPS(dev, &enc424j600_ethtool_ops);
+
+	enc424j600_lowpower(priv, true);
+
+	ret = register_netdev(dev);
+	if (ret) {
+		if (netif_msg_probe(priv))
+			dev_err(&spi->dev, "register netdev " DRV_NAME
+				" failed (ret = %d)\n", ret);
+		goto error_register;
+	}
+	dev_info(&dev->dev, DRV_NAME " driver registered\n");
+
+	return ret;
+
+error_register:
+	free_irq(spi->irq, priv);
+error_irq:
+	free_netdev(dev);
+	if (!enc424j600_enable_dma)
+		kfree(priv->spi_rx_buf);
+error_rx_buf:
+	if (!enc424j600_enable_dma)
+		kfree(priv->spi_tx_buf);
+error_tx_buf:
+	if (enc424j600_enable_dma) {
+		dma_free_coherent(&spi->dev, PAGE_SIZE,
+				  priv->spi_tx_buf, priv->spi_tx_dma);
+	}
+error_alloc:
+	return ret;
+}
+
+static int __devexit enc424j600_remove(struct spi_device *spi)
+{
+	struct enc424j600_net *priv = dev_get_drvdata(&spi->dev);
+
+	if (netif_msg_drv(priv))
+		printk(KERN_DEBUG DRV_NAME ": remove\n");
+
+	unregister_netdev(priv->netdev);
+	free_irq(spi->irq, priv);
+	free_netdev(priv->netdev);
+
+	return 0;
+}
+
+static struct spi_driver enc424j600_driver = {
+	.driver = {
+		   .name = DRV_NAME,
+		   .owner = THIS_MODULE,
+		   },
+	.probe = enc424j600_probe,
+	.remove = __devexit_p(enc424j600_remove),
+};
+
+static int __init enc424j600_init(void)
+{
+	msec20_to_jiffies = msecs_to_jiffies(20);
+	/*autoneg works from 1600ms */
+	msec2000_to_jiffies = msecs_to_jiffies(2000);
+
+	return spi_register_driver(&enc424j600_driver);
+}
+
+module_init(enc424j600_init);
+
+static void __exit enc424j600_exit(void)
+{
+	spi_unregister_driver(&enc424j600_driver);
+}
+
+module_exit(enc424j600_exit);
+
+MODULE_DESCRIPTION(DRV_NAME " ethernet driver");
+MODULE_AUTHOR("Balaji Venkatachalam <balaji.v@thotakaa.com>");
+MODULE_LICENSE("GPL");
+module_param_named(debug, debug.msg_enable, int, 0);
+MODULE_PARM_DESC(debug, "Debug verbosity level (0=none, ..., ffff=all)");
+module_param(enc424j600_enable_dma, int, S_IRUGO);
+MODULE_PARM_DESC(enc424j600_enable_dma, "Enable SPI DMA. Default: 0 (Off)");
+MODULE_ALIAS("spi:" DRV_NAME);
diff -uprN -X a/Documentation/dontdiff a/drivers/net/enc424j600_hw.h
b/drivers/net/enc424j600_hw.h
--- a/drivers/net/enc424j600_hw.h	1970-01-01 05:30:00.000000000 +0530
+++ b/drivers/net/enc424j600_hw.h	2011-01-05 23:51:24.000000000 +0530
@@ -0,0 +1,460 @@
+/*
+* enc424j600_hw.h: Register definitions
+*
+*/
+
+#ifndef _ENC424J600_HW_H
+#define _ENC424J600_HW_H
+
+/*
+* ENC424J600 Control Registers
+* Control register definitions are a combination of address
+* and bank number
+* - Register address (bits 0-4)
+* - Bank number (bits 5-6)
+*/
+#define ADDR_MASK 0x1F
+#define BANK_MASK 0x60
+#define BANK_SHIFT 5
+
+/* All-bank registers */
+#define EUDASTL 0x16
+#define EUDASTH 0x17
+#define EUDANDL 0x18
+#define EUDANDH 0x19
+#define ESTATL 0x1A
+#define ESTATH 0x1B
+#define EIRL 0x1C
+#define EIRH 0x1D
+#define ECON1L 0x1E
+#define ECON1H 0x1F
+
+/* Bank 0 registers */
+#define ETXSTL (0x00 | 0x00)
+#define ETXSTH (0x01 | 0x00)
+#define ETXLENL (0x02 | 0x00)
+#define ETXLENH (0x03 | 0x00)
+#define ERXSTL (0x04 | 0x00)
+#define ERXSTH (0x05 | 0x00)
+#define ERXTAILL (0x06 | 0x00)
+#define ERXTAILH (0x07 | 0x00)
+#define ERXHEADL (0x08 | 0x00)
+#define ERXHEADH (0x09 | 0x00)
+#define EDMASTL (0x0A | 0x00)
+#define EDMASTH (0x0B | 0x00)
+#define EDMALENL (0x0C | 0x00)
+#define EDMALENH (0x0D | 0x00)
+#define EDMADSTL (0x0E | 0x00)
+#define EDMADSTH (0x0F | 0x00)
+#define EDMACSL (0x10 | 0x00)
+#define EDMACSH (0x11 | 0x00)
+#define ETXSTATL (0x12 | 0x00)
+#define ETXSTATH (0x13 | 0x00)
+#define ETXWIREL (0x14 | 0x00)
+#define ETXWIREH (0x15 | 0x00)
+
+/* Bank 1 registers */
+#define EHT1L (0x00 | 0x20)
+#define EHT1H (0x01 | 0x20)
+#define EHT2L (0x02 | 0x20)
+#define EHT2H (0x03 | 0x20)
+#define EHT3L (0x04 | 0x20)
+#define EHT3H (0x05 | 0x20)
+#define EHT4L (0x06 | 0x20)
+#define EHT4H (0x07 | 0x20)
+#define EPMM1L (0x08 | 0x20)
+#define EPMM1H (0x09 | 0x20)
+#define EPMM2L (0x0A | 0x20)
+#define EPMM2H (0x0B | 0x20)
+#define EPMM3L (0x0C | 0x20)
+#define EPMM3H (0x0D | 0x20)
+#define EPMM4L (0x0E | 0x20)
+#define EPMM4H (0x0F | 0x20)
+#define EPMCSL (0x10 | 0x20)
+#define EPMCSH (0x11 | 0x20)
+#define EPMOL (0x12 | 0x20)
+#define EPMOH (0x13 | 0x20)
+#define ERXFCONL (0x14 | 0x20)
+#define ERXFCONH (0x15 | 0x20)
+
+/* Bank 2 registers */
+#define MACON1L (0x00 | 0x40)
+#define MACON1H (0x01 | 0x40)
+#define MACON2L (0x02 | 0x40)
+#define MACON2H (0x03 | 0x40)
+#define MABBIPGL (0x04 | 0x40)
+#define MABBIPGH (0x05 | 0x40)
+#define MAIPGL (0x06 | 0x40)
+#define MAIPGH (0x07 | 0x40)
+#define MACLCONL (0x08 | 0x40)
+#define MACLCONH (0x09 | 0x40)
+#define MAMXFLL (0x0A | 0x40)
+#define MAMXFLH (0x0B | 0x40)
+#define MICMDL (0x12 | 0x40)
+#define MICMDH (0x13 | 0x40)
+#define MIREGADRL (0x14 | 0x40)
+#define MIREGADRH (0x15 | 0x40)
+
+/* Bank 3 registers */
+#define MAADR3L (0x00 | 0x60)
+#define MAADR3H (0x01 | 0x60)
+#define MAADR2L (0x02 | 0x60)
+#define MAADR2H (0x03 | 0x60)
+#define MAADR1L (0x04 | 0x60)
+#define MAADR1H (0x05 | 0x60)
+#define MIWRL (0x06 | 0x60)
+#define MIWRH (0x07 | 0x60)
+#define MIRDL (0x08 | 0x60)
+#define MIRDH (0x09 | 0x60)
+#define MISTATL (0x0A | 0x60)
+#define MISTATH (0x0B | 0x60)
+#define EPAUSL (0x0C | 0x60)
+#define EPAUSH (0x0D | 0x60)
+#define ECON2L (0x0E | 0x60)
+#define ECON2H (0x0F | 0x60)
+#define ERXWML (0x10 | 0x60)
+#define ERXWMH (0x11 | 0x60)
+#define EIEL (0x12 | 0x60)
+#define EIEH (0x13 | 0x60)
+#define EIDLEDL (0x14 | 0x60)
+#define EIDLEDH (0x15 | 0x60)
+
+/* Unbanked registers */
+#define EGPDATA (0x00 | 0x80)
+#define ERXDATA (0x02 | 0x80)
+#define EUDADATA (0x04 | 0x80)
+#define EGPRDPTL (0x06 | 0x80)
+#define EGPRDPTH (0x07 | 0x80)
+#define EGPWRPTL (0x08 | 0x80)
+#define EGPWRPTH (0x09 | 0x80)
+#define ERXRDPTL (0x0A | 0x80)
+#define ERXRDPTH (0x0B | 0x80)
+#define ERXWRPTL (0x0C | 0x80)
+#define ERXWRPTH (0x0D | 0x80)
+#define EUDARDPTL (0x0E | 0x80)
+#define EUDARDPTH (0x0F | 0x80)
+#define EUDAWRPTL (0x10 | 0x80)
+#define EUDAWRPTH (0x11 | 0x80)
+
+/* PHY registers */
+#define PHCON1 0x00
+#define PHSTAT1 0x01
+#define PHANA 0x04
+#define PHANLPA 0x05
+#define PHANE 0x06
+#define PHCON2 0x11
+#define PHSTAT2 0x1B
+#define PHSTAT3 0x1F
+
+/* Single-byte instructions */
+#define BXSEL(bank) (0xC0 + (bank & (BANK_MASK >> BANK_SHIFT)) * 2)
+/* Bank X Select */
+#define B0SEL 0xC0		/* Bank 0 Select */
+#define B1SEL 0xC2		/* Bank 1 Select */
+#define B2SEL 0xC4		/* Bank 2 Select */
+#define B3SEL 0xC6		/* Bank 3 Select */
+#define SETETHRST 0xCA		/* System Reset */
+#define FCDISABLE 0xE0		/* Flow Control Disable */
+#define FCSINGLE 0xE2		/* Flow Control Single */
+#define FCMULTIPLE 0xE4		/* Flow Control Multiple */
+#define FCCLEAR 0xE6		/* Flow Control Clear */
+#define SETPKTDEC 0xCC		/* Decrement Packet Counter */
+#define DMASTOP 0xD2		/* DMA Stop */
+#define DMACKSUM 0xD8		/* DMA Start Checksum */
+#define DMACKSUMS 0xDA		/* DMA Start Checksum with Seed */
+#define DMACOPY 0xDC		/* DMA Start Copy */
+#define DMACOPYS 0xDE		/* DMA Start Copy and Checksum with Seed */
+#define SETTXRTS 0xD4		/* Request Packet Transmission */
+#define ENABLERX 0xE8		/* Enable RX */
+#define DISABLERX 0xEA		/* Disable RX */
+#define SETEIE 0xEC		/* Enable Interrupts */
+#define CLREIE 0xEE		/* Disable Interrupts */
+
+/* Two byte instructions */
+#define RBSEL 0xC8		/* Read Bank Select */
+
+/* Three byte instructions */
+#define WGPRDPT 0x60		/* Write EGPRDPT */
+#define RGPRDPT 0x62		/* Read EGPRDPT */
+#define WRXRDPT 0x64		/* Write ERXRDPT */
+#define RRXRDPT 0x66		/* Read ERXRDPT */
+#define WUDARDPT 0x68		/* Write EUDARDPT */
+#define RUDARDPT 0x6A		/* Read EUDARDPT */
+#define WGPWRPT 0x6C		/* Write EGPWRPT */
+#define RGPWRPT 0x6E		/* Read EGPWRPT */
+#define WRXWRPT 0x70		/* Write ERXWRPT */
+#define RRXWRPT 0x72		/* Read ERXWRPT */
+#define WUDAWRPT 0x74		/* Write EUDAWRPT */
+#define RUDAWRPT 0x76		/* Read EUDAWRPT */
+
+/* n byte instructions */
+#define RCR(addr) (0x00 | (addr & ADDR_MASK))	/* Read Control Register */
+#define WCR(addr) (0x40 | (addr & ADDR_MASK))	/* Write Control Register */
+#define RCRU 0x20		/* Read Control Register Unbanked */
+#define WCRU 0x22		/* Write Control Register Unbanked */
+#define BFS(addr) (0x80 | (addr & ADDR_MASK))	/* Bit Field Set */
+#define BFC(addr) (0xA0 | (addr & ADDR_MASK))	/* Bit Field Clear */
+#define BFSU 0x24		/* Bit Field Set Unbanked */
+#define BFCU 0x26		/* Bit Field Clear Unbanked */
+#define RGPDATA 0x28		/* Read EGPDATA */
+#define WGPDATA 0x2A		/* Write EGPDATA */
+#define RRXDATA 0x2C		/* Read ERXDATA */
+#define WRXDATA 0x2E		/* Write ERXDATA */
+#define RUDADATA 0x30		/* Read EUDADATA */
+#define WUDADATA 0x32		/* Write EUDADATA */
+
+/* Register bit definitions */
+/* ESTATH */
+#define INT (1 << 7)
+#define FCIDLE (1 << 6)
+#define RXBUSY (1 << 5)
+#define CLKRDY (1 << 4)
+#define PHYDPX (1 << 2)
+#define PHYLNK (1 << 0)
+
+/* EIRH */
+/*for ease of use lets access it as a word*/
+#define CRYPTEN (1 << 15)
+#define MODEXIF (1 << 14)
+#define HASHIF (1 << 13)
+#define AESIF (1 << 12)
+#define LINKIF (1 << 11)
+
+/* EIRL */
+#define PKTIF (1 << 6)
+#define DMAIF (1 << 5)
+#define TXIF (1 << 3)
+#define TXABTIF (1 << 2)
+#define RXABTIF (1 << 1)
+#define PCFULIF (1 << 0)
+
+/* ECON1H */
+#define MODEXST (1 << 7)
+#define HASHEN (1 << 6)
+#define HASHOP (1 << 5)
+#define HASHLST (1 << 4)
+#define AESST (1 << 3)
+#define AESOP1 (1 << 2)
+#define AESOP0 (1 << 1)
+#define PKTDEC (1 << 0)
+
+/* ECON1L */
+#define FCOP1 (1 << 7)
+#define FCOP0 (1 << 6)
+#define DMAST (1 << 5)
+#define DMACPY (1 << 4)
+#define DMACSSD (1 << 3)
+#define DMANOCS (1 << 2)
+#define TXRTS (1 << 1)
+#define RXEN (1 << 0)
+
+/* ETXSTATH */
+#define LATECOL (1 << 2)
+#define MAXCOL (1 << 1)
+#define EXDEFER (1 << 0)
+
+/* ETXSTATL */
+#define ETXSTATL_DEFER (1 << 7)
+#define CRCBAD (1 << 4)
+#define COLCNT_MASK 0xF
+
+/* ERXFCONH */
+#define HTEN (1 << 7)
+#define MPEN (1 << 6)
+#define NOTPM (1 << 4)
+#define PMEN3 (1 << 3)
+#define PMEN2 (1 << 2)
+#define PMEN1 (1 << 1)
+#define PMEN0 (1 << 0)
+
+/* ERXFCONL */
+#define CRCEEN (1 << 7)
+#define CRCEN (1 << 6)
+#define RUNTEEN (1 << 5)
+#define RUNTEN (1 << 4)
+#define UCEN (1 << 3)
+#define NOTMEEN (1 << 2)
+#define MCEN (1 << 1)
+#define BCEN (1 << 0)
+/*no bytewise access*/
+/* MACON1L */
+#define LOOPBK (1 << 4)
+#define RXPAUS (1 << 2)
+#define PASSALL (1 << 1)
+
+/* MACON2 */
+#define MACON2_DEFER (1 << 14)
+#define BPEN (1 << 13)
+#define NOBKOFF (1 << 12)
+#define PADCFG2 (1 << 7)
+#define PADCFG1 (1 << 6)
+#define PADCFG0 (1 << 5)
+#define TXCRCEN (1 << 4)
+#define PHDREN (1 << 3)
+#define HFRMEN (1 << 2)
+#define FULDPX (1 << 0)
+
+/* MAIPG */
+/* value of the high byte is given by the reserved bits,
+* value of the low byte is recomended setting of the
+* IPG parameter.
+*/
+#define MAIPGH_VAL 0x0C
+#define MAIPGL_VAL 0x12
+
+/* MIREGADRH */
+#define MIREGADRH_VAL 0x01
+
+/* MIREGADRL */
+#define PHREG_MASK 0x1F
+
+/* MICMDL */
+#define MIISCAN (1 << 1)
+#define MIIRD (1 << 0)
+
+/* MISTATL */
+#define NVALID (1 << 2)
+#define SCAN (1 << 1)
+#define BUSY (1 << 0)
+
+/* ECON2H */
+#define ETHEN (1 << 7)
+#define STRCH (1 << 6)
+#define TXMAC (1 << 5)
+#define SHA1MD5 (1 << 4)
+#define COCON3 (1 << 3)
+#define COCON2 (1 << 2)
+#define COCON1 (1 << 1)
+#define COCON0 (1 << 0)
+
+/* ECON2L */
+#define AUTOFC (1 << 7)
+#define TXRST (1 << 6)
+#define RXRST (1 << 5)
+#define ETHRST (1 << 4)
+#define MODLEN1 (1 << 3)
+#define MODLEN0 (1 << 2)
+#define AESLEN1 (1 << 1)
+#define AESLEN0 (1 << 0)
+
+/* EIEH */
+#define INTIE (1 << 7)
+#define MODEXIE (1 << 6)
+#define HASHIE (1 << 5)
+#define AESIE (1 << 4)
+#define LINKIE (1 << 3)
+
+/* EIEL */
+#define PKTIE (1 << 6)
+#define DMAIE (1 << 5)
+#define TXIE (1 << 3)
+#define TXABTIE (1 << 2)
+#define RXABTIE (1 << 1)
+#define PCFULIE (1 << 0)
+
+/* EIDLEDH */
+#define LACFG3 (1 << 7)
+#define LACFG2 (1 << 6)
+#define LACFG1 (1 << 5)
+#define LACFG0 (1 << 4)
+#define LBCFG3 (1 << 3)
+#define LBCFG2 (1 << 2)
+#define LBCFG1 (1 << 1)
+#define LBCFG0 (1 << 0)
+
+/* EIDLEDL */
+#define DEVID_SHIFT 5
+#define DEVID_MASK (0x7 << DEVID_SHIFT)
+#define REVID_SHIFT 0
+#define REVID_MASK (0x1F << REVID_SHIFT)
+
+/* PHANA */
+/* Default value for PHY initialization*/
+#define PHANA_DEFAULT 0x05E1
+
+/* PHCON1 */
+#define PRST (1 << 15)
+#define PLOOPBK (1 << 14)
+#define SPD100 (1 << 13)
+#define ANEN (1 << 12)
+#define PSLEEP (1 << 11)
+#define RENEG (1 << 9)
+#define PFULDPX (1 << 8)
+
+/* PHSTAT */
+#define FULL100 (1 << 14)
+#define HALF100 (1 << 13)
+#define FULL10 (1 << 12)
+#define HALF10 (1 << 11)
+#define ANDONE (1 << 5)
+#define LRFAULT (1 << 4)
+#define ANABLE (1 << 3)
+#define LLSTAT (1 << 2)
+#define EXTREGS (1 << 0)
+
+#define EUDAST_TEST_VAL 0x1234
+
+#define TSV_SIZE 7
+
+#define ENC424J600_DEV_ID 0x1
+
+/* Configuration */
+
+/* Led is on when the link is present and driven low
+* temporarily when packet is TX'd or RX'd */
+#define LED_A_SETTINGS 0xC
+
+/* Led is on if the link is in 100 Mbps mode */
+#define LED_B_SETTINGS 0x8
+
+/* maximum ethernet frame length
+* Currently not used as a limit anywhere
+* (we're using the "huge frame enable" feature of
+* enc424j600). */
+#define MAX_FRAMELEN 1518
+
+/* Size in bytes of the receive buffer in enc424j600.
+* Must be word aligned (even).
+*/
+#define RX_BUFFER_SIZE (15 * MAX_FRAMELEN)
+
+/* Start of the general purpose area in sram */
+#define SRAM_GP_START 0x0
+
+/* SRAM size */
+#define SRAM_SIZE 0x6000
+
+/* Start of the receive buffer */
+#define ERXST_VAL (SRAM_SIZE - RX_BUFFER_SIZE)
+
+#define RSV_RXLONGEVDROPEV	16
+#define RSV_CARRIEREV		18
+#define RSV_CRCERROR		20
+#define RSV_LENCHECKERR		21
+#define RSV_LENOUTOFRANGE	22
+#define RSV_RXOK		23
+#define RSV_RXMULTICAST		24
+#define RSV_RXBROADCAST		25
+#define RSV_DRIBBLENIBBLE	26
+#define RSV_RXCONTROLFRAME	27
+#define RSV_RXPAUSEFRAME	28
+#define RSV_RXUNKNOWNOPCODE	29
+#define RSV_RXTYPEVLAN		30
+
+#define RSV_RUNTFILTERMATCH	31
+#define RSV_NOTMEFILTERMATCH	32
+#define RSV_HASHFILTERMATCH	33
+#define RSV_MAGICPKTFILTERMATCH	34
+#define RSV_PTRNMTCHFILTERMATCH	35
+#define RSV_UNICASTFILTERMATCH	36
+
+#define RSV_SIZE		8
+#define RSV_BITMASK(x)		(1 << ((x) - 16))
+#define RSV_GETBIT(x, y)	(((x) & RSV_BITMASK(y)) ? 1 : 0)
+
+/* Put RX buffer at 0 as suggested by the Errata datasheet */
+
+#define RXSTART_INIT		ERXST_VAL
+#define RXEND_INIT		0x5FFF
+
+#endif
diff -uprN -X a/Documentation/dontdiff a/drivers/net/Kconfig
b/drivers/net/Kconfig
--- a/drivers/net/Kconfig	2010-07-05 22:41:43.000000000 +0530
+++ b/drivers/net/Kconfig	2011-01-16 15:26:16.000000000 +0530
@@ -973,6 +973,16 @@ config ENC28J60_WRITEVERIFY
 	  Enable the verify after the buffer write useful for debugging purpose.
 	  If unsure, say N.

+config ENC424J600
+	tristate "ENC424J600 support"
+	depends on EXPERIMENTAL && SPI && NET_ETHERNET
+	select CRC32
+	---help---
+		Support for the Microchip EN424J600 ethernet chip.
+
+		To compile this driver as a module, choose M here. The module will be
+		called enc424j600.
+
 config ETHOC
 	tristate "OpenCores 10/100 Mbps Ethernet MAC support"
 	depends on NET_ETHERNET && HAS_IOMEM
diff -uprN -X a/Documentation/dontdiff a/drivers/net/Makefile
b/drivers/net/Makefile
--- a/drivers/net/Makefile	2010-07-05 22:41:43.000000000 +0530
+++ b/drivers/net/Makefile	2011-01-05 21:46:57.000000000 +0530
@@ -240,6 +240,7 @@ obj-$(CONFIG_PASEMI_MAC) += pasemi_mac_d
 pasemi_mac_driver-objs := pasemi_mac.o pasemi_mac_ethtool.o
 obj-$(CONFIG_MLX4_CORE) += mlx4/
 obj-$(CONFIG_ENC28J60) += enc28j60.o
+obj-$(CONFIG_ENC424J600) += enc424j600.o
 obj-$(CONFIG_ETHOC) += ethoc.o

 obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o

^ permalink raw reply

* [PATCH 4/4] batman-adv: Make vis info stack traversal threadsafe
From: Sven Eckelmann @ 2011-01-30  9:38 UTC (permalink / raw)
  To: davem; +Cc: netdev, Sven Eckelmann
In-Reply-To: <201101301036.57928.sven@narfation.org>

The batman-adv vis server has to a stack which stores all information
about packets which should be send later. This stack is protected
with a spinlock that is used to prevent concurrent write access to it.

The send_vis_packets function has to take all elements from the stack
and send them to other hosts over the primary interface. The send will
be initiated without the lock which protects the stack.

The implementation using list_for_each_entry_safe has the problem that
it stores the next element as "safe ptr" to allow the deletion of the
current element in the list. The list may be modified during the
unlock/lock pair in the loop body which may make the safe pointer
not pointing to correct next element.

It is safer to remove and use the first element from the stack until no
elements are available. This does not need reduntant information which
would have to be validated each time the lock was removed.

Reported-by: Russell Senior <russell@personaltelco.net>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/vis.c |    7 ++++---
 1 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index 988296c..de1022c 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -816,7 +816,7 @@ static void send_vis_packets(struct work_struct *work)
 		container_of(work, struct delayed_work, work);
 	struct bat_priv *bat_priv =
 		container_of(delayed_work, struct bat_priv, vis_work);
-	struct vis_info *info, *temp;
+	struct vis_info *info;
 
 	spin_lock_bh(&bat_priv->vis_hash_lock);
 	purge_vis_packets(bat_priv);
@@ -826,8 +826,9 @@ static void send_vis_packets(struct work_struct *work)
 		send_list_add(bat_priv, bat_priv->my_vis_info);
 	}
 
-	list_for_each_entry_safe(info, temp, &bat_priv->vis_send_list,
-				 send_list) {
+	while (!list_empty(&bat_priv->vis_send_list)) {
+		info = list_first_entry(&bat_priv->vis_send_list,
+					typeof(*info), send_list);
 
 		kref_get(&info->refcount);
 		spin_unlock_bh(&bat_priv->vis_hash_lock);
-- 
1.7.2.3


^ permalink raw reply related

* [PATCH 3/4] batman-adv: Remove vis info element in free_info
From: Sven Eckelmann @ 2011-01-30  9:38 UTC (permalink / raw)
  To: davem; +Cc: netdev, Sven Eckelmann
In-Reply-To: <201101301036.57928.sven@narfation.org>

The free_info function will be called when no reference to the info
object exists anymore. It must be ensured that the allocated memory
gets freed and not only the elements which are managed by the info
object.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/vis.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index 0be55be..988296c 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -64,6 +64,7 @@ static void free_info(struct kref *ref)
 
 	spin_unlock_bh(&bat_priv->vis_list_lock);
 	kfree_skb(info->skb_packet);
+	kfree(info);
 }
 
 /* Compare two vis packets, used by the hashing algorithm */
-- 
1.7.2.3


^ permalink raw reply related

* [PATCH 2/4] batman-adv: Remove vis info on hashing errors
From: Sven Eckelmann @ 2011-01-30  9:38 UTC (permalink / raw)
  To: davem; +Cc: netdev, Sven Eckelmann
In-Reply-To: <201101301036.57928.sven@narfation.org>

A newly created vis info object must be removed when it couldn't be
added to the hash. The old_info which has to be replaced was already
removed and isn't related to the hash anymore.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/vis.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index f69a374..0be55be 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -444,7 +444,7 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv,
 			      info);
 	if (hash_added < 0) {
 		/* did not work (for some reason) */
-		kref_put(&old_info->refcount, free_info);
+		kref_put(&info->refcount, free_info);
 		info = NULL;
 	}
 
-- 
1.7.2.3


^ permalink raw reply related

* [PATCH 1/4] batman-adv: Fix kernel panic when fetching vis data on a vis server
From: Sven Eckelmann @ 2011-01-30  9:38 UTC (permalink / raw)
  To: davem; +Cc: netdev, Linus Lüssing, Sven Eckelmann
In-Reply-To: <201101301036.57928.sven@narfation.org>

From: Linus Lüssing <linus.luessing@web.de>

The hash_iterate removal introduced a bug leading to a kernel panic when
fetching the vis data on a vis server. That commit forgot to rename one
variable name, which this commit fixes now.

Reported-by: Russell Senior <russell@personaltelco.net>
Signed-off-by: Linus Lüssing <linus.luessing@web.de>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/vis.c |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c
index cd4c423..f69a374 100644
--- a/net/batman-adv/vis.c
+++ b/net/batman-adv/vis.c
@@ -268,10 +268,10 @@ int vis_seq_print_text(struct seq_file *seq, void *offset)
 				buff_pos += sprintf(buff + buff_pos, "%pM,",
 						entry->addr);
 
-				for (i = 0; i < packet->entries; i++)
+				for (j = 0; j < packet->entries; j++)
 					buff_pos += vis_data_read_entry(
 							buff + buff_pos,
-							&entries[i],
+							&entries[j],
 							entry->addr,
 							entry->primary);
 
-- 
1.7.2.3


^ permalink raw reply related

* Re: pull request: batman-adv 2011-01-29
From: Sven Eckelmann @ 2011-01-30  9:36 UTC (permalink / raw)
  To: David Miller
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	b.a.t.m.a.n-ZwoEplunGu2X36UT3dwllkB+6BGkLq7r
In-Reply-To: <20110129.190815.226768755.davem-fT/PcQaiUtIeIZ0/mPfg9Q@public.gmane.org>

[-- Attachment #1: Type: Text/Plain, Size: 1167 bytes --]

On Sunday 30 January 2011 04:08:15 David Miller wrote:
> I'm not taking 13 patches in one big bunch at this point post-merge-window,
> now matter what they are.

Thats why I tried to provide the 6 bug fixes only version without polishing - 
I could also try to reduce it to a 4 critical version (oops/memleak patches 
only without protocol implementation failures).

Or is the problem that I tried to get them in as pull request?

thanks,
	Sven


Here are the oops/memleak patches only:

The following changes since commit aa0adb1a85e159cf57f0e11282bc6c9e3606a5f3:

  batman-adv: Use "__attribute__" shortcut macros (2011-01-16 03:25:19 +0100)

are available in the git repository at:
  git://git.open-mesh.org/ecsv/linux-merge.git batman-adv/merge-oopsonly

Linus Lüssing (1):
      batman-adv: Fix kernel panic when fetching vis data on a vis server

Sven Eckelmann (3):
      batman-adv: Remove vis info on hashing errors
      batman-adv: Remove vis info element in free_info
      batman-adv: Make vis info stack traversal threadsafe

 net/batman-adv/vis.c |   14 ++++++++------
 1 files changed, 8 insertions(+), 6 deletions(-)

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* Re: [PATCH] caif: bugfix - add caif headers for userspace usage.
From: David Miller @ 2011-01-30  9:15 UTC (permalink / raw)
  To: sjur.brandeland; +Cc: netdev
In-Reply-To: <1296342637-7039-1-git-send-email-sjur.brandeland@stericsson.com>

From: Sjur Brændeland <sjur.brandeland@stericsson.com>
Date: Sun, 30 Jan 2011 00:10:37 +0100

> Add caif_socket.h and if_caif.h to the kernel header files
> exported for use by userspace.
> 
> Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>

Applied to net-2.6

^ permalink raw reply

* Re: [PATCH] net: Fix ip link add netns oops
From: David Miller @ 2011-01-30  9:15 UTC (permalink / raw)
  To: ebiederm; +Cc: netdev, eswierk, stable
In-Reply-To: <m1fwsbus25.fsf@fess.ebiederm.org>

From: ebiederm@xmission.com (Eric W. Biederman)
Date: Sat, 29 Jan 2011 16:57:22 -0800

> 
> Ed Swierk <eswierk@bigswitch.com> writes:
>> On 2.6.35.7
>>  ip link add link eth0 netns 9999 type macvlan
>> where 9999 is a nonexistent PID triggers an oops and causes all network functions to hang:
>> [10663.821898] BUG: unable to handle kernel NULL pointer dereference at 000000000000006d
>>  [10663.821917] IP: [<ffffffff8149c2fa>] __dev_alloc_name+0x9a/0x170
 ...
> This bug was introduced in:
> commit 81adee47dfb608df3ad0b91d230fb3cef75f0060
> Author: Eric W. Biederman <ebiederm@aristanetworks.com>
> Date:   Sun Nov 8 00:53:51 2009 -0800
> 
>     net: Support specifying the network namespace upon device creation.
...
> Where apparently I forgot to add error handling to the path where we create
> a new network device in a new network namespace, and pass in an invalid pid.
> 
> Cc: stable@kernel.org
> Reported-by: Ed Swierk <eswierk@bigswitch.com>
> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>

Applied, thanks Eric.

^ permalink raw reply

* Re: [PATCH] net: Add compat ioctl support for the ipv4 multicast ioctl SIOCGETSGCNT
From: David Miller @ 2011-01-30  9:14 UTC (permalink / raw)
  To: ebiederm; +Cc: netdev, eric.dumazet, kaber
In-Reply-To: <m18vy3uof7.fsf@fess.ebiederm.org>

From: ebiederm@xmission.com (Eric W. Biederman)
Date: Sat, 29 Jan 2011 18:15:56 -0800

> 
> SIOCGETSGCNT is not a unique ioctl value as it it maps tio SIOCPROTOPRIVATE +1,
> which unfortunately means the existing infrastructure for compat networking
> ioctls is insufficient.  A trivial compact ioctl implementation would conflict
> with:
> 
> SIOCAX25ADDUID
> SIOCAIPXPRISLT
> SIOCGETSGCNT_IN6
> SIOCGETSGCNT
> SIOCRSSCAUSE
> SIOCX25SSUBSCRIP
> SIOCX25SDTEFACILITIES
> 
> To make this work I have updated the compat_ioctl decode path to mirror the
> the normal ioctl decode path.  I have added an ipv4 inet_compat_ioctl function
> so that I can have ipv4 specific compat ioctls.   I have added a compat_ioctl
> function into struct proto so I can break out ioctls by which kind of ip socket
> I am using.  I have added a compat_raw_ioctl function because SIOCGETSGCNT only
> works on raw sockets.  I have added a ipmr_compat_ioctl that mirrors the normal
> ipmr_ioctl.
> 
> This was necessary because unfortunately the struct layout for the SIOCGETSGCNT
> has unsigned longs in it so changes between 32bit and 64bit kernels.
> 
> This change was sufficient to run a 32bit ip multicast routing daemon on a
> 64bit kernel.
> 
> Reported-by: Bill Fenner <fenner@aristanetworks.com>
> Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>

Thanks a lot for doing this work Eric, applied to net-2.6.

^ permalink raw reply

* Re: [PATCH net-2.6] slcan: fix referenced website in Kconfig help text
From: David Miller @ 2011-01-30  9:09 UTC (permalink / raw)
  To: socketcan; +Cc: netdev
In-Reply-To: <4D445786.70701@hartkopp.net>

From: Oliver Hartkopp <socketcan@hartkopp.net>
Date: Sat, 29 Jan 2011 19:08:06 +0100

> Fix the referenced project website to www.mictronics.de in the Kconfig help
> text for the slcan driver.
> 
> Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>

Applied, thanks.

^ permalink raw reply

* Re: gro: Reset dev pointer on reuse
From: David Miller @ 2011-01-30  4:45 UTC (permalink / raw)
  To: herbert; +Cc: netdev
In-Reply-To: <20110130041047.GA6290@gondor.apana.org.au>

From: Herbert Xu <herbert@gondor.apana.org.au>
Date: Sun, 30 Jan 2011 15:10:47 +1100

> gro: Reset dev pointer on reuse
> 
> On older kernels the VLAN code may zero skb->dev before dropping
> it and causing it to be reused by GRO.
> 
> Unfortunately we didn't reset skb->dev in that case which causes
> the next GRO user to get a bogus skb->dev pointer.
> 
> This particular problem no longer happens with the current upstream
> kernel due to changes in VLAN processing.
> 
> However, for correctness we should still reset the skb->dev pointer
> in the GRO reuse function in case a future user does the same thing.
> 
> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

Applied, thanks Herbert.

^ permalink raw reply

* gro: Reset dev pointer on reuse
From: Herbert Xu @ 2011-01-30  4:10 UTC (permalink / raw)
  To: David S. Miller, netdev

Hi:

gro: Reset dev pointer on reuse

On older kernels the VLAN code may zero skb->dev before dropping
it and causing it to be reused by GRO.

Unfortunately we didn't reset skb->dev in that case which causes
the next GRO user to get a bogus skb->dev pointer.

This particular problem no longer happens with the current upstream
kernel due to changes in VLAN processing.

However, for correctness we should still reset the skb->dev pointer
in the GRO reuse function in case a future user does the same thing.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

diff --git a/net/core/dev.c b/net/core/dev.c
index 24ea2d7..93e44db 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3424,6 +3424,7 @@ static void napi_reuse_skb(struct napi_struct *napi, struct sk_buff *skb)
 	__skb_pull(skb, skb_headlen(skb));
 	skb_reserve(skb, NET_IP_ALIGN - skb_headroom(skb));
 	skb->vlan_tci = 0;
+	skb->dev = napi->dev;
 
 	napi->skb = skb;
 }

Cheers,
-- 
Email: Herbert Xu <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply related

* Re: pull request: batman-adv 2011-01-29
From: David Miller @ 2011-01-30  3:08 UTC (permalink / raw)
  To: sven-KaDOiPu9UxWEi8DpZVb4nw
  Cc: netdev-u79uwXL29TY76Z2rM5mHXA,
	b.a.t.m.a.n-ZwoEplunGu2X36UT3dwllkB+6BGkLq7r
In-Reply-To: <1296337660-12376-1-git-send-email-sven-KaDOiPu9UxWEi8DpZVb4nw@public.gmane.org>

From: Sven Eckelmann <sven-KaDOiPu9UxWEi8DpZVb4nw@public.gmane.org>
Date: Sat, 29 Jan 2011 22:47:27 +0100

> Hi,
> 
> I would like to propose following changes for net-2.6.git/2.6.38 which fixes
> some bugs in the fragmentation and vis code. There are also some minor cleanup
> patches available. If you don't want to merge those cleanup patches then please
> look bellow the first diffstat for an alternative way to get only the bugfixes.

I'm not taking 13 patches in one big bunch at this point post-merge-window,
now matter what they are.

^ permalink raw reply

* [PATCH] net: Add compat ioctl support for the ipv4 multicast ioctl SIOCGETSGCNT
From: Eric W. Biederman @ 2011-01-30  2:15 UTC (permalink / raw)
  To: netdev; +Cc: David Miller, Eric Dumazet, Patrick McHardy


SIOCGETSGCNT is not a unique ioctl value as it it maps tio SIOCPROTOPRIVATE +1,
which unfortunately means the existing infrastructure for compat networking
ioctls is insufficient.  A trivial compact ioctl implementation would conflict
with:

SIOCAX25ADDUID
SIOCAIPXPRISLT
SIOCGETSGCNT_IN6
SIOCGETSGCNT
SIOCRSSCAUSE
SIOCX25SSUBSCRIP
SIOCX25SDTEFACILITIES

To make this work I have updated the compat_ioctl decode path to mirror the
the normal ioctl decode path.  I have added an ipv4 inet_compat_ioctl function
so that I can have ipv4 specific compat ioctls.   I have added a compat_ioctl
function into struct proto so I can break out ioctls by which kind of ip socket
I am using.  I have added a compat_raw_ioctl function because SIOCGETSGCNT only
works on raw sockets.  I have added a ipmr_compat_ioctl that mirrors the normal
ipmr_ioctl.

This was necessary because unfortunately the struct layout for the SIOCGETSGCNT
has unsigned longs in it so changes between 32bit and 64bit kernels.

This change was sufficient to run a 32bit ip multicast routing daemon on a
64bit kernel.

Reported-by: Bill Fenner <fenner@aristanetworks.com>
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
---
 include/linux/mroute.h |    1 +
 include/net/sock.h     |    2 ++
 net/ipv4/af_inet.c     |   16 ++++++++++++++++
 net/ipv4/ipmr.c        |   46 ++++++++++++++++++++++++++++++++++++++++++++++
 net/ipv4/raw.c         |   19 +++++++++++++++++++
 5 files changed, 84 insertions(+), 0 deletions(-)

diff --git a/include/linux/mroute.h b/include/linux/mroute.h
index 0fa7a3a..b21d567 100644
--- a/include/linux/mroute.h
+++ b/include/linux/mroute.h
@@ -150,6 +150,7 @@ static inline int ip_mroute_opt(int opt)
 extern int ip_mroute_setsockopt(struct sock *, int, char __user *, unsigned int);
 extern int ip_mroute_getsockopt(struct sock *, int, char __user *, int __user *);
 extern int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg);
+extern int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg);
 extern int ip_mr_init(void);
 #else
 static inline
diff --git a/include/net/sock.h b/include/net/sock.h
index 73a4f97..221212e 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -734,6 +734,8 @@ struct proto {
 					int level,
 					int optname, char __user *optval,
 					int __user *option);
+	int			(*compat_ioctl)(struct sock *sk,
+					unsigned int cmd, unsigned long arg);
 #endif
 	int			(*sendmsg)(struct kiocb *iocb, struct sock *sk,
 					   struct msghdr *msg, size_t len);
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index f581f77..8bda7c9 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -880,6 +880,19 @@ int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 }
 EXPORT_SYMBOL(inet_ioctl);
 
+#ifdef CONFIG_COMPAT
+int inet_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+	int err = -ENOIOCTLCMD;
+
+	if (sk->sk_prot->compat_ioctl)
+		err = sk->sk_prot->compat_ioctl(sk, cmd, arg);
+
+	return err;
+}
+#endif
+
 const struct proto_ops inet_stream_ops = {
 	.family		   = PF_INET,
 	.owner		   = THIS_MODULE,
@@ -903,6 +916,7 @@ const struct proto_ops inet_stream_ops = {
 #ifdef CONFIG_COMPAT
 	.compat_setsockopt = compat_sock_common_setsockopt,
 	.compat_getsockopt = compat_sock_common_getsockopt,
+	.compat_ioctl	   = inet_compat_ioctl,
 #endif
 };
 EXPORT_SYMBOL(inet_stream_ops);
@@ -929,6 +943,7 @@ const struct proto_ops inet_dgram_ops = {
 #ifdef CONFIG_COMPAT
 	.compat_setsockopt = compat_sock_common_setsockopt,
 	.compat_getsockopt = compat_sock_common_getsockopt,
+	.compat_ioctl	   = inet_compat_ioctl,
 #endif
 };
 EXPORT_SYMBOL(inet_dgram_ops);
@@ -959,6 +974,7 @@ static const struct proto_ops inet_sockraw_ops = {
 #ifdef CONFIG_COMPAT
 	.compat_setsockopt = compat_sock_common_setsockopt,
 	.compat_getsockopt = compat_sock_common_getsockopt,
+	.compat_ioctl	   = inet_compat_ioctl,
 #endif
 };
 
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index 86dd569..b294da7 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -60,6 +60,7 @@
 #include <linux/notifier.h>
 #include <linux/if_arp.h>
 #include <linux/netfilter_ipv4.h>
+#include <linux/compat.h>
 #include <net/ipip.h>
 #include <net/checksum.h>
 #include <net/netlink.h>
@@ -1434,6 +1435,51 @@ int ipmr_ioctl(struct sock *sk, int cmd, void __user *arg)
 	}
 }
 
+#ifdef CONFIG_COMPAT
+struct compat_sioc_sg_req {
+	struct in_addr src;
+	struct in_addr grp;
+	compat_ulong_t pktcnt;
+	compat_ulong_t bytecnt;
+	compat_ulong_t wrong_if;
+};
+
+int ipmr_compat_ioctl(struct sock *sk, unsigned int cmd, void __user *arg)
+{
+	struct sioc_sg_req sr;
+	struct mfc_cache *c;
+	struct net *net = sock_net(sk);
+	struct mr_table *mrt;
+
+	mrt = ipmr_get_table(net, raw_sk(sk)->ipmr_table ? : RT_TABLE_DEFAULT);
+	if (mrt == NULL)
+		return -ENOENT;
+
+	switch (cmd) {
+	case SIOCGETSGCNT:
+		if (copy_from_user(&sr, arg, sizeof(sr)))
+			return -EFAULT;
+
+		rcu_read_lock();
+		c = ipmr_cache_find(mrt, sr.src.s_addr, sr.grp.s_addr);
+		if (c) {
+			sr.pktcnt = c->mfc_un.res.pkt;
+			sr.bytecnt = c->mfc_un.res.bytes;
+			sr.wrong_if = c->mfc_un.res.wrong_if;
+			rcu_read_unlock();
+
+			if (copy_to_user(arg, &sr, sizeof(sr)))
+				return -EFAULT;
+			return 0;
+		}
+		rcu_read_unlock();
+		return -EADDRNOTAVAIL;
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+#endif
+
 
 static int ipmr_device_event(struct notifier_block *this, unsigned long event, void *ptr)
 {
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index 1f85ef2..f5967b0 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -76,6 +76,7 @@
 #include <linux/seq_file.h>
 #include <linux/netfilter.h>
 #include <linux/netfilter_ipv4.h>
+#include <linux/compat.h>
 
 static struct raw_hashinfo raw_v4_hashinfo = {
 	.lock = __RW_LOCK_UNLOCKED(raw_v4_hashinfo.lock),
@@ -839,6 +840,23 @@ static int raw_ioctl(struct sock *sk, int cmd, unsigned long arg)
 	}
 }
 
+#ifdef CONFIG_COMPAT
+static int compat_raw_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+	case SIOCOUTQ:
+	case SIOCINQ:
+		return -ENOIOCTLCMD;
+	default:
+#ifdef CONFIG_IP_MROUTE
+		return ipmr_compat_ioctl(sk, cmd, compat_ptr(arg));
+#else
+		return -ENOIOCTLCMD;
+#endif	
+	}
+}
+#endif
+
 struct proto raw_prot = {
 	.name		   = "RAW",
 	.owner		   = THIS_MODULE,
@@ -861,6 +879,7 @@ struct proto raw_prot = {
 #ifdef CONFIG_COMPAT
 	.compat_setsockopt = compat_raw_setsockopt,
 	.compat_getsockopt = compat_raw_getsockopt,
+	.compat_ioctl	   = compat_raw_ioctl,
 #endif
 };
 
-- 
1.6.5.2.143.g8cc62


^ permalink raw reply related

* [PATCH] net: Fix ip link add netns oops
From: Eric W. Biederman @ 2011-01-30  0:57 UTC (permalink / raw)
  To: netdev; +Cc: David Miller, Ed Swierk, stable


Ed Swierk <eswierk@bigswitch.com> writes:
> On 2.6.35.7
>  ip link add link eth0 netns 9999 type macvlan
> where 9999 is a nonexistent PID triggers an oops and causes all network functions to hang:
> [10663.821898] BUG: unable to handle kernel NULL pointer dereference at 000000000000006d
>  [10663.821917] IP: [<ffffffff8149c2fa>] __dev_alloc_name+0x9a/0x170
>  [10663.821933] PGD 1d3927067 PUD 22f5c5067 PMD 0
>  [10663.821944] Oops: 0000 [#1] SMP
>  [10663.821953] last sysfs file: /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
>  [10663.821959] CPU 3
>  [10663.821963] Modules linked in: macvlan ip6table_filter ip6_tables rfcomm ipt_MASQUERADE binfmt_misc iptable_nat nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_state nf_conntrack sco ipt_REJECT bnep l2cap xt_tcpudp iptable_filter ip_tables x_tables bridge stp vboxnetadp vboxnetflt vboxdrv kvm_intel kvm parport_pc ppdev snd_hda_codec_intelhdmi snd_hda_codec_conexant arc4 iwlagn iwlcore mac80211 snd_hda_intel snd_hda_codec snd_hwdep snd_pcm snd_seq_midi snd_rawmidi i915 snd_seq_midi_event snd_seq thinkpad_acpi drm_kms_helper btusb tpm_tis nvram uvcvideo snd_timer snd_seq_device bluetooth videodev v4l1_compat v4l2_compat_ioctl32 tpm drm tpm_bios snd cfg80211 psmouse serio_raw intel_ips soundcore snd_page_alloc intel_agp i2c_algo_bit video output netconsole configfs lp parport usbhid hid e100
 0e sdhci_pci ahci libahci sdhci led_class
>  [10663.822155]
>  [10663.822161] Pid: 6000, comm: ip Not tainted 2.6.35-23-generic #41-Ubuntu 2901CTO/2901CTO
>  [10663.822167] RIP: 0010:[<ffffffff8149c2fa>] [<ffffffff8149c2fa>] __dev_alloc_name+0x9a/0x170
>  [10663.822177] RSP: 0018:ffff88014aebf7b8 EFLAGS: 00010286
>  [10663.822182] RAX: 00000000fffffff4 RBX: ffff8801ad900800 RCX: 0000000000000000
>  [10663.822187] RDX: ffff880000000000 RSI: 0000000000000000 RDI: ffff88014ad63000
>  [10663.822191] RBP: ffff88014aebf808 R08: 0000000000000041 R09: 0000000000000041
>  [10663.822196] R10: 0000000000000000 R11: dead000000200200 R12: ffff88014aebf818
>  [10663.822201] R13: fffffffffffffffd R14: ffff88014aebf918 R15: ffff88014ad62000
>  [10663.822207] FS: 00007f00c487f700(0000) GS:ffff880001f80000(0000) knlGS:0000000000000000
>  [10663.822212] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
>  [10663.822216] CR2: 000000000000006d CR3: 0000000231f19000 CR4: 00000000000026e0
>  [10663.822221] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
>  [10663.822226] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
>  [10663.822231] Process ip (pid: 6000, threadinfo ffff88014aebe000, task ffff88014afb16e0)
>  [10663.822236] Stack:
>  [10663.822240] ffff88014aebf808 ffffffff814a2bb5 ffff88014aebf7e8 00000000a00ee8d6
>  [10663.822251] <0> 0000000000000000 ffffffffa00ef940 ffff8801ad900800 ffff88014aebf818
>  [10663.822265] <0> ffff88014aebf918 ffff8801ad900800 ffff88014aebf858 ffffffff8149c413
>  [10663.822281] Call Trace:
>  [10663.822290] [<ffffffff814a2bb5>] ? dev_addr_init+0x75/0xb0
>  [10663.822298] [<ffffffff8149c413>] dev_alloc_name+0x43/0x90
>  [10663.822307] [<ffffffff814a85ee>] rtnl_create_link+0xbe/0x1b0
>  [10663.822314] [<ffffffff814ab2aa>] rtnl_newlink+0x48a/0x570
>  [10663.822321] [<ffffffff814aafcc>] ? rtnl_newlink+0x1ac/0x570
>  [10663.822332] [<ffffffff81030064>] ? native_x2apic_icr_read+0x4/0x20
>  [10663.822339] [<ffffffff814a8c17>] rtnetlink_rcv_msg+0x177/0x290
>  [10663.822346] [<ffffffff814a8aa0>] ? rtnetlink_rcv_msg+0x0/0x290
>  [10663.822354] [<ffffffff814c25d9>] netlink_rcv_skb+0xa9/0xd0
>  [10663.822360] [<ffffffff814a8a85>] rtnetlink_rcv+0x25/0x40
>  [10663.822367] [<ffffffff814c223e>] netlink_unicast+0x2de/0x2f0
>  [10663.822374] [<ffffffff814c303e>] netlink_sendmsg+0x1fe/0x2e0
>  [10663.822383] [<ffffffff81488533>] sock_sendmsg+0xf3/0x120
>  [10663.822391] [<ffffffff815899fe>] ? _raw_spin_lock+0xe/0x20
>  [10663.822400] [<ffffffff81168656>] ? __d_lookup+0x136/0x150
>  [10663.822406] [<ffffffff815899fe>] ? _raw_spin_lock+0xe/0x20
>  [10663.822414] [<ffffffff812b7a0d>] ? _atomic_dec_and_lock+0x4d/0x80
>  [10663.822422] [<ffffffff8116ea90>] ? mntput_no_expire+0x30/0x110
>  [10663.822429] [<ffffffff81486ff5>] ? move_addr_to_kernel+0x65/0x70
>  [10663.822435] [<ffffffff81493308>] ? verify_iovec+0x88/0xe0
>  [10663.822442] [<ffffffff81489020>] sys_sendmsg+0x240/0x3a0
> [10663.822450] [<ffffffff8111e2a9>] ? __do_fault+0x479/0x560
>  [10663.822457] [<ffffffff815899fe>] ? _raw_spin_lock+0xe/0x20
>  [10663.822465] [<ffffffff8116cf4a>] ? alloc_fd+0x10a/0x150
>  [10663.822473] [<ffffffff8158d76e>] ? do_page_fault+0x15e/0x350
>  [10663.822482] [<ffffffff8100a0f2>] system_call_fastpath+0x16/0x1b
>  [10663.822487] Code: 90 48 8d 78 02 be 25 00 00 00 e8 92 1d e2 ff 48 85 c0 75 cf bf 20 00 00 00 e8 c3 b1 c6 ff 49 89 c7 b8 f4 ff ff ff 4d 85 ff 74 bd <4d> 8b 75 70 49 8d 45 70 48 89 45 b8 49 83 ee 58 eb 28 48 8d 55
>  [10663.822618] RIP [<ffffffff8149c2fa>] __dev_alloc_name+0x9a/0x170
>  [10663.822627] RSP <ffff88014aebf7b8>
>  [10663.822631] CR2: 000000000000006d
>  [10663.822636] ---[ end trace 3dfd6c3ad5327ca7 ]---

This bug was introduced in:
commit 81adee47dfb608df3ad0b91d230fb3cef75f0060
Author: Eric W. Biederman <ebiederm@aristanetworks.com>
Date:   Sun Nov 8 00:53:51 2009 -0800

    net: Support specifying the network namespace upon device creation.

    There is no good reason to not support userspace specifying the
    network namespace during device creation, and it makes it easier
    to create a network device and pass it to a child network namespace
    with a well known name.

    We have to be careful to ensure that the target network namespace
    for the new device exists through the life of the call.  To keep
    that logic clear I have factored out the network namespace grabbing
    logic into rtnl_link_get_net.

    In addtion we need to continue to pass the source network namespace
    to the rtnl_link_ops.newlink method so that we can find the base
    device source network namespace.

    Signed-off-by: Eric W. Biederman <ebiederm@aristanetworks.com>
    Acked-by: Eric Dumazet <eric.dumazet@gmail.com>

Where apparently I forgot to add error handling to the path where we create
a new network device in a new network namespace, and pass in an invalid pid.

Cc: stable@kernel.org
Reported-by: Ed Swierk <eswierk@bigswitch.com>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
---
 net/core/rtnetlink.c |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)

diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index b2a718d..d84fef4 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -1517,6 +1517,9 @@ replay:
 			snprintf(ifname, IFNAMSIZ, "%s%%d", ops->kind);
 
 		dest_net = rtnl_link_get_net(net, tb);
+		if (IS_ERR(dest_net))
+			return PTR_ERR(dest_net);
+
 		dev = rtnl_create_link(net, dest_net, ifname, ops, tb);
 
 		if (IS_ERR(dev))
-- 
1.6.5.2.143.g8cc62


^ permalink raw reply related

* [PATCH] caif: bugfix - add caif headers for userspace usage.
From: Sjur Brændeland @ 2011-01-29 23:10 UTC (permalink / raw)
  To: netdev, davem; +Cc: Sjur Brændeland

Add caif_socket.h and if_caif.h to the kernel header files
exported for use by userspace.

Signed-off-by: Sjur Braendeland <sjur.brandeland@stericsson.com>
---
Hi Dave,

This patch applies cleanly on net-2.6 and net-next-2.6
Please apply as appropriate.

Thanks,
Sjur Brændeland

 include/linux/Kbuild      |    1 +
 include/linux/caif/Kbuild |    2 ++
 2 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/caif/Kbuild

diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 2296d8b..b0ada6f 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -1,5 +1,6 @@
 header-y += byteorder/
 header-y += can/
+header-y += caif/
 header-y += dvb/
 header-y += hdlc/
 header-y += isdn/
diff --git a/include/linux/caif/Kbuild b/include/linux/caif/Kbuild
new file mode 100644
index 0000000..a9cf250
--- /dev/null
+++ b/include/linux/caif/Kbuild
@@ -0,0 +1,2 @@
+header-y += caif_socket.h
+header-y += if_caif.h
-- 
1.7.0.4


^ permalink raw reply related

* Re: [PATCH] ethtool: implement G/SFEATURES calls
From: Ben Hutchings @ 2011-01-29 22:47 UTC (permalink / raw)
  To: Michał Mirosław; +Cc: netdev
In-Reply-To: <4d4fee145908eb232f2766b7d8cbfebaeeffc5e5.1296325736.git.mirq-linux@rere.qmqm.pl>

On Sat, 2011-01-29 at 19:39 +0100, Michał Mirosław wrote:
[...]
> @@ -196,8 +202,8 @@ static struct option {
>  		"		[ rx-mini N ]\n"
>  		"		[ rx-jumbo N ]\n"
>  	        "		[ tx N ]\n" },
> -    { "-k", "--show-offload", MODE_GOFFLOAD, "Get protocol offload information" },
> -    { "-K", "--offload", MODE_SOFFLOAD, "Set protocol offload",
> +    { "-k", "--show-offload", MODE_GOFFLOAD, "Get protocol offload information (deprecated)" },
> +    { "-K", "--offload", MODE_SOFFLOAD, "Set protocol offload (deprecated)",
>  		"		[ rx on|off ]\n"
>  		"		[ tx on|off ]\n"
>  		"		[ sg on|off ]\n"
[...]

I see no reason to deprecate the existing options.

Ben.

-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


^ permalink raw reply

* Re: [PATCH v3 4/5] net: introduce NETIF_F_RXCSUM
From: Ben Hutchings @ 2011-01-29 22:45 UTC (permalink / raw)
  To: Michał Mirosław; +Cc: netdev
In-Reply-To: <6ca4686615452830809f496a494c7b8ae6d9f082.1296325509.git.mirq-linux@rere.qmqm.pl>

On Sat, 2011-01-29 at 19:39 +0100, Michał Mirosław wrote:
> Introduce NETIF_F_RXCSUM to replace device-private flags for RX checksum
> offload. Integrate it with ndo_fix_features.
> 
> ethtool_op_get_rx_csum() is removed altogether as nothing in-tree uses it.
> 
> Signed-off-by: Michał Mirosław <mirq-linux@rere.qmqm.pl>
Reviewed-by: Ben Hutchings <bhutchings@solarflare.com>

Ben.

-- 
Ben Hutchings, Senior Software Engineer, Solarflare Communications
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


^ 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