162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Linux ARCnet driver - device-independent routines
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Written 1997 by David Woodhouse.
562306a36Sopenharmony_ci * Written 1994-1999 by Avery Pennarun.
662306a36Sopenharmony_ci * Written 1999-2000 by Martin Mares <mj@ucw.cz>.
762306a36Sopenharmony_ci * Derived from skeleton.c by Donald Becker.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
1062306a36Sopenharmony_ci *  for sponsoring the further development of this driver.
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * **********************
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * The original copyright was as follows:
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * skeleton.c Written 1993 by Donald Becker.
1762306a36Sopenharmony_ci * Copyright 1993 United States Government as represented by the
1862306a36Sopenharmony_ci * Director, National Security Agency.  This software may only be used
1962306a36Sopenharmony_ci * and distributed according to the terms of the GNU General Public License as
2062306a36Sopenharmony_ci * modified by SRC, incorporated herein by reference.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * **********************
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * The change log is now in a file called ChangeLog in this directory.
2562306a36Sopenharmony_ci *
2662306a36Sopenharmony_ci * Sources:
2762306a36Sopenharmony_ci *  - Crynwr arcnet.com/arcether.com packet drivers.
2862306a36Sopenharmony_ci *  - arcnet.c v0.00 dated 1/1/94 and apparently by
2962306a36Sopenharmony_ci *     Donald Becker - it didn't work :)
3062306a36Sopenharmony_ci *  - skeleton.c v0.05 dated 11/16/93 by Donald Becker
3162306a36Sopenharmony_ci *     (from Linux Kernel 1.1.45)
3262306a36Sopenharmony_ci *  - RFC's 1201 and 1051 - re: TCP/IP over ARCnet
3362306a36Sopenharmony_ci *  - The official ARCnet COM9026 data sheets (!) thanks to
3462306a36Sopenharmony_ci *     Ken Cornetet <kcornete@nyx10.cs.du.edu>
3562306a36Sopenharmony_ci *  - The official ARCnet COM20020 data sheets.
3662306a36Sopenharmony_ci *  - Information on some more obscure ARCnet controller chips, thanks
3762306a36Sopenharmony_ci *     to the nice people at SMSC.
3862306a36Sopenharmony_ci *  - net/inet/eth.c (from kernel 1.1.50) for header-building info.
3962306a36Sopenharmony_ci *  - Alternate Linux ARCnet source by V.Shergin <vsher@sao.stavropol.su>
4062306a36Sopenharmony_ci *  - Textual information and more alternate source from Joachim Koenig
4162306a36Sopenharmony_ci *     <jojo@repas.de>
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include <linux/module.h>
4762306a36Sopenharmony_ci#include <linux/types.h>
4862306a36Sopenharmony_ci#include <linux/delay.h>
4962306a36Sopenharmony_ci#include <linux/netdevice.h>
5062306a36Sopenharmony_ci#include <linux/if_arp.h>
5162306a36Sopenharmony_ci#include <net/arp.h>
5262306a36Sopenharmony_ci#include <linux/init.h>
5362306a36Sopenharmony_ci#include <linux/jiffies.h>
5462306a36Sopenharmony_ci#include <linux/errqueue.h>
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#include <linux/leds.h>
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci#include "arcdevice.h"
5962306a36Sopenharmony_ci#include "com9026.h"
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* "do nothing" functions for protocol drivers */
6262306a36Sopenharmony_cistatic void null_rx(struct net_device *dev, int bufnum,
6362306a36Sopenharmony_ci		    struct archdr *pkthdr, int length);
6462306a36Sopenharmony_cistatic int null_build_header(struct sk_buff *skb, struct net_device *dev,
6562306a36Sopenharmony_ci			     unsigned short type, uint8_t daddr);
6662306a36Sopenharmony_cistatic int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
6762306a36Sopenharmony_ci			   int length, int bufnum);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic void arcnet_rx(struct net_device *dev, int bufnum);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* one ArcProto per possible proto ID.  None of the elements of
7262306a36Sopenharmony_ci * arc_proto_map are allowed to be NULL; they will get set to
7362306a36Sopenharmony_ci * arc_proto_default instead.  It also must not be NULL; if you would like
7462306a36Sopenharmony_ci * to set it to NULL, set it to &arc_proto_null instead.
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_cistruct ArcProto *arc_proto_map[256];
7762306a36Sopenharmony_ciEXPORT_SYMBOL(arc_proto_map);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistruct ArcProto *arc_proto_default;
8062306a36Sopenharmony_ciEXPORT_SYMBOL(arc_proto_default);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistruct ArcProto *arc_bcast_proto;
8362306a36Sopenharmony_ciEXPORT_SYMBOL(arc_bcast_proto);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistruct ArcProto *arc_raw_proto;
8662306a36Sopenharmony_ciEXPORT_SYMBOL(arc_raw_proto);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic struct ArcProto arc_proto_null = {
8962306a36Sopenharmony_ci	.suffix		= '?',
9062306a36Sopenharmony_ci	.mtu		= XMTU,
9162306a36Sopenharmony_ci	.is_ip          = 0,
9262306a36Sopenharmony_ci	.rx		= null_rx,
9362306a36Sopenharmony_ci	.build_header	= null_build_header,
9462306a36Sopenharmony_ci	.prepare_tx	= null_prepare_tx,
9562306a36Sopenharmony_ci	.continue_tx    = NULL,
9662306a36Sopenharmony_ci	.ack_tx         = NULL
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/* Exported function prototypes */
10062306a36Sopenharmony_ciint arcnet_debug = ARCNET_DEBUG;
10162306a36Sopenharmony_ciEXPORT_SYMBOL(arcnet_debug);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci/* Internal function prototypes */
10462306a36Sopenharmony_cistatic int arcnet_header(struct sk_buff *skb, struct net_device *dev,
10562306a36Sopenharmony_ci			 unsigned short type, const void *daddr,
10662306a36Sopenharmony_ci			 const void *saddr, unsigned len);
10762306a36Sopenharmony_cistatic int go_tx(struct net_device *dev);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int debug = ARCNET_DEBUG;
11062306a36Sopenharmony_cimodule_param(debug, int, 0);
11162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int __init arcnet_init(void)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	int count;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	arcnet_debug = debug;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	pr_info("arcnet loaded\n");
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* initialize the protocol map */
12262306a36Sopenharmony_ci	arc_raw_proto = arc_proto_default = arc_bcast_proto = &arc_proto_null;
12362306a36Sopenharmony_ci	for (count = 0; count < 256; count++)
12462306a36Sopenharmony_ci		arc_proto_map[count] = arc_proto_default;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (BUGLVL(D_DURING))
12762306a36Sopenharmony_ci		pr_info("struct sizes: %zd %zd %zd %zd %zd\n",
12862306a36Sopenharmony_ci			sizeof(struct arc_hardware),
12962306a36Sopenharmony_ci			sizeof(struct arc_rfc1201),
13062306a36Sopenharmony_ci			sizeof(struct arc_rfc1051),
13162306a36Sopenharmony_ci			sizeof(struct arc_eth_encap),
13262306a36Sopenharmony_ci			sizeof(struct archdr));
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic void __exit arcnet_exit(void)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cimodule_init(arcnet_init);
14262306a36Sopenharmony_cimodule_exit(arcnet_exit);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/* Dump the contents of an sk_buff */
14562306a36Sopenharmony_ci#if ARCNET_DEBUG_MAX & D_SKB
14662306a36Sopenharmony_civoid arcnet_dump_skb(struct net_device *dev,
14762306a36Sopenharmony_ci		     struct sk_buff *skb, char *desc)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	char hdr[32];
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* dump the packet */
15262306a36Sopenharmony_ci	snprintf(hdr, sizeof(hdr), "%6s:%s skb->data:", dev->name, desc);
15362306a36Sopenharmony_ci	print_hex_dump(KERN_DEBUG, hdr, DUMP_PREFIX_OFFSET,
15462306a36Sopenharmony_ci		       16, 1, skb->data, skb->len, true);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ciEXPORT_SYMBOL(arcnet_dump_skb);
15762306a36Sopenharmony_ci#endif
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/* Dump the contents of an ARCnet buffer */
16062306a36Sopenharmony_ci#if (ARCNET_DEBUG_MAX & (D_RX | D_TX))
16162306a36Sopenharmony_cistatic void arcnet_dump_packet(struct net_device *dev, int bufnum,
16262306a36Sopenharmony_ci			       char *desc, int take_arcnet_lock)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
16562306a36Sopenharmony_ci	int i, length;
16662306a36Sopenharmony_ci	unsigned long flags = 0;
16762306a36Sopenharmony_ci	static uint8_t buf[512];
16862306a36Sopenharmony_ci	char hdr[32];
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/* hw.copy_from_card expects IRQ context so take the IRQ lock
17162306a36Sopenharmony_ci	 * to keep it single threaded
17262306a36Sopenharmony_ci	 */
17362306a36Sopenharmony_ci	if (take_arcnet_lock)
17462306a36Sopenharmony_ci		spin_lock_irqsave(&lp->lock, flags);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	lp->hw.copy_from_card(dev, bufnum, 0, buf, 512);
17762306a36Sopenharmony_ci	if (take_arcnet_lock)
17862306a36Sopenharmony_ci		spin_unlock_irqrestore(&lp->lock, flags);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* if the offset[0] byte is nonzero, this is a 256-byte packet */
18162306a36Sopenharmony_ci	length = (buf[2] ? 256 : 512);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* dump the packet */
18462306a36Sopenharmony_ci	snprintf(hdr, sizeof(hdr), "%6s:%s packet dump:", dev->name, desc);
18562306a36Sopenharmony_ci	print_hex_dump(KERN_DEBUG, hdr, DUMP_PREFIX_OFFSET,
18662306a36Sopenharmony_ci		       16, 1, buf, length, true);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci#else
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci#define arcnet_dump_packet(dev, bufnum, desc, take_arcnet_lock) do { } while (0)
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci#endif
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci/* Trigger a LED event in response to a ARCNET device event */
19662306a36Sopenharmony_civoid arcnet_led_event(struct net_device *dev, enum arcnet_led_event event)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	switch (event) {
20162306a36Sopenharmony_ci	case ARCNET_LED_EVENT_RECON:
20262306a36Sopenharmony_ci		led_trigger_blink_oneshot(lp->recon_led_trig, 350, 350, 0);
20362306a36Sopenharmony_ci		break;
20462306a36Sopenharmony_ci	case ARCNET_LED_EVENT_OPEN:
20562306a36Sopenharmony_ci		led_trigger_event(lp->tx_led_trig, LED_OFF);
20662306a36Sopenharmony_ci		led_trigger_event(lp->recon_led_trig, LED_OFF);
20762306a36Sopenharmony_ci		break;
20862306a36Sopenharmony_ci	case ARCNET_LED_EVENT_STOP:
20962306a36Sopenharmony_ci		led_trigger_event(lp->tx_led_trig, LED_OFF);
21062306a36Sopenharmony_ci		led_trigger_event(lp->recon_led_trig, LED_OFF);
21162306a36Sopenharmony_ci		break;
21262306a36Sopenharmony_ci	case ARCNET_LED_EVENT_TX:
21362306a36Sopenharmony_ci		led_trigger_blink_oneshot(lp->tx_led_trig, 50, 50, 0);
21462306a36Sopenharmony_ci		break;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(arcnet_led_event);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic void arcnet_led_release(struct device *gendev, void *res)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(to_net_dev(gendev));
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	led_trigger_unregister_simple(lp->tx_led_trig);
22462306a36Sopenharmony_ci	led_trigger_unregister_simple(lp->recon_led_trig);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/* Register ARCNET LED triggers for a arcnet device
22862306a36Sopenharmony_ci *
22962306a36Sopenharmony_ci * This is normally called from a driver's probe function
23062306a36Sopenharmony_ci */
23162306a36Sopenharmony_civoid devm_arcnet_led_init(struct net_device *netdev, int index, int subid)
23262306a36Sopenharmony_ci{
23362306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(netdev);
23462306a36Sopenharmony_ci	void *res;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	res = devres_alloc(arcnet_led_release, 0, GFP_KERNEL);
23762306a36Sopenharmony_ci	if (!res) {
23862306a36Sopenharmony_ci		netdev_err(netdev, "cannot register LED triggers\n");
23962306a36Sopenharmony_ci		return;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	snprintf(lp->tx_led_trig_name, sizeof(lp->tx_led_trig_name),
24362306a36Sopenharmony_ci		 "arc%d-%d-tx", index, subid);
24462306a36Sopenharmony_ci	snprintf(lp->recon_led_trig_name, sizeof(lp->recon_led_trig_name),
24562306a36Sopenharmony_ci		 "arc%d-%d-recon", index, subid);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	led_trigger_register_simple(lp->tx_led_trig_name,
24862306a36Sopenharmony_ci				    &lp->tx_led_trig);
24962306a36Sopenharmony_ci	led_trigger_register_simple(lp->recon_led_trig_name,
25062306a36Sopenharmony_ci				    &lp->recon_led_trig);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	devres_add(&netdev->dev, res);
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_arcnet_led_init);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci/* Unregister a protocol driver from the arc_proto_map.  Protocol drivers
25762306a36Sopenharmony_ci * are responsible for registering themselves, but the unregister routine
25862306a36Sopenharmony_ci * is pretty generic so we'll do it here.
25962306a36Sopenharmony_ci */
26062306a36Sopenharmony_civoid arcnet_unregister_proto(struct ArcProto *proto)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	int count;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (arc_proto_default == proto)
26562306a36Sopenharmony_ci		arc_proto_default = &arc_proto_null;
26662306a36Sopenharmony_ci	if (arc_bcast_proto == proto)
26762306a36Sopenharmony_ci		arc_bcast_proto = arc_proto_default;
26862306a36Sopenharmony_ci	if (arc_raw_proto == proto)
26962306a36Sopenharmony_ci		arc_raw_proto = arc_proto_default;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	for (count = 0; count < 256; count++) {
27262306a36Sopenharmony_ci		if (arc_proto_map[count] == proto)
27362306a36Sopenharmony_ci			arc_proto_map[count] = arc_proto_default;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ciEXPORT_SYMBOL(arcnet_unregister_proto);
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci/* Add a buffer to the queue.  Only the interrupt handler is allowed to do
27962306a36Sopenharmony_ci * this, unless interrupts are disabled.
28062306a36Sopenharmony_ci *
28162306a36Sopenharmony_ci * Note: we don't check for a full queue, since there aren't enough buffers
28262306a36Sopenharmony_ci * to more than fill it.
28362306a36Sopenharmony_ci */
28462306a36Sopenharmony_cistatic void release_arcbuf(struct net_device *dev, int bufnum)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
28762306a36Sopenharmony_ci	int i;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	lp->buf_queue[lp->first_free_buf++] = bufnum;
29062306a36Sopenharmony_ci	lp->first_free_buf %= 5;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (BUGLVL(D_DURING)) {
29362306a36Sopenharmony_ci		arc_printk(D_DURING, dev, "release_arcbuf: freed #%d; buffer queue is now: ",
29462306a36Sopenharmony_ci			   bufnum);
29562306a36Sopenharmony_ci		for (i = lp->next_buf; i != lp->first_free_buf; i = (i + 1) % 5)
29662306a36Sopenharmony_ci			arc_cont(D_DURING, "#%d ", lp->buf_queue[i]);
29762306a36Sopenharmony_ci		arc_cont(D_DURING, "\n");
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci/* Get a buffer from the queue.
30262306a36Sopenharmony_ci * If this returns -1, there are no buffers available.
30362306a36Sopenharmony_ci */
30462306a36Sopenharmony_cistatic int get_arcbuf(struct net_device *dev)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
30762306a36Sopenharmony_ci	int buf = -1, i;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (!atomic_dec_and_test(&lp->buf_lock)) {
31062306a36Sopenharmony_ci		/* already in this function */
31162306a36Sopenharmony_ci		arc_printk(D_NORMAL, dev, "get_arcbuf: overlap (%d)!\n",
31262306a36Sopenharmony_ci			   lp->buf_lock.counter);
31362306a36Sopenharmony_ci	} else {			/* we can continue */
31462306a36Sopenharmony_ci		if (lp->next_buf >= 5)
31562306a36Sopenharmony_ci			lp->next_buf -= 5;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci		if (lp->next_buf == lp->first_free_buf) {
31862306a36Sopenharmony_ci			arc_printk(D_NORMAL, dev, "get_arcbuf: BUG: no buffers are available??\n");
31962306a36Sopenharmony_ci		} else {
32062306a36Sopenharmony_ci			buf = lp->buf_queue[lp->next_buf++];
32162306a36Sopenharmony_ci			lp->next_buf %= 5;
32262306a36Sopenharmony_ci		}
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	if (BUGLVL(D_DURING)) {
32662306a36Sopenharmony_ci		arc_printk(D_DURING, dev, "get_arcbuf: got #%d; buffer queue is now: ",
32762306a36Sopenharmony_ci			   buf);
32862306a36Sopenharmony_ci		for (i = lp->next_buf; i != lp->first_free_buf; i = (i + 1) % 5)
32962306a36Sopenharmony_ci			arc_cont(D_DURING, "#%d ", lp->buf_queue[i]);
33062306a36Sopenharmony_ci		arc_cont(D_DURING, "\n");
33162306a36Sopenharmony_ci	}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	atomic_inc(&lp->buf_lock);
33462306a36Sopenharmony_ci	return buf;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic int choose_mtu(void)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	int count, mtu = 65535;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	/* choose the smallest MTU of all available encaps */
34262306a36Sopenharmony_ci	for (count = 0; count < 256; count++) {
34362306a36Sopenharmony_ci		if (arc_proto_map[count] != &arc_proto_null &&
34462306a36Sopenharmony_ci		    arc_proto_map[count]->mtu < mtu) {
34562306a36Sopenharmony_ci			mtu = arc_proto_map[count]->mtu;
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return mtu == 65535 ? XMTU : mtu;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic const struct header_ops arcnet_header_ops = {
35362306a36Sopenharmony_ci	.create = arcnet_header,
35462306a36Sopenharmony_ci};
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic const struct net_device_ops arcnet_netdev_ops = {
35762306a36Sopenharmony_ci	.ndo_open	= arcnet_open,
35862306a36Sopenharmony_ci	.ndo_stop	= arcnet_close,
35962306a36Sopenharmony_ci	.ndo_start_xmit = arcnet_send_packet,
36062306a36Sopenharmony_ci	.ndo_tx_timeout = arcnet_timeout,
36162306a36Sopenharmony_ci};
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci/* Setup a struct device for ARCnet. */
36462306a36Sopenharmony_cistatic void arcdev_setup(struct net_device *dev)
36562306a36Sopenharmony_ci{
36662306a36Sopenharmony_ci	dev->type = ARPHRD_ARCNET;
36762306a36Sopenharmony_ci	dev->netdev_ops = &arcnet_netdev_ops;
36862306a36Sopenharmony_ci	dev->header_ops = &arcnet_header_ops;
36962306a36Sopenharmony_ci	dev->hard_header_len = sizeof(struct arc_hardware);
37062306a36Sopenharmony_ci	dev->mtu = choose_mtu();
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	dev->addr_len = ARCNET_ALEN;
37362306a36Sopenharmony_ci	dev->tx_queue_len = 100;
37462306a36Sopenharmony_ci	dev->broadcast[0] = 0x00;	/* for us, broadcasts are address 0 */
37562306a36Sopenharmony_ci	dev->watchdog_timeo = TX_TIMEOUT;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* New-style flags. */
37862306a36Sopenharmony_ci	dev->flags = IFF_BROADCAST;
37962306a36Sopenharmony_ci}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_cistatic void arcnet_timer(struct timer_list *t)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct arcnet_local *lp = from_timer(lp, t, timer);
38462306a36Sopenharmony_ci	struct net_device *dev = lp->dev;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	spin_lock_irq(&lp->lock);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (!lp->reset_in_progress && !netif_carrier_ok(dev)) {
38962306a36Sopenharmony_ci		netif_carrier_on(dev);
39062306a36Sopenharmony_ci		netdev_info(dev, "link up\n");
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	spin_unlock_irq(&lp->lock);
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistatic void reset_device_work(struct work_struct *work)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct arcnet_local *lp;
39962306a36Sopenharmony_ci	struct net_device *dev;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	lp = container_of(work, struct arcnet_local, reset_work);
40262306a36Sopenharmony_ci	dev = lp->dev;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	/* Do not bring the network interface back up if an ifdown
40562306a36Sopenharmony_ci	 * was already done.
40662306a36Sopenharmony_ci	 */
40762306a36Sopenharmony_ci	if (!netif_running(dev) || !lp->reset_in_progress)
40862306a36Sopenharmony_ci		return;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	rtnl_lock();
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* Do another check, in case of an ifdown that was triggered in
41362306a36Sopenharmony_ci	 * the small race window between the exit condition above and
41462306a36Sopenharmony_ci	 * acquiring RTNL.
41562306a36Sopenharmony_ci	 */
41662306a36Sopenharmony_ci	if (!netif_running(dev) || !lp->reset_in_progress)
41762306a36Sopenharmony_ci		goto out;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	dev_close(dev);
42062306a36Sopenharmony_ci	dev_open(dev, NULL);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ciout:
42362306a36Sopenharmony_ci	rtnl_unlock();
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic void arcnet_reply_tasklet(struct tasklet_struct *t)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct arcnet_local *lp = from_tasklet(lp, t, reply_tasklet);
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	struct sk_buff *ackskb, *skb;
43162306a36Sopenharmony_ci	struct sock_exterr_skb *serr;
43262306a36Sopenharmony_ci	struct sock *sk;
43362306a36Sopenharmony_ci	int ret;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	local_irq_disable();
43662306a36Sopenharmony_ci	skb = lp->outgoing.skb;
43762306a36Sopenharmony_ci	if (!skb || !skb->sk) {
43862306a36Sopenharmony_ci		local_irq_enable();
43962306a36Sopenharmony_ci		return;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	sock_hold(skb->sk);
44362306a36Sopenharmony_ci	sk = skb->sk;
44462306a36Sopenharmony_ci	ackskb = skb_clone_sk(skb);
44562306a36Sopenharmony_ci	sock_put(skb->sk);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (!ackskb) {
44862306a36Sopenharmony_ci		local_irq_enable();
44962306a36Sopenharmony_ci		return;
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	serr = SKB_EXT_ERR(ackskb);
45362306a36Sopenharmony_ci	memset(serr, 0, sizeof(*serr));
45462306a36Sopenharmony_ci	serr->ee.ee_errno = ENOMSG;
45562306a36Sopenharmony_ci	serr->ee.ee_origin = SO_EE_ORIGIN_TXSTATUS;
45662306a36Sopenharmony_ci	serr->ee.ee_data = skb_shinfo(skb)->tskey;
45762306a36Sopenharmony_ci	serr->ee.ee_info = lp->reply_status;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/* finally erasing outgoing skb */
46062306a36Sopenharmony_ci	dev_kfree_skb(lp->outgoing.skb);
46162306a36Sopenharmony_ci	lp->outgoing.skb = NULL;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	ackskb->dev = lp->dev;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	ret = sock_queue_err_skb(sk, ackskb);
46662306a36Sopenharmony_ci	if (ret)
46762306a36Sopenharmony_ci		dev_kfree_skb_irq(ackskb);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	local_irq_enable();
47062306a36Sopenharmony_ci};
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_cistruct net_device *alloc_arcdev(const char *name)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	struct net_device *dev;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	dev = alloc_netdev(sizeof(struct arcnet_local),
47762306a36Sopenharmony_ci			   name && *name ? name : "arc%d", NET_NAME_UNKNOWN,
47862306a36Sopenharmony_ci			   arcdev_setup);
47962306a36Sopenharmony_ci	if (dev) {
48062306a36Sopenharmony_ci		struct arcnet_local *lp = netdev_priv(dev);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		lp->dev = dev;
48362306a36Sopenharmony_ci		spin_lock_init(&lp->lock);
48462306a36Sopenharmony_ci		timer_setup(&lp->timer, arcnet_timer, 0);
48562306a36Sopenharmony_ci		INIT_WORK(&lp->reset_work, reset_device_work);
48662306a36Sopenharmony_ci	}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	return dev;
48962306a36Sopenharmony_ci}
49062306a36Sopenharmony_ciEXPORT_SYMBOL(alloc_arcdev);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_civoid free_arcdev(struct net_device *dev)
49362306a36Sopenharmony_ci{
49462306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	/* Do not cancel this at ->ndo_close(), as the workqueue itself
49762306a36Sopenharmony_ci	 * indirectly calls the ifdown path through dev_close().
49862306a36Sopenharmony_ci	 */
49962306a36Sopenharmony_ci	cancel_work_sync(&lp->reset_work);
50062306a36Sopenharmony_ci	free_netdev(dev);
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ciEXPORT_SYMBOL(free_arcdev);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/* Open/initialize the board.  This is called sometime after booting when
50562306a36Sopenharmony_ci * the 'ifconfig' program is run.
50662306a36Sopenharmony_ci *
50762306a36Sopenharmony_ci * This routine should set everything up anew at each open, even registers
50862306a36Sopenharmony_ci * that "should" only need to be set once at boot, so that there is
50962306a36Sopenharmony_ci * non-reboot way to recover if something goes wrong.
51062306a36Sopenharmony_ci */
51162306a36Sopenharmony_ciint arcnet_open(struct net_device *dev)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
51462306a36Sopenharmony_ci	int count, newmtu, error;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	arc_printk(D_INIT, dev, "opened.");
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (!try_module_get(lp->hw.owner))
51962306a36Sopenharmony_ci		return -ENODEV;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (BUGLVL(D_PROTO)) {
52262306a36Sopenharmony_ci		arc_printk(D_PROTO, dev, "protocol map (default is '%c'): ",
52362306a36Sopenharmony_ci			   arc_proto_default->suffix);
52462306a36Sopenharmony_ci		for (count = 0; count < 256; count++)
52562306a36Sopenharmony_ci			arc_cont(D_PROTO, "%c", arc_proto_map[count]->suffix);
52662306a36Sopenharmony_ci		arc_cont(D_PROTO, "\n");
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	tasklet_setup(&lp->reply_tasklet, arcnet_reply_tasklet);
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	arc_printk(D_INIT, dev, "arcnet_open: resetting card.\n");
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	/* try to put the card in a defined state - if it fails the first
53462306a36Sopenharmony_ci	 * time, actually reset it.
53562306a36Sopenharmony_ci	 */
53662306a36Sopenharmony_ci	error = -ENODEV;
53762306a36Sopenharmony_ci	if (lp->hw.reset(dev, 0) && lp->hw.reset(dev, 1))
53862306a36Sopenharmony_ci		goto out_module_put;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	newmtu = choose_mtu();
54162306a36Sopenharmony_ci	if (newmtu < dev->mtu)
54262306a36Sopenharmony_ci		dev->mtu = newmtu;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	arc_printk(D_INIT, dev, "arcnet_open: mtu: %d.\n", dev->mtu);
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	/* autodetect the encapsulation for each host. */
54762306a36Sopenharmony_ci	memset(lp->default_proto, 0, sizeof(lp->default_proto));
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	/* the broadcast address is special - use the 'bcast' protocol */
55062306a36Sopenharmony_ci	for (count = 0; count < 256; count++) {
55162306a36Sopenharmony_ci		if (arc_proto_map[count] == arc_bcast_proto) {
55262306a36Sopenharmony_ci			lp->default_proto[0] = count;
55362306a36Sopenharmony_ci			break;
55462306a36Sopenharmony_ci		}
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* initialize buffers */
55862306a36Sopenharmony_ci	atomic_set(&lp->buf_lock, 1);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	lp->next_buf = lp->first_free_buf = 0;
56162306a36Sopenharmony_ci	release_arcbuf(dev, 0);
56262306a36Sopenharmony_ci	release_arcbuf(dev, 1);
56362306a36Sopenharmony_ci	release_arcbuf(dev, 2);
56462306a36Sopenharmony_ci	release_arcbuf(dev, 3);
56562306a36Sopenharmony_ci	lp->cur_tx = lp->next_tx = -1;
56662306a36Sopenharmony_ci	lp->cur_rx = -1;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	lp->rfc1201.sequence = 1;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	/* bring up the hardware driver */
57162306a36Sopenharmony_ci	if (lp->hw.open)
57262306a36Sopenharmony_ci		lp->hw.open(dev);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (dev->dev_addr[0] == 0)
57562306a36Sopenharmony_ci		arc_printk(D_NORMAL, dev, "WARNING!  Station address 00 is reserved for broadcasts!\n");
57662306a36Sopenharmony_ci	else if (dev->dev_addr[0] == 255)
57762306a36Sopenharmony_ci		arc_printk(D_NORMAL, dev, "WARNING!  Station address FF may confuse DOS networking programs!\n");
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
58062306a36Sopenharmony_ci	if (lp->hw.status(dev) & RESETflag) {
58162306a36Sopenharmony_ci		arc_printk(D_DEBUG, dev, "%s: %d: %s\n",
58262306a36Sopenharmony_ci			   __FILE__, __LINE__, __func__);
58362306a36Sopenharmony_ci		lp->hw.command(dev, CFLAGScmd | RESETclear);
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
58762306a36Sopenharmony_ci	/* make sure we're ready to receive IRQ's. */
58862306a36Sopenharmony_ci	lp->hw.intmask(dev, 0);
58962306a36Sopenharmony_ci	udelay(1);		/* give it time to set the mask before
59062306a36Sopenharmony_ci				 * we reset it again. (may not even be
59162306a36Sopenharmony_ci				 * necessary)
59262306a36Sopenharmony_ci				 */
59362306a36Sopenharmony_ci	arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
59462306a36Sopenharmony_ci	lp->intmask = NORXflag | RECONflag;
59562306a36Sopenharmony_ci	lp->hw.intmask(dev, lp->intmask);
59662306a36Sopenharmony_ci	arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	netif_carrier_off(dev);
59962306a36Sopenharmony_ci	netif_start_queue(dev);
60062306a36Sopenharmony_ci	mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000));
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	arcnet_led_event(dev, ARCNET_LED_EVENT_OPEN);
60362306a36Sopenharmony_ci	return 0;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci out_module_put:
60662306a36Sopenharmony_ci	module_put(lp->hw.owner);
60762306a36Sopenharmony_ci	return error;
60862306a36Sopenharmony_ci}
60962306a36Sopenharmony_ciEXPORT_SYMBOL(arcnet_open);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci/* The inverse routine to arcnet_open - shuts down the card. */
61262306a36Sopenharmony_ciint arcnet_close(struct net_device *dev)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	arcnet_led_event(dev, ARCNET_LED_EVENT_STOP);
61762306a36Sopenharmony_ci	del_timer_sync(&lp->timer);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	netif_stop_queue(dev);
62062306a36Sopenharmony_ci	netif_carrier_off(dev);
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	tasklet_kill(&lp->reply_tasklet);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	/* flush TX and disable RX */
62562306a36Sopenharmony_ci	lp->hw.intmask(dev, 0);
62662306a36Sopenharmony_ci	lp->hw.command(dev, NOTXcmd);	/* stop transmit */
62762306a36Sopenharmony_ci	lp->hw.command(dev, NORXcmd);	/* disable receive */
62862306a36Sopenharmony_ci	mdelay(1);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	/* shut down the card */
63162306a36Sopenharmony_ci	lp->hw.close(dev);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/* reset counters */
63462306a36Sopenharmony_ci	lp->reset_in_progress = 0;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	module_put(lp->hw.owner);
63762306a36Sopenharmony_ci	return 0;
63862306a36Sopenharmony_ci}
63962306a36Sopenharmony_ciEXPORT_SYMBOL(arcnet_close);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic int arcnet_header(struct sk_buff *skb, struct net_device *dev,
64262306a36Sopenharmony_ci			 unsigned short type, const void *daddr,
64362306a36Sopenharmony_ci			 const void *saddr, unsigned len)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	const struct arcnet_local *lp = netdev_priv(dev);
64662306a36Sopenharmony_ci	uint8_t _daddr, proto_num;
64762306a36Sopenharmony_ci	struct ArcProto *proto;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	arc_printk(D_DURING, dev,
65062306a36Sopenharmony_ci		   "create header from %d to %d; protocol %d (%Xh); size %u.\n",
65162306a36Sopenharmony_ci		   saddr ? *(uint8_t *)saddr : -1,
65262306a36Sopenharmony_ci		   daddr ? *(uint8_t *)daddr : -1,
65362306a36Sopenharmony_ci		   type, type, len);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (skb->len != 0 && len != skb->len)
65662306a36Sopenharmony_ci		arc_printk(D_NORMAL, dev, "arcnet_header: Yikes!  skb->len(%d) != len(%d)!\n",
65762306a36Sopenharmony_ci			   skb->len, len);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* Type is host order - ? */
66062306a36Sopenharmony_ci	if (type == ETH_P_ARCNET) {
66162306a36Sopenharmony_ci		proto = arc_raw_proto;
66262306a36Sopenharmony_ci		arc_printk(D_DEBUG, dev, "arc_raw_proto used. proto='%c'\n",
66362306a36Sopenharmony_ci			   proto->suffix);
66462306a36Sopenharmony_ci		_daddr = daddr ? *(uint8_t *)daddr : 0;
66562306a36Sopenharmony_ci	} else if (!daddr) {
66662306a36Sopenharmony_ci		/* if the dest addr isn't provided, we can't choose an
66762306a36Sopenharmony_ci		 * encapsulation!  Store the packet type (eg. ETH_P_IP)
66862306a36Sopenharmony_ci		 * for now, and we'll push on a real header when we do
66962306a36Sopenharmony_ci		 * rebuild_header.
67062306a36Sopenharmony_ci		 */
67162306a36Sopenharmony_ci		*(uint16_t *)skb_push(skb, 2) = type;
67262306a36Sopenharmony_ci		/* XXX: Why not use skb->mac_len? */
67362306a36Sopenharmony_ci		if (skb->network_header - skb->mac_header != 2)
67462306a36Sopenharmony_ci			arc_printk(D_NORMAL, dev, "arcnet_header: Yikes!  diff (%u) is not 2!\n",
67562306a36Sopenharmony_ci				   skb->network_header - skb->mac_header);
67662306a36Sopenharmony_ci		return -2;	/* return error -- can't transmit yet! */
67762306a36Sopenharmony_ci	} else {
67862306a36Sopenharmony_ci		/* otherwise, we can just add the header as usual. */
67962306a36Sopenharmony_ci		_daddr = *(uint8_t *)daddr;
68062306a36Sopenharmony_ci		proto_num = lp->default_proto[_daddr];
68162306a36Sopenharmony_ci		proto = arc_proto_map[proto_num];
68262306a36Sopenharmony_ci		arc_printk(D_DURING, dev, "building header for %02Xh using protocol '%c'\n",
68362306a36Sopenharmony_ci			   proto_num, proto->suffix);
68462306a36Sopenharmony_ci		if (proto == &arc_proto_null && arc_bcast_proto != proto) {
68562306a36Sopenharmony_ci			arc_printk(D_DURING, dev, "actually, let's use '%c' instead.\n",
68662306a36Sopenharmony_ci				   arc_bcast_proto->suffix);
68762306a36Sopenharmony_ci			proto = arc_bcast_proto;
68862306a36Sopenharmony_ci		}
68962306a36Sopenharmony_ci	}
69062306a36Sopenharmony_ci	return proto->build_header(skb, dev, type, _daddr);
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci/* Called by the kernel in order to transmit a packet. */
69462306a36Sopenharmony_cinetdev_tx_t arcnet_send_packet(struct sk_buff *skb,
69562306a36Sopenharmony_ci			       struct net_device *dev)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
69862306a36Sopenharmony_ci	struct archdr *pkt;
69962306a36Sopenharmony_ci	struct arc_rfc1201 *soft;
70062306a36Sopenharmony_ci	struct ArcProto *proto;
70162306a36Sopenharmony_ci	int txbuf;
70262306a36Sopenharmony_ci	unsigned long flags;
70362306a36Sopenharmony_ci	int retval;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	arc_printk(D_DURING, dev,
70662306a36Sopenharmony_ci		   "transmit requested (status=%Xh, txbufs=%d/%d, len=%d, protocol %x)\n",
70762306a36Sopenharmony_ci		   lp->hw.status(dev), lp->cur_tx, lp->next_tx, skb->len, skb->protocol);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	pkt = (struct archdr *)skb->data;
71062306a36Sopenharmony_ci	soft = &pkt->soft.rfc1201;
71162306a36Sopenharmony_ci	proto = arc_proto_map[soft->proto];
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	arc_printk(D_SKB_SIZE, dev, "skb: transmitting %d bytes to %02X\n",
71462306a36Sopenharmony_ci		   skb->len, pkt->hard.dest);
71562306a36Sopenharmony_ci	if (BUGLVL(D_SKB))
71662306a36Sopenharmony_ci		arcnet_dump_skb(dev, skb, "tx");
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	/* fits in one packet? */
71962306a36Sopenharmony_ci	if (skb->len - ARC_HDR_SIZE > XMTU && !proto->continue_tx) {
72062306a36Sopenharmony_ci		arc_printk(D_NORMAL, dev, "fixme: packet too large: compensating badly!\n");
72162306a36Sopenharmony_ci		dev_kfree_skb(skb);
72262306a36Sopenharmony_ci		return NETDEV_TX_OK;	/* don't try again */
72362306a36Sopenharmony_ci	}
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	/* We're busy transmitting a packet... */
72662306a36Sopenharmony_ci	netif_stop_queue(dev);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
72962306a36Sopenharmony_ci	lp->hw.intmask(dev, 0);
73062306a36Sopenharmony_ci	if (lp->next_tx == -1)
73162306a36Sopenharmony_ci		txbuf = get_arcbuf(dev);
73262306a36Sopenharmony_ci	else
73362306a36Sopenharmony_ci		txbuf = -1;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	if (txbuf != -1) {
73662306a36Sopenharmony_ci		lp->outgoing.skb = skb;
73762306a36Sopenharmony_ci		if (proto->prepare_tx(dev, pkt, skb->len, txbuf) &&
73862306a36Sopenharmony_ci		    !proto->ack_tx) {
73962306a36Sopenharmony_ci			/* done right away and we don't want to acknowledge
74062306a36Sopenharmony_ci			 *  the package later - forget about it now
74162306a36Sopenharmony_ci			 */
74262306a36Sopenharmony_ci			dev->stats.tx_bytes += skb->len;
74362306a36Sopenharmony_ci		} else {
74462306a36Sopenharmony_ci			/* do it the 'split' way */
74562306a36Sopenharmony_ci			lp->outgoing.proto = proto;
74662306a36Sopenharmony_ci			lp->outgoing.skb = skb;
74762306a36Sopenharmony_ci			lp->outgoing.pkt = pkt;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci			if (proto->continue_tx &&
75062306a36Sopenharmony_ci			    proto->continue_tx(dev, txbuf)) {
75162306a36Sopenharmony_ci				arc_printk(D_NORMAL, dev,
75262306a36Sopenharmony_ci					   "bug! continue_tx finished the first time! (proto='%c')\n",
75362306a36Sopenharmony_ci					   proto->suffix);
75462306a36Sopenharmony_ci			}
75562306a36Sopenharmony_ci		}
75662306a36Sopenharmony_ci		retval = NETDEV_TX_OK;
75762306a36Sopenharmony_ci		lp->next_tx = txbuf;
75862306a36Sopenharmony_ci	} else {
75962306a36Sopenharmony_ci		retval = NETDEV_TX_BUSY;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n",
76362306a36Sopenharmony_ci		   __FILE__, __LINE__, __func__, lp->hw.status(dev));
76462306a36Sopenharmony_ci	/* make sure we didn't ignore a TX IRQ while we were in here */
76562306a36Sopenharmony_ci	lp->hw.intmask(dev, 0);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
76862306a36Sopenharmony_ci	lp->intmask |= TXFREEflag | EXCNAKflag;
76962306a36Sopenharmony_ci	lp->hw.intmask(dev, lp->intmask);
77062306a36Sopenharmony_ci	arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n",
77162306a36Sopenharmony_ci		   __FILE__, __LINE__, __func__, lp->hw.status(dev));
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	arcnet_led_event(dev, ARCNET_LED_EVENT_TX);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
77662306a36Sopenharmony_ci	return retval;		/* no need to try again */
77762306a36Sopenharmony_ci}
77862306a36Sopenharmony_ciEXPORT_SYMBOL(arcnet_send_packet);
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci/* Actually start transmitting a packet that was loaded into a buffer
78162306a36Sopenharmony_ci * by prepare_tx.  This should _only_ be called by the interrupt handler.
78262306a36Sopenharmony_ci */
78362306a36Sopenharmony_cistatic int go_tx(struct net_device *dev)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	arc_printk(D_DURING, dev, "go_tx: status=%Xh, intmask=%Xh, next_tx=%d, cur_tx=%d\n",
78862306a36Sopenharmony_ci		   lp->hw.status(dev), lp->intmask, lp->next_tx, lp->cur_tx);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (lp->cur_tx != -1 || lp->next_tx == -1)
79162306a36Sopenharmony_ci		return 0;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	if (BUGLVL(D_TX))
79462306a36Sopenharmony_ci		arcnet_dump_packet(dev, lp->next_tx, "go_tx", 0);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	lp->cur_tx = lp->next_tx;
79762306a36Sopenharmony_ci	lp->next_tx = -1;
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* start sending */
80062306a36Sopenharmony_ci	lp->hw.command(dev, TXcmd | (lp->cur_tx << 3));
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	dev->stats.tx_packets++;
80362306a36Sopenharmony_ci	lp->lasttrans_dest = lp->lastload_dest;
80462306a36Sopenharmony_ci	lp->lastload_dest = 0;
80562306a36Sopenharmony_ci	lp->excnak_pending = 0;
80662306a36Sopenharmony_ci	lp->intmask |= TXFREEflag | EXCNAKflag;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	return 1;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci/* Called by the kernel when transmit times out */
81262306a36Sopenharmony_civoid arcnet_timeout(struct net_device *dev, unsigned int txqueue)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	unsigned long flags;
81562306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
81662306a36Sopenharmony_ci	int status = lp->hw.status(dev);
81762306a36Sopenharmony_ci	char *msg;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
82062306a36Sopenharmony_ci	if (status & TXFREEflag) {	/* transmit _DID_ finish */
82162306a36Sopenharmony_ci		msg = " - missed IRQ?";
82262306a36Sopenharmony_ci	} else {
82362306a36Sopenharmony_ci		msg = "";
82462306a36Sopenharmony_ci		dev->stats.tx_aborted_errors++;
82562306a36Sopenharmony_ci		lp->timed_out = 1;
82662306a36Sopenharmony_ci		lp->hw.command(dev, NOTXcmd | (lp->cur_tx << 3));
82762306a36Sopenharmony_ci	}
82862306a36Sopenharmony_ci	dev->stats.tx_errors++;
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	/* make sure we didn't miss a TX or a EXC NAK IRQ */
83162306a36Sopenharmony_ci	lp->hw.intmask(dev, 0);
83262306a36Sopenharmony_ci	lp->intmask |= TXFREEflag | EXCNAKflag;
83362306a36Sopenharmony_ci	lp->hw.intmask(dev, lp->intmask);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	if (time_after(jiffies, lp->last_timeout + 10 * HZ)) {
83862306a36Sopenharmony_ci		arc_printk(D_EXTRA, dev, "tx timed out%s (status=%Xh, intmask=%Xh, dest=%02Xh)\n",
83962306a36Sopenharmony_ci			   msg, status, lp->intmask, lp->lasttrans_dest);
84062306a36Sopenharmony_ci		lp->last_timeout = jiffies;
84162306a36Sopenharmony_ci	}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (lp->cur_tx == -1)
84462306a36Sopenharmony_ci		netif_wake_queue(dev);
84562306a36Sopenharmony_ci}
84662306a36Sopenharmony_ciEXPORT_SYMBOL(arcnet_timeout);
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci/* The typical workload of the driver: Handle the network interface
84962306a36Sopenharmony_ci * interrupts. Establish which device needs attention, and call the correct
85062306a36Sopenharmony_ci * chipset interrupt handler.
85162306a36Sopenharmony_ci */
85262306a36Sopenharmony_ciirqreturn_t arcnet_interrupt(int irq, void *dev_id)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	struct net_device *dev = dev_id;
85562306a36Sopenharmony_ci	struct arcnet_local *lp;
85662306a36Sopenharmony_ci	int recbuf, status, diagstatus, didsomething, boguscount;
85762306a36Sopenharmony_ci	unsigned long flags;
85862306a36Sopenharmony_ci	int retval = IRQ_NONE;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	arc_printk(D_DURING, dev, "\n");
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	arc_printk(D_DURING, dev, "in arcnet_interrupt\n");
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	lp = netdev_priv(dev);
86562306a36Sopenharmony_ci	BUG_ON(!lp);
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	if (lp->reset_in_progress)
87062306a36Sopenharmony_ci		goto out;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	/* RESET flag was enabled - if device is not running, we must
87362306a36Sopenharmony_ci	 * clear it right away (but nothing else).
87462306a36Sopenharmony_ci	 */
87562306a36Sopenharmony_ci	if (!netif_running(dev)) {
87662306a36Sopenharmony_ci		if (lp->hw.status(dev) & RESETflag)
87762306a36Sopenharmony_ci			lp->hw.command(dev, CFLAGScmd | RESETclear);
87862306a36Sopenharmony_ci		lp->hw.intmask(dev, 0);
87962306a36Sopenharmony_ci		spin_unlock_irqrestore(&lp->lock, flags);
88062306a36Sopenharmony_ci		return retval;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	arc_printk(D_DURING, dev, "in arcnet_inthandler (status=%Xh, intmask=%Xh)\n",
88462306a36Sopenharmony_ci		   lp->hw.status(dev), lp->intmask);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	boguscount = 5;
88762306a36Sopenharmony_ci	do {
88862306a36Sopenharmony_ci		status = lp->hw.status(dev);
88962306a36Sopenharmony_ci		diagstatus = (status >> 8) & 0xFF;
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_ci		arc_printk(D_DEBUG, dev, "%s: %d: %s: status=%x\n",
89262306a36Sopenharmony_ci			   __FILE__, __LINE__, __func__, status);
89362306a36Sopenharmony_ci		didsomething = 0;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci		/* RESET flag was enabled - card is resetting and if RX is
89662306a36Sopenharmony_ci		 * disabled, it's NOT because we just got a packet.
89762306a36Sopenharmony_ci		 *
89862306a36Sopenharmony_ci		 * The card is in an undefined state.
89962306a36Sopenharmony_ci		 * Clear it out and start over.
90062306a36Sopenharmony_ci		 */
90162306a36Sopenharmony_ci		if (status & RESETflag) {
90262306a36Sopenharmony_ci			arc_printk(D_NORMAL, dev, "spurious reset (status=%Xh)\n",
90362306a36Sopenharmony_ci				   status);
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci			lp->reset_in_progress = 1;
90662306a36Sopenharmony_ci			netif_stop_queue(dev);
90762306a36Sopenharmony_ci			netif_carrier_off(dev);
90862306a36Sopenharmony_ci			schedule_work(&lp->reset_work);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci			/* get out of the interrupt handler! */
91162306a36Sopenharmony_ci			goto out;
91262306a36Sopenharmony_ci		}
91362306a36Sopenharmony_ci		/* RX is inhibited - we must have received something.
91462306a36Sopenharmony_ci		 * Prepare to receive into the next buffer.
91562306a36Sopenharmony_ci		 *
91662306a36Sopenharmony_ci		 * We don't actually copy the received packet from the card
91762306a36Sopenharmony_ci		 * until after the transmit handler runs (and possibly
91862306a36Sopenharmony_ci		 * launches the next tx); this should improve latency slightly
91962306a36Sopenharmony_ci		 * if we get both types of interrupts at once.
92062306a36Sopenharmony_ci		 */
92162306a36Sopenharmony_ci		recbuf = -1;
92262306a36Sopenharmony_ci		if (status & lp->intmask & NORXflag) {
92362306a36Sopenharmony_ci			recbuf = lp->cur_rx;
92462306a36Sopenharmony_ci			arc_printk(D_DURING, dev, "Buffer #%d: receive irq (status=%Xh)\n",
92562306a36Sopenharmony_ci				   recbuf, status);
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci			lp->cur_rx = get_arcbuf(dev);
92862306a36Sopenharmony_ci			if (lp->cur_rx != -1) {
92962306a36Sopenharmony_ci				arc_printk(D_DURING, dev, "enabling receive to buffer #%d\n",
93062306a36Sopenharmony_ci					   lp->cur_rx);
93162306a36Sopenharmony_ci				lp->hw.command(dev, RXcmd | (lp->cur_rx << 3) | RXbcasts);
93262306a36Sopenharmony_ci			}
93362306a36Sopenharmony_ci			didsomething++;
93462306a36Sopenharmony_ci		}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci		if ((diagstatus & EXCNAKflag)) {
93762306a36Sopenharmony_ci			arc_printk(D_DURING, dev, "EXCNAK IRQ (diagstat=%Xh)\n",
93862306a36Sopenharmony_ci				   diagstatus);
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_ci			lp->hw.command(dev, NOTXcmd);      /* disable transmit */
94162306a36Sopenharmony_ci			lp->excnak_pending = 1;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci			lp->hw.command(dev, EXCNAKclear);
94462306a36Sopenharmony_ci			lp->intmask &= ~(EXCNAKflag);
94562306a36Sopenharmony_ci			didsomething++;
94662306a36Sopenharmony_ci		}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci		/* a transmit finished, and we're interested in it. */
94962306a36Sopenharmony_ci		if ((status & lp->intmask & TXFREEflag) || lp->timed_out) {
95062306a36Sopenharmony_ci			int ackstatus;
95162306a36Sopenharmony_ci			lp->intmask &= ~(TXFREEflag | EXCNAKflag);
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci			if (status & TXACKflag)
95462306a36Sopenharmony_ci				ackstatus = 2;
95562306a36Sopenharmony_ci			else if (lp->excnak_pending)
95662306a36Sopenharmony_ci				ackstatus = 1;
95762306a36Sopenharmony_ci			else
95862306a36Sopenharmony_ci				ackstatus = 0;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci			arc_printk(D_DURING, dev, "TX IRQ (stat=%Xh)\n",
96162306a36Sopenharmony_ci				   status);
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci			if (lp->cur_tx != -1 && !lp->timed_out) {
96462306a36Sopenharmony_ci				if (!(status & TXACKflag)) {
96562306a36Sopenharmony_ci					if (lp->lasttrans_dest != 0) {
96662306a36Sopenharmony_ci						arc_printk(D_EXTRA, dev,
96762306a36Sopenharmony_ci							   "transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
96862306a36Sopenharmony_ci							   status,
96962306a36Sopenharmony_ci							   lp->lasttrans_dest);
97062306a36Sopenharmony_ci						dev->stats.tx_errors++;
97162306a36Sopenharmony_ci						dev->stats.tx_carrier_errors++;
97262306a36Sopenharmony_ci					} else {
97362306a36Sopenharmony_ci						arc_printk(D_DURING, dev,
97462306a36Sopenharmony_ci							   "broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
97562306a36Sopenharmony_ci							   status,
97662306a36Sopenharmony_ci							   lp->lasttrans_dest);
97762306a36Sopenharmony_ci					}
97862306a36Sopenharmony_ci				}
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci				if (lp->outgoing.proto &&
98162306a36Sopenharmony_ci				    lp->outgoing.proto->ack_tx) {
98262306a36Sopenharmony_ci					lp->outgoing.proto
98362306a36Sopenharmony_ci						->ack_tx(dev, ackstatus);
98462306a36Sopenharmony_ci				}
98562306a36Sopenharmony_ci				lp->reply_status = ackstatus;
98662306a36Sopenharmony_ci				tasklet_hi_schedule(&lp->reply_tasklet);
98762306a36Sopenharmony_ci			}
98862306a36Sopenharmony_ci			if (lp->cur_tx != -1)
98962306a36Sopenharmony_ci				release_arcbuf(dev, lp->cur_tx);
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci			lp->cur_tx = -1;
99262306a36Sopenharmony_ci			lp->timed_out = 0;
99362306a36Sopenharmony_ci			didsomething++;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci			/* send another packet if there is one */
99662306a36Sopenharmony_ci			go_tx(dev);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci			/* continue a split packet, if any */
99962306a36Sopenharmony_ci			if (lp->outgoing.proto &&
100062306a36Sopenharmony_ci			    lp->outgoing.proto->continue_tx) {
100162306a36Sopenharmony_ci				int txbuf = get_arcbuf(dev);
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci				if (txbuf != -1) {
100462306a36Sopenharmony_ci					if (lp->outgoing.proto->continue_tx(dev, txbuf)) {
100562306a36Sopenharmony_ci						/* that was the last segment */
100662306a36Sopenharmony_ci						dev->stats.tx_bytes += lp->outgoing.skb->len;
100762306a36Sopenharmony_ci						if (!lp->outgoing.proto->ack_tx) {
100862306a36Sopenharmony_ci							dev_kfree_skb_irq(lp->outgoing.skb);
100962306a36Sopenharmony_ci							lp->outgoing.proto = NULL;
101062306a36Sopenharmony_ci						}
101162306a36Sopenharmony_ci					}
101262306a36Sopenharmony_ci					lp->next_tx = txbuf;
101362306a36Sopenharmony_ci				}
101462306a36Sopenharmony_ci			}
101562306a36Sopenharmony_ci			/* inform upper layers of idleness, if necessary */
101662306a36Sopenharmony_ci			if (lp->cur_tx == -1)
101762306a36Sopenharmony_ci				netif_wake_queue(dev);
101862306a36Sopenharmony_ci		}
101962306a36Sopenharmony_ci		/* now process the received packet, if any */
102062306a36Sopenharmony_ci		if (recbuf != -1) {
102162306a36Sopenharmony_ci			if (BUGLVL(D_RX))
102262306a36Sopenharmony_ci				arcnet_dump_packet(dev, recbuf, "rx irq", 0);
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci			arcnet_rx(dev, recbuf);
102562306a36Sopenharmony_ci			release_arcbuf(dev, recbuf);
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci			didsomething++;
102862306a36Sopenharmony_ci		}
102962306a36Sopenharmony_ci		if (status & lp->intmask & RECONflag) {
103062306a36Sopenharmony_ci			lp->hw.command(dev, CFLAGScmd | CONFIGclear);
103162306a36Sopenharmony_ci			dev->stats.tx_carrier_errors++;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci			arc_printk(D_RECON, dev, "Network reconfiguration detected (status=%Xh)\n",
103462306a36Sopenharmony_ci				   status);
103562306a36Sopenharmony_ci			if (netif_carrier_ok(dev)) {
103662306a36Sopenharmony_ci				netif_carrier_off(dev);
103762306a36Sopenharmony_ci				netdev_info(dev, "link down\n");
103862306a36Sopenharmony_ci			}
103962306a36Sopenharmony_ci			mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000));
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci			arcnet_led_event(dev, ARCNET_LED_EVENT_RECON);
104262306a36Sopenharmony_ci			/* MYRECON bit is at bit 7 of diagstatus */
104362306a36Sopenharmony_ci			if (diagstatus & 0x80)
104462306a36Sopenharmony_ci				arc_printk(D_RECON, dev, "Put out that recon myself\n");
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci			/* is the RECON info empty or old? */
104762306a36Sopenharmony_ci			if (!lp->first_recon || !lp->last_recon ||
104862306a36Sopenharmony_ci			    time_after(jiffies, lp->last_recon + HZ * 10)) {
104962306a36Sopenharmony_ci				if (lp->network_down)
105062306a36Sopenharmony_ci					arc_printk(D_NORMAL, dev, "reconfiguration detected: cabling restored?\n");
105162306a36Sopenharmony_ci				lp->first_recon = lp->last_recon = jiffies;
105262306a36Sopenharmony_ci				lp->num_recons = lp->network_down = 0;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci				arc_printk(D_DURING, dev, "recon: clearing counters.\n");
105562306a36Sopenharmony_ci			} else {	/* add to current RECON counter */
105662306a36Sopenharmony_ci				lp->last_recon = jiffies;
105762306a36Sopenharmony_ci				lp->num_recons++;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci				arc_printk(D_DURING, dev, "recon: counter=%d, time=%lds, net=%d\n",
106062306a36Sopenharmony_ci					   lp->num_recons,
106162306a36Sopenharmony_ci					   (lp->last_recon - lp->first_recon) / HZ,
106262306a36Sopenharmony_ci					   lp->network_down);
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci				/* if network is marked up;
106562306a36Sopenharmony_ci				 * and first_recon and last_recon are 60+ apart;
106662306a36Sopenharmony_ci				 * and the average no. of recons counted is
106762306a36Sopenharmony_ci				 *    > RECON_THRESHOLD/min;
106862306a36Sopenharmony_ci				 * then print a warning message.
106962306a36Sopenharmony_ci				 */
107062306a36Sopenharmony_ci				if (!lp->network_down &&
107162306a36Sopenharmony_ci				    (lp->last_recon - lp->first_recon) <= HZ * 60 &&
107262306a36Sopenharmony_ci				    lp->num_recons >= RECON_THRESHOLD) {
107362306a36Sopenharmony_ci					lp->network_down = 1;
107462306a36Sopenharmony_ci					arc_printk(D_NORMAL, dev, "many reconfigurations detected: cabling problem?\n");
107562306a36Sopenharmony_ci				} else if (!lp->network_down &&
107662306a36Sopenharmony_ci					   lp->last_recon - lp->first_recon > HZ * 60) {
107762306a36Sopenharmony_ci					/* reset counters if we've gone for
107862306a36Sopenharmony_ci					 *  over a minute.
107962306a36Sopenharmony_ci					 */
108062306a36Sopenharmony_ci					lp->first_recon = lp->last_recon;
108162306a36Sopenharmony_ci					lp->num_recons = 1;
108262306a36Sopenharmony_ci				}
108362306a36Sopenharmony_ci			}
108462306a36Sopenharmony_ci		} else if (lp->network_down &&
108562306a36Sopenharmony_ci			   time_after(jiffies, lp->last_recon + HZ * 10)) {
108662306a36Sopenharmony_ci			if (lp->network_down)
108762306a36Sopenharmony_ci				arc_printk(D_NORMAL, dev, "cabling restored?\n");
108862306a36Sopenharmony_ci			lp->first_recon = lp->last_recon = 0;
108962306a36Sopenharmony_ci			lp->num_recons = lp->network_down = 0;
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci			arc_printk(D_DURING, dev, "not recon: clearing counters anyway.\n");
109262306a36Sopenharmony_ci			netif_carrier_on(dev);
109362306a36Sopenharmony_ci		}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		if (didsomething)
109662306a36Sopenharmony_ci			retval |= IRQ_HANDLED;
109762306a36Sopenharmony_ci	} while (--boguscount && didsomething);
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	arc_printk(D_DURING, dev, "arcnet_interrupt complete (status=%Xh, count=%d)\n",
110062306a36Sopenharmony_ci		   lp->hw.status(dev), boguscount);
110162306a36Sopenharmony_ci	arc_printk(D_DURING, dev, "\n");
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	lp->hw.intmask(dev, 0);
110462306a36Sopenharmony_ci	udelay(1);
110562306a36Sopenharmony_ci	lp->hw.intmask(dev, lp->intmask);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ciout:
110862306a36Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
110962306a36Sopenharmony_ci	return retval;
111062306a36Sopenharmony_ci}
111162306a36Sopenharmony_ciEXPORT_SYMBOL(arcnet_interrupt);
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci/* This is a generic packet receiver that calls arcnet??_rx depending on the
111462306a36Sopenharmony_ci * protocol ID found.
111562306a36Sopenharmony_ci */
111662306a36Sopenharmony_cistatic void arcnet_rx(struct net_device *dev, int bufnum)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
111962306a36Sopenharmony_ci	union {
112062306a36Sopenharmony_ci		struct archdr pkt;
112162306a36Sopenharmony_ci		char buf[512];
112262306a36Sopenharmony_ci	} rxdata;
112362306a36Sopenharmony_ci	struct arc_rfc1201 *soft;
112462306a36Sopenharmony_ci	int length, ofs;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci	soft = &rxdata.pkt.soft.rfc1201;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	lp->hw.copy_from_card(dev, bufnum, 0, &rxdata.pkt, ARC_HDR_SIZE);
112962306a36Sopenharmony_ci	if (rxdata.pkt.hard.offset[0]) {
113062306a36Sopenharmony_ci		ofs = rxdata.pkt.hard.offset[0];
113162306a36Sopenharmony_ci		length = 256 - ofs;
113262306a36Sopenharmony_ci	} else {
113362306a36Sopenharmony_ci		ofs = rxdata.pkt.hard.offset[1];
113462306a36Sopenharmony_ci		length = 512 - ofs;
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	/* get the full header, if possible */
113862306a36Sopenharmony_ci	if (sizeof(rxdata.pkt.soft) <= length) {
113962306a36Sopenharmony_ci		lp->hw.copy_from_card(dev, bufnum, ofs, soft, sizeof(rxdata.pkt.soft));
114062306a36Sopenharmony_ci	} else {
114162306a36Sopenharmony_ci		memset(&rxdata.pkt.soft, 0, sizeof(rxdata.pkt.soft));
114262306a36Sopenharmony_ci		lp->hw.copy_from_card(dev, bufnum, ofs, soft, length);
114362306a36Sopenharmony_ci	}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci	arc_printk(D_DURING, dev, "Buffer #%d: received packet from %02Xh to %02Xh (%d+4 bytes)\n",
114662306a36Sopenharmony_ci		   bufnum, rxdata.pkt.hard.source, rxdata.pkt.hard.dest, length);
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	dev->stats.rx_packets++;
114962306a36Sopenharmony_ci	dev->stats.rx_bytes += length + ARC_HDR_SIZE;
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci	/* call the right receiver for the protocol */
115262306a36Sopenharmony_ci	if (arc_proto_map[soft->proto]->is_ip) {
115362306a36Sopenharmony_ci		if (BUGLVL(D_PROTO)) {
115462306a36Sopenharmony_ci			struct ArcProto
115562306a36Sopenharmony_ci			*oldp = arc_proto_map[lp->default_proto[rxdata.pkt.hard.source]],
115662306a36Sopenharmony_ci			*newp = arc_proto_map[soft->proto];
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci			if (oldp != newp) {
115962306a36Sopenharmony_ci				arc_printk(D_PROTO, dev,
116062306a36Sopenharmony_ci					   "got protocol %02Xh; encap for host %02Xh is now '%c' (was '%c')\n",
116162306a36Sopenharmony_ci					   soft->proto, rxdata.pkt.hard.source,
116262306a36Sopenharmony_ci					   newp->suffix, oldp->suffix);
116362306a36Sopenharmony_ci			}
116462306a36Sopenharmony_ci		}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci		/* broadcasts will always be done with the last-used encap. */
116762306a36Sopenharmony_ci		lp->default_proto[0] = soft->proto;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci		/* in striking contrast, the following isn't a hack. */
117062306a36Sopenharmony_ci		lp->default_proto[rxdata.pkt.hard.source] = soft->proto;
117162306a36Sopenharmony_ci	}
117262306a36Sopenharmony_ci	/* call the protocol-specific receiver. */
117362306a36Sopenharmony_ci	arc_proto_map[soft->proto]->rx(dev, bufnum, &rxdata.pkt, length);
117462306a36Sopenharmony_ci}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_cistatic void null_rx(struct net_device *dev, int bufnum,
117762306a36Sopenharmony_ci		    struct archdr *pkthdr, int length)
117862306a36Sopenharmony_ci{
117962306a36Sopenharmony_ci	arc_printk(D_PROTO, dev,
118062306a36Sopenharmony_ci		   "rx: don't know how to deal with proto %02Xh from host %02Xh.\n",
118162306a36Sopenharmony_ci		   pkthdr->soft.rfc1201.proto, pkthdr->hard.source);
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic int null_build_header(struct sk_buff *skb, struct net_device *dev,
118562306a36Sopenharmony_ci			     unsigned short type, uint8_t daddr)
118662306a36Sopenharmony_ci{
118762306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci	arc_printk(D_PROTO, dev,
119062306a36Sopenharmony_ci		   "tx: can't build header for encap %02Xh; load a protocol driver.\n",
119162306a36Sopenharmony_ci		   lp->default_proto[daddr]);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	/* always fails */
119462306a36Sopenharmony_ci	return 0;
119562306a36Sopenharmony_ci}
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci/* the "do nothing" prepare_tx function warns that there's nothing to do. */
119862306a36Sopenharmony_cistatic int null_prepare_tx(struct net_device *dev, struct archdr *pkt,
119962306a36Sopenharmony_ci			   int length, int bufnum)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci	struct arcnet_local *lp = netdev_priv(dev);
120262306a36Sopenharmony_ci	struct arc_hardware newpkt;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	arc_printk(D_PROTO, dev, "tx: no encap for this host; load a protocol driver.\n");
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	/* send a packet to myself -- will never get received, of course */
120762306a36Sopenharmony_ci	newpkt.source = newpkt.dest = dev->dev_addr[0];
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	/* only one byte of actual data (and it's random) */
121062306a36Sopenharmony_ci	newpkt.offset[0] = 0xFF;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	lp->hw.copy_to_card(dev, bufnum, 0, &newpkt, ARC_HDR_SIZE);
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	return 1;		/* done */
121562306a36Sopenharmony_ci}
1216