/*======================================================================
    fmvj18x_cs.c,v 1.9 1996/08/06 03:13:53 root Exp

    A fmvj18x (and its compatibles) PCMCIA client driver

    Written 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

    Network code is based on the "FMV-18x driver" by Yutaka TAMIYA

    This software may be used and distributed according to the terms
    of the GNU Public License.

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

#include <pcmcia/config.h>
#include <pcmcia/k_compat.h>

#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/interrupt.h>
#include <linux/in.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/system.h>

#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/if_arp.h>
#include <linux/ioport.h>

#include <pcmcia/version.h>
#include <pcmcia/cs_types.h>
#include <pcmcia/cs.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>

/*
   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_PARM(pc_debug, "i");
#endif

/*
   For debugging this driver you may need more information.
   To enable printing registers or status, set 'fmvj18x_debug=#' option .
 */
#ifdef FMVJ18X_DEBUG
static fmvj18x_debug = FMVJ18X_DEBUG;
#else
static fmvj18x_debug = 2;
#endif /* FMVJ18X_DEBUG */

/* Bit map of interrupts to choose from */
/* This means pick from 15, 14, 12, 11, 10, 9, 7, 5, 4, and 3 */
static u_long irq_mask = 0xdeb8;

/* SRAM configuration */
/* 0:4KB*2 TX buffer   else:8KB*2 TX buffer */
static int sram_config = 0;

MODULE_PARM(irq_mask, "i");
MODULE_PARM(sram_config, "i");

/*====================================================================*/
/* 
   driver version infomation 
 */
#ifdef PCMCIA_DEBUG
static char *version =
 "fmvj18x_cs.c,v 1.9 1996/08/06 03:13:53 root Exp";
#endif

/*====================================================================*/
/*
    PCMCIA event handlers
 */
static void fmvj18x_config(dev_link_t *link);
static void fmvj18x_release(u_long arg);
static int fmvj18x_event(event_t event, int priority,
			  event_callback_args_t *args);
static int fmvj18x_init(struct device *dev);
static dev_link_t *fmvj18x_attach(void);
static void fmvj18x_detach(dev_link_t *);

/*
    LAN controler(MBH86960A) specific routines
 */
static int fjn_config(struct device *dev, struct ifmap *map);
static int fjn_open(struct device *dev);
static int fjn_close(struct device *dev);
static int fjn_start_xmit(struct sk_buff *skb, struct device *dev);
static void fjn_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs);
static void fjn_rx(struct device *dev);
static void fjn_reset(struct device *dev);
static struct net_device_stats *fjn_get_stats(struct device *dev);

#ifdef NEW_MULTICAST
static void set_multicast_list(struct device *dev);
#else
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
#endif /* NEW_MULTICAST */

static dev_info_t dev_info = "fmvj18x_cs";
static dev_link_t *dev_list = NULL;

/*
    card type
 */
typedef enum { MBH10302, MBH10304, TDK, CONTEC } cardtype_t;

/*
    driver specific data structure
*/
typedef struct local_info_t {
    dev_node_t node;
    struct net_device_stats stats;
    long open_time;
    uint tx_started:1;
    uint tx_queue;
    ushort tx_queue_len;
    cardtype_t cardtype;
    ushort sent;
} local_info_t;


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

/* 
    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 Controler off (CONFIG_0) */
#define CONFIG0_RST_1        0xde /* Data Link Controler 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 controler ignores interrupts */
#define INTR_ON              0x1d /* LAN controler will catch interrupts */

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

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

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

static dev_link_t *fmvj18x_attach(void)
{
    client_reg_t client_reg;
    dev_link_t *link;
    struct device *dev;
    int ret;
    
#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk(KERN_DEBUG "fmvj18x_attach()\n");
#endif

    /* Initialize the dev_link_t structure */
    link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL);
    memset(link, 0, sizeof(struct dev_link_t));

    link->release.function = &fmvj18x_release;
    link->release.data = (u_long)link;

    /* 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_INFO2_VALID|IRQ_LEVEL_ID;
    link->irq.IRQInfo2 = irq_mask;
    link->irq.Handler = &fjn_interrupt;
    
    /* General socket configuration */
    link->conf.Attributes = CONF_ENABLE_IRQ;
    link->conf.Vcc = 50;
    link->conf.IntType = INT_MEMORY_AND_IO;

    /* Make up a FMVJ18x specific data structure */
    dev = kmalloc(sizeof(struct device), GFP_KERNEL);
    memset(dev, 0, sizeof(struct device));
    dev->priv = kmalloc(sizeof(local_info_t), GFP_KERNEL);
    memset(dev->priv, 0, sizeof(local_info_t));

    /* The FMVJ18x specific entries in the device structure. */
    dev->hard_start_xmit = &fjn_start_xmit;
    dev->set_config = &fjn_config;
    dev->get_stats = &fjn_get_stats;
    dev->set_multicast_list = &set_multicast_list;
    ether_setup(dev);
    dev->name = ((local_info_t *)dev->priv)->node.dev_name;
    dev->init = &fmvj18x_init;
    dev->open = &fjn_open;
    dev->stop = &fjn_close;
    dev->tbusy = 0xFF;
    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 = &fmvj18x_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);
	fmvj18x_detach(link);
	return NULL;
    }

    return link;
} /* fmvj18x_attach */

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

static void fmvj18x_detach(dev_link_t *link)
{
    dev_link_t **linkp;

#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk(KERN_DEBUG "fmvj18x_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;

    if (link->state & DEV_CONFIG) {
	fmvj18x_release((u_long)link);
	if (link->state & DEV_STALE_CONFIG) {
	    link->state |= DEV_STALE_LINK;
	    return;
	}
    }

    /* Break the link with Card Services */
    if (link->handle)
	CardServices(DeregisterClient, link->handle);
    
    /* Unlink device structure, free pieces */
    *linkp = link->next;
    if (link->priv) {
	struct device *dev = link->priv;
	
	if (dev->priv) 
	    kfree_s(dev->priv, sizeof(local_info_t));
	kfree_s(dev, sizeof(struct device));
    }
    kfree_s(link, sizeof(struct dev_link_t));
    
} /* fmvj18x_detach */

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

static void fmvj18x_config(dev_link_t *link)
{
    client_handle_t handle;
    tuple_t tuple;
    cisparse_t parse;
    struct device *dev;
    u_char buf[64];
    int i;
    short ioaddr;
    int ret;
    cardtype_t cardtype;
    char *card_name = "unknown";
    
    handle = link->handle;
    dev =link->priv;

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

    /*
       This reads the card's CONFIG tuple to find its configuration
       registers.
    */
    do {
	tuple.DesiredTuple = CISTPL_CONFIG;
	ret = CardServices(GetFirstTuple, handle, &tuple);
	if (ret != CS_SUCCESS) break;
	tuple.TupleData = buf;
	tuple.TupleDataMax = 64;
	tuple.TupleOffset = 0;
	ret = CardServices(GetTupleData, handle, &tuple);
	if (ret != CS_SUCCESS) break;
	ret = CardServices(ParseTuple, handle, &tuple, &parse);
    } while (0);
    if (ret != CS_SUCCESS) {
	cs_error(ParseTuple, ret);
	link->state &= ~DEV_CONFIG_PENDING;
	return;
    }
    
    /* 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 (CardServices(GetFirstTuple, handle, &tuple) == CS_SUCCESS) {
	/* Yes, I have CISTPL_FUNCE. Let's check CISTPL_MANFID */
	tuple.DesiredTuple = CISTPL_MANFID ;
	tuple.TupleOffset = 0;
	if (CardServices(GetFirstTuple, handle, &tuple) != CS_SUCCESS) {
	    fmvj18x_release((u_long)link);
	    return;
	}
	tuple.TupleOffset = 0;
	if (CardServices(GetTupleData, handle, &tuple) != CS_SUCCESS) {
	    fmvj18x_release((u_long)link);
	    return;
	}
	if( tuple.TupleData[0] == 0x05 && tuple.TupleData[1] == 0x01 ) {
	    /* Manufacture is TDK */
	    cardtype = TDK;
	    link->conf.ConfigIndex = 0x04;
	} else if( tuple.TupleData[0] == 0x01 && tuple.TupleData[1] == 0xc0 ) {
	    /* Manufacture is CONTEC */
	    cardtype = CONTEC;
	    link->conf.ConfigIndex = 1;
	} else {
	    /* FMV-J182(MBH10304) have MACID on CIS */
	    cardtype = MBH10304;
	    link->conf.ConfigIndex = 0x60;
	}
    } else {
	/* old type card */
	cardtype = MBH10302;
	link->conf.ConfigIndex = 1;
    }
    do {
	ret = CardServices(RequestIO, link->handle, &link->io);
	if (ret != CS_SUCCESS) {
	    cs_error(RequestIO, ret);
	    goto end;
	}
	ret = CardServices(RequestIRQ, link->handle, &link->irq);
	if (ret != CS_SUCCESS) {
	    cs_error(RequestIRQ, ret);
	    goto end;
	}
	ret = CardServices(RequestConfiguration, link->handle, &link->conf);
	if (ret != CS_SUCCESS) {
	    cs_error(RequestConfiguration, ret);
	    goto end;
	}
	dev->irq = link->irq.AssignedIRQ;
	irq2dev_map[dev->irq] = dev;
	dev->base_addr = link->io.BasePort1;
	dev->tbusy = 0;
	ret = register_netdev(dev);
	if (ret != 0) {
	    printk(KERN_NOTICE "fmvj18x_cs: register_netdev() failed\n");
	    goto end;
	}
    } while (0);

    ioaddr = dev->base_addr;

    /* Power On chip and select bank 0 */
    outb(BANK_0, ioaddr + CONFIG_1);
    /* Reset controler */
    if( sram_config == 0 ) 
	outb(CONFIG0_RST, ioaddr + CONFIG_0);
    else
	outb(CONFIG0_RST_1, ioaddr + CONFIG_0);
    
    /* Set hardware address */
    switch (cardtype) {
    case MBH10304:
    case TDK:
    case CONTEC:
	do {
	    u_char *node_id;
	    
	    tuple.DesiredTuple = CISTPL_FUNCE;
	    tuple.TupleOffset = 0;
	    ret = CardServices(GetFirstTuple, handle, &tuple);
	    if (ret != CS_SUCCESS) goto end;
	    tuple.TupleOffset = 0;
	    ret = CardServices(GetTupleData, handle, &tuple);
	    if (ret != CS_SUCCESS) goto end;
	    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 ) {
		    ret = CardServices(GetNextTuple, handle, &tuple) ;
		    if (ret != CS_SUCCESS) goto end;
		    ret = CardServices(GetTupleData, handle, &tuple) ;
		    if (ret != CS_SUCCESS) goto end;
		}
		node_id = &(tuple.TupleData[2]);
		if( cardtype == TDK ) {
		    card_name = "TDK LAK-CD021";
		} else {
		    card_name = "C-NET(PC)C";
		}
	    }
	    /* Read MACID from CIS */
	    for (i = 0; i < 6; i++)
		dev->dev_addr[i] = node_id[i];
	} while (0);
	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;
    }

    link->dev = &((local_info_t *)dev->priv)->node;
    link->state &= ~DEV_CONFIG_PENDING;

    ((struct local_info_t *)dev->priv)->cardtype = cardtype ;
    /* 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", dev->dev_addr[i]);
    printk("\n");

end:
    /* If any step failed, release any partially configured state */
    if (ret != 0) {
	fmvj18x_release((u_long)link);
	return;
    }
} /* fmvj18x_config */
 
/*====================================================================*/

static void fmvj18x_release(u_long arg)
{
    dev_link_t *link = (dev_link_t *)arg;
    struct device *dev = link->priv;

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

    /*
       If the device is currently in use, we won't release until it
       is actually closed.
    */
    if (link->open) {
#ifdef PCMCIA_DEBUG
	if (pc_debug)
	    printk(KERN_DEBUG "fmvj18x_cs: release postponed, '%s' "
		   "still open\n", link->dev->dev_name);
#endif 
	link->state |= DEV_STALE_CONFIG;
	return;
    }

    /* Unlink the device chain */
    if (link->dev)
	unregister_netdev(dev);
    link->dev = NULL;
    
    /* Don't bother checking to see if these succeed or not */
    CardServices(ReleaseWindow, link->win);
    CardServices(ReleaseConfiguration, link->handle);
    CardServices(ReleaseIO, link->handle, &link->io);
    CardServices(ReleaseIRQ, link->handle, &link->irq);
    link->state &= ~DEV_CONFIG;
    
    if (link->state & DEV_STALE_LINK)
	fmvj18x_detach(link);
    
} /* fmvj18x_release */

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

static int fmvj18x_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(KERN_DEBUG "fmvj18x_event()\n");
#endif
    
    switch (event) {
#ifdef PCMCIA_DEBUG
    case CS_EVENT_REGISTRATION_COMPLETE:
	if (pc_debug)
	    printk(KERN_DEBUG "fmvj18x_cs: registration complete\n");
	break;
#endif
    case CS_EVENT_CARD_REMOVAL:
	link->state &= ~DEV_PRESENT;
	if (link->state & DEV_CONFIG) {
	    dev->tbusy = 0xFF; 
	    dev->start = 0;
	    link->release.expires = RUN_AT(HZ/20);
	    add_timer(&link->release);
	}
	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) {
		dev->tbusy = 0xFF; 
		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) {
		dev->tbusy = 0;
		dev->start = 1;
		fjn_reset(dev);
	    }
	}
	break;
    }
    return 0;
} /* fmvj18x_event */

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

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

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

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

void cleanup_module(void)
{
#ifdef PCMCIA_DEBUG
    if (pc_debug)
	printk(KERN_DEBUG "fmvj18x_cs: unloading\n");
#endif
    unregister_pcmcia_driver(&dev_info);
    while (dev_list != NULL) {
	if (dev_list->state & DEV_CONFIG)
	    fmvj18x_release((u_long)dev_list);
	fmvj18x_detach(dev_list);
    }
}

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

static void fjn_interrupt IRQ(int irq, void *dev_id, struct pt_regs *regs)
{
    struct device *dev = (struct device *)(irq2dev_map[irq]);
    int ioaddr;
    local_info_t *lp;
    unsigned short tx_stat, rx_stat;

    if (dev == NULL) {
        printk(KERN_NOTICE "fjn_interrupt(): irq %d for "
	       "unknown device.\n", irq);
        return;
    }
    if (dev->interrupt) {
        printk(KERN_NOTICE "%s: re-entering the interrupt handler.\n",
	       dev->name);
        return;
    }
    dev->interrupt = 1;
    lp = (struct local_info_t *)dev->priv;
    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);
    
    if (fmvj18x_debug > 4) {
        printk(KERN_DEBUG "%s: interrupt, rx_status %02x.\n",
	       dev->name, rx_stat);
        printk(KERN_DEBUG "               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;
	    dev->tbusy = 0;
	    mark_bh(NET_BH);	/* Inform upper layers. */
	} else {
	    lp->tx_started = 0;
	    dev->tbusy = 0;
	    mark_bh(NET_BH);	/* Inform upper layers. */
	}
    }
    if (fmvj18x_debug > 4) {
        printk(KERN_DEBUG "%s: exiting interrupt,\n", dev->name);
        printk(KERN_DEBUG "    tx_status %02x, rx_status %02x.\n",
	       tx_stat, rx_stat);
    }

    dev->interrupt = 0;
    outb(D_TX_INTR, ioaddr + TX_INTR);
    outb(D_RX_INTR, ioaddr + RX_INTR);

    return;
} /* fjn_interrupt */

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

static int fjn_start_xmit(struct sk_buff *skb, struct device *dev)
{
    struct local_info_t *lp = (struct local_info_t *)dev->priv;
    int ioaddr = dev->base_addr;

    if (dev->tbusy) {
	/* If we get here, some higher level has decided we are broken.
	   There should really be a "kick me" function call instead. */
	int tickssofar = jiffies - dev->trans_start;
	if (tickssofar < 10)
	    return 1;
	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... */
	cli();

	fjn_reset(dev);

	lp->tx_started = 0;
	lp->tx_queue = 0;
	lp->tx_queue_len = 0;
	lp->sent = 0;
	lp->open_time = jiffies;
	dev->interrupt = 0;
	dev->tbusy = 0;
	dev->start = 1;
    
	sti();
    }

#if (LINUX_VERSION_CODE < VERSION(2,1,25))
    if (skb == NULL) {
	dev_tint(dev);
	return 0;
    }
#endif
    
    /* Block a timer-based transmit from overlapping.  This could better be
       done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
    if (set_bit(0, (void*)&dev->tbusy) != 0)
	printk(KERN_NOTICE "%s: Transmitter access conflict.\n", dev->name);
    else {
	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
	unsigned char *buf = skb->data;

	if (length > ETH_FRAME_LEN) {
	    if (fmvj18x_debug)
		printk(KERN_NOTICE "%s: Attempting to send a large packet"
		       " (%d bytes).\n", dev->name, length);
	    return 1;
	}

	if (fmvj18x_debug > 4)
	    printk(KERN_DEBUG "%s: Transmitting a packet of length %lu.\n",
		   dev->name, (unsigned long)skb->len);
#if (LINUX_VERSION_CODE >= VERSION(2,1,25))
	lp->stats.tx_bytes += skb->len;
#endif

	/* 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;
	    dev->tbusy = 0;
	} else {
	    if( sram_config == 0 ) {
		if (lp->tx_queue_len < (4096 - (ETH_FRAME_LEN +2)) )
		    /* Yes, there is room for one more packet. */
		    dev->tbusy = 0;
	    } else {
		if (lp->tx_queue_len < (8192 - (ETH_FRAME_LEN +2)) && 
						lp->tx_queue < 127 )
		    /* Yes, there is room for one more packet. */
		    dev->tbusy = 0;
	    }
	}

	/* Re-enable interrupts */
	outb(D_TX_INTR, ioaddr + TX_INTR);
	outb(D_RX_INTR, ioaddr + RX_INTR);
    }
    dev_kfree_skb (skb, FREE_WRITE);

    return 0;
} /* fjn_start_xmit */

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

static void fjn_reset(struct device *dev)
{
    struct local_info_t *lp = (struct local_info_t *)dev->priv;
    int ioaddr = dev->base_addr;
    int i;

    if (fmvj18x_debug > 4) {
	printk(KERN_DEBUG "fjn_reset(%s) called.\n",dev->name);
    }

    /* Power On chip and select bank 0 */
    outb(BANK_0, ioaddr + CONFIG_1);
    /* Reset buffers */
    if( sram_config == 0 ) 
	outb(CONFIG0_RST, ioaddr + CONFIG_0);
    else
	outb(CONFIG0_RST_1, ioaddr + CONFIG_0);

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

    if (fmvj18x_debug > 4) {
	printk(KERN_DEBUG "node id: ");
	for (i = 0; i < 6; i++) 
	    printk("%02X ",inb(ioaddr + NODE_ID + i));
	printk("\n");
    }
    /* Switch to bank 1 */
    outb(BANK_1, 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) */
    outb(BANK_2, ioaddr + CONFIG_1);

    /* set 16col ctrl bits */
    if( lp->cardtype == TDK ) 
        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 != TDK ) 
    		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 controler */
    if( lp->cardtype != TDK ) 
		outb(INTR_ON, ioaddr + LAN_CTRL);
} /* fjn_reset */

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

static void fjn_rx(struct device *dev)
{
    struct local_info_t *lp = (struct local_info_t *)dev->priv;
    int ioaddr = dev->base_addr;
    int boguscount = 10;	/* 5 -> 10: by agy 19940922 */

    if (fmvj18x_debug > 4)
        printk(KERN_DEBUG "%s: in rx_packet(), rx_status %02x.\n",
               dev->name, inb(ioaddr + RX_STATUS));

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

	if (fmvj18x_debug > 4)
	    printk(KERN_DEBUG "%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 {
	    ushort 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 = ALLOC_SKB(pkt_len);
	    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;

#define BLOCK_INPUT(buf, len) \
	    insw(ioaddr + DATAPORT, buf, (len + 1) >> 1)

	    GET_PACKET(dev, skb, pkt_len);

	    if (fmvj18x_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");
	    }

	    netif_rx(skb);
	    lp->stats.rx_packets++;
#if (LINUX_VERSION_CODE >= VERSION(2,1,25))
	    lp->stats.rx_bytes += skb->len;
#endif
	}
	if (--boguscount <= 0)
	    break;
    }

    /* If any worth-while packets have been received, dev_rint()
	   has done a mark_bh(NET_BH) for us and will work on them
	   when we get to the bottom-half routine. */
/*
    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 (fmvj18x_debug > 5 && i > 0)
	    printk(KERN_DEBUG "%s: Exint Rx packet with mode %02x after"
		   " %d ticks.\n", dev->name, inb(ioaddr + RX_MODE), i);
    }
*/

    return;
} /* fjn_rx */

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

static int fjn_config(struct device *dev, struct ifmap *map){
    return 0;
} /* fjn_config */

static int fjn_open(struct device *dev)
{
    struct local_info_t *lp = (struct local_info_t *)dev->priv;
    dev_link_t *link;

    if (fmvj18x_debug > 4)
        printk(KERN_DEBUG "fjn_open('%s').\n", dev->name);

    for (link = dev_list; link; link = link->next)
	if (link->priv == dev) break;
    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;
    dev->interrupt = 0;
    dev->tbusy = 0;
    dev->start = 1;
    
    MOD_INC_USE_COUNT;

    return 0;
} /* fjn_open */

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

static int fjn_close(struct device *dev)
{
    int ioaddr = dev->base_addr;
    struct local_info_t *lp = (struct local_info_t *)dev->priv;
    dev_link_t *link;

    if (fmvj18x_debug > 4)
        printk(KERN_DEBUG "fjn_open('%s').\n", dev->name);

    for (link = dev_list; link; link = link->next)
	if (link->priv == dev) break;
    if (link == NULL)
	return -ENODEV;
    
    if (fmvj18x_debug > 2)
	printk(KERN_DEBUG "%s: shutting down ethercard.\n", dev->name);

    ((struct local_info_t *)dev->priv)->open_time = 0;

    dev->tbusy = 1;
    dev->start = 0;

    /* 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 != TDK ) 
    		outb(INTR_OFF, ioaddr + LAN_CTRL);

    link->open--;
    dev->start = 0;
    if (link->state & DEV_STALE_CONFIG) {
	link->release.expires = RUN_AT(HZ/20);
	link->state |= DEV_RELEASE_PENDING;
	add_timer(&link->release);
    }
    MOD_DEC_USE_COUNT;

    return 0;
} /* fjn_close */

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

static struct net_device_stats *fjn_get_stats(struct device *dev)
{
    local_info_t *lp = (local_info_t *)dev->priv;

    cli();
    sti();

    return &lp->stats;
} /* fjn_get_stats */

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

#ifdef NEW_MULTICAST
#define num_addrs dev->mc_count
static void set_multicast_list(struct device *dev)
#else
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
#endif /* NEW_MULTICAST */
{
    int ioaddr = dev->base_addr;

    if (num_addrs) {
	outb(RECV_ALL, ioaddr + RX_MODE);
    }else {
	outb(ID_MATCHED, ioaddr + RX_MODE);
    }
}

