BBN-Vax-TCP/dev/acc.c

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

#include "acc.h"
#ifdef NACC > 0

/*
 * ACC LH/DH IMP interface
 *
 */

#include "../h/param.h"
#include "../h/systm.h"
#include "../bbnnet/mbuf.h"
#include "../bbnnet/net.h"
#include "../bbnnet/imp.h"
#include "../bbnnet/ifcb.h"
#include "../h/map.h"
#include "../h/pte.h"
#include "../h/buf.h"
#include "../h/ubareg.h"
#include "../h/ubavar.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/accreg.h"


int     accprobe(), accattach(), acc_iint(), acc_oint();
struct  uba_device *accinfo[NACC];
u_short accstd[] = { 0 };
struct  uba_driver accdriver =
	{ accprobe, 0, accattach, 0, accstd, "acc", accinfo };

struct mbuf netflush;		/* spare buffer for doing flushes */

/*
 * Cause the ACC to interrupt.
 */
accprobe(reg)
	caddr_t reg;
{
	register int br, cvec;
	register struct impregs *addr = (struct impregs *)reg;

#ifdef lint
	br = 0; cvec = br; br = cvec;
#endif

	/*
	 * Reset transmitter and receiver and wait for things to settle.
	 */
	addr->istat = ireset;
	DELAY(100000);
	addr->ostat = oreset;
	DELAY(100000);

	/*
	 * Cause transmitter interrupt by doing a null DMA.
	 */
	addr->ostat = obusback;
	DELAY(100000);
	addr->owcnt = 0;
	addr->ostat = oienab | ogo;
	DELAY(100000);
	addr->ostat = 0;
	if (cvec && cvec != 0x200)
		cvec -= 4;              /* transmit -> receive */
	return (1);
}

accattach(ui)
	struct uba_device *ui;
{

	/* no local state to set up */
}

/*
 * Initialize the imp interface.
 */
acc_init(ip)
register struct ifcb *ip;
{	
	register struct impregs *addr;
	register struct uba_device *ui;
	register struct mbuf *m;
	int s, unit, ubano;
	extern lbolt;

	unit = minor(ip->if_dev);
	if (unit >= NACC || (ui = accinfo[unit]) == NULL || ui->ui_alive == 0) {
		printf("acc%d not alive\n", unit);
		return;
	}
	addr = (struct impregs *)ui->ui_addr;

	/*
	 * Do a hard reset first time thru, then try to set host ready
	 * relay: keep closing relay until host ready sets, use timeout
	 * to reschedule until host ready finally sets.  This avoids
	 * hanging up the entire net process while waiting for an errant
	 * interface to set.
	 */
	if (!ip->if_flag&IMPSETTING) {
		/*
		 * Reset the imp interface.
		 */
		s = spl5();
		addr->istat = ireset;
		addr->ostat = oreset;
		addr->ostat = obusback;	     	/* reset host master ready */
		addr->ostat = 0;
		splx(s);
		ip->if_flag |= IMPSETTING;
		addr->istat = ihmasrdy;		/* close the relay */
		sleep((caddr_t)&lbolt, PZERO);
	}
	while (!(addr->istat&ihstrdy) || (addr->istat&(imrdyerr|imntrdy))) {
		/*
		 * Keep turning imrdyerr off.
		 */
		addr->istat = ihmasrdy;
		timeout((caddr_t)acc_init, (caddr_t)ip, HZ);
		return;
	}

	/*
	 * host ready now set, set up uba mappings, flags, queues, put
	 * out the first read, and send three no-ops
	 */
	ip->if_flag &= ~IMPSETTING;
	ip->if_flag |= IMPTRYING;	/* ignore three no-ops from imp */
	ip->if_error = FALSE;
	ip->if_active = FALSE;
	ip->if_flush = FALSE;
	ip->if_blocked = FALSE;

	/*
	 * free uba map regs and info if necessary, and reinit 
	 */
	ubano = ui->ui_ubanum;
	if (ip->if_oaddr)
		ubarelse(ubano, &ip->if_oaddr);
	if (ip->if_iaddr)
		ubarelse(ubano, &ip->if_iaddr);

	ip->if_oaddr = uballoc(ubano, mtophys(netutl), MLEN, 0);
	ip->if_iaddr = uballoc(ubano, mtophys(netutl), MLEN, 0);

	/*
	 * get a buffer to start a read operation 
	 */
	if ((m = m_get(0)) == NULL) {
		printf("acc%d: cannot initialize\n", unit);
		return;
	}
	m->m_off = MHEAD;
	ip->if_inq_msg = m;
	ip->if_inq_cur = m;
	/*
	 * Try to send 3 no-ops to IMP.  If successful, put out a read
	 * and declare i/f available.
	 */
	if (imp_noops(ip)) {
		acc_read(ip, (caddr_t)((int)m + m->m_off), MLEN);
		ip->if_needinit = FALSE;
		ip->if_avail = TRUE;
	}
}

/*
 * Start an output operation on an mbuf.
 */
acc_output(ip)
register struct ifcb *ip;
{
	register struct mbuf *m;
	register struct imp *l;
	register struct impregs *addr;
	register struct uba_device *ui;
        int len, start;
	u_short uaddr;

	if ((ui = accinfo[minor(ip->if_dev)]) == NULL)
		return;
	addr = (struct impregs *)ui->ui_addr;

	start = FALSE;
	/*
	 * If output isn't active, attempt to
	 * start sending a new packet.
	 */
	if (!ip->if_active) {
		if ((m = ip->if_outq_hd) == NULL)
			/*
			 * Nothing left to send.
			 */
			return;
		start = TRUE;
		ip->if_active = TRUE;		/* set myself active */
		ip->if_outq_hd = m->m_act;	/* -> next packet chain */
		ip->if_outq_cur = m;		/* -> current mbuf */
		l = (struct imp *)((int)m + m->m_off);  /* ->1822 leader */
		ip->if_olen = l->i_mlen;	/* length to send */
		l->i_mlen = short_to_net(l->i_mlen) << 3;
	} else {
		m = ip->if_outq_cur;		/* -> next mbuf to send */
		l = (struct imp *)((int)m + m->m_off);
	}

	/*
	 * Map packet to be sent to UNIBUS.
	 */
	uaddr = ubaremap(ui->ui_ubanum, ip->if_oaddr, (caddr_t)l);

	/*
	 * Make sure length of type 0 messages comes from mbuf.  All others
	 * must be 80 bits.
	 */
	if (!start || l->i_type == 0)
		len = MIN(ip->if_olen, m->m_len);
	else
		ip->if_olen = len = 10;

#ifdef IMPDEBUG
	printf("acc%d: ", minor(ip->if_dev));
	imp_prt("SND", l);
	printf("addr= %x ubaddr= %x len= %x\n", l, uaddr, len);
#endif

	/*
	 * Stuff UNIBUS address, word count,
	 * and output status register.
	 * Adjust remaining packet length.
	 */
	addr->spo = (int)uaddr;
	addr->owcnt = -((len + 1) >> 1);
	ip->if_olen -= len;
	if (ip->if_olen <= 0)			/* end of message */
		addr->ostat = ((short) oienab + oenlbit +
                                     ((uaddr&0x30000)>>12) + ogo);
	else
		addr->ostat = ((short) oienab +
                                     ((uaddr&0x30000)>>12) + ogo);
}

/*
 * Read from the imp into a data area.
 */
acc_read(ip, daddr, size)
register struct ifcb *ip;
caddr_t daddr;
int size;
{
	register struct impregs *addr;
	register struct uba_device *ui;
        u_short uaddr;
	int s;

	if ((ui = accinfo[minor(ip->if_dev)]) == NULL)
		return;
	addr = (struct impregs *)ui->ui_addr;

	/*
	 * Remap input buffer; reset device
	 * to receive another packet.
	 */
	uaddr = ubaremap(ui->ui_ubanum, ip->if_iaddr, (caddr_t)daddr);
#ifdef IMPDEBUG
	printf("acc%d: RCV addr= %x ubaddr= %x len= %x\n", minor(ip->if_dev), 
			addr, uaddr, size);
#endif
	s = spl5();
	addr->spi = (int)(uaddr);
	addr->iwcnt = - (size >> 1);
	addr->istat = (ihmasrdy|iienab|iwren| ((uaddr & 0x30000)>> 12) | igo);
	splx(s);
}

/*
 * IMP output interrupt handler.
 */
acc_oint(unit)
int unit;
{
	register struct ifcb *ip;
	register struct impregs *addr;
	register struct uba_device *ui;
	int errbits = 0;

	ui = accinfo[unit];
	addr = (struct impregs *)ui->ui_addr;

	/* 
	 * Find ifcb for interrupting device.
	 */
	for (ip = ifcb; ip < &ifcb[nifcb]; ip++)
		if (major(ip->if_dev) == ACCDEV && minor(ip->if_dev) == unit)
			goto gotodev;
	printf("acc%d: oint: can't find ifcb\n", unit);
	return;

gotodev:
	/*
	 * Ignore unexpected errors.
	 */
	if (!ip->if_active) {
		printf("acc%d: phantom output intr ostat=%b olen=%d m=%X\n",
		    unit, addr->ostat, ACC_OBITS, ip->if_olen, ip->if_outq_cur);
		return;
	}

#ifdef IMPDEBUG
	printf("acc%d: write done\n", unit);
	errbits = addr->ostat & (ocomperr|otimout|omrdyerr|obusback|ogo);
	if (errbits || (~addr->ostat & (odevrdy|oienab)))
		printf("acc%d: err ostat=%b\n", unit, addr->ostat, ACC_OBITS);
#endif

	if (addr->ostat & otimout) 
		printf("acc%d: timeout", unit);

	/*
	 * If more to send, free current buf and get next, otherwise
	 * reset active flag to start next msg in imp_output
	 */
	if ((ip->if_outq_cur = m_free(ip->if_outq_cur)) == NULL ||
	    ip->if_olen <= 0) {
		ip->if_active = FALSE;
		if (ip->if_outq_cur != NULL)
			m_freem(ip->if_outq_cur);
	}

	/*
	 * fire up next  output
	 */
	acc_output(ip);	
}

/*
 * IMP input interrupt handler
 */
acc_iint(unit)
int unit;
{
	register struct ifcb *ip;
	register struct imp *l;
    	register struct mbuf *m, *n;
	register struct impregs *addr;
	register struct uba_device *ui;
	int trying;

	/* 
	 * Find ifcb for interrupting device.
	 */
	for (ip = ifcb; ip < &ifcb[nifcb]; ip++)
		if (major(ip->if_dev) == ACCDEV && minor(ip->if_dev) == unit)
			goto gotidev;
	printf("acc%d: iint: can't find ifcb\n", unit);
	return;

gotidev:
	if ((m = ip->if_inq_cur) == NULL)
		return;
	ui = accinfo[unit];
	addr = (struct impregs *)ui->ui_addr;

#ifdef IMPDEBUG
	printf("acc%d: read done ", unit);
	l = (struct imp *)((int)m +  m->m_off);
	imp_prt("RCV", l);
	printf("\n");
#endif

	trying = ip->if_flag & IMPTRYING;
	if (addr->istat & iendmsg) {
		ip->if_flag |= IMPENDMSG;
		if (trying)
			ip->if_flag = (ip->if_flag & ~IMPTRYING) | --trying;
	}
	ip->if_error = (addr->istat & (icomperr|itimout|imntrdy|imrdyerr|igo)) 
			? TRUE : FALSE;
	if (ip->if_error ||
	    (~addr->istat & (ihstrdy|iwren|idevrdy|iienab|ihmasrdy))) {
		printf("acc%d: input error csri=%b\n", unit,
		    addr->istat, ACC_IBITS);
		/*
		 * ignore errors for init no-ops
		 */
		if (trying) {
			ip->if_error = FALSE;
			addr->istat = ihmasrdy|iwren|iienab;
			acc_read(ip, (caddr_t)((int)m + m->m_off), MLEN);
			return;
		}
	}

	addr->istat = ihmasrdy|iwren|iienab;

	/*
	 * Get length of message.
	 * Make sure message length does not exceed maximum.
	 * (Could be imp trying to send us garbage.)
	 */
	m->m_len = MLEN + (addr->iwcnt << 1);
	ip->if_ilen += m->m_len;
	if (ip->if_ilen > ip->if_mtu && !ip->if_flush) {
		printf("acc%d: ilen error ilen=%X mtu=%X\n", 
				unit, ip->if_ilen, ip->if_mtu);
		ip->if_error = TRUE;
	}

	if (ip->if_flag & IMPENDMSG) {
		/*
		 * Queue good packet for input processing and awaken net
		 * input process
		 */
		if (!ip->if_flush) {
			if (ip->if_inq_hd != NULL)
				ip->if_inq_tl->m_act = ip->if_inq_msg;
			else
				ip->if_inq_hd = ip->if_inq_msg;
			ip->if_inq_tl = ip->if_inq_msg;
			ip->if_inq_msg = NULL;
			wakeup((caddr_t)&ifcb[0]);	
		}
		
		/*
		 * Get buffer for next packet to be read.
		 */
		if ((n = m_get(0)) != NULL) {
			/*
			 * Got a buffer; fill it in.
			 */
			ip->if_flush = FALSE;
			ip->if_ilen = 0;
			n->m_off = MHEAD;
			ip->if_inq_msg = n;
			ip->if_inq_cur = n;
		} else {
			/*
			 * No more buffers; flush and clear RFNM status for
			 * this i/f
			 */
			ip->if_flush = TRUE;
			ip->if_inq_msg = n = (struct mbuf *)&netflush;
			h_reset(ip->if_net);
			netstat.imp_flushes++;
		}
		ip->if_flag &= ~IMPENDMSG;
	} else
		/*
		 * Not end of packet; if not flushing
		 * try to get more buffers.  If there is
		 * an error or no more buffers free partial message.
		 */
		if (!ip->if_flush)
			if (!ip->if_error && (n = m_get(0)) != NULL) {
				m->m_next = n;
				n->m_off = MHEAD;
				ip->if_inq_cur = n;
			} else {
				m_freem(ip->if_inq_msg);
				ip->if_flush = TRUE;
				n = (struct mbuf *)&netflush;
				ip->if_inq_cur = n;
				ip->if_inq_msg = n;
				h_reset(ip->if_net);
				netstat.imp_flushes++;
			}
	/*
	 * Finally, set up the next read.
	 */
	acc_read(ip, (caddr_t)((int)n + n->m_off), MLEN);
}	

/*
 * Reset driver in case of uba init.
 */
accreset(uban)
int uban;
{
	register i;
	register struct ifcb *ip;
	register struct uba_device *ui;

	/* look for all acc's on selected uba */

	for (i = 0; i < NACC; i++)
		if ((ui = accinfo[i]) != NULL && ui->ui_ubanum == uban) {
			for (ip = ifcb; ip < &ifcb[nifcb]; ip++)
				if (minor(ip->if_dev) == i) 
					netreset(ip);
		}
}

#ifdef IMPDEBUG
imp_prt(s, l)
register char *l;
	char *s;
{

	printf("<%s> ",s);
	prt_byte(l, 12);
	prt_byte(l+12, 20);
	prt_byte(l+32, 20);
}

prt_byte(s, ct)
register char *s;
	int ct;
{
	register i, j, c;

	for (i=0; i<ct; i++) {
		c = *s++;
		for (j=0; j<2 ; j++)
			putchar("0123456789abcdef"[(c>>((1-j)*4))&0xf]);
		putchar(' ');
	}
	putchar('\n');
}
#endif IMPDEBUG
#endif NACC