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