SysIII/usr/src/uts/vax/pwb/pcl.c
/*
* PCL-11 Multiplexing / Demultiplexing Driver
* Permits 8 two-way communications between 16 machines.
*/
# include "sys/param.h"
# include "sys/proc.h"
# include "sys/dir.h"
# include "sys/user.h"
# include "sys/pcl.h"
# include "sys/buf.h"
# ifdef pdp11
# include "sys/map.h"
# endif
# ifdef pdp11
paddr_t pcl_uba; /* Unibus address for transfers */
# endif
struct buf pcl_buf; /* buffer header for UBM */
struct pcl * pcl_ioq; /* head of linked list of transmissions */
struct pcl pclctrl; /* pcl control channel interface */
struct pcldb pcldb[PCLINDX]; /* buffer for debug info */
int pclindx; /* index of next debug record */
struct pcl * pclspare; /* available slot for conversation */
/*
* open: first time only - call initialization routine.
* Find available communications structure for this channel.
* Check that channel is not in use or waiting for close
* message from remote.
*/
pclopen(dev)
int dev;
{
register struct pcl *p;
if(pcl_buf.b_flags == 0)
pclinit();
spl5();
do {
if(p = pclsrch(dev)) {
if(p->pcl_flag & P_OPEN)
u.u_error = EBUSY;
} else if((p = pclspare) == NULL)
u.u_error = ENOSPC;
if(u.u_error) {
spl0();
return;
}
} while(pclwait(p));
p->pcl_dev = dev;
p->pcl_flag = p->pcl_flag & P_ROPEN | P_OPEN | P_RETRY;
p->pcl_pgrp = u.u_procp->p_pgrp;
p->pcl_icnt = p->pcl_ioff = 0;
p->pcl_hdr = PCLOPEN | pclchan(dev);
p->pcl_ocnt = 10;
pclqueuer(p);
pclwait(p);
if(p->pcl_flag & P_XERR) {
u.u_error = EIO;
p->pcl_flag &= P_ROPEN;
}
spl0();
}
/*
* close: transmit close message to remote. If header is
* set, a transmission is pending and pclxintr will send close
* message on completion.
*/
pclclose(dev)
int dev;
{
register struct pcl *p;
spl5();
p = pclsrch(dev);
p->pcl_flag &= P_ROPEN;
if(p->pcl_hdr == 0) {
p->pcl_hdr = PCLCLOSE | pclchan(dev);
p->pcl_ocnt = 0;
pclqueuer(p);
}
spl0();
}
/*
* read: locate corresponding communication structure.
* Return characters from buffer, then EOF indication
* else wait for next transmission. Last done only if
* remote is still open.
*/
pclread(dev)
int dev;
{
register struct pcl *p;
register int c;
spl5();
p = pclsrch(dev);
retry:
if(p->pcl_icnt) {
c = min(u.u_count, p->pcl_icnt);
move(p->pcl_ibuf + p->pcl_ioff, c, B_READ);
p->pcl_icnt -= c;
p->pcl_ioff += c;
} else if(p->pcl_flag & P_REOF)
p->pcl_flag &= ~P_REOF;
else if(p->pcl_flag & P_RERR) {
p->pcl_flag &= ~P_RERR;
u.u_error = EIO;
} else if(p->pcl_flag & P_ROPEN) {
p->pcl_flag |= P_READ;
sleep((caddr_t) p, PCLRPRI);
goto retry;
} else
u.u_error = EBADF;
spl0();
}
/*
* write: wait until header is available for use.
* Gather up user data into output buffer and arrange
* for transmission. Wait for completion.
*/
pclwrite(dev)
int dev;
{
register struct pcl *p;
spl5();
p = pclsrch(dev);
if((p->pcl_flag & P_ROPEN) == 0) {
u.u_error = EBADF;
spl0();
return;
}
/* should add separate wait for xmit and busy */
pclwait(p);
if(u.u_count == 0) {
p->pcl_hdr = PCLEOF | pclchan(p->pcl_dev);
p->pcl_ocnt = 10; /* send data to permit rejection */
pclqueuer(p);
pclwait(p);
} else while(u.u_count && (p->pcl_flag & P_XERR) == 0) {
p->pcl_ocnt = min(u.u_count, PCLBSZ);
if(move(p->pcl_obuf, p->pcl_ocnt, B_WRITE))
break;
if(p->pcl_ocnt & 01) {
p->pcl_ocnt++;
p->pcl_hdr = PCLODATA | pclchan(p->pcl_dev);
} else
p->pcl_hdr = PCLEDATA | pclchan(p->pcl_dev);
pclqueuer(p);
pclwait(p);
}
if(p->pcl_flag & P_XERR) {
p->pcl_flag &= ~P_XERR;
u.u_error = EIO;
}
pclwake(p, P_WRITE);
}
/*
* start: prepare for transmission. Verify that channel is still open
* as this may be a retry. Exception: close message (of course).
*/
pclxstart()
{
register struct pcl *p;
register struct pclhw *hw;
# ifdef pdp11
paddr_t uba;
# else
register int uba;
extern _sdata;
# endif
if((p = pcl_ioq) == NULL)
return;
if((p->pcl_flag & P_OPEN) == 0) {
p->pcl_ocnt = 0;
p->pcl_hdr = PCLCLOSE | pclchan(p->pcl_dev);
}
hw = *pcl_addr;
pcl_buf.b_flags |= B_BUSY;
p->pcl_flag &= ~P_XERR;
hw->pcl_tcr = TXINIT;
hw->pcl_tcr |= IE;
# ifdef pdp11
uba = pcl_uba + p->pcl_obuf - pcl_buf.b_paddr;
# else
uba = (int) p->pcl_obuf - (int) (&_sdata);
# endif
hw->pcl_tba = (short) uba;
hw->pcl_tcr |= (short) ((uba & EABITS) >> EAOFF);
hw->pcl_tbc = -(p->pcl_ocnt + sizeof pcl_pcl[0].pcl_hdr);
hw->pcl_tdb = p->pcl_hdr;
hw->pcl_tcr |= (pclmach(p->pcl_dev) << DSTOFF) |
SNDWD | STTXM | TXNPR | RIB;
pcldebug(4, hw->pcl_tcr, hw->pcl_tsr, p->pcl_hdr);
}
/*
* start: prepare for reception.
*/
pclrstart(p)
register struct pcl *p;
{
register struct pclhw *hw;
# ifdef pdp11
paddr_t uba;
# else
register int uba;
extern _sdata;
# endif
hw = *pcl_addr;
hw->pcl_rbc = -PCLBSZ;
# ifdef pdp11
uba = pcl_uba + p->pcl_ibuf - pcl_buf.b_paddr;
# else
uba = (int) p->pcl_ibuf - (int) (&_sdata);
# endif
hw->pcl_rba = (short) uba;
hw->pcl_rcr = (short) (RCVDAT | RCNPR | IE |
((uba & EABITS) >> EAOFF));
}
/*
* queuer: place transmission request at the end of the
* linked list of requests. Start output if the list
* was previously empty.
*/
pclqueuer(p)
register struct pcl *p;
{
register int sps;
register struct pcl *q;
sps = spl5();
p->pcl_ioq = NULL;
if(pcl_ioq == NULL) {
pcl_ioq = p;
pclxstart();
} else {
for(q = pcl_ioq; q->pcl_ioq; q = q->pcl_ioq);
q->pcl_ioq = p;
}
splx(sps);
}
/*
* receiver interrupt: examine incoming header.
* Reject transmission if channel not open or input buffer
* is not available. Some headers are not followed
* by data - sending a signal, sending status, closing
* a channel - and cannot be rejected.
*/
pclRintr()
{
register int rsr, dev;
register struct pclhw *hw;
static struct pcl *p;
static short hdr;
hw = *pcl_addr;
rsr = hw->pcl_rsr;
dev = (((hw->pcl_rcr & SRCPCL) >> SRCOFF) - 1) << 3;
if(rsr & ERR)
if(p == NULL)
printf("pcl recv err\n");
else {
p->pcl_flag |= P_RERR;
pclwake(p, P_READ);
if(pclhdr(hdr) == PCLOPEN)
p->pcl_flag &= ~P_ROPEN;
}
else if(rsr & REJCOM)
;
else if(rsr & SUCTXF && p != NULL) {
p->pcl_ioff = 0;
if(pclhdr(hdr) == PCLEOF) {
p->pcl_flag |= P_REOF;
p->pcl_icnt = 0;
pclwake(p, P_READ);
} else if(pclhdr(hdr) == PCLOPEN) {
/* fetch uid and gid from buffer */
p->pcl_flag |= P_ROPEN;
pclwake(p, P_WOPEN);
} else {
p->pcl_icnt = PCLBSZ + hw->pcl_rbc -
(pclhdr(hdr) == PCLODATA ? 1 : 0);
pclwake(p, P_READ);
}
} else if(rsr & DTORDY) {
hdr = hw->pcl_rdb;
dev |= pclchan(hdr);
p = pclsrch(dev);
switch(pclhdr(hdr)) {
case PCLEOF:
case PCLEDATA:
case PCLODATA:
if(p == NULL || (p->pcl_flag & P_OPEN) == 0)
break;
if(p->pcl_flag & P_REOF || p->pcl_icnt)
hw->pcl_rcr |= REJ;
else
pclrstart(p);
pcldebug(2, hw->pcl_rcr, rsr, hdr);
return;
case PCLOPEN:
if(p) {
if(p->pcl_flag & P_WASC)
break;
if(p->pcl_flag & P_ROPEN) {
pclwake(p, P_READ | P_WRITE | P_WOPEN);
p->pcl_flag &= ~P_ROPEN;
break;
}
} else {
if((p = pclspare) == NULL) {
hw->pcl_rcr |= REJ;
return;
}
p->pcl_dev = dev;
}
p->pcl_flag |= P_ROPEN;
pclrstart(p);
return;
case PCLCTRL:
p = &pclctrl;
if((pclctrl.pcl_flag & P_OPEN) == 0)
break;
if(pclctrl.pcl_icnt)
hw->pcl_rcr |= REJ;
else
pclrstart(&pclctrl);
return;
case PCLSIGNAL:
if(p && p->pcl_flag & P_OPEN)
signal(p->pcl_pgrp, (hdr >> 4) & 037);
break;
case PCLCLOSE:
if(p) {
p->pcl_flag &= ~P_ROPEN;
pclwake(p, P_READ | P_WRITE | P_WOPEN);
if(p->pcl_flag & P_OPEN)
p->pcl_flag |= P_WASC;
}
break;
default:
printf("pcl bad hdr: %o\n", hdr);
}
} else
printf("pcl rsr err %o\n", rsr);
p = NULL;
pcldebug(1, hw->pcl_rcr, rsr, hdr);
hw->pcl_rcr = RCINIT;
hw->pcl_rcr |= IE | RCVWD;
}
/*
* transmitter interrupt: wakeup writer on successful transmission.
* Also, those waiting for write buffer. Attempt retry
* on rejection if retry bit is set.
*/
pclXintr()
{
register struct pcl *p;
register struct pclhw *hw;
register int tsr;
hw = *pcl_addr;
tsr = hw->pcl_tsr;
if((pcl_buf.b_flags & B_BUSY) == 0)
printf("pcl xmit int %o\n", tsr);
else if(tsr & (ERR | SORE | SUCTXF | TBSBSY)) {
p = pcl_ioq;
pcl_ioq = pcl_ioq->pcl_ioq;
if(tsr & MSTDWN)
hw->pcl_mmr |= MASTER;
if(tsr & SORE && p->pcl_flag & P_RETRY)
timeout(pclqueuer, p, PCLDELAY);
else if(p->pcl_flag & P_OPEN) {
if(tsr & (SORE | ERR | TBSBSY))
p->pcl_flag |= P_XERR;
p->pcl_hdr = NULL;
pclwake(p, P_WRITE);
} else if(pclhdr(p->pcl_hdr) != PCLCLOSE) {
p->pcl_hdr = PCLCLOSE | pclchan(p->pcl_dev);
p->pcl_ocnt = 0;
pclqueuer(p);
} else
p->pcl_hdr = NULL;
} else
printf("pcl tsr err %o\n", tsr);
pcl_buf.b_flags &= ~B_BUSY;
pcldebug(3, hw->pcl_tcr, tsr, p->pcl_hdr);
pclxstart();
}
/*
* search: locate a communication structure for a channel
* given the machine id and the logical channel. Leave address
* of an available slot in pclspare.
*/
struct pcl *
pclsrch(dev)
register int dev;
{
register struct pcl *p, *e;
e = &pcl_pcl[pcl_cnt];
pclspare = NULL;
for(p = pcl_pcl; p != e; p++)
if(p->pcl_flag & (P_OPEN | P_ROPEN)) {
if(p->pcl_dev == dev)
return(p);
} else if(pclspare == NULL)
pclspare = p;
return(NULL);
}
/*
* wait: wait until output buffer and header are available
* for use in a transmission.
*/
pclwait(p)
register struct pcl *p;
{
register int sps, ret = 0;
sps = spl5();
while(p->pcl_hdr) {
ret++;
p->pcl_flag |= P_WRITE;
sleep((caddr_t) p + 1, PCLWPRI);
}
splx(sps);
return(ret);
}
/*
* control: used to send control information across the
* link and synchronize the two sides.
*/
pclioctl(dev, cmd, arg, mode)
{
register struct pcl *p;
struct ctrlmsg {
char *addr;
int count;
} cb;
spl5();
p = pclsrch(dev);
if(p->pcl_flag & P_NOCTRL)
u.u_error = ENOTTY;
else switch(cmd) {
case WAIT:
while((p->pcl_flag & (P_ROPEN | P_WASC)) == 0) {
p->pcl_flag |= P_WOPEN;
sleep((caddr_t) p + 2, PCLRPRI);
}
if(p->pcl_flag & P_WASC)
u.u_error = EBADF;
break;
case FLAG:
p->pcl_flag &= ~(P_RETRY | P_NOCTRL);
p->pcl_flag |= (arg & (P_RETRY | P_NOCTRL));
break;
case SIG:
pclwait(p);
p->pcl_hdr = PCLSIGNAL | pclchan(dev) |
((arg & 037) << 4);
p->pcl_ocnt = 0;
pclqueuer(p);
pclwait(p);
break;
case CTRL:
if(p->pcl_flags & P_RSTR)
u.u_error = EPERM;
else if(copyin((char *) arg, (char *) &cb, sizeof cb))
u.u_error = EFAULT;
else if(cb.count == 0 || cb.count > PCLBSZ)
u.u_error = EINVAL;
else {
pclwait(p);
# ifdef pdp11
if(copyio(p->pcl_obuf,cb.addr,cb.count,U_WUD)) {
# else
if(copyin(cb.addr, p->pcl_obuf, cb.count)) {
# endif
u.u_error = EFAULT;
break;
}
p->pcl_ocnt = cb.count;
p->pcl_hdr = PCLCTRL | pclchan(dev);
pclqueuer(p);
pclwait(p);
if(p->pcl_flag & P_XERR) {
p->pcl_flag &= ~P_XERR;
u.u_error = EIO;
}
pclwake(p, P_WRITE);
}
break;
case RSTR:
p->pcl_flags |= P_RSTR;
break;
default:
u.u_error = EINVAL;
}
spl0();
}
/*
* wakeup: activate roadblocked process.
*/
pclwake(p, flag)
register struct pcl *p;
register int flag;
{
if(p->pcl_flag & P_READ)
wakeup((caddr_t) p);
if(p->pcl_flag & P_WRITE)
wakeup((caddr_t) p + 1);
if(p->pcl_flag & P_WOPEN)
wakeup((caddr_t) p + 2);
p->pcl_flag &= ~flag;
}
/*
* debug stuff: circular array of values captured at appropriate times.
* Contains type of record, command register, status register,
* and pcl structure header.
*/
pcldebug(t, c, s, h)
int t, c, s, h;
{
pcldb[pclindx].pcl_record = t;
pcldb[pclindx].pcl_cmdreg = c;
pcldb[pclindx].pcl_statreg = s;
pcldb[pclindx].pcl_header = h;
if(++pclindx == PCLINDX)
pclindx = 0;
}
/*
* init: allocate physical memory for buffers. Insure the
* existance of a bus master. Initialize buffer addresses.
*/
# ifdef pdp11
pclinit()
{
register struct pclhw *hw;
register struct pcl *p;
register char (*c)[PCLBSZ] = 0;
paddr_t pa;
int b;
b = PCLBSZ * 2 * (pcl_cnt + 1);
if((pa = ctob((paddr_t) (unsigned) malloc(coremap, btoc(b)))) ==
(paddr_t) 0) {
u.u_error = ENOMEM;
return;
}
pcl_buf.b_paddr = pa;
for(p = pcl_pcl; p != &pcl_pcl[pcl_cnt]; p++) {
p->pcl_ibuf = pa + (paddr_t)(unsigned)c++;
p->pcl_obuf = pa + (paddr_t)(unsigned)c++;
}
pclctrl.pcl_ibuf = pa + (paddr_t)(unsigned)c;
pcl_buf.b_flags = B_PHYS;
pcl_buf.b_bcount = b;
pcl_uba = ubmaddr(&pcl_buf, ubmalloc(b));
hw = *pcl_addr;
hw->pcl_mmr |= MASTER;
hw->pcl_rcr = RCINIT;
hw->pcl_rcr |= IE | RCVWD;
}
# else
pclinit()
{
register struct pclhw *hw;
pcl_buf.b_flags = B_PHYS;
hw = *pcl_addr;
hw->pcl_mmr |= MASTER;
hw->pcl_rcr = RCINIT;
hw->pcl_rcr |= IE | RCVWD;
}
# endif
/*
* control open: call initialization routine. Insure
* exclusive use.
*/
pclcopen()
{
if(pcl_buf.b_flags == 0)
pclinit();
if(pclctrl.pcl_flag & P_OPEN)
u.u_error = EBUSY;
else
pclctrl.pcl_flag = P_OPEN;
}
/*
* control close: unlock control channel. Discard any
* un-read data.
*/
pclcclose()
{
spl5();
pclctrl.pcl_icnt = 0;
pclctrl.pcl_flag = 0;
spl0();
}
/*
* control read: wait for stty control message.
*/
pclcread()
{
register struct pcl *p;
p = &pclctrl;
spl5();
while(p->pcl_icnt == 0) {
p->pcl_flag |= P_READ;
sleep((caddr_t) p, PCLRPRI);
}
spl0();
move(p->pcl_ibuf, min(u.u_count, p->pcl_icnt), B_READ);
spl5();
p->pcl_flag &= ~P_READ;
p->pcl_icnt = 0;
spl0();
}