FAS 2.06 async driver, part 1/2

Uwe Doering gemini at geminix.mbx.sub.org
Sun Mar 25 03:01:42 AEST 1990


Hi netlanders!

Here is FAS 2.06, the successor of the now wide-spread FAS 2.05 public
domain async driver for i386 based UNIX flavors.

This posting is long overdue because we've had some trouble with the
news distribution here in Germany. Sorry.

The driver was tested under ISC 386/ix 2.0.2, MicroPort SYSV/386
Vers. 3.0e and has been reported to work with ESIX, too. A verbal
description of how to modify FAS for SCO UNIX was posted recently in
comp.unix.i386 (message IDs: <7848 at netcom.UUCP> and <7854 at netcom.UUCP>).

Here are the basic features:

-  It supports the NS16550 UART chip in FIFO mode.
-  Modem sharing for dial-in and dial-out on the same port is possible.
-  It has hardware flow control.

New highlights in this release are:

-  Large input and output buffers. This makes throughput less dependant on
   system load (at least with streaming protocols).
-  Fully configurable modem and hardware handshake control lines. There are
   no hard-wired control lines in the driver itself. Therefor, if you need
   a special control line configuration you don't need to wade through
   the driver source code.
-  More bullet-proof against certain event combinations. Uucp e-protocol
   works quite reliable now if you have an FAS 2.06 on both sides.
-  Even faster than FAS 2.05, that is, it eats up less CPU time.
-  Many other bug fixes and improvements.

For newcomers: You need to replace the 8250 or NS16450 chips on your
serial card with NS16550A chips if you want to use speeds >2400 bps
without losing input characters.

Note: I won't email you any sources or patches because international
mailing costs are prohibitive in West Germany. But of course I will
post patches.

     Uwe


#! /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:
#	INSTALLATION
#	fas.c
# This archive created: Sat Mar 24 16:49:44 1990
export PATH; PATH=/bin:/usr/bin:$PATH
echo shar: "extracting 'INSTALLATION'" '(7535 characters)'
if test -f 'INSTALLATION'
then
	echo shar: "will not over-write existing file 'INSTALLATION'"
else
sed 's/^X//' << \SHAR_EOF > 'INSTALLATION'
XInstallation guide for the FAS Final Async Solution driver
X----------------------------------------------------------
X
XTo install this driver you need a manual of your serial card,
Xyour system manuals and a certain knowledge about what actually
Xis a serial driver.
X
XYou have to be user root to compile and install the driver!
X
X
XCompilation phase
X=================
X
XFirst of all you should copy the makefile that matches your system
Xto the file `makefile'. Make sure that the makefile contains the
Xproper compiler switches for your system. You may also look at
X`fas.c' to find out what defines are possible for conditional
Xcompilation. You should also check the paths and file names used
Xin the makefile.
X
XAfter that you chose one of the space-xxxxx configuration files
Xthat matches your serial card and copy this to `space.c'. If you
Xdon't find a matching space file you should copy the one that
Xcomes closest to your card to `space.c'.
X
XIn any case you should check the entries in that file against your
Xcard's manual and jumper settings. The space files contain explanations
Xabout what data you have to enter.
X
XIf your `space.c' is filled in properly you simply type `make' to
Xcompile the driver. If you don't get error messages you may begin
Xwith the actual installation. Otherwise you have to find the cause of
Xthe trouble. Some error reasons may be missing include files, macros
Xthat are defined in different include files or missing at all, you
Xuse an ANSI compiler that complains about non-ANSI constructions.
XIn the latter case you need to convince the compiler somehow to
Xwork in non-ANSI (standard C) mode.
X
X
XInstallation phase
X==================
X
XIf you want to have both the original asy and the new FAS driver in
Xthe kernel the only restriction is that ports and interrupt vectors
Xcan't be shared between the two drivers. Each driver controls its own
Xseparate set of ports and IRQs.
X
XuPort: Copy one of the config-xxxxx files to `config'. Check out
X       whether the interrupt vectors in this file reflect the
X       jumper settings on your serial card. Note that an IRQ2 on
X       your card is an interrupt vector of 9. All other IRQ numbers
X       correspond to the vector number, though.
X
X       Next you have to tell the config program to include the new
X       driver at kernel link time. This is done by a line in the file
X       /etc/atconf/systems/system.std. Look for the line containing
X       `asy' followed by an asterisk (`*') and a comment. Create a
X       similar line where `asy' is substituted with `fas'. Add a
X       proper comment. If you don't want to use any ports under the
X       DOS emulator you should insert an asterisk at the beginning
X       of the line that contains the word `asy'. This excludes the
X       asy driver from the kernel. But if you want to have it in the
X       kernel you have to configure it to only use the ports you
X       need under DOS. The remaining ports should be controlled by
X       the FAS driver.
X
X       Now type `make install' and after that you are ready to link
X       a new kernel. Refer to your system manuals on how to do that.
X
X       Before you reboot the new kernel make sure that you create
X       the proper tty device nodes in /dev. But first you should
X       remove all device nodes belonging to the original asy driver
X       that you don't need any more.
X
X       Now create your own tty device nodes that fit your needs.
X       The default device name prefix for the FAS driver is `ttyF'.
X       That is, ttyF00, ttyF01 ... and ttyFM00, ttyFM01 ...
X       You may chose another prefix, but note that some utilities
X       like uustat depend on tty-devices beginning with `tty'!
X
X       The default value for the major device number is 4, and
X       sane values for the minor device numbers are 48 + device #
X       for the dialout node and 208 + device # for the dialin (getty)
X       node. Device # counts from 0 and reflects the actual port number.
X       Refer to fas.c (function `fasopen') for a description of the
X       possible minor device numbers.
X
X       Remember to change the inittab file to the new device names.
X       Other files that contain tty-names for some reason should be
X       updated, too (ttytype, Devices etc.).
X
X       After you have booted the new kernel, provided your configuration
X       was correct, you should be able to use the serial devices.
X
X
XISC:   Copy one of the s_fas-xxxxx files to `s_fas'. Make sure
X       that you have a separate line in it for each block of contiguous
X       port addresses assigned to the same interrupt vector (check
X       the jumper settings on your serial card). Each line contains
X       the number of ports bound to that interrupt, the start and
X       end address of the first (lowest or only) port on this interrupt
X       line and other data. Refer to your ISC manuals if you need to
X       change this file. Note that an IRQ2 on your card is an interrupt
X       vector of 9. All other IRQ numbers correspond to the vector
X       number, though.
X
X       Then you copy one of the n_fas-xxxxx files to `n_fas'. This
X       file contains data needed to automatically create tty device
X       nodes at installation time. Things you may want to change are
X       the tty node name and the minor device number (last field) for
X       that node. Make sure you have a node for every port on your
X       serial card in this file.
X
X       Sane values for the minor device numbers are 48 + device #
X       for the dialout node and 208 + device # for the dialin (getty)
X       node. Device # counts from 0 and reflects the actual port number.
X       Refer to fas.c (function `fasopen') for a description of the
X       possible minor device numbers.
X
X       Now copy one of the i_fas-xxxxx files to `i_fas'. This file
X       contains the getty lines for the inittab file which is rebuild
X       every time a new kernel is installed. Make sure that you use the
X       same device names here as in the file `n_fas'.
X
X       Next you have to modify the configuration files for your original
X       asy driver to create only those devices you need for the DOS
X       emulator. Make sure that the asy driver only controls ports and
X       interrupt vectors not used by the FAS driver. If you don't need
X       the asy driver at all you can exclude it from the kernel.
X
X       To tell the kernel config program how to link in the FAS driver
X       you have to add the following line to the file /etc/conf/cf.d/mdevice:
X
X       fas	Iocrwi	iHct	fas	0	4	1	16	-1
X
X       The 6th field contains the major device number of the driver. You
X       may change this if it collides with another driver.
X
X       All this is in the ISC manuals. Read them carefully.
X
X       Now type `make install' and after that you are ready to link
X       a new kernel. Refer to your system manuals on how to do that.
X
X       You may have to change files that contain tty-names for some
X       reason to the new device names (ttytype, Devices etc.). Take
X       the names from `n_fas'.
X
X       After you have booted the new kernel, provided your configuration
X       was correct, you should be able to use the serial devices.
X
X
XOther flavors of UNIX
X=====================
X
XCheck out if one of the above installation schemes is similar to the
Xone you need for your system. Make the appropriate changes and try
Xout if it works. If your system is entirely different you have to
Xfind out from your manuals how to install device drivers. But if
Xyou use a UNIX SYSV/386 3.X you should get it to work eventually.
X
XGood luck.
SHAR_EOF
if test 7535 -ne "`wc -c < 'INSTALLATION'`"
then
	echo shar: "error transmitting 'INSTALLATION'" '(should have been 7535 characters)'
fi
fi
echo shar: "extracting 'fas.c'" '(50067 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 at m2c.m2c.org
XUSA                     voice (508) 366-2813
X*/
X
X/* Current author:
XUwe Doering		gemini at geminix.mbx.sub.org
XBillstedter Pfad 17 B
X1000 Berlin 20
XWest Germany
X*/
X
X#ident	"@(#)fas.c	2.06"
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 (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 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
Xextern uint	inb ();
Xextern void	outb ();
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 void	fas_xproc ();
Xstatic int	fas_rxfer ();
Xstatic int	fas_xxfer ();
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 void	ttrstrt ();
Xextern void	ttinit ();
Xextern int	ttiocom ();
Xextern void	ttyflush ();
Xextern uint	spltty ();
Xextern uint	splx ();
Xextern int	sleep ();
Xextern void	wakeup ();
Xextern void	signal ();
Xextern uint	timeout ();
Xextern void	untimeout ();
Xextern void	delay ();
Xextern void	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_modem [];
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 pointers to the first fas_info structure for each
X   interrupt vector
X*/
Xstatic struct fas_info	*fas_first_int_user [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*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 5 seconds with continuous character flow.
X*/
Xstatic uint	fas_xbuf_size [CBAUD + 1] =
X{	0,		50/2,
X	75/2,		110/2,
X	269/4,		150/2,
X	200/2,		300/2,
X	600/2,		1200/2,
X	1800/2,		2400/2,
X	4800/2,		9600/2,
X	19200/2,	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_ENABLE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_UNBLOCK_ENABLE | 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_ENABLE,
X	OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_UNBLOCK_ENABLE | 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		(void) 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			fip->uart_port_0 = port;
X			fip->uart_port_1 = port + 1;
X			fip->uart_port_2 = port + 2;
X			fip->uart_port_3 = port + 3;
X			fip->uart_port_4 = port + 4;
X			fip->uart_port_5 = port + 5;
X			fip->uart_port_6 = port + 6;
X			fip->int_ack_port = fas_int_ack_port [unit];
X			fip->int_ack = fas_int_ack [unit];
X			fip->vec = fas_vec [unit];
X			fip->modem.i = fas_modem [unit];
X			fip->flow.i = fas_flow [unit];
X			fip->recv_ring_put_ptr = fip->recv_buffer;
X			fip->recv_ring_take_ptr = fip->recv_buffer;
X			fip->xmit_ring_put_ptr = fip->xmit_buffer;
X			fip->xmit_ring_take_ptr = fip->xmit_buffer;
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->lcr.c = 0;
X			outb (LINE_CTL_PORT, fip->lcr.i);
X			fip->mcr.i = fas_mcb [unit];
X			outb (MDM_CTL_PORT, fip->mcr.i);
X
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			{
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 [fip->vec])
X				outb (port, fas_mux_ack [fip->vec]);
X
X			/* show that it is present and configured */
X			fip->device_flags.s |= DF_DEVICE_CONFIGURED;
X		}
X	}
X
X#if defined (NEED_PUT_GETCHAR)
X	fip = &fas_info [0];
X	fip->mcr.c |= 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#endif
X
X	port_stat [unit] = '\0';
X	printf ("\nFAS 2.06.0 async driver: Port 0-%d init state is [%s]\n\n",
X			unit - 1,
X			port_stat);
X
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 carrier signal is totally ignored. With carrier high->low
X		  *no* SIGHUP signal is generated.
X	    0 1   After an initial open, the carrier signal is ignored.
X		  Although, carrier high->low generates a SIGHUP signal. From
X		  thereon the device is carrier controlled until the last
X		  process has closed the device. An ioctl call with a TCSETA*
X                  command resets the device to ignore carrier again until the
X                  next carrier high->low.
X	    1 0   The device is carrier controlled. Additionally, if on open
X		  the carrier signal is low, a SIGHUP signal is sent
X                  immediately.
X	    1 1   The device behaves the same as with mode `0 1'. Additionally,
X		  if on open the carrier 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 carrier controlled.
X	    0 1   The device is carrier controlled. An unblock signal wakes
X                  up the waiting open and I/O is possible regardless of
X                  carrier until a carrier high->low. Thereafter the device
X                  is again fully carrier 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 carrier controlled, this implies the generation of
X	 a SIGHUP signal with every carrier 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		(void) 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 carrier 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		(void) 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, fip->msr);	/* set up modem status flags */
X	}
X
X	/* If getty open and the FNDELAY flag is not set,
X	   block and wait for carrier.
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			(void) 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 & fip->modem.m.ca)
X		&& !(fip->cflag & CLOCAL))
X	{
X		signal (ttyp->t_pgrp, SIGHUP);
X		ttyflush (ttyp, FREAD | FWRITE);
X	}
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*/
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, fip->msr);	/* 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	(void) 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		(void) 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 hardware handshake, restart input.
X	*/
X	if ((fip->device_flags.i & DF_HWI_STOPPED)
X		&& (fip->recv_ring_cnt < HW_LOW_WATER))
X	{
X		fip->mcr.c |= fip->flow.m.ic;
X		outb (MDM_CTL_PORT, fip->mcr.i);
X		fip->device_flags.s &= ~DF_HWI_STOPPED;
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_SWI_STOPPED)
X		&& (fip->recv_ring_cnt < SW_LOW_WATER))
X	{
X		fip->device_flags.s &= ~DF_SWI_STOPPED;
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	(void) splx (old_level);
X	return(0);
X}
X
X/* write characters to the output buffer */
Xint
Xfaswrite (dev)
Xint	dev;
X{
X	register struct fas_info	*fip;
X	register struct tty	*ttyp;
X	int	old_level;
X
X	fip = fas_info_ptr [GET_UNIT (dev)];
X
X	ttyp = fip->tty;
X
X	(*linesw [ttyp->t_line].l_write) (ttyp);
X
X	old_level = spltty ();
X
X	/* fill the transmitter ring buffer as much as possible */
X	while (!fas_xxfer (fip))
X	{
X		(void) splx (old_level);	/* allow some interrupts */
X		old_level = spltty ();
X	}
X
X	(void) splx (old_level);
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		(void) 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	(void) 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	int	xmit_ring_size;
X	REGVAR;
X
X	cflag = fip->tty->t_cflag;
X
X	/* hangup line if it is baud rate 0, else enable line */
X	if ((cflag & CBAUD) == B0)
X	{
X		cflag = (cflag & ~CBAUD) | (fip->cflag & CBAUD);
X		fip->mcr.c &= ~fip->modem.m.en;
X		outb (MDM_CTL_PORT, fip->mcr.i);
X		fip->device_flags.s &= ~DF_MODEM_ENABLED;
X	}
X	else
X	{
X		if (!(fip->device_flags.i & DF_MODEM_ENABLED))
X		{
X			fip->mcr.c |= fip->modem.m.en;
X			outb (MDM_CTL_PORT, fip->mcr.i);
X			fip->device_flags.s |= DF_MODEM_ENABLED;
X		}
X	}
X
X	/* don't change break flag */
X	fip->lcr.c &= LC_SET_BREAK_LEVEL;
X
X	/* set character size */
X	switch (cflag & CSIZE)
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	default:
X		fip->lcr.c |= LC_WORDLEN_8;
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	/* We write the counter divisor and the LCR register only
X	   if we need to (whenever the corresponding cflag bits
X	   have changed). This prevents character corruption under
X	   some special conditions.
X	*/
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		outb (LINE_CTL_PORT, fip->lcr.i);
X		/* check dynamic xmit ring buffer size against boundaries,
X                   modify it if necessary and update the fas_info structure
X		*/
X		xmit_ring_size = fas_xbuf_size [cflag & CBAUD] - TTXOHI;
X		if (xmit_ring_size < OUTPUT_FIFO_SIZE * 2)
X			xmit_ring_size = OUTPUT_FIFO_SIZE * 2;
X		if (xmit_ring_size > XMIT_BUFF_SIZE)
X			xmit_ring_size = XMIT_BUFF_SIZE;
X		fip->xmit_ring_size = xmit_ring_size;
X		/* disable the transmitter for some time to give the
X		   receiving side a chance to follow this change (only if
X		   there has been output recently)
X		*/
X		if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
X		{
X			if (!(fip->tty->t_state & BUSY))
X				fip->device_flags.s |= DF_XMIT_DISABLED;
X		}
X	}
X	else if ((cflag ^ fip->cflag) & (CSIZE | CSTOPB | PARENB | PARODD))
X	{
X		outb (LINE_CTL_PORT, fip->lcr.i);
X		/* disable the transmitter for some time to give the
X		   receiving side a chance to follow this change (only if
X		   there has been output recently)
X		*/
X		if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
X		{
X			if (!(fip->tty->t_state & BUSY))
X				fip->device_flags.s |= DF_XMIT_DISABLED;
X		}
X	}
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 & fip->modem.m.ca)
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#if defined (XCLUDE)	/* SYSV 3.2 Xenix compatibility */
X	if (cflag & XCLUDE)
X		fip->o_state |= OS_EXCLUSIVE_OPEN;
X	else
X		fip->o_state &= ~OS_EXCLUSIVE_OPEN;
X#endif
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	status;
X	int	done;
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	do
X	{
X		done = TRUE;
X		fip = fas_first_int_user [vect];
X
X		/* loop through all users of this interrupt vector */
X		for (; ; fip = fip->next_int_user)
X		{
X			if (!fip)
X				break;	/* all users done */
X
X			/* process only ports that we expect ints from
X			   and that actually need to be serviced
X			*/
Xfastloop:
X			if (inb (INT_ID_PORT) & II_NO_INTS_PENDING)
X			{
X				/* speed beats beauty */
X				fip = fip->next_int_user;
X				if (fip)
X					goto fastloop;
X				break;
X			}
X
X			done = FALSE;	/* not done if we got an int */
X
X			do
X			{
X				/* read in all the characters from the FIFO */
X				if ((status = inb (LINE_STATUS_PORT))
X					& LS_RCV_INT)
X				{
X					status = fas_rproc (fip, status);
X					sysinfo.rcvint++;
X				}
X
X				/* Is it a transmitter empty int ? */
X				if ((status & LS_XMIT_AVAIL)
X					&& (fip->device_flags.i & DF_XMIT_BUSY))
X				{
X					fip->device_flags.s &= ~DF_XMIT_BUSY;
X					fas_cmd (fip, fip->tty, T_OUTPUT);
X					if (!(fip->device_flags.i & DF_XMIT_BUSY)
X						&& !fip->xmit_ring_cnt)
X					{
X						fip->device_flags.s |=
X							DF_GUARD_TIMEOUT
X							| DF_ADAPT_TIMEOUT;
X						fip->tty->t_state |=
X							TIMEOUT;
X						fip->timeout_idx =
X							timeout (
X							ttrstrt, fip->tty,
X							fas_ctimes [fip->cflag
X								& CBAUD]);
X					}
X					sysinfo.xmtint++;
X				}
X
X				/* Has there been a polarity change on
X				   some of the modem lines ?
X				*/
X				if (((status = inb (MDM_STATUS_PORT))
X					^ fip->msr) & MS_ANY_PRESENT)
X				{
X					fas_mproc (fip, status);
X					sysinfo.mdmint++;
X				}
X			} while (!(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
X			/* process the characters in the ring buffer */
X			if (fip->recv_ring_cnt)
X			{
X				(void) fas_rxfer (fip);
X				/* If input buffer level has risen above the
X				   high water mark and input is not yet
X				   stopped, stop input by hardware handshake.
X				*/
X				if ((fip->o_state & OS_HW_HANDSHAKE)
X					&& !(fip->device_flags.i & DF_HWI_STOPPED)
X					&& (fip->recv_ring_cnt > HW_HIGH_WATER))
X				{
X					fip->mcr.c &= ~fip->flow.m.ic;
X					outb (MDM_CTL_PORT, fip->mcr.i);
X					fip->device_flags.s |= DF_HWI_STOPPED;
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_SWI_STOPPED)
X					&& (fip->recv_ring_cnt > SW_HIGH_WATER))
X				{
X					fip->device_flags.s |= DF_SWI_STOPPED;
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	} while (!done);
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, mdm_status)
Xregister struct fas_info	*fip;
Xregister uint	mdm_status;
X{
X	register struct tty	*ttyp;
X
X	ttyp = fip->tty;
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_status & fip->flow.m.oc)
X			|| (~mdm_status & fip->flow.m.oe))
X		{
X			if (fip->device_flags.i & DF_HWO_STOPPED)
X			{
X				fip->device_flags.s &= ~DF_HWO_STOPPED;
X				fas_cmd (fip, ttyp, T_OUTPUT);
X			}
X		}
X		else
X			fip->device_flags.s |= DF_HWO_STOPPED;
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_status & fip->modem.m.ca))
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 & fip->modem.m.ca))
X			{
X				ttyp->t_state &= ~CARR_ON;
X				if (ttyp->t_state & ISOPEN)
X					signal (ttyp->t_pgrp, SIGHUP);
X				ttyflush (ttyp, FREAD | FWRITE);
X			}
X		}
X	}
X
X	/* Check the unblock signal. If low->high edge, fake CARR_ON state
X	   flag and wake up getty open.
X	*/
X	if ((fip->o_state & OS_UNBLOCK_ENABLE)
X		&& !(fip->cflag & CLOCAL)
X		&& !(~mdm_status & fip->modem.m.ub)
X		&& (~fip->msr & fip->modem.m.ub)
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_status;
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 (fip->device_flags.i & DF_SWO_STOPPED)
X				{
X					if ((charac == CSTART)
X						|| (fip->iflag & IXANY))
X					{
X						fip->device_flags.s &=
X							~DF_SWO_STOPPED;
X						ttyp->t_state &= ~TTSTOP;
X						/* restart output */
X						fas_cmd (fip, ttyp, T_OUTPUT);
X					}
X				}
X				else
X				{
X					if (charac == CSTOP)
X					{
X						fip->device_flags.s |=
X							DF_SWO_STOPPED;
X						ttyp->t_state |= TTSTOP;
X					}
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				metta [csize] = 0xff;
X				csize++;
X			}
X			else
X			{
X				if (fip->recv_ring_cnt <= RECV_BUFF_SIZE - 4)
X				{
X					fip->recv_ring_cnt++;
X					*fip->recv_ring_put_ptr = charac;
X					if (++fip->recv_ring_put_ptr
X						!= &fip->recv_buffer
X							[RECV_BUFF_SIZE])
X						continue;
X					fip->recv_ring_put_ptr =
X							&fip->recv_buffer [0];
X				}
X				continue;
X			}
X		}
X
X		if (!(csize) || (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		do
X		{
X			do
X			{
X				*fip->recv_ring_put_ptr = (metta - 1)[csize];
X				if (++fip->recv_ring_put_ptr
X					== &fip->recv_buffer [RECV_BUFF_SIZE])
X					break;
X				csize--;
X			} while (csize);
X			if (!csize)
X				break;
X			fip->recv_ring_put_ptr = &fip->recv_buffer [0];
X			csize--;
X		} while (csize);
X	} while ((line_status = inb (LINE_STATUS_PORT)) & LS_RCV_INT);
X
X	return (line_status);
X}
X
X/* Output characters to the transmitter register. */
Xstatic void
Xfas_xproc (fip, out_cnt)
Xregister struct fas_info	*fip;
Xregister uint	out_cnt;
X{
X	REGVAR;
X
X	fip->xmit_ring_cnt -= out_cnt;
X
X	do
X	{
X		do
X		{
X			outb (XMT_DATA_PORT, *fip->xmit_ring_take_ptr);
X			if (++fip->xmit_ring_take_ptr
X					== &fip->xmit_buffer [XMIT_BUFF_SIZE])
X				break;
X			out_cnt--;
X		} while (out_cnt);
X		if (!out_cnt)
X			break;
X		fip->xmit_ring_take_ptr = &fip->xmit_buffer [0];
X		out_cnt--;
X	} while (out_cnt);
X}
X
X/* Receiver 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_xfer;
X
X	ttyp = fip->tty;
X
X	/* determin how many characters to transfer */
X	num_to_xfer = (TTYHOG - 1) - ttyp->t_rawq.c_cc;
X
X	if (!num_to_xfer || !ttyp->t_rbuf.c_ptr)
X		return (1);	/* input buffer full */
X
X	/* determin how many characters are in one contigous block */
X	if (fip->recv_ring_cnt < num_to_xfer)
X		num_to_xfer = fip->recv_ring_cnt;
X	if (ttyp->t_rbuf.c_count < num_to_xfer)
X		num_to_xfer = ttyp->t_rbuf.c_count;
X	if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X	{
X		if (INPUT_FIFO_SIZE * 2 < num_to_xfer)
X			num_to_xfer = INPUT_FIFO_SIZE * 2;
X	}
X	else
X	{
X		if (8 < num_to_xfer)
X			num_to_xfer = 8;
X	}
X
X	fip->recv_ring_cnt -= num_to_xfer;
X	ttyp->t_rbuf.c_count -= num_to_xfer;
X
X	/* do the transfer */
X	do
X	{
X		do
X		{
X			*ttyp->t_rbuf.c_ptr = *fip->recv_ring_take_ptr;
X			ttyp->t_rbuf.c_ptr++;
X			if (++fip->recv_ring_take_ptr
X					== &fip->recv_buffer [RECV_BUFF_SIZE])
X				break;
X			num_to_xfer--;
X		} while (num_to_xfer);
X		if (!num_to_xfer)
X			break;
X		fip->recv_ring_take_ptr = &fip->recv_buffer [0];
X		num_to_xfer--;
X	} while (num_to_xfer);
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/* UNIX buffer -> transmitter ring buffer transfer function. */
Xstatic int
Xfas_xxfer (fip)
Xregister struct fas_info	*fip;
X{
X	register struct tty	*ttyp;
X	register int	num_to_xfer;
X
X	ttyp = fip->tty;
X
X	/* set the maximum character limit */
X	num_to_xfer = fip->xmit_ring_size - fip->xmit_ring_cnt;
X
X	/* Return if transmitter ring buffer is full. */
X	if (num_to_xfer < 1)
X		return (1);
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			return (1);
X	}
X
X	/* Determine how many chars to transfer this time. */
X	if (ttyp->t_tbuf.c_count < num_to_xfer)
X		num_to_xfer = ttyp->t_tbuf.c_count;
X	if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X	{
X		if (OUTPUT_FIFO_SIZE * 2 < num_to_xfer)
X			num_to_xfer = OUTPUT_FIFO_SIZE * 2;
X	}
X	else
X	{
X		if (8 < num_to_xfer)
X			num_to_xfer = 8;
X	}
X
X	fip->xmit_ring_cnt += num_to_xfer;
X	ttyp->t_tbuf.c_count -= num_to_xfer;
X	ttyp->t_state |= BUSY;
X
X	/* do the transfer */
X	do
X	{
X		do
X		{
X			*fip->xmit_ring_put_ptr = *ttyp->t_tbuf.c_ptr;
X			ttyp->t_tbuf.c_ptr++;
X			if (++fip->xmit_ring_put_ptr
X					== &fip->xmit_buffer [XMIT_BUFF_SIZE])
X				break;
X			num_to_xfer--;
X		} while (num_to_xfer);
X		if (!num_to_xfer)
X			break;
X		fip->xmit_ring_put_ptr = &fip->xmit_buffer [0];
X		num_to_xfer--;
X	} while (num_to_xfer);
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;
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			/* set up break request flags */
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			fip->timeout_idx = timeout (ttrstrt, ttyp, BREAK_TIME);
X			break;
X		}
X		/* reset break state */
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_GUARD_TIMEOUT
X						| DF_ADAPT_TIMEOUT;
X			fip->timeout_idx = timeout (ttrstrt, ttyp,
X					fas_ctimes [fip->cflag & CBAUD]);
X			break;
X		}
X		/* handle hangup request */
X		if (fip->device_flags.i & DF_DO_HANGUP)
X		{
X			if (fip->device_flags.i & DF_MODEM_ENABLED)
X			{
X				fip->mcr.c &= ~(fip->modem.m.en
X						| fip->flow.m.ic);
X				outb (MDM_CTL_PORT, fip->mcr.i);
X				fip->device_flags.s &= ~DF_MODEM_ENABLED;
X				(void) timeout (ttrstrt, ttyp, HANGUP_TIME);
X				break;
X			}
X			else
X			{
X				fip->device_flags.s &= ~DF_DO_HANGUP;
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, fip->msr);	/* set up mdm stat flags */
X				}
X				release_device_lock (fip);
X				break;
X			}
X		}
X		if (fip->device_flags.i & DF_GUARD_TIMEOUT)
X		{
X			fip->device_flags.s &= ~(DF_XMIT_DISABLED
X						| DF_GUARD_TIMEOUT);
X			if (!fip->xmit_ring_cnt)
X				ttyp->t_state &= ~BUSY;
X			fip->timeout_idx = timeout (ttrstrt, ttyp, ADAPT_TIME);
X		}
X		else
X		{
X			fip->device_flags.s &= ~(DF_XMIT_DISABLED
X						| DF_ADAPT_TIMEOUT);
X		}
X		ttyp->t_state &= ~TIMEOUT;
X		/* FALL THRU */
X
X	case T_OUTPUT:	/* output characters to the transmitter */
Xstart:
X		/* proceed only if transmitter is available */
X		if (fip->device_flags.i & (DF_HWO_STOPPED | DF_XMIT_DISABLED
X						| DF_XMIT_BUSY))
X			break;
X
X		/* transfer some characters to the transmitter ring buffer */
X		(void) fas_xxfer (fip);
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_SWI_STOPPED)
X						? CSTOP
X						: CSTART);
X			ttyp->t_state &= ~(TTXON | TTXOFF);
X			fip->device_flags.s |= DF_XMIT_BUSY;
X			fip->device_flags.s &= ~DF_SW_FC_REQ;
X			/* disable adapt timeout */
X			if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
X			{
X				fip->device_flags.s &= ~(DF_GUARD_TIMEOUT
X							| DF_ADAPT_TIMEOUT);
X				ttyp->t_state &= ~TIMEOUT;
X				untimeout (fip->timeout_idx);
X			}
X			num_to_output--;
X		}
X
X		/* bail out if output is suspended by XOFF */
X		if (fip->device_flags.i & DF_SWO_STOPPED)
X			break;
X
X		/* Determine how many chars to put into the transmitter
X		   register.
X		*/
X		if (fip->xmit_ring_cnt < num_to_output)
X			num_to_output = fip->xmit_ring_cnt;
X
X		/* no characters available ? */
X		if (!num_to_output)
X			break;
X
X		/* output characters */
X		fas_xproc (fip, num_to_output);
X
X		/* signal that transmitter is busy now */
X		fip->device_flags.s |= DF_XMIT_BUSY;
X		/* disable adapt timeout */
X		if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
X		{
X			fip->device_flags.s &= ~(DF_GUARD_TIMEOUT
X						| DF_ADAPT_TIMEOUT);
X			ttyp->t_state &= ~TIMEOUT;
X			untimeout (fip->timeout_idx);
X		}
X		break;
X
X	case T_SUSPEND:	/* suspend character output */
X		fip->device_flags.s |= DF_SWO_STOPPED;
X		ttyp->t_state |= TTSTOP;
X		break;
X
X	case T_RESUME:	/* restart character output */
X		fip->device_flags.s &= ~DF_SWO_STOPPED;
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_HWI_STOPPED)
X		{
X			fip->mcr.c |= fip->flow.m.ic;
X			outb (MDM_CTL_PORT, fip->mcr.i);
X			fip->device_flags.s &= ~DF_HWI_STOPPED;
X		}
X		ttyp->t_state &= ~TBLOCK;
X		if (fip->device_flags.i & DF_SWI_STOPPED)
X		{
X			fip->device_flags.s &= ~DF_SWI_STOPPED;
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
X		fip->xmit_ring_take_ptr = fip->xmit_ring_put_ptr;
X		fip->xmit_ring_cnt = 0;
X
X		if (!(fip->device_flags.i & (DF_XMIT_BUSY | DF_GUARD_TIMEOUT)))
X			ttyp->t_state &= ~BUSY;
X
X		fip->device_flags.s &= ~DF_SWO_STOPPED;
X		ttyp->t_state &= ~TTSTOP;
X
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		break;
X
X	case T_BREAK:	/* do a break on the transmitter line */
X		if (fip->device_flags.i & DF_XMIT_DISABLED)
X		{
X			fip->device_flags.s |= DF_DO_BREAK;
X			ttyp->t_state |= TIMEOUT;
X		}
X		else
X		{
X			/* set up break request flags */
X			fip->lcr.c |= LC_SET_BREAK_LEVEL;
X			outb (LINE_CTL_PORT, fip->lcr.i);
X			/* disable adapt timeout */
X			if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
X			{
X				fip->device_flags.s &= ~DF_ADAPT_TIMEOUT;
X				untimeout (fip->timeout_idx);
X			}
X			fip->device_flags.s |= DF_XMIT_DISABLED;
X			ttyp->t_state |= TIMEOUT;
X			fip->timeout_idx = timeout (ttrstrt, ttyp, BREAK_TIME);
X		}
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_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	fip->xmit_ring_take_ptr = fip->xmit_ring_put_ptr;
X	fip->xmit_ring_cnt = 0;
X
X	/* hook into the interrupt users chain */
X	fip->next_int_user = fas_first_int_user [fip->vec];
X	if (fip->next_int_user)
X		fip->next_int_user->prev_int_user = fip;
X	fas_first_int_user [fip->vec] = fip;
X	fip->prev_int_user = (struct fas_info *) NULL;
X
X	fip->lcr.c = 0;
X	outb (LINE_CTL_PORT, fip->lcr.i);
X
X	if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X		outb (FIFO_CTL_PORT, STANDARD_FIFO_CLEAR);
X
X	/* clear 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
X	if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X		outb (FIFO_CTL_PORT, STANDARD_FIFO_INIT);
X
X	fip->ier.c = IE_INIT_MODE;
X	outb (INT_ENABLE_PORT, fip->ier.i);
X
X	fip->msr = inb (MDM_STATUS_PORT);
X
X	fip->mcr.c |= fip->modem.m.en | fip->flow.m.ic;
X	outb (MDM_CTL_PORT, fip->mcr.i);
X
X	fip->device_flags.s |= DF_DEVICE_OPEN | DF_MODEM_ENABLED;
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
X	if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
X		outb (FIFO_CTL_PORT, STANDARD_FIFO_CLEAR);
X
X	/* unhook from interrupt users chain */
X	if (fip->prev_int_user)
X		fip->prev_int_user->next_int_user = fip->next_int_user;
X	else
X		fas_first_int_user [fip->vec] = fip->next_int_user;
X	if (fip->next_int_user)
X		fip->next_int_user->prev_int_user = fip->prev_int_user;
X
X	/* disable adapt timeout */
X	if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
X	{
X		fip->device_flags.s &= ~(DF_XMIT_DISABLED | DF_ADAPT_TIMEOUT);
X		untimeout (fip->timeout_idx);
X	}
X
X	if (fip->cflag & HUPCL)
X	{
X		/* request hangup */
X		fip->device_flags.s |= DF_DO_HANGUP;
X		(void) timeout (ttrstrt, fip->tty, HANGUP_DELAY);
X	}
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			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			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 &= ~fip->flow.m.ic;
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	(void) splx (old_level);
X	return (0);
X}
X#endif
SHAR_EOF
if test 50067 -ne "`wc -c < 'fas.c'`"
then
	echo shar: "error transmitting 'fas.c'" '(should have been 50067 characters)'
fi
fi
exit 0
#	End of shell archive
-- 
Uwe Doering   |  Domain   : gemini at geminix.mbx.sub.org
Berlin        |---------------------------------------------------------------
West Germany  |  Bangpath : ...!uunet!pyramid!tub!tmpmbx!geminix!gemini



More information about the Comp.unix.i386 mailing list