File:  [Research Unix] / researchv9 / sys / boot / stand / if_le.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:21:59 2018 UTC (8 years, 3 months ago) by root
Branches: belllabs, MAIN
CVS tags: researchv9-SUN3_old, researchv9-SUN3, HEAD
researchv9-SUN3(old)

#ifndef LEBOOT
#ifndef lint
static	char sccsid[] = "@(#)if_le.c 1.1 86/02/03 Copyr 1985 Sun Micro";
#endif
#endif

/*
 * Copyright (c) 1985 by Sun Microsystems, Inc.
 */

/*
 * For Sun-3/25 debugging we want DEBUG on.  For production, turn it off.
 */
/* #define DEBUG */
/* #define DEBUG1 */

#define LANCEBUG 1	/* Rev C Lance chip bug */

/* 
 * Parameters controlling TIMEBOMB action: times out if chip hangs.
 *
 */
#define	TIMEBOMB	1000000	/* One million times around is all... */

/*
 * Sun Lance Ethernet Controller interface
 */
#include "saio.h"
#include "param.h"
#include "../h/socket.h"
#include "../sunif/if_lereg.h"
#include "../net/if.h"
#include "../netinet/in.h"
#include "../netinet/if_ether.h"
#include "../mon/idprom.h"
#include "../mon/cpu.map.h"

/* Determine whether we are PROM or not. */
#ifdef LEBOOT
#define PROM
#endif LEBOOT

int	lancexmit(), lancepoll(), lancereset();

struct saif leif = {
	lancexmit,
	lancepoll,
	lancereset,
};

#define	LANCERBUFSIZ	1600
#define	LANCETBUFSIZ	1600

struct lance_softc {
	char		es_scrat[PROTOSCRATCH];	/* work space for nd */
	struct le_device    	*es_lance;	/* Device register address */
	struct ether_addr	es_enaddr;	/* Our Ethernet address */
	struct le_init_block	es_ib;		/* Initialization block */
	struct le_md		*es_rmdp[2];	/* Receive Desc Ring ptrs */
	struct le_md		*es_tmdp;	/* Transmit Desc Ring ptr */
	u_char			es_rbuf[2][LANCERBUFSIZ]; /* Receive Buffers */
#ifndef PROM
	u_char			es_tbuf[LANCETBUFSIZ];	/* Transmit Buffer */
#endif	PROM
	int			es_next_rmd;	/* Next descriptor in ring */
	/*
	 * The transmit and receive ring descriptors.  The es_{r,t}mdp
	 * entries point into these arrays.  We allocate an extra one of
	 * each so that we can guarantee 8-byte alignment.  (That is, we
	 * don't necessarily point locations an integral number of le_mds
	 * from the start of the arrays.
	 */
	struct le_md		es_rmd[3],
				es_tmd[2];
};


/*
 * Need to initialize the Ethernet control reg to:
 *	Reset is active
 *	Loopback is NOT active
 *	Interrupt enable is not active.
 */
u_long lancestd[] = {VIOPG_AMD_ETHER << BYTES_PG_SHIFT};

struct devinfo lanceinfo = {
	sizeof (struct le_device),
	sizeof (struct lance_softc),
	0,				/* Local bytes (we use dma) */
	1,				/* Standard addr count */
	lancestd,			/* Standard addrs */
	MAP_OBIO,			/* Device type */
	0,				/* transfer size handled by ND */
};

int lanceprobe(), xxboot(), lanceopen(), lanceclose(), etherstrategy();
int nullsys();

struct boottab ledriver = {
	"le", lanceprobe, xxboot, lanceopen, lanceclose,
	etherstrategy, "le: Sun/Lance Ethernet", &lanceinfo,
};

extern struct ether_addr etherbroadcastaddr;

/*
 * Probe for device.
 * Must return -1 for failure for monitor probe
 */
lanceprobe(sip)
	struct saioreq *sip;
{
	register short *sp;
	struct idprom id;

	if (IDFORM_1 == idprom(IDFORM_1, &id)
	    && id.id_machine == IDM_SUN3_M25)
		return (0);
	else
		return (-1);
}

/*
 * Open Lance Ethernet nd connection, return -1 for errors.
 */
lanceopen(sip)
	struct saioreq *sip;
{
	register int result;

#ifdef DEBUG1
	printf("le: lanceopen[\n");
#endif
	sip->si_sif = &leif;
	if ( lanceinit(sip) || (result = etheropen(sip)) < 0 ) {
		lanceclose(sip);		/* Make sure we kill chip */
#ifdef DEBUG1
		printf("le: lanceopen --> -1\n");
#endif
		return (-1);
	}
#ifdef DEBUG1
	printf("le: lanceopen --> %x\n", result);
#endif
	return (result);
}

/*
 * Set up memory maps and Ethernet chip.
 * Returns 1 for error (after printing message), 0 for ok.
 */
int
lanceinit(sip)
	struct saioreq *sip;
{
	register struct lance_softc *es;
	int paddr;
	int i;
	struct idprom id;
	
	/* 
	 * Our locals were obtined from DMA space, now put the locals
	 * pointer in the standard place.  This is OK since close()
	 * will only deallocate from devdata if its size in devinfo is >0.
	 */
	es = (struct lance_softc *)sip->si_dmaaddr;
	sip->si_devdata = (caddr_t)es;
	es->es_lance = (struct le_device *) sip->si_devaddr;

	if (IDFORM_1 != idprom(IDFORM_1, &id)) {
		printf("le: No ID PROM\n");
		return 1;
	}

	return lancereset(es, sip);
}

/*
 * Basic Lance initialization
 * Returns 1 for error (after printing message), 0 for ok.
 */
int
lancereset(es, sip)
	register struct lance_softc *es;
	struct saioreq *sip;
{
	register struct le_device *le = es->es_lance;
	register struct le_init_block *ib = &es->es_ib;
	int timeout = TIMEBOMB;
	int i;

#ifdef DEBUG1
	printf("le: lancereset(%x, %x)\n", es, sip);
#endif

	/* Reset the chip */
	le->le_rap = LE_CSR0;
	le->le_csr = LE_STOP;

	/* Perform the basic initialization */
	
	/* Construct the initialization block */
	bzero((caddr_t)&es->es_ib, sizeof (struct le_init_block));

	/* Leave the mode word 0 for normal operating mode */

	myetheraddr(&es->es_enaddr);

	/* Oh, for a consistent byte ordering among processors */
	ib->ib_padr[0] = es->es_enaddr.ether_addr_octet[1];
	ib->ib_padr[1] = es->es_enaddr.ether_addr_octet[0];
	ib->ib_padr[2] = es->es_enaddr.ether_addr_octet[3];
	ib->ib_padr[3] = es->es_enaddr.ether_addr_octet[2];
	ib->ib_padr[4] = es->es_enaddr.ether_addr_octet[5];
	ib->ib_padr[5] = es->es_enaddr.ether_addr_octet[4];

	/* Leave address filter 0 -- we don't want Multicast packets */

	/*
	 * Set up transmit and receive ring pointers,
	 * taking the 8-byte alignment requirement into account.
	 */
	es->es_rmdp[0] = (struct le_md *) ((u_long)(&es->es_rmd[0]) & ~07) + 1;
	es->es_rmdp[1] = es->es_rmdp[0] + 1;
	es->es_tmdp    = (struct le_md *) ((u_long)(&es->es_tmd[0]) & ~07) + 1;

	ib->ib_rdrp.drp_laddr = (long)es->es_rmdp[0];
	ib->ib_rdrp.drp_haddr = (long)es->es_rmdp[0] >> 16;
	ib->ib_rdrp.drp_len  = 1;   /* 2 to the 1 power = 2 */
	
	ib->ib_tdrp.drp_laddr = (long)es->es_tmdp;
	ib->ib_tdrp.drp_haddr = (long)es->es_tmdp >> 16;
	ib->ib_tdrp.drp_len  = 0;   /* 2 to the 0 power = 1 */

	/* Clear all the descriptors */
	bzero((caddr_t)es->es_rmdp[0], 2 * sizeof (struct le_md));
	bzero((caddr_t)es->es_tmdp, sizeof (struct le_md));

	/* Give the init block to the chip */
	le->le_rap = LE_CSR1;	/* select the low address register */
	le->le_rdp = (long)ib & 0xffff;

	le->le_rap = LE_CSR2;	/* select the high address register */
	le->le_rdp = ((long)ib >> 16) & 0xff;

	le->le_rap = LE_CSR3;	/* Bus Master control register */
	le->le_rdp = LE_BSWP;

	le->le_rap = LE_CSR0;	/* main control/status register */
	le->le_csr = LE_INIT;

	while( ! (le->le_csr & LE_IDON) ) {
		if (timeout-- <= 0) {
		    printf("le: cannot initialize\n");
		    return (1);
		}
	}
	le->le_csr = LE_IDON;	/* Clear the indication */

	/* Hang out the receive buffers */
	es->es_next_rmd = 0;

	install_buf_in_rmd(es->es_rbuf[0], es->es_rmdp[0]);
	install_buf_in_rmd(es->es_rbuf[1], es->es_rmdp[1]);

	le->le_csr = LE_STRT;

#ifdef DEBUG1
	printf("le: lancereset returns OK\n");
#endif
	return 0;		/* It all worked! */
}

install_buf_in_rmd(buffer, rmd)
	u_char *buffer;
	register struct le_md *rmd;
{
	rmd->lmd_ladr = (u_short)buffer;
	rmd->lmd_hadr = (long)buffer >> 16;
	rmd->lmd_bcnt = -LANCERBUFSIZ;
	rmd->lmd_mcnt = 0;
	rmd->lmd_flags = LMD_OWN;
}

/*
 * Transmit a packet.
 * If possible, just points to the packet without copying it anywhere.
 */
lancexmit(es, buf, count)
	register struct lance_softc *es;
	char *buf;
	int count;
{
	register struct le_device *le = es->es_lance;
	struct le_md *tmd = es->es_tmdp; /* Transmit Msg. Descriptor */
	caddr_t tbuf;
	int timeout = TIMEBOMB;

#ifdef DEBUG2
	printf( "xmit np_blkno %x\n",
		((struct ndpack *)(buf+14))->np_blkno);
#endif
	/*
	 * We assume the buffer is in an area accessible by the chip.
	 * The caller of xmit is currently ndxmit(), which only sends
	 * an nd structure, which we happen to know is allocated in the
	 * right area (in fact, it's part of the struct nd which
	 * is the first thing in our own es structure).  If we wish to
	 * use xmit for some other purpose, the buffer might not be
	 * accessible by the chip, so to be general we ought to copy
	 * into some accessible place.  HOWEVER, PROM space is really tight,
	 * so this generality is not free.
	 */
#ifdef PROM
	tbuf = buf;
#else  PROM
/* FIXME, constant address masks here! */
	if ( ((int)buf & 0x0F000000) == 0x0F000000) { /* we can point to it */
	    tbuf = buf;
	} else {
	    tbuf = (caddr_t)es->es_tbuf;
	    bcopy((caddr_t)buf, tbuf, count);
	}
#endif PROM
	
	tmd->lmd_hadr = (int)tbuf >> 16;
	tmd->lmd_ladr = (u_short) tbuf;
	tmd->lmd_bcnt = -count;
	
#ifdef notdef
	if (tmd->lmd_bcnt < -MINPACKET)
	    tmd->lmd_bcnt = -MINPACKET;
#endif
	tmd->lmd_flags3 = 0;
	tmd->lmd_flags = LMD_STP | LMD_ENP | LMD_OWN;
	
	le->le_csr = LE_TDMD;

	do {
	    if ( le->le_csr & LE_TINT ) {
		le->le_csr = LE_TINT; /* Clear interrupt */
		break;
	    }
	} while ( --timeout > 0);

	if ( (tmd->lmd_flags & LMD_ERR)
	||   (tmd->lmd_flags3 & TMD_BUFF)
	||   (timeout <= 0) ) {
#ifdef DEBUG
		printf("le: xmit failed - tmd1 flags %x tmd3 %x\n",
	       		tmd->lmd_flags, tmd->lmd_flags3);
#endif
		return (1);
	}

	return (0);
}

int
lancepoll(es, buf)
	register struct lance_softc *es;
	char *buf;
{
	register struct le_device *le = es->es_lance;
	register struct le_md *rmd;
	register struct ether_header *header;
	int length;

#ifdef DEBUG1
	printf("le: poll\n");
#endif
	if ( ! (le->le_csr & LE_RINT)  )
		return (0);		/* No packet yet */

#ifdef DEBUG1
	printf("le: received packet\n");
#endif

	le->le_csr = LE_RINT; 		/* Clear interrupt */

	rmd = es->es_rmdp[es->es_next_rmd];

	if ( (rmd->lmd_flags & ~RMD_OFLO) != (LMD_STP|LMD_ENP) ) {
#ifdef DEBUG
		printf("Receive packet error - rmd flags %x\n",rmd->lmd_flags);
#endif
		length = 0;
		goto restorebuf;
	}

	/* Get input data length and a pointer to the ethernet header */

	length = rmd->lmd_mcnt - 4;	/* don't count the 4 CRC bytes */
	header = (struct ether_header *)es->es_rbuf[es->es_next_rmd];

#ifdef LANCEBUG
	/*
	 * Check for unreported packet errors.  Rev C of the LANCE chip
	 * has a bug which can cause "random" bytes to be prepended to
	 * the start of the packet.  The work-around is to make sure that
	 * the Ethernet destination address in the packet matches our
	 * address.
	 */
#define ether_addr_not_equal(a,b)	\
	(  ( *(long  *)(&a.ether_addr_octet[0]) != \
	     *(long  *)(&b.ether_addr_octet[0]) )  \
	|| ( *(short *)(&a.ether_addr_octet[4]) != \
	     *(short *)(&b.ether_addr_octet[4]) )  \
	)

    	if( ether_addr_not_equal(header->ether_dhost, es->es_enaddr)
	&&  ether_addr_not_equal(header->ether_dhost, etherbroadcastaddr) ) {
		printf("le: LANCE Rev C Extra Byte(s) bug; Packet punted\n");
		length = 0;
		/* Don't return directly; restore the buffer first */
	}
#endif LANCEBUG

#ifdef DEBUG2
    	if( header->ether_dhost.ether_addr_octet[0] == 0xff )
		printf("Broadcast packet\n");
	else	printf("recv np_blkno %x\n",
	        	((struct ndpack *)(buf+14))->np_blkno);
#endif
	
	/* Copy packet to user's buffer */
	if ( length > 0 )
		bcopy((caddr_t)header, buf, length);

restorebuf:
	rmd->lmd_mcnt = 0;
	rmd->lmd_flags = LMD_OWN;

	/* Get ready to use the other buffer next time */
	/* What about errors ? */
	es->es_next_rmd = 1 - es->es_next_rmd;

	return (length);
}

/*
 * Close down Lance Ethernet device.
 * On the Model 25, we reset the chip and take it off the wire, since
 * it is sharing main memory with us (occasionally reading and writing),
 * and most programs don't know how to deal with that -- they just assume
 * that main memory is theirs to play with.
 */
lanceclose(sip)
	struct saioreq *sip;
{
	struct lance_softc *es = (struct lance_softc *) sip->si_devdata;
	struct le_device *le = es->es_lance;

	/* Reset the chip */
	le->le_rap = LE_CSR0;
	le->le_csr = LE_STOP;
}

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.