vserver 1.9.5.x5
[linux-2.6.git] / drivers / s390 / net / qeth_main.c
index 3e374dd..535f46b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.145 $)
+ * linux/drivers/s390/net/qeth_main.c ($Revision: 1.191 $)
  *
  * Linux on zSeries OSA Express and HiperSockets support
  *
@@ -12,7 +12,7 @@
  *                       Frank Pavlic (pavlic@de.ibm.com) and
  *                       Thomas Spatzier <tspat@de.ibm.com>
  *
- *    $Revision: 1.145 $        $Date: 2004/10/08 15:08:40 $
+ *    $Revision: 1.191 $        $Date: 2005/01/31 13:13:57 $
  *
  * 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
@@ -29,7 +29,6 @@
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-
 /***
  * eye catcher; just for debugging purposes
  */
@@ -42,16 +41,9 @@ qeth_eyecatcher(void)
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/mm.h>
-
-#include <asm/io.h>
-#include <asm/ebcdic.h>
-#include <linux/ctype.h>
-#include <asm/semaphore.h>
-#include <asm/timex.h>
 #include <linux/ip.h>
 #include <linux/inetdevice.h>
 #include <linux/netdevice.h>
@@ -63,23 +55,29 @@ 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 <net/arp.h>
+#include <net/ip.h>
+#include <net/route.h>
+
+#include <asm/ebcdic.h>
+#include <asm/io.h>
+#include <asm/qeth.h>
+#include <asm/timex.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
 #include "qeth.h"
 #include "qeth_mpc.h"
 #include "qeth_fs.h"
 
-#define VERSION_QETH_C "$Revision: 1.145 $"
+#define VERSION_QETH_C "$Revision: 1.191 $"
 static const char *version = "qeth S/390 OSA-Express driver";
 
 /**
@@ -160,6 +158,9 @@ qeth_set_online(struct ccwgroup_device *);
 static struct qeth_ipaddr *
 qeth_get_addr_buffer(enum qeth_prot_versions);
 
+static void
+qeth_set_multicast_list(struct net_device *);
+
 static void
 qeth_notify_processes(void)
 {
@@ -197,7 +198,7 @@ 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 +251,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);
 }
@@ -511,6 +513,7 @@ static int
 qeth_set_offline(struct ccwgroup_device *cgdev)
 {
        struct qeth_card *card = (struct qeth_card *) cgdev->dev.driver_data;
+       int rc = 0;
        enum qeth_card_states recover_flag;
 
        QETH_DBF_TEXT(setup, 3, "setoffl");
@@ -522,15 +525,21 @@ qeth_set_offline(struct ccwgroup_device *cgdev)
                           CARD_BUS_ID(card));
                return -ERESTARTSYS;
        }
-       ccw_device_set_offline(CARD_DDEV(card));
-       ccw_device_set_offline(CARD_WDEV(card));
-       ccw_device_set_offline(CARD_RDEV(card));
+       if ((rc = ccw_device_set_offline(CARD_DDEV(card))) ||
+           (rc = ccw_device_set_offline(CARD_WDEV(card))) ||
+           (rc = ccw_device_set_offline(CARD_RDEV(card)))) {
+               QETH_DBF_TEXT_(setup, 2, "1err%d", rc);
+       }
        if (recover_flag == CARD_STATE_UP)
                card->state = CARD_STATE_RECOVER;
        qeth_notify_processes();
        return 0;
 }
 
+static int
+qeth_wait_for_threads(struct qeth_card *card, unsigned long threads);
+
+
 static void
 qeth_remove_device(struct ccwgroup_device *cgdev)
 {
@@ -543,6 +552,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);
@@ -618,7 +630,7 @@ __qeth_ref_ip_on_card(struct qeth_card *card, struct qeth_ipaddr *todo,
        if (todo->users > 0){
                /* for VIPA and RXIP limit refcount to 1 */
                if (todo->type != QETH_IP_TYPE_NORMAL)
-                       addr->users = 1;
+                       todo->users = 1;
                return 1;
        } else
                return 0;
@@ -661,7 +673,10 @@ __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 ((tmp->proto        == QETH_PROT_IPV4)     &&
                    (addr->proto       == QETH_PROT_IPV4)     &&
                    (tmp->type         == addr->type)         &&
@@ -693,14 +708,18 @@ __qeth_insert_ip_todo(struct qeth_card *card, struct qeth_ipaddr *addr, int add)
                }
                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;
        }
 }
@@ -718,8 +737,8 @@ qeth_delete_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
        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);
@@ -737,8 +756,8 @@ qeth_add_ip(struct qeth_card *card, struct qeth_ipaddr *addr)
        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 +765,21 @@ 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;
+       struct qeth_ipaddr *addr, *tmp;
+       int rc;
 
-       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);
+       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);
+                       }
                }
        }
 }
@@ -766,7 +787,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 +795,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,24 +826,22 @@ 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 *);
@@ -888,28 +919,7 @@ qeth_do_run_thread(struct qeth_card *card, unsigned long thread)
 }
 
 static int
-qeth_register_mc_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))
-               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_set_ip_addr_list(card);
-       qeth_clear_thread_running_bit(card, QETH_SET_MC_THREAD);
-       return 0;
-}
-
-static int
-qeth_register_ip_address(void *ptr)
+qeth_register_ip_addresses(void *ptr)
 {
        struct qeth_card *card;
 
@@ -989,9 +999,7 @@ 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_RECOVER_THREAD))
                kernel_thread(qeth_recover, (void *) card, SIGCHLD);
 }
@@ -1010,6 +1018,7 @@ 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;
+       card->options.layer2 = 0;
 }
 
 /**
@@ -1041,7 +1050,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 */
@@ -1306,6 +1320,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;
@@ -1372,6 +1387,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);
@@ -1575,9 +1591,8 @@ 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);
+       /* this function will also schedule the SET_IP_THREAD */
+       qeth_set_multicast_list(card->dev);
 }
 
 static struct qeth_ipa_cmd *
@@ -1597,25 +1612,19 @@ 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)) {
-                                       netif_carrier_off(card->dev);
-                                       netif_stop_queue(card->dev);
-                               }
+                               netif_carrier_off(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);
-                               }
+                               netif_carrier_on(card->dev);
                                qeth_reset_ip_addresses(card);
                                return NULL;
                        case IPA_CMD_REGISTER_LOCAL_ADDR:
@@ -1624,7 +1633,7 @@ qeth_check_ipa_data(struct qeth_card *card, struct qeth_cmd_buffer *iob)
                        case IPA_CMD_UNREGISTER_LOCAL_ADDR:
                                PRINT_WARN("probably problem on %s: "
                                           "received IPA command 0x%X\n",
-                                          card->info.if_name,
+                                          QETH_CARD_IFNAME(card),
                                           cmd->hdr.command);
                                break;
                        default:
@@ -1815,10 +1824,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);
+
+       if (card->options.layer2)
+               prot_type = QETH_PROT_LAYER2;
+       else
+               prot_type = QETH_PROT_TCPIP;
+
+       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);
 
@@ -1951,6 +1968,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 +1979,12 @@ qeth_ulp_enable(struct qeth_card *card)
 
        *(QETH_ULP_ENABLE_LINKNUM(iob->data)) =
                (__u8) card->info.portno;
+       if (card->options.layer2)
+               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),
@@ -2084,7 +2107,11 @@ 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)
+               skb_len = (*hdr)->hdr.l2.pkt_length;
+       else
+               skb_len = (*hdr)->hdr.l3.length;
+
        if (!skb_len)
                return NULL;
        if (card->options.fake_ll){
@@ -2123,7 +2150,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));
        }
@@ -2134,10 +2161,10 @@ no_mem:
 static inline unsigned short
 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 +2172,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 +2181,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)
@@ -2206,8 +2232,8 @@ 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 */
@@ -2221,28 +2247,52 @@ 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;
+
+       skb->pkt_type = PACKET_HOST;
+       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)) {
+               vlan_id = hdr->hdr.l2.vlan_id;
+               skb_pull(skb, VLAN_HLEN);
+       }
+#endif
+       skb->protocol = qeth_type_trans(skb, skb->dev);
+       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 +2309,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 +2325,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 +2332,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,14 +2345,22 @@ 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
+                       qeth_rebuild_skb(card, skb, hdr);
                /* is device UP ? */
                if (!(card->dev->flags & IFF_UP)){
                        dev_kfree_skb_any(skb);
                        continue;
                }
-               skb->dev = card->dev;
+#ifdef CONFIG_QETH_VLAN
+               if (vlan_tag)
+                       vlan_hwaccel_rx(skb, card->vlangrp, vlan_tag);
+               else
+#endif
                rxrc = netif_rx(skb);
                card->dev->last_rx = jiffies;
                card->stats.rx_packets++;
@@ -2759,7 +2818,8 @@ 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);
 #ifdef CONFIG_QETH_PERF_STATS
@@ -3211,13 +3271,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;
 }
@@ -3319,6 +3381,26 @@ qeth_get_netdevice(enum qeth_card_types type, enum qeth_link_types linktype)
        return dev;
 }
 
+/*hard_header fake function; used in case fake_ll is set */
+static int
+qeth_fake_header(struct sk_buff *skb, struct net_device *dev,
+                    unsigned short type, void *daddr, void *saddr,
+                    unsigned len)
+{
+       struct ethhdr *hdr;
+       struct qeth_card *card;
+
+       card = (struct qeth_card *)dev->priv;
+        hdr = (struct ethhdr *)skb_push(skb, QETH_FAKE_LL_LEN);
+       memcpy(hdr->h_source, card->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;
+}
+
 static inline int
 qeth_send_packet(struct qeth_card *, struct sk_buff *);
 
@@ -3333,25 +3415,46 @@ 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
+       if (dev->hard_header == qeth_fake_header) {
+               if ((skb = qeth_pskb_unshare(skb, GFP_ATOMIC)) == NULL) {
+                        card->stats.tx_dropped++;
+                        dev_kfree_skb_irq(skb);
+                        return 0;
+                }
+                skb_pull(skb, QETH_FAKE_LL_LEN);
+       }
        /*
         * We only call netif_stop_queue in case of errors. Since we've
         * got our own synchronization on queues we can keep the stack's
         * queue running.
         */
-       if ((rc = qeth_send_packet(card, skb)))
-               netif_stop_queue(dev);
+       if ((rc = qeth_send_packet(card, skb))){
+               if (rc == -EBUSY) {
+                       netif_stop_queue(dev);
+                       rc = NETDEV_TX_BUSY;
+               } 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;
+               }
+       }
 
 #ifdef CONFIG_QETH_PERF_STATS
        card->perf_stats.outbound_time += qeth_get_micros() -
@@ -3442,6 +3545,11 @@ qeth_open(struct net_device *dev)
        if (card->state != CARD_STATE_SOFTSETUP)
                return -ENODEV;
 
+       if ( (card->options.layer2) &&
+            (!card->info.layer2_mac_registered)) {
+               QETH_DBF_TEXT(trace,4,"nomacadr");
+               return -EPERM;
+       }
        card->dev->flags |= IFF_UP;
        netif_start_queue(dev);
        card->data.state = CH_STATE_UP;
@@ -3450,7 +3558,6 @@ qeth_open(struct net_device *dev)
        if (!card->lan_online){
                if (netif_carrier_ok(dev))
                        netif_carrier_off(dev);
-               netif_stop_queue(dev);
        }
        return 0;
 }
@@ -3491,12 +3598,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:
@@ -3575,13 +3682,14 @@ qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
                if (!new_skb) {
                        PRINT_ERR("qeth_prepare_skb: could "
                                  "not realloc headroom for qeth_hdr "
-                                 "on interface %s", card->info.if_name);
+                                 "on interface %s", QETH_CARD_IFNAME(card));
                        return -ENOMEM;
                }
                *skb = new_skb;
        }
 #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,14 +3699,13 @@ 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));
@@ -3612,7 +3719,7 @@ qeth_prepare_skb(struct qeth_card *card, struct sk_buff **skb,
              QETH_IP_HEADER_SIZE) & (~(PAGE_SIZE - 1)))) {
                PRINT_ERR("qeth_prepare_skb: misaligned "
                          "packet on interface %s. Discarded.",
-                         card->info.if_name);
+                         QETH_CARD_IFNAME(card));
                return -EINVAL;
        }
        return 0;
@@ -3641,51 +3748,121 @@ qeth_get_qeth_hdr_flags6(int cast_type)
        return ct | QETH_CAST_UNICAST;
 }
 
+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
+}
+
 static inline 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");
+
+       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),
                            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;
                }
@@ -3874,7 +4051,7 @@ out:
 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;
@@ -3883,7 +4060,8 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
 
        QETH_DBF_TEXT(trace, 6, "sendpkt");
 
-       ipv = qeth_get_ip_version(skb);
+       if (!card->options.layer2)
+               ipv = qeth_get_ip_version(skb);
        cast_type = qeth_get_cast_type(card, skb);
        queue = card->qdio.out_qs
                [qeth_get_priority_queue(card, skb, ipv, cast_type)];
@@ -4048,7 +4226,7 @@ qeth_arp_set_no_entries(struct qeth_card *card, int no_entries)
                return -EOPNOTSUPP;
        if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
                PRINT_WARN("ARP processing not supported "
-                          "on %s!\n", card->info.if_name);
+                          "on %s!\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
        rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
@@ -4058,7 +4236,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;
@@ -4226,12 +4404,12 @@ qeth_arp_query(struct qeth_card *card, char *udata)
         * funcs flags); since all zeros is no valueable information,
         * we say EOPNOTSUPP for all ARP functions
         */
-       if (card->info.guestlan)
-               return -EOPNOTSUPP;
+       /*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 */
@@ -4252,7 +4430,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 +4544,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 */
@@ -4400,7 +4580,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);
 
@@ -4440,7 +4620,7 @@ qeth_arp_add_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry)
                return -EOPNOTSUPP;
        if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
                PRINT_WARN("ARP processing not supported "
-                          "on %s!\n", card->info.if_name);
+                          "on %s!\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
 
@@ -4457,7 +4637,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;
@@ -4483,7 +4663,7 @@ qeth_arp_remove_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry
                return -EOPNOTSUPP;
        if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
                PRINT_WARN("ARP processing not supported "
-                          "on %s!\n", card->info.if_name);
+                          "on %s!\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
        memcpy(buf, entry, 12);
@@ -4500,7 +4680,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;
@@ -4524,7 +4704,7 @@ qeth_arp_flush_cache(struct qeth_card *card)
                return -EOPNOTSUPP;
        if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
                PRINT_WARN("ARP processing not supported "
-                          "on %s!\n", card->info.if_name);
+                          "on %s!\n", QETH_CARD_IFNAME(card));
                return -EOPNOTSUPP;
        }
        rc = qeth_send_simple_setassparms(card, IPA_ARP_PROCESSING,
@@ -4532,7 +4712,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;
@@ -4555,21 +4735,24 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
 
        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 +4763,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 +4775,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;
                }
@@ -4741,7 +4926,7 @@ qeth_free_vlan_addresses4(struct qeth_card *card, unsigned short vid)
        in_dev = __in_dev_get(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;
@@ -4782,13 +4967,70 @@ qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid)
        in6_dev_put(in6_dev);
 }
 
+static void
+qeth_layer2_send_setdelvlan(struct qeth_card *card, __u16 i,
+                           enum qeth_ipa_cmds ipacmd)
+{
+       int rc;
+       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;
+
+       rc = qeth_send_ipa_cmd(card, iob, NULL, NULL);
+        if (rc) {
+                PRINT_ERR("Error in processing VLAN %i on %s: 0x%x. "
+                         "Continuing\n",i, QETH_CARD_IFNAME(card), rc);
+               QETH_DBF_TEXT_(trace, 2, "L2VL%4x", ipacmd);
+               QETH_DBF_TEXT_(trace, 2, "L2%s", CARD_BUS_ID(card));
+               QETH_DBF_TEXT_(trace, 2, "err%d", rc);
+        }
+}
+
+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 */
@@ -4800,38 +5042,35 @@ qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short 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
 
-static int
-qeth_neigh_setup(struct net_device *dev, struct neigh_parms *np)
+/**
+ * set multicast address on card
+ */
+static void
+qeth_set_multicast_list(struct net_device *dev)
 {
-       return 0;
-}
+       struct qeth_card *card = (struct qeth_card *) dev->priv;
 
+       QETH_DBF_TEXT(trace,3,"setmulti");
+       qeth_delete_mc_addresses(card);
+       qeth_add_multicast_ipv4(card);
 #ifdef CONFIG_QETH_IPV6
-int
-qeth_ipv6_generate_eui64(u8 * eui, struct net_device *dev)
-{
-       switch (dev->type) {
-       case ARPHRD_ETHER:
-       case ARPHRD_FDDI:
-       case ARPHRD_IEEE802_TR:
-               if (dev->addr_len != ETH_ALEN)
-                       return -1;
-               memcpy(eui, dev->dev_addr, 3);
-               memcpy(eui + 5, dev->dev_addr + 3, 3);
-               eui[3] = (dev->dev_id >> 8) & 0xff;
-               eui[4] = dev->dev_id & 0xff;
-               return 0;
-       }
-       return -1;
+       qeth_add_multicast_ipv6(card);
+#endif
+       if (qeth_set_thread_start_bit(card, QETH_SET_IP_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)
@@ -4861,24 +5100,19 @@ qeth_get_addr_buffer(enum qeth_prot_versions prot)
 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 +5146,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;
@@ -4980,8 +5215,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;
@@ -5020,19 +5256,182 @@ qeth_add_multicast_ipv6(struct qeth_card *card)
 }
 #endif /* CONFIG_QETH_IPV6 */
 
-/**
- * set multicast address on card
- */
-static void
-qeth_set_multicast_list(struct net_device *dev)
+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.layer2_mac_registered = 0;
+               cmd->hdr.return_code = -EIO;
+       } else {
+               card->info.layer2_mac_registered = 1;
+               memcpy(card->dev->dev_addr,cmd->data.setdelmac.mac,
+                      OSA_ADDR_LEN);
+               PRINT_INFO("MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x "
+                          "successfully registered on device %s\n",
+                          card->dev->dev_addr[0], card->dev->dev_addr[1],
+                          card->dev->dev_addr[2], card->dev->dev_addr[3],
+                          card->dev->dev_addr[4], card->dev->dev_addr[5],
+                          card->dev->name);
+       }
+       return 0;
+}
+
+static int
+qeth_layer2_send_setmac(struct qeth_card *card, __u8 *mac)
+{
+       QETH_DBF_TEXT(trace, 2, "L2Setmac");
+       return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_SETVMAC,
+                                         qeth_layer2_send_setmac_cb);
+}
+
+static int
+qeth_layer2_send_delmac_cb(struct qeth_card *card,
+                          struct qeth_reply *reply,
+                          unsigned long data)
+{
+       struct qeth_ipa_cmd *cmd;
+
+       QETH_DBF_TEXT(trace, 2, "L2Dmaccb");
+       cmd = (struct qeth_ipa_cmd *) data;
+       if (cmd->hdr.return_code) {
+               PRINT_WARN("Error in deregistering MAC address on " \
+                          "device %s: x%x\n", CARD_BUS_ID(card),
+                          cmd->hdr.return_code);
+               QETH_DBF_TEXT_(trace, 2, "err%d", cmd->hdr.return_code);
+               cmd->hdr.return_code = -EIO;
+               return 0;
+       }
+       card->info.layer2_mac_registered = 0;
+
+       return 0;
+}
+static int
+qeth_layer2_send_delmac(struct qeth_card *card, __u8 *mac)
+{
+       QETH_DBF_TEXT(trace, 2, "L2Delmac");
+       if (!card->info.layer2_mac_registered)
+               return 0;
+       return qeth_layer2_send_setdelmac(card, mac, IPA_CMD_DELVMAC,
+                                         qeth_layer2_send_delmac_cb);
+}
+
+static int
+qeth_layer2_set_mac_address(struct net_device *dev, void *p)
+{
+       struct sockaddr *addr = p;
        struct qeth_card *card;
+       int rc = 0;
 
-       QETH_DBF_TEXT(trace,3,"setmulti");
+       QETH_DBF_TEXT(trace, 3, "setmac");
+
+       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;
+       }
+       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 +5444,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,7 +5538,30 @@ 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)
+{
+       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;
@@ -5175,7 +5600,8 @@ qeth_register_addr_entry(struct qeth_card *card, struct qeth_ipaddr *addr)
 }
 
 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 +5634,24 @@ 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 int
 qeth_netdev_init(struct net_device *dev)
 {
@@ -5230,30 +5674,30 @@ 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) {
+       dev->hard_header = card->orig_hard_header;
+       if (qeth_get_netdev_flags(card) & IFF_NOARP) {
                dev->rebuild_header = NULL;
                dev->hard_header = NULL;
+               if (card->options.fake_ll)
+                       dev->hard_header = qeth_fake_header;
                dev->header_cache_update = NULL;
                dev->hard_header_cache = NULL;
        }
 #ifdef CONFIG_QETH_IPV6
        /*IPv6 address autoconfiguration stuff*/
-       card->dev->dev_id = card->info.unique_id & 0xffff;
        if (!(card->info.unique_id & UNIQUE_ID_NOT_BY_CARD))
-               card->dev->generate_eui64 = qeth_ipv6_generate_eui64;
-
-
+               card->dev->dev_id = card->info.unique_id & 0xffff;
 #endif
        dev->hard_header_parse = NULL;
-       dev->set_mac_address = 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;
 
@@ -5345,22 +5789,26 @@ retry:
                QETH_DBF_TEXT_(setup, 2, "5err%d", rc);
                goto out;
        }
+       /*network device will be recovered*/
+       if (card->dev) {
+               card->dev->hard_header = card->orig_hard_header;
+               return 0;
+       }
        /* at first set_online allocate netdev */
+       card->dev = qeth_get_netdevice(card->info.type,
+                                      card->info.link_type);
        if (!card->dev){
-               card->dev = qeth_get_netdevice(card->info.type,
-                                              card->info.link_type);
-               if (!card->dev){
-                       qeth_qdio_clear_card(card, card->info.type ==
-                                            QETH_CARD_TYPE_OSAE);
-                       rc = -ENODEV;
-                       QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
-                       goto out;
-               }
-               card->dev->priv = card;
-               card->dev->type = qeth_get_arphdr_type(card->info.type,
-                                                      card->info.link_type);
-               card->dev->init = qeth_netdev_init;
+               qeth_qdio_clear_card(card, card->info.type ==
+                                    QETH_CARD_TYPE_OSAE);
+               rc = -ENODEV;
+               QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
+               goto out;
        }
+       card->dev->priv = card;
+       card->orig_hard_header = card->dev->hard_header;
+       card->dev->type = qeth_get_arphdr_type(card->info.type,
+                                              card->info.link_type);
+       card->dev->init = qeth_netdev_init;
        return 0;
 out:
        PRINT_ERR("Initialization in hardsetup failed! rc=%d\n", rc);
@@ -5560,6 +6008,31 @@ 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_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,
@@ -5629,6 +6102,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);
@@ -5724,7 +6201,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 +6209,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 +6223,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 +6232,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 +6250,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 +6259,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 +6272,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 +6281,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 +6302,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 +6311,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;
@@ -5851,14 +6331,14 @@ qeth_softsetup_ipv6(struct qeth_card *card)
        rc = qeth_send_startlan(card, QETH_PROT_IPV6);
        if (rc) {
                PRINT_ERR("IPv6 startlan failed on %s\n",
-                         card->info.if_name);
+                         QETH_CARD_IFNAME(card));
                return rc;
        }
        netif_wake_queue(card->dev);
        rc = qeth_query_ipassists(card,QETH_PROT_IPV6);
        if (rc) {
                PRINT_ERR("IPv6 query ipassist failed on %s\n",
-                         card->info.if_name);
+                         QETH_CARD_IFNAME(card));
                return rc;
        }
        rc = qeth_send_simple_setassparms(card, IPA_IPV6,
@@ -5866,7 +6346,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 +6354,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 +6362,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 +6380,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 +6397,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 +6406,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 +6414,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 +6423,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 +6445,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 +6454,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 +6469,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;
        }
@@ -6123,7 +6603,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;
 }
@@ -6150,7 +6630,7 @@ qeth_setrouting_v6(struct qeth_card *card)
                card->options.route6.type = NO_ROUTER;
                PRINT_WARN("Error (0x%04x) while setting routing type on %s. "
                           "Type set to 'no router'.\n",
-                          rc, card->info.if_name);
+                          rc, QETH_CARD_IFNAME(card));
        }
 #endif
        return rc;
@@ -6177,6 +6657,22 @@ qeth_softsetup_card(struct qeth_card *card)
                        return rc;
        } else
                card->lan_online = 1;
+       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,6 +6681,7 @@ 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);
+out:
        netif_stop_queue(card->dev);
        return 0;
 }
@@ -6255,7 +6752,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 +6770,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);
 }
@@ -6314,7 +6811,6 @@ qeth_wait_for_threads(struct qeth_card *card, unsigned long threads)
 static int
 qeth_stop_card(struct qeth_card *card)
 {
-       int recover_flag = 0;
        int rc = 0;
 
        QETH_DBF_TEXT(setup ,2,"stopcard");
@@ -6326,22 +6822,30 @@ 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 (!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,18 +6985,14 @@ qeth_print_status_message(struct qeth_card *card)
 static int
 qeth_register_netdev(struct qeth_card *card)
 {
-       int rc;
-
        QETH_DBF_TEXT(setup, 3, "regnetd");
-       if (card->dev->reg_state != NETREG_UNINITIALIZED)
+       if (card->dev->reg_state != NETREG_UNINITIALIZED) {
+               qeth_netdev_init(card->dev);
                return 0;
+       }
        /* sysfs magic */
        SET_NETDEV_DEV(card->dev, &card->gdev->dev);
-       rc = register_netdev(card->dev);
-       if (!rc)
-               strcpy(card->info.if_name, card->dev->name);
-
-       return rc;
+       return register_netdev(card->dev);
 }
 
 static void
@@ -6503,10 +7003,61 @@ qeth_start_again(struct qeth_card *card)
        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);
+       /* 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) {
+                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));
+                }
+                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)
 {
@@ -6526,47 +7077,50 @@ 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;
        }
 
+       if (card->options.layer2)
+               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*/
-       qeth_set_allowed_threads(card, 0xffffffff, 0);
+       if (card->options.layer2)
+               qeth_set_allowed_threads(card, QETH_RECOVER_THREAD, 0);
+       else
+               qeth_set_allowed_threads(card, 0xffffffff, 0);
        if (recover_flag == CARD_STATE_RECOVER)
                qeth_start_again(card);
        qeth_notify_processes();
@@ -6714,10 +7268,14 @@ 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));
@@ -6736,6 +7294,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 +7448,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 +7521,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 +7579,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) {