Coherent4.2.10/io.386/alx.c

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

/* $Header: /ker/io.386/RCS/alx.c,v 2.6 93/10/29 00:58:25 nigel Exp Locker: nigel $ */
/* (-lgl
 * 	COHERENT Driver Kit Version 2.1.0
 * 	Copyright (c) 1982, 1993 by Mark Williams Company.
 * 	All rights reserved. May not be copied without permission.
 -lgl) */
/*
 * Shared parts of IBM async port drivers.
 *
 * $Log:	alx.c,v $
 * Revision 2.6  93/10/29  00:58:25  nigel
 * R98 (aka 4.2 Beta) prior to removing System Global memory
 * 
 * Revision 2.5  93/09/13  08:06:19  nigel
 * Changed to reflect the fact that entry points are 'void' once more
 * 
 * Revision 2.4  93/08/19  10:38:25  nigel
 * r83 ioctl (), corefile, new headers
 * 
 * Revision 2.3  93/08/19  04:02:04  nigel
 * Nigel's R83
 */

#include <kernel/timeout.h>

#include <sys/coherent.h>
#include <sys/al.h>
#include <sys/con.h>
#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/tty.h>
#include <sys/clist.h>
#include <sys/ins8250.h>
#include <sys/sched.h>
#include <sys/silo.h>
#include <sys/signal.h>

#define ALPORT	(((COM_DDP *) (tp->t_ddp))->port)
#define AL_NUM	(((COM_DDP *) (tp->t_ddp))->com_num)

#define DTRTMOUT  3	/* DTR timeout interval in seconds for close */

/*
 * For rawin silo (see poll_clk.h), use last element of si_buf to count
 * the number of characters in the silo.
 */
#if 0
#define SILO_CHAR_COUNT	si_buf[SI_BUFSIZ-1]
#else
#define SILO_CHAR_COUNT	si_cnt
#endif
#define SILO_HIGH_MARK	(SI_BUFSIZ-SI_BUFSIZ/4)
#define SILO_LOW_MARK	(SI_BUFSIZ/4)
#define MAX_SILO_INDEX	(SI_BUFSIZ-2)
#define MAX_SILO_CHARS	(SI_BUFSIZ-1)

/*
 * The following silo FLUSH macros are always called at high priority!
 */
#ifdef NO_ISILO
#define RAWIN_FLUSH(in_silo)
#else
#define RAWIN_FLUSH(in_silo)	{ in_silo->si_ox = in_silo->si_ix; \
	  in_silo->SILO_CHAR_COUNT = 0; }
#endif
#define RAWOUT_FLUSH(out_silo) { out_silo->si_ox = out_silo->si_ix; }

int	al_sg_set = 0;
int	al_sg_clr = 0;
static	int poll_divisor;  /* set by set_poll_rate(), read by alxclk() */

/*
 * functions herein
 */

void	alxparam ();
void	alxcycle ();
void	alxbreak ();
static	int alxclk();
static	void alxpoll();
static	void alx_send();
static	int iocbaud[4];
static	char ioclcr[4];

/*
 * Port addresses are now patchable.
 */
int AL_ADDR[] = {
#if BOB_H
	0x280, 0x288, 0x290, 0x298
#else
	0x3F8, 0x2F8, 0x3E8, 0x2E8
#endif
};

/*
 * Baud rate table and polling rate table.
 * Indexed by ioctl bit rates.
 */
extern int albaud[], alp_rate[];

/*
 *	the following is for debug only
 */

#if	TRACER & TRACE_HAL
#include <sys/proc.h>

int ASY_OR = 0;
#define LSR_READ(lval, port) \
  { lval = inb((port)+LSR); if (lval & LS_OVER) ASY_OR++; }
#define CDUMP(text, tp)	cdump(text, tp);
#define tprintf(str)	{ T_HAL(4, printf(str)); }
#define REPORT_OE {if(ASY_OR&&(t_hal&0x20)){printf("oe=%d ",ASY_OR);ASY_OR=0;}}
cdump(message, tp)
char *message;
TTY *tp;
{
	int i, b;
	char cmd[11];

	if ((t_hal & 4) == 0)
		return;
	for (i = 0; i < NUM_AL_PORTS; i++) {
		if (tp_table[i]) {
			b = ((COM_DDP *)(tp_table[i]->t_ddp))->port;
			printf("%x:%x:%x:%x ", i+1, b, inb(b+MCR), inb(b+IER));
		}
	}
	for (i = 0; i < 10; i++) {
		cmd[i] = u.u_comm[i];
	}
	cmd[10] = '\0';
	printf("poll=%d cmd=%s pid=%d ", poll_rate, cmd, SELF->p_pid);
	printf("%s\n", message);
	if (tp) {
		printf("#%d f=%x op=%d ", AL_NUM, tp->t_flags, tp->t_open);
		printf("in_use=%d irq=%d has_irq=%d ",
		  com_usage[AL_NUM].in_use,
		  com_usage[AL_NUM].irq,
		  com_usage[AL_NUM].has_irq);
		printf("poll=%d hcls=%d ohlt=%d\n",
		  com_usage[AL_NUM].poll,
		  com_usage[AL_NUM].hcls,
		  com_usage[AL_NUM].ohlt);
	}
}
#else
#define CDUMP(text, tp)
#define REPORT_OE
#define LSR_READ(lval, port)	{ lval = inb((port)+LSR); }
#endif


/*
 * set_poll_rate is called when a port is opened or closed or changes speed
 * it sets the polling rate only as fast as needed, and shuts off polling
 * whenever possible
 */

static void
set_poll_rate()
{
	int port_num, max_rate, port_rate;

	/*
	 * If another driver has the polling clock, do nothing.
	 */
	if (poll_owner & ~ POLL_AL)
		return;

	/*
	 * Find highest valid polling rate in units of HZ/10.
	 * If using FIFO chip, can poll at 1/16 the usual rate.
	 */
	max_rate = 0;
	for (port_num = 0; port_num < NUM_AL_PORTS; port_num++) {
		if (com_usage[port_num].poll) {
			port_rate = alp_rate[(tp_table[port_num])->t_sgttyb.sg_ispeed];
			if (com_usage[port_num].uart_type == US_16550A) {
				port_rate /= 16;
				if (port_rate % HZ)
					port_rate += HZ - (port_rate % HZ);
			}
			if (max_rate < port_rate)
				max_rate = port_rate;
		}
	}
	/*
	 * if max_rate is not current rate, adjust the system clock
	 */
	if (max_rate != poll_rate) {
		poll_rate = max_rate;
		poll_divisor = poll_rate/HZ;  /* used in alxclk() */
		altclk_out();		/* stop previous polling */
		poll_owner &= ~ POLL_AL;
		if (max_rate) {	/* resume polling at new rate if needed */
			poll_owner |= POLL_AL;
			altclk_in(poll_rate, alxclk);
		}
		CDUMP("new rate", 0)
	}
}


/*
 * alxopen()
 */

void
alxopen(dev, mode, tp, irqtty)
dev_t	dev;
int	mode;
TTY	*tp, **irqtty;
{
	int	s;
	int	b;
	int	minor_h;  /* minor device number including high bit */
	unsigned char	msr;

	minor_h = minor(dev);     /* complete minor number */
	b = ALPORT;

	if (com_usage [AL_NUM].uart_type == US_NONE) { /* chip not found */
		set_user_error (ENXIO);
		goto bad_open;
	}

	if ((tp->t_flags & T_EXCL) != 0 && ! super ()) {
		set_user_error (ENODEV);
		goto bad_open;
	}

	if (drvl [major (dev)].d_time) {	/* Modem settling */
		set_user_error (EBUSY);
		goto bad_open;
	}

	/*
	 * Can't open a polled port if another driver is using polling.
	 */

	if ((dev & CPOLL) != 0 && (poll_owner & ~ POLL_AL) != 0) {
		set_user_error (EBUSY);
		goto bad_open;
	}

	/*
	 * Can't have both com[13] or both com[24] IRQ at once.
	 */
	if ((dev & CPOLL) == 0 && com_usage [AL_NUM ^ 2].irq &&
	    com_usage [AL_NUM ^ 2].in_use) {
		set_user_error (EBUSY);
		goto bad_open;
	}

	/*
	 * If port already in use, are new and old open modes compatible?
	 */
	if (com_usage [AL_NUM].in_use) {
		int oldmode = 0, newmode = 0; /* mctl:1 poll:2 flow:4 */

		if (tp->t_flags & T_MODC)
			oldmode += 1;
		if (com_usage [AL_NUM].irq == 0)
			oldmode += 2;
		if (tp->t_flags & T_CFLOW)
			oldmode += 4;
		if ((minor_h & NMODC) == 0)
			newmode += 1;
		if (dev & CPOLL)
			newmode += 2;
		if (minor_h & CFLOW)
			newmode += 4;
		if (oldmode != newmode) {
			set_user_error (EBUSY);
			goto bad_open;
		}
	}

	/*
	 * Sleep here if another process is opening or closing the port.
	 * This can happen if:
	 *   another process is trying a first open and awaiting CD;
	 *   another process is closing the port after losing CD;
	 *   a remote process opened the port, spawned a daemon,
	 *     and disconnected, and the daemon ignored SIGHUP and is
	 *     improperly keeping the port open.
	 * Don't try to set tp->t_flags before this sleep!  During
	 *   the sleep, ttclose() may be called and clear the flags.
	 */
	while (com_usage [AL_NUM].in_use &&
	       (com_usage [AL_NUM].hcls ||
		((minor_h & NMODC) == 0 && (inb (b + MSR) & MS_RLSD) == 0))) {

		if (x_sleep ((char *) & tp->t_open, pritty, slpriSigCatch,
			     "alxopn1") == PROCESS_SIGNALLED) {
			set_user_error (EINTR);
			goto bad_open;
		}
	}

	/*
	 * If port already in use, are new and old open modes compatible?
	 * If not in use, mark it as such.
	 */
	if (com_usage [AL_NUM].in_use) {
		int oldmode = 0, newmode = 0; /* mctl:1 poll:2 flow:4 */

		if (tp->t_flags & T_MODC)
			oldmode += 1;
		if (com_usage[AL_NUM].irq == 0)
			oldmode += 2;
		if (tp->t_flags & T_CFLOW)
			oldmode += 4;
		if ((minor_h & NMODC) == 0)
			newmode += 1;
		if (dev & CPOLL)
			newmode += 2;
		if (minor_h & CFLOW)
			newmode += 4;
		if (oldmode != newmode) {
			set_user_error (EBUSY);
			goto bad_open;
		}
	} else {
		/*
		 * Save modes for this open attempt to avoid future conflicts.
		 * Then start alxcycle() for this port.
		 */
		if (dev & CPOLL)
			com_usage[AL_NUM].irq = 0;
		else
			com_usage[AL_NUM].irq = 1;
		if (minor_h & CFLOW)
			tp->t_flags |= T_CFLOW;
		else
			tp->t_flags &= ~T_CFLOW;
		if (minor_h & NMODC)
			tp->t_flags &= ~T_MODC;
		else
			tp->t_flags |= T_MODC;
	}
	com_usage[AL_NUM].in_use++;
	/*
	 * From here, error exit is bad_open_u.
	 */

	if (tp->t_open == 0) {        /* not already open */
		if (!(dev & CPOLL)) {
			*irqtty = tp_table[AL_NUM];
			com_usage[AL_NUM].has_irq = 1;
		}

		/*
		 * Need to start cycling to scan for CD.
		 */
		alxcycle(tp);

		s = sphi();
		/*
		 * Raise basic modem control lines even if modem
		 * control hasn't been specified.
		 * MC_OUT2 turns on NON-open-collector IRQ line from the UART.
		 * since we can't have two UART's on same IRQ with MC_OUT2 on
		 */
		if (dev & CPOLL) {
			outb(b+MCR, MC_RTS|MC_DTR);
		} else {
			outb(b+MCR, MC_RTS|MC_DTR|MC_OUT2);
			outb(b+IER, IENABLE);
		}

		if ((minor_h & NMODC) == 0) {	/* want modem control? */
			tp->t_flags |= T_HOPEN | T_STOP;
			for (;;) {	/* wait for carrier */
				msr = inb(b+MSR);
				/*
				 * If carrier detect present
				 *   if port not already open
				 *     break out of loop and finish first open
				 *   else
				 *     do second (or third, etc.) open
				 */
				if (msr & MS_RLSD)
					break;

				/* wait for carrier */
				if (x_sleep ((char *) & tp->t_open, pritty,
					     slpriSigCatch, "alxopn2")
				    == PROCESS_SIGNALLED) {
					outb (b + MCR, 0);
			    		outb (b + IER, 0);
					set_user_error (EINTR);
					tp->t_flags &= ~(T_HOPEN | T_STOP);
					spl(s);
					goto bad_open_u;
				}
			}

			/*
			 * Mark that we are no longer hanging in open.
			 * Allow output over the port unless hardware flow
			 * control says not to.
			 */
			tp->t_flags &= ~T_HOPEN;
			tp->t_flags &= ~T_STOP;
			if (!(tp->t_flags & T_CFLOW) || (msr & MS_CTS))
				com_usage[AL_NUM].ohlt = 0;
			else
				com_usage[AL_NUM].ohlt = 1;

			/*
			 * Awaken any other opens on same device.
			 */
			wakeup((char *)(&tp->t_open));
		}
		tp->t_flags |= T_CARR;
		ttopen(tp);				/* stty inits */

		/*
		 * Allow custom modification of defaults.
		 */
		tp->t_sgttyb.sg_flags |=  al_sg_set;
		tp->t_sgttyb.sg_flags &= ~al_sg_clr;

		alxparam (tp);
		spl(s);
	} /* end of first-open case */

	tp->t_open++;
	ttsetgrp(tp, dev, mode);

	/*
	 * Turn on polling for the port.
	 */
	if (dev & CPOLL) {
		com_usage[AL_NUM].poll = 1;
		set_poll_rate();
	}

	CDUMP((dev&CPOLL)?"open polled":"open irq", tp)
	return;

bad_open_u:
	--com_usage[AL_NUM].in_use;
	wakeup((char *)(&tp->t_open));
bad_open:
	;
}

/*
 * alxclose()
 *
 *	Called whenever kernel closes a com port.
 */

void
alxclose(dev, mode, tp)
dev_t	dev;
int	mode;
TTY	*tp;
{
	register int b;
	int maj;
	int flags;
	int s;
	unsigned char lsr;
	silo_t * out_silo = &com_usage[AL_NUM].raw_out;
	silo_t * in_silo = &com_usage[AL_NUM].raw_in;

	if (--tp->t_open)
		goto closed;
	s = sphi();

	/*
	 * Called at high priority by alclose after al_buff is drained
	 */
	com_usage[AL_NUM].hcls = 1;	/* disallow reopen til done closing */
	flags = tp->t_flags;		/* save flags - ttclose zeroes them */
	ttclose(tp);
	b = ALPORT;

	/*
	 * Wait for output silo and uart xmit buffer to empty.
	 * Allow signal to break the sleep.
	 */
	for (;;) {
		LSR_READ(lsr, b);
		if ((lsr & LS_TxIDLE)
		  && (out_silo->si_ix == out_silo->si_ox))
			break;
CDUMP("slp cls", tp)
		if (x_sleep ((char *) out_silo, pritty, slpriSigCatch,
			     "alxcls1") == PROCESS_SIGNALLED) {
			RAWOUT_FLUSH(out_silo);
			break;
		}
	}

	/*
	 * If not hanging in open
	 */
	if ((flags & T_HOPEN) == 0) {
		/*
		 * Disable interrupts.
		 */
		outb(b+IER, 0);
		outb(b+MCR, inb(b+MCR)&(~MC_OUT2));
	}

	/*
	 * If hupcls
	 */
	if (flags & T_HPCL) {
		/*
		 * Hangup port - drop DTR and RTS.
		 */
		outb(b+MCR, inb(b+MCR)&MC_OUT2);

		/*
		 * Hold dtr low for timeout
		 */
		maj = major(dev);
		drvl[maj].d_time = 1;
CDUMP("slp DTR", tp)
		x_sleep ((char *) & drvl [maj].d_time, pritty, slpriNoSig,
			 "alxcls2");
		drvl [maj].d_time = 0;
	}
	com_usage [AL_NUM].poll = 0;
	set_poll_rate ();
	RAWIN_FLUSH (in_silo);
	com_usage [AL_NUM].hcls = 0;	/* allow reopen - done closing */
	wakeup ((char *) & tp->t_open);
	spl (s);
closed:
	-- com_usage [AL_NUM].in_use;
	wakeup ((char *) & tp->t_open);
	CDUMP ("closed", tp)
}


/*
 * Common c_timer routine for async ports.
 */

void
alxtimer(dev)
dev_t dev;
{
	if (++ drvl [major (dev)].d_time > DTRTMOUT)
		wakeup ((char *) & drvl [major (dev)].d_time);
}


/*
 * Common c_ioctl routine for async ports.
 */

void
alxioctl(dev, com, vec, tp)
dev_t	dev;
int com;
struct sgttyb *vec;
TTY 	*tp;
{
	register int	s, b;
	int stat1, stat2;
	unsigned char	msr;
	unsigned char ier_save;
	silo_t * out_silo = &com_usage[AL_NUM].raw_out;
	silo_t * in_silo = &com_usage[AL_NUM].raw_in;

	s = sphi();
	b = ALPORT;
	ier_save=inb(b+IER);
	stat1 = inb(b+MCR);		/* get current MCR register status */
	stat2 = inb(b+LCR);		/* get current LCR register status */

	switch(com) {
	case TIOCSBRK:			/* set BREAK */
		outb(b+LCR, stat2|LC_SBRK);
		break;

	case TIOCCBRK:			/* clear BREAK */
		outb(b+LCR, stat2 & ~LC_SBRK);
		break;

	case TIOCSDTR:			/* set DTR */
		outb(b+MCR, stat1|MC_DTR);
		break;

	case TIOCCDTR:			/* clear DTR */
		outb(b+MCR, stat1 & ~MC_DTR);
		break;

	case TIOCSRTS:			/* set RTS */
		outb(b+MCR, stat1|MC_RTS);
		break;

	case TIOCCRTS:			/* clear RTS */
		outb(b+MCR, stat1 & ~MC_RTS);
		break;

	case TIOCRSPEED:		/* set "raw" I/O speed divisor */
		outb(b+LCR, stat2|LC_DLAB);  /* set speed latch bit */
		outb(b+DLL, (unsigned) vec);
		outb(b+DLH, (unsigned) vec >> 8);
		outb(b+LCR, stat2);       /* reset latch bit */
		break;

	case TIOCWORDL:		/* set word length and stop bits */
		outb(b+LCR, ((stat2&~0x7) | ((unsigned) vec & 0x7)));
		break;

	case TIOCRMSR:		/* get CTS/DSR/RI/RLSD (MSR) */
		msr = inb(b+MSR);
		stat1 = msr >> 4;
		kucopy(&stat1, (unsigned *) vec, sizeof(unsigned));
		break;

	case TIOCFLUSH:		/* Flush silos here, queues in tty.c */
		RAWIN_FLUSH(in_silo);
		RAWOUT_FLUSH(out_silo);
		/* fall through to default... */
	default:
		ttioctl(tp, com, vec);
	}
	outb(b+IER, ier_save);
	spl(s);
}

void
alxparam(tp)
TTY	*tp;
{
	register int	b;
	register int	baud;
	int s;
	char newlcr;
	int write_baud=1, write_lcr=1;
	int alnum;

	b = ALPORT;

	/*
	 * error if input speed not the same as output speed
	 */
	if (tp->t_sgttyb.sg_ispeed != tp->t_sgttyb.sg_ospeed) {
		set_user_error (ENODEV);
		return;
 	}

	if ((baud = albaud[tp->t_sgttyb.sg_ispeed]) == 0) {
		if (tp->t_flags & T_MODC) {  /* modem control? */
			s = sphi ();
			tp->t_flags &= ~ T_CARR;  /* indicate no carrier */
			outb (b + MCR, inb (b + MCR) & MC_OUT2); /* hangup */
			spl (s);
		}
		write_baud = 0;
	}

	switch (tp->t_sgttyb.sg_flags & (EVENP|ODDP|RAW)) {
	case ODDP:
		newlcr = LC_CS7|LC_PARENB;
		break;
	case EVENP:
		newlcr = LC_CS7|LC_PARENB|LC_PAREVEN;
		break;
	default:
		newlcr = LC_CS8;
		break;
	}

	alnum = AL_NUM;
	if (alnum >= 0 && alnum < 4) {
		if (baud == iocbaud[alnum]) {
			write_baud = 0;
			if (newlcr == ioclcr[alnum]) {
				write_lcr = 0;
			}
		}
		iocbaud[alnum] = baud;
		ioclcr[alnum] = newlcr;
	}

	if (write_lcr) {
		unsigned char ier_save;
		s=sphi();
		ier_save=inb(b+IER);
		if (write_baud) {
			outb(b+LCR, LC_DLAB);
			outb(b+DLL, baud);
			outb(b+DLH, baud >> 8);
		}
		outb(b+LCR, newlcr);
		if (com_usage[AL_NUM].uart_type == US_16550A)
			outb(b+FCR, FC_ENABLE | FC_Rx_RST | FC_Rx_08);
		outb(b+IER, ier_save);
		spl(s);
	}

	set_poll_rate();
}

/*
 * Middle level processor.
 *
 *	Invoked 10 times per second.  (Once every ten clock ticks.)
 *	Tranfers rawin buffer [from intr level] to canonical input queue.
 *	Checks modem status for loss of carrier.
 *	Transfers output queue to rawout buffer [for intr level].
 */

void
alxcycle(tp)
TTY * tp;
{
	register int b;
	register int n;
	unsigned char msr, mcr;
	int s;
	silo_t * out_silo = &com_usage[AL_NUM].raw_out;
	silo_t * in_silo = &com_usage[AL_NUM].raw_in;

	REPORT_OE;
	/*
	 * Check Carrier Detect (RLSD).
	 *
	 * Modem status interrupts were not enabled due to 8250 hardware bug.
	 * Enabling modem status and receive interrupts may cause lockup
	 * on older parts.
	 */
	if (tp->t_flags & T_MODC) {

		/*
		 * Get status
		 */
		msr = inb(ALPORT+MSR);

		/*
		 * Carrier changed.
		 */
		if ((msr & MS_RLSD) && !(tp->t_flags & T_CARR)) {
			/*
			 * Carrier is on - wakeup open.
			 */
			s = sphi();
			tp->t_flags |= T_CARR;
			spl(s);
			wakeup((char *)(&tp->t_open));
		}

		if (!(msr & MS_RLSD) && (tp->t_flags & T_CARR)) {
			s = sphi();
			RAWIN_FLUSH(in_silo);
			RAWOUT_FLUSH(out_silo);
			tp->t_flags &= ~T_CARR;
			spl(s);
			tthup(tp);
		}
	}

	/*
	 * Empty raw input buffer.
	 *
	 * The line discipline module (tty.c) will set T_ISTOP true when the
	 * tt input queue is nearly full (tp->t_iq.cq_cc >= IHILIM), and make
	 * T_ISTOP false when it's ready for more input.
	 *
	 * When T_ISTOP is true, ttin() simply discards the character passed.
	 */
#ifndef NOISILO
	if (!(tp->t_flags & T_ISTOP)) {
		while (in_silo->SILO_CHAR_COUNT > 0) {
			s = sphi();
			ttin(tp, in_silo->si_buf[in_silo->si_ox]);
			if (in_silo->si_ox < MAX_SILO_INDEX)
				in_silo->si_ox++;
			else
				in_silo->si_ox = 0;
			in_silo->SILO_CHAR_COUNT--;
			spl(s);
		}
	}
#endif

	/*
	 * Hardware flow control.
	 *	Check CTS to see if we need to halt output.
	 *	(MS_INTR should have done this - repeat code here to be sure)
	 *	Check input silo to see if we need to raise RTS.
	 */
	if (tp->t_flags & T_CFLOW) {

		/*
		 * Get status
		 */
		msr = inb(ALPORT+MSR);

		s = sphi();
		if (msr & MS_CTS)
			com_usage[AL_NUM].ohlt = 0;
		else
			com_usage[AL_NUM].ohlt = 1;
		spl(s);

#if	TRACER & TRACE_HAL
if(t_hal & 4) {static cts = 0;
if (!cts && (msr & MS_CTS)) {
	cts = 1;
	printf("[");
} else if (cts && !(msr & MS_CTS)) {
	cts = 0;
	printf("]");
}}
#endif

		/*
		 * If using hardware flow control, see if we need to drop RTS.
		 */
		if ( (tp->t_flags & T_CFLOW)
#ifdef NO_ISILO
		&& (tp->t_flags & T_ISTOP)) {
#else
		&& (in_silo->SILO_CHAR_COUNT > SILO_HIGH_MARK)) {
#endif
			s = sphi();
			mcr = inb(ALPORT+MCR);
			if (mcr & MC_RTS) {
				outb(ALPORT+MCR, mcr & ~MC_RTS);
#if	TRACER & TRACE_HAL
tprintf("-");
#endif
			}
			spl(s);
		}

		/*
		 * If input silo below low mark, assert RTS.
		 */
#ifdef NO_ISILO
		if ((tp->t_flags & T_ISTOP) == 0) {
#else
		if (in_silo->SILO_CHAR_COUNT <= SILO_LOW_MARK) {
#endif
			s = sphi();
			mcr = inb(ALPORT+MCR);
			if ((mcr & MC_RTS) == 0) {
				outb(ALPORT+MCR, mcr | MC_RTS);
#if	TRACER & TRACE_HAL
tprintf("+");
#endif
			}
			spl(s);
		}
	}

	/*
	 * Calculate free output slot count.
	 */
	n  = sizeof(out_silo->si_buf) - 1;
	n += out_silo->si_ox - out_silo->si_ix;
	n %= sizeof(out_silo->si_buf);

	/*
	 * Fill raw output buffer.
	 */
	for (;;) {
		if (--n < 0)
			break;
		s = sphi();
		b = ttout(tp);
		spl(s);
		if (b < 0)
			break;

		s = sphi();
		out_silo->si_buf[out_silo->si_ix] = b;
		if (out_silo->si_ix >= sizeof(out_silo->si_buf) - 1)
			out_silo->si_ix = 0;
		else
			out_silo->si_ix++;
		spl(s);
	}

	/*
	 * (Re)start output, wake sleeping processes, etc.
	 */
	ttstart(tp);

	/*
	 * Schedule next cycle.
	 */
	if (com_usage[AL_NUM].in_use)
		timeout(&tp->t_rawtim, HZ/10, alxcycle, tp);
}

/*
 * Serial Transmit Start Routine.
 */

void
alxstart(tp)
TTY * tp;
{
	int b;
	int s;
	int need_xmit = 1;	/* True if should start sending data now. */
	silo_t * out_silo = &com_usage[AL_NUM].raw_out;

	/*
	 * Read line status register AFTER disabling interrupts.
	 */
	s = sphi();
	LSR_READ(b, ALPORT);

	/*
	 * Process break indication.
	 * NOTE: Break indication cleared when line status register was read.
	 */
	if (b & LS_BREAK)
		defer (alxbreak, tp);

	/*
	 * If no output data, it may be time to finish closing the port;
	 * but won't need another xmit interrupt.
	 */
	if (out_silo->si_ix == out_silo->si_ox) {
		wakeup((char *)out_silo);
		need_xmit = 0;
	}

	/*
	 * Do nothing if output is stopped.
	 */
	if (tp->t_flags & T_STOP)
		need_xmit = 0;
	if (com_usage[AL_NUM].ohlt)
		need_xmit = 0;

	/*
	 * Start data transmission by writing to UART xmit reg.
	 */
	if ((b & LS_TxRDY) && need_xmit) {
		int xmit_count;

		xmit_count = (com_usage[AL_NUM].uart_type == US_16550A)?16:1;
		alx_send(out_silo, ALPORT+DREG, xmit_count);
	}

	spl(s);
}

/*
 * Serial Received Break Handler.
 */

void
alxbreak(tp)
TTY * tp;
{
	int s;
	silo_t * out_silo = &com_usage[AL_NUM].raw_out;
	silo_t * in_silo = &com_usage[AL_NUM].raw_in;

	s = sphi();
	RAWIN_FLUSH(in_silo);
	RAWOUT_FLUSH(out_silo);
	spl(s);
	ttsignal(tp, SIGINT);
}

/*
 * Serial Interrupt Handler.
 */

void
alxintr(tp)
TTY * tp;
{
	int c;
	register int port = ALPORT;
	unsigned char msr, lsr;
	int xmit_count;
	silo_t * in_silo = &com_usage[AL_NUM].raw_in;
	silo_t * out_silo = &com_usage[AL_NUM].raw_out;

	if (tp) {
rescan:
		switch (inb(port+IIR) & 0x07) {

		case LS_INTR:
			LSR_READ(lsr, port);
			if (lsr & LS_BREAK)
				defer(alxbreak, tp);
			goto rescan;

		case Rx_INTR:
			c = inb(port+DREG);
			if (tp->t_open == 0)
				goto rescan;
			/*
			 * Must recognize XOFF quickly to avoid transmit overrun.
			 * Recognize XON here as well to avoid race conditions.
			 */
			if (_IS_IXON_MODE (tp)) {
				/*
				 * XOFF.
				 */
				if (_IS_STOP_CHAR (tp, c)) {
					tp->t_flags |= T_STOP;
					goto rescan;
				}

				/*
				 * XON.
				 */
				if (_IS_START_CHAR (tp, c)) {
					tp->t_flags &= ~T_STOP;
					goto rescan;
				}
			}

			/*
			 * Save char in raw input buffer.
			 */
#ifdef NO_ISILO
			if (tp->t_flags & T_ISTOP) {
				/*
				 * If using hardware flow control, we need to drop RTS.
				 */
				if (tp->t_flags & T_CFLOW) {
					unsigned char mcr = inb(port+MCR);
					if (mcr & MC_RTS)
						outb(port+MCR, mcr & ~MC_RTS);
				}
			} else {
				ttin(tp, c);
			}
#else
			if (in_silo->SILO_CHAR_COUNT < MAX_SILO_CHARS) {
				in_silo->si_buf[in_silo->si_ix] = c;
				if (in_silo->si_ix < MAX_SILO_INDEX)
					in_silo->si_ix++;
				else
					in_silo->si_ix = 0;
				in_silo->SILO_CHAR_COUNT++;
			}

			/*
			 * If using hardware flow control, see if we need to drop RTS.
			 */
			if ( (tp->t_flags & T_CFLOW)
			&& (in_silo->SILO_CHAR_COUNT > SILO_HIGH_MARK)) {
				unsigned char mcr = inb(port+MCR);
				if (mcr & MC_RTS) {
					outb(port+MCR, mcr & ~MC_RTS);
				}
			}
#endif
			goto rescan;

		case Tx_INTR:
			/*
			 * Do nothing if output is stopped.
			 */
			if (tp->t_flags & T_STOP)
				goto rescan;
			if (com_usage[AL_NUM].ohlt)
				goto rescan;

			/*
			 * Transmit next char in raw output buffer.
			 */
			xmit_count =
			  (com_usage[AL_NUM].uart_type == US_16550A)?16:1;
			alx_send(out_silo, port+DREG, xmit_count);
			goto rescan;

		case MS_INTR:
			/*
			 * Get status (and clear interrupt).
			 */
			msr = inb(port+MSR);

			/*
			 * Hardware flow control.
			 *	Check CTS to see if we need to halt output.
			 */
			if (tp->t_flags & T_CFLOW) {
				if (msr & MS_CTS)
					com_usage[AL_NUM].ohlt = 0;
				else
					com_usage[AL_NUM].ohlt = 1;
			}

			goto rescan;
		} /* endswitch */
	} else {
		/*
		 * If tp is zero, an interrupt occurred before things
		 * are fully set up.  Just try to clear all pending
		 * interrupts from ANY serial ports.
		 */
		int com_num;
		for (com_num = 0; com_num < 4; com_num++) {
			port = AL_ADDR[com_num];
			inb(port+IIR);
			inb(port+LSR);
			inb(port+MSR);
			inb(port+DREG);
		}
	}
}

/*
 * alxclk will be called every time T0 interrupts - if it returns 0,
 * the usual system timer interrupt stuff is done
 */
static int alxclk()
{
	static int count;
	int i;

	for (i = 0; i < NUM_AL_PORTS;  i++)
		if (com_usage[i].poll)
			alxpoll(tp_table[i]);
	count++;
	if (count >= poll_divisor)
		count = 0;
	return count;
}

/*
 * alxpoll()
 *
 * Serial polling handler.  Compare to alxintr().
 */

static void
alxpoll(tp)
register TTY * tp;
{
	int c;
	int port = ALPORT;
	int xmit_count;
	unsigned char lsr;
	silo_t * in_silo = &com_usage[AL_NUM].raw_in;
	silo_t * out_silo = &com_usage[AL_NUM].raw_out;

	/*
	 * Check for received break first.
	 * This status is wiped out on reading the LSR.
	 */
	LSR_READ(lsr, port);
	if (lsr & LS_BREAK)
		defer(alxbreak, tp);

	/*
	 * Handle all incoming characters.
	 */
	for (;;) {
		LSR_READ(lsr, port);
		if ((lsr & LS_RxRDY) == 0)
			break;
		c = inb(port+DREG);
		if (tp->t_open == 0)
			continue;
		/*
		 * Must recognize XOFF quickly to avoid transmit overrun.
		 * Recognize XON here as well to avoid race conditions.
		 */
		if (_IS_IXON_MODE (tp)) {
			/*
			 * XOFF.
			 */
			if (_IS_STOP_CHAR (tp, c)) {
				tp->t_flags |= T_STOP;
				continue;
			}

			/*
			 * XON.
			 */
			if (_IS_START_CHAR (tp, c)) {
				tp->t_flags &= ~T_STOP;
				continue;
			}
		}

		/*
		 * Save char in raw input buffer.
		 */
#ifdef NO_ISILO
		if (tp->t_flags & T_ISTOP) {
			/*
			 * If using hardware flow control, we need to drop RTS.
			 */
			if (tp->t_flags & T_CFLOW) {
				unsigned char mcr = inb(port+MCR);
				if (mcr & MC_RTS)
					outb(port+MCR, mcr & ~MC_RTS);
			}
		} else {
			ttin(tp, c);
		}
#else
		if (in_silo->SILO_CHAR_COUNT < MAX_SILO_CHARS) {
			in_silo->si_buf[in_silo->si_ix] = c;
			if (in_silo->si_ix < MAX_SILO_INDEX)
				in_silo->si_ix++;
			else
				in_silo->si_ix = 0;
			in_silo->SILO_CHAR_COUNT++;
		}

		/*
		 * If using hardware flow control, see if we need to drop RTS.
		 */
		if ( (tp->t_flags & T_CFLOW)
		  && (in_silo->SILO_CHAR_COUNT > SILO_HIGH_MARK)) {
			unsigned char mcr = inb(port+MCR);
			if (mcr & MC_RTS) {
				outb(port+MCR, mcr & ~MC_RTS);
			}
		}
#endif
	}

	/*
	 * Handle outgoing characters.
	 * Do nothing if output is stopped.
	 */
	LSR_READ(lsr, port);
	if ((lsr & LS_TxRDY)
	  && !(tp->t_flags & T_STOP)
	  && !(com_usage[AL_NUM].ohlt)) {
		/*
		 * Transmit next char in raw output buffer.
		 */
		xmit_count = (com_usage[AL_NUM].uart_type == US_16550A)?16:1;
		alx_send(out_silo, port+DREG, xmit_count);
	}

	/*
	 * Hardware flow control.
	 *	Check CTS to see if we need to halt output.
	 */
	if (tp->t_flags & T_CFLOW) {
		if (inb(port+MSR) & MS_CTS)
			com_usage[AL_NUM].ohlt = 0;
		else
			com_usage[AL_NUM].ohlt = 1;
	}
}

/*
 * alx_send()
 *
 * Write to xmit data register of the UART.
 * Assume all checking about whether it's time to send has been done already.
 * Called by time-critical IRQ and polling routines!
 *
 * "rawout" is the output silo for the TTY struct supplying data to the port.
 * "dreg" is the i/o address of the UART xmit data register.
 * "xmit_count" is the max number of chars we can write (16 for FIFO parts).
 */

static void
alx_send(rawout, dreg, xmit_count)
register silo_t * rawout;
int dreg, xmit_count;
{
	/*
	 * Transmit next chars in raw output buffer.
	 */
	for (;(rawout->si_ix != rawout->si_ox) && xmit_count; xmit_count--) {
		outb(dreg, rawout->si_buf[rawout->si_ox]);
		/*
		 * Adjust raw output buffer output index.
		 */
		if (++rawout->si_ox >= sizeof(rawout->si_buf))
			rawout->si_ox = 0;
	}
}