fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / net / arcnet / arcnet.c
index 57c1b7d..4e91dab 100644 (file)
@@ -44,7 +44,6 @@
 #define VERSION "arcnet: v3.93 BETA 2000/04/29 - by Avery Pennarun et al.\n"
 
 #include <linux/module.h>
-#include <linux/config.h>
 #include <linux/types.h>
 #include <linux/delay.h>
 #include <linux/netdevice.h>
@@ -52,7 +51,7 @@
 #include <net/arp.h>
 #include <linux/init.h>
 #include <linux/arcdevice.h>
-
+#include <linux/jiffies.h>
 
 /* "do nothing" functions for protocol drivers */
 static void null_rx(struct net_device *dev, int bufnum,
@@ -62,6 +61,7 @@ static int null_build_header(struct sk_buff *skb, struct net_device *dev,
 static int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
                           int length, int bufnum);
 
+static void arcnet_rx(struct net_device *dev, int bufnum);
 
 /*
  * one ArcProto per possible proto ID.  None of the elements of
@@ -69,29 +69,30 @@ static int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
  * arc_proto_default instead.  It also must not be NULL; if you would like
  * to set it to NULL, set it to &arc_proto_null instead.
  */
-struct ArcProto *arc_proto_map[256], *arc_proto_default, *arc_bcast_proto;
+ struct ArcProto *arc_proto_map[256], *arc_proto_default,
+   *arc_bcast_proto, *arc_raw_proto;
 
-struct ArcProto arc_proto_null =
+static struct ArcProto arc_proto_null =
 {
        .suffix         = '?',
        .mtu            = XMTU,
+       .is_ip          = 0,
        .rx             = null_rx,
        .build_header   = null_build_header,
        .prepare_tx     = null_prepare_tx,
+       .continue_tx    = NULL,
+       .ack_tx         = NULL
 };
 
-static spinlock_t arcnet_lock = SPIN_LOCK_UNLOCKED;
-
 /* Exported function prototypes */
 int arcnet_debug = ARCNET_DEBUG;
 
 EXPORT_SYMBOL(arc_proto_map);
 EXPORT_SYMBOL(arc_proto_default);
 EXPORT_SYMBOL(arc_bcast_proto);
-EXPORT_SYMBOL(arc_proto_null);
+EXPORT_SYMBOL(arc_raw_proto);
 EXPORT_SYMBOL(arcnet_unregister_proto);
 EXPORT_SYMBOL(arcnet_debug);
-EXPORT_SYMBOL(arcdev_setup);
 EXPORT_SYMBOL(alloc_arcdev);
 EXPORT_SYMBOL(arcnet_interrupt);
 
@@ -108,7 +109,7 @@ static struct net_device_stats *arcnet_get_stats(struct net_device *dev);
 static int go_tx(struct net_device *dev);
 
 static int debug = ARCNET_DEBUG;
-MODULE_PARM(debug, "i");
+module_param(debug, int, 0);
 MODULE_LICENSE("GPL");
 
 static int __init arcnet_init(void)
@@ -117,7 +118,7 @@ static int __init arcnet_init(void)
 
        arcnet_debug = debug;
 
-       printk(VERSION);
+       printk("arcnet loaded.\n");
 
 #ifdef ALPHA_WARNING
        BUGLVL(D_EXTRA) {
@@ -131,7 +132,7 @@ static int __init arcnet_init(void)
 #endif
 
        /* initialize the protocol map */
-       arc_proto_default = arc_bcast_proto = &arc_proto_null;
+       arc_raw_proto = arc_proto_default = arc_bcast_proto = &arc_proto_null;
        for (count = 0; count < 256; count++)
                arc_proto_map[count] = arc_proto_default;
 
@@ -155,7 +156,8 @@ module_exit(arcnet_exit);
  * Dump the contents of an sk_buff
  */
 #if ARCNET_DEBUG_MAX & D_SKB
-void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc)
+void arcnet_dump_skb(struct net_device *dev,
+                    struct sk_buff *skb, char *desc)
 {
        int i;
 
@@ -176,18 +178,22 @@ EXPORT_SYMBOL(arcnet_dump_skb);
  * Dump the contents of an ARCnet buffer
  */
 #if (ARCNET_DEBUG_MAX & (D_RX | D_TX))
-void arcnet_dump_packet(struct net_device *dev, int bufnum, char *desc)
+static void arcnet_dump_packet(struct net_device *dev, int bufnum,
+                              char *desc, int take_arcnet_lock)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
        int i, length;
-       unsigned long flags;
+       unsigned long flags = 0;
        static uint8_t buf[512];
 
        /* hw.copy_from_card expects IRQ context so take the IRQ lock
           to keep it single threaded */
-       spin_lock_irqsave(&arcnet_lock, flags);
+       if(take_arcnet_lock)
+               spin_lock_irqsave(&lp->lock, flags);
+
        lp->hw.copy_from_card(dev, bufnum, 0, buf, 512);
-       spin_unlock_irqrestore(&arcnet_lock, flags);
+       if(take_arcnet_lock)
+               spin_unlock_irqrestore(&lp->lock, flags);
 
        /* if the offset[0] byte is nonzero, this is a 256-byte packet */
        length = (buf[2] ? 256 : 512);
@@ -202,7 +208,10 @@ void arcnet_dump_packet(struct net_device *dev, int bufnum, char *desc)
 
 }
 
-EXPORT_SYMBOL(arcnet_dump_packet);
+#else
+
+#define arcnet_dump_packet(dev, bufnum, desc,take_arcnet_lock) do { } while (0)
+
 #endif
 
 
@@ -219,6 +228,8 @@ void arcnet_unregister_proto(struct ArcProto *proto)
                arc_proto_default = &arc_proto_null;
        if (arc_bcast_proto == proto)
                arc_bcast_proto = arc_proto_default;
+       if (arc_raw_proto == proto)
+               arc_raw_proto = arc_proto_default;
 
        for (count = 0; count < 256; count++) {
                if (arc_proto_map[count] == proto)
@@ -236,7 +247,7 @@ void arcnet_unregister_proto(struct ArcProto *proto)
  */
 static void release_arcbuf(struct net_device *dev, int bufnum)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
        int i;
 
        lp->buf_queue[lp->first_free_buf++] = bufnum;
@@ -245,7 +256,7 @@ static void release_arcbuf(struct net_device *dev, int bufnum)
        BUGLVL(D_DURING) {
                BUGMSG(D_DURING, "release_arcbuf: freed #%d; buffer queue is now: ",
                       bufnum);
-               for (i = lp->next_buf; i != lp->first_free_buf; i = ++i % 5)
+               for (i = lp->next_buf; i != lp->first_free_buf; i = (i+1) % 5)
                        BUGMSG2(D_DURING, "#%d ", lp->buf_queue[i]);
                BUGMSG2(D_DURING, "\n");
        }
@@ -258,11 +269,14 @@ static void release_arcbuf(struct net_device *dev, int bufnum)
  */
 static int get_arcbuf(struct net_device *dev)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
        int buf = -1, i;
 
-       if (!atomic_dec_and_test(&lp->buf_lock))        /* already in this function */
-               BUGMSG(D_NORMAL, "get_arcbuf: overlap (%d)!\n", lp->buf_lock.counter);
+       if (!atomic_dec_and_test(&lp->buf_lock)) {
+               /* already in this function */
+               BUGMSG(D_NORMAL, "get_arcbuf: overlap (%d)!\n",
+                      lp->buf_lock.counter);
+       }
        else {                  /* we can continue */
                if (lp->next_buf >= 5)
                        lp->next_buf -= 5;
@@ -278,7 +292,7 @@ static int get_arcbuf(struct net_device *dev)
 
        BUGLVL(D_DURING) {
                BUGMSG(D_DURING, "get_arcbuf: got #%d; buffer queue is now: ", buf);
-               for (i = lp->next_buf; i != lp->first_free_buf; i = ++i % 5)
+               for (i = lp->next_buf; i != lp->first_free_buf; i = (i+1) % 5)
                        BUGMSG2(D_DURING, "#%d ", lp->buf_queue[i]);
                BUGMSG2(D_DURING, "\n");
        }
@@ -305,14 +319,14 @@ static int choose_mtu(void)
 
 
 /* Setup a struct device for ARCnet. */
-void arcdev_setup(struct net_device *dev)
+static void arcdev_setup(struct net_device *dev)
 {
        dev->type = ARPHRD_ARCNET;
        dev->hard_header_len = sizeof(struct archdr);
        dev->mtu = choose_mtu();
 
        dev->addr_len = ARCNET_ALEN;
-       dev->tx_queue_len = 30;
+       dev->tx_queue_len = 100;
        dev->broadcast[0] = 0x00;       /* for us, broadcasts are address 0 */
        dev->watchdog_timeo = TX_TIMEOUT;
 
@@ -334,8 +348,16 @@ void arcdev_setup(struct net_device *dev)
 
 struct net_device *alloc_arcdev(char *name)
 {
-       return alloc_netdev(sizeof(struct arcnet_local),
-                           name && *name ? name : "arc%d", arcdev_setup);
+       struct net_device *dev;
+
+       dev = alloc_netdev(sizeof(struct arcnet_local),
+                          name && *name ? name : "arc%d", arcdev_setup);
+       if(dev) {
+               struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+               spin_lock_init(&lp->lock);
+       }
+
+       return dev;
 }
 
 /*
@@ -348,9 +370,11 @@ struct net_device *alloc_arcdev(char *name)
  */
 static int arcnet_open(struct net_device *dev)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
        int count, newmtu, error;
 
+       BUGMSG(D_INIT,"opened.");
+
        if (!try_module_get(lp->hw.owner))
                return -ENODEV;
 
@@ -377,6 +401,8 @@ static int arcnet_open(struct net_device *dev)
        if (newmtu < dev->mtu)
                dev->mtu = newmtu;
 
+       BUGMSG(D_INIT, "arcnet_open: mtu: %d.\n", dev->mtu);
+
        /* autodetect the encapsulation for each host. */
        memset(lp->default_proto, 0, sizeof(lp->default_proto));
 
@@ -390,6 +416,7 @@ static int arcnet_open(struct net_device *dev)
 
        /* initialize buffers */
        atomic_set(&lp->buf_lock, 1);
+
        lp->next_buf = lp->first_free_buf = 0;
        release_arcbuf(dev, 0);
        release_arcbuf(dev, 1);
@@ -401,7 +428,8 @@ static int arcnet_open(struct net_device *dev)
        lp->rfc1201.sequence = 1;
 
        /* bring up the hardware driver */
-       lp->hw.open(dev);
+       if (lp->hw.open)
+               lp->hw.open(dev);
 
        if (dev->dev_addr[0] == 0)
                BUGMSG(D_NORMAL, "WARNING!  Station address 00 is reserved "
@@ -410,17 +438,24 @@ static int arcnet_open(struct net_device *dev)
                BUGMSG(D_NORMAL, "WARNING!  Station address FF may confuse "
                       "DOS networking programs!\n");
 
-       if (ASTATUS() & RESETflag)
+       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
+       if (ASTATUS() & RESETflag) {
+               BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
                ACOMMAND(CFLAGScmd | RESETclear);
+       }
+
 
+       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
        /* make sure we're ready to receive IRQ's. */
        AINTMASK(0);
        udelay(1);              /* give it time to set the mask before
                                 * we reset it again. (may not even be
                                 * necessary)
                                 */
+       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
        lp->intmask = NORXflag | RECONflag;
        AINTMASK(lp->intmask);
+       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
 
        netif_start_queue(dev);
 
@@ -435,7 +470,7 @@ static int arcnet_open(struct net_device *dev)
 /* The inverse routine to arcnet_open - shuts down the card. */
 static int arcnet_close(struct net_device *dev)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
 
        netif_stop_queue(dev);
 
@@ -456,7 +491,7 @@ static int arcnet_header(struct sk_buff *skb, struct net_device *dev,
                         unsigned short type, void *daddr, void *saddr,
                         unsigned len)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
        uint8_t _daddr, proto_num;
        struct ArcProto *proto;
 
@@ -466,32 +501,41 @@ static int arcnet_header(struct sk_buff *skb, struct net_device *dev,
               daddr ? *(uint8_t *) daddr : -1,
               type, type, len);
 
-       if (len != skb->len)
+       if (skb->len!=0 && len != skb->len)
                BUGMSG(D_NORMAL, "arcnet_header: Yikes!  skb->len(%d) != len(%d)!\n",
                       skb->len, len);
 
-       /*
-        * if the dest addr isn't provided, we can't choose an encapsulation!
-        * Store the packet type (eg. ETH_P_IP) for now, and we'll push on a
-        * real header when we do rebuild_header. 
-        */
-       if (!daddr) {
+
+       /* Type is host order - ? */
+       if(type == ETH_P_ARCNET) {
+               proto = arc_raw_proto;
+               BUGMSG(D_DEBUG, "arc_raw_proto used. proto='%c'\n",proto->suffix);
+               _daddr = daddr ? *(uint8_t *) daddr : 0;
+       }
+       else if (!daddr) {
+               /*
+                * if the dest addr isn't provided, we can't choose an encapsulation!
+                * Store the packet type (eg. ETH_P_IP) for now, and we'll push on a
+                * real header when we do rebuild_header.
+                */
                *(uint16_t *) skb_push(skb, 2) = type;
                if (skb->nh.raw - skb->mac.raw != 2)
                        BUGMSG(D_NORMAL, "arcnet_header: Yikes!  diff (%d) is not 2!\n",
-                              skb->nh.raw - skb->mac.raw);
+                              (int)(skb->nh.raw - skb->mac.raw));
                return -2;      /* return error -- can't transmit yet! */
        }
-       /* otherwise, we can just add the header as usual. */
-       _daddr = *(uint8_t *) daddr;
-       proto_num = lp->default_proto[_daddr];
-       proto = arc_proto_map[proto_num];
-       BUGMSG(D_DURING, "building header for %02Xh using protocol '%c'\n",
-              proto_num, proto->suffix);
-       if (proto == &arc_proto_null && arc_bcast_proto != proto) {
-               BUGMSG(D_DURING, "actually, let's use '%c' instead.\n",
-                      arc_bcast_proto->suffix);
-               proto = arc_bcast_proto;
+       else {
+               /* otherwise, we can just add the header as usual. */
+               _daddr = *(uint8_t *) daddr;
+               proto_num = lp->default_proto[_daddr];
+               proto = arc_proto_map[proto_num];
+               BUGMSG(D_DURING, "building header for %02Xh using protocol '%c'\n",
+                      proto_num, proto->suffix);
+               if (proto == &arc_proto_null && arc_bcast_proto != proto) {
+                       BUGMSG(D_DURING, "actually, let's use '%c' instead.\n",
+                              arc_bcast_proto->suffix);
+                       proto = arc_bcast_proto;
+               }
        }
        return proto->build_header(skb, dev, type, _daddr);
 }
@@ -505,7 +549,7 @@ static int arcnet_header(struct sk_buff *skb, struct net_device *dev,
 static int arcnet_rebuild_header(struct sk_buff *skb)
 {
        struct net_device *dev = skb->dev;
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
        int status = 0;         /* default is failure */
        unsigned short type;
        uint8_t daddr=0;
@@ -514,10 +558,11 @@ static int arcnet_rebuild_header(struct sk_buff *skb)
        if (skb->nh.raw - skb->mac.raw != 2) {
                BUGMSG(D_NORMAL,
                     "rebuild_header: shouldn't be here! (hdrsize=%d)\n",
-                      skb->nh.raw - skb->mac.raw);
+                    (int)(skb->nh.raw - skb->mac.raw));
                return 0;
        }
        type = *(uint16_t *) skb_pull(skb, 2);
+       BUGMSG(D_DURING, "rebuild header for protocol %Xh\n", type);
 
        if (type == ETH_P_IP) {
 #ifdef CONFIG_INET
@@ -549,15 +594,17 @@ static int arcnet_rebuild_header(struct sk_buff *skb)
 /* Called by the kernel in order to transmit a packet. */
 static int arcnet_send_packet(struct sk_buff *skb, struct net_device *dev)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
        struct archdr *pkt;
        struct arc_rfc1201 *soft;
        struct ArcProto *proto;
        int txbuf;
+       unsigned long flags;
+       int freeskb, retval;
 
        BUGMSG(D_DURING,
-              "transmit requested (status=%Xh, txbufs=%d/%d, len=%d)\n",
-              ASTATUS(), lp->cur_tx, lp->next_tx, skb->len);
+              "transmit requested (status=%Xh, txbufs=%d/%d, len=%d, protocol %x)\n",
+              ASTATUS(), lp->cur_tx, lp->next_tx, skb->len,skb->protocol);
 
        pkt = (struct archdr *) skb->data;
        soft = &pkt->soft.rfc1201;
@@ -571,45 +618,63 @@ static int arcnet_send_packet(struct sk_buff *skb, struct net_device *dev)
        if (skb->len - ARC_HDR_SIZE > XMTU && !proto->continue_tx) {
                BUGMSG(D_NORMAL, "fixme: packet too large: compensating badly!\n");
                dev_kfree_skb(skb);
-               return 0;       /* don't try again */
+               return NETDEV_TX_OK;    /* don't try again */
        }
 
        /* We're busy transmitting a packet... */
        netif_stop_queue(dev);
 
+       spin_lock_irqsave(&lp->lock, flags);
        AINTMASK(0);
-
-       txbuf = get_arcbuf(dev);
+       if(lp->next_tx == -1)
+               txbuf = get_arcbuf(dev);
+       else {
+               txbuf = -1;
+       }
        if (txbuf != -1) {
-               if (proto->prepare_tx(dev, pkt, skb->len, txbuf)) {
-                       /* done right away */
+               if (proto->prepare_tx(dev, pkt, skb->len, txbuf) &&
+                   !proto->ack_tx) {
+                       /* done right away and we don't want to acknowledge
+                          the package later - forget about it now */
                        lp->stats.tx_bytes += skb->len;
-                       dev_kfree_skb(skb);
+                       freeskb = 1;
                } else {
                        /* do it the 'split' way */
                        lp->outgoing.proto = proto;
                        lp->outgoing.skb = skb;
                        lp->outgoing.pkt = pkt;
 
-                       if (!proto->continue_tx)
-                               BUGMSG(D_NORMAL, "bug! prep_tx==0, but no continue_tx!\n");
-                       else if (proto->continue_tx(dev, txbuf)) {
-                               BUGMSG(D_NORMAL,
-                                      "bug! continue_tx finished the first time! "
-                                      "(proto='%c')\n", proto->suffix);
+                       freeskb = 0;
+
+                       if (proto->continue_tx &&
+                           proto->continue_tx(dev, txbuf)) {
+                         BUGMSG(D_NORMAL,
+                                "bug! continue_tx finished the first time! "
+                                "(proto='%c')\n", proto->suffix);
                        }
                }
-
+               retval = NETDEV_TX_OK;
+               dev->trans_start = jiffies;
                lp->next_tx = txbuf;
-       } else
-               dev_kfree_skb(skb);
+       } else {
+               retval = NETDEV_TX_BUSY;
+               freeskb = 0;
+       }
 
+       BUGMSG(D_DEBUG, "%s: %d: %s, status: %x\n",__FILE__,__LINE__,__FUNCTION__,ASTATUS());
        /* make sure we didn't ignore a TX IRQ while we were in here */
        AINTMASK(0);
-       lp->intmask |= TXFREEflag;
+
+       BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
+       lp->intmask |= TXFREEflag|EXCNAKflag;
        AINTMASK(lp->intmask);
+       BUGMSG(D_DEBUG, "%s: %d: %s, status: %x\n",__FILE__,__LINE__,__FUNCTION__,ASTATUS());
 
-       return 0;               /* no need to try again */
+       spin_unlock_irqrestore(&lp->lock, flags);
+       if (freeskb) {
+               dev_kfree_skb(skb);
+       }
+       return retval;          /* no need to try again */
 }
 
 
@@ -619,7 +684,7 @@ static int arcnet_send_packet(struct sk_buff *skb, struct net_device *dev)
  */
 static int go_tx(struct net_device *dev)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
 
        BUGMSG(D_DURING, "go_tx: status=%Xh, intmask=%Xh, next_tx=%d, cur_tx=%d\n",
               ASTATUS(), lp->intmask, lp->next_tx, lp->cur_tx);
@@ -627,7 +692,7 @@ static int go_tx(struct net_device *dev)
        if (lp->cur_tx != -1 || lp->next_tx == -1)
                return 0;
 
-       BUGLVL(D_TX) arcnet_dump_packet(dev, lp->next_tx, "go_tx");
+       BUGLVL(D_TX) arcnet_dump_packet(dev, lp->next_tx, "go_tx", 0);
 
        lp->cur_tx = lp->next_tx;
        lp->next_tx = -1;
@@ -635,11 +700,11 @@ static int go_tx(struct net_device *dev)
        /* start sending */
        ACOMMAND(TXcmd | (lp->cur_tx << 3));
 
-       dev->trans_start = jiffies;
        lp->stats.tx_packets++;
        lp->lasttrans_dest = lp->lastload_dest;
        lp->lastload_dest = 0;
-       lp->intmask |= TXFREEflag;
+       lp->excnak_pending = 0;
+       lp->intmask |= TXFREEflag|EXCNAKflag;
 
        return 1;
 }
@@ -649,11 +714,11 @@ static int go_tx(struct net_device *dev)
 static void arcnet_timeout(struct net_device *dev)
 {
        unsigned long flags;
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
        int status = ASTATUS();
        char *msg;
 
-       spin_lock_irqsave(&arcnet_lock, flags);
+       spin_lock_irqsave(&lp->lock, flags);
        if (status & TXFREEflag) {      /* transmit _DID_ finish */
                msg = " - missed IRQ?";
        } else {
@@ -664,14 +729,14 @@ static void arcnet_timeout(struct net_device *dev)
        }
        lp->stats.tx_errors++;
 
-       /* make sure we didn't miss a TX IRQ */
+       /* make sure we didn't miss a TX or a EXC NAK IRQ */
        AINTMASK(0);
-       lp->intmask |= TXFREEflag;
+       lp->intmask |= TXFREEflag|EXCNAKflag;
        AINTMASK(lp->intmask);
        
-       spin_unlock_irqrestore(&arcnet_lock, flags);
+       spin_unlock_irqrestore(&lp->lock, flags);
 
-       if (jiffies - lp->last_timeout > 10*HZ) {
+       if (time_after(jiffies, lp->last_timeout + 10*HZ)) {
                BUGMSG(D_EXTRA, "tx timed out%s (status=%Xh, intmask=%Xh, dest=%02Xh)\n",
                       msg, status, lp->intmask, lp->lasttrans_dest);
                lp->last_timeout = jiffies;
@@ -687,22 +752,22 @@ static void arcnet_timeout(struct net_device *dev)
  * interrupts. Establish which device needs attention, and call the correct
  * chipset interrupt handler.
  */
-irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+irqreturn_t arcnet_interrupt(int irq, void *dev_id)
 {
        struct net_device *dev = dev_id;
        struct arcnet_local *lp;
-       int recbuf, status, didsomething, boguscount;
+       int recbuf, status, diagstatus, didsomething, boguscount;
+       int retval = IRQ_NONE;
 
        BUGMSG(D_DURING, "\n");
 
        BUGMSG(D_DURING, "in arcnet_interrupt\n");
-
-       spin_lock(&arcnet_lock);
        
-       lp = (struct arcnet_local *) dev->priv;
-       if (!lp)
-               BUG();
+       lp = dev->priv;
+       BUG_ON(!lp);
                
+       spin_lock(&lp->lock);
+
        /*
         * RESET flag was enabled - if device is not running, we must clear it right
         * away (but nothing else).
@@ -711,7 +776,7 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                if (ASTATUS() & RESETflag)
                        ACOMMAND(CFLAGScmd | RESETclear);
                AINTMASK(0);
-               spin_unlock(&arcnet_lock);
+               spin_unlock(&lp->lock);
                return IRQ_HANDLED;
        }
 
@@ -721,6 +786,10 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        boguscount = 5;
        do {
                status = ASTATUS();
+                diagstatus = (status >> 8) & 0xFF;
+
+               BUGMSG(D_DEBUG, "%s: %d: %s: status=%x\n",
+                       __FILE__,__LINE__,__FUNCTION__,status);
                didsomething = 0;
 
                /*
@@ -760,24 +829,55 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                        }
                        didsomething++;
                }
+
+               if((diagstatus & EXCNAKflag)) {
+                       BUGMSG(D_DURING, "EXCNAK IRQ (diagstat=%Xh)\n",
+                              diagstatus);
+
+                        ACOMMAND(NOTXcmd);      /* disable transmit */
+                        lp->excnak_pending = 1;
+
+                        ACOMMAND(EXCNAKclear);
+                       lp->intmask &= ~(EXCNAKflag);
+                        didsomething++;
+                }
+
+
                /* a transmit finished, and we're interested in it. */
                if ((status & lp->intmask & TXFREEflag) || lp->timed_out) {
-                       lp->intmask &= ~TXFREEflag;
+                       lp->intmask &= ~(TXFREEflag|EXCNAKflag);
 
                        BUGMSG(D_DURING, "TX IRQ (stat=%Xh)\n", status);
 
-                       if (lp->cur_tx != -1 && !(status & TXACKflag) && !lp->timed_out) {
-                               if (lp->lasttrans_dest != 0) {
-                                       BUGMSG(D_EXTRA, "transmit was not acknowledged! "
-                                           "(status=%Xh, dest=%02Xh)\n",
-                                            status, lp->lasttrans_dest);
-                                       lp->stats.tx_errors++;
-                                       lp->stats.tx_carrier_errors++;
-                               } else {
-                                       BUGMSG(D_DURING,
-                                              "broadcast was not acknowledged; that's normal "
-                                           "(status=%Xh, dest=%02Xh)\n",
-                                            status, lp->lasttrans_dest);
+                       if (lp->cur_tx != -1 && !lp->timed_out) {
+                               if(!(status & TXACKflag)) {
+                                       if (lp->lasttrans_dest != 0) {
+                                               BUGMSG(D_EXTRA,
+                                                      "transmit was not acknowledged! "
+                                                      "(status=%Xh, dest=%02Xh)\n",
+                                                      status, lp->lasttrans_dest);
+                                               lp->stats.tx_errors++;
+                                               lp->stats.tx_carrier_errors++;
+                                       } else {
+                                               BUGMSG(D_DURING,
+                                                      "broadcast was not acknowledged; that's normal "
+                                                      "(status=%Xh, dest=%02Xh)\n",
+                                                      status, lp->lasttrans_dest);
+                                       }
+                               }
+
+                               if (lp->outgoing.proto &&
+                                   lp->outgoing.proto->ack_tx) {
+                                 int ackstatus;
+                                 if(status & TXACKflag)
+                                    ackstatus=2;
+                                  else if(lp->excnak_pending)
+                                    ackstatus=1;
+                                  else
+                                    ackstatus=0;
+
+                                  lp->outgoing.proto
+                                    ->ack_tx(dev, ackstatus);
                                }
                        }
                        if (lp->cur_tx != -1)
@@ -797,8 +897,11 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                                        if (lp->outgoing.proto->continue_tx(dev, txbuf)) {
                                                /* that was the last segment */
                                                lp->stats.tx_bytes += lp->outgoing.skb->len;
-                                               dev_kfree_skb_irq(lp->outgoing.skb);
-                                               lp->outgoing.proto = NULL;
+                                               if(!lp->outgoing.proto->ack_tx)
+                                                 {
+                                                   dev_kfree_skb_irq(lp->outgoing.skb);
+                                                   lp->outgoing.proto = NULL;
+                                                 }
                                        }
                                        lp->next_tx = txbuf;
                                }
@@ -809,7 +912,7 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
                }
                /* now process the received packet, if any */
                if (recbuf != -1) {
-                       BUGLVL(D_RX) arcnet_dump_packet(dev, recbuf, "rx irq");
+                       BUGLVL(D_RX) arcnet_dump_packet(dev, recbuf, "rx irq", 0);
 
                        arcnet_rx(dev, recbuf);
                        release_arcbuf(dev, recbuf);
@@ -822,6 +925,9 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
                        BUGMSG(D_RECON, "Network reconfiguration detected (status=%Xh)\n",
                               status);
+                       /* MYRECON bit is at bit 7 of diagstatus */
+                       if(diagstatus & 0x80)
+                               BUGMSG(D_RECON,"Put out that recon myself\n");
 
                        /* is the RECON info empty or old? */
                        if (!lp->first_recon || !lp->last_recon ||
@@ -867,6 +973,10 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
                        BUGMSG(D_DURING, "not recon: clearing counters anyway.\n");
                }
+
+               if(didsomething) {
+                       retval |= IRQ_HANDLED;
+               }
        }
        while (--boguscount && didsomething);
 
@@ -879,8 +989,8 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
        udelay(1);
        AINTMASK(lp->intmask);
        
-       spin_unlock(&arcnet_lock);
-       return IRQ_RETVAL(didsomething);
+       spin_unlock(&lp->lock);
+       return retval;
 }
 
 
@@ -888,9 +998,9 @@ irqreturn_t arcnet_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  * This is a generic packet receiver that calls arcnet??_rx depending on the
  * protocol ID found.
  */
-void arcnet_rx(struct net_device *dev, int bufnum)
+static void arcnet_rx(struct net_device *dev, int bufnum)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
        struct archdr pkt;
        struct arc_rfc1201 *soft;
        int length, ofs;
@@ -907,7 +1017,7 @@ void arcnet_rx(struct net_device *dev, int bufnum)
        }
 
        /* get the full header, if possible */
-       if (sizeof(pkt.soft) < length)
+       if (sizeof(pkt.soft) <= length)
                lp->hw.copy_from_card(dev, bufnum, ofs, soft, sizeof(pkt.soft));
        else {
                memset(&pkt.soft, 0, sizeof(pkt.soft));
@@ -922,7 +1032,7 @@ void arcnet_rx(struct net_device *dev, int bufnum)
        lp->stats.rx_bytes += length + ARC_HDR_SIZE;
 
        /* call the right receiver for the protocol */
-       if (arc_proto_map[soft->proto] != &arc_proto_null) {
+       if (arc_proto_map[soft->proto]->is_ip) {
                BUGLVL(D_PROTO) {
                        struct ArcProto
                        *oldp = arc_proto_map[lp->default_proto[pkt.hard.source]],
@@ -954,7 +1064,7 @@ void arcnet_rx(struct net_device *dev, int bufnum)
  */
 static struct net_device_stats *arcnet_get_stats(struct net_device *dev)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
        return &lp->stats;
 }
 
@@ -971,7 +1081,7 @@ static void null_rx(struct net_device *dev, int bufnum,
 static int null_build_header(struct sk_buff *skb, struct net_device *dev,
                             unsigned short type, uint8_t daddr)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
 
        BUGMSG(D_PROTO,
               "tx: can't build header for encap %02Xh; load a protocol driver.\n",
@@ -986,7 +1096,7 @@ static int null_build_header(struct sk_buff *skb, struct net_device *dev,
 static int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
                           int length, int bufnum)
 {
-       struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+       struct arcnet_local *lp = dev->priv;
        struct arc_hardware newpkt;
 
        BUGMSG(D_PROTO, "tx: no encap for this host; load a protocol driver.\n");