/*********************************************************************
 *                
 * Filename:      eagle_eye.c
 * Version:       0.1
 * Description:   Device driver for the VLSI VL82C147 PCI IrDA chipset
 * Status:        Experimental.
 * Author:        Dag Brattli <dagb@cs.uit.no>
 * Created at:    Fri May 21 12:11:08 1999
 * Modified at:   Tue Jun 22 11:44:49 1999
 * Modified by:   Dag Brattli <dagb@cs.uit.no>
 * 
 *     Copyright (c) 1999 Dag Brattli, All Rights Reserved.
 *     
 *     This program 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.
 * 
 *     This program 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
 *     
 ********************************************************************/

#include <linux/module.h>
 
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/skbuff.h>
#include <linux/netdevice.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/malloc.h>
#include <linux/init.h>
#include <linux/pci.h>

#include <asm/io.h>
#include <asm/dma.h>
#include <asm/byteorder.h>

#include <net/irda/irda.h>
#include <net/irda/irmod.h>
#include <net/irda/wrapper.h>
#include <net/irda/irda_device.h>

#include <net/irda/eagle_eye.h>

#undef USE_UART

#ifdef USE_UART
#include <net/irda/irport.h>
#endif /* USE_UART */

/* "Knobs" that adjust features and parameters. */
/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
   Setting to > 1512 effectively disables this feature. */
static const int rx_copybreak = 200;

/* Some values here only for performance evaluation and path-coverage
   debugging. */
static int rx_nocopy = 0, rx_copy = 0, queued_packet = 0;

static char *driver_name = "eagle_eye";
static int  qos_mtt_bits = 0x07;           /* 1 ms or more */

static int  io2 = 0x2f8; /* Address used by UART */

static struct eagle_eye_cb *dev_self = NULL;

/* Some prototypes */
static int  eagle_eye_open(struct pci_dev *pci);
static int  eagle_eye_close(struct irda_device *idev);
static int  eagle_eye_set_rx_mode(struct irda_device *idev); 
static void eagle_eye_set_tx_mode(struct irda_device *idev, int iobase);
static int  eagle_eye_receive(struct irda_device *idev);
static int  eagle_eye_hard_xmit(struct sk_buff *skb, struct device *dev);
static void eagle_eye_change_speed(struct irda_device *idev, int baud);
static void eagle_eye_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void eagle_eye_wait_until_sent(struct irda_device *idev);
static int  eagle_eye_is_receiving(struct irda_device *idev);
static void eagle_eye_init_buffers(struct eagle_eye_cb *self);
static int  eagle_eye_init_rings(struct eagle_eye_cb *self, int iobase);
static int  eagle_eye_init_chip(struct pci_dev *pci, int iobase);
static void eagle_eye_set_master(struct eagle_eye_cb *self, int status);
static int eagle_eye_start_clock(struct eagle_eye_cb *self);
static void eagle_eye_stop_clock(struct eagle_eye_cb *self);

static int  eagle_eye_net_init(struct device *dev);
static int  eagle_eye_net_open(struct device *dev);
static int  eagle_eye_net_close(struct device *dev);

/*
 * Function eagle_eye_init ()
 *
 *    Initialize chip. Just try to find out how many chips we are dealing with
 *    and where they are
 */
__initfunc(int eagle_eye_init(void))
{
	struct pci_dev *pci_dev = NULL;	
	int ret = -ENODEV;

	pci_dev = pci_find_device(PCI_VENDOR_ID_VLSI, 
				  PCI_DEVICE_ID_VLSI_82C147, 
				  pci_dev);
	if (pci_dev) {
		MESSAGE("VLSI: Found 82C147 chip at 0x%0lx irq %d\n",
			pci_dev->base_address[0] & PCI_BASE_ADDRESS_IO_MASK,
			pci_dev->irq);
		
		ret = eagle_eye_open(pci_dev);
	}	
	return ret;
}

/*
 * Function eagle_eye_cleanup ()
 *
 *    Close all configured chips
 *
 */
#ifdef MODULE
void eagle_eye_cleanup(void)
{
	int i;

        DEBUG(4, __FUNCTION__ "()\n");

	if (dev_self)
		eagle_eye_close(&(dev_self->idev));
}
#endif /* MODULE */

/*
 * Function eagle_eye_open (iobase, irq)
 *
 *    Open driver instance
 *
 */
int eagle_eye_open(struct pci_dev *pci)
{
	struct irda_device *idev;
        struct eagle_eye_cb *self;
	int ret;

	DEBUG(0, __FUNCTION__ "()\n");

	/*
	 *  Allocate new instance of the driver
	 */
	self = kmalloc(sizeof(struct eagle_eye_cb), GFP_KERNEL);
	if (self == NULL) {
		printk( KERN_ERR "IrDA: Can't allocate memory for "
			"IrDA control block!\n");
		return -ENOMEM;
	}
	memset(self, 0, sizeof(struct eagle_eye_cb));
   
	/* Need to store self somewhere */
	dev_self = self;

	self->pdev = pci;
	self->base = pci->base_address[0] & PCI_BASE_ADDRESS_IO_MASK;

	idev = &self->idev;

	/* Initialize IO */
	idev->io.iobase    = self->base;
	idev->io.iobase2   = io2;
        idev->io.irq       = pci->irq;
        idev->io.io_ext    = CHIP_IO_EXTENT;
	idev->io.fifo_size = 16;

	/* Lock the port that we need */
	ret = check_region(idev->io.iobase, idev->io.io_ext);
	if (ret < 0) { 
		DEBUG( 0, __FUNCTION__ "(), can't get iobase of 0x%03x\n",
		       idev->io.iobase);
		/* eagle_eye_cleanup( self->idev);  */
		return -ENODEV;
	}
	request_region(idev->io.iobase, idev->io.io_ext, idev->name);

        /* Initialize QoS for this device */
	irda_init_max_qos_capabilies(&idev->qos);
	
	/* The only value we must override it the baudrate */

	/* The HP HDLS-1100 does not support 1152000! */
	idev->qos.baud_rate.bits = IR_9600|IR_19200|IR_38400|IR_57600|
		IR_115200|IR_576000|IR_1152000|(IR_4000000 << 8);

	/* The HP HDLS-1100 needs 1 ms according to the specs */
	idev->qos.min_turn_time.bits = qos_mtt_bits;
	irda_qos_bits_to_value(&idev->qos);
	
	idev->flags = IFF_FIR|IFF_MIR|IFF_SIR|IFF_DMA;
#ifdef USE_UART
	idev->flags |= IFF_PIO;

	/* Specify which buffer allocation policy we need */
	idev->rx_buff.flags = GFP_KERNEL;
	idev->tx_buff.flags = GFP_KERNEL;

	idev->rx_buff.truesize = 4000; 
	idev->tx_buff.truesize = 4000;
#endif	
	/* Initialize callbacks */
	idev->change_speed           = eagle_eye_change_speed;
	idev->wait_until_sent        = eagle_eye_wait_until_sent;
        idev->is_receiving           = eagle_eye_is_receiving;

	/* Override the network functions we need to use */
	idev->netdev.init            = eagle_eye_net_init;
	idev->netdev.hard_start_xmit = eagle_eye_hard_xmit;
	idev->netdev.open            = eagle_eye_net_open;
	idev->netdev.stop            = eagle_eye_net_close;

	eagle_eye_set_master(self, TRUE);
	eagle_eye_start_clock(self);
	eagle_eye_init_chip(pci, self->base);
	eagle_eye_init_rings(self, self->base);

	/* Open the IrDA device */
	irda_device_open(idev, driver_name, self);

	return 0;
}

/*
 * Function eagle_eye_close (idev)
 *
 *    Close driver instance
 *
 */
static int eagle_eye_close(struct irda_device *idev)
{
	struct eagle_eye_cb *self;
	int iobase;
	int i;

	DEBUG(0, __FUNCTION__ "()\n");

	ASSERT(idev != NULL, return -1;);
	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);

        iobase = idev->io.iobase;
	self = (struct eagle_eye_cb *) idev->priv;

	eagle_eye_set_master(self, FALSE);

	eagle_eye_stop_clock(self);

	/* Release the PORT that this driver is using */
	DEBUG(0, __FUNCTION__ "(), Releasing Region %03x\n", idev->io.iobase);
	release_region(idev->io.iobase, idev->io.io_ext);

	irda_device_close(idev);

	/* Free queued frames */
	for (i=0;i<TX_RING_SIZE;i++) {
		if (self->tx_buf[i])
			kfree(self->tx_buf[i]);
	}
	for (i=0;i<RX_RING_SIZE;i++) {
		if (self->rx_buf[i])
			kfree(self->rx_buf[i]);
	}
	
	if (self->ring_mem)
		kfree(self->ring_mem);

	kfree(self);

	return 0;
}

/*
 * Function eagle_eye_set_master (self, status)
 *
 *    Enable (or disable) the ability for the chip to act as a master on
 *    the PCI bus.
 */
static void eagle_eye_set_master(struct eagle_eye_cb *self, int status)
{
	__u8 command;

	DEBUG (0, __FUNCTION__ "()\n");

	if (status)
		pci_set_master(self->pdev);
	else {
		pci_read_config_byte (self->pdev, PCI_COMMAND, &command);
		command &= ~PCI_COMMAND_MASTER;
		pci_write_config_byte (self->pdev, PCI_COMMAND, command);
	}
}

/*
 * Function eagle_eye_probe (iobase, irq, dma)
 *
 *    Returns non-negative on success.
 *
 */
static int eagle_eye_init_chip(struct pci_dev *pci, int iobase)
{
	__u8 misc;
	__u16 vendor_id;
	__u32 irbase;

	DEBUG(0, "VLSI VL82C147 (Eagle Eye) Driver loaded\n");

	pci_read_config_word(pci, 0, &vendor_id);

	DEBUG(0, __FUNCTION__ "(), vendor_id=%#x\n", vendor_id);

#ifdef USE_UART	
	pci_read_config_byte(pci, PCI_IRMISC, &misc);

	DEBUG(0, __FUNCTION__ "(), misc=0x%02x\n", misc);

	/* Enable UART address decoding */
	pci_write_config_byte(pci, PCI_IRMISC, misc|IRMISC_UART_EN);

	/* Disable Master enable */
	outb(0, iobase+IRCFG);
#endif
	/* Set master page */
	pci_write_config_byte(pci, PCI_MSTRPAGE, 0);

	/* FIXME: */
	outb(0, iobase+IRENABLE);
	outw(2048, iobase+MAX_PACKET_LEN);

	return 0;
}


#define EXTERNAL_CLOCK
static int eagle_eye_start_clock(struct eagle_eye_cb *self)
{
	__u8 lock=0;
	__u8 clkctl;
	int count = 0;
	int bogus = 0;

	DEBUG(0, __FUNCTION__ "()\n");

	/* Power up PLL, but don't start clock yet */
	clkctl = CLK_CTL_PD | CLK_CTL_CLKSTP;
#ifdef EXTERNAL_CLOCK
	clkctl |= CLK_CTL_EXTCLK;
#endif
	pci_write_config_byte(self->pdev, PCI_CLKCTL, clkctl);
	
	udelay(450);

	/* Must get a lock success 3 times before we trust it */
	while (!lock && (count < 3)) {
		udelay(50);
		pci_read_config_byte(self->pdev, PCI_CLKCTL, &lock);
		DEBUG(0, __FUNCTION__ "clkctl=0x%02x\n", lock);
		lock &= CLK_CTL_LOCK;
		if (lock)
			count++;
		if (bogus++ > 300)
			break;
	}
	if (!lock)
		return -1;

	/* PLL clock should now be stabilized, so enable clock */
	pci_write_config_byte(self->pdev, PCI_CLKCTL, CLK_CTL_PD);	

	return 0;
}

static void eagle_eye_stop_clock(struct eagle_eye_cb *self)
{
	DEBUG(0, __FUNCTION__ "()\n");

	/* Stop clock and power down PLL */
	pci_write_config_byte(self->pdev, PCI_CLKCTL, CLK_CTL_CLKSTP);
}

/*
 * Function eagle_eye_init_buffers (self)
 *
 *    Set up the buffers
 *
 */
static void eagle_eye_init_buffers(struct eagle_eye_cb *self)
{
	__u8 *buf;
	int i;

	DEBUG(0, __FUNCTION__ "()\n");

	for (i=0; i<RX_RING_SIZE; i++) {
		buf = kmalloc(MAX_FRAME_SIZE, GFP_ATOMIC|GFP_DMA);
		if (!buf) {
			ERROR(__FUNCTION__ "(), unable to kmalloc!\n");
			return;
		}
		self->rx_buf[i] = buf;
		self->rx_ring[i].u.addr = virt_to_bus(buf);
		self->rx_ring[i].u.s.status = RX_STAT_ACTIVE;		
	}
	for (i=0; i<TX_RING_SIZE; i++) {
		buf = kmalloc(MAX_FRAME_SIZE, GFP_ATOMIC|GFP_DMA);
		if (!buf) {
			ERROR(__FUNCTION__ "(), unable to kmalloc!\n");
			return;
		}
		self->tx_buf[i] = buf;
		self->tx_ring[i].u.addr = virt_to_bus(buf);
		self->tx_ring[i].u.s.status = 0;	       
	}
}

/*
 * Function eagle_eye_init_rings (self)
 *
 *    Prepare Tx and Rx DMA rings and buffers
 *
 */
static int eagle_eye_init_rings(struct eagle_eye_cb *self, int iobase)
{
	__u32 physaddr;
	int size;

	DEBUG(0, __FUNCTION__ "()\n");

	/* Allocate memory for ring descriptors */
	size = 1024 + /* Must align to 1K */
		512 + /* Tx ring is 512 bytes after Rx ring */
		sizeof(struct ring_entry) * TX_RING_SIZE;

	self->ring_mem = kmalloc(size, GFP_KERNEL | GFP_DMA);
	if (!self->ring_mem) {
		ERROR(__FUNCTION__ "(), unable to kmalloc!\n");
		return -ENOMEM;
	}
		
	DEBUG(0, __FUNCTION__ "(), self->ring_mem=%p\n", self->ring_mem);

	/* Align address to 1K */
	self->ring_base = (__u8 *)(((__u32) self->ring_mem) & ~(1024-1));
	self->ring_base += 1024;

	DEBUG(0, __FUNCTION__ "(), self->ring_base=%p\n", self->ring_base);

	self->rx_ring = (struct ring_entry *) self->ring_base;
	self->tx_ring = (struct ring_entry *) self->ring_base + 512;

	physaddr = virt_to_bus(self->ring_base);

	DEBUG(0, __FUNCTION__ "(), physaddr=%#x\n", physaddr);

	/* Tell chip where the ring buffers are */
	outw((physaddr >> 10) & 0x3fff, iobase+RING_BASE);
	
	/* Tell chip the size of the rings */
	outb(RING_SIZE_TX_8 | RING_SIZE_RX_8, iobase+RING_SIZE);	

	/* Now, we prepare the Rx buffers (skb's) */
	eagle_eye_init_buffers(self);

	return 0;
}

/*
 * Function eagle_eye_change_speed (idev, baud)
 *
 *    Change the speed of the device
 *
 */
void eagle_eye_change_speed(struct irda_device *idev, int speed)
{
	unsigned long flags;
	__u16 nphyctl;
	int iobase; 
	__u16 config;

	DEBUG(0, __FUNCTION__ "(), speed=%d\n", speed);

	ASSERT(idev != NULL, return;);
	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);

	iobase = idev->io.iobase;

	/* Serialize access to the driver */
	spin_lock_irqsave(&idev->lock, flags);

	/* Update accounting for new speed */
	idev->io.baudrate = speed;

#ifdef USE_UART
	/* Install the correct transmit handler */
	if (speed > 115200)
		/* Install the FIR transmit handler */
		idev->netdev.hard_start_xmit = &eagle_eye_hard_xmit;
	else		
		/* Install the SIR transmit handler */
		idev->netdev.hard_start_xmit = &irport_hard_xmit;

	irport_change_speed(idev, speed);
	
	goto out; /* Safest way to make sure irq and lock are given back */
#endif
	/* Disable IR */
	outb(0, iobase+IRENABLE);

	/* Default configuration */
	config = IRCFG_MASTER | IRCFG_RX_ANY | IRCFG_SIR | IRCFG_SIR_FILT | 
		IRCFG_ENRX;
	
	switch (speed) {
	case 2400:
		nphyctl = NPHYCTL_2400;
		break;
	case 9600:
	default:
		nphyctl = NPHYCTL_9600;
		break;		
	case 19200:
		nphyctl = NPHYCTL_19200;
		break;		
	case 38400:
		nphyctl = NPHYCTL_38400;
		break;		
	case 57600:
		nphyctl = NPHYCTL_57600;
		break;	       
	case 115200:
		nphyctl = NPHYCTL_115200;
		break;
	case 1152000:
		nphyctl = NPHYCTL_MIR;
		config = IRCFG_MASTER | IRCFG_CRC16 | IRCFG_MIR;
		break;		
	case 4000000:
		nphyctl = NPHYCTL_FIR;
		config = IRCFG_MASTER | IRCFG_FIR;
		break;
	}

	/* Set physical configuration */
	DEBUG(0, __FUNCTION__ "(), nphyctl=%#x\n", nphyctl);
	outw(nphyctl, iobase+NPHYCTL);
	DEBUG(0, __FUNCTION__ "(), nphyctl=%#x\n", inw(iobase+NPHYCTL));
	
	/* Set chip configuration */
	DEBUG(0, __FUNCTION__ "(), config=%#x\n", config);
	outw(config, iobase+IRCFG);
	DEBUG(0, __FUNCTION__ "(), config=%#x\n", inw(iobase+IRCFG));
	
	/* Enable IR */
	outb(IRENABLE_IREN, iobase+IRENABLE);
 out:
	/* Release control of the driver */
	spin_unlock_irqrestore(&idev->lock, flags);
}

/*
 * Function eagle_eye_hard_xmit (skb, dev)
 *
 *    Sets up a DMA transfer to send the current frame.
 *
 */
int eagle_eye_hard_xmit(struct sk_buff *skb, struct device *dev)
{
	struct eagle_eye_cb *self;
	struct irda_device *idev;
	unsigned long flags;
	struct timeval now;
	int iobase;
	__u8 status;
	int mtt, diff;
	int entry;
	int len;

	idev = (struct irda_device *) dev->priv;

	ASSERT(idev != NULL, return 0;);
	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return 0;);

	self = idev->priv;
	iobase = idev->io.iobase;

	DEBUG(0, __FUNCTION__ "(%ld), skb->len=%d\n", jiffies, (int) skb->len);

	DEBUG(0, __FUNCTION__ "(), dirty_tx=%d, cur_tx=%d\n", self->dirty_tx,
	      self->cur_tx);
	
	/* Lock transmit buffer */
	if (irda_lock((void *) &dev->tbusy) == FALSE)
		return -EBUSY;

	/* Serialize access to the driver */
	spin_lock_irqsave(&idev->lock, flags);

	status = TX_STAT_CLR_ENTX | TX_STAT_ACTIVE;

	/* Calculate the Tx descriptor entry index */
	entry = self->cur_tx % TX_RING_SIZE;

	DEBUG(0, __FUNCTION__ "(), entry=%d\n", entry);

	/* Do not set CRC for SIR mode */
	if (idev->io.baudrate <= 115200 ) {
		status |= TX_STAT_DISABLE_CRC;

		len = async_wrap_skb(skb, self->tx_buf[entry], MAX_FRAME_SIZE);
	} else {
		len = skb->len;
		memcpy(self->tx_buf[entry], skb->data, len);
	}
	
	self->tx_ring[entry].count = len;
	self->tx_ring[entry].u.s.status = status;

	self->cur_tx++;
	if (self->cur_tx - self->dirty_tx > TX_RING_SIZE - 1)
		self->tx_full = 1;

	/* Start transmit only if there is currently no transmit going on */
	if ((self->cur_tx - self->dirty_tx) == 1) {
		/* Extract minimum turnaround time from skb */
		mtt = irda_get_mtt(skb);
		
		/* Currently we just use udelay for the MTT delay */
		if (mtt) {
			/* Check how much time we have used already */
			do_gettimeofday(&now);
			diff = now.tv_usec - self->stamp.tv_usec;
			if (diff < 0) 
				diff += 1000000;
			
			/* Check if the mtt is larger than the time we have
			 * already used by all the protocol processing
			 */
			if (mtt > diff) {
				DEBUG(4, __FUNCTION__ "(), delay=%ld us\n", 
				      mtt-diff);
				/* Currently we just use udelay for the MTT
                                 * delay 
				 */
				udelay(mtt-diff);
			}
		}
		eagle_eye_set_tx_mode(idev, iobase);

		/* Start transmit */
		outw(0, iobase+PROMPT);
	}	

	/* Request more frames if Tx ring is not full */
	if (!self->tx_full)
		/* Not busy transmitting anymore */
		dev->tbusy = 0;

	spin_unlock_irqrestore(&idev->lock, flags);

	DEBUG(0, __FUNCTION__ "(), done, dirty_tx=%d, cur_tx=%d\n", 
	      self->dirty_tx, self->cur_tx);

	return 0;
}

/*
 * Function eagle_eye_tx_begin (idev, iobase)
 *
 *    Prepare for transmission
 *
 */
static void eagle_eye_set_tx_mode(struct irda_device *idev, int iobase)
{
        DEBUG(0, __FUNCTION__ "()\n");

	/* Clear the IR enable bit */
	outb(0, iobase+IRENABLE);
	
	/* Enable Tx and Rx */
	outw(inw(iobase+IRCFG) | IRCFG_ENTX | IRCFG_ENRX | IRCFG_MASTER, 
	     iobase+IRCFG);
 
	/* Clear all interrupt bits by writing a one into them */
	outb(IRINTR_INT_MASK, iobase+IRINTR);

	/* Enable Rx and Tx interrupts */
	outb(IRINTR_TPKT_EN | IRINTR_RPKT_EN, iobase+IRINTR);
	
	/* Set the IR enable bit */
	outb(IRENABLE_IREN, iobase+IRENABLE);
}

/*
 * Function eagle_eye_dma_receive (idev)
 *
 *    Get ready for receiving a frame. The device will initiate a DMA
 *    if it starts to receive a frame.
 *
 */
int eagle_eye_set_rx_mode(struct irda_device *idev) 
{
	struct eagle_eye_cb *self;
	int iobase;

	ASSERT(idev != NULL, return -1;);
	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return -1;);

	DEBUG(0, __FUNCTION__ "\n");

	self = idev->priv;
	iobase= idev->io.iobase;
	
	/* Enable Rx */
	outw(inw(iobase+IRCFG) | IRCFG_ENRX | IRCFG_MASTER, iobase+IRCFG);

	/* Clear all interrupt bits by writing a one into them */
	outb(IRINTR_INT_MASK, iobase+IRINTR);
	
	/* Enable Rx and Tx interrupts */
	outb(IRINTR_TPKT_EN | IRINTR_RPKT_EN, iobase+IRINTR);

	/* Start Receive */
	outw(0, iobase+PROMPT);

	return 0;
}

/*
 * Function eagle_eye_dma_xmit_complete (idev)
 *
 *    The transfer of a frame in finished. So do the necessary things
 *
 *    
 */
void eagle_eye_xmit_complete(struct irda_device *idev)
{
	struct eagle_eye_cb *self;
	unsigned int dirty_tx;
	int iobase;

	ASSERT(idev != NULL, return;);
	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return;);

	self = idev->priv;
	iobase = idev->io.iobase;

	DEBUG(0, __FUNCTION__ "(), dirty_tx=%d, cur_tx=%d\n", self->dirty_tx,
	      self->cur_tx);

	dirty_tx = self->dirty_tx;

	while (self->cur_tx - dirty_tx > 0) {
		int entry = dirty_tx % TX_RING_SIZE;
		
		/* Check if this entry have been processed yet */
		if (inb(iobase+RING_PTR_TX) == entry)
			break;
		dirty_tx++;
	}

	self->dirty_tx = dirty_tx;
	if (self->tx_full && (self->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
		self->tx_full= 0;
		idev->netdev.tbusy = 0;
		mark_bh(NET_BH);
	}
}

/*
 * Function eagle_eye_receive (idev)
 *
 *    Finished with receiving a frame
 *
 */
int eagle_eye_receive(struct irda_device *idev)
{
	struct eagle_eye_cb *self = idev->priv;
	int entry = self->cur_rx % RX_RING_SIZE;
	int iobase = idev->io.iobase;
	__u8 rx_status;

	DEBUG(0, __FUNCTION__ "(), dirty_rx=%d, cur_rx=%d\n", self->dirty_rx,
	      self->cur_rx);

	while (!((rx_status = self->rx_ring[entry].u.s.status) & 
		 RX_STAT_ACTIVE)) 
	{
		if (rx_status & RX_STAT_ANY_ERR) { /* Error, update stats. */
			DEBUG(0, __FUNCTION__ "(), Rx error: status %2x.\n",
			      rx_status);
			idev->stats.rx_errors++;
			if (rx_status & RX_STAT_OVERRUN)  
				idev->stats.rx_over_errors++;
			if (rx_status & RX_STAT_LENGTH)  
				idev->stats.rx_length_errors++;
			if (rx_status & RX_STAT_PHYERR)  
				idev->stats.rx_frame_errors++;
			if (rx_status & RX_STAT_CRCERR)  
				idev->stats.rx_crc_errors++;
		} else {
			short frame_len = self->rx_ring[entry].count;
			struct sk_buff *skb;

			idev->stats.rx_bytes += frame_len;
			DEBUG(0, __FUNCTION__ 
			      "(), Receiving packet size %d status %4.4x.\n",
			      frame_len, rx_status);

			/* 
			 * Check if the packet is long enough to just
			 * accept without copying to a properly sized
			 * skbuff. 
			 */
			skb = dev_alloc_skb(frame_len+1);
			if (!skb) {
				ERROR(__FUNCTION__"(), unable to alloc skb\n");
				break;
			}

			skb->dev = &idev->netdev;
			skb->mac.raw = skb->data;
			skb->protocol = htons (ETH_P_IRDA);
			
		       	/* Align IP on 16 byte boundaries */
			skb_reserve(skb, 1);
			
			memcpy(skb_put(skb, frame_len),
			       bus_to_virt(self->rx_ring[entry].u.addr),
			       frame_len);			

			/* Got a frame, so remember the time */
			do_gettimeofday(&self->stamp);
			skb->len = frame_len;
			netif_rx(skb);
			idev->netdev.last_rx = jiffies;
			idev->stats.rx_packets++;
		}
		entry = (++self->cur_rx) % RX_RING_SIZE;
	}
	DEBUG(0, __FUNCTION__ "(), done! dirty_rx=%d, cur_rx=%d\n", 
	      self->dirty_rx, self->cur_rx);

	return 0;
}

/*
 * Function eagle_eye_interrupt (irq, dev_id, regs)
 *
 *    An interrupt from the chip has arrived. Time to do some work
 *
 */
static void eagle_eye_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
	struct irda_device *idev = (struct irda_device *) dev_id;
	int boguscount = 0;
	__u8 irintr;
	int iobase;

	if (idev == NULL) {
		WARNING("%s: irq %d for unknown device.\n", driver_name, irq);
		return;
	}

#ifdef USE_UART
	if (idev->io.baudrate <= 115200)
		return irport_interrupt(irq, dev_id, regs);
#endif
	spin_lock(&idev->lock);

	idev->netdev.interrupt = 1;

	iobase = idev->io.iobase;

	irintr = inb(iobase+IRINTR) & IRINTR_INT_MASK;

	DEBUG(0, __FUNCTION__ "(), irintr = 0x%02x\n", irintr);
	while (irintr) {
		/* Check if we have received any frames */
		if (irintr & IRINTR_RPKT_INT) {
			eagle_eye_receive(idev);

			/* Clear interrupt */
			outb(inb(iobase+IRINTR) | IRINTR_RPKT_INT, 
			     iobase+IRINTR);
		}

		/* Check if finished transmitting a frame */
		if (irintr & IRINTR_TPKT_INT) {
			eagle_eye_xmit_complete(idev);

			/* Clear interrupt */
			outb(inb(iobase+IRINTR) | IRINTR_TPKT_INT, 
			     iobase+IRINTR);
		}

		if (boguscount++ > 32) {
			DEBUG(0, __FUNCTION__ "(), breaking!\n");
			break;
		}
		irintr = inb(iobase+IRINTR) & IRINTR_INT_MASK;
	}
	idev->netdev.interrupt = 0;

	spin_unlock(&self->lock);
}

/*
 * Function eagle_eye_wait_until_sent (idev)
 *
 *    This function should put the current thread to sleep until all data 
 *    have been sent, so it is safe to f.eks. change the speed.
 */
static void eagle_eye_wait_until_sent(struct irda_device *idev)
{
	current->state = TASK_INTERRUPTIBLE;
	schedule_timeout(60*HZ/1000);
}

/*
 * Function eagle_eye_is_receiving (idev)
 *
 *    Return TRUE is we are currently receiving a frame
 *
 */
static int eagle_eye_is_receiving(struct irda_device *idev)
{
	int status = FALSE;
	int iobase;

	ASSERT(idev != NULL, return FALSE;);
	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return FALSE;);

	if (idev->io.baudrate > 115200) {
		iobase = idev->io.iobase;

	} else 
		status = (idev->rx_buff.state != OUTSIDE_FRAME);
	
	return status;
}

/*
 * Function eagle_eye_net_init (dev)
 *
 *    
 *
 */
static int eagle_eye_net_init(struct device *dev)
{
	DEBUG(0, __FUNCTION__ "()\n");

	/* Set up to be a normal IrDA network device driver */
	irda_device_setup(dev);

	/* Insert overrides below this line! */

	return 0;
}


/*
 * Function eagle_eye_net_open (dev)
 *
 *    Start the device
 *
 */
static int eagle_eye_net_open(struct device *dev)
{
	struct irda_device *idev;
	int iobase;
	
	DEBUG(0, __FUNCTION__ "()\n");
	
	ASSERT(dev != NULL, return -1;);
	idev = (struct irda_device *) dev->priv;
	
	ASSERT(idev != NULL, return 0;);
	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return 0;);
	
	iobase = idev->io.iobase;

	if (request_irq(idev->io.irq, eagle_eye_interrupt, 0, idev->name, 
			 (void *) idev)) {
		return -EAGAIN;
	}
		
	/* Ready to play! */
	dev->tbusy = 0;
	dev->interrupt = 0;
	dev->start = 1;
#ifdef USE_UART
	/* Start SIR driver */
	irport_start(idev, idev->io.iobase2);
#endif
 	/* Enable some interrupts so we can receive frames again */
 
	MOD_INC_USE_COUNT;

	return 0;
}

/*
 * Function eagle_eye_net_close (dev)
 *
 *    Stop the device
 *
 */
static int eagle_eye_net_close(struct device *dev)
{
	struct irda_device *idev;
	int iobase;

	DEBUG(0, __FUNCTION__ "()\n");
	
	/* Stop device */
	dev->tbusy = 1;
	dev->start = 0;

	ASSERT(dev != NULL, return -1;);
	idev = (struct irda_device *) dev->priv;
	
	ASSERT(idev != NULL, return 0;);
	ASSERT(idev->magic == IRDA_DEVICE_MAGIC, return 0;);
	
	iobase = idev->io.iobase;

#ifdef USE_UART
	/* Stop SIR driver */
	irport_stop(idev, idev->io.iobase2);
#endif
	free_irq(idev->io.irq, idev);

	MOD_DEC_USE_COUNT;

	return 0;
}

#ifdef MODULE

MODULE_AUTHOR("Dag Brattli <dagb@cs.uit.no>");
MODULE_DESCRIPTION("VLSI VL82C147 IrDA Device Driver");

MODULE_PARM(qos_mtt_bits, "i");

/*
 * Function init_module (void)
 *
 *    
 *
 */
int init_module(void)
{
	return eagle_eye_init();
}

/*
 * Function cleanup_module (void)
 *
 *    
 *
 */
void cleanup_module(void)
{
	eagle_eye_cleanup();
}
#endif /* MODULE */
