BBN-Vax-TCP/dev/acc.c
#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