/* plip.c: A parallel port "network" driver for linux. */
/*
    Written 1993 by Donald Becker.  This is unreleased software.
    
    This is parallel port packet pusher.  It's actually more general
    than the "IP" in its name suggests -- but 'plip' is just such a
    great name!

    The Author may be reached as becker@super.org or
    C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
*/

static char *version =
    "plip.c:v0.04 Mar 19 1993 Donald Becker (becker@super.org)\n";

#include <linux/config.h>

/*
  Sources:
	Ideas and protocols came from Russ Nelson's (nelson@crynwr.com)
	"parallel.asm" parallel port packet driver.
  The "Crynwr" parallel port standard specifies the following protocol:
   send header nibble '8'
   count-low octet
   count-high octet
   ... data octets
   checksum octet
Each octet is sent as <wait for rx. '1'> <send 0x10+(octet&0x0F)>
			<wait for rx. '0'> <send 0x00+((octet>>4)&0x0F)>
The cable used is a de facto standard parallel null cable -- sold as
a "LapLink" cable by various places.  You'll need a 10-conductor cable to
make one yourself.  The wiring is:
    INIT	16 - 16		SLCTIN	17 - 17
    GROUND	25 - 25
    D0->ERROR	2 - 15		15 - 2
    D1->SLCT	3 - 13		13 - 3
    D2->PAPOUT	4 - 12		12 - 4
    D3->ACK	5 - 10		10 - 5
    D4->BUSY	6 - 11		11 - 6
  Do not connect the other pins.  They are
    D5,D6,D7 are 7,8,9
    STROBE is 1, FEED is 14
    extra grounds are 18,19,20,21,22,23,24
*/

#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/ptrace.h>
#include <asm/system.h>
#include <asm/io.h>
#include <linux/in.h>
#include <errno.h>

#include "dev.h"
#include "eth.h"
#include "timer.h"
#include "ip.h"
#include "protocol.h"
#include "tcp.h"
#include "skbuff.h"
#include "sock.h"
#include "arp.h"

/* use 0 for production, 1 for verification, >2 for debug */
#ifndef PLIP_DEBUG
#define PLIP_DEBUG 9
#endif
static unsigned int plip_debug = PLIP_DEBUG;

/* The map from IRQ number (as passed to the interrupt handler) to
   'struct device'. */
extern struct device *irq2dev_map[16];

#define PAR_DATA	0
#define PAR_STATUS	1
#define PAR_CONTROL	2
/* Common network statistics -- these will be in *.h someday. */
struct netstats {
    int tx_packets;
    int rx_packets;
    int tx_errors;
    int rx_errors;
    int missed_packets;
    int soft_tx_errors;
    int soft_rx_errors;
    int soft_trx_err_bits;
};
static struct netstats *localstats;

/* Index to functions, as function prototypes. */
extern int plip_probe(int ioaddr, struct device *dev);
/* Put in the device structure. */
static int plip_open(struct device *dev);
static int plip_close(struct device *dev);
static int plip_tx_packet(struct sk_buff *skb, struct device *dev);

/* Routines used internally. */
/* Dispatch from interrupts. */
static void plip_interrupt(int reg_ptr);
static int plip_write(struct device *dev, unsigned char *buf, int length);

int
plip_init(struct device *dev)
{
    int i;

    /* Alpha testers must have the version number to report bugs. */
    if (plip_debug > 1) {
	static int version_shown = 0;
	if (! version_shown)
	    printk(version), version_shown++;
    }

    /* We don't actually probe for anything here, although we might
       someday check to see if there's bi-directional port at
       dev->base_addr. */

    /* Initialize the device structure. */
    dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);
    memset(dev->priv, 0, sizeof(struct netstats));
    localstats = (struct netstats*) dev->priv;

    for (i = 0; i < DEV_NUMBUFFS; i++)
	dev->buffs[i] = NULL;
    dev->hard_header	= eth_header;
    dev->add_arp	= eth_add_arp;
    dev->queue_xmit	= dev_queue_xmit;
    dev->rebuild_header	= eth_rebuild_header;
    dev->type_trans	= eth_type_trans;

    dev->open		= &plip_open;
    dev->stop		= &plip_close;
    dev->hard_start_xmit = &plip_tx_packet;

    /* These are ethernet specific. */
    dev->type = ARPHRD_ETHER;
    dev->hard_header_len = ETH_HLEN;
    dev->mtu		= 1500; /* eth_mtu */
    dev->addr_len	= ETH_ALEN;
    for (i = 0; i < dev->addr_len; i++) {
	dev->broadcast[i]=0xff;
	dev->dev_addr[i] = i;	/* The physical address is 0:1:2:3:4:5! */
    }

    /* New-style flags. */
    dev->flags		= IFF_BROADCAST;
    dev->family		= AF_INET;
    dev->pa_addr	= 0;
    dev->pa_brdaddr	= 0;
    dev->pa_mask	= 0;
    dev->pa_alen	= sizeof(unsigned long);

    printk("%s: using parallel port at %#3x, IRQ %d.\n", dev->name,
	   dev->base_addr, dev->irq);

    return 0;
}

/* Open/initialize the board.  This is called (in the current kernel)
   sometime after booting when the 'config <dev->name>' program is
   run.

   This routine gets exclusive access to the parallel port by allocating
   its IRQ line.
   */
static int
plip_open(struct device *dev)
{
    if (dev->irq == 0)
	dev->irq = 7;
    if (request_irq(dev->irq , &plip_interrupt) != 0) {
	if (plip_debug > 2)
	    printk("%s: couldn't get the IRQ.\n", dev->name);
	return EAGAIN;
    }

    irq2dev_map[dev->irq] = dev;
    outb(0x10, dev->base_addr + PAR_CONTROL);		/* Enable the rx interrupt. */
    dev->tbusy = 0;		/* Transmit busy...  */
    dev->interrupt = 0;
    dev->start = 1;
    return 0;
}

/* The inverse routine to plip_open(). */
static int
plip_close(struct device *dev)
{
    dev->start = 0;
    free_irq(dev->irq);
    irq2dev_map[dev->irq] = NULL;
    outb(0x00, dev->base_addr);		/* Release the interrupt. */
    return 0;
}

static int
plip_tx_packet(struct sk_buff *skb, struct device *dev)
{
    int ret_val;
    /* If some higher layer thinks we've missed an tx-done interrupt
       we are passed NULL. Caution: dev_tint() handles the cli()/sti()
       itself. */
    if (skb == NULL) {
	dev_tint(dev);
	return 0;
    }

    /* Pretend we are an ethernet and fill in the header.  This could use
       a simplified routine someday. */
    if (!skb->arp  &&  dev->rebuild_header(skb+1, dev)) {
	skb->dev = dev;
	arp_queue (skb);
	return 0;
    }

    dev->trans_start = jiffies;
    ret_val = plip_write(dev, (unsigned char *)(skb+1), skb->len);
    if (skb->free)
	kfree_skb (skb, FREE_WRITE);
    dev->tbusy = 0;
    mark_bh (INET_BH);
    return ret_val;
}


static inline int get_byte(struct device *dev)
{
    unsigned char val;
    unsigned char low_nibble;
    int boguscount = 1500;
    do {
	val = inb(dev->base_addr + PAR_STATUS);
    } while ( ! (val & 0x80) && --boguscount > 0);
    low_nibble = (val >> 3) & 0x0f;
    if (plip_debug > 8)
	printk("%1x", low_nibble);
    outb(0x10, dev->base_addr + PAR_DATA);
    do {
	val = inb(dev->base_addr + PAR_STATUS);
    } while ((val & 0x80)  &&  --boguscount > 0);
    if (plip_debug > 8)
	printk("%1x %s", low_nibble,
	       boguscount <= 0 ? "timeout":"");
    outb(0x00, dev->base_addr + PAR_DATA);
    return low_nibble | ((val << 1) & 0xf0);
}

/* The typical workload of the driver:
   Handle the parallel port interrupts. */
static void
plip_interrupt(int reg_ptr)
{
    int irq = -(((struct pt_regs *)reg_ptr)->orig_eax+2);
    struct device *dev = irq2dev_map[irq];
    int boguscount = 1500;
    unsigned length;
    int sksize;
    struct sk_buff *skb;

    if (dev == NULL) {
	printk ("plip_interrupt(): irq %d for unknown device.\n", irq);
	return;
    }
    dev->interrupt = 1;
    outb(0x00, dev->base_addr + PAR_CONTROL);		/* Disable the rx interrupt. */
    sti(); /* Allow other interrupts. */
    
    if (plip_debug >= 4)
	printk("%s: interrupt.\n", dev->name);
    
    localstats = (struct netstats*) dev->priv;
    
    /* Receive the packet here. */
    if (inb(dev->base_addr + PAR_STATUS) != 0xc7) {
	localstats->rx_errors++;			/* No interrupt! */
	if (plip_debug > 4)
	    printk("%s: No interrupt (status=%#02x)!\n",
		   dev->name, inb(dev->base_addr + PAR_STATUS));
	return;
    }
    outb(1, dev->base_addr + PAR_DATA);		/* Ack: 'Ready' */
    length = get_byte(dev);
    length |= (get_byte(dev) << 8);
    if (length > dev->mtu) {
	printk("%s: Bogus packet size %d, dropping it.\n", dev->name, length);
	return;
    }
    boguscount = length << 5;
    sksize = sizeof(struct sk_buff) + length;
    skb = (struct sk_buff *) kmalloc(sksize, GFP_ATOMIC);
    if (skb == NULL) {
	if (plip_debug)
	    printk("%s: Couldn't allocate a sk_buff of size %d.\n",
		   dev->name, sksize);
	localstats->rx_errors++;
	return;
    }
    skb->lock = 0;
    skb->mem_len = sksize;
    skb->mem_addr = skb;
    {
	/* 'skb+1' points to the start of sk_buff data area. */
	unsigned char *buf = (unsigned char *) (skb+1);
	int checksum = 0;

	while (length--) {
	    unsigned char new_byte = get_byte(dev);
	    checksum += new_byte, *buf++ = new_byte;
	}
	if (checksum != get_byte(dev))
	    localstats->soft_rx_errors++;
	else if(dev_rint((unsigned char *)skb, length, IN_SKBUFF, dev)) {
	    printk("%s: receive buffers full.\n", dev->name);
	    localstats->rx_errors++;
	    return;
	}
    }
    /* Wait for the remote end to reset. */
    while (inb(dev->base_addr + PAR_STATUS) != 0x87)
	if (boguscount-- <= 0 )
	    break;
    outb(0x00, dev->base_addr + PAR_DATA);
    outb(0x10, dev->base_addr + PAR_CONTROL);		/* Enable the rx interrupt. */
    localstats->rx_packets++;
    return;
}

    
static inline int send_byte(struct device *dev, unsigned char val)
{
    int boguscount = 1500;
    if (plip_debug > 8)
	printk("send(%02x) ", val);
    outb(0x10 | val, dev->base_addr);
    while(inb(dev->base_addr+PAR_STATUS) & 0x80)
	if (--boguscount <= 0) break;
    outb(val >> 4, dev->base_addr);
    while((inb(dev->base_addr+PAR_STATUS) & 0x80) == 0)
	if (--boguscount <= 0) break;
    if (plip_debug > 4 && boguscount <= 0)
	printk("timeout");
}
static int
plip_write(struct device *dev, unsigned char *buf, int length)
{
    int timeout = 1000;			/* Approx 1 ms. */
    char checksum = 0;
    int i;
    if (plip_debug > 5)
	printk("%s: plip_write(%d) %02x %02x %02x %02x %02x...",
	       dev->name, length, buf[0], buf[1], buf[2], buf[3], buf[4]);
    if (length > dev->mtu) {
	printk("%s: packet too big, %d.\n", dev->name, length);
	return 1;
    }
    /* This starts the packet protocol by triggering a remote IRQ. */
    outb(0x00, dev->base_addr + PAR_CONTROL);	/* Disable my rx interrupt. */
    outb(0x08, dev->base_addr + PAR_DATA); 	/* Trigger remote rx interrupt. */
    while((inb(dev->base_addr+PAR_STATUS) & 0x08) == 0 )
	if (--timeout < 0) {
	    outb(0x00, dev->base_addr);
	    localstats->tx_errors++;
	    if (plip_debug > 3)
		printk("%s: Connect failed during send_packet() (length=%d).\n",
		       dev->name, length);
	    /* We failed to send the packet.  To emulate the ethernet we
	       should pretent the send worked fine, but we don't right now. */
	    return 1;			/* Failed to send the packet! */
	}
    send_byte(dev, length); send_byte(dev, length >> 8);
    for (i = 0; i < length; i++)
	checksum += buf[i], send_byte(dev, buf[i]);
    send_byte(dev, checksum);
    outb(0x00, dev->base_addr);
    outb(0x10, dev->base_addr + PAR_CONTROL);	/* Enable the rx interrupt. */
    localstats->tx_packets++;
    if (plip_debug > 5)
	printk("plip_write(%d) done.\n", length);
    return 0;
}

/*
 * Local variables:
 *  compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c plip.c"
 *  version-control: t
 *  kept-new-versions: 5
 * End:
 */
