diff options
| author | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 09:55:01 -0400 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 11:08:21 -0400 |
| commit | 4617721c502b2ddaa4e324e86da4997edf738fa5 (patch) | |
| tree | 350aa83b47da0b574a814795392b20b0bed83cbc /drivers/net/arcnet | |
| download | linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip | |
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since
it can be declared outside of the ftrace.c code, it calls
ftrace_ops_init() on the ops in every global function. The issue is that
if two tasks call it on the same ops at the same time it is possible to
have the initialization of one corrupt the initialization of the other
call.
Create a ops_mutex to use to synchronize every initialization of the
ftrace_ops. The mutex is taken within checking the ftrace_ops flag that
states it was initializied but the flag is checked again after the mutex
has been taken. Checking first outside the mutex allows it to shortcut
having to take the mutex. But then the check needs to be done again after
the mute is taken in case of races.
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home
Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock")
Reported-by: sashiko-bot@kernel.org
Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/net/arcnet')
| -rw-r--r-- | drivers/net/arcnet/Kconfig | 86 | ||||
| -rw-r--r-- | drivers/net/arcnet/Makefile | 11 | ||||
| -rw-r--r-- | drivers/net/arcnet/arc-rawmode.c | 190 | ||||
| -rw-r--r-- | drivers/net/arcnet/arcdevice.h | 376 | ||||
| -rw-r--r-- | drivers/net/arcnet/arcnet.c | 1217 | ||||
| -rw-r--r-- | drivers/net/arcnet/capmode.c | 269 | ||||
| -rw-r--r-- | drivers/net/arcnet/com20020-pci.c | 556 | ||||
| -rw-r--r-- | drivers/net/arcnet/com20020.c | 394 | ||||
| -rw-r--r-- | drivers/net/arcnet/com20020.h | 132 | ||||
| -rw-r--r-- | drivers/net/arcnet/com9026.h | 18 | ||||
| -rw-r--r-- | drivers/net/arcnet/rfc1051.c | 243 | ||||
| -rw-r--r-- | drivers/net/arcnet/rfc1201.c | 548 |
12 files changed, 4040 insertions, 0 deletions
diff --git a/drivers/net/arcnet/Kconfig b/drivers/net/arcnet/Kconfig new file mode 100644 index 000000000..4611a3716 --- /dev/null +++ b/drivers/net/arcnet/Kconfig @@ -0,0 +1,86 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Arcnet configuration +# + +menuconfig ARCNET + depends on NETDEVICES && PCI && HAS_IOPORT + 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.rst>. + + You need both this driver, and the driver for the particular ARCnet + chipset of your card. + + 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.rst> + 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.rst> 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_COM20020 + tristate "ARCnet COM20020 chipset driver" + depends on LEDS_CLASS + 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_PCI + tristate "Support for COM20020 on PCI" + depends on ARCNET_COM20020 && PCI + +endif # ARCNET diff --git a/drivers/net/arcnet/Makefile b/drivers/net/arcnet/Makefile new file mode 100644 index 000000000..e5df03a08 --- /dev/null +++ b/drivers/net/arcnet/Makefile @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0 +# 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_COM20020) += com20020.o +obj-$(CONFIG_ARCNET_COM20020_PCI) += com20020-pci.o diff --git a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c new file mode 100644 index 000000000..57f172906 --- /dev/null +++ b/drivers/net/arcnet/arc-rawmode.c @@ -0,0 +1,190 @@ +/* + * 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 + * + * ********************** + */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + +#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 "arcdevice.h" + +/* 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; + + arc_printk(D_DURING, dev, "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) { + 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)); + + if (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 = 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; + + arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n", + lp->next_tx, lp->cur_tx, bufnum); + + /* hard header is not included in packet length */ + length -= ARC_HDR_SIZE; + + if (length > XMTU) { + /* should never happen! other people already check for this. */ + arc_printk(D_NORMAL, dev, "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; + } + + arc_printk(D_DURING, dev, "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 */ +} + +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; + + pr_info("raw mode (`r') encapsulation support loaded\n"); + + 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_DESCRIPTION("ARCnet raw mode packet interface module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/arcnet/arcdevice.h b/drivers/net/arcnet/arcdevice.h new file mode 100644 index 000000000..a7c217359 --- /dev/null +++ b/drivers/net/arcnet/arcdevice.h @@ -0,0 +1,376 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. NET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Definitions used by the ARCnet driver. + * + * Authors: Avery Pennarun and David Woodhouse + */ +#ifndef _LINUX_ARCDEVICE_H +#define _LINUX_ARCDEVICE_H + +#include <asm/timex.h> +#include <linux/if_arcnet.h> + +#ifdef __KERNEL__ +#include <linux/interrupt.h> +#include <linux/workqueue.h> + +/* + * RECON_THRESHOLD is the maximum number of RECON messages to receive + * within one minute before printing a "cabling problem" warning. The + * default value should be fine. + * + * After that, a "cabling restored" message will be printed on the next IRQ + * if no RECON messages have been received for 10 seconds. + * + * Do not define RECON_THRESHOLD at all if you want to disable this feature. + */ +#define RECON_THRESHOLD 30 + +/* + * Define this to the minimum "timeout" value. If a transmit takes longer + * than TX_TIMEOUT jiffies, Linux will abort the TX and retry. On a large + * network, or one with heavy network traffic, this timeout may need to be + * increased. The larger it is, though, the longer it will be between + * necessary transmits - don't set this too high. + */ +#define TX_TIMEOUT (HZ * 200 / 1000) + +/* Display warnings about the driver being an ALPHA version. */ +#undef ALPHA_WARNING + +/* + * Debugging bitflags: each option can be enabled individually. + * + * Note: only debug flags included in the ARCNET_DEBUG_MAX define will + * actually be available. GCC will (at least, GCC 2.7.0 will) notice + * lines using a BUGLVL not in ARCNET_DEBUG_MAX and automatically optimize + * them out. + */ +#define D_NORMAL 1 /* important operational info */ +#define D_EXTRA 2 /* useful, but non-vital information */ +#define D_INIT 4 /* show init/probe messages */ +#define D_INIT_REASONS 8 /* show reasons for discarding probes */ +#define D_RECON 32 /* print a message whenever token is lost */ +#define D_PROTO 64 /* debug auto-protocol support */ +/* debug levels below give LOTS of output during normal operation! */ +#define D_DURING 128 /* trace operations (including irq's) */ +#define D_TX 256 /* show tx packets */ +#define D_RX 512 /* show rx packets */ +#define D_SKB 1024 /* show skb's */ +#define D_SKB_SIZE 2048 /* show skb sizes */ +#define D_TIMING 4096 /* show time needed to copy buffers to card */ +#define D_DEBUG 8192 /* Very detailed debug line for line */ + +#ifndef ARCNET_DEBUG_MAX +#define ARCNET_DEBUG_MAX (127) /* change to ~0 if you want detailed debugging */ +#endif + +#ifndef ARCNET_DEBUG +#define ARCNET_DEBUG (D_NORMAL | D_EXTRA) +#endif +extern int arcnet_debug; + +#define BUGLVL(x) ((x) & ARCNET_DEBUG_MAX & arcnet_debug) + +/* macros to simplify debug checking */ +#define arc_printk(x, dev, fmt, ...) \ +do { \ + if (BUGLVL(x)) { \ + if ((x) == D_NORMAL) \ + netdev_warn(dev, fmt, ##__VA_ARGS__); \ + else if ((x) < D_DURING) \ + netdev_info(dev, fmt, ##__VA_ARGS__); \ + else \ + netdev_dbg(dev, fmt, ##__VA_ARGS__); \ + } \ +} while (0) + +#define arc_cont(x, fmt, ...) \ +do { \ + if (BUGLVL(x)) \ + pr_cont(fmt, ##__VA_ARGS__); \ +} while (0) + +/* see how long a function call takes to run, expressed in CPU cycles */ +#define TIME(dev, name, bytes, call) \ +do { \ + if (BUGLVL(D_TIMING)) { \ + unsigned long _x, _y; \ + _x = get_cycles(); \ + call; \ + _y = get_cycles(); \ + arc_printk(D_TIMING, dev, \ + "%s: %d bytes in %lu cycles == %lu Kbytes/100Mcycle\n", \ + name, bytes, _y - _x, \ + 100000000 / 1024 * bytes / (_y - _x + 1)); \ + } else { \ + call; \ + } \ +} while (0) + +/* + * Time needed to reset the card - in ms (milliseconds). This works on my + * SMC PC100. I can't find a reference that tells me just how long I + * should wait. + */ +#define RESETtime (300) + +/* + * These are the max/min lengths of packet payload, not including the + * arc_hardware header, but definitely including the soft header. + * + * Note: packet sizes 254, 255, 256 are impossible because of the way + * ARCnet registers work That's why RFC1201 defines "exception" packets. + * In non-RFC1201 protocols, we have to just tack some extra bytes on the + * end. + */ +#define MTU 253 /* normal packet max size */ +#define MinTU 257 /* extended packet min size */ +#define XMTU 508 /* extended packet max size */ + +/* status/interrupt mask bit fields */ +#define TXFREEflag 0x01 /* transmitter available */ +#define TXACKflag 0x02 /* transmitted msg. ackd */ +#define RECONflag 0x04 /* network reconfigured */ +#define TESTflag 0x08 /* test flag */ +#define EXCNAKflag 0x08 /* excessive nak flag */ +#define RESETflag 0x10 /* power-on-reset */ +#define RES1flag 0x20 /* reserved - usually set by jumper */ +#define RES2flag 0x40 /* reserved - usually set by jumper */ +#define NORXflag 0x80 /* receiver inhibited */ + +/* Flags used for IO-mapped memory operations */ +#define AUTOINCflag 0x40 /* Increase location with each access */ +#define IOMAPflag 0x02 /* (for 90xx) Use IO mapped memory, not mmap */ +#define ENABLE16flag 0x80 /* (for 90xx) Enable 16-bit mode */ + +/* in the command register, the following bits have these meanings: + * 0-2 command + * 3-4 page number (for enable rcv/xmt command) + * 7 receive broadcasts + */ +#define NOTXcmd 0x01 /* disable transmitter */ +#define NORXcmd 0x02 /* disable receiver */ +#define TXcmd 0x03 /* enable transmitter */ +#define RXcmd 0x04 /* enable receiver */ +#define CONFIGcmd 0x05 /* define configuration */ +#define CFLAGScmd 0x06 /* clear flags */ +#define TESTcmd 0x07 /* load test flags */ +#define STARTIOcmd 0x18 /* start internal operation */ + +/* flags for "clear flags" command */ +#define RESETclear 0x08 /* power-on-reset */ +#define CONFIGclear 0x10 /* system reconfigured */ + +#define EXCNAKclear 0x0E /* Clear and acknowledge the excessive nak bit */ + +/* flags for "load test flags" command */ +#define TESTload 0x08 /* test flag (diagnostic) */ + +/* byte deposited into first address of buffers on reset */ +#define TESTvalue 0321 /* that's octal for 0xD1 :) */ + +/* for "enable receiver" command */ +#define RXbcasts 0x80 /* receive broadcasts */ + +/* flags for "define configuration" command */ +#define NORMALconf 0x00 /* 1-249 byte packets */ +#define EXTconf 0x08 /* 250-504 byte packets */ + +/* card feature flags, set during auto-detection. + * (currently only used by com20020pci) + */ +#define ARC_IS_5MBIT 1 /* card default speed is 5MBit */ +#define ARC_CAN_10MBIT 2 /* card uses COM20022, supporting 10MBit, + but default is 2.5MBit. */ +#define ARC_HAS_LED 4 /* card has software controlled LEDs */ +#define ARC_HAS_ROTARY 8 /* card has rotary encoder */ + +/* information needed to define an encapsulation driver */ +struct ArcProto { + char suffix; /* a for RFC1201, e for ether-encap, etc. */ + int mtu; /* largest possible packet */ + int is_ip; /* This is an ip plugin - not a raw thing */ + + void (*rx)(struct net_device *dev, int bufnum, + struct archdr *pkthdr, int length); + int (*build_header)(struct sk_buff *skb, struct net_device *dev, + unsigned short ethproto, uint8_t daddr); + + /* these functions return '1' if the skb can now be freed */ + int (*prepare_tx)(struct net_device *dev, struct archdr *pkt, + int length, int bufnum); + int (*continue_tx)(struct net_device *dev, int bufnum); + int (*ack_tx)(struct net_device *dev, int acked); +}; + +extern struct ArcProto *arc_proto_map[256], *arc_proto_default, + *arc_bcast_proto, *arc_raw_proto; + +/* + * "Incoming" is information needed for each address that could be sending + * to us. Mostly for partially-received split packets. + */ +struct Incoming { + struct sk_buff *skb; /* packet data buffer */ + __be16 sequence; /* sequence number of assembly */ + uint8_t lastpacket, /* number of last packet (from 1) */ + numpackets; /* number of packets in split */ +}; + +/* only needed for RFC1201 */ +struct Outgoing { + struct ArcProto *proto; /* protocol driver that owns this: + * if NULL, no packet is pending. + */ + struct sk_buff *skb; /* buffer from upper levels */ + struct archdr *pkt; /* a pointer into the skb */ + uint16_t length, /* bytes total */ + dataleft, /* bytes left */ + segnum, /* segment being sent */ + numsegs; /* number of segments */ +}; + +#define ARCNET_LED_NAME_SZ (IFNAMSIZ + 6) + +struct arcnet_local { + uint8_t config, /* current value of CONFIG register */ + timeout, /* Extended timeout for COM20020 */ + backplane, /* Backplane flag for COM20020 */ + clockp, /* COM20020 clock divider */ + clockm, /* COM20020 clock multiplier flag */ + setup, /* Contents of setup1 register */ + setup2, /* Contents of setup2 register */ + intmask; /* current value of INTMASK register */ + uint8_t default_proto[256]; /* default encap to use for each host */ + int cur_tx, /* buffer used by current transmit, or -1 */ + next_tx, /* buffer where a packet is ready to send */ + cur_rx; /* current receive buffer */ + int lastload_dest, /* can last loaded packet be acked? */ + lasttrans_dest; /* can last TX'd packet be acked? */ + int timed_out; /* need to process TX timeout and drop packet */ + unsigned long last_timeout; /* time of last reported timeout */ + char *card_name; /* card ident string */ + int card_flags; /* special card features */ + + /* On preemptive and SMP a lock is needed */ + spinlock_t lock; + + struct led_trigger *tx_led_trig; + char tx_led_trig_name[ARCNET_LED_NAME_SZ]; + struct led_trigger *recon_led_trig; + char recon_led_trig_name[ARCNET_LED_NAME_SZ]; + + struct timer_list timer; + + struct net_device *dev; + int reply_status; + struct work_struct reply_work; + + /* + * Buffer management: an ARCnet card has 4 x 512-byte buffers, each of + * which can be used for either sending or receiving. The new dynamic + * buffer management routines use a simple circular queue of available + * buffers, and take them as they're needed. This way, we simplify + * situations in which we (for example) want to pre-load a transmit + * buffer, or start receiving while we copy a received packet to + * memory. + * + * The rules: only the interrupt handler is allowed to _add_ buffers to + * the queue; thus, this doesn't require a lock. Both the interrupt + * handler and the transmit function will want to _remove_ buffers, so + * we need to handle the situation where they try to do it at the same + * time. + * + * If next_buf == first_free_buf, the queue is empty. Since there are + * only four possible buffers, the queue should never be full. + */ + atomic_t buf_lock; + int buf_queue[5]; + int next_buf, first_free_buf; + + /* network "reconfiguration" handling */ + unsigned long first_recon; /* time of "first" RECON message to count */ + unsigned long last_recon; /* time of most recent RECON */ + int num_recons; /* number of RECONs between first and last. */ + int network_down; /* do we think the network is down? */ + + int excnak_pending; /* We just got an excessive nak interrupt */ + + /* RESET flag handling */ + int reset_in_progress; + struct work_struct reset_work; + + struct { + uint16_t sequence; /* sequence number (incs with each packet) */ + __be16 aborted_seq; + + struct Incoming incoming[256]; /* one from each address */ + } rfc1201; + + /* really only used by rfc1201, but we'll pretend it's not */ + struct Outgoing outgoing; /* packet currently being sent */ + + /* hardware-specific functions */ + struct { + struct module *owner; + void (*command)(struct net_device *dev, int cmd); + int (*status)(struct net_device *dev); + void (*intmask)(struct net_device *dev, int mask); + int (*reset)(struct net_device *dev, int really_reset); + void (*open)(struct net_device *dev); + void (*close)(struct net_device *dev); + void (*datatrigger) (struct net_device * dev, int enable); + void (*recontrigger) (struct net_device * dev, int enable); + + void (*copy_to_card)(struct net_device *dev, int bufnum, + int offset, void *buf, int count); + void (*copy_from_card)(struct net_device *dev, int bufnum, + int offset, void *buf, int count); + } hw; + + void __iomem *mem_start; /* pointer to ioremap'ed MMIO */ +}; + +enum arcnet_led_event { + ARCNET_LED_EVENT_RECON, + ARCNET_LED_EVENT_OPEN, + ARCNET_LED_EVENT_STOP, + ARCNET_LED_EVENT_TX, +}; + +void arcnet_led_event(struct net_device *netdev, enum arcnet_led_event event); +void devm_arcnet_led_init(struct net_device *netdev, int index, int subid); + +#if ARCNET_DEBUG_MAX & D_SKB +void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc); +#else +static inline +void arcnet_dump_skb(struct net_device *dev, struct sk_buff *skb, char *desc) +{ +} +#endif + +void arcnet_unregister_proto(struct ArcProto *proto); +irqreturn_t arcnet_interrupt(int irq, void *dev_id); + +struct net_device *alloc_arcdev(const char *name); +void free_arcdev(struct net_device *dev); + +int arcnet_open(struct net_device *dev); +int arcnet_close(struct net_device *dev); +netdev_tx_t arcnet_send_packet(struct sk_buff *skb, + struct net_device *dev); +void arcnet_timeout(struct net_device *dev, unsigned int txqueue); + +static inline void arcnet_set_addr(struct net_device *dev, u8 addr) +{ + dev_addr_set(dev, &addr); +} + +#endif /* __KERNEL__ */ +#endif /* _LINUX_ARCDEVICE_H */ diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c new file mode 100644 index 000000000..882972604 --- /dev/null +++ b/drivers/net/arcnet/arcnet.c @@ -0,0 +1,1217 @@ +/* + * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#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/jiffies.h> +#include <linux/errqueue.h> + +#include <linux/leds.h> +#include <linux/workqueue.h> + +#include "arcdevice.h" +#include "com9026.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]; +EXPORT_SYMBOL(arc_proto_map); + +struct ArcProto *arc_proto_default; +EXPORT_SYMBOL(arc_proto_default); + +struct ArcProto *arc_bcast_proto; +EXPORT_SYMBOL(arc_bcast_proto); + +struct ArcProto *arc_raw_proto; +EXPORT_SYMBOL(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(arcnet_debug); + +/* 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_DESCRIPTION("ARCnet core driver"); +MODULE_LICENSE("GPL"); + +static int __init arcnet_init(void) +{ + int count; + + arcnet_debug = debug; + + pr_info("arcnet loaded\n"); + + /* 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; + + if (BUGLVL(D_DURING)) + pr_info("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 + +/* Trigger a LED event in response to a ARCNET device event */ +void arcnet_led_event(struct net_device *dev, enum arcnet_led_event event) +{ + struct arcnet_local *lp = netdev_priv(dev); + + switch (event) { + case ARCNET_LED_EVENT_RECON: + led_trigger_blink_oneshot(lp->recon_led_trig, 350, 350, 0); + break; + case ARCNET_LED_EVENT_OPEN: + led_trigger_event(lp->tx_led_trig, LED_OFF); + led_trigger_event(lp->recon_led_trig, LED_OFF); + break; + case ARCNET_LED_EVENT_STOP: + led_trigger_event(lp->tx_led_trig, LED_OFF); + led_trigger_event(lp->recon_led_trig, LED_OFF); + break; + case ARCNET_LED_EVENT_TX: + led_trigger_blink_oneshot(lp->tx_led_trig, 50, 50, 0); + break; + } +} +EXPORT_SYMBOL_GPL(arcnet_led_event); + +static void arcnet_led_release(struct device *gendev, void *res) +{ + struct arcnet_local *lp = netdev_priv(to_net_dev(gendev)); + + led_trigger_unregister_simple(lp->tx_led_trig); + led_trigger_unregister_simple(lp->recon_led_trig); +} + +/* Register ARCNET LED triggers for a arcnet device + * + * This is normally called from a driver's probe function + */ +void devm_arcnet_led_init(struct net_device *netdev, int index, int subid) +{ + struct arcnet_local *lp = netdev_priv(netdev); + void *res; + + res = devres_alloc(arcnet_led_release, 0, GFP_KERNEL); + if (!res) { + netdev_err(netdev, "cannot register LED triggers\n"); + return; + } + + snprintf(lp->tx_led_trig_name, sizeof(lp->tx_led_trig_name), + "arc%d-%d-tx", index, subid); + snprintf(lp->recon_led_trig_name, sizeof(lp->recon_led_trig_name), + "arc%d-%d-recon", index, subid); + + led_trigger_register_simple(lp->tx_led_trig_name, + &lp->tx_led_trig); + led_trigger_register_simple(lp->recon_led_trig_name, + &lp->recon_led_trig); + + devres_add(&netdev->dev, res); +} +EXPORT_SYMBOL_GPL(devm_arcnet_led_init); + +/* 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; + } +} +EXPORT_SYMBOL(arcnet_unregister_proto); + +/* 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; + + if (BUGLVL(D_DURING)) { + arc_printk(D_DURING, dev, "release_arcbuf: freed #%d; buffer queue is now: ", + bufnum); + for (i = lp->next_buf; i != lp->first_free_buf; i = (i + 1) % 5) + arc_cont(D_DURING, "#%d ", lp->buf_queue[i]); + arc_cont(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 */ + arc_printk(D_NORMAL, dev, "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) { + arc_printk(D_NORMAL, dev, "get_arcbuf: BUG: no buffers are available??\n"); + } else { + buf = lp->buf_queue[lp->next_buf++]; + lp->next_buf %= 5; + } + } + + if (BUGLVL(D_DURING)) { + arc_printk(D_DURING, dev, "get_arcbuf: got #%d; buffer queue is now: ", + buf); + for (i = lp->next_buf; i != lp->first_free_buf; i = (i + 1) % 5) + arc_cont(D_DURING, "#%d ", lp->buf_queue[i]); + arc_cont(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 arc_hardware); + 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; +} + +static void arcnet_timer(struct timer_list *t) +{ + struct arcnet_local *lp = timer_container_of(lp, t, timer); + struct net_device *dev = lp->dev; + + spin_lock_irq(&lp->lock); + + if (!lp->reset_in_progress && !netif_carrier_ok(dev)) { + netif_carrier_on(dev); + netdev_info(dev, "link up\n"); + } + + spin_unlock_irq(&lp->lock); +} + +static void reset_device_work(struct work_struct *work) +{ + struct arcnet_local *lp; + struct net_device *dev; + + lp = container_of(work, struct arcnet_local, reset_work); + dev = lp->dev; + + /* Do not bring the network interface back up if an ifdown + * was already done. + */ + if (!netif_running(dev) || !lp->reset_in_progress) + return; + + rtnl_lock(); + + /* Do another check, in case of an ifdown that was triggered in + * the small race window between the exit condition above and + * acquiring RTNL. + */ + if (!netif_running(dev) || !lp->reset_in_progress) + goto out; + + dev_close(dev); + dev_open(dev, NULL); + +out: + rtnl_unlock(); +} + +static void arcnet_reply_work(struct work_struct *t) +{ + struct arcnet_local *lp = from_work(lp, t, reply_work); + + struct sk_buff *ackskb, *skb; + struct sock_exterr_skb *serr; + struct sock *sk; + int ret; + + local_irq_disable(); + skb = lp->outgoing.skb; + if (!skb || !skb->sk) { + local_irq_enable(); + return; + } + + sock_hold(skb->sk); + sk = skb->sk; + ackskb = skb_clone_sk(skb); + sock_put(skb->sk); + + if (!ackskb) { + local_irq_enable(); + return; + } + + serr = SKB_EXT_ERR(ackskb); + memset(serr, 0, sizeof(*serr)); + serr->ee.ee_errno = ENOMSG; + serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS; + serr->ee.ee_data = skb_shinfo(skb)->tskey; + serr->ee.ee_info = lp->reply_status; + + /* finally erasing outgoing skb */ + dev_kfree_skb(lp->outgoing.skb); + lp->outgoing.skb = NULL; + + ackskb->dev = lp->dev; + + ret = sock_queue_err_skb(sk, ackskb); + if (ret) + dev_kfree_skb_irq(ackskb); + + local_irq_enable(); +}; + +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); + + lp->dev = dev; + spin_lock_init(&lp->lock); + timer_setup(&lp->timer, arcnet_timer, 0); + INIT_WORK(&lp->reset_work, reset_device_work); + } + + return dev; +} +EXPORT_SYMBOL(alloc_arcdev); + +void free_arcdev(struct net_device *dev) +{ + struct arcnet_local *lp = netdev_priv(dev); + + /* Do not cancel this at ->ndo_close(), as the workqueue itself + * indirectly calls the ifdown path through dev_close(). + */ + cancel_work_sync(&lp->reset_work); + free_netdev(dev); +} +EXPORT_SYMBOL(free_arcdev); + +/* 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; + + arc_printk(D_INIT, dev, "opened."); + + if (!try_module_get(lp->hw.owner)) + return -ENODEV; + + if (BUGLVL(D_PROTO)) { + arc_printk(D_PROTO, dev, "protocol map (default is '%c'): ", + arc_proto_default->suffix); + for (count = 0; count < 256; count++) + arc_cont(D_PROTO, "%c", arc_proto_map[count]->suffix); + arc_cont(D_PROTO, "\n"); + } + + INIT_WORK(&lp->reply_work, arcnet_reply_work); + + arc_printk(D_INIT, dev, "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 (lp->hw.reset(dev, 0) && lp->hw.reset(dev, 1)) + goto out_module_put; + + newmtu = choose_mtu(); + if (newmtu < dev->mtu) + dev->mtu = newmtu; + + arc_printk(D_INIT, dev, "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) + arc_printk(D_NORMAL, dev, "WARNING! Station address 00 is reserved for broadcasts!\n"); + else if (dev->dev_addr[0] == 255) + arc_printk(D_NORMAL, dev, "WARNING! Station address FF may confuse DOS networking programs!\n"); + + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + if (lp->hw.status(dev) & RESETflag) { + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", + __FILE__, __LINE__, __func__); + lp->hw.command(dev, CFLAGScmd | RESETclear); + } + + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + /* make sure we're ready to receive IRQ's. */ + lp->hw.intmask(dev, 0); + udelay(1); /* give it time to set the mask before + * we reset it again. (may not even be + * necessary) + */ + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + lp->intmask = NORXflag | RECONflag; + lp->hw.intmask(dev, lp->intmask); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + + netif_carrier_off(dev); + netif_start_queue(dev); + mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000)); + + arcnet_led_event(dev, ARCNET_LED_EVENT_OPEN); + return 0; + + out_module_put: + module_put(lp->hw.owner); + return error; +} +EXPORT_SYMBOL(arcnet_open); + +/* The inverse routine to arcnet_open - shuts down the card. */ +int arcnet_close(struct net_device *dev) +{ + struct arcnet_local *lp = netdev_priv(dev); + + arcnet_led_event(dev, ARCNET_LED_EVENT_STOP); + timer_delete_sync(&lp->timer); + + netif_stop_queue(dev); + netif_carrier_off(dev); + + cancel_work_sync(&lp->reply_work); + + /* flush TX and disable RX */ + lp->hw.intmask(dev, 0); + lp->hw.command(dev, NOTXcmd); /* stop transmit */ + lp->hw.command(dev, NORXcmd); /* disable receive */ + mdelay(1); + + /* shut down the card */ + lp->hw.close(dev); + + /* reset counters */ + lp->reset_in_progress = 0; + + module_put(lp->hw.owner); + return 0; +} +EXPORT_SYMBOL(arcnet_close); + +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; + + arc_printk(D_DURING, dev, + "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) + arc_printk(D_NORMAL, dev, "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; + arc_printk(D_DEBUG, dev, "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) + arc_printk(D_NORMAL, dev, "arcnet_header: Yikes! diff (%u) is not 2!\n", + 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]; + arc_printk(D_DURING, dev, "building header for %02Xh using protocol '%c'\n", + proto_num, proto->suffix); + if (proto == &arc_proto_null && arc_bcast_proto != proto) { + arc_printk(D_DURING, dev, "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 retval; + + arc_printk(D_DURING, dev, + "transmit requested (status=%Xh, txbufs=%d/%d, len=%d, protocol %x)\n", + lp->hw.status(dev), 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]; + + arc_printk(D_SKB_SIZE, dev, "skb: transmitting %d bytes to %02X\n", + skb->len, pkt->hard.dest); + if (BUGLVL(D_SKB)) + arcnet_dump_skb(dev, skb, "tx"); + + /* fits in one packet? */ + if (skb->len - ARC_HDR_SIZE > XMTU && !proto->continue_tx) { + arc_printk(D_NORMAL, dev, "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); + lp->hw.intmask(dev, 0); + if (lp->next_tx == -1) + txbuf = get_arcbuf(dev); + else + txbuf = -1; + + if (txbuf != -1) { + lp->outgoing.skb = skb; + 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; + } else { + /* do it the 'split' way */ + lp->outgoing.proto = proto; + lp->outgoing.skb = skb; + lp->outgoing.pkt = pkt; + + if (proto->continue_tx && + proto->continue_tx(dev, txbuf)) { + arc_printk(D_NORMAL, dev, + "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; + } + + arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n", + __FILE__, __LINE__, __func__, lp->hw.status(dev)); + /* make sure we didn't ignore a TX IRQ while we were in here */ + lp->hw.intmask(dev, 0); + + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + lp->intmask |= TXFREEflag | EXCNAKflag; + lp->hw.intmask(dev, lp->intmask); + arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n", + __FILE__, __LINE__, __func__, lp->hw.status(dev)); + + arcnet_led_event(dev, ARCNET_LED_EVENT_TX); + + spin_unlock_irqrestore(&lp->lock, flags); + return retval; /* no need to try again */ +} +EXPORT_SYMBOL(arcnet_send_packet); + +/* 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); + + arc_printk(D_DURING, dev, "go_tx: status=%Xh, intmask=%Xh, next_tx=%d, cur_tx=%d\n", + lp->hw.status(dev), lp->intmask, lp->next_tx, lp->cur_tx); + + if (lp->cur_tx != -1 || lp->next_tx == -1) + return 0; + + if (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 */ + lp->hw.command(dev, 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 int txqueue) +{ + unsigned long flags; + struct arcnet_local *lp = netdev_priv(dev); + int status = lp->hw.status(dev); + 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; + lp->hw.command(dev, NOTXcmd | (lp->cur_tx << 3)); + } + dev->stats.tx_errors++; + + /* make sure we didn't miss a TX or a EXC NAK IRQ */ + lp->hw.intmask(dev, 0); + lp->intmask |= TXFREEflag | EXCNAKflag; + lp->hw.intmask(dev, lp->intmask); + + spin_unlock_irqrestore(&lp->lock, flags); + + if (time_after(jiffies, lp->last_timeout + 10 * HZ)) { + arc_printk(D_EXTRA, dev, "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); +} +EXPORT_SYMBOL(arcnet_timeout); + +/* 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; + unsigned long flags; + int retval = IRQ_NONE; + + arc_printk(D_DURING, dev, "\n"); + + arc_printk(D_DURING, dev, "in arcnet_interrupt\n"); + + lp = netdev_priv(dev); + BUG_ON(!lp); + + spin_lock_irqsave(&lp->lock, flags); + + if (lp->reset_in_progress) + goto out; + + /* RESET flag was enabled - if device is not running, we must + * clear it right away (but nothing else). + */ + if (!netif_running(dev)) { + if (lp->hw.status(dev) & RESETflag) + lp->hw.command(dev, CFLAGScmd | RESETclear); + lp->hw.intmask(dev, 0); + spin_unlock_irqrestore(&lp->lock, flags); + return retval; + } + + arc_printk(D_DURING, dev, "in arcnet_inthandler (status=%Xh, intmask=%Xh)\n", + lp->hw.status(dev), lp->intmask); + + boguscount = 5; + do { + status = lp->hw.status(dev); + diagstatus = (status >> 8) & 0xFF; + + arc_printk(D_DEBUG, dev, "%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) { + arc_printk(D_NORMAL, dev, "spurious reset (status=%Xh)\n", + status); + + lp->reset_in_progress = 1; + netif_stop_queue(dev); + netif_carrier_off(dev); + schedule_work(&lp->reset_work); + + /* get out of the interrupt handler! */ + goto out; + } + /* 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; + arc_printk(D_DURING, dev, "Buffer #%d: receive irq (status=%Xh)\n", + recbuf, status); + + lp->cur_rx = get_arcbuf(dev); + if (lp->cur_rx != -1) { + arc_printk(D_DURING, dev, "enabling receive to buffer #%d\n", + lp->cur_rx); + lp->hw.command(dev, RXcmd | (lp->cur_rx << 3) | RXbcasts); + } + didsomething++; + } + + if ((diagstatus & EXCNAKflag)) { + arc_printk(D_DURING, dev, "EXCNAK IRQ (diagstat=%Xh)\n", + diagstatus); + + lp->hw.command(dev, NOTXcmd); /* disable transmit */ + lp->excnak_pending = 1; + + lp->hw.command(dev, EXCNAKclear); + lp->intmask &= ~(EXCNAKflag); + didsomething++; + } + + /* a transmit finished, and we're interested in it. */ + if ((status & lp->intmask & TXFREEflag) || lp->timed_out) { + int ackstatus; + lp->intmask &= ~(TXFREEflag | EXCNAKflag); + + if (status & TXACKflag) + ackstatus = 2; + else if (lp->excnak_pending) + ackstatus = 1; + else + ackstatus = 0; + + arc_printk(D_DURING, dev, "TX IRQ (stat=%Xh)\n", + status); + + if (lp->cur_tx != -1 && !lp->timed_out) { + if (!(status & TXACKflag)) { + if (lp->lasttrans_dest != 0) { + arc_printk(D_EXTRA, dev, + "transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n", + status, + lp->lasttrans_dest); + dev->stats.tx_errors++; + dev->stats.tx_carrier_errors++; + } else { + arc_printk(D_DURING, dev, + "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) { + lp->outgoing.proto + ->ack_tx(dev, ackstatus); + } + lp->reply_status = ackstatus; + queue_work(system_bh_highpri_wq, &lp->reply_work); + } + 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) { + if (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) { + lp->hw.command(dev, CFLAGScmd | CONFIGclear); + dev->stats.tx_carrier_errors++; + + arc_printk(D_RECON, dev, "Network reconfiguration detected (status=%Xh)\n", + status); + if (netif_carrier_ok(dev)) { + netif_carrier_off(dev); + netdev_info(dev, "link down\n"); + } + mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000)); + + arcnet_led_event(dev, ARCNET_LED_EVENT_RECON); + /* MYRECON bit is at bit 7 of diagstatus */ + if (diagstatus & 0x80) + arc_printk(D_RECON, dev, "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) + arc_printk(D_NORMAL, dev, "reconfiguration detected: cabling restored?\n"); + lp->first_recon = lp->last_recon = jiffies; + lp->num_recons = lp->network_down = 0; + + arc_printk(D_DURING, dev, "recon: clearing counters.\n"); + } else { /* add to current RECON counter */ + lp->last_recon = jiffies; + lp->num_recons++; + + arc_printk(D_DURING, dev, "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; + arc_printk(D_NORMAL, dev, "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) + arc_printk(D_NORMAL, dev, "cabling restored?\n"); + lp->first_recon = lp->last_recon = 0; + lp->num_recons = lp->network_down = 0; + + arc_printk(D_DURING, dev, "not recon: clearing counters anyway.\n"); + netif_carrier_on(dev); + } + + if (didsomething) + retval |= IRQ_HANDLED; + } while (--boguscount && didsomething); + + arc_printk(D_DURING, dev, "arcnet_interrupt complete (status=%Xh, count=%d)\n", + lp->hw.status(dev), boguscount); + arc_printk(D_DURING, dev, "\n"); + + lp->hw.intmask(dev, 0); + udelay(1); + lp->hw.intmask(dev, lp->intmask); + +out: + spin_unlock_irqrestore(&lp->lock, flags); + return retval; +} +EXPORT_SYMBOL(arcnet_interrupt); + +/* 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); + union { + struct archdr pkt; + char buf[512]; + } rxdata; + struct arc_rfc1201 *soft; + int length, ofs; + + soft = &rxdata.pkt.soft.rfc1201; + + lp->hw.copy_from_card(dev, bufnum, 0, &rxdata.pkt, ARC_HDR_SIZE); + if (rxdata.pkt.hard.offset[0]) { + ofs = rxdata.pkt.hard.offset[0]; + length = 256 - ofs; + } else { + ofs = rxdata.pkt.hard.offset[1]; + length = 512 - ofs; + } + + /* get the full header, if possible */ + if (sizeof(rxdata.pkt.soft) <= length) { + lp->hw.copy_from_card(dev, bufnum, ofs, soft, sizeof(rxdata.pkt.soft)); + } else { + memset(&rxdata.pkt.soft, 0, sizeof(rxdata.pkt.soft)); + lp->hw.copy_from_card(dev, bufnum, ofs, soft, length); + } + + arc_printk(D_DURING, dev, "Buffer #%d: received packet from %02Xh to %02Xh (%d+4 bytes)\n", + bufnum, rxdata.pkt.hard.source, rxdata.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) { + if (BUGLVL(D_PROTO)) { + struct ArcProto + *oldp = arc_proto_map[lp->default_proto[rxdata.pkt.hard.source]], + *newp = arc_proto_map[soft->proto]; + + if (oldp != newp) { + arc_printk(D_PROTO, dev, + "got protocol %02Xh; encap for host %02Xh is now '%c' (was '%c')\n", + soft->proto, rxdata.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[rxdata.pkt.hard.source] = soft->proto; + } + /* call the protocol-specific receiver. */ + arc_proto_map[soft->proto]->rx(dev, bufnum, &rxdata.pkt, length); +} + +static void null_rx(struct net_device *dev, int bufnum, + struct archdr *pkthdr, int length) +{ + arc_printk(D_PROTO, dev, + "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); + + arc_printk(D_PROTO, dev, + "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; + + arc_printk(D_PROTO, dev, "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..7a0a79973 --- /dev/null +++ b/drivers/net/arcnet/capmode.c @@ -0,0 +1,269 @@ +/* + * 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 + * + * ********************** + */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + +#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 "arcdevice.h" + +/* 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; + char *pktbuf, *pkthdrbuf; + int ofs; + + arc_printk(D_DURING, dev, "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) { + 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)); + + if (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 = skb_push(skb, hdr_size); + + arc_printk(D_PROTO, dev, "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); + + arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n", + lp->next_tx, lp->cur_tx, bufnum); + + arc_printk(D_PROTO, dev, "Sending for cap packet %x.\n", + *((int *)&pkt->soft.cap.cookie[0])); + + if (length > XMTU) { + /* should never happen! other people already check for this. */ + arc_printk(D_NORMAL, dev, "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; + } + + arc_printk(D_DURING, dev, "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); + + arc_printk(D_DURING, dev, "capmode: ack_tx: protocol: %x: result: %d\n", + lp->outgoing.skb->protocol, acked); + + if (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) + 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; + + arc_printk(D_PROTO, dev, "Acknowledge for cap packet %x.\n", + *((int *)&ackpkt->soft.cap.cookie[0])); + + ackskb->protocol = cpu_to_be16(ETH_P_ARCNET); + + if (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 = { + .suffix = 'r', + .mtu = XMTU, + .rx = rx, + .build_header = build_header, + .prepare_tx = prepare_tx, + .ack_tx = ack_tx +}; + +static int __init capmode_module_init(void) +{ + int count; + + pr_info("cap mode (`c') encapsulation support loaded\n"); + + 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; + + 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_DESCRIPTION("ARCnet CAP mode packet interface module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c new file mode 100644 index 000000000..19cd36cba --- /dev/null +++ b/drivers/net/arcnet/com20020-pci.c @@ -0,0 +1,556 @@ +/* + * 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 + * + * ********************** + */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + +#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/list.h> +#include <linux/io.h> +#include <linux/leds.h> + +#include "arcdevice.h" +#include "com20020.h" + +/* 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_DESCRIPTION("ARCnet COM20020 chipset PCI driver"); +MODULE_LICENSE("GPL"); + +static void led_tx_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct com20020_dev *card; + struct com20020_priv *priv; + struct com20020_pci_card_info *ci; + + card = container_of(led_cdev, struct com20020_dev, tx_led); + + priv = card->pci_priv; + ci = priv->ci; + + outb(!!value, priv->misc + ci->leds[card->index].green); +} + +static void led_recon_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct com20020_dev *card; + struct com20020_priv *priv; + struct com20020_pci_card_info *ci; + + card = container_of(led_cdev, struct com20020_dev, recon_led); + + priv = card->pci_priv; + ci = priv->ci; + + outb(!!value, priv->misc + ci->leds[card->index].red); +} + +static ssize_t backplane_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct net_device *net_dev = to_net_dev(dev); + struct arcnet_local *lp = netdev_priv(net_dev); + + return sprintf(buf, "%s\n", lp->backplane ? "true" : "false"); +} +static DEVICE_ATTR_RO(backplane_mode); + +static struct attribute *com20020_state_attrs[] = { + &dev_attr_backplane_mode.attr, + NULL, +}; + +static const struct attribute_group com20020_state_group = { + .name = NULL, + .attrs = com20020_state_attrs, +}; + +static struct com20020_pci_card_info card_info_2p5mbit; + +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 com20020_pci_channel_map *mm; + struct net_device *dev; + struct arcnet_local *lp; + struct com20020_priv *priv; + int ioaddr, ret; + u8 i; + struct resource *r; + + ret = 0; + + 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; + if (!ci) + ci = &card_info_2p5mbit; + + priv->ci = ci; + mm = &ci->misc_map; + + pci_set_drvdata(pdev, priv); + + INIT_LIST_HEAD(&priv->list_dev); + + if (mm->size) { + ioaddr = pci_resource_start(pdev, mm->bar) + mm->offset; + r = devm_request_region(&pdev->dev, ioaddr, mm->size, + "com20020-pci"); + if (!r) { + pr_err("IO region %xh-%xh already allocated.\n", + ioaddr, ioaddr + mm->size - 1); + return -EBUSY; + } + priv->misc = ioaddr; + } + + for (i = 0; i < ci->devcount; i++) { + struct com20020_pci_channel_map *cm = &ci->chan_map_tbl[i]; + struct com20020_dev *card; + int dev_id_mask = 0xf; + + dev = alloc_arcdev(device); + if (!dev) { + ret = -ENOMEM; + break; + } + dev->dev_port = i; + + dev->netdev_ops = &com20020_netdev_ops; + + lp = netdev_priv(dev); + + arc_printk(D_NORMAL, dev, "%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 err_free_arcdev; + } + + /* Dummy access after Reset + * ARCNET controller needs + * this access to detect bustype + */ + outb(0x00, ioaddr + COM20020_REG_W_COMMAND); + inb(ioaddr + COM20020_REG_R_DIAGSTAT); + + SET_NETDEV_DEV(dev, &pdev->dev); + dev->base_addr = ioaddr; + arcnet_set_addr(dev, node); + dev->sysfs_groups[0] = &com20020_state_group; + 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; + + lp->backplane = (inb(priv->misc) >> (2 + i)) & 0x1; + + if (!strncmp(ci->name, "EAE PLX-PCI FB2", 15)) + lp->backplane = 1; + + if (ci->flags & ARC_HAS_ROTARY) { + /* Get the dev_id from the PLX rotary coder */ + if (!strncmp(ci->name, "EAE PLX-PCI MA1", 15)) + dev_id_mask = 0x3; + dev->dev_id = (inb(priv->misc + ci->rotary) >> 4) & dev_id_mask; + snprintf(dev->name, sizeof(dev->name), "arc%d-%d", dev->dev_id, i); + } + + if (inb(ioaddr + COM20020_REG_R_STATUS) == 0xFF) { + pr_err("IO address %Xh is empty!\n", ioaddr); + ret = -EIO; + goto err_free_arcdev; + } + if (com20020_check(dev)) { + ret = -EIO; + goto err_free_arcdev; + } + + ret = com20020_found(dev, IRQF_SHARED); + if (ret) + goto err_free_arcdev; + + card = devm_kzalloc(&pdev->dev, sizeof(struct com20020_dev), + GFP_KERNEL); + if (!card) { + ret = -ENOMEM; + goto err_free_arcdev; + } + + card->index = i; + card->pci_priv = priv; + + if (ci->flags & ARC_HAS_LED) { + card->tx_led.brightness_set = led_tx_set; + card->tx_led.default_trigger = devm_kasprintf(&pdev->dev, + GFP_KERNEL, "arc%d-%d-tx", + dev->dev_id, i); + if (!card->tx_led.default_trigger) { + ret = -ENOMEM; + goto err_free_arcdev; + } + card->tx_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "pci:green:tx:%d-%d", + dev->dev_id, i); + if (!card->tx_led.name) { + ret = -ENOMEM; + goto err_free_arcdev; + } + card->tx_led.dev = &dev->dev; + card->recon_led.brightness_set = led_recon_set; + card->recon_led.default_trigger = devm_kasprintf(&pdev->dev, + GFP_KERNEL, "arc%d-%d-recon", + dev->dev_id, i); + if (!card->recon_led.default_trigger) { + ret = -ENOMEM; + goto err_free_arcdev; + } + card->recon_led.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, + "pci:red:recon:%d-%d", + dev->dev_id, i); + if (!card->recon_led.name) { + ret = -ENOMEM; + goto err_free_arcdev; + } + card->recon_led.dev = &dev->dev; + + ret = devm_led_classdev_register(&pdev->dev, &card->tx_led); + if (ret) + goto err_free_arcdev; + + ret = devm_led_classdev_register(&pdev->dev, &card->recon_led); + if (ret) + goto err_free_arcdev; + + dev_set_drvdata(&dev->dev, card); + devm_arcnet_led_init(dev, dev->dev_id, i); + } + + card->dev = dev; + list_add(&card->list, &priv->list_dev); + continue; + +err_free_arcdev: + free_arcdev(dev); + break; + } + if (ret) + 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_arcdev(dev); + } +} + +static struct com20020_pci_card_info card_info_10mbit = { + .name = "ARC-PCI", + .devcount = 1, + .chan_map_tbl = { + { + .bar = 2, + .offset = 0x00, + .size = 0x08, + }, + }, + .flags = ARC_CAN_10MBIT, +}; + +static struct com20020_pci_card_info card_info_5mbit = { + .name = "ARC-PCI", + .devcount = 1, + .chan_map_tbl = { + { + .bar = 2, + .offset = 0x00, + .size = 0x08, + }, + }, + .flags = ARC_IS_5MBIT, +}; + +static struct com20020_pci_card_info card_info_2p5mbit = { + .name = "ARC-PCI", + .devcount = 1, + .chan_map_tbl = { + { + .bar = 2, + .offset = 0x00, + .size = 0x08, + }, + }, +}; + +static struct com20020_pci_card_info card_info_sohard = { + .name = "SOHARD SH ARC-PCI", + .devcount = 1, + /* SOHARD needs PCI base addr 4 */ + .chan_map_tbl = { + { + .bar = 4, + .offset = 0x00, + .size = 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 = { + { + .bar = 2, + .offset = 0x00, + .size = 0x08, + }, + }, + .misc_map = { + .bar = 2, + .offset = 0x10, + .size = 0x04, + }, + .leds = { + { + .green = 0x0, + .red = 0x1, + }, + }, + .rotary = 0x0, + .flags = ARC_HAS_ROTARY | ARC_HAS_LED | ARC_CAN_10MBIT, +}; + +static struct com20020_pci_card_info card_info_eae_ma1 = { + .name = "EAE PLX-PCI MA1", + .devcount = 2, + .chan_map_tbl = { + { + .bar = 2, + .offset = 0x00, + .size = 0x08, + }, { + .bar = 2, + .offset = 0x08, + .size = 0x08, + } + }, + .misc_map = { + .bar = 2, + .offset = 0x10, + .size = 0x04, + }, + .leds = { + { + .green = 0x0, + .red = 0x1, + }, { + .green = 0x2, + .red = 0x3, + }, + }, + .rotary = 0x0, + .flags = ARC_HAS_ROTARY | ARC_HAS_LED | ARC_CAN_10MBIT, +}; + +static struct com20020_pci_card_info card_info_eae_fb2 = { + .name = "EAE PLX-PCI FB2", + .devcount = 1, + .chan_map_tbl = { + { + .bar = 2, + .offset = 0x00, + .size = 0x08, + }, + }, + .misc_map = { + .bar = 2, + .offset = 0x10, + .size = 0x04, + }, + .leds = { + { + .green = 0x0, + .red = 0x1, + }, + }, + .rotary = 0x0, + .flags = ARC_HAS_ROTARY | ARC_HAS_LED | ARC_CAN_10MBIT, +}; + +static const struct pci_device_id com20020pci_id_table[] = { + { + PCI_DEVICE(0x1571, 0xa001), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa002), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa003), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa004), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa005), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa006), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa007), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa008), + .driver_data = 0, + }, { + PCI_DEVICE(0x1571, 0xa009), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa00a), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa00b), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa00c), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa00d), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa00e), + .driver_data = (kernel_ulong_t)&card_info_5mbit, + }, { + PCI_DEVICE(0x1571, 0xa201), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x1571, 0xa202), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x1571, 0xa203), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x1571, 0xa204), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x1571, 0xa205), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x1571, 0xa206), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE_SUB(0x10B5, 0x9030, 0x10B5, 0x2978), + .driver_data = (kernel_ulong_t)&card_info_sohard, + }, { + PCI_DEVICE_SUB(0x10B5, 0x9050, 0x10B5, 0x2273), + .driver_data = (kernel_ulong_t)&card_info_sohard, + }, { + PCI_DEVICE_SUB(0x10B5, 0x9050, 0x10B5, 0x3263), + .driver_data = (kernel_ulong_t)&card_info_eae_arc1, + }, { + PCI_DEVICE_SUB(0x10B5, 0x9050, 0x10B5, 0x3292), + .driver_data = (kernel_ulong_t)&card_info_eae_ma1, + }, { + PCI_DEVICE_SUB(0x10B5, 0x9050, 0x10B5, 0x3294), + .driver_data = (kernel_ulong_t)&card_info_eae_fb2, + }, { + PCI_DEVICE(0x14BA, 0x6000), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, { + PCI_DEVICE(0x10B5, 0x2200), + .driver_data = (kernel_ulong_t)&card_info_10mbit, + }, + { } +}; + +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, +}; + +module_pci_driver(com20020pci_driver); diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c new file mode 100644 index 000000000..21f962357 --- /dev/null +++ b/drivers/net/arcnet/com20020.c @@ -0,0 +1,394 @@ +/* + * 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 + * + * ********************** + */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + +#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/io.h> + +#include "arcdevice.h" +#include "com20020.h" + +static const char * const clockrates[] = { + "XXXXXXX", "XXXXXXXX", "XXXXXX", "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_rx_mode(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, + ioaddr + COM20020_REG_W_ADDR_HI); + outb(ofs & 0xff, ioaddr + COM20020_REG_W_ADDR_LO); + + /* copy the data */ + TIME(dev, "insb", count, + insb(ioaddr + COM20020_REG_RW_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, ioaddr + COM20020_REG_W_ADDR_HI); + outb(ofs & 0xff, ioaddr + COM20020_REG_W_ADDR_LO); + + /* copy the data */ + TIME(dev, "outsb", count, + outsb(ioaddr + COM20020_REG_RW_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); + + outb(XTOcfg(3) | RESETcfg, ioaddr + COM20020_REG_W_CONFIG); + udelay(5); + outb(XTOcfg(3), ioaddr + COM20020_REG_W_CONFIG); + 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; + + com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); + outb(lp->setup, ioaddr + COM20020_REG_W_XREG); + + if (lp->clockm != 0) { + com20020_set_subaddress(lp, ioaddr, SUB_SETUP2); + outb(lp->setup2, ioaddr + COM20020_REG_W_XREG); + + /* must now write the magic "restart operation" command */ + mdelay(1); + outb(STARTIOcmd, ioaddr + COM20020_REG_W_COMMAND); + } + + lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; + /* set node ID to 0x42 (but transmitter is disabled, so it's okay) */ + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); + outb(0x42, ioaddr + COM20020_REG_W_XREG); + + status = inb(ioaddr + COM20020_REG_R_STATUS); + + if ((status & 0x99) != (NORXflag | TXFREEflag | RESETflag)) { + arc_printk(D_NORMAL, dev, "status invalid (%Xh).\n", status); + return -ENODEV; + } + arc_printk(D_INIT_REASONS, dev, "status after reset: %X\n", status); + + outb(CFLAGScmd | RESETclear | CONFIGclear, + ioaddr + COM20020_REG_W_COMMAND); + status = inb(ioaddr + COM20020_REG_R_STATUS); + arc_printk(D_INIT_REASONS, dev, "status after reset acknowledged: %X\n", + status); + + /* Read first location of memory */ + outb(0 | RDDATAflag | AUTOINCflag, ioaddr + COM20020_REG_W_ADDR_HI); + outb(0, ioaddr + COM20020_REG_W_ADDR_LO); + + status = inb(ioaddr + COM20020_REG_RW_MEMDATA); + if (status != TESTvalue) { + arc_printk(D_NORMAL, dev, "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; + + dev_addr_set(dev, hwaddr->sa_data); + com20020_set_subaddress(lp, ioaddr, SUB_NODE); + outb(dev->dev_addr[0], ioaddr + COM20020_REG_W_XREG); + + return 0; +} + +static int com20020_netdev_open(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + struct arcnet_local *lp = netdev_priv(dev); + + lp->config |= TXENcfg; + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); + + return arcnet_open(dev); +} + +static int com20020_netdev_close(struct net_device *dev) +{ + int ioaddr = dev->base_addr; + struct arcnet_local *lp = netdev_priv(dev); + + arcnet_close(dev); + + /* disable transmitter */ + lp->config &= ~TXENcfg; + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); + return 0; +} + +const struct net_device_ops com20020_netdev_ops = { + .ndo_open = com20020_netdev_open, + .ndo_stop = com20020_netdev_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_rx_mode, +}; + +/* 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; + + /* FIXME: do this some other way! */ + if (!dev->dev_addr[0]) + arcnet_set_addr(dev, inb(ioaddr + 8)); + + com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); + outb(lp->setup, ioaddr + COM20020_REG_W_XREG); + + if (lp->card_flags & ARC_CAN_10MBIT) { + com20020_set_subaddress(lp, ioaddr, SUB_SETUP2); + outb(lp->setup2, ioaddr + COM20020_REG_W_XREG); + + /* must now write the magic "restart operation" command */ + mdelay(1); + outb(STARTIOcmd, ioaddr + COM20020_REG_W_COMMAND); + } + + lp->config = (lp->timeout << 3) | (lp->backplane << 2) | SUB_NODE; + /* Default 0x38 + register: Node ID */ + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); + outb(dev->dev_addr[0], ioaddr + COM20020_REG_W_XREG); + + /* reserve the irq */ + if (request_irq(dev->irq, arcnet_interrupt, shared, + "arcnet (COM20020)", dev)) { + arc_printk(D_NORMAL, dev, "Can't get IRQ %d!\n", dev->irq); + return -ENODEV; + } + + arc_printk(D_NORMAL, dev, "%s: station %02Xh found at %03lXh, IRQ %d.\n", + lp->card_name, dev->dev_addr[0], dev->base_addr, dev->irq); + + if (lp->backplane) + arc_printk(D_NORMAL, dev, "Using backplane mode.\n"); + + if (lp->timeout != 3) + arc_printk(D_NORMAL, dev, "Using extended timeout value of %d\n", + lp->timeout); + + arc_printk(D_NORMAL, dev, "Using CKP %d - data rate %s\n", + lp->setup >> 1, + clockrates[3 - + ((lp->setup2 & 0xF0) >> 4) + + ((lp->setup & 0x0F) >> 1)]); + /* The clockrates array index looks very fragile. + * It seems like it could have negative indexing. + */ + + 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; + + arc_printk(D_DEBUG, dev, "%s: %d: %s: dev: %p, lp: %p, dev->name: %s\n", + __FILE__, __LINE__, __func__, dev, lp, dev->name); + arc_printk(D_INIT, dev, "Resetting %s (status=%02Xh)\n", + dev->name, inb(ioaddr + COM20020_REG_R_STATUS)); + + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + lp->config |= (lp->timeout << 3) | (lp->backplane << 2); + /* power-up defaults */ + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + + if (really_reset) { + /* reset the card */ + outb(lp->config | RESETcfg, ioaddr + COM20020_REG_W_CONFIG); + udelay(5); + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); + mdelay(RESETtime * 2); + /* COM20020 seems to be slower sometimes */ + } + /* clear flags & end reset */ + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + outb(CFLAGScmd | RESETclear | CONFIGclear, + ioaddr + COM20020_REG_W_COMMAND); + + /* verify that the ARCnet signature byte is present */ + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + + com20020_copy_from_card(dev, 0, 0, &inbyte, 1); + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__); + if (inbyte != TESTvalue) { + arc_printk(D_DEBUG, dev, "%s: %d: %s\n", + __FILE__, __LINE__, __func__); + arc_printk(D_NORMAL, dev, "reset failed: TESTvalue not present.\n"); + return 1; + } + /* enable extended (512-byte) packets */ + outb(CONFIGcmd | EXTconf, ioaddr + COM20020_REG_W_COMMAND); + + arc_printk(D_DEBUG, dev, "%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; + + arc_printk(D_DURING, dev, "Setting mask to %x at %x\n", mask, ioaddr); + outb(mask, ioaddr + COM20020_REG_W_INTMASK); +} + +static void com20020_command(struct net_device *dev, int cmd) +{ + u_int ioaddr = dev->base_addr; + + outb(cmd, ioaddr + COM20020_REG_W_COMMAND); +} + +static int com20020_status(struct net_device *dev) +{ + u_int ioaddr = dev->base_addr; + + return inb(ioaddr + COM20020_REG_R_STATUS) + + (inb(ioaddr + COM20020_REG_R_DIAGSTAT) << 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; + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); +} + +/* ARCnet does not support multicast, only unicast and broadcast */ +static void com20020_set_rx_mode(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)) + arc_printk(D_NORMAL, dev, "Setting promiscuous flag...\n"); + com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); + lp->setup |= PROMISCset; + outb(lp->setup, ioaddr + COM20020_REG_W_XREG); + } else { + /* Disable promiscuous mode, use normal mode */ + if ((lp->setup & PROMISCset)) + arc_printk(D_NORMAL, dev, "Resetting promiscuous flag...\n"); + com20020_set_subaddress(lp, ioaddr, SUB_SETUP1); + lp->setup &= ~PROMISCset; + outb(lp->setup, ioaddr + COM20020_REG_W_XREG); + } +} + +#ifdef CONFIG_ARCNET_COM20020_PCI_MODULE +EXPORT_SYMBOL(com20020_check); +EXPORT_SYMBOL(com20020_found); +EXPORT_SYMBOL(com20020_netdev_ops); +#endif + +MODULE_DESCRIPTION("ARCnet COM20020 chipset core driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/arcnet/com20020.h b/drivers/net/arcnet/com20020.h new file mode 100644 index 000000000..68f49fdc2 --- /dev/null +++ b/drivers/net/arcnet/com20020.h @@ -0,0 +1,132 @@ +/* + * Linux ARCnet driver - COM20020 chipset support - function declarations + * + * Written 1997 by David Woodhouse. + * 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 + * + * ********************** + */ +#ifndef __COM20020_H +#define __COM20020_H +#include <linux/leds.h> + +int com20020_check(struct net_device *dev); +int com20020_found(struct net_device *dev, int shared); +extern const struct net_device_ops com20020_netdev_ops; + +/* The number of low I/O ports used by the card. */ +#define ARCNET_TOTAL_SIZE 8 + +#define PLX_PCI_MAX_CARDS 2 + +struct ledoffsets { + int green; + int red; +}; + +struct com20020_pci_channel_map { + u32 bar; + u32 offset; + u32 size; /* 0x00 - auto, e.g. length of entire bar */ +}; + +struct com20020_pci_card_info { + const char *name; + u8 devcount; + + struct com20020_pci_channel_map chan_map_tbl[PLX_PCI_MAX_CARDS]; + struct com20020_pci_channel_map misc_map; + + struct ledoffsets leds[PLX_PCI_MAX_CARDS]; + int rotary; + + unsigned int flags; +}; + +struct com20020_priv { + struct com20020_pci_card_info *ci; + struct list_head list_dev; + resource_size_t misc; +}; + +struct com20020_dev { + struct list_head list; + struct net_device *dev; + + struct led_classdev tx_led; + struct led_classdev recon_led; + + struct com20020_priv *pci_priv; + int index; +}; + +#define COM20020_REG_W_INTMASK 0 /* writable */ +#define COM20020_REG_R_STATUS 0 /* readable */ +#define COM20020_REG_W_COMMAND 1 /* standard arcnet commands */ +#define COM20020_REG_R_DIAGSTAT 1 /* diagnostic status */ +#define COM20020_REG_W_ADDR_HI 2 /* control for IO-mapped memory */ +#define COM20020_REG_W_ADDR_LO 3 +#define COM20020_REG_RW_MEMDATA 4 /* data port for IO-mapped memory */ +#define COM20020_REG_W_SUBADR 5 /* the extended port _XREG refers to */ +#define COM20020_REG_W_CONFIG 6 /* configuration */ +#define COM20020_REG_W_XREG 7 /* extra + * (indexed by _CONFIG or _SUBADDR) + */ + +/* in the ADDR_HI register */ +#define RDDATAflag 0x80 /* next access is a read (not a write) */ + +/* in the DIAGSTAT register */ +#define NEWNXTIDflag 0x02 /* ID to which token is passed has changed */ + +/* in the CONFIG register */ +#define RESETcfg 0x80 /* put card in reset state */ +#define TXENcfg 0x20 /* enable TX */ +#define XTOcfg(x) ((x) << 3) /* extended timeout */ + +/* in SETUP register */ +#define PROMISCset 0x10 /* enable RCV_ALL */ +#define P1MODE 0x80 /* enable P1-MODE for Backplane */ +#define SLOWARB 0x01 /* enable Slow Arbitration for >=5Mbps */ + +/* COM2002x */ +#define SUB_TENTATIVE 0 /* tentative node ID */ +#define SUB_NODE 1 /* node ID */ +#define SUB_SETUP1 2 /* various options */ +#define SUB_TEST 3 /* test/diag register */ + +/* COM20022 only */ +#define SUB_SETUP2 4 /* sundry options */ +#define SUB_BUSCTL 5 /* bus control options */ +#define SUB_DMACOUNT 6 /* DMA count options */ + +static inline void com20020_set_subaddress(struct arcnet_local *lp, + int ioaddr, int val) +{ + if (val < 4) { + lp->config = (lp->config & ~0x03) | val; + outb(lp->config, ioaddr + COM20020_REG_W_CONFIG); + } else { + outb(val, ioaddr + COM20020_REG_W_SUBADR); + } +} + +#endif /* __COM20020_H */ diff --git a/drivers/net/arcnet/com9026.h b/drivers/net/arcnet/com9026.h new file mode 100644 index 000000000..6adbc1871 --- /dev/null +++ b/drivers/net/arcnet/com9026.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __COM9026_H +#define __COM9026_H + +/* COM 9026 controller chip --> ARCnet register addresses */ + +#define COM9026_REG_W_INTMASK 0 /* writable */ +#define COM9026_REG_R_STATUS 0 /* readable */ +#define COM9026_REG_W_COMMAND 1 /* writable, returns random vals on read (?) */ +#define COM9026_REG_RW_CONFIG 2 /* Configuration register */ +#define COM9026_REG_R_RESET 8 /* software reset (on read) */ +#define COM9026_REG_RW_MEMDATA 12 /* Data port for IO-mapped memory */ +#define COM9026_REG_W_ADDR_LO 14 /* Control registers for said */ +#define COM9026_REG_W_ADDR_HI 15 + +#define COM9026_REG_R_STATION 1 /* Station ID */ + +#endif diff --git a/drivers/net/arcnet/rfc1051.c b/drivers/net/arcnet/rfc1051.c new file mode 100644 index 000000000..46519ca63 --- /dev/null +++ b/drivers/net/arcnet/rfc1051.c @@ -0,0 +1,243 @@ +/* + * 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 + * + * ********************** + */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + +#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 "arcdevice.h" + +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) +{ + pr_info("%s\n", "RFC1051 \"simple standard\" (`s') encapsulation support loaded"); + + 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_DESCRIPTION("ARCNet packet format (RFC 1051) module"); +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; + + arc_printk(D_DURING, dev, "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) { + 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)); + + if (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 = 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: + arc_printk(D_NORMAL, dev, "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; + + arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n", + lp->next_tx, lp->cur_tx, bufnum); + + /* hard header is not included in packet length */ + length -= ARC_HDR_SIZE; + + if (length > XMTU) { + /* should never happen! other people already check for this. */ + arc_printk(D_NORMAL, dev, "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..f2ec58d06 --- /dev/null +++ b/drivers/net/arcnet/rfc1201.c @@ -0,0 +1,548 @@ +/* + * 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 + * + * ********************** + */ + +#define pr_fmt(fmt) "arcnet:" KBUILD_MODNAME ": " fmt + +#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 "arcdevice.h" + +MODULE_DESCRIPTION("ARCNet packet format (RFC 1201) module"); +MODULE_LICENSE("GPL"); + +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) +{ + pr_info("%s\n", "RFC1201 \"standard\" (`a') encapsulation support loaded"); + + 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]; + + arc_printk(D_DURING, dev, "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) { + arc_printk(D_DURING, dev, "compensating for exception packet\n"); + } else { + arc_printk(D_EXTRA, dev, "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 */ + arc_printk(D_RX, dev, "incoming is not split (splitflag=%d)\n", + soft->split_flag); + + if (in->skb) { /* already assembling one! */ + arc_printk(D_EXTRA, dev, "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) { + 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? */ + arc_printk(D_EXTRA, dev, + "ARP source address was 00h, set to %02Xh\n", + saddr); + dev->stats.rx_crc_errors++; + *cptr = saddr; + } else { + arc_printk(D_DURING, dev, "ARP source address (%Xh) is fine.\n", + *cptr); + } + } else { + arc_printk(D_NORMAL, dev, "funny-shaped ARP packet. (%Xh, %Xh)\n", + arp->ar_hln, arp->ar_pln); + dev->stats.rx_errors++; + dev->stats.rx_crc_errors++; + } + } + if (BUGLVL(D_SKB)) + arcnet_dump_skb(dev, skb, "rx"); + + skb->protocol = type_trans(skb, dev); + netif_rx(skb); + } else { /* split packet */ + /* NOTE: MS-DOS 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. + */ + + arc_printk(D_RX, dev, "packet is split (splitflag=%d, seq=%d)\n", + soft->split_flag, in->sequence); + + if (in->skb && in->sequence != soft->sequence) { + arc_printk(D_EXTRA, dev, "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 */ + arc_printk(D_RX, dev, "brand new splitpacket (splitflag=%d)\n", + soft->split_flag); + if (in->skb) { /* already assembling one! */ + arc_printk(D_EXTRA, dev, "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) { + arc_printk(D_EXTRA, dev, "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) { + arc_printk(D_NORMAL, dev, "(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) { + arc_printk(D_EXTRA, dev, "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 not the right flag */ + if (packetnum != in->lastpacket) { + /* harmless duplicate? ignore. */ + if (packetnum <= in->lastpacket - 1) { + arc_printk(D_EXTRA, dev, "duplicate splitpacket ignored! (splitflag=%d)\n", + soft->split_flag); + dev->stats.rx_errors++; + dev->stats.rx_frame_errors++; + return; + } + /* "bad" duplicate, kill reassembly */ + arc_printk(D_EXTRA, dev, "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; + + arc_printk(D_SKB_SIZE, dev, "skb: received %d bytes from %02X (unsplit)\n", + skb->len, pkt->hard.source); + arc_printk(D_SKB_SIZE, dev, "skb: received %d bytes from %02X (split)\n", + skb->len, pkt->hard.source); + if (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 = 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: + arc_printk(D_NORMAL, dev, "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; + + arc_printk(D_DURING, dev, "prepare_tx: txbufs=%d/%d/%d\n", + lp->next_tx, lp->cur_tx, bufnum); + + /* hard header is not included in packet length */ + length -= ARC_HDR_SIZE; + 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; + + arc_printk(D_DURING, dev, "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; + + arc_printk(D_DURING, dev, + "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; +} |
