home *** CD-ROM | disk | FTP | other *** search
- /* FAS Final Async Solution driver for 386 versions of system V UNIX */
-
- /* Originally written by
- Jim Murray encore!cloud9!jjmhome!jjm
- 2 Mohawk Circle harvard!m2c!jjmhome!jjm
- Westboro Mass 01581 jjm%jjmhome@m2c.m2c.org
- USA voice (508) 366-2813
- */
-
- /* Current author:
- Uwe Doering gemini@geminix.mbx.sub.org
- Billstedter Pfad 17 B
- 1000 Berlin 20
- West Germany
- */
-
- #ident "@(#)fas.c 2.06"
-
- /* Note: This source code was quite heavily optimized for speed. You
- may wonder that register variables aren't used everywhere.
- This is because there is an overhead in memory accesses
- when using register variables. As you may know data accesses
- usually need much more wait states on the memory bus than
- code accesses (because of page or cache misses). Therefore
- saving some data accesses has higher priority than saving
- code accesses.
-
- You may also note some not very elegant constructions that
- may be intentional because they are faster. If you want to
- make style improvements you should check the assembler output
- whether this wouldn't slow things down.
-
- Decisions for speed optimization were based on assembler
- listings produced by the standard UNIX V 3.X/386 C compiler.
- */
-
- #include <sys/fas.h>
-
- #if !defined (__GNUC__)
- #include <sys/inline.h>
-
- /* This is a terrible ugly kludge to speed up the `inb' and `outb'
- functions. I.e., originally, the `outb' inline function had an
- overhead of four data memory accesses for parameter passing. This
- parameter passing actually consumed more clock cycles than the
- assembler `outb' command itself. Although this solution can't
- prevent unnessessary register moves it limits them at least to
- register to register moves that are much faster. You need a
- line like the following in the declaration part of every
- function that uses `inb' or `outb' calls:
-
- REGVAR;
-
- This hack should work with every compiler that knows about the
- UNIX V 3.X/386 standard compiler's inline assembler directives.
- */
-
- asm void loadal (val)
- {
- %reg val;
- movl val,%eax
- %mem val;
- movb val,%al
- }
-
- asm void loaddx (val)
- {
- %reg val;
- movl val,%edx
- %mem val;
- movw val,%dx
- }
-
- asm void outbyte ()
- {
- outb (%dx)
- }
-
- asm int inbyte ()
- {
- xorl %eax,%eax
- inb (%dx)
- }
-
- /* The port parameter of the `outb' macro must be one of the predefined
- port macros from `fas.h' or a simple uint variable (no indirection
- is allowed). Additionally, `fip' must be a register variable in the
- functions where `outb' is used. This prevents the destruction of the
- `eax' CPU register while loading the `edx' register with the port
- address. This is highly compiler implementation specific.
- */
- #define outb(port,val) (regvar = (val), loadal (regvar), regvar = (port), loaddx (regvar), outbyte ())
-
- #define inb(port) (regvar = (port), loaddx (regvar), inbyte ())
-
- #define REGVAR register uint regvar
-
- /* This function inserts the address optimization assembler pseudo-op
- wherever called.
- */
-
- asm void optim ()
- {
- .optim
- }
-
- /* This dummy function has nothing to do but to call optim so that
- the `.optim' assembler pseudo-op will be included in the assembler
- file. This must be the first of all functions.
- */
-
- #if defined (OPTIM) /* Define for uPort, ISC doesn't know about */
- static void /* `.optim', but has turned on optimization by */
- dummy () /* default, so we don't need it there anyway. */
- {
- optim ();
- }
- #endif
-
- #else
-
- #define REGVAR
-
- extern uint inb ();
- extern void outb ();
-
- #endif
-
- /* functions provided by this driver */
- int fasinit ();
- int fasopen ();
- int fasclose ();
- int fasread ();
- int faswrite ();
- int fasioctl ();
- int fasintr ();
- static int fas_proc ();
- static void fas_param ();
- static void fas_mproc ();
- static uint fas_rproc ();
- static void fas_xproc ();
- static int fas_rxfer ();
- static int fas_xxfer ();
- static void fas_cmd ();
- static void fas_open_device ();
- static void fas_close_device ();
- static int fas_test_device ();
-
- /* functions used by this driver */
- extern void ttrstrt ();
- extern void ttinit ();
- extern int ttiocom ();
- extern void ttyflush ();
- extern uint spltty ();
- extern uint splx ();
- extern int sleep ();
- extern void wakeup ();
- extern void signal ();
- extern uint timeout ();
- extern void untimeout ();
- extern void delay ();
- extern void printf ();
-
- /* the following stuff is defined in space.c */
- extern uint fas_physical_units;
- extern uint fas_port [];
- extern uint fas_vec [];
- extern uint fas_mcb [];
- extern uint fas_modem [];
- extern uint fas_flow [];
- extern uint fas_int_ack_port [];
- extern uint fas_int_ack [];
- extern uint fas_mux_ack_port [];
- extern uint fas_mux_ack [];
- extern struct fas_info fas_info [];
- extern struct tty fas_tty [];
- extern struct fas_info *fas_info_ptr [];
- extern struct tty *fas_tty_ptr [];
- /* end of space.c references */
-
- /* fas_is_initted
- Flag to indicate that we have been thru init.
- This is realy only necessary for systems that use asyputchar
- and asygetchar but it doesn't hurt to have it anyway.
- */
- int fas_is_initted = FALSE;
-
- /* array of pointers to the first fas_info structure for each
- interrupt vector
- */
- static struct fas_info *fas_first_int_user [NUM_INT_VECTORS];
-
- /* the values for the various baud rates */
- static uint fas_speeds [CBAUD + 1] =
- { 0, BAUD_BASE/50,
- BAUD_BASE/75, BAUD_BASE/110,
- (2*BAUD_BASE+134)/269, BAUD_BASE/150,
- BAUD_BASE/200, BAUD_BASE/300,
- BAUD_BASE/600, BAUD_BASE/1200,
- BAUD_BASE/1800, BAUD_BASE/2400,
- BAUD_BASE/4800, BAUD_BASE/9600,
- BAUD_BASE/19200, BAUD_BASE/38400
- };
-
- /* time for one character to completely leave the transmitter shift register */
- static uint fas_ctimes [CBAUD + 1] =
- { 1, HZ*15/50+2,
- HZ*15/75+2, HZ*15/110+2,
- HZ*30/269+2, HZ*15/150+2,
- HZ*15/200+2, HZ*15/300+2,
- HZ*15/600+2, HZ*15/1200+2,
- HZ*15/1800+2, HZ*15/2400+2,
- HZ*15/4800+2, HZ*15/9600+2,
- HZ*15/19200+2, HZ*15/38400+2
- };
-
- /* dynamically adapt xmit buffer size to baud rate to prevent long buffer
- drains at low speeds
- These values are checked against boundaries and will be modified if
- necessary before use. Checking is done in fas_param (). Drain time
- is about 5 seconds with continuous character flow.
- */
- static uint fas_xbuf_size [CBAUD + 1] =
- { 0, 50/2,
- 75/2, 110/2,
- 269/4, 150/2,
- 200/2, 300/2,
- 600/2, 1200/2,
- 1800/2, 2400/2,
- 4800/2, 9600/2,
- 19200/2, 38400/2
- };
-
- /* lookup table for minor device number -> open mode flags translation */
- static uint fas_open_modes [16] =
- {
- OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL,
- OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL | OS_HW_HANDSHAKE,
- OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON,
- OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_HW_HANDSHAKE,
- OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN,
- OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN | OS_HW_HANDSHAKE,
- OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN | OS_FAKE_CARR_ON,
- OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN | OS_FAKE_CARR_ON | OS_HW_HANDSHAKE,
- OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT,
- OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_HW_HANDSHAKE,
- OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_UNBLOCK_ENABLE,
- OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_UNBLOCK_ENABLE | OS_HW_HANDSHAKE,
- OS_OPEN_FOR_GETTY | OS_WAIT_OPEN,
- OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_HW_HANDSHAKE,
- OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_UNBLOCK_ENABLE,
- OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_UNBLOCK_ENABLE | OS_HW_HANDSHAKE
- };
-
- /* The following defines are used to take apart the minor device numbers. */
- #define GET_UNIT(dev) ((dev) & 0x0f)
- #define GET_OPEN_MODE(dev) (fas_open_modes [((dev) >> 4) & 0x0f])
-
- /* lock device against concurrent use */
- #define get_device_lock(fip) \
- {\
- /* sleep while device is used by an other process */\
- while ((fip)->device_flags.i & DF_DEVICE_LOCKED)\
- (void) sleep((caddr_t) &(fip)->device_flags.i, PZERO - 1);\
- (fip)->device_flags.s |= DF_DEVICE_LOCKED;\
- }
-
- /* release device */
- #define release_device_lock(fip) \
- {\
- (fip)->device_flags.s &= ~DF_DEVICE_LOCKED;\
- /* wakeup the process that may wait for this device */\
- wakeup ((caddr_t) &(fip)->device_flags.i);\
- }
-
- /* fasinit
- This routine checks for the presense of the devices in the fas_port
- array and if the device is present tests and initializes it.
- During the initialization if the device is determined to be a
- NS16550A chip the DF_DEVICE_HAS_FIFO flag is set and the FIFO will
- be used.
-
- At boot time you will see a status message on the screen with a string
- of symbols that show the init state of the ports. The symbols are as
- follows:
-
- - not defined in the fas_port array
- ? can't initialize port
- ! port test procedure failed
- * port is initialized
- F port is initialized and has FIFOs (NS16550)
-
- This is convenient to check whether you have entered the proper port
- base addresses in space.c.
- */
-
- int
- fasinit ()
- {
- register struct fas_info *fip;
- register uint unit;
- uint logical_units;
- uint port;
- char port_stat [MAX_UNITS + 1];
- REGVAR;
-
- if (fas_is_initted)
- return(0);
-
- fas_is_initted = TRUE;
-
- for (unit = 0, logical_units = fas_physical_units * 2;
- unit < logical_units; unit++)
- fas_tty_ptr [unit] = &fas_tty [unit];
-
- for (unit = 0; unit < fas_physical_units; unit++)
- {
- fas_info_ptr [unit] = fip = &fas_info [unit];
- port_stat [unit] = '-';
- if (port = fas_port [unit])
- {
- /* init all of its ports */
- fip->uart_port_0 = port;
- fip->uart_port_1 = port + 1;
- fip->uart_port_2 = port + 2;
- fip->uart_port_3 = port + 3;
- fip->uart_port_4 = port + 4;
- fip->uart_port_5 = port + 5;
- fip->uart_port_6 = port + 6;
- fip->int_ack_port = fas_int_ack_port [unit];
- fip->int_ack = fas_int_ack [unit];
- fip->vec = fas_vec [unit];
- fip->modem.i = fas_modem [unit];
- fip->flow.i = fas_flow [unit];
- fip->recv_ring_put_ptr = fip->recv_buffer;
- fip->recv_ring_take_ptr = fip->recv_buffer;
- fip->xmit_ring_put_ptr = fip->xmit_buffer;
- fip->xmit_ring_take_ptr = fip->xmit_buffer;
-
- fip->ier.c = IE_NONE; /* disable all ints */
- outb (INT_ENABLE_PORT, fip->ier.i);
- if (inb (INT_ENABLE_PORT) != fip->ier.i)
- {
- port_stat [unit] = '?';
- continue; /* a hardware error */
- }
-
- if (!fas_test_device (fip))
- {
- port_stat [unit] = '!';
- continue; /* a hardware error */
- }
-
- fip->lcr.c = 0;
- outb (LINE_CTL_PORT, fip->lcr.i);
- fip->mcr.i = fas_mcb [unit];
- outb (MDM_CTL_PORT, fip->mcr.i);
-
- port_stat [unit] = '*';
-
- /* let's see if it's an NS16550 */
- outb (FIFO_CTL_PORT, STANDARD_FIFO_INIT);
- if (!(~inb (INT_ID_PORT) & II_FIFO_ENABLED))
- {
- fip->device_flags.s |= DF_DEVICE_HAS_FIFO;
- port_stat [unit] = 'F';
- }
- /* clear and disable the FIFO */
- outb (FIFO_CTL_PORT, STANDARD_FIFO_CLEAR);
-
- /* clear potential interrupts */
- inb (MDM_STATUS_PORT);
- inb (RCV_DATA_PORT);
- inb (RCV_DATA_PORT);
- inb (LINE_STATUS_PORT);
- inb (INT_ID_PORT);
- if (INT_ACK_PORT)
- outb (INT_ACK_PORT, fip->int_ack);
- if (port = fas_mux_ack_port [fip->vec])
- outb (port, fas_mux_ack [fip->vec]);
-
- /* show that it is present and configured */
- fip->device_flags.s |= DF_DEVICE_CONFIGURED;
- }
- }
-
- #if defined (NEED_PUT_GETCHAR)
- fip = &fas_info [0];
- fip->mcr.c |= INITIAL_MDM_CONTROL;
- outb (MDM_CTL_PORT, fip->mcr.i);
-
- fip->lcr.c = INITIAL_LINE_CONTROL;
- outb (LINE_CTL_PORT, fip->lcr.i | LC_ENABLE_DIVISOR);
- outb (DIVISOR_LSB_PORT, INITIAL_BAUD_RATE);
- outb (DIVISOR_MSB_PORT, (INITIAL_BAUD_RATE) >> 8);
- outb (LINE_CTL_PORT, fip->lcr.i);
- #endif
-
- port_stat [unit] = '\0';
- printf ("\nFAS 2.06.0 async driver: Port 0-%d init state is [%s]\n\n",
- unit - 1,
- port_stat);
-
- return(0);
- }
-
- /* Open a line */
-
- /* There are a few differences between this and a normal serial
- device.
-
- For each physical port there are two logical minor devices.
- For each logical minor device there are several operating modes
- that are selected by some of the higher bits of the minor
- device number. Only one logical minor device can be open at
- the same time.
-
- - Minor devices that *don't* block on open if no carrier is present:
-
- Bitmap: 0 m m h x x x x
-
- `m m' are the mode bits as follows:
-
- 0 0 The carrier signal is totally ignored. With carrier high->low
- *no* SIGHUP signal is generated.
- 0 1 After an initial open, the carrier signal is ignored.
- Although, carrier high->low generates a SIGHUP signal. From
- thereon the device is carrier controlled until the last
- process has closed the device. An ioctl call with a TCSETA*
- command resets the device to ignore carrier again until the
- next carrier high->low.
- 1 0 The device is carrier controlled. Additionally, if on open
- the carrier signal is low, a SIGHUP signal is sent
- immediately.
- 1 1 The device behaves the same as with mode `0 1'. Additionally,
- if on open the carrier signal is low, a SIGHUP signal is sent
- immediately.
-
- - Minor devices that *do* block on open if no carrier is present:
-
- Bitmap: 1 m m h x x x x
-
- `m m' are the mode bits as follows:
-
- 0 0 The device is carrier controlled.
- 0 1 The device is carrier controlled. An unblock signal wakes
- up the waiting open and I/O is possible regardless of
- carrier until a carrier high->low. Thereafter the device
- is again fully carrier controlled.
- 1 0 Same as mode `0 0', but a parallel non-blocking open
- is possible while waiting for carrier.
- 1 1 Same as mode `0 1', but a parallel non-blocking open
- is possible while waiting for carrier.
-
- - Description of the remaining bits of the bitmap:
-
- `h' If set to `1', the device has hardware handshake. Refer
- to the `space.c' file to determine which port signals
- are actually used for that purpose.
- `x x x x'
- This is the physical port number. This driver supports
- up to 16 ports. If you need more, you should use an
- intelligent serial card because more than 16 devices
- will eat up to much CPU time with this dumb-port approach.
-
- - Note: If a device is carrier controlled, this implies the generation of
- a SIGHUP signal with every carrier high->low. This is of course only
- true if the CLOCAL flag is *not* set.
-
- If you use more than a few ports and you have a high volume of
- receiver data at a high baud rate, the ports may lose characters.
- This is simply a hardware limitation and can't be cured by any
- software. But there is a pin-to-pin compatible replacement
- for the i8250 and NS16450 devices. It's the NS16550 and has
- separate 16-character I/O FIFOs. This will make any character
- loss at least very improbable. Also, the system is loaded
- much less because whenever possible up to 16 characters are
- processed at a single port interrupt.
-
- On my own system I prefer a minor device number of `0011xxxx'
- (48 + device #) for the non-blocking tty node and `1101xxxx'
- (208 + device #) for the blocking tty node. This gives me
- the SIGHUP signal on carrier loss and hardware flow control
- with both logical devices. Dialout while a dialin open
- is waiting for the carrier is also possible with this setup.
-
- This function is called for every open, as opposed to the fasclose
- function which is called only with the last close.
- */
-
- int
- fasopen (dev, flag)
- int dev;
- int flag;
- {
- register struct fas_info *fip;
- register struct tty *ttyp;
- register uint open_mode;
- uint physical_unit;
- int old_level;
-
- physical_unit = GET_UNIT (dev);
-
- /* check for valid port number */
- if (physical_unit >= fas_physical_units)
- {
- u.u_error = ENXIO;
- return(-1);
- }
-
- fip = fas_info_ptr [physical_unit];
-
- /* was the port present at init time ? */
- if (!(fip->device_flags.i & DF_DEVICE_CONFIGURED))
- {
- u.u_error = ENXIO;
- return(-1);
- }
-
- open_mode = GET_OPEN_MODE (dev);
-
- old_level = spltty ();
- get_device_lock (fip);
-
- /* If this is a getty open and the device is already open
- for dialout, wait until device is closed.
- */
- while ((open_mode & OS_OPEN_FOR_GETTY)
- && (fip->o_state & OS_OPEN_FOR_DIALOUT))
- {
- release_device_lock (fip);
- (void) sleep ((caddr_t) &fip->o_state, TTIPRI);
- get_device_lock (fip);
- }
-
- /* If the device is already open and another open uses a different
- open mode or if a getty open waits for carrier and doesn't allow
- parallel dialout opens, return with I/O error.
- */
- if ((fip->o_state & ((open_mode & OS_OPEN_FOR_GETTY)
- ? (OS_OPEN_STATES | OS_WAIT_OPEN)
- : (OS_OPEN_STATES | OS_NO_DIALOUT)))
- && ((open_mode ^ fip->o_state) & OS_TEST_MASK))
- {
- u.u_error = EIO;
- release_device_lock (fip);
- (void) splx (old_level);
- return(-1);
- }
-
- /* set up pointer to tty structure */
- ttyp = (open_mode & OS_OPEN_FOR_GETTY)
- ? fas_tty_ptr [physical_unit + fas_physical_units]
- : fas_tty_ptr [physical_unit];
-
- /* things to do on first open only */
- if (!(fip->o_state & ((open_mode & OS_OPEN_FOR_GETTY)
- ? (OS_OPEN_STATES | OS_WAIT_OPEN)
- : OS_OPEN_STATES)))
- {
- /* init data structures */
- fip->tty = ttyp;
- ttinit (ttyp);
- ttyp->t_proc = fas_proc;
- fip->po_state = fip->o_state;
- fip->o_state = open_mode & ~OS_OPEN_STATES;
- /* open physical device if not yet open */
- if (!(fip->device_flags.i & DF_DEVICE_OPEN))
- fas_open_device (fip);
- fas_param (fip); /* set up port registers */
- fas_mproc (fip, fip->msr); /* set up modem status flags */
- }
-
- /* If getty open and the FNDELAY flag is not set,
- block and wait for carrier.
- */
- if ((open_mode & OS_OPEN_FOR_GETTY) && !(flag & FNDELAY))
- {
- /* sleep while open for dialout or no carrier */
- while ((fip->o_state & OS_OPEN_FOR_DIALOUT)
- || !(ttyp->t_state & CARR_ON))
- {
- ttyp->t_state |= WOPEN;
- release_device_lock (fip);
- (void) sleep((caddr_t) &ttyp->t_canq, TTIPRI);
- get_device_lock (fip);
- ttyp->t_state &= ~WOPEN;
- }
- }
-
- (*linesw [ttyp->t_line].l_open) (ttyp);
-
- /* set open type flags */
- fip->o_state = open_mode;
-
- if ((open_mode & OS_CHECK_CARR_ON_OPEN)
- && (~fip->msr & fip->modem.m.ca)
- && !(fip->cflag & CLOCAL))
- {
- signal (ttyp->t_pgrp, SIGHUP);
- ttyflush (ttyp, FREAD | FWRITE);
- }
-
- release_device_lock (fip);
- (void) splx (old_level);
- return(0);
- }
-
- /* Close a tty line. This is only called if there is no other
- concurrent open left. A blocked getty open is not counted as
- a concurrent open because in this state it isn't really open.
- */
- int
- fasclose (dev)
- int dev;
- {
- register struct fas_info *fip;
- register struct tty *ttyp;
- uint open_mode;
- uint physical_unit;
- int old_level;
-
- physical_unit = GET_UNIT (dev);
-
- fip = fas_info_ptr [physical_unit];
-
- open_mode = GET_OPEN_MODE (dev);
-
- /* set up pointer to tty structure */
- ttyp = (open_mode & OS_OPEN_FOR_GETTY)
- ? fas_tty_ptr [physical_unit + fas_physical_units]
- : fas_tty_ptr [physical_unit];
-
- old_level = spltty ();
-
- (*linesw [ttyp->t_line].l_close) (ttyp);
-
- get_device_lock (fip);
-
- if (open_mode & OS_OPEN_FOR_GETTY)
- {
- /* not waiting any more */
- ttyp->t_state &= ~WOPEN;
- if (!(fip->o_state & OS_OPEN_FOR_DIALOUT))
- {
- fas_close_device (fip);
- fip->o_state = OS_DEVICE_CLOSED;
- }
- else
- fip->po_state = OS_DEVICE_CLOSED;
- }
- else
- {
- fas_close_device (fip);
- fip->o_state = OS_DEVICE_CLOSED;
- /* If there is a waiting getty open on
- this port, reopen the physical device.
- */
- if (fip->po_state & OS_WAIT_OPEN)
- {
- /* get the getty version of the
- tty structure
- */
- fip->tty = fas_tty_ptr [physical_unit
- + fas_physical_units];
- fip->o_state = fip->po_state;
- fip->po_state = OS_DEVICE_CLOSED;
- if (!(fip->device_flags.i & DF_DO_HANGUP))
- {
- fas_open_device (fip);
- fas_param (fip); /* set up port registers */
- fas_mproc (fip, fip->msr); /* set up modem status flags */
- }
- }
- wakeup ((caddr_t) &fip->o_state);
- }
-
- if (!(fip->device_flags.i & DF_DO_HANGUP))
- release_device_lock (fip);
-
- (void) splx (old_level);
- return(0);
- }
-
- /* read characters from the input buffer */
- int
- fasread (dev)
- int dev;
- {
- register struct fas_info *fip;
- register struct tty *ttyp;
- int old_level;
- REGVAR;
-
- fip = fas_info_ptr [GET_UNIT (dev)];
-
- ttyp = fip->tty;
-
- (*linesw [ttyp->t_line].l_read) (ttyp);
-
- old_level = spltty ();
-
- /* fill the unix buffer as much as possible */
- while (fip->recv_ring_cnt)
- {
- if (fas_rxfer (fip))
- break;
-
- (void) splx (old_level); /* allow some interrupts */
- old_level = spltty ();
- }
-
- /* If input buffer level has dropped below
- the low water mark and input was stopped
- by hardware handshake, restart input.
- */
- if ((fip->device_flags.i & DF_HWI_STOPPED)
- && (fip->recv_ring_cnt < HW_LOW_WATER))
- {
- fip->mcr.c |= fip->flow.m.ic;
- outb (MDM_CTL_PORT, fip->mcr.i);
- fip->device_flags.s &= ~DF_HWI_STOPPED;
- }
-
- /* If input buffer level has dropped below
- the low water mark and input was stopped
- by XOFF, send XON to restart input.
- */
- if ((fip->device_flags.i & DF_SWI_STOPPED)
- && (fip->recv_ring_cnt < SW_LOW_WATER))
- {
- fip->device_flags.s &= ~DF_SWI_STOPPED;
- fip->device_flags.s ^= DF_SW_FC_REQ;
- if (fip->device_flags.i & DF_SW_FC_REQ)
- {
- ttyp->t_state |= TTXON;
- fas_cmd (fip, ttyp, T_OUTPUT);
- }
- else
- ttyp->t_state &= ~TTXOFF;
- }
-
- (void) splx (old_level);
- return(0);
- }
-
- /* write characters to the output buffer */
- int
- faswrite (dev)
- int dev;
- {
- register struct fas_info *fip;
- register struct tty *ttyp;
- int old_level;
-
- fip = fas_info_ptr [GET_UNIT (dev)];
-
- ttyp = fip->tty;
-
- (*linesw [ttyp->t_line].l_write) (ttyp);
-
- old_level = spltty ();
-
- /* fill the transmitter ring buffer as much as possible */
- while (!fas_xxfer (fip))
- {
- (void) splx (old_level); /* allow some interrupts */
- old_level = spltty ();
- }
-
- (void) splx (old_level);
- return(0);
- }
-
- /* process ioctl calls */
- int
- fasioctl (dev, cmd, arg3, arg4)
- int dev;
- int cmd;
- union ioctl_arg arg3;
- int arg4;
- {
- register struct fas_info *fip;
- register struct tty *ttyp;
- uint old_t_state;
- int old_level;
-
- fip = fas_info_ptr [GET_UNIT (dev)];
-
- ttyp = fip->tty;
-
- /* if it is a TCSETA* command, call fas_param () */
- if (ttiocom (ttyp, cmd, arg3, arg4))
- {
- old_level = spltty ();
- old_t_state = ttyp->t_state;
- fas_param (fip);
- /* if we switched off CLOCAL mode and the *real* carrier
- is missing we send the SIGHUP signal once
- */
- if (!(ttyp->t_state & CARR_ON) && (old_t_state & CARR_ON))
- {
- signal (ttyp->t_pgrp, SIGHUP);
- ttyflush (ttyp, FREAD | FWRITE);
- }
- (void) splx (old_level);
- }
- return(0);
- }
-
- /* pass fas commands to the fas multi-function procedure */
- static int
- fas_proc (ttyp, arg2)
- struct tty *ttyp;
- int arg2;
- {
- register uint physical_unit;
- int old_level;
-
- physical_unit = ttyp - &fas_tty [0];
- if (physical_unit >= fas_physical_units)
- physical_unit -= fas_physical_units;
-
- old_level = spltty ();
- fas_cmd (fas_info_ptr [physical_unit], ttyp, arg2);
- (void) splx (old_level);
- return (0);
- }
-
- /* set up a port according to the given termio structure */
- static void
- fas_param (fip)
- register struct fas_info *fip;
- {
- register uint cflag;
- uint divisor;
- int xmit_ring_size;
- REGVAR;
-
- cflag = fip->tty->t_cflag;
-
- /* hangup line if it is baud rate 0, else enable line */
- if ((cflag & CBAUD) == B0)
- {
- cflag = (cflag & ~CBAUD) | (fip->cflag & CBAUD);
- fip->mcr.c &= ~fip->modem.m.en;
- outb (MDM_CTL_PORT, fip->mcr.i);
- fip->device_flags.s &= ~DF_MODEM_ENABLED;
- }
- else
- {
- if (!(fip->device_flags.i & DF_MODEM_ENABLED))
- {
- fip->mcr.c |= fip->modem.m.en;
- outb (MDM_CTL_PORT, fip->mcr.i);
- fip->device_flags.s |= DF_MODEM_ENABLED;
- }
- }
-
- /* don't change break flag */
- fip->lcr.c &= LC_SET_BREAK_LEVEL;
-
- /* set character size */
- switch (cflag & CSIZE)
- {
- case CS5:
- fip->lcr.c |= LC_WORDLEN_5;
- break;
-
- case CS6:
- fip->lcr.c |= LC_WORDLEN_6;
- break;
-
- case CS7:
- fip->lcr.c |= LC_WORDLEN_7;
- break;
-
- default:
- fip->lcr.c |= LC_WORDLEN_8;
- break;
- }
-
- /* set # of stop bits */
- if (cflag & CSTOPB)
- fip->lcr.c |= LC_STOPBITS_LONG;
-
- /* set parity */
- if (cflag & PARENB)
- {
- fip->lcr.c |= LC_ENABLE_PARITY;
-
- if (!(cflag & PARODD))
- fip->lcr.c |= LC_EVEN_PARITY;
- }
-
- /* We write the counter divisor and the LCR register only
- if we need to (whenever the corresponding cflag bits
- have changed). This prevents character corruption under
- some special conditions.
- */
- if ((cflag ^ fip->cflag) & CBAUD)
- {
- /* get counter divisor for selected baud rate */
- divisor = fas_speeds [cflag & CBAUD];
- outb (LINE_CTL_PORT, fip->lcr.i | LC_ENABLE_DIVISOR);
- outb (DIVISOR_LSB_PORT, divisor);
- outb (DIVISOR_MSB_PORT, divisor >> 8);
- outb (LINE_CTL_PORT, fip->lcr.i);
- /* check dynamic xmit ring buffer size against boundaries,
- modify it if necessary and update the fas_info structure
- */
- xmit_ring_size = fas_xbuf_size [cflag & CBAUD] - TTXOHI;
- if (xmit_ring_size < OUTPUT_FIFO_SIZE * 2)
- xmit_ring_size = OUTPUT_FIFO_SIZE * 2;
- if (xmit_ring_size > XMIT_BUFF_SIZE)
- xmit_ring_size = XMIT_BUFF_SIZE;
- fip->xmit_ring_size = xmit_ring_size;
- /* disable the transmitter for some time to give the
- receiving side a chance to follow this change (only if
- there has been output recently)
- */
- if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
- {
- if (!(fip->tty->t_state & BUSY))
- fip->device_flags.s |= DF_XMIT_DISABLED;
- }
- }
- else if ((cflag ^ fip->cflag) & (CSIZE | CSTOPB | PARENB | PARODD))
- {
- outb (LINE_CTL_PORT, fip->lcr.i);
- /* disable the transmitter for some time to give the
- receiving side a chance to follow this change (only if
- there has been output recently)
- */
- if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
- {
- if (!(fip->tty->t_state & BUSY))
- fip->device_flags.s |= DF_XMIT_DISABLED;
- }
- }
-
- /* disable modem control signals if required by open mode */
- if (fip->o_state & OS_CLOCAL)
- cflag |= CLOCAL;
-
- /* Fake the carrier detect state flag if CLOCAL mode or if
- requested by open mode.
- */
- if (!(~fip->msr & fip->modem.m.ca)
- || (fip->o_state & OS_FAKE_CARR_ON)
- || (cflag & CLOCAL))
- fip->tty->t_state |= CARR_ON;
- else
- fip->tty->t_state &= ~CARR_ON;
-
- #if defined (XCLUDE) /* SYSV 3.2 Xenix compatibility */
- if (cflag & XCLUDE)
- fip->o_state |= OS_EXCLUSIVE_OPEN;
- else
- fip->o_state &= ~OS_EXCLUSIVE_OPEN;
- #endif
-
- fip->cflag = cflag;
- fip->iflag = fip->tty->t_iflag;
- }
-
- /* Main fas interrupt handler. Actual character processing is splitted
- into sub-functions.
- */
- int
- fasintr (vect)
- int vect;
- {
- register struct fas_info *fip;
- register uint status;
- int done;
- uint port;
- REGVAR;
-
- /* I believe that the 8259 is set up for edge trigger. Therefore
- I must loop until I make a complete pass without getting
- any UARTs that are interrupting.
- */
- do
- {
- done = TRUE;
- fip = fas_first_int_user [vect];
-
- /* loop through all users of this interrupt vector */
- for (; ; fip = fip->next_int_user)
- {
- if (!fip)
- break; /* all users done */
-
- /* process only ports that we expect ints from
- and that actually need to be serviced
- */
- fastloop:
- if (inb (INT_ID_PORT) & II_NO_INTS_PENDING)
- {
- /* speed beats beauty */
- fip = fip->next_int_user;
- if (fip)
- goto fastloop;
- break;
- }
-
- done = FALSE; /* not done if we got an int */
-
- do
- {
- /* read in all the characters from the FIFO */
- if ((status = inb (LINE_STATUS_PORT))
- & LS_RCV_INT)
- {
- status = fas_rproc (fip, status);
- sysinfo.rcvint++;
- }
-
- /* Is it a transmitter empty int ? */
- if ((status & LS_XMIT_AVAIL)
- && (fip->device_flags.i & DF_XMIT_BUSY))
- {
- fip->device_flags.s &= ~DF_XMIT_BUSY;
- fas_cmd (fip, fip->tty, T_OUTPUT);
- if (!(fip->device_flags.i & DF_XMIT_BUSY)
- && !fip->xmit_ring_cnt)
- {
- fip->device_flags.s |=
- DF_GUARD_TIMEOUT
- | DF_ADAPT_TIMEOUT;
- fip->tty->t_state |=
- TIMEOUT;
- fip->timeout_idx =
- timeout (
- ttrstrt, fip->tty,
- fas_ctimes [fip->cflag
- & CBAUD]);
- }
- sysinfo.xmtint++;
- }
-
- /* Has there been a polarity change on
- some of the modem lines ?
- */
- if (((status = inb (MDM_STATUS_PORT))
- ^ fip->msr) & MS_ANY_PRESENT)
- {
- fas_mproc (fip, status);
- sysinfo.mdmint++;
- }
- } while (!(inb (INT_ID_PORT) & II_NO_INTS_PENDING));
-
- /* clear the port interrupt */
- if (INT_ACK_PORT)
- outb (INT_ACK_PORT, fip->int_ack);
-
- /* process the characters in the ring buffer */
- if (fip->recv_ring_cnt)
- {
- (void) fas_rxfer (fip);
- /* If input buffer level has risen above the
- high water mark and input is not yet
- stopped, stop input by hardware handshake.
- */
- if ((fip->o_state & OS_HW_HANDSHAKE)
- && !(fip->device_flags.i & DF_HWI_STOPPED)
- && (fip->recv_ring_cnt > HW_HIGH_WATER))
- {
- fip->mcr.c &= ~fip->flow.m.ic;
- outb (MDM_CTL_PORT, fip->mcr.i);
- fip->device_flags.s |= DF_HWI_STOPPED;
- }
- /* If input buffer level has risen above the
- high water mark and input is not yet
- stopped, send XOFF to stop input.
- */
- if ((fip->iflag & IXOFF)
- && !(fip->device_flags.i & DF_SWI_STOPPED)
- && (fip->recv_ring_cnt > SW_HIGH_WATER))
- {
- fip->device_flags.s |= DF_SWI_STOPPED;
- fip->device_flags.s ^= DF_SW_FC_REQ;
- if (fip->device_flags.i & DF_SW_FC_REQ)
- {
- fip->tty->t_state |= TTXOFF;
- fas_cmd (fip, fip->tty, T_OUTPUT);
- }
- else
- fip->tty->t_state &= ~TTXON;
- }
- }
- }
- } while (!done);
-
- /* clear the mux interrupt since we have scanned all
- of the ports that share this interrupt vector
- */
- if (port = fas_mux_ack_port [vect])
- outb (port, fas_mux_ack [vect]);
-
- return(0);
- }
-
- /* modem status interrupt handler */
- static void
- fas_mproc (fip, mdm_status)
- register struct fas_info *fip;
- register uint mdm_status;
- {
- register struct tty *ttyp;
-
- ttyp = fip->tty;
-
- /* Check the output flow control signals and set the state flag
- accordingly.
- */
- if (fip->o_state & OS_HW_HANDSHAKE)
- {
- if (!(~mdm_status & fip->flow.m.oc)
- || (~mdm_status & fip->flow.m.oe))
- {
- if (fip->device_flags.i & DF_HWO_STOPPED)
- {
- fip->device_flags.s &= ~DF_HWO_STOPPED;
- fas_cmd (fip, ttyp, T_OUTPUT);
- }
- }
- else
- fip->device_flags.s |= DF_HWO_STOPPED;
- }
-
- /* Check the carrier detect signal and set the state flags
- accordingly. Also, if not in clocal mode, send SIGHUP on
- carrier loss and flush the buffers.
- */
- if (!(fip->cflag & CLOCAL))
- {
- if (!(~mdm_status & fip->modem.m.ca))
- {
- ttyp->t_state |= CARR_ON;
- /* Unblock getty open only if it is ready to run. */
- if (fip->o_state & OS_WAIT_OPEN)
- wakeup ((caddr_t) &ttyp->t_canq);
- }
- else
- {
- if (!(~fip->msr & fip->modem.m.ca))
- {
- ttyp->t_state &= ~CARR_ON;
- if (ttyp->t_state & ISOPEN)
- signal (ttyp->t_pgrp, SIGHUP);
- ttyflush (ttyp, FREAD | FWRITE);
- }
- }
- }
-
- /* Check the unblock signal. If low->high edge, fake CARR_ON state
- flag and wake up getty open.
- */
- if ((fip->o_state & OS_UNBLOCK_ENABLE)
- && !(fip->cflag & CLOCAL)
- && !(~mdm_status & fip->modem.m.ub)
- && (~fip->msr & fip->modem.m.ub)
- && (fip->o_state & OS_WAIT_OPEN))
- {
- ttyp->t_state |= CARR_ON;
- wakeup ((caddr_t) &ttyp->t_canq);
- }
-
- fip->msr = mdm_status;
- }
-
- /* Receiver interrupt handler. Translates input characters to character
- sequences as described in TERMIO(7) man page.
- */
- static uint
- fas_rproc (fip, line_status)
- register struct fas_info *fip;
- uint line_status;
- {
- struct tty *ttyp;
- uint charac;
- register uint csize;
- unchar metta [4];
- REGVAR;
-
- ttyp = fip->tty;
-
- /* Translate characters from FIFO according to the TERMIO(7)
- man page.
- */
- do
- {
- charac = (line_status & LS_RCV_AVAIL)
- ? inb (RCV_DATA_PORT)
- : 0; /* was line status int only */
-
- if (!(fip->cflag & CREAD) || !(ttyp->t_state & ISOPEN))
- continue;
-
- csize = 0;
-
- if (fip->iflag & ISTRIP)
- charac &= 0x7f;
-
- if ((line_status & LS_PARITY_ERROR)
- && !(fip->iflag & INPCK))
- line_status &= ~LS_PARITY_ERROR;
-
- if (line_status & (LS_PARITY_ERROR
- | LS_FRAMING_ERROR
- | LS_BREAK_DETECTED))
- {
- if (line_status & LS_BREAK_DETECTED)
- {
- if (!(fip->iflag & IGNBRK))
- if (fip->iflag & BRKINT)
- (*linesw [ttyp->t_line].l_input)
- (ttyp, L_BREAK);
- else
- {
- metta [csize] = 0;
- csize++;
- if (fip->iflag & PARMRK)
- {
- metta [csize] = 0;
- csize++;
- metta [csize] = 0xff;
- csize++;
- }
- }
- }
- else if (!(fip->iflag & IGNPAR))
- if (fip->iflag & PARMRK)
- {
- metta [csize] = charac;
- csize++;
- metta [csize] = 0;
- csize++;
- metta [csize] = 0xff;
- csize++;
- }
- else
- {
- metta [csize] = 0;
- csize++;
- }
- }
- else if (line_status & LS_RCV_AVAIL)
- {
- if (fip->iflag & IXON)
- {
- if (fip->device_flags.i & DF_SWO_STOPPED)
- {
- if ((charac == CSTART)
- || (fip->iflag & IXANY))
- {
- fip->device_flags.s &=
- ~DF_SWO_STOPPED;
- ttyp->t_state &= ~TTSTOP;
- /* restart output */
- fas_cmd (fip, ttyp, T_OUTPUT);
- }
- }
- else
- {
- if (charac == CSTOP)
- {
- fip->device_flags.s |=
- DF_SWO_STOPPED;
- ttyp->t_state |= TTSTOP;
- }
- }
- if ((charac == CSTART) || (charac == CSTOP))
- continue;
- }
-
- if ((charac == 0xff) && (fip->iflag & PARMRK))
- {
- metta [csize] = 0xff;
- csize++;
- metta [csize] = 0xff;
- csize++;
- }
- else
- {
- if (fip->recv_ring_cnt <= RECV_BUFF_SIZE - 4)
- {
- fip->recv_ring_cnt++;
- *fip->recv_ring_put_ptr = charac;
- if (++fip->recv_ring_put_ptr
- != &fip->recv_buffer
- [RECV_BUFF_SIZE])
- continue;
- fip->recv_ring_put_ptr =
- &fip->recv_buffer [0];
- }
- continue;
- }
- }
-
- if (!(csize) || (fip->recv_ring_cnt > RECV_BUFF_SIZE - 4))
- continue;
-
- fip->recv_ring_cnt += csize;
-
- /* store translation in ring buffer */
- do
- {
- do
- {
- *fip->recv_ring_put_ptr = (metta - 1)[csize];
- if (++fip->recv_ring_put_ptr
- == &fip->recv_buffer [RECV_BUFF_SIZE])
- break;
- csize--;
- } while (csize);
- if (!csize)
- break;
- fip->recv_ring_put_ptr = &fip->recv_buffer [0];
- csize--;
- } while (csize);
- } while ((line_status = inb (LINE_STATUS_PORT)) & LS_RCV_INT);
-
- return (line_status);
- }
-
- /* Output characters to the transmitter register. */
- static void
- fas_xproc (fip, out_cnt)
- register struct fas_info *fip;
- register uint out_cnt;
- {
- REGVAR;
-
- fip->xmit_ring_cnt -= out_cnt;
-
- do
- {
- do
- {
- outb (XMT_DATA_PORT, *fip->xmit_ring_take_ptr);
- if (++fip->xmit_ring_take_ptr
- == &fip->xmit_buffer [XMIT_BUFF_SIZE])
- break;
- out_cnt--;
- } while (out_cnt);
- if (!out_cnt)
- break;
- fip->xmit_ring_take_ptr = &fip->xmit_buffer [0];
- out_cnt--;
- } while (out_cnt);
- }
-
- /* Receiver ring buffer -> UNIX buffer transfer function. */
- static int
- fas_rxfer (fip)
- register struct fas_info *fip;
- {
- register struct tty *ttyp;
- register uint num_to_xfer;
-
- ttyp = fip->tty;
-
- /* determin how many characters to transfer */
- num_to_xfer = (TTYHOG - 1) - ttyp->t_rawq.c_cc;
-
- if (!num_to_xfer || !ttyp->t_rbuf.c_ptr)
- return (1); /* input buffer full */
-
- /* determin how many characters are in one contigous block */
- if (fip->recv_ring_cnt < num_to_xfer)
- num_to_xfer = fip->recv_ring_cnt;
- if (ttyp->t_rbuf.c_count < num_to_xfer)
- num_to_xfer = ttyp->t_rbuf.c_count;
- if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
- {
- if (INPUT_FIFO_SIZE * 2 < num_to_xfer)
- num_to_xfer = INPUT_FIFO_SIZE * 2;
- }
- else
- {
- if (8 < num_to_xfer)
- num_to_xfer = 8;
- }
-
- fip->recv_ring_cnt -= num_to_xfer;
- ttyp->t_rbuf.c_count -= num_to_xfer;
-
- /* do the transfer */
- do
- {
- do
- {
- *ttyp->t_rbuf.c_ptr = *fip->recv_ring_take_ptr;
- ttyp->t_rbuf.c_ptr++;
- if (++fip->recv_ring_take_ptr
- == &fip->recv_buffer [RECV_BUFF_SIZE])
- break;
- num_to_xfer--;
- } while (num_to_xfer);
- if (!num_to_xfer)
- break;
- fip->recv_ring_take_ptr = &fip->recv_buffer [0];
- num_to_xfer--;
- } while (num_to_xfer);
-
- ttyp->t_rbuf.c_ptr -= ttyp->t_rbuf.c_size
- - ttyp->t_rbuf.c_count;
- (*linesw [ttyp->t_line].l_input) (ttyp, L_BUF);
-
- return (0);
- }
-
- /* UNIX buffer -> transmitter ring buffer transfer function. */
- static int
- fas_xxfer (fip)
- register struct fas_info *fip;
- {
- register struct tty *ttyp;
- register int num_to_xfer;
-
- ttyp = fip->tty;
-
- /* set the maximum character limit */
- num_to_xfer = fip->xmit_ring_size - fip->xmit_ring_cnt;
-
- /* Return if transmitter ring buffer is full. */
- if (num_to_xfer < 1)
- return (1);
-
- /* Check if tbuf is empty. If it is empty, reset buffer
- pointer and counter and get the next chunk of output
- characters.
- */
- if (!ttyp->t_tbuf.c_ptr || !ttyp->t_tbuf.c_count)
- {
- if (ttyp->t_tbuf.c_ptr)
- ttyp->t_tbuf.c_ptr -= ttyp->t_tbuf.c_size;
- if (!((*linesw [ttyp->t_line].l_output) (ttyp)
- & CPRES))
- return (1);
- }
-
- /* Determine how many chars to transfer this time. */
- if (ttyp->t_tbuf.c_count < num_to_xfer)
- num_to_xfer = ttyp->t_tbuf.c_count;
- if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
- {
- if (OUTPUT_FIFO_SIZE * 2 < num_to_xfer)
- num_to_xfer = OUTPUT_FIFO_SIZE * 2;
- }
- else
- {
- if (8 < num_to_xfer)
- num_to_xfer = 8;
- }
-
- fip->xmit_ring_cnt += num_to_xfer;
- ttyp->t_tbuf.c_count -= num_to_xfer;
- ttyp->t_state |= BUSY;
-
- /* do the transfer */
- do
- {
- do
- {
- *fip->xmit_ring_put_ptr = *ttyp->t_tbuf.c_ptr;
- ttyp->t_tbuf.c_ptr++;
- if (++fip->xmit_ring_put_ptr
- == &fip->xmit_buffer [XMIT_BUFF_SIZE])
- break;
- num_to_xfer--;
- } while (num_to_xfer);
- if (!num_to_xfer)
- break;
- fip->xmit_ring_put_ptr = &fip->xmit_buffer [0];
- num_to_xfer--;
- } while (num_to_xfer);
-
- return (0);
- }
-
- /* Several functions for flow control, character output and special event
- requests and handling.
- */
- static void
- fas_cmd (fip, ttyp, arg2)
- register struct fas_info *fip;
- register struct tty *ttyp;
- int arg2;
- {
- uint num_to_output, out_cnt;
- REGVAR;
-
- switch (arg2)
- {
- case T_TIME: /* process delayed events */
- /* handle break request */
- if (fip->device_flags.i & DF_DO_BREAK)
- {
- /* set up break request flags */
- fip->lcr.c |= LC_SET_BREAK_LEVEL;
- outb (LINE_CTL_PORT, fip->lcr.i);
- fip->device_flags.s &= ~DF_DO_BREAK;
- fip->timeout_idx = timeout (ttrstrt, ttyp, BREAK_TIME);
- break;
- }
- /* reset break state */
- if (fip->lcr.i & LC_SET_BREAK_LEVEL)
- {
- fip->lcr.c &= ~LC_SET_BREAK_LEVEL;
- outb (LINE_CTL_PORT, fip->lcr.i);
- fip->device_flags.s |= DF_GUARD_TIMEOUT
- | DF_ADAPT_TIMEOUT;
- fip->timeout_idx = timeout (ttrstrt, ttyp,
- fas_ctimes [fip->cflag & CBAUD]);
- break;
- }
- /* handle hangup request */
- if (fip->device_flags.i & DF_DO_HANGUP)
- {
- if (fip->device_flags.i & DF_MODEM_ENABLED)
- {
- fip->mcr.c &= ~(fip->modem.m.en
- | fip->flow.m.ic);
- outb (MDM_CTL_PORT, fip->mcr.i);
- fip->device_flags.s &= ~DF_MODEM_ENABLED;
- (void) timeout (ttrstrt, ttyp, HANGUP_TIME);
- break;
- }
- else
- {
- fip->device_flags.s &= ~DF_DO_HANGUP;
- /* If there was a waiting getty open on this
- port, reopen the physical device.
- */
- if (fip->o_state & OS_WAIT_OPEN)
- {
- fas_open_device (fip);
- fas_param (fip); /* set up port regs */
- fas_mproc (fip, fip->msr); /* set up mdm stat flags */
- }
- release_device_lock (fip);
- break;
- }
- }
- if (fip->device_flags.i & DF_GUARD_TIMEOUT)
- {
- fip->device_flags.s &= ~(DF_XMIT_DISABLED
- | DF_GUARD_TIMEOUT);
- if (!fip->xmit_ring_cnt)
- ttyp->t_state &= ~BUSY;
- fip->timeout_idx = timeout (ttrstrt, ttyp, ADAPT_TIME);
- }
- else
- {
- fip->device_flags.s &= ~(DF_XMIT_DISABLED
- | DF_ADAPT_TIMEOUT);
- }
- ttyp->t_state &= ~TIMEOUT;
- /* FALL THRU */
-
- case T_OUTPUT: /* output characters to the transmitter */
- start:
- /* proceed only if transmitter is available */
- if (fip->device_flags.i & (DF_HWO_STOPPED | DF_XMIT_DISABLED
- | DF_XMIT_BUSY))
- break;
-
- /* transfer some characters to the transmitter ring buffer */
- (void) fas_xxfer (fip);
-
- /* determine the transmitter FIFO size */
- num_to_output = (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
- ? OUTPUT_FIFO_SIZE
- : 1;
-
- /* handle XON/XOFF input flow control requests */
- if (fip->device_flags.i & DF_SW_FC_REQ)
- {
- outb (XMT_DATA_PORT, (fip->device_flags.i & DF_SWI_STOPPED)
- ? CSTOP
- : CSTART);
- ttyp->t_state &= ~(TTXON | TTXOFF);
- fip->device_flags.s |= DF_XMIT_BUSY;
- fip->device_flags.s &= ~DF_SW_FC_REQ;
- /* disable adapt timeout */
- if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
- {
- fip->device_flags.s &= ~(DF_GUARD_TIMEOUT
- | DF_ADAPT_TIMEOUT);
- ttyp->t_state &= ~TIMEOUT;
- untimeout (fip->timeout_idx);
- }
- num_to_output--;
- }
-
- /* bail out if output is suspended by XOFF */
- if (fip->device_flags.i & DF_SWO_STOPPED)
- break;
-
- /* Determine how many chars to put into the transmitter
- register.
- */
- if (fip->xmit_ring_cnt < num_to_output)
- num_to_output = fip->xmit_ring_cnt;
-
- /* no characters available ? */
- if (!num_to_output)
- break;
-
- /* output characters */
- fas_xproc (fip, num_to_output);
-
- /* signal that transmitter is busy now */
- fip->device_flags.s |= DF_XMIT_BUSY;
- /* disable adapt timeout */
- if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
- {
- fip->device_flags.s &= ~(DF_GUARD_TIMEOUT
- | DF_ADAPT_TIMEOUT);
- ttyp->t_state &= ~TIMEOUT;
- untimeout (fip->timeout_idx);
- }
- break;
-
- case T_SUSPEND: /* suspend character output */
- fip->device_flags.s |= DF_SWO_STOPPED;
- ttyp->t_state |= TTSTOP;
- break;
-
- case T_RESUME: /* restart character output */
- fip->device_flags.s &= ~DF_SWO_STOPPED;
- ttyp->t_state &= ~TTSTOP;
- goto start;
-
- case T_BLOCK: /* stop character input, request XOFF */
- ttyp->t_state |= TBLOCK;
- break; /* note: we do our own XON/XOFF */
-
- case T_UNBLOCK: /* restart character input, request XON */
- ttyp->t_state &= ~TBLOCK;
- break; /* note: we do our own XON/XOFF */
-
- case T_RFLUSH: /* flush input buffers and restart input */
- fip->recv_ring_take_ptr = fip->recv_ring_put_ptr;
- fip->recv_ring_cnt = 0;
- if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
- outb (FIFO_CTL_PORT, STANDARD_FIFO_SETUP
- | FIFO_CLR_RECV);
- if (fip->device_flags.i & DF_HWI_STOPPED)
- {
- fip->mcr.c |= fip->flow.m.ic;
- outb (MDM_CTL_PORT, fip->mcr.i);
- fip->device_flags.s &= ~DF_HWI_STOPPED;
- }
- ttyp->t_state &= ~TBLOCK;
- if (fip->device_flags.i & DF_SWI_STOPPED)
- {
- fip->device_flags.s &= ~DF_SWI_STOPPED;
- fip->device_flags.s ^= DF_SW_FC_REQ;
- if (fip->device_flags.i & DF_SW_FC_REQ)
- {
- ttyp->t_state |= TTXON;
- goto start;
- }
- else
- ttyp->t_state &= ~TTXOFF;
- }
- break;
-
- case T_WFLUSH: /* flush output buffer and restart output */
- if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
- outb (FIFO_CTL_PORT, STANDARD_FIFO_SETUP
- | FIFO_CLR_XMIT);
-
- fip->xmit_ring_take_ptr = fip->xmit_ring_put_ptr;
- fip->xmit_ring_cnt = 0;
-
- if (!(fip->device_flags.i & (DF_XMIT_BUSY | DF_GUARD_TIMEOUT)))
- ttyp->t_state &= ~BUSY;
-
- fip->device_flags.s &= ~DF_SWO_STOPPED;
- ttyp->t_state &= ~TTSTOP;
-
- if (ttyp->t_tbuf.c_ptr)
- ttyp->t_tbuf.c_ptr -= ttyp->t_tbuf.c_size
- - ttyp->t_tbuf.c_count;
- do
- {
- ttyp->t_tbuf.c_count = 0;
- } while ((*linesw [ttyp->t_line].l_output) (ttyp) & CPRES);
- break;
-
- case T_BREAK: /* do a break on the transmitter line */
- if (fip->device_flags.i & DF_XMIT_DISABLED)
- {
- fip->device_flags.s |= DF_DO_BREAK;
- ttyp->t_state |= TIMEOUT;
- }
- else
- {
- /* set up break request flags */
- fip->lcr.c |= LC_SET_BREAK_LEVEL;
- outb (LINE_CTL_PORT, fip->lcr.i);
- /* disable adapt timeout */
- if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
- {
- fip->device_flags.s &= ~DF_ADAPT_TIMEOUT;
- untimeout (fip->timeout_idx);
- }
- fip->device_flags.s |= DF_XMIT_DISABLED;
- ttyp->t_state |= TIMEOUT;
- fip->timeout_idx = timeout (ttrstrt, ttyp, BREAK_TIME);
- }
- break;
-
- case T_PARM: /* set up the port according to the termio structure */
- fas_param (fip);
- break;
-
- case T_SWTCH: /* handle layer switch request */
- break;
- }
- }
-
- /* open device physically */
- static void
- fas_open_device (fip)
- register struct fas_info *fip;
- {
- REGVAR;
-
- fip->device_flags.s &= DF_DEVICE_CONFIGURED | DF_DEVICE_HAS_FIFO
- | DF_DEVICE_LOCKED;
- fip->cflag = 0;
- fip->iflag = 0;
- fip->recv_ring_take_ptr = fip->recv_ring_put_ptr;
- fip->recv_ring_cnt = 0;
- fip->xmit_ring_take_ptr = fip->xmit_ring_put_ptr;
- fip->xmit_ring_cnt = 0;
-
- /* hook into the interrupt users chain */
- fip->next_int_user = fas_first_int_user [fip->vec];
- if (fip->next_int_user)
- fip->next_int_user->prev_int_user = fip;
- fas_first_int_user [fip->vec] = fip;
- fip->prev_int_user = (struct fas_info *) NULL;
-
- fip->lcr.c = 0;
- outb (LINE_CTL_PORT, fip->lcr.i);
-
- if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
- outb (FIFO_CTL_PORT, STANDARD_FIFO_CLEAR);
-
- /* clear interrupts */
- inb (MDM_STATUS_PORT);
- inb (RCV_DATA_PORT);
- inb (RCV_DATA_PORT);
- inb (LINE_STATUS_PORT);
- inb (INT_ID_PORT);
- if (INT_ACK_PORT)
- outb (INT_ACK_PORT, fip->int_ack);
-
- if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
- outb (FIFO_CTL_PORT, STANDARD_FIFO_INIT);
-
- fip->ier.c = IE_INIT_MODE;
- outb (INT_ENABLE_PORT, fip->ier.i);
-
- fip->msr = inb (MDM_STATUS_PORT);
-
- fip->mcr.c |= fip->modem.m.en | fip->flow.m.ic;
- outb (MDM_CTL_PORT, fip->mcr.i);
-
- fip->device_flags.s |= DF_DEVICE_OPEN | DF_MODEM_ENABLED;
- }
-
- /* close device physically */
- static void
- fas_close_device (fip)
- register struct fas_info *fip;
- {
- REGVAR;
-
- fip->device_flags.s &= ~DF_DEVICE_OPEN;
- fip->ier.c = IE_NONE; /* disable all ints from UART */
- outb (INT_ENABLE_PORT, fip->ier.i);
- if (INT_ACK_PORT)
- outb (INT_ACK_PORT, fip->int_ack);
-
- if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
- outb (FIFO_CTL_PORT, STANDARD_FIFO_CLEAR);
-
- /* unhook from interrupt users chain */
- if (fip->prev_int_user)
- fip->prev_int_user->next_int_user = fip->next_int_user;
- else
- fas_first_int_user [fip->vec] = fip->next_int_user;
- if (fip->next_int_user)
- fip->next_int_user->prev_int_user = fip->prev_int_user;
-
- /* disable adapt timeout */
- if (fip->device_flags.i & DF_ADAPT_TIMEOUT)
- {
- fip->device_flags.s &= ~(DF_XMIT_DISABLED | DF_ADAPT_TIMEOUT);
- untimeout (fip->timeout_idx);
- }
-
- if (fip->cflag & HUPCL)
- {
- /* request hangup */
- fip->device_flags.s |= DF_DO_HANGUP;
- (void) timeout (ttrstrt, fip->tty, HANGUP_DELAY);
- }
- }
-
- /* test device thoroughly */
- static int
- fas_test_device (fip)
- register struct fas_info *fip;
- {
- register unchar *cptr;
- int done;
- REGVAR;
-
- /* make sure FIFO is off */
- outb (FIFO_CTL_PORT, STANDARD_FIFO_CLEAR);
-
- /* set counter divisor */
- outb (LINE_CTL_PORT, LC_ENABLE_DIVISOR);
- outb (DIVISOR_LSB_PORT, fas_speeds [B38400]);
- outb (DIVISOR_MSB_PORT, fas_speeds [B38400] >> 8);
- outb (LINE_CTL_PORT, 0);
-
- /* switch to local loopback */
- outb (MDM_CTL_PORT, MC_SET_LOOPBACK);
- delay (fas_ctimes [B38400]);
-
- /* clear flags */
- inb (RCV_DATA_PORT);
- inb (RCV_DATA_PORT);
- inb (LINE_STATUS_PORT);
-
- /* test pattern */
- cptr = (unchar *) "\377\125\252\045\244\0";
-
- do
- {
- done = FALSE;
-
- /* test transmitter and receiver with parity odd */
- outb (LINE_CTL_PORT, LC_WORDLEN_8 | LC_ENABLE_PARITY);
- if (~inb (LINE_STATUS_PORT) & (LS_XMIT_AVAIL | LS_XMIT_COMPLETE))
- break;
-
- outb (XMT_DATA_PORT, *cptr);
- delay (fas_ctimes [B38400]);
- if ((inb (LINE_STATUS_PORT) & LS_RCV_INT) != LS_RCV_AVAIL)
- break;
-
- if (inb (RCV_DATA_PORT) != *cptr)
- break;
-
- /* test transmitter and receiver with parity even */
- outb (LINE_CTL_PORT, LC_WORDLEN_8 | LC_ENABLE_PARITY
- | LC_EVEN_PARITY);
- if (~inb (LINE_STATUS_PORT) & (LS_XMIT_AVAIL | LS_XMIT_COMPLETE))
- break;
-
- outb (XMT_DATA_PORT, *cptr);
- delay (fas_ctimes [B38400]);
- if ((inb (LINE_STATUS_PORT) & LS_RCV_INT) != LS_RCV_AVAIL)
- break;
-
- if (inb (RCV_DATA_PORT) != *cptr)
- break;
-
- done = TRUE;
- } while (*cptr++);
-
- if (done)
- {
- /* test pattern */
- cptr = (unchar *) "\005\140\012\220\006\120\011\240\017\360\0\0";
-
- do
- {
- done = FALSE;
-
- /* test modem control and status lines */
- outb (MDM_CTL_PORT, *cptr | MC_SET_LOOPBACK);
- if ((inb (MDM_STATUS_PORT) & MS_ANY_PRESENT)
- != *(cptr + 1))
- break;
-
- done = TRUE;
- } while (*((ushort *) cptr)++);
- }
-
- /* switch back to normal operation */
- outb (MDM_CTL_PORT, 0);
- delay (fas_ctimes [B38400]);
-
- return (done);
- }
-
- #if defined (NEED_PUT_GETCHAR)
-
- int
- asyputchar (arg1)
- unchar arg1;
- {
- register struct fas_info *fip;
- REGVAR;
-
- if (!fas_is_initted)
- fasinit();
-
- fip = &fas_info [0];
- if (fip->device_flags.i & DF_DEVICE_CONFIGURED)
- {
- while (!(inb (LINE_STATUS_PORT) & LS_XMIT_AVAIL))
- ;
- outb (XMT_DATA_PORT, arg1);
- if (arg1 == 10)
- asyputchar(13);
- }
- return(0);
- }
-
- int
- asygetchar ()
- {
- register struct fas_info *fip;
- REGVAR;
-
- if (!fas_is_initted)
- fasinit();
-
- fip = &fas_info [0];
- if ((fip->device_flags.i & DF_DEVICE_CONFIGURED)
- && (inb (LINE_STATUS_PORT) & LS_RCV_AVAIL))
- return (inb (RCV_DATA_PORT));
- else
- return (-1);
- }
- #endif
-
- #if defined (NEED_INIT8250)
-
- /* reset the requested port to be used directly by a DOS process */
- int
- init8250 (port, ier)
- ushort port, ier; /* ier not used in this stub */
- {
- register struct fas_info *fip;
- register uint physical_unit;
- int old_level;
- REGVAR;
-
- /* See if the port address matches a port that is used by
- the fas driver.
- */
- for (physical_unit = 0; physical_unit < fas_physical_units;
- physical_unit++)
- if (port == fas_port [physical_unit])
- break;
-
- if (physical_unit >= fas_physical_units)
- return(-1); /* port didn't match */
-
- fip = fas_info_ptr [physical_unit];
-
- old_level = spltty ();
-
- fip->ier.c = IE_NONE;
- outb (INT_ENABLE_PORT, fip->ier.i);
- if (INT_ACK_PORT)
- outb (INT_ACK_PORT, fip->int_ack);
-
- fip->mcr.c &= ~fip->flow.m.ic;
- outb (MDM_CTL_PORT, fip->mcr.i);
-
- if (fip->device_flags.i & DF_DEVICE_HAS_FIFO)
- outb (FIFO_CTL_PORT, STANDARD_FIFO_CLEAR);
-
- inb (MDM_STATUS_PORT);
- inb (RCV_DATA_PORT);
- inb (RCV_DATA_PORT);
- inb (LINE_STATUS_PORT);
- inb (INT_ID_PORT);
- (void) splx (old_level);
- return (0);
- }
- #endif
-