Merge to Fedora kernel-2.6.17-1.2187_FC5 patched with stable patch-2.6.17.13-vs2...
[linux-2.6.git] / drivers / s390 / net / qeth_main.c
index 3e374dd..2908669 100644 (file)
@@ -1,6 +1,5 @@
 /*
- *
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.145 $)
+ * linux/drivers/s390/net/qeth_main.c
  *
  * Linux on zSeries OSA Express and HiperSockets support
  *
@@ -9,11 +8,9 @@
  *    Author(s): Original Code written by
  *                       Utz Bacher (utz.bacher@de.ibm.com)
  *              Rewritten by
- *                       Frank Pavlic (pavlic@de.ibm.com) and
+ *                       Frank Pavlic (fpavlic@de.ibm.com) and
  *                       Thomas Spatzier <tspat@de.ibm.com>
  *
- *    $Revision: 1.145 $        $Date: 2004/10/08 15:08:40 $
- *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
  */
 
 
-/***
- * eye catcher; just for debugging purposes
- */
-void volatile
-qeth_eyecatcher(void)
-{
-       return;
-}
-
 #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>
@@ -63,23 +44,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 <asm/s390_rdev.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.145 $"
 static const char *version = "qeth S/390 OSA-Express driver";
 
 /**
@@ -89,7 +79,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;
 
@@ -157,9 +147,18 @@ 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);
 
+static void
+qeth_set_multicast_list(struct net_device *);
+
+static void
+qeth_setadp_promisc_mode(struct qeth_card *);
+
 static void
 qeth_notify_processes(void)
 {
@@ -197,7 +196,6 @@ qeth_notifier_register(struct task_struct *p, int signum)
 {
        struct qeth_notify_list_struct *n_entry;
 
-       QETH_DBF_TEXT(trace, 2, "notreg");
        /*check first if entry already exists*/
        spin_lock(&qeth_notify_lock);
        list_for_each_entry(n_entry, &qeth_notify_list, list) {
@@ -250,6 +248,7 @@ qeth_free_card(struct qeth_card *card)
                free_netdev(card->dev);
        qeth_clear_ip_list(card, 0, 0);
        qeth_clear_ipato_list(card);
+       kfree(card->ip_tbd_list);
        qeth_free_qdio_buffers(card);
        kfree(card);
 }
@@ -298,12 +297,10 @@ qeth_alloc_card(void)
        struct qeth_card *card;
 
        QETH_DBF_TEXT(setup, 2, "alloccrd");
-       card = (struct qeth_card *) kmalloc(sizeof(struct qeth_card),
-                                           GFP_DMA|GFP_KERNEL);
+       card = kzalloc(sizeof(struct qeth_card), GFP_DMA|GFP_KERNEL);
        if (!card)
                return NULL;
        QETH_DBF_HEX(setup, 2, &card, sizeof(void *));
-       memset(card, 0, sizeof(struct qeth_card));
        if (qeth_setup_channel(&card->read)) {
                kfree(card);
                return NULL;
@@ -505,32 +502,49 @@ 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, rc2 = 0, rc3 = 0;
        enum qeth_card_states recover_flag;
 
        QETH_DBF_TEXT(setup, 3, "setoffl");
        QETH_DBF_HEX(setup, 3, &card, sizeof(void *));
 
+       if (card->dev && netif_carrier_ok(card->dev))
+               netif_carrier_off(card->dev);
        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));
+       rc  = ccw_device_set_offline(CARD_DDEV(card));
+       rc2 = ccw_device_set_offline(CARD_WDEV(card));
+       rc3 = ccw_device_set_offline(CARD_RDEV(card));
+       if (!rc)
+               rc = (rc2) ? rc2 : rc3;
+       if (rc)
+               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)
 {
@@ -543,6 +557,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);
@@ -585,11 +602,20 @@ __qeth_ref_ip_on_card(struct qeth_card *card, struct qeth_ipaddr *todo,
        int found = 0;
 
        list_for_each_entry(addr, &card->ip_list, entry) {
+               if (card->options.layer2) {
+                       if ((addr->type == todo->type) &&
+                           (memcmp(&addr->mac, &todo->mac,
+                                   OSA_ADDR_LEN) == 0)) {
+                               found = 1;
+                               break;
+                       }
+                       continue;
+               }
                if ((addr->proto     == QETH_PROT_IPV4)  &&
                    (todo->proto     == QETH_PROT_IPV4)  &&
                    (addr->type      == todo->type)      &&
                    (addr->u.a4.addr == todo->u.a4.addr) &&
-                   (addr->u.a4.mask == todo->u.a4.mask)   ){
+                   (addr->u.a4.mask == todo->u.a4.mask){
                        found = 1;
                        break;
                }
@@ -598,12 +624,12 @@ __qeth_ref_ip_on_card(struct qeth_card *card, struct qeth_ipaddr *todo,
                    (addr->type        == todo->type)         &&
                    (addr->u.a6.pfxlen == todo->u.a6.pfxlen)  &&
                    (memcmp(&addr->u.a6.addr, &todo->u.a6.addr,
-                           sizeof(struct in6_addr)) == 0))     {
+                           sizeof(struct in6_addr)) == 0)) {
                        found = 1;
                        break;
                }
        }
-       if (found){
+       if (found) {
                addr->users += todo->users;
                if (addr->users <= 0){
                        *__addr = addr;
@@ -615,10 +641,10 @@ __qeth_ref_ip_on_card(struct qeth_card *card, struct qeth_ipaddr *todo,
                        return 0;
                }
        }
-       if (todo->users > 0){
+       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;
@@ -661,13 +687,26 @@ __qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add)
        struct qeth_ipaddr *tmp, *t;
        int found = 0;
 
-       list_for_each_entry_safe(tmp, t, &card->ip_tbd_list, entry) {
+       list_for_each_entry_safe(tmp, t, card->ip_tbd_list, entry) {
+               if ((addr->type == QETH_IP_TYPE_DEL_ALL_MC) &&
+                   (tmp->type == QETH_IP_TYPE_DEL_ALL_MC))
+                       return 0;
+               if (card->options.layer2) {
+                       if ((tmp->type  == addr->type)  &&
+                           (tmp->is_multicast == addr->is_multicast) &&
+                           (memcmp(&tmp->mac, &addr->mac,
+                                   OSA_ADDR_LEN) == 0)) {
+                               found = 1;
+                               break;
+                       }
+                       continue;
+               }
                if ((tmp->proto        == QETH_PROT_IPV4)     &&
                    (addr->proto       == QETH_PROT_IPV4)     &&
                    (tmp->type         == addr->type)         &&
                    (tmp->is_multicast == addr->is_multicast) &&
                    (tmp->u.a4.addr    == addr->u.a4.addr)    &&
-                   (tmp->u.a4.mask    == addr->u.a4.mask)      ){
+                   (tmp->u.a4.mask    == addr->u.a4.mask){
                        found = 1;
                        break;
                }
@@ -677,7 +716,7 @@ __qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add)
                    (tmp->is_multicast == addr->is_multicast)  &&
                    (tmp->u.a6.pfxlen  == addr->u.a6.pfxlen)   &&
                    (memcmp(&tmp->u.a6.addr, &addr->u.a6.addr,
-                           sizeof(struct in6_addr)) == 0)        ){
+                           sizeof(struct in6_addr)) == 0){
                        found = 1;
                        break;
                }
@@ -687,20 +726,24 @@ __qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add)
                        tmp->users += addr->users;
                else
                        tmp->users += add? 1:-1;
-               if (tmp->users == 0){
+               if (tmp->users == 0) {
                        list_del(&tmp->entry);
                        kfree(tmp);
                }
                return 0;
        } else {
-               if (addr->users == 0)
-                       addr->users += add? 1:-1;
-               if (add && (addr->type == QETH_IP_TYPE_NORMAL) &&
-                   qeth_is_addr_covered_by_ipato(card, addr)){
-                       QETH_DBF_TEXT(trace, 2, "tkovaddr");
-                       addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
+               if (addr->type == QETH_IP_TYPE_DEL_ALL_MC)
+                       list_add(&addr->entry, card->ip_tbd_list);
+               else {
+                       if (addr->users == 0)
+                               addr->users += add? 1:-1;
+                       if (add && (addr->type == QETH_IP_TYPE_NORMAL) &&
+                           qeth_is_addr_covered_by_ipato(card, addr)){
+                               QETH_DBF_TEXT(trace, 2, "tkovaddr");
+                               addr->set_flags |= QETH_IPA_SETIP_TAKEOVER_FLAG;
+                       }
+                       list_add_tail(&addr->entry, card->ip_tbd_list);
                }
-               list_add_tail(&addr->entry, &card->ip_tbd_list);
                return 1;
        }
 }
@@ -714,12 +757,15 @@ qeth_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
        unsigned long flags;
        int rc = 0;
 
-       QETH_DBF_TEXT(trace,4,"delip");
-       if (addr->proto == QETH_PROT_IPV4)
-               QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4);
+       QETH_DBF_TEXT(trace, 4, "delip");
+
+       if (card->options.layer2)
+               QETH_DBF_HEX(trace, 4, &addr->mac, 6);
+       else if (addr->proto == QETH_PROT_IPV4)
+               QETH_DBF_HEX(trace, 4, &addr->u.a4.addr, 4);
        else {
-               QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4);
-               QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4);
+               QETH_DBF_HEX(trace, 4, &addr->u.a6.addr, 8);
+               QETH_DBF_HEX(trace, 4, ((char *)&addr->u.a6.addr) + 8, 8);
        }
        spin_lock_irqsave(&card->ip_lock, flags);
        rc = __qeth_insert_ip_todo(card, addr, 0);
@@ -733,12 +779,14 @@ qeth_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
        unsigned long flags;
        int rc = 0;
 
-       QETH_DBF_TEXT(trace,4,"addip");
-       if (addr->proto == QETH_PROT_IPV4)
-               QETH_DBF_HEX(trace,4,&addr->u.a4.addr,4);
+       QETH_DBF_TEXT(trace, 4, "addip");
+       if (card->options.layer2)
+               QETH_DBF_HEX(trace, 4, &addr->mac, 6);
+       else if (addr->proto == QETH_PROT_IPV4)
+               QETH_DBF_HEX(trace, 4, &addr->u.a4.addr, 4);
        else {
-               QETH_DBF_HEX(trace,4,&addr->u.a6.addr,4);
-               QETH_DBF_HEX(trace,4,((char *)&addr->u.a6.addr)+4,4);
+               QETH_DBF_HEX(trace, 4, &addr->u.a6.addr, 8);
+               QETH_DBF_HEX(trace, 4, ((char *)&addr->u.a6.addr) + 8, 8);
        }
        spin_lock_irqsave(&card->ip_lock, flags);
        rc = __qeth_insert_ip_todo(card, addr, 1);
@@ -746,19 +794,22 @@ qeth_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
        return rc;
 }
 
-static void
-qeth_reinsert_todos(struct qeth_card *card, struct list_head *todos)
+static inline void
+__qeth_delete_all_mc(struct qeth_card *card, unsigned long *flags)
 {
-       struct qeth_ipaddr *todo, *tmp;
-
-       list_for_each_entry_safe(todo, tmp, todos, entry){
-               list_del_init(&todo->entry);
-               if (todo->users < 0) {
-                       if (!qeth_delete_ip(card, todo))
-                               kfree(todo);
-               } else {
-                       if (!qeth_add_ip(card, todo))
-                               kfree(todo);
+       struct qeth_ipaddr *addr, *tmp;
+       int rc;
+again:
+       list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
+               if (addr->is_multicast) {
+                       spin_unlock_irqrestore(&card->ip_lock, *flags);
+                       rc = qeth_deregister_addr_entry(card, addr);
+                       spin_lock_irqsave(&card->ip_lock, *flags);
+                       if (!rc) {
+                               list_del(&addr->entry);
+                               kfree(addr);
+                               goto again;
+                       }
                }
        }
 }
@@ -766,7 +817,7 @@ qeth_reinsert_todos(struct qeth_card *card, struct list_head *todos)
 static void
 qeth_set_ip_addr_list(struct qeth_card *card)
 {
-       struct list_head failed_todos;
+       struct list_head *tbd_list;
        struct qeth_ipaddr *todo, *addr;
        unsigned long flags;
        int rc;
@@ -774,13 +825,25 @@ qeth_set_ip_addr_list(struct qeth_card *card)
        QETH_DBF_TEXT(trace, 2, "sdiplist");
        QETH_DBF_HEX(trace, 2, &card, sizeof(void *));
 
-       INIT_LIST_HEAD(&failed_todos);
-
        spin_lock_irqsave(&card->ip_lock, flags);
-       while (!list_empty(&card->ip_tbd_list)) {
-               todo = list_entry(card->ip_tbd_list.next,
-                                 struct qeth_ipaddr, entry);
-               list_del_init(&todo->entry);
+       tbd_list = card->ip_tbd_list;
+       card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);
+       if (!card->ip_tbd_list) {
+               QETH_DBF_TEXT(trace, 0, "silnomem");
+               card->ip_tbd_list = tbd_list;
+               spin_unlock_irqrestore(&card->ip_lock, flags);
+               return;
+       } else
+               INIT_LIST_HEAD(card->ip_tbd_list);
+
+       while (!list_empty(tbd_list)){
+               todo = list_entry(tbd_list->next, struct qeth_ipaddr, entry);
+               list_del(&todo->entry);
+               if (todo->type == QETH_IP_TYPE_DEL_ALL_MC){
+                       __qeth_delete_all_mc(card, &flags);
+                       kfree(todo);
+                       continue;
+               }
                rc = __qeth_ref_ip_on_card(card, todo, &addr);
                if (rc == 0) {
                        /* nothing to be done; only adjusted refcount */
@@ -793,28 +856,27 @@ qeth_set_ip_addr_list(struct qeth_card *card)
                        if (!rc)
                                list_add_tail(&todo->entry, &card->ip_list);
                        else
-                               list_add_tail(&todo->entry, &failed_todos);
+                               kfree(todo);
                } else if (rc == -1) {
                        /* on-card entry to be removed */
                        list_del_init(&addr->entry);
                        spin_unlock_irqrestore(&card->ip_lock, flags);
                        rc = qeth_deregister_addr_entry(card, addr);
                        spin_lock_irqsave(&card->ip_lock, flags);
-                       if (!rc) {
+                       if (!rc)
                                kfree(addr);
-                               kfree(todo);
-                       } else {
+                       else
                                list_add_tail(&addr->entry, &card->ip_list);
-                               list_add_tail(&todo->entry, &failed_todos);
-                       }
+                       kfree(todo);
                }
        }
        spin_unlock_irqrestore(&card->ip_lock, flags);
-       qeth_reinsert_todos(card, &failed_todos);
+       kfree(tbd_list);
 }
 
 static void qeth_delete_mc_addresses(struct qeth_card *);
 static void qeth_add_multicast_ipv4(struct qeth_card *);
+static void qeth_layer2_add_multicast(struct qeth_card *);
 #ifdef CONFIG_QETH_IPV6
 static void qeth_add_multicast_ipv6(struct qeth_card *);
 #endif
@@ -888,39 +950,36 @@ qeth_do_run_thread(struct qeth_card *card, unsigned long thread)
 }
 
 static int
-qeth_register_mc_addresses(void *ptr)
+qeth_register_ip_addresses(void *ptr)
 {
        struct qeth_card *card;
 
        card = (struct qeth_card *) ptr;
-       daemonize("qeth_reg_mcaddrs");
-       QETH_DBF_TEXT(trace,4,"regmcth1");
-       if (!qeth_do_run_thread(card, QETH_SET_MC_THREAD))
+       daemonize("qeth_reg_ip");
+       QETH_DBF_TEXT(trace,4,"regipth1");
+       if (!qeth_do_run_thread(card, QETH_SET_IP_THREAD))
                return 0;
-       QETH_DBF_TEXT(trace,4,"regmcth2");
-       qeth_delete_mc_addresses(card);
-       qeth_add_multicast_ipv4(card);
-#ifdef CONFIG_QETH_IPV6
-       qeth_add_multicast_ipv6(card);
-#endif
+       QETH_DBF_TEXT(trace,4,"regipth2");
        qeth_set_ip_addr_list(card);
-       qeth_clear_thread_running_bit(card, QETH_SET_MC_THREAD);
+       qeth_clear_thread_running_bit(card, QETH_SET_IP_THREAD);
        return 0;
 }
 
+/*
+ * Drive the SET_PROMISC_MODE thread
+ */
 static int
-qeth_register_ip_address(void *ptr)
+qeth_set_promisc_mode(void *ptr)
 {
-       struct qeth_card *card;
+       struct qeth_card *card = (struct qeth_card *) ptr;
 
-       card = (struct qeth_card *) ptr;
-       daemonize("qeth_reg_ip");
-       QETH_DBF_TEXT(trace,4,"regipth1");
-       if (!qeth_do_run_thread(card, QETH_SET_IP_THREAD))
+       daemonize("qeth_setprm");
+       QETH_DBF_TEXT(trace,4,"setprm1");
+       if (!qeth_do_run_thread(card, QETH_SET_PROMISC_MODE_THREAD))
                return 0;
-       QETH_DBF_TEXT(trace,4,"regipth2");
-       qeth_set_ip_addr_list(card);
-       qeth_clear_thread_running_bit(card, QETH_SET_IP_THREAD);
+       QETH_DBF_TEXT(trace,4,"setprm2");
+       qeth_setadp_promisc_mode(card);
+       qeth_clear_thread_running_bit(card, QETH_SET_PROMISC_MODE_THREAD);
        return 0;
 }
 
@@ -940,8 +999,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));
@@ -958,7 +1017,6 @@ void
 qeth_schedule_recovery(struct qeth_card *card)
 {
        QETH_DBF_TEXT(trace,2,"startrec");
-
        if (qeth_set_thread_start_bit(card, QETH_RECOVER_THREAD) == 0)
                schedule_work(&card->kernel_thread_starter);
 }
@@ -989,9 +1047,9 @@ qeth_start_kernel_thread(struct qeth_card *card)
                return;
 
        if (qeth_do_start_thread(card, QETH_SET_IP_THREAD))
-               kernel_thread(qeth_register_ip_address, (void *) card, SIGCHLD);
-       if (qeth_do_start_thread(card, QETH_SET_MC_THREAD))
-               kernel_thread(qeth_register_mc_addresses, (void *)card,SIGCHLD);
+               kernel_thread(qeth_register_ip_addresses, (void *)card,SIGCHLD);
+       if (qeth_do_start_thread(card, QETH_SET_PROMISC_MODE_THREAD))
+               kernel_thread(qeth_set_promisc_mode, (void *)card, SIGCHLD);
        if (qeth_do_start_thread(card, QETH_RECOVER_THREAD))
                kernel_thread(qeth_recover, (void *) card, SIGCHLD);
 }
@@ -1010,6 +1068,10 @@ qeth_set_intial_options(struct qeth_card *card)
        card->options.fake_broadcast = 0;
        card->options.add_hhlen = DEFAULT_ADD_HHLEN;
        card->options.fake_ll = 0;
+       if (card->info.type == QETH_CARD_TYPE_OSN)
+               card->options.layer2 = 1;
+       else
+               card->options.layer2 = 0;
 }
 
 /**
@@ -1033,6 +1095,7 @@ qeth_setup_card(struct qeth_card *card)
        spin_lock_init(&card->vlanlock);
        card->vlangrp = NULL;
 #endif
+       spin_lock_init(&card->lock);
        spin_lock_init(&card->ip_lock);
        spin_lock_init(&card->thread_mask_lock);
        card->thread_start_mask = 0;
@@ -1041,7 +1104,12 @@ qeth_setup_card(struct qeth_card *card)
        INIT_WORK(&card->kernel_thread_starter,
                  (void *)qeth_start_kernel_thread,card);
        INIT_LIST_HEAD(&card->ip_list);
-       INIT_LIST_HEAD(&card->ip_tbd_list);
+       card->ip_tbd_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
+       if (!card->ip_tbd_list) {
+               QETH_DBF_TEXT(setup, 0, "iptbdnom");
+               return -ENOMEM;
+       }
+       INIT_LIST_HEAD(card->ip_tbd_list);
        INIT_LIST_HEAD(&card->cmd_waiter_list);
        init_waitqueue_head(&card->wait_q);
        /* intial options */
@@ -1056,6 +1124,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)
 {
@@ -1063,12 +1160,20 @@ qeth_determine_card_type(struct qeth_card *card)
 
        QETH_DBF_TEXT(setup, 2, "detcdtyp");
 
+       card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT;
+       card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
        while (known_devices[i][4]) {
                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];
                        card->info.is_multicast_different = known_devices[i][9];
+                       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;
+                       }
                        return 0;
                }
                i++;
@@ -1092,36 +1197,38 @@ qeth_probe_device(struct ccwgroup_device *gdev)
        if (!get_device(dev))
                return -ENODEV;
 
+       QETH_DBF_TEXT_(setup, 2, "%s", gdev->dev.bus_id);
+
        card = qeth_alloc_card();
        if (!card) {
                put_device(dev);
                QETH_DBF_TEXT_(setup, 2, "1err%d", -ENOMEM);
                return -ENOMEM;
        }
-       if ((rc = qeth_setup_card(card))){
-               QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
-               put_device(dev);
-               qeth_free_card(card);
-               return rc;
-       }
+       card->read.ccwdev  = gdev->cdev[0];
+       card->write.ccwdev = gdev->cdev[1];
+       card->data.ccwdev  = gdev->cdev[2];
        gdev->dev.driver_data = card;
        card->gdev = gdev;
        gdev->cdev[0]->handler = qeth_irq;
        gdev->cdev[1]->handler = qeth_irq;
        gdev->cdev[2]->handler = qeth_irq;
 
-       rc = qeth_create_device_attributes(dev);
-       if (rc) {
+       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);
                put_device(dev);
                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);
+       if ((rc = qeth_setup_card(card))){
+               QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
+               put_device(dev);
+               qeth_free_card(card);
+               return rc;
+       }
+       rc = qeth_create_device_attributes(dev);
+       if (rc) {
                put_device(dev);
                qeth_free_card(card);
                return rc;
@@ -1255,7 +1362,7 @@ qeth_wait_for_buffer(struct qeth_channel *channel)
 static void
 qeth_clear_cmd_buffers(struct qeth_channel *channel)
 {
-       int cnt = 0;
+       int cnt;
 
        for (cnt=0; cnt < QETH_CMD_BUFFER_NO; cnt++)
                qeth_release_buffer(channel,&channel->iob[cnt]);
@@ -1285,7 +1392,7 @@ qeth_idx_activate_get_answer(struct qeth_channel *channel,
        channel->ccw.cda = (__u32) __pa(iob->data);
 
        wait_event(card->wait_q,
-                  atomic_compare_and_swap(0,1,&channel->irq_pending) == 0);
+                  atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0);
        QETH_DBF_TEXT(setup, 6, "noirqpnd");
        spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
        rc = ccw_device_start(channel->ccwdev,
@@ -1306,6 +1413,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;
@@ -1351,7 +1459,7 @@ qeth_idx_activate_channel(struct qeth_channel *channel,
        memcpy(QETH_IDX_ACT_QDIO_DEV_REALADDR(iob->data), &temp, 2);
 
        wait_event(card->wait_q,
-                  atomic_compare_and_swap(0,1,&channel->irq_pending) == 0);
+                  atomic_cmpxchg(&channel->irq_pending, 0, 1) == 0);
        QETH_DBF_TEXT(setup, 6, "noirqpnd");
        spin_lock_irqsave(get_ccwdev_lock(channel->ccwdev), flags);
        rc = ccw_device_start(channel->ccwdev,
@@ -1372,6 +1480,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);
@@ -1503,7 +1612,7 @@ qeth_issue_next_read(struct qeth_card *card)
        }
        qeth_setup_ccw(&card->read, iob->data, QETH_BUFSIZE);
        wait_event(card->wait_q,
-                  atomic_compare_and_swap(0,1,&card->read.irq_pending) == 0);
+                  atomic_cmpxchg(&card->read.irq_pending, 0, 1) == 0);
        QETH_DBF_TEXT(trace, 6, "noirqpnd");
        rc = ccw_device_start(card->read.ccwdev, &card->read.ccw,
                              (addr_t) iob, 0, 0);
@@ -1521,9 +1630,8 @@ qeth_alloc_reply(struct qeth_card *card)
 {
        struct qeth_reply *reply;
 
-       reply = kmalloc(sizeof(struct qeth_reply), GFP_ATOMIC);
+       reply = kzalloc(sizeof(struct qeth_reply), GFP_ATOMIC);
        if (reply){
-               memset(reply, 0, sizeof(struct qeth_reply));
                atomic_set(&reply->refcnt, 1);
                reply->card = card;
        };
@@ -1569,16 +1677,6 @@ qeth_cmd_timeout(unsigned long data)
        spin_unlock_irqrestore(&reply->card->lock, flags);
 }
 
-static void
-qeth_reset_ip_addresses(struct qeth_card *card)
-{
-       QETH_DBF_TEXT(trace, 2, "rstipadd");
-
-       qeth_clear_ip_list(card, 0, 1);
-       if ( (qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD) == 0) ||
-            (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0) )
-               schedule_work(&card->kernel_thread_starter);
-}
 
 static struct qeth_ipa_cmd *
 qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
@@ -1597,35 +1695,27 @@ 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;
-                               if (netif_carrier_ok(card->dev)) {
+                               if (card->dev && netif_carrier_ok(card->dev))
                                        netif_carrier_off(card->dev);
-                                       netif_stop_queue(card->dev);
-                               }
                                return NULL;
                        case IPA_CMD_STARTLAN:
                                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;
-                               if (!netif_carrier_ok(card->dev)) {
-                                       netif_carrier_on(card->dev);
-                                       netif_wake_queue(card->dev);
-                               }
-                               qeth_reset_ip_addresses(card);
+                               qeth_schedule_recovery(card);
                                return NULL;
+                       case IPA_CMD_MODCCID:
+                               return cmd;
                        case IPA_CMD_REGISTER_LOCAL_ADDR:
                                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 "
@@ -1682,6 +1772,14 @@ qeth_send_control_data_cb(struct qeth_channel *channel,
        cmd = qeth_check_ipa_data(card, iob);
        if ((cmd == NULL) && (card->state != CARD_STATE_DOWN))
                goto out;
+       /*in case of OSN : check if cmd is set */
+       if (card->info.type == QETH_CARD_TYPE_OSN &&
+           cmd &&
+           cmd->hdr.command != IPA_CMD_STARTLAN &&
+           card->osn_info.assist_cb != NULL) {
+               card->osn_info.assist_cb(card->dev, cmd);
+               goto out;
+       }
 
        spin_lock_irqsave(&card->lock, flags);
        list_for_each_entry_safe(reply, r, &card->cmd_waiter_list, list) {
@@ -1698,14 +1796,13 @@ qeth_send_control_data_cb(struct qeth_channel *channel,
                                        keep_reply = reply->callback(card,
                                                        reply,
                                                        (unsigned long)cmd);
-                               }
-                               else
+                               } else
                                        keep_reply = reply->callback(card,
                                                        reply,
                                                        (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) {
@@ -1729,6 +1826,24 @@ out:
        qeth_release_buffer(channel,iob);
 }
 
+static inline void
+qeth_prepare_control_data(struct qeth_card *card, int len,
+struct qeth_cmd_buffer *iob)
+{
+       qeth_setup_ccw(&card->write,iob->data,len);
+       iob->callback = qeth_release_buffer;
+
+       memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data),
+              &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH);
+       card->seqno.trans_hdr++;
+       memcpy(QETH_PDU_HEADER_SEQ_NO(iob->data),
+              &card->seqno.pdu_hdr, QETH_SEQ_NO_LENGTH);
+       card->seqno.pdu_hdr++;
+       memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(iob->data),
+              &card->seqno.pdu_hdr_ack, QETH_SEQ_NO_LENGTH);
+       QETH_DBF_HEX(control, 2, iob->data, QETH_DBF_CONTROL_LEN);
+}
+
 static int
 qeth_send_control_data(struct qeth_card *card, int len,
                       struct qeth_cmd_buffer *iob,
@@ -1739,24 +1854,11 @@ qeth_send_control_data(struct qeth_card *card, int len,
 {
        int rc;
        unsigned long flags;
-       struct qeth_reply *reply;
+       struct qeth_reply *reply = NULL;
        struct timer_list timer;
 
        QETH_DBF_TEXT(trace, 2, "sendctl");
 
-       qeth_setup_ccw(&card->write,iob->data,len);
-
-       memcpy(QETH_TRANSPORT_HEADER_SEQ_NO(iob->data),
-              &card->seqno.trans_hdr, QETH_SEQ_NO_LENGTH);
-       card->seqno.trans_hdr++;
-
-       memcpy(QETH_PDU_HEADER_SEQ_NO(iob->data),
-              &card->seqno.pdu_hdr, QETH_SEQ_NO_LENGTH);
-       card->seqno.pdu_hdr++;
-       memcpy(QETH_PDU_HEADER_ACK_SEQ_NO(iob->data),
-              &card->seqno.pdu_hdr_ack, QETH_SEQ_NO_LENGTH);
-       iob->callback = qeth_release_buffer;
-
        reply = qeth_alloc_reply(card);
        if (!reply) {
                PRINT_WARN("Could no alloc qeth_reply!\n");
@@ -1771,17 +1873,18 @@ qeth_send_control_data(struct qeth_card *card, int len,
        init_timer(&timer);
        timer.function = qeth_cmd_timeout;
        timer.data = (unsigned long) reply;
-       if (IS_IPA(iob->data))
-               timer.expires = jiffies + QETH_IPA_TIMEOUT;
-       else
-               timer.expires = jiffies + QETH_TIMEOUT;
        init_waitqueue_head(&reply->wait_q);
        spin_lock_irqsave(&card->lock, flags);
        list_add_tail(&reply->list, &card->cmd_waiter_list);
        spin_unlock_irqrestore(&card->lock, flags);
        QETH_DBF_HEX(control, 2, iob->data, QETH_DBF_CONTROL_LEN);
        wait_event(card->wait_q,
-                  atomic_compare_and_swap(0,1,&card->write.irq_pending) == 0);
+                  atomic_cmpxchg(&card->write.irq_pending, 0, 1) == 0);
+       qeth_prepare_control_data(card, len, iob);
+       if (IS_IPA(iob->data))
+               timer.expires = jiffies + QETH_IPA_TIMEOUT;
+       else
+               timer.expires = jiffies + QETH_TIMEOUT;
        QETH_DBF_TEXT(trace, 6, "noirqpnd");
        spin_lock_irqsave(get_ccwdev_lock(card->write.ccwdev), flags);
        rc = ccw_device_start(card->write.ccwdev, &card->write.ccw,
@@ -1808,6 +1911,62 @@ qeth_send_control_data(struct qeth_card *card, int len,
        return rc;
 }
 
+static int
+qeth_osn_send_control_data(struct qeth_card *card, int len,
+                          struct qeth_cmd_buffer *iob)
+{
+       unsigned long flags;
+       int rc = 0;
+
+       QETH_DBF_TEXT(trace, 5, "osndctrd");
+
+       wait_event(card->wait_q,
+                  atomic_cmpxchg(&card->write.irq_pending, 0, 1) == 0);
+       qeth_prepare_control_data(card, len, iob);
+       QETH_DBF_TEXT(trace, 6, "osnoirqp");
+       spin_lock_irqsave(get_ccwdev_lock(card->write.ccwdev), flags);
+       rc = ccw_device_start(card->write.ccwdev, &card->write.ccw,
+                             (addr_t) iob, 0, 0);
+       spin_unlock_irqrestore(get_ccwdev_lock(card->write.ccwdev), flags);
+       if (rc){
+               PRINT_WARN("qeth_osn_send_control_data: "
+                          "ccw_device_start rc = %i\n", rc);
+               QETH_DBF_TEXT_(trace, 2, " err%d", rc);
+               qeth_release_buffer(iob->channel, iob);
+               atomic_set(&card->write.irq_pending, 0);
+               wake_up(&card->wait_q);
+       }
+       return rc;
+}
+
+static inline void
+qeth_prepare_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+                    char prot_type)
+{
+       memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
+       memcpy(QETH_IPA_CMD_PROT_TYPE(iob->data),&prot_type,1);
+       memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
+              &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
+}
+
+static int
+qeth_osn_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+                     int data_len)
+{
+       u16 s1, s2;
+
+       QETH_DBF_TEXT(trace,4,"osndipa");
+
+       qeth_prepare_ipa_cmd(card, iob, QETH_PROT_OSN2);
+       s1 = (u16)(IPA_PDU_HEADER_SIZE + data_len);
+       s2 = (u16)data_len;
+       memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2);
+       memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2);
+       memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2);
+       memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2);
+       return qeth_osn_send_control_data(card, s1, iob);
+}
+
 static int
 qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
                  int (*reply_cb)
@@ -1815,13 +1974,18 @@ qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
                  void *reply_param)
 {
        int rc;
+       char prot_type;
 
        QETH_DBF_TEXT(trace,4,"sendipa");
 
-       memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
-       memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
-              &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
-
+       if (card->options.layer2)
+               if (card->info.type == QETH_CARD_TYPE_OSN)
+                       prot_type = QETH_PROT_OSN2;
+               else
+                       prot_type = QETH_PROT_LAYER2;
+       else
+               prot_type = QETH_PROT_TCPIP;
+       qeth_prepare_ipa_cmd(card,iob,prot_type);
        rc = qeth_send_control_data(card, IPA_CMD_LENGTH, iob,
                                    reply_cb, reply_param);
        return rc;
@@ -1951,6 +2115,7 @@ static int
 qeth_ulp_enable(struct qeth_card *card)
 {
        int rc;
+       char prot_type;
        struct qeth_cmd_buffer *iob;
 
        /*FIXME: trace view callbacks*/
@@ -1961,7 +2126,15 @@ qeth_ulp_enable(struct qeth_card *card)
 
        *(QETH_ULP_ENABLE_LINKNUM(iob->data)) =
                (__u8) card->info.portno;
+       if (card->options.layer2)
+               if (card->info.type == QETH_CARD_TYPE_OSN)
+                       prot_type = QETH_PROT_OSN2;
+               else
+                       prot_type = QETH_PROT_LAYER2;
+       else
+               prot_type = QETH_PROT_TCPIP;
 
+       memcpy(QETH_ULP_ENABLE_PROT_TYPE(iob->data),&prot_type,1);
        memcpy(QETH_ULP_ENABLE_DEST_ADDR(iob->data),
               &card->token.cm_connection_r, QETH_MPC_TOKEN_LENGTH);
        memcpy(QETH_ULP_ENABLE_FILTER_TOKEN(iob->data),
@@ -2026,36 +2199,39 @@ qeth_ulp_setup(struct qeth_card *card)
 }
 
 static inline int
-qeth_check_for_inbound_error(struct qeth_qdio_buffer *buf,
-                            unsigned int qdio_error,
-                            unsigned int siga_error)
+qeth_check_qdio_errors(struct qdio_buffer *buf, unsigned int qdio_error,
+                      unsigned int siga_error, const char *dbftext)
 {
-       int rc = 0;
-
        if (qdio_error || siga_error) {
-               QETH_DBF_TEXT(trace, 2, "qdinerr");
-               QETH_DBF_TEXT(qerr, 2, "qdinerr");
+               QETH_DBF_TEXT(trace, 2, dbftext);
+               QETH_DBF_TEXT(qerr, 2, dbftext);
                QETH_DBF_TEXT_(qerr, 2, " F15=%02X",
-                              buf->buffer->element[15].flags & 0xff);
+                              buf->element[15].flags & 0xff);
                QETH_DBF_TEXT_(qerr, 2, " F14=%02X",
-                              buf->buffer->element[14].flags & 0xff);
+                              buf->element[14].flags & 0xff);
                QETH_DBF_TEXT_(qerr, 2, " qerr=%X", qdio_error);
                QETH_DBF_TEXT_(qerr, 2, " serr=%X", siga_error);
-               rc = 1;
+               return 1;
        }
-       return rc;
+       return 0;
 }
 
 static inline struct sk_buff *
-qeth_get_skb(unsigned int length)
+qeth_get_skb(unsigned int length, struct qeth_hdr *hdr)
 {
        struct sk_buff* skb;
+       int add_len;
+
+       add_len = 0;
+       if (hdr->hdr.osn.id == QETH_HEADER_TYPE_OSN)
+               add_len = sizeof(struct qeth_hdr);
 #ifdef CONFIG_QETH_VLAN
-       if ((skb = dev_alloc_skb(length + VLAN_HLEN)))
-               skb_reserve(skb, VLAN_HLEN);
-#else
-       skb = dev_alloc_skb(length);
+       else
+               add_len = VLAN_HLEN;
 #endif
+       skb = dev_alloc_skb(length + add_len);
+       if (skb && add_len)
+               skb_reserve(skb, add_len);
        return skb;
 }
 
@@ -2084,14 +2260,27 @@ qeth_get_next_skb(struct qeth_card *card, struct qdio_buffer *buffer,
        *hdr = element->addr + offset;
 
        offset += sizeof(struct qeth_hdr);
-       skb_len = (*hdr)->length;
+       if (card->options.layer2)
+               if (card->info.type == QETH_CARD_TYPE_OSN)
+                       skb_len = (*hdr)->hdr.osn.pdu_length;
+               else
+                       skb_len = (*hdr)->hdr.l2.pkt_length;
+       else
+               skb_len = (*hdr)->hdr.l3.length;
+
        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);
-       } else if (!(skb = qeth_get_skb(skb_len)))
+               if(card->dev->type == ARPHRD_IEEE802_TR){
+                       if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_TR, *hdr)))
+                               goto no_mem;
+                       skb_reserve(skb,QETH_FAKE_LL_LEN_TR);
+               } else {
+                       if (!(skb = qeth_get_skb(skb_len+QETH_FAKE_LL_LEN_ETH, *hdr)))
+                               goto no_mem;
+                       skb_reserve(skb,QETH_FAKE_LL_LEN_ETH);
+               }
+       } else if (!(skb = qeth_get_skb(skb_len, *hdr)))
                goto no_mem;
        data_ptr = element->addr + offset;
        while (skb_len) {
@@ -2123,7 +2312,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));
        }
@@ -2131,13 +2320,13 @@ no_mem:
        return NULL;
 }
 
-static inline unsigned short
+static inline __be16
 qeth_type_trans(struct sk_buff *skb, struct net_device *dev)
 {
-       struct ethhdr *eth;
        struct qeth_card *card;
+       struct ethhdr *eth;
 
-       QETH_DBF_TEXT(trace,5,"typtrans");
+       QETH_DBF_TEXT(trace,6,"typtrans");
 
        card = (struct qeth_card *)dev->priv;
 #ifdef CONFIG_TR
@@ -2145,9 +2334,8 @@ qeth_type_trans(struct sk_buff *skb, struct net_device *dev)
            (card->info.link_type == QETH_LINK_TYPE_LANE_TR))
                return tr_type_trans(skb,dev);
 #endif /* CONFIG_TR */
-
        skb->mac.raw = skb->data;
-       skb_pull(skb, ETH_ALEN * 2 + sizeof (short));
+       skb_pull(skb, ETH_HLEN );
        eth = eth_hdr(skb);
 
        if (*eth->h_dest & 1) {
@@ -2155,9 +2343,9 @@ qeth_type_trans(struct sk_buff *skb, struct net_device *dev)
                        skb->pkt_type = PACKET_BROADCAST;
                else
                        skb->pkt_type = PACKET_MULTICAST;
-       } else {
+       } else if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
                skb->pkt_type = PACKET_OTHERHOST;
-       }
+
        if (ntohs(eth->h_proto) >= 1536)
                return eth->h_proto;
        if (*(unsigned short *) (skb->data) == 0xFFFF)
@@ -2166,14 +2354,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;
 
@@ -2190,10 +2432,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);
@@ -2206,14 +2445,24 @@ qeth_rebuild_skb_fake_ll(struct qeth_card *card, struct sk_buff *skb,
                memcpy(fake_hdr->h_dest, card->dev->dev_addr, ETH_ALEN);
        }
        /* the source MAC address */
-       if (hdr->ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
-               memcpy(fake_hdr->h_source, &hdr->dest_addr[2], ETH_ALEN);
+       if (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_SRC_MAC_ADDR)
+               memcpy(fake_hdr->h_source, &hdr->hdr.l3.dest_addr[2], ETH_ALEN);
        else
                memset(fake_hdr->h_source, 0, ETH_ALEN);
        /* the protocol */
        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)
@@ -2221,28 +2470,59 @@ qeth_rebuild_skb_vlan(struct qeth_card *card, struct sk_buff *skb,
 #ifdef CONFIG_QETH_VLAN
        u16 *vlan_tag;
 
-       if (hdr->ext_flags & QETH_HDR_EXT_VLAN_FRAME) {
+       if (hdr->hdr.l3.ext_flags &
+           (QETH_HDR_EXT_VLAN_FRAME | QETH_HDR_EXT_INCLUDE_VLAN_TAG)) {
                vlan_tag = (u16 *) skb_push(skb, VLAN_HLEN);
-               *vlan_tag = hdr->vlan_id;
+               *vlan_tag = (hdr->hdr.l3.ext_flags & QETH_HDR_EXT_VLAN_FRAME)?
+                       hdr->hdr.l3.vlan_id : *((u16 *)&hdr->hdr.l3.dest_addr[12]);
                *(vlan_tag + 1) = skb->protocol;
                skb->protocol = __constant_htons(ETH_P_8021Q);
        }
 #endif /* CONFIG_QETH_VLAN */
 }
 
+static inline __u16
+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)) {
+               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
+       *((__u32 *)skb->cb) = ++card->seqno.pkt_seqno;
+       return vlan_id;
+}
+
 static inline void
 qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
                 struct qeth_hdr *hdr)
 {
 #ifdef CONFIG_QETH_IPV6
-       if (hdr->flags & QETH_HDR_PASSTHRU){
+       if (hdr->hdr.l3.flags & QETH_HDR_PASSTHRU) {
+               skb->pkt_type = PACKET_HOST;
                skb->protocol = qeth_type_trans(skb, card->dev);
                return;
        }
 #endif /* CONFIG_QETH_IPV6 */
-       skb->protocol = htons((hdr->flags & QETH_HDR_IPV6)? ETH_P_IPV6 :
+       skb->protocol = htons((hdr->hdr.l3.flags & QETH_HDR_IPV6)? ETH_P_IPV6 :
                              ETH_P_IP);
-       switch (hdr->flags & QETH_HDR_CAST_MASK){
+       switch (hdr->hdr.l3.flags & QETH_HDR_CAST_MASK){
        case QETH_CAST_UNICAST:
                skb->pkt_type = PACKET_HOST;
                break;
@@ -2259,13 +2539,14 @@ qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
        default:
                skb->pkt_type = PACKET_HOST;
        }
+       qeth_rebuild_skb_vlan(card, skb, hdr);
        if (card->options.fake_ll)
                qeth_rebuild_skb_fake_ll(card, skb, hdr);
        else
                skb->mac.raw = skb->data;
        skb->ip_summed = card->options.checksum_type;
        if (card->options.checksum_type == HW_CHECKSUMMING){
-               if ( (hdr->ext_flags &
+               if ( (hdr->hdr.l3.ext_flags &
                      (QETH_HDR_EXT_CSUM_HDR_REQ |
                       QETH_HDR_EXT_CSUM_TRANSP_REQ)) ==
                     (QETH_HDR_EXT_CSUM_HDR_REQ |
@@ -2274,7 +2555,6 @@ qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb,
                else
                        skb->ip_summed = SW_CHECKSUMMING;
        }
-       qeth_rebuild_skb_vlan(card, skb, hdr);
 }
 
 static inline void
@@ -2282,10 +2562,11 @@ qeth_process_inbound_buffer(struct qeth_card *card,
                            struct qeth_qdio_buffer *buf, int index)
 {
        struct qdio_buffer_element *element;
-       int offset;
        struct sk_buff *skb;
        struct qeth_hdr *hdr;
+       int offset;
        int rxrc;
+       __u16 vlan_tag = 0;
 
        /* get first element of current buffer */
        element = (struct qdio_buffer_element *)&buf->buffer->element[0];
@@ -2294,15 +2575,30 @@ qeth_process_inbound_buffer(struct qeth_card *card,
        card->perf_stats.bufs_rec++;
 #endif
        while((skb = qeth_get_next_skb(card, buf->buffer, &element,
-                                      &offset, &hdr))){
-               qeth_rebuild_skb(card, skb, hdr);
+                                      &offset, &hdr))) {
+               skb->dev = card->dev;
+               if (hdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2)
+                       vlan_tag = qeth_layer2_rebuild_skb(card, skb, hdr);
+               else if (hdr->hdr.l3.id == QETH_HEADER_TYPE_LAYER3)
+                       qeth_rebuild_skb(card, skb, hdr);
+               else { /*in case of OSN*/
+                       skb_push(skb, sizeof(struct qeth_hdr));
+                       memcpy(skb->data, hdr, sizeof(struct qeth_hdr));
+               }
                /* is device UP ? */
                if (!(card->dev->flags & IFF_UP)){
                        dev_kfree_skb_any(skb);
                        continue;
                }
-               skb->dev = card->dev;
-               rxrc = netif_rx(skb);
+#ifdef CONFIG_QETH_VLAN
+               if (vlan_tag)
+                       vlan_hwaccel_rx(skb, card->vlangrp, vlan_tag);
+               else
+#endif
+               if (card->info.type == QETH_CARD_TYPE_OSN)
+                       rxrc = card->osn_info.data_cb(skb);
+               else
+                       rxrc = netif_rx(skb);
                card->dev->last_rx = jiffies;
                card->stats.rx_packets++;
                card->stats.rx_bytes += skb->len;
@@ -2362,14 +2658,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);
@@ -2465,8 +2762,9 @@ qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status,
        for (i = first_element; i < (first_element + count); ++i) {
                index = i % QDIO_MAX_BUFFERS_PER_Q;
                buffer = &card->qdio.in_q->bufs[index];
-               if (!((status == QDIO_STATUS_LOOK_FOR_ERROR) &&
-                     qeth_check_for_inbound_error(buffer, qdio_err, siga_err)))
+               if (!((status & QDIO_STATUS_LOOK_FOR_ERROR) &&
+                     qeth_check_qdio_errors(buffer->buffer,
+                                            qdio_err, siga_err,"qinerr")))
                        qeth_process_inbound_buffer(card, buffer, index);
                /* clear buffer and give back to hardware */
                qeth_put_buffer_pool_entry(card, buffer->pool_entry);
@@ -2481,12 +2779,13 @@ qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status,
 static inline int
 qeth_handle_send_error(struct qeth_card *card,
                       struct qeth_qdio_out_buffer *buffer,
-                      int qdio_err, int siga_err)
+                      unsigned int qdio_err, unsigned int siga_err)
 {
        int sbalf15 = buffer->buffer->element[15].flags & 0xff;
        int cc = siga_err & 3;
 
        QETH_DBF_TEXT(trace, 6, "hdsnderr");
+       qeth_check_qdio_errors(buffer->buffer, qdio_err, siga_err, "qouterr");
        switch (cc) {
        case 0:
                if (qdio_err){
@@ -2512,14 +2811,14 @@ qeth_handle_send_error(struct qeth_card *card,
                QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card));
                return QETH_SEND_ERROR_LINK_FAILURE;
        case 3:
+       default:
                QETH_DBF_TEXT(trace, 1, "SIGAcc3");
                QETH_DBF_TEXT_(trace,1,"%s",CARD_BUS_ID(card));
                return QETH_SEND_ERROR_KICK_IT;
        }
-       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)
 {
@@ -2560,9 +2859,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
                }
        }
 
@@ -2583,11 +2879,9 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
                queue->card->perf_stats.outbound_do_qdio_start_time;
 #endif
        if (rc){
-               QETH_DBF_SPRINTF(trace, 0, "qeth_flush_buffers: do_QDIO "
-                                "returned error (%i) on device %s.",
-                                rc, CARD_DDEV_ID(queue->card));
                QETH_DBF_TEXT(trace, 2, "flushbuf");
                QETH_DBF_TEXT_(trace, 2, " err%d", rc);
+               QETH_DBF_TEXT_(trace, 2, "%s", CARD_DDEV_ID(queue->card));
                queue->card->stats.tx_errors += count;
                /* this must not happen under normal circumstances. if it
                 * happens something is really wrong -> recover */
@@ -2684,6 +2978,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
@@ -2698,20 +2993,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);
                }
        }
 }
@@ -2730,18 +3027,14 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
        QETH_DBF_TEXT(trace, 6, "qdouhdl");
        if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
                if (status & QDIO_STATUS_ACTIVATE_CHECK_CONDITION){
-                       QETH_DBF_SPRINTF(trace, 2, "On device %s: "
-                                        "received active check "
-                                        "condition (0x%08x).",
-                                        CARD_BUS_ID(card), status);
-                       QETH_DBF_TEXT(trace, 2, "chkcond");
+                       QETH_DBF_TEXT(trace, 2, "achkcond");
+                       QETH_DBF_TEXT_(trace, 2, "%s", CARD_BUS_ID(card));
                        QETH_DBF_TEXT_(trace, 2, "%08x", status);
                        netif_stop_queue(card->dev);
                        qeth_schedule_recovery(card);
                        return;
                }
        }
-
 #ifdef CONFIG_QETH_PERF_STATS
        card->perf_stats.outbound_handler_cnt++;
        card->perf_stats.outbound_handler_start_time = qeth_get_micros();
@@ -2749,7 +3042,8 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
        for(i = first_element; i < (first_element + count); ++i){
                buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
                /*we only handle the KICK_IT error by doing a recovery */
-               if (qeth_handle_send_error(card, buffer, qdio_error, siga_error)
+               if (qeth_handle_send_error(card, buffer,
+                                          qdio_error, siga_error)
                                == QETH_SEND_ERROR_KICK_IT){
                        netif_stop_queue(card->dev);
                        qeth_schedule_recovery(card);
@@ -2759,26 +3053,19 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
        }
        atomic_sub(count, &queue->used_buffers);
        /* check if we need to do something on this outbound queue */
-       qeth_check_outbound_queue(queue);
+       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'];
@@ -2787,8 +3074,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
@@ -2838,7 +3135,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){
@@ -2846,7 +3143,7 @@ qeth_alloc_buffer_pool(struct qeth_card *card)
                        return -ENOMEM;
                }
                for(j = 0; j < QETH_MAX_BUFFER_ELEMENTS(card); ++j){
-                       ptr = (void *) __get_free_page(GFP_KERNEL);
+                       ptr = (void *) __get_free_page(GFP_KERNEL|GFP_DMA);
                        if (!ptr) {
                                while (j > 0)
                                        free_page((unsigned long)
@@ -2890,7 +3187,8 @@ qeth_alloc_qdio_buffers(struct qeth_card *card)
        if (card->qdio.state == QETH_QDIO_ALLOCATED)
                return 0;
 
-       card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q), GFP_KERNEL);
+       card->qdio.in_q = kmalloc(sizeof(struct qeth_qdio_q),
+                                 GFP_KERNEL|GFP_DMA);
        if (!card->qdio.in_q)
                return - ENOMEM;
        QETH_DBF_TEXT(setup, 2, "inq");
@@ -2915,7 +3213,7 @@ qeth_alloc_qdio_buffers(struct qeth_card *card)
        }
        for (i = 0; i < card->qdio.no_out_queues; ++i){
                card->qdio.out_qs[i] = kmalloc(sizeof(struct qeth_qdio_out_q),
-                                              GFP_KERNEL);
+                                              GFP_KERNEL|GFP_DMA);
                if (!card->qdio.out_qs[i]){
                        while (i > 0)
                                kfree(card->qdio.out_qs[--i]);
@@ -2926,12 +3224,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;
@@ -2986,9 +3285,6 @@ qeth_init_qdio_info(struct qeth_card *card)
        card->qdio.in_buf_pool.buf_count = card->qdio.init_pool.buf_count;
        INIT_LIST_HEAD(&card->qdio.in_buf_pool.entry_list);
        INIT_LIST_HEAD(&card->qdio.init_pool.entry_list);
-       /* outbound */
-       card->qdio.do_prio_queueing = QETH_PRIOQ_DEFAULT;
-       card->qdio.default_out_queue = QETH_DEFAULT_QUEUE;
 }
 
 static int
@@ -3048,10 +3344,15 @@ 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 = kzalloc(QDIO_MAX_BUFFERS_PER_Q * sizeof(char),
+                             GFP_KERNEL);
+       if (!qib_param_field)
                return -ENOMEM;
 
+       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) {
@@ -3167,26 +3468,32 @@ qeth_halt_channel(struct qeth_channel *channel)
 static int
 qeth_halt_channels(struct qeth_card *card)
 {
-       int rc = 0;
+       int rc1 = 0, rc2=0, rc3 = 0;
 
        QETH_DBF_TEXT(trace,3,"haltchs");
-       if ((rc = qeth_halt_channel(&card->read)))
-               return rc;
-       if ((rc = qeth_halt_channel(&card->write)))
-               return rc;
-       return  qeth_halt_channel(&card->data);
+       rc1 = qeth_halt_channel(&card->read);
+       rc2 = qeth_halt_channel(&card->write);
+       rc3 = qeth_halt_channel(&card->data);
+       if (rc1)
+               return rc1;
+       if (rc2)
+               return rc2;
+       return rc3;
 }
 static int
 qeth_clear_channels(struct qeth_card *card)
 {
-       int rc = 0;
+       int rc1 = 0, rc2=0, rc3 = 0;
 
        QETH_DBF_TEXT(trace,3,"clearchs");
-       if ((rc = qeth_clear_channel(&card->read)))
-               return rc;
-       if ((rc = qeth_clear_channel(&card->write)))
-               return rc;
-       return  qeth_clear_channel(&card->data);
+       rc1 = qeth_clear_channel(&card->read);
+       rc2 = qeth_clear_channel(&card->write);
+       rc3 = qeth_clear_channel(&card->data);
+       if (rc1)
+               return rc1;
+       if (rc2)
+               return rc2;
+       return rc3;
 }
 
 static int
@@ -3211,13 +3518,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;
 }
@@ -3254,23 +3563,23 @@ qeth_mpc_initialize(struct qeth_card *card)
        }
        if ((rc = qeth_cm_enable(card))){
                QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
-               return rc;
+               goto out_qdio;
        }
        if ((rc = qeth_cm_setup(card))){
                QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
-               return rc;
+               goto out_qdio;
        }
        if ((rc = qeth_ulp_enable(card))){
                QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
-               return rc;
+               goto out_qdio;
        }
        if ((rc = qeth_ulp_setup(card))){
                QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
-               return rc;
+               goto out_qdio;
        }
        if ((rc = qeth_alloc_qdio_buffers(card))){
                QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
-               return rc;
+               goto out_qdio;
        }
        if ((rc = qeth_qdio_establish(card))){
                QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
@@ -3288,7 +3597,7 @@ qeth_mpc_initialize(struct qeth_card *card)
 
        return 0;
 out_qdio:
-       qeth_qdio_clear_card(card, card->info.type==QETH_CARD_TYPE_OSAE);
+       qeth_qdio_clear_card(card, card->info.type!=QETH_CARD_TYPE_IQD);
        return rc;
 }
 
@@ -3313,12 +3622,42 @@ qeth_get_netdevice(enum qeth_card_types type, enum qeth_link_types linktype)
        case QETH_CARD_TYPE_IQD:
                dev = alloc_netdev(0, "hsi%d", ether_setup);
                break;
+       case QETH_CARD_TYPE_OSN:
+               dev = alloc_netdev(0, "osn%d", ether_setup);
+               break;
        default:
                dev = alloc_etherdev(0);
        }
        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 *);
 
@@ -3333,26 +3672,34 @@ qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (skb==NULL) {
                card->stats.tx_dropped++;
                card->stats.tx_errors++;
-               return -EIO;
+               /* return OK; otherwise ksoftirqd goes to 100% */
+               return NETDEV_TX_OK;
        }
-       if ((card->state != CARD_STATE_UP) || !netif_carrier_ok(dev)) {
+       if ((card->state != CARD_STATE_UP) || !card->lan_online) {
                card->stats.tx_dropped++;
                card->stats.tx_errors++;
                card->stats.tx_carrier_errors++;
-               return -EIO;
+               dev_kfree_skb_any(skb);
+               /* return OK; otherwise ksoftirqd goes to 100% */
+               return NETDEV_TX_OK;
        }
 #ifdef CONFIG_QETH_PERF_STATS
        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);
-
+       netif_stop_queue(dev);
+       if ((rc = qeth_send_packet(card, skb))) {
+               if (rc == -EBUSY) {
+                       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% */
+                       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;
@@ -3377,6 +3724,9 @@ qeth_verify_vlan_dev(struct net_device *dev, struct qeth_card *card)
                        break;
                }
        }
+       if (rc && !(VLAN_DEV_INFO(dev)->real_dev->priv == (void *)card))
+               return 0;
+
 #endif
        return rc;
 }
@@ -3442,16 +3792,19 @@ qeth_open(struct net_device *dev)
        if (card->state != CARD_STATE_SOFTSETUP)
                return -ENODEV;
 
-       card->dev->flags |= IFF_UP;
-       netif_start_queue(dev);
+       if ( (card->info.type != QETH_CARD_TYPE_OSN) &&
+            (card->options.layer2) &&
+            (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))) {
+               QETH_DBF_TEXT(trace,4,"nomacadr");
+               return -EPERM;
+       }
        card->data.state = CH_STATE_UP;
        card->state = CARD_STATE_UP;
+       card->dev->flags |= IFF_UP;
+       netif_start_queue(dev);
 
-       if (!card->lan_online){
-               if (netif_carrier_ok(dev))
-                       netif_carrier_off(dev);
-               netif_stop_queue(dev);
-       }
+       if (!card->lan_online && netif_carrier_ok(dev))
+               netif_carrier_off(dev);
        return 0;
 }
 
@@ -3464,7 +3817,7 @@ qeth_stop(struct net_device *dev)
 
        card = (struct qeth_card *) dev->priv;
 
-       netif_stop_queue(dev);
+       netif_tx_disable(dev);
        card->dev->flags &= ~IFF_UP;
        if (card->state == CARD_STATE_UP)
                card->state = CARD_STATE_SOFTSETUP;
@@ -3476,6 +3829,9 @@ qeth_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
 {
        int cast_type = RTN_UNSPEC;
 
+       if (card->info.type == QETH_CARD_TYPE_OSN)
+               return cast_type;
+
        if (skb->dst && skb->dst->neighbour){
                cast_type = skb->dst->neighbour->type;
                if ((cast_type == RTN_BROADCAST) ||
@@ -3491,12 +3847,12 @@ qeth_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
        else if (skb->protocol == ETH_P_IP)
                return ((skb->nh.raw[16] & 0xf0) == 0xe0) ? RTN_MULTICAST : 0;
        /* ... */
-       if (!memcmp(skb->nh.raw, skb->dev->broadcast, 6))
+       if (!memcmp(skb->data, skb->dev->broadcast, 6))
                return RTN_BROADCAST;
        else {
                u16 hdr_mac;
 
-               hdr_mac = *((u16 *)skb->nh.raw);
+               hdr_mac = *((u16 *)skb->data);
                /* tr multicast? */
                switch (card->info.link_type) {
                case QETH_LINK_TYPE_HSTR:
@@ -3504,6 +3860,7 @@ qeth_get_cast_type(struct qeth_card *card, struct sk_buff *skb)
                        if ((hdr_mac == QETH_TR_MAC_NC) ||
                            (hdr_mac == QETH_TR_MAC_C))
                                return RTN_MULTICAST;
+                       break;
                /* eth or so multicast? */
                 default:
                        if ((hdr_mac == QETH_ETH_MAC_V4) ||
@@ -3542,8 +3899,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;
        }
 }
 
@@ -3564,24 +3922,22 @@ 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;
+       int rc = 0;
 #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;
+       if (card->info.type == QETH_CARD_TYPE_OSN) {
+               *hdr = (struct qeth_hdr *)(*skb)->data;
+               return rc;
        }
+        rc = qeth_realloc_headroom(card, skb, sizeof(struct qeth_hdr));
+        if (rc)
+                return rc;
 #ifdef CONFIG_QETH_VLAN
-       if (card->vlangrp && vlan_tx_tag_present(*skb) && (ipv == 6)){
+       if (card->vlangrp && vlan_tx_tag_present(*skb) &&
+           ((ipv == 6) || card->options.layer2) ) {
                /*
                 * Move the mac addresses (6 bytes src, 6 bytes dest)
                 * to the beginning of the new header.  We are using three
@@ -3591,30 +3947,19 @@ qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
                memcpy((*skb)->data, (*skb)->data + 4, 4);
                memcpy((*skb)->data + 4, (*skb)->data + 8, 4);
                memcpy((*skb)->data + 8, (*skb)->data + 12, 4);
-               tag = (u16 *) (*skb)->data + 12;
+               tag = (u16 *)((*skb)->data + 12);
                /*
                 * first two bytes  = ETH_P_8021Q (0x8100)
                 * second two bytes = VLANID
                 */
                *tag = __constant_htons(ETH_P_8021Q);
-               *(tag + 1) = vlan_tx_tag_get(*skb);
-               *(tag + 1) = htons(*(tag + 1));
+               *(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;
 }
 
@@ -3642,80 +3987,157 @@ qeth_get_qeth_hdr_flags6(int cast_type)
 }
 
 static inline void
+qeth_layer2_get_packet_type(struct qeth_card *card, struct qeth_hdr *hdr,
+                           struct sk_buff *skb)
+{
+       __u16 hdr_mac;
+
+       if (!memcmp(skb->data+QETH_HEADER_SIZE,
+                   skb->dev->broadcast,6)) { /* broadcast? */
+               *(__u32 *)hdr->hdr.l2.flags |=
+                        QETH_LAYER2_FLAG_BROADCAST << 8;
+               return;
+       }
+       hdr_mac=*((__u16*)skb->data);
+       /* tr multicast? */
+       switch (card->info.link_type) {
+       case QETH_LINK_TYPE_HSTR:
+       case QETH_LINK_TYPE_LANE_TR:
+               if ((hdr_mac == QETH_TR_MAC_NC) ||
+                   (hdr_mac == QETH_TR_MAC_C) )
+                       *(__u32 *)hdr->hdr.l2.flags |=
+                               QETH_LAYER2_FLAG_MULTICAST << 8;
+               else
+                       *(__u32 *)hdr->hdr.l2.flags |=
+                               QETH_LAYER2_FLAG_UNICAST << 8;
+               break;
+               /* eth or so multicast? */
+       default:
+               if ( (hdr_mac==QETH_ETH_MAC_V4) ||
+                    (hdr_mac==QETH_ETH_MAC_V6) )
+                       *(__u32 *)hdr->hdr.l2.flags |=
+                               QETH_LAYER2_FLAG_MULTICAST << 8;
+               else
+                       *(__u32 *)hdr->hdr.l2.flags |=
+                               QETH_LAYER2_FLAG_UNICAST << 8;
+       }
+}
+
+static inline void
+qeth_layer2_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
+                       struct sk_buff *skb, int cast_type)
+{
+       memset(hdr, 0, sizeof(struct qeth_hdr));
+       hdr->hdr.l2.id = QETH_HEADER_TYPE_LAYER2;
+
+       /* set byte 0 to "0x02" and byte 3 to casting flags */
+       if (cast_type==RTN_MULTICAST)
+               *(__u32 *)hdr->hdr.l2.flags |= QETH_LAYER2_FLAG_MULTICAST << 8;
+       else if (cast_type==RTN_BROADCAST)
+               *(__u32 *)hdr->hdr.l2.flags |= QETH_LAYER2_FLAG_BROADCAST << 8;
+        else
+               qeth_layer2_get_packet_type(card, hdr, skb);
+
+       hdr->hdr.l2.pkt_length = skb->len-QETH_HEADER_SIZE;
+#ifdef CONFIG_QETH_VLAN
+       /* VSWITCH relies on the VLAN
+        * information to be present in
+        * the QDIO header */
+       if ((card->vlangrp != NULL) &&
+           vlan_tx_tag_present(skb)) {
+               *(__u32 *)hdr->hdr.l2.flags |= QETH_LAYER2_FLAG_VLAN << 8;
+               hdr->hdr.l2.vlan_id = vlan_tx_tag_get(skb);
+       }
+#endif
+}
+
+void
 qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr,
                struct sk_buff *skb, int ipv, int cast_type)
 {
-       hdr->id = 1;
-       hdr->ext_flags = 0;
-
        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;
+       }
+       hdr->hdr.l3.id = QETH_HEADER_TYPE_LAYER3;
+       hdr->hdr.l3.ext_flags = 0;
 #ifdef CONFIG_QETH_VLAN
        /*
         * before we're going to overwrite this location with next hop ip.
         * v6 uses passthrough, v4 sets the tag in the QDIO header.
         */
        if (card->vlangrp && vlan_tx_tag_present(skb)) {
-               hdr->ext_flags = (ipv == 4)? QETH_EXT_HDR_VLAN_FRAME :
-                                            QETH_EXT_HDR_INCLUDE_VLAN_TAG;
-               hdr->vlan_id = vlan_tx_tag_get(skb);
+               hdr->hdr.l3.ext_flags = (ipv == 4) ?
+                       QETH_HDR_EXT_VLAN_FRAME :
+                       QETH_HDR_EXT_INCLUDE_VLAN_TAG;
+               hdr->hdr.l3.vlan_id = vlan_tx_tag_get(skb);
        }
 #endif /* CONFIG_QETH_VLAN */
-       hdr->length = skb->len - sizeof(struct qeth_hdr);
+       hdr->hdr.l3.length = skb->len - sizeof(struct qeth_hdr);
        if (ipv == 4) {  /* IPv4 */
-               hdr->flags = qeth_get_qeth_hdr_flags4(cast_type);
-               memset(hdr->dest_addr, 0, 12);
+               hdr->hdr.l3.flags = qeth_get_qeth_hdr_flags4(cast_type);
+               memset(hdr->hdr.l3.dest_addr, 0, 12);
                if ((skb->dst) && (skb->dst->neighbour)) {
-                       *((u32 *) (&hdr->dest_addr[12])) =
+                       *((u32 *) (&hdr->hdr.l3.dest_addr[12])) =
                            *((u32 *) skb->dst->neighbour->primary_key);
                } else {
                        /* fill in destination address used in ip header */
-                       *((u32 *) (&hdr->dest_addr[12])) = skb->nh.iph->daddr;
+                       *((u32 *) (&hdr->hdr.l3.dest_addr[12])) = skb->nh.iph->daddr;
                }
        } else if (ipv == 6) { /* IPv6 or passthru */
-               hdr->flags = qeth_get_qeth_hdr_flags6(cast_type);
+               hdr->hdr.l3.flags = qeth_get_qeth_hdr_flags6(cast_type);
                if ((skb->dst) && (skb->dst->neighbour)) {
-                       memcpy(hdr->dest_addr,
+                       memcpy(hdr->hdr.l3.dest_addr,
                               skb->dst->neighbour->primary_key, 16);
                } else {
                        /* fill in destination address used in ip header */
-                       memcpy(hdr->dest_addr, &skb->nh.ipv6h->daddr, 16);
+                       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->flags = QETH_CAST_BROADCAST | QETH_HDR_PASSTHRU;
+                       hdr->hdr.l3.flags = QETH_CAST_BROADCAST |
+                                               QETH_HDR_PASSTHRU;
                } else {
-                       hdr->flags = (cast_type == RTN_MULTICAST) ?
+                       hdr->hdr.l3.flags = (cast_type == RTN_MULTICAST) ?
                                QETH_CAST_MULTICAST | QETH_HDR_PASSTHRU :
                                QETH_CAST_UNICAST | QETH_HDR_PASSTHRU;
                }
        }
 }
 
-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
@@ -3733,11 +4155,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
@@ -3750,25 +4212,28 @@ 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");
 
        /* spin until we get the queue ... */
-       while (atomic_compare_and_swap(QETH_OUT_Q_UNLOCKED,
-                                      QETH_OUT_Q_LOCKED,
-                                      &queue->state));
+       while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED,
+                             QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);
        /* ... now we've got the queue */
        index = queue->next_buf_to_fill;
        buffer = &queue->bufs[queue->next_buf_to_fill];
@@ -3781,30 +4246,49 @@ 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");
 
        /* spin until we get the queue ... */
-       while (atomic_compare_and_swap(QETH_OUT_Q_UNLOCKED,
-                                      QETH_OUT_Q_LOCKED,
-                                      &queue->state));
+       while (atomic_cmpxchg(&queue->state, QETH_OUT_Q_UNLOCKED,
+                             QETH_OUT_Q_LOCKED) != QETH_OUT_Q_UNLOCKED);
        start_index = queue->next_buf_to_fill;
        buffer = &queue->bufs[queue->next_buf_to_fill];
        /*
@@ -3819,34 +4303,58 @@ 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);
+       else if (!atomic_read(&queue->set_pci_flags_count))
+               atomic_swap(&queue->state, QETH_OUT_Q_LOCKED_FLUSH);
        /*
         * queue->state will go from LOCKED -> UNLOCKED or from
         * LOCKED_FLUSH -> LOCKED if output_handler wanted to 'notify' us
@@ -3854,6 +4362,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);
                /*
@@ -3862,55 +4372,152 @@ 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 elems)
+{
+       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 + elems) > QETH_MAX_BUFFER_ELEMENTS(card)){
+                PRINT_ERR("qeth_do_send_packet: invalid size of "
+                          "IP packet (Number=%d / Length=%d). Discarded.\n",
+                          (elements_needed+elems), skb->len);
+                return 0;
+        }
+        return elements_needed;
+}
+
 static inline int
 qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
 {
-       int ipv;
+       int ipv = 0;
        int cast_type;
        struct qeth_qdio_out_q *queue;
-       struct qeth_hdr *hdr;
-       int elements_needed;
+       struct qeth_hdr *hdr = NULL;
+       int elements_needed = 0;
+       enum qeth_large_send_types large_send = QETH_LARGE_SEND_NO;
+       struct qeth_eddp_context *ctx = NULL;
+       int tx_bytes = skb->len;
+       unsigned short nr_frags = skb_shinfo(skb)->nr_frags;
        int rc;
 
        QETH_DBF_TEXT(trace, 6, "sendpkt");
 
-       ipv = qeth_get_ip_version(skb);
+       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);
+                       }
+               }
+       }
+       if ((card->info.type == QETH_CARD_TYPE_OSN) &&
+               (skb->protocol == htons(ETH_P_IPV6))) {
+               dev_kfree_skb_any(skb);
+               return 0;
+       }
        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 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_is_gso(skb))
+               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;
+               }
+               if (card->info.type != QETH_CARD_TYPE_OSN)
+                       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 {
+               int elems = qeth_get_elements_no(card,(void*) hdr, skb,
+                                                elements_needed);
+               if (!elems)
+                       return -EINVAL;
+               elements_needed += elems;
        }
 
        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;
+               card->stats.tx_bytes += tx_bytes;
+#ifdef CONFIG_QETH_PERF_STATS
+               if (skb_is_gso(skb) && !(large_send == QETH_LARGE_SEND_NO)) {
+                       card->perf_stats.large_send_bytes += skb->len;
+                       card->perf_stats.large_send_cnt++;
+               }
+               if (nr_frags > 0){
+                       card->perf_stats.sg_skbs_sent++;
+                       /* nr_frags + skb->data */
+                       card->perf_stats.sg_frags_sent +=
+                               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;
 }
@@ -3925,6 +4532,7 @@ qeth_mdio_read(struct net_device *dev, int phy_id, int regnum)
        case MII_BMCR: /* Basic mode control register */
                rc = BMCR_FULLDPLX;
                if ((card->info.link_type != QETH_LINK_TYPE_GBIT_ETH)&&
+                   (card->info.link_type != QETH_LINK_TYPE_OSN) &&
                    (card->info.link_type != QETH_LINK_TYPE_10GBIT_ETH))
                        rc |= BMCR_SPEED100;
                break;
@@ -3974,38 +4582,11 @@ qeth_mdio_read(struct net_device *dev, int phy_id, int regnum)
        case MII_NCONFIG: /* network interface config */
                break;
        default:
-               rc = 0;
                break;
        }
        return rc;
 }
 
-static void
-qeth_mdio_write(struct net_device *dev, int phy_id, int regnum, int value)
-{
-       switch(regnum){
-       case MII_BMCR: /* Basic mode control register */
-       case MII_BMSR: /* Basic mode status register */
-       case MII_PHYSID1: /* PHYS ID 1 */
-       case MII_PHYSID2: /* PHYS ID 2 */
-       case MII_ADVERTISE: /* Advertisement control reg */
-       case MII_LPA: /* Link partner ability reg */
-       case MII_EXPANSION: /* Expansion register */
-       case MII_DCOUNTER: /* disconnect counter */
-       case MII_FCSCOUNTER: /* false carrier counter */
-       case MII_NWAYTEST: /* N-way auto-neg test register */
-       case MII_RERRCOUNTER: /* rx error counter */
-       case MII_SREVISION: /* silicon revision */
-       case MII_RESV1: /* reserved 1 */
-       case MII_LBRERROR: /* loopback, rx, bypass error */
-       case MII_PHYADDR: /* physical address */
-       case MII_RESV2: /* reserved 2 */
-       case MII_TPISTATUS: /* TPI status for 10mbps */
-       case MII_NCONFIG: /* network interface config */
-       default:
-               break;
-       }
-}
 
 static inline const char *
 qeth_arp_get_error_cause(int *rc)
@@ -4043,12 +4624,16 @@ qeth_arp_set_no_entries(struct qeth_card *card, int no_entries)
 
        QETH_DBF_TEXT(trace,3,"arpstnoe");
 
-       /* TODO: really not supported by GuestLAN? */
+       /*
+        * currently GuestLAN only supports the ARP assist function
+        * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_SET_NO_ENTRIES;
+        * thus we say EOPNOTSUPP for this ARP function
+        */
        if (card->info.guestlan)
                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,
@@ -4058,7 +4643,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;
@@ -4220,26 +4805,17 @@ qeth_arp_query(struct qeth_card *card, char *udata)
 
        QETH_DBF_TEXT(trace,3,"arpquery");
 
-       /*
-        * currently GuestLAN  does only deliver all zeros on query arp,
-        * even though arp processing is supported (according to IPA supp.
-        * funcs flags); since all zeros is no valueable information,
-        * we say EOPNOTSUPP for all ARP functions
-        */
-       if (card->info.guestlan)
-               return -EOPNOTSUPP;
        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 */
        if (copy_from_user(&qinfo, udata, 6))
                return -EFAULT;
-       if (!(qinfo.udata = kmalloc(qinfo.udata_len, GFP_KERNEL)))
+       if (!(qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL)))
                return -ENOMEM;
-       memset(qinfo.udata, 0, qinfo.udata_len);
        qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET;
        iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
                                       IPA_CMD_ASS_ARP_QUERY_INFO,
@@ -4252,7 +4828,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 {
@@ -4366,9 +4942,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 */
@@ -4385,11 +4963,10 @@ qeth_snmp_command(struct qeth_card *card, char *udata)
                return -EFAULT;
        }
        qinfo.udata_len = ureq->hdr.data_len;
-       if (!(qinfo.udata = kmalloc(qinfo.udata_len, GFP_KERNEL))){
+       if (!(qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL))){
                kfree(ureq);
                return -ENOMEM;
        }
-       memset(qinfo.udata, 0, qinfo.udata_len);
        qinfo.udata_offset = sizeof(struct qeth_snmp_ureq_hdr);
 
        iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL,
@@ -4400,7 +4977,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);
 
@@ -4414,6 +4991,10 @@ qeth_default_setassparms_cb(struct qeth_card *, struct qeth_reply *,
                            unsigned long);
 
 static int
+qeth_default_setadapterparms_cb(struct qeth_card *card,
+                                struct qeth_reply *reply,
+                                unsigned long data);
+static int
 qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *,
                      __u16, long,
                      int (*reply_cb)
@@ -4431,16 +5012,15 @@ qeth_arp_add_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry)
        QETH_DBF_TEXT(trace,3,"arpadent");
 
        /*
-        * currently GuestLAN  does only deliver all zeros on query arp,
-        * even though arp processing is supported (according to IPA supp.
-        * funcs flags); since all zeros is no valueable information,
-        * we say EOPNOTSUPP for all ARP functions
+        * currently GuestLAN only supports the ARP assist function
+        * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_ADD_ENTRY;
+        * thus we say EOPNOTSUPP for this ARP function
         */
        if (card->info.guestlan)
                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;
        }
 
@@ -4457,7 +5037,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;
@@ -4474,16 +5054,15 @@ qeth_arp_remove_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry
        QETH_DBF_TEXT(trace,3,"arprment");
 
        /*
-        * currently GuestLAN  does only deliver all zeros on query arp,
-        * even though arp processing is supported (according to IPA supp.
-        * funcs flags); since all zeros is no valueable information,
-        * we say EOPNOTSUPP for all ARP functions
+        * currently GuestLAN only supports the ARP assist function
+        * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_REMOVE_ENTRY;
+        * thus we say EOPNOTSUPP for this ARP function
         */
        if (card->info.guestlan)
                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);
@@ -4500,7 +5079,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;
@@ -4515,16 +5094,15 @@ qeth_arp_flush_cache(struct qeth_card *card)
        QETH_DBF_TEXT(trace,3,"arpflush");
 
        /*
-        * currently GuestLAN  does only deliver all zeros on query arp,
-        * even though arp processing is supported (according to IPA supp.
-        * funcs flags); since all zeros is no valueable information,
-        * we say EOPNOTSUPP for all ARP functions
-        */
+        * currently GuestLAN only supports the ARP assist function
+        * IPA_CMD_ASS_ARP_QUERY_INFO, but not IPA_CMD_ASS_ARP_FLUSH_CACHE;
+        * thus we say EOPNOTSUPP for this ARP function
+       */
        if (card->info.guestlan || (card->info.type == QETH_CARD_TYPE_IQD))
                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,
@@ -4532,7 +5110,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;
@@ -4553,23 +5131,29 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
             (card->state != CARD_STATE_SOFTSETUP))
                return -ENODEV;
 
+       if (card->info.type == QETH_CARD_TYPE_OSN)
+               return -EPERM;
+
        switch (cmd){
        case SIOC_QETH_ARP_SET_NO_ENTRIES:
-               if (!capable(CAP_NET_ADMIN)){
+               if ( !capable(CAP_NET_ADMIN) ||
+                    (card->options.layer2) ) {
                        rc = -EPERM;
                        break;
                }
                rc = qeth_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue);
                break;
        case SIOC_QETH_ARP_QUERY_INFO:
-               if (!capable(CAP_NET_ADMIN)){
+               if ( !capable(CAP_NET_ADMIN) ||
+                    (card->options.layer2) ) {
                        rc = -EPERM;
                        break;
                }
                rc = qeth_arp_query(card, rq->ifr_ifru.ifru_data);
                break;
        case SIOC_QETH_ARP_ADD_ENTRY:
-               if (!capable(CAP_NET_ADMIN)){
+               if ( !capable(CAP_NET_ADMIN) ||
+                    (card->options.layer2) ) {
                        rc = -EPERM;
                        break;
                }
@@ -4580,7 +5164,8 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                        rc = qeth_arp_add_entry(card, &arp_entry);
                break;
        case SIOC_QETH_ARP_REMOVE_ENTRY:
-               if (!capable(CAP_NET_ADMIN)){
+               if ( !capable(CAP_NET_ADMIN) ||
+                    (card->options.layer2) ) {
                        rc = -EPERM;
                        break;
                }
@@ -4591,7 +5176,8 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                        rc = qeth_arp_remove_entry(card, &arp_entry);
                break;
        case SIOC_QETH_ARP_FLUSH_CACHE:
-               if (!capable(CAP_NET_ADMIN)){
+               if ( !capable(CAP_NET_ADMIN) ||
+                    (card->options.layer2) ) {
                        rc = -EPERM;
                        break;
                }
@@ -4618,21 +5204,6 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                        mii_data->val_out = qeth_mdio_read(dev,mii_data->phy_id,
                                                           mii_data->reg_num);
                break;
-       case SIOCSMIIREG:
-               rc = -EOPNOTSUPP;
-               break;
-               /* TODO: remove return if qeth_mdio_write does something */
-               if (!capable(CAP_NET_ADMIN)){
-                       rc = -EPERM;
-                       break;
-               }
-               mii_data = if_mii(rq);
-               if (mii_data->phy_id != 0)
-                       rc = -EINVAL;
-               else
-                       qeth_mdio_write(dev, mii_data->phy_id, mii_data->reg_num,
-                                       mii_data->val_in);
-               break;
        default:
                rc = -EOPNOTSUPP;
        }
@@ -4735,13 +5306,12 @@ qeth_free_vlan_addresses4(struct qeth_card *card, unsigned short vid)
        struct qeth_ipaddr *addr;
 
        QETH_DBF_TEXT(trace, 4, "frvaddr4");
-       if (!card->vlangrp)
-               return;
+
        rcu_read_lock();
-       in_dev = __in_dev_get(card->vlangrp->vlan_devices[vid]);
+       in_dev = __in_dev_get_rcu(card->vlangrp->vlan_devices[vid]);
        if (!in_dev)
                goto out;
-       for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next){
+       for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
                addr = qeth_get_addr_buffer(QETH_PROT_IPV4);
                if (addr){
                        addr->u.a4.addr = ifa->ifa_address;
@@ -4758,13 +5328,13 @@ 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;
 
        QETH_DBF_TEXT(trace, 4, "frvaddr6");
-       if (!card->vlangrp)
-               return;
+
        in6_dev = in6_dev_get(card->vlangrp->vlan_devices[vid]);
        if (!in6_dev)
                return;
@@ -4780,58 +5350,198 @@ qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid)
                }
        }
        in6_dev_put(in6_dev);
+#endif /* CONFIG_QETH_IPV6 */
+}
+
+static void
+qeth_free_vlan_addresses(struct qeth_card *card, unsigned short vid)
+{
+       if (card->options.layer2 || !card->vlangrp)
+               return;
+       qeth_free_vlan_addresses4(card, vid);
+       qeth_free_vlan_addresses6(card, vid);
+}
+
+static int
+qeth_layer2_send_setdelvlan_cb(struct qeth_card *card,
+                               struct qeth_reply *reply,
+                               unsigned long data)
+{
+        struct qeth_ipa_cmd *cmd;
+
+        QETH_DBF_TEXT(trace, 2, "L2sdvcb");
+        cmd = (struct qeth_ipa_cmd *) data;
+        if (cmd->hdr.return_code) {
+               PRINT_ERR("Error in processing VLAN %i on %s: 0x%x. "
+                         "Continuing\n",cmd->data.setdelvlan.vlan_id,
+                         QETH_CARD_IFNAME(card), cmd->hdr.return_code);
+               QETH_DBF_TEXT_(trace, 2, "L2VL%4x", cmd->hdr.command);
+               QETH_DBF_TEXT_(trace, 2, "L2%s", CARD_BUS_ID(card));
+               QETH_DBF_TEXT_(trace, 2, "err%d", cmd->hdr.return_code);
+       }
+        return 0;
+}
+
+static int
+qeth_layer2_send_setdelvlan(struct qeth_card *card, __u16 i,
+                           enum qeth_ipa_cmds ipacmd)
+{
+       struct qeth_ipa_cmd *cmd;
+       struct qeth_cmd_buffer *iob;
+
+       QETH_DBF_TEXT_(trace, 4, "L2sdv%x",ipacmd);
+       iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
+       cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+        cmd->data.setdelvlan.vlan_id = i;
+       return qeth_send_ipa_cmd(card, iob,
+                                qeth_layer2_send_setdelvlan_cb, NULL);
+}
+
+static void
+qeth_layer2_process_vlans(struct qeth_card *card, int clear)
+{
+        unsigned short  i;
+
+       QETH_DBF_TEXT(trace, 3, "L2prcvln");
+
+       if (!card->vlangrp)
+               return;
+       for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) {
+               if (card->vlangrp->vlan_devices[i] == NULL)
+                       continue;
+               if (clear)
+                       qeth_layer2_send_setdelvlan(card, i, IPA_CMD_DELVLAN);
+               else
+                       qeth_layer2_send_setdelvlan(card, i, IPA_CMD_SETVLAN);
+        }
+}
+
+/*add_vid is layer 2 used only ....*/
+static void
+qeth_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
+{
+       struct qeth_card *card;
+
+       QETH_DBF_TEXT_(trace, 4, "aid:%d", vid);
+
+       card = (struct qeth_card *) dev->priv;
+       if (!card->options.layer2)
+               return;
+       qeth_layer2_send_setdelvlan(card, vid, IPA_CMD_SETVLAN);
 }
 
+/*... kill_vid used for both modes*/
 static void
 qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
 {
        struct qeth_card *card;
        unsigned long flags;
 
-       QETH_DBF_TEXT(trace,4,"vlkilvid");
+       QETH_DBF_TEXT_(trace, 4, "kid:%d", vid);
 
        card = (struct qeth_card *) dev->priv;
        /* free all skbs for the vlan device */
        qeth_free_vlan_skbs(card, vid);
        spin_lock_irqsave(&card->vlanlock, flags);
        /* unregister IP addresses of vlan device */
-       qeth_free_vlan_addresses4(card, vid);
-       qeth_free_vlan_addresses6(card, vid);
+       qeth_free_vlan_addresses(card, vid);
        if (card->vlangrp)
                card->vlangrp->vlan_devices[vid] = NULL;
        spin_unlock_irqrestore(&card->vlanlock, flags);
-       if ( (qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD) == 0) ||
-            (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0) )
-               schedule_work(&card->kernel_thread_starter);
+       if (card->options.layer2)
+               qeth_layer2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN);
+       qeth_set_multicast_list(card->dev);
 }
 #endif
-
+/**
+ * Examine hardware response to SET_PROMISC_MODE
+ */
 static int
-qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np)
+qeth_setadp_promisc_mode_cb(struct qeth_card *card,
+                           struct qeth_reply *reply,
+                           unsigned long data)
 {
+       struct qeth_ipa_cmd *cmd;
+       struct qeth_ipacmd_setadpparms *setparms;
+
+       QETH_DBF_TEXT(trace,4,"prmadpcb");
+
+       cmd = (struct qeth_ipa_cmd *) data;
+       setparms = &(cmd->data.setadapterparms);
+
+        qeth_default_setadapterparms_cb(card, reply, (unsigned long)cmd);
+       if (cmd->hdr.return_code) {
+               QETH_DBF_TEXT_(trace,4,"prmrc%2.2x",cmd->hdr.return_code);
+               setparms->data.mode = SET_PROMISC_MODE_OFF;
+       }
+       card->info.promisc_mode = setparms->data.mode;
        return 0;
 }
+/*
+ * Set promiscuous mode (on or off) (SET_PROMISC_MODE command)
+ */
+static void
+qeth_setadp_promisc_mode(struct qeth_card *card)
+{
+       enum qeth_ipa_promisc_modes mode;
+       struct net_device *dev = card->dev;
+       struct qeth_cmd_buffer *iob;
+       struct qeth_ipa_cmd *cmd;
 
-#ifdef CONFIG_QETH_IPV6
-int
-qeth_ipv6_generate_eui64(u8 * eui, struct net_device *dev)
+       QETH_DBF_TEXT(trace, 4, "setprom");
+
+       if (((dev->flags & IFF_PROMISC) &&
+            (card->info.promisc_mode == SET_PROMISC_MODE_ON)) ||
+           (!(dev->flags & IFF_PROMISC) &&
+            (card->info.promisc_mode == SET_PROMISC_MODE_OFF)))
+               return;
+       mode = SET_PROMISC_MODE_OFF;
+       if (dev->flags & IFF_PROMISC)
+               mode = SET_PROMISC_MODE_ON;
+       QETH_DBF_TEXT_(trace, 4, "mode:%x", mode);
+
+       iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_PROMISC_MODE,
+                       sizeof(struct qeth_ipacmd_setadpparms));
+       cmd = (struct qeth_ipa_cmd *)(iob->data + IPA_PDU_HEADER_SIZE);
+       cmd->data.setadapterparms.data.mode = mode;
+       qeth_send_ipa_cmd(card, iob, qeth_setadp_promisc_mode_cb, NULL);
+}
+
+/**
+ * set multicast address on card
+ */
+static void
+qeth_set_multicast_list(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;
+       struct qeth_card *card = (struct qeth_card *) dev->priv;
+
+       if (card->info.type == QETH_CARD_TYPE_OSN)
+               return ;
+
+       QETH_DBF_TEXT(trace, 3, "setmulti");
+       qeth_delete_mc_addresses(card);
+       if (card->options.layer2) {
+               qeth_layer2_add_multicast(card);
+               goto out;
        }
-       return -1;
+       qeth_add_multicast_ipv4(card);
+#ifdef CONFIG_QETH_IPV6
+       qeth_add_multicast_ipv6(card);
+#endif
+out:
+       if (qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD) == 0)
+               schedule_work(&card->kernel_thread_starter);
+       if (!qeth_adp_supported(card, IPA_SETADP_SET_PROMISC_MODE))
+               return;
+       if (qeth_set_thread_start_bit(card, QETH_SET_PROMISC_MODE_THREAD)==0)
+               schedule_work(&card->kernel_thread_starter);
+}
 
+static int
+qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np)
+{
+       return 0;
 }
-#endif
 
 static void
 qeth_get_mac_for_ipm(__u32 ipm, char *mac, struct net_device *dev)
@@ -4847,38 +5557,120 @@ qeth_get_addr_buffer(enum qeth_prot_versions prot)
 {
        struct qeth_ipaddr *addr;
 
-       addr = kmalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC);
+       addr = kzalloc(sizeof(struct qeth_ipaddr), GFP_ATOMIC);
        if (addr == NULL) {
                PRINT_WARN("Not enough memory to add address\n");
                return NULL;
        }
-       memset(addr,0,sizeof(struct qeth_ipaddr));
        addr->type = QETH_IP_TYPE_NORMAL;
        addr->proto = prot;
        return addr;
 }
 
+int
+qeth_osn_assist(struct net_device *dev,
+               void *data,
+               int data_len)
+{
+       struct qeth_cmd_buffer *iob;
+       struct qeth_card *card;
+       int rc;
+
+       QETH_DBF_TEXT(trace, 2, "osnsdmc");
+       if (!dev)
+               return -ENODEV;
+       card = (struct qeth_card *)dev->priv;
+       if (!card)
+               return -ENODEV;
+       if ((card->state != CARD_STATE_UP) &&
+           (card->state != CARD_STATE_SOFTSETUP))
+               return -ENODEV;
+       iob = qeth_wait_for_buffer(&card->write);
+       memcpy(iob->data+IPA_PDU_HEADER_SIZE, data, data_len);
+       rc = qeth_osn_send_ipa_cmd(card, iob, data_len);
+       return rc;
+}
+
+static struct net_device *
+qeth_netdev_by_devno(unsigned char *read_dev_no)
+{
+       struct qeth_card *card;
+       struct net_device *ndev;
+       unsigned char *readno;
+       __u16 temp_dev_no, card_dev_no;
+       char *endp;
+       unsigned long flags;
+
+       ndev = NULL;
+       memcpy(&temp_dev_no, read_dev_no, 2);
+       read_lock_irqsave(&qeth_card_list.rwlock, flags);
+       list_for_each_entry(card, &qeth_card_list.list, list) {
+               readno = CARD_RDEV_ID(card);
+               readno += (strlen(readno) - 4);
+               card_dev_no = simple_strtoul(readno, &endp, 16);
+               if (card_dev_no == temp_dev_no) {
+                       ndev = card->dev;
+                       break;
+               }
+       }
+       read_unlock_irqrestore(&qeth_card_list.rwlock, flags);
+       return ndev;
+}
+
+int
+qeth_osn_register(unsigned char *read_dev_no,
+                 struct net_device **dev,
+                 int (*assist_cb)(struct net_device *, void *),
+                 int (*data_cb)(struct sk_buff *))
+{
+       struct qeth_card * card;
+
+       QETH_DBF_TEXT(trace, 2, "osnreg");
+       *dev = qeth_netdev_by_devno(read_dev_no);
+       if (*dev == NULL)
+               return -ENODEV;
+       card = (struct qeth_card *)(*dev)->priv;
+       if (!card)
+               return -ENODEV;
+       if ((assist_cb == NULL) || (data_cb == NULL))
+               return -EINVAL;
+       card->osn_info.assist_cb = assist_cb;
+       card->osn_info.data_cb = data_cb;
+       return 0;
+}
+
+void
+qeth_osn_deregister(struct net_device * dev)
+{
+       struct qeth_card *card;
+
+       QETH_DBF_TEXT(trace, 2, "osndereg");
+       if (!dev)
+               return;
+       card = (struct qeth_card *)dev->priv;
+       if (!card)
+               return;
+       card->osn_info.assist_cb = NULL;
+       card->osn_info.data_cb = NULL;
+       return;
+}
+
 static void
 qeth_delete_mc_addresses(struct qeth_card *card)
 {
-       struct qeth_ipaddr *ipm, *iptodo;
+       struct qeth_ipaddr *iptodo;
        unsigned long flags;
 
        QETH_DBF_TEXT(trace,4,"delmc");
-       spin_lock_irqsave(&card->ip_lock, flags);
-       list_for_each_entry(ipm, &card->ip_list, entry){
-               if (!ipm->is_multicast)
-                       continue;
-               iptodo = qeth_get_addr_buffer(ipm->proto);
-               if (!iptodo) {
-                       QETH_DBF_TEXT(trace, 2, "dmcnomem");
-                       continue;
-               }
-               memcpy(iptodo, ipm, sizeof(struct qeth_ipaddr));
-               iptodo->users = iptodo->users * -1;
-               if (!__qeth_insert_ip_todo(card, iptodo, 0))
-                       kfree(iptodo);
+       iptodo = qeth_get_addr_buffer(QETH_PROT_IPV4);
+       if (!iptodo) {
+               QETH_DBF_TEXT(trace, 2, "dmcnomem");
+               return;
        }
+       iptodo->type = QETH_IP_TYPE_DEL_ALL_MC;
+       spin_lock_irqsave(&card->ip_lock, flags);
+       if (!__qeth_insert_ip_todo(card, iptodo, 0))
+               kfree(iptodo);
        spin_unlock_irqrestore(&card->ip_lock, flags);
 }
 
@@ -4912,8 +5704,9 @@ qeth_add_vlan_mc(struct qeth_card *card)
        int i;
 
        QETH_DBF_TEXT(trace,4,"addmcvl");
-       if (!qeth_is_supported(card,IPA_FULL_VLAN) ||
-           (card->vlangrp == NULL))
+       if ( ((card->options.layer2 == 0) &&
+             (!qeth_is_supported(card,IPA_FULL_VLAN))) ||
+            (card->vlangrp == NULL) )
                return ;
 
        vg = card->vlangrp;
@@ -4948,6 +5741,24 @@ qeth_add_multicast_ipv4(struct qeth_card *card)
        in_dev_put(in4_dev);
 }
 
+static void
+qeth_layer2_add_multicast(struct qeth_card *card)
+{
+       struct qeth_ipaddr *ipm;
+       struct dev_mc_list *dm;
+
+       QETH_DBF_TEXT(trace,4,"L2addmc");
+       for (dm = card->dev->mc_list; dm; dm = dm->next) {
+               ipm = qeth_get_addr_buffer(QETH_PROT_IPV4);
+               if (!ipm)
+                       continue;
+               memcpy(ipm->mac,dm->dmi_addr,MAX_ADDR_LEN);
+               ipm->is_multicast = 1;
+               if (!qeth_add_ip(card, ipm))
+                       kfree(ipm);
+       }
+}
+
 #ifdef CONFIG_QETH_IPV6
 static inline void
 qeth_add_mc6(struct qeth_card *card, struct inet6_dev *in6_dev)
@@ -4980,8 +5791,9 @@ qeth_add_vlan_mc6(struct qeth_card *card)
        int i;
 
        QETH_DBF_TEXT(trace,4,"admc6vl");
-       if (!qeth_is_supported(card,IPA_FULL_VLAN) ||
-           (card->vlangrp == NULL))
+       if ( ((card->options.layer2 == 0) &&
+             (!qeth_is_supported(card,IPA_FULL_VLAN))) ||
+            (card->vlangrp == NULL))
                return ;
 
        vg = card->vlangrp;
@@ -5008,7 +5820,6 @@ qeth_add_multicast_ipv6(struct qeth_card *card)
        QETH_DBF_TEXT(trace,4,"chkmcv6");
        if (!qeth_is_supported(card, IPA_IPV6))
                return ;
-
        in6_dev = in6_dev_get(card->dev);
        if (in6_dev == NULL)
                return;
@@ -5018,21 +5829,190 @@ qeth_add_multicast_ipv6(struct qeth_card *card)
        read_unlock(&in6_dev->lock);
        in6_dev_put(in6_dev);
 }
-#endif /* CONFIG_QETH_IPV6 */
+#endif /* CONFIG_QETH_IPV6 */
+
+static int
+qeth_layer2_send_setdelmac(struct qeth_card *card, __u8 *mac,
+                          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;
+
+       QETH_DBF_TEXT(trace, 2, "L2sdmac");
+       iob = qeth_get_ipacmd_buffer(card, ipacmd, QETH_PROT_IPV4);
+       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, 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.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
+               cmd->hdr.return_code = -EIO;
+       } else {
+               card->info.mac_bits |= QETH_LAYER2_MAC_REGISTERED;
+               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.mac_bits &= ~QETH_LAYER2_MAC_REGISTERED;
+
+       return 0;
+}
+static int
+qeth_layer2_send_delmac(struct qeth_card *card, __u8 *mac)
+{
+       QETH_DBF_TEXT(trace, 2, "L2Delmac");
+       if (!(card->info.mac_bits & QETH_LAYER2_MAC_REGISTERED))
+               return 0;
+       return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_DELVMAC,
+                                         qeth_layer2_send_delmac_cb);
+}
 
-/**
- * set multicast address on card
- */
-static void
-qeth_set_multicast_list(struct net_device *dev)
+static int
+qeth_layer2_set_mac_address(struct net_device *dev, void *p)
 {
+       struct sockaddr *addr = p;
        struct qeth_card *card;
+       int rc = 0;
+
+       QETH_DBF_TEXT(trace, 3, "setmac");
 
-       QETH_DBF_TEXT(trace,3,"setmulti");
+       if (qeth_verify_dev(dev) != QETH_REAL_CARD) {
+               QETH_DBF_TEXT(trace, 3, "setmcINV");
+               return -EOPNOTSUPP;
+       }
        card = (struct qeth_card *) dev->priv;
 
-       if (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0)
-               schedule_work(&card->kernel_thread_starter);
+       if (!card->options.layer2) {
+               PRINT_WARN("Setting MAC address on %s is not supported "
+                          "in Layer 3 mode.\n", dev->name);
+               QETH_DBF_TEXT(trace, 3, "setmcLY3");
+               return -EOPNOTSUPP;
+       }
+       if (card->info.type == QETH_CARD_TYPE_OSN) {
+               PRINT_WARN("Setting MAC address on %s is not supported.\n",
+                          dev->name);
+               QETH_DBF_TEXT(trace, 3, "setmcOSN");
+               return -EOPNOTSUPP;
+       }
+       QETH_DBF_TEXT_(trace, 3, "%s", CARD_BUS_ID(card));
+       QETH_DBF_HEX(trace, 3, addr->sa_data, OSA_ADDR_LEN);
+       rc = qeth_layer2_send_delmac(card, &card->dev->dev_addr[0]);
+       if (!rc)
+               rc = qeth_layer2_send_setmac(card, addr->sa_data);
+       return rc;
 }
 
 static void
@@ -5045,7 +6025,10 @@ qeth_fill_ipacmd_header(struct qeth_card *card, struct qeth_ipa_cmd *cmd,
        cmd->hdr.seqno = card->seqno.ipa;
        cmd->hdr.adapter_type = qeth_get_ipa_adp_type(card->info.link_type);
        cmd->hdr.rel_adapter_no = (__u8) card->info.portno;
-       cmd->hdr.prim_version_no = 1;
+       if (card->options.layer2)
+               cmd->hdr.prim_version_no = 2;
+       else
+               cmd->hdr.prim_version_no = 1;
        cmd->hdr.param_count = 1;
        cmd->hdr.prot_version = prot;
        cmd->hdr.ipa_supported = 0;
@@ -5136,9 +6119,32 @@ qeth_send_setdelip(struct qeth_card *card, struct qeth_ipaddr *addr,
 }
 
 static int
-qeth_register_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
+qeth_layer2_register_addr_entry(struct qeth_card *card,
+                               struct qeth_ipaddr *addr)
 {
-       //char buf[50];
+       if (!addr->is_multicast)
+               return 0;
+       QETH_DBF_TEXT(trace, 2, "setgmac");
+       QETH_DBF_HEX(trace,3,&addr->mac[0],OSA_ADDR_LEN);
+       return qeth_layer2_send_setgroupmac(card, &addr->mac[0]);
+}
+
+static int
+qeth_layer2_deregister_addr_entry(struct qeth_card *card,
+                                 struct qeth_ipaddr *addr)
+{
+       if (!addr->is_multicast)
+               return 0;
+       QETH_DBF_TEXT(trace, 2, "delgmac");
+       QETH_DBF_HEX(trace,3,&addr->mac[0],OSA_ADDR_LEN);
+       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];
        int rc;
        int cnt = 3;
 
@@ -5164,18 +6170,16 @@ qeth_register_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
        } 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;
 }
 
 static int
-qeth_deregister_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
+qeth_layer3_deregister_addr_entry(struct qeth_card *card,
+                                 struct qeth_ipaddr *addr)
 {
        //char buf[50];
        int rc;
@@ -5208,6 +6212,129 @@ qeth_deregister_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
        return rc;
 }
 
+static int
+qeth_register_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
+{
+       if (card->options.layer2)
+               return qeth_layer2_register_addr_entry(card, addr);
+
+       return qeth_layer3_register_addr_entry(card, addr);
+}
+
+static int
+qeth_deregister_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
+{
+       if (card->options.layer2)
+               return qeth_layer2_deregister_addr_entry(card, 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)
 {
@@ -5230,8 +6357,9 @@ qeth_netdev_init(struct net_device *dev)
 #ifdef CONFIG_QETH_VLAN
        dev->vlan_rx_register = qeth_vlan_rx_register;
        dev->vlan_rx_kill_vid = qeth_vlan_rx_kill_vid;
+       dev->vlan_rx_add_vid = qeth_vlan_rx_add_vid;
 #endif
-       if (qeth_get_netdev_flags(card->info.type) & IFF_NOARP) {
+       if (qeth_get_netdev_flags(card) & IFF_NOARP) {
                dev->rebuild_header = NULL;
                dev->hard_header = NULL;
                dev->header_cache_update = NULL;
@@ -5239,24 +6367,24 @@ qeth_netdev_init(struct net_device *dev)
        }
 #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
+       if (card->options.fake_ll &&
+               (qeth_get_netdev_flags(card) & IFF_NOARP))
+                       dev->hard_header = qeth_fake_header;
        dev->hard_header_parse = NULL;
-       dev->set_mac_address = NULL;
-       dev->flags |= qeth_get_netdev_flags(card->info.type);
+       dev->set_mac_address = qeth_layer2_set_mac_address;
+       dev->flags |= qeth_get_netdev_flags(card);
        if ((card->options.fake_broadcast) ||
            (card->info.broadcast_capable))
                dev->flags |= IFF_BROADCAST;
-
        dev->hard_header_len =
-               qeth_get_hlen(card->info.link_type) + card->options.add_hhlen;
+                       qeth_get_hlen(card->info.link_type) + card->options.add_hhlen;
        dev->addr_len = OSA_ADDR_LEN;
        dev->mtu = card->info.initial_mtu;
-
+       if (card->info.type != QETH_CARD_TYPE_OSN)
+               SET_ETHTOOL_OPS(dev, &qeth_ethtool_ops);
        SET_MODULE_OWNER(dev);
        return 0;
 }
@@ -5273,6 +6401,7 @@ qeth_init_func_level(struct qeth_card *card)
                                        QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT;
        } else {
                if (card->info.type == QETH_CARD_TYPE_IQD)
+               /*FIXME:why do we have same values for  dis and ena for osae??? */
                        card->info.func_level =
                                QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT;
                else
@@ -5302,7 +6431,7 @@ retry:
                ccw_device_set_online(CARD_WDEV(card));
                ccw_device_set_online(CARD_DDEV(card));
        }
-       rc = qeth_qdio_clear_card(card,card->info.type==QETH_CARD_TYPE_OSAE);
+       rc = qeth_qdio_clear_card(card,card->info.type!=QETH_CARD_TYPE_IQD);
        if (rc == -ERESTARTSYS) {
                QETH_DBF_TEXT(setup, 2, "break1");
                return rc;
@@ -5345,22 +6474,29 @@ 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;
+               if (card->options.fake_ll &&
+                   (qeth_get_netdev_flags(card) & IFF_NOARP))
+                       card->dev->hard_header = qeth_fake_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_IQD);
+               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);
@@ -5408,6 +6544,8 @@ qeth_default_setadapterparms_cb(struct qeth_card *card,
        return 0;
 }
 
+
+
 static int
 qeth_query_setadapterparms_cb(struct qeth_card *card, struct qeth_reply *reply,
                              unsigned long data)
@@ -5448,8 +6586,13 @@ qeth_setadpparms_change_macaddr_cb(struct qeth_card *card,
        QETH_DBF_TEXT(trace,4,"chgmaccb");
 
        cmd = (struct qeth_ipa_cmd *) data;
-       memcpy(card->dev->dev_addr,
-              &cmd->data.setadapterparms.data.change_addr.addr,OSA_ADDR_LEN);
+       if (!card->options.layer2 || card->info.guestlan ||
+           !(card->info.mac_bits & QETH_LAYER2_MAC_READ)) {
+               memcpy(card->dev->dev_addr,
+                      &cmd->data.setadapterparms.data.change_addr.addr,
+                      OSA_ADDR_LEN);
+               card->info.mac_bits |= QETH_LAYER2_MAC_READ;
+       }
        qeth_default_setadapterparms_cb(card, reply, (unsigned long) cmd);
        return 0;
 }
@@ -5560,6 +6703,37 @@ qeth_setadapter_parms(struct qeth_card *card)
        return rc;
 }
 
+static int
+qeth_layer2_initialize(struct qeth_card *card)
+{
+        int rc = 0;
+
+
+        QETH_DBF_TEXT(setup, 2, "doL2init");
+        QETH_DBF_TEXT_(setup, 2, "doL2%s", CARD_BUS_ID(card));
+
+       rc = qeth_query_setadapterparms(card);
+       if (rc) {
+               PRINT_WARN("could not query adapter parameters on device %s: "
+                          "x%x\n", CARD_BUS_ID(card), rc);
+       }
+
+       rc = qeth_setadpparms_change_macaddr(card);
+       if (rc) {
+               PRINT_WARN("couldn't get MAC address on "
+                          "device %s: x%x\n",
+                          CARD_BUS_ID(card), rc);
+               QETH_DBF_TEXT_(setup, 2,"1err%d",rc);
+               return rc;
+        }
+       QETH_DBF_HEX(setup,2, card->dev->dev_addr, OSA_ADDR_LEN);
+
+       rc = qeth_layer2_send_setmac(card, &card->dev->dev_addr[0]);
+        if (rc)
+               QETH_DBF_TEXT_(setup, 2,"2err%d",rc);
+        return 0;
+}
+
 
 static int
 qeth_send_startstoplan(struct qeth_card *card, enum qeth_ipa_cmds ipacmd,
@@ -5613,12 +6787,18 @@ qeth_query_ipassists_cb(struct qeth_card *card, struct qeth_reply *reply,
        if (cmd->hdr.prot_version == QETH_PROT_IPV4) {
                card->options.ipa4.supported_funcs = cmd->hdr.ipa_supported;
                card->options.ipa4.enabled_funcs = cmd->hdr.ipa_enabled;
+               /* Disable IPV6 support hard coded for Hipersockets */
+               if(card->info.type == QETH_CARD_TYPE_IQD)
+                       card->options.ipa4.supported_funcs &= ~IPA_IPV6;
        } else {
 #ifdef CONFIG_QETH_IPV6
                card->options.ipa6.supported_funcs = cmd->hdr.ipa_supported;
                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;
 }
 
@@ -5629,6 +6809,10 @@ qeth_query_ipassists(struct qeth_card *card, enum qeth_prot_versions prot)
        struct qeth_cmd_buffer *iob;
 
        QETH_DBF_TEXT_(setup, 2, "qipassi%i", prot);
+       if (card->options.layer2) {
+               QETH_DBF_TEXT(setup, 2, "noprmly2");
+               return -EPERM;
+       }
 
        iob = qeth_get_ipacmd_buffer(card,IPA_CMD_QIPASSIST,prot);
        rc = qeth_send_ipa_cmd(card, iob, qeth_query_ipassists_cb, NULL);
@@ -5671,7 +6855,7 @@ qeth_send_setassparms(struct qeth_card *card, struct qeth_cmd_buffer *iob,
        cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
        if (len <= sizeof(__u32))
                cmd->data.setassparms.data.flags_32bit = (__u32) data;
-       else if (len > sizeof(__u32))
+       else   /* (len > sizeof(__u32)) */
                memcpy(&cmd->data.setassparms.data, (void *) data, len);
 
        rc = qeth_send_ipa_cmd(card, iob, reply_cb, reply_param);
@@ -5724,7 +6908,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,
@@ -5732,7 +6916,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;
 }
@@ -5746,7 +6930,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;
        }
 
@@ -5755,7 +6939,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;
@@ -5773,7 +6957,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;
        }
 
@@ -5782,7 +6966,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;
 }
 
@@ -5795,7 +6979,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;
        }
 
@@ -5804,10 +6988,13 @@ 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 |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+               card->dev->features |=
+                       NETIF_F_HW_VLAN_FILTER |
+                       NETIF_F_HW_VLAN_TX |
+                       NETIF_F_HW_VLAN_RX;
        }
 #endif /* QETH_VLAN */
        return rc;
@@ -5822,7 +7009,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;
        }
 
@@ -5831,7 +7018,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;
@@ -5847,18 +7034,16 @@ qeth_softsetup_ipv6(struct qeth_card *card)
 
        QETH_DBF_TEXT(trace,3,"softipv6");
 
-       netif_stop_queue(card->dev);
        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,
@@ -5866,7 +7051,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,
@@ -5874,7 +7059,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,
@@ -5882,7 +7067,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");
@@ -5900,7 +7085,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);
@@ -5917,7 +7102,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;
        }
@@ -5926,7 +7111,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;
        }
 
@@ -5934,7 +7119,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;
@@ -5943,7 +7128,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;
@@ -5965,7 +7150,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,
@@ -5974,7 +7159,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;
@@ -5989,19 +7174,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;
        }
@@ -6012,26 +7197,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;
 
-       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");
+       QETH_DBF_TEXT(trace,3,"sttso");
+
+       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)
@@ -6045,6 +7237,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;
 }
 
@@ -6123,7 +7316,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;
 }
@@ -6139,23 +7332,55 @@ qeth_setrouting_v6(struct qeth_card *card)
        qeth_correct_routing_type(card, &card->options.route6.type,
                                  QETH_PROT_IPV6);
 
-       if ((card->options.route6.type == NO_ROUTER) ||
-           ((card->info.type == QETH_CARD_TYPE_OSAE) &&
-            (card->options.route6.type == MULTICAST_ROUTER) &&
-            !qeth_is_supported6(card,IPA_OSA_MC_ROUTER)))
-               return 0;
        rc = qeth_send_setrouting(card, card->options.route6.type,
                                  QETH_PROT_IPV6);
        if (rc) {
                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, enum qeth_large_send_types type)
+{
+       int rc = 0;
+
+       if (card->dev == NULL) {
+               card->options.large_send = type;
+               return 0;
+       }
+       if (card->state == CARD_STATE_UP)
+               netif_tx_disable(card->dev);
+       card->options.large_send = type;
+       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;
+       }
+       if (card->state == CARD_STATE_UP)
+               netif_wake_queue(card->dev);
+       return rc;
+}
+
 /*
  * softsetup card: init IPA stuff
  */
@@ -6177,6 +7402,25 @@ qeth_softsetup_card(struct qeth_card *card)
                        return rc;
        } else
                card->lan_online = 1;
+       if (card->info.type==QETH_CARD_TYPE_OSN)
+               goto out;
+       qeth_set_large_send(card, card->options.large_send);
+       if (card->options.layer2) {
+               card->dev->features |=
+                       NETIF_F_HW_VLAN_FILTER |
+                       NETIF_F_HW_VLAN_TX |
+                       NETIF_F_HW_VLAN_RX;
+               card->dev->flags|=IFF_MULTICAST|IFF_BROADCAST;
+               card->info.broadcast_capable=1;
+               if ((rc = qeth_layer2_initialize(card))) {
+                       QETH_DBF_TEXT_(setup, 2, "L2err%d", rc);
+                       return rc;
+               }
+#ifdef CONFIG_QETH_VLAN
+               qeth_layer2_process_vlans(card, 0);
+#endif
+               goto out;
+       }
        if ((rc = qeth_setadapter_parms(card)))
                QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
        if ((rc = qeth_start_ipassists(card)))
@@ -6185,7 +7429,8 @@ qeth_softsetup_card(struct qeth_card *card)
                QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
        if ((rc = qeth_setrouting_v6(card)))
                QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
-       netif_stop_queue(card->dev);
+out:
+       netif_tx_disable(card->dev);
        return 0;
 }
 
@@ -6255,7 +7500,7 @@ qeth_clear_ip_list(struct qeth_card *card, int clean, int recover)
        QETH_DBF_TEXT(trace,4,"clearip");
        spin_lock_irqsave(&card->ip_lock, flags);
        /* clear todo list */
-       list_for_each_entry_safe(addr, tmp, &card->ip_tbd_list, entry){
+       list_for_each_entry_safe(addr, tmp, card->ip_tbd_list, entry){
                list_del(&addr->entry);
                kfree(addr);
        }
@@ -6273,7 +7518,7 @@ qeth_clear_ip_list(struct qeth_card *card, int clean, int recover)
                        kfree(addr);
                        continue;
                }
-               list_add_tail(&addr->entry, &card->ip_tbd_list);
+               list_add_tail(&addr->entry, card->ip_tbd_list);
        }
        spin_unlock_irqrestore(&card->ip_lock, flags);
 }
@@ -6312,9 +7557,8 @@ 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 recover_flag = 0;
        int rc = 0;
 
        QETH_DBF_TEXT(setup ,2,"stopcard");
@@ -6326,22 +7570,35 @@ 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)) {
-               recover_flag = 1;
-               rtnl_lock();
-               dev_close(card->dev);
-               rtnl_unlock();
-               if (!card->use_hard_stop)
+               if (recovery_mode &&
+                   card->info.type != QETH_CARD_TYPE_OSN) {
+                       qeth_stop(card->dev);
+               } else {
+                       rtnl_lock();
+                       dev_close(card->dev);
+                       rtnl_unlock();
+               }
+               if (!card->use_hard_stop) {
+                       __u8 *mac = &card->dev->dev_addr[0];
+                       rc = qeth_layer2_send_delmac(card, mac);
+                       QETH_DBF_TEXT_(setup, 2, "Lerr%d", rc);
                        if ((rc = qeth_send_stoplan(card)))
                                QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+               }
                card->state = CARD_STATE_SOFTSETUP;
        }
        if (card->state == CARD_STATE_SOFTSETUP) {
-               qeth_clear_ip_list(card, !card->use_hard_stop, recover_flag);
+#ifdef CONFIG_QETH_VLAN
+               if (card->options.layer2)
+                       qeth_layer2_process_vlans(card, 1);
+#endif
+               qeth_clear_ip_list(card, !card->use_hard_stop, 1);
                qeth_clear_ipacmd_list(card);
                card->state = CARD_STATE_HARDSETUP;
        }
        if (card->state == CARD_STATE_HARDSETUP) {
-               if (!card->use_hard_stop)
+               if ((!card->use_hard_stop) &&
+                   (!card->options.layer2))
                        if ((rc = qeth_put_unique_id(card)))
                                QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
                qeth_qdio_clear_card(card, 0);
@@ -6481,34 +7738,87 @@ 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)
                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 (qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD) == 0)
-               schedule_work(&card->kernel_thread_starter);
+       if (recovery_mode &&
+           card->info.type != QETH_CARD_TYPE_OSN) {
+               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);
+}
+
+
+/* Layer 2 specific stuff */
+#define IGNORE_PARAM_EQ(option,value,reset_value,msg) \
+        if (card->options.option == value) { \
+                PRINT_ERR("%s not supported with layer 2 " \
+                          "functionality, ignoring option on read" \
+                         "channel device %s .\n",msg,CARD_RDEV_ID(card)); \
+                card->options.option = reset_value; \
+        }
+#define IGNORE_PARAM_NEQ(option,value,reset_value,msg) \
+        if (card->options.option != value) { \
+                PRINT_ERR("%s not supported with layer 2 " \
+                          "functionality, ignoring option on read" \
+                         "channel device %s .\n",msg,CARD_RDEV_ID(card)); \
+                card->options.option = reset_value; \
+        }
+
+
+static void qeth_make_parameters_consistent(struct qeth_card *card)
+{
+
+       if (card->options.layer2 == 0)
+               return;
+       if (card->info.type == QETH_CARD_TYPE_OSN)
+               return;
+       if (card->info.type == QETH_CARD_TYPE_IQD) {
+                       PRINT_ERR("Device %s does not support layer 2 functionality." \
+                         " Ignoring layer2 option.\n",CARD_BUS_ID(card));
+                       card->options.layer2 = 0;
+               return;
+       }
+               IGNORE_PARAM_NEQ(route4.type, NO_ROUTER, NO_ROUTER,
+                                "Routing options are");
+#ifdef CONFIG_QETH_IPV6
+               IGNORE_PARAM_NEQ(route6.type, NO_ROUTER, NO_ROUTER,
+                                "Routing options are");
+#endif
+               IGNORE_PARAM_EQ(checksum_type, HW_CHECKSUMMING,
+                               QETH_CHECKSUM_DEFAULT,
+                               "Checksumming options are");
+               IGNORE_PARAM_NEQ(broadcast_mode, QETH_TR_BROADCAST_ALLRINGS,
+                                QETH_TR_BROADCAST_ALLRINGS,
+                                "Broadcast mode options are");
+               IGNORE_PARAM_NEQ(macaddr_mode, QETH_TR_MACADDR_NONCANONICAL,
+                                QETH_TR_MACADDR_NONCANONICAL,
+                                "Canonical MAC addr options are");
+               IGNORE_PARAM_NEQ(fake_broadcast, 0, 0,
+                        "Broadcast faking options are");
+               IGNORE_PARAM_NEQ(add_hhlen, DEFAULT_ADD_HHLEN,
+                                DEFAULT_ADD_HHLEN,"Option add_hhlen is");
+        IGNORE_PARAM_NEQ(fake_ll, 0, 0,"Option fake_ll is");
 }
 
+
 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;
@@ -6526,54 +7836,53 @@ 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;
        }
 
+       qeth_make_parameters_consistent(card);
+
        if ((rc = qeth_hardsetup_card(card))){
                QETH_DBF_TEXT_(setup, 2, "2err%d", rc);
                goto out_remove;
        }
        card->state = CARD_STATE_HARDSETUP;
 
-       if ((rc = qeth_query_ipassists(card,QETH_PROT_IPV4))){
-               QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
-               /*TODO: rc !=0*/
-       } else
+       if (!(rc = qeth_query_ipassists(card,QETH_PROT_IPV4)))
                rc = qeth_get_unique_id(card);
 
-       if (rc) {
-               QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
+       if (rc && card->options.layer2 == 0) {
+               QETH_DBF_TEXT_(setup, 2, "3err%d", rc);
                goto out_remove;
        }
        qeth_print_status_message(card);
        if ((rc = qeth_register_netdev(card))){
-               QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
+               QETH_DBF_TEXT_(setup, 2, "4err%d", rc);
                goto out_remove;
        }
        if ((rc = qeth_softsetup_card(card))){
-               QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
+               QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
                goto out_remove;
        }
        card->state = CARD_STATE_SOFTSETUP;
 
        if ((rc = qeth_init_qdio_queues(card))){
-               QETH_DBF_TEXT_(setup, 2, "7err%d", rc);
+               QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
                goto out_remove;
        }
-/*maybe it was set offline without ifconfig down
- * we can also use this state for recovery purposes*/
+       netif_carrier_on(card->dev);
+
        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));
@@ -6584,9 +7893,16 @@ 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},
+       {CCW_DEVICE(0x1731, 0x06), driver_info:QETH_CARD_TYPE_OSN},
        {},
 };
 MODULE_DEVICE_TABLE(ccw, qeth_ids);
@@ -6633,31 +7949,31 @@ static int
 qeth_register_dbf_views(void)
 {
        qeth_dbf_setup = debug_register(QETH_DBF_SETUP_NAME,
-                                       QETH_DBF_SETUP_INDEX,
+                                       QETH_DBF_SETUP_PAGES,
                                        QETH_DBF_SETUP_NR_AREAS,
                                        QETH_DBF_SETUP_LEN);
        qeth_dbf_misc = debug_register(QETH_DBF_MISC_NAME,
-                                      QETH_DBF_MISC_INDEX,
+                                      QETH_DBF_MISC_PAGES,
                                       QETH_DBF_MISC_NR_AREAS,
                                       QETH_DBF_MISC_LEN);
        qeth_dbf_data = debug_register(QETH_DBF_DATA_NAME,
-                                      QETH_DBF_DATA_INDEX,
+                                      QETH_DBF_DATA_PAGES,
                                       QETH_DBF_DATA_NR_AREAS,
                                       QETH_DBF_DATA_LEN);
        qeth_dbf_control = debug_register(QETH_DBF_CONTROL_NAME,
-                                         QETH_DBF_CONTROL_INDEX,
+                                         QETH_DBF_CONTROL_PAGES,
                                          QETH_DBF_CONTROL_NR_AREAS,
                                          QETH_DBF_CONTROL_LEN);
        qeth_dbf_sense = debug_register(QETH_DBF_SENSE_NAME,
-                                       QETH_DBF_SENSE_INDEX,
+                                       QETH_DBF_SENSE_PAGES,
                                        QETH_DBF_SENSE_NR_AREAS,
                                        QETH_DBF_SENSE_LEN);
        qeth_dbf_qerr = debug_register(QETH_DBF_QERR_NAME,
-                                      QETH_DBF_QERR_INDEX,
+                                      QETH_DBF_QERR_PAGES,
                                       QETH_DBF_QERR_NR_AREAS,
                                       QETH_DBF_QERR_LEN);
        qeth_dbf_trace = debug_register(QETH_DBF_TRACE_NAME,
-                                       QETH_DBF_TRACE_INDEX,
+                                       QETH_DBF_TRACE_PAGES,
                                        QETH_DBF_TRACE_NR_AREAS,
                                        QETH_DBF_TRACE_LEN);
 
@@ -6699,7 +8015,6 @@ static int (*qeth_old_arp_constructor) (struct neighbour *);
 
 static struct neigh_ops arp_direct_ops_template = {
        .family = AF_INET,
-       .destructor = NULL,
        .solicit = NULL,
        .error_report = NULL,
        .output = dev_queue_xmit,
@@ -6714,13 +8029,17 @@ qeth_arp_constructor(struct neighbour *neigh)
        struct net_device *dev = neigh->dev;
        struct in_device *in_dev;
        struct neigh_parms *parms;
+       struct qeth_card *card;
 
-       if (!qeth_verify_dev(dev)) {
-               return qeth_old_arp_constructor(neigh);
-       }
+       card = qeth_get_card_from_dev(dev);
+       if (card == NULL)
+               goto out;
+       if((card->options.layer2) ||
+          (card->dev->hard_header == qeth_fake_header))
+               goto out;
 
        rcu_read_lock();
-       in_dev = rcu_dereference(__in_dev_get(dev));
+       in_dev = __in_dev_get_rcu(dev);
        if (in_dev == NULL) {
                rcu_read_unlock();
                return -EINVAL;
@@ -6736,6 +8055,8 @@ qeth_arp_constructor(struct neighbour *neigh)
        neigh->ops = arp_direct_ops;
        neigh->output = neigh->ops->queue_xmit;
        return 0;
+out:
+       return qeth_old_arp_constructor(neigh);
 }
 #endif  /*CONFIG_QETH_IPV6*/
 
@@ -6888,7 +8209,7 @@ qeth_add_vipa(struct qeth_card *card, enum qeth_prot_versions proto,
                return -ENOMEM;
        spin_lock_irqsave(&card->ip_lock, flags);
        if (__qeth_address_exists_in_list(&card->ip_list, ipaddr, 0) ||
-           __qeth_address_exists_in_list(&card->ip_tbd_list, ipaddr, 0))
+           __qeth_address_exists_in_list(card->ip_tbd_list, ipaddr, 0))
                rc = -EEXIST;
        spin_unlock_irqrestore(&card->ip_lock, flags);
        if (rc){
@@ -6961,7 +8282,7 @@ qeth_add_rxip(struct qeth_card *card, enum qeth_prot_versions proto,
                return -ENOMEM;
        spin_lock_irqsave(&card->ip_lock, flags);
        if (__qeth_address_exists_in_list(&card->ip_list, ipaddr, 0) ||
-           __qeth_address_exists_in_list(&card->ip_tbd_list, ipaddr, 0))
+           __qeth_address_exists_in_list(card->ip_tbd_list, ipaddr, 0))
                rc = -EEXIST;
        spin_unlock_irqrestore(&card->ip_lock, flags);
        if (rc){
@@ -7019,6 +8340,8 @@ qeth_ip_event(struct notifier_block *this,
        card = qeth_get_card_from_dev(dev);
        if (!card)
                return NOTIFY_DONE;
+       if (card->options.layer2)
+               return NOTIFY_DONE;
 
        addr = qeth_get_addr_buffer(QETH_PROT_IPV4);
        if (addr != NULL) {
@@ -7106,20 +8429,22 @@ static struct notifier_block qeth_ip6_notifier = {
 #endif
 
 static int
-qeth_reboot_event(struct notifier_block *this, unsigned long event, void *ptr)
+__qeth_reboot_event_card(struct device *dev, void *data)
 {
-
-       struct device *entry;
        struct qeth_card *card;
 
-       down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
-              list_for_each_entry(entry, &qeth_ccwgroup_driver.driver.devices,
-                                  driver_list) {
-                      card = (struct qeth_card *) entry->driver_data;
-                      qeth_clear_ip_list(card, 0, 0);
-                      qeth_qdio_clear_card(card, 0);
-              }
-       up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem);
+       card = (struct qeth_card *) dev->driver_data;
+       qeth_clear_ip_list(card, 0, 0);
+       qeth_qdio_clear_card(card, 0);
+       return 0;
+}
+
+static int
+qeth_reboot_event(struct notifier_block *this, unsigned long event, void *ptr)
+{
+
+       driver_for_each_device(&qeth_ccwgroup_driver.driver, NULL, NULL,
+                              __qeth_reboot_event_card);
        return NOTIFY_DONE;
 }
 
@@ -7241,13 +8566,7 @@ qeth_init(void)
 {
        int rc=0;
 
-       qeth_eyecatcher();
-       PRINT_INFO("loading %s (%s/%s/%s/%s/%s/%s/%s %s %s)\n",
-                  version, VERSION_QETH_C, VERSION_QETH_H,
-                  VERSION_QETH_MPC_H, VERSION_QETH_MPC_C,
-                  VERSION_QETH_FS_H, VERSION_QETH_PROC_C,
-                  VERSION_QETH_SYS_C, QETH_VERSION_IPV6,
-                  QETH_VERSION_VLAN);
+       PRINT_INFO("loading %s\n", version);
 
        INIT_LIST_HEAD(&qeth_card_list.list);
        INIT_LIST_HEAD(&qeth_notify_list);
@@ -7322,10 +8641,12 @@ again:
        printk("qeth: removed\n");
 }
 
-EXPORT_SYMBOL(qeth_eyecatcher);
+EXPORT_SYMBOL(qeth_osn_register);
+EXPORT_SYMBOL(qeth_osn_deregister);
+EXPORT_SYMBOL(qeth_osn_assist);
 module_init(qeth_init);
 module_exit(qeth_exit);
-MODULE_AUTHOR("Frank Pavlic <pavlic@de.ibm.com>");
+MODULE_AUTHOR("Frank Pavlic <fpavlic@de.ibm.com>");
 MODULE_DESCRIPTION("Linux on zSeries OSA Express and HiperSockets support\n" \
                                      "Copyright 2000,2003 IBM Corporation\n");