From: gemini@geminix.in-berlin.de (Uwe Doering)
Subject: FAS 2.10 async driver, part 3/5
Organization: Private UNIX Site
Date: Mon, 17 Aug 1992 11:38:06 GMT

Submitted-by: gemini@geminix.in-berlin.de
Archive-name: fas210/part03

#!/bin/sh
# this is fas210.03 (part 3 of fas210)
# do not concatenate these parts, unpack them in order with /bin/sh
# file config-ast4c12 continued
#
if test ! -r _shar_seq_.tmp; then
	echo 'Please unpack part 1 first!'
	exit 1
fi
(read Scheck
 if test "$Scheck" != 3; then
	echo Please unpack part "$Scheck" next!
	exit 1
 else
	exit 0
 fi
) < _shar_seq_.tmp || exit 1
if test ! -f _shar_wnt_.tmp; then
	echo 'x - still skipping config-ast4c12'
else
echo 'x - continuing file config-ast4c12'
sed 's/^X//' << 'SHAR_EOF' >> 'config-ast4c12' &&
Xcharacter(4)
X
X* its name
Xprefix = fas
X
X* The interrupt vectors handled by this controller
Xintvec = 9,4,3
X
X* its mask level
Xintpri = SPLTTY
X
X* the functions it supports
Xfunctions = init, open, close, read, write, ioctl, tty
SHAR_EOF
echo 'File config-ast4c12 is complete' &&
true || echo 'restore of config-ast4c12 failed'
rm -f _shar_wnt_.tmp
fi
# ============= config-c12 ==============
if test -f 'config-c12' -a X"$1" != X"-c"; then
	echo 'x - skipping config-c12 (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting config-c12 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'config-c12' &&
X* its character device number 4
Xcharacter(4)
X
X* its name
Xprefix = fas
X
X* The interrupt vectors handled by this controller
Xintvec = 4,3
X
X* its mask level
Xintpri = SPLTTY
X
X* the functions it supports
Xfunctions = init, open, close, read, write, ioctl, tty
SHAR_EOF
true || echo 'restore of config-c12 failed'
rm -f _shar_wnt_.tmp
fi
# ============= config-c123 ==============
if test -f 'config-c123' -a X"$1" != X"-c"; then
	echo 'x - skipping config-c123 (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting config-c123 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'config-c123' &&
X* its character device number 4
Xcharacter(4)
X
X* its name
Xprefix = fas
X
X* The interrupt vectors handled by this controller
Xintvec = 4,3,9
X
X* its mask level
Xintpri = SPLTTY
X
X* the functions it supports
Xfunctions = init, open, close, read, write, ioctl, tty
SHAR_EOF
true || echo 'restore of config-c123 failed'
rm -f _shar_wnt_.tmp
fi
# ============= config-digi8c1 ==============
if test -f 'config-digi8c1' -a X"$1" != X"-c"; then
	echo 'x - skipping config-digi8c1 (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting config-digi8c1 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'config-digi8c1' &&
X* its character device number 4
Xcharacter(4)
X
X* its name
Xprefix = fas
X
X* The interrupt vectors handled by this controller
Xintvec = 3,4
X
X* its mask level
Xintpri = SPLTTY
X
X* the functions it supports
Xfunctions = init, open, close, read, write, ioctl, tty
SHAR_EOF
true || echo 'restore of config-digi8c1 failed'
rm -f _shar_wnt_.tmp
fi
# ============= config-hub6 ==============
if test -f 'config-hub6' -a X"$1" != X"-c"; then
	echo 'x - skipping config-hub6 (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting config-hub6 (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'config-hub6' &&
X* its character device number 4
Xcharacter(4)
X
X* its name
Xprefix = fas
X
X* The interrupt vectors handled by this controller
Xintvec = 3
X
X* its mask level
Xintpri = SPLTTY
X
X* the functions it supports
Xfunctions = init, open, close, read, write, ioctl, tty
SHAR_EOF
true || echo 'restore of config-hub6 failed'
rm -f _shar_wnt_.tmp
fi
# ============= fas.c ==============
if test -f 'fas.c' -a X"$1" != X"-c"; then
	echo 'x - skipping fas.c (File already exists)'
	rm -f _shar_wnt_.tmp
else
> _shar_wnt_.tmp
echo 'x - extracting fas.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'fas.c' &&
X/* FAS Final Async Solution driver for 286/386 versions of system V UNIX */
X
X/* FAS was developed by
XUwe Doering             INET : gemini@geminix.in-berlin.de
XBillstedter Pfad 17 b   UUCP : ...!unido!fub!geminix.in-berlin.de!gemini
X1000 Berlin 20
XGermany
X*/
X
X#if !defined (M_I286)
X#ident	"@(#)fas.c	2.10"
X#endif
X
X/* Note: This source code has been quite heavily optimized for speed.
X         You may wonder that register variables aren't used everywhere.
X         This is because there is an overhead in memory accesses
X         when using register variables. As you may know data accesses
X         usually need much more wait states on the memory bus than
X         code accesses (because of page or cache misses). Therefore,
X         saving some data accesses has higher priority than saving
X         code accesses.
X
X         You may also note some not very elegant constructions that
X         may be intentional because they are faster. If you want to
X         make style improvements you should check the assembler output
X         whether this wouldn't slow things down.
X
X         Decisions for speed optimization were based on assembler
X         listings produced by the standard UNIX V [34].X/386 C compiler.
X*/
X
X#if defined (XENIX)
X
X#include "fas.h"
X#if defined (__GNUC__) && !defined (NO_ASM)
X#define outb(port,val) \
X({\
X	__asm__ volatile ("outb %1,%0" : : "d" ((ushort) (port)), "a" ((unchar) (val)));\
X})
X
X#define inb(port) \
X({\
X	unchar __val;\
X	__asm__ volatile ("inb %1,%0" : "=a" (__val) : "d" ((ushort) (port)));\
X	__val;\
X})
X#endif	/* __GNUC__ && !NO_ASM */
X#define REGVAR
X
X#else	/* XENIX */
X
X#include <sys/fas.h>
X#if defined (__GNUC__)
X#if !defined (NO_ASM)
X#if defined (SCO)
X#define outb(port,val) \
X({\
X	__asm__ volatile ("outb %1,%0" : : "d" ((ushort) (port)), "a" ((unchar) (val)));\
X})
X
X#define inb(port) \
X({\
X	unchar __val;\
X	__asm__ volatile ("inb %1,%0" : "=a" (__val) : "d" ((ushort) (port)));\
X	__val;\
X})
X#else	/* SCO */
X#define outb(port,val) \
X({\
X	__asm__ volatile ("outb (%0)" : : "d" ((ushort) (port)), "a" ((unchar) (val)));\
X})
X
X#define inb(port) \
X({\
X	unchar __val;\
X	__asm__ volatile ("inb (%1)" : "=a" (__val) : "d" ((ushort) (port)));\
X	__val;\
X})
X#endif	/* SCO */
X#endif	/* !NO_ASM */
X
X#define REGVAR
X#else	/* __GNUC__ */
X#if !defined (NO_ASM)
X#include <sys/inline.h>
X
X/* This is a terrible ugly kludge to speed up the `inb' and `outb'
X   functions. I.e., originally, the `outb' inline function had an
X   overhead of four data memory accesses for parameter passing. This
X   parameter passing actually consumed more clock cycles than the
X   assembler `outb' command itself. Although this solution can't
X   prevent unnessessary register moves it limits them at least to
X   register to register moves that are much faster. You need a
X   line like the following in the declaration part of every
X   function that uses `inb' or `outb' calls:
X
X	REGVAR;
X
X   This hack should work with every compiler that knows about the
X   UNIX V [34].X/386 standard compiler's inline assembler directives.
X*/
X
Xasm	void loadal (val)
X{
X%reg	val;
X	movl	val,%eax
X%mem	val;
X	movb	val,%al
X}
X
Xasm	void loaddx (val)
X{
X%reg	val;
X	movl	val,%edx
X%mem	val;
X	movw	val,%dx
X}
X
Xasm	int outbyte ()
X{
X	outb	(%dx)
X}
X
Xasm	int inbyte ()
X{
X	xorl	%eax,%eax
X	inb	(%dx)
X}
X
X/* The port parameter of the `outb' macro must be one of the predefined
X   port macros from `fas.h' or a simple uint variable (no indirection
X   is allowed). Additionally, `fip' must be a register variable in the
X   functions where `outb' is used. This prevents the destruction of the
X   `eax' CPU register while loading the `edx' register with the port
X   address. This is highly compiler implementation specific.
X*/
X#define outb(port,val) (regvar = (val), loadal (regvar), regvar = (port), loaddx (regvar), outbyte ())
X
X#define inb(port) (regvar = (port), loaddx (regvar), inbyte ())
X
X#define REGVAR register uint	regvar
X
X/* This function inserts the address optimization assembler pseudo-op
X   wherever called.
X*/
X
Xasm	void optim ()
X{
X	.optim
X}
X
X/* This dummy function has nothing to do but to call optim so that
X   the `.optim' assembler pseudo-op will be included in the assembler
X   file. This must be the first of all functions.
X*/
X
X#if defined (OPTIM)	/* Define for uPort, ISC doesn't know about */
Xstatic void		/* `.optim', but has turned on optimization by */
Xdummy ()		/* default, so we don't need it there anyway. */
X{
X	optim ();
X}
X#endif	/* OPTIM */
X#else	/* !NO_ASM */
X#define REGVAR
X#endif	/* !NO_ASM */
X#endif	/* __GNUC__ */
X
X#endif	/* XENIX */
X
X#if defined (SCO) || defined (XENIX)
X#define asyputchar sioputchar
X#define asygetchar siogetchar
X#endif
X
X/* functions provided by this driver */
Xint		fasinit ();
Xint		fasopen ();
Xint		fasclose ();
Xint		fasread ();
Xint		faswrite ();
Xint		fasioctl ();
Xint		fasintr ();
X#if defined (NEED_PUT_GETCHAR)
Xint		asyputchar ();
Xint		asygetchar ();
X#endif
X#if defined (NEED_INIT8250)
Xint		init8250 ();
X#endif
Xstatic int	fas_proc ();
Xstatic void	fas_param ();
Xstatic void	fas_mproc ();
Xstatic void	fas_rproc ();
Xstatic void	fas_xproc ();
Xstatic void	fas_event ();
X#if defined (HAVE_VPIX)
Xstatic bool	fas_vpix_sr ();
X#endif
Xstatic void	fas_msi_disable ();
Xstatic void	fas_msi_enable ();
Xstatic void	fas_rdi_disable ();
Xstatic void	fas_rdi_enable ();
Xstatic bool	fas_rxfer ();
Xstatic void	fas_xxfer ();
Xstatic void	fas_hwi_start ();
Xstatic void	fas_hwi_stop ();
Xstatic void	fas_send_xon ();
Xstatic void	fas_send_xoff ();
Xstatic void	fas_hdx_start ();
Xstatic void	fas_hangup ();
Xstatic void	fas_timeout ();
Xstatic void	fas_cmd ();
Xstatic void	fas_open_device ();
Xstatic void	fas_close_device ();
Xstatic void	fas_pos_by_speed ();
Xstatic uint	fas_make_ctl_val ();
Xstatic uint	fas_test_device ();
X
X/* external functions used by this driver */
Xextern int	ttinit ();
Xextern int	ttiocom ();
Xextern int	ttyflush ();
Xextern int	SPLINT ();
Xextern int	SPLWRK ();
Xextern int	splx ();
Xextern int	sleep ();
Xextern int	wakeup ();
Xextern void	longjmp ();
X#if !defined (SVR4)
Xextern int	signal ();
X#endif
Xextern int	timeout ();
Xextern int	untimeout ();
X#if defined (SCO) || defined (XENIX)
Xextern int	printcfg ();
X#else
Xextern int	printf ();
X#endif
X#if defined (HAVE_VPIX)
Xextern int	fubyte ();
Xextern int	subyte ();
Xextern int	v86setint ();
X#endif
X#if defined (NO_ASM) || (defined (XENIX) && !defined (__GNUC__))
Xextern int	inb ();
Xextern int	outb ();
X#endif
X
X/* external data objects used by this driver */
Xextern int	tthiwat [];
Xextern int	ttlowat [];
Xextern int	ttyhog;
X
X/* the following stuff is defined in space.c */
Xextern uint	fas_physical_units;
Xextern ulong	fas_port [];
Xextern uint	fas_init_seq [];
Xextern uint	fas_int_ack_seq [];
Xextern uint	fas_mcb [];
Xextern ulong	fas_modem [];
Xextern ulong	fas_flow [];
Xextern uint	fas_ctl_port [];
Xextern uint	fas_ctl_val [];
Xextern struct fas_internals	fas_internals [];
Xextern struct tty		fas_tty [];
Xextern struct fas_internals	*fas_internals_ptr [];
Xextern struct tty		*fas_tty_ptr [];
X/* end of space.c references */
X
X/* fas_is_initted
X   Flag to indicate that we have been thru init.
X   This is realy only necessary for systems that use asyputchar
X   and asygetchar but it doesn't hurt to have it anyway.
X*/
Xstatic bool	fas_is_initted = FALSE;
X
X/* event_scheduled
X   Flag to indicate that the event handler has been scheduled
X   via the timeout () function.
X*/
Xstatic bool	event_scheduled = FALSE;
X
X/* pointers to the first and last fas_internals structure of the
X   interrupt users chain
X*/
Xstatic struct fas_internals	*fas_first_int_user, *fas_last_int_user;
X
X/* thresholds for the character transfer to the CLIST buffers */
Xstatic uint	max_unix_fill;
X#if defined (HAVE_VPIX)
Xstatic uint	max_vpix_fill;
X#endif
Xstatic uint	min_read_chunk;
X
X/* counters for receiver overruns, each UART type has its own counter
X   indexed by the device type
X*/
Xuint	fas_overrun [NUMBER_OF_TYPES];
X
X/* counter for temporarily disabled modem status interrupts due
X   to noise on the modem status lines
X*/
Xuint	fas_msi_noise;
X
X/* UNIX to FAS mapping of baud rates (normal speed) */
Xstatic n_ushort	fas_baud [CBAUD + 1] =
X{	B38400,		B50,
X	B75,		B110,
X	B134,		B150,
X	B200,		B300,
X	B600,		B1200,
X	B1800,		B2400,
X	B4800,		B9600,
X	B19200,		B38400
X};
X
X/* UNIX to FAS mapping of baud rates (high speed) */
Xstatic n_ushort	fas_hbaud [CBAUD + 1] =
X{	B0,		B50,
X	B75,		B110,
X	B134,		B150,
X	B200,		B300,
X	B600,		B1200,
X	B1800,		B2400,
X	B4800,		B9600,
X	B19200,		BHIGHSPEED
X};
X
X/* Note: The baud rate in fas_hbaud that is overridden by BHIGHSPEED
X         has to be entered at fas_baud [0] !
X
X   Note also: If you want to use a speed different from B38400 for
X              BHIGHSPEED, you have to make sure that the tthiwat [] and
X              ttlowat [] values for that baud rate are high enough
X              to allow the CLIST function to keep the FAS transmit
X              buffer filled. Look at fasinit () on how to do that.
X*/
X
X/* the divisor values for the various baud rates */
X/* 57600 bps version */
Xstatic n_ushort	fas_speeds1 [CBAUD + 1] =
X{	BAUD_BASE/57600,	BAUD_BASE/50,
X	BAUD_BASE/75,		BAUD_BASE/110,
X	(2*BAUD_BASE+134)/269,	BAUD_BASE/150,
X	BAUD_BASE/200,		BAUD_BASE/300,
X	BAUD_BASE/600,		BAUD_BASE/1200,
X	BAUD_BASE/1800,		BAUD_BASE/2400,
X	BAUD_BASE/4800,		BAUD_BASE/9600,
X	BAUD_BASE/19200,	BAUD_BASE/38400
X};
X
X/* 115200 bps version */
Xstatic n_ushort	fas_speeds2 [CBAUD + 1] =
X{	BAUD_BASE/115200,	BAUD_BASE/50,
X	BAUD_BASE/75,		BAUD_BASE/110,
X	(2*BAUD_BASE+134)/269,	BAUD_BASE/150,
X	BAUD_BASE/200,		BAUD_BASE/300,
X	BAUD_BASE/600,		BAUD_BASE/1200,
X	BAUD_BASE/1800,		BAUD_BASE/2400,
X	BAUD_BASE/4800,		BAUD_BASE/9600,
X	BAUD_BASE/19200,	BAUD_BASE/38400
X};
X
X/* time for one character to completely leave the transmitter shift register */
X/* 57600 bps version */
Xstatic n_ushort	fas_ctimes1 [CBAUD + 1] =
X{	HZ*15/57600+2,	HZ*15/50+2,
X	HZ*15/75+2,	HZ*15/110+2,
X	HZ*30/269+2,	HZ*15/150+2,
X	HZ*15/200+2,	HZ*15/300+2,
X	HZ*15/600+2,	HZ*15/1200+2,
X	HZ*15/1800+2,	HZ*15/2400+2,
X	HZ*15/4800+2,	HZ*15/9600+2,
X	HZ*15/19200+2,	HZ*15/38400+2
X};
X
X/* time for one character to completely leave the transmitter shift register */
X/* 115200 bps version */
Xstatic n_ushort	fas_ctimes2 [CBAUD + 1] =
X{	HZ*15/115200+2,	HZ*15/50+2,
X	HZ*15/75+2,	HZ*15/110+2,
X	HZ*30/269+2,	HZ*15/150+2,
X	HZ*15/200+2,	HZ*15/300+2,
X	HZ*15/600+2,	HZ*15/1200+2,
X	HZ*15/1800+2,	HZ*15/2400+2,
X	HZ*15/4800+2,	HZ*15/9600+2,
X	HZ*15/19200+2,	HZ*15/38400+2
X};
X
X/* dynamically adapt xmit buffer size to baud rate to prevent long buffer
X   drains at low speeds
X   These values are checked against boundaries and will be modified if
X   necessary before use. Checking is done in fas_param (). Drain time
X   is about 2 seconds with continuous character flow.
X*/
X/* 57600 bps version */
Xstatic n_ushort	fas_xbuf_size1 [CBAUD + 1] =
X{	57600/5,	50/5,
X	75/5,		110/5,
X	269/10,		150/5,
X	200/5,		300/5,
X	600/5,		1200/5,
X	1800/5,		2400/5,
X	4800/5,		9600/5,
X	19200/5,	38400/5
X};
X
X/* 115200 bps version */
Xstatic n_ushort	fas_xbuf_size2 [CBAUD + 1] =
X{	115200/5,	50/5,
X	75/5,		110/5,
X	269/10,		150/5,
X	200/5,		300/5,
X	600/5,		1200/5,
X	1800/5,		2400/5,
X	4800/5,		9600/5,
X	19200/5,	38400/5
X};
X
X/* lookup table for minor device number -> open mode flags translation */
Xstatic n_ushort	fas_open_modes [16] =
X{
X	OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL,
X	OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL | OS_HWO_HANDSHAKE
X							| OS_HWI_HANDSHAKE,
X	OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL | OS_HWO_HANDSHAKE,
X	OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL | OS_HWO_HANDSHAKE
X							| OS_HDX_HANDSHAKE,
X	OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON,
X	OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_HWO_HANDSHAKE
X						| OS_HWI_HANDSHAKE,
X	OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_HWO_HANDSHAKE,
X	OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_HWO_HANDSHAKE
X						| OS_HDX_HANDSHAKE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_HWO_HANDSHAKE
X							| OS_HWI_HANDSHAKE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_HWO_HANDSHAKE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_HWO_HANDSHAKE
X							| OS_HDX_HANDSHAKE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_HWO_HANDSHAKE
X						| OS_HWI_HANDSHAKE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_HWO_HANDSHAKE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_HWO_HANDSHAKE
X						| OS_HDX_HANDSHAKE
X};
X
X/* The following defines are used to access multiplexed ports. */
X#define GET_PORT(port,num) \
X	((fip->device_flags.i & DF_CTL_EVERY)\
X			? (port)\
X			: (port) + (num))
X
X#define fas_first_ctl(fip,port) \
X	((void) (((fip)->device_flags.i & DF_CTL_FIRST)\
X			? outb (CTL_PORT, (port).p.ctl)\
X			: 0))
X
X#define fas_every_ctl(fip,port) \
X	((void) (((fip)->device_flags.i & DF_CTL_EVERY)\
X			? outb (CTL_PORT, (port).p.ctl)\
X			: 0))
X
X#define fas_ctl(fip,port) \
X	((void) (((fip)->device_flags.i & (DF_CTL_FIRST | DF_CTL_EVERY))\
X			? outb (CTL_PORT, (port).p.ctl)\
X			: 0))
X
X#define fas_first_outb(fip,port,val) \
X	((void) (((fip)->device_flags.i & (DF_CTL_FIRST | DF_CTL_EVERY))\
X			? outb (CTL_PORT, (port).p.ctl)\
X			: 0),\
X		(void) outb ((port).addr, (val)))
X
X#define fas_outb(fip,port,val) \
X	((void) (((fip)->device_flags.i & DF_CTL_EVERY)\
X			? outb (CTL_PORT, (port).p.ctl)\
X			: 0),\
X		(void) outb ((port).addr, (val)))
X
X#define fas_same_outb(fip,port,val) \
X	((void) outb ((port).addr, (val)))
X
X#define fas_first_inb(fip,port) \
X	((void) (((fip)->device_flags.i & (DF_CTL_FIRST | DF_CTL_EVERY))\
X			? outb (CTL_PORT, (port).p.ctl)\
X			: 0),\
X		inb ((port).addr))
X
X#define fas_inb(fip,port) \
X	((void) (((fip)->device_flags.i & DF_CTL_EVERY)\
X			? outb (CTL_PORT, (port).p.ctl)\
X			: 0),\
X		inb ((port).addr))
X
X#define fas_same_inb(fip,port) \
X	(inb ((port).addr))
X
X/* The following defines are used to take apart the minor device numbers. */
X#define GET_UNIT(dev)		((dev) & 0x0f)
X#define GET_OPEN_MODE(dev)	(fas_open_modes [((dev) >> 4) & 0x0f])
X
X/* lock device against concurrent use */
X#define get_device_lock(fip,prio) \
X{\
X	/* sleep while device is used by an other process */\
X	while ((fip)->device_flags.i & DF_DEVICE_LOCKED)\
X		(void) sleep ((caddr_t) &(fip)->device_flags.i, (prio));\
X	(fip)->device_flags.s |= DF_DEVICE_LOCKED;\
X}
X
X/* release device */
X#define release_device_lock(fip) \
X{\
X	(fip)->device_flags.s &= ~DF_DEVICE_LOCKED;\
X	/* wakeup the process that may wait for this device */\
X	(void) wakeup ((caddr_t) &(fip)->device_flags.i);\
X}
X
X/* schedule event */
X#define event_sched(fip,event) \
X{\
X	(fip)->event_flags.s |= (event);\
X	if (!event_scheduled)\
X	{\
X		event_scheduled = TRUE;\
X		(void) timeout (fas_event, (caddr_t) NULL, 1);\
X	}\
X}
X
X/* fasinit
X   This routine checks for the presense of the devices in the fas_port
X   array and if the device is present tests and initializes it.
X   During the initialization the device type is automatically determined
X   and the UART is handled according to its requirements.
X*/
X
Xint
Xfasinit ()
X{
X	register struct fas_internals	*fip;
X	register uint	unit;
X	uint	logical_units, port, *seq_ptr;
X	uint	baud_rate;
X	int	max_tthiwat, max_ttlowat;
X	char	port_stat [MAX_UNITS + 1];
X	REGVAR;
X
X	if (fas_is_initted)
X		return (0);
X
X	fas_is_initted = TRUE;
X
X	/* kludge to make sure that the tty buffer high water levels
X	   for fast baud rates are high enough to ensure max. throughput
X	*/
X	for (baud_rate = B50, max_tthiwat = 0; baud_rate <= B38400; ++baud_rate)
X	{
X		if (tthiwat [baud_rate] >= max_tthiwat)
X		{
X			max_tthiwat = tthiwat [baud_rate];
X			max_ttlowat = ttlowat [baud_rate];
X		}
X		else
X		{
X			tthiwat [baud_rate] = max_tthiwat;
X			ttlowat [baud_rate] = max_ttlowat;
X		}
X	}
X
X	/* initialize thresholds for the character transfer to
X	   the CLIST buffers
X	*/
X	max_unix_fill = ttyhog - 1;
X	min_read_chunk = (MIN_READ_CHUNK * 2 > max_unix_fill)
X				? max_unix_fill / 2
X				: MIN_READ_CHUNK;
X#if defined (HAVE_VPIX)
X	max_vpix_fill = (MAX_VPIX_FILL > max_unix_fill)
X				? max_unix_fill
X				: MAX_VPIX_FILL;
X	if (min_read_chunk * 2 > max_vpix_fill)
X		min_read_chunk = max_vpix_fill / 2;
X#endif
X
X	/* execute the init sequence for the serial cards */
X	for (seq_ptr = &fas_init_seq [0]; *seq_ptr; ++seq_ptr)
X	{
X		port = *seq_ptr;
X		++seq_ptr;
X		if (*seq_ptr & READ_PORT)
X			(void) inb (port);
X		else
X			(void) outb (port, *seq_ptr);
X	}
X
X	/* setup the list of pointers to the tty structures */
X	for (unit = 0, logical_units = fas_physical_units * 2;
X		unit < logical_units; ++unit)
X		fas_tty_ptr [unit] = &fas_tty [unit];
X
X	/* setup and initialize all serial ports */
X	for (unit = 0; unit < fas_physical_units; ++unit)
X	{
X		fas_internals_ptr [unit] = fip = &fas_internals [unit];
X		port_stat [unit] = '-';
X		if (port = (uint) ((ushort) (fas_port [unit])))
X		{
X			/* init all of its ports */
X			if (fas_ctl_port [unit])
X			{
X				fip->ctl_port = fas_ctl_port [unit];
X
X				if (fas_ctl_val [unit] & 0xff00)
X					fip->device_flags.s |= DF_CTL_EVERY;
X				else
X					fip->device_flags.s |= DF_CTL_FIRST;
X			}
X
X			fip->port_0.p.addr = GET_PORT (port, 0);
X			fip->port_1.p.addr = GET_PORT (port, 1);
X			fip->port_2.p.addr = GET_PORT (port, 2);
X			fip->port_3.p.addr = GET_PORT (port, 3);
X			fip->port_4.p.addr = GET_PORT (port, 4);
X			fip->port_5.p.addr = GET_PORT (port, 5);
X			fip->port_6.p.addr = GET_PORT (port, 6);
X			fip->port_0.p.ctl = fas_make_ctl_val (fip, unit, 0);
X			fip->port_1.p.ctl = fas_make_ctl_val (fip, unit, 1);
X			fip->port_2.p.ctl = fas_make_ctl_val (fip, unit, 2);
X			fip->port_3.p.ctl = fas_make_ctl_val (fip, unit, 3);
X			fip->port_4.p.ctl = fas_make_ctl_val (fip, unit, 4);
X			fip->port_5.p.ctl = fas_make_ctl_val (fip, unit, 5);
X			fip->port_6.p.ctl = fas_make_ctl_val (fip, unit, 6);
X			fip->modem.l = fas_modem [unit];
X			fip->flow.l = fas_flow [unit];
X			fip->po_state = fip->o_state = OS_DEVICE_CLOSED;
X
X			/* mask off invalid bits */
X			fip->modem.m.di &= MC_ANY_CONTROL;
X			fip->modem.m.eo &= MC_ANY_CONTROL;
X			fip->modem.m.ei &= MC_ANY_CONTROL;
X			fip->modem.m.ca &= MS_ANY_PRESENT;
X			fip->flow.m.ic &= MC_ANY_CONTROL;
X			fip->flow.m.oc &= MS_ANY_PRESENT;
X			fip->flow.m.oe &= MS_ANY_PRESENT;
X			fip->flow.m.hc &= MC_ANY_CONTROL;
X
X			fip->recv_ring_put_ptr = &fip->recv_buffer [0];
X			fip->recv_ring_take_ptr = &fip->recv_buffer [0];
X			fip->xmit_ring_put_ptr = &fip->xmit_buffer [0];
X			fip->xmit_ring_take_ptr = &fip->xmit_buffer [0];
X
X			/* disable all ints */
X			fas_first_outb (fip, INT_ENABLE_PORT,
X							fip->ier = IE_NONE);
X
X			/* is there a serial chip ? */
X			if (fas_same_inb (fip, INT_ENABLE_PORT) != fip->ier)
X			{
X				port_stat [unit] = '?';
X				continue;	/* a hardware error */
X			}
X
X			/* test the chip thoroughly */
X			if (!(fas_port [unit] & NO_TEST)
X				&& (port_stat [unit]
X						= (fas_test_device (fip) + '0'))
X					!= '0')
X				continue;	/* a hardware error */
X
X			fas_outb (fip, LINE_CTL_PORT, fip->lcr = 0);
X			fas_outb (fip, MDM_CTL_PORT, fip->mcr
X					= fas_mcb [unit] | fip->modem.m.di);
X
X			fip->device_type = TYPE_NS16450;
X			fip->xmit_fifo_size = 1;
X			port_stat [unit] = '*';
X
X			/* let's see if it's an NS16550A */
X			fas_outb (fip, NS_FIFO_CTL_PORT, NS_FIFO_INIT_CMD);
X			if (!(~fas_inb (fip, INT_ID_PORT) & II_NS_FIFO_ENABLED))
X			{
X				fip->device_type = TYPE_NS16550A;
X				fip->xmit_fifo_size = OUTPUT_NS_FIFO_SIZE;
X				port_stat [unit] = 'F';
X				fas_outb (fip, NS_FIFO_CTL_PORT, NS_FIFO_CLEAR_CMD);
X			}
X			else
X			{
X				fas_outb (fip, NS_FIFO_CTL_PORT, NS_FIFO_CLEAR_CMD);
X				/* or is it an i82510 ? */
X				fas_outb (fip, I_BANK_PORT, I_BANK_2);
X				if (!(~fas_inb (fip, I_BANK_PORT) & I_BANK_2))
X				{
X					fip->device_type = TYPE_I82510;
X					fip->xmit_fifo_size = OUTPUT_I_FIFO_SIZE;
X					port_stat [unit] = 'f';
X					fas_outb (fip, I_BANK_PORT, I_BANK_1);
X					fas_outb (fip, I_TCM_PORT, I_FIFO_CLR_XMIT);
X					fas_outb (fip, I_RCM_PORT, I_FIFO_CLR_RECV);
X				}
X				fas_outb (fip, I_BANK_PORT, I_BANK_0);
X			}
X
X			/* disable FIFOs if requested in space.c */
X			if ((fas_port [unit] & NO_FIFO)
X					&& (fip->device_type != TYPE_NS16450))
X			{
X				fip->device_type = TYPE_NS16450;
X				fip->xmit_fifo_size = 1;
X				port_stat [unit] = '+';
X			}
X
X			/* clear potential interrupts */
X			(void) fas_inb (fip, MDM_STATUS_PORT);
X			(void) fas_inb (fip, RCV_DATA_PORT);
X			if (fas_inb (fip, LINE_STATUS_PORT) & LS_RCV_AVAIL)
X				(void) fas_inb (fip, RCV_DATA_PORT);
X			fip->lsr = 0;
X			(void) fas_inb (fip, INT_ID_PORT);
X
X			/* do we want a high speed port ? */
X			if (fas_port [unit] & HS_115200)
X				fip->device_flags.s |= DF_HIGH_SPEED | DF_115K;
X			else if (fas_port [unit] & HS_57600)
X				fip->device_flags.s |= DF_HIGH_SPEED;
X
X			/* do we want the fdx meaning of CTSFLOW/RTSFLOW ? */
X			if (fas_port [unit] & NEW_CTSRTS)
X				fip->flow_flags.s |= FF_NEW_CTSRTS;
X
X			/* show that it is present and configured */
X			fip->device_flags.s |= DF_DEVICE_CONFIGURED;
X		}
X	}
X
X	/* execute the interrupt acknowledge sequence for the serial cards */
X	for (seq_ptr = &fas_int_ack_seq [0]; *seq_ptr; ++seq_ptr)
X	{
X		port = *seq_ptr;
X		++seq_ptr;
X		if (*seq_ptr & READ_PORT)
X			(void) inb (port);
X		else
X			(void) outb (port, *seq_ptr);
X	}
X
X#if defined (NEED_PUT_GETCHAR)
X	fip = &fas_internals [0];
X	fip->mcr &= ~fip->modem.m.di;
X	fas_first_outb (fip, MDM_CTL_PORT, fip->mcr |= INITIAL_MDM_CONTROL);
X
X	fip->lcr = INITIAL_LINE_CONTROL;
X	fas_outb (fip, LINE_CTL_PORT, fip->lcr | LC_ENABLE_DIVISOR);
X	fas_outb (fip, DIVISOR_LSB_PORT, INITIAL_BAUD_RATE);
X	fas_outb (fip, DIVISOR_MSB_PORT, (INITIAL_BAUD_RATE) >> 8);
X	fas_outb (fip, LINE_CTL_PORT, fip->lcr);
X#endif
X
X#if defined (SCO) || defined (XENIX)
X	for (unit = 0; unit < fas_physical_units; ++unit)
X		(void) printcfg ("fas", (uint) ((ushort) (fas_port [unit])), 7,
X					-1, -1,
X					"unit=%d type=%c release=2.10.0",
X					unit, port_stat [unit]);
X#else
X	port_stat [unit] = '\0';
X	(void) printf ("\nFAS 2.10.0 async driver: Unit 0-%d init state is [%s]\n\n",
X			unit - 1,
X			port_stat);
X#endif
X	return (0);
X}
X
X/* Open a tty line. This function is called for every open, as opposed
X   to the fasclose function which is called only with the last close.
X   (called at SPL0)
X*/
Xint
Xfasopen (dev, flag)
Xint	dev;
Xint	flag;
X{
X	register struct fas_internals	*fip;
X	register struct tty	*ttyp;
X	register n_ushort	open_mode;
X	uint	unit;
X	bool	have_lock;
X	int	old_level;
X
X	/* check for valid port number */
X	if ((unit = GET_UNIT (dev)) >= fas_physical_units)
X	{
X		u.u_error = ENXIO;
X		return (-1);
X	}
X
X	fip = fas_internals_ptr [unit];
X
X	/* was the port present at init time ? */
X	if (!(fip->device_flags.i & DF_DEVICE_CONFIGURED))
X	{
X		u.u_error = ENXIO;
X		return (-1);
X	}
X
X	open_mode = GET_OPEN_MODE (dev);
X	have_lock = FALSE;
X	old_level = SPLINT ();
X
X	/* loop until we've got the device lock and, owning the lock, we've
X	   checked whether the current open mode permits us to open the
X	   device
X	*/
X	for (;;)
X	{
X		/* If this is a getty open, the FNDELAY flag is not set,
X		   and the device is already open for dialout, wait until
X		   device is closed.
X		*/
X		while ((open_mode & OS_OPEN_FOR_GETTY)
X#if defined (FNONBLOCK)
X				&& !(flag & (FNDELAY | FNONBLOCK))
X#else
X				&& !(flag & FNDELAY)
X#endif
X				&& (fip->o_state & OS_OPEN_FOR_DIALOUT))
X		{
X			if (have_lock)
X			{
X				release_device_lock (fip);
X				have_lock = FALSE;
X			}
X			(void) sleep ((caddr_t) &fip->o_state, TTIPRI);
X		}
X		
X		/* If the device is already open and another open uses a
X		   different open mode or if a getty open waits for carrier
X		   and doesn't allow parallel dialout opens, return with
X		   EBUSY error.
X		*/
X		if ((fip->o_state & ((open_mode & OS_OPEN_FOR_GETTY)
X					? (OS_OPEN_STATES | OS_WAIT_OPEN)
X					: (OS_OPEN_STATES | OS_NO_DIALOUT)))
X			&& ((flag & FEXCL)
X				|| ((open_mode ^ fip->o_state) & (u.u_uid
X							? OS_TEST_MASK
X							: OS_SU_TEST_MASK))))
X		{
X			if (have_lock)
X				release_device_lock (fip);
X			(void) splx (old_level);
X			u.u_error = EBUSY;
X			return (-1);
X		}
X
X		/* If device is already open and the FAPPEND flag is set,
X		   flush the output buffers. This may be used to release
X		   processes that got stuck in fasclose () during an
X		   exit () call.
X		*/
X		if ((fip->o_state & OS_OPEN_STATES) && (flag & FAPPEND))
X		{
X			flag &= ~FAPPEND;	/* flush only once */
X			(void) SPLWRK ();
X			(void) ttyflush (fip->tty, FWRITE);
X			(void) SPLINT ();
X		}
X
X		/* if we don't have the device lock, yet, try to get it */
X		if (!have_lock)
X		{
X			if (fip->device_flags.i & DF_DEVICE_LOCKED)
X			{
X				get_device_lock (fip, TTIPRI);
X				have_lock = TRUE;
X				/* we had to sleep for some time to get the
X				   lock, therefore, re-check whether the
X				   current open mode still permits us to
X				   open the device
X				*/
X				continue;
X			}
X			else
X				get_device_lock (fip, TTIPRI);
X		}
X
X		break;
X	}
X
X	/* disable subsequent opens */
X	if (flag & FEXCL)
X		open_mode |= OS_EXCLUSIVE_OPEN_1;
X
X	/* set up pointer to tty structure */
X	ttyp = (open_mode & OS_OPEN_FOR_GETTY)
X		? fas_tty_ptr [unit + fas_physical_units]
X		: fas_tty_ptr [unit];
X
X	/* things to do on first open only */
X	if (!(fip->o_state & ((open_mode & OS_OPEN_FOR_GETTY)
X				? (OS_OPEN_STATES | OS_WAIT_OPEN)
X				: OS_OPEN_STATES)))
X	{
X		/* clear carrier of getty device */
X		if (fip->o_state & OS_WAIT_OPEN)
X			fip->tty->t_state &= ~CARR_ON;
X		/* init data structures */
X		fip->tty = ttyp;
X		(void) ttinit (ttyp);
X		ttyp->t_proc = fas_proc;
X		fip->po_state = fip->o_state;
X		fip->o_state = open_mode & ~OS_OPEN_STATES;
X		fas_open_device (fip);		/* open physical device */
X
X		/* allow pending tty interrupts */
X		(void) SPLWRK ();
X		(void) SPLINT ();
X	}
X
X	/* If getty open and the FNDELAY flag is not set,
X	   block and wait for carrier if device not yet open.
X	*/
X	if ((open_mode & OS_OPEN_FOR_GETTY)
X#if defined (FNONBLOCK)
X			&& !(flag & (FNDELAY | FNONBLOCK)))
X#else
X			&& !(flag & FNDELAY))
X#endif
X	{
X		/* sleep while open for dialout or no carrier */
X		while ((fip->o_state & OS_OPEN_FOR_DIALOUT)
X			|| !(ttyp->t_state & (ISOPEN | CARR_ON)))
X		{
X			ttyp->t_state |= WOPEN;
X			release_device_lock (fip);
X			(void) sleep ((caddr_t) &ttyp->t_canq, TTIPRI);
X			get_device_lock (fip, TTIPRI);
X			ttyp->t_state &= ~WOPEN;
X		}
X	}
X
X	/* we can't have concurrent ISOPEN and WOPEN states, so
X	   wake up gettys as we are about to set the ISOPEN flag
X	*/
X	if (ttyp->t_state & WOPEN)
X		(void) wakeup ((caddr_t) &ttyp->t_canq);
X
X	(*linesw [ttyp->t_line].l_open) (ttyp);
X
X	/* set open type flags */
X	fip->o_state = open_mode;
X
X	release_device_lock (fip);
X	(void) splx (old_level);
X	return (0);
X}
X
X/* Close a tty line. This is only called if there is no other
X   concurrent open left. A blocked getty open is not counted as
X   a concurrent open because in this state it isn't really open.
X   (called at SPL0)
X*/
Xint
Xfasclose (dev)
Xint	dev;
X{
X	register struct fas_internals	*fip;
X	register struct tty	*ttyp;
X	register n_ushort	open_mode;
X	uint	unit;
X	bool	was_signal = FALSE;
X	int	old_level;
X
X	fip = fas_internals_ptr [unit = GET_UNIT (dev)];
X
X	/* set up pointer to tty structure */
X	ttyp = ((open_mode = GET_OPEN_MODE (dev)) & OS_OPEN_FOR_GETTY)
X		? fas_tty_ptr [unit + fas_physical_units]
X		: fas_tty_ptr [unit];
X	
X	old_level = SPLINT ();
X	get_device_lock (fip, PZERO - 1);
X
X	if ((fip->o_state == OS_DEVICE_CLOSED)
X			|| ((open_mode & OS_OPEN_FOR_GETTY)
X				&& (fip->o_state & OS_OPEN_FOR_DIALOUT)
X				&& (fip->po_state == OS_DEVICE_CLOSED)))
X	{
X		/* device is not open on the driver level */
X		release_device_lock (fip);
X		(void) splx (old_level);
X		return (0);
X	}
X
X	if (!((open_mode & OS_OPEN_FOR_GETTY)
X				&& (fip->o_state & OS_OPEN_FOR_DIALOUT)))
X	{
X		/* wait for buffer drain and catch interrupts */
X		while ((ttyp->t_state & CARR_ON) && (ttyp->t_outq.c_cc
X					|| (ttyp->t_state & (BUSY | TIMEOUT))))
X		{
X			ttyp->t_state |= TTIOW;
X			if (sleep ((caddr_t) &ttyp->t_oflag, was_signal
X							? PZERO - 1
X							: TTOPRI | PCATCH))
X			{
X				/* caught signal */
X				was_signal = TRUE;
X				ttyp->t_state &= ~TTIOW;
X				(void) SPLWRK ();
X				(void) ttyflush (ttyp, FWRITE);
X				(void) SPLINT ();
X			}
X		}
X
X		/* block transmitter and wait until it is
X		   empty
X		*/
X		fip->device_flags.s |= DF_XMIT_LOCKED;
X		while (fip->device_flags.i & (DF_XMIT_BUSY
X						| DF_XMIT_BREAK
X						| DF_GUARD_TIMEOUT))
X			(void) sleep ((caddr_t) &fip->device_flags.i,
X								PZERO - 1);
X
X		ttyp->t_state &= ~(WOPEN | CARR_ON);
X		/* disable receiver data interrupts */
X		if (fip->device_flags.i & DF_RDI_ENABLED)
X			fas_rdi_disable (fip);
X		/* re-link fas_internals structure */
X		fas_pos_by_speed (fip);
X		(void) SPLWRK ();
X		(void) ttyflush (ttyp, FREAD | FWRITE);
X		(void) SPLINT ();
X	}
X	else
X		ttyp->t_state &= ~(WOPEN | CARR_ON);
X
X	(*linesw [ttyp->t_line].l_close) (ttyp);
X
X	if (open_mode & OS_OPEN_FOR_GETTY)
X	{
X		if (!(fip->o_state & OS_OPEN_FOR_DIALOUT))
X		{
X			fas_close_device (fip);
X			fip->o_state = OS_DEVICE_CLOSED;
X		}
X		else	/* a parallel dialout open is active */
X			fip->po_state = OS_DEVICE_CLOSED;
X	}
X	else
X	{
X		fas_close_device (fip);
X		fip->o_state = OS_DEVICE_CLOSED;
X		/* If there is a waiting getty open on
X		   this port, reopen the physical device.
X		*/
X		if (fip->po_state & OS_WAIT_OPEN)
X		{
X			/* get the getty version of the
X			   tty structure
X			*/
X			fip->tty = fas_tty_ptr [unit + fas_physical_units];
X			fip->o_state = fip->po_state;
X			fip->po_state = OS_DEVICE_CLOSED;
X			if (!(fip->device_flags.i & DF_DO_HANGUP))
X				fas_open_device (fip);
X		}
X
X		/* wake up getty open (if any) */
X		(void) wakeup ((caddr_t) &fip->o_state);
X	}
X
X	if (!(fip->device_flags.i & DF_DO_HANGUP))
X		release_device_lock (fip);
X
X	(void) splx (old_level);
X
X	if (was_signal)
X#if defined (SVR4)
X		longjmp (&u.u_qsav);
X#else
X		longjmp (u.u_qsav);
X#endif
X	
X	return (0);
X}
X
X/* Read characters from the input buffer.
X   (called at SPL0)
X*/
Xint
Xfasread (dev)
Xint	dev;
X{
X	register struct fas_internals	*fip;
X	register struct tty	*ttyp;
X
X	fip = fas_internals_ptr [GET_UNIT (dev)];
X	ttyp = fip->tty;
X
X	(*linesw [ttyp->t_line].l_read) (ttyp);
X	return (0);
X}
X
X/* Write characters to the output buffer.
X   (called at SPL0)
X*/
Xint
Xfaswrite (dev)
Xint	dev;
X{
X	register struct fas_internals	*fip;
X	register struct tty	*ttyp;
X
X	fip = fas_internals_ptr [GET_UNIT (dev)];
X	ttyp = fip->tty;
X
X	(*linesw [ttyp->t_line].l_write) (ttyp);
X	return (0);
X}
X
X/* Process ioctl calls.
X   (called at SPL0)
X*/
Xint
Xfasioctl (dev, cmd, arg3, arg4)
Xint	dev;
Xint	cmd;
Xunion ioctl_arg	arg3;
Xint	arg4;
X{
X	register struct fas_internals	*fip;
X	register struct tty	*ttyp;
X	int	v86_cmd, v86_data;
X	int	old_level;
X	REGVAR;
X
X	fip = fas_internals_ptr [GET_UNIT (dev)];
X	ttyp = fip->tty;
X
X	/* process ioctl commands */
X	switch (cmd)
X	{
X#if defined (HAVE_VPIX)
X		case AIOCINTTYPE:	/* set pseudorupt type */
X			switch (arg3.iarg)
X			{
X				case V86VI_KBD:
X				case V86VI_SERIAL0:
X				case V86VI_SERIAL1:
X					old_level = SPLINT ();
X					fip->v86_intmask = arg3.iarg;
X					(void) splx (old_level);
X					break;
X
X				default:
X					old_level = SPLINT ();
X					fip->v86_intmask = V86VI_SERIAL0;
X					(void) splx (old_level);
X					break;
X			}
X			break;
X
X		case AIOCDOSMODE:	/* enable dos mode */
X			if (!(fip->iflag & DOSMODE))
X			{
X				old_level = SPLINT ();
X				fip->v86_proc = u.u_procp->p_v86;
X				if (!(fip->v86_intmask))
X					fip->v86_intmask = V86VI_SERIAL0;
X				ttyp->t_iflag |= DOSMODE;
X				if (fip->v86_intmask != V86VI_KBD)
X					ttyp->t_cflag |= CLOCAL;
X				fas_param (fip, SOFT_INIT);
X				(void) splx (old_level);
X			}
X			u.u_rval1 = 0;
X			break;
X
X		case AIOCNONDOSMODE:	/* disable dos mode */
X			if (fip->iflag & DOSMODE)
X			{
X				old_level = SPLINT ();
X				fip->v86_proc = (v86_t *) NULL;
X				fip->v86_intmask = 0;
X				ttyp->t_iflag &= ~DOSMODE;
X				if (fip->flow_flags.i & FF_RXFER_STOPPED)
X				{
X					fip->flow_flags.s &= ~FF_RXFER_STOPPED;
X					/* schedule character transfer
X					   to UNIX buffer
X					*/
X					if (fip->recv_ring_cnt)
X						event_sched (fip, EF_DO_RXFER);
X				}
X				fip->lcr &= ~LC_SET_BREAK_LEVEL;
X				fas_param (fip, HARD_INIT);
X				(void) splx (old_level);
X			}
X			u.u_rval1 = 0;
X			break;
X
X		case AIOCSERIALOUT:	/* setup port registers for dos */
X			if (fip->iflag & DOSMODE)
X			{
X				/* wait until output is done */
X				old_level = SPLINT ();
X				while (ttyp->t_outq.c_cc
X					|| (ttyp->t_state & (BUSY | TIMEOUT)))
X				{
X					ttyp->t_state |= TTIOW;
X					(void) sleep ((caddr_t) &ttyp->t_oflag,
X									TTOPRI);
X				}
X
X				/* block transmitter and wait until it is
X				   empty
X				*/
X				fip->device_flags.s |= DF_XMIT_LOCKED;
X				while (fip->device_flags.i & (DF_XMIT_BUSY
X							| DF_XMIT_BREAK
X							| DF_GUARD_TIMEOUT))
X					(void) sleep ((caddr_t) &fip->
X								device_flags.i,
X							PZERO - 1);
X				(void) splx (old_level);
X
X				/* get port write command */
X				v86_cmd = fubyte (arg3.cparg);
X				/* set divisor lsb requested */
X				if (v86_cmd & SIO_MASK (SO_DIVLLSB))
X				{
X					v86_data = fubyte (arg3.cparg
X								+ SO_DIVLLSB);
X					old_level = SPLINT ();
X					fas_first_outb (fip, LINE_CTL_PORT, fip->lcr
X							| LC_ENABLE_DIVISOR);
X					fas_outb (fip, DIVISOR_LSB_PORT, v86_data);
X					fas_outb (fip, LINE_CTL_PORT, fip->lcr
X							& ~LC_ENABLE_DIVISOR);
X					(void) splx (old_level);
X				}
X				/* set divisor msb requested */
X				if (v86_cmd & SIO_MASK (SO_DIVLMSB))
X				{
X					v86_data = fubyte (arg3.cparg
X								+ SO_DIVLMSB);
X					old_level = SPLINT ();
X					fas_first_outb (fip, LINE_CTL_PORT, fip->lcr
X							| LC_ENABLE_DIVISOR);
X					fas_outb (fip, DIVISOR_MSB_PORT, v86_data);
X					fas_outb (fip, LINE_CTL_PORT, fip->lcr
X							& ~LC_ENABLE_DIVISOR);
X					(void) splx (old_level);
X				}
X				/* set lcr requested */
X				if (v86_cmd & SIO_MASK (SO_LCR))
X				{
X					v86_data = fubyte (arg3.cparg + SO_LCR);
X					old_level = SPLINT ();
X					fip->lcr = v86_data
X							& ~LC_ENABLE_DIVISOR;
X					fas_first_outb (fip, LINE_CTL_PORT, fip->lcr);
X					(void) splx (old_level);
X				}
X				/* set mcr requested */
X				if (v86_cmd & SIO_MASK (SO_MCR))
X				{
X					v86_data = fubyte (arg3.cparg + SO_MCR);
X					old_level = SPLINT ();
X					/* virtual dtr processing */
X					if (v86_data & MC_SET_DTR)
X					{
X						fip->device_flags.s
X							|= DF_MODEM_ENABLED;
X						fip->mcr |= (fip->o_state
X								& OS_WAIT_OPEN)
X							? fip->modem.m.ei
X							: fip->modem.m.eo;
X					}
X					else
X					{
X						fip->device_flags.s
X							&= ~DF_MODEM_ENABLED;
X						fip->mcr &= (fip->o_state
X								& OS_WAIT_OPEN)
X							? ~fip->modem.m.ei
X							: ~fip->modem.m.eo;
X					}
X					/* virtual rts processing */
X					if (fip->flow_flags.i
X							& FF_HWI_HANDSHAKE)
X					{
X					  if (v86_data & MC_SET_RTS)
X					  {
X						if (fip->flow_flags.i
X							& FF_RXFER_STOPPED)
X						{
X						  fip->flow_flags.s
X							&= ~FF_RXFER_STOPPED;
X						  /* schedule character transfer
X						     to UNIX buffer
X						  */
X						  if (fip->recv_ring_cnt)
X							event_sched (fip,
X								EF_DO_RXFER);
X						}
X					  }
X					  else
X						fip->flow_flags.s
X							|= FF_RXFER_STOPPED;
X					}
X					else if (!(fip->flow_flags.i
X							& FF_HDX_HANDSHAKE))
X					{
X						if (v86_data & MC_SET_RTS)
X							fip->mcr
X							  |= fip->flow.m.hc;
X						else
X							fip->mcr
X							  &= ~fip->flow.m.hc;
X					}
X					fas_first_outb (fip, MDM_CTL_PORT, fip->mcr);
X					(void) splx (old_level);
X				}
X
X				old_level = SPLINT ();
X				/* enable transmitter and restart output */
X				fip->device_flags.s &= ~DF_XMIT_LOCKED;
X				fas_xproc (fip);
X				(void) splx (old_level);
X			}
X			break;
X
X		case AIOCSERIALIN:	/* read port registers for dos */
X			if (fip->iflag & DOSMODE)
X			{
X				v86_cmd = fubyte (arg3.cparg);
X				if (v86_cmd & SIO_MASK (SI_MSR))
X				{
X					(void) subyte (arg3.cparg + SI_MSR,
X							((fip->flow_flags.i
X							  & FF_HWO_HANDSHAKE)
X							? fip->msr
X							  | fip->flow.m.oc
X							  | fip->flow.m.oe
X							: fip->msr)
X							& MS_ANY_PRESENT);
X				}
X			}
X			break;
X
X		case AIOCSETSS:	/* set start/stop characters */
X			old_level = SPLINT ();
X			fip->start_char =
X				((struct termss *) &arg3.iarg)->ss_start;
X			fip->stop_char =
X				((struct termss *) &arg3.iarg)->ss_stop;
X			(void) splx (old_level);
X			break;
X
X		case AIOCINFO:	/* show what type of device we are */
X			u.u_rval1 = ('a' << 8) | GET_UNIT (dev);
X			break;
X#endif
X#if defined (RTS_TOG)
X		case RTS_TOG:	/* set hardware handshake output */
X			if (arg3.iarg == 0)
X			{
X			    /* low level */
X			    old_level = SPLINT ();
X			    /* set hardware handshake output only if unused */
X			    if (!(fip->flow_flags.i & (FF_HWI_HANDSHAKE
X							| FF_HDX_HANDSHAKE
X							| FF_DEF_HHO_LOW))
X#if defined (HAVE_VPIX)
X					&& !(fip->iflag & DOSMODE)
X#endif
X					)
X			    {
X				fas_first_outb (fip, MDM_CTL_PORT,
X						fip->mcr &= ~fip->flow.m.hc);
X			    }
X			    fip->flow_flags.s |= FF_DEF_HHO_LOW;
X			    (void) splx (old_level);
X			}
X			else if (arg3.iarg == 1)
X			{
X			    /* high level */
X			    old_level = SPLINT ();
X			    /* set hardware handshake output only if unused */
X			    if ((fip->flow_flags.i & (FF_HWI_HANDSHAKE
X							| FF_HDX_HANDSHAKE
X							| FF_DEF_HHO_LOW))
X					== FF_DEF_HHO_LOW
X#if defined (HAVE_VPIX)
X					&& !(fip->iflag & DOSMODE)
X#endif
X					)
X			    {
X				fas_first_outb (fip, MDM_CTL_PORT,
X						fip->mcr |= fip->flow.m.hc);
X			    }
X			    fip->flow_flags.s &= ~FF_DEF_HHO_LOW;
X			    (void) splx (old_level);
X			}
X			break;
X#endif
X		default:	/* default ioctl processing */
X			/* If it is a TCSETA* command, call fas_param ().
X			   There is a bug in ttiocom with TCSELE. It
X			   always tells the tty driver to reprogram the
X			   port. This will cause lost input characters.
X			   So we don't do it. SCO UNIX and Xenix use
X			   IOC_SELECT instead of TCSELE!
X			*/
X#if defined (TCSELE)
X			if (ttiocom (ttyp, cmd, arg3, arg4) && cmd != TCSELE)
X#else
X#if defined (IOC_SELECT)
X			if (ttiocom (ttyp, cmd, arg3, arg4)
X						&& cmd != IOC_SELECT)
X#else
X			if (ttiocom (ttyp, cmd, arg3, arg4))
X#endif
X#endif
X			{
X				/* reprogram the port */
X				old_level = SPLINT ();
X				fas_param (fip, SOFT_INIT);
X				(void) splx (old_level);
X			}
X			/* restart input if we are in cooked mode */
X			if (fip->recv_ring_cnt
X				&& (ttyp->t_line || (ttyp->t_lflag & ICANON)))
X			{
X				old_level = SPLINT ();
X				event_sched (fip, EF_DO_RXFER);
X				(void) splx (old_level);
X			}
X			break;
X	}
X	return (0);
X}
X
X/* Pass FAS commands to the FAS multi-function procedure.
X   (called at SPL0)
X*/
Xstatic int
Xfas_proc (ttyp, arg2)
Xstruct tty	*ttyp;
Xint	arg2;
X{
X	register uint	unit;
X	int	old_level;
X
X	unit = ttyp - &fas_tty [0];
X	if (unit >= fas_physical_units)
X		unit -= fas_physical_units;
X
X	old_level = SPLINT ();
X	fas_cmd (fas_internals_ptr [unit], ttyp, arg2);
X	(void) splx (old_level);
X	return (0);
X}
X
X/* Set up a port according to the given termio structure.
X   (called at >= SPLINT)
X*/
Xstatic void
Xfas_param (fip, init_type)
Xregister struct	fas_internals	*fip;
Xint	init_type;
X{
X	register n_ushort	cflag;
X	n_ushort	fake_cflag;
X	n_ushort	divisor;
X	int	xmit_ring_size;
X	bool	do_flush;
X	int	old_level;
X	REGVAR;
X
X	cflag = fip->tty->t_cflag;
X	fip->iflag = fip->tty->t_iflag;
X	do_flush = FALSE;
X
X#if defined (HAVE_VPIX)
X	/* we don't set port registers if we are in dos mode */
X	if (fip->iflag & DOSMODE)
X	{
X		/* This is a kludge. We don't know what baud rate
X		   DOS will use. Therefore, we assume a rather low
X		   one to be on the safe side.
X		*/
X		cflag = (cflag & ~CBAUD) | B300;
X		goto setflags2;
X	}
X#endif
X	/* Make sure that we have a valid baud rate. If we don't
X	   get one, take the previous baud rate.
X	*/
X	fake_cflag = (cflag & ~CBAUD)
X			| (((cflag & CBAUD) == B0)
X				? (fip->cflag & CBAUD)
X				: (fip->device_flags.i & DF_HIGH_SPEED)
X					? fas_hbaud [cflag & CBAUD]
X					: fas_baud [cflag & CBAUD]);
X
X	/* if soft init mode: don't set port registers if cflag didn't change */
X	if ((init_type == SOFT_INIT)
X		&& !((cflag ^ ((fip->cflag & ~CBAUD)
X				| ((fip->device_flags.i & DF_MODEM_ENABLED)
X					? fas_baud [fip->cflag & CBAUD]
X					: B0)))
X			& (CBAUD | CSIZE | CSTOPB | PARENB | PARODD)))
X	{
X		cflag = fake_cflag;
X		goto setflags;
X	}
X
X	/* hangup line if it is baud rate 0, else enable line */
X	if ((cflag & CBAUD) == B0)
X	{
X		if (fip->device_flags.i & DF_MODEM_ENABLED)
X		{
X			fip->mcr &= (fip->o_state & OS_WAIT_OPEN)
X					? ~fip->modem.m.ei
X					: ~fip->modem.m.eo;
X			fas_first_outb (fip, MDM_CTL_PORT, fip->mcr);
X			fip->device_flags.s &= ~DF_MODEM_ENABLED;
X		}
X	}
X	else
X	{
X		if (!(fip->device_flags.i & DF_MODEM_ENABLED))
X		{
X			fip->mcr |= (fip->o_state & OS_WAIT_OPEN)
X					? fip->modem.m.ei
X					: fip->modem.m.eo;
X			fas_first_outb (fip, MDM_CTL_PORT, fip->mcr);
X			fip->device_flags.s |= DF_MODEM_ENABLED;
X		}
X	}
X
X	cflag = fake_cflag;
X
X	/* don't change break flag */
X	fip->lcr &= LC_SET_BREAK_LEVEL;
X
X	/* set character size */
X	switch (cflag & CSIZE)
X	{
X	case CS5:
X		fip->lcr |= LC_WORDLEN_5;
X		break;
X
X	case CS6:
X		fip->lcr |= LC_WORDLEN_6;
X		break;
X
X	case CS7:
X		fip->lcr |= LC_WORDLEN_7;
X		break;
X
X	default:
X		fip->lcr |= LC_WORDLEN_8;
X		break;
X	}
X
X	/* set # of stop bits */
X	if (cflag & CSTOPB)
X		fip->lcr |= LC_STOPBITS_LONG;
X
X	/* set parity */
X	if (cflag & PARENB)
X	{
X		fip->lcr |= LC_ENABLE_PARITY;
X
X		if (!(cflag & PARODD))
X			fip->lcr |= LC_EVEN_PARITY;
X	}
X
X	/* set LCR and baud rate */
X	fas_first_outb (fip, LINE_CTL_PORT, fip->lcr | LC_ENABLE_DIVISOR);
X	fas_outb (fip, DIVISOR_LSB_PORT,
X				divisor = (fip->device_flags.i & DF_115K)
X						? fas_speeds2 [cflag & CBAUD]
X						: fas_speeds1 [cflag & CBAUD]);
X	fas_outb (fip, DIVISOR_MSB_PORT, divisor >> 8);
X	fas_outb (fip, LINE_CTL_PORT, fip->lcr);
X
Xsetflags:
X	/* check dynamic xmit ring buffer size against boundaries,
X	   modify it if necessary and update the fas_internals structure
X	*/
X	if ((xmit_ring_size = ((fip->device_flags.i & DF_115K)
X				? fas_xbuf_size2 [cflag & CBAUD]
X				: fas_xbuf_size1 [cflag & CBAUD])
X				- tthiwat [fas_baud [cflag & CBAUD]])
X			< MAX_OUTPUT_FIFO_SIZE * 2)
X	{
Xsetflags2:
X		xmit_ring_size = MAX_OUTPUT_FIFO_SIZE * 2;
X	}
X	fip->xmit_ring_size = (xmit_ring_size > XMIT_BUFF_SIZE)
X				? XMIT_BUFF_SIZE
X				: xmit_ring_size;
X
X	/* disable modem control signals if required by open mode */
X	if (fip->o_state & OS_CLOCAL)
X		cflag |= CLOCAL;
X
X	/* Select hardware handshake depending on the minor device
X	   number and various termio flags (if they are available).
X	*/
X	fip->flow_flags.s &= ~(FF_HWO_HANDSHAKE
X				| FF_HWI_HANDSHAKE
X				| FF_HDX_HANDSHAKE);
X	if (fip->o_state & (OS_HWO_HANDSHAKE | OS_HWI_HANDSHAKE
X					| OS_HDX_HANDSHAKE))
X	{
X		if (fip->o_state & OS_HWO_HANDSHAKE)
X			fip->flow_flags.s |= FF_HWO_HANDSHAKE;
X		if (fip->o_state & OS_HWI_HANDSHAKE)
X			fip->flow_flags.s |= FF_HWI_HANDSHAKE;
X		if (fip->o_state & OS_HDX_HANDSHAKE)
X			fip->flow_flags.s |= FF_HDX_HANDSHAKE;
X	}
X	else
X	{
X#if defined (CTSXON) && defined (RTSXOFF)	/* SysVr4 compatibility */
X		if (fip->iflag & CTSXON)
X			fip->flow_flags.s |= FF_HWO_HANDSHAKE;
X		if (fip->iflag & RTSXOFF)
X			fip->flow_flags.s |= FF_HWI_HANDSHAKE;
X#else
X#if defined (CTSFLOW) && defined (RTSFLOW)	/* SCO Xenix compatibility */
X		if (!(cflag & CLOCAL) || (fip->flow_flags.i & FF_NEW_CTSRTS))
X		{
X#if defined (CRTSFL)	/* SCO UNIX 3.2.4 compatibility */
X			if ((cflag & (CRTSFL | CTSFLOW | RTSFLOW)) == CRTSFL)
X				fip->flow_flags.s |= FF_HWO_HANDSHAKE
X							| FF_HWI_HANDSHAKE;
X			else
X#endif
X			{
X				if (cflag & CTSFLOW)
X					fip->flow_flags.s |= FF_HWO_HANDSHAKE;
X				if (cflag & RTSFLOW)
X					fip->flow_flags.s |= (fip->flow_flags.i
X								& FF_NEW_CTSRTS)
X							    ? FF_HWI_HANDSHAKE
X							    : FF_HDX_HANDSHAKE;
X			}
X		}
X#endif
X#endif
X	}
X
X	/* Determine whether to enable MSI, or not.
X	   Set the interrupt enable port accordingly.
X	*/
X#if defined (HAVE_VPIX)
X	if ((cflag & CLOCAL) && !(fip->flow_flags.i & FF_HWO_HANDSHAKE)
X		&& !(fip->iflag & DOSMODE))
X#else
X	if ((cflag & CLOCAL) && !(fip->flow_flags.i & FF_HWO_HANDSHAKE))
X#endif
X	{
X		if (fip->device_flags.i & DF_MSI_ENABLED)
X			fas_msi_disable (fip);
X	}
X	else
X	{
X		if (!(fip->device_flags.i & DF_MSI_ENABLED))
X			fas_msi_enable (fip);
X	}
X
X	/* Fake the carrier detect state flag if CLOCAL mode or if
X	   requested by open mode.
X	*/
X	if (!(~fip->msr & fip->modem.m.ca)
X		|| (cflag & CLOCAL)
X		|| ((fip->o_state & OS_FAKE_CARR_ON)
X			&& (!(fip->o_state & OS_OPEN_STATES)
X				|| (fip->tty->t_state & CARR_ON))))
X		fip->tty->t_state |= CARR_ON;
X	else if (fip->tty->t_state & CARR_ON)
X	{
X		fip->tty->t_state &= ~CARR_ON;
X		/* flush buffers on carrier drop */
X		do_flush = TRUE;
X	}
X
X	if ((fip->tty->t_state & CARR_ON) && (cflag & CREAD))
X	{
X		/* enable receiver data interrupts */
X		if (!(fip->device_flags.i & DF_RDI_ENABLED))
X			fas_rdi_enable (fip);
X	}
X	else
X	{
X		/* disable receiver data interrupts */
X		if (fip->device_flags.i & DF_RDI_ENABLED)
X			fas_rdi_disable (fip);
X	}
X
X#if defined (XCLUDE)	/* SYSV 3.2 Xenix compatibility */
X	/* Permit exclusive use of this device. */
X	if (cflag & XCLUDE)
X		fip->o_state |= OS_EXCLUSIVE_OPEN_2;
X	else
X		fip->o_state &= ~OS_EXCLUSIVE_OPEN_2;
X#endif
X
X	fip->cflag = cflag;
X
X	/* re-link fas_internals structure */
X	fas_pos_by_speed (fip);
X
X	/* flush buffers if necessary */
X	if (do_flush)
X	{
X		/* lock transmitter */
X		fip->device_flags.s |= DF_XMIT_LOCKED;
X		old_level = SPLWRK ();
X		(void) ttyflush (fip->tty, FREAD | FWRITE);
X		(void) splx (old_level);
X		/* enable transmitter */
X		fip->device_flags.s &= ~DF_XMIT_LOCKED;
X	}
X
X	/* setup handshake flags */
X#if defined (HAVE_VPIX)
X	if ((fip->flow_flags.i & (FF_HWI_HANDSHAKE | FF_HDX_HANDSHAKE))
X		|| !(fip->iflag & DOSMODE))
X#endif
X	{
X		/* clear flow control output bits */
X		fip->mcr &= ~(fip->flow.m.ic | fip->flow.m.hc);
X		fip->flow_flags.s &= ~(FF_HWI_STARTED | FF_HDX_STARTED);
X
X		/* raise flow control bits if necessary */
X		if (fip->flow_flags.i & FF_HWI_HANDSHAKE)
X		{
X			if (fip->recv_ring_cnt < HW_LOW_WATER)
X			{
X				fip->mcr |= fip->flow.m.ic;
X				fip->flow_flags.s |= FF_HWI_STARTED;
X			}
X		}
X		else if (fip->flow_flags.i & FF_HDX_HANDSHAKE)
X		{
X			if (fip->tty->t_state & BUSY)
X			{
X				fip->mcr |= fip->flow.m.hc;
X				fip->flow_flags.s |= FF_HDX_STARTED;
X			}
X		}
X		else if (!(fip->flow_flags.i & FF_DEF_HHO_LOW))
X			fip->mcr |= fip->flow.m.hc;
X
X		fas_first_outb (fip, MDM_CTL_PORT, fip->mcr);
X	}
X
X	/* set hardware output flow control flags */
X	if (!(~fip->new_msr & fip->flow.m.oc)
X			|| (~fip->new_msr & fip->flow.m.oe)
X			|| !(fip->flow_flags.i & FF_HWO_HANDSHAKE))
X		fip->flow_flags.s &= ~FF_HWO_STOPPED;
X	else
X		fip->flow_flags.s |= FF_HWO_STOPPED;
X
X	/* check software input flow control */
X	if (fip->flow_flags.i & FF_SWI_STOPPED)
X	{
X		if (!(fip->iflag & IXOFF)
X				|| (fip->recv_ring_cnt < SW_LOW_WATER))
X			fas_send_xon (fip);
X	}
X	else
X	{
X		if ((fip->iflag & IXOFF)
X				&& (fip->recv_ring_cnt > SW_HIGH_WATER))
X			fas_send_xoff (fip);
X	}
X
X	/* restart output */
X	fas_xproc (fip);
X}
X
X/* Main FAS interrupt handler. Actual character processing is splitted
X   into sub-functions.
X   (called at SPLINT)
X*/
Xint
Xfasintr (vect)
Xint	vect;
X{
X	register struct fas_internals	*fip;
X	bool	done, was_rint, was_xint, was_mint, do_xproc;
X	REGVAR;
X
X	done = TRUE;
X	was_rint = was_xint = was_mint = FALSE;
X
X	/* The 8259 interrupt controller is set up for edge trigger.
X	   Therefore, we must loop until we make a complete pass without
X	   getting any UARTs that are interrupting.
X	*/
X	do
X	{
X		/* We need a short delay if the previous loop
X		   had work to do. This is necessary to ensure
X		   that even with fast CPUs the time where the
X		   UART interrupt line is low between two
X		   interrupts is long enough that the PIC
X		   gets a proper edge to trigger a new
X		   interrupt. Therefore, we read the IER of
X		   the first int user because this doesn't
X		   have any side effects and causes a delay
X		   that is rather CPU speed independent.
X		   I HATE BRAIN-DEAD HARDWARE !
X		*/
X
X		if (!(fip = fas_first_int_user))
X			break;	/* false alarm: must be a spurious int */
X
X		if (!done)
X		{
X			(void) fas_first_inb (fip, INT_ENABLE_PORT);
X			done = TRUE;
X		}
X
X		/* Loop through all users of the interrupt users chain
X		   and look for characters in the receiver buffer.
X		   We poll the line status register regardless of
X		   whether the UART has interrupted or not. This
X		   synchronizes concurrently running receiver FIFOs
X		   and dramatically reduces the overall interrupt
X		   frequency.
X		*/
X		for (; fip; fip = fip->next_int_user)
X		{
X			register n_unchar	lstatus;
Xfastloop1:
X			/* read in all the characters from the FIFO */
X			if (!(fip->device_flags.i & DF_RDI_ENABLED)
X				|| (!(fip->lsr & LS_RCV_AVAIL)
X					&& !((fip->lsr = lstatus
X						= fas_first_inb (fip,
X							LINE_STATUS_PORT),
X								lstatus)
X						& LS_RCV_AVAIL)))
X			{
X				/* speed beats beauty */
X				fip = fip->next_int_user;
X				if (fip)
X					goto fastloop1;
X				break;
X			}
X
X			done = FALSE;
X
X			/* do the character processing */
X			fas_rproc (fip);
X
X			/* schedule character transfer to UNIX buffer */
X			if (!(fip->flow_flags.i & FF_RXFER_STOPPED))
X				event_sched (fip, EF_DO_RXFER);
X
X			/* check input buffer high water marks */
X			if ((fip->recv_ring_cnt > HW_HIGH_WATER)
X				&& (fip->flow_flags.i & (FF_HWI_HANDSHAKE
X							| FF_HWI_STARTED))
X					== (FF_HWI_HANDSHAKE | FF_HWI_STARTED))
X			{
X				fas_first_outb (fip, MDM_CTL_PORT,
X						fip->mcr &= ~fip->flow.m.ic);
X				fip->flow_flags.s &= ~FF_HWI_STARTED;
X			}
X			if ((fip->recv_ring_cnt > SW_HIGH_WATER)
X				    && (fip->iflag & IXOFF)
X				    && !(fip->flow_flags.i & FF_SWI_STOPPED))
X				fas_send_xoff (fip);
X
X			was_rint = TRUE;
X		}
X
X		/* loop through all users of the interrupt users chain
X		   and look for interrupts
X		*/
X		for (fip = fas_first_int_user; fip; fip = fip->next_int_user)
X		{
Xfastloop2:
X			/* process only ports that have an interrupt
X			   pending
X			*/
X			if (fas_first_inb (fip, INT_ID_PORT)
X							& II_NO_INTS_PENDING)
X			{
X				/* speed beats beauty */
X				fip = fip->next_int_user;
X				if (fip)
X					goto fastloop2;
X				break;
X			}
X
X			done = FALSE;
X
X			{
X			    register n_unchar	mstatus;
X
X			    /* Has there been a polarity change on
X			       some of the modem lines ?
X			    */
X			    if ((fip->device_flags.i & DF_MSI_ENABLED)
X				&& ((mstatus = fas_inb (fip, MDM_STATUS_PORT))
X						& MS_ANY_DELTA))
X			    {
X				/* if the same modem status line
X				   is responsible for a modem status
X				   interrupt twice during two
X				   event scheduler runs, we disable
X				   modem status interrupts until we
X				   process this interrupt in the event
X				   scheduler
X				*/
X				if (mstatus & fip->new_msr & MS_ANY_DELTA)
X				{
X					fas_msi_disable (fip);
X					++fas_msi_noise;
X				}
X				/* Do special RING line handling.
X				   RING generates an int only on the
X				   trailing edge.
X				*/
X				mstatus = (mstatus & ~MS_RING_PRESENT)
X					| (fip->new_msr & MS_RING_PRESENT);
X				if (mstatus & MS_RING_TEDGE)
X					mstatus |= MS_RING_PRESENT;
X				if ((mstatus ^ fip->new_msr) & MS_ANY_PRESENT)
X				{
X				    do_xproc = FALSE;
X				    /* check hw flow flags */
X				    if (!(~mstatus & fip->flow.m.oc)
X						|| (~mstatus & fip->flow.m.oe)
X						|| !(fip->flow_flags.i
X							& FF_HWO_HANDSHAKE))
X				    {
X					if (fip->flow_flags.i & FF_HWO_STOPPED)
X					{
X					    fip->flow_flags.s &=
X							~FF_HWO_STOPPED;
X					    do_xproc = TRUE;
X					}
X				    }
X				    else
X					fip->flow_flags.s |= FF_HWO_STOPPED;
X				    /* check carrier detect
X				       note: the actual carrier detect
X				             processing is done in fas_mproc ()
X				    */
X	    			    if (!(fip->cflag & CLOCAL)
X					    && (fip->tty->t_state & CARR_ON))
X	    			    {
X					if (!(~mstatus & fip->modem.m.ca))
X					{
X					    if (fip->flow_flags.i
X							& FF_CARR_STOPPED)
X					    {
X						fip->flow_flags.s &=
X							~FF_CARR_STOPPED;
X						do_xproc = TRUE;
X					    }
X					}
X					else if (!(~fip->new_msr
X							& fip->modem.m.ca))
X					    fip->flow_flags.s
X							|= FF_CARR_STOPPED;
X				    }
X				    if (do_xproc)
X					fas_xproc (fip);
X				    event_sched (fip, EF_DO_MPROC);
X				}
X				else
X				    event_sched (fip, EF_RESET_DELTA_BITS);
X				/* "or" the delta flags to prevent
X				   excessive modem status interrupts
X				*/
X				fip->new_msr = mstatus | (fip->new_msr
X								& MS_ANY_DELTA);
X				was_mint = TRUE;
X			    }
X			}
X
X			{
X			    register n_unchar	lstatus;
X
X			    /* Is it a transmitter empty int ? */
X			    if ((fip->lsr = lstatus = fas_inb (fip,
X							LINE_STATUS_PORT),
X								lstatus)
X						& LS_XMIT_AVAIL)
X			    {
X				if (fip->device_flags.i & DF_XMIT_BUSY)
X				{
X				    fip->device_flags.s &= ~DF_XMIT_BUSY;
X				    /* do the character processing */
X				    fas_xproc (fip);
X				    if (!(fip->device_flags.i & DF_XMIT_BUSY))
X				    {
X					fip->device_flags.s |= DF_GUARD_TIMEOUT;
X					fip->timeout_idx = timeout (
X						fas_timeout,
X						(caddr_t) fip,
X						(fip->device_flags.i & DF_115K)
X						    ? fas_ctimes2 [fip->cflag
X								& CBAUD]
X						    : fas_ctimes1 [fip->cflag
X								& CBAUD]);
X				    }
X				    was_xint = TRUE;
X				}
X			    }
X			}
X		}
X
X		if (!done)
X		{
X			register uint	*seq_ptr;
X			uint	port;
X
X			/* execute the interrupt acknowledge sequence for the
X			   serial cards
X			*/
X			seq_ptr = &fas_int_ack_seq [0];
X			if (*seq_ptr)
X			{
X				do
X				{
X					port = *seq_ptr;
X					++seq_ptr;
X					if (*seq_ptr & READ_PORT)
X						(void) inb (port);
X					else
X						(void) outb (port, *seq_ptr);
X				} while (*++seq_ptr);
X			}
X		}
X	} while (!done);
X		
X	/* provide statistical infos */
X	if (was_rint)
X		++sysinfo.rcvint;
X	if (was_xint)
X		++sysinfo.xmtint;
X	if (was_mint)
X		++sysinfo.mdmint;
X	return (0);
X}
X
X/* Modem status handler.
X   (called at SPLHI)
X*/
Xstatic void
Xfas_mproc (fip)
Xregister struct fas_internals	*fip;
X{
X	register n_unchar	mdm_status;
X	int	old_level;
X
X	mdm_status = fip->new_msr;
X	fip->new_msr &= ~MS_RING_PRESENT;
X
X	{
X	    register struct tty	*ttyp;
X
X	    ttyp = fip->tty;
X
X	    /* Check the carrier detect signal and set the state flags
X	       accordingly. Also, if not in clocal mode, send SIGHUP on
X	       carrier loss and flush the buffers.
X	    */
X	    if (!(fip->cflag & CLOCAL) && (ttyp->t_state & (WOPEN | ISOPEN)))
X	    {
X		if (!(~mdm_status & fip->modem.m.ca))
X		{
X			/* Wake up getty open on carrier low->high. */
X			if (!(ttyp->t_state & CARR_ON))
X			{
X				if ((ttyp->t_state |= CARR_ON) & WOPEN)
X					(void) wakeup ((caddr_t) &ttyp->t_canq);
X				/* enable receiver data interrupts */
X				if ((fip->cflag & CREAD)
X						&& !(fip->device_flags.i
X							& DF_RDI_ENABLED))
X					fas_rdi_enable (fip);
X				/* re-link fas_internals structure */
X				fas_pos_by_speed (fip);
X			}
X		}
X		else
X		{
X			if (!(~fip->msr & fip->modem.m.ca))
X			{
X				ttyp->t_state &= ~CARR_ON;
X				/* disable receiver data interrupts */
X				if (fip->device_flags.i & DF_RDI_ENABLED)
X					fas_rdi_disable (fip);
X				/* re-link fas_internals structure */
X				fas_pos_by_speed (fip);
X				old_level = SPLWRK ();
X				if (ttyp->t_pgrp)
X				{
X					(void) signal (ttyp->t_pgrp, SIGHUP);
X#if defined (SIGCONT)
X					/* make sure processes are awake */
X					(void) signal (ttyp->t_pgrp, SIGCONT);
X#endif
X				}
X				(void) ttyflush (ttyp, FREAD | FWRITE);
X				(void) splx (old_level);
X			}
X		}
X	    }
X	}
X
X#if defined (HAVE_VPIX)
X	if (((fip->iflag & (DOSMODE | PARMRK))
X			== (DOSMODE | PARMRK))
X		&& (fip->v86_intmask != V86VI_KBD))
X	{
X		register n_unchar	vpix_status;
SHAR_EOF
true || echo 'restore of fas.c failed'
fi
echo 'End of fas210 part 3'
echo 'File fas.c is continued in part 4'
echo 4 > _shar_seq_.tmp
exit 0
-- 
Uwe Doering  |  INET : gemini@geminix.in-berlin.de
Berlin       |----------------------------------------------------------------
Germany      |  UUCP : ...!unido!fub!geminix.in-berlin.de!gemini
Xref: sparky comp.unix.sysv386:13378 alt.fan.john-palmer:330