patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / s390 / net / qeth_main.c
index ba6ed9b..8aefa28 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *
- * linux/drivers/s390/net/qeth_main.c ($Revision: 1.89 $)
+ * linux/drivers/s390/net/qeth_main.c ($Revision: 1.121 $)
  *
  * 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.89 $         $Date: 2004/04/27 16:27:26 $
+ *    $Revision: 1.121 $        $Date: 2004/06/11 16:32:15 $
  *
  * 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
@@ -78,10 +78,9 @@ qeth_eyecatcher(void)
 #include "qeth_mpc.h"
 #include "qeth_fs.h"
 
-#define VERSION_QETH_C "$Revision: 1.89 $"
-static const char *version = "qeth S/390 OSA-Express driver ("
-       VERSION_QETH_C "/" VERSION_QETH_H "/" VERSION_QETH_MPC_H
-       QETH_VERSION_IPV6 QETH_VERSION_VLAN ")";
+#define VERSION_QETH_C "$Revision: 1.121 $"
+static const char *version = "qeth S/390 OSA-Express driver";
+
 /**
  * Debug Facility Stuff
  */
@@ -92,7 +91,8 @@ static debug_info_t *qeth_dbf_control = NULL;
 static debug_info_t *qeth_dbf_trace = NULL;
 static debug_info_t *qeth_dbf_sense = NULL;
 static debug_info_t *qeth_dbf_qerr = NULL;
-static char qeth_dbf_text_buf[255];
+
+DEFINE_PER_CPU(char[256], qeth_dbf_txt_buf);
 
 /**
  * some more definitions and declarations
@@ -101,12 +101,13 @@ static unsigned int known_devices[][10] = QETH_MODELLIST_ARRAY;
 
 /* list of our cards */
 struct qeth_card_list_struct qeth_card_list;
+/*process list want to be notified*/
+spinlock_t qeth_notify_lock;
+struct list_head qeth_notify_list;
 
 static void qeth_send_control_data_cb(struct qeth_channel *,
                                      struct qeth_cmd_buffer *);
 
-static atomic_t qeth_hsi_count;
-
 /**
  * here we go with function implementation
  */
@@ -154,6 +155,72 @@ qeth_irq_tasklet(unsigned long);
 
 static int
 qeth_set_online(struct ccwgroup_device *);
+
+static struct qeth_ipaddr *
+qeth_get_addr_buffer(enum qeth_prot_versions);
+
+static void
+qeth_notify_processes(void)
+{
+       /*notify all  registered processes */
+       struct qeth_notify_list_struct *n_entry;
+
+       QETH_DBF_TEXT(trace,3,"procnoti");
+       spin_lock(&qeth_notify_lock);
+       list_for_each_entry(n_entry, &qeth_notify_list, list) {
+               send_sig(n_entry->signum, n_entry->task, 1);
+       }
+       spin_unlock(&qeth_notify_lock);
+
+}
+int
+qeth_notifier_unregister(struct task_struct *p)
+{
+       struct qeth_notify_list_struct *n_entry, *tmp;
+
+       QETH_DBF_TEXT(trace, 2, "notunreg");
+       spin_lock(&qeth_notify_lock);
+       list_for_each_entry_safe(n_entry, tmp, &qeth_notify_list, list) {
+               if (n_entry->task == p) {
+                       list_del(&n_entry->list);
+                       kfree(n_entry);
+                       goto out;
+               }
+       }
+out:
+       spin_unlock(&qeth_notify_lock);
+       return 0;
+}
+int
+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) {
+               if (n_entry->task == p) {
+                       n_entry->signum = signum;
+                       spin_unlock(&qeth_notify_lock);
+                       return 0;
+               }
+       }
+       spin_unlock(&qeth_notify_lock);
+
+       n_entry = (struct qeth_notify_list_struct *)
+               kmalloc(sizeof(struct qeth_notify_list_struct),GFP_KERNEL);
+       if (!n_entry)
+               return -ENOMEM;
+       n_entry->task = p;
+       n_entry->signum = signum;
+       spin_lock(&qeth_notify_lock);
+       list_add(&n_entry->list,&qeth_notify_list);
+       spin_unlock(&qeth_notify_lock);
+       return 0;
+}
+
+
 /**
  * free channel command buffers
  */
@@ -459,6 +526,7 @@ qeth_set_offline(struct ccwgroup_device *cgdev)
        ccw_device_set_offline(CARD_RDEV(card));
        if (recover_flag == CARD_STATE_UP)
                card->state = CARD_STATE_RECOVER;
+       qeth_notify_processes();
        return 0;
 }
 
@@ -478,8 +546,6 @@ qeth_remove_device(struct ccwgroup_device *cgdev)
                card->use_hard_stop = 1;
                qeth_set_offline(cgdev);
        }
-       if (card->info.type == QETH_CARD_TYPE_IQD)
-               atomic_dec(&qeth_hsi_count);
        /* remove form our internal list */
        write_lock_irqsave(&qeth_card_list.rwlock, flags);
        list_del(&card->list);
@@ -700,7 +766,7 @@ static void
 qeth_set_ip_addr_list(struct qeth_card *card)
 {
        struct list_head failed_todos;
-       struct qeth_ipaddr *todo, *addr, *tmp;
+       struct qeth_ipaddr *todo, *addr;
        unsigned long flags;
        int rc;
 
@@ -709,9 +775,10 @@ qeth_set_ip_addr_list(struct qeth_card *card)
 
        INIT_LIST_HEAD(&failed_todos);
 
-process_todos:
        spin_lock_irqsave(&card->ip_lock, flags);
-       list_for_each_entry_safe(todo, tmp, &card->ip_tbd_list, entry) {
+       while (!list_empty(&card->ip_tbd_list)) {
+               todo = list_entry(card->ip_tbd_list.next,
+                                 struct qeth_ipaddr, entry);
                list_del_init(&todo->entry);
                rc = __qeth_ref_ip_on_card(card, todo, &addr);
                if (rc == 0) {
@@ -721,28 +788,24 @@ process_todos:
                        /* new entry to be added to on-card list */
                        spin_unlock_irqrestore(&card->ip_lock, flags);
                        rc = qeth_register_addr_entry(card, todo);
-                       if (!rc){
-                               spin_lock_irqsave(&card->ip_lock, flags);
+                       spin_lock_irqsave(&card->ip_lock, flags);
+                       if (!rc)
                                list_add_tail(&todo->entry, &card->ip_list);
-                               spin_unlock_irqrestore(&card->ip_lock, flags);
-                       } else
+                       else
                                list_add_tail(&todo->entry, &failed_todos);
-                       goto process_todos;
                } 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) {
                                kfree(addr);
                                kfree(todo);
                        } else {
-                               spin_lock_irqsave(&card->ip_lock, flags);
                                list_add_tail(&addr->entry, &card->ip_list);
                                list_add_tail(&todo->entry, &failed_todos);
-                               spin_unlock_irqrestore(&card->ip_lock, flags);
                        }
-                       goto process_todos;
                }
        }
        spin_unlock_irqrestore(&card->ip_lock, flags);
@@ -937,7 +1000,6 @@ qeth_set_intial_options(struct qeth_card *card)
        card->options.checksum_type = QETH_CHECKSUM_DEFAULT;
        card->options.broadcast_mode = QETH_TR_BROADCAST_ALLRINGS;
        card->options.macaddr_mode = QETH_TR_MACADDR_NONCANONICAL;
-       card->options.enable_takeover = 1;
        card->options.fake_broadcast = 0;
        card->options.add_hhlen = DEFAULT_ADD_HHLEN;
        card->options.fake_ll = 0;
@@ -998,10 +1060,6 @@ qeth_determine_card_type(struct qeth_card *card)
                if ((CARD_RDEV(card)->id.dev_type == known_devices[i][2]) &&
                    (CARD_RDEV(card)->id.dev_model == known_devices[i][3])) {
                        card->info.type = known_devices[i][4];
-                       if (card->options.enable_takeover)
-                               card->info.func_level = known_devices[i][6];
-                       else
-                               card->info.func_level = known_devices[i][7];
                        card->qdio.no_out_queues = known_devices[i][8];
                        card->info.is_multicast_different = known_devices[i][9];
                        return 0;
@@ -1456,7 +1514,7 @@ qeth_alloc_reply(struct qeth_card *card)
 {
        struct qeth_reply *reply;
 
-       reply = kmalloc(sizeof(struct qeth_reply), GFP_KERNEL|GFP_ATOMIC);
+       reply = kmalloc(sizeof(struct qeth_reply), GFP_ATOMIC);
        if (reply){
                memset(reply, 0, sizeof(struct qeth_reply));
                atomic_set(&reply->refcnt, 1);
@@ -1627,17 +1685,20 @@ qeth_send_control_data_cb(struct qeth_channel *channel,
                        spin_unlock_irqrestore(&card->lock, flags);
                        keep_reply = 0;
                        if (reply->callback != NULL) {
-                               if (cmd)
+                               if (cmd) {
+                                       reply->offset = (__u16)((char*)cmd -
+                                                               (char *)iob->data);
                                        keep_reply = reply->callback(card,
                                                        reply,
                                                        (unsigned long)cmd);
+                               }
                                else
                                        keep_reply = reply->callback(card,
                                                        reply,
                                                        (unsigned long)iob);
                        }
                        if (cmd)
-                               reply->rc = cmd->hdr.return_code;
+                               reply->rc = (s16) cmd->hdr.return_code;
                        else if (iob->rc)
                                reply->rc = iob->rc;
                        if (keep_reply) {
@@ -1703,7 +1764,10 @@ qeth_send_control_data(struct qeth_card *card, int len,
        init_timer(&timer);
        timer.function = qeth_cmd_timeout;
        timer.data = (unsigned long) reply;
-       timer.expires = jiffies + QETH_TIMEOUT;
+       if (IS_IPA(iob->data))
+               timer.expires = jiffies + QETH_IPA_TIMEOUT;
+       else
+               timer.expires = jiffies + QETH_TIMEOUT;
        init_waitqueue_head(&reply->wait_q);
        spin_lock_irqsave(&card->lock, flags);
        list_add_tail(&reply->list, &card->cmd_waiter_list);
@@ -1743,12 +1807,10 @@ qeth_send_ipa_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
                  (struct qeth_card *,struct qeth_reply*, unsigned long),
                  void *reply_param)
 {
-       struct qeth_ipa_cmd *cmd;
        int rc;
 
        QETH_DBF_TEXT(trace,4,"sendipa");
 
-       cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
        memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
        memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
               &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
@@ -2227,12 +2289,11 @@ qeth_process_inbound_buffer(struct qeth_card *card,
        while((skb = qeth_get_next_skb(card, buf->buffer, &element,
                                       &offset, &hdr))){
                qeth_rebuild_skb(card, skb, hdr);
-
-#ifdef CONFIG_QETH_PERF_STATS
-               card->perf_stats.inbound_time += qeth_get_micros() -
-                       card->perf_stats.inbound_start_time;
-               card->perf_stats.inbound_cnt++;
-#endif
+               /* is device UP ? */
+               if (!(card->dev->flags & IFF_UP)){
+                       dev_kfree_skb_irq(skb);
+                       continue;
+               }
                skb->dev = card->dev;
                rxrc = netif_rx(skb);
                card->dev->last_rx = jiffies;
@@ -2284,13 +2345,17 @@ qeth_init_input_buffer(struct qeth_card *card, struct qeth_qdio_buffer *buf)
 }
 
 static inline void
-qeth_clear_output_buffer(struct qeth_card *card,
+qeth_clear_output_buffer(struct qeth_qdio_out_q *queue,
                         struct qeth_qdio_out_buffer *buf)
 {
        int i;
        struct sk_buff *skb;
 
-       for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i){
+       /* is PCI flag set on buffer? */
+       if (buf->buffer->element[0].flags & 0x40)
+               atomic_dec(&queue->set_pci_flags_count);
+
+       for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(queue->card); ++i){
                buf->buffer->element[i].length = 0;
                buf->buffer->element[i].addr = NULL;
                buf->buffer->element[i].flags = 0;
@@ -2300,7 +2365,7 @@ qeth_clear_output_buffer(struct qeth_card *card,
                }
        }
        buf->next_element_to_fill = 0;
-       buf->state = QETH_QDIO_BUF_EMPTY;
+       atomic_set(&buf->state, QETH_QDIO_BUF_EMPTY);
 }
 
 static inline void
@@ -2330,9 +2395,17 @@ qeth_queue_input_buffer(struct qeth_card *card, int index)
                 * 'index') un-requeued -> this buffer is the first buffer that
                 * will be requeued the next time
                 */
+#ifdef CONFIG_QETH_PERF_STATS
+               card->perf_stats.inbound_do_qdio_cnt++;
+               card->perf_stats.inbound_do_qdio_start_time = qeth_get_micros();
+#endif
                rc = do_QDIO(CARD_DDEV(card),
-                            QDIO_FLAG_SYNC_INPUT,
+                            QDIO_FLAG_SYNC_INPUT | QDIO_FLAG_UNDER_INTERRUPT,
                             0, queue->next_buf_to_init, count, NULL);
+#ifdef CONFIG_QETH_PERF_STATS
+               card->perf_stats.inbound_do_qdio_time += qeth_get_micros() -
+                       card->perf_stats.inbound_do_qdio_start_time;
+#endif
                if (rc){
                        PRINT_WARN("qeth_queue_input_buffer's do_QDIO "
                                   "return %i (device %s).\n",
@@ -2369,6 +2442,7 @@ qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status,
        card = (struct qeth_card *) card_ptr;
        net_dev = card->dev;
 #ifdef CONFIG_QETH_PERF_STATS
+       card->perf_stats.inbound_cnt++;
        card->perf_stats.inbound_start_time = qeth_get_micros();
 #endif
        if (status & QDIO_STATUS_LOOK_FOR_ERROR) {
@@ -2391,6 +2465,10 @@ qeth_qdio_input_handler(struct ccw_device * ccwdev, unsigned int status,
                qeth_put_buffer_pool_entry(card, buffer->pool_entry);
                qeth_queue_input_buffer(card, index);
        }
+#ifdef CONFIG_QETH_PERF_STATS
+       card->perf_stats.inbound_time += qeth_get_micros() -
+               card->perf_stats.inbound_start_time;
+#endif
 }
 
 static inline int
@@ -2449,6 +2527,9 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
                buf->buffer->element[buf->next_element_to_fill - 1].flags |=
                                SBAL_FLAGS_LAST_ENTRY;
 
+               if (queue->card->info.type == QETH_CARD_TYPE_IQD)
+                       continue;
+
                if (!queue->do_pack){
                        if ((atomic_read(&queue->used_buffers) >=
                                (QETH_HIGH_WATERMARK_PACK -
@@ -2479,6 +2560,10 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
        }
 
        queue->card->dev->trans_start = jiffies;
+#ifdef CONFIG_QETH_PERF_STATS
+       queue->card->perf_stats.outbound_do_qdio_cnt++;
+       queue->card->perf_stats.outbound_do_qdio_start_time = qeth_get_micros();
+#endif
        if (under_int)
                rc = do_QDIO(CARD_DDEV(queue->card),
                             QDIO_FLAG_SYNC_OUTPUT | QDIO_FLAG_UNDER_INTERRUPT,
@@ -2486,6 +2571,10 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
        else
                rc = do_QDIO(CARD_DDEV(queue->card), QDIO_FLAG_SYNC_OUTPUT,
                             queue->queue_no, index, count, NULL);
+#ifdef CONFIG_QETH_PERF_STATS
+       queue->card->perf_stats.outbound_do_qdio_time += qeth_get_micros() -
+               queue->card->perf_stats.outbound_do_qdio_start_time;
+#endif
        if (rc){
                QETH_DBF_SPRINTF(trace, 0, "qeth_flush_buffers: do_QDIO "
                                 "returned error (%i) on device %s.",
@@ -2493,11 +2582,19 @@ qeth_flush_buffers(struct qeth_qdio_out_q *queue, int under_int,
                QETH_DBF_TEXT(trace, 2, "flushbuf");
                QETH_DBF_TEXT_(trace, 2, " err%d", rc);
                queue->card->stats.tx_errors += count;
+               /* ok, since do_QDIO went wrong the buffers have not been given
+                * to the hardware. they still belong to us, so we can clear
+                * them and reuse then, i.e. set back next_buf_to_fill*/
+               for (i = index; i < index + count; ++i) {
+                       buf = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
+                       qeth_clear_output_buffer(queue, buf);
+               }
+               queue->next_buf_to_fill = index;
                return;
        }
+       atomic_add(count, &queue->used_buffers);
 #ifdef CONFIG_QETH_PERF_STATS
        queue->card->perf_stats.bufs_sent += count;
-       queue->card->perf_stats.outbound_cnt++;
 #endif
 }
 
@@ -2533,11 +2630,11 @@ qeth_switch_packing_state(struct qeth_qdio_out_q *queue)
                        queue->do_pack = 0;
                        /* flush packing buffers */
                        buffer = &queue->bufs[queue->next_buf_to_fill];
-                       BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED);
-                       if (buffer->next_element_to_fill > 0) {
-                               buffer->state = QETH_QDIO_BUF_PRIMED;
+                       if ((atomic_read(&buffer->state) ==
+                                               QETH_QDIO_BUF_EMPTY) &&
+                           (buffer->next_element_to_fill > 0)) {
+                               atomic_set(&buffer->state,QETH_QDIO_BUF_PRIMED);
                                flush_count++;
-                               atomic_inc(&queue->used_buffers);
                                queue->next_buf_to_fill =
                                        (queue->next_buf_to_fill + 1) %
                                        QDIO_MAX_BUFFERS_PER_Q;
@@ -2551,17 +2648,17 @@ static inline void
 qeth_flush_buffers_on_no_pci(struct qeth_qdio_out_q *queue, int under_int)
 {
        struct qeth_qdio_out_buffer *buffer;
+       int index;
 
-       buffer = &queue->bufs[queue->next_buf_to_fill];
-       BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED);
-       if (buffer->next_element_to_fill > 0){
+       index = queue->next_buf_to_fill;
+       buffer = &queue->bufs[index];
+       if((atomic_read(&buffer->state) == QETH_QDIO_BUF_EMPTY) &&
+          (buffer->next_element_to_fill > 0)){
                /* it's a packing buffer */
-               buffer->state = QETH_QDIO_BUF_PRIMED;
-               atomic_inc(&queue->used_buffers);
-               qeth_flush_buffers(queue, under_int, queue->next_buf_to_fill,
-                                  1);
+               atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED);
                queue->next_buf_to_fill =
                        (queue->next_buf_to_fill + 1) % QDIO_MAX_BUFFERS_PER_Q;
+               qeth_flush_buffers(queue, under_int, index, 1);
        }
 }
 
@@ -2591,6 +2688,10 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
                }
        }
 
+#ifdef CONFIG_QETH_PERF_STATS
+       card->perf_stats.outbound_handler_cnt++;
+       card->perf_stats.outbound_handler_start_time = qeth_get_micros();
+#endif
        for(i = first_element; i < (first_element + count); ++i){
                buffer = &queue->bufs[i % QDIO_MAX_BUFFERS_PER_Q];
                /*we only handle the KICK_IT error by doing a recovery */
@@ -2600,15 +2701,15 @@ qeth_qdio_output_handler(struct ccw_device * ccwdev, unsigned int status,
                        qeth_schedule_recovery(card);
                        return;
                }
-               /* is PCI flag set on buffer? */
-               if (buffer->buffer->element[0].flags & 0x40)
-                       atomic_dec(&queue->set_pci_flags_count);
-
-               qeth_clear_output_buffer(card, buffer);
-               atomic_dec(&queue->used_buffers);
+               qeth_clear_output_buffer(queue, buffer);
        }
+       atomic_sub(count, &queue->used_buffers);
 
        netif_wake_queue(card->dev);
+#ifdef CONFIG_QETH_PERF_STATS
+       card->perf_stats.outbound_handler_time += qeth_get_micros() -
+               card->perf_stats.outbound_handler_start_time;
+#endif
 }
 
 static char*
@@ -2795,8 +2896,8 @@ qeth_free_qdio_buffers(struct qeth_card *card)
        /* free outbound qdio_qs */
        for (i = 0; i < card->qdio.no_out_queues; ++i){
                for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
-                       qeth_clear_output_buffer(card, &card->qdio.
-                                               out_qs[i]->bufs[j]);
+                       qeth_clear_output_buffer(card->qdio.out_qs[i],
+                                       &card->qdio.out_qs[i]->bufs[j]);
                kfree(card->qdio.out_qs[i]);
        }
        kfree(card->qdio.out_qs);
@@ -2813,8 +2914,8 @@ qeth_clear_qdio_buffers(struct qeth_card *card)
        for (i = 0; i < card->qdio.no_out_queues; ++i)
                if (card->qdio.out_qs[i]){
                        for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
-                               qeth_clear_output_buffer(card, &card->qdio.
-                                               out_qs[i]->bufs[j]);
+                               qeth_clear_output_buffer(card->qdio.out_qs[i],
+                                               &card->qdio.out_qs[i]->bufs[j]);
                }
 }
 
@@ -2866,8 +2967,8 @@ qeth_init_qdio_queues(struct qeth_card *card)
                memset(card->qdio.out_qs[i]->qdio_bufs, 0,
                       QDIO_MAX_BUFFERS_PER_Q * sizeof(struct qdio_buffer));
                for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j){
-                       qeth_clear_output_buffer(card, &card->qdio.
-                                                out_qs[i]->bufs[j]);
+                       qeth_clear_output_buffer(card->qdio.out_qs[i],
+                                       &card->qdio.out_qs[i]->bufs[j]);
                }
                card->qdio.out_qs[i]->card = card;
                card->qdio.out_qs[i]->next_buf_to_fill = 0;
@@ -3134,20 +3235,6 @@ out_qdio:
        return rc;
 }
 
-static void
-qeth_set_device_name(struct qeth_card *card)
-{
-       char buf[IF_NAME_LEN];
-
-       memset(buf, 0, IF_NAME_LEN);
-       if (card->info.type == QETH_CARD_TYPE_IQD) {
-               sprintf(buf,"hsi%d", atomic_read(&qeth_hsi_count));
-               atomic_inc(&qeth_hsi_count);
-               memcpy(card->dev->name,buf,IF_NAME_LEN);
-       }
-
-}
-
 static struct net_device *
 qeth_get_netdevice(enum qeth_card_types type, enum qeth_link_types linktype)
 {
@@ -3167,6 +3254,8 @@ qeth_get_netdevice(enum qeth_card_types type, enum qeth_link_types linktype)
                }
                break;
        case QETH_CARD_TYPE_IQD:
+               dev = alloc_netdev(0, "hsi%d", ether_setup);
+               break;
        default:
                dev = alloc_etherdev(0);
        }
@@ -3195,11 +3284,8 @@ qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
                card->stats.tx_carrier_errors++;
                return -EIO;
        }
-       if (netif_queue_stopped(dev) ) {
-               card->stats.tx_dropped++;
-               return -EBUSY;
-       }
 #ifdef CONFIG_QETH_PERF_STATS
+       card->perf_stats.outbound_cnt++;
        card->perf_stats.outbound_start_time = qeth_get_micros();
 #endif
        /*
@@ -3210,6 +3296,10 @@ qeth_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
        if (!(rc = qeth_send_packet(card, skb)))
                netif_wake_queue(dev);
 
+#ifdef CONFIG_QETH_PERF_STATS
+       card->perf_stats.outbound_time += qeth_get_micros() -
+               card->perf_stats.outbound_start_time;
+#endif
        return rc;
 }
 
@@ -3556,7 +3646,6 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf
        int first_lap = 1;
 
        QETH_DBF_TEXT(trace, 6, "qdfillbf");
-
        buffer = buf->buffer;
        atomic_inc(&skb->users);
        skb_queue_tail(&buf->skb_list, skb);
@@ -3591,7 +3680,7 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf
        if (!queue->do_pack) {
                QETH_DBF_TEXT(trace, 6, "fillbfnp");
                /* set state to PRIMED -> will be flushed */
-               buf->state = QETH_QDIO_BUF_PRIMED;
+               atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
        } else {
                QETH_DBF_TEXT(trace, 6, "fillbfpa");
 #ifdef CONFIG_QETH_PERF_STATS
@@ -3603,73 +3692,97 @@ qeth_fill_buffer(struct qeth_qdio_out_q *queue, struct qeth_qdio_out_buffer *buf
                         * packed buffer if full -> set state PRIMED
                         * -> will be flushed
                         */
-                       buf->state = QETH_QDIO_BUF_PRIMED;
+                       atomic_set(&buf->state, QETH_QDIO_BUF_PRIMED);
                }
        }
        return 0;
 }
 
 static inline int
-qeth_do_send_packet(struct qeth_card *card, struct sk_buff *skb,
-                   struct qeth_qdio_out_q *queue, int ipv,
-                   int cast_type)
+qeth_do_send_packet_fast(struct qeth_card *card, struct qeth_qdio_out_q *queue,
+                        struct sk_buff *skb, struct qeth_hdr *hdr,
+                        int elements_needed)
+{
+       struct qeth_qdio_out_buffer *buffer;
+       int index;
+
+       QETH_DBF_TEXT(trace, 6, "dosndpfa");
+
+       spin_lock(&queue->lock);
+       index = queue->next_buf_to_fill;
+       buffer = &queue->bufs[queue->next_buf_to_fill];
+       /*
+        * check if buffer is empty to make sure that we do not 'overtake'
+        * ourselves and try to fill a buffer that is already primed
+        */
+       if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY) {
+               card->stats.tx_dropped++;
+               spin_unlock(&queue->lock);
+               return -EBUSY;
+       }
+       queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
+                                 QDIO_MAX_BUFFERS_PER_Q;
+       qeth_fill_buffer(queue, buffer, (char *)hdr, skb);
+       qeth_flush_buffers(queue, 0, index, 1);
+       spin_unlock(&queue->lock);
+       return 0;
+}
+
+static inline int
+qeth_do_send_packet(struct qeth_card *card, struct qeth_qdio_out_q *queue,
+                   struct sk_buff *skb, struct qeth_hdr *hdr,
+                   int elements_needed)
 {
-       struct qeth_hdr *hdr;
        struct qeth_qdio_out_buffer *buffer;
-       int elements_needed;
        int start_index;
        int flush_count = 0;
-       int rc;
+       int rc = 0;
 
        QETH_DBF_TEXT(trace, 6, "dosndpkt");
 
-       if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){
-               QETH_DBF_TEXT_(trace, 4, "1err%d", rc);
-               return rc;
-       }
-       qeth_fill_header(card, hdr, skb, ipv, cast_type);
-       elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) + skb->len)
-                               >> PAGE_SHIFT);
-       if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){
-               PRINT_ERR("qeth_do_send_packet: invalid size of "
-                                "IP packet. Discarded.");
-               return -EINVAL;
-       }
-
        spin_lock(&queue->lock);
        start_index = queue->next_buf_to_fill;
        buffer = &queue->bufs[queue->next_buf_to_fill];
-       BUG_ON(buffer->state == QETH_QDIO_BUF_PRIMED);
+       /*
+        * check if buffer is empty to make sure that we do not 'overtake'
+        * ourselves and try to fill a buffer that is already primed
+        */
+       if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){
+               card->stats.tx_dropped++;
+               spin_unlock(&queue->lock);
+               return -EBUSY;
+       }
        if (queue->do_pack){
                /* does packet fit in current buffer? */
-               if((QETH_MAX_BUFFER_ELEMENTS(card) - buffer->next_element_to_fill)
-                               < elements_needed){
+               if((QETH_MAX_BUFFER_ELEMENTS(card) -
+                   buffer->next_element_to_fill) < elements_needed){
                        /* ... no -> set state PRIMED */
-                       buffer->state = QETH_QDIO_BUF_PRIMED;
+                       atomic_set(&buffer->state, QETH_QDIO_BUF_PRIMED);
                        flush_count++;
-                       atomic_inc(&queue->used_buffers);
                        queue->next_buf_to_fill =
                                (queue->next_buf_to_fill + 1) %
                                QDIO_MAX_BUFFERS_PER_Q;
                        buffer = &queue->bufs[queue->next_buf_to_fill];
+                       /* we did a step forward, so check buffer state again */
+                       if (atomic_read(&buffer->state) != QETH_QDIO_BUF_EMPTY){
+                               card->stats.tx_dropped++;
+                               qeth_flush_buffers(queue, 0, start_index, 1);
+                               spin_unlock(&queue->lock);
+                               /* return EBUSY because we sent old packet, not
+                                * the current one */
+                               return -EBUSY;
+                       }
                }
        }
-
-       rc = qeth_fill_buffer(queue, buffer, (char *)hdr, skb);
-       if (rc) {
-               PRINT_WARN("qeth_do_send_packet: error during "
-                             "qeth_fill_buffer.");
-               card->stats.tx_dropped++;
-       } else if (buffer->state == QETH_QDIO_BUF_PRIMED){
+       qeth_fill_buffer(queue, buffer, (char *)hdr, skb);
+       if (atomic_read(&buffer->state) == QETH_QDIO_BUF_PRIMED){
                /* next time fill the next buffer */
                flush_count++;
-               atomic_inc(&queue->used_buffers);
                queue->next_buf_to_fill = (queue->next_buf_to_fill + 1) %
                        QDIO_MAX_BUFFERS_PER_Q;
        }
        /* check if we need to switch packing state of this queue */
-       if (card->info.type != QETH_CARD_TYPE_IQD)
-               flush_count += qeth_switch_packing_state(queue);
+       flush_count += qeth_switch_packing_state(queue);
 
        if (flush_count)
                qeth_flush_buffers(queue, 0, start_index, flush_count);
@@ -3678,7 +3791,6 @@ qeth_do_send_packet(struct qeth_card *card, struct sk_buff *skb,
                qeth_flush_buffers_on_no_pci(queue, 0);
 
        spin_unlock(&queue->lock);
-
        return rc;
 }
 
@@ -3688,6 +3800,8 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
        int ipv;
        int cast_type;
        struct qeth_qdio_out_q *queue;
+       struct qeth_hdr *hdr;
+       int elements_needed;
        int rc;
 
        QETH_DBF_TEXT(trace, 6, "sendpkt");
@@ -3696,24 +3810,30 @@ qeth_send_packet(struct qeth_card *card, struct sk_buff *skb)
        cast_type = qeth_get_cast_type(card, skb);
        queue = card->qdio.out_qs
                [qeth_get_priority_queue(card, skb, ipv, cast_type)];
-       /* do we have empty buffers? */
-       rc = (atomic_read(&queue->used_buffers) >=
-             QDIO_MAX_BUFFERS_PER_Q - 1) ? -EBUSY : 0;
-       if (rc) {
-               card->stats.tx_dropped++;
+
+       if ((rc = qeth_prepare_skb(card, &skb, &hdr, ipv))){
                QETH_DBF_TEXT_(trace, 4, "1err%d", rc);
                return rc;
        }
+       qeth_fill_header(card, hdr, skb, ipv, cast_type);
+       elements_needed = 1 + (((((unsigned long) hdr) % PAGE_SIZE) + skb->len)
+                               >> PAGE_SHIFT);
+       if (elements_needed > QETH_MAX_BUFFER_ELEMENTS(card)){
+               PRINT_ERR("qeth_do_send_packet: invalid size of "
+                                "IP packet. Discarded.");
+               return -EINVAL;
+       }
 
-       rc = qeth_do_send_packet(card, skb, queue, ipv, cast_type);
+       if (card->info.type != QETH_CARD_TYPE_IQD)
+               rc = qeth_do_send_packet(card, queue, skb, hdr,
+                                        elements_needed);
+       else
+               rc = qeth_do_send_packet_fast(card, queue, skb, hdr,
+                                             elements_needed);
 
        if (!rc){
                card->stats.tx_packets++;
                card->stats.tx_bytes += skb->len;
-#ifdef CONFIG_QETH_PERF_STATS
-               card->perf_stats.outbound_time += qeth_get_micros() -
-                       card->perf_stats.outbound_start_time;
-#endif
        }
        return rc;
 }
@@ -3859,69 +3979,107 @@ qeth_arp_set_no_entries(struct qeth_card *card, int no_entries)
        if (rc) {
                tmp = rc;
                PRINT_WARN("Could not set number of ARP entries on %s: "
-                          "%s (0x%x)\n",
+                          "%s (0x%x/%d)\n",
                           card->info.if_name, qeth_arp_get_error_cause(&rc),
-                          tmp);
+                          tmp, tmp);
        }
        return rc;
 }
 
+static inline void
+qeth_copy_arp_entries_stripped(struct qeth_arp_query_info *qinfo,
+                              struct qeth_arp_query_data *qdata,
+                              int entry_size, int uentry_size)
+{
+       char *entry_ptr;
+       char *uentry_ptr;
+       int i;
+
+       entry_ptr = (char *)&qdata->data;
+       uentry_ptr = (char *)(qinfo->udata + qinfo->udata_offset);
+       for (i = 0; i < qdata->no_entries; ++i){
+               /* strip off 32 bytes "media specific information" */
+               memcpy(uentry_ptr, (entry_ptr + 32), entry_size - 32);
+               entry_ptr += entry_size;
+               uentry_ptr += uentry_size;
+       }
+}
+
 static int
 qeth_arp_query_cb(struct qeth_card *card, struct qeth_reply *reply,
                  unsigned long data)
 {
-       struct qeth_ipa_arp_cmd *cmd;
+       struct qeth_ipa_cmd *cmd;
        struct qeth_arp_query_data *qdata;
        struct qeth_arp_query_info *qinfo;
        int entry_size;
+       int uentry_size;
        int i;
 
        QETH_DBF_TEXT(trace,4,"arpquecb");
 
        qinfo = (struct qeth_arp_query_info *) reply->param;
-       cmd = (struct qeth_ipa_arp_cmd *) data;
-       if (cmd->ihdr.return_code) {
-               QETH_DBF_TEXT_(trace,4,"qaer1%i", cmd->ihdr.return_code);
+       cmd = (struct qeth_ipa_cmd *) data;
+       if (cmd->hdr.return_code) {
+               QETH_DBF_TEXT_(trace,4,"qaer1%i", cmd->hdr.return_code);
                return 0;
        }
-       if (cmd->shdr.return_code) {
-               cmd->ihdr.return_code = cmd->shdr.return_code;
-               QETH_DBF_TEXT_(trace,4,"qaer2%i", cmd->ihdr.return_code);
+       if (cmd->data.setassparms.hdr.return_code) {
+               cmd->hdr.return_code = cmd->data.setassparms.hdr.return_code;
+               QETH_DBF_TEXT_(trace,4,"qaer2%i", cmd->hdr.return_code);
                return 0;
        }
-       qdata = &cmd->data.query_arp;
+       qdata = &cmd->data.setassparms.data.query_arp;
        switch(qdata->reply_bits){
        case 5:
-               entry_size = sizeof(struct qeth_arp_qi_entry5);
+               uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry5);
+               if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
+                       uentry_size = sizeof(struct qeth_arp_qi_entry5_short);
                break;
        case 7:
-               entry_size = sizeof(struct qeth_arp_qi_entry7);
-               break;
+               /* fall through to default */
        default:
                /* tr is the same as eth -> entry7 */
-               entry_size = sizeof(struct qeth_arp_qi_entry7);
+               uentry_size = entry_size = sizeof(struct qeth_arp_qi_entry7);
+               if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
+                       uentry_size = sizeof(struct qeth_arp_qi_entry7_short);
                break;
        }
        /* check if there is enough room in userspace */
        if ((qinfo->udata_len - qinfo->udata_offset) <
-                       qdata->no_entries * entry_size){
+                       qdata->no_entries * uentry_size){
                QETH_DBF_TEXT_(trace, 4, "qaer3%i", -ENOMEM);
-               cmd->ihdr.return_code = -ENOMEM;
+               cmd->hdr.return_code = -ENOMEM;
+               PRINT_WARN("query ARP user space buffer is too small for "
+                          "the returned number of ARP entries. "
+                          "Aborting query!\n");
                goto out_error;
        }
-       QETH_DBF_TEXT_(trace, 4, "anore%i", cmd->shdr.number_of_replies);
-       QETH_DBF_TEXT_(trace, 4, "aseqn%i", cmd->shdr.seq_no);
+       QETH_DBF_TEXT_(trace, 4, "anore%i",
+                      cmd->data.setassparms.hdr.number_of_replies);
+       QETH_DBF_TEXT_(trace, 4, "aseqn%i", cmd->data.setassparms.hdr.seq_no);
        QETH_DBF_TEXT_(trace, 4, "anoen%i", qdata->no_entries);
-       for (i = 0; i < qdata->no_entries; ++i){
+
+       if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES) {
+               /* strip off "media specific information" */
+               qeth_copy_arp_entries_stripped(qinfo, qdata, entry_size,
+                                              uentry_size);
+       } else
+               /*copy entries to user buffer*/
                memcpy(qinfo->udata + qinfo->udata_offset,
-                      qdata->data + i*entry_size, entry_size);
-               qinfo->no_entries++;
-               qinfo->udata_offset += entry_size;
-       }
+                      (char *)&qdata->data, qdata->no_entries*uentry_size);
+
+       qinfo->no_entries += qdata->no_entries;
+       qinfo->udata_offset += (qdata->no_entries*uentry_size);
        /* check if all replies received ... */
-       if (cmd->shdr.seq_no < cmd->shdr.number_of_replies)
+       if (cmd->data.setassparms.hdr.seq_no <
+           cmd->data.setassparms.hdr.number_of_replies)
                return 1;
        memcpy(qinfo->udata, &qinfo->no_entries, 4);
+       /* keep STRIP_ENTRIES flag so the user program can distinguish
+        * stripped entries from normal ones */
+       if (qinfo->mask_bits & QETH_QARP_STRIP_ENTRIES)
+               qdata->reply_bits |= QETH_QARP_STRIP_ENTRIES;
        memcpy(qinfo->udata + QETH_QARP_MASK_OFFSET,&qdata->reply_bits,2);
        return 0;
 out_error:
@@ -3930,70 +4088,54 @@ out_error:
        return 0;
 }
 
-static struct qeth_cmd_buffer *
-qeth_get_ipacmd_buffer(struct qeth_card *, enum qeth_ipa_cmds,
-                      enum qeth_prot_versions);
-
-struct qeth_cmd_buffer *
-qeth_get_ipa_arp_cmd_buffer(struct qeth_card *card, u16 cmd_code,
-                           u32 data_len, enum qeth_prot_versions proto)
+static int
+qeth_send_ipa_arp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+                     int len, int (*reply_cb)(struct qeth_card *,
+                                              struct qeth_reply *,
+                                              unsigned long),
+                     void *reply_param)
 {
-       struct qeth_cmd_buffer *iob;
-       struct qeth_ipa_arp_cmd *cmd;
-       u16 s1, s2;
-
-       QETH_DBF_TEXT(trace,4,"getarpcm");
-       iob = qeth_get_ipacmd_buffer(card, IPA_CMD_SETASSPARMS, proto);
+       QETH_DBF_TEXT(trace,4,"sendarp");
 
        memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
-
-       if ((IPA_PDU_HEADER_SIZE + QETH_ARP_CMD_BASE_LEN + data_len) > 256) {
-               /* adjust sizes in IPA_PDU_HEADER */
-               s1 = (u32) IPA_PDU_HEADER_SIZE + QETH_ARP_CMD_BASE_LEN +
-                          data_len;
-               s2 = (u32) QETH_ARP_CMD_BASE_LEN + data_len;
-               memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2);
-               memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2);
-               memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2);
-               memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2);
-       }
-
-       cmd = (struct qeth_ipa_arp_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
-       cmd->shdr.assist_no = IPA_ARP_PROCESSING;
-       cmd->shdr.length = 8 + data_len;
-       cmd->shdr.command_code = cmd_code;
-       cmd->shdr.return_code = 0;
-       cmd->shdr.seq_no = 0;
-
-       return iob;
+       memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
+              &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
+       return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob,
+                                     reply_cb, reply_param);
 }
 
 static int
-qeth_send_ipa_arp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
-                     char *data, int data_len,
-                     int (*reply_cb)
-                     (struct qeth_card *,struct qeth_reply*, unsigned long),
+qeth_send_ipa_snmp_cmd(struct qeth_card *card, struct qeth_cmd_buffer *iob,
+                     int len, int (*reply_cb)(struct qeth_card *,
+                                              struct qeth_reply *,
+                                              unsigned long),
                      void *reply_param)
 {
-       int rc;
+       u16 s1, s2;
 
-       QETH_DBF_TEXT(trace,4,"sendarp");
+       QETH_DBF_TEXT(trace,4,"sendsnmp");
 
-       memcpy(QETH_IPA_ARP_DATA_POS(iob->data), data, data_len);
+       memcpy(iob->data, IPA_PDU_HEADER, IPA_PDU_HEADER_SIZE);
        memcpy(QETH_IPA_CMD_DEST_ADDR(iob->data),
               &card->token.ulp_connection_r, QETH_MPC_TOKEN_LENGTH);
-
-       rc = qeth_send_control_data(card, IPA_PDU_HEADER_SIZE +
-                                   QETH_ARP_CMD_BASE_LEN + data_len, iob,
-                                   reply_cb, reply_param);
-       return rc;
+       /* adjust PDU length fields in IPA_PDU_HEADER */
+       s1 = (u32) IPA_PDU_HEADER_SIZE + len;
+       s2 = (u32) len;
+       memcpy(QETH_IPA_PDU_LEN_TOTAL(iob->data), &s1, 2);
+       memcpy(QETH_IPA_PDU_LEN_PDU1(iob->data), &s2, 2);
+       memcpy(QETH_IPA_PDU_LEN_PDU2(iob->data), &s2, 2);
+       memcpy(QETH_IPA_PDU_LEN_PDU3(iob->data), &s2, 2);
+       return qeth_send_control_data(card, IPA_PDU_HEADER_SIZE + len, iob,
+                                     reply_cb, reply_param);
 }
 
+static struct qeth_cmd_buffer *
+qeth_get_setassparms_cmd(struct qeth_card *, enum qeth_ipa_funcs,
+                        __u16, __u16, enum qeth_prot_versions);
 static int
 qeth_arp_query(struct qeth_card *card, char *udata)
 {
        struct qeth_cmd_buffer *iob;
-       struct qeth_arp_query_data *qdata;
        struct qeth_arp_query_info qinfo = {0, };
        int tmp;
        int rc;
@@ -4008,40 +4150,32 @@ qeth_arp_query(struct qeth_card *card, char *udata)
         */
        if (card->info.guestlan)
                return -EOPNOTSUPP;
-       if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
+       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);
                return -EOPNOTSUPP;
        }
-       /* get size of userspace mem area */
-       if (copy_from_user(&qinfo.udata_len, udata, 4))
+       /* get size of userspace buffer and mask_bits -> 6 bytes */
+       if (copy_from_user(&qinfo, udata, 6))
                return -EFAULT;
        if (!(qinfo.udata = kmalloc(qinfo.udata_len, GFP_KERNEL)))
                return -ENOMEM;
        memset(qinfo.udata, 0, qinfo.udata_len);
        qinfo.udata_offset = QETH_QARP_ENTRIES_OFFSET;
-       /* alloc mem area for the actual query */
-       if (!(qdata = kmalloc(sizeof(struct qeth_arp_query_data),
-                             GFP_KERNEL))){
-               kfree(qinfo.udata);
-               return -ENOMEM;
-       }
-       memset(qdata, 0, sizeof(struct qeth_arp_query_data));
-       /* do not give sizeof(struct qeth_arp_query_data) to next command;
-        * this would cause the IPA PDU size to be set to a value of > 256
-        * and this is to much for HiperSockets */
-       iob = qeth_get_ipa_arp_cmd_buffer(card, IPA_CMD_ASS_ARP_QUERY_INFO,
-                                         0, QETH_PROT_IPV4);
+       iob = qeth_get_setassparms_cmd(card, IPA_ARP_PROCESSING,
+                                      IPA_CMD_ASS_ARP_QUERY_INFO,
+                                      sizeof(int),QETH_PROT_IPV4);
+
        rc = qeth_send_ipa_arp_cmd(card, iob,
-                                  (char *) qdata,
-                                  sizeof(struct qeth_arp_query_data),
-                                  qeth_arp_query_cb,
-                                  (void *)&qinfo);
+                                  QETH_SETASS_BASE_LEN+QETH_ARP_CMD_LEN,
+                                  qeth_arp_query_cb, (void *)&qinfo);
        if (rc) {
                tmp = rc;
-               PRINT_WARN("Error while querying ARP cache on %s: %s (0x%x)\n",
+               PRINT_WARN("Error while querying ARP cache on %s: %s "
+                          "(0x%x/%d)\n",
                           card->info.if_name, qeth_arp_get_error_cause(&rc),
-                          tmp);
+                          tmp, tmp);
                copy_to_user(udata, qinfo.udata, 4);
        } else {
                copy_to_user(udata, qinfo.udata, qinfo.udata_len);
@@ -4050,13 +4184,155 @@ qeth_arp_query(struct qeth_card *card, char *udata)
        return rc;
 }
 
+/**
+ * SNMP command callback
+ */
 static int
-qeth_default_setassparms_cb(struct qeth_card *, struct qeth_reply *,
-                           unsigned long);
+qeth_snmp_command_cb(struct qeth_card *card, struct qeth_reply *reply,
+                    unsigned long sdata)
+{
+       struct qeth_ipa_cmd *cmd;
+       struct qeth_arp_query_info *qinfo;
+       struct qeth_snmp_cmd *snmp;
+       unsigned char *data;
+       __u16 data_len;
+
+       QETH_DBF_TEXT(trace,3,"snpcmdcb");
+
+       cmd = (struct qeth_ipa_cmd *) sdata;
+       data = (unsigned char *)((char *)cmd - reply->offset);
+       qinfo = (struct qeth_arp_query_info *) reply->param;
+       snmp = &cmd->data.setadapterparms.data.snmp;
+
+       if (cmd->hdr.return_code) {
+               QETH_DBF_TEXT_(trace,4,"scer1%i", cmd->hdr.return_code);
+               return 0;
+       }
+       if (cmd->data.setadapterparms.hdr.return_code) {
+               cmd->hdr.return_code = cmd->data.setadapterparms.hdr.return_code;
+               QETH_DBF_TEXT_(trace,4,"scer2%i", cmd->hdr.return_code);
+               return 0;
+       }
+       data_len = *((__u16*)QETH_IPA_PDU_LEN_PDU1(data));
+       if (cmd->data.setadapterparms.hdr.seq_no == 1)
+               data_len -= (__u16)((char *)&snmp->data - (char *)cmd);
+       else
+               data_len -= (__u16)((char*)&snmp->request - (char *)cmd);
+
+       /* check if there is enough room in userspace */
+       if ((qinfo->udata_len - qinfo->udata_offset) < data_len) {
+               QETH_DBF_TEXT_(trace, 4, "scer3%i", -ENOMEM);
+               cmd->hdr.return_code = -ENOMEM;
+               return 0;
+       }
+       QETH_DBF_TEXT_(trace, 4, "snore%i",
+                      cmd->data.setadapterparms.hdr.used_total);
+       QETH_DBF_TEXT_(trace, 4, "sseqn%i", cmd->data.setadapterparms.hdr.seq_no);
+       /*copy entries to user buffer*/
+       if (cmd->data.setadapterparms.hdr.seq_no == 1) {
+               memcpy(qinfo->udata + qinfo->udata_offset,
+                      (char *)snmp,
+                      data_len + offsetof(struct qeth_snmp_cmd,data));
+               qinfo->udata_offset += offsetof(struct qeth_snmp_cmd, data);
+       } else {
+               memcpy(qinfo->udata + qinfo->udata_offset,
+                      (char *)&snmp->request, data_len);
+       }
+       qinfo->udata_offset += data_len;
+       /* check if all replies received ... */
+               QETH_DBF_TEXT_(trace, 4, "srtot%i",
+                              cmd->data.setadapterparms.hdr.used_total);
+               QETH_DBF_TEXT_(trace, 4, "srseq%i",
+                              cmd->data.setadapterparms.hdr.seq_no);
+       if (cmd->data.setadapterparms.hdr.seq_no <
+           cmd->data.setadapterparms.hdr.used_total)
+               return 1;
+       return 0;
+}
 
 static struct qeth_cmd_buffer *
-qeth_get_setassparms_cmd(struct qeth_card *, enum qeth_ipa_funcs,
-                        __u16, __u16, enum qeth_prot_versions);
+qeth_get_ipacmd_buffer(struct qeth_card *, enum qeth_ipa_cmds,
+                      enum qeth_prot_versions );
+
+static struct qeth_cmd_buffer *
+qeth_get_adapter_cmd(struct qeth_card *card, __u32 command, __u32 cmdlen)
+{
+       struct qeth_cmd_buffer *iob;
+       struct qeth_ipa_cmd *cmd;
+
+       iob = qeth_get_ipacmd_buffer(card,IPA_CMD_SETADAPTERPARMS,
+                                    QETH_PROT_IPV4);
+       cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+       cmd->data.setadapterparms.hdr.cmdlength = cmdlen;
+       cmd->data.setadapterparms.hdr.command_code = command;
+       cmd->data.setadapterparms.hdr.used_total = 1;
+       cmd->data.setadapterparms.hdr.seq_no = 1;
+
+       return iob;
+}
+
+/**
+ * function to send SNMP commands to OSA-E card
+ */
+static int
+qeth_snmp_command(struct qeth_card *card, char *udata)
+{
+       struct qeth_cmd_buffer *iob;
+       struct qeth_ipa_cmd *cmd;
+       struct qeth_snmp_ureq *ureq;
+       int req_len;
+       struct qeth_arp_query_info qinfo = {0, };
+       int rc = 0;
+
+       QETH_DBF_TEXT(trace,3,"snmpcmd");
+
+       if (card->info.guestlan)
+               return -EOPNOTSUPP;
+       if (!qeth_adp_supported(card,IPA_SETADP_SET_SNMP_CONTROL)) {
+               PRINT_WARN("SNMP Query MIBS not supported "
+                          "on %s!\n", card->info.if_name);
+               return -EOPNOTSUPP;
+       }
+       /* skip 4 bytes (data_len struct member) to get req_len */
+       if (copy_from_user(&req_len, udata + sizeof(int), sizeof(int)))
+               return -EFAULT;
+       ureq = kmalloc(req_len, GFP_KERNEL);
+       if (!ureq) {
+               QETH_DBF_TEXT(trace, 2, "snmpnome");
+               return -ENOMEM;
+       }
+       if (copy_from_user(ureq, udata, req_len)){
+               kfree(ureq);
+               return -EFAULT;
+       }
+       qinfo.udata_len = ureq->hdr.data_len;
+       if (!(qinfo.udata = kmalloc(qinfo.udata_len, GFP_KERNEL))){
+               kfree(ureq);
+               return -ENOMEM;
+       }
+       memset(qinfo.udata, 0, qinfo.udata_len);
+       qinfo.udata_offset = sizeof(struct qeth_snmp_ureq_hdr);
+
+       iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL,
+                                  QETH_SNMP_SETADP_CMDLENGTH + req_len);
+       cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
+       memcpy(&cmd->data.setadapterparms.data.snmp, &ureq->cmd, req_len);
+       rc = qeth_send_ipa_snmp_cmd(card, iob, QETH_SETADP_BASE_LEN + req_len,
+                                   qeth_snmp_command_cb, (void *)&qinfo);
+       if (rc)
+               PRINT_WARN("SNMP command failed on %s: (0x%x)\n",
+                          card->info.if_name, rc);
+        else
+               copy_to_user(udata, qinfo.udata, qinfo.udata_len);
+
+       kfree(ureq);
+       kfree(qinfo.udata);
+       return rc;
+}
+
+static int
+qeth_default_setassparms_cb(struct qeth_card *, struct qeth_reply *,
+                           unsigned long);
 
 static int
 qeth_send_setassparms(struct qeth_card *, struct qeth_cmd_buffer *,
@@ -4101,9 +4377,9 @@ qeth_arp_add_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry)
                tmp = rc;
                qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
                PRINT_WARN("Could not add ARP entry for address %s on %s: "
-                          "%s (0x%x)\n",
+                          "%s (0x%x/%d)\n",
                           buf, card->info.if_name,
-                          qeth_arp_get_error_cause(&rc), tmp);
+                          qeth_arp_get_error_cause(&rc), tmp, tmp);
        }
        return rc;
 }
@@ -4144,9 +4420,9 @@ qeth_arp_remove_entry(struct qeth_card *card, struct qeth_arp_cache_entry *entry
                memset(buf, 0, 16);
                qeth_ipaddr4_to_string((u8 *)entry->ipaddr, buf);
                PRINT_WARN("Could not delete ARP entry for address %s on %s: "
-                          "%s (0x%x)\n",
+                          "%s (0x%x/%d)\n",
                           buf, card->info.if_name,
-                          qeth_arp_get_error_cause(&rc), tmp);
+                          qeth_arp_get_error_cause(&rc), tmp, tmp);
        }
        return rc;
 }
@@ -4165,7 +4441,7 @@ qeth_arp_flush_cache(struct qeth_card *card)
         * funcs flags); since all zeros is no valueable information,
         * we say EOPNOTSUPP for all ARP functions
         */
-       if (card->info.guestlan)
+       if (card->info.guestlan || (card->info.type == QETH_CARD_TYPE_IQD))
                return -EOPNOTSUPP;
        if (!qeth_is_supported(card,IPA_ARP_PROCESSING)) {
                PRINT_WARN("ARP processing not supported "
@@ -4176,9 +4452,9 @@ qeth_arp_flush_cache(struct qeth_card *card)
                                          IPA_CMD_ASS_ARP_FLUSH_CACHE, 0);
        if (rc){
                tmp = rc;
-               PRINT_WARN("Could not flush ARP cache on %s: %s (0x%x)\n",
+               PRINT_WARN("Could not flush ARP cache on %s: %s (0x%x/%d)\n",
                           card->info.if_name, qeth_arp_get_error_cause(&rc),
-                          tmp);
+                          tmp, tmp);
        }
        return rc;
 }
@@ -4198,7 +4474,6 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                return -ENODEV;
 
        switch (cmd){
-       case SIOCDEVPRIVATE:
        case SIOC_QETH_ARP_SET_NO_ENTRIES:
                if (!capable(CAP_NET_ADMIN)){
                        rc = -EPERM;
@@ -4206,7 +4481,6 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                }
                rc = qeth_arp_set_no_entries(card, rq->ifr_ifru.ifru_ivalue);
                break;
-       case SIOCDEVPRIVATE+1:
        case SIOC_QETH_ARP_QUERY_INFO:
                if (!capable(CAP_NET_ADMIN)){
                        rc = -EPERM;
@@ -4214,7 +4488,6 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                }
                rc = qeth_arp_query(card, rq->ifr_ifru.ifru_data);
                break;
-       case SIOCDEVPRIVATE+2:
        case SIOC_QETH_ARP_ADD_ENTRY:
                if (!capable(CAP_NET_ADMIN)){
                        rc = -EPERM;
@@ -4226,7 +4499,6 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                else
                        rc = qeth_arp_add_entry(card, &arp_entry);
                break;
-       case SIOCDEVPRIVATE+3:
        case SIOC_QETH_ARP_REMOVE_ENTRY:
                if (!capable(CAP_NET_ADMIN)){
                        rc = -EPERM;
@@ -4238,7 +4510,6 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                else
                        rc = qeth_arp_remove_entry(card, &arp_entry);
                break;
-       case SIOCDEVPRIVATE+4:
        case SIOC_QETH_ARP_FLUSH_CACHE:
                if (!capable(CAP_NET_ADMIN)){
                        rc = -EPERM;
@@ -4246,18 +4517,21 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                }
                rc = qeth_arp_flush_cache(card);
                break;
-       case SIOCDEVPRIVATE+5:
        case SIOC_QETH_ADP_SET_SNMP_CONTROL:
+               rc = qeth_snmp_command(card, rq->ifr_ifru.ifru_data);
                break;
-       case SIOCDEVPRIVATE+6:
        case SIOC_QETH_GET_CARD_TYPE:
+               if ((card->info.type == QETH_CARD_TYPE_OSAE) &&
+                   !card->info.guestlan)
+                       return 1;
+               return 0;
                break;
        case SIOCGMIIPHY:
-               mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data;
+               mii_data = if_mii(rq);
                mii_data->phy_id = 0;
                break;
        case SIOCGMIIREG:
-               mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data;
+               mii_data = if_mii(rq);
                if (mii_data->phy_id != 0)
                        rc = -EINVAL;
                else
@@ -4272,7 +4546,7 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
                        rc = -EPERM;
                        break;
                }
-               mii_data = (struct mii_ioctl_data *) &rq->ifr_ifru.ifru_data;
+               mii_data = if_mii(rq);
                if (mii_data->phy_id != 0)
                        rc = -EINVAL;
                else
@@ -4282,6 +4556,8 @@ qeth_do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
        default:
                rc = -EOPNOTSUPP;
        }
+       if (rc)
+               QETH_DBF_TEXT_(trace, 2, "ioce%d", rc);
        return rc;
 }
 
@@ -4325,27 +4601,124 @@ static void
 qeth_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
 {
        struct qeth_card *card;
+       unsigned long flags;
 
        QETH_DBF_TEXT(trace,4,"vlanreg");
 
        card = (struct qeth_card *) dev->priv;
-       spin_lock_irq(&card->vlanlock);
+       spin_lock_irqsave(&card->vlanlock, flags);
        card->vlangrp = grp;
-       spin_unlock_irq(&card->vlanlock);
+       spin_unlock_irqrestore(&card->vlanlock, flags);
+}
+
+static inline void
+qeth_free_vlan_buffer(struct qeth_card *card, struct qeth_qdio_out_buffer *buf,
+                     unsigned short vid)
+{
+       int i;
+       struct sk_buff *skb;
+       struct sk_buff_head tmp_list;
+
+       skb_queue_head_init(&tmp_list);
+       for(i = 0; i < QETH_MAX_BUFFER_ELEMENTS(card); ++i){
+               while ((skb = skb_dequeue(&buf->skb_list))){
+                       if (vlan_tx_tag_present(skb) &&
+                           (vlan_tx_tag_get(skb) == vid)) {
+                               atomic_dec(&skb->users);
+                               dev_kfree_skb(skb);
+                       } else
+                               skb_queue_tail(&tmp_list, skb);
+               }
+       }
+       while ((skb = skb_dequeue(&tmp_list)))
+               skb_queue_tail(&buf->skb_list, skb);
+}
+
+static void
+qeth_free_vlan_skbs(struct qeth_card *card, unsigned short vid)
+{
+       int i, j;
+
+       QETH_DBF_TEXT(trace, 4, "frvlskbs");
+       for (i = 0; i < card->qdio.no_out_queues; ++i){
+               for (j = 0; j < QDIO_MAX_BUFFERS_PER_Q; ++j)
+                       qeth_free_vlan_buffer(card, &card->qdio.
+                                             out_qs[i]->bufs[j], vid);
+       }
+}
+
+static void
+qeth_free_vlan_addresses4(struct qeth_card *card, unsigned short vid)
+{
+       struct in_device *in_dev;
+       struct in_ifaddr *ifa;
+       struct qeth_ipaddr *addr;
+
+       QETH_DBF_TEXT(trace, 4, "frvaddr4");
+       if (!card->vlangrp)
+               return;
+       in_dev = in_dev_get(card->vlangrp->vlan_devices[vid]);
+       if (!in_dev)
+               return;
+       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;
+                       addr->u.a4.mask = ifa->ifa_mask;
+                       addr->type = QETH_IP_TYPE_NORMAL;
+                       if (!qeth_delete_ip(card, addr))
+                               kfree(addr);
+               }
+       }
+       in_dev_put(in_dev);
+}
+
+static void
+qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid)
+{
+       struct inet6_dev *in6_dev;
+       struct inet6_ifaddr *ifa;
+       struct qeth_ipaddr *addr;
+
+       QETH_DBF_TEXT(trace, 4, "frvaddr6");
+       if (!card->vlangrp)
+               return;
+       in6_dev = in6_dev_get(card->vlangrp->vlan_devices[vid]);
+       if (!in6_dev)
+               return;
+       for (ifa = in6_dev->addr_list; ifa; ifa = ifa->lst_next){
+               addr = qeth_get_addr_buffer(QETH_PROT_IPV6);
+               if (addr){
+                       memcpy(&addr->u.a6.addr, &ifa->addr,
+                              sizeof(struct in6_addr));
+                       addr->u.a6.pfxlen = ifa->prefix_len;
+                       addr->type = QETH_IP_TYPE_NORMAL;
+                       if (!qeth_delete_ip(card, addr))
+                               kfree(addr);
+               }
+       }
+       in6_dev_put(in6_dev);
 }
 
 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");
 
        card = (struct qeth_card *) dev->priv;
-       spin_lock_irq(&card->vlanlock);
+       /* free all skbs for the vlan device */
+       qeth_free_vlan_skbs(card, vid);
+       spin_lock_irqsave(&card->vlanlock, flags);
+       /* unregister IP addresses of vlan device */
+       qeth_free_vlan_addresses4(card, vid);
+       qeth_free_vlan_addresses6(card, vid);
        if (card->vlangrp)
                card->vlangrp->vlan_devices[vid] = NULL;
-       spin_unlock_irq(&card->vlanlock);
+       spin_unlock_irqrestore(&card->vlanlock, flags);
+       qeth_set_thread_start_bit(card, QETH_SET_IP_THREAD);
        /* delete mc addresses for this vlan dev */
        qeth_set_thread_start_bit(card, QETH_SET_MC_THREAD);
        schedule_work(&card->kernel_thread_starter);
@@ -4401,10 +4774,6 @@ qeth_get_addr_buffer(enum qeth_prot_versions prot)
        memset(addr,0,sizeof(struct qeth_ipaddr));
        addr->type = QETH_IP_TYPE_NORMAL;
        addr->proto = prot;
-       addr->is_multicast = 0;
-       addr->users = 0;
-       addr->set_flags = 0;
-       addr->del_flags = 0;
        return addr;
 }
 
@@ -4811,6 +5180,26 @@ qeth_netdev_init(struct net_device *dev)
        return 0;
 }
 
+static void
+qeth_init_func_level(struct qeth_card *card)
+{
+       if (card->ipato.enabled) {
+               if (card->info.type == QETH_CARD_TYPE_IQD)
+                               card->info.func_level =
+                                       QETH_IDX_FUNC_LEVEL_IQD_ENA_IPAT;
+               else
+                               card->info.func_level =
+                                       QETH_IDX_FUNC_LEVEL_OSAE_ENA_IPAT;
+       } else {
+               if (card->info.type == QETH_CARD_TYPE_IQD)
+                       card->info.func_level =
+                               QETH_IDX_FUNC_LEVEL_IQD_DIS_IPAT;
+               else
+                       card->info.func_level =
+                               QETH_IDX_FUNC_LEVEL_OSAE_DIS_IPAT;
+       }
+}
+
 /**
  * hardsetup card, initialize MPC and QDIO stuff
  */
@@ -4848,6 +5237,7 @@ retry:
                return rc;
        }
        qeth_init_tokens(card);
+       qeth_init_func_level(card);
        rc = qeth_idx_activate_channel(&card->read, qeth_idx_read_cb);
        if (rc == -ERESTARTSYS) {
                QETH_DBF_TEXT(setup, 2, "break2");
@@ -4885,7 +5275,6 @@ retry:
                        QETH_DBF_TEXT_(setup, 2, "6err%d", rc);
                        goto out;
                }
-               qeth_set_device_name(card);
                card->dev->priv = card;
                card->dev->type = qeth_get_arphdr_type(card->info.type,
                                                       card->info.link_type);
@@ -4897,24 +5286,6 @@ out:
        return rc;
 }
 
-static struct qeth_cmd_buffer *
-qeth_get_adapter_cmd(struct qeth_card *card, __u32 command)
-{
-       struct qeth_cmd_buffer *iob;
-       struct qeth_ipa_cmd *cmd;
-
-       iob = qeth_get_ipacmd_buffer(card,IPA_CMD_SETADAPTERPARMS,
-                                    QETH_PROT_IPV4);
-       cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
-       cmd->data.setadapterparms.cmdlength =
-                               sizeof(struct qeth_ipacmd_setadpparms);
-       cmd->data.setadapterparms.command_code = command;
-       cmd->data.setadapterparms.frames_used_total = 1;
-       cmd->data.setadapterparms.frame_seq_no = 1;
-
-       return iob;
-}
-
 static int
 qeth_default_setassparms_cb(struct qeth_card *card, struct qeth_reply *reply,
                            unsigned long data)
@@ -4952,7 +5323,7 @@ qeth_default_setadapterparms_cb(struct qeth_card *card,
 
        cmd = (struct qeth_ipa_cmd *) data;
        if (cmd->hdr.return_code == 0)
-               cmd->hdr.return_code = cmd->data.setadapterparms.return_code;
+               cmd->hdr.return_code = cmd->data.setadapterparms.hdr.return_code;
        return 0;
 }
 
@@ -4980,7 +5351,8 @@ qeth_query_setadapterparms(struct qeth_card *card)
        struct qeth_cmd_buffer *iob;
 
        QETH_DBF_TEXT(trace,3,"queryadp");
-       iob = qeth_get_adapter_cmd(card,IPA_SETADP_QUERY_COMMANDS_SUPPORTED);
+       iob = qeth_get_adapter_cmd(card, IPA_SETADP_QUERY_COMMANDS_SUPPORTED,
+                                  sizeof(struct qeth_ipacmd_setadpparms));
        rc = qeth_send_ipa_cmd(card, iob, qeth_query_setadapterparms_cb, NULL);
        return rc;
 }
@@ -5010,7 +5382,8 @@ qeth_setadpparms_change_macaddr(struct qeth_card *card)
 
        QETH_DBF_TEXT(trace,4,"chgmac");
 
-       iob = qeth_get_adapter_cmd(card,IPA_SETADP_ALTER_MAC_ADDRESS);
+       iob = qeth_get_adapter_cmd(card,IPA_SETADP_ALTER_MAC_ADDRESS,
+                                  sizeof(struct qeth_ipacmd_setadpparms));
        cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
        cmd->data.setadapterparms.data.change_addr.cmd = CHANGE_ADDR_READ_MAC;
        cmd->data.setadapterparms.data.change_addr.addr_size = OSA_ADDR_LEN;
@@ -5030,7 +5403,8 @@ qeth_send_setadp_mode(struct qeth_card *card, __u32 command, __u32 mode)
 
        QETH_DBF_TEXT(trace,4,"adpmode");
 
-       iob = qeth_get_adapter_cmd(card, command);
+       iob = qeth_get_adapter_cmd(card, command,
+                                  sizeof(struct qeth_ipacmd_setadpparms));
        cmd = (struct qeth_ipa_cmd *)(iob->data+IPA_PDU_HEADER_SIZE);
        cmd->data.setadapterparms.data.mode = mode;
        rc = qeth_send_ipa_cmd(card, iob, qeth_default_setadapterparms_cb,
@@ -5459,18 +5833,20 @@ qeth_start_ipa_broadcast(struct qeth_card *card)
        int rc;
 
        QETH_DBF_TEXT(trace,3,"stbrdcst");
+       card->info.broadcast_capable = 0;
        if (!qeth_is_supported(card, IPA_FILTERING)) {
                PRINT_WARN("Broadcast not supported on %s\n",
                           card->info.if_name);
-               return -EOPNOTSUPP;
+               rc = -EOPNOTSUPP;
+               goto out;
        }
        rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
                                          IPA_CMD_ASS_START, 0);
        if (rc) {
-               PRINT_WARN("Could not enable broadcasting "
+               PRINT_WARN("Could not enable broadcasting filtering "
                           "on %s: 0x%x\n",
                           card->info.if_name, rc);
-               return rc;
+               goto out;
        }
 
        rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
@@ -5478,12 +5854,24 @@ qeth_start_ipa_broadcast(struct qeth_card *card)
        if (rc) {
                PRINT_WARN("Could not set up broadcast filtering on %s: 0x%x\n",
                           card->info.if_name, rc);
-               return rc;
+               goto out;
        }
+       card->info.broadcast_capable = QETH_BROADCAST_WITH_ECHO;
        PRINT_INFO("Broadcast enabled \n");
-       card->dev->flags |= IFF_BROADCAST;
-       card->info.broadcast_capable = 1;
-       return 0;
+       rc = qeth_send_simple_setassparms(card, IPA_FILTERING,
+                                         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);
+               goto out;
+       }
+       card->info.broadcast_capable = QETH_BROADCAST_WITHOUT_ECHO;
+out:
+       if (card->info.broadcast_capable)
+               card->dev->flags |= IFF_BROADCAST;
+       else
+               card->dev->flags &= ~IFF_BROADCAST;
+       return rc;
 }
 
 static int
@@ -5781,7 +6169,6 @@ static void
 qeth_clear_ip_list(struct qeth_card *card, int clean, int recover)
 {
        struct qeth_ipaddr *addr, *tmp;
-       int first_run = 1;
        unsigned long flags;
 
        QETH_DBF_TEXT(trace,4,"clearip");
@@ -5791,29 +6178,21 @@ qeth_clear_ip_list(struct qeth_card *card, int clean, int recover)
                list_del(&addr->entry);
                kfree(addr);
        }
-again:
-       if (first_run)
-               first_run = 0;
-       else
-               spin_lock_irqsave(&card->ip_lock, flags);
 
-       list_for_each_entry_safe(addr, tmp, &card->ip_list, entry) {
+       while (!list_empty(&card->ip_list)) {
+               addr = list_entry(card->ip_list.next,
+                                 struct qeth_ipaddr, entry);
                list_del_init(&addr->entry);
-               if (clean){
+               if (clean) {
                        spin_unlock_irqrestore(&card->ip_lock, flags);
                        qeth_deregister_addr_entry(card, addr);
+                       spin_lock_irqsave(&card->ip_lock, flags);
                }
-               if (!recover || addr->is_multicast)
+               if (!recover || addr->is_multicast) {
                        kfree(addr);
-               else {
-                       if (clean)
-                               spin_lock_irqsave(&card->ip_lock, flags);
-                       list_add_tail(&addr->entry, &card->ip_tbd_list);
-                       if (clean) {
-                               spin_unlock_irqrestore(&card->ip_lock, flags);
-                               goto again;
-                       }
+                       continue;
                }
+               list_add_tail(&addr->entry, &card->ip_tbd_list);
        }
        spin_unlock_irqrestore(&card->ip_lock, flags);
 }
@@ -6109,7 +6488,7 @@ qeth_set_online(struct ccwgroup_device *gdev)
        qeth_set_allowed_threads(card, 0xffffffff, 0);
        if (recover_flag == CARD_STATE_RECOVER)
                qeth_start_again(card);
-
+       qeth_notify_processes();
        return 0;
 out_remove:
        card->use_hard_stop = 1;
@@ -6775,12 +7154,18 @@ qeth_init(void)
        int rc=0;
 
        qeth_eyecatcher();
-       printk(KERN_INFO "qeth: loading %s\n",version);
+       PRINT_INFO("loading %s (%s/%s/%s/%s/%s/%s/%s %s %s)\n",
+                  version, VERSION_QETH_C, VERSION_QETH_H,
+                  VERSION_QETH_MPC_H, VERSION_QETH_MPC_C,
+                  VERSION_QETH_FS_H, VERSION_QETH_PROC_C,
+                  VERSION_QETH_SYS_C, QETH_VERSION_IPV6,
+                  QETH_VERSION_VLAN);
 
        INIT_LIST_HEAD(&qeth_card_list.list);
+       INIT_LIST_HEAD(&qeth_notify_list);
+       spin_lock_init(&qeth_notify_lock);
        rwlock_init(&qeth_card_list.rwlock);
 
-       atomic_set(&qeth_hsi_count, 0);
        if (qeth_register_dbf_views())
                goto out_err;
        if (qeth_sysfs_register())