/*======================================================================

    A non-shared-memory PCMCIA ethernet driver for linux

    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, the PreMax PE-200, and the Socket EA cards.

    Written by David Hinds, dhinds@allegro.stanford.edu

    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 Public License,
    incorporated herein by reference.
    Donald Becker may be reached at becker@cesdis1.gsfc.nasa.gov
    
======================================================================*/

#include <linux/config.h>

#ifdef MODULE
#include <linux/module.h>
#include <linux/version.h>
#endif

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/malloc.h>
#include <linux/string.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/system.h>

#include <linux/netdevice.h>
#include <drivers/net/8390.h>

#include "version.h"
#include "cs_types.h"
#include "cs.h"
#include "cistpl.h"
#include "ds.h"

#define DE650_BASE	(dev->base_addr)
#define DE650_CMD	0x00
#define DE650_DATAPORT	0x10	/* NatSemi-defined port window offset. */
#define SOCKET_IFPORT	0x18	/* Socket EA transceiver selection */
#define DE650_RESET	0x1f	/* Issue a read to reset, a write to clear. */

#define DE650_START_PG	0x40	/* First page of TX buffer */
#define DE650_STOP_PG	0x80	/* Last page +1 of RX ring */

#define DE650_RDC_TIMEOUT 0x02	/* Max wait in jiffies for Tx RDC */

static char *if_names[] = {"10baseT", "undefined", "undefined", "BNC"};

#ifdef PCMCIA_DEBUG
static int pc_debug = PCMCIA_DEBUG;
static char *version =
    "de650_cs.c 1.65 1995/05/15 04:46:01 (David Hinds)\n";
#endif

/*====================================================================*/

/* Parameters that can be set with 'insmod' */

/* Bit map of interrupts to choose from */
static u_long irq_mask = 0xdeb8;

/* Transceiver type, for Socket EA cards */
static int if_port = 3;

/* Insert a pause in block_output, for Socket EA and Accton cards */
static int delay_output = 0;

/* Length of delay, in microseconds */
static int delay_time = 4;

/*====================================================================*/

int de650_probe(struct device *dev);

static void de650_config(dev_link_t *link);
static void de650_release(u_long arg);
static int de650_event(event_t event, int priority,
		       event_callback_args_t *args);

static int de650_open(struct device *dev);
static int de650_close(struct device *dev);

static void de650_reset_8390(struct device *dev);
static int de650_block_input(struct device *dev, int count,
			     char *buf, int ring_offset);
static void de650_block_output(struct device *dev, const int count,
			       const unsigned char *buf,
			       const int start_page);

static dev_info_t dev_info = "de650_cs";

static dev_link_t *de650_attach(void);
static void de650_detach(dev_link_t *);

static dev_link_t *dev_list = NULL;

/*====================================================================*/

/* Lookup table for finding the hardware ethernet address */

typedef struct hwaddr_info_t {
    u_long	offset;
    u_char	a0, a1, a2;
    char	*id;
} hwaddr_info_t;

static hwaddr_info_t hw_info[] = {
    { 0x0040, 0x00, 0x80, 0xc8, "D-Link DE-650 / Linksys E-Card" },
    { 0x0110, 0x00, 0x40, 0x33, "EP-210 Ethernet" },
    { 0x0110, 0x00, 0x40, 0x95, "RPTI EP400" },
    { 0x07f0, 0x00, 0x20, 0xe0, "PreMax PE-200" },
    { 0x0ff0, 0x00, 0x00, 0xe8, "Accton EN2212" },
    { 0x4000, 0x00, 0xc0, 0x1b, "Socket EA" },
    { 0x5000, 0x00, 0x00, 0xe8, "Maxtech PCN2000" },
    { 0x0110, 0x00, 0x40, 0xf6, "Katron PE-520" }
};

#define IS_ACCTON(dev) (ei_status.name == hw_info[4].id)
#define IS_SOCKET(dev) (ei_status.name == hw_info[5].id)

#define NR_INFO (sizeof(hw_info)/sizeof(hwaddr_info_t))

/*====================================================================*/

static void cs_error(int func, int ret)
{
    CardServices(ReportError, dev_info, (void *)func, (void *)ret);
}

/*======================================================================

    If we're linked into the kernel, this gets called from the net
    driver initialization code in Space.c.  It returns failure,
    since all de650 devices are dynamically allocated.
    
======================================================================*/

#ifndef MODULE
int de650_probe(struct device *dev)
{
#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk(version);
#endif
    register_pcmcia_driver(&dev_info, &de650_attach, &de650_detach);
    return -1;
}
#endif

/*======================================================================

    We never need to do anything when a de650 device is "initialized"
    by the net software, because we only register already-found cards.
    
======================================================================*/

static int de650_init(struct device *dev)
{
    return 0;
}

/*======================================================================

    de650_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 *de650_attach(void)
{
    client_reg_t client_reg;
    dev_link_t *link;
    struct device *dev;
    int ret;
    
#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk("de650_attach()\n");
#endif

    /* Create new ethernet device */
    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
    memset(link, 0, sizeof(struct dev_link_t));
    link->release.function = &de650_release;
    link->release.data = (u_long)link;
    link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
    link->io.NumPorts1 = link->io.NumPorts2 = 16;
    link->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
    link->io.IOAddrLines = 5;
    link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
    link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID;
    link->irq.IRQInfo2 = irq_mask;
    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;
    
    dev = kmalloc(sizeof(struct device), GFP_KERNEL);
    memset(dev, 0, sizeof(struct device));
    ethdev_init(dev);
    dev->name = link->dev_name;
    dev->init = &de650_init;
    dev->open = &de650_open;
    dev->stop = &de650_close;
    dev->tbusy = 1;
    link->priv = dev;
    
    /* Register with Card Services */
    link->next = dev_list;
    dev_list = link;
    client_reg.dev_info = &dev_info;
    client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE;
    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 = &de650_event;
    client_reg.Version = 0x0210;
    client_reg.event_callback_args.client_data = link;
    ret = CardServices(RegisterClient, &link->handle, &client_reg);
    if (ret != 0) {
	cs_error(RegisterClient, ret);
	de650_detach(link);
	return NULL;
    }
    
    return link;
} /* de650_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 de650_detach(dev_link_t *link)
{
    dev_link_t **linkp;
    long flags;
    
#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk("de650_detach(0x%p)\n", link);
#endif
    
    /* Locate device structure */
    for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
	if (*linkp == link) break;
    if (*linkp == NULL)
	return;

    save_flags(flags);
    cli();
    if (link->state & DEV_RELEASE_PENDING) {
	del_timer(&link->release);
	link->state &= ~DEV_RELEASE_PENDING;
    }
    restore_flags(flags);
    
    if (link->state & DEV_CONFIG) {
	de650_release((u_long)link);
	if (link->state & DEV_STALE_CONFIG) {
	    link->state |= DEV_STALE_LINK;
	    return;
	}
    }

    if (link->handle)
	CardServices(DeregisterClient, link->handle);
    
    /* Unlink device structure, free bits */
    *linkp = link->next;
    if (link->priv) {
	struct device *dev = link->priv;
	if (dev->priv)
	    kfree_s(dev->priv, sizeof(struct ei_device));
	kfree_s(dev, sizeof(struct device));
    }
    kfree_s(link, sizeof(struct dev_link_t));
    
} /* de650_detach */

/*======================================================================

    de650_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 de650_config(dev_link_t *link)
{
    client_handle_t handle;
    tuple_t tuple;
    cisparse_t parse;
    struct device *dev;
    int i, j;
    u_char buf[64], *base;
    char *id = NULL;
    win_req_t req;
    memreq_t mem;
    
    handle = link->handle;
    dev = link->priv;

#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk("de650_config(0x%p)\n", link);
#endif

    do {
	tuple.Attributes = 0;
	tuple.DesiredTuple = CISTPL_CONFIG;
	i = CardServices(GetFirstTuple, handle, &tuple);
	if (i != CS_SUCCESS) break;
	tuple.TupleData = buf;
	tuple.TupleDataMax = 64;
	tuple.TupleOffset = 0;
	i = CardServices(GetTupleData, handle, &tuple);
	if (i != CS_SUCCESS) break;
	i = CardServices(ParseTuple, handle, &tuple, &parse);
	if (i != CS_SUCCESS) break;
	link->conf.ConfigBase = (caddr_t)parse.config.base;
    } while (0);
    if (i != CS_SUCCESS) {
	printk("de650_cs: unable to parse CIS\n");
	return;
    }
    
    /* Get the hardware address from the CIS */
    do {

	/* Allocate a 8K memory window */
	req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
	req.Base = NULL;
	req.Size = 0x1000;
	req.AccessSpeed = 0;
	link->win = (window_handle_t)link->handle;
	i = CardServices(RequestWindow, &link->win, &req);
	if (i != 0) {
	    cs_error(RequestWindow, i);
	    break;
	}

	mem.Page = 0;
	for (j = 0; j < NR_INFO; j++) {
	    mem.CardOffset = hw_info[j].offset & ~0x0fff;
	    CardServices(MapMemPage, link->win, &mem);
	    base = &req.Base[hw_info[j].offset & 0x0fff];
	    if ((base[0] == hw_info[j].a0) &&
		(base[2] == hw_info[j].a1) &&
		(base[4] == hw_info[j].a2))
		break;
	}
	if (j == NR_INFO) {
	    printk("de650_cs: unable to find hardware net address\n");
	    i = -1;
	    break;
	}
	id = hw_info[j].id;
	for (j = 0; j < 6; j++)
	    dev->dev_addr[j] = base[j<<1];
    } while (0);
    if (link->win)
	CardServices(ReleaseWindow, link->win);
    if (i != CS_SUCCESS) {
	return;
    }

    /* Configure card */
    link->state |= DEV_CONFIG;

    do {
	for (j = 0x300; j < 0x400; j += 0x20) {
	    link->io.BasePort1 = j;
	    link->io.BasePort2 = j+0x10;
	    i = CardServices(RequestIO, link->handle, &link->io);
	    if (i == CS_SUCCESS) break;
	}
	if (i != CS_SUCCESS) {
	    cs_error(RequestIO, i);
	    break;
	}
	i = CardServices(RequestIRQ, link->handle, &link->irq);
	if (i != CS_SUCCESS) {
	    cs_error(RequestIRQ, i);
	    break;
	}
	i = CardServices(RequestConfiguration, link->handle, &link->conf);
	if (i != CS_SUCCESS) {
	    cs_error(RequestConfiguration, i);
	    break;
	}
	i = request_irq(link->irq.AssignedIRQ, &ei_interrupt,
			0, "de650_cs");
	if (i != CS_SUCCESS) {
	    printk("de650_cs: unable to allocate irq %ld\n",
		   link->irq.AssignedIRQ);
	    break;
	}
	dev->irq = link->irq.AssignedIRQ;
	dev->base_addr = link->io.BasePort1;
	dev->if_port = if_port;
	dev->tbusy = 0;
	i = register_netdev(dev);
	if (i != 0) {
	    printk("de650_cs: register_netdev() failed\n");
	    break;
	}
    } while (0);
    
    if (i != 0) {
	de650_release((u_long)link);
	return;
    }
    
    ei_status.name = id;
    ei_status.word16 = 1;
    ei_status.tx_start_page = DE650_START_PG;
    ei_status.rx_start_page = ei_status.tx_start_page + TX_PAGES;
    ei_status.stop_page = DE650_STOP_PG;
    ei_status.reset_8390 = &de650_reset_8390;
    ei_status.block_input = &de650_block_input;
    ei_status.block_output = &de650_block_output;

    if (IS_SOCKET(dev) || IS_ACCTON(dev))
	delay_output = 1;
    
    printk("%s: %s, port %#3lx, irq %d,",
	   dev->name, id, dev->base_addr, dev->irq);
    if (IS_SOCKET(dev))
	printk(" %s port,", if_names[dev->if_port]);
    for (i = 0; i < ETHER_ADDR_LEN; i++)
	printk(" %02X", dev->dev_addr[i]);
    printk("\n");
    
} /* de650_config */

/*======================================================================

    After a card is removed, de650_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 de650_release(u_long arg)
{
    dev_link_t *link = (dev_link_t *)arg;
    struct device *dev = link->priv;

#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk("de650_release(0x%p)\n", link);
#endif
    
    if (link->open) {
	printk("de650_cs: release postponed, '%s' still open\n",
	       link->dev_name);
	link->state |= DEV_STALE_CONFIG;
	return;
    }
    
    if (link->dev_name[0] != '\0')
	unregister_netdev(dev);
    
    CardServices(ReleaseConfiguration, link->handle);
    CardServices(ReleaseIO, link->handle, &link->io);
    CardServices(ReleaseIRQ, link->handle, &link->irq);
    if (dev->irq != 0) {
	free_irq(dev->irq);
	irq2dev_map[dev->irq] = NULL;
    }
    
    link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING);
    if (link->state & DEV_STALE_LINK)
	de650_detach(link);

} /* de650_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.
    
======================================================================*/

static int de650_event(event_t event, int priority,
		       event_callback_args_t *args)
{
    dev_link_t *link = args->client_data;
    struct device *dev = link->priv;

#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk("de650_event()\n");
#endif
    
    switch (event) {
#ifdef PCMCIA_DEBUG
    case CS_EVENT_REGISTRATION_COMPLETE:
	if (pc_debug)
	    printk("de650_cs: registration complete\n");
	break;
#endif
    case CS_EVENT_CARD_REMOVAL:
	link->state &= ~DEV_PRESENT;
	if (link->state & DEV_CONFIG) {
	    dev->tbusy = 1; dev->start = 0;
	    link->release.expires = 5;
	    link->state |= DEV_RELEASE_PENDING;
	    add_timer(&link->release);
	}
	break;
    case CS_EVENT_CARD_INSERTION:
	link->state |= DEV_PRESENT;
	de650_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) {
		dev->tbusy = 1; dev->start = 0;
	    }
	    CardServices(ReleaseConfiguration, link->handle);
	}
	break;
    case CS_EVENT_PM_RESUME:
	link->state &= ~DEV_SUSPEND;
	/* Fall through... */
    case CS_EVENT_CARD_RESET:
	if (link->state & DEV_CONFIG) {
	    CardServices(RequestConfiguration, link->handle, &link->conf);
	    if (link->open) {
		de650_reset_8390(dev);
		NS8390_init(dev, 1);
		dev->tbusy = 0; dev->start = 1;
	    }
	}
	break;
    }
    return 0;
} /* de650_event */

/*====================================================================*/

static int de650_open(struct device *dev)
{
    dev_link_t *link;
    u_char tmp;

#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk("de650_open('%s')\n", dev->name);
#endif
    
    for (link = dev_list; link; link = link->next)
	if (link->priv == dev) break;
    if (!DEV_OK(link))
	return -ENODEV;
    
    link->open++;
#ifdef MODULE
    MOD_INC_USE_COUNT;
#endif
    
    if (IS_SOCKET(dev)) {
	tmp = inb_p(DE650_BASE+SOCKET_IFPORT) & ~3;
	if (dev->if_port == 3)
	    tmp |= 1;
	outb_p(tmp, DE650_BASE+SOCKET_IFPORT);
    }
    
    return ei_open(dev);
} /* de650_open */

/*====================================================================*/

static int de650_close(struct device *dev)
{
    dev_link_t *link;

#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk("de650_close('%s')\n", dev->name);
#endif
    
    for (link = dev_list; link; link = link->next)
	if (link->priv == dev) break;
    if (link == NULL)
	return -ENODEV;

    link->open--; dev->start = 0;
    if (link->state & DEV_STALE_CONFIG)
	de650_release((u_long)link);
    
#ifdef MODULE
    MOD_DEC_USE_COUNT;
#endif
    
    return 0;
} /* de650_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 de650_reset_8390(struct device *dev)
{
    int tmp, i;
     
    if (ei_debug > 1) printk("resetting the 8390 t=%ld...", jiffies);
    ei_status.txing = 0;
     
    tmp = inb_p(DE650_BASE + DE650_RESET);
    outb_p(tmp, DE650_BASE + DE650_RESET);
    for (i = 0; i < 100; i++) {
	if ((inb_p(DE650_BASE+EN0_ISR) & ENISR_RESET) != 0)
	    break;
	udelay(100L);
    }
    if (i == 100)
	printk("%s: de650_reset_8390() did not complete.\n", dev->name);
    
    if (IS_SOCKET(dev)) {
	tmp = inb_p(DE650_BASE+SOCKET_IFPORT) & ~3;
	if (dev->if_port == 3)
	    tmp |= 1;
	outb_p(tmp, DE650_BASE+SOCKET_IFPORT);
    }
	    
} /* de650_reset_8390 */

/*======================================================================

    Block input and output, similar to the Crynwr packet driver.

======================================================================*/

static int de650_block_input(struct device *dev, int count,
			     char *buf, int ring_offset)
{
    int xfer_count = count;

#ifdef PCMCIA_DEBUG
    if (ei_debug > 4)
	if (count != 4) printk("%s: [bi=%d]\n", dev->name, count+4);
#endif
    if (set_bit(0,(void *)&ei_status.dmaing)) {
	if (ei_debug > 0)
	    printk("%s: DMAing conflict in de650_block_input."
		   "[DMAstat:%1x][irqlock:%1x]\n",
		   dev->name, ei_status.dmaing, ei_status.irqlock);
	return 0;
    }
    ei_status.dmaing |= 0x02;
    outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, DE650_BASE+DE650_CMD);
    outb_p(count & 0xff, DE650_BASE + EN0_RCNTLO);
    outb_p(count >> 8, DE650_BASE + EN0_RCNTHI);
    outb_p(ring_offset & 0xff, DE650_BASE + EN0_RSARLO);
    outb_p(ring_offset >> 8, DE650_BASE + EN0_RSARHI);
    outb_p(E8390_RREAD+E8390_START, DE650_BASE+DE650_CMD);

    insw(DE650_BASE + DE650_DATAPORT,buf,count>>1);
    if (count & 0x01)
	buf[count-1] = inb(DE650_BASE + DE650_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! Check the "DMA" address instead. */
	    int high = inb_p(DE650_BASE + EN0_RSARHI);
	    int low = inb_p(DE650_BASE + EN0_RSARLO);
	    addr = (high << 8) + low;
	    if (((ring_offset + xfer_count) & 0xff) == (addr & 0xff))
		break;
	} while (--tries > 0);
	if (tries <= 0)
	    printk("%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, DE650_BASE + EN0_ISR);
    ei_status.dmaing &= ~0x03;
    return ring_offset + count;
} /* de650_block_input */
  
/*====================================================================*/

static void de650_block_output(struct device *dev, int count,
			       const unsigned char *buf,
			       const int start_page)
{
#ifdef PCMCIA_DEBUG
    int retries = 0;
#endif
    u_long dma_start;
    
#ifdef PCMCIA_DEBUG
    if (ei_debug > 4) printk("%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 (set_bit(0, (void *)&ei_status.dmaing)) {
	if (ei_debug > 0) 
	    printk("%s: DMAing conflict in de650_block_output."
		   "[DMAstat:%1x][irqlock:%1x]\n",
		   dev->name, ei_status.dmaing, ei_status.irqlock);
	  return;
    }
    ei_status.dmaing |= 0x04;
    /* We should already be in page 0, but to be safe... */
    outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, DE650_BASE+DE650_CMD);

#ifdef PCMCIA_DEBUG
 retry:
#endif

    outb_p(ENISR_RDC, DE650_BASE + EN0_ISR);
    
    /* Now the normal output. */
    outb_p(count & 0xff, DE650_BASE + EN0_RCNTLO);
    outb_p(count >> 8,   DE650_BASE + EN0_RCNTHI);
    outb_p(0x00, DE650_BASE + EN0_RSARLO);
    outb_p(start_page, DE650_BASE + EN0_RSARHI);
    
    outb_p(E8390_RWRITE+E8390_START, DE650_BASE+DE650_CMD);
    outsw(DE650_BASE + DE650_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 {
	    /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
	       -- it's broken! Check the "DMA" address instead. */
	    int high = inb_p(DE650_BASE + EN0_RSARHI);
	    int low = inb_p(DE650_BASE + EN0_RSARLO);
	    addr = (high << 8) + low;
	    if ((start_page << 8) + count == addr)
		break;
	} while (--tries > 0);
	if (tries <= 0) {
	    printk("%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(DE650_BASE + EN0_ISR) & ENISR_RDC) == 0)
	if (jiffies - dma_start > DE650_RDC_TIMEOUT) {
	    printk("%s: timeout waiting for Tx RDC.\n", dev->name);
	    de650_reset_8390(dev);
	    NS8390_init(dev, 1);
	    break;
	}
    
    if (delay_output)
	udelay((long)delay_time);
    ei_status.dmaing &= ~0x05;

} /* de650_block_output */

/*====================================================================*/

#ifdef MODULE
char kernel_version[] = UTS_RELEASE;

int init_module(void)
{
    servinfo_t serv;
#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk(version);
#endif
    CardServices(GetCardServicesInfo, &serv);
    if (serv.Revision != CS_RELEASE_CODE) {
	printk("de650_cs: Card Services release does not match!\n");
	return -1;
    }
    register_pcmcia_driver(&dev_info, &de650_attach, &de650_detach);
    return 0;
}

void cleanup_module(void)
{
    printk("de650_cs: unloading\n");
    unregister_pcmcia_driver(&dev_info);
    while (dev_list != NULL)
	de650_detach(dev_list);
}
#endif /* MODULE */
