static char rcsid[] = "$Header: vo.c,v 820.1 86/12/04 19:57:02 root Exp $";
static char sccsid[]="%W% %Y% %Q% %G%";

/************************************************************************
*									*
*				Copyright 1984				*
*			VALID LOGIC SYSTEMS INCORPORATED		*
*									*
*	This listing contains confidential proprietary information	*
*	which is not to be disclosed to unauthorized persons without	*
*	written consent of an officer of Valid Logic Systems 		*
*	Incorporated.							*
*									*
*	The copyright notice appearing above is included to provide	*
*	statutory protection in the event of unauthorized or 		*
*	unintentional public disclosure.				*
*									*
************************************************************************/

/*
 * History:
 * ========
 * 860324 jht	Made vo.c and oct.c work together for 4.2bsd Siemens BS2000;
 */

#include "oct.h"
#include "vo.h"
#if NOCT > 0 && NVO > 0

#include "../h/param.h"		/* various types and macros */
#include "../h/ioctl.h"
#include "../h/tty.h"		/* tty structure and parameters */
#include "../h/dir.h"		/* so user.h can be included */
#include "../h/user.h"		/* ENXIO */
#include "../h/conf.h"		/* linesw stuff */
#include "../h/uio.h"
#include "../h/kernel.h"
#include "../h/pdma.h"
#include "../s32/vectors.h"
#include "../machine/cpu.h"
#include "../s32dev/octreg.h"
#include "../s32/reentrant.h"
#include "../s32dev/circbuf.h"

#define NPORTS 8
struct tty vo_tty[NPORTS];
extern struct tty oct_tty[];

char voreset=0,voscanning=0;
int vostart(),voscan();
extern char partab[];
extern char *vgMmem;
#ifdef	bsd42

/*
 * PRESUMPTIONS: 
 * -------------
 *
 *	1) 1:1 map
 *
 *		from	 vo_tty[0-(NPORTS-1)]
 *		to	oct_tty[0-(NPORTS-1)],
 *
 *		wherewith voscan() wakes up octopen().
 *
 *	2) NPORTS==NLINE
 */
short	savedVoMinor[NPORTS];	/* Mapping between vo and oct devices */
#endif	bsd42

#define VOBASE 0x7000

#define ADDR(tp) ((struct ocdevice *)((struct pdma *)(tp)->t_addr)->p_addr)

voopen(dev, flag)
dev_t dev;
{
	register struct tty *tp=vo_tty+minor(dev);
	register int error;

	if (vgMmem==0) return(ENODEV);
	if (voreset==0) {
		register struct cb1 *cbp=(struct cb1 *)SSEG1_VA;
		register int i;
		setSeg(cbp,vgMmem+VOBASE);
		for (i=2*NPORTS; --i>=0; cbp++) {cbp->cb1in=2; cbp->cb1out=2;}
		relSeg(--cbp);
		timeout(voscan,0,hz*4/100);
		voreset++;
	}
	tp->t_addr=oct_tty[minor(dev)].t_addr;
	error=octopen(dev,flag);
#ifdef	bsd42
	if (!error)
		/*
		 * Record the mapping for later use
		 * by voscan(), which will wake up octopen(),
		 * who is waiting upon vo_tty[minor(dev)].t_rawq
		 *
		 * NOTE:  octopen(), upon discovering that it is us,
		 * will also flag-as-open his corresponding oct_tty.
		 * This will prevent spurious conflicting opens
		 * originating from his side.
		 */
		savedVoMinor[minor(dev)] = ~minor(dev);	/* Don't overload '0' */
#endif	bsd42
	tp->t_oproc=vostart;
	return(error);
}

/* voclose(dev) uses octclose */
/* voread(dev,uio) uses octread */
/* vowrite(dev,uio) uses octwrite */

extern char *stkalloc();

vostart(tp)
register struct tty *tp;
{
	register struct cb1 *cbp=(struct cb1 *)(SSEG1_VA+
		(NPORTS+minor(tp->t_dev))*sizeof(struct cb1));
		/* check TS_ASLEEP because ttinput() can call us to echo
		/* even when output is full.
		*/
	while (0<tp->t_outq.c_cc && !(tp->t_state&TS_ASLEEP)) {
		register char *p;
		register int c,j,n;
		register char *buf;
		setSeg(cbp, vgMmem+VOBASE);
		while (0==(j=cb1room(cbp))) {
			register int s;
			relSeg(cbp);
			if (voscanning&2) return; /* can't sleep in interrupt */
			s=spl1();
			tp->t_state |= TS_ASLEEP;
			sleep((caddr_t) &tp->t_outq, TTOPRI);
			splx(s);
			setSeg(cbp, vgMmem+VOBASE);
		}
		for (p=buf=stkalloc(n=j); --j>=0 && (c=getc(&tp->t_outq))>=0; ) 
			if (tp->t_flags&RAW) *p++=c;
			else *p++=c|(partab[c]&0x80);
		cb1put(cbp,buf,p-buf);
		stkfree(n);
		relSeg(cbp);
	}
}

voioctl(dev, cmd, addr, flag)
caddr_t addr;
dev_t dev;
{
	register int error;

	if (0==(error=octioctl(dev,cmd,addr,flag))) {
		if (cmd==TIOCSETN || cmd==TIOCSETP) {/* change speed */
			extern char oct_baud[];
			register struct ocdevice *op=ADDR(vo_tty+minor(dev));
			register struct tty *tp=vo_tty+minor(dev);
			op->occr=OCCR_CLR;
			op->ocmr=OCMR1_STOP1|OCMR1_LEN8|OCMR1_MDAS16;
			op->ocmr=OCMR2_TXC|OCMR2_RXC|oct_baud[tp->t_ospeed&0xF];
			op->occr=OCCR_RTS|OCCR_DTR|OCCR_RXEN;
		}
	}
	return(error);
}

#define VOWATER 32
voscan()
{
	register struct tty *tp;
	register struct cb1 *cbp=(struct cb1 *)SSEG1_VA;
	register int i;
	voscanning|=2;
	IsetSeg(cbp,vgMmem+VOBASE);
	for (tp=vo_tty,i=NPORTS; --i>=0; cbp++,tp++) {
		register int j;

#ifndef bsd42
		if (!(tp->t_state&TS_ISOPEN)) continue;
		j=cbp[NPORTS].cb1in-cbp[NPORTS].cb1out;
	        if (0==(j&((256-1)&~(VOWATER-1)))) {/* output nearly empty */
			/* (*linesw[tp->t_line].l_start) (tp); */
			if (tp->t_state&TS_ASLEEP) {
				tp->t_state&=~TS_ASLEEP;
				wakeup((caddr_t) &tp->t_outq);
			} else if (tp->t_outq.c_cc!=0) vostart(tp);
                }
#else bsd42

	   /*
	    * If the line is open,
	    * perform any required (normal) output processing.
	    */
	   if (tp->t_state&TS_ISOPEN) {
		j=cbp[NPORTS].cb1in-cbp[NPORTS].cb1out;
	        if (0==(j&((256-1)&~(VOWATER-1)))) {/* output nearly empty */
			/* (*linesw[tp->t_line].l_start) (tp); */
			if (tp->t_state&TS_ASLEEP) {
#ifndef	BS2000_MODS
				tp->t_state&=~TS_ASLEEP;
#else	BS2000_MODS
				/*
				 * In 4.2bsd, ttywait() sleeps
				 * if TS_BUSY is asserted.
				 *
				 * Somebody is asserting it:
				 * octstart() and possibly someone
				 * OTHER than vo*() or bk*() assert TS_BUSY
				 * after they launch their corresponding h/w.
				 * To date, dunno who...
				 *
				 * No output is in progress, so turn it off.
				 * This was not a problem in 4.1bsd.
				 */
				tp->t_state&=~(TS_ASLEEP|TS_BUSY);
#endif	BS2000_MODS
				wakeup((caddr_t) &tp->t_outq);
			} else if (tp->t_outq.c_cc!=0) vostart(tp);
                }
	   } else {

		   /*
		    * The vo_tty[i] line is NOT yet open.
		    * Perhaps the input-line is sleep()-ing until
		    * TS_CARR_ON is asserted (by us.)
		    * If user is blocked in octopen() waiting for the
		    * vo_tty[i] line to become available (which it is),
		    * then we can assert "carrier on" for him from here.
		    */
		   if  ( (tp->t_state & TS_WOPEN)	/* Pending open	*/
		    &&  !(tp->t_state & TS_CARR_ON)	/* Dormant	*/
		    &&   (cbp->cb1in != cbp->cb1out)	/* We have input*/
		       )
		   {
			/*
			 * The Siemens BS2000 protocol uses
			 * octopen()/octclose() with vo_tty[i],
			 * which makes for an interesting state machine!
			 *
			 * NOTE:  The R8.0 (4.2bsd) octopen() sleep()-s
			 * upon TS_CARR_ON in vo_tty[minor(dev)],
			 * having asserted TS_WOPEN, so kick him once
			 * to break up the log jam.
			 *
			 * This is not a problem with R7.x (4.1bsd),
			 * which it does not sleep() on vo_tty[i] in octopen().
			 *
			 *
			 * PRESUMPTION:  1:1 map
			 *	from	 vo_tty[0-(NPORTS-1)]
			 *	to	oct_tty[0-(NPORTS-1)]
			 *
			 * octscan() will re-assert TS_CARR_ON
			 * for any oct_tty[i]'s every few seconds,
			 * due to the Valid's wiring and
			 * jumpers on the Octal Serial pc board.
			 * We also preclude spurious
			 * octopen()'s using another, which might use a
			 * more direct path(name) to the Octal Serial
			 * h/w by also (sort of...) locking oct_tty[i].
			 */
			tp->t_state |= TS_CARR_ON;	/* Turn US on...  */
			wakeup((caddr_t)&tp->t_rawq);
		   }
	   }
	   /*
	    * Get out cleanly if we were processing something
	    * for the octal-driver side -- line was not open yet.
	    * Handle any input when and if she gets her line open.
	    */
	   if (!(tp->t_state & TS_ISOPEN))
		continue;

#endif bsd42
		if (cbp->cb1in!=cbp->cb1out /* input not empty */
		  && !(tp->t_state&TS_TBLOCK) /* able to process */) {
			register int (*cfunc)();
			register char *p;
			register char *buf;
			register int n=cb1full(cbp);
			register struct	tty	*octTp;

			buf=stkalloc(j=n);
			cb1get(cbp,buf,j);
			IrelSeg(cbp);
			cfunc=linesw[tp->t_line].l_rint;
#ifdef	bsd42
			/*
			 * Invoke ttystart(), via (*cfunc)().
			 */
#endif	bsd42
			for (p=buf; --j>=0; ) (*cfunc)(*p++, tp);
			stkfree(n);
			IsetSeg(cbp,vgMmem+VOBASE);
#ifdef	bsd42
			/*
			 * In 4.2bsd, ttywait() sleeps
			 * if TS_BUSY is asserted.
			 *
			 * Somebody is asserting it:
			 * octstart() (which is NOT involved here,)
			 * and possibly someone OTHER
			 * than vo*() or bk*() assert TS_BUSY
			 * after they launch their corresponding h/w.
			 * To date, dunno who...
			 *
			 * The input is complete, so turn it off.
			 * This was not a problem in 4.1bsd.
			 */
			tp->t_state &= ~TS_BUSY;
#endif	bsd42
	        }
	}
	IrelSeg(cbp);
	voscanning&=~2;
	timeout(voscan,0,hz*4/100);
}

/* voselect(dev,flag) uses ttselect */
#endif NOCT > 0 && NVO > 0
