Automatic merge of rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git branch HEAD
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 3a0a55b..7c5c1ac 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -1555,6 +1555,7 @@
 	tristate "SiS 900/7016 PCI Fast Ethernet Adapter support"
 	depends on NET_PCI && PCI
 	select CRC32
+	select MII
 	---help---
 	  This is a driver for the Fast Ethernet PCI network cards based on
 	  the SiS 900 and SiS 7016 chips. The SiS 900 core is also embedded in
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index cda48c5..4ebcd05 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -81,6 +81,7 @@
  *			   cause DMA to kfree'd memory.
  *	0.31: 14 Nov 2004: ethtool support for getting/setting link
  *	                   capabilities.
+ *	0.32: 16 Apr 2005: RX_ERROR4 handling added.
  *
  * Known bugs:
  * We suspect that on some hardware no TX done interrupts are generated.
@@ -92,7 +93,7 @@
  * DEV_NEED_TIMERIRQ will not harm you on sane hardware, only generating a few
  * superfluous timer interrupts from the nic.
  */
-#define FORCEDETH_VERSION		"0.31"
+#define FORCEDETH_VERSION		"0.32"
 #define DRV_NAME			"forcedeth"
 
 #include <linux/module.h>
@@ -109,6 +110,7 @@
 #include <linux/mii.h>
 #include <linux/random.h>
 #include <linux/init.h>
+#include <linux/if_vlan.h>
 
 #include <asm/irq.h>
 #include <asm/io.h>
@@ -1013,6 +1015,59 @@
 	spin_unlock_irq(&np->lock);
 }
 
+/*
+ * Called when the nic notices a mismatch between the actual data len on the
+ * wire and the len indicated in the 802 header
+ */
+static int nv_getlen(struct net_device *dev, void *packet, int datalen)
+{
+	int hdrlen;	/* length of the 802 header */
+	int protolen;	/* length as stored in the proto field */
+
+	/* 1) calculate len according to header */
+	if ( ((struct vlan_ethhdr *)packet)->h_vlan_proto == __constant_htons(ETH_P_8021Q)) {
+		protolen = ntohs( ((struct vlan_ethhdr *)packet)->h_vlan_encapsulated_proto );
+		hdrlen = VLAN_HLEN;
+	} else {
+		protolen = ntohs( ((struct ethhdr *)packet)->h_proto);
+		hdrlen = ETH_HLEN;
+	}
+	dprintk(KERN_DEBUG "%s: nv_getlen: datalen %d, protolen %d, hdrlen %d\n",
+				dev->name, datalen, protolen, hdrlen);
+	if (protolen > ETH_DATA_LEN)
+		return datalen; /* Value in proto field not a len, no checks possible */
+
+	protolen += hdrlen;
+	/* consistency checks: */
+	if (datalen > ETH_ZLEN) {
+		if (datalen >= protolen) {
+			/* more data on wire than in 802 header, trim of
+			 * additional data.
+			 */
+			dprintk(KERN_DEBUG "%s: nv_getlen: accepting %d bytes.\n",
+					dev->name, protolen);
+			return protolen;
+		} else {
+			/* less data on wire than mentioned in header.
+			 * Discard the packet.
+			 */
+			dprintk(KERN_DEBUG "%s: nv_getlen: discarding long packet.\n",
+					dev->name);
+			return -1;
+		}
+	} else {
+		/* short packet. Accept only if 802 values are also short */
+		if (protolen > ETH_ZLEN) {
+			dprintk(KERN_DEBUG "%s: nv_getlen: discarding short packet.\n",
+					dev->name);
+			return -1;
+		}
+		dprintk(KERN_DEBUG "%s: nv_getlen: accepting %d bytes.\n",
+				dev->name, datalen);
+		return datalen;
+	}
+}
+
 static void nv_rx_process(struct net_device *dev)
 {
 	struct fe_priv *np = get_nvpriv(dev);
@@ -1064,7 +1119,7 @@
 				np->stats.rx_errors++;
 				goto next_pkt;
 			}
-			if (Flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3|NV_RX_ERROR4)) {
+			if (Flags & (NV_RX_ERROR1|NV_RX_ERROR2|NV_RX_ERROR3)) {
 				np->stats.rx_errors++;
 				goto next_pkt;
 			}
@@ -1078,22 +1133,24 @@
 				np->stats.rx_errors++;
 				goto next_pkt;
 			}
-			if (Flags & NV_RX_ERROR) {
-				/* framing errors are soft errors, the rest is fatal. */
-				if (Flags & NV_RX_FRAMINGERR) {
-					if (Flags & NV_RX_SUBSTRACT1) {
-						len--;
-					}
-				} else {
+			if (Flags & NV_RX_ERROR4) {
+				len = nv_getlen(dev, np->rx_skbuff[i]->data, len);
+				if (len < 0) {
 					np->stats.rx_errors++;
 					goto next_pkt;
 				}
 			}
+			/* framing errors are soft errors. */
+			if (Flags & NV_RX_FRAMINGERR) {
+				if (Flags & NV_RX_SUBSTRACT1) {
+					len--;
+				}
+			}
 		} else {
 			if (!(Flags & NV_RX2_DESCRIPTORVALID))
 				goto next_pkt;
 
-			if (Flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3|NV_RX2_ERROR4)) {
+			if (Flags & (NV_RX2_ERROR1|NV_RX2_ERROR2|NV_RX2_ERROR3)) {
 				np->stats.rx_errors++;
 				goto next_pkt;
 			}
@@ -1107,17 +1164,19 @@
 				np->stats.rx_errors++;
 				goto next_pkt;
 			}
-			if (Flags & NV_RX2_ERROR) {
-				/* framing errors are soft errors, the rest is fatal. */
-				if (Flags & NV_RX2_FRAMINGERR) {
-					if (Flags & NV_RX2_SUBSTRACT1) {
-						len--;
-					}
-				} else {
+			if (Flags & NV_RX2_ERROR4) {
+				len = nv_getlen(dev, np->rx_skbuff[i]->data, len);
+				if (len < 0) {
 					np->stats.rx_errors++;
 					goto next_pkt;
 				}
 			}
+			/* framing errors are soft errors */
+			if (Flags & NV_RX2_FRAMINGERR) {
+				if (Flags & NV_RX2_SUBSTRACT1) {
+					len--;
+				}
+			}
 			Flags &= NV_RX2_CHECKSUMMASK;
 			if (Flags == NV_RX2_CHECKSUMOK1 ||
 					Flags == NV_RX2_CHECKSUMOK2 ||
@@ -1480,6 +1539,13 @@
 	enable_irq(dev->irq);
 }
 
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void nv_poll_controller(struct net_device *dev)
+{
+	nv_do_nic_poll((unsigned long) dev);
+}
+#endif
+
 static void nv_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
 {
 	struct fe_priv *np = get_nvpriv(dev);
@@ -1962,6 +2028,9 @@
 	dev->get_stats = nv_get_stats;
 	dev->change_mtu = nv_change_mtu;
 	dev->set_multicast_list = nv_set_multicast;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+	dev->poll_controller = nv_poll_controller;
+#endif
 	SET_ETHTOOL_OPS(dev, &ops);
 	dev->tx_timeout = nv_tx_timeout;
 	dev->watchdog_timeo = NV_WATCHDOG_TIMEO;
diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c
index 855f8b2..13ed8dc 100644
--- a/drivers/net/iseries_veth.c
+++ b/drivers/net/iseries_veth.c
@@ -924,7 +924,7 @@
 
 	spin_lock_irqsave(&cnx->lock, flags);
 
-	if (! cnx->state & VETH_STATE_READY)
+	if (! (cnx->state & VETH_STATE_READY))
 		goto drop;
 
 	if ((skb->len - 14) > VETH_MAX_MTU)
@@ -1023,6 +1023,8 @@
 
 	lpmask = veth_transmit_to_many(skb, lpmask, dev);
 
+	dev->trans_start = jiffies;
+
 	if (! lpmask) {
 		dev_kfree_skb(skb);
 	} else {
@@ -1262,13 +1264,18 @@
 
 		vlan = skb->data[9];
 		dev = veth_dev[vlan];
-		if (! dev)
-			/* Some earlier versions of the driver sent
-			   broadcasts down all connections, even to
-			   lpars that weren't on the relevant vlan.
-			   So ignore packets belonging to a vlan we're
-			   not on. */
+		if (! dev) {
+			/*
+			 * Some earlier versions of the driver sent
+			 * broadcasts down all connections, even to lpars
+			 * that weren't on the relevant vlan. So ignore
+			 * packets belonging to a vlan we're not on.
+			 * We can also be here if we receive packets while
+			 * the driver is going down, because then dev is NULL.
+			 */
+			dev_kfree_skb_irq(skb);
 			continue;
+		}
 
 		port = (struct veth_port *)dev->priv;
 		dest = *((u64 *) skb->data) & 0xFFFFFFFFFFFF0000;
@@ -1381,18 +1388,25 @@
 {
 	int i;
 
-	vio_unregister_driver(&veth_driver);
+	/* Stop the queues first to stop any new packets being sent. */
+	for (i = 0; i < HVMAXARCHITECTEDVIRTUALLANS; i++)
+		if (veth_dev[i])
+			netif_stop_queue(veth_dev[i]);
 
+	/* Stop the connections before we unregister the driver. This
+	 * ensures there's no skbs lying around holding the device open. */
 	for (i = 0; i < HVMAXARCHITECTEDLPS; ++i)
 		veth_stop_connection(i);
 
 	HvLpEvent_unregisterHandler(HvLpEvent_Type_VirtualLan);
 
 	/* Hypervisor callbacks may have scheduled more work while we
-	 * were destroying connections. Now that we've disconnected from
+	 * were stoping connections. Now that we've disconnected from
 	 * the hypervisor make sure everything's finished. */
 	flush_scheduled_work();
 
+	vio_unregister_driver(&veth_driver);
+
 	for (i = 0; i < HVMAXARCHITECTEDLPS; ++i)
 		veth_destroy_connection(i);
 
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
index 223bdad..babb59e 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/natsemi.c
@@ -2433,9 +2433,9 @@
 		rx_mode = RxFilterEnable | AcceptBroadcast
 			| AcceptMulticast | AcceptMyPhys;
 		for (i = 0; i < 64; i += 2) {
-			writew(HASH_TABLE + i, ioaddr + RxFilterAddr);
-			writew((mc_filter[i+1]<<8) + mc_filter[i],
-				ioaddr + RxFilterData);
+			writel(HASH_TABLE + i, ioaddr + RxFilterAddr);
+			writel((mc_filter[i + 1] << 8) + mc_filter[i],
+			       ioaddr + RxFilterData);
 		}
 	}
 	writel(rx_mode, ioaddr + RxFilterAddr);
diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c
index 2fcc181..c336b46 100644
--- a/drivers/net/ns83820.c
+++ b/drivers/net/ns83820.c
@@ -1,4 +1,4 @@
-#define _VERSION "0.20"
+#define VERSION "0.22"
 /* ns83820.c by Benjamin LaHaise with contributions.
  *
  * Questions/comments/discussion to linux-ns83820@kvack.org.
@@ -63,9 +63,11 @@
  *			     -	fix missed txok introduced during performance
  *				tuning
  *			0.20 -	fix stupid RFEN thinko.  i am such a smurf.
- *
  *	20040828	0.21 -	add hardware vlan accleration
  *				by Neil Horman <nhorman@redhat.com>
+ *	20050406	0.22 -	improved DAC ifdefs from Andi Kleen	
+ *			     -	removal of dead code from Adrian Bunk
+ *			     -	fix half duplex collision behaviour
  * Driver Overview
  * ===============
  *
@@ -129,18 +131,6 @@
 #undef Dprintk
 #define	Dprintk			dprintk
 
-#if defined(CONFIG_HIGHMEM64G) || defined(__ia64__)
-#define USE_64BIT_ADDR	"+"
-#endif
-
-#if defined(USE_64BIT_ADDR)
-#define	VERSION	_VERSION USE_64BIT_ADDR
-#define TRY_DAC	1
-#else
-#define	VERSION	_VERSION
-#define TRY_DAC	0
-#endif
-
 /* tunables */
 #define RX_BUF_SIZE	1500	/* 8192 */
 #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
@@ -386,22 +376,16 @@
 #define LINK_DOWN		0x02
 #define LINK_UP			0x04
 
-#ifdef USE_64BIT_ADDR
-#define HW_ADDR_LEN	8
+#define HW_ADDR_LEN	sizeof(dma_addr_t) 
 #define desc_addr_set(desc, addr)				\
 	do {							\
-		u64 __addr = (addr);				\
-		(desc)[0] = cpu_to_le32(__addr);		\
-		(desc)[1] = cpu_to_le32(__addr >> 32);		\
+		((desc)[0] = cpu_to_le32(addr));		\
+		if (HW_ADDR_LEN == 8)		 		\
+			(desc)[1] = cpu_to_le32(((u64)addr) >> 32);	\
 	} while(0)
 #define desc_addr_get(desc)					\
-		(((u64)le32_to_cpu((desc)[1]) << 32)		\
-		     | le32_to_cpu((desc)[0]))
-#else
-#define HW_ADDR_LEN	4
-#define desc_addr_set(desc, addr)	((desc)[0] = cpu_to_le32(addr))
-#define desc_addr_get(desc)		(le32_to_cpu((desc)[0]))
-#endif
+	(le32_to_cpu((desc)[0]) | \
+	(HW_ADDR_LEN == 8 ? ((dma_addr_t)le32_to_cpu((desc)[1]))<<32 : 0))
 
 #define DESC_LINK		0
 #define DESC_BUFPTR		(DESC_LINK + HW_ADDR_LEN/4)
@@ -727,11 +711,23 @@
 		speed = ((cfg / CFG_SPDSTS0) & 3);
 		fullduplex = (cfg & CFG_DUPSTS);
 
-		if (fullduplex)
+		if (fullduplex) {
 			new_cfg |= CFG_SB;
+			writel(readl(dev->base + TXCFG)
+					| TXCFG_CSI | TXCFG_HBI,
+			       dev->base + TXCFG);
+			writel(readl(dev->base + RXCFG) | RXCFG_RX_FD,
+			       dev->base + RXCFG);
+		} else {
+			writel(readl(dev->base + TXCFG)
+					& ~(TXCFG_CSI | TXCFG_HBI),
+			       dev->base + TXCFG);
+			writel(readl(dev->base + RXCFG) & ~(RXCFG_RX_FD),
+			       dev->base + RXCFG);
+		}
 
 		if ((cfg & CFG_LNKSTS) &&
-		    ((new_cfg ^ dev->CFG_cache) & CFG_MODE_1000)) {
+		    ((new_cfg ^ dev->CFG_cache) != 0)) {
 			writel(new_cfg, dev->base + CFG);
 			dev->CFG_cache = new_cfg;
 		}
@@ -1189,7 +1185,6 @@
 
 	for (;;) {
 		volatile u32 *desc = dev->tx_descs + (free_idx * DESC_SIZE);
-		u32 residue = 0;
 
 		dprintk("frag[%3u]: %4u @ 0x%08Lx\n", free_idx, len,
 			(unsigned long long)buf);
@@ -1199,17 +1194,11 @@
 		desc_addr_set(desc + DESC_BUFPTR, buf);
 		desc[DESC_EXTSTS] = cpu_to_le32(extsts);
 
-		cmdsts = ((nr_frags|residue) ? CMDSTS_MORE : do_intr ? CMDSTS_INTR : 0);
+		cmdsts = ((nr_frags) ? CMDSTS_MORE : do_intr ? CMDSTS_INTR : 0);
 		cmdsts |= (desc == first_desc) ? 0 : CMDSTS_OWN;
 		cmdsts |= len;
 		desc[DESC_CMDSTS] = cpu_to_le32(cmdsts);
 
-		if (residue) {
-			buf += len;
-			len = residue;
-			continue;
-		}
-
 		if (!nr_frags)
 			break;
 
@@ -1841,7 +1830,8 @@
 	int using_dac = 0;
 
 	/* See if we can set the dma mask early on; failure is fatal. */
-	if (TRY_DAC && !pci_set_dma_mask(pci_dev, 0xffffffffffffffffULL)) {
+	if (sizeof(dma_addr_t) == 8 && 
+	 	!pci_set_dma_mask(pci_dev, 0xffffffffffffffffULL)) {
 		using_dac = 1;
 	} else if (!pci_set_dma_mask(pci_dev, 0xffffffff)) {
 		using_dac = 0;
@@ -1972,9 +1962,8 @@
 	/* When compiled with 64 bit addressing, we must always enable
 	 * the 64 bit descriptor format.
 	 */
-#ifdef USE_64BIT_ADDR
-	dev->CFG_cache |= CFG_M64ADDR;
-#endif
+	if (sizeof(dma_addr_t) == 8) 
+		dev->CFG_cache |= CFG_M64ADDR;
 	if (using_dac)
 		dev->CFG_cache |= CFG_T64ADDR;
 
diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c
index 3e9d9aa..3107aed 100644
--- a/drivers/net/sis900.c
+++ b/drivers/net/sis900.c
@@ -162,6 +162,7 @@
 	struct mii_phy * mii;
 	struct mii_phy * first_mii; /* record the first mii structure */
 	unsigned int cur_phy;
+	struct mii_if_info mii_info;
 
 	struct timer_list timer; /* Link status detection timer. */
 	u8 autong_complete; /* 1: auto-negotiate complete  */
@@ -203,7 +204,7 @@
 static int sis900_mii_probe (struct net_device * net_dev);
 static void sis900_init_rxfilter (struct net_device * net_dev);
 static u16 read_eeprom(long ioaddr, int location);
-static u16 mdio_read(struct net_device *net_dev, int phy_id, int location);
+static int mdio_read(struct net_device *net_dev, int phy_id, int location);
 static void mdio_write(struct net_device *net_dev, int phy_id, int location, int val);
 static void sis900_timer(unsigned long data);
 static void sis900_check_mode (struct net_device *net_dev, struct mii_phy *mii_phy);
@@ -478,7 +479,13 @@
 		sis_priv->msg_enable = sis900_debug;
 	else
 		sis_priv->msg_enable = SIS900_DEF_MSG;
-		
+
+	sis_priv->mii_info.dev = net_dev;
+	sis_priv->mii_info.mdio_read = mdio_read;
+	sis_priv->mii_info.mdio_write = mdio_write;
+	sis_priv->mii_info.phy_id_mask = 0x1f;
+	sis_priv->mii_info.reg_num_mask = 0x1f;
+
 	/* Get Mac address according to the chip revision */
 	pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &(sis_priv->chipset_rev));
 	if(netif_msg_probe(sis_priv))
@@ -725,6 +732,8 @@
 		       pci_name(sis_priv->pci_dev), sis_priv->cur_phy);
 	}
 	
+	sis_priv->mii_info.phy_id = sis_priv->cur_phy;
+
 	status = mdio_read(net_dev, sis_priv->cur_phy, MII_CONTROL);
 	status &= (~MII_CNTL_ISOLATE);
 
@@ -852,7 +861,7 @@
  *	Please see SiS7014 or ICS spec
  */
 
-static u16 mdio_read(struct net_device *net_dev, int phy_id, int location)
+static int mdio_read(struct net_device *net_dev, int phy_id, int location)
 {
 	long mdio_addr = net_dev->base_addr + mear;
 	int mii_cmd = MIIread|(phy_id<<MIIpmdShift)|(location<<MIIregShift);
@@ -1966,10 +1975,47 @@
 	sis_priv->msg_enable = value;
 }
 
+static u32 sis900_get_link(struct net_device *net_dev)
+{
+	struct sis900_private *sis_priv = net_dev->priv;
+	return mii_link_ok(&sis_priv->mii_info);
+}
+
+static int sis900_get_settings(struct net_device *net_dev,
+				struct ethtool_cmd *cmd)
+{
+	struct sis900_private *sis_priv = net_dev->priv;
+	spin_lock_irq(&sis_priv->lock);
+	mii_ethtool_gset(&sis_priv->mii_info, cmd);
+	spin_unlock_irq(&sis_priv->lock);
+	return 0;
+}
+
+static int sis900_set_settings(struct net_device *net_dev,
+				struct ethtool_cmd *cmd)
+{
+	struct sis900_private *sis_priv = net_dev->priv;
+	int rt;
+	spin_lock_irq(&sis_priv->lock);
+	rt = mii_ethtool_sset(&sis_priv->mii_info, cmd);
+	spin_unlock_irq(&sis_priv->lock);
+	return rt;
+}
+
+static int sis900_nway_reset(struct net_device *net_dev)
+{
+	struct sis900_private *sis_priv = net_dev->priv;
+	return mii_nway_restart(&sis_priv->mii_info);
+}
+
 static struct ethtool_ops sis900_ethtool_ops = {
 	.get_drvinfo 	= sis900_get_drvinfo,
 	.get_msglevel	= sis900_get_msglevel,
 	.set_msglevel	= sis900_set_msglevel,
+	.get_link	= sis900_get_link,
+	.get_settings	= sis900_get_settings,
+	.set_settings	= sis900_set_settings,
+	.nway_reset	= sis900_nway_reset,
 };
 
 /**
diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c
index a7ffa64..9680a30 100644
--- a/drivers/net/tlan.c
+++ b/drivers/net/tlan.c
@@ -193,6 +193,12 @@
 static  int duplex[MAX_TLAN_BOARDS];
 static  int speed[MAX_TLAN_BOARDS];
 static  int boards_found;
+module_param_array(aui, int, NULL, 0);
+module_param_array(duplex, int, NULL, 0);
+module_param_array(speed, int, NULL, 0);
+MODULE_PARM_DESC(aui, "ThunderLAN use AUI port(s) (0-1)");
+MODULE_PARM_DESC(duplex, "ThunderLAN duplex setting(s) (0-default, 1-half, 2-full)");
+MODULE_PARM_DESC(speed, "ThunderLAN port speen setting(s) (0,10,100)");
 
 MODULE_AUTHOR("Maintainer: Samuel Chessman <chessman@tux.org>");
 MODULE_DESCRIPTION("Driver for TI ThunderLAN based ethernet PCI adapters");
@@ -204,8 +210,13 @@
 
 /* Turn on debugging. See Documentation/networking/tlan.txt for details */
 static  int		debug;
+module_param(debug, int, 0);
+MODULE_PARM_DESC(debug, "ThunderLAN debug mask");
 
 static	int		bbuf;
+module_param(bbuf, int, 0);
+MODULE_PARM_DESC(bbuf, "ThunderLAN use big buffer (0-1)");
+
 static	u8		*TLanPadBuffer;
 static  dma_addr_t	TLanPadBufferDMA;
 static	char		TLanSignature[] = "TLAN";
@@ -2381,6 +2392,7 @@
 		TLan_SetTimer( dev, (10*HZ), TLAN_TIMER_FINISH_RESET );
 		return;
 	}
+	TLan_SetMulticastList(dev);
 
 } /* TLan_FinishReset */
 
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index 463c789..fb10a2d 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -754,7 +754,7 @@
   u8 zero;
   u8 ssidLen;
   u8 ssid[32];
-  u16 rssi;
+  u16 dBm;
 #define CAP_ESS (1<<0)
 #define CAP_IBSS (1<<1)
 #define CAP_PRIVACY (1<<4)
@@ -1125,6 +1125,9 @@
 static int encapsulate(struct airo_info *ai, etherHead *pPacket, MICBuffer *buffer, int len);
 static int decapsulate(struct airo_info *ai, MICBuffer *mic, etherHead *pPacket, u16 payLen);
 
+static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi);
+static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm);
+
 #include <linux/crypto.h>
 #endif
 
@@ -1713,6 +1716,7 @@
 	list->fh.dwell = le16_to_cpu(list->fh.dwell);
 	list->dsChannel = le16_to_cpu(list->dsChannel);
 	list->atimWindow = le16_to_cpu(list->atimWindow);
+	list->dBm = le16_to_cpu(list->dBm);
 	return rc;
 }
 
@@ -3245,7 +3249,10 @@
 					wstats.level = 0x100 - apriv->rssi[hdr.rssi[1]].rssidBm;
 				else
 					wstats.level = (hdr.rssi[1] + 321) / 2;
-				wstats.updated = 3;	
+				wstats.noise = apriv->wstats.qual.noise;
+				wstats.updated = IW_QUAL_LEVEL_UPDATED
+					| IW_QUAL_QUAL_UPDATED
+					| IW_QUAL_NOISE_UPDATED;
 				/* Update spy records */
 				wireless_spy_update(dev, sa, &wstats);
 			}
@@ -3588,7 +3595,10 @@
 			wstats.level = 0x100 - ai->rssi[hdr.rssi[1]].rssidBm;
 		else
 			wstats.level = (hdr.rssi[1] + 321) / 2;
-		wstats.updated = 3;
+		wstats.noise = ai->wstats.qual.noise;
+		wstats.updated = IW_QUAL_QUAL_UPDATED
+			| IW_QUAL_LEVEL_UPDATED
+			| IW_QUAL_NOISE_UPDATED;
 		/* Update spy records */
 		wireless_spy_update(ai->dev, sa, &wstats);
 	}
@@ -3679,7 +3689,7 @@
 		status = PC4500_readrid(ai,RID_RSSI,&rssi_rid,sizeof(rssi_rid),lock);
 		if ( status == SUCCESS ) {
 			if (ai->rssi || (ai->rssi = kmalloc(512, GFP_KERNEL)) != NULL)
-				memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512);
+				memcpy(ai->rssi, (u8*)&rssi_rid + 2, 512); /* Skip RID length member */
 		}
 		else {
 			if (ai->rssi) {
@@ -5348,7 +5358,7 @@
 				(int)BSSList_rid.bssid[5],
 				(int)BSSList_rid.ssidLen,
 				BSSList_rid.ssid,
-				(int)BSSList_rid.rssi);
+				(int)BSSList_rid.dBm);
 		ptr += sprintf(ptr, " channel = %d %s %s %s %s\n",
 				(int)BSSList_rid.dsChannel,
 				BSSList_rid.cap & CAP_ESS ? "ESS" : "",
@@ -5593,6 +5603,29 @@
  * would not work at all... - Jean II
  */
 
+static u8 airo_rssi_to_dbm (tdsRssiEntry *rssi_rid, u8 rssi)
+{
+	if( !rssi_rid )
+		return 0;
+
+	return (0x100 - rssi_rid[rssi].rssidBm);
+}
+
+static u8 airo_dbm_to_pct (tdsRssiEntry *rssi_rid, u8 dbm)
+{
+	int i;
+
+	if( !rssi_rid )
+		return 0;
+
+	for( i = 0; i < 256; i++ )
+		if (rssi_rid[i].rssidBm == dbm)
+			return rssi_rid[i].rssipct;
+
+	return 0;
+}
+
+
 static int airo_get_quality (StatusRid *status_rid, CapabilityRid *cap_rid)
 {
 	int quality = 0;
@@ -6443,12 +6476,30 @@
 	}
 	range->num_frequency = k;
 
-	/* Hum... Should put the right values there */
-	range->max_qual.qual = airo_get_max_quality(&cap_rid);
-	range->max_qual.level = 0x100 - 120;	/* -120 dBm */
-	range->max_qual.noise = 0;
 	range->sensitivity = 65535;
 
+	/* Hum... Should put the right values there */
+	if (local->rssi)
+		range->max_qual.qual = 100;	/* % */
+	else
+		range->max_qual.qual = airo_get_max_quality(&cap_rid);
+	range->max_qual.level = 0;	/* 0 means we use dBm  */
+	range->max_qual.noise = 0;
+	range->max_qual.updated = 0;
+
+	/* Experimental measurements - boundary 11/5.5 Mb/s */
+	/* Note : with or without the (local->rssi), results
+	 * are somewhat different. - Jean II */
+	if (local->rssi) {
+		range->avg_qual.qual = 50;	/* % */
+		range->avg_qual.level = 186;	/* -70 dBm */
+	} else {
+		range->avg_qual.qual = airo_get_avg_quality(&cap_rid);
+		range->avg_qual.level = 176;	/* -80 dBm */
+	}
+	range->avg_qual.noise = 0;
+	range->avg_qual.updated = 0;
+
 	for(i = 0 ; i < 8 ; i++) {
 		range->bitrate[i] = cap_rid.supportedRates[i] * 500000;
 		if(range->bitrate[i] == 0)
@@ -6508,15 +6559,6 @@
 	range->max_retry = 65535;
 	range->min_r_time = 1024;
 	range->max_r_time = 65535 * 1024;
-	/* Experimental measurements - boundary 11/5.5 Mb/s */
-	/* Note : with or without the (local->rssi), results
-	 * are somewhat different. - Jean II */
-	range->avg_qual.qual = airo_get_avg_quality(&cap_rid);
-	if (local->rssi)
-		range->avg_qual.level = 186;	/* -70 dBm */
-	else
-		range->avg_qual.level = 176;	/* -80 dBm */
-	range->avg_qual.noise = 0;
 
 	/* Event capability (kernel + driver) */
 	range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
@@ -6676,12 +6718,18 @@
 		loseSync = 0;
 		memcpy(address[i].sa_data, BSSList.bssid, ETH_ALEN);
 		address[i].sa_family = ARPHRD_ETHER;
-		if (local->rssi)
-			qual[i].level = 0x100 - local->rssi[BSSList.rssi].rssidBm;
-		else
-			qual[i].level = (BSSList.rssi + 321) / 2;
-		qual[i].qual = qual[i].noise = 0;
-		qual[i].updated = 2;
+		if (local->rssi) {
+			qual[i].level = 0x100 - BSSList.dBm;
+			qual[i].qual = airo_dbm_to_pct( local->rssi, BSSList.dBm );
+			qual[i].updated = IW_QUAL_QUAL_UPDATED;
+		} else {
+			qual[i].level = (BSSList.dBm + 321) / 2;
+			qual[i].qual = 0;
+			qual[i].updated = IW_QUAL_QUAL_INVALID;
+		}
+		qual[i].noise = local->wstats.qual.noise;
+		qual[i].updated = IW_QUAL_LEVEL_UPDATED
+				| IW_QUAL_NOISE_UPDATED;
 		if (BSSList.index == 0xffff)
 			break;
 	}
@@ -6760,7 +6808,7 @@
 static inline char *airo_translate_scan(struct net_device *dev,
 					char *current_ev,
 					char *end_buf,
-					BSSListRid *list)
+					BSSListRid *bss)
 {
 	struct airo_info *ai = dev->priv;
 	struct iw_event		iwe;		/* Temporary buffer */
@@ -6771,22 +6819,22 @@
 	/* First entry *MUST* be the AP MAC address */
 	iwe.cmd = SIOCGIWAP;
 	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
-	memcpy(iwe.u.ap_addr.sa_data, list->bssid, ETH_ALEN);
+	memcpy(iwe.u.ap_addr.sa_data, bss->bssid, ETH_ALEN);
 	current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
 
 	/* Other entries will be displayed in the order we give them */
 
 	/* Add the ESSID */
-	iwe.u.data.length = list->ssidLen;
+	iwe.u.data.length = bss->ssidLen;
 	if(iwe.u.data.length > 32)
 		iwe.u.data.length = 32;
 	iwe.cmd = SIOCGIWESSID;
 	iwe.u.data.flags = 1;
-	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, list->ssid);
+	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid);
 
 	/* Add mode */
 	iwe.cmd = SIOCGIWMODE;
-	capabilities = le16_to_cpu(list->cap);
+	capabilities = le16_to_cpu(bss->cap);
 	if(capabilities & (CAP_ESS | CAP_IBSS)) {
 		if(capabilities & CAP_ESS)
 			iwe.u.mode = IW_MODE_MASTER;
@@ -6797,19 +6845,25 @@
 
 	/* Add frequency */
 	iwe.cmd = SIOCGIWFREQ;
-	iwe.u.freq.m = le16_to_cpu(list->dsChannel);
+	iwe.u.freq.m = le16_to_cpu(bss->dsChannel);
 	iwe.u.freq.m = frequency_list[iwe.u.freq.m] * 100000;
 	iwe.u.freq.e = 1;
 	current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
 
 	/* Add quality statistics */
 	iwe.cmd = IWEVQUAL;
-	if (ai->rssi)
-		iwe.u.qual.level = 0x100 - ai->rssi[list->rssi].rssidBm;
-	else
-		iwe.u.qual.level = (list->rssi + 321) / 2;
-	iwe.u.qual.noise = 0;
-	iwe.u.qual.qual = 0;
+	if (ai->rssi) {
+		iwe.u.qual.level = 0x100 - bss->dBm;
+		iwe.u.qual.qual = airo_dbm_to_pct( ai->rssi, bss->dBm );
+		iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED;
+	} else {
+		iwe.u.qual.level = (bss->dBm + 321) / 2;
+		iwe.u.qual.qual = 0;
+		iwe.u.qual.updated = IW_QUAL_QUAL_INVALID;
+	}
+	iwe.u.qual.noise = ai->wstats.qual.noise;
+	iwe.u.qual.updated = IW_QUAL_LEVEL_UPDATED
+			| IW_QUAL_NOISE_UPDATED;
 	current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
 
 	/* Add encryption capability */
@@ -6819,7 +6873,7 @@
 	else
 		iwe.u.data.flags = IW_ENCODE_DISABLED;
 	iwe.u.data.length = 0;
-	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, list->ssid);
+	current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss->ssid);
 
 	/* Rate : stuffing multiple values in a single event require a bit
 	 * more of magic - Jean II */
@@ -6831,10 +6885,10 @@
 	/* Max 8 values */
 	for(i = 0 ; i < 8 ; i++) {
 		/* NULL terminated */
-		if(list->rates[i] == 0)
+		if(bss->rates[i] == 0)
 			break;
 		/* Bit rate given in 500 kb/s units (+ 0x80) */
-		iwe.u.bitrate.value = ((list->rates[i] & 0x7f) * 500000);
+		iwe.u.bitrate.value = ((bss->rates[i] & 0x7f) * 500000);
 		/* Add new value to event */
 		current_val = iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN);
 	}
@@ -7153,18 +7207,22 @@
 	/* The status */
 	local->wstats.status = status_rid.mode;
 
-	/* Signal quality and co. But where is the noise level ??? */
-	local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid);
-	if (local->rssi)
-		local->wstats.qual.level = 0x100 - local->rssi[status_rid.sigQuality].rssidBm;
-	else
+	/* Signal quality and co */
+	if (local->rssi) {
+		local->wstats.qual.level = airo_rssi_to_dbm( local->rssi, status_rid.sigQuality );
+		/* normalizedSignalStrength appears to be a percentage */
+		local->wstats.qual.qual = status_rid.normalizedSignalStrength;
+	} else {
 		local->wstats.qual.level = (status_rid.normalizedSignalStrength + 321) / 2;
+		local->wstats.qual.qual = airo_get_quality(&status_rid, &cap_rid);
+	}
+	local->wstats.qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED;
 	if (status_rid.len >= 124) {
-		local->wstats.qual.noise = 256 - status_rid.noisedBm;
-		local->wstats.qual.updated = 7;
+		local->wstats.qual.noise = 0x100 - status_rid.noisedBm;
+		local->wstats.qual.updated |= IW_QUAL_NOISE_UPDATED;
 	} else {
 		local->wstats.qual.noise = 0;
-		local->wstats.qual.updated = 3;
+		local->wstats.qual.updated |= IW_QUAL_NOISE_INVALID;
 	}
 
 	/* Packets discarded in the wireless adapter due to wireless
diff --git a/drivers/net/wireless/atmel_cs.c b/drivers/net/wireless/atmel_cs.c
index a91b507..a4ed28d 100644
--- a/drivers/net/wireless/atmel_cs.c
+++ b/drivers/net/wireless/atmel_cs.c
@@ -321,6 +321,7 @@
 	{ 0x01bf, 0x3302, NULL, ATMEL_FW_TYPE_502E, "Belkin F5D6020-V2" }, 
 	{ 0, 0, "BT/Voyager 1020 Laptop Adapter", ATMEL_FW_TYPE_502, "BT Voyager 1020" },
 	{ 0, 0, "IEEE 802.11b/Wireless LAN PC Card", ATMEL_FW_TYPE_502, "Siemens Gigaset PC Card II" },
+	{ 0, 0, "IEEE 802.11b/Wireless LAN Card S", ATMEL_FW_TYPE_504_2958, "Siemens Gigaset PC Card II" },
 	{ 0, 0, "CNet/CNWLC 11Mbps Wireless PC Card V-5", ATMEL_FW_TYPE_502E, "CNet CNWLC-811ARL" },
 	{ 0, 0, "Wireless/PC_CARD", ATMEL_FW_TYPE_502D, "Planet WL-3552" },
 	{ 0, 0, "OEM/11Mbps Wireless LAN PC Card V-3", ATMEL_FW_TYPE_502, "OEM 11Mbps WLAN PCMCIA Card" },
diff --git a/drivers/s390/net/Makefile b/drivers/s390/net/Makefile
index 7cabb80..90d4d0e 100644
--- a/drivers/s390/net/Makefile
+++ b/drivers/s390/net/Makefile
@@ -9,6 +9,7 @@
 obj-$(CONFIG_SMSGIUCV) += smsgiucv.o
 obj-$(CONFIG_CTC) += ctc.o fsm.o cu3088.o
 obj-$(CONFIG_LCS) += lcs.o cu3088.o
-qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o qeth_eddp.o qeth_tso.o
+obj-$(CONFIG_CLAW) += claw.o cu3088.o
+qeth-y := qeth_main.o qeth_mpc.o qeth_sys.o qeth_eddp.o 
 qeth-$(CONFIG_PROC_FS) += qeth_proc.o
 obj-$(CONFIG_QETH) += qeth.o
diff --git a/drivers/s390/net/ctcdbug.h b/drivers/s390/net/ctcdbug.h
index ef88839..7fe2ebd 100644
--- a/drivers/s390/net/ctcdbug.h
+++ b/drivers/s390/net/ctcdbug.h
@@ -1,6 +1,6 @@
 /*
  *
- * linux/drivers/s390/net/ctcdbug.h ($Revision: 1.4 $)
+ * linux/drivers/s390/net/ctcdbug.h ($Revision: 1.5 $)
  *
  * CTC / ESCON network driver - s390 dbf exploit.
  *
@@ -9,7 +9,7 @@
  *    Author(s): Original Code written by
  *			  Peter Tiedemann (ptiedem@de.ibm.com)
  *
- *    $Revision: 1.4 $	 $Date: 2004/10/15 09:26:58 $
+ *    $Revision: 1.5 $	 $Date: 2005/02/27 19:46:44 $
  *
  * 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
@@ -25,9 +25,11 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-
+#ifndef _CTCDBUG_H_
+#define _CTCDBUG_H_
 
 #include <asm/debug.h>
+#include "ctcmain.h"
 /**
  * Debug Facility stuff
  */
@@ -41,7 +43,7 @@
 #define CTC_DBF_DATA_LEN 128
 #define CTC_DBF_DATA_INDEX 3
 #define CTC_DBF_DATA_NR_AREAS 1
-#define CTC_DBF_DATA_LEVEL 2
+#define CTC_DBF_DATA_LEVEL 3
 
 #define CTC_DBF_TRACE_NAME "ctc_trace"
 #define CTC_DBF_TRACE_LEN 16
@@ -121,3 +123,5 @@
 	printk("\n");
 }
 
+
+#endif
diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c
index 7266bf5..ff3e95e 100644
--- a/drivers/s390/net/ctcmain.c
+++ b/drivers/s390/net/ctcmain.c
@@ -1,5 +1,5 @@
 /*
- * $Id: ctcmain.c,v 1.72 2005/03/17 10:51:52 ptiedem Exp $
+ * $Id: ctcmain.c,v 1.74 2005/03/24 09:04:17 mschwide Exp $
  *
  * CTC / ESCON network driver
  *
@@ -37,12 +37,11 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.72 $
+ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.74 $
  *
  */
 
 #undef DEBUG
-
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -74,288 +73,13 @@
 #include "ctctty.h"
 #include "fsm.h"
 #include "cu3088.h"
+
 #include "ctcdbug.h"
+#include "ctcmain.h"
 
 MODULE_AUTHOR("(C) 2000 IBM Corp. by Fritz Elfert (felfert@millenux.com)");
 MODULE_DESCRIPTION("Linux for S/390 CTC/Escon Driver");
 MODULE_LICENSE("GPL");
-
-/**
- * CCW commands, used in this driver.
- */
-#define CCW_CMD_WRITE		0x01
-#define CCW_CMD_READ		0x02
-#define CCW_CMD_SET_EXTENDED	0xc3
-#define CCW_CMD_PREPARE		0xe3
-
-#define CTC_PROTO_S390          0
-#define CTC_PROTO_LINUX         1
-#define CTC_PROTO_LINUX_TTY     2
-#define CTC_PROTO_OS390         3
-#define CTC_PROTO_MAX           3
-
-#define CTC_BUFSIZE_LIMIT       65535
-#define CTC_BUFSIZE_DEFAULT     32768
-
-#define CTC_TIMEOUT_5SEC        5000
-
-#define CTC_INITIAL_BLOCKLEN    2
-
-#define READ			0
-#define WRITE			1
-
-#define CTC_ID_SIZE             BUS_ID_SIZE+3
-
-
-struct ctc_profile {
-	unsigned long maxmulti;
-	unsigned long maxcqueue;
-	unsigned long doios_single;
-	unsigned long doios_multi;
-	unsigned long txlen;
-	unsigned long tx_time;
-	struct timespec send_stamp;
-};
-
-/**
- * Definition of one channel
- */
-struct channel {
-
-	/**
-	 * Pointer to next channel in list.
-	 */
-	struct channel *next;
-	char id[CTC_ID_SIZE];
-	struct ccw_device *cdev;
-
-	/**
-	 * Type of this channel.
-	 * CTC/A or Escon for valid channels.
-	 */
-	enum channel_types type;
-
-	/**
-	 * Misc. flags. See CHANNEL_FLAGS_... below
-	 */
-	__u32 flags;
-
-	/**
-	 * The protocol of this channel
-	 */
-	__u16 protocol;
-
-	/**
-	 * I/O and irq related stuff
-	 */
-	struct ccw1 *ccw;
-	struct irb *irb;
-
-	/**
-	 * RX/TX buffer size
-	 */
-	int max_bufsize;
-
-	/**
-	 * Transmit/Receive buffer.
-	 */
-	struct sk_buff *trans_skb;
-
-	/**
-	 * Universal I/O queue.
-	 */
-	struct sk_buff_head io_queue;
-
-	/**
-	 * TX queue for collecting skb's during busy.
-	 */
-	struct sk_buff_head collect_queue;
-
-	/**
-	 * Amount of data in collect_queue.
-	 */
-	int collect_len;
-
-	/**
-	 * spinlock for collect_queue and collect_len
-	 */
-	spinlock_t collect_lock;
-
-	/**
-	 * Timer for detecting unresposive
-	 * I/O operations.
-	 */
-	fsm_timer timer;
-
-	/**
-	 * Retry counter for misc. operations.
-	 */
-	int retry;
-
-	/**
-	 * The finite state machine of this channel
-	 */
-	fsm_instance *fsm;
-
-	/**
-	 * The corresponding net_device this channel
-	 * belongs to.
-	 */
-	struct net_device *netdev;
-
-	struct ctc_profile prof;
-
-	unsigned char *trans_skb_data;
-
-	__u16 logflags;
-};
-
-#define CHANNEL_FLAGS_READ            0
-#define CHANNEL_FLAGS_WRITE           1
-#define CHANNEL_FLAGS_INUSE           2
-#define CHANNEL_FLAGS_BUFSIZE_CHANGED 4
-#define CHANNEL_FLAGS_FAILED          8
-#define CHANNEL_FLAGS_WAITIRQ        16
-#define CHANNEL_FLAGS_RWMASK 1
-#define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK)
-
-#define LOG_FLAG_ILLEGALPKT  1
-#define LOG_FLAG_ILLEGALSIZE 2
-#define LOG_FLAG_OVERRUN     4
-#define LOG_FLAG_NOMEM       8
-
-#define CTC_LOGLEVEL_INFO     1
-#define CTC_LOGLEVEL_NOTICE   2
-#define CTC_LOGLEVEL_WARN     4
-#define CTC_LOGLEVEL_EMERG    8
-#define CTC_LOGLEVEL_ERR     16
-#define CTC_LOGLEVEL_DEBUG   32
-#define CTC_LOGLEVEL_CRIT    64
-
-#define CTC_LOGLEVEL_DEFAULT \
-(CTC_LOGLEVEL_INFO | CTC_LOGLEVEL_NOTICE | CTC_LOGLEVEL_WARN | CTC_LOGLEVEL_CRIT)
-
-#define CTC_LOGLEVEL_MAX     ((CTC_LOGLEVEL_CRIT<<1)-1)
-
-static int loglevel = CTC_LOGLEVEL_DEFAULT;
-
-#define ctc_pr_debug(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_DEBUG) printk(KERN_DEBUG fmt,##arg); } while (0)
-
-#define ctc_pr_info(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_INFO) printk(KERN_INFO fmt,##arg); } while (0)
-
-#define ctc_pr_notice(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_NOTICE) printk(KERN_NOTICE fmt,##arg); } while (0)
-
-#define ctc_pr_warn(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_WARN) printk(KERN_WARNING fmt,##arg); } while (0)
-
-#define ctc_pr_emerg(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_EMERG) printk(KERN_EMERG fmt,##arg); } while (0)
-
-#define ctc_pr_err(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_ERR) printk(KERN_ERR fmt,##arg); } while (0)
-
-#define ctc_pr_crit(fmt, arg...) \
-do { if (loglevel & CTC_LOGLEVEL_CRIT) printk(KERN_CRIT fmt,##arg); } while (0)
-
-/**
- * Linked list of all detected channels.
- */
-static struct channel *channels = NULL;
-
-struct ctc_priv {
-	struct net_device_stats stats;
-	unsigned long tbusy;
-	/**
-	 * The finite state machine of this interface.
-	 */
-	fsm_instance *fsm;
-	/**
-	 * The protocol of this device
-	 */
-	__u16 protocol;
- 	/**
- 	 * Timer for restarting after I/O Errors
- 	 */
- 	fsm_timer               restart_timer;
-
-	int buffer_size;
-
-	struct channel *channel[2];
-};
-
-/**
- * Definition of our link level header.
- */
-struct ll_header {
-	__u16 length;
-	__u16 type;
-	__u16 unused;
-};
-#define LL_HEADER_LENGTH (sizeof(struct ll_header))
-
-/**
- * Compatibility macros for busy handling
- * of network devices.
- */
-static __inline__ void
-ctc_clear_busy(struct net_device * dev)
-{
-	clear_bit(0, &(((struct ctc_priv *) dev->priv)->tbusy));
-	if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY)
-		netif_wake_queue(dev);
-}
-
-static __inline__ int
-ctc_test_and_set_busy(struct net_device * dev)
-{
-	if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY)
-		netif_stop_queue(dev);
-	return test_and_set_bit(0, &((struct ctc_priv *) dev->priv)->tbusy);
-}
-
-/**
- * Print Banner.
- */
-static void
-print_banner(void)
-{
-	static int printed = 0;
-	char vbuf[] = "$Revision: 1.72 $";
-	char *version = vbuf;
-
-	if (printed)
-		return;
-	if ((version = strchr(version, ':'))) {
-		char *p = strchr(version + 1, '$');
-		if (p)
-			*p = '\0';
-	} else
-		version = " ??? ";
-	printk(KERN_INFO "CTC driver Version%s"
-#ifdef DEBUG
-		    " (DEBUG-VERSION, " __DATE__ __TIME__ ")"
-#endif
-		    " initialized\n", version);
-	printed = 1;
-}
-
-/**
- * Return type of a detected device.
- */
-static enum channel_types
-get_channel_type(struct ccw_device_id *id)
-{
-	enum channel_types type = (enum channel_types) id->driver_info;
-
-	if (type == channel_type_ficon)
-		type = channel_type_escon;
-
-	return type;
-}
-
 /**
  * States of the interface statemachine.
  */
@@ -371,7 +95,7 @@
 	/**
 	 * MUST be always the last element!!
 	 */
-	NR_DEV_STATES
+	CTC_NR_DEV_STATES
 };
 
 static const char *dev_state_names[] = {
@@ -399,7 +123,7 @@
 	/**
 	 * MUST be always the last element!!
 	 */
-	NR_DEV_EVENTS
+	CTC_NR_DEV_EVENTS
 };
 
 static const char *dev_event_names[] = {
@@ -476,40 +200,6 @@
 	NR_CH_EVENTS,
 };
 
-static const char *ch_event_names[] = {
-	"ccw_device success",
-	"ccw_device busy",
-	"ccw_device enodev",
-	"ccw_device ioerr",
-	"ccw_device unknown",
-
-	"Status ATTN & BUSY",
-	"Status ATTN",
-	"Status BUSY",
-
-	"Unit check remote reset",
-	"Unit check remote system reset",
-	"Unit check TX timeout",
-	"Unit check TX parity",
-	"Unit check Hardware failure",
-	"Unit check RX parity",
-	"Unit check ZERO",
-	"Unit check Unknown",
-
-	"SubChannel check Unknown",
-
-	"Machine check failure",
-	"Machine check operational",
-
-	"IRQ normal",
-	"IRQ final",
-
-	"Timer",
-
-	"Start",
-	"Stop",
-};
-
 /**
  * States of the channel statemachine.
  */
@@ -545,6 +235,87 @@
 	NR_CH_STATES,
 };
 
+static int loglevel = CTC_LOGLEVEL_DEFAULT;
+
+/**
+ * Linked list of all detected channels.
+ */
+static struct channel *channels = NULL;
+
+/**
+ * Print Banner.
+ */
+static void
+print_banner(void)
+{
+	static int printed = 0;
+	char vbuf[] = "$Revision: 1.74 $";
+	char *version = vbuf;
+
+	if (printed)
+		return;
+	if ((version = strchr(version, ':'))) {
+		char *p = strchr(version + 1, '$');
+		if (p)
+			*p = '\0';
+	} else
+		version = " ??? ";
+	printk(KERN_INFO "CTC driver Version%s"
+#ifdef DEBUG
+		    " (DEBUG-VERSION, " __DATE__ __TIME__ ")"
+#endif
+		    " initialized\n", version);
+	printed = 1;
+}
+
+/**
+ * Return type of a detected device.
+ */
+static enum channel_types
+get_channel_type(struct ccw_device_id *id)
+{
+	enum channel_types type = (enum channel_types) id->driver_info;
+
+	if (type == channel_type_ficon)
+		type = channel_type_escon;
+
+	return type;
+}
+
+static const char *ch_event_names[] = {
+	"ccw_device success",
+	"ccw_device busy",
+	"ccw_device enodev",
+	"ccw_device ioerr",
+	"ccw_device unknown",
+
+	"Status ATTN & BUSY",
+	"Status ATTN",
+	"Status BUSY",
+
+	"Unit check remote reset",
+	"Unit check remote system reset",
+	"Unit check TX timeout",
+	"Unit check TX parity",
+	"Unit check Hardware failure",
+	"Unit check RX parity",
+	"Unit check ZERO",
+	"Unit check Unknown",
+
+	"SubChannel check Unknown",
+
+	"Machine check failure",
+	"Machine check operational",
+
+	"IRQ normal",
+	"IRQ final",
+
+	"Timer",
+
+	"Start",
+	"Stop",
+};
+
 static const char *ch_state_names[] = {
 	"Idle",
 	"Stopped",
@@ -1934,7 +1705,6 @@
 	ch->cdev = cdev;
 	snprintf(ch->id, CTC_ID_SIZE, "ch-%s", cdev->dev.bus_id);
 	ch->type = type;
-	loglevel = CTC_LOGLEVEL_DEFAULT;
 	ch->fsm = init_fsm(ch->id, ch_state_names,
 			   ch_event_names, NR_CH_STATES, NR_CH_EVENTS,
 			   ch_fsm, CH_FSM_LEN, GFP_KERNEL);
@@ -2697,6 +2467,7 @@
 /*
  * sysfs attributes
  */
+
 static ssize_t
 buffer_show(struct device *dev, char *buf)
 {
@@ -2715,57 +2486,61 @@
 	struct ctc_priv *priv;
 	struct net_device *ndev;
 	int bs1;
+	char buffer[16];
 
 	DBF_TEXT(trace, 3, __FUNCTION__);
+	DBF_TEXT(trace, 3, buf);
 	priv = dev->driver_data;
-	if (!priv)
+	if (!priv) {
+		DBF_TEXT(trace, 3, "bfnopriv");
 		return -ENODEV;
-	ndev = priv->channel[READ]->netdev;
-	if (!ndev)
-		return -ENODEV;
-	sscanf(buf, "%u", &bs1);
+	}
 
+	sscanf(buf, "%u", &bs1);
 	if (bs1 > CTC_BUFSIZE_LIMIT)
-		return -EINVAL;
+		goto einval;
+	if (bs1 < (576 + LL_HEADER_LENGTH + 2))
+		goto einval;
+	priv->buffer_size = bs1;	// just to overwrite the default
+
+	ndev = priv->channel[READ]->netdev;
+	if (!ndev) {
+		DBF_TEXT(trace, 3, "bfnondev");
+		return -ENODEV;
+	}
+
 	if ((ndev->flags & IFF_RUNNING) &&
 	    (bs1 < (ndev->mtu + LL_HEADER_LENGTH + 2)))
-		return -EINVAL;
-	if (bs1 < (576 + LL_HEADER_LENGTH + 2))
-		return -EINVAL;
+		goto einval;
 
-	priv->buffer_size = bs1;
-	priv->channel[READ]->max_bufsize =
-	    priv->channel[WRITE]->max_bufsize = bs1;
+	priv->channel[READ]->max_bufsize = bs1;
+	priv->channel[WRITE]->max_bufsize = bs1;
 	if (!(ndev->flags & IFF_RUNNING))
 		ndev->mtu = bs1 - LL_HEADER_LENGTH - 2;
 	priv->channel[READ]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
 	priv->channel[WRITE]->flags |= CHANNEL_FLAGS_BUFSIZE_CHANGED;
 
+	sprintf(buffer, "%d",priv->buffer_size);
+	DBF_TEXT(trace, 3, buffer);
 	return count;
 
+einval:
+	DBF_TEXT(trace, 3, "buff_err");
+	return -EINVAL;
 }
 
 static ssize_t
 loglevel_show(struct device *dev, char *buf)
 {
-	struct ctc_priv *priv;
-
-	priv = dev->driver_data;
-	if (!priv)
-		return -ENODEV;
 	return sprintf(buf, "%d\n", loglevel);
 }
 
 static ssize_t
 loglevel_write(struct device *dev, const char *buf, size_t count)
 {
-	struct ctc_priv *priv;
 	int ll1;
 
 	DBF_TEXT(trace, 5, __FUNCTION__);
-	priv = dev->driver_data;
-	if (!priv)
-		return -ENODEV;
 	sscanf(buf, "%i", &ll1);
 
 	if ((ll1 > CTC_LOGLEVEL_MAX) || (ll1 < 0))
@@ -2835,27 +2610,6 @@
 	return count;
 }
 
-static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
-static DEVICE_ATTR(loglevel, 0644, loglevel_show, loglevel_write);
-static DEVICE_ATTR(stats, 0644, stats_show, stats_write);
-
-static int
-ctc_add_attributes(struct device *dev)
-{
-//	device_create_file(dev, &dev_attr_buffer);
-	device_create_file(dev, &dev_attr_loglevel);
-	device_create_file(dev, &dev_attr_stats);
-	return 0;
-}
-
-static void
-ctc_remove_attributes(struct device *dev)
-{
-	device_remove_file(dev, &dev_attr_stats);
-	device_remove_file(dev, &dev_attr_loglevel);
-//	device_remove_file(dev, &dev_attr_buffer);
-}
-
 
 static void
 ctc_netdev_unregister(struct net_device * dev)
@@ -2899,52 +2653,6 @@
 #endif
 }
 
-/**
- * Initialize everything of the net device except the name and the
- * channel structs.
- */
-static struct net_device *
-ctc_init_netdevice(struct net_device * dev, int alloc_device, 
-		   struct ctc_priv *privptr)
-{
-	if (!privptr)
-		return NULL;
-
-	DBF_TEXT(setup, 3, __FUNCTION__);
-	if (alloc_device) {
-		dev = kmalloc(sizeof (struct net_device), GFP_KERNEL);
-		if (!dev)
-			return NULL;
-		memset(dev, 0, sizeof (struct net_device));
-	}
-
-	dev->priv = privptr;
-	privptr->fsm = init_fsm("ctcdev", dev_state_names,
-				dev_event_names, NR_DEV_STATES, NR_DEV_EVENTS,
-				dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
-	if (privptr->fsm == NULL) {
-		if (alloc_device)
-			kfree(dev);
-		return NULL;
-	}
-	fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
-	fsm_settimer(privptr->fsm, &privptr->restart_timer);
-	if (dev->mtu == 0)
-		dev->mtu = CTC_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2;
-	dev->hard_start_xmit = ctc_tx;
-	dev->open = ctc_open;
-	dev->stop = ctc_close;
-	dev->get_stats = ctc_stats;
-	dev->change_mtu = ctc_change_mtu;
-	dev->hard_header_len = LL_HEADER_LENGTH + 2;
-	dev->addr_len = 0;
-	dev->type = ARPHRD_SLIP;
-	dev->tx_queue_len = 100;
-	dev->flags = IFF_POINTOPOINT | IFF_NOARP;
-	SET_MODULE_OWNER(dev);
-	return dev;
-}
-
 static ssize_t
 ctc_proto_show(struct device *dev, char *buf)
 {
@@ -2977,7 +2685,6 @@
 	return count;
 }
 
-static DEVICE_ATTR(protocol, 0644, ctc_proto_show, ctc_proto_store);
 
 static ssize_t
 ctc_type_show(struct device *dev, char *buf)
@@ -2991,8 +2698,13 @@
 	return sprintf(buf, "%s\n", cu3088_type[cgdev->cdev[0]->id.driver_info]);
 }
 
+static DEVICE_ATTR(buffer, 0644, buffer_show, buffer_write);
+static DEVICE_ATTR(protocol, 0644, ctc_proto_show, ctc_proto_store);
 static DEVICE_ATTR(type, 0444, ctc_type_show, NULL);
 
+static DEVICE_ATTR(loglevel, 0644, loglevel_show, loglevel_write);
+static DEVICE_ATTR(stats, 0644, stats_show, stats_write);
+
 static struct attribute *ctc_attr[] = {
 	&dev_attr_protocol.attr,
 	&dev_attr_type.attr,
@@ -3005,6 +2717,21 @@
 };
 
 static int
+ctc_add_attributes(struct device *dev)
+{
+	device_create_file(dev, &dev_attr_loglevel);
+	device_create_file(dev, &dev_attr_stats);
+	return 0;
+}
+
+static void
+ctc_remove_attributes(struct device *dev)
+{
+	device_remove_file(dev, &dev_attr_stats);
+	device_remove_file(dev, &dev_attr_loglevel);
+}
+
+static int
 ctc_add_files(struct device *dev)
 {
 	pr_debug("%s() called\n", __FUNCTION__);
@@ -3028,15 +2755,15 @@
  *
  * @returns 0 on success, !0 on failure.
  */
-
 static int
 ctc_probe_device(struct ccwgroup_device *cgdev)
 {
 	struct ctc_priv *priv;
 	int rc;
+	char buffer[16];
 
 	pr_debug("%s() called\n", __FUNCTION__);
-	DBF_TEXT(trace, 3, __FUNCTION__);
+	DBF_TEXT(setup, 3, __FUNCTION__);
 
 	if (!get_device(&cgdev->dev))
 		return -ENODEV;
@@ -3060,10 +2787,70 @@
 	cgdev->cdev[1]->handler = ctc_irq_handler;
 	cgdev->dev.driver_data = priv;
 
+	sprintf(buffer, "%p", priv);
+	DBF_TEXT(data, 3, buffer);
+
+	sprintf(buffer, "%u", (unsigned int)sizeof(struct ctc_priv));
+	DBF_TEXT(data, 3, buffer);
+
+	sprintf(buffer, "%p", &channels);
+	DBF_TEXT(data, 3, buffer);
+
+	sprintf(buffer, "%u", (unsigned int)sizeof(struct channel));
+	DBF_TEXT(data, 3, buffer);
+
 	return 0;
 }
 
 /**
+ * Initialize everything of the net device except the name and the
+ * channel structs.
+ */
+static struct net_device *
+ctc_init_netdevice(struct net_device * dev, int alloc_device,
+		   struct ctc_priv *privptr)
+{
+	if (!privptr)
+		return NULL;
+
+	DBF_TEXT(setup, 3, __FUNCTION__);
+
+	if (alloc_device) {
+		dev = kmalloc(sizeof (struct net_device), GFP_KERNEL);
+		if (!dev)
+			return NULL;
+		memset(dev, 0, sizeof (struct net_device));
+	}
+
+	dev->priv = privptr;
+	privptr->fsm = init_fsm("ctcdev", dev_state_names,
+				dev_event_names, CTC_NR_DEV_STATES, CTC_NR_DEV_EVENTS,
+				dev_fsm, DEV_FSM_LEN, GFP_KERNEL);
+	if (privptr->fsm == NULL) {
+		if (alloc_device)
+			kfree(dev);
+		return NULL;
+	}
+	fsm_newstate(privptr->fsm, DEV_STATE_STOPPED);
+	fsm_settimer(privptr->fsm, &privptr->restart_timer);
+	if (dev->mtu == 0)
+		dev->mtu = CTC_BUFSIZE_DEFAULT - LL_HEADER_LENGTH - 2;
+	dev->hard_start_xmit = ctc_tx;
+	dev->open = ctc_open;
+	dev->stop = ctc_close;
+	dev->get_stats = ctc_stats;
+	dev->change_mtu = ctc_change_mtu;
+	dev->hard_header_len = LL_HEADER_LENGTH + 2;
+	dev->addr_len = 0;
+	dev->type = ARPHRD_SLIP;
+	dev->tx_queue_len = 100;
+	dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+	SET_MODULE_OWNER(dev);
+	return dev;
+}
+
+
+/**
  *
  * Setup an interface.
  *
@@ -3081,6 +2868,7 @@
 	struct ctc_priv *privptr;
 	struct net_device *dev;
 	int ret;
+	char buffer[16];
 
 	pr_debug("%s() called\n", __FUNCTION__);
 	DBF_TEXT(setup, 3, __FUNCTION__);
@@ -3089,6 +2877,9 @@
 	if (!privptr)
 		return -ENODEV;
 
+	sprintf(buffer, "%d", privptr->buffer_size);
+	DBF_TEXT(setup, 3, buffer);
+
 	type = get_channel_type(&cgdev->cdev[0]->id);
 	
 	snprintf(read_id, CTC_ID_SIZE, "ch-%s", cgdev->cdev[0]->dev.bus_id);
@@ -3177,9 +2968,10 @@
 	struct ctc_priv *priv;
 	struct net_device *ndev;
 		
-	DBF_TEXT(trace, 3, __FUNCTION__);
+	DBF_TEXT(setup, 3, __FUNCTION__);
 	pr_debug("%s() called\n", __FUNCTION__);
 
+
 	priv = cgdev->dev.driver_data;
 	ndev = NULL;
 	if (!priv)
@@ -3215,7 +3007,6 @@
 		channel_remove(priv->channel[READ]);
 	if (priv->channel[WRITE])
 		channel_remove(priv->channel[WRITE]);
-	
 	priv->channel[READ] = priv->channel[WRITE] = NULL;
 
 	return 0;
@@ -3228,7 +3019,7 @@
 	struct ctc_priv *priv;
 
 	pr_debug("%s() called\n", __FUNCTION__);
-	DBF_TEXT(trace, 3, __FUNCTION__);
+	DBF_TEXT(setup, 3, __FUNCTION__);
 
 	priv = cgdev->dev.driver_data;
 	if (!priv)
@@ -3265,6 +3056,7 @@
 static void __exit
 ctc_exit(void)
 {
+	DBF_TEXT(setup, 3, __FUNCTION__);
 	unregister_cu3088_discipline(&ctc_group_driver);
 	ctc_tty_cleanup();
 	ctc_unregister_dbf_views();
@@ -3282,6 +3074,10 @@
 {
 	int ret = 0;
 
+	loglevel = CTC_LOGLEVEL_DEFAULT;
+
+	DBF_TEXT(setup, 3, __FUNCTION__);
+
 	print_banner();
 
 	ret = ctc_register_dbf_views();
diff --git a/drivers/s390/net/ctcmain.h b/drivers/s390/net/ctcmain.h
new file mode 100644
index 0000000..ba3605f
--- /dev/null
+++ b/drivers/s390/net/ctcmain.h
@@ -0,0 +1,276 @@
+/*
+ * $Id: ctcmain.h,v 1.4 2005/03/24 09:04:17 mschwide Exp $
+ *
+ * CTC / ESCON network driver
+ *
+ * Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
+ * Author(s): Fritz Elfert (elfert@de.ibm.com, felfert@millenux.com)
+	      Peter Tiedemann (ptiedem@de.ibm.com)
+ *
+ *
+ * Documentation used:
+ *  - Principles of Operation (IBM doc#: SA22-7201-06)
+ *  - Common IO/-Device Commands and Self Description (IBM doc#: SA22-7204-02)
+ *  - Common IO/-Device Commands and Self Description (IBM doc#: SN22-5535)
+ *  - ESCON Channel-to-Channel Adapter (IBM doc#: SA22-7203-00)
+ *  - ESCON I/O Interface (IBM doc#: SA22-7202-029
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * RELEASE-TAG: CTC/ESCON network driver $Revision: 1.4 $
+ *
+ */
+
+#ifndef _CTCMAIN_H_
+#define _CTCMAIN_H_
+
+#include <asm/ccwdev.h>
+#include <asm/ccwgroup.h>
+
+#include "ctctty.h"
+#include "fsm.h"
+#include "cu3088.h"
+
+
+/**
+ * CCW commands, used in this driver.
+ */
+#define CCW_CMD_WRITE		0x01
+#define CCW_CMD_READ		0x02
+#define CCW_CMD_SET_EXTENDED	0xc3
+#define CCW_CMD_PREPARE		0xe3
+
+#define CTC_PROTO_S390          0
+#define CTC_PROTO_LINUX         1
+#define CTC_PROTO_LINUX_TTY     2
+#define CTC_PROTO_OS390         3
+#define CTC_PROTO_MAX           3
+
+#define CTC_BUFSIZE_LIMIT       65535
+#define CTC_BUFSIZE_DEFAULT     32768
+
+#define CTC_TIMEOUT_5SEC        5000
+
+#define CTC_INITIAL_BLOCKLEN    2
+
+#define READ			0
+#define WRITE			1
+
+#define CTC_ID_SIZE             BUS_ID_SIZE+3
+
+
+struct ctc_profile {
+	unsigned long maxmulti;
+	unsigned long maxcqueue;
+	unsigned long doios_single;
+	unsigned long doios_multi;
+	unsigned long txlen;
+	unsigned long tx_time;
+	struct timespec send_stamp;
+};
+
+/**
+ * Definition of one channel
+ */
+struct channel {
+
+	/**
+	 * Pointer to next channel in list.
+	 */
+	struct channel *next;
+	char id[CTC_ID_SIZE];
+	struct ccw_device *cdev;
+
+	/**
+	 * Type of this channel.
+	 * CTC/A or Escon for valid channels.
+	 */
+	enum channel_types type;
+
+	/**
+	 * Misc. flags. See CHANNEL_FLAGS_... below
+	 */
+	__u32 flags;
+
+	/**
+	 * The protocol of this channel
+	 */
+	__u16 protocol;
+
+	/**
+	 * I/O and irq related stuff
+	 */
+	struct ccw1 *ccw;
+	struct irb *irb;
+
+	/**
+	 * RX/TX buffer size
+	 */
+	int max_bufsize;
+
+	/**
+	 * Transmit/Receive buffer.
+	 */
+	struct sk_buff *trans_skb;
+
+	/**
+	 * Universal I/O queue.
+	 */
+	struct sk_buff_head io_queue;
+
+	/**
+	 * TX queue for collecting skb's during busy.
+	 */
+	struct sk_buff_head collect_queue;
+
+	/**
+	 * Amount of data in collect_queue.
+	 */
+	int collect_len;
+
+	/**
+	 * spinlock for collect_queue and collect_len
+	 */
+	spinlock_t collect_lock;
+
+	/**
+	 * Timer for detecting unresposive
+	 * I/O operations.
+	 */
+	fsm_timer timer;
+
+	/**
+	 * Retry counter for misc. operations.
+	 */
+	int retry;
+
+	/**
+	 * The finite state machine of this channel
+	 */
+	fsm_instance *fsm;
+
+	/**
+	 * The corresponding net_device this channel
+	 * belongs to.
+	 */
+	struct net_device *netdev;
+
+	struct ctc_profile prof;
+
+	unsigned char *trans_skb_data;
+
+	__u16 logflags;
+};
+
+#define CHANNEL_FLAGS_READ            0
+#define CHANNEL_FLAGS_WRITE           1
+#define CHANNEL_FLAGS_INUSE           2
+#define CHANNEL_FLAGS_BUFSIZE_CHANGED 4
+#define CHANNEL_FLAGS_FAILED          8
+#define CHANNEL_FLAGS_WAITIRQ        16
+#define CHANNEL_FLAGS_RWMASK 1
+#define CHANNEL_DIRECTION(f) (f & CHANNEL_FLAGS_RWMASK)
+
+#define LOG_FLAG_ILLEGALPKT  1
+#define LOG_FLAG_ILLEGALSIZE 2
+#define LOG_FLAG_OVERRUN     4
+#define LOG_FLAG_NOMEM       8
+
+#define CTC_LOGLEVEL_INFO     1
+#define CTC_LOGLEVEL_NOTICE   2
+#define CTC_LOGLEVEL_WARN     4
+#define CTC_LOGLEVEL_EMERG    8
+#define CTC_LOGLEVEL_ERR     16
+#define CTC_LOGLEVEL_DEBUG   32
+#define CTC_LOGLEVEL_CRIT    64
+
+#define CTC_LOGLEVEL_DEFAULT \
+(CTC_LOGLEVEL_INFO | CTC_LOGLEVEL_NOTICE | CTC_LOGLEVEL_WARN | CTC_LOGLEVEL_CRIT)
+
+#define CTC_LOGLEVEL_MAX     ((CTC_LOGLEVEL_CRIT<<1)-1)
+
+#define ctc_pr_debug(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_DEBUG) printk(KERN_DEBUG fmt,##arg); } while (0)
+
+#define ctc_pr_info(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_INFO) printk(KERN_INFO fmt,##arg); } while (0)
+
+#define ctc_pr_notice(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_NOTICE) printk(KERN_NOTICE fmt,##arg); } while (0)
+
+#define ctc_pr_warn(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_WARN) printk(KERN_WARNING fmt,##arg); } while (0)
+
+#define ctc_pr_emerg(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_EMERG) printk(KERN_EMERG fmt,##arg); } while (0)
+
+#define ctc_pr_err(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_ERR) printk(KERN_ERR fmt,##arg); } while (0)
+
+#define ctc_pr_crit(fmt, arg...) \
+do { if (loglevel & CTC_LOGLEVEL_CRIT) printk(KERN_CRIT fmt,##arg); } while (0)
+
+struct ctc_priv {
+	struct net_device_stats stats;
+	unsigned long tbusy;
+	/**
+	 * The finite state machine of this interface.
+	 */
+	fsm_instance *fsm;
+	/**
+	 * The protocol of this device
+	 */
+	__u16 protocol;
+ 	/**
+ 	 * Timer for restarting after I/O Errors
+ 	 */
+ 	fsm_timer               restart_timer;
+
+	int buffer_size;
+
+	struct channel *channel[2];
+};
+
+/**
+ * Definition of our link level header.
+ */
+struct ll_header {
+	__u16 length;
+	__u16 type;
+	__u16 unused;
+};
+#define LL_HEADER_LENGTH (sizeof(struct ll_header))
+
+/**
+ * Compatibility macros for busy handling
+ * of network devices.
+ */
+static __inline__ void
+ctc_clear_busy(struct net_device * dev)
+{
+	clear_bit(0, &(((struct ctc_priv *) dev->priv)->tbusy));
+	if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY)
+		netif_wake_queue(dev);
+}
+
+static __inline__ int
+ctc_test_and_set_busy(struct net_device * dev)
+{
+	if (((struct ctc_priv *)dev->priv)->protocol != CTC_PROTO_LINUX_TTY)
+		netif_stop_queue(dev);
+	return test_and_set_bit(0, &((struct ctc_priv *) dev->priv)->tbusy);
+}
+
+#endif
diff --git a/drivers/s390/net/ctctty.c b/drivers/s390/net/ctctty.c
index 9257d60..3080393 100644
--- a/drivers/s390/net/ctctty.c
+++ b/drivers/s390/net/ctctty.c
@@ -1,5 +1,5 @@
 /*
- * $Id: ctctty.c,v 1.26 2004/08/04 11:06:55 mschwide Exp $
+ * $Id: ctctty.c,v 1.29 2005/04/05 08:50:44 mschwide Exp $
  *
  * CTC / ESCON network driver, tty interface.
  *
@@ -1056,8 +1056,7 @@
 	info->tty = 0;
 	tty->closing = 0;
 	if (info->blocked_open) {
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(HZ/2);
+		msleep_interruptible(500);
 		wake_up_interruptible(&info->open_wait);
 	}
 	info->flags &= ~(CTC_ASYNC_NORMAL_ACTIVE | CTC_ASYNC_CLOSING);
diff --git a/drivers/s390/net/cu3088.c b/drivers/s390/net/cu3088.c
index 1b0a9f1..0075894 100644
--- a/drivers/s390/net/cu3088.c
+++ b/drivers/s390/net/cu3088.c
@@ -1,5 +1,5 @@
 /*
- * $Id: cu3088.c,v 1.34 2004/06/15 13:16:27 pavlic Exp $
+ * $Id: cu3088.c,v 1.35 2005/03/30 19:28:52 richtera Exp $
  *
  * CTC / LCS ccw_device driver
  *
@@ -39,6 +39,7 @@
 	"FICON channel",
 	"P390 LCS card",
 	"OSA LCS card",
+	"CLAW channel device",
 	"unknown channel type",
 	"unsupported channel type",
 };
@@ -51,6 +52,7 @@
 	{ CCW_DEVICE(0x3088, 0x1e), .driver_info = channel_type_ficon },
 	{ CCW_DEVICE(0x3088, 0x01), .driver_info = channel_type_p390 },
 	{ CCW_DEVICE(0x3088, 0x60), .driver_info = channel_type_osa2 },
+	{ CCW_DEVICE(0x3088, 0x61), .driver_info = channel_type_claw },
 	{ /* end of list */ }
 };
 
diff --git a/drivers/s390/net/cu3088.h b/drivers/s390/net/cu3088.h
index 0ec49a8..1753661 100644
--- a/drivers/s390/net/cu3088.h
+++ b/drivers/s390/net/cu3088.h
@@ -23,6 +23,9 @@
 	/* Device is a OSA2 card */
 	channel_type_osa2,
 
+	/* Device is a CLAW channel device */
+	channel_type_claw,
+
 	/* Device is a channel, but we don't know
 	 * anything about it */
 	channel_type_unknown,
diff --git a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c
index 1ac6563..e08e74e 100644
--- a/drivers/s390/net/iucv.c
+++ b/drivers/s390/net/iucv.c
@@ -1,5 +1,5 @@
 /* 
- * $Id: iucv.c,v 1.43 2005/02/09 14:47:43 braunu Exp $
+ * $Id: iucv.c,v 1.45 2005/04/26 22:59:06 braunu Exp $
  *
  * IUCV network driver
  *
@@ -29,7 +29,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
- * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.43 $
+ * RELEASE-TAG: IUCV lowlevel driver $Revision: 1.45 $
  *
  */
 
@@ -355,7 +355,7 @@
 static void
 iucv_banner(void)
 {
-	char vbuf[] = "$Revision: 1.43 $";
+	char vbuf[] = "$Revision: 1.45 $";
 	char *version = vbuf;
 
 	if ((version = strchr(version, ':'))) {
@@ -2553,12 +2553,12 @@
 #endif
 EXPORT_SYMBOL (iucv_reply_prmmsg);
 EXPORT_SYMBOL (iucv_send);
-#if 0
 EXPORT_SYMBOL (iucv_send2way);
 EXPORT_SYMBOL (iucv_send2way_array);
-EXPORT_SYMBOL (iucv_send_array);
 EXPORT_SYMBOL (iucv_send2way_prmmsg);
 EXPORT_SYMBOL (iucv_send2way_prmmsg_array);
+#if 0
+EXPORT_SYMBOL (iucv_send_array);
 EXPORT_SYMBOL (iucv_send_prmmsg);
 EXPORT_SYMBOL (iucv_setmask);
 #endif
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index 0f76e94..cccfed2 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -11,7 +11,7 @@
  *			  Frank Pavlic (pavlic@de.ibm.com) and
  *		 	  Martin Schwidefsky <schwidefsky@de.ibm.com>
  *
- *    $Revision: 1.96 $	 $Date: 2004/11/11 13:42:33 $
+ *    $Revision: 1.98 $	 $Date: 2005/04/18 13:41:29 $
  *
  * 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
@@ -59,7 +59,7 @@
 /**
  * initialization string for output
  */
-#define VERSION_LCS_C  "$Revision: 1.96 $"
+#define VERSION_LCS_C  "$Revision: 1.98 $"
 
 static char version[] __initdata = "LCS driver ("VERSION_LCS_C "/" VERSION_LCS_H ")";
 static char debug_buffer[255];
@@ -1098,14 +1098,6 @@
 		PRINT_ERR("Query IPAssist failed. Assuming unsupported!\n");
 		return -EOPNOTSUPP;
 	}
-	/* Print out supported assists: IPv6 */
-	PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name,
-		   (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
-		   "with" : "without");
-	/* Print out supported assist: Multicast */
-	PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name,
-		   (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
-		   "with" : "without");
 	if (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT)
 		return 0;
 	return -EOPNOTSUPP;
@@ -1160,7 +1152,7 @@
 		}
 	}
 	/* re-insert all entries from the failed_list into ipm_list */
-	list_for_each_entry(ipm, &failed_list, list) {
+	list_for_each_entry_safe(ipm, tmp, &failed_list, list) {
 		list_del_init(&ipm->list);
 		list_add_tail(&ipm->list, &card->ipm_list);
 	}
@@ -2198,30 +2190,39 @@
 	if (!dev)
 		goto out;
 	card->dev = dev;
-netdev_out:
 	card->dev->priv = card;
 	card->dev->open = lcs_open_device;
 	card->dev->stop = lcs_stop_device;
 	card->dev->hard_start_xmit = lcs_start_xmit;
 	card->dev->get_stats = lcs_getstats;
 	SET_MODULE_OWNER(dev);
-	if (lcs_register_netdev(ccwgdev) != 0)
-		goto out;
 	memcpy(card->dev->dev_addr, card->mac, LCS_MAC_LENGTH);
 #ifdef CONFIG_IP_MULTICAST
 	if (!lcs_check_multicast_support(card))
 		card->dev->set_multicast_list = lcs_set_multicast_list;
 #endif
-	netif_stop_queue(card->dev);
+netdev_out:
 	lcs_set_allowed_threads(card,0xffffffff);
 	if (recover_state == DEV_STATE_RECOVER) {
 		lcs_set_multicast_list(card->dev);
 		card->dev->flags |= IFF_UP;
 		netif_wake_queue(card->dev);
 		card->state = DEV_STATE_UP;
-	} else
+	} else {
 		lcs_stopcard(card);
+	}
 
+	if (lcs_register_netdev(ccwgdev) != 0)
+		goto out;
+
+	/* Print out supported assists: IPv6 */
+	PRINT_INFO("LCS device %s %s IPv6 support\n", card->dev->name,
+		   (card->ip_assists_supported & LCS_IPASS_IPV6_SUPPORT) ?
+		   "with" : "without");
+	/* Print out supported assist: Multicast */
+	PRINT_INFO("LCS device %s %s Multicast support\n", card->dev->name,
+		   (card->ip_assists_supported & LCS_IPASS_MULTICAST_SUPPORT) ?
+		   "with" : "without");
 	return 0;
 out:
 
diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h
index a341041..a755b57 100644
--- a/drivers/s390/net/qeth.h
+++ b/drivers/s390/net/qeth.h
@@ -24,7 +24,7 @@
 
 #include "qeth_mpc.h"
 
-#define VERSION_QETH_H 		"$Revision: 1.135 $"
+#define VERSION_QETH_H 		"$Revision: 1.139 $"
 
 #ifdef CONFIG_QETH_IPV6
 #define QETH_VERSION_IPV6 	":IPv6"
@@ -288,7 +288,8 @@
 #define QETH_TX_TIMEOUT		100 * HZ
 #define QETH_HEADER_SIZE	32
 #define MAX_PORTNO 		15
-#define QETH_FAKE_LL_LEN 	ETH_HLEN
+#define QETH_FAKE_LL_LEN_ETH	ETH_HLEN
+#define QETH_FAKE_LL_LEN_TR	(sizeof(struct trh_hdr)-TR_MAXRIFLEN+sizeof(struct trllc))
 #define QETH_FAKE_LL_V6_ADDR_POS 24
 
 /*IPv6 address autoconfiguration stuff*/
@@ -369,6 +370,25 @@
 	} hdr;
 } __attribute__ ((packed));
 
+/*TCP Segmentation Offload header*/
+struct qeth_hdr_ext_tso {
+        __u16 hdr_tot_len;
+        __u8  imb_hdr_no;
+        __u8  reserved;
+        __u8  hdr_type;
+        __u8  hdr_version;
+        __u16 hdr_len;
+        __u32 payload_len;
+        __u16 mss;
+        __u16 dg_hdr_len;
+        __u8  padding[16];
+} __attribute__ ((packed));
+
+struct qeth_hdr_tso {
+        struct qeth_hdr hdr; 	/*hdr->hdr.l3.xxx*/
+	struct qeth_hdr_ext_tso ext;
+} __attribute__ ((packed));
+
 
 /* flags for qeth_hdr.flags */
 #define QETH_HDR_PASSTHRU 0x10
@@ -866,6 +886,7 @@
         return hdr;
 }
 
+
 inline static int
 qeth_get_hlen(__u8 link_type)
 {
@@ -873,19 +894,19 @@
 	switch (link_type) {
 	case QETH_LINK_TYPE_HSTR:
 	case QETH_LINK_TYPE_LANE_TR:
-		return sizeof(struct qeth_hdr) + TR_HLEN;
+		return sizeof(struct qeth_hdr_tso) + TR_HLEN;
 	default:
 #ifdef CONFIG_QETH_VLAN
-		return sizeof(struct qeth_hdr) + VLAN_ETH_HLEN;
+		return sizeof(struct qeth_hdr_tso) + VLAN_ETH_HLEN;
 #else
-		return sizeof(struct qeth_hdr) + ETH_HLEN;
+		return sizeof(struct qeth_hdr_tso) + ETH_HLEN;
 #endif
 	}
 #else  /* CONFIG_QETH_IPV6 */
 #ifdef CONFIG_QETH_VLAN
-	return sizeof(struct qeth_hdr) + VLAN_HLEN;
+	return sizeof(struct qeth_hdr_tso) + VLAN_HLEN;
 #else
-	return sizeof(struct qeth_hdr);
+	return sizeof(struct qeth_hdr_tso);
 #endif
 #endif /* CONFIG_QETH_IPV6 */
 }
diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c
index 7ee1c06..f94f1f25 100644
--- a/drivers/s390/net/qeth_eddp.c
+++ b/drivers/s390/net/qeth_eddp.c
@@ -1,6 +1,6 @@
 /*
  *
- * linux/drivers/s390/net/qeth_eddp.c ($Revision: 1.11 $)
+ * linux/drivers/s390/net/qeth_eddp.c ($Revision: 1.13 $)
  *
  * Enhanced Device Driver Packing (EDDP) support for the qeth driver.
  *
@@ -8,7 +8,7 @@
  *
  *    Author(s): Thomas Spatzier <tspat@de.ibm.com>
  *
- *    $Revision: 1.11 $	 $Date: 2005/03/24 09:04:18 $
+ *    $Revision: 1.13 $	 $Date: 2005/05/04 20:19:18 $
  *
  */
 #include <linux/config.h>
@@ -85,7 +85,7 @@
 qeth_eddp_buf_release_contexts(struct qeth_qdio_out_buffer *buf)
 {
 	struct qeth_eddp_context_reference *ref;
-
+	
 	QETH_DBF_TEXT(trace, 6, "eddprctx");
 	while (!list_empty(&buf->ctx_list)){
 		ref = list_entry(buf->ctx_list.next,
@@ -139,7 +139,7 @@
 					   "buffer!\n");
 				goto out;
 			}
-		}
+		}		
 		/* check if the whole next skb fits into current buffer */
 		if ((QETH_MAX_BUFFER_ELEMENTS(queue->card) -
 					buf->next_element_to_fill)
@@ -152,7 +152,7 @@
 			 * and increment ctx's refcnt */
 			must_refcnt = 1;
 			continue;
-		}
+		}	
 		if (must_refcnt){
 			must_refcnt = 0;
 			if (qeth_eddp_buf_ref_context(buf, ctx)){
@@ -202,40 +202,29 @@
 	return flush_cnt;
 }
 
-static inline int
-qeth_get_skb_data_len(struct sk_buff *skb)
-{
-	int len = skb->len;
-	int i;
-
-	for (i = 0; i < skb_shinfo(skb)->nr_frags; ++i)
-		len -= skb_shinfo(skb)->frags[i].size;
-	return len;
-}
-
 static inline void
 qeth_eddp_create_segment_hdrs(struct qeth_eddp_context *ctx,
-			      struct qeth_eddp_data *eddp)
+			      struct qeth_eddp_data *eddp, int data_len)
 {
 	u8 *page;
 	int page_remainder;
 	int page_offset;
-	int hdr_len;
+	int pkt_len;
 	struct qeth_eddp_element *element;
 
 	QETH_DBF_TEXT(trace, 5, "eddpcrsh");
 	page = ctx->pages[ctx->offset >> PAGE_SHIFT];
 	page_offset = ctx->offset % PAGE_SIZE;
 	element = &ctx->elements[ctx->num_elements];
-	hdr_len = eddp->nhl + eddp->thl;
+	pkt_len = eddp->nhl + eddp->thl + data_len;
 	/* FIXME: layer2 and VLAN !!! */
 	if (eddp->qh.hdr.l2.id == QETH_HEADER_TYPE_LAYER2)
-		hdr_len += ETH_HLEN;
+		pkt_len += ETH_HLEN;
 	if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q))
-		hdr_len += VLAN_HLEN;
-	/* does complete header fit in current page ? */
+		pkt_len += VLAN_HLEN;
+	/* does complete packet fit in current page ? */
 	page_remainder = PAGE_SIZE - page_offset;
-	if (page_remainder < (sizeof(struct qeth_hdr) + hdr_len)){
+	if (page_remainder < (sizeof(struct qeth_hdr) + pkt_len)){
 		/* no -> go to start of next page */
 		ctx->offset += page_remainder;
 		page = ctx->pages[ctx->offset >> PAGE_SHIFT];
@@ -281,7 +270,7 @@
 	int left_in_frag;
 	int copy_len;
 	u8 *src;
-
+	
 	QETH_DBF_TEXT(trace, 5, "eddpcdtc");
 	if (skb_shinfo(eddp->skb)->nr_frags == 0) {
 		memcpy(dst, eddp->skb->data + eddp->skb_offset, len);
@@ -292,7 +281,7 @@
 		while (len > 0) {
 			if (eddp->frag < 0) {
 				/* we're in skb->data */
-				left_in_frag = qeth_get_skb_data_len(eddp->skb)
+				left_in_frag = (eddp->skb->len - eddp->skb->data_len)
 						- eddp->skb_offset;
 				src = eddp->skb->data + eddp->skb_offset;
 			} else {
@@ -424,7 +413,7 @@
 	struct tcphdr *tcph;
 	int data_len;
 	u32 hcsum;
-
+	
 	QETH_DBF_TEXT(trace, 5, "eddpftcp");
 	eddp->skb_offset = sizeof(struct qeth_hdr) + eddp->nhl + eddp->thl;
 	tcph = eddp->skb->h.th;
@@ -464,7 +453,7 @@
 		else
 			hcsum = qeth_eddp_check_tcp6_hdr(eddp, data_len);
 		/* fill the next segment into the context */
-		qeth_eddp_create_segment_hdrs(ctx, eddp);
+		qeth_eddp_create_segment_hdrs(ctx, eddp, data_len);
 		qeth_eddp_create_segment_data_tcp(ctx, eddp, data_len, hcsum);
 		if (eddp->skb_offset >= eddp->skb->len)
 			break;
@@ -474,13 +463,13 @@
 		eddp->th.tcp.h.seq += data_len;
 	}
 }
-
+			   
 static inline int
 qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx,
 			   struct sk_buff *skb, struct qeth_hdr *qhdr)
 {
 	struct qeth_eddp_data *eddp = NULL;
-
+	
 	QETH_DBF_TEXT(trace, 5, "eddpficx");
 	/* create our segmentation headers and copy original headers */
 	if (skb->protocol == ETH_P_IP)
@@ -520,7 +509,7 @@
 			 int hdr_len)
 {
 	int skbs_per_page;
-
+	
 	QETH_DBF_TEXT(trace, 5, "eddpcanp");
 	/* can we put multiple skbs in one page? */
 	skbs_per_page = PAGE_SIZE / (skb_shinfo(skb)->tso_size + hdr_len);
@@ -600,7 +589,7 @@
 			     struct qeth_hdr *qhdr)
 {
 	struct qeth_eddp_context *ctx = NULL;
-
+	
 	QETH_DBF_TEXT(trace, 5, "creddpct");
 	if (skb->protocol == ETH_P_IP)
 		ctx = qeth_eddp_create_context_generic(card, skb,
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c
index 607b925..208127a 100644
--- a/drivers/s390/net/qeth_main.c
+++ b/drivers/s390/net/qeth_main.c
@@ -1,6 +1,6 @@
 /*
  *
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.206 $)
+ * linux/drivers/s390/net/qeth_main.c ($Revision: 1.214 $)
  *
  * Linux on zSeries OSA Express and HiperSockets support
  *
@@ -12,7 +12,7 @@
  *			  Frank Pavlic (pavlic@de.ibm.com) and
  *		 	  Thomas Spatzier <tspat@de.ibm.com>
  *
- *    $Revision: 1.206 $	 $Date: 2005/03/24 09:04:18 $
+ *    $Revision: 1.214 $	 $Date: 2005/05/04 20:19:18 $
  *
  * 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
@@ -80,7 +80,7 @@
 #include "qeth_eddp.h"
 #include "qeth_tso.h"
 
-#define VERSION_QETH_C "$Revision: 1.206 $"
+#define VERSION_QETH_C "$Revision: 1.214 $"
 static const char *version = "qeth S/390 OSA-Express driver";
 
 /**
@@ -158,6 +158,9 @@
 static int
 qeth_set_online(struct ccwgroup_device *);
 
+static int
+__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode);
+
 static struct qeth_ipaddr *
 qeth_get_addr_buffer(enum qeth_prot_versions);
 
@@ -510,10 +513,10 @@
 	wake_up(&card->wait_q);
 }
 
-static int qeth_stop_card(struct qeth_card *);
+static int qeth_stop_card(struct qeth_card *, int);
 
 static int
-qeth_set_offline(struct ccwgroup_device *cgdev)
+__qeth_set_offline(struct ccwgroup_device *cgdev, int recovery_mode)
 {
 	struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data;
 	int rc = 0;
@@ -523,7 +526,7 @@
 	QETH_DBF_HEX(setup, 3, &card, sizeof(void *));
 
 	recover_flag = card->state;
-	if (qeth_stop_card(card) == -ERESTARTSYS){
+	if (qeth_stop_card(card, recovery_mode) == -ERESTARTSYS){
 		PRINT_WARN("Stopping card %s interrupted by user!\n",
 			   CARD_BUS_ID(card));
 		return -ERESTARTSYS;
@@ -540,6 +543,12 @@
 }
 
 static int
+qeth_set_offline(struct ccwgroup_device *cgdev)
+{
+	return  __qeth_set_offline(cgdev, 0);
+}
+
+static int
 qeth_wait_for_threads(struct qeth_card *card, unsigned long threads);
 
 
@@ -953,8 +962,8 @@
 	PRINT_WARN("Recovery of device %s started ...\n",
 		   CARD_BUS_ID(card));
 	card->use_hard_stop = 1;
-	qeth_set_offline(card->gdev);
-	rc = qeth_set_online(card->gdev);
+	__qeth_set_offline(card->gdev,1);
+	rc = __qeth_set_online(card->gdev,1);
 	if (!rc)
 		PRINT_INFO("Device %s successfully recovered!\n",
 			   CARD_BUS_ID(card));
@@ -2152,9 +2161,15 @@
 	if (!skb_len)
 		return NULL;
 	if (card->options.fake_ll){
-		if (!(skb = qeth_get_skb(skb_len + QETH_FAKE_LL_LEN)))
-			goto no_mem;
-		skb_pull(skb, QETH_FAKE_LL_LEN);
+		if(card->dev->type == ARPHRD_IEEE802_TR){
+			if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_TR)))
+				goto no_mem;
+			skb_reserve(skb,QETH_FAKE_LL_LEN_TR);
+		} else {
+			if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_ETH)))
+				goto no_mem;
+			skb_reserve(skb,QETH_FAKE_LL_LEN_ETH);
+		}
 	} else if (!(skb = qeth_get_skb(skb_len)))
 		goto no_mem;
 	data_ptr = element->addr + offset;
@@ -2229,14 +2244,68 @@
 }
 
 static inline void
-qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
+qeth_rebuild_skb_fake_ll_tr(struct qeth_card *card, struct sk_buff *skb,
+			 struct qeth_hdr *hdr)
+{
+	struct trh_hdr *fake_hdr;
+	struct trllc *fake_llc;
+	struct iphdr *ip_hdr;
+
+	QETH_DBF_TEXT(trace,5,"skbfktr");
+	skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_TR;
+	/* this is a fake ethernet header */
+	fake_hdr = (struct trh_hdr *) skb->mac.raw;
+
+	/* the destination MAC address */
+	switch (skb->pkt_type){
+	case PACKET_MULTICAST:
+		switch (skb->protocol){
+#ifdef CONFIG_QETH_IPV6
+		case __constant_htons(ETH_P_IPV6):
+			ndisc_mc_map((struct in6_addr *)
+				     skb->data + QETH_FAKE_LL_V6_ADDR_POS,
+				     fake_hdr->daddr, card->dev, 0);
+			break;
+#endif /* CONFIG_QETH_IPV6 */
+		case __constant_htons(ETH_P_IP):
+			ip_hdr = (struct iphdr *)skb->data;
+			ip_tr_mc_map(ip_hdr->daddr, fake_hdr->daddr);
+			break;
+		default:
+			memcpy(fake_hdr->daddr, card->dev->dev_addr, TR_ALEN);
+		}
+		break;
+	case PACKET_BROADCAST:
+		memset(fake_hdr->daddr, 0xff, TR_ALEN);
+		break;
+	default:
+		memcpy(fake_hdr->daddr, card->dev->dev_addr, TR_ALEN);
+	}
+	/* the source MAC address */
+	if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
+		memcpy(fake_hdr->saddr, &hdr->hdr.l3.dest_addr[2], TR_ALEN);
+	else
+		memset(fake_hdr->saddr, 0, TR_ALEN);
+	fake_hdr->rcf=0;
+	fake_llc = (struct trllc*)&(fake_hdr->rcf);
+	fake_llc->dsap = EXTENDED_SAP;
+	fake_llc->ssap = EXTENDED_SAP;
+	fake_llc->llc  = UI_CMD;
+	fake_llc->protid[0] = 0;
+	fake_llc->protid[1] = 0;
+	fake_llc->protid[2] = 0;
+	fake_llc->ethertype = ETH_P_IP;
+}
+
+static inline void
+qeth_rebuild_skb_fake_ll_eth(struct qeth_card *card, struct sk_buff *skb,
 			 struct qeth_hdr *hdr)
 {
 	struct ethhdr *fake_hdr;
 	struct iphdr *ip_hdr;
 
-	QETH_DBF_TEXT(trace,5,"skbfake");
-	skb->mac.raw = skb->data - QETH_FAKE_LL_LEN;
+	QETH_DBF_TEXT(trace,5,"skbfketh");
+	skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_ETH;
 	/* this is a fake ethernet header */
 	fake_hdr = (struct ethhdr *) skb->mac.raw;
 
@@ -2253,10 +2322,7 @@
 #endif /* CONFIG_QETH_IPV6 */
 		case __constant_htons(ETH_P_IP):
 			ip_hdr = (struct iphdr *)skb->data;
-			if (card->dev->type == ARPHRD_IEEE802_TR)
-				ip_tr_mc_map(ip_hdr->daddr, fake_hdr->h_dest);
-			else
-				ip_eth_mc_map(ip_hdr->daddr, fake_hdr->h_dest);
+			ip_eth_mc_map(ip_hdr->daddr, fake_hdr->h_dest);
 			break;
 		default:
 			memcpy(fake_hdr->h_dest, card->dev->dev_addr, ETH_ALEN);
@@ -2278,6 +2344,16 @@
 }
 
 static inline void
+qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
+			struct qeth_hdr *hdr)
+{
+	if (card->dev->type == ARPHRD_IEEE802_TR)
+		qeth_rebuild_skb_fake_ll_tr(card, skb, hdr);
+	else
+		qeth_rebuild_skb_fake_ll_eth(card, skb, hdr);
+}
+
+static inline void
 qeth_rebuild_skb_vlan(struct qeth_card *card, struct sk_buff *skb,
 		      struct qeth_hdr *hdr)
 {
@@ -3440,16 +3516,25 @@
 		     unsigned short type, void *daddr, void *saddr,
 		     unsigned len)
 {
-	struct ethhdr *hdr;
+	if(dev->type == ARPHRD_IEEE802_TR){
+		struct trh_hdr *hdr;
+        	hdr = (struct trh_hdr *)skb_push(skb, QETH_FAKE_LL_LEN_TR);
+		memcpy(hdr->saddr, dev->dev_addr, TR_ALEN);
+        	memcpy(hdr->daddr, "FAKELL", TR_ALEN);
+		return QETH_FAKE_LL_LEN_TR;
 
-        hdr = (struct ethhdr *)skb_push(skb, QETH_FAKE_LL_LEN);
-	memcpy(hdr->h_source, dev->dev_addr, ETH_ALEN);
-        memcpy(hdr->h_dest, "FAKELL", ETH_ALEN);
-        if (type != ETH_P_802_3)
-                hdr->h_proto = htons(type);
-        else
-                hdr->h_proto = htons(len);
-	return QETH_FAKE_LL_LEN;
+	} else {
+		struct ethhdr *hdr;
+        	hdr = (struct ethhdr *)skb_push(skb, QETH_FAKE_LL_LEN_ETH);
+		memcpy(hdr->h_source, dev->dev_addr, ETH_ALEN);
+        	memcpy(hdr->h_dest, "FAKELL", ETH_ALEN);
+        	if (type != ETH_P_802_3)
+                	hdr->h_proto = htons(type);
+        	else
+                	hdr->h_proto = htons(len);
+		return QETH_FAKE_LL_LEN_ETH;
+
+	}
 }
 
 static inline int
@@ -3710,16 +3795,12 @@
 qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
 		 struct qeth_hdr **hdr, int ipv)
 {
-	int rc = 0;
 #ifdef CONFIG_QETH_VLAN
 	u16 *tag;
 #endif
 
 	QETH_DBF_TEXT(trace, 6, "prepskb");
 
-	rc = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr));
-	if (rc)
-		return rc;
 #ifdef CONFIG_QETH_VLAN
 	if (card->vlangrp && vlan_tx_tag_present(*skb) &&
 	    ((ipv == 6) || card->options.layer2) ) {
@@ -3882,9 +3963,15 @@
 			memcpy(hdr->hdr.l3.dest_addr, &skb->nh.ipv6h->daddr, 16);
 		}
 	} else { /* passthrough */
-		if (!memcmp(skb->data + sizeof(struct qeth_hdr),
+                if((skb->dev->type == ARPHRD_IEEE802_TR) &&
+		    !memcmp(skb->data + sizeof(struct qeth_hdr) + 
+		    sizeof(__u16), skb->dev->broadcast, 6)) {
+			hdr->hdr.l3.flags = QETH_CAST_BROADCAST |
+						QETH_HDR_PASSTHRU;
+		} else if (!memcmp(skb->data + sizeof(struct qeth_hdr),
 			    skb->dev->broadcast, 6)) {   /* broadcast? */
-			hdr->hdr.l3.flags = QETH_CAST_BROADCAST | QETH_HDR_PASSTHRU;
+			hdr->hdr.l3.flags = QETH_CAST_BROADCAST |
+						QETH_HDR_PASSTHRU;
 		} else {
  			hdr->hdr.l3.flags = (cast_type == RTN_MULTICAST) ?
  				QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU :
@@ -3894,67 +3981,29 @@
 }
 
 static inline void
-__qeth_fill_buffer_frag(struct sk_buff *skb, struct qdio_buffer *buffer,
-			int *next_element_to_fill)
-{
-	int length = skb->len;
-	struct skb_frag_struct *frag;
-	int fragno;
-	unsigned long addr;
-	int element;
-	int first_lap = 1;
-
-	fragno = skb_shinfo(skb)->nr_frags; /* start with last frag */
-	element = *next_element_to_fill + fragno;
-	while (length > 0) {
-		if (fragno > 0) {
-			frag = &skb_shinfo(skb)->frags[fragno - 1];
-			addr = (page_to_pfn(frag->page) << PAGE_SHIFT) +
-				frag->page_offset;
-			buffer->element[element].addr = (char *)addr;
-			buffer->element[element].length = frag->size;
-			length -= frag->size;
-			if (first_lap)
-				buffer->element[element].flags =
-				    SBAL_FLAGS_LAST_FRAG;
-			else
-				buffer->element[element].flags =
-				    SBAL_FLAGS_MIDDLE_FRAG;
-		} else {
-			buffer->element[element].addr = skb->data;
-			buffer->element[element].length = length;
-			length = 0;
-			buffer->element[element].flags =
-				SBAL_FLAGS_FIRST_FRAG;
-		}
-		element--;
-		fragno--;
-		first_lap = 0;
-	}
-	*next_element_to_fill += skb_shinfo(skb)->nr_frags + 1;
-}
-
-static inline void
 __qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer,
-		   int *next_element_to_fill)
+		   int is_tso, int *next_element_to_fill)
 {
 	int length = skb->len;
 	int length_here;
 	int element;
 	char *data;
-	int first_lap = 1;
+	int first_lap ;
 
 	element = *next_element_to_fill;
 	data = skb->data;
+	first_lap = (is_tso == 0 ? 1 : 0);
+
 	while (length > 0) {
 		/* length_here is the remaining amount of data in this page */
 		length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE);
 		if (length < length_here)
 			length_here = length;
+
 		buffer->element[element].addr = data;
 		buffer->element[element].length = length_here;
 		length -= length_here;
-		if (!length){
+		if (!length) {
 			if (first_lap)
 				buffer->element[element].flags = 0;
 			else
@@ -3981,17 +4030,35 @@
 		 struct sk_buff *skb)
 {
 	struct qdio_buffer *buffer;
-	int flush_cnt = 0;
+	struct qeth_hdr_tso *hdr;
+	int flush_cnt = 0, hdr_len, large_send = 0;
 
 	QETH_DBF_TEXT(trace, 6, "qdfillbf");
+
 	buffer = buf->buffer;
 	atomic_inc(&skb->users);
 	skb_queue_tail(&buf->skb_list, skb);
+
+	hdr  = (struct qeth_hdr_tso *) skb->data;
+	/*check first on TSO ....*/
+	if (hdr->hdr.hdr.l3.id == QETH_HEADER_TYPE_TSO) {
+		int element = buf->next_element_to_fill;
+
+		hdr_len = sizeof(struct qeth_hdr_tso) + hdr->ext.dg_hdr_len;
+		/*fill first buffer entry only with header information */
+		buffer->element[element].addr = skb->data;
+		buffer->element[element].length = hdr_len;
+		buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG;
+		buf->next_element_to_fill++;
+		skb->data += hdr_len;
+		skb->len  -= hdr_len;
+		large_send = 1;
+	}
 	if (skb_shinfo(skb)->nr_frags == 0)
-		__qeth_fill_buffer(skb, buffer,
+		__qeth_fill_buffer(skb, buffer, large_send,
 				   (int *)&buf->next_element_to_fill);
 	else
-		__qeth_fill_buffer_frag(skb, buffer,
+		__qeth_fill_buffer_frag(skb, buffer, large_send,
 					(int *)&buf->next_element_to_fill);
 
 	if (!queue->do_pack) {
@@ -4184,6 +4251,25 @@
 }
 
 static inline int
+qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb)
+{
+	int elements_needed = 0;
+
+        if (skb_shinfo(skb)->nr_frags > 0) {
+                elements_needed = (skb_shinfo(skb)->nr_frags + 1);
+	}
+        if (elements_needed == 0 )
+                elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE)
+                                        + skb->len) >> PAGE_SHIFT);
+        if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){
+                PRINT_ERR("qeth_do_send_packet: invalid size of "
+                          "IP packet. Discarded.");
+                return 0;
+        }
+        return elements_needed;
+}
+
+static inline int
 qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
 {
 	int ipv = 0;
@@ -4205,7 +4291,11 @@
                         	dev_kfree_skb_irq(skb);
                         	return 0;
                 	}
-                	skb_pull(skb, QETH_FAKE_LL_LEN);
+			if(card->dev->type == ARPHRD_IEEE802_TR){
+				skb_pull(skb, QETH_FAKE_LL_LEN_TR);
+			} else {
+                		skb_pull(skb, QETH_FAKE_LL_LEN_ETH);
+			}
 		}
 	}
 	cast_type = qeth_get_cast_type(card, skb);
@@ -4221,19 +4311,25 @@
 	if (skb_shinfo(skb)->tso_size)
 		large_send = card->options.large_send;
 
-	if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){
-		QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc);
-		return rc;
-	}
 	/*are we able to do TSO ? If so ,prepare and send it from here */
 	if ((large_send == QETH_LARGE_SEND_TSO) &&
 	    (cast_type == RTN_UNSPEC)) {
-		rc = qeth_tso_send_packet(card, skb, queue,
-					  ipv, cast_type);
-		goto do_statistics;
+		rc = qeth_tso_prepare_packet(card, skb, ipv, cast_type);
+		if (rc) {
+			card->stats.tx_dropped++;
+			card->stats.tx_errors++;
+			dev_kfree_skb_any(skb);
+			return NETDEV_TX_OK;
+		} 
+		elements_needed++;
+	} else {
+		if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))) {
+			QETH_DBF_TEXT_(trace, 4, "pskbe%d", rc);
+			return rc;
+		}
+		qeth_fill_header(card, hdr, skb, ipv, cast_type);
 	}
 
-	qeth_fill_header(card, hdr, skb, ipv, cast_type);
 	if (large_send == QETH_LARGE_SEND_EDDP) {
 		ctx = qeth_eddp_create_context(card, skb, hdr);
 		if (ctx == NULL) {
@@ -4241,7 +4337,7 @@
 			return -EINVAL;
 		}
 	} else {
-		elements_needed = qeth_get_elements_no(card,(void*) hdr, skb);
+		elements_needed += qeth_get_elements_no(card,(void*) hdr, skb);
 		if (!elements_needed)
 			return -EINVAL;
 	}
@@ -4252,12 +4348,12 @@
 	else
 		rc = qeth_do_send_packet_fast(card, queue, skb, hdr,
 					      elements_needed, ctx);
-do_statistics:
 	if (!rc){
 		card->stats.tx_packets++;
 		card->stats.tx_bytes += skb->len;
 #ifdef CONFIG_QETH_PERF_STATS
-		if (skb_shinfo(skb)->tso_size) {
+		if (skb_shinfo(skb)->tso_size &&
+		   !(large_send == QETH_LARGE_SEND_NO)) {
 			card->perf_stats.large_send_bytes += skb->len;
 			card->perf_stats.large_send_cnt++;
 		}
@@ -7154,7 +7250,7 @@
 }
 
 static int
-qeth_stop_card(struct qeth_card *card)
+qeth_stop_card(struct qeth_card *card, int recovery_mode)
 {
 	int rc = 0;
 
@@ -7167,9 +7263,13 @@
 	if (card->read.state == CH_STATE_UP &&
 	    card->write.state == CH_STATE_UP &&
 	    (card->state == CARD_STATE_UP)) {
-		rtnl_lock();
-		dev_close(card->dev);
-		rtnl_unlock();
+		if(recovery_mode) {
+			qeth_stop(card->dev);
+		} else {
+			rtnl_lock();
+			dev_close(card->dev);
+			rtnl_unlock();
+		}
 		if (!card->use_hard_stop) {
 			__u8 *mac = &card->dev->dev_addr[0];
 			rc = qeth_layer2_send_delmac(card, mac);
@@ -7341,13 +7441,17 @@
 }
 
 static void
-qeth_start_again(struct qeth_card *card)
+qeth_start_again(struct qeth_card *card, int recovery_mode)
 {
 	QETH_DBF_TEXT(setup ,2, "startag");
 
-	rtnl_lock();
-	dev_open(card->dev);
-	rtnl_unlock();
+	if(recovery_mode) {
+		qeth_open(card->dev);
+	} else {
+		rtnl_lock();
+		dev_open(card->dev);
+		rtnl_unlock();
+	}
 	/* this also sets saved unicast addresses */
 	qeth_set_multicast_list(card->dev);
 }
@@ -7404,7 +7508,7 @@
 
 
 static int
-qeth_set_online(struct ccwgroup_device *gdev)
+__qeth_set_online(struct ccwgroup_device *gdev, int recovery_mode)
 {
 	struct qeth_card *card = gdev->dev.driver_data;
 	int rc = 0;
@@ -7464,12 +7568,12 @@
  * we can also use this state for recovery purposes*/
 	qeth_set_allowed_threads(card, 0xffffffff, 0);
 	if (recover_flag == CARD_STATE_RECOVER)
-		qeth_start_again(card);
+		qeth_start_again(card, recovery_mode);
 	qeth_notify_processes();
 	return 0;
 out_remove:
 	card->use_hard_stop = 1;
-	qeth_stop_card(card);
+	qeth_stop_card(card, 0);
 	ccw_device_set_offline(CARD_DDEV(card));
 	ccw_device_set_offline(CARD_WDEV(card));
 	ccw_device_set_offline(CARD_RDEV(card));
@@ -7480,6 +7584,12 @@
 	return -ENODEV;
 }
 
+static int
+qeth_set_online(struct ccwgroup_device *gdev)
+{
+	return __qeth_set_online(gdev, 0);
+}
+
 static struct ccw_device_id qeth_ids[] = {
 	{CCW_DEVICE(0x1731, 0x01), driver_info:QETH_CARD_TYPE_OSAE},
 	{CCW_DEVICE(0x1731, 0x05), driver_info:QETH_CARD_TYPE_IQD},
diff --git a/drivers/s390/net/qeth_tso.c b/drivers/s390/net/qeth_tso.c
deleted file mode 100644
index c919762..0000000
--- a/drivers/s390/net/qeth_tso.c
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * linux/drivers/s390/net/qeth_tso.c ($Revision: 1.6 $)
- *
- * Header file for qeth TCP Segmentation Offload support.
- *
- * Copyright 2004 IBM Corporation
- *
- *    Author(s): Frank Pavlic <pavlic@de.ibm.com>
- *
- *    $Revision: 1.6 $	 $Date: 2005/03/24 09:04:18 $
- *
- */
-
-#include <linux/skbuff.h>
-#include <linux/tcp.h>
-#include <linux/ip.h>
-#include <linux/ipv6.h>
-#include <net/ip6_checksum.h>
-#include "qeth.h"
-#include "qeth_mpc.h"
-#include "qeth_tso.h"
-
-/**
- * skb already partially prepared
- * classic qdio header in skb->data
- * */
-static inline struct qeth_hdr_tso *
-qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb)
-{
-	int rc = 0;
-
-	QETH_DBF_TEXT(trace, 5, "tsoprsk");
-	rc = qeth_realloc_headroom(card, skb,sizeof(struct qeth_hdr_ext_tso));
-	if (rc)
-		return NULL;
-
-	return qeth_push_skb(card, skb, sizeof(struct qeth_hdr_ext_tso));
-}
-
-/**
- * fill header for a TSO packet
- */
-static inline void
-qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb)
-{
-	struct qeth_hdr_tso *hdr;
-	struct tcphdr *tcph;
-	struct iphdr *iph;
-
-	QETH_DBF_TEXT(trace, 5, "tsofhdr");
-
-	hdr  = (struct qeth_hdr_tso *) skb->data;
-	iph  = skb->nh.iph;
-	tcph = skb->h.th;
-	/*fix header to TSO values ...*/
-	hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO;
-	/*set values which are fix for the first approach ...*/
-	hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso);
-	hdr->ext.imb_hdr_no  = 1;
-	hdr->ext.hdr_type    = 1;
-	hdr->ext.hdr_version = 1;
-	hdr->ext.hdr_len     = 28;
-	/*insert non-fix values */
-	hdr->ext.mss = skb_shinfo(skb)->tso_size;
-	hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4);
-	hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len -
-				       sizeof(struct qeth_hdr_tso));
-}
-
-/**
- * change some header values as requested by hardware
- */
-static inline void
-qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb)
-{
-	struct iphdr *iph;
-	struct ipv6hdr *ip6h;
-	struct tcphdr *tcph;
-
-	iph  = skb->nh.iph;
-	ip6h = skb->nh.ipv6h;
-	tcph = skb->h.th;
-
-	tcph->check = 0;
-	if (skb->protocol == ETH_P_IPV6) {
-		ip6h->payload_len = 0;
-		tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
-					       0, IPPROTO_TCP, 0);
-		return;
-	}
-	/*OSA want us to set these values ...*/
-	tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
-					 0, IPPROTO_TCP, 0);
-	iph->tot_len = 0;
-	iph->check = 0;
-}
-
-static inline struct qeth_hdr_tso *
-qeth_tso_prepare_packet(struct qeth_card *card, struct sk_buff *skb,
-			int ipv, int cast_type)
-{
-	struct qeth_hdr_tso *hdr;
-	int rc = 0;
-
-	QETH_DBF_TEXT(trace, 5, "tsoprep");
-
-	/*get headroom for tso qdio header */
-	hdr = (struct qeth_hdr_tso *) qeth_tso_prepare_skb(card, &skb);
-	if (hdr == NULL) {
-		QETH_DBF_TEXT_(trace, 4, "2err%d", rc);
-		return NULL;
-	}
-	memset(hdr, 0, sizeof(struct qeth_hdr_tso));
-	/*fill first 32 bytes of  qdio header as used
-	 *FIXME: TSO has two struct members
-	 * with different names but same size
-	 * */
-	qeth_fill_header(card, &hdr->hdr, skb, ipv, cast_type);
-	qeth_tso_fill_header(card, skb);
-	qeth_tso_set_tcpip_header(card, skb);
-	return hdr;
-}
-
-static inline int
-qeth_tso_get_queue_buffer(struct qeth_qdio_out_q *queue)
-{
-	struct qeth_qdio_out_buffer *buffer;
-	int flush_cnt = 0;
-
-	QETH_DBF_TEXT(trace, 5, "tsobuf");
-
-	/* force to non-packing*/
-	if (queue->do_pack)
-		queue->do_pack = 0;
-	buffer = &queue->bufs[queue->next_buf_to_fill];
-	/* get a new buffer if current is already in use*/
-	if ((atomic_read(&buffer->state) == QETH_QDIO_BUF_EMPTY) &&
-	    (buffer->next_element_to_fill > 0)) {
-		atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED);
-		queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
-					  QDIO_MAX_BUFFERS_PER_Q;
-		flush_cnt++;
-	}
-	return flush_cnt;
-}
-
-static inline void
-__qeth_tso_fill_buffer_frag(struct qeth_qdio_out_buffer *buf,
-			  struct sk_buff *skb)
-{
-	struct skb_frag_struct *frag;
-	struct qdio_buffer *buffer;
-	int fragno, cnt, element;
-	unsigned long addr;
-
-        QETH_DBF_TEXT(trace, 6, "tsfilfrg");
-
-	/*initialize variables ...*/
-	fragno = skb_shinfo(skb)->nr_frags;
-	buffer = buf->buffer;
-	element = buf->next_element_to_fill;
-	/*fill buffer elements .....*/
-	for (cnt = 0; cnt < fragno; cnt++) {
-		frag = &skb_shinfo(skb)->frags[cnt];
-		addr = (page_to_pfn(frag->page) << PAGE_SHIFT) +
-			frag->page_offset;
-		buffer->element[element].addr = (char *)addr;
-		buffer->element[element].length = frag->size;
-		if (cnt < (fragno - 1))
-			buffer->element[element].flags =
-				SBAL_FLAGS_MIDDLE_FRAG;
-		else
-			buffer->element[element].flags =
-				SBAL_FLAGS_LAST_FRAG;
-		element++;
-	}
-	buf->next_element_to_fill = element;
-}
-
-static inline int
-qeth_tso_fill_buffer(struct qeth_qdio_out_buffer *buf,
-		     struct sk_buff *skb)
-{
-        int length, length_here, element;
-        int hdr_len;
-	struct qdio_buffer *buffer;
-	struct qeth_hdr_tso *hdr;
-	char *data;
-
-        QETH_DBF_TEXT(trace, 3, "tsfilbuf");
-
-	/*increment user count and queue skb ...*/
-        atomic_inc(&skb->users);
-        skb_queue_tail(&buf->skb_list, skb);
-
-	/*initialize all variables...*/
-        buffer = buf->buffer;
-	hdr = (struct qeth_hdr_tso *)skb->data;
-	hdr_len = sizeof(struct qeth_hdr_tso) + hdr->ext.dg_hdr_len;
-	data = skb->data + hdr_len;
-	length = skb->len - hdr_len;
-        element = buf->next_element_to_fill;
-	/*fill first buffer entry only with header information */
-	buffer->element[element].addr = skb->data;
-	buffer->element[element].length = hdr_len;
-	buffer->element[element].flags = SBAL_FLAGS_FIRST_FRAG;
-	buf->next_element_to_fill++;
-
-	if (skb_shinfo(skb)->nr_frags > 0) {
-                 __qeth_tso_fill_buffer_frag(buf, skb);
-                 goto out;
-        }
-
-       /*start filling buffer entries ...*/
-        element++;
-        while (length > 0) {
-                /* length_here is the remaining amount of data in this page */
-		length_here = PAGE_SIZE - ((unsigned long) data % PAGE_SIZE);
-		if (length < length_here)
-                        length_here = length;
-                buffer->element[element].addr = data;
-                buffer->element[element].length = length_here;
-                length -= length_here;
-                if (!length)
-                        buffer->element[element].flags =
-                                SBAL_FLAGS_LAST_FRAG;
-                 else
-                         buffer->element[element].flags =
-                                 SBAL_FLAGS_MIDDLE_FRAG;
-                data += length_here;
-                element++;
-        }
-        /*set the buffer to primed  ...*/
-        buf->next_element_to_fill = element;
-out:
-	atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
-        return 1;
-}
-
-int
-qeth_tso_send_packet(struct qeth_card *card, struct sk_buff *skb,
-		     struct qeth_qdio_out_q *queue, int ipv, int cast_type)
-{
-	int flush_cnt = 0;
-	struct qeth_hdr_tso *hdr;
-	struct qeth_qdio_out_buffer *buffer;
-        int start_index;
-
-	QETH_DBF_TEXT(trace, 3, "tsosend");
-
-	if (!(hdr = qeth_tso_prepare_packet(card, skb, ipv, cast_type)))
-	     	return -ENOMEM;
-	/*check if skb fits in one SBAL ...*/
-	if (!(qeth_get_elements_no(card, (void*)hdr, skb)))
-		return -EINVAL;
-	/*lock queue, force switching to non-packing and send it ...*/
-	while (atomic_compare_and_swap(QETH_OUT_Q_UNLOCKED,
-                                       QETH_OUT_Q_LOCKED,
-                                       &queue->state));
-        start_index = queue->next_buf_to_fill;
-        buffer = &queue->bufs[queue->next_buf_to_fill];
-	/*check if card is too busy ...*/
-	if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){
-		card->stats.tx_dropped++;
-		goto out;
-	}
-	/*let's force to non-packing and get a new SBAL*/
-	flush_cnt += qeth_tso_get_queue_buffer(queue);
-	buffer = &queue->bufs[queue->next_buf_to_fill];
-	if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) {
-		card->stats.tx_dropped++;
-		goto out;
-	}
-	flush_cnt += qeth_tso_fill_buffer(buffer, skb);
-	queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
-				   QDIO_MAX_BUFFERS_PER_Q;
-out:
-	atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
-	if (flush_cnt)
-		qeth_flush_buffers(queue, 0, start_index, flush_cnt);
-	/*do some statistics */
-	card->stats.tx_packets++;
-	card->stats.tx_bytes += skb->len;
-	return 0;
-}
diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h
index 83504de..ad33e6f 100644
--- a/drivers/s390/net/qeth_tso.h
+++ b/drivers/s390/net/qeth_tso.h
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/s390/net/qeth_tso.h ($Revision: 1.4 $)
+ * linux/drivers/s390/net/qeth_tso.h ($Revision: 1.7 $)
  *
  * Header file for qeth TCP Segmentation Offload support.
  *
@@ -7,52 +7,148 @@
  *
  *    Author(s): Frank Pavlic <pavlic@de.ibm.com>
  *
- *    $Revision: 1.4 $	 $Date: 2005/03/24 09:04:18 $
+ *    $Revision: 1.7 $	 $Date: 2005/05/04 20:19:18 $
  *
  */
 #ifndef __QETH_TSO_H__
 #define __QETH_TSO_H__
 
+#include <linux/skbuff.h>
+#include <linux/tcp.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <net/ip6_checksum.h>
+#include "qeth.h"
+#include "qeth_mpc.h"
 
-extern int
-qeth_tso_send_packet(struct qeth_card *, struct sk_buff *,
-		     struct qeth_qdio_out_q *, int , int);
 
-struct qeth_hdr_ext_tso {
-        __u16 hdr_tot_len;
-        __u8  imb_hdr_no;
-        __u8  reserved;
-        __u8  hdr_type;
-        __u8  hdr_version;
-        __u16 hdr_len;
-        __u32 payload_len;
-        __u16 mss;
-        __u16 dg_hdr_len;
-        __u8  padding[16];
-} __attribute__ ((packed));
+static inline struct qeth_hdr_tso *
+qeth_tso_prepare_skb(struct qeth_card *card, struct sk_buff **skb)
+{
+	QETH_DBF_TEXT(trace, 5, "tsoprsk");
+	return qeth_push_skb(card, skb, sizeof(struct qeth_hdr_tso));
+}
 
-struct qeth_hdr_tso {
-        struct qeth_hdr hdr; 	/*hdr->hdr.l3.xxx*/
-	struct qeth_hdr_ext_tso ext;
-} __attribute__ ((packed));
+/**
+ * fill header for a TSO packet
+ */
+static inline void
+qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb)
+{
+	struct qeth_hdr_tso *hdr;
+	struct tcphdr *tcph;
+	struct iphdr *iph;
 
-/*some helper functions*/
+	QETH_DBF_TEXT(trace, 5, "tsofhdr");
+
+	hdr  = (struct qeth_hdr_tso *) skb->data;
+	iph  = skb->nh.iph;
+	tcph = skb->h.th;
+	/*fix header to TSO values ...*/
+	hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO;
+	/*set values which are fix for the first approach ...*/
+	hdr->ext.hdr_tot_len = (__u16) sizeof(struct qeth_hdr_ext_tso);
+	hdr->ext.imb_hdr_no  = 1;
+	hdr->ext.hdr_type    = 1;
+	hdr->ext.hdr_version = 1;
+	hdr->ext.hdr_len     = 28;
+	/*insert non-fix values */
+	hdr->ext.mss = skb_shinfo(skb)->tso_size;
+	hdr->ext.dg_hdr_len = (__u16)(iph->ihl*4 + tcph->doff*4);
+	hdr->ext.payload_len = (__u16)(skb->len - hdr->ext.dg_hdr_len -
+				       sizeof(struct qeth_hdr_tso));
+}
+
+/**
+ * change some header values as requested by hardware
+ */
+static inline void
+qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb)
+{
+	struct iphdr *iph;
+	struct ipv6hdr *ip6h;
+	struct tcphdr *tcph;
+
+	iph  = skb->nh.iph;
+	ip6h = skb->nh.ipv6h;
+	tcph = skb->h.th;
+
+	tcph->check = 0;
+	if (skb->protocol == ETH_P_IPV6) {
+		ip6h->payload_len = 0;
+		tcph->check = ~csum_ipv6_magic(&ip6h->saddr, &ip6h->daddr,
+					       0, IPPROTO_TCP, 0);
+		return;
+	}
+	/*OSA want us to set these values ...*/
+	tcph->check = ~csum_tcpudp_magic(iph->saddr, iph->daddr,
+					 0, IPPROTO_TCP, 0);
+	iph->tot_len = 0;
+	iph->check = 0;
+}
 
 static inline int
-qeth_get_elements_no(struct qeth_card *card, void *hdr, struct sk_buff *skb)
+qeth_tso_prepare_packet(struct qeth_card *card, struct sk_buff *skb,
+			int ipv, int cast_type)
 {
-	int elements_needed = 0;
+	struct qeth_hdr_tso *hdr;
 
-	if (skb_shinfo(skb)->nr_frags > 0)
-		elements_needed = (skb_shinfo(skb)->nr_frags + 1);
-	if (elements_needed == 0 )
-		elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE)
-					+ skb->len) >> PAGE_SHIFT);
-	if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){
-		PRINT_ERR("qeth_do_send_packet: invalid size of "
-			  "IP packet. Discarded.");
-		return 0;
+	QETH_DBF_TEXT(trace, 5, "tsoprep");
+
+	hdr = (struct qeth_hdr_tso *) qeth_tso_prepare_skb(card, &skb);
+	if (hdr == NULL) {
+		QETH_DBF_TEXT(trace, 4, "tsoperr");
+		return -ENOMEM;
 	}
-	return elements_needed;
+	memset(hdr, 0, sizeof(struct qeth_hdr_tso));
+	/*fill first 32 bytes of  qdio header as used
+	 *FIXME: TSO has two struct members
+	 * with different names but same size
+	 * */
+	qeth_fill_header(card, &hdr->hdr, skb, ipv, cast_type);
+	qeth_tso_fill_header(card, skb);
+	qeth_tso_set_tcpip_header(card, skb);
+	return 0;
+}
+
+static inline void
+__qeth_fill_buffer_frag(struct sk_buff *skb, struct qdio_buffer *buffer,
+			int is_tso, int *next_element_to_fill)
+{
+	struct skb_frag_struct *frag;
+	int fragno;
+	unsigned long addr;
+	int element, cnt, dlen;
+	
+	fragno = skb_shinfo(skb)->nr_frags;
+	element = *next_element_to_fill;
+	dlen = 0;
+	
+	if (is_tso)
+		buffer->element[element].flags =
+			SBAL_FLAGS_MIDDLE_FRAG;
+	else
+		buffer->element[element].flags =
+			SBAL_FLAGS_FIRST_FRAG;
+	if ( (dlen = (skb->len - skb->data_len)) ) {
+		buffer->element[element].addr = skb->data;
+		buffer->element[element].length = dlen;
+		element++;
+	}
+	for (cnt = 0; cnt < fragno; cnt++) {
+		frag = &skb_shinfo(skb)->frags[cnt];
+		addr = (page_to_pfn(frag->page) << PAGE_SHIFT) +
+			frag->page_offset;
+		buffer->element[element].addr = (char *)addr;
+		buffer->element[element].length = frag->size;
+		if (cnt < (fragno - 1))
+			buffer->element[element].flags =
+				SBAL_FLAGS_MIDDLE_FRAG;
+		else
+			buffer->element[element].flags =
+				SBAL_FLAGS_LAST_FRAG;
+		element++;
+	}
+	*next_element_to_fill = element;
 }
 #endif /* __QETH_TSO_H__ */