diff options
Diffstat (limited to 'drivers/net/arcnet')
-rw-r--r-- | drivers/net/arcnet/Kconfig | 136 | ||||
-rw-r--r-- | drivers/net/arcnet/Makefile | 15 | ||||
-rw-r--r-- | drivers/net/arcnet/arc-rawmode.c | 203 | ||||
-rw-r--r-- | drivers/net/arcnet/arc-rimi.c | 398 | ||||
-rw-r--r-- | drivers/net/arcnet/arcnet.c | 1047 | ||||
-rw-r--r-- | drivers/net/arcnet/capmode.c | 274 | ||||
-rw-r--r-- | drivers/net/arcnet/com20020-isa.c | 221 | ||||
-rw-r--r-- | drivers/net/arcnet/com20020-pci.c | 417 | ||||
-rw-r--r-- | drivers/net/arcnet/com20020.c | 383 | ||||
-rw-r--r-- | drivers/net/arcnet/com20020_cs.c | 331 | ||||
-rw-r--r-- | drivers/net/arcnet/com90io.c | 433 | ||||
-rw-r--r-- | drivers/net/arcnet/com90xx.c | 704 | ||||
-rw-r--r-- | drivers/net/arcnet/rfc1051.c | 251 | ||||
-rw-r--r-- | drivers/net/arcnet/rfc1201.c | 547 |
14 files changed, 5360 insertions, 0 deletions
diff --git a/drivers/net/arcnet/Kconfig b/drivers/net/arcnet/Kconfig new file mode 100644 index 000000000..84fb6349a --- /dev/null +++ b/drivers/net/arcnet/Kconfig @@ -0,0 +1,136 @@ +# +# Arcnet configuration +# + +menuconfig ARCNET + depends on NETDEVICES && (ISA || PCI || PCMCIA) + 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. The module will + be called arcnet. + +if ARCNET + +config ARCNET_1201 + tristate "Enable standard ARCNet packet format (RFC 1201)" + 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)" + ---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" + 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" + 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 + transmission: + 0=no hardware acknowledge + 1=excessive nak + 2=transmission accepted by the receiver 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" + 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. The module will + be called com90xx. + +config ARCNET_COM90xxIO + tristate "ARCnet COM90xx (IO mapped) chipset driver" + ---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. The module will + be called com90io. + +config ARCNET_RIM_I + tristate "ARCnet COM90xx (RIM I) chipset driver" + ---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. The module will + be called arc-rimi. + +config ARCNET_COM20020 + tristate "ARCnet COM20020 chipset driver" + 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. 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 + +config ARCNET_COM20020_CS + tristate "COM20020 ARCnet PCMCIA support" + depends on ARCNET_COM20020 && PCMCIA + help + Say Y here if you intend to attach this type of ARCnet PCMCIA card + to your computer. + + To compile this driver as a module, choose M here: the module will be + called com20020_cs. If unsure, say N. + +endif # ARCNET diff --git a/drivers/net/arcnet/Makefile b/drivers/net/arcnet/Makefile new file mode 100644 index 000000000..5ce8ee63e --- /dev/null +++ b/drivers/net/arcnet/Makefile @@ -0,0 +1,15 @@ +# 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 +obj-$(CONFIG_ARCNET_COM20020_CS) += com20020_cs.o diff --git a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c new file mode 100644 index 000000000..705e6ce2e --- /dev/null +++ b/drivers/net/arcnet/arc-rawmode.c @@ -0,0 +1,203 @@ +/* + * 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/gfp.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); + +static 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 = netdev_priv(dev); + struct sk_buff *skb; + struct archdr *pkt = pkthdr; + int ofs; + + BUGMSG(D_DURING, "it's a raw packet (length=%d)\n", length); + + if (length > MTU) + 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"); + dev->stats.rx_dropped++; + return; + } + skb_put(skb, length + ARC_HDR_SIZE); + skb->dev = dev; + + pkt = (struct archdr *) skb->data; + + skb_reset_mac_header(skb); + 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 = cpu_to_be16(ETH_P_ARCNET); + netif_rx(skb); +} + + +/* + * 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 = netdev_priv(dev); + 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 000000000..b8b4c7ba8 --- /dev/null +++ b/drivers/net/arcnet/arc-rimi.c @@ -0,0 +1,398 @@ +/* + * 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/delay.h> +#include <linux/netdevice.h> +#include <linux/bootmem.h> +#include <linux/init.h> +#include <linux/interrupt.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"); + + BUGLVL(D_NORMAL) printk("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) { + BUGLVL(D_NORMAL) printk("No autoprobe for RIM I; you " + "must specify the shmem and irq!\n"); + return -ENODEV; + } + if (dev->dev_addr[0] == 0) { + BUGLVL(D_NORMAL) printk("You need to specify your card's station " + "ID!\n"); + return -ENODEV; + } + /* + * Grab the memory region at mem_start for MIRROR_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, MIRROR_SIZE, "arcnet (90xx)")) { + BUGLVL(D_NORMAL) printk("Card memory already allocated\n"); + return -ENODEV; + } + return arcrimi_found(dev); +} + +static int check_mirror(unsigned long addr, size_t size) +{ + void __iomem *p; + int res = -1; + + if (!request_mem_region(addr, size, "arcnet (90xx)")) + return -1; + + p = ioremap(addr, size); + if (p) { + if (readb(p) == TESTvalue) + res = 1; + else + res = 0; + iounmap(p); + } + + release_mem_region(addr, size); + return res; +} + +/* + * 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; + void __iomem *p; + int mirror_size; + int err; + + p = ioremap(dev->mem_start, MIRROR_SIZE); + if (!p) { + release_mem_region(dev->mem_start, MIRROR_SIZE); + BUGMSG(D_NORMAL, "Can't ioremap\n"); + return -ENODEV; + } + + /* reserve the irq */ + if (request_irq(dev->irq, arcnet_interrupt, 0, "arcnet (RIM I)", dev)) { + iounmap(p); + release_mem_region(dev->mem_start, MIRROR_SIZE); + BUGMSG(D_NORMAL, "Can't get IRQ %d!\n", dev->irq); + return -ENODEV; + } + + shmem = dev->mem_start; + writeb(TESTvalue, p); + writeb(dev->dev_addr[0], p + 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 (readb(p) == TESTvalue && + check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 && + check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1) + mirror_size = 2 * MIRROR_SIZE; + + first_mirror = shmem - mirror_size; + while (check_mirror(first_mirror, mirror_size) == 1) + first_mirror -= mirror_size; + first_mirror += mirror_size; + + last_mirror = shmem + mirror_size; + while (check_mirror(last_mirror, mirror_size) == 1) + 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 = netdev_priv(dev); + 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. + */ + iounmap(p); + release_mem_region(shmem, MIRROR_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 = netdev_priv(dev); + 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 = netdev_priv(dev); + void __iomem *ioaddr = lp->mem_start + 0x800; + + AINTMASK(mask); +} + +static int arcrimi_status(struct net_device *dev) +{ + struct arcnet_local *lp = netdev_priv(dev); + void __iomem *ioaddr = lp->mem_start + 0x800; + + return ASTATUS(); +} + +static void arcrimi_command(struct net_device *dev, int cmd) +{ + struct arcnet_local *lp = netdev_priv(dev); + 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 = netdev_priv(dev); + 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 = netdev_priv(dev); + 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 = netdev_priv(dev); + + 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 000000000..10f71c732 --- /dev/null +++ b/drivers/net/arcnet/arcnet.c @@ -0,0 +1,1047 @@ +/* + * 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.94 BETA 2007/02/08 - by Avery Pennarun et al.\n" + +#include <linux/module.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> +#include <linux/jiffies.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); + +static void arcnet_rx(struct net_device *dev, 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; + +static struct ArcProto arc_proto_null = +{ + .suffix = '?', + .mtu = XMTU, + .is_ip = 0, + .rx = null_rx, + .build_header = null_build_header, + .prepare_tx = null_prepare_tx, + .continue_tx = NULL, + .ack_tx = NULL +}; + +/* 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(arcnet_unregister_proto); +EXPORT_SYMBOL(arcnet_debug); +EXPORT_SYMBOL(alloc_arcdev); +EXPORT_SYMBOL(arcnet_interrupt); +EXPORT_SYMBOL(arcnet_open); +EXPORT_SYMBOL(arcnet_close); +EXPORT_SYMBOL(arcnet_send_packet); +EXPORT_SYMBOL(arcnet_timeout); + +/* Internal function prototypes */ +static int arcnet_header(struct sk_buff *skb, struct net_device *dev, + unsigned short type, const void *daddr, + const void *saddr, unsigned len); +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("arcnet loaded.\n"); + +#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) +{ + char hdr[32]; + + /* dump the packet */ + snprintf(hdr, sizeof(hdr), "%6s:%s skb->data:", dev->name, desc); + print_hex_dump(KERN_DEBUG, hdr, DUMP_PREFIX_OFFSET, + 16, 1, skb->data, skb->len, true); +} + +EXPORT_SYMBOL(arcnet_dump_skb); +#endif + + +/* + * Dump the contents of an ARCnet buffer + */ +#if (ARCNET_DEBUG_MAX & (D_RX | D_TX)) +static void arcnet_dump_packet(struct net_device *dev, int bufnum, + char *desc, int take_arcnet_lock) +{ + struct arcnet_local *lp = netdev_priv(dev); + int i, length; + unsigned long flags = 0; + static uint8_t buf[512]; + char hdr[32]; + + /* 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); + + /* dump the packet */ + snprintf(hdr, sizeof(hdr), "%6s:%s packet dump:", dev->name, desc); + print_hex_dump(KERN_DEBUG, hdr, DUMP_PREFIX_OFFSET, + 16, 1, buf, length, true); +} + +#else + +#define arcnet_dump_packet(dev, bufnum, desc,take_arcnet_lock) do { } while (0) + +#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 = netdev_priv(dev); + 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 = netdev_priv(dev); + 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; +} + +static const struct header_ops arcnet_header_ops = { + .create = arcnet_header, +}; + +static const struct net_device_ops arcnet_netdev_ops = { + .ndo_open = arcnet_open, + .ndo_stop = arcnet_close, + .ndo_start_xmit = arcnet_send_packet, + .ndo_tx_timeout = arcnet_timeout, +}; + +/* Setup a struct device for ARCnet. */ +static void arcdev_setup(struct net_device *dev) +{ + dev->type = ARPHRD_ARCNET; + dev->netdev_ops = &arcnet_netdev_ops; + dev->header_ops = &arcnet_header_ops; + 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; + +} + +struct net_device *alloc_arcdev(const char *name) +{ + struct net_device *dev; + + dev = alloc_netdev(sizeof(struct arcnet_local), + name && *name ? name : "arc%d", NET_NAME_UNKNOWN, + arcdev_setup); + if(dev) { + struct arcnet_local *lp = netdev_priv(dev); + 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. + */ +int arcnet_open(struct net_device *dev) +{ + struct arcnet_local *lp = netdev_priv(dev); + int count, newmtu, error; + + BUGMSG(D_INIT,"opened."); + + if (!try_module_get(lp->hw.owner)) + return -ENODEV; + + BUGLVL(D_PROTO) { + 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__,__func__); + if (ASTATUS() & RESETflag) { + BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + ACOMMAND(CFLAGScmd | RESETclear); + } + + + BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + /* 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__,__func__); + lp->intmask = NORXflag | RECONflag; + AINTMASK(lp->intmask); + BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + + 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. */ +int arcnet_close(struct net_device *dev) +{ + struct arcnet_local *lp = netdev_priv(dev); + + 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, const void *daddr, + const void *saddr, unsigned len) +{ + const struct arcnet_local *lp = netdev_priv(dev); + 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; + /* + * XXX: Why not use skb->mac_len? + */ + if (skb->network_header - skb->mac_header != 2) + BUGMSG(D_NORMAL, "arcnet_header: Yikes! diff (%d) is not 2!\n", + (int)(skb->network_header - skb->mac_header)); + 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); +} + +/* Called by the kernel in order to transmit a packet. */ +netdev_tx_t arcnet_send_packet(struct sk_buff *skb, + struct net_device *dev) +{ + struct arcnet_local *lp = netdev_priv(dev); + struct archdr *pkt; + struct arc_rfc1201 *soft; + struct ArcProto *proto; + int txbuf; + unsigned long flags; + int freeskb, retval; + + BUGMSG(D_DURING, + "transmit requested (status=%Xh, txbufs=%d/%d, len=%d, 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 NETDEV_TX_OK; /* don't try again */ + } + + /* We're busy transmitting a packet... */ + netif_stop_queue(dev); + + spin_lock_irqsave(&lp->lock, flags); + AINTMASK(0); + if(lp->next_tx == -1) + txbuf = get_arcbuf(dev); + else { + txbuf = -1; + } + 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 */ + dev->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; + + freeskb = 0; + + if (proto->continue_tx && + proto->continue_tx(dev, txbuf)) { + BUGMSG(D_NORMAL, + "bug! continue_tx finished the first time! " + "(proto='%c')\n", proto->suffix); + } + } + retval = NETDEV_TX_OK; + lp->next_tx = txbuf; + } else { + retval = NETDEV_TX_BUSY; + freeskb = 0; + } + + BUGMSG(D_DEBUG, "%s: %d: %s, status: %x\n",__FILE__,__LINE__,__func__,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__,__func__); + lp->intmask |= TXFREEflag|EXCNAKflag; + AINTMASK(lp->intmask); + BUGMSG(D_DEBUG, "%s: %d: %s, status: %x\n",__FILE__,__LINE__,__func__,ASTATUS()); + + spin_unlock_irqrestore(&lp->lock, flags); + if (freeskb) { + dev_kfree_skb(skb); + } + return retval; /* 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 = netdev_priv(dev); + + 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->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 */ +void arcnet_timeout(struct net_device *dev) +{ + unsigned long flags; + struct arcnet_local *lp = netdev_priv(dev); + int status = ASTATUS(); + char *msg; + + spin_lock_irqsave(&lp->lock, flags); + if (status & TXFREEflag) { /* transmit _DID_ finish */ + msg = " - missed IRQ?"; + } else { + msg = ""; + dev->stats.tx_aborted_errors++; + lp->timed_out = 1; + ACOMMAND(NOTXcmd | (lp->cur_tx << 3)); + } + dev->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 (time_after(jiffies, lp->last_timeout + 10*HZ)) { + BUGMSG(D_EXTRA, "tx timed out%s (status=%Xh, intmask=%Xh, dest=%02Xh)\n", + msg, status, lp->intmask, lp->lasttrans_dest); + lp->last_timeout = jiffies; + } + + 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 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 = netdev_priv(dev); + BUG_ON(!lp); + + spin_lock(&lp->lock); + + /* + * RESET flag was enabled - if device is not running, we must clear it right + * away (but nothing else). + */ + if (!netif_running(dev)) { + if (ASTATUS() & RESETflag) + ACOMMAND(CFLAGScmd | RESETclear); + AINTMASK(0); + spin_unlock(&lp->lock); + return retval; + } + + 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__,__func__,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); + dev->stats.tx_errors++; + dev->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 */ + dev->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); + dev->stats.tx_carrier_errors++; + + BUGMSG(D_RECON, "Network reconfiguration detected (status=%Xh)\n", + status); + /* MYRECON bit is at bit 7 of diagstatus */ + if(diagstatus & 0x80) + BUGMSG(D_RECON,"Put out that recon myself\n"); + + /* is the RECON info empty or old? */ + if (!lp->first_recon || !lp->last_recon || + time_after(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 && + time_after(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. + */ +static void arcnet_rx(struct net_device *dev, int bufnum) +{ + struct arcnet_local *lp = netdev_priv(dev); + struct archdr pkt; + struct arc_rfc1201 *soft; + int length, ofs; + + soft = &pkt.soft.rfc1201; + + lp->hw.copy_from_card(dev, bufnum, 0, &pkt, 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); + + dev->stats.rx_packets++; + dev->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); +} + + +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 = netdev_priv(dev); + + 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 = netdev_priv(dev); + 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 000000000..42fce91b7 --- /dev/null +++ b/drivers/net/arcnet/capmode.c @@ -0,0 +1,274 @@ +/* + * 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/gfp.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" + +/* packet receiver */ +static void rx(struct net_device *dev, int bufnum, + struct archdr *pkthdr, int length) +{ + struct arcnet_local *lp = netdev_priv(dev); + 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"); + dev->stats.rx_dropped++; + return; + } + skb_put(skb, length + ARC_HDR_SIZE + sizeof(int)); + skb->dev = dev; + skb_reset_mac_header(skb); + pkt = (struct archdr *)skb_mac_header(skb); + 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 = cpu_to_be16(ETH_P_ARCNET); + netif_rx(skb); +} + + +/* + * 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 = netdev_priv(dev); + 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 = netdev_priv(dev); + 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; + + skb_reset_mac_header(ackskb); + ackpkt = (struct archdr *)skb_mac_header(ackskb); + /* skb_pull(ackskb, ARC_HDR_SIZE); */ + + skb_copy_from_linear_data(lp->outgoing.skb, ackpkt, + 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 = cpu_to_be16(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; +} + +static struct ArcProto capmode_proto = +{ + 'r', + XMTU, + 0, + rx, + build_header, + prepare_tx, + NULL, + ack_tx +}; + +static 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; +} + +static int __init capmode_module_init(void) +{ + printk(VERSION); + arcnet_cap_init(); + return 0; +} + +static void __exit capmode_module_exit(void) +{ + arcnet_unregister_proto(&capmode_proto); +} +module_init(capmode_module_init); +module_exit(capmode_module_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/arcnet/com20020-isa.c b/drivers/net/arcnet/com20020-isa.c new file mode 100644 index 000000000..45c61a2c5 --- /dev/null +++ b/drivers/net/arcnet/com20020-isa.c @@ -0,0 +1,221 @@ +/* + * 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/errno.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/init.h> +#include <linux/interrupt.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 = netdev_priv(dev); + 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 ((int)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 ((int)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; + + dev->netdev_ops = &com20020_netdev_ops; + + lp = netdev_priv(dev); + 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 000000000..96edc1346 --- /dev/null +++ b/drivers/net/arcnet/com20020-pci.c @@ -0,0 +1,417 @@ +/* + * 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/errno.h> +#include <linux/netdevice.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/arcdevice.h> +#include <linux/com20020.h> +#include <linux/list.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 void com20020pci_remove(struct pci_dev *pdev); + +static int com20020pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct com20020_pci_card_info *ci; + struct net_device *dev; + struct arcnet_local *lp; + struct com20020_priv *priv; + int i, ioaddr, ret; + struct resource *r; + + if (pci_enable_device(pdev)) + return -EIO; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct com20020_priv), + GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ci = (struct com20020_pci_card_info *)id->driver_data; + priv->ci = ci; + + INIT_LIST_HEAD(&priv->list_dev); + + + for (i = 0; i < ci->devcount; i++) { + struct com20020_pci_channel_map *cm = &ci->chan_map_tbl[i]; + struct com20020_dev *card; + + dev = alloc_arcdev(device); + if (!dev) { + ret = -ENOMEM; + goto out_port; + } + + dev->netdev_ops = &com20020_netdev_ops; + + lp = netdev_priv(dev); + + BUGMSG(D_NORMAL, "%s Controls\n", ci->name); + ioaddr = pci_resource_start(pdev, cm->bar) + cm->offset; + + r = devm_request_region(&pdev->dev, ioaddr, cm->size, + "com20020-pci"); + if (!r) { + pr_err("IO region %xh-%xh already allocated.\n", + ioaddr, ioaddr + cm->size - 1); + ret = -EBUSY; + goto out_port; + } + + /* Dummy access after Reset + * ARCNET controller needs + * this access to detect bustype + */ + outb(0x00, ioaddr + 1); + inb(ioaddr + 1); + + dev->base_addr = ioaddr; + dev->dev_addr[0] = node; + dev->irq = pdev->irq; + lp->card_name = "PCI COM20020"; + lp->card_flags = ci->flags; + lp->backplane = backplane; + lp->clockp = clockp & 7; + lp->clockm = clockm & 3; + lp->timeout = timeout; + lp->hw.owner = THIS_MODULE; + + if (ASTATUS() == 0xFF) { + pr_err("IO address %Xh is empty!\n", ioaddr); + ret = -EIO; + goto out_port; + } + if (com20020_check(dev)) { + ret = -EIO; + goto out_port; + } + + card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev), + GFP_KERNEL); + if (!card) { + pr_err("%s out of memory!\n", __func__); + return -ENOMEM; + } + + card->index = i; + card->pci_priv = priv; + card->dev = dev; + + dev_set_drvdata(&dev->dev, card); + + ret = com20020_found(dev, IRQF_SHARED); + if (ret) + goto out_port; + + list_add(&card->list, &priv->list_dev); + } + + pci_set_drvdata(pdev, priv); + + return 0; + +out_port: + com20020pci_remove(pdev); + return ret; +} + +static void com20020pci_remove(struct pci_dev *pdev) +{ + struct com20020_dev *card, *tmpcard; + struct com20020_priv *priv; + + priv = pci_get_drvdata(pdev); + + list_for_each_entry_safe(card, tmpcard, &priv->list_dev, list) { + struct net_device *dev = card->dev; + + unregister_netdev(dev); + free_irq(dev->irq, dev); + free_netdev(dev); + } +} + +static struct com20020_pci_card_info card_info_10mbit = { + .name = "ARC-PCI", + .devcount = 1, + .chan_map_tbl = { + { 2, 0x00, 0x08 }, + }, + .flags = ARC_CAN_10MBIT, +}; + +static struct com20020_pci_card_info card_info_5mbit = { + .name = "ARC-PCI", + .devcount = 1, + .chan_map_tbl = { + { 2, 0x00, 0x08 }, + }, + .flags = ARC_IS_5MBIT, +}; + +static struct com20020_pci_card_info card_info_sohard = { + .name = "PLX-PCI", + .devcount = 1, + /* SOHARD needs PCI base addr 4 */ + .chan_map_tbl = { + {4, 0x00, 0x08}, + }, + .flags = ARC_CAN_10MBIT, +}; + +static struct com20020_pci_card_info card_info_eae_arc1 = { + .name = "EAE PLX-PCI ARC1", + .devcount = 1, + .chan_map_tbl = { + { 2, 0x00, 0x08 }, + }, + .flags = ARC_CAN_10MBIT, +}; + +static struct com20020_pci_card_info card_info_eae_ma1 = { + .name = "EAE PLX-PCI MA1", + .devcount = 2, + .chan_map_tbl = { + { 2, 0x00, 0x08 }, + { 2, 0x08, 0x08 } + }, + .flags = ARC_CAN_10MBIT, +}; + +static const 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, + (kernel_ulong_t)&card_info_5mbit + }, + { + 0x1571, 0xa00a, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_5mbit + }, + { + 0x1571, 0xa00b, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_5mbit + }, + { + 0x1571, 0xa00c, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_5mbit + }, + { + 0x1571, 0xa00d, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_5mbit + }, + { + 0x1571, 0xa00e, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_5mbit + }, + { + 0x1571, 0xa201, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_10mbit + }, + { + 0x1571, 0xa202, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_10mbit + }, + { + 0x1571, 0xa203, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_10mbit + }, + { + 0x1571, 0xa204, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_10mbit + }, + { + 0x1571, 0xa205, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_10mbit + }, + { + 0x1571, 0xa206, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_10mbit + }, + { + 0x10B5, 0x9030, + 0x10B5, 0x2978, + 0, 0, + (kernel_ulong_t)&card_info_sohard + }, + { + 0x10B5, 0x9050, + 0x10B5, 0x2273, + 0, 0, + (kernel_ulong_t)&card_info_sohard + }, + { + 0x10B5, 0x9050, + 0x10B5, 0x3263, + 0, 0, + (kernel_ulong_t)&card_info_eae_arc1 + }, + { + 0x10B5, 0x9050, + 0x10B5, 0x3292, + 0, 0, + (kernel_ulong_t)&card_info_eae_ma1 + }, + { + 0x14BA, 0x6000, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_10mbit + }, + { + 0x10B5, 0x2200, + PCI_ANY_ID, PCI_ANY_ID, + 0, 0, + (kernel_ulong_t)&card_info_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 = com20020pci_remove, +}; + +static int __init com20020pci_init(void) +{ + BUGLVL(D_NORMAL) printk(VERSION); + return pci_register_driver(&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 000000000..1a8437842 --- /dev/null +++ b/drivers/net/arcnet/com20020.c @@ -0,0 +1,383 @@ +/* + * 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/errno.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/init.h> +#include <linux/interrupt.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 = netdev_priv(dev); + + 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->clockm != 0) + { + 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; +} + +static int com20020_set_hwaddr(struct net_device *dev, void *addr) +{ + int ioaddr = dev->base_addr; + struct arcnet_local *lp = netdev_priv(dev); + struct sockaddr *hwaddr = addr; + + memcpy(dev->dev_addr, hwaddr->sa_data, 1); + SET_SUBADR(SUB_NODE); + outb(dev->dev_addr[0], _XREG); + + return 0; +} + +const struct net_device_ops com20020_netdev_ops = { + .ndo_open = arcnet_open, + .ndo_stop = arcnet_close, + .ndo_start_xmit = arcnet_send_packet, + .ndo_tx_timeout = arcnet_timeout, + .ndo_set_mac_address = com20020_set_hwaddr, + .ndo_set_rx_mode = com20020_set_mc_list, +}; + +/* 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 = netdev_priv(dev); + + 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; + + 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 = netdev_priv(dev); + 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__,__func__,dev,lp,dev->name); + BUGMSG(D_INIT, "Resetting %s (status=%02Xh)\n", + dev->name, ASTATUS()); + + BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + lp->config = TXENcfg | (lp->timeout << 3) | (lp->backplane << 2); + /* power-up defaults */ + SETCONF; + BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + + 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__,__func__); + ACOMMAND(CFLAGScmd | RESETclear | CONFIGclear); + + /* verify that the ARCnet signature byte is present */ + BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + + com20020_copy_from_card(dev, 0, 0, &inbyte, 1); + BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + if (inbyte != TESTvalue) { + BUGMSG(D_DEBUG, "%s: %d: %s\n",__FILE__,__LINE__,__func__); + 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__,__func__); + + /* 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 = netdev_priv(dev); + 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 = netdev_priv(dev); + 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); + } +} + +#if defined(CONFIG_ARCNET_COM20020_PCI_MODULE) || \ + defined(CONFIG_ARCNET_COM20020_ISA_MODULE) || \ + defined(CONFIG_ARCNET_COM20020_CS_MODULE) +EXPORT_SYMBOL(com20020_check); +EXPORT_SYMBOL(com20020_found); +EXPORT_SYMBOL(com20020_netdev_ops); +#endif + +MODULE_LICENSE("GPL"); + +#ifdef MODULE + +static int __init com20020_module_init(void) +{ + BUGLVL(D_NORMAL) printk(VERSION); + return 0; +} + +static void __exit com20020_module_exit(void) +{ +} +module_init(com20020_module_init); +module_exit(com20020_module_exit); +#endif /* MODULE */ diff --git a/drivers/net/arcnet/com20020_cs.c b/drivers/net/arcnet/com20020_cs.c new file mode 100644 index 000000000..057d95821 --- /dev/null +++ b/drivers/net/arcnet/com20020_cs.c @@ -0,0 +1,331 @@ +/* + * Linux ARCnet driver - COM20020 PCMCIA support + * + * Written 1994-1999 by Avery Pennarun, + * based on an ISA version by David Woodhouse. + * Derived from ibmtr_cs.c by Steve Kipisz (pcmcia-cs 3.1.4) + * which was derived from pcnet_cs.c by David Hinds. + * Some additional portions 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. + * + * ********************** + * Changes: + * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000 + * - reorganize kmallocs in com20020_attach, checking all for failure + * and releasing the previous allocations if one fails + * ********************** + * + * For more details, see drivers/net/arcnet.c + * + * ********************** + */ +#include <linux/kernel.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/timer.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/arcdevice.h> +#include <linux/com20020.h> + +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> + +#include <asm/io.h> + +#define VERSION "arcnet: COM20020 PCMCIA support loaded.\n" + + +static void regdump(struct net_device *dev) +{ +#ifdef DEBUG + int ioaddr = dev->base_addr; + int count; + + netdev_dbg(dev, "register dump:\n"); + for (count = ioaddr; count < ioaddr + 16; count++) + { + if (!(count % 16)) + pr_cont("%04X:", count); + pr_cont(" %02X", inb(count)); + } + pr_cont("\n"); + + netdev_dbg(dev, "buffer0 dump:\n"); + /* set up the address register */ + count = 0; + outb((count >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI); + outb(count & 0xff, _ADDR_LO); + + for (count = 0; count < 256+32; count++) + { + if (!(count % 16)) + pr_cont("%04X:", count); + + /* copy the data */ + pr_cont(" %02X", inb(_MEMDATA)); + } + pr_cont("\n"); +#endif +} + + + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +static int node; +static int timeout = 3; +static int backplane; +static int clockp; +static int clockm; + +module_param(node, int, 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 com20020_config(struct pcmcia_device *link); +static void com20020_release(struct pcmcia_device *link); + +static void com20020_detach(struct pcmcia_device *p_dev); + +/*====================================================================*/ + +static int com20020_probe(struct pcmcia_device *p_dev) +{ + struct com20020_dev *info; + struct net_device *dev; + struct arcnet_local *lp; + + dev_dbg(&p_dev->dev, "com20020_attach()\n"); + + /* Create new network device */ + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + goto fail_alloc_info; + + dev = alloc_arcdev(""); + if (!dev) + goto fail_alloc_dev; + + lp = netdev_priv(dev); + lp->timeout = timeout; + lp->backplane = backplane; + lp->clockp = clockp; + lp->clockm = clockm & 3; + lp->hw.owner = THIS_MODULE; + + /* fill in our module parameters as defaults */ + dev->dev_addr[0] = node; + + p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_8; + p_dev->resource[0]->end = 16; + p_dev->config_flags |= CONF_ENABLE_IRQ; + + info->dev = dev; + p_dev->priv = info; + + return com20020_config(p_dev); + +fail_alloc_dev: + kfree(info); +fail_alloc_info: + return -ENOMEM; +} /* com20020_attach */ + +static void com20020_detach(struct pcmcia_device *link) +{ + struct com20020_dev *info = link->priv; + struct net_device *dev = info->dev; + + dev_dbg(&link->dev, "detach...\n"); + + dev_dbg(&link->dev, "com20020_detach\n"); + + dev_dbg(&link->dev, "unregister...\n"); + + unregister_netdev(dev); + + /* + * this is necessary because we register our IRQ separately + * from card services. + */ + if (dev->irq) + free_irq(dev->irq, dev); + + com20020_release(link); + + /* Unlink device structure, free bits */ + dev_dbg(&link->dev, "unlinking...\n"); + if (link->priv) + { + dev = info->dev; + if (dev) + { + dev_dbg(&link->dev, "kfree...\n"); + free_netdev(dev); + } + dev_dbg(&link->dev, "kfree2...\n"); + kfree(info); + } + +} /* com20020_detach */ + +static int com20020_config(struct pcmcia_device *link) +{ + struct arcnet_local *lp; + struct com20020_dev *info; + struct net_device *dev; + int i, ret; + int ioaddr; + + info = link->priv; + dev = info->dev; + + dev_dbg(&link->dev, "config...\n"); + + dev_dbg(&link->dev, "com20020_config\n"); + + dev_dbg(&link->dev, "baseport1 is %Xh\n", + (unsigned int) link->resource[0]->start); + + i = -ENODEV; + link->io_lines = 16; + + if (!link->resource[0]->start) + { + for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10) + { + link->resource[0]->start = ioaddr; + i = pcmcia_request_io(link); + if (i == 0) + break; + } + } + else + i = pcmcia_request_io(link); + + if (i != 0) + { + dev_dbg(&link->dev, "requestIO failed totally!\n"); + goto failed; + } + + ioaddr = dev->base_addr = link->resource[0]->start; + dev_dbg(&link->dev, "got ioaddr %Xh\n", ioaddr); + + dev_dbg(&link->dev, "request IRQ %d\n", + link->irq); + if (!link->irq) + { + dev_dbg(&link->dev, "requestIRQ failed totally!\n"); + goto failed; + } + + dev->irq = link->irq; + + ret = pcmcia_enable_device(link); + if (ret) + goto failed; + + if (com20020_check(dev)) + { + regdump(dev); + goto failed; + } + + lp = netdev_priv(dev); + lp->card_name = "PCMCIA COM20020"; + lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */ + + SET_NETDEV_DEV(dev, &link->dev); + + i = com20020_found(dev, 0); /* calls register_netdev */ + + if (i != 0) { + dev_notice(&link->dev, + "com20020_found() failed\n"); + goto failed; + } + + netdev_dbg(dev, "port %#3lx, irq %d\n", + dev->base_addr, dev->irq); + return 0; + +failed: + dev_dbg(&link->dev, "com20020_config failed...\n"); + com20020_release(link); + return -ENODEV; +} /* com20020_config */ + +static void com20020_release(struct pcmcia_device *link) +{ + dev_dbg(&link->dev, "com20020_release\n"); + pcmcia_disable_device(link); +} + +static int com20020_suspend(struct pcmcia_device *link) +{ + struct com20020_dev *info = link->priv; + struct net_device *dev = info->dev; + + if (link->open) + netif_device_detach(dev); + + return 0; +} + +static int com20020_resume(struct pcmcia_device *link) +{ + struct com20020_dev *info = link->priv; + struct net_device *dev = info->dev; + + if (link->open) { + int ioaddr = dev->base_addr; + struct arcnet_local *lp = netdev_priv(dev); + ARCRESET; + } + + return 0; +} + +static const struct pcmcia_device_id com20020_ids[] = { + PCMCIA_DEVICE_PROD_ID12("Contemporary Control Systems, Inc.", + "PCM20 Arcnet Adapter", 0x59991666, 0x95dfffaf), + PCMCIA_DEVICE_PROD_ID12("SoHard AG", + "SH ARC PCMCIA", 0xf8991729, 0x69dff0c7), + PCMCIA_DEVICE_NULL +}; +MODULE_DEVICE_TABLE(pcmcia, com20020_ids); + +static struct pcmcia_driver com20020_cs_driver = { + .owner = THIS_MODULE, + .name = "com20020_cs", + .probe = com20020_probe, + .remove = com20020_detach, + .id_table = com20020_ids, + .suspend = com20020_suspend, + .resume = com20020_resume, +}; +module_pcmcia_driver(com20020_cs_driver); diff --git a/drivers/net/arcnet/com90io.c b/drivers/net/arcnet/com90io.c new file mode 100644 index 000000000..487d780eb --- /dev/null +++ b/drivers/net/arcnet/com90io.c @@ -0,0 +1,433 @@ +/* + * 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/delay.h> +#include <linux/netdevice.h> +#include <linux/bootmem.h> +#include <linux/init.h> +#include <linux/interrupt.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 request_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 ((int)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 */ + if (!request_region(dev->base_addr, ARCNET_TOTAL_SIZE, "arcnet (COM90xx-IO)")) { + free_irq(dev->irq, dev); + return -EBUSY; + } + + lp = netdev_priv(dev); + 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 = netdev_priv(dev); + 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; + + 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 000000000..b80fbe40a --- /dev/null +++ b/drivers/net/arcnet/com90xx.c @@ -0,0 +1,704 @@ +/* + * 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/interrupt.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/slab.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, void __iomem *); +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}; + unsigned long *shmems; + void __iomem **iomem; + int numports, numshmems, *port; + u_long *p; + int index; + + if (!io && !irq && !shmem && !*device && com90xx_skip_probe) + return; + + shmems = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(unsigned long), + GFP_KERNEL); + if (!shmems) + return; + iomem = kzalloc(((0x100000-0xa0000) / 0x800) * sizeof(void __iomem *), + GFP_KERNEL); + if (!iomem) { + kfree(shmems); + 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"); + kfree(shmems); + kfree(iomem); + 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 (index = 0, p = &shmems[0]; index < numshmems; p++, index++) { + void __iomem *base; + + numprint++; + numprint %= 8; + if (!numprint) { + BUGMSG2(D_INIT, "\n"); + BUGMSG2(D_INIT, "S3: "); + } + BUGMSG2(D_INIT, "%lXh ", *p); + + if (!request_mem_region(*p, MIRROR_SIZE, "arcnet (90xx)")) { + BUGMSG2(D_INIT_REASONS, "(request_mem_region)\n"); + BUGMSG2(D_INIT_REASONS, "Stage 3: "); + BUGLVL(D_INIT_REASONS) numprint = 0; + goto out; + } + base = ioremap(*p, MIRROR_SIZE); + if (!base) { + BUGMSG2(D_INIT_REASONS, "(ioremap)\n"); + BUGMSG2(D_INIT_REASONS, "Stage 3: "); + BUGLVL(D_INIT_REASONS) numprint = 0; + goto out1; + } + if (readb(base) != TESTvalue) { + BUGMSG2(D_INIT_REASONS, "(%02Xh != %02Xh)\n", + readb(base), TESTvalue); + BUGMSG2(D_INIT_REASONS, "S3: "); + BUGLVL(D_INIT_REASONS) numprint = 0; + goto out2; + } + /* 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. + */ + writeb(0x42, base); + if (readb(base) != 0x42) { + BUGMSG2(D_INIT_REASONS, "(read only)\n"); + BUGMSG2(D_INIT_REASONS, "S3: "); + goto out2; + } + BUGMSG2(D_INIT_REASONS, "\n"); + BUGMSG2(D_INIT_REASONS, "S3: "); + BUGLVL(D_INIT_REASONS) numprint = 0; + iomem[index] = base; + continue; + out2: + iounmap(base); + out1: + release_mem_region(*p, MIRROR_SIZE); + out: + *p-- = shmems[--numshmems]; + index--; + } + 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); + kfree(shmems); + kfree(iomem); + 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 */ + writeb(TESTvalue, iomem[0]); + } +#else + inb(_RESET); + mdelay(RESETtime); +#endif + + for (index = 0; index < numshmems; index++) { + u_long ptr = shmems[index]; + void __iomem *base = iomem[index]; + + if (readb(base) == TESTvalue) { /* found one */ + BUGMSG2(D_INIT, "%lXh)\n", *p); + openparen = 0; + + /* register the card */ + if (com90xx_found(*port, airq, ptr, base) == 0) + found = 1; + numprint = -1; + + /* remove shmem from the list */ + shmems[index] = shmems[--numshmems]; + iomem[index] = iomem[numshmems]; + break; /* go to the next I/O port */ + } else { + BUGMSG2(D_INIT_REASONS, "%Xh-", readb(base)); + } + } + + 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 (index = 0; index < numshmems; index++) { + writeb(TESTvalue, iomem[index]); + iounmap(iomem[index]); + release_mem_region(shmems[index], MIRROR_SIZE); + } + kfree(shmems); + kfree(iomem); +} + +static int check_mirror(unsigned long addr, size_t size) +{ + void __iomem *p; + int res = -1; + + if (!request_mem_region(addr, size, "arcnet (90xx)")) + return -1; + + p = ioremap(addr, size); + if (p) { + if (readb(p) == TESTvalue) + res = 1; + else + res = 0; + iounmap(p); + } + + release_mem_region(addr, size); + return res; +} + +/* 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, void __iomem *p) +{ + 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"); + iounmap(p); + release_mem_region(shmem, MIRROR_SIZE); + return -ENOMEM; + } + lp = netdev_priv(dev); + /* 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 (readb(p) == TESTvalue && + check_mirror(shmem - MIRROR_SIZE, MIRROR_SIZE) == 0 && + check_mirror(shmem - 2 * MIRROR_SIZE, MIRROR_SIZE) == 1) + mirror_size = 2 * MIRROR_SIZE; + + first_mirror = shmem - mirror_size; + while (check_mirror(first_mirror, mirror_size) == 1) + first_mirror -= mirror_size; + first_mirror += mirror_size; + + last_mirror = shmem + mirror_size; + while (check_mirror(last_mirror, mirror_size) == 1) + last_mirror += mirror_size; + last_mirror -= mirror_size; + + dev->mem_start = first_mirror; + dev->mem_end = last_mirror + MIRROR_SIZE - 1; + + iounmap(p); + release_mem_region(shmem, MIRROR_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. + */ +static int com90xx_reset(struct net_device *dev, int really_reset) +{ + struct arcnet_local *lp = netdev_priv(dev); + 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 = netdev_priv(dev); + 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 = netdev_priv(dev); + 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 = netdev_priv(dev); + + 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 000000000..f81db4070 --- /dev/null +++ b/drivers/net/arcnet/rfc1051.c @@ -0,0 +1,251 @@ +/* + * 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/gfp.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 __be16 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 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 __be16 type_trans(struct sk_buff *skb, struct net_device *dev) +{ + 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_reset_mac_header(skb); + 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: + dev->stats.rx_errors++; + dev->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 = netdev_priv(dev); + 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"); + dev->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); +} + + +/* + * 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) +{ + 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); + dev->stats.tx_errors++; + dev->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 = netdev_priv(dev); + 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 000000000..b71431aae --- /dev/null +++ b/drivers/net/arcnet/rfc1201.c @@ -0,0 +1,547 @@ +/* + * 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/gfp.h> +#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 __be16 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); + +static 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 __be16 type_trans(struct sk_buff *skb, struct net_device *dev) +{ + struct archdr *pkt = (struct archdr *) skb->data; + struct arc_rfc1201 *soft = &pkt->soft.rfc1201; + int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE; + + /* Pull off the arcnet header. */ + skb_reset_mac_header(skb); + 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: + dev->stats.rx_errors++; + dev->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 = netdev_priv(dev); + 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); + dev->stats.rx_errors++; + dev->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"); + dev->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); + dev->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); + dev->stats.rx_errors++; + dev->stats.rx_crc_errors++; + } + } + BUGLVL(D_SKB) arcnet_dump_skb(dev, skb, "rx"); + + skb->protocol = type_trans(skb, dev); + netif_rx(skb); + } 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; + dev->stats.rx_errors++; + dev->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); + dev->stats.rx_errors++; + dev->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; + dev->stats.rx_errors++; + dev->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; + dev->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); + dev->stats.rx_errors++; + dev->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); + dev->stats.rx_errors++; + dev->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; + dev->stats.rx_errors++; + dev->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); + } + } +} + + +/* 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 = netdev_priv(dev); + 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); + dev->stats.tx_errors++; + dev->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 = netdev_priv(dev); + 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 = htons(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 = netdev_priv(dev); + 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 = netdev_priv(dev); + 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; +} |