upgrade to fedora-2.6.12-1.1398.FC4 + vserver 2.0.rc7
[linux-2.6.git] / drivers / s390 / net / qeth_main.c
index de83f3d..8490fb8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.170 $)
+ * 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.170 $        $Date: 2004/11/17 09:54:06 $
+ *    $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
@@ -41,16 +41,9 @@ qeth_eyecatcher(void)
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/mm.h>
-
-#include <asm/io.h>
-#include <asm/ebcdic.h>
-#include <linux/ctype.h>
-#include <asm/semaphore.h>
-#include <asm/timex.h>
 #include <linux/ip.h>
 #include <linux/inetdevice.h>
 #include <linux/netdevice.h>
@@ -62,23 +55,32 @@ qeth_eyecatcher(void)
 #include <linux/tcp.h>
 #include <linux/icmp.h>
 #include <linux/skbuff.h>
-#include <net/route.h>
-#include <net/arp.h>
 #include <linux/in.h>
 #include <linux/igmp.h>
-#include <net/ip.h>
-#include <asm/uaccess.h>
 #include <linux/init.h>
 #include <linux/reboot.h>
-#include <asm/qeth.h>
 #include <linux/mii.h>
 #include <linux/rcupdate.h>
+#include <linux/ethtool.h>
+
+#include <net/arp.h>
+#include <net/ip.h>
+#include <net/route.h>
+
+#include <asm/ebcdic.h>
+#include <asm/io.h>
+#include <asm/qeth.h>
+#include <asm/timex.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
 
 #include "qeth.h"
 #include "qeth_mpc.h"
 #include "qeth_fs.h"
+#include "qeth_eddp.h"
+#include "qeth_tso.h"
 
-#define VERSION_QETH_C "$Revision: 1.170 $"
+#define VERSION_QETH_C "$Revision: 1.214 $"
 static const char *version = "qeth S/390 OSA-Express driver";
 
 /**
@@ -88,7 +90,7 @@ static debug_info_t *qeth_dbf_setup = NULL;
 static debug_info_t *qeth_dbf_data = NULL;
 static debug_info_t *qeth_dbf_misc = NULL;
 static debug_info_t *qeth_dbf_control = NULL;
-static debug_info_t *qeth_dbf_trace = NULL;
+debug_info_t *qeth_dbf_trace = NULL;
 static debug_info_t *qeth_dbf_sense = NULL;
 static debug_info_t *qeth_dbf_qerr = NULL;
 
@@ -156,6 +158,9 @@ qeth_irq_tasklet(unsigned long);
 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);
 
@@ -508,32 +513,45 @@ qeth_irq_tasklet(unsigned long data)
        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;
        enum qeth_card_states recover_flag;
 
        QETH_DBF_TEXT(setup, 3, "setoffl");
        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;
        }
-       ccw_device_set_offline(CARD_DDEV(card));
-       ccw_device_set_offline(CARD_WDEV(card));
-       ccw_device_set_offline(CARD_RDEV(card));
+       if ((rc = ccw_device_set_offline(CARD_DDEV(card))) ||
+           (rc = ccw_device_set_offline(CARD_WDEV(card))) ||
+           (rc = ccw_device_set_offline(CARD_RDEV(card)))) {
+               QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+       }
        if (recover_flag == CARD_STATE_UP)
                card->state = CARD_STATE_RECOVER;
        qeth_notify_processes();
        return 0;
 }
 
+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);
+
+
 static void
 qeth_remove_device(struct ccwgroup_device *cgdev)
 {
@@ -546,6 +564,9 @@ qeth_remove_device(struct ccwgroup_device *cgdev)
        if (!card)
                return;
 
+       if (qeth_wait_for_threads(card, 0xffffffff))
+               return;
+
        if (cgdev->state == CCWGROUP_ONLINE){
                card->use_hard_stop = 1;
                qeth_set_offline(cgdev);
@@ -621,7 +642,7 @@ __qeth_ref_ip_on_card(struct qeth_card *card, struct qeth_ipaddr *todo,
        if (todo->users > 0){
                /* for VIPA and RXIP limit refcount to 1 */
                if (todo->type != QETH_IP_TYPE_NORMAL)
-                       addr->users = 1;
+                       todo->users = 1;
                return 1;
        } else
                return 0;
@@ -941,8 +962,8 @@ qeth_recover(void *ptr)
        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));
@@ -1061,6 +1082,35 @@ qeth_setup_card(struct qeth_card *card)
        return 0;
 }
 
+static int
+is_1920_device (struct qeth_card *card)
+{
+       int single_queue = 0;
+       struct ccw_device *ccwdev;
+       struct channelPath_dsc {
+               u8 flags;
+               u8 lsn;
+               u8 desc;
+               u8 chpid;
+               u8 swla;
+               u8 zeroes;
+               u8 chla;
+               u8 chpp;
+       } *chp_dsc;
+
+       QETH_DBF_TEXT(setup, 2, "chk_1920");
+
+       ccwdev = card->data.ccwdev;
+       chp_dsc = (struct channelPath_dsc *)ccw_device_get_chp_desc(ccwdev, 0);
+       if (chp_dsc != NULL) {
+               /* CHPP field bit 6 == 1 -> single queue */
+               single_queue = ((chp_dsc->chpp & 0x02) == 0x02);
+               kfree(chp_dsc);
+       }
+       QETH_DBF_TEXT_(setup, 2, "rc:%x", single_queue);
+       return single_queue;
+}
+
 static int
 qeth_determine_card_type(struct qeth_card *card)
 {
@@ -1072,7 +1122,14 @@ qeth_determine_card_type(struct qeth_card *card)
                if ((CARD_RDEV(card)->id.dev_type == known_devices[i][2]) &&
                    (CARD_RDEV(card)->id.dev_model == known_devices[i][3])) {
                        card->info.type = known_devices[i][4];
-                       card->qdio.no_out_queues = known_devices[i][8];
+                       if (is_1920_device(card)) {
+                               PRINT_INFO("Priority Queueing not able "
+                                          "due to hardware limitations!\n");
+                               card->qdio.no_out_queues = 1;
+                               card->qdio.default_out_queue = 0;
+                       } else {
+                               card->qdio.no_out_queues = known_devices[i][8];
+                       }
                        card->info.is_multicast_different = known_devices[i][9];
                        return 0;
                }
@@ -1103,6 +1160,10 @@ qeth_probe_device(struct ccwgroup_device *gdev)
                QETH_DBF_TEXT_(setup, 2, "1err%d", -ENOMEM);
                return -ENOMEM;
        }
+       card->read.ccwdev  = gdev->cdev[0];
+       card->write.ccwdev = gdev->cdev[1];
+       card->data.ccwdev  = gdev->cdev[2];
+
        if ((rc = qeth_setup_card(card))){
                QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
                put_device(dev);
@@ -1121,9 +1182,6 @@ qeth_probe_device(struct ccwgroup_device *gdev)
                qeth_free_card(card);
                return rc;
        }
-       card->read.ccwdev  = gdev->cdev[0];
-       card->write.ccwdev = gdev->cdev[1];
-       card->data.ccwdev  = gdev->cdev[2];
        if ((rc = qeth_determine_card_type(card))){
                PRINT_WARN("%s: not a valid card type\n", __func__);
                QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
@@ -1311,6 +1369,7 @@ qeth_idx_activate_get_answer(struct qeth_channel *channel,
        if (channel->state != CH_STATE_UP){
                rc = -ETIME;
                QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
+               qeth_clear_cmd_buffers(channel);
        } else
                rc = 0;
        return rc;
@@ -1377,6 +1436,7 @@ qeth_idx_activate_channel(struct qeth_channel *channel,
        if (channel->state != CH_STATE_ACTIVATING) {
                PRINT_WARN("qeth: IDX activate timed out!\n");
                QETH_DBF_TEXT_(setup, 2, "2err%d", -ETIME);
+               qeth_clear_cmd_buffers(channel);
                return -ETIME;
        }
        return qeth_idx_activate_get_answer(channel,idx_reply_cb);
@@ -1601,7 +1661,7 @@ qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
                                           "there is a network problem or "
                                           "someone pulled the cable or "
                                           "disabled the port.\n",
-                                          card->info.if_name,
+                                          QETH_CARD_IFNAME(card),
                                           card->info.chpid);
                                card->lan_online = 0;
                                netif_carrier_off(card->dev);
@@ -1610,7 +1670,7 @@ qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
                                PRINT_INFO("Link reestablished on %s "
                                           "(CHPID 0x%X). Scheduling "
                                           "IP address reset.\n",
-                                          card->info.if_name,
+                                          QETH_CARD_IFNAME(card),
                                           card->info.chpid);
                                card->lan_online = 1;
                                netif_carrier_on(card->dev);
@@ -1620,10 +1680,7 @@ qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
                                QETH_DBF_TEXT(trace,3, "irla");
                                break;
                        case IPA_CMD_UNREGISTER_LOCAL_ADDR:
-                               PRINT_WARN("probably problem on %s: "
-                                          "received IPA command 0x%X\n",
-                                          card->info.if_name,
-                                          cmd->hdr.command);
+                               QETH_DBF_TEXT(trace,3, "urla");
                                break;
                        default:
                                PRINT_WARN("Received data is IPA "
@@ -1703,7 +1760,7 @@ qeth_send_control_data_cb(struct qeth_channel *channel,
                                                        (unsigned long)iob);
                        }
                        if (cmd)
-                               reply->rc = (s16) cmd->hdr.return_code;
+                               reply->rc = (u16) cmd->hdr.return_code;
                        else if (iob->rc)
                                reply->rc = iob->rc;
                        if (keep_reply) {
@@ -2104,9 +2161,15 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer,
        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;
@@ -2139,7 +2202,7 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer,
 no_mem:
        if (net_ratelimit()){
                PRINT_WARN("No memory for packet received on %s.\n",
-                          card->info.if_name);
+                          QETH_CARD_IFNAME(card));
                QETH_DBF_TEXT(trace,2,"noskbmem");
                QETH_DBF_TEXT_(trace,2,"%s",CARD_BUS_ID(card));
        }
@@ -2181,14 +2244,68 @@ qeth_type_trans(struct sk_buff *skb, struct net_device *dev)
 }
 
 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;
 
@@ -2205,10 +2322,7 @@ qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
 #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);
@@ -2229,6 +2343,16 @@ qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
        fake_hdr->h_proto = skb->protocol;
 }
 
+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)
@@ -2252,19 +2376,25 @@ qeth_layer2_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
                        struct qeth_hdr *hdr)
 {
        unsigned short vlan_id = 0;
+#ifdef CONFIG_QETH_VLAN
+       struct vlan_hdr *vhdr;
+#endif
 
        skb->pkt_type = PACKET_HOST;
+       skb->protocol = qeth_type_trans(skb, skb->dev);
        if (card->options.checksum_type == NO_CHECKSUMMING)
                skb->ip_summed = CHECKSUM_UNNECESSARY;
        else
                skb->ip_summed = CHECKSUM_NONE;
 #ifdef CONFIG_QETH_VLAN
        if (hdr->hdr.l2.flags[2] & (QETH_LAYER2_FLAG_VLAN)) {
-               skb_pull(skb, VLAN_HLEN);
+               vhdr = (struct vlan_hdr *) skb->data;
+               skb->protocol =
+                       __constant_htons(vhdr->h_vlan_encapsulated_proto);
                vlan_id = hdr->hdr.l2.vlan_id;
+               skb_pull(skb, VLAN_HLEN);
        }
 #endif
-       skb->protocol = qeth_type_trans(skb, skb->dev);
        return vlan_id;
 }
 
@@ -2410,14 +2540,15 @@ qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
        if (buf->buffer->element[0].flags & 0x40)
                atomic_dec(&queue->set_pci_flags_count);
 
+       while ((skb = skb_dequeue(&buf->skb_list))){
+               atomic_dec(&skb->users);
+               dev_kfree_skb_any(skb);
+       }
+       qeth_eddp_buf_release_contexts(buf);
        for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i){
                buf->buffer->element[i].length = 0;
                buf->buffer->element[i].addr = NULL;
                buf->buffer->element[i].flags = 0;
-               while ((skb = skb_dequeue(&buf->skb_list))){
-                       atomic_dec(&skb->users);
-                       dev_kfree_skb_any(skb);
-               }
        }
        buf->next_element_to_fill = 0;
        atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY);
@@ -2567,7 +2698,7 @@ qeth_handle_send_error(struct qeth_card *card,
        return QETH_SEND_ERROR_LINK_FAILURE;
 }
 
-static inline void
+void
 qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
                   int index, int count)
 {
@@ -2608,9 +2739,6 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
                                atomic_inc(&queue->set_pci_flags_count);
                                buf->buffer->element[0].flags |= 0x40;
                        }
-#ifdef CONFIG_QETH_PERF_STATS
-                       queue->card->perf_stats.bufs_sent_pack++;
-#endif
                }
        }
 
@@ -2732,6 +2860,7 @@ qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
 {
        int index;
        int flush_cnt = 0;
+       int q_was_packing = 0;
 
        /*
         * check if weed have to switch to non-packing mode or if
@@ -2746,20 +2875,22 @@ qeth_check_outbound_queue(struct qeth_qdio_out_q *queue)
                         * do_send_packet. So, we check if there is a
                         * packing buffer to be flushed here.
                         */
-                       /* TODO: try if we get a performance improvement
-                        * by calling netif_stop_queue here */
-                       /* save start index for flushing */
+                       netif_stop_queue(queue->card->dev);
                        index = queue->next_buf_to_fill;
+                       q_was_packing = queue->do_pack;
                        flush_cnt += qeth_switch_to_nonpacking_if_needed(queue);
                        if (!flush_cnt &&
                            !atomic_read(&queue->set_pci_flags_count))
                                flush_cnt +=
                                        qeth_flush_buffers_on_no_pci(queue);
-                       /* were done with updating critical queue members */
-                       atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
-                       /* flushing can be done outside the lock */
+#ifdef CONFIG_QETH_PERF_STATS
+                       if (q_was_packing)
+                               queue->card->perf_stats.bufs_sent_pack +=
+                                       flush_cnt;
+#endif
                        if (flush_cnt)
                                qeth_flush_buffers(queue, 1, index, flush_cnt);
+                       atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
                }
        }
 }
@@ -2789,7 +2920,6 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
                        return;
                }
        }
-
 #ifdef CONFIG_QETH_PERF_STATS
        card->perf_stats.outbound_handler_cnt++;
        card->perf_stats.outbound_handler_start_time = qeth_get_micros();
@@ -2810,24 +2940,16 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
        if (card->info.type != QETH_CARD_TYPE_IQD)
                qeth_check_outbound_queue(queue);
 
-       netif_wake_queue(card->dev);
+       netif_wake_queue(queue->card->dev);
 #ifdef CONFIG_QETH_PERF_STATS
        card->perf_stats.outbound_handler_time += qeth_get_micros() -
                card->perf_stats.outbound_handler_start_time;
 #endif
 }
 
-static char*
-qeth_create_qib_param_field(struct qeth_card *card)
+static void
+qeth_create_qib_param_field(struct qeth_card *card, char *param_field)
 {
-       char *param_field;
-
-       param_field = kmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char),
-                             GFP_KERNEL);
-       if (!param_field)
-               return NULL;
-
-       memset(param_field, 0, QDIO_MAX_BUFFERS_PER_Q * sizeof(char));
 
        param_field[0] = _ascebc['P'];
        param_field[1] = _ascebc['C'];
@@ -2836,8 +2958,18 @@ qeth_create_qib_param_field(struct qeth_card *card)
        *((unsigned int *) (&param_field[4])) = QETH_PCI_THRESHOLD_A(card);
        *((unsigned int *) (&param_field[8])) = QETH_PCI_THRESHOLD_B(card);
        *((unsigned int *) (&param_field[12])) = QETH_PCI_TIMER_VALUE(card);
+}
 
-       return param_field;
+static void
+qeth_create_qib_param_field_blkt(struct qeth_card *card, char *param_field)
+{
+       param_field[16] = _ascebc['B'];
+        param_field[17] = _ascebc['L'];
+        param_field[18] = _ascebc['K'];
+        param_field[19] = _ascebc['T'];
+        *((unsigned int *) (&param_field[20])) = card->info.blkt.time_total;
+        *((unsigned int *) (&param_field[24])) = card->info.blkt.inter_packet;
+        *((unsigned int *) (&param_field[28])) = card->info.blkt.inter_packet_jumbo;
 }
 
 static void
@@ -2887,7 +3019,7 @@ qeth_alloc_buffer_pool(struct qeth_card *card)
        void *ptr;
        int i, j;
 
-       QETH_DBF_TEXT(trace,5,"clwkpool");
+       QETH_DBF_TEXT(trace,5,"alocpool");
        for (i = 0; i < card->qdio.init_pool.buf_count; ++i){
                pool_entry = kmalloc(sizeof(*pool_entry), GFP_KERNEL);
                if (!pool_entry){
@@ -2975,12 +3107,13 @@ qeth_alloc_qdio_buffers(struct qeth_card *card)
                QETH_DBF_HEX(setup, 2, &card->qdio.out_qs[i], sizeof(void *));
                memset(card->qdio.out_qs[i], 0, sizeof(struct qeth_qdio_out_q));
                card->qdio.out_qs[i]->queue_no = i;
-               /* give inbound qeth_qdio_buffers their qdio_buffers */
+               /* give outbound qeth_qdio_buffers their qdio_buffers */
                for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j){
                        card->qdio.out_qs[i]->bufs[j].buffer =
                                &card->qdio.out_qs[i]->qdio_bufs[j];
                        skb_queue_head_init(&card->qdio.out_qs[i]->bufs[j].
                                            skb_list);
+                       INIT_LIST_HEAD(&card->qdio.out_qs[i]->bufs[j].ctx_list);
                }
        }
        card->qdio.state = QETH_QDIO_ALLOCATED;
@@ -3097,10 +3230,17 @@ qeth_qdio_establish(struct qeth_card *card)
        int rc;
 
        QETH_DBF_TEXT(setup, 2, "qdioest");
-       qib_param_field = qeth_create_qib_param_field(card);
-       if (!qib_param_field)
+
+       qib_param_field = kmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char),
+                             GFP_KERNEL);
+       if (!qib_param_field)
                return -ENOMEM;
 
+       memset(qib_param_field, 0, QDIO_MAX_BUFFERS_PER_Q * sizeof(char));
+
+       qeth_create_qib_param_field(card, qib_param_field);
+       qeth_create_qib_param_field_blkt(card, qib_param_field);
+
        in_sbal_ptrs = kmalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(void *),
                               GFP_KERNEL);
        if (!in_sbal_ptrs) {
@@ -3260,13 +3400,15 @@ qeth_qdio_clear_card(struct qeth_card *card, int use_halt)
 
        QETH_DBF_TEXT(trace,3,"qdioclr");
        if (card->qdio.state == QETH_QDIO_ESTABLISHED){
-               qdio_cleanup(CARD_DDEV(card),
+               if ((rc = qdio_cleanup(CARD_DDEV(card),
                             (card->info.type == QETH_CARD_TYPE_IQD) ?
                             QDIO_FLAG_CLEANUP_USING_HALT :
-                            QDIO_FLAG_CLEANUP_USING_CLEAR);
+                            QDIO_FLAG_CLEANUP_USING_CLEAR)))
+                       QETH_DBF_TEXT_(trace, 3, "1err%d", rc);
                card->qdio.state = QETH_QDIO_ALLOCATED;
        }
-       rc = qeth_clear_halt_card(card, use_halt);
+       if ((rc = qeth_clear_halt_card(card, use_halt)))
+               QETH_DBF_TEXT_(trace, 3, "2err%d", rc);
        card->state = CARD_STATE_DOWN;
        return rc;
 }
@@ -3368,6 +3510,33 @@ qeth_get_netdevice(enum qeth_card_types type, enum qeth_link_types linktype)
        return dev;
 }
 
+/*hard_header fake function; used in case fake_ll is set */
+static int
+qeth_fake_header(struct sk_buff *skb, struct net_device *dev,
+                    unsigned short type, void *daddr, void *saddr,
+                    unsigned len)
+{
+       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;
+
+       } 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
 qeth_send_packet(struct qeth_card *, struct sk_buff *);
 
@@ -3397,24 +3566,19 @@ qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        card->perf_stats.outbound_cnt++;
        card->perf_stats.outbound_start_time = qeth_get_micros();
 #endif
-       /*
-        * We only call netif_stop_queue in case of errors. Since we've
-        * got our own synchronization on queues we can keep the stack's
-        * queue running.
-        */
-       if ((rc = qeth_send_packet(card, skb))){
+       netif_stop_queue(dev);
+       if ((rc = qeth_send_packet(card, skb))) {
                if (rc == -EBUSY) {
-                       netif_stop_queue(dev);
-                       rc = NETDEV_TX_BUSY;
+                       return NETDEV_TX_BUSY;
                } else {
                        card->stats.tx_errors++;
                        card->stats.tx_dropped++;
                        dev_kfree_skb_any(skb);
-                       /* set to OK; otherwise ksoftirqd goes to 100% */
+                       /*set to OK; otherwise ksoftirqd goes to 100% */
                        rc = NETDEV_TX_OK;
                }
        }
-
+       netif_wake_queue(dev);
 #ifdef CONFIG_QETH_PERF_STATS
        card->perf_stats.outbound_time += qeth_get_micros() -
                card->perf_stats.outbound_start_time;
@@ -3608,8 +3772,9 @@ qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb,
                        /* TODO: IPv6!!! */
                }
                return card->qdio.default_out_queue;
+       case 1: /* fallthrough for single-out-queue 1920-device */
        default:
-               return 0;
+               return card->qdio.default_out_queue;
        }
 }
 
@@ -3630,22 +3795,12 @@ static inline int
 qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
                 struct qeth_hdr **hdr, int ipv)
 {
-       struct sk_buff *new_skb;
 #ifdef CONFIG_QETH_VLAN
        u16 *tag;
 #endif
 
        QETH_DBF_TEXT(trace, 6, "prepskb");
-       if (skb_headroom(*skb) < sizeof(struct qeth_hdr)){
-               new_skb = skb_realloc_headroom(*skb, sizeof(struct qeth_hdr));
-               if (!new_skb) {
-                       PRINT_ERR("qeth_prepare_skb: could "
-                                 "not realloc headroom for qeth_hdr "
-                                 "on interface %s", card->info.if_name);
-                       return -ENOMEM;
-               }
-               *skb = new_skb;
-       }
+
 #ifdef CONFIG_QETH_VLAN
        if (card->vlangrp && vlan_tx_tag_present(*skb) &&
            ((ipv == 6) || card->options.layer2) ) {
@@ -3667,20 +3822,10 @@ qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
                *(tag + 1) = htons(vlan_tx_tag_get(*skb));
        }
 #endif
-       *hdr = (struct qeth_hdr *) skb_push(*skb, sizeof(struct qeth_hdr));
-       /*
-        * sanity check, the Linux memory allocation scheme should
-        * never present us cases like this one (the 32bytes header plus
-        * the first 40 bytes of the paket cross a 4k boundary)
-        */
-       if ((((unsigned long) *hdr) & (~(PAGE_SIZE - 1))) !=
-           (((unsigned long) *hdr + sizeof(struct qeth_hdr) +
-             QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
-               PRINT_ERR("qeth_prepare_skb: misaligned "
-                         "packet on interface %s. Discarded.",
-                         card->info.if_name);
+       *hdr = (struct qeth_hdr *)
+               qeth_push_skb(card, skb, sizeof(struct qeth_hdr));
+       if (hdr == NULL)
                return -EINVAL;
-       }
        return 0;
 }
 
@@ -3772,12 +3917,13 @@ qeth_layer2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
 #endif
 }
 
-static inline void
+void
 qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                struct sk_buff *skb, int ipv, int cast_type)
 {
        QETH_DBF_TEXT(trace, 6, "fillhdr");
 
+       memset(hdr, 0, sizeof(struct qeth_hdr));
        if (card->options.layer2) {
                qeth_layer2_fill_header(card, hdr, skb, cast_type);
                return;
@@ -3817,9 +3963,15 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                        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 :
@@ -3828,30 +3980,30 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
        }
 }
 
-static inline int
-qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf,
-                char *data, struct sk_buff *skb)
+static inline void
+__qeth_fill_buffer(struct sk_buff *skb, struct qdio_buffer *buffer,
+                  int is_tso, int *next_element_to_fill)
 {
-       struct qdio_buffer *buffer;
        int length = skb->len;
        int length_here;
        int element;
-       int first_lap = 1;
+       char *data;
+       int first_lap ;
+
+       element = *next_element_to_fill;
+       data = skb->data;
+       first_lap = (is_tso == 0 ? 1 : 0);
 
-       QETH_DBF_TEXT(trace, 6, "qdfillbf");
-       buffer = buf->buffer;
-       atomic_inc(&skb->users);
-       skb_queue_tail(&buf->skb_list, skb);
-       element = buf->next_element_to_fill;
        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
@@ -3869,11 +4021,51 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf
                element++;
                first_lap = 0;
        }
-       buf->next_element_to_fill = element;
+       *next_element_to_fill = element;
+}
+
+static inline int
+qeth_fill_buffer(struct qeth_qdio_out_q *queue,
+                struct qeth_qdio_out_buffer *buf,
+                struct sk_buff *skb)
+{
+       struct qdio_buffer *buffer;
+       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, large_send,
+                                  (int *)&buf->next_element_to_fill);
+       else
+               __qeth_fill_buffer_frag(skb, buffer, large_send,
+                                       (int *)&buf->next_element_to_fill);
+
        if (!queue->do_pack) {
                QETH_DBF_TEXT(trace, 6, "fillbfnp");
                /* set state to PRIMED -> will be flushed */
                atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
+               flush_cnt = 1;
        } else {
                QETH_DBF_TEXT(trace, 6, "fillbfpa");
 #ifdef CONFIG_QETH_PERF_STATS
@@ -3886,17 +4078,21 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf
                         * -> will be flushed
                         */
                        atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
+                       flush_cnt = 1;
                }
        }
-       return 0;
+       return flush_cnt;
 }
 
 static inline int
 qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                         struct sk_buff *skb, struct qeth_hdr *hdr,
-                        int elements_needed)
+                        int elements_needed,
+                        struct qeth_eddp_context *ctx)
 {
        struct qeth_qdio_out_buffer *buffer;
+       int buffers_needed = 0;
+       int flush_cnt = 0;
        int index;
 
        QETH_DBF_TEXT(trace, 6, "dosndpfa");
@@ -3917,22 +4113,42 @@ qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
                return -EBUSY;
        }
-       queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
-                                 QDIO_MAX_BUFFERS_PER_Q;
+       if (ctx == NULL)
+               queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
+                                         QDIO_MAX_BUFFERS_PER_Q;
+       else {
+               buffers_needed = qeth_eddp_check_buffers_for_context(queue,ctx);
+               if (buffers_needed < 0) {
+                       card->stats.tx_dropped++;
+                       atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
+                       return -EBUSY;
+               }
+               queue->next_buf_to_fill =
+                       (queue->next_buf_to_fill + buffers_needed) %
+                       QDIO_MAX_BUFFERS_PER_Q;
+       }
        atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
-       qeth_fill_buffer(queue, buffer, (char *)hdr, skb);
-       qeth_flush_buffers(queue, 0, index, 1);
+       if (ctx == NULL) {
+               qeth_fill_buffer(queue, buffer, skb);
+               qeth_flush_buffers(queue, 0, index, 1);
+       } else {
+               flush_cnt = qeth_eddp_fill_buffer(queue, ctx, index);
+               WARN_ON(buffers_needed != flush_cnt);
+               qeth_flush_buffers(queue, 0, index, flush_cnt);
+       }
        return 0;
 }
 
 static inline int
 qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                    struct sk_buff *skb, struct qeth_hdr *hdr,
-                   int elements_needed)
+                   int elements_needed, struct qeth_eddp_context *ctx)
 {
        struct qeth_qdio_out_buffer *buffer;
        int start_index;
        int flush_count = 0;
+       int do_pack = 0;
+       int tmp;
        int rc = 0;
 
        QETH_DBF_TEXT(trace, 6, "dosndpkt");
@@ -3955,34 +4171,56 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
        /* check if we need to switch packing state of this queue */
        qeth_switch_to_packing_if_needed(queue);
        if (queue->do_pack){
-               /* does packet fit in current buffer? */
-               if((QETH_MAX_BUFFER_ELEMENTS(card) -
-                   buffer->next_element_to_fill) < elements_needed){
-                       /* ... no -> set state PRIMED */
-                       atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED);
-                       flush_count++;
-                       queue->next_buf_to_fill =
-                               (queue->next_buf_to_fill + 1) %
-                               QDIO_MAX_BUFFERS_PER_Q;
-                       buffer = &queue->bufs[queue->next_buf_to_fill];
-                       /* we did a step forward, so check buffer state again */
-                       if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){
+               do_pack = 1;
+               if (ctx == NULL) {
+                       /* does packet fit in current buffer? */
+                       if((QETH_MAX_BUFFER_ELEMENTS(card) -
+                           buffer->next_element_to_fill) < elements_needed){
+                               /* ... no -> set state PRIMED */
+                               atomic_set(&buffer->state,QETH_QDIO_BUF_PRIMED);
+                               flush_count++;
+                               queue->next_buf_to_fill =
+                                       (queue->next_buf_to_fill + 1) %
+                                       QDIO_MAX_BUFFERS_PER_Q;
+                               buffer = &queue->bufs[queue->next_buf_to_fill];
+                               /* we did a step forward, so check buffer state
+                                * again */
+                               if (atomic_read(&buffer->state) !=
+                                               QETH_QDIO_BUF_EMPTY){
+                                       card->stats.tx_dropped++;
+                                       qeth_flush_buffers(queue, 0, start_index, flush_count);
+                                       atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
+                                       return -EBUSY;
+                               }
+                       }
+               } else {
+                       /* check if we have enough elements (including following
+                        * free buffers) to handle eddp context */
+                       if (qeth_eddp_check_buffers_for_context(queue,ctx) < 0){
+                               printk("eddp tx_dropped 1\n");
                                card->stats.tx_dropped++;
-                               /* return EBUSY because we sent old packet, not
-                                * the current one */
                                rc = -EBUSY;
-                               atomic_set(&queue->state, QETH_OUT_Q_UNLOCKED);
                                goto out;
                        }
                }
        }
-       qeth_fill_buffer(queue, buffer, (char *)hdr, skb);
-       if (atomic_read(&buffer->state) == QETH_QDIO_BUF_PRIMED){
-               /* next time fill the next buffer */
-               flush_count++;
-               queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
-                       QDIO_MAX_BUFFERS_PER_Q;
+       if (ctx == NULL)
+               tmp = qeth_fill_buffer(queue, buffer, skb);
+       else {
+               tmp = qeth_eddp_fill_buffer(queue,ctx,queue->next_buf_to_fill);
+               if (tmp < 0) {
+                       printk("eddp tx_dropped 2\n");
+                       card->stats.tx_dropped++;
+                       rc = - EBUSY;
+                       goto out;
+               }
        }
+       queue->next_buf_to_fill = (queue->next_buf_to_fill + tmp) %
+                                 QDIO_MAX_BUFFERS_PER_Q;
+       flush_count += tmp;
+out:
+       if (flush_count)
+               qeth_flush_buffers(queue, 0, start_index, flush_count);
        /*
         * queue->state will go from LOCKED -> UNLOCKED or from
         * LOCKED_FLUSH -> LOCKED if output_handler wanted to 'notify' us
@@ -3990,6 +4228,8 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
         * In that case we will enter this loop
         */
        while (atomic_dec_return(&queue->state)){
+               flush_count = 0;
+               start_index = queue->next_buf_to_fill;
                /* check if we can go back to non-packing state */
                flush_count += qeth_switch_to_nonpacking_if_needed(queue);
                /*
@@ -3998,15 +4238,37 @@ qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
                 */
                if (!flush_count && !atomic_read(&queue->set_pci_flags_count))
                        flush_count += qeth_flush_buffers_on_no_pci(queue);
+               if (flush_count)
+                       qeth_flush_buffers(queue, 0, start_index, flush_count);
        }
        /* at this point the queue is UNLOCKED again */
-out:
-       if (flush_count)
-               qeth_flush_buffers(queue, 0, start_index, flush_count);
+#ifdef CONFIG_QETH_PERF_STATS
+       if (do_pack)
+               queue->card->perf_stats.bufs_sent_pack += flush_count;
+#endif /* CONFIG_QETH_PERF_STATS */
 
        return rc;
 }
 
+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)
 {
@@ -4014,46 +4276,102 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
        int cast_type;
        struct qeth_qdio_out_q *queue;
        struct qeth_hdr *hdr;
-       int elements_needed;
+       int elements_needed = 0;
+       enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
+       struct qeth_eddp_context *ctx = NULL;
        int rc;
 
        QETH_DBF_TEXT(trace, 6, "sendpkt");
 
-       if (!card->options.layer2)
+       if (!card->options.layer2) {
                ipv = qeth_get_ip_version(skb);
+               if ((card->dev->hard_header == qeth_fake_header) && ipv) {
+                               if ((skb = qeth_pskb_unshare(skb,GFP_ATOMIC)) == NULL) {
+                               card->stats.tx_dropped++;
+                               dev_kfree_skb_irq(skb);
+                               return 0;
+                       }
+                       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);
        if ((cast_type == RTN_BROADCAST) && (card->info.broadcast_capable == 0)){
                card->stats.tx_dropped++;
                card->stats.tx_errors++;
                dev_kfree_skb_any(skb);
-               return 0;
+               return NETDEV_TX_OK;
        }
        queue = card->qdio.out_qs
                [qeth_get_priority_queue(card, skb, ipv, cast_type)];
 
-       if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){
-               QETH_DBF_TEXT_(trace, 4, "1err%d", rc);
-               return rc;
+       if (skb_shinfo(skb)->tso_size)
+               large_send = card->options.large_send;
+
+       /*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_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);
-       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 -EINVAL;
+
+       if (large_send == QETH_LARGE_SEND_EDDP) {
+               ctx = qeth_eddp_create_context(card, skb, hdr);
+               if (ctx == NULL) {
+                       PRINT_WARN("could not create eddp context\n");
+                       return -EINVAL;
+               }
+       } else {
+               elements_needed += qeth_get_elements_no(card,(void*) hdr, skb);
+               if (!elements_needed)
+                       return -EINVAL;
        }
 
        if (card->info.type != QETH_CARD_TYPE_IQD)
                rc = qeth_do_send_packet(card, queue, skb, hdr,
-                                        elements_needed);
+                                        elements_needed, ctx);
        else
                rc = qeth_do_send_packet_fast(card, queue, skb, hdr,
-                                             elements_needed);
-
+                                             elements_needed, ctx);
        if (!rc){
                card->stats.tx_packets++;
                card->stats.tx_bytes += skb->len;
+#ifdef CONFIG_QETH_PERF_STATS
+               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++;
+               }
+               if (skb_shinfo(skb)->nr_frags > 0){
+                       card->perf_stats.sg_skbs_sent++;
+                       /* nr_frags + skb->data */
+                       card->perf_stats.sg_frags_sent +=
+                               skb_shinfo(skb)->nr_frags + 1;
+               }
+#endif /* CONFIG_QETH_PERF_STATS */
+       }
+       if (ctx != NULL) {
+               /* drop creator's reference */
+               qeth_eddp_put_context(ctx);
+               /* free skb; it's not referenced by a buffer */
+               if (rc == 0)
+                       dev_kfree_skb_any(skb);
+
        }
        return rc;
 }
@@ -4191,7 +4509,7 @@ qeth_arp_set_no_entries(struct qeth_card *card, int no_entries)
                return -EOPNOTSUPP;
        if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
                PRINT_WARN("ARP processing not supported "
-                          "on %s!\n", card->info.if_name);
+                          "on %s!\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
        rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
@@ -4201,7 +4519,7 @@ qeth_arp_set_no_entries(struct qeth_card *card, int no_entries)
                tmp = rc;
                PRINT_WARN("Could not set number of ARP entries on %s: "
                           "%s (0x%x/%d)\n",
-                          card->info.if_name, qeth_arp_get_error_cause(&rc),
+                          QETH_CARD_IFNAME(card), qeth_arp_get_error_cause(&rc),
                           tmp, tmp);
        }
        return rc;
@@ -4374,7 +4692,7 @@ qeth_arp_query(struct qeth_card *card, char *udata)
        if (!qeth_is_supported(card,/*IPA_QUERY_ARP_ADDR_INFO*/
                               IPA_ARP_PROCESSING)) {
                PRINT_WARN("ARP processing not supported "
-                          "on %s!\n", card->info.if_name);
+                          "on %s!\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
        /* get size of userspace buffer and mask_bits -> 6 bytes */
@@ -4395,7 +4713,7 @@ qeth_arp_query(struct qeth_card *card, char *udata)
                tmp = rc;
                PRINT_WARN("Error while querying ARP cache on %s: %s "
                           "(0x%x/%d)\n",
-                          card->info.if_name, qeth_arp_get_error_cause(&rc),
+                          QETH_CARD_IFNAME(card), qeth_arp_get_error_cause(&rc),
                           tmp, tmp);
                copy_to_user(udata, qinfo.udata, 4);
        } else {
@@ -4509,9 +4827,11 @@ qeth_snmp_command(struct qeth_card *card, char *udata)
 
        if (card->info.guestlan)
                return -EOPNOTSUPP;
-       if (!qeth_adp_supported(card,IPA_SETADP_SET_SNMP_CONTROL)) {
+
+       if ((!qeth_adp_supported(card,IPA_SETADP_SET_SNMP_CONTROL)) &&
+           (!card->options.layer2) ) {
                PRINT_WARN("SNMP Query MIBS not supported "
-                          "on %s!\n", card->info.if_name);
+                          "on %s!\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
        /* skip 4 bytes (data_len struct member) to get req_len */
@@ -4543,7 +4863,7 @@ qeth_snmp_command(struct qeth_card *card, char *udata)
                                    qeth_snmp_command_cb, (void *)&qinfo);
        if (rc)
                PRINT_WARN("SNMP command failed on %s: (0x%x)\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
         else
                copy_to_user(udata, qinfo.udata, qinfo.udata_len);
 
@@ -4583,7 +4903,7 @@ qeth_arp_add_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry)
                return -EOPNOTSUPP;
        if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
                PRINT_WARN("ARP processing not supported "
-                          "on %s!\n", card->info.if_name);
+                          "on %s!\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
 
@@ -4600,7 +4920,7 @@ qeth_arp_add_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry)
                qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
                PRINT_WARN("Could not add ARP entry for address %s on %s: "
                           "%s (0x%x/%d)\n",
-                          buf, card->info.if_name,
+                          buf, QETH_CARD_IFNAME(card),
                           qeth_arp_get_error_cause(&rc), tmp, tmp);
        }
        return rc;
@@ -4626,7 +4946,7 @@ qeth_arp_remove_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry
                return -EOPNOTSUPP;
        if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
                PRINT_WARN("ARP processing not supported "
-                          "on %s!\n", card->info.if_name);
+                          "on %s!\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
        memcpy(buf, entry, 12);
@@ -4643,7 +4963,7 @@ qeth_arp_remove_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry
                qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
                PRINT_WARN("Could not delete ARP entry for address %s on %s: "
                           "%s (0x%x/%d)\n",
-                          buf, card->info.if_name,
+                          buf, QETH_CARD_IFNAME(card),
                           qeth_arp_get_error_cause(&rc), tmp, tmp);
        }
        return rc;
@@ -4667,7 +4987,7 @@ qeth_arp_flush_cache(struct qeth_card *card)
                return -EOPNOTSUPP;
        if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
                PRINT_WARN("ARP processing not supported "
-                          "on %s!\n", card->info.if_name);
+                          "on %s!\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
        rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
@@ -4675,7 +4995,7 @@ qeth_arp_flush_cache(struct qeth_card *card)
        if (rc){
                tmp = rc;
                PRINT_WARN("Could not flush ARP cache on %s: %s (0x%x/%d)\n",
-                          card->info.if_name, qeth_arp_get_error_cause(&rc),
+                          QETH_CARD_IFNAME(card), qeth_arp_get_error_cause(&rc),
                           tmp, tmp);
        }
        return rc;
@@ -4907,6 +5227,7 @@ out:
 static void
 qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid)
 {
+#ifdef CONFIG_QETH_IPV6
        struct inet6_dev *in6_dev;
        struct inet6_ifaddr *ifa;
        struct qeth_ipaddr *addr;
@@ -4929,6 +5250,7 @@ qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid)
                }
        }
        in6_dev_put(in6_dev);
+#endif /* CONFIG_QETH_IPV6 */
 }
 #else
 #define qeth_free_vlan_addresses6(card, vid)   do{ ; }while(0)
@@ -4950,7 +5272,7 @@ qeth_layer2_send_setdelvlan(struct qeth_card *card, __u16 i,
        rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
         if (rc) {
                 PRINT_ERR("Error in processing VLAN %i on %s: 0x%x. "
-                         "Continuing\n",i, card->info.if_name, rc);
+                         "Continuing\n",i, QETH_CARD_IFNAME(card), rc);
                QETH_DBF_TEXT_(trace, 2, "L2VL%4x", ipacmd);
                QETH_DBF_TEXT_(trace, 2, "L2%s", CARD_BUS_ID(card));
                QETH_DBF_TEXT_(trace, 2, "err%d", rc);
@@ -5039,27 +5361,6 @@ qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np)
        return 0;
 }
 
-#ifdef CONFIG_QETH_IPV6
-int
-qeth_ipv6_generate_eui64(u8 * eui, struct net_device *dev)
-{
-       switch (dev->type) {
-       case ARPHRD_ETHER:
-       case ARPHRD_FDDI:
-       case ARPHRD_IEEE802_TR:
-               if (dev->addr_len != ETH_ALEN)
-                       return -1;
-               memcpy(eui, dev->dev_addr, 3);
-               memcpy(eui + 5, dev->dev_addr + 3, 3);
-               eui[3] = (dev->dev_id >> 8) & 0xff;
-               eui[4] = dev->dev_id & 0xff;
-               return 0;
-       }
-       return -1;
-
-}
-#endif
-
 static void
 qeth_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev)
 {
@@ -5230,7 +5531,8 @@ qeth_add_multicast_ipv6(struct qeth_card *card)
        struct inet6_dev *in6_dev;
 
        QETH_DBF_TEXT(trace,4,"chkmcv6");
-       if (!qeth_is_supported(card, IPA_IPV6))
+       if ((card->options.layer2 == 0) &&
+           (!qeth_is_supported(card, IPA_IPV6)) )
                return ;
 
        in6_dev = in6_dev_get(card->dev);
@@ -5246,7 +5548,10 @@ qeth_add_multicast_ipv6(struct qeth_card *card)
 
 static int
 qeth_layer2_send_setdelmac(struct qeth_card *card, __u8 *mac,
-                          enum qeth_ipa_cmds ipacmd)
+                          enum qeth_ipa_cmds ipacmd,
+                          int (*reply_cb) (struct qeth_card *,
+                                           struct qeth_reply*,
+                                           unsigned long))
 {
        struct qeth_ipa_cmd *cmd;
        struct qeth_cmd_buffer *iob;
@@ -5256,9 +5561,139 @@ qeth_layer2_send_setdelmac(struct qeth_card *card, __u8 *mac,
        cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
         cmd->data.setdelmac.mac_length = OSA_ADDR_LEN;
         memcpy(&cmd->data.setdelmac.mac, mac, OSA_ADDR_LEN);
-       return qeth_send_ipa_cmd(card, iob, NULL, NULL);
+       return qeth_send_ipa_cmd(card, iob, reply_cb, NULL);
+}
+
+static int
+qeth_layer2_send_setgroupmac_cb(struct qeth_card *card,
+                               struct qeth_reply *reply,
+                               unsigned long data)
+{
+       struct qeth_ipa_cmd *cmd;
+       __u8 *mac;
+
+       QETH_DBF_TEXT(trace, 2, "L2Sgmacb");
+       cmd = (struct qeth_ipa_cmd *) data;
+       mac = &cmd->data.setdelmac.mac[0];
+       /* MAC already registered, needed in couple/uncouple case */
+       if (cmd->hdr.return_code == 0x2005) {
+               PRINT_WARN("Group MAC %02x:%02x:%02x:%02x:%02x:%02x " \
+                         "already existing on %s \n",
+                         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+                         QETH_CARD_IFNAME(card));
+               cmd->hdr.return_code = 0;
+       }
+       if (cmd->hdr.return_code)
+               PRINT_ERR("Could not set group MAC " \
+                         "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
+                         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+                         QETH_CARD_IFNAME(card),cmd->hdr.return_code);
+       return 0;
+}
+
+static int
+qeth_layer2_send_setgroupmac(struct qeth_card *card, __u8 *mac)
+{
+       QETH_DBF_TEXT(trace, 2, "L2Sgmac");
+       return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_SETGMAC,
+                                         qeth_layer2_send_setgroupmac_cb);
 }
 
+static int
+qeth_layer2_send_delgroupmac_cb(struct qeth_card *card,
+                               struct qeth_reply *reply,
+                               unsigned long data)
+{
+       struct qeth_ipa_cmd *cmd;
+       __u8 *mac;
+
+       QETH_DBF_TEXT(trace, 2, "L2Dgmacb");
+       cmd = (struct qeth_ipa_cmd *) data;
+       mac = &cmd->data.setdelmac.mac[0];
+       if (cmd->hdr.return_code)
+               PRINT_ERR("Could not delete group MAC " \
+                         "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
+                         mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+                         QETH_CARD_IFNAME(card), cmd->hdr.return_code);
+       return 0;
+}
+
+static int
+qeth_layer2_send_delgroupmac(struct qeth_card *card, __u8 *mac)
+{
+       QETH_DBF_TEXT(trace, 2, "L2Dgmac");
+       return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_DELGMAC,
+                                         qeth_layer2_send_delgroupmac_cb);
+}
+
+static int
+qeth_layer2_send_setmac_cb(struct qeth_card *card,
+                          struct qeth_reply *reply,
+                          unsigned long data)
+{
+       struct qeth_ipa_cmd *cmd;
+
+       QETH_DBF_TEXT(trace, 2, "L2Smaccb");
+       cmd = (struct qeth_ipa_cmd *) data;
+       if (cmd->hdr.return_code) {
+               QETH_DBF_TEXT_(trace, 2, "L2er%x", cmd->hdr.return_code);
+               PRINT_WARN("Error in registering MAC address on " \
+                          "device %s: x%x\n", CARD_BUS_ID(card),
+                          cmd->hdr.return_code);
+               card->info.layer2_mac_registered = 0;
+               cmd->hdr.return_code = -EIO;
+       } else {
+               card->info.layer2_mac_registered = 1;
+               memcpy(card->dev->dev_addr,cmd->data.setdelmac.mac,
+                      OSA_ADDR_LEN);
+               PRINT_INFO("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
+                          "successfully registered on device %s\n",
+                          card->dev->dev_addr[0], card->dev->dev_addr[1],
+                          card->dev->dev_addr[2], card->dev->dev_addr[3],
+                          card->dev->dev_addr[4], card->dev->dev_addr[5],
+                          card->dev->name);
+       }
+       return 0;
+}
+
+static int
+qeth_layer2_send_setmac(struct qeth_card *card, __u8 *mac)
+{
+       QETH_DBF_TEXT(trace, 2, "L2Setmac");
+       return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_SETVMAC,
+                                         qeth_layer2_send_setmac_cb);
+}
+
+static int
+qeth_layer2_send_delmac_cb(struct qeth_card *card,
+                          struct qeth_reply *reply,
+                          unsigned long data)
+{
+       struct qeth_ipa_cmd *cmd;
+
+       QETH_DBF_TEXT(trace, 2, "L2Dmaccb");
+       cmd = (struct qeth_ipa_cmd *) data;
+       if (cmd->hdr.return_code) {
+               PRINT_WARN("Error in deregistering MAC address on " \
+                          "device %s: x%x\n", CARD_BUS_ID(card),
+                          cmd->hdr.return_code);
+               QETH_DBF_TEXT_(trace, 2, "err%d", cmd->hdr.return_code);
+               cmd->hdr.return_code = -EIO;
+               return 0;
+       }
+       card->info.layer2_mac_registered = 0;
+
+       return 0;
+}
+static int
+qeth_layer2_send_delmac(struct qeth_card *card, __u8 *mac)
+{
+       QETH_DBF_TEXT(trace, 2, "L2Delmac");
+       if (!card->info.layer2_mac_registered)
+               return 0;
+       return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_DELVMAC,
+                                         qeth_layer2_send_delmac_cb);
+}
 
 static int
 qeth_layer2_set_mac_address(struct net_device *dev, void *p)
@@ -5283,32 +5718,9 @@ qeth_layer2_set_mac_address(struct net_device *dev, void *p)
        }
        QETH_DBF_TEXT_(trace, 3, "%s", CARD_BUS_ID(card));
        QETH_DBF_HEX(trace, 3, addr->sa_data, OSA_ADDR_LEN);
-       if (card->info.layer2_mac_registered)
-               rc = qeth_layer2_send_setdelmac(card, &card->dev->dev_addr[0],
-                                               IPA_CMD_DELVMAC);
-       if (rc) {
-               PRINT_WARN("Error in deregistering MAC address on " \
-                          "device %s: x%x\n", CARD_BUS_ID(card), rc);
-               QETH_DBF_TEXT_(trace, 2, "err%d", rc);
-               return -EIO;
-       }
-       card->info.layer2_mac_registered = 0;
-
-       rc = qeth_layer2_send_setdelmac(card, addr->sa_data, IPA_CMD_SETVMAC);
-       if (rc) {
-               PRINT_WARN("Error in registering MAC address on " \
-                          "device %s: x%x\n", CARD_BUS_ID(card), rc);
-               QETH_DBF_TEXT_(trace, 2, "2err%d", rc);
-               return -EIO;
-       }
-       card->info.layer2_mac_registered = 1;
-       memcpy(dev->dev_addr, addr->sa_data, OSA_ADDR_LEN);
-       PRINT_INFO("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
-                  "successfully registered on device %s\n",
-                  dev->dev_addr[0],dev->dev_addr[1],dev->dev_addr[2],
-                  dev->dev_addr[3],dev->dev_addr[4],dev->dev_addr[5],
-                  dev->name);
-
+       rc = qeth_layer2_send_delmac(card, &card->dev->dev_addr[0]);
+       if (!rc)
+               rc = qeth_layer2_send_setmac(card, addr->sa_data);
        return rc;
 }
 
@@ -5419,52 +5831,29 @@ static int
 qeth_layer2_register_addr_entry(struct qeth_card *card,
                                struct qeth_ipaddr *addr)
 {
-       int rc = 0;
-
        if (!addr->is_multicast)
                return 0;
-
        QETH_DBF_TEXT(trace, 2, "setgmac");
        QETH_DBF_HEX(trace,3,&addr->mac[0],OSA_ADDR_LEN);
-       rc = qeth_layer2_send_setdelmac(card, &addr->mac[0],
-                                       IPA_CMD_SETGMAC);
-       if (rc)
-               PRINT_ERR("Could not set group MAC " \
-                         "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
-                         addr->mac[0],addr->mac[1],addr->mac[2],
-                         addr->mac[3],addr->mac[4],addr->mac[5],
-                         card->info.if_name,rc);
-       return rc;
+       return qeth_layer2_send_setgroupmac(card, &addr->mac[0]);
 }
 
 static int
 qeth_layer2_deregister_addr_entry(struct qeth_card *card,
                                  struct qeth_ipaddr *addr)
 {
-       int rc = 0;
-
        if (!addr->is_multicast)
                return 0;
-
        QETH_DBF_TEXT(trace, 2, "delgmac");
        QETH_DBF_HEX(trace,3,&addr->mac[0],OSA_ADDR_LEN);
-       rc = qeth_layer2_send_setdelmac(card, &addr->mac[0],
-                                       IPA_CMD_DELGMAC);
-       if (rc)
-               PRINT_ERR("Could not delete group MAC " \
-                         "%02x:%02x:%02x:%02x:%02x:%02x on %s: %x\n",
-                         addr->mac[0],addr->mac[1],addr->mac[2],
-                         addr->mac[3],addr->mac[4],addr->mac[5],
-                         card->info.if_name,rc);
-       return rc;
-
+       return qeth_layer2_send_delgroupmac(card, &addr->mac[0]);
 }
 
 static int
 qeth_layer3_register_addr_entry(struct qeth_card *card,
                                struct qeth_ipaddr *addr)
 {
-       //char buf[50];
+       char buf[50];
        int rc;
        int cnt = 3;
 
@@ -5490,12 +5879,9 @@ qeth_layer3_register_addr_entry(struct qeth_card *card,
        } while ((--cnt > 0) && rc);
        if (rc){
                QETH_DBF_TEXT(trace, 2, "FAILED");
-               /* TODO: re-activate this warning as soon as we have a
-                * clean mirco code
                qeth_ipaddr_to_string(addr->proto, (u8 *)&addr->u, buf);
-               PRINT_WARN("Could not register IP address %s (rc=%x)\n",
-                          buf, rc);
-               */
+               PRINT_WARN("Could not register IP address %s (rc=0x%x/%d)\n",
+                          buf, rc, rc);
        }
        return rc;
 }
@@ -5553,6 +5939,111 @@ qeth_deregister_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
        return qeth_layer3_deregister_addr_entry(card, addr);
 }
 
+static u32
+qeth_ethtool_get_tx_csum(struct net_device *dev)
+{
+       /* We may need to say that we support tx csum offload if
+        * we do EDDP or TSO. There are discussions going on to
+        * enforce rules in the stack and in ethtool that make
+        * SG and TSO depend on HW_CSUM. At the moment there are
+        * no such rules....
+        * If we say yes here, we have to checksum outbound packets
+        * any time. */
+       return 0;
+}
+
+static int
+qeth_ethtool_set_tx_csum(struct net_device *dev, u32 data)
+{
+       return -EINVAL;
+}
+
+static u32
+qeth_ethtool_get_rx_csum(struct net_device *dev)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       return (card->options.checksum_type == HW_CHECKSUMMING);
+}
+
+static int
+qeth_ethtool_set_rx_csum(struct net_device *dev, u32 data)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       if ((card->state != CARD_STATE_DOWN) &&
+           (card->state != CARD_STATE_RECOVER))
+               return -EPERM;
+       if (data)
+               card->options.checksum_type = HW_CHECKSUMMING;
+       else
+               card->options.checksum_type = SW_CHECKSUMMING;
+       return 0;
+}
+
+static u32
+qeth_ethtool_get_sg(struct net_device *dev)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       return ((card->options.large_send != QETH_LARGE_SEND_NO) &&
+               (dev->features & NETIF_F_SG));
+}
+
+static int
+qeth_ethtool_set_sg(struct net_device *dev, u32 data)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       if (data) {
+               if (card->options.large_send != QETH_LARGE_SEND_NO)
+                       dev->features |= NETIF_F_SG;
+               else {
+                       dev->features &= ~NETIF_F_SG;
+                       return -EINVAL;
+               }
+       } else
+               dev->features &= ~NETIF_F_SG;
+       return 0;
+}
+
+static u32
+qeth_ethtool_get_tso(struct net_device *dev)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       return ((card->options.large_send != QETH_LARGE_SEND_NO) &&
+               (dev->features & NETIF_F_TSO));
+}
+
+static int
+qeth_ethtool_set_tso(struct net_device *dev, u32 data)
+{
+       struct qeth_card *card = (struct qeth_card *)dev->priv;
+
+       if (data) {
+               if (card->options.large_send != QETH_LARGE_SEND_NO)
+                       dev->features |= NETIF_F_TSO;
+               else {
+                       dev->features &= ~NETIF_F_TSO;
+                       return -EINVAL;
+               }
+       } else
+               dev->features &= ~NETIF_F_TSO;
+       return 0;
+}
+
+static struct ethtool_ops qeth_ethtool_ops = {
+       .get_tx_csum = qeth_ethtool_get_tx_csum,
+       .set_tx_csum = qeth_ethtool_set_tx_csum,
+       .get_rx_csum = qeth_ethtool_get_rx_csum,
+       .set_rx_csum = qeth_ethtool_set_rx_csum,
+       .get_sg      = qeth_ethtool_get_sg,
+       .set_sg      = qeth_ethtool_set_sg,
+       .get_tso     = qeth_ethtool_get_tso,
+       .set_tso     = qeth_ethtool_set_tso,
+};
+
 static int
 qeth_netdev_init(struct net_device *dev)
 {
@@ -5577,19 +6068,19 @@ qeth_netdev_init(struct net_device *dev)
        dev->vlan_rx_kill_vid = qeth_vlan_rx_kill_vid;
        dev->vlan_rx_add_vid = qeth_vlan_rx_add_vid;
 #endif
+       dev->hard_header = card->orig_hard_header;
        if (qeth_get_netdev_flags(card) & IFF_NOARP) {
                dev->rebuild_header = NULL;
                dev->hard_header = NULL;
+               if (card->options.fake_ll)
+                       dev->hard_header = qeth_fake_header;
                dev->header_cache_update = NULL;
                dev->hard_header_cache = NULL;
        }
 #ifdef CONFIG_QETH_IPV6
        /*IPv6 address autoconfiguration stuff*/
-       card->dev->dev_id = card->info.unique_id & 0xffff;
        if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD))
-               card->dev->generate_eui64 = qeth_ipv6_generate_eui64;
-
-
+               card->dev->dev_id = card->info.unique_id & 0xffff;
 #endif
        dev->hard_header_parse = NULL;
        dev->set_mac_address = qeth_layer2_set_mac_address;
@@ -5602,6 +6093,8 @@ qeth_netdev_init(struct net_device *dev)
        dev->addr_len = OSA_ADDR_LEN;
        dev->mtu = card->info.initial_mtu;
 
+       SET_ETHTOOL_OPS(dev, &qeth_ethtool_ops);
+
        SET_MODULE_OWNER(dev);
        return 0;
 }
@@ -5690,22 +6183,26 @@ retry:
                QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
                goto out;
        }
+       /*network device will be recovered*/
+       if (card->dev) {
+               card->dev->hard_header = card->orig_hard_header;
+               return 0;
+       }
        /* at first set_online allocate netdev */
+       card->dev = qeth_get_netdevice(card->info.type,
+                                      card->info.link_type);
        if (!card->dev){
-               card->dev = qeth_get_netdevice(card->info.type,
-                                              card->info.link_type);
-               if (!card->dev){
-                       qeth_qdio_clear_card(card, card->info.type ==
-                                            QETH_CARD_TYPE_OSAE);
-                       rc = -ENODEV;
-                       QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
-                       goto out;
-               }
-               card->dev->priv = card;
-               card->dev->type = qeth_get_arphdr_type(card->info.type,
-                                                      card->info.link_type);
-               card->dev->init = qeth_netdev_init;
+               qeth_qdio_clear_card(card, card->info.type ==
+                                    QETH_CARD_TYPE_OSAE);
+               rc = -ENODEV;
+               QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
+               goto out;
        }
+       card->dev->priv = card;
+       card->orig_hard_header = card->dev->hard_header;
+       card->dev->type = qeth_get_arphdr_type(card->info.type,
+                                              card->info.link_type);
+       card->dev->init = qeth_netdev_init;
        return 0;
 out:
        PRINT_ERR("Initialization in hardsetup failed! rc=%d\n", rc);
@@ -5924,15 +6421,9 @@ qeth_layer2_initialize(struct qeth_card *card)
         }
        QETH_DBF_HEX(setup,2, card->dev->dev_addr, OSA_ADDR_LEN);
 
-       rc = qeth_layer2_send_setdelmac(card, &card->dev->dev_addr[0],
-                                       IPA_CMD_SETVMAC);
-        if (rc) {
-               card->info.layer2_mac_registered = 0;
-                PRINT_WARN("Error in processing MAC address on " \
-                           "device %s: x%x\n",CARD_BUS_ID(card),rc);
+       rc = qeth_layer2_send_setmac(card, &card->dev->dev_addr[0]);
+        if (rc)
                QETH_DBF_TEXT_(setup, 2,"2err%d",rc);
-        } else
-               card->info.layer2_mac_registered = 1;
         return 0;
 }
 
@@ -5995,6 +6486,9 @@ qeth_query_ipassists_cb(struct qeth_card *card, struct qeth_reply *reply,
                card->options.ipa6.enabled_funcs = cmd->hdr.ipa_enabled;
 #endif
        }
+       QETH_DBF_TEXT(setup, 2, "suppenbl");
+       QETH_DBF_TEXT_(setup, 2, "%x",cmd->hdr.ipa_supported);
+       QETH_DBF_TEXT_(setup, 2, "%x",cmd->hdr.ipa_enabled);
        return 0;
 }
 
@@ -6104,7 +6598,7 @@ qeth_start_ipa_arp_processing(struct qeth_card *card)
 
        if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
                PRINT_WARN("ARP processing not supported "
-                          "on %s!\n", card->info.if_name);
+                          "on %s!\n", QETH_CARD_IFNAME(card));
                return 0;
        }
        rc = qeth_send_simple_setassparms(card,IPA_ARP_PROCESSING,
@@ -6112,7 +6606,7 @@ qeth_start_ipa_arp_processing(struct qeth_card *card)
        if (rc) {
                PRINT_WARN("Could not start ARP processing "
                           "assist on %s: 0x%x\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
        }
        return rc;
 }
@@ -6126,7 +6620,7 @@ qeth_start_ipa_ip_fragmentation(struct qeth_card *card)
 
        if (!qeth_is_supported(card, IPA_IP_FRAGMENTATION)) {
                PRINT_INFO("Hardware IP fragmentation not supported on %s\n",
-                          card->info.if_name);
+                          QETH_CARD_IFNAME(card));
                return  -EOPNOTSUPP;
        }
 
@@ -6135,7 +6629,7 @@ qeth_start_ipa_ip_fragmentation(struct qeth_card *card)
        if (rc) {
                PRINT_WARN("Could not start Hardware IP fragmentation "
                           "assist on %s: 0x%x\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
        } else
                PRINT_INFO("Hardware IP fragmentation enabled \n");
        return rc;
@@ -6153,7 +6647,7 @@ qeth_start_ipa_source_mac(struct qeth_card *card)
 
        if (!qeth_is_supported(card, IPA_SOURCE_MAC)) {
                PRINT_INFO("Inbound source address not "
-                          "supported on %s\n", card->info.if_name);
+                          "supported on %s\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
 
@@ -6162,7 +6656,7 @@ qeth_start_ipa_source_mac(struct qeth_card *card)
        if (rc)
                PRINT_WARN("Could not start inbound source "
                           "assist on %s: 0x%x\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
        return rc;
 }
 
@@ -6175,7 +6669,7 @@ qeth_start_ipa_vlan(struct qeth_card *card)
 
 #ifdef CONFIG_QETH_VLAN
        if (!qeth_is_supported(card, IPA_FULL_VLAN)) {
-               PRINT_WARN("VLAN not supported on %s\n", card->info.if_name);
+               PRINT_WARN("VLAN not supported on %s\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
 
@@ -6184,7 +6678,7 @@ qeth_start_ipa_vlan(struct qeth_card *card)
        if (rc) {
                PRINT_WARN("Could not start vlan "
                           "assist on %s: 0x%x\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
        } else {
                PRINT_INFO("VLAN enabled \n");
                card->dev->features |=
@@ -6205,7 +6699,7 @@ qeth_start_ipa_multicast(struct qeth_card *card)
 
        if (!qeth_is_supported(card, IPA_MULTICASTING)) {
                PRINT_WARN("Multicast not supported on %s\n",
-                          card->info.if_name);
+                          QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
 
@@ -6214,7 +6708,7 @@ qeth_start_ipa_multicast(struct qeth_card *card)
        if (rc) {
                PRINT_WARN("Could not start multicast "
                           "assist on %s: rc=%i\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
        } else {
                PRINT_INFO("Multicast enabled\n");
                card->dev->flags |= IFF_MULTICAST;
@@ -6234,14 +6728,14 @@ qeth_softsetup_ipv6(struct qeth_card *card)
        rc = qeth_send_startlan(card, QETH_PROT_IPV6);
        if (rc) {
                PRINT_ERR("IPv6 startlan failed on %s\n",
-                         card->info.if_name);
+                         QETH_CARD_IFNAME(card));
                return rc;
        }
        netif_wake_queue(card->dev);
        rc = qeth_query_ipassists(card,QETH_PROT_IPV6);
        if (rc) {
                PRINT_ERR("IPv6 query ipassist failed on %s\n",
-                         card->info.if_name);
+                         QETH_CARD_IFNAME(card));
                return rc;
        }
        rc = qeth_send_simple_setassparms(card, IPA_IPV6,
@@ -6249,7 +6743,7 @@ qeth_softsetup_ipv6(struct qeth_card *card)
        if (rc) {
                PRINT_WARN("IPv6 start assist (version 4) failed "
                           "on %s: 0x%x\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
                return rc;
        }
        rc = qeth_send_simple_setassparms_ipv6(card, IPA_IPV6,
@@ -6257,7 +6751,7 @@ qeth_softsetup_ipv6(struct qeth_card *card)
        if (rc) {
                PRINT_WARN("IPV6 start assist (version 6) failed  "
                           "on %s: 0x%x\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
                return rc;
        }
        rc = qeth_send_simple_setassparms_ipv6(card, IPA_PASSTHRU,
@@ -6265,7 +6759,7 @@ qeth_softsetup_ipv6(struct qeth_card *card)
        if (rc) {
                PRINT_WARN("Could not enable passthrough "
                           "on %s: 0x%x\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
                return rc;
        }
        PRINT_INFO("IPV6 enabled \n");
@@ -6283,7 +6777,7 @@ qeth_start_ipa_ipv6(struct qeth_card *card)
 
        if (!qeth_is_supported(card, IPA_IPV6)) {
                PRINT_WARN("IPv6 not supported on %s\n",
-                          card->info.if_name);
+                          QETH_CARD_IFNAME(card));
                return 0;
        }
        rc = qeth_softsetup_ipv6(card);
@@ -6300,7 +6794,7 @@ qeth_start_ipa_broadcast(struct qeth_card *card)
        card->info.broadcast_capable = 0;
        if (!qeth_is_supported(card, IPA_FILTERING)) {
                PRINT_WARN("Broadcast not supported on %s\n",
-                          card->info.if_name);
+                          QETH_CARD_IFNAME(card));
                rc = -EOPNOTSUPP;
                goto out;
        }
@@ -6309,7 +6803,7 @@ qeth_start_ipa_broadcast(struct qeth_card *card)
        if (rc) {
                PRINT_WARN("Could not enable broadcasting filtering "
                           "on %s: 0x%x\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
                goto out;
        }
 
@@ -6317,7 +6811,7 @@ qeth_start_ipa_broadcast(struct qeth_card *card)
                                          IPA_CMD_ASS_CONFIGURE, 1);
        if (rc) {
                PRINT_WARN("Could not set up broadcast filtering on %s: 0x%x\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
                goto out;
        }
        card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
@@ -6326,7 +6820,7 @@ qeth_start_ipa_broadcast(struct qeth_card *card)
                                          IPA_CMD_ASS_ENABLE, 1);
        if (rc) {
                PRINT_WARN("Could not set up broadcast echo filtering on "
-                          "%s: 0x%x\n", card->info.if_name, rc);
+                          "%s: 0x%x\n", QETH_CARD_IFNAME(card), rc);
                goto out;
        }
        card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO;
@@ -6348,7 +6842,7 @@ qeth_send_checksum_command(struct qeth_card *card)
        if (rc) {
                PRINT_WARN("Starting Inbound HW Checksumming failed on %s: "
                           "0x%x,\ncontinuing using Inbound SW Checksumming\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
                return rc;
        }
        rc = qeth_send_simple_setassparms(card, IPA_INBOUND_CHECKSUM,
@@ -6357,7 +6851,7 @@ qeth_send_checksum_command(struct qeth_card *card)
        if (rc) {
                PRINT_WARN("Enabling Inbound HW Checksumming failed on %s: "
                           "0x%x,\ncontinuing using Inbound SW Checksumming\n",
-                          card->info.if_name, rc);
+                          QETH_CARD_IFNAME(card), rc);
                return rc;
        }
        return 0;
@@ -6372,19 +6866,19 @@ qeth_start_ipa_checksum(struct qeth_card *card)
 
        if (card->options.checksum_type == NO_CHECKSUMMING) {
                PRINT_WARN("Using no checksumming on %s.\n",
-                          card->info.if_name);
+                          QETH_CARD_IFNAME(card));
                return 0;
        }
        if (card->options.checksum_type == SW_CHECKSUMMING) {
                PRINT_WARN("Using SW checksumming on %s.\n",
-                          card->info.if_name);
+                          QETH_CARD_IFNAME(card));
                return 0;
        }
        if (!qeth_is_supported(card, IPA_INBOUND_CHECKSUM)) {
                PRINT_WARN("Inbound HW Checksumming not "
                           "supported on %s,\ncontinuing "
                           "using Inbound SW Checksumming\n",
-                          card->info.if_name);
+                          QETH_CARD_IFNAME(card));
                card->options.checksum_type = SW_CHECKSUMMING;
                return 0;
        }
@@ -6395,26 +6889,33 @@ qeth_start_ipa_checksum(struct qeth_card *card)
        return rc;
 }
 
-/*
-static inline void
-qeth_print_ipassist_status(struct qeth_card *card)
+static int
+qeth_start_ipa_tso(struct qeth_card *card)
 {
-       char buf[255];
-       int offset = 0;
+       int rc;
+
+       QETH_DBF_TEXT(trace,3,"sttso");
 
-       offset += sprintf(buf, "IPAssist options of %s: ", card->info.if_name);
-       if (qeth_is_enabled(card, IPA_ARP_PROCESSING))
-               offset += sprintf(buf+offset, "ARP ");
-       if (qeth_is_enabled(card, IPA_IP_FRAGMENTATION))
-               offset += sprintf(buf+offset, "IP_FRAG");
-       if (qeth_is_enabled(card, IPA_SOURCE_MAC))
-               offset += sprintf(buf+offset, "SRC_MAC");
-       if (qeth_is_enabled(card, IPA_FULL_VLAN))
-               offset += sprintf(buf+offset, "VLAN");
-       if (qeth_is_enabled(card, IPA_VLAN_PRIO))
-               offset += sprintf(buf+offset, "VLAN_PRIO");
+       if (!qeth_is_supported(card, IPA_OUTBOUND_TSO)) {
+               PRINT_WARN("Outbound TSO not supported on %s\n",
+                          QETH_CARD_IFNAME(card));
+               rc = -EOPNOTSUPP;
+       } else {
+               rc = qeth_send_simple_setassparms(card, IPA_OUTBOUND_TSO,
+                                                 IPA_CMD_ASS_START,0);
+               if (rc)
+                       PRINT_WARN("Could not start outbound TSO "
+                                  "assist on %s: rc=%i\n",
+                                  QETH_CARD_IFNAME(card), rc);
+               else
+                       PRINT_INFO("Outbound TSO enabled\n");
+       }
+       if (rc && (card->options.large_send == QETH_LARGE_SEND_TSO)){
+               card->options.large_send = QETH_LARGE_SEND_NO;
+               card->dev->features &= ~ (NETIF_F_TSO | NETIF_F_SG);
+       }
+       return rc;
 }
-*/
 
 static int
 qeth_start_ipassists(struct qeth_card *card)
@@ -6428,6 +6929,7 @@ qeth_start_ipassists(struct qeth_card *card)
        qeth_start_ipa_ipv6(card);              /* go on*/
        qeth_start_ipa_broadcast(card);         /* go on*/
        qeth_start_ipa_checksum(card);          /* go on*/
+       qeth_start_ipa_tso(card);               /* go on*/
        return 0;
 }
 
@@ -6506,7 +7008,7 @@ qeth_setrouting_v4(struct qeth_card *card)
                card->options.route4.type = NO_ROUTER;
                PRINT_WARN("Error (0x%04x) while setting routing type on %s. "
                           "Type set to 'no router'.\n",
-                          rc, card->info.if_name);
+                          rc, QETH_CARD_IFNAME(card));
        }
        return rc;
 }
@@ -6533,12 +7035,46 @@ qeth_setrouting_v6(struct qeth_card *card)
                card->options.route6.type = NO_ROUTER;
                PRINT_WARN("Error (0x%04x) while setting routing type on %s. "
                           "Type set to 'no router'.\n",
-                          rc, card->info.if_name);
+                          rc, QETH_CARD_IFNAME(card));
        }
 #endif
        return rc;
 }
 
+int
+qeth_set_large_send(struct qeth_card *card)
+{
+       int rc = 0;
+
+       if (card->dev == NULL)
+               return 0;
+
+       netif_stop_queue(card->dev);
+       switch (card->options.large_send) {
+       case QETH_LARGE_SEND_EDDP:
+               card->dev->features |= NETIF_F_TSO | NETIF_F_SG;
+               break;
+       case QETH_LARGE_SEND_TSO:
+               if (qeth_is_supported(card, IPA_OUTBOUND_TSO)){
+                       card->dev->features |= NETIF_F_TSO | NETIF_F_SG;
+               } else {
+                       PRINT_WARN("TSO not supported on %s. "
+                                  "large_send set to 'no'.\n",
+                                  card->dev->name);
+                       card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG);
+                       card->options.large_send = QETH_LARGE_SEND_NO;
+                       rc = -EOPNOTSUPP;
+               }
+               break;
+       default: /* includes QETH_LARGE_SEND_NO */
+               card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG);
+               break;
+       }
+
+       netif_wake_queue(card->dev);
+       return rc;
+}
+
 /*
  * softsetup card: init IPA stuff
  */
@@ -6576,6 +7112,12 @@ qeth_softsetup_card(struct qeth_card *card)
 #endif
                goto out;
        }
+       if ((card->options.large_send == QETH_LARGE_SEND_EDDP) ||
+           (card->options.large_send == QETH_LARGE_SEND_TSO))
+               card->dev->features |= NETIF_F_TSO | NETIF_F_SG;
+       else
+               card->dev->features &= ~(NETIF_F_TSO | NETIF_F_SG);
+
        if ((rc = qeth_setadapter_parms(card)))
                QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
        if ((rc = qeth_start_ipassists(card)))
@@ -6712,7 +7254,7 @@ qeth_wait_for_threads(struct qeth_card *card, unsigned long threads)
 }
 
 static int
-qeth_stop_card(struct qeth_card *card)
+qeth_stop_card(struct qeth_card *card, int recovery_mode)
 {
        int rc = 0;
 
@@ -6725,14 +7267,17 @@ qeth_stop_card(struct qeth_card *card)
        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];
-                       if ((rc = qeth_layer2_send_setdelmac(card, mac,
-                                                           IPA_CMD_DELVMAC)));
-                               QETH_DBF_TEXT_(setup, 2, "Lerr%d", rc);
+                       rc = qeth_layer2_send_delmac(card, mac);
+                       QETH_DBF_TEXT_(setup, 2, "Lerr%d", rc);
                        if ((rc = qeth_send_stoplan(card)))
                                QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
                }
@@ -6889,28 +7434,28 @@ qeth_print_status_message(struct qeth_card *card)
 static int
 qeth_register_netdev(struct qeth_card *card)
 {
-       int rc;
-
        QETH_DBF_TEXT(setup, 3, "regnetd");
-       if (card->dev->reg_state != NETREG_UNINITIALIZED)
+       if (card->dev->reg_state != NETREG_UNINITIALIZED) {
+               qeth_netdev_init(card->dev);
                return 0;
+       }
        /* sysfs magic */
        SET_NETDEV_DEV(card->dev, &card->gdev->dev);
-       rc = register_netdev(card->dev);
-       if (!rc)
-               strcpy(card->info.if_name, card->dev->name);
-
-       return rc;
+       return register_netdev(card->dev);
 }
 
 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);
 }
@@ -6967,7 +7512,7 @@ static void qeth_make_parameters_consistent(struct qeth_card *card)
 
 
 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;
@@ -6985,9 +7530,9 @@ qeth_set_online(struct ccwgroup_device *gdev)
        }
 
        recover_flag = card->state;
-       if (ccw_device_set_online(CARD_RDEV(card)) ||
-           ccw_device_set_online(CARD_WDEV(card)) ||
-           ccw_device_set_online(CARD_DDEV(card))){
+       if ((rc = ccw_device_set_online(CARD_RDEV(card))) ||
+           (rc = ccw_device_set_online(CARD_WDEV(card))) ||
+           (rc = ccw_device_set_online(CARD_DDEV(card)))){
                QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
                return -EIO;
        }
@@ -7025,17 +7570,14 @@ qeth_set_online(struct ccwgroup_device *gdev)
        }
 /*maybe it was set offline without ifconfig down
  * we can also use this state for recovery purposes*/
-       if (card->options.layer2)
-               qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 0);
-       else
-               qeth_set_allowed_threads(card, 0xffffffff, 0);
+       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));
@@ -7046,6 +7588,12 @@ out_remove:
        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},
@@ -7181,7 +7729,8 @@ qeth_arp_constructor(struct neighbour *neigh)
        card = qeth_get_card_from_dev(dev);
        if (card == NULL)
                goto out;
-       if(card->options.layer2)
+       if((card->options.layer2) ||
+          (card->dev->hard_header == qeth_fake_header))
                goto out;
 
        rcu_read_lock();