/* bri_support.c:*/
/*
	Joel Katz -- PCBRI Emulator Control Code
	Portions Copyright 1995 -- Joel Katz
	Based upon code by Donald Becker which is
	Portions Copyright 1993 United States Government as 
	represented by the Director, National Security Agency.
	Portions Copyright 1995 -- Combinet, Inc.

   TODO:

   1) Move 'priv' code to other file.

  */

static char *version =
    "bri_supp.c:v1.02 -BETA- Joel Katz - Stimpson@Panix.COM\n";

#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/fs.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/string.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <linux/malloc.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/in.h>
#include <linux/interrupt.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>

#include "bri.h"

#define BRI_INB(x) bri_inb(ebri_base+28,x)
#define BRI_OUTB(x,y) bri_outb(ebri_base+28,x,y)

static void wait_for_em(unsigned short rdyport)
{
 int i=0;
 while(inb_p(rdyport)&1)
 {
  i++;
  if(i>30000) { return; }
 }
}

static inline void bri_outb(unsigned short rdyport,
 unsigned char val, unsigned short port)
{
 outb(val,port);
 wait_for_em(rdyport);
}

static inline unsigned short bri_inb(unsigned short rdyport,
 unsigned short port)
{
 unsigned short j;
 unsigned long f;
 save_flags(f);
 cli();
 inb(port);
 wait_for_em(rdyport);
 j=inb(port);
 wait_for_em(rdyport);
 restore_flags(f);
 return j;
}

#define bri_ei_reset_bri (bri_ei_local->reset_bri)
#define bri_ei_block_output (bri_ei_local->block_output)
#define bri_ei_block_input (bri_ei_local->block_input)

/* use 0 for production, 1 for verification, >2 for debug */
#ifdef EI_DEBUG
int bri_ei_debug = EI_DEBUG;
#else
int bri_ei_debug = 2;
#endif

/* Max number of packets received at one Intr.
   Currently this may only be examined by a kernel debugger. */
static int high_water_mark = 0;

/* Index to functions. */
static void bri_ei_tx_intr(struct device *dev);
static void bri_ei_receive(struct device *dev);
static void bri_ei_rx_overrun(struct device *dev);

/* Routines generic to bri-based boards. */
static void bri_trigger_send(struct device *dev, unsigned int length,
								int start_page);
#ifdef HAVE_MULTICAST
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
#endif


/* Open/initialize the board.  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.
   */
int bri_ei_open(struct device *dev)
{
    struct bri_ei_device *bri_ei_local = (struct bri_ei_device *) dev->priv;
    
    if ( ! bri_ei_local) {
		printk("%s: Opening a non-existent physical device\n", dev->name);
		return ENXIO;
    }
    
    irq2dev_map[dev->irq] = dev;
    bri_init(dev, 1);
    dev->start = 1;
    bri_ei_local->irqlock = 0;
    return 0;
}

static int bri_ei_start_xmit(struct sk_buff *skb, struct device *dev)
{
    int ebri_base = dev->base_addr;
    struct bri_ei_device *bri_ei_local = (struct bri_ei_device *) dev->priv;
    int length, send_length;
    unsigned long flags;
    
	/* We normally shouldn't be called if dev->tbusy is set, but the
	   existing code does anyway.
	   If it has been too long (> 100 or 150ms.) since the last Tx we assume
	   the board has died and kick it. */

    if (dev->tbusy) {	/* Do timeouts, just like the 8003 driver. */
		int txsr = BRI_INB(ebri_base+EN0_TSR), isr;
		int tickssofar = jiffies - dev->trans_start;
		if (tickssofar < 2	||	(tickssofar < 3 && ! 
		   (txsr & ENTSR_PTX))) {
			return 1;
		}
		isr = BRI_INB(ebri_base+EN0_ISR);
		if (dev->start == 0) {
			printk("%s: xmit on stopped card\n", dev->name);
			return 1;
		}
		printk("%s: transmit timed out, TX status %#2x, ISR %#2x.\n",
			   dev->name, txsr, isr);
		/* Does the bri thinks it has posted an interrupt? */
		if (isr)
printk("%s: BRI issued IRQ, but driver didn't get it.\n", dev->name);
		else {
			/* The bri probably hasn't intialized the line yet. */
			printk("%s: Possible ISDN line problem?\n", dev->name);
			if(bri_ei_local->stat.tx_packets==0)
				bri_ei_local->interface_num ^= 1; 	/* Try a different xcvr.  */
		}
		/* Try to restart the card.  
		   Perhaps the user has fixed something. */
		bri_ei_reset_bri(dev);
		bri_init(dev, 1);
		dev->trans_start = jiffies;
    }
    
    /* Sending a NULL skb means some higher layer thinks we've missed an
       tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
       itself. */
    if (skb == NULL) {
		dev_tint(dev);
		return 0;
    }
    
    length = skb->len;
    if (skb->len <= 0)
		return 0;

    save_flags(flags);
    cli();
    if((set_bit(0, (void *)&dev->tbusy)!=0)||bri_ei_local->irqlock)
	{
	 printk("BRI: TX access conflict\n");
	 restore_flags(flags);
	 return 1;
	}

    /* Mask interrupts from the ethercard. */
    BRI_OUTB(0x00, ebri_base + EN0_IMR);
    bri_ei_local->irqlock = 1;
    restore_flags(flags);

    send_length = ETH_ZLEN < length ? length : ETH_ZLEN;

    if (bri_ei_local->pingpong) {
		int output_page;
		if (bri_ei_local->tx1 == 0) {
			output_page = bri_ei_local->tx_start_page;
			bri_ei_local->tx1 = send_length;
			if (bri_ei_debug  &&  bri_ei_local->tx2 > 0)
				printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
					   dev->name, bri_ei_local->tx2, bri_ei_local->lasttx,
					   bri_ei_local->txing);
		} else if (bri_ei_local->tx2 == 0) {
			output_page = bri_ei_local->tx_start_page + 6;
			bri_ei_local->tx2 = send_length;
			if (bri_ei_debug  &&  bri_ei_local->tx1 > 0)
				printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
					   dev->name, bri_ei_local->tx1, bri_ei_local->lasttx,
					   bri_ei_local->txing);
		} else {	/* We should never get here. */
			printk("%s: No packet buffer space for ping-pong ??\n",
				   dev->name);
			bri_ei_local->irqlock = 0;
			dev->tbusy = 1;
			BRI_OUTB(ENISR_ALL,  ebri_base + EN0_IMR);
			return 1;
		}
		bri_ei_block_output(dev, length, skb->data, output_page);
		if (! bri_ei_local->txing) {
			bri_ei_local->txing=1;
			bri_trigger_send(dev, send_length, output_page);
			dev->trans_start = jiffies;
			if (output_page == bri_ei_local->tx_start_page)
				bri_ei_local->tx1 = -1, bri_ei_local->lasttx = -1;
			else
				bri_ei_local->tx2 = -1, bri_ei_local->lasttx = -2;
		} else
			bri_ei_local->txqueue++;

		dev->tbusy = (bri_ei_local->tx1  &&  bri_ei_local->tx2);
    } else {  /* No pingpong, just a single Tx buffer. */
    		bri_ei_local->txing=1;
		bri_ei_block_output(dev, length, skb->data, bri_ei_local->tx_start_page);
		bri_trigger_send(dev, send_length, bri_ei_local->tx_start_page);
		dev->trans_start = jiffies;
		dev->tbusy = 1;
    }
    
    /* Turn bri interrupts back on. */
    bri_ei_local->irqlock = 0;
    BRI_OUTB(ENISR_ALL, ebri_base + EN0_IMR);

    dev_kfree_skb (skb, FREE_WRITE);
    
    return 0;
}

/* The typical workload of the driver:
   Handle the ether interface interrupts. */
void bri_ei_interrupt(int irq, struct pt_regs * regs)
{
    struct device *dev = (struct device *)(irq2dev_map[irq]);
    int ebri_base;
    int interrupts, boguscount = 0;
    struct bri_ei_device *bri_ei_local;
    
    if (dev == NULL) {
		printk ("net_interrupt(): irq %d for unknown device.\n", irq);
		return;
    }
    ebri_base = dev->base_addr;
    bri_ei_local = (struct bri_ei_device *) dev->priv;
    if (dev->interrupt || bri_ei_local->irqlock) {
		/* The "irqlock" check is only for testing. */
		printk(bri_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, BRI_INB(ebri_base + EN0_ISR),
			   BRI_INB(ebri_base + EN0_IMR));
		return;
    }
    
    dev->interrupt = 1;
    
    /* Change to page 0 and read the intr status reg. */
    BRI_OUTB(Ebri_NODMA+Ebri_PAGE0, ebri_base + Ebri_CMD);
    if (bri_ei_debug > 3)
		printk("%s: interrupt(isr=%#2.2x).\n", dev->name,
			   BRI_INB(ebri_base + EN0_ISR));
    
    /* !!Assumption!! -- we stay in page 0.	 Don't break this. */
    while ((interrupts = BRI_INB(ebri_base + EN0_ISR)) != 0
		   && ++boguscount < 9) {
		if (dev->start == 0) {
			printk("%s: interrupt from stopped card\n", dev->name);
			interrupts = 0;
			break;
		}
		if (interrupts & ENISR_OVER) {
			bri_ei_rx_overrun(dev);
		} else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
			/* Got a good (?) packet. */
			bri_ei_receive(dev);
		}
		/* Push the next to-transmit packet through. */
		if (interrupts & ENISR_TX) {
			bri_ei_tx_intr(dev);
		} else if (interrupts & ENISR_COUNTERS) {
			bri_ei_local->stat.rx_frame_errors += BRI_INB(ebri_base + EN0_COUNTER0);
			bri_ei_local->stat.rx_crc_errors   += BRI_INB(ebri_base + EN0_COUNTER1);
			bri_ei_local->stat.rx_missed_errors+= BRI_INB(ebri_base + EN0_COUNTER2);
			BRI_OUTB(ENISR_COUNTERS, ebri_base + EN0_ISR); /* Ack intr. */
		}
		
		/* Ignore the transmit errs and reset intr for now. */
		if (interrupts & ENISR_TX_ERR) {
			BRI_OUTB(ENISR_TX_ERR, ebri_base + EN0_ISR); /* Ack intr. */
		}
		if (interrupts & ENISR_RDC) {
		        if(dev->mem_start)
			  BRI_OUTB(ENISR_RDC, ebri_base+EN0_ISR);
		}
		BRI_OUTB(Ebri_NODMA+Ebri_PAGE0+Ebri_START, ebri_base + Ebri_CMD);
    }
    
    if ((interrupts & ~ENISR_RDC) && bri_ei_debug) {
		BRI_OUTB(Ebri_NODMA+Ebri_PAGE0+Ebri_START, ebri_base + Ebri_CMD);
		if (boguscount == 36)
		{
			printk("%s: Too much work at interrupt, status %#2.2x\n",
				   dev->name, interrupts);
		 BRI_OUTB(ENISR_ALL, ebri_base+EN0_ISR);
		} else {
			printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
			BRI_OUTB(0xff,ebri_base+EN0_ISR);
		       }
    	}
    dev->interrupt = 0;
    return;
}

/* We have finished a transmit: check for errors and then trigger the next
   packet to be sent. */
static void bri_ei_tx_intr(struct device *dev)
{
    int ebri_base = dev->base_addr;
    int status = BRI_INB(ebri_base + EN0_TSR);
    struct bri_ei_device *bri_ei_local = (struct bri_ei_device *) dev->priv;
    
    BRI_OUTB(ENISR_TX, ebri_base + EN0_ISR); /* Ack intr. */
    
    if (bri_ei_local->pingpong) {
		bri_ei_local->txqueue--;
		if (bri_ei_local->tx1 < 0) {
			if (bri_ei_local->lasttx != 1 && bri_ei_local->lasttx != -1)
				printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
					   bri_ei_local->name, bri_ei_local->lasttx, bri_ei_local->tx1);
			bri_ei_local->tx1 = 0;
			dev->tbusy = 0;
			if (bri_ei_local->tx2 > 0) {
			        bri_ei_local->txing = 1;
				bri_trigger_send(dev, bri_ei_local->tx2, bri_ei_local->tx_start_page + 6);
				dev->trans_start = jiffies;
				bri_ei_local->tx2 = -1,
				bri_ei_local->lasttx = 2;
			} else
				bri_ei_local->lasttx = 20, bri_ei_local->txing = 0;
		} else if (bri_ei_local->tx2 < 0) {
			if (bri_ei_local->lasttx != 2  &&  bri_ei_local->lasttx != -2)
				printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
					   bri_ei_local->name, bri_ei_local->lasttx, bri_ei_local->tx2);
			bri_ei_local->tx2 = 0;
			dev->tbusy = 0;
			if (bri_ei_local->tx1 > 0) {
			        bri_ei_local->txing = 1;
				bri_trigger_send(dev, bri_ei_local->tx1, bri_ei_local->tx_start_page);
				dev->trans_start = jiffies;
				bri_ei_local->tx1 = -1;
				bri_ei_local->lasttx = 1;
			} else
				bri_ei_local->lasttx = 10, bri_ei_local->txing = 0;
		} else
			printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
				   dev->name, bri_ei_local->lasttx);
    } else {
		bri_ei_local->txing = 0;
		dev->tbusy = 0;
    }

    /* Minimize Tx latency: update the statistics after we restart TXing. */
	if (status & ENTSR_COL) bri_ei_local->stat.collisions++;
    if (status & ENTSR_PTX)
		bri_ei_local->stat.tx_packets++;
    else {
		bri_ei_local->stat.tx_errors++;
		if (status & ENTSR_ABT) bri_ei_local->stat.tx_aborted_errors++;
		if (status & ENTSR_CRS) bri_ei_local->stat.tx_carrier_errors++;
		if (status & ENTSR_FU)  bri_ei_local->stat.tx_fifo_errors++;
		if (status & ENTSR_CDH) bri_ei_local->stat.tx_heartbeat_errors++;
		if (status & ENTSR_OWC) bri_ei_local->stat.tx_window_errors++;
	}
    
    mark_bh(NET_BH);
}

/* We have a good packet(s), get it/them out of the buffers. */

static void bri_ei_receive(struct device *dev)
{
    int ebri_base = dev->base_addr;
    struct bri_ei_device *bri_ei_local = (struct bri_ei_device *) dev->priv;
    int rxing_page, this_frame, next_frame, current_offset;
    int rx_pkt_count = 0;
    struct ebri_pkt_hdr rx_frame;
    int num_rx_pages = bri_ei_local->stop_page-bri_ei_local->rx_start_page;
    
    while (++rx_pkt_count < 10) {
		int pkt_len;
		
		/* Get the rx page (incoming packet pointer). */
		BRI_OUTB(Ebri_NODMA+Ebri_PAGE1, ebri_base + Ebri_CMD);
		rxing_page = BRI_INB(ebri_base + EN1_CURPAG);
		BRI_OUTB(Ebri_NODMA+Ebri_PAGE0, ebri_base + Ebri_CMD);
		
		/* Remove one frame from the ring.  Boundary is alway a page behind. */
		this_frame = BRI_INB(ebri_base + EN0_BOUNDARY) + 1;
		if (this_frame >= bri_ei_local->stop_page)
			this_frame = bri_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.)  */
		if (bri_ei_debug > 0  &&  this_frame != bri_ei_local->current_page)
			printk("%s: mismatched read page pointers %2x vs %2x.\n",
				   dev->name, this_frame, bri_ei_local->current_page);
		
		if (this_frame == rxing_page)	/* Read all the frames? */
			break;				/* Done for now */
		
		current_offset = this_frame << 8;
		bri_ei_block_input(dev, sizeof(rx_frame), (char *)&rx_frame,
					   current_offset);
		
		pkt_len = rx_frame.count - sizeof(rx_frame);
		
		next_frame = this_frame + 1 + ((pkt_len+4)>>8);
		
		/* Check for bogosity warned by 3c503 book: the status byte is never
		   written.  This happened a lot during testing! This code should be
		   cleaned up someday. */
		if (rx_frame.next != next_frame
			&& rx_frame.next != next_frame + 1
			&& rx_frame.next != next_frame - num_rx_pages
			&& rx_frame.next != next_frame + 1 - num_rx_pages) {
			bri_ei_local->current_page = rxing_page;
			BRI_OUTB(bri_ei_local->current_page-1, ebri_base+EN0_BOUNDARY);
			bri_ei_local->stat.rx_errors++;
			continue;
		}

		if (pkt_len < 60  ||  pkt_len > 1518) {
			if (bri_ei_debug)
				printk("%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n",
					   dev->name, rx_frame.count, rx_frame.status,
					   rx_frame.next);
			bri_ei_local->stat.rx_errors++;
		} else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
			struct sk_buff *skb;
			
			skb = alloc_skb(pkt_len+2, GFP_ATOMIC);
			if (skb == NULL) {
				if (bri_ei_debug > 1)
					printk("%s: Couldn't allocate a sk_buff of size %d.\n",
						   dev->name, pkt_len);
				bri_ei_local->stat.rx_dropped++;
				break;
			} else {
				skb_reserve(skb,2);
				skb->len = pkt_len;
				skb->dev = dev;
				
				bri_ei_block_input(dev, pkt_len, (char *) skb->data,
							   current_offset + sizeof(rx_frame));
				netif_rx(skb);
				bri_ei_local->stat.rx_packets++;
			}
		} else {
			int errs = rx_frame.status;
			if (bri_ei_debug)
				printk("%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n",
					   dev->name, rx_frame.status, rx_frame.next,
					   rx_frame.count);
			if (errs & ENRSR_FO)
				bri_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 >= bri_ei_local->stop_page) {
			printk("%s: next frame inconsistency, %#2x..\n", dev->name,
				   next_frame);
			next_frame = bri_ei_local->rx_start_page;
		}
		bri_ei_local->current_page = next_frame;
		BRI_OUTB(next_frame-1, ebri_base+EN0_BOUNDARY);
    }
    /* 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. */

	/* Record the maximum Rx packet queue. */
	if (rx_pkt_count > high_water_mark)
		high_water_mark = rx_pkt_count;

    /* Bug alert!  Reset ENISR_OVER to avoid spurious overruns! */
    BRI_OUTB(ENISR_RX+ENISR_RX_ERR+ENISR_OVER, ebri_base+EN0_ISR);
    return;
}

/* We have a receiver overrun: we have to kick the bri to get it started
   again.*/
static void bri_ei_rx_overrun(struct device *dev)
{
    int ebri_base = dev->base_addr;
    int reset_start_time = jiffies;
    struct bri_ei_device *bri_ei_local = (struct bri_ei_device *) dev->priv;
    
    /* We should already be stopped and in page0.  Remove after testing. */
    BRI_OUTB(Ebri_NODMA+Ebri_PAGE0+Ebri_STOP, ebri_base+Ebri_CMD);
    
    if (bri_ei_debug > 1)
		printk("%s: Receiver overrun.\n", dev->name);
    bri_ei_local->stat.rx_over_errors++;
    
    /* Wait for the reset to complete.	This should happen almost instantly,
	   but could take up to 1.5msec in certain rare instances.  There is no
	   easy way of timing something in that range, so we use 'jiffies' as
	   a sanity check. Could crash, fix using loops_per_sec FIXME */
	   
    while ((BRI_INB(ebri_base+EN0_ISR) & ENISR_RESET) == 0)
		if (jiffies - reset_start_time > 2) {
			printk("%s: reset did not complete at ei_rx_overrun.\n",
				   dev->name);
			bri_init(dev, 1);
			return;
		}
    
    /* Remove packets right away. */
    bri_ei_receive(dev);
    
    BRI_OUTB(0xff, ebri_base+EN0_ISR);
    /* Generic bri insns to start up again, same as in open_bri(). */
    BRI_OUTB(Ebri_NODMA + Ebri_PAGE0 + Ebri_START, ebri_base + Ebri_CMD);
    BRI_OUTB(Ebri_TXCONFIG, ebri_base + EN0_TXCR); /* xmit on. */
}

static struct enet_statistics *get_stats(struct device *dev)
{
    short ioaddr = dev->base_addr;
    struct bri_ei_device *bri_ei_local = (struct bri_ei_device *) dev->priv;
    
    if(dev->start==0) return &bri_ei_local->stat;
    
    /* Read the counter registers, assuming we are in page 0. */
    bri_ei_local->stat.rx_frame_errors += bri_inb(ioaddr+28,ioaddr + EN0_COUNTER0);
    bri_ei_local->stat.rx_crc_errors   += bri_inb(ioaddr+28,ioaddr + EN0_COUNTER1);
    bri_ei_local->stat.rx_missed_errors+= bri_inb(ioaddr+28,ioaddr + EN0_COUNTER2);
    
    return &bri_ei_local->stat;
}

#ifdef HAVE_MULTICAST
/* Set or clear the multicast filter for this adaptor.
   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.
   */
static void set_multicast_list(struct device *dev, int num_addrs, void *addrs)
{
    short ioaddr = dev->base_addr;
    
    if (num_addrs > 0) {
		/* The multicast-accept list is initialized to accept-all, and we
		   rely on higher-level filtering for now. */
		bri_outb(ioaddr+28,Ebri_RXCONFIG | 0x08, ioaddr + EN0_RXCR);
    } else if (num_addrs < 0)
		bri_outb(ioaddr+28,Ebri_RXCONFIG | 0x18, ioaddr + EN0_RXCR);
    else
		bri_outb(ioaddr+28,Ebri_RXCONFIG, ioaddr + EN0_RXCR);
}
#endif

/* Initialize the rest of the bri device structure. */
int bri_ethdev_init(struct device *dev)
{
    if (bri_ei_debug > 1)
		printk(version);
    
    if (dev->priv == NULL) {
		struct bri_ei_device *bri_ei_local;
		
		dev->priv = kmalloc(sizeof(struct bri_ei_device), GFP_KERNEL);
		memset(dev->priv, 0, sizeof(struct bri_ei_device));
		bri_ei_local = (struct bri_ei_device *)dev->priv;
		bri_ei_local->pingpong = 1;
    }
    
    /* The open call may be overridden by the card-specific code. */
    if (dev->open == NULL)
		dev->open = &bri_ei_open;
    /* We should have a dev->stop entry also. */
    dev->hard_start_xmit = &bri_ei_start_xmit;
    dev->get_stats	= get_stats;
#ifdef HAVE_MULTICAST
    dev->set_multicast_list = &set_multicast_list;
#endif

    ether_setup(dev);
        
    return 0;
}


void bri_init(struct device *dev, int startp)
{
    int ebri_base = dev->base_addr;
    struct bri_ei_device *bri_ei_local = (struct bri_ei_device *) dev->priv;
    int i;
    int endcfg = (0x48 | ENDCFG_WTS);
    unsigned long flags;
    
    /* Follow National Semi's recommendations for init */
    BRI_OUTB(Ebri_NODMA+Ebri_PAGE0+Ebri_STOP, ebri_base); /* 0x21 */
    BRI_OUTB(endcfg, ebri_base + EN0_DCFG);	/* 0x48 or 0x49 */
    /* Clear the remote byte count registers. */
    BRI_OUTB(0x00,  ebri_base + EN0_RCNTLO);
    BRI_OUTB(0x00,  ebri_base + EN0_RCNTHI);
    /* Set to monitor and loopback mode -- this is vital!. */
    BRI_OUTB(Ebri_RXOFF, ebri_base + EN0_RXCR); /* 0x20 */
    BRI_OUTB(Ebri_TXOFF, ebri_base + EN0_TXCR); /* 0x02 */
    /* Set the transmit page and receive ring. */
    BRI_OUTB(bri_ei_local->tx_start_page, ebri_base + EN0_TPSR);
    bri_ei_local->tx1 = bri_ei_local->tx2 = 0;
    BRI_OUTB(bri_ei_local->rx_start_page, ebri_base + EN0_STARTPG);
    BRI_OUTB(bri_ei_local->stop_page-1, ebri_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
    bri_ei_local->current_page = bri_ei_local->rx_start_page;	/* assert boundary+1 */
    BRI_OUTB(bri_ei_local->stop_page,	  ebri_base + EN0_STOPPG);
    /* Clear the pending interrupts and mask. */
    BRI_OUTB(0xFF, ebri_base + EN0_ISR);
    BRI_OUTB(0x00,  ebri_base + EN0_IMR);
    
    /* Copy the station address into the DSbri registers,
       and set the multicast hash bitmap to receive all multicasts. */
    save_flags(flags);
    cli();
    BRI_OUTB(Ebri_NODMA + Ebri_PAGE1 + Ebri_STOP, ebri_base); /* 0x61 */
    for(i = 0; i < 6; i++) {
		BRI_OUTB(dev->dev_addr[i], ebri_base + EN1_PHYS + 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++)
		BRI_OUTB(0xff, ebri_base + EN1_MULT + i);
    
    BRI_OUTB(bri_ei_local->rx_start_page, ebri_base + EN1_CURPAG);
    BRI_OUTB(Ebri_NODMA+Ebri_PAGE0+Ebri_STOP, ebri_base);
    restore_flags(flags);
    dev->tbusy = 0;
    dev->interrupt = 0;
    bri_ei_local->tx1 = bri_ei_local->tx2 = 0;
    bri_ei_local->txing = 0;
    if (startp) {
		BRI_OUTB(0xff,  ebri_base + EN0_ISR);
		BRI_OUTB(ENISR_ALL,  ebri_base + EN0_IMR);
		BRI_OUTB(Ebri_NODMA+Ebri_PAGE0+Ebri_START, ebri_base);
		BRI_OUTB(Ebri_TXCONFIG, ebri_base + EN0_TXCR); /* xmit on. */
		BRI_OUTB(Ebri_RXCONFIG,	ebri_base + EN0_RXCR); /* rx on,  */
    }
   BRI_OUTB(0x49,ebri_base+14);

#ifdef LOOPBACK_ONLY
 /* This doesn't really work -- the BRI doesn't yet fully support loopback */
    BRI_OUTB(4,ebri_base+13);
    BRI_OUTB(28,ebri_base+12);
#endif
    return;
}

/* Trigger a transmit start, assuming the length is valid. */
static void bri_trigger_send(struct device *dev, unsigned int length,
								int start_page)
{
    int ebri_base = dev->base_addr;
    
    BRI_OUTB(Ebri_NODMA+Ebri_PAGE0, ebri_base);
    
    if (BRI_INB(ebri_base) & Ebri_TRANS) {
		printk("%s: trigger_send() called with the transmitter busy.\n",
			   dev->name);
		return;
    }
    BRI_OUTB(length & 0xff, ebri_base + EN0_TCNTLO);
    BRI_OUTB(length >> 8, ebri_base + EN0_TCNTHI);
    BRI_OUTB(start_page, ebri_base + EN0_TPSR);
    BRI_OUTB(Ebri_NODMA+Ebri_TRANS+Ebri_START, ebri_base);
    return;
}

#ifdef MODULE
char kernel_version[] = UTS_RELEASE;

int init_module(void)
{
     printk("PCBRI support routines installed\n");
     printk(version);
     return 0;
}

void
cleanup_module(void)
{
 printk("PCBRI support routines removed\n");
}
#endif /* MODULE */
