aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/arcnet
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/arcnet
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/net/arcnet')
-rw-r--r--drivers/net/arcnet/Kconfig140
-rw-r--r--drivers/net/arcnet/Makefile14
-rw-r--r--drivers/net/arcnet/arc-rawmode.c204
-rw-r--r--drivers/net/arcnet/arc-rimi.c368
-rw-r--r--drivers/net/arcnet/arcnet.c1102
-rw-r--r--drivers/net/arcnet/capmode.c296
-rw-r--r--drivers/net/arcnet/com20020-isa.c219
-rw-r--r--drivers/net/arcnet/com20020-pci.c189
-rw-r--r--drivers/net/arcnet/com20020.c357
-rw-r--r--drivers/net/arcnet/com90io.c435
-rw-r--r--drivers/net/arcnet/com90xx.c646
-rw-r--r--drivers/net/arcnet/rfc1051.c253
-rw-r--r--drivers/net/arcnet/rfc1201.c549
13 files changed, 4772 insertions, 0 deletions
diff --git a/drivers/net/arcnet/Kconfig b/drivers/net/arcnet/Kconfig
new file mode 100644
index 00000000000..948de2532a1
--- /dev/null
+++ b/drivers/net/arcnet/Kconfig
@@ -0,0 +1,140 @@
+#
+# Arcnet configuration
+#
+
+menu "ARCnet devices"
+ depends on NETDEVICES && (ISA || PCI)
+
+config ARCNET
+ tristate "ARCnet support"
+ ---help---
+ If you have a network card of this type, say Y and check out the
+ (arguably) beautiful poetry in
+ <file:Documentation/networking/arcnet.txt>.
+
+ You need both this driver, and the driver for the particular ARCnet
+ chipset of your card. If you don't know, then it's probably a
+ COM90xx type card, so say Y (or M) to "ARCnet COM90xx chipset
+ support" below.
+
+ You might also want to have a look at the Ethernet-HOWTO, available
+ from <http://www.tldp.org/docs.html#howto>(even though ARCnet
+ is not really Ethernet).
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module will
+ be called arcnet.
+
+config ARCNET_1201
+ tristate "Enable standard ARCNet packet format (RFC 1201)"
+ depends on ARCNET
+ help
+ This allows you to use RFC1201 with your ARCnet card via the virtual
+ arc0 device. You need to say Y here to communicate with
+ industry-standard RFC1201 implementations, like the arcether.com
+ packet driver or most DOS/Windows ODI drivers. Please read the
+ ARCnet documentation in <file:Documentation/networking/arcnet.txt>
+ for more information about using arc0.
+
+config ARCNET_1051
+ tristate "Enable old ARCNet packet format (RFC 1051)"
+ depends on ARCNET
+ ---help---
+ This allows you to use RFC1051 with your ARCnet card via the virtual
+ arc0s device. You only need arc0s if you want to talk to ARCnet
+ software complying with the "old" standard, specifically, the DOS
+ arcnet.com packet driver, Amigas running AmiTCP, and some variants
+ of NetBSD. You do not need to say Y here to communicate with
+ industry-standard RFC1201 implementations, like the arcether.com
+ packet driver or most DOS/Windows ODI drivers. RFC1201 is included
+ automatically as the arc0 device. Please read the ARCnet
+ documentation in <file:Documentation/networking/arcnet.txt> for more
+ information about using arc0e and arc0s.
+
+config ARCNET_RAW
+ tristate "Enable raw mode packet interface"
+ depends on ARCNET
+ help
+ ARCnet "raw mode" packet encapsulation, no soft headers. Unlikely
+ to work unless talking to a copy of the same Linux arcnet driver,
+ but perhaps marginally faster in that case.
+
+config ARCNET_CAP
+ tristate "Enable CAP mode packet interface"
+ depends on ARCNET
+ help
+ ARCnet "cap mode" packet encapsulation. Used to get the hardware
+ acknowledge back to userspace. After the initial protocol byte every
+ packet is stuffed with an extra 4 byte "cookie" which doesn't
+ actually appear on the network. After transmit the driver will send
+ back a packet with protocol byte 0 containing the status of the
+ transmition:
+ 0=no hardware acknowledge
+ 1=excessive nak
+ 2=transmition accepted by the reciever hardware
+
+ Received packets are also stuffed with the extra 4 bytes but it will
+ be random data.
+
+ Cap only listens to protocol 1-8.
+
+config ARCNET_COM90xx
+ tristate "ARCnet COM90xx (normal) chipset driver"
+ depends on ARCNET
+ help
+ This is the chipset driver for the standard COM90xx cards. If you
+ have always used the old ARCnet driver without knowing what type of
+ card you had, this is probably the one for you.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module will
+ be called com90xx.
+
+config ARCNET_COM90xxIO
+ tristate "ARCnet COM90xx (IO mapped) chipset driver"
+ depends on ARCNET
+ ---help---
+ This is the chipset driver for the COM90xx cards, using them in
+ IO-mapped mode instead of memory-mapped mode. This is slower than
+ the normal driver. Only use it if your card doesn't support shared
+ memory.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module will
+ be called com90io.
+
+config ARCNET_RIM_I
+ tristate "ARCnet COM90xx (RIM I) chipset driver"
+ depends on ARCNET
+ ---help---
+ This is yet another chipset driver for the COM90xx cards, but this
+ time only using memory-mapped mode, and no IO ports at all. This
+ driver is completely untested, so if you have one of these cards,
+ please mail <dwmw2@infradead.org>, especially if it works!
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module will
+ be called arc-rimi.
+
+config ARCNET_COM20020
+ tristate "ARCnet COM20020 chipset driver"
+ depends on ARCNET
+ help
+ This is the driver for the new COM20020 chipset. It supports such
+ things as promiscuous mode, so packet sniffing is possible, and
+ extra diagnostic information.
+
+ To compile this driver as a module, choose M here and read
+ <file:Documentation/networking/net-modules.txt>. The module will
+ be called com20020.
+
+config ARCNET_COM20020_ISA
+ tristate "Support for COM20020 on ISA"
+ depends on ARCNET_COM20020 && ISA
+
+config ARCNET_COM20020_PCI
+ tristate "Support for COM20020 on PCI"
+ depends on ARCNET_COM20020 && PCI
+
+endmenu
+
diff --git a/drivers/net/arcnet/Makefile b/drivers/net/arcnet/Makefile
new file mode 100644
index 00000000000..5861af543d4
--- /dev/null
+++ b/drivers/net/arcnet/Makefile
@@ -0,0 +1,14 @@
+# Makefile for linux/drivers/net/arcnet
+#
+
+obj-$(CONFIG_ARCNET) += arcnet.o
+obj-$(CONFIG_ARCNET_1201) += rfc1201.o
+obj-$(CONFIG_ARCNET_1051) += rfc1051.o
+obj-$(CONFIG_ARCNET_RAW) += arc-rawmode.o
+obj-$(CONFIG_ARCNET_CAP) += capmode.o
+obj-$(CONFIG_ARCNET_COM90xx) += com90xx.o
+obj-$(CONFIG_ARCNET_COM90xxIO) += com90io.o
+obj-$(CONFIG_ARCNET_RIM_I) += arc-rimi.o
+obj-$(CONFIG_ARCNET_COM20020) += com20020.o
+obj-$(CONFIG_ARCNET_COM20020_ISA) += com20020-isa.o
+obj-$(CONFIG_ARCNET_COM20020_PCI) += com20020-pci.o
diff --git a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c
new file mode 100644
index 00000000000..e1ea29b0cd1
--- /dev/null
+++ b/drivers/net/arcnet/arc-rawmode.c
@@ -0,0 +1,204 @@
+/*
+ * Linux ARCnet driver - "raw mode" packet encapsulation (no soft headers)
+ *
+ * Written 1994-1999 by Avery Pennarun.
+ * Derived from skeleton.c by Donald Becker.
+ *
+ * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
+ * for sponsoring the further development of this driver.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if_arp.h>
+#include <net/arp.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/arcdevice.h>
+
+#define VERSION "arcnet: raw mode (`r') encapsulation support loaded.\n"
+
+
+static void rx(struct net_device *dev, int bufnum,
+ struct archdr *pkthdr, int length);
+static int build_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, uint8_t daddr);
+static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
+ int bufnum);
+
+struct ArcProto rawmode_proto =
+{
+ .suffix = 'r',
+ .mtu = XMTU,
+ .rx = rx,
+ .build_header = build_header,
+ .prepare_tx = prepare_tx,
+ .continue_tx = NULL,
+ .ack_tx = NULL
+};
+
+
+static int __init arcnet_raw_init(void)
+{
+ int count;
+
+ printk(VERSION);
+
+ for (count = 0; count < 256; count++)
+ if (arc_proto_map[count] == arc_proto_default)
+ arc_proto_map[count] = &rawmode_proto;
+
+ /* for raw mode, we only set the bcast proto if there's no better one */
+ if (arc_bcast_proto == arc_proto_default)
+ arc_bcast_proto = &rawmode_proto;
+
+ arc_proto_default = &rawmode_proto;
+ return 0;
+}
+
+static void __exit arcnet_raw_exit(void)
+{
+ arcnet_unregister_proto(&rawmode_proto);
+}
+
+module_init(arcnet_raw_init);
+module_exit(arcnet_raw_exit);
+
+MODULE_LICENSE("GPL");
+
+
+/* packet receiver */
+static void rx(struct net_device *dev, int bufnum,
+ struct archdr *pkthdr, int length)
+{
+ struct arcnet_local *lp = dev->priv;
+ struct sk_buff *skb;
+ struct archdr *pkt = pkthdr;
+ int ofs;
+
+ BUGMSG(D_DURING, "it's a raw packet (length=%d)\n", length);
+
+ if (length >= MinTU)
+ ofs = 512 - length;
+ else
+ ofs = 256 - length;
+
+ skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
+ if (skb == NULL) {
+ BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
+ lp->stats.rx_dropped++;
+ return;
+ }
+ skb_put(skb, length + ARC_HDR_SIZE);
+ skb->dev = dev;
+
+ pkt = (struct archdr *) skb->data;
+
+ skb->mac.raw = skb->data;
+ skb_pull(skb, ARC_HDR_SIZE);
+
+ /* up to sizeof(pkt->soft) has already been copied from the card */
+ memcpy(pkt, pkthdr, sizeof(struct archdr));
+ if (length > sizeof(pkt->soft))
+ lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
+ pkt->soft.raw + sizeof(pkt->soft),
+ length - sizeof(pkt->soft));
+
+ BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
+
+ skb->protocol = __constant_htons(ETH_P_ARCNET);
+;
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+}
+
+
+/*
+ * Create the ARCnet hard/soft headers for raw mode.
+ * There aren't any soft headers in raw mode - not even the protocol id.
+ */
+static int build_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, uint8_t daddr)
+{
+ int hdr_size = ARC_HDR_SIZE;
+ struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
+
+ /*
+ * Set the source hardware address.
+ *
+ * This is pretty pointless for most purposes, but it can help in
+ * debugging. ARCnet does not allow us to change the source address in
+ * the actual packet sent)
+ */
+ pkt->hard.source = *dev->dev_addr;
+
+ /* see linux/net/ethernet/eth.c to see where I got the following */
+
+ if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
+ /*
+ * FIXME: fill in the last byte of the dest ipaddr here to better
+ * comply with RFC1051 in "noarp" mode.
+ */
+ pkt->hard.dest = 0;
+ return hdr_size;
+ }
+ /* otherwise, just fill it in and go! */
+ pkt->hard.dest = daddr;
+
+ return hdr_size; /* success */
+}
+
+
+static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
+ int bufnum)
+{
+ struct arcnet_local *lp = dev->priv;
+ struct arc_hardware *hard = &pkt->hard;
+ int ofs;
+
+ BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
+ lp->next_tx, lp->cur_tx, bufnum);
+
+ length -= ARC_HDR_SIZE; /* hard header is not included in packet length */
+
+ if (length > XMTU) {
+ /* should never happen! other people already check for this. */
+ BUGMSG(D_NORMAL, "Bug! prepare_tx with size %d (> %d)\n",
+ length, XMTU);
+ length = XMTU;
+ }
+ if (length > MinTU) {
+ hard->offset[0] = 0;
+ hard->offset[1] = ofs = 512 - length;
+ } else if (length > MTU) {
+ hard->offset[0] = 0;
+ hard->offset[1] = ofs = 512 - length - 3;
+ } else
+ hard->offset[0] = ofs = 256 - length;
+
+ BUGMSG(D_DURING, "prepare_tx: length=%d ofs=%d\n",
+ length,ofs);
+
+ lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
+ lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft, length);
+
+ lp->lastload_dest = hard->dest;
+
+ return 1; /* done */
+}
diff --git a/drivers/net/arcnet/arc-rimi.c b/drivers/net/arcnet/arc-rimi.c
new file mode 100644
index 00000000000..38c3f033f73
--- /dev/null
+++ b/drivers/net/arcnet/arc-rimi.c
@@ -0,0 +1,368 @@
+/*
+ * Linux ARCnet driver - "RIM I" (entirely mem-mapped) cards
+ *
+ * Written 1994-1999 by Avery Pennarun.
+ * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
+ * Derived from skeleton.c by Donald Becker.
+ *
+ * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
+ * for sponsoring the further development of this driver.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/bootmem.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/arcdevice.h>
+
+
+#define VERSION "arcnet: RIM I (entirely mem-mapped) support\n"
+
+
+/* Internal function declarations */
+
+static int arcrimi_probe(struct net_device *dev);
+static int arcrimi_found(struct net_device *dev);
+static void arcrimi_command(struct net_device *dev, int command);
+static int arcrimi_status(struct net_device *dev);
+static void arcrimi_setmask(struct net_device *dev, int mask);
+static int arcrimi_reset(struct net_device *dev, int really_reset);
+static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count);
+static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count);
+
+/* Handy defines for ARCnet specific stuff */
+
+/* Amount of I/O memory used by the card */
+#define BUFFER_SIZE (512)
+#define MIRROR_SIZE (BUFFER_SIZE*4)
+
+/* COM 9026 controller chip --> ARCnet register addresses */
+#define _INTMASK (ioaddr+0) /* writable */
+#define _STATUS (ioaddr+0) /* readable */
+#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
+#define _RESET (ioaddr+8) /* software reset (on read) */
+#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */
+#define _ADDR_HI (ioaddr+15) /* Control registers for said */
+#define _ADDR_LO (ioaddr+14)
+#define _CONFIG (ioaddr+2) /* Configuration register */
+
+#undef ASTATUS
+#undef ACOMMAND
+#undef AINTMASK
+
+#define ASTATUS() readb(_STATUS)
+#define ACOMMAND(cmd) writeb((cmd),_COMMAND)
+#define AINTMASK(msk) writeb((msk),_INTMASK)
+#define SETCONF() writeb(lp->config,_CONFIG)
+
+
+/*
+ * We cannot probe for a RIM I card; one reason is I don't know how to reset
+ * them. In fact, we can't even get their node ID automatically. So, we
+ * need to be passed a specific shmem address, IRQ, and node ID.
+ */
+static int __init arcrimi_probe(struct net_device *dev)
+{
+ BUGLVL(D_NORMAL) printk(VERSION);
+ BUGLVL(D_NORMAL) printk("E-mail me if you actually test the RIM I driver, please!\n");
+
+ BUGMSG(D_NORMAL, "Given: node %02Xh, shmem %lXh, irq %d\n",
+ dev->dev_addr[0], dev->mem_start, dev->irq);
+
+ if (dev->mem_start <= 0 || dev->irq <= 0) {
+ BUGMSG(D_NORMAL, "No autoprobe for RIM I; you "
+ "must specify the shmem and irq!\n");
+ return -ENODEV;
+ }
+ /*
+ * Grab the memory region at mem_start for BUFFER_SIZE bytes.
+ * Later in arcrimi_found() the real size will be determined
+ * and this reserve will be released and the correct size
+ * will be taken.
+ */
+ if (!request_mem_region(dev->mem_start, BUFFER_SIZE, "arcnet (90xx)")) {
+ BUGMSG(D_NORMAL, "Card memory already allocated\n");
+ return -ENODEV;
+ }
+ if (dev->dev_addr[0] == 0) {
+ release_mem_region(dev->mem_start, BUFFER_SIZE);
+ BUGMSG(D_NORMAL, "You need to specify your card's station "
+ "ID!\n");
+ return -ENODEV;
+ }
+ return arcrimi_found(dev);
+}
+
+
+/*
+ * Set up the struct net_device associated with this card. Called after
+ * probing succeeds.
+ */
+static int __init arcrimi_found(struct net_device *dev)
+{
+ struct arcnet_local *lp;
+ unsigned long first_mirror, last_mirror, shmem;
+ int mirror_size;
+ int err;
+
+ /* reserve the irq */
+ if (request_irq(dev->irq, &arcnet_interrupt, 0, "arcnet (RIM I)", dev)) {
+ release_mem_region(dev->mem_start, BUFFER_SIZE);
+ BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
+ return -ENODEV;
+ }
+
+ shmem = dev->mem_start;
+ isa_writeb(TESTvalue, shmem);
+ isa_writeb(dev->dev_addr[0], shmem + 1); /* actually the node ID */
+
+ /* find the real shared memory start/end points, including mirrors */
+
+ /* guess the actual size of one "memory mirror" - the number of
+ * bytes between copies of the shared memory. On most cards, it's
+ * 2k (or there are no mirrors at all) but on some, it's 4k.
+ */
+ mirror_size = MIRROR_SIZE;
+ if (isa_readb(shmem) == TESTvalue
+ && isa_readb(shmem - mirror_size) != TESTvalue
+ && isa_readb(shmem - 2 * mirror_size) == TESTvalue)
+ mirror_size *= 2;
+
+ first_mirror = last_mirror = shmem;
+ while (isa_readb(first_mirror) == TESTvalue)
+ first_mirror -= mirror_size;
+ first_mirror += mirror_size;
+
+ while (isa_readb(last_mirror) == TESTvalue)
+ last_mirror += mirror_size;
+ last_mirror -= mirror_size;
+
+ dev->mem_start = first_mirror;
+ dev->mem_end = last_mirror + MIRROR_SIZE - 1;
+
+ /* initialize the rest of the device structure. */
+
+ lp = dev->priv;
+ lp->card_name = "RIM I";
+ lp->hw.command = arcrimi_command;
+ lp->hw.status = arcrimi_status;
+ lp->hw.intmask = arcrimi_setmask;
+ lp->hw.reset = arcrimi_reset;
+ lp->hw.owner = THIS_MODULE;
+ lp->hw.copy_to_card = arcrimi_copy_to_card;
+ lp->hw.copy_from_card = arcrimi_copy_from_card;
+
+ /*
+ * re-reserve the memory region - arcrimi_probe() alloced this reqion
+ * but didn't know the real size. Free that region and then re-get
+ * with the correct size. There is a VERY slim chance this could
+ * fail.
+ */
+ release_mem_region(shmem, BUFFER_SIZE);
+ if (!request_mem_region(dev->mem_start,
+ dev->mem_end - dev->mem_start + 1,
+ "arcnet (90xx)")) {
+ BUGMSG(D_NORMAL, "Card memory already allocated\n");
+ goto err_free_irq;
+ }
+
+ lp->mem_start = ioremap(dev->mem_start, dev->mem_end - dev->mem_start + 1);
+ if (!lp->mem_start) {
+ BUGMSG(D_NORMAL, "Can't remap device memory!\n");
+ goto err_release_mem;
+ }
+
+ /* get and check the station ID from offset 1 in shmem */
+ dev->dev_addr[0] = readb(lp->mem_start + 1);
+
+ BUGMSG(D_NORMAL, "ARCnet RIM I: station %02Xh found at IRQ %d, "
+ "ShMem %lXh (%ld*%d bytes).\n",
+ dev->dev_addr[0],
+ dev->irq, dev->mem_start,
+ (dev->mem_end - dev->mem_start + 1) / mirror_size, mirror_size);
+
+ err = register_netdev(dev);
+ if (err)
+ goto err_unmap;
+
+ return 0;
+
+err_unmap:
+ iounmap(lp->mem_start);
+err_release_mem:
+ release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
+err_free_irq:
+ free_irq(dev->irq, dev);
+ return -EIO;
+}
+
+
+/*
+ * Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+static int arcrimi_reset(struct net_device *dev, int really_reset)
+{
+ struct arcnet_local *lp = dev->priv;
+ void __iomem *ioaddr = lp->mem_start + 0x800;
+
+ BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", dev->name, ASTATUS());
+
+ if (really_reset) {
+ writeb(TESTvalue, ioaddr - 0x800); /* fake reset */
+ return 0;
+ }
+ ACOMMAND(CFLAGScmd | RESETclear); /* clear flags & end reset */
+ ACOMMAND(CFLAGScmd | CONFIGclear);
+
+ /* enable extended (512-byte) packets */
+ ACOMMAND(CONFIGcmd | EXTconf);
+
+ /* done! return success. */
+ return 0;
+}
+
+static void arcrimi_setmask(struct net_device *dev, int mask)
+{
+ struct arcnet_local *lp = dev->priv;
+ void __iomem *ioaddr = lp->mem_start + 0x800;
+
+ AINTMASK(mask);
+}
+
+static int arcrimi_status(struct net_device *dev)
+{
+ struct arcnet_local *lp = dev->priv;
+ void __iomem *ioaddr = lp->mem_start + 0x800;
+
+ return ASTATUS();
+}
+
+static void arcrimi_command(struct net_device *dev, int cmd)
+{
+ struct arcnet_local *lp = dev->priv;
+ void __iomem *ioaddr = lp->mem_start + 0x800;
+
+ ACOMMAND(cmd);
+}
+
+static void arcrimi_copy_to_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count)
+{
+ struct arcnet_local *lp = dev->priv;
+ void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
+ TIME("memcpy_toio", count, memcpy_toio(memaddr, buf, count));
+}
+
+
+static void arcrimi_copy_from_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count)
+{
+ struct arcnet_local *lp = dev->priv;
+ void __iomem *memaddr = lp->mem_start + 0x800 + bufnum * 512 + offset;
+ TIME("memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
+}
+
+static int node;
+static int io; /* use the insmod io= irq= node= options */
+static int irq;
+static char device[9]; /* use eg. device=arc1 to change name */
+
+module_param(node, int, 0);
+module_param(io, int, 0);
+module_param(irq, int, 0);
+module_param_string(device, device, sizeof(device), 0);
+MODULE_LICENSE("GPL");
+
+static struct net_device *my_dev;
+
+static int __init arc_rimi_init(void)
+{
+ struct net_device *dev;
+
+ dev = alloc_arcdev(device);
+ if (!dev)
+ return -ENOMEM;
+
+ if (node && node != 0xff)
+ dev->dev_addr[0] = node;
+
+ dev->mem_start = io;
+ dev->irq = irq;
+ if (dev->irq == 2)
+ dev->irq = 9;
+
+ if (arcrimi_probe(dev)) {
+ free_netdev(dev);
+ return -EIO;
+ }
+
+ my_dev = dev;
+ return 0;
+}
+
+static void __exit arc_rimi_exit(void)
+{
+ struct net_device *dev = my_dev;
+ struct arcnet_local *lp = dev->priv;
+
+ unregister_netdev(dev);
+ iounmap(lp->mem_start);
+ release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
+ free_irq(dev->irq, dev);
+ free_netdev(dev);
+}
+
+#ifndef MODULE
+static int __init arcrimi_setup(char *s)
+{
+ int ints[8];
+ s = get_options(s, 8, ints);
+ if (!ints[0])
+ return 1;
+ switch (ints[0]) {
+ default: /* ERROR */
+ printk("arcrimi: Too many arguments.\n");
+ case 3: /* Node ID */
+ node = ints[3];
+ case 2: /* IRQ */
+ irq = ints[2];
+ case 1: /* IO address */
+ io = ints[1];
+ }
+ if (*s)
+ snprintf(device, sizeof(device), "%s", s);
+ return 1;
+}
+__setup("arcrimi=", arcrimi_setup);
+#endif /* MODULE */
+
+module_init(arc_rimi_init)
+module_exit(arc_rimi_exit)
diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c
new file mode 100644
index 00000000000..4f9f69e22c1
--- /dev/null
+++ b/drivers/net/arcnet/arcnet.c
@@ -0,0 +1,1102 @@
+/*
+ * Linux ARCnet driver - device-independent routines
+ *
+ * Written 1997 by David Woodhouse.
+ * Written 1994-1999 by Avery Pennarun.
+ * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
+ * Derived from skeleton.c by Donald Becker.
+ *
+ * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
+ * for sponsoring the further development of this driver.
+ *
+ * **********************
+ *
+ * The original copyright was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * The change log is now in a file called ChangeLog in this directory.
+ *
+ * Sources:
+ * - Crynwr arcnet.com/arcether.com packet drivers.
+ * - arcnet.c v0.00 dated 1/1/94 and apparently by
+ * Donald Becker - it didn't work :)
+ * - skeleton.c v0.05 dated 11/16/93 by Donald Becker
+ * (from Linux Kernel 1.1.45)
+ * - RFC's 1201 and 1051 - re: TCP/IP over ARCnet
+ * - The official ARCnet COM9026 data sheets (!) thanks to
+ * Ken Cornetet <kcornete@nyx10.cs.du.edu>
+ * - The official ARCnet COM20020 data sheets.
+ * - Information on some more obscure ARCnet controller chips, thanks
+ * to the nice people at SMSC.
+ * - net/inet/eth.c (from kernel 1.1.50) for header-building info.
+ * - Alternate Linux ARCnet source by V.Shergin <vsher@sao.stavropol.su>
+ * - Textual information and more alternate source from Joachim Koenig
+ * <jojo@repas.de>
+ */
+
+#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>
+#include <linux/if_arp.h>
+#include <net/arp.h>
+#include <linux/init.h>
+#include <linux/arcdevice.h>
+
+/* "do nothing" functions for protocol drivers */
+static void null_rx(struct net_device *dev, int bufnum,
+ struct archdr *pkthdr, int length);
+static int null_build_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, uint8_t daddr);
+static int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
+ int length, int bufnum);
+
+
+/*
+ * one ArcProto per possible proto ID. None of the elements of
+ * arc_proto_map are allowed to be NULL; they will get set to
+ * 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, *arc_raw_proto;
+
+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
+};
+
+/* 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_raw_proto);
+EXPORT_SYMBOL(arc_proto_null);
+EXPORT_SYMBOL(arcnet_unregister_proto);
+EXPORT_SYMBOL(arcnet_debug);
+EXPORT_SYMBOL(alloc_arcdev);
+EXPORT_SYMBOL(arcnet_interrupt);
+
+/* Internal function prototypes */
+static int arcnet_open(struct net_device *dev);
+static int arcnet_close(struct net_device *dev);
+static int arcnet_send_packet(struct sk_buff *skb, struct net_device *dev);
+static void arcnet_timeout(struct net_device *dev);
+static int arcnet_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, void *daddr, void *saddr,
+ unsigned len);
+static int arcnet_rebuild_header(struct sk_buff *skb);
+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_param(debug, int, 0);
+MODULE_LICENSE("GPL");
+
+static int __init arcnet_init(void)
+{
+ int count;
+
+ arcnet_debug = debug;
+
+ printk(VERSION);
+
+#ifdef ALPHA_WARNING
+ BUGLVL(D_EXTRA) {
+ printk("arcnet: ***\n"
+ "arcnet: * Read arcnet.txt for important release notes!\n"
+ "arcnet: *\n"
+ "arcnet: * This is an ALPHA version! (Last stable release: v3.02) E-mail\n"
+ "arcnet: * me if you have any questions, comments, or bug reports.\n"
+ "arcnet: ***\n");
+ }
+#endif
+
+ /* initialize the protocol map */
+ 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;
+
+ BUGLVL(D_DURING)
+ printk("arcnet: struct sizes: %Zd %Zd %Zd %Zd %Zd\n",
+ sizeof(struct arc_hardware), sizeof(struct arc_rfc1201),
+ sizeof(struct arc_rfc1051), sizeof(struct arc_eth_encap),
+ sizeof(struct archdr));
+
+ return 0;
+}
+
+static void __exit arcnet_exit(void)
+{
+}
+
+module_init(arcnet_init);
+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)
+{
+ int i;
+
+ printk(KERN_DEBUG "%6s: skb dump (%s) follows:", dev->name, desc);
+ for (i = 0; i < skb->len; i++) {
+ if (i % 16 == 0)
+ printk("\n" KERN_DEBUG "[%04X] ", i);
+ printk("%02X ", ((u_char *) skb->data)[i]);
+ }
+ printk("\n");
+}
+
+EXPORT_SYMBOL(arcnet_dump_skb);
+#endif
+
+
+/*
+ * 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,
+ int take_arcnet_lock)
+{
+ struct arcnet_local *lp = dev->priv;
+ int i, length;
+ 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 */
+ if(take_arcnet_lock)
+ spin_lock_irqsave(&lp->lock, flags);
+
+ lp->hw.copy_from_card(dev, bufnum, 0, buf, 512);
+ 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);
+
+ printk(KERN_DEBUG "%6s: packet dump (%s) follows:", dev->name, desc);
+ for (i = 0; i < length; i++) {
+ if (i % 16 == 0)
+ printk("\n" KERN_DEBUG "[%04X] ", i);
+ printk("%02X ", buf[i]);
+ }
+ printk("\n");
+
+}
+
+EXPORT_SYMBOL(arcnet_dump_packet);
+#endif
+
+
+/*
+ * Unregister a protocol driver from the arc_proto_map. Protocol drivers
+ * are responsible for registering themselves, but the unregister routine
+ * is pretty generic so we'll do it here.
+ */
+void arcnet_unregister_proto(struct ArcProto *proto)
+{
+ int count;
+
+ if (arc_proto_default == 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)
+ arc_proto_map[count] = arc_proto_default;
+ }
+}
+
+
+/*
+ * Add a buffer to the queue. Only the interrupt handler is allowed to do
+ * this, unless interrupts are disabled.
+ *
+ * Note: we don't check for a full queue, since there aren't enough buffers
+ * to more than fill it.
+ */
+static void release_arcbuf(struct net_device *dev, int bufnum)
+{
+ struct arcnet_local *lp = dev->priv;
+ int i;
+
+ lp->buf_queue[lp->first_free_buf++] = bufnum;
+ lp->first_free_buf %= 5;
+
+ 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+1) % 5)
+ BUGMSG2(D_DURING, "#%d ", lp->buf_queue[i]);
+ BUGMSG2(D_DURING, "\n");
+ }
+}
+
+
+/*
+ * Get a buffer from the queue. If this returns -1, there are no buffers
+ * available.
+ */
+static int get_arcbuf(struct net_device *dev)
+{
+ 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);
+ }
+ else { /* we can continue */
+ if (lp->next_buf >= 5)
+ lp->next_buf -= 5;
+
+ if (lp->next_buf == lp->first_free_buf)
+ BUGMSG(D_NORMAL, "get_arcbuf: BUG: no buffers are available??\n");
+ else {
+ buf = lp->buf_queue[lp->next_buf++];
+ lp->next_buf %= 5;
+ }
+ }
+
+
+ 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+1) % 5)
+ BUGMSG2(D_DURING, "#%d ", lp->buf_queue[i]);
+ BUGMSG2(D_DURING, "\n");
+ }
+
+ atomic_inc(&lp->buf_lock);
+ return buf;
+}
+
+
+static int choose_mtu(void)
+{
+ int count, mtu = 65535;
+
+ /* choose the smallest MTU of all available encaps */
+ for (count = 0; count < 256; count++) {
+ if (arc_proto_map[count] != &arc_proto_null
+ && arc_proto_map[count]->mtu < mtu) {
+ mtu = arc_proto_map[count]->mtu;
+ }
+ }
+
+ return mtu == 65535 ? XMTU : mtu;
+}
+
+
+/* Setup a struct device for ARCnet. */
+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 = 100;
+ dev->broadcast[0] = 0x00; /* for us, broadcasts are address 0 */
+ dev->watchdog_timeo = TX_TIMEOUT;
+
+ /* New-style flags. */
+ dev->flags = IFF_BROADCAST;
+
+ /*
+ * Put in this stuff here, so we don't have to export the symbols to
+ * the chipset drivers.
+ */
+ dev->open = arcnet_open;
+ dev->stop = arcnet_close;
+ dev->hard_start_xmit = arcnet_send_packet;
+ dev->tx_timeout = arcnet_timeout;
+ dev->get_stats = arcnet_get_stats;
+ dev->hard_header = arcnet_header;
+ dev->rebuild_header = arcnet_rebuild_header;
+}
+
+struct net_device *alloc_arcdev(char *name)
+{
+ 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;
+}
+
+/*
+ * Open/initialize the board. This is called sometime after booting when
+ * the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even registers
+ * that "should" only need to be set once at boot, so that there is
+ * non-reboot way to recover if something goes wrong.
+ */
+static int arcnet_open(struct net_device *dev)
+{
+ struct arcnet_local *lp = dev->priv;
+ int count, newmtu, error;
+
+ BUGMSG(D_INIT,"opened.");
+
+ if (!try_module_get(lp->hw.owner))
+ return -ENODEV;
+
+ BUGLVL(D_PROTO) {
+ int count;
+ BUGMSG(D_PROTO, "protocol map (default is '%c'): ",
+ arc_proto_default->suffix);
+ for (count = 0; count < 256; count++)
+ BUGMSG2(D_PROTO, "%c", arc_proto_map[count]->suffix);
+ BUGMSG2(D_PROTO, "\n");
+ }
+
+
+ BUGMSG(D_INIT, "arcnet_open: resetting card.\n");
+
+ /* try to put the card in a defined state - if it fails the first
+ * time, actually reset it.
+ */
+ error = -ENODEV;
+ if (ARCRESET(0) && ARCRESET(1))
+ goto out_module_put;
+
+ newmtu = choose_mtu();
+ 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));
+
+ /* the broadcast address is special - use the 'bcast' protocol */
+ for (count = 0; count < 256; count++) {
+ if (arc_proto_map[count] == arc_bcast_proto) {
+ lp->default_proto[0] = count;
+ break;
+ }
+ }
+
+ /* initialize buffers */
+ atomic_set(&lp->buf_lock, 1);
+
+ lp->next_buf = lp->first_free_buf = 0;
+ release_arcbuf(dev, 0);
+ release_arcbuf(dev, 1);
+ release_arcbuf(dev, 2);
+ release_arcbuf(dev, 3);
+ lp->cur_tx = lp->next_tx = -1;
+ lp->cur_rx = -1;
+
+ lp->rfc1201.sequence = 1;
+
+ /* bring up the hardware driver */
+ if (lp->hw.open)
+ lp->hw.open(dev);
+
+ if (dev->dev_addr[0] == 0)
+ BUGMSG(D_NORMAL, "WARNING! Station address 00 is reserved "
+ "for broadcasts!\n");
+ else if (dev->dev_addr[0] == 255)
+ BUGMSG(D_NORMAL, "WARNING! Station address FF may confuse "
+ "DOS networking programs!\n");
+
+ 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);
+
+ return 0;
+
+ out_module_put:
+ module_put(lp->hw.owner);
+ return error;
+}
+
+
+/* The inverse routine to arcnet_open - shuts down the card. */
+static int arcnet_close(struct net_device *dev)
+{
+ struct arcnet_local *lp = dev->priv;
+
+ netif_stop_queue(dev);
+
+ /* flush TX and disable RX */
+ AINTMASK(0);
+ ACOMMAND(NOTXcmd); /* stop transmit */
+ ACOMMAND(NORXcmd); /* disable receive */
+ mdelay(1);
+
+ /* shut down the card */
+ lp->hw.close(dev);
+ module_put(lp->hw.owner);
+ return 0;
+}
+
+
+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 = dev->priv;
+ uint8_t _daddr, proto_num;
+ struct ArcProto *proto;
+
+ BUGMSG(D_DURING,
+ "create header from %d to %d; protocol %d (%Xh); size %u.\n",
+ saddr ? *(uint8_t *) saddr : -1,
+ daddr ? *(uint8_t *) daddr : -1,
+ type, type, len);
+
+ if (skb->len!=0 && len != skb->len)
+ BUGMSG(D_NORMAL, "arcnet_header: Yikes! skb->len(%d) != len(%d)!\n",
+ skb->len, len);
+
+
+ /* 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",
+ (int)(skb->nh.raw - skb->mac.raw));
+ return -2; /* return error -- can't transmit yet! */
+ }
+ 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);
+}
+
+
+/*
+ * Rebuild the ARCnet hard header. This is called after an ARP (or in the
+ * future other address resolution) has completed on this sk_buff. We now
+ * let ARP fill in the destination field.
+ */
+static int arcnet_rebuild_header(struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ struct arcnet_local *lp = dev->priv;
+ int status = 0; /* default is failure */
+ unsigned short type;
+ uint8_t daddr=0;
+ struct ArcProto *proto;
+
+ if (skb->nh.raw - skb->mac.raw != 2) {
+ BUGMSG(D_NORMAL,
+ "rebuild_header: shouldn't be here! (hdrsize=%d)\n",
+ (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
+ BUGMSG(D_DURING, "rebuild header for ethernet protocol %Xh\n", type);
+ status = arp_find(&daddr, skb) ? 1 : 0;
+ BUGMSG(D_DURING, " rebuilt: dest is %d; protocol %Xh\n",
+ daddr, type);
+#endif
+ } else {
+ BUGMSG(D_NORMAL,
+ "I don't understand ethernet protocol %Xh addresses!\n", type);
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+ }
+
+ /* if we couldn't resolve the address... give up. */
+ if (!status)
+ return 0;
+
+ /* add the _real_ header this time! */
+ proto = arc_proto_map[lp->default_proto[daddr]];
+ proto->build_header(skb, dev, type, daddr);
+
+ return 1; /* success */
+}
+
+
+
+/* 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 = dev->priv;
+ struct archdr *pkt;
+ struct arc_rfc1201 *soft;
+ struct ArcProto *proto;
+ int txbuf;
+ unsigned long flags;
+ int freeskb = 0;
+
+ BUGMSG(D_DURING,
+ "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;
+ proto = arc_proto_map[soft->proto];
+
+ BUGMSG(D_SKB_SIZE, "skb: transmitting %d bytes to %02X\n",
+ skb->len, pkt->hard.dest);
+ BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "tx");
+
+ /* fits in one packet? */
+ 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 */
+ }
+
+ /* We're busy transmitting a packet... */
+ netif_stop_queue(dev);
+
+ spin_lock_irqsave(&lp->lock, flags);
+ AINTMASK(0);
+
+ txbuf = get_arcbuf(dev);
+ if (txbuf != -1) {
+ 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;
+ freeskb = 1;
+ } else {
+ /* do it the 'split' way */
+ lp->outgoing.proto = proto;
+ lp->outgoing.skb = skb;
+ lp->outgoing.pkt = pkt;
+
+ if (proto->continue_tx &&
+ proto->continue_tx(dev, txbuf)) {
+ BUGMSG(D_NORMAL,
+ "bug! continue_tx finished the first time! "
+ "(proto='%c')\n", proto->suffix);
+ }
+ }
+
+ lp->next_tx = txbuf;
+ } else {
+ freeskb = 1;
+ }
+
+ 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);
+
+ 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());
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+ if (freeskb) {
+ dev_kfree_skb(skb);
+ }
+ return 0; /* no need to try again */
+}
+
+
+/*
+ * Actually start transmitting a packet that was loaded into a buffer
+ * by prepare_tx. This should _only_ be called by the interrupt handler.
+ */
+static int go_tx(struct net_device *dev)
+{
+ 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);
+
+ if (lp->cur_tx != -1 || lp->next_tx == -1)
+ return 0;
+
+ BUGLVL(D_TX) arcnet_dump_packet(dev, lp->next_tx, "go_tx", 0);
+
+ lp->cur_tx = lp->next_tx;
+ lp->next_tx = -1;
+
+ /* 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->excnak_pending = 0;
+ lp->intmask |= TXFREEflag|EXCNAKflag;
+
+ return 1;
+}
+
+
+/* Called by the kernel when transmit times out */
+static void arcnet_timeout(struct net_device *dev)
+{
+ unsigned long flags;
+ struct arcnet_local *lp = dev->priv;
+ int status = ASTATUS();
+ char *msg;
+
+ spin_lock_irqsave(&lp->lock, flags);
+ if (status & TXFREEflag) { /* transmit _DID_ finish */
+ msg = " - missed IRQ?";
+ } else {
+ msg = "";
+ lp->stats.tx_aborted_errors++;
+ lp->timed_out = 1;
+ ACOMMAND(NOTXcmd | (lp->cur_tx << 3));
+ }
+ lp->stats.tx_errors++;
+
+ /* make sure we didn't miss a TX or a EXC NAK IRQ */
+ AINTMASK(0);
+ lp->intmask |= TXFREEflag|EXCNAKflag;
+ AINTMASK(lp->intmask);
+
+ spin_unlock_irqrestore(&lp->lock, flags);
+
+ if (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;
+ }
+
+ if (lp->cur_tx == -1)
+ netif_wake_queue(dev);
+}
+
+
+/*
+ * The typical workload of the driver: Handle the network interface
+ * 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)
+{
+ struct net_device *dev = dev_id;
+ struct arcnet_local *lp;
+ int recbuf, status, diagstatus, didsomething, boguscount;
+ int retval = IRQ_NONE;
+
+ BUGMSG(D_DURING, "\n");
+
+ BUGMSG(D_DURING, "in arcnet_interrupt\n");
+
+ lp = dev->priv;
+ if (!lp)
+ BUG();
+
+ spin_lock(&lp->lock);
+
+ /*
+ * RESET flag was enabled - if device is not running, we must clear it right
+ * away (but nothing else).
+ */
+ if (!netif_running(dev)) {
+ if (ASTATUS() & RESETflag)
+ ACOMMAND(CFLAGScmd | RESETclear);
+ AINTMASK(0);
+ spin_unlock(&lp->lock);
+ return IRQ_HANDLED;
+ }
+
+ BUGMSG(D_DURING, "in arcnet_inthandler (status=%Xh, intmask=%Xh)\n",
+ ASTATUS(), lp->intmask);
+
+ boguscount = 5;
+ do {
+ status = ASTATUS();
+ diagstatus = (status >> 8) & 0xFF;
+
+ BUGMSG(D_DEBUG, "%s: %d: %s: status=%x\n",
+ __FILE__,__LINE__,__FUNCTION__,status);
+ didsomething = 0;
+
+ /*
+ * RESET flag was enabled - card is resetting and if RX is
+ * disabled, it's NOT because we just got a packet.
+ *
+ * The card is in an undefined state. Clear it out and start over.
+ */
+ if (status & RESETflag) {
+ BUGMSG(D_NORMAL, "spurious reset (status=%Xh)\n", status);
+ arcnet_close(dev);
+ arcnet_open(dev);
+
+ /* get out of the interrupt handler! */
+ break;
+ }
+ /*
+ * RX is inhibited - we must have received something. Prepare to
+ * receive into the next buffer.
+ *
+ * We don't actually copy the received packet from the card until
+ * after the transmit handler runs (and possibly launches the next
+ * tx); this should improve latency slightly if we get both types
+ * of interrupts at once.
+ */
+ recbuf = -1;
+ if (status & lp->intmask & NORXflag) {
+ recbuf = lp->cur_rx;
+ BUGMSG(D_DURING, "Buffer #%d: receive irq (status=%Xh)\n",
+ recbuf, status);
+
+ lp->cur_rx = get_arcbuf(dev);
+ if (lp->cur_rx != -1) {
+ BUGMSG(D_DURING, "enabling receive to buffer #%d\n",
+ lp->cur_rx);
+ ACOMMAND(RXcmd | (lp->cur_rx << 3) | RXbcasts);
+ }
+ 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|EXCNAKflag);
+
+ BUGMSG(D_DURING, "TX IRQ (stat=%Xh)\n", status);
+
+ 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)
+ release_arcbuf(dev, lp->cur_tx);
+
+ lp->cur_tx = -1;
+ lp->timed_out = 0;
+ didsomething++;
+
+ /* send another packet if there is one */
+ go_tx(dev);
+
+ /* continue a split packet, if any */
+ if (lp->outgoing.proto && lp->outgoing.proto->continue_tx) {
+ int txbuf = get_arcbuf(dev);
+ if (txbuf != -1) {
+ if (lp->outgoing.proto->continue_tx(dev, txbuf)) {
+ /* that was the last segment */
+ lp->stats.tx_bytes += lp->outgoing.skb->len;
+ if(!lp->outgoing.proto->ack_tx)
+ {
+ dev_kfree_skb_irq(lp->outgoing.skb);
+ lp->outgoing.proto = NULL;
+ }
+ }
+ lp->next_tx = txbuf;
+ }
+ }
+ /* inform upper layers of idleness, if necessary */
+ if (lp->cur_tx == -1)
+ netif_wake_queue(dev);
+ }
+ /* now process the received packet, if any */
+ if (recbuf != -1) {
+ BUGLVL(D_RX) arcnet_dump_packet(dev, recbuf, "rx irq", 0);
+
+ arcnet_rx(dev, recbuf);
+ release_arcbuf(dev, recbuf);
+
+ didsomething++;
+ }
+ if (status & lp->intmask & RECONflag) {
+ ACOMMAND(CFLAGScmd | CONFIGclear);
+ lp->stats.tx_carrier_errors++;
+
+ BUGMSG(D_RECON, "Network reconfiguration detected (status=%Xh)\n",
+ status);
+
+ /* is the RECON info empty or old? */
+ if (!lp->first_recon || !lp->last_recon ||
+ jiffies - lp->last_recon > HZ * 10) {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL, "reconfiguration detected: cabling restored?\n");
+ lp->first_recon = lp->last_recon = jiffies;
+ lp->num_recons = lp->network_down = 0;
+
+ BUGMSG(D_DURING, "recon: clearing counters.\n");
+ } else { /* add to current RECON counter */
+ lp->last_recon = jiffies;
+ lp->num_recons++;
+
+ BUGMSG(D_DURING, "recon: counter=%d, time=%lds, net=%d\n",
+ lp->num_recons,
+ (lp->last_recon - lp->first_recon) / HZ,
+ lp->network_down);
+
+ /* if network is marked up;
+ * and first_recon and last_recon are 60+ apart;
+ * and the average no. of recons counted is
+ * > RECON_THRESHOLD/min;
+ * then print a warning message.
+ */
+ if (!lp->network_down
+ && (lp->last_recon - lp->first_recon) <= HZ * 60
+ && lp->num_recons >= RECON_THRESHOLD) {
+ lp->network_down = 1;
+ BUGMSG(D_NORMAL, "many reconfigurations detected: cabling problem?\n");
+ } else if (!lp->network_down
+ && lp->last_recon - lp->first_recon > HZ * 60) {
+ /* reset counters if we've gone for over a minute. */
+ lp->first_recon = lp->last_recon;
+ lp->num_recons = 1;
+ }
+ }
+ } else if (lp->network_down && jiffies - lp->last_recon > HZ * 10) {
+ if (lp->network_down)
+ BUGMSG(D_NORMAL, "cabling restored?\n");
+ lp->first_recon = lp->last_recon = 0;
+ lp->num_recons = lp->network_down = 0;
+
+ BUGMSG(D_DURING, "not recon: clearing counters anyway.\n");
+ }
+
+ if(didsomething) {
+ retval |= IRQ_HANDLED;
+ }
+ }
+ while (--boguscount && didsomething);
+
+ BUGMSG(D_DURING, "arcnet_interrupt complete (status=%Xh, count=%d)\n",
+ ASTATUS(), boguscount);
+ BUGMSG(D_DURING, "\n");
+
+
+ AINTMASK(0);
+ udelay(1);
+ AINTMASK(lp->intmask);
+
+ spin_unlock(&lp->lock);
+ return retval;
+}
+
+
+/*
+ * 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)
+{
+ struct arcnet_local *lp = dev->priv;
+ struct archdr pkt;
+ struct arc_rfc1201 *soft;
+ int length, ofs;
+
+ soft = &pkt.soft.rfc1201;
+
+ lp->hw.copy_from_card(dev, bufnum, 0, &pkt, sizeof(ARC_HDR_SIZE));
+ if (pkt.hard.offset[0]) {
+ ofs = pkt.hard.offset[0];
+ length = 256 - ofs;
+ } else {
+ ofs = pkt.hard.offset[1];
+ length = 512 - ofs;
+ }
+
+ /* get the full header, if possible */
+ 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));
+ lp->hw.copy_from_card(dev, bufnum, ofs, soft, length);
+ }
+
+ BUGMSG(D_DURING, "Buffer #%d: received packet from %02Xh to %02Xh "
+ "(%d+4 bytes)\n",
+ bufnum, pkt.hard.source, pkt.hard.dest, length);
+
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += length + ARC_HDR_SIZE;
+
+ /* call the right receiver for the protocol */
+ if (arc_proto_map[soft->proto]->is_ip) {
+ BUGLVL(D_PROTO) {
+ struct ArcProto
+ *oldp = arc_proto_map[lp->default_proto[pkt.hard.source]],
+ *newp = arc_proto_map[soft->proto];
+
+ if (oldp != newp) {
+ BUGMSG(D_PROTO,
+ "got protocol %02Xh; encap for host %02Xh is now '%c'"
+ " (was '%c')\n", soft->proto, pkt.hard.source,
+ newp->suffix, oldp->suffix);
+ }
+ }
+
+ /* broadcasts will always be done with the last-used encap. */
+ lp->default_proto[0] = soft->proto;
+
+ /* in striking contrast, the following isn't a hack. */
+ lp->default_proto[pkt.hard.source] = soft->proto;
+ }
+ /* call the protocol-specific receiver. */
+ arc_proto_map[soft->proto]->rx(dev, bufnum, &pkt, length);
+}
+
+
+
+/*
+ * Get the current statistics. This may be called with the card open or
+ * closed.
+ */
+static struct net_device_stats *arcnet_get_stats(struct net_device *dev)
+{
+ struct arcnet_local *lp = dev->priv;
+ return &lp->stats;
+}
+
+
+static void null_rx(struct net_device *dev, int bufnum,
+ struct archdr *pkthdr, int length)
+{
+ BUGMSG(D_PROTO,
+ "rx: don't know how to deal with proto %02Xh from host %02Xh.\n",
+ pkthdr->soft.rfc1201.proto, pkthdr->hard.source);
+}
+
+
+static int null_build_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, uint8_t daddr)
+{
+ struct arcnet_local *lp = dev->priv;
+
+ BUGMSG(D_PROTO,
+ "tx: can't build header for encap %02Xh; load a protocol driver.\n",
+ lp->default_proto[daddr]);
+
+ /* always fails */
+ return 0;
+}
+
+
+/* the "do nothing" prepare_tx function warns that there's nothing to do. */
+static int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
+ int length, int bufnum)
+{
+ struct arcnet_local *lp = dev->priv;
+ struct arc_hardware newpkt;
+
+ BUGMSG(D_PROTO, "tx: no encap for this host; load a protocol driver.\n");
+
+ /* send a packet to myself -- will never get received, of course */
+ newpkt.source = newpkt.dest = dev->dev_addr[0];
+
+ /* only one byte of actual data (and it's random) */
+ newpkt.offset[0] = 0xFF;
+
+ lp->hw.copy_to_card(dev, bufnum, 0, &newpkt, ARC_HDR_SIZE);
+
+ return 1; /* done */
+}
diff --git a/drivers/net/arcnet/capmode.c b/drivers/net/arcnet/capmode.c
new file mode 100644
index 00000000000..16e155b0412
--- /dev/null
+++ b/drivers/net/arcnet/capmode.c
@@ -0,0 +1,296 @@
+/*
+ * Linux ARCnet driver - "cap mode" packet encapsulation.
+ * It adds sequence numbers to packets for communicating between a user space
+ * application and the driver. After a transmit it sends a packet with protocol
+ * byte 0 back up to the userspace containing the sequence number of the packet
+ * plus the transmit-status on the ArcNet.
+ *
+ * Written 2002-4 by Esben Nielsen, Vestas Wind Systems A/S
+ * Derived from arc-rawmode.c by Avery Pennarun.
+ * arc-rawmode was in turned based on skeleton.c, see below.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if_arp.h>
+#include <net/arp.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/arcdevice.h>
+
+#define VERSION "arcnet: cap mode (`c') encapsulation support loaded.\n"
+
+
+static void rx(struct net_device *dev, int bufnum,
+ struct archdr *pkthdr, int length);
+static int build_header(struct sk_buff *skb,
+ struct net_device *dev,
+ unsigned short type,
+ uint8_t daddr);
+static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
+ int bufnum);
+static int ack_tx(struct net_device *dev, int acked);
+
+
+struct ArcProto capmode_proto =
+{
+ 'r',
+ XMTU,
+ 0,
+ rx,
+ build_header,
+ prepare_tx,
+ NULL,
+ ack_tx
+};
+
+
+void arcnet_cap_init(void)
+{
+ int count;
+
+ for (count = 1; count <= 8; count++)
+ if (arc_proto_map[count] == arc_proto_default)
+ arc_proto_map[count] = &capmode_proto;
+
+ /* for cap mode, we only set the bcast proto if there's no better one */
+ if (arc_bcast_proto == arc_proto_default)
+ arc_bcast_proto = &capmode_proto;
+
+ arc_proto_default = &capmode_proto;
+ arc_raw_proto = &capmode_proto;
+}
+
+
+#ifdef MODULE
+
+int __init init_module(void)
+{
+ printk(VERSION);
+ arcnet_cap_init();
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ arcnet_unregister_proto(&capmode_proto);
+}
+
+MODULE_LICENSE("GPL");
+#endif /* MODULE */
+
+
+
+/* packet receiver */
+static void rx(struct net_device *dev, int bufnum,
+ struct archdr *pkthdr, int length)
+{
+ struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+ struct sk_buff *skb;
+ struct archdr *pkt = pkthdr;
+ char *pktbuf, *pkthdrbuf;
+ int ofs;
+
+ BUGMSG(D_DURING, "it's a raw(cap) packet (length=%d)\n", length);
+
+ if (length >= MinTU)
+ ofs = 512 - length;
+ else
+ ofs = 256 - length;
+
+ skb = alloc_skb(length + ARC_HDR_SIZE + sizeof(int), GFP_ATOMIC);
+ if (skb == NULL) {
+ BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
+ lp->stats.rx_dropped++;
+ return;
+ }
+ skb_put(skb, length + ARC_HDR_SIZE + sizeof(int));
+ skb->dev = dev;
+
+ pkt = (struct archdr *) skb->data;
+
+ skb->mac.raw = skb->data;
+ skb_pull(skb, ARC_HDR_SIZE);
+
+ /* up to sizeof(pkt->soft) has already been copied from the card */
+ /* squeeze in an int for the cap encapsulation */
+
+ /* use these variables to be sure we count in bytes, not in
+ sizeof(struct archdr) */
+ pktbuf=(char*)pkt;
+ pkthdrbuf=(char*)pkthdr;
+ memcpy(pktbuf, pkthdrbuf, ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto));
+ memcpy(pktbuf+ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto)+sizeof(int),
+ pkthdrbuf+ARC_HDR_SIZE+sizeof(pkt->soft.cap.proto),
+ sizeof(struct archdr)-ARC_HDR_SIZE-sizeof(pkt->soft.cap.proto));
+
+ if (length > sizeof(pkt->soft))
+ lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
+ pkt->soft.raw + sizeof(pkt->soft)
+ + sizeof(int),
+ length - sizeof(pkt->soft));
+
+ BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
+
+ skb->protocol = __constant_htons(ETH_P_ARCNET);
+;
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+}
+
+
+/*
+ * Create the ARCnet hard/soft headers for cap mode.
+ * There aren't any soft headers in cap mode - not even the protocol id.
+ */
+static int build_header(struct sk_buff *skb,
+ struct net_device *dev,
+ unsigned short type,
+ uint8_t daddr)
+{
+ int hdr_size = ARC_HDR_SIZE;
+ struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
+
+ BUGMSG(D_PROTO, "Preparing header for cap packet %x.\n",
+ *((int*)&pkt->soft.cap.cookie[0]));
+ /*
+ * Set the source hardware address.
+ *
+ * This is pretty pointless for most purposes, but it can help in
+ * debugging. ARCnet does not allow us to change the source address in
+ * the actual packet sent)
+ */
+ pkt->hard.source = *dev->dev_addr;
+
+ /* see linux/net/ethernet/eth.c to see where I got the following */
+
+ if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
+ /*
+ * FIXME: fill in the last byte of the dest ipaddr here to better
+ * comply with RFC1051 in "noarp" mode.
+ */
+ pkt->hard.dest = 0;
+ return hdr_size;
+ }
+ /* otherwise, just fill it in and go! */
+ pkt->hard.dest = daddr;
+
+ return hdr_size; /* success */
+}
+
+
+static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
+ int bufnum)
+{
+ struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+ struct arc_hardware *hard = &pkt->hard;
+ int ofs;
+
+
+ /* hard header is not included in packet length */
+ length -= ARC_HDR_SIZE;
+ /* And neither is the cookie field */
+ length -= sizeof(int);
+
+ BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
+ lp->next_tx, lp->cur_tx, bufnum);
+
+ BUGMSG(D_PROTO, "Sending for cap packet %x.\n",
+ *((int*)&pkt->soft.cap.cookie[0]));
+
+ if (length > XMTU) {
+ /* should never happen! other people already check for this. */
+ BUGMSG(D_NORMAL, "Bug! prepare_tx with size %d (> %d)\n",
+ length, XMTU);
+ length = XMTU;
+ }
+ if (length > MinTU) {
+ hard->offset[0] = 0;
+ hard->offset[1] = ofs = 512 - length;
+ } else if (length > MTU) {
+ hard->offset[0] = 0;
+ hard->offset[1] = ofs = 512 - length - 3;
+ } else
+ hard->offset[0] = ofs = 256 - length;
+
+ BUGMSG(D_DURING, "prepare_tx: length=%d ofs=%d\n",
+ length,ofs);
+
+ // Copy the arcnet-header + the protocol byte down:
+ lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
+ lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft.cap.proto,
+ sizeof(pkt->soft.cap.proto));
+
+ // Skip the extra integer we have written into it as a cookie
+ // but write the rest of the message:
+ lp->hw.copy_to_card(dev, bufnum, ofs+1,
+ ((unsigned char*)&pkt->soft.cap.mes),length-1);
+
+ lp->lastload_dest = hard->dest;
+
+ return 1; /* done */
+}
+
+
+static int ack_tx(struct net_device *dev, int acked)
+{
+ struct arcnet_local *lp = (struct arcnet_local *) dev->priv;
+ struct sk_buff *ackskb;
+ struct archdr *ackpkt;
+ int length=sizeof(struct arc_cap);
+
+ BUGMSG(D_DURING, "capmode: ack_tx: protocol: %x: result: %d\n",
+ lp->outgoing.skb->protocol, acked);
+
+ BUGLVL(D_SKB) arcnet_dump_skb(dev, lp->outgoing.skb, "ack_tx");
+
+ /* Now alloc a skb to send back up through the layers: */
+ ackskb = alloc_skb(length + ARC_HDR_SIZE , GFP_ATOMIC);
+ if (ackskb == NULL) {
+ BUGMSG(D_NORMAL, "Memory squeeze, can't acknowledge.\n");
+ goto free_outskb;
+ }
+
+ skb_put(ackskb, length + ARC_HDR_SIZE );
+ ackskb->dev = dev;
+
+ ackpkt = (struct archdr *) ackskb->data;
+
+ ackskb->mac.raw = ackskb->data;
+ /* skb_pull(ackskb, ARC_HDR_SIZE); */
+
+
+ memcpy(ackpkt, lp->outgoing.skb->data, ARC_HDR_SIZE+sizeof(struct arc_cap));
+ ackpkt->soft.cap.proto=0; /* using protocol 0 for acknowledge */
+ ackpkt->soft.cap.mes.ack=acked;
+
+ BUGMSG(D_PROTO, "Ackknowledge for cap packet %x.\n",
+ *((int*)&ackpkt->soft.cap.cookie[0]));
+
+ ackskb->protocol = __constant_htons(ETH_P_ARCNET);
+
+ BUGLVL(D_SKB) arcnet_dump_skb(dev, ackskb, "ack_tx_recv");
+ netif_rx(ackskb);
+
+ free_outskb:
+ dev_kfree_skb_irq(lp->outgoing.skb);
+ lp->outgoing.proto = NULL; /* We are always finished when in this protocol */
+
+ return 0;
+}
diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c
new file mode 100644
index 00000000000..9289e6103de
--- /dev/null
+++ b/drivers/net/arcnet/com20020-isa.c
@@ -0,0 +1,219 @@
+/*
+ * Linux ARCnet driver - COM20020 chipset support
+ *
+ * Written 1997 by David Woodhouse.
+ * Written 1994-1999 by Avery Pennarun.
+ * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
+ * Derived from skeleton.c by Donald Becker.
+ *
+ * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
+ * for sponsoring the further development of this driver.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/arcdevice.h>
+#include <linux/com20020.h>
+
+#include <asm/io.h>
+
+#define VERSION "arcnet: COM20020 ISA support (by David Woodhouse et al.)\n"
+
+
+/*
+ * We cannot (yet) probe for an IO mapped card, although we can check that
+ * it's where we were told it was, and even do autoirq.
+ */
+static int __init com20020isa_probe(struct net_device *dev)
+{
+ int ioaddr;
+ unsigned long airqmask;
+ struct arcnet_local *lp = dev->priv;
+ int err;
+
+ BUGLVL(D_NORMAL) printk(VERSION);
+
+ ioaddr = dev->base_addr;
+ if (!ioaddr) {
+ BUGMSG(D_NORMAL, "No autoprobe (yet) for IO mapped cards; you "
+ "must specify the base address!\n");
+ return -ENODEV;
+ }
+ if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "arcnet (COM20020)")) {
+ BUGMSG(D_NORMAL, "IO region %xh-%xh already allocated.\n",
+ ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
+ return -ENXIO;
+ }
+ if (ASTATUS() == 0xFF) {
+ BUGMSG(D_NORMAL, "IO address %x empty\n", ioaddr);
+ err = -ENODEV;
+ goto out;
+ }
+ if (com20020_check(dev)) {
+ err = -ENODEV;
+ goto out;
+ }
+
+ if (!dev->irq) {
+ /* if we do this, we're sure to get an IRQ since the
+ * card has just reset and the NORXflag is on until
+ * we tell it to start receiving.
+ */
+ BUGMSG(D_INIT_REASONS, "intmask was %02Xh\n", inb(_INTMASK));
+ outb(0, _INTMASK);
+ airqmask = probe_irq_on();
+ outb(NORXflag, _INTMASK);
+ udelay(1);
+ outb(0, _INTMASK);
+ dev->irq = probe_irq_off(airqmask);
+
+ if (dev->irq <= 0) {
+ BUGMSG(D_INIT_REASONS, "Autoprobe IRQ failed first time\n");
+ airqmask = probe_irq_on();
+ outb(NORXflag, _INTMASK);
+ udelay(5);
+ outb(0, _INTMASK);
+ dev->irq = probe_irq_off(airqmask);
+ if (dev->irq <= 0) {
+ BUGMSG(D_NORMAL, "Autoprobe IRQ failed.\n");
+ err = -ENODEV;
+ goto out;
+ }
+ }
+ }
+
+ lp->card_name = "ISA COM20020";
+ if ((err = com20020_found(dev, 0)) != 0)
+ goto out;
+
+ return 0;
+
+out:
+ release_region(ioaddr, ARCNET_TOTAL_SIZE);
+ return err;
+}
+
+static int node = 0;
+static int io = 0x0; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq = 0; /* or use the insmod io= irq= shmem= options */
+static char device[9]; /* use eg. device="arc1" to change name */
+static int timeout = 3;
+static int backplane = 0;
+static int clockp = 0;
+static int clockm = 0;
+
+module_param(node, int, 0);
+module_param(io, int, 0);
+module_param(irq, int, 0);
+module_param_string(device, device, sizeof(device), 0);
+module_param(timeout, int, 0);
+module_param(backplane, int, 0);
+module_param(clockp, int, 0);
+module_param(clockm, int, 0);
+
+MODULE_LICENSE("GPL");
+
+static struct net_device *my_dev;
+
+static int __init com20020_init(void)
+{
+ struct net_device *dev;
+ struct arcnet_local *lp;
+
+ dev = alloc_arcdev(device);
+ if (!dev)
+ return -ENOMEM;
+
+ if (node && node != 0xff)
+ dev->dev_addr[0] = node;
+
+ lp = dev->priv;
+ lp->backplane = backplane;
+ lp->clockp = clockp & 7;
+ lp->clockm = clockm & 3;
+ lp->timeout = timeout & 3;
+ lp->hw.owner = THIS_MODULE;
+
+ dev->base_addr = io;
+ dev->irq = irq;
+
+ if (dev->irq == 2)
+ dev->irq = 9;
+
+ if (com20020isa_probe(dev)) {
+ free_netdev(dev);
+ return -EIO;
+ }
+
+ my_dev = dev;
+ return 0;
+}
+
+static void __exit com20020_exit(void)
+{
+ unregister_netdev(my_dev);
+ free_irq(my_dev->irq, my_dev);
+ release_region(my_dev->base_addr, ARCNET_TOTAL_SIZE);
+ free_netdev(my_dev);
+}
+
+#ifndef MODULE
+static int __init com20020isa_setup(char *s)
+{
+ int ints[8];
+
+ s = get_options(s, 8, ints);
+ if (!ints[0])
+ return 1;
+
+ switch (ints[0]) {
+ default: /* ERROR */
+ printk("com90xx: Too many arguments.\n");
+ case 6: /* Timeout */
+ timeout = ints[6];
+ case 5: /* CKP value */
+ clockp = ints[5];
+ case 4: /* Backplane flag */
+ backplane = ints[4];
+ case 3: /* Node ID */
+ node = ints[3];
+ case 2: /* IRQ */
+ irq = ints[2];
+ case 1: /* IO address */
+ io = ints[1];
+ }
+ if (*s)
+ snprintf(device, sizeof(device), "%s", s);
+ return 1;
+}
+
+__setup("com20020=", com20020isa_setup);
+
+#endif /* MODULE */
+
+module_init(com20020_init)
+module_exit(com20020_exit)
diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c
new file mode 100644
index 00000000000..96636ca8754
--- /dev/null
+++ b/drivers/net/arcnet/com20020-pci.c
@@ -0,0 +1,189 @@
+/*
+ * Linux ARCnet driver - COM20020 PCI support
+ * Contemporary Controls PCI20 and SOHARD SH-ARC PCI
+ *
+ * Written 1994-1999 by Avery Pennarun,
+ * based on an ISA version by David Woodhouse.
+ * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
+ * Derived from skeleton.c by Donald Becker.
+ *
+ * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
+ * for sponsoring the further development of this driver.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/arcdevice.h>
+#include <linux/com20020.h>
+
+#include <asm/io.h>
+
+
+#define VERSION "arcnet: COM20020 PCI support\n"
+
+/* Module parameters */
+
+static int node;
+static char device[9]; /* use eg. device="arc1" to change name */
+static int timeout = 3;
+static int backplane;
+static int clockp;
+static int clockm;
+
+module_param(node, int, 0);
+module_param_string(device, device, sizeof(device), 0);
+module_param(timeout, int, 0);
+module_param(backplane, int, 0);
+module_param(clockp, int, 0);
+module_param(clockm, int, 0);
+MODULE_LICENSE("GPL");
+
+static int __devinit com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct net_device *dev;
+ struct arcnet_local *lp;
+ int ioaddr, err;
+
+ if (pci_enable_device(pdev))
+ return -EIO;
+ dev = alloc_arcdev(device);
+ if (!dev)
+ return -ENOMEM;
+ lp = dev->priv;
+
+ pci_set_drvdata(pdev, dev);
+
+ // SOHARD needs PCI base addr 4
+ if (pdev->vendor==0x10B5) {
+ BUGMSG(D_NORMAL, "SOHARD\n");
+ ioaddr = pci_resource_start(pdev, 4);
+ }
+ else {
+ BUGMSG(D_NORMAL, "Contemporary Controls\n");
+ ioaddr = pci_resource_start(pdev, 2);
+ }
+
+ if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com20020-pci")) {
+ BUGMSG(D_INIT, "IO region %xh-%xh already allocated.\n",
+ ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
+ err = -EBUSY;
+ goto out_dev;
+ }
+
+ // Dummy access after Reset
+ // ARCNET controller needs this access to detect bustype
+ outb(0x00,ioaddr+1);
+ inb(ioaddr+1);
+
+ dev->base_addr = ioaddr;
+ dev->irq = pdev->irq;
+ dev->dev_addr[0] = node;
+ lp->card_name = "PCI COM20020";
+ lp->card_flags = id->driver_data;
+ lp->backplane = backplane;
+ lp->clockp = clockp & 7;
+ lp->clockm = clockm & 3;
+ lp->timeout = timeout;
+ lp->hw.owner = THIS_MODULE;
+
+ if (ASTATUS() == 0xFF) {
+ BUGMSG(D_NORMAL, "IO address %Xh was reported by PCI BIOS, "
+ "but seems empty!\n", ioaddr);
+ err = -EIO;
+ goto out_port;
+ }
+ if (com20020_check(dev)) {
+ err = -EIO;
+ goto out_port;
+ }
+
+ if ((err = com20020_found(dev, SA_SHIRQ)) != 0)
+ goto out_port;
+
+ return 0;
+
+out_port:
+ release_region(ioaddr, ARCNET_TOTAL_SIZE);
+out_dev:
+ free_netdev(dev);
+ return err;
+}
+
+static void __devexit com20020pci_remove(struct pci_dev *pdev)
+{
+ struct net_device *dev = pci_get_drvdata(pdev);
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
+ free_netdev(dev);
+}
+
+static struct pci_device_id com20020pci_id_table[] = {
+ { 0x1571, 0xa001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x1571, 0xa002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x1571, 0xa003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x1571, 0xa004, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x1571, 0xa005, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x1571, 0xa006, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x1571, 0xa007, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x1571, 0xa008, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { 0x1571, 0xa009, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT },
+ { 0x1571, 0xa00a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT },
+ { 0x1571, 0xa00b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT },
+ { 0x1571, 0xa00c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT },
+ { 0x1571, 0xa00d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT },
+ { 0x1571, 0xa201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
+ { 0x1571, 0xa202, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
+ { 0x1571, 0xa203, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
+ { 0x1571, 0xa204, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
+ { 0x1571, 0xa205, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
+ { 0x1571, 0xa206, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
+ { 0x10B5, 0x9050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
+ {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, com20020pci_id_table);
+
+static struct pci_driver com20020pci_driver = {
+ .name = "com20020",
+ .id_table = com20020pci_id_table,
+ .probe = com20020pci_probe,
+ .remove = __devexit_p(com20020pci_remove),
+};
+
+static int __init com20020pci_init(void)
+{
+ BUGLVL(D_NORMAL) printk(VERSION);
+ return pci_module_init(&com20020pci_driver);
+}
+
+static void __exit com20020pci_cleanup(void)
+{
+ pci_unregister_driver(&com20020pci_driver);
+}
+
+module_init(com20020pci_init)
+module_exit(com20020pci_cleanup)
diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c
new file mode 100644
index 00000000000..0dc70c7b794
--- /dev/null
+++ b/drivers/net/arcnet/com20020.c
@@ -0,0 +1,357 @@
+/*
+ * Linux ARCnet driver - COM20020 chipset support
+ *
+ * Written 1997 by David Woodhouse.
+ * Written 1994-1999 by Avery Pennarun.
+ * Written 1999 by Martin Mares <mj@ucw.cz>.
+ * Derived from skeleton.c by Donald Becker.
+ *
+ * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
+ * for sponsoring the further development of this driver.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/init.h>
+#include <linux/arcdevice.h>
+#include <linux/com20020.h>
+
+#include <asm/io.h>
+
+#define VERSION "arcnet: COM20020 chipset support (by David Woodhouse et al.)\n"
+
+static char *clockrates[] =
+{"10 Mb/s", "Reserved", "5 Mb/s",
+ "2.5 Mb/s", "1.25Mb/s", "625 Kb/s", "312.5 Kb/s",
+ "156.25 Kb/s", "Reserved", "Reserved", "Reserved"};
+
+static void com20020_command(struct net_device *dev, int command);
+static int com20020_status(struct net_device *dev);
+static void com20020_setmask(struct net_device *dev, int mask);
+static int com20020_reset(struct net_device *dev, int really_reset);
+static void com20020_copy_to_card(struct net_device *dev, int bufnum,
+ int offset, void *buf, int count);
+static void com20020_copy_from_card(struct net_device *dev, int bufnum,
+ int offset, void *buf, int count);
+static void com20020_set_mc_list(struct net_device *dev);
+static void com20020_close(struct net_device *);
+
+static void com20020_copy_from_card(struct net_device *dev, int bufnum,
+ int offset, void *buf, int count)
+{
+ int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset;
+
+ /* set up the address register */
+ outb((ofs >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI);
+ outb(ofs & 0xff, _ADDR_LO);
+
+ /* copy the data */
+ TIME("insb", count, insb(_MEMDATA, buf, count));
+}
+
+
+static void com20020_copy_to_card(struct net_device *dev, int bufnum,
+ int offset, void *buf, int count)
+{
+ int ioaddr = dev->base_addr, ofs = 512 * bufnum + offset;
+
+ /* set up the address register */
+ outb((ofs >> 8) | AUTOINCflag, _ADDR_HI);
+ outb(ofs & 0xff, _ADDR_LO);
+
+ /* copy the data */
+ TIME("outsb", count, outsb(_MEMDATA, buf, count));
+}
+
+
+/* Reset the card and check some basic stuff during the detection stage. */
+int com20020_check(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr, status;
+ struct arcnet_local *lp = dev->priv;
+
+ ARCRESET0;
+ mdelay(RESETtime);
+
+ lp->setup = lp->clockm ? 0 : (lp->clockp << 1);
+ lp->setup2 = (lp->clockm << 4) | 8;
+
+ /* CHECK: should we do this for SOHARD cards ? */
+ /* Enable P1Mode for backplane mode */
+ lp->setup = lp->setup | P1MODE;
+
+ SET_SUBADR(SUB_SETUP1);
+ outb(lp->setup, _XREG);
+
+ if (lp->card_flags & ARC_CAN_10MBIT)
+ {
+ SET_SUBADR(SUB_SETUP2);
+ outb(lp->setup2, _XREG);
+
+ /* must now write the magic "restart operation" command */
+ mdelay(1);
+ outb(0x18, _COMMAND);
+ }
+
+ lp->config = 0x21 | (lp->timeout << 3) | (lp->backplane << 2);
+ /* set node ID to 0x42 (but transmitter is disabled, so it's okay) */
+ SETCONF;
+ outb(0x42, ioaddr + BUS_ALIGN*7);
+
+ status = ASTATUS();
+
+ if ((status & 0x99) != (NORXflag | TXFREEflag | RESETflag)) {
+ BUGMSG(D_NORMAL, "status invalid (%Xh).\n", status);
+ return -ENODEV;
+ }
+ BUGMSG(D_INIT_REASONS, "status after reset: %X\n", status);
+
+ /* Enable TX */
+ outb(0x39, _CONFIG);
+ outb(inb(ioaddr + BUS_ALIGN*8), ioaddr + BUS_ALIGN*7);
+
+ ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
+
+ status = ASTATUS();
+ BUGMSG(D_INIT_REASONS, "status after reset acknowledged: %X\n",
+ status);
+
+ /* Read first location of memory */
+ outb(0 | RDDATAflag | AUTOINCflag, _ADDR_HI);
+ outb(0, _ADDR_LO);
+
+ if ((status = inb(_MEMDATA)) != TESTvalue) {
+ BUGMSG(D_NORMAL, "Signature byte not found (%02Xh != D1h).\n",
+ status);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+/* Set up the struct net_device associated with this card. Called after
+ * probing succeeds.
+ */
+int com20020_found(struct net_device *dev, int shared)
+{
+ struct arcnet_local *lp;
+ int ioaddr = dev->base_addr;
+
+ /* Initialize the rest of the device structure. */
+
+ lp = dev->priv;
+
+ lp->hw.owner = THIS_MODULE;
+ lp->hw.command = com20020_command;
+ lp->hw.status = com20020_status;
+ lp->hw.intmask = com20020_setmask;
+ lp->hw.reset = com20020_reset;
+ lp->hw.copy_to_card = com20020_copy_to_card;
+ lp->hw.copy_from_card = com20020_copy_from_card;
+ lp->hw.close = com20020_close;
+
+ dev->set_multicast_list = com20020_set_mc_list;
+
+ if (!dev->dev_addr[0])
+ dev->dev_addr[0] = inb(ioaddr + BUS_ALIGN*8); /* FIXME: do this some other way! */
+
+ SET_SUBADR(SUB_SETUP1);
+ outb(lp->setup, _XREG);
+
+ if (lp->card_flags & ARC_CAN_10MBIT)
+ {
+ SET_SUBADR(SUB_SETUP2);
+ outb(lp->setup2, _XREG);
+
+ /* must now write the magic "restart operation" command */
+ mdelay(1);
+ outb(0x18, _COMMAND);
+ }
+
+ lp->config = 0x20 | (lp->timeout << 3) | (lp->backplane << 2) | 1;
+ /* Default 0x38 + register: Node ID */
+ SETCONF;
+ outb(dev->dev_addr[0], _XREG);
+
+ /* reserve the irq */
+ if (request_irq(dev->irq, &arcnet_interrupt, shared,
+ "arcnet (COM20020)", dev)) {
+ BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
+ return -ENODEV;
+ }
+
+ dev->base_addr = ioaddr;
+
+ BUGMSG(D_NORMAL, "%s: station %02Xh found at %03lXh, IRQ %d.\n",
+ lp->card_name, dev->dev_addr[0], dev->base_addr, dev->irq);
+
+ if (lp->backplane)
+ BUGMSG(D_NORMAL, "Using backplane mode.\n");
+
+ if (lp->timeout != 3)
+ BUGMSG(D_NORMAL, "Using extended timeout value of %d.\n", lp->timeout);
+
+ BUGMSG(D_NORMAL, "Using CKP %d - data rate %s.\n",
+ lp->setup >> 1,
+ clockrates[3 - ((lp->setup2 & 0xF0) >> 4) + ((lp->setup & 0x0F) >> 1)]);
+
+ if (register_netdev(dev)) {
+ free_irq(dev->irq, dev);
+ return -EIO;
+ }
+ return 0;
+}
+
+
+/*
+ * Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+static int com20020_reset(struct net_device *dev, int really_reset)
+{
+ struct arcnet_local *lp = dev->priv;
+ u_int ioaddr = dev->base_addr;
+ u_char inbyte;
+
+ BUGMSG(D_DEBUG, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n",
+ __FILE__,__LINE__,__FUNCTION__,dev,lp,dev->name);
+ BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n",
+ dev->name, ASTATUS());
+
+ BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
+ lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2);
+ /* power-up defaults */
+ SETCONF;
+ BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
+
+ if (really_reset) {
+ /* reset the card */
+ ARCRESET;
+ mdelay(RESETtime * 2); /* COM20020 seems to be slower sometimes */
+ }
+ /* clear flags & end reset */
+ BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
+ ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
+
+ /* verify that the ARCnet signature byte is present */
+ BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
+
+ com20020_copy_from_card(dev, 0, 0, &inbyte, 1);
+ BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
+ if (inbyte != TESTvalue) {
+ BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
+ BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n");
+ return 1;
+ }
+ /* enable extended (512-byte) packets */
+ ACOMMAND(CONFIGcmd | EXTconf);
+ BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__FUNCTION__);
+
+ /* done! return success. */
+ return 0;
+}
+
+
+static void com20020_setmask(struct net_device *dev, int mask)
+{
+ u_int ioaddr = dev->base_addr;
+ BUGMSG(D_DURING, "Setting mask to %x at %x\n",mask,ioaddr);
+ AINTMASK(mask);
+}
+
+
+static void com20020_command(struct net_device *dev, int cmd)
+{
+ u_int ioaddr = dev->base_addr;
+ ACOMMAND(cmd);
+}
+
+
+static int com20020_status(struct net_device *dev)
+{
+ u_int ioaddr = dev->base_addr;
+
+ return ASTATUS() + (ADIAGSTATUS()<<8);
+}
+
+static void com20020_close(struct net_device *dev)
+{
+ struct arcnet_local *lp = dev->priv;
+ int ioaddr = dev->base_addr;
+
+ /* disable transmitter */
+ lp->config &= ~TXENcfg;
+ SETCONF;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1 Promiscuous mode, receive all packets
+ * num_addrs == 0 Normal mode, clear multicast list
+ * num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ * best-effort filtering.
+ * FIXME - do multicast stuff, not just promiscuous.
+ */
+static void com20020_set_mc_list(struct net_device *dev)
+{
+ struct arcnet_local *lp = dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if ((dev->flags & IFF_PROMISC) && (dev->flags & IFF_UP)) { /* Enable promiscuous mode */
+ if (!(lp->setup & PROMISCset))
+ BUGMSG(D_NORMAL, "Setting promiscuous flag...\n");
+ SET_SUBADR(SUB_SETUP1);
+ lp->setup |= PROMISCset;
+ outb(lp->setup, _XREG);
+ } else
+ /* Disable promiscuous mode, use normal mode */
+ {
+ if ((lp->setup & PROMISCset))
+ BUGMSG(D_NORMAL, "Resetting promiscuous flag...\n");
+ SET_SUBADR(SUB_SETUP1);
+ lp->setup &= ~PROMISCset;
+ outb(lp->setup, _XREG);
+ }
+}
+
+#ifdef MODULE
+
+EXPORT_SYMBOL(com20020_check);
+EXPORT_SYMBOL(com20020_found);
+
+MODULE_LICENSE("GPL");
+
+int init_module(void)
+{
+ BUGLVL(D_NORMAL) printk(VERSION);
+ return 0;
+}
+
+void cleanup_module(void)
+{
+}
+
+#endif /* MODULE */
diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c
new file mode 100644
index 00000000000..52c77cbe8c6
--- /dev/null
+++ b/drivers/net/arcnet/com90io.c
@@ -0,0 +1,435 @@
+/*
+ * Linux ARCnet driver - COM90xx chipset (IO-mapped buffers)
+ *
+ * Written 1997 by David Woodhouse.
+ * Written 1994-1999 by Avery Pennarun.
+ * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
+ * Derived from skeleton.c by Donald Becker.
+ *
+ * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
+ * for sponsoring the further development of this driver.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/bootmem.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/arcdevice.h>
+
+
+#define VERSION "arcnet: COM90xx IO-mapped mode support (by David Woodhouse et el.)\n"
+
+
+/* Internal function declarations */
+
+static int com90io_found(struct net_device *dev);
+static void com90io_command(struct net_device *dev, int command);
+static int com90io_status(struct net_device *dev);
+static void com90io_setmask(struct net_device *dev, int mask);
+static int com90io_reset(struct net_device *dev, int really_reset);
+static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count);
+static void com90io_copy_from_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count);
+
+
+/* Handy defines for ARCnet specific stuff */
+
+/* The number of low I/O ports used by the card. */
+#define ARCNET_TOTAL_SIZE 16
+
+/* COM 9026 controller chip --> ARCnet register addresses */
+#define _INTMASK (ioaddr+0) /* writable */
+#define _STATUS (ioaddr+0) /* readable */
+#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
+#define _RESET (ioaddr+8) /* software reset (on read) */
+#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */
+#define _ADDR_HI (ioaddr+15) /* Control registers for said */
+#define _ADDR_LO (ioaddr+14)
+#define _CONFIG (ioaddr+2) /* Configuration register */
+
+#undef ASTATUS
+#undef ACOMMAND
+#undef AINTMASK
+
+#define ASTATUS() inb(_STATUS)
+#define ACOMMAND(cmd) outb((cmd),_COMMAND)
+#define AINTMASK(msk) outb((msk),_INTMASK)
+#define SETCONF() outb((lp->config),_CONFIG)
+
+
+/****************************************************************************
+ * *
+ * IO-mapped operation routines *
+ * *
+ ****************************************************************************/
+
+#undef ONE_AT_A_TIME_TX
+#undef ONE_AT_A_TIME_RX
+
+static u_char get_buffer_byte(struct net_device *dev, unsigned offset)
+{
+ int ioaddr = dev->base_addr;
+
+ outb(offset >> 8, _ADDR_HI);
+ outb(offset & 0xff, _ADDR_LO);
+
+ return inb(_MEMDATA);
+}
+
+#ifdef ONE_AT_A_TIME_TX
+static void put_buffer_byte(struct net_device *dev, unsigned offset, u_char datum)
+{
+ int ioaddr = dev->base_addr;
+
+ outb(offset >> 8, _ADDR_HI);
+ outb(offset & 0xff, _ADDR_LO);
+
+ outb(datum, _MEMDATA);
+}
+
+#endif
+
+
+static void get_whole_buffer(struct net_device *dev, unsigned offset, unsigned length, char *dest)
+{
+ int ioaddr = dev->base_addr;
+
+ outb((offset >> 8) | AUTOINCflag, _ADDR_HI);
+ outb(offset & 0xff, _ADDR_LO);
+
+ while (length--)
+#ifdef ONE_AT_A_TIME_RX
+ *(dest++) = get_buffer_byte(dev, offset++);
+#else
+ *(dest++) = inb(_MEMDATA);
+#endif
+}
+
+static void put_whole_buffer(struct net_device *dev, unsigned offset, unsigned length, char *dest)
+{
+ int ioaddr = dev->base_addr;
+
+ outb((offset >> 8) | AUTOINCflag, _ADDR_HI);
+ outb(offset & 0xff, _ADDR_LO);
+
+ while (length--)
+#ifdef ONE_AT_A_TIME_TX
+ put_buffer_byte(dev, offset++, *(dest++));
+#else
+ outb(*(dest++), _MEMDATA);
+#endif
+}
+
+/*
+ * We cannot probe for an IO mapped card either, although we can check that
+ * it's where we were told it was, and even autoirq
+ */
+static int __init com90io_probe(struct net_device *dev)
+{
+ int ioaddr = dev->base_addr, status;
+ unsigned long airqmask;
+
+ BUGLVL(D_NORMAL) printk(VERSION);
+ BUGLVL(D_NORMAL) printk("E-mail me if you actually test this driver, please!\n");
+
+ if (!ioaddr) {
+ BUGMSG(D_NORMAL, "No autoprobe for IO mapped cards; you "
+ "must specify the base address!\n");
+ return -ENODEV;
+ }
+ if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "com90io probe")) {
+ BUGMSG(D_INIT_REASONS, "IO check_region %x-%x failed.\n",
+ ioaddr, ioaddr + ARCNET_TOTAL_SIZE - 1);
+ return -ENXIO;
+ }
+ if (ASTATUS() == 0xFF) {
+ BUGMSG(D_INIT_REASONS, "IO address %x empty\n", ioaddr);
+ goto err_out;
+ }
+ inb(_RESET);
+ mdelay(RESETtime);
+
+ status = ASTATUS();
+
+ if ((status & 0x9D) != (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
+ BUGMSG(D_INIT_REASONS, "Status invalid (%Xh).\n", status);
+ goto err_out;
+ }
+ BUGMSG(D_INIT_REASONS, "Status after reset: %X\n", status);
+
+ ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
+
+ BUGMSG(D_INIT_REASONS, "Status after reset acknowledged: %X\n", status);
+
+ status = ASTATUS();
+
+ if (status & RESETflag) {
+ BUGMSG(D_INIT_REASONS, "Eternal reset (status=%Xh)\n", status);
+ goto err_out;
+ }
+ outb((0x16 | IOMAPflag) & ~ENABLE16flag, _CONFIG);
+
+ /* Read first loc'n of memory */
+
+ outb(AUTOINCflag, _ADDR_HI);
+ outb(0, _ADDR_LO);
+
+ if ((status = inb(_MEMDATA)) != 0xd1) {
+ BUGMSG(D_INIT_REASONS, "Signature byte not found"
+ " (%Xh instead).\n", status);
+ goto err_out;
+ }
+ if (!dev->irq) {
+ /*
+ * if we do this, we're sure to get an IRQ since the
+ * card has just reset and the NORXflag is on until
+ * we tell it to start receiving.
+ */
+
+ airqmask = probe_irq_on();
+ outb(NORXflag, _INTMASK);
+ udelay(1);
+ outb(0, _INTMASK);
+ dev->irq = probe_irq_off(airqmask);
+
+ if (dev->irq <= 0) {
+ BUGMSG(D_INIT_REASONS, "Autoprobe IRQ failed\n");
+ goto err_out;
+ }
+ }
+ release_region(ioaddr, ARCNET_TOTAL_SIZE); /* end of probing */
+ return com90io_found(dev);
+
+err_out:
+ release_region(ioaddr, ARCNET_TOTAL_SIZE);
+ return -ENODEV;
+}
+
+
+/* Set up the struct net_device associated with this card. Called after
+ * probing succeeds.
+ */
+static int __init com90io_found(struct net_device *dev)
+{
+ struct arcnet_local *lp;
+ int ioaddr = dev->base_addr;
+ int err;
+
+ /* Reserve the irq */
+ if (request_irq(dev->irq, &arcnet_interrupt, 0, "arcnet (COM90xx-IO)", dev)) {
+ BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq);
+ return -ENODEV;
+ }
+ /* Reserve the I/O region - guaranteed to work by check_region */
+ if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE, "arcnet (COM90xx-IO)")) {
+ free_irq(dev->irq, dev);
+ return -EBUSY;
+ }
+
+ lp = dev->priv;
+ lp->card_name = "COM90xx I/O";
+ lp->hw.command = com90io_command;
+ lp->hw.status = com90io_status;
+ lp->hw.intmask = com90io_setmask;
+ lp->hw.reset = com90io_reset;
+ lp->hw.owner = THIS_MODULE;
+ lp->hw.copy_to_card = com90io_copy_to_card;
+ lp->hw.copy_from_card = com90io_copy_from_card;
+
+ lp->config = (0x16 | IOMAPflag) & ~ENABLE16flag;
+ SETCONF();
+
+ /* get and check the station ID from offset 1 in shmem */
+
+ dev->dev_addr[0] = get_buffer_byte(dev, 1);
+
+ err = register_netdev(dev);
+ if (err) {
+ outb((inb(_CONFIG) & ~IOMAPflag), _CONFIG);
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
+ return err;
+ }
+
+ BUGMSG(D_NORMAL, "COM90IO: station %02Xh found at %03lXh, IRQ %d.\n",
+ dev->dev_addr[0], dev->base_addr, dev->irq);
+
+ return 0;
+}
+
+
+/*
+ * Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+static int com90io_reset(struct net_device *dev, int really_reset)
+{
+ struct arcnet_local *lp = dev->priv;
+ short ioaddr = dev->base_addr;
+
+ BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", dev->name, ASTATUS());
+
+ if (really_reset) {
+ /* reset the card */
+ inb(_RESET);
+ mdelay(RESETtime);
+ }
+ /* Set the thing to IO-mapped, 8-bit mode */
+ lp->config = (0x1C | IOMAPflag) & ~ENABLE16flag;
+ SETCONF();
+
+ ACOMMAND(CFLAGScmd | RESETclear); /* clear flags & end reset */
+ ACOMMAND(CFLAGScmd | CONFIGclear);
+
+ /* verify that the ARCnet signature byte is present */
+ if (get_buffer_byte(dev, 0) != TESTvalue) {
+ BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n");
+ return 1;
+ }
+ /* enable extended (512-byte) packets */
+ ACOMMAND(CONFIGcmd | EXTconf);
+
+ /* done! return success. */
+ return 0;
+}
+
+
+static void com90io_command(struct net_device *dev, int cmd)
+{
+ short ioaddr = dev->base_addr;
+
+ ACOMMAND(cmd);
+}
+
+
+static int com90io_status(struct net_device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ return ASTATUS();
+}
+
+
+static void com90io_setmask(struct net_device *dev, int mask)
+{
+ short ioaddr = dev->base_addr;
+
+ AINTMASK(mask);
+}
+
+static void com90io_copy_to_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count)
+{
+ TIME("put_whole_buffer", count, put_whole_buffer(dev, bufnum * 512 + offset, count, buf));
+}
+
+
+static void com90io_copy_from_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count)
+{
+ TIME("get_whole_buffer", count, get_whole_buffer(dev, bufnum * 512 + offset, count, buf));
+}
+
+static int io; /* use the insmod io= irq= shmem= options */
+static int irq;
+static char device[9]; /* use eg. device=arc1 to change name */
+
+module_param(io, int, 0);
+module_param(irq, int, 0);
+module_param_string(device, device, sizeof(device), 0);
+MODULE_LICENSE("GPL");
+
+#ifndef MODULE
+static int __init com90io_setup(char *s)
+{
+ int ints[4];
+ s = get_options(s, 4, ints);
+ if (!ints[0])
+ return 0;
+ switch (ints[0]) {
+ default: /* ERROR */
+ printk("com90io: Too many arguments.\n");
+ case 2: /* IRQ */
+ irq = ints[2];
+ case 1: /* IO address */
+ io = ints[1];
+ }
+ if (*s)
+ snprintf(device, sizeof(device), "%s", s);
+ return 1;
+}
+__setup("com90io=", com90io_setup);
+#endif
+
+static struct net_device *my_dev;
+
+static int __init com90io_init(void)
+{
+ struct net_device *dev;
+ int err;
+
+ dev = alloc_arcdev(device);
+ if (!dev)
+ return -ENOMEM;
+
+ SET_MODULE_OWNER(dev);
+
+ dev->base_addr = io;
+ dev->irq = irq;
+ if (dev->irq == 2)
+ dev->irq = 9;
+
+ err = com90io_probe(dev);
+
+ if (err) {
+ free_netdev(dev);
+ return err;
+ }
+
+ my_dev = dev;
+ return 0;
+}
+
+static void __exit com90io_exit(void)
+{
+ struct net_device *dev = my_dev;
+ int ioaddr = dev->base_addr;
+
+ unregister_netdev(dev);
+
+ /* Set the thing back to MMAP mode, in case the old driver is loaded later */
+ outb((inb(_CONFIG) & ~IOMAPflag), _CONFIG);
+
+ free_irq(dev->irq, dev);
+ release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
+ free_netdev(dev);
+}
+
+module_init(com90io_init)
+module_exit(com90io_exit)
diff --git a/drivers/net/arcnet/com90xx.c b/drivers/net/arcnet/com90xx.c
new file mode 100644
index 00000000000..6c2c9b9ac6d
--- /dev/null
+++ b/drivers/net/arcnet/com90xx.c
@@ -0,0 +1,646 @@
+/*
+ * Linux ARCnet driver - COM90xx chipset (memory-mapped buffers)
+ *
+ * Written 1994-1999 by Avery Pennarun.
+ * Written 1999 by Martin Mares <mj@ucw.cz>.
+ * Derived from skeleton.c by Donald Becker.
+ *
+ * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
+ * for sponsoring the further development of this driver.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <asm/io.h>
+#include <linux/arcdevice.h>
+
+
+#define VERSION "arcnet: COM90xx chipset support\n"
+
+
+/* Define this to speed up the autoprobe by assuming if only one io port and
+ * shmem are left in the list at Stage 5, they must correspond to each
+ * other.
+ *
+ * This is undefined by default because it might not always be true, and the
+ * extra check makes the autoprobe even more careful. Speed demons can turn
+ * it on - I think it should be fine if you only have one ARCnet card
+ * installed.
+ *
+ * If no ARCnet cards are installed, this delay never happens anyway and thus
+ * the option has no effect.
+ */
+#undef FAST_PROBE
+
+
+/* Internal function declarations */
+static int com90xx_found(int ioaddr, int airq, u_long shmem);
+static void com90xx_command(struct net_device *dev, int command);
+static int com90xx_status(struct net_device *dev);
+static void com90xx_setmask(struct net_device *dev, int mask);
+static int com90xx_reset(struct net_device *dev, int really_reset);
+static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count);
+static void com90xx_copy_from_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count);
+
+/* Known ARCnet cards */
+
+static struct net_device *cards[16];
+static int numcards;
+
+/* Handy defines for ARCnet specific stuff */
+
+/* The number of low I/O ports used by the card */
+#define ARCNET_TOTAL_SIZE 16
+
+/* Amount of I/O memory used by the card */
+#define BUFFER_SIZE (512)
+#define MIRROR_SIZE (BUFFER_SIZE*4)
+
+/* COM 9026 controller chip --> ARCnet register addresses */
+#define _INTMASK (ioaddr+0) /* writable */
+#define _STATUS (ioaddr+0) /* readable */
+#define _COMMAND (ioaddr+1) /* writable, returns random vals on read (?) */
+#define _CONFIG (ioaddr+2) /* Configuration register */
+#define _RESET (ioaddr+8) /* software reset (on read) */
+#define _MEMDATA (ioaddr+12) /* Data port for IO-mapped memory */
+#define _ADDR_HI (ioaddr+15) /* Control registers for said */
+#define _ADDR_LO (ioaddr+14)
+
+#undef ASTATUS
+#undef ACOMMAND
+#undef AINTMASK
+
+#define ASTATUS() inb(_STATUS)
+#define ACOMMAND(cmd) outb((cmd),_COMMAND)
+#define AINTMASK(msk) outb((msk),_INTMASK)
+
+
+static int com90xx_skip_probe __initdata = 0;
+
+/* Module parameters */
+
+static int io; /* use the insmod io= irq= shmem= options */
+static int irq;
+static int shmem;
+static char device[9]; /* use eg. device=arc1 to change name */
+
+module_param(io, int, 0);
+module_param(irq, int, 0);
+module_param(shmem, int, 0);
+module_param_string(device, device, sizeof(device), 0);
+
+static void __init com90xx_probe(void)
+{
+ int count, status, ioaddr, numprint, airq, openparen = 0;
+ unsigned long airqmask;
+ int ports[(0x3f0 - 0x200) / 16 + 1] =
+ {0};
+ u_long shmems[(0xFF800 - 0xA0000) / 2048 + 1] =
+ {0};
+ int numports, numshmems, *port;
+ u_long *p;
+
+ if (!io && !irq && !shmem && !*device && com90xx_skip_probe)
+ return;
+
+ BUGLVL(D_NORMAL) printk(VERSION);
+
+ /* set up the arrays where we'll store the possible probe addresses */
+ numports = numshmems = 0;
+ if (io)
+ ports[numports++] = io;
+ else
+ for (count = 0x200; count <= 0x3f0; count += 16)
+ ports[numports++] = count;
+ if (shmem)
+ shmems[numshmems++] = shmem;
+ else
+ for (count = 0xA0000; count <= 0xFF800; count += 2048)
+ shmems[numshmems++] = count;
+
+ /* Stage 1: abandon any reserved ports, or ones with status==0xFF
+ * (empty), and reset any others by reading the reset port.
+ */
+ numprint = -1;
+ for (port = &ports[0]; port - ports < numports; port++) {
+ numprint++;
+ numprint %= 8;
+ if (!numprint) {
+ BUGMSG2(D_INIT, "\n");
+ BUGMSG2(D_INIT, "S1: ");
+ }
+ BUGMSG2(D_INIT, "%Xh ", *port);
+
+ ioaddr = *port;
+
+ if (!request_region(*port, ARCNET_TOTAL_SIZE, "arcnet (90xx)")) {
+ BUGMSG2(D_INIT_REASONS, "(request_region)\n");
+ BUGMSG2(D_INIT_REASONS, "S1: ");
+ BUGLVL(D_INIT_REASONS) numprint = 0;
+ *port-- = ports[--numports];
+ continue;
+ }
+ if (ASTATUS() == 0xFF) {
+ BUGMSG2(D_INIT_REASONS, "(empty)\n");
+ BUGMSG2(D_INIT_REASONS, "S1: ");
+ BUGLVL(D_INIT_REASONS) numprint = 0;
+ release_region(*port, ARCNET_TOTAL_SIZE);
+ *port-- = ports[--numports];
+ continue;
+ }
+ inb(_RESET); /* begin resetting card */
+
+ BUGMSG2(D_INIT_REASONS, "\n");
+ BUGMSG2(D_INIT_REASONS, "S1: ");
+ BUGLVL(D_INIT_REASONS) numprint = 0;
+ }
+ BUGMSG2(D_INIT, "\n");
+
+ if (!numports) {
+ BUGMSG2(D_NORMAL, "S1: No ARCnet cards found.\n");
+ return;
+ }
+ /* Stage 2: we have now reset any possible ARCnet cards, so we can't
+ * do anything until they finish. If D_INIT, print the list of
+ * cards that are left.
+ */
+ numprint = -1;
+ for (port = &ports[0]; port < ports + numports; port++) {
+ numprint++;
+ numprint %= 8;
+ if (!numprint) {
+ BUGMSG2(D_INIT, "\n");
+ BUGMSG2(D_INIT, "S2: ");
+ }
+ BUGMSG2(D_INIT, "%Xh ", *port);
+ }
+ BUGMSG2(D_INIT, "\n");
+ mdelay(RESETtime);
+
+ /* Stage 3: abandon any shmem addresses that don't have the signature
+ * 0xD1 byte in the right place, or are read-only.
+ */
+ numprint = -1;
+ for (p = &shmems[0]; p < shmems + numshmems; p++) {
+ u_long ptr = *p;
+
+ numprint++;
+ numprint %= 8;
+ if (!numprint) {
+ BUGMSG2(D_INIT, "\n");
+ BUGMSG2(D_INIT, "S3: ");
+ }
+ BUGMSG2(D_INIT, "%lXh ", *p);
+
+ if (!request_mem_region(*p, BUFFER_SIZE, "arcnet (90xx)")) {
+ BUGMSG2(D_INIT_REASONS, "(request_mem_region)\n");
+ BUGMSG2(D_INIT_REASONS, "Stage 3: ");
+ BUGLVL(D_INIT_REASONS) numprint = 0;
+ *p-- = shmems[--numshmems];
+ continue;
+ }
+ if (isa_readb(ptr) != TESTvalue) {
+ BUGMSG2(D_INIT_REASONS, "(%02Xh != %02Xh)\n",
+ isa_readb(ptr), TESTvalue);
+ BUGMSG2(D_INIT_REASONS, "S3: ");
+ BUGLVL(D_INIT_REASONS) numprint = 0;
+ release_mem_region(*p, BUFFER_SIZE);
+ *p-- = shmems[--numshmems];
+ continue;
+ }
+ /* By writing 0x42 to the TESTvalue location, we also make
+ * sure no "mirror" shmem areas show up - if they occur
+ * in another pass through this loop, they will be discarded
+ * because *cptr != TESTvalue.
+ */
+ isa_writeb(0x42, ptr);
+ if (isa_readb(ptr) != 0x42) {
+ BUGMSG2(D_INIT_REASONS, "(read only)\n");
+ BUGMSG2(D_INIT_REASONS, "S3: ");
+ release_mem_region(*p, BUFFER_SIZE);
+ *p-- = shmems[--numshmems];
+ continue;
+ }
+ BUGMSG2(D_INIT_REASONS, "\n");
+ BUGMSG2(D_INIT_REASONS, "S3: ");
+ BUGLVL(D_INIT_REASONS) numprint = 0;
+ }
+ BUGMSG2(D_INIT, "\n");
+
+ if (!numshmems) {
+ BUGMSG2(D_NORMAL, "S3: No ARCnet cards found.\n");
+ for (port = &ports[0]; port < ports + numports; port++)
+ release_region(*port, ARCNET_TOTAL_SIZE);
+ return;
+ }
+ /* Stage 4: something of a dummy, to report the shmems that are
+ * still possible after stage 3.
+ */
+ numprint = -1;
+ for (p = &shmems[0]; p < shmems + numshmems; p++) {
+ numprint++;
+ numprint %= 8;
+ if (!numprint) {
+ BUGMSG2(D_INIT, "\n");
+ BUGMSG2(D_INIT, "S4: ");
+ }
+ BUGMSG2(D_INIT, "%lXh ", *p);
+ }
+ BUGMSG2(D_INIT, "\n");
+
+ /* Stage 5: for any ports that have the correct status, can disable
+ * the RESET flag, and (if no irq is given) generate an autoirq,
+ * register an ARCnet device.
+ *
+ * Currently, we can only register one device per probe, so quit
+ * after the first one is found.
+ */
+ numprint = -1;
+ for (port = &ports[0]; port < ports + numports; port++) {
+ int found = 0;
+ numprint++;
+ numprint %= 8;
+ if (!numprint) {
+ BUGMSG2(D_INIT, "\n");
+ BUGMSG2(D_INIT, "S5: ");
+ }
+ BUGMSG2(D_INIT, "%Xh ", *port);
+
+ ioaddr = *port;
+ status = ASTATUS();
+
+ if ((status & 0x9D)
+ != (NORXflag | RECONflag | TXFREEflag | RESETflag)) {
+ BUGMSG2(D_INIT_REASONS, "(status=%Xh)\n", status);
+ BUGMSG2(D_INIT_REASONS, "S5: ");
+ BUGLVL(D_INIT_REASONS) numprint = 0;
+ release_region(*port, ARCNET_TOTAL_SIZE);
+ *port-- = ports[--numports];
+ continue;
+ }
+ ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear);
+ status = ASTATUS();
+ if (status & RESETflag) {
+ BUGMSG2(D_INIT_REASONS, " (eternal reset, status=%Xh)\n",
+ status);
+ BUGMSG2(D_INIT_REASONS, "S5: ");
+ BUGLVL(D_INIT_REASONS) numprint = 0;
+ release_region(*port, ARCNET_TOTAL_SIZE);
+ *port-- = ports[--numports];
+ continue;
+ }
+ /* skip this completely if an IRQ was given, because maybe
+ * we're on a machine that locks during autoirq!
+ */
+ if (!irq) {
+ /* if we do this, we're sure to get an IRQ since the
+ * card has just reset and the NORXflag is on until
+ * we tell it to start receiving.
+ */
+ airqmask = probe_irq_on();
+ AINTMASK(NORXflag);
+ udelay(1);
+ AINTMASK(0);
+ airq = probe_irq_off(airqmask);
+
+ if (airq <= 0) {
+ BUGMSG2(D_INIT_REASONS, "(airq=%d)\n", airq);
+ BUGMSG2(D_INIT_REASONS, "S5: ");
+ BUGLVL(D_INIT_REASONS) numprint = 0;
+ release_region(*port, ARCNET_TOTAL_SIZE);
+ *port-- = ports[--numports];
+ continue;
+ }
+ } else {
+ airq = irq;
+ }
+
+ BUGMSG2(D_INIT, "(%d,", airq);
+ openparen = 1;
+
+ /* Everything seems okay. But which shmem, if any, puts
+ * back its signature byte when the card is reset?
+ *
+ * If there are multiple cards installed, there might be
+ * multiple shmems still in the list.
+ */
+#ifdef FAST_PROBE
+ if (numports > 1 || numshmems > 1) {
+ inb(_RESET);
+ mdelay(RESETtime);
+ } else {
+ /* just one shmem and port, assume they match */
+ isa_writeb(TESTvalue, shmems[0]);
+ }
+#else
+ inb(_RESET);
+ mdelay(RESETtime);
+#endif
+
+ for (p = &shmems[0]; p < shmems + numshmems; p++) {
+ u_long ptr = *p;
+
+ if (isa_readb(ptr) == TESTvalue) { /* found one */
+ BUGMSG2(D_INIT, "%lXh)\n", *p);
+ openparen = 0;
+
+ /* register the card */
+ if (com90xx_found(*port, airq, *p) == 0)
+ found = 1;
+ numprint = -1;
+
+ /* remove shmem from the list */
+ *p = shmems[--numshmems];
+ break; /* go to the next I/O port */
+ } else {
+ BUGMSG2(D_INIT_REASONS, "%Xh-", isa_readb(ptr));
+ }
+ }
+
+ if (openparen) {
+ BUGLVL(D_INIT) printk("no matching shmem)\n");
+ BUGLVL(D_INIT_REASONS) printk("S5: ");
+ BUGLVL(D_INIT_REASONS) numprint = 0;
+ }
+ if (!found)
+ release_region(*port, ARCNET_TOTAL_SIZE);
+ *port-- = ports[--numports];
+ }
+
+ BUGLVL(D_INIT_REASONS) printk("\n");
+
+ /* Now put back TESTvalue on all leftover shmems. */
+ for (p = &shmems[0]; p < shmems + numshmems; p++) {
+ isa_writeb(TESTvalue, *p);
+ release_mem_region(*p, BUFFER_SIZE);
+ }
+}
+
+
+/* Set up the struct net_device associated with this card. Called after
+ * probing succeeds.
+ */
+static int __init com90xx_found(int ioaddr, int airq, u_long shmem)
+{
+ struct net_device *dev = NULL;
+ struct arcnet_local *lp;
+ u_long first_mirror, last_mirror;
+ int mirror_size;
+
+ /* allocate struct net_device */
+ dev = alloc_arcdev(device);
+ if (!dev) {
+ BUGMSG2(D_NORMAL, "com90xx: Can't allocate device!\n");
+ release_mem_region(shmem, BUFFER_SIZE);
+ return -ENOMEM;
+ }
+ lp = dev->priv;
+ /* find the real shared memory start/end points, including mirrors */
+
+ /* guess the actual size of one "memory mirror" - the number of
+ * bytes between copies of the shared memory. On most cards, it's
+ * 2k (or there are no mirrors at all) but on some, it's 4k.
+ */
+ mirror_size = MIRROR_SIZE;
+ if (isa_readb(shmem) == TESTvalue
+ && isa_readb(shmem - mirror_size) != TESTvalue
+ && isa_readb(shmem - 2 * mirror_size) == TESTvalue)
+ mirror_size *= 2;
+
+ first_mirror = last_mirror = shmem;
+ while (isa_readb(first_mirror) == TESTvalue)
+ first_mirror -= mirror_size;
+ first_mirror += mirror_size;
+
+ while (isa_readb(last_mirror) == TESTvalue)
+ last_mirror += mirror_size;
+ last_mirror -= mirror_size;
+
+ dev->mem_start = first_mirror;
+ dev->mem_end = last_mirror + MIRROR_SIZE - 1;
+
+ release_mem_region(shmem, BUFFER_SIZE);
+ if (!request_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1, "arcnet (90xx)"))
+ goto err_free_dev;
+
+ /* reserve the irq */
+ if (request_irq(airq, &arcnet_interrupt, 0, "arcnet (90xx)", dev)) {
+ BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", airq);
+ goto err_release_mem;
+ }
+ dev->irq = airq;
+
+ /* Initialize the rest of the device structure. */
+ lp->card_name = "COM90xx";
+ lp->hw.command = com90xx_command;
+ lp->hw.status = com90xx_status;
+ lp->hw.intmask = com90xx_setmask;
+ lp->hw.reset = com90xx_reset;
+ lp->hw.owner = THIS_MODULE;
+ lp->hw.copy_to_card = com90xx_copy_to_card;
+ lp->hw.copy_from_card = com90xx_copy_from_card;
+ lp->mem_start = ioremap(dev->mem_start, dev->mem_end - dev->mem_start + 1);
+ if (!lp->mem_start) {
+ BUGMSG(D_NORMAL, "Can't remap device memory!\n");
+ goto err_free_irq;
+ }
+
+ /* get and check the station ID from offset 1 in shmem */
+ dev->dev_addr[0] = readb(lp->mem_start + 1);
+
+ dev->base_addr = ioaddr;
+
+ BUGMSG(D_NORMAL, "COM90xx station %02Xh found at %03lXh, IRQ %d, "
+ "ShMem %lXh (%ld*%xh).\n",
+ dev->dev_addr[0],
+ dev->base_addr, dev->irq, dev->mem_start,
+ (dev->mem_end - dev->mem_start + 1) / mirror_size, mirror_size);
+
+ if (register_netdev(dev))
+ goto err_unmap;
+
+ cards[numcards++] = dev;
+ return 0;
+
+err_unmap:
+ iounmap(lp->mem_start);
+err_free_irq:
+ free_irq(dev->irq, dev);
+err_release_mem:
+ release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
+err_free_dev:
+ free_netdev(dev);
+ return -EIO;
+}
+
+
+static void com90xx_command(struct net_device *dev, int cmd)
+{
+ short ioaddr = dev->base_addr;
+
+ ACOMMAND(cmd);
+}
+
+
+static int com90xx_status(struct net_device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ return ASTATUS();
+}
+
+
+static void com90xx_setmask(struct net_device *dev, int mask)
+{
+ short ioaddr = dev->base_addr;
+
+ AINTMASK(mask);
+}
+
+
+/*
+ * Do a hardware reset on the card, and set up necessary registers.
+ *
+ * This should be called as little as possible, because it disrupts the
+ * token on the network (causes a RECON) and requires a significant delay.
+ *
+ * However, it does make sure the card is in a defined state.
+ */
+int com90xx_reset(struct net_device *dev, int really_reset)
+{
+ struct arcnet_local *lp = dev->priv;
+ short ioaddr = dev->base_addr;
+
+ BUGMSG(D_INIT, "Resetting (status=%02Xh)\n", ASTATUS());
+
+ if (really_reset) {
+ /* reset the card */
+ inb(_RESET);
+ mdelay(RESETtime);
+ }
+ ACOMMAND(CFLAGScmd | RESETclear); /* clear flags & end reset */
+ ACOMMAND(CFLAGScmd | CONFIGclear);
+
+ /* don't do this until we verify that it doesn't hurt older cards! */
+ /* outb(inb(_CONFIG) | ENABLE16flag, _CONFIG); */
+
+ /* verify that the ARCnet signature byte is present */
+ if (readb(lp->mem_start) != TESTvalue) {
+ if (really_reset)
+ BUGMSG(D_NORMAL, "reset failed: TESTvalue not present.\n");
+ return 1;
+ }
+ /* enable extended (512-byte) packets */
+ ACOMMAND(CONFIGcmd | EXTconf);
+
+ /* clean out all the memory to make debugging make more sense :) */
+ BUGLVL(D_DURING)
+ memset_io(lp->mem_start, 0x42, 2048);
+
+ /* done! return success. */
+ return 0;
+}
+
+static void com90xx_copy_to_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count)
+{
+ struct arcnet_local *lp = dev->priv;
+ void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
+ TIME("memcpy_toio", count, memcpy_toio(memaddr, buf, count));
+}
+
+
+static void com90xx_copy_from_card(struct net_device *dev, int bufnum, int offset,
+ void *buf, int count)
+{
+ struct arcnet_local *lp = dev->priv;
+ void __iomem *memaddr = lp->mem_start + bufnum * 512 + offset;
+ TIME("memcpy_fromio", count, memcpy_fromio(buf, memaddr, count));
+}
+
+
+MODULE_LICENSE("GPL");
+
+static int __init com90xx_init(void)
+{
+ if (irq == 2)
+ irq = 9;
+ com90xx_probe();
+ if (!numcards)
+ return -EIO;
+ return 0;
+}
+
+static void __exit com90xx_exit(void)
+{
+ struct net_device *dev;
+ struct arcnet_local *lp;
+ int count;
+
+ for (count = 0; count < numcards; count++) {
+ dev = cards[count];
+ lp = dev->priv;
+
+ unregister_netdev(dev);
+ free_irq(dev->irq, dev);
+ iounmap(lp->mem_start);
+ release_region(dev->base_addr, ARCNET_TOTAL_SIZE);
+ release_mem_region(dev->mem_start, dev->mem_end - dev->mem_start + 1);
+ free_netdev(dev);
+ }
+}
+
+module_init(com90xx_init);
+module_exit(com90xx_exit);
+
+#ifndef MODULE
+static int __init com90xx_setup(char *s)
+{
+ int ints[8];
+
+ s = get_options(s, 8, ints);
+ if (!ints[0] && !*s) {
+ printk("com90xx: Disabled.\n");
+ return 1;
+ }
+
+ switch (ints[0]) {
+ default: /* ERROR */
+ printk("com90xx: Too many arguments.\n");
+ case 3: /* Mem address */
+ shmem = ints[3];
+ case 2: /* IRQ */
+ irq = ints[2];
+ case 1: /* IO address */
+ io = ints[1];
+ }
+
+ if (*s)
+ snprintf(device, sizeof(device), "%s", s);
+
+ return 1;
+}
+
+__setup("com90xx=", com90xx_setup);
+#endif
diff --git a/drivers/net/arcnet/rfc1051.c b/drivers/net/arcnet/rfc1051.c
new file mode 100644
index 00000000000..6d7913704fb
--- /dev/null
+++ b/drivers/net/arcnet/rfc1051.c
@@ -0,0 +1,253 @@
+/*
+ * Linux ARCnet driver - RFC1051 ("simple" standard) packet encapsulation
+ *
+ * Written 1994-1999 by Avery Pennarun.
+ * Derived from skeleton.c by Donald Becker.
+ *
+ * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
+ * for sponsoring the further development of this driver.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if_arp.h>
+#include <net/arp.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/arcdevice.h>
+
+#define VERSION "arcnet: RFC1051 \"simple standard\" (`s') encapsulation support loaded.\n"
+
+
+static unsigned short type_trans(struct sk_buff *skb, struct net_device *dev);
+static void rx(struct net_device *dev, int bufnum,
+ struct archdr *pkthdr, int length);
+static int build_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, uint8_t daddr);
+static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
+ int bufnum);
+
+
+struct ArcProto rfc1051_proto =
+{
+ .suffix = 's',
+ .mtu = XMTU - RFC1051_HDR_SIZE,
+ .is_ip = 1,
+ .rx = rx,
+ .build_header = build_header,
+ .prepare_tx = prepare_tx,
+ .continue_tx = NULL,
+ .ack_tx = NULL
+};
+
+
+static int __init arcnet_rfc1051_init(void)
+{
+ printk(VERSION);
+
+ arc_proto_map[ARC_P_IP_RFC1051]
+ = arc_proto_map[ARC_P_ARP_RFC1051]
+ = &rfc1051_proto;
+
+ /* if someone else already owns the broadcast, we won't take it */
+ if (arc_bcast_proto == arc_proto_default)
+ arc_bcast_proto = &rfc1051_proto;
+
+ return 0;
+}
+
+static void __exit arcnet_rfc1051_exit(void)
+{
+ arcnet_unregister_proto(&rfc1051_proto);
+}
+
+module_init(arcnet_rfc1051_init);
+module_exit(arcnet_rfc1051_exit);
+
+MODULE_LICENSE("GPL");
+
+/*
+ * Determine a packet's protocol ID.
+ *
+ * With ARCnet we have to convert everything to Ethernet-style stuff.
+ */
+static unsigned short type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+ struct arcnet_local *lp = dev->priv;
+ struct archdr *pkt = (struct archdr *) skb->data;
+ struct arc_rfc1051 *soft = &pkt->soft.rfc1051;
+ int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
+
+ /* Pull off the arcnet header. */
+ skb->mac.raw = skb->data;
+ skb_pull(skb, hdr_size);
+
+ if (pkt->hard.dest == 0)
+ skb->pkt_type = PACKET_BROADCAST;
+ else if (dev->flags & IFF_PROMISC) {
+ /* if we're not sending to ourselves :) */
+ if (pkt->hard.dest != dev->dev_addr[0])
+ skb->pkt_type = PACKET_OTHERHOST;
+ }
+ /* now return the protocol number */
+ switch (soft->proto) {
+ case ARC_P_IP_RFC1051:
+ return htons(ETH_P_IP);
+ case ARC_P_ARP_RFC1051:
+ return htons(ETH_P_ARP);
+
+ default:
+ lp->stats.rx_errors++;
+ lp->stats.rx_crc_errors++;
+ return 0;
+ }
+
+ return htons(ETH_P_IP);
+}
+
+
+/* packet receiver */
+static void rx(struct net_device *dev, int bufnum,
+ struct archdr *pkthdr, int length)
+{
+ struct arcnet_local *lp = dev->priv;
+ struct sk_buff *skb;
+ struct archdr *pkt = pkthdr;
+ int ofs;
+
+ BUGMSG(D_DURING, "it's a raw packet (length=%d)\n", length);
+
+ if (length >= MinTU)
+ ofs = 512 - length;
+ else
+ ofs = 256 - length;
+
+ skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
+ if (skb == NULL) {
+ BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
+ lp->stats.rx_dropped++;
+ return;
+ }
+ skb_put(skb, length + ARC_HDR_SIZE);
+ skb->dev = dev;
+
+ pkt = (struct archdr *) skb->data;
+
+ /* up to sizeof(pkt->soft) has already been copied from the card */
+ memcpy(pkt, pkthdr, sizeof(struct archdr));
+ if (length > sizeof(pkt->soft))
+ lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
+ pkt->soft.raw + sizeof(pkt->soft),
+ length - sizeof(pkt->soft));
+
+ BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
+
+ skb->protocol = type_trans(skb, dev);
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+}
+
+
+/*
+ * Create the ARCnet hard/soft headers for RFC1051.
+ */
+static int build_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, uint8_t daddr)
+{
+ struct arcnet_local *lp = dev->priv;
+ int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE;
+ struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
+ struct arc_rfc1051 *soft = &pkt->soft.rfc1051;
+
+ /* set the protocol ID according to RFC1051 */
+ switch (type) {
+ case ETH_P_IP:
+ soft->proto = ARC_P_IP_RFC1051;
+ break;
+ case ETH_P_ARP:
+ soft->proto = ARC_P_ARP_RFC1051;
+ break;
+ default:
+ BUGMSG(D_NORMAL, "RFC1051: I don't understand protocol %d (%Xh)\n",
+ type, type);
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+ return 0;
+ }
+
+
+ /*
+ * Set the source hardware address.
+ *
+ * This is pretty pointless for most purposes, but it can help in
+ * debugging. ARCnet does not allow us to change the source address in
+ * the actual packet sent)
+ */
+ pkt->hard.source = *dev->dev_addr;
+
+ /* see linux/net/ethernet/eth.c to see where I got the following */
+
+ if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
+ /*
+ * FIXME: fill in the last byte of the dest ipaddr here to better
+ * comply with RFC1051 in "noarp" mode.
+ */
+ pkt->hard.dest = 0;
+ return hdr_size;
+ }
+ /* otherwise, just fill it in and go! */
+ pkt->hard.dest = daddr;
+
+ return hdr_size; /* success */
+}
+
+
+static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
+ int bufnum)
+{
+ struct arcnet_local *lp = dev->priv;
+ struct arc_hardware *hard = &pkt->hard;
+ int ofs;
+
+ BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
+ lp->next_tx, lp->cur_tx, bufnum);
+
+ length -= ARC_HDR_SIZE; /* hard header is not included in packet length */
+
+ if (length > XMTU) {
+ /* should never happen! other people already check for this. */
+ BUGMSG(D_NORMAL, "Bug! prepare_tx with size %d (> %d)\n",
+ length, XMTU);
+ length = XMTU;
+ }
+ if (length > MinTU) {
+ hard->offset[0] = 0;
+ hard->offset[1] = ofs = 512 - length;
+ } else if (length > MTU) {
+ hard->offset[0] = 0;
+ hard->offset[1] = ofs = 512 - length - 3;
+ } else
+ hard->offset[0] = ofs = 256 - length;
+
+ lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
+ lp->hw.copy_to_card(dev, bufnum, ofs, &pkt->soft, length);
+
+ lp->lastload_dest = hard->dest;
+
+ return 1; /* done */
+}
diff --git a/drivers/net/arcnet/rfc1201.c b/drivers/net/arcnet/rfc1201.c
new file mode 100644
index 00000000000..6b6ae4bf3d3
--- /dev/null
+++ b/drivers/net/arcnet/rfc1201.c
@@ -0,0 +1,549 @@
+/*
+ * Linux ARCnet driver - RFC1201 (standard) packet encapsulation
+ *
+ * Written 1994-1999 by Avery Pennarun.
+ * Derived from skeleton.c by Donald Becker.
+ *
+ * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
+ * for sponsoring the further development of this driver.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ *
+ * **********************
+ *
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/arcdevice.h>
+
+MODULE_LICENSE("GPL");
+#define VERSION "arcnet: RFC1201 \"standard\" (`a') encapsulation support loaded.\n"
+
+
+static unsigned short type_trans(struct sk_buff *skb, struct net_device *dev);
+static void rx(struct net_device *dev, int bufnum,
+ struct archdr *pkthdr, int length);
+static int build_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, uint8_t daddr);
+static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
+ int bufnum);
+static int continue_tx(struct net_device *dev, int bufnum);
+
+struct ArcProto rfc1201_proto =
+{
+ .suffix = 'a',
+ .mtu = 1500, /* could be more, but some receivers can't handle it... */
+ .is_ip = 1, /* This is for sending IP and ARP packages */
+ .rx = rx,
+ .build_header = build_header,
+ .prepare_tx = prepare_tx,
+ .continue_tx = continue_tx,
+ .ack_tx = NULL
+};
+
+
+static int __init arcnet_rfc1201_init(void)
+{
+ printk(VERSION);
+
+ arc_proto_map[ARC_P_IP]
+ = arc_proto_map[ARC_P_IPV6]
+ = arc_proto_map[ARC_P_ARP]
+ = arc_proto_map[ARC_P_RARP]
+ = arc_proto_map[ARC_P_IPX]
+ = arc_proto_map[ARC_P_NOVELL_EC]
+ = &rfc1201_proto;
+
+ /* if someone else already owns the broadcast, we won't take it */
+ if (arc_bcast_proto == arc_proto_default)
+ arc_bcast_proto = &rfc1201_proto;
+
+ return 0;
+}
+
+static void __exit arcnet_rfc1201_exit(void)
+{
+ arcnet_unregister_proto(&rfc1201_proto);
+}
+
+module_init(arcnet_rfc1201_init);
+module_exit(arcnet_rfc1201_exit);
+
+/*
+ * Determine a packet's protocol ID.
+ *
+ * With ARCnet we have to convert everything to Ethernet-style stuff.
+ */
+static unsigned short type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+ struct archdr *pkt = (struct archdr *) skb->data;
+ struct arc_rfc1201 *soft = &pkt->soft.rfc1201;
+ struct arcnet_local *lp = dev->priv;
+ int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
+
+ /* Pull off the arcnet header. */
+ skb->mac.raw = skb->data;
+ skb_pull(skb, hdr_size);
+
+ if (pkt->hard.dest == 0)
+ skb->pkt_type = PACKET_BROADCAST;
+ else if (dev->flags & IFF_PROMISC) {
+ /* if we're not sending to ourselves :) */
+ if (pkt->hard.dest != dev->dev_addr[0])
+ skb->pkt_type = PACKET_OTHERHOST;
+ }
+ /* now return the protocol number */
+ switch (soft->proto) {
+ case ARC_P_IP:
+ return htons(ETH_P_IP);
+ case ARC_P_IPV6:
+ return htons(ETH_P_IPV6);
+ case ARC_P_ARP:
+ return htons(ETH_P_ARP);
+ case ARC_P_RARP:
+ return htons(ETH_P_RARP);
+
+ case ARC_P_IPX:
+ case ARC_P_NOVELL_EC:
+ return htons(ETH_P_802_3);
+ default:
+ lp->stats.rx_errors++;
+ lp->stats.rx_crc_errors++;
+ return 0;
+ }
+
+ return htons(ETH_P_IP);
+}
+
+
+/* packet receiver */
+static void rx(struct net_device *dev, int bufnum,
+ struct archdr *pkthdr, int length)
+{
+ struct arcnet_local *lp = dev->priv;
+ struct sk_buff *skb;
+ struct archdr *pkt = pkthdr;
+ struct arc_rfc1201 *soft = &pkthdr->soft.rfc1201;
+ int saddr = pkt->hard.source, ofs;
+ struct Incoming *in = &lp->rfc1201.incoming[saddr];
+
+ BUGMSG(D_DURING, "it's an RFC1201 packet (length=%d)\n", length);
+
+ if (length >= MinTU)
+ ofs = 512 - length;
+ else
+ ofs = 256 - length;
+
+ if (soft->split_flag == 0xFF) { /* Exception Packet */
+ if (length >= 4 + RFC1201_HDR_SIZE)
+ BUGMSG(D_DURING, "compensating for exception packet\n");
+ else {
+ BUGMSG(D_EXTRA, "short RFC1201 exception packet from %02Xh",
+ saddr);
+ return;
+ }
+
+ /* skip over 4-byte junkola */
+ length -= 4;
+ ofs += 4;
+ lp->hw.copy_from_card(dev, bufnum, 512 - length,
+ soft, sizeof(pkt->soft));
+ }
+ if (!soft->split_flag) { /* not split */
+ BUGMSG(D_RX, "incoming is not split (splitflag=%d)\n",
+ soft->split_flag);
+
+ if (in->skb) { /* already assembling one! */
+ BUGMSG(D_EXTRA, "aborting assembly (seq=%d) for unsplit packet (splitflag=%d, seq=%d)\n",
+ in->sequence, soft->split_flag, soft->sequence);
+ lp->rfc1201.aborted_seq = soft->sequence;
+ dev_kfree_skb_irq(in->skb);
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors++;
+ in->skb = NULL;
+ }
+ in->sequence = soft->sequence;
+
+ skb = alloc_skb(length + ARC_HDR_SIZE, GFP_ATOMIC);
+ if (skb == NULL) {
+ BUGMSG(D_NORMAL, "Memory squeeze, dropping packet.\n");
+ lp->stats.rx_dropped++;
+ return;
+ }
+ skb_put(skb, length + ARC_HDR_SIZE);
+ skb->dev = dev;
+
+ pkt = (struct archdr *) skb->data;
+ soft = &pkt->soft.rfc1201;
+
+ /* up to sizeof(pkt->soft) has already been copied from the card */
+ memcpy(pkt, pkthdr, sizeof(struct archdr));
+ if (length > sizeof(pkt->soft))
+ lp->hw.copy_from_card(dev, bufnum, ofs + sizeof(pkt->soft),
+ pkt->soft.raw + sizeof(pkt->soft),
+ length - sizeof(pkt->soft));
+
+ /*
+ * ARP packets have problems when sent from some DOS systems: the
+ * source address is always 0! So we take the hardware source addr
+ * (which is impossible to fumble) and insert it ourselves.
+ */
+ if (soft->proto == ARC_P_ARP) {
+ struct arphdr *arp = (struct arphdr *) soft->payload;
+
+ /* make sure addresses are the right length */
+ if (arp->ar_hln == 1 && arp->ar_pln == 4) {
+ uint8_t *cptr = (uint8_t *) arp + sizeof(struct arphdr);
+
+ if (!*cptr) { /* is saddr = 00? */
+ BUGMSG(D_EXTRA,
+ "ARP source address was 00h, set to %02Xh.\n",
+ saddr);
+ lp->stats.rx_crc_errors++;
+ *cptr = saddr;
+ } else {
+ BUGMSG(D_DURING, "ARP source address (%Xh) is fine.\n",
+ *cptr);
+ }
+ } else {
+ BUGMSG(D_NORMAL, "funny-shaped ARP packet. (%Xh, %Xh)\n",
+ arp->ar_hln, arp->ar_pln);
+ lp->stats.rx_errors++;
+ lp->stats.rx_crc_errors++;
+ }
+ }
+ BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
+
+ skb->protocol = type_trans(skb, dev);
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ } else { /* split packet */
+ /*
+ * NOTE: MSDOS ARP packet correction should only need to apply to
+ * unsplit packets, since ARP packets are so short.
+ *
+ * My interpretation of the RFC1201 document is that if a packet is
+ * received out of order, the entire assembly process should be
+ * aborted.
+ *
+ * The RFC also mentions "it is possible for successfully received
+ * packets to be retransmitted." As of 0.40 all previously received
+ * packets are allowed, not just the most recent one.
+ *
+ * We allow multiple assembly processes, one for each ARCnet card
+ * possible on the network. Seems rather like a waste of memory,
+ * but there's no other way to be reliable.
+ */
+
+ BUGMSG(D_RX, "packet is split (splitflag=%d, seq=%d)\n",
+ soft->split_flag, in->sequence);
+
+ if (in->skb && in->sequence != soft->sequence) {
+ BUGMSG(D_EXTRA, "wrong seq number (saddr=%d, expected=%d, seq=%d, splitflag=%d)\n",
+ saddr, in->sequence, soft->sequence,
+ soft->split_flag);
+ dev_kfree_skb_irq(in->skb);
+ in->skb = NULL;
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors++;
+ in->lastpacket = in->numpackets = 0;
+ }
+ if (soft->split_flag & 1) { /* first packet in split */
+ BUGMSG(D_RX, "brand new splitpacket (splitflag=%d)\n",
+ soft->split_flag);
+ if (in->skb) { /* already assembling one! */
+ BUGMSG(D_EXTRA, "aborting previous (seq=%d) assembly "
+ "(splitflag=%d, seq=%d)\n",
+ in->sequence, soft->split_flag,
+ soft->sequence);
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors++;
+ dev_kfree_skb_irq(in->skb);
+ }
+ in->sequence = soft->sequence;
+ in->numpackets = ((unsigned) soft->split_flag >> 1) + 2;
+ in->lastpacket = 1;
+
+ if (in->numpackets > 16) {
+ BUGMSG(D_EXTRA, "incoming packet more than 16 segments; dropping. (splitflag=%d)\n",
+ soft->split_flag);
+ lp->rfc1201.aborted_seq = soft->sequence;
+ lp->stats.rx_errors++;
+ lp->stats.rx_length_errors++;
+ return;
+ }
+ in->skb = skb = alloc_skb(508 * in->numpackets + ARC_HDR_SIZE,
+ GFP_ATOMIC);
+ if (skb == NULL) {
+ BUGMSG(D_NORMAL, "(split) memory squeeze, dropping packet.\n");
+ lp->rfc1201.aborted_seq = soft->sequence;
+ lp->stats.rx_dropped++;
+ return;
+ }
+ skb->dev = dev;
+ pkt = (struct archdr *) skb->data;
+ soft = &pkt->soft.rfc1201;
+
+ memcpy(pkt, pkthdr, ARC_HDR_SIZE + RFC1201_HDR_SIZE);
+ skb_put(skb, ARC_HDR_SIZE + RFC1201_HDR_SIZE);
+
+ soft->split_flag = 0; /* end result won't be split */
+ } else { /* not first packet */
+ int packetnum = ((unsigned) soft->split_flag >> 1) + 1;
+
+ /*
+ * if we're not assembling, there's no point trying to
+ * continue.
+ */
+ if (!in->skb) {
+ if (lp->rfc1201.aborted_seq != soft->sequence) {
+ BUGMSG(D_EXTRA, "can't continue split without starting "
+ "first! (splitflag=%d, seq=%d, aborted=%d)\n",
+ soft->split_flag, soft->sequence,
+ lp->rfc1201.aborted_seq);
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors++;
+ }
+ return;
+ }
+ in->lastpacket++;
+ if (packetnum != in->lastpacket) { /* not the right flag! */
+ /* harmless duplicate? ignore. */
+ if (packetnum <= in->lastpacket - 1) {
+ BUGMSG(D_EXTRA, "duplicate splitpacket ignored! (splitflag=%d)\n",
+ soft->split_flag);
+ lp->stats.rx_errors++;
+ lp->stats.rx_frame_errors++;
+ return;
+ }
+ /* "bad" duplicate, kill reassembly */
+ BUGMSG(D_EXTRA, "out-of-order splitpacket, reassembly "
+ "(seq=%d) aborted (splitflag=%d, seq=%d)\n",
+ in->sequence, soft->split_flag, soft->sequence);
+ lp->rfc1201.aborted_seq = soft->sequence;
+ dev_kfree_skb_irq(in->skb);
+ in->skb = NULL;
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors++;
+ in->lastpacket = in->numpackets = 0;
+ return;
+ }
+ pkt = (struct archdr *) in->skb->data;
+ soft = &pkt->soft.rfc1201;
+ }
+
+ skb = in->skb;
+
+ lp->hw.copy_from_card(dev, bufnum, ofs + RFC1201_HDR_SIZE,
+ skb->data + skb->len,
+ length - RFC1201_HDR_SIZE);
+ skb_put(skb, length - RFC1201_HDR_SIZE);
+
+ /* are we done? */
+ if (in->lastpacket == in->numpackets) {
+ in->skb = NULL;
+ in->lastpacket = in->numpackets = 0;
+
+ BUGMSG(D_SKB_SIZE, "skb: received %d bytes from %02X (unsplit)\n",
+ skb->len, pkt->hard.source);
+ BUGMSG(D_SKB_SIZE, "skb: received %d bytes from %02X (split)\n",
+ skb->len, pkt->hard.source);
+ BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx");
+
+ skb->protocol = type_trans(skb, dev);
+ netif_rx(skb);
+ dev->last_rx = jiffies;
+ }
+ }
+}
+
+
+/* Create the ARCnet hard/soft headers for RFC1201. */
+static int build_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type, uint8_t daddr)
+{
+ struct arcnet_local *lp = dev->priv;
+ int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE;
+ struct archdr *pkt = (struct archdr *) skb_push(skb, hdr_size);
+ struct arc_rfc1201 *soft = &pkt->soft.rfc1201;
+
+ /* set the protocol ID according to RFC1201 */
+ switch (type) {
+ case ETH_P_IP:
+ soft->proto = ARC_P_IP;
+ break;
+ case ETH_P_IPV6:
+ soft->proto = ARC_P_IPV6;
+ break;
+ case ETH_P_ARP:
+ soft->proto = ARC_P_ARP;
+ break;
+ case ETH_P_RARP:
+ soft->proto = ARC_P_RARP;
+ break;
+ case ETH_P_IPX:
+ case ETH_P_802_3:
+ case ETH_P_802_2:
+ soft->proto = ARC_P_IPX;
+ break;
+ case ETH_P_ATALK:
+ soft->proto = ARC_P_ATALK;
+ break;
+ default:
+ BUGMSG(D_NORMAL, "RFC1201: I don't understand protocol %d (%Xh)\n",
+ type, type);
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+ return 0;
+ }
+
+ /*
+ * Set the source hardware address.
+ *
+ * This is pretty pointless for most purposes, but it can help in
+ * debugging. ARCnet does not allow us to change the source address in
+ * the actual packet sent)
+ */
+ pkt->hard.source = *dev->dev_addr;
+
+ soft->sequence = htons(lp->rfc1201.sequence++);
+ soft->split_flag = 0; /* split packets are done elsewhere */
+
+ /* see linux/net/ethernet/eth.c to see where I got the following */
+
+ if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) {
+ /*
+ * FIXME: fill in the last byte of the dest ipaddr here to better
+ * comply with RFC1051 in "noarp" mode. For now, always broadcasting
+ * will probably at least get packets sent out :)
+ */
+ pkt->hard.dest = 0;
+ return hdr_size;
+ }
+ /* otherwise, drop in the dest address */
+ pkt->hard.dest = daddr;
+ return hdr_size;
+}
+
+
+static void load_pkt(struct net_device *dev, struct arc_hardware *hard,
+ struct arc_rfc1201 *soft, int softlen, int bufnum)
+{
+ struct arcnet_local *lp = dev->priv;
+ int ofs;
+
+ /* assume length <= XMTU: someone should have handled that by now. */
+
+ if (softlen > MinTU) {
+ hard->offset[0] = 0;
+ hard->offset[1] = ofs = 512 - softlen;
+ } else if (softlen > MTU) { /* exception packet - add an extra header */
+ struct arc_rfc1201 excsoft;
+
+ excsoft.proto = soft->proto;
+ excsoft.split_flag = 0xff;
+ excsoft.sequence = 0xffff;
+
+ hard->offset[0] = 0;
+ ofs = 512 - softlen;
+ hard->offset[1] = ofs - RFC1201_HDR_SIZE;
+ lp->hw.copy_to_card(dev, bufnum, ofs - RFC1201_HDR_SIZE,
+ &excsoft, RFC1201_HDR_SIZE);
+ } else
+ hard->offset[0] = ofs = 256 - softlen;
+
+ lp->hw.copy_to_card(dev, bufnum, 0, hard, ARC_HDR_SIZE);
+ lp->hw.copy_to_card(dev, bufnum, ofs, soft, softlen);
+
+ lp->lastload_dest = hard->dest;
+}
+
+
+static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length,
+ int bufnum)
+{
+ struct arcnet_local *lp = dev->priv;
+ const int maxsegsize = XMTU - RFC1201_HDR_SIZE;
+ struct Outgoing *out;
+
+
+ BUGMSG(D_DURING, "prepare_tx: txbufs=%d/%d/%d\n",
+ lp->next_tx, lp->cur_tx, bufnum);
+
+ length -= ARC_HDR_SIZE; /* hard header is not included in packet length */
+ pkt->soft.rfc1201.split_flag = 0;
+
+ /* need to do a split packet? */
+ if (length > XMTU) {
+ out = &lp->outgoing;
+
+ out->length = length - RFC1201_HDR_SIZE;
+ out->dataleft = lp->outgoing.length;
+ out->numsegs = (out->dataleft + maxsegsize - 1) / maxsegsize;
+ out->segnum = 0;
+
+ BUGMSG(D_DURING, "rfc1201 prep_tx: ready for %d-segment split "
+ "(%d bytes, seq=%d)\n", out->numsegs, out->length,
+ pkt->soft.rfc1201.sequence);
+
+ return 0; /* not done */
+ }
+ /* just load the packet into the buffers and send it off */
+ load_pkt(dev, &pkt->hard, &pkt->soft.rfc1201, length, bufnum);
+
+ return 1; /* done */
+}
+
+
+static int continue_tx(struct net_device *dev, int bufnum)
+{
+ struct arcnet_local *lp = dev->priv;
+ struct Outgoing *out = &lp->outgoing;
+ struct arc_hardware *hard = &out->pkt->hard;
+ struct arc_rfc1201 *soft = &out->pkt->soft.rfc1201, *newsoft;
+ int maxsegsize = XMTU - RFC1201_HDR_SIZE;
+ int seglen;
+
+ BUGMSG(D_DURING,
+ "rfc1201 continue_tx: loading segment %d(+1) of %d (seq=%d)\n",
+ out->segnum, out->numsegs, soft->sequence);
+
+ /* the "new" soft header comes right before the data chunk */
+ newsoft = (struct arc_rfc1201 *)
+ (out->pkt->soft.raw + out->length - out->dataleft);
+
+ if (!out->segnum) /* first packet; newsoft == soft */
+ newsoft->split_flag = ((out->numsegs - 2) << 1) | 1;
+ else {
+ newsoft->split_flag = out->segnum << 1;
+ newsoft->proto = soft->proto;
+ newsoft->sequence = soft->sequence;
+ }
+
+ seglen = maxsegsize;
+ if (seglen > out->dataleft)
+ seglen = out->dataleft;
+ out->dataleft -= seglen;
+
+ load_pkt(dev, hard, newsoft, seglen + RFC1201_HDR_SIZE, bufnum);
+
+ out->segnum++;
+ if (out->segnum >= out->numsegs)
+ return 1;
+ else
+ return 0;
+}