gemini@geminix.in-berlin.de (Uwe Doering) (09/19/90)
#!/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@geminix.in-berlin.de Berlin |---------------------------------------------------------------- Germany | Bangpath : ...!unido!fub!tmpmbx!geminix!gemini