[comp.unix.i386] FAS 2.05 async driver, part 2/2

gemini@geminix.UUCP (Uwe Doering) (01/15/90)

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	fas.c
#	fas.h
# This archive created: Sun Jan 14 21:42:01 1990
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'fas.c'" '(42626 characters)'
if test -f 'fas.c'
then
	echo shar: "will not over-write existing file 'fas.c'"
else
sed 's/^X//' << \SHAR_EOF > 'fas.c'
X/* FAS Final Async Solution driver for 386 versions of system V UNIX */
X
X/* Originally written by
XJim Murray              encore!cloud9!jjmhome!jjm
X2 Mohawk Circle         harvard!m2c!jjmhome!jjm
XWestboro Mass 01581     jjm%jjmhome@m2c.m2c.org
XUSA                     voice (508) 366-2813
X*/
X
X/* Current author:
XUwe Doering		gemini@netmbx.UUCP
XBillstedter Pfad 17 B
X1000 Berlin 20
XWest Germany
X*/
X
X#ident	"@(#)fas.c	2.05"
X
X/* Note: This source code was quite heavily optimized for speed. You
X         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 (page or cache misses). Therefore saving some
X         data accesses has higher priority than saving code accesses.
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 3.X/386 C compiler.
X*/
X
X#include <sys/fas.h>
X
X#if !defined (__GNUC__)
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 3.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	void 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
X
X#else
X
X#define REGVAR
X
X#endif
X
X/* functions provided by this driver */
Xint		fasinit ();
Xint		fasopen ();
Xint		fasclose ();
Xint		fasread ();
Xint		faswrite ();
Xint		fasioctl ();
Xint		fasintr ();
Xstatic int	fas_proc ();
Xstatic void	fas_param ();
Xstatic void	fas_mproc ();
Xstatic uint	fas_rproc ();
Xstatic int	fas_rxfer ();
Xstatic void	fas_cmd ();
Xstatic void	fas_open_device ();
Xstatic void	fas_close_device ();
Xstatic int	fas_test_device ();
X
X/* functions used by this driver */
Xextern int	ttrstrt ();
Xextern int	ttinit ();
Xextern int	ttiocom ();
Xextern int	ttyflush ();
Xextern int	spltty ();
Xextern int	splx ();
Xextern int	sleep ();
Xextern int	wakeup ();
Xextern int	signal ();
Xextern int	timeout ();
Xextern int	delay ();
Xextern void	longjmp ();
Xextern int	printf ();
X
X/* the following stuff is defined in space.c */
Xextern uint	fas_physical_units;
Xextern uint	fas_port [];
Xextern uint	fas_vec [];
Xextern uint	fas_mcb [];
Xextern uint	fas_flow [];
Xextern uint	fas_int_ack_port [];
Xextern uint	fas_int_ack [];
Xextern uint	fas_mux_ack_port [];
Xextern uint	fas_mux_ack [];
Xextern struct fas_info	fas_info [];
Xextern struct tty	fas_tty [];
Xextern struct fas_info	*fas_info_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*/
Xint	fas_is_initted = FALSE;
X
X/* array of linked lists of fas_info structures for each interrupt vector
X   this is filled in at init time
X*/
Xstatic struct fas_info	*fas_vector_users [NUM_INT_VECTORS];
X
X/* the values for the various baud rates */
Xstatic uint	fas_speeds [CBAUD + 1] =
X{	0,			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 */
Xstatic uint	fas_ctimes [CBAUD + 1] =
X{	1,		HZ*30/50+2,
X	HZ*30/75+2,	HZ*30/110+2,
X	HZ*60/269+2,	HZ*30/150+2,
X	HZ*30/200+2,	HZ*30/300+2,
X	HZ*30/600+2,	HZ*30/1200+2,
X	HZ*30/1800+2,	HZ*30/2400+2,
X	HZ*30/4800+2,	HZ*30/9600+2,
X	HZ*30/19200+2,	HZ*30/38400+2
X};
X
X/* lookup table for minor device number -> open mode flags translation */
Xstatic uint	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_HW_HANDSHAKE,
X	OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON,
X	OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_HW_HANDSHAKE,
X	OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN,
X	OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN | OS_HW_HANDSHAKE,
X	OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN | OS_FAKE_CARR_ON,
X	OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN | OS_FAKE_CARR_ON | OS_HW_HANDSHAKE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_HW_HANDSHAKE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_UNBLOCK_ON_RING,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_UNBLOCK_ON_RING | OS_HW_HANDSHAKE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_HW_HANDSHAKE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_UNBLOCK_ON_RING,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_UNBLOCK_ON_RING | OS_HW_HANDSHAKE
X};
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) \
X{\
X	/* sleep while device is used by an other process */\
X	while ((fip)->device_flags.i & DF_DEVICE_LOCKED)\
X		sleep((caddr_t) &(fip)->device_flags.i, PZERO - 1);\
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	wakeup ((caddr_t) &(fip)->device_flags.i);\
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 if the device is determined to be a
X   NS16550A chip the DF_DEVICE_HAS_FIFO flag is set and the FIFO will
X   be used.
X
X   At boot time you will see a status message on the screen with a string
X   of symbols that show the init state of the ports. The symbols are as
X   follows:
X
X      -   not defined in the fas_port array
X      ?   can't initialize port
X      !   port test procedure failed
X      *   port is initialized
X      F   port is initialized and has FIFOs (NS16550)
X
X   This is convenient to check whether you have entered the proper port
X   base addresses in space.c.
X*/
X
Xint
Xfasinit ()
X{
X	register struct fas_info	*fip;
X	register uint	unit;
X	uint	logical_units;
X	uint	port;
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	for (unit = 0, logical_units = fas_physical_units * 2;
X		unit < logical_units; unit++)
X		fas_tty_ptr [unit] = &fas_tty [unit];
X
X	for (unit = 0; unit < fas_physical_units; unit++)
X	{
X		fas_info_ptr [unit] = fip = &fas_info [unit];
X		port_stat [unit] = '-';
X		if (port = fas_port [unit])
X		{
X			/* init all of its ports */
X			RCV_DATA_PORT = port + RCV_DATA_OFFSET;
X			XMT_DATA_PORT = port + XMT_DATA_OFFSET;
X			INT_ENABLE_PORT = port + INT_ENABLE_OFFSET;
X			INT_ID_PORT = port + INT_ID_OFFSET;
X			FIFO_CTL_PORT = port + FIFO_CTL_OFFSET;
X			LINE_CTL_PORT = port + LINE_CTL_OFFSET;
X			MDM_CTL_PORT = port + MDM_CTL_OFFSET;
X			LINE_STATUS_PORT = port + LINE_STATUS_OFFSET;
X			MDM_STATUS_PORT = port + MDM_STATUS_OFFSET;
X			DIVISOR_LSB_PORT = port + DIVISOR_LSB_OFFSET;
X			DIVISOR_MSB_PORT = port + DIVISOR_MSB_OFFSET;
X			INT_ACK_PORT = fas_int_ack_port [unit];
X			fip->int_ack = fas_int_ack [unit];
X			fip->recv_ring_put_ptr = fip->recv_buffer;
X			fip->recv_ring_take_ptr = fip->recv_buffer;
X			fip->recv_ring_cnt = 0;
X			fip->h_mask = fas_flow [unit];
X
X			fip->ier.c = IE_NONE;	/* disable all ints */
X			outb (INT_ENABLE_PORT, fip->ier.i);
X			if (inb (INT_ENABLE_PORT) != fip->ier.i)
X			{
X				port_stat [unit] = '?';
X				continue;	/* a hardware error */
X			}
X
X			if (!fas_test_device (fip))
X			{
X				port_stat [unit] = '!';
X				continue;	/* a hardware error */
X			}
X
X			fip->mcr.c = fas_mcb [unit] | (INITIAL_MDM_CONTROL);
X			outb (MDM_CTL_PORT, fip->mcr.i);
X
X			fip->lcr.c = INITIAL_LINE_CONTROL;
X			outb (LINE_CTL_PORT, fip->lcr.i | LC_ENABLE_DIVISOR);
X			outb (DIVISOR_LSB_PORT, INITIAL_BAUD_RATE);
X			outb (DIVISOR_MSB_PORT, (INITIAL_BAUD_RATE) >> 8);
X			outb (LINE_CTL_PORT, fip->lcr.i);
X			port_stat [unit] = '*';
X
X			/* let's see if it's an NS16550 */
X			outb (FIFO_CTL_PORT, STANDARD_FIFO_INIT);
X			if ((inb (INT_ID_PORT) & II_FIFO_ENABLED)
X					== II_FIFO_ENABLED)
X			{
X				fip->device_flags.s |= DF_DEVICE_HAS_FIFO;
X				port_stat [unit] = 'F';
X			}
X			/* clear and disable the FIFO */
X			outb (FIFO_CTL_PORT, STANDARD_FIFO_CLEAR);
X
X			/* clear potential interrupts */
X			inb (MDM_STATUS_PORT);
X			inb (RCV_DATA_PORT);
X			inb (RCV_DATA_PORT);
X			inb (LINE_STATUS_PORT);
X			inb (INT_ID_PORT);
X			if (INT_ACK_PORT)
X				outb (INT_ACK_PORT, fip->int_ack);
X			if (port = fas_mux_ack_port [fas_vec [unit]])
X				outb (port, fas_mux_ack [fas_vec [unit]]);
X
X			/* show that it is present and configured */
X			fip->device_flags.s |= DF_DEVICE_CONFIGURED;
X
X			/* link in as interrupt user */
X			fip->next_interrupt_user = fas_vector_users [fas_vec [unit]];
X			fas_vector_users [fas_vec [unit]] = fip;
X		}
X	}
X	port_stat [unit] = '\0';
X	printf ("\nFAS 2.05 async driver: port 0-%d init state is [%s]\n\n",
X			unit - 1,
X			port_stat);
X	return(0);
X}
X
X/* Open a line */
X
X/* There are a few differences between this and a normal serial
X   device.
X
X   For each physical port there are two logical minor devices.
X   For each logical minor device there are several operating modes
X   that are selected by some of the higher bits of the minor
X   device number. Only one logical minor device can be open at
X   the same time.
X
X - Minor devices that *don't* block on open if no carrier is present:
X
X	Bitmap:   0 m m h x x x x
X
X	   `m m' are the mode bits as follows:
X
X	    0 0   The DCD signal is totally ignored. With DCD high->low
X		  *no* SIGHUP signal is generated.
X	    0 1   After an initial open, the DCD signal is ignored.
X		  Although, DCD high->low generates a SIGHUP signal. From
X		  thereon the device is DCD controlled until the last
X		  process has closed the device. An ioctl call with a
X		  TCSETA* command resets the device to ignore DCD again
X		  until the next DCD high->low.
X	    1 0   The device is DCD controlled. Additionally, if on open
X		  the DCD signal is low, a SIGHUP signal is sent immediately.
X	    1 1   The device behaves the same as with mode `0 1'. Additionally,
X		  if on open the DCD signal is low, a SIGHUP signal is sent
X		  immediately.
X
X - Minor devices that *do* block on open if no carrier is present:
X
X	Bitmap:   1 m m h x x x x
X
X	   `m m' are the mode bits as follows:
X
X	    0 0   The device is DCD controlled.
X	    0 1   The device is DCD controlled. A RING signal unblocks
X		  the waiting open and I/O is possible regardless of
X		  DCD until a DCD high->low. Thereafter the device is
X		  again fully DCD controlled.
X	    1 0   Same as mode `0 0', but a parallel non-blocking open
X		  is possible while waiting for carrier.
X	    1 1   Same as mode `0 1', but a parallel non-blocking open
X		  is possible while waiting for carrier.
X
X - Description of the remaining bits of the bitmap:
X
X	   `h'    If set to `1', the device has hardware handshake. Refer
X		  to the `space.c' file to determine which port signals
X		  are actually used for that purpose.
X	   `x x x x'
X		  This is the physical port number. This driver supports
X		  up to 16 ports. If you need more, you should use an
X		  intelligent serial card because more than 16 devices
X		  will eat up to much CPU time with this dumb-port approach.
X
X - Note: If a device is DCD controlled, this implies the generation of
X	 a SIGHUP signal with every DCD high->low. This is of course only
X	 true if the CLOCAL flag is *not* set.
X
X	 If you use more than a few ports and you have a high volume of
X	 receiver data at a high baud rate, the ports may lose characters.
X	 This is simply a hardware limitation and can't be cured by any
X	 software. But there is a pin-to-pin compatible replacement
X	 for the i8250 and NS16450 devices. It's the NS16550 and has
X	 separate 16-character I/O FIFOs. This will make any character
X	 loss at least very improbable. Also, the system is loaded
X	 much less because whenever possible up to 16 characters are
X	 processed at a single port interrupt.
X
X         On my own system I prefer a minor device number of `0011xxxx'
X         (48 + device #) for the non-blocking tty node and `1101xxxx'
X         (208 + device #) for the blocking tty node. This gives me
X         the SIGHUP signal on carrier loss and hardware flow control
X         with both logical devices. Dialout while a dialin open
X         is waiting for the carrier is also possible with this setup.
X
XThis function is called for every open, as opposed to the fasclose
Xfunction which is called only with the last close.
X*/
X
Xint
Xfasopen (dev, flag)
Xint	dev;
Xint	flag;
X{
X	register struct fas_info	*fip;
X	register struct tty		*ttyp;
X	register uint	open_mode;
X	uint	physical_unit;
X	int	old_level;
X
X	physical_unit = GET_UNIT (dev);
X
X	/* check for valid port number */
X	if (physical_unit >= fas_physical_units)
X	{
X		u.u_error = ENXIO;
X		return(-1);
X	}
X
X	fip = fas_info_ptr [physical_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
X	old_level = spltty ();
X	get_device_lock (fip);
X
X	/* If this is a getty open and the device is already open
X	   for dialout, wait until device is closed.
X	*/
X	while ((open_mode & OS_OPEN_FOR_GETTY)
X			&& (fip->o_state & OS_OPEN_FOR_DIALOUT))
X	{
X		release_device_lock (fip);
X		sleep ((caddr_t) &fip->o_state, TTIPRI);
X		get_device_lock (fip);
X	}
X	
X	/* If the device is already open and another open uses a different
X	   open mode or if a getty open waits for DCD and doesn't allow
X	   parallel dialout opens, return with I/O 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		&& ((open_mode ^ fip->o_state) & OS_TEST_MASK))
X	{
X		u.u_error = EIO;
X		release_device_lock (fip);
X		splx (old_level);
X		return(-1);
X	}
X
X	/* set up pointer to tty structure */
X	ttyp = (open_mode & OS_OPEN_FOR_GETTY)
X		? fas_tty_ptr [physical_unit + fas_physical_units]
X		: fas_tty_ptr [physical_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		/* init data structures */
X		fip->tty = ttyp;
X		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		/* open physical device if not yet open */
X		if (!(fip->device_flags.i & DF_DEVICE_OPEN))
X			fas_open_device (fip);
X		fas_param (fip);	/* set up port registers */
X		fas_mproc (fip);	/* set up modem status flags */
X	}
X
X	/* If getty open and the FNDELAY flag is not set,
X	   block and wait for DCD.
X	*/
X	if ((open_mode & OS_OPEN_FOR_GETTY) && !(flag & FNDELAY))
X	{
X		/* sleep while open for dialout or no carrier */
X		while ((fip->o_state & OS_OPEN_FOR_DIALOUT)
X			|| !(ttyp->t_state & CARR_ON))
X		{
X			ttyp->t_state |= WOPEN;
X			release_device_lock (fip);
X			sleep((caddr_t) &ttyp->t_canq, TTIPRI);
X			get_device_lock (fip);
X			ttyp->t_state &= ~WOPEN;
X		}
X	}
X
X	(*linesw [ttyp->t_line].l_open) (ttyp);
X
X	/* set open type flags */
X	fip->o_state = open_mode;
X
X	if ((open_mode & OS_CHECK_CARR_ON_OPEN)
X		&& !(fip->msr & MS_DCD_PRESENT)
X		&& !(fip->cflag & CLOCAL))
X	{
X		signal (ttyp->t_pgrp, SIGHUP);
X		ttyflush (ttyp, FREAD | FWRITE);
X	}
X
X	release_device_lock (fip);
X	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*/
Xint
Xfasclose (dev)
Xint	dev;
X{
X	register struct fas_info	*fip;
X	register struct tty		*ttyp;
X	uint	open_mode;
X	uint	physical_unit;
X	int	old_level;
X
X	physical_unit = GET_UNIT (dev);
X
X	fip = fas_info_ptr [physical_unit];
X
X	open_mode = GET_OPEN_MODE (dev);
X
X	/* set up pointer to tty structure */
X	ttyp = (open_mode & OS_OPEN_FOR_GETTY)
X		? fas_tty_ptr [physical_unit + fas_physical_units]
X		: fas_tty_ptr [physical_unit];
X	
X	old_level = spltty ();
X
X	(*linesw [ttyp->t_line].l_close) (ttyp);
X
X	get_device_lock (fip);
X
X	if (open_mode & OS_OPEN_FOR_GETTY)
X	{
X		/* not waiting any more */
X		ttyp->t_state &= ~WOPEN;
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
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 [physical_unit
X					+ 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			{
X				fas_open_device (fip);
X				fas_param (fip);	/* set up port registers */
X				fas_mproc (fip);	/* set up modem status flags */
X			}
X		}
X		wakeup ((caddr_t) &fip->o_state);
X	}
X
X	if (!(fip->device_flags.i & DF_DO_HANGUP))
X		release_device_lock (fip);
X
X	splx (old_level);
X	return(0);
X}
X
X/* read characters from the input buffer */
Xint
Xfasread (dev)
Xint	dev;
X{
X	register struct fas_info	*fip;
X	register struct tty	*ttyp;
X	int	old_level;
X	REGVAR;
X
X	fip = fas_info_ptr [GET_UNIT (dev)];
X
X	ttyp = fip->tty;
X
X	(*linesw [ttyp->t_line].l_read) (ttyp);
X
X	old_level = spltty ();
X
X	/* fill the unix buffer as much as possible */
X	while (fip->recv_ring_cnt)
X	{
X		if (fas_rxfer (fip))
X			break;
X
X		splx (old_level);	/* allow some interrupts */
X		old_level = spltty ();
X	}
X
X	/* If input buffer level has dropped below
X	   the low water mark and input was stopped
X	   by RTS low, set RTS high to restart input.
X	*/
X	if ((fip->device_flags.i & DF_HWISTOP)
X		&& (fip->recv_ring_cnt < HW_LOW_WATER))
X	{
X		fip->mcr.c |= MC_SET_RTS;
X			outb (MDM_CTL_PORT, fip->mcr.i);
X		fip->device_flags.s &= ~DF_HWISTOP;
X	}
X
X	/* If input buffer level has dropped below
X	   the low water mark and input was stopped
X	   by XOFF, send XON to restart input.
X	*/
X	if ((fip->device_flags.i & DF_SWISTOP)
X		&& (fip->recv_ring_cnt < SW_LOW_WATER))
X	{
X		fip->device_flags.s &= ~DF_SWISTOP;
X		fip->device_flags.s ^= DF_SW_FC_REQ;
X		if (fip->device_flags.i & DF_SW_FC_REQ)
X		{
X			ttyp->t_state |= TTXON;
X			fas_cmd (fip, ttyp, T_OUTPUT);
X		}
X		else
X			ttyp->t_state &= ~TTXOFF;
X	}
X
X	splx (old_level);
X	return(0);
X}
X
X/* write characters to the output buffer */
Xint
Xfaswrite (dev)
Xint	dev;
X{
X	register struct tty	*ttyp;
X
X	ttyp = fas_info_ptr [GET_UNIT (dev)]->tty;
X
X	(*linesw [ttyp->t_line].l_write) (ttyp);
X	return(0);
X}
X
X/* process ioctl calls */
Xint
Xfasioctl (dev, cmd, arg3, arg4)
Xint	dev;
Xint	cmd;
Xunion ioctl_arg	arg3;
Xint	arg4;
X{
X	register struct fas_info	*fip;
X	register struct tty	*ttyp;
X	uint	old_t_state;
X	int	old_level;
X
X	fip = fas_info_ptr [GET_UNIT (dev)];
X
X	ttyp = fip->tty;
X
X	/* if it is a TCSETA* command, call fas_param () */
X	if (ttiocom (ttyp, cmd, arg3, arg4))
X	{
X		old_level = spltty ();
X		old_t_state = ttyp->t_state;
X		fas_param (fip);
X		/* if we switched off CLOCAL mode and the *real* carrier
X		   is missing we send the SIGHUP signal once
X		*/
X		if (!(ttyp->t_state & CARR_ON) && (old_t_state & CARR_ON))
X		{
X			signal (ttyp->t_pgrp, SIGHUP);
X			ttyflush (ttyp, FREAD | FWRITE);
X		}
X		splx (old_level);
X	}
X	return(0);
X}
X
X/* pass fas commands to the fas multi-function procedure */
Xstatic int
Xfas_proc (ttyp, arg2)
Xstruct tty	*ttyp;
Xint	arg2;
X{
X	register uint	physical_unit;
X	int	old_level;
X
X	physical_unit = ttyp - &fas_tty [0];
X	if (physical_unit >= fas_physical_units)
X		physical_unit -= fas_physical_units;
X
X	old_level = spltty ();
X	fas_cmd (fas_info_ptr [physical_unit], ttyp, arg2);
X	splx (old_level);
X	return (0);
X}
X
X/* set up a port according to the given termio structure */
Xstatic void
Xfas_param (fip)
Xregister struct fas_info	*fip;
X{
X	register uint	cflag;
X	uint	divisor;
X	REGVAR;
X
X	cflag = fip->tty->t_cflag;
X
X	/* drop DTR if it is baud rate 0, else raise DTR */
X	if ((cflag & CBAUD) == B0)
X	{
X		cflag = (cflag & ~CBAUD) | (fip->cflag & CBAUD);
X		fip->mcr.c &= ~MC_SET_DTR;
X		outb (MDM_CTL_PORT, fip->mcr.i);
X	}
X	else
X	{
X		if (!(fip->mcr.i & MC_SET_DTR))
X		{
X			fip->mcr.c |= MC_SET_DTR;
X			outb (MDM_CTL_PORT, fip->mcr.i);
X		}
X	}
X
X	/* set character size */
X	switch (cflag & CSIZE)
X	{
X	default:
X	case CS8:
X		fip->lcr.c = LC_WORDLEN_8;
X		break;
X
X	case CS5:
X		fip->lcr.c = LC_WORDLEN_5;
X		break;
X
X	case CS6:
X		fip->lcr.c = LC_WORDLEN_6;
X		break;
X
X	case CS7:
X		fip->lcr.c = LC_WORDLEN_7;
X		break;
X	}
X
X	/* set # of stop bits */
X	if (cflag & CSTOPB)
X		fip->lcr.c |= LC_STOPBITS_LONG;
X
X	/* set parity */
X	if (cflag & PARENB)
X	{
X		fip->lcr.c |= LC_ENABLE_PARITY;
X
X		if (!(cflag & PARODD))
X			fip->lcr.c |= LC_EVEN_PARITY;
X	}
X
X	/* change counter divisor only if baud rate has changed */
X	if ((cflag ^ fip->cflag) & CBAUD)
X	{
X		/* get counter divisor for selected baud rate */
X		divisor = fas_speeds [cflag & CBAUD];
X		outb (LINE_CTL_PORT, fip->lcr.i | LC_ENABLE_DIVISOR);
X		outb (DIVISOR_LSB_PORT, divisor);
X		outb (DIVISOR_MSB_PORT, divisor >> 8);
X	}
X
X	outb (LINE_CTL_PORT, fip->lcr.i);
X
X	/* disable modem control signals if required by open mode */
X	if (fip->o_state & OS_CLOCAL)
X		cflag |= CLOCAL;
X
X	/* Fake the carrier detect state flag if CLOCAL mode or if
X	   requested by open mode.
X	*/
X	if ((fip->msr & MS_DCD_PRESENT)
X		|| (fip->o_state & OS_FAKE_CARR_ON)
X		|| (cflag & CLOCAL))
X		fip->tty->t_state |= CARR_ON;
X	else
X		fip->tty->t_state &= ~CARR_ON;
X
X	fip->cflag = cflag;
X	fip->iflag = fip->tty->t_iflag;
X}
X
X/* Main fas interrupt handler. Actual character processing is splitted
X   into sub-functions.
X*/
Xint
Xfasintr (vect)
Xint	vect;
X{
X	register struct fas_info	*fip;
X	register uint	line_status;
X	int	done = FALSE;
X	uint	port;
X	REGVAR;
X
X	/* I believe that the 8259 is set up for edge trigger. Therefore
X	   I must loop until I make a complete pass without getting
X	   any UARTs that are interrupting.
X	*/
X	while (!done)
X	{
X		done = TRUE;
X
X		/* loop through all users of this interrupt vector */
X		for (fip = fas_vector_users [vect]; fip;
X				fip = fip->next_interrupt_user)
X		{
X			/* process only ports that we expect ints from */
X			if (!(fip->ier.c))
X				continue;
X
X			for (;;)
X			{
X				/* any ints left on this port ? */
X				if (inb (INT_ID_PORT) & II_NO_INTS_PENDING)
X				{
X					/* clear the port interrupt */
X					if (INT_ACK_PORT)
X						outb (INT_ACK_PORT, fip->int_ack);
X					break;
X				}
X
X				done = FALSE;	/* not done if we got one */
X
X				/* read in all the characters from the FIFO */
X				if ((line_status = inb (LINE_STATUS_PORT))
X					& LS_RCV_INT)
X				{
X					line_status = fas_rproc (fip, line_status);
X					sysinfo.rcvint++;
X				}
X
X				/* Is it a transmitter empty int ? */
X				if ((line_status & LS_XMIT_AVAIL)
X					&& (fip->device_flags.i & DF_XMIT_BUSY))
X				{
X					fip->device_flags.s &= ~DF_XMIT_BUSY;
X					fip->tty->t_state &= ~BUSY;
X					fas_cmd (fip, fip->tty, T_OUTPUT);
X					sysinfo.xmtint++;
X				}
X
X				/* Has there been a polarity change on
X				   some of the modem lines ?
X				*/
X				if ((inb (MDM_STATUS_PORT) ^ fip->msr)
X					& MS_ANY_PRESENT)
X				{
X					fas_mproc (fip);
X					sysinfo.mdmint++;
X				}
X			}
X
X			/* process the characters in the ring buffer */
X			if (fip->recv_ring_cnt)
X			{
X				fas_rxfer (fip);
X				/* If input buffer level has risen above the
X				   high water mark and input is not yet
X				   stopped, set RTS low to stop input.
X				*/
X				if ((fip->o_state & OS_HW_HANDSHAKE)
X					&& !(fip->device_flags.i & DF_HWISTOP)
X					&& (fip->recv_ring_cnt > HW_HIGH_WATER))
X				{
X					fip->mcr.c &= ~MC_SET_RTS;
X					outb (MDM_CTL_PORT, fip->mcr.i);
X					fip->device_flags.s |= DF_HWISTOP;
X				}
X				/* If input buffer level has risen above the
X				   high water mark and input is not yet
X				   stopped, send XOFF to stop input.
X				*/
X				if ((fip->iflag & IXOFF)
X					&& !(fip->device_flags.i & DF_SWISTOP)
X					&& (fip->recv_ring_cnt > SW_HIGH_WATER))
X				{
X					fip->device_flags.s |= DF_SWISTOP;
X					fip->device_flags.s ^= DF_SW_FC_REQ;
X					if (fip->device_flags.i & DF_SW_FC_REQ)
X					{
X						fip->tty->t_state |= TTXOFF;
X						fas_cmd (fip, fip->tty, T_OUTPUT);
X					}
X					else
X						fip->tty->t_state &= ~TTXON;
X				}
X			}
X		}
X	}
X
X	/* clear the mux interrupt since we have scanned all
X	   of the ports that share this interrupt vector
X	*/	
X	if (port = fas_mux_ack_port [vect])
X		outb (port, fas_mux_ack [vect]);
X
X	return(0);
X}
X
X/* modem status interrupt handler */
Xstatic void
Xfas_mproc (fip)
Xregister struct fas_info	*fip;
X{
X	register struct tty	*ttyp;
X	register uint	mdm_state;
X	REGVAR;
X
X	ttyp = fip->tty;
X
X	mdm_state = inb (MDM_STATUS_PORT);
X
X	/* Check the output flow control signals and set the state flag
X	   accordingly.
X	*/
X	if (fip->o_state & OS_HW_HANDSHAKE)
X	{
X		if ((mdm_state & fip->h_mask) == fip->h_mask)
X		{
X			if (fip->device_flags.i & DF_HWOSTOP)
X			{
X				fip->device_flags.s &= ~DF_HWOSTOP;
X				fas_cmd (fip, ttyp, T_OUTPUT);
X			}
X		}
X		else
X			fip->device_flags.s |= DF_HWOSTOP;
X	}
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))
X	{
X		if (mdm_state & MS_DCD_PRESENT)
X		{
X			ttyp->t_state |= CARR_ON;
X			/* Unblock getty open only if it is ready to run. */
X			if (fip->o_state & OS_WAIT_OPEN)
X				wakeup ((caddr_t) &ttyp->t_canq);
X		}
X		else
X		{
X			if (fip->msr & MS_DCD_PRESENT)
X			{
X				ttyp->t_state &= ~CARR_ON;
X				if (ttyp->t_state & ISOPEN)
X				{
X					signal (ttyp->t_pgrp, SIGHUP);
X					ttyflush (ttyp, FREAD | FWRITE);
X				}
X			}
X		}
X	}
X
X	/* Check the ring signal. If low->high edge, fake CARR_ON state
X	   flag and wake up getty open.
X	*/
X	if ((fip->o_state & OS_UNBLOCK_ON_RING)
X		&& !(fip->cflag & CLOCAL)
X		&& (mdm_state & MS_RING_PRESENT)
X		&& !(fip->msr & MS_RING_PRESENT)
X		&& (fip->o_state & OS_WAIT_OPEN))
X	{
X		ttyp->t_state |= CARR_ON;
X		wakeup ((caddr_t) &ttyp->t_canq);
X	}
X
X	fip->msr = mdm_state;
X}
X
X/* Receiver interrupt handler. Translates input characters to character
X   sequences as described in TERMIO(7) man page.
X*/
Xstatic uint
Xfas_rproc (fip, line_status)
Xregister struct fas_info	*fip;
Xuint	line_status;
X{
X	struct tty	*ttyp;
X	uint	charac;
X	register uint	csize;
X	unchar	metta [4];
X	REGVAR;
X
X	ttyp = fip->tty;
X
X	/* Translate characters from FIFO according to the TERMIO(7)
X	   man page.
X	*/
X	do
X	{
X		charac = (line_status & LS_RCV_AVAIL)
X				? inb (RCV_DATA_PORT)
X				: 0;	/* was line status int only */
X
X		if (!(fip->cflag & CREAD) || !(ttyp->t_state & ISOPEN))
X			continue;
X
X		csize = 0;
X
X		if (fip->iflag & ISTRIP)
X			charac &= 0x7f;
X
X		if ((line_status & LS_PARITY_ERROR)
X			&& !(fip->iflag & INPCK))
X			line_status &= ~LS_PARITY_ERROR;
X
X		if (line_status & (LS_PARITY_ERROR
X					| LS_FRAMING_ERROR
X					| LS_BREAK_DETECTED))
X		{
X			if (line_status & LS_BREAK_DETECTED)
X			{
X				if (!(fip->iflag & IGNBRK))
X					if (fip->iflag & BRKINT)
X						(*linesw [ttyp->t_line].l_input)
X							(ttyp, L_BREAK);
X					else
X					{
X						metta [csize] = 0;
X						csize++;
X						if (fip->iflag & PARMRK)
X						{
X							metta [csize] = 0;
X							csize++;
X							metta [csize] = 0xff;
X							csize++;
X						}
X					}
X			}
X			else if (!(fip->iflag & IGNPAR))
X				if (fip->iflag & PARMRK)
X				{
X					metta [csize] = charac;
X					csize++;
X					metta [csize] = 0;
X					csize++;
X					metta [csize] = 0xff;
X					csize++;
X				}
X				else
X				{
X					metta [csize] = 0;
X					csize++;
X				}
X		}
X		else if (line_status & LS_RCV_AVAIL)
X		{
X			if (fip->iflag & IXON)
X			{
X				if (ttyp->t_state & TTSTOP)
X				{
X					if ((charac == CSTART)
X						|| (fip->iflag & IXANY))
X					{
X						ttyp->t_state &= ~TTSTOP;
X						/* fake transmitter busy flag
X						   to restart output
X						*/
X						fip->device_flags.s |=
X							DF_XMIT_BUSY;
X					}
X				}
X				else
X				{
X					if (charac == CSTOP)
X						ttyp->t_state |= TTSTOP;
X				}
X				if ((charac == CSTART) || (charac == CSTOP))
X					continue;
X			}
X
X			if ((charac == 0xff) && (fip->iflag & PARMRK))
X			{
X				metta [csize] = 0xff;
X				csize++;
X			}
X
X			metta [csize] = charac;
X			csize++;
X		}
X
X		if (fip->recv_ring_cnt > RECV_BUFF_SIZE - 4)
X			continue;
X
X		fip->recv_ring_cnt += csize;
X
X		/* store translation in ring buffer */
X		while (csize)
X		{
X			*fip->recv_ring_put_ptr = metta [--csize];
X			if (++fip->recv_ring_put_ptr
X				== &fip->recv_buffer [RECV_BUFF_SIZE])
X				fip->recv_ring_put_ptr
X					= &fip->recv_buffer [0];
X		}
X	} while ((line_status = inb (LINE_STATUS_PORT)) & LS_RCV_INT);
X
X	return (line_status);
X}
X
X/* Ring buffer -> UNIX buffer transfer function. */
Xstatic int
Xfas_rxfer (fip)
Xregister struct fas_info	*fip;
X{
X	register struct tty	*ttyp;
X	register uint	num_to_input;
X
X	ttyp = fip->tty;
X
X	num_to_input = (TTYHOG - 1) - ttyp->t_rawq.c_cc;
X
X	if (!num_to_input || !ttyp->t_rbuf.c_ptr)
X		return (1);	/* input buffer full */
X
X	if (fip->recv_ring_cnt < num_to_input)
X		num_to_input = fip->recv_ring_cnt;
X	if (ttyp->t_rbuf.c_count < num_to_input)
X		num_to_input = ttyp->t_rbuf.c_count;
X	if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X	{
X		if (INPUT_FIFO_SIZE * 2 < num_to_input)
X			num_to_input = INPUT_FIFO_SIZE * 2;
X	}
X	else
X	{
X		if (8 < num_to_input)
X			num_to_input = 8;
X	}
X
X	fip->recv_ring_cnt -= num_to_input;
X	ttyp->t_rbuf.c_count -= num_to_input;
X
X	while (num_to_input)
X	{
X		*ttyp->t_rbuf.c_ptr = *fip->recv_ring_take_ptr;
X		if (++fip->recv_ring_take_ptr
X				== &fip->recv_buffer [RECV_BUFF_SIZE])
X			fip->recv_ring_take_ptr = &fip->recv_buffer [0];
X		ttyp->t_rbuf.c_ptr++;
X		num_to_input--;
X	}
X
X	ttyp->t_rbuf.c_ptr -= ttyp->t_rbuf.c_size
X				- ttyp->t_rbuf.c_count;
X	(*linesw [ttyp->t_line].l_input) (ttyp, L_BUF);
X
X	return (0);
X}
X
X/* Several functions for flow control, character output and special event
X   requests and handling.
X*/
Xstatic void
Xfas_cmd (fip, ttyp, arg2)
Xregister struct fas_info	*fip;
Xregister struct tty	*ttyp;
Xint	arg2;
X{
X	uint	num_to_output, out_cnt, cbg_cnt;
X	REGVAR;
X
X	switch (arg2)
X	{
X	case T_TIME:	/* process delayed events */
X		/* handle break request */
X		if (fip->device_flags.i & DF_DO_BREAK)
X		{
X			if (fip->lcr.i & LC_SET_BREAK_LEVEL)
X			{
X				fip->lcr.c &= ~LC_SET_BREAK_LEVEL;
X				outb (LINE_CTL_PORT, fip->lcr.i);
X				fip->device_flags.s &= ~DF_DO_BREAK;
X				timeout (ttrstrt, ttyp,
X					fas_ctimes [fip->cflag & CBAUD]);
X				break;
X			}
X			else
X			{
X				fip->lcr.c |= LC_SET_BREAK_LEVEL;
X				outb (LINE_CTL_PORT, fip->lcr.i);
X					/* 250 msec */
X				timeout (ttrstrt, ttyp, HZ / 4);
X				break;
X			}
X		}
X		/* handle hangup request */
X		if (fip->device_flags.i & DF_DO_HANGUP)
X		{
X			if (fip->mcr.i & MC_SET_DTR)
X			{
X				fip->mcr.c &= ~MC_SET_DTR;
X				outb (MDM_CTL_PORT, fip->mcr.i);
X				timeout (ttrstrt, ttyp, HANGUP_TIME);
X				break;
X			}
X			else
X			{
X				ttyp->t_state &= ~TIMEOUT;
X				fip->device_flags.s &= ~(DF_DO_HANGUP
X							| DF_XMIT_DISABLED);
X				/* If there was a waiting getty open on this
X				   port, reopen the physical device.
X				*/
X				if (fip->o_state & OS_WAIT_OPEN)
X				{
X					fas_open_device (fip);
X					fas_param (fip);	/* set up port regs */
X					fas_mproc (fip);	/* set up mdm stat flags */
X				}
X				release_device_lock (fip);
X				break;
X			}
X		}
X		ttyp->t_state &= ~TIMEOUT;
X		fip->device_flags.s &= ~DF_XMIT_DISABLED;
X		/* FALL THRU */
X
X	case T_OUTPUT:	/* output characters to the transmitter */
Xstart:
X		/* proceed only if output not stopped internally */
X		if (fip->device_flags.i & (DF_HWOSTOP | DF_XMIT_DISABLED
X						| DF_XMIT_BUSY))
X			break;
X
X		/* determine the transmitter FIFO size */
X		num_to_output = (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X				? OUTPUT_FIFO_SIZE
X				: 1;
X
X		/* handle XON/XOFF input flow control requests */
X		if (fip->device_flags.i & DF_SW_FC_REQ)
X		{
X			outb (XMT_DATA_PORT, (fip->device_flags.i & DF_SWISTOP)
X						? CSTOP
X						: CSTART);
X			ttyp->t_state |= BUSY;
X			ttyp->t_state &= ~(TTXON | TTXOFF);
X			fip->device_flags.s |= DF_XMIT_BUSY;
X			fip->device_flags.s &= ~DF_SW_FC_REQ;
X			num_to_output--;
X		}
X
X		/* bail out if output is suspended by XOFF */
X		if (ttyp->t_state & TTSTOP)
X			break;
X
X		/* init cbuffer get counter */
X		cbg_cnt = 2;
X
X		/* Fill the transmitter FIFO. We limit the number of times
X                   a new cbuffer is requested because the request function
X                   is time consuming and this loop is running with interrupts
X                   disabled.
X		*/
X		while (num_to_output && cbg_cnt)
X		{
X			/* Check if tbuf is empty. If it is empty, reset buffer
X			   pointer and counter and get the next chunk of output
X			   characters.
X			*/
X			if (!ttyp->t_tbuf.c_ptr || !ttyp->t_tbuf.c_count)
X			{
X				if (ttyp->t_tbuf.c_ptr)
X					ttyp->t_tbuf.c_ptr -= ttyp->t_tbuf.c_size;
X				if (!((*linesw [ttyp->t_line].l_output) (ttyp)
X						& CPRES))
X					break;
X
X				cbg_cnt--;
X			}
X
X			/* Determine how many chars to put into the transmitter
X			   register.
X			*/
X			out_cnt = min (ttyp->t_tbuf.c_count, num_to_output);
X			num_to_output -= out_cnt;
X			ttyp->t_tbuf.c_count -= out_cnt;
X
X			/* output characters to the transmitter register */
X			while (out_cnt)
X			{
X				outb (XMT_DATA_PORT, *ttyp->t_tbuf.c_ptr);
X				ttyp->t_tbuf.c_ptr++;
X				out_cnt--;
X			}
X			/* signal that transmitter is busy now */
X			ttyp->t_state |= BUSY;
X			fip->device_flags.s |= DF_XMIT_BUSY;
X		}
X		break;
X
X	case T_SUSPEND:	/* suspend character output */
X		ttyp->t_state |= TTSTOP;
X		break;
X
X	case T_RESUME:	/* restart character output */
X		ttyp->t_state &= ~TTSTOP;
X		goto start;
X
X	case T_BLOCK:	/* stop character input, request XOFF */
X		ttyp->t_state |= TBLOCK;
X		break;	/* note: we do our own XON/XOFF */
X
X	case T_UNBLOCK:	/* restart character input, request XON */
X		ttyp->t_state &= ~TBLOCK;
X		break;	/* note: we do our own XON/XOFF */
X
X	case T_RFLUSH:	/* flush input buffers and restart input */
X		fip->recv_ring_take_ptr = fip->recv_ring_put_ptr;
X		fip->recv_ring_cnt = 0;
X		if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X			outb (FIFO_CTL_PORT, STANDARD_FIFO_SETUP
X						| FIFO_CLR_RECV);
X		if (fip->device_flags.i & DF_HWISTOP)
X		{
X			fip->mcr.c |= MC_SET_RTS;
X			outb (MDM_CTL_PORT, fip->mcr.i);
X			fip->device_flags.s &= ~DF_HWISTOP;
X		}
X		ttyp->t_state &= ~TBLOCK;
X		if (fip->device_flags.i & DF_SWISTOP)
X		{
X			fip->device_flags.s &= ~DF_SWISTOP;
X			fip->device_flags.s ^= DF_SW_FC_REQ;
X			if (fip->device_flags.i & DF_SW_FC_REQ)
X			{
X				ttyp->t_state |= TTXON;
X				goto start;
X			}
X			else
X				ttyp->t_state &= ~TTXOFF;
X		}
X		break;
X
X	case T_WFLUSH:	/* flush output buffer and restart output */
X		if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X			outb (FIFO_CTL_PORT, STANDARD_FIFO_SETUP
X						| FIFO_CLR_XMIT);
X		if (ttyp->t_tbuf.c_ptr)
X			ttyp->t_tbuf.c_ptr -= ttyp->t_tbuf.c_size
X						- ttyp->t_tbuf.c_count;
X		do
X		{
X			ttyp->t_tbuf.c_count = 0;
X		} while ((*linesw [ttyp->t_line].l_output) (ttyp) & CPRES);
X		ttyp->t_state &= ~TTSTOP;
X		break;
X
X	case T_BREAK:	/* do a break on the transmitter line */
X		/* set up break request flags */
X		fip->device_flags.s |= DF_DO_BREAK | DF_XMIT_DISABLED;
X		ttyp->t_state |= TIMEOUT;
X		timeout (ttrstrt, ttyp, fas_ctimes [fip->cflag & CBAUD]);
X		break;
X
X	case T_PARM:	/* set up the port according to the termio structure */
X		fas_param (fip);
X		break;
X
X	case T_SWTCH:	/* handle layer switch request */
X		break;
X	}
X}
X
X/* open device physically */
Xstatic void
Xfas_open_device (fip)
Xregister struct fas_info	*fip;
X{
X	REGVAR;
X
X	fip->device_flags.s &= DF_DEVICE_CONFIGURED | DF_DEVICE_HAS_FIFO
X				| DF_DEVICE_OPEN | DF_DEVICE_LOCKED;
X	fip->cflag = 0;
X	fip->iflag = 0;
X	fip->recv_ring_take_ptr = fip->recv_ring_put_ptr;
X	fip->recv_ring_cnt = 0;
X
X	fip->msr = inb (MDM_STATUS_PORT);
X	inb (RCV_DATA_PORT);
X	inb (RCV_DATA_PORT);
X	inb (LINE_STATUS_PORT);
X	inb (INT_ID_PORT);
X	if (INT_ACK_PORT)
X		outb (INT_ACK_PORT, fip->int_ack);
X	if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X		outb (FIFO_CTL_PORT, STANDARD_FIFO_INIT);
X	fip->ier.c = IE_INIT_MODE;
X	outb (INT_ENABLE_PORT, fip->ier.i);
X
X	fip->mcr.c |= MC_SET_DTR | MC_SET_RTS;
X	outb (MDM_CTL_PORT, fip->mcr.i);
X
X	fip->device_flags.s |= DF_DEVICE_OPEN;
X}
X
X/* close device physically */
Xstatic void
Xfas_close_device (fip)
Xregister struct fas_info	*fip;
X{
X	REGVAR;
X
X	fip->device_flags.s &= ~DF_DEVICE_OPEN;
X	fip->ier.c = IE_NONE;	/* disable all ints from UART */
X	outb (INT_ENABLE_PORT, fip->ier.i);
X	if (INT_ACK_PORT)
X		outb (INT_ACK_PORT, fip->int_ack);
X	if (fip->cflag & HUPCL)
X	{
X		fip->mcr.c &= ~MC_SET_RTS;
X		outb (MDM_CTL_PORT, fip->mcr.i);
X		/* request hangup */
X		fip->device_flags.s |= DF_DO_HANGUP | DF_XMIT_DISABLED;
X		fip->tty->t_state |= TIMEOUT;
X		timeout (ttrstrt, fip->tty,
X				max (fas_ctimes [fip->cflag & CBAUD],
X							HANGUP_DELAY));
X	}
X	if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X		outb (FIFO_CTL_PORT, STANDARD_FIFO_CLEAR);
X}
X
X/* test device thoroughly */
Xstatic int
Xfas_test_device (fip)
Xregister struct fas_info	*fip;
X{
X	register unchar	*cptr;
X	int	done;
X	REGVAR;
X
X	/* make sure FIFO is off */
X	outb (FIFO_CTL_PORT, STANDARD_FIFO_CLEAR);
X
X	/* set counter divisor */
X	outb (LINE_CTL_PORT, LC_ENABLE_DIVISOR);
X	outb (DIVISOR_LSB_PORT, fas_speeds [B38400]);
X	outb (DIVISOR_MSB_PORT, fas_speeds [B38400] >> 8);
X	outb (LINE_CTL_PORT, 0);
X
X	/* switch to local loopback */
X	outb (MDM_CTL_PORT, MC_SET_LOOPBACK);
X	delay (fas_ctimes [B38400]);
X
X	/* clear flags */
X	inb (RCV_DATA_PORT);
X	inb (RCV_DATA_PORT);
X	inb (LINE_STATUS_PORT);
X
X	/* test pattern */
X	cptr = (unchar *) "\377\125\252\045\244\0";
X
X	do
X	{
X		done = FALSE;
X
X		/* test transmitter and receiver with parity odd */
X		outb (LINE_CTL_PORT, LC_WORDLEN_8 | LC_ENABLE_PARITY);
X		if ((inb (LINE_STATUS_PORT) & (LS_XMIT_AVAIL | LS_XMIT_COMPLETE))
X			!= (LS_XMIT_AVAIL | LS_XMIT_COMPLETE))
X			break;
X
X		outb (XMT_DATA_PORT, *cptr);
X		delay (fas_ctimes [B38400]);
X		if ((inb (LINE_STATUS_PORT) & LS_RCV_INT) != LS_RCV_AVAIL)
X			break;
X
X		if (inb (RCV_DATA_PORT) != *cptr)
X			break;
X
X		/* test transmitter and receiver with parity even */
X		outb (LINE_CTL_PORT, LC_WORDLEN_8 | LC_ENABLE_PARITY
X					| LC_EVEN_PARITY);
X		if ((inb (LINE_STATUS_PORT) & (LS_XMIT_AVAIL | LS_XMIT_COMPLETE))
X			!= (LS_XMIT_AVAIL | LS_XMIT_COMPLETE))
X			break;
X
X		outb (XMT_DATA_PORT, *cptr);
X		delay (fas_ctimes [B38400]);
X		if ((inb (LINE_STATUS_PORT) & LS_RCV_INT) != LS_RCV_AVAIL)
X			break;
X
X		if (inb (RCV_DATA_PORT) != *cptr)
X			break;
X
X		done = TRUE;
X	} while (*cptr++);
X
X	if (done)
X	{
X		/* test pattern */
X		cptr = (unchar *) "\005\140\012\220\006\120\011\240\017\360\0\0";
X
X		do
X		{
X			done = FALSE;
X
X			/* test modem control and status lines */
X			outb (MDM_CTL_PORT, *cptr | MC_SET_LOOPBACK);
X			if ((inb (MDM_STATUS_PORT) & MS_ANY_PRESENT)
X				!= *(cptr + 1))
X				break;
X
X			done = TRUE;
X		} while (*((ushort *) cptr)++);
X	}
X
X	/* switch back to normal operation */
X	outb (MDM_CTL_PORT, 0);
X	delay (fas_ctimes [B38400]);
X
X	return (done);
X}
X
X#if defined (NEED_PUT_GETCHAR)
X
Xint
Xasyputchar (arg1)
Xunchar	arg1;
X{
X	register struct	fas_info	*fip;
X	REGVAR;
X
X	if (!fas_is_initted)
X		fasinit();
X
X	fip = &fas_info [0];
X	if (fip->device_flags.i & DF_DEVICE_CONFIGURED)
X	{
X		while (!(inb (LINE_STATUS_PORT) & LS_XMIT_AVAIL))
X			;
X		outb (XMT_DATA_PORT, arg1);
X		if (arg1 == 10)
X			asyputchar(13);
X	}
X	return(0);
X}
X
Xint
Xasygetchar ()
X{
X	register struct	fas_info	*fip;
X	REGVAR;
X
X	if (!fas_is_initted)
X		fasinit();
X
X	fip = &fas_info [0];
X	if ((fip->device_flags.i & DF_DEVICE_CONFIGURED)
X		&& (inb (LINE_STATUS_PORT) & LS_RCV_AVAIL))
X		return (inb (RCV_DATA_PORT));
X	else
X		return (-1);
X}
X#endif
X
X#if defined (NEED_INIT8250)
X
X/* reset the requested port to be used directly by a DOS process */
Xint
Xinit8250 (port, ier)
Xushort	port, ier;	/* ier not used in this stub */
X{
X	register struct fas_info	*fip;
X	register uint	physical_unit;
X	int	old_level;
X	REGVAR;
X
X	/* See if the port address matches a port that is used by
X	   the fas driver.
X	*/
X	for (physical_unit = 0; physical_unit < fas_physical_units;
X			physical_unit++)
X		if (port == fas_port [physical_unit])
X			break;
X
X	if (physical_unit >= fas_physical_units)
X		return(-1);	/* port didn't match */
X
X	fip = fas_info_ptr [physical_unit];
X
X	old_level = spltty ();
X
X	fip->ier.c = IE_NONE;
X	outb (INT_ENABLE_PORT, fip->ier.i);
X	if (INT_ACK_PORT)
X		outb (INT_ACK_PORT, fip->int_ack);
X
X	fip->mcr.c &= ~MC_SET_RTS;
X	outb (MDM_CTL_PORT, fip->mcr.i);
X
X	if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X		outb (FIFO_CTL_PORT, STANDARD_FIFO_CLEAR);
X
X	inb (MDM_STATUS_PORT);
X	inb (RCV_DATA_PORT);
X	inb (RCV_DATA_PORT);
X	inb (LINE_STATUS_PORT);
X	inb (INT_ID_PORT);
X	splx (old_level);
X	return (0);
X}
X#endif
SHAR_EOF
if test 42626 -ne "`wc -c < 'fas.c'`"
then
	echo shar: "error transmitting 'fas.c'" '(should have been 42626 characters)'
fi
fi
echo shar: "extracting 'fas.h'" '(9355 characters)'
if test -f 'fas.h'
then
	echo shar: "will not over-write existing file 'fas.h'"
else
sed 's/^X//' << \SHAR_EOF > 'fas.h'
X/* This file contains various defines for the FAS async driver.
X   If you change anything here you have to recompile the driver module.
X*/
X
X#ident	"@(#)fas.h	2.05"
X
X#include <sys/param.h>
X#include <sys/types.h>
X#include <sys/signal.h>
X#include <sys/buf.h>
X#include <sys/iobuf.h>
X#include <sys/dir.h>
X#include <sys/user.h>
X#include <sys/errno.h>
X#include <sys/tty.h>
X#include <sys/conf.h>
X#include <sys/sysinfo.h>
X#include <sys/file.h>
X#include <sys/termio.h>
X#include <sys/ioctl.h>
X#include <macros.h>
X
X#if defined (TRUE)
X#undef TRUE
X#endif
X#define	TRUE	(1)
X
X#if defined (FALSE)
X#undef FALSE
X#endif
X#define FALSE	(0)
X
X/* Uncomment the following line if you need asyputchar and asygetchar.
X   Bell Tech needs these.  uPort has them burried in the kd device.
X*/
X/* #define NEED_PUT_GETCHAR 1	/* */
X
X/* Uncomment the following line if you need init8250. DosMerge needs
X   this function, but only if you link the kernel without the original
X   asy driver.
X*/
X/* #define NEED_INIT8250	/* */
X
X/* Initial line control register.  Value will only be meaningfull for
X   asyputchar and asygetchar and they are only meaningfull if
X   NEED_PUT_GETCHAR is defined.
X*/
X#define	INITIAL_LINE_CONTROL	LC_WORDLEN_8
X
X/* Initial baud rate.  Value will only be meaningfull for
X   asyputchar and asygetchar and they are only meaningfull if
X   NEED_PUT_GETCHAR is defined.
X*/
X#define INITIAL_BAUD_RATE	(BAUD_BASE/9600)
X
X/* Initial modem control register.  This should probably not have to
X   be touched.  It is here because some terminals used as the console
X   require one or more of the modem signals set.
X*/
X#define INITIAL_MDM_CONTROL	0
X
X/****************************************************/
X/* Nothing past this line should have to be changed */
X/****************************************************/
X
X#define NUM_INT_VECTORS	16	/* sixteen vectors possible but only
X				   the first eight are normally used
X				*/
X
X#define MAX_UNITS	16	/* we will only use that many units */
X
X/* Miscellaneous Constants */
X
X#define BAUD_BASE	(1843200 / 16)	/* 115200 bps */
X#define HANGUP_DELAY	((HZ) / 10)	/* 100 msec */
X#define HANGUP_TIME	((HZ) / 2)	/* 500 msec */
X#define	RECV_BUFF_SIZE	1024		/* receiver ring buffer size */
X#define SW_LOW_WATER	512		/* sw flow control trigger levels */
X#define SW_HIGH_WATER	768
X#define HW_LOW_WATER	896		/* hw flow control trigger levels */
X#define HW_HIGH_WATER	960
X
X/* define the local open flags */
X
X#define OS_DEVICE_CLOSED	0x0000
X#define OS_OPEN_FOR_DIALOUT	0x0001
X#define OS_OPEN_FOR_GETTY	0x0002
X#define OS_WAIT_OPEN		0x0004
X#define OS_NO_DIALOUT		0x0008
X#define OS_CHECK_CARR_ON_OPEN	0x0010
X#define OS_FAKE_CARR_ON		0x0020
X#define OS_UNBLOCK_ON_RING	0x0040
X#define OS_CLOCAL		0x0080
X#define OS_HW_HANDSHAKE		0x0100
X
X#define OS_OPEN_STATES		(OS_OPEN_FOR_DIALOUT | OS_OPEN_FOR_GETTY)
X#define OS_TEST_MASK		(OS_OPEN_FOR_DIALOUT | OS_NO_DIALOUT \
X				| OS_CHECK_CARR_ON_OPEN | OS_FAKE_CARR_ON \
X				| OS_UNBLOCK_ON_RING | OS_CLOCAL \
X				| OS_HW_HANDSHAKE)
X
X/* define the hardware handshake flags */
X
X#define HH_CTS	MS_CTS_PRESENT
X#define HH_DSR	MS_DSR_PRESENT
X#define HH_BOTH	(MS_CTS_PRESENT | MS_DSR_PRESENT)
X
X/* define the device status flags */
X
X#define DF_DEVICE_CONFIGURED	0x0001	/* device is configured */
X#define DF_DEVICE_HAS_FIFO	0x0002	/* it's an NS16550 */
X#define DF_DEVICE_OPEN		0x0004	/* physical device is open */
X#define DF_DEVICE_LOCKED	0x0008	/* physical device locked */
X#define DF_SWISTOP		0x0010	/* input stopped by sw flow control */
X#define DF_SW_FC_REQ		0x0020	/* sw input flow control request */
X#define	DF_HWOSTOP		0x0040	/* output stopped by hw handshake */
X#define DF_HWISTOP		0x0080	/* input stopped by hw handshake */
X#define DF_XMIT_DISABLED	0x0100	/* transmitter is disabled */
X#define DF_XMIT_BUSY		0x0200	/* transmitter is busy */
X#define DF_DO_BREAK		0x0400	/* delayed BREAK request */
X#define DF_DO_HANGUP		0x0800	/* delayed hangup request */
X
X/* description of the NS16X50 Asychronous Communications Element */
X
X#define RCV_DATA_OFFSET		0
X#define XMT_DATA_OFFSET		0
X#define INT_ENABLE_OFFSET	1
X#define INT_ID_OFFSET		2
X#define FIFO_CTL_OFFSET		2
X#define LINE_CTL_OFFSET		3
X#define MDM_CTL_OFFSET		4
X#define LINE_STATUS_OFFSET	5
X#define MDM_STATUS_OFFSET	6
X#define DIVISOR_LSB_OFFSET	0
X#define DIVISOR_MSB_OFFSET	1
X
X/* define an easy way to refenence the absolute port addresses */
X
X#define RCV_DATA_PORT		(fip->rec_data_port)
X#define XMT_DATA_PORT		(fip->xmt_data_port)
X#define INT_ENABLE_PORT		(fip->int_ena_port)
X#define INT_ID_PORT		(fip->int_id_port)
X#define FIFO_CTL_PORT		(fip->fifo_ctrl_port)
X#define LINE_CTL_PORT		(fip->line_ctrl_port)
X#define MDM_CTL_PORT		(fip->mdm_ctrl_port)
X#define LINE_STATUS_PORT	(fip->line_stat_port)
X#define MDM_STATUS_PORT		(fip->mdm_stat_port)
X#define DIVISOR_LSB_PORT	(fip->div_lsb_port)
X#define DIVISOR_MSB_PORT	(fip->div_msb_port)
X#define INT_ACK_PORT		(fip->int_ack_port)
X
X/* modem status port */
X
X#define MS_CTS_CHANGED		0x01
X#define MS_DSR_CHANGED		0x02
X#define MS_RING_CHANGED		0x04
X#define MS_DCD_CHANGED		0x08
X#define MS_CTS_PRESENT		0x10
X#define MS_DSR_PRESENT		0x20
X#define MS_RING_PRESENT		0x40
X#define MS_DCD_PRESENT		0x80
X
X#define MS_ANY_PRESENT	(MS_CTS_PRESENT | MS_DSR_PRESENT | MS_RING_PRESENT \
X				| MS_DCD_PRESENT)
X
X/* interrupt enable port */
X
X#define IE_NONE				0x00
X#define	IE_RECV_DATA_AVAILABLE		0x01
X#define	IE_XMIT_HOLDING_BUFFER_EMPTY	0x02
X#define IE_LINE_STATUS			0x04
X#define IE_MODEM_STATUS			0x08
X
X#define IE_INIT_MODE	(IE_RECV_DATA_AVAILABLE | IE_XMIT_HOLDING_BUFFER_EMPTY \
X			| IE_LINE_STATUS | IE_MODEM_STATUS)
X
X/* interrupt id port */
X
X#define II_NO_INTS_PENDING	0x01
X#define II_CODE_MASK		0x07
X#define II_MODEM_STATE		0x00
X#define II_XMTD_CHAR		0x02
X#define II_RCVD_CHAR		0x04
X#define II_RCV_ERROR		0x06
X#define II_FIFO_TIMEOUT		0x08
X#define II_FIFO_ENABLED		0xC0
X
X/* line control port */
X
X#define	LC_WORDLEN_MASK		0x03
X#define	LC_WORDLEN_5		0x00
X#define	LC_WORDLEN_6		0x01
X#define	LC_WORDLEN_7		0x02
X#define	LC_WORDLEN_8		0x03
X#define LC_STOPBITS_LONG	0x04
X#define LC_ENABLE_PARITY	0x08
X#define LC_EVEN_PARITY		0x10
X#define LC_STICK_PARITY		0x20
X#define LC_SET_BREAK_LEVEL	0x40
X#define LC_ENABLE_DIVISOR	0x80
X
X/* modem control port */
X
X#define MC_SET_DTR		0x01
X#define MC_SET_RTS		0x02
X#define MC_SET_OUT1		0x04
X#define MC_SET_OUT2		0x08	/* tristates int line when false */
X#define MC_SET_LOOPBACK		0x10
X
X/* line status port */
X
X#define LS_RCV_AVAIL		0x01
X#define LS_OVERRUN		0x02
X#define LS_PARITY_ERROR		0x04
X#define LS_FRAMING_ERROR	0x08
X#define LS_BREAK_DETECTED	0x10
X#define LS_XMIT_AVAIL		0x20
X#define LS_XMIT_COMPLETE	0x40
X 
X#define LS_RCV_INT	(LS_RCV_AVAIL | LS_OVERRUN | LS_PARITY_ERROR \
X			| LS_FRAMING_ERROR | LS_BREAK_DETECTED)
X
X/* fifo control port (NS16550 only) */
X
X#define	FIFO_ENABLE	0x01
X#define	FIFO_CLR_RECV	0x02
X#define	FIFO_CLR_XMIT	0x04
X#define	FIFO_START_DMA	0x08
X#define FIFO_SIZE_1	0x00
X#define FIFO_SIZE_4	0x40
X#define FIFO_SIZE_8	0x80
X#define FIFO_SIZE_14	0xC0
X#define FIFO_SIZE_MASK	0xC0
X
X#define STANDARD_FIFO_CLEAR	0
X#define STANDARD_FIFO_SETUP	(FIFO_SIZE_8 | FIFO_ENABLE)
X#define STANDARD_FIFO_INIT	(STANDARD_FIFO_SETUP | FIFO_CLR_RECV \
X				| FIFO_CLR_XMIT)
X
X#define INPUT_FIFO_SIZE		16
X#define OUTPUT_FIFO_SIZE	16
X
X
X/* This structure contains everything one would like to know about
X   an open device.  There is one of these for each physical unit.
X
X   We use several unions to eliminate most integer type conversions
X   at run-time. The standard UNIX V 3.X/386 C compiler forces all
X   operands in expressions and all function parameters to type int.
X   To save some time, with the means of unions we deliver type int
X   at the proper locations while dealing with the original type
X   wherever int would be slower.
X
X   This is highly compiler implementation specific. But for the sake
X   of speed the end justifies the means.
X*/
X
Xstruct	fas_info
X{
X	struct	tty	*tty;	/* the tty structure */
X	struct	fas_info *next_interrupt_user;/* link to next fas_info struct */
X	uint	iflag;		/* current terminal input flags */
X	uint	cflag;		/* current terminal hardware control flags */
X	union {			/* flags about the device */
X		ushort	s;
X		uint	i;
X	} device_flags;
X	uint	o_state;	/* current open state */
X	uint	po_state;	/* previous open state */
X	uint	h_mask;		/* hardware handshake bit mask */
X	uint	msr;		/* modem status register value */
X	union {			/* modem control register value */
X		unchar	c;
X		uint	i;
X	} mcr;
X	union {			/* line control register value */
X		unchar	c;
X		uint	i;
X	} lcr;
X	union {			/* interrupt enable register value */
X		unchar	c;
X		uint	i;
X	} ier;
X	uint	rec_data_port;	/* receive data port */
X	uint	xmt_data_port;	/* transmit data port */
X	uint	int_ena_port;	/* interrupt mask port */
X	uint	int_id_port;	/* interrupt identification port */
X	uint	fifo_ctrl_port;	/* fifo control port (NS16550 only) */
X	uint	line_ctrl_port;	/* line control port */
X	uint	mdm_ctrl_port;	/* modem control port */
X	uint	line_stat_port;	/* line status port */
X	uint	mdm_stat_port;	/* modem status port */
X	uint	div_lsb_port;	/* divisor lsb latch port */
X	uint	div_msb_port;	/* divisor msb latch port */
X	uint	int_ack_port;	/* int ack port */
X	uint	int_ack;	/* int ack value */
X	uint	recv_ring_cnt;	/* receiver ring buffer counter */
X	unchar	*recv_ring_put_ptr;	/* recv ring buf put ptr */
X	unchar	*recv_ring_take_ptr;	/* recv ring buf take ptr */
X	unchar	recv_buffer [RECV_BUFF_SIZE];	/* recv ring buf */
X};
SHAR_EOF
if test 9355 -ne "`wc -c < 'fas.h'`"
then
	echo shar: "error transmitting 'fas.h'" '(should have been 9355 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
Uwe Doering   |  Domain   : gemini@netmbx.UUCP
Berlin        |---------------------------------------------------------------
West Germany  |  Bangpath : ...!uunet!unido!tmpmbx!netmbx!gemini