summary refs log tree commit diff
path: root/drivers/net/pcmcia
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/net/pcmcia
downloadlinux-1da177e4c3f41524e886b7f1b8a0c1fc7321cac2.tar.gz
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
Diffstat (limited to 'drivers/net/pcmcia')
-rw-r--r--drivers/net/pcmcia/3c574_cs.c1307
-rw-r--r--drivers/net/pcmcia/3c589_cs.c1081
-rw-r--r--drivers/net/pcmcia/Kconfig132
-rw-r--r--drivers/net/pcmcia/Makefile16
-rw-r--r--drivers/net/pcmcia/axnet_cs.c1864
-rw-r--r--drivers/net/pcmcia/com20020_cs.c509
-rw-r--r--drivers/net/pcmcia/fmvj18x_cs.c1286
-rw-r--r--drivers/net/pcmcia/ibmtr_cs.c535
-rw-r--r--drivers/net/pcmcia/nmclan_cs.c1699
-rw-r--r--drivers/net/pcmcia/ositech.h358
-rw-r--r--drivers/net/pcmcia/pcnet_cs.c1659
-rw-r--r--drivers/net/pcmcia/smc91c92_cs.c2260
-rw-r--r--drivers/net/pcmcia/xirc2ps_cs.c2031
13 files changed, 14737 insertions, 0 deletions
diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c
new file mode 100644
index 000000000000..41e517114807
--- /dev/null
+++ b/drivers/net/pcmcia/3c574_cs.c
@@ -0,0 +1,1307 @@
+/* 3c574.c: A PCMCIA ethernet driver for the 3com 3c574 "RoadRunner".
+
+	Written 1993-1998 by
+	Donald Becker, becker@scyld.com, (driver core) and
+	David Hinds, dahinds@users.sourceforge.net (from his PC card code).
+	Locking fixes (C) Copyright 2003 Red Hat Inc
+
+	This software may be used and distributed according to the terms of
+	the GNU General Public License, incorporated herein by reference.
+
+	This driver derives from Donald Becker's 3c509 core, which has the
+	following copyright:
+	Copyright 1993 United States Government as represented by the
+	Director, National Security Agency.
+	
+
+*/
+
+/*
+				Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the 3Com 3c574 PC card Fast Ethernet
+Adapter.
+
+II. Board-specific settings
+
+None -- PC cards are autoconfigured.
+
+III. Driver operation
+
+The 3c574 uses a Boomerang-style interface, without the bus-master capability.
+See the Boomerang driver and documentation for most details.
+
+IV. Notes and chip documentation.
+
+Two added registers are used to enhance PIO performance, RunnerRdCtrl and
+RunnerWrCtrl.  These are 11 bit down-counters that are preloaded with the
+count of word (16 bits) reads or writes the driver is about to do to the Rx
+or Tx FIFO.  The chip is then able to hide the internal-PCI-bus to PC-card
+translation latency by buffering the I/O operations with an 8 word FIFO.
+Note: No other chip accesses are permitted when this buffer is used.
+
+A second enhancement is that both attribute and common memory space
+0x0800-0x0fff can translated to the PIO FIFO.  Thus memory operations (faster
+with *some* PCcard bridges) may be used instead of I/O operations.
+This is enabled by setting the 0x10 bit in the PCMCIA LAN COR.
+
+Some slow PC card bridges work better if they never see a WAIT signal.
+This is configured by setting the 0x20 bit in the PCMCIA LAN COR.
+Only do this after testing that it is reliable and improves performance.
+
+The upper five bits of RunnerRdCtrl are used to window into PCcard
+configuration space registers.  Window 0 is the regular Boomerang/Odie
+register set, 1-5 are various PC card control registers, and 16-31 are
+the (reversed!) CIS table.
+
+A final note: writing the InternalConfig register in window 3 with an
+invalid ramWidth is Very Bad.
+
+V. References
+
+http://www.scyld.com/expert/NWay.html
+http://www.national.com/pf/DP/DP83840.html
+
+Thanks to Terry Murphy of 3Com for providing development information for
+earlier 3Com products.
+
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/ethtool.h>
+#include <linux/bitops.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/mem_op.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("3Com 3c574 series PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+INT_MODULE_PARM(max_interrupt_work, 32);
+
+/* Force full duplex modes? */
+INT_MODULE_PARM(full_duplex, 0);
+
+/* Autodetect link polarity reversal? */
+INT_MODULE_PARM(auto_polarity, 1);
+
+#ifdef PCMCIA_DEBUG
+INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"3c574_cs.c 1.65ac1 2003/04/07 Donald Becker/David Hinds, becker@scyld.com.\n";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((800*HZ)/1000)
+
+/* To minimize the size of the driver source and make the driver more
+   readable not all constants are symbolically defined.
+   You'll need the manual if you want to understand driver details anyway. */
+/* Offsets from base I/O address. */
+#define EL3_DATA	0x00
+#define EL3_CMD		0x0e
+#define EL3_STATUS	0x0e
+
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+
+/* The top five bits written to EL3_CMD are a command, the lower
+   11 bits are the parameter, if applicable. */
+enum el3_cmds {
+	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+	TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+	SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
+	StatsDisable = 22<<11, StopCoax = 23<<11,
+};
+
+enum elxl_status {
+	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+	IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000 };
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
+};
+
+enum Window0 {
+	Wn0EepromCmd = 10, Wn0EepromData = 12, /* EEPROM command/address, data. */
+	IntrStatus=0x0E,		/* Valid in all windows. */
+};
+/* These assumes the larger EEPROM. */
+enum Win0_EEPROM_cmds {
+	EEPROM_Read = 0x200, EEPROM_WRITE = 0x100, EEPROM_ERASE = 0x300,
+	EEPROM_EWENB = 0x30,		/* Enable erasing/writing for 10 msec. */
+	EEPROM_EWDIS = 0x00,		/* Disable EWENB before 10 msec timeout. */
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+   On the "Odie" this window is always mapped at offsets 0x10-0x1f.
+   Except for TxFree, which is overlapped by RunnerWrCtrl. */
+enum Window1 {
+	TX_FIFO = 0x10,  RX_FIFO = 0x10,  RxErrors = 0x14,
+	RxStatus = 0x18,  Timer=0x1A, TxStatus = 0x1B,
+	TxFree = 0x0C, /* Remaining free bytes in Tx buffer. */
+	RunnerRdCtrl = 0x16, RunnerWrCtrl = 0x1c,
+};
+
+enum Window3 {			/* Window 3: MAC/config bits. */
+	Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
+};
+union wn3_config {
+	int i;
+	struct w3_config_fields {
+		unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
+		int pad8:8;
+		unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
+		int pad24:7;
+	} u;
+};
+
+enum Window4 {		/* Window 4: Xcvr/media bits. */
+	Wn4_FIFODiag = 4, Wn4_NetDiag = 6, Wn4_PhysicalMgmt=8, Wn4_Media = 10,
+};
+
+#define MEDIA_TP	0x00C0	/* Enable link beat and jabber for 10baseT. */
+
+struct el3_private {
+	dev_link_t link;
+	dev_node_t node;
+	struct net_device_stats stats;
+	u16 advertising, partner;		/* NWay media advertisement */
+	unsigned char phys;			/* MII device address */
+	unsigned int autoselect:1, default_media:3;	/* Read from the EEPROM/Wn3_Config. */
+	/* for transceiver monitoring */
+	struct timer_list media;
+	unsigned short media_status;
+	unsigned short fast_poll;
+	unsigned long last_irq;
+	spinlock_t window_lock;			/* Guards the Window selection */
+};
+
+/* Set iff a MII transceiver on any interface requires mdio preamble.
+   This only set with the original DP83840 on older 3c905 boards, so the extra
+   code size of a per-interface flag is not worthwhile. */
+static char mii_preamble_required = 0;
+
+/* Index of functions. */
+
+static void tc574_config(dev_link_t *link);
+static void tc574_release(dev_link_t *link);
+static int tc574_event(event_t event, int priority,
+					   event_callback_args_t *args);
+
+static void mdio_sync(kio_addr_t ioaddr, int bits);
+static int mdio_read(kio_addr_t ioaddr, int phy_id, int location);
+static void mdio_write(kio_addr_t ioaddr, int phy_id, int location, int value);
+static unsigned short read_eeprom(kio_addr_t ioaddr, int index);
+static void tc574_wait_for_completion(struct net_device *dev, int cmd);
+
+static void tc574_reset(struct net_device *dev);
+static void media_check(unsigned long arg);
+static int el3_open(struct net_device *dev);
+static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void update_stats(struct net_device *dev);
+static struct net_device_stats *el3_get_stats(struct net_device *dev);
+static int el3_rx(struct net_device *dev, int worklimit);
+static int el3_close(struct net_device *dev);
+static void el3_tx_timeout(struct net_device *dev);
+static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static struct ethtool_ops netdev_ethtool_ops;
+static void set_rx_mode(struct net_device *dev);
+
+static dev_info_t dev_info = "3c574_cs";
+
+static dev_link_t *tc574_attach(void);
+static void tc574_detach(dev_link_t *);
+
+static dev_link_t *dev_list;
+
+/*
+	tc574_attach() creates an "instance" of the driver, allocating
+	local data structures for one device.  The device is registered
+	with Card Services.
+*/
+
+static dev_link_t *tc574_attach(void)
+{
+	struct el3_private *lp;
+	client_reg_t client_reg;
+	dev_link_t *link;
+	struct net_device *dev;
+	int ret;
+
+	DEBUG(0, "3c574_attach()\n");
+
+	/* Create the PC card device object. */
+	dev = alloc_etherdev(sizeof(struct el3_private));
+	if (!dev)
+		return NULL;
+	lp = netdev_priv(dev);
+	link = &lp->link;
+	link->priv = dev;
+
+	spin_lock_init(&lp->window_lock);
+	link->io.NumPorts1 = 32;
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+	link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+	link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+	link->irq.Handler = &el3_interrupt;
+	link->irq.Instance = dev;
+	link->conf.Attributes = CONF_ENABLE_IRQ;
+	link->conf.Vcc = 50;
+	link->conf.IntType = INT_MEMORY_AND_IO;
+	link->conf.ConfigIndex = 1;
+	link->conf.Present = PRESENT_OPTION;
+
+	/* The EL3-specific entries in the device structure. */
+	dev->hard_start_xmit = &el3_start_xmit;
+	dev->get_stats = &el3_get_stats;
+	dev->do_ioctl = &el3_ioctl;
+	SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+	dev->set_multicast_list = &set_rx_mode;
+	dev->open = &el3_open;
+	dev->stop = &el3_close;
+#ifdef HAVE_TX_TIMEOUT
+	dev->tx_timeout = el3_tx_timeout;
+	dev->watchdog_timeo = TX_TIMEOUT;
+#endif
+
+	/* Register with Card Services */
+	link->next = dev_list;
+	dev_list = link;
+	client_reg.dev_info = &dev_info;
+	client_reg.EventMask =
+		CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+			CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+				CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+	client_reg.event_handler = &tc574_event;
+	client_reg.Version = 0x0210;
+	client_reg.event_callback_args.client_data = link;
+	ret = pcmcia_register_client(&link->handle, &client_reg);
+	if (ret != 0) {
+		cs_error(link->handle, RegisterClient, ret);
+		tc574_detach(link);
+		return NULL;
+	}
+
+	return link;
+} /* tc574_attach */
+
+/*
+
+	This deletes a driver "instance".  The device is de-registered
+	with Card Services.  If it has been released, all local data
+	structures are freed.  Otherwise, the structures will be freed
+	when the device is released.
+
+*/
+
+static void tc574_detach(dev_link_t *link)
+{
+	struct net_device *dev = link->priv;
+	dev_link_t **linkp;
+
+	DEBUG(0, "3c574_detach(0x%p)\n", link);
+
+	/* Locate device structure */
+	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+		if (*linkp == link) break;
+	if (*linkp == NULL)
+		return;
+
+	if (link->dev)
+		unregister_netdev(dev);
+
+	if (link->state & DEV_CONFIG)
+		tc574_release(link);
+
+	if (link->handle)
+		pcmcia_deregister_client(link->handle);
+
+	/* Unlink device structure, free bits */
+	*linkp = link->next;
+	free_netdev(dev);
+} /* tc574_detach */
+
+/*
+	tc574_config() is scheduled to run after a CARD_INSERTION event
+	is received, to configure the PCMCIA socket, and to make the
+	ethernet device available to the system.
+*/
+
+#define CS_CHECK(fn, ret) \
+  do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
+
+static void tc574_config(dev_link_t *link)
+{
+	client_handle_t handle = link->handle;
+	struct net_device *dev = link->priv;
+	struct el3_private *lp = netdev_priv(dev);
+	tuple_t tuple;
+	cisparse_t parse;
+	unsigned short buf[32];
+	int last_fn, last_ret, i, j;
+	kio_addr_t ioaddr;
+	u16 *phys_addr;
+	char *cardname;
+	union wn3_config config;
+
+	phys_addr = (u16 *)dev->dev_addr;
+
+	DEBUG(0, "3c574_config(0x%p)\n", link);
+
+	tuple.Attributes = 0;
+	tuple.DesiredTuple = CISTPL_CONFIG;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	tuple.TupleData = (cisdata_t *)buf;
+	tuple.TupleDataMax = 64;
+	tuple.TupleOffset = 0;
+	CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+	CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+	link->conf.ConfigBase = parse.config.base;
+	link->conf.Present = parse.config.rmask[0];
+
+	/* Configure card */
+	link->state |= DEV_CONFIG;
+
+	link->io.IOAddrLines = 16;
+	for (i = j = 0; j < 0x400; j += 0x20) {
+		link->io.BasePort1 = j ^ 0x300;
+		i = pcmcia_request_io(link->handle, &link->io);
+		if (i == CS_SUCCESS) break;
+	}
+	if (i != CS_SUCCESS) {
+		cs_error(link->handle, RequestIO, i);
+		goto failed;
+	}
+	CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+	CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+
+	dev->irq = link->irq.AssignedIRQ;
+	dev->base_addr = link->io.BasePort1;
+
+	ioaddr = dev->base_addr;
+
+	/* The 3c574 normally uses an EEPROM for configuration info, including
+	   the hardware address.  The future products may include a modem chip
+	   and put the address in the CIS. */
+	tuple.DesiredTuple = 0x88;
+	if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) {
+		pcmcia_get_tuple_data(handle, &tuple);
+		for (i = 0; i < 3; i++)
+			phys_addr[i] = htons(buf[i]);
+	} else {
+		EL3WINDOW(0);
+		for (i = 0; i < 3; i++)
+			phys_addr[i] = htons(read_eeprom(ioaddr, i + 10));
+		if (phys_addr[0] == 0x6060) {
+			printk(KERN_NOTICE "3c574_cs: IO port conflict at 0x%03lx"
+				   "-0x%03lx\n", dev->base_addr, dev->base_addr+15);
+			goto failed;
+		}
+	}
+	tuple.DesiredTuple = CISTPL_VERS_1;
+	if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS &&
+		pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS &&
+		pcmcia_parse_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
+		cardname = parse.version_1.str + parse.version_1.ofs[1];
+	} else
+		cardname = "3Com 3c574";
+
+	{
+		u_char mcr;
+		outw(2<<11, ioaddr + RunnerRdCtrl);
+		mcr = inb(ioaddr + 2);
+		outw(0<<11, ioaddr + RunnerRdCtrl);
+		printk(KERN_INFO "  ASIC rev %d,", mcr>>3);
+		EL3WINDOW(3);
+		config.i = inl(ioaddr + Wn3_Config);
+		lp->default_media = config.u.xcvr;
+		lp->autoselect = config.u.autoselect;
+	}
+
+	init_timer(&lp->media);
+
+	{
+		int phy;
+		
+		/* Roadrunner only: Turn on the MII transceiver */
+		outw(0x8040, ioaddr + Wn3_Options);
+		mdelay(1);
+		outw(0xc040, ioaddr + Wn3_Options);
+		tc574_wait_for_completion(dev, TxReset);
+		tc574_wait_for_completion(dev, RxReset);
+		mdelay(1);
+		outw(0x8040, ioaddr + Wn3_Options);
+		
+		EL3WINDOW(4);
+		for (phy = 1; phy <= 32; phy++) {
+			int mii_status;
+			mdio_sync(ioaddr, 32);
+			mii_status = mdio_read(ioaddr, phy & 0x1f, 1);
+			if (mii_status != 0xffff) {
+				lp->phys = phy & 0x1f;
+				DEBUG(0, "  MII transceiver at index %d, status %x.\n",
+					  phy, mii_status);
+				if ((mii_status & 0x0040) == 0)
+					mii_preamble_required = 1;
+				break;
+			}
+		}
+		if (phy > 32) {
+			printk(KERN_NOTICE "  No MII transceivers found!\n");
+			goto failed;
+		}
+		i = mdio_read(ioaddr, lp->phys, 16) | 0x40;
+		mdio_write(ioaddr, lp->phys, 16, i);
+		lp->advertising = mdio_read(ioaddr, lp->phys, 4);
+		if (full_duplex) {
+			/* Only advertise the FD media types. */
+			lp->advertising &= ~0x02a0;
+			mdio_write(ioaddr, lp->phys, 4, lp->advertising);
+		}
+	}
+
+	link->state &= ~DEV_CONFIG_PENDING;
+	link->dev = &lp->node;
+	SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+
+	if (register_netdev(dev) != 0) {
+		printk(KERN_NOTICE "3c574_cs: register_netdev() failed\n");
+		link->dev = NULL;
+		goto failed;
+	}
+
+	strcpy(lp->node.dev_name, dev->name);
+
+	printk(KERN_INFO "%s: %s at io %#3lx, irq %d, hw_addr ",
+		   dev->name, cardname, dev->base_addr, dev->irq);
+	for (i = 0; i < 6; i++)
+		printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : ".\n"));
+	printk(" %dK FIFO split %s Rx:Tx, %sMII interface.\n",
+		   8 << config.u.ram_size, ram_split[config.u.ram_split],
+		   config.u.autoselect ? "autoselect " : "");
+
+	return;
+
+cs_failed:
+	cs_error(link->handle, last_fn, last_ret);
+failed:
+	tc574_release(link);
+	return;
+
+} /* tc574_config */
+
+/*
+	After a card is removed, tc574_release() will unregister the net
+	device, and release the PCMCIA configuration.  If the device is
+	still open, this will be postponed until it is closed.
+*/
+
+static void tc574_release(dev_link_t *link)
+{
+	DEBUG(0, "3c574_release(0x%p)\n", link);
+
+	pcmcia_release_configuration(link->handle);
+	pcmcia_release_io(link->handle, &link->io);
+	pcmcia_release_irq(link->handle, &link->irq);
+
+	link->state &= ~DEV_CONFIG;
+}
+
+/*
+	The card status event handler.  Mostly, this schedules other
+	stuff to run after an event is received.  A CARD_REMOVAL event
+	also sets some flags to discourage the net drivers from trying
+	to talk to the card any more.
+*/
+
+static int tc574_event(event_t event, int priority,
+					   event_callback_args_t *args)
+{
+	dev_link_t *link = args->client_data;
+	struct net_device *dev = link->priv;
+
+	DEBUG(1, "3c574_event(0x%06x)\n", event);
+
+	switch (event) {
+	case CS_EVENT_CARD_REMOVAL:
+		link->state &= ~DEV_PRESENT;
+		if (link->state & DEV_CONFIG)
+			netif_device_detach(dev);
+		break;
+	case CS_EVENT_CARD_INSERTION:
+		link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+		tc574_config(link);
+		break;
+	case CS_EVENT_PM_SUSPEND:
+		link->state |= DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_RESET_PHYSICAL:
+		if (link->state & DEV_CONFIG) {
+			if (link->open)
+				netif_device_detach(dev);
+			pcmcia_release_configuration(link->handle);
+		}
+		break;
+	case CS_EVENT_PM_RESUME:
+		link->state &= ~DEV_SUSPEND;
+		/* Fall through... */
+	case CS_EVENT_CARD_RESET:
+		if (link->state & DEV_CONFIG) {
+			pcmcia_request_configuration(link->handle, &link->conf);
+			if (link->open) {
+				tc574_reset(dev);
+				netif_device_attach(dev);
+			}
+		}
+		break;
+	}
+	return 0;
+} /* tc574_event */
+
+static void dump_status(struct net_device *dev)
+{
+	kio_addr_t ioaddr = dev->base_addr;
+	EL3WINDOW(1);
+	printk(KERN_INFO "  irq status %04x, rx status %04x, tx status "
+		   "%02x, tx free %04x\n", inw(ioaddr+EL3_STATUS),
+		   inw(ioaddr+RxStatus), inb(ioaddr+TxStatus),
+		   inw(ioaddr+TxFree));
+	EL3WINDOW(4);
+	printk(KERN_INFO "  diagnostics: fifo %04x net %04x ethernet %04x"
+		   " media %04x\n", inw(ioaddr+0x04), inw(ioaddr+0x06),
+		   inw(ioaddr+0x08), inw(ioaddr+0x0a));
+	EL3WINDOW(1);
+}
+
+/*
+  Use this for commands that may take time to finish
+*/
+static void tc574_wait_for_completion(struct net_device *dev, int cmd)
+{
+	int i = 1500;
+	outw(cmd, dev->base_addr + EL3_CMD);
+	while (--i > 0)
+		if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break;
+	if (i == 0)
+		printk(KERN_NOTICE "%s: command 0x%04x did not complete!\n", dev->name, cmd);
+}
+
+/* Read a word from the EEPROM using the regular EEPROM access register.
+   Assume that we are in register window zero.
+ */
+static unsigned short read_eeprom(kio_addr_t ioaddr, int index)
+{
+	int timer;
+	outw(EEPROM_Read + index, ioaddr + Wn0EepromCmd);
+	/* Pause for at least 162 usec for the read to take place. */
+	for (timer = 1620; timer >= 0; timer--) {
+		if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
+			break;
+	}
+	return inw(ioaddr + Wn0EepromData);
+}
+
+/* MII transceiver control section.
+   Read and write the MII registers using software-generated serial
+   MDIO protocol.  See the MII specifications or DP83840A data sheet
+   for details.
+   The maxium data clock rate is 2.5 Mhz.  The timing is easily met by the
+   slow PC card interface. */
+
+#define MDIO_SHIFT_CLK	0x01
+#define MDIO_DIR_WRITE	0x04
+#define MDIO_DATA_WRITE0 (0x00 | MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1 (0x02 | MDIO_DIR_WRITE)
+#define MDIO_DATA_READ	0x02
+#define MDIO_ENB_IN		0x00
+
+/* Generate the preamble required for initial synchronization and
+   a few older transceivers. */
+static void mdio_sync(kio_addr_t ioaddr, int bits)
+{
+	kio_addr_t mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+	/* Establish sync by sending at least 32 logic ones. */
+	while (-- bits >= 0) {
+		outw(MDIO_DATA_WRITE1, mdio_addr);
+		outw(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, mdio_addr);
+	}
+}
+
+static int mdio_read(kio_addr_t ioaddr, int phy_id, int location)
+{
+	int i;
+	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+	unsigned int retval = 0;
+	kio_addr_t mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+
+	if (mii_preamble_required)
+		mdio_sync(ioaddr, 32);
+
+	/* Shift the read command bits out. */
+	for (i = 14; i >= 0; i--) {
+		int dataval = (read_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+		outw(dataval, mdio_addr);
+		outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+	}
+	/* Read the two transition, 16 data, and wire-idle bits. */
+	for (i = 19; i > 0; i--) {
+		outw(MDIO_ENB_IN, mdio_addr);
+		retval = (retval << 1) | ((inw(mdio_addr) & MDIO_DATA_READ) ? 1 : 0);
+		outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+	}
+	return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(kio_addr_t ioaddr, int phy_id, int location, int value)
+{
+	int write_cmd = 0x50020000 | (phy_id << 23) | (location << 18) | value;
+	kio_addr_t mdio_addr = ioaddr + Wn4_PhysicalMgmt;
+	int i;
+
+	if (mii_preamble_required)
+		mdio_sync(ioaddr, 32);
+
+	/* Shift the command bits out. */
+	for (i = 31; i >= 0; i--) {
+		int dataval = (write_cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+		outw(dataval, mdio_addr);
+		outw(dataval | MDIO_SHIFT_CLK, mdio_addr);
+	}
+	/* Leave the interface idle. */
+	for (i = 1; i >= 0; i--) {
+		outw(MDIO_ENB_IN, mdio_addr);
+		outw(MDIO_ENB_IN | MDIO_SHIFT_CLK, mdio_addr);
+	}
+
+	return;
+}
+
+/* Reset and restore all of the 3c574 registers. */
+static void tc574_reset(struct net_device *dev)
+{
+	struct el3_private *lp = netdev_priv(dev);
+	int i;
+	kio_addr_t ioaddr = dev->base_addr;
+	unsigned long flags;
+
+	tc574_wait_for_completion(dev, TotalReset|0x10);
+
+	spin_lock_irqsave(&lp->window_lock, flags);
+	/* Clear any transactions in progress. */
+	outw(0, ioaddr + RunnerWrCtrl);
+	outw(0, ioaddr + RunnerRdCtrl);
+
+	/* Set the station address and mask. */
+	EL3WINDOW(2);
+	for (i = 0; i < 6; i++)
+		outb(dev->dev_addr[i], ioaddr + i);
+	for (; i < 12; i+=2)
+		outw(0, ioaddr + i);
+
+	/* Reset config options */
+	EL3WINDOW(3);
+	outb((dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
+	outl((lp->autoselect ? 0x01000000 : 0) | 0x0062001b,
+		 ioaddr + Wn3_Config);
+	/* Roadrunner only: Turn on the MII transceiver. */
+	outw(0x8040, ioaddr + Wn3_Options);
+	mdelay(1);
+	outw(0xc040, ioaddr + Wn3_Options);
+	EL3WINDOW(1);
+	spin_unlock_irqrestore(&lp->window_lock, flags);
+	
+	tc574_wait_for_completion(dev, TxReset);
+	tc574_wait_for_completion(dev, RxReset);
+	mdelay(1);
+	spin_lock_irqsave(&lp->window_lock, flags);
+	EL3WINDOW(3);
+	outw(0x8040, ioaddr + Wn3_Options);
+
+	/* Switch to the stats window, and clear all stats by reading. */
+	outw(StatsDisable, ioaddr + EL3_CMD);
+	EL3WINDOW(6);
+	for (i = 0; i < 10; i++)
+		inb(ioaddr + i);
+	inw(ioaddr + 10);
+	inw(ioaddr + 12);
+	EL3WINDOW(4);
+	inb(ioaddr + 12);
+	inb(ioaddr + 13);
+
+	/* .. enable any extra statistics bits.. */
+	outw(0x0040, ioaddr + Wn4_NetDiag);
+	
+	EL3WINDOW(1);
+	spin_unlock_irqrestore(&lp->window_lock, flags);
+	
+	/* .. re-sync MII and re-fill what NWay is advertising. */
+	mdio_sync(ioaddr, 32);
+	mdio_write(ioaddr, lp->phys, 4, lp->advertising);
+	if (!auto_polarity) {
+		/* works for TDK 78Q2120 series MII's */
+		int i = mdio_read(ioaddr, lp->phys, 16) | 0x20;
+		mdio_write(ioaddr, lp->phys, 16, i);
+	}
+
+	spin_lock_irqsave(&lp->window_lock, flags);
+	/* Switch to register set 1 for normal use, just for TxFree. */
+	set_rx_mode(dev);
+	spin_unlock_irqrestore(&lp->window_lock, flags);
+	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+	/* Allow status bits to be seen. */
+	outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+	/* Ack all pending events, and set active indicator mask. */
+	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+		 ioaddr + EL3_CMD);
+	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+		 | AdapterFailure | RxEarly, ioaddr + EL3_CMD);
+}
+
+static int el3_open(struct net_device *dev)
+{
+	struct el3_private *lp = netdev_priv(dev);
+	dev_link_t *link = &lp->link;
+
+	if (!DEV_OK(link))
+		return -ENODEV;
+	
+	link->open++;
+	netif_start_queue(dev);
+	
+	tc574_reset(dev);
+	lp->media.function = &media_check;
+	lp->media.data = (unsigned long) dev;
+	lp->media.expires = jiffies + HZ;
+	add_timer(&lp->media);
+	
+	DEBUG(2, "%s: opened, status %4.4x.\n",
+		  dev->name, inw(dev->base_addr + EL3_STATUS));
+	
+	return 0;
+}
+
+static void el3_tx_timeout(struct net_device *dev)
+{
+	struct el3_private *lp = netdev_priv(dev);
+	kio_addr_t ioaddr = dev->base_addr;
+	
+	printk(KERN_NOTICE "%s: Transmit timed out!\n", dev->name);
+	dump_status(dev);
+	lp->stats.tx_errors++;
+	dev->trans_start = jiffies;
+	/* Issue TX_RESET and TX_START commands. */
+	tc574_wait_for_completion(dev, TxReset);
+	outw(TxEnable, ioaddr + EL3_CMD);
+	netif_wake_queue(dev);
+}
+
+static void pop_tx_status(struct net_device *dev)
+{
+	struct el3_private *lp = netdev_priv(dev);
+	kio_addr_t ioaddr = dev->base_addr;
+	int i;
+    
+	/* Clear the Tx status stack. */
+	for (i = 32; i > 0; i--) {
+		u_char tx_status = inb(ioaddr + TxStatus);
+		if (!(tx_status & 0x84))
+			break;
+		/* reset transmitter on jabber error or underrun */
+		if (tx_status & 0x30)
+			tc574_wait_for_completion(dev, TxReset);
+		if (tx_status & 0x38) {
+			DEBUG(1, "%s: transmit error: status 0x%02x\n",
+				  dev->name, tx_status);
+			outw(TxEnable, ioaddr + EL3_CMD);
+			lp->stats.tx_aborted_errors++;
+		}
+		outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
+	}
+}
+
+static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	kio_addr_t ioaddr = dev->base_addr;
+	struct el3_private *lp = netdev_priv(dev);
+	unsigned long flags;
+
+	DEBUG(3, "%s: el3_start_xmit(length = %ld) called, "
+		  "status %4.4x.\n", dev->name, (long)skb->len,
+		  inw(ioaddr + EL3_STATUS));
+
+	spin_lock_irqsave(&lp->window_lock, flags);
+	outw(skb->len, ioaddr + TX_FIFO);
+	outw(0, ioaddr + TX_FIFO);
+	outsl(ioaddr + TX_FIFO, skb->data, (skb->len+3)>>2);
+
+	dev->trans_start = jiffies;
+
+	/* TxFree appears only in Window 1, not offset 0x1c. */
+	if (inw(ioaddr + TxFree) <= 1536) {
+		netif_stop_queue(dev);
+		/* Interrupt us when the FIFO has room for max-sized packet. 
+		   The threshold is in units of dwords. */
+		outw(SetTxThreshold + (1536>>2), ioaddr + EL3_CMD);
+	}
+
+	pop_tx_status(dev);
+	spin_unlock_irqrestore(&lp->window_lock, flags);
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+/* The EL3 interrupt handler. */
+static irqreturn_t el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *) dev_id;
+	struct el3_private *lp = netdev_priv(dev);
+	kio_addr_t ioaddr;
+	unsigned status;
+	int work_budget = max_interrupt_work;
+	int handled = 0;
+
+	if (!netif_device_present(dev))
+		return IRQ_NONE;
+	ioaddr = dev->base_addr;
+
+	DEBUG(3, "%s: interrupt, status %4.4x.\n",
+		  dev->name, inw(ioaddr + EL3_STATUS));
+
+	spin_lock(&lp->window_lock);
+	
+	while ((status = inw(ioaddr + EL3_STATUS)) &
+		   (IntLatch | RxComplete | RxEarly | StatsFull)) {
+		if (!netif_device_present(dev) ||
+			((status & 0xe000) != 0x2000)) {
+			DEBUG(1, "%s: Interrupt from dead card\n", dev->name);
+			break;
+		}
+
+		handled = 1;
+
+		if (status & RxComplete)
+			work_budget = el3_rx(dev, work_budget);
+
+		if (status & TxAvailable) {
+			DEBUG(3, "  TX room bit was handled.\n");
+			/* There's room in the FIFO for a full-sized packet. */
+			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+			netif_wake_queue(dev);
+		}
+
+		if (status & TxComplete)
+			pop_tx_status(dev);
+
+		if (status & (AdapterFailure | RxEarly | StatsFull)) {
+			/* Handle all uncommon interrupts. */
+			if (status & StatsFull)
+				update_stats(dev);
+			if (status & RxEarly) {
+				work_budget = el3_rx(dev, work_budget);
+				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+			}
+			if (status & AdapterFailure) {
+				u16 fifo_diag;
+				EL3WINDOW(4);
+				fifo_diag = inw(ioaddr + Wn4_FIFODiag);
+				EL3WINDOW(1);
+				printk(KERN_NOTICE "%s: adapter failure, FIFO diagnostic"
+					   " register %04x.\n", dev->name, fifo_diag);
+				if (fifo_diag & 0x0400) {
+					/* Tx overrun */
+					tc574_wait_for_completion(dev, TxReset);
+					outw(TxEnable, ioaddr + EL3_CMD);
+				}
+				if (fifo_diag & 0x2000) {
+					/* Rx underrun */
+					tc574_wait_for_completion(dev, RxReset);
+					set_rx_mode(dev);
+					outw(RxEnable, ioaddr + EL3_CMD);
+				}
+				outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+			}
+		}
+
+		if (--work_budget < 0) {
+			DEBUG(0, "%s: Too much work in interrupt, "
+				  "status %4.4x.\n", dev->name, status);
+			/* Clear all interrupts */
+			outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+			break;
+		}
+		/* Acknowledge the IRQ. */
+		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+	}
+
+	DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",
+		  dev->name, inw(ioaddr + EL3_STATUS));
+		  
+	spin_unlock(&lp->window_lock);
+	return IRQ_RETVAL(handled);
+}
+
+/*
+    This timer serves two purposes: to check for missed interrupts
+	(and as a last resort, poll the NIC for events), and to monitor
+	the MII, reporting changes in cable status.
+*/
+static void media_check(unsigned long arg)
+{
+	struct net_device *dev = (struct net_device *) arg;
+	struct el3_private *lp = netdev_priv(dev);
+	kio_addr_t ioaddr = dev->base_addr;
+	unsigned long flags;
+	unsigned short /* cable, */ media, partner;
+
+	if (!netif_device_present(dev))
+		goto reschedule;
+	
+	/* Check for pending interrupt with expired latency timer: with
+	   this, we can limp along even if the interrupt is blocked */
+	if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + Timer) == 0xff)) {
+		if (!lp->fast_poll)
+			printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);
+		el3_interrupt(dev->irq, lp, NULL);
+		lp->fast_poll = HZ;
+	}
+	if (lp->fast_poll) {
+		lp->fast_poll--;
+		lp->media.expires = jiffies + 2*HZ/100;
+		add_timer(&lp->media);
+		return;
+	}
+
+	spin_lock_irqsave(&lp->window_lock, flags);
+	EL3WINDOW(4);
+	media = mdio_read(ioaddr, lp->phys, 1);
+	partner = mdio_read(ioaddr, lp->phys, 5);
+	EL3WINDOW(1);
+	
+	if (media != lp->media_status) {
+		if ((media ^ lp->media_status) & 0x0004)
+			printk(KERN_INFO "%s: %s link beat\n", dev->name,
+				   (lp->media_status & 0x0004) ? "lost" : "found");
+		if ((media ^ lp->media_status) & 0x0020) {
+			lp->partner = 0;
+			if (lp->media_status & 0x0020) {
+				printk(KERN_INFO "%s: autonegotiation restarted\n",
+					   dev->name);
+			} else if (partner) {
+				partner &= lp->advertising;
+				lp->partner = partner;
+				printk(KERN_INFO "%s: autonegotiation complete: "
+					   "%sbaseT-%cD selected\n", dev->name,
+					   ((partner & 0x0180) ? "100" : "10"),
+					   ((partner & 0x0140) ? 'F' : 'H'));
+			} else {
+				printk(KERN_INFO "%s: link partner did not autonegotiate\n",
+					   dev->name);
+			}
+
+			EL3WINDOW(3);
+			outb((partner & 0x0140 ? 0x20 : 0) |
+				 (dev->mtu > 1500 ? 0x40 : 0), ioaddr + Wn3_MAC_Ctrl);
+			EL3WINDOW(1);
+
+		}
+		if (media & 0x0010)
+			printk(KERN_INFO "%s: remote fault detected\n",
+				   dev->name);
+		if (media & 0x0002)
+			printk(KERN_INFO "%s: jabber detected\n", dev->name);
+		lp->media_status = media;
+	}
+	spin_unlock_irqrestore(&lp->window_lock, flags);
+
+reschedule:
+	lp->media.expires = jiffies + HZ;
+	add_timer(&lp->media);
+}
+
+static struct net_device_stats *el3_get_stats(struct net_device *dev)
+{
+	struct el3_private *lp = netdev_priv(dev);
+
+	if (netif_device_present(dev)) {
+		unsigned long flags;
+		spin_lock_irqsave(&lp->window_lock, flags);
+		update_stats(dev);
+		spin_unlock_irqrestore(&lp->window_lock, flags);
+	}
+	return &lp->stats;
+}
+
+/*  Update statistics.
+	Suprisingly this need not be run single-threaded, but it effectively is.
+	The counters clear when read, so the adds must merely be atomic.
+ */
+static void update_stats(struct net_device *dev)
+{
+	struct el3_private *lp = netdev_priv(dev);
+	kio_addr_t ioaddr = dev->base_addr;
+	u8 rx, tx, up;
+
+	DEBUG(2, "%s: updating the statistics.\n", dev->name);
+
+	if (inw(ioaddr+EL3_STATUS) == 0xffff) /* No card. */
+		return;
+		
+	/* Unlike the 3c509 we need not turn off stats updates while reading. */
+	/* Switch to the stats window, and read everything. */
+	EL3WINDOW(6);
+	lp->stats.tx_carrier_errors 		+= inb(ioaddr + 0);
+	lp->stats.tx_heartbeat_errors		+= inb(ioaddr + 1);
+	/* Multiple collisions. */	   	inb(ioaddr + 2);
+	lp->stats.collisions			+= inb(ioaddr + 3);
+	lp->stats.tx_window_errors		+= inb(ioaddr + 4);
+	lp->stats.rx_fifo_errors		+= inb(ioaddr + 5);
+	lp->stats.tx_packets			+= inb(ioaddr + 6);
+	up		 			 = inb(ioaddr + 9);
+	lp->stats.tx_packets			+= (up&0x30) << 4;
+	/* Rx packets   */			   inb(ioaddr + 7);
+	/* Tx deferrals */			   inb(ioaddr + 8);
+	rx		 			 = inw(ioaddr + 10);
+	tx					 = inw(ioaddr + 12);
+
+	EL3WINDOW(4);
+	/* BadSSD */				   inb(ioaddr + 12);
+	up					 = inb(ioaddr + 13);
+
+	lp->stats.tx_bytes 			+= tx + ((up & 0xf0) << 12);
+
+	EL3WINDOW(1);
+}
+
+static int el3_rx(struct net_device *dev, int worklimit)
+{
+	struct el3_private *lp = netdev_priv(dev);
+	kio_addr_t ioaddr = dev->base_addr;
+	short rx_status;
+	
+	DEBUG(3, "%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
+		  dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
+	while (!((rx_status = inw(ioaddr + RxStatus)) & 0x8000) &&
+		   (--worklimit >= 0)) {
+		if (rx_status & 0x4000) { /* Error, update stats. */
+			short error = rx_status & 0x3800;
+			lp->stats.rx_errors++;
+			switch (error) {
+			case 0x0000:	lp->stats.rx_over_errors++; break;
+			case 0x0800:	lp->stats.rx_length_errors++; break;
+			case 0x1000:	lp->stats.rx_frame_errors++; break;
+			case 0x1800:	lp->stats.rx_length_errors++; break;
+			case 0x2000:	lp->stats.rx_frame_errors++; break;
+			case 0x2800:	lp->stats.rx_crc_errors++; break;
+			}
+		} else {
+			short pkt_len = rx_status & 0x7ff;
+			struct sk_buff *skb;
+
+			skb = dev_alloc_skb(pkt_len+5);
+
+			DEBUG(3, "  Receiving packet size %d status %4.4x.\n",
+				  pkt_len, rx_status);
+			if (skb != NULL) {
+				skb->dev = dev;
+				skb_reserve(skb, 2);
+				insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
+						((pkt_len+3)>>2));
+				skb->protocol = eth_type_trans(skb, dev);
+				netif_rx(skb);
+				dev->last_rx = jiffies;
+				lp->stats.rx_packets++;
+				lp->stats.rx_bytes += pkt_len;
+			} else {
+				DEBUG(1, "%s: couldn't allocate a sk_buff of"
+					  " size %d.\n", dev->name, pkt_len);
+				lp->stats.rx_dropped++;
+			}
+		}
+		tc574_wait_for_completion(dev, RxDiscard);
+	}
+
+	return worklimit;
+}
+
+static void netdev_get_drvinfo(struct net_device *dev,
+			       struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, "3c574_cs");
+}
+
+static struct ethtool_ops netdev_ethtool_ops = {
+	.get_drvinfo		= netdev_get_drvinfo,
+};
+
+/* Provide ioctl() calls to examine the MII xcvr state. */
+static int el3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct el3_private *lp = netdev_priv(dev);
+	kio_addr_t ioaddr = dev->base_addr;
+	u16 *data = (u16 *)&rq->ifr_ifru;
+	int phy = lp->phys & 0x1f;
+
+	DEBUG(2, "%s: In ioct(%-.6s, %#4.4x) %4.4x %4.4x %4.4x %4.4x.\n",
+		  dev->name, rq->ifr_ifrn.ifrn_name, cmd,
+		  data[0], data[1], data[2], data[3]);
+
+	switch(cmd) {
+	case SIOCGMIIPHY:		/* Get the address of the PHY in use. */
+		data[0] = phy;
+	case SIOCGMIIREG:		/* Read the specified MII register. */
+		{
+			int saved_window;
+			unsigned long flags;
+
+			spin_lock_irqsave(&lp->window_lock, flags);
+			saved_window = inw(ioaddr + EL3_CMD) >> 13;
+			EL3WINDOW(4);
+			data[3] = mdio_read(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+			EL3WINDOW(saved_window);
+			spin_unlock_irqrestore(&lp->window_lock, flags);
+			return 0;
+		}
+	case SIOCSMIIREG:		/* Write the specified MII register */
+		{
+			int saved_window;
+                       unsigned long flags;
+
+			if (!capable(CAP_NET_ADMIN))
+				return -EPERM;
+			spin_lock_irqsave(&lp->window_lock, flags);
+			saved_window = inw(ioaddr + EL3_CMD) >> 13;
+			EL3WINDOW(4);
+			mdio_write(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2]);
+			EL3WINDOW(saved_window);
+			spin_unlock_irqrestore(&lp->window_lock, flags);
+			return 0;
+		}
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+/* The Odie chip has a 64 bin multicast filter, but the bit layout is not
+   documented.  Until it is we revert to receiving all multicast frames when
+   any multicast reception is desired.
+   Note: My other drivers emit a log message whenever promiscuous mode is
+   entered to help detect password sniffers.  This is less desirable on
+   typical PC card machines, so we omit the message.
+   */
+
+static void set_rx_mode(struct net_device *dev)
+{
+	kio_addr_t ioaddr = dev->base_addr;
+
+	if (dev->flags & IFF_PROMISC)
+		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+			 ioaddr + EL3_CMD);
+	else if (dev->mc_count || (dev->flags & IFF_ALLMULTI))
+		outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+	else
+		outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+}
+
+static int el3_close(struct net_device *dev)
+{
+	kio_addr_t ioaddr = dev->base_addr;
+	struct el3_private *lp = netdev_priv(dev);
+	dev_link_t *link = &lp->link;
+
+	DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
+	
+	if (DEV_OK(link)) {
+		unsigned long flags;
+
+		/* Turn off statistics ASAP.  We update lp->stats below. */
+		outw(StatsDisable, ioaddr + EL3_CMD);
+		
+		/* Disable the receiver and transmitter. */
+		outw(RxDisable, ioaddr + EL3_CMD);
+		outw(TxDisable, ioaddr + EL3_CMD);
+		
+		/* Note: Switching to window 0 may disable the IRQ. */
+		EL3WINDOW(0);
+		spin_lock_irqsave(&lp->window_lock, flags);
+		update_stats(dev);
+		spin_unlock_irqrestore(&lp->window_lock, flags);
+	}
+
+	link->open--;
+	netif_stop_queue(dev);
+	del_timer_sync(&lp->media);
+
+	return 0;
+}
+
+static struct pcmcia_driver tc574_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "3c574_cs",
+	},
+	.attach		= tc574_attach,
+	.detach		= tc574_detach,
+};
+
+static int __init init_tc574(void)
+{
+	return pcmcia_register_driver(&tc574_driver);
+}
+
+static void __exit exit_tc574(void)
+{
+	pcmcia_unregister_driver(&tc574_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_tc574);
+module_exit(exit_tc574);
diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c
new file mode 100644
index 000000000000..89abdda1d343
--- /dev/null
+++ b/drivers/net/pcmcia/3c589_cs.c
@@ -0,0 +1,1081 @@
+/*======================================================================
+
+    A PCMCIA ethernet driver for the 3com 3c589 card.
+    
+    Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
+
+    3c589_cs.c 1.162 2001/10/13 00:08:50
+
+    The network driver code is based on Donald Becker's 3c589 code:
+    
+    Written 1994 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 of the GNU General Public License,
+    incorporated herein by reference.
+    Donald Becker may be reached at becker@scyld.com
+    
+    Updated for 2.5.x by Alan Cox <alan@redhat.com>
+
+======================================================================*/
+
+#define DRV_NAME	"3c589_cs"
+#define DRV_VERSION	"1.162-ac"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/bitops.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+/* To minimize the size of the driver source I only define operating
+   constants if they are used several times.  You'll need the manual
+   if you want to understand driver details. */
+/* Offsets from base I/O address. */
+#define EL3_DATA	0x00
+#define EL3_TIMER	0x0a
+#define EL3_CMD		0x0e
+#define EL3_STATUS	0x0e
+
+#define EEPROM_READ	0x0080
+#define EEPROM_BUSY	0x8000
+
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+
+/* The top five bits written to EL3_CMD are a command, the lower
+   11 bits are the parameter, if applicable. */
+enum c509cmd {
+    TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+    RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+    TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+    FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+    SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+    SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
+    StatsDisable = 22<<11, StopCoax = 23<<11,
+};
+
+enum c509status {
+    IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+    TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+    IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000
+};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+    RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
+};
+
+/* Register window 1 offsets, the window used in normal operation. */
+#define TX_FIFO		0x00
+#define RX_FIFO		0x00
+#define RX_STATUS 	0x08
+#define TX_STATUS 	0x0B
+#define TX_FREE		0x0C	/* Remaining free bytes in Tx buffer. */
+
+#define WN0_IRQ		0x08	/* Window 0: Set IRQ line in bits 12-15. */
+#define WN4_MEDIA	0x0A	/* Window 4: Various transcvr/media bits. */
+#define MEDIA_TP	0x00C0	/* Enable link beat and jabber for 10baseT. */
+#define MEDIA_LED	0x0001	/* Enable link light on 3C589E cards. */
+
+/* Time in jiffies before concluding Tx hung */
+#define TX_TIMEOUT	((400*HZ)/1000)
+
+struct el3_private {
+    dev_link_t		link;
+    dev_node_t 		node;
+    struct net_device_stats stats;
+    /* For transceiver monitoring */
+    struct timer_list	media;
+    u16			media_status;
+    u16			fast_poll;
+    unsigned long	last_irq;
+    spinlock_t		lock;
+};
+
+static char *if_names[] = { "auto", "10baseT", "10base2", "AUI" };
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("3Com 3c589 series PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+/* Special hook for setting if_port when module is loaded */
+INT_MODULE_PARM(if_port, 0);
+
+#ifdef PCMCIA_DEBUG
+INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+DRV_NAME ".c " DRV_VERSION " 2001/10/13 00:08:50 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+static void tc589_config(dev_link_t *link);
+static void tc589_release(dev_link_t *link);
+static int tc589_event(event_t event, int priority,
+		       event_callback_args_t *args);
+
+static u16 read_eeprom(kio_addr_t ioaddr, int index);
+static void tc589_reset(struct net_device *dev);
+static void media_check(unsigned long arg);
+static int el3_config(struct net_device *dev, struct ifmap *map);
+static int el3_open(struct net_device *dev);
+static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t el3_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void update_stats(struct net_device *dev);
+static struct net_device_stats *el3_get_stats(struct net_device *dev);
+static int el3_rx(struct net_device *dev);
+static int el3_close(struct net_device *dev);
+static void el3_tx_timeout(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static struct ethtool_ops netdev_ethtool_ops;
+
+static dev_info_t dev_info = "3c589_cs";
+
+static dev_link_t *tc589_attach(void);
+static void tc589_detach(dev_link_t *);
+
+static dev_link_t *dev_list;
+
+/*======================================================================
+
+    tc589_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+======================================================================*/
+
+static dev_link_t *tc589_attach(void)
+{
+    struct el3_private *lp;
+    client_reg_t client_reg;
+    dev_link_t *link;
+    struct net_device *dev;
+    int ret;
+
+    DEBUG(0, "3c589_attach()\n");
+    
+    /* Create new ethernet device */
+    dev = alloc_etherdev(sizeof(struct el3_private));
+    if (!dev)
+	 return NULL;
+    lp = netdev_priv(dev);
+    link = &lp->link;
+    link->priv = dev;
+
+    spin_lock_init(&lp->lock);
+    link->io.NumPorts1 = 16;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->irq.Handler = &el3_interrupt;
+    link->irq.Instance = dev;
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.ConfigIndex = 1;
+    link->conf.Present = PRESENT_OPTION;
+    
+    /* The EL3-specific entries in the device structure. */
+    SET_MODULE_OWNER(dev);
+    dev->hard_start_xmit = &el3_start_xmit;
+    dev->set_config = &el3_config;
+    dev->get_stats = &el3_get_stats;
+    dev->set_multicast_list = &set_multicast_list;
+    dev->open = &el3_open;
+    dev->stop = &el3_close;
+#ifdef HAVE_TX_TIMEOUT
+    dev->tx_timeout = el3_tx_timeout;
+    dev->watchdog_timeo = TX_TIMEOUT;
+#endif
+    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &tc589_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != 0) {
+	cs_error(link->handle, RegisterClient, ret);
+	tc589_detach(link);
+	return NULL;
+    }
+    
+    return link;
+} /* tc589_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void tc589_detach(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    dev_link_t **linkp;
+    
+    DEBUG(0, "3c589_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    if (link->dev)
+	unregister_netdev(dev);
+
+    if (link->state & DEV_CONFIG)
+	tc589_release(link);
+    
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+    
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    free_netdev(dev);
+} /* tc589_detach */
+
+/*======================================================================
+
+    tc589_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    ethernet device available to the system.
+    
+======================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void tc589_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    struct el3_private *lp = netdev_priv(dev);
+    tuple_t tuple;
+    cisparse_t parse;
+    u16 buf[32], *phys_addr;
+    int last_fn, last_ret, i, j, multi = 0, fifo;
+    kio_addr_t ioaddr;
+    char *ram_split[] = {"5:3", "3:1", "1:1", "3:5"};
+    
+    DEBUG(0, "3c589_config(0x%p)\n", link);
+
+    phys_addr = (u16 *)dev->dev_addr;
+    tuple.Attributes = 0;
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.TupleOffset = 0;
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+    
+    /* Is this a 3c562? */
+    tuple.DesiredTuple = CISTPL_MANFID;
+    tuple.Attributes = TUPLE_RETURN_COMMON;
+    if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) &&
+	(pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS)) {
+	if (le16_to_cpu(buf[0]) != MANFID_3COM)
+	    printk(KERN_INFO "3c589_cs: hmmm, is this really a "
+		   "3Com card??\n");
+	multi = (le16_to_cpu(buf[1]) == PRODID_3COM_3C562);
+    }
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    /* For the 3c562, the base address must be xx00-xx7f */
+    link->io.IOAddrLines = 16;
+    for (i = j = 0; j < 0x400; j += 0x10) {
+	if (multi && (j & 0x80)) continue;
+	link->io.BasePort1 = j ^ 0x300;
+	i = pcmcia_request_io(link->handle, &link->io);
+	if (i == CS_SUCCESS) break;
+    }
+    if (i != CS_SUCCESS) {
+	cs_error(link->handle, RequestIO, i);
+	goto failed;
+    }
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+	
+    dev->irq = link->irq.AssignedIRQ;
+    dev->base_addr = link->io.BasePort1;
+    ioaddr = dev->base_addr;
+    EL3WINDOW(0);
+
+    /* The 3c589 has an extra EEPROM for configuration info, including
+       the hardware address.  The 3c562 puts the address in the CIS. */
+    tuple.DesiredTuple = 0x88;
+    if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) {
+	pcmcia_get_tuple_data(handle, &tuple);
+	for (i = 0; i < 3; i++)
+	    phys_addr[i] = htons(buf[i]);
+    } else {
+	for (i = 0; i < 3; i++)
+	    phys_addr[i] = htons(read_eeprom(ioaddr, i));
+	if (phys_addr[0] == 0x6060) {
+	    printk(KERN_ERR "3c589_cs: IO port conflict at 0x%03lx"
+		   "-0x%03lx\n", dev->base_addr, dev->base_addr+15);
+	    goto failed;
+	}
+    }
+
+    /* The address and resource configuration register aren't loaded from
+       the EEPROM and *must* be set to 0 and IRQ3 for the PCMCIA version. */
+    outw(0x3f00, ioaddr + 8);
+    fifo = inl(ioaddr);
+
+    /* The if_port symbol can be set when the module is loaded */
+    if ((if_port >= 0) && (if_port <= 3))
+	dev->if_port = if_port;
+    else
+	printk(KERN_ERR "3c589_cs: invalid if_port requested\n");
+    
+    link->dev = &lp->node;
+    link->state &= ~DEV_CONFIG_PENDING;
+    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+
+    if (register_netdev(dev) != 0) {
+	printk(KERN_ERR "3c589_cs: register_netdev() failed\n");
+	link->dev = NULL;
+	goto failed;
+    }
+
+    strcpy(lp->node.dev_name, dev->name);
+
+    printk(KERN_INFO "%s: 3Com 3c%s, io %#3lx, irq %d, hw_addr ",
+	   dev->name, (multi ? "562" : "589"), dev->base_addr,
+	   dev->irq);
+    for (i = 0; i < 6; i++)
+	printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+    printk(KERN_INFO "  %dK FIFO split %s Rx:Tx, %s xcvr\n",
+	   (fifo & 7) ? 32 : 8, ram_split[(fifo >> 16) & 3],
+	   if_names[dev->if_port]);
+    return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    tc589_release(link);
+    return;
+    
+} /* tc589_config */
+
+/*======================================================================
+
+    After a card is removed, tc589_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+    
+======================================================================*/
+
+static void tc589_release(dev_link_t *link)
+{
+    DEBUG(0, "3c589_release(0x%p)\n", link);
+    
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    
+    link->state &= ~DEV_CONFIG;
+}
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+    
+======================================================================*/
+
+static int tc589_event(event_t event, int priority,
+		       event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    struct net_device *dev = link->priv;
+    
+    DEBUG(1, "3c589_event(0x%06x)\n", event);
+    
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG)
+	    netif_device_detach(dev);
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	tc589_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG) {
+	    if (link->open)
+		netif_device_detach(dev);
+	    pcmcia_release_configuration(link->handle);
+	}
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG) {
+	    pcmcia_request_configuration(link->handle, &link->conf);
+	    if (link->open) {
+		tc589_reset(dev);
+		netif_device_attach(dev);
+	    }
+	}
+	break;
+    }
+    return 0;
+} /* tc589_event */
+
+/*====================================================================*/
+
+/*
+  Use this for commands that may take time to finish
+*/
+static void tc589_wait_for_completion(struct net_device *dev, int cmd)
+{
+    int i = 100;
+    outw(cmd, dev->base_addr + EL3_CMD);
+    while (--i > 0)
+	if (!(inw(dev->base_addr + EL3_STATUS) & 0x1000)) break;
+    if (i == 0)
+	printk(KERN_WARNING "%s: command 0x%04x did not complete!\n",
+	       dev->name, cmd);
+}
+
+/*
+  Read a word from the EEPROM using the regular EEPROM access register.
+  Assume that we are in register window zero.
+*/
+static u16 read_eeprom(kio_addr_t ioaddr, int index)
+{
+    int i;
+    outw(EEPROM_READ + index, ioaddr + 10);
+    /* Reading the eeprom takes 162 us */
+    for (i = 1620; i >= 0; i--)
+	if ((inw(ioaddr + 10) & EEPROM_BUSY) == 0)
+	    break;
+    return inw(ioaddr + 12);
+}
+
+/*
+  Set transceiver type, perhaps to something other than what the user
+  specified in dev->if_port.
+*/
+static void tc589_set_xcvr(struct net_device *dev, int if_port)
+{
+    struct el3_private *lp = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    
+    EL3WINDOW(0);
+    switch (if_port) {
+    case 0: case 1: outw(0, ioaddr + 6); break;
+    case 2: outw(3<<14, ioaddr + 6); break;
+    case 3: outw(1<<14, ioaddr + 6); break;
+    }
+    /* On PCMCIA, this just turns on the LED */
+    outw((if_port == 2) ? StartCoax : StopCoax, ioaddr + EL3_CMD);
+    /* 10baseT interface, enable link beat and jabber check. */
+    EL3WINDOW(4);
+    outw(MEDIA_LED | ((if_port < 2) ? MEDIA_TP : 0), ioaddr + WN4_MEDIA);
+    EL3WINDOW(1);
+    if (if_port == 2)
+	lp->media_status = ((dev->if_port == 0) ? 0x8000 : 0x4000);
+    else
+	lp->media_status = ((dev->if_port == 0) ? 0x4010 : 0x8800);
+}
+
+static void dump_status(struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+    EL3WINDOW(1);
+    printk(KERN_INFO "  irq status %04x, rx status %04x, tx status "
+	   "%02x  tx free %04x\n", inw(ioaddr+EL3_STATUS),
+	   inw(ioaddr+RX_STATUS), inb(ioaddr+TX_STATUS),
+	   inw(ioaddr+TX_FREE));
+    EL3WINDOW(4);
+    printk(KERN_INFO "  diagnostics: fifo %04x net %04x ethernet %04x"
+	   " media %04x\n", inw(ioaddr+0x04), inw(ioaddr+0x06),
+	   inw(ioaddr+0x08), inw(ioaddr+0x0a));
+    EL3WINDOW(1);
+}
+
+/* Reset and restore all of the 3c589 registers. */
+static void tc589_reset(struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+    int i;
+    
+    EL3WINDOW(0);
+    outw(0x0001, ioaddr + 4);			/* Activate board. */ 
+    outw(0x3f00, ioaddr + 8);			/* Set the IRQ line. */
+    
+    /* Set the station address in window 2. */
+    EL3WINDOW(2);
+    for (i = 0; i < 6; i++)
+	outb(dev->dev_addr[i], ioaddr + i);
+
+    tc589_set_xcvr(dev, dev->if_port);
+    
+    /* Switch to the stats window, and clear all stats by reading. */
+    outw(StatsDisable, ioaddr + EL3_CMD);
+    EL3WINDOW(6);
+    for (i = 0; i < 9; i++)
+	inb(ioaddr+i);
+    inw(ioaddr + 10);
+    inw(ioaddr + 12);
+    
+    /* Switch to register set 1 for normal use. */
+    EL3WINDOW(1);
+
+    /* Accept b-cast and phys addr only. */
+    outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+    outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+    outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+    outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+    /* Allow status bits to be seen. */
+    outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+    /* Ack all pending events, and set active indicator mask. */
+    outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+	 ioaddr + EL3_CMD);
+    outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+	 | AdapterFailure, ioaddr + EL3_CMD);
+}
+
+static void netdev_get_drvinfo(struct net_device *dev,
+			       struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+	sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr);
+}
+
+#ifdef PCMCIA_DEBUG
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+	return pc_debug;
+}
+
+static void netdev_set_msglevel(struct net_device *dev, u32 level)
+{
+	pc_debug = level;
+}
+#endif /* PCMCIA_DEBUG */
+
+static struct ethtool_ops netdev_ethtool_ops = {
+	.get_drvinfo		= netdev_get_drvinfo,
+#ifdef PCMCIA_DEBUG
+	.get_msglevel		= netdev_get_msglevel,
+	.set_msglevel		= netdev_set_msglevel,
+#endif /* PCMCIA_DEBUG */
+};
+
+static int el3_config(struct net_device *dev, struct ifmap *map)
+{
+    if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+	if (map->port <= 3) {
+	    dev->if_port = map->port;
+	    printk(KERN_INFO "%s: switched to %s port\n",
+		   dev->name, if_names[dev->if_port]);
+	    tc589_set_xcvr(dev, dev->if_port);
+	} else
+	    return -EINVAL;
+    }
+    return 0;
+}
+
+static int el3_open(struct net_device *dev)
+{
+    struct el3_private *lp = netdev_priv(dev);
+    dev_link_t *link = &lp->link;
+    
+    if (!DEV_OK(link))
+	return -ENODEV;
+
+    link->open++;
+    netif_start_queue(dev);
+    
+    tc589_reset(dev);
+    init_timer(&lp->media);
+    lp->media.function = &media_check;
+    lp->media.data = (unsigned long) dev;
+    lp->media.expires = jiffies + HZ;
+    add_timer(&lp->media);
+
+    DEBUG(1, "%s: opened, status %4.4x.\n",
+	  dev->name, inw(dev->base_addr + EL3_STATUS));
+    
+    return 0;
+}
+
+static void el3_tx_timeout(struct net_device *dev)
+{
+    struct el3_private *lp = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    
+    printk(KERN_WARNING "%s: Transmit timed out!\n", dev->name);
+    dump_status(dev);
+    lp->stats.tx_errors++;
+    dev->trans_start = jiffies;
+    /* Issue TX_RESET and TX_START commands. */
+    tc589_wait_for_completion(dev, TxReset);
+    outw(TxEnable, ioaddr + EL3_CMD);
+    netif_wake_queue(dev);
+}
+
+static void pop_tx_status(struct net_device *dev)
+{
+    struct el3_private *lp = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    int i;
+    
+    /* Clear the Tx status stack. */
+    for (i = 32; i > 0; i--) {
+	u_char tx_status = inb(ioaddr + TX_STATUS);
+	if (!(tx_status & 0x84)) break;
+	/* reset transmitter on jabber error or underrun */
+	if (tx_status & 0x30)
+	    tc589_wait_for_completion(dev, TxReset);
+	if (tx_status & 0x38) {
+	    DEBUG(1, "%s: transmit error: status 0x%02x\n",
+		  dev->name, tx_status);
+	    outw(TxEnable, ioaddr + EL3_CMD);
+	    lp->stats.tx_aborted_errors++;
+	}
+	outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
+    }
+}
+
+static int el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+    struct el3_private *priv = netdev_priv(dev);
+
+    DEBUG(3, "%s: el3_start_xmit(length = %ld) called, "
+	  "status %4.4x.\n", dev->name, (long)skb->len,
+	  inw(ioaddr + EL3_STATUS));
+
+    priv->stats.tx_bytes += skb->len;
+
+    /* Put out the doubleword header... */
+    outw(skb->len, ioaddr + TX_FIFO);
+    outw(0x00, ioaddr + TX_FIFO);
+    /* ... and the packet rounded to a doubleword. */
+    outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+
+    dev->trans_start = jiffies;
+    if (inw(ioaddr + TX_FREE) <= 1536) {
+	netif_stop_queue(dev);
+	/* Interrupt us when the FIFO has room for max-sized packet. */
+	outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+    }
+
+    dev_kfree_skb(skb);
+    pop_tx_status(dev);
+    
+    return 0;
+}
+
+/* The EL3 interrupt handler. */
+static irqreturn_t el3_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct net_device *dev = (struct net_device *) dev_id;
+    struct el3_private *lp = netdev_priv(dev);
+    kio_addr_t ioaddr;
+    __u16 status;
+    int i = 0, handled = 1;
+    
+    if (!netif_device_present(dev))
+	return IRQ_NONE;
+
+    ioaddr = dev->base_addr;
+
+    DEBUG(3, "%s: interrupt, status %4.4x.\n",
+	  dev->name, inw(ioaddr + EL3_STATUS));
+
+    spin_lock(&lp->lock);    
+    while ((status = inw(ioaddr + EL3_STATUS)) &
+	(IntLatch | RxComplete | StatsFull)) {
+	if ((status & 0xe000) != 0x2000) {
+	    DEBUG(1, "%s: interrupt from dead card\n", dev->name);
+	    handled = 0;
+	    break;
+	}
+	
+	if (status & RxComplete)
+	    el3_rx(dev);
+	
+	if (status & TxAvailable) {
+	    DEBUG(3, "    TX room bit was handled.\n");
+	    /* There's room in the FIFO for a full-sized packet. */
+	    outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+	    netif_wake_queue(dev);
+	}
+	
+	if (status & TxComplete)
+	    pop_tx_status(dev);
+
+	if (status & (AdapterFailure | RxEarly | StatsFull)) {
+	    /* Handle all uncommon interrupts. */
+	    if (status & StatsFull)		/* Empty statistics. */
+		update_stats(dev);
+	    if (status & RxEarly) {		/* Rx early is unused. */
+		el3_rx(dev);
+		outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+	    }
+	    if (status & AdapterFailure) {
+		u16 fifo_diag;
+		EL3WINDOW(4);
+		fifo_diag = inw(ioaddr + 4);
+		EL3WINDOW(1);
+		printk(KERN_WARNING "%s: adapter failure, FIFO diagnostic"
+		       " register %04x.\n", dev->name, fifo_diag);
+		if (fifo_diag & 0x0400) {
+		    /* Tx overrun */
+		    tc589_wait_for_completion(dev, TxReset);
+		    outw(TxEnable, ioaddr + EL3_CMD);
+		}
+		if (fifo_diag & 0x2000) {
+		    /* Rx underrun */
+		    tc589_wait_for_completion(dev, RxReset);
+		    set_multicast_list(dev);
+		    outw(RxEnable, ioaddr + EL3_CMD);
+		}
+		outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+	    }
+	}
+	
+	if (++i > 10) {
+	    printk(KERN_ERR "%s: infinite loop in interrupt, "
+		   "status %4.4x.\n", dev->name, status);
+	    /* Clear all interrupts */
+	    outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+	    break;
+	}
+	/* Acknowledge the IRQ. */
+	outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+    }
+
+    lp->last_irq = jiffies;
+    spin_unlock(&lp->lock);    
+    DEBUG(3, "%s: exiting interrupt, status %4.4x.\n",
+	  dev->name, inw(ioaddr + EL3_STATUS));
+    return IRQ_RETVAL(handled);
+}
+
+static void media_check(unsigned long arg)
+{
+    struct net_device *dev = (struct net_device *)(arg);
+    struct el3_private *lp = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    u16 media, errs;
+    unsigned long flags;
+
+    if (!netif_device_present(dev)) goto reschedule;
+
+    EL3WINDOW(1);
+    /* Check for pending interrupt with expired latency timer: with
+       this, we can limp along even if the interrupt is blocked */
+    if ((inw(ioaddr + EL3_STATUS) & IntLatch) &&
+	(inb(ioaddr + EL3_TIMER) == 0xff)) {
+	if (!lp->fast_poll)
+	    printk(KERN_WARNING "%s: interrupt(s) dropped!\n", dev->name);
+	el3_interrupt(dev->irq, lp, NULL);
+	lp->fast_poll = HZ;
+    }
+    if (lp->fast_poll) {
+	lp->fast_poll--;
+	lp->media.expires = jiffies + HZ/100;
+	add_timer(&lp->media);
+	return;
+    }
+
+    /* lp->lock guards the EL3 window. Window should always be 1 except
+       when the lock is held */
+    spin_lock_irqsave(&lp->lock, flags);    
+    EL3WINDOW(4);
+    media = inw(ioaddr+WN4_MEDIA) & 0xc810;
+
+    /* Ignore collisions unless we've had no irq's recently */
+    if (jiffies - lp->last_irq < HZ) {
+	media &= ~0x0010;
+    } else {
+	/* Try harder to detect carrier errors */
+	EL3WINDOW(6);
+	outw(StatsDisable, ioaddr + EL3_CMD);
+	errs = inb(ioaddr + 0);
+	outw(StatsEnable, ioaddr + EL3_CMD);
+	lp->stats.tx_carrier_errors += errs;
+	if (errs || (lp->media_status & 0x0010)) media |= 0x0010;
+    }
+
+    if (media != lp->media_status) {
+	if ((media & lp->media_status & 0x8000) &&
+	    ((lp->media_status ^ media) & 0x0800))
+	    printk(KERN_INFO "%s: %s link beat\n", dev->name,
+		   (lp->media_status & 0x0800 ? "lost" : "found"));
+	else if ((media & lp->media_status & 0x4000) &&
+		 ((lp->media_status ^ media) & 0x0010))
+	    printk(KERN_INFO "%s: coax cable %s\n", dev->name,
+		   (lp->media_status & 0x0010 ? "ok" : "problem"));
+	if (dev->if_port == 0) {
+	    if (media & 0x8000) {
+		if (media & 0x0800)
+		    printk(KERN_INFO "%s: flipped to 10baseT\n",
+			   dev->name);
+		else
+		    tc589_set_xcvr(dev, 2);
+	    } else if (media & 0x4000) {
+		if (media & 0x0010)
+		    tc589_set_xcvr(dev, 1);
+		else
+		    printk(KERN_INFO "%s: flipped to 10base2\n",
+			   dev->name);
+	    }
+	}
+	lp->media_status = media;
+    }
+    
+    EL3WINDOW(1);
+    spin_unlock_irqrestore(&lp->lock, flags);    
+
+reschedule:
+    lp->media.expires = jiffies + HZ;
+    add_timer(&lp->media);
+}
+
+static struct net_device_stats *el3_get_stats(struct net_device *dev)
+{
+    struct el3_private *lp = netdev_priv(dev);
+    unsigned long flags;
+    dev_link_t *link = &lp->link;
+
+    if (DEV_OK(link)) {
+    	spin_lock_irqsave(&lp->lock, flags);
+	update_stats(dev);
+	spin_unlock_irqrestore(&lp->lock, flags);
+    }
+    return &lp->stats;
+}
+
+/*
+  Update statistics.  We change to register window 6, so this should be run
+  single-threaded if the device is active. This is expected to be a rare
+  operation, and it's simpler for the rest of the driver to assume that
+  window 1 is always valid rather than use a special window-state variable.
+  
+  Caller must hold the lock for this
+*/
+static void update_stats(struct net_device *dev)
+{
+    struct el3_private *lp = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+
+    DEBUG(2, "%s: updating the statistics.\n", dev->name);
+    /* Turn off statistics updates while reading. */
+    outw(StatsDisable, ioaddr + EL3_CMD);
+    /* Switch to the stats window, and read everything. */
+    EL3WINDOW(6);
+    lp->stats.tx_carrier_errors 	+= inb(ioaddr + 0);
+    lp->stats.tx_heartbeat_errors	+= inb(ioaddr + 1);
+    /* Multiple collisions. */	   	inb(ioaddr + 2);
+    lp->stats.collisions		+= inb(ioaddr + 3);
+    lp->stats.tx_window_errors		+= inb(ioaddr + 4);
+    lp->stats.rx_fifo_errors		+= inb(ioaddr + 5);
+    lp->stats.tx_packets		+= inb(ioaddr + 6);
+    /* Rx packets   */			inb(ioaddr + 7);
+    /* Tx deferrals */			inb(ioaddr + 8);
+    /* Rx octets */			inw(ioaddr + 10);
+    /* Tx octets */			inw(ioaddr + 12);
+    
+    /* Back to window 1, and turn statistics back on. */
+    EL3WINDOW(1);
+    outw(StatsEnable, ioaddr + EL3_CMD);
+}
+
+static int el3_rx(struct net_device *dev)
+{
+    struct el3_private *lp = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    int worklimit = 32;
+    short rx_status;
+    
+    DEBUG(3, "%s: in rx_packet(), status %4.4x, rx_status %4.4x.\n",
+	  dev->name, inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
+    while (!((rx_status = inw(ioaddr + RX_STATUS)) & 0x8000) &&
+	   (--worklimit >= 0)) {
+	if (rx_status & 0x4000) { /* Error, update stats. */
+	    short error = rx_status & 0x3800;
+	    lp->stats.rx_errors++;
+	    switch (error) {
+	    case 0x0000:	lp->stats.rx_over_errors++; break;
+	    case 0x0800:	lp->stats.rx_length_errors++; break;
+	    case 0x1000:	lp->stats.rx_frame_errors++; break;
+	    case 0x1800:	lp->stats.rx_length_errors++; break;
+	    case 0x2000:	lp->stats.rx_frame_errors++; break;
+	    case 0x2800:	lp->stats.rx_crc_errors++; break;
+	    }
+	} else {
+	    short pkt_len = rx_status & 0x7ff;
+	    struct sk_buff *skb;
+	    
+	    skb = dev_alloc_skb(pkt_len+5);
+	    
+	    DEBUG(3, "    Receiving packet size %d status %4.4x.\n",
+		  pkt_len, rx_status);
+	    if (skb != NULL) {
+		skb->dev = dev;
+		skb_reserve(skb, 2);
+		insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len),
+			(pkt_len+3)>>2);
+		skb->protocol = eth_type_trans(skb, dev);
+		netif_rx(skb);
+		dev->last_rx = jiffies;
+		lp->stats.rx_packets++;
+		lp->stats.rx_bytes += pkt_len;
+	    } else {
+		DEBUG(1, "%s: couldn't allocate a sk_buff of"
+		      " size %d.\n", dev->name, pkt_len);
+		lp->stats.rx_dropped++;
+	    }
+	}
+	/* Pop the top of the Rx FIFO */
+	tc589_wait_for_completion(dev, RxDiscard);
+    }
+    if (worklimit == 0)
+	printk(KERN_WARNING "%s: too much work in el3_rx!\n", dev->name);
+    return 0;
+}
+
+static void set_multicast_list(struct net_device *dev)
+{
+    struct el3_private *lp = netdev_priv(dev);
+    dev_link_t *link = &lp->link;
+    kio_addr_t ioaddr = dev->base_addr;
+    u16 opts = SetRxFilter | RxStation | RxBroadcast;
+
+    if (!(DEV_OK(link))) return;
+    if (dev->flags & IFF_PROMISC)
+	opts |= RxMulticast | RxProm;
+    else if (dev->mc_count || (dev->flags & IFF_ALLMULTI))
+	opts |= RxMulticast;
+    outw(opts, ioaddr + EL3_CMD);
+}
+
+static int el3_close(struct net_device *dev)
+{
+    struct el3_private *lp = netdev_priv(dev);
+    dev_link_t *link = &lp->link;
+    kio_addr_t ioaddr = dev->base_addr;
+    
+    DEBUG(1, "%s: shutting down ethercard.\n", dev->name);
+
+    if (DEV_OK(link)) {
+	/* Turn off statistics ASAP.  We update lp->stats below. */
+	outw(StatsDisable, ioaddr + EL3_CMD);
+	
+	/* Disable the receiver and transmitter. */
+	outw(RxDisable, ioaddr + EL3_CMD);
+	outw(TxDisable, ioaddr + EL3_CMD);
+	
+	if (dev->if_port == 2)
+	    /* Turn off thinnet power.  Green! */
+	    outw(StopCoax, ioaddr + EL3_CMD);
+	else if (dev->if_port == 1) {
+	    /* Disable link beat and jabber */
+	    EL3WINDOW(4);
+	    outw(0, ioaddr + WN4_MEDIA);
+	}
+	
+	/* Switching back to window 0 disables the IRQ. */
+	EL3WINDOW(0);
+	/* But we explicitly zero the IRQ line select anyway. */
+	outw(0x0f00, ioaddr + WN0_IRQ);
+        
+	/* Check if the card still exists */
+	if ((inw(ioaddr+EL3_STATUS) & 0xe000) == 0x2000)
+	    update_stats(dev);
+    }
+
+    link->open--;
+    netif_stop_queue(dev);
+    del_timer_sync(&lp->media);
+    
+    return 0;
+}
+
+static struct pcmcia_driver tc589_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "3c589_cs",
+	},
+	.attach		= tc589_attach,
+	.detach		= tc589_detach,
+};
+
+static int __init init_tc589(void)
+{
+	return pcmcia_register_driver(&tc589_driver);
+}
+
+static void __exit exit_tc589(void)
+{
+	pcmcia_unregister_driver(&tc589_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_tc589);
+module_exit(exit_tc589);
diff --git a/drivers/net/pcmcia/Kconfig b/drivers/net/pcmcia/Kconfig
new file mode 100644
index 000000000000..74f862001247
--- /dev/null
+++ b/drivers/net/pcmcia/Kconfig
@@ -0,0 +1,132 @@
+#
+# PCMCIA Network device configuration
+#
+
+menu "PCMCIA network device support"
+	depends on NETDEVICES && PCMCIA!=n
+
+config NET_PCMCIA
+	bool "PCMCIA network device support"
+	---help---
+	  Say Y if you would like to include support for any PCMCIA or CardBus
+	  network adapters, then say Y to the driver for your particular card
+	  below.  PCMCIA- or PC-cards are credit-card size devices often used
+	  with laptops computers; CardBus is the newer and faster version of
+	  PCMCIA.
+
+	  To use your PC-cards, you will need supporting software from David
+	  Hinds' pcmcia-cs package (see the file <file:Documentation/Changes>
+	  for location).  You also want to check out the PCMCIA-HOWTO,
+	  available from <http://www.tldp.org/docs.html#howto>.
+
+	  If unsure, say N.
+
+config PCMCIA_3C589
+	tristate "3Com 3c589 PCMCIA support"
+	depends on NET_PCMCIA && PCMCIA
+	help
+	  Say Y here if you intend to attach a 3Com 3c589 or compatible PCMCIA
+	  (PC-card) Ethernet card to your computer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called 3c589_cs.  If unsure, say N.
+
+config PCMCIA_3C574
+	tristate "3Com 3c574 PCMCIA support"
+	depends on NET_PCMCIA && PCMCIA
+	help
+	  Say Y here if you intend to attach a 3Com 3c574 or compatible PCMCIA
+	  (PC-card) Fast Ethernet card to your computer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called 3c574_cs.  If unsure, say N.
+
+config PCMCIA_FMVJ18X
+	tristate "Fujitsu FMV-J18x PCMCIA support"
+	depends on NET_PCMCIA && PCMCIA
+	select CRC32
+	help
+	  Say Y here if you intend to attach a Fujitsu FMV-J18x or compatible
+	  PCMCIA (PC-card) Ethernet card to your computer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called fmvj18x_cs.  If unsure, say N.
+
+config PCMCIA_PCNET
+	tristate "NE2000 compatible PCMCIA support"
+	depends on NET_PCMCIA && PCMCIA
+	select CRC32
+	help
+	  Say Y here if you intend to attach an NE2000 compatible PCMCIA
+	  (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called pcnet_cs.  If unsure, say N.
+
+config PCMCIA_NMCLAN
+	tristate "New Media PCMCIA support"
+	depends on NET_PCMCIA && PCMCIA
+	help
+	  Say Y here if you intend to attach a New Media Ethernet or LiveWire
+	  PCMCIA (PC-card) Ethernet card to your computer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called nmclan_cs.  If unsure, say N.
+
+config PCMCIA_SMC91C92
+	tristate "SMC 91Cxx PCMCIA support"
+	depends on NET_PCMCIA && PCMCIA
+	select CRC32
+	select MII
+	help
+	  Say Y here if you intend to attach an SMC 91Cxx compatible PCMCIA
+	  (PC-card) Ethernet or Fast Ethernet card to your computer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called smc91c92_cs.  If unsure, say N.
+
+config PCMCIA_XIRC2PS
+	tristate "Xircom 16-bit PCMCIA support"
+	depends on NET_PCMCIA && PCMCIA
+	help
+	  Say Y here if you intend to attach a Xircom 16-bit PCMCIA (PC-card)
+	  Ethernet or Fast Ethernet card to your computer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called xirc2ps_cs.  If unsure, say N.
+
+config PCMCIA_AXNET
+	tristate "Asix AX88190 PCMCIA support"
+	depends on NET_PCMCIA && PCMCIA
+	---help---
+	  Say Y here if you intend to attach an Asix AX88190-based PCMCIA
+	  (PC-card) Fast Ethernet card to your computer.  These cards are
+	  nearly NE2000 compatible but need a separate driver due to a few
+	  misfeatures.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called axnet_cs.  If unsure, say N.
+
+config ARCNET_COM20020_CS
+	tristate "COM20020 ARCnet PCMCIA support"
+	depends on NET_PCMCIA && ARCNET_COM20020 && PCMCIA
+	help
+	  Say Y here if you intend to attach this type of ARCnet PCMCIA card
+	  to your computer.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called com20020_cs.  If unsure, say N.
+
+config PCMCIA_IBMTR
+	tristate "IBM PCMCIA tokenring adapter support"
+	depends on NET_PCMCIA && IBMTR!=y && TR && PCMCIA && !64BIT
+	help
+	  Say Y here if you intend to attach this type of Token Ring PCMCIA
+	  card to your computer. You then also need to say Y to "Token Ring
+	  driver support".
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ibmtr_cs.
+
+endmenu
+
diff --git a/drivers/net/pcmcia/Makefile b/drivers/net/pcmcia/Makefile
new file mode 100644
index 000000000000..87d2d99f4c14
--- /dev/null
+++ b/drivers/net/pcmcia/Makefile
@@ -0,0 +1,16 @@
+#
+# Makefile for the Linux PCMCIA network device drivers.
+#
+
+# 16-bit client drivers
+obj-$(CONFIG_PCMCIA_3C589)	+= 3c589_cs.o
+obj-$(CONFIG_PCMCIA_3C574)	+= 3c574_cs.o
+obj-$(CONFIG_PCMCIA_FMVJ18X)	+= fmvj18x_cs.o
+obj-$(CONFIG_PCMCIA_NMCLAN)	+= nmclan_cs.o
+obj-$(CONFIG_PCMCIA_PCNET)	+= pcnet_cs.o
+obj-$(CONFIG_PCMCIA_SMC91C92)	+= smc91c92_cs.o
+obj-$(CONFIG_PCMCIA_XIRC2PS)	+= xirc2ps_cs.o
+obj-$(CONFIG_ARCNET_COM20020_CS)+= com20020_cs.o
+obj-$(CONFIG_PCMCIA_AXNET)	+= axnet_cs.o
+
+obj-$(CONFIG_PCMCIA_IBMTR)	+= ibmtr_cs.o
diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c
new file mode 100644
index 000000000000..853b586e481a
--- /dev/null
+++ b/drivers/net/pcmcia/axnet_cs.c
@@ -0,0 +1,1864 @@
+/*======================================================================
+
+    A PCMCIA ethernet driver for Asix AX88190-based cards
+
+    The Asix AX88190 is a NS8390-derived chipset with a few nasty
+    idiosyncracies that make it very inconvenient to support with a
+    standard 8390 driver.  This driver is based on pcnet_cs, with the
+    tweaked 8390 code grafted on the end.  Much of what I did was to
+    clean up and update a similar driver supplied by Asix, which was
+    adapted by William Lee, william@asix.com.tw.
+
+    Copyright (C) 2001 David A. Hinds -- dahinds@users.sourceforge.net
+
+    axnet_cs.c 1.28 2002/06/29 06:27:37
+
+    The network driver code is based on Donald Becker's NE2000 code:
+
+    Written 1992,1993 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 of the GNU General Public License,
+    incorporated herein by reference.
+    Donald Becker may be reached at becker@scyld.com
+
+======================================================================*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include "../8390.h"
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define AXNET_CMD	0x00
+#define AXNET_DATAPORT	0x10	/* NatSemi-defined port window offset. */
+#define AXNET_RESET	0x1f	/* Issue a read to reset, a write to clear. */
+#define AXNET_MII_EEP	0x14	/* Offset of MII access port */
+#define AXNET_TEST	0x15	/* Offset of TEST Register port */
+#define AXNET_GPIO	0x17	/* Offset of General Purpose Register Port */
+
+#define AXNET_START_PG	0x40	/* First page of TX buffer */
+#define AXNET_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+#define AXNET_RDC_TIMEOUT 0x02	/* Max wait in jiffies for Tx RDC */
+
+#define IS_AX88190	0x0001
+#define IS_AX88790	0x0002
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("Asix AX88190 PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#ifdef PCMCIA_DEBUG
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"axnet_cs.c 1.28 2002/06/29 06:27:37 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+static void axnet_config(dev_link_t *link);
+static void axnet_release(dev_link_t *link);
+static int axnet_event(event_t event, int priority,
+		       event_callback_args_t *args);
+static int axnet_open(struct net_device *dev);
+static int axnet_close(struct net_device *dev);
+static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static struct ethtool_ops netdev_ethtool_ops;
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs);
+static void ei_watchdog(u_long arg);
+static void axnet_reset_8390(struct net_device *dev);
+
+static int mdio_read(kio_addr_t addr, int phy_id, int loc);
+static void mdio_write(kio_addr_t addr, int phy_id, int loc, int value);
+
+static void get_8390_hdr(struct net_device *,
+			 struct e8390_pkt_hdr *, int);
+static void block_input(struct net_device *dev, int count,
+			struct sk_buff *skb, int ring_offset);
+static void block_output(struct net_device *dev, int count,
+			 const u_char *buf, const int start_page);
+
+static dev_link_t *axnet_attach(void);
+static void axnet_detach(dev_link_t *);
+
+static dev_info_t dev_info = "axnet_cs";
+static dev_link_t *dev_list;
+
+static void axdev_setup(struct net_device *dev);
+static void AX88190_init(struct net_device *dev, int startp);
+static int ax_open(struct net_device *dev);
+static int ax_close(struct net_device *dev);
+static irqreturn_t ax_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+/*====================================================================*/
+
+typedef struct axnet_dev_t {
+    dev_link_t		link;
+    dev_node_t		node;
+    caddr_t		base;
+    struct timer_list	watchdog;
+    int			stale, fast_poll;
+    u_short		link_status;
+    u_char		duplex_flag;
+    int			phy_id;
+    int			flags;
+} axnet_dev_t;
+
+static inline axnet_dev_t *PRIV(struct net_device *dev)
+{
+	void *p = (char *)netdev_priv(dev) + sizeof(struct ei_device);
+	return p;
+}
+
+/*======================================================================
+
+    axnet_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+======================================================================*/
+
+static dev_link_t *axnet_attach(void)
+{
+    axnet_dev_t *info;
+    dev_link_t *link;
+    struct net_device *dev;
+    client_reg_t client_reg;
+    int ret;
+
+    DEBUG(0, "axnet_attach()\n");
+
+    dev = alloc_netdev(sizeof(struct ei_device) + sizeof(axnet_dev_t),
+			"eth%d", axdev_setup);
+
+    if (!dev)
+	return NULL;
+
+    info = PRIV(dev);
+    link = &info->link;
+    link->priv = dev;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    dev->open = &axnet_open;
+    dev->stop = &axnet_close;
+    dev->do_ioctl = &axnet_ioctl;
+    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &axnet_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != CS_SUCCESS) {
+	cs_error(link->handle, RegisterClient, ret);
+	axnet_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* axnet_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void axnet_detach(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    dev_link_t **linkp;
+
+    DEBUG(0, "axnet_detach(0x%p)\n", link);
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    if (link->dev)
+	unregister_netdev(dev);
+
+    if (link->state & DEV_CONFIG)
+	axnet_release(link);
+
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    free_netdev(dev);
+} /* axnet_detach */
+
+/*======================================================================
+
+    This probes for a card's hardware address by reading the PROM.
+
+======================================================================*/
+
+static int get_prom(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    kio_addr_t ioaddr = dev->base_addr;
+    int i, j;
+
+    /* This is based on drivers/net/ne.c */
+    struct {
+	u_char value, offset;
+    } program_seq[] = {
+	{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+	{0x01,	EN0_DCFG},	/* Set word-wide access. */
+	{0x00,	EN0_RCNTLO},	/* Clear the count regs. */
+	{0x00,	EN0_RCNTHI},
+	{0x00,	EN0_IMR},	/* Mask completion irq. */
+	{0xFF,	EN0_ISR},
+	{E8390_RXOFF|0x40, EN0_RXCR},	/* 0x60  Set to monitor */
+	{E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */
+	{0x10,	EN0_RCNTLO},
+	{0x00,	EN0_RCNTHI},
+	{0x00,	EN0_RSARLO},	/* DMA starting at 0x0400. */
+	{0x04,	EN0_RSARHI},
+	{E8390_RREAD+E8390_START, E8390_CMD},
+    };
+
+    /* Not much of a test, but the alternatives are messy */
+    if (link->conf.ConfigBase != 0x03c0)
+	return 0;
+
+    axnet_reset_8390(dev);
+    mdelay(10);
+
+    for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
+	outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+    for (i = 0; i < 6; i += 2) {
+	j = inw(ioaddr + AXNET_DATAPORT);
+	dev->dev_addr[i] = j & 0xff;
+	dev->dev_addr[i+1] = j >> 8;
+    }
+    return 1;
+} /* get_prom */
+
+/*======================================================================
+
+    axnet_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    ethernet device available to the system.
+
+======================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static int try_io_port(dev_link_t *link)
+{
+    int j, ret;
+    if (link->io.NumPorts1 == 32) {
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+	if (link->io.NumPorts2 > 0) {
+	    /* for master/slave multifunction cards */
+	    link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+	    link->irq.Attributes = 
+		IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+	}
+    } else {
+	/* This should be two 16-port windows */
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+	link->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
+    }
+    if (link->io.BasePort1 == 0) {
+	link->io.IOAddrLines = 16;
+	for (j = 0; j < 0x400; j += 0x20) {
+	    link->io.BasePort1 = j ^ 0x300;
+	    link->io.BasePort2 = (j ^ 0x300) + 0x10;
+	    ret = pcmcia_request_io(link->handle, &link->io);
+	    if (ret == CS_SUCCESS) return ret;
+	}
+	return ret;
+    } else {
+	return pcmcia_request_io(link->handle, &link->io);
+    }
+}
+
+static void axnet_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    axnet_dev_t *info = PRIV(dev);
+    tuple_t tuple;
+    cisparse_t parse;
+    int i, j, last_ret, last_fn;
+    u_short buf[64];
+    config_info_t conf;
+
+    DEBUG(0, "axnet_config(0x%p)\n", link);
+
+    tuple.Attributes = 0;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.TupleOffset = 0;
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    link->conf.ConfigBase = parse.config.base;
+    /* don't trust the CIS on this; Linksys got it wrong */
+    link->conf.Present = 0x63;
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    /* Look up current Vcc */
+    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
+    link->conf.Vcc = conf.Vcc;
+
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    tuple.Attributes = 0;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    while (last_ret == CS_SUCCESS) {
+	cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+	cistpl_io_t *io = &(parse.cftable_entry.io);
+	
+	if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+		pcmcia_parse_tuple(handle, &tuple, &parse) != 0 ||
+		cfg->index == 0 || cfg->io.nwin == 0)
+	    goto next_entry;
+	
+	link->conf.ConfigIndex = 0x05;
+	/* For multifunction cards, by convention, we configure the
+	   network function with window 0, and serial with window 1 */
+	if (io->nwin > 1) {
+	    i = (io->win[1].len > io->win[0].len);
+	    link->io.BasePort2 = io->win[1-i].base;
+	    link->io.NumPorts2 = io->win[1-i].len;
+	} else {
+	    i = link->io.NumPorts2 = 0;
+	}
+	link->io.BasePort1 = io->win[i].base;
+	link->io.NumPorts1 = io->win[i].len;
+	link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+	if (link->io.NumPorts1 + link->io.NumPorts2 >= 32) {
+	    last_ret = try_io_port(link);
+	    if (last_ret == CS_SUCCESS) break;
+	}
+    next_entry:
+	last_ret = pcmcia_get_next_tuple(handle, &tuple);
+    }
+    if (last_ret != CS_SUCCESS) {
+	cs_error(handle, RequestIO, last_ret);
+	goto failed;
+    }
+
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+    
+    if (link->io.NumPorts2 == 8) {
+	link->conf.Attributes |= CONF_ENABLE_SPKR;
+	link->conf.Status = CCSR_AUDIO_ENA;
+    }
+    
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+    dev->irq = link->irq.AssignedIRQ;
+    dev->base_addr = link->io.BasePort1;
+
+    if (!get_prom(link)) {
+	printk(KERN_NOTICE "axnet_cs: this is not an AX88190 card!\n");
+	printk(KERN_NOTICE "axnet_cs: use pcnet_cs instead.\n");
+	goto failed;
+    }
+
+    ei_status.name = "AX88190";
+    ei_status.word16 = 1;
+    ei_status.tx_start_page = AXNET_START_PG;
+    ei_status.rx_start_page = AXNET_START_PG + TX_PAGES;
+    ei_status.stop_page = AXNET_STOP_PG;
+    ei_status.reset_8390 = &axnet_reset_8390;
+    ei_status.get_8390_hdr = &get_8390_hdr;
+    ei_status.block_input = &block_input;
+    ei_status.block_output = &block_output;
+
+    if (inb(dev->base_addr + AXNET_TEST) != 0)
+	info->flags |= IS_AX88790;
+    else
+	info->flags |= IS_AX88190;
+
+    if (info->flags & IS_AX88790)
+	outb(0x10, dev->base_addr + AXNET_GPIO);  /* select Internal PHY */
+
+    for (i = 0; i < 32; i++) {
+	j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
+	if ((j != 0) && (j != 0xffff)) break;
+    }
+
+    /* Maybe PHY is in power down mode. (PPD_SET = 1) 
+       Bit 2 of CCSR is active low. */ 
+    if (i == 32) {
+	conf_reg_t reg = { 0, CS_WRITE, CISREG_CCSR, 0x04 };
+ 	pcmcia_access_configuration_register(link->handle, &reg);
+	for (i = 0; i < 32; i++) {
+	    j = mdio_read(dev->base_addr + AXNET_MII_EEP, i, 1);
+	    if ((j != 0) && (j != 0xffff)) break;
+	}
+    }
+
+    info->phy_id = (i < 32) ? i : -1;
+    link->dev = &info->node;
+    link->state &= ~DEV_CONFIG_PENDING;
+    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+
+    if (register_netdev(dev) != 0) {
+	printk(KERN_NOTICE "axnet_cs: register_netdev() failed\n");
+	link->dev = NULL;
+	goto failed;
+    }
+
+    strcpy(info->node.dev_name, dev->name);
+
+    printk(KERN_INFO "%s: Asix AX88%d90: io %#3lx, irq %d, hw_addr ",
+	   dev->name, ((info->flags & IS_AX88790) ? 7 : 1),
+	   dev->base_addr, dev->irq);
+    for (i = 0; i < 6; i++)
+	printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+    if (info->phy_id != -1) {
+	DEBUG(0, "  MII transceiver at index %d, status %x.\n", info->phy_id, j);
+    } else {
+	printk(KERN_NOTICE "  No MII transceivers found!\n");
+    }
+    return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    axnet_release(link);
+    link->state &= ~DEV_CONFIG_PENDING;
+    return;
+} /* axnet_config */
+
+/*======================================================================
+
+    After a card is removed, axnet_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void axnet_release(dev_link_t *link)
+{
+    DEBUG(0, "axnet_release(0x%p)\n", link);
+
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+
+    link->state &= ~DEV_CONFIG;
+}
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+======================================================================*/
+
+static int axnet_event(event_t event, int priority,
+		       event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    struct net_device *dev = link->priv;
+
+    DEBUG(2, "axnet_event(0x%06x)\n", event);
+
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG)
+	    netif_device_detach(dev);
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	axnet_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG) {
+	    if (link->open)
+		netif_device_detach(dev);
+	    pcmcia_release_configuration(link->handle);
+	}
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG) {
+	    pcmcia_request_configuration(link->handle, &link->conf);
+	    if (link->open) {
+		axnet_reset_8390(dev);
+		AX88190_init(dev, 1);
+		netif_device_attach(dev);
+	    }
+	}
+	break;
+    }
+    return 0;
+} /* axnet_event */
+
+/*======================================================================
+
+    MII interface support
+
+======================================================================*/
+
+#define MDIO_SHIFT_CLK		0x01
+#define MDIO_DATA_WRITE0	0x00
+#define MDIO_DATA_WRITE1	0x08
+#define MDIO_DATA_READ		0x04
+#define MDIO_MASK		0x0f
+#define MDIO_ENB_IN		0x02
+
+static void mdio_sync(kio_addr_t addr)
+{
+    int bits;
+    for (bits = 0; bits < 32; bits++) {
+	outb_p(MDIO_DATA_WRITE1, addr);
+	outb_p(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+    }
+}
+
+static int mdio_read(kio_addr_t addr, int phy_id, int loc)
+{
+    u_int cmd = (0xf6<<10)|(phy_id<<5)|loc;
+    int i, retval = 0;
+
+    mdio_sync(addr);
+    for (i = 14; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb_p(dat, addr);
+	outb_p(dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 19; i > 0; i--) {
+	outb_p(MDIO_ENB_IN, addr);
+	retval = (retval << 1) | ((inb_p(addr) & MDIO_DATA_READ) != 0);
+	outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr);
+    }
+    return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(kio_addr_t addr, int phy_id, int loc, int value)
+{
+    u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+    int i;
+
+    mdio_sync(addr);
+    for (i = 31; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb_p(dat, addr);
+	outb_p(dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 1; i >= 0; i--) {
+	outb_p(MDIO_ENB_IN, addr);
+	outb_p(MDIO_ENB_IN | MDIO_SHIFT_CLK, addr);
+    }
+}
+
+/*====================================================================*/
+
+static int axnet_open(struct net_device *dev)
+{
+    axnet_dev_t *info = PRIV(dev);
+    dev_link_t *link = &info->link;
+    
+    DEBUG(2, "axnet_open('%s')\n", dev->name);
+
+    if (!DEV_OK(link))
+	return -ENODEV;
+
+    link->open++;
+
+    request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev);
+
+    info->link_status = 0x00;
+    init_timer(&info->watchdog);
+    info->watchdog.function = &ei_watchdog;
+    info->watchdog.data = (u_long)dev;
+    info->watchdog.expires = jiffies + HZ;
+    add_timer(&info->watchdog);
+
+    return ax_open(dev);
+} /* axnet_open */
+
+/*====================================================================*/
+
+static int axnet_close(struct net_device *dev)
+{
+    axnet_dev_t *info = PRIV(dev);
+    dev_link_t *link = &info->link;
+
+    DEBUG(2, "axnet_close('%s')\n", dev->name);
+
+    ax_close(dev);
+    free_irq(dev->irq, dev);
+    
+    link->open--;
+    netif_stop_queue(dev);
+    del_timer_sync(&info->watchdog);
+
+    return 0;
+} /* axnet_close */
+
+/*======================================================================
+
+    Hard reset the card.  This used to pause for the same period that
+    a 8390 reset command required, but that shouldn't be necessary.
+
+======================================================================*/
+
+static void axnet_reset_8390(struct net_device *dev)
+{
+    kio_addr_t nic_base = dev->base_addr;
+    int i;
+
+    ei_status.txing = ei_status.dmaing = 0;
+
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD);
+
+    outb(inb(nic_base + AXNET_RESET), nic_base + AXNET_RESET);
+
+    for (i = 0; i < 100; i++) {
+	if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
+	    break;
+	udelay(100);
+    }
+    outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */
+    
+    if (i == 100)
+	printk(KERN_ERR "%s: axnet_reset_8390() did not complete.\n",
+	       dev->name);
+    
+} /* axnet_reset_8390 */
+
+/*====================================================================*/
+
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct net_device *dev = dev_id;
+    PRIV(dev)->stale = 0;
+    return ax_interrupt(irq, dev_id, regs);
+}
+
+static void ei_watchdog(u_long arg)
+{
+    struct net_device *dev = (struct net_device *)(arg);
+    axnet_dev_t *info = PRIV(dev);
+    kio_addr_t nic_base = dev->base_addr;
+    kio_addr_t mii_addr = nic_base + AXNET_MII_EEP;
+    u_short link;
+
+    if (!netif_device_present(dev)) goto reschedule;
+
+    /* Check for pending interrupt with expired latency timer: with
+       this, we can limp along even if the interrupt is blocked */
+    if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) {
+	if (!info->fast_poll)
+	    printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);
+	ei_irq_wrapper(dev->irq, dev, NULL);
+	info->fast_poll = HZ;
+    }
+    if (info->fast_poll) {
+	info->fast_poll--;
+	info->watchdog.expires = jiffies + 1;
+	add_timer(&info->watchdog);
+	return;
+    }
+
+    if (info->phy_id < 0)
+	goto reschedule;
+    link = mdio_read(mii_addr, info->phy_id, 1);
+    if (!link || (link == 0xffff)) {
+	printk(KERN_INFO "%s: MII is missing!\n", dev->name);
+	info->phy_id = -1;
+	goto reschedule;
+    }
+
+    link &= 0x0004;
+    if (link != info->link_status) {
+	u_short p = mdio_read(mii_addr, info->phy_id, 5);
+	printk(KERN_INFO "%s: %s link beat\n", dev->name,
+	       (link) ? "found" : "lost");
+	if (link) {
+	    info->duplex_flag = (p & 0x0140) ? 0x80 : 0x00;
+	    if (p)
+		printk(KERN_INFO "%s: autonegotiation complete: "
+		       "%sbaseT-%cD selected\n", dev->name,
+		       ((p & 0x0180) ? "100" : "10"),
+		       ((p & 0x0140) ? 'F' : 'H'));
+	    else
+		printk(KERN_INFO "%s: link partner did not autonegotiate\n",
+		       dev->name);
+	    AX88190_init(dev, 1);
+	}
+	info->link_status = link;
+    }
+
+reschedule:
+    info->watchdog.expires = jiffies + HZ;
+    add_timer(&info->watchdog);
+}
+
+static void netdev_get_drvinfo(struct net_device *dev,
+			       struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, "axnet_cs");
+}
+
+static struct ethtool_ops netdev_ethtool_ops = {
+	.get_drvinfo		= netdev_get_drvinfo,
+};
+
+/*====================================================================*/
+
+static int axnet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+    axnet_dev_t *info = PRIV(dev);
+    u16 *data = (u16 *)&rq->ifr_ifru;
+    kio_addr_t mii_addr = dev->base_addr + AXNET_MII_EEP;
+    switch (cmd) {
+    case SIOCGMIIPHY:
+	data[0] = info->phy_id;
+    case SIOCGMIIREG:		/* Read MII PHY register. */
+	data[3] = mdio_read(mii_addr, data[0], data[1] & 0x1f);
+	return 0;
+    case SIOCSMIIREG:		/* Write MII PHY register. */
+	if (!capable(CAP_NET_ADMIN))
+	    return -EPERM;
+	mdio_write(mii_addr, data[0], data[1] & 0x1f, data[2]);
+	return 0;
+    }
+    return -EOPNOTSUPP;
+}
+
+/*====================================================================*/
+
+static void get_8390_hdr(struct net_device *dev,
+			 struct e8390_pkt_hdr *hdr,
+			 int ring_page)
+{
+    kio_addr_t nic_base = dev->base_addr;
+
+    outb_p(0, nic_base + EN0_RSARLO);		/* On page boundary */
+    outb_p(ring_page, nic_base + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD);
+
+    insw(nic_base + AXNET_DATAPORT, hdr,
+	    sizeof(struct e8390_pkt_hdr)>>1);
+    /* Fix for big endian systems */
+    hdr->count = le16_to_cpu(hdr->count);
+
+}
+
+/*====================================================================*/
+
+static void block_input(struct net_device *dev, int count,
+			struct sk_buff *skb, int ring_offset)
+{
+    kio_addr_t nic_base = dev->base_addr;
+    int xfer_count = count;
+    char *buf = skb->data;
+
+#ifdef PCMCIA_DEBUG
+    if ((ei_debug > 4) && (count != 4))
+	printk(KERN_DEBUG "%s: [bi=%d]\n", dev->name, count+4);
+#endif
+    outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+    outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, nic_base + AXNET_CMD);
+
+    insw(nic_base + AXNET_DATAPORT,buf,count>>1);
+    if (count & 0x01)
+	buf[count-1] = inb(nic_base + AXNET_DATAPORT), xfer_count++;
+
+}
+
+/*====================================================================*/
+
+static void block_output(struct net_device *dev, int count,
+			 const u_char *buf, const int start_page)
+{
+    kio_addr_t nic_base = dev->base_addr;
+
+#ifdef PCMCIA_DEBUG
+    if (ei_debug > 4)
+	printk(KERN_DEBUG "%s: [bo=%d]\n", dev->name, count);
+#endif
+
+    /* Round the count up for word writes.  Do we need to do this?
+       What effect will an odd byte count have on the 8390?
+       I should check someday. */
+    if (count & 0x01)
+	count++;
+
+    outb_p(0x00, nic_base + EN0_RSARLO);
+    outb_p(start_page, nic_base + EN0_RSARHI);
+    outb_p(E8390_RWRITE+E8390_START, nic_base + AXNET_CMD);
+    outsw(nic_base + AXNET_DATAPORT, buf, count>>1);
+}
+
+static struct pcmcia_driver axnet_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "axnet_cs",
+	},
+	.attach		= axnet_attach,
+	.detach		= axnet_detach,
+};
+
+static int __init init_axnet_cs(void)
+{
+	return pcmcia_register_driver(&axnet_cs_driver);
+}
+
+static void __exit exit_axnet_cs(void)
+{
+	pcmcia_unregister_driver(&axnet_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_axnet_cs);
+module_exit(exit_axnet_cs);
+
+/*====================================================================*/
+
+/* 8390.c: A general NS8390 ethernet driver core for linux. */
+/*
+	Written 1992-94 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
+	of the GNU General Public License, incorporated herein by reference.
+
+	The author may be reached as becker@scyld.com, or C/O
+	Scyld Computing Corporation
+	410 Severn Ave., Suite 210
+	Annapolis MD 21403
+
+  This is the chip-specific code for many 8390-based ethernet adaptors.
+  This is not a complete driver, it must be combined with board-specific
+  code such as ne.c, wd.c, 3c503.c, etc.
+
+  Seeing how at least eight drivers use this code, (not counting the
+  PCMCIA ones either) it is easy to break some card by what seems like
+  a simple innocent change. Please contact me or Donald if you think
+  you have found something that needs changing. -- PG
+
+  Changelog:
+
+  Paul Gortmaker	: remove set_bit lock, other cleanups.
+  Paul Gortmaker	: add ei_get_8390_hdr() so we can pass skb's to 
+			  ei_block_input() for eth_io_copy_and_sum().
+  Paul Gortmaker	: exchange static int ei_pingpong for a #define,
+			  also add better Tx error handling.
+  Paul Gortmaker	: rewrite Rx overrun handling as per NS specs.
+  Alexey Kuznetsov	: use the 8390's six bit hash multicast filter.
+  Paul Gortmaker	: tweak ANK's above multicast changes a bit.
+  Paul Gortmaker	: update packet statistics for v2.1.x
+  Alan Cox		: support arbitary stupid port mappings on the
+  			  68K Macintosh. Support >16bit I/O spaces
+  Paul Gortmaker	: add kmod support for auto-loading of the 8390
+			  module by all drivers that require it.
+  Alan Cox		: Spinlocking work, added 'BUG_83C690'
+  Paul Gortmaker	: Separate out Tx timeout code from Tx path.
+
+  Sources:
+  The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+
+  */
+
+static const char *version_8390 =
+    "8390.c:v1.10cvs 9/23/94 Donald Becker (becker@scyld.com)\n";
+
+#include <linux/bitops.h>
+#include <asm/irq.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+
+#include <linux/etherdevice.h>
+
+#define BUG_83C690
+
+/* These are the operational function interfaces to board-specific
+   routines.
+	void reset_8390(struct net_device *dev)
+		Resets the board associated with DEV, including a hardware reset of
+		the 8390.  This is only called when there is a transmit timeout, and
+		it is always followed by 8390_init().
+	void block_output(struct net_device *dev, int count, const unsigned char *buf,
+					  int start_page)
+		Write the COUNT bytes of BUF to the packet buffer at START_PAGE.  The
+		"page" value uses the 8390's 256-byte pages.
+	void get_8390_hdr(struct net_device *dev, struct e8390_hdr *hdr, int ring_page)
+		Read the 4 byte, page aligned 8390 header. *If* there is a
+		subsequent read, it will be of the rest of the packet.
+	void block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
+		Read COUNT bytes from the packet buffer into the skb data area. Start 
+		reading from RING_OFFSET, the address as the 8390 sees it.  This will always
+		follow the read of the 8390 header. 
+*/
+#define ei_reset_8390 (ei_local->reset_8390)
+#define ei_block_output (ei_local->block_output)
+#define ei_block_input (ei_local->block_input)
+#define ei_get_8390_hdr (ei_local->get_8390_hdr)
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef ei_debug
+int ei_debug = 1;
+#endif
+
+/* Index to functions. */
+static void ei_tx_intr(struct net_device *dev);
+static void ei_tx_err(struct net_device *dev);
+static void ei_tx_timeout(struct net_device *dev);
+static void ei_receive(struct net_device *dev);
+static void ei_rx_overrun(struct net_device *dev);
+
+/* Routines generic to NS8390-based boards. */
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+								int start_page);
+static void set_multicast_list(struct net_device *dev);
+static void do_set_multicast_list(struct net_device *dev);
+
+/*
+ *	SMP and the 8390 setup.
+ *
+ *	The 8390 isnt exactly designed to be multithreaded on RX/TX. There is
+ *	a page register that controls bank and packet buffer access. We guard
+ *	this with ei_local->page_lock. Nobody should assume or set the page other
+ *	than zero when the lock is not held. Lock holders must restore page 0
+ *	before unlocking. Even pure readers must take the lock to protect in 
+ *	page 0.
+ *
+ *	To make life difficult the chip can also be very slow. We therefore can't
+ *	just use spinlocks. For the longer lockups we disable the irq the device
+ *	sits on and hold the lock. We must hold the lock because there is a dual
+ *	processor case other than interrupts (get stats/set multicast list in
+ *	parallel with each other and transmit).
+ *
+ *	Note: in theory we can just disable the irq on the card _but_ there is
+ *	a latency on SMP irq delivery. So we can easily go "disable irq" "sync irqs"
+ *	enter lock, take the queued irq. So we waddle instead of flying.
+ *
+ *	Finally by special arrangement for the purpose of being generally 
+ *	annoying the transmit function is called bh atomic. That places
+ *	restrictions on the user context callers as disable_irq won't save
+ *	them.
+ */
+ 
+/**
+ * ax_open - Open/initialize the board.
+ * @dev: network device to initialize
+ *
+ * This routine goes all-out, setting everything
+ * up anew at each open, even though many of these registers should only
+ * need to be set once at boot.
+ */
+static int ax_open(struct net_device *dev)
+{
+	unsigned long flags;
+	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
+
+#ifdef HAVE_TX_TIMEOUT
+	/* The card I/O part of the driver (e.g. 3c503) can hook a Tx timeout
+	    wrapper that does e.g. media check & then calls ei_tx_timeout. */
+	if (dev->tx_timeout == NULL)
+		 dev->tx_timeout = ei_tx_timeout;
+	if (dev->watchdog_timeo <= 0)
+		 dev->watchdog_timeo = TX_TIMEOUT;
+#endif
+
+	/*
+	 *	Grab the page lock so we own the register set, then call
+	 *	the init function.
+	 */
+      
+      	spin_lock_irqsave(&ei_local->page_lock, flags);
+	AX88190_init(dev, 1);
+	/* Set the flag before we drop the lock, That way the IRQ arrives
+	   after its set and we get no silly warnings */
+	netif_start_queue(dev);
+      	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+	ei_local->irqlock = 0;
+	return 0;
+}
+
+#define dev_lock(dev) (((struct ei_device *)netdev_priv(dev))->page_lock)
+
+/**
+ * ax_close - shut down network device
+ * @dev: network device to close
+ *
+ * Opposite of ax_open(). Only used when "ifconfig <devname> down" is done.
+ */
+int ax_close(struct net_device *dev)
+{
+	unsigned long flags;
+
+	/*
+	 *      Hold the page lock during close
+	 */
+
+	spin_lock_irqsave(&dev_lock(dev), flags);
+	AX88190_init(dev, 0);
+	spin_unlock_irqrestore(&dev_lock(dev), flags);
+	netif_stop_queue(dev);
+	return 0;
+}
+
+/**
+ * ei_tx_timeout - handle transmit time out condition
+ * @dev: network device which has apparently fallen asleep
+ *
+ * Called by kernel when device never acknowledges a transmit has
+ * completed (or failed) - i.e. never posted a Tx related interrupt.
+ */
+
+void ei_tx_timeout(struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
+	int txsr, isr, tickssofar = jiffies - dev->trans_start;
+	unsigned long flags;
+
+	ei_local->stat.tx_errors++;
+
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+	txsr = inb(e8390_base+EN0_TSR);
+	isr = inb(e8390_base+EN0_ISR);
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+
+	printk(KERN_DEBUG "%s: Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n",
+		dev->name, (txsr & ENTSR_ABT) ? "excess collisions." :
+		(isr) ? "lost interrupt?" : "cable problem?", txsr, isr, tickssofar);
+
+	if (!isr && !ei_local->stat.tx_packets) 
+	{
+		/* The 8390 probably hasn't gotten on the cable yet. */
+		ei_local->interface_num ^= 1;   /* Try a different xcvr.  */
+	}
+
+	/* Ugly but a reset can be slow, yet must be protected */
+		
+	disable_irq_nosync(dev->irq);
+	spin_lock(&ei_local->page_lock);
+		
+	/* Try to restart the card.  Perhaps the user has fixed something. */
+	ei_reset_8390(dev);
+	AX88190_init(dev, 1);
+		
+	spin_unlock(&ei_local->page_lock);
+	enable_irq(dev->irq);
+	netif_wake_queue(dev);
+}
+    
+/**
+ * ei_start_xmit - begin packet transmission
+ * @skb: packet to be sent
+ * @dev: network device to which packet is sent
+ *
+ * Sends a packet to an 8390 network device.
+ */
+ 
+static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
+	int length, send_length, output_page;
+	unsigned long flags;
+	u8 packet[ETH_ZLEN];
+	
+	netif_stop_queue(dev);
+
+	length = skb->len;
+
+	/* Mask interrupts from the ethercard. 
+	   SMP: We have to grab the lock here otherwise the IRQ handler
+	   on another CPU can flip window and race the IRQ mask set. We end
+	   up trashing the mcast filter not disabling irqs if we don't lock */
+	   
+	spin_lock_irqsave(&ei_local->page_lock, flags);
+	outb_p(0x00, e8390_base + EN0_IMR);
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+	
+	/*
+	 *	Slow phase with lock held.
+	 */
+	 
+	disable_irq_nosync(dev->irq);
+	
+	spin_lock(&ei_local->page_lock);
+	
+	ei_local->irqlock = 1;
+
+	send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
+	
+	/*
+	 * We have two Tx slots available for use. Find the first free
+	 * slot, and then perform some sanity checks. With two Tx bufs,
+	 * you get very close to transmitting back-to-back packets. With
+	 * only one Tx buf, the transmitter sits idle while you reload the
+	 * card, leaving a substantial gap between each transmitted packet.
+	 */
+
+	if (ei_local->tx1 == 0) 
+	{
+		output_page = ei_local->tx_start_page;
+		ei_local->tx1 = send_length;
+		if (ei_debug  &&  ei_local->tx2 > 0)
+			printk(KERN_DEBUG "%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
+				dev->name, ei_local->tx2, ei_local->lasttx, ei_local->txing);
+	}
+	else if (ei_local->tx2 == 0) 
+	{
+		output_page = ei_local->tx_start_page + TX_PAGES/2;
+		ei_local->tx2 = send_length;
+		if (ei_debug  &&  ei_local->tx1 > 0)
+			printk(KERN_DEBUG "%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
+				dev->name, ei_local->tx1, ei_local->lasttx, ei_local->txing);
+	}
+	else
+	{	/* We should never get here. */
+		if (ei_debug)
+			printk(KERN_DEBUG "%s: No Tx buffers free! tx1=%d tx2=%d last=%d\n",
+				dev->name, ei_local->tx1, ei_local->tx2, ei_local->lasttx);
+		ei_local->irqlock = 0;
+		netif_stop_queue(dev);
+		outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+		spin_unlock(&ei_local->page_lock);
+		enable_irq(dev->irq);
+		ei_local->stat.tx_errors++;
+		return 1;
+	}
+
+	/*
+	 * Okay, now upload the packet and trigger a send if the transmitter
+	 * isn't already sending. If it is busy, the interrupt handler will
+	 * trigger the send later, upon receiving a Tx done interrupt.
+	 */
+
+	if (length == skb->len)
+		ei_block_output(dev, length, skb->data, output_page);
+	else {
+		memset(packet, 0, ETH_ZLEN);
+		memcpy(packet, skb->data, skb->len);
+		ei_block_output(dev, length, packet, output_page);
+	}
+	
+	if (! ei_local->txing) 
+	{
+		ei_local->txing = 1;
+		NS8390_trigger_send(dev, send_length, output_page);
+		dev->trans_start = jiffies;
+		if (output_page == ei_local->tx_start_page) 
+		{
+			ei_local->tx1 = -1;
+			ei_local->lasttx = -1;
+		}
+		else 
+		{
+			ei_local->tx2 = -1;
+			ei_local->lasttx = -2;
+		}
+	}
+	else ei_local->txqueue++;
+
+	if (ei_local->tx1  &&  ei_local->tx2)
+		netif_stop_queue(dev);
+	else
+		netif_start_queue(dev);
+
+	/* Turn 8390 interrupts back on. */
+	ei_local->irqlock = 0;
+	outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+	
+	spin_unlock(&ei_local->page_lock);
+	enable_irq(dev->irq);
+
+	dev_kfree_skb (skb);
+	ei_local->stat.tx_bytes += send_length;
+    
+	return 0;
+}
+
+/**
+ * ax_interrupt - handle the interrupts from an 8390
+ * @irq: interrupt number
+ * @dev_id: a pointer to the net_device
+ * @regs: unused
+ *
+ * Handle the ether interface interrupts. We pull packets from
+ * the 8390 via the card specific functions and fire them at the networking
+ * stack. We also handle transmit completions and wake the transmit path if
+ * necessary. We also update the counters and do other housekeeping as
+ * needed.
+ */
+
+static irqreturn_t ax_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct net_device *dev = dev_id;
+	long e8390_base;
+	int interrupts, nr_serviced = 0, i;
+	struct ei_device *ei_local;
+    	int handled = 0;
+
+	if (dev == NULL) 
+	{
+		printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+		return IRQ_NONE;
+	}
+    
+	e8390_base = dev->base_addr;
+	ei_local = (struct ei_device *) netdev_priv(dev);
+
+	/*
+	 *	Protect the irq test too.
+	 */
+	 
+	spin_lock(&ei_local->page_lock);
+
+	if (ei_local->irqlock) 
+	{
+#if 1 /* This might just be an interrupt for a PCI device sharing this line */
+		/* The "irqlock" check is only for testing. */
+		printk(ei_local->irqlock
+			   ? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n"
+			   : "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n",
+			   dev->name, inb_p(e8390_base + EN0_ISR),
+			   inb_p(e8390_base + EN0_IMR));
+#endif
+		spin_unlock(&ei_local->page_lock);
+		return IRQ_NONE;
+	}
+    
+	if (ei_debug > 3)
+		printk(KERN_DEBUG "%s: interrupt(isr=%#2.2x).\n", dev->name,
+			   inb_p(e8390_base + EN0_ISR));
+
+	outb_p(0x00, e8390_base + EN0_ISR);
+	ei_local->irqlock = 1;
+   
+	/* !!Assumption!! -- we stay in page 0.	 Don't break this. */
+	while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
+		   && ++nr_serviced < MAX_SERVICE) 
+	{
+		if (!netif_running(dev) || (interrupts == 0xff)) {
+			if (ei_debug > 1)
+				printk(KERN_WARNING "%s: interrupt from stopped card\n", dev->name);
+			outb_p(interrupts, e8390_base + EN0_ISR);
+			interrupts = 0;
+			break;
+		}
+		handled = 1;
+
+		/* AX88190 bug fix. */
+		outb_p(interrupts, e8390_base + EN0_ISR);
+		for (i = 0; i < 10; i++) {
+			if (!(inb(e8390_base + EN0_ISR) & interrupts))
+				break;
+			outb_p(0, e8390_base + EN0_ISR);
+			outb_p(interrupts, e8390_base + EN0_ISR);
+		}
+		if (interrupts & ENISR_OVER) 
+			ei_rx_overrun(dev);
+		else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) 
+		{
+			/* Got a good (?) packet. */
+			ei_receive(dev);
+		}
+		/* Push the next to-transmit packet through. */
+		if (interrupts & ENISR_TX)
+			ei_tx_intr(dev);
+		else if (interrupts & ENISR_TX_ERR)
+			ei_tx_err(dev);
+
+		if (interrupts & ENISR_COUNTERS) 
+		{
+			ei_local->stat.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);
+			ei_local->stat.rx_crc_errors   += inb_p(e8390_base + EN0_COUNTER1);
+			ei_local->stat.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);
+		}
+	}
+    
+	if (interrupts && ei_debug) 
+	{
+		handled = 1;
+		if (nr_serviced >= MAX_SERVICE) 
+		{
+			/* 0xFF is valid for a card removal */
+			if(interrupts!=0xFF)
+				printk(KERN_WARNING "%s: Too much work at interrupt, status %#2.2x\n",
+				   dev->name, interrupts);
+			outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+		} else {
+			printk(KERN_WARNING "%s: unknown interrupt %#2x\n", dev->name, interrupts);
+			outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+		}
+	}
+
+	/* Turn 8390 interrupts back on. */
+	ei_local->irqlock = 0;
+	outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+
+	spin_unlock(&ei_local->page_lock);
+	return IRQ_RETVAL(handled);
+}
+
+/**
+ * ei_tx_err - handle transmitter error
+ * @dev: network device which threw the exception
+ *
+ * A transmitter error has happened. Most likely excess collisions (which
+ * is a fairly normal condition). If the error is one where the Tx will
+ * have been aborted, we try and send another one right away, instead of
+ * letting the failed packet sit and collect dust in the Tx buffer. This
+ * is a much better solution as it avoids kernel based Tx timeouts, and
+ * an unnecessary card reset.
+ *
+ * Called with lock held.
+ */
+
+static void ei_tx_err(struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
+	unsigned char txsr = inb_p(e8390_base+EN0_TSR);
+	unsigned char tx_was_aborted = txsr & (ENTSR_ABT+ENTSR_FU);
+
+#ifdef VERBOSE_ERROR_DUMP
+	printk(KERN_DEBUG "%s: transmitter error (%#2x): ", dev->name, txsr);
+	if (txsr & ENTSR_ABT)
+		printk("excess-collisions ");
+	if (txsr & ENTSR_ND)
+		printk("non-deferral ");
+	if (txsr & ENTSR_CRS)
+		printk("lost-carrier ");
+	if (txsr & ENTSR_FU)
+		printk("FIFO-underrun ");
+	if (txsr & ENTSR_CDH)
+		printk("lost-heartbeat ");
+	printk("\n");
+#endif
+
+	if (tx_was_aborted)
+		ei_tx_intr(dev);
+	else 
+	{
+		ei_local->stat.tx_errors++;
+		if (txsr & ENTSR_CRS) ei_local->stat.tx_carrier_errors++;
+		if (txsr & ENTSR_CDH) ei_local->stat.tx_heartbeat_errors++;
+		if (txsr & ENTSR_OWC) ei_local->stat.tx_window_errors++;
+	}
+}
+
+/**
+ * ei_tx_intr - transmit interrupt handler
+ * @dev: network device for which tx intr is handled
+ *
+ * We have finished a transmit: check for errors and then trigger the next
+ * packet to be sent. Called with lock held.
+ */
+
+static void ei_tx_intr(struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
+	int status = inb(e8390_base + EN0_TSR);
+    
+	/*
+	 * There are two Tx buffers, see which one finished, and trigger
+	 * the send of another one if it exists.
+	 */
+	ei_local->txqueue--;
+
+	if (ei_local->tx1 < 0) 
+	{
+		if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
+			printk(KERN_ERR "%s: bogus last_tx_buffer %d, tx1=%d.\n",
+				ei_local->name, ei_local->lasttx, ei_local->tx1);
+		ei_local->tx1 = 0;
+		if (ei_local->tx2 > 0) 
+		{
+			ei_local->txing = 1;
+			NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
+			dev->trans_start = jiffies;
+			ei_local->tx2 = -1,
+			ei_local->lasttx = 2;
+		}
+		else ei_local->lasttx = 20, ei_local->txing = 0;	
+	}
+	else if (ei_local->tx2 < 0) 
+	{
+		if (ei_local->lasttx != 2  &&  ei_local->lasttx != -2)
+			printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
+				ei_local->name, ei_local->lasttx, ei_local->tx2);
+		ei_local->tx2 = 0;
+		if (ei_local->tx1 > 0) 
+		{
+			ei_local->txing = 1;
+			NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
+			dev->trans_start = jiffies;
+			ei_local->tx1 = -1;
+			ei_local->lasttx = 1;
+		}
+		else
+			ei_local->lasttx = 10, ei_local->txing = 0;
+	}
+//	else printk(KERN_WARNING "%s: unexpected TX-done interrupt, lasttx=%d.\n",
+//			dev->name, ei_local->lasttx);
+
+	/* Minimize Tx latency: update the statistics after we restart TXing. */
+	if (status & ENTSR_COL)
+		ei_local->stat.collisions++;
+	if (status & ENTSR_PTX)
+		ei_local->stat.tx_packets++;
+	else 
+	{
+		ei_local->stat.tx_errors++;
+		if (status & ENTSR_ABT) 
+		{
+			ei_local->stat.tx_aborted_errors++;
+			ei_local->stat.collisions += 16;
+		}
+		if (status & ENTSR_CRS) 
+			ei_local->stat.tx_carrier_errors++;
+		if (status & ENTSR_FU) 
+			ei_local->stat.tx_fifo_errors++;
+		if (status & ENTSR_CDH)
+			ei_local->stat.tx_heartbeat_errors++;
+		if (status & ENTSR_OWC)
+			ei_local->stat.tx_window_errors++;
+	}
+	netif_wake_queue(dev);
+}
+
+/**
+ * ei_receive - receive some packets
+ * @dev: network device with which receive will be run
+ *
+ * We have a good packet(s), get it/them out of the buffers. 
+ * Called with lock held.
+ */
+
+static void ei_receive(struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
+	unsigned char rxing_page, this_frame, next_frame;
+	unsigned short current_offset;
+	int rx_pkt_count = 0;
+	struct e8390_pkt_hdr rx_frame;
+    
+	while (++rx_pkt_count < 10) 
+	{
+		int pkt_len, pkt_stat;
+		
+		/* Get the rx page (incoming packet pointer). */
+		rxing_page = inb_p(e8390_base + EN1_CURPAG -1);
+		
+		/* Remove one frame from the ring.  Boundary is always a page behind. */
+		this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
+		if (this_frame >= ei_local->stop_page)
+			this_frame = ei_local->rx_start_page;
+		
+		/* Someday we'll omit the previous, iff we never get this message.
+		   (There is at least one clone claimed to have a problem.)  
+		   
+		   Keep quiet if it looks like a card removal. One problem here
+		   is that some clones crash in roughly the same way.
+		 */
+		if (ei_debug > 0  &&  this_frame != ei_local->current_page && (this_frame!=0x0 || rxing_page!=0xFF))
+			printk(KERN_ERR "%s: mismatched read page pointers %2x vs %2x.\n",
+				   dev->name, this_frame, ei_local->current_page);
+		
+		if (this_frame == rxing_page)	/* Read all the frames? */
+			break;				/* Done for now */
+		
+		current_offset = this_frame << 8;
+		ei_get_8390_hdr(dev, &rx_frame, this_frame);
+		
+		pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr);
+		pkt_stat = rx_frame.status;
+		
+		next_frame = this_frame + 1 + ((pkt_len+4)>>8);
+		
+		if (pkt_len < 60  ||  pkt_len > 1518) 
+		{
+			if (ei_debug)
+				printk(KERN_DEBUG "%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n",
+					   dev->name, rx_frame.count, rx_frame.status,
+					   rx_frame.next);
+			ei_local->stat.rx_errors++;
+			ei_local->stat.rx_length_errors++;
+		}
+		 else if ((pkt_stat & 0x0F) == ENRSR_RXOK) 
+		{
+			struct sk_buff *skb;
+			
+			skb = dev_alloc_skb(pkt_len+2);
+			if (skb == NULL) 
+			{
+				if (ei_debug > 1)
+					printk(KERN_DEBUG "%s: Couldn't allocate a sk_buff of size %d.\n",
+						   dev->name, pkt_len);
+				ei_local->stat.rx_dropped++;
+				break;
+			}
+			else
+			{
+				skb_reserve(skb,2);	/* IP headers on 16 byte boundaries */
+				skb->dev = dev;
+				skb_put(skb, pkt_len);	/* Make room */
+				ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
+				skb->protocol=eth_type_trans(skb,dev);
+				netif_rx(skb);
+				dev->last_rx = jiffies;
+				ei_local->stat.rx_packets++;
+				ei_local->stat.rx_bytes += pkt_len;
+				if (pkt_stat & ENRSR_PHY)
+					ei_local->stat.multicast++;
+			}
+		} 
+		else 
+		{
+			if (ei_debug)
+				printk(KERN_DEBUG "%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+					   dev->name, rx_frame.status, rx_frame.next,
+					   rx_frame.count);
+			ei_local->stat.rx_errors++;
+			/* NB: The NIC counts CRC, frame and missed errors. */
+			if (pkt_stat & ENRSR_FO)
+				ei_local->stat.rx_fifo_errors++;
+		}
+		next_frame = rx_frame.next;
+		
+		/* This _should_ never happen: it's here for avoiding bad clones. */
+		if (next_frame >= ei_local->stop_page) {
+			printk("%s: next frame inconsistency, %#2x\n", dev->name,
+				   next_frame);
+			next_frame = ei_local->rx_start_page;
+		}
+		ei_local->current_page = next_frame;
+		outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
+	}
+
+	return;
+}
+
+/**
+ * ei_rx_overrun - handle receiver overrun
+ * @dev: network device which threw exception
+ *
+ * We have a receiver overrun: we have to kick the 8390 to get it started
+ * again. Problem is that you have to kick it exactly as NS prescribes in
+ * the updated datasheets, or "the NIC may act in an unpredictable manner."
+ * This includes causing "the NIC to defer indefinitely when it is stopped
+ * on a busy network."  Ugh.
+ * Called with lock held. Don't call this with the interrupts off or your
+ * computer will hate you - it takes 10ms or so. 
+ */
+
+static void ei_rx_overrun(struct net_device *dev)
+{
+	axnet_dev_t *info = (axnet_dev_t *)dev;
+	long e8390_base = dev->base_addr;
+	unsigned char was_txing, must_resend = 0;
+	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
+    
+	/*
+	 * Record whether a Tx was in progress and then issue the
+	 * stop command.
+	 */
+	was_txing = inb_p(e8390_base+E8390_CMD) & E8390_TRANS;
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+    
+	if (ei_debug > 1)
+		printk(KERN_DEBUG "%s: Receiver overrun.\n", dev->name);
+	ei_local->stat.rx_over_errors++;
+    
+	/* 
+	 * Wait a full Tx time (1.2ms) + some guard time, NS says 1.6ms total.
+	 * Early datasheets said to poll the reset bit, but now they say that
+	 * it "is not a reliable indicator and subsequently should be ignored."
+	 * We wait at least 10ms.
+	 */
+
+	mdelay(10);
+
+	/*
+	 * Reset RBCR[01] back to zero as per magic incantation.
+	 */
+	outb_p(0x00, e8390_base+EN0_RCNTLO);
+	outb_p(0x00, e8390_base+EN0_RCNTHI);
+
+	/*
+	 * See if any Tx was interrupted or not. According to NS, this
+	 * step is vital, and skipping it will cause no end of havoc.
+	 */
+
+	if (was_txing)
+	{ 
+		unsigned char tx_completed = inb_p(e8390_base+EN0_ISR) & (ENISR_TX+ENISR_TX_ERR);
+		if (!tx_completed)
+			must_resend = 1;
+	}
+
+	/*
+	 * Have to enter loopback mode and then restart the NIC before
+	 * you are allowed to slurp packets up off the ring.
+	 */
+	outb_p(E8390_TXOFF, e8390_base + EN0_TXCR);
+	outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
+
+	/*
+	 * Clear the Rx ring of all the debris, and ack the interrupt.
+	 */
+	ei_receive(dev);
+
+	/*
+	 * Leave loopback mode, and resend any packet that got stopped.
+	 */
+	outb_p(E8390_TXCONFIG | info->duplex_flag, e8390_base + EN0_TXCR); 
+	if (must_resend)
+    		outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START + E8390_TRANS, e8390_base + E8390_CMD);
+}
+
+/*
+ *	Collect the stats. This is called unlocked and from several contexts.
+ */
+ 
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+	long ioaddr = dev->base_addr;
+	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
+	unsigned long flags;
+    
+	/* If the card is stopped, just return the present stats. */
+	if (!netif_running(dev))
+		return &ei_local->stat;
+
+	spin_lock_irqsave(&ei_local->page_lock,flags);
+	/* Read the counter registers, assuming we are in page 0. */
+	ei_local->stat.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0);
+	ei_local->stat.rx_crc_errors   += inb_p(ioaddr + EN0_COUNTER1);
+	ei_local->stat.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2);
+	spin_unlock_irqrestore(&ei_local->page_lock, flags);
+    
+	return &ei_local->stat;
+}
+
+/**
+ * do_set_multicast_list - set/clear multicast filter
+ * @dev: net device for which multicast filter is adjusted
+ *
+ *	Set or clear the multicast filter for this adaptor. May be called
+ *	from a BH in 2.1.x. Must be called with lock held. 
+ */
+ 
+static void do_set_multicast_list(struct net_device *dev)
+{
+	long e8390_base = dev->base_addr;
+
+  	if(dev->flags&IFF_PROMISC)
+  		outb_p(E8390_RXCONFIG | 0x58, e8390_base + EN0_RXCR);
+	else if(dev->flags&IFF_ALLMULTI || dev->mc_list)
+  		outb_p(E8390_RXCONFIG | 0x48, e8390_base + EN0_RXCR);
+  	else
+  		outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR);
+}
+
+/*
+ *	Called without lock held. This is invoked from user context and may
+ *	be parallel to just about everything else. Its also fairly quick and
+ *	not called too often. Must protect against both bh and irq users
+ */
+
+static void set_multicast_list(struct net_device *dev)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev_lock(dev), flags);
+	do_set_multicast_list(dev);
+	spin_unlock_irqrestore(&dev_lock(dev), flags);
+}	
+
+/**
+ * axdev_setup - init rest of 8390 device struct
+ * @dev: network device structure to init
+ *
+ * Initialize the rest of the 8390 device structure.  Do NOT __init
+ * this, as it is used by 8390 based modular drivers too.
+ */
+
+static void axdev_setup(struct net_device *dev)
+{
+	struct ei_device *ei_local;
+	if (ei_debug > 1)
+		printk(version_8390);
+    
+	SET_MODULE_OWNER(dev);
+
+		
+	ei_local = (struct ei_device *)netdev_priv(dev);
+	spin_lock_init(&ei_local->page_lock);
+    
+	dev->hard_start_xmit = &ei_start_xmit;
+	dev->get_stats	= get_stats;
+	dev->set_multicast_list = &set_multicast_list;
+
+	ether_setup(dev);
+}
+
+/* This page of functions should be 8390 generic */
+/* Follow National Semi's recommendations for initializing the "NIC". */
+
+/**
+ * AX88190_init - initialize 8390 hardware
+ * @dev: network device to initialize
+ * @startp: boolean.  non-zero value to initiate chip processing
+ *
+ *	Must be called with lock held.
+ */
+
+static void AX88190_init(struct net_device *dev, int startp)
+{
+	axnet_dev_t *info = PRIV(dev);
+	long e8390_base = dev->base_addr;
+	struct ei_device *ei_local = (struct ei_device *) netdev_priv(dev);
+	int i;
+	int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+    
+	if(sizeof(struct e8390_pkt_hdr)!=4)
+    		panic("8390.c: header struct mispacked\n");    
+	/* Follow National Semi's recommendations for initing the DP83902. */
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD); /* 0x21 */
+	outb_p(endcfg, e8390_base + EN0_DCFG);	/* 0x48 or 0x49 */
+	/* Clear the remote byte count registers. */
+	outb_p(0x00,  e8390_base + EN0_RCNTLO);
+	outb_p(0x00,  e8390_base + EN0_RCNTHI);
+	/* Set to monitor and loopback mode -- this is vital!. */
+	outb_p(E8390_RXOFF|0x40, e8390_base + EN0_RXCR); /* 0x60 */
+	outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
+	/* Set the transmit page and receive ring. */
+	outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
+	ei_local->tx1 = ei_local->tx2 = 0;
+	outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
+	outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY);	/* 3c503 says 0x3f,NS0x26*/
+	ei_local->current_page = ei_local->rx_start_page;		/* assert boundary+1 */
+	outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
+	/* Clear the pending interrupts and mask. */
+	outb_p(0xFF, e8390_base + EN0_ISR);
+	outb_p(0x00,  e8390_base + EN0_IMR);
+    
+	/* Copy the station address into the DS8390 registers. */
+
+	outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base+E8390_CMD); /* 0x61 */
+	for(i = 0; i < 6; i++) 
+	{
+		outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS_SHIFT(i));
+		if(inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i])
+			printk(KERN_ERR "Hw. address read/write mismap %d\n",i);
+	}
+	/*
+	 * Initialize the multicast list to accept-all.  If we enable multicast
+	 * the higher levels can do the filtering.
+	 */
+	for (i = 0; i < 8; i++)
+		outb_p(0xff, e8390_base + EN1_MULT + i);
+
+	outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+	outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+	netif_start_queue(dev);
+	ei_local->tx1 = ei_local->tx2 = 0;
+	ei_local->txing = 0;
+
+	if (startp) 
+	{
+		outb_p(0xff,  e8390_base + EN0_ISR);
+		outb_p(ENISR_ALL,  e8390_base + EN0_IMR);
+		outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base+E8390_CMD);
+		outb_p(E8390_TXCONFIG | info->duplex_flag,
+		       e8390_base + EN0_TXCR); /* xmit on. */
+		/* 3c503 TechMan says rxconfig only after the NIC is started. */
+		outb_p(E8390_RXCONFIG | 0x40, e8390_base + EN0_RXCR); /* rx on, */
+		do_set_multicast_list(dev);	/* (re)load the mcast table */
+	}
+}
+
+/* Trigger a transmit start, assuming the length is valid. 
+   Always called with the page lock held */
+   
+static void NS8390_trigger_send(struct net_device *dev, unsigned int length,
+								int start_page)
+{
+	long e8390_base = dev->base_addr;
+ 	struct ei_device *ei_local __attribute((unused)) = (struct ei_device *) netdev_priv(dev);
+    
+	if (inb_p(e8390_base) & E8390_TRANS) 
+	{
+		printk(KERN_WARNING "%s: trigger_send() called with the transmitter busy.\n",
+			dev->name);
+		return;
+	}
+	outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
+	outb_p(length >> 8, e8390_base + EN0_TCNTHI);
+	outb_p(start_page, e8390_base + EN0_TPSR);
+	outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base+E8390_CMD);
+}
diff --git a/drivers/net/pcmcia/com20020_cs.c b/drivers/net/pcmcia/com20020_cs.c
new file mode 100644
index 000000000000..4294e1e3f156
--- /dev/null
+++ b/drivers/net/pcmcia/com20020_cs.c
@@ -0,0 +1,509 @@
+/*
+ * Linux ARCnet driver - COM20020 PCMCIA support
+ * 
+ * Written 1994-1999 by Avery Pennarun,
+ *    based on an ISA version by David Woodhouse.
+ * Derived from ibmtr_cs.c by Steve Kipisz (pcmcia-cs 3.1.4)
+ *    which was derived from pcnet_cs.c by David Hinds.
+ * Some additional portions derived from skeleton.c by Donald Becker.
+ *
+ * Special thanks to Contemporary Controls, Inc. (www.ccontrols.com)
+ *  for sponsoring the further development of this driver.
+ *
+ * **********************
+ *
+ * The original copyright of skeleton.c was as follows:
+ *
+ * skeleton.c Written 1993 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency.  This software may only be used
+ * and distributed according to the terms of the GNU General Public License as
+ * modified by SRC, incorporated herein by reference.
+ * 
+ * **********************
+ * Changes:
+ * Arnaldo Carvalho de Melo <acme@conectiva.com.br> - 08/08/2000
+ * - reorganize kmallocs in com20020_attach, checking all for failure
+ *   and releasing the previous allocations if one fails
+ * **********************
+ * 
+ * For more details, see drivers/net/arcnet.c
+ *
+ * **********************
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/arcdevice.h>
+#include <linux/com20020.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#define VERSION "arcnet: COM20020 PCMCIA support loaded.\n"
+
+#ifdef PCMCIA_DEBUG
+
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+
+static void regdump(struct net_device *dev)
+{
+    int ioaddr = dev->base_addr;
+    int count;
+    
+    printk("com20020 register dump:\n");
+    for (count = ioaddr; count < ioaddr + 16; count++)
+    {
+	if (!(count % 16))
+	    printk("\n%04X: ", count);
+	printk("%02X ", inb(count));
+    }
+    printk("\n");
+    
+    printk("buffer0 dump:\n");
+	/* set up the address register */
+        count = 0;
+	outb((count >> 8) | RDDATAflag | AUTOINCflag, _ADDR_HI);
+	outb(count & 0xff, _ADDR_LO);
+    
+    for (count = 0; count < 256+32; count++)
+    {
+	if (!(count % 16))
+	    printk("\n%04X: ", count);
+	
+	/* copy the data */
+	printk("%02X ", inb(_MEMDATA));
+    }
+    printk("\n");
+}
+
+#else
+
+#define DEBUG(n, args...) do { } while (0)
+static inline void regdump(struct net_device *dev) { }
+
+#endif
+
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int node;
+static int timeout = 3;
+static int backplane;
+static int clockp;
+static int clockm;
+
+module_param(node, int, 0);
+module_param(timeout, int, 0);
+module_param(backplane, int, 0);
+module_param(clockp, int, 0);
+module_param(clockm, int, 0);
+
+MODULE_LICENSE("GPL");
+
+/*====================================================================*/
+
+static void com20020_config(dev_link_t *link);
+static void com20020_release(dev_link_t *link);
+static int com20020_event(event_t event, int priority,
+                       event_callback_args_t *args);
+
+static dev_info_t dev_info = "com20020_cs";
+
+static dev_link_t *com20020_attach(void);
+static void com20020_detach(dev_link_t *);
+
+static dev_link_t *dev_list;
+
+/*====================================================================*/
+
+typedef struct com20020_dev_t {
+    struct net_device       *dev;
+    dev_node_t          node;
+} com20020_dev_t;
+
+/*======================================================================
+
+    com20020_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+======================================================================*/
+
+static dev_link_t *com20020_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    com20020_dev_t *info;
+    struct net_device *dev;
+    int ret;
+    struct arcnet_local *lp;
+    
+    DEBUG(0, "com20020_attach()\n");
+
+    /* Create new network device */
+    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
+    if (!link)
+	return NULL;
+
+    info = kmalloc(sizeof(struct com20020_dev_t), GFP_KERNEL);
+    if (!info)
+	goto fail_alloc_info;
+
+    dev = alloc_arcdev("");
+    if (!dev)
+	goto fail_alloc_dev;
+
+    memset(info, 0, sizeof(struct com20020_dev_t));
+    memset(link, 0, sizeof(struct dev_link_t));
+    lp = dev->priv;
+    lp->timeout = timeout;
+    lp->backplane = backplane;
+    lp->clockp = clockp;
+    lp->clockm = clockm & 3;
+    lp->hw.owner = THIS_MODULE;
+
+    /* fill in our module parameters as defaults */
+    dev->dev_addr[0] = node;
+
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    link->io.NumPorts1 = 16;
+    link->io.IOAddrLines = 16;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.Present = PRESENT_OPTION;
+
+
+    link->irq.Instance = info->dev = dev;
+    link->priv = info;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+        CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+        CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+        CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &com20020_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != 0) {
+        cs_error(link->handle, RegisterClient, ret);
+        com20020_detach(link);
+        return NULL;
+    }
+
+    return link;
+
+fail_alloc_dev:
+    kfree(info);
+fail_alloc_info:
+    kfree(link);
+    return NULL;
+} /* com20020_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void com20020_detach(dev_link_t *link)
+{
+    struct com20020_dev_t *info = link->priv;
+    dev_link_t **linkp;
+    struct net_device *dev; 
+    
+    DEBUG(1,"detach...\n");
+
+    DEBUG(0, "com20020_detach(0x%p)\n", link);
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+        if (*linkp == link) break;
+    if (*linkp == NULL)
+        return;
+
+    dev = info->dev;
+
+    if (link->dev) {
+	DEBUG(1,"unregister...\n");
+
+	unregister_netdev(dev);
+	    
+	/*
+	 * this is necessary because we register our IRQ separately
+	 * from card services.
+	 */
+	if (dev->irq)
+	    free_irq(dev->irq, dev);
+    }
+
+    if (link->state & DEV_CONFIG)
+        com20020_release(link);
+
+    if (link->handle)
+        pcmcia_deregister_client(link->handle);
+
+    /* Unlink device structure, free bits */
+    DEBUG(1,"unlinking...\n");
+    *linkp = link->next;
+    if (link->priv)
+    {
+	dev = info->dev;
+	if (dev)
+	{
+	    DEBUG(1,"kfree...\n");
+	    free_netdev(dev);
+	}
+	DEBUG(1,"kfree2...\n");
+	kfree(info);
+    }
+    DEBUG(1,"kfree3...\n");
+    kfree(link);
+
+} /* com20020_detach */
+
+/*======================================================================
+
+    com20020_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    device available to the system.
+
+======================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void com20020_config(dev_link_t *link)
+{
+    struct arcnet_local *lp;
+    client_handle_t handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    com20020_dev_t *info;
+    struct net_device *dev;
+    int i, last_ret, last_fn;
+    u_char buf[64];
+    int ioaddr;
+
+    handle = link->handle;
+    info = link->priv;
+    dev = info->dev;
+
+    DEBUG(1,"config...\n");
+
+    DEBUG(0, "com20020_config(0x%p)\n", link);
+
+    tuple.Attributes = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    link->conf.ConfigBase = parse.config.base;
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    DEBUG(1,"arcnet: baseport1 is %Xh\n", link->io.BasePort1);
+    i = !CS_SUCCESS;
+    if (!link->io.BasePort1)
+    {
+	for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x10)
+	{
+	    link->io.BasePort1 = ioaddr;
+	    i = pcmcia_request_io(link->handle, &link->io);
+	    if (i == CS_SUCCESS)
+		break;
+	}
+    }
+    else
+	i = pcmcia_request_io(link->handle, &link->io);
+    
+    if (i != CS_SUCCESS)
+    {
+	DEBUG(1,"arcnet: requestIO failed totally!\n");
+	goto failed;
+    }
+	
+    ioaddr = dev->base_addr = link->io.BasePort1;
+    DEBUG(1,"arcnet: got ioaddr %Xh\n", ioaddr);
+
+    DEBUG(1,"arcnet: request IRQ %d (%Xh/%Xh)\n",
+	   link->irq.AssignedIRQ,
+	   link->irq.IRQInfo1, link->irq.IRQInfo2);
+    i = pcmcia_request_irq(link->handle, &link->irq);
+    if (i != CS_SUCCESS)
+    {
+	DEBUG(1,"arcnet: requestIRQ failed totally!\n");
+	goto failed;
+    }
+
+    dev->irq = link->irq.AssignedIRQ;
+
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+
+    if (com20020_check(dev))
+    {
+	regdump(dev);
+	goto failed;
+    }
+    
+    lp = dev->priv;
+    lp->card_name = "PCMCIA COM20020";
+    lp->card_flags = ARC_CAN_10MBIT; /* pretend all of them can 10Mbit */
+
+    link->dev = &info->node;
+    link->state &= ~DEV_CONFIG_PENDING;
+    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+
+    i = com20020_found(dev, 0);	/* calls register_netdev */
+    
+    if (i != 0) {
+	DEBUG(1,KERN_NOTICE "com20020_cs: com20020_found() failed\n");
+	link->dev = NULL;
+	goto failed;
+    }
+
+    strcpy(info->node.dev_name, dev->name);
+
+    DEBUG(1,KERN_INFO "%s: port %#3lx, irq %d\n",
+           dev->name, dev->base_addr, dev->irq);
+    return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    DEBUG(1,"com20020_config failed...\n");
+    com20020_release(link);
+} /* com20020_config */
+
+/*======================================================================
+
+    After a card is removed, com20020_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void com20020_release(dev_link_t *link)
+{
+
+    DEBUG(1,"release...\n");
+
+    DEBUG(0, "com20020_release(0x%p)\n", link);
+
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+
+    link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
+}
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+======================================================================*/
+
+static int com20020_event(event_t event, int priority,
+			  event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    com20020_dev_t *info = link->priv;
+    struct net_device *dev = info->dev;
+
+    DEBUG(1, "com20020_event(0x%06x)\n", event);
+    
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+        link->state &= ~DEV_PRESENT;
+        if (link->state & DEV_CONFIG)
+            netif_device_detach(dev);
+        break;
+    case CS_EVENT_CARD_INSERTION:
+        link->state |= DEV_PRESENT;
+	com20020_config(link); 
+	break;
+    case CS_EVENT_PM_SUSPEND:
+        link->state |= DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+        if (link->state & DEV_CONFIG) {
+            if (link->open) {
+                netif_device_detach(dev);
+            }
+            pcmcia_release_configuration(link->handle);
+        }
+        break;
+    case CS_EVENT_PM_RESUME:
+        link->state &= ~DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+        if (link->state & DEV_CONFIG) {
+            pcmcia_request_configuration(link->handle, &link->conf);
+            if (link->open) {
+		int ioaddr = dev->base_addr;
+		struct arcnet_local *lp = dev->priv;
+		ARCRESET;
+            }
+        }
+        break;
+    }
+    return 0;
+} /* com20020_event */
+
+
+
+static struct pcmcia_driver com20020_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "com20020_cs",
+	},
+	.attach		= com20020_attach,
+	.detach		= com20020_detach,
+};
+
+static int __init init_com20020_cs(void)
+{
+	return pcmcia_register_driver(&com20020_cs_driver);
+}
+
+static void __exit exit_com20020_cs(void)
+{
+	pcmcia_unregister_driver(&com20020_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_com20020_cs);
+module_exit(exit_com20020_cs);
diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c
new file mode 100644
index 000000000000..0424865e8094
--- /dev/null
+++ b/drivers/net/pcmcia/fmvj18x_cs.c
@@ -0,0 +1,1286 @@
+/*======================================================================
+    fmvj18x_cs.c 2.8 2002/03/23
+
+    A fmvj18x (and its compatibles) PCMCIA client driver
+
+    Contributed by Shingo Fujimoto, shingo@flab.fujitsu.co.jp
+
+    TDK LAK-CD021 and CONTEC C-NET(PC)C support added by 
+    Nobuhiro Katayama, kata-n@po.iijnet.or.jp
+
+    The PCMCIA client code is based on code written by David Hinds.
+    Network code is based on the "FMV-18x driver" by Yutaka TAMIYA
+    but is actually largely Donald Becker's AT1700 driver, which
+    carries the following attribution:
+
+    Written 1993-94 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
+    of the GNU General Public License, incorporated herein by reference.
+    
+    The author may be reached as becker@scyld.com, or C/O
+    Scyld Computing Corporation
+    410 Severn Ave., Suite 210
+    Annapolis MD 21403
+   
+======================================================================*/
+
+#define DRV_NAME	"fmvj18x_cs"
+#define DRV_VERSION	"2.8"
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/crc32.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_DESCRIPTION("fmvj18x and compatible PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+/* SRAM configuration */
+/* 0:4KB*2 TX buffer   else:8KB*2 TX buffer */
+INT_MODULE_PARM(sram_config, 0);
+
+#ifdef PCMCIA_DEBUG
+INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version = DRV_NAME ".c " DRV_VERSION " 2002/03/23";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+/*
+    PCMCIA event handlers
+ */
+static void fmvj18x_config(dev_link_t *link);
+static int fmvj18x_get_hwinfo(dev_link_t *link, u_char *node_id);
+static int fmvj18x_setup_mfc(dev_link_t *link);
+static void fmvj18x_release(dev_link_t *link);
+static int fmvj18x_event(event_t event, int priority,
+			  event_callback_args_t *args);
+static dev_link_t *fmvj18x_attach(void);
+static void fmvj18x_detach(dev_link_t *);
+
+/*
+    LAN controller(MBH86960A) specific routines
+ */
+static int fjn_config(struct net_device *dev, struct ifmap *map);
+static int fjn_open(struct net_device *dev);
+static int fjn_close(struct net_device *dev);
+static int fjn_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t fjn_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void fjn_rx(struct net_device *dev);
+static void fjn_reset(struct net_device *dev);
+static struct net_device_stats *fjn_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static void fjn_tx_timeout(struct net_device *dev);
+static struct ethtool_ops netdev_ethtool_ops;
+
+static dev_info_t dev_info = "fmvj18x_cs";
+static dev_link_t *dev_list;
+
+/*
+    card type
+ */
+typedef enum { MBH10302, MBH10304, TDK, CONTEC, LA501, UNGERMANN, 
+	       XXX10304
+} cardtype_t;
+
+/*
+    driver specific data structure
+*/
+typedef struct local_info_t {
+    dev_link_t link;
+    dev_node_t node;
+    struct net_device_stats stats;
+    long open_time;
+    uint tx_started:1;
+    uint tx_queue;
+    u_short tx_queue_len;
+    cardtype_t cardtype;
+    u_short sent;
+    u_char mc_filter[8];
+} local_info_t;
+
+#define MC_FILTERBREAK 64
+
+/*====================================================================*/
+/* 
+    ioport offset from the base address 
+ */
+#define TX_STATUS               0 /* transmit status register */
+#define RX_STATUS               1 /* receive status register */
+#define TX_INTR                 2 /* transmit interrupt mask register */
+#define RX_INTR                 3 /* receive interrupt mask register */
+#define TX_MODE                 4 /* transmit mode register */
+#define RX_MODE                 5 /* receive mode register */
+#define CONFIG_0                6 /* configuration register 0 */
+#define CONFIG_1                7 /* configuration register 1 */
+
+#define NODE_ID                 8 /* node ID register            (bank 0) */
+#define MAR_ADR                 8 /* multicast address registers (bank 1) */
+
+#define DATAPORT                8 /* buffer mem port registers   (bank 2) */
+#define TX_START               10 /* transmit start register */
+#define COL_CTRL               11 /* 16 collision control register */
+#define BMPR12                 12 /* reserved */
+#define BMPR13                 13 /* reserved */
+#define RX_SKIP                14 /* skip received packet register */
+
+#define LAN_CTRL               16 /* LAN card control register */
+
+#define MAC_ID               0x1a /* hardware address */
+#define UNGERMANN_MAC_ID     0x18 /* UNGERMANN-BASS hardware address */
+
+/* 
+    control bits 
+ */
+#define ENA_TMT_OK           0x80
+#define ENA_TMT_REC          0x20
+#define ENA_COL              0x04
+#define ENA_16_COL           0x02
+#define ENA_TBUS_ERR         0x01
+
+#define ENA_PKT_RDY          0x80
+#define ENA_BUS_ERR          0x40
+#define ENA_LEN_ERR          0x08
+#define ENA_ALG_ERR          0x04
+#define ENA_CRC_ERR          0x02
+#define ENA_OVR_FLO          0x01
+
+/* flags */
+#define F_TMT_RDY            0x80 /* can accept new packet */
+#define F_NET_BSY            0x40 /* carrier is detected */
+#define F_TMT_OK             0x20 /* send packet successfully */
+#define F_SRT_PKT            0x10 /* short packet error */
+#define F_COL_ERR            0x04 /* collision error */
+#define F_16_COL             0x02 /* 16 collision error */
+#define F_TBUS_ERR           0x01 /* bus read error */
+
+#define F_PKT_RDY            0x80 /* packet(s) in buffer */
+#define F_BUS_ERR            0x40 /* bus read error */
+#define F_LEN_ERR            0x08 /* short packet */
+#define F_ALG_ERR            0x04 /* frame error */
+#define F_CRC_ERR            0x02 /* CRC error */
+#define F_OVR_FLO            0x01 /* overflow error */
+
+#define F_BUF_EMP            0x40 /* receive buffer is empty */
+
+#define F_SKP_PKT            0x05 /* drop packet in buffer */
+
+/* default bitmaps */
+#define D_TX_INTR  ( ENA_TMT_OK )
+#define D_RX_INTR  ( ENA_PKT_RDY | ENA_LEN_ERR \
+		   | ENA_ALG_ERR | ENA_CRC_ERR | ENA_OVR_FLO )
+#define TX_STAT_M  ( F_TMT_RDY )
+#define RX_STAT_M  ( F_PKT_RDY | F_LEN_ERR \
+                   | F_ALG_ERR | F_CRC_ERR | F_OVR_FLO )
+
+/* commands */
+#define D_TX_MODE            0x06 /* no tests, detect carrier */
+#define ID_MATCHED           0x02 /* (RX_MODE) */
+#define RECV_ALL             0x03 /* (RX_MODE) */
+#define CONFIG0_DFL          0x5a /* 16bit bus, 4K x 2 Tx queues */
+#define CONFIG0_DFL_1        0x5e /* 16bit bus, 8K x 2 Tx queues */
+#define CONFIG0_RST          0xda /* Data Link Controller off (CONFIG_0) */
+#define CONFIG0_RST_1        0xde /* Data Link Controller off (CONFIG_0) */
+#define BANK_0               0xa0 /* bank 0 (CONFIG_1) */
+#define BANK_1               0xa4 /* bank 1 (CONFIG_1) */
+#define BANK_2               0xa8 /* bank 2 (CONFIG_1) */
+#define CHIP_OFF             0x80 /* contrl chip power off (CONFIG_1) */
+#define DO_TX                0x80 /* do transmit packet */
+#define SEND_PKT             0x81 /* send a packet */
+#define AUTO_MODE            0x07 /* Auto skip packet on 16 col detected */
+#define MANU_MODE            0x03 /* Stop and skip packet on 16 col */
+#define TDK_AUTO_MODE        0x47 /* Auto skip packet on 16 col detected */
+#define TDK_MANU_MODE        0x43 /* Stop and skip packet on 16 col */
+#define INTR_OFF             0x0d /* LAN controller ignores interrupts */
+#define INTR_ON              0x1d /* LAN controller will catch interrupts */
+
+#define TX_TIMEOUT		((400*HZ)/1000)
+
+#define BANK_0U              0x20 /* bank 0 (CONFIG_1) */
+#define BANK_1U              0x24 /* bank 1 (CONFIG_1) */
+#define BANK_2U              0x28 /* bank 2 (CONFIG_1) */
+
+static dev_link_t *fmvj18x_attach(void)
+{
+    local_info_t *lp;
+    dev_link_t *link;
+    struct net_device *dev;
+    client_reg_t client_reg;
+    int ret;
+    
+    DEBUG(0, "fmvj18x_attach()\n");
+
+    /* Make up a FMVJ18x specific data structure */
+    dev = alloc_etherdev(sizeof(local_info_t));
+    if (!dev)
+	return NULL;
+    lp = netdev_priv(dev);
+    link = &lp->link;
+    link->priv = dev;
+
+    /* The io structure describes IO port mapping */
+    link->io.NumPorts1 = 32;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 5;
+
+    /* Interrupt setup */
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->irq.Handler = &fjn_interrupt;
+    link->irq.Instance = dev;
+    
+    /* General socket configuration */
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    /* The FMVJ18x specific entries in the device structure. */
+    SET_MODULE_OWNER(dev);
+    dev->hard_start_xmit = &fjn_start_xmit;
+    dev->set_config = &fjn_config;
+    dev->get_stats = &fjn_get_stats;
+    dev->set_multicast_list = &set_rx_mode;
+    dev->open = &fjn_open;
+    dev->stop = &fjn_close;
+#ifdef HAVE_TX_TIMEOUT
+    dev->tx_timeout = fjn_tx_timeout;
+    dev->watchdog_timeo = TX_TIMEOUT;
+#endif
+    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+    
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &fmvj18x_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != 0) {
+	cs_error(link->handle, RegisterClient, ret);
+	fmvj18x_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* fmvj18x_attach */
+
+/*====================================================================*/
+
+static void fmvj18x_detach(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    dev_link_t **linkp;
+    
+    DEBUG(0, "fmvj18x_detach(0x%p)\n", link);
+    
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    if (link->dev)
+	unregister_netdev(dev);
+
+    if (link->state & DEV_CONFIG)
+	fmvj18x_release(link);
+
+    /* Break the link with Card Services */
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+    
+    /* Unlink device structure, free pieces */
+    *linkp = link->next;
+    free_netdev(dev);
+} /* fmvj18x_detach */
+
+/*====================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static int mfc_try_io_port(dev_link_t *link)
+{
+    int i, ret;
+    static kio_addr_t serial_base[5] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, 0x0 };
+
+    for (i = 0; i < 5; i++) {
+	link->io.BasePort2 = serial_base[i];
+	link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+	if (link->io.BasePort2 == 0) {
+	    link->io.NumPorts2 = 0;
+	    printk(KERN_NOTICE "fmvj18x_cs: out of resource for serial\n");
+	}
+	ret = pcmcia_request_io(link->handle, &link->io);
+	if (ret == CS_SUCCESS) return ret;
+    }
+    return ret;
+}
+
+static int ungermann_try_io_port(dev_link_t *link)
+{
+    int ret;
+    kio_addr_t ioaddr;
+    /*
+	Ungermann-Bass Access/CARD accepts 0x300,0x320,0x340,0x360
+	0x380,0x3c0 only for ioport.
+    */
+    for (ioaddr = 0x300; ioaddr < 0x3e0; ioaddr += 0x20) {
+	link->io.BasePort1 = ioaddr;
+	ret = pcmcia_request_io(link->handle, &link->io);
+	if (ret == CS_SUCCESS) {
+	    /* calculate ConfigIndex value */
+	    link->conf.ConfigIndex = 
+		((link->io.BasePort1 & 0x0f0) >> 3) | 0x22;
+	    return ret;
+	}
+    }
+    return ret;	/* RequestIO failed */
+}
+
+static void fmvj18x_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    local_info_t *lp = netdev_priv(dev);
+    tuple_t tuple;
+    cisparse_t parse;
+    u_short buf[32];
+    int i, last_fn, last_ret, ret;
+    kio_addr_t ioaddr;
+    cardtype_t cardtype;
+    char *card_name = "unknown";
+    u_char *node_id;
+
+    DEBUG(0, "fmvj18x_config(0x%p)\n", link);
+
+    /*
+       This reads the card's CONFIG tuple to find its configuration
+       registers.
+    */
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    tuple.TupleData = (u_char *)buf;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    link->conf.ConfigBase = parse.config.base; 
+    link->conf.Present = parse.config.rmask[0];
+
+    tuple.DesiredTuple = CISTPL_FUNCE;
+    tuple.TupleOffset = 0;
+    if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) {
+	/* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+	CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+	link->conf.ConfigIndex = parse.cftable_entry.index;
+	tuple.DesiredTuple = CISTPL_MANFID;
+	if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS)
+	    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+	else
+	    buf[0] = 0xffff;
+	switch (le16_to_cpu(buf[0])) {
+	case MANFID_TDK:
+	    cardtype = TDK;
+	    if (le16_to_cpu(buf[1]) == PRODID_TDK_CF010) {
+		cs_status_t status;
+		pcmcia_get_status(handle, &status);
+		if (status.CardState & CS_EVENT_3VCARD)
+		    link->conf.Vcc = 33; /* inserted in 3.3V slot */
+	    } else if (le16_to_cpu(buf[1]) == PRODID_TDK_GN3410) {
+		/* MultiFunction Card */
+		link->conf.ConfigBase = 0x800;
+		link->conf.ConfigIndex = 0x47;
+		link->io.NumPorts2 = 8;
+	    }
+	    break;
+	case MANFID_CONTEC:
+	    cardtype = CONTEC;
+	    break;
+	case MANFID_FUJITSU:
+	    if (le16_to_cpu(buf[1]) == PRODID_FUJITSU_MBH10302)
+                /* RATOC REX-5588/9822/4886's PRODID are 0004(=MBH10302),
+                   but these are MBH10304 based card. */ 
+		cardtype = MBH10304;
+	    else if (le16_to_cpu(buf[1]) == PRODID_FUJITSU_MBH10304)
+		cardtype = MBH10304;
+	    else
+		cardtype = LA501;
+	    break;
+	default:
+	    cardtype = MBH10304;
+	}
+    } else {
+	/* old type card */
+	tuple.DesiredTuple = CISTPL_MANFID;
+	if (pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS)
+	    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+	else
+	    buf[0] = 0xffff;
+	switch (le16_to_cpu(buf[0])) {
+	case MANFID_FUJITSU:
+	    if (le16_to_cpu(buf[1]) == PRODID_FUJITSU_MBH10304) {
+		cardtype = XXX10304;    /* MBH10304 with buggy CIS */
+	        link->conf.ConfigIndex = 0x20;
+	    } else {
+		cardtype = MBH10302;    /* NextCom NC5310, etc. */
+		link->conf.ConfigIndex = 1;
+	    }
+	    break;
+	case MANFID_UNGERMANN:
+	    cardtype = UNGERMANN;
+	    break;
+	default:
+	    cardtype = MBH10302;
+	    link->conf.ConfigIndex = 1;
+	}
+    }
+
+    if (link->io.NumPorts2 != 0) {
+    	link->irq.Attributes =
+		IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT;
+	ret = mfc_try_io_port(link);
+	if (ret != CS_SUCCESS) goto cs_failed;
+    } else if (cardtype == UNGERMANN) {
+	ret = ungermann_try_io_port(link);
+	if (ret != CS_SUCCESS) goto cs_failed;
+    } else { 
+	CS_CHECK(RequestIO, pcmcia_request_io(link->handle, &link->io));
+    }
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+    dev->irq = link->irq.AssignedIRQ;
+    dev->base_addr = link->io.BasePort1;
+
+    if (link->io.BasePort2 != 0)
+	fmvj18x_setup_mfc(link);
+
+    ioaddr = dev->base_addr;
+
+    /* Reset controller */
+    if (sram_config == 0) 
+	outb(CONFIG0_RST, ioaddr + CONFIG_0);
+    else
+	outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
+
+    /* Power On chip and select bank 0 */
+    if (cardtype == MBH10302)
+	outb(BANK_0, ioaddr + CONFIG_1);
+    else
+	outb(BANK_0U, ioaddr + CONFIG_1);
+    
+    /* Set hardware address */
+    switch (cardtype) {
+    case MBH10304:
+    case TDK:
+    case LA501:
+    case CONTEC:
+	tuple.DesiredTuple = CISTPL_FUNCE;
+	tuple.TupleOffset = 0;
+	CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+	tuple.TupleOffset = 0;
+	CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+	if (cardtype == MBH10304) {
+	    /* MBH10304's CIS_FUNCE is corrupted */
+	    node_id = &(tuple.TupleData[5]);
+	    card_name = "FMV-J182";
+	} else {
+	    while (tuple.TupleData[0] != CISTPL_FUNCE_LAN_NODE_ID ) {
+		CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(handle, &tuple));
+		CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+	    }
+	    node_id = &(tuple.TupleData[2]);
+	    if( cardtype == TDK ) {
+		card_name = "TDK LAK-CD021";
+	    } else if( cardtype == LA501 ) {
+		card_name = "LA501";
+	    } else {
+		card_name = "C-NET(PC)C";
+	    }
+	}
+	/* Read MACID from CIS */
+	for (i = 0; i < 6; i++)
+	    dev->dev_addr[i] = node_id[i];
+	break;
+    case UNGERMANN:
+	/* Read MACID from register */
+	for (i = 0; i < 6; i++) 
+	    dev->dev_addr[i] = inb(ioaddr + UNGERMANN_MAC_ID + i);
+	card_name = "Access/CARD";
+	break;
+    case XXX10304:
+	/* Read MACID from Buggy CIS */
+	if (fmvj18x_get_hwinfo(link, tuple.TupleData) == -1) {
+	    printk(KERN_NOTICE "fmvj18x_cs: unable to read hardware net address.\n");
+	    goto failed;
+	}
+	for (i = 0 ; i < 6; i++) {
+	    dev->dev_addr[i] = tuple.TupleData[i];
+	}
+	card_name = "FMV-J182";
+	break;
+    case MBH10302:
+    default:
+	/* Read MACID from register */
+	for (i = 0; i < 6; i++) 
+	    dev->dev_addr[i] = inb(ioaddr + MAC_ID + i);
+	card_name = "FMV-J181";
+	break;
+    }
+
+    lp->cardtype = cardtype;
+    link->dev = &lp->node;
+    link->state &= ~DEV_CONFIG_PENDING;
+    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+
+    if (register_netdev(dev) != 0) {
+	printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n");
+	link->dev = NULL;
+	goto failed;
+    }
+
+    strcpy(lp->node.dev_name, dev->name);
+
+    /* print current configuration */
+    printk(KERN_INFO "%s: %s, sram %s, port %#3lx, irq %d, hw_addr ", 
+	   dev->name, card_name, sram_config == 0 ? "4K TX*2" : "8K TX*2", 
+	   dev->base_addr, dev->irq);
+    for (i = 0; i < 6; i++)
+	printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+
+    return;
+    
+cs_failed:
+    /* All Card Services errors end up here */
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    fmvj18x_release(link);
+    link->state &= ~DEV_CONFIG_PENDING;
+
+} /* fmvj18x_config */
+/*====================================================================*/
+
+static int fmvj18x_get_hwinfo(dev_link_t *link, u_char *node_id)
+{
+    win_req_t req;
+    memreq_t mem;
+    u_char __iomem *base;
+    int i, j;
+
+    /* Allocate a small memory window */
+    req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+    req.Base = 0; req.Size = 0;
+    req.AccessSpeed = 0;
+    i = pcmcia_request_window(&link->handle, &req, &link->win);
+    if (i != CS_SUCCESS) {
+	cs_error(link->handle, RequestWindow, i);
+	return -1;
+    }
+
+    base = ioremap(req.Base, req.Size);
+    mem.Page = 0;
+    mem.CardOffset = 0;
+    pcmcia_map_mem_page(link->win, &mem);
+
+    /*
+     *  MBH10304 CISTPL_FUNCE_LAN_NODE_ID format
+     *  22 0d xx xx xx 04 06 yy yy yy yy yy yy ff
+     *  'xx' is garbage.
+     *  'yy' is MAC address.
+    */ 
+    for (i = 0; i < 0x200; i++) {
+	if (readb(base+i*2) == 0x22) {	
+	    if (readb(base+(i-1)*2) == 0xff
+	     && readb(base+(i+5)*2) == 0x04
+	     && readb(base+(i+6)*2) == 0x06
+	     && readb(base+(i+13)*2) == 0xff) 
+		break;
+	}
+    }
+
+    if (i != 0x200) {
+	for (j = 0 ; j < 6; j++,i++) {
+	    node_id[j] = readb(base+(i+7)*2);
+	}
+    }
+
+    iounmap(base);
+    j = pcmcia_release_window(link->win);
+    if (j != CS_SUCCESS)
+	cs_error(link->handle, ReleaseWindow, j);
+    return (i != 0x200) ? 0 : -1;
+
+} /* fmvj18x_get_hwinfo */
+/*====================================================================*/
+
+static int fmvj18x_setup_mfc(dev_link_t *link)
+{
+    win_req_t req;
+    memreq_t mem;
+    u_char __iomem *base;
+    int i, j;
+    struct net_device *dev = link->priv;
+    kio_addr_t ioaddr;
+
+    /* Allocate a small memory window */
+    req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+    req.Base = 0; req.Size = 0;
+    req.AccessSpeed = 0;
+    i = pcmcia_request_window(&link->handle, &req, &link->win);
+    if (i != CS_SUCCESS) {
+	cs_error(link->handle, RequestWindow, i);
+	return -1;
+    }
+
+    base = ioremap(req.Base, req.Size);
+    mem.Page = 0;
+    mem.CardOffset = 0;
+    pcmcia_map_mem_page(link->win, &mem);
+
+    ioaddr = dev->base_addr;
+    writeb(0x47, base+0x800);	/* Config Option Register of LAN */
+    writeb(0x0, base+0x802);	/* Config and Status Register */
+
+    writeb(ioaddr & 0xff, base+0x80a);		/* I/O Base(Low) of LAN */
+    writeb((ioaddr >> 8) & 0xff, base+0x80c);	/* I/O Base(High) of LAN */
+   
+    writeb(0x45, base+0x820);	/* Config Option Register of Modem */
+    writeb(0x8, base+0x822);	/* Config and Status Register */
+
+    iounmap(base);
+    j = pcmcia_release_window(link->win);
+    if (j != CS_SUCCESS)
+	cs_error(link->handle, ReleaseWindow, j);
+    return 0;
+
+}
+/*====================================================================*/
+
+static void fmvj18x_release(dev_link_t *link)
+{
+
+    DEBUG(0, "fmvj18x_release(0x%p)\n", link);
+
+    /* Don't bother checking to see if these succeed or not */
+    pcmcia_release_window(link->win);
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    
+    link->state &= ~DEV_CONFIG;
+}
+
+/*====================================================================*/
+
+static int fmvj18x_event(event_t event, int priority,
+			  event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    struct net_device *dev = link->priv;
+
+    DEBUG(1, "fmvj18x_event(0x%06x)\n", event);
+    
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG)
+	    netif_device_detach(dev);
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	fmvj18x_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG) {
+	    if (link->open)
+		netif_device_detach(dev);
+	    pcmcia_release_configuration(link->handle);
+	}
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG) {
+	    pcmcia_request_configuration(link->handle, &link->conf);
+	    if (link->open) {
+		fjn_reset(dev);
+		netif_device_attach(dev);
+	    }
+	}
+	break;
+    }
+    return 0;
+} /* fmvj18x_event */
+
+static struct pcmcia_driver fmvj18x_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "fmvj18x_cs",
+	},
+	.attach		= fmvj18x_attach,
+	.detach		= fmvj18x_detach,
+};
+
+static int __init init_fmvj18x_cs(void)
+{
+	return pcmcia_register_driver(&fmvj18x_cs_driver);
+}
+
+static void __exit exit_fmvj18x_cs(void)
+{
+	pcmcia_unregister_driver(&fmvj18x_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_fmvj18x_cs);
+module_exit(exit_fmvj18x_cs);
+
+/*====================================================================*/
+
+static irqreturn_t fjn_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct net_device *dev = dev_id;
+    local_info_t *lp = netdev_priv(dev);
+    kio_addr_t ioaddr;
+    unsigned short tx_stat, rx_stat;
+
+    if (lp == NULL) {
+        printk(KERN_NOTICE "fjn_interrupt(): irq %d for "
+	       "unknown device.\n", irq);
+        return IRQ_NONE;
+    }
+    ioaddr = dev->base_addr;
+
+    /* avoid multiple interrupts */
+    outw(0x0000, ioaddr + TX_INTR);
+
+    /* wait for a while */
+    udelay(1);
+
+    /* get status */
+    tx_stat = inb(ioaddr + TX_STATUS);
+    rx_stat = inb(ioaddr + RX_STATUS);
+
+    /* clear status */
+    outb(tx_stat, ioaddr + TX_STATUS);
+    outb(rx_stat, ioaddr + RX_STATUS);
+    
+    DEBUG(4, "%s: interrupt, rx_status %02x.\n", dev->name, rx_stat);
+    DEBUG(4, "               tx_status %02x.\n", tx_stat);
+    
+    if (rx_stat || (inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
+	/* there is packet(s) in rx buffer */
+	fjn_rx(dev);
+    }
+    if (tx_stat & F_TMT_RDY) {
+	lp->stats.tx_packets += lp->sent ;
+        lp->sent = 0 ;
+	if (lp->tx_queue) {
+	    outb(DO_TX | lp->tx_queue, ioaddr + TX_START);
+	    lp->sent = lp->tx_queue ;
+	    lp->tx_queue = 0;
+	    lp->tx_queue_len = 0;
+	    dev->trans_start = jiffies;
+	} else {
+	    lp->tx_started = 0;
+	}
+	netif_wake_queue(dev);
+    }
+    DEBUG(4, "%s: exiting interrupt,\n", dev->name);
+    DEBUG(4, "    tx_status %02x, rx_status %02x.\n", tx_stat, rx_stat);
+
+    outb(D_TX_INTR, ioaddr + TX_INTR);
+    outb(D_RX_INTR, ioaddr + RX_INTR);
+    return IRQ_HANDLED;
+
+} /* fjn_interrupt */
+
+/*====================================================================*/
+
+static void fjn_tx_timeout(struct net_device *dev)
+{
+    struct local_info_t *lp = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+
+    printk(KERN_NOTICE "%s: transmit timed out with status %04x, %s?\n",
+	   dev->name, htons(inw(ioaddr + TX_STATUS)),
+	   inb(ioaddr + TX_STATUS) & F_TMT_RDY
+	   ? "IRQ conflict" : "network cable problem");
+    printk(KERN_NOTICE "%s: timeout registers: %04x %04x %04x "
+	   "%04x %04x %04x %04x %04x.\n",
+	   dev->name, htons(inw(ioaddr + 0)),
+	   htons(inw(ioaddr + 2)), htons(inw(ioaddr + 4)),
+	   htons(inw(ioaddr + 6)), htons(inw(ioaddr + 8)),
+	   htons(inw(ioaddr +10)), htons(inw(ioaddr +12)),
+	   htons(inw(ioaddr +14)));
+    lp->stats.tx_errors++;
+    /* ToDo: We should try to restart the adaptor... */
+    local_irq_disable();
+    fjn_reset(dev);
+
+    lp->tx_started = 0;
+    lp->tx_queue = 0;
+    lp->tx_queue_len = 0;
+    lp->sent = 0;
+    lp->open_time = jiffies;
+    local_irq_enable();
+    netif_wake_queue(dev);
+}
+
+static int fjn_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+    struct local_info_t *lp = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    short length = skb->len;
+    
+    if (length < ETH_ZLEN)
+    {
+    	skb = skb_padto(skb, ETH_ZLEN);
+    	if (skb == NULL)
+    		return 0;
+    	length = ETH_ZLEN;
+    }
+
+    netif_stop_queue(dev);
+
+    {
+	unsigned char *buf = skb->data;
+
+	if (length > ETH_FRAME_LEN) {
+	    printk(KERN_NOTICE "%s: Attempting to send a large packet"
+		   " (%d bytes).\n", dev->name, length);
+	    return 1;
+	}
+
+	DEBUG(4, "%s: Transmitting a packet of length %lu.\n",
+	      dev->name, (unsigned long)skb->len);
+	lp->stats.tx_bytes += skb->len;
+
+	/* Disable both interrupts. */
+	outw(0x0000, ioaddr + TX_INTR);
+
+	/* wait for a while */
+	udelay(1);
+
+	outw(length, ioaddr + DATAPORT);
+	outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+
+	lp->tx_queue++;
+	lp->tx_queue_len += ((length+3) & ~1);
+
+	if (lp->tx_started == 0) {
+	    /* If the Tx is idle, always trigger a transmit. */
+	    outb(DO_TX | lp->tx_queue, ioaddr + TX_START);
+	    lp->sent = lp->tx_queue ;
+	    lp->tx_queue = 0;
+	    lp->tx_queue_len = 0;
+	    dev->trans_start = jiffies;
+	    lp->tx_started = 1;
+	    netif_start_queue(dev);
+	} else {
+	    if( sram_config == 0 ) {
+		if (lp->tx_queue_len < (4096 - (ETH_FRAME_LEN +2)) )
+		    /* Yes, there is room for one more packet. */
+		    netif_start_queue(dev);
+	    } else {
+		if (lp->tx_queue_len < (8192 - (ETH_FRAME_LEN +2)) && 
+						lp->tx_queue < 127 )
+		    /* Yes, there is room for one more packet. */
+		    netif_start_queue(dev);
+	    }
+	}
+
+	/* Re-enable interrupts */
+	outb(D_TX_INTR, ioaddr + TX_INTR);
+	outb(D_RX_INTR, ioaddr + RX_INTR);
+    }
+    dev_kfree_skb (skb);
+
+    return 0;
+} /* fjn_start_xmit */
+
+/*====================================================================*/
+
+static void fjn_reset(struct net_device *dev)
+{
+    struct local_info_t *lp = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    int i;
+
+    DEBUG(4, "fjn_reset(%s) called.\n",dev->name);
+
+    /* Reset controller */
+    if( sram_config == 0 ) 
+	outb(CONFIG0_RST, ioaddr + CONFIG_0);
+    else
+	outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
+
+    /* Power On chip and select bank 0 */
+    if (lp->cardtype == MBH10302)
+	outb(BANK_0, ioaddr + CONFIG_1);
+    else
+	outb(BANK_0U, ioaddr + CONFIG_1);
+
+    /* Set Tx modes */
+    outb(D_TX_MODE, ioaddr + TX_MODE);
+    /* set Rx modes */
+    outb(ID_MATCHED, ioaddr + RX_MODE);
+
+    /* Set hardware address */
+    for (i = 0; i < 6; i++) 
+        outb(dev->dev_addr[i], ioaddr + NODE_ID + i);
+
+    /* Switch to bank 1 */
+    if (lp->cardtype == MBH10302)
+	outb(BANK_1, ioaddr + CONFIG_1);
+    else
+	outb(BANK_1U, ioaddr + CONFIG_1);
+
+    /* set the multicast table to accept none. */
+    for (i = 0; i < 6; i++) 
+        outb(0x00, ioaddr + MAR_ADR + i);
+
+    /* Switch to bank 2 (runtime mode) */
+    if (lp->cardtype == MBH10302)
+	outb(BANK_2, ioaddr + CONFIG_1);
+    else
+	outb(BANK_2U, ioaddr + CONFIG_1);
+
+    /* set 16col ctrl bits */
+    if( lp->cardtype == TDK || lp->cardtype == CONTEC) 
+        outb(TDK_AUTO_MODE, ioaddr + COL_CTRL);
+    else
+        outb(AUTO_MODE, ioaddr + COL_CTRL);
+
+    /* clear Reserved Regs */
+    outb(0x00, ioaddr + BMPR12);
+    outb(0x00, ioaddr + BMPR13);
+
+    /* reset Skip packet reg. */
+    outb(0x01, ioaddr + RX_SKIP);
+
+    /* Enable Tx and Rx */
+    if( sram_config == 0 )
+	outb(CONFIG0_DFL, ioaddr + CONFIG_0);
+    else
+	outb(CONFIG0_DFL_1, ioaddr + CONFIG_0);
+
+    /* Init receive pointer ? */
+    inw(ioaddr + DATAPORT);
+    inw(ioaddr + DATAPORT);
+
+    /* Clear all status */
+    outb(0xff, ioaddr + TX_STATUS);
+    outb(0xff, ioaddr + RX_STATUS);
+
+    if (lp->cardtype == MBH10302)
+	outb(INTR_OFF, ioaddr + LAN_CTRL);
+
+    /* Turn on Rx interrupts */
+    outb(D_TX_INTR, ioaddr + TX_INTR);
+    outb(D_RX_INTR, ioaddr + RX_INTR);
+
+    /* Turn on interrupts from LAN card controller */
+    if (lp->cardtype == MBH10302)
+	outb(INTR_ON, ioaddr + LAN_CTRL);
+} /* fjn_reset */
+
+/*====================================================================*/
+
+static void fjn_rx(struct net_device *dev)
+{
+    struct local_info_t *lp = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    int boguscount = 10;	/* 5 -> 10: by agy 19940922 */
+
+    DEBUG(4, "%s: in rx_packet(), rx_status %02x.\n",
+	  dev->name, inb(ioaddr + RX_STATUS));
+
+    while ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == 0) {
+	u_short status = inw(ioaddr + DATAPORT);
+
+	DEBUG(4, "%s: Rxing packet mode %02x status %04x.\n",
+	      dev->name, inb(ioaddr + RX_MODE), status);
+#ifndef final_version
+	if (status == 0) {
+	    outb(F_SKP_PKT, ioaddr + RX_SKIP);
+	    break;
+	}
+#endif
+	if ((status & 0xF0) != 0x20) {	/* There was an error. */
+	    lp->stats.rx_errors++;
+	    if (status & F_LEN_ERR) lp->stats.rx_length_errors++;
+	    if (status & F_ALG_ERR) lp->stats.rx_frame_errors++;
+	    if (status & F_CRC_ERR) lp->stats.rx_crc_errors++;
+	    if (status & F_OVR_FLO) lp->stats.rx_over_errors++;
+	} else {
+	    u_short pkt_len = inw(ioaddr + DATAPORT);
+	    /* Malloc up new buffer. */
+	    struct sk_buff *skb;
+
+	    if (pkt_len > 1550) {
+		printk(KERN_NOTICE "%s: The FMV-18x claimed a very "
+		       "large packet, size %d.\n", dev->name, pkt_len);
+		outb(F_SKP_PKT, ioaddr + RX_SKIP);
+		lp->stats.rx_errors++;
+		break;
+	    }
+	    skb = dev_alloc_skb(pkt_len+2);
+	    if (skb == NULL) {
+		printk(KERN_NOTICE "%s: Memory squeeze, dropping "
+		       "packet (len %d).\n", dev->name, pkt_len);
+		outb(F_SKP_PKT, ioaddr + RX_SKIP);
+		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);
+
+#ifdef PCMCIA_DEBUG
+	    if (pc_debug > 5) {
+		int i;
+		printk(KERN_DEBUG "%s: Rxed packet of length %d: ",
+		       dev->name, pkt_len);
+		for (i = 0; i < 14; i++)
+		    printk(" %02x", skb->data[i]);
+		printk(".\n");
+	    }
+#endif
+
+	    netif_rx(skb);
+	    dev->last_rx = jiffies;
+	    lp->stats.rx_packets++;
+	    lp->stats.rx_bytes += pkt_len;
+	}
+	if (--boguscount <= 0)
+	    break;
+    }
+
+    /* If any worth-while packets have been received, dev_rint()
+	   has done a netif_wake_queue() for us and will work on them
+	   when we get to the bottom-half routine. */
+/*
+    if (lp->cardtype != TDK) {
+	int i;
+	for (i = 0; i < 20; i++) {
+	    if ((inb(ioaddr + RX_MODE) & F_BUF_EMP) == F_BUF_EMP)
+		break;
+	    (void)inw(ioaddr + DATAPORT);  /+ dummy status read +/
+	    outb(F_SKP_PKT, ioaddr + RX_SKIP);
+	}
+
+	if (i > 0)
+	    DEBUG(5, "%s: Exint Rx packet with mode %02x after "
+		  "%d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i);
+    }
+*/
+
+    return;
+} /* fjn_rx */
+
+/*====================================================================*/
+
+static void netdev_get_drvinfo(struct net_device *dev,
+			       struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+	sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr);
+}
+
+#ifdef PCMCIA_DEBUG
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+	return pc_debug;
+}
+
+static void netdev_set_msglevel(struct net_device *dev, u32 level)
+{
+	pc_debug = level;
+}
+#endif /* PCMCIA_DEBUG */
+
+static struct ethtool_ops netdev_ethtool_ops = {
+	.get_drvinfo		= netdev_get_drvinfo,
+#ifdef PCMCIA_DEBUG
+	.get_msglevel		= netdev_get_msglevel,
+	.set_msglevel		= netdev_set_msglevel,
+#endif /* PCMCIA_DEBUG */
+};
+
+static int fjn_config(struct net_device *dev, struct ifmap *map){
+    return 0;
+}
+
+static int fjn_open(struct net_device *dev)
+{
+    struct local_info_t *lp = netdev_priv(dev);
+    dev_link_t *link = &lp->link;
+
+    DEBUG(4, "fjn_open('%s').\n", dev->name);
+
+    if (!DEV_OK(link))
+	return -ENODEV;
+    
+    link->open++;
+    
+    fjn_reset(dev);
+    
+    lp->tx_started = 0;
+    lp->tx_queue = 0;
+    lp->tx_queue_len = 0;
+    lp->open_time = jiffies;
+    netif_start_queue(dev);
+    
+    return 0;
+} /* fjn_open */
+
+/*====================================================================*/
+
+static int fjn_close(struct net_device *dev)
+{
+    struct local_info_t *lp = netdev_priv(dev);
+    dev_link_t *link = &lp->link;
+    kio_addr_t ioaddr = dev->base_addr;
+
+    DEBUG(4, "fjn_close('%s').\n", dev->name);
+
+    lp->open_time = 0;
+    netif_stop_queue(dev);
+
+    /* Set configuration register 0 to disable Tx and Rx. */
+    if( sram_config == 0 ) 
+	outb(CONFIG0_RST ,ioaddr + CONFIG_0);
+    else
+	outb(CONFIG0_RST_1 ,ioaddr + CONFIG_0);
+
+    /* Update the statistics -- ToDo. */
+
+    /* Power-down the chip.  Green, green, green! */
+    outb(CHIP_OFF ,ioaddr + CONFIG_1);
+
+    /* Set the ethernet adaptor disable IRQ */
+    if (lp->cardtype == MBH10302)
+	outb(INTR_OFF, ioaddr + LAN_CTRL);
+
+    link->open--;
+
+    return 0;
+} /* fjn_close */
+
+/*====================================================================*/
+
+static struct net_device_stats *fjn_get_stats(struct net_device *dev)
+{
+    local_info_t *lp = netdev_priv(dev);
+    return &lp->stats;
+} /* fjn_get_stats */
+
+/*====================================================================*/
+
+/*
+  Set the multicast/promiscuous mode for this adaptor.
+*/
+
+static void set_rx_mode(struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+    struct local_info_t *lp = netdev_priv(dev);
+    u_char mc_filter[8];		 /* Multicast hash filter */
+    u_long flags;
+    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;
+	     i++, mclist = mclist->next) {
+	    unsigned int bit =
+	    	ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f;
+	    mc_filter[bit >> 3] |= (1 << bit);
+	}
+    }
+
+    local_irq_save(flags); 
+    if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) {
+	int saved_bank = inb(ioaddr + CONFIG_1);
+	/* Switch to bank 1 and set the multicast table. */
+	outb(0xe4, ioaddr + CONFIG_1);
+	for (i = 0; i < 8; i++)
+	    outb(mc_filter[i], ioaddr + 8 + i);
+	memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter));
+	outb(saved_bank, ioaddr + CONFIG_1);
+    }
+    local_irq_restore(flags);
+}
diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c
new file mode 100644
index 000000000000..3107ccfe8f3d
--- /dev/null
+++ b/drivers/net/pcmcia/ibmtr_cs.c
@@ -0,0 +1,535 @@
+/*======================================================================
+
+    A PCMCIA token-ring driver for IBM-based cards
+
+    This driver supports the IBM PCMCIA Token-Ring Card.
+    Written by Steve Kipisz, kipisz@vnet.ibm.com or
+                             bungy@ibm.net
+
+    Written 1995,1996.
+
+    This code is based on pcnet_cs.c from David Hinds.
+    
+    V2.2.0 February 1999 - Mike Phillips phillim@amtrak.com
+
+    Linux V2.2.x presented significant changes to the underlying
+    ibmtr.c code.  Mainly the code became a lot more organized and
+    modular.
+
+    This caused the old PCMCIA Token Ring driver to give up and go 
+    home early. Instead of just patching the old code to make it 
+    work, the PCMCIA code has been streamlined, updated and possibly
+    improved.
+
+    This code now only contains code required for the Card Services.
+    All we do here is set the card up enough so that the real ibmtr.c
+    driver can find it and work with it properly.
+
+    i.e. We set up the io port, irq, mmio memory and shared ram
+    memory.  This enables ibmtr_probe in ibmtr.c to find the card and
+    configure it as though it was a normal ISA and/or PnP card.
+
+    CHANGES
+
+    v2.2.5 April 1999 Mike Phillips (phillim@amtrak.com)
+    Obscure bug fix, required changed to ibmtr.c not ibmtr_cs.c
+    
+    v2.2.7 May 1999 Mike Phillips (phillim@amtrak.com)
+    Updated to version 2.2.7 to match the first version of the kernel
+    that the modification to ibmtr.c were incorporated into.
+    
+    v2.2.17 July 2000 Burt Silverman (burts@us.ibm.com)
+    Address translation feature of PCMCIA controller is usable so
+    memory windows can be placed in High memory (meaning above
+    0xFFFFF.)
+
+======================================================================*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/module.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/trdevice.h>
+#include <linux/ibmtr.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#define PCMCIA
+#include "../tokenring/ibmtr.c"
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"ibmtr_cs.c 1.10   1996/01/06 05:19:00 (Steve Kipisz)\n"
+"           2.2.7  1999/05/03 12:00:00 (Mike Phillips)\n"
+"           2.4.2  2001/30/28 Midnight (Burt Silverman)\n";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+/* MMIO base address */
+static u_long mmiobase = 0xce000;
+
+/* SRAM base address */
+static u_long srambase = 0xd0000;
+
+/* SRAM size 8,16,32,64 */
+static u_long sramsize = 64;
+
+/* Ringspeed 4,16 */
+static int ringspeed = 16;
+
+module_param(mmiobase, ulong, 0);
+module_param(srambase, ulong, 0);
+module_param(sramsize, ulong, 0);
+module_param(ringspeed, int, 0);
+MODULE_LICENSE("GPL");
+
+/*====================================================================*/
+
+static void ibmtr_config(dev_link_t *link);
+static void ibmtr_hw_setup(struct net_device *dev, u_int mmiobase);
+static void ibmtr_release(dev_link_t *link);
+static int ibmtr_event(event_t event, int priority,
+                       event_callback_args_t *args);
+
+static dev_info_t dev_info = "ibmtr_cs";
+
+static dev_link_t *ibmtr_attach(void);
+static void ibmtr_detach(dev_link_t *);
+
+static dev_link_t *dev_list;
+
+extern int ibmtr_probe_card(struct net_device *dev);
+extern irqreturn_t tok_interrupt (int irq, void *dev_id, struct pt_regs *regs);
+
+/*====================================================================*/
+
+typedef struct ibmtr_dev_t {
+    dev_link_t		link;
+    struct net_device	*dev;
+    dev_node_t          node;
+    window_handle_t     sram_win_handle;
+    struct tok_info	*ti;
+} ibmtr_dev_t;
+
+static void netdev_get_drvinfo(struct net_device *dev,
+			       struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, "ibmtr_cs");
+}
+
+static struct ethtool_ops netdev_ethtool_ops = {
+	.get_drvinfo		= netdev_get_drvinfo,
+};
+
+/*======================================================================
+
+    ibmtr_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+======================================================================*/
+
+static dev_link_t *ibmtr_attach(void)
+{
+    ibmtr_dev_t *info;
+    dev_link_t *link;
+    struct net_device *dev;
+    client_reg_t client_reg;
+    int ret;
+    
+    DEBUG(0, "ibmtr_attach()\n");
+
+    /* Create new token-ring device */
+    info = kmalloc(sizeof(*info), GFP_KERNEL); 
+    if (!info) return NULL;
+    memset(info,0,sizeof(*info));
+    dev = alloc_trdev(sizeof(struct tok_info));
+    if (!dev) { 
+	kfree(info); 
+	return NULL;
+    } 
+
+    link = &info->link;
+    link->priv = info;
+    info->ti = netdev_priv(dev);
+
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+    link->io.NumPorts1 = 4;
+    link->io.IOAddrLines = 16;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->irq.Handler = &tok_interrupt;
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.Present = PRESENT_OPTION;
+
+    link->irq.Instance = info->dev = dev;
+    
+    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+        CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+        CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+        CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &ibmtr_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != 0) {
+        cs_error(link->handle, RegisterClient, ret);
+	goto out_detach;
+    }
+
+out:
+    return link;
+
+out_detach:
+    ibmtr_detach(link);
+    link = NULL;
+    goto out;
+} /* ibmtr_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void ibmtr_detach(dev_link_t *link)
+{
+    struct ibmtr_dev_t *info = link->priv;
+    dev_link_t **linkp;
+    struct net_device *dev;
+
+    DEBUG(0, "ibmtr_detach(0x%p)\n", link);
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+        if (*linkp == link) break;
+    if (*linkp == NULL)
+        return;
+
+    dev = info->dev;
+
+    if (link->dev)
+	unregister_netdev(dev);
+
+    {
+	struct tok_info *ti = netdev_priv(dev);
+	del_timer_sync(&(ti->tr_timer));
+    }
+    if (link->state & DEV_CONFIG)
+        ibmtr_release(link);
+
+    if (link->handle)
+        pcmcia_deregister_client(link->handle);
+
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    free_netdev(dev);
+    kfree(info); 
+} /* ibmtr_detach */
+
+/*======================================================================
+
+    ibmtr_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    token-ring device available to the system.
+
+======================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void ibmtr_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    ibmtr_dev_t *info = link->priv;
+    struct net_device *dev = info->dev;
+    struct tok_info *ti = netdev_priv(dev);
+    tuple_t tuple;
+    cisparse_t parse;
+    win_req_t req;
+    memreq_t mem;
+    int i, last_ret, last_fn;
+    u_char buf[64];
+
+    DEBUG(0, "ibmtr_config(0x%p)\n", link);
+
+    tuple.Attributes = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    link->conf.ConfigBase = parse.config.base;
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    link->conf.ConfigIndex = 0x61;
+
+    /* Determine if this is PRIMARY or ALTERNATE. */
+
+    /* Try PRIMARY card at 0xA20-0xA23 */
+    link->io.BasePort1 = 0xA20;
+    i = pcmcia_request_io(link->handle, &link->io);
+    if (i != CS_SUCCESS) {
+	/* Couldn't get 0xA20-0xA23.  Try ALTERNATE at 0xA24-0xA27. */
+	link->io.BasePort1 = 0xA24;
+	CS_CHECK(RequestIO, pcmcia_request_io(link->handle, &link->io));
+    }
+    dev->base_addr = link->io.BasePort1;
+
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(link->handle, &link->irq));
+    dev->irq = link->irq.AssignedIRQ;
+    ti->irq = link->irq.AssignedIRQ;
+    ti->global_int_enable=GLOBAL_INT_ENABLE+((dev->irq==9) ? 2 : dev->irq);
+
+    /* Allocate the MMIO memory window */
+    req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+    req.Attributes |= WIN_USE_WAIT;
+    req.Base = 0; 
+    req.Size = 0x2000;
+    req.AccessSpeed = 250;
+    CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &link->win));
+
+    mem.CardOffset = mmiobase;
+    mem.Page = 0;
+    CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem));
+    ti->mmio = ioremap(req.Base, req.Size);
+
+    /* Allocate the SRAM memory window */
+    req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+    req.Attributes |= WIN_USE_WAIT;
+    req.Base = 0;
+    req.Size = sramsize * 1024;
+    req.AccessSpeed = 250;
+    CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &info->sram_win_handle));
+
+    mem.CardOffset = srambase;
+    mem.Page = 0;
+    CS_CHECK(MapMemPage, pcmcia_map_mem_page(info->sram_win_handle, &mem));
+
+    ti->sram_base = mem.CardOffset >> 12;
+    ti->sram_virt = ioremap(req.Base, req.Size);
+    ti->sram_phys = req.Base;
+
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link->handle, &link->conf));
+
+    /*  Set up the Token-Ring Controller Configuration Register and
+        turn on the card.  Check the "Local Area Network Credit Card
+        Adapters Technical Reference"  SC30-3585 for this info.  */
+    ibmtr_hw_setup(dev, mmiobase);
+
+    link->dev = &info->node;
+    link->state &= ~DEV_CONFIG_PENDING;
+    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+
+    i = ibmtr_probe_card(dev);
+    if (i != 0) {
+	printk(KERN_NOTICE "ibmtr_cs: register_netdev() failed\n");
+	link->dev = NULL;
+	goto failed;
+    }
+
+    strcpy(info->node.dev_name, dev->name);
+
+    printk(KERN_INFO "%s: port %#3lx, irq %d,",
+           dev->name, dev->base_addr, dev->irq);
+    printk (" mmio %#5lx,", (u_long)ti->mmio);
+    printk (" sram %#5lx,", (u_long)ti->sram_base << 12);
+    printk ("\n" KERN_INFO "  hwaddr=");
+    for (i = 0; i < TR_ALEN; i++)
+        printk("%02X", dev->dev_addr[i]);
+    printk("\n");
+    return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    ibmtr_release(link);
+} /* ibmtr_config */
+
+/*======================================================================
+
+    After a card is removed, ibmtr_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void ibmtr_release(dev_link_t *link)
+{
+    ibmtr_dev_t *info = link->priv;
+    struct net_device *dev = info->dev;
+
+    DEBUG(0, "ibmtr_release(0x%p)\n", link);
+
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    if (link->win) {
+	struct tok_info *ti = netdev_priv(dev);
+	iounmap(ti->mmio);
+	pcmcia_release_window(link->win);
+	pcmcia_release_window(info->sram_win_handle);
+    }
+
+    link->state &= ~DEV_CONFIG;
+}
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+======================================================================*/
+
+static int ibmtr_event(event_t event, int priority,
+                       event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    ibmtr_dev_t *info = link->priv;
+    struct net_device *dev = info->dev;
+
+    DEBUG(1, "ibmtr_event(0x%06x)\n", event);
+
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+        link->state &= ~DEV_PRESENT;
+        if (link->state & DEV_CONFIG) {
+	    /* set flag to bypass normal interrupt code */
+	    struct tok_info *priv = netdev_priv(dev);
+	    priv->sram_phys |= 1;
+	    netif_device_detach(dev);
+        }
+        break;
+    case CS_EVENT_CARD_INSERTION:
+        link->state |= DEV_PRESENT;
+	ibmtr_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+        link->state |= DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+        if (link->state & DEV_CONFIG) {
+            if (link->open)
+		netif_device_detach(dev);
+            pcmcia_release_configuration(link->handle);
+        }
+        break;
+    case CS_EVENT_PM_RESUME:
+        link->state &= ~DEV_SUSPEND;
+        /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+        if (link->state & DEV_CONFIG) {
+            pcmcia_request_configuration(link->handle, &link->conf);
+            if (link->open) {
+		ibmtr_probe(dev);	/* really? */
+		netif_device_attach(dev);
+            }
+        }
+        break;
+    }
+    return 0;
+} /* ibmtr_event */
+
+/*====================================================================*/
+
+static void ibmtr_hw_setup(struct net_device *dev, u_int mmiobase)
+{
+    int i;
+
+    /* Bizarre IBM behavior, there are 16 bits of information we
+       need to set, but the card only allows us to send 4 bits at a 
+       time.  For each byte sent to base_addr, bits 7-4 tell the
+       card which part of the 16 bits we are setting, bits 3-0 contain 
+       the actual information */
+
+    /* First nibble provides 4 bits of mmio */
+    i = (mmiobase >> 16) & 0x0F;
+    outb(i, dev->base_addr);
+
+    /* Second nibble provides 3 bits of mmio */
+    i = 0x10 | ((mmiobase >> 12) & 0x0E);
+    outb(i, dev->base_addr);
+
+    /* Third nibble, hard-coded values */
+    i = 0x26;
+    outb(i, dev->base_addr);
+
+    /* Fourth nibble sets shared ram page size */
+
+    /* 8 = 00, 16 = 01, 32 = 10, 64 = 11 */          
+    i = (sramsize >> 4) & 0x07;
+    i = ((i == 4) ? 3 : i) << 2;
+    i |= 0x30;
+
+    if (ringspeed == 16)
+	i |= 2;
+    if (dev->base_addr == 0xA24)
+	i |= 1;
+    outb(i, dev->base_addr);
+
+    /* 0x40 will release the card for use */
+    outb(0x40, dev->base_addr);
+
+    return;
+}
+
+static struct pcmcia_driver ibmtr_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "ibmtr_cs",
+	},
+	.attach		= ibmtr_attach,
+	.detach		= ibmtr_detach,
+};
+
+static int __init init_ibmtr_cs(void)
+{
+	return pcmcia_register_driver(&ibmtr_cs_driver);
+}
+
+static void __exit exit_ibmtr_cs(void)
+{
+	pcmcia_unregister_driver(&ibmtr_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_ibmtr_cs);
+module_exit(exit_ibmtr_cs);
diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c
new file mode 100644
index 000000000000..4603807fcafb
--- /dev/null
+++ b/drivers/net/pcmcia/nmclan_cs.c
@@ -0,0 +1,1699 @@
+/* ----------------------------------------------------------------------------
+Linux PCMCIA ethernet adapter driver for the New Media Ethernet LAN.
+  nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao
+
+  The Ethernet LAN uses the Advanced Micro Devices (AMD) Am79C940 Media
+  Access Controller for Ethernet (MACE).  It is essentially the Am2150
+  PCMCIA Ethernet card contained in the Am2150 Demo Kit.
+
+Written by Roger C. Pao <rpao@paonet.org>
+  Copyright 1995 Roger C. Pao
+  Linux 2.5 cleanups Copyright Red Hat 2003
+
+  This software may be used and distributed according to the terms of
+  the GNU General Public License.
+
+Ported to Linux 1.3.* network driver environment by
+  Matti Aarnio <mea@utu.fi>
+
+References
+
+  Am2150 Technical Reference Manual, Revision 1.0, August 17, 1993
+  Am79C940 (MACE) Data Sheet, 1994
+  Am79C90 (C-LANCE) Data Sheet, 1994
+  Linux PCMCIA Programmer's Guide v1.17
+  /usr/src/linux/net/inet/dev.c, Linux kernel 1.2.8
+
+  Eric Mears, New Media Corporation
+  Tom Pollard, New Media Corporation
+  Dean Siasoyco, New Media Corporation
+  Ken Lesniak, Silicon Graphics, Inc. <lesniak@boston.sgi.com>
+  Donald Becker <becker@scyld.com>
+  David Hinds <dahinds@users.sourceforge.net>
+
+  The Linux client driver is based on the 3c589_cs.c client driver by
+  David Hinds.
+
+  The Linux network driver outline is based on the 3c589_cs.c driver,
+  the 8390.c driver, and the example skeleton.c kernel code, which are
+  by Donald Becker.
+
+  The Am2150 network driver hardware interface code is based on the
+  OS/9000 driver for the New Media Ethernet LAN by Eric Mears.
+
+  Special thanks for testing and help in debugging this driver goes
+  to Ken Lesniak.
+
+-------------------------------------------------------------------------------
+Driver Notes and Issues
+-------------------------------------------------------------------------------
+
+1. Developed on a Dell 320SLi
+   PCMCIA Card Services 2.6.2
+   Linux dell 1.2.10 #1 Thu Jun 29 20:23:41 PDT 1995 i386
+
+2. rc.pcmcia may require loading pcmcia_core with io_speed=300:
+   'insmod pcmcia_core.o io_speed=300'.
+   This will avoid problems with fast systems which causes rx_framecnt
+   to return random values.
+
+3. If hot extraction does not work for you, use 'ifconfig eth0 down'
+   before extraction.
+
+4. There is a bad slow-down problem in this driver.
+
+5. Future: Multicast processing.  In the meantime, do _not_ compile your
+   kernel with multicast ip enabled.
+
+-------------------------------------------------------------------------------
+History
+-------------------------------------------------------------------------------
+Log: nmclan_cs.c,v
+ * 2.5.75-ac1 2003/07/11 Alan Cox <alan@redhat.com>
+ * Fixed hang on card eject as we probe it
+ * Cleaned up to use new style locking.
+ *
+ * Revision 0.16  1995/07/01  06:42:17  rpao
+ * Bug fix: nmclan_reset() called CardServices incorrectly.
+ *
+ * Revision 0.15  1995/05/24  08:09:47  rpao
+ * Re-implement MULTI_TX dev->tbusy handling.
+ *
+ * Revision 0.14  1995/05/23  03:19:30  rpao
+ * Added, in nmclan_config(), "tuple.Attributes = 0;".
+ * Modified MACE ID check to ignore chip revision level.
+ * Avoid tx_free_frames race condition between _start_xmit and _interrupt.
+ *
+ * Revision 0.13  1995/05/18  05:56:34  rpao
+ * Statistics changes.
+ * Bug fix: nmclan_reset did not enable TX and RX: call restore_multicast_list.
+ * Bug fix: mace_interrupt checks ~MACE_IMR_DEFAULT.  Fixes driver lockup.
+ *
+ * Revision 0.12  1995/05/14  00:12:23  rpao
+ * Statistics overhaul.
+ *
+
+95/05/13 rpao	V0.10a
+		Bug fix: MACE statistics counters used wrong I/O ports.
+		Bug fix: mace_interrupt() needed to allow statistics to be
+		processed without RX or TX interrupts pending.
+95/05/11 rpao	V0.10
+		Multiple transmit request processing.
+		Modified statistics to use MACE counters where possible.
+95/05/10 rpao	V0.09 Bug fix: Must use IO_DATA_PATH_WIDTH_AUTO.
+		*Released
+95/05/10 rpao	V0.08
+		Bug fix: Make all non-exported functions private by using
+		static keyword.
+		Bug fix: Test IntrCnt _before_ reading MACE_IR.
+95/05/10 rpao	V0.07 Statistics.
+95/05/09 rpao	V0.06 Fix rx_framecnt problem by addition of PCIC wait states.
+
+---------------------------------------------------------------------------- */
+
+#define DRV_NAME	"nmclan_cs"
+#define DRV_VERSION	"0.16"
+
+
+/* ----------------------------------------------------------------------------
+Conditional Compilation Options
+---------------------------------------------------------------------------- */
+
+#define MULTI_TX			0
+#define RESET_ON_TIMEOUT		1
+#define TX_INTERRUPTABLE		1
+#define RESET_XILINX			0
+
+/* ----------------------------------------------------------------------------
+Include Files
+---------------------------------------------------------------------------- */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/bitops.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+/* ----------------------------------------------------------------------------
+Defines
+---------------------------------------------------------------------------- */
+
+#define ETHER_ADDR_LEN			ETH_ALEN
+					/* 6 bytes in an Ethernet Address */
+#define MACE_LADRF_LEN			8
+					/* 8 bytes in Logical Address Filter */
+
+/* Loop Control Defines */
+#define MACE_MAX_IR_ITERATIONS		10
+#define MACE_MAX_RX_ITERATIONS		12
+	/*
+	TBD: Dean brought this up, and I assumed the hardware would
+	handle it:
+
+	If MACE_MAX_RX_ITERATIONS is > 1, rx_framecnt may still be
+	non-zero when the isr exits.  We may not get another interrupt
+	to process the remaining packets for some time.
+	*/
+
+/*
+The Am2150 has a Xilinx XC3042 field programmable gate array (FPGA)
+which manages the interface between the MACE and the PCMCIA bus.  It
+also includes buffer management for the 32K x 8 SRAM to control up to
+four transmit and 12 receive frames at a time.
+*/
+#define AM2150_MAX_TX_FRAMES		4
+#define AM2150_MAX_RX_FRAMES		12
+
+/* Am2150 Ethernet Card I/O Mapping */
+#define AM2150_RCV			0x00
+#define AM2150_XMT			0x04
+#define AM2150_XMT_SKIP			0x09
+#define AM2150_RCV_NEXT			0x0A
+#define AM2150_RCV_FRAME_COUNT		0x0B
+#define AM2150_MACE_BANK		0x0C
+#define AM2150_MACE_BASE		0x10
+
+/* MACE Registers */
+#define MACE_RCVFIFO			0
+#define MACE_XMTFIFO			1
+#define MACE_XMTFC			2
+#define MACE_XMTFS			3
+#define MACE_XMTRC			4
+#define MACE_RCVFC			5
+#define MACE_RCVFS			6
+#define MACE_FIFOFC			7
+#define MACE_IR				8
+#define MACE_IMR			9
+#define MACE_PR				10
+#define MACE_BIUCC			11
+#define MACE_FIFOCC			12
+#define MACE_MACCC			13
+#define MACE_PLSCC			14
+#define MACE_PHYCC			15
+#define MACE_CHIPIDL			16
+#define MACE_CHIPIDH			17
+#define MACE_IAC			18
+/* Reserved */
+#define MACE_LADRF			20
+#define MACE_PADR			21
+/* Reserved */
+/* Reserved */
+#define MACE_MPC			24
+/* Reserved */
+#define MACE_RNTPC			26
+#define MACE_RCVCC			27
+/* Reserved */
+#define MACE_UTR			29
+#define MACE_RTR1			30
+#define MACE_RTR2			31
+
+/* MACE Bit Masks */
+#define MACE_XMTRC_EXDEF		0x80
+#define MACE_XMTRC_XMTRC		0x0F
+
+#define MACE_XMTFS_XMTSV		0x80
+#define MACE_XMTFS_UFLO			0x40
+#define MACE_XMTFS_LCOL			0x20
+#define MACE_XMTFS_MORE			0x10
+#define MACE_XMTFS_ONE			0x08
+#define MACE_XMTFS_DEFER		0x04
+#define MACE_XMTFS_LCAR			0x02
+#define MACE_XMTFS_RTRY			0x01
+
+#define MACE_RCVFS_RCVSTS		0xF000
+#define MACE_RCVFS_OFLO			0x8000
+#define MACE_RCVFS_CLSN			0x4000
+#define MACE_RCVFS_FRAM			0x2000
+#define MACE_RCVFS_FCS			0x1000
+
+#define MACE_FIFOFC_RCVFC		0xF0
+#define MACE_FIFOFC_XMTFC		0x0F
+
+#define MACE_IR_JAB			0x80
+#define MACE_IR_BABL			0x40
+#define MACE_IR_CERR			0x20
+#define MACE_IR_RCVCCO			0x10
+#define MACE_IR_RNTPCO			0x08
+#define MACE_IR_MPCO			0x04
+#define MACE_IR_RCVINT			0x02
+#define MACE_IR_XMTINT			0x01
+
+#define MACE_MACCC_PROM			0x80
+#define MACE_MACCC_DXMT2PD		0x40
+#define MACE_MACCC_EMBA			0x20
+#define MACE_MACCC_RESERVED		0x10
+#define MACE_MACCC_DRCVPA		0x08
+#define MACE_MACCC_DRCVBC		0x04
+#define MACE_MACCC_ENXMT		0x02
+#define MACE_MACCC_ENRCV		0x01
+
+#define MACE_PHYCC_LNKFL		0x80
+#define MACE_PHYCC_DLNKTST		0x40
+#define MACE_PHYCC_REVPOL		0x20
+#define MACE_PHYCC_DAPC			0x10
+#define MACE_PHYCC_LRT			0x08
+#define MACE_PHYCC_ASEL			0x04
+#define MACE_PHYCC_RWAKE		0x02
+#define MACE_PHYCC_AWAKE		0x01
+
+#define MACE_IAC_ADDRCHG		0x80
+#define MACE_IAC_PHYADDR		0x04
+#define MACE_IAC_LOGADDR		0x02
+
+#define MACE_UTR_RTRE			0x80
+#define MACE_UTR_RTRD			0x40
+#define MACE_UTR_RPA			0x20
+#define MACE_UTR_FCOLL			0x10
+#define MACE_UTR_RCVFCSE		0x08
+#define MACE_UTR_LOOP_INCL_MENDEC	0x06
+#define MACE_UTR_LOOP_NO_MENDEC		0x04
+#define MACE_UTR_LOOP_EXTERNAL		0x02
+#define MACE_UTR_LOOP_NONE		0x00
+#define MACE_UTR_RESERVED		0x01
+
+/* Switch MACE register bank (only 0 and 1 are valid) */
+#define MACEBANK(win_num) outb((win_num), ioaddr + AM2150_MACE_BANK)
+
+#define MACE_IMR_DEFAULT \
+  (0xFF - \
+    ( \
+      MACE_IR_CERR | \
+      MACE_IR_RCVCCO | \
+      MACE_IR_RNTPCO | \
+      MACE_IR_MPCO | \
+      MACE_IR_RCVINT | \
+      MACE_IR_XMTINT \
+    ) \
+  )
+#undef MACE_IMR_DEFAULT
+#define MACE_IMR_DEFAULT 0x00 /* New statistics handling: grab everything */
+
+#define TX_TIMEOUT		((400*HZ)/1000)
+
+/* ----------------------------------------------------------------------------
+Type Definitions
+---------------------------------------------------------------------------- */
+
+typedef struct _mace_statistics {
+    /* MACE_XMTFS */
+    int xmtsv;
+    int uflo;
+    int lcol;
+    int more;
+    int one;
+    int defer;
+    int lcar;
+    int rtry;
+
+    /* MACE_XMTRC */
+    int exdef;
+    int xmtrc;
+
+    /* RFS1--Receive Status (RCVSTS) */
+    int oflo;
+    int clsn;
+    int fram;
+    int fcs;
+
+    /* RFS2--Runt Packet Count (RNTPC) */
+    int rfs_rntpc;
+
+    /* RFS3--Receive Collision Count (RCVCC) */
+    int rfs_rcvcc;
+
+    /* MACE_IR */
+    int jab;
+    int babl;
+    int cerr;
+    int rcvcco;
+    int rntpco;
+    int mpco;
+
+    /* MACE_MPC */
+    int mpc;
+
+    /* MACE_RNTPC */
+    int rntpc;
+
+    /* MACE_RCVCC */
+    int rcvcc;
+} mace_statistics;
+
+typedef struct _mace_private {
+    dev_link_t link;
+    dev_node_t node;
+    struct net_device_stats linux_stats; /* Linux statistics counters */
+    mace_statistics mace_stats; /* MACE chip statistics counters */
+
+    /* restore_multicast_list() state variables */
+    int multicast_ladrf[MACE_LADRF_LEN]; /* Logical address filter */
+    int multicast_num_addrs;
+
+    char tx_free_frames; /* Number of free transmit frame buffers */
+    char tx_irq_disabled; /* MACE TX interrupt disabled */
+    
+    spinlock_t bank_lock; /* Must be held if you step off bank 0 */
+} mace_private;
+
+/* ----------------------------------------------------------------------------
+Private Global Variables
+---------------------------------------------------------------------------- */
+
+#ifdef PCMCIA_DEBUG
+static char rcsid[] =
+"nmclan_cs.c,v 0.16 1995/07/01 06:42:17 rpao Exp rpao";
+static char *version =
+DRV_NAME " " DRV_VERSION " (Roger C. Pao)";
+#endif
+
+static dev_info_t dev_info="nmclan_cs";
+static dev_link_t *dev_list;
+
+static char *if_names[]={
+    "Auto", "10baseT", "BNC",
+};
+
+/* ----------------------------------------------------------------------------
+Parameters
+	These are the parameters that can be set during loading with
+	'insmod'.
+---------------------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("New Media PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+/* 0=auto, 1=10baseT, 2 = 10base2, default=auto */
+INT_MODULE_PARM(if_port, 0);
+
+#ifdef PCMCIA_DEBUG
+INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+#else
+#define DEBUG(n, args...)
+#endif
+
+/* ----------------------------------------------------------------------------
+Function Prototypes
+---------------------------------------------------------------------------- */
+
+static void nmclan_config(dev_link_t *link);
+static void nmclan_release(dev_link_t *link);
+static int nmclan_event(event_t event, int priority,
+			event_callback_args_t *args);
+
+static void nmclan_reset(struct net_device *dev);
+static int mace_config(struct net_device *dev, struct ifmap *map);
+static int mace_open(struct net_device *dev);
+static int mace_close(struct net_device *dev);
+static int mace_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void mace_tx_timeout(struct net_device *dev);
+static irqreturn_t mace_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static struct net_device_stats *mace_get_stats(struct net_device *dev);
+static int mace_rx(struct net_device *dev, unsigned char RxCnt);
+static void restore_multicast_list(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static struct ethtool_ops netdev_ethtool_ops;
+
+
+static dev_link_t *nmclan_attach(void);
+static void nmclan_detach(dev_link_t *);
+
+/* ----------------------------------------------------------------------------
+nmclan_attach
+	Creates an "instance" of the driver, allocating local data
+	structures for one device.  The device is registered with Card
+	Services.
+---------------------------------------------------------------------------- */
+
+static dev_link_t *nmclan_attach(void)
+{
+    mace_private *lp;
+    dev_link_t *link;
+    struct net_device *dev;
+    client_reg_t client_reg;
+    int ret;
+
+    DEBUG(0, "nmclan_attach()\n");
+    DEBUG(1, "%s\n", rcsid);
+
+    /* Create new ethernet device */
+    dev = alloc_etherdev(sizeof(mace_private));
+    if (!dev)
+	return NULL;
+    lp = netdev_priv(dev);
+    link = &lp->link;
+    link->priv = dev;
+    
+    spin_lock_init(&lp->bank_lock);
+    link->io.NumPorts1 = 32;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 5;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->irq.Handler = &mace_interrupt;
+    link->irq.Instance = dev;
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.ConfigIndex = 1;
+    link->conf.Present = PRESENT_OPTION;
+
+    lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
+
+    SET_MODULE_OWNER(dev);
+    dev->hard_start_xmit = &mace_start_xmit;
+    dev->set_config = &mace_config;
+    dev->get_stats = &mace_get_stats;
+    dev->set_multicast_list = &set_multicast_list;
+    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+    dev->open = &mace_open;
+    dev->stop = &mace_close;
+#ifdef HAVE_TX_TIMEOUT
+    dev->tx_timeout = mace_tx_timeout;
+    dev->watchdog_timeo = TX_TIMEOUT;
+#endif
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &nmclan_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != 0) {
+	cs_error(link->handle, RegisterClient, ret);
+	nmclan_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* nmclan_attach */
+
+/* ----------------------------------------------------------------------------
+nmclan_detach
+	This deletes a driver "instance".  The device is de-registered
+	with Card Services.  If it has been released, all local data
+	structures are freed.  Otherwise, the structures will be freed
+	when the device is released.
+---------------------------------------------------------------------------- */
+
+static void nmclan_detach(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    dev_link_t **linkp;
+
+    DEBUG(0, "nmclan_detach(0x%p)\n", link);
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    if (link->dev)
+	unregister_netdev(dev);
+
+    if (link->state & DEV_CONFIG)
+	nmclan_release(link);
+
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    free_netdev(dev);
+} /* nmclan_detach */
+
+/* ----------------------------------------------------------------------------
+mace_read
+	Reads a MACE register.  This is bank independent; however, the
+	caller must ensure that this call is not interruptable.  We are
+	assuming that during normal operation, the MACE is always in
+	bank 0.
+---------------------------------------------------------------------------- */
+static int mace_read(mace_private *lp, kio_addr_t ioaddr, int reg)
+{
+  int data = 0xFF;
+  unsigned long flags;
+
+  switch (reg >> 4) {
+    case 0: /* register 0-15 */
+      data = inb(ioaddr + AM2150_MACE_BASE + reg);
+      break;
+    case 1: /* register 16-31 */
+      spin_lock_irqsave(&lp->bank_lock, flags);
+      MACEBANK(1);
+      data = inb(ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
+      MACEBANK(0);
+      spin_unlock_irqrestore(&lp->bank_lock, flags);
+      break;
+  }
+  return (data & 0xFF);
+} /* mace_read */
+
+/* ----------------------------------------------------------------------------
+mace_write
+	Writes to a MACE register.  This is bank independent; however,
+	the caller must ensure that this call is not interruptable.  We
+	are assuming that during normal operation, the MACE is always in
+	bank 0.
+---------------------------------------------------------------------------- */
+static void mace_write(mace_private *lp, kio_addr_t ioaddr, int reg, int data)
+{
+  unsigned long flags;
+
+  switch (reg >> 4) {
+    case 0: /* register 0-15 */
+      outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + reg);
+      break;
+    case 1: /* register 16-31 */
+      spin_lock_irqsave(&lp->bank_lock, flags);
+      MACEBANK(1);
+      outb(data & 0xFF, ioaddr + AM2150_MACE_BASE + (reg & 0x0F));
+      MACEBANK(0);
+      spin_unlock_irqrestore(&lp->bank_lock, flags);
+      break;
+  }
+} /* mace_write */
+
+/* ----------------------------------------------------------------------------
+mace_init
+	Resets the MACE chip.
+---------------------------------------------------------------------------- */
+static int mace_init(mace_private *lp, kio_addr_t ioaddr, char *enet_addr)
+{
+  int i;
+  int ct = 0;
+
+  /* MACE Software reset */
+  mace_write(lp, ioaddr, MACE_BIUCC, 1);
+  while (mace_read(lp, ioaddr, MACE_BIUCC) & 0x01) {
+    /* Wait for reset bit to be cleared automatically after <= 200ns */;
+    if(++ct > 500)
+    {
+    	printk(KERN_ERR "mace: reset failed, card removed ?\n");
+    	return -1;
+    }
+    udelay(1);
+  }
+  mace_write(lp, ioaddr, MACE_BIUCC, 0);
+
+  /* The Am2150 requires that the MACE FIFOs operate in burst mode. */
+  mace_write(lp, ioaddr, MACE_FIFOCC, 0x0F);
+
+  mace_write(lp,ioaddr, MACE_RCVFC, 0); /* Disable Auto Strip Receive */
+  mace_write(lp, ioaddr, MACE_IMR, 0xFF); /* Disable all interrupts until _open */
+
+  /*
+   * Bit 2-1 PORTSEL[1-0] Port Select.
+   * 00 AUI/10Base-2
+   * 01 10Base-T
+   * 10 DAI Port (reserved in Am2150)
+   * 11 GPSI
+   * For this card, only the first two are valid.
+   * So, PLSCC should be set to
+   * 0x00 for 10Base-2
+   * 0x02 for 10Base-T
+   * Or just set ASEL in PHYCC below!
+   */
+  switch (if_port) {
+    case 1:
+      mace_write(lp, ioaddr, MACE_PLSCC, 0x02);
+      break;
+    case 2:
+      mace_write(lp, ioaddr, MACE_PLSCC, 0x00);
+      break;
+    default:
+      mace_write(lp, ioaddr, MACE_PHYCC, /* ASEL */ 4);
+      /* ASEL Auto Select.  When set, the PORTSEL[1-0] bits are overridden,
+	 and the MACE device will automatically select the operating media
+	 interface port. */
+      break;
+  }
+
+  mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_PHYADDR);
+  /* Poll ADDRCHG bit */
+  ct = 0;
+  while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
+  {
+  	if(++ ct > 500)
+  	{
+  		printk(KERN_ERR "mace: ADDRCHG timeout, card removed ?\n");
+  		return -1;
+  	}
+  }
+  /* Set PADR register */
+  for (i = 0; i < ETHER_ADDR_LEN; i++)
+    mace_write(lp, ioaddr, MACE_PADR, enet_addr[i]);
+
+  /* MAC Configuration Control Register should be written last */
+  /* Let set_multicast_list set this. */
+  /* mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV); */
+  mace_write(lp, ioaddr, MACE_MACCC, 0x00);
+  return 0;
+} /* mace_init */
+
+/* ----------------------------------------------------------------------------
+nmclan_config
+	This routine is scheduled to run after a CARD_INSERTION event
+	is received, to configure the PCMCIA socket, and to make the
+	ethernet device available to the system.
+---------------------------------------------------------------------------- */
+
+#define CS_CHECK(fn, ret) \
+  do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static void nmclan_config(dev_link_t *link)
+{
+  client_handle_t handle = link->handle;
+  struct net_device *dev = link->priv;
+  mace_private *lp = netdev_priv(dev);
+  tuple_t tuple;
+  cisparse_t parse;
+  u_char buf[64];
+  int i, last_ret, last_fn;
+  kio_addr_t ioaddr;
+
+  DEBUG(0, "nmclan_config(0x%p)\n", link);
+
+  tuple.Attributes = 0;
+  tuple.TupleData = buf;
+  tuple.TupleDataMax = 64;
+  tuple.TupleOffset = 0;
+  tuple.DesiredTuple = CISTPL_CONFIG;
+  CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+  CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+  CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+  link->conf.ConfigBase = parse.config.base;
+
+  /* Configure card */
+  link->state |= DEV_CONFIG;
+
+  CS_CHECK(RequestIO, pcmcia_request_io(handle, &link->io));
+  CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+  CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+  dev->irq = link->irq.AssignedIRQ;
+  dev->base_addr = link->io.BasePort1;
+
+  ioaddr = dev->base_addr;
+
+  /* Read the ethernet address from the CIS. */
+  tuple.DesiredTuple = 0x80 /* CISTPL_CFTABLE_ENTRY_MISC */;
+  tuple.TupleData = buf;
+  tuple.TupleDataMax = 64;
+  tuple.TupleOffset = 0;
+  CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+  CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+  memcpy(dev->dev_addr, tuple.TupleData, ETHER_ADDR_LEN);
+
+  /* Verify configuration by reading the MACE ID. */
+  {
+    char sig[2];
+
+    sig[0] = mace_read(lp, ioaddr, MACE_CHIPIDL);
+    sig[1] = mace_read(lp, ioaddr, MACE_CHIPIDH);
+    if ((sig[0] == 0x40) && ((sig[1] & 0x0F) == 0x09)) {
+      DEBUG(0, "nmclan_cs configured: mace id=%x %x\n",
+	    sig[0], sig[1]);
+    } else {
+      printk(KERN_NOTICE "nmclan_cs: mace id not found: %x %x should"
+	     " be 0x40 0x?9\n", sig[0], sig[1]);
+      link->state &= ~DEV_CONFIG_PENDING;
+      return;
+    }
+  }
+
+  if(mace_init(lp, ioaddr, dev->dev_addr) == -1)
+  	goto failed;
+
+  /* The if_port symbol can be set when the module is loaded */
+  if (if_port <= 2)
+    dev->if_port = if_port;
+  else
+    printk(KERN_NOTICE "nmclan_cs: invalid if_port requested\n");
+
+  link->dev = &lp->node;
+  link->state &= ~DEV_CONFIG_PENDING;
+  SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+
+  i = register_netdev(dev);
+  if (i != 0) {
+    printk(KERN_NOTICE "nmclan_cs: register_netdev() failed\n");
+    link->dev = NULL;
+    goto failed;
+  }
+
+  strcpy(lp->node.dev_name, dev->name);
+
+  printk(KERN_INFO "%s: nmclan: port %#3lx, irq %d, %s port, hw_addr ",
+	 dev->name, dev->base_addr, dev->irq, if_names[dev->if_port]);
+  for (i = 0; i < 6; i++)
+      printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+  return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    nmclan_release(link);
+    return;
+
+} /* nmclan_config */
+
+/* ----------------------------------------------------------------------------
+nmclan_release
+	After a card is removed, nmclan_release() will unregister the
+	net device, and release the PCMCIA configuration.  If the device
+	is still open, this will be postponed until it is closed.
+---------------------------------------------------------------------------- */
+static void nmclan_release(dev_link_t *link)
+{
+
+  DEBUG(0, "nmclan_release(0x%p)\n", link);
+
+  pcmcia_release_configuration(link->handle);
+  pcmcia_release_io(link->handle, &link->io);
+  pcmcia_release_irq(link->handle, &link->irq);
+
+  link->state &= ~DEV_CONFIG;
+}
+
+/* ----------------------------------------------------------------------------
+nmclan_event
+	The card status event handler.  Mostly, this schedules other
+	stuff to run after an event is received.  A CARD_REMOVAL event
+	also sets some flags to discourage the net drivers from trying
+	to talk to the card any more.
+---------------------------------------------------------------------------- */
+static int nmclan_event(event_t event, int priority,
+		       event_callback_args_t *args)
+{
+  dev_link_t *link = args->client_data;
+  struct net_device *dev = link->priv;
+
+  DEBUG(1, "nmclan_event(0x%06x)\n", event);
+
+  switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+      link->state &= ~DEV_PRESENT;
+      if (link->state & DEV_CONFIG)
+	netif_device_detach(dev);
+      break;
+    case CS_EVENT_CARD_INSERTION:
+      link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+      nmclan_config(link);
+      break;
+    case CS_EVENT_PM_SUSPEND:
+      link->state |= DEV_SUSPEND;
+      /* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+      if (link->state & DEV_CONFIG) {
+	if (link->open)
+	  netif_device_detach(dev);
+	pcmcia_release_configuration(link->handle);
+      }
+      break;
+    case CS_EVENT_PM_RESUME:
+      link->state &= ~DEV_SUSPEND;
+      /* Fall through... */
+    case CS_EVENT_CARD_RESET:
+      if (link->state & DEV_CONFIG) {
+	pcmcia_request_configuration(link->handle, &link->conf);
+	if (link->open) {
+	  nmclan_reset(dev);
+	  netif_device_attach(dev);
+	}
+      }
+      break;
+    case CS_EVENT_RESET_REQUEST:
+      return 1;
+      break;
+  }
+  return 0;
+} /* nmclan_event */
+
+/* ----------------------------------------------------------------------------
+nmclan_reset
+	Reset and restore all of the Xilinx and MACE registers.
+---------------------------------------------------------------------------- */
+static void nmclan_reset(struct net_device *dev)
+{
+  mace_private *lp = netdev_priv(dev);
+
+#if RESET_XILINX
+  dev_link_t *link = &lp->link;
+  conf_reg_t reg;
+  u_long OrigCorValue; 
+
+  /* Save original COR value */
+  reg.Function = 0;
+  reg.Action = CS_READ;
+  reg.Offset = CISREG_COR;
+  reg.Value = 0;
+  pcmcia_access_configuration_register(link->handle, &reg);
+  OrigCorValue = reg.Value;
+
+  /* Reset Xilinx */
+  reg.Action = CS_WRITE;
+  reg.Offset = CISREG_COR;
+  DEBUG(1, "nmclan_reset: OrigCorValue=0x%lX, resetting...\n",
+	OrigCorValue);
+  reg.Value = COR_SOFT_RESET;
+  pcmcia_access_configuration_register(link->handle, &reg);
+  /* Need to wait for 20 ms for PCMCIA to finish reset. */
+
+  /* Restore original COR configuration index */
+  reg.Value = COR_LEVEL_REQ | (OrigCorValue & COR_CONFIG_MASK);
+  pcmcia_access_configuration_register(link->handle, &reg);
+  /* Xilinx is now completely reset along with the MACE chip. */
+  lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
+
+#endif /* #if RESET_XILINX */
+
+  /* Xilinx is now completely reset along with the MACE chip. */
+  lp->tx_free_frames=AM2150_MAX_TX_FRAMES;
+
+  /* Reinitialize the MACE chip for operation. */
+  mace_init(lp, dev->base_addr, dev->dev_addr);
+  mace_write(lp, dev->base_addr, MACE_IMR, MACE_IMR_DEFAULT);
+
+  /* Restore the multicast list and enable TX and RX. */
+  restore_multicast_list(dev);
+} /* nmclan_reset */
+
+/* ----------------------------------------------------------------------------
+mace_config
+	[Someone tell me what this is supposed to do?  Is if_port a defined
+	standard?  If so, there should be defines to indicate 1=10Base-T,
+	2=10Base-2, etc. including limited automatic detection.]
+---------------------------------------------------------------------------- */
+static int mace_config(struct net_device *dev, struct ifmap *map)
+{
+  if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+    if (map->port <= 2) {
+      dev->if_port = map->port;
+      printk(KERN_INFO "%s: switched to %s port\n", dev->name,
+	     if_names[dev->if_port]);
+    } else
+      return -EINVAL;
+  }
+  return 0;
+} /* mace_config */
+
+/* ----------------------------------------------------------------------------
+mace_open
+	Open device driver.
+---------------------------------------------------------------------------- */
+static int mace_open(struct net_device *dev)
+{
+  kio_addr_t ioaddr = dev->base_addr;
+  mace_private *lp = netdev_priv(dev);
+  dev_link_t *link = &lp->link;
+
+  if (!DEV_OK(link))
+    return -ENODEV;
+
+  link->open++;
+
+  MACEBANK(0);
+
+  netif_start_queue(dev);
+  nmclan_reset(dev);
+
+  return 0; /* Always succeed */
+} /* mace_open */
+
+/* ----------------------------------------------------------------------------
+mace_close
+	Closes device driver.
+---------------------------------------------------------------------------- */
+static int mace_close(struct net_device *dev)
+{
+  kio_addr_t ioaddr = dev->base_addr;
+  mace_private *lp = netdev_priv(dev);
+  dev_link_t *link = &lp->link;
+
+  DEBUG(2, "%s: shutting down ethercard.\n", dev->name);
+
+  /* Mask off all interrupts from the MACE chip. */
+  outb(0xFF, ioaddr + AM2150_MACE_BASE + MACE_IMR);
+
+  link->open--;
+  netif_stop_queue(dev);
+
+  return 0;
+} /* mace_close */
+
+static void netdev_get_drvinfo(struct net_device *dev,
+			       struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+	sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr);
+}
+
+#ifdef PCMCIA_DEBUG
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+	return pc_debug;
+}
+
+static void netdev_set_msglevel(struct net_device *dev, u32 level)
+{
+	pc_debug = level;
+}
+#endif /* PCMCIA_DEBUG */
+
+static struct ethtool_ops netdev_ethtool_ops = {
+	.get_drvinfo		= netdev_get_drvinfo,
+#ifdef PCMCIA_DEBUG
+	.get_msglevel		= netdev_get_msglevel,
+	.set_msglevel		= netdev_set_msglevel,
+#endif /* PCMCIA_DEBUG */
+};
+
+/* ----------------------------------------------------------------------------
+mace_start_xmit
+	This routine begins the packet transmit function.  When completed,
+	it will generate a transmit interrupt.
+
+	According to /usr/src/linux/net/inet/dev.c, if _start_xmit
+	returns 0, the "packet is now solely the responsibility of the
+	driver."  If _start_xmit returns non-zero, the "transmission
+	failed, put skb back into a list."
+---------------------------------------------------------------------------- */
+
+static void mace_tx_timeout(struct net_device *dev)
+{
+  mace_private *lp = netdev_priv(dev);
+  dev_link_t *link = &lp->link;
+
+  printk(KERN_NOTICE "%s: transmit timed out -- ", dev->name);
+#if RESET_ON_TIMEOUT
+  printk("resetting card\n");
+  pcmcia_reset_card(link->handle, NULL);
+#else /* #if RESET_ON_TIMEOUT */
+  printk("NOT resetting card\n");
+#endif /* #if RESET_ON_TIMEOUT */
+  dev->trans_start = jiffies;
+  netif_wake_queue(dev);
+}
+
+static int mace_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+  mace_private *lp = netdev_priv(dev);
+  kio_addr_t ioaddr = dev->base_addr;
+
+  netif_stop_queue(dev);
+
+  DEBUG(3, "%s: mace_start_xmit(length = %ld) called.\n",
+	dev->name, (long)skb->len);
+
+#if (!TX_INTERRUPTABLE)
+  /* Disable MACE TX interrupts. */
+  outb(MACE_IMR_DEFAULT | MACE_IR_XMTINT,
+    ioaddr + AM2150_MACE_BASE + MACE_IMR);
+  lp->tx_irq_disabled=1;
+#endif /* #if (!TX_INTERRUPTABLE) */
+
+  {
+    /* This block must not be interrupted by another transmit request!
+       mace_tx_timeout will take care of timer-based retransmissions from
+       the upper layers.  The interrupt handler is guaranteed never to
+       service a transmit interrupt while we are in here.
+    */
+
+    lp->linux_stats.tx_bytes += skb->len;
+    lp->tx_free_frames--;
+
+    /* WARNING: Write the _exact_ number of bytes written in the header! */
+    /* Put out the word header [must be an outw()] . . . */
+    outw(skb->len, ioaddr + AM2150_XMT);
+    /* . . . and the packet [may be any combination of outw() and outb()] */
+    outsw(ioaddr + AM2150_XMT, skb->data, skb->len >> 1);
+    if (skb->len & 1) {
+      /* Odd byte transfer */
+      outb(skb->data[skb->len-1], ioaddr + AM2150_XMT);
+    }
+
+    dev->trans_start = jiffies;
+
+#if MULTI_TX
+    if (lp->tx_free_frames > 0)
+      netif_start_queue(dev);
+#endif /* #if MULTI_TX */
+  }
+
+#if (!TX_INTERRUPTABLE)
+  /* Re-enable MACE TX interrupts. */
+  lp->tx_irq_disabled=0;
+  outb(MACE_IMR_DEFAULT, ioaddr + AM2150_MACE_BASE + MACE_IMR);
+#endif /* #if (!TX_INTERRUPTABLE) */
+
+  dev_kfree_skb(skb);
+
+  return 0;
+} /* mace_start_xmit */
+
+/* ----------------------------------------------------------------------------
+mace_interrupt
+	The interrupt handler.
+---------------------------------------------------------------------------- */
+static irqreturn_t mace_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct net_device *dev = (struct net_device *) dev_id;
+  mace_private *lp = netdev_priv(dev);
+  kio_addr_t ioaddr = dev->base_addr;
+  int status;
+  int IntrCnt = MACE_MAX_IR_ITERATIONS;
+
+  if (dev == NULL) {
+    DEBUG(2, "mace_interrupt(): irq 0x%X for unknown device.\n",
+	  irq);
+    return IRQ_NONE;
+  }
+
+  if (lp->tx_irq_disabled) {
+    printk(
+      (lp->tx_irq_disabled?
+       KERN_NOTICE "%s: Interrupt with tx_irq_disabled "
+       "[isr=%02X, imr=%02X]\n": 
+       KERN_NOTICE "%s: Re-entering the interrupt handler "
+       "[isr=%02X, imr=%02X]\n"),
+      dev->name,
+      inb(ioaddr + AM2150_MACE_BASE + MACE_IR),
+      inb(ioaddr + AM2150_MACE_BASE + MACE_IMR)
+    );
+    /* WARNING: MACE_IR has been read! */
+    return IRQ_NONE;
+  }
+
+  if (!netif_device_present(dev)) {
+    DEBUG(2, "%s: interrupt from dead card\n", dev->name);
+    return IRQ_NONE;
+  }
+
+  do {
+    /* WARNING: MACE_IR is a READ/CLEAR port! */
+    status = inb(ioaddr + AM2150_MACE_BASE + MACE_IR);
+
+    DEBUG(3, "mace_interrupt: irq 0x%X status 0x%X.\n", irq, status);
+
+    if (status & MACE_IR_RCVINT) {
+      mace_rx(dev, MACE_MAX_RX_ITERATIONS);
+    }
+
+    if (status & MACE_IR_XMTINT) {
+      unsigned char fifofc;
+      unsigned char xmtrc;
+      unsigned char xmtfs;
+
+      fifofc = inb(ioaddr + AM2150_MACE_BASE + MACE_FIFOFC);
+      if ((fifofc & MACE_FIFOFC_XMTFC)==0) {
+	lp->linux_stats.tx_errors++;
+	outb(0xFF, ioaddr + AM2150_XMT_SKIP);
+      }
+
+      /* Transmit Retry Count (XMTRC, reg 4) */
+      xmtrc = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTRC);
+      if (xmtrc & MACE_XMTRC_EXDEF) lp->mace_stats.exdef++;
+      lp->mace_stats.xmtrc += (xmtrc & MACE_XMTRC_XMTRC);
+
+      if (
+        (xmtfs = inb(ioaddr + AM2150_MACE_BASE + MACE_XMTFS)) &
+        MACE_XMTFS_XMTSV /* Transmit Status Valid */
+      ) {
+	lp->mace_stats.xmtsv++;
+
+	if (xmtfs & ~MACE_XMTFS_XMTSV) {
+	  if (xmtfs & MACE_XMTFS_UFLO) {
+	    /* Underflow.  Indicates that the Transmit FIFO emptied before
+	       the end of frame was reached. */
+	    lp->mace_stats.uflo++;
+	  }
+	  if (xmtfs & MACE_XMTFS_LCOL) {
+	    /* Late Collision */
+	    lp->mace_stats.lcol++;
+	  }
+	  if (xmtfs & MACE_XMTFS_MORE) {
+	    /* MORE than one retry was needed */
+	    lp->mace_stats.more++;
+	  }
+	  if (xmtfs & MACE_XMTFS_ONE) {
+	    /* Exactly ONE retry occurred */
+	    lp->mace_stats.one++;
+	  }
+	  if (xmtfs & MACE_XMTFS_DEFER) {
+	    /* Transmission was defered */
+	    lp->mace_stats.defer++;
+	  }
+	  if (xmtfs & MACE_XMTFS_LCAR) {
+	    /* Loss of carrier */
+	    lp->mace_stats.lcar++;
+	  }
+	  if (xmtfs & MACE_XMTFS_RTRY) {
+	    /* Retry error: transmit aborted after 16 attempts */
+	    lp->mace_stats.rtry++;
+	  }
+        } /* if (xmtfs & ~MACE_XMTFS_XMTSV) */
+
+      } /* if (xmtfs & MACE_XMTFS_XMTSV) */
+
+      lp->linux_stats.tx_packets++;
+      lp->tx_free_frames++;
+      netif_wake_queue(dev);
+    } /* if (status & MACE_IR_XMTINT) */
+
+    if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) {
+      if (status & MACE_IR_JAB) {
+        /* Jabber Error.  Excessive transmit duration (20-150ms). */
+        lp->mace_stats.jab++;
+      }
+      if (status & MACE_IR_BABL) {
+        /* Babble Error.  >1518 bytes transmitted. */
+        lp->mace_stats.babl++;
+      }
+      if (status & MACE_IR_CERR) {
+	/* Collision Error.  CERR indicates the absence of the
+	   Signal Quality Error Test message after a packet
+	   transmission. */
+        lp->mace_stats.cerr++;
+      }
+      if (status & MACE_IR_RCVCCO) {
+        /* Receive Collision Count Overflow; */
+        lp->mace_stats.rcvcco++;
+      }
+      if (status & MACE_IR_RNTPCO) {
+        /* Runt Packet Count Overflow */
+        lp->mace_stats.rntpco++;
+      }
+      if (status & MACE_IR_MPCO) {
+        /* Missed Packet Count Overflow */
+        lp->mace_stats.mpco++;
+      }
+    } /* if (status & ~MACE_IMR_DEFAULT & ~MACE_IR_RCVINT & ~MACE_IR_XMTINT) */
+
+  } while ((status & ~MACE_IMR_DEFAULT) && (--IntrCnt));
+
+  return IRQ_HANDLED;
+} /* mace_interrupt */
+
+/* ----------------------------------------------------------------------------
+mace_rx
+	Receives packets.
+---------------------------------------------------------------------------- */
+static int mace_rx(struct net_device *dev, unsigned char RxCnt)
+{
+  mace_private *lp = netdev_priv(dev);
+  kio_addr_t ioaddr = dev->base_addr;
+  unsigned char rx_framecnt;
+  unsigned short rx_status;
+
+  while (
+    ((rx_framecnt = inb(ioaddr + AM2150_RCV_FRAME_COUNT)) > 0) &&
+    (rx_framecnt <= 12) && /* rx_framecnt==0xFF if card is extracted. */
+    (RxCnt--)
+  ) {
+    rx_status = inw(ioaddr + AM2150_RCV);
+
+    DEBUG(3, "%s: in mace_rx(), framecnt 0x%X, rx_status"
+	  " 0x%X.\n", dev->name, rx_framecnt, rx_status);
+
+    if (rx_status & MACE_RCVFS_RCVSTS) { /* Error, update stats. */
+      lp->linux_stats.rx_errors++;
+      if (rx_status & MACE_RCVFS_OFLO) {
+        lp->mace_stats.oflo++;
+      }
+      if (rx_status & MACE_RCVFS_CLSN) {
+        lp->mace_stats.clsn++;
+      }
+      if (rx_status & MACE_RCVFS_FRAM) {
+	lp->mace_stats.fram++;
+      }
+      if (rx_status & MACE_RCVFS_FCS) {
+        lp->mace_stats.fcs++;
+      }
+    } else {
+      short pkt_len = (rx_status & ~MACE_RCVFS_RCVSTS) - 4;
+        /* Auto Strip is off, always subtract 4 */
+      struct sk_buff *skb;
+
+      lp->mace_stats.rfs_rntpc += inb(ioaddr + AM2150_RCV);
+        /* runt packet count */
+      lp->mace_stats.rfs_rcvcc += inb(ioaddr + AM2150_RCV);
+        /* rcv collision count */
+
+      DEBUG(3, "    receiving packet size 0x%X rx_status"
+	    " 0x%X.\n", pkt_len, rx_status);
+
+      skb = dev_alloc_skb(pkt_len+2);
+
+      if (skb != NULL) {
+	skb->dev = dev;
+
+	skb_reserve(skb, 2);
+	insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1);
+	if (pkt_len & 1)
+	    *(skb->tail-1) = inb(ioaddr + AM2150_RCV);
+	skb->protocol = eth_type_trans(skb, dev);
+	
+	netif_rx(skb); /* Send the packet to the upper (protocol) layers. */
+
+	dev->last_rx = jiffies;
+	lp->linux_stats.rx_packets++;
+	lp->linux_stats.rx_bytes += skb->len;
+	outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
+	continue;
+      } else {
+	DEBUG(1, "%s: couldn't allocate a sk_buff of size"
+	      " %d.\n", dev->name, pkt_len);
+	lp->linux_stats.rx_dropped++;
+      }
+    }
+    outb(0xFF, ioaddr + AM2150_RCV_NEXT); /* skip to next frame */
+  } /* while */
+
+  return 0;
+} /* mace_rx */
+
+/* ----------------------------------------------------------------------------
+pr_linux_stats
+---------------------------------------------------------------------------- */
+static void pr_linux_stats(struct net_device_stats *pstats)
+{
+  DEBUG(2, "pr_linux_stats\n");
+  DEBUG(2, " rx_packets=%-7ld        tx_packets=%ld\n",
+	(long)pstats->rx_packets, (long)pstats->tx_packets);
+  DEBUG(2, " rx_errors=%-7ld         tx_errors=%ld\n",
+	(long)pstats->rx_errors, (long)pstats->tx_errors);
+  DEBUG(2, " rx_dropped=%-7ld        tx_dropped=%ld\n",
+	(long)pstats->rx_dropped, (long)pstats->tx_dropped);
+  DEBUG(2, " multicast=%-7ld         collisions=%ld\n",
+	(long)pstats->multicast, (long)pstats->collisions);
+
+  DEBUG(2, " rx_length_errors=%-7ld  rx_over_errors=%ld\n",
+	(long)pstats->rx_length_errors, (long)pstats->rx_over_errors);
+  DEBUG(2, " rx_crc_errors=%-7ld     rx_frame_errors=%ld\n",
+	(long)pstats->rx_crc_errors, (long)pstats->rx_frame_errors);
+  DEBUG(2, " rx_fifo_errors=%-7ld    rx_missed_errors=%ld\n",
+	(long)pstats->rx_fifo_errors, (long)pstats->rx_missed_errors);
+
+  DEBUG(2, " tx_aborted_errors=%-7ld tx_carrier_errors=%ld\n",
+	(long)pstats->tx_aborted_errors, (long)pstats->tx_carrier_errors);
+  DEBUG(2, " tx_fifo_errors=%-7ld    tx_heartbeat_errors=%ld\n",
+	(long)pstats->tx_fifo_errors, (long)pstats->tx_heartbeat_errors);
+  DEBUG(2, " tx_window_errors=%ld\n",
+	(long)pstats->tx_window_errors);
+} /* pr_linux_stats */
+
+/* ----------------------------------------------------------------------------
+pr_mace_stats
+---------------------------------------------------------------------------- */
+static void pr_mace_stats(mace_statistics *pstats)
+{
+  DEBUG(2, "pr_mace_stats\n");
+
+  DEBUG(2, " xmtsv=%-7d             uflo=%d\n",
+	pstats->xmtsv, pstats->uflo);
+  DEBUG(2, " lcol=%-7d              more=%d\n",
+	pstats->lcol, pstats->more);
+  DEBUG(2, " one=%-7d               defer=%d\n",
+	pstats->one, pstats->defer);
+  DEBUG(2, " lcar=%-7d              rtry=%d\n",
+	pstats->lcar, pstats->rtry);
+
+  /* MACE_XMTRC */
+  DEBUG(2, " exdef=%-7d             xmtrc=%d\n",
+	pstats->exdef, pstats->xmtrc);
+
+  /* RFS1--Receive Status (RCVSTS) */
+  DEBUG(2, " oflo=%-7d              clsn=%d\n",
+	pstats->oflo, pstats->clsn);
+  DEBUG(2, " fram=%-7d              fcs=%d\n",
+	pstats->fram, pstats->fcs);
+
+  /* RFS2--Runt Packet Count (RNTPC) */
+  /* RFS3--Receive Collision Count (RCVCC) */
+  DEBUG(2, " rfs_rntpc=%-7d         rfs_rcvcc=%d\n",
+	pstats->rfs_rntpc, pstats->rfs_rcvcc);
+
+  /* MACE_IR */
+  DEBUG(2, " jab=%-7d               babl=%d\n",
+	pstats->jab, pstats->babl);
+  DEBUG(2, " cerr=%-7d              rcvcco=%d\n",
+	pstats->cerr, pstats->rcvcco);
+  DEBUG(2, " rntpco=%-7d            mpco=%d\n",
+	pstats->rntpco, pstats->mpco);
+
+  /* MACE_MPC */
+  DEBUG(2, " mpc=%d\n", pstats->mpc);
+
+  /* MACE_RNTPC */
+  DEBUG(2, " rntpc=%d\n", pstats->rntpc);
+
+  /* MACE_RCVCC */
+  DEBUG(2, " rcvcc=%d\n", pstats->rcvcc);
+
+} /* pr_mace_stats */
+
+/* ----------------------------------------------------------------------------
+update_stats
+	Update statistics.  We change to register window 1, so this
+	should be run single-threaded if the device is active. This is
+	expected to be a rare operation, and it's simpler for the rest
+	of the driver to assume that window 0 is always valid rather
+	than use a special window-state variable.
+
+	oflo & uflo should _never_ occur since it would mean the Xilinx
+	was not able to transfer data between the MACE FIFO and the
+	card's SRAM fast enough.  If this happens, something is
+	seriously wrong with the hardware.
+---------------------------------------------------------------------------- */
+static void update_stats(kio_addr_t ioaddr, struct net_device *dev)
+{
+  mace_private *lp = netdev_priv(dev);
+
+  lp->mace_stats.rcvcc += mace_read(lp, ioaddr, MACE_RCVCC);
+  lp->mace_stats.rntpc += mace_read(lp, ioaddr, MACE_RNTPC);
+  lp->mace_stats.mpc += mace_read(lp, ioaddr, MACE_MPC);
+  /* At this point, mace_stats is fully updated for this call.
+     We may now update the linux_stats. */
+
+  /* The MACE has no equivalent for linux_stats field which are commented
+     out. */
+
+  /* lp->linux_stats.multicast; */
+  lp->linux_stats.collisions = 
+    lp->mace_stats.rcvcco * 256 + lp->mace_stats.rcvcc;
+    /* Collision: The MACE may retry sending a packet 15 times
+       before giving up.  The retry count is in XMTRC.
+       Does each retry constitute a collision?
+       If so, why doesn't the RCVCC record these collisions? */
+
+  /* detailed rx_errors: */
+  lp->linux_stats.rx_length_errors = 
+    lp->mace_stats.rntpco * 256 + lp->mace_stats.rntpc;
+  /* lp->linux_stats.rx_over_errors */
+  lp->linux_stats.rx_crc_errors = lp->mace_stats.fcs;
+  lp->linux_stats.rx_frame_errors = lp->mace_stats.fram;
+  lp->linux_stats.rx_fifo_errors = lp->mace_stats.oflo;
+  lp->linux_stats.rx_missed_errors = 
+    lp->mace_stats.mpco * 256 + lp->mace_stats.mpc;
+
+  /* detailed tx_errors */
+  lp->linux_stats.tx_aborted_errors = lp->mace_stats.rtry;
+  lp->linux_stats.tx_carrier_errors = lp->mace_stats.lcar;
+    /* LCAR usually results from bad cabling. */
+  lp->linux_stats.tx_fifo_errors = lp->mace_stats.uflo;
+  lp->linux_stats.tx_heartbeat_errors = lp->mace_stats.cerr;
+  /* lp->linux_stats.tx_window_errors; */
+
+  return;
+} /* update_stats */
+
+/* ----------------------------------------------------------------------------
+mace_get_stats
+	Gathers ethernet statistics from the MACE chip.
+---------------------------------------------------------------------------- */
+static struct net_device_stats *mace_get_stats(struct net_device *dev)
+{
+  mace_private *lp = netdev_priv(dev);
+
+  update_stats(dev->base_addr, dev);
+
+  DEBUG(1, "%s: updating the statistics.\n", dev->name);
+  pr_linux_stats(&lp->linux_stats);
+  pr_mace_stats(&lp->mace_stats);
+
+  return &lp->linux_stats;
+} /* net_device_stats */
+
+/* ----------------------------------------------------------------------------
+updateCRC
+	Modified from Am79C90 data sheet.
+---------------------------------------------------------------------------- */
+
+#ifdef BROKEN_MULTICAST
+
+static void updateCRC(int *CRC, int bit)
+{
+  int poly[]={
+    1,1,1,0, 1,1,0,1,
+    1,0,1,1, 1,0,0,0,
+    1,0,0,0, 0,0,1,1,
+    0,0,1,0, 0,0,0,0
+  }; /* CRC polynomial.  poly[n] = coefficient of the x**n term of the
+	CRC generator polynomial. */
+
+  int j;
+
+  /* shift CRC and control bit (CRC[32]) */
+  for (j = 32; j > 0; j--)
+    CRC[j] = CRC[j-1];
+  CRC[0] = 0;
+
+  /* If bit XOR(control bit) = 1, set CRC = CRC XOR polynomial. */
+  if (bit ^ CRC[32])
+    for (j = 0; j < 32; j++)
+      CRC[j] ^= poly[j];
+} /* updateCRC */
+
+/* ----------------------------------------------------------------------------
+BuildLAF
+	Build logical address filter.
+	Modified from Am79C90 data sheet.
+
+Input
+	ladrf: logical address filter (contents initialized to 0)
+	adr: ethernet address
+---------------------------------------------------------------------------- */
+static void BuildLAF(int *ladrf, int *adr)
+{
+  int CRC[33]={1}; /* CRC register, 1 word/bit + extra control bit */
+
+  int i, byte; /* temporary array indices */
+  int hashcode; /* the output object */
+
+  CRC[32]=0;
+
+  for (byte = 0; byte < 6; byte++)
+    for (i = 0; i < 8; i++)
+      updateCRC(CRC, (adr[byte] >> i) & 1);
+
+  hashcode = 0;
+  for (i = 0; i < 6; i++)
+    hashcode = (hashcode << 1) + CRC[i];
+
+  byte = hashcode >> 3;
+  ladrf[byte] |= (1 << (hashcode & 7));
+
+#ifdef PCMCIA_DEBUG
+  if (pc_debug > 2) {
+    printk(KERN_DEBUG "    adr =");
+    for (i = 0; i < 6; i++)
+      printk(" %02X", adr[i]);
+    printk("\n" KERN_DEBUG "    hashcode = %d(decimal), ladrf[0:63]"
+	   " =", hashcode);
+    for (i = 0; i < 8; i++)
+      printk(" %02X", ladrf[i]);
+    printk("\n");
+  }
+#endif
+} /* BuildLAF */
+
+/* ----------------------------------------------------------------------------
+restore_multicast_list
+	Restores the multicast filter for MACE chip to the last
+	set_multicast_list() call.
+
+Input
+	multicast_num_addrs
+	multicast_ladrf[]
+---------------------------------------------------------------------------- */
+static void restore_multicast_list(struct net_device *dev)
+{
+  mace_private *lp = netdev_priv(dev);
+  int num_addrs = lp->multicast_num_addrs;
+  int *ladrf = lp->multicast_ladrf;
+  kio_addr_t ioaddr = dev->base_addr;
+  int i;
+
+  DEBUG(2, "%s: restoring Rx mode to %d addresses.\n",
+	dev->name, num_addrs);
+
+  if (num_addrs > 0) {
+
+    DEBUG(1, "Attempt to restore multicast list detected.\n");
+
+    mace_write(lp, ioaddr, MACE_IAC, MACE_IAC_ADDRCHG | MACE_IAC_LOGADDR);
+    /* Poll ADDRCHG bit */
+    while (mace_read(lp, ioaddr, MACE_IAC) & MACE_IAC_ADDRCHG)
+      ;
+    /* Set LADRF register */
+    for (i = 0; i < MACE_LADRF_LEN; i++)
+      mace_write(lp, ioaddr, MACE_LADRF, ladrf[i]);
+
+    mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_RCVFCSE | MACE_UTR_LOOP_EXTERNAL);
+    mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+
+  } else if (num_addrs < 0) {
+
+    /* Promiscuous mode: receive all packets */
+    mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+    mace_write(lp, ioaddr, MACE_MACCC,
+      MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
+    );
+
+  } else {
+
+    /* Normal mode */
+    mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+    mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+
+  }
+} /* restore_multicast_list */
+
+/* ----------------------------------------------------------------------------
+set_multicast_list
+	Set or clear the multicast filter for this adaptor.
+
+Input
+	num_addrs == -1	Promiscuous mode, receive all packets
+	num_addrs == 0	Normal mode, clear multicast list
+	num_addrs > 0	Multicast mode, receive normal and MC packets, and do
+			best-effort filtering.
+Output
+	multicast_num_addrs
+	multicast_ladrf[]
+---------------------------------------------------------------------------- */
+
+static void set_multicast_list(struct net_device *dev)
+{
+  mace_private *lp = netdev_priv(dev);
+  int adr[ETHER_ADDR_LEN] = {0}; /* Ethernet address */
+  int i;
+  struct dev_mc_list *dmi = dev->mc_list;
+
+#ifdef PCMCIA_DEBUG
+  if (pc_debug > 1) {
+    static int old;
+    if (dev->mc_count != old) {
+      old = dev->mc_count;
+      DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+	    dev->name, old);
+    }
+  }
+#endif
+
+  /* Set multicast_num_addrs. */
+  lp->multicast_num_addrs = dev->mc_count;
+
+  /* Set multicast_ladrf. */
+  if (num_addrs > 0) {
+    /* Calculate multicast logical address filter */
+    memset(lp->multicast_ladrf, 0, MACE_LADRF_LEN);
+    for (i = 0; i < dev->mc_count; i++) {
+      memcpy(adr, dmi->dmi_addr, ETHER_ADDR_LEN);
+      dmi = dmi->next;
+      BuildLAF(lp->multicast_ladrf, adr);
+    }
+  }
+
+  restore_multicast_list(dev);
+
+} /* set_multicast_list */
+
+#endif /* BROKEN_MULTICAST */
+
+static void restore_multicast_list(struct net_device *dev)
+{
+  kio_addr_t ioaddr = dev->base_addr;
+  mace_private *lp = netdev_priv(dev);
+
+  DEBUG(2, "%s: restoring Rx mode to %d addresses.\n", dev->name,
+	lp->multicast_num_addrs);
+
+  if (dev->flags & IFF_PROMISC) {
+    /* Promiscuous mode: receive all packets */
+    mace_write(lp,ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+    mace_write(lp, ioaddr, MACE_MACCC,
+      MACE_MACCC_PROM | MACE_MACCC_ENXMT | MACE_MACCC_ENRCV
+    );
+  } else {
+    /* Normal mode */
+    mace_write(lp, ioaddr, MACE_UTR, MACE_UTR_LOOP_EXTERNAL);
+    mace_write(lp, ioaddr, MACE_MACCC, MACE_MACCC_ENXMT | MACE_MACCC_ENRCV);
+  }
+} /* restore_multicast_list */
+
+static void set_multicast_list(struct net_device *dev)
+{
+  mace_private *lp = netdev_priv(dev);
+
+#ifdef PCMCIA_DEBUG
+  if (pc_debug > 1) {
+    static int old;
+    if (dev->mc_count != old) {
+      old = dev->mc_count;
+      DEBUG(0, "%s: setting Rx mode to %d addresses.\n",
+	    dev->name, old);
+    }
+  }
+#endif
+
+  lp->multicast_num_addrs = dev->mc_count;
+  restore_multicast_list(dev);
+
+} /* set_multicast_list */
+
+static struct pcmcia_driver nmclan_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "nmclan_cs",
+	},
+	.attach		= nmclan_attach,
+	.detach		= nmclan_detach,
+};
+
+static int __init init_nmclan_cs(void)
+{
+	return pcmcia_register_driver(&nmclan_cs_driver);
+}
+
+static void __exit exit_nmclan_cs(void)
+{
+	pcmcia_unregister_driver(&nmclan_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_nmclan_cs);
+module_exit(exit_nmclan_cs);
diff --git a/drivers/net/pcmcia/ositech.h b/drivers/net/pcmcia/ositech.h
new file mode 100644
index 000000000000..4126efc355bd
--- /dev/null
+++ b/drivers/net/pcmcia/ositech.h
@@ -0,0 +1,358 @@
+/*
+    This file contains the firmware of Seven of Diamonds from OSITECH.
+    (Special thanks to Kevin MacPherson of OSITECH)
+
+    This software may be used and distributed according to the terms of
+    the GNU General Public License, incorporated herein by reference.
+*/
+
+    static const u_char __Xilinx7OD[] = {
+    0xFF, 0x04, 0xA0, 0x36, 0xF3, 0xEC, 0xFF, 0xFF, 0xFF, 0xDF, 0xFB, 0xFF,
+    0xF3, 0xFF, 0xFF, 0xFF, 
+    0xEF, 0x3F, 0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x7F, 0xFE, 0xFF,
+    0xCE, 0xFE, 0xFE, 0xFE, 
+    0xFE, 0xDE, 0xBD, 0xDD, 0xFD, 0xFF, 0xFD, 0xCF, 0xF7, 0xBF, 0x7F, 0xFF,
+    0x7F, 0x3F, 0xFE, 0xBF, 
+    0xFF, 0xFF, 0xFF, 0xBC, 0xFF, 0xFF, 0xBD, 0xB5, 0x7F, 0x7F, 0xBF, 0xBF,
+    0x7F, 0xFF, 0xEF, 0xFF, 
+    0xFF, 0xFF, 0xFB, 0xFF, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xDE,
+    0xFE, 0xFE, 0xFA, 0xDE, 
+    0xBD, 0xFD, 0xED, 0xFD, 0xFD, 0xCF, 0xEF, 0xEF, 0xEF, 0xEF, 0xC7, 0xDF,
+    0xDF, 0xDF, 0xDF, 0xDF, 
+    0xFF, 0x7E, 0xFE, 0xFD, 0x7D, 0x6D, 0xEE, 0xFE, 0x7C, 0xFB, 0xF4, 0xFB,
+    0xCF, 0xDB, 0xDF, 0xFF, 
+    0xFF, 0xBB, 0x7F, 0xFF, 0x7F, 0xFF, 0xF7, 0xFF, 0x9E, 0xBF, 0x3B, 0xBF,
+    0xBF, 0x7F, 0x7F, 0x7F, 
+    0x7E, 0x6F, 0xDF, 0xEF, 0xF5, 0xF6, 0xFD, 0xF6, 0xF5, 0xED, 0xEB, 0xFF,
+    0xEF, 0xEF, 0xEF, 0x7E, 
+    0x7F, 0x7F, 0x6F, 0x7F, 0xFF, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xEF, 0xBF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0x1F, 0x1F, 0xEE, 0xFF, 0xBC,
+    0xB7, 0xFF, 0xDF, 0xFF, 
+    0xDF, 0xEF, 0x3B, 0xE3, 0xD3, 0xFF, 0xFB, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF,
+    0xFF, 0xBA, 0xBF, 0x2D, 
+    0xDB, 0xBD, 0xFD, 0xDB, 0xDF, 0xFA, 0xFB, 0xFF, 0xEF, 0xFB, 0xDB, 0xF3,
+    0xFF, 0xDF, 0xFD, 0x7F, 
+    0xEF, 0xFB, 0xFF, 0xFF, 0xBE, 0xBF, 0x27, 0xBA, 0xFE, 0xFB, 0xDF, 0xFF,
+    0xF6, 0xFF, 0xFF, 0xEF, 
+    0xFB, 0xDB, 0xF3, 0xD9, 0x9A, 0x3F, 0xFF, 0xAF, 0xBF, 0xFF, 0xFF, 0xBE,
+    0x3F, 0x37, 0xBD, 0x96, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAE, 0xFB, 0xF3, 0xF3, 0xEB, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xF7, 0xFA, 0xBC, 0xAE, 0xFE, 0xBE, 0xFE, 0xBB, 0x7F, 0xFD, 0xFF,
+    0x7F, 0xEF, 0xF7, 0xFB, 
+    0xBB, 0xD7, 0xF7, 0x7F, 0xFF, 0xF7, 0xFF, 0xFF, 0xF7, 0xBC, 0xED, 0xFD,
+    0xBD, 0x9D, 0x7D, 0x7B, 
+    0xFB, 0x7B, 0x7B, 0xFB, 0xAF, 0xFF, 0xFE, 0xFD, 0xFD, 0xFE, 0xFE, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xF7, 
+    0xAA, 0xB9, 0xBF, 0x8F, 0xBF, 0xDF, 0xFF, 0x7F, 0xFF, 0xFF, 0x7F, 0xCF,
+    0xFB, 0xEB, 0xCB, 0xEB, 
+    0xEE, 0xFF, 0xFF, 0xD7, 0xFF, 0xFF, 0xFF, 0x3E, 0x33, 0x3F, 0x1C, 0x7C,
+    0xFC, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xCF, 0xD3, 0xF3, 0xE3, 0xF3, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xEB, 0xFE, 0x35, 
+    0x3F, 0x3D, 0xFD, 0xFD, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xEF, 0x6F, 0xE3,
+    0xE3, 0xE3, 0xEF, 0xFF, 
+    0xFF, 0xDF, 0xFF, 0xFF, 0xF7, 0xFE, 0x3E, 0x5E, 0xFE, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFD, 0xFF, 0xFF, 
+    0xAF, 0xCF, 0xF2, 0xCB, 0xCF, 0x8E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD,
+    0xFC, 0x3E, 0x1F, 0x9E, 
+    0xAD, 0xFD, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xEF, 0xFF, 0xB3, 0xF7, 0xE7,
+    0xF7, 0xFA, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xEE, 0xEB, 0xAB, 0xAF, 0x9F, 0xE3, 0x7F, 0xFF, 0xDE,
+    0xFF, 0x7F, 0xEE, 0xFF, 
+    0xFF, 0xFB, 0x3A, 0xFA, 0xFF, 0xF2, 0x77, 0xFF, 0xFF, 0xF7, 0xFE, 0xFF,
+    0xFE, 0xBD, 0xAE, 0xDE, 
+    0x7D, 0x7D, 0xFD, 0xFF, 0xBF, 0xEE, 0xFF, 0xFD, 0xFF, 0xDB, 0xFB, 0xFF,
+    0xF7, 0xEF, 0xFB, 0xFF, 
+    0xFF, 0xFE, 0xFF, 0x2D, 0xAF, 0xB9, 0xFD, 0x79, 0xFB, 0xFA, 0xFF, 0xBF,
+    0xEF, 0xFF, 0xFF, 0x91, 
+    0xFA, 0xFB, 0xDF, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xFC, 0xCF, 0x37, 0xBF,
+    0xBF, 0xFF, 0x7F, 0x7F, 
+    0xFF, 0xFF, 0xFF, 0xAF, 0xFF, 0xFF, 0xF3, 0xFB, 0xFB, 0xFF, 0xF5, 0xEF,
+    0xFF, 0xFF, 0xF7, 0xFA, 
+    0xFF, 0xFF, 0xEE, 0xFA, 0xFE, 0xFB, 0x55, 0xDD, 0xFF, 0x7F, 0xAF, 0xFE,
+    0xFF, 0xFB, 0xFB, 0xF5, 
+    0xFF, 0xF7, 0xEF, 0xFF, 0xFF, 0xFF, 0xBE, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D,
+    0x7B, 0x7B, 0x7B, 0x7B, 
+    0xFB, 0xAE, 0xFF, 0xFD, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xF7, 0xDA, 0xB7, 0x61, 
+    0xFF, 0xB9, 0x59, 0xF3, 0x73, 0xF3, 0xDF, 0x7F, 0x6F, 0xDF, 0xEF, 0xF7,
+    0xEB, 0xEB, 0xD7, 0xFF, 
+    0xD7, 0xFF, 0xFF, 0xF7, 0xFE, 0x7F, 0xFB, 0x3E, 0x38, 0x73, 0xF6, 0x7F,
+    0xFC, 0xFF, 0xFF, 0xCF, 
+    0xFF, 0xB7, 0xFB, 0xB3, 0xB3, 0x67, 0xFF, 0xE7, 0xFD, 0xFF, 0xEF, 0xF6,
+    0x7F, 0xB7, 0xBC, 0xF5, 
+    0x7B, 0xF6, 0xF7, 0xF5, 0xFF, 0xFF, 0xEF, 0xFF, 0xF7, 0xFF, 0xF7, 0xCE,
+    0xE7, 0xFF, 0x9F, 0xFF, 
+    0xFF, 0xF5, 0xFE, 0x7D, 0xFF, 0x5F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xEF, 0xFF, 0xF6, 
+    0xCB, 0xDB, 0xEE, 0xFE, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFE, 0x7F, 0xBE,
+    0x1E, 0x3E, 0xFE, 0xFF, 
+    0x7D, 0xFE, 0xFF, 0xFF, 0xEF, 0xBF, 0xE7, 0xFF, 0xE3, 0xE3, 0xFF, 0xDF,
+    0xE7, 0xFF, 0xFF, 0xFF, 
+    0xB8, 0xEF, 0xB7, 0x2F, 0xEE, 0xFF, 0xDF, 0xFF, 0xBF, 0xFF, 0x7F, 0xEF,
+    0xEB, 0xBF, 0xA3, 0xD3, 
+    0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xBE, 0xFD, 0x3F, 0xCF, 0xFD,
+    0xFB, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xAF, 0xFB, 0xBF, 0xBB, 0xBF, 0xDB, 0xFD, 0xFB, 0xFF, 0xFF,
+    0xFF, 0xFF, 0x3E, 0xFE, 
+    0x3F, 0xBA, 0xBA, 0xFE, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xEF, 0xC3, 0x7F,
+    0xB2, 0x9B, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0x3C, 0xFF, 0x3F, 0x3C, 0xFF, 0xFE, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xAF, 0xF3, 0xFE, 0xF3, 0xE3, 0xEB, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xF7,
+    0x9A, 0xFE, 0xAF, 0x9E, 
+    0xBE, 0xFE, 0xFF, 0xDF, 0xFF, 0xFF, 0x7B, 0xEF, 0xF7, 0xBF, 0xFB, 0xFB,
+    0xFB, 0xFF, 0xFF, 0x7F, 
+    0xFF, 0xFF, 0xFF, 0xBC, 0xBD, 0xFD, 0xBD, 0xDD, 0x7D, 0x7B, 0x7B, 0x7B,
+    0x7B, 0xFB, 0xAE, 0xFF, 
+    0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xF7, 0x9A, 0xFF,
+    0x9F, 0xFF, 0xAF, 0xEF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xCF, 0xF3, 0xFF, 0xEB, 0xFF, 0xEB, 0xFF,
+    0xFF, 0xBF, 0xFF, 0xFF, 
+    0xEF, 0xFE, 0xFF, 0x37, 0xFC, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xCF, 0xEF, 0xFD, 0xF3, 
+    0xFF, 0xEE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0xFD, 0x2F, 0xFD,
+    0xFF, 0xFD, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xEF, 0xCF, 0xFF, 0xF3, 0xBF, 0x69, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFE, 
+    0xFB, 0x9F, 0xFF, 0xBF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x87,
+    0xFE, 0xDA, 0xEF, 0xCF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xEF, 0xBF, 0xEF, 0xEF, 0xFD,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xEF, 0xFD, 0xFF, 0x7B, 0xFF, 0xEB, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xEB, 0xF8, 0xFF, 0xEF, 
+    0xAF, 0xFF, 0xFF, 0xBD, 0xFF, 0xFF, 0xFF, 0x7F, 0xEE, 0x7F, 0xEF, 0xFF,
+    0xBB, 0xFF, 0xBF, 0xFB, 
+    0xFF, 0xFF, 0xFF, 0xF7, 0xF6, 0xFB, 0xBD, 0xFD, 0xDD, 0xF5, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xAF, 
+    0xFF, 0x5F, 0xF5, 0xDF, 0xFF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF6,
+    0xF3, 0xFF, 0xDE, 0xFE, 
+    0xEF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xDE, 0xDF, 0x5F, 0xDF,
+    0xFD, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xAF, 0xFF, 0xFF, 
+    0xEF, 0xED, 0xFF, 0xDF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xDA, 0xBD, 0xBE,
+    0xAE, 0xFE, 0x7F, 0xFD, 
+    0xDF, 0xFF, 0xFF, 0x7F, 0xEF, 0xFF, 0xFB, 0xFB, 0xFB, 0x7F, 0xF7, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xF7, 
+    0xBC, 0xFD, 0xBD, 0xBD, 0xBD, 0xFD, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAE,
+    0xFF, 0xFF, 0xFD, 0xFF, 
+    0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFA, 0x9F, 0xBF, 0xBF, 0xCF,
+    0x7F, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xAF, 0xFF, 0xEB, 0xEB, 0xEB, 0xFF, 0xD7, 0xFE, 0xFF, 0xFF,
+    0xBF, 0xE7, 0xFE, 0xBF, 
+    0x7F, 0xFC, 0xFF, 0xFF, 0xED, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0xFF, 0xFB,
+    0xFB, 0xFF, 0xFF, 0xDD, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBD, 0xDF, 0x9D, 0xFD, 0xDF, 0xB9,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xEF, 0xFF, 0xFB, 0xEF, 0xEB, 0xFF, 0xDE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xF6, 0x9F, 0xFF, 0xFC, 
+    0xFE, 0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xDF, 0xFA, 0xCD, 0xCF,
+    0xBF, 0x9F, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xF7, 0xFE, 0xBF, 0xFF, 0xDF, 0xEF, 0x5F, 0xFF, 0xFF, 0xFF,
+    0xFF, 0x7F, 0x6F, 0xFF, 
+    0xBB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0xFF,
+    0x5F, 0xFF, 0xBF, 0xBF, 
+    0xF9, 0xFF, 0xFF, 0xFF, 0x7F, 0x6E, 0x7B, 0xFF, 0xEF, 0xFD, 0xEB, 0xDF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xF7, 0xB6, 0x3E, 0xFC, 0xFD, 0xBF, 0x7E, 0xFB, 0xFF, 0xFF, 0xFF, 0xF7,
+    0xEF, 0xF7, 0xF3, 0xF7, 
+    0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6E, 0x35, 0x79, 0xFF,
+    0xBF, 0xFC, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xEF, 0xFB, 0x53, 0xDF, 0xFF, 0xEB, 0xBF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xBC, 
+    0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xF5,
+    0xFF, 0xF7, 0xFF, 0xFB, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBA, 0xAA, 0xEE, 0xFE, 0x3F, 0x7D,
+    0xFD, 0xFF, 0xFF, 0xFF, 
+    0x7F, 0xAF, 0x77, 0xFB, 0xFB, 0xFF, 0xFB, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xF7, 0xBE, 0xBD, 0xBD, 
+    0xBD, 0xBD, 0xFD, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAE, 0xFF, 0xEF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFC, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0x9A, 0xD9, 0xB8, 0xFF, 0xFF, 0x79, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xCF, 
+    0xFB, 0xFF, 0xEB, 0xFF, 0xEB, 0xD7, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xDE,
+    0xF8, 0xFB, 0xFE, 0x3F, 
+    0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xAD, 0xBF, 0xFA, 0xFF, 0x73,
+    0xDF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0x3A, 0xF5, 0xB7, 0xFC, 0x3F, 0xF9, 0xFD, 0xFF, 0xFF, 0xFF,
+    0x7F, 0xEF, 0xF3, 0xFF, 
+    0xBF, 0xFE, 0xF3, 0x9F, 0xFE, 0xFF, 0xFF, 0xFF, 0xF7, 0x3E, 0xFF, 0xFF,
+    0xFF, 0xBF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xD3, 0xFE, 0xDB, 0xFF, 0xDB, 0xDF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0x3E, 0xFF, 0xBF, 0xFF, 0x7F, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0x8F,
+    0xF3, 0xFF, 0xED, 0xFF, 
+    0xF7, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xF6, 0x3C, 0xFE, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0x9F, 0xEF, 0xEF, 0xD1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0x7E, 0xBF, 
+    0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBB, 0xEF, 0xDF, 0xF1,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE, 0x3E, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xBF, 
+    0xEF, 0xFD, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF,
+    0xFC, 0x3E, 0xFE, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2E, 0xEF, 0xF3, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xF7, 0xBA, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0x7F, 0xAF, 0xFB, 
+    0xFB, 0xFD, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xF2, 0xD6, 0xED,
+    0xBD, 0xBD, 0xBD, 0x7D, 
+    0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0x92, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F,
+    0xAF, 0xEB, 0xEB, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFE, 0x2E, 0xFE, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0x4F, 0xEF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFE, 
+    0x3C, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xCE,
+    0xC3, 0xFD, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xEF, 0xCF, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xF7, 0xEE, 0x3E, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xDF, 0xE2, 0xFF,
+    0xFF, 0xFF, 0xFB, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xF6, 0xBE, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0x7F, 0xEE, 
+    0x5F, 0xE6, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E,
+    0x7D, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xF3, 0xFB, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xBF, 0xF7, 0x36, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xEF, 0xD3, 0xF6, 
+    0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x7F, 0xEE,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xAF, 0xEF, 0xEB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xBA, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEE,
+    0xFB, 0xFA, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xD6, 0xFD, 0xBD, 0xBD, 0xBD,
+    0x7D, 0x7B, 0x7B, 0x7B, 
+    0x7B, 0xFB, 0xAE, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xF7, 0xBA, 0xBF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xEB, 0x6B,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFE, 0xBE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0x4F, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF,
+    0x3E, 0x6E, 0xFC, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xC3, 0xC9, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0x3E, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xEF, 0xFB, 
+    0xD5, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE,
+    0xFE, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x6F, 0xEF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFB,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xF6, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE,
+    0xEF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7, 0xFF, 0xFE, 0xFF, 0xF7, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0x7F, 0xFA, 0xEF, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xE7, 0xFF, 0xFE, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xFE, 0xEF, 0xBF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xA7, 0xFF, 0xFC, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0x7F, 
+    0xFE, 0xAE, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE7,
+    0xF7, 0xFA, 0xFF, 0xFD, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xAF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xF7, 0xBE, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B,
+    0x7B, 0x7B, 0xFB, 0xAF, 
+    0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCA,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x6F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xE7, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xCF, 0xFE, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xDF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xE7, 0xF2, 0xFC, 
+    0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xAE, 0xEF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x7E, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xEF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF,
+    0xFE, 0xFE, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDF, 0xEF, 0xDD, 0xFE, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xAF, 0xEF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBA, 0xFE,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFA, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xF6, 0x9C, 0xBD, 0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB,
+    0xAE, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0x7A, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xDF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0x6F, 0xEF, 0xF7, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xF7, 0xFE, 
+    0xFE, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xEB,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0x9E, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xEF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFE, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xEF, 0xCB, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFD, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xBE, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xEF, 
+    0xEF, 0xFF, 0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF8,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFB, 0xAF, 0x7F, 0xFF, 
+    0xFF, 0xFF, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xEF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xBF, 0xFF, 
+    0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAE,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0x7F, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xF7, 0xBC, 0xBD, 
+    0xBD, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xAF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xFA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0x7F, 
+    0xAF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF,
+    0xFE, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xCF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF,
+    0xFF, 0xFF, 0xEF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF,
+    0xFF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xBF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+    0xEF, 0xFF, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xFE, 0xFF, 0x9F, 0x9F,
+    0x9F, 0x3F, 0x3F, 0x3F, 
+    0x3F, 0x3F, 0xFF, 0xEF, 0xDF, 0xDF, 0xDF, 0xDF, 0xCF, 0xB7, 0xBF, 0xBF,
+    0xBF, 0xBF, 0xFF, 0xBC, 
+    0xB9, 0x9D, 0xBD, 0xBD, 0x7D, 0x7B, 0x7B, 0x7B, 0x7B, 0xFB, 0xEF, 0xD7,
+    0xF5, 0xF3, 0xF1, 0xD1, 
+    0x65, 0xE3, 0xE3, 0xE3, 0xA3, 0xFF, 0xFE, 0x7F, 0xFE, 0xDE, 0xDE, 0xFF,
+    0xBD, 0xBD, 0xBD, 0xBD, 
+    0xDF, 0xEF, 0xFB, 0xF7, 0xF3, 0xF3, 0xF3, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7,
+    0xFB, 0xFE, 0xFF, 0xFF, 
+    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+    
+    };
diff --git a/drivers/net/pcmcia/pcnet_cs.c b/drivers/net/pcmcia/pcnet_cs.c
new file mode 100644
index 000000000000..b0126304ca08
--- /dev/null
+++ b/drivers/net/pcmcia/pcnet_cs.c
@@ -0,0 +1,1659 @@
+/*======================================================================
+
+    A PCMCIA ethernet driver for NS8390-based cards
+
+    This driver supports the D-Link DE-650 and Linksys EthernetCard
+    cards, the newer D-Link and Linksys combo cards, Accton EN2212
+    cards, the RPTI EP400, and the PreMax PE-200 in non-shared-memory
+    mode, and the IBM Credit Card Adapter, the NE4100, the Thomas
+    Conrad ethernet card, and the Kingston KNE-PCM/x in shared-memory
+    mode.  It will also handle the Socket EA card in either mode.
+
+    Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
+
+    pcnet_cs.c 1.153 2003/11/09 18:53:09
+    
+    The network driver code is based on Donald Becker's NE2000 code:
+
+    Written 1992,1993 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 of the GNU General Public License,
+    incorporated herein by reference.
+    Donald Becker may be reached at becker@scyld.com
+
+    Based also on Keith Moore's changes to Don Becker's code, for IBM
+    CCAE support.  Drivers merged back together, and shared-memory
+    Socket EA support added, by Ken Raeburn, September 1995.
+
+======================================================================*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <../drivers/net/8390.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define PCNET_CMD	0x00
+#define PCNET_DATAPORT	0x10	/* NatSemi-defined port window offset. */
+#define PCNET_RESET	0x1f	/* Issue a read to reset, a write to clear. */
+#define PCNET_MISC	0x18	/* For IBM CCAE and Socket EA cards */
+
+#define PCNET_START_PG	0x40	/* First page of TX buffer */
+#define PCNET_STOP_PG	0x80	/* Last page +1 of RX ring */
+
+/* Socket EA cards have a larger packet buffer */
+#define SOCKET_START_PG	0x01
+#define SOCKET_STOP_PG	0xff
+
+#define PCNET_RDC_TIMEOUT (2*HZ/100)	/* Max wait in jiffies for Tx RDC */
+
+static char *if_names[] = { "auto", "10baseT", "10base2"};
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+static char *version =
+"pcnet_cs.c 1.153 2003/11/09 18:53:09 (David Hinds)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>");
+MODULE_DESCRIPTION("NE2000 compatible PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+INT_MODULE_PARM(if_port,	1);	/* Transceiver type */
+INT_MODULE_PARM(use_big_buf,	1);	/* use 64K packet buffer? */
+INT_MODULE_PARM(mem_speed,	0);	/* shared mem speed, in ns */
+INT_MODULE_PARM(delay_output,	0);	/* pause after xmit? */
+INT_MODULE_PARM(delay_time,	4);	/* in usec */
+INT_MODULE_PARM(use_shmem,	-1);	/* use shared memory? */
+INT_MODULE_PARM(full_duplex,	0);	/* full duplex? */
+
+/* Ugh!  Let the user hardwire the hardware address for queer cards */
+static int hw_addr[6] = { 0, /* ... */ };
+module_param_array(hw_addr, int, NULL, 0);
+
+/*====================================================================*/
+
+static void mii_phy_probe(struct net_device *dev);
+static void pcnet_config(dev_link_t *link);
+static void pcnet_release(dev_link_t *link);
+static int pcnet_event(event_t event, int priority,
+		       event_callback_args_t *args);
+static int pcnet_open(struct net_device *dev);
+static int pcnet_close(struct net_device *dev);
+static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static struct ethtool_ops netdev_ethtool_ops;
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs);
+static void ei_watchdog(u_long arg);
+static void pcnet_reset_8390(struct net_device *dev);
+static int set_config(struct net_device *dev, struct ifmap *map);
+static int setup_shmem_window(dev_link_t *link, int start_pg,
+			      int stop_pg, int cm_offset);
+static int setup_dma_config(dev_link_t *link, int start_pg,
+			    int stop_pg);
+
+static dev_link_t *pcnet_attach(void);
+static void pcnet_detach(dev_link_t *);
+
+static dev_info_t dev_info = "pcnet_cs";
+static dev_link_t *dev_list;
+
+/*====================================================================*/
+
+typedef struct hw_info_t {
+    u_int	offset;
+    u_char	a0, a1, a2;
+    u_int	flags;
+} hw_info_t;
+
+#define DELAY_OUTPUT	0x01
+#define HAS_MISC_REG	0x02
+#define USE_BIG_BUF	0x04
+#define HAS_IBM_MISC	0x08
+#define IS_DL10019	0x10
+#define IS_DL10022	0x20
+#define HAS_MII		0x40
+#define USE_SHMEM	0x80	/* autodetected */
+
+#define AM79C9XX_HOME_PHY	0x00006B90  /* HomePNA PHY */
+#define AM79C9XX_ETH_PHY	0x00006B70  /* 10baseT PHY */
+#define MII_PHYID_REV_MASK	0xfffffff0
+#define MII_PHYID_REG1		0x02
+#define MII_PHYID_REG2		0x03
+
+static hw_info_t hw_info[] = {
+    { /* Accton EN2212 */ 0x0ff0, 0x00, 0x00, 0xe8, DELAY_OUTPUT }, 
+    { /* Allied Telesis LA-PCM */ 0x0ff0, 0x00, 0x00, 0xf4, 0 },
+    { /* APEX MultiCard */ 0x03f4, 0x00, 0x20, 0xe5, 0 },
+    { /* ASANTE FriendlyNet */ 0x4910, 0x00, 0x00, 0x94,
+      DELAY_OUTPUT | HAS_IBM_MISC },
+    { /* Danpex EN-6200P2 */ 0x0110, 0x00, 0x40, 0xc7, 0 },
+    { /* DataTrek NetCard */ 0x0ff0, 0x00, 0x20, 0xe8, 0 },
+    { /* Dayna CommuniCard E */ 0x0110, 0x00, 0x80, 0x19, 0 },
+    { /* D-Link DE-650 */ 0x0040, 0x00, 0x80, 0xc8, 0 },
+    { /* EP-210 Ethernet */ 0x0110, 0x00, 0x40, 0x33, 0 },
+    { /* EP4000 Ethernet */ 0x01c0, 0x00, 0x00, 0xb4, 0 },
+    { /* Epson EEN10B */ 0x0ff0, 0x00, 0x00, 0x48,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* ELECOM Laneed LD-CDWA */ 0xb8, 0x08, 0x00, 0x42, 0 },
+    { /* Hypertec Ethernet */ 0x01c0, 0x00, 0x40, 0x4c, 0 },
+    { /* IBM CCAE */ 0x0ff0, 0x08, 0x00, 0x5a,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* IBM CCAE */ 0x0ff0, 0x00, 0x04, 0xac,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* IBM CCAE */ 0x0ff0, 0x00, 0x06, 0x29,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* IBM FME */ 0x0374, 0x08, 0x00, 0x5a,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* IBM FME */ 0x0374, 0x00, 0x04, 0xac,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* Kansai KLA-PCM/T */ 0x0ff0, 0x00, 0x60, 0x87,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* NSC DP83903 */ 0x0374, 0x08, 0x00, 0x17,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* NSC DP83903 */ 0x0374, 0x00, 0xc0, 0xa8,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* NSC DP83903 */ 0x0374, 0x00, 0xa0, 0xb0,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* NSC DP83903 */ 0x0198, 0x00, 0x20, 0xe0,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* I-O DATA PCLA/T */ 0x0ff0, 0x00, 0xa0, 0xb0, 0 },
+    { /* Katron PE-520 */ 0x0110, 0x00, 0x40, 0xf6, 0 },
+    { /* Kingston KNE-PCM/x */ 0x0ff0, 0x00, 0xc0, 0xf0,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* Kingston KNE-PCM/x */ 0x0ff0, 0xe2, 0x0c, 0x0f,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* Kingston KNE-PC2 */ 0x0180, 0x00, 0xc0, 0xf0, 0 },
+    { /* Maxtech PCN2000 */ 0x5000, 0x00, 0x00, 0xe8, 0 },
+    { /* NDC Instant-Link */ 0x003a, 0x00, 0x80, 0xc6, 0 },
+    { /* NE2000 Compatible */ 0x0ff0, 0x00, 0xa0, 0x0c, 0 },
+    { /* Network General Sniffer */ 0x0ff0, 0x00, 0x00, 0x65,
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* Panasonic VEL211 */ 0x0ff0, 0x00, 0x80, 0x45, 
+      HAS_MISC_REG | HAS_IBM_MISC },
+    { /* PreMax PE-200 */ 0x07f0, 0x00, 0x20, 0xe0, 0 },
+    { /* RPTI EP400 */ 0x0110, 0x00, 0x40, 0x95, 0 },
+    { /* SCM Ethernet */ 0x0ff0, 0x00, 0x20, 0xcb, 0 },
+    { /* Socket EA */ 0x4000, 0x00, 0xc0, 0x1b,
+      DELAY_OUTPUT | HAS_MISC_REG | USE_BIG_BUF },
+    { /* Socket LP-E CF+ */ 0x01c0, 0x00, 0xc0, 0x1b, 0 },
+    { /* SuperSocket RE450T */ 0x0110, 0x00, 0xe0, 0x98, 0 },
+    { /* Volktek NPL-402CT */ 0x0060, 0x00, 0x40, 0x05, 0 },
+    { /* NEC PC-9801N-J12 */ 0x0ff0, 0x00, 0x00, 0x4c, 0 },
+    { /* PCMCIA Technology OEM */ 0x01c8, 0x00, 0xa0, 0x0c, 0 }
+};
+
+#define NR_INFO		(sizeof(hw_info)/sizeof(hw_info_t))
+
+static hw_info_t default_info = { 0, 0, 0, 0, 0 };
+static hw_info_t dl10019_info = { 0, 0, 0, 0, IS_DL10019|HAS_MII };
+static hw_info_t dl10022_info = { 0, 0, 0, 0, IS_DL10022|HAS_MII };
+
+typedef struct pcnet_dev_t {
+    dev_link_t		link;
+    dev_node_t		node;
+    u_int		flags;
+    void		__iomem *base;
+    struct timer_list	watchdog;
+    int			stale, fast_poll;
+    u_char		phy_id;
+    u_char		eth_phy, pna_phy;
+    u_short		link_status;
+    u_long		mii_reset;
+} pcnet_dev_t;
+
+static inline pcnet_dev_t *PRIV(struct net_device *dev)
+{
+	char *p = netdev_priv(dev);
+	return (pcnet_dev_t *)(p + sizeof(struct ei_device));
+}
+
+/*======================================================================
+
+    pcnet_attach() creates an "instance" of the driver, allocating
+    local data structures for one device.  The device is registered
+    with Card Services.
+
+======================================================================*/
+
+static dev_link_t *pcnet_attach(void)
+{
+    pcnet_dev_t *info;
+    dev_link_t *link;
+    struct net_device *dev;
+    client_reg_t client_reg;
+    int ret;
+
+    DEBUG(0, "pcnet_attach()\n");
+
+    /* Create new ethernet device */
+    dev = __alloc_ei_netdev(sizeof(pcnet_dev_t));
+    if (!dev) return NULL;
+    info = PRIV(dev);
+    link = &info->link;
+    link->priv = dev;
+
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    SET_MODULE_OWNER(dev);
+    dev->open = &pcnet_open;
+    dev->stop = &pcnet_close;
+    dev->set_config = &set_config;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &pcnet_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != CS_SUCCESS) {
+	cs_error(link->handle, RegisterClient, ret);
+	pcnet_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* pcnet_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void pcnet_detach(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    dev_link_t **linkp;
+
+    DEBUG(0, "pcnet_detach(0x%p)\n", link);
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    if (link->dev)
+	unregister_netdev(dev);
+
+    if (link->state & DEV_CONFIG)
+	pcnet_release(link);
+
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    free_netdev(dev);
+} /* pcnet_detach */
+
+/*======================================================================
+
+    This probes for a card's hardware address, for card types that
+    encode this information in their CIS.
+
+======================================================================*/
+
+static hw_info_t *get_hwinfo(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    win_req_t req;
+    memreq_t mem;
+    u_char __iomem *base, *virt;
+    int i, j;
+
+    /* Allocate a small memory window */
+    req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+    req.Base = 0; req.Size = 0;
+    req.AccessSpeed = 0;
+    i = pcmcia_request_window(&link->handle, &req, &link->win);
+    if (i != CS_SUCCESS) {
+	cs_error(link->handle, RequestWindow, i);
+	return NULL;
+    }
+
+    virt = ioremap(req.Base, req.Size);
+    mem.Page = 0;
+    for (i = 0; i < NR_INFO; i++) {
+	mem.CardOffset = hw_info[i].offset & ~(req.Size-1);
+	pcmcia_map_mem_page(link->win, &mem);
+	base = &virt[hw_info[i].offset & (req.Size-1)];
+	if ((readb(base+0) == hw_info[i].a0) &&
+	    (readb(base+2) == hw_info[i].a1) &&
+	    (readb(base+4) == hw_info[i].a2))
+	    break;
+    }
+    if (i < NR_INFO) {
+	for (j = 0; j < 6; j++)
+	    dev->dev_addr[j] = readb(base + (j<<1));
+    }
+    
+    iounmap(virt);
+    j = pcmcia_release_window(link->win);
+    if (j != CS_SUCCESS)
+	cs_error(link->handle, ReleaseWindow, j);
+    return (i < NR_INFO) ? hw_info+i : NULL;
+} /* get_hwinfo */
+
+/*======================================================================
+
+    This probes for a card's hardware address by reading the PROM.
+    It checks the address against a list of known types, then falls
+    back to a simple NE2000 clone signature check.
+
+======================================================================*/
+
+static hw_info_t *get_prom(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    kio_addr_t ioaddr = dev->base_addr;
+    u_char prom[32];
+    int i, j;
+
+    /* This is lifted straight from drivers/net/ne.c */
+    struct {
+	u_char value, offset;
+    } program_seq[] = {
+	{E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+	{0x48,	EN0_DCFG},	/* Set byte-wide (0x48) access. */
+	{0x00,	EN0_RCNTLO},	/* Clear the count regs. */
+	{0x00,	EN0_RCNTHI},
+	{0x00,	EN0_IMR},	/* Mask completion irq. */
+	{0xFF,	EN0_ISR},
+	{E8390_RXOFF, EN0_RXCR},	/* 0x20  Set to monitor */
+	{E8390_TXOFF, EN0_TXCR},	/* 0x02  and loopback mode. */
+	{32,	EN0_RCNTLO},
+	{0x00,	EN0_RCNTHI},
+	{0x00,	EN0_RSARLO},	/* DMA starting at 0x0000. */
+	{0x00,	EN0_RSARHI},
+	{E8390_RREAD+E8390_START, E8390_CMD},
+    };
+
+    pcnet_reset_8390(dev);
+    mdelay(10);
+
+    for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
+	outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+    for (i = 0; i < 32; i++)
+	prom[i] = inb(ioaddr + PCNET_DATAPORT);
+    for (i = 0; i < NR_INFO; i++) {
+	if ((prom[0] == hw_info[i].a0) &&
+	    (prom[2] == hw_info[i].a1) &&
+	    (prom[4] == hw_info[i].a2))
+	    break;
+    }
+    if ((i < NR_INFO) || ((prom[28] == 0x57) && (prom[30] == 0x57))) {
+	for (j = 0; j < 6; j++)
+	    dev->dev_addr[j] = prom[j<<1];
+	return (i < NR_INFO) ? hw_info+i : &default_info;
+    }
+    return NULL;
+} /* get_prom */
+
+/*======================================================================
+
+    For DL10019 based cards, like the Linksys EtherFast
+
+======================================================================*/
+
+static hw_info_t *get_dl10019(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    int i;
+    u_char sum;
+
+    for (sum = 0, i = 0x14; i < 0x1c; i++)
+	sum += inb_p(dev->base_addr + i);
+    if (sum != 0xff)
+	return NULL;
+    for (i = 0; i < 6; i++)
+	dev->dev_addr[i] = inb_p(dev->base_addr + 0x14 + i);
+    i = inb(dev->base_addr + 0x1f);
+    return ((i == 0x91)||(i == 0x99)) ? &dl10022_info : &dl10019_info;
+}
+
+/*======================================================================
+
+    For Asix AX88190 based cards
+
+======================================================================*/
+
+static hw_info_t *get_ax88190(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    kio_addr_t ioaddr = dev->base_addr;
+    int i, j;
+
+    /* Not much of a test, but the alternatives are messy */
+    if (link->conf.ConfigBase != 0x03c0)
+	return NULL;
+
+    outb_p(0x01, ioaddr + EN0_DCFG);	/* Set word-wide access. */
+    outb_p(0x00, ioaddr + EN0_RSARLO);	/* DMA starting at 0x0400. */
+    outb_p(0x04, ioaddr + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, ioaddr + E8390_CMD);
+
+    for (i = 0; i < 6; i += 2) {
+	j = inw(ioaddr + PCNET_DATAPORT);
+	dev->dev_addr[i] = j & 0xff;
+	dev->dev_addr[i+1] = j >> 8;
+    }
+    printk(KERN_NOTICE "pcnet_cs: this is an AX88190 card!\n");
+    printk(KERN_NOTICE "pcnet_cs: use axnet_cs instead.\n");
+    return NULL;
+}
+
+/*======================================================================
+
+    This should be totally unnecessary... but when we can't figure
+    out the hardware address any other way, we'll let the user hard
+    wire it when the module is initialized.
+
+======================================================================*/
+
+static hw_info_t *get_hwired(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    int i;
+
+    for (i = 0; i < 6; i++)
+	if (hw_addr[i] != 0) break;
+    if (i == 6)
+	return NULL;
+
+    for (i = 0; i < 6; i++)
+	dev->dev_addr[i] = hw_addr[i];
+
+    return &default_info;
+} /* get_hwired */
+
+/*======================================================================
+
+    pcnet_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    ethernet device available to the system.
+
+======================================================================*/
+
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static int try_io_port(dev_link_t *link)
+{
+    int j, ret;
+    if (link->io.NumPorts1 == 32) {
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+	if (link->io.NumPorts2 > 0) {
+	    /* for master/slave multifunction cards */
+	    link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+	    link->irq.Attributes = 
+		IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+	}
+    } else {
+	/* This should be two 16-port windows */
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+	link->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
+    }
+    if (link->io.BasePort1 == 0) {
+	link->io.IOAddrLines = 16;
+	for (j = 0; j < 0x400; j += 0x20) {
+	    link->io.BasePort1 = j ^ 0x300;
+	    link->io.BasePort2 = (j ^ 0x300) + 0x10;
+	    ret = pcmcia_request_io(link->handle, &link->io);
+	    if (ret == CS_SUCCESS) return ret;
+	}
+	return ret;
+    } else {
+	return pcmcia_request_io(link->handle, &link->io);
+    }
+}
+
+static void pcnet_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    pcnet_dev_t *info = PRIV(dev);
+    tuple_t tuple;
+    cisparse_t parse;
+    int i, last_ret, last_fn, start_pg, stop_pg, cm_offset;
+    int manfid = 0, prodid = 0, has_shmem = 0;
+    u_short buf[64];
+    config_info_t conf;
+    hw_info_t *hw_info;
+
+    DEBUG(0, "pcnet_config(0x%p)\n", link);
+
+    tuple.Attributes = 0;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.TupleOffset = 0;
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    CS_CHECK(GetTupleData, pcmcia_get_tuple_data(handle, &tuple));
+    CS_CHECK(ParseTuple, pcmcia_parse_tuple(handle, &tuple, &parse));
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    /* Look up current Vcc */
+    CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(handle, &conf));
+    link->conf.Vcc = conf.Vcc;
+
+    tuple.DesiredTuple = CISTPL_MANFID;
+    tuple.Attributes = TUPLE_RETURN_COMMON;
+    if ((pcmcia_get_first_tuple(handle, &tuple) == CS_SUCCESS) &&
+ 	(pcmcia_get_tuple_data(handle, &tuple) == CS_SUCCESS)) {
+	manfid = le16_to_cpu(buf[0]);
+	prodid = le16_to_cpu(buf[1]);
+    }
+    
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+    tuple.Attributes = 0;
+    CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(handle, &tuple));
+    while (last_ret == CS_SUCCESS) {
+	cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
+	cistpl_io_t *io = &(parse.cftable_entry.io);
+	
+	if (pcmcia_get_tuple_data(handle, &tuple) != 0 ||
+			pcmcia_parse_tuple(handle, &tuple, &parse) != 0 ||
+			cfg->index == 0 || cfg->io.nwin == 0)
+		goto next_entry;
+	
+	link->conf.ConfigIndex = cfg->index;
+	/* For multifunction cards, by convention, we configure the
+	   network function with window 0, and serial with window 1 */
+	if (io->nwin > 1) {
+	    i = (io->win[1].len > io->win[0].len);
+	    link->io.BasePort2 = io->win[1-i].base;
+	    link->io.NumPorts2 = io->win[1-i].len;
+	} else {
+	    i = link->io.NumPorts2 = 0;
+	}
+	has_shmem = ((cfg->mem.nwin == 1) &&
+		     (cfg->mem.win[0].len >= 0x4000));
+	link->io.BasePort1 = io->win[i].base;
+	link->io.NumPorts1 = io->win[i].len;
+	link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK;
+	if (link->io.NumPorts1 + link->io.NumPorts2 >= 32) {
+	    last_ret = try_io_port(link);
+	    if (last_ret == CS_SUCCESS) break;
+	}
+    next_entry:
+	last_ret = pcmcia_get_next_tuple(handle, &tuple);
+    }
+    if (last_ret != CS_SUCCESS) {
+	cs_error(handle, RequestIO, last_ret);
+	goto failed;
+    }
+
+    CS_CHECK(RequestIRQ, pcmcia_request_irq(handle, &link->irq));
+    
+    if (link->io.NumPorts2 == 8) {
+	link->conf.Attributes |= CONF_ENABLE_SPKR;
+	link->conf.Status = CCSR_AUDIO_ENA;
+    }
+    if ((manfid == MANFID_IBM) &&
+	(prodid == PRODID_IBM_HOME_AND_AWAY))
+	link->conf.ConfigIndex |= 0x10;
+    
+    CS_CHECK(RequestConfiguration, pcmcia_request_configuration(handle, &link->conf));
+    dev->irq = link->irq.AssignedIRQ;
+    dev->base_addr = link->io.BasePort1;
+    if (info->flags & HAS_MISC_REG) {
+	if ((if_port == 1) || (if_port == 2))
+	    dev->if_port = if_port;
+	else
+	    printk(KERN_NOTICE "pcnet_cs: invalid if_port requested\n");
+    } else {
+	dev->if_port = 0;
+    }
+
+    hw_info = get_hwinfo(link);
+    if (hw_info == NULL)
+	hw_info = get_prom(link);
+    if (hw_info == NULL)
+	hw_info = get_dl10019(link);
+    if (hw_info == NULL)
+	hw_info = get_ax88190(link);
+    if (hw_info == NULL)
+	hw_info = get_hwired(link);
+    
+    if (hw_info == NULL) {
+	printk(KERN_NOTICE "pcnet_cs: unable to read hardware net"
+	       " address for io base %#3lx\n", dev->base_addr);
+	goto failed;
+    }
+
+    info->flags = hw_info->flags;
+    /* Check for user overrides */
+    info->flags |= (delay_output) ? DELAY_OUTPUT : 0;
+    if ((manfid == MANFID_SOCKET) &&
+	((prodid == PRODID_SOCKET_LPE) ||
+	 (prodid == PRODID_SOCKET_LPE_CF) ||
+	 (prodid == PRODID_SOCKET_EIO)))
+	info->flags &= ~USE_BIG_BUF;
+    if (!use_big_buf)
+	info->flags &= ~USE_BIG_BUF;
+    
+    if (info->flags & USE_BIG_BUF) {
+	start_pg = SOCKET_START_PG;
+	stop_pg = SOCKET_STOP_PG;
+	cm_offset = 0x10000;
+    } else {
+	start_pg = PCNET_START_PG;
+	stop_pg = PCNET_STOP_PG;
+	cm_offset = 0;
+    }
+
+    /* has_shmem is ignored if use_shmem != -1 */
+    if ((use_shmem == 0) || (!has_shmem && (use_shmem == -1)) ||
+	(setup_shmem_window(link, start_pg, stop_pg, cm_offset) != 0))
+	setup_dma_config(link, start_pg, stop_pg);
+
+    ei_status.name = "NE2000";
+    ei_status.word16 = 1;
+    ei_status.reset_8390 = &pcnet_reset_8390;
+
+    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+
+    if (info->flags & (IS_DL10019|IS_DL10022)) {
+	u_char id = inb(dev->base_addr + 0x1a);
+	dev->do_ioctl = &ei_ioctl;
+	mii_phy_probe(dev);
+	if ((id == 0x30) && !info->pna_phy && (info->eth_phy == 4))
+	    info->eth_phy = 0;
+    }
+
+    link->dev = &info->node;
+    link->state &= ~DEV_CONFIG_PENDING;
+    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+    dev->poll_controller = ei_poll;
+#endif
+
+    if (register_netdev(dev) != 0) {
+	printk(KERN_NOTICE "pcnet_cs: register_netdev() failed\n");
+	link->dev = NULL;
+	goto failed;
+    }
+
+    strcpy(info->node.dev_name, dev->name);
+
+    if (info->flags & (IS_DL10019|IS_DL10022)) {
+	u_char id = inb(dev->base_addr + 0x1a);
+	printk(KERN_INFO "%s: NE2000 (DL100%d rev %02x): ",
+	       dev->name, ((info->flags & IS_DL10022) ? 22 : 19), id);
+	if (info->pna_phy)
+	    printk("PNA, ");
+    } else {
+	printk(KERN_INFO "%s: NE2000 Compatible: ", dev->name);
+    }
+    printk("io %#3lx, irq %d,", dev->base_addr, dev->irq);
+    if (info->flags & USE_SHMEM)
+	printk (" mem %#5lx,", dev->mem_start);
+    if (info->flags & HAS_MISC_REG)
+	printk(" %s xcvr,", if_names[dev->if_port]);
+    printk(" hw_addr ");
+    for (i = 0; i < 6; i++)
+	printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+    return;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    pcnet_release(link);
+    link->state &= ~DEV_CONFIG_PENDING;
+    return;
+} /* pcnet_config */
+
+/*======================================================================
+
+    After a card is removed, pcnet_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void pcnet_release(dev_link_t *link)
+{
+    pcnet_dev_t *info = PRIV(link->priv);
+
+    DEBUG(0, "pcnet_release(0x%p)\n", link);
+
+    if (info->flags & USE_SHMEM) {
+	iounmap(info->base);
+	pcmcia_release_window(link->win);
+    }
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+
+    link->state &= ~DEV_CONFIG;
+}
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+======================================================================*/
+
+static int pcnet_event(event_t event, int priority,
+		       event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    struct net_device *dev = link->priv;
+
+    DEBUG(2, "pcnet_event(0x%06x)\n", event);
+
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG)
+	    netif_device_detach(dev);
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	pcnet_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG) {
+	    if (link->open)
+		netif_device_detach(dev);
+	    pcmcia_release_configuration(link->handle);
+	}
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG) {
+	    pcmcia_request_configuration(link->handle, &link->conf);
+	    if (link->open) {
+		pcnet_reset_8390(dev);
+		NS8390_init(dev, 1);
+		netif_device_attach(dev);
+	    }
+	}
+	break;
+    }
+    return 0;
+} /* pcnet_event */
+
+/*======================================================================
+
+    MII interface support for DL10019 and DL10022 based cards
+
+    On the DL10019, the MII IO direction bit is 0x10; on the DL10022
+    it is 0x20.  Setting both bits seems to work on both card types.
+
+======================================================================*/
+
+#define DLINK_GPIO		0x1c
+#define DLINK_DIAG		0x1d
+#define DLINK_EEPROM		0x1e
+
+#define MDIO_SHIFT_CLK		0x80
+#define MDIO_DATA_OUT		0x40
+#define MDIO_DIR_WRITE		0x30
+#define MDIO_DATA_WRITE0	(MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1	(MDIO_DIR_WRITE | MDIO_DATA_OUT)
+#define MDIO_DATA_READ		0x10
+#define MDIO_MASK		0x0f
+
+static void mdio_sync(kio_addr_t addr)
+{
+    int bits, mask = inb(addr) & MDIO_MASK;
+    for (bits = 0; bits < 32; bits++) {
+	outb(mask | MDIO_DATA_WRITE1, addr);
+	outb(mask | MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+    }
+}
+
+static int mdio_read(kio_addr_t addr, int phy_id, int loc)
+{
+    u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
+    int i, retval = 0, mask = inb(addr) & MDIO_MASK;
+
+    mdio_sync(addr);
+    for (i = 13; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb(mask | dat, addr);
+	outb(mask | dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 19; i > 0; i--) {
+	outb(mask, addr);
+	retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
+	outb(mask | MDIO_SHIFT_CLK, addr);
+    }
+    return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(kio_addr_t addr, int phy_id, int loc, int value)
+{
+    u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+    int i, mask = inb(addr) & MDIO_MASK;
+
+    mdio_sync(addr);
+    for (i = 31; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb(mask | dat, addr);
+	outb(mask | dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 1; i >= 0; i--) {
+	outb(mask, addr);
+	outb(mask | MDIO_SHIFT_CLK, addr);
+    }
+}
+
+static void mdio_reset(kio_addr_t addr, int phy_id)
+{
+    outb_p(0x08, addr);
+    outb_p(0x0c, addr);
+    outb_p(0x08, addr);
+    outb_p(0x0c, addr);
+    outb_p(0x00, addr);
+}
+
+/*======================================================================
+
+    EEPROM access routines for DL10019 and DL10022 based cards
+
+======================================================================*/
+
+#define EE_EEP		0x40
+#define EE_ASIC		0x10
+#define EE_CS		0x08
+#define EE_CK		0x04
+#define EE_DO		0x02
+#define EE_DI		0x01
+#define EE_ADOT		0x01	/* DataOut for ASIC */
+#define EE_READ_CMD	0x06
+
+#define DL19FDUPLX	0x0400	/* DL10019 Full duplex mode */
+
+static int read_eeprom(kio_addr_t ioaddr, int location)
+{
+    int i, retval = 0;
+    kio_addr_t ee_addr = ioaddr + DLINK_EEPROM;
+    int read_cmd = location | (EE_READ_CMD << 8);
+
+    outb(0, ee_addr);
+    outb(EE_EEP|EE_CS, ee_addr);
+
+    /* Shift the read command bits out. */
+    for (i = 10; i >= 0; i--) {
+	short dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
+	outb_p(EE_EEP|EE_CS|dataval, ee_addr);
+	outb_p(EE_EEP|EE_CS|dataval|EE_CK, ee_addr);
+    }
+    outb(EE_EEP|EE_CS, ee_addr);
+
+    for (i = 16; i > 0; i--) {
+	outb_p(EE_EEP|EE_CS | EE_CK, ee_addr);
+	retval = (retval << 1) | ((inb(ee_addr) & EE_DI) ? 1 : 0);
+	outb_p(EE_EEP|EE_CS, ee_addr);
+    }
+
+    /* Terminate the EEPROM access. */
+    outb(0, ee_addr);
+    return retval;
+}
+
+/*
+    The internal ASIC registers can be changed by EEPROM READ access
+    with EE_ASIC bit set.
+    In ASIC mode, EE_ADOT is used to output the data to the ASIC.
+*/
+
+static void write_asic(kio_addr_t ioaddr, int location, short asic_data)
+{
+	int i;
+	kio_addr_t ee_addr = ioaddr + DLINK_EEPROM;
+	short dataval;
+	int read_cmd = location | (EE_READ_CMD << 8);
+
+	asic_data |= read_eeprom(ioaddr, location);
+
+	outb(0, ee_addr);
+	outb(EE_ASIC|EE_CS|EE_DI, ee_addr);
+
+	read_cmd = read_cmd >> 1;
+
+	/* Shift the read command bits out. */
+	for (i = 9; i >= 0; i--) {
+		dataval = (read_cmd & (1 << i)) ? EE_DO : 0;
+		outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
+		outb_p(EE_ASIC|EE_CS|EE_DI|dataval|EE_CK, ee_addr);
+		outb_p(EE_ASIC|EE_CS|EE_DI|dataval, ee_addr);
+	}
+	// sync
+	outb(EE_ASIC|EE_CS, ee_addr);
+	outb(EE_ASIC|EE_CS|EE_CK, ee_addr);
+	outb(EE_ASIC|EE_CS, ee_addr);
+
+	for (i = 15; i >= 0; i--) {
+		dataval = (asic_data & (1 << i)) ? EE_ADOT : 0;
+		outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
+		outb_p(EE_ASIC|EE_CS|dataval|EE_CK, ee_addr);
+		outb_p(EE_ASIC|EE_CS|dataval, ee_addr);
+	}
+
+	/* Terminate the ASIC access. */
+	outb(EE_ASIC|EE_DI, ee_addr);
+	outb(EE_ASIC|EE_DI| EE_CK, ee_addr);
+	outb(EE_ASIC|EE_DI, ee_addr);
+
+	outb(0, ee_addr);
+}
+
+/*====================================================================*/
+
+static void set_misc_reg(struct net_device *dev)
+{
+    kio_addr_t nic_base = dev->base_addr;
+    pcnet_dev_t *info = PRIV(dev);
+    u_char tmp;
+    
+    if (info->flags & HAS_MISC_REG) {
+	tmp = inb_p(nic_base + PCNET_MISC) & ~3;
+	if (dev->if_port == 2)
+	    tmp |= 1;
+	if (info->flags & USE_BIG_BUF)
+	    tmp |= 2;
+	if (info->flags & HAS_IBM_MISC)
+	    tmp |= 8;
+	outb_p(tmp, nic_base + PCNET_MISC);
+    }
+    if (info->flags & IS_DL10022) {
+	if (info->flags & HAS_MII) {
+	    mdio_reset(nic_base + DLINK_GPIO, info->eth_phy);
+	    /* Restart MII autonegotiation */
+	    mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x0000);
+	    mdio_write(nic_base + DLINK_GPIO, info->eth_phy, 0, 0x1200);
+	    info->mii_reset = jiffies;
+	} else {
+	    outb(full_duplex ? 4 : 0, nic_base + DLINK_DIAG);
+	}
+    }
+}
+
+/*====================================================================*/
+
+static void mii_phy_probe(struct net_device *dev)
+{
+    pcnet_dev_t *info = PRIV(dev);
+    kio_addr_t mii_addr = dev->base_addr + DLINK_GPIO;
+    int i;
+    u_int tmp, phyid;
+
+    for (i = 31; i >= 0; i--) {
+	tmp = mdio_read(mii_addr, i, 1);
+	if ((tmp == 0) || (tmp == 0xffff))
+	    continue;
+	tmp = mdio_read(mii_addr, i, MII_PHYID_REG1);
+	phyid = tmp << 16;
+	phyid |= mdio_read(mii_addr, i, MII_PHYID_REG2);
+	phyid &= MII_PHYID_REV_MASK;
+	DEBUG(0, "%s: MII at %d is 0x%08x\n", dev->name, i, phyid);
+	if (phyid == AM79C9XX_HOME_PHY) {
+	    info->pna_phy = i;
+	} else if (phyid != AM79C9XX_ETH_PHY) {
+	    info->eth_phy = i;
+	}
+    }
+}
+
+static int pcnet_open(struct net_device *dev)
+{
+    pcnet_dev_t *info = PRIV(dev);
+    dev_link_t *link = &info->link;
+    
+    DEBUG(2, "pcnet_open('%s')\n", dev->name);
+
+    if (!DEV_OK(link))
+	return -ENODEV;
+
+    link->open++;
+
+    set_misc_reg(dev);
+    request_irq(dev->irq, ei_irq_wrapper, SA_SHIRQ, dev_info, dev);
+
+    info->phy_id = info->eth_phy;
+    info->link_status = 0x00;
+    init_timer(&info->watchdog);
+    info->watchdog.function = &ei_watchdog;
+    info->watchdog.data = (u_long)dev;
+    info->watchdog.expires = jiffies + HZ;
+    add_timer(&info->watchdog);
+
+    return ei_open(dev);
+} /* pcnet_open */
+
+/*====================================================================*/
+
+static int pcnet_close(struct net_device *dev)
+{
+    pcnet_dev_t *info = PRIV(dev);
+    dev_link_t *link = &info->link;
+
+    DEBUG(2, "pcnet_close('%s')\n", dev->name);
+
+    ei_close(dev);
+    free_irq(dev->irq, dev);
+    
+    link->open--;
+    netif_stop_queue(dev);
+    del_timer_sync(&info->watchdog);
+
+    return 0;
+} /* pcnet_close */
+
+/*======================================================================
+
+    Hard reset the card.  This used to pause for the same period that
+    a 8390 reset command required, but that shouldn't be necessary.
+
+======================================================================*/
+
+static void pcnet_reset_8390(struct net_device *dev)
+{
+    kio_addr_t nic_base = dev->base_addr;
+    int i;
+
+    ei_status.txing = ei_status.dmaing = 0;
+
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, nic_base + E8390_CMD);
+
+    outb(inb(nic_base + PCNET_RESET), nic_base + PCNET_RESET);
+
+    for (i = 0; i < 100; i++) {
+	if ((inb_p(nic_base+EN0_ISR) & ENISR_RESET) != 0)
+	    break;
+	udelay(100);
+    }
+    outb_p(ENISR_RESET, nic_base + EN0_ISR); /* Ack intr. */
+    
+    if (i == 100)
+	printk(KERN_ERR "%s: pcnet_reset_8390() did not complete.\n",
+	       dev->name);
+    set_misc_reg(dev);
+    
+} /* pcnet_reset_8390 */
+
+/*====================================================================*/
+
+static int set_config(struct net_device *dev, struct ifmap *map)
+{
+    pcnet_dev_t *info = PRIV(dev);
+    if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+	if (!(info->flags & HAS_MISC_REG))
+	    return -EOPNOTSUPP;
+	else if ((map->port < 1) || (map->port > 2))
+	    return -EINVAL;
+	dev->if_port = map->port;
+	printk(KERN_INFO "%s: switched to %s port\n",
+	       dev->name, if_names[dev->if_port]);
+	NS8390_init(dev, 1);
+    }
+    return 0;
+}
+
+/*====================================================================*/
+
+static irqreturn_t ei_irq_wrapper(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct net_device *dev = dev_id;
+    pcnet_dev_t *info = PRIV(dev);
+    irqreturn_t ret = ei_interrupt(irq, dev_id, regs);
+
+    if (ret == IRQ_HANDLED)
+	    info->stale = 0;
+    return ret;
+}
+
+static void ei_watchdog(u_long arg)
+{
+    struct net_device *dev = (struct net_device *)arg;
+    pcnet_dev_t *info = PRIV(dev);
+    kio_addr_t nic_base = dev->base_addr;
+    kio_addr_t mii_addr = nic_base + DLINK_GPIO;
+    u_short link;
+
+    if (!netif_device_present(dev)) goto reschedule;
+
+    /* Check for pending interrupt with expired latency timer: with
+       this, we can limp along even if the interrupt is blocked */
+    outb_p(E8390_NODMA+E8390_PAGE0, nic_base + E8390_CMD);
+    if (info->stale++ && (inb_p(nic_base + EN0_ISR) & ENISR_ALL)) {
+	if (!info->fast_poll)
+	    printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);
+	ei_irq_wrapper(dev->irq, dev, NULL);
+	info->fast_poll = HZ;
+    }
+    if (info->fast_poll) {
+	info->fast_poll--;
+	info->watchdog.expires = jiffies + 1;
+	add_timer(&info->watchdog);
+	return;
+    }
+
+    if (!(info->flags & HAS_MII))
+	goto reschedule;
+
+    mdio_read(mii_addr, info->phy_id, 1);
+    link = mdio_read(mii_addr, info->phy_id, 1);
+    if (!link || (link == 0xffff)) {
+	if (info->eth_phy) {
+	    info->phy_id = info->eth_phy = 0;
+	} else {
+	    printk(KERN_INFO "%s: MII is missing!\n", dev->name);
+	    info->flags &= ~HAS_MII;
+	}
+	goto reschedule;
+    }
+
+    link &= 0x0004;
+    if (link != info->link_status) {
+	u_short p = mdio_read(mii_addr, info->phy_id, 5);
+	printk(KERN_INFO "%s: %s link beat\n", dev->name,
+	       (link) ? "found" : "lost");
+	if (link && (info->flags & IS_DL10022)) {
+	    /* Disable collision detection on full duplex links */
+	    outb((p & 0x0140) ? 4 : 0, nic_base + DLINK_DIAG);
+	} else if (link && (info->flags & IS_DL10019)) {
+	    /* Disable collision detection on full duplex links */
+	    write_asic(dev->base_addr, 4, (p & 0x140) ? DL19FDUPLX : 0);
+	}
+	if (link) {
+	    if (info->phy_id == info->eth_phy) {
+		if (p)
+		    printk(KERN_INFO "%s: autonegotiation complete: "
+			   "%sbaseT-%cD selected\n", dev->name,
+			   ((p & 0x0180) ? "100" : "10"),
+			   ((p & 0x0140) ? 'F' : 'H'));
+		else
+		    printk(KERN_INFO "%s: link partner did not "
+			   "autonegotiate\n", dev->name);
+	    }
+	    NS8390_init(dev, 1);
+	}
+	info->link_status = link;
+    }
+    if (info->pna_phy && time_after(jiffies, info->mii_reset + 6*HZ)) {
+	link = mdio_read(mii_addr, info->eth_phy, 1) & 0x0004;
+	if (((info->phy_id == info->pna_phy) && link) ||
+	    ((info->phy_id != info->pna_phy) && !link)) {
+	    /* isolate this MII and try flipping to the other one */
+	    mdio_write(mii_addr, info->phy_id, 0, 0x0400);
+	    info->phy_id ^= info->pna_phy ^ info->eth_phy;
+	    printk(KERN_INFO "%s: switched to %s transceiver\n", dev->name,
+		   (info->phy_id == info->eth_phy) ? "ethernet" : "PNA");
+	    mdio_write(mii_addr, info->phy_id, 0,
+		       (info->phy_id == info->eth_phy) ? 0x1000 : 0);
+	    info->link_status = 0;
+	    info->mii_reset = jiffies;
+	}
+    }
+
+reschedule:
+    info->watchdog.expires = jiffies + HZ;
+    add_timer(&info->watchdog);
+}
+
+/*====================================================================*/
+
+static void netdev_get_drvinfo(struct net_device *dev,
+			       struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, "pcnet_cs");
+}
+
+static struct ethtool_ops netdev_ethtool_ops = {
+	.get_drvinfo		= netdev_get_drvinfo,
+};
+
+/*====================================================================*/
+
+
+static int ei_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+    pcnet_dev_t *info = PRIV(dev);
+    u16 *data = (u16 *)&rq->ifr_ifru;
+    kio_addr_t mii_addr = dev->base_addr + DLINK_GPIO;
+    switch (cmd) {
+    case SIOCGMIIPHY:
+	data[0] = info->phy_id;
+    case SIOCGMIIREG:		/* Read MII PHY register. */
+	data[3] = mdio_read(mii_addr, data[0], data[1] & 0x1f);
+	return 0;
+    case SIOCSMIIREG:		/* Write MII PHY register. */
+	if (!capable(CAP_NET_ADMIN))
+	    return -EPERM;
+	mdio_write(mii_addr, data[0], data[1] & 0x1f, data[2]);
+	return 0;
+    }
+    return -EOPNOTSUPP;
+}
+
+/*====================================================================*/
+
+static void dma_get_8390_hdr(struct net_device *dev,
+			     struct e8390_pkt_hdr *hdr,
+			     int ring_page)
+{
+    kio_addr_t nic_base = dev->base_addr;
+
+    if (ei_status.dmaing) {
+	printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input."
+	       "[DMAstat:%1x][irqlock:%1x]\n",
+	       dev->name, ei_status.dmaing, ei_status.irqlock);
+	return;
+    }
+    
+    ei_status.dmaing |= 0x01;
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
+    outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+    outb_p(0, nic_base + EN0_RCNTHI);
+    outb_p(0, nic_base + EN0_RSARLO);		/* On page boundary */
+    outb_p(ring_page, nic_base + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD);
+
+    insw(nic_base + PCNET_DATAPORT, hdr,
+	    sizeof(struct e8390_pkt_hdr)>>1);
+    /* Fix for big endian systems */
+    hdr->count = le16_to_cpu(hdr->count);
+
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+    ei_status.dmaing &= ~0x01;
+}
+
+/*====================================================================*/
+
+static void dma_block_input(struct net_device *dev, int count,
+			    struct sk_buff *skb, int ring_offset)
+{
+    kio_addr_t nic_base = dev->base_addr;
+    int xfer_count = count;
+    char *buf = skb->data;
+
+#ifdef PCMCIA_DEBUG
+    if ((ei_debug > 4) && (count != 4))
+	printk(KERN_DEBUG "%s: [bi=%d]\n", dev->name, count+4);
+#endif
+    if (ei_status.dmaing) {
+	printk(KERN_NOTICE "%s: DMAing conflict in dma_block_input."
+	       "[DMAstat:%1x][irqlock:%1x]\n",
+	       dev->name, ei_status.dmaing, ei_status.irqlock);
+	return;
+    }
+    ei_status.dmaing |= 0x01;
+    outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base + PCNET_CMD);
+    outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+    outb_p(count >> 8, nic_base + EN0_RCNTHI);
+    outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+    outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+    outb_p(E8390_RREAD+E8390_START, nic_base + PCNET_CMD);
+
+    insw(nic_base + PCNET_DATAPORT,buf,count>>1);
+    if (count & 0x01)
+	buf[count-1] = inb(nic_base + PCNET_DATAPORT), xfer_count++;
+
+    /* This was for the ALPHA version only, but enough people have
+       encountering problems that it is still here. */
+#ifdef PCMCIA_DEBUG
+    if (ei_debug > 4) {		/* DMA termination address check... */
+	int addr, tries = 20;
+	do {
+	    /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+	       -- it's broken for Rx on some cards! */
+	    int high = inb_p(nic_base + EN0_RSARHI);
+	    int low = inb_p(nic_base + EN0_RSARLO);
+	    addr = (high << 8) + low;
+	    if (((ring_offset + xfer_count) & 0xff) == (addr & 0xff))
+		break;
+	} while (--tries > 0);
+	if (tries <= 0)
+	    printk(KERN_NOTICE "%s: RX transfer address mismatch,"
+		   "%#4.4x (expected) vs. %#4.4x (actual).\n",
+		   dev->name, ring_offset + xfer_count, addr);
+    }
+#endif
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+    ei_status.dmaing &= ~0x01;
+} /* dma_block_input */
+
+/*====================================================================*/
+
+static void dma_block_output(struct net_device *dev, int count,
+			     const u_char *buf, const int start_page)
+{
+    kio_addr_t nic_base = dev->base_addr;
+    pcnet_dev_t *info = PRIV(dev);
+#ifdef PCMCIA_DEBUG
+    int retries = 0;
+#endif
+    u_long dma_start;
+
+#ifdef PCMCIA_DEBUG
+    if (ei_debug > 4)
+	printk(KERN_DEBUG "%s: [bo=%d]\n", dev->name, count);
+#endif
+
+    /* Round the count up for word writes.  Do we need to do this?
+       What effect will an odd byte count have on the 8390?
+       I should check someday. */
+    if (count & 0x01)
+	count++;
+    if (ei_status.dmaing) {
+	printk(KERN_NOTICE "%s: DMAing conflict in dma_block_output."
+	       "[DMAstat:%1x][irqlock:%1x]\n",
+	       dev->name, ei_status.dmaing, ei_status.irqlock);
+	return;
+    }
+    ei_status.dmaing |= 0x01;
+    /* We should already be in page 0, but to be safe... */
+    outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base+PCNET_CMD);
+
+#ifdef PCMCIA_DEBUG
+  retry:
+#endif
+
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);
+
+    /* Now the normal output. */
+    outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+    outb_p(count >> 8,   nic_base + EN0_RCNTHI);
+    outb_p(0x00, nic_base + EN0_RSARLO);
+    outb_p(start_page, nic_base + EN0_RSARHI);
+
+    outb_p(E8390_RWRITE+E8390_START, nic_base + PCNET_CMD);
+    outsw(nic_base + PCNET_DATAPORT, buf, count>>1);
+
+    dma_start = jiffies;
+
+#ifdef PCMCIA_DEBUG
+    /* This was for the ALPHA version only, but enough people have
+       encountering problems that it is still here. */
+    if (ei_debug > 4) {	/* DMA termination address check... */
+	int addr, tries = 20;
+	do {
+	    int high = inb_p(nic_base + EN0_RSARHI);
+	    int low = inb_p(nic_base + EN0_RSARLO);
+	    addr = (high << 8) + low;
+	    if ((start_page << 8) + count == addr)
+		break;
+	} while (--tries > 0);
+	if (tries <= 0) {
+	    printk(KERN_NOTICE "%s: Tx packet transfer address mismatch,"
+		   "%#4.4x (expected) vs. %#4.4x (actual).\n",
+		   dev->name, (start_page << 8) + count, addr);
+	    if (retries++ == 0)
+		goto retry;
+	}
+    }
+#endif
+
+    while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+	if (time_after(jiffies, dma_start + PCNET_RDC_TIMEOUT)) {
+	    printk(KERN_NOTICE "%s: timeout waiting for Tx RDC.\n",
+		   dev->name);
+	    pcnet_reset_8390(dev);
+	    NS8390_init(dev, 1);
+	    break;
+	}
+
+    outb_p(ENISR_RDC, nic_base + EN0_ISR);	/* Ack intr. */
+    if (info->flags & DELAY_OUTPUT)
+	udelay((long)delay_time);
+    ei_status.dmaing &= ~0x01;
+}
+
+/*====================================================================*/
+
+static int setup_dma_config(dev_link_t *link, int start_pg,
+			    int stop_pg)
+{
+    struct net_device *dev = link->priv;
+
+    ei_status.tx_start_page = start_pg;
+    ei_status.rx_start_page = start_pg + TX_PAGES;
+    ei_status.stop_page = stop_pg;
+
+    /* set up block i/o functions */
+    ei_status.get_8390_hdr = &dma_get_8390_hdr;
+    ei_status.block_input = &dma_block_input;
+    ei_status.block_output = &dma_block_output;
+
+    return 0;
+}
+
+/*====================================================================*/
+
+static void copyin(void *dest, void __iomem *src, int c)
+{
+    u_short *d = dest;
+    u_short __iomem *s = src;
+    int odd;
+
+    if (c <= 0)
+	return;
+    odd = (c & 1); c >>= 1;
+
+    if (c) {
+	do { *d++ = __raw_readw(s++); } while (--c);
+    }
+    /* get last byte by fetching a word and masking */
+    if (odd)
+	*((u_char *)d) = readw(s) & 0xff;
+}
+
+static void copyout(void __iomem *dest, const void *src, int c)
+{
+    u_short __iomem *d = dest;
+    const u_short *s = src;
+    int odd;
+
+    if (c <= 0)
+	return;
+    odd = (c & 1); c >>= 1;
+
+    if (c) {
+	do { __raw_writew(*s++, d++); } while (--c);
+    }
+    /* copy last byte doing a read-modify-write */
+    if (odd)
+	writew((readw(d) & 0xff00) | *(u_char *)s, d);
+}
+
+/*====================================================================*/
+
+static void shmem_get_8390_hdr(struct net_device *dev,
+			       struct e8390_pkt_hdr *hdr,
+			       int ring_page)
+{
+    void __iomem *xfer_start = ei_status.mem + (TX_PAGES<<8)
+				+ (ring_page << 8)
+				- (ei_status.rx_start_page << 8);
+    
+    copyin(hdr, xfer_start, sizeof(struct e8390_pkt_hdr));
+    /* Fix for big endian systems */
+    hdr->count = le16_to_cpu(hdr->count);
+}
+
+/*====================================================================*/
+
+static void shmem_block_input(struct net_device *dev, int count,
+			      struct sk_buff *skb, int ring_offset)
+{
+    void __iomem *xfer_start = ei_status.mem + (TX_PAGES<<8)
+				+ ring_offset
+				- (ei_status.rx_start_page << 8);
+    char *buf = skb->data;
+    
+    if (xfer_start + count > (void __iomem *)ei_status.rmem_end) {
+	/* We must wrap the input move. */
+	int semi_count = (void __iomem *)ei_status.rmem_end - xfer_start;
+	copyin(buf, xfer_start, semi_count);
+	buf += semi_count;
+	xfer_start = ei_status.mem + (TX_PAGES<<8);
+	count -= semi_count;
+    }
+    copyin(buf, xfer_start, count);
+}
+
+/*====================================================================*/
+
+static void shmem_block_output(struct net_device *dev, int count,
+			       const u_char *buf, const int start_page)
+{
+    void __iomem *shmem = ei_status.mem + (start_page << 8);
+    shmem -= ei_status.tx_start_page << 8;
+    copyout(shmem, buf, count);
+}
+
+/*====================================================================*/
+
+static int setup_shmem_window(dev_link_t *link, int start_pg,
+			      int stop_pg, int cm_offset)
+{
+    struct net_device *dev = link->priv;
+    pcnet_dev_t *info = PRIV(dev);
+    win_req_t req;
+    memreq_t mem;
+    int i, window_size, offset, last_ret, last_fn;
+
+    window_size = (stop_pg - start_pg) << 8;
+    if (window_size > 32 * 1024)
+	window_size = 32 * 1024;
+
+    /* Make sure it's a power of two.  */
+    while ((window_size & (window_size - 1)) != 0)
+	window_size += window_size & ~(window_size - 1);
+
+    /* Allocate a memory window */
+    req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE;
+    req.Attributes |= WIN_USE_WAIT;
+    req.Base = 0; req.Size = window_size;
+    req.AccessSpeed = mem_speed;
+    CS_CHECK(RequestWindow, pcmcia_request_window(&link->handle, &req, &link->win));
+
+    mem.CardOffset = (start_pg << 8) + cm_offset;
+    offset = mem.CardOffset % window_size;
+    mem.CardOffset -= offset;
+    mem.Page = 0;
+    CS_CHECK(MapMemPage, pcmcia_map_mem_page(link->win, &mem));
+
+    /* Try scribbling on the buffer */
+    info->base = ioremap(req.Base, window_size);
+    for (i = 0; i < (TX_PAGES<<8); i += 2)
+	__raw_writew((i>>1), info->base+offset+i);
+    udelay(100);
+    for (i = 0; i < (TX_PAGES<<8); i += 2)
+	if (__raw_readw(info->base+offset+i) != (i>>1)) break;
+    pcnet_reset_8390(dev);
+    if (i != (TX_PAGES<<8)) {
+	iounmap(info->base);
+	pcmcia_release_window(link->win);
+	info->base = NULL; link->win = NULL;
+	goto failed;
+    }
+    
+    ei_status.mem = info->base + offset;
+    dev->mem_start = (u_long)ei_status.mem;
+    dev->mem_end = ei_status.rmem_end = (u_long)info->base + req.Size;
+
+    ei_status.tx_start_page = start_pg;
+    ei_status.rx_start_page = start_pg + TX_PAGES;
+    ei_status.stop_page = start_pg + ((req.Size - offset) >> 8);
+
+    /* set up block i/o functions */
+    ei_status.get_8390_hdr = &shmem_get_8390_hdr;
+    ei_status.block_input = &shmem_block_input;
+    ei_status.block_output = &shmem_block_output;
+
+    info->flags |= USE_SHMEM;
+    return 0;
+
+cs_failed:
+    cs_error(link->handle, last_fn, last_ret);
+failed:
+    return 1;
+}
+
+/*====================================================================*/
+
+static struct pcmcia_driver pcnet_driver = {
+	.drv		= {
+		.name	= "pcnet_cs",
+	},
+	.attach		= pcnet_attach,
+	.detach		= pcnet_detach,
+	.owner		= THIS_MODULE,
+};
+
+static int __init init_pcnet_cs(void)
+{
+    return pcmcia_register_driver(&pcnet_driver);
+}
+
+static void __exit exit_pcnet_cs(void)
+{
+    DEBUG(0, "pcnet_cs: unloading\n");
+    pcmcia_unregister_driver(&pcnet_driver);
+    BUG_ON(dev_list != NULL);
+}
+
+module_init(init_pcnet_cs);
+module_exit(exit_pcnet_cs);
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c
new file mode 100644
index 000000000000..85a152173148
--- /dev/null
+++ b/drivers/net/pcmcia/smc91c92_cs.c
@@ -0,0 +1,2260 @@
+/*======================================================================
+
+    A PCMCIA ethernet driver for SMC91c92-based cards.
+
+    This driver supports Megahertz PCMCIA ethernet cards; and
+    Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem
+    multifunction cards.
+
+    Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
+
+    smc91c92_cs.c 1.122 2002/10/25 06:26:39
+
+    This driver contains code written by Donald Becker
+    (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au),
+    David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman
+    (erik@vt.edu).  Donald wrote the SMC 91c92 code using parts of
+    Erik's SMC 91c94 driver.  Rowan wrote a similar driver, and I've
+    incorporated some parts of his driver here.  I (Dave) wrote most
+    of the PCMCIA glue code, and the Ositech support code.  Kelly
+    Stephens (kstephen@holli.com) added support for the Motorola
+    Mariner, with help from Allen Brost.
+
+    This software may be used and distributed according to the terms of
+    the GNU General Public License, incorporated herein by reference.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/crc32.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+/* Ositech Seven of Diamonds firmware */
+#include "ositech.h"
+
+/*====================================================================*/
+
+static char *if_names[] = { "auto", "10baseT", "10base2"};
+
+/* Module parameters */
+
+MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver");
+MODULE_LICENSE("GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+/*
+  Transceiver/media type.
+   0 = auto
+   1 = 10baseT (and autoselect if #define AUTOSELECT),
+   2 = AUI/10base2,
+*/
+INT_MODULE_PARM(if_port, 0);
+
+#ifdef PCMCIA_DEBUG
+INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG);
+static const char *version =
+"smc91c92_cs.c 0.09 1996/8/4 Donald Becker, becker@scyld.com.\n";
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
+#else
+#define DEBUG(n, args...)
+#endif
+
+#define DRV_NAME	"smc91c92_cs"
+#define DRV_VERSION	"1.122"
+
+/*====================================================================*/
+
+/* Operational parameter that usually are not changed. */
+
+/* Time in jiffies before concluding Tx hung */
+#define TX_TIMEOUT		((400*HZ)/1000)
+
+/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
+#define INTR_WORK		4
+
+/* Times to check the check the chip before concluding that it doesn't
+   currently have room for another Tx packet. */
+#define MEMORY_WAIT_TIME       	8
+
+static dev_info_t dev_info = "smc91c92_cs";
+
+static dev_link_t *dev_list;
+
+struct smc_private {
+    dev_link_t			link;
+    spinlock_t			lock;
+    u_short			manfid;
+    u_short			cardid;
+    struct net_device_stats	stats;
+    dev_node_t			node;
+    struct sk_buff		*saved_skb;
+    int				packets_waiting;
+    void			__iomem *base;
+    u_short			cfg;
+    struct timer_list		media;
+    int				watchdog, tx_err;
+    u_short			media_status;
+    u_short			fast_poll;
+    u_short			link_status;
+    struct mii_if_info		mii_if;
+    int				duplex;
+    int				rx_ovrn;
+};
+
+/* Special definitions for Megahertz multifunction cards */
+#define MEGAHERTZ_ISR		0x0380
+
+/* Special function registers for Motorola Mariner */
+#define MOT_LAN			0x0000
+#define MOT_UART		0x0020
+#define MOT_EEPROM		0x20
+
+#define MOT_NORMAL \
+(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA)
+
+/* Special function registers for Ositech cards */
+#define OSITECH_AUI_CTL		0x0c
+#define OSITECH_PWRDOWN		0x0d
+#define OSITECH_RESET		0x0e
+#define OSITECH_ISR		0x0f
+#define OSITECH_AUI_PWR		0x0c
+#define OSITECH_RESET_ISR	0x0e
+
+#define OSI_AUI_PWR		0x40
+#define OSI_LAN_PWRDOWN		0x02
+#define OSI_MODEM_PWRDOWN	0x01
+#define OSI_LAN_RESET		0x02
+#define OSI_MODEM_RESET		0x01
+
+/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */
+#define	BANK_SELECT		14		/* Window select register. */
+#define SMC_SELECT_BANK(x)  { outw(x, ioaddr + BANK_SELECT); }
+
+/* Bank 0 registers. */
+#define	TCR 		0	/* transmit control register */
+#define	 TCR_CLEAR	0	/* do NOTHING */
+#define  TCR_ENABLE	0x0001	/* if this is 1, we can transmit */
+#define	 TCR_PAD_EN	0x0080	/* pads short packets to 64 bytes */
+#define  TCR_MONCSN	0x0400  /* Monitor Carrier. */
+#define  TCR_FDUPLX	0x0800  /* Full duplex mode. */
+#define	 TCR_NORMAL TCR_ENABLE | TCR_PAD_EN
+
+#define EPH		2	/* Ethernet Protocol Handler report. */
+#define  EPH_TX_SUC	0x0001
+#define  EPH_SNGLCOL	0x0002
+#define  EPH_MULCOL	0x0004
+#define  EPH_LTX_MULT	0x0008
+#define  EPH_16COL	0x0010
+#define  EPH_SQET	0x0020
+#define  EPH_LTX_BRD	0x0040
+#define  EPH_TX_DEFR	0x0080
+#define  EPH_LAT_COL	0x0200
+#define  EPH_LOST_CAR	0x0400
+#define  EPH_EXC_DEF	0x0800
+#define  EPH_CTR_ROL	0x1000
+#define  EPH_RX_OVRN	0x2000
+#define  EPH_LINK_OK	0x4000
+#define  EPH_TX_UNRN	0x8000
+#define MEMINFO		8	/* Memory Information Register */
+#define MEMCFG		10	/* Memory Configuration Register */
+
+/* Bank 1 registers. */
+#define CONFIG			0
+#define  CFG_MII_SELECT		0x8000	/* 91C100 only */
+#define  CFG_NO_WAIT		0x1000
+#define  CFG_FULL_STEP		0x0400
+#define  CFG_SET_SQLCH		0x0200
+#define  CFG_AUI_SELECT	 	0x0100
+#define  CFG_16BIT		0x0080
+#define  CFG_DIS_LINK		0x0040
+#define  CFG_STATIC		0x0030
+#define  CFG_IRQ_SEL_1		0x0004
+#define  CFG_IRQ_SEL_0		0x0002
+#define BASE_ADDR		2
+#define	ADDR0			4
+#define	GENERAL			10
+#define	CONTROL			12
+#define  CTL_STORE		0x0001
+#define  CTL_RELOAD		0x0002
+#define  CTL_EE_SELECT		0x0004
+#define  CTL_TE_ENABLE		0x0020
+#define  CTL_CR_ENABLE		0x0040
+#define  CTL_LE_ENABLE		0x0080
+#define  CTL_AUTO_RELEASE	0x0800
+#define	 CTL_POWERDOWN		0x2000
+
+/* Bank 2 registers. */
+#define MMU_CMD		0
+#define	 MC_ALLOC	0x20  	/* or with number of 256 byte packets */
+#define	 MC_RESET	0x40
+#define  MC_RELEASE  	0x80  	/* remove and release the current rx packet */
+#define  MC_FREEPKT  	0xA0  	/* Release packet in PNR register */
+#define  MC_ENQUEUE	0xC0 	/* Enqueue the packet for transmit */
+#define	PNR_ARR		2
+#define FIFO_PORTS	4
+#define  FP_RXEMPTY	0x8000
+#define	POINTER		6
+#define  PTR_AUTO_INC	0x0040
+#define  PTR_READ	0x2000
+#define	 PTR_AUTOINC 	0x4000
+#define	 PTR_RCV	0x8000
+#define	DATA_1		8
+#define	INTERRUPT	12
+#define  IM_RCV_INT		0x1
+#define	 IM_TX_INT		0x2
+#define	 IM_TX_EMPTY_INT	0x4
+#define	 IM_ALLOC_INT		0x8
+#define	 IM_RX_OVRN_INT		0x10
+#define	 IM_EPH_INT		0x20
+
+#define	RCR		4
+enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002,
+	     RxEnable = 0x0100, RxStripCRC = 0x0200};
+#define  RCR_SOFTRESET	0x8000 	/* resets the chip */
+#define	 RCR_STRIP_CRC	0x200	/* strips CRC */
+#define  RCR_ENABLE	0x100	/* IFF this is set, we can receive packets */
+#define  RCR_ALMUL	0x4 	/* receive all multicast packets */
+#define	 RCR_PROMISC	0x2	/* enable promiscuous mode */
+
+/* the normal settings for the RCR register : */
+#define	 RCR_NORMAL	(RCR_STRIP_CRC | RCR_ENABLE)
+#define  RCR_CLEAR	0x0		/* set it to a base state */
+#define	COUNTER		6
+
+/* BANK 3 -- not the same values as in smc9194! */
+#define	MULTICAST0	0
+#define	MULTICAST2	2
+#define	MULTICAST4	4
+#define	MULTICAST6	6
+#define MGMT    	8
+#define REVISION	0x0a
+
+/* Transmit status bits. */
+#define TS_SUCCESS 0x0001
+#define TS_16COL   0x0010
+#define TS_LATCOL  0x0200
+#define TS_LOSTCAR 0x0400
+
+/* Receive status bits. */
+#define RS_ALGNERR	0x8000
+#define RS_BADCRC	0x2000
+#define RS_ODDFRAME	0x1000
+#define RS_TOOLONG	0x0800
+#define RS_TOOSHORT	0x0400
+#define RS_MULTICAST	0x0001
+#define RS_ERRORS	(RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
+
+#define set_bits(v, p) outw(inw(p)|(v), (p))
+#define mask_bits(v, p) outw(inw(p)&(v), (p))
+
+/*====================================================================*/
+
+static dev_link_t *smc91c92_attach(void);
+static void smc91c92_detach(dev_link_t *);
+static void smc91c92_config(dev_link_t *link);
+static void smc91c92_release(dev_link_t *link);
+static int smc91c92_event(event_t event, int priority,
+			  event_callback_args_t *args);
+
+static int smc_open(struct net_device *dev);
+static int smc_close(struct net_device *dev);
+static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static void smc_tx_timeout(struct net_device *dev);
+static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void smc_rx(struct net_device *dev);
+static struct net_device_stats *smc_get_stats(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static int s9k_config(struct net_device *dev, struct ifmap *map);
+static void smc_set_xcvr(struct net_device *dev, int if_port);
+static void smc_reset(struct net_device *dev);
+static void media_check(u_long arg);
+static void mdio_sync(kio_addr_t addr);
+static int mdio_read(struct net_device *dev, int phy_id, int loc);
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int value);
+static int smc_link_ok(struct net_device *dev);
+static struct ethtool_ops ethtool_ops;
+
+/*======================================================================
+
+  smc91c92_attach() creates an "instance" of the driver, allocating
+  local data structures for one device.  The device is registered
+  with Card Services.
+
+======================================================================*/
+
+static dev_link_t *smc91c92_attach(void)
+{
+    client_reg_t client_reg;
+    struct smc_private *smc;
+    dev_link_t *link;
+    struct net_device *dev;
+    int ret;
+
+    DEBUG(0, "smc91c92_attach()\n");
+
+    /* Create new ethernet device */
+    dev = alloc_etherdev(sizeof(struct smc_private));
+    if (!dev)
+	return NULL;
+    smc = netdev_priv(dev);
+    link = &smc->link;
+    link->priv = dev;
+
+    spin_lock_init(&smc->lock);
+    link->io.NumPorts1 = 16;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+    link->io.IOAddrLines = 4;
+    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    link->irq.Handler = &smc_interrupt;
+    link->irq.Instance = dev;
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+
+    /* The SMC91c92-specific entries in the device structure. */
+    SET_MODULE_OWNER(dev);
+    dev->hard_start_xmit = &smc_start_xmit;
+    dev->get_stats = &smc_get_stats;
+    dev->set_config = &s9k_config;
+    dev->set_multicast_list = &set_rx_mode;
+    dev->open = &smc_open;
+    dev->stop = &smc_close;
+    dev->do_ioctl = &smc_ioctl;
+    SET_ETHTOOL_OPS(dev, &ethtool_ops);
+#ifdef HAVE_TX_TIMEOUT
+    dev->tx_timeout = smc_tx_timeout;
+    dev->watchdog_timeo = TX_TIMEOUT;
+#endif
+
+    smc->mii_if.dev = dev;
+    smc->mii_if.mdio_read = mdio_read;
+    smc->mii_if.mdio_write = mdio_write;
+    smc->mii_if.phy_id_mask = 0x1f;
+    smc->mii_if.reg_num_mask = 0x1f;
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &smc91c92_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    ret = pcmcia_register_client(&link->handle, &client_reg);
+    if (ret != 0) {
+	cs_error(link->handle, RegisterClient, ret);
+	smc91c92_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* smc91c92_attach */
+
+/*======================================================================
+
+    This deletes a driver "instance".  The device is de-registered
+    with Card Services.  If it has been released, all local data
+    structures are freed.  Otherwise, the structures will be freed
+    when the device is released.
+
+======================================================================*/
+
+static void smc91c92_detach(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    dev_link_t **linkp;
+
+    DEBUG(0, "smc91c92_detach(0x%p)\n", link);
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link) break;
+    if (*linkp == NULL)
+	return;
+
+    if (link->dev)
+	unregister_netdev(dev);
+
+    if (link->state & DEV_CONFIG)
+	smc91c92_release(link);
+
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+
+    /* Unlink device structure, free bits */
+    *linkp = link->next;
+    free_netdev(dev);
+} /* smc91c92_detach */
+
+/*====================================================================*/
+
+static int cvt_ascii_address(struct net_device *dev, char *s)
+{
+    int i, j, da, c;
+
+    if (strlen(s) != 12)
+	return -1;
+    for (i = 0; i < 6; i++) {
+	da = 0;
+	for (j = 0; j < 2; j++) {
+	    c = *s++;
+	    da <<= 4;
+	    da += ((c >= '0') && (c <= '9')) ?
+		(c - '0') : ((c & 0x0f) + 9);
+	}
+	dev->dev_addr[i] = da;
+    }
+    return 0;
+}
+
+/*====================================================================*/
+
+static int first_tuple(client_handle_t handle, tuple_t *tuple,
+		cisparse_t *parse)
+{
+	int i;
+
+	if ((i = pcmcia_get_first_tuple(handle, tuple)) != CS_SUCCESS ||
+			(i = pcmcia_get_tuple_data(handle, tuple)) != CS_SUCCESS)
+		return i;
+	return pcmcia_parse_tuple(handle, tuple, parse);
+}
+
+static int next_tuple(client_handle_t handle, tuple_t *tuple,
+		cisparse_t *parse)
+{
+	int i;
+
+	if ((i = pcmcia_get_next_tuple(handle, tuple)) != CS_SUCCESS ||
+			(i = pcmcia_get_tuple_data(handle, tuple)) != CS_SUCCESS)
+		return i;
+	return pcmcia_parse_tuple(handle, tuple, parse);
+}
+
+/*======================================================================
+
+    Configuration stuff for Megahertz cards
+
+    mhz_3288_power() is used to power up a 3288's ethernet chip.
+    mhz_mfc_config() handles socket setup for multifunction (1144
+    and 3288) cards.  mhz_setup() gets a card's hardware ethernet
+    address.
+
+======================================================================*/
+
+static int mhz_3288_power(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    struct smc_private *smc = netdev_priv(dev);
+    u_char tmp;
+
+    /* Read the ISR twice... */
+    readb(smc->base+MEGAHERTZ_ISR);
+    udelay(5);
+    readb(smc->base+MEGAHERTZ_ISR);
+
+    /* Pause 200ms... */
+    mdelay(200);
+
+    /* Now read and write the COR... */
+    tmp = readb(smc->base + link->conf.ConfigBase + CISREG_COR);
+    udelay(5);
+    writeb(tmp, smc->base + link->conf.ConfigBase + CISREG_COR);
+
+    return 0;
+}
+
+static int mhz_mfc_config(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    struct smc_private *smc = netdev_priv(dev);
+    tuple_t tuple;
+    cisparse_t parse;
+    u_char buf[255];
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    win_req_t req;
+    memreq_t mem;
+    int i, k;
+
+    link->conf.Attributes |= CONF_ENABLE_SPKR;
+    link->conf.Status = CCSR_AUDIO_ENA;
+    link->irq.Attributes =
+	IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT;
+    link->io.IOAddrLines = 16;
+    link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+    link->io.NumPorts2 = 8;
+
+    tuple.Attributes = tuple.TupleOffset = 0;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+
+    i = first_tuple(link->handle, &tuple, &parse);
+    /* The Megahertz combo cards have modem-like CIS entries, so
+       we have to explicitly try a bunch of port combinations. */
+    while (i == CS_SUCCESS) {
+	link->conf.ConfigIndex = cf->index;
+	link->io.BasePort2 = cf->io.win[0].base;
+	for (k = 0; k < 0x400; k += 0x10) {
+	    if (k & 0x80) continue;
+	    link->io.BasePort1 = k ^ 0x300;
+	    i = pcmcia_request_io(link->handle, &link->io);
+	    if (i == CS_SUCCESS) break;
+	}
+	if (i == CS_SUCCESS) break;
+	i = next_tuple(link->handle, &tuple, &parse);
+    }
+    if (i != CS_SUCCESS)
+	return i;
+    dev->base_addr = link->io.BasePort1;
+
+    /* Allocate a memory window, for accessing the ISR */
+    req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+    req.Base = req.Size = 0;
+    req.AccessSpeed = 0;
+    i = pcmcia_request_window(&link->handle, &req, &link->win);
+    if (i != CS_SUCCESS)
+	return i;
+    smc->base = ioremap(req.Base, req.Size);
+    mem.CardOffset = mem.Page = 0;
+    if (smc->manfid == MANFID_MOTOROLA)
+	mem.CardOffset = link->conf.ConfigBase;
+    i = pcmcia_map_mem_page(link->win, &mem);
+
+    if ((i == CS_SUCCESS)
+	&& (smc->manfid == MANFID_MEGAHERTZ)
+	&& (smc->cardid == PRODID_MEGAHERTZ_EM3288))
+	mhz_3288_power(link);
+
+    return i;
+}
+
+static int mhz_setup(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    u_char buf[255], *station_addr;
+
+    tuple.Attributes = tuple.TupleOffset = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = sizeof(buf);
+
+    /* Read the station address from the CIS.  It is stored as the last
+       (fourth) string in the Version 1 Version/ID tuple. */
+    tuple.DesiredTuple = CISTPL_VERS_1;
+    if (first_tuple(handle, &tuple, &parse) != CS_SUCCESS)
+	return -1;
+    /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
+    if (next_tuple(handle, &tuple, &parse) != CS_SUCCESS)
+	first_tuple(handle, &tuple, &parse);
+    if (parse.version_1.ns > 3) {
+	station_addr = parse.version_1.str + parse.version_1.ofs[3];
+	if (cvt_ascii_address(dev, station_addr) == 0)
+	    return 0;
+    }
+
+    /* Another possibility: for the EM3288, in a special tuple */
+    tuple.DesiredTuple = 0x81;
+    if (pcmcia_get_first_tuple(handle, &tuple) != CS_SUCCESS)
+	return -1;
+    if (pcmcia_get_tuple_data(handle, &tuple) != CS_SUCCESS)
+	return -1;
+    buf[12] = '\0';
+    if (cvt_ascii_address(dev, buf) == 0)
+	return 0;
+
+    return -1;
+}
+
+/*======================================================================
+
+    Configuration stuff for the Motorola Mariner
+
+    mot_config() writes directly to the Mariner configuration
+    registers because the CIS is just bogus.
+
+======================================================================*/
+
+static void mot_config(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    struct smc_private *smc = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    kio_addr_t iouart = link->io.BasePort2;
+
+    /* Set UART base address and force map with COR bit 1 */
+    writeb(iouart & 0xff,        smc->base + MOT_UART + CISREG_IOBASE_0);
+    writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1);
+    writeb(MOT_NORMAL,           smc->base + MOT_UART + CISREG_COR);
+
+    /* Set SMC base address and force map with COR bit 1 */
+    writeb(ioaddr & 0xff,        smc->base + MOT_LAN + CISREG_IOBASE_0);
+    writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1);
+    writeb(MOT_NORMAL,           smc->base + MOT_LAN + CISREG_COR);
+
+    /* Wait for things to settle down */
+    mdelay(100);
+}
+
+static int mot_setup(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    kio_addr_t ioaddr = dev->base_addr;
+    int i, wait, loop;
+    u_int addr;
+
+    /* Read Ethernet address from Serial EEPROM */
+
+    for (i = 0; i < 3; i++) {
+	SMC_SELECT_BANK(2);
+	outw(MOT_EEPROM + i, ioaddr + POINTER);
+	SMC_SELECT_BANK(1);
+	outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL);
+
+	for (loop = wait = 0; loop < 200; loop++) {
+	    udelay(10);
+	    wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL));
+	    if (wait == 0) break;
+	}
+	
+	if (wait)
+	    return -1;
+	
+	addr = inw(ioaddr + GENERAL);
+	dev->dev_addr[2*i]   = addr & 0xff;
+	dev->dev_addr[2*i+1] = (addr >> 8) & 0xff;
+    }
+
+    return 0;
+}
+
+/*====================================================================*/
+
+static int smc_config(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    u_char buf[255];
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+    int i;
+
+    tuple.Attributes = tuple.TupleOffset = 0;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+
+    link->io.NumPorts1 = 16;
+    i = first_tuple(link->handle, &tuple, &parse);
+    while (i != CS_NO_MORE_ITEMS) {
+	if (i == CS_SUCCESS) {
+	    link->conf.ConfigIndex = cf->index;
+	    link->io.BasePort1 = cf->io.win[0].base;
+	    link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK;
+	    i = pcmcia_request_io(link->handle, &link->io);
+	    if (i == CS_SUCCESS) break;
+	}
+	i = next_tuple(link->handle, &tuple, &parse);
+    }
+    if (i == CS_SUCCESS)
+	dev->base_addr = link->io.BasePort1;
+    return i;
+}
+
+static int smc_setup(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    tuple_t tuple;
+    cisparse_t parse;
+    cistpl_lan_node_id_t *node_id;
+    u_char buf[255], *station_addr;
+    int i;
+
+    tuple.Attributes = tuple.TupleOffset = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = sizeof(buf);
+
+    /* Check for a LAN function extension tuple */
+    tuple.DesiredTuple = CISTPL_FUNCE;
+    i = first_tuple(handle, &tuple, &parse);
+    while (i == CS_SUCCESS) {
+	if (parse.funce.type == CISTPL_FUNCE_LAN_NODE_ID)
+	    break;
+	i = next_tuple(handle, &tuple, &parse);
+    }
+    if (i == CS_SUCCESS) {
+	node_id = (cistpl_lan_node_id_t *)parse.funce.data;
+	if (node_id->nb == 6) {
+	    for (i = 0; i < 6; i++)
+		dev->dev_addr[i] = node_id->id[i];
+	    return 0;
+	}
+    }
+    /* Try the third string in the Version 1 Version/ID tuple. */
+    tuple.DesiredTuple = CISTPL_VERS_1;
+    if (first_tuple(handle, &tuple, &parse) != CS_SUCCESS)
+	return -1;
+    station_addr = parse.version_1.str + parse.version_1.ofs[2];
+    if (cvt_ascii_address(dev, station_addr) == 0)
+	return 0;
+
+    return -1;
+}
+
+/*====================================================================*/
+
+static int osi_config(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    static kio_addr_t com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
+    int i, j;
+
+    link->conf.Attributes |= CONF_ENABLE_SPKR;
+    link->conf.Status = CCSR_AUDIO_ENA;
+    link->irq.Attributes =
+	IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT;
+    link->io.NumPorts1 = 64;
+    link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+    link->io.NumPorts2 = 8;
+    link->io.IOAddrLines = 16;
+
+    /* Enable Hard Decode, LAN, Modem */
+    link->conf.ConfigIndex = 0x23;
+
+    for (i = j = 0; j < 4; j++) {
+	link->io.BasePort2 = com[j];
+	i = pcmcia_request_io(link->handle, &link->io);
+	if (i == CS_SUCCESS) break;
+    }
+    if (i != CS_SUCCESS) {
+	/* Fallback: turn off hard decode */
+	link->conf.ConfigIndex = 0x03;
+	link->io.NumPorts2 = 0;
+	i = pcmcia_request_io(link->handle, &link->io);
+    }
+    dev->base_addr = link->io.BasePort1 + 0x10;
+    return i;
+}
+
+static int osi_setup(dev_link_t *link, u_short manfid, u_short cardid)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    tuple_t tuple;
+    u_char buf[255];
+    int i;
+
+    tuple.Attributes = TUPLE_RETURN_COMMON;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = sizeof(buf);
+    tuple.TupleOffset = 0;
+
+    /* Read the station address from tuple 0x90, subtuple 0x04 */
+    tuple.DesiredTuple = 0x90;
+    i = pcmcia_get_first_tuple(handle, &tuple);
+    while (i == CS_SUCCESS) {
+	i = pcmcia_get_tuple_data(handle, &tuple);
+	if ((i != CS_SUCCESS) || (buf[0] == 0x04))
+	    break;
+	i = pcmcia_get_next_tuple(handle, &tuple);
+    }
+    if (i != CS_SUCCESS)
+	return -1;
+    for (i = 0; i < 6; i++)
+	dev->dev_addr[i] = buf[i+2];
+
+    if (((manfid == MANFID_OSITECH) &&
+	 (cardid == PRODID_OSITECH_SEVEN)) ||
+	((manfid == MANFID_PSION) &&
+	 (cardid == PRODID_PSION_NET100))) {
+	/* Download the Seven of Diamonds firmware */
+	for (i = 0; i < sizeof(__Xilinx7OD); i++) {
+	    outb(__Xilinx7OD[i], link->io.BasePort1+2);
+	    udelay(50);
+	}
+    } else if (manfid == MANFID_OSITECH) {
+	/* Make sure both functions are powered up */
+	set_bits(0x300, link->io.BasePort1 + OSITECH_AUI_PWR);
+	/* Now, turn on the interrupt for both card functions */
+	set_bits(0x300, link->io.BasePort1 + OSITECH_RESET_ISR);
+	DEBUG(2, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n",
+	      inw(link->io.BasePort1 + OSITECH_AUI_PWR),
+	      inw(link->io.BasePort1 + OSITECH_RESET_ISR));
+    }
+
+    return 0;
+}
+
+/*======================================================================
+
+    This verifies that the chip is some SMC91cXX variant, and returns
+    the revision code if successful.  Otherwise, it returns -ENODEV.
+
+======================================================================*/
+
+static int check_sig(dev_link_t *link)
+{
+    struct net_device *dev = link->priv;
+    kio_addr_t ioaddr = dev->base_addr;
+    int width;
+    u_short s;
+
+    SMC_SELECT_BANK(1);
+    if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) {
+	/* Try powering up the chip */
+	outw(0, ioaddr + CONTROL);
+	mdelay(55);
+    }
+
+    /* Try setting bus width */
+    width = (link->io.Attributes1 == IO_DATA_PATH_WIDTH_AUTO);
+    s = inb(ioaddr + CONFIG);
+    if (width)
+	s |= CFG_16BIT;
+    else
+	s &= ~CFG_16BIT;
+    outb(s, ioaddr + CONFIG);
+
+    /* Check Base Address Register to make sure bus width is OK */
+    s = inw(ioaddr + BASE_ADDR);
+    if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) &&
+	((s >> 8) != (s & 0xff))) {
+	SMC_SELECT_BANK(3);
+	s = inw(ioaddr + REVISION);
+	return (s & 0xff);
+    }
+
+    if (width) {
+	event_callback_args_t args;
+	printk(KERN_INFO "smc91c92_cs: using 8-bit IO window.\n");
+	args.client_data = link;
+	smc91c92_event(CS_EVENT_RESET_PHYSICAL, 0, &args);
+	pcmcia_release_io(link->handle, &link->io);
+	link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+	pcmcia_request_io(link->handle, &link->io);
+	smc91c92_event(CS_EVENT_CARD_RESET, 0, &args);
+	return check_sig(link);
+    }
+    return -ENODEV;
+}
+
+/*======================================================================
+
+    smc91c92_config() is scheduled to run after a CARD_INSERTION event
+    is received, to configure the PCMCIA socket, and to make the
+    ethernet device available to the system.
+
+======================================================================*/
+
+#define CS_EXIT_TEST(ret, svc, label) \
+if (ret != CS_SUCCESS) { cs_error(link->handle, svc, ret); goto label; }
+
+static void smc91c92_config(dev_link_t *link)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    struct smc_private *smc = netdev_priv(dev);
+    tuple_t tuple;
+    cisparse_t parse;
+    u_short buf[32];
+    char *name;
+    int i, j, rev;
+    kio_addr_t ioaddr;
+    u_long mir;
+
+    DEBUG(0, "smc91c92_config(0x%p)\n", link);
+
+    tuple.Attributes = tuple.TupleOffset = 0;
+    tuple.TupleData = (cisdata_t *)buf;
+    tuple.TupleDataMax = sizeof(buf);
+
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    i = first_tuple(handle, &tuple, &parse);
+    CS_EXIT_TEST(i, ParseTuple, config_failed);
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present = parse.config.rmask[0];
+
+    tuple.DesiredTuple = CISTPL_MANFID;
+    tuple.Attributes = TUPLE_RETURN_COMMON;
+    if (first_tuple(handle, &tuple, &parse) == CS_SUCCESS) {
+	smc->manfid = parse.manfid.manf;
+	smc->cardid = parse.manfid.card;
+    }
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    if ((smc->manfid == MANFID_OSITECH) &&
+	(smc->cardid != PRODID_OSITECH_SEVEN)) {
+	i = osi_config(link);
+    } else if ((smc->manfid == MANFID_MOTOROLA) ||
+	       ((smc->manfid == MANFID_MEGAHERTZ) &&
+		((smc->cardid == PRODID_MEGAHERTZ_VARIOUS) ||
+		 (smc->cardid == PRODID_MEGAHERTZ_EM3288)))) {
+	i = mhz_mfc_config(link);
+    } else {
+	i = smc_config(link);
+    }
+    CS_EXIT_TEST(i, RequestIO, config_failed);
+
+    i = pcmcia_request_irq(link->handle, &link->irq);
+    CS_EXIT_TEST(i, RequestIRQ, config_failed);
+    i = pcmcia_request_configuration(link->handle, &link->conf);
+    CS_EXIT_TEST(i, RequestConfiguration, config_failed);
+
+    if (smc->manfid == MANFID_MOTOROLA)
+	mot_config(link);
+
+    dev->irq = link->irq.AssignedIRQ;
+
+    if ((if_port >= 0) && (if_port <= 2))
+	dev->if_port = if_port;
+    else
+	printk(KERN_NOTICE "smc91c92_cs: invalid if_port requested\n");
+
+    switch (smc->manfid) {
+    case MANFID_OSITECH:
+    case MANFID_PSION:
+	i = osi_setup(link, smc->manfid, smc->cardid); break;
+    case MANFID_SMC:
+    case MANFID_NEW_MEDIA:
+	i = smc_setup(link); break;
+    case 0x128: /* For broken Megahertz cards */
+    case MANFID_MEGAHERTZ:
+	i = mhz_setup(link); break;
+    case MANFID_MOTOROLA:
+    default: /* get the hw address from EEPROM */
+	i = mot_setup(link); break;
+    }
+
+    if (i != 0) {
+	printk(KERN_NOTICE "smc91c92_cs: Unable to find hardware address.\n");
+	goto config_undo;
+    }
+
+    smc->duplex = 0;
+    smc->rx_ovrn = 0;
+
+    rev = check_sig(link);
+    name = "???";
+    if (rev > 0)
+	switch (rev >> 4) {
+	case 3: name = "92"; break;
+	case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break;
+	case 5: name = "95"; break;
+	case 7: name = "100"; break;
+	case 8: name = "100-FD"; break;
+	case 9: name = "110"; break;
+	}
+
+    ioaddr = dev->base_addr;
+    if (rev > 0) {
+	u_long mcr;
+	SMC_SELECT_BANK(0);
+	mir = inw(ioaddr + MEMINFO) & 0xff;
+	if (mir == 0xff) mir++;
+	/* Get scale factor for memory size */
+	mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200;
+	mir *= 128 * (1<<((mcr >> 9) & 7));
+	SMC_SELECT_BANK(1);
+	smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT;
+	smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC;
+	if (smc->manfid == MANFID_OSITECH)
+	    smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0;
+	if ((rev >> 4) >= 7)
+	    smc->cfg |= CFG_MII_SELECT;
+    } else
+	mir = 0;
+
+    if (smc->cfg & CFG_MII_SELECT) {
+	SMC_SELECT_BANK(3);
+
+	for (i = 0; i < 32; i++) {
+	    j = mdio_read(dev, i, 1);
+	    if ((j != 0) && (j != 0xffff)) break;
+	}
+	smc->mii_if.phy_id = (i < 32) ? i : -1;
+
+	SMC_SELECT_BANK(0);
+    }
+
+    link->dev = &smc->node;
+    link->state &= ~DEV_CONFIG_PENDING;
+    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+
+    if (register_netdev(dev) != 0) {
+	printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n");
+	link->dev = NULL;
+	goto config_undo;
+    }
+
+    strcpy(smc->node.dev_name, dev->name);
+
+    printk(KERN_INFO "%s: smc91c%s rev %d: io %#3lx, irq %d, "
+	   "hw_addr ", dev->name, name, (rev & 0x0f), dev->base_addr,
+	   dev->irq);
+    for (i = 0; i < 6; i++)
+	printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n"));
+
+    if (rev > 0) {
+	if (mir & 0x3ff)
+	    printk(KERN_INFO "  %lu byte", mir);
+	else
+	    printk(KERN_INFO "  %lu kb", mir>>10);
+	printk(" buffer, %s xcvr\n", (smc->cfg & CFG_MII_SELECT) ?
+	       "MII" : if_names[dev->if_port]);
+    }
+
+    if (smc->cfg & CFG_MII_SELECT) {
+	if (smc->mii_if.phy_id != -1) {
+	    DEBUG(0, "  MII transceiver at index %d, status %x.\n",
+		  smc->mii_if.phy_id, j);
+	} else {
+    	    printk(KERN_NOTICE "  No MII transceivers found!\n");
+	}
+    }
+
+    return;
+
+config_undo:
+    unregister_netdev(dev);
+config_failed:			/* CS_EXIT_TEST() calls jump to here... */
+    smc91c92_release(link);
+    link->state &= ~DEV_CONFIG_PENDING;
+
+} /* smc91c92_config */
+
+/*======================================================================
+
+    After a card is removed, smc91c92_release() will unregister the net
+    device, and release the PCMCIA configuration.  If the device is
+    still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void smc91c92_release(dev_link_t *link)
+{
+
+    DEBUG(0, "smc91c92_release(0x%p)\n", link);
+
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    if (link->win) {
+	struct net_device *dev = link->priv;
+	struct smc_private *smc = netdev_priv(dev);
+	iounmap(smc->base);
+	pcmcia_release_window(link->win);
+    }
+
+    link->state &= ~DEV_CONFIG;
+}
+
+/*======================================================================
+
+    The card status event handler.  Mostly, this schedules other
+    stuff to run after an event is received.  A CARD_REMOVAL event
+    also sets some flags to discourage the net drivers from trying
+    to talk to the card any more.
+
+======================================================================*/
+
+static int smc91c92_event(event_t event, int priority,
+			  event_callback_args_t *args)
+{
+    dev_link_t *link = args->client_data;
+    struct net_device *dev = link->priv;
+    struct smc_private *smc = netdev_priv(dev);
+    int i;
+
+    DEBUG(1, "smc91c92_event(0x%06x)\n", event);
+
+    switch (event) {
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG)
+	    netif_device_detach(dev);
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	smc91c92_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG) {
+	    if (link->open)
+		netif_device_detach(dev);
+	    pcmcia_release_configuration(link->handle);
+	}
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG) {
+	    if ((smc->manfid == MANFID_MEGAHERTZ) &&
+		(smc->cardid == PRODID_MEGAHERTZ_EM3288))
+		mhz_3288_power(link);
+	    pcmcia_request_configuration(link->handle, &link->conf);
+	    if (smc->manfid == MANFID_MOTOROLA)
+		mot_config(link);
+	    if ((smc->manfid == MANFID_OSITECH) &&
+		(smc->cardid != PRODID_OSITECH_SEVEN)) {
+		/* Power up the card and enable interrupts */
+		set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
+		set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
+	    }
+	    if (((smc->manfid == MANFID_OSITECH) &&
+	 	(smc->cardid == PRODID_OSITECH_SEVEN)) ||
+		((smc->manfid == MANFID_PSION) &&
+	 	(smc->cardid == PRODID_PSION_NET100))) {
+		/* Download the Seven of Diamonds firmware */
+		for (i = 0; i < sizeof(__Xilinx7OD); i++) {
+	    	    outb(__Xilinx7OD[i], link->io.BasePort1+2);
+	   	    udelay(50);
+		}
+	    }
+	    if (link->open) {
+		smc_reset(dev);
+		netif_device_attach(dev);
+	    }
+	}
+	break;
+    }
+    return 0;
+} /* smc91c92_event */
+
+/*======================================================================
+
+    MII interface support for SMC91cXX based cards
+======================================================================*/
+
+#define MDIO_SHIFT_CLK		0x04
+#define MDIO_DATA_OUT		0x01
+#define MDIO_DIR_WRITE		0x08
+#define MDIO_DATA_WRITE0	(MDIO_DIR_WRITE)
+#define MDIO_DATA_WRITE1	(MDIO_DIR_WRITE | MDIO_DATA_OUT)
+#define MDIO_DATA_READ		0x02
+
+static void mdio_sync(kio_addr_t addr)
+{
+    int bits;
+    for (bits = 0; bits < 32; bits++) {
+	outb(MDIO_DATA_WRITE1, addr);
+	outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
+    }
+}
+
+static int mdio_read(struct net_device *dev, int phy_id, int loc)
+{
+    kio_addr_t addr = dev->base_addr + MGMT;
+    u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
+    int i, retval = 0;
+
+    mdio_sync(addr);
+    for (i = 13; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb(dat, addr);
+	outb(dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 19; i > 0; i--) {
+	outb(0, addr);
+	retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
+	outb(MDIO_SHIFT_CLK, addr);
+    }
+    return (retval>>1) & 0xffff;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int loc, int value)
+{
+    kio_addr_t addr = dev->base_addr + MGMT;
+    u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
+    int i;
+
+    mdio_sync(addr);
+    for (i = 31; i >= 0; i--) {
+	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
+	outb(dat, addr);
+	outb(dat | MDIO_SHIFT_CLK, addr);
+    }
+    for (i = 1; i >= 0; i--) {
+	outb(0, addr);
+	outb(MDIO_SHIFT_CLK, addr);
+    }
+}
+
+/*======================================================================
+
+    The driver core code, most of which should be common with a
+    non-PCMCIA implementation.
+
+======================================================================*/
+
+#ifdef PCMCIA_DEBUG
+static void smc_dump(struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+    u_short i, w, save;
+    save = inw(ioaddr + BANK_SELECT);
+    for (w = 0; w < 4; w++) {
+	SMC_SELECT_BANK(w);
+	printk(KERN_DEBUG "bank %d: ", w);
+	for (i = 0; i < 14; i += 2)
+	    printk(" %04x", inw(ioaddr + i));
+	printk("\n");
+    }
+    outw(save, ioaddr + BANK_SELECT);
+}
+#endif
+
+static int smc_open(struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    dev_link_t *link = &smc->link;
+
+#ifdef PCMCIA_DEBUG
+    DEBUG(0, "%s: smc_open(%p), ID/Window %4.4x.\n",
+	  dev->name, dev, inw(dev->base_addr + BANK_SELECT));
+    if (pc_debug > 1) smc_dump(dev);
+#endif
+
+    /* Check that the PCMCIA card is still here. */
+    if (!DEV_OK(link))
+	return -ENODEV;
+    /* Physical device present signature. */
+    if (check_sig(link) < 0) {
+	printk("smc91c92_cs: Yikes!  Bad chip signature!\n");
+	return -ENODEV;
+    }
+    link->open++;
+
+    netif_start_queue(dev);
+    smc->saved_skb = NULL;
+    smc->packets_waiting = 0;
+
+    smc_reset(dev);
+    init_timer(&smc->media);
+    smc->media.function = &media_check;
+    smc->media.data = (u_long) dev;
+    smc->media.expires = jiffies + HZ;
+    add_timer(&smc->media);
+
+    return 0;
+} /* smc_open */
+
+/*====================================================================*/
+
+static int smc_close(struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    dev_link_t *link = &smc->link;
+    kio_addr_t ioaddr = dev->base_addr;
+
+    DEBUG(0, "%s: smc_close(), status %4.4x.\n",
+	  dev->name, inw(ioaddr + BANK_SELECT));
+
+    netif_stop_queue(dev);
+
+    /* Shut off all interrupts, and turn off the Tx and Rx sections.
+       Don't bother to check for chip present. */
+    SMC_SELECT_BANK(2);	/* Nominally paranoia, but do no assume... */
+    outw(0, ioaddr + INTERRUPT);
+    SMC_SELECT_BANK(0);
+    mask_bits(0xff00, ioaddr + RCR);
+    mask_bits(0xff00, ioaddr + TCR);
+
+    /* Put the chip into power-down mode. */
+    SMC_SELECT_BANK(1);
+    outw(CTL_POWERDOWN, ioaddr + CONTROL );
+
+    link->open--;
+    del_timer_sync(&smc->media);
+
+    return 0;
+} /* smc_close */
+
+/*======================================================================
+
+   Transfer a packet to the hardware and trigger the packet send.
+   This may be called at either from either the Tx queue code
+   or the interrupt handler.
+
+======================================================================*/
+
+static void smc_hardware_send_packet(struct net_device * dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    struct sk_buff *skb = smc->saved_skb;
+    kio_addr_t ioaddr = dev->base_addr;
+    u_char packet_no;
+
+    if (!skb) {
+	printk(KERN_ERR "%s: In XMIT with no packet to send.\n", dev->name);
+	return;
+    }
+
+    /* There should be a packet slot waiting. */
+    packet_no = inw(ioaddr + PNR_ARR) >> 8;
+    if (packet_no & 0x80) {
+	/* If not, there is a hardware problem!  Likely an ejected card. */
+	printk(KERN_WARNING "%s: 91c92 hardware Tx buffer allocation"
+	       " failed, status %#2.2x.\n", dev->name, packet_no);
+	dev_kfree_skb_irq(skb);
+	smc->saved_skb = NULL;
+	netif_start_queue(dev);
+	return;
+    }
+
+    smc->stats.tx_bytes += skb->len;
+    /* The card should use the just-allocated buffer. */
+    outw(packet_no, ioaddr + PNR_ARR);
+    /* point to the beginning of the packet */
+    outw(PTR_AUTOINC , ioaddr + POINTER);
+
+    /* Send the packet length (+6 for status, length and ctl byte)
+       and the status word (set to zeros). */
+    {
+	u_char *buf = skb->data;
+	u_int length = skb->len; /* The chip will pad to ethernet min. */
+
+	DEBUG(2, "%s: Trying to xmit packet of length %d.\n",
+	      dev->name, length);
+	
+	/* send the packet length: +6 for status word, length, and ctl */
+	outw(0, ioaddr + DATA_1);
+	outw(length + 6, ioaddr + DATA_1);
+	outsw(ioaddr + DATA_1, buf, length >> 1);
+	
+	/* The odd last byte, if there is one, goes in the control word. */
+	outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1);
+    }
+
+    /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */
+    outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) |
+	 (inw(ioaddr + INTERRUPT) & 0xff00),
+	 ioaddr + INTERRUPT);
+
+    /* The chip does the rest of the work. */
+    outw(MC_ENQUEUE , ioaddr + MMU_CMD);
+
+    smc->saved_skb = NULL;
+    dev_kfree_skb_irq(skb);
+    dev->trans_start = jiffies;
+    netif_start_queue(dev);
+    return;
+}
+
+/*====================================================================*/
+
+static void smc_tx_timeout(struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+
+    printk(KERN_NOTICE "%s: SMC91c92 transmit timed out, "
+	   "Tx_status %2.2x status %4.4x.\n",
+	   dev->name, inw(ioaddr)&0xff, inw(ioaddr + 2));
+    smc->stats.tx_errors++;
+    smc_reset(dev);
+    dev->trans_start = jiffies;
+    smc->saved_skb = NULL;
+    netif_wake_queue(dev);
+}
+
+static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    u_short num_pages;
+    short time_out, ir;
+
+    netif_stop_queue(dev);
+
+    DEBUG(2, "%s: smc_start_xmit(length = %d) called,"
+	  " status %4.4x.\n", dev->name, skb->len, inw(ioaddr + 2));
+
+    if (smc->saved_skb) {
+	/* THIS SHOULD NEVER HAPPEN. */
+	smc->stats.tx_aborted_errors++;
+	printk(KERN_DEBUG "%s: Internal error -- sent packet while busy.\n",
+	       dev->name);
+	return 1;
+    }
+    smc->saved_skb = skb;
+
+    num_pages = skb->len >> 8;
+
+    if (num_pages > 7) {
+	printk(KERN_ERR "%s: Far too big packet error.\n", dev->name);
+	dev_kfree_skb (skb);
+	smc->saved_skb = NULL;
+	smc->stats.tx_dropped++;
+	return 0;		/* Do not re-queue this packet. */
+    }
+    /* A packet is now waiting. */
+    smc->packets_waiting++;
+
+    SMC_SELECT_BANK(2);	/* Paranoia, we should always be in window 2 */
+
+    /* need MC_RESET to keep the memory consistent. errata? */
+    if (smc->rx_ovrn) {
+	outw(MC_RESET, ioaddr + MMU_CMD);
+	smc->rx_ovrn = 0;
+    }
+
+    /* Allocate the memory; send the packet now if we win. */
+    outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD);
+    for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) {
+	ir = inw(ioaddr+INTERRUPT);
+	if (ir & IM_ALLOC_INT) {
+	    /* Acknowledge the interrupt, send the packet. */
+	    outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT);
+	    smc_hardware_send_packet(dev);	/* Send the packet now.. */
+	    return 0;
+	}
+    }
+
+    /* Otherwise defer until the Tx-space-allocated interrupt. */
+    DEBUG(2, "%s: memory allocation deferred.\n", dev->name);
+    outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
+
+    return 0;
+}
+
+/*======================================================================
+
+    Handle a Tx anomolous event.  Entered while in Window 2.
+
+======================================================================*/
+
+static void smc_tx_err(struct net_device * dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    int saved_packet = inw(ioaddr + PNR_ARR) & 0xff;
+    int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f;
+    int tx_status;
+
+    /* select this as the packet to read from */
+    outw(packet_no, ioaddr + PNR_ARR);
+
+    /* read the first word from this packet */
+    outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER);
+
+    tx_status = inw(ioaddr + DATA_1);
+
+    smc->stats.tx_errors++;
+    if (tx_status & TS_LOSTCAR) smc->stats.tx_carrier_errors++;
+    if (tx_status & TS_LATCOL)  smc->stats.tx_window_errors++;
+    if (tx_status & TS_16COL) {
+	smc->stats.tx_aborted_errors++;
+	smc->tx_err++;
+    }
+
+    if (tx_status & TS_SUCCESS) {
+	printk(KERN_NOTICE "%s: Successful packet caused error "
+	       "interrupt?\n", dev->name);
+    }
+    /* re-enable transmit */
+    SMC_SELECT_BANK(0);
+    outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
+    SMC_SELECT_BANK(2);
+
+    outw(MC_FREEPKT, ioaddr + MMU_CMD); 	/* Free the packet memory. */
+
+    /* one less packet waiting for me */
+    smc->packets_waiting--;
+
+    outw(saved_packet, ioaddr + PNR_ARR);
+    return;
+}
+
+/*====================================================================*/
+
+static void smc_eph_irq(struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    u_short card_stats, ephs;
+
+    SMC_SELECT_BANK(0);
+    ephs = inw(ioaddr + EPH);
+    DEBUG(2, "%s: Ethernet protocol handler interrupt, status"
+	  " %4.4x.\n", dev->name, ephs);
+    /* Could be a counter roll-over warning: update stats. */
+    card_stats = inw(ioaddr + COUNTER);
+    /* single collisions */
+    smc->stats.collisions += card_stats & 0xF;
+    card_stats >>= 4;
+    /* multiple collisions */
+    smc->stats.collisions += card_stats & 0xF;
+#if 0 		/* These are for when linux supports these statistics */
+    card_stats >>= 4;			/* deferred */
+    card_stats >>= 4;			/* excess deferred */
+#endif
+    /* If we had a transmit error we must re-enable the transmitter. */
+    outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
+
+    /* Clear a link error interrupt. */
+    SMC_SELECT_BANK(1);
+    outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL);
+    outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
+	 ioaddr + CONTROL);
+    SMC_SELECT_BANK(2);
+}
+
+/*====================================================================*/
+
+static irqreturn_t smc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct net_device *dev = dev_id;
+    struct smc_private *smc = netdev_priv(dev);
+    kio_addr_t ioaddr;
+    u_short saved_bank, saved_pointer, mask, status;
+    unsigned int handled = 1;
+    char bogus_cnt = INTR_WORK;		/* Work we are willing to do. */
+
+    if (!netif_device_present(dev))
+	return IRQ_NONE;
+
+    ioaddr = dev->base_addr;
+
+    DEBUG(3, "%s: SMC91c92 interrupt %d at %#x.\n", dev->name,
+	  irq, ioaddr);
+
+    smc->watchdog = 0;
+    saved_bank = inw(ioaddr + BANK_SELECT);
+    if ((saved_bank & 0xff00) != 0x3300) {
+	/* The device does not exist -- the card could be off-line, or
+	   maybe it has been ejected. */
+	DEBUG(1, "%s: SMC91c92 interrupt %d for non-existent"
+	      "/ejected device.\n", dev->name, irq);
+	handled = 0;
+	goto irq_done;
+    }
+
+    SMC_SELECT_BANK(2);
+    saved_pointer = inw(ioaddr + POINTER);
+    mask = inw(ioaddr + INTERRUPT) >> 8;
+    /* clear all interrupts */
+    outw(0, ioaddr + INTERRUPT);
+
+    do { /* read the status flag, and mask it */
+	status = inw(ioaddr + INTERRUPT) & 0xff;
+	DEBUG(3, "%s: Status is %#2.2x (mask %#2.2x).\n", dev->name,
+	      status, mask);
+	if ((status & mask) == 0) {
+	    if (bogus_cnt == INTR_WORK)
+		handled = 0;
+	    break;
+	}
+	if (status & IM_RCV_INT) {
+	    /* Got a packet(s). */
+	    smc_rx(dev);
+	}
+	if (status & IM_TX_INT) {
+	    smc_tx_err(dev);
+	    outw(IM_TX_INT, ioaddr + INTERRUPT);
+	}
+	status &= mask;
+	if (status & IM_TX_EMPTY_INT) {
+	    outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT);
+	    mask &= ~IM_TX_EMPTY_INT;
+	    smc->stats.tx_packets += smc->packets_waiting;
+	    smc->packets_waiting = 0;
+	}
+	if (status & IM_ALLOC_INT) {
+	    /* Clear this interrupt so it doesn't happen again */
+	    mask &= ~IM_ALLOC_INT;
+	
+	    smc_hardware_send_packet(dev);
+	
+	    /* enable xmit interrupts based on this */
+	    mask |= (IM_TX_EMPTY_INT | IM_TX_INT);
+	
+	    /* and let the card send more packets to me */
+	    netif_wake_queue(dev);
+	}
+	if (status & IM_RX_OVRN_INT) {
+	    smc->stats.rx_errors++;
+	    smc->stats.rx_fifo_errors++;
+	    if (smc->duplex)
+		smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */
+	    outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT);
+	}
+	if (status & IM_EPH_INT)
+	    smc_eph_irq(dev);
+    } while (--bogus_cnt);
+
+    DEBUG(3, "  Restoring saved registers mask %2.2x bank %4.4x"
+	  " pointer %4.4x.\n", mask, saved_bank, saved_pointer);
+
+    /* restore state register */
+    outw((mask<<8), ioaddr + INTERRUPT);
+    outw(saved_pointer, ioaddr + POINTER);
+    SMC_SELECT_BANK(saved_bank);
+
+    DEBUG(3, "%s: Exiting interrupt IRQ%d.\n", dev->name, irq);
+
+irq_done:
+
+    if ((smc->manfid == MANFID_OSITECH) &&
+	(smc->cardid != PRODID_OSITECH_SEVEN)) {
+	/* Retrigger interrupt if needed */
+	mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR);
+	set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR);
+    }
+    if (smc->manfid == MANFID_MOTOROLA) {
+	u_char cor;
+	cor = readb(smc->base + MOT_UART + CISREG_COR);
+	writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR);
+	writeb(cor, smc->base + MOT_UART + CISREG_COR);
+	cor = readb(smc->base + MOT_LAN + CISREG_COR);
+	writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR);
+	writeb(cor, smc->base + MOT_LAN + CISREG_COR);
+    }
+#ifdef DOES_NOT_WORK
+    if (smc->base != NULL) { /* Megahertz MFC's */
+	readb(smc->base+MEGAHERTZ_ISR);
+	readb(smc->base+MEGAHERTZ_ISR);
+    }
+#endif
+    return IRQ_RETVAL(handled);
+}
+
+/*====================================================================*/
+
+static void smc_rx(struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    int rx_status;
+    int packet_length;	/* Caution: not frame length, rather words
+			   to transfer from the chip. */
+
+    /* Assertion: we are in Window 2. */
+
+    if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) {
+	printk(KERN_ERR "%s: smc_rx() with nothing on Rx FIFO.\n",
+	       dev->name);
+	return;
+    }
+
+    /*  Reset the read pointer, and read the status and packet length. */
+    outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER);
+    rx_status = inw(ioaddr + DATA_1);
+    packet_length = inw(ioaddr + DATA_1) & 0x07ff;
+
+    DEBUG(2, "%s: Receive status %4.4x length %d.\n",
+	  dev->name, rx_status, packet_length);
+
+    if (!(rx_status & RS_ERRORS)) {		
+	/* do stuff to make a new packet */
+	struct sk_buff *skb;
+	
+	/* Note: packet_length adds 5 or 6 extra bytes here! */
+	skb = dev_alloc_skb(packet_length+2);
+	
+	if (skb == NULL) {
+	    DEBUG(1, "%s: Low memory, packet dropped.\n", dev->name);
+	    smc->stats.rx_dropped++;
+	    outw(MC_RELEASE, ioaddr + MMU_CMD);
+	    return;
+	}
+	
+	packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6);
+	skb_reserve(skb, 2);
+	insw(ioaddr+DATA_1, skb_put(skb, packet_length),
+	     (packet_length+1)>>1);
+	skb->protocol = eth_type_trans(skb, dev);
+	
+	skb->dev = dev;
+	netif_rx(skb);
+	dev->last_rx = jiffies;
+	smc->stats.rx_packets++;
+	smc->stats.rx_bytes += packet_length;
+	if (rx_status & RS_MULTICAST)
+	    smc->stats.multicast++;
+    } else {
+	/* error ... */
+	smc->stats.rx_errors++;
+	
+	if (rx_status & RS_ALGNERR)  smc->stats.rx_frame_errors++;
+	if (rx_status & (RS_TOOSHORT | RS_TOOLONG))
+	    smc->stats.rx_length_errors++;
+	if (rx_status & RS_BADCRC)	smc->stats.rx_crc_errors++;
+    }
+    /* Let the MMU free the memory of this packet. */
+    outw(MC_RELEASE, ioaddr + MMU_CMD);
+
+    return;
+}
+
+/*====================================================================*/
+
+static struct net_device_stats *smc_get_stats(struct net_device *dev)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    /* Nothing to update - the 91c92 is a pretty primative chip. */
+    return &smc->stats;
+}
+
+/*======================================================================
+
+    Calculate values for the hardware multicast filter hash table.
+
+======================================================================*/
+
+static void fill_multicast_tbl(int count, struct dev_mc_list *addrs,
+			       u_char *multicast_table)
+{
+    struct dev_mc_list	*mc_addr;
+
+    for (mc_addr = addrs;  mc_addr && --count > 0;  mc_addr = mc_addr->next) {
+	u_int position = ether_crc(6, mc_addr->dmi_addr);
+#ifndef final_version		/* Verify multicast address. */
+	if ((mc_addr->dmi_addr[0] & 1) == 0)
+	    continue;
+#endif
+	multicast_table[position >> 29] |= 1 << ((position >> 26) & 7);
+    }
+}
+
+/*======================================================================
+
+    Set the receive mode.
+
+    This routine is used by both the protocol level to notify us of
+    promiscuous/multicast mode changes, and by the open/reset code to
+    initialize the Rx registers.  We always set the multicast list and
+    leave the receiver running.
+
+======================================================================*/
+
+static void set_rx_mode(struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+    struct smc_private *smc = netdev_priv(dev);
+    u_int multicast_table[ 2 ] = { 0, };
+    unsigned long flags;
+    u_short rx_cfg_setting;
+
+    if (dev->flags & IFF_PROMISC) {
+	printk(KERN_NOTICE "%s: setting Rx mode to promiscuous.\n", dev->name);
+	rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti;
+    } else if (dev->flags & IFF_ALLMULTI)
+	rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti;
+    else {
+	if (dev->mc_count)  {
+	    fill_multicast_tbl(dev->mc_count, dev->mc_list,
+			       (u_char *)multicast_table);
+	}
+	rx_cfg_setting = RxStripCRC | RxEnable;
+    }
+
+    /* Load MC table and Rx setting into the chip without interrupts. */
+    spin_lock_irqsave(&smc->lock, flags);
+    SMC_SELECT_BANK(3);
+    outl(multicast_table[0], ioaddr + MULTICAST0);
+    outl(multicast_table[1], ioaddr + MULTICAST4);
+    SMC_SELECT_BANK(0);
+    outw(rx_cfg_setting, ioaddr + RCR);
+    SMC_SELECT_BANK(2);
+    spin_unlock_irqrestore(&smc->lock, flags);
+
+    return;
+}
+
+/*======================================================================
+
+    Senses when a card's config changes. Here, it's coax or TP.
+
+======================================================================*/
+
+static int s9k_config(struct net_device *dev, struct ifmap *map)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
+	if (smc->cfg & CFG_MII_SELECT)
+	    return -EOPNOTSUPP;
+	else if (map->port > 2)
+	    return -EINVAL;
+	dev->if_port = map->port;
+	printk(KERN_INFO "%s: switched to %s port\n",
+	       dev->name, if_names[dev->if_port]);
+	smc_reset(dev);
+    }
+    return 0;
+}
+
+/*======================================================================
+
+    Reset the chip, reloading every register that might be corrupted.
+
+======================================================================*/
+
+/*
+  Set transceiver type, perhaps to something other than what the user
+  specified in dev->if_port.
+*/
+static void smc_set_xcvr(struct net_device *dev, int if_port)
+{
+    struct smc_private *smc = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    u_short saved_bank;
+
+    saved_bank = inw(ioaddr + BANK_SELECT);
+    SMC_SELECT_BANK(1);
+    if (if_port == 2) {
+	outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG);
+	if ((smc->manfid == MANFID_OSITECH) &&
+	    (smc->cardid != PRODID_OSITECH_SEVEN))
+	    set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
+	smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002);
+    } else {
+	outw(smc->cfg, ioaddr + CONFIG);
+	if ((smc->manfid == MANFID_OSITECH) &&
+	    (smc->cardid != PRODID_OSITECH_SEVEN))
+	    mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
+	smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001);
+    }
+    SMC_SELECT_BANK(saved_bank);
+}
+
+static void smc_reset(struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+    struct smc_private *smc = netdev_priv(dev);
+    int i;
+
+    DEBUG(0, "%s: smc91c92 reset called.\n", dev->name);
+
+    /* The first interaction must be a write to bring the chip out
+       of sleep mode. */
+    SMC_SELECT_BANK(0);
+    /* Reset the chip. */
+    outw(RCR_SOFTRESET, ioaddr + RCR);
+    udelay(10);
+
+    /* Clear the transmit and receive configuration registers. */
+    outw(RCR_CLEAR, ioaddr + RCR);
+    outw(TCR_CLEAR, ioaddr + TCR);
+
+    /* Set the Window 1 control, configuration and station addr registers.
+       No point in writing the I/O base register ;-> */
+    SMC_SELECT_BANK(1);
+    /* Automatically release succesfully transmitted packets,
+       Accept link errors, counter and Tx error interrupts. */
+    outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
+	 ioaddr + CONTROL);
+    smc_set_xcvr(dev, dev->if_port);
+    if ((smc->manfid == MANFID_OSITECH) &&
+	(smc->cardid != PRODID_OSITECH_SEVEN))
+	outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) |
+	     (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00),
+	     ioaddr - 0x10 + OSITECH_AUI_PWR);
+
+    /* Fill in the physical address.  The databook is wrong about the order! */
+    for (i = 0; i < 6; i += 2)
+	outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i],
+	     ioaddr + ADDR0 + i);
+
+    /* Reset the MMU */
+    SMC_SELECT_BANK(2);
+    outw(MC_RESET, ioaddr + MMU_CMD);
+    outw(0, ioaddr + INTERRUPT);
+
+    /* Re-enable the chip. */
+    SMC_SELECT_BANK(0);
+    outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) |
+	 TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR);
+    set_rx_mode(dev);
+
+    if (smc->cfg & CFG_MII_SELECT) {
+	SMC_SELECT_BANK(3);
+
+	/* Reset MII */
+	mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000);
+
+	/* Advertise 100F, 100H, 10F, 10H */
+	mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1);
+
+	/* Restart MII autonegotiation */
+	mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000);
+	mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200);
+    }
+
+    /* Enable interrupts. */
+    SMC_SELECT_BANK(2);
+    outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,
+	 ioaddr + INTERRUPT);
+}
+
+/*======================================================================
+
+    Media selection timer routine
+
+======================================================================*/
+
+static void media_check(u_long arg)
+{
+    struct net_device *dev = (struct net_device *) arg;
+    struct smc_private *smc = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    u_short i, media, saved_bank;
+    u_short link;
+
+    saved_bank = inw(ioaddr + BANK_SELECT);
+
+    if (!netif_device_present(dev))
+	goto reschedule;
+
+    SMC_SELECT_BANK(2);
+
+    /* need MC_RESET to keep the memory consistent. errata? */
+    if (smc->rx_ovrn) {
+	outw(MC_RESET, ioaddr + MMU_CMD);
+	smc->rx_ovrn = 0;
+    }
+    i = inw(ioaddr + INTERRUPT);
+    SMC_SELECT_BANK(0);
+    media = inw(ioaddr + EPH) & EPH_LINK_OK;
+    SMC_SELECT_BANK(1);
+    media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;
+
+    /* Check for pending interrupt with watchdog flag set: with
+       this, we can limp along even if the interrupt is blocked */
+    if (smc->watchdog++ && ((i>>8) & i)) {
+	if (!smc->fast_poll)
+	    printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name);
+	smc_interrupt(dev->irq, smc, NULL);
+	smc->fast_poll = HZ;
+    }
+    if (smc->fast_poll) {
+	smc->fast_poll--;
+	smc->media.expires = jiffies + HZ/100;
+	add_timer(&smc->media);
+	SMC_SELECT_BANK(saved_bank);
+	return;
+    }
+
+    if (smc->cfg & CFG_MII_SELECT) {
+	if (smc->mii_if.phy_id < 0)
+	    goto reschedule;
+
+	SMC_SELECT_BANK(3);
+	link = mdio_read(dev, smc->mii_if.phy_id, 1);
+	if (!link || (link == 0xffff)) {
+  	    printk(KERN_INFO "%s: MII is missing!\n", dev->name);
+	    smc->mii_if.phy_id = -1;
+	    goto reschedule;
+	}
+
+	link &= 0x0004;
+	if (link != smc->link_status) {
+	    u_short p = mdio_read(dev, smc->mii_if.phy_id, 5);
+	    printk(KERN_INFO "%s: %s link beat\n", dev->name,
+		(link) ? "found" : "lost");
+	    smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40))
+			   ? TCR_FDUPLX : 0);
+	    if (link) {
+	        printk(KERN_INFO "%s: autonegotiation complete: "
+		       "%sbaseT-%cD selected\n", dev->name,
+		       ((p & 0x0180) ? "100" : "10"),
+		       (smc->duplex ? 'F' : 'H'));
+	    }
+	    SMC_SELECT_BANK(0);
+	    outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR);
+	    smc->link_status = link;
+	}
+	goto reschedule;
+    }
+
+    /* Ignore collisions unless we've had no rx's recently */
+    if (jiffies - dev->last_rx > HZ) {
+	if (smc->tx_err || (smc->media_status & EPH_16COL))
+	    media |= EPH_16COL;
+    }
+    smc->tx_err = 0;
+
+    if (media != smc->media_status) {
+	if ((media & smc->media_status & 1) &&
+	    ((smc->media_status ^ media) & EPH_LINK_OK))
+	    printk(KERN_INFO "%s: %s link beat\n", dev->name,
+		   (smc->media_status & EPH_LINK_OK ? "lost" : "found"));
+	else if ((media & smc->media_status & 2) &&
+		 ((smc->media_status ^ media) & EPH_16COL))
+	    printk(KERN_INFO "%s: coax cable %s\n", dev->name,
+		   (media & EPH_16COL ? "problem" : "ok"));
+	if (dev->if_port == 0) {
+	    if (media & 1) {
+		if (media & EPH_LINK_OK)
+		    printk(KERN_INFO "%s: flipped to 10baseT\n",
+			   dev->name);
+		else
+		    smc_set_xcvr(dev, 2);
+	    } else {
+		if (media & EPH_16COL)
+		    smc_set_xcvr(dev, 1);
+		else
+		    printk(KERN_INFO "%s: flipped to 10base2\n",
+			   dev->name);
+	    }
+	}
+	smc->media_status = media;
+    }
+
+reschedule:
+    smc->media.expires = jiffies + HZ;
+    add_timer(&smc->media);
+    SMC_SELECT_BANK(saved_bank);
+}
+
+static int smc_link_ok(struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+    struct smc_private *smc = netdev_priv(dev);
+
+    if (smc->cfg & CFG_MII_SELECT) {
+	return mii_link_ok(&smc->mii_if);
+    } else {
+        SMC_SELECT_BANK(0);
+	return inw(ioaddr + EPH) & EPH_LINK_OK;
+    }
+}
+
+static int smc_netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+    u16 tmp;
+    kio_addr_t ioaddr = dev->base_addr;
+
+    ecmd->supported = (SUPPORTED_TP | SUPPORTED_AUI |
+	SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full);
+		
+    SMC_SELECT_BANK(1);
+    tmp = inw(ioaddr + CONFIG);
+    ecmd->port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP;
+    ecmd->transceiver = XCVR_INTERNAL;
+    ecmd->speed = SPEED_10;
+    ecmd->phy_address = ioaddr + MGMT;
+
+    SMC_SELECT_BANK(0);
+    tmp = inw(ioaddr + TCR);
+    ecmd->duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF;
+
+    return 0;
+}
+
+static int smc_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+    u16 tmp;
+    kio_addr_t ioaddr = dev->base_addr;
+
+    if (ecmd->speed != SPEED_10)
+    	return -EINVAL;
+    if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
+    	return -EINVAL;
+    if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI)
+	return -EINVAL;
+    if (ecmd->transceiver != XCVR_INTERNAL)
+    	return -EINVAL;
+
+    if (ecmd->port == PORT_AUI)
+	smc_set_xcvr(dev, 1);
+    else
+	smc_set_xcvr(dev, 0);
+
+    SMC_SELECT_BANK(0);
+    tmp = inw(ioaddr + TCR);
+    if (ecmd->duplex == DUPLEX_FULL)
+	tmp |= TCR_FDUPLX;
+    else
+	tmp &= ~TCR_FDUPLX;
+    outw(tmp, ioaddr + TCR);
+	
+    return 0;
+}
+
+static int check_if_running(struct net_device *dev)
+{
+	if (!netif_running(dev))
+		return -EINVAL;
+	return 0;
+}
+
+static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, DRV_NAME);
+	strcpy(info->version, DRV_VERSION);
+}
+
+static int smc_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	struct smc_private *smc = netdev_priv(dev);
+	kio_addr_t ioaddr = dev->base_addr;
+	u16 saved_bank = inw(ioaddr + BANK_SELECT);
+	int ret;
+
+	SMC_SELECT_BANK(3);
+	spin_lock_irq(&smc->lock);
+	if (smc->cfg & CFG_MII_SELECT)
+		ret = mii_ethtool_gset(&smc->mii_if, ecmd);
+	else
+		ret = smc_netdev_get_ecmd(dev, ecmd);
+	spin_unlock_irq(&smc->lock);
+	SMC_SELECT_BANK(saved_bank);
+	return ret;
+}
+
+static int smc_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
+{
+	struct smc_private *smc = netdev_priv(dev);
+	kio_addr_t ioaddr = dev->base_addr;
+	u16 saved_bank = inw(ioaddr + BANK_SELECT);
+	int ret;
+
+	SMC_SELECT_BANK(3);
+	spin_lock_irq(&smc->lock);
+	if (smc->cfg & CFG_MII_SELECT)
+		ret = mii_ethtool_sset(&smc->mii_if, ecmd);
+	else
+		ret = smc_netdev_set_ecmd(dev, ecmd);
+	spin_unlock_irq(&smc->lock);
+	SMC_SELECT_BANK(saved_bank);
+	return ret;
+}
+
+static u32 smc_get_link(struct net_device *dev)
+{
+	struct smc_private *smc = netdev_priv(dev);
+	kio_addr_t ioaddr = dev->base_addr;
+	u16 saved_bank = inw(ioaddr + BANK_SELECT);
+	u32 ret;
+
+	SMC_SELECT_BANK(3);
+	spin_lock_irq(&smc->lock);
+	ret = smc_link_ok(dev);
+	spin_unlock_irq(&smc->lock);
+	SMC_SELECT_BANK(saved_bank);
+	return ret;
+}
+
+#ifdef PCMCIA_DEBUG
+static u32 smc_get_msglevel(struct net_device *dev)
+{
+	return pc_debug;
+}
+
+static void smc_set_msglevel(struct net_device *dev, u32 val)
+{
+	pc_debug = val;
+}
+#endif
+
+static int smc_nway_reset(struct net_device *dev)
+{
+	struct smc_private *smc = netdev_priv(dev);
+	if (smc->cfg & CFG_MII_SELECT) {
+		kio_addr_t ioaddr = dev->base_addr;
+		u16 saved_bank = inw(ioaddr + BANK_SELECT);
+		int res;
+
+		SMC_SELECT_BANK(3);
+		res = mii_nway_restart(&smc->mii_if);
+		SMC_SELECT_BANK(saved_bank);
+
+		return res;
+	} else
+		return -EOPNOTSUPP;
+}
+
+static struct ethtool_ops ethtool_ops = {
+	.begin = check_if_running,
+	.get_drvinfo = smc_get_drvinfo,
+	.get_settings = smc_get_settings,
+	.set_settings = smc_set_settings,
+	.get_link = smc_get_link,
+#ifdef PCMCIA_DEBUG
+	.get_msglevel = smc_get_msglevel,
+	.set_msglevel = smc_set_msglevel,
+#endif
+	.nway_reset = smc_nway_reset,
+};
+
+static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct smc_private *smc = netdev_priv(dev);
+	struct mii_ioctl_data *mii = if_mii(rq);
+	int rc = 0;
+	u16 saved_bank;
+	kio_addr_t ioaddr = dev->base_addr;
+
+	if (!netif_running(dev))
+		return -EINVAL;
+
+	spin_lock_irq(&smc->lock);
+	saved_bank = inw(ioaddr + BANK_SELECT);
+	SMC_SELECT_BANK(3);
+	rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL);
+	SMC_SELECT_BANK(saved_bank);
+	spin_unlock_irq(&smc->lock);
+	return rc;
+}
+
+static struct pcmcia_driver smc91c92_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "smc91c92_cs",
+	},
+	.attach		= smc91c92_attach,
+	.detach		= smc91c92_detach,
+};
+
+static int __init init_smc91c92_cs(void)
+{
+	return pcmcia_register_driver(&smc91c92_cs_driver);
+}
+
+static void __exit exit_smc91c92_cs(void)
+{
+	pcmcia_unregister_driver(&smc91c92_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_smc91c92_cs);
+module_exit(exit_smc91c92_cs);
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c
new file mode 100644
index 000000000000..58177d67ea12
--- /dev/null
+++ b/drivers/net/pcmcia/xirc2ps_cs.c
@@ -0,0 +1,2031 @@
+/* [xirc2ps_cs.c wk 03.11.99] (1.40 1999/11/18 00:06:03)
+ * Xircom CreditCard Ethernet Adapter IIps driver
+ * Xircom Realport 10/100 (RE-100) driver 
+ *
+ * This driver supports various Xircom CreditCard Ethernet adapters
+ * including the CE2, CE IIps, RE-10, CEM28, CEM33, CE33, CEM56,
+ * CE3-100, CE3B, RE-100, REM10BT, and REM56G-100.
+ *
+ * 2000-09-24 <psheer@icon.co.za> The Xircom CE3B-100 may not
+ * autodetect the media properly. In this case use the
+ * if_port=1 (for 10BaseT) or if_port=4 (for 100BaseT) options
+ * to force the media type.
+ * 
+ * Written originally by Werner Koch based on David Hinds' skeleton of the
+ * PCMCIA driver.
+ *
+ * Copyright (c) 1997,1998 Werner Koch (dd9jn)
+ *
+ * This driver is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * It is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *
+ *
+ * ALTERNATIVELY, this driver may be distributed under the terms of
+ * the following license, in which case the provisions of this license
+ * are required INSTEAD OF the GNU General Public License.  (This clause
+ * is necessary due to a potential bad interaction between the GPL and
+ * the restrictions contained in a BSD-style copyright.)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, and the entire permission notice in its entirety,
+ *    including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <linux/ethtool.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/bitops.h>
+
+#include <pcmcia/version.h>
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ciscode.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#ifndef MANFID_COMPAQ
+  #define MANFID_COMPAQ 	   0x0138
+  #define MANFID_COMPAQ2	   0x0183  /* is this correct? */
+#endif
+
+#include <pcmcia/ds.h>
+
+/* Time in jiffies before concluding Tx hung */
+#define TX_TIMEOUT	((400*HZ)/1000)
+
+/****************
+ * Some constants used to access the hardware
+ */
+
+/* Register offsets and value constans */
+#define XIRCREG_CR  0	/* Command register (wr) */
+enum xirc_cr {
+    TransmitPacket = 0x01,
+    SoftReset = 0x02,
+    EnableIntr = 0x04,
+    ForceIntr  = 0x08,
+    ClearTxFIFO = 0x10,
+    ClearRxOvrun = 0x20,
+    RestartTx	 = 0x40
+};
+#define XIRCREG_ESR 0	/* Ethernet status register (rd) */
+enum xirc_esr {
+    FullPktRcvd = 0x01, /* full packet in receive buffer */
+    PktRejected = 0x04, /* a packet has been rejected */
+    TxPktPend = 0x08,	/* TX Packet Pending */
+    IncorPolarity = 0x10,
+    MediaSelect = 0x20	/* set if TP, clear if AUI */
+};
+#define XIRCREG_PR  1	/* Page Register select */
+#define XIRCREG_EDP 4	/* Ethernet Data Port Register */
+#define XIRCREG_ISR 6	/* Ethernet Interrupt Status Register */
+enum xirc_isr {
+    TxBufOvr = 0x01,	/* TX Buffer Overflow */
+    PktTxed  = 0x02,	/* Packet Transmitted */
+    MACIntr  = 0x04,	/* MAC Interrupt occurred */
+    TxResGrant = 0x08,	/* Tx Reservation Granted */
+    RxFullPkt = 0x20,	/* Rx Full Packet */
+    RxPktRej  = 0x40,	/* Rx Packet Rejected */
+    ForcedIntr= 0x80	/* Forced Interrupt */
+};
+#define XIRCREG1_IMR0 12 /* Ethernet Interrupt Mask Register (on page 1)*/
+#define XIRCREG1_IMR1 13
+#define XIRCREG0_TSO  8  /* Transmit Space Open Register (on page 0)*/
+#define XIRCREG0_TRS  10 /* Transmit reservation Size Register (page 0)*/
+#define XIRCREG0_DO   12 /* Data Offset Register (page 0) (wr) */
+#define XIRCREG0_RSR  12 /* Receive Status Register (page 0) (rd) */
+enum xirc_rsr {
+    PhyPkt = 0x01,	/* set:physical packet, clear: multicast packet */
+    BrdcstPkt = 0x02,	/* set if it is a broadcast packet */
+    PktTooLong = 0x04,	/* set if packet length > 1518 */
+    AlignErr = 0x10,	/* incorrect CRC and last octet not complete */
+    CRCErr = 0x20,	/* incorrect CRC and last octet is complete */
+    PktRxOk = 0x80	/* received ok */
+};
+#define XIRCREG0_PTR 13 /* packets transmitted register (rd) */
+#define XIRCREG0_RBC 14 /* receive byte count regsister (rd) */
+#define XIRCREG1_ECR 14 /* ethernet configurationn register */
+enum xirc_ecr {
+    FullDuplex = 0x04,	/* enable full duplex mode */
+    LongTPMode = 0x08,	/* adjust for longer lengths of TP cable */
+    DisablePolCor = 0x10,/* disable auto polarity correction */
+    DisableLinkPulse = 0x20, /* disable link pulse generation */
+    DisableAutoTx = 0x40, /* disable auto-transmit */
+};
+#define XIRCREG2_RBS 8	/* receive buffer start register */
+#define XIRCREG2_LED 10 /* LED Configuration register */
+/* values for the leds:    Bits 2-0 for led 1
+ *  0 disabled		   Bits 5-3 for led 2
+ *  1 collision
+ *  2 noncollision
+ *  3 link_detected
+ *  4 incor_polarity
+ *  5 jabber
+ *  6 auto_assertion
+ *  7 rx_tx_activity
+ */
+#define XIRCREG2_MSR 12 /* Mohawk specific register */
+
+#define XIRCREG4_GPR0 8 /* General Purpose Register 0 */
+#define XIRCREG4_GPR1 9 /* General Purpose Register 1 */
+#define XIRCREG2_GPR2 13 /* General Purpose Register 2 (page2!)*/
+#define XIRCREG4_BOV 10 /* Bonding Version Register */
+#define XIRCREG4_LMA 12 /* Local Memory Address Register */
+#define XIRCREG4_LMD 14 /* Local Memory Data Port */
+/* MAC register can only by accessed with 8 bit operations */
+#define XIRCREG40_CMD0 8    /* Command Register (wr) */
+enum xirc_cmd { 	    /* Commands */
+    Transmit = 0x01,
+    EnableRecv = 0x04,
+    DisableRecv = 0x08,
+    Abort = 0x10,
+    Online = 0x20,
+    IntrAck = 0x40,
+    Offline = 0x80
+};
+#define XIRCREG5_RHSA0	10  /* Rx Host Start Address */
+#define XIRCREG40_RXST0 9   /* Receive Status Register */
+#define XIRCREG40_TXST0 11  /* Transmit Status Register 0 */
+#define XIRCREG40_TXST1 12  /* Transmit Status Register 10 */
+#define XIRCREG40_RMASK0 13  /* Receive Mask Register */
+#define XIRCREG40_TMASK0 14  /* Transmit Mask Register 0 */
+#define XIRCREG40_TMASK1 15  /* Transmit Mask Register 0 */
+#define XIRCREG42_SWC0	8   /* Software Configuration 0 */
+#define XIRCREG42_SWC1	9   /* Software Configuration 1 */
+#define XIRCREG42_BOC	10  /* Back-Off Configuration */
+#define XIRCREG44_TDR0	8   /* Time Domain Reflectometry 0 */
+#define XIRCREG44_TDR1	9   /* Time Domain Reflectometry 1 */
+#define XIRCREG44_RXBC_LO 10 /* Rx Byte Count 0 (rd) */
+#define XIRCREG44_RXBC_HI 11 /* Rx Byte Count 1 (rd) */
+#define XIRCREG45_REV	 15 /* Revision Register (rd) */
+#define XIRCREG50_IA	8   /* Individual Address (8-13) */
+
+static char *if_names[] = { "Auto", "10BaseT", "10Base2", "AUI", "100BaseT" };
+
+/****************
+ * All the PCMCIA modules use PCMCIA_DEBUG to control debugging.  If
+ * you do not define PCMCIA_DEBUG at all, all the debug code will be
+ * left out.  If you compile with PCMCIA_DEBUG=0, the debug code will
+ * be present but disabled -- but it can then be enabled for specific
+ * modules at load time with a 'pc_debug=#' option to insmod.
+ */
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KDBG_XIRC args)
+#else
+#define DEBUG(n, args...)
+#endif
+
+#define KDBG_XIRC KERN_DEBUG   "xirc2ps_cs: "
+#define KERR_XIRC KERN_ERR     "xirc2ps_cs: "
+#define KWRN_XIRC KERN_WARNING "xirc2ps_cs: "
+#define KNOT_XIRC KERN_NOTICE  "xirc2ps_cs: "
+#define KINF_XIRC KERN_INFO    "xirc2ps_cs: "
+
+/* card types */
+#define XIR_UNKNOWN  0	/* unknown: not supported */
+#define XIR_CE	     1	/* (prodid 1) different hardware: not supported */
+#define XIR_CE2      2	/* (prodid 2) */
+#define XIR_CE3      3	/* (prodid 3) */
+#define XIR_CEM      4	/* (prodid 1) different hardware: not supported */
+#define XIR_CEM2     5	/* (prodid 2) */
+#define XIR_CEM3     6	/* (prodid 3) */
+#define XIR_CEM33    7	/* (prodid 4) */
+#define XIR_CEM56M   8	/* (prodid 5) */
+#define XIR_CEM56    9	/* (prodid 6) */
+#define XIR_CM28    10	/* (prodid 3) modem only: not supported here */
+#define XIR_CM33    11	/* (prodid 4) modem only: not supported here */
+#define XIR_CM56    12	/* (prodid 5) modem only: not supported here */
+#define XIR_CG	    13	/* (prodid 1) GSM modem only: not supported */
+#define XIR_CBE     14	/* (prodid 1) cardbus ethernet: not supported */
+/*====================================================================*/
+
+/* Module parameters */
+
+MODULE_DESCRIPTION("Xircom PCMCIA ethernet driver");
+MODULE_LICENSE("Dual MPL/GPL");
+
+#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
+
+INT_MODULE_PARM(if_port,	0);
+INT_MODULE_PARM(full_duplex,	0);
+INT_MODULE_PARM(do_sound, 	1);
+INT_MODULE_PARM(lockup_hack,	0);  /* anti lockup hack */
+
+/*====================================================================*/
+
+/* We do not process more than these number of bytes during one
+ * interrupt. (Of course we receive complete packets, so this is not
+ * an exact value).
+ * Something between 2000..22000; first value gives best interrupt latency,
+ * the second enables the usage of the complete on-chip buffer. We use the
+ * high value as the initial value.
+ */
+static unsigned maxrx_bytes = 22000;
+
+/* MII management prototypes */
+static void mii_idle(kio_addr_t ioaddr);
+static void mii_putbit(kio_addr_t ioaddr, unsigned data);
+static int  mii_getbit(kio_addr_t ioaddr);
+static void mii_wbits(kio_addr_t ioaddr, unsigned data, int len);
+static unsigned mii_rd(kio_addr_t ioaddr, u_char phyaddr, u_char phyreg);
+static void mii_wr(kio_addr_t ioaddr, u_char phyaddr, u_char phyreg,
+		   unsigned data, int len);
+
+/*
+ * The event() function is this driver's Card Services event handler.
+ * It will be called by Card Services when an appropriate card status
+ * event is received.  The config() and release() entry points are
+ * used to configure or release a socket, in response to card insertion
+ * and ejection events.  They are invoked from the event handler.
+ */
+
+static int has_ce2_string(dev_link_t * link);
+static void xirc2ps_config(dev_link_t * link);
+static void xirc2ps_release(dev_link_t * link);
+static int xirc2ps_event(event_t event, int priority,
+			 event_callback_args_t * args);
+
+/****************
+ * The attach() and detach() entry points are used to create and destroy
+ * "instances" of the driver, where each instance represents everything
+ * needed to manage one actual PCMCIA card.
+ */
+
+static dev_link_t *xirc2ps_attach(void);
+static void xirc2ps_detach(dev_link_t *);
+
+/****************
+ * You'll also need to prototype all the functions that will actually
+ * be used to talk to your device.  See 'pcmem_cs' for a good example
+ * of a fully self-sufficient driver; the other drivers rely more or
+ * less on other parts of the kernel.
+ */
+
+static irqreturn_t xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+/*
+ * The dev_info variable is the "key" that is used to match up this
+ * device driver with appropriate cards, through the card configuration
+ * database.
+ */
+
+static dev_info_t dev_info = "xirc2ps_cs";
+
+/****************
+ * A linked list of "instances" of the device.  Each actual
+ * PCMCIA card corresponds to one device instance, and is described
+ * by one dev_link_t structure (defined in ds.h).
+ *
+ * You may not want to use a linked list for this -- for example, the
+ * memory card driver uses an array of dev_link_t pointers, where minor
+ * device numbers are used to derive the corresponding array index.
+ */
+
+static dev_link_t *dev_list;
+
+/****************
+ * A dev_link_t structure has fields for most things that are needed
+ * to keep track of a socket, but there will usually be some device
+ * specific information that also needs to be kept track of.  The
+ * 'priv' pointer in a dev_link_t structure can be used to point to
+ * a device-specific private data structure, like this.
+ *
+ * A driver needs to provide a dev_node_t structure for each device
+ * on a card.  In some cases, there is only one device per card (for
+ * example, ethernet cards, modems).  In other cases, there may be
+ * many actual or logical devices (SCSI adapters, memory cards with
+ * multiple partitions).  The dev_node_t structures need to be kept
+ * in a linked list starting at the 'dev' field of a dev_link_t
+ * structure.  We allocate them in the card's private data structure,
+ * because they generally can't be allocated dynamically.
+ */
+
+typedef struct local_info_t {
+    dev_link_t link;
+    dev_node_t node;
+    struct net_device_stats stats;
+    int card_type;
+    int probe_port;
+    int silicon; /* silicon revision. 0=old CE2, 1=Scipper, 4=Mohawk */
+    int mohawk;  /* a CE3 type card */
+    int dingo;	 /* a CEM56 type card */
+    int new_mii; /* has full 10baseT/100baseT MII */
+    int modem;	 /* is a multi function card (i.e with a modem) */
+    void __iomem *dingo_ccr; /* only used for CEM56 cards */
+    unsigned last_ptr_value; /* last packets transmitted value */
+    const char *manf_str;
+} local_info_t;
+
+/****************
+ * Some more prototypes
+ */
+static int do_start_xmit(struct sk_buff *skb, struct net_device *dev);
+static void do_tx_timeout(struct net_device *dev);
+static struct net_device_stats *do_get_stats(struct net_device *dev);
+static void set_addresses(struct net_device *dev);
+static void set_multicast_list(struct net_device *dev);
+static int set_card_type(dev_link_t *link, const void *s);
+static int do_config(struct net_device *dev, struct ifmap *map);
+static int do_open(struct net_device *dev);
+static int do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static struct ethtool_ops netdev_ethtool_ops;
+static void hardreset(struct net_device *dev);
+static void do_reset(struct net_device *dev, int full);
+static int init_mii(struct net_device *dev);
+static void do_powerdown(struct net_device *dev);
+static int do_stop(struct net_device *dev);
+
+/*=============== Helper functions =========================*/
+static int
+first_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+{
+	int err;
+
+	if ((err = pcmcia_get_first_tuple(handle, tuple)) == 0 &&
+			(err = pcmcia_get_tuple_data(handle, tuple)) == 0)
+		err = pcmcia_parse_tuple(handle, tuple, parse);
+	return err;
+}
+
+static int
+next_tuple(client_handle_t handle, tuple_t *tuple, cisparse_t *parse)
+{
+	int err;
+
+	if ((err = pcmcia_get_next_tuple(handle, tuple)) == 0 &&
+			(err = pcmcia_get_tuple_data(handle, tuple)) == 0)
+		err = pcmcia_parse_tuple(handle, tuple, parse);
+	return err;
+}
+
+#define SelectPage(pgnr)   outb((pgnr), ioaddr + XIRCREG_PR)
+#define GetByte(reg)	   ((unsigned)inb(ioaddr + (reg)))
+#define GetWord(reg)	   ((unsigned)inw(ioaddr + (reg)))
+#define PutByte(reg,value) outb((value), ioaddr+(reg))
+#define PutWord(reg,value) outw((value), ioaddr+(reg))
+
+/*====== Functions used for debugging =================================*/
+#if defined(PCMCIA_DEBUG) && 0 /* reading regs may change system status */
+static void
+PrintRegisters(struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+
+    if (pc_debug > 1) {
+	int i, page;
+
+	printk(KDBG_XIRC "Register  common: ");
+	for (i = 0; i < 8; i++)
+	    printk(" %2.2x", GetByte(i));
+	printk("\n");
+	for (page = 0; page <= 8; page++) {
+	    printk(KDBG_XIRC "Register page %2x: ", page);
+	    SelectPage(page);
+	    for (i = 8; i < 16; i++)
+		printk(" %2.2x", GetByte(i));
+	    printk("\n");
+	}
+	for (page=0x40 ; page <= 0x5f; page++) {
+	    if (page == 0x43 || (page >= 0x46 && page <= 0x4f)
+		|| (page >= 0x51 && page <=0x5e))
+		continue;
+	    printk(KDBG_XIRC "Register page %2x: ", page);
+	    SelectPage(page);
+	    for (i = 8; i < 16; i++)
+		printk(" %2.2x", GetByte(i));
+	    printk("\n");
+	}
+    }
+}
+#endif /* PCMCIA_DEBUG */
+
+/*============== MII Management functions ===============*/
+
+/****************
+ * Turn around for read
+ */
+static void
+mii_idle(kio_addr_t ioaddr)
+{
+    PutByte(XIRCREG2_GPR2, 0x04|0); /* drive MDCK low */
+    udelay(1);
+    PutByte(XIRCREG2_GPR2, 0x04|1); /* and drive MDCK high */
+    udelay(1);
+}
+
+/****************
+ * Write a bit to MDI/O
+ */
+static void
+mii_putbit(kio_addr_t ioaddr, unsigned data)
+{
+  #if 1
+    if (data) {
+	PutByte(XIRCREG2_GPR2, 0x0c|2|0); /* set MDIO */
+	udelay(1);
+	PutByte(XIRCREG2_GPR2, 0x0c|2|1); /* and drive MDCK high */
+	udelay(1);
+    } else {
+	PutByte(XIRCREG2_GPR2, 0x0c|0|0); /* clear MDIO */
+	udelay(1);
+	PutByte(XIRCREG2_GPR2, 0x0c|0|1); /* and drive MDCK high */
+	udelay(1);
+    }
+  #else
+    if (data) {
+	PutWord(XIRCREG2_GPR2-1, 0x0e0e);
+	udelay(1);
+	PutWord(XIRCREG2_GPR2-1, 0x0f0f);
+	udelay(1);
+    } else {
+	PutWord(XIRCREG2_GPR2-1, 0x0c0c);
+	udelay(1);
+	PutWord(XIRCREG2_GPR2-1, 0x0d0d);
+	udelay(1);
+    }
+  #endif
+}
+
+/****************
+ * Get a bit from MDI/O
+ */
+static int
+mii_getbit(kio_addr_t ioaddr)
+{
+    unsigned d;
+
+    PutByte(XIRCREG2_GPR2, 4|0); /* drive MDCK low */
+    udelay(1);
+    d = GetByte(XIRCREG2_GPR2); /* read MDIO */
+    PutByte(XIRCREG2_GPR2, 4|1); /* drive MDCK high again */
+    udelay(1);
+    return d & 0x20; /* read MDIO */
+}
+
+static void
+mii_wbits(kio_addr_t ioaddr, unsigned data, int len)
+{
+    unsigned m = 1 << (len-1);
+    for (; m; m >>= 1)
+	mii_putbit(ioaddr, data & m);
+}
+
+static unsigned
+mii_rd(kio_addr_t ioaddr,	u_char phyaddr, u_char phyreg)
+{
+    int i;
+    unsigned data=0, m;
+
+    SelectPage(2);
+    for (i=0; i < 32; i++)		/* 32 bit preamble */
+	mii_putbit(ioaddr, 1);
+    mii_wbits(ioaddr, 0x06, 4); 	/* Start and opcode for read */
+    mii_wbits(ioaddr, phyaddr, 5);	/* PHY address to be accessed */
+    mii_wbits(ioaddr, phyreg, 5);	/* PHY register to read */
+    mii_idle(ioaddr);			/* turn around */
+    mii_getbit(ioaddr);
+
+    for (m = 1<<15; m; m >>= 1)
+	if (mii_getbit(ioaddr))
+	    data |= m;
+    mii_idle(ioaddr);
+    return data;
+}
+
+static void
+mii_wr(kio_addr_t ioaddr, u_char phyaddr, u_char phyreg, unsigned data, int len)
+{
+    int i;
+
+    SelectPage(2);
+    for (i=0; i < 32; i++)		/* 32 bit preamble */
+	mii_putbit(ioaddr, 1);
+    mii_wbits(ioaddr, 0x05, 4); 	/* Start and opcode for write */
+    mii_wbits(ioaddr, phyaddr, 5);	/* PHY address to be accessed */
+    mii_wbits(ioaddr, phyreg, 5);	/* PHY Register to write */
+    mii_putbit(ioaddr, 1);		/* turn around */
+    mii_putbit(ioaddr, 0);
+    mii_wbits(ioaddr, data, len);	/* And write the data */
+    mii_idle(ioaddr);
+}
+
+/*============= Main bulk of functions	=========================*/
+
+/****************
+ * xirc2ps_attach() creates an "instance" of the driver, allocating
+ * local data structures for one device.  The device is registered
+ * with Card Services.
+ *
+ * The dev_link structure is initialized, but we don't actually
+ * configure the card at this point -- we wait until we receive a
+ * card insertion event.
+ */
+
+static dev_link_t *
+xirc2ps_attach(void)
+{
+    client_reg_t client_reg;
+    dev_link_t *link;
+    struct net_device *dev;
+    local_info_t *local;
+    int err;
+
+    DEBUG(0, "attach()\n");
+
+    /* Allocate the device structure */
+    dev = alloc_etherdev(sizeof(local_info_t));
+    if (!dev)
+	    return NULL;
+    local = netdev_priv(dev);
+    link = &local->link;
+    link->priv = dev;
+
+    /* General socket configuration */
+    link->conf.Attributes = CONF_ENABLE_IRQ;
+    link->conf.Vcc = 50;
+    link->conf.IntType = INT_MEMORY_AND_IO;
+    link->conf.ConfigIndex = 1;
+    link->conf.Present = PRESENT_OPTION;
+    link->irq.Handler = xirc2ps_interrupt;
+    link->irq.Instance = dev;
+
+    /* Fill in card specific entries */
+    SET_MODULE_OWNER(dev);
+    dev->hard_start_xmit = &do_start_xmit;
+    dev->set_config = &do_config;
+    dev->get_stats = &do_get_stats;
+    dev->do_ioctl = &do_ioctl;
+    SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+    dev->set_multicast_list = &set_multicast_list;
+    dev->open = &do_open;
+    dev->stop = &do_stop;
+#ifdef HAVE_TX_TIMEOUT
+    dev->tx_timeout = do_tx_timeout;
+    dev->watchdog_timeo = TX_TIMEOUT;
+#endif
+
+    /* Register with Card Services */
+    link->next = dev_list;
+    dev_list = link;
+    client_reg.dev_info = &dev_info;
+    client_reg.EventMask =
+	CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL |
+	CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET |
+	CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME;
+    client_reg.event_handler = &xirc2ps_event;
+    client_reg.Version = 0x0210;
+    client_reg.event_callback_args.client_data = link;
+    if ((err = pcmcia_register_client(&link->handle, &client_reg))) {
+	cs_error(link->handle, RegisterClient, err);
+	xirc2ps_detach(link);
+	return NULL;
+    }
+
+    return link;
+} /* xirc2ps_attach */
+
+/****************
+ *  This deletes a driver "instance".  The device is de-registered
+ *  with Card Services.  If it has been released, all local data
+ *  structures are freed.  Otherwise, the structures will be freed
+ *  when the device is released.
+ */
+
+static void
+xirc2ps_detach(dev_link_t * link)
+{
+    struct net_device *dev = link->priv;
+    dev_link_t **linkp;
+
+    DEBUG(0, "detach(0x%p)\n", link);
+
+    /* Locate device structure */
+    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
+	if (*linkp == link)
+	    break;
+    if (!*linkp) {
+	DEBUG(0, "detach(0x%p): dev_link lost\n", link);
+	return;
+    }
+
+    if (link->dev)
+	unregister_netdev(dev);
+
+    /*
+     * If the device is currently configured and active, we won't
+     * actually delete it yet.	Instead, it is marked so that when
+     * the release() function is called, that will trigger a proper
+     * detach().
+     */
+    if (link->state & DEV_CONFIG)
+	xirc2ps_release(link);
+
+    /* Break the link with Card Services */
+    if (link->handle)
+	pcmcia_deregister_client(link->handle);
+
+    /* Unlink device structure, free it */
+    *linkp = link->next;
+    free_netdev(dev);
+} /* xirc2ps_detach */
+
+/****************
+ * Detect the type of the card. s is the buffer with the data of tuple 0x20
+ * Returns: 0 := not supported
+ *		       mediaid=11 and prodid=47
+ * Media-Id bits:
+ *  Ethernet	    0x01
+ *  Tokenring	    0x02
+ *  Arcnet	    0x04
+ *  Wireless	    0x08
+ *  Modem	    0x10
+ *  GSM only	    0x20
+ * Prod-Id bits:
+ *  Pocket	    0x10
+ *  External	    0x20
+ *  Creditcard	    0x40
+ *  Cardbus	    0x80
+ *
+ */
+static int
+set_card_type(dev_link_t *link, const void *s)
+{
+    struct net_device *dev = link->priv;
+    local_info_t *local = netdev_priv(dev);
+  #ifdef PCMCIA_DEBUG
+    unsigned cisrev = ((const unsigned char *)s)[2];
+  #endif
+    unsigned mediaid= ((const unsigned char *)s)[3];
+    unsigned prodid = ((const unsigned char *)s)[4];
+
+    DEBUG(0, "cisrev=%02x mediaid=%02x prodid=%02x\n",
+	  cisrev, mediaid, prodid);
+
+    local->mohawk = 0;
+    local->dingo = 0;
+    local->modem = 0;
+    local->card_type = XIR_UNKNOWN;
+    if (!(prodid & 0x40)) {
+	printk(KNOT_XIRC "Ooops: Not a creditcard\n");
+	return 0;
+    }
+    if (!(mediaid & 0x01)) {
+	printk(KNOT_XIRC "Not an Ethernet card\n");
+	return 0;
+    }
+    if (mediaid & 0x10) {
+	local->modem = 1;
+	switch(prodid & 15) {
+	  case 1: local->card_type = XIR_CEM   ; break;
+	  case 2: local->card_type = XIR_CEM2  ; break;
+	  case 3: local->card_type = XIR_CEM3  ; break;
+	  case 4: local->card_type = XIR_CEM33 ; break;
+	  case 5: local->card_type = XIR_CEM56M;
+		  local->mohawk = 1;
+		  break;
+	  case 6:
+	  case 7: /* 7 is the RealPort 10/56 */
+		  local->card_type = XIR_CEM56 ;
+		  local->mohawk = 1;
+		  local->dingo = 1;
+		  break;
+	}
+    } else {
+	switch(prodid & 15) {
+	  case 1: local->card_type = has_ce2_string(link)? XIR_CE2 : XIR_CE ;
+		  break;
+	  case 2: local->card_type = XIR_CE2; break;
+	  case 3: local->card_type = XIR_CE3;
+		  local->mohawk = 1;
+		  break;
+	}
+    }
+    if (local->card_type == XIR_CE || local->card_type == XIR_CEM) {
+	printk(KNOT_XIRC "Sorry, this is an old CE card\n");
+	return 0;
+    }
+    if (local->card_type == XIR_UNKNOWN)
+	printk(KNOT_XIRC "unknown card (mediaid=%02x prodid=%02x)\n",
+	       mediaid, prodid);
+
+    return 1;
+}
+
+/****************
+ * There are some CE2 cards out which claim to be a CE card.
+ * This function looks for a "CE2" in the 3rd version field.
+ * Returns: true if this is a CE2
+ */
+static int
+has_ce2_string(dev_link_t * link)
+{
+    client_handle_t handle = link->handle;
+    tuple_t tuple;
+    cisparse_t parse;
+    u_char buf[256];
+
+    tuple.Attributes = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = 254;
+    tuple.TupleOffset = 0;
+    tuple.DesiredTuple = CISTPL_VERS_1;
+    if (!first_tuple(handle, &tuple, &parse) && parse.version_1.ns > 2) {
+	if (strstr(parse.version_1.str + parse.version_1.ofs[2], "CE2"))
+	    return 1;
+    }
+    return 0;
+}
+
+/****************
+ * xirc2ps_config() is scheduled to run after a CARD_INSERTION event
+ * is received, to configure the PCMCIA socket, and to make the
+ * ethernet device available to the system.
+ */
+static void
+xirc2ps_config(dev_link_t * link)
+{
+    client_handle_t handle = link->handle;
+    struct net_device *dev = link->priv;
+    local_info_t *local = netdev_priv(dev);
+    tuple_t tuple;
+    cisparse_t parse;
+    kio_addr_t ioaddr;
+    int err, i;
+    u_char buf[64];
+    cistpl_lan_node_id_t *node_id = (cistpl_lan_node_id_t*)parse.funce.data;
+    cistpl_cftable_entry_t *cf = &parse.cftable_entry;
+
+    local->dingo_ccr = NULL;
+
+    DEBUG(0, "config(0x%p)\n", link);
+
+    /*
+     * This reads the card's CONFIG tuple to find its configuration
+     * registers.
+     */
+    tuple.Attributes = 0;
+    tuple.TupleData = buf;
+    tuple.TupleDataMax = 64;
+    tuple.TupleOffset = 0;
+
+    /* Is this a valid	card */
+    tuple.DesiredTuple = CISTPL_MANFID;
+    if ((err=first_tuple(handle, &tuple, &parse))) {
+	printk(KNOT_XIRC "manfid not found in CIS\n");
+	goto failure;
+    }
+
+    switch(parse.manfid.manf) {
+      case MANFID_XIRCOM:
+	local->manf_str = "Xircom";
+	break;
+      case MANFID_ACCTON:
+	local->manf_str = "Accton";
+	break;
+      case MANFID_COMPAQ:
+      case MANFID_COMPAQ2:
+	local->manf_str = "Compaq";
+	break;
+      case MANFID_INTEL:
+	local->manf_str = "Intel";
+	break;
+      case MANFID_TOSHIBA:
+	local->manf_str = "Toshiba";
+	break;
+      default:
+	printk(KNOT_XIRC "Unknown Card Manufacturer ID: 0x%04x\n",
+	       (unsigned)parse.manfid.manf);
+	goto failure;
+    }
+    DEBUG(0, "found %s card\n", local->manf_str);
+
+    if (!set_card_type(link, buf)) {
+	printk(KNOT_XIRC "this card is not supported\n");
+	goto failure;
+    }
+
+    /* get configuration stuff */
+    tuple.DesiredTuple = CISTPL_CONFIG;
+    if ((err=first_tuple(handle, &tuple, &parse)))
+	goto cis_error;
+    link->conf.ConfigBase = parse.config.base;
+    link->conf.Present =    parse.config.rmask[0];
+
+    /* get the ethernet address from the CIS */
+    tuple.DesiredTuple = CISTPL_FUNCE;
+    for (err = first_tuple(handle, &tuple, &parse); !err;
+			     err = next_tuple(handle, &tuple, &parse)) {
+	/* Once I saw two CISTPL_FUNCE_LAN_NODE_ID entries:
+	 * the first one with a length of zero the second correct -
+	 * so I skip all entries with length 0 */
+	if (parse.funce.type == CISTPL_FUNCE_LAN_NODE_ID
+	    && ((cistpl_lan_node_id_t *)parse.funce.data)->nb)
+	    break;
+    }
+    if (err) { /* not found: try to get the node-id from tuple 0x89 */
+	tuple.DesiredTuple = 0x89;  /* data layout looks like tuple 0x22 */
+	if ((err = pcmcia_get_first_tuple(handle, &tuple)) == 0 &&
+		(err = pcmcia_get_tuple_data(handle, &tuple)) == 0) {
+	    if (tuple.TupleDataLen == 8 && *buf == CISTPL_FUNCE_LAN_NODE_ID)
+		memcpy(&parse, buf, 8);
+	    else
+		err = -1;
+	}
+    }
+    if (err) { /* another try	(James Lehmer's CE2 version 4.1)*/
+	tuple.DesiredTuple = CISTPL_FUNCE;
+	for (err = first_tuple(handle, &tuple, &parse); !err;
+				 err = next_tuple(handle, &tuple, &parse)) {
+	    if (parse.funce.type == 0x02 && parse.funce.data[0] == 1
+		&& parse.funce.data[1] == 6 && tuple.TupleDataLen == 13) {
+		buf[1] = 4;
+		memcpy(&parse, buf+1, 8);
+		break;
+	    }
+	}
+    }
+    if (err) {
+	printk(KNOT_XIRC "node-id not found in CIS\n");
+	goto failure;
+    }
+    node_id = (cistpl_lan_node_id_t *)parse.funce.data;
+    if (node_id->nb != 6) {
+	printk(KNOT_XIRC "malformed node-id in CIS\n");
+	goto failure;
+    }
+    for (i=0; i < 6; i++)
+	dev->dev_addr[i] = node_id->id[i];
+
+    /* Configure card */
+    link->state |= DEV_CONFIG;
+
+    link->io.IOAddrLines =10;
+    link->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+    link->irq.Attributes = IRQ_HANDLE_PRESENT;
+    link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+    if (local->modem) {
+	int pass;
+
+	if (do_sound) {
+	    link->conf.Attributes |= CONF_ENABLE_SPKR;
+	    link->conf.Status |= CCSR_AUDIO_ENA;
+	}
+	link->irq.Attributes |= IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED ;
+	link->io.NumPorts2 = 8;
+	link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
+	if (local->dingo) {
+	    /* Take the Modem IO port from the CIS and scan for a free
+	     * Ethernet port */
+	    link->io.NumPorts1 = 16; /* no Mako stuff anymore */
+	    tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	    for (err = first_tuple(handle, &tuple, &parse); !err;
+				 err = next_tuple(handle, &tuple, &parse)) {
+		if (cf->io.nwin > 0  &&  (cf->io.win[0].base & 0xf) == 8) {
+		    for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) {
+			link->conf.ConfigIndex = cf->index ;
+			link->io.BasePort2 = cf->io.win[0].base;
+			link->io.BasePort1 = ioaddr;
+			if (!(err=pcmcia_request_io(link->handle, &link->io)))
+			    goto port_found;
+		    }
+		}
+	    }
+	} else {
+	    link->io.NumPorts1 = 18;
+	    /* We do 2 passes here: The first one uses the regular mapping and
+	     * the second tries again, thereby considering that the 32 ports are
+	     * mirrored every 32 bytes. Actually we use a mirrored port for
+	     * the Mako if (on the first pass) the COR bit 5 is set.
+	     */
+	    for (pass=0; pass < 2; pass++) {
+		tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+		for (err = first_tuple(handle, &tuple, &parse); !err;
+				     err = next_tuple(handle, &tuple, &parse)){
+		    if (cf->io.nwin > 0  &&  (cf->io.win[0].base & 0xf) == 8){
+			link->conf.ConfigIndex = cf->index ;
+			link->io.BasePort2 = cf->io.win[0].base;
+			link->io.BasePort1 = link->io.BasePort2
+				    + (pass ? (cf->index & 0x20 ? -24:8)
+					    : (cf->index & 0x20 ?   8:-24));
+			if (!(err=pcmcia_request_io(link->handle, &link->io)))
+			    goto port_found;
+		    }
+		}
+	    }
+	    /* if special option:
+	     * try to configure as Ethernet only.
+	     * .... */
+	}
+	printk(KNOT_XIRC "no ports available\n");
+    } else {
+	link->irq.Attributes |= IRQ_TYPE_EXCLUSIVE;
+	link->io.NumPorts1 = 16;
+	for (ioaddr = 0x300; ioaddr < 0x400; ioaddr += 0x10) {
+	    link->io.BasePort1 = ioaddr;
+	    if (!(err=pcmcia_request_io(link->handle, &link->io)))
+		goto port_found;
+	}
+	link->io.BasePort1 = 0; /* let CS decide */
+	if ((err=pcmcia_request_io(link->handle, &link->io))) {
+	    cs_error(link->handle, RequestIO, err);
+	    goto config_error;
+	}
+    }
+  port_found:
+    if (err)
+	 goto config_error;
+
+    /****************
+     * Now allocate an interrupt line.	Note that this does not
+     * actually assign a handler to the interrupt.
+     */
+    if ((err=pcmcia_request_irq(link->handle, &link->irq))) {
+	cs_error(link->handle, RequestIRQ, err);
+	goto config_error;
+    }
+
+    /****************
+     * This actually configures the PCMCIA socket -- setting up
+     * the I/O windows and the interrupt mapping.
+     */
+    if ((err=pcmcia_request_configuration(link->handle, &link->conf))) {
+	cs_error(link->handle, RequestConfiguration, err);
+	goto config_error;
+    }
+
+    if (local->dingo) {
+	conf_reg_t reg;
+	win_req_t req;
+	memreq_t mem;
+
+	/* Reset the modem's BAR to the correct value
+	 * This is necessary because in the RequestConfiguration call,
+	 * the base address of the ethernet port (BasePort1) is written
+	 * to the BAR registers of the modem.
+	 */
+	reg.Action = CS_WRITE;
+	reg.Offset = CISREG_IOBASE_0;
+	reg.Value = link->io.BasePort2 & 0xff;
+	if ((err = pcmcia_access_configuration_register(link->handle, &reg))) {
+	    cs_error(link->handle, AccessConfigurationRegister, err);
+	    goto config_error;
+	}
+	reg.Action = CS_WRITE;
+	reg.Offset = CISREG_IOBASE_1;
+	reg.Value = (link->io.BasePort2 >> 8) & 0xff;
+	if ((err = pcmcia_access_configuration_register(link->handle, &reg))) {
+	    cs_error(link->handle, AccessConfigurationRegister, err);
+	    goto config_error;
+	}
+
+	/* There is no config entry for the Ethernet part which
+	 * is at 0x0800. So we allocate a window into the attribute
+	 * memory and write direct to the CIS registers
+	 */
+	req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
+	req.Base = req.Size = 0;
+	req.AccessSpeed = 0;
+	if ((err = pcmcia_request_window(&link->handle, &req, &link->win))) {
+	    cs_error(link->handle, RequestWindow, err);
+	    goto config_error;
+	}
+	local->dingo_ccr = ioremap(req.Base,0x1000) + 0x0800;
+	mem.CardOffset = 0x0;
+	mem.Page = 0;
+	if ((err = pcmcia_map_mem_page(link->win, &mem))) {
+	    cs_error(link->handle, MapMemPage, err);
+	    goto config_error;
+	}
+
+	/* Setup the CCRs; there are no infos in the CIS about the Ethernet
+	 * part.
+	 */
+	writeb(0x47, local->dingo_ccr + CISREG_COR);
+	ioaddr = link->io.BasePort1;
+	writeb(ioaddr & 0xff	  , local->dingo_ccr + CISREG_IOBASE_0);
+	writeb((ioaddr >> 8)&0xff , local->dingo_ccr + CISREG_IOBASE_1);
+
+      #if 0
+	{
+	    u_char tmp;
+	    printk(KERN_INFO "ECOR:");
+	    for (i=0; i < 7; i++) {
+		tmp = readb(local->dingo_ccr + i*2);
+		printk(" %02x", tmp);
+	    }
+	    printk("\n");
+	    printk(KERN_INFO "DCOR:");
+	    for (i=0; i < 4; i++) {
+		tmp = readb(local->dingo_ccr + 0x20 + i*2);
+		printk(" %02x", tmp);
+	    }
+	    printk("\n");
+	    printk(KERN_INFO "SCOR:");
+	    for (i=0; i < 10; i++) {
+		tmp = readb(local->dingo_ccr + 0x40 + i*2);
+		printk(" %02x", tmp);
+	    }
+	    printk("\n");
+	}
+      #endif
+
+	writeb(0x01, local->dingo_ccr + 0x20);
+	writeb(0x0c, local->dingo_ccr + 0x22);
+	writeb(0x00, local->dingo_ccr + 0x24);
+	writeb(0x00, local->dingo_ccr + 0x26);
+	writeb(0x00, local->dingo_ccr + 0x28);
+    }
+
+    /* The if_port symbol can be set when the module is loaded */
+    local->probe_port=0;
+    if (!if_port) {
+	local->probe_port = dev->if_port = 1;
+    } else if ((if_port >= 1 && if_port <= 2) ||
+	       (local->mohawk && if_port==4))
+	dev->if_port = if_port;
+    else
+	printk(KNOT_XIRC "invalid if_port requested\n");
+
+    /* we can now register the device with the net subsystem */
+    dev->irq = link->irq.AssignedIRQ;
+    dev->base_addr = link->io.BasePort1;
+
+    if (local->dingo)
+	do_reset(dev, 1); /* a kludge to make the cem56 work */
+
+    link->dev = &local->node;
+    link->state &= ~DEV_CONFIG_PENDING;
+    SET_NETDEV_DEV(dev, &handle_to_dev(handle));
+
+    if ((err=register_netdev(dev))) {
+	printk(KNOT_XIRC "register_netdev() failed\n");
+	link->dev = NULL;
+	goto config_error;
+    }
+
+    strcpy(local->node.dev_name, dev->name);
+
+    /* give some infos about the hardware */
+    printk(KERN_INFO "%s: %s: port %#3lx, irq %d, hwaddr",
+	 dev->name, local->manf_str,(u_long)dev->base_addr, (int)dev->irq);
+    for (i = 0; i < 6; i++)
+	printk("%c%02X", i?':':' ', dev->dev_addr[i]);
+    printk("\n");
+
+    return;
+
+  config_error:
+    link->state &= ~DEV_CONFIG_PENDING;
+    xirc2ps_release(link);
+    return;
+
+  cis_error:
+    printk(KNOT_XIRC "unable to parse CIS\n");
+  failure:
+    link->state &= ~DEV_CONFIG_PENDING;
+} /* xirc2ps_config */
+
+/****************
+ * After a card is removed, xirc2ps_release() will unregister the net
+ * device, and release the PCMCIA configuration.  If the device is
+ * still open, this will be postponed until it is closed.
+ */
+static void
+xirc2ps_release(dev_link_t *link)
+{
+
+    DEBUG(0, "release(0x%p)\n", link);
+
+    if (link->win) {
+	struct net_device *dev = link->priv;
+	local_info_t *local = netdev_priv(dev);
+	if (local->dingo)
+	    iounmap(local->dingo_ccr - 0x0800);
+	pcmcia_release_window(link->win);
+    }
+    pcmcia_release_configuration(link->handle);
+    pcmcia_release_io(link->handle, &link->io);
+    pcmcia_release_irq(link->handle, &link->irq);
+    link->state &= ~DEV_CONFIG;
+
+} /* xirc2ps_release */
+
+/*====================================================================*/
+
+/****************
+ * The card status event handler.  Mostly, this schedules other
+ * stuff to run after an event is received.  A CARD_REMOVAL event
+ * also sets some flags to discourage the net drivers from trying
+ * to talk to the card any more.
+ *
+ * When a CARD_REMOVAL event is received, we immediately set a flag
+ * to block future accesses to this device.  All the functions that
+ * actually access the device should check this flag to make sure
+ * the card is still present.
+ */
+
+static int
+xirc2ps_event(event_t event, int priority,
+	      event_callback_args_t * args)
+{
+    dev_link_t *link = args->client_data;
+    struct net_device *dev = link->priv;
+
+    DEBUG(0, "event(%d)\n", (int)event);
+
+    switch (event) {
+    case CS_EVENT_REGISTRATION_COMPLETE:
+	DEBUG(0, "registration complete\n");
+	break;
+    case CS_EVENT_CARD_REMOVAL:
+	link->state &= ~DEV_PRESENT;
+	if (link->state & DEV_CONFIG)
+	    netif_device_detach(dev);
+	break;
+    case CS_EVENT_CARD_INSERTION:
+	link->state |= DEV_PRESENT | DEV_CONFIG_PENDING;
+	xirc2ps_config(link);
+	break;
+    case CS_EVENT_PM_SUSPEND:
+	link->state |= DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_RESET_PHYSICAL:
+	if (link->state & DEV_CONFIG) {
+	    if (link->open) {
+		netif_device_detach(dev);
+		do_powerdown(dev);
+	    }
+	    pcmcia_release_configuration(link->handle);
+	}
+	break;
+    case CS_EVENT_PM_RESUME:
+	link->state &= ~DEV_SUSPEND;
+	/* Fall through... */
+    case CS_EVENT_CARD_RESET:
+	if (link->state & DEV_CONFIG) {
+	    pcmcia_request_configuration(link->handle, &link->conf);
+	    if (link->open) {
+		do_reset(dev,1);
+		netif_device_attach(dev);
+	    }
+	}
+	break;
+    }
+    return 0;
+} /* xirc2ps_event */
+
+/*====================================================================*/
+
+/****************
+ * This is the Interrupt service route.
+ */
+static irqreturn_t
+xirc2ps_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+    struct net_device *dev = (struct net_device *)dev_id;
+    local_info_t *lp = netdev_priv(dev);
+    kio_addr_t ioaddr;
+    u_char saved_page;
+    unsigned bytes_rcvd;
+    unsigned int_status, eth_status, rx_status, tx_status;
+    unsigned rsr, pktlen;
+    ulong start_ticks = jiffies; /* fixme: jiffies rollover every 497 days
+				  * is this something to worry about?
+				  * -- on a laptop?
+				  */
+
+    if (!netif_device_present(dev))
+	return IRQ_HANDLED;
+
+    ioaddr = dev->base_addr;
+    if (lp->mohawk) { /* must disable the interrupt */
+	PutByte(XIRCREG_CR, 0);
+    }
+
+    DEBUG(6, "%s: interrupt %d at %#x.\n", dev->name, irq, ioaddr);
+
+    saved_page = GetByte(XIRCREG_PR);
+    /* Read the ISR to see whats the cause for the interrupt.
+     * This also clears the interrupt flags on CE2 cards
+     */
+    int_status = GetByte(XIRCREG_ISR);
+    bytes_rcvd = 0;
+  loop_entry:
+    if (int_status == 0xff) { /* card may be ejected */
+	DEBUG(3, "%s: interrupt %d for dead card\n", dev->name, irq);
+	goto leave;
+    }
+    eth_status = GetByte(XIRCREG_ESR);
+
+    SelectPage(0x40);
+    rx_status  = GetByte(XIRCREG40_RXST0);
+    PutByte(XIRCREG40_RXST0, (~rx_status & 0xff));
+    tx_status = GetByte(XIRCREG40_TXST0);
+    tx_status |= GetByte(XIRCREG40_TXST1) << 8;
+    PutByte(XIRCREG40_TXST0, 0);
+    PutByte(XIRCREG40_TXST1, 0);
+
+    DEBUG(3, "%s: ISR=%#2.2x ESR=%#2.2x RSR=%#2.2x TSR=%#4.4x\n",
+	  dev->name, int_status, eth_status, rx_status, tx_status);
+
+    /***** receive section ******/
+    SelectPage(0);
+    while (eth_status & FullPktRcvd) {
+	rsr = GetByte(XIRCREG0_RSR);
+	if (bytes_rcvd > maxrx_bytes && (rsr & PktRxOk)) {
+	    /* too many bytes received during this int, drop the rest of the
+	     * packets */
+	    lp->stats.rx_dropped++;
+	    DEBUG(2, "%s: RX drop, too much done\n", dev->name);
+	} else if (rsr & PktRxOk) {
+	    struct sk_buff *skb;
+
+	    pktlen = GetWord(XIRCREG0_RBC);
+	    bytes_rcvd += pktlen;
+
+	    DEBUG(5, "rsr=%#02x packet_length=%u\n", rsr, pktlen);
+
+	    skb = dev_alloc_skb(pktlen+3); /* 1 extra so we can use insw */
+	    if (!skb) {
+		printk(KNOT_XIRC "low memory, packet dropped (size=%u)\n",
+		       pktlen);
+		lp->stats.rx_dropped++;
+	    } else { /* okay get the packet */
+		skb_reserve(skb, 2);
+		if (lp->silicon == 0 ) { /* work around a hardware bug */
+		    unsigned rhsa; /* receive start address */
+
+		    SelectPage(5);
+		    rhsa = GetWord(XIRCREG5_RHSA0);
+		    SelectPage(0);
+		    rhsa += 3; /* skip control infos */
+		    if (rhsa >= 0x8000)
+			rhsa = 0;
+		    if (rhsa + pktlen > 0x8000) {
+			unsigned i;
+			u_char *buf = skb_put(skb, pktlen);
+			for (i=0; i < pktlen ; i++, rhsa++) {
+			    buf[i] = GetByte(XIRCREG_EDP);
+			    if (rhsa == 0x8000) {
+				rhsa = 0;
+				i--;
+			    }
+			}
+		    } else {
+			insw(ioaddr+XIRCREG_EDP,
+				skb_put(skb, pktlen), (pktlen+1)>>1);
+		    }
+		}
+	      #if 0
+		else if (lp->mohawk) {
+		    /* To use this 32 bit access we should use
+		     * a manual optimized loop
+		     * Also the words are swapped, we can get more
+		     * performance by using 32 bit access and swapping
+		     * the words in a register. Will need this for cardbus
+		     *
+		     * Note: don't forget to change the ALLOC_SKB to .. +3
+		     */
+		    unsigned i;
+		    u_long *p = skb_put(skb, pktlen);
+		    register u_long a;
+		    kio_addr_t edpreg = ioaddr+XIRCREG_EDP-2;
+		    for (i=0; i < len ; i += 4, p++) {
+			a = inl(edpreg);
+			__asm__("rorl $16,%0\n\t"
+				:"=q" (a)
+				: "0" (a));
+			*p = a;
+		    }
+		}
+	      #endif
+		else {
+		    insw(ioaddr+XIRCREG_EDP, skb_put(skb, pktlen),
+			    (pktlen+1)>>1);
+		}
+		skb->protocol = eth_type_trans(skb, dev);
+		skb->dev = dev;
+		netif_rx(skb);
+		dev->last_rx = jiffies;
+		lp->stats.rx_packets++;
+		lp->stats.rx_bytes += pktlen;
+		if (!(rsr & PhyPkt))
+		    lp->stats.multicast++;
+	    }
+	} else { /* bad packet */
+	    DEBUG(5, "rsr=%#02x\n", rsr);
+	}
+	if (rsr & PktTooLong) {
+	    lp->stats.rx_frame_errors++;
+	    DEBUG(3, "%s: Packet too long\n", dev->name);
+	}
+	if (rsr & CRCErr) {
+	    lp->stats.rx_crc_errors++;
+	    DEBUG(3, "%s: CRC error\n", dev->name);
+	}
+	if (rsr & AlignErr) {
+	    lp->stats.rx_fifo_errors++; /* okay ? */
+	    DEBUG(3, "%s: Alignment error\n", dev->name);
+	}
+
+	/* clear the received/dropped/error packet */
+	PutWord(XIRCREG0_DO, 0x8000); /* issue cmd: skip_rx_packet */
+
+	/* get the new ethernet status */
+	eth_status = GetByte(XIRCREG_ESR);
+    }
+    if (rx_status & 0x10) { /* Receive overrun */
+	lp->stats.rx_over_errors++;
+	PutByte(XIRCREG_CR, ClearRxOvrun);
+	DEBUG(3, "receive overrun cleared\n");
+    }
+
+    /***** transmit section ******/
+    if (int_status & PktTxed) {
+	unsigned n, nn;
+
+	n = lp->last_ptr_value;
+	nn = GetByte(XIRCREG0_PTR);
+	lp->last_ptr_value = nn;
+	if (nn < n) /* rollover */
+	    lp->stats.tx_packets += 256 - n;
+	else if (n == nn) { /* happens sometimes - don't know why */
+	    DEBUG(0, "PTR not changed?\n");
+	} else
+	    lp->stats.tx_packets += lp->last_ptr_value - n;
+	netif_wake_queue(dev);
+    }
+    if (tx_status & 0x0002) {	/* Execessive collissions */
+	DEBUG(0, "tx restarted due to execssive collissions\n");
+	PutByte(XIRCREG_CR, RestartTx);  /* restart transmitter process */
+    }
+    if (tx_status & 0x0040)
+	lp->stats.tx_aborted_errors++;
+
+    /* recalculate our work chunk so that we limit the duration of this
+     * ISR to about 1/10 of a second.
+     * Calculate only if we received a reasonable amount of bytes.
+     */
+    if (bytes_rcvd > 1000) {
+	u_long duration = jiffies - start_ticks;
+
+	if (duration >= HZ/10) { /* if more than about 1/10 second */
+	    maxrx_bytes = (bytes_rcvd * (HZ/10)) / duration;
+	    if (maxrx_bytes < 2000)
+		maxrx_bytes = 2000;
+	    else if (maxrx_bytes > 22000)
+		maxrx_bytes = 22000;
+	    DEBUG(1, "set maxrx=%u (rcvd=%u ticks=%lu)\n",
+		  maxrx_bytes, bytes_rcvd, duration);
+	} else if (!duration && maxrx_bytes < 22000) {
+	    /* now much faster */
+	    maxrx_bytes += 2000;
+	    if (maxrx_bytes > 22000)
+		maxrx_bytes = 22000;
+	    DEBUG(1, "set maxrx=%u\n", maxrx_bytes);
+	}
+    }
+
+  leave:
+    if (lockup_hack) {
+	if (int_status != 0xff && (int_status = GetByte(XIRCREG_ISR)) != 0)
+	    goto loop_entry;
+    }
+    SelectPage(saved_page);
+    PutByte(XIRCREG_CR, EnableIntr);  /* re-enable interrupts */
+    /* Instead of dropping packets during a receive, we could
+     * force an interrupt with this command:
+     *	  PutByte(XIRCREG_CR, EnableIntr|ForceIntr);
+     */
+    return IRQ_HANDLED;
+} /* xirc2ps_interrupt */
+
+/*====================================================================*/
+
+static void
+do_tx_timeout(struct net_device *dev)
+{
+    local_info_t *lp = netdev_priv(dev);
+    printk(KERN_NOTICE "%s: transmit timed out\n", dev->name);
+    lp->stats.tx_errors++;
+    /* reset the card */
+    do_reset(dev,1);
+    dev->trans_start = jiffies;
+    netif_wake_queue(dev);
+}
+
+static int
+do_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+    local_info_t *lp = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    int okay;
+    unsigned freespace;
+    unsigned pktlen = skb? skb->len : 0;
+
+    DEBUG(1, "do_start_xmit(skb=%p, dev=%p) len=%u\n",
+	  skb, dev, pktlen);
+
+
+    /* adjust the packet length to min. required
+     * and hope that the buffer is large enough
+     * to provide some random data.
+     * fixme: For Mohawk we can change this by sending
+     * a larger packetlen than we actually have; the chip will
+     * pad this in his buffer with random bytes
+     */
+    if (pktlen < ETH_ZLEN)
+    {
+        skb = skb_padto(skb, ETH_ZLEN);
+        if (skb == NULL)
+        	return 0;
+	pktlen = ETH_ZLEN;
+    }
+
+    netif_stop_queue(dev);
+    SelectPage(0);
+    PutWord(XIRCREG0_TRS, (u_short)pktlen+2);
+    freespace = GetWord(XIRCREG0_TSO);
+    okay = freespace & 0x8000;
+    freespace &= 0x7fff;
+    /* TRS doesn't work - (indeed it is eliminated with sil-rev 1) */
+    okay = pktlen +2 < freespace;
+    DEBUG(2 + (okay ? 2 : 0), "%s: avail. tx space=%u%s\n",
+	  dev->name, freespace, okay ? " (okay)":" (not enough)");
+    if (!okay) { /* not enough space */
+	return 1;  /* upper layer may decide to requeue this packet */
+    }
+    /* send the packet */
+    PutWord(XIRCREG_EDP, (u_short)pktlen);
+    outsw(ioaddr+XIRCREG_EDP, skb->data, pktlen>>1);
+    if (pktlen & 1)
+	PutByte(XIRCREG_EDP, skb->data[pktlen-1]);
+
+    if (lp->mohawk)
+	PutByte(XIRCREG_CR, TransmitPacket|EnableIntr);
+
+    dev_kfree_skb (skb);
+    dev->trans_start = jiffies;
+    lp->stats.tx_bytes += pktlen;
+    netif_start_queue(dev);
+    return 0;
+}
+
+static struct net_device_stats *
+do_get_stats(struct net_device *dev)
+{
+    local_info_t *lp = netdev_priv(dev);
+
+    /*	lp->stats.rx_missed_errors = GetByte(?) */
+    return &lp->stats;
+}
+
+/****************
+ * Set all addresses: This first one is the individual address,
+ * the next 9 addresses are taken from the multicast list and
+ * the rest is filled with the individual address.
+ */
+static void
+set_addresses(struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+    local_info_t *lp = netdev_priv(dev);
+    struct dev_mc_list *dmi = dev->mc_list;
+    char *addr;
+    int i,j,k,n;
+
+    SelectPage(k=0x50);
+    for (i=0,j=8,n=0; ; i++, j++) {
+	if (i > 5) {
+	    if (++n > 9)
+		break;
+	    i = 0;
+	}
+	if (j > 15) {
+	    j = 8;
+	    k++;
+	    SelectPage(k);
+	}
+
+	if (n && n <= dev->mc_count && dmi) {
+	    addr = dmi->dmi_addr;
+	    dmi = dmi->next;
+	} else
+	    addr = dev->dev_addr;
+
+	if (lp->mohawk)
+	    PutByte(j, addr[5-i]);
+	else
+	    PutByte(j, addr[i]);
+    }
+    SelectPage(0);
+}
+
+/****************
+ * Set or clear the multicast filter for this adaptor.
+ * We can filter up to 9 addresses, if more are requested we set
+ * multicast promiscuous mode.
+ */
+
+static void
+set_multicast_list(struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+
+    SelectPage(0x42);
+    if (dev->flags & IFF_PROMISC) { /* snoop */
+	PutByte(XIRCREG42_SWC1, 0x06); /* set MPE and PME */
+    } else if (dev->mc_count > 9 || (dev->flags & IFF_ALLMULTI)) {
+	PutByte(XIRCREG42_SWC1, 0x06); /* set MPE */
+    } else if (dev->mc_count) {
+	/* the chip can filter 9 addresses perfectly */
+	PutByte(XIRCREG42_SWC1, 0x00);
+	SelectPage(0x40);
+	PutByte(XIRCREG40_CMD0, Offline);
+	set_addresses(dev);
+	SelectPage(0x40);
+	PutByte(XIRCREG40_CMD0, EnableRecv | Online);
+    } else { /* standard usage */
+	PutByte(XIRCREG42_SWC1, 0x00);
+    }
+    SelectPage(0);
+}
+
+static int
+do_config(struct net_device *dev, struct ifmap *map)
+{
+    local_info_t *local = netdev_priv(dev);
+
+    DEBUG(0, "do_config(%p)\n", dev);
+    if (map->port != 255 && map->port != dev->if_port) {
+	if (map->port > 4)
+	    return -EINVAL;
+	if (!map->port) {
+	    local->probe_port = 1;
+	    dev->if_port = 1;
+	} else {
+	    local->probe_port = 0;
+	    dev->if_port = map->port;
+	}
+	printk(KERN_INFO "%s: switching to %s port\n",
+	       dev->name, if_names[dev->if_port]);
+	do_reset(dev,1);  /* not the fine way :-) */
+    }
+    return 0;
+}
+
+/****************
+ * Open the driver
+ */
+static int
+do_open(struct net_device *dev)
+{
+    local_info_t *lp = netdev_priv(dev);
+    dev_link_t *link = &lp->link;
+
+    DEBUG(0, "do_open(%p)\n", dev);
+
+    /* Check that the PCMCIA card is still here. */
+    /* Physical device present signature. */
+    if (!DEV_OK(link))
+	return -ENODEV;
+
+    /* okay */
+    link->open++;
+
+    netif_start_queue(dev);
+    do_reset(dev,1);
+
+    return 0;
+}
+
+static void netdev_get_drvinfo(struct net_device *dev,
+			       struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, "xirc2ps_cs");
+	sprintf(info->bus_info, "PCMCIA 0x%lx", dev->base_addr);
+}
+
+static struct ethtool_ops netdev_ethtool_ops = {
+	.get_drvinfo		= netdev_get_drvinfo,
+};
+
+static int
+do_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+    local_info_t *local = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    u16 *data = (u16 *)&rq->ifr_ifru;
+
+    DEBUG(1, "%s: ioctl(%-.6s, %#04x) %04x %04x %04x %04x\n",
+	  dev->name, rq->ifr_ifrn.ifrn_name, cmd,
+	  data[0], data[1], data[2], data[3]);
+
+    if (!local->mohawk)
+	return -EOPNOTSUPP;
+
+    switch(cmd) {
+      case SIOCGMIIPHY:		/* Get the address of the PHY in use. */
+	data[0] = 0;		/* we have only this address */
+	/* fall trough */
+      case SIOCGMIIREG:		/* Read the specified MII register. */
+	data[3] = mii_rd(ioaddr, data[0] & 0x1f, data[1] & 0x1f);
+	break;
+      case SIOCSMIIREG:		/* Write the specified MII register */
+	if (!capable(CAP_NET_ADMIN))
+	    return -EPERM;
+	mii_wr(ioaddr, data[0] & 0x1f, data[1] & 0x1f, data[2], 16);
+	break;
+      default:
+	return -EOPNOTSUPP;
+    }
+    return 0;
+}
+
+static void
+hardreset(struct net_device *dev)
+{
+    local_info_t *local = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+
+    SelectPage(4);
+    udelay(1);
+    PutByte(XIRCREG4_GPR1, 0);	     /* clear bit 0: power down */
+    msleep(40);				     /* wait 40 msec */
+    if (local->mohawk)
+	PutByte(XIRCREG4_GPR1, 1);	 /* set bit 0: power up */
+    else
+	PutByte(XIRCREG4_GPR1, 1 | 4);	 /* set bit 0: power up, bit 2: AIC */
+    msleep(20);			     /* wait 20 msec */
+}
+
+static void
+do_reset(struct net_device *dev, int full)
+{
+    local_info_t *local = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    unsigned value;
+
+    DEBUG(0, "%s: do_reset(%p,%d)\n", dev? dev->name:"eth?", dev, full);
+
+    hardreset(dev);
+    PutByte(XIRCREG_CR, SoftReset); /* set */
+    msleep(20);			     /* wait 20 msec */
+    PutByte(XIRCREG_CR, 0);	     /* clear */
+    msleep(40);			     /* wait 40 msec */
+    if (local->mohawk) {
+	SelectPage(4);
+	/* set pin GP1 and GP2 to output  (0x0c)
+	 * set GP1 to low to power up the ML6692 (0x00)
+	 * set GP2 to high to power up the 10Mhz chip  (0x02)
+	 */
+	PutByte(XIRCREG4_GPR0, 0x0e);
+    }
+
+    /* give the circuits some time to power up */
+    msleep(500);			/* about 500ms */
+
+    local->last_ptr_value = 0;
+    local->silicon = local->mohawk ? (GetByte(XIRCREG4_BOV) & 0x70) >> 4
+				   : (GetByte(XIRCREG4_BOV) & 0x30) >> 4;
+
+    if (local->probe_port) {
+	if (!local->mohawk) {
+	    SelectPage(4);
+	    PutByte(XIRCREG4_GPR0, 4);
+	    local->probe_port = 0;
+	}
+    } else if (dev->if_port == 2) { /* enable 10Base2 */
+	SelectPage(0x42);
+	PutByte(XIRCREG42_SWC1, 0xC0);
+    } else { /* enable 10BaseT */
+	SelectPage(0x42);
+	PutByte(XIRCREG42_SWC1, 0x80);
+    }
+    msleep(40);			     /* wait 40 msec to let it complete */
+
+  #ifdef PCMCIA_DEBUG
+    if (pc_debug) {
+	SelectPage(0);
+	value = GetByte(XIRCREG_ESR);	 /* read the ESR */
+	printk(KERN_DEBUG "%s: ESR is: %#02x\n", dev->name, value);
+    }
+  #endif
+
+    /* setup the ECR */
+    SelectPage(1);
+    PutByte(XIRCREG1_IMR0, 0xff); /* allow all ints */
+    PutByte(XIRCREG1_IMR1, 1	); /* and Set TxUnderrunDetect */
+    value = GetByte(XIRCREG1_ECR);
+  #if 0
+    if (local->mohawk)
+	value |= DisableLinkPulse;
+    PutByte(XIRCREG1_ECR, value);
+  #endif
+    DEBUG(0, "%s: ECR is: %#02x\n", dev->name, value);
+
+    SelectPage(0x42);
+    PutByte(XIRCREG42_SWC0, 0x20); /* disable source insertion */
+
+    if (local->silicon != 1) {
+	/* set the local memory dividing line.
+	 * The comments in the sample code say that this is only
+	 * settable with the scipper version 2 which is revision 0.
+	 * Always for CE3 cards
+	 */
+	SelectPage(2);
+	PutWord(XIRCREG2_RBS, 0x2000);
+    }
+
+    if (full)
+	set_addresses(dev);
+
+    /* Hardware workaround:
+     * The receive byte pointer after reset is off by 1 so we need
+     * to move the offset pointer back to 0.
+     */
+    SelectPage(0);
+    PutWord(XIRCREG0_DO, 0x2000); /* change offset command, off=0 */
+
+    /* setup MAC IMRs and clear status registers */
+    SelectPage(0x40);		     /* Bit 7 ... bit 0 */
+    PutByte(XIRCREG40_RMASK0, 0xff); /* ROK, RAB, rsv, RO, CRC, AE, PTL, MP */
+    PutByte(XIRCREG40_TMASK0, 0xff); /* TOK, TAB, SQE, LL, TU, JAB, EXC, CRS */
+    PutByte(XIRCREG40_TMASK1, 0xb0); /* rsv, rsv, PTD, EXT, rsv,rsv,rsv, rsv*/
+    PutByte(XIRCREG40_RXST0,  0x00); /* ROK, RAB, REN, RO, CRC, AE, PTL, MP */
+    PutByte(XIRCREG40_TXST0,  0x00); /* TOK, TAB, SQE, LL, TU, JAB, EXC, CRS */
+    PutByte(XIRCREG40_TXST1,  0x00); /* TEN, rsv, PTD, EXT, retry_counter:4  */
+
+    if (full && local->mohawk && init_mii(dev)) {
+	if (dev->if_port == 4 || local->dingo || local->new_mii) {
+	    printk(KERN_INFO "%s: MII selected\n", dev->name);
+	    SelectPage(2);
+	    PutByte(XIRCREG2_MSR, GetByte(XIRCREG2_MSR) | 0x08);
+	    msleep(20);
+	} else {
+	    printk(KERN_INFO "%s: MII detected; using 10mbs\n",
+		   dev->name);
+	    SelectPage(0x42);
+	    if (dev->if_port == 2) /* enable 10Base2 */
+		PutByte(XIRCREG42_SWC1, 0xC0);
+	    else  /* enable 10BaseT */
+		PutByte(XIRCREG42_SWC1, 0x80);
+	    msleep(40);			/* wait 40 msec to let it complete */
+	}
+	if (full_duplex)
+	    PutByte(XIRCREG1_ECR, GetByte(XIRCREG1_ECR | FullDuplex));
+    } else {  /* No MII */
+	SelectPage(0);
+	value = GetByte(XIRCREG_ESR);	 /* read the ESR */
+	dev->if_port = (value & MediaSelect) ? 1 : 2;
+    }
+
+    /* configure the LEDs */
+    SelectPage(2);
+    if (dev->if_port == 1 || dev->if_port == 4) /* TP: Link and Activity */
+	PutByte(XIRCREG2_LED, 0x3b);
+    else			      /* Coax: Not-Collision and Activity */
+	PutByte(XIRCREG2_LED, 0x3a);
+
+    if (local->dingo)
+	PutByte(0x0b, 0x04); /* 100 Mbit LED */
+
+    /* enable receiver and put the mac online */
+    if (full) {
+	SelectPage(0x40);
+	PutByte(XIRCREG40_CMD0, EnableRecv | Online);
+    }
+
+    /* setup Ethernet IMR and enable interrupts */
+    SelectPage(1);
+    PutByte(XIRCREG1_IMR0, 0xff);
+    udelay(1);
+    SelectPage(0);
+    PutByte(XIRCREG_CR, EnableIntr);
+    if (local->modem && !local->dingo) { /* do some magic */
+	if (!(GetByte(0x10) & 0x01))
+	    PutByte(0x10, 0x11); /* unmask master-int bit */
+    }
+
+    if (full)
+	printk(KERN_INFO "%s: media %s, silicon revision %d\n",
+	       dev->name, if_names[dev->if_port], local->silicon);
+    /* We should switch back to page 0 to avoid a bug in revision 0
+     * where regs with offset below 8 can't be read after an access
+     * to the MAC registers */
+    SelectPage(0);
+}
+
+/****************
+ * Initialize the Media-Independent-Interface
+ * Returns: True if we have a good MII
+ */
+static int
+init_mii(struct net_device *dev)
+{
+    local_info_t *local = netdev_priv(dev);
+    kio_addr_t ioaddr = dev->base_addr;
+    unsigned control, status, linkpartner;
+    int i;
+
+    if (if_port == 4 || if_port == 1) { /* force 100BaseT or 10BaseT */
+	dev->if_port = if_port;
+	local->probe_port = 0;
+	return 1;
+    }
+
+    status = mii_rd(ioaddr,  0, 1);
+    if ((status & 0xff00) != 0x7800)
+	return 0; /* No MII */
+
+    local->new_mii = (mii_rd(ioaddr, 0, 2) != 0xffff);
+    
+    if (local->probe_port)
+	control = 0x1000; /* auto neg */
+    else if (dev->if_port == 4)
+	control = 0x2000; /* no auto neg, 100mbs mode */
+    else
+	control = 0x0000; /* no auto neg, 10mbs mode */
+    mii_wr(ioaddr,  0, 0, control, 16);
+    udelay(100);
+    control = mii_rd(ioaddr, 0, 0);
+
+    if (control & 0x0400) {
+	printk(KERN_NOTICE "%s can't take PHY out of isolation mode\n",
+	       dev->name);
+	local->probe_port = 0;
+	return 0;
+    }
+
+    if (local->probe_port) {
+	/* according to the DP83840A specs the auto negotiation process
+	 * may take up to 3.5 sec, so we use this also for our ML6692
+	 * Fixme: Better to use a timer here!
+	 */
+	for (i=0; i < 35; i++) {
+	    msleep(100);	 /* wait 100 msec */
+	    status = mii_rd(ioaddr,  0, 1);
+	    if ((status & 0x0020) && (status & 0x0004))
+		break;
+	}
+
+	if (!(status & 0x0020)) {
+	    printk(KERN_INFO "%s: autonegotiation failed;"
+		   " using 10mbs\n", dev->name);
+	    if (!local->new_mii) {
+		control = 0x0000;
+		mii_wr(ioaddr,  0, 0, control, 16);
+		udelay(100);
+		SelectPage(0);
+		dev->if_port = (GetByte(XIRCREG_ESR) & MediaSelect) ? 1 : 2;
+	    }
+	} else {
+	    linkpartner = mii_rd(ioaddr, 0, 5);
+	    printk(KERN_INFO "%s: MII link partner: %04x\n",
+		   dev->name, linkpartner);
+	    if (linkpartner & 0x0080) {
+		dev->if_port = 4;
+	    } else
+		dev->if_port = 1;
+	}
+    }
+
+    return 1;
+}
+
+static void
+do_powerdown(struct net_device *dev)
+{
+
+    kio_addr_t ioaddr = dev->base_addr;
+
+    DEBUG(0, "do_powerdown(%p)\n", dev);
+
+    SelectPage(4);
+    PutByte(XIRCREG4_GPR1, 0);	     /* clear bit 0: power down */
+    SelectPage(0);
+}
+
+static int
+do_stop(struct net_device *dev)
+{
+    kio_addr_t ioaddr = dev->base_addr;
+    local_info_t *lp = netdev_priv(dev);
+    dev_link_t *link = &lp->link;
+
+    DEBUG(0, "do_stop(%p)\n", dev);
+
+    if (!link)
+	return -ENODEV;
+
+    netif_stop_queue(dev);
+
+    SelectPage(0);
+    PutByte(XIRCREG_CR, 0);  /* disable interrupts */
+    SelectPage(0x01);
+    PutByte(XIRCREG1_IMR0, 0x00); /* forbid all ints */
+    SelectPage(4);
+    PutByte(XIRCREG4_GPR1, 0);	/* clear bit 0: power down */
+    SelectPage(0);
+
+    link->open--;
+    return 0;
+}
+
+static struct pcmcia_driver xirc2ps_cs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= "xirc2ps_cs",
+	},
+	.attach		= xirc2ps_attach,
+	.detach		= xirc2ps_detach,
+};
+
+static int __init
+init_xirc2ps_cs(void)
+{
+	return pcmcia_register_driver(&xirc2ps_cs_driver);
+}
+
+static void __exit
+exit_xirc2ps_cs(void)
+{
+	pcmcia_unregister_driver(&xirc2ps_cs_driver);
+	BUG_ON(dev_list != NULL);
+}
+
+module_init(init_xirc2ps_cs);
+module_exit(exit_xirc2ps_cs);
+
+#ifndef MODULE
+static int __init setup_xirc2ps_cs(char *str)
+{
+	/* if_port, full_duplex, do_sound, lockup_hack
+	 */
+	int ints[10] = { -1 };
+
+	str = get_options(str, 9, ints);
+
+#define MAYBE_SET(X,Y) if (ints[0] >= Y && ints[Y] != -1) { X = ints[Y]; }
+	MAYBE_SET(if_port, 3);
+	MAYBE_SET(full_duplex, 4);
+	MAYBE_SET(do_sound, 5);
+	MAYBE_SET(lockup_hack, 6);
+#undef  MAYBE_SET
+
+	return 0;
+}
+
+__setup("xirc2ps_cs=", setup_xirc2ps_cs);
+#endif