File:  [Power 6/32 Unix Tahoe 4.2BSD] / cci / sys / tahoeif / if_ace.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs
Sun Jul 28 12:29:50 2019 UTC (7 years ago) by root
Branches: bsd, MAIN
CVS tags: v121, HEAD
Power 6/32 Unix version 1.21

/*								*/
/*	if_ace: ACC VERSAbus Ethernet controler (v/eiu)		*/
/*								*/
#include "ace.h"
#if NACE > 0

#include "../machine/pte.h"

#include "../h/param.h"
#include "../h/systm.h"
#include "../h/mbuf.h"
#include "../h/buf.h"
#include "../h/protosw.h"
#include "../h/socket.h"
#include "../h/vmmac.h"
#include "../h/ioctl.h"
#include "../h/errno.h"
#include "../h/vmparam.h"

#include "../net/if.h"
#include "../net/netisr.h"
#include "../net/route.h"
#include "../netinet/in.h"
#include "../netinet/in_systm.h"
#include "../netinet/ip.h"
#include "../netinet/ip_var.h"
#include "../netinet/if_ether.h"
#include "../netpup/pup.h"

#include "../machine/mtpr.h"
#include "../tahoeif/if_acereg.h"
#include "../vba/vbavar.h"
#include "../tahoeif/if_debug.h"

#define	LONET	124
#define	LOHOST	1
#define	TRUE	1
#define	FALSE	0

				/* configuration table, for 2 units	*/
				/*  should be defined by config		*/
#define	ACEVECTOR	0x90
long	acestd[] = { 0x0ff0000, 0xff0100 };		/* controller	*/

extern	char	ace0utl[], ace1utl[];			/* dpm		*/
char	*acemap[]= { ace0utl, ace1utl };
extern	long	ACE0map[], ACE1map[];
long	*ACEmap[] = {ACE0map, ACE1map };
long	ACEmapa[] = {0xfff80000,0xfff90000};

char	ace_station[6*NACE] = {				/* addresses	*/
	~0x8,~0x0,~0x3,~0x0,~0x0,~0x1,
#if	(NACE > 1)
	~0x8,~0x0,~0x3,~0x0,~0x0,~0x2,
#if	(NACE > 2)
	~0x8,~0x0,~0x3,~0x0,~0x0,~0x3,
	~0x8,~0x0,~0x3,~0x0,~0x0,~0x4,
#endif	(NACE > 2)
#endif	(NACE > 1)
};

char ace_hash_code[8*NACE]={				/* hash tables	*/
	~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,/*u 0*/
#if	(NACE > 1)
        ~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,/*u 1*/
#if	(NACE > 2)
        ~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,/*u 2*/
        ~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF,~0xF /*u 3*/
#endif	(NACE > 2)
#endif	(NACE > 1)
}; 

short random_mask_tbl[16] = {				/* backoff table*/
	0x0040, 0x00C0, 0x01C0, 0x03C0, 
        0x07C0, 0x0FC0, 0x1FC0, 0x3FC0,
        0x7FC0, 0xFFC0, 0xFFC0, 0xFFC0,
        0xFFC0, 0xFFC0, 0xFFC0, 0xFFC0  };

int	aceprobe(), aceattach(), acerint(), acecint();
struct	vba_device *aceinfo[NACE];
struct	vba_driver acedriver =
	{ aceprobe, 0,aceattach,0,acestd,"ace",aceinfo,"v/eiu",0 };
	
#define	ACEUNIT(x)	minor(x)

int	aceinit(),aceoutput(),aceioctl(),acereset();
struct	mbuf *aceget();

struct	ace_softc ace_softc[NACE];
extern	struct	ifnet	loif;

int	numace	= NACE;				/* for nstat unix command */
int	printerror = 1;
int 	save_ace_inpkt = 0;
struct 	inp_err		aceerr[NACE];
extern	int		saverrpkt();
#define INPTRACE(X)	if (save_ace_inpkt) X

/*------ debugging tools ------------*/
#ifdef ACEPRINTFS
int	aceprintfs;
#define INTTRACE		1
#define RTINTRACE 		2
#define STARTTRACE 		4
#define ACEOTRACE 		8
#define RTINTRACE1 		0x10
#define IOCTLTRACE		0x20
#define ATRACE			0x40
#define SETETTRACE		0x80
#define	ALLTRACE		(INTTRACE | RTINTRACE | STARTTRACE | ACEOTRACE\
			| RTINTRACE1 | IOCTLTRACE | ATRACE | SETETTRACE)

#define ACETRACE(X,Y)	{if (aceprintfs & X) Y}
#else ACEPRINTFS
#define ACETRACE(X,Y)
#endif ACEPRINTFS

/*------ performance study tools ---------------*/
#ifdef NET_PERF
#define	ACESTUDY(X)	{X}
#else  NET_PERF
#define ACESTUDY(X)
#endif

aceprobe(reg)
caddr_t reg;
{
register struct acedevice *addr = (struct acedevice *)reg;
register i;

	ACETRACE(ALLTRACE, printf("aceprobe: veiu @%X\n",reg);)


#ifdef lint
	i = 0; acerint(i); acecint(i);
#endif

	if (badaddr(reg,2)) return(0);
	movew((short)CSR_RESET,&addr->csr);
	DELAY(10000);
	return (1);

}


/*
 * Interface exists: make available by filling in network interface
 * record.  System will initialize the interface when it is ready
 * to accept packets.
 */
aceattach(ui)
struct vba_device *ui;
{
register short	unit = ui->ui_unit;
register struct ace_softc *is = &ace_softc[unit];
register struct ifnet *ifp = &is->is_if;
register struct acedevice *addr = (struct acedevice *)ui->ui_addr;
register short *pData0,i, data;
register char *pData1;
register short Csr;
struct sockaddr_in *sin;

	ACETRACE( ALLTRACE, printf("ace%dattach: ui %X\n",unit,ui);)

	ifp->if_unit = unit;
	ifp->if_name = "ace";
	ifp->if_mtu = ETHERMTU;

	/*
	 * Set vector,station's addresses,cleanup dpm.
	 */
		/* set the station address, copy to arp structure */
	acesetetaddr(unit,addr, &ace_station[unit * 6]);
	/*
	for (pData0 = (short *)addr->rar.station_addr,
	     pData1 = &ace_station[unit * 6], i = 6;
	     --i >= 0;) {
	    movew((short)(*pData1++),(pData0++)); 
	    }
	for (pData0 = (short *)addr->rar.station_addr,
	     pData1 = (char *)(is->is_addr), i = 6;
	     --i >= 0;)
	    *(pData1++) = ~(*(pData0++));

	ACETRACE(ALLTRACE,
		printf("ace%d: addr=%x:%x:%x:%x:%x:%x\n",
			unit,
			is->is_addr[0]&0xff, is->is_addr[1]&0xff,
			is->is_addr[2]&0xff, is->is_addr[3]&0xff,
			is->is_addr[4]&0xff, is->is_addr[5]&0xff);)
	*/

	is->is_promiscuous = (FALSE)? CSR_PROMISCUOUS: 0;
	for (pData0 = (short *)addr->rar.mcast_hash_code, 
	     pData1 = &ace_hash_code[unit * 8], i =  8;
	     --i >= 0;){
	    movew((short)(*pData1++),(pData0++)); 
	}

	movew((short)~0xffff,&addr->rar.broadcast_en[0]); 
	movew((short)~0xffff,&addr->rar.broadcast_en[1]);

	aceclean((int)unit);

	sin = (struct sockaddr_in *)&ifp->if_addr;
	sin->sin_family = AF_INET;
	sin->sin_addr = arpmyaddr((struct arpcom *)0);
	ifp->if_init = aceinit;
	ifp->if_output = aceoutput;
	ifp->if_ioctl = aceioctl;
	ifp->if_reset = acereset;
	if_attach(ifp);
}

acesetetaddr(unit,addr,station_addr)
short unit;
struct acedevice *addr;
char *station_addr;
{
register short *pData0, i;
register char *pData1;
register struct ace_softc *is = &ace_softc[unit];

	for (pData0 = (short *)addr->rar.station_addr,
	     	pData1 = station_addr, i = 6; --i >= 0;) {
	    movew((short)(*pData1++),(pData0++)); 
	    }
	for (pData0 = (short *)addr->rar.station_addr,
	     	pData1 = (char *)(is->is_addr), i = 6; --i >= 0;)
	    *(pData1++) = ~(*(pData0++));

	ACETRACE(SETETTRACE,
		printf("ace%d: addr=%x:%x:%x:%x:%x:%x\n",
			unit,
			is->is_addr[0]&0xff, is->is_addr[1]&0xff,
			is->is_addr[2]&0xff, is->is_addr[3]&0xff,
			is->is_addr[4]&0xff, is->is_addr[5]&0xff);)
}


/*
 * Reset of interface after "system" reset.
 */
acereset(unit, vban)
int unit, vban;
{
register struct vba_device *ui;

	ACETRACE(ALLTRACE, printf("ace%dreset:\n",unit,vban);)

	if (unit >= NACE || (ui = aceinfo[unit]) == 0 || ui->ui_alive == 0 ||
	    ui->ui_vbanum != vban)
		return;
	printf(" ace%d", unit);
	aceinit(unit);
}

/*
 * Initialization of interface; clear recorded pending operations
 */
aceinit(unit)
int unit;
{
register struct ace_softc *is = &ace_softc[unit];
register struct vba_device *ui = aceinfo[unit];
register struct acedevice *addr;
register struct ifnet *ifp = &is->is_if;
register struct sockaddr_in *sin;
register short Csr;
register int i,s;

	ACETRACE(ALLTRACE, printf("ace%dinit:\n",unit);)

	sin = (struct sockaddr_in *)&ifp->if_addr;
	if (sin->sin_addr.s_addr == 0)		/* address still unknown */
		return;
	if (!(ifp->if_flags & IFF_RUNNING))
	  {

	/*
	 * Reset the controller, initialize the recieve buffers,
	 * and turn the controller on again and set board online.
	 */
	    addr = (struct acedevice *)ui->ui_addr;
	    s = splimp();
	    movew((short)CSR_RESET,&addr->csr);
	    DELAY(10000);

	    /* clean up dpm since the controller might jumble dpm after
	       reset */
	    aceclean((int)unit);
	    movew((short)CSR_GO,&addr->csr);	/* addr->csr = CSR_GO; */
	    Csr = addr->csr;
	    if (Csr & CSR_ACTIVE)
	      {
		movew((short)(ACEVECTOR + (unit*8)),&addr->ivct);
		Csr |= CSR_ENINT | is->is_promiscuous;
		if (ifp->if_net == LONET)
		  	Csr |= (CSR_LOOP3);
		movew(Csr,&addr->csr);

		is->is_flags = 0;
		is->is_xcnt = 0;
		is->is_if.if_flags |= IFF_UP|IFF_RUNNING;
	      }
	    else
	      {
 		ACETRACE(ALLTRACE,
			printf("ace%dinit: would not go active\n",unit);)
	      }
	    splx(s);
	  }

	if (is->is_if.if_flags & IFF_UP) {
		if_rtinit(&is->is_if, RTF_UP);
		aceStart((int)unit);
		}
	arpattach(&is->is_ac);
	arpwhohas(&is->is_ac, &sin->sin_addr);
}

/*
 * Start output on interface.
 * Get another datagram to send off of the interface queue,
 * and map it to the interface before starting the output.
 *
 */
acestart(dev)
dev_t dev;
{
register struct tx_segment *txs;
register long len,x;
int unit = ACEUNIT(dev);
struct vba_device *ui = aceinfo[unit];
register struct acedevice *addr = (struct acedevice *)ui->ui_addr;
register struct ace_softc *is = &ace_softc[unit];
register long	tripnum = 0;
struct mbuf *m;
short retries,idx;

	ACETRACE(STARTTRACE,
		printf("ace%dstart: dev %X, txnext seg %x\n",unit,dev,
			(addr->tseg >> 11) & 0xf);)

	ACESTUDY(is->is_stats.tx_startcnt++;)
again:
	txs = (struct tx_segment*)(is->is_dpm + (is->is_txnext << 11));
	if (txs->tx_csr & TCS_TBFULL) {
		ACETRACE(ALLTRACE, printf("ace%dstart:segment %x is busy\n", 
				unit, is->is_txnext);)
		is->is_stats.tx_busy++;
		return;
	}

	x = splimp();
	IF_DEQUEUE(&is->is_if.if_snd, m);
	splx(x);
	if (m == 0)
		return;

	len = aceput((int) unit, txs->tx_data, m);
	retries = (txs->tx_csr & TCS_RTC);
	acebakoff(is,txs,retries);

	/*
	 * Ensure minimum packet length.
	 * This makes the safe assumtion that there are no virtual holes
	 * after the data.
	 * For security, it might be wise to zero out the added bytes,
	 * but we're mainly interested in speed at the moment.
	 */
	if (len - SIZEOF_ETHEADER < ETHERMIN)
		len = ETHERMIN + SIZEOF_ETHEADER;
	if (++is->is_txnext > SEG_MAX) 
		is->is_txnext = is->is_segboundry;
	is->is_if.if_opackets++;
	is->is_xcnt++;
	len = (len & 0x7fff) | TCS_TBFULL;

	ACETRACE(STARTTRACE,
	     printf("ace%dstart GO [csr %x][%x,%x,%x,%x,%x,%x] [%x,%x,%x,%x,%x,%x] [%x] [%x] \n",unit,
			(len & 0xffff),
			(txs->tx_data[0] & 0xff),(txs->tx_data[1] & 0xff), 
			(txs->tx_data[2] & 0xff),(txs->tx_data[3] & 0xff), 
			(txs->tx_data[4] & 0xff),(txs->tx_data[5] & 0xff), 
			(txs->tx_data[6] & 0xff),(txs->tx_data[7] & 0xff), 
			(txs->tx_data[8] & 0xff),(txs->tx_data[9] & 0xff), 
			(txs->tx_data[10] & 0xff),(txs->tx_data[11] & 0xff),
			(txs->tx_data[12] & 0xff),(txs->tx_data[13] & 0xff));)
	movew((short)len,txs);

	goto again;
}

/*
 * Transmit done interrupt.
 */
acecint(unit)
int unit;
{
register struct ace_softc *is = &ace_softc[unit];
struct vba_device *ui = aceinfo[unit];
register struct acedevice *addr = (struct acedevice *)ui->ui_addr;
register struct tx_segment *txseg;	/* ptr to V/EIU tx seg            */
short txidx,eostat;

	ACESTUDY(is->is_stats.tx_cintcnt++;)

	if (is->is_xcnt <= 0)  {
		txidx = ((addr->tseg >> 11) & 0xf);
	    	printf("ace%dcint: stray xmit interrupt,xcnt %d\n",
			unit,is->is_xcnt);
		printf("\t**eoctr %d, ctlr's tseg %d\n",is->is_eoctr,txidx);
		is->is_xcnt = 0;
		aceStart((int)unit);
		return;
	}

	is->is_xcnt--;
	txseg = (struct tx_segment *)((is->is_eoctr << 11) + is->is_dpm);
	if (((eostat = txseg->tx_csr) & TCS_TBFULL) == 0) {
	    	is->is_stats.tx_retries += (eostat & TCS_RTC);
	    	if (eostat & TCS_RTFAIL)  {
			is->is_stats.tx_discarded++;
			is->is_if.if_oerrors++;
		}
	    	else 
			is->is_stats.tx_datagrams++;

	    	if (++is->is_eoctr >= 16)
	      		is->is_eoctr = is->is_segboundry; 
	} 
	else {
		ACESTUDY(is->is_stats.tx_cbusy++;)
	}

	aceStart((int)unit);
}

/*
 * Ethernet interface receiver interrupt.
 * If input error just drop packet.
 * Otherwise purge input buffered data path and examine 
 * packet to determine type.  If can't determine length
 * from type, then have to drop packet.  Othewise decapsulate
 * packet based on type and pass to type specific higher-level
 * input routine.
 */


acerint(unit)
int unit;
{
register struct ace_softc *is = &ace_softc[unit];
register struct ifqueue *inq;
register struct ether_header *ace;
register struct rx_segment *rxseg;	/* ptr to V/EIU rx seg            */
register long  tripnum = 0;
struct acedevice *addr = (struct acedevice *)aceinfo[unit]->ui_addr;
short eistat;
struct mbuf *m;
struct inp_err	*acer = &aceerr[unit];
int len, s, off, resid, orglen;
	
	ACESTUDY(is->is_stats.rx_rintcnt++;)
more:
	rxseg = (struct rx_segment *)((is->is_eictr << 11) + is->is_dpm);

	if ((eistat = rxseg->rx_csr) & RCS_RBFULL)
	  {
	    is->is_if.if_ipackets++;
	    if (++is->is_eictr >= is->is_segboundry) 
		    is->is_eictr = 0;

	    ACETRACE(RTINTRACE, printf("ace%drint: is_eictr %x,[csr %x]\n",
			unit,is->is_eictr,(eistat & 0xffff));)
	    ACESTUDY(if (tripnum) is->is_stats.rx_rwoint++;)

	    len = (eistat & RCS_RBC);
	    if ((eistat & (RCS_ROVRN | RCS_RCRC | RCS_RODD)) ||
		(len < ET_MINLEN) || (len > (ET_MAXLEN+CRC_SIZE)))
	      {
		if (eistat & RCS_ROVRN) is->is_stats.rx_overruns++;
		if (eistat & RCS_RCRC) is->is_stats.rx_crc_errors++;
		if (eistat & RCS_RODD) is->is_stats.rx_align_errors++;
		if (len < ET_MINLEN) is->is_stats.rx_underruns++;
		if (len > (ET_MAXLEN+CRC_SIZE)) is->is_stats.rx_overruns++;
		is->is_if.if_ierrors++;
		rxseg->rx_csr = 0;

		ACETRACE( RTINTRACE,
			if (is->is_if.if_ierrors % 100 == 0)
				printf("ace%d: += 100 input errors\n", unit);)
		return;
	      }
	    else is->is_stats.rx_datagrams++;
	  }
	else
	  {
	    if (tripnum==0) {
		ACESTUDY(is->is_stats.rx_busy++;)
	    }
	    return;
	  }
	ace = (struct ether_header *)(rxseg->rx_data);
	orglen = len;
	len -= SIZEOF_ETHEADER;

	/*
	 * Deal with trailer protocol: if type is PUP trailer
	 * get true type from first 16-bit word past data.
	 * Remember that type was trailer by setting off.
	 */
	ace->ether_type = ntohs((u_short)ace->ether_type);
#define	acedataaddr(ace, off, type)	((type)(((caddr_t)(((char *)ace)+SIZEOF_ETHEADER)+(off))))

/*#define acedataaddr(ace, off, type) ((type)(((caddr_t)((ace)+1)+(off)))) */

	if (ace->ether_type >= ETHERPUP_TRAIL &&
	    ace->ether_type < ETHERPUP_TRAIL+ETHERPUP_NTRAILER) {
		off = (ace->ether_type - ETHERPUP_TRAIL) * 512;
		if (off >= ETHERMTU) {
			acer->bad_offset++;
			INPTRACE(saverrpkt(rxseg->rx_data, B_OFFSET, orglen);) 
			goto setup;		/* sanity */
		}
		ace->ether_type = ntohs(*acedataaddr(ace, off, u_short *));
		resid = ntohs(*(acedataaddr(ace, off+2, u_short *)));
		if (off + resid > len) {
			acer->bad_length++;
			INPTRACE(saverrpkt(rxseg->rx_data, B_LENGTH, orglen);) 
			goto setup;		/* sanity */
		}
		len = off + resid;
	} else
		off = 0;
	if (len == 0) {
		acer->bad_length++;
		INPTRACE(saverrpkt(rxseg->rx_data, B_LENGTH, orglen);) 
		goto setup;
	}

	/*
	 * Pull packet off interface.  Off is nonzero if packet
	 * has trailing header; aceget will then force this header
	 * information to be at the front, but we still have to drop
	 * the type and length which are at the front of any trailer data.
	 */
	m = aceget((int)unit, rxseg->rx_data, len, off);
	if (m == 0) {
		acer->h_nobuffer++; /* host runs out of buf */
		goto setup;
	}
	if (off) {
		m->m_off += 2 * sizeof (u_short);
		m->m_len -= 2 * sizeof (u_short);
	}
	switch (ace->ether_type) {

#ifdef INET
	case ETHERPUP_IPTYPE:
		if (tripnum == 0) {
			/* rely on the fact that ip_input() will drain
			   the incoming queue after the first software 
			   interrupt */
			schednetisr(NETISR_IP);
		}
		inq = &ipintrq;
		break;

	case ETHERPUP_ARPTYPE:
		arpinput(&is->is_ac, m);
		goto setup;
#endif
	default:
		acer->bad_packetype++;
		if (printerror) { 
			printf("\nace%d: Undefined packet type 0x%x ", unit,
				ace->ether_type);
			printf("from host: %x.%x.%x.%x.%x.%x\n", 
				ace->ether_shost[0], ace->ether_shost[1], 
				ace->ether_shost[2], ace->ether_shost[3],
				ace->ether_shost[4], ace->ether_shost[5]);
		}	
		INPTRACE(saverrpkt(rxseg->rx_data, B_PACKETYPE, orglen);) 
		m_freem(m);
		goto setup;
	}

	if (IF_QFULL(inq)) {
		acer->inq_full++;
		IF_DROP(inq);
		m_freem(m);
		goto setup;
	}
	s = splimp();
	IF_ENQUEUE(inq, m);
	splx(s);

	ACETRACE(RTINTRACE1,printf("ace%dacerint: enqlen %d: ",
					unit, inq->ifq_len);)
	ACETRACE(RTINTRACE1,aceprm(unit, m);)
setup:
	rxseg->rx_csr = 0;
	goto more;
}

/*
 * Ethernet output routine.
 * Encapsulate a packet of type family for the local net.
 * Use trailer local net encapsulation if enough data in first
 * packet leaves a multiple of 512 bytes of data in remainder.
 */
aceoutput(ifp, m0, dst)
struct ifnet *ifp;
struct mbuf *m0;
struct sockaddr *dst;
{
register short unit = ifp->if_unit;
register struct ace_softc *is = &ace_softc[unit];
register struct mbuf *m = m0;
register struct ether_header *ace;
register int off;
register struct mbuf *mcopy;
int type, s, error;
u_char edst[6];
struct in_addr idst;

#ifndef INET
	ACETRACE(ACEOTRACE, 
		printf("ace%doutput: ifp %lx, m0 %lx, dst ADDR %lx: ", 
			unit,ifp,m0, dst);)
	ACETRACE(ACEOTRACE,aceprm(unit, m0);)
#endif

	switch (dst->sa_family) {

#ifdef INET
	case AF_INET:
		idst = ((struct sockaddr_in *)dst)->sin_addr;
		ACETRACE(ACEOTRACE, 
			printf("ace%doutput: ifp %lx, m0 %lx, dst %lx: ", 
				unit,ifp,m0, idst.S_un.S_addr);)
		ACETRACE(ACEOTRACE,aceprm(unit, m0);)

		/* translate internet to ethernet address */
		switch(arpresolve(&is->is_ac, m, &idst, edst)) {
		case ARPRESOLVE_WILLSEND:
			return (0);	/* if not yet resolved */
		case ARPRESOLVE_BROADCAST:
			mcopy = m_copy(m, 0, (int)M_COPYALL);
			MBUFNULL(42, mcopy);	/* for debugging */
			if (mcopy)
				looutput(&loif, mcopy, dst);

			/* falls through ... */
		case ARPRESOLVE_OK:
			break;
		}

		off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;

		/* need per host negotiation */
		if ((ifp->if_flags & IFF_NOTRAILERS) == 0)
		  if (off > 0 && (off & 0x1ff) == 0 &&
		      m->m_off >= MMINOFF + 2 * sizeof (u_short))
		    {
			type = ETHERPUP_TRAIL + (off>>9);
			m->m_off -= 2 * sizeof (u_short);
			m->m_len += 2 * sizeof (u_short);
			*mtod(m, u_short *) = htons((u_short)ETHERPUP_IPTYPE);
			*(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
			goto gottrailertype;
		    }
		type = ETHERPUP_IPTYPE;
		off = 0;
		goto gottype;
#endif

	case AF_UNSPEC:
		ace = (struct ether_header *)dst->sa_data;
		bcopy((caddr_t)ace->ether_dhost, (caddr_t)edst, sizeof (edst));
		type = ace->ether_type;
		goto gottype;

	default:
		if (printerror)
		     printf("ace%d: can't handle af%d\n", unit,dst->sa_family);
		error = EAFNOSUPPORT;
		goto bad;
	}

gottrailertype:
	/*
	 * Packet to be sent as trailer: move first packet
	 * (control information) to end of chain.
	 */
	while (m->m_next)
		m = m->m_next;
	m->m_next = m0;
	m = m0->m_next;
	m0->m_next = 0;
	m0 = m;

gottype:
	/*
	 * Add local net header.  If no space in first mbuf,
	 * allocate another.
	 */
	if (m->m_off > MMAXOFF ||
	    MMINOFF + SIZEOF_ETHEADER > m->m_off) { 
		m = m_get(M_DONTWAIT, MT_HEADER);
		MBUFNULL(30, m);
		if (m == 0) {
			error = ENOBUFS;
			goto bad;
		}
		m->m_next = m0;
		m->m_off = MMINOFF;
		m->m_len = SIZEOF_ETHEADER;
	} else {
		m->m_off -= SIZEOF_ETHEADER;
		m->m_len += SIZEOF_ETHEADER;
	}
	ace = mtod(m, struct ether_header *);
	ace->ether_type = htons((u_short)type);
	bcopy((caddr_t)edst, (caddr_t)ace->ether_dhost, sizeof (edst));
	bcopy((caddr_t)is->is_addr, (caddr_t)ace->ether_shost, 6);

	/*
	 * Queue message on interface, and start output if interface
	 * not yet active.
	 */
	s = splimp();
	if (IF_QFULL(&ifp->if_snd)) {
		IF_DROP(&ifp->if_snd);
		splx(s);
		m_freem(m);
		return (ENOBUFS);
	}
	IF_ENQUEUE(&ifp->if_snd, m);
	splx(s);

	ACETRACE (ACEOTRACE, printf("ace%doutput: enqu mbuf %lx: ",unit,m);)
	ACETRACE (ACEOTRACE, aceprm(unit, m);)
	ACESTUDY (is->is_stats.tx_outcnt++;)

	aceStart((int)unit);
	return (0);

bad:
	ACETRACE(ACEOTRACE, printf("ace%doutput: BAD.\n");)
	m_freem(m0);
	return (error);
}

aceStart(unit)
int unit;
{
register struct ace_softc *is = &ace_softc[unit];

	if (!(is->is_flags & ACEF_OACTIVE)) {
		is->is_flags |= ACEF_OACTIVE;
		acestart((dev_t)unit);
		is->is_flags &= ~ACEF_OACTIVE;
	}
}

/*
 * Routine to copy from mbuf chain to transmit buffer on the VERSAbus
 * If packet size is less than the minimum legal size,
 * the buffer is expanded.  We probably should zero out the extra
 * bytes for security, but that would slow things down.
 */
aceput(unit, txbuf, m)
int	unit;			/* for statistics collection */
u_char *txbuf;
struct mbuf *m;
{
register	u_char *bp;
register	u_char *mcp;
register 	short *s1;
register 	short *s2;
register	unsigned len;
register	struct mbuf *mp;
int total, idx;

	ACETRACE(STARTTRACE, printf("ace%daceput: txbuf %X, mbuf %X, m->m_off %d\n",
		unit, txbuf, m, m->m_off);)
	ACETRACE(STARTTRACE,aceprm(unit, m);)

	total = 0;
	bp = (u_char *)txbuf;
	for (mp = m;(mp); mp = mp->m_next)
	  {
		len = mp->m_len;
		if (len == 0)
			continue;

		total += len;
		mcp = mtod(mp, u_char *);

		/*bcopy((caddr_t)mcp,(caddr_t)bp,len);*/
		/*bp += len;*/

		if (((int)mcp & 01) && ((int)bp & 01)) {
			/* source & destination at odd addresses */
			/* *bp++ = *mcp++; */
			asm("movob (r11),(r12)");
			asm("addl2 $1,r12");
			asm("addl2 $1,r11");
			--len;
		}
		if (len > 1 && (((int)mcp & 01)==0) && (((int)bp & 01)==0)) {
			register int	l;

			s1 = (short *)bp;
			s2 = (short *)mcp;
			l = (len >> 1);		/* count # of shorts */
			while (l-->0) {		/* copy shorts */
				/* *s1++ = *s2++; */
				asm("movow (r9),(r10)");
				asm("addl2 $2,r10");
				asm("addl2 $2,r9");
			}
			len &= 1;		/* # remaining bytes */
			bp = (u_char *)s1;
			mcp = (u_char *)s2;
		}
		while (len-- > 0) {
			/* *bp++ = *mcp++;  */
			asm("movob (r11),(r12)");
			asm("addl2 $1,r12");
			asm("addl2 $1,r11");
		}
	  }
	m_freem(m);
	return(total);
}

movew(data,to)
short data;
short *to;
{
	asm("movow 6(fp),*8(fp)");
}


#ifdef ACEPRINTFS
aceprm(unit, mp)
int unit;
struct mbuf *mp;
{
	register u_char *mcp;
	register struct mbuf *mpp;
	register long idx;
	register long totlen,lx;

	    for(totlen=0,mpp=mp->m_next; mpp; mpp=mpp->m_next) 
		totlen += mpp->m_len; 

	    mcp = mtod(mp, u_char *);
	    printf("ace%d mbuf[len %d,off %d],[next %lx,totlen %ld],[",
			unit, mp->m_len,mp->m_off,mp->m_next,totlen);
	    for (idx=0 ; (idx < 20 ); idx++)
		  {
		    printf("%x ",mcp[idx] & 0xff);
		  }
	    printf("]\n");
}
#endif ACEPRINTFS

/*
 * Routine to copy from VERSAbus memory into mbufs.
 *
 * Warning: This makes the fairly safe assumption that
 * mbufs have even lengths.
 */
struct mbuf *
aceget(unit, rxbuf, totlen, off0)
int	unit;			/* for statistics collection */
u_char *rxbuf;
int totlen, off0;
{
register	u_char *cp;
register	u_char *mcp;
register	int	tlen;
int		len;

register struct mbuf *m;
struct mbuf *top = 0, **mp = &top;
int off = off0;

	ACETRACE(RTINTRACE, printf("ace%daceget: rxbuf %lx, len %d, off %d\n",
				unit, rxbuf,totlen,off0);)

	cp = rxbuf + SIZEOF_ETHEADER;
	while (totlen > 0)
	  {
	  register int words;

		MGET(m, M_DONTWAIT, MT_DATA);
		MBUFNULL(33, m);
		if (m == 0)
			goto bad;
		if (off) {
			len = totlen - off;
			cp = rxbuf +
				SIZEOF_ETHEADER + off;
		} else
			len = totlen;

		if (len >= CLBYTES) {
			struct mbuf *p;

			MCLGET(p, 1);
			if (p != 0) {
				m->m_len = len = CLBYTES;
				m->m_off = (int)p - (int)m;
			} else {
				m->m_len = len = MIN(MLEN, len);
				m->m_off = MMINOFF;
			}
		} else {
			m->m_len = len = MIN(MLEN, len);
			m->m_off = MMINOFF;
		}

		mcp = mtod(m, u_char *);

		/*bcopy((caddr_t)cp, (caddr_t)mcp, len);*/
		/*cp += len; mcp += len;*/

		tlen = len;
		if (((int)mcp & 01) && ((int)cp & 01)) {
			/* source & destination at odd addresses */
			*mcp++ = *cp++;
			--tlen;
		}
		if (tlen > 1 && (((int)mcp & 01)==0) && (((int)cp & 01)==0)) {
			register short	*s1;
			register short	*s2;
			register int	l;

			s1 = (short *)mcp;
			s2 = (short *)cp;
			l = (tlen >> 1);		/* count # of shorts */
			while (l-->0)		/* copy shorts */
				*s1++ = *s2++;
			tlen &= 1;		/* # remaining bytes */
			mcp = (u_char *)s1;
			cp = (u_char *)s2;
		}
		while (tlen-- > 0)
			*mcp++ = *cp++;

		*mp = m;
		mp = &m->m_next;
		if (off == 0) {
			totlen -= len;
			continue;
		}
		off += len;
		if (off == totlen) {
			cp = rxbuf + SIZEOF_ETHEADER;
			off = 0;
			totlen = off0;
		}
	  }
	return (top);
bad:
	m_freem(top);
	return (0);
}

/*+procedure							*/
/*								*/

acebakoff(is, txseg, retries)
struct ace_softc *is;		/* ptr to UCB for this device     */
struct tx_segment *txseg;	/* ptr to V/EIU tx seg            */
register int retries;		/* # of randoms to generate       */
{
short *pMask;			/* ptr to mask table.	     */
register short *pBakNum;	/* ptr to # entry in Tx seg table */
register short random_num;	/* random number value.	     */


	pMask   = &random_mask_tbl[0];
	pBakNum = &txseg->tx_backoff[0];

	for (; --retries >= 0;)
	  {
	    random_num   = (is->is_currnd = (is->is_currnd * 18741)-13849);
	    random_num  &= *(pMask++);
	    *(pBakNum++) = random_num ^ (short)(0xFF00 | 0x00FC);
	  }
}

/*
 * Process an ioctl request.
 */
aceioctl(ifp, cmd, data)
register struct ifnet *ifp;
int cmd;
caddr_t data;
{
register short unit = ifp->if_unit;
register struct vba_device *ui;
register struct acedevice *addr;
register struct sockaddr_in *et_addr;
register struct ace_softc *is = &ace_softc[ifp->if_unit];
register struct ifreq *ifr = (struct ifreq *)data;
int s , error = 0;

	ACETRACE (IOCTLTRACE,
		printf("ace%daceioctl: ifp %X, cmd %X, data %X\n", unit, ifp, cmd, data);)

	switch (cmd) {

	case SIOCSIFADDR:
		s = splimp();
		if (ifp->if_flags & IFF_RUNNING)
			if_rtinit(ifp, -1);	/* delete previous route */
		acesetaddr(ifp, (struct sockaddr_in *)&ifr->ifr_addr);
		aceinit(ifp->if_unit);
		splx(s);
		break;

	case SIOCSETETADDR:	/* Set Ethernet station address */
		s = splimp();
		ifp->if_flags &= (~IFF_RUNNING | IFF_UP);
		et_addr = (struct sockaddr_in *)&ifr->ifr_addr;
		ui = aceinfo[ifp->if_unit];
		addr = (struct acedevice *)ui->ui_addr;
	    	movew((short)CSR_RESET,&addr->csr);	/* Reset again */
	    	DELAY(10000);
		/* Set station address and copy addr to arp struct */
		acesetetaddr(ifp->if_unit,addr,&et_addr->sin_zero[2]);
		aceinit(ifp->if_unit);		/* Re-initialize */
		splx(s);
		break;

	case SIOCGETETADDR:	/* Get Foreign Hosts' Ethernet address */
		arpwhohas(&is->is_ac, (struct in_addr *)ifr->ifr_data);
		break;

	default:
		error = EINVAL;
	}
	return (error);
}

acesetaddr(ifp, sin)
register struct ifnet *ifp;
register struct sockaddr_in *sin;
{
register short unit = ifp->if_unit;

	ACETRACE (IOCTLTRACE, printf("ace%dacesetaddr: ifp %X, sin %X\n",unit,ifp,sin);)

	arpremove(&ifp->if_addr);
	ifp->if_addr = *(struct sockaddr *)sin;
	ifp->if_net = in_netof(sin->sin_addr);
	ifp->if_host[0] = in_lnaof(sin->sin_addr);
	sin = (struct sockaddr_in *)&ifp->if_broadaddr;
	sin->sin_family = AF_INET;
	sin->sin_addr = if_makeaddr(ifp->if_net, INADDR_ANY);
	ifp->if_flags |= IFF_BROADCAST;
}

aceclean(unit)
int	unit;
{
register struct ace_softc *is = &ace_softc[unit];
register struct ifnet *ifp = &is->is_if;
register struct vba_device *ui = aceinfo[unit];
register struct acedevice *addr = (struct acedevice *)ui->ui_addr;
register short i, data;
register char *pData1;

	is->is_dpm = acemap[unit];		/* init dpm		*/
	ioaccess(ACEmap[unit],ACEmapa[unit],ACEBPTE);
	bzero((char *)is->is_dpm, (16384*2));

	is->is_currnd = 49123;
	is->is_segboundry = (addr->segb >> 11) & 0xf;
	for (pData1 = (char*)((int)is->is_dpm + (is->is_segboundry << 11)),
	     i = (SEG_MAX + 1) - is->is_segboundry;
	     (--i >= 0); pData1 += sizeof (struct tx_segment))
	     acebakoff(is,((struct tx_segment*)pData1),15);

	is->is_eictr = 0;
	is->is_eoctr = is->is_txnext = is->is_segboundry;
	bzero((char *)&is->is_stats, sizeof(is->is_stats));

}
#endif	NACE > 0

unix.superglobalmegacorp.com

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