SysIII/usr/src/uts/vax/pwb/pcl.c

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

/*
 *	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();
}