diff options
Diffstat (limited to 'drivers/net/arcnet')
-rw-r--r-- | drivers/net/arcnet/Kconfig | 140 | ||||
-rw-r--r-- | drivers/net/arcnet/Makefile | 14 | ||||
-rw-r--r-- | drivers/net/arcnet/arc-rawmode.c | 204 | ||||
-rw-r--r-- | drivers/net/arcnet/arc-rimi.c | 368 | ||||
-rw-r--r-- | drivers/net/arcnet/arcnet.c | 1102 | ||||
-rw-r--r-- | drivers/net/arcnet/capmode.c | 296 | ||||
-rw-r--r-- | drivers/net/arcnet/com20020-isa.c | 219 | ||||
-rw-r--r-- | drivers/net/arcnet/com20020-pci.c | 189 | ||||
-rw-r--r-- | drivers/net/arcnet/com20020.c | 357 | ||||
-rw-r--r-- | drivers/net/arcnet/com90io.c | 435 | ||||
-rw-r--r-- | drivers/net/arcnet/com90xx.c | 646 | ||||
-rw-r--r-- | drivers/net/arcnet/rfc1051.c | 253 | ||||
-rw-r--r-- | drivers/net/arcnet/rfc1201.c | 549 |
13 files changed, 4772 insertions, 0 deletions
diff --git a/drivers/net/arcnet/Kconfig b/drivers/net/arcnet/Kconfig new file mode 100644 index 0000000..948de25 --- /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 0000000..5861af5 --- /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 0000000..e1ea29b --- /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 0000000..38c3f03 --- /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 0000000..4f9f69e --- /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 0000000..16e155b --- /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 0000000..9289e61 --- /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 0000000..96636ca --- /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 0000000..0dc70c7 --- /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 0000000..52c77cb --- /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 0000000..6c2c9b9 --- /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 0000000..6d79137 --- /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 0000000..6b6ae4b --- /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; +} |