summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-07-22 16:49:58 (GMT)
committerLinus Torvalds <torvalds@linux-foundation.org>2009-07-22 16:49:58 (GMT)
commit402168cee1a0b791c54c54a68ae21bb26880f40b (patch)
tree127b1bd8f2d06b18449998022eaab1c20baab950
parent12e8ffe35eaca1411afc1f3124478f8e2b65eedc (diff)
parenta947a39d52f5b647a2fd5eca55d39e722a2fa90f (diff)
downloadlinux-fsl-qoriq-402168cee1a0b791c54c54a68ae21bb26880f40b.tar.xz
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6: (37 commits) sky2: Avoid races in sky2_down drivers/net/mlx4: Adjust constant drivers/net: Move a dereference below a NULL test drivers/net: Move a dereference below a NULL test connector: maintainer/mail update. USB host CDC Phonet network interface driver macsonic, jazzsonic: fix oops on module unload macsonic: move probe function to .devinit.text can: switch carrier on if device was stopped while in bus-off state can: restart device even if dev_alloc_skb() fails can: sja1000: remove duplicated includes New device ID for sc92031 [1088:2031] 3c589_cs: re-initialize the multicast in the tc589_reset Fix error return for setsockopt(SO_TIMESTAMPING) netxen: fix thermal check and shutdown netxen: fix deadlock on dev close netxen: fix context deletion sequence net: Micrel KS8851 SPI network driver tcp: Use correct peer adr when copying MD5 keys tcp: Fix MD5 signature checking on IPv4 mapped sockets ...
-rw-r--r--CREDITS2
-rw-r--r--Documentation/RCU/rculist_nulls.txt7
-rw-r--r--Documentation/connector/cn_test.c4
-rw-r--r--Documentation/connector/ucon.c2
-rw-r--r--Documentation/networking/6pack.txt2
-rw-r--r--MAINTAINERS11
-rw-r--r--drivers/connector/cn_queue.c2
-rw-r--r--drivers/connector/connector.c4
-rw-r--r--drivers/net/Kconfig6
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/arm/Kconfig8
-rw-r--r--drivers/net/arm/Makefile1
-rw-r--r--drivers/net/arm/w90p910_ether.c1105
-rw-r--r--drivers/net/atl1c/atl1c.h8
-rw-r--r--drivers/net/atl1c/atl1c_main.c2
-rw-r--r--drivers/net/bnx2x_link.c3
-rw-r--r--drivers/net/bonding/bond_main.c12
-rw-r--r--drivers/net/can/dev.c8
-rw-r--r--drivers/net/can/sja1000/sja1000.c1
-rw-r--r--drivers/net/e100.c3
-rw-r--r--drivers/net/hamradio/6pack.c2
-rw-r--r--drivers/net/ibm_newemac/rgmii.c7
-rw-r--r--drivers/net/ixgbe/ixgbe_dcb_nl.c6
-rw-r--r--drivers/net/jazzsonic.c1
-rw-r--r--drivers/net/ks8851.c1322
-rw-r--r--drivers/net/ks8851.h296
-rw-r--r--drivers/net/macsonic.c15
-rw-r--r--drivers/net/mlx4/en_ethtool.c2
-rw-r--r--drivers/net/netxen/netxen_nic.h3
-rw-r--r--drivers/net/netxen/netxen_nic_ctx.c13
-rw-r--r--drivers/net/netxen/netxen_nic_hw.c9
-rw-r--r--drivers/net/netxen/netxen_nic_init.c5
-rw-r--r--drivers/net/netxen/netxen_nic_main.c36
-rw-r--r--drivers/net/pcmcia/3c589_cs.c21
-rw-r--r--drivers/net/sc92031.c1
-rw-r--r--drivers/net/sky2.c25
-rw-r--r--drivers/net/usb/Kconfig8
-rw-r--r--drivers/net/usb/Makefile1
-rw-r--r--drivers/net/usb/cdc-phonet.c461
-rw-r--r--drivers/net/usb/cdc_eem.c2
-rw-r--r--include/net/sock.h32
-rw-r--r--include/net/tcp.h5
-rw-r--r--net/can/bcm.c4
-rw-r--r--net/can/raw.c4
-rw-r--r--net/core/sock.c22
-rw-r--r--net/ipv4/tcp_ipv4.c3
-rw-r--r--net/ipv4/tcp_output.c2
-rw-r--r--net/ipv6/tcp_ipv6.c3
-rw-r--r--net/netfilter/nf_conntrack_core.c21
-rw-r--r--net/netfilter/xt_osf.c5
50 files changed, 3417 insertions, 112 deletions
diff --git a/CREDITS b/CREDITS
index 2b88fb3..e76d300 100644
--- a/CREDITS
+++ b/CREDITS
@@ -1856,7 +1856,7 @@ E: rfkoenig@immd4.informatik.uni-erlangen.de
D: The Linux Support Team Erlangen
N: Andreas Koensgen
-E: ajk@iehk.rwth-aachen.de
+E: ajk@comnets.uni-bremen.de
D: 6pack driver for AX.25
N: Harald Koerfgen
diff --git a/Documentation/RCU/rculist_nulls.txt b/Documentation/RCU/rculist_nulls.txt
index 93cb28d..18f9651 100644
--- a/Documentation/RCU/rculist_nulls.txt
+++ b/Documentation/RCU/rculist_nulls.txt
@@ -83,11 +83,12 @@ not detect it missed following items in original chain.
obj = kmem_cache_alloc(...);
lock_chain(); // typically a spin_lock()
obj->key = key;
-atomic_inc(&obj->refcnt);
/*
* we need to make sure obj->key is updated before obj->next
+ * or obj->refcnt
*/
smp_wmb();
+atomic_set(&obj->refcnt, 1);
hlist_add_head_rcu(&obj->obj_node, list);
unlock_chain(); // typically a spin_unlock()
@@ -159,6 +160,10 @@ out:
obj = kmem_cache_alloc(cachep);
lock_chain(); // typically a spin_lock()
obj->key = key;
+/*
+ * changes to obj->key must be visible before refcnt one
+ */
+smp_wmb();
atomic_set(&obj->refcnt, 1);
/*
* insert obj in RCU way (readers might be traversing chain)
diff --git a/Documentation/connector/cn_test.c b/Documentation/connector/cn_test.c
index f688eba..6a5be5d 100644
--- a/Documentation/connector/cn_test.c
+++ b/Documentation/connector/cn_test.c
@@ -1,7 +1,7 @@
/*
* cn_test.c
*
- * 2004-2005 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -194,5 +194,5 @@ module_init(cn_test_init);
module_exit(cn_test_fini);
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
MODULE_DESCRIPTION("Connector's test module");
diff --git a/Documentation/connector/ucon.c b/Documentation/connector/ucon.c
index d738cde..c5092ad 100644
--- a/Documentation/connector/ucon.c
+++ b/Documentation/connector/ucon.c
@@ -1,7 +1,7 @@
/*
* ucon.c
*
- * Copyright (c) 2004+ Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * Copyright (c) 2004+ Evgeniy Polyakov <zbr@ioremap.net>
*
*
* This program is free software; you can redistribute it and/or modify
diff --git a/Documentation/networking/6pack.txt b/Documentation/networking/6pack.txt
index d0777a1..8f33942 100644
--- a/Documentation/networking/6pack.txt
+++ b/Documentation/networking/6pack.txt
@@ -1,7 +1,7 @@
This is the 6pack-mini-HOWTO, written by
Andreas Könsgen DG3KQ
-Internet: ajk@iehk.rwth-aachen.de
+Internet: ajk@comnets.uni-bremen.de
AMPR-net: dg3kq@db0pra.ampr.org
AX.25: dg3kq@db0ach.#nrw.deu.eu
diff --git a/MAINTAINERS b/MAINTAINERS
index 18c3f0c..ebc2691 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -150,7 +150,7 @@ F: drivers/scsi/53c700*
6PACK NETWORK DRIVER FOR AX.25
P: Andreas Koensgen
-M: ajk@iehk.rwth-aachen.de
+M: ajk@comnets.uni-bremen.de
L: linux-hams@vger.kernel.org
S: Maintained
F: drivers/net/hamradio/6pack.c
@@ -1612,6 +1612,13 @@ S: Supported
F: fs/configfs/
F: include/linux/configfs.h
+CONNECTOR
+P: Evgeniy Polyakov
+M: zbr@ioremap.net
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/connector/
+
CONTROL GROUPS (CGROUPS)
P: Paul Menage
M: menage@google.com
@@ -4089,6 +4096,7 @@ L: netfilter@vger.kernel.org
L: coreteam@netfilter.org
W: http://www.netfilter.org/
W: http://www.iptables.org/
+T: git://git.kernel.org/pub/scm/linux/kernel/git/kaber/nf-2.6.git
S: Supported
F: include/linux/netfilter*
F: include/linux/netfilter/
@@ -5586,7 +5594,6 @@ S: Odd Fixes
F: drivers/net/starfire*
STARMODE RADIO IP (STRIP) PROTOCOL DRIVER
-W: http://mosquitonet.Stanford.EDU/strip.html
S: Orphan
F: drivers/net/wireless/strip.c
F: include/linux/if_strip.h
diff --git a/drivers/connector/cn_queue.c b/drivers/connector/cn_queue.c
index c769ef2..408c2af 100644
--- a/drivers/connector/cn_queue.c
+++ b/drivers/connector/cn_queue.c
@@ -1,7 +1,7 @@
/*
* cn_queue.c
*
- * 2004-2005 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c
index fd336c5..08b2500 100644
--- a/drivers/connector/connector.c
+++ b/drivers/connector/connector.c
@@ -1,7 +1,7 @@
/*
* connector.c
*
- * 2004-2005 Copyright (c) Evgeniy Polyakov <johnpol@2ka.mipt.ru>
+ * 2004+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
@@ -33,7 +33,7 @@
#include <net/sock.h>
MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
+MODULE_AUTHOR("Evgeniy Polyakov <zbr@ioremap.net>");
MODULE_DESCRIPTION("Generic userspace <-> kernelspace connector.");
static u32 cn_idx = CN_IDX_CONNECTOR;
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index c155bd3..b5a7513 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1729,6 +1729,12 @@ config KS8842
help
This platform driver is for Micrel KSZ8842 chip.
+config KS8851
+ tristate "Micrel KS8851 SPI"
+ depends on SPI
+ help
+ SPI driver for Micrel KS8851 SPI attached network chip.
+
config VIA_RHINE
tristate "VIA Rhine support"
depends on NET_PCI && PCI
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 4b58a59..ead8cab 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -88,6 +88,7 @@ obj-$(CONFIG_SKGE) += skge.o
obj-$(CONFIG_SKY2) += sky2.o
obj-$(CONFIG_SKFP) += skfp/
obj-$(CONFIG_KS8842) += ks8842.o
+obj-$(CONFIG_KS8851) += ks8851.o
obj-$(CONFIG_VIA_RHINE) += via-rhine.o
obj-$(CONFIG_VIA_VELOCITY) += via-velocity.o
obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o
diff --git a/drivers/net/arm/Kconfig b/drivers/net/arm/Kconfig
index 2895db1..c37ee9e 100644
--- a/drivers/net/arm/Kconfig
+++ b/drivers/net/arm/Kconfig
@@ -63,3 +63,11 @@ config IXP4XX_ETH
help
Say Y here if you want to use built-in Ethernet ports
on IXP4xx processor.
+
+config W90P910_ETH
+ tristate "Nuvoton w90p910 Ethernet support"
+ depends on ARM && ARCH_W90X900
+ select PHYLIB
+ help
+ Say Y here if you want to use built-in Ethernet ports
+ on w90p910 processor.
diff --git a/drivers/net/arm/Makefile b/drivers/net/arm/Makefile
index 811a3cc..303171f 100644
--- a/drivers/net/arm/Makefile
+++ b/drivers/net/arm/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_ARM_AT91_ETHER) += at91_ether.o
obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
obj-$(CONFIG_EP93XX_ETH) += ep93xx_eth.o
obj-$(CONFIG_IXP4XX_ETH) += ixp4xx_eth.o
+obj-$(CONFIG_W90P910_ETH) += w90p910_ether.o
diff --git a/drivers/net/arm/w90p910_ether.c b/drivers/net/arm/w90p910_ether.c
new file mode 100644
index 0000000..616fb79
--- /dev/null
+++ b/drivers/net/arm/w90p910_ether.c
@@ -0,0 +1,1105 @@
+/*
+ * Copyright (c) 2008-2009 Nuvoton technology corporation.
+ *
+ * Wan ZongShun <mcuos.com@gmail.com>
+ *
+ * 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;version 2 of the License.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mii.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#define DRV_MODULE_NAME "w90p910-emc"
+#define DRV_MODULE_VERSION "0.1"
+
+/* Ethernet MAC Registers */
+#define REG_CAMCMR 0x00
+#define REG_CAMEN 0x04
+#define REG_CAMM_BASE 0x08
+#define REG_CAML_BASE 0x0c
+#define REG_TXDLSA 0x88
+#define REG_RXDLSA 0x8C
+#define REG_MCMDR 0x90
+#define REG_MIID 0x94
+#define REG_MIIDA 0x98
+#define REG_FFTCR 0x9C
+#define REG_TSDR 0xa0
+#define REG_RSDR 0xa4
+#define REG_DMARFC 0xa8
+#define REG_MIEN 0xac
+#define REG_MISTA 0xb0
+#define REG_CTXDSA 0xcc
+#define REG_CTXBSA 0xd0
+#define REG_CRXDSA 0xd4
+#define REG_CRXBSA 0xd8
+
+/* mac controller bit */
+#define MCMDR_RXON 0x01
+#define MCMDR_ACP (0x01 << 3)
+#define MCMDR_SPCRC (0x01 << 5)
+#define MCMDR_TXON (0x01 << 8)
+#define MCMDR_FDUP (0x01 << 18)
+#define MCMDR_ENMDC (0x01 << 19)
+#define MCMDR_OPMOD (0x01 << 20)
+#define SWR (0x01 << 24)
+
+/* cam command regiser */
+#define CAMCMR_AUP 0x01
+#define CAMCMR_AMP (0x01 << 1)
+#define CAMCMR_ABP (0x01 << 2)
+#define CAMCMR_CCAM (0x01 << 3)
+#define CAMCMR_ECMP (0x01 << 4)
+#define CAM0EN 0x01
+
+/* mac mii controller bit */
+#define MDCCR (0x0a << 20)
+#define PHYAD (0x01 << 8)
+#define PHYWR (0x01 << 16)
+#define PHYBUSY (0x01 << 17)
+#define PHYPRESP (0x01 << 18)
+#define CAM_ENTRY_SIZE 0x08
+
+/* rx and tx status */
+#define TXDS_TXCP (0x01 << 19)
+#define RXDS_CRCE (0x01 << 17)
+#define RXDS_PTLE (0x01 << 19)
+#define RXDS_RXGD (0x01 << 20)
+#define RXDS_ALIE (0x01 << 21)
+#define RXDS_RP (0x01 << 22)
+
+/* mac interrupt status*/
+#define MISTA_EXDEF (0x01 << 19)
+#define MISTA_TXBERR (0x01 << 24)
+#define MISTA_TDU (0x01 << 23)
+#define MISTA_RDU (0x01 << 10)
+#define MISTA_RXBERR (0x01 << 11)
+
+#define ENSTART 0x01
+#define ENRXINTR 0x01
+#define ENRXGD (0x01 << 4)
+#define ENRXBERR (0x01 << 11)
+#define ENTXINTR (0x01 << 16)
+#define ENTXCP (0x01 << 18)
+#define ENTXABT (0x01 << 21)
+#define ENTXBERR (0x01 << 24)
+#define ENMDC (0x01 << 19)
+#define PHYBUSY (0x01 << 17)
+#define MDCCR_VAL 0xa00000
+
+/* rx and tx owner bit */
+#define RX_OWEN_DMA (0x01 << 31)
+#define RX_OWEN_CPU (~(0x03 << 30))
+#define TX_OWEN_DMA (0x01 << 31)
+#define TX_OWEN_CPU (~(0x01 << 31))
+
+/* tx frame desc controller bit */
+#define MACTXINTEN 0x04
+#define CRCMODE 0x02
+#define PADDINGMODE 0x01
+
+/* fftcr controller bit */
+#define TXTHD (0x03 << 8)
+#define BLENGTH (0x01 << 20)
+
+/* global setting for driver */
+#define RX_DESC_SIZE 50
+#define TX_DESC_SIZE 10
+#define MAX_RBUFF_SZ 0x600
+#define MAX_TBUFF_SZ 0x600
+#define TX_TIMEOUT 50
+#define DELAY 1000
+#define CAM0 0x0
+
+static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg);
+
+struct w90p910_rxbd {
+ unsigned int sl;
+ unsigned int buffer;
+ unsigned int reserved;
+ unsigned int next;
+};
+
+struct w90p910_txbd {
+ unsigned int mode;
+ unsigned int buffer;
+ unsigned int sl;
+ unsigned int next;
+};
+
+struct recv_pdesc {
+ struct w90p910_rxbd desclist[RX_DESC_SIZE];
+ char recv_buf[RX_DESC_SIZE][MAX_RBUFF_SZ];
+};
+
+struct tran_pdesc {
+ struct w90p910_txbd desclist[TX_DESC_SIZE];
+ char tran_buf[RX_DESC_SIZE][MAX_TBUFF_SZ];
+};
+
+struct w90p910_ether {
+ struct recv_pdesc *rdesc;
+ struct recv_pdesc *rdesc_phys;
+ struct tran_pdesc *tdesc;
+ struct tran_pdesc *tdesc_phys;
+ struct net_device_stats stats;
+ struct platform_device *pdev;
+ struct sk_buff *skb;
+ struct clk *clk;
+ struct clk *rmiiclk;
+ struct mii_if_info mii;
+ struct timer_list check_timer;
+ void __iomem *reg;
+ unsigned int rxirq;
+ unsigned int txirq;
+ unsigned int cur_tx;
+ unsigned int cur_rx;
+ unsigned int finish_tx;
+ unsigned int rx_packets;
+ unsigned int rx_bytes;
+ unsigned int start_tx_ptr;
+ unsigned int start_rx_ptr;
+ unsigned int linkflag;
+ spinlock_t lock;
+};
+
+static void update_linkspeed_register(struct net_device *dev,
+ unsigned int speed, unsigned int duplex)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = __raw_readl(ether->reg + REG_MCMDR);
+
+ if (speed == SPEED_100) {
+ /* 100 full/half duplex */
+ if (duplex == DUPLEX_FULL) {
+ val |= (MCMDR_OPMOD | MCMDR_FDUP);
+ } else {
+ val |= MCMDR_OPMOD;
+ val &= ~MCMDR_FDUP;
+ }
+ } else {
+ /* 10 full/half duplex */
+ if (duplex == DUPLEX_FULL) {
+ val |= MCMDR_FDUP;
+ val &= ~MCMDR_OPMOD;
+ } else {
+ val &= ~(MCMDR_FDUP | MCMDR_OPMOD);
+ }
+ }
+
+ __raw_writel(val, ether->reg + REG_MCMDR);
+}
+
+static void update_linkspeed(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev;
+ unsigned int bmsr, bmcr, lpa, speed, duplex;
+
+ pdev = ether->pdev;
+
+ if (!mii_link_ok(&ether->mii)) {
+ ether->linkflag = 0x0;
+ netif_carrier_off(dev);
+ dev_warn(&pdev->dev, "%s: Link down.\n", dev->name);
+ return;
+ }
+
+ if (ether->linkflag == 1)
+ return;
+
+ bmsr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMSR);
+ bmcr = w90p910_mdio_read(dev, ether->mii.phy_id, MII_BMCR);
+
+ if (bmcr & BMCR_ANENABLE) {
+ if (!(bmsr & BMSR_ANEGCOMPLETE))
+ return;
+
+ lpa = w90p910_mdio_read(dev, ether->mii.phy_id, MII_LPA);
+
+ if ((lpa & LPA_100FULL) || (lpa & LPA_100HALF))
+ speed = SPEED_100;
+ else
+ speed = SPEED_10;
+
+ if ((lpa & LPA_100FULL) || (lpa & LPA_10FULL))
+ duplex = DUPLEX_FULL;
+ else
+ duplex = DUPLEX_HALF;
+
+ } else {
+ speed = (bmcr & BMCR_SPEED100) ? SPEED_100 : SPEED_10;
+ duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
+ }
+
+ update_linkspeed_register(dev, speed, duplex);
+
+ dev_info(&pdev->dev, "%s: Link now %i-%s\n", dev->name, speed,
+ (duplex == DUPLEX_FULL) ? "FullDuplex" : "HalfDuplex");
+ ether->linkflag = 0x01;
+
+ netif_carrier_on(dev);
+}
+
+static void w90p910_check_link(unsigned long dev_id)
+{
+ struct net_device *dev = (struct net_device *) dev_id;
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ update_linkspeed(dev);
+ mod_timer(&ether->check_timer, jiffies + msecs_to_jiffies(1000));
+}
+
+static void w90p910_write_cam(struct net_device *dev,
+ unsigned int x, unsigned char *pval)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int msw, lsw;
+
+ msw = (pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | pval[3];
+
+ lsw = (pval[4] << 24) | (pval[5] << 16);
+
+ __raw_writel(lsw, ether->reg + REG_CAML_BASE + x * CAM_ENTRY_SIZE);
+ __raw_writel(msw, ether->reg + REG_CAMM_BASE + x * CAM_ENTRY_SIZE);
+}
+
+static void w90p910_init_desc(struct net_device *dev)
+{
+ struct w90p910_ether *ether;
+ struct w90p910_txbd *tdesc, *tdesc_phys;
+ struct w90p910_rxbd *rdesc, *rdesc_phys;
+ unsigned int i, j;
+
+ ether = netdev_priv(dev);
+
+ ether->tdesc = (struct tran_pdesc *)
+ dma_alloc_coherent(NULL, sizeof(struct tran_pdesc),
+ (dma_addr_t *) &ether->tdesc_phys, GFP_KERNEL);
+
+ ether->rdesc = (struct recv_pdesc *)
+ dma_alloc_coherent(NULL, sizeof(struct recv_pdesc),
+ (dma_addr_t *) &ether->rdesc_phys, GFP_KERNEL);
+
+ for (i = 0; i < TX_DESC_SIZE; i++) {
+ tdesc = &(ether->tdesc->desclist[i]);
+
+ j = ((i + 1) / TX_DESC_SIZE);
+
+ if (j != 0) {
+ tdesc_phys = &(ether->tdesc_phys->desclist[0]);
+ ether->start_tx_ptr = (unsigned int)tdesc_phys;
+ tdesc->next = (unsigned int)ether->start_tx_ptr;
+ } else {
+ tdesc_phys = &(ether->tdesc_phys->desclist[i+1]);
+ tdesc->next = (unsigned int)tdesc_phys;
+ }
+
+ tdesc->buffer = (unsigned int)ether->tdesc_phys->tran_buf[i];
+ tdesc->sl = 0;
+ tdesc->mode = 0;
+ }
+
+ for (i = 0; i < RX_DESC_SIZE; i++) {
+ rdesc = &(ether->rdesc->desclist[i]);
+
+ j = ((i + 1) / RX_DESC_SIZE);
+
+ if (j != 0) {
+ rdesc_phys = &(ether->rdesc_phys->desclist[0]);
+ ether->start_rx_ptr = (unsigned int)rdesc_phys;
+ rdesc->next = (unsigned int)ether->start_rx_ptr;
+ } else {
+ rdesc_phys = &(ether->rdesc_phys->desclist[i+1]);
+ rdesc->next = (unsigned int)rdesc_phys;
+ }
+
+ rdesc->sl = RX_OWEN_DMA;
+ rdesc->buffer = (unsigned int)ether->rdesc_phys->recv_buf[i];
+ }
+}
+
+static void w90p910_set_fifo_threshold(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = TXTHD | BLENGTH;
+ __raw_writel(val, ether->reg + REG_FFTCR);
+}
+
+static void w90p910_return_default_idle(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = __raw_readl(ether->reg + REG_MCMDR);
+ val |= SWR;
+ __raw_writel(val, ether->reg + REG_MCMDR);
+}
+
+static void w90p910_trigger_rx(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ __raw_writel(ENSTART, ether->reg + REG_RSDR);
+}
+
+static void w90p910_trigger_tx(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ __raw_writel(ENSTART, ether->reg + REG_TSDR);
+}
+
+static void w90p910_enable_mac_interrupt(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = ENTXINTR | ENRXINTR | ENRXGD | ENTXCP;
+ val |= ENTXBERR | ENRXBERR | ENTXABT;
+
+ __raw_writel(val, ether->reg + REG_MIEN);
+}
+
+static void w90p910_get_and_clear_int(struct net_device *dev,
+ unsigned int *val)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ *val = __raw_readl(ether->reg + REG_MISTA);
+ __raw_writel(*val, ether->reg + REG_MISTA);
+}
+
+static void w90p910_set_global_maccmd(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = __raw_readl(ether->reg + REG_MCMDR);
+ val |= MCMDR_SPCRC | MCMDR_ENMDC | MCMDR_ACP | ENMDC;
+ __raw_writel(val, ether->reg + REG_MCMDR);
+}
+
+static void w90p910_enable_cam(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ w90p910_write_cam(dev, CAM0, dev->dev_addr);
+
+ val = __raw_readl(ether->reg + REG_CAMEN);
+ val |= CAM0EN;
+ __raw_writel(val, ether->reg + REG_CAMEN);
+}
+
+static void w90p910_enable_cam_command(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = CAMCMR_ECMP | CAMCMR_ABP | CAMCMR_AMP;
+ __raw_writel(val, ether->reg + REG_CAMCMR);
+}
+
+static void w90p910_enable_tx(struct net_device *dev, unsigned int enable)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = __raw_readl(ether->reg + REG_MCMDR);
+
+ if (enable)
+ val |= MCMDR_TXON;
+ else
+ val &= ~MCMDR_TXON;
+
+ __raw_writel(val, ether->reg + REG_MCMDR);
+}
+
+static void w90p910_enable_rx(struct net_device *dev, unsigned int enable)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ unsigned int val;
+
+ val = __raw_readl(ether->reg + REG_MCMDR);
+
+ if (enable)
+ val |= MCMDR_RXON;
+ else
+ val &= ~MCMDR_RXON;
+
+ __raw_writel(val, ether->reg + REG_MCMDR);
+}
+
+static void w90p910_set_curdest(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ __raw_writel(ether->start_rx_ptr, ether->reg + REG_RXDLSA);
+ __raw_writel(ether->start_tx_ptr, ether->reg + REG_TXDLSA);
+}
+
+static void w90p910_reset_mac(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ spin_lock(&ether->lock);
+
+ w90p910_enable_tx(dev, 0);
+ w90p910_enable_rx(dev, 0);
+ w90p910_set_fifo_threshold(dev);
+ w90p910_return_default_idle(dev);
+
+ if (!netif_queue_stopped(dev))
+ netif_stop_queue(dev);
+
+ w90p910_init_desc(dev);
+
+ dev->trans_start = jiffies;
+ ether->cur_tx = 0x0;
+ ether->finish_tx = 0x0;
+ ether->cur_rx = 0x0;
+
+ w90p910_set_curdest(dev);
+ w90p910_enable_cam(dev);
+ w90p910_enable_cam_command(dev);
+ w90p910_enable_mac_interrupt(dev);
+ w90p910_enable_tx(dev, 1);
+ w90p910_enable_rx(dev, 1);
+ w90p910_trigger_tx(dev);
+ w90p910_trigger_rx(dev);
+
+ dev->trans_start = jiffies;
+
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+
+ spin_unlock(&ether->lock);
+}
+
+static void w90p910_mdio_write(struct net_device *dev,
+ int phy_id, int reg, int data)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev;
+ unsigned int val, i;
+
+ pdev = ether->pdev;
+
+ __raw_writel(data, ether->reg + REG_MIID);
+
+ val = (phy_id << 0x08) | reg;
+ val |= PHYBUSY | PHYWR | MDCCR_VAL;
+ __raw_writel(val, ether->reg + REG_MIIDA);
+
+ for (i = 0; i < DELAY; i++) {
+ if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0)
+ break;
+ }
+
+ if (i == DELAY)
+ dev_warn(&pdev->dev, "mdio write timed out\n");
+}
+
+static int w90p910_mdio_read(struct net_device *dev, int phy_id, int reg)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev;
+ unsigned int val, i, data;
+
+ pdev = ether->pdev;
+
+ val = (phy_id << 0x08) | reg;
+ val |= PHYBUSY | MDCCR_VAL;
+ __raw_writel(val, ether->reg + REG_MIIDA);
+
+ for (i = 0; i < DELAY; i++) {
+ if ((__raw_readl(ether->reg + REG_MIIDA) & PHYBUSY) == 0)
+ break;
+ }
+
+ if (i == DELAY) {
+ dev_warn(&pdev->dev, "mdio read timed out\n");
+ data = 0xffff;
+ } else {
+ data = __raw_readl(ether->reg + REG_MIID);
+ }
+
+ return data;
+}
+
+static int set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *address = addr;
+
+ if (!is_valid_ether_addr(address->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, address->sa_data, dev->addr_len);
+ w90p910_write_cam(dev, CAM0, dev->dev_addr);
+
+ return 0;
+}
+
+static int w90p910_ether_close(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ dma_free_writecombine(NULL, sizeof(struct w90p910_rxbd),
+ ether->rdesc, (dma_addr_t)ether->rdesc_phys);
+ dma_free_writecombine(NULL, sizeof(struct w90p910_txbd),
+ ether->tdesc, (dma_addr_t)ether->tdesc_phys);
+
+ netif_stop_queue(dev);
+
+ del_timer_sync(&ether->check_timer);
+ clk_disable(ether->rmiiclk);
+ clk_disable(ether->clk);
+
+ free_irq(ether->txirq, dev);
+ free_irq(ether->rxirq, dev);
+
+ return 0;
+}
+
+static struct net_device_stats *w90p910_ether_stats(struct net_device *dev)
+{
+ struct w90p910_ether *ether;
+
+ ether = netdev_priv(dev);
+
+ return &ether->stats;
+}
+
+static int w90p910_send_frame(struct net_device *dev,
+ unsigned char *data, int length)
+{
+ struct w90p910_ether *ether;
+ struct w90p910_txbd *txbd;
+ struct platform_device *pdev;
+ unsigned char *buffer;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ txbd = &ether->tdesc->desclist[ether->cur_tx];
+ buffer = ether->tdesc->tran_buf[ether->cur_tx];
+ if (length > 1514) {
+ dev_err(&pdev->dev, "send data %d bytes, check it\n", length);
+ length = 1514;
+ }
+
+ txbd->sl = length & 0xFFFF;
+
+ memcpy(buffer, data, length);
+
+ txbd->mode = TX_OWEN_DMA | PADDINGMODE | CRCMODE | MACTXINTEN;
+
+ w90p910_enable_tx(dev, 1);
+
+ w90p910_trigger_tx(dev);
+
+ ether->cur_tx = (ether->cur_tx+1) % TX_DESC_SIZE;
+ txbd = &ether->tdesc->desclist[ether->cur_tx];
+
+ dev->trans_start = jiffies;
+
+ if (txbd->mode & TX_OWEN_DMA)
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+static int w90p910_ether_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ if (!(w90p910_send_frame(dev, skb->data, skb->len))) {
+ ether->skb = skb;
+ dev_kfree_skb_irq(skb);
+ return 0;
+ }
+ return -1;
+}
+
+static irqreturn_t w90p910_tx_interrupt(int irq, void *dev_id)
+{
+ struct w90p910_ether *ether;
+ struct w90p910_txbd *txbd;
+ struct platform_device *pdev;
+ struct tran_pdesc *tran_pdesc;
+ struct net_device *dev;
+ unsigned int cur_entry, entry, status;
+
+ dev = (struct net_device *)dev_id;
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ spin_lock(&ether->lock);
+
+ w90p910_get_and_clear_int(dev, &status);
+
+ cur_entry = __raw_readl(ether->reg + REG_CTXDSA);
+
+ tran_pdesc = ether->tdesc_phys;
+ entry = (unsigned int)(&tran_pdesc->desclist[ether->finish_tx]);
+
+ while (entry != cur_entry) {
+ txbd = &ether->tdesc->desclist[ether->finish_tx];
+
+ ether->finish_tx = (ether->finish_tx + 1) % TX_DESC_SIZE;
+
+ if (txbd->sl & TXDS_TXCP) {
+ ether->stats.tx_packets++;
+ ether->stats.tx_bytes += txbd->sl & 0xFFFF;
+ } else {
+ ether->stats.tx_errors++;
+ }
+
+ txbd->sl = 0x0;
+ txbd->mode = 0x0;
+
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+
+ entry = (unsigned int)(&tran_pdesc->desclist[ether->finish_tx]);
+ }
+
+ if (status & MISTA_EXDEF) {
+ dev_err(&pdev->dev, "emc defer exceed interrupt\n");
+ } else if (status & MISTA_TXBERR) {
+ dev_err(&pdev->dev, "emc bus error interrupt\n");
+ w90p910_reset_mac(dev);
+ } else if (status & MISTA_TDU) {
+ if (netif_queue_stopped(dev))
+ netif_wake_queue(dev);
+ }
+
+ spin_unlock(&ether->lock);
+
+ return IRQ_HANDLED;
+}
+
+static void netdev_rx(struct net_device *dev)
+{
+ struct w90p910_ether *ether;
+ struct w90p910_rxbd *rxbd;
+ struct platform_device *pdev;
+ struct recv_pdesc *rdesc_phys;
+ struct sk_buff *skb;
+ unsigned char *data;
+ unsigned int length, status, val, entry;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+ rdesc_phys = ether->rdesc_phys;
+
+ rxbd = &ether->rdesc->desclist[ether->cur_rx];
+
+ do {
+ val = __raw_readl(ether->reg + REG_CRXDSA);
+ entry = (unsigned int)&rdesc_phys->desclist[ether->cur_rx];
+
+ if (val == entry)
+ break;
+
+ status = rxbd->sl;
+ length = status & 0xFFFF;
+
+ if (status & RXDS_RXGD) {
+ data = ether->rdesc->recv_buf[ether->cur_rx];
+ skb = dev_alloc_skb(length+2);
+ if (!skb) {
+ dev_err(&pdev->dev, "get skb buffer error\n");
+ ether->stats.rx_dropped++;
+ return;
+ }
+
+ skb->dev = dev;
+ skb_reserve(skb, 2);
+ skb_put(skb, length);
+ skb_copy_to_linear_data(skb, data, length);
+ skb->protocol = eth_type_trans(skb, dev);
+ ether->stats.rx_packets++;
+ ether->stats.rx_bytes += length;
+ netif_rx(skb);
+ } else {
+ ether->stats.rx_errors++;
+
+ if (status & RXDS_RP) {
+ dev_err(&pdev->dev, "rx runt err\n");
+ ether->stats.rx_length_errors++;
+ } else if (status & RXDS_CRCE) {
+ dev_err(&pdev->dev, "rx crc err\n");
+ ether->stats.rx_crc_errors++;
+ }
+
+ if (status & RXDS_ALIE) {
+ dev_err(&pdev->dev, "rx aligment err\n");
+ ether->stats.rx_frame_errors++;
+ } else if (status & RXDS_PTLE) {
+ dev_err(&pdev->dev, "rx longer err\n");
+ ether->stats.rx_over_errors++;
+ }
+ }
+
+ rxbd->sl = RX_OWEN_DMA;
+ rxbd->reserved = 0x0;
+ ether->cur_rx = (ether->cur_rx+1) % RX_DESC_SIZE;
+ rxbd = &ether->rdesc->desclist[ether->cur_rx];
+
+ dev->last_rx = jiffies;
+ } while (1);
+}
+
+static irqreturn_t w90p910_rx_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev;
+ struct w90p910_ether *ether;
+ struct platform_device *pdev;
+ unsigned int status;
+
+ dev = (struct net_device *)dev_id;
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ spin_lock(&ether->lock);
+
+ w90p910_get_and_clear_int(dev, &status);
+
+ if (status & MISTA_RDU) {
+ netdev_rx(dev);
+
+ w90p910_trigger_rx(dev);
+
+ spin_unlock(&ether->lock);
+ return IRQ_HANDLED;
+ } else if (status & MISTA_RXBERR) {
+ dev_err(&pdev->dev, "emc rx bus error\n");
+ w90p910_reset_mac(dev);
+ }
+
+ netdev_rx(dev);
+ spin_unlock(&ether->lock);
+ return IRQ_HANDLED;
+}
+
+static int w90p910_ether_open(struct net_device *dev)
+{
+ struct w90p910_ether *ether;
+ struct platform_device *pdev;
+
+ ether = netdev_priv(dev);
+ pdev = ether->pdev;
+
+ w90p910_reset_mac(dev);
+ w90p910_set_fifo_threshold(dev);
+ w90p910_set_curdest(dev);
+ w90p910_enable_cam(dev);
+ w90p910_enable_cam_command(dev);
+ w90p910_enable_mac_interrupt(dev);
+ w90p910_set_global_maccmd(dev);
+ w90p910_enable_rx(dev, 1);
+
+ ether->rx_packets = 0x0;
+ ether->rx_bytes = 0x0;
+
+ if (request_irq(ether->txirq, w90p910_tx_interrupt,
+ 0x0, pdev->name, dev)) {
+ dev_err(&pdev->dev, "register irq tx failed\n");
+ return -EAGAIN;
+ }
+
+ if (request_irq(ether->rxirq, w90p910_rx_interrupt,
+ 0x0, pdev->name, dev)) {
+ dev_err(&pdev->dev, "register irq rx failed\n");
+ return -EAGAIN;
+ }
+
+ mod_timer(&ether->check_timer, jiffies + msecs_to_jiffies(1000));
+ netif_start_queue(dev);
+ w90p910_trigger_rx(dev);
+
+ dev_info(&pdev->dev, "%s is OPENED\n", dev->name);
+
+ return 0;
+}
+
+static void w90p910_ether_set_multicast_list(struct net_device *dev)
+{
+ struct w90p910_ether *ether;
+ unsigned int rx_mode;
+
+ ether = netdev_priv(dev);
+
+ if (dev->flags & IFF_PROMISC)
+ rx_mode = CAMCMR_AUP | CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP;
+ else if ((dev->flags & IFF_ALLMULTI) || dev->mc_list)
+ rx_mode = CAMCMR_AMP | CAMCMR_ABP | CAMCMR_ECMP;
+ else
+ rx_mode = CAMCMR_ECMP | CAMCMR_ABP;
+ __raw_writel(rx_mode, ether->reg + REG_CAMCMR);
+}
+
+static int w90p910_ether_ioctl(struct net_device *dev,
+ struct ifreq *ifr, int cmd)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ struct mii_ioctl_data *data = if_mii(ifr);
+
+ return generic_mii_ioctl(&ether->mii, data, cmd, NULL);
+}
+
+static void w90p910_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *info)
+{
+ strcpy(info->driver, DRV_MODULE_NAME);
+ strcpy(info->version, DRV_MODULE_VERSION);
+}
+
+static int w90p910_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ return mii_ethtool_gset(&ether->mii, cmd);
+}
+
+static int w90p910_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ return mii_ethtool_sset(&ether->mii, cmd);
+}
+
+static int w90p910_nway_reset(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ return mii_nway_restart(&ether->mii);
+}
+
+static u32 w90p910_get_link(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ return mii_link_ok(&ether->mii);
+}
+
+static const struct ethtool_ops w90p910_ether_ethtool_ops = {
+ .get_settings = w90p910_get_settings,
+ .set_settings = w90p910_set_settings,
+ .get_drvinfo = w90p910_get_drvinfo,
+ .nway_reset = w90p910_nway_reset,
+ .get_link = w90p910_get_link,
+};
+
+static const struct net_device_ops w90p910_ether_netdev_ops = {
+ .ndo_open = w90p910_ether_open,
+ .ndo_stop = w90p910_ether_close,
+ .ndo_start_xmit = w90p910_ether_start_xmit,
+ .ndo_get_stats = w90p910_ether_stats,
+ .ndo_set_multicast_list = w90p910_ether_set_multicast_list,
+ .ndo_set_mac_address = set_mac_address,
+ .ndo_do_ioctl = w90p910_ether_ioctl,
+ .ndo_validate_addr = eth_validate_addr,
+ .ndo_change_mtu = eth_change_mtu,
+};
+
+static void __init get_mac_address(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+ struct platform_device *pdev;
+ char addr[6];
+
+ pdev = ether->pdev;
+
+ addr[0] = 0x00;
+ addr[1] = 0x02;
+ addr[2] = 0xac;
+ addr[3] = 0x55;
+ addr[4] = 0x88;
+ addr[5] = 0xa8;
+
+ if (is_valid_ether_addr(addr))
+ memcpy(dev->dev_addr, &addr, 0x06);
+ else
+ dev_err(&pdev->dev, "invalid mac address\n");
+}
+
+static int w90p910_ether_setup(struct net_device *dev)
+{
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ ether_setup(dev);
+ dev->netdev_ops = &w90p910_ether_netdev_ops;
+ dev->ethtool_ops = &w90p910_ether_ethtool_ops;
+
+ dev->tx_queue_len = 16;
+ dev->dma = 0x0;
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ get_mac_address(dev);
+
+ spin_lock_init(&ether->lock);
+
+ ether->cur_tx = 0x0;
+ ether->cur_rx = 0x0;
+ ether->finish_tx = 0x0;
+ ether->linkflag = 0x0;
+ ether->mii.phy_id = 0x01;
+ ether->mii.phy_id_mask = 0x1f;
+ ether->mii.reg_num_mask = 0x1f;
+ ether->mii.dev = dev;
+ ether->mii.mdio_read = w90p910_mdio_read;
+ ether->mii.mdio_write = w90p910_mdio_write;
+
+ setup_timer(&ether->check_timer, w90p910_check_link,
+ (unsigned long)dev);
+
+ return 0;
+}
+
+static int __devinit w90p910_ether_probe(struct platform_device *pdev)
+{
+ struct w90p910_ether *ether;
+ struct net_device *dev;
+ struct resource *res;
+ int error;
+
+ dev = alloc_etherdev(sizeof(struct w90p910_ether));
+ if (!dev)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get I/O memory\n");
+ error = -ENXIO;
+ goto failed_free;
+ }
+
+ res = request_mem_region(res->start, resource_size(res), pdev->name);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ error = -EBUSY;
+ goto failed_free;
+ }
+
+ ether = netdev_priv(dev);
+
+ ether->reg = ioremap(res->start, resource_size(res));
+ if (ether->reg == NULL) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto failed_free_mem;
+ }
+
+ ether->txirq = platform_get_irq(pdev, 0);
+ if (ether->txirq < 0) {
+ dev_err(&pdev->dev, "failed to get ether tx irq\n");
+ error = -ENXIO;
+ goto failed_free_io;
+ }
+
+ ether->rxirq = platform_get_irq(pdev, 1);
+ if (ether->rxirq < 0) {
+ dev_err(&pdev->dev, "failed to get ether rx irq\n");
+ error = -ENXIO;
+ goto failed_free_txirq;
+ }
+
+ platform_set_drvdata(pdev, dev);
+
+ ether->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(ether->clk)) {
+ dev_err(&pdev->dev, "failed to get ether clock\n");
+ error = PTR_ERR(ether->clk);
+ goto failed_free_rxirq;
+ }
+
+ ether->rmiiclk = clk_get(&pdev->dev, "RMII");
+ if (IS_ERR(ether->rmiiclk)) {
+ dev_err(&pdev->dev, "failed to get ether clock\n");
+ error = PTR_ERR(ether->rmiiclk);
+ goto failed_put_clk;
+ }
+
+ ether->pdev = pdev;
+
+ w90p910_ether_setup(dev);
+
+ error = register_netdev(dev);
+ if (error != 0) {
+ dev_err(&pdev->dev, "Regiter EMC w90p910 FAILED\n");
+ error = -ENODEV;
+ goto failed_put_rmiiclk;
+ }
+
+ return 0;
+failed_put_rmiiclk:
+ clk_put(ether->rmiiclk);
+failed_put_clk:
+ clk_put(ether->clk);
+failed_free_rxirq:
+ free_irq(ether->rxirq, pdev);
+ platform_set_drvdata(pdev, NULL);
+failed_free_txirq:
+ free_irq(ether->txirq, pdev);
+failed_free_io:
+ iounmap(ether->reg);
+failed_free_mem:
+ release_mem_region(res->start, resource_size(res));
+failed_free:
+ free_netdev(dev);
+ return error;
+}
+
+static int __devexit w90p910_ether_remove(struct platform_device *pdev)
+{
+ struct net_device *dev = platform_get_drvdata(pdev);
+ struct w90p910_ether *ether = netdev_priv(dev);
+
+ unregister_netdev(dev);
+ clk_put(ether->rmiiclk);
+ clk_put(ether->clk);
+ del_timer_sync(&ether->check_timer);
+ platform_set_drvdata(pdev, NULL);
+ free_netdev(dev);
+ return 0;
+}
+
+static struct platform_driver w90p910_ether_driver = {
+ .probe = w90p910_ether_probe,
+ .remove = __devexit_p(w90p910_ether_remove),
+ .driver = {
+ .name = "w90p910-emc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init w90p910_ether_init(void)
+{
+ return platform_driver_register(&w90p910_ether_driver);
+}
+
+static void __exit w90p910_ether_exit(void)
+{
+ platform_driver_unregister(&w90p910_ether_driver);
+}
+
+module_init(w90p910_ether_init);
+module_exit(w90p910_ether_exit);
+
+MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
+MODULE_DESCRIPTION("w90p910 MAC driver!");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:w90p910-emc");
+
diff --git a/drivers/net/atl1c/atl1c.h b/drivers/net/atl1c/atl1c.h
index e1658ef..2a1120a 100644
--- a/drivers/net/atl1c/atl1c.h
+++ b/drivers/net/atl1c/atl1c.h
@@ -188,14 +188,14 @@ struct atl1c_tpd_ext_desc {
#define RRS_HDS_TYPE_DATA 2
#define RRS_IS_NO_HDS_TYPE(flag) \
- (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == 0)
+ ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == 0)
#define RRS_IS_HDS_HEAD(flag) \
- (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == \
+ ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \
RRS_HDS_TYPE_HEAD)
#define RRS_IS_HDS_DATA(flag) \
- (((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK == \
+ ((((flag) >> (RRS_HDS_TYPE_SHIFT)) & RRS_HDS_TYPE_MASK) == \
RRS_HDS_TYPE_DATA)
/* rrs word 3 bit 0:31 */
@@ -245,7 +245,7 @@ struct atl1c_tpd_ext_desc {
#define RRS_PACKET_TYPE_802_3 1
#define RRS_PACKET_TYPE_ETH 0
#define RRS_PACKET_IS_ETH(word) \
- (((word) >> RRS_PACKET_TYPE_SHIFT) & RRS_PACKET_TYPE_MASK == \
+ ((((word) >> RRS_PACKET_TYPE_SHIFT) & RRS_PACKET_TYPE_MASK) == \
RRS_PACKET_TYPE_ETH)
#define RRS_RXD_IS_VALID(word) \
((((word) >> RRS_RXD_UPDATED_SHIFT) & RRS_RXD_UPDATED_MASK) == 1)
diff --git a/drivers/net/atl1c/atl1c_main.c b/drivers/net/atl1c/atl1c_main.c
index cd547a2..a383122 100644
--- a/drivers/net/atl1c/atl1c_main.c
+++ b/drivers/net/atl1c/atl1c_main.c
@@ -1689,7 +1689,7 @@ static void atl1c_clean_rx_irq(struct atl1c_adapter *adapter, u8 que,
if (likely(RRS_RXD_IS_VALID(rrs->word3))) {
rfd_num = (rrs->word0 >> RRS_RX_RFD_CNT_SHIFT) &
RRS_RX_RFD_CNT_MASK;
- if (unlikely(rfd_num) != 1)
+ if (unlikely(rfd_num != 1))
/* TODO support mul rfd*/
if (netif_msg_rx_err(adapter))
dev_warn(&pdev->dev,
diff --git a/drivers/net/bnx2x_link.c b/drivers/net/bnx2x_link.c
index ed648ac..2ee581a 100644
--- a/drivers/net/bnx2x_link.c
+++ b/drivers/net/bnx2x_link.c
@@ -4212,13 +4212,14 @@ static void bnx2x_turn_off_sf(struct bnx2x *bp, u8 port)
u8 bnx2x_get_ext_phy_fw_version(struct link_params *params, u8 driver_loaded,
u8 *version, u16 len)
{
- struct bnx2x *bp = params->bp;
+ struct bnx2x *bp;
u32 ext_phy_type = 0;
u32 spirom_ver = 0;
u8 status = 0 ;
if (version == NULL || params == NULL)
return -EINVAL;
+ bp = params->bp;
spirom_ver = REG_RD(bp, params->shmem_base +
offsetof(struct shmem_region,
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index d927f71..aa1be1f 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -1459,8 +1459,16 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
* ether type (eg ARPHRD_ETHER and ARPHRD_INFINIBAND) share the same bond
*/
if (bond->slave_cnt == 0) {
- if (slave_dev->type != ARPHRD_ETHER)
- bond_setup_by_slave(bond_dev, slave_dev);
+ if (bond_dev->type != slave_dev->type) {
+ dev_close(bond_dev);
+ pr_debug("%s: change device type from %d to %d\n",
+ bond_dev->name, bond_dev->type, slave_dev->type);
+ if (slave_dev->type != ARPHRD_ETHER)
+ bond_setup_by_slave(bond_dev, slave_dev);
+ else
+ ether_setup(bond_dev);
+ dev_open(bond_dev);
+ }
} else if (bond_dev->type != slave_dev->type) {
pr_err(DRV_NAME ": %s ether type (%d) is different "
"from other slaves (%d), can not enslave it.\n",
diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c
index 574dadd..9e4283a 100644
--- a/drivers/net/can/dev.c
+++ b/drivers/net/can/dev.c
@@ -346,7 +346,7 @@ void can_restart(unsigned long data)
skb = dev_alloc_skb(sizeof(struct can_frame));
if (skb == NULL) {
err = -ENOMEM;
- goto out;
+ goto restart;
}
skb->dev = dev;
skb->protocol = htons(ETH_P_CAN);
@@ -361,13 +361,13 @@ void can_restart(unsigned long data)
stats->rx_packets++;
stats->rx_bytes += cf->can_dlc;
+restart:
dev_dbg(dev->dev.parent, "restarted\n");
priv->can_stats.restarts++;
/* Now restart the device */
err = priv->do_set_mode(dev, CAN_MODE_START);
-out:
netif_carrier_on(dev);
if (err)
dev_err(dev->dev.parent, "Error %d during restart", err);
@@ -473,6 +473,10 @@ int open_candev(struct net_device *dev)
return -EINVAL;
}
+ /* Switch carrier on if device was stopped while in bus-off state */
+ if (!netif_carrier_ok(dev))
+ netif_carrier_on(dev);
+
setup_timer(&priv->restart_timer, can_restart, (unsigned long)dev);
return 0;
diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c
index 571f133..08ebee7 100644
--- a/drivers/net/can/sja1000/sja1000.c
+++ b/drivers/net/can/sja1000/sja1000.c
@@ -63,7 +63,6 @@
#include <linux/can.h>
#include <linux/can/dev.h>
#include <linux/can/error.h>
-#include <linux/can/dev.h>
#include "sja1000.h"
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index efa680f..41b648a 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -1897,6 +1897,9 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx,
if (ioread8(&nic->csr->scb.status) & rus_no_res)
nic->ru_running = RU_SUSPENDED;
+ pci_dma_sync_single_for_device(nic->pdev, rx->dma_addr,
+ sizeof(struct rfd),
+ PCI_DMA_BIDIRECTIONAL);
return -ENODATA;
}
diff --git a/drivers/net/hamradio/6pack.c b/drivers/net/hamradio/6pack.c
index 1551600..981ab53 100644
--- a/drivers/net/hamradio/6pack.c
+++ b/drivers/net/hamradio/6pack.c
@@ -3,7 +3,7 @@
* devices like TTY. It interfaces between a raw TTY and the
* kernel's AX.25 protocol layers.
*
- * Authors: Andreas Könsgen <ajk@iehk.rwth-aachen.de>
+ * Authors: Andreas Könsgen <ajk@comnets.uni-bremen.de>
* Ralf Baechle DL5RB <ralf@linux-mips.org>
*
* Quite a lot of stuff "stolen" by Joerg Reuter from slip.c, written by
diff --git a/drivers/net/ibm_newemac/rgmii.c b/drivers/net/ibm_newemac/rgmii.c
index 1d5379d..8d76cb8 100644
--- a/drivers/net/ibm_newemac/rgmii.c
+++ b/drivers/net/ibm_newemac/rgmii.c
@@ -188,11 +188,12 @@ void rgmii_put_mdio(struct of_device *ofdev, int input)
void rgmii_detach(struct of_device *ofdev, int input)
{
struct rgmii_instance *dev = dev_get_drvdata(&ofdev->dev);
- struct rgmii_regs __iomem *p = dev->base;
-
- mutex_lock(&dev->lock);
+ struct rgmii_regs __iomem *p;
BUG_ON(!dev || dev->users == 0);
+ p = dev->base;
+
+ mutex_lock(&dev->lock);
RGMII_DBG(dev, "detach(%d)" NL, input);
diff --git a/drivers/net/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ixgbe/ixgbe_dcb_nl.c
index 7c5978a..da2c851 100644
--- a/drivers/net/ixgbe/ixgbe_dcb_nl.c
+++ b/drivers/net/ixgbe/ixgbe_dcb_nl.c
@@ -106,8 +106,6 @@ static u8 ixgbe_dcbnl_get_state(struct net_device *netdev)
{
struct ixgbe_adapter *adapter = netdev_priv(netdev);
- DPRINTK(DRV, INFO, "Get DCB Admin Mode.\n");
-
return !!(adapter->flags & IXGBE_FLAG_DCB_ENABLED);
}
@@ -116,8 +114,6 @@ static u8 ixgbe_dcbnl_set_state(struct net_device *netdev, u8 state)
u8 err = 0;
struct ixgbe_adapter *adapter = netdev_priv(netdev);
- DPRINTK(DRV, INFO, "Set DCB Admin Mode.\n");
-
if (state > 0) {
/* Turn on DCB */
if (adapter->flags & IXGBE_FLAG_DCB_ENABLED)
@@ -175,6 +171,8 @@ static void ixgbe_dcbnl_get_perm_hw_addr(struct net_device *netdev,
struct ixgbe_adapter *adapter = netdev_priv(netdev);
int i, j;
+ memset(perm_addr, 0xff, MAX_ADDR_LEN);
+
for (i = 0; i < netdev->addr_len; i++)
perm_addr[i] = adapter->hw.mac.perm_addr[i];
diff --git a/drivers/net/jazzsonic.c b/drivers/net/jazzsonic.c
index d12106b..2f28609 100644
--- a/drivers/net/jazzsonic.c
+++ b/drivers/net/jazzsonic.c
@@ -229,6 +229,7 @@ static int __init jazz_sonic_probe(struct platform_device *pdev)
lp = netdev_priv(dev);
lp->device = &pdev->dev;
SET_NETDEV_DEV(dev, &pdev->dev);
+ platform_set_drvdata(pdev, dev);
netdev_boot_setup_check(dev);
diff --git a/drivers/net/ks8851.c b/drivers/net/ks8851.c
new file mode 100644
index 0000000..9a1dea6
--- /dev/null
+++ b/drivers/net/ks8851.c
@@ -0,0 +1,1322 @@
+/* drivers/net/ks8651.c
+ *
+ * Copyright 2009 Simtec Electronics
+ * http://www.simtec.co.uk/
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define DEBUG
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/cache.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+
+#include <linux/spi/spi.h>
+
+#include "ks8851.h"
+
+/**
+ * struct ks8851_rxctrl - KS8851 driver rx control
+ * @mchash: Multicast hash-table data.
+ * @rxcr1: KS_RXCR1 register setting
+ * @rxcr2: KS_RXCR2 register setting
+ *
+ * Representation of the settings needs to control the receive filtering
+ * such as the multicast hash-filter and the receive register settings. This
+ * is used to make the job of working out if the receive settings change and
+ * then issuing the new settings to the worker that will send the necessary
+ * commands.
+ */
+struct ks8851_rxctrl {
+ u16 mchash[4];
+ u16 rxcr1;
+ u16 rxcr2;
+};
+
+/**
+ * union ks8851_tx_hdr - tx header data
+ * @txb: The header as bytes
+ * @txw: The header as 16bit, little-endian words
+ *
+ * A dual representation of the tx header data to allow
+ * access to individual bytes, and to allow 16bit accesses
+ * with 16bit alignment.
+ */
+union ks8851_tx_hdr {
+ u8 txb[6];
+ __le16 txw[3];
+};
+
+/**
+ * struct ks8851_net - KS8851 driver private data
+ * @netdev: The network device we're bound to
+ * @spidev: The spi device we're bound to.
+ * @lock: Lock to ensure that the device is not accessed when busy.
+ * @statelock: Lock on this structure for tx list.
+ * @mii: The MII state information for the mii calls.
+ * @rxctrl: RX settings for @rxctrl_work.
+ * @tx_work: Work queue for tx packets
+ * @irq_work: Work queue for servicing interrupts
+ * @rxctrl_work: Work queue for updating RX mode and multicast lists
+ * @txq: Queue of packets for transmission.
+ * @spi_msg1: pre-setup SPI transfer with one message, @spi_xfer1.
+ * @spi_msg2: pre-setup SPI transfer with two messages, @spi_xfer2.
+ * @txh: Space for generating packet TX header in DMA-able data
+ * @rxd: Space for receiving SPI data, in DMA-able space.
+ * @txd: Space for transmitting SPI data, in DMA-able space.
+ * @msg_enable: The message flags controlling driver output (see ethtool).
+ * @fid: Incrementing frame id tag.
+ * @rc_ier: Cached copy of KS_IER.
+ * @rc_rxqcr: Cached copy of KS_RXQCR.
+ *
+ * The @lock ensures that the chip is protected when certain operations are
+ * in progress. When the read or write packet transfer is in progress, most
+ * of the chip registers are not ccessible until the transfer is finished and
+ * the DMA has been de-asserted.
+ *
+ * The @statelock is used to protect information in the structure which may
+ * need to be accessed via several sources, such as the network driver layer
+ * or one of the work queues.
+ *
+ * We align the buffers we may use for rx/tx to ensure that if the SPI driver
+ * wants to DMA map them, it will not have any problems with data the driver
+ * modifies.
+ */
+struct ks8851_net {
+ struct net_device *netdev;
+ struct spi_device *spidev;
+ struct mutex lock;
+ spinlock_t statelock;
+
+ union ks8851_tx_hdr txh ____cacheline_aligned;
+ u8 rxd[8];
+ u8 txd[8];
+
+ u32 msg_enable ____cacheline_aligned;
+ u16 tx_space;
+ u8 fid;
+
+ u16 rc_ier;
+ u16 rc_rxqcr;
+
+ struct mii_if_info mii;
+ struct ks8851_rxctrl rxctrl;
+
+ struct work_struct tx_work;
+ struct work_struct irq_work;
+ struct work_struct rxctrl_work;
+
+ struct sk_buff_head txq;
+
+ struct spi_message spi_msg1;
+ struct spi_message spi_msg2;
+ struct spi_transfer spi_xfer1;
+ struct spi_transfer spi_xfer2[2];
+};
+
+static int msg_enable;
+
+#define ks_info(_ks, _msg...) dev_info(&(_ks)->spidev->dev, _msg)
+#define ks_warn(_ks, _msg...) dev_warn(&(_ks)->spidev->dev, _msg)
+#define ks_dbg(_ks, _msg...) dev_dbg(&(_ks)->spidev->dev, _msg)
+#define ks_err(_ks, _msg...) dev_err(&(_ks)->spidev->dev, _msg)
+
+/* shift for byte-enable data */
+#define BYTE_EN(_x) ((_x) << 2)
+
+/* turn register number and byte-enable mask into data for start of packet */
+#define MK_OP(_byteen, _reg) (BYTE_EN(_byteen) | (_reg) << (8+2) | (_reg) >> 6)
+
+/* SPI register read/write calls.
+ *
+ * All these calls issue SPI transactions to access the chip's registers. They
+ * all require that the necessary lock is held to prevent accesses when the
+ * chip is busy transfering packet data (RX/TX FIFO accesses).
+ */
+
+/**
+ * ks8851_wrreg16 - write 16bit register value to chip
+ * @ks: The chip state
+ * @reg: The register address
+ * @val: The value to write
+ *
+ * Issue a write to put the value @val into the register specified in @reg.
+ */
+static void ks8851_wrreg16(struct ks8851_net *ks, unsigned reg, unsigned val)
+{
+ struct spi_transfer *xfer = &ks->spi_xfer1;
+ struct spi_message *msg = &ks->spi_msg1;
+ __le16 txb[2];
+ int ret;
+
+ txb[0] = cpu_to_le16(MK_OP(reg & 2 ? 0xC : 0x03, reg) | KS_SPIOP_WR);
+ txb[1] = cpu_to_le16(val);
+
+ xfer->tx_buf = txb;
+ xfer->rx_buf = NULL;
+ xfer->len = 4;
+
+ ret = spi_sync(ks->spidev, msg);
+ if (ret < 0)
+ ks_err(ks, "spi_sync() failed\n");
+}
+
+/**
+ * ks8851_rx_1msg - select whether to use one or two messages for spi read
+ * @ks: The device structure
+ *
+ * Return whether to generate a single message with a tx and rx buffer
+ * supplied to spi_sync(), or alternatively send the tx and rx buffers
+ * as separate messages.
+ *
+ * Depending on the hardware in use, a single message may be more efficient
+ * on interrupts or work done by the driver.
+ *
+ * This currently always returns true until we add some per-device data passed
+ * from the platform code to specify which mode is better.
+ */
+static inline bool ks8851_rx_1msg(struct ks8851_net *ks)
+{
+ return true;
+}
+
+/**
+ * ks8851_rdreg - issue read register command and return the data
+ * @ks: The device state
+ * @op: The register address and byte enables in message format.
+ * @rxb: The RX buffer to return the result into
+ * @rxl: The length of data expected.
+ *
+ * This is the low level read call that issues the necessary spi message(s)
+ * to read data from the register specified in @op.
+ */
+static void ks8851_rdreg(struct ks8851_net *ks, unsigned op,
+ u8 *rxb, unsigned rxl)
+{
+ struct spi_transfer *xfer;
+ struct spi_message *msg;
+ __le16 *txb = (__le16 *)ks->txd;
+ u8 *trx = ks->rxd;
+ int ret;
+
+ txb[0] = cpu_to_le16(op | KS_SPIOP_RD);
+
+ if (ks8851_rx_1msg(ks)) {
+ msg = &ks->spi_msg1;
+ xfer = &ks->spi_xfer1;
+
+ xfer->tx_buf = txb;
+ xfer->rx_buf = trx;
+ xfer->len = rxl + 2;
+ } else {
+ msg = &ks->spi_msg2;
+ xfer = ks->spi_xfer2;
+
+ xfer->tx_buf = txb;
+ xfer->rx_buf = NULL;
+ xfer->len = 2;
+
+ xfer++;
+ xfer->tx_buf = NULL;
+ xfer->rx_buf = trx;
+ xfer->len = rxl;
+ }
+
+ ret = spi_sync(ks->spidev, msg);
+ if (ret < 0)
+ ks_err(ks, "read: spi_sync() failed\n");
+ else if (ks8851_rx_1msg(ks))
+ memcpy(rxb, trx + 2, rxl);
+ else
+ memcpy(rxb, trx, rxl);
+}
+
+/**
+ * ks8851_rdreg8 - read 8 bit register from device
+ * @ks: The chip information
+ * @reg: The register address
+ *
+ * Read a 8bit register from the chip, returning the result
+*/
+static unsigned ks8851_rdreg8(struct ks8851_net *ks, unsigned reg)
+{
+ u8 rxb[1];
+
+ ks8851_rdreg(ks, MK_OP(1 << (reg & 3), reg), rxb, 1);
+ return rxb[0];
+}
+
+/**
+ * ks8851_rdreg16 - read 16 bit register from device
+ * @ks: The chip information
+ * @reg: The register address
+ *
+ * Read a 16bit register from the chip, returning the result
+*/
+static unsigned ks8851_rdreg16(struct ks8851_net *ks, unsigned reg)
+{
+ __le16 rx = 0;
+
+ ks8851_rdreg(ks, MK_OP(reg & 2 ? 0xC : 0x3, reg), (u8 *)&rx, 2);
+ return le16_to_cpu(rx);
+}
+
+/**
+ * ks8851_rdreg32 - read 32 bit register from device
+ * @ks: The chip information
+ * @reg: The register address
+ *
+ * Read a 32bit register from the chip.
+ *
+ * Note, this read requires the address be aligned to 4 bytes.
+*/
+static unsigned ks8851_rdreg32(struct ks8851_net *ks, unsigned reg)
+{
+ __le32 rx = 0;
+
+ WARN_ON(reg & 3);
+
+ ks8851_rdreg(ks, MK_OP(0xf, reg), (u8 *)&rx, 4);
+ return le32_to_cpu(rx);
+}
+
+/**
+ * ks8851_soft_reset - issue one of the soft reset to the device
+ * @ks: The device state.
+ * @op: The bit(s) to set in the GRR
+ *
+ * Issue the relevant soft-reset command to the device's GRR register
+ * specified by @op.
+ *
+ * Note, the delays are in there as a caution to ensure that the reset
+ * has time to take effect and then complete. Since the datasheet does
+ * not currently specify the exact sequence, we have chosen something
+ * that seems to work with our device.
+ */
+static void ks8851_soft_reset(struct ks8851_net *ks, unsigned op)
+{
+ ks8851_wrreg16(ks, KS_GRR, op);
+ mdelay(1); /* wait a short time to effect reset */
+ ks8851_wrreg16(ks, KS_GRR, 0);
+ mdelay(1); /* wait for condition to clear */
+}
+
+/**
+ * ks8851_write_mac_addr - write mac address to device registers
+ * @dev: The network device
+ *
+ * Update the KS8851 MAC address registers from the address in @dev.
+ *
+ * This call assumes that the chip is not running, so there is no need to
+ * shutdown the RXQ process whilst setting this.
+*/
+static int ks8851_write_mac_addr(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ u16 *mcp = (u16 *)dev->dev_addr;
+
+ mutex_lock(&ks->lock);
+
+ ks8851_wrreg16(ks, KS_MARL, mcp[0]);
+ ks8851_wrreg16(ks, KS_MARM, mcp[1]);
+ ks8851_wrreg16(ks, KS_MARH, mcp[2]);
+
+ mutex_unlock(&ks->lock);
+
+ return 0;
+}
+
+/**
+ * ks8851_init_mac - initialise the mac address
+ * @ks: The device structure
+ *
+ * Get or create the initial mac address for the device and then set that
+ * into the station address register. Currently we assume that the device
+ * does not have a valid mac address in it, and so we use random_ether_addr()
+ * to create a new one.
+ *
+ * In future, the driver should check to see if the device has an EEPROM
+ * attached and whether that has a valid ethernet address in it.
+ */
+static void ks8851_init_mac(struct ks8851_net *ks)
+{
+ struct net_device *dev = ks->netdev;
+
+ random_ether_addr(dev->dev_addr);
+ ks8851_write_mac_addr(dev);
+}
+
+/**
+ * ks8851_irq - device interrupt handler
+ * @irq: Interrupt number passed from the IRQ hnalder.
+ * @pw: The private word passed to register_irq(), our struct ks8851_net.
+ *
+ * Disable the interrupt from happening again until we've processed the
+ * current status by scheduling ks8851_irq_work().
+ */
+static irqreturn_t ks8851_irq(int irq, void *pw)
+{
+ struct ks8851_net *ks = pw;
+
+ disable_irq_nosync(irq);
+ schedule_work(&ks->irq_work);
+ return IRQ_HANDLED;
+}
+
+/**
+ * ks8851_rdfifo - read data from the receive fifo
+ * @ks: The device state.
+ * @buff: The buffer address
+ * @len: The length of the data to read
+ *
+ * Issue an RXQ FIFO read command and read the @len ammount of data from
+ * the FIFO into the buffer specified by @buff.
+ */
+static void ks8851_rdfifo(struct ks8851_net *ks, u8 *buff, unsigned len)
+{
+ struct spi_transfer *xfer = ks->spi_xfer2;
+ struct spi_message *msg = &ks->spi_msg2;
+ u8 txb[1];
+ int ret;
+
+ if (netif_msg_rx_status(ks))
+ ks_dbg(ks, "%s: %d@%p\n", __func__, len, buff);
+
+ /* set the operation we're issuing */
+ txb[0] = KS_SPIOP_RXFIFO;
+
+ xfer->tx_buf = txb;
+ xfer->rx_buf = NULL;
+ xfer->len = 1;
+
+ xfer++;
+ xfer->rx_buf = buff;
+ xfer->tx_buf = NULL;
+ xfer->len = len;
+
+ ret = spi_sync(ks->spidev, msg);
+ if (ret < 0)
+ ks_err(ks, "%s: spi_sync() failed\n", __func__);
+}
+
+/**
+ * ks8851_dbg_dumpkkt - dump initial packet contents to debug
+ * @ks: The device state
+ * @rxpkt: The data for the received packet
+ *
+ * Dump the initial data from the packet to dev_dbg().
+*/
+static void ks8851_dbg_dumpkkt(struct ks8851_net *ks, u8 *rxpkt)
+{
+ ks_dbg(ks, "pkt %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
+ rxpkt[4], rxpkt[5], rxpkt[6], rxpkt[7],
+ rxpkt[8], rxpkt[9], rxpkt[10], rxpkt[11],
+ rxpkt[12], rxpkt[13], rxpkt[14], rxpkt[15]);
+}
+
+/**
+ * ks8851_rx_pkts - receive packets from the host
+ * @ks: The device information.
+ *
+ * This is called from the IRQ work queue when the system detects that there
+ * are packets in the receive queue. Find out how many packets there are and
+ * read them from the FIFO.
+ */
+static void ks8851_rx_pkts(struct ks8851_net *ks)
+{
+ struct sk_buff *skb;
+ unsigned rxfc;
+ unsigned rxlen;
+ unsigned rxstat;
+ u32 rxh;
+ u8 *rxpkt;
+
+ rxfc = ks8851_rdreg8(ks, KS_RXFC);
+
+ if (netif_msg_rx_status(ks))
+ ks_dbg(ks, "%s: %d packets\n", __func__, rxfc);
+
+ /* Currently we're issuing a read per packet, but we could possibly
+ * improve the code by issuing a single read, getting the receive
+ * header, allocating the packet and then reading the packet data
+ * out in one go.
+ *
+ * This form of operation would require us to hold the SPI bus'
+ * chipselect low during the entie transaction to avoid any
+ * reset to the data stream comming from the chip.
+ */
+
+ for (; rxfc != 0; rxfc--) {
+ rxh = ks8851_rdreg32(ks, KS_RXFHSR);
+ rxstat = rxh & 0xffff;
+ rxlen = rxh >> 16;
+
+ if (netif_msg_rx_status(ks))
+ ks_dbg(ks, "rx: stat 0x%04x, len 0x%04x\n",
+ rxstat, rxlen);
+
+ /* the length of the packet includes the 32bit CRC */
+
+ /* set dma read address */
+ ks8851_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI | 0x00);
+
+ /* start the packet dma process, and set auto-dequeue rx */
+ ks8851_wrreg16(ks, KS_RXQCR,
+ ks->rc_rxqcr | RXQCR_SDA | RXQCR_ADRFE);
+
+ if (rxlen > 0) {
+ skb = netdev_alloc_skb(ks->netdev, rxlen + 2 + 8);
+ if (!skb) {
+ /* todo - dump frame and move on */
+ }
+
+ /* two bytes to ensure ip is aligned, and four bytes
+ * for the status header and 4 bytes of garbage */
+ skb_reserve(skb, 2 + 4 + 4);
+
+ rxpkt = skb_put(skb, rxlen - 4) - 8;
+
+ /* align the packet length to 4 bytes, and add 4 bytes
+ * as we're getting the rx status header as well */
+ ks8851_rdfifo(ks, rxpkt, ALIGN(rxlen, 4) + 8);
+
+ if (netif_msg_pktdata(ks))
+ ks8851_dbg_dumpkkt(ks, rxpkt);
+
+ skb->protocol = eth_type_trans(skb, ks->netdev);
+ netif_rx(skb);
+
+ ks->netdev->stats.rx_packets++;
+ ks->netdev->stats.rx_bytes += rxlen - 4;
+ }
+
+ ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
+ }
+}
+
+/**
+ * ks8851_irq_work - work queue handler for dealing with interrupt requests
+ * @work: The work structure that was scheduled by schedule_work()
+ *
+ * This is the handler invoked when the ks8851_irq() is called to find out
+ * what happened, as we cannot allow ourselves to sleep whilst waiting for
+ * anything other process has the chip's lock.
+ *
+ * Read the interrupt status, work out what needs to be done and then clear
+ * any of the interrupts that are not needed.
+ */
+static void ks8851_irq_work(struct work_struct *work)
+{
+ struct ks8851_net *ks = container_of(work, struct ks8851_net, irq_work);
+ unsigned status;
+ unsigned handled = 0;
+
+ mutex_lock(&ks->lock);
+
+ status = ks8851_rdreg16(ks, KS_ISR);
+
+ if (netif_msg_intr(ks))
+ dev_dbg(&ks->spidev->dev, "%s: status 0x%04x\n",
+ __func__, status);
+
+ if (status & IRQ_LCI) {
+ /* should do something about checking link status */
+ handled |= IRQ_LCI;
+ }
+
+ if (status & IRQ_LDI) {
+ u16 pmecr = ks8851_rdreg16(ks, KS_PMECR);
+ pmecr &= ~PMECR_WKEVT_MASK;
+ ks8851_wrreg16(ks, KS_PMECR, pmecr | PMECR_WKEVT_LINK);
+
+ handled |= IRQ_LDI;
+ }
+
+ if (status & IRQ_RXPSI)
+ handled |= IRQ_RXPSI;
+
+ if (status & IRQ_TXI) {
+ handled |= IRQ_TXI;
+
+ /* no lock here, tx queue should have been stopped */
+
+ /* update our idea of how much tx space is available to the
+ * system */
+ ks->tx_space = ks8851_rdreg16(ks, KS_TXMIR);
+
+ if (netif_msg_intr(ks))
+ ks_dbg(ks, "%s: txspace %d\n", __func__, ks->tx_space);
+ }
+
+ if (status & IRQ_RXI)
+ handled |= IRQ_RXI;
+
+ if (status & IRQ_SPIBEI) {
+ dev_err(&ks->spidev->dev, "%s: spi bus error\n", __func__);
+ handled |= IRQ_SPIBEI;
+ }
+
+ ks8851_wrreg16(ks, KS_ISR, handled);
+
+ if (status & IRQ_RXI) {
+ /* the datasheet says to disable the rx interrupt during
+ * packet read-out, however we're masking the interrupt
+ * from the device so do not bother masking just the RX
+ * from the device. */
+
+ ks8851_rx_pkts(ks);
+ }
+
+ /* if something stopped the rx process, probably due to wanting
+ * to change the rx settings, then do something about restarting
+ * it. */
+ if (status & IRQ_RXPSI) {
+ struct ks8851_rxctrl *rxc = &ks->rxctrl;
+
+ /* update the multicast hash table */
+ ks8851_wrreg16(ks, KS_MAHTR0, rxc->mchash[0]);
+ ks8851_wrreg16(ks, KS_MAHTR1, rxc->mchash[1]);
+ ks8851_wrreg16(ks, KS_MAHTR2, rxc->mchash[2]);
+ ks8851_wrreg16(ks, KS_MAHTR3, rxc->mchash[3]);
+
+ ks8851_wrreg16(ks, KS_RXCR2, rxc->rxcr2);
+ ks8851_wrreg16(ks, KS_RXCR1, rxc->rxcr1);
+ }
+
+ mutex_unlock(&ks->lock);
+
+ if (status & IRQ_TXI)
+ netif_wake_queue(ks->netdev);
+
+ enable_irq(ks->netdev->irq);
+}
+
+/**
+ * calc_txlen - calculate size of message to send packet
+ * @len: Lenght of data
+ *
+ * Returns the size of the TXFIFO message needed to send
+ * this packet.
+ */
+static inline unsigned calc_txlen(unsigned len)
+{
+ return ALIGN(len + 4, 4);
+}
+
+/**
+ * ks8851_wrpkt - write packet to TX FIFO
+ * @ks: The device state.
+ * @txp: The sk_buff to transmit.
+ * @irq: IRQ on completion of the packet.
+ *
+ * Send the @txp to the chip. This means creating the relevant packet header
+ * specifying the length of the packet and the other information the chip
+ * needs, such as IRQ on completion. Send the header and the packet data to
+ * the device.
+ */
+static void ks8851_wrpkt(struct ks8851_net *ks, struct sk_buff *txp, bool irq)
+{
+ struct spi_transfer *xfer = ks->spi_xfer2;
+ struct spi_message *msg = &ks->spi_msg2;
+ unsigned fid = 0;
+ int ret;
+
+ if (netif_msg_tx_queued(ks))
+ dev_dbg(&ks->spidev->dev, "%s: skb %p, %d@%p, irq %d\n",
+ __func__, txp, txp->len, txp->data, irq);
+
+ fid = ks->fid++;
+ fid &= TXFR_TXFID_MASK;
+
+ if (irq)
+ fid |= TXFR_TXIC; /* irq on completion */
+
+ /* start header at txb[1] to align txw entries */
+ ks->txh.txb[1] = KS_SPIOP_TXFIFO;
+ ks->txh.txw[1] = cpu_to_le16(fid);
+ ks->txh.txw[2] = cpu_to_le16(txp->len);
+
+ xfer->tx_buf = &ks->txh.txb[1];
+ xfer->rx_buf = NULL;
+ xfer->len = 5;
+
+ xfer++;
+ xfer->tx_buf = txp->data;
+ xfer->rx_buf = NULL;
+ xfer->len = ALIGN(txp->len, 4);
+
+ ret = spi_sync(ks->spidev, msg);
+ if (ret < 0)
+ ks_err(ks, "%s: spi_sync() failed\n", __func__);
+}
+
+/**
+ * ks8851_done_tx - update and then free skbuff after transmitting
+ * @ks: The device state
+ * @txb: The buffer transmitted
+ */
+static void ks8851_done_tx(struct ks8851_net *ks, struct sk_buff *txb)
+{
+ struct net_device *dev = ks->netdev;
+
+ dev->stats.tx_bytes += txb->len;
+ dev->stats.tx_packets++;
+
+ dev_kfree_skb(txb);
+}
+
+/**
+ * ks8851_tx_work - process tx packet(s)
+ * @work: The work strucutre what was scheduled.
+ *
+ * This is called when a number of packets have been scheduled for
+ * transmission and need to be sent to the device.
+ */
+static void ks8851_tx_work(struct work_struct *work)
+{
+ struct ks8851_net *ks = container_of(work, struct ks8851_net, tx_work);
+ struct sk_buff *txb;
+ bool last = false;
+
+ mutex_lock(&ks->lock);
+
+ while (!last) {
+ txb = skb_dequeue(&ks->txq);
+ last = skb_queue_empty(&ks->txq);
+
+ ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_SDA);
+ ks8851_wrpkt(ks, txb, last);
+ ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
+ ks8851_wrreg16(ks, KS_TXQCR, TXQCR_METFE);
+
+ ks8851_done_tx(ks, txb);
+ }
+
+ mutex_unlock(&ks->lock);
+}
+
+/**
+ * ks8851_set_powermode - set power mode of the device
+ * @ks: The device state
+ * @pwrmode: The power mode value to write to KS_PMECR.
+ *
+ * Change the power mode of the chip.
+ */
+static void ks8851_set_powermode(struct ks8851_net *ks, unsigned pwrmode)
+{
+ unsigned pmecr;
+
+ if (netif_msg_hw(ks))
+ ks_dbg(ks, "setting power mode %d\n", pwrmode);
+
+ pmecr = ks8851_rdreg16(ks, KS_PMECR);
+ pmecr &= ~PMECR_PM_MASK;
+ pmecr |= pwrmode;
+
+ ks8851_wrreg16(ks, KS_PMECR, pmecr);
+}
+
+/**
+ * ks8851_net_open - open network device
+ * @dev: The network device being opened.
+ *
+ * Called when the network device is marked active, such as a user executing
+ * 'ifconfig up' on the device.
+ */
+static int ks8851_net_open(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+
+ /* lock the card, even if we may not actually be doing anything
+ * else at the moment */
+ mutex_lock(&ks->lock);
+
+ if (netif_msg_ifup(ks))
+ ks_dbg(ks, "opening %s\n", dev->name);
+
+ /* bring chip out of any power saving mode it was in */
+ ks8851_set_powermode(ks, PMECR_PM_NORMAL);
+
+ /* issue a soft reset to the RX/TX QMU to put it into a known
+ * state. */
+ ks8851_soft_reset(ks, GRR_QMU);
+
+ /* setup transmission parameters */
+
+ ks8851_wrreg16(ks, KS_TXCR, (TXCR_TXE | /* enable transmit process */
+ TXCR_TXPE | /* pad to min length */
+ TXCR_TXCRC | /* add CRC */
+ TXCR_TXFCE)); /* enable flow control */
+
+ /* auto-increment tx data, reset tx pointer */
+ ks8851_wrreg16(ks, KS_TXFDPR, TXFDPR_TXFPAI);
+
+ /* setup receiver control */
+
+ ks8851_wrreg16(ks, KS_RXCR1, (RXCR1_RXPAFMA | /* from mac filter */
+ RXCR1_RXFCE | /* enable flow control */
+ RXCR1_RXBE | /* broadcast enable */
+ RXCR1_RXUE | /* unicast enable */
+ RXCR1_RXE)); /* enable rx block */
+
+ /* transfer entire frames out in one go */
+ ks8851_wrreg16(ks, KS_RXCR2, RXCR2_SRDBL_FRAME);
+
+ /* set receive counter timeouts */
+ ks8851_wrreg16(ks, KS_RXDTTR, 1000); /* 1ms after first frame to IRQ */
+ ks8851_wrreg16(ks, KS_RXDBCTR, 4096); /* >4Kbytes in buffer to IRQ */
+ ks8851_wrreg16(ks, KS_RXFCTR, 10); /* 10 frames to IRQ */
+
+ ks->rc_rxqcr = (RXQCR_RXFCTE | /* IRQ on frame count exceeded */
+ RXQCR_RXDBCTE | /* IRQ on byte count exceeded */
+ RXQCR_RXDTTE); /* IRQ on time exceeded */
+
+ ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
+
+ /* clear then enable interrupts */
+
+#define STD_IRQ (IRQ_LCI | /* Link Change */ \
+ IRQ_TXI | /* TX done */ \
+ IRQ_RXI | /* RX done */ \
+ IRQ_SPIBEI | /* SPI bus error */ \
+ IRQ_TXPSI | /* TX process stop */ \
+ IRQ_RXPSI) /* RX process stop */
+
+ ks->rc_ier = STD_IRQ;
+ ks8851_wrreg16(ks, KS_ISR, STD_IRQ);
+ ks8851_wrreg16(ks, KS_IER, STD_IRQ);
+
+ netif_start_queue(ks->netdev);
+
+ if (netif_msg_ifup(ks))
+ ks_dbg(ks, "network device %s up\n", dev->name);
+
+ mutex_unlock(&ks->lock);
+ return 0;
+}
+
+/**
+ * ks8851_net_stop - close network device
+ * @dev: The device being closed.
+ *
+ * Called to close down a network device which has been active. Cancell any
+ * work, shutdown the RX and TX process and then place the chip into a low
+ * power state whilst it is not being used.
+ */
+static int ks8851_net_stop(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+
+ if (netif_msg_ifdown(ks))
+ ks_info(ks, "%s: shutting down\n", dev->name);
+
+ netif_stop_queue(dev);
+
+ mutex_lock(&ks->lock);
+
+ /* stop any outstanding work */
+ flush_work(&ks->irq_work);
+ flush_work(&ks->tx_work);
+ flush_work(&ks->rxctrl_work);
+
+ /* turn off the IRQs and ack any outstanding */
+ ks8851_wrreg16(ks, KS_IER, 0x0000);
+ ks8851_wrreg16(ks, KS_ISR, 0xffff);
+
+ /* shutdown RX process */
+ ks8851_wrreg16(ks, KS_RXCR1, 0x0000);
+
+ /* shutdown TX process */
+ ks8851_wrreg16(ks, KS_TXCR, 0x0000);
+
+ /* set powermode to soft power down to save power */
+ ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN);
+
+ /* ensure any queued tx buffers are dumped */
+ while (!skb_queue_empty(&ks->txq)) {
+ struct sk_buff *txb = skb_dequeue(&ks->txq);
+
+ if (netif_msg_ifdown(ks))
+ ks_dbg(ks, "%s: freeing txb %p\n", __func__, txb);
+
+ dev_kfree_skb(txb);
+ }
+
+ mutex_unlock(&ks->lock);
+ return 0;
+}
+
+/**
+ * ks8851_start_xmit - transmit packet
+ * @skb: The buffer to transmit
+ * @dev: The device used to transmit the packet.
+ *
+ * Called by the network layer to transmit the @skb. Queue the packet for
+ * the device and schedule the necessary work to transmit the packet when
+ * it is free.
+ *
+ * We do this to firstly avoid sleeping with the network device locked,
+ * and secondly so we can round up more than one packet to transmit which
+ * means we can try and avoid generating too many transmit done interrupts.
+ */
+static int ks8851_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ unsigned needed = calc_txlen(skb->len);
+ int ret = NETDEV_TX_OK;
+
+ if (netif_msg_tx_queued(ks))
+ ks_dbg(ks, "%s: skb %p, %d@%p\n", __func__,
+ skb, skb->len, skb->data);
+
+ spin_lock(&ks->statelock);
+
+ if (needed > ks->tx_space) {
+ netif_stop_queue(dev);
+ ret = NETDEV_TX_BUSY;
+ } else {
+ ks->tx_space -= needed;
+ skb_queue_tail(&ks->txq, skb);
+ }
+
+ spin_unlock(&ks->statelock);
+ schedule_work(&ks->tx_work);
+
+ return ret;
+}
+
+/**
+ * ks8851_rxctrl_work - work handler to change rx mode
+ * @work: The work structure this belongs to.
+ *
+ * Lock the device and issue the necessary changes to the receive mode from
+ * the network device layer. This is done so that we can do this without
+ * having to sleep whilst holding the network device lock.
+ *
+ * Since the recommendation from Micrel is that the RXQ is shutdown whilst the
+ * receive parameters are programmed, we issue a write to disable the RXQ and
+ * then wait for the interrupt handler to be triggered once the RXQ shutdown is
+ * complete. The interrupt handler then writes the new values into the chip.
+ */
+static void ks8851_rxctrl_work(struct work_struct *work)
+{
+ struct ks8851_net *ks = container_of(work, struct ks8851_net, rxctrl_work);
+
+ mutex_lock(&ks->lock);
+
+ /* need to shutdown RXQ before modifying filter parameters */
+ ks8851_wrreg16(ks, KS_RXCR1, 0x00);
+
+ mutex_unlock(&ks->lock);
+}
+
+static void ks8851_set_rx_mode(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ struct ks8851_rxctrl rxctrl;
+
+ memset(&rxctrl, 0, sizeof(rxctrl));
+
+ if (dev->flags & IFF_PROMISC) {
+ /* interface to receive everything */
+
+ rxctrl.rxcr1 = RXCR1_RXAE | RXCR1_RXINVF;
+ } else if (dev->flags & IFF_ALLMULTI) {
+ /* accept all multicast packets */
+
+ rxctrl.rxcr1 = (RXCR1_RXME | RXCR1_RXAE |
+ RXCR1_RXPAFMA | RXCR1_RXMAFMA);
+ } else if (dev->flags & IFF_MULTICAST && dev->mc_count > 0) {
+ struct dev_mc_list *mcptr = dev->mc_list;
+ u32 crc;
+ int i;
+
+ /* accept some multicast */
+
+ for (i = dev->mc_count; i > 0; i--) {
+ crc = ether_crc(ETH_ALEN, mcptr->dmi_addr);
+ crc >>= (32 - 6); /* get top six bits */
+
+ rxctrl.mchash[crc >> 4] |= (1 << (crc & 0xf));
+ mcptr = mcptr->next;
+ }
+
+ rxctrl.rxcr1 = RXCR1_RXME | RXCR1_RXAE | RXCR1_RXPAFMA;
+ } else {
+ /* just accept broadcast / unicast */
+ rxctrl.rxcr1 = RXCR1_RXPAFMA;
+ }
+
+ rxctrl.rxcr1 |= (RXCR1_RXUE | /* unicast enable */
+ RXCR1_RXBE | /* broadcast enable */
+ RXCR1_RXE | /* RX process enable */
+ RXCR1_RXFCE); /* enable flow control */
+
+ rxctrl.rxcr2 |= RXCR2_SRDBL_FRAME;
+
+ /* schedule work to do the actual set of the data if needed */
+
+ spin_lock(&ks->statelock);
+
+ if (memcmp(&rxctrl, &ks->rxctrl, sizeof(rxctrl)) != 0) {
+ memcpy(&ks->rxctrl, &rxctrl, sizeof(ks->rxctrl));
+ schedule_work(&ks->rxctrl_work);
+ }
+
+ spin_unlock(&ks->statelock);
+}
+
+static int ks8851_set_mac_address(struct net_device *dev, void *addr)
+{
+ struct sockaddr *sa = addr;
+
+ if (netif_running(dev))
+ return -EBUSY;
+
+ if (!is_valid_ether_addr(sa->sa_data))
+ return -EADDRNOTAVAIL;
+
+ memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
+ return ks8851_write_mac_addr(dev);
+}
+
+static int ks8851_net_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+
+ if (!netif_running(dev))
+ return -EINVAL;
+
+ return generic_mii_ioctl(&ks->mii, if_mii(req), cmd, NULL);
+}
+
+static const struct net_device_ops ks8851_netdev_ops = {
+ .ndo_open = ks8851_net_open,
+ .ndo_stop = ks8851_net_stop,
+ .ndo_do_ioctl = ks8851_net_ioctl,
+ .ndo_start_xmit = ks8851_start_xmit,
+ .ndo_set_mac_address = ks8851_set_mac_address,
+ .ndo_set_rx_mode = ks8851_set_rx_mode,
+ .ndo_change_mtu = eth_change_mtu,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+/* ethtool support */
+
+static void ks8851_get_drvinfo(struct net_device *dev,
+ struct ethtool_drvinfo *di)
+{
+ strlcpy(di->driver, "KS8851", sizeof(di->driver));
+ strlcpy(di->version, "1.00", sizeof(di->version));
+ strlcpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info));
+}
+
+static u32 ks8851_get_msglevel(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ return ks->msg_enable;
+}
+
+static void ks8851_set_msglevel(struct net_device *dev, u32 to)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ ks->msg_enable = to;
+}
+
+static int ks8851_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ return mii_ethtool_gset(&ks->mii, cmd);
+}
+
+static int ks8851_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ return mii_ethtool_sset(&ks->mii, cmd);
+}
+
+static u32 ks8851_get_link(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ return mii_link_ok(&ks->mii);
+}
+
+static int ks8851_nway_reset(struct net_device *dev)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ return mii_nway_restart(&ks->mii);
+}
+
+static const struct ethtool_ops ks8851_ethtool_ops = {
+ .get_drvinfo = ks8851_get_drvinfo,
+ .get_msglevel = ks8851_get_msglevel,
+ .set_msglevel = ks8851_set_msglevel,
+ .get_settings = ks8851_get_settings,
+ .set_settings = ks8851_set_settings,
+ .get_link = ks8851_get_link,
+ .nway_reset = ks8851_nway_reset,
+};
+
+/* MII interface controls */
+
+/**
+ * ks8851_phy_reg - convert MII register into a KS8851 register
+ * @reg: MII register number.
+ *
+ * Return the KS8851 register number for the corresponding MII PHY register
+ * if possible. Return zero if the MII register has no direct mapping to the
+ * KS8851 register set.
+ */
+static int ks8851_phy_reg(int reg)
+{
+ switch (reg) {
+ case MII_BMCR:
+ return KS_P1MBCR;
+ case MII_BMSR:
+ return KS_P1MBSR;
+ case MII_PHYSID1:
+ return KS_PHY1ILR;
+ case MII_PHYSID2:
+ return KS_PHY1IHR;
+ case MII_ADVERTISE:
+ return KS_P1ANAR;
+ case MII_LPA:
+ return KS_P1ANLPR;
+ }
+
+ return 0x0;
+}
+
+/**
+ * ks8851_phy_read - MII interface PHY register read.
+ * @dev: The network device the PHY is on.
+ * @phy_addr: Address of PHY (ignored as we only have one)
+ * @reg: The register to read.
+ *
+ * This call reads data from the PHY register specified in @reg. Since the
+ * device does not support all the MII registers, the non-existant values
+ * are always returned as zero.
+ *
+ * We return zero for unsupported registers as the MII code does not check
+ * the value returned for any error status, and simply returns it to the
+ * caller. The mii-tool that the driver was tested with takes any -ve error
+ * as real PHY capabilities, thus displaying incorrect data to the user.
+ */
+static int ks8851_phy_read(struct net_device *dev, int phy_addr, int reg)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ int ksreg;
+ int result;
+
+ ksreg = ks8851_phy_reg(reg);
+ if (!ksreg)
+ return 0x0; /* no error return allowed, so use zero */
+
+ mutex_lock(&ks->lock);
+ result = ks8851_rdreg16(ks, ksreg);
+ mutex_unlock(&ks->lock);
+
+ return result;
+}
+
+static void ks8851_phy_write(struct net_device *dev,
+ int phy, int reg, int value)
+{
+ struct ks8851_net *ks = netdev_priv(dev);
+ int ksreg;
+
+ ksreg = ks8851_phy_reg(reg);
+ if (ksreg) {
+ mutex_lock(&ks->lock);
+ ks8851_wrreg16(ks, ksreg, value);
+ mutex_unlock(&ks->lock);
+ }
+}
+
+/**
+ * ks8851_read_selftest - read the selftest memory info.
+ * @ks: The device state
+ *
+ * Read and check the TX/RX memory selftest information.
+ */
+static int ks8851_read_selftest(struct ks8851_net *ks)
+{
+ unsigned both_done = MBIR_TXMBF | MBIR_RXMBF;
+ int ret = 0;
+ unsigned rd;
+
+ rd = ks8851_rdreg16(ks, KS_MBIR);
+
+ if ((rd & both_done) != both_done) {
+ ks_warn(ks, "Memory selftest not finished\n");
+ return 0;
+ }
+
+ if (rd & MBIR_TXMBFA) {
+ ks_err(ks, "TX memory selftest fail\n");
+ ret |= 1;
+ }
+
+ if (rd & MBIR_RXMBFA) {
+ ks_err(ks, "RX memory selftest fail\n");
+ ret |= 2;
+ }
+
+ return 0;
+}
+
+/* driver bus management functions */
+
+static int __devinit ks8851_probe(struct spi_device *spi)
+{
+ struct net_device *ndev;
+ struct ks8851_net *ks;
+ int ret;
+
+ ndev = alloc_etherdev(sizeof(struct ks8851_net));
+ if (!ndev) {
+ dev_err(&spi->dev, "failed to alloc ethernet device\n");
+ return -ENOMEM;
+ }
+
+ spi->bits_per_word = 8;
+
+ ks = netdev_priv(ndev);
+
+ ks->netdev = ndev;
+ ks->spidev = spi;
+ ks->tx_space = 6144;
+
+ mutex_init(&ks->lock);
+ spin_lock_init(&ks->statelock);
+
+ INIT_WORK(&ks->tx_work, ks8851_tx_work);
+ INIT_WORK(&ks->irq_work, ks8851_irq_work);
+ INIT_WORK(&ks->rxctrl_work, ks8851_rxctrl_work);
+
+ /* initialise pre-made spi transfer messages */
+
+ spi_message_init(&ks->spi_msg1);
+ spi_message_add_tail(&ks->spi_xfer1, &ks->spi_msg1);
+
+ spi_message_init(&ks->spi_msg2);
+ spi_message_add_tail(&ks->spi_xfer2[0], &ks->spi_msg2);
+ spi_message_add_tail(&ks->spi_xfer2[1], &ks->spi_msg2);
+
+ /* setup mii state */
+ ks->mii.dev = ndev;
+ ks->mii.phy_id = 1,
+ ks->mii.phy_id_mask = 1;
+ ks->mii.reg_num_mask = 0xf;
+ ks->mii.mdio_read = ks8851_phy_read;
+ ks->mii.mdio_write = ks8851_phy_write;
+
+ dev_info(&spi->dev, "message enable is %d\n", msg_enable);
+
+ /* set the default message enable */
+ ks->msg_enable = netif_msg_init(msg_enable, (NETIF_MSG_DRV |
+ NETIF_MSG_PROBE |
+ NETIF_MSG_LINK));
+
+ skb_queue_head_init(&ks->txq);
+
+ SET_ETHTOOL_OPS(ndev, &ks8851_ethtool_ops);
+ SET_NETDEV_DEV(ndev, &spi->dev);
+
+ dev_set_drvdata(&spi->dev, ks);
+
+ ndev->if_port = IF_PORT_100BASET;
+ ndev->netdev_ops = &ks8851_netdev_ops;
+ ndev->irq = spi->irq;
+
+ /* simple check for a valid chip being connected to the bus */
+
+ if ((ks8851_rdreg16(ks, KS_CIDER) & ~CIDER_REV_MASK) != CIDER_ID) {
+ dev_err(&spi->dev, "failed to read device ID\n");
+ ret = -ENODEV;
+ goto err_id;
+ }
+
+ ks8851_read_selftest(ks);
+ ks8851_init_mac(ks);
+
+ ret = request_irq(spi->irq, ks8851_irq, IRQF_TRIGGER_LOW,
+ ndev->name, ks);
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to get irq\n");
+ goto err_irq;
+ }
+
+ ret = register_netdev(ndev);
+ if (ret) {
+ dev_err(&spi->dev, "failed to register network device\n");
+ goto err_netdev;
+ }
+
+ dev_info(&spi->dev, "revision %d, MAC %pM, IRQ %d\n",
+ CIDER_REV_GET(ks8851_rdreg16(ks, KS_CIDER)),
+ ndev->dev_addr, ndev->irq);
+
+ return 0;
+
+
+err_netdev:
+ free_irq(ndev->irq, ndev);
+
+err_id:
+err_irq:
+ free_netdev(ndev);
+ return ret;
+}
+
+static int __devexit ks8851_remove(struct spi_device *spi)
+{
+ struct ks8851_net *priv = dev_get_drvdata(&spi->dev);
+
+ if (netif_msg_drv(priv))
+ dev_info(&spi->dev, "remove");
+
+ unregister_netdev(priv->netdev);
+ free_irq(spi->irq, priv);
+ free_netdev(priv->netdev);
+
+ return 0;
+}
+
+static struct spi_driver ks8851_driver = {
+ .driver = {
+ .name = "ks8851",
+ .owner = THIS_MODULE,
+ },
+ .probe = ks8851_probe,
+ .remove = __devexit_p(ks8851_remove),
+};
+
+static int __init ks8851_init(void)
+{
+ return spi_register_driver(&ks8851_driver);
+}
+
+static void __exit ks8851_exit(void)
+{
+ spi_unregister_driver(&ks8851_driver);
+}
+
+module_init(ks8851_init);
+module_exit(ks8851_exit);
+
+MODULE_DESCRIPTION("KS8851 Network driver");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
+
+module_param_named(message, msg_enable, int, 0);
+MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)");
diff --git a/drivers/net/ks8851.h b/drivers/net/ks8851.h
new file mode 100644
index 0000000..85abe14
--- /dev/null
+++ b/drivers/net/ks8851.h
@@ -0,0 +1,296 @@
+/* drivers/net/ks8851.h
+ *
+ * Copyright 2009 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * KS8851 register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define KS_CCR 0x08
+#define CCR_EEPROM (1 << 9)
+#define CCR_SPI (1 << 8)
+#define CCR_32PIN (1 << 0)
+
+/* MAC address registers */
+#define KS_MARL 0x10
+#define KS_MARM 0x12
+#define KS_MARH 0x14
+
+#define KS_OBCR 0x20
+#define OBCR_ODS_16mA (1 << 6)
+
+#define KS_EEPCR 0x22
+#define EEPCR_EESA (1 << 4)
+#define EEPCR_EESB (1 << 3)
+#define EEPCR_EEDO (1 << 2)
+#define EEPCR_EESCK (1 << 1)
+#define EEPCR_EECS (1 << 0)
+
+#define KS_MBIR 0x24
+#define MBIR_TXMBF (1 << 12)
+#define MBIR_TXMBFA (1 << 11)
+#define MBIR_RXMBF (1 << 4)
+#define MBIR_RXMBFA (1 << 3)
+
+#define KS_GRR 0x26
+#define GRR_QMU (1 << 1)
+#define GRR_GSR (1 << 0)
+
+#define KS_WFCR 0x2A
+#define WFCR_MPRXE (1 << 7)
+#define WFCR_WF3E (1 << 3)
+#define WFCR_WF2E (1 << 2)
+#define WFCR_WF1E (1 << 1)
+#define WFCR_WF0E (1 << 0)
+
+#define KS_WF0CRC0 0x30
+#define KS_WF0CRC1 0x32
+#define KS_WF0BM0 0x34
+#define KS_WF0BM1 0x36
+#define KS_WF0BM2 0x38
+#define KS_WF0BM3 0x3A
+
+#define KS_WF1CRC0 0x40
+#define KS_WF1CRC1 0x42
+#define KS_WF1BM0 0x44
+#define KS_WF1BM1 0x46
+#define KS_WF1BM2 0x48
+#define KS_WF1BM3 0x4A
+
+#define KS_WF2CRC0 0x50
+#define KS_WF2CRC1 0x52
+#define KS_WF2BM0 0x54
+#define KS_WF2BM1 0x56
+#define KS_WF2BM2 0x58
+#define KS_WF2BM3 0x5A
+
+#define KS_WF3CRC0 0x60
+#define KS_WF3CRC1 0x62
+#define KS_WF3BM0 0x64
+#define KS_WF3BM1 0x66
+#define KS_WF3BM2 0x68
+#define KS_WF3BM3 0x6A
+
+#define KS_TXCR 0x70
+#define TXCR_TCGICMP (1 << 8)
+#define TXCR_TCGUDP (1 << 7)
+#define TXCR_TCGTCP (1 << 6)
+#define TXCR_TCGIP (1 << 5)
+#define TXCR_FTXQ (1 << 4)
+#define TXCR_TXFCE (1 << 3)
+#define TXCR_TXPE (1 << 2)
+#define TXCR_TXCRC (1 << 1)
+#define TXCR_TXE (1 << 0)
+
+#define KS_TXSR 0x72
+#define TXSR_TXLC (1 << 13)
+#define TXSR_TXMC (1 << 12)
+#define TXSR_TXFID_MASK (0x3f << 0)
+#define TXSR_TXFID_SHIFT (0)
+#define TXSR_TXFID_GET(_v) (((_v) >> 0) & 0x3f)
+
+#define KS_RXCR1 0x74
+#define RXCR1_FRXQ (1 << 15)
+#define RXCR1_RXUDPFCC (1 << 14)
+#define RXCR1_RXTCPFCC (1 << 13)
+#define RXCR1_RXIPFCC (1 << 12)
+#define RXCR1_RXPAFMA (1 << 11)
+#define RXCR1_RXFCE (1 << 10)
+#define RXCR1_RXEFE (1 << 9)
+#define RXCR1_RXMAFMA (1 << 8)
+#define RXCR1_RXBE (1 << 7)
+#define RXCR1_RXME (1 << 6)
+#define RXCR1_RXUE (1 << 5)
+#define RXCR1_RXAE (1 << 4)
+#define RXCR1_RXINVF (1 << 1)
+#define RXCR1_RXE (1 << 0)
+
+#define KS_RXCR2 0x76
+#define RXCR2_SRDBL_MASK (0x7 << 5)
+#define RXCR2_SRDBL_SHIFT (5)
+#define RXCR2_SRDBL_4B (0x0 << 5)
+#define RXCR2_SRDBL_8B (0x1 << 5)
+#define RXCR2_SRDBL_16B (0x2 << 5)
+#define RXCR2_SRDBL_32B (0x3 << 5)
+#define RXCR2_SRDBL_FRAME (0x4 << 5)
+#define RXCR2_IUFFP (1 << 4)
+#define RXCR2_RXIUFCEZ (1 << 3)
+#define RXCR2_UDPLFE (1 << 2)
+#define RXCR2_RXICMPFCC (1 << 1)
+#define RXCR2_RXSAF (1 << 0)
+
+#define KS_TXMIR 0x78
+
+#define KS_RXFHSR 0x7C
+#define RXFSHR_RXFV (1 << 15)
+#define RXFSHR_RXICMPFCS (1 << 13)
+#define RXFSHR_RXIPFCS (1 << 12)
+#define RXFSHR_RXTCPFCS (1 << 11)
+#define RXFSHR_RXUDPFCS (1 << 10)
+#define RXFSHR_RXBF (1 << 7)
+#define RXFSHR_RXMF (1 << 6)
+#define RXFSHR_RXUF (1 << 5)
+#define RXFSHR_RXMR (1 << 4)
+#define RXFSHR_RXFT (1 << 3)
+#define RXFSHR_RXFTL (1 << 2)
+#define RXFSHR_RXRF (1 << 1)
+#define RXFSHR_RXCE (1 << 0)
+
+#define KS_RXFHBCR 0x7E
+#define KS_TXQCR 0x80
+#define TXQCR_AETFE (1 << 2)
+#define TXQCR_TXQMAM (1 << 1)
+#define TXQCR_METFE (1 << 0)
+
+#define KS_RXQCR 0x82
+#define RXQCR_RXDTTS (1 << 12)
+#define RXQCR_RXDBCTS (1 << 11)
+#define RXQCR_RXFCTS (1 << 10)
+#define RXQCR_RXIPHTOE (1 << 9)
+#define RXQCR_RXDTTE (1 << 7)
+#define RXQCR_RXDBCTE (1 << 6)
+#define RXQCR_RXFCTE (1 << 5)
+#define RXQCR_ADRFE (1 << 4)
+#define RXQCR_SDA (1 << 3)
+#define RXQCR_RRXEF (1 << 0)
+
+#define KS_TXFDPR 0x84
+#define TXFDPR_TXFPAI (1 << 14)
+#define TXFDPR_TXFP_MASK (0x7ff << 0)
+#define TXFDPR_TXFP_SHIFT (0)
+
+#define KS_RXFDPR 0x86
+#define RXFDPR_RXFPAI (1 << 14)
+
+#define KS_RXDTTR 0x8C
+#define KS_RXDBCTR 0x8E
+
+#define KS_IER 0x90
+#define KS_ISR 0x92
+#define IRQ_LCI (1 << 15)
+#define IRQ_TXI (1 << 14)
+#define IRQ_RXI (1 << 13)
+#define IRQ_RXOI (1 << 11)
+#define IRQ_TXPSI (1 << 9)
+#define IRQ_RXPSI (1 << 8)
+#define IRQ_TXSAI (1 << 6)
+#define IRQ_RXWFDI (1 << 5)
+#define IRQ_RXMPDI (1 << 4)
+#define IRQ_LDI (1 << 3)
+#define IRQ_EDI (1 << 2)
+#define IRQ_SPIBEI (1 << 1)
+#define IRQ_DEDI (1 << 0)
+
+#define KS_RXFCTR 0x9C
+#define KS_RXFC 0x9D
+#define RXFCTR_RXFC_MASK (0xff << 8)
+#define RXFCTR_RXFC_SHIFT (8)
+#define RXFCTR_RXFC_GET(_v) (((_v) >> 8) & 0xff)
+#define RXFCTR_RXFCT_MASK (0xff << 0)
+#define RXFCTR_RXFCT_SHIFT (0)
+
+#define KS_TXNTFSR 0x9E
+
+#define KS_MAHTR0 0xA0
+#define KS_MAHTR1 0xA2
+#define KS_MAHTR2 0xA4
+#define KS_MAHTR3 0xA6
+
+#define KS_FCLWR 0xB0
+#define KS_FCHWR 0xB2
+#define KS_FCOWR 0xB4
+
+#define KS_CIDER 0xC0
+#define CIDER_ID 0x8870
+#define CIDER_REV_MASK (0x7 << 1)
+#define CIDER_REV_SHIFT (1)
+#define CIDER_REV_GET(_v) (((_v) >> 1) & 0x7)
+
+#define KS_CGCR 0xC6
+
+#define KS_IACR 0xC8
+#define IACR_RDEN (1 << 12)
+#define IACR_TSEL_MASK (0x3 << 10)
+#define IACR_TSEL_SHIFT (10)
+#define IACR_TSEL_MIB (0x3 << 10)
+#define IACR_ADDR_MASK (0x1f << 0)
+#define IACR_ADDR_SHIFT (0)
+
+#define KS_IADLR 0xD0
+#define KS_IAHDR 0xD2
+
+#define KS_PMECR 0xD4
+#define PMECR_PME_DELAY (1 << 14)
+#define PMECR_PME_POL (1 << 12)
+#define PMECR_WOL_WAKEUP (1 << 11)
+#define PMECR_WOL_MAGICPKT (1 << 10)
+#define PMECR_WOL_LINKUP (1 << 9)
+#define PMECR_WOL_ENERGY (1 << 8)
+#define PMECR_AUTO_WAKE_EN (1 << 7)
+#define PMECR_WAKEUP_NORMAL (1 << 6)
+#define PMECR_WKEVT_MASK (0xf << 2)
+#define PMECR_WKEVT_SHIFT (2)
+#define PMECR_WKEVT_GET(_v) (((_v) >> 2) & 0xf)
+#define PMECR_WKEVT_ENERGY (0x1 << 2)
+#define PMECR_WKEVT_LINK (0x2 << 2)
+#define PMECR_WKEVT_MAGICPKT (0x4 << 2)
+#define PMECR_WKEVT_FRAME (0x8 << 2)
+#define PMECR_PM_MASK (0x3 << 0)
+#define PMECR_PM_SHIFT (0)
+#define PMECR_PM_NORMAL (0x0 << 0)
+#define PMECR_PM_ENERGY (0x1 << 0)
+#define PMECR_PM_SOFTDOWN (0x2 << 0)
+#define PMECR_PM_POWERSAVE (0x3 << 0)
+
+/* Standard MII PHY data */
+#define KS_P1MBCR 0xE4
+#define KS_P1MBSR 0xE6
+#define KS_PHY1ILR 0xE8
+#define KS_PHY1IHR 0xEA
+#define KS_P1ANAR 0xEC
+#define KS_P1ANLPR 0xEE
+
+#define KS_P1SCLMD 0xF4
+#define P1SCLMD_LEDOFF (1 << 15)
+#define P1SCLMD_TXIDS (1 << 14)
+#define P1SCLMD_RESTARTAN (1 << 13)
+#define P1SCLMD_DISAUTOMDIX (1 << 10)
+#define P1SCLMD_FORCEMDIX (1 << 9)
+#define P1SCLMD_AUTONEGEN (1 << 7)
+#define P1SCLMD_FORCE100 (1 << 6)
+#define P1SCLMD_FORCEFDX (1 << 5)
+#define P1SCLMD_ADV_FLOW (1 << 4)
+#define P1SCLMD_ADV_100BT_FDX (1 << 3)
+#define P1SCLMD_ADV_100BT_HDX (1 << 2)
+#define P1SCLMD_ADV_10BT_FDX (1 << 1)
+#define P1SCLMD_ADV_10BT_HDX (1 << 0)
+
+#define KS_P1CR 0xF6
+#define P1CR_HP_MDIX (1 << 15)
+#define P1CR_REV_POL (1 << 13)
+#define P1CR_OP_100M (1 << 10)
+#define P1CR_OP_FDX (1 << 9)
+#define P1CR_OP_MDI (1 << 7)
+#define P1CR_AN_DONE (1 << 6)
+#define P1CR_LINK_GOOD (1 << 5)
+#define P1CR_PNTR_FLOW (1 << 4)
+#define P1CR_PNTR_100BT_FDX (1 << 3)
+#define P1CR_PNTR_100BT_HDX (1 << 2)
+#define P1CR_PNTR_10BT_FDX (1 << 1)
+#define P1CR_PNTR_10BT_HDX (1 << 0)
+
+/* TX Frame control */
+
+#define TXFR_TXIC (1 << 15)
+#define TXFR_TXFID_MASK (0x3f << 0)
+#define TXFR_TXFID_SHIFT (0)
+
+/* SPI frame opcodes */
+#define KS_SPIOP_RD (0x00)
+#define KS_SPIOP_WR (0x40)
+#define KS_SPIOP_RXFIFO (0x80)
+#define KS_SPIOP_TXFIFO (0xC0)
diff --git a/drivers/net/macsonic.c b/drivers/net/macsonic.c
index acd143d..61eabca 100644
--- a/drivers/net/macsonic.c
+++ b/drivers/net/macsonic.c
@@ -179,7 +179,7 @@ static const struct net_device_ops macsonic_netdev_ops = {
.ndo_set_mac_address = eth_mac_addr,
};
-static int __init macsonic_init(struct net_device *dev)
+static int __devinit macsonic_init(struct net_device *dev)
{
struct sonic_local* lp = netdev_priv(dev);
@@ -223,7 +223,7 @@ static int __init macsonic_init(struct net_device *dev)
return 0;
}
-static int __init mac_onboard_sonic_ethernet_addr(struct net_device *dev)
+static int __devinit mac_onboard_sonic_ethernet_addr(struct net_device *dev)
{
struct sonic_local *lp = netdev_priv(dev);
const int prom_addr = ONBOARD_SONIC_PROM_BASE;
@@ -288,7 +288,7 @@ static int __init mac_onboard_sonic_ethernet_addr(struct net_device *dev)
} else return 0;
}
-static int __init mac_onboard_sonic_probe(struct net_device *dev)
+static int __devinit mac_onboard_sonic_probe(struct net_device *dev)
{
/* Bwahahaha */
static int once_is_more_than_enough;
@@ -409,7 +409,7 @@ static int __init mac_onboard_sonic_probe(struct net_device *dev)
return macsonic_init(dev);
}
-static int __init mac_nubus_sonic_ethernet_addr(struct net_device *dev,
+static int __devinit mac_nubus_sonic_ethernet_addr(struct net_device *dev,
unsigned long prom_addr,
int id)
{
@@ -424,7 +424,7 @@ static int __init mac_nubus_sonic_ethernet_addr(struct net_device *dev,
return 0;
}
-static int __init macsonic_ident(struct nubus_dev *ndev)
+static int __devinit macsonic_ident(struct nubus_dev *ndev)
{
if (ndev->dr_hw == NUBUS_DRHW_ASANTE_LC &&
ndev->dr_sw == NUBUS_DRSW_SONIC_LC)
@@ -449,7 +449,7 @@ static int __init macsonic_ident(struct nubus_dev *ndev)
return -1;
}
-static int __init mac_nubus_sonic_probe(struct net_device *dev)
+static int __devinit mac_nubus_sonic_probe(struct net_device *dev)
{
static int slots;
struct nubus_dev* ndev = NULL;
@@ -562,7 +562,7 @@ static int __init mac_nubus_sonic_probe(struct net_device *dev)
return macsonic_init(dev);
}
-static int __init mac_sonic_probe(struct platform_device *pdev)
+static int __devinit mac_sonic_probe(struct platform_device *pdev)
{
struct net_device *dev;
struct sonic_local *lp;
@@ -575,6 +575,7 @@ static int __init mac_sonic_probe(struct platform_device *pdev)
lp = netdev_priv(dev);
lp->device = &pdev->dev;
SET_NETDEV_DEV(dev, &pdev->dev);
+ platform_set_drvdata(pdev, dev);
/* This will catch fatal stuff like -ENOMEM as well as success */
err = mac_onboard_sonic_probe(dev);
diff --git a/drivers/net/mlx4/en_ethtool.c b/drivers/net/mlx4/en_ethtool.c
index 091f990..86467b4 100644
--- a/drivers/net/mlx4/en_ethtool.c
+++ b/drivers/net/mlx4/en_ethtool.c
@@ -220,7 +220,7 @@ static int mlx4_en_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
cmd->autoneg = AUTONEG_DISABLE;
cmd->supported = SUPPORTED_10000baseT_Full;
- cmd->advertising = SUPPORTED_10000baseT_Full;
+ cmd->advertising = ADVERTISED_1000baseT_Full;
if (netif_carrier_ok(dev)) {
cmd->speed = SPEED_10000;
cmd->duplex = DUPLEX_FULL;
diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h
index e1cdba7..f86e050 100644
--- a/drivers/net/netxen/netxen_nic.h
+++ b/drivers/net/netxen/netxen_nic.h
@@ -210,6 +210,7 @@
#define NETXEN_CTX_SIGNATURE 0xdee0
#define NETXEN_CTX_SIGNATURE_V2 0x0002dee0
#define NETXEN_CTX_RESET 0xbad0
+#define NETXEN_CTX_D3_RESET 0xacc0
#define NETXEN_RCV_PRODUCER(ringid) (ringid)
#define PHAN_PEG_RCV_INITIALIZED 0xff01
@@ -773,6 +774,8 @@ struct nx_host_tx_ring {
u32 crb_cmd_consumer;
u32 num_desc;
+ struct netdev_queue *txq;
+
struct netxen_cmd_buffer *cmd_buf_arr;
struct cmd_desc_type0 *desc_head;
dma_addr_t phys_addr;
diff --git a/drivers/net/netxen/netxen_nic_ctx.c b/drivers/net/netxen/netxen_nic_ctx.c
index 4754f5c..9f8ae47 100644
--- a/drivers/net/netxen/netxen_nic_ctx.c
+++ b/drivers/net/netxen/netxen_nic_ctx.c
@@ -684,10 +684,8 @@ int netxen_alloc_hw_resources(struct netxen_adapter *adapter)
goto err_out_free;
} else {
err = netxen_init_old_ctx(adapter);
- if (err) {
- netxen_free_hw_resources(adapter);
- return err;
- }
+ if (err)
+ goto err_out_free;
}
return 0;
@@ -708,15 +706,18 @@ void netxen_free_hw_resources(struct netxen_adapter *adapter)
int port = adapter->portnum;
if (adapter->fw_major >= 4) {
- nx_fw_cmd_destroy_tx_ctx(adapter);
nx_fw_cmd_destroy_rx_ctx(adapter);
+ nx_fw_cmd_destroy_tx_ctx(adapter);
} else {
netxen_api_lock(adapter);
NXWR32(adapter, CRB_CTX_SIGNATURE_REG(port),
- NETXEN_CTX_RESET | port);
+ NETXEN_CTX_D3_RESET | port);
netxen_api_unlock(adapter);
}
+ /* Allow dma queues to drain after context reset */
+ msleep(20);
+
recv_ctx = &adapter->recv_ctx;
if (recv_ctx->hwctx != NULL) {
diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c
index ce3b89d..b9123d4 100644
--- a/drivers/net/netxen/netxen_nic_hw.c
+++ b/drivers/net/netxen/netxen_nic_hw.c
@@ -461,13 +461,14 @@ netxen_send_cmd_descs(struct netxen_adapter *adapter,
i = 0;
tx_ring = adapter->tx_ring;
- netif_tx_lock_bh(adapter->netdev);
+ __netif_tx_lock_bh(tx_ring->txq);
producer = tx_ring->producer;
consumer = tx_ring->sw_consumer;
- if (nr_desc >= find_diff_among(producer, consumer, tx_ring->num_desc)) {
- netif_tx_unlock_bh(adapter->netdev);
+ if (nr_desc >= netxen_tx_avail(tx_ring)) {
+ netif_tx_stop_queue(tx_ring->txq);
+ __netif_tx_unlock_bh(tx_ring->txq);
return -EBUSY;
}
@@ -490,7 +491,7 @@ netxen_send_cmd_descs(struct netxen_adapter *adapter,
netxen_nic_update_cmd_producer(adapter, tx_ring);
- netif_tx_unlock_bh(adapter->netdev);
+ __netif_tx_unlock_bh(tx_ring->txq);
return 0;
}
diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c
index b899bd5..5d3343e 100644
--- a/drivers/net/netxen/netxen_nic_init.c
+++ b/drivers/net/netxen/netxen_nic_init.c
@@ -214,6 +214,7 @@ int netxen_alloc_sw_resources(struct netxen_adapter *adapter)
adapter->tx_ring = tx_ring;
tx_ring->num_desc = adapter->num_txd;
+ tx_ring->txq = netdev_get_tx_queue(netdev, 0);
cmd_buf_arr = vmalloc(TX_BUFF_RINGSIZE(tx_ring));
if (cmd_buf_arr == NULL) {
@@ -1400,10 +1401,10 @@ int netxen_process_cmd_ring(struct netxen_adapter *adapter)
smp_mb();
if (netif_queue_stopped(netdev) && netif_carrier_ok(netdev)) {
- netif_tx_lock(netdev);
+ __netif_tx_lock(tx_ring->txq, smp_processor_id());
if (netxen_tx_avail(tx_ring) > TX_STOP_THRESH)
netif_wake_queue(netdev);
- netif_tx_unlock(netdev);
+ __netif_tx_unlock(tx_ring->txq);
}
}
/*
diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c
index 27539dd..637ac8b 100644
--- a/drivers/net/netxen/netxen_nic_main.c
+++ b/drivers/net/netxen/netxen_nic_main.c
@@ -215,9 +215,9 @@ netxen_napi_disable(struct netxen_adapter *adapter)
for (ring = 0; ring < adapter->max_sds_rings; ring++) {
sds_ring = &recv_ctx->sds_rings[ring];
- napi_disable(&sds_ring->napi);
netxen_nic_disable_int(sds_ring);
- synchronize_irq(sds_ring->irq);
+ napi_synchronize(&sds_ring->napi);
+ napi_disable(&sds_ring->napi);
}
}
@@ -833,11 +833,11 @@ netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev)
adapter->ahw.linkup = 0;
- netxen_napi_enable(adapter);
-
if (adapter->max_sds_rings > 1)
netxen_config_rss(adapter, 1);
+ netxen_napi_enable(adapter);
+
if (adapter->capabilities & NX_FW_CAPABILITY_LINK_NOTIFICATION)
netxen_linkevent_request(adapter, 1);
else
@@ -851,8 +851,9 @@ netxen_nic_up(struct netxen_adapter *adapter, struct net_device *netdev)
static void
netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev)
{
+ spin_lock(&adapter->tx_clean_lock);
netif_carrier_off(netdev);
- netif_stop_queue(netdev);
+ netif_tx_disable(netdev);
if (adapter->stop_port)
adapter->stop_port(adapter);
@@ -863,9 +864,10 @@ netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev)
netxen_napi_disable(adapter);
netxen_release_tx_buffers(adapter);
+ spin_unlock(&adapter->tx_clean_lock);
- FLUSH_SCHEDULED_WORK();
del_timer_sync(&adapter->watchdog_timer);
+ FLUSH_SCHEDULED_WORK();
}
@@ -943,8 +945,8 @@ err_out_free_sw:
static void
netxen_nic_detach(struct netxen_adapter *adapter)
{
- netxen_release_rx_buffers(adapter);
netxen_free_hw_resources(adapter);
+ netxen_release_rx_buffers(adapter);
netxen_nic_free_irq(adapter);
netxen_free_sw_resources(adapter);
@@ -1533,10 +1535,12 @@ static int netxen_nic_check_temp(struct netxen_adapter *adapter)
printk(KERN_ALERT
"%s: Device temperature %d degrees C exceeds"
" maximum allowed. Hardware has been shut down.\n",
- netxen_nic_driver_name, temp_val);
+ netdev->name, temp_val);
+
+ netif_device_detach(netdev);
+ netxen_nic_down(adapter, netdev);
+ netxen_nic_detach(adapter);
- netif_carrier_off(netdev);
- netif_stop_queue(netdev);
rv = 1;
} else if (temp_state == NX_TEMP_WARN) {
if (adapter->temp == NX_TEMP_NORMAL) {
@@ -1544,13 +1548,13 @@ static int netxen_nic_check_temp(struct netxen_adapter *adapter)
"%s: Device temperature %d degrees C "
"exceeds operating range."
" Immediate action needed.\n",
- netxen_nic_driver_name, temp_val);
+ netdev->name, temp_val);
}
} else {
if (adapter->temp == NX_TEMP_WARN) {
printk(KERN_INFO
"%s: Device temperature is now %d degrees C"
- " in normal range.\n", netxen_nic_driver_name,
+ " in normal range.\n", netdev->name,
temp_val);
}
}
@@ -1623,7 +1627,7 @@ void netxen_watchdog_task(struct work_struct *work)
struct netxen_adapter *adapter =
container_of(work, struct netxen_adapter, watchdog_task);
- if ((adapter->portnum == 0) && netxen_nic_check_temp(adapter))
+ if (netxen_nic_check_temp(adapter))
return;
if (!adapter->has_link_events)
@@ -1645,6 +1649,9 @@ static void netxen_tx_timeout_task(struct work_struct *work)
struct netxen_adapter *adapter =
container_of(work, struct netxen_adapter, tx_timeout_task);
+ if (!netif_running(adapter->netdev))
+ return;
+
printk(KERN_ERR "%s %s: transmit timeout, resetting.\n",
netxen_nic_driver_name, adapter->netdev->name);
@@ -1757,7 +1764,8 @@ static int netxen_nic_poll(struct napi_struct *napi, int budget)
if ((work_done < budget) && tx_complete) {
napi_complete(&sds_ring->napi);
- netxen_nic_enable_int(sds_ring);
+ if (netif_running(adapter->netdev))
+ netxen_nic_enable_int(sds_ring);
}
return work_done;
diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c
index ec7cf5a..690b9c7 100644
--- a/drivers/net/pcmcia/3c589_cs.c
+++ b/drivers/net/pcmcia/3c589_cs.c
@@ -156,6 +156,7 @@ static struct net_device_stats *el3_get_stats(struct net_device *dev);
static int el3_rx(struct net_device *dev);
static int el3_close(struct net_device *dev);
static void el3_tx_timeout(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
static void set_multicast_list(struct net_device *dev);
static const struct ethtool_ops netdev_ethtool_ops;
@@ -488,8 +489,7 @@ static void tc589_reset(struct net_device *dev)
/* Switch to register set 1 for normal use. */
EL3WINDOW(1);
- /* Accept b-cast and phys addr only. */
- outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+ set_rx_mode(dev);
outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
@@ -700,7 +700,7 @@ static irqreturn_t el3_interrupt(int irq, void *dev_id)
if (fifo_diag & 0x2000) {
/* Rx underrun */
tc589_wait_for_completion(dev, RxReset);
- set_multicast_list(dev);
+ set_rx_mode(dev);
outw(RxEnable, ioaddr + EL3_CMD);
}
outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
@@ -905,14 +905,11 @@ static int el3_rx(struct net_device *dev)
return 0;
}
-static void set_multicast_list(struct net_device *dev)
+static void set_rx_mode(struct net_device *dev)
{
- struct el3_private *lp = netdev_priv(dev);
- struct pcmcia_device *link = lp->p_dev;
unsigned int ioaddr = dev->base_addr;
u16 opts = SetRxFilter | RxStation | RxBroadcast;
- if (!pcmcia_dev_present(link)) return;
if (dev->flags & IFF_PROMISC)
opts |= RxMulticast | RxProm;
else if (dev->mc_count || (dev->flags & IFF_ALLMULTI))
@@ -920,6 +917,16 @@ static void set_multicast_list(struct net_device *dev)
outw(opts, ioaddr + EL3_CMD);
}
+static void set_multicast_list(struct net_device *dev)
+{
+ struct el3_private *priv = netdev_priv(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ set_rx_mode(dev);
+ spin_unlock_irqrestore(&priv->lock, flags);
+}
+
static int el3_close(struct net_device *dev)
{
struct el3_private *lp = netdev_priv(dev);
diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c
index 18821f2..e3156c9 100644
--- a/drivers/net/sc92031.c
+++ b/drivers/net/sc92031.c
@@ -1593,6 +1593,7 @@ out:
static struct pci_device_id sc92031_pci_device_id_table[] __devinitdata = {
{ PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x2031) },
{ PCI_DEVICE(PCI_VENDOR_ID_SILAN, 0x8139) },
+ { PCI_DEVICE(0x1088, 0x2031) },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, sc92031_pci_device_id_table);
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index daf961a..3550c5d 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -1151,14 +1151,7 @@ stopped:
/* reset the Rx prefetch unit */
sky2_write32(hw, Y2_QADDR(rxq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
-
- /* Reset the RAM Buffer receive queue */
- sky2_write8(hw, RB_ADDR(rxq, RB_CTRL), RB_RST_SET);
-
- /* Reset Rx MAC FIFO */
- sky2_write8(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), GMF_RST_SET);
-
- sky2_read8(hw, B0_CTST);
+ mmiowb();
}
/* Clean out receive buffer area, assumes receiver hardware stopped */
@@ -1825,12 +1818,6 @@ static int sky2_down(struct net_device *dev)
if (netif_msg_ifdown(sky2))
printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
- /* Disable port IRQ */
- imask = sky2_read32(hw, B0_IMSK);
- imask &= ~portirq_msk[port];
- sky2_write32(hw, B0_IMSK, imask);
- sky2_read32(hw, B0_IMSK);
-
/* Force flow control off */
sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF);
@@ -1870,8 +1857,6 @@ static int sky2_down(struct net_device *dev)
sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET);
- sky2_rx_stop(sky2);
-
sky2_write8(hw, SK_REG(port, RX_GMF_CTRL_T), GMF_RST_SET);
sky2_write8(hw, SK_REG(port, TX_GMF_CTRL_T), GMF_RST_SET);
@@ -1881,6 +1866,14 @@ static int sky2_down(struct net_device *dev)
sky2_write32(hw, STAT_ISR_TIMER_CNT, 0);
sky2_read8(hw, STAT_ISR_TIMER_CTRL);
+ sky2_rx_stop(sky2);
+
+ /* Disable port IRQ */
+ imask = sky2_read32(hw, B0_IMSK);
+ imask &= ~portirq_msk[port];
+ sky2_write32(hw, B0_IMSK, imask);
+ sky2_read32(hw, B0_IMSK);
+
synchronize_irq(hw->pdev->irq);
napi_synchronize(&hw->napi);
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index a906d39..c47237c 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -369,4 +369,12 @@ config USB_NET_INT51X1
(Powerline Communications) solution with an Intellon
INT51x1/INT5200 chip, like the "devolo dLan duo".
+config USB_CDC_PHONET
+ tristate "CDC Phonet support"
+ depends on PHONET
+ help
+ Choose this option to support the Phonet interface to a Nokia
+ cellular modem, as found on most Nokia handsets with the
+ "PC suite" USB profile.
+
endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index b870b0b..e17afb7 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -21,4 +21,5 @@ obj-$(CONFIG_USB_NET_ZAURUS) += zaurus.o
obj-$(CONFIG_USB_NET_MCS7830) += mcs7830.o
obj-$(CONFIG_USB_USBNET) += usbnet.o
obj-$(CONFIG_USB_NET_INT51X1) += int51x1.o
+obj-$(CONFIG_USB_CDC_PHONET) += cdc-phonet.o
diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c
new file mode 100644
index 0000000..792af72
--- /dev/null
+++ b/drivers/net/usb/cdc-phonet.c
@@ -0,0 +1,461 @@
+/*
+ * phonet.c -- USB CDC Phonet host driver
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation. All rights reserved.
+ *
+ * Author: Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/cdc.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_phonet.h>
+
+#define PN_MEDIA_USB 0x1B
+
+static const unsigned rxq_size = 17;
+
+struct usbpn_dev {
+ struct net_device *dev;
+
+ struct usb_interface *intf, *data_intf;
+ struct usb_device *usb;
+ unsigned int tx_pipe, rx_pipe;
+ u8 active_setting;
+ u8 disconnected;
+
+ unsigned tx_queue;
+ spinlock_t tx_lock;
+
+ spinlock_t rx_lock;
+ struct sk_buff *rx_skb;
+ struct urb *urbs[0];
+};
+
+static void tx_complete(struct urb *req);
+static void rx_complete(struct urb *req);
+
+/*
+ * Network device callbacks
+ */
+static int usbpn_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct usbpn_dev *pnd = netdev_priv(dev);
+ struct urb *req = NULL;
+ unsigned long flags;
+ int err;
+
+ if (skb->protocol != htons(ETH_P_PHONET))
+ goto drop;
+
+ req = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!req)
+ goto drop;
+ usb_fill_bulk_urb(req, pnd->usb, pnd->tx_pipe, skb->data, skb->len,
+ tx_complete, skb);
+ req->transfer_flags = URB_ZERO_PACKET;
+ err = usb_submit_urb(req, GFP_ATOMIC);
+ if (err) {
+ usb_free_urb(req);
+ goto drop;
+ }
+
+ spin_lock_irqsave(&pnd->tx_lock, flags);
+ pnd->tx_queue++;
+ if (pnd->tx_queue >= dev->tx_queue_len)
+ netif_stop_queue(dev);
+ spin_unlock_irqrestore(&pnd->tx_lock, flags);
+ return 0;
+
+drop:
+ dev_kfree_skb(skb);
+ dev->stats.tx_dropped++;
+ return 0;
+}
+
+static void tx_complete(struct urb *req)
+{
+ struct sk_buff *skb = req->context;
+ struct net_device *dev = skb->dev;
+ struct usbpn_dev *pnd = netdev_priv(dev);
+
+ switch (req->status) {
+ case 0:
+ dev->stats.tx_bytes += skb->len;
+ break;
+
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev->stats.tx_aborted_errors++;
+ default:
+ dev->stats.tx_errors++;
+ dev_dbg(&dev->dev, "TX error (%d)\n", req->status);
+ }
+ dev->stats.tx_packets++;
+
+ spin_lock(&pnd->tx_lock);
+ pnd->tx_queue--;
+ netif_wake_queue(dev);
+ spin_unlock(&pnd->tx_lock);
+
+ dev_kfree_skb_any(skb);
+ usb_free_urb(req);
+}
+
+static int rx_submit(struct usbpn_dev *pnd, struct urb *req, gfp_t gfp_flags)
+{
+ struct net_device *dev = pnd->dev;
+ struct page *page;
+ int err;
+
+ page = __netdev_alloc_page(dev, gfp_flags);
+ if (!page)
+ return -ENOMEM;
+
+ usb_fill_bulk_urb(req, pnd->usb, pnd->rx_pipe, page_address(page),
+ PAGE_SIZE, rx_complete, dev);
+ req->transfer_flags = 0;
+ err = usb_submit_urb(req, gfp_flags);
+ if (unlikely(err)) {
+ dev_dbg(&dev->dev, "RX submit error (%d)\n", err);
+ netdev_free_page(dev, page);
+ }
+ return err;
+}
+
+static void rx_complete(struct urb *req)
+{
+ struct net_device *dev = req->context;
+ struct usbpn_dev *pnd = netdev_priv(dev);
+ struct page *page = virt_to_page(req->transfer_buffer);
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ switch (req->status) {
+ case 0:
+ spin_lock_irqsave(&pnd->rx_lock, flags);
+ skb = pnd->rx_skb;
+ if (!skb) {
+ skb = pnd->rx_skb = netdev_alloc_skb(dev, 12);
+ if (likely(skb)) {
+ /* Can't use pskb_pull() on page in IRQ */
+ memcpy(skb_put(skb, 1), page_address(page), 1);
+ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
+ page, 1, req->actual_length);
+ page = NULL;
+ }
+ } else {
+ skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
+ page, 0, req->actual_length);
+ page = NULL;
+ }
+ if (req->actual_length < PAGE_SIZE)
+ pnd->rx_skb = NULL; /* Last fragment */
+ else
+ skb = NULL;
+ spin_unlock_irqrestore(&pnd->rx_lock, flags);
+ if (skb) {
+ skb->protocol = htons(ETH_P_PHONET);
+ skb_reset_mac_header(skb);
+ __skb_pull(skb, 1);
+ skb->dev = dev;
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+
+ netif_rx(skb);
+ }
+ goto resubmit;
+
+ case -ENOENT:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ req = NULL;
+ break;
+
+ case -EOVERFLOW:
+ dev->stats.rx_over_errors++;
+ dev_dbg(&dev->dev, "RX overflow\n");
+ break;
+
+ case -EILSEQ:
+ dev->stats.rx_crc_errors++;
+ break;
+ }
+
+ dev->stats.rx_errors++;
+resubmit:
+ if (page)
+ netdev_free_page(dev, page);
+ if (req)
+ rx_submit(pnd, req, GFP_ATOMIC);
+}
+
+static int usbpn_close(struct net_device *dev);
+
+static int usbpn_open(struct net_device *dev)
+{
+ struct usbpn_dev *pnd = netdev_priv(dev);
+ int err;
+ unsigned i;
+ unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber;
+
+ err = usb_set_interface(pnd->usb, num, pnd->active_setting);
+ if (err)
+ return err;
+
+ for (i = 0; i < rxq_size; i++) {
+ struct urb *req = usb_alloc_urb(0, GFP_KERNEL);
+
+ if (!req || rx_submit(pnd, req, GFP_KERNEL)) {
+ usbpn_close(dev);
+ return -ENOMEM;
+ }
+ pnd->urbs[i] = req;
+ }
+
+ netif_wake_queue(dev);
+ return 0;
+}
+
+static int usbpn_close(struct net_device *dev)
+{
+ struct usbpn_dev *pnd = netdev_priv(dev);
+ unsigned i;
+ unsigned num = pnd->data_intf->cur_altsetting->desc.bInterfaceNumber;
+
+ netif_stop_queue(dev);
+
+ for (i = 0; i < rxq_size; i++) {
+ struct urb *req = pnd->urbs[i];
+
+ if (!req)
+ continue;
+ usb_kill_urb(req);
+ usb_free_urb(req);
+ pnd->urbs[i] = NULL;
+ }
+
+ return usb_set_interface(pnd->usb, num, !pnd->active_setting);
+}
+
+static int usbpn_set_mtu(struct net_device *dev, int new_mtu)
+{
+ if ((new_mtu < PHONET_MIN_MTU) || (new_mtu > PHONET_MAX_MTU))
+ return -EINVAL;
+
+ dev->mtu = new_mtu;
+ return 0;
+}
+
+static const struct net_device_ops usbpn_ops = {
+ .ndo_open = usbpn_open,
+ .ndo_stop = usbpn_close,
+ .ndo_start_xmit = usbpn_xmit,
+ .ndo_change_mtu = usbpn_set_mtu,
+};
+
+static void usbpn_setup(struct net_device *dev)
+{
+ dev->features = 0;
+ dev->netdev_ops = &usbpn_ops,
+ dev->header_ops = &phonet_header_ops;
+ dev->type = ARPHRD_PHONET;
+ dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+ dev->mtu = PHONET_MAX_MTU;
+ dev->hard_header_len = 1;
+ dev->dev_addr[0] = PN_MEDIA_USB;
+ dev->addr_len = 1;
+ dev->tx_queue_len = 3;
+
+ dev->destructor = free_netdev;
+}
+
+/*
+ * USB driver callbacks
+ */
+static struct usb_device_id usbpn_ids[] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_VENDOR
+ | USB_DEVICE_ID_MATCH_INT_CLASS
+ | USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+ .idVendor = 0x0421, /* Nokia */
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = 0xFE,
+ },
+ { },
+};
+
+MODULE_DEVICE_TABLE(usb, usbpn_ids);
+
+static struct usb_driver usbpn_driver;
+
+int usbpn_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ static const char ifname[] = "usbpn%d";
+ const struct usb_cdc_union_desc *union_header = NULL;
+ const struct usb_cdc_header_desc *phonet_header = NULL;
+ const struct usb_host_interface *data_desc;
+ struct usb_interface *data_intf;
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ struct net_device *dev;
+ struct usbpn_dev *pnd;
+ u8 *data;
+ int len, err;
+
+ data = intf->altsetting->extra;
+ len = intf->altsetting->extralen;
+ while (len >= 3) {
+ u8 dlen = data[0];
+ if (dlen < 3)
+ return -EINVAL;
+
+ /* bDescriptorType */
+ if (data[1] == USB_DT_CS_INTERFACE) {
+ /* bDescriptorSubType */
+ switch (data[2]) {
+ case USB_CDC_UNION_TYPE:
+ if (union_header || dlen < 5)
+ break;
+ union_header =
+ (struct usb_cdc_union_desc *)data;
+ break;
+ case 0xAB:
+ if (phonet_header || dlen < 5)
+ break;
+ phonet_header =
+ (struct usb_cdc_header_desc *)data;
+ break;
+ }
+ }
+ data += dlen;
+ len -= dlen;
+ }
+
+ if (!union_header || !phonet_header)
+ return -EINVAL;
+
+ data_intf = usb_ifnum_to_if(usbdev, union_header->bSlaveInterface0);
+ if (data_intf == NULL)
+ return -ENODEV;
+ /* Data interface has one inactive and one active setting */
+ if (data_intf->num_altsetting != 2)
+ return -EINVAL;
+ if (data_intf->altsetting[0].desc.bNumEndpoints == 0
+ && data_intf->altsetting[1].desc.bNumEndpoints == 2)
+ data_desc = data_intf->altsetting + 1;
+ else
+ if (data_intf->altsetting[0].desc.bNumEndpoints == 2
+ && data_intf->altsetting[1].desc.bNumEndpoints == 0)
+ data_desc = data_intf->altsetting;
+ else
+ return -EINVAL;
+
+ dev = alloc_netdev(sizeof(*pnd) + sizeof(pnd->urbs[0]) * rxq_size,
+ ifname, usbpn_setup);
+ if (!dev)
+ return -ENOMEM;
+
+ pnd = netdev_priv(dev);
+ SET_NETDEV_DEV(dev, &intf->dev);
+ netif_stop_queue(dev);
+
+ pnd->dev = dev;
+ pnd->usb = usb_get_dev(usbdev);
+ pnd->intf = intf;
+ pnd->data_intf = data_intf;
+ spin_lock_init(&pnd->tx_lock);
+ spin_lock_init(&pnd->rx_lock);
+ /* Endpoints */
+ if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) {
+ pnd->rx_pipe = usb_rcvbulkpipe(usbdev,
+ data_desc->endpoint[0].desc.bEndpointAddress);
+ pnd->tx_pipe = usb_sndbulkpipe(usbdev,
+ data_desc->endpoint[1].desc.bEndpointAddress);
+ } else {
+ pnd->rx_pipe = usb_rcvbulkpipe(usbdev,
+ data_desc->endpoint[1].desc.bEndpointAddress);
+ pnd->tx_pipe = usb_sndbulkpipe(usbdev,
+ data_desc->endpoint[0].desc.bEndpointAddress);
+ }
+ pnd->active_setting = data_desc - data_intf->altsetting;
+
+ err = usb_driver_claim_interface(&usbpn_driver, data_intf, pnd);
+ if (err)
+ goto out;
+
+ /* Force inactive mode until the network device is brought UP */
+ usb_set_interface(usbdev, union_header->bSlaveInterface0,
+ !pnd->active_setting);
+ usb_set_intfdata(intf, pnd);
+
+ err = register_netdev(dev);
+ if (err) {
+ usb_driver_release_interface(&usbpn_driver, data_intf);
+ goto out;
+ }
+
+ dev_dbg(&dev->dev, "USB CDC Phonet device found\n");
+ return 0;
+
+out:
+ usb_set_intfdata(intf, NULL);
+ free_netdev(dev);
+ return err;
+}
+
+static void usbpn_disconnect(struct usb_interface *intf)
+{
+ struct usbpn_dev *pnd = usb_get_intfdata(intf);
+ struct usb_device *usb = pnd->usb;
+
+ if (pnd->disconnected)
+ return;
+
+ pnd->disconnected = 1;
+ usb_driver_release_interface(&usbpn_driver,
+ (pnd->intf == intf) ? pnd->data_intf : pnd->intf);
+ unregister_netdev(pnd->dev);
+ usb_put_dev(usb);
+}
+
+static struct usb_driver usbpn_driver = {
+ .name = "cdc_phonet",
+ .probe = usbpn_probe,
+ .disconnect = usbpn_disconnect,
+ .id_table = usbpn_ids,
+};
+
+static int __init usbpn_init(void)
+{
+ return usb_register(&usbpn_driver);
+}
+
+static void __exit usbpn_exit(void)
+{
+ usb_deregister(&usbpn_driver);
+}
+
+module_init(usbpn_init);
+module_exit(usbpn_exit);
+
+MODULE_AUTHOR("Remi Denis-Courmont");
+MODULE_DESCRIPTION("USB CDC Phonet host interface");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/usb/cdc_eem.c b/drivers/net/usb/cdc_eem.c
index cd35d50..45cebfb 100644
--- a/drivers/net/usb/cdc_eem.c
+++ b/drivers/net/usb/cdc_eem.c
@@ -311,7 +311,7 @@ static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
* bmCRC = 0 : CRC = 0xDEADBEEF
*/
if (header & BIT(14))
- crc2 = ~crc32_le(~0, skb2->data, len);
+ crc2 = ~crc32_le(~0, skb2->data, skb2->len);
else
crc2 = 0xdeadbeef;
diff --git a/include/net/sock.h b/include/net/sock.h
index 2c0da92..950409d 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -104,15 +104,15 @@ struct net;
/**
* struct sock_common - minimal network layer representation of sockets
+ * @skc_node: main hash linkage for various protocol lookup tables
+ * @skc_nulls_node: main hash linkage for UDP/UDP-Lite protocol
+ * @skc_refcnt: reference count
+ * @skc_hash: hash value used with various protocol lookup tables
* @skc_family: network address family
* @skc_state: Connection state
* @skc_reuse: %SO_REUSEADDR setting
* @skc_bound_dev_if: bound device index if != 0
- * @skc_node: main hash linkage for various protocol lookup tables
- * @skc_nulls_node: main hash linkage for UDP/UDP-Lite protocol
* @skc_bind_node: bind hash linkage for various protocol lookup tables
- * @skc_refcnt: reference count
- * @skc_hash: hash value used with various protocol lookup tables
* @skc_prot: protocol handlers inside a network family
* @skc_net: reference to the network namespace of this socket
*
@@ -120,17 +120,21 @@ struct net;
* for struct sock and struct inet_timewait_sock.
*/
struct sock_common {
- unsigned short skc_family;
- volatile unsigned char skc_state;
- unsigned char skc_reuse;
- int skc_bound_dev_if;
+ /*
+ * first fields are not copied in sock_copy()
+ */
union {
struct hlist_node skc_node;
struct hlist_nulls_node skc_nulls_node;
};
- struct hlist_node skc_bind_node;
atomic_t skc_refcnt;
+
unsigned int skc_hash;
+ unsigned short skc_family;
+ volatile unsigned char skc_state;
+ unsigned char skc_reuse;
+ int skc_bound_dev_if;
+ struct hlist_node skc_bind_node;
struct proto *skc_prot;
#ifdef CONFIG_NET_NS
struct net *skc_net;
@@ -208,15 +212,17 @@ struct sock {
* don't add nothing before this first member (__sk_common) --acme
*/
struct sock_common __sk_common;
+#define sk_node __sk_common.skc_node
+#define sk_nulls_node __sk_common.skc_nulls_node
+#define sk_refcnt __sk_common.skc_refcnt
+
+#define sk_copy_start __sk_common.skc_hash
+#define sk_hash __sk_common.skc_hash
#define sk_family __sk_common.skc_family
#define sk_state __sk_common.skc_state
#define sk_reuse __sk_common.skc_reuse
#define sk_bound_dev_if __sk_common.skc_bound_dev_if
-#define sk_node __sk_common.skc_node
-#define sk_nulls_node __sk_common.skc_nulls_node
#define sk_bind_node __sk_common.skc_bind_node
-#define sk_refcnt __sk_common.skc_refcnt
-#define sk_hash __sk_common.skc_hash
#define sk_prot __sk_common.skc_prot
#define sk_net __sk_common.skc_net
kmemcheck_bitfield_begin(flags);
diff --git a/include/net/tcp.h b/include/net/tcp.h
index 19f4150..88af843 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1425,6 +1425,11 @@ struct tcp_request_sock_ops {
#ifdef CONFIG_TCP_MD5SIG
struct tcp_md5sig_key *(*md5_lookup) (struct sock *sk,
struct request_sock *req);
+ int (*calc_md5_hash) (char *location,
+ struct tcp_md5sig_key *md5,
+ struct sock *sk,
+ struct request_sock *req,
+ struct sk_buff *skb);
#endif
};
diff --git a/net/can/bcm.c b/net/can/bcm.c
index 95d7f32..72720c7 100644
--- a/net/can/bcm.c
+++ b/net/can/bcm.c
@@ -75,6 +75,7 @@ static __initdata const char banner[] = KERN_INFO
MODULE_DESCRIPTION("PF_CAN broadcast manager protocol");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_ALIAS("can-proto-2");
/* easy access to can_frame payload */
static inline u64 GET_U64(const struct can_frame *cp)
@@ -1469,6 +1470,9 @@ static int bcm_release(struct socket *sock)
bo->ifindex = 0;
}
+ sock_orphan(sk);
+ sock->sk = NULL;
+
release_sock(sk);
sock_put(sk);
diff --git a/net/can/raw.c b/net/can/raw.c
index 6aa154e..f4cc445 100644
--- a/net/can/raw.c
+++ b/net/can/raw.c
@@ -62,6 +62,7 @@ static __initdata const char banner[] =
MODULE_DESCRIPTION("PF_CAN raw protocol");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Urs Thuermann <urs.thuermann@volkswagen.de>");
+MODULE_ALIAS("can-proto-1");
#define MASK_ALL 0
@@ -306,6 +307,9 @@ static int raw_release(struct socket *sock)
ro->bound = 0;
ro->count = 0;
+ sock_orphan(sk);
+ sock->sk = NULL;
+
release_sock(sk);
sock_put(sk);
diff --git a/net/core/sock.c b/net/core/sock.c
index ba5d211..bbb25be 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -631,7 +631,7 @@ set_rcvbuf:
case SO_TIMESTAMPING:
if (val & ~SOF_TIMESTAMPING_MASK) {
- ret = EINVAL;
+ ret = -EINVAL;
break;
}
sock_valbool_flag(sk, SOCK_TIMESTAMPING_TX_HARDWARE,
@@ -919,13 +919,19 @@ static inline void sock_lock_init(struct sock *sk)
af_family_keys + sk->sk_family);
}
+/*
+ * Copy all fields from osk to nsk but nsk->sk_refcnt must not change yet,
+ * even temporarly, because of RCU lookups. sk_node should also be left as is.
+ */
static void sock_copy(struct sock *nsk, const struct sock *osk)
{
#ifdef CONFIG_SECURITY_NETWORK
void *sptr = nsk->sk_security;
#endif
-
- memcpy(nsk, osk, osk->sk_prot->obj_size);
+ BUILD_BUG_ON(offsetof(struct sock, sk_copy_start) !=
+ sizeof(osk->sk_node) + sizeof(osk->sk_refcnt));
+ memcpy(&nsk->sk_copy_start, &osk->sk_copy_start,
+ osk->sk_prot->obj_size - offsetof(struct sock, sk_copy_start));
#ifdef CONFIG_SECURITY_NETWORK
nsk->sk_security = sptr;
security_sk_clone(osk, nsk);
@@ -1140,6 +1146,11 @@ struct sock *sk_clone(const struct sock *sk, const gfp_t priority)
newsk->sk_err = 0;
newsk->sk_priority = 0;
+ /*
+ * Before updating sk_refcnt, we must commit prior changes to memory
+ * (Documentation/RCU/rculist_nulls.txt for details)
+ */
+ smp_wmb();
atomic_set(&newsk->sk_refcnt, 2);
/*
@@ -1855,6 +1866,11 @@ void sock_init_data(struct socket *sock, struct sock *sk)
sk->sk_stamp = ktime_set(-1L, 0);
+ /*
+ * Before updating sk_refcnt, we must commit prior changes to memory
+ * (Documentation/RCU/rculist_nulls.txt for details)
+ */
+ smp_wmb();
atomic_set(&sk->sk_refcnt, 1);
atomic_set(&sk->sk_wmem_alloc, 1);
atomic_set(&sk->sk_drops, 0);
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 5a1ca26..6d88219 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -1160,6 +1160,7 @@ struct request_sock_ops tcp_request_sock_ops __read_mostly = {
#ifdef CONFIG_TCP_MD5SIG
static struct tcp_request_sock_ops tcp_request_sock_ipv4_ops = {
.md5_lookup = tcp_v4_reqsk_md5_lookup,
+ .calc_md5_hash = tcp_v4_md5_hash_skb,
};
#endif
@@ -1373,7 +1374,7 @@ struct sock *tcp_v4_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
*/
char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC);
if (newkey != NULL)
- tcp_v4_md5_do_add(newsk, inet_sk(sk)->daddr,
+ tcp_v4_md5_do_add(newsk, newinet->daddr,
newkey, key->keylen);
newsk->sk_route_caps &= ~NETIF_F_GSO_MASK;
}
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 5bdf08d..bd62712 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -2261,7 +2261,7 @@ struct sk_buff *tcp_make_synack(struct sock *sk, struct dst_entry *dst,
#ifdef CONFIG_TCP_MD5SIG
/* Okay, we have all we need - do the md5 hash if needed */
if (md5) {
- tp->af_specific->calc_md5_hash(md5_hash_location,
+ tcp_rsk(req)->af_specific->calc_md5_hash(md5_hash_location,
md5, NULL, req, skb);
}
#endif
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 58810c6..d849dd5 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -896,6 +896,7 @@ struct request_sock_ops tcp6_request_sock_ops __read_mostly = {
#ifdef CONFIG_TCP_MD5SIG
static struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {
.md5_lookup = tcp_v6_reqsk_md5_lookup,
+ .calc_md5_hash = tcp_v6_md5_hash_skb,
};
#endif
@@ -1441,7 +1442,7 @@ static struct sock * tcp_v6_syn_recv_sock(struct sock *sk, struct sk_buff *skb,
*/
char *newkey = kmemdup(key->key, key->keylen, GFP_ATOMIC);
if (newkey != NULL)
- tcp_v6_md5_do_add(newsk, &inet6_sk(sk)->daddr,
+ tcp_v6_md5_do_add(newsk, &newnp->daddr,
newkey, key->keylen);
}
#endif
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index 7508f11..b5869b9 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -561,23 +561,38 @@ struct nf_conn *nf_conntrack_alloc(struct net *net,
}
}
- ct = kmem_cache_zalloc(nf_conntrack_cachep, gfp);
+ /*
+ * Do not use kmem_cache_zalloc(), as this cache uses
+ * SLAB_DESTROY_BY_RCU.
+ */
+ ct = kmem_cache_alloc(nf_conntrack_cachep, gfp);
if (ct == NULL) {
pr_debug("nf_conntrack_alloc: Can't alloc conntrack.\n");
atomic_dec(&net->ct.count);
return ERR_PTR(-ENOMEM);
}
-
+ /*
+ * Let ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.next
+ * and ct->tuplehash[IP_CT_DIR_REPLY].hnnode.next unchanged.
+ */
+ memset(&ct->tuplehash[IP_CT_DIR_MAX], 0,
+ sizeof(*ct) - offsetof(struct nf_conn, tuplehash[IP_CT_DIR_MAX]));
spin_lock_init(&ct->lock);
- atomic_set(&ct->ct_general.use, 1);
ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple = *orig;
+ ct->tuplehash[IP_CT_DIR_ORIGINAL].hnnode.pprev = NULL;
ct->tuplehash[IP_CT_DIR_REPLY].tuple = *repl;
+ ct->tuplehash[IP_CT_DIR_REPLY].hnnode.pprev = NULL;
/* Don't set timer yet: wait for confirmation */
setup_timer(&ct->timeout, death_by_timeout, (unsigned long)ct);
#ifdef CONFIG_NET_NS
ct->ct_net = net;
#endif
+ /*
+ * changes to lookup keys must be done before setting refcnt to 1
+ */
+ smp_wmb();
+ atomic_set(&ct->ct_general.use, 1);
return ct;
}
EXPORT_SYMBOL_GPL(nf_conntrack_alloc);
diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c
index 863e409..0f482e2 100644
--- a/net/netfilter/xt_osf.c
+++ b/net/netfilter/xt_osf.c
@@ -330,7 +330,8 @@ static bool xt_osf_match_packet(const struct sk_buff *skb,
fcount++;
if (info->flags & XT_OSF_LOG)
- nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
+ nf_log_packet(p->family, p->hooknum, skb,
+ p->in, p->out, NULL,
"%s [%s:%s] : %pi4:%d -> %pi4:%d hops=%d\n",
f->genre, f->version, f->subtype,
&ip->saddr, ntohs(tcp->source),
@@ -345,7 +346,7 @@ static bool xt_osf_match_packet(const struct sk_buff *skb,
rcu_read_unlock();
if (!fcount && (info->flags & XT_OSF_LOG))
- nf_log_packet(p->hooknum, 0, skb, p->in, p->out, NULL,
+ nf_log_packet(p->family, p->hooknum, skb, p->in, p->out, NULL,
"Remote OS is not known: %pi4:%u -> %pi4:%u\n",
&ip->saddr, ntohs(tcp->source),
&ip->daddr, ntohs(tcp->dest));