/* Copyright 1989 by the Massachusetts Institute of Technology */

/* Quick standalone hack for quick CGW booting. */

#include	<types.h>
#include	<sys.h>
#include	"../gw/src/const.h"
#include	"../gw/src/param.h"
#include	"../gw/src/defs.h"
#include	"../gw/src/macs.h"
#include	"../gw/src/net.h"
#include	"../gw/src/ext.h"
#include	"../gw/src/status.h"
#include	<sys/types.h>
#include	"nbregs.h"

#define	ETHMINSIZ	64

extern unsigned char etmyaddr[6];

#define	se_csrselect	nb_ni_rap	/* csr select (0-3) */
#define	se_csr		nb_ni_rdp	/* csr data register */

/*
 * Network INIT Block
 */
struct se_initb {
	u_short se_mode;		/* NIB_MODE mode word 		*/
	u_short	se_sta_addr[3]; 	/* NIB_PADR station address 	*/
	u_short	se_multi_mask[4];	/* NIB_LADRF Multicast addr mask*/
	u_short	se_rcvlist_lo,		/* NIB_RDRP Rcv Dsc Ring Ptr	*/
		se_rcvlist_hi:8,	/* Rcv list hi addr 		*/
		se_rcvresv:5,		/* reserved			*/
		se_rcvlen:3;		/* RLEN Rcv ring length		*/
	u_short	se_xmtlist_lo,		/* NIB_TDRP Xmt Dsc Ring Ptr	*/
		se_xmtlist_hi:8,	/* Xmt list hi addr 		*/
		se_xmtresv:5,		/* reserved			*/
		se_xmtlen:3;		/* TLEN Xmt ring length		*/
};

/*
 * VAXstar initialization block mode word
 */
#define SE_DRX			0x0001	/* Disable receiver		*/
#define SE_DTX			0x0002	/* Disable transmitter		*/
#define SE_LOOP			0x0004	/* Loopback control		*/
#define SE_DTCR			0x0008	/* Disable transmit CRC		*/
#define SE_COLL			0x0010	/* Force collision		*/
#define SE_DIRTY		0x0020	/* Disable retry		*/
#define SE_INTL			0x0040	/* Internal loopback		*/
#define SE_PROM			0x8000	/* Promiscuous mode		*/

/*
 * VAXstar ring descriptors
 */
struct se_ring	{
	u_short se_addr_lo;		/* Low order bits of buf addr	*/
	char se_addr_hi;		/* Hi order bits of buf addr	*/
	char se_flag;			/* rcv/xmt status flag		*/
	short se_buf_len;		/* buffer length (2's comp)	*/
	u_short se_flag2;		/* transmit: status/TDR		*/
					/* receive: len of packet	*/
};

/*
 * VAXstar receive status (se_flag)
 */
#define SE_ENP			0x0001	/* End of packet		*/
#define SE_STP			0x0002	/* Start of packet		*/
#define SE_RBUFF		0x0004	/* Receive Buffer error		*/
#define SE_CRC			0x0008	/* Checksum error		*/
#define SE_OFLO			0x0010	/* Overflow error		*/
#define SE_FRAM			0x0020	/* Framing error		*/
#define SE_RT_ERR		0x0040	/* Lance Error summary 		*/
#define SE_OWN			0x0080	/* Owned flag (1=Lance, 0=host)	*/

/*
 * VAXstar transmit status (se_flag)
 */
/* #define SE_ENP		0x0001	/* End of packet		*/
/* #define SE_STP		0x0002	/* Start of packet		*/
#define	SE_DEF			0x0004	/* Deferred - network busy	*/
#define	SE_ONE			0x0008	/* One retry was required	*/
#define	SE_MORE			0x0010	/* More retries were required	*/
/* #define SE_XXXX		0x0020	/* reserved			*/
/* #define SE_RT_ERR		0x0040	/* Lance Error summary		*/
/* #define SE_OWN		0x0080	/* Owned flag (1=Lance, 0=host)	*/

/* receive length */
#define SE_MCNT			0x0fff	/* low 12 bits of se_flag2	*/

/* transmit status (se_flag2,15:10) */
#define	SE_TBUFF		0x8000	/* Transmit buffer error	*/
#define	SE_UFLO			0x4000	/* Underflow			*/
#define	SE_LCOL			0x1000	/* Late collision		*/
#define	SE_LCAR			0x0800	/* Loss of carrier		*/
#define	SE_RTRY			0x0400	/* Retries exhausted		*/

/* transmit TDR (se_flag2,9:0) */
#define SE_TDR			0x03ff	/* Time Domain Reflectometer	*/

/*
 * VAXstar command and status bits (CSR0)
 */
#define SE_INIT			0x0001	/* (Re)-Initialize		*/
#define SE_START		0x0002	/* Start operation		*/
#define SE_STOP			0x0004	/* Reset firmware (stop)	*/
#define SE_RESET	SE_STOP		/*   "				*/
#define SE_TDMD			0x0008	/* Transmit on demand		*/
#define SE_XMIT_DEMAND	SE_TDMD		/*   "				*/
#define SE_TXON			0x0010	/* Transmitter is enabled	*/
#define SE_RXON			0x0020	/* Receiver is enabled		*/
#define SE_INEA			0x0040	/* Interrupt enable		*/
#define SE_INT_ENABLE	SE_INEA		/*   "				*/
#define SE_INTR			0x0080	/* Interrupt request		*/
#define SE_IDON 		0x0100	/* Initialization done		*/
#define SE_TINT			0x0200	/* Transmitter interrupt	*/
#define SE_XMIT_INT	SE_TINT		/*   "				*/
#define SE_RINT			0x0400	/* Receive interrupt		*/
#define SE_RCV_INT	SE_RINT		/*   "				*/
#define SE_MERR			0x0800	/* Memory error			*/
#define SE_MISS			0x1000	/* Missed packet		*/
#define SE_CERR			0x2000	/* Collision error		*/
#define SE_BABL			0x4000	/* Transmit timeout err		*/
#define SE_ERR			0x8000	/* Error summary		*/

/*
 * VAXstar CSR select
 */
#define SE_CSR0			0x0000	/* CSR 0			*/
#define SE_CSR1			0x0001	/* CSR 1			*/
#define SE_CSR2			0x0002	/* CSR 2			*/
#define SE_CSR3			0x0003	/* CSR 3			*/

/*
 * Address Descriptor bit definitions
 */

#define	VALID	0x8000
#define	CHAIN	0x4000
#define	EOM	0x2000
#define	SETUP	0x1000
#define	LBYTE	0x80
#define	HBYTE	0x40


/*
 * Buffers for setup.
 */
#define NRECBUFS	32	/* must be a power of 2 */
#define LOGNRECBUFS	5	/* log base 2 of NRECBUFS */
#define RECBUFSIZE	2048

struct se_initb	*se_init_buf;
struct se_ring *sexmt, *sercv;
char seib[sizeof(struct se_initb)+8];
char serb[sizeof(struct se_ring)*(NRECBUFS+1) + 8];
#define quadalign(x)	((((word)(x))+8)&0xfffffff8)
char serbuf[RECBUFSIZE * NRECBUFS];
int currcv;

se_in_up(devp)
reg	dct	*devp;
{
    	struct nb_regs *nb_addr = (struct nb_regs *)VAXSTARNEXUS;
    	struct nb1_regs *nb1_addr = (struct nb1_regs *)QMEMVAXSTAR;
	int i, j;

#ifdef DEBUG
	printf("se_in_up()...");
#endif

	devp->d_flg |= D_INI;

	/* reset the device */
	nb1_addr->se_csrselect = SE_CSR0;
	nb1_addr->se_csr = SE_STOP;

	/* allocate buffers */
	se_init_buf = (struct se_initb *)quadalign(seib);
	sexmt = (struct se_ring *)quadalign(serb);
	sercv = sexmt + 1;

	/* setup the initialization buffer */
	bzero(se_init_buf, sizeof(struct se_initb));
	se_init_buf->se_mode = 0;
	for ( i=0, j=0; j < 3; j++ ) {	/* copy address from ROM */
	    se_init_buf->se_sta_addr[j] =
	      (short)(nb_addr->nb_narom[4*i++]&0xff);
	    se_init_buf->se_sta_addr[j] |=
	      (short)((nb_addr->nb_narom[4*i++]&0xff)<<8);
	}
	for (i = 0; i < 3; i++) {	/* copy address for program */
	    etmyaddr[i*2] = se_init_buf->se_sta_addr[i] & 0xff;
	    etmyaddr[i*2 + 1] = se_init_buf->se_sta_addr[i]>>8 & 0xff;
	}
	se_init_buf->se_xmtlist_lo = ((word)sexmt) & 0xffff;
	se_init_buf->se_xmtlist_hi = (((word)sexmt)>>16) & 0xff;
	se_init_buf->se_rcvlen = 0;
	se_init_buf->se_rcvlist_lo = ((word)sercv) & 0xffff;
	se_init_buf->se_rcvlist_hi = (((word)sercv)>>16) & 0xff;
	se_init_buf->se_rcvlen = LOGNRECBUFS;
	/* setup the receive and transmit rings */
	bzero(sexmt, sizeof(struct se_ring));
	bzero(sercv, sizeof(struct se_ring)*NRECBUFS );
	for (i = 0; i < NRECBUFS; i++) {
	    sercv[i].se_addr_lo = ((word)(&serbuf[RECBUFSIZE*i])) & 0xffff;
	    sercv[i].se_addr_hi = (((word)(&serbuf[RECBUFSIZE*i]))>>16) & 0xff;
	    sercv[i].se_buf_len = -RECBUFSIZE;
	    sercv[i].se_flag = SE_OWN;
	}
	currcv = 0;

	/* verify that structures are longword aligned */
	if (((word)se_init_buf) & 0x07) {	/* shouldn't happen */
		printf("se: initb not on QUADword boundary");
		printf(": %x\n", ((word)se_init_buf) & 0x07);
	}
	if (se_init_buf->se_xmtlist_lo & 0x07) {	/* shouldn't happen */
		printf("se: xmtlist not on QUADword boundary");
		printf(": %x\n", se_init_buf->se_xmtlist_lo & 0x07);
	}
	if (se_init_buf->se_rcvlist_lo & 0x07) {	/* shouldn't happen */
		printf("se: rcvlist not on QUADword boundary");
		printf(": %x\n", se_init_buf->se_rcvlist_lo & 0x07);
	}

	/* setup the device */
	nb1_addr->se_csrselect = SE_CSR1;
	nb1_addr->se_csr = 0xffff & ((word)se_init_buf);
	nb1_addr->se_csrselect = SE_CSR2;
	nb1_addr->se_csr = 0xff & (((word)se_init_buf) >> 16);

	nb1_addr->se_csrselect = SE_CSR0;
	nb1_addr->se_csr = (SE_IDON | SE_INIT);

	/* wait for the device to be done */
	j=0;
	while (j++ < 10000) {
		if ((nb1_addr->se_csr & SE_IDON) != 0) {
#ifdef DEBUG
		    printf("se_in_up: INIT DONE: %d...", j);
#endif
		    break;
		}
	}
	/* make sure got out okay */
	if ((nb1_addr->se_csr & SE_IDON) == 0) {
	    if (nb1_addr->se_csr & SE_ERR) {
		printf("se: initialization error, csr = %04x\n",
		       nb1_addr->se_csr & 0xffff);
	    } else {
		printf("se: cannot initialize Lance\n");
	    }
	    return(0);
	}
	nb1_addr->se_csr = SE_START;
#ifdef TIMING
	printf("@");
#endif
	return(1);
}


/* Shut down network interface before program exits */
se_down()
{
    struct nb1_regs *nb1_addr = (struct nb1_regs *)QMEMVAXSTAR;
    int i;

    nb1_addr->se_csrselect = SE_CSR0;
    nb1_addr->se_csr = SE_STOP;
    /* sleep here for a while because the LANCE will modify the
     * receive buffer before it is done.   When this routine returns,
     * the ethernet should be completely dormant.
     */
    for (i = 100000; i > 0; i--);
}


/* Output initialization transfer routine */
se_out(devp)
reg	dct	*devp;
{
    struct nb1_regs *nb1_addr = (struct nb1_regs *)QMEMVAXSTAR;
    int j;

    /* due to a bug in the LANCE chip, if we get an input overrun we
     * have to reinitialize the chip.
     */
    if (nb1_addr->se_csr & SE_MISS) {
	se_in_up(devp);
	for (j = 0; j < 1000; j++);
    }

    /* wait for previous transmit to complete */
    j=0;
    while (j++ < 100000) {
	if ((sexmt->se_flag & SE_OWN) == 0) {
	    break;
	}
    }
    if (sexmt->se_flag & SE_OWN)
      printf("se_out: transmit timeout\n");
    if (sexmt->se_flag & SE_RT_ERR)
      printf("se_out: ERROR: %x\n", sexmt->se_flag);

    /* setup the transmit buffer */
    sexmt->se_addr_lo = ((word)devp->d_addr) & 0xffff;
    sexmt->se_addr_hi = (((word)devp->d_addr)>>16) & 0xff;
    if (devp->d_breq < ETHMINSIZ) devp->d_breq = ETHMINSIZ;
    sexmt->se_buf_len = -devp->d_breq;
    sexmt->se_flag = SE_OWN | SE_STP | SE_ENP;

#ifdef DEBUG
    printf("about to transmit, status = %x\n", nb1_addr->se_csr);
#endif
#ifdef TIMING
    printf("!");
#endif

    /* start the transmit */
    nb1_addr->se_csrselect = SE_CSR0;
    nb1_addr->se_csr = SE_TDMD | SE_START;

#ifdef ndef
    /* wait for the transmit to complete */
    j=0;
    while (j++ < 100000) {
	if ((sexmt->se_flag & SE_OWN) == 0) {
	    break;
	}
    }
    if (sexmt->se_flag & SE_OWN)
      printf("se_out: transmit timeout\n");
    if (sexmt->se_flag & SE_RT_ERR)
      printf("se_out: ERROR: %x\n", sexmt->se_flag);
#endif
#ifdef DEBUG
    printf("done transmitting, status = %x\n", nb1_addr->se_csr);
#endif
}


/* Read a packet in from the network.  Waits up to 1 ms for a packet 
 * to be received.
 */

int se_dev_read(devp, data, len)
dct *devp;
byte *data;
int len;

{
    struct nb1_regs *nb1_addr = (struct nb1_regs *)QMEMVAXSTAR;
    int rlen, j;

    /* due to a bug in the LANCE chip, if we get an input overrun we
     * have to reinitialize the chip.
     */
    if (nb1_addr->se_csr & SE_MISS)
      se_in_up(devp);

    j=0;
    while (j++ < 85) {		/* This loop constant has been tuned for a
				 * .001 second delay on a VS2000
				 */
	if ((sercv[currcv].se_flag & SE_OWN) == 0) {
	    break;
	}
    }
    if (sercv[currcv].se_flag & SE_OWN) {
	return(0);
    }

    /* got a packet */
    rlen = (sercv[currcv].se_flag2 & SE_MCNT) - 4;
    if (sercv[currcv].se_flag & SE_RT_ERR || rlen > len) {
	if (rlen > len)
	  printf("received packet too long\n");
	else
	  printf("rcv error: %x,%x\n", nb1_addr->se_csr,sercv[currcv].se_flag);
	/* re-init buffer */
	sercv[currcv].se_buf_len = -RECBUFSIZE;
	sercv[currcv].se_flag = SE_OWN;
	currcv = (currcv + 1) % NRECBUFS;
	return(0);
    }
    bcopy(&serbuf[RECBUFSIZE * currcv], data, rlen);

#ifdef TIMING
    printf("#");
#endif
    /* re-init buffer */
    sercv[currcv].se_buf_len = -RECBUFSIZE;
    sercv[currcv].se_flag2 = 0;
    sercv[currcv].se_flag = SE_OWN;
    currcv = (currcv + 1) % NRECBUFS;
    return(len);
}
