/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1987,1988
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:asy.c 12.0$ */
/* $ACIS:asy.c 12.0$ */
/* $Source: /ibm/acis/usr/sys/caio/RCS/asy.c,v $ */

#if !defined(lint) && !defined(NO_RCS_HDRS)
static char *rcsid = "$Header:asy.c 12.0$";
#endif
/*
 * Multiport asynchronous driver
 */
#include "asy.h"
#if NASY > 0

#include "../h/param.h"
#include "../h/conf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/systm.h"
#include "../h/uio.h"
#include "../h/file.h"
#include "../machine/debug.h"
#include <syslog.h>

#ifdef ATR
extern int cold;		/* if 1, we're still in startup */
#define ASY_ADDR(addr) ((struct asydevice *)((addr) + pcif_io_b));
#else
#define ASY_ADDR(addr) ((struct asydevice *)((addr)));
#endif ATR

#include "../machineio/asyreg.h"
#include "../machine/io.h"
#include "../h/buf.h"
#include "../machineio/ioccvar.h"
#include "kernel.h"
#ifdef ATR
#include "../ca_atr/pcif.h"	/* need to get at pcvec_map[] */
#endif ATR

#include "ap.h"
#if NAP > 0
#include "../machineio/apdefs.h"
#endif

#include "../machine/softint.h"

#define ASY_SETBAUD(addr, baud) { \
	    IOOUT( &(addr)->lcr, (IOIN( &(addr)->lcr ) | LCR_DLAB) );\
	    IOOUT( &(addr)->dllsb, asybaudtbl[(baud)].lsb);\
	    IOOUT( &(addr)->dlmsb, asybaudtbl[(baud)].msb);\
	    IOOUT( &(addr)->lcr, (IOIN( &(addr)->lcr ) & ~LCR_DLAB) );}

static struct asybaudtbl {
	unsigned char msb;
	unsigned char lsb;
} asybaudtbl[NSPEEDS] = {
	 /* B0     */ 	0x00, 0x00,
	 /* B50    */ 	0x09, 0x00,
	 /* B75    */ 	0x06, 0x00,
	 /* B110   */ 	0x04, 0x17,
	 /* B134   */ 	0x03, 0x5b,
	 /* B150   */ 	0x03, 0x00,
	 /* B200   */ 	0x02, 0x40,
	 /* B300   */ 	0x01, 0x80,
	 /* B600   */ 	0x00, 0xc0,
	 /* B1200  */ 	0x00, 0x60,
	 /* B1800  */ 	0x00, 0x40,
	 /* B2400  */ 	0x00, 0x30,
	 /* B4800  */ 	0x00, 0x18,
	 /* B9600  */ 	0x00, 0x0c,
	 /* B19200 */ 	0x00, 0x06,
	 /* EXTB   */ 	0x00, 0x00
};

/* output rate at various speeds; division by 10 is to convert baud to 
 * characters per second */
static int asyoutputrate[] = {
	10/10, 50/10, 75/10, 110/10, 134/10, 150/10, 200/10, 300/10,
	600/10, 1200/10, 1800/10, 2400/10, 4800/10, 9600/10, 19200/10, 38400/10
	};
#define ISPEED B9600		/* default input and output speed */

 /*
  * Encoding of the minor device number.
  * Numbering from the right to left:
  * 	Bits 0 & 1 encode the port number (0-3) on the multiport adapter
  * 	Bits 2, 3 & 4 encode the adapter number as follows:
  *			(0-3) for the multiport adapters
  *			(4-5) for the COM adapters
  *
  *			 4 3 2 1 0
  *			___________
  *			|     |   |
  *			-----------
  */
#define NPORTS	4		/* number of serial port per multiport card */
#define MAXCTLR 6		/* total ctrl cards */
#define MAXUNIT 18		/* total number of ports configured */
struct cbuf asycbuf[MAXUNIT];
 /*
  * Terminology:
  *
  *	ctlr    -- refers to the card number and can have a value from 0 to NASY
  *	port    -- always has a value from 0 to 3 to indicate slot A, B, C, 
  *		   or D	on a given ctlr
  *	unit    -- refers to the index into the tty struct array for a port
  *	           on any ctlr.
  */

 /*
  * Encoding of index into tty structure array:  
  *		2 2 2 2 | 1 1 1 1 | 1 1 1 1 | 1 1
  *	unit    3 2 1 0 | 9 8 7 6 | 5 4 3 2 | 1 0 9 8 | 7 6 5 4 | 3 2 1 0
  *		________|_________|_________|_________|_________|________
  *	port	      0 |       0 | 3 2 1 0 | 3 2 1 0 | 3 2 1 0 | 3 2 1 0
  *	        --------|---------|---------|---------|---------|--------
  *	ctlr       5         4	       3         2	   1         0
  *
  * Note that 17-19 and 21-23 are not used since there is only one port on
  * each of the COM cards.
  *
  * To calculate the index number:
  *			UNIT = (ctlr# * NPORTS ) + port#
  */
#define UNIT(ctlr, port)	(((ctlr) << 2) + (port))
#define CTLR(unit)		((unit) >> 2)
#define PORT(unit)		((unit) & 0x03)
#define PORTMASK(port)		((1 << (port)) & 0x0f) 


struct tty asy[MAXUNIT];
/* Average new into old with aging factor time */
#define	ave(smooth, cnt, time) \
	smooth = ((time - 1) * (smooth) + (cnt)) / (time)
#define	FASTTIMER	(hz/100) /* scan rate with silos on */
#define	SLOWTIMER	(hz)    /* scan rate without silos on */
int	asysilos = 0;		/* mask of ports with silo in use */
int	asytimeout = 0;		/* mask ports with enq timeout set */
int	asychars[MAXUNIT];	/* recent input count */
int	asyinchars[MAXUNIT];	/* previous input count */
int	asyrate[MAXUNIT];	/* smoothed input count */
int	asyhighrate = 100;	/* silo on if dhchars >= dhhighrate */
int     asylowrate = 15;
int 	asyfasttimers = 0;  /*DEBUG*/
static short timerstarted;


#if NAP > 0
int asyoutc();
#endif
int asystart(), ttrstrt(), asyattach(), asyprobe(), asyint(), asytimer();

/* following is a hokey test for a quad card */
#ifdef IBMRTPC
#define isquad(addr) (0x0000f000 & (int) (addr) )
#endif IBMRTPC

#ifdef ATR
/* this will eliminate all the COM addresses */
#define isquad(addr) ( (0x0000f000 & (int)(addr)) && (0x00000010 &(int)(addr)) )
#endif ATR

/* define standard addresses for autoconf */
caddr_t asystd[] = {
#ifdef ATR
	(caddr_t)0x00001230,	       /* Multiport adaptor 1 */
	(caddr_t)0x00002230,	       /* Multiport adaptor 2 */
	(caddr_t)0x00003230,	       /* Multiport adaptor 3 */
	(caddr_t)0x00004230,	       /* Multiport adaptor 4 */
	(caddr_t)0x000003f8,	       /* PC/PS asynch adaptor COM1 */
	(caddr_t)0x000002f8,	       /* PC/PS asynch adaptor COM2 */
	(caddr_t)0x00003220,	       /* PC/PS asynch adaptor COM3 */
	(caddr_t)0x00003228,	       /* PC/PS asynch adaptor COM4 */
	(caddr_t)0x00004220,	       /* PC/PS asynch adaptor COM5 */
	(caddr_t)0x00004228,	       /* PC/PS asynch adaptor COM6 */
	(caddr_t)0x00005220,	       /* PC/PS asynch adaptor COM7 */
	(caddr_t)0x00005228,	       /* PC/PS asynch adaptor COM8 */
#endif ATR
#ifdef IBMRTPC
	(caddr_t)0xf0001230,	       /* Multiport adapter 1 */
	(caddr_t)0xf0002230,	       /* Multiport adapter 2 */
	(caddr_t)0xf0003230,	       /* Multiport adapter 3 */
	(caddr_t)0xf0004230,	       /* Multiport adapter 4 */
/* we do not define the COM cards here for an RT because this screws up the 
 * the ctlr number in autoconf if there are no multiport cards present
 */
#endif IBMRTPC
	0
};

#ifdef ATR
#define	COM1_ADDR	0x000003f8
#define	COM2_ADDR	0x000002f8
#define	COM3_ADDR	0x00003220
#define	COM4_ADDR	0x00003228
#define	COM5_ADDR	0x00004220
#define	COM6_ADDR	0x00004228
#define	COM7_ADDR	0x00005220
#define	COM8_ADDR	0x00005228
#endif ATR

#ifdef IBMRTPC
#define BADD_ADDR (caddr_t) 0xeeeeeeee /* to catch bad IRQ number */

caddr_t asyreset[16] = {
	BADD_ADDR, BADD_ADDR, BADD_ADDR, BADD_ADDR, /* 0 - 3 */
	BADD_ADDR, BADD_ADDR, BADD_ADDR, BADD_ADDR, /* 4 - 7 */
	BADD_ADDR, (caddr_t)0xf00002f2,	
	  (caddr_t)0xf00006f2, (caddr_t)0xf00006f3, /* 8 - 11 */
	BADD_ADDR, BADD_ADDR, BADD_ADDR, BADD_ADDR, /* 12 - 15 */
};
#endif IBMRTPC

char asysoftCAR[NASY];	    		/*  software carrier detect  */
unsigned char asysoftFIFO[MAXUNIT];	/*  software - FIFO mode */

struct iocc_device *asydinfo[NASY];
struct iocc_driver asydriver = {
	/* probe, slave, attach, dgo, addr, dname, dinfo, mname, csr, intr */
	   asyprobe, 0, asyattach, 0, asystd, "asy", asydinfo, 0, 0, asyint
};

/*   asyprobe will try to generate an interrupt so that the system can
 *   determine whether or not the async ctlrs specified in the config
 *   file are truly present.  It will also try to determine the ctlrs
 *   interrupt request level (IRQ).  The interrupts are generated using 
 *   the loopback feature provided by the INS8250.
 */

asyprobe(addr)
	register caddr_t addr;
{
	register struct asydevice *asyp = ASY_ADDR(addr);
	char saveiir;

	DEBUGF(ttydebug, printf("asyprobe(%x):\n", addr));
	

	if (isquad(asyp)) {
#ifdef ATR
		return(PROBE_BAD);  /* no quad card support in ATR version */
#endif ATR
#ifdef IBMRTPC
		IOOUT( &asyp->ppir, 0xff); /* initialize ppir - ONLY ONCE */
		DEBUGF(ttydebug, 
		printf("ppir=%x, iir=%x\n",IOIN(&asyp->ppir),IOIN(&asyp->iir)));

		/* reset all shared interrupts */
		IOOUT(asyreset[9], 0);
		IOOUT(asyreset[10], 0);
		IOOUT(asyreset[11], 0);
#endif IBMRTPC
	}
	
#ifdef ATR
	DEBUGF(ttydebug, printf("asyprobe: pcif_io_b = 0x%x\n", pcif_io_b));

	if ((int)addr == COM1_ADDR) 
		pcvec_map[4] = 1;	/* forward IRQ4 interrupts from PS/2 */
	else
		pcvec_map[3] = 1;	/* forward IRQ3 interrupts from PS/2 */
#endif ATR

	DEBUGF(ttydebug, printf(" lsr=%x,", IOIN( &asyp->lsr ) ) );
	DEBUGF(ttydebug, printf(" msr=%x\n", IOIN( &asyp->msr ) ) );
	IOOUT(&asyp->ier, IER_MSR);     	/* enable modem status int. */
	IOOUT(&asyp->mcr, MCR_IEN);		/* enable modem ints (only */
	DELAY(1);				/* for COM card; does not */
						/* affect Multiport card) */
	IOOUT(&asyp->mcr, MCR_IEN | MCR_LOOP) ; /* enable LOOP mode */
	DELAY(1);
	IOOUT(&asyp->mcr, MCR_IEN); 		/* disable LOOP mode */
	PROBE_DELAY(10000);
	DEBUGF(ttydebug, printf(" iir=%x", IOIN( &asyp->iir )));
	IOOUT(&asyp->ier,0);	        	/* disable all interrupts */
	saveiir = IOIN(&asyp->msr);		/* reset modem interrupts */
	DEBUGF(ttydebug, printf(" msr=%x\n", saveiir));

#ifdef IBMRTPC
	/* reset all shared interrupts */
	if (isquad(addr)) {
		IOOUT(asyreset[9], 0);
		IOOUT(asyreset[10], 0);
		IOOUT(asyreset[11], 0);
		PROBE_DELAY(10000);
	}
#endif IBMRTPC
	return (PROBE_OK);
}


asyattach(iod)
	register struct iocc_device *iod;
{
	register struct tty *tp;
	register int ctlr = iod->iod_unit;
	register int port;  /* line number on this ctlr */
	register int saveiir;
	register struct asydevice *addr = ASY_ADDR(iod->iod_addr);

	DEBUGF(ttydebug & SHOW_INIT, printf("asyattach: ctlr=%d addr=%x\n", ctlr,addr));
	/* initializes the tty struct for each port on this ctlr */
	for (port = 0, tp = &asy[UNIT(ctlr,port)]; port < NPORTS; port++, addr++, tp++) {
		DEBUGF(ttydebug & SHOW_INIT, 
		printf("port = %d, tp = %x, addr = %x\n", port, tp, addr ));
		tp->t_addr = (caddr_t)addr;
		tp->t_cbufp = &asycbuf[UNIT(ctlr,port)];
		tp->t_state = 0;
		/* these instructions have no effect on non-FIFO card */
		/* clear fifos */
		IOOUT(&addr->fcr, FCR_FCR1|FCR_FCR2);
 		/* enable FIFO mode, interrupt at 14 chars */
 		IOOUT(&addr->fcr,
			FCR_FCR0|FCR_FCR6|FCR_FCR7);
		saveiir=IOIN(&addr->iir) ;
 		if (saveiir & (IIR_FIFO1 | IIR_FIFO2)) {
 			++asysoftFIFO[UNIT(ctlr,port)];	
		}
		else
			asysoftFIFO[UNIT(ctlr,port)] = 0;
				
		asysoftCAR[iod->iod_unit] = iod->iod_flags; /* carrier detect */
		if (!isquad(addr))
			break;	       /* only one port per PC card */
	}
}

/*ARGSUSED*/
asyopen(dev, flag)
	dev_t dev;
{
	register struct tty *tp;
	register int unit = minor(dev);
	register int s, msrbits, error;
	register struct asydevice *asyaddr;
	register struct iocc_device *iod;
	int ctlr = CTLR(unit);

	DEBUGF(ttydebug & SHOW_OPEN, printf("asyopen%x: \n", unit));
	s = spl_asy();
	if (unit >=MAXUNIT || (iod=asydinfo[ctlr])==0 || iod->iod_alive==0) {
		(void) splx(s);
		return (ENXIO);
	}

	tp = &asy[unit];
	if (tp->t_state & TS_XCLUDE && u.u_uid != 0) {
		(void) splx(s);
		return (EBUSY);
	}

	asyaddr = (struct asydevice *)tp->t_addr;
	tp->t_state |= TS_WOPEN;

	if (tp->t_state & TS_CLOSE) {
		ttywait(tp);			/* wait til complete */
	}
	/* if this is first open, initialize tty state to defaults */
	if ((tp->t_state & TS_ISOPEN) == 0) {
		ttychars(tp);
		tp->t_oproc = asystart;
		tp->t_ospeed = tp->t_ispeed = ISPEED;
		tp->t_flags = ODDP | EVENP | ECHO;
 
		asyparam(unit);

      	    	if (asysoftFIFO[unit]) {
			/* flush FIFO */
			IOOUT(&asyaddr->fcr, FCR_FCR1|FCR_FCR2);
 			/* enable FIFO mode, interrupt at 1 chars */
 			IOOUT(&asyaddr->fcr, FCR_FCR0);
		}
		/* enable receive, line, modem and transmit interrupts */
		IOOUT(&asyaddr->ier, IER_DR | IER_LSR | IER_MSR | IER_THRE);
		/* turn DTR RTS on */
     		if (isquad(asyaddr))
			IOOUT(&asyaddr->mcr, MCR_DTR | MCR_RTS);
		else
			IOOUT(&asyaddr->mcr, MCR_IEN | MCR_DTR | MCR_RTS);
		(void) splx(s);
	} 
	s = spl_asy();
	/* check if carrier is or up modem is on */
	msrbits = IOIN(&asyaddr->msr);
	if ((msrbits & MSR_DCD) || 
 	     ( asysoftCAR[ctlr] & PORTMASK(PORT(unit)) ) ) 
 		tp->t_state |= TS_CARR_ON;
	/* wait for carrier to come up */
	while ( (tp->t_state & TS_CARR_ON) == 0 ) {
		tp->t_state |= TS_WOPEN;
		sleep((caddr_t) &tp->t_rawq, TTIPRI);
	}
	(void) splx(s);

	DEBUGF(ttydebug & SHOW_OPEN, printf("asyopen%d: ier=%x, mcr=%x", 
		unit, IOIN(&asyaddr->ier), IOIN(&asyaddr->mcr) ));
	if ( error = (*linesw[tp->t_line].l_open)(dev, tp) ) 
		asyclose(dev);
	DEBUGF(ttydebug & SHOW_OPEN, printf(" ldisc=%x, flags=%x, state=%x\n",
		tp->t_line, tp->t_flags, tp->t_state ));
	if (timerstarted == 0) {
		timeout(asytimer, (caddr_t) 0, SLOWTIMER);
		++timerstarted;
	}
	return ( error );
}


/*ARGSUSED*/
asyclose(dev)
	dev_t dev;
{
	register int unit = minor(dev);
	register struct tty *tp = &asy[unit];
	register struct asydevice *asyaddr = (struct asydevice *)tp->t_addr;
	register int s = spl_asy();
	int asycls();

	DEBUGF(ttydebug & SHOW_OPEN, printf("In asyclose ..."));

	tp->t_state |= TS_CLOSE;		/* close in progress */
	asychars[unit] = 0;
	asysilos &= ~(1 << unit);

	timeout(asycls, tp, asyouttime(tp));	/* set timeout for close */

	(*linesw[tp->t_line].l_close)(tp);
	IOOUT(&asyaddr->lcr, IOIN(&asyaddr->lcr) & ~LCR_SETB);  /* clr break */
	if ((tp->t_state&(TS_WOPEN|TS_HUPCLS))||(tp->t_state&TS_ISOPEN)==0) {
		IOOUT(&asyaddr->ier, ~(IER_DR | IER_LSR | IER_MSR) );
     		if (isquad(asyaddr))
			IOOUT(&asyaddr->mcr, 0);
		else
			IOOUT(&asyaddr->mcr, MCR_IEN);
 		IOOUT(&asyaddr->fcr, ~(FCR_FCR0));
		DEBUGF(ttydebug & SHOW_OPEN, 
			printf("turning off DTR, RTS...disabling ints..."));
	}
close1:
	if (tp->t_state & TS_CLOSE)		/* close in progress */
		untimeout(asycls, tp);		/* cancel the timeout */
	ttyclose(tp);
	DEBUGF(ttydebug & SHOW_OPEN, printf("asyclose end\n"));
	(void) splx(s);
}

int asyfasttimers, asytransitions, asyslowtimers, asybogons;  /*DEBUG*/
/*
 * At software clock interrupt time, check status.
 * Empty all the asy silos that are in use, and decide whether
 * to turn any silos off or on.
 */
asytimer()
{
	register int unit = 0, s;
	static int timercalls;
	register struct tty *tp;
	register struct asydevice *asyaddr;

	if (asysilos) {
		s = spl_asy();
		asyfasttimers++;		/*DEBUG*/
		timercalls++;
		for (unit = 0; unit < MAXUNIT; unit++)
			if (asysilos & (1 << unit)) {
			   tp = &asy[unit];
			   asychars[unit] += asyrint(unit, tp,
					(struct asydevice *)tp->t_addr);
			}
		splx(s);
	}
	if ((asysilos == 0) || ((timercalls+=FASTTIMER) >= SLOWTIMER)) {
		asyslowtimers++;		/*DEBUG*/
		timercalls = 0;
		for (unit = 0; unit < MAXUNIT; unit++) {
		    ave(asyrate[unit], asychars[unit], 8);
		    if ((asychars[unit] >= asyhighrate) &&
		      ((asysilos & (1 << unit)) == 0)) {
			asysilos |= (1 << unit);
			asytransitions++;		/*DEBUG*/
      	    		if (asysoftFIFO[unit]) {
			   	tp = &asy[unit];
				asyaddr = (struct asydevice *)tp->t_addr;
 				/* FIFO mode, trigger at 14 chars */
 				IOOUT(&asyaddr->fcr,
					FCR_FCR0|FCR_FCR6|FCR_FCR7);
			}
		    } else if ((asysilos & (1 << unit)) &&
		      (asyrate[unit] < asylowrate)) {
			asysilos &= ~(1 << unit);
      	    		if (asysoftFIFO[unit]) {
			   	tp = &asy[unit];
				asyaddr = (struct asydevice *)tp->t_addr;
 				/* FIFO mode, trigger at 1 char */
 				IOOUT(&asyaddr->fcr, FCR_FCR0);
			}
		    }
		    asychars[unit] = 0;
		}
	}
	timeout(asytimer, (caddr_t) 0, asysilos? FASTTIMER: SLOWTIMER);
}


/*
 * return an overestimate of the number of clock ticks it will take
 * to output the remaining output after close has started
 * the multiplcation by 3 and division by 2 give a 50% overestimate 
 * plus hz gives an additional second just in case.
 */

asyouttime(tp)
register struct tty *tp;
{
	return (hz + 3 * hz * tp->t_outq.c_cc / (2 * asyoutputrate[tp->t_ospeed]));
}

/*
 * routine called when close takes longer than it should to flush output
 * and get things going again
 */
asycls(tp)
register struct tty *tp;
{
	register int s = spl_asy();

	DEBUGF(ttydebug & SHOW_OPEN, printf("asycls called\n"));
	if (tp->t_state & TS_CLOSE) {		/* if close in progress */
		ttyflush(tp, FREAD|FWRITE);
		tp->t_state = 0;
	}
	(void) splx(s);
}


/*ARGSUSED*/
asyread(dev, uio)
	register dev_t dev;
	register struct uio *uio;
{
	register struct tty *tp = &asy[minor(dev)];
	DEBUGF(ttydebug & SHOW_RDWR, {
	    register struct asydevice *asyaddr=(struct asydevice *)tp->t_addr;
	    printf("In asyread: asyaddr=(%x) lcr=(%x) ier=(%x)\n", 
		    asyaddr, IOIN(&asyaddr->lcr), IOIN(&asyaddr->ier) ); 
	} );
	return ((*linesw[tp->t_line].l_read)(tp, uio));
}


/*ARGSUSED*/
asywrite(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	register struct tty *tp = &asy[minor(dev)];

	DEBUGF(ttydebug & SHOW_RDWR, printf("In asywrite\n"));
	return ((*linesw[tp->t_line].l_write)(tp, uio));
}



/*ARGSUSED*/
/* modeled after dh-11 */
asyioctl(dev, cmd, addr, flag)
	dev_t dev;
	caddr_t addr;
{
	register int unit = minor(dev);
	register struct tty *tp = &asy[unit];
	register struct asydevice *asyaddr = (struct asydevice *)tp->t_addr;
	register int error, s;
 	int trigger_level;
 	char trigger_bits;

	DEBUGF(ttydebug, printf("asyioctl%d: cmd=%x, flag=%x\n",unit,cmd,flag));
	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, addr, flag);
#if NAP > 0
	if ( cmd == TIOCSETD )
		if ( *(int *)addr == APLDISC )
			tp->t_oproc = asyoutc;
		else 
			tp->t_oproc = asystart;
#endif NAP
	if (error >= 0)
		return (error);
	error = ttioctl(tp, cmd, addr, flag);
	if (error >= 0) {
		if (cmd == TIOCSETP || cmd == TIOCSETN || cmd == TIOCLBIS ||
		    cmd == TIOCLBIC || cmd == TIOCLSET ) {
			asyparam(unit);
		}
		return (error);
	}
	s = spl_asy();
	switch (cmd) {
	case TIOCSBRK:		       /* Set break */
		IOOUT(&asyaddr->lcr, IOIN(&asyaddr->lcr) | LCR_SETB);
		break;
	case TIOCCBRK:		       /* Clear break */
		IOOUT(&asyaddr->lcr, IOIN(&asyaddr->lcr) & ~LCR_SETB);
		break;
	case TIOCSDTR:		       /* Set DTR and RTS */
     		if (isquad(asyaddr))
			IOOUT(&asyaddr->mcr, MCR_DTR | MCR_RTS);
		else
			IOOUT(&asyaddr->mcr, MCR_IEN | MCR_DTR | MCR_RTS);
		break;
	case TIOCCDTR:		       /* Clear DTR and RTS */
     		if (isquad(asyaddr))
			IOOUT(&asyaddr->mcr, 0);
		else
			IOOUT(&asyaddr->mcr, MCR_IEN);
		break;
 	case TIOCSETT:		       /* Set trigger level */
 		trigger_level = *(int *)addr;
 		if (trigger_level < 4)  
  			trigger_bits = 0;
 		else
 			if (trigger_level < 8)
 				trigger_bits = FCR_FCR6;
 			else
 	       		 	if (trigger_level < 14)
 					trigger_bits = FCR_FCR7;
 				else 
  					trigger_bits = (FCR_FCR7 | FCR_FCR6);
 		IOOUT(&asyaddr->fcr, (FCR_FCR0 | trigger_bits));
 		break;	
	default:
		(void) splx(s);
		return (ENOTTY);
	}
	(void) splx(s);
	return (0);
}




asyparam(unit)
	register int unit;
{
	register struct tty *tp = &asy[unit];
	register struct asydevice *asyaddr = (struct asydevice *)tp->t_addr;
	register char bits;
	register int s;

	DEBUGF(ttydebug & SHOW_IO, 
 	    printf("asyparam%d: tp->t_flags=(%x)", unit, tp->t_flags));

	s = spl_asy();
	if (tp->t_ispeed == B0) {
		tp->t_state |= TS_HUPCLS;
		IOOUT(&asyaddr->mcr, MCR_IEN); /* hang up line (DTR,RTS =0) */
		(void) splx(s);
		return;
	}

	/* Set the baud rate and initial asy settings */
	ASY_SETBAUD(asyaddr, tp->t_ispeed);
	if (tp->t_flags & (RAW | LITOUT | PASS8))
		bits = LCR_8BIT;
	else
		bits = (LCR_7BIT | LCR_PEN);
	if (tp->t_flags & EVENP)
		bits |= LCR_EPS;
	if (tp->t_ispeed == B110)
		bits |= LCR_STB;
	IOOUT(&asyaddr->lcr, bits);


	DEBUGF(ttydebug & SHOW_REGS, printf("lcr=(%x)\n", IOIN(&asyaddr->lcr)));
	DEBUGF(ttydebug & SHOW_IO, printf(" asyparam end\n"));
	(void) splx(s);
}


/*
 * unit indicates which port on a 4 line ctlr caused the interrupt
 */
asyint(ctlr)
	register int ctlr;
{
	register int unit = UNIT(ctlr,0);
	register struct asydevice *asyaddr = ASY_ADDR(asydinfo[ctlr]->iod_addr);
	register struct tty *tp;
	register unsigned char saveiir;
	register unsigned char port = 0;
	register char c;
	register int counter;

#ifdef ATR
	/*
	 * Since we can share interrupts, we can be called during
	 * startup when probing ports that share the same IRQ level.
	 * Return with an error in this case.
	 */
	if (cold)
		return(-1);
#endif ATR
	DEBUGF(ttydebug & SHOW_INTR,printf("asyint%d: addr=%x ",unit,asyaddr));
#ifdef IBMRTPC
     	if (isquad(asyaddr)) {
    		port = IOIN(&asyaddr->ppir);
    		port &= 0x0F;
		DEBUGF(ttydebug & SHOW_INTR, printf("port=%x ",port));
		if (0 == port) {
			saveiir = IOIN(&asyaddr->iir);
			if ((0xC0 & saveiir)||(0x01 == saveiir))
				;
			else {
				log(LOG_WARNING,
			     "asyint: no ppir, saveiir = %x\n",
					saveiir);
			}
			return(0);
		}
portselect:
		if (port & PORT_A) {
	    		unit = UNIT(ctlr,0);
			port &= ~PORT_A;
		}
		else if (port & PORT_B) {
	    		unit = UNIT(ctlr,1);
			port &= ~PORT_B;
		}
		else if (port & PORT_C) {
	    		unit = UNIT(ctlr,2);
			port &= ~PORT_C;
		}
		else if (port & PORT_D) {
	    		unit = UNIT(ctlr,3);
			port &= ~PORT_D;
		}
		else {
	    		unit = UNIT(ctlr,0);
			printf("asyint: Bogus port = %x\n", port);
		}
	}
#endif IBMRTPC
	tp = &asy[unit];
	asyaddr = (struct asydevice *)tp->t_addr;
	saveiir = IOIN(&asyaddr->iir);
	if (IIR_PEND & saveiir) { 
		++asybogons;
		return(0);
	}
     	counter = asychars[unit];
	do {
		switch (saveiir & IIR_MASK) {
		   case IIR_RXB:
     			while (IOIN(&asyaddr->lsr) & LSR_DR) {
				c = IOIN(&asyaddr->rxb);
				(*linesw[tp->t_line].l_rint)(c,tp);
				++counter;
			}
     			break;
     		   case IIR_LSR:
     		   case IIR_CTI:
     			counter += asyrint(unit, tp, asyaddr);
     			break;
     		   case IIR_TXB:
     			asyxint(unit, tp, asyaddr);
     			break;
     		   case IIR_MSR:
     			asymint(unit, tp, asyaddr);
     			break;
		   default:
			printf("asy%d:iir=%x\n",unit,saveiir);
			break;
		}
	} while (((saveiir=IOIN(&asyaddr->iir))&IIR_PEND)==0);
     	asychars[unit] = counter;
#ifdef IBMRTPC
	if ((port) && isquad(asyaddr))
		goto portselect; /*more than one port pending*/
#endif
	return(0);
}

/*ARGSUSED*/
int
asyrint(unit, tp, asyaddr)
	register int unit;
	register struct tty *tp;
	register struct asydevice *asyaddr;
{
	register int c;
	register char savelsr;
	int charcount = 0;

	DEBUGF(ttydebug & SHOW_INTR, printf("asyrint%d: \n", unit));
	while ((savelsr = IOIN(&asyaddr->lsr)) & LSR_DR) {
		c = IOIN(&asyaddr->rxb);
#if NAP > 0
		if (tp->t_line == APLDISC) {
			if (savelsr & LSR_BI)
				c = APBREAK;
			else if (savelsr & LSR_FE)
				c = APERROR;
			DEBUGF(ttydebug & SHOW_CHARS, 
				printf("AP GOT -> (%x)\n", (char)c));
			(*linesw[tp->t_line].l_rint)(c, tp);
		} else 
#endif NAP
		{
		if (savelsr & (LSR_PE | LSR_FE | LSR_OR | LSR_BI)) {
			if ((savelsr & LSR_PE) && 
			    ((tp->t_flags & (EVENP|ODDP)) != (EVENP|ODDP)) &&
			    ((tp->t_flags & (EVENP|ODDP)) != 0) ) {
				DEBUGF(ttydebug,
					printf("asy%d: parity error\n",unit));
	    			continue;
			}
			/*
			 * A framing error (break) generates
			 * a null (in raw mode, for getty), or
			 * an interrupt (in cooked/cbreak modes).
			 */
			if (savelsr & (LSR_FE | LSR_BI))
				if (tp->t_flags & RAW)
					c = 0;
				else
					c = tp->t_intrc;

			if (savelsr & LSR_OR)
				log(LOG_WARNING, "asy%d: overrun\n", unit);
			DEBUGF(ttydebug & SHOW_CHARS, 
				printf("GOT -> (%c) (%x)\n", c, c));
		} /* end if errors */
		(*linesw[tp->t_line].l_rint)(c,tp);
	        } /* end else */
		if (charcount > 32)
			break; /* DEBUG */
	} /* end while */
	DEBUGF(ttydebug & SHOW_INTR, 
		printf("asy%d: charcount=%d\n",unit,charcount));
	return(charcount);
}

/*ARGSUSED*/
asyxint(unit, tp, asyaddr)
	register int unit;
	register struct tty *tp;
	register struct asydevice *asyaddr;
{
	DEBUGF(ttydebug & SHOW_INTR, printf("asyxint%d: ", unit));
#if NAP > 0
	if (tp->t_line == APLDISC) {
		DEBUGF(ttydebug & SHOW_INTR, printf("calling AP xint \n"));
		(*linesw[tp->t_line].l_start)(tp);
	} else 
#endif NAP
	{
		tp->t_state &= ~TS_BUSY;
		if (tp->t_line)
			(*linesw[tp->t_line].l_start)(tp);
		else
			asystart(tp);
	}
	DEBUGF(ttydebug & SHOW_INTR, printf("asyxint end\n"));
}

asystart(tp)
	register struct tty *tp;
{
	register struct asydevice *asyaddr = (struct asydevice *)tp->t_addr;
 	register int i, s;
 	register int c = 0;

	DEBUGF(ttydebug & SHOW_IO, printf("asystart addr=%x\n", asyaddr));
	/*
	 * Must hold interrupts to prevent state of the tp
	 * from changing
	 */
	s = spl_asy();
	/*
	 * if it's currently active, or delaying, no need to do anything.
	 */
	if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))
		goto out2;
 	if (IOIN(&asyaddr->lsr) & LSR_THRE) {
      	    /* unit = tp - asy */
      	    if ((asysoftFIFO[tp - asy]) &&
       		(tp->t_flags & (RAW | LITOUT))) {
      		for (i=0; i<9; i++) {
       			if (tp->t_outq.c_cc <= 0)
       				goto out1;
			if ((c = getc(&tp->t_outq)) == -1)
       				goto out1;
       			IOOUT(&asyaddr->txb, c);
      	    	}
	    }
       	
            do {
		if ((tp->t_outq.c_cc <= 0) || (c = getc(&tp->t_outq)) == -1)
                 	goto out1;
                 DEBUGF(ttydebug & SHOW_CHARS,
                 	printf("put (%c) (%x)\n", (char)(c), (char)(c) ));
                 if ( tp->t_flags & (RAW | LITOUT) || (c <= 0177) ) 
                     IOOUT(&asyaddr->txb, c);
                 else {
                     timeout(ttrstrt, (caddr_t)tp, (c & 0177));
                     tp->t_state |= TS_TIMEOUT;
                     DEBUGF(ttydebug & SHOW_CHARS,
				printf("asystart timeout\n"));
                     goto out1;
	        }
	    } while (IOIN(&asyaddr->lsr) & LSR_THRE);
	}
	tp->t_state |= TS_BUSY;
	DEBUGF(ttydebug & SHOW_REGS, printf("asystart: enable TX int\n"));
	IOOUT(&asyaddr->ier, IOIN(&asyaddr->ier) | (IER_THRE));
	goto out2;
out1:
	tp->t_state &= ~TS_BUSY;

	/*
	 * if there are sleepers, and output has drained below low
	 * water mark, wake up sleepers.
	 */

	if (tp->t_outq.c_cc <= TTLOWAT(tp)) { 
		if (tp->t_state & TS_ASLEEP) {
			tp->t_state &= ~TS_ASLEEP;
			wakeup((caddr_t) & tp->t_outq);
		}
		if (tp->t_wsel) {
			selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
			tp->t_wsel = 0;
			tp->t_state &= ~TS_WCOLL;
		}
	}  
out2:
	(void) splx(s);
	DEBUGF(ttydebug & SHOW_IO, printf(" asystart: done\n" ));
}


asymint(unit, tp, asyaddr)
	register int unit;
	register struct tty *tp;
	register struct asydevice *asyaddr;
{
	register char savemsr;

	savemsr = IOIN(&asyaddr->msr);
	DEBUGF(ttydebug & SHOW_INTR, 
		printf("asymint%d: savemsr=%x\n", unit, savemsr ) );
#if NAP > 0
	if (tp->t_line == APLDISC)
		return (0);
#endif
	if (savemsr & MSR_DDCD) {     /* if carrier transition */
		if (savemsr & MSR_DCD) { /* carrier on */
			DEBUGF( ttydebug & SHOW_INTR,printf("Carrier detect "));
			if ((tp->t_state & TS_CARR_ON) == 0) {
				wakeup((caddr_t) &tp->t_rawq);
			        (*linesw[tp->t_line].l_modem)(tp, 1);
				tp->t_state |= TS_CARR_ON;
			}
		} else {  		  /* carrier dropped */
			DEBUGF( ttydebug & SHOW_INTR,printf("Carrier lost "));	
			if (tp->t_state & TS_CARR_ON) {
				gsignal(tp->t_pgrp, SIGHUP);
				gsignal(tp->t_pgrp, SIGCONT);
				ttyflush(tp, FREAD | FWRITE);
			}
			(*linesw[tp->t_line].l_modem)(tp, 0);
			tp->t_state &= ~TS_CARR_ON;
		}
	}
	DEBUGF(ttydebug & SHOW_INTR, printf("asymint end\n"));
}

asyselect(dev, rw)
	dev_t dev;
	int rw;
{
	register struct tty *tp = &asy[minor(dev)];

	return ((*linesw[tp->t_line].l_select)(dev, rw));
}

/* 
 * This routine is for the support of the 3812 printer. It will perform one
 * of three functions:
 *	    1) set the break bit 
 *	    2) clear the break bit  
 *	or  3) output one character.
 */
#if NAP > 0
asyoutc(tp, c)
	register struct tty *tp;
	register int c;
{
	register struct asydevice *asyaddr = (struct asydevice *)tp->t_addr;
	register int s = spl_asy();

	DEBUGF(ttydebug & SHOW_CHARS, printf("asyoutc: PUT (%x)\n", (char) c));
	if (c >= 0)
		IOOUT(&asyaddr->txb, c);
	else {
		switch (c) {
		case APBREAKON:
			IOOUT(&asyaddr->lcr, IOIN(&asyaddr->lcr) | LCR_SETB);
			break;
		case APBREAKOFF:
			IOOUT(&asyaddr->lcr, IOIN(&asyaddr->lcr) & ~LCR_SETB);
			break;
		case APINTMASK:
			IOOUT(&asyaddr->ier, (IER_LSR | IER_THRE | IER_DR));
			break;
		}
	}
	(void) splx(s);
}
#endif

#endif NASY
