FAS 2.07 async driver, part 2/3
Uwe Doering
gemini at geminix.in-berlin.de
Wed Sep 19 05:14:08 AEST 1990
#!/bin/sh
# this is fas207.02 (part 2 of a multipart archive)
# do not concatenate these parts, unpack them in order with /bin/sh
# file fas.c continued
#
if test ! -r _shar_seq_.tmp; then
echo 'Please unpack part 1 first!'
exit 1
fi
(read Scheck
if test "$Scheck" != 2; then
echo Please unpack part "$Scheck" next!
exit 1
else
exit 0
fi
) < _shar_seq_.tmp || exit 1
if test ! -f _shar_wnt_.tmp; then
echo 'x - still skipping fas.c'
else
echo 'x - continuing file fas.c'
sed 's/^X//' << 'SHAR_EOF' >> 'fas.c' &&
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#endif /* XENIX */
X
X/* functions provided by this driver */
Xint fasinit ();
Xint fasopen ();
Xint fasclose ();
Xint fasread ();
Xint faswrite ();
Xint fasioctl ();
Xint fasintr ();
X#if defined (NEED_PUT_GETCHAR)
Xint asyputchar ();
Xint asygetchar ();
X#endif
X#if defined (NEED_INIT8250)
Xint init8250 ();
X#endif
Xstatic int fas_proc ();
Xstatic void fas_param ();
Xstatic void fas_fproc ();
Xstatic void fas_mproc ();
Xstatic uint fas_rproc ();
Xstatic void fas_xproc ();
Xstatic void fas_event ();
X#if defined (HAVE_VPIX)
Xstatic int fas_vpix_sr ();
X#endif
Xstatic void fas_rxfer ();
Xstatic void fas_xxfer ();
Xstatic void fas_ihlw_check ();
Xstatic void fas_hangup ();
Xstatic void fas_timeout ();
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 ttinit ();
Xextern int ttiocom ();
Xextern int ttyflush ();
Xextern int SPLINT ();
Xextern int SPLWRK ();
Xextern int splx ();
Xextern int sleep ();
Xextern int wakeup ();
Xextern int signal ();
Xextern int timeout ();
Xextern int untimeout ();
X#if defined (SCO)
Xextern int printcfg ();
X#else
Xextern int printf ();
X#endif
X#if defined (HAVE_VPIX)
Xextern int ttywait ();
Xextern int fubyte ();
Xextern int subyte ();
Xextern int v86setint ();
X#endif
X#if defined (XENIX)
Xextern int inb ();
Xextern void outb ();
X#endif
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_init_seq [];
Xextern uint fas_mcb [];
Xextern uint fas_modem [];
Xextern uint fas_flow [];
Xextern uint fas_int_ack_port [];
Xextern uint fas_int_ack [];
Xextern uint fas_mux_ack_port [];
Xextern uint fas_mux_ack [];
Xextern struct fas_info fas_info [];
Xextern struct tty fas_tty [];
Xextern struct fas_info *fas_info_ptr [];
Xextern struct tty *fas_tty_ptr [];
X/* end of space.c references */
X
X/* fas_is_initted
X Flag to indicate that we have been thru init.
X This is realy only necessary for systems that use asyputchar
X and asygetchar but it doesn't hurt to have it anyway.
X*/
Xstatic int fas_is_initted = FALSE;
X
X/* array of pointers to the first fas_info structure for each
X interrupt vector
X*/
Xstatic struct fas_info *fas_first_int_user [NUM_INT_VECTORS];
X
X/* the values for the various baud rates */
Xstatic uint fas_speeds [CBAUD + 1] =
X{ 1, BAUD_BASE/50,
X BAUD_BASE/75, BAUD_BASE/110,
X (2*BAUD_BASE+134)/269, BAUD_BASE/150,
X BAUD_BASE/200, BAUD_BASE/300,
X BAUD_BASE/600, BAUD_BASE/1200,
X BAUD_BASE/1800, BAUD_BASE/2400,
X BAUD_BASE/4800, BAUD_BASE/9600,
X BAUD_BASE/19200, BAUD_BASE/38400
X};
X
X/* time for one character to completely leave the transmitter shift register */
Xstatic uint fas_ctimes [CBAUD + 1] =
X{ 1, HZ*15/50+2,
X HZ*15/75+2, HZ*15/110+2,
X HZ*30/269+2, HZ*15/150+2,
X HZ*15/200+2, HZ*15/300+2,
X HZ*15/600+2, HZ*15/1200+2,
X HZ*15/1800+2, HZ*15/2400+2,
X HZ*15/4800+2, HZ*15/9600+2,
X HZ*15/19200+2, HZ*15/38400+2
X};
X
X/* dynamically adapt xmit buffer size to baud rate to prevent long buffer
X drains at low speeds
X These values are checked against boundaries and will be modified if
X necessary before use. Checking is done in fas_param (). Drain time
X is about 5 seconds with continuous character flow.
X*/
Xstatic uint fas_xbuf_size [CBAUD + 1] =
X{ 1, 50/2,
X 75/2, 110/2,
X 269/4, 150/2,
X 200/2, 300/2,
X 600/2, 1200/2,
X 1800/2, 2400/2,
X 4800/2, 9600/2,
X 19200/2, 38400/2
X};
X
X/* lookup table for minor device number -> open mode flags translation */
Xstatic uint fas_open_modes [16] =
X{
X OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL,
X OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_CLOCAL | OS_HW_HANDSHAKE,
X OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON,
X OS_OPEN_FOR_DIALOUT | OS_FAKE_CARR_ON | OS_HW_HANDSHAKE,
X OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN,
X OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN | OS_HW_HANDSHAKE,
X OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN | OS_FAKE_CARR_ON,
X OS_OPEN_FOR_DIALOUT | OS_CHECK_CARR_ON_OPEN | OS_FAKE_CARR_ON | OS_HW_HANDSHAKE,
X OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT,
X OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_HW_HANDSHAKE,
X OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_UNBLOCK_ENABLE,
X OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_NO_DIALOUT | OS_UNBLOCK_ENABLE | OS_HW_HANDSHAKE,
X OS_OPEN_FOR_GETTY | OS_WAIT_OPEN,
X OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_HW_HANDSHAKE,
X OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_UNBLOCK_ENABLE,
X OS_OPEN_FOR_GETTY | OS_WAIT_OPEN | OS_UNBLOCK_ENABLE | OS_HW_HANDSHAKE
X};
X
X/* The following defines are used to take apart the minor device numbers. */
X#define GET_UNIT(dev) ((dev) & 0x0f)
X#define GET_OPEN_MODE(dev) (fas_open_modes [((dev) >> 4) & 0x0f])
X
X/* lock device against concurrent use */
X#define get_device_lock(fip) \
X{\
X /* sleep while device is used by an other process */\
X while ((fip)->device_flags.i & DF_DEVICE_LOCKED)\
X (void) sleep ((caddr_t) &(fip)->device_flags.i, PZERO - 1);\
X (fip)->device_flags.s |= DF_DEVICE_LOCKED;\
X}
X
X/* release device */
X#define release_device_lock(fip) \
X{\
X (fip)->device_flags.s &= ~DF_DEVICE_LOCKED;\
X /* wakeup the process that may wait for this device */\
X (void) wakeup ((caddr_t) &(fip)->device_flags.i);\
X}
X
X/* schedule event */
X#define event_sched(fip,event) \
X{\
X (fip)->event_flags.s |= (event);\
X if (!((fip)->event_flags.i & EF_EVENT_SCHEDULED))\
X {\
X (fip)->event_flags.s |= EF_EVENT_SCHEDULED;\
X (void) timeout (fas_event, (fip), EVENT_TIME);\
X }\
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 an
X NS16550A chip the DF_DEVICE_IS_NS16550 flag is set and the FIFOs will
X be used. If the device is an i82510 chip the DF_DEVICE_IS_I82510 flag
X is set and the device will be handled accordingly.
X*/
X
Xint
Xfasinit ()
X{
X register struct fas_info *fip;
X register uint unit;
X uint logical_units;
X uint port, *seq_ptr;
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 /* execute the init sequence for the serial card */
X for (seq_ptr = fas_init_seq; *seq_ptr; seq_ptr++)
X {
X port = *seq_ptr;
X seq_ptr++;
X if (*seq_ptr > 0xff)
X inb (port);
X else
X outb (port, *seq_ptr);
X }
X
X /* setup the list of pointers to the tty structures */
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 /* setup and initialize all serial ports */
X for (unit = 0; unit < fas_physical_units; unit++)
X {
X fas_info_ptr [unit] = fip = &fas_info [unit];
X port_stat [unit] = '-';
X if (port = fas_port [unit])
X {
X /* init all of its ports */
X fip->uart_port_0 = port;
X fip->uart_port_1 = port + 1;
X fip->uart_port_2 = port + 2;
X fip->uart_port_3 = port + 3;
X fip->uart_port_4 = port + 4;
X fip->uart_port_5 = port + 5;
X fip->uart_port_6 = port + 6;
X fip->int_ack_port = fas_int_ack_port [unit];
X fip->int_ack = fas_int_ack [unit];
X fip->vec = fas_vec [unit];
X fip->modem.i = fas_modem [unit];
X fip->flow.i = fas_flow [unit];
X fip->recv_ring_put_ptr = fip->recv_buffer;
X fip->recv_ring_take_ptr = fip->recv_buffer;
X fip->xmit_ring_put_ptr = fip->xmit_buffer;
X fip->xmit_ring_take_ptr = fip->xmit_buffer;
X
X fip->ier = IE_NONE; /* disable all ints */
X outb (INT_ENABLE_PORT, fip->ier);
X
X /* is there a serial chip ? */
X if (inb (INT_ENABLE_PORT) != fip->ier)
X {
X port_stat [unit] = '?';
X continue; /* a hardware error */
X }
X
X /* test the chip thoroughly */
X if ((port_stat [unit] = (fas_test_device (fip) + '0'))
X != '0')
X {
X continue; /* a hardware error */
X }
X
X fip->lcr = 0;
X outb (LINE_CTL_PORT, fip->lcr);
X fip->mcr = fas_mcb [unit];
X outb (MDM_CTL_PORT, fip->mcr);
X
X port_stat [unit] = '*';
X
X /* let's see if it's an NS16550 */
X outb (NS_FIFO_CTL_PORT, STANDARD_NS_FIFO_INIT);
X if (!(~inb (INT_ID_PORT) & II_NS_FIFO_ENABLED))
X {
X fip->device_flags.s |= DF_DEVICE_IS_NS16550;
X port_stat [unit] = 'F';
X outb (NS_FIFO_CTL_PORT, STANDARD_NS_FIFO_CLEAR);
X }
X else
X {
X outb (NS_FIFO_CTL_PORT, STANDARD_NS_FIFO_CLEAR);
X /* or is it an i82510 ? */
X outb (I_BANK_PORT, I_BANK_2);
X if (!(~inb (I_BANK_PORT) & I_BANK_2))
X {
X fip->device_flags.s |= DF_DEVICE_IS_I82510;
X port_stat [unit] = 'f';
X outb (I_BANK_PORT, I_BANK_1);
X outb (I_TCM_PORT, I_FIFO_CLR_XMIT);
X outb (I_RCM_PORT, I_FIFO_CLR_RECV);
X }
X outb (I_BANK_PORT, I_BANK_0);
X }
X
X /* clear potential interrupts */
X inb (MDM_STATUS_PORT);
X inb (RCV_DATA_PORT);
X inb (RCV_DATA_PORT);
X inb (LINE_STATUS_PORT);
X inb (INT_ID_PORT);
X if (INT_ACK_PORT)
X outb (INT_ACK_PORT, fip->int_ack);
X if (port = fas_mux_ack_port [fip->vec])
X outb (port, fas_mux_ack [fip->vec]);
X
X /* show that it is present and configured */
X fip->device_flags.s |= DF_DEVICE_CONFIGURED;
X }
X }
X
X#if defined (NEED_PUT_GETCHAR)
X fip = &fas_info [0];
X fip->mcr |= INITIAL_MDM_CONTROL;
X outb (MDM_CTL_PORT, fip->mcr);
X
X fip->lcr = INITIAL_LINE_CONTROL;
X outb (LINE_CTL_PORT, fip->lcr | 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);
X#endif
X
X#if defined (SCO)
X for (unit = 0; unit < fas_physical_units; unit++)
X (void) printcfg ("fas", fas_port [unit], 7, fas_vec [unit], -1,
X "unit=%d type=%c release=2.07.0",
X unit, port_stat [unit]);
X#else
X port_stat [unit] = '\0';
X (void) printf ("\nFAS 2.07.0 async driver: Port 0-%d init state is [%s]\n\n",
X unit - 1,
X port_stat);
X#endif
X return (0);
X}
X
X/* Open a tty line. This function is called for every open, as opposed
X to the fasclose function which is called only with the last close.
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 = SPLINT ();
X get_device_lock (fip);
X
X /* If this is a getty open, the device is already open for
X dialout and the FNDELAY flag is not set, wait until device
X is closed.
X */
X while ((open_mode & OS_OPEN_FOR_GETTY)
X && (fip->o_state & OS_OPEN_FOR_DIALOUT)
X && !(flag & FNDELAY))
X {
X release_device_lock (fip);
X (void) sleep ((caddr_t) &fip->o_state, TTIPRI);
X get_device_lock (fip);
X }
X
X /* If the device is already open and another open uses a different
X open mode or if a getty open waits for carrier and doesn't allow
X parallel dialout opens, return with EBUSY 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 && ((flag & FEXCL)
X || ((open_mode ^ fip->o_state) & (u.u_uid
X ? OS_TEST_MASK
X : OS_SU_TEST_MASK))))
X {
X u.u_error = EBUSY;
X release_device_lock (fip);
X (void) splx (old_level);
X return (-1);
X }
X
X /* disable subsequent opens */
X if (flag & FEXCL)
X open_mode |= OS_EXCLUSIVE_OPEN_1;
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 (void) 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#if defined (HAVE_VPIX)
X /* initialize VP/ix related variables */
X fip->v86_proc = (v86_t *) NULL;
X fip->v86_intmask = 0;
X fip->v86_ss.ss_start = CSTART;
X fip->v86_ss.ss_stop = CSTOP;
X#endif
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 /* allow pending tty interrupts */
X (void) SPLWRK ();
X (void) SPLINT ();
X }
X
X /* If getty open and the FNDELAY flag is not set,
X block and wait for carrier if device not yet open.
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 & (ISOPEN | CARR_ON)))
X {
X ttyp->t_state |= WOPEN;
X release_device_lock (fip);
X (void) sleep ((caddr_t) &ttyp->t_canq, TTIPRI);
X get_device_lock (fip);
X }
X ttyp->t_state &= ~WOPEN;
X }
X
X /* wakeup processes that are still sleeping in getty open */
X if (ttyp->t_state & WOPEN)
X (void) wakeup ((caddr_t) &ttyp->t_canq);
X
X /* we need to flush the receiver with the first open */
X if (!(fip->o_state & OS_OPEN_STATES))
X fas_cmd (fip, ttyp, T_RFLUSH);
X
X (*linesw [ttyp->t_line].l_open) (ttyp);
X
X /* set open type flags */
X fip->o_state = open_mode;
X
X if ((open_mode & OS_CHECK_CARR_ON_OPEN)
X && (~fip->msr & fip->modem.m.ca)
X && !(fip->cflag & CLOCAL))
X {
X (void) SPLWRK ();
X (void) signal (ttyp->t_pgrp, SIGHUP);
X (void) ttyflush (ttyp, FREAD | FWRITE);
X (void) SPLINT ();
X }
X
X release_device_lock (fip);
X (void) splx (old_level);
X return (0);
X}
X
X/* Close a tty line. This is only called if there is no other
X concurrent open left. A blocked getty open is not counted as
X a concurrent open because in this state it isn't really open.
X*/
Xint
Xfasclose (dev)
Xint dev;
X{
X register struct fas_info *fip;
X register struct tty *ttyp;
X uint open_mode;
X uint physical_unit;
X int old_level;
X
X physical_unit = GET_UNIT (dev);
X
X fip = fas_info_ptr [physical_unit];
X
X open_mode = GET_OPEN_MODE (dev);
X
X /* set up pointer to tty structure */
X ttyp = (open_mode & OS_OPEN_FOR_GETTY)
X ? fas_tty_ptr [physical_unit + fas_physical_units]
X : fas_tty_ptr [physical_unit];
X
X old_level = SPLINT ();
X
X (*linesw [ttyp->t_line].l_close) (ttyp);
X
X get_device_lock (fip);
X
X /* allow pending tty interrupts */
X (void) SPLWRK ();
X (void) SPLINT ();
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 defined (HAVE_VPIX)
X /* initialize VP/ix related variables */
X fip->v86_proc = (v86_t *) NULL;
X fip->v86_intmask = 0;
X fip->v86_ss.ss_start = CSTART;
X fip->v86_ss.ss_stop = CSTOP;
X#endif
X if (!(fip->device_flags.i & DF_DO_HANGUP))
X {
X fas_open_device (fip);
X /* set up port registers */
X fas_param (fip);
X /* set up modem status flags */
X fas_mproc (fip);
X }
X }
X (void) wakeup ((caddr_t) &fip->o_state);
X }
X
X if (!(fip->device_flags.i & DF_DO_HANGUP))
X release_device_lock (fip);
X
X (void) splx (old_level);
X return (0);
X}
X
X/* read characters from the input buffer */
Xint
Xfasread (dev)
Xint dev;
X{
X register struct fas_info *fip;
X register struct tty *ttyp;
X int old_level;
X
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 = SPLINT ();
X
X#if defined (HAVE_VPIX)
X /* wakeup VP/ix */
X if ((fip->iflag & DOSMODE) && ttyp->t_rawq.c_cc)
X event_sched (fip, EF_SIGNAL_VPIX);
X#endif
X /* schedule character transfer to UNIX buffer */
X if (fip->recv_ring_cnt
X#if defined (HAVE_VPIX)
X && (((fip->iflag & DOSMODE)
X ? MAX_VPIX_FILL - MIN_READ_CHUNK
X : MAX_UNIX_FILL - MIN_READ_CHUNK)
X >= fip->tty->t_rawq.c_cc)
X#else
X && ((MAX_UNIX_FILL - MIN_READ_CHUNK) >= fip->tty->t_rawq.c_cc)
X#endif
X && !(fip->flow_flags.i & FF_RXFER_STOPPED))
X {
X event_sched (fip, EF_DO_RXFER);
X }
X
X (void) splx (old_level);
X return (0);
X}
X
X/* write characters to the output buffer */
Xint
Xfaswrite (dev)
Xint dev;
X{
X register struct tty *ttyp;
X
X ttyp = fas_info_ptr [GET_UNIT (dev)]->tty;
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 int v86_cmd, v86_data;
X int old_level;
X REGVAR;
X
X fip = fas_info_ptr [GET_UNIT (dev)];
X
X ttyp = fip->tty;
X
X /* process ioctl commands */
X switch (cmd)
X {
X#if defined (HAVE_VPIX)
X case AIOCINTTYPE: /* set pseudorupt type */
X switch (arg3.iarg)
X {
X case V86VI_KBD:
X case V86VI_SERIAL0:
X case V86VI_SERIAL1:
X intr_disable ();
X fip->v86_intmask = arg3.iarg;
X intr_restore ();
X break;
X
X default:
X intr_disable ();
X fip->v86_intmask = V86VI_SERIAL0;
X intr_restore ();
X break;
X }
X break;
X
X case AIOCDOSMODE: /* enable dos mode */
X if (!(fip->iflag & DOSMODE))
X {
X old_level = SPLINT ();
X fip->v86_proc = u.u_procp->p_v86;
X if (!(fip->v86_intmask))
X fip->v86_intmask = V86VI_SERIAL0;
X ttyp->t_iflag |= DOSMODE;
X if (fip->v86_intmask != V86VI_KBD)
X ttyp->t_cflag |= CLOCAL;
X fas_param (fip);
X (void) splx (old_level);
X }
X u.u_r.r_reg.r_val1 = 0;
X break;
X
X case AIOCNONDOSMODE: /* disable dos mode */
X if (fip->iflag & DOSMODE)
X {
X old_level = SPLINT ();
X fip->v86_proc = (v86_t *) NULL;
X fip->v86_intmask = 0;
X ttyp->t_iflag &= ~DOSMODE;
X if (fip->flow_flags.i & FF_RXFER_STOPPED)
X {
X fip->flow_flags.s &= ~FF_RXFER_STOPPED;
X /* schedule character transfer
X to UNIX buffer
X */
X if (fip->recv_ring_cnt)
X event_sched (fip, EF_DO_RXFER);
X }
X fip->lcr &= ~LC_SET_BREAK_LEVEL;
X fas_param (fip);
X (void) splx (old_level);
X }
X u.u_r.r_reg.r_val1 = 0;
X break;
X
X case AIOCSERIALOUT: /* setup port registers for dos */
X if ((fip->iflag & DOSMODE) && fip->v86_proc)
X {
X /* wait until output is done */
X (void) ttywait (ttyp);
X
X /* block transmitter and wait until it is
X empty
X */
X old_level = SPLINT ();
X fip->device_flags.s |= DF_XMIT_LOCKED;
X while (fip->device_flags.i & (DF_XMIT_BUSY
X | DF_GUARD_TIMEOUT))
X (void) sleep ((caddr_t) &(fip)->
X device_flags.i,
X PZERO - 1);
X (void) splx (old_level);
X
X /* get port write command */
X v86_cmd = fubyte (arg3.cparg);
X /* set divisor lsb requested */
X if (v86_cmd & SIO_MASK(SO_DIVLLSB))
X {
X v86_data = fubyte (arg3.cparg
X + SO_DIVLLSB);
X intr_disable ();
X outb (LINE_CTL_PORT, fip->lcr
X | LC_ENABLE_DIVISOR);
X outb (DIVISOR_LSB_PORT, v86_data);
X outb (LINE_CTL_PORT, fip->lcr
X & ~LC_ENABLE_DIVISOR);
X intr_restore ();
X }
X /* set divisor msb requested */
X if (v86_cmd & SIO_MASK(SO_DIVLMSB))
X {
X v86_data = fubyte (arg3.cparg
X + SO_DIVLMSB);
X intr_disable ();
X outb (LINE_CTL_PORT, fip->lcr
X | LC_ENABLE_DIVISOR);
X outb (DIVISOR_MSB_PORT, v86_data);
X outb (LINE_CTL_PORT, fip->lcr
X & ~LC_ENABLE_DIVISOR);
X intr_restore ();
X }
X /* set lcr requested */
X if (v86_cmd & SIO_MASK(SO_LCR))
X {
X v86_data = fubyte (arg3.cparg + SO_LCR);
X intr_disable ();
X fip->lcr = v86_data
X & ~LC_ENABLE_DIVISOR;
X outb (LINE_CTL_PORT, fip->lcr);
X intr_restore ();
X }
X /* set mcr requested */
X if (v86_cmd & SIO_MASK(SO_MCR))
X {
X v86_data = fubyte (arg3.cparg + SO_MCR);
X old_level = SPLINT ();
X /* virtual dtr processing */
X if (v86_data & MC_SET_DTR)
X {
X fip->device_flags.s
X |= DF_MODEM_ENABLED;
X fip->mcr |= fip->modem.m.en;
X }
X else
X {
X fip->device_flags.s
X &= ~DF_MODEM_ENABLED;
X fip->mcr &= ~fip->modem.m.en;
X }
X /* virtual rts processing */
X if (!(fip->flow_flags.i
X & FF_HWI_HANDSHAKE))
X {
X if (v86_data & MC_SET_RTS)
X {
X fip->flow_flags.s
X &= ~FF_HWI_STOPPED;
X fip->mcr
X |= fip->flow.m.ic;
X }
X else
X {
X fip->flow_flags.s
X |= FF_HWI_STOPPED;
X fip->mcr
X &= ~fip->flow.m.ic;
X }
X }
X else
X {
X if (v86_data & MC_SET_RTS)
X {
X if (fip->flow_flags.i
X & FF_RXFER_STOPPED)
X {
X fip->flow_flags.s
X &= ~FF_RXFER_STOPPED;
X /* schedule character transfer
X to UNIX buffer
X */
X if (fip->recv_ring_cnt)
X event_sched (fip,
X EF_DO_RXFER);
X }
X }
X else
X fip->flow_flags.s
X |= FF_RXFER_STOPPED;
X }
X outb (MDM_CTL_PORT, fip->mcr);
X (void) splx (old_level);
X }
X
X old_level = SPLINT ();
X /* enable transmitter and restart output */
X fip->device_flags.s &= ~DF_XMIT_LOCKED;
X fas_xproc (fip);
X (void) splx (old_level);
X }
X break;
X
X case AIOCSERIALIN: /* read port registers for dos */
X if ((fip->iflag & DOSMODE) && fip->v86_proc)
X {
X v86_cmd = fubyte (arg3.cparg);
X if (v86_cmd & SIO_MASK(SI_MSR))
X {
X (void) subyte (arg3.cparg + SI_MSR,
X fip->msr);
X }
X }
X break;
X
X case AIOCSETSS: /* set start/stop characters */
X intr_disable ();
X *((short *) &fip->v86_ss) = arg3.iarg;
X intr_restore ();
X break;
X
X case AIOCINFO: /* show what type of device we are */
X u.u_r.r_reg.r_val1 = ('a' << 8) | (dev & 0xff);
X break;
X#endif
X default: /* default ioctl processing */
X /* if it is a TCSETA* command, call fas_param () */
X if (ttiocom (ttyp, cmd, arg3, arg4))
X {
X old_level = SPLINT ();
X fas_param (fip);
X (void) splx (old_level);
X }
X break;
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 = SPLINT ();
X fas_cmd (fas_info_ptr [physical_unit], ttyp, arg2);
X (void) splx (old_level);
X return (0);
X}
X
X/* set up a port according to the given termio structure */
Xstatic void
Xfas_param (fip)
Xregister struct fas_info *fip;
X{
X register uint cflag;
X uint divisor;
X int xmit_ring_size;
X REGVAR;
X
X /* lock transmitter and wait until it is empty */
X fip->device_flags.s |= DF_XMIT_LOCKED;
X while (fip->device_flags.i & (DF_XMIT_BUSY | DF_GUARD_TIMEOUT))
X (void) sleep ((caddr_t) &(fip)->device_flags.i, PZERO - 1);
X
X cflag = fip->tty->t_cflag;
X
X#if defined (HAVE_VPIX)
X /* we don't set port registers if we are in dos mode */
X if (fip->tty->t_iflag & DOSMODE)
X goto setflags;
X#endif
X /* hangup line if it is baud rate 0, else enable line */
X if ((cflag & CBAUD) == B0)
X {
X cflag = (cflag & ~CBAUD) | (fip->cflag & CBAUD);
X fip->mcr &= ~fip->modem.m.en;
X outb (MDM_CTL_PORT, fip->mcr);
X fip->device_flags.s &= ~DF_MODEM_ENABLED;
X }
X else
X {
X if (!(fip->device_flags.i & DF_MODEM_ENABLED))
X {
X fip->mcr |= fip->modem.m.en;
X outb (MDM_CTL_PORT, fip->mcr);
X fip->device_flags.s |= DF_MODEM_ENABLED;
X }
X }
X
X /* don't change break flag */
X fip->lcr &= LC_SET_BREAK_LEVEL;
X
X /* set character size */
X switch (cflag & CSIZE)
X {
X case CS5:
X fip->lcr |= LC_WORDLEN_5;
X break;
X
X case CS6:
X fip->lcr |= LC_WORDLEN_6;
X break;
X
X case CS7:
X fip->lcr |= LC_WORDLEN_7;
X break;
X
X default:
X fip->lcr |= LC_WORDLEN_8;
X break;
X }
X
X /* set # of stop bits */
X if (cflag & CSTOPB)
X fip->lcr |= LC_STOPBITS_LONG;
X
X /* set parity */
X if (cflag & PARENB)
X {
X fip->lcr |= LC_ENABLE_PARITY;
X
X if (!(cflag & PARODD))
X fip->lcr |= LC_EVEN_PARITY;
X }
X
X /* get counter divisor for selected baud rate */
X divisor = fas_speeds [cflag & CBAUD];
X /* set LCR and baud rate */
X outb (LINE_CTL_PORT, fip->lcr | LC_ENABLE_DIVISOR);
X outb (DIVISOR_LSB_PORT, divisor);
X outb (DIVISOR_MSB_PORT, divisor >> 8);
X outb (LINE_CTL_PORT, fip->lcr);
X
X /* check dynamic xmit ring buffer size against boundaries,
X modify it if necessary and update the fas_info structure
X */
X xmit_ring_size = fas_xbuf_size [cflag & CBAUD] - TTXOHI;
X if (xmit_ring_size < OUTPUT_NS_FIFO_SIZE * 2)
X {
Xsetflags:
X xmit_ring_size = OUTPUT_NS_FIFO_SIZE * 2;
X }
X if (xmit_ring_size > XMIT_BUFF_SIZE)
X xmit_ring_size = XMIT_BUFF_SIZE;
X fip->xmit_ring_size = xmit_ring_size;
X
X /* disable modem control signals if required by open mode */
X if (fip->o_state & OS_CLOCAL)
X cflag |= CLOCAL;
X
X /* Fake the carrier detect state flag if CLOCAL mode or if
X requested by open mode.
X */
X if (!(~fip->msr & fip->modem.m.ca)
X || (fip->o_state & OS_FAKE_CARR_ON)
X || (cflag & CLOCAL))
X fip->tty->t_state |= CARR_ON;
X else
X fip->tty->t_state &= ~CARR_ON;
X
X#if defined (XCLUDE) /* SYSV 3.2 Xenix compatibility */
X /* Permit exclusive use of this device. */
X if (cflag & XCLUDE)
X fip->o_state |= OS_EXCLUSIVE_OPEN_2;
X else
X fip->o_state &= ~OS_EXCLUSIVE_OPEN_2;
X#endif
X
X /* Select hardware handshake depending on the minor device
X number and the CTSFLOW and RTSFLOW flags (if they are
X available).
X */
X if (fip->o_state & OS_HW_HANDSHAKE)
X {
X fip->flow_flags.s |= FF_HWO_HANDSHAKE | FF_HWI_HANDSHAKE;
X }
X else
X {
X fip->flow_flags.s &= ~(FF_HWO_HANDSHAKE | FF_HWI_HANDSHAKE);
X#if defined (CTSFLOW) /* SYSV 3.2 Xenix compatibility */
X if (cflag & CTSFLOW)
X fip->flow_flags.s |= FF_HWO_HANDSHAKE;
X#endif
X#if defined (RTSFLOW) /* SYSV 3.2 Xenix compatibility */
X if (cflag & RTSFLOW)
X fip->flow_flags.s |= FF_HWI_HANDSHAKE;
X#endif
X }
X
X fip->cflag = cflag;
X fip->iflag = fip->tty->t_iflag;
X
X /* enable transmitter */
X fip->device_flags.s &= ~DF_XMIT_LOCKED;
X
X /* setup handshake flags */
X fas_ihlw_check (fip);
X fas_fproc (fip, fip->new_msr);
X
X /* restart output */
X fas_xproc (fip);
X}
X
X/* Main fas interrupt handler. Actual character processing is splitted
X into sub-functions.
X*/
Xint
Xfasintr (vect)
Xint vect;
X{
X register struct fas_info *fip;
X register uint status;
X int done;
X uint port, old_recv_count;
X REGVAR;
X
X /* The 8259 interrupt controller is set up for edge trigger.
X Therefor we must loop until we make a complete pass without
X getting any UARTs that are interrupting.
X */
X do
X {
X done = TRUE;
X fip = fas_first_int_user [vect];
X
X /* loop through all users of this interrupt vector */
X for (;; fip = fip->next_int_user)
X {
X if (!fip)
X break; /* all users done */
X
X /* process only ports that we expect ints from
X and that actually need to be serviced
X */
Xfastloop:
X if (inb (INT_ID_PORT) & II_NO_INTS_PENDING)
X {
X /* speed beats beauty */
X fip = fip->next_int_user;
X if (fip)
X goto fastloop;
X break;
X }
X
X done = FALSE; /* not done if we got an int */
X old_recv_count = fip->recv_ring_cnt;
X
X do
X {
X /* read in all the characters from the FIFO */
X if ((status = inb (LINE_STATUS_PORT))
X & LS_RCV_INT)
X {
X status = fas_rproc (fip, status);
X sysinfo.rcvint++;
X }
X
X /* Is it a transmitter empty int ? */
X if ((status & LS_XMIT_AVAIL)
X && (fip->device_flags.i & DF_XMIT_BUSY))
X {
X fip->device_flags.s &= ~DF_XMIT_BUSY;
X fas_xproc (fip);
X if (!(fip->device_flags.i & DF_XMIT_BUSY)
X && !fip->xmit_ring_cnt)
X {
X fip->device_flags.s |=
X DF_GUARD_TIMEOUT;
X fip->tty->t_state |=
X TIMEOUT;
X fip->timeout_idx =
X timeout (
X fas_timeout, fip,
X fas_ctimes [fip->cflag
X & CBAUD]);
X }
X sysinfo.xmtint++;
X }
X
X /* Has there been a polarity change on
X some of the modem lines ?
X */
X if ((status = inb (MDM_STATUS_PORT))
X & MS_ANY_DELTA)
X {
X /* Do special RING line handling.
X RING generates an int only on the
X trailing edge.
X */
X status = (status & ~MS_RING_PRESENT)
X | (fip->new_msr
X & MS_RING_PRESENT);
X if (status & MS_RING_TEDGE)
X status |= MS_RING_PRESENT;
X if ((status ^ fip->new_msr)
X & MS_ANY_PRESENT)
X {
X /* check hw flow flags */
X fas_fproc (fip, status);
X fip->new_msr = status;
X event_sched (fip, EF_DO_MPROC);
X }
X sysinfo.mdmint++;
X }
X } while (!(inb (INT_ID_PORT) & II_NO_INTS_PENDING));
X
X /* clear the port interrupt */
X if (INT_ACK_PORT)
X outb (INT_ACK_PORT, fip->int_ack);
X
X /* schedule character transfer to UNIX buffer */
X if (fip->recv_ring_cnt
X#if defined (HAVE_VPIX)
X && (((fip->iflag & DOSMODE)
X ? MAX_VPIX_FILL - MIN_READ_CHUNK
X : MAX_UNIX_FILL - MIN_READ_CHUNK)
X >= fip->tty->t_rawq.c_cc)
X#else
X && ((MAX_UNIX_FILL - MIN_READ_CHUNK)
X >= fip->tty->t_rawq.c_cc)
X#endif
X && !(fip->flow_flags.i & FF_RXFER_STOPPED))
X {
X event_sched (fip, EF_DO_RXFER);
X }
X
X /* check input buffer high/low water marks */
X if (fip->recv_ring_cnt != old_recv_count)
X fas_ihlw_check (fip);
X }
X } while (!done);
X
X /* clear the mux interrupt since we have scanned all
X of the ports that share this interrupt vector
X */
X if (port = fas_mux_ack_port [vect])
X outb (port, fas_mux_ack [vect]);
X
X return (0);
X}
X
X/* hardware flow control interrupt handler */
Xstatic void
Xfas_fproc (fip, mdm_status)
Xregister struct fas_info *fip;
Xregister uint mdm_status;
X{
X /* Check the output flow control signals and set the state flag
X accordingly.
X */
X if (!(~mdm_status & fip->flow.m.oc)
X || (~mdm_status & fip->flow.m.oe)
X || !(fip->flow_flags.i & FF_HWO_HANDSHAKE))
X {
X if (fip->flow_flags.i & FF_HWO_STOPPED)
X {
X fip->flow_flags.s &= ~FF_HWO_STOPPED;
X fas_xproc (fip);
X }
X }
X else
X fip->flow_flags.s |= FF_HWO_STOPPED;
X}
X
X/* modem status handler */
Xstatic void
Xfas_mproc (fip)
Xregister struct fas_info *fip;
X{
X register struct tty *ttyp;
X register uint mdm_status;
X int old_level;
X
X ttyp = fip->tty;
X mdm_status = fip->new_msr;
X fip->new_msr &= ~MS_RING_PRESENT;
X
X /* Check the carrier detect signal and set the state flags
X accordingly. Also, if not in clocal mode, send SIGHUP on
X carrier loss and flush the buffers.
X */
X if (!(fip->cflag & CLOCAL))
X {
X if (!(~mdm_status & fip->modem.m.ca))
X {
X ttyp->t_state |= CARR_ON;
X /* Unblock getty open only if it is ready to run. */
X if (ttyp->t_state & WOPEN)
X (void) wakeup ((caddr_t) &ttyp->t_canq);
X }
X else
X {
X if (!(~fip->msr & fip->modem.m.ca))
X {
X ttyp->t_state &= ~CARR_ON;
X old_level = SPLWRK ();
X if (ttyp->t_state & ISOPEN)
X (void) signal (ttyp->t_pgrp, SIGHUP);
X (void) ttyflush (ttyp, FREAD | FWRITE);
X (void) splx (old_level);
X }
X }
X }
X
X /* Check the unblock signal. If low->high edge, fake CARR_ON state
X flag and wake up getty open.
X */
X if ((fip->o_state & OS_UNBLOCK_ENABLE)
X && !(fip->cflag & CLOCAL)
X && !(~mdm_status & fip->modem.m.ub)
X && (~fip->msr & fip->modem.m.ub)
X && (ttyp->t_state & WOPEN))
X {
X ttyp->t_state |= CARR_ON;
X (void) wakeup ((caddr_t) &ttyp->t_canq);
X }
X
X fip->msr = mdm_status & ~MS_RING_PRESENT;
X
X /* re-schedule if modem status flags have changed in the mean time */
X if ((fip->new_msr ^ fip->msr) & MS_ANY_PRESENT)
X event_sched (fip, EF_DO_MPROC);
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 /* do we have to junk the character ? */
X if (!(fip->cflag & CREAD) || !(ttyp->t_state & ISOPEN))
X {
X /* if there are FIFOs we take a short cut */
X if (fip->device_flags.i & DF_DEVICE_IS_NS16550)
X outb (NS_FIFO_CTL_PORT, STANDARD_NS_FIFO_SETUP
X | NS_FIFO_CLR_RECV);
X else if (fip->device_flags.i & DF_DEVICE_IS_I82510)
X {
X outb (I_BANK_PORT, I_BANK_1);
X outb (I_RCM_PORT, I_FIFO_CLR_RECV);
X outb (I_BANK_PORT, I_BANK_0);
X }
X continue;
X }
X
X csize = 0;
X
X /* strip off 8th bit ? */
X if (fip->iflag & ISTRIP)
X charac &= 0x7f;
X
X /* ignore parity errors ? */
X if ((line_status & LS_PARITY_ERROR)
X && !(fip->iflag & INPCK))
X line_status &= ~LS_PARITY_ERROR;
X
X /* do we have some kind of character error ? */
X if (line_status & (LS_PARITY_ERROR
X | LS_FRAMING_ERROR
X | LS_BREAK_DETECTED))
X {
X#if defined (HAVE_VPIX)
X if ((fip->iflag & (DOSMODE | PARMRK))
X == (DOSMODE | PARMRK))
X {
X /* send status bits to VP/ix */
X (void) fas_vpix_sr (fip, 1,
X line_status & (LS_PARITY_ERROR
X | LS_FRAMING_ERROR
X | LS_BREAK_DETECTED));
X /* we don't handle character errors in
X this routine if we are in DOS mode
X */
X line_status &= ~(LS_PARITY_ERROR
X | LS_FRAMING_ERROR
X | LS_BREAK_DETECTED);
X goto valid_char;
X }
X#endif
X /* is it a BREAK ? */
X if (line_status & LS_BREAK_DETECTED)
X {
X if (!(fip->iflag & IGNBRK))
X if (fip->iflag & BRKINT)
X {
X /* do BREAK interrupt */
X event_sched (fip, EF_DO_BRKINT);
X }
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
Xvalid_char:
X /* is there a character to process ? */
X if (line_status & LS_RCV_AVAIL)
X {
X if (fip->iflag & IXON)
X {
X /* do output start/stop handling */
X if (fip->flow_flags.i & FF_SWO_STOPPED)
X {
X#if defined (HAVE_VPIX)
X if ((charac == fip->v86_ss.ss_start)
X#else
X if ((charac == CSTART)
X#endif
X || (fip->iflag & IXANY))
X {
X fip->flow_flags.s &=
X ~FF_SWO_STOPPED;
X ttyp->t_state &= ~TTSTOP;
X /* restart output */
X fas_xproc (fip);
X }
X }
X else
X {
X#if defined (HAVE_VPIX)
X if (charac == fip->v86_ss.ss_stop)
X#else
X if (charac == CSTOP)
X#endif
X {
X fip->flow_flags.s |=
X FF_SWO_STOPPED;
X ttyp->t_state |= TTSTOP;
X }
X }
X /* we don't put start/stop characters
X into the receiver buffer
X */
X#if defined (HAVE_VPIX)
X if ((charac == fip->v86_ss.ss_start)
X || (charac == fip->v86_ss.ss_stop))
X#else
X if ((charac == CSTART)
X || (charac == CSTOP))
X#endif
X continue;
X }
X
X if ((charac == 0xff) && (fip->iflag & PARMRK))
X {
X metta [csize] = 0xff;
X csize++;
X metta [csize] = 0xff;
X csize++;
X }
X else
X {
X /* we take a short-cut if only one character
X has to be put into the receiver buffer
X */
X if (fip->recv_ring_cnt < RECV_BUFF_SIZE)
X {
X fip->recv_ring_cnt++;
X *fip->recv_ring_put_ptr = charac;
X if (++fip->recv_ring_put_ptr
X != &fip->recv_buffer
X [RECV_BUFF_SIZE])
X continue;
X fip->recv_ring_put_ptr =
X &fip->recv_buffer [0];
X }
X continue;
X }
X }
X
X if (!(csize) || (fip->recv_ring_cnt + csize > RECV_BUFF_SIZE))
X continue; /* nothing to put into recv buffer */
X
X fip->recv_ring_cnt += csize;
X
X /* store translation in ring buffer */
X do
X {
X do
X {
X *fip->recv_ring_put_ptr = (metta - 1) [csize];
X if (++fip->recv_ring_put_ptr
X == &fip->recv_buffer [RECV_BUFF_SIZE])
X break;
X } while (--csize);
X if (!csize)
X break;
X fip->recv_ring_put_ptr = &fip->recv_buffer [0];
X } while (--csize);
X } while ((line_status = inb (LINE_STATUS_PORT)) & LS_RCV_INT);
X
X return (line_status);
X}
X
X/* Output characters to the transmitter register. */
Xstatic void
Xfas_xproc (fip)
Xregister struct fas_info *fip;
X{
X register uint num_to_output;
X REGVAR;
X
X /* proceed only if transmitter is available */
X if ((fip->device_flags.i & (DF_XMIT_BUSY | DF_XMIT_BREAK
X | DF_XMIT_LOCKED))
X || (fip->flow_flags.i & FF_HWO_STOPPED))
X goto sched;
X
X /* determine the transmitter FIFO size */
X if (fip->device_flags.i & (DF_DEVICE_IS_NS16550
X | DF_DEVICE_IS_I82510))
X {
X if (fip->device_flags.i & DF_DEVICE_IS_NS16550)
X num_to_output = OUTPUT_NS_FIFO_SIZE;
X else
X num_to_output = OUTPUT_I_FIFO_SIZE;
X }
X else
X num_to_output = 1;
X
X /* handle XON/XOFF input flow control requests */
X if (fip->flow_flags.i & FF_SW_FC_REQ)
X {
X#if defined (HAVE_VPIX)
X outb (XMT_DATA_PORT, (fip->flow_flags.i & FF_SWI_STOPPED)
X ? fip->v86_ss.ss_stop
X : fip->v86_ss.ss_start);
X#else
X outb (XMT_DATA_PORT, (fip->flow_flags.i & FF_SWI_STOPPED)
X ? CSTOP
X : CSTART);
X#endif
X fip->tty->t_state &= ~(TTXON | TTXOFF);
X fip->device_flags.s |= DF_XMIT_BUSY;
X fip->flow_flags.s &= ~FF_SW_FC_REQ;
X /* disable guard timeout */
X if (fip->device_flags.i & DF_GUARD_TIMEOUT)
X {
X fip->device_flags.s &= ~DF_GUARD_TIMEOUT;
X fip->tty->t_state &= ~TIMEOUT;
X (void) untimeout (fip->timeout_idx);
X }
X num_to_output--;
X }
X
X /* bail out if output is suspended by XOFF */
X if (fip->flow_flags.i & FF_SWO_STOPPED)
X goto sched;
X
X /* Determine how many chars to put into the transmitter
X register.
X */
X if (fip->xmit_ring_cnt < num_to_output)
X num_to_output = fip->xmit_ring_cnt;
X
X /* no characters available ? */
X if (!num_to_output)
X goto sched;
X
X /* output characters */
X fip->xmit_ring_cnt -= num_to_output;
X
X do
X {
X do
X {
X outb (XMT_DATA_PORT, *fip->xmit_ring_take_ptr);
X if (++fip->xmit_ring_take_ptr
X == &fip->xmit_buffer [XMIT_BUFF_SIZE])
X break;
X } while (--num_to_output);
X if (!num_to_output)
X break;
X fip->xmit_ring_take_ptr = &fip->xmit_buffer [0];
X } while (--num_to_output);
X
X /* signal that transmitter is busy now */
X fip->device_flags.s |= DF_XMIT_BUSY;
X /* disable guard timeout */
X if (fip->device_flags.i & DF_GUARD_TIMEOUT)
X {
X fip->device_flags.s &= ~DF_GUARD_TIMEOUT;
X fip->tty->t_state &= ~TIMEOUT;
X (void) untimeout (fip->timeout_idx);
X }
X
X /* schedule fas_xxfer () if there are more characters to transfer
X into the transmitter ring buffer
X */
Xsched:
X if ((fip->xmit_ring_size > fip->xmit_ring_cnt)
X && (fip->tty->t_outq.c_cc || fip->tty->t_tbuf.c_count))
X {
X event_sched (fip, EF_DO_XXFER);
X }
X}
X
X/* Asynchronous event handler. Scheduled by functions that can't do the
X processing themselves because of execution time restrictions.
X*/
Xstatic void
Xfas_event (fip)
Xregister struct fas_info *fip;
X{
X int old_level;
X
X old_level = SPLINT ();
X
X do
X {
X /* do the break interrupt */
X if (fip->event_flags.i & EF_DO_BRKINT)
X {
X fip->event_flags.s &= ~EF_DO_BRKINT;
X if (fip->tty->t_state & ISOPEN)
X {
X (void) SPLWRK ();
X (*linesw [fip->tty->t_line].l_input)
X (fip->tty, L_BREAK);
X (void) SPLINT ();
X }
X }
X
X /* transfer characters to the UNIX input buffer */
X if (fip->event_flags.i & EF_DO_RXFER)
X {
X fip->event_flags.s &= ~EF_DO_RXFER;
X if (!(fip->flow_flags.i & FF_RXFER_STOPPED))
X {
X (void) SPLWRK ();
X fas_rxfer (fip);
X (void) SPLINT ();
X /* check input buffer high/low water marks */
X fas_ihlw_check (fip);
X }
X }
X
X /* transfer characters to the output ring buffer */
X if (fip->event_flags.i & EF_DO_XXFER)
X {
X fip->event_flags.s &= ~EF_DO_XXFER;
X (void) SPLWRK ();
X fas_xxfer (fip);
X (void) SPLINT ();
X /* output characters */
X fas_xproc (fip);
X }
X
X /* check the modem signals */
X if (fip->event_flags.i & EF_DO_MPROC)
X {
X fip->event_flags.s &= ~EF_DO_MPROC;
X fas_mproc (fip);
X#if defined (HAVE_VPIX)
X if (((fip->iflag & (DOSMODE | PARMRK))
X == (DOSMODE | PARMRK))
X && (fip->v86_intmask != V86VI_KBD))
X {
X /* send status bits to VP/ix */
X if (fas_vpix_sr (fip, 2, fip->msr))
X fip->event_flags.s |= EF_DO_RXFER;
X }
X#endif
X }
X
X#if defined (HAVE_VPIX)
X /* send pseudorupt to VP/ix */
X if (fip->event_flags.i & EF_SIGNAL_VPIX)
X {
X fip->event_flags.s &= ~EF_SIGNAL_VPIX;
X if ((fip->iflag & DOSMODE) && fip->v86_proc)
X {
X (void) SPLWRK ();
X (void) v86setint (fip->v86_proc,
X fip->v86_intmask);
X (void) SPLINT ();
X }
X }
X#endif
X } while (fip->event_flags.i & EF_EVENT_MASK); /* all done ? */
X
X fip->event_flags.s &= ~EF_EVENT_SCHEDULED;
X (void) splx (old_level);
X}
X
X#if defined (HAVE_VPIX)
X/* Send port status register to VP/ix */
Xstatic int
Xfas_vpix_sr (fip, token, status)
Xregister struct fas_info *fip;
Xuint token;
Xuint status;
X{
X if ((fip->recv_ring_cnt <= RECV_BUFF_SIZE - 3)
X && (fip->tty->t_state & ISOPEN))
X {
X /* sent the character sequence 0xff, <token>, <status>
X to VP/ix
X */
X fip->recv_ring_cnt += 3;
X
X *fip->recv_ring_put_ptr = 0xff;
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 *fip->recv_ring_put_ptr = token;
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 *fip->recv_ring_put_ptr = status;
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 return (TRUE);
X }
X return (FALSE);
X}
X#endif
X
X/* Receiver ring buffer -> UNIX buffer transfer function. */
Xstatic void
Xfas_rxfer (fip)
Xregister struct fas_info *fip;
X{
X register struct tty *ttyp;
X register int num_to_xfer;
X int num_save;
X int old_level;
X
X ttyp = fip->tty;
X
X for (;;)
X {
X if (!fip->recv_ring_cnt || !ttyp->t_rbuf.c_ptr)
X break; /* no characters to transfer */
X
X /* determine how many characters to transfer */
X#if defined (HAVE_VPIX)
X num_to_xfer = ((fip->iflag & DOSMODE)
X ? MAX_VPIX_FILL
X : MAX_UNIX_FILL) - ttyp->t_rawq.c_cc;
X#else
X num_to_xfer = MAX_UNIX_FILL - ttyp->t_rawq.c_cc;
X#endif
X
X if (num_to_xfer < MIN_READ_CHUNK)
X break; /* input buffer full */
X
X#if defined (HAVE_VPIX)
X /* wakeup VP/ix */
X if ((fip->iflag & DOSMODE) && !ttyp->t_rawq.c_cc)
X event_sched (fip, EF_SIGNAL_VPIX);
X#endif
X
X /* determine how many characters are in one contigous block */
X if (fip->recv_ring_cnt < num_to_xfer)
X num_to_xfer = fip->recv_ring_cnt;
X if (&fip->recv_buffer [RECV_BUFF_SIZE] - fip->recv_ring_take_ptr
X < num_to_xfer)
X num_to_xfer = &fip->recv_buffer [RECV_BUFF_SIZE]
X - fip->recv_ring_take_ptr;
X if (ttyp->t_rbuf.c_count < num_to_xfer)
X num_to_xfer = ttyp->t_rbuf.c_count;
X
X num_save = num_to_xfer;
X ttyp->t_rbuf.c_count -= num_to_xfer;
X
X /* do the transfer */
X do
X {
X *ttyp->t_rbuf.c_ptr = *fip->recv_ring_take_ptr;
X ttyp->t_rbuf.c_ptr++;
X fip->recv_ring_take_ptr++;
X } while (--num_to_xfer);
X
X if (fip->recv_ring_take_ptr == &fip->recv_buffer [RECV_BUFF_SIZE])
X fip->recv_ring_take_ptr = &fip->recv_buffer [0];
X
X intr_disable ();
X fip->recv_ring_cnt -= num_save;
X intr_restore ();
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}
X
X/* UNIX buffer -> transmitter ring buffer transfer function. */
Xstatic void
Xfas_xxfer (fip)
Xregister struct fas_info *fip;
X{
X register struct tty *ttyp;
X register int num_to_xfer;
X int num_save;
X int old_level;
X
X ttyp = fip->tty;
X
X for (;;)
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
X /* set the maximum character limit */
X num_to_xfer = fip->xmit_ring_size - fip->xmit_ring_cnt;
X
X /* Return if transmitter ring buffer is full. */
X if (num_to_xfer < 1)
X break;
X
X /* Determine how many chars to transfer this time. */
X if (&fip->xmit_buffer [XMIT_BUFF_SIZE] - fip->xmit_ring_put_ptr
X < num_to_xfer)
X num_to_xfer = &fip->xmit_buffer [XMIT_BUFF_SIZE]
X - fip->xmit_ring_put_ptr;
X if (ttyp->t_tbuf.c_count < num_to_xfer)
X num_to_xfer = ttyp->t_tbuf.c_count;
X
X num_save = num_to_xfer;
X ttyp->t_tbuf.c_count -= num_to_xfer;
X ttyp->t_state |= BUSY;
X
X /* do the transfer */
X do
X {
X *fip->xmit_ring_put_ptr = *ttyp->t_tbuf.c_ptr;
X ttyp->t_tbuf.c_ptr++;
X fip->xmit_ring_put_ptr++;
X } while (--num_to_xfer);
X
X if (fip->xmit_ring_put_ptr == &fip->xmit_buffer [XMIT_BUFF_SIZE])
X fip->xmit_ring_put_ptr = &fip->xmit_buffer [0];
X
X intr_disable ();
X fip->xmit_ring_cnt += num_save;
X intr_restore ();
X }
X}
X
X/* Input buffer high/low water mark check. */
Xstatic void
Xfas_ihlw_check (fip)
Xregister struct fas_info *fip;
X{
X REGVAR;
X
X#if defined (HAVE_VPIX)
X /* don't touch the mcr if we are in dos mode and hardware input
X handshake is disabled (dos handles the handshake line(s)
X on its own in this mode)
X */
X if ((fip->iflag & DOSMODE) && !(fip->flow_flags.i & FF_HWI_HANDSHAKE))
X goto swflow;
X#endif
X if (fip->flow_flags.i & FF_HWI_STOPPED)
X {
X /* If input buffer level has dropped below
X the low water mark and input was stopped
X by hardware handshake, restart input.
X */
X if (!(fip->flow_flags.i & FF_HWI_HANDSHAKE)
X || (fip->recv_ring_cnt < HW_LOW_WATER))
X {
X fip->mcr |= fip->flow.m.ic;
X outb (MDM_CTL_PORT, fip->mcr);
X fip->flow_flags.s &= ~FF_HWI_STOPPED;
X }
X }
X else
X {
X /* If input buffer level has risen above the
X high water mark and input is not yet
X stopped, stop input by hardware handshake.
X */
X if ((fip->flow_flags.i & FF_HWI_HANDSHAKE)
X && (fip->recv_ring_cnt > HW_HIGH_WATER))
X {
X fip->mcr &= ~fip->flow.m.ic;
X outb (MDM_CTL_PORT, fip->mcr);
X fip->flow_flags.s |= FF_HWI_STOPPED;
X }
X }
X
Xswflow:
X if (fip->flow_flags.i & FF_SWI_STOPPED)
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->iflag & IXOFF)
X || (fip->recv_ring_cnt < SW_LOW_WATER))
X {
X fip->flow_flags.s &= ~FF_SWI_STOPPED;
X fip->flow_flags.s ^= FF_SW_FC_REQ;
X if (fip->flow_flags.i & FF_SW_FC_REQ)
X {
X fip->tty->t_state |= TTXON;
X fas_xproc (fip);
X }
X else
X fip->tty->t_state &= ~TTXOFF;
X }
X }
X else
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->recv_ring_cnt > SW_HIGH_WATER))
X {
X fip->flow_flags.s |= FF_SWI_STOPPED;
X fip->flow_flags.s ^= FF_SW_FC_REQ;
X if (fip->flow_flags.i & FF_SW_FC_REQ)
X {
X fip->tty->t_state |= TTXOFF;
X fas_xproc (fip);
X }
X else
X fip->tty->t_state &= ~TTXON;
X }
X }
X}
X
X/* Handle hangup after last close */
Xstatic void
Xfas_hangup (fip)
Xregister struct fas_info *fip;
X{
X int old_level;
X REGVAR;
X
X old_level = SPLINT ();
X
X if (fip->device_flags.i & DF_MODEM_ENABLED)
X {
X fip->mcr &= ~(fip->modem.m.en
X | fip->flow.m.ic);
X outb (MDM_CTL_PORT, fip->mcr);
X fip->device_flags.s &= ~DF_MODEM_ENABLED;
X (void) timeout (fas_hangup, fip, HANGUP_TIME);
X }
X else
X {
X fip->device_flags.s &= ~DF_DO_HANGUP;
X /* If there was a waiting getty open on this
X port, reopen the physical device.
X */
X if (fip->o_state & OS_WAIT_OPEN)
X {
X fas_open_device (fip);
X fas_param (fip); /* set up port regs */
X fas_mproc (fip); /* set up mdm stat flags */
X }
X release_device_lock (fip);
X }
X (void) splx (old_level);
X}
X
X/* main timeout function */
Xstatic void
Xfas_timeout (fip)
Xregister struct fas_info *fip;
X{
X int old_level;
X REGVAR;
X
X old_level = SPLINT ();
X
X /* handle break request */
X if (fip->device_flags.i & DF_DO_BREAK)
X {
X /* set up break request flags */
X fip->lcr |= LC_SET_BREAK_LEVEL;
X outb (LINE_CTL_PORT, fip->lcr);
X fip->device_flags.s &= ~(DF_DO_BREAK | DF_GUARD_TIMEOUT);
X (void) timeout (fas_timeout, fip, BREAK_TIME);
X (void) splx (old_level);
X return;
X }
X
X /* reset break state */
X if (fip->device_flags.i & DF_XMIT_BREAK)
X {
X if (fip->lcr & LC_SET_BREAK_LEVEL)
X {
X fip->lcr &= ~LC_SET_BREAK_LEVEL;
X outb (LINE_CTL_PORT, fip->lcr);
X fip->device_flags.s |= DF_GUARD_TIMEOUT;
X fip->timeout_idx = timeout (fas_timeout, fip,
X fas_ctimes [fip->cflag & CBAUD]);
X (void) splx (old_level);
X return;
X }
X fip->device_flags.s &= ~DF_XMIT_BREAK;
X /* restart output after BREAK */
X fas_xproc (fip);
X }
X
X /* handle character guard timeout */
X if (fip->device_flags.i & DF_GUARD_TIMEOUT)
X {
X fip->device_flags.s &= ~DF_GUARD_TIMEOUT;
X if (!fip->xmit_ring_cnt
X && !fip->tty->t_outq.c_cc
X && !fip->tty->t_tbuf.c_count)
X {
X fip->tty->t_state &= ~BUSY;
X }
X }
X
X fip->tty->t_state &= ~TIMEOUT;
X
X event_sched (fip, EF_DO_XXFER);
X
X (void) wakeup ((caddr_t) &(fip)->device_flags.i);
X (void) splx (old_level);
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 REGVAR;
X
X switch (arg2)
X {
X case T_TIME: /* timeout */
X goto start_output;
X
X case T_OUTPUT: /* output characters to the transmitter */
X if (fip->xmit_ring_size > fip->xmit_ring_cnt)
X {
Xstart_output:
X event_sched (fip, EF_DO_XXFER);
X }
X break;
X
X case T_SUSPEND: /* suspend character output */
X fip->flow_flags.s |= FF_SWO_STOPPED;
X ttyp->t_state |= TTSTOP;
X break;
X
X case T_RESUME: /* restart character output */
X fip->flow_flags.s &= ~FF_SWO_STOPPED;
X ttyp->t_state &= ~TTSTOP;
X fas_xproc (fip);
X break;
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 if (fip->device_flags.i & DF_DEVICE_IS_NS16550)
X outb (NS_FIFO_CTL_PORT, STANDARD_NS_FIFO_SETUP
X | NS_FIFO_CLR_RECV);
X else if (fip->device_flags.i & DF_DEVICE_IS_I82510)
X {
X outb (I_BANK_PORT, I_BANK_1);
X outb (I_RCM_PORT, I_FIFO_CLR_RECV);
X outb (I_BANK_PORT, I_BANK_0);
X }
X
X fip->recv_ring_take_ptr = fip->recv_ring_put_ptr;
X fip->recv_ring_cnt = 0;
X
X if (fip->flow_flags.i & FF_HWI_STOPPED)
X {
X fip->mcr |= fip->flow.m.ic;
X outb (MDM_CTL_PORT, fip->mcr);
X fip->flow_flags.s &= ~FF_HWI_STOPPED;
X }
X
X ttyp->t_state &= ~TBLOCK;
X
X if (fip->flow_flags.i & FF_SWI_STOPPED)
X {
X fip->flow_flags.s &= ~FF_SWI_STOPPED;
X fip->flow_flags.s ^= FF_SW_FC_REQ;
X if (fip->flow_flags.i & FF_SW_FC_REQ)
X {
X ttyp->t_state |= TTXON;
X fas_xproc (fip);
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_IS_NS16550)
X outb (NS_FIFO_CTL_PORT, STANDARD_NS_FIFO_SETUP
X | NS_FIFO_CLR_XMIT);
X else if (fip->device_flags.i & DF_DEVICE_IS_I82510)
X {
X outb (I_BANK_PORT, I_BANK_1);
X outb (I_TCM_PORT, I_FIFO_CLR_XMIT);
X outb (I_BANK_PORT, I_BANK_0);
X }
X
X fip->xmit_ring_take_ptr = fip->xmit_ring_put_ptr;
X fip->xmit_ring_cnt = 0;
X
X fip->flow_flags.s &= ~FF_SWO_STOPPED;
X ttyp->t_state &= ~TTSTOP;
X
X if (ttyp->t_tbuf.c_ptr)
X ttyp->t_tbuf.c_ptr += ttyp->t_tbuf.c_count;
X ttyp->t_tbuf.c_count = 0;
X
X if (!(fip->device_flags.i & (DF_XMIT_BUSY | DF_GUARD_TIMEOUT))
X && (ttyp->t_state & BUSY))
X {
X ttyp->t_state &= ~BUSY;
X goto start_output;
X }
X break;
X
X case T_BREAK: /* do a break on the transmitter line */
X fip->device_flags.s |= DF_XMIT_BREAK;
X ttyp->t_state |= TIMEOUT;
X if (fip->device_flags.i & (DF_XMIT_BUSY | DF_GUARD_TIMEOUT))
X {
X fip->device_flags.s |= DF_DO_BREAK;
X }
X else
X {
X /* set up break request flags */
X fip->lcr |= LC_SET_BREAK_LEVEL;
X outb (LINE_CTL_PORT, fip->lcr);
X (void) timeout (fas_timeout, fip, BREAK_TIME);
X }
X break;
X
X case T_PARM: /* set up the port according to the termio structure */
X fas_param (fip);
X break;
X
X case T_SWTCH: /* handle layer switch request */
X break;
X }
X}
X
X/* open device physically */
Xstatic void
Xfas_open_device (fip)
Xregister struct fas_info *fip;
X{
X REGVAR;
X
X fip->device_flags.s &= DF_DEVICE_CONFIGURED | DF_DEVICE_IS_NS16550
X | DF_DEVICE_IS_I82510 | DF_DEVICE_LOCKED;
X fip->flow_flags.s = 0;
X fip->cflag = 0;
X fip->iflag = 0;
X fip->recv_ring_take_ptr = fip->recv_ring_put_ptr;
X fip->recv_ring_cnt = 0;
X fip->xmit_ring_take_ptr = fip->xmit_ring_put_ptr;
X fip->xmit_ring_cnt = 0;
X
X /* hook into the interrupt users chain */
X fip->next_int_user = fas_first_int_user [fip->vec];
X if (fip->next_int_user)
X fip->next_int_user->prev_int_user = fip;
X fas_first_int_user [fip->vec] = fip;
X fip->prev_int_user = (struct fas_info *) NULL;
X
X fip->lcr = 0;
X outb (LINE_CTL_PORT, fip->lcr);
X
X if (fip->device_flags.i & DF_DEVICE_IS_NS16550)
X outb (NS_FIFO_CTL_PORT, STANDARD_NS_FIFO_CLEAR);
X else if (fip->device_flags.i & DF_DEVICE_IS_I82510)
X {
X outb (I_BANK_PORT, I_BANK_1);
X outb (I_TCM_PORT, I_FIFO_CLR_XMIT);
X outb (I_RCM_PORT, I_FIFO_CLR_RECV);
X outb (I_BANK_PORT, I_BANK_2);
X outb (I_IDM_PORT, STANDARD_I_FIFO_CLEAR);
X outb (I_BANK_PORT, I_BANK_0);
X }
X
X /* clear interrupts */
X inb (MDM_STATUS_PORT);
X inb (RCV_DATA_PORT);
X inb (RCV_DATA_PORT);
X inb (LINE_STATUS_PORT);
X inb (INT_ID_PORT);
X if (INT_ACK_PORT)
X outb (INT_ACK_PORT, fip->int_ack);
X
X if (fip->device_flags.i & DF_DEVICE_IS_NS16550)
X outb (NS_FIFO_CTL_PORT, STANDARD_NS_FIFO_SETUP);
X else if (fip->device_flags.i & DF_DEVICE_IS_I82510)
X {
X outb (I_BANK_PORT, I_BANK_2);
X outb (I_IDM_PORT, STANDARD_I_FIFO_SETUP);
X outb (I_BANK_PORT, I_BANK_0);
X }
X
X fip->msr = fip->new_msr = inb (MDM_STATUS_PORT)
X & (MS_CTS_PRESENT
X | MS_DSR_PRESENT
X | MS_DCD_PRESENT);
X
X fip->ier = IE_INIT_MODE;
X outb (INT_ENABLE_PORT, fip->ier);
X
X fip->mcr |= fip->modem.m.en | fip->flow.m.ic;
X outb (MDM_CTL_PORT, fip->mcr);
X
X fip->device_flags.s |= DF_DEVICE_OPEN | DF_MODEM_ENABLED;
X}
X
X/* close device physically */
Xstatic void
Xfas_close_device (fip)
Xregister struct fas_info *fip;
X{
X REGVAR;
X
X fip->device_flags.s &= ~DF_DEVICE_OPEN;
X fip->ier = IE_NONE; /* disable all ints from UART */
X outb (INT_ENABLE_PORT, fip->ier);
X if (INT_ACK_PORT)
X outb (INT_ACK_PORT, fip->int_ack);
X
X if (fip->device_flags.i & DF_DEVICE_IS_NS16550)
X outb (NS_FIFO_CTL_PORT, STANDARD_NS_FIFO_CLEAR);
X else if (fip->device_flags.i & DF_DEVICE_IS_I82510)
X {
X outb (I_BANK_PORT, I_BANK_1);
X outb (I_TCM_PORT, I_FIFO_CLR_XMIT);
X outb (I_RCM_PORT, I_FIFO_CLR_RECV);
X outb (I_BANK_PORT, I_BANK_2);
X outb (I_IDM_PORT, STANDARD_I_FIFO_CLEAR);
X outb (I_BANK_PORT, I_BANK_0);
X }
X
X /* reset break level */
X fip->lcr &= ~LC_SET_BREAK_LEVEL;
X outb (LINE_CTL_PORT, fip->lcr);
X
X /* unhook from interrupt users chain */
X if (fip->prev_int_user)
X fip->prev_int_user->next_int_user = fip->next_int_user;
X else
X fas_first_int_user [fip->vec] = fip->next_int_user;
X if (fip->next_int_user)
X fip->next_int_user->prev_int_user = fip->prev_int_user;
X
X if (fip->cflag & HUPCL)
X {
X /* request hangup */
X fip->device_flags.s |= DF_DO_HANGUP;
X (void) timeout (fas_hangup, fip, HANGUP_DELAY);
X }
X}
X
X/* test device thoroughly */
Xstatic int
Xfas_test_device (fip)
Xregister struct fas_info *fip;
X{
X register unchar *cptr;
X int done, delay_count, i;
X static uint lcrval [3] =
X {
X LC_WORDLEN_8,
X LC_WORDLEN_8 | LC_ENABLE_PARITY,
X LC_WORDLEN_8 | LC_ENABLE_PARITY | LC_EVEN_PARITY
X };
X REGVAR;
X
X /* make sure FIFO is off */
X outb (NS_FIFO_CTL_PORT, STANDARD_NS_FIFO_CLEAR);
X outb (I_BANK_PORT, I_BANK_2);
X outb (I_IDM_PORT, STANDARD_I_FIFO_CLEAR);
X outb (I_BANK_PORT, I_BANK_0);
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
X done = 0;
X
X /* wait until the transmitter register is empty */
X for (delay_count = 20000;
X delay_count && (~inb (LINE_STATUS_PORT)
X & (LS_XMIT_AVAIL | LS_XMIT_COMPLETE));
X delay_count--)
X ;
X
X if (!delay_count)
X done = 1;
X
X if (!done)
X {
X /* clear flags */
X inb (RCV_DATA_PORT);
X inb (RCV_DATA_PORT);
X inb (LINE_STATUS_PORT);
X
X /* make sure there are no more characters in the
X receiver register
X */
X for (delay_count = 20000;
X delay_count && !(inb (LINE_STATUS_PORT) & LS_RCV_AVAIL);
X delay_count--)
X ;
X
X if (delay_count)
X inb (RCV_DATA_PORT);
X
X /* test pattern */
X cptr = (unchar *) "\377\125\252\045\244\0";
X
X do
X {
X for (i = 0; i < 3; i++)
X {
X /* test transmitter and receiver
SHAR_EOF
true || echo 'restore of fas.c failed'
fi
echo 'End of part 2'
echo 'File fas.c is continued in part 3'
echo 3 > _shar_seq_.tmp
exit 0
--
Uwe Doering | Domain : gemini at geminix.in-berlin.de
Berlin |----------------------------------------------------------------
Germany | Bangpath : ...!unido!fub!tmpmbx!geminix!gemini
More information about the Alt.sources
mailing list