FAS 2.05 async driver, part 2/2
Uwe Doering
gemini at geminix.UUCP
Mon Jan 15 08:04:35 AEST 1990
#! /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 at m2c.m2c.org
XUSA voice (508) 366-2813
X*/
X
X/* Current author:
XUwe Doering gemini at 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 at netmbx.UUCP
Berlin |---------------------------------------------------------------
West Germany | Bangpath : ...!uunet!unido!tmpmbx!netmbx!gemini
More information about the Alt.sources
mailing list