arcnet.c 32.2 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
2
/*
 * Linux ARCnet driver - device-independent routines
3
 *
Linus Torvalds's avatar
Linus Torvalds committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 * 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.
 *
 * **********************
23
 *
Linus Torvalds's avatar
Linus Torvalds committed
24
25
26
27
 * The change log is now in a file called ChangeLog in this directory.
 *
 * Sources:
 *  - Crynwr arcnet.com/arcether.com packet drivers.
28
 *  - arcnet.c v0.00 dated 1/1/94 and apparently by
Linus Torvalds's avatar
Linus Torvalds committed
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
 *     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>
 */

44
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
Linus Torvalds's avatar
Linus Torvalds committed
45
46
47
48
49
50
51
52

#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>
53
#include <linux/jiffies.h>
54
#include <linux/errqueue.h>
Linus Torvalds's avatar
Linus Torvalds committed
55

56
57
#include <linux/leds.h>

58
#include "arcdevice.h"
59
#include "com9026.h"
60

Linus Torvalds's avatar
Linus Torvalds committed
61
62
63
64
65
66
67
68
/* "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);

69
static void arcnet_rx(struct net_device *dev, int bufnum);
Linus Torvalds's avatar
Linus Torvalds committed
70

71
/* one ArcProto per possible proto ID.  None of the elements of
Linus Torvalds's avatar
Linus Torvalds committed
72
73
74
75
 * 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.
 */
76
77
78
79
80
81
82
83
84
85
86
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);
Linus Torvalds's avatar
Linus Torvalds committed
87

88
static struct ArcProto arc_proto_null = {
Linus Torvalds's avatar
Linus Torvalds committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
	.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,
105
106
			 unsigned short type, const void *daddr,
			 const void *saddr, unsigned len);
Linus Torvalds's avatar
Linus Torvalds committed
107
108
109
110
111
112
113
114
115
116
117
118
static int go_tx(struct net_device *dev);

static int debug = ARCNET_DEBUG;
module_param(debug, int, 0);
MODULE_LICENSE("GPL");

static int __init arcnet_init(void)
{
	int count;

	arcnet_debug = debug;

119
	pr_info("arcnet loaded\n");
Linus Torvalds's avatar
Linus Torvalds committed
120
121
122
123
124
125

	/* 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;

126
	if (BUGLVL(D_DURING))
127
		pr_info("struct sizes: %zd %zd %zd %zd %zd\n",
128
129
130
131
132
			sizeof(struct arc_hardware),
			sizeof(struct arc_rfc1201),
			sizeof(struct arc_rfc1051),
			sizeof(struct arc_eth_encap),
			sizeof(struct archdr));
Linus Torvalds's avatar
Linus Torvalds committed
133
134
135
136
137
138
139
140
141
142
143

	return 0;
}

static void __exit arcnet_exit(void)
{
}

module_init(arcnet_init);
module_exit(arcnet_exit);

144
/* Dump the contents of an sk_buff */
Linus Torvalds's avatar
Linus Torvalds committed
145
146
147
148
#if ARCNET_DEBUG_MAX & D_SKB
void arcnet_dump_skb(struct net_device *dev,
		     struct sk_buff *skb, char *desc)
{
149
	char hdr[32];
Linus Torvalds's avatar
Linus Torvalds committed
150

151
152
153
154
	/* 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);
Linus Torvalds's avatar
Linus Torvalds committed
155
156
157
158
}
EXPORT_SYMBOL(arcnet_dump_skb);
#endif

159
/* Dump the contents of an ARCnet buffer */
Linus Torvalds's avatar
Linus Torvalds committed
160
#if (ARCNET_DEBUG_MAX & (D_RX | D_TX))
161
162
static void arcnet_dump_packet(struct net_device *dev, int bufnum,
			       char *desc, int take_arcnet_lock)
Linus Torvalds's avatar
Linus Torvalds committed
163
{
164
	struct arcnet_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
165
166
167
	int i, length;
	unsigned long flags = 0;
	static uint8_t buf[512];
168
	char hdr[32];
Linus Torvalds's avatar
Linus Torvalds committed
169
170

	/* hw.copy_from_card expects IRQ context so take the IRQ lock
171
172
	 * to keep it single threaded
	 */
173
	if (take_arcnet_lock)
Linus Torvalds's avatar
Linus Torvalds committed
174
175
176
		spin_lock_irqsave(&lp->lock, flags);

	lp->hw.copy_from_card(dev, bufnum, 0, buf, 512);
177
	if (take_arcnet_lock)
Linus Torvalds's avatar
Linus Torvalds committed
178
179
180
181
182
		spin_unlock_irqrestore(&lp->lock, flags);

	/* if the offset[0] byte is nonzero, this is a 256-byte packet */
	length = (buf[2] ? 256 : 512);

183
184
185
186
	/* 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);
Linus Torvalds's avatar
Linus Torvalds committed
187
188
}

189
190
#else

191
#define arcnet_dump_packet(dev, bufnum, desc, take_arcnet_lock) do { } while (0)
192

Linus Torvalds's avatar
Linus Torvalds committed
193
194
#endif

195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/* 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);
	unsigned long led_delay = 350;
	unsigned long tx_delay = 50;

	switch (event) {
	case ARCNET_LED_EVENT_RECON:
		led_trigger_blink_oneshot(lp->recon_led_trig,
					  &led_delay, &led_delay, 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,
					  &tx_delay, &tx_delay, 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);

260
/* Unregister a protocol driver from the arc_proto_map.  Protocol drivers
Linus Torvalds's avatar
Linus Torvalds committed
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
 * 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;
	}
}
280
EXPORT_SYMBOL(arcnet_unregister_proto);
Linus Torvalds's avatar
Linus Torvalds committed
281

282
/* Add a buffer to the queue.  Only the interrupt handler is allowed to do
Linus Torvalds's avatar
Linus Torvalds committed
283
 * this, unless interrupts are disabled.
284
 *
Linus Torvalds's avatar
Linus Torvalds committed
285
286
287
288
289
 * 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)
{
290
	struct arcnet_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
291
292
293
294
295
	int i;

	lp->buf_queue[lp->first_free_buf++] = bufnum;
	lp->first_free_buf %= 5;

296
	if (BUGLVL(D_DURING)) {
297
298
		arc_printk(D_DURING, dev, "release_arcbuf: freed #%d; buffer queue is now: ",
			   bufnum);
299
		for (i = lp->next_buf; i != lp->first_free_buf; i = (i + 1) % 5)
300
301
			arc_cont(D_DURING, "#%d ", lp->buf_queue[i]);
		arc_cont(D_DURING, "\n");
Linus Torvalds's avatar
Linus Torvalds committed
302
303
304
	}
}

305
306
/* Get a buffer from the queue.
 * If this returns -1, there are no buffers available.
Linus Torvalds's avatar
Linus Torvalds committed
307
308
309
 */
static int get_arcbuf(struct net_device *dev)
{
310
	struct arcnet_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
311
312
313
314
	int buf = -1, i;

	if (!atomic_dec_and_test(&lp->buf_lock)) {
		/* already in this function */
315
316
		arc_printk(D_NORMAL, dev, "get_arcbuf: overlap (%d)!\n",
			   lp->buf_lock.counter);
317
	} else {			/* we can continue */
Linus Torvalds's avatar
Linus Torvalds committed
318
319
320
		if (lp->next_buf >= 5)
			lp->next_buf -= 5;

321
		if (lp->next_buf == lp->first_free_buf) {
322
			arc_printk(D_NORMAL, dev, "get_arcbuf: BUG: no buffers are available??\n");
323
		} else {
Linus Torvalds's avatar
Linus Torvalds committed
324
325
326
327
328
			buf = lp->buf_queue[lp->next_buf++];
			lp->next_buf %= 5;
		}
	}

329
	if (BUGLVL(D_DURING)) {
330
331
		arc_printk(D_DURING, dev, "get_arcbuf: got #%d; buffer queue is now: ",
			   buf);
332
		for (i = lp->next_buf; i != lp->first_free_buf; i = (i + 1) % 5)
333
334
			arc_cont(D_DURING, "#%d ", lp->buf_queue[i]);
		arc_cont(D_DURING, "\n");
Linus Torvalds's avatar
Linus Torvalds committed
335
336
337
338
339
340
341
342
343
344
345
346
	}

	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++) {
347
348
		if (arc_proto_map[count] != &arc_proto_null &&
		    arc_proto_map[count]->mtu < mtu) {
Linus Torvalds's avatar
Linus Torvalds committed
349
350
351
352
353
354
355
			mtu = arc_proto_map[count]->mtu;
		}
	}

	return mtu == 65535 ? XMTU : mtu;
}

356
357
358
359
static const struct header_ops arcnet_header_ops = {
	.create = arcnet_header,
};

360
361
362
363
364
365
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,
};
Linus Torvalds's avatar
Linus Torvalds committed
366
367
368
369
370

/* Setup a struct device for ARCnet. */
static void arcdev_setup(struct net_device *dev)
{
	dev->type = ARPHRD_ARCNET;
371
	dev->netdev_ops = &arcnet_netdev_ops;
372
	dev->header_ops = &arcnet_header_ops;
373
	dev->hard_header_len = sizeof(struct arc_hardware);
Linus Torvalds's avatar
Linus Torvalds committed
374
375
376
377
378
379
380
381
382
383
384
	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;
}

385
static void arcnet_timer(struct timer_list *t)
386
{
387
388
	struct arcnet_local *lp = from_timer(lp, t, timer);
	struct net_device *dev = lp->dev;
389
390
391
392
393
394
395

	if (!netif_carrier_ok(dev)) {
		netif_carrier_on(dev);
		netdev_info(dev, "link up\n");
	}
}

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
static void arcnet_reply_tasklet(unsigned long data)
{
	struct arcnet_local *lp = (struct arcnet_local *)data;

	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)
		kfree_skb(ackskb);

	local_irq_enable();
};

442
struct net_device *alloc_arcdev(const char *name)
Linus Torvalds's avatar
Linus Torvalds committed
443
444
445
446
{
	struct net_device *dev;

	dev = alloc_netdev(sizeof(struct arcnet_local),
447
448
			   name && *name ? name : "arc%d", NET_NAME_UNKNOWN,
			   arcdev_setup);
449
	if (dev) {
450
		struct arcnet_local *lp = netdev_priv(dev);
451

452
		lp->dev = dev;
Linus Torvalds's avatar
Linus Torvalds committed
453
		spin_lock_init(&lp->lock);
454
		timer_setup(&lp->timer, arcnet_timer, 0);
Linus Torvalds's avatar
Linus Torvalds committed
455
456
457
458
	}

	return dev;
}
459
EXPORT_SYMBOL(alloc_arcdev);
Linus Torvalds's avatar
Linus Torvalds committed
460

461
/* Open/initialize the board.  This is called sometime after booting when
Linus Torvalds's avatar
Linus Torvalds committed
462
463
464
465
466
467
 * 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.
 */
468
int arcnet_open(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
469
{
470
	struct arcnet_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
471
472
	int count, newmtu, error;

473
	arc_printk(D_INIT, dev, "opened.");
Linus Torvalds's avatar
Linus Torvalds committed
474
475
476
477

	if (!try_module_get(lp->hw.owner))
		return -ENODEV;

478
	if (BUGLVL(D_PROTO)) {
479
480
		arc_printk(D_PROTO, dev, "protocol map (default is '%c'): ",
			   arc_proto_default->suffix);
Linus Torvalds's avatar
Linus Torvalds committed
481
		for (count = 0; count < 256; count++)
482
483
			arc_cont(D_PROTO, "%c", arc_proto_map[count]->suffix);
		arc_cont(D_PROTO, "\n");
Linus Torvalds's avatar
Linus Torvalds committed
484
485
	}

486
487
488
	tasklet_init(&lp->reply_tasklet, arcnet_reply_tasklet,
		     (unsigned long)lp);

489
	arc_printk(D_INIT, dev, "arcnet_open: resetting card.\n");
Linus Torvalds's avatar
Linus Torvalds committed
490
491
492
493
494

	/* try to put the card in a defined state - if it fails the first
	 * time, actually reset it.
	 */
	error = -ENODEV;
495
	if (lp->hw.reset(dev, 0) && lp->hw.reset(dev, 1))
Linus Torvalds's avatar
Linus Torvalds committed
496
497
498
499
500
501
		goto out_module_put;

	newmtu = choose_mtu();
	if (newmtu < dev->mtu)
		dev->mtu = newmtu;

502
	arc_printk(D_INIT, dev, "arcnet_open: mtu: %d.\n", dev->mtu);
Linus Torvalds's avatar
Linus Torvalds committed
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532

	/* 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)
533
		arc_printk(D_NORMAL, dev, "WARNING!  Station address 00 is reserved for broadcasts!\n");
Linus Torvalds's avatar
Linus Torvalds committed
534
	else if (dev->dev_addr[0] == 255)
535
		arc_printk(D_NORMAL, dev, "WARNING!  Station address FF may confuse DOS networking programs!\n");
Linus Torvalds's avatar
Linus Torvalds committed
536

537
	arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
538
	if (lp->hw.status(dev) & RESETflag) {
539
540
		arc_printk(D_DEBUG, dev, "%s: %d: %s\n",
			   __FILE__, __LINE__, __func__);
541
		lp->hw.command(dev, CFLAGScmd | RESETclear);
Linus Torvalds's avatar
Linus Torvalds committed
542
543
	}

544
	arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
Linus Torvalds's avatar
Linus Torvalds committed
545
	/* make sure we're ready to receive IRQ's. */
546
	lp->hw.intmask(dev, 0);
Linus Torvalds's avatar
Linus Torvalds committed
547
548
549
550
	udelay(1);		/* give it time to set the mask before
				 * we reset it again. (may not even be
				 * necessary)
				 */
551
	arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
Linus Torvalds's avatar
Linus Torvalds committed
552
	lp->intmask = NORXflag | RECONflag;
553
	lp->hw.intmask(dev, lp->intmask);
554
	arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
Linus Torvalds's avatar
Linus Torvalds committed
555

556
	netif_carrier_off(dev);
Linus Torvalds's avatar
Linus Torvalds committed
557
	netif_start_queue(dev);
558
	mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000));
Linus Torvalds's avatar
Linus Torvalds committed
559

560
	arcnet_led_event(dev, ARCNET_LED_EVENT_OPEN);
Linus Torvalds's avatar
Linus Torvalds committed
561
562
563
564
565
566
	return 0;

 out_module_put:
	module_put(lp->hw.owner);
	return error;
}
567
EXPORT_SYMBOL(arcnet_open);
Linus Torvalds's avatar
Linus Torvalds committed
568
569

/* The inverse routine to arcnet_open - shuts down the card. */
570
int arcnet_close(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
571
{
572
	struct arcnet_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
573

574
	arcnet_led_event(dev, ARCNET_LED_EVENT_STOP);
575
576
	del_timer_sync(&lp->timer);

Linus Torvalds's avatar
Linus Torvalds committed
577
	netif_stop_queue(dev);
578
	netif_carrier_off(dev);
Linus Torvalds's avatar
Linus Torvalds committed
579

580
581
	tasklet_kill(&lp->reply_tasklet);

Linus Torvalds's avatar
Linus Torvalds committed
582
	/* flush TX and disable RX */
583
584
585
	lp->hw.intmask(dev, 0);
	lp->hw.command(dev, NOTXcmd);	/* stop transmit */
	lp->hw.command(dev, NORXcmd);	/* disable receive */
Linus Torvalds's avatar
Linus Torvalds committed
586
587
588
589
590
591
592
	mdelay(1);

	/* shut down the card */
	lp->hw.close(dev);
	module_put(lp->hw.owner);
	return 0;
}
593
EXPORT_SYMBOL(arcnet_close);
Linus Torvalds's avatar
Linus Torvalds committed
594
595

static int arcnet_header(struct sk_buff *skb, struct net_device *dev,
596
597
			 unsigned short type, const void *daddr,
			 const void *saddr, unsigned len)
Linus Torvalds's avatar
Linus Torvalds committed
598
{
599
	const struct arcnet_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
600
601
602
	uint8_t _daddr, proto_num;
	struct ArcProto *proto;

603
604
605
606
607
	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);
Linus Torvalds's avatar
Linus Torvalds committed
608

609
	if (skb->len != 0 && len != skb->len)
610
611
		arc_printk(D_NORMAL, dev, "arcnet_header: Yikes!  skb->len(%d) != len(%d)!\n",
			   skb->len, len);
Linus Torvalds's avatar
Linus Torvalds committed
612

613
614
615
	/* Type is host order - ? */
	if (type == ETH_P_ARCNET) {
		proto = arc_raw_proto;
616
617
		arc_printk(D_DEBUG, dev, "arc_raw_proto used. proto='%c'\n",
			   proto->suffix);
618
		_daddr = daddr ? *(uint8_t *)daddr : 0;
619
	} else if (!daddr) {
620
621
622
623
		/* 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.
Linus Torvalds's avatar
Linus Torvalds committed
624
		 */
625
		*(uint16_t *)skb_push(skb, 2) = type;
626
		/* XXX: Why not use skb->mac_len? */
627
		if (skb->network_header - skb->mac_header != 2)
628
629
			arc_printk(D_NORMAL, dev, "arcnet_header: Yikes!  diff (%u) is not 2!\n",
				   skb->network_header - skb->mac_header);
Linus Torvalds's avatar
Linus Torvalds committed
630
		return -2;	/* return error -- can't transmit yet! */
631
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
632
		/* otherwise, we can just add the header as usual. */
633
		_daddr = *(uint8_t *)daddr;
Linus Torvalds's avatar
Linus Torvalds committed
634
635
		proto_num = lp->default_proto[_daddr];
		proto = arc_proto_map[proto_num];
636
637
		arc_printk(D_DURING, dev, "building header for %02Xh using protocol '%c'\n",
			   proto_num, proto->suffix);
Linus Torvalds's avatar
Linus Torvalds committed
638
		if (proto == &arc_proto_null && arc_bcast_proto != proto) {
639
640
			arc_printk(D_DURING, dev, "actually, let's use '%c' instead.\n",
				   arc_bcast_proto->suffix);
Linus Torvalds's avatar
Linus Torvalds committed
641
642
643
644
645
646
647
			proto = arc_bcast_proto;
		}
	}
	return proto->build_header(skb, dev, type, _daddr);
}

/* Called by the kernel in order to transmit a packet. */
648
netdev_tx_t arcnet_send_packet(struct sk_buff *skb,
649
			       struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
650
{
651
	struct arcnet_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
652
653
654
655
656
	struct archdr *pkt;
	struct arc_rfc1201 *soft;
	struct ArcProto *proto;
	int txbuf;
	unsigned long flags;
657
	int retval;
Linus Torvalds's avatar
Linus Torvalds committed
658

659
660
	arc_printk(D_DURING, dev,
		   "transmit requested (status=%Xh, txbufs=%d/%d, len=%d, protocol %x)\n",
661
		   lp->hw.status(dev), lp->cur_tx, lp->next_tx, skb->len, skb->protocol);
Linus Torvalds's avatar
Linus Torvalds committed
662

663
	pkt = (struct archdr *)skb->data;
Linus Torvalds's avatar
Linus Torvalds committed
664
665
666
	soft = &pkt->soft.rfc1201;
	proto = arc_proto_map[soft->proto];

667
668
	arc_printk(D_SKB_SIZE, dev, "skb: transmitting %d bytes to %02X\n",
		   skb->len, pkt->hard.dest);
669
670
	if (BUGLVL(D_SKB))
		arcnet_dump_skb(dev, skb, "tx");
Linus Torvalds's avatar
Linus Torvalds committed
671
672
673

	/* fits in one packet? */
	if (skb->len - ARC_HDR_SIZE > XMTU && !proto->continue_tx) {
674
		arc_printk(D_NORMAL, dev, "fixme: packet too large: compensating badly!\n");
Linus Torvalds's avatar
Linus Torvalds committed
675
		dev_kfree_skb(skb);
676
		return NETDEV_TX_OK;	/* don't try again */
Linus Torvalds's avatar
Linus Torvalds committed
677
678
679
680
681
682
	}

	/* We're busy transmitting a packet... */
	netif_stop_queue(dev);

	spin_lock_irqsave(&lp->lock, flags);
683
	lp->hw.intmask(dev, 0);
684
	if (lp->next_tx == -1)
685
		txbuf = get_arcbuf(dev);
686
	else
687
		txbuf = -1;
688

Linus Torvalds's avatar
Linus Torvalds committed
689
	if (txbuf != -1) {
690
		lp->outgoing.skb = skb;
Linus Torvalds's avatar
Linus Torvalds committed
691
692
693
		if (proto->prepare_tx(dev, pkt, skb->len, txbuf) &&
		    !proto->ack_tx) {
			/* done right away and we don't want to acknowledge
694
695
			 *  the package later - forget about it now
			 */
696
			dev->stats.tx_bytes += skb->len;
Linus Torvalds's avatar
Linus Torvalds committed
697
698
699
700
701
702
703
704
		} 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)) {
705
706
707
				arc_printk(D_NORMAL, dev,
					   "bug! continue_tx finished the first time! (proto='%c')\n",
					   proto->suffix);
Linus Torvalds's avatar
Linus Torvalds committed
708
709
			}
		}
710
		retval = NETDEV_TX_OK;
Linus Torvalds's avatar
Linus Torvalds committed
711
712
		lp->next_tx = txbuf;
	} else {
713
		retval = NETDEV_TX_BUSY;
Linus Torvalds's avatar
Linus Torvalds committed
714
715
	}

716
	arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n",
717
		   __FILE__, __LINE__, __func__, lp->hw.status(dev));
Linus Torvalds's avatar
Linus Torvalds committed
718
	/* make sure we didn't ignore a TX IRQ while we were in here */
719
	lp->hw.intmask(dev, 0);
Linus Torvalds's avatar
Linus Torvalds committed
720

721
	arc_printk(D_DEBUG, dev, "%s: %d: %s\n", __FILE__, __LINE__, __func__);
722
	lp->intmask |= TXFREEflag | EXCNAKflag;
723
	lp->hw.intmask(dev, lp->intmask);
724
	arc_printk(D_DEBUG, dev, "%s: %d: %s, status: %x\n",
725
		   __FILE__, __LINE__, __func__, lp->hw.status(dev));
Linus Torvalds's avatar
Linus Torvalds committed
726

727
728
	arcnet_led_event(dev, ARCNET_LED_EVENT_TX);

Linus Torvalds's avatar
Linus Torvalds committed
729
	spin_unlock_irqrestore(&lp->lock, flags);
730
	return retval;		/* no need to try again */
Linus Torvalds's avatar
Linus Torvalds committed
731
}
732
EXPORT_SYMBOL(arcnet_send_packet);
Linus Torvalds's avatar
Linus Torvalds committed
733

734
/* Actually start transmitting a packet that was loaded into a buffer
Linus Torvalds's avatar
Linus Torvalds committed
735
736
737
738
 * by prepare_tx.  This should _only_ be called by the interrupt handler.
 */
static int go_tx(struct net_device *dev)
{
739
	struct arcnet_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
740

741
	arc_printk(D_DURING, dev, "go_tx: status=%Xh, intmask=%Xh, next_tx=%d, cur_tx=%d\n",
742
		   lp->hw.status(dev), lp->intmask, lp->next_tx, lp->cur_tx);
Linus Torvalds's avatar
Linus Torvalds committed
743
744
745
746

	if (lp->cur_tx != -1 || lp->next_tx == -1)
		return 0;

747
748
	if (BUGLVL(D_TX))
		arcnet_dump_packet(dev, lp->next_tx, "go_tx", 0);
Linus Torvalds's avatar
Linus Torvalds committed
749
750
751
752
753

	lp->cur_tx = lp->next_tx;
	lp->next_tx = -1;

	/* start sending */
754
	lp->hw.command(dev, TXcmd | (lp->cur_tx << 3));
Linus Torvalds's avatar
Linus Torvalds committed
755

756
	dev->stats.tx_packets++;
Linus Torvalds's avatar
Linus Torvalds committed
757
758
759
	lp->lasttrans_dest = lp->lastload_dest;
	lp->lastload_dest = 0;
	lp->excnak_pending = 0;
760
	lp->intmask |= TXFREEflag | EXCNAKflag;
Linus Torvalds's avatar
Linus Torvalds committed
761
762
763
764
765

	return 1;
}

/* Called by the kernel when transmit times out */
766
void arcnet_timeout(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
767
768
{
	unsigned long flags;
769
	struct arcnet_local *lp = netdev_priv(dev);
770
	int status = lp->hw.status(dev);
Linus Torvalds's avatar
Linus Torvalds committed
771
772
773
774
775
776
777
	char *msg;

	spin_lock_irqsave(&lp->lock, flags);
	if (status & TXFREEflag) {	/* transmit _DID_ finish */
		msg = " - missed IRQ?";
	} else {
		msg = "";
778
		dev->stats.tx_aborted_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
779
		lp->timed_out = 1;
780
		lp->hw.command(dev, NOTXcmd | (lp->cur_tx << 3));
Linus Torvalds's avatar
Linus Torvalds committed
781
	}
782
	dev->stats.tx_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
783
784

	/* make sure we didn't miss a TX or a EXC NAK IRQ */
785
	lp->hw.intmask(dev, 0);
786
	lp->intmask |= TXFREEflag | EXCNAKflag;
787
	lp->hw.intmask(dev, lp->intmask);
788

Linus Torvalds's avatar
Linus Torvalds committed
789
790
	spin_unlock_irqrestore(&lp->lock, flags);

791
	if (time_after(jiffies, lp->last_timeout + 10 * HZ)) {
792
793
		arc_printk(D_EXTRA, dev, "tx timed out%s (status=%Xh, intmask=%Xh, dest=%02Xh)\n",
			   msg, status, lp->intmask, lp->lasttrans_dest);
Linus Torvalds's avatar
Linus Torvalds committed
794
795
796
797
798
799
		lp->last_timeout = jiffies;
	}

	if (lp->cur_tx == -1)
		netif_wake_queue(dev);
}
800
EXPORT_SYMBOL(arcnet_timeout);
Linus Torvalds's avatar
Linus Torvalds committed
801

802
/* The typical workload of the driver: Handle the network interface
Linus Torvalds's avatar
Linus Torvalds committed
803
804
805
 * interrupts. Establish which device needs attention, and call the correct
 * chipset interrupt handler.
 */
806
irqreturn_t arcnet_interrupt(int irq, void *dev_id)
Linus Torvalds's avatar
Linus Torvalds committed
807
808
809
810
{
	struct net_device *dev = dev_id;
	struct arcnet_local *lp;
	int recbuf, status, diagstatus, didsomething, boguscount;
811
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
812
813
	int retval = IRQ_NONE;

814
	arc_printk(D_DURING, dev, "\n");
Linus Torvalds's avatar
Linus Torvalds committed
815

816
	arc_printk(D_DURING, dev, "in arcnet_interrupt\n");
817
818

	lp = netdev_priv(dev);
819
	BUG_ON(!lp);
820

821
	spin_lock_irqsave(&lp->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
822

823
824
	/* RESET flag was enabled - if device is not running, we must
	 * clear it right away (but nothing else).
Linus Torvalds's avatar
Linus Torvalds committed
825
826
	 */
	if (!netif_running(dev)) {
827
828
829
		if (lp->hw.status(dev) & RESETflag)
			lp->hw.command(dev, CFLAGScmd | RESETclear);
		lp->hw.intmask(dev, 0);
830
		spin_unlock_irqrestore(&lp->lock, flags);
831
		return retval;
Linus Torvalds's avatar
Linus Torvalds committed
832
833
	}

834
	arc_printk(D_DURING, dev, "in arcnet_inthandler (status=%Xh, intmask=%Xh)\n",
835
		   lp->hw.status(dev), lp->intmask);
Linus Torvalds's avatar
Linus Torvalds committed
836
837
838

	boguscount = 5;
	do {
839
		status = lp->hw.status(dev);
840
		diagstatus = (status >> 8) & 0xFF;
Linus Torvalds's avatar
Linus Torvalds committed
841

842
843
		arc_printk(D_DEBUG, dev, "%s: %d: %s: status=%x\n",
			   __FILE__, __LINE__, __func__, status);
Linus Torvalds's avatar
Linus Torvalds committed
844
845
		didsomething = 0;

846
		/* RESET flag was enabled - card is resetting and if RX is
Linus Torvalds's avatar
Linus Torvalds committed
847
		 * disabled, it's NOT because we just got a packet.
848
		 *
849
850
		 * The card is in an undefined state.
		 * Clear it out and start over.
Linus Torvalds's avatar
Linus Torvalds committed
851
852
		 */
		if (status & RESETflag) {
853
854
			arc_printk(D_NORMAL, dev, "spurious reset (status=%Xh)\n",
				   status);
Linus Torvalds's avatar
Linus Torvalds committed
855
856
857
858
859
860
			arcnet_close(dev);
			arcnet_open(dev);

			/* get out of the interrupt handler! */
			break;
		}
861
862
		/* RX is inhibited - we must have received something.
		 * Prepare to receive into the next buffer.
863
		 *
864
865
866
867
		 * 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.
Linus Torvalds's avatar
Linus Torvalds committed
868
869
870
871
		 */
		recbuf = -1;
		if (status & lp->intmask & NORXflag) {
			recbuf = lp->cur_rx;
872
873
			arc_printk(D_DURING, dev, "Buffer #%d: receive irq (status=%Xh)\n",
				   recbuf, status);
Linus Torvalds's avatar
Linus Torvalds committed
874
875
876

			lp->cur_rx = get_arcbuf(dev);
			if (lp->cur_rx != -1) {
877
878
				arc_printk(D_DURING, dev, "enabling receive to buffer #%d\n",
					   lp->cur_rx);
879
				lp->hw.command(dev, RXcmd | (lp->cur_rx << 3) | RXbcasts);
Linus Torvalds's avatar
Linus Torvalds committed
880
881
882
883
			}
			didsomething++;
		}

884
		if ((diagstatus & EXCNAKflag)) {
885
886
			arc_printk(D_DURING, dev, "EXCNAK IRQ (diagstat=%Xh)\n",
				   diagstatus);
Linus Torvalds's avatar
Linus Torvalds committed
887

888
			lp->hw.command(dev, NOTXcmd);      /* disable transmit */
889
			lp->excnak_pending = 1;
Linus Torvalds's avatar
Linus Torvalds committed
890

891
			lp->hw.command(dev, EXCNAKclear);
Linus Torvalds's avatar
Linus Torvalds committed
892
			lp->intmask &= ~(EXCNAKflag);
893
894
			didsomething++;
		}
Linus Torvalds's avatar
Linus Torvalds committed
895
896
897

		/* a transmit finished, and we're interested in it. */
		if ((status & lp->intmask & TXFREEflag) || lp->timed_out) {
898
			int ackstatus;
899
			lp->intmask &= ~(TXFREEflag | EXCNAKflag);
Linus Torvalds's avatar
Linus Torvalds committed
900

901
902
903
904
905
906
907
			if (status & TXACKflag)
				ackstatus = 2;
			else if (lp->excnak_pending)
				ackstatus = 1;
			else
				ackstatus = 0;

Joe Perches's avatar
Joe Perches committed
908
909
			arc_printk(D_DURING, dev, "TX IRQ (stat=%Xh)\n",
				   status);
Linus Torvalds's avatar
Linus Torvalds committed
910
911

			if (lp->cur_tx != -1 && !lp->timed_out) {
912
				if (!(status & TXACKflag)) {
Linus Torvalds's avatar
Linus Torvalds committed
913
					if (lp->lasttrans_dest != 0) {
914
915
916
917
						arc_printk(D_EXTRA, dev,
							   "transmit was not acknowledged! (status=%Xh, dest=%02Xh)\n",
							   status,
							   lp->lasttrans_dest);
918
919
						dev->stats.tx_errors++;
						dev->stats.tx_carrier_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
920
					} else {
921
922
923
924
						arc_printk(D_DURING, dev,
							   "broadcast was not acknowledged; that's normal (status=%Xh, dest=%02Xh)\n",
							   status,
							   lp->lasttrans_dest);
Linus Torvalds's avatar
Linus Torvalds committed
925
926
927
928
929
					}
				}

				if (lp->outgoing.proto &&
				    lp->outgoing.proto->ack_tx) {
930
931
					lp->outgoing.proto
						->ack_tx(dev, ackstatus);
Linus Torvalds's avatar
Linus Torvalds committed
932
				}
933
934
				lp->reply_status = ackstatus;
				tasklet_hi_schedule(&lp->reply_tasklet);
Linus Torvalds's avatar
Linus Torvalds committed
935
936
937
938
939
940
941
942
943
944
945
946
			}
			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 */
Joe Perches's avatar
Joe Perches committed
947
948
			if (lp->outgoing.proto &&
			    lp->outgoing.proto->continue_tx) {
Linus Torvalds's avatar
Linus Torvalds committed
949
				int txbuf = get_arcbuf(dev);
950

Linus Torvalds's avatar
Linus Torvalds committed
951
952
953
				if (txbuf != -1) {
					if (lp->outgoing.proto->continue_tx(dev, txbuf)) {
						/* that was the last segment */
954
						dev->stats.tx_bytes += lp->outgoing.skb->len;
955
						if (!lp->outgoing.proto->ack_tx) {
956
957
958
							dev_kfree_skb_irq(lp->outgoing.skb);
							lp->outgoing.proto = NULL;
						}
Linus Torvalds's avatar
Linus Torvalds committed
959
960
961
962
963
964
965
966
967
968
					}
					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) {
969
970
			if (BUGLVL(D_RX))
				arcnet_dump_packet(dev, recbuf, "rx irq", 0);
Linus Torvalds's avatar
Linus Torvalds committed
971
972
973
974
975
976
977

			arcnet_rx(dev, recbuf);
			release_arcbuf(dev, recbuf);

			didsomething++;
		}
		if (status & lp->intmask & RECONflag) {
978
			lp->hw.command(dev, CFLAGScmd | CONFIGclear);
979
			dev->stats.tx_carrier_errors++;
Linus Torvalds's avatar
Linus Torvalds committed
980

981
982
			arc_printk(D_RECON, dev, "Network reconfiguration detected (status=%Xh)\n",
				   status);
983
984
985
986
987
988
			if (netif_carrier_ok(dev)) {
				netif_carrier_off(dev);
				netdev_info(dev, "link down\n");
			}
			mod_timer(&lp->timer, jiffies + msecs_to_jiffies(1000));

989
			arcnet_led_event(dev, ARCNET_LED_EVENT_RECON);
990
			/* MYRECON bit is at bit 7 of diagstatus */
991
			if (diagstatus & 0x80)
992
				arc_printk(D_RECON, dev, "Put out that recon myself\n");
Linus Torvalds's avatar
Linus Torvalds committed
993
994
995

			/* is the RECON info empty or old? */
			if (!lp->first_recon || !lp->last_recon ||
996
			    time_after(jiffies, lp->last_recon + HZ * 10)) {
Linus Torvalds's avatar
Linus Torvalds committed
997
				if (lp->network_down)
998
					arc_printk(D_NORMAL, dev, "reconfiguration detected: cabling restored?\n");
Linus Torvalds's avatar
Linus Torvalds committed
999
1000
1001
				lp->first_recon = lp->last_recon = jiffies;
				lp->num_recons = lp->network_down = 0;

1002
				arc_printk(D_DURING, dev, "recon: clearing counters.\n");
Linus Torvalds's avatar
Linus Torvalds committed
1003
1004
1005
1006
			} else {	/* add to current RECON counter */
				lp->last_recon = jiffies;
				lp->num_recons++;

1007
1008
1009
1010
				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);
Linus Torvalds's avatar
Linus Torvalds committed
1011
1012
1013
1014
1015
1016
1017

				/* 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.
				 */
1018
1019
1020
				if (!lp->network_down &&
				    (lp->last_recon - lp->first_recon) <= HZ * 60 &&
				    lp->num_recons >= RECON_THRESHOLD) {
Linus Torvalds's avatar
Linus Torvalds committed
1021
					lp->network_down = 1;
1022
					arc_printk(D_NORMAL, dev, "many reconfigurations detected: cabling problem?\n");
1023
1024
				} else if (!lp->network_down &&
					   lp->last_recon - lp->first_recon > HZ * 60) {
Joe Perches's avatar
Joe Perches committed
1025
1026
1027
					/* reset counters if we've gone for
					 *  over a minute.
					 */
Linus Torvalds's avatar
Linus Torvalds committed
1028
1029
1030
1031
					lp->first_recon = lp->last_recon;
					lp->num_recons = 1;
				}
			}
1032
		} else if (lp->network_down &&
1033
			   time_after(jiffies, lp->last_recon + HZ * 10)) {
Linus Torvalds's avatar
Linus Torvalds committed
1034
			if (lp->network_down)
1035
				arc_printk(D_NORMAL, dev, "cabling restored?\n");
Linus Torvalds's avatar
Linus Torvalds committed
1036
1037
1038
			lp->first_recon = lp->last_recon = 0;
			lp->num_recons = lp->network_down = 0;

1039
			arc_printk(D_DURING, dev, "not recon: clearing counters anyway.\n");
1040
			netif_carrier_on(dev);
Linus Torvalds's avatar
Linus Torvalds committed
1041
1042
		}

1043
		if (didsomething)
Linus Torvalds's avatar
Linus Torvalds committed
1044
			retval |= IRQ_HANDLED;
1045
	} while (--boguscount && didsomething);
Linus Torvalds's avatar
Linus Torvalds committed
1046

1047
	arc_printk(D_DURING, dev, "arcnet_interrupt complete (status=%Xh, count=%d)\n",
1048
		   lp->hw.status(dev), boguscount);
1049
	arc_printk(D_DURING, dev, "\n");
Linus Torvalds's avatar
Linus Torvalds committed
1050

1051
	lp->hw.intmask(dev, 0);
Linus Torvalds's avatar
Linus Torvalds committed
1052
	udelay(1);
1053
	lp->hw.intmask(dev, lp->intmask);
1054

1055
	spin_unlock_irqrestore(&lp->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
1056
1057
	return retval;
}
1058
EXPORT_SYMBOL(arcnet_interrupt);
Linus Torvalds's avatar
Linus Torvalds committed
1059

1060
/* This is a generic packet receiver that calls arcnet??_rx depending on the
Linus Torvalds's avatar
Linus Torvalds committed
1061
1062
 * protocol ID found.
 */
1063
static void arcnet_rx(struct net_device *dev, int bufnum)
Linus Torvalds's avatar
Linus Torvalds committed
1064
{
1065
	struct arcnet_local *lp = netdev_priv(dev);
1066
1067
1068
1069
	union {
		struct archdr pkt;
		char buf[512];
	} rxdata;
Linus Torvalds's avatar
Linus Torvalds committed
1070
1071
1072
	struct arc_rfc1201 *soft;
	int length, ofs;

1073
	soft = &rxdata.pkt.soft.rfc1201;
Linus Torvalds's avatar
Linus Torvalds committed
1074

1075
1076
1077
	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];
Linus Torvalds's avatar
Linus Torvalds committed
1078
1079
		length = 256 - ofs;
	} else {
1080
		ofs = rxdata.pkt.hard.offset[1];
Linus Torvalds's avatar
Linus Torvalds committed
1081
1082
1083
1084
		length = 512 - ofs;
	}

	/* get the full header, if possible */
1085
1086
	if (sizeof(rxdata.pkt.soft) <= length) {
		lp->hw.copy_from_card(dev