/*
 * Driver for Dlink's DE600 pocket lan adaptor connected to the cartridge
 * port via the TUW hardware (adapter hardware description can be found
 * at fortec.tuwien.ac.at).
 *
 * This driver REQUIRES you to connect the interrupt wire to pin 11 (BUSY)
 * of your centronics port, ie it does not poll for incomming packets during
 * the VBL interrupt like the tuw-tcp driver.
 *
 * This driver has been developed and tested on a 16MHz ST. If you run in
 * on substantially faster machines a few wait()'s may be needed here and
 * there. Please contact me in case of success or failure...
 *
 * On a 16MHz ST (TT) the driver reaches the following throughput:
 * ftp send:  ~88 (~180) k/sec (ncftp 1.7.5 -> wu-ftpd 2.0)
 * ftp recv:  ~85 (~150) k/sec (ncftp 1.7.5 <- wu-ftpd 2.0)
 * tcp send:  ~90 (~200) k/sec (tcpcl -> tcpsv)
 * tcp recv:  ~90 (~160) k/sec (tcpsv <- tcpcl)
 * nfs read:  ~50 (    ) k/sec
 * nfs write: ~45 (    ) k/sec
 *
 * A note to NFS. When you mount a filesystem you must tell 'mount' to
 * use a maximum receive block size of MTU-300 bytes! Otherwise the
 * UDP datagrams must be fragmented and the lots of fragments will flood
 * the DE600's receive buffers wich leads to *very* low performance, ie
 * ALWAYS mount like this:
 *	mount -o rsize=1200 remote:/dir /nfs/dir
 *
 * 01/15/95, Kay Roemer.
 */

#include <string.h>
#include "config.h"
#include "netinfo.h"
#include "kerbind.h"
#include "atarierr.h"
#include "sockios.h"
#include "sockerr.h"
#include "buf.h"
#include "if.h"
#include "ifeth.h"
#include "de600.h"
#include "util.h"

#define DE600_VERSION	"0.4"

/*
 * 200 Hz timer
 */
#define HZ200 (*(volatile long *)0x4baL)

/*
 * Some macros for managing the output queue.
 */
#define Q_EMPTY(p)	((p)->tx_used <= 0)
#define Q_FULL(p)	((p)->tx_used >= TX_PAGES)
#define Q_LAST(p)	((p)->tx_addr[(p)->tx_tail])
#define Q_DEL(p)	{ --(p)->tx_used; (p)->tx_tail ^= 1; }
#define Q_ADD(p, addr)	{ ++(p)->tx_used; (p)->tx_addr[(p)->tx_head] = (addr);\
			  (p)->tx_head ^= 1; }

struct de600 {
	unsigned short	rx_page;	/* high nibble of nic's command reg */
	unsigned short	tx_addr[2];	/* queue of page start addresses */
	unsigned short	tx_head;	/* queue head */
	unsigned short	tx_tail;	/* queue tail */
	unsigned short	tx_used;	/* no of used queue entries */
};

static struct netif if_de600;
static struct de600 de600_priv;

static long	de600_open	(struct netif *);
static long	de600_close	(struct netif *);
static long	de600_output	(struct netif *, BUF *, char *, short, short);
static long	de600_ioctl	(struct netif *, short, long);

static long	de600_probe	(struct netif *);
static long	de600_reset	(struct netif *);
static void	de600_install_ints (void);
static void	de600_recv_packet (struct netif *);

static long
de600_open (nif)
	struct netif *nif;
{
	de600_reset (nif);
	return 0;
}

static long
de600_close (nif)
	struct netif *nif;
{
	return 0;
}

static long
de600_output (nif, buf, hwaddr, hwlen, pktype)
	struct netif *nif;
	BUF *buf;
	char *hwaddr;
	short hwlen, pktype;
{
	struct de600 *pr = nif->data;
	short stat, addr, len, sr;
	long ticks;
	BUF *nbuf;

	nbuf = eth_build_hdr (buf, nif, hwaddr, pktype);
	if (nbuf == 0) {
		++nif->out_errors;
		return ENSMEM;
	}
	if (nif->bpf)
		bpf_input (nif, nbuf);
	/*
	 * These spl()'s are absolutely necesarry to protect de600_cli().
	 * If you remove them somehow the return address on the stack
	 * gets corrupted when a BUSY interrupt happens during the
	 * jsr _de600_cli.
	 */
	sr = spl7 ();
	de600_cli ();
	de600_dints ();
	spl (sr);

	select_nic ();
	if (Q_FULL (pr)) {
		c_conws ("de600: output queue full!\r\n");
		buf_deref (nbuf, BUF_NORMAL);
		select_prn ();
		de600_sti ();
		de600_reset (nif);
		return ENSMEM;
	}
	/*
	 * Enet packets must at least have 60 bytes.
	 */
	len = nbuf->dend - nbuf->dstart;
	addr = (pr->tx_head + 1) * MEM_2K - MAX (len, MIN_LEN);
	/*
	 * Setup address and send packet
	 */
	send_addr (RW_ADDR, addr);
	send_block (nbuf->dstart, len);
	/*
	 * If nic busy sending something than wait for it to finish
	 */
	if (!Q_EMPTY (pr)) {
		ticks = HZ200+1;
		do {
			stat = recv_stat () & (TX_BUSY|TX_FAILED16|RX_GOOD);
			if (stat & RX_GOOD) {
				stat &= ~RX_GOOD;
				de600_recv_packet (nif);
			}
			if (stat == 0) {
				Q_DEL (pr);
			} else if (stat == TX_FAILED16) {
				++nif->collisions;
				send_addr (TX_ADDR, Q_LAST (pr));
				send_cmd (pr->rx_page, TX_ENABLE);
				ticks = HZ200+1;
			}
		} while (!Q_EMPTY (pr) && HZ200 - ticks <= 0);
		/*
		 * transmission timed out
		 */
		if (!Q_EMPTY (pr)) {
			c_conws ("de600: timeout, check network cable!\r\n");
			buf_deref (nbuf, BUF_NORMAL);
			++nif->out_errors;
			select_prn ();
			de600_sti ();
			de600_reset (nif);
			return ETIMEDOUT;
		}
	}
	Q_ADD (pr, addr);
	/*
	 * Tell nic where to send from and enable transmitter
	 */
	send_addr (TX_ADDR, addr);
	send_cmd (pr->rx_page, TX_ENABLE);

	select_prn ();
	de600_sti ();
	++nif->out_packets;
	buf_deref (nbuf, BUF_NORMAL);
	return 0;
}

static long
de600_ioctl (nif, cmd, arg)
	struct netif *nif;
	short cmd;
	long arg;
{
	switch (cmd) {
	case SIOCSIFNETMASK:
	case SIOCSIFFLAGS:
	case SIOCSIFADDR:
		return 0;

	case SIOCSIFMTU:
		/*
		 * Limit MTU to 1500 bytes. MintNet has alraedy set nif->mtu
		 * to the new value, we only limit it here.
		 */
		if (nif->mtu > ETH_MAX_DLEN)
			nif->mtu = ETH_MAX_DLEN;
		return 0;
	}
	return EINVFN;
}

long
driver_init (void)
{
	static char message[100];
	short sr;

	strcpy (if_de600.name, "en");
	if_de600.unit = if_getfreeunit ("en");
	if_de600.metric = 0;
	if_de600.flags = IFF_BROADCAST;
	if_de600.mtu = 1500;
	if_de600.timer = 0;
	if_de600.hwtype = HWTYPE_ETH;
	if_de600.hwlocal.len =
	if_de600.hwbrcst.len = ETH_ALEN;

	if_de600.rcv.maxqlen = IF_MAXQ;
	if_de600.snd.maxqlen = IF_MAXQ;

	if_de600.open = de600_open;
	if_de600.close = de600_close;
	if_de600.output = de600_output;
	if_de600.ioctl = de600_ioctl;

	if_de600.timeout = 0;

	if_de600.data = &de600_priv;

	/*
	 * Tell upper layers the max. number of packets we are able to
	 * receive in fast succession.
	 */
	if_de600.maxpackets = 1;

	sr = spl7 ();
	de600_cli ();
	de600_dints ();
	spl (sr);
	
	if (de600_probe (&if_de600) < 0) {
		de600_sti ();
		c_conws ("de600: driver not installed.\r\n");
		return 1;
	}
	de600_install_ints ();
	de600_sti ();

	if_register (&if_de600);

	sprintf (message, "DE600 driver v%s (en%d) (%x:%x:%x:%x:%x:%x)\r\n",
		DE600_VERSION,
		if_de600.unit,
		if_de600.hwlocal.addr[0],
		if_de600.hwlocal.addr[1],
		if_de600.hwlocal.addr[2],
		if_de600.hwlocal.addr[3],
		if_de600.hwlocal.addr[4],
		if_de600.hwlocal.addr[5]);
	c_conws (message);
	return 0;
}

static long
de600_reset (nif)
	struct netif *nif;
{
	struct de600 *pr = nif->data;
	short i, sr;

	sr = spl7 ();
	de600_cli ();
	de600_dints ();
	spl (sr);

	select_nic ();
	/*
	 * Reset adapter
	 */
	send_cmd (0, RESET_ON);
	wait ();
	send_cmd (0, RESET_OFF);
	wait ();
	/*
	 * Set enet address
	 */
	send_addr (RW_ADDR, MEM_ROM);
	for (i = 0; i < ETH_ALEN; ++i)
		send_byte (WRITE_DATA, nif->hwlocal.addr[i]);

	memset (pr, 0, sizeof (*pr));
	pr->rx_page = RX_BP | RX_PAGE1;
	/*
	 * Enable receiver
	 */
	send_cmd (pr->rx_page, RX_ENABLE);
	select_prn ();
	de600_sti ();
	return 0;
}

static long
de600_probe (nif)
	struct netif *nif;
{
	unsigned char *cp;
	short i;

	select_nic ();
	/*
	 * Reset the adapter
	 */
	recv_stat ();
	send_cmd (0, RESET_ON);
	wait ();
	send_cmd (0, RESET_OFF);
	wait ();
	if (recv_stat () & 0xf0) {
		c_conws ("de600: no DE600 detected.\r\n");
		return -1;
	}
	/*
	 * Try to read magic code and last 3 bytes of hw
	 * address
	 */
	send_addr (RW_ADDR, MEM_ROM);
	wait ();
	for (i = 0; i < ETH_ALEN; ++i) {
		nif->hwlocal.addr[i] = recv_byte (READ_DATA);
		nif->hwbrcst.addr[i] = 0xff;
	}
	/*
	 * Check magic
	 */
	cp = nif->hwlocal.addr;
	if (cp[1] != 0xde || cp[2] != 0x15) {
		c_conws ("de600: magic check failed.\r\n");
		return -1;
	}
	/*
	 * Install real address
	 */
	cp[0] = 0x00;
	cp[1] = 0x80;
	cp[2] = 0xc8;
	cp[3] &= 0x0f;
	cp[3] |= 0x70;
	select_prn ();
	return 0;
}

/*
 * Busy interrupt routine
 */
void
de600_int (void)
{
	struct de600 *pr = if_de600.data;
	unsigned char stat;
	short sr;

	sr = spl7 ();
	de600_cli ();
	spl (sr);

	select_nic ();
	stat = recv_stat ();
	do {
		/*
		 * See if packet arrived. Reenable receiver
		 */
		if (stat & RX_GOOD)
			de600_recv_packet (&if_de600);
		else if (!(stat & RX_BUSY))
			send_cmd (pr->rx_page, RX_ENABLE);
		/*
		 * If something has been sent then either delete it
		 * from send queue or resend in case of collision.
		 */
		if (!Q_EMPTY (pr) && !(stat & TX_BUSY)) {
			if (stat & TX_FAILED16) {
				++if_de600.collisions;
				send_addr (TX_ADDR, Q_LAST (pr));
				send_cmd (pr->rx_page, TX_ENABLE);
			} else {
				Q_DEL (pr);
			}
		}
		stat = recv_stat ();
	} while (stat & RX_GOOD);
	select_prn ();
	de600_sti ();
}

/*
 * Read a packet out of the adapter and pass it to the upper layers
 */
static void
de600_recv_packet (nif)
	struct netif *nif;
{
	struct de600 *pr = nif->data;
	unsigned short pktlen, addr;
	BUF *b;

	/*
	 * Read packet length (excluding 32 bit crc)
	 */
	pktlen =  (unsigned short)recv_byte (RX_LEN);
	pktlen |= (unsigned short)recv_byte (RX_LEN) << 8;
	pktlen -= 4;
	/*
	 * Switch to next recv page and enable receiver
	 */
	pr->rx_page ^= RX_PAGE2;
	send_cmd (pr->rx_page, RX_ENABLE);

	if (pktlen < 32 || pktlen > 1535) {
		++nif->in_errors;
		if (pktlen > 10000)
			de600_reset (nif);
		return;
	}
	b = buf_alloc (pktlen+100, 50, BUF_ATOMIC);
	if (b == 0) {
		++nif->in_errors;
		return;
	}
	b->dend += pktlen;
	addr = (pr->rx_page & RX_PAGE2) ? MEM_4K : MEM_6K;
	/*
	 * Set receive address and read the packet
	 */
	send_addr (RW_ADDR, addr);
	recv_block (b->dstart, pktlen);
	/*
	 * Pass packet to upper layers
	 */
	if (nif->bpf)
		bpf_input (nif, b);
	if (!if_input (nif, b, 0, eth_remove_hdr (b)))
		++nif->in_packets;
	else
		++nif->in_errors;
}

/*
 * Install interrupt handler
 */
static void
de600_install_ints (void)
{
	de600_old_busy_int = s_etexec ((short)0x40, (long)de600_busy_int);

	__asm__("bset	#0, 0xfffffa09:w;"	/* IERB */
		"bset	#0, 0xfffffa03:w;");	/* AER */
}
