V8/usr/sys/chncp/chil.c

Compare this file to the similar file:
Show the results in this format:

#include "../chunix/chsys.h"
#include "../chunix/chconf.h"
#include "../chaos/chaos.h"
#include "../chaos/address-res.h"

#ifdef VMUNIX
#include "../h/buf.h"
#include "../h/ubavar.h"
#include "../h/pte.h"
#include "../h/ubareg.h"
#endif VMUNIX

/*
 * Chaos device driver for the interlan ethernet interface.
 *
 * This driver depends heavily on the fact that the device will
 * interrupt exactly once per receive buffer and once per command
 * regardless of how many times the CSR is read (and thus the
 * DONE bits cleared).
 * Until a lot of this chaos code is rewritten, it is too painful to DMA
 * directly into a packet buffer since the buffers must be > 512 bytes, even
 * for the smallest packet.  Thus we just have fixed buffers, and copy the data out.
 * For transmission, we do indeed DMA directly from packets.
 * Note that for the VAX we permanently allocate a buffered data path for transmit,
 * unless NOHOG is defined, in which case we allocate as needed.
 * For receive, we use a direct data path and a permanently-mapped buffer.
 *
 * NOTE!!! The size of this structure must not be an even multiple of 8 !!!
 * If it is then "buffer chaining" will occur!
 */
union chilpkt {
	struct il_stat					ilp_stat;
	struct {
		struct il_rheader			ilp_Rhdr;
		union {
			struct ar_packet		ilp_Arpkt;
			struct {
				struct pkt_header	ilp_Chhead;
				char		ilp_Chdata[CHMAXDATA];
			}				ilp_Chpkt;
		}					ilp_data;
		char					ilp_crc[4];
	}						ilp_edata;
};
#define ilp_arpkt	ilp_edata.ilp_data.ilp_Arpkt
#define ilp_chhead	ilp_edata.ilp_data.ilp_Chpkt.ilp_Chhead
#define ilp_chdata	ilp_edata.ilp_data.ilp_Chpkt.ilp_Chdata
#define ilp_chpkt	ilp_edata.ilp_data.ilp_Chpkt
#define ilp_rhdr	ilp_edata.ilp_Rhdr
int chilstart(), chilreset();

/*
#define	DEBUG
*/

#ifdef DEBUG
#define IF_DEBUG(x)	(x)
#else
#define IF_DEBUG(x)
#endif

#define NOHOG      /* Don't hog buffered data path (for xmit) */

#define splIL()		spl5()

/*
 * Address resolution data:
 * This should move to an "address resolution" file when more than one
 * Ethernet device is supported.
 */
#define ETHER_BROADCAST {-1, -1, -1, -1, -1, -1}
u_char ether_broadcast[6] = ETHER_BROADCAST;
#define NPAIRS	20	/* how many addresses should we remember */
struct ar_pair	{
	chaddr	arp_chaos;
	u_char 	arp_ether[6];
	long	arp_time;
};
long	arptime;	/* LRU clock for ar_pair slots */

/*
 * Canned request packet we send out when we don't have the ethernet address
 * of an outgoing packet.
 */
struct chilapkt	{
	struct il_xheader	ar_xhdr;
	struct ar_packet	ar_pkt;
};
/*
 * Software state per interface.
 */
struct chilsoft {
	char	il_unit;
#ifdef VMUNIX
	char	il_ubanum;
	char	il_rbdp;
	char	il_xbdp;
	int	il_ruba;
	int	il_xuba;
#endif
	short	il_oactive;	/* is output active? */
	short	il_startrcv;	/* hang receive next chance */
	short	il_scsr;
	u_char	il_enaddr[6];	/* board's ethernet address */
	short	il_doreply;	/* Reply buffer full */
	short	il_replying;	/* Reply buffer being transmitted */
	struct ildevice		*il_devaddr;
	struct chxcvr		il_xcvr;
	struct ar_pair		il_pairs[NPAIRS];
	struct ar_pair		*il_epairs;
	struct il_xheader	il_savex;	/* Yick. */
	union chilpkt		il_rpkt;	/* DMA input buffer */
	struct chilapkt		il_arrequest;	/* Address request buffer */
	struct chilapkt		il_arreply;	/* Address reply buffer */
} chilsoft[NCHIL];

/*
 * Error codes
 */
char *ilerrs[] = {
	"success",			/* 0 */
	"success with retries", 	/* 01 */
	"illegal command",		/* 02 */
	"inappropriate command",	/* 03 */
	"failure",			/* 04 */
	"buffer size exceeded",		/* 05 */
	"frame too small",		/* 06 */
	0,				/* 07 */
	"excessive collisions",		/* 010 */
	0,				/* 011 */
	"buffer alignment error",	/* 012 */
	0,				/* 013 */
	0,				/* 014 */
	0,				/* 015 */
	0,				/* 016 */
	"non-existent memory"		/* 017 */
};

char *ildiag[] = {
	"success",			/* 0 */
	"checksum error",		/* 1 */
	"NM10 dma error",		/* 2 */
	"transmitter error",		/* 3 */
	"receiver error",		/* 4 */
	"loopback failure",		/* 5 */
};
long chillost;
long chilarrp;
long chilarls;
long chilxstat;
long chilrst;
long chilrin;
long chilcin;
long chilar;
long chilxrq;
long chilxrp;

#ifdef VMUNIX

/*
 * This stuff should be elsewhere, but isn't right now.
 */

#ifdef VAX780
#define UBAPURGEBITS UBADPR_BNE
#else
#define UBAPURGEBITS (UBADPR_PURGE|UBADPR_NXM|UBADPR_UCE)
#endif VAX780

#define PURGEBDP(ubanum, bdpnum) \
	uba_hd[ubanum].uh_uba->uba_dpr[bdpnum] |= UBAPURGEBITS

/*
 * Autoconfiguration support for VMUNIX
 */
int chilprobe(), chilattach(), chilrint(), chilcint();
struct uba_device *chilinfo[NCHIL];
unsigned short chilstd[] = { 0 };
struct uba_driver childriver = { chilprobe, 0, chilattach, 0, chilstd, "chil", chilinfo };

/*
 * Do an OFFLINE command to cause an interrupt for the
 * autoconfigure stuff.
 */
chilprobe(reg)
	caddr_t reg;
{
	register int br, cvec;		/* r11, r10 */
	register struct ildevice *addr = (struct ildevice *)reg;
	register int i;

#ifdef lint
	{
		int j = 0;

		br = 0; cvec = br; br = cvec;
		i = j; j = i;
		chilrint(0); chilcint(0);
	}
#endif
	addr->il_csr = ILC_OFFLINE|IL_CIE;
	DELAY(100000);
	i = addr->il_csr;		/* clear CDONE */
	if (cvec > 0 && cvec != 0x200)
		cvec -= 4;

	return (1);
}

/*
 * Attach interface
 */
chilattach(ui)
	register struct uba_device *ui;
{
	chilsoft[ui->ui_unit].il_ubanum = ui->ui_ubanum;
	chilsetup(ui->ui_unit, (struct ildevice *)ui->ui_addr);
}
#else VMUNIX
int chilndev = NCHIL;
int chiladdr[NCHIL] = { CHIL_ADDR };
#endif VMUNIX

/*
 * Do the run-time initialization of the given transceiver.
 * Reception is not enabled.
 */
chilsetup(unit, addr)
int unit;
register struct ildevice *addr;
{
	register struct chilsoft *sp = &chilsoft[unit];
	int s;

	sp->il_xcvr.xc_devaddr = (unsigned *)addr;	/* This indicates attachment! */
	sp->il_xcvr.xc_addr = 0;	/* We don't know chaos addr yet */
	sp->il_xcvr.xc_start = chilstart;
	sp->il_xcvr.xc_reset = chilreset;
	sp->il_xcvr.xc_ilinfo.il_soft = sp;
	sp->il_epairs = &sp->il_pairs[NPAIRS];
	sp->il_devaddr = addr;
	sp->il_unit = unit;

#ifdef VMUNIX

#ifndef NOHOG
	sp->il_xuba = uballoc(sp->il_ubanum, (caddr_t)&sp->il_rpkt,
				sizeof(union chilpkt), UBA_CANTWAIT|UBA_NEEDBDP);
	sp->il_xbdp = (sp->il_xuba >> 28) & 0x0f;
#endif not NOHOG

	sp->il_ruba = uballoc(sp->il_ubanum, (caddr_t)&sp->il_rpkt,
				sizeof(union chilpkt), UBA_CANTWAIT);
	sp->il_rbdp = (sp->il_ruba >> 28) & 0x0f;
#endif VMUNIX

	s = splIL();
	ilbusywait(sp, ILC_RESET, 0);		/* Initialize interface */
	addr->il_bar = sp->il_ruba & 0xffff;
	addr->il_bcr = sizeof(struct il_stat);
	ilbusywait(sp, ILC_STAT, (sp->il_ruba >> 2) & IL_EUA);
	if (sp->il_rbdp)
		PURGEBDP(sp->il_ubanum, sp->il_rbdp);
	splx(s);
	bcopy(sp->il_rpkt.ilp_stat.ils_addr, sp->il_enaddr, 6);
	bcopy(ether_broadcast, sp->il_arrequest.ar_xhdr.ilx_dhost, 6);
	sp->il_arrequest.ar_xhdr.ilx_type = ILADDR_TYPE;
	sp->il_arrequest.ar_pkt.ar_hardware = AR_ETHERNET;
	sp->il_arrequest.ar_pkt.ar_protocol = ILCHAOS_TYPE;
	sp->il_arrequest.ar_pkt.ar_hlength = 6;
	sp->il_arrequest.ar_pkt.ar_plength = 2;
	sp->il_arrequest.ar_pkt.ar_opcode = AR_REQUEST;
	bcopy(sp->il_enaddr, sp->il_arrequest.ar_pkt.ar_esender, 6);
	bcopy((u_char *)&sp->il_arrequest, (u_char *)&sp->il_arreply,
	      sizeof(struct chilapkt));
	sp->il_arreply.ar_pkt.ar_opcode = AR_REPLY;

	printf("chil%d: ether addr %x %x %x %x %x %x module %s firmware %s\n",
		unit,
		sp->il_enaddr[0]&0xff, sp->il_enaddr[1]&0xff,
		sp->il_enaddr[2]&0xff, sp->il_enaddr[3]&0xff,
		sp->il_enaddr[4]&0xff, sp->il_enaddr[5]&0xff,
		sp->il_rpkt.ilp_stat.ils_module,
		sp->il_rpkt.ilp_stat.ils_firmware);
}

/*
 * chaos interlan initialization. Also does initialization to the static xcvr
 * structure since initialized unions don't work anyway.
 */
chilinit()
{
	int unit;

	for (unit = 0; unit < NCHIL; unit++) {

#ifndef VMUNIX
		chilsetup(unit, (struct ildevice *)chiladdr[unit]);
#endif not VMUNIX

		chilreset(&chilsoft[unit].il_xcvr);
	}
}
/*
 * Receive our chaosnet address
 */
chilseta(dev, addr)
unsigned dev;
unsigned short addr;
{
	register struct chilsoft *sp;
	register struct chroute *r;
		
	if (dev >= NCHIL)
		return 1;
	else {
		sp = &chilsoft[dev];
		if (sp->il_xcvr.xc_devaddr == 0)
			return 1;
		if (sp->il_xcvr.xc_addr != 0) {  /* If we were already connected */
			r = &Chroutetab[sp->il_xcvr.xc_subnet];
			r->rt_type = 0;
			r->rt_cost = CHHCOST;
		}
		sp->il_xcvr.xc_addr = addr;
		if (sp->il_xcvr.xc_subnet >= CHNSUBNET)
			return 1;
		sp->il_arrequest.ar_pkt.ar_csender.ch_addr = addr;
		sp->il_arreply.ar_pkt.ar_csender.ch_addr = addr;
		r = &Chroutetab[sp->il_xcvr.xc_subnet];
		r->rt_type = CHDIRECT;
		r->rt_cost = CHHCOST/2;
		r->rt_xcvr = &sp->il_xcvr;
		return 0;
	}
}
/*
 * Reset interface after UNIBUS reset.
 */
chilreset(xp)
struct chxcvr *xp;
{
	register struct chilsoft *sp = xp->xc_ilinfo.il_soft;
	register int s;

	if (sp == 0)
		return;
	s = splIL();
	ilbusywait(sp, ILC_RESET, 0);
	ilbusywait(sp, ILC_CLPBAK, 0);
	ilbusywait(sp, ILC_CLPRMSC, 0);
	ilbusywait(sp, ILC_CRCVERR, 0);
	ilbusywait(sp, ILC_FLUSH, 0);
	ilbusywait(sp, ILC_ONLINE, 0);
	chilrstart(sp);
	splx(s);
}

/*
 * Start output on an idle line
 */
chilstart(xp)
struct chxcvr *xp;
{
	register struct chilsoft *sp = xp->xc_ilinfo.il_soft;

/*	IF_DEBUG(printf("chilstart (unit %d)\n", sp->il_unit)); */
	if (sp->il_xcvr.xc_tpkt != NOPKT)
		panic("chilstart: already busy");
	if (sp->il_oactive == 0)
		chilcint(sp->il_unit);
}
/*
 * Command (usually transmit) done interrupt.
 */
chilcint(dev)
{
	register struct ildevice *addr;
	register struct il_xheader *epkt;
	register struct chilsoft *sp = &chilsoft[dev];
	register struct packet *pkt;
	int elength;

	chilcin++;
	LOCK;
	addr = (struct ildevice *)sp->il_xcvr.xc_devaddr;
	IF_DEBUG(printf("chilcint: unit %d  oactive %d  xbdp %d\n",
		minor(dev), sp->il_oactive, (sp->il_xuba >> 28) & 0x0f));

#ifdef VMUNIX
#ifdef NOHOG
	if (sp->il_oactive)
		ubarelse(sp->il_ubanum, &sp->il_xuba);
#else
	if (sp->il_xbdp)
		PURGEBDP(sp->il_ubanum, sp->il_xbdp);
#endif NOHOG
#endif VMUNIX

	sp->il_oactive = 0;
	sp->il_scsr = addr->il_csr;
	if (sp->il_replying) {
		sp->il_replying = 0;
		sp->il_doreply = 0;
	} else if ((pkt = sp->il_xcvr.xc_tpkt) != NOPKT) {
		if ((sp->il_scsr & IL_STATUS) > 1) {
			chilxstat = sp->il_scsr;
			sp->il_xcvr.xc_abrt++;
		} else {
			if (sp->il_scsr & IL_STATUS)
				sp->il_xcvr.xc_abrt++;
			sp->il_xcvr.xc_xmtd++;
		}
		bcopy(&sp->il_savex,
		      (char *)&pkt->pk_phead - sizeof(struct il_xheader),
		      sizeof(struct il_xheader));
		IF_DEBUG(printf("chil: pkt %d (op %o didx %x) done\n",
				pkt->pk_pkn, pkt->pk_op, pkt->pk_didx & 0xffff));
		xmitdone(pkt);
	}
	/*
	 * If the last receive interrupt happened while a command was
	 * in progress, it couldn't prime the receive buffer then, so it
	 * set the flag telling us to do it here.
	 */
	if (sp->il_startrcv)
		chilrstart(sp);
	/*
	 * If last receive interrupt created an address resolution reply
	 * to send, send it rather than another data packet.
	 */
	if (sp->il_doreply) {
		sp->il_replying = 1;
		epkt = (struct il_xheader *)&sp->il_arreply;
		elength = sizeof(struct chilapkt);
		chilxrp++;
	} else if ((pkt = xmitnext(&sp->il_xcvr)) == NOPKT)
		return;
	else {
		IF_DEBUG(printf("chil: pkt %d (op %o didx %x) started\n",
				pkt->pk_pkn, pkt->pk_op, pkt->pk_didx & 0xffff));
		epkt = (struct il_xheader *)
			((char *)&pkt->pk_phead - sizeof(struct il_xheader));
		elength = sizeof(struct il_xheader) +
			  sizeof(struct pkt_header) +
			  pkt->pk_len;
		bcopy((char *)epkt, (char *)&sp->il_savex,
		      sizeof(struct il_xheader));
		if (pkt->pk_xdest == 0) {
			epkt->ilx_type = ILCHAOS_TYPE;
			bcopy(ether_broadcast, epkt->ilx_dhost, 6);
		} else {
			register struct ar_pair *app;

			for (app = sp->il_pairs;
			     app < sp->il_epairs && app->arp_chaos.ch_addr;
			     app++)
				if (pkt->pk_xdest == app->arp_chaos.ch_addr) {
					app->arp_time = ++arptime;
					epkt->ilx_type = ILCHAOS_TYPE;
					bcopy(app->arp_ether, epkt->ilx_dhost,
					      6);
					app = 0;
					break;
				}
			if (app) {
				chilxrq++;
				/*
				 * We have no cached address translation -
				 * we must ask for one.
				 */
				epkt = (struct il_xheader *)&sp->il_arrequest;
				sp->il_arrequest.ar_pkt.ar_ctarget.ch_addr =
					pkt->pk_xdest;
				elength = sizeof(struct chilapkt);
			}
		}
	}

#ifdef NOHOG
	sp->il_xuba = uballoc(sp->il_ubanum, (caddr_t)epkt, elength, UBA_WANTBDP);
#else
	sp->il_xuba = ubaremap(sp->il_ubanum, sp->il_xuba, (caddr_t)epkt);
#endif NOHOG

	IF_DEBUG(printf("  new uba map: xbdp %d  addr %x\n",
		(sp->il_xuba >> 28) & 0x0f, sp->il_xuba & 0xfff));

	addr->il_bar = sp->il_xuba & 0xffff;
	addr->il_bcr = (elength + 1) & ~1;
	addr->il_csr = ((sp->il_xuba >> 2) & IL_EUA) |
			ILC_XMIT|IL_CIE|IL_RIE;
	sp->il_oactive++;
}
/*
 * Receiver interrupt
 */
chilrint(dev)
{
	register struct chilsoft *sp = &chilsoft[dev];
	register struct packet *pkt;	/* shouldn't be reg on pdp11 */
	int elength;

	chilrin++;
	if (sp->il_rbdp)
		PURGEBDP(sp->il_ubanum, sp->il_rbdp);
	if (sp->il_rpkt.ilp_rhdr.ilr_status == 0377) {
		printf("chil%d: Receive interrupt with no packet!\n");
		return;
	}
	if (sp->il_rpkt.ilp_rhdr.ilr_status & 4) {
		sp->il_xcvr.xc_rej++;
		chillost++;
	}
	/*
	 * Data length is chaos packet size plus header (minus
	 * frame status and length == 4 plus 4 for the crc == 0!)
	 */
	elength = sp->il_rpkt.ilp_rhdr.ilr_length - sizeof(struct il_rheader);
	if (sp->il_rpkt.ilp_rhdr.ilr_status & 3)
		printf("chil%d: Received erroneous frame. Status: %d\n",
			dev, sp->il_rpkt.ilp_rhdr.ilr_status);
	else if (sp->il_rpkt.ilp_rhdr.ilr_type == ILADDR_TYPE)
		chilrar(sp, &sp->il_rpkt.ilp_arpkt, elength);
	else if (sp->il_rpkt.ilp_rhdr.ilr_type == ILCHAOS_TYPE)
		if (elength < sizeof(struct pkt_header) ||
		    elength < sizeof(struct pkt_header) + sp->il_rpkt.ilp_chhead.ph_len)
			sp->il_xcvr.xc_leng++;
		else  {
			register struct chroute *r;
			
			r = &Chroutetab[sp->il_xcvr.xc_subnet];
			r->rt_type = CHDIRECT;
			r->rt_cost = CHCCOST;
			r->rt_xcvr = &sp->il_xcvr;
			if ((pkt = pkalloc((int)sp->il_rpkt.ilp_chhead.ph_len,
					   1)) == NOPKT)
				sp->il_xcvr.xc_rej++;
			else {
				bcopy((u_char *)&sp->il_rpkt.ilp_chpkt,
				      (u_char *)&pkt->pk_phead,
				      sizeof(struct pkt_header) +
				      sp->il_rpkt.ilp_chhead.ph_len);
				sp->il_xcvr.xc_rcvd++;
				LOCK;
				if (pkt->pk_daddr == sp->il_xcvr.xc_addr &&
				    Chmyaddr != -1)
				    pkt->pk_daddr = Chmyaddr;
				rcvpkt(pkt);
			}
		}
	if (sp->il_oactive)
		sp->il_startrcv = 1;
	else
		chilrstart(sp);
}
chilrstart(sp)
register struct chilsoft *sp;
{
	register struct ildevice *addr = sp->il_devaddr;

	chilrst++;
	sp->il_rpkt.ilp_rhdr.ilr_status = 0177777;
	addr->il_bar = sp->il_ruba & 0xffff;
	addr->il_bcr = sizeof(union chilpkt);
	addr->il_csr = (sp->il_ruba >> 2) & IL_EUA |
			ILC_RCV|IL_RIE;
	while (!((sp->il_scsr = addr->il_csr) & IL_CDONE))
		/* Busy wait */;
	if (sp->il_scsr & IL_STATUS)
		printf("il%d: receive buffer error: %d\n",
			sp->il_unit, sp->il_scsr & IL_STATUS);
	sp->il_startrcv = 0;
}
/*
 * Receive an address resolution packet.
 */
chilrar(sp, arp, length)
register struct chilsoft *sp;
register struct ar_packet *arp;
{
	register struct ar_pair *app;
	register struct ar_pair *nap;

	u_char *eaddr;

	chilar++;
	if (length < sizeof(struct ar_packet) ||
	    arp->ar_hardware != AR_ETHERNET ||
	    arp->ar_protocol != ILCHAOS_TYPE ||
	    arp->ar_plength != sizeof(chaddr) ||
	    arp->ar_csender.ch_addr == 0)
		return;
	eaddr = 0;
	nap = sp->il_pairs;
	for (app = nap; app < sp->il_epairs && app->arp_chaos.ch_addr; app++)
		if (arp->ar_csender.ch_addr == app->arp_chaos.ch_addr) {
			eaddr = app->arp_ether;
			break;
		} else if (app->arp_time < nap->arp_time)
			nap = app;
	/*
	 * If we are already cacheing the senders addresses,
	 * update our cache with possibly new information.
	 */
	if (eaddr)
		bcopy(arp->ar_esender, app->arp_ether, 6);
	if (arp->ar_ctarget.ch_addr != sp->il_xcvr.xc_addr)
		return;
	/*
	 * If we have never heard of this host before, find
	 * a slot and remember him.
	 * Note that we leave the time alone since we haven't used the entry.
	 * This is to prevent other hosts from flushing our cache
	 */
	if (eaddr == 0) {
		bcopy(arp->ar_esender, nap->arp_ether, 6);
		nap->arp_chaos.ch_addr = arp->ar_csender.ch_addr;
	}
	if (arp->ar_opcode == AR_REQUEST)
		if (sp->il_doreply)
			chilarls++;
		else {
			chilarrp++;
			IF_DEBUG(printf("chilrar: sending arp (unit %d)\n",
					sp->il_unit));
			sp->il_arreply.ar_pkt.ar_ctarget.ch_addr = arp->ar_csender.ch_addr;
			bcopy(arp->ar_esender, sp->il_arreply.ar_pkt.ar_etarget, 6);
			bcopy(arp->ar_esender, sp->il_arreply.ar_xhdr.ilx_dhost, 6);
			sp->il_doreply = 1;
			if (sp->il_oactive == 0)
				chilcint(sp->il_unit);
		}
}			
#define MAXWAIT	1000000		/* on the order of 10 seconds */
/*
 * Perform a command and busy-wait for the interrupt.
 * Used when it's not safe to sleep.
 * This should never be called to receive a packet.
 */
ilbusywait(sp, command, bits)
register struct chilsoft *sp;
int command, bits;
{
	register struct ildevice *addr = sp->il_devaddr;
	register int csr, i;

	csr = addr->il_csr;
	IF_DEBUG(printf("il%d: busy wait %x csr %x bcr %x bar %x bits %x",
sp->il_unit, command, csr&0xffff, addr->il_bcr&0xffff, addr->il_bar&0xffff, bits));
	addr->il_csr = bits|command;
	for (i = 0; !((csr = addr->il_csr) & IL_CDONE); i++)
		if (i >= MAXWAIT) {
			printf("il%d: lost CDONE\n", sp->il_unit);
			return;
		}
	IF_DEBUG((csr & IL_STATUS) ? printf("%s\n",
		(command == ILC_DIAG || command == ILC_RESET ?
		ildiag : ilerrs)[csr & IL_STATUS])
			: printf("done\n"));
}