at1700.c 28 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8
/* at1700.c: A network device driver for  the Allied Telesis AT1700.

	Written 1993-98 by Donald Becker.

	Copyright 1993 United States Government as represented by the
	Director, National Security Agency.

	This software may be used and distributed according to the terms
Linus Torvalds's avatar
Linus Torvalds committed
9
	of the GNU General Public License, incorporated herein by reference.
Linus Torvalds's avatar
Linus Torvalds committed
10

Linus Torvalds's avatar
Linus Torvalds committed
11 12 13 14
	The author may be reached as becker@scyld.com, or C/O
	Scyld Computing Corporation
	410 Severn Ave., Suite 210
	Annapolis MD 21403
Linus Torvalds's avatar
Linus Torvalds committed
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

	This is a device driver for the Allied Telesis AT1700, and
        Fujitsu FMV-181/182/181A/182A/183/184/183A/184A, which are
	straight-forward Fujitsu MB86965 implementations.

	Modification for Fujitsu FMV-18X cards is done by Yutaka Tamiya
	(tamy@flab.fujitsu.co.jp). 

  Sources:
    The Fujitsu MB86965 datasheet.

	After the initial version of this driver was written Gerry Sawkins of
	ATI provided their EEPROM configuration code header file.
    Thanks to NIIBE Yutaka <gniibe@mri.co.jp> for bug fixes.

    MCA bus (AT1720) support by Rene Schmit <rene@bss.lu>

  Bugs:
	The MB86965 has a design flaw that makes all probes unreliable.  Not
	only is it difficult to detect, it also moves around in I/O space in
	response to inb()s from other device probes!
*/
37 38 39 40
/*
	99/03/03  Allied Telesis RE1000 Plus support by T.Hagawa
	99/12/30	port to 2.3.35 by K.Takai
*/
Linus Torvalds's avatar
Linus Torvalds committed
41 42

#include <linux/config.h>
43 44 45
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
46
#include <linux/mca-legacy.h>
Linus Torvalds's avatar
Linus Torvalds committed
47 48 49 50 51 52 53
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/in.h>
54
#include <linux/skbuff.h>
Linus Torvalds's avatar
Linus Torvalds committed
55
#include <linux/slab.h>
Linus Torvalds's avatar
Linus Torvalds committed
56 57
#include <linux/string.h>
#include <linux/init.h>
Linus Torvalds's avatar
Linus Torvalds committed
58
#include <linux/crc32.h>
59

Linus Torvalds's avatar
Linus Torvalds committed
60 61 62 63 64
#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>

Linus Torvalds's avatar
Linus Torvalds committed
65 66 67
static char version[] __initdata =
	"at1700.c:v1.15 4/7/98  Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";

Linus Torvalds's avatar
Linus Torvalds committed
68 69 70 71 72 73 74
/* Tunable parameters. */

/* When to switch from the 64-entry multicast filter to Rx-all-multicast. */
#define MC_FILTERBREAK 64

/* These unusual address orders are used to verify the CONFIG register. */

Linus Torvalds's avatar
Linus Torvalds committed
75
static int fmv18x_probe_list[] __initdata = {
Linus Torvalds's avatar
Linus Torvalds committed
76 77 78 79 80 81 82
	0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x300, 0x340, 0
};

/*
 *	ISA
 */

83
#ifndef CONFIG_X86_PC9800
Stephen Hemminger's avatar
Stephen Hemminger committed
84
static unsigned at1700_probe_list[] __initdata = {
Linus Torvalds's avatar
Linus Torvalds committed
85 86 87
	0x260, 0x280, 0x2a0, 0x240, 0x340, 0x320, 0x380, 0x300, 0
};

88
#else /* CONFIG_X86_PC9800 */
Stephen Hemminger's avatar
Stephen Hemminger committed
89
static unsigned at1700_probe_list[] __initdata = {
90 91 92 93
	0x1d6, 0x1d8, 0x1da, 0x1d4, 0xd4, 0xd2, 0xd8, 0xd0, 0
};

#endif /* CONFIG_X86_PC9800 */
Linus Torvalds's avatar
Linus Torvalds committed
94 95 96 97
/*
 *	MCA
 */
#ifdef CONFIG_MCA	
Linus Torvalds's avatar
Linus Torvalds committed
98
static int at1700_ioaddr_pattern[] __initdata = {
Linus Torvalds's avatar
Linus Torvalds committed
99 100 101
	0x00, 0x04, 0x01, 0x05, 0x02, 0x06, 0x03, 0x07
};

Linus Torvalds's avatar
Linus Torvalds committed
102
static int at1700_mca_probe_list[] __initdata = {
Linus Torvalds's avatar
Linus Torvalds committed
103 104 105
	0x400, 0x1400, 0x2400, 0x3400, 0x4400, 0x5400, 0x6400, 0x7400, 0
};

Linus Torvalds's avatar
Linus Torvalds committed
106
static int at1700_irq_pattern[] __initdata = {
Linus Torvalds's avatar
Linus Torvalds committed
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
	0x00, 0x00, 0x00, 0x30, 0x70, 0xb0, 0x00, 0x00,
	0x00, 0xf0, 0x34, 0x74, 0xb4, 0x00, 0x00, 0xf4, 0x00
};
#endif

/* use 0 for production, 1 for verification, >2 for debug */
#ifndef NET_DEBUG
#define NET_DEBUG 1
#endif
static unsigned int net_debug = NET_DEBUG;

typedef unsigned char uchar;

/* Information that need to be kept for each board. */
struct net_local {
	struct net_device_stats stats;
	spinlock_t lock;
	unsigned char mc_filter[8];
	uint jumpered:1;			/* Set iff the board has jumper config. */
	uint tx_started:1;			/* Packets are on the Tx queue. */
	uint tx_queue_ready:1;			/* Tx queue is ready to be sent. */
	uint rx_started:1;			/* Packets are Rxing. */
	uchar tx_queue;				/* Number of packet on the Tx queue. */
	char mca_slot;				/* -1 means ISA */
	ushort tx_queue_len;			/* Current length of the Tx queue. */
};


/* Offsets from the base address. */
136
#ifndef CONFIG_X86_PC9800
Linus Torvalds's avatar
Linus Torvalds committed
137 138 139 140 141 142 143 144 145 146 147 148 149 150
#define STATUS			0
#define TX_STATUS		0
#define RX_STATUS		1
#define TX_INTR			2		/* Bit-mapped interrupt enable registers. */
#define RX_INTR			3
#define TX_MODE			4
#define RX_MODE			5
#define CONFIG_0		6		/* Misc. configuration settings. */
#define CONFIG_1		7
/* Run-time register bank 2 definitions. */
#define DATAPORT		8		/* Word-wide DMA or programmed-I/O dataport. */
#define TX_START		10
#define COL16CNTL		11		/* Controll Reg for 16 collisions */
#define MODE13			13
151
#define RX_CTRL			14
Linus Torvalds's avatar
Linus Torvalds committed
152 153 154 155 156 157 158 159
/* Configuration registers only on the '865A/B chips. */
#define EEPROM_Ctrl 	16
#define EEPROM_Data 	17
#define CARDSTATUS	16			/* FMV-18x Card Status */
#define CARDSTATUS1	17			/* FMV-18x Card Status */
#define IOCONFIG		18		/* Either read the jumper, or move the I/O. */
#define IOCONFIG1		19
#define	SAPROM			20		/* The station address PROM, if no EEPROM. */
160
#define MODE24			24
Linus Torvalds's avatar
Linus Torvalds committed
161 162
#define RESET			31		/* Write to reset some parts of the chip. */
#define AT1700_IO_EXTENT	32
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
#define PORT_OFFSET(o) (o)
#else /* CONFIG_X86_PC9800 */
#define STATUS			(0x0000)
#define TX_STATUS		(0x0000)
#define RX_STATUS		(0x0001)
#define TX_INTR			(0x0200)/* Bit-mapped interrupt enable registers. */
#define RX_INTR			(0x0201)
#define TX_MODE			(0x0400)
#define RX_MODE			(0x0401)
#define CONFIG_0		(0x0600)/* Misc. configuration settings. */
#define CONFIG_1		(0x0601)
/* Run-time register bank 2 definitions. */
#define DATAPORT		(0x0800)/* Word-wide DMA or programmed-I/O dataport. */
#define TX_START		(0x0a00)
#define COL16CNTL		(0x0a01)/* Controll Reg for 16 collisions */
#define MODE13			(0x0c01)
#define RX_CTRL			(0x0e00)
/* Configuration registers only on the '865A/B chips. */
#define EEPROM_Ctrl 	(0x1000)
#define EEPROM_Data 	(0x1200)
#define CARDSTATUS	16			/* FMV-18x Card Status */
#define CARDSTATUS1	17			/* FMV-18x Card Status */
#define IOCONFIG		(0x1400)/* Either read the jumper, or move the I/O. */
#define IOCONFIG1		(0x1600)
#define	SAPROM			20		/* The station address PROM, if no EEPROM. */
#define	MODE24			(0x1800)/* The station address PROM, if no EEPROM. */
#define RESET			(0x1e01)/* Write to reset some parts of the chip. */
#define PORT_OFFSET(o) ({ int _o_ = (o); (_o_ & ~1) * 0x100 + (_o_ & 1); })
#endif /* CONFIG_X86_PC9800 */

Linus Torvalds's avatar
Linus Torvalds committed
193 194 195 196 197 198 199

#define TX_TIMEOUT		10


/* Index to functions, as function prototypes. */

static int at1700_probe1(struct net_device *dev, int ioaddr);
Linus Torvalds's avatar
Linus Torvalds committed
200
static int read_eeprom(long ioaddr, int location);
Linus Torvalds's avatar
Linus Torvalds committed
201 202
static int net_open(struct net_device *dev);
static int	net_send_packet(struct sk_buff *skb, struct net_device *dev);
Jeff Garzik's avatar
Jeff Garzik committed
203
static irqreturn_t net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
Linus Torvalds's avatar
Linus Torvalds committed
204 205 206 207 208 209 210 211 212 213 214 215 216 217
static void net_rx(struct net_device *dev);
static int net_close(struct net_device *dev);
static struct net_device_stats *net_get_stats(struct net_device *dev);
static void set_rx_mode(struct net_device *dev);
static void net_tx_timeout (struct net_device *dev);


#ifdef CONFIG_MCA
struct at1720_mca_adapters_struct {
	char* name;
	int id;
};
/* rEnE : maybe there are others I don't know off... */

Linus Torvalds's avatar
Linus Torvalds committed
218
static struct at1720_mca_adapters_struct at1720_mca_adapters[] __initdata = {
Linus Torvalds's avatar
Linus Torvalds committed
219 220
	{ "Allied Telesys AT1720AT",	0x6410 },
	{ "Allied Telesys AT1720BT", 	0x6413 },
Linus Torvalds's avatar
Linus Torvalds committed
221
	{ "Allied Telesys AT1720T",	0x6416 },
Linus Torvalds's avatar
Linus Torvalds committed
222 223 224 225 226 227 228 229 230 231 232
	{ NULL, 0 },
};
#endif

/* Check for a network adaptor of this type, and return '0' iff one exists.
   If dev->base_addr == 0, probe all likely locations.
   If dev->base_addr == 1, always return failure.
   If dev->base_addr == 2, allocate space for the device and return success
   (detachable devices only).
   */

Stephen Hemminger's avatar
Stephen Hemminger committed
233 234 235 236 237 238 239 240 241
#ifndef CONFIG_X86_PC9800
static int io = 0x260;
#else
static int io = 0xd0;
#endif

static int irq;

static void cleanup_card(struct net_device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
242
{
Stephen Hemminger's avatar
Stephen Hemminger committed
243
#ifdef CONFIG_MCA	
244
	struct net_local *lp = netdev_priv(dev);
Stephen Hemminger's avatar
Stephen Hemminger committed
245 246 247 248 249 250 251 252 253 254 255 256 257 258
	if (lp->mca_slot)
		mca_mark_as_unused(lp->mca_slot);
#endif	
	free_irq(dev->irq, NULL);
#ifndef CONFIG_X86_PC9800
	release_region(dev->base_addr, AT1700_IO_EXTENT);
#else
	{
		int i;
		for (i = 0; i < 0x2000; i += 0x200)
			release_region(dev->base_addr + i, 2);
	}
#endif
}
Linus Torvalds's avatar
Linus Torvalds committed
259

Stephen Hemminger's avatar
Stephen Hemminger committed
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
struct net_device * __init at1700_probe(int unit)
{
	struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
	unsigned *port;
	int err = 0;

	if (!dev)
		return ERR_PTR(-ENODEV);

	if (unit >= 0) {
		sprintf(dev->name, "eth%d", unit);
		netdev_boot_setup_check(dev);
		io = dev->base_addr;
		irq = dev->irq;
	} else {
		dev->base_addr = io;
		dev->irq = irq;
	}
Linus Torvalds's avatar
Linus Torvalds committed
278

Stephen Hemminger's avatar
Stephen Hemminger committed
279
	SET_MODULE_OWNER(dev);
Linus Torvalds's avatar
Linus Torvalds committed
280

Stephen Hemminger's avatar
Stephen Hemminger committed
281 282 283 284 285 286 287 288 289 290 291 292
	if (io > 0x1ff) {	/* Check a single specified location. */
		err = at1700_probe1(dev, io);
	} else if (io != 0) {	/* Don't probe at all. */
		err = -ENXIO;
	} else {
		for (port = at1700_probe_list; *port; port++) {
			if (at1700_probe1(dev, *port) == 0)
				break;
			dev->irq = irq;
		}
		if (!*port)
			err = -ENODEV;
Linus Torvalds's avatar
Linus Torvalds committed
293
	}
Stephen Hemminger's avatar
Stephen Hemminger committed
294 295 296 297 298 299 300 301 302 303 304
	if (err)
		goto out;
	err = register_netdev(dev);
	if (err)
		goto out1;
	return dev;
out1:
	cleanup_card(dev);
out:
	free_netdev(dev);
	return ERR_PTR(err);
Linus Torvalds's avatar
Linus Torvalds committed
305 306 307 308 309 310 311 312 313 314
}

/* The Fujitsu datasheet suggests that the NIC be probed for by checking its
   "signature", the default bit pattern after a reset.  This *doesn't* work --
   there is no way to reset the bus interface without a complete power-cycle!

   It turns out that ATI came to the same conclusion I did: the only thing
   that can be done is checking a few bits and then diving right into an
   EEPROM read. */

Linus Torvalds's avatar
Linus Torvalds committed
315
static int __init at1700_probe1(struct net_device *dev, int ioaddr)
Linus Torvalds's avatar
Linus Torvalds committed
316 317 318 319 320 321
{
	char fmv_irqmap[4] = {3, 7, 10, 15};
	char fmv_irqmap_pnp[8] = {3, 4, 5, 7, 9, 10, 11, 15};
	char at1700_irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15};
	unsigned int i, irq, is_fmv18x = 0, is_at1700 = 0;
	int slot, ret = -ENODEV;
322 323
	struct net_local *lp = netdev_priv(dev);

324
#ifndef CONFIG_X86_PC9800
Linus Torvalds's avatar
Linus Torvalds committed
325 326
	if (!request_region(ioaddr, AT1700_IO_EXTENT, dev->name))
		return -EBUSY;
327 328 329 330 331 332 333 334 335 336 337
#else
	for (i = 0; i < 0x2000; i += 0x0200) {
		if (!request_region(ioaddr + i, 2, dev->name)) {
			while (i > 0) {
				i -= 0x0200;
				release_region(ioaddr + i, 2);
			}
			return -EBUSY;
		}
	}
#endif
Linus Torvalds's avatar
Linus Torvalds committed
338

Stephen Hemminger's avatar
Stephen Hemminger committed
339 340 341 342
	/* Resetting the chip doesn't reset the ISA interface, so don't bother.
	   That means we have to be careful with the register values we probe
	   for.
	 */
Linus Torvalds's avatar
Linus Torvalds committed
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
#ifdef notdef
	printk("at1700 probe at %#x, eeprom is %4.4x %4.4x %4.4x ctrl %4.4x.\n",
		   ioaddr, read_eeprom(ioaddr, 4), read_eeprom(ioaddr, 5),
		   read_eeprom(ioaddr, 6), inw(ioaddr + EEPROM_Ctrl));
#endif

#ifdef CONFIG_MCA
	/* rEnE (rene@bss.lu): got this from 3c509 driver source , adapted for AT1720 */

    /* Based on Erik Nygren's (nygren@mit.edu) 3c529 patch, heavily
	modified by Chris Beauregard (cpbeaure@csclub.uwaterloo.ca)
	to support standard MCA probing. */

	/* redone for multi-card detection by ZP Gu (zpg@castle.net) */
	/* now works as a module */

	if (MCA_bus) {
		int j;
		int l_i;
		u_char pos3, pos4;

		for (j = 0; at1720_mca_adapters[j].name != NULL; j ++) {
			slot = 0;
			while (slot != MCA_NOTFOUND) {
				
				slot = mca_find_unused_adapter( at1720_mca_adapters[j].id, slot );
				if (slot == MCA_NOTFOUND) break;

				/* if we get this far, an adapter has been detected and is
				enabled */

				pos3 = mca_read_stored_pos( slot, 3 );
				pos4 = mca_read_stored_pos( slot, 4 );

				for (l_i = 0; l_i < 0x09; l_i++)
					if (( pos3 & 0x07) == at1700_ioaddr_pattern[l_i])
						break;
				ioaddr = at1700_mca_probe_list[l_i];
				
				for (irq = 0; irq < 0x10; irq++)
					if (((((pos4>>4) & 0x0f) | (pos3 & 0xf0)) & 0xff) == at1700_irq_pattern[irq])
						break;

					/* probing for a card at a particular IO/IRQ */
Stephen Hemminger's avatar
Stephen Hemminger committed
387 388
				if ((dev->irq && dev->irq != irq) ||
				    (dev->base_addr && dev->base_addr != ioaddr)) {
Linus Torvalds's avatar
Linus Torvalds committed
389 390 391 392
				  	slot++;		/* probing next slot */
				  	continue;
				}

Stephen Hemminger's avatar
Stephen Hemminger committed
393
				dev->irq = irq;
Linus Torvalds's avatar
Linus Torvalds committed
394 395 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
				
				/* claim the slot */
				mca_set_adapter_name( slot, at1720_mca_adapters[j].name );
				mca_mark_as_used(slot);

				goto found;
			}
		}
		/* if we get here, we didn't find an MCA adapter - try ISA */
	}
#endif
	slot = -1;
	/* We must check for the EEPROM-config boards first, else accessing
	   IOCONFIG0 will move the board! */
	if (at1700_probe_list[inb(ioaddr + IOCONFIG1) & 0x07] == ioaddr
		&& read_eeprom(ioaddr, 4) == 0x0000
		&& (read_eeprom(ioaddr, 5) & 0xff00) == 0xF400)
		is_at1700 = 1;
	else if (inb(ioaddr   + SAPROM    ) == 0x00
		&& inb(ioaddr + SAPROM + 1) == 0x00
		&& inb(ioaddr + SAPROM + 2) == 0x0e)
		is_fmv18x = 1;
	else {
		goto err_out;
	}
			
#ifdef CONFIG_MCA
found:
#endif

		/* Reset the internal state machines. */
	outb(0, ioaddr + RESET);

427 428
	if (is_at1700) {
#ifndef CONFIG_X86_PC9800
Linus Torvalds's avatar
Linus Torvalds committed
429 430
		irq = at1700_irqmap[(read_eeprom(ioaddr, 12)&0x04)
						   | (read_eeprom(ioaddr, 0)>>14)];
431 432 433 434 435 436 437
#else
		{
			char re1000plus_irqmap[4] = {3, 5, 6, 12};
			irq = re1000plus_irqmap[inb(ioaddr + IOCONFIG1) >> 6];
		}
#endif
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
438 439 440 441 442 443 444 445 446 447 448 449 450
		/* Check PnP mode for FMV-183/184/183A/184A. */
		/* This PnP routine is very poor. IO and IRQ should be known. */
		if (inb(ioaddr + CARDSTATUS1) & 0x20) {
			irq = dev->irq;
			for (i = 0; i < 8; i++) {
				if (irq == fmv_irqmap_pnp[i])
					break;
			}
			if (i == 8) {
				goto err_out;
			}
		} else {
			if (fmv18x_probe_list[inb(ioaddr + IOCONFIG) & 0x07] != ioaddr)
451
				goto err_out;
Linus Torvalds's avatar
Linus Torvalds committed
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
			irq = fmv_irqmap[(inb(ioaddr + IOCONFIG)>>6) & 0x03];
		}
	}

	printk("%s: %s found at %#3x, IRQ %d, address ", dev->name,
		   is_at1700 ? "AT1700" : "FMV-18X", ioaddr, irq);

	dev->base_addr = ioaddr;
	dev->irq = irq;

	if (is_at1700) {
		for(i = 0; i < 3; i++) {
			unsigned short eeprom_val = read_eeprom(ioaddr, 4+i);
			printk("%04x", eeprom_val);
			((unsigned short *)dev->dev_addr)[i] = ntohs(eeprom_val);
		}
	} else {
		for(i = 0; i < 6; i++) {
			unsigned char val = inb(ioaddr + SAPROM + i);
			printk("%02x", val);
			dev->dev_addr[i] = val;
		}
	}

	/* The EEPROM word 12 bit 0x0400 means use regular 100 ohm 10baseT signals,
	   rather than 150 ohm shielded twisted pair compensation.
	   0x0000 == auto-sense the interface
	   0x0800 == use TP interface
	   0x1800 == use coax interface
	   */
	{
		const char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2"};
		if (is_at1700) {
			ushort setup_value = read_eeprom(ioaddr, 12);
			dev->if_port = setup_value >> 8;
		} else {
			ushort setup_value = inb(ioaddr + CARDSTATUS);
			switch (setup_value & 0x07) {
			case 0x01: /* 10base5 */
			case 0x02: /* 10base2 */
				dev->if_port = 0x18; break;
			case 0x04: /* 10baseT */
				dev->if_port = 0x08; break;
			default:   /* auto-sense */
				dev->if_port = 0x00; break;
			}
		}
		printk(" %s interface.\n", porttype[(dev->if_port>>3) & 3]);
	}

	/* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
	   bus access, two 4K Tx queues, and disabled Tx and Rx. */
	outb(0xda, ioaddr + CONFIG_0);

	/* Set the station address in bank zero. */
	outb(0x00, ioaddr + CONFIG_1);
	for (i = 0; i < 6; i++)
509
		outb(dev->dev_addr[i], ioaddr + PORT_OFFSET(8 + i));
Linus Torvalds's avatar
Linus Torvalds committed
510 511 512 513

	/* Switch to bank 1 and set the multicast table to accept none. */
	outb(0x04, ioaddr + CONFIG_1);
	for (i = 0; i < 8; i++)
514
		outb(0x00, ioaddr + PORT_OFFSET(8 + i));
Linus Torvalds's avatar
Linus Torvalds committed
515 516 517 518 519


	/* Switch to bank 2 */
	/* Lock our I/O address, and set manual processing mode for 16 collisions. */
	outb(0x08, ioaddr + CONFIG_1);
520
#ifndef CONFIG_X86_PC9800
Linus Torvalds's avatar
Linus Torvalds committed
521
	outb(dev->if_port, ioaddr + MODE13);
522 523 524
#else
	outb(0, ioaddr + MODE13);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
525 526 527 528 529
	outb(0x00, ioaddr + COL16CNTL);

	if (net_debug)
		printk(version);

Stephen Hemminger's avatar
Stephen Hemminger committed
530
	memset(lp, 0, sizeof(struct net_local));
Linus Torvalds's avatar
Linus Torvalds committed
531 532 533 534 535 536 537 538 539

	dev->open		= net_open;
	dev->stop		= net_close;
	dev->hard_start_xmit = net_send_packet;
	dev->get_stats	= net_get_stats;
	dev->set_multicast_list = &set_rx_mode;
	dev->tx_timeout = net_tx_timeout;
	dev->watchdog_timeo = TX_TIMEOUT;

Stephen Hemminger's avatar
Stephen Hemminger committed
540
	spin_lock_init(&lp->lock);
Linus Torvalds's avatar
Linus Torvalds committed
541 542 543 544 545 546 547 548

	lp->jumpered = is_fmv18x;
	lp->mca_slot = slot;
	/* Snarf the interrupt vector now. */
	ret = request_irq(irq, &net_interrupt, 0, dev->name, dev);
	if (ret) {
		printk ("  AT1700 at %#3x is unusable due to a conflict on"
				"IRQ %d.\n", ioaddr, irq);
Stephen Hemminger's avatar
Stephen Hemminger committed
549
		goto err_out;
Linus Torvalds's avatar
Linus Torvalds committed
550 551 552 553 554
	}

	return 0;

err_out:
555
#ifndef CONFIG_X86_PC9800
Linus Torvalds's avatar
Linus Torvalds committed
556
	release_region(ioaddr, AT1700_IO_EXTENT);
557 558 559 560
#else
	for (i = 0; i < 0x2000; i += 0x0200)
		release_region(ioaddr + i, 2);
#endif
Linus Torvalds's avatar
Linus Torvalds committed
561 562 563 564 565 566 567 568 569 570 571
	return ret;
}


/*  EEPROM_Ctrl bits. */
#define EE_SHIFT_CLK	0x40	/* EEPROM shift clock, in reg. 16. */
#define EE_CS			0x20	/* EEPROM chip select, in reg. 16. */
#define EE_DATA_WRITE	0x80	/* EEPROM chip data in, in reg. 17. */
#define EE_DATA_READ	0x80	/* EEPROM chip data out, in reg. 17. */

/* Delay between EEPROM clock transitions. */
572
#ifndef CONFIG_X86_PC9800
Dave Jones's avatar
Dave Jones committed
573
#define eeprom_delay()	do { } while (0)
574 575 576
#else
#define eeprom_delay()	__asm__ ("out%B0 %%al,%0" :: "N"(0x5f))
#endif
Linus Torvalds's avatar
Linus Torvalds committed
577 578 579 580 581 582

/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD	(5 << 6)
#define EE_READ_CMD		(6 << 6)
#define EE_ERASE_CMD	(7 << 6)

Linus Torvalds's avatar
Linus Torvalds committed
583
static int __init read_eeprom(long ioaddr, int location)
Linus Torvalds's avatar
Linus Torvalds committed
584 585 586
{
	int i;
	unsigned short retval = 0;
Linus Torvalds's avatar
Linus Torvalds committed
587 588
	long ee_addr = ioaddr + EEPROM_Ctrl;
	long ee_daddr = ioaddr + EEPROM_Data;
Linus Torvalds's avatar
Linus Torvalds committed
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
	int read_cmd = location | EE_READ_CMD;

	/* Shift the read command bits out. */
	for (i = 9; i >= 0; i--) {
		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
		outb(EE_CS, ee_addr);
		outb(dataval, ee_daddr);
		eeprom_delay();
		outb(EE_CS | EE_SHIFT_CLK, ee_addr);	/* EEPROM clock tick. */
		eeprom_delay();
	}
	outb(EE_DATA_WRITE, ee_daddr);
	for (i = 16; i > 0; i--) {
		outb(EE_CS, ee_addr);
		eeprom_delay();
		outb(EE_CS | EE_SHIFT_CLK, ee_addr);
		eeprom_delay();
		retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0);
	}

	/* Terminate the EEPROM access. */
	outb(EE_CS, ee_addr);
	eeprom_delay();
	outb(EE_SHIFT_CLK, ee_addr);
	outb(0, ee_addr);
	return retval;
}



static int net_open(struct net_device *dev)
{
621
	struct net_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651
	int ioaddr = dev->base_addr;

	/* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
	   bus access, and two 4K Tx queues. */
	outb(0x5a, ioaddr + CONFIG_0);

	/* Powerup, switch to register bank 2, and enable the Rx and Tx. */
	outb(0xe8, ioaddr + CONFIG_1);

	lp->tx_started = 0;
	lp->tx_queue_ready = 1;
	lp->rx_started = 0;
	lp->tx_queue = 0;
	lp->tx_queue_len = 0;

	/* Turn on hardware Tx and Rx interrupts. */
	outb(0x82, ioaddr + TX_INTR);
	outb(0x81, ioaddr + RX_INTR);

	/* Enable the IRQ on boards of fmv18x it is feasible. */
	if (lp->jumpered) {
		outb(0x80, ioaddr + IOCONFIG1);
	}

	netif_start_queue(dev);
	return 0;
}

static void net_tx_timeout (struct net_device *dev)
{
652
	struct net_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
653 654 655 656 657 658
	int ioaddr = dev->base_addr;

	printk ("%s: transmit timed out with status %04x, %s?\n", dev->name,
		inw (ioaddr + STATUS), inb (ioaddr + TX_STATUS) & 0x80
		? "IRQ conflict" : "network cable problem");
	printk ("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n",
659 660 661
	 dev->name, inw(ioaddr + TX_STATUS), inw(ioaddr + TX_INTR), inw(ioaddr + TX_MODE),
		inw(ioaddr + CONFIG_0), inw(ioaddr + DATAPORT), inw(ioaddr + TX_START),
		inw(ioaddr + MODE13 - 1), inw(ioaddr + RX_CTRL));
Linus Torvalds's avatar
Linus Torvalds committed
662 663
	lp->stats.tx_errors++;
	/* ToDo: We should try to restart the adaptor... */
664
	outw(0xffff, ioaddr + MODE24);
Linus Torvalds's avatar
Linus Torvalds committed
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
	outw (0xffff, ioaddr + TX_STATUS);
	outb (0x5a, ioaddr + CONFIG_0);
	outb (0xe8, ioaddr + CONFIG_1);
	outw (0x8182, ioaddr + TX_INTR);
	outb (0x00, ioaddr + TX_START);
	outb (0x03, ioaddr + COL16CNTL);

	dev->trans_start = jiffies;

	lp->tx_started = 0;
	lp->tx_queue_ready = 1;
	lp->rx_started = 0;
	lp->tx_queue = 0;
	lp->tx_queue_len = 0;

	netif_wake_queue(dev);
}


static int net_send_packet (struct sk_buff *skb, struct net_device *dev)
{
686
	struct net_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
687 688
	int ioaddr = dev->base_addr;
	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
Alan Cox's avatar
Alan Cox committed
689
	short len = skb->len;
Linus Torvalds's avatar
Linus Torvalds committed
690
	unsigned char *buf = skb->data;
Alan Cox's avatar
Alan Cox committed
691
	static u8 pad[ETH_ZLEN];
Linus Torvalds's avatar
Linus Torvalds committed
692 693 694 695 696 697 698 699 700 701 702

	netif_stop_queue (dev);

	/* We may not start transmitting unless we finish transferring
	   a packet into the Tx queue. During executing the following
	   codes we possibly catch a Tx interrupt. Thus we flag off
	   tx_queue_ready, so that we prevent the interrupt routine
	   (net_interrupt) to start transmitting. */
	lp->tx_queue_ready = 0;
	{
		outw (length, ioaddr + DATAPORT);
Alan Cox's avatar
Alan Cox committed
703 704 705 706 707 708 709 710 711 712
		/* Packet data */
		outsw (ioaddr + DATAPORT, buf, len >> 1);
		/* Check for dribble byte */
		if (len & 1) {
			outw(skb->data[skb->len-1], ioaddr + DATAPORT);
			len++;
		}
		/* Check for packet padding */
		if (length != skb->len)
			outsw(ioaddr + DATAPORT, pad, (length - len + 1) >> 1);
Linus Torvalds's avatar
Linus Torvalds committed
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736

		lp->tx_queue++;
		lp->tx_queue_len += length + 2;
	}
	lp->tx_queue_ready = 1;

	if (lp->tx_started == 0) {
		/* If the Tx is idle, always trigger a transmit. */
		outb (0x80 | lp->tx_queue, ioaddr + TX_START);
		lp->tx_queue = 0;
		lp->tx_queue_len = 0;
		dev->trans_start = jiffies;
		lp->tx_started = 1;
		netif_start_queue (dev);
	} else if (lp->tx_queue_len < 4096 - 1502)
		/* Yes, there is room for one more packet. */
		netif_start_queue (dev);
	dev_kfree_skb (skb);

	return 0;
}

/* The typical workload of the driver:
   Handle the network interface interrupts. */
Jeff Garzik's avatar
Jeff Garzik committed
737
static irqreturn_t
Linus Torvalds's avatar
Linus Torvalds committed
738 739 740 741 742
net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	struct net_device *dev = dev_id;
	struct net_local *lp;
	int ioaddr, status;
Jeff Garzik's avatar
Jeff Garzik committed
743
	int handled = 0;
Linus Torvalds's avatar
Linus Torvalds committed
744 745 746

	if (dev == NULL) {
		printk ("at1700_interrupt(): irq %d for unknown device.\n", irq);
Jeff Garzik's avatar
Jeff Garzik committed
747
		return IRQ_NONE;
Linus Torvalds's avatar
Linus Torvalds committed
748 749 750
	}

	ioaddr = dev->base_addr;
751
	lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
	
	spin_lock (&lp->lock);
	
	status = inw(ioaddr + TX_STATUS);
	outw(status, ioaddr + TX_STATUS);

	if (net_debug > 4)
		printk("%s: Interrupt with status %04x.\n", dev->name, status);
	if (lp->rx_started == 0 &&
	    (status & 0xff00 || (inb(ioaddr + RX_MODE) & 0x40) == 0)) {
		/* Got a packet(s).
		   We cannot execute net_rx more than once at the same time for
		   the same device. During executing net_rx, we possibly catch a
		   Tx interrupt. Thus we flag on rx_started, so that we prevent
		   the interrupt routine (net_interrupt) to dive into net_rx
		   again. */
Jeff Garzik's avatar
Jeff Garzik committed
768
		handled = 1;
Linus Torvalds's avatar
Linus Torvalds committed
769 770 771 772 773 774 775
		lp->rx_started = 1;
		outb(0x00, ioaddr + RX_INTR);	/* Disable RX intr. */
		net_rx(dev);
		outb(0x81, ioaddr + RX_INTR);	/* Enable  RX intr. */
		lp->rx_started = 0;
	}
	if (status & 0x00ff) {
Jeff Garzik's avatar
Jeff Garzik committed
776
		handled = 1;
Linus Torvalds's avatar
Linus Torvalds committed
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803
		if (status & 0x02) {
			/* More than 16 collisions occurred */
			if (net_debug > 4)
				printk("%s: 16 Collision occur during Txing.\n", dev->name);
			/* Cancel sending a packet. */
			outb(0x03, ioaddr + COL16CNTL);
			lp->stats.collisions++;
		}
		if (status & 0x82) {
			lp->stats.tx_packets++;
			/* The Tx queue has any packets and is not being
			   transferred a packet from the host, start
			   transmitting. */
			if (lp->tx_queue && lp->tx_queue_ready) {
				outb(0x80 | lp->tx_queue, ioaddr + TX_START);
				lp->tx_queue = 0;
				lp->tx_queue_len = 0;
				dev->trans_start = jiffies;
				netif_wake_queue (dev);
			} else {
				lp->tx_started = 0;
				netif_wake_queue (dev);
			}
		}
	}

	spin_unlock (&lp->lock);
Jeff Garzik's avatar
Jeff Garzik committed
804
	return IRQ_RETVAL(handled);
Linus Torvalds's avatar
Linus Torvalds committed
805 806 807 808 809 810
}

/* We have a good packet(s), get it/them out of the buffers. */
static void
net_rx(struct net_device *dev)
{
811
	struct net_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
812 813 814 815 816 817 818 819 820 821 822 823
	int ioaddr = dev->base_addr;
	int boguscount = 5;

	while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
		ushort status = inw(ioaddr + DATAPORT);
		ushort pkt_len = inw(ioaddr + DATAPORT);

		if (net_debug > 4)
			printk("%s: Rxing packet mode %02x status %04x.\n",
				   dev->name, inb(ioaddr + RX_MODE), status);
#ifndef final_version
		if (status == 0) {
824
			outb(0x05, ioaddr + RX_CTRL);
Linus Torvalds's avatar
Linus Torvalds committed
825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
			break;
		}
#endif

		if ((status & 0xF0) != 0x20) {	/* There was an error. */
			lp->stats.rx_errors++;
			if (status & 0x08) lp->stats.rx_length_errors++;
			if (status & 0x04) lp->stats.rx_frame_errors++;
			if (status & 0x02) lp->stats.rx_crc_errors++;
			if (status & 0x01) lp->stats.rx_over_errors++;
		} else {
			/* Malloc up new buffer. */
			struct sk_buff *skb;

			if (pkt_len > 1550) {
				printk("%s: The AT1700 claimed a very large packet, size %d.\n",
					   dev->name, pkt_len);
				/* Prime the FIFO and then flush the packet. */
				inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
844
				outb(0x05, ioaddr + RX_CTRL);
Linus Torvalds's avatar
Linus Torvalds committed
845 846 847 848 849 850 851 852 853
				lp->stats.rx_errors++;
				break;
			}
			skb = dev_alloc_skb(pkt_len+3);
			if (skb == NULL) {
				printk("%s: Memory squeeze, dropping packet (len %d).\n",
					   dev->name, pkt_len);
				/* Prime the FIFO and then flush the packet. */
				inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
854
				outb(0x05, ioaddr + RX_CTRL);
Linus Torvalds's avatar
Linus Torvalds committed
855 856 857 858 859 860 861 862 863
				lp->stats.rx_dropped++;
				break;
			}
			skb->dev = dev;
			skb_reserve(skb,2);

			insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
			skb->protocol=eth_type_trans(skb, dev);
			netif_rx(skb);
Linus Torvalds's avatar
Linus Torvalds committed
864
			dev->last_rx = jiffies;
Linus Torvalds's avatar
Linus Torvalds committed
865
			lp->stats.rx_packets++;
Linus Torvalds's avatar
Linus Torvalds committed
866
			lp->stats.rx_bytes += pkt_len;
Linus Torvalds's avatar
Linus Torvalds committed
867 868 869 870 871 872 873 874 875 876 877 878 879 880
		}
		if (--boguscount <= 0)
			break;
	}

	/* If any worth-while packets have been received, dev_rint()
	   has done a mark_bh(NET_BH) for us and will work on them
	   when we get to the bottom-half routine. */
	{
		int i;
		for (i = 0; i < 20; i++) {
			if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40)
				break;
			inw(ioaddr + DATAPORT);				/* dummy status read */
881
			outb(0x05, ioaddr + RX_CTRL);
Linus Torvalds's avatar
Linus Torvalds committed
882 883 884 885 886 887 888 889 890 891 892 893
		}

		if (net_debug > 5)
			printk("%s: Exint Rx packet with mode %02x after %d ticks.\n",
				   dev->name, inb(ioaddr + RX_MODE), i);
	}
	return;
}

/* The inverse routine to net_open(). */
static int net_close(struct net_device *dev)
{
894
	struct net_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921
	int ioaddr = dev->base_addr;

	netif_stop_queue(dev);

	/* Set configuration register 0 to disable Tx and Rx. */
	outb(0xda, ioaddr + CONFIG_0);

	/* No statistic counters on the chip to update. */

	/* Disable the IRQ on boards of fmv18x where it is feasible. */
	if (lp->jumpered) {
		outb(0x00, ioaddr + IOCONFIG1);
		free_irq(dev->irq, dev);
	}

	/* Power-down the chip.  Green, green, green! */
	outb(0x00, ioaddr + CONFIG_1);
	return 0;
}

/* Get the current statistics.
   This may be called with the card open or closed.
   There are no on-chip counters, so this function is trivial.
*/
static struct net_device_stats *
net_get_stats(struct net_device *dev)
{
922
	struct net_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
923 924 925 926 927 928 929 930 931 932 933
	return &lp->stats;
}

/*
  Set the multicast/promiscuous mode for this adaptor.
*/

static void
set_rx_mode(struct net_device *dev)
{
	int ioaddr = dev->base_addr;
934
	struct net_local *lp = netdev_priv(dev);
Linus Torvalds's avatar
Linus Torvalds committed
935
	unsigned char mc_filter[8];		 /* Multicast hash filter */
Dave Jones's avatar
Dave Jones committed
936
	unsigned long flags;
Linus Torvalds's avatar
Linus Torvalds committed
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
	int i;

	if (dev->flags & IFF_PROMISC) {
		/* Unconditionally log net taps. */
		printk("%s: Promiscuous mode enabled.\n", dev->name);
		memset(mc_filter, 0xff, sizeof(mc_filter));
		outb(3, ioaddr + RX_MODE);	/* Enable promiscuous mode */
	} else if (dev->mc_count > MC_FILTERBREAK
			   ||  (dev->flags & IFF_ALLMULTI)) {
		/* Too many to filter perfectly -- accept all multicasts. */
		memset(mc_filter, 0xff, sizeof(mc_filter));
		outb(2, ioaddr + RX_MODE);	/* Use normal mode. */
	} else if (dev->mc_count == 0) {
		memset(mc_filter, 0x00, sizeof(mc_filter));
		outb(1, ioaddr + RX_MODE);	/* Ignore almost all multicasts. */
	} else {
		struct dev_mc_list *mclist;
		int i;

		memset(mc_filter, 0, sizeof(mc_filter));
		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
Jeff Garzik's avatar
Jeff Garzik committed
958 959 960 961 962
			 i++, mclist = mclist->next) {
			unsigned int bit =
				ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 26;
			mc_filter[bit >> 3] |= (1 << bit);
		}
sawa's avatar
sawa committed
963
		outb(0x02, ioaddr + RX_MODE);	/* Use normal mode. */
Linus Torvalds's avatar
Linus Torvalds committed
964 965
	}

966
	spin_lock_irqsave (&lp->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
967 968 969 970 971
	if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) {
		int saved_bank = inw(ioaddr + CONFIG_0);
		/* Switch to bank 1 and set the multicast table. */
		outw((saved_bank & ~0x0C00) | 0x0480, ioaddr + CONFIG_0);
		for (i = 0; i < 8; i++)
972
			outb(mc_filter[i], ioaddr + PORT_OFFSET(8 + i));
Linus Torvalds's avatar
Linus Torvalds committed
973 974 975
		memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter));
		outw(saved_bank, ioaddr + CONFIG_0);
	}
976
	spin_unlock_irqrestore (&lp->lock, flags);
Linus Torvalds's avatar
Linus Torvalds committed
977 978 979 980
	return;
}

#ifdef MODULE
Stephen Hemminger's avatar
Stephen Hemminger committed
981
static struct net_device *dev_at1700;
Linus Torvalds's avatar
Linus Torvalds committed
982 983 984 985

MODULE_PARM(io, "i");
MODULE_PARM(irq, "i");
MODULE_PARM(net_debug, "i");
Linus Torvalds's avatar
Linus Torvalds committed
986 987 988
MODULE_PARM_DESC(io, "AT1700/FMV18X I/O base address");
MODULE_PARM_DESC(irq, "AT1700/FMV18X IRQ number");
MODULE_PARM_DESC(net_debug, "AT1700/FMV18X debug level (0-6)");
Linus Torvalds's avatar
Linus Torvalds committed
989 990 991 992 993

int init_module(void)
{
	if (io == 0)
		printk("at1700: You should not use auto-probing with insmod!\n");
Stephen Hemminger's avatar
Stephen Hemminger committed
994 995 996
	dev_at1700 = at1700_probe(-1);
	if (IS_ERR(dev_at1700))
		return PTR_ERR(dev_at1700);
Linus Torvalds's avatar
Linus Torvalds committed
997 998 999 1000 1001 1002
	return 0;
}

void
cleanup_module(void)
{
Stephen Hemminger's avatar
Stephen Hemminger committed
1003 1004 1005
	unregister_netdev(dev_at1700);
	cleanup_card(dev_at1700);
	free_netdev(dev_at1700);
Linus Torvalds's avatar
Linus Torvalds committed
1006 1007
}
#endif /* MODULE */
Linus Torvalds's avatar
Linus Torvalds committed
1008 1009
MODULE_LICENSE("GPL");

Linus Torvalds's avatar
Linus Torvalds committed
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020

/*
 * Local variables:
 *  compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c"
 *  alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c"
 *  tab-width: 4
 *  c-basic-offset: 4
 *  c-indent-level: 4
 * End:
 */