diff --git a/sys/dev/cy/cy.c b/sys/dev/cy/cy.c index d10b91158d05..3f765678f0c1 100644 --- a/sys/dev/cy/cy.c +++ b/sys/dev/cy/cy.c @@ -1,2615 +1,2616 @@ /*- * cyclades cyclom-y serial driver * Andrew Herbert , 17 August 1993 * * Copyright (c) 1993 Andrew Herbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name Andrew Herbert may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.69 1998/08/19 04:17:38 bde Exp $ + * $Id: cy.c,v 1.70 1998/08/20 05:21:50 bde Exp $ */ #include "opt_compat.h" #include "opt_devfs.h" #include "cy.h" /* * TODO: * Implement BREAK. * Fix overflows when closing line. * Atomic COR change. * Consoles. */ /* * Temporary compile-time configuration options. */ #define RxFifoThreshold (CD1400_RX_FIFO_SIZE / 2) /* Number of chars in the receiver FIFO before an * an interrupt is generated. Should depend on * line speed. Needs to be about 6 on a 486DX33 * for 4 active ports at 115200 bps. Why doesn't * 10 work? */ #define PollMode /* Use polling-based irq service routine, not the * hardware svcack lines. Must be defined for * Cyclom-16Y boards. Less efficient for Cyclom-8Ys, * and stops 4 * 115200 bps from working. */ #undef Smarts /* Enable slightly more CD1400 intelligence. Mainly * the output CR/LF processing, plus we can avoid a * few checks usually done in ttyinput(). * * XXX not fully implemented, and not particularly * worthwhile. */ #undef CyDebug /* Include debugging code (not very expensive). */ /* These will go away. */ #undef SOFT_CTS_OFLOW #define SOFT_HOTCHAR #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #ifdef SMP #define disable_intr() COM_DISABLE_INTR() #define enable_intr() COM_ENABLE_INTR() #endif /* SMP */ /* * Dictionary so that I can name everything *sio* or *com* to compare with * sio.c. There is also lots of ugly formatting and unnecessary ifdefs to * simplify the comparision. These will go away. */ #define LSR_BI CD1400_RDSR_BREAK #define LSR_FE CD1400_RDSR_FE #define LSR_OE CD1400_RDSR_OE #define LSR_PE CD1400_RDSR_PE #define MCR_DTR CD1400_MSVR2_DTR #define MCR_RTS CD1400_MSVR1_RTS #define MSR_CTS CD1400_MSVR2_CTS #define MSR_DCD CD1400_MSVR2_CD #define MSR_DSR CD1400_MSVR2_DSR #define MSR_RI CD1400_MSVR2_RI #define NSIO (NCY * CY_MAX_PORTS) #define comconsole cyconsole #define comdefaultrate cydefaultrate #define com_events cy_events #define comhardclose cyhardclose #define commctl cymctl #define comparam cyparam #define comspeed cyspeed #define comstart cystart #define comwakeup cywakeup #define nsio_tty ncy_tty #define p_com_addr p_cy_addr #define sioattach cyattach #define sioclose cyclose #define siodevtotty cydevtotty #define siodriver cydriver #define siodtrwakeup cydtrwakeup #define siointr cyintr #define siointr1 cyintr1 #define sioioctl cyioctl #define sioopen cyopen #define siopoll cypoll #define sioprobe cyprobe #define sioread cyread #define siosettimeout cysettimeout #define siostop cystop #define siowrite cywrite #define sio_registered cy_registered #define sio_timeout cy_timeout #define sio_timeout_handle cy_timeout_handle #define sio_timeouts_until_log cy_timeouts_until_log #define sio_tty cy_tty #define CY_MAX_PORTS (CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s) /* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */ #define CD1400_xIVR_CHAN_SHIFT 3 #define CD1400_xIVR_CHAN 0x1F #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RS_IBUFSIZE 256 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) (((mynor) >> 16) * CY_MAX_PORTS \ | (((mynor) & 0xff) & ~MINOR_MAGIC_MASK)) /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * siostop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_char bool_t; /* boolean */ typedef u_char volatile *cy_addr; /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ #if 0 u_char cfcr_image; /* copy of value written to CFCR */ u_char fifo_image; /* copy of value written to FIFO */ #endif u_char gfrcr_image; /* copy of value read from GFRCR */ #if 0 bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t loses_outints; /* nonzero if device loses output interrupts */ #endif u_char mcr_dtr; /* MCR bit that is wired to DTR */ u_char mcr_image; /* copy of value written to MCR */ u_char mcr_rts; /* MCR bit that is wired to RTS */ #if 0 #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ #endif int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ #if 0 u_int tx_fifo_size; #endif u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ int cy_align; /* index for register alignment */ cy_addr cy_iobase; /* base address of this port's cyclom */ cy_addr iobase; /* base address of this port's cd1400 */ int mcr_rts_reg; /* cd1400 reg number of reg holding mcr_rts */ struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; u_int recv_exception; /* exception chars received */ u_int mdm; /* modem signal changes */ #ifdef CyDebug u_int start_count; /* no. of calls to comstart() */ u_int start_real; /* no. of calls that did something */ #endif u_char channel_control;/* CD1400 CCR control command shadow */ u_char cor[3]; /* CD1400 COR1-3 shadows */ u_char intr_enable; /* CD1400 SRER shadow */ /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; #ifdef DEVFS void *devfs_token_ttyd; void *devfs_token_ttyl; void *devfs_token_ttyi; void *devfs_token_cuaa; void *devfs_token_cual; void *devfs_token_cuai; #endif }; /* PCI driver entry point. */ int cyattach_common __P((cy_addr cy_iobase, int cy_align)); static int cy_units __P((cy_addr cy_iobase, int cy_align)); static int sioattach __P((struct isa_device *dev)); static void cd1400_channel_cmd __P((cy_addr iobase, int cmd, int cy_align)); static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); #if 0 static void siointr1 __P((struct com_s *com)); #endif static int commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static swihand_t siopoll; static int sioprobe __P((struct isa_device *dev)); static void siosettimeout __P((void)); static int comspeed __P((speed_t speed, u_long cy_clock, int *prescaler_io)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com)); #ifdef CyDebug void cystatus __P((int unit)); #endif static char driver_name[] = "cy"; /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) struct isa_driver siodriver = { sioprobe, sioattach, driver_name }; static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static d_stop_t siostop; static d_devtotty_t siodevtotty; -#define CDEV_MAJOR 48 -static struct cdevsw sio_cdevsw = { +#define CDEV_MAJOR 48 +static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, ttpoll, nommap, NULL, driver_name, - NULL, -1, + NULL, -1, nodump, nopsize, + D_TTY, }; static int comconsole = -1; static speed_t comdefaultrate = TTYDEF_SPEED; static u_int com_events; /* input chars + weighted output completions */ static bool_t sio_registered; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); #if 0 /* XXX */ static struct tty *sio_tty[NSIO]; #else static struct tty sio_tty[NSIO]; #endif static const int nsio_tty = NSIO; #ifdef CyDebug static u_int cd_inbs; static u_int cy_inbs; static u_int cd_outbs; static u_int cy_outbs; static u_int cy_svrr_probes; static u_int cy_timeouts; #endif static int cy_chip_offset[] = { 0x0000, 0x0400, 0x0800, 0x0c00, 0x0200, 0x0600, 0x0a00, 0x0e00, }; static int cy_nr_cd1400s[NCY]; static int cy_total_devices; #undef RxFifoThreshold static int volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2); static int sioprobe(dev) struct isa_device *dev; { cy_addr iobase; iobase = (cy_addr)dev->id_maddr; /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ cd_inb(iobase, CY16_RESET, 0); /* XXX? */ DELAY(500); /* wait for the board to get its act together */ /* this is needed to get the board out of reset */ cd_outb(iobase, CY_CLEAR_INTR, 0, 0); DELAY(500); return (cy_units(iobase, 0) == 0 ? 0 : -1); } static int cy_units(cy_iobase, cy_align) cy_addr cy_iobase; int cy_align; { int cyu; u_char firmware_version; int i; cy_addr iobase; for (cyu = 0; cyu < CY_MAX_CD1400s; ++cyu) { iobase = cy_iobase + (cy_chip_offset[cyu] << cy_align); /* wait for chip to become ready for new command */ for (i = 0; i < 10; i++) { DELAY(50); if (!cd_inb(iobase, CD1400_CCR, cy_align)) break; } /* clear the GFRCR register */ cd_outb(iobase, CD1400_GFRCR, cy_align, 0); /* issue a reset command */ cd_outb(iobase, CD1400_CCR, cy_align, CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET); /* wait for the CD1400 to initialize itself */ for (i = 0; i < 200; i++) { DELAY(50); /* retrieve firmware version */ firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align); if ((firmware_version & 0xf0) == 0x40) break; } /* * Anything in the 0x40-0x4F range is fine. * If one CD1400 is bad then we don't support higher * numbered good ones on this board. */ if ((firmware_version & 0xf0) != 0x40) break; } return (cyu); } static int sioattach(isdp) struct isa_device *isdp; { int adapter; adapter = cyattach_common((cy_addr) isdp->id_maddr, 0); if (adapter < 0) return (0); /* * XXX * This kludge is to allow ISA/PCI device specifications in the * kernel config file to be in any order. */ if (isdp->id_unit != adapter) { printf("cy%d: attached as cy%d\n", isdp->id_unit, adapter); isdp->id_unit = adapter; /* XXX */ } isdp->id_ri_flags |= RI_FAST; return (1); } int cyattach_common(cy_iobase, cy_align) cy_addr cy_iobase; int cy_align; { int adapter; int cyu; dev_t dev; u_char firmware_version; cy_addr iobase; int ncyu; int unit; adapter = cy_total_devices; if ((u_int)adapter >= NCY) { printf( "cy%d: can't attach adapter: insufficient cy devices configured\n", adapter); return (-1); } ncyu = cy_units(cy_iobase, cy_align); if (ncyu == 0) return (-1); cy_nr_cd1400s[adapter] = ncyu; cy_total_devices++; unit = adapter * CY_MAX_PORTS; for (cyu = 0; cyu < ncyu; ++cyu) { int cdu; iobase = (cy_addr) (cy_iobase + (cy_chip_offset[cyu] << cy_align)); firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align); /* Set up a receive timeout period of than 1+ ms. */ cd_outb(iobase, CD1400_PPR, cy_align, howmany(CY_CLOCK(firmware_version) / CD1400_PPR_PRESCALER, 1000)); for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { struct com_s *com; int s; com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT); if (com == NULL) break; bzero(com, sizeof *com); com->unit = unit; com->gfrcr_image = firmware_version; if (CY_RTS_DTR_SWAPPED(firmware_version)) { com->mcr_dtr = MCR_RTS; com->mcr_rts = MCR_DTR; com->mcr_rts_reg = CD1400_MSVR2; } else { com->mcr_dtr = MCR_DTR; com->mcr_rts = MCR_RTS; com->mcr_rts_reg = CD1400_MSVR1; } com->dtr_wait = 3 * hz; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->cy_align = cy_align; com->cy_iobase = cy_iobase; com->iobase = iobase; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; } termioschars(&com->it_in); com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; com->it_out = com->it_in; s = spltty(); com_addr(unit) = com; splx(s); if (!sio_registered) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &sio_cdevsw, NULL); register_swi(SWI_TTY, siopoll); sio_registered = TRUE; } #ifdef DEVFS com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, unit, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyc%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyic%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_LOCK_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttylc%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuac%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaic%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cualc%r%r", adapter, unit % CY_MAX_PORTS); #endif } } /* ensure an edge for the next interrupt */ cd_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); return (adapter); } static int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; cy_addr iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0); if (error != 0) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "cybi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; tp->t_ififosize = 2 * RS_IBUFSIZE; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; #if 0 (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; #endif ++com->wopeners; iobase = com->iobase; /* reset this channel */ cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); cd1400_channel_cmd(iobase, CD1400_CCR_CMDRESET, com->cy_align); /* * Resetting disables the transmitter and receiver as well as * flushing the fifos so some of our cached state becomes * invalid. The documentation suggests that all registers * for the current channel are reset to defaults, but * apparently none are. We wouldn't want DTR cleared. */ com->channel_control = 0; /* Encode per-board unit in LIVR for access in intr routines. */ cd_outb(iobase, CD1400_LIVR, com->cy_align, (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); /* * raise dtr and generally set things up correctly. this * has the side-effect of selecting the appropriate cd1400 * channel, to help us with subsequent channel control stuff */ error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ #if 0 if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); DELAY(100); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(100); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); enable_intr(); #else /* !0 */ /* XXX raise RTS too */ (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); disable_intr(); com->prev_modem_status = com->last_modem_status = cd_inb(iobase, CD1400_MSVR2, com->cy_align); cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); enable_intr(); #endif /* 0 */ /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "cybi" * instead of "cydcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0); --com->wopeners; if (error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); #ifdef broken /* session holds a ref to the tty; can't deallocate */ ttyfree(tp); com->tp = sio_tty[unit] = NULL; #endif return (0); } static void comhardclose(com) struct com_s *com; { cy_addr iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty(); #if 0 com->poll = FALSE; com->poll_output = FALSE; #endif com->do_timestamp = 0; cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); #if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); #endif { #if 0 outb(iobase + com_ier, 0); #else disable_intr(); cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable = 0); enable_intr(); #endif tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { (void)commctl(com, TIOCM_DTR, DMBIC); /* Disable receiver (leave transmitter enabled). */ com->channel_control = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN | CD1400_CCR_RCVDIS; cd1400_channel_cmd(iobase, com->channel_control, com->cy_align); if (com->dtr_wait != 0) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } #if 0 if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ outb(iobase + com_fifo, 0); } #endif com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); tp = com_addr(MINOR_TO_UNIT(mynor))->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; #ifdef Smarts /* XXX duplicate ttwrite(), but without so much output processing on * CR & LF chars. Hardly worth the effort, given that high-throughput * sessions are raw anyhow. */ #else return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); #endif } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } void siointr(unit) int unit; { int baseu; int cy_align; cy_addr cy_iobase; int cyu; cy_addr iobase; u_char status; COM_LOCK(); /* XXX could this be placed down lower in the loop? */ baseu = unit * CY_MAX_PORTS; cy_align = com_addr(baseu)->cy_align; cy_iobase = com_addr(baseu)->cy_iobase; /* check each CD1400 in turn */ for (cyu = 0; cyu < cy_nr_cd1400s[unit]; ++cyu) { iobase = (cy_addr) (cy_iobase + (cy_chip_offset[cyu] << cy_align)); /* poll to see if it has any work */ status = cd_inb(iobase, CD1400_SVRR, cy_align); if (status == 0) continue; #ifdef CyDebug ++cy_svrr_probes; #endif /* service requests as appropriate, giving priority to RX */ if (status & CD1400_SVRR_RXRDY) { struct com_s *com; u_int count; u_char *ioptr; u_char line_status; u_char recv_data; u_char serv_type; #ifdef PollMode u_char save_car; u_char save_rir; #endif #ifdef PollMode save_rir = cd_inb(iobase, CD1400_RIR, cy_align); save_car = cd_inb(iobase, CD1400_CAR, cy_align); /* enter rx service */ cd_outb(iobase, CD1400_CAR, cy_align, save_rir); serv_type = cd_inb(iobase, CD1400_RIVR, cy_align); com = com_addr(baseu + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #else /* ack receive service */ serv_type = cy_inb(iobase, CY8_SVCACKR); com = com_addr(baseu + + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif if (serv_type & CD1400_RIVR_EXCEPTION) { ++com->recv_exception; line_status = cd_inb(iobase, CD1400_RDSR, cy_align); /* break/unnattached error bits or real input? */ recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifndef SOFT_HOTCHAR if (line_status & CD1400_RDSR_SPECIAL && com->hotchar != 0) setsofttty(); #endif #if 1 /* XXX "intelligent" PFO error handling would break O error handling */ if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { /* Don't store PE if IGNPAR and BI if IGNBRK, this hack allows "raw" tty optimization works even if IGN* is set. */ if ( com->tp == NULL || !(com->tp->t_state & TS_ISOPEN) || (line_status & (LSR_PE|LSR_FE)) && (com->tp->t_iflag & IGNPAR) || (line_status & LSR_BI) && (com->tp->t_iflag & IGNBRK)) goto cont; if ( (line_status & (LSR_PE|LSR_FE)) && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) && ((line_status & LSR_FE) || (line_status & LSR_PE) && (com->tp->t_iflag & INPCK))) recv_data = 0; } #endif /* 1 */ ++com->bytes_in; #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, cy_align, com->mcr_image &= ~com->mcr_rts); #endif if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } goto cont; } else { int ifree; count = cd_inb(iobase, CD1400_RDCR, cy_align); if (!count) goto cont; com->bytes_in += count; ioptr = com->iptr; ifree = com->ibufend - ioptr; if (count > ifree) { count -= ifree; com_events += ifree; if (ifree != 0) { if (com->do_timestamp) microtime(&com->timestamp); do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = 0; ++ioptr; } while (--ifree != 0); } com->delta_error_counts [CE_INTERRUPT_BUF_OVERFLOW] += count; do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif } while (--count != 0); } else { if (com->do_timestamp) microtime(&com->timestamp); if (ioptr <= com->ihighwater && ioptr + count > com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, cy_align, com->mcr_image &= ~com->mcr_rts); #endif com_events += count; do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = 0; ++ioptr; } while (--count != 0); } com->iptr = ioptr; } cont: /* terminate service context */ #ifdef PollMode cd_outb(iobase, CD1400_RIR, cy_align, save_rir & ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY)); cd_outb(iobase, CD1400_CAR, cy_align, save_car); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } if (status & CD1400_SVRR_MDMCH) { struct com_s *com; u_char modem_status; #ifdef PollMode u_char save_car; u_char save_mir; #else u_char vector; #endif #ifdef PollMode save_mir = cd_inb(iobase, CD1400_MIR, cy_align); save_car = cd_inb(iobase, CD1400_CAR, cy_align); /* enter modem service */ cd_outb(iobase, CD1400_CAR, cy_align, save_mir); com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS + (save_mir & CD1400_MIR_CHAN)); #else /* ack modem service */ vector = cy_inb(iobase, CY8_SVCACKM); com = com_addr(baseu + ((vector >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif ++com->mdm; modem_status = cd_inb(iobase, CD1400_MSVR2, cy_align); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } #ifdef SOFT_CTS_OFLOW /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) { com->state |= CS_ODEVREADY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) && !(com->intr_enable & CD1400_SRER_TXRDY)) cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } else { com->state &= ~CS_ODEVREADY; if (com->intr_enable & CD1400_SRER_TXRDY) cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } } #endif } /* terminate service context */ #ifdef PollMode cd_outb(iobase, CD1400_MIR, cy_align, save_mir & ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY)); cd_outb(iobase, CD1400_CAR, cy_align, save_car); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } if (status & CD1400_SVRR_TXRDY) { struct com_s *com; #ifdef PollMode u_char save_car; u_char save_tir; #else u_char vector; #endif #ifdef PollMode save_tir = cd_inb(iobase, CD1400_TIR, cy_align); save_car = cd_inb(iobase, CD1400_CAR, cy_align); /* enter tx service */ cd_outb(iobase, CD1400_CAR, cy_align, save_tir); com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS + (save_tir & CD1400_TIR_CHAN)); #else /* ack transmit service */ vector = cy_inb(iobase, CY8_SVCACKT); com = com_addr(baseu + ((vector >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { u_char *ioptr; u_int ocount; ioptr = com->obufq.l_head; ocount = com->obufq.l_tail - ioptr; if (ocount > CD1400_TX_FIFO_SIZE) ocount = CD1400_TX_FIFO_SIZE; com->bytes_out += ocount; do cd_outb(iobase, CD1400_TDR, cy_align, *ioptr++); while (--ocount != 0); com->obufq.l_head = ioptr; if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ com->state &= ~CS_BUSY; cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; /* handle at high level ASAP */ setsofttty(); } } } /* terminate service context */ #ifdef PollMode cd_outb(iobase, CD1400_TIR, cy_align, save_tir & ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY)); cd_outb(iobase, CD1400_CAR, cy_align, save_car); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } } /* ensure an edge for the next interrupt */ cd_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); schedsofttty(); COM_UNLOCK(); } #if 0 static void siointr1(com) struct com_s *com; { } #endif static int sioioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; cy_addr iobase; int mynor; int s; struct tty *tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) int oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } tp = com->tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return (error); s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, com); if (error != ENOIOCTL) { splx(s); return (error); } cd_outb(iobase, CD1400_CAR, com->cy_align, MINOR_TO_UNIT(mynor) & CD1400_CAR_CHAN); switch (cmd) { #if 0 case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; #endif /* 0 */ case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } static void siopoll() { int unit; #ifdef CyDebug ++cy_timeouts; #endif if (com_events == 0) return; repeat: for (unit = 0; unit < NSIO; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; cy_addr iobase; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL) { /* * XXX forget any events related to closed devices * (actually never opened devices) so that we don't * loop. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); if (incc != 0) log(LOG_DEBUG, "sio%d: %d events for device with no tp\n", unit, incc); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) && !(tp->t_state & TS_TBLOCK)) #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else iobase = com->iobase, cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN), cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image |= com->mcr_rts); #endif enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; if (!(com->state & CS_BUSY)) com->tp->t_state &= ~TS_BUSY; enable_intr(); (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { int bits; int cflag; struct com_s *com; u_char cor_change; u_long cy_clock; int idivisor; int iflag; cy_addr iobase; int iprescaler; int itimeout; int odivisor; int oprescaler; u_char opt; int s; int unit; /* do historical conversions */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); /* check requested parameters */ cy_clock = CY_CLOCK(com->gfrcr_image); idivisor = comspeed(t->c_ispeed, cy_clock, &iprescaler); if (idivisor < 0) return (EINVAL); odivisor = comspeed(t->c_ospeed, cy_clock, &oprescaler); if (odivisor < 0) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ iobase = com->iobase; s = spltty(); cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); if (odivisor == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); if (idivisor != 0) { cd_outb(iobase, CD1400_RBPR, com->cy_align, idivisor); cd_outb(iobase, CD1400_RCOR, com->cy_align, iprescaler); } if (odivisor != 0) { cd_outb(iobase, CD1400_TBPR, com->cy_align, odivisor); cd_outb(iobase, CD1400_TCOR, com->cy_align, oprescaler); } /* * channel control * receiver enable * transmitter enable (always set) */ cflag = t->c_cflag; opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS); if (opt != com->channel_control) { com->channel_control = opt; cd1400_channel_cmd(iobase, opt, com->cy_align); } #ifdef Smarts /* set special chars */ /* XXX if one is _POSIX_VDISABLE, can't use some others */ if (t->c_cc[VSTOP] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR1, com->cy_align, t->c_cc[VSTOP]); if (t->c_cc[VSTART] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR2, com->cy_align, t->c_cc[VSTART]); if (t->c_cc[VINTR] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR3, com->cy_align, t->c_cc[VINTR]); if (t->c_cc[VSUSP] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR4, com->cy_align, t->c_cc[VSUSP]); #endif /* * set channel option register 1 - * parity mode * stop bits * char length */ opt = 0; /* parity */ if (cflag & PARENB) { if (cflag & PARODD) opt |= CD1400_COR1_PARODD; opt |= CD1400_COR1_PARNORMAL; } iflag = t->c_iflag; if (!(iflag & INPCK)) opt |= CD1400_COR1_NOINPCK; bits = 1 + 1; /* stop bits */ if (cflag & CSTOPB) { ++bits; opt |= CD1400_COR1_STOP2; } /* char length */ switch (cflag & CSIZE) { case CS5: bits += 5; opt |= CD1400_COR1_CS5; break; case CS6: bits += 6; opt |= CD1400_COR1_CS6; break; case CS7: bits += 7; opt |= CD1400_COR1_CS7; break; default: bits += 8; opt |= CD1400_COR1_CS8; break; } cor_change = 0; if (opt != com->cor[0]) { cor_change |= CD1400_CCR_COR1; cd_outb(iobase, CD1400_COR1, com->cy_align, com->cor[0] = opt); } /* * Set receive time-out period, normally to max(one char time, 5 ms). */ if (t->c_ispeed == 0) itimeout = cd_inb(iobase, CD1400_RTPR, com->cy_align); else { itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed; #ifdef SOFT_HOTCHAR #define MIN_RTP 1 #else #define MIN_RTP 5 #endif if (itimeout < MIN_RTP) itimeout = MIN_RTP; } if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0 && t->c_cc[VTIME] * 10 > itimeout) itimeout = t->c_cc[VTIME] * 10; if (itimeout > 255) itimeout = 255; cd_outb(iobase, CD1400_RTPR, com->cy_align, itimeout); /* * set channel option register 2 - * flow control */ opt = 0; #ifdef Smarts if (iflag & IXANY) opt |= CD1400_COR2_IXANY; if (iflag & IXOFF) opt |= CD1400_COR2_IXOFF; #endif #ifndef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_COR2_CCTS_OFLOW; #endif if (opt != com->cor[1]) { cor_change |= CD1400_CCR_COR2; cd_outb(iobase, CD1400_COR2, com->cy_align, com->cor[1] = opt); } /* * set channel option register 3 - * receiver FIFO interrupt threshold * flow control */ opt = RxFifoThreshold; #ifdef Smarts if (t->c_lflag & ICANON) opt |= CD1400_COR3_SCD34; /* detect INTR & SUSP chars */ if (iflag & IXOFF) /* detect and transparently handle START and STOP chars */ opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12; #endif if (opt != com->cor[2]) { cor_change |= CD1400_CCR_COR3; cd_outb(iobase, CD1400_COR3, com->cy_align, com->cor[2] = opt); } /* notify the CD1400 if COR1-3 have changed */ if (cor_change) cd1400_channel_cmd(iobase, CD1400_CCR_CMDCORCHG | cor_change, com->cy_align); /* * set channel option register 4 - * CR/NL processing * break processing * received exception processing */ opt = 0; if (iflag & IGNCR) opt |= CD1400_COR4_IGNCR; #ifdef Smarts /* * we need a new ttyinput() for this, as we don't want to * have ICRNL && INLCR being done in both layers, or to have * synchronisation problems */ if (iflag & ICRNL) opt |= CD1400_COR4_ICRNL; if (iflag & INLCR) opt |= CD1400_COR4_INLCR; #endif if (iflag & IGNBRK) opt |= CD1400_COR4_IGNBRK; if (!(iflag & BRKINT)) opt |= CD1400_COR4_NOBRKINT; #if 0 /* XXX using this "intelligence" breaks reporting of overruns. */ if (iflag & IGNPAR) opt |= CD1400_COR4_PFO_DISCARD; else { if (iflag & PARMRK) opt |= CD1400_COR4_PFO_ESC; else opt |= CD1400_COR4_PFO_NUL; } #else opt |= CD1400_COR4_PFO_EXCEPTION; #endif cd_outb(iobase, CD1400_COR4, com->cy_align, opt); /* * set channel option register 5 - */ opt = 0; if (iflag & ISTRIP) opt |= CD1400_COR5_ISTRIP; if (t->c_iflag & IEXTEN) /* enable LNEXT (e.g. ctrl-v quoting) handling */ opt |= CD1400_COR5_LNEXT; #ifdef Smarts if (t->c_oflag & ONLCR) opt |= CD1400_COR5_ONLCR; if (t->c_oflag & OCRNL) opt |= CD1400_COR5_OCRNL; #endif cd_outb(iobase, CD1400_COR5, com->cy_align, opt); /* * We always generate modem status change interrupts for CD changes. * Among other things, this is necessary to track TS_CARR_ON for * pstat to print even when the driver doesn't care. CD changes * should be rare so interrupts for them are not worth extra code to * avoid. We avoid interrupts for other modem status changes (except * for CTS changes when SOFT_CTS_OFLOW is configured) since this is * simplest and best. */ /* * set modem change option register 1 * generate modem interrupts on which 1 -> 0 input transitions * also controls auto-DTR output flow-control, which we don't use */ opt = CD1400_MCOR1_CDzd; #ifdef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_MCOR1_CTSzd; #endif cd_outb(iobase, CD1400_MCOR1, com->cy_align, opt); /* * set modem change option register 2 * generate modem interrupts on specific 0 -> 1 input transitions */ opt = CD1400_MCOR2_CDod; #ifdef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_MCOR2_CTSod; #endif cd_outb(iobase, CD1400_MCOR2, com->cy_align, opt); /* * XXX should have done this long ago, but there is too much state * to change all atomically. */ disable_intr(); com->state &= ~CS_TTGO; if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) { com->state |= CS_RTS_IFLOW; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image |= com->mcr_rts); #endif } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; #ifdef SOFT_CTS_OFLOW com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; } #endif /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); #if 0 /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); #endif if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { if (!(com->intr_enable & CD1400_SRER_TXRDY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } else { if (com->intr_enable & CD1400_SRER_TXRDY) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } enable_intr(); splx(s); comstart(tp); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; cy_addr iobase; int s; #ifdef CyDebug bool_t started; #endif int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); #ifdef CyDebug ++com->start_count; started = FALSE; #endif disable_intr(); cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); if (tp->t_state & TS_TTSTOP) { com->state &= ~CS_TTGO; if (com->intr_enable & CD1400_SRER_TXRDY) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } else { com->state |= CS_TTGO; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) && !(com->intr_enable & CD1400_SRER_TXRDY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & com->mcr_rts && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image &= ~com->mcr_rts); #endif } else { if (!(com->mcr_image & com->mcr_rts) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image |= com->mcr_rts); #endif } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { #ifdef CyDebug started = TRUE; #endif com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } enable_intr(); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { #ifdef CyDebug started = TRUE; #endif com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } enable_intr(); } tp->t_state |= TS_BUSY; } #ifdef CyDebug if (started) ++com->start_real; #endif #if 0 disable_intr(); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ enable_intr(); #endif ttwwakeup(tp); splx(s); } static void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); disable_intr(); if (rw & FWRITE) { com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); comstart(tp); /* XXX should clear h/w fifos too. */ } static struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO) return (NULL); return (&sio_tty[unit]); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { cy_addr iobase; int mcr; int msr; iobase = com->iobase; if (how == DMGET) { if (com->channel_control & CD1400_CCR_RCVEN) bits |= TIOCM_LE; mcr = com->mcr_image; if (mcr & com->mcr_dtr) bits |= TIOCM_DTR; if (mcr & com->mcr_rts) /* XXX wired on for Cyclom-8Ys */ bits |= TIOCM_RTS; /* * We must read the modem status from the hardware because * we don't generate modem status change interrupts for all * changes, so com->prev_modem_status is not guaranteed to * be up to date. This is safe, unlike for sio, because * reading the status register doesn't clear pending modem * status change interrupts. */ msr = cd_inb(iobase, CD1400_MSVR2, com->cy_align); if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; if (msr & MSR_RI) /* XXX not connected except for Cyclom-16Y? */ bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= com->mcr_dtr; if (bits & TIOCM_RTS) mcr |= com->mcr_rts; disable_intr(); switch (how) { case DMSET: com->mcr_image = mcr; cd_outb(iobase, CD1400_MSVR1, com->cy_align, mcr); cd_outb(iobase, CD1400_MSVR2, com->cy_align, mcr); break; case DMBIS: com->mcr_image = mcr = com->mcr_image | mcr; cd_outb(iobase, CD1400_MSVR1, com->cy_align, mcr); cd_outb(iobase, CD1400_MSVR2, com->cy_align, mcr); break; case DMBIC: com->mcr_image = mcr = com->mcr_image & ~mcr; cd_outb(iobase, CD1400_MSVR1, com->cy_align, mcr); cd_outb(iobase, CD1400_MSVR2, com->cy_align, mcr); break; } enable_intr(); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN) { someopen = TRUE; #if 0 if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } #endif } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); #if 0 /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } #endif /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < NSIO; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } static void disc_optim(tp, t, com) struct tty *tp; struct termios *t; struct com_s *com; { #ifndef SOFT_HOTCHAR cy_addr iobase; u_char opt; #endif /* * XXX can skip a lot more cases if Smarts. Maybe * (IGNCR | ISTRIP | IXON) in c_iflag. But perhaps we * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state. */ if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; com->hotchar = linesw[tp->t_line].l_hotchar; #ifndef SOFT_HOTCHAR iobase = com->iobase; cd_outb(iobase, CD1400_CAR, com->cy_align, com->unit & CD1400_CAR_CHAN); opt = com->cor[2] & ~CD1400_COR3_SCD34; if (com->hotchar != 0) { cd_outb(iobase, CD1400_SCHR3, com->cy_align, com->hotchar); cd_outb(iobase, CD1400_SCHR4, com->cy_align, com->hotchar); opt |= CD1400_COR3_SCD34; } if (opt != com->cor[2]) { cd_outb(iobase, CD1400_COR3, com->cy_align, com->cor[2] = opt); cd1400_channel_cmd(com->iobase, CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3, com->cy_align); } #endif } #ifdef Smarts /* standard line discipline input routine */ int cyinput(c, tp) int c; struct tty *tp; { /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK * bits, as they are done by the CD1400. Hardly worth the effort, * given that high-throughput sessions are raw anyhow. */ } #endif /* Smarts */ static int comspeed(speed, cy_clock, prescaler_io) speed_t speed; u_long cy_clock; int *prescaler_io; { int actual; int error; int divider; int prescaler; int prescaler_unit; if (speed == 0) return (0); if (speed < 0 || speed > 150000) return (-1); /* determine which prescaler to use */ for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; prescaler_unit--, prescaler >>= 2) { if (cy_clock / prescaler / speed > 63) break; } divider = (cy_clock / prescaler * 2 / speed + 1) / 2; /* round off */ if (divider > 255) divider = 255; actual = cy_clock/prescaler/divider; /* 10 times error in percent: */ error = ((actual - (long)speed) * 2000 / (long)speed + 1) / 2; /* 3.0% max error tolerance */ if (error < -30 || error > 30) return (-1); #if 0 printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); printf("divider = %d (%x)\n", divider, divider); printf("actual = %d\n", actual); printf("error = %d\n", error); #endif *prescaler_io = prescaler_unit; return (divider); } static void cd1400_channel_cmd(iobase, cmd, cy_align) cy_addr iobase; int cmd; int cy_align; { /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, as the card is probed every round? Replaced delaycount with 8k. Either delaycount has to be implemented in FreeBSD or more sensible way of doing these should be implemented. DELAY isn't enough here. */ u_int maxwait = 5 * 8 * 1024; /* approx. 5 ms */ /* wait for processing of previous command to complete */ while (cd_inb(iobase, CD1400_CCR, cy_align) && maxwait--) ; if (!maxwait) log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n", 5 * 8 * 1024); cd_outb(iobase, CD1400_CCR, cy_align, cmd); } #ifdef CyDebug /* useful in ddb */ void cystatus(unit) int unit; { struct com_s *com; cy_addr iobase; u_int ocount; struct tty *tp; com = com_addr(unit); printf("info for channel %d\n", unit); printf("------------------\n"); printf("total cyclom service probes:\t%d\n", cy_svrr_probes); printf("calls to upper layer:\t\t%d\n", cy_timeouts); if (com == NULL) return; iobase = com->iobase; printf("\n"); printf("cd1400 base address:\\tt%p\n", iobase); cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); printf("saved channel_control:\t\t0x%02x\n", com->channel_control); printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n", com->cor[0], com->cor[1], com->cor[2]); printf("service request enable reg:\t0x%02x (0x%02x cached)\n", cd_inb(iobase, CD1400_SRER, com->cy_align), com->intr_enable); printf("service request register:\t0x%02x\n", cd_inb(iobase, CD1400_SVRR, com->cy_align)); printf("modem status:\t\t\t0x%02x (0x%02x cached)\n", cd_inb(iobase, CD1400_MSVR2, com->cy_align), com->prev_modem_status); printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n", cd_inb(iobase, CD1400_RIR, com->cy_align), cd_inb(iobase, CD1400_TIR, com->cy_align), cd_inb(iobase, CD1400_MIR, com->cy_align)); printf("\n"); printf("com state:\t\t\t0x%02x\n", com->state); printf("calls to comstart():\t\t%d (%d useful)\n", com->start_count, com->start_real); printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf); ocount = 0; if (com->obufs[0].l_queued) ocount += com->obufs[0].l_tail - com->obufs[0].l_head; if (com->obufs[1].l_queued) ocount += com->obufs[1].l_tail - com->obufs[1].l_head; printf("tx buffer chars:\t\t%u\n", ocount); printf("received chars:\t\t\t%d\n", com->bytes_in); printf("received exceptions:\t\t%d\n", com->recv_exception); printf("modem signal deltas:\t\t%d\n", com->mdm); printf("transmitted chars:\t\t%d\n", com->bytes_out); printf("\n"); tp = com->tp; if (tp != NULL) { printf("tty state:\t\t\t0x%08x\n", tp->t_state); printf( "upper layer queue lengths:\t%d raw, %d canon, %d output\n", tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); } else printf("tty state:\t\t\tclosed\n"); } #endif /* CyDebug */ diff --git a/sys/dev/cy/cy_isa.c b/sys/dev/cy/cy_isa.c index d10b91158d05..3f765678f0c1 100644 --- a/sys/dev/cy/cy_isa.c +++ b/sys/dev/cy/cy_isa.c @@ -1,2615 +1,2616 @@ /*- * cyclades cyclom-y serial driver * Andrew Herbert , 17 August 1993 * * Copyright (c) 1993 Andrew Herbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name Andrew Herbert may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.69 1998/08/19 04:17:38 bde Exp $ + * $Id: cy.c,v 1.70 1998/08/20 05:21:50 bde Exp $ */ #include "opt_compat.h" #include "opt_devfs.h" #include "cy.h" /* * TODO: * Implement BREAK. * Fix overflows when closing line. * Atomic COR change. * Consoles. */ /* * Temporary compile-time configuration options. */ #define RxFifoThreshold (CD1400_RX_FIFO_SIZE / 2) /* Number of chars in the receiver FIFO before an * an interrupt is generated. Should depend on * line speed. Needs to be about 6 on a 486DX33 * for 4 active ports at 115200 bps. Why doesn't * 10 work? */ #define PollMode /* Use polling-based irq service routine, not the * hardware svcack lines. Must be defined for * Cyclom-16Y boards. Less efficient for Cyclom-8Ys, * and stops 4 * 115200 bps from working. */ #undef Smarts /* Enable slightly more CD1400 intelligence. Mainly * the output CR/LF processing, plus we can avoid a * few checks usually done in ttyinput(). * * XXX not fully implemented, and not particularly * worthwhile. */ #undef CyDebug /* Include debugging code (not very expensive). */ /* These will go away. */ #undef SOFT_CTS_OFLOW #define SOFT_HOTCHAR #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #ifdef SMP #define disable_intr() COM_DISABLE_INTR() #define enable_intr() COM_ENABLE_INTR() #endif /* SMP */ /* * Dictionary so that I can name everything *sio* or *com* to compare with * sio.c. There is also lots of ugly formatting and unnecessary ifdefs to * simplify the comparision. These will go away. */ #define LSR_BI CD1400_RDSR_BREAK #define LSR_FE CD1400_RDSR_FE #define LSR_OE CD1400_RDSR_OE #define LSR_PE CD1400_RDSR_PE #define MCR_DTR CD1400_MSVR2_DTR #define MCR_RTS CD1400_MSVR1_RTS #define MSR_CTS CD1400_MSVR2_CTS #define MSR_DCD CD1400_MSVR2_CD #define MSR_DSR CD1400_MSVR2_DSR #define MSR_RI CD1400_MSVR2_RI #define NSIO (NCY * CY_MAX_PORTS) #define comconsole cyconsole #define comdefaultrate cydefaultrate #define com_events cy_events #define comhardclose cyhardclose #define commctl cymctl #define comparam cyparam #define comspeed cyspeed #define comstart cystart #define comwakeup cywakeup #define nsio_tty ncy_tty #define p_com_addr p_cy_addr #define sioattach cyattach #define sioclose cyclose #define siodevtotty cydevtotty #define siodriver cydriver #define siodtrwakeup cydtrwakeup #define siointr cyintr #define siointr1 cyintr1 #define sioioctl cyioctl #define sioopen cyopen #define siopoll cypoll #define sioprobe cyprobe #define sioread cyread #define siosettimeout cysettimeout #define siostop cystop #define siowrite cywrite #define sio_registered cy_registered #define sio_timeout cy_timeout #define sio_timeout_handle cy_timeout_handle #define sio_timeouts_until_log cy_timeouts_until_log #define sio_tty cy_tty #define CY_MAX_PORTS (CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s) /* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */ #define CD1400_xIVR_CHAN_SHIFT 3 #define CD1400_xIVR_CHAN 0x1F #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RS_IBUFSIZE 256 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) (((mynor) >> 16) * CY_MAX_PORTS \ | (((mynor) & 0xff) & ~MINOR_MAGIC_MASK)) /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * siostop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_char bool_t; /* boolean */ typedef u_char volatile *cy_addr; /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ #if 0 u_char cfcr_image; /* copy of value written to CFCR */ u_char fifo_image; /* copy of value written to FIFO */ #endif u_char gfrcr_image; /* copy of value read from GFRCR */ #if 0 bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t loses_outints; /* nonzero if device loses output interrupts */ #endif u_char mcr_dtr; /* MCR bit that is wired to DTR */ u_char mcr_image; /* copy of value written to MCR */ u_char mcr_rts; /* MCR bit that is wired to RTS */ #if 0 #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ #endif int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ #if 0 u_int tx_fifo_size; #endif u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ int cy_align; /* index for register alignment */ cy_addr cy_iobase; /* base address of this port's cyclom */ cy_addr iobase; /* base address of this port's cd1400 */ int mcr_rts_reg; /* cd1400 reg number of reg holding mcr_rts */ struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; u_int recv_exception; /* exception chars received */ u_int mdm; /* modem signal changes */ #ifdef CyDebug u_int start_count; /* no. of calls to comstart() */ u_int start_real; /* no. of calls that did something */ #endif u_char channel_control;/* CD1400 CCR control command shadow */ u_char cor[3]; /* CD1400 COR1-3 shadows */ u_char intr_enable; /* CD1400 SRER shadow */ /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; #ifdef DEVFS void *devfs_token_ttyd; void *devfs_token_ttyl; void *devfs_token_ttyi; void *devfs_token_cuaa; void *devfs_token_cual; void *devfs_token_cuai; #endif }; /* PCI driver entry point. */ int cyattach_common __P((cy_addr cy_iobase, int cy_align)); static int cy_units __P((cy_addr cy_iobase, int cy_align)); static int sioattach __P((struct isa_device *dev)); static void cd1400_channel_cmd __P((cy_addr iobase, int cmd, int cy_align)); static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); #if 0 static void siointr1 __P((struct com_s *com)); #endif static int commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static swihand_t siopoll; static int sioprobe __P((struct isa_device *dev)); static void siosettimeout __P((void)); static int comspeed __P((speed_t speed, u_long cy_clock, int *prescaler_io)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com)); #ifdef CyDebug void cystatus __P((int unit)); #endif static char driver_name[] = "cy"; /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) struct isa_driver siodriver = { sioprobe, sioattach, driver_name }; static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static d_stop_t siostop; static d_devtotty_t siodevtotty; -#define CDEV_MAJOR 48 -static struct cdevsw sio_cdevsw = { +#define CDEV_MAJOR 48 +static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, ttpoll, nommap, NULL, driver_name, - NULL, -1, + NULL, -1, nodump, nopsize, + D_TTY, }; static int comconsole = -1; static speed_t comdefaultrate = TTYDEF_SPEED; static u_int com_events; /* input chars + weighted output completions */ static bool_t sio_registered; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); #if 0 /* XXX */ static struct tty *sio_tty[NSIO]; #else static struct tty sio_tty[NSIO]; #endif static const int nsio_tty = NSIO; #ifdef CyDebug static u_int cd_inbs; static u_int cy_inbs; static u_int cd_outbs; static u_int cy_outbs; static u_int cy_svrr_probes; static u_int cy_timeouts; #endif static int cy_chip_offset[] = { 0x0000, 0x0400, 0x0800, 0x0c00, 0x0200, 0x0600, 0x0a00, 0x0e00, }; static int cy_nr_cd1400s[NCY]; static int cy_total_devices; #undef RxFifoThreshold static int volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2); static int sioprobe(dev) struct isa_device *dev; { cy_addr iobase; iobase = (cy_addr)dev->id_maddr; /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ cd_inb(iobase, CY16_RESET, 0); /* XXX? */ DELAY(500); /* wait for the board to get its act together */ /* this is needed to get the board out of reset */ cd_outb(iobase, CY_CLEAR_INTR, 0, 0); DELAY(500); return (cy_units(iobase, 0) == 0 ? 0 : -1); } static int cy_units(cy_iobase, cy_align) cy_addr cy_iobase; int cy_align; { int cyu; u_char firmware_version; int i; cy_addr iobase; for (cyu = 0; cyu < CY_MAX_CD1400s; ++cyu) { iobase = cy_iobase + (cy_chip_offset[cyu] << cy_align); /* wait for chip to become ready for new command */ for (i = 0; i < 10; i++) { DELAY(50); if (!cd_inb(iobase, CD1400_CCR, cy_align)) break; } /* clear the GFRCR register */ cd_outb(iobase, CD1400_GFRCR, cy_align, 0); /* issue a reset command */ cd_outb(iobase, CD1400_CCR, cy_align, CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET); /* wait for the CD1400 to initialize itself */ for (i = 0; i < 200; i++) { DELAY(50); /* retrieve firmware version */ firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align); if ((firmware_version & 0xf0) == 0x40) break; } /* * Anything in the 0x40-0x4F range is fine. * If one CD1400 is bad then we don't support higher * numbered good ones on this board. */ if ((firmware_version & 0xf0) != 0x40) break; } return (cyu); } static int sioattach(isdp) struct isa_device *isdp; { int adapter; adapter = cyattach_common((cy_addr) isdp->id_maddr, 0); if (adapter < 0) return (0); /* * XXX * This kludge is to allow ISA/PCI device specifications in the * kernel config file to be in any order. */ if (isdp->id_unit != adapter) { printf("cy%d: attached as cy%d\n", isdp->id_unit, adapter); isdp->id_unit = adapter; /* XXX */ } isdp->id_ri_flags |= RI_FAST; return (1); } int cyattach_common(cy_iobase, cy_align) cy_addr cy_iobase; int cy_align; { int adapter; int cyu; dev_t dev; u_char firmware_version; cy_addr iobase; int ncyu; int unit; adapter = cy_total_devices; if ((u_int)adapter >= NCY) { printf( "cy%d: can't attach adapter: insufficient cy devices configured\n", adapter); return (-1); } ncyu = cy_units(cy_iobase, cy_align); if (ncyu == 0) return (-1); cy_nr_cd1400s[adapter] = ncyu; cy_total_devices++; unit = adapter * CY_MAX_PORTS; for (cyu = 0; cyu < ncyu; ++cyu) { int cdu; iobase = (cy_addr) (cy_iobase + (cy_chip_offset[cyu] << cy_align)); firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align); /* Set up a receive timeout period of than 1+ ms. */ cd_outb(iobase, CD1400_PPR, cy_align, howmany(CY_CLOCK(firmware_version) / CD1400_PPR_PRESCALER, 1000)); for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { struct com_s *com; int s; com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT); if (com == NULL) break; bzero(com, sizeof *com); com->unit = unit; com->gfrcr_image = firmware_version; if (CY_RTS_DTR_SWAPPED(firmware_version)) { com->mcr_dtr = MCR_RTS; com->mcr_rts = MCR_DTR; com->mcr_rts_reg = CD1400_MSVR2; } else { com->mcr_dtr = MCR_DTR; com->mcr_rts = MCR_RTS; com->mcr_rts_reg = CD1400_MSVR1; } com->dtr_wait = 3 * hz; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->cy_align = cy_align; com->cy_iobase = cy_iobase; com->iobase = iobase; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; } termioschars(&com->it_in); com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; com->it_out = com->it_in; s = spltty(); com_addr(unit) = com; splx(s); if (!sio_registered) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &sio_cdevsw, NULL); register_swi(SWI_TTY, siopoll); sio_registered = TRUE; } #ifdef DEVFS com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, unit, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyc%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyic%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_LOCK_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttylc%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuac%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaic%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cualc%r%r", adapter, unit % CY_MAX_PORTS); #endif } } /* ensure an edge for the next interrupt */ cd_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); return (adapter); } static int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; cy_addr iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0); if (error != 0) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "cybi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; tp->t_ififosize = 2 * RS_IBUFSIZE; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; #if 0 (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; #endif ++com->wopeners; iobase = com->iobase; /* reset this channel */ cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); cd1400_channel_cmd(iobase, CD1400_CCR_CMDRESET, com->cy_align); /* * Resetting disables the transmitter and receiver as well as * flushing the fifos so some of our cached state becomes * invalid. The documentation suggests that all registers * for the current channel are reset to defaults, but * apparently none are. We wouldn't want DTR cleared. */ com->channel_control = 0; /* Encode per-board unit in LIVR for access in intr routines. */ cd_outb(iobase, CD1400_LIVR, com->cy_align, (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); /* * raise dtr and generally set things up correctly. this * has the side-effect of selecting the appropriate cd1400 * channel, to help us with subsequent channel control stuff */ error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ #if 0 if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); DELAY(100); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(100); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); enable_intr(); #else /* !0 */ /* XXX raise RTS too */ (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); disable_intr(); com->prev_modem_status = com->last_modem_status = cd_inb(iobase, CD1400_MSVR2, com->cy_align); cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); enable_intr(); #endif /* 0 */ /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "cybi" * instead of "cydcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0); --com->wopeners; if (error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); #ifdef broken /* session holds a ref to the tty; can't deallocate */ ttyfree(tp); com->tp = sio_tty[unit] = NULL; #endif return (0); } static void comhardclose(com) struct com_s *com; { cy_addr iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty(); #if 0 com->poll = FALSE; com->poll_output = FALSE; #endif com->do_timestamp = 0; cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); #if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); #endif { #if 0 outb(iobase + com_ier, 0); #else disable_intr(); cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable = 0); enable_intr(); #endif tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { (void)commctl(com, TIOCM_DTR, DMBIC); /* Disable receiver (leave transmitter enabled). */ com->channel_control = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN | CD1400_CCR_RCVDIS; cd1400_channel_cmd(iobase, com->channel_control, com->cy_align); if (com->dtr_wait != 0) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } #if 0 if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ outb(iobase + com_fifo, 0); } #endif com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); tp = com_addr(MINOR_TO_UNIT(mynor))->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; #ifdef Smarts /* XXX duplicate ttwrite(), but without so much output processing on * CR & LF chars. Hardly worth the effort, given that high-throughput * sessions are raw anyhow. */ #else return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); #endif } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } void siointr(unit) int unit; { int baseu; int cy_align; cy_addr cy_iobase; int cyu; cy_addr iobase; u_char status; COM_LOCK(); /* XXX could this be placed down lower in the loop? */ baseu = unit * CY_MAX_PORTS; cy_align = com_addr(baseu)->cy_align; cy_iobase = com_addr(baseu)->cy_iobase; /* check each CD1400 in turn */ for (cyu = 0; cyu < cy_nr_cd1400s[unit]; ++cyu) { iobase = (cy_addr) (cy_iobase + (cy_chip_offset[cyu] << cy_align)); /* poll to see if it has any work */ status = cd_inb(iobase, CD1400_SVRR, cy_align); if (status == 0) continue; #ifdef CyDebug ++cy_svrr_probes; #endif /* service requests as appropriate, giving priority to RX */ if (status & CD1400_SVRR_RXRDY) { struct com_s *com; u_int count; u_char *ioptr; u_char line_status; u_char recv_data; u_char serv_type; #ifdef PollMode u_char save_car; u_char save_rir; #endif #ifdef PollMode save_rir = cd_inb(iobase, CD1400_RIR, cy_align); save_car = cd_inb(iobase, CD1400_CAR, cy_align); /* enter rx service */ cd_outb(iobase, CD1400_CAR, cy_align, save_rir); serv_type = cd_inb(iobase, CD1400_RIVR, cy_align); com = com_addr(baseu + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #else /* ack receive service */ serv_type = cy_inb(iobase, CY8_SVCACKR); com = com_addr(baseu + + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif if (serv_type & CD1400_RIVR_EXCEPTION) { ++com->recv_exception; line_status = cd_inb(iobase, CD1400_RDSR, cy_align); /* break/unnattached error bits or real input? */ recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifndef SOFT_HOTCHAR if (line_status & CD1400_RDSR_SPECIAL && com->hotchar != 0) setsofttty(); #endif #if 1 /* XXX "intelligent" PFO error handling would break O error handling */ if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { /* Don't store PE if IGNPAR and BI if IGNBRK, this hack allows "raw" tty optimization works even if IGN* is set. */ if ( com->tp == NULL || !(com->tp->t_state & TS_ISOPEN) || (line_status & (LSR_PE|LSR_FE)) && (com->tp->t_iflag & IGNPAR) || (line_status & LSR_BI) && (com->tp->t_iflag & IGNBRK)) goto cont; if ( (line_status & (LSR_PE|LSR_FE)) && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) && ((line_status & LSR_FE) || (line_status & LSR_PE) && (com->tp->t_iflag & INPCK))) recv_data = 0; } #endif /* 1 */ ++com->bytes_in; #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, cy_align, com->mcr_image &= ~com->mcr_rts); #endif if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } goto cont; } else { int ifree; count = cd_inb(iobase, CD1400_RDCR, cy_align); if (!count) goto cont; com->bytes_in += count; ioptr = com->iptr; ifree = com->ibufend - ioptr; if (count > ifree) { count -= ifree; com_events += ifree; if (ifree != 0) { if (com->do_timestamp) microtime(&com->timestamp); do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = 0; ++ioptr; } while (--ifree != 0); } com->delta_error_counts [CE_INTERRUPT_BUF_OVERFLOW] += count; do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif } while (--count != 0); } else { if (com->do_timestamp) microtime(&com->timestamp); if (ioptr <= com->ihighwater && ioptr + count > com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, cy_align, com->mcr_image &= ~com->mcr_rts); #endif com_events += count; do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = 0; ++ioptr; } while (--count != 0); } com->iptr = ioptr; } cont: /* terminate service context */ #ifdef PollMode cd_outb(iobase, CD1400_RIR, cy_align, save_rir & ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY)); cd_outb(iobase, CD1400_CAR, cy_align, save_car); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } if (status & CD1400_SVRR_MDMCH) { struct com_s *com; u_char modem_status; #ifdef PollMode u_char save_car; u_char save_mir; #else u_char vector; #endif #ifdef PollMode save_mir = cd_inb(iobase, CD1400_MIR, cy_align); save_car = cd_inb(iobase, CD1400_CAR, cy_align); /* enter modem service */ cd_outb(iobase, CD1400_CAR, cy_align, save_mir); com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS + (save_mir & CD1400_MIR_CHAN)); #else /* ack modem service */ vector = cy_inb(iobase, CY8_SVCACKM); com = com_addr(baseu + ((vector >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif ++com->mdm; modem_status = cd_inb(iobase, CD1400_MSVR2, cy_align); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } #ifdef SOFT_CTS_OFLOW /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) { com->state |= CS_ODEVREADY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) && !(com->intr_enable & CD1400_SRER_TXRDY)) cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } else { com->state &= ~CS_ODEVREADY; if (com->intr_enable & CD1400_SRER_TXRDY) cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } } #endif } /* terminate service context */ #ifdef PollMode cd_outb(iobase, CD1400_MIR, cy_align, save_mir & ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY)); cd_outb(iobase, CD1400_CAR, cy_align, save_car); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } if (status & CD1400_SVRR_TXRDY) { struct com_s *com; #ifdef PollMode u_char save_car; u_char save_tir; #else u_char vector; #endif #ifdef PollMode save_tir = cd_inb(iobase, CD1400_TIR, cy_align); save_car = cd_inb(iobase, CD1400_CAR, cy_align); /* enter tx service */ cd_outb(iobase, CD1400_CAR, cy_align, save_tir); com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS + (save_tir & CD1400_TIR_CHAN)); #else /* ack transmit service */ vector = cy_inb(iobase, CY8_SVCACKT); com = com_addr(baseu + ((vector >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { u_char *ioptr; u_int ocount; ioptr = com->obufq.l_head; ocount = com->obufq.l_tail - ioptr; if (ocount > CD1400_TX_FIFO_SIZE) ocount = CD1400_TX_FIFO_SIZE; com->bytes_out += ocount; do cd_outb(iobase, CD1400_TDR, cy_align, *ioptr++); while (--ocount != 0); com->obufq.l_head = ioptr; if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ com->state &= ~CS_BUSY; cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; /* handle at high level ASAP */ setsofttty(); } } } /* terminate service context */ #ifdef PollMode cd_outb(iobase, CD1400_TIR, cy_align, save_tir & ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY)); cd_outb(iobase, CD1400_CAR, cy_align, save_car); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } } /* ensure an edge for the next interrupt */ cd_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); schedsofttty(); COM_UNLOCK(); } #if 0 static void siointr1(com) struct com_s *com; { } #endif static int sioioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; cy_addr iobase; int mynor; int s; struct tty *tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) int oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } tp = com->tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return (error); s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, com); if (error != ENOIOCTL) { splx(s); return (error); } cd_outb(iobase, CD1400_CAR, com->cy_align, MINOR_TO_UNIT(mynor) & CD1400_CAR_CHAN); switch (cmd) { #if 0 case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; #endif /* 0 */ case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } static void siopoll() { int unit; #ifdef CyDebug ++cy_timeouts; #endif if (com_events == 0) return; repeat: for (unit = 0; unit < NSIO; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; cy_addr iobase; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL) { /* * XXX forget any events related to closed devices * (actually never opened devices) so that we don't * loop. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); if (incc != 0) log(LOG_DEBUG, "sio%d: %d events for device with no tp\n", unit, incc); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) && !(tp->t_state & TS_TBLOCK)) #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else iobase = com->iobase, cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN), cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image |= com->mcr_rts); #endif enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; if (!(com->state & CS_BUSY)) com->tp->t_state &= ~TS_BUSY; enable_intr(); (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { int bits; int cflag; struct com_s *com; u_char cor_change; u_long cy_clock; int idivisor; int iflag; cy_addr iobase; int iprescaler; int itimeout; int odivisor; int oprescaler; u_char opt; int s; int unit; /* do historical conversions */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); /* check requested parameters */ cy_clock = CY_CLOCK(com->gfrcr_image); idivisor = comspeed(t->c_ispeed, cy_clock, &iprescaler); if (idivisor < 0) return (EINVAL); odivisor = comspeed(t->c_ospeed, cy_clock, &oprescaler); if (odivisor < 0) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ iobase = com->iobase; s = spltty(); cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); if (odivisor == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); if (idivisor != 0) { cd_outb(iobase, CD1400_RBPR, com->cy_align, idivisor); cd_outb(iobase, CD1400_RCOR, com->cy_align, iprescaler); } if (odivisor != 0) { cd_outb(iobase, CD1400_TBPR, com->cy_align, odivisor); cd_outb(iobase, CD1400_TCOR, com->cy_align, oprescaler); } /* * channel control * receiver enable * transmitter enable (always set) */ cflag = t->c_cflag; opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS); if (opt != com->channel_control) { com->channel_control = opt; cd1400_channel_cmd(iobase, opt, com->cy_align); } #ifdef Smarts /* set special chars */ /* XXX if one is _POSIX_VDISABLE, can't use some others */ if (t->c_cc[VSTOP] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR1, com->cy_align, t->c_cc[VSTOP]); if (t->c_cc[VSTART] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR2, com->cy_align, t->c_cc[VSTART]); if (t->c_cc[VINTR] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR3, com->cy_align, t->c_cc[VINTR]); if (t->c_cc[VSUSP] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR4, com->cy_align, t->c_cc[VSUSP]); #endif /* * set channel option register 1 - * parity mode * stop bits * char length */ opt = 0; /* parity */ if (cflag & PARENB) { if (cflag & PARODD) opt |= CD1400_COR1_PARODD; opt |= CD1400_COR1_PARNORMAL; } iflag = t->c_iflag; if (!(iflag & INPCK)) opt |= CD1400_COR1_NOINPCK; bits = 1 + 1; /* stop bits */ if (cflag & CSTOPB) { ++bits; opt |= CD1400_COR1_STOP2; } /* char length */ switch (cflag & CSIZE) { case CS5: bits += 5; opt |= CD1400_COR1_CS5; break; case CS6: bits += 6; opt |= CD1400_COR1_CS6; break; case CS7: bits += 7; opt |= CD1400_COR1_CS7; break; default: bits += 8; opt |= CD1400_COR1_CS8; break; } cor_change = 0; if (opt != com->cor[0]) { cor_change |= CD1400_CCR_COR1; cd_outb(iobase, CD1400_COR1, com->cy_align, com->cor[0] = opt); } /* * Set receive time-out period, normally to max(one char time, 5 ms). */ if (t->c_ispeed == 0) itimeout = cd_inb(iobase, CD1400_RTPR, com->cy_align); else { itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed; #ifdef SOFT_HOTCHAR #define MIN_RTP 1 #else #define MIN_RTP 5 #endif if (itimeout < MIN_RTP) itimeout = MIN_RTP; } if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0 && t->c_cc[VTIME] * 10 > itimeout) itimeout = t->c_cc[VTIME] * 10; if (itimeout > 255) itimeout = 255; cd_outb(iobase, CD1400_RTPR, com->cy_align, itimeout); /* * set channel option register 2 - * flow control */ opt = 0; #ifdef Smarts if (iflag & IXANY) opt |= CD1400_COR2_IXANY; if (iflag & IXOFF) opt |= CD1400_COR2_IXOFF; #endif #ifndef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_COR2_CCTS_OFLOW; #endif if (opt != com->cor[1]) { cor_change |= CD1400_CCR_COR2; cd_outb(iobase, CD1400_COR2, com->cy_align, com->cor[1] = opt); } /* * set channel option register 3 - * receiver FIFO interrupt threshold * flow control */ opt = RxFifoThreshold; #ifdef Smarts if (t->c_lflag & ICANON) opt |= CD1400_COR3_SCD34; /* detect INTR & SUSP chars */ if (iflag & IXOFF) /* detect and transparently handle START and STOP chars */ opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12; #endif if (opt != com->cor[2]) { cor_change |= CD1400_CCR_COR3; cd_outb(iobase, CD1400_COR3, com->cy_align, com->cor[2] = opt); } /* notify the CD1400 if COR1-3 have changed */ if (cor_change) cd1400_channel_cmd(iobase, CD1400_CCR_CMDCORCHG | cor_change, com->cy_align); /* * set channel option register 4 - * CR/NL processing * break processing * received exception processing */ opt = 0; if (iflag & IGNCR) opt |= CD1400_COR4_IGNCR; #ifdef Smarts /* * we need a new ttyinput() for this, as we don't want to * have ICRNL && INLCR being done in both layers, or to have * synchronisation problems */ if (iflag & ICRNL) opt |= CD1400_COR4_ICRNL; if (iflag & INLCR) opt |= CD1400_COR4_INLCR; #endif if (iflag & IGNBRK) opt |= CD1400_COR4_IGNBRK; if (!(iflag & BRKINT)) opt |= CD1400_COR4_NOBRKINT; #if 0 /* XXX using this "intelligence" breaks reporting of overruns. */ if (iflag & IGNPAR) opt |= CD1400_COR4_PFO_DISCARD; else { if (iflag & PARMRK) opt |= CD1400_COR4_PFO_ESC; else opt |= CD1400_COR4_PFO_NUL; } #else opt |= CD1400_COR4_PFO_EXCEPTION; #endif cd_outb(iobase, CD1400_COR4, com->cy_align, opt); /* * set channel option register 5 - */ opt = 0; if (iflag & ISTRIP) opt |= CD1400_COR5_ISTRIP; if (t->c_iflag & IEXTEN) /* enable LNEXT (e.g. ctrl-v quoting) handling */ opt |= CD1400_COR5_LNEXT; #ifdef Smarts if (t->c_oflag & ONLCR) opt |= CD1400_COR5_ONLCR; if (t->c_oflag & OCRNL) opt |= CD1400_COR5_OCRNL; #endif cd_outb(iobase, CD1400_COR5, com->cy_align, opt); /* * We always generate modem status change interrupts for CD changes. * Among other things, this is necessary to track TS_CARR_ON for * pstat to print even when the driver doesn't care. CD changes * should be rare so interrupts for them are not worth extra code to * avoid. We avoid interrupts for other modem status changes (except * for CTS changes when SOFT_CTS_OFLOW is configured) since this is * simplest and best. */ /* * set modem change option register 1 * generate modem interrupts on which 1 -> 0 input transitions * also controls auto-DTR output flow-control, which we don't use */ opt = CD1400_MCOR1_CDzd; #ifdef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_MCOR1_CTSzd; #endif cd_outb(iobase, CD1400_MCOR1, com->cy_align, opt); /* * set modem change option register 2 * generate modem interrupts on specific 0 -> 1 input transitions */ opt = CD1400_MCOR2_CDod; #ifdef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_MCOR2_CTSod; #endif cd_outb(iobase, CD1400_MCOR2, com->cy_align, opt); /* * XXX should have done this long ago, but there is too much state * to change all atomically. */ disable_intr(); com->state &= ~CS_TTGO; if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) { com->state |= CS_RTS_IFLOW; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image |= com->mcr_rts); #endif } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; #ifdef SOFT_CTS_OFLOW com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; } #endif /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); #if 0 /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); #endif if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { if (!(com->intr_enable & CD1400_SRER_TXRDY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } else { if (com->intr_enable & CD1400_SRER_TXRDY) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } enable_intr(); splx(s); comstart(tp); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; cy_addr iobase; int s; #ifdef CyDebug bool_t started; #endif int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); #ifdef CyDebug ++com->start_count; started = FALSE; #endif disable_intr(); cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); if (tp->t_state & TS_TTSTOP) { com->state &= ~CS_TTGO; if (com->intr_enable & CD1400_SRER_TXRDY) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } else { com->state |= CS_TTGO; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) && !(com->intr_enable & CD1400_SRER_TXRDY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & com->mcr_rts && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image &= ~com->mcr_rts); #endif } else { if (!(com->mcr_image & com->mcr_rts) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image |= com->mcr_rts); #endif } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { #ifdef CyDebug started = TRUE; #endif com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } enable_intr(); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { #ifdef CyDebug started = TRUE; #endif com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } enable_intr(); } tp->t_state |= TS_BUSY; } #ifdef CyDebug if (started) ++com->start_real; #endif #if 0 disable_intr(); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ enable_intr(); #endif ttwwakeup(tp); splx(s); } static void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); disable_intr(); if (rw & FWRITE) { com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); comstart(tp); /* XXX should clear h/w fifos too. */ } static struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO) return (NULL); return (&sio_tty[unit]); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { cy_addr iobase; int mcr; int msr; iobase = com->iobase; if (how == DMGET) { if (com->channel_control & CD1400_CCR_RCVEN) bits |= TIOCM_LE; mcr = com->mcr_image; if (mcr & com->mcr_dtr) bits |= TIOCM_DTR; if (mcr & com->mcr_rts) /* XXX wired on for Cyclom-8Ys */ bits |= TIOCM_RTS; /* * We must read the modem status from the hardware because * we don't generate modem status change interrupts for all * changes, so com->prev_modem_status is not guaranteed to * be up to date. This is safe, unlike for sio, because * reading the status register doesn't clear pending modem * status change interrupts. */ msr = cd_inb(iobase, CD1400_MSVR2, com->cy_align); if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; if (msr & MSR_RI) /* XXX not connected except for Cyclom-16Y? */ bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= com->mcr_dtr; if (bits & TIOCM_RTS) mcr |= com->mcr_rts; disable_intr(); switch (how) { case DMSET: com->mcr_image = mcr; cd_outb(iobase, CD1400_MSVR1, com->cy_align, mcr); cd_outb(iobase, CD1400_MSVR2, com->cy_align, mcr); break; case DMBIS: com->mcr_image = mcr = com->mcr_image | mcr; cd_outb(iobase, CD1400_MSVR1, com->cy_align, mcr); cd_outb(iobase, CD1400_MSVR2, com->cy_align, mcr); break; case DMBIC: com->mcr_image = mcr = com->mcr_image & ~mcr; cd_outb(iobase, CD1400_MSVR1, com->cy_align, mcr); cd_outb(iobase, CD1400_MSVR2, com->cy_align, mcr); break; } enable_intr(); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN) { someopen = TRUE; #if 0 if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } #endif } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); #if 0 /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } #endif /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < NSIO; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } static void disc_optim(tp, t, com) struct tty *tp; struct termios *t; struct com_s *com; { #ifndef SOFT_HOTCHAR cy_addr iobase; u_char opt; #endif /* * XXX can skip a lot more cases if Smarts. Maybe * (IGNCR | ISTRIP | IXON) in c_iflag. But perhaps we * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state. */ if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; com->hotchar = linesw[tp->t_line].l_hotchar; #ifndef SOFT_HOTCHAR iobase = com->iobase; cd_outb(iobase, CD1400_CAR, com->cy_align, com->unit & CD1400_CAR_CHAN); opt = com->cor[2] & ~CD1400_COR3_SCD34; if (com->hotchar != 0) { cd_outb(iobase, CD1400_SCHR3, com->cy_align, com->hotchar); cd_outb(iobase, CD1400_SCHR4, com->cy_align, com->hotchar); opt |= CD1400_COR3_SCD34; } if (opt != com->cor[2]) { cd_outb(iobase, CD1400_COR3, com->cy_align, com->cor[2] = opt); cd1400_channel_cmd(com->iobase, CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3, com->cy_align); } #endif } #ifdef Smarts /* standard line discipline input routine */ int cyinput(c, tp) int c; struct tty *tp; { /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK * bits, as they are done by the CD1400. Hardly worth the effort, * given that high-throughput sessions are raw anyhow. */ } #endif /* Smarts */ static int comspeed(speed, cy_clock, prescaler_io) speed_t speed; u_long cy_clock; int *prescaler_io; { int actual; int error; int divider; int prescaler; int prescaler_unit; if (speed == 0) return (0); if (speed < 0 || speed > 150000) return (-1); /* determine which prescaler to use */ for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; prescaler_unit--, prescaler >>= 2) { if (cy_clock / prescaler / speed > 63) break; } divider = (cy_clock / prescaler * 2 / speed + 1) / 2; /* round off */ if (divider > 255) divider = 255; actual = cy_clock/prescaler/divider; /* 10 times error in percent: */ error = ((actual - (long)speed) * 2000 / (long)speed + 1) / 2; /* 3.0% max error tolerance */ if (error < -30 || error > 30) return (-1); #if 0 printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); printf("divider = %d (%x)\n", divider, divider); printf("actual = %d\n", actual); printf("error = %d\n", error); #endif *prescaler_io = prescaler_unit; return (divider); } static void cd1400_channel_cmd(iobase, cmd, cy_align) cy_addr iobase; int cmd; int cy_align; { /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, as the card is probed every round? Replaced delaycount with 8k. Either delaycount has to be implemented in FreeBSD or more sensible way of doing these should be implemented. DELAY isn't enough here. */ u_int maxwait = 5 * 8 * 1024; /* approx. 5 ms */ /* wait for processing of previous command to complete */ while (cd_inb(iobase, CD1400_CCR, cy_align) && maxwait--) ; if (!maxwait) log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n", 5 * 8 * 1024); cd_outb(iobase, CD1400_CCR, cy_align, cmd); } #ifdef CyDebug /* useful in ddb */ void cystatus(unit) int unit; { struct com_s *com; cy_addr iobase; u_int ocount; struct tty *tp; com = com_addr(unit); printf("info for channel %d\n", unit); printf("------------------\n"); printf("total cyclom service probes:\t%d\n", cy_svrr_probes); printf("calls to upper layer:\t\t%d\n", cy_timeouts); if (com == NULL) return; iobase = com->iobase; printf("\n"); printf("cd1400 base address:\\tt%p\n", iobase); cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); printf("saved channel_control:\t\t0x%02x\n", com->channel_control); printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n", com->cor[0], com->cor[1], com->cor[2]); printf("service request enable reg:\t0x%02x (0x%02x cached)\n", cd_inb(iobase, CD1400_SRER, com->cy_align), com->intr_enable); printf("service request register:\t0x%02x\n", cd_inb(iobase, CD1400_SVRR, com->cy_align)); printf("modem status:\t\t\t0x%02x (0x%02x cached)\n", cd_inb(iobase, CD1400_MSVR2, com->cy_align), com->prev_modem_status); printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n", cd_inb(iobase, CD1400_RIR, com->cy_align), cd_inb(iobase, CD1400_TIR, com->cy_align), cd_inb(iobase, CD1400_MIR, com->cy_align)); printf("\n"); printf("com state:\t\t\t0x%02x\n", com->state); printf("calls to comstart():\t\t%d (%d useful)\n", com->start_count, com->start_real); printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf); ocount = 0; if (com->obufs[0].l_queued) ocount += com->obufs[0].l_tail - com->obufs[0].l_head; if (com->obufs[1].l_queued) ocount += com->obufs[1].l_tail - com->obufs[1].l_head; printf("tx buffer chars:\t\t%u\n", ocount); printf("received chars:\t\t\t%d\n", com->bytes_in); printf("received exceptions:\t\t%d\n", com->recv_exception); printf("modem signal deltas:\t\t%d\n", com->mdm); printf("transmitted chars:\t\t%d\n", com->bytes_out); printf("\n"); tp = com->tp; if (tp != NULL) { printf("tty state:\t\t\t0x%08x\n", tp->t_state); printf( "upper layer queue lengths:\t%d raw, %d canon, %d output\n", tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); } else printf("tty state:\t\t\tclosed\n"); } #endif /* CyDebug */ diff --git a/sys/dev/dgb/dgb.c b/sys/dev/dgb/dgb.c index 9537c12055c5..aac272582aa2 100644 --- a/sys/dev/dgb/dgb.c +++ b/sys/dev/dgb/dgb.c @@ -1,2312 +1,2313 @@ /*- - * dgb.c $Id: dgb.c,v 1.38 1998/08/12 23:44:22 brian Exp $ + * dgb.c $Id: dgb.c,v 1.39 1998/08/16 01:21:49 bde Exp $ * * Digiboard driver. * * Stage 1. "Better than nothing". * Stage 2. "Gee, it works!". * * Based on sio driver by Bruce Evans and on Linux driver by Troy * De Jongh or * which is under GNU General Public License version 2 so this driver * is forced to be under GPL 2 too. * * Written by Serge Babkin, * Joint Stock Commercial Bank "Chelindbank" * (Chelyabinsk, Russia) * babkin@hq.icb.chel.su * * Assorted hacks to make it more functional and working under 3.0-current. * Fixed broken routines to prevent processes hanging on closed (thanks * to Bruce for his patience and assistance). Thanks also to Maxim Bolotin * for his patches which did most of the work to get this * running under 2.2/3.0-current. * Implemented ioctls: TIOCMSDTRWAIT, TIOCMGDTRWAIT, TIOCTIMESTAMP & * TIOCDCDTIMESTAMP. * Sysctl debug flag is now a bitflag, to filter noise during debugging. * David L. Nugent */ #include "opt_compat.h" #include "opt_devfs.h" #include "dgb.h" #if NDGB > 0 /* Helg: i.e.25 times per sec board will be polled */ #define POLLSPERSEC 25 /* How many charactes can we write to input tty rawq */ #define DGB_IBUFSIZE (TTYHOG-100) /* the overall number of ports controlled by this driver */ #ifndef NDGBPORTS # define NDGBPORTS (NDGB*16) #endif #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #include #include #define DGB_DEBUG /* Enable debugging info via sysctl */ #include #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define UNIT_MASK 0x30000 #define PORT_MASK 0x1F #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) (((mynor) & UNIT_MASK)>>16) #define MINOR_TO_PORT(mynor) ((mynor) & PORT_MASK) /* types. XXX - should be elsewhere */ typedef u_char bool_t; /* boolean */ /* digiboard port structure */ struct dgb_p { bool_t status; u_char unit; /* board unit number */ u_char pnum; /* port number */ u_char omodem; /* FEP output modem status */ u_char imodem; /* FEP input modem status */ u_char modemfake; /* Modem values to be forced */ u_char modem; /* Force values */ u_char hflow; u_char dsr; u_char dcd; u_char stopc; u_char startc; u_char stopca; u_char startca; u_char fepstopc; u_char fepstartc; u_char fepstopca; u_char fepstartca; u_char txwin; u_char rxwin; ushort fepiflag; ushort fepcflag; ushort fepoflag; ushort txbufhead; ushort txbufsize; ushort rxbufhead; ushort rxbufsize; int close_delay; int count; int blocked_open; int event; int asyncflags; u_long statusflags; u_char *txptr; u_char *rxptr; volatile struct board_chan *brdchan; struct tty *tty; bool_t active_out; /* nonzero if the callout device is open */ u_int wopeners; /* # processes waiting for DCD in open() */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; /* flags of state, are used in sleep() too */ u_char closing; /* port is being closed now */ u_char draining; /* port is being drained now */ u_char used; /* port is being used now */ u_char mustdrain; /* data must be waited to drain in dgbparam() */ #ifdef DEVFS struct { void *tty; void *ttyi; void *ttyl; void *cua; void *cuai; void *cual; } devfs_token; #endif }; /* Digiboard per-board structure */ struct dgb_softc { /* struct board_info */ u_char status; /* status: DISABLED/ENABLED */ u_char unit; /* unit number */ u_char type; /* type of card: PCXE, PCXI, PCXEVE */ u_char altpin; /* do we need alternate pin setting ? */ int numports; /* number of ports on card */ int port; /* I/O port */ u_char *vmem; /* virtual memory address */ long pmem; /* physical memory address */ int mem_seg; /* internal memory segment */ struct dgb_p *ports; /* pointer to array of port descriptors */ struct tty *ttys; /* pointer to array of TTY structures */ volatile struct global_data *mailbox; }; static struct dgb_softc dgb_softc[NDGB]; static struct dgb_p dgb_ports[NDGBPORTS]; static struct tty dgb_tty[NDGBPORTS]; /* * The public functions in the com module ought to be declared in a com-driver * system header. */ /* Interrupt handling entry points. */ static void dgbpoll __P((void *unit_c)); /* Device switch entry points. */ #define dgbreset noreset #define dgbmmap nommap #define dgbstrategy nostrategy static int dgbattach __P((struct isa_device *dev)); static int dgbprobe __P((struct isa_device *dev)); static void fepcmd(struct dgb_p *port, unsigned cmd, unsigned op1, unsigned op2, unsigned ncmds, unsigned bytecmd); static void dgbstart __P((struct tty *tp)); static int dgbparam __P((struct tty *tp, struct termios *t)); static void dgbhardclose __P((struct dgb_p *port)); static void dgb_drain_or_flush __P((struct dgb_p *port)); static int dgbdrain __P((struct dgb_p *port)); static void dgb_pause __P((void *chan)); static void wakeflush __P((void *p)); static void disc_optim __P((struct tty *tp, struct termios *t)); struct isa_driver dgbdriver = { dgbprobe, dgbattach, "dgb",0 }; static d_open_t dgbopen; static d_close_t dgbclose; static d_read_t dgbread; static d_write_t dgbwrite; static d_ioctl_t dgbioctl; static d_stop_t dgbstop; static d_devtotty_t dgbdevtotty; -#define CDEV_MAJOR 58 -static struct cdevsw dgb_cdevsw = - { dgbopen, dgbclose, dgbread, dgbwrite, /*58*/ - dgbioctl, dgbstop, noreset, dgbdevtotty, /* dgb */ - ttpoll, nommap, NULL, "dgb", NULL, -1 }; +#define CDEV_MAJOR 58 +static struct cdevsw dgb_cdevsw = { + dgbopen, dgbclose, dgbread, dgbwrite, + dgbioctl, dgbstop, noreset, dgbdevtotty, + ttpoll, nommap, NULL, "dgb", + NULL, -1, nodump, nopsize, + D_TTY, +}; static speed_t dgbdefaultrate = TTYDEF_SPEED; static struct speedtab dgbspeedtab[] = { 0, FEP_B0, /* old (sysV-like) Bx codes */ 50, FEP_B50, 75, FEP_B75, 110, FEP_B110, 134, FEP_B134, 150, FEP_B150, 200, FEP_B200, 300, FEP_B300, 600, FEP_B600, 1200, FEP_B1200, 1800, FEP_B1800, 2400, FEP_B2400, 4800, FEP_B4800, 9600, FEP_B9600, 19200, FEP_B19200, 38400, FEP_B38400, 57600, (FEP_FASTBAUD|FEP_B50), /* B50 & fast baud table */ 115200, (FEP_FASTBAUD|FEP_B110), /* B100 & fast baud table */ -1, -1 }; static struct dbgflagtbl { tcflag_t in_mask; tcflag_t in_val; tcflag_t out_val; } dgb_cflags[] = { { PARODD, PARODD, FEP_PARODD }, { PARENB, PARENB, FEP_PARENB }, { CSTOPB, CSTOPB, FEP_CSTOPB }, { CSIZE, CS5, FEP_CS6 }, { CSIZE, CS6, FEP_CS6 }, { CSIZE, CS7, FEP_CS7 }, { CSIZE, CS8, FEP_CS8 }, { CLOCAL, CLOCAL, FEP_CLOCAL }, { (tcflag_t)-1 } }, dgb_iflags[] = { { IGNBRK, IGNBRK, FEP_IGNBRK }, { BRKINT, BRKINT, FEP_BRKINT }, { IGNPAR, IGNPAR, FEP_IGNPAR }, { PARMRK, PARMRK, FEP_PARMRK }, { INPCK, INPCK, FEP_INPCK }, { ISTRIP, ISTRIP, FEP_ISTRIP }, { IXON, IXON, FEP_IXON }, { IXOFF, IXOFF, FEP_IXOFF }, { IXANY, IXANY, FEP_IXANY }, { (tcflag_t)-1 } }, dgb_flow[] = { { CRTSCTS, CRTSCTS, CTS|RTS }, { CRTSCTS, CCTS_OFLOW, CTS }, { CRTSCTS, CRTS_IFLOW, RTS }, { (tcflag_t)-1 } }; /* xlat bsd termios flags to dgb sys-v style */ static tcflag_t dgbflags(struct dbgflagtbl *tbl, tcflag_t input) { tcflag_t output = 0; int i; for (i=0; tbl[i].in_mask != (tcflag_t)-1; i++) { if ((input & tbl[i].in_mask) == tbl[i].in_val) output |= tbl[i].out_val; } return output; } #ifdef DGB_DEBUG static int dgbdebug=0; SYSCTL_INT(_debug, OID_AUTO, dgb_debug, CTLFLAG_RW, &dgbdebug, 0, ""); #endif static __inline int setwin __P((struct dgb_softc *sc, unsigned addr)); static __inline int setinitwin __P((struct dgb_softc *sc, unsigned addr)); static __inline void hidewin __P((struct dgb_softc *sc)); static __inline void towin __P((struct dgb_softc *sc, int win)); /*Helg: to allow recursive dgb...() calls */ typedef struct { /* If we were called and don't want to disturb we need: */ int port; /* write to this port */ u_char data; /* this data on exit */ /* or DATA_WINOFF to close memory window on entry */ } BoardMemWinState; /* so several channels and even boards can coexist */ #define DATA_WINOFF 0 static BoardMemWinState bmws; /* return current memory window state and close window */ static BoardMemWinState bmws_get(void) { BoardMemWinState bmwsRet=bmws; if(bmws.data!=DATA_WINOFF) outb(bmws.port, bmws.data=DATA_WINOFF); return bmwsRet; } /* restore memory window state */ static void bmws_set(BoardMemWinState ws) { if(ws.data != bmws.data || ws.port!=bmws.port ) { if(bmws.data!=DATA_WINOFF) outb(bmws.port,DATA_WINOFF); if(ws.data!=DATA_WINOFF) outb(ws.port, ws.data); bmws=ws; } } static __inline int setwin(sc,addr) struct dgb_softc *sc; unsigned int addr; { if(sc->type==PCXEVE) { outb(bmws.port=sc->port+1, bmws.data=FEPWIN|(addr>>13)); DPRINT3(DB_WIN,"dgb%d: switched to window 0x%x\n",sc->unit,addr>>13); return (addr & 0x1FFF); } else { outb(bmws.port=sc->port,bmws.data=FEPMEM); return addr; } } static __inline int setinitwin(sc,addr) struct dgb_softc *sc; unsigned int addr; { if(sc->type==PCXEVE) { outb(bmws.port=sc->port+1, bmws.data=FEPWIN|(addr>>13)); DPRINT3(DB_WIN,"dgb%d: switched to window 0x%x\n",sc->unit,addr>>13); return (addr & 0x1FFF); } else { outb(bmws.port=sc->port,bmws.data=inb(sc->port)|FEPMEM); return addr; } } static __inline void hidewin(sc) struct dgb_softc *sc; { bmws.data=0; if(sc->type==PCXEVE) outb(bmws.port=sc->port+1, bmws.data); else outb(bmws.port=sc->port, bmws.data); } static __inline void towin(sc,win) struct dgb_softc *sc; int win; { if(sc->type==PCXEVE) { outb(bmws.port=sc->port+1, bmws.data=win); } else { outb(bmws.port=sc->port,bmws.data=FEPMEM); } } static int dgbprobe(dev) struct isa_device *dev; { struct dgb_softc *sc= &dgb_softc[dev->id_unit]; int i, v, t; u_long win_size; /* size of vizible memory window */ u_char *mem; int addr; int unit=dev->id_unit; sc->unit=dev->id_unit; sc->port=dev->id_iobase; if(dev->id_flags & DGBFLAG_ALTPIN) sc->altpin=1; else sc->altpin=0; /* left 24 bits only (ISA address) */ sc->pmem=((intptr_t)(void *)dev->id_maddr & 0xFFFFFF); DPRINT4(DB_INFO,"dgb%d: port 0x%x mem 0x%lx\n",unit,sc->port,sc->pmem); outb(sc->port, FEPRST); sc->status=DISABLED; for(i=0; i< 1000; i++) { DELAY(1); if( (inb(sc->port) & FEPMASK) == FEPRST ) { sc->status=ENABLED; DPRINT3(DB_EXCEPT,"dgb%d: got reset after %d us\n",unit,i); break; } } if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dgb%d: failed to respond\n",dev->id_unit); return 0; } /* check type of card and get internal memory characteristics */ v=inb(sc->port); if( v & 0x1 ) { switch( v&0x30 ) { case 0: sc->mem_seg=0xF000; win_size=0x10000; printf("dgb%d: PC/Xi 64K\n",dev->id_unit); break; case 0x10: sc->mem_seg=0xE000; win_size=0x20000; printf("dgb%d: PC/Xi 128K\n",dev->id_unit); break; case 0x20: sc->mem_seg=0xC000; win_size=0x40000; printf("dgb%d: PC/Xi 256K\n",dev->id_unit); break; default: /* case 0x30: */ sc->mem_seg=0x8000; win_size=0x80000; printf("dgb%d: PC/Xi 512K\n",dev->id_unit); break; } sc->type=PCXI; } else { outb(sc->port, 1); v=inb(sc->port); if( v & 0x1 ) { printf("dgb%d: PC/Xm isn't supported\n",dev->id_unit); sc->status=DISABLED; return 0; } sc->mem_seg=0xF000; if(dev->id_flags==DGBFLAG_NOWIN || ( v&0xC0 )==0) { win_size=0x10000; printf("dgb%d: PC/Xe 64K\n",dev->id_unit); sc->type=PCXE; } else { win_size=0x2000; printf("dgb%d: PC/Xe 64/8K (windowed)\n",dev->id_unit); sc->type=PCXEVE; if((u_long)sc->pmem & ~0xFFE000) { printf("dgb%d: warning: address 0x%lx truncated to 0x%lx\n", dev->id_unit, sc->pmem, sc->pmem & 0xFFE000); dev->id_maddr= (u_char *)(void *)(intptr_t)( sc->pmem & 0xFFE000 ); } } } /* save size of vizible memory segment */ dev->id_msize=win_size; /* map memory */ dev->id_maddr=sc->vmem=pmap_mapdev(sc->pmem,dev->id_msize); outb(sc->port, FEPCLR); /* drop RESET */ hidewin(sc); /* Helg: to set initial bmws state */ return 4; /* we need I/O space of 4 ports */ } static int dgbattach(dev) struct isa_device *dev; { int unit=dev->id_unit; struct dgb_softc *sc= &dgb_softc[dev->id_unit]; int i, t; u_char *mem; u_char *ptr; int addr; struct dgb_p *port; volatile struct board_chan *bc; struct global_data *gd; int shrinkmem; int nfails; ushort *pstat; int lowwater; static int nports=0; #ifdef DEVFS char suffix; #endif if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dbg%d: try to attach a disabled card\n",unit); return 0; } mem=sc->vmem; DPRINT3(DB_INFO,"dgb%d: internal memory segment 0x%x\n",unit,sc->mem_seg); outb(sc->port, FEPRST); DELAY(1); for(i=0; (inb(sc->port) & FEPMASK) != FEPRST ; i++) { if(i>10000) { printf("dgb%d: 1st reset failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } DELAY(1); } DPRINT3(DB_INFO,"dgb%d: got reset after %d us\n",unit,i); /* for PCXEVE set up interrupt and base address */ if(sc->type==PCXEVE) { t=(((u_long)sc->pmem>>8) & 0xFFE0) | 0x10 /* enable windowing */; /* IRQ isn't used */ outb(sc->port+2,t & 0xFF); outb(sc->port+3,t>>8); } else if(sc->type==PCXE) { t=(((u_long)sc->pmem>>8) & 0xFFE0) /* disable windowing */; outb(sc->port+2,t & 0xFF); outb(sc->port+3,t>>8); } if(sc->type==PCXI || sc->type==PCXE) { outb(sc->port, FEPRST|FEPMEM); DELAY(1); for(i=0; (inb(sc->port) & FEPMASK) != (FEPRST|FEPMEM) ; i++) { if(i>10000) { printf("dgb%d: 2nd reset failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } DELAY(1); } DPRINT3(DB_INFO,"dgb%d: got memory after %d us\n",unit,i); } mem=sc->vmem; /* very short memory test */ addr=setinitwin(sc,BOTWIN); *(u_long *)(mem+addr) = 0xA55A3CC3; if(*(u_long *)(mem+addr)!=0xA55A3CC3) { printf("dgb%d: 1st memory test failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } addr=setinitwin(sc,TOPWIN); *(u_long *)(mem+addr) = 0x5AA5C33C; if(*(u_long *)(mem+addr)!=0x5AA5C33C) { printf("dgb%d: 2nd memory test failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } addr=setinitwin(sc,BIOSCODE+((0xF000-sc->mem_seg)<<4)); *(u_long *)(mem+addr) = 0x5AA5C33C; if(*(u_long *)(mem+addr)!=0x5AA5C33C) { printf("dgb%d: 3rd (BIOS) memory test failed\n",dev->id_unit); } addr=setinitwin(sc,MISCGLOBAL); for(i=0; i<16; i++) { mem[addr+i]=0; } if(sc->type==PCXI || sc->type==PCXE) { addr=BIOSCODE+((0xF000-sc->mem_seg)<<4); DPRINT3(DB_INFO,"dgb%d: BIOS local address=0x%x\n",unit,addr); ptr= mem+addr; for(i=0; i=5) { printf("dgb%d: 4th memory test (BIOS load) fails\n",unit); break; } } outb(sc->port,FEPMEM); for(i=0; (inb(sc->port) & FEPMASK) != FEPMEM ; i++) { if(i>10000) { printf("dgb%d: BIOS start failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } DELAY(1); } DPRINT3(DB_INFO,"dgb%d: reset dropped after %d us\n",unit,i); for(i=0; i<200000; i++) { if( *((ushort *)(mem+MISCGLOBAL)) == *((ushort *)"GD") ) goto load_fep; DELAY(1); } printf("dgb%d: BIOS download failed\n",dev->id_unit); DPRINT4(DB_EXCEPT,"dgb%d: code=0x%x must be 0x%x\n", dev->id_unit, *((ushort *)(mem+MISCGLOBAL)), *((ushort *)"GD")); sc->status=DISABLED; hidewin(sc); return 0; } if(sc->type==PCXEVE) { /* set window 7 */ outb(sc->port+1,0xFF); ptr= mem+(BIOSCODE & 0x1FFF); for(i=0; i=5) { printf("dgb%d: 4th memory test (BIOS load) fails\n",unit); break; } } outb(sc->port,FEPCLR); setwin(sc,0); for(i=0; (inb(sc->port) & FEPMASK) != FEPCLR ; i++) { if(i>10000) { printf("dgb%d: BIOS start failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } DELAY(1); } DPRINT3(DB_INFO,"dgb%d: reset dropped after %d us\n",unit,i); addr=setwin(sc,MISCGLOBAL); for(i=0; i<200000; i++) { if(*(ushort *)(mem+addr)== *(ushort *)"GD") goto load_fep; DELAY(1); } printf("dgb%d: BIOS download failed\n",dev->id_unit); DPRINT5(DB_EXCEPT,"dgb%d: Error#(0x%x,0x%x) code=0x%x\n", dev->id_unit, *(ushort *)(mem+0xC12), *(ushort *)(mem+0xC14), *(ushort *)(mem+MISCGLOBAL)); sc->status=DISABLED; hidewin(sc); return 0; } load_fep: DPRINT2(DB_INFO,"dgb%d: BIOS loaded\n",dev->id_unit); addr=setwin(sc,FEPCODE); ptr= mem+addr; for(i=0; imem_seg+FEPCODESEG; *(ushort *)(mem+addr+ 4)=0; *(ushort *)(mem+addr+ 6)=FEPCODESEG; *(ushort *)(mem+addr+ 8)=0; *(ushort *)(mem+addr+10)=pcxx_ncook; outb(sc->port,FEPMEM|FEPINT); /* send interrupt to BIOS */ outb(sc->port,FEPMEM); for(i=0; *(ushort *)(mem+addr)!=0; i++) { if(i>200000) { printf("dgb%d: FEP code download failed\n",unit); DPRINT3(DB_EXCEPT,"dgb%d: code=0x%x must be 0\n", unit, *(ushort *)(mem+addr)); sc->status=DISABLED; hidewin(sc); return 0; } } DPRINT2(DB_INFO,"dgb%d: FEP code loaded\n",unit); *(ushort *)(mem+setwin(sc,FEPSTAT))=0; addr=setwin(sc,MBOX); *(ushort *)(mem+addr+0)=1; *(ushort *)(mem+addr+2)=FEPCODESEG; *(ushort *)(mem+addr+4)=0x4; outb(sc->port,FEPINT); /* send interrupt to BIOS */ outb(sc->port,FEPCLR); addr=setwin(sc,FEPSTAT); for(i=0; *(ushort *)(mem+addr)!= *(ushort *)"OS"; i++) { if(i>200000) { printf("dgb%d: FEP/OS start failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } } DPRINT2(DB_INFO,"dgb%d: FEP/OS started\n",dev->id_unit); sc->numports= *(ushort *)(mem+setwin(sc,NPORT)); printf("dgb%d: %d ports\n",unit,sc->numports); if(sc->numports > MAX_DGB_PORTS) { printf("dgb%d: too many ports\n",unit); sc->status=DISABLED; hidewin(sc); return 0; } if(nports+sc->numports>NDGBPORTS) { printf("dgb%d: only %d ports are usable\n", unit, NDGBPORTS-nports); sc->numports=NDGBPORTS-nports; } /* allocate port and tty structures */ sc->ports=&dgb_ports[nports]; sc->ttys=&dgb_tty[nports]; nports+=sc->numports; addr=setwin(sc,PORTBASE); pstat=(ushort *)(mem+addr); for(i=0; inumports && pstat[i]; i++) if(pstat[i]) sc->ports[i].status=ENABLED; else { sc->ports[i].status=DISABLED; printf("dgb%d: port%d is broken\n", unit, i); } /* We should now init per-port structures */ bc=(volatile struct board_chan *)(mem + CHANSTRUCT); sc->mailbox=(volatile struct global_data *)(mem + FEP_GLOBAL); if(sc->numports<3) shrinkmem=1; else shrinkmem=0; for(i=0; inumports; i++, bc++) { port= &sc->ports[i]; port->tty=&sc->ttys[i]; port->unit=unit; port->brdchan=bc; if(sc->altpin) { port->dsr=CD; port->dcd=DSR; } else { port->dcd=CD; port->dsr=DSR; } port->pnum=i; if(shrinkmem) { DPRINT2(DB_INFO,"dgb%d: shrinking memory\n",unit); fepcmd(port, SETBUFFER, 32, 0, 0, 0); shrinkmem=0; } if(sc->type!=PCXEVE) { port->txptr=mem+((bc->tseg-sc->mem_seg)<<4); port->rxptr=mem+((bc->rseg-sc->mem_seg)<<4); port->txwin=port->rxwin=0; } else { port->txptr=mem+( ((bc->tseg-sc->mem_seg)<<4) & 0x1FFF ); port->rxptr=mem+( ((bc->rseg-sc->mem_seg)<<4) & 0x1FFF ); port->txwin=FEPWIN | ((bc->tseg-sc->mem_seg)>>9); port->rxwin=FEPWIN | ((bc->rseg-sc->mem_seg)>>9); } port->txbufhead=0; port->rxbufhead=0; port->txbufsize=bc->tmax+1; port->rxbufsize=bc->rmax+1; lowwater= (port->txbufsize>=2000) ? 1024 : (port->txbufsize/2); setwin(sc,0); fepcmd(port, STXLWATER, lowwater, 0, 10, 0); fepcmd(port, SRXLWATER, port->rxbufsize/4, 0, 10, 0); fepcmd(port, SRXHWATER, 3*port->rxbufsize/4, 0, 10, 0); bc->edelay=100; bc->idata=1; port->startc=bc->startc; port->startca=bc->startca; port->stopc=bc->stopc; port->stopca=bc->stopca; /*port->close_delay=50;*/ port->close_delay=3 * hz; port->do_timestamp=0; port->do_dcd_timestamp=0; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ port->it_in.c_iflag = TTYDEF_IFLAG; port->it_in.c_oflag = TTYDEF_OFLAG; port->it_in.c_cflag = TTYDEF_CFLAG; port->it_in.c_lflag = TTYDEF_LFLAG; termioschars(&port->it_in); port->it_in.c_ispeed = port->it_in.c_ospeed = dgbdefaultrate; port->it_out = port->it_in; #ifdef DEVFS /* MAX_DGB_PORTS is 32 => [0-9a-v] */ suffix = i < 10 ? '0' + i : 'a' + i - 10; port->devfs_token.tty = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyD%d%c", unit, suffix); port->devfs_token.ttyi = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i+32, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyiD%d%c", unit, suffix); port->devfs_token.ttyl = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i+64, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttylD%d%c", unit, suffix); port->devfs_token.cua = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i+128, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaD%d%c", unit, suffix); port->devfs_token.cuai = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i+160, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaiD%d%c", unit, suffix); port->devfs_token.cual = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i+192, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cualD%d%c", unit, suffix); #endif } hidewin(sc); /* register the polling function */ timeout(dgbpoll, (void *)unit, hz/POLLSPERSEC); return 1; } /* ARGSUSED */ static int dgbopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct dgb_softc *sc; struct tty *tp; int unit; int mynor; int pnum; struct dgb_p *port; int s,cs; int error; volatile struct board_chan *bc; error=0; mynor=minor(dev); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); if(unit >= NDGB) { DPRINT2(DB_EXCEPT,"dgb%d: try to open a nonexisting card\n",unit); return ENXIO; } sc=&dgb_softc[unit]; if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dgb%d: try to open a disabled card\n",unit); return ENXIO; } if(pnum>=sc->numports) { DPRINT3(DB_EXCEPT,"dgb%d: try to open non-existing port %d\n",unit,pnum); return ENXIO; } if(mynor & CONTROL_MASK) return 0; tp=&sc->ttys[pnum]; port=&sc->ports[pnum]; bc=port->brdchan; open_top: s=spltty(); while(port->closing) { error=tsleep(&port->closing, TTOPRI|PCATCH, "dgocl", 0); if(error) { DPRINT4(DB_OPEN,"dgb%d: port%d: tsleep(dgocl) error=%d\n",unit,pnum,error); goto out; } } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!port->active_out) { error = EBUSY; DPRINT4(DB_OPEN,"dgb%d: port%d: BUSY error=%d\n",unit,pnum,error); goto out; } } else { if (port->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; DPRINT4(DB_OPEN,"dgb%d: port%d: BUSY error=%d\n",unit,pnum,error); goto out; } error = tsleep(&port->active_out, TTIPRI | PCATCH, "dgbi", 0); if (error != 0) { DPRINT4(DB_OPEN,"dgb%d: port%d: tsleep(dgbi) error=%d\n", unit,pnum,error); goto out; } splx(s); goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc=dgbstart; tp->t_param=dgbparam; tp->t_dev=dev; tp->t_termios= (mynor & CALLOUT_MASK) ? port->it_out : port->it_in; cs=splclock(); setwin(sc,0); port->imodem=bc->mstat; bc->rout=bc->rin; /* clear input queue */ bc->idata=1; #ifdef PRINT_BUFSIZE printf("dgb buffers tx=%x:%x rx=%x:%x\n",bc->tseg,bc->tmax,bc->rseg,bc->rmax); #endif hidewin(sc); splx(cs); port->wopeners++; error=dgbparam(tp, &tp->t_termios); port->wopeners--; if(error!=0) { DPRINT4(DB_OPEN,"dgb%d: port%d: dgbparam error=%d\n",unit,pnum,error); goto out; } - ttsetwater(tp); - /* handle fake DCD for callout devices */ /* and initial DCD */ if( (port->imodem & port->dcd) || mynor & CALLOUT_MASK ) linesw[tp->t_line].l_modem(tp,1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++port->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "dgdcd", 0); --port->wopeners; if (error != 0) { DPRINT4(DB_OPEN,"dgb%d: port%d: tsleep(dgdcd) error=%d\n",unit,pnum,error); goto out; } splx(s); goto open_top; } error = linesw[tp->t_line].l_open(dev, tp); disc_optim(tp,&tp->t_termios); DPRINT4(DB_OPEN,"dgb%d: port%d: l_open error=%d\n",unit,pnum,error); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) port->active_out = TRUE; port->used=1; /* If any port is open (i.e. the open() call is completed for it) * the device is busy */ out: disc_optim(tp,&tp->t_termios); splx(s); if( !(tp->t_state & TS_ISOPEN) && port->wopeners==0 ) dgbhardclose(port); DPRINT4(DB_OPEN,"dgb%d: port%d: open() returns %d\n",unit,pnum,error); return error; } /*ARGSUSED*/ static int dgbclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { int mynor; struct tty *tp; int unit, pnum; struct dgb_softc *sc; struct dgb_p *port; int s; int i; mynor=minor(dev); if(mynor & CONTROL_MASK) return 0; unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); sc=&dgb_softc[unit]; tp=&sc->ttys[pnum]; port=sc->ports+pnum; DPRINT3(DB_CLOSE,"dgb%d: port%d: closing\n",unit,pnum); DPRINT3(DB_CLOSE,"dgb%d: port%d: draining port\n",unit,pnum); dgb_drain_or_flush(port); s=spltty(); port->closing=1; DPRINT3(DB_CLOSE,"dgb%d: port%d: closing line disc\n",unit,pnum); linesw[tp->t_line].l_close(tp,flag); disc_optim(tp,&tp->t_termios); DPRINT3(DB_CLOSE,"dgb%d: port%d: hard closing\n",unit,pnum); dgbhardclose(port); DPRINT3(DB_CLOSE,"dgb%d: port%d: closing tty\n",unit,pnum); ttyclose(tp); port->closing=0; wakeup(&port->closing); port->used=0; /* mark the card idle when all ports are closed */ for(i=0; inumports; i++) if(sc->ports[i].used) break; splx(s); DPRINT3(DB_CLOSE,"dgb%d: port%d: closed\n",unit,pnum); wakeup(TSA_CARR_ON(tp)); wakeup(&port->active_out); port->active_out=0; DPRINT3(DB_CLOSE,"dgb%d: port%d: close exit\n",unit,pnum); return 0; } static void dgbhardclose(port) struct dgb_p *port; { struct dgb_softc *sc=&dgb_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int cs; cs=splclock(); port->do_timestamp = 0; setwin(sc,0); bc->idata=0; bc->iempty=0; bc->ilow=0; if(port->tty->t_cflag & HUPCL) { port->omodem &= ~(RTS|DTR); fepcmd(port, SETMODEM, 0, DTR|RTS, 0, 1); } hidewin(sc); splx(cs); timeout(dgb_pause, &port->brdchan, hz/2); tsleep(&port->brdchan, TTIPRI | PCATCH, "dgclo", 0); } static void dgb_pause(chan) void *chan; { wakeup((caddr_t)chan); } static int dgbread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int error, unit, pnum; mynor=minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); tp=&dgb_softc[unit].ttys[pnum]; error=linesw[tp->t_line].l_read(tp, uio, flag); DPRINT4(DB_RD,"dgb%d: port%d: read() returns %d\n",unit,pnum,error); return error; } static int dgbwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int error, unit, pnum; mynor=minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); tp=&dgb_softc[unit].ttys[pnum]; error=linesw[tp->t_line].l_write(tp, uio, flag); DPRINT4(DB_WR,"dgb%d: port%d: write() returns %d\n",unit,pnum,error); return error; } static void dgbpoll(unit_c) void *unit_c; { int unit=(int)unit_c; int pnum; struct dgb_p *port; struct dgb_softc *sc=&dgb_softc[unit]; int head, tail; u_char *eventbuf; int event, mstat, lstat; volatile struct board_chan *bc; struct tty *tp; int rhead, rtail; int whead, wtail; int size; int c=0; u_char *ptr; int ocount; int ibuf_full,obuf_full; BoardMemWinState ws=bmws_get(); if(sc->status==DISABLED) { printf("dgb%d: polling of disabled board stopped\n",unit); return; } setwin(sc,0); head=sc->mailbox->ein; tail=sc->mailbox->eout; while(head!=tail) { if(head >= FEP_IMAX-FEP_ISTART || tail >= FEP_IMAX-FEP_ISTART || (head|tail) & 03 ) { printf("dgb%d: event queue's head or tail is wrong! hd=%d,tl=%d\n", unit,head,tail); break; } eventbuf=sc->vmem+tail+FEP_ISTART; pnum=eventbuf[0]; event=eventbuf[1]; mstat=eventbuf[2]; lstat=eventbuf[3]; port=&sc->ports[pnum]; bc=port->brdchan; tp=&sc->ttys[pnum]; if(pnum>=sc->numports || port->status==DISABLED) { printf("dgb%d: port%d: got event on nonexisting port\n",unit,pnum); } else if(port->used || port->wopeners>0 ) { int wrapmask=port->rxbufsize-1; if( !(event & ALL_IND) ) printf("dgb%d: port%d: ? event 0x%x mstat 0x%x lstat 0x%x\n", unit, pnum, event, mstat, lstat); if(event & DATA_IND) { DPRINT3(DB_DATA,"dgb%d: port%d: DATA_IND\n",unit,pnum); rhead=bc->rin & wrapmask; rtail=bc->rout & wrapmask; if( !(tp->t_cflag & CREAD) || !port->used ) { bc->rout=rhead; goto end_of_data; } if(bc->orun) { printf("dgb%d: port%d: overrun\n", unit, pnum); bc->orun=0; } if(!(tp->t_state & TS_ISOPEN)) goto end_of_data; for(ibuf_full=FALSE;rhead!=rtail && !ibuf_full;) { DPRINT5(DB_RXDATA,"dgb%d: port%d: p rx head=%d tail=%d\n", unit,pnum,rhead,rtail); if(rhead>rtail) size=rhead-rtail; else size=port->rxbufsize-rtail; ptr=port->rxptr+rtail; /* Helg: */ if( tp->t_rawq.c_cc + size > DGB_IBUFSIZE ) { size=DGB_IBUFSIZE-tp->t_rawq.c_cc; DPRINT1(DB_RXDATA,"*"); ibuf_full=TRUE; } if(size) { if (tp->t_state & TS_CAN_BYPASS_L_RINT) { DPRINT1(DB_RXDATA,"!"); towin(sc,port->rxwin); tk_nin += size; tk_rawcc += size; tp->t_rawcc += size; b_to_q(ptr,size,&tp->t_rawq); setwin(sc,0); } else { int i=size; unsigned char chr; do { towin(sc,port->rxwin); chr= *ptr++; hidewin(sc); (*linesw[tp->t_line].l_rint)(chr, tp); } while (--i > 0 ); setwin(sc,0); } } rtail= (rtail + size) & wrapmask; bc->rout=rtail; rhead=bc->rin & wrapmask; hidewin(sc); ttwakeup(tp); setwin(sc,0); } end_of_data: ; } if(event & MODEMCHG_IND) { DPRINT3(DB_MODEM,"dgb%d: port%d: MODEMCHG_IND\n",unit,pnum); port->imodem=mstat; if(mstat & port->dcd) { hidewin(sc); linesw[tp->t_line].l_modem(tp,1); setwin(sc,0); wakeup(TSA_CARR_ON(tp)); } else { hidewin(sc); linesw[tp->t_line].l_modem(tp,0); setwin(sc,0); if( port->draining) { port->draining=0; wakeup(&port->draining); } } } if(event & BREAK_IND) { if((tp->t_state & TS_ISOPEN) && (tp->t_iflag & IGNBRK)) { DPRINT3(DB_BREAK,"dgb%d: port%d: BREAK_IND\n",unit,pnum); hidewin(sc); linesw[tp->t_line].l_rint(TTY_BI, tp); setwin(sc,0); } } /* Helg: with output flow control */ if(event & (LOWTX_IND | EMPTYTX_IND) ) { DPRINT3(DB_TXDATA,"dgb%d: port%d: LOWTX_IND or EMPTYTX_IND\n",unit,pnum); if( (event & EMPTYTX_IND ) && tp->t_outq.c_cc==0 && port->draining) { port->draining=0; wakeup(&port->draining); bc->ilow=0; bc->iempty=0; } else { int wrapmask=port->txbufsize-1; for(obuf_full=FALSE; tp->t_outq.c_cc!=0 && !obuf_full; ) { int s; /* add "last-minute" data to write buffer */ if(!(tp->t_state & TS_BUSY)) { hidewin(sc); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ ttwwakeup(tp); #else if(tp->t_outq.c_cc <= tp->t_lowat) { if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } /* selwakeup(&tp->t_wsel); */ } #endif setwin(sc,0); } s=spltty(); whead=bc->tin & wrapmask; wtail=bc->tout & wrapmask; if(wheadtxbufsize-whead; if(wtail==0) size--; } if(size==0) { DPRINT5(DB_WR,"dgb: head=%d tail=%d size=%d full=%d\n", whead,wtail,size,obuf_full); bc->iempty=1; bc->ilow=1; obuf_full=TRUE; splx(s); break; } towin(sc,port->txwin); ocount=q_to_b(&tp->t_outq, port->txptr+whead, size); whead+=ocount; setwin(sc,0); bc->tin=whead; bc->tin=whead & wrapmask; splx(s); } if(obuf_full) { DPRINT1(DB_WR," +BUSY\n"); tp->t_state|=TS_BUSY; } else { DPRINT1(DB_WR," -BUSY\n"); hidewin(sc); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ /* should clear TS_BUSY before ttwwakeup */ if(tp->t_state & TS_BUSY) { tp->t_state &= ~TS_BUSY; linesw[tp->t_line].l_start(tp); ttwwakeup(tp); } #else if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } tp->t_state &= ~TS_BUSY; #endif setwin(sc,0); } } end_of_buffer: ; } bc->idata=1; /* require event on incoming data */ } else { bc=port->brdchan; DPRINT4(DB_EXCEPT,"dgb%d: port%d: got event 0x%x on closed port\n", unit,pnum,event); bc->rout=bc->rin; bc->idata=bc->iempty=bc->ilow=0; } tail= (tail+4) & (FEP_IMAX-FEP_ISTART-4); } sc->mailbox->eout=tail; bmws_set(ws); timeout(dgbpoll, unit_c, hz/POLLSPERSEC); } static int dgbioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct dgb_softc *sc; int unit, pnum; struct dgb_p *port; int mynor; struct tty *tp; volatile struct board_chan *bc; int error; int s,cs; int tiocm_xxx; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) u_long oldcmd; struct termios term; #endif BoardMemWinState ws=bmws_get(); mynor=minor(dev); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); sc=&dgb_softc[unit]; port=&sc->ports[pnum]; tp=&sc->ttys[pnum]; bc=port->brdchan; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &port->it_out : &port->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { DPRINT6(DB_PARAM,"dgb%d: port%d: dgbioctl-ISNOW c=0x%x i=0x%x l=0x%x\n",unit,pnum,term.c_cflag,term.c_iflag,term.c_lflag); } oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in; DPRINT6(DB_PARAM,"dgb%d: port%d: dgbioctl-TOSET c=0x%x i=0x%x l=0x%x\n",unit,pnum,dt->c_cflag,dt->c_iflag,dt->c_lflag); dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } if(cmd==TIOCSTOP) { cs=splclock(); setwin(sc,0); fepcmd(port, PAUSETX, 0, 0, 0, 0); bmws_set(ws); splx(cs); return 0; } else if(cmd==TIOCSTART) { cs=splclock(); setwin(sc,0); fepcmd(port, RESUMETX, 0, 0, 0, 0); bmws_set(ws); splx(cs); return 0; } if(cmd==TIOCSETAW || cmd==TIOCSETAF) port->mustdrain=1; error = linesw[tp->t_line].l_ioctl(tp, cmd, data, flag, p); if (error != ENOIOCTL) return error; s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp,&tp->t_termios); port->mustdrain=0; if (error != ENOIOCTL) { splx(s); if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { DPRINT6(DB_PARAM,"dgb%d: port%d: dgbioctl-RES c=0x%x i=0x%x l=0x%x\n",unit,pnum,tp->t_cflag,tp->t_iflag,tp->t_lflag); } return error; } switch (cmd) { case TIOCSBRK: /* Helg: commented */ /* error=dgbdrain(port);*/ if(error!=0) { splx(s); return error; } cs=splclock(); setwin(sc,0); /* now it sends 250 millisecond break because I don't know */ /* how to send an infinite break */ fepcmd(port, SENDBREAK, 250, 0, 10, 0); hidewin(sc); splx(cs); break; case TIOCCBRK: /* now it's empty */ break; case TIOCSDTR: DPRINT3(DB_MODEM,"dgb%d: port%d: set DTR\n",unit,pnum); port->omodem |= DTR; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS, 0, 1); if( !(bc->mstat & DTR) ) { DPRINT3(DB_MODEM,"dgb%d: port%d: DTR is off\n",unit,pnum); } hidewin(sc); splx(cs); break; case TIOCCDTR: DPRINT3(DB_MODEM,"dgb%d: port%d: reset DTR\n",unit,pnum); port->omodem &= ~DTR; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); if( bc->mstat & DTR ) { DPRINT3(DB_MODEM,"dgb%d: port%d: DTR is on\n",unit,pnum); } hidewin(sc); splx(cs); break; case TIOCMSET: if(*(int *)data & TIOCM_DTR) port->omodem |=DTR; else port->omodem &=~DTR; if(*(int *)data & TIOCM_RTS) port->omodem |=RTS; else port->omodem &=~RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMBIS: if(*(int *)data & TIOCM_DTR) port->omodem |=DTR; if(*(int *)data & TIOCM_RTS) port->omodem |=RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMBIC: if(*(int *)data & TIOCM_DTR) port->omodem &=~DTR; if(*(int *)data & TIOCM_RTS) port->omodem &=~RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMGET: setwin(sc,0); port->imodem=bc->mstat; hidewin(sc); tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */ DPRINT3(DB_MODEM,"dgb%d: port%d: modem stat -- ",unit,pnum); if (port->imodem & DTR) { DPRINT1(DB_MODEM,"DTR "); tiocm_xxx |= TIOCM_DTR; } if (port->imodem & RTS) { DPRINT1(DB_MODEM,"RTS "); tiocm_xxx |= TIOCM_RTS; } if (port->imodem & CTS) { DPRINT1(DB_MODEM,"CTS "); tiocm_xxx |= TIOCM_CTS; } if (port->imodem & port->dcd) { DPRINT1(DB_MODEM,"DCD "); tiocm_xxx |= TIOCM_CD; } if (port->imodem & port->dsr) { DPRINT1(DB_MODEM,"DSR "); tiocm_xxx |= TIOCM_DSR; } if (port->imodem & RI) { DPRINT1(DB_MODEM,"RI "); tiocm_xxx |= TIOCM_RI; } *(int *)data = tiocm_xxx; DPRINT1(DB_MODEM,"--\n"); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } port->close_delay = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = port->close_delay * 100 / hz; break; case TIOCTIMESTAMP: port->do_timestamp = TRUE; *(struct timeval *)data = port->timestamp; break; case TIOCDCDTIMESTAMP: port->do_dcd_timestamp = TRUE; *(struct timeval *)data = port->dcd_timestamp; break; default: bmws_set(ws); splx(s); return ENOTTY; } bmws_set(ws); splx(s); return 0; } static void wakeflush(p) void *p; { struct dgb_p *port=p; wakeup(&port->draining); } /* wait for the output to drain */ static int dgbdrain(port) struct dgb_p *port; { struct dgb_softc *sc=&dgb_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int error; int head, tail; BoardMemWinState ws=bmws_get(); setwin(sc,0); bc->iempty=1; tail=bc->tout; head=bc->tin; while(tail!=head) { DPRINT5(DB_WR,"dgb%d: port%d: drain: head=%d tail=%d\n", port->unit, port->pnum, head, tail); hidewin(sc); port->draining=1; timeout(wakeflush,port, hz); error=tsleep(&port->draining, TTIPRI | PCATCH, "dgdrn", 0); port->draining=0; setwin(sc,0); if (error != 0) { DPRINT4(DB_WR,"dgb%d: port%d: tsleep(dgdrn) error=%d\n", port->unit,port->pnum,error); bc->iempty=0; bmws_set(ws); return error; } tail=bc->tout; head=bc->tin; } DPRINT5(DB_WR,"dgb%d: port%d: drain: head=%d tail=%d\n", port->unit, port->pnum, head, tail); bmws_set(ws); return 0; } /* wait for the output to drain */ /* or simply clear the buffer it it's stopped */ static void dgb_drain_or_flush(port) struct dgb_p *port; { struct tty *tp=port->tty; struct dgb_softc *sc=&dgb_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int error; int lasttail; int head, tail; setwin(sc,0); lasttail=-1; bc->iempty=1; tail=bc->tout; head=bc->tin; while(tail!=head /* && tail!=lasttail */ ) { DPRINT5(DB_WR,"dgb%d: port%d: flush: head=%d tail=%d\n", port->unit, port->pnum, head, tail); /* if there is no carrier simply clean the buffer */ if( !(tp->t_state & TS_CARR_ON) ) { bc->tout=bc->tin=0; bc->iempty=0; hidewin(sc); return; } hidewin(sc); port->draining=1; timeout(wakeflush,port, hz); error=tsleep(&port->draining, TTIPRI | PCATCH, "dgfls", 0); port->draining=0; setwin(sc,0); if (error != 0) { DPRINT4(DB_WR,"dgb%d: port%d: tsleep(dgfls) error=%d\n", port->unit,port->pnum,error); /* silently clean the buffer */ bc->tout=bc->tin=0; bc->iempty=0; hidewin(sc); return; } lasttail=tail; tail=bc->tout; head=bc->tin; } hidewin(sc); DPRINT5(DB_WR,"dgb%d: port%d: flush: head=%d tail=%d\n", port->unit, port->pnum, head, tail); } static int dgbparam(tp, t) struct tty *tp; struct termios *t; { int dev=tp->t_dev; int mynor=minor(dev); int unit=MINOR_TO_UNIT(dev); int pnum=MINOR_TO_PORT(dev); struct dgb_softc *sc=&dgb_softc[unit]; struct dgb_p *port=&sc->ports[pnum]; volatile struct board_chan *bc=port->brdchan; int cflag; int head; int mval; int iflag; int hflow; int s,cs; BoardMemWinState ws=bmws_get(); DPRINT6(DB_PARAM,"dgb%d: port%d: dgbparm c=0x%x i=0x%x l=0x%x\n",unit,pnum,t->c_cflag,t->c_iflag,t->c_lflag); if(port->mustdrain) { DPRINT3(DB_PARAM,"dgb%d: port%d: must call dgbdrain()\n",unit,pnum); dgbdrain(port); } cflag=ttspeedtab(t->c_ospeed, dgbspeedtab); if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; if (cflag < 0 /* || cflag > 0 && t->c_ispeed != t->c_ospeed */) { DPRINT4(DB_PARAM,"dgb%d: port%d: invalid cflag=0%o\n",unit,pnum,cflag); return (EINVAL); } cs=splclock(); setwin(sc,0); if(cflag==0) { /* hangup */ DPRINT3(DB_PARAM,"dgb%d: port%d: hangup\n",unit,pnum); head=bc->rin; bc->rout=head; head=bc->tin; fepcmd(port, STOUT, (unsigned)head, 0, 0, 0); mval= port->omodem & ~(DTR|RTS); } else { cflag |= dgbflags(dgb_cflags, t->c_cflag); if(cflag!=port->fepcflag) { port->fepcflag=cflag; DPRINT5(DB_PARAM,"dgb%d: port%d: set cflag=0x%x c=0x%x\n", unit,pnum,cflag,t->c_cflag&~CRTSCTS); fepcmd(port, SETCTRLFLAGS, (unsigned)cflag, 0, 0, 0); } mval= port->omodem | (DTR|RTS); } iflag=dgbflags(dgb_iflags, t->c_iflag); if(iflag!=port->fepiflag) { port->fepiflag=iflag; DPRINT5(DB_PARAM,"dgb%d: port%d: set iflag=0x%x c=0x%x\n",unit,pnum,iflag,t->c_iflag); fepcmd(port, SETIFLAGS, (unsigned)iflag, 0, 0, 0); } bc->mint=port->dcd; hflow=dgbflags(dgb_flow, t->c_cflag); if(hflow!=port->hflow) { port->hflow=hflow; DPRINT5(DB_PARAM,"dgb%d: port%d: set hflow=0x%x f=0x%x\n",unit,pnum,hflow,t->c_cflag&CRTSCTS); fepcmd(port, SETHFLOW, (unsigned)hflow, 0xff, 0, 1); } if(port->omodem != mval) { DPRINT5(DB_PARAM,"dgb%d: port%d: setting modem parameters 0x%x was 0x%x\n", unit,pnum,mval,port->omodem); port->omodem=mval; fepcmd(port, SETMODEM, (unsigned)mval, RTS|DTR, 0, 1); } if(port->fepstartc!=t->c_cc[VSTART] || port->fepstopc!=t->c_cc[VSTOP]) { DPRINT5(DB_PARAM,"dgb%d: port%d: set startc=%d, stopc=%d\n",unit,pnum,t->c_cc[VSTART],t->c_cc[VSTOP]); port->fepstartc=t->c_cc[VSTART]; port->fepstopc=t->c_cc[VSTOP]; fepcmd(port, SONOFFC, port->fepstartc, port->fepstopc, 0, 1); } bmws_set(ws); splx(cs); return 0; } static void dgbstart(tp) struct tty *tp; { int unit; int pnum; struct dgb_p *port; struct dgb_softc *sc; volatile struct board_chan *bc; int head, tail; int size, ocount; int s; int wmask; BoardMemWinState ws=bmws_get(); unit=MINOR_TO_UNIT(minor(tp->t_dev)); pnum=MINOR_TO_PORT(minor(tp->t_dev)); sc=&dgb_softc[unit]; port=&sc->ports[pnum]; bc=port->brdchan; wmask=port->txbufsize-1; s=spltty(); while( tp->t_outq.c_cc!=0 ) { int cs; #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ ttwwakeup(tp); #else if(tp->t_outq.c_cc <= tp->t_lowat) { if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } /*selwakeup(&tp->t_wsel);*/ } #endif cs=splclock(); setwin(sc,0); head=bc->tin & wmask; do { tail=bc->tout; } while (tail != bc->tout); tail=bc->tout & wmask; DPRINT5(DB_WR,"dgb%d: port%d: s tx head=%d tail=%d\n",unit,pnum,head,tail); #ifdef LEAVE_FREE_CHARS if(tail>head) { size=tail-head-LEAVE_FREE_CHARS; if (size <0) size==0; } else { size=port->txbufsize-head; if(tail+port->txbufsize < head) size==0; } } #else if(tail>head) size=tail-head-1; else { size=port->txbufsize-head/*-1*/; if(tail==0) size--; } #endif if(size==0) { bc->iempty=1; bc->ilow=1; splx(cs); bmws_set(ws); tp->t_state|=TS_BUSY; splx(s); return; } towin(sc,port->txwin); ocount=q_to_b(&tp->t_outq, port->txptr+head, size); head+=ocount; if(head>=port->txbufsize) head-=port->txbufsize; setwin(sc,0); bc->tin=head; DPRINT5(DB_WR,"dgb%d: port%d: tx avail=%d count=%d\n",unit,pnum,size,ocount); hidewin(sc); splx(cs); } bmws_set(ws); splx(s); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ if(tp->t_state & TS_BUSY) { tp->t_state&=~TS_BUSY; linesw[tp->t_line].l_start(tp); ttwwakeup(tp); } #else if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } tp->t_state&=~TS_BUSY; #endif } void dgbstop(tp, rw) struct tty *tp; int rw; { int unit; int pnum; struct dgb_p *port; struct dgb_softc *sc; volatile struct board_chan *bc; int head; int s; BoardMemWinState ws=bmws_get(); unit=MINOR_TO_UNIT(minor(tp->t_dev)); pnum=MINOR_TO_PORT(minor(tp->t_dev)); sc=&dgb_softc[unit]; port=&sc->ports[pnum]; bc=port->brdchan; DPRINT3(DB_WR,"dgb%d: port%d: stop\n",port->unit, port->pnum); s = spltty(); setwin(sc,0); if (rw & FWRITE) { /* clear output queue */ bc->tout=bc->tin=0; bc->ilow=0;bc->iempty=0; } if (rw & FREAD) { /* clear input queue */ bc->rout=bc->rin; bc->idata=1; } hidewin(sc); bmws_set(ws); splx(s); dgbstart(tp); } struct tty * dgbdevtotty(dev) dev_t dev; { int mynor, pnum, unit; struct dgb_softc *sc; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NDGB) return (NULL); pnum = MINOR_TO_PORT(mynor); sc = &dgb_softc[unit]; if (pnum >= sc->numports) return (NULL); return (&sc->ttys[pnum]); } static void fepcmd(port, cmd, op1, op2, ncmds, bytecmd) struct dgb_p *port; unsigned cmd, op1, op2, ncmds, bytecmd; { struct dgb_softc *sc=&dgb_softc[port->unit]; u_char *mem=sc->vmem; unsigned tail, head; int count, n; if(port->status==DISABLED) { printf("dgb%d: port%d: FEP command on disabled port\n", port->unit, port->pnum); return; } /* setwin(sc,0); Require this to be set by caller */ head=sc->mailbox->cin; if(head>=(FEP_CMAX-FEP_CSTART) || (head & 3)) { printf("dgb%d: port%d: wrong pointer head of command queue : 0x%x\n", port->unit, port->pnum, head); return; } mem[head+FEP_CSTART+0]=cmd; mem[head+FEP_CSTART+1]=port->pnum; if(bytecmd) { mem[head+FEP_CSTART+2]=op1; mem[head+FEP_CSTART+3]=op2; } else { mem[head+FEP_CSTART+2]=op1&0xff; mem[head+FEP_CSTART+3]=(op1>>8)&0xff; } DPRINT7(DB_FEP,"dgb%d: port%d: %s cmd=0x%x op1=0x%x op2=0x%x\n", port->unit, port->pnum, (bytecmd)?"byte":"word", cmd, mem[head+FEP_CSTART+2], mem[head+FEP_CSTART+3]); head=(head+4) & (FEP_CMAX-FEP_CSTART-4); sc->mailbox->cin=head; count=FEPTIMEOUT; while (count-- != 0) { head=sc->mailbox->cin; tail=sc->mailbox->cout; n = (head-tail) & (FEP_CMAX-FEP_CSTART-4); if(n <= ncmds * (sizeof(ushort)*4)) return; } printf("dgb%d(%d): timeout on FEP cmd=0x%x\n", port->unit, port->pnum, cmd); } static void disc_optim(tp, t) struct tty *tp; struct termios *t; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; } static dgb_devsw_installed = 0; static void dgb_drvinit(void *unused) { dev_t dev; if( ! dgb_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&dgb_cdevsw, NULL); dgb_devsw_installed = 1; } } SYSINIT(dgbdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,dgb_drvinit,NULL) #endif /* NDGB > 0 */ diff --git a/sys/dev/dgb/dgm.c b/sys/dev/dgb/dgm.c index 487b1aa73847..eca931ae8639 100644 --- a/sys/dev/dgb/dgm.c +++ b/sys/dev/dgb/dgm.c @@ -1,2120 +1,2121 @@ /*- - * $Id: dgm.c,v 1.3 1998/08/12 17:38:09 bde Exp $ + * $Id: dgm.c,v 1.4 1998/08/16 01:21:49 bde Exp $ * * This driver and the associated header files support the ISA PC/Xem * Digiboards. Its evolutionary roots are described below. * Jack O'Neill * * Digiboard driver. * * Stage 1. "Better than nothing". * Stage 2. "Gee, it works!". * * Based on sio driver by Bruce Evans and on Linux driver by Troy * De Jongh or * which is under GNU General Public License version 2 so this driver * is forced to be under GPL 2 too. * * Written by Serge Babkin, * Joint Stock Commercial Bank "Chelindbank" * (Chelyabinsk, Russia) * babkin@hq.icb.chel.su * * Assorted hacks to make it more functional and working under 3.0-current. * Fixed broken routines to prevent processes hanging on closed (thanks * to Bruce for his patience and assistance). Thanks also to Maxim Bolotin * for his patches which did most of the work to get this * running under 2.2/3.0-current. * Implemented ioctls: TIOCMSDTRWAIT, TIOCMGDTRWAIT, TIOCTIMESTAMP & * TIOCDCDTIMESTAMP. * Sysctl debug flag is now a bitflag, to filter noise during debugging. * David L. Nugent */ #include "opt_compat.h" #include "opt_devfs.h" #include "dgm.h" #if NDGM > 0 /* Helg: i.e.25 times per sec board will be polled */ #define POLLSPERSEC 25 /* How many charactes can we write to input tty rawq */ #define DGB_IBUFSIZE (TTYHOG-100) /* the overall number of ports controlled by this driver */ #ifndef NDGMPORTS # define NDGMPORTS (NDGM*64) #endif #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #include #define CALLOUT_MASK 0x40000 #define CONTROL_MASK 0xC0 #define CONTROL_INIT_STATE 0x40 #define CONTROL_LOCK_STATE 0x80 #define UNIT_MASK 0x30000 #define PORT_MASK 0x3F #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) (((mynor) & UNIT_MASK)>>16) #define MINOR_TO_PORT(mynor) ((mynor) & PORT_MASK) /* types. XXX - should be elsewhere */ typedef u_char bool_t; /* boolean */ /* digiboard port structure */ struct dgm_p { bool_t status; u_char unit; /* board unit number */ u_char pnum; /* port number */ u_char omodem; /* FEP output modem status */ u_char imodem; /* FEP input modem status */ u_char modemfake; /* Modem values to be forced */ u_char modem; /* Force values */ u_char hflow; u_char dsr; u_char dcd; u_char stopc; u_char startc; u_char stopca; u_char startca; u_char fepstopc; u_char fepstartc; u_char fepstopca; u_char fepstartca; u_char txwin; u_char rxwin; ushort fepiflag; ushort fepcflag; ushort fepoflag; ushort txbufhead; ushort txbufsize; ushort rxbufhead; ushort rxbufsize; int close_delay; int count; int blocked_open; int event; int asyncflags; u_long statusflags; u_char *txptr; u_char *rxptr; volatile struct board_chan *brdchan; struct tty *tty; bool_t active_out; /* nonzero if the callout device is open */ u_int wopeners; /* # processes waiting for DCD in open() */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; /* flags of state, are used in sleep() too */ u_char closing; /* port is being closed now */ u_char draining; /* port is being drained now */ u_char used; /* port is being used now */ u_char mustdrain; /* data must be waited to drain in dgmparam() */ #ifdef DEVFS struct { void *tty; void *ttyi; void *ttyl; void *cua; void *cuai; void *cual; } devfs_token; #endif }; /* Digiboard per-board structure */ struct dgm_softc { /* struct board_info */ u_char status; /* status: DISABLED/ENABLED */ u_char unit; /* unit number */ u_char type; /* type of card: PCXE, PCXI, PCXEVE */ u_char altpin; /* do we need alternate pin setting ? */ int numports; /* number of ports on card */ int port; /* I/O port */ u_char *vmem; /* virtual memory address */ long pmem; /* physical memory address */ int mem_seg; /* internal memory segment */ struct dgm_p *ports; /* pointer to array of port descriptors */ struct tty *ttys; /* pointer to array of TTY structures */ volatile struct global_data *mailbox; }; static struct dgm_softc dgm_softc[NDGM]; static struct dgm_p dgm_ports[NDGMPORTS]; static struct tty dgm_tty[NDGMPORTS]; int fi(void); /* * The public functions in the com module ought to be declared in a com-driver * system header. */ /* Interrupt handling entry points. */ static void dgmpoll __P((void *unit_c)); /* Device switch entry points. */ #define dgmreset noreset #define dgmmmap nommap #define dgmstrategy nostrategy static int dgmattach __P((struct isa_device *dev)); static int dgmprobe __P((struct isa_device *dev)); static void fepcmd(struct dgm_p *port, unsigned cmd, unsigned op1, unsigned op2, unsigned ncmds, unsigned bytecmd); static void dgmstart __P((struct tty *tp)); static int dgmparam __P((struct tty *tp, struct termios *t)); static void dgmhardclose __P((struct dgm_p *port)); static void dgm_drain_or_flush __P((struct dgm_p *port)); static int dgmdrain __P((struct dgm_p *port)); static void dgm_pause __P((void *chan)); static void wakeflush __P((void *p)); static void disc_optim __P((struct tty *tp, struct termios *t)); struct isa_driver dgmdriver = { dgmprobe, dgmattach, "dgm",0 }; static d_open_t dgmopen; static d_close_t dgmclose; static d_read_t dgmread; static d_write_t dgmwrite; static d_ioctl_t dgmioctl; static d_stop_t dgmstop; static d_devtotty_t dgmdevtotty; -#define CDEV_MAJOR 101 -static struct cdevsw dgm_cdevsw = - { dgmopen, dgmclose, dgmread, dgmwrite, - dgmioctl, dgmstop, noreset, dgmdevtotty, /* dgm */ - ttpoll, nommap, NULL, "dgm", NULL, -1 }; +#define CDEV_MAJOR 101 +static struct cdevsw dgm_cdevsw = { + dgmopen, dgmclose, dgmread, dgmwrite, + dgmioctl, dgmstop, noreset, dgmdevtotty, + ttpoll, nommap, NULL, "dgm", + NULL, -1, nodump, nopsize, + D_TTY, +}; static speed_t dgmdefaultrate = TTYDEF_SPEED; static struct speedtab dgmspeedtab[] = { 0, FEP_B0, /* old (sysV-like) Bx codes */ 50, FEP_B50, 75, FEP_B75, 110, FEP_B110, 134, FEP_B134, 150, FEP_B150, 200, FEP_B200, 300, FEP_B300, 600, FEP_B600, 1200, FEP_B1200, 1800, FEP_B1800, 2400, FEP_B2400, 4800, FEP_B4800, 9600, FEP_B9600, 19200, FEP_B19200, 38400, FEP_B38400, 57600, (FEP_FASTBAUD|FEP_B50), /* B50 & fast baud table */ 115200, (FEP_FASTBAUD|FEP_B110), /* B100 & fast baud table */ -1, -1 }; static struct dbgflagtbl { tcflag_t in_mask; tcflag_t in_val; tcflag_t out_val; } dgm_cflags[] = { { PARODD, PARODD, FEP_PARODD }, { PARENB, PARENB, FEP_PARENB }, { CSTOPB, CSTOPB, FEP_CSTOPB }, { CSIZE, CS5, FEP_CS6 }, { CSIZE, CS6, FEP_CS6 }, { CSIZE, CS7, FEP_CS7 }, { CSIZE, CS8, FEP_CS8 }, { CLOCAL, CLOCAL, FEP_CLOCAL }, { (tcflag_t)-1 } }, dgm_iflags[] = { { IGNBRK, IGNBRK, FEP_IGNBRK }, { BRKINT, BRKINT, FEP_BRKINT }, { IGNPAR, IGNPAR, FEP_IGNPAR }, { PARMRK, PARMRK, FEP_PARMRK }, { INPCK, INPCK, FEP_INPCK }, { ISTRIP, ISTRIP, FEP_ISTRIP }, { IXON, IXON, FEP_IXON }, { IXOFF, IXOFF, FEP_IXOFF }, { IXANY, IXANY, FEP_IXANY }, { (tcflag_t)-1 } }, dgm_flow[] = { { CRTSCTS, CRTSCTS, CTS|RTS }, { CRTSCTS, CCTS_OFLOW, CTS }, { CRTSCTS, CRTS_IFLOW, RTS }, { (tcflag_t)-1 } }; /* xlat bsd termios flags to dgm sys-v style */ static tcflag_t dgmflags(struct dbgflagtbl *tbl, tcflag_t input) { tcflag_t output = 0; int i; for (i=0; tbl[i].in_mask != (tcflag_t)-1; i++) { if ((input & tbl[i].in_mask) == tbl[i].in_val) output |= tbl[i].out_val; } return output; } static int dgmdebug=0; SYSCTL_INT(_debug, OID_AUTO, dgm_debug, CTLFLAG_RW, &dgmdebug, 0, ""); static __inline int setwin __P((struct dgm_softc *sc, unsigned addr)); static __inline void hidewin __P((struct dgm_softc *sc)); static __inline void towin __P((struct dgm_softc *sc, int win)); /*Helg: to allow recursive dgm...() calls */ typedef struct { /* If we were called and don't want to disturb we need: */ int port; /* write to this port */ u_char data; /* this data on exit */ /* or DATA_WINOFF to close memory window on entry */ } BoardMemWinState; /* so several channels and even boards can coexist */ #define DATA_WINOFF 0 static BoardMemWinState bmws; /* return current memory window state and close window */ static BoardMemWinState bmws_get(void) { BoardMemWinState bmwsRet=bmws; if(bmws.data!=DATA_WINOFF) outb(bmws.port, bmws.data=DATA_WINOFF); return bmwsRet; } /* restore memory window state */ static void bmws_set(BoardMemWinState ws) { if(ws.data != bmws.data || ws.port!=bmws.port ) { if(bmws.data!=DATA_WINOFF) outb(bmws.port,DATA_WINOFF); if(ws.data!=DATA_WINOFF) outb(ws.port, ws.data); bmws=ws; } } static __inline int setwin(sc,addr) struct dgm_softc *sc; unsigned int addr; { outb(bmws.port=sc->port+1,bmws.data=FEPWIN|(addr >> 15)); return (addr & 0x7FFF); } static __inline void hidewin(sc) struct dgm_softc *sc; { bmws.data=0; outb(bmws.port=sc->port+1, bmws.data); } static __inline void towin(sc,win) struct dgm_softc *sc; int win; { outb(bmws.port=sc->port+1, bmws.data=win); } static int dgmprobe(dev) struct isa_device *dev; { struct dgm_softc *sc= &dgm_softc[dev->id_unit]; int i, v, t; u_char *mem; int addr; int unit=dev->id_unit; sc->unit=dev->id_unit; sc->port=dev->id_iobase; if(dev->id_flags & DGBFLAG_ALTPIN) sc->altpin=1; else sc->altpin=0; /* left 24 bits only (ISA address) */ sc->pmem=((intptr_t)(void *)dev->id_maddr & 0xFFFFFF); DPRINT4(DB_INFO,"dgm%d: port 0x%x mem 0x%lx\n",unit,sc->port,sc->pmem); outb(sc->port, FEPRST); sc->status=DISABLED; for(i=0; i< 1000; i++) { DELAY(1); if( (inb(sc->port) & FEPMASK) == FEPRST ) { sc->status=ENABLED; DPRINT3(DB_EXCEPT,"dgm%d: got reset after %d us\n",unit,i); break; } } if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dgm%d: failed to respond\n",dev->id_unit); return 0; } /* check type of card and get internal memory characteristics */ v=inb(sc->port); printf("dgm%d: PC/Xem\n",dev->id_unit); sc->type=PCXEM; sc->mem_seg=0x8000; /* save size of vizible memory segment */ /* all PCXEMs have a 32k window size */ dev->id_msize=0x8000; /* map memory */ dev->id_maddr=sc->vmem=pmap_mapdev(sc->pmem,dev->id_msize); outb(sc->port, FEPCLR); /* drop RESET */ hidewin(sc); /* Helg: to set initial bmws state */ return 4; /* we need I/O space of 4 ports */ } static int dgmattach(dev) struct isa_device *dev; { int unit=dev->id_unit; struct dgm_softc *sc= &dgm_softc[dev->id_unit]; int i, t; u_char *mem; u_char *ptr; int addr; struct dgm_p *port; volatile struct board_chan *bc; struct global_data *gd; int shrinkmem; int nfails; ushort *pstat; int lowwater; static int nports=0; char ch; int stuff; if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dbg%d: try to attach a disabled card\n",unit); return 0; } mem=sc->vmem; DPRINT3(DB_INFO,"dgm%d: internal memory segment 0x%x\n",unit,sc->mem_seg); outb(sc->port, FEPRST); DELAY(1); for(i=0; (inb(sc->port) & FEPMASK) != FEPRST ; i++) { if(i>10000) { printf("dgm%d: 1st reset failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } DELAY(1); } DPRINT3(DB_INFO,"dgm%d: got reset after %d us\n",unit,i); t=(((u_long)sc->pmem>>8)) /* disable windowing */; outb(sc->port+2,t & 0xFF); outb(sc->port+3,t>>8); mem=sc->vmem; /* very short memory test */ addr=setwin(sc,BOTWIN); *(u_long *)(mem+addr) = 0xA55A3CC3; if(*(u_long *)(mem+addr)!=0xA55A3CC3) { printf("dgm%d: 1st memory test failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } addr=setwin(sc,TOPWIN); *(u_long *)(mem+addr) = 0x5AA5C33C; if(*(u_long *)(mem+addr)!=0x5AA5C33C) { printf("dgm%d: 2nd memory test failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } addr=setwin(sc,BIOSCODE+((0xF000-sc->mem_seg)<<4)); *(u_long *)(mem+addr) = 0x5AA5C33C; if(*(u_long *)(mem+addr)!=0x5AA5C33C) { printf("dgm%d: 3rd (BIOS) memory test failed\n",dev->id_unit); } addr=setwin(sc,MISCGLOBAL); for(i=0; i<16; i++) { mem[addr+i]=0; } addr=setwin(sc,BIOSOFFSET); ptr=mem+addr; for(i=0; ptrid_msize; i++){ *ptr++ = pcem_bios[i]; } ptr=mem+BIOSOFFSET; for(i=0; ptrid_msize; i++){ if(*ptr++ != pcem_bios[i]){ printf("Low BIOS load failed\n"); sc->status = DISABLED; hidewin(sc); return 0; } } addr=setwin(sc,dev->id_msize); ptr =mem+addr; for(;i < pcem_nbios; i++){ *ptr++ = pcem_bios[i]; } ptr=mem; for(i = dev->id_msize - BIOSOFFSET; i < pcem_nbios; i++){ if(*ptr++ != pcem_bios[i]){ printf("High BIOS load failed\n"); sc->status = DISABLED; hidewin(sc); return 0; } } printf("dgm%d: DigiBIOS loaded, initializing",dev->id_unit); addr=setwin(sc,0); *(u_int *)(mem+addr+0)=0x0bf00401; *(u_int *)(mem+addr+4)=0; *(ushort *)(mem+addr+0xc00)=0; outb(sc->port, 0); for(i = 0;*(u_char *)(mem+addr+ 0xc00) != 0x47;i++){ DELAY(10000); if(i> 3000){ printf("\nBIOS initialize failed(1)\n"); sc->status = DISABLED; hidewin(sc); return 0; } } if(*(u_char *)(mem+addr+ 0xc01) != 0x44){ printf("\nBIOS initialize failed(2)\n"); sc->status = DISABLED; hidewin(sc); return 0; } printf(", DigiBIOS running\n"); DELAY(10000); addr=setwin(sc,BIOSOFFSET); ptr=mem+addr; for(i=0; istatus = DISABLED; hidewin(sc); return 0; } } printf("dgm%d: FEP/OS loaded, initializing",dev->id_unit); addr=setwin(sc,0); *(ushort *)(mem+addr+0xd20)=0; *(u_int *)(mem+addr+0xc34)=0xbfc01004; *(u_int *)(mem+addr+0xc30)=0x3L; outb(sc->port,0); for(i = 0;*(u_char *)(mem+addr+ 0xd20) != 'O';i++){ DELAY(10000); if(i> 3000){ printf("\nFEP/OS initialize failed(1)\n"); sc->status = DISABLED; hidewin(sc); return 0; } } if(*(u_char *)(mem+addr+ 0xd21) != 'S'){ printf("\nFEP/OS initialize failed(2)\n"); sc->status = DISABLED; hidewin(sc); return 0; } printf(", FEP/OS running\n"); sc->numports= *(ushort *)(mem+setwin(sc,NPORT)); printf("dgm%d: %d ports\n",unit,sc->numports); if(sc->numports > MAX_DGM_PORTS) { printf("dgm%d: too many ports\n",unit); sc->status=DISABLED; hidewin(sc); return 0; } if(nports+sc->numports>NDGMPORTS) { printf("dgm%d: only %d ports are usable\n", unit, NDGMPORTS-nports); sc->numports=NDGMPORTS-nports; } /* allocate port and tty structures */ sc->ports=&dgm_ports[nports]; sc->ttys=&dgm_tty[nports]; nports+=sc->numports; for(i=0; inumports; i++) sc->ports[i].status = ENABLED; /* We should now init per-port structures */ setwin(sc,0); bc=(volatile struct board_chan *)(mem + CHANSTRUCT); sc->mailbox=(volatile struct global_data *)(mem + FEP_GLOBAL); if(sc->numports<3) shrinkmem=1; else shrinkmem=0; for(i=0; inumports; i++, bc++) { port= &sc->ports[i]; port->tty=&sc->ttys[i]; port->unit=unit; port->brdchan=bc; port->dcd=CD; port->dsr=DSR; port->pnum=i; if(shrinkmem) { DPRINT2(DB_INFO,"dgm%d: shrinking memory\n",unit); fepcmd(port, SETBUFFER, 32, 0, 0, 0); shrinkmem=0; } /* HERE */ port->txptr=mem+( ((bc->tseg)<<4) & 0x7FFF ); port->rxptr=mem+( ((bc->rseg)<<4) & 0x7FFF ); port->txwin=FEPWIN | ((bc->tseg)>>11); port->rxwin=FEPWIN | ((bc->rseg)>>11); port->txbufhead=0; port->rxbufhead=0; port->txbufsize=bc->tmax+1; port->rxbufsize=bc->rmax+1; lowwater= (port->txbufsize>=2000) ? 1024 : (port->txbufsize/2); setwin(sc,0); fepcmd(port, STXLWATER, lowwater, 0, 10, 0); fepcmd(port, SRXLWATER, port->rxbufsize/4, 0, 10, 0); fepcmd(port, SRXHWATER, 3*port->rxbufsize/4, 0, 10, 0); bc->edelay=100; bc->idata=1; port->startc=bc->startc; port->startca=bc->startca; port->stopc=bc->stopc; port->stopca=bc->stopca; /*port->close_delay=50;*/ port->close_delay=3 * hz; port->do_timestamp=0; port->do_dcd_timestamp=0; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ port->it_in.c_iflag = TTYDEF_IFLAG; port->it_in.c_oflag = TTYDEF_OFLAG; port->it_in.c_cflag = TTYDEF_CFLAG; port->it_in.c_lflag = TTYDEF_LFLAG; termioschars(&port->it_in); port->it_in.c_ispeed = port->it_in.c_ospeed = dgmdefaultrate; port->it_out = port->it_in; #ifdef DEVFS port->devfs_token.tty = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyM%d%x", unit, i + 0xa0); port->devfs_token.ttyi = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i+64, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyiM%d%x", unit, i + 0xa0); port->devfs_token.ttyl = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i+128, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttylM%d%x", unit, i + 0xa0); port->devfs_token.cua = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i+262144, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaM%d%x", unit, i + 0xa0); port->devfs_token.cuai = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i+262208, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaiM%d%x", unit, i + 0xa0); port->devfs_token.cual = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i+262272, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cualM%d%x", unit, i + 0xa0); #endif } hidewin(sc); /* register the polling function */ timeout(dgmpoll, (void *)unit, hz/POLLSPERSEC); return 1; } /* ARGSUSED */ static int dgmopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct dgm_softc *sc; struct tty *tp; int unit; int mynor; int pnum; struct dgm_p *port; int s,cs; int error; volatile struct board_chan *bc; error=0; mynor=minor(dev); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); if(unit >= NDGM) { DPRINT2(DB_EXCEPT,"dgm%d: try to open a nonexisting card\n",unit); return ENXIO; } sc=&dgm_softc[unit]; if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dgm%d: try to open a disabled card\n",unit); return ENXIO; } if(pnum>=sc->numports) { DPRINT3(DB_EXCEPT,"dgm%d: try to open non-existing port %d\n",unit,pnum); return ENXIO; } if(mynor & CONTROL_MASK) return 0; tp=&sc->ttys[pnum]; port=&sc->ports[pnum]; bc=port->brdchan; open_top: s=spltty(); while(port->closing) { error=tsleep(&port->closing, TTOPRI|PCATCH, "dgocl", 0); if(error) { DPRINT4(DB_OPEN,"dgm%d: port%d: tsleep(dgocl) error=%d\n",unit,pnum,error); goto out; } } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!port->active_out) { error = EBUSY; DPRINT4(DB_OPEN,"dgm%d: port%d: BUSY error=%d\n",unit,pnum,error); goto out; } } else { if (port->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; DPRINT4(DB_OPEN,"dgm%d: port%d: BUSY error=%d\n",unit,pnum,error); goto out; } error = tsleep(&port->active_out, TTIPRI | PCATCH, "dgmi", 0); if (error != 0) { DPRINT4(DB_OPEN,"dgm%d: port%d: tsleep(dgmi) error=%d\n", unit,pnum,error); goto out; } splx(s); goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc=dgmstart; tp->t_param=dgmparam; tp->t_dev=dev; tp->t_termios= (mynor & CALLOUT_MASK) ? port->it_out : port->it_in; cs=splclock(); setwin(sc,0); port->imodem=bc->mstat; bc->rout=bc->rin; /* clear input queue */ bc->idata=1; #ifdef PRINT_BUFSIZE printf("dgm buffers tx=%x:%x rx=%x:%x\n",bc->tseg,bc->tmax,bc->rseg,bc->rmax); #endif hidewin(sc); splx(cs); port->wopeners++; error=dgmparam(tp, &tp->t_termios); port->wopeners--; if(error!=0) { DPRINT4(DB_OPEN,"dgm%d: port%d: dgmparam error=%d\n",unit,pnum,error); goto out; } - ttsetwater(tp); - /* handle fake DCD for callout devices */ /* and initial DCD */ if( (port->imodem & port->dcd) || mynor & CALLOUT_MASK ) linesw[tp->t_line].l_modem(tp,1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++port->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "dgdcd", 0); --port->wopeners; if (error != 0) { DPRINT4(DB_OPEN,"dgm%d: port%d: tsleep(dgdcd) error=%d\n",unit,pnum,error); goto out; } splx(s); goto open_top; } error = linesw[tp->t_line].l_open(dev, tp); disc_optim(tp,&tp->t_termios); DPRINT4(DB_OPEN,"dgm%d: port%d: l_open error=%d\n",unit,pnum,error); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) port->active_out = TRUE; port->used=1; /* If any port is open (i.e. the open() call is completed for it) * the device is busy */ out: disc_optim(tp,&tp->t_termios); splx(s); if( !(tp->t_state & TS_ISOPEN) && port->wopeners==0 ) dgmhardclose(port); DPRINT4(DB_OPEN,"dgm%d: port%d: open() returns %d\n",unit,pnum,error); return error; } /*ARGSUSED*/ static int dgmclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { int mynor; struct tty *tp; int unit, pnum; struct dgm_softc *sc; struct dgm_p *port; int s; int i; mynor=minor(dev); if(mynor & CONTROL_MASK) return 0; unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); sc=&dgm_softc[unit]; tp=&sc->ttys[pnum]; port=sc->ports+pnum; DPRINT3(DB_CLOSE,"dgm%d: port%d: closing\n",unit,pnum); DPRINT3(DB_CLOSE,"dgm%d: port%d: draining port\n",unit,pnum); dgm_drain_or_flush(port); s=spltty(); port->closing=1; DPRINT3(DB_CLOSE,"dgm%d: port%d: closing line disc\n",unit,pnum); linesw[tp->t_line].l_close(tp,flag); disc_optim(tp,&tp->t_termios); DPRINT3(DB_CLOSE,"dgm%d: port%d: hard closing\n",unit,pnum); dgmhardclose(port); DPRINT3(DB_CLOSE,"dgm%d: port%d: closing tty\n",unit,pnum); ttyclose(tp); port->closing=0; wakeup(&port->closing); port->used=0; /* mark the card idle when all ports are closed */ for(i=0; inumports; i++) if(sc->ports[i].used) break; splx(s); DPRINT3(DB_CLOSE,"dgm%d: port%d: closed\n",unit,pnum); wakeup(TSA_CARR_ON(tp)); wakeup(&port->active_out); port->active_out=0; DPRINT3(DB_CLOSE,"dgm%d: port%d: close exit\n",unit,pnum); return 0; } static void dgmhardclose(port) struct dgm_p *port; { struct dgm_softc *sc=&dgm_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int cs; cs=splclock(); port->do_timestamp = 0; setwin(sc,0); bc->idata=0; bc->iempty=0; bc->ilow=0; if(port->tty->t_cflag & HUPCL) { port->omodem &= ~(RTS|DTR); fepcmd(port, SETMODEM, 0, DTR|RTS, 0, 1); } hidewin(sc); splx(cs); timeout(dgm_pause, &port->brdchan, hz/2); tsleep(&port->brdchan, TTIPRI | PCATCH, "dgclo", 0); } static void dgm_pause(chan) void *chan; { wakeup((caddr_t)chan); } static int dgmread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int error, unit, pnum; mynor=minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); tp=&dgm_softc[unit].ttys[pnum]; error=linesw[tp->t_line].l_read(tp, uio, flag); DPRINT4(DB_RD,"dgm%d: port%d: read() returns %d\n",unit,pnum,error); return error; } static int dgmwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int error, unit, pnum; mynor=minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); tp=&dgm_softc[unit].ttys[pnum]; error=linesw[tp->t_line].l_write(tp, uio, flag); DPRINT4(DB_WR,"dgm%d: port%d: write() returns %d\n",unit,pnum,error); return error; } static void dgmpoll(unit_c) void *unit_c; { int unit=(int)unit_c; int pnum; struct dgm_p *port; struct dgm_softc *sc=&dgm_softc[unit]; int head, tail; u_char *eventbuf; int event, mstat, lstat; volatile struct board_chan *bc; struct tty *tp; int rhead, rtail; int whead, wtail; int size; int c=0; u_char *ptr; int ocount; int ibuf_full,obuf_full; BoardMemWinState ws=bmws_get(); if(sc->status==DISABLED) { printf("dgm%d: polling of disabled board stopped\n",unit); return; } setwin(sc,0); head=sc->mailbox->ein; tail=sc->mailbox->eout; while(head!=tail) { if(head >= FEP_IMAX-FEP_ISTART || tail >= FEP_IMAX-FEP_ISTART || (head|tail) & 03 ) { printf("dgm%d: event queue's head or tail is wrong! hd=%d,tl=%d\n", unit,head,tail); break; } eventbuf=sc->vmem+tail+FEP_ISTART; pnum=eventbuf[0]; event=eventbuf[1]; mstat=eventbuf[2]; lstat=eventbuf[3]; port=&sc->ports[pnum]; bc=port->brdchan; tp=&sc->ttys[pnum]; if(pnum>=sc->numports || port->status==DISABLED) { printf("dgm%d: port%d: got event on nonexisting port\n",unit,pnum); } else if(port->used || port->wopeners>0 ) { int wrapmask=port->rxbufsize-1; if( !(event & ALL_IND) ) printf("dgm%d: port%d: ? event 0x%x mstat 0x%x lstat 0x%x\n", unit, pnum, event, mstat, lstat); if(event & DATA_IND) { DPRINT3(DB_DATA,"dgm%d: port%d: DATA_IND\n",unit,pnum); rhead=bc->rin & wrapmask; rtail=bc->rout & wrapmask; if( !(tp->t_cflag & CREAD) || !port->used ) { bc->rout=rhead; goto end_of_data; } if(bc->orun) { printf("dgm%d: port%d: overrun\n", unit, pnum); bc->orun=0; } if(!(tp->t_state & TS_ISOPEN)) goto end_of_data; for(ibuf_full=FALSE;rhead!=rtail && !ibuf_full;) { DPRINT5(DB_RXDATA,"dgm%d: port%d: p rx head=%d tail=%d\n", unit,pnum,rhead,rtail); if(rhead>rtail) size=rhead-rtail; else size=port->rxbufsize-rtail; ptr=port->rxptr+rtail; /* Helg: */ if( tp->t_rawq.c_cc + size > DGB_IBUFSIZE ) { size=DGB_IBUFSIZE-tp->t_rawq.c_cc; DPRINT1(DB_RXDATA,"*"); ibuf_full=TRUE; } if(size) { if (tp->t_state & TS_CAN_BYPASS_L_RINT) { DPRINT1(DB_RXDATA,"!"); towin(sc,port->rxwin); tk_nin += size; tk_rawcc += size; tp->t_rawcc += size; b_to_q(ptr,size,&tp->t_rawq); setwin(sc,0); } else { int i=size; unsigned char chr; do { towin(sc,port->rxwin); chr= *ptr++; hidewin(sc); (*linesw[tp->t_line].l_rint)(chr, tp); } while (--i > 0 ); setwin(sc,0); } } rtail= (rtail + size) & wrapmask; bc->rout=rtail; rhead=bc->rin & wrapmask; hidewin(sc); ttwakeup(tp); setwin(sc,0); } end_of_data: ; } if(event & MODEMCHG_IND) { DPRINT3(DB_MODEM,"dgm%d: port%d: MODEMCHG_IND\n",unit,pnum); port->imodem=mstat; if(mstat & port->dcd) { hidewin(sc); linesw[tp->t_line].l_modem(tp,1); setwin(sc,0); wakeup(TSA_CARR_ON(tp)); } else { hidewin(sc); linesw[tp->t_line].l_modem(tp,0); setwin(sc,0); if( port->draining) { port->draining=0; wakeup(&port->draining); } } } if(event & BREAK_IND) { if((tp->t_state & TS_ISOPEN) && (tp->t_iflag & IGNBRK)) { DPRINT3(DB_BREAK,"dgm%d: port%d: BREAK_IND\n",unit,pnum); hidewin(sc); linesw[tp->t_line].l_rint(TTY_BI, tp); setwin(sc,0); } } /* Helg: with output flow control */ if(event & (LOWTX_IND | EMPTYTX_IND) ) { DPRINT3(DB_TXDATA,"dgm%d: port%d: LOWTX_IND or EMPTYTX_IND\n",unit,pnum); if( (event & EMPTYTX_IND ) && tp->t_outq.c_cc==0 && port->draining) { port->draining=0; wakeup(&port->draining); bc->ilow=0; bc->iempty=0; } else { int wrapmask=port->txbufsize-1; for(obuf_full=FALSE; tp->t_outq.c_cc!=0 && !obuf_full; ) { int s; /* add "last-minute" data to write buffer */ if(!(tp->t_state & TS_BUSY)) { hidewin(sc); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ ttwwakeup(tp); #else if(tp->t_outq.c_cc <= tp->t_lowat) { if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } /* selwakeup(&tp->t_wsel); */ } #endif setwin(sc,0); } s=spltty(); whead=bc->tin & wrapmask; wtail=bc->tout & wrapmask; if(wheadtxbufsize-whead; if(wtail==0) size--; } if(size==0) { DPRINT5(DB_WR,"dgm: head=%d tail=%d size=%d full=%d\n", whead,wtail,size,obuf_full); bc->iempty=1; bc->ilow=1; obuf_full=TRUE; splx(s); break; } towin(sc,port->txwin); ocount=q_to_b(&tp->t_outq, port->txptr+whead, size); whead+=ocount; setwin(sc,0); bc->tin=whead; bc->tin=whead & wrapmask; splx(s); } if(obuf_full) { DPRINT1(DB_WR," +BUSY\n"); tp->t_state|=TS_BUSY; } else { DPRINT1(DB_WR," -BUSY\n"); hidewin(sc); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ /* should clear TS_BUSY before ttwwakeup */ if(tp->t_state & TS_BUSY) { tp->t_state &= ~TS_BUSY; linesw[tp->t_line].l_start(tp); ttwwakeup(tp); } #else if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } tp->t_state &= ~TS_BUSY; #endif setwin(sc,0); } } end_of_buffer: ; } bc->idata=1; /* require event on incoming data */ } else { bc=port->brdchan; DPRINT4(DB_EXCEPT,"dgm%d: port%d: got event 0x%x on closed port\n", unit,pnum,event); bc->rout=bc->rin; bc->idata=bc->iempty=bc->ilow=0; } tail= (tail+4) & (FEP_IMAX-FEP_ISTART-4); } sc->mailbox->eout=tail; bmws_set(ws); timeout(dgmpoll, unit_c, hz/POLLSPERSEC); } static int dgmioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct dgm_softc *sc; int unit, pnum; struct dgm_p *port; int mynor; struct tty *tp; volatile struct board_chan *bc; int error; int s,cs; int tiocm_xxx; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) u_long oldcmd; struct termios term; #endif BoardMemWinState ws=bmws_get(); mynor=minor(dev); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); sc=&dgm_softc[unit]; port=&sc->ports[pnum]; tp=&sc->ttys[pnum]; bc=port->brdchan; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &port->it_out : &port->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { DPRINT6(DB_PARAM,"dgm%d: port%d: dgmioctl-ISNOW c=0x%x i=0x%x l=0x%x\n",unit,pnum,term.c_cflag,term.c_iflag,term.c_lflag); } oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in; DPRINT6(DB_PARAM,"dgm%d: port%d: dgmioctl-TOSET c=0x%x i=0x%x l=0x%x\n",unit,pnum,dt->c_cflag,dt->c_iflag,dt->c_lflag); dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } if(cmd==TIOCSTOP) { cs=splclock(); setwin(sc,0); fepcmd(port, PAUSETX, 0, 0, 0, 0); bmws_set(ws); splx(cs); return 0; } else if(cmd==TIOCSTART) { cs=splclock(); setwin(sc,0); fepcmd(port, RESUMETX, 0, 0, 0, 0); bmws_set(ws); splx(cs); return 0; } if(cmd==TIOCSETAW || cmd==TIOCSETAF) port->mustdrain=1; error = linesw[tp->t_line].l_ioctl(tp, cmd, data, flag, p); if (error != ENOIOCTL) return error; s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp,&tp->t_termios); port->mustdrain=0; if (error != ENOIOCTL) { splx(s); if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { DPRINT6(DB_PARAM,"dgm%d: port%d: dgmioctl-RES c=0x%x i=0x%x l=0x%x\n",unit,pnum,tp->t_cflag,tp->t_iflag,tp->t_lflag); } return error; } switch (cmd) { case TIOCSBRK: /* Helg: commented */ /* error=dgmdrain(port);*/ if(error!=0) { splx(s); return error; } cs=splclock(); setwin(sc,0); /* now it sends 250 millisecond break because I don't know */ /* how to send an infinite break */ fepcmd(port, SENDBREAK, 250, 0, 10, 0); hidewin(sc); splx(cs); break; case TIOCCBRK: /* now it's empty */ break; case TIOCSDTR: DPRINT3(DB_MODEM,"dgm%d: port%d: set DTR\n",unit,pnum); port->omodem |= DTR; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS, 0, 1); if( !(bc->mstat & DTR) ) { DPRINT3(DB_MODEM,"dgm%d: port%d: DTR is off\n",unit,pnum); } hidewin(sc); splx(cs); break; case TIOCCDTR: DPRINT3(DB_MODEM,"dgm%d: port%d: reset DTR\n",unit,pnum); port->omodem &= ~DTR; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); if( bc->mstat & DTR ) { DPRINT3(DB_MODEM,"dgm%d: port%d: DTR is on\n",unit,pnum); } hidewin(sc); splx(cs); break; case TIOCMSET: if(*(int *)data & TIOCM_DTR) port->omodem |=DTR; else port->omodem &=~DTR; if(*(int *)data & TIOCM_RTS) port->omodem |=RTS; else port->omodem &=~RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMBIS: if(*(int *)data & TIOCM_DTR) port->omodem |=DTR; if(*(int *)data & TIOCM_RTS) port->omodem |=RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMBIC: if(*(int *)data & TIOCM_DTR) port->omodem &=~DTR; if(*(int *)data & TIOCM_RTS) port->omodem &=~RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMGET: setwin(sc,0); port->imodem=bc->mstat; hidewin(sc); tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */ DPRINT3(DB_MODEM,"dgm%d: port%d: modem stat -- ",unit,pnum); if (port->imodem & DTR) { DPRINT1(DB_MODEM,"DTR "); tiocm_xxx |= TIOCM_DTR; } if (port->imodem & RTS) { DPRINT1(DB_MODEM,"RTS "); tiocm_xxx |= TIOCM_RTS; } if (port->imodem & CTS) { DPRINT1(DB_MODEM,"CTS "); tiocm_xxx |= TIOCM_CTS; } if (port->imodem & port->dcd) { DPRINT1(DB_MODEM,"DCD "); tiocm_xxx |= TIOCM_CD; } if (port->imodem & port->dsr) { DPRINT1(DB_MODEM,"DSR "); tiocm_xxx |= TIOCM_DSR; } if (port->imodem & RI) { DPRINT1(DB_MODEM,"RI "); tiocm_xxx |= TIOCM_RI; } *(int *)data = tiocm_xxx; DPRINT1(DB_MODEM,"--\n"); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } port->close_delay = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = port->close_delay * 100 / hz; break; case TIOCTIMESTAMP: port->do_timestamp = TRUE; *(struct timeval *)data = port->timestamp; break; case TIOCDCDTIMESTAMP: port->do_dcd_timestamp = TRUE; *(struct timeval *)data = port->dcd_timestamp; break; default: bmws_set(ws); splx(s); return ENOTTY; } bmws_set(ws); splx(s); return 0; } static void wakeflush(p) void *p; { struct dgm_p *port=p; wakeup(&port->draining); } /* wait for the output to drain */ static int dgmdrain(port) struct dgm_p *port; { struct dgm_softc *sc=&dgm_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int error; int head, tail; BoardMemWinState ws=bmws_get(); setwin(sc,0); bc->iempty=1; tail=bc->tout; head=bc->tin; while(tail!=head) { DPRINT5(DB_WR,"dgm%d: port%d: drain: head=%d tail=%d\n", port->unit, port->pnum, head, tail); hidewin(sc); port->draining=1; timeout(wakeflush,port, hz); error=tsleep(&port->draining, TTIPRI | PCATCH, "dgdrn", 0); port->draining=0; setwin(sc,0); if (error != 0) { DPRINT4(DB_WR,"dgm%d: port%d: tsleep(dgdrn) error=%d\n", port->unit,port->pnum,error); bc->iempty=0; bmws_set(ws); return error; } tail=bc->tout; head=bc->tin; } DPRINT5(DB_WR,"dgm%d: port%d: drain: head=%d tail=%d\n", port->unit, port->pnum, head, tail); bmws_set(ws); return 0; } /* wait for the output to drain */ /* or simply clear the buffer it it's stopped */ static void dgm_drain_or_flush(port) struct dgm_p *port; { struct tty *tp=port->tty; struct dgm_softc *sc=&dgm_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int error; int lasttail; int head, tail; setwin(sc,0); lasttail=-1; bc->iempty=1; tail=bc->tout; head=bc->tin; while(tail!=head /* && tail!=lasttail */ ) { DPRINT5(DB_WR,"dgm%d: port%d: flush: head=%d tail=%d\n", port->unit, port->pnum, head, tail); /* if there is no carrier simply clean the buffer */ if( !(tp->t_state & TS_CARR_ON) ) { bc->tout=bc->tin=0; bc->iempty=0; hidewin(sc); return; } hidewin(sc); port->draining=1; timeout(wakeflush,port, hz); error=tsleep(&port->draining, TTIPRI | PCATCH, "dgfls", 0); port->draining=0; setwin(sc,0); if (error != 0) { DPRINT4(DB_WR,"dgm%d: port%d: tsleep(dgfls) error=%d\n", port->unit,port->pnum,error); /* silently clean the buffer */ bc->tout=bc->tin=0; bc->iempty=0; hidewin(sc); return; } lasttail=tail; tail=bc->tout; head=bc->tin; } hidewin(sc); DPRINT5(DB_WR,"dgm%d: port%d: flush: head=%d tail=%d\n", port->unit, port->pnum, head, tail); } static int dgmparam(tp, t) struct tty *tp; struct termios *t; { int dev=tp->t_dev; int mynor=minor(dev); int unit=MINOR_TO_UNIT(dev); int pnum=MINOR_TO_PORT(dev); struct dgm_softc *sc=&dgm_softc[unit]; struct dgm_p *port=&sc->ports[pnum]; volatile struct board_chan *bc=port->brdchan; int cflag; int head; int mval; int iflag; int hflow; int s,cs; BoardMemWinState ws=bmws_get(); DPRINT6(DB_PARAM,"dgm%d: port%d: dgmparm c=0x%x i=0x%x l=0x%x\n",unit,pnum,t->c_cflag,t->c_iflag,t->c_lflag); if(port->mustdrain) { DPRINT3(DB_PARAM,"dgm%d: port%d: must call dgmdrain()\n",unit,pnum); dgmdrain(port); } cflag=ttspeedtab(t->c_ospeed, dgmspeedtab); if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; if (cflag < 0 /* || cflag > 0 && t->c_ispeed != t->c_ospeed */) { DPRINT4(DB_PARAM,"dgm%d: port%d: invalid cflag=0%o\n",unit,pnum,cflag); return (EINVAL); } cs=splclock(); setwin(sc,0); if(cflag==0) { /* hangup */ DPRINT3(DB_PARAM,"dgm%d: port%d: hangup\n",unit,pnum); head=bc->rin; bc->rout=head; head=bc->tin; fepcmd(port, STOUT, (unsigned)head, 0, 0, 0); mval= port->omodem & ~(DTR|RTS); } else { cflag |= dgmflags(dgm_cflags, t->c_cflag); if(cflag!=port->fepcflag) { port->fepcflag=cflag; DPRINT5(DB_PARAM,"dgm%d: port%d: set cflag=0x%x c=0x%x\n", unit,pnum,cflag,t->c_cflag&~CRTSCTS); fepcmd(port, SETCTRLFLAGS, (unsigned)cflag, 0, 0, 0); } mval= port->omodem | (DTR|RTS); } iflag=dgmflags(dgm_iflags, t->c_iflag); if(iflag!=port->fepiflag) { port->fepiflag=iflag; DPRINT5(DB_PARAM,"dgm%d: port%d: set iflag=0x%x c=0x%x\n",unit,pnum,iflag,t->c_iflag); fepcmd(port, SETIFLAGS, (unsigned)iflag, 0, 0, 0); } bc->mint=port->dcd; hflow=dgmflags(dgm_flow, t->c_cflag); if(hflow!=port->hflow) { port->hflow=hflow; DPRINT5(DB_PARAM,"dgm%d: port%d: set hflow=0x%x f=0x%x\n",unit,pnum,hflow,t->c_cflag&CRTSCTS); fepcmd(port, SETHFLOW, (unsigned)hflow, 0xff, 0, 1); } if(port->omodem != mval) { DPRINT5(DB_PARAM,"dgm%d: port%d: setting modem parameters 0x%x was 0x%x\n", unit,pnum,mval,port->omodem); port->omodem=mval; fepcmd(port, SETMODEM, (unsigned)mval, RTS|DTR, 0, 1); } if(port->fepstartc!=t->c_cc[VSTART] || port->fepstopc!=t->c_cc[VSTOP]) { DPRINT5(DB_PARAM,"dgm%d: port%d: set startc=%d, stopc=%d\n",unit,pnum,t->c_cc[VSTART],t->c_cc[VSTOP]); port->fepstartc=t->c_cc[VSTART]; port->fepstopc=t->c_cc[VSTOP]; fepcmd(port, SONOFFC, port->fepstartc, port->fepstopc, 0, 1); } bmws_set(ws); splx(cs); return 0; } static void dgmstart(tp) struct tty *tp; { int unit; int pnum; struct dgm_p *port; struct dgm_softc *sc; volatile struct board_chan *bc; int head, tail; int size, ocount; int s; int wmask; BoardMemWinState ws=bmws_get(); unit=MINOR_TO_UNIT(minor(tp->t_dev)); pnum=MINOR_TO_PORT(minor(tp->t_dev)); sc=&dgm_softc[unit]; port=&sc->ports[pnum]; bc=port->brdchan; wmask=port->txbufsize-1; s=spltty(); while( tp->t_outq.c_cc!=0 ) { int cs; #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ ttwwakeup(tp); #else if(tp->t_outq.c_cc <= tp->t_lowat) { if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } /*selwakeup(&tp->t_wsel);*/ } #endif cs=splclock(); setwin(sc,0); head=bc->tin & wmask; /*HERE*/ do { tail=bc->tout; } while (tail != bc->tout); tail=bc->tout & wmask; DPRINT5(DB_WR,"dgm%d: port%d: s tx head=%d tail=%d\n",unit,pnum,head,tail); #ifdef LEAVE_FREE_CHARS if(tail>head) { size=tail-head-LEAVE_FREE_CHARS; if (size <0) size==0; } else { size=port->txbufsize-head; if(tail+port->txbufsize < head) size==0; } } #else if(tail>head) size=tail-head-1; else { size=port->txbufsize-head/*-1*/; if(tail==0) size--; } #endif if(size==0) { bc->iempty=1; bc->ilow=1; splx(cs); bmws_set(ws); tp->t_state|=TS_BUSY; splx(s); return; } towin(sc,port->txwin); ocount=q_to_b(&tp->t_outq, port->txptr+head, size); head+=ocount; if(head>=port->txbufsize) head-=port->txbufsize; setwin(sc,0); bc->tin=head; DPRINT5(DB_WR,"dgm%d: port%d: tx avail=%d count=%d\n",unit,pnum,size,ocount); hidewin(sc); splx(cs); } bmws_set(ws); splx(s); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ if(tp->t_state & TS_BUSY) { tp->t_state&=~TS_BUSY; linesw[tp->t_line].l_start(tp); ttwwakeup(tp); } #else if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } tp->t_state&=~TS_BUSY; #endif } void dgmstop(tp, rw) struct tty *tp; int rw; { int unit; int pnum; struct dgm_p *port; struct dgm_softc *sc; volatile struct board_chan *bc; int head; int s; BoardMemWinState ws=bmws_get(); unit=MINOR_TO_UNIT(minor(tp->t_dev)); pnum=MINOR_TO_PORT(minor(tp->t_dev)); sc=&dgm_softc[unit]; port=&sc->ports[pnum]; bc=port->brdchan; DPRINT3(DB_WR,"dgm%d: port%d: stop\n",port->unit, port->pnum); s = spltty(); setwin(sc,0); if (rw & FWRITE) { /* clear output queue */ bc->tout=bc->tin=0; bc->ilow=0;bc->iempty=0; } if (rw & FREAD) { /* clear input queue */ bc->rout=bc->rin; bc->idata=1; } hidewin(sc); bmws_set(ws); splx(s); dgmstart(tp); } struct tty * dgmdevtotty(dev) dev_t dev; { int mynor, pnum, unit; struct dgm_softc *sc; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NDGM) return (NULL); pnum = MINOR_TO_PORT(mynor); sc = &dgm_softc[unit]; if (pnum >= sc->numports) return (NULL); return (&sc->ttys[pnum]); } static void fepcmd(port, cmd, op1, op2, ncmds, bytecmd) struct dgm_p *port; unsigned cmd, op1, op2, ncmds, bytecmd; { struct dgm_softc *sc=&dgm_softc[port->unit]; u_char *mem=sc->vmem; unsigned tail, head; int count, n; if(port->status==DISABLED) { printf("dgm%d: port%d: FEP command on disabled port\n", port->unit, port->pnum); return; } /* setwin(sc,0); Require this to be set by caller */ head=sc->mailbox->cin; if(head>=(FEP_CMAX-FEP_CSTART) || (head & 3)) { printf("dgm%d: port%d: wrong pointer head of command queue : 0x%x\n", port->unit, port->pnum, head); return; } mem[head+FEP_CSTART+0]=cmd; mem[head+FEP_CSTART+1]=port->pnum; if(bytecmd) { mem[head+FEP_CSTART+2]=op1; mem[head+FEP_CSTART+3]=op2; } else { mem[head+FEP_CSTART+2]=op1&0xff; mem[head+FEP_CSTART+3]=(op1>>8)&0xff; } DPRINT7(DB_FEP,"dgm%d: port%d: %s cmd=0x%x op1=0x%x op2=0x%x\n", port->unit, port->pnum, (bytecmd)?"byte":"word", cmd, mem[head+FEP_CSTART+2], mem[head+FEP_CSTART+3]); head=(head+4) & (FEP_CMAX-FEP_CSTART-4); sc->mailbox->cin=head; count=FEPTIMEOUT; while (count-- != 0) { head=sc->mailbox->cin; tail=sc->mailbox->cout; n = (head-tail) & (FEP_CMAX-FEP_CSTART-4); if(n <= ncmds * (sizeof(ushort)*4)) return; } printf("dgm%d(%d): timeout on FEP cmd=0x%x\n", port->unit, port->pnum, cmd); } static void disc_optim(tp, t) struct tty *tp; struct termios *t; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; } static dgm_devsw_installed = 0; static void dgm_drvinit(void *unused) { dev_t dev; if( ! dgm_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&dgm_cdevsw, NULL); dgm_devsw_installed = 1; } } SYSINIT(dgmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,dgm_drvinit,NULL) int fi(){ return 0; } #endif /* NDGM > 0 */ diff --git a/sys/dev/rc/rc.c b/sys/dev/rc/rc.c index 021f089cea73..5f845c6fbc02 100644 --- a/sys/dev/rc/rc.c +++ b/sys/dev/rc/rc.c @@ -1,1507 +1,1508 @@ /* * Copyright (C) 1995 by Pavel Antonov, Moscow, Russia. * Copyright (C) 1995 by Andrey A. Chernov, Moscow, Russia. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * SDL Communications Riscom/8 (based on Cirrus Logic CL-CD180) driver * */ #include "rc.h" #if NRC > 0 #include "opt_devfs.h" /*#define RCDEBUG*/ #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #include /* Prototypes */ static int rcprobe __P((struct isa_device *)); static int rcattach __P((struct isa_device *)); #define rcin(port) RC_IN (nec, port) #define rcout(port,v) RC_OUT (nec, port, v) #define WAITFORCCR(u,c) rc_wait0(nec, (u), (c), __LINE__) #define CCRCMD(u,c,cmd) WAITFORCCR((u), (c)); rcout(CD180_CCR, (cmd)) #define RC_IBUFSIZE 256 #define RB_I_HIGH_WATER (TTYHOG - 2 * RC_IBUFSIZE) #define RC_OBUFSIZE 512 #define RC_IHIGHWATER (3 * RC_IBUFSIZE / 4) #define INPUT_FLAGS_SHIFT (2 * RC_IBUFSIZE) #define LOTS_OF_EVENTS 64 #define RC_FAKEID 0x10 #define RC_PROBED 1 #define RC_ATTACHED 2 #define GET_UNIT(dev) (minor(dev) & 0x3F) #define CALLOUT(dev) (minor(dev) & 0x80) /* For isa routines */ struct isa_driver rcdriver = { rcprobe, rcattach, "rc" }; static d_open_t rcopen; static d_close_t rcclose; static d_read_t rcread; static d_write_t rcwrite; static d_ioctl_t rcioctl; static d_stop_t rcstop; static d_devtotty_t rcdevtotty; -#define CDEV_MAJOR 63 -static struct cdevsw rc_cdevsw = - { rcopen, rcclose, rcread, rcwrite, /*63*/ - rcioctl, rcstop, noreset, rcdevtotty,/* rc */ - ttpoll, nommap, NULL, "rc", NULL, -1 }; +#define CDEV_MAJOR 63 +static struct cdevsw rc_cdevsw = { + rcopen, rcclose, rcread, rcwrite, + rcioctl, rcstop, noreset, rcdevtotty, + ttpoll, nommap, NULL, "rc", + NULL, -1, nodump, nopsize, + D_TTY, +}; /* Per-board structure */ static struct rc_softc { u_int rcb_probed; /* 1 - probed, 2 - attached */ u_int rcb_addr; /* Base I/O addr */ u_int rcb_unit; /* unit # */ u_char rcb_dtr; /* DTR status */ struct rc_chans *rcb_baserc; /* base rc ptr */ } rc_softc[NRC]; /* Per-channel structure */ static struct rc_chans { struct rc_softc *rc_rcb; /* back ptr */ u_short rc_flags; /* Misc. flags */ int rc_chan; /* Channel # */ u_char rc_ier; /* intr. enable reg */ u_char rc_msvr; /* modem sig. status */ u_char rc_cor2; /* options reg */ u_char rc_pendcmd; /* special cmd pending */ u_int rc_dtrwait; /* dtr timeout */ u_int rc_dcdwaits; /* how many waits DCD in open */ u_char rc_hotchar; /* end packed optimize */ struct tty *rc_tp; /* tty struct */ u_char *rc_iptr; /* Chars input buffer */ u_char *rc_hiwat; /* hi-water mark */ u_char *rc_bufend; /* end of buffer */ u_char *rc_optr; /* ptr in output buf */ u_char *rc_obufend; /* end of output buf */ u_char rc_ibuf[4 * RC_IBUFSIZE]; /* input buffer */ u_char rc_obuf[RC_OBUFSIZE]; /* output buffer */ #ifdef DEVFS void *devfs_token; #endif } rc_chans[NRC * CD180_NCHAN]; static int rc_scheduled_event = 0; /* for pstat -t */ static struct tty rc_tty[NRC * CD180_NCHAN]; static const int nrc_tty = NRC * CD180_NCHAN; /* Flags */ #define RC_DTR_OFF 0x0001 /* DTR wait, for close/open */ #define RC_ACTOUT 0x0002 /* Dial-out port active */ #define RC_RTSFLOW 0x0004 /* RTS flow ctl enabled */ #define RC_CTSFLOW 0x0008 /* CTS flow ctl enabled */ #define RC_DORXFER 0x0010 /* RXFER event planned */ #define RC_DOXXFER 0x0020 /* XXFER event planned */ #define RC_MODCHG 0x0040 /* Modem status changed */ #define RC_OSUSP 0x0080 /* Output suspended */ #define RC_OSBUSY 0x0100 /* start() routine in progress */ #define RC_WAS_BUFOVFL 0x0200 /* low-level buffer ovferflow */ #define RC_WAS_SILOVFL 0x0400 /* silo buffer overflow */ #define RC_SEND_RDY 0x0800 /* ready to send */ /* Table for translation of RCSR status bits to internal form */ static int rc_rcsrt[16] = { 0, TTY_OE, TTY_FE, TTY_FE|TTY_OE, TTY_PE, TTY_PE|TTY_OE, TTY_PE|TTY_FE, TTY_PE|TTY_FE|TTY_OE, TTY_BI, TTY_BI|TTY_OE, TTY_BI|TTY_FE, TTY_BI|TTY_FE|TTY_OE, TTY_BI|TTY_PE, TTY_BI|TTY_PE|TTY_OE, TTY_BI|TTY_PE|TTY_FE, TTY_BI|TTY_PE|TTY_FE|TTY_OE }; /* Static prototypes */ static void rc_hwreset __P((int, int, unsigned int)); static int rc_test __P((int, int)); static void rc_discard_output __P((struct rc_chans *)); static void rc_hardclose __P((struct rc_chans *)); static int rc_modctl __P((struct rc_chans *, int, int)); static void rc_start __P((struct tty *)); static int rc_param __P((struct tty *, struct termios *)); static swihand_t rcpoll; static void rc_reinit __P((struct rc_softc *)); #ifdef RCDEBUG static void printrcflags(); #endif static timeout_t rc_dtrwakeup; static timeout_t rc_wakeup; static void disc_optim __P((struct tty *tp, struct termios *t, struct rc_chans *)); static void rc_wait0 __P((int nec, int unit, int chan, int line)); /**********************************************/ /* Quick device probing */ static int rcprobe(dvp) struct isa_device *dvp; { int irq = ffs(dvp->id_irq) - 1; register int nec = dvp->id_iobase; if (dvp->id_unit > NRC) return 0; if (!RC_VALIDADDR(nec)) { printf("rc%d: illegal base address %x\n", dvp->id_unit, nec); return 0; } if (!RC_VALIDIRQ(irq)) { printf("rc%d: illegal IRQ value %d\n", dvp->id_unit, irq); return 0; } rcout(CD180_PPRL, 0x22); /* Random values to Prescale reg. */ rcout(CD180_PPRH, 0x11); if (rcin(CD180_PPRL) != 0x22 || rcin(CD180_PPRH) != 0x11) return 0; /* Now, test the board more thoroughly, with diagnostic */ if (rc_test(nec, dvp->id_unit)) return 0; rc_softc[dvp->id_unit].rcb_probed = RC_PROBED; return 0xF; } static int rcattach(dvp) struct isa_device *dvp; { register int chan, nec = dvp->id_iobase; struct rc_softc *rcb = &rc_softc[dvp->id_unit]; struct rc_chans *rc = &rc_chans[dvp->id_unit * CD180_NCHAN]; static int rc_started = 0; struct tty *tp; /* Thorooughly test the device */ if (rcb->rcb_probed != RC_PROBED) return 0; rcb->rcb_addr = nec; rcb->rcb_dtr = 0; rcb->rcb_baserc = rc; /*rcb->rcb_chipid = 0x10 + dvp->id_unit;*/ printf("rc%d: %d chans, firmware rev. %c\n", dvp->id_unit, CD180_NCHAN, (rcin(CD180_GFRCR) & 0xF) + 'A'); for (chan = 0; chan < CD180_NCHAN; chan++, rc++) { rc->rc_rcb = rcb; rc->rc_chan = chan; rc->rc_iptr = rc->rc_ibuf; rc->rc_bufend = &rc->rc_ibuf[RC_IBUFSIZE]; rc->rc_hiwat = &rc->rc_ibuf[RC_IHIGHWATER]; rc->rc_flags = rc->rc_ier = rc->rc_msvr = 0; rc->rc_cor2 = rc->rc_pendcmd = 0; rc->rc_optr = rc->rc_obufend = rc->rc_obuf; rc->rc_dtrwait = 3 * hz; rc->rc_dcdwaits= 0; rc->rc_hotchar = 0; tp = rc->rc_tp = &rc_tty[chan]; ttychars(tp); tp->t_lflag = tp->t_iflag = tp->t_oflag = 0; tp->t_cflag = TTYDEF_CFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; #ifdef DEVFS /* FIX THIS to reflect real devices */ rc->devfs_token = devfs_add_devswf(&rc_cdevsw, (dvp->id_unit * CD180_NCHAN) + chan, DV_CHR, 0, 0, 0600, "rc%d.%d", dvp->id_unit, chan); #endif } rcb->rcb_probed = RC_ATTACHED; if (!rc_started) { register_swi(SWI_TTY, rcpoll); rc_wakeup((void *)NULL); rc_started = 0; } return 1; } /* RC interrupt handling */ void rcintr(unit) int unit; { register struct rc_softc *rcb = &rc_softc[unit]; register struct rc_chans *rc; register int nec, resid; register u_char val, iack, bsr, ucnt, *optr; int good_data, t_state; if (rcb->rcb_probed != RC_ATTACHED) { printf("rc%d: bogus interrupt\n", unit); return; } nec = rcb->rcb_addr; bsr = ~(rcin(RC_BSR)); if (!(bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT))) { printf("rc%d: extra interrupt\n", unit); rcout(CD180_EOIR, 0); return; } while (bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT)) { #ifdef RCDEBUG_DETAILED printf("rc%d: intr (%02x) %s%s%s%s\n", unit, bsr, (bsr & RC_BSR_TOUT)?"TOUT ":"", (bsr & RC_BSR_RXINT)?"RXINT ":"", (bsr & RC_BSR_TXINT)?"TXINT ":"", (bsr & RC_BSR_MOINT)?"MOINT":""); #endif if (bsr & RC_BSR_TOUT) { printf("rc%d: hardware failure, reset board\n", unit); rcout(RC_CTOUT, 0); rc_reinit(rcb); return; } if (bsr & RC_BSR_RXINT) { iack = rcin(RC_PILR_RX); good_data = (iack == (GIVR_IT_RGDI | RC_FAKEID)); if (!good_data && iack != (GIVR_IT_REI | RC_FAKEID)) { printf("rc%d: fake rxint: %02x\n", unit, iack); goto more_intrs; } rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH); t_state = rc->rc_tp->t_state; /* Do RTS flow control stuff */ if ( (rc->rc_flags & RC_RTSFLOW) || !(t_state & TS_ISOPEN) ) { if ( ( !(t_state & TS_ISOPEN) || (t_state & TS_TBLOCK) ) && (rc->rc_msvr & MSVR_RTS) ) rcout(CD180_MSVR, rc->rc_msvr &= ~MSVR_RTS); else if (!(rc->rc_msvr & MSVR_RTS)) rcout(CD180_MSVR, rc->rc_msvr |= MSVR_RTS); } ucnt = rcin(CD180_RDCR) & 0xF; resid = 0; if (t_state & TS_ISOPEN) { /* check for input buffer overflow */ if ((rc->rc_iptr + ucnt) >= rc->rc_bufend) { resid = ucnt; ucnt = rc->rc_bufend - rc->rc_iptr; resid -= ucnt; if (!(rc->rc_flags & RC_WAS_BUFOVFL)) { rc->rc_flags |= RC_WAS_BUFOVFL; rc_scheduled_event++; } } optr = rc->rc_iptr; /* check foor good data */ if (good_data) { while (ucnt-- > 0) { val = rcin(CD180_RDR); optr[0] = val; optr[INPUT_FLAGS_SHIFT] = 0; optr++; rc_scheduled_event++; if (val != 0 && val == rc->rc_hotchar) setsofttty(); } } else { /* Store also status data */ while (ucnt-- > 0) { iack = rcin(CD180_RCSR); if (iack & RCSR_Timeout) break; if ( (iack & RCSR_OE) && !(rc->rc_flags & RC_WAS_SILOVFL)) { rc->rc_flags |= RC_WAS_SILOVFL; rc_scheduled_event++; } val = rcin(CD180_RDR); /* Don't store PE if IGNPAR and BREAK if IGNBRK, this hack allows "raw" tty optimization works even if IGN* is set. */ if ( !(iack & (RCSR_PE|RCSR_FE|RCSR_Break)) || (!(iack & (RCSR_PE|RCSR_FE)) || !(rc->rc_tp->t_iflag & IGNPAR)) && (!(iack & RCSR_Break) || !(rc->rc_tp->t_iflag & IGNBRK))) { if ( (iack & (RCSR_PE|RCSR_FE)) && (t_state & TS_CAN_BYPASS_L_RINT) && ((iack & RCSR_FE) || (iack & RCSR_PE) && (rc->rc_tp->t_iflag & INPCK))) val = 0; else if (val != 0 && val == rc->rc_hotchar) setsofttty(); optr[0] = val; optr[INPUT_FLAGS_SHIFT] = iack; optr++; rc_scheduled_event++; } } } rc->rc_iptr = optr; rc->rc_flags |= RC_DORXFER; } else resid = ucnt; /* Clear FIFO if necessary */ while (resid-- > 0) { if (!good_data) iack = rcin(CD180_RCSR); else iack = 0; if (iack & RCSR_Timeout) break; (void) rcin(CD180_RDR); } goto more_intrs; } if (bsr & RC_BSR_MOINT) { iack = rcin(RC_PILR_MODEM); if (iack != (GIVR_IT_MSCI | RC_FAKEID)) { printf("rc%d: fake moint: %02x\n", unit, iack); goto more_intrs; } rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH); iack = rcin(CD180_MCR); rc->rc_msvr = rcin(CD180_MSVR); rcout(CD180_MCR, 0); #ifdef RCDEBUG printrcflags(rc, "moint"); #endif if (rc->rc_flags & RC_CTSFLOW) { if (rc->rc_msvr & MSVR_CTS) rc->rc_flags |= RC_SEND_RDY; else rc->rc_flags &= ~RC_SEND_RDY; } else rc->rc_flags |= RC_SEND_RDY; if ((iack & MCR_CDchg) && !(rc->rc_flags & RC_MODCHG)) { rc_scheduled_event += LOTS_OF_EVENTS; rc->rc_flags |= RC_MODCHG; setsofttty(); } goto more_intrs; } if (bsr & RC_BSR_TXINT) { iack = rcin(RC_PILR_TX); if (iack != (GIVR_IT_TDI | RC_FAKEID)) { printf("rc%d: fake txint: %02x\n", unit, iack); goto more_intrs; } rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH); if ( (rc->rc_flags & RC_OSUSP) || !(rc->rc_flags & RC_SEND_RDY) ) goto more_intrs; /* Handle breaks and other stuff */ if (rc->rc_pendcmd) { rcout(CD180_COR2, rc->rc_cor2 |= COR2_ETC); rcout(CD180_TDR, CD180_C_ESC); rcout(CD180_TDR, rc->rc_pendcmd); rcout(CD180_COR2, rc->rc_cor2 &= ~COR2_ETC); rc->rc_pendcmd = 0; goto more_intrs; } optr = rc->rc_optr; resid = rc->rc_obufend - optr; if (resid > CD180_NFIFO) resid = CD180_NFIFO; while (resid-- > 0) rcout(CD180_TDR, *optr++); rc->rc_optr = optr; /* output completed? */ if (optr >= rc->rc_obufend) { rcout(CD180_IER, rc->rc_ier &= ~IER_TxRdy); #ifdef RCDEBUG printf("rc%d/%d: output completed\n", unit, rc->rc_chan); #endif if (!(rc->rc_flags & RC_DOXXFER)) { rc_scheduled_event += LOTS_OF_EVENTS; rc->rc_flags |= RC_DOXXFER; setsofttty(); } } } more_intrs: rcout(CD180_EOIR, 0); /* end of interrupt */ rcout(RC_CTOUT, 0); bsr = ~(rcin(RC_BSR)); } } /* Feed characters to output buffer */ static void rc_start(tp) register struct tty *tp; { register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)]; register int nec = rc->rc_rcb->rcb_addr, s; if (rc->rc_flags & RC_OSBUSY) return; s = spltty(); rc->rc_flags |= RC_OSBUSY; disable_intr(); if (tp->t_state & TS_TTSTOP) rc->rc_flags |= RC_OSUSP; else rc->rc_flags &= ~RC_OSUSP; /* Do RTS flow control stuff */ if ( (rc->rc_flags & RC_RTSFLOW) && (tp->t_state & TS_TBLOCK) && (rc->rc_msvr & MSVR_RTS) ) { rcout(CD180_CAR, rc->rc_chan); rcout(CD180_MSVR, rc->rc_msvr &= ~MSVR_RTS); } else if (!(rc->rc_msvr & MSVR_RTS)) { rcout(CD180_CAR, rc->rc_chan); rcout(CD180_MSVR, rc->rc_msvr |= MSVR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) goto out; #ifdef RCDEBUG printrcflags(rc, "rcstart"); #endif ttwwakeup(tp); #ifdef RCDEBUG printf("rcstart: outq = %d obuf = %d\n", tp->t_outq.c_cc, rc->rc_obufend - rc->rc_optr); #endif if (tp->t_state & TS_BUSY) goto out; /* output still in progress ... */ if (tp->t_outq.c_cc > 0) { u_int ocnt; tp->t_state |= TS_BUSY; ocnt = q_to_b(&tp->t_outq, rc->rc_obuf, sizeof rc->rc_obuf); disable_intr(); rc->rc_optr = rc->rc_obuf; rc->rc_obufend = rc->rc_optr + ocnt; enable_intr(); if (!(rc->rc_ier & IER_TxRdy)) { #ifdef RCDEBUG printf("rc%d/%d: rcstart enable txint\n", rc->rc_rcb->rcb_unit, rc->rc_chan); #endif rcout(CD180_CAR, rc->rc_chan); rcout(CD180_IER, rc->rc_ier |= IER_TxRdy); } } out: rc->rc_flags &= ~RC_OSBUSY; (void) splx(s); } /* Handle delayed events. */ void rcpoll() { register struct rc_chans *rc; register struct rc_softc *rcb; register u_char *tptr, *eptr; register struct tty *tp; register int chan, icnt, nec, unit; if (rc_scheduled_event == 0) return; repeat: for (unit = 0; unit < NRC; unit++) { rcb = &rc_softc[unit]; rc = rcb->rcb_baserc; nec = rc->rc_rcb->rcb_addr; for (chan = 0; chan < CD180_NCHAN; rc++, chan++) { tp = rc->rc_tp; #ifdef RCDEBUG if (rc->rc_flags & (RC_DORXFER|RC_DOXXFER|RC_MODCHG| RC_WAS_BUFOVFL|RC_WAS_SILOVFL)) printrcflags(rc, "rcevent"); #endif if (rc->rc_flags & RC_WAS_BUFOVFL) { disable_intr(); rc->rc_flags &= ~RC_WAS_BUFOVFL; rc_scheduled_event--; enable_intr(); printf("rc%d/%d: interrupt-level buffer overflow\n", unit, chan); } if (rc->rc_flags & RC_WAS_SILOVFL) { disable_intr(); rc->rc_flags &= ~RC_WAS_SILOVFL; rc_scheduled_event--; enable_intr(); printf("rc%d/%d: silo overflow\n", unit, chan); } if (rc->rc_flags & RC_MODCHG) { disable_intr(); rc->rc_flags &= ~RC_MODCHG; rc_scheduled_event -= LOTS_OF_EVENTS; enable_intr(); (*linesw[tp->t_line].l_modem)(tp, !!(rc->rc_msvr & MSVR_CD)); } if (rc->rc_flags & RC_DORXFER) { disable_intr(); rc->rc_flags &= ~RC_DORXFER; eptr = rc->rc_iptr; if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) tptr = &rc->rc_ibuf[RC_IBUFSIZE]; else tptr = rc->rc_ibuf; icnt = eptr - tptr; if (icnt > 0) { if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) { rc->rc_iptr = rc->rc_ibuf; rc->rc_bufend = &rc->rc_ibuf[RC_IBUFSIZE]; rc->rc_hiwat = &rc->rc_ibuf[RC_IHIGHWATER]; } else { rc->rc_iptr = &rc->rc_ibuf[RC_IBUFSIZE]; rc->rc_bufend = &rc->rc_ibuf[2 * RC_IBUFSIZE]; rc->rc_hiwat = &rc->rc_ibuf[RC_IBUFSIZE + RC_IHIGHWATER]; } if ( (rc->rc_flags & RC_RTSFLOW) && (tp->t_state & TS_ISOPEN) && !(tp->t_state & TS_TBLOCK) && !(rc->rc_msvr & MSVR_RTS) ) { rcout(CD180_CAR, chan); rcout(CD180_MSVR, rc->rc_msvr |= MSVR_RTS); } rc_scheduled_event -= icnt; } enable_intr(); if (icnt <= 0 || !(tp->t_state & TS_ISOPEN)) goto done1; if ( (tp->t_state & TS_CAN_BYPASS_L_RINT) && !(tp->t_state & TS_LOCAL)) { if ((tp->t_rawq.c_cc + icnt) >= RB_I_HIGH_WATER && ((rc->rc_flags & RC_RTSFLOW) || (tp->t_iflag & IXOFF)) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += icnt; tk_rawcc += icnt; tp->t_rawcc += icnt; if (b_to_q(tptr, icnt, &tp->t_rawq)) printf("rc%d/%d: tty-level buffer overflow\n", unit, chan); ttwakeup(tp); if ((tp->t_state & TS_TTSTOP) && ((tp->t_iflag & IXANY) || (tp->t_cc[VSTART] == tp->t_cc[VSTOP]))) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; rc_start(tp); } } else { for (; tptr < eptr; tptr++) (*linesw[tp->t_line].l_rint) (tptr[0] | rc_rcsrt[tptr[INPUT_FLAGS_SHIFT] & 0xF], tp); } done1: ; } if (rc->rc_flags & RC_DOXXFER) { disable_intr(); rc_scheduled_event -= LOTS_OF_EVENTS; rc->rc_flags &= ~RC_DOXXFER; rc->rc_tp->t_state &= ~TS_BUSY; enable_intr(); (*linesw[tp->t_line].l_start)(tp); } } if (rc_scheduled_event == 0) break; } if (rc_scheduled_event >= LOTS_OF_EVENTS) goto repeat; } static void rcstop(tp, rw) register struct tty *tp; int rw; { register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)]; u_char *tptr, *eptr; #ifdef RCDEBUG printf("rc%d/%d: rcstop %s%s\n", rc->rc_rcb->rcb_unit, rc->rc_chan, (rw & FWRITE)?"FWRITE ":"", (rw & FREAD)?"FREAD":""); #endif if (rw & FWRITE) rc_discard_output(rc); disable_intr(); if (rw & FREAD) { rc->rc_flags &= ~RC_DORXFER; eptr = rc->rc_iptr; if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) { tptr = &rc->rc_ibuf[RC_IBUFSIZE]; rc->rc_iptr = &rc->rc_ibuf[RC_IBUFSIZE]; } else { tptr = rc->rc_ibuf; rc->rc_iptr = rc->rc_ibuf; } rc_scheduled_event -= eptr - tptr; } if (tp->t_state & TS_TTSTOP) rc->rc_flags |= RC_OSUSP; else rc->rc_flags &= ~RC_OSUSP; enable_intr(); } static int rcopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register struct rc_chans *rc; register struct tty *tp; int unit, nec, s, error = 0; unit = GET_UNIT(dev); if (unit >= NRC * CD180_NCHAN) return ENXIO; if (rc_softc[unit / CD180_NCHAN].rcb_probed != RC_ATTACHED) return ENXIO; rc = &rc_chans[unit]; tp = rc->rc_tp; nec = rc->rc_rcb->rcb_addr; #ifdef RCDEBUG printf("rc%d/%d: rcopen: dev %x\n", rc->rc_rcb->rcb_unit, unit, dev); #endif s = spltty(); again: while (rc->rc_flags & RC_DTR_OFF) { error = tsleep(&(rc->rc_dtrwait), TTIPRI | PCATCH, "rcdtr", 0); if (error != 0) goto out; } if (tp->t_state & TS_ISOPEN) { if (CALLOUT(dev)) { if (!(rc->rc_flags & RC_ACTOUT)) { error = EBUSY; goto out; } } else { if (rc->rc_flags & RC_ACTOUT) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } if (error = tsleep(&rc->rc_rcb, TTIPRI|PCATCH, "rcbi", 0)) goto out; goto again; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { tp->t_oproc = rc_start; tp->t_param = rc_param; tp->t_dev = dev; if (CALLOUT(dev)) tp->t_cflag |= CLOCAL; else tp->t_cflag &= ~CLOCAL; error = rc_param(tp, &tp->t_termios); if (error) goto out; (void) rc_modctl(rc, TIOCM_RTS|TIOCM_DTR, DMSET); - ttsetwater(tp); - if ((rc->rc_msvr & MSVR_CD) || CALLOUT(dev)) (*linesw[tp->t_line].l_modem)(tp, 1); } if (!(tp->t_state & TS_CARR_ON) && !CALLOUT(dev) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { rc->rc_dcdwaits++; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "rcdcd", 0); rc->rc_dcdwaits--; if (error != 0) goto out; goto again; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, rc); if ((tp->t_state & TS_ISOPEN) && CALLOUT(dev)) rc->rc_flags |= RC_ACTOUT; out: (void) splx(s); if(rc->rc_dcdwaits == 0 && !(tp->t_state & TS_ISOPEN)) rc_hardclose(rc); return error; } static int rcclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register struct rc_chans *rc; register struct tty *tp; int s, unit = GET_UNIT(dev); if (unit >= NRC * CD180_NCHAN) return ENXIO; rc = &rc_chans[unit]; tp = rc->rc_tp; #ifdef RCDEBUG printf("rc%d/%d: rcclose dev %x\n", rc->rc_rcb->rcb_unit, unit, dev); #endif s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, rc); rcstop(tp, FREAD | FWRITE); rc_hardclose(rc); ttyclose(tp); splx(s); return 0; } static void rc_hardclose(rc) register struct rc_chans *rc; { register int s, nec = rc->rc_rcb->rcb_addr; register struct tty *tp = rc->rc_tp; s = spltty(); rcout(CD180_CAR, rc->rc_chan); /* Disable rx/tx intrs */ rcout(CD180_IER, rc->rc_ier = 0); if ( (tp->t_cflag & HUPCL) || !(rc->rc_flags & RC_ACTOUT) && !(rc->rc_msvr & MSVR_CD) && !(tp->t_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN) ) { CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_ResetChan); WAITFORCCR(rc->rc_rcb->rcb_unit, rc->rc_chan); (void) rc_modctl(rc, TIOCM_RTS, DMSET); if (rc->rc_dtrwait) { timeout(rc_dtrwakeup, rc, rc->rc_dtrwait); rc->rc_flags |= RC_DTR_OFF; } } rc->rc_flags &= ~RC_ACTOUT; wakeup((caddr_t) &rc->rc_rcb); /* wake bi */ wakeup(TSA_CARR_ON(tp)); (void) splx(s); } /* Read from line */ static int rcread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct tty *tp = rc_chans[GET_UNIT(dev)].rc_tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } /* Write to line */ static int rcwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct tty *tp = rc_chans[GET_UNIT(dev)].rc_tp; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } /* Reset the bastard */ static void rc_hwreset(unit, nec, chipid) register int unit, nec; unsigned int chipid; { CCRCMD(unit, -1, CCR_HWRESET); /* Hardware reset */ DELAY(20000); WAITFORCCR(unit, -1); rcout(RC_CTOUT, 0); /* Clear timeout */ rcout(CD180_GIVR, chipid); rcout(CD180_GICR, 0); /* Set Prescaler Registers (1 msec) */ rcout(CD180_PPRL, ((RC_OSCFREQ + 999) / 1000) & 0xFF); rcout(CD180_PPRH, ((RC_OSCFREQ + 999) / 1000) >> 8); /* Initialize Priority Interrupt Level Registers */ rcout(CD180_PILR1, RC_PILR_MODEM); rcout(CD180_PILR2, RC_PILR_TX); rcout(CD180_PILR3, RC_PILR_RX); /* Reset DTR */ rcout(RC_DTREG, ~0); } /* Set channel parameters */ static int rc_param(tp, ts) register struct tty *tp; struct termios *ts; { register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)]; register int nec = rc->rc_rcb->rcb_addr; int idivs, odivs, s, val, cflag, iflag, lflag, inpflow; if ( ts->c_ospeed < 0 || ts->c_ospeed > 76800 || ts->c_ispeed < 0 || ts->c_ispeed > 76800 ) return (EINVAL); if (ts->c_ispeed == 0) ts->c_ispeed = ts->c_ospeed; odivs = RC_BRD(ts->c_ospeed); idivs = RC_BRD(ts->c_ispeed); s = spltty(); /* Select channel */ rcout(CD180_CAR, rc->rc_chan); /* If speed == 0, hangup line */ if (ts->c_ospeed == 0) { CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_ResetChan); WAITFORCCR(rc->rc_rcb->rcb_unit, rc->rc_chan); (void) rc_modctl(rc, TIOCM_DTR, DMBIC); } tp->t_state &= ~TS_CAN_BYPASS_L_RINT; cflag = ts->c_cflag; iflag = ts->c_iflag; lflag = ts->c_lflag; if (idivs > 0) { rcout(CD180_RBPRL, idivs & 0xFF); rcout(CD180_RBPRH, idivs >> 8); } if (odivs > 0) { rcout(CD180_TBPRL, odivs & 0xFF); rcout(CD180_TBPRH, odivs >> 8); } /* set timeout value */ if (ts->c_ispeed > 0) { int itm = ts->c_ispeed > 2400 ? 5 : 10000 / ts->c_ispeed + 1; if ( !(lflag & ICANON) && ts->c_cc[VMIN] != 0 && ts->c_cc[VTIME] != 0 && ts->c_cc[VTIME] * 10 > itm) itm = ts->c_cc[VTIME] * 10; rcout(CD180_RTPR, itm <= 255 ? itm : 255); } switch (cflag & CSIZE) { case CS5: val = COR1_5BITS; break; case CS6: val = COR1_6BITS; break; case CS7: val = COR1_7BITS; break; default: case CS8: val = COR1_8BITS; break; } if (cflag & PARENB) { val |= COR1_NORMPAR; if (cflag & PARODD) val |= COR1_ODDP; if (!(cflag & INPCK)) val |= COR1_Ignore; } else val |= COR1_Ignore; if (cflag & CSTOPB) val |= COR1_2SB; rcout(CD180_COR1, val); /* Set FIFO threshold */ val = ts->c_ospeed <= 4800 ? 1 : CD180_NFIFO / 2; inpflow = 0; if ( (iflag & IXOFF) && ( ts->c_cc[VSTOP] != _POSIX_VDISABLE && ( ts->c_cc[VSTART] != _POSIX_VDISABLE || (iflag & IXANY) ) ) ) { inpflow = 1; val |= COR3_SCDE|COR3_FCT; } rcout(CD180_COR3, val); /* Initialize on-chip automatic flow control */ val = 0; rc->rc_flags &= ~(RC_CTSFLOW|RC_SEND_RDY); if (cflag & CCTS_OFLOW) { rc->rc_flags |= RC_CTSFLOW; val |= COR2_CtsAE; } else rc->rc_flags |= RC_SEND_RDY; if (tp->t_state & TS_TTSTOP) rc->rc_flags |= RC_OSUSP; else rc->rc_flags &= ~RC_OSUSP; if (cflag & CRTS_IFLOW) rc->rc_flags |= RC_RTSFLOW; else rc->rc_flags &= ~RC_RTSFLOW; if (inpflow) { if (ts->c_cc[VSTART] != _POSIX_VDISABLE) rcout(CD180_SCHR1, ts->c_cc[VSTART]); rcout(CD180_SCHR2, ts->c_cc[VSTOP]); val |= COR2_TxIBE; if (iflag & IXANY) val |= COR2_IXM; } rcout(CD180_COR2, rc->rc_cor2 = val); CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); disc_optim(tp, ts, rc); /* modem ctl */ val = cflag & CLOCAL ? 0 : MCOR1_CDzd; if (cflag & CCTS_OFLOW) val |= MCOR1_CTSzd; rcout(CD180_MCOR1, val); val = cflag & CLOCAL ? 0 : MCOR2_CDod; if (cflag & CCTS_OFLOW) val |= MCOR2_CTSod; rcout(CD180_MCOR2, val); /* enable i/o and interrupts */ CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_XMTREN | ((cflag & CREAD) ? CCR_RCVREN : CCR_RCVRDIS)); WAITFORCCR(rc->rc_rcb->rcb_unit, rc->rc_chan); rc->rc_ier = cflag & CLOCAL ? 0 : IER_CD; if (cflag & CCTS_OFLOW) rc->rc_ier |= IER_CTS; if (cflag & CREAD) rc->rc_ier |= IER_RxData; if (tp->t_state & TS_BUSY) rc->rc_ier |= IER_TxRdy; if (ts->c_ospeed != 0) rc_modctl(rc, TIOCM_DTR, DMBIS); if ((cflag & CCTS_OFLOW) && (rc->rc_msvr & MSVR_CTS)) rc->rc_flags |= RC_SEND_RDY; rcout(CD180_IER, rc->rc_ier); (void) splx(s); return 0; } /* Re-initialize board after bogus interrupts */ static void rc_reinit(rcb) struct rc_softc *rcb; { register struct rc_chans *rc, *rce; register int nec; nec = rcb->rcb_addr; rc_hwreset(rcb->rcb_unit, nec, RC_FAKEID); rc = &rc_chans[rcb->rcb_unit * CD180_NCHAN]; rce = rc + CD180_NCHAN; for (; rc < rce; rc++) (void) rc_param(rc->rc_tp, &rc->rc_tp->t_termios); } static int rcioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; int flag; caddr_t data; struct proc *p; { register struct rc_chans *rc = &rc_chans[GET_UNIT(dev)]; register int s, error; struct tty *tp = rc->rc_tp; error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return (error); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, rc); if (error != ENOIOCTL) return (error); s = spltty(); switch (cmd) { case TIOCSBRK: rc->rc_pendcmd = CD180_C_SBRK; break; case TIOCCBRK: rc->rc_pendcmd = CD180_C_EBRK; break; case TIOCSDTR: (void) rc_modctl(rc, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void) rc_modctl(rc, TIOCM_DTR, DMBIC); break; case TIOCMGET: *(int *) data = rc_modctl(rc, 0, DMGET); break; case TIOCMSET: (void) rc_modctl(rc, *(int *) data, DMSET); break; case TIOCMBIC: (void) rc_modctl(rc, *(int *) data, DMBIC); break; case TIOCMBIS: (void) rc_modctl(rc, *(int *) data, DMBIS); break; case TIOCMSDTRWAIT: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } rc->rc_dtrwait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = rc->rc_dtrwait * 100 / hz; break; default: (void) splx(s); return ENOTTY; } (void) splx(s); return 0; } /* Modem control routines */ static int rc_modctl(rc, bits, cmd) register struct rc_chans *rc; int bits, cmd; { register int nec = rc->rc_rcb->rcb_addr; u_char *dtr = &rc->rc_rcb->rcb_dtr, msvr; rcout(CD180_CAR, rc->rc_chan); switch (cmd) { case DMSET: rcout(RC_DTREG, (bits & TIOCM_DTR) ? ~(*dtr |= 1 << rc->rc_chan) : ~(*dtr &= ~(1 << rc->rc_chan))); msvr = rcin(CD180_MSVR); if (bits & TIOCM_RTS) msvr |= MSVR_RTS; else msvr &= ~MSVR_RTS; if (bits & TIOCM_DTR) msvr |= MSVR_DTR; else msvr &= ~MSVR_DTR; rcout(CD180_MSVR, msvr); break; case DMBIS: if (bits & TIOCM_DTR) rcout(RC_DTREG, ~(*dtr |= 1 << rc->rc_chan)); msvr = rcin(CD180_MSVR); if (bits & TIOCM_RTS) msvr |= MSVR_RTS; if (bits & TIOCM_DTR) msvr |= MSVR_DTR; rcout(CD180_MSVR, msvr); break; case DMGET: bits = TIOCM_LE; msvr = rc->rc_msvr = rcin(CD180_MSVR); if (msvr & MSVR_RTS) bits |= TIOCM_RTS; if (msvr & MSVR_CTS) bits |= TIOCM_CTS; if (msvr & MSVR_DSR) bits |= TIOCM_DSR; if (msvr & MSVR_DTR) bits |= TIOCM_DTR; if (msvr & MSVR_CD) bits |= TIOCM_CD; if (~rcin(RC_RIREG) & (1 << rc->rc_chan)) bits |= TIOCM_RI; return bits; case DMBIC: if (bits & TIOCM_DTR) rcout(RC_DTREG, ~(*dtr &= ~(1 << rc->rc_chan))); msvr = rcin(CD180_MSVR); if (bits & TIOCM_RTS) msvr &= ~MSVR_RTS; if (bits & TIOCM_DTR) msvr &= ~MSVR_DTR; rcout(CD180_MSVR, msvr); break; } rc->rc_msvr = rcin(CD180_MSVR); return 0; } /* Test the board. */ int rc_test(nec, unit) register int nec; int unit; { int chan = 0; int i = 0, rcnt, old_level; unsigned int iack, chipid; unsigned short divs; static u_char ctest[] = "\377\125\252\045\244\0\377"; #define CTLEN 8 #define ERR(s) { \ printf("rc%d: ", unit); printf s ; printf("\n"); \ (void) splx(old_level); return 1; } struct rtest { u_char txbuf[CD180_NFIFO]; /* TX buffer */ u_char rxbuf[CD180_NFIFO]; /* RX buffer */ int rxptr; /* RX pointer */ int txptr; /* TX pointer */ } tchans[CD180_NCHAN]; old_level = spltty(); chipid = RC_FAKEID; /* First, reset board to inital state */ rc_hwreset(unit, nec, chipid); divs = RC_BRD(19200); /* Initialize channels */ for (chan = 0; chan < CD180_NCHAN; chan++) { /* Select and reset channel */ rcout(CD180_CAR, chan); CCRCMD(unit, chan, CCR_ResetChan); WAITFORCCR(unit, chan); /* Set speed */ rcout(CD180_RBPRL, divs & 0xFF); rcout(CD180_RBPRH, divs >> 8); rcout(CD180_TBPRL, divs & 0xFF); rcout(CD180_TBPRH, divs >> 8); /* set timeout value */ rcout(CD180_RTPR, 0); /* Establish local loopback */ rcout(CD180_COR1, COR1_NOPAR | COR1_8BITS | COR1_1SB); rcout(CD180_COR2, COR2_LLM); rcout(CD180_COR3, CD180_NFIFO); CCRCMD(unit, chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); CCRCMD(unit, chan, CCR_RCVREN | CCR_XMTREN); WAITFORCCR(unit, chan); rcout(CD180_MSVR, MSVR_RTS); /* Fill TXBUF with test data */ for (i = 0; i < CD180_NFIFO; i++) { tchans[chan].txbuf[i] = ctest[i]; tchans[chan].rxbuf[i] = 0; } tchans[chan].txptr = tchans[chan].rxptr = 0; /* Now, start transmit */ rcout(CD180_IER, IER_TxMpty|IER_RxData); } /* Pseudo-interrupt poll stuff */ for (rcnt = 10000; rcnt-- > 0; rcnt--) { i = ~(rcin(RC_BSR)); if (i & RC_BSR_TOUT) ERR(("BSR timeout bit set\n")) else if (i & RC_BSR_TXINT) { iack = rcin(RC_PILR_TX); if (iack != (GIVR_IT_TDI | chipid)) ERR(("Bad TX intr ack (%02x != %02x)\n", iack, GIVR_IT_TDI | chipid)); chan = (rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH; /* If no more data to transmit, disable TX intr */ if (tchans[chan].txptr >= CD180_NFIFO) { iack = rcin(CD180_IER); rcout(CD180_IER, iack & ~IER_TxMpty); } else { for (iack = tchans[chan].txptr; iack < CD180_NFIFO; iack++) rcout(CD180_TDR, tchans[chan].txbuf[iack]); tchans[chan].txptr = iack; } rcout(CD180_EOIR, 0); } else if (i & RC_BSR_RXINT) { u_char ucnt; iack = rcin(RC_PILR_RX); if (iack != (GIVR_IT_RGDI | chipid) && iack != (GIVR_IT_REI | chipid)) ERR(("Bad RX intr ack (%02x != %02x)\n", iack, GIVR_IT_RGDI | chipid)) chan = (rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH; ucnt = rcin(CD180_RDCR) & 0xF; while (ucnt-- > 0) { iack = rcin(CD180_RCSR); if (iack & RCSR_Timeout) break; if (iack & 0xF) ERR(("Bad char chan %d (RCSR = %02X)\n", chan, iack)) if (tchans[chan].rxptr > CD180_NFIFO) ERR(("Got extra chars chan %d\n", chan)) tchans[chan].rxbuf[tchans[chan].rxptr++] = rcin(CD180_RDR); } rcout(CD180_EOIR, 0); } rcout(RC_CTOUT, 0); for (iack = chan = 0; chan < CD180_NCHAN; chan++) if (tchans[chan].rxptr >= CD180_NFIFO) iack++; if (iack == CD180_NCHAN) break; } for (chan = 0; chan < CD180_NCHAN; chan++) { /* Select and reset channel */ rcout(CD180_CAR, chan); CCRCMD(unit, chan, CCR_ResetChan); } if (!rcnt) ERR(("looses characters during local loopback\n")) /* Now, check data */ for (chan = 0; chan < CD180_NCHAN; chan++) for (i = 0; i < CD180_NFIFO; i++) if (ctest[i] != tchans[chan].rxbuf[i]) ERR(("data mismatch chan %d ptr %d (%d != %d)\n", chan, i, ctest[i], tchans[chan].rxbuf[i])) (void) splx(old_level); return 0; } #ifdef RCDEBUG static void printrcflags(rc, comment) struct rc_chans *rc; char *comment; { u_short f = rc->rc_flags; register int nec = rc->rc_rcb->rcb_addr; printf("rc%d/%d: %s flags: %s%s%s%s%s%s%s%s%s%s%s%s\n", rc->rc_rcb->rcb_unit, rc->rc_chan, comment, (f & RC_DTR_OFF)?"DTR_OFF " :"", (f & RC_ACTOUT) ?"ACTOUT " :"", (f & RC_RTSFLOW)?"RTSFLOW " :"", (f & RC_CTSFLOW)?"CTSFLOW " :"", (f & RC_DORXFER)?"DORXFER " :"", (f & RC_DOXXFER)?"DOXXFER " :"", (f & RC_MODCHG) ?"MODCHG " :"", (f & RC_OSUSP) ?"OSUSP " :"", (f & RC_OSBUSY) ?"OSBUSY " :"", (f & RC_WAS_BUFOVFL) ?"BUFOVFL " :"", (f & RC_WAS_SILOVFL) ?"SILOVFL " :"", (f & RC_SEND_RDY) ?"SEND_RDY":""); rcout(CD180_CAR, rc->rc_chan); printf("rc%d/%d: msvr %02x ier %02x ccsr %02x\n", rc->rc_rcb->rcb_unit, rc->rc_chan, rcin(CD180_MSVR), rcin(CD180_IER), rcin(CD180_CCSR)); } #endif /* RCDEBUG */ static struct tty * rcdevtotty(dev) dev_t dev; { int unit; unit = GET_UNIT(dev); if (unit >= NRC * CD180_NCHAN) return NULL; return (&rc_tty[unit]); } static void rc_dtrwakeup(chan) void *chan; { struct rc_chans *rc; rc = (struct rc_chans *)chan; rc->rc_flags &= ~RC_DTR_OFF; wakeup(&rc->rc_dtrwait); } static void rc_discard_output(rc) struct rc_chans *rc; { disable_intr(); if (rc->rc_flags & RC_DOXXFER) { rc_scheduled_event -= LOTS_OF_EVENTS; rc->rc_flags &= ~RC_DOXXFER; } rc->rc_optr = rc->rc_obufend; rc->rc_tp->t_state &= ~TS_BUSY; enable_intr(); ttwwakeup(rc->rc_tp); } static void rc_wakeup(chan) void *chan; { timeout(rc_wakeup, (caddr_t)NULL, 1); if (rc_scheduled_event != 0) { int s; s = splsofttty(); rcpoll(); splx(s); } } static void disc_optim(tp, t, rc) struct tty *tp; struct termios *t; struct rc_chans *rc; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; rc->rc_hotchar = linesw[tp->t_line].l_hotchar; } static void rc_wait0(nec, unit, chan, line) int nec, unit, chan, line; { int rcnt; for (rcnt = 50; rcnt && rcin(CD180_CCR); rcnt--) DELAY(30); if (rcnt == 0) printf("rc%d/%d: channel command timeout, rc.c line: %d\n", unit, chan, line); } static rc_devsw_installed = 0; static void rc_drvinit(void *unused) { dev_t dev; if( ! rc_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&rc_cdevsw, NULL); rc_devsw_installed = 1; } } SYSINIT(rcdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,rc_drvinit,NULL) #endif /* NRC */ diff --git a/sys/dev/rp/rp.c b/sys/dev/rp/rp.c index e26c46c9482e..847660e70c92 100644 --- a/sys/dev/rp/rp.c +++ b/sys/dev/rp/rp.c @@ -1,2059 +1,2058 @@ /* * Copyright (c) Comtrol Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted prodived that the follwoing conditions * are met. * 1. Redistributions of source code must retain the above copyright * notive, this list of conditions and the following disclainer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials prodided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Comtrol Corporation. * 4. The name of Comtrol Corporation may not be used to endorse or * promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL COMTROL CORPORATION BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * rp.c - for RocketPort FreeBSD */ #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #define ROCKET_C #include #include #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif static Byte_t RData[RDATASIZE] = { 0x00, 0x09, 0xf6, 0x82, 0x02, 0x09, 0x86, 0xfb, 0x04, 0x09, 0x00, 0x0a, 0x06, 0x09, 0x01, 0x0a, 0x08, 0x09, 0x8a, 0x13, 0x0a, 0x09, 0xc5, 0x11, 0x0c, 0x09, 0x86, 0x85, 0x0e, 0x09, 0x20, 0x0a, 0x10, 0x09, 0x21, 0x0a, 0x12, 0x09, 0x41, 0xff, 0x14, 0x09, 0x82, 0x00, 0x16, 0x09, 0x82, 0x7b, 0x18, 0x09, 0x8a, 0x7d, 0x1a, 0x09, 0x88, 0x81, 0x1c, 0x09, 0x86, 0x7a, 0x1e, 0x09, 0x84, 0x81, 0x20, 0x09, 0x82, 0x7c, 0x22, 0x09, 0x0a, 0x0a }; static Byte_t RRegData[RREGDATASIZE]= { 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */ 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */ 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */ 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */ 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */ 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */ 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */ 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */ 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */ 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */ 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */ 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */ 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */ }; static CONTROLLER_T sController[CTL_SIZE] = { {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}} }; #if 0 /* IRQ number to MUDBAC register 2 mapping */ Byte_t sIRQMap[16] = { 0,0,0,0x10,0x20,0x30,0,0,0,0x40,0x50,0x60,0x70,0,0,0x80 }; #endif static Byte_t sBitMapClrTbl[8] = { 0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f }; static Byte_t sBitMapSetTbl[8] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 }; /*************************************************************************** Function: sInitController Purpose: Initialization of controller global registers and controller structure. Call: sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize, IRQNum,Frequency,PeriodicOnly) CONTROLLER_T *CtlP; Ptr to controller structure int CtlNum; Controller number ByteIO_t MudbacIO; Mudbac base I/O address. ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. This list must be in the order the AIOPs will be found on the controller. Once an AIOP in the list is not found, it is assumed that there are no more AIOPs on the controller. int AiopIOListSize; Number of addresses in AiopIOList int IRQNum; Interrupt Request number. Can be any of the following: 0: Disable global interrupts 3: IRQ 3 4: IRQ 4 5: IRQ 5 9: IRQ 9 10: IRQ 10 11: IRQ 11 12: IRQ 12 15: IRQ 15 Byte_t Frequency: A flag identifying the frequency of the periodic interrupt, can be any one of the following: FREQ_DIS - periodic interrupt disabled FREQ_137HZ - 137 Hertz FREQ_69HZ - 69 Hertz FREQ_34HZ - 34 Hertz FREQ_17HZ - 17 Hertz FREQ_9HZ - 9 Hertz FREQ_4HZ - 4 Hertz If IRQNum is set to 0 the Frequency parameter is overidden, it is forced to a value of FREQ_DIS. int PeriodicOnly: TRUE if all interrupts except the periodic interrupt are to be blocked. FALSE is both the periodic interrupt and other channel interrupts are allowed. If IRQNum is set to 0 the PeriodicOnly parameter is overidden, it is forced to a value of FALSE. Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller initialization failed. Comments: If periodic interrupts are to be disabled but AIOP interrupts are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE. If interrupts are to be completely disabled set IRQNum to 0. Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an invalid combination. This function performs initialization of global interrupt modes, but it does not actually enable global interrupts. To enable and disable global interrupts use functions sEnGlobalInt() and sDisGlobalInt(). Enabling of global interrupts is normally not done until all other initializations are complete. Even if interrupts are globally enabled, they must also be individually enabled for each channel that is to generate interrupts. Warnings: No range checking on any of the parameters is done. No context switches are allowed while executing this function. After this function all AIOPs on the controller are disabled, they can be enabled with sEnAiop(). */ int sInitController( CONTROLLER_T *CtlP, int CtlNum, ByteIO_t MudbacIO, ByteIO_t *AiopIOList, int AiopIOListSize, int IRQNum, Byte_t Frequency, int PeriodicOnly) { int i; ByteIO_t io; CtlP->CtlNum = CtlNum; CtlP->BusType = isISA; CtlP->CtlID = CTLID_0001; /* controller release 1 */ CtlP->MBaseIO = MudbacIO; CtlP->MReg1IO = MudbacIO + 1; CtlP->MReg2IO = MudbacIO + 2; CtlP->MReg3IO = MudbacIO + 3; #if 1 CtlP->MReg2 = 0; /* interrupt disable */ CtlP->MReg3 = 0; /* no periodic interrupts */ #else if(sIRQMap[IRQNum] == 0) /* interrupts globally disabled */ { CtlP->MReg2 = 0; /* interrupt disable */ CtlP->MReg3 = 0; /* no periodic interrupts */ } else { CtlP->MReg2 = sIRQMap[IRQNum]; /* set IRQ number */ CtlP->MReg3 = Frequency; /* set frequency */ if(PeriodicOnly) /* periodic interrupt only */ { CtlP->MReg3 |= PERIODIC_ONLY; } } #endif sOutB(CtlP->MReg2IO,CtlP->MReg2); sOutB(CtlP->MReg3IO,CtlP->MReg3); sControllerEOI(CtlP); /* clear EOI if warm init */ /* Init AIOPs */ CtlP->NumAiop = 0; for(i=0; i < AiopIOListSize; i++) { io = AiopIOList[i]; CtlP->AiopIO[i] = (WordIO_t)io; CtlP->AiopIntChanIO[i] = io + _INT_CHAN; sOutB(CtlP->MReg2IO,CtlP->MReg2 | (i & 0x03)); /* AIOP index */ sOutB(MudbacIO,(Byte_t)(io >> 6)); /* set up AIOP I/O in MUDBAC */ sEnAiop(CtlP,i); /* enable the AIOP */ CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ { sDisAiop(CtlP,i); /* disable AIOP */ break; /* done looking for AIOPs */ } CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */ sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE); /* clock prescaler */ sOutB(io + _INDX_DATA,CLOCK_PRESC); CtlP->NumAiop++; /* bump count of AIOPs */ sDisAiop(CtlP,i); /* disable AIOP */ } if(CtlP->NumAiop == 0) return(-1); else return(CtlP->NumAiop); } int sPCIInitController( CONTROLLER_T *CtlP, int CtlNum, ByteIO_t *AiopIOList, int AiopIOListSize, int IRQNum, Byte_t Frequency, int PeriodicOnly) { int i; ByteIO_t io; CtlP->CtlNum = CtlNum; CtlP->BusType = isPCI; CtlP->CtlID = CTLID_0001; /* controller release 1 */ CtlP->PCIIO = (WordIO_t)((ByteIO_t)AiopIOList[0] + _PCI_INT_FUNC); sPCIControllerEOI(CtlP); /* Init AIOPs */ CtlP->NumAiop = 0; for(i=0; i < AiopIOListSize; i++) { io = AiopIOList[i]; CtlP->AiopIO[i] = (WordIO_t)io; CtlP->AiopIntChanIO[i] = io + _INT_CHAN; CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ { break; /* done looking for AIOPs */ } CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */ sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE); /* clock prescaler */ sOutB(io + _INDX_DATA,CLOCK_PRESC); CtlP->NumAiop++; /* bump count of AIOPs */ } if(CtlP->NumAiop == 0) return(-1); else return(CtlP->NumAiop); } /*************************************************************************** Function: sReadAiopID Purpose: Read the AIOP idenfication number directly from an AIOP. Call: sReadAiopID(io) ByteIO_t io: AIOP base I/O address Return: int: Flag AIOPID_XXXX if a valid AIOP is found, where X is replace by an identifying number. Flag AIOPID_NULL if no valid AIOP is found Warnings: No context switches are allowed while executing this function. */ int sReadAiopID(ByteIO_t io) { Byte_t AiopID; /* ID byte from AIOP */ sOutB(io + _CMD_REG,RESET_ALL); /* reset AIOP */ sOutB(io + _CMD_REG,0x0); AiopID = sInB(io + _CHN_STAT0) & 0x07; if(AiopID == 0x06) return(1); else /* AIOP does not exist */ return(-1); } /*************************************************************************** Function: sReadAiopNumChan Purpose: Read the number of channels available in an AIOP directly from an AIOP. Call: sReadAiopNumChan(io) WordIO_t io: AIOP base I/O address Return: int: The number of channels available Comments: The number of channels is determined by write/reads from identical offsets within the SRAM address spaces for channels 0 and 4. If the channel 4 space is mirrored to channel 0 it is a 4 channel AIOP, otherwise it is an 8 channel. Warnings: No context switches are allowed while executing this function. */ int sReadAiopNumChan(WordIO_t io) { Word_t x; sOutDW((DWordIO_t)io + _INDX_ADDR,0x12340000L); /* write to chan 0 SRAM */ sOutW(io + _INDX_ADDR,0); /* read from SRAM, chan 0 */ x = sInW(io + _INDX_DATA); sOutW(io + _INDX_ADDR,0x4000); /* read from SRAM, chan 4 */ if(x != sInW(io + _INDX_DATA)) /* if different must be 8 chan */ return(8); else return(4); } /*************************************************************************** Function: sInitChan Purpose: Initialization of a channel and channel structure Call: sInitChan(CtlP,ChP,AiopNum,ChanNum) CONTROLLER_T *CtlP; Ptr to controller structure CHANNEL_T *ChP; Ptr to channel structure int AiopNum; AIOP number within controller int ChanNum; Channel number within AIOP Return: int: TRUE if initialization succeeded, FALSE if it fails because channel number exceeds number of channels available in AIOP. Comments: This function must be called before a channel can be used. Warnings: No range checking on any of the parameters is done. No context switches are allowed while executing this function. */ int sInitChan( CONTROLLER_T *CtlP, CHANNEL_T *ChP, int AiopNum, int ChanNum) { int i; WordIO_t AiopIO; WordIO_t ChIOOff; Byte_t *ChR; Word_t ChOff; static Byte_t R[4]; if(ChanNum >= CtlP->AiopNumChan[AiopNum]) return(FALSE); /* exceeds num chans in AIOP */ /* Channel, AIOP, and controller identifiers */ ChP->CtlP = CtlP; ChP->ChanID = CtlP->AiopID[AiopNum]; ChP->AiopNum = AiopNum; ChP->ChanNum = ChanNum; /* Global direct addresses */ AiopIO = CtlP->AiopIO[AiopNum]; ChP->Cmd = (ByteIO_t)AiopIO + _CMD_REG; ChP->IntChan = (ByteIO_t)AiopIO + _INT_CHAN; ChP->IntMask = (ByteIO_t)AiopIO + _INT_MASK; ChP->IndexAddr = (DWordIO_t)AiopIO + _INDX_ADDR; ChP->IndexData = AiopIO + _INDX_DATA; /* Channel direct addresses */ ChIOOff = AiopIO + ChP->ChanNum * 2; ChP->TxRxData = ChIOOff + _TD0; ChP->ChanStat = ChIOOff + _CHN_STAT0; ChP->TxRxCount = ChIOOff + _FIFO_CNT0; ChP->IntID = (ByteIO_t)AiopIO + ChP->ChanNum + _INT_ID0; /* Initialize the channel from the RData array */ for(i=0; i < RDATASIZE; i+=4) { R[0] = RData[i]; R[1] = RData[i+1] + 0x10 * ChanNum; R[2] = RData[i+2]; R[3] = RData[i+3]; sOutDW(ChP->IndexAddr,*((DWord_t *)&R[0])); } ChR = ChP->R; for(i=0; i < RREGDATASIZE; i+=4) { ChR[i] = RRegData[i]; ChR[i+1] = RRegData[i+1] + 0x10 * ChanNum; ChR[i+2] = RRegData[i+2]; ChR[i+3] = RRegData[i+3]; } /* Indexed registers */ ChOff = (Word_t)ChanNum * 0x1000; ChP->BaudDiv[0] = (Byte_t)(ChOff + _BAUD); ChP->BaudDiv[1] = (Byte_t)((ChOff + _BAUD) >> 8); ChP->BaudDiv[2] = (Byte_t)BRD9600; ChP->BaudDiv[3] = (Byte_t)(BRD9600 >> 8); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->BaudDiv[0]); ChP->TxControl[0] = (Byte_t)(ChOff + _TX_CTRL); ChP->TxControl[1] = (Byte_t)((ChOff + _TX_CTRL) >> 8); ChP->TxControl[2] = 0; ChP->TxControl[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); ChP->RxControl[0] = (Byte_t)(ChOff + _RX_CTRL); ChP->RxControl[1] = (Byte_t)((ChOff + _RX_CTRL) >> 8); ChP->RxControl[2] = 0; ChP->RxControl[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxEnables[0] = (Byte_t)(ChOff + _TX_ENBLS); ChP->TxEnables[1] = (Byte_t)((ChOff + _TX_ENBLS) >> 8); ChP->TxEnables[2] = 0; ChP->TxEnables[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxEnables[0]); ChP->TxCompare[0] = (Byte_t)(ChOff + _TXCMP1); ChP->TxCompare[1] = (Byte_t)((ChOff + _TXCMP1) >> 8); ChP->TxCompare[2] = 0; ChP->TxCompare[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxCompare[0]); ChP->TxReplace1[0] = (Byte_t)(ChOff + _TXREP1B1); ChP->TxReplace1[1] = (Byte_t)((ChOff + _TXREP1B1) >> 8); ChP->TxReplace1[2] = 0; ChP->TxReplace1[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace1[0]); ChP->TxReplace2[0] = (Byte_t)(ChOff + _TXREP2); ChP->TxReplace2[1] = (Byte_t)((ChOff + _TXREP2) >> 8); ChP->TxReplace2[2] = 0; ChP->TxReplace2[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace2[0]); ChP->TxFIFOPtrs = ChOff + _TXF_OUTP; ChP->TxFIFO = ChOff + _TX_FIFO; sOutB(ChP->Cmd,(Byte_t)ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */ sOutB(ChP->Cmd,(Byte_t)ChanNum); /* remove reset Tx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ sOutW(ChP->IndexData,0); ChP->RxFIFOPtrs = ChOff + _RXF_OUTP; ChP->RxFIFO = ChOff + _RX_FIFO; sOutB(ChP->Cmd,(Byte_t)ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */ sOutB(ChP->Cmd,(Byte_t)ChanNum); /* remove reset Rx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */ sOutW(ChP->IndexData,0); sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ sOutW(ChP->IndexData,0); ChP->TxPrioCnt = ChOff + _TXP_CNT; sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioCnt); sOutB(ChP->IndexData,0); ChP->TxPrioPtr = ChOff + _TXP_PNTR; sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioPtr); sOutB(ChP->IndexData,0); ChP->TxPrioBuf = ChOff + _TXP_BUF; sEnRxProcessor(ChP); /* start the Rx processor */ return(TRUE); } /*************************************************************************** Function: sStopRxProcessor Purpose: Stop the receive processor from processing a channel. Call: sStopRxProcessor(ChP) CHANNEL_T *ChP; Ptr to channel structure Comments: The receive processor can be started again with sStartRxProcessor(). This function causes the receive processor to skip over the stopped channel. It does not stop it from processing other channels. Warnings: No context switches are allowed while executing this function. Do not leave the receive processor stopped for more than one character time. After calling this function a delay of 4 uS is required to ensure that the receive processor is no longer processing this channel. */ void sStopRxProcessor(CHANNEL_T *ChP) { Byte_t R[4]; R[0] = ChP->R[0]; R[1] = ChP->R[1]; R[2] = 0x0a; R[3] = ChP->R[3]; sOutDW(ChP->IndexAddr,*(DWord_t *)&R[0]); } /*************************************************************************** Function: sFlushRxFIFO Purpose: Flush the Rx FIFO Call: sFlushRxFIFO(ChP) CHANNEL_T *ChP; Ptr to channel structure Return: void Comments: To prevent data from being enqueued or dequeued in the Tx FIFO while it is being flushed the receive processor is stopped and the transmitter is disabled. After these operations a 4 uS delay is done before clearing the pointers to allow the receive processor to stop. These items are handled inside this function. Warnings: No context switches are allowed while executing this function. */ void sFlushRxFIFO(CHANNEL_T *ChP) { int i; Byte_t Ch; /* channel number within AIOP */ int RxFIFOEnabled; /* TRUE if Rx FIFO enabled */ if(sGetRxCnt(ChP) == 0) /* Rx FIFO empty */ return; /* don't need to flush */ RxFIFOEnabled = FALSE; if(ChP->R[0x32] == 0x08) /* Rx FIFO is enabled */ { RxFIFOEnabled = TRUE; sDisRxFIFO(ChP); /* disable it */ for(i=0; i < 2000/200; i++) /* delay 2 uS to allow proc to disable FIFO*/ sInB(ChP->IntChan); /* depends on bus i/o timing */ } sGetChanStatus(ChP); /* clear any pending Rx errors in chan stat */ Ch = (Byte_t)sGetChanNum(ChP); sOutB(ChP->Cmd,Ch | RESRXFCNT); /* apply reset Rx FIFO count */ sOutB(ChP->Cmd,Ch); /* remove reset Rx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */ sOutW(ChP->IndexData,0); sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ sOutW(ChP->IndexData,0); if(RxFIFOEnabled) sEnRxFIFO(ChP); /* enable Rx FIFO */ } /*************************************************************************** Function: sFlushTxFIFO Purpose: Flush the Tx FIFO Call: sFlushTxFIFO(ChP) CHANNEL_T *ChP; Ptr to channel structure Return: void Comments: To prevent data from being enqueued or dequeued in the Tx FIFO while it is being flushed the receive processor is stopped and the transmitter is disabled. After these operations a 4 uS delay is done before clearing the pointers to allow the receive processor to stop. These items are handled inside this function. Warnings: No context switches are allowed while executing this function. */ void sFlushTxFIFO(CHANNEL_T *ChP) { int i; Byte_t Ch; /* channel number within AIOP */ int TxEnabled; /* TRUE if transmitter enabled */ if(sGetTxCnt(ChP) == 0) /* Tx FIFO empty */ return; /* don't need to flush */ TxEnabled = FALSE; if(ChP->TxControl[3] & TX_ENABLE) { TxEnabled = TRUE; sDisTransmit(ChP); /* disable transmitter */ } sStopRxProcessor(ChP); /* stop Rx processor */ for(i = 0; i < 4000/200; i++) /* delay 4 uS to allow proc to stop */ sInB(ChP->IntChan); /* depends on bus i/o timing */ Ch = (Byte_t)sGetChanNum(ChP); sOutB(ChP->Cmd,Ch | RESTXFCNT); /* apply reset Tx FIFO count */ sOutB(ChP->Cmd,Ch); /* remove reset Tx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ sOutW(ChP->IndexData,0); if(TxEnabled) sEnTransmit(ChP); /* enable transmitter */ sStartRxProcessor(ChP); /* restart Rx processor */ } /*************************************************************************** Function: sWriteTxPrioByte Purpose: Write a byte of priority transmit data to a channel Call: sWriteTxPrioByte(ChP,Data) CHANNEL_T *ChP; Ptr to channel structure Byte_t Data; The transmit data byte Return: int: 1 if the bytes is successfully written, otherwise 0. Comments: The priority byte is transmitted before any data in the Tx FIFO. Warnings: No context switches are allowed while executing this function. */ int sWriteTxPrioByte(CHANNEL_T *ChP, Byte_t Data) { Byte_t DWBuf[4]; /* buffer for double word writes */ Word_t *WordPtr; /* must be far because Win SS != DS */ register DWordIO_t IndexAddr; if(sGetTxCnt(ChP) > 1) /* write it to Tx priority buffer */ { IndexAddr = ChP->IndexAddr; sOutW((WordIO_t)IndexAddr,ChP->TxPrioCnt); /* get priority buffer status */ if(sInB((ByteIO_t)ChP->IndexData) & PRI_PEND) /* priority buffer busy */ return(0); /* nothing sent */ WordPtr = (Word_t *)(&DWBuf[0]); *WordPtr = ChP->TxPrioBuf; /* data byte address */ DWBuf[2] = Data; /* data byte value */ sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */ *WordPtr = ChP->TxPrioCnt; /* Tx priority count address */ DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */ DWBuf[3] = 0; /* priority buffer pointer */ sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */ } else /* write it to Tx FIFO */ { sWriteTxByte(sGetTxRxDataIO(ChP),Data); } return(1); /* 1 byte sent */ } /*************************************************************************** Function: sEnInterrupts Purpose: Enable one or more interrupts for a channel Call: sEnInterrupts(ChP,Flags) CHANNEL_T *ChP; Ptr to channel structure Word_t Flags: Interrupt enable flags, can be any combination of the following flags: TXINT_EN: Interrupt on Tx FIFO empty RXINT_EN: Interrupt on Rx FIFO at trigger level (see sSetRxTrigger()) SRCINT_EN: Interrupt on SRC (Special Rx Condition) MCINT_EN: Interrupt on modem input change CHANINT_EN: Allow channel interrupt signal to the AIOP's Interrupt Channel Register. Return: void Comments: If an interrupt enable flag is set in Flags, that interrupt will be enabled. If an interrupt enable flag is not set in Flags, that interrupt will not be changed. Interrupts can be disabled with function sDisInterrupts(). This function sets the appropriate bit for the channel in the AIOP's Interrupt Mask Register if the CHANINT_EN flag is set. This allows this channel's bit to be set in the AIOP's Interrupt Channel Register. Interrupts must also be globally enabled before channel interrupts will be passed on to the host. This is done with function sEnGlobalInt(). In some cases it may be desirable to disable interrupts globally but enable channel interrupts. This would allow the global interrupt status register to be used to determine which AIOPs need service. */ void sEnInterrupts(CHANNEL_T *ChP,Word_t Flags) { Byte_t Mask; /* Interrupt Mask Register */ ChP->RxControl[2] |= ((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxControl[2] |= ((Byte_t)Flags & TXINT_EN); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); if(Flags & CHANINT_EN) { Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum]; sOutB(ChP->IntMask,Mask); } } /*************************************************************************** Function: sDisInterrupts Purpose: Disable one or more interrupts for a channel Call: sDisInterrupts(ChP,Flags) CHANNEL_T *ChP; Ptr to channel structure Word_t Flags: Interrupt flags, can be any combination of the following flags: TXINT_EN: Interrupt on Tx FIFO empty RXINT_EN: Interrupt on Rx FIFO at trigger level (see sSetRxTrigger()) SRCINT_EN: Interrupt on SRC (Special Rx Condition) MCINT_EN: Interrupt on modem input change CHANINT_EN: Disable channel interrupt signal to the AIOP's Interrupt Channel Register. Return: void Comments: If an interrupt flag is set in Flags, that interrupt will be disabled. If an interrupt flag is not set in Flags, that interrupt will not be changed. Interrupts can be enabled with function sEnInterrupts(). This function clears the appropriate bit for the channel in the AIOP's Interrupt Mask Register if the CHANINT_EN flag is set. This blocks this channel's bit from being set in the AIOP's Interrupt Channel Register. */ void sDisInterrupts(CHANNEL_T *ChP,Word_t Flags) { Byte_t Mask; /* Interrupt Mask Register */ ChP->RxControl[2] &= ~((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxControl[2] &= ~((Byte_t)Flags & TXINT_EN); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); if(Flags & CHANINT_EN) { Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum]; sOutB(ChP->IntMask,Mask); } } /********************************************************************* Begin FreeBsd-specific driver code **********************************************************************/ static int rpprobe __P((struct isa_device *)); static int rpattach __P((struct isa_device *)); static char* rp_pciprobe(pcici_t tag, pcidi_t type); static void rp_pciattach(pcici_t tag, int unit); static u_long rp_pcicount; static struct pci_device rp_pcidevice = { "rp", rp_pciprobe, rp_pciattach, &rp_pcicount, NULL }; DATA_SET (pcidevice_set, rp_pcidevice); static timeout_t rpdtrwakeup; struct isa_driver rpdriver = { rpprobe, rpattach, "rp" }; -#define CDEV_MAJOR 81 - static char driver_name[] = "rp"; static d_open_t rpopen; static d_close_t rpclose; static d_read_t rpread; static d_write_t rpwrite; static d_ioctl_t rpioctl; static d_stop_t rpstop; static d_devtotty_t rpdevtotty; -static struct cdevsw rp_cdevsw = - { rpopen, rpclose, rpread, rpwrite, - rpioctl, rpstop, noreset, rpdevtotty, - ttpoll, nommap, NULL, driver_name, - NULL, -1}; +#define CDEV_MAJOR 81 +static struct cdevsw rp_cdevsw = { + rpopen, rpclose, rpread, rpwrite, + rpioctl, rpstop, noreset, rpdevtotty, + ttpoll, nommap, NULL, driver_name, + NULL, -1, nodump, nopsize, + D_TTY, +}; static int rp_controller_port = 0; static int rp_num_ports_open = 0; static int rp_timeout; static int ndevs = 0; static int minor_to_unit[128]; static struct tty rp_tty[128]; static int rp_num_ports[4]; /* Number of ports on each controller */ #define _INLINE_ __inline #define POLL_INTERVAL 1 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_UNIT(dev) (MINOR_TO_UNIT(minor(dev)) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_MAGIC(dev) ((minor(dev)) & ~MINOR_MAGIC_MASK) #define IS_CALLOUT(dev) (minor(dev) & CALLOUT_MASK) #define IS_CONTROL(dev) (minor(dev) & CONTROL_MASK) #define RP_ISMULTIPORT(dev) ((dev)->id_flags & 0x1) #define RP_MPMASTER(dev) (((dev)->id_flags >> 8) & 0xff) #define RP_NOTAST4(dev) ((dev)->id_flags & 0x04) static struct rp_port *p_rp_addr[4]; static struct rp_port *p_rp_table[MAX_RP_PORTS]; #define rp_addr(unit) (p_rp_addr[unit]) #define rp_table(port) (p_rp_table[port]) /* * The top-level routines begin here */ int rpselect __P((dev_t, int, struct proc *)); static int rpparam __P((struct tty *, struct termios *)); static void rpstart __P((struct tty *)); static void rphardclose __P((struct rp_port *)); #define rpmap nomap #define rpreset noreset #define rpstrategy nostrategy static void rp_disc_optim __P((struct tty *tp, struct termios *t, struct rp_port *rp)); static _INLINE_ void rp_do_receive(struct rp_port *rp, struct tty *tp, CHANNEL_t *cp, unsigned int ChanStatus) { int spl; unsigned int CharNStat; int ToRecv, wRecv, ch; ToRecv = sGetRxCnt(cp); if(ToRecv == 0) return; /* If status indicates there are errored characters in the FIFO, then enter status mode (a word in FIFO holds characters and status) */ if(ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { if(!(ChanStatus & STATMODE)) { ChanStatus |= STATMODE; sEnRxStatusMode(cp); } } /* if we previously entered status mode then read down the FIFO one word at a time, pulling apart the character and the status. Update error counters depending on status. */ if(ChanStatus & STATMODE) { while(ToRecv) { if(tp->t_state & TS_TBLOCK) { break; } CharNStat = sInW(sGetTxRxDataIO(cp)); ch = CharNStat & 0xff; if((CharNStat & STMBREAK) || (CharNStat & STMFRAMEH)) ch |= TTY_FE; else if (CharNStat & STMPARITYH) ch |= TTY_PE; else if (CharNStat & STMRCVROVRH) rp->rp_overflows++; (*linesw[tp->t_line].l_rint)(ch, tp); ToRecv--; } /* After emtying FIFO in status mode, turn off status mode */ if(sGetRxCnt(cp) == 0) sDisRxStatusMode(cp); } else { while (ToRecv) { if(tp->t_state & TS_TBLOCK) { break; } ch = (u_char) sInB(sGetTxRxDataIO(cp)); spl = spltty(); (*linesw[tp->t_line].l_rint)(ch, tp); splx(spl); ToRecv--; } } } static _INLINE_ void rp_handle_port(struct rp_port *rp) { CHANNEL_t *cp; struct tty *tp; unsigned int IntMask, ChanStatus; int oldcts, ToRecv; if(!rp) return; cp = &rp->rp_channel; tp = rp->rp_tty; IntMask = sGetChanIntID(cp); IntMask = IntMask & rp->rp_intmask; ChanStatus = sGetChanStatus(cp); if(IntMask & RXF_TRIG) if(!(tp->t_state & TS_TBLOCK) && (tp->t_state & TS_CARR_ON) && (tp->t_state & TS_ISOPEN)) { rp_do_receive(rp, tp, cp, ChanStatus); } if(IntMask & DELTA_CD) { if(ChanStatus & CD_ACT) { if(!(tp->t_state & TS_CARR_ON) ) { (void)(*linesw[tp->t_line].l_modem)(tp, 1); } } else { if((tp->t_state & TS_CARR_ON)) { (void)(*linesw[tp->t_line].l_modem)(tp, 0); if((*linesw[tp->t_line].l_modem)(tp, 0) == 0) { rphardclose(rp); } } } } /* oldcts = rp->rp_cts; rp->rp_cts = ((ChanStatus & CTS_ACT) != 0); if(oldcts != rp->rp_cts) { printf("CTS change (now %s)... on port %d\n", rp->rp_cts ? "on" : "off", rp->rp_port); } */ } static void rp_do_poll(void *not_used) { CONTROLLER_t *ctl; struct rp_port *rp; struct tty *tp; int unit, aiop, ch, line, count; unsigned char CtlMask, AiopMask; for(unit = 0; unit <= ndevs; unit++) { rp = rp_addr(unit); ctl = rp->rp_ctlp; if(ctl->BusType == isPCI) CtlMask = sPCIGetControllerIntStatus(ctl); else CtlMask = sGetControllerIntStatus(ctl); for(aiop=0; CtlMask; CtlMask >>=1, aiop++) { if(CtlMask & 1) { AiopMask = sGetAiopIntStatus(ctl, aiop); for(ch = 0; AiopMask; AiopMask >>=1, ch++) { if(AiopMask & 1) { line = (unit << 5) | (aiop << 3) | ch; rp = rp_table(line); rp_handle_port(rp); } } } } for(line = 0, rp = rp_addr(unit); line < rp_num_ports[unit]; line++, rp++) { tp = rp->rp_tty; if((tp->t_state & TS_BUSY) && (tp->t_state & TS_ISOPEN)) { count = sGetTxCnt(&rp->rp_channel); if(count == 0) tp->t_state &= ~(TS_BUSY); if(!(tp->t_state & TS_TTSTOP) && (count <= rp->rp_restart)) { (*linesw[tp->t_line].l_start)(tp); } } } } if(rp_num_ports_open) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); } static char* rp_pciprobe(pcici_t tag, pcidi_t type) { int vendor_id; vendor_id = type & 0xffff; switch(vendor_id) case 0x11fe: return("rp"); return(NULL); } static int rpprobe(dev) struct isa_device *dev; { struct isa_device *idev; int controller, unit; int i, aiop, num_aiops; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; CONTROLLER_t *ctlp; unit = dev->id_unit; if (dev->id_unit >= 4) { printf("rpprobe: unit number %d invalid.\n", dev->id_unit); return 1; } printf("probing for RocketPort(ISA) unit %d\n", unit); if (rp_controller_port) controller = rp_controller_port; else { controller = dev->id_iobase + 0x40; } for (aiop=0; aiopid_iobase + (aiop * 0x400); ctlp = sCtlNumToCtlPtr(dev->id_unit); num_aiops = sInitController(ctlp, dev->id_unit, controller + ((unit-rp_pcicount)*0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); if (num_aiops <= 0) { printf("board%d init failed\n", unit); return 0; } if (rp_controller_port) { dev->id_msize = 64; } else { dev->id_msize = 68; rp_controller_port = controller; } dev->id_irq = 0; return 1; } static void rp_pciattach(pcici_t tag, int unit) { dev_t rp_dev; int success, rpmajor, oldspl; u_short iobase; int num_ports, num_chan, num_aiops; int aiop, chan, port; int ChanStatus, line, i, count; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; struct rp_port *rp; struct tty *tty; CONTROLLER_t *ctlp; char status; success = pci_map_port(tag, 0x10, &iobase); if(!success) printf("ioaddr mapping failed for RocketPort(PCI)\n"); for(aiop=0; aiop < MAX_AIOPS_PER_BOARD; aiop++) aiopio[aiop] = iobase + (aiop * 0x40); ctlp = sCtlNumToCtlPtr(unit); num_aiops = sPCIInitController(ctlp, unit, aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); num_ports = 0; for(aiop=0; aiop < num_aiops; aiop++) { sResetAiopByNum(ctlp, aiop); num_ports += sGetAiopNumChan(ctlp, aiop); } printf("RocketPort%d = %d ports\n", unit, num_ports); rp_num_ports[unit] = num_ports; rp = (struct rp_port *) malloc(sizeof(struct rp_port) * num_ports, M_TTYS, M_NOWAIT); if(rp == 0) { printf("rp_attach: Could not malloc rp_ports structures\n"); return; } count = unit * 32; /* board times max ports per card SG */ for(i=count;i < (count + rp_num_ports[unit]);i++) minor_to_unit[i] = unit; bzero(rp, sizeof(struct rp_port) * num_ports); tty = (struct tty *) malloc(sizeof(struct tty) * num_ports, M_TTYS, M_NOWAIT); if(tty == 0) { printf("rp_attach: Could not malloc tty structures\n"); return; } bzero(tty, sizeof(struct tty) * num_ports); oldspl = spltty(); rp_addr(unit) = rp; splx(oldspl); rp_dev = makedev(CDEV_MAJOR, unit); cdevsw_add(&rp_dev, &rp_cdevsw, NULL); port = 0; for(aiop=0; aiop < num_aiops; aiop++) { num_chan = sGetAiopNumChan(ctlp, aiop); for(chan=0; chan < num_chan; chan++, port++, rp++, tty++) { rp->rp_tty = tty; rp->rp_port = port; rp->rp_ctlp = ctlp; rp->rp_unit = unit; rp->rp_chan = chan; rp->rp_aiop = aiop; tty->t_line = 0; /* tty->t_termios = deftermios; */ rp->dtr_wait = 3 * hz; rp->it_in.c_iflag = 0; rp->it_in.c_oflag = 0; rp->it_in.c_cflag = TTYDEF_CFLAG; rp->it_in.c_lflag = 0; termioschars(&rp->it_in); /* termioschars(&tty->t_termios); */ rp->it_in.c_ispeed = rp->it_in.c_ospeed = TTYDEF_SPEED; rp->it_out = rp->it_in; rp->rp_intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; ChanStatus = sGetChanStatus(&rp->rp_channel); if(sInitChan(ctlp, &rp->rp_channel, aiop, chan) == 0) { printf("RocketPort sInitChan(%d, %d, %d) failed \n", unit, aiop, chan); return; } ChanStatus = sGetChanStatus(&rp->rp_channel); rp->rp_cts = (ChanStatus & CTS_ACT) != 0; line = (unit << 5) | (aiop << 3) | chan; rp_table(line) = rp; /* devfs_add_devswf(&rp_cdevsw, port, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyR%r", port); devfs_add_devswf(&rp_cdevsw, port | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyRi%r", port); */ } } } static int rpattach(dev) struct isa_device *dev; { struct isa_device *idev; dev_t rp_dev; int iobase, unit, rpmajor, oldspl; int num_ports, num_chan, num_aiops; int aiop, chan, port; int ChanStatus, line, i, count; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; struct rp_port *rp; struct tty *tty; CONTROLLER_t *ctlp; char status; iobase = dev->id_iobase; unit = dev->id_unit; ndevs = unit; for(aiop=0; aiop < MAX_AIOPS_PER_BOARD; aiop++) aiopio[aiop] = iobase + (aiop * 0x400); ctlp = sCtlNumToCtlPtr(unit); num_aiops = sInitController(ctlp, unit, rp_controller_port + ((unit-rp_pcicount) * 0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); num_ports = 0; for(aiop=0; aiop < num_aiops; aiop++) { sResetAiopByNum(ctlp, aiop); sEnAiop(ctlp, aiop); num_ports += sGetAiopNumChan(ctlp, aiop); } printf("RocketPort%d = %d ports\n", unit, num_ports); rp_num_ports[unit] = num_ports; rp = (struct rp_port *) malloc(sizeof(struct rp_port) * num_ports, M_TTYS, M_NOWAIT); if(rp == 0) { printf("rp_attach: Could not malloc rp_ports structures\n"); return(0); } count = unit * 32; /* board # times max ports per card SG */ for(i=count;i < (count + rp_num_ports[unit]);i++) minor_to_unit[i] = unit; bzero(rp, sizeof(struct rp_port) * num_ports); tty = (struct tty *) malloc(sizeof(struct tty) * num_ports, M_TTYS, M_NOWAIT); if(tty == 0) { printf("rp_attach: Could not malloc tty structures\n"); return(0); } bzero(tty, sizeof(struct tty) * num_ports); oldspl = spltty(); rp_addr(unit) = rp; splx(oldspl); rp_dev = makedev(CDEV_MAJOR, unit); cdevsw_add(&rp_dev, &rp_cdevsw, NULL); port = 0; for(aiop=0; aiop < num_aiops; aiop++) { num_chan = sGetAiopNumChan(ctlp, aiop); for(chan=0; chan < num_chan; chan++, port++, rp++, tty++) { rp->rp_tty = tty; rp->rp_port = port; rp->rp_ctlp = ctlp; rp->rp_unit = unit; rp->rp_chan = chan; rp->rp_aiop = aiop; tty->t_line = 0; /* tty->t_termios = deftermios; */ rp->dtr_wait = 3 * hz; rp->it_in.c_iflag = 0; rp->it_in.c_oflag = 0; rp->it_in.c_cflag = TTYDEF_CFLAG; rp->it_in.c_lflag = 0; termioschars(&rp->it_in); /* termioschars(&tty->t_termios); */ rp->it_in.c_ispeed = rp->it_in.c_ospeed = TTYDEF_SPEED; rp->it_out = rp->it_in; rp->rp_intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; ChanStatus = sGetChanStatus(&rp->rp_channel); if(sInitChan(ctlp, &rp->rp_channel, aiop, chan) == 0) { printf("RocketPort sInitChan(%d, %d, %d) failed \n", unit, aiop, chan); return(0); } ChanStatus = sGetChanStatus(&rp->rp_channel); rp->rp_cts = (ChanStatus & CTS_ACT) != 0; line = (unit << 5) | (aiop << 3) | chan; rp_table(line) = rp; } } idev = find_isadev(isa_devtab_tty, &rpdriver, RP_MPMASTER(dev) + rp_pcicount); if(idev == NULL) { printf("rp%d: master device %d not configured\n", dev->id_unit, RP_MPMASTER(dev)); } /* printf("COOL!! Device is found!!\n"); for(rpmajor=0;rpmajor> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; if(IS_CONTROL(dev)) return(0); rp = rp_addr(unit) + port; /* rp->rp_tty = &rp_tty[rp->rp_port]; */ tp = rp->rp_tty; oldspl = spltty(); open_top: while(rp->state & ~SET_DTR) { error = tsleep(&rp->dtr_wait, TTIPRI | PCATCH, "rpdtr", 0); if(error != 0) goto out; } if(tp->t_state & TS_ISOPEN) { if(IS_CALLOUT(dev)) { if(!rp->active_out) { error = EBUSY; goto out; } } else { if(rp->active_out) { if(flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&rp->active_out, TTIPRI | PCATCH, "rpbi", 0); if(error != 0) goto out; goto open_top; } } if(tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { splx(oldspl); return(EBUSY); } } else { tp->t_dev = dev; tp->t_param = rpparam; tp->t_oproc = rpstart; tp->t_line = 0; tp->t_termios = IS_CALLOUT(dev) ? rp->it_out : rp->it_in; flags = 0; flags |= SET_RTS; flags |= SET_DTR; rp->rp_channel.TxControl[3] = ((rp->rp_channel.TxControl[3] & ~(SET_RTS | SET_DTR)) | flags); sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); sSetRxTrigger(&rp->rp_channel, TRIG_1); sDisRxStatusMode(&rp->rp_channel); sFlushRxFIFO(&rp->rp_channel); sFlushTxFIFO(&rp->rp_channel); sEnInterrupts(&rp->rp_channel, (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN)); sSetRxTrigger(&rp->rp_channel, TRIG_1); sDisRxStatusMode(&rp->rp_channel); sClrTxXOFF(&rp->rp_channel); /* sDisRTSFlowCtl(&rp->rp_channel); sDisCTSFlowCtl(&rp->rp_channel); */ sDisTxSoftFlowCtl(&rp->rp_channel); sStartRxProcessor(&rp->rp_channel); sEnRxFIFO(&rp->rp_channel); sEnTransmit(&rp->rp_channel); /* sSetDTR(&rp->rp_channel); sSetRTS(&rp->rp_channel); */ ++rp->wopeners; error = rpparam(tp, &tp->t_termios); --rp->wopeners; if(error != 0) { splx(oldspl); return(error); } - ttsetwater(tp); - rp_num_ports_open++; IntMask = sGetChanIntID(&rp->rp_channel); IntMask = IntMask & rp->rp_intmask; ChanStatus = sGetChanStatus(&rp->rp_channel); if((IntMask & DELTA_CD) || IS_CALLOUT(dev)) { if((ChanStatus & CD_ACT) || IS_CALLOUT(dev)) { (void)(*linesw[tp->t_line].l_modem)(tp, 1); } } if(rp_num_ports_open == 1) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); } if(!(flag&O_NONBLOCK) && !(tp->t_cflag&CLOCAL) && !(tp->t_state & TS_CARR_ON) && !(IS_CALLOUT(dev))) { ++rp->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "rpdcd", 0); --rp->wopeners; if(error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); rp_disc_optim(tp, &tp->t_termios, rp); if(tp->t_state & TS_ISOPEN && IS_CALLOUT(dev)) rp->active_out = TRUE; /* if(rp_num_ports_open == 1) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); */ out: splx(oldspl); if(!(tp->t_state & TS_ISOPEN) && rp->wopeners == 0) { rphardclose(rp); } return(error); } int rpclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { int oldspl, unit, mynor, umynor, port, status, i; /* SG */ struct rp_port *rp; struct tty *tp; CHANNEL_t *cp; umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(0); rp = rp_addr(unit) + port; cp = &rp->rp_channel; tp = rp->rp_tty; oldspl = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); rp_disc_optim(tp, &tp->t_termios, rp); rpstop(tp, FREAD | FWRITE); rphardclose(rp); tp->t_state &= ~TS_BUSY; ttyclose(tp); splx(oldspl); return(0); } static void rphardclose(struct rp_port *rp) { int status, oldspl, mynor; struct tty *tp; CHANNEL_t *cp; cp = &rp->rp_channel; tp = rp->rp_tty; mynor = MINOR_MAGIC(tp->t_dev); sFlushRxFIFO(cp); sFlushTxFIFO(cp); sDisTransmit(cp); sDisInterrupts(cp, TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN); sDisRTSFlowCtl(cp); sDisCTSFlowCtl(cp); sDisTxSoftFlowCtl(cp); sClrTxXOFF(cp); if(tp->t_cflag&HUPCL || !(tp->t_state&TS_ISOPEN) || !rp->active_out) { sClrDTR(cp); } if(IS_CALLOUT(tp->t_dev)) { sClrDTR(cp); } if(rp->dtr_wait != 0) { timeout(rpdtrwakeup, rp, rp->dtr_wait); rp->state |= ~SET_DTR; } rp->active_out = FALSE; wakeup(&rp->active_out); wakeup(TSA_CARR_ON(tp)); } static int rpread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct rp_port *rp; struct tty *tp; int unit, i, mynor, umynor, port, error = 0; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(ENODEV); rp = rp_addr(unit) + port; tp = rp->rp_tty; error = (*linesw[tp->t_line].l_read)(tp, uio, flag); return(error); } static int rpwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct rp_port *rp; struct tty *tp; int unit, i, mynor, port, umynor, error = 0; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(ENODEV); rp = rp_addr(unit) + port; tp = rp->rp_tty; while(rp->rp_disable_writes) { rp->rp_waiting = 1; if(error = ttysleep(tp, (caddr_t)rp, TTOPRI|PCATCH, "rp_write", 0)) { return(error); } } error = (*linesw[tp->t_line].l_write)(tp, uio, flag); return error; } static void rpdtrwakeup(void *chan) { struct rp_port *rp; rp = (struct rp_port *)chan; rp->state &= SET_DTR; wakeup(&rp->dtr_wait); } int rpioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct rp_port *rp; CHANNEL_t *cp; struct tty *tp; int unit, mynor, port, umynor; /* SG */ int oldspl, cflag, iflag, oflag, lflag; int i, error = 0; char status; int arg, flags, result, ChanStatus; int oldcmd; struct termios term, *t; umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; if(IS_CONTROL(dev)) { struct termios *ct; switch (IS_CONTROL(dev)) { case CONTROL_INIT_STATE: ct = IS_CALLOUT(dev) ? &rp->it_out : &rp->it_in; break; case CONTROL_LOCK_STATE: ct = IS_CALLOUT(dev) ? &rp->lt_out : &rp->lt_in; break; default: return(ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if(error != 0) return(error); *ct = *(struct termios *)data; return(0); case TIOCGETA: *(struct termios *)data = *ct; return(0); case TIOCGETD: *(int *)data = TTYDISC; return(0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return(0); default: return(ENOTTY); } } tp = rp->rp_tty; cp = &rp->rp_channel; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if(error != 0) return(error); if(cmd != oldcmd) { data = (caddr_t)&term; } #endif if((cmd == TIOCSETA) || (cmd == TIOCSETAW) || (cmd == TIOCSETAF)) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = IS_CALLOUT(dev) ? &rp->lt_out : &rp->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for(cc = 0; cc < NCCS; ++cc) if(lt->c_cc[cc] = tp->t_cc[cc]) dt->c_cc[cc] = tp->t_cc[cc]; if(lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if(lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } t = &tp->t_termios; error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if(error != ENOIOCTL) { return(error); } oldspl = spltty(); flags = rp->rp_channel.TxControl[3]; error = ttioctl(tp, cmd, data, flag); flags = rp->rp_channel.TxControl[3]; rp_disc_optim(tp, &tp->t_termios, rp); if(error != ENOIOCTL) { splx(oldspl); return(error); } switch(cmd) { case TIOCSBRK: sSendBreak(&rp->rp_channel); break; case TIOCCBRK: sClrBreak(&rp->rp_channel); break; case TIOCSDTR: sSetDTR(&rp->rp_channel); sSetRTS(&rp->rp_channel); break; case TIOCCDTR: sClrDTR(&rp->rp_channel); break; case TIOCMSET: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] = ((rp->rp_channel.TxControl[3] & ~(SET_RTS | SET_DTR)) | flags); sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMBIS: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] |= flags; sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMBIC: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] &= ~flags; sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMGET: ChanStatus = sGetChanStatusLo(&rp->rp_channel); flags = rp->rp_channel.TxControl[3]; result = TIOCM_LE; /* always on while open for some reason */ result |= (((flags & SET_DTR) ? TIOCM_DTR : 0) | ((flags & SET_RTS) ? TIOCM_RTS : 0) | ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) | ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) | ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0)); if(rp->rp_channel.RxControl[2] & RTSFC_EN) { result |= TIOCM_RTS; } *(int *)data = result; break; case TIOCMSDTRWAIT: error = suser(p->p_ucred, &p->p_acflag); if(error != 0) { splx(oldspl); return(error); } rp->dtr_wait = *(int *)data * hz/100; break; case TIOCMGDTRWAIT: *(int *)data = rp->dtr_wait * 100/hz; break; default: splx(oldspl); return ENOTTY; } splx(oldspl); return(0); } static struct speedtab baud_table[] = { B0, 0, B50, BRD50, B75, BRD75, B110, BRD110, B134, BRD134, B150, BRD150, B200, BRD200, B300, BRD300, B600, BRD600, B1200, BRD1200, B1800, BRD1800, B2400, BRD2400, B4800, BRD4800, B9600, BRD9600, B19200, BRD19200, B38400, BRD38400, B7200, BRD7200, B14400, BRD14400, B57600, BRD57600, B76800, BRD76800, B115200, BRD115200, B230400, BRD230400, -1, -1 }; static int rpparam(tp, t) struct tty *tp; struct termios *t; { struct rp_port *rp; CHANNEL_t *cp; int unit, i, mynor, port, umynor; /* SG */ int oldspl, cflag, iflag, oflag, lflag; int ospeed, flags; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; oldspl = spltty(); cflag = t->c_cflag; iflag = t->c_iflag; oflag = t->c_oflag; lflag = t->c_lflag; ospeed = ttspeedtab(t->c_ispeed, baud_table); if(ospeed < 0 || t->c_ispeed != t->c_ospeed) return(EINVAL); tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = cflag; tp->t_iflag = iflag; tp->t_oflag = oflag; tp->t_lflag = lflag; if(t->c_ospeed == 0) { sClrDTR(cp); return(0); } rp->rp_fifo_lw = ((t->c_ospeed*2) / 1000) +1; /* Set baud rate ----- we only pay attention to ispeed */ sSetDTR(cp); sSetRTS(cp); sSetBaud(cp, ospeed); if(cflag & CSTOPB) { sSetStop2(cp); } else { sSetStop1(cp); } if(cflag & PARENB) { sEnParity(cp); if(cflag & PARODD) { sSetOddParity(cp); } else { sSetEvenParity(cp); } } else { sDisParity(cp); } if((cflag & CSIZE) == CS8) { sSetData8(cp); rp->rp_imask = 0xFF; } else { sSetData7(cp); rp->rp_imask = 0x7F; } if(iflag & ISTRIP) { rp->rp_imask &= 0x7F; } if(cflag & CLOCAL) { rp->rp_intmask &= ~DELTA_CD; } else { rp->rp_intmask |= DELTA_CD; } /* Put flow control stuff here */ if(cflag & CCTS_OFLOW) { sEnCTSFlowCtl(cp); } else { sDisCTSFlowCtl(cp); } if(cflag & CRTS_IFLOW) { rp->rp_rts_iflow = 1; } else { rp->rp_rts_iflow = 0; } if(cflag & CRTS_IFLOW) { sEnRTSFlowCtl(cp); } else { sDisRTSFlowCtl(cp); } rp_disc_optim(tp, t, rp); if((cflag & CLOCAL) || (sGetChanStatusLo(cp) & CD_ACT)) { tp->t_state |= TS_CARR_ON; wakeup(TSA_CARR_ON(tp)); } /* tp->t_state |= TS_CAN_BYPASS_L_RINT; flags = rp->rp_channel.TxControl[3]; if(flags & SET_DTR) else if(flags & SET_RTS) else */ splx(oldspl); return(0); } static void rp_disc_optim(tp, t, rp) struct tty *tp; struct termios *t; struct rp_port *rp; { if(!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) &&(!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) &&(!(t->c_iflag & PARMRK) ||(t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; } static void rpstart(tp) struct tty *tp; { struct rp_port *rp; CHANNEL_t *cp; struct clist *qp; int unit, i, mynor, port, umynor; /* SG */ char status, ch, flags; int spl, xmit_fifo_room; int count, ToRecv; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; flags = rp->rp_channel.TxControl[3]; spl = spltty(); if(tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(spl); return; } if(rp->rp_xmit_stopped) { sEnTransmit(cp); rp->rp_xmit_stopped = 0; } count = sGetTxCnt(cp); if(tp->t_outq.c_cc == 0) { if((tp->t_state & TS_BUSY) && (count == 0)) { tp->t_state &= ~TS_BUSY; } ttwwakeup(tp); splx(spl); return; } xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); qp = &tp->t_outq; count = 0; if(xmit_fifo_room > 0 && qp->c_cc > 0) { tp->t_state |= TS_BUSY; } while(xmit_fifo_room > 0 && qp->c_cc > 0) { ch = getc(qp); sOutB(sGetTxRxDataIO(cp), ch); xmit_fifo_room--; count++; } rp->rp_restart = (qp->c_cc > 0) ? rp->rp_fifo_lw : 0; ttwwakeup(tp); splx(spl); } static void rpstop(tp, flag) register struct tty *tp; int flag; { struct rp_port *rp; CHANNEL_t *cp; struct clist *qp; int unit, mynor, port, umynor; /* SG */ char status, ch; int spl, xmit_fifo_room; int i, count; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; spl = spltty(); if(tp->t_state & TS_BUSY) { if((tp->t_state&TS_TTSTOP) == 0) { sFlushTxFIFO(cp); } else { if(rp->rp_xmit_stopped == 0) { sDisTransmit(cp); rp->rp_xmit_stopped = 1; } } } splx(spl); rpstart(tp); } int rpselect(dev, flag, p) dev_t dev; int flag; struct proc *p; { return(0); } struct tty * rpdevtotty(dev_t dev) { struct rp_port *rp; int unit, i, port, mynor, umynor; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(NULL); rp = rp_addr(unit) + port; return(rp->rp_tty); } diff --git a/sys/dev/si/si.c b/sys/dev/si/si.c index 4b37f61e3a98..8b7dc26e3c0c 100644 --- a/sys/dev/si/si.c +++ b/sys/dev/si/si.c @@ -1,2824 +1,2824 @@ /* * Device driver for Specialix range (SI/XIO) of serial line multiplexors. * * Copyright (C) 1990, 1992, 1998 Specialix International, * Copyright (C) 1993, Andy Rutter * Copyright (C) 1995, Peter Wemm * * Originally derived from: SunOS 4.x version * Ported from BSDI version to FreeBSD by Peter Wemm. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notices, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notices, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Andy Rutter of * Advanced Methods and Tools Ltd. based on original information * from Specialix International. * 4. Neither the name of Advanced Methods and Tools, nor Specialix * International may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHORS BE LIABLE. * - * $Id: si.c,v 1.74 1998/08/16 00:57:07 bde Exp $ + * $Id: si.c,v 1.75 1998/08/16 01:04:48 bde Exp $ */ #ifndef lint static const char si_copyright1[] = "@(#) Copyright (C) Specialix International, 1990,1992,1998", si_copyright2[] = "@(#) Copyright (C) Andy Rutter 1993", si_copyright3[] = "@(#) Copyright (C) Peter Wemm 1995"; #endif /* not lint */ #include "opt_compat.h" #include "opt_debug_si.h" #include "opt_devfs.h" #include #include #if defined(COMPAT_43) || defined(COMPAT_SUNOS) #include #endif #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #include #include #include #include #include #include "pci.h" #if NPCI > 0 #include #endif #include "eisa.h" #if NEISA > 0 #include #include #endif #include "si.h" /* * This device driver is designed to interface the Specialix International * SI, XIO and SX range of serial multiplexor cards to FreeBSD on an ISA, * EISA or PCI bus machine. * * The controller is interfaced to the host via dual port RAM * and an interrupt. * * The code for the Host 1 (very old ISA cards) has not been tested. */ #define POLL /* turn on poller to scan for lost interrupts */ #define REALPOLL /* on each poll, scan for work regardless */ #define POLLHZ (hz/10) /* 10 times per second */ #define SI_I_HIGH_WATER (TTYHOG - 2 * SI_BUFFERSIZE) #define INT_COUNT 25000 /* max of 125 ints per second */ #define JET_INT_COUNT 100 /* max of 100 ints per second */ #define RXINT_COUNT 1 /* one rxint per 10 milliseconds */ enum si_mctl { GET, SET, BIS, BIC }; static void si_command __P((struct si_port *, int, int)); static int si_modem __P((struct si_port *, enum si_mctl, int)); static void si_write_enable __P((struct si_port *, int)); static int si_Sioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); static void si_start __P((struct tty *)); static timeout_t si_lstart; static void si_disc_optim __P((struct tty *tp, struct termios *t, struct si_port *pp)); static void sihardclose __P((struct si_port *pp)); static void sidtrwakeup __P((void *chan)); static int siparam __P((struct tty *, struct termios *)); static int siprobe __P((struct isa_device *id)); static int siattach __P((struct isa_device *id)); static void si_modem_state __P((struct si_port *pp, struct tty *tp, int hi_ip)); static void si_intr __P((int unit)); static char * si_modulename __P((int host_type, int uart_type)); struct isa_driver sidriver = { siprobe, siattach, "si" }; static u_long sipcieisacount = 0; #if NPCI > 0 static char *sipciprobe __P((pcici_t, pcidi_t)); static void sipciattach __P((pcici_t, int)); static struct pci_device sipcidev = { "si", sipciprobe, sipciattach, &sipcieisacount, NULL, }; DATA_SET (pcidevice_set, sipcidev); #endif #if NEISA > 0 static int si_eisa_probe __P((void)); static int si_eisa_attach __P((struct eisa_device *ed)); static struct eisa_driver si_eisa_driver = { "si", si_eisa_probe, si_eisa_attach, NULL, &sipcieisacount, }; DATA_SET(eisadriver_set, si_eisa_driver); #endif static d_open_t siopen; static d_close_t siclose; static d_read_t siread; static d_write_t siwrite; static d_ioctl_t siioctl; static d_stop_t sistop; static d_devtotty_t sidevtotty; -#define CDEV_MAJOR 68 -static struct cdevsw si_cdevsw = - { siopen, siclose, siread, siwrite, /*68*/ - siioctl, sistop, noreset, sidevtotty,/* si */ - ttpoll, nommap, NULL, "si", NULL, -1 }; - +#define CDEV_MAJOR 68 +static struct cdevsw si_cdevsw = { + siopen, siclose, siread, siwrite, + siioctl, sistop, noreset, sidevtotty, + ttpoll, nommap, NULL, "si", + NULL, -1, nodump, nopsize, + D_TTY, +}; #ifdef SI_DEBUG /* use: ``options "SI_DEBUG"'' in your config file */ static void si_dprintf __P((struct si_port *pp, int flags, const char *fmt, ...)); static char *si_mctl2str __P((enum si_mctl cmd)); #define DPRINT(x) si_dprintf x #else #define DPRINT(x) /* void */ #endif static int si_Nports; static int si_Nmodules; static int si_debug = 0; /* data, not bss, so it's patchable */ SYSCTL_INT(_machdep, OID_AUTO, si_debug, CTLFLAG_RW, &si_debug, 0, ""); static struct tty *si_tty; /* where the firmware lives; defined in si2_z280.c and si3_t225.c */ /* old: si2_z280.c */ extern unsigned char si2_z280_download[]; extern unsigned short si2_z280_downloadaddr; extern int si2_z280_dsize; /* new: si3_t225.c */ extern unsigned char si3_t225_download[]; extern unsigned short si3_t225_downloadaddr; extern int si3_t225_dsize; extern unsigned char si3_t225_bootstrap[]; extern unsigned short si3_t225_bootloadaddr; extern int si3_t225_bsize; struct si_softc { int sc_type; /* adapter type */ char *sc_typename; /* adapter type string */ struct si_port *sc_ports; /* port structures for this card */ caddr_t sc_paddr; /* physical addr of iomem */ caddr_t sc_maddr; /* kvaddr of iomem */ int sc_nport; /* # ports on this card */ int sc_irq; /* copy of attach irq */ #if NEISA > 0 int sc_eisa_iobase; /* EISA io port address */ int sc_eisa_irq; /* EISA irq number */ #endif #ifdef DEVFS struct { void *ttya; void *cuaa; void *ttyl; void *cual; void *ttyi; void *cuai; } devfs_token[32]; /* what is the max per card? */ void *control_token; #endif }; static struct si_softc si_softc[NSI]; /* up to 4 elements */ #ifndef B2000 /* not standard, but the hardware knows it. */ # define B2000 2000 #endif static struct speedtab bdrates[] = { B75, CLK75, /* 0x0 */ B110, CLK110, /* 0x1 */ B150, CLK150, /* 0x3 */ B300, CLK300, /* 0x4 */ B600, CLK600, /* 0x5 */ B1200, CLK1200, /* 0x6 */ B2000, CLK2000, /* 0x7 */ B2400, CLK2400, /* 0x8 */ B4800, CLK4800, /* 0x9 */ B9600, CLK9600, /* 0xb */ B19200, CLK19200, /* 0xc */ B38400, CLK38400, /* 0x2 (out of order!) */ B57600, CLK57600, /* 0xd */ B115200, CLK110, /* 0x1 (dupe!, 110 baud on "si") */ -1, -1 }; /* populated with approx character/sec rates - translated at card * initialisation time to chars per tick of the clock */ static int done_chartimes = 0; static struct speedtab chartimes[] = { B75, 8, B110, 11, B150, 15, B300, 30, B600, 60, B1200, 120, B2000, 200, B2400, 240, B4800, 480, B9600, 960, B19200, 1920, B38400, 3840, B57600, 5760, B115200, 11520, -1, -1 }; static volatile int in_intr = 0; /* Inside interrupt handler? */ #ifdef POLL static int si_pollrate; /* in addition to irq */ static int si_realpoll; /* poll HW on timer */ SYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, si_realpoll, CTLFLAG_RW, &si_realpoll, 0, ""); static int init_finished = 0; static void si_poll __P((void *)); #endif /* * Array of adapter types and the corresponding RAM size. The order of * entries here MUST match the ordinal of the adapter type. */ static char *si_type[] = { "EMPTY", "SIHOST", "SIMCA", /* FreeBSD does not support Microchannel */ "SIHOST2", "SIEISA", "SIPCI", "SXPCI", "SXISA", }; #if NPCI > 0 static char * sipciprobe(configid, deviceid) pcici_t configid; pcidi_t deviceid; { switch (deviceid) { case 0x400011cb: return("Specialix SI/XIO PCI host card"); break; case 0x200011cb: if (pci_conf_read(configid, SIJETSSIDREG) == 0x020011cb) return("Specialix SX PCI host card"); else return NULL; break; default: return NULL; } /*NOTREACHED*/ } void sipciattach(configid, unit) pcici_t configid; int unit; { struct isa_device id; vm_offset_t vaddr,paddr; u_long mapval = 0; /* shut up gcc, should not be needed */ switch ( pci_conf_read(configid, 0) >> 16 ) { case 0x4000: si_softc[unit].sc_type = SIPCI; mapval = SIPCIBADR; break; case 0x2000: si_softc[unit].sc_type = SIJETPCI; mapval = SIJETBADR; break; } if (!pci_map_mem(configid, mapval, &vaddr, &paddr)) { printf("si%d: couldn't map memory\n", unit); } /* * We're cheating here a little bit. The argument to an ISA * interrupt routine is the unit number. The argument to a * PCI interrupt handler is a void *, but we're simply going * to be lazy and hand it the unit number. */ if (!pci_map_int(configid, (pci_inthand_t *) si_intr, (void *)unit, &tty_imask)) { printf("si%d: couldn't map interrupt\n", unit); } si_softc[unit].sc_typename = si_type[si_softc[unit].sc_type]; /* * More cheating: We're going to dummy up a struct isa_device * and call the other attach routine. We don't really have to * fill in very much of the structure, since we filled in a * little of the soft state already. */ id.id_unit = unit; id.id_maddr = (caddr_t) vaddr; siattach(&id); } #endif #if NEISA > 0 static const char *si_eisa_match __P((eisa_id_t id)); static const char * si_eisa_match(id) eisa_id_t id; { if (id == SIEISADEVID) return ("Specialix SI/XIO EISA host card"); return (NULL); } static int si_eisa_probe(void) { struct eisa_device *ed = NULL; int count, irq; for (count=0; (ed = eisa_match_dev(ed, si_eisa_match)) != NULL; count++) { u_long port,maddr; port = (ed->ioconf.slot * EISA_SLOT_SIZE) + SIEISABASE; eisa_add_iospace(ed, port, SIEISAIOSIZE, RESVADDR_NONE); maddr = (inb(port+1) << 24) | (inb(port) << 16); irq = ((inb(port+2) >> 4) & 0xf); eisa_add_mspace(ed, maddr, SIEISA_MEMSIZE, RESVADDR_NONE); eisa_add_intr(ed, irq); eisa_registerdev(ed, &si_eisa_driver); count++; } return count; } static int si_eisa_attach(ed) struct eisa_device *ed; { struct isa_device id; resvaddr_t *maddr,*iospace; u_int irq; struct si_softc *sc; sc = &si_softc[ed->unit]; sc->sc_type = SIEISA; sc->sc_typename = si_type[sc->sc_type]; if ((iospace = ed->ioconf.ioaddrs.lh_first) == NULL) { printf("si%d: no iospace??\n", ed->unit); return -1; } sc->sc_eisa_iobase = iospace->addr; irq = ((inb(iospace->addr + 2) >> 4) & 0xf); sc->sc_eisa_irq = irq; if ((maddr = ed->ioconf.maddrs.lh_first) == NULL) { printf("si%d: where am I??\n", ed->unit); return -1; } eisa_reg_start(ed); if (eisa_reg_iospace(ed, iospace)) { printf("si%d: failed to register iospace 0x%x\n", ed->unit, iospace); return -1; } if (eisa_reg_mspace(ed, maddr)) { printf("si%d: failed to register memspace 0x%x\n", ed->unit, maddr); return -1; } /* * We're cheating here a little bit. The argument to an ISA * interrupt routine is the unit number. The argument to a * EISA interrupt handler is a void *, but we're simply going * to be lazy and hand it the unit number. */ if (eisa_reg_intr(ed, irq, (void (*)(void *)) si_intr, (void *)(intptr_t)(ed->unit), &tty_imask, 1)) { printf("si%d: failed to register interrupt %d\n", ed->unit, irq); return -1; } eisa_reg_end(ed); if (eisa_enable_intr(ed, irq)) { return -1; } /* * More cheating: We're going to dummy up a struct isa_device * and call the other attach routine. We don't really have to * fill in very much of the structure, since we filled in a * little of the soft state already. */ id.id_unit = ed->unit; id.id_maddr = (caddr_t) pmap_mapdev(maddr->addr, SIEISA_MEMSIZE); return (siattach(&id)); } #endif /* Look for a valid board at the given mem addr */ static int siprobe(id) struct isa_device *id; { struct si_softc *sc; int type; u_int i, ramsize; volatile BYTE was, *ux; volatile unsigned char *maddr; unsigned char *paddr; si_pollrate = POLLHZ; /* default 10 per second */ #ifdef REALPOLL si_realpoll = 1; /* scan always */ #endif maddr = id->id_maddr; /* virtual address... */ paddr = (caddr_t)vtophys(id->id_maddr); /* physical address... */ DPRINT((0, DBG_AUTOBOOT, "si%d: probe at virtual=0x%x physical=0x%x\n", id->id_unit, id->id_maddr, paddr)); /* * this is a lie, but it's easier than trying to handle caching * and ram conflicts in the >1M and <16M region. */ if ((caddr_t)paddr < (caddr_t)IOM_BEGIN || (caddr_t)paddr >= (caddr_t)IOM_END) { printf("si%d: iomem (%p) out of range\n", id->id_unit, (void *)paddr); return(0); } if (id->id_unit >= NSI) { /* THIS IS IMPOSSIBLE */ return(0); } if (((u_int)paddr & 0x7fff) != 0) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: iomem (%x) not on 32k boundary\n", id->id_unit, paddr)); return(0); } if (si_softc[id->id_unit].sc_typename) { /* EISA or PCI has taken this unit, choose another */ for (i=0; i < NSI; i++) { if (si_softc[i].sc_typename == NULL) { id->id_unit = i; break; } } if (i >= NSI) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: cannot realloc unit\n", id->id_unit)); return (0); } } for (i=0; i < NSI; i++) { sc = &si_softc[i]; if ((caddr_t)sc->sc_paddr == (caddr_t)paddr) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: iomem (%x) already configured to si%d\n", id->id_unit, sc->sc_paddr, i)); return(0); } } /* Is there anything out there? (0x17 is just an arbitrary number) */ *maddr = 0x17; if (*maddr != 0x17) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: 0x17 check fail at phys 0x%x\n", id->id_unit, paddr)); fail: return(0); } /* * Let's look first for a JET ISA card, since that's pretty easy * * All jet hosts are supposed to have this string in the IDROM, * but it's not worth checking on self-IDing busses like PCI. */ { unsigned char *jet_chk_str = "JET HOST BY KEV#"; for (i = 0; i < strlen(jet_chk_str); i++) if (jet_chk_str[i] != *(maddr + SIJETIDSTR + 2 * i)) goto try_mk2; } DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: JET first check - 0x%x\n", id->id_unit, (*(maddr+SIJETIDBASE)))); if (*(maddr+SIJETIDBASE) != (SISPLXID&0xff)) goto try_mk2; DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: JET second check - 0x%x\n", id->id_unit, (*(maddr+SIJETIDBASE+2)))); if (*(maddr+SIJETIDBASE+2) != ((SISPLXID&0xff00)>>8)) goto try_mk2; /* It must be a Jet ISA or RIO card */ DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: JET id check - 0x%x\n", id->id_unit, (*(maddr+SIUNIQID)))); if ((*(maddr+SIUNIQID) & 0xf0) !=0x20) goto try_mk2; /* It must be a Jet ISA SI/XIO card */ *(maddr + SIJETCONFIG) = 0; type = SIJETISA; ramsize = SIJET_RAMSIZE; goto got_card; /* * OK, now to see if whatever responded is really an SI card. * Try for a MK II next (SIHOST2) */ try_mk2: for (i = SIPLSIG; i < SIPLSIG + 8; i++) if ((*(maddr+i) & 7) != (~(BYTE)i & 7)) goto try_mk1; /* It must be an SIHOST2 */ *(maddr + SIPLRESET) = 0; *(maddr + SIPLIRQCLR) = 0; *(maddr + SIPLIRQSET) = 0x10; type = SIHOST2; ramsize = SIHOST2_RAMSIZE; goto got_card; /* * Its not a MK II, so try for a MK I (SIHOST) */ try_mk1: *(maddr+SIRESET) = 0x0; /* reset the card */ *(maddr+SIINTCL) = 0x0; /* clear int */ *(maddr+SIRAM) = 0x17; if (*(maddr+SIRAM) != (BYTE)0x17) goto fail; *(maddr+0x7ff8) = 0x17; if (*(maddr+0x7ff8) != (BYTE)0x17) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: 0x17 check fail at phys 0x%x = 0x%x\n", id->id_unit, paddr+0x77f8, *(maddr+0x77f8))); goto fail; } /* It must be an SIHOST (maybe?) - there must be a better way XXX */ type = SIHOST; ramsize = SIHOST_RAMSIZE; got_card: DPRINT((0, DBG_AUTOBOOT, "si%d: found type %d card, try memory test\n", id->id_unit, type)); /* Try the acid test */ ux = maddr + SIRAM; for (i = 0; i < ramsize; i++, ux++) *ux = (BYTE)(i&0xff); ux = maddr + SIRAM; for (i = 0; i < ramsize; i++, ux++) { if ((was = *ux) != (BYTE)(i&0xff)) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: match fail at phys 0x%x, was %x should be %x\n", id->id_unit, paddr + i, was, i&0xff)); goto fail; } } /* clear out the RAM */ ux = maddr + SIRAM; for (i = 0; i < ramsize; i++) *ux++ = 0; ux = maddr + SIRAM; for (i = 0; i < ramsize; i++) { if ((was = *ux++) != 0) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: clear fail at phys 0x%x, was %x\n", id->id_unit, paddr + i, was)); goto fail; } } /* * Success, we've found a valid board, now fill in * the adapter structure. */ switch (type) { case SIHOST2: if ((id->id_irq & (IRQ11|IRQ12|IRQ15)) == 0) { bad_irq: DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: bad IRQ value - %d\n", id->id_unit, id->id_irq)); return(0); } id->id_msize = SIHOST2_MEMSIZE; break; case SIHOST: if ((id->id_irq & (IRQ11|IRQ12|IRQ15)) == 0) { goto bad_irq; } id->id_msize = SIHOST_MEMSIZE; break; case SIJETISA: if ((id->id_irq & (IRQ9|IRQ10|IRQ11|IRQ12|IRQ15)) == 0) { goto bad_irq; } id->id_msize = SIJETISA_MEMSIZE; break; case SIMCA: /* MCA */ default: printf("si%d: %s not supported\n", id->id_unit, si_type[type]); return(0); } id->id_intr = (inthand2_t *)si_intr; /* set here instead of config */ si_softc[id->id_unit].sc_type = type; si_softc[id->id_unit].sc_typename = si_type[type]; return(-1); /* -1 == found */ } /* * We have to make an 8 bit version of bcopy, since some cards can't * deal with 32 bit I/O */ #if 1 static void si_bcopy(const void *src, void *dst, size_t len) { while (len--) *(((u_char *)dst)++) = *(((u_char *)src)++); } #else #define si_bcopy bcopy #endif /* * Attach the device. Initialize the card. * * This routine also gets called by the EISA and PCI attach routines. * It presumes that the softstate for the unit has had had its type field * and the EISA specific stuff filled in, as well as the kernel virtual * base address and the unit number of the isa_device struct. */ static int siattach(id) struct isa_device *id; { int unit = id->id_unit; struct si_softc *sc = &si_softc[unit]; struct si_port *pp; volatile struct si_channel *ccbp; volatile struct si_reg *regp; volatile caddr_t maddr; struct si_module *modp; struct tty *tp; struct speedtab *spt; int nmodule, nport, x, y; int uart_type; DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", id->id_unit)); sc->sc_paddr = (caddr_t)vtophys(id->id_maddr); sc->sc_maddr = id->id_maddr; sc->sc_irq = id->id_irq; DPRINT((0, DBG_AUTOBOOT, "si%d: type: %s paddr: %x maddr: %x\n", unit, sc->sc_typename, sc->sc_paddr, sc->sc_maddr)); sc->sc_ports = NULL; /* mark as uninitialised */ maddr = sc->sc_maddr; /* Stop the CPU first so it won't stomp around while we load */ switch (sc->sc_type) { #if NEISA > 0 case SIEISA: outb(sc->sc_eisa_iobase + 2, sc->sc_eisa_irq << 4); break; #endif #if NPCI > 0 case SIPCI: *(maddr+SIPCIRESET) = 0; break; case SIJETPCI: /* fall through to JET ISA */ #endif case SIJETISA: *(maddr+SIJETCONFIG) = 0; break; case SIHOST2: *(maddr+SIPLRESET) = 0; break; case SIHOST: *(maddr+SIRESET) = 0; break; default: /* this should never happen */ printf("si%d: unsupported configuration\n", unit); return 0; break; } /* OK, now lets download the download code */ if (SI_ISJET(sc->sc_type)) { DPRINT((0, DBG_DOWNLOAD, "si%d: jet_download: nbytes %d\n", id->id_unit, si3_t225_dsize)); si_bcopy(si3_t225_download, maddr + si3_t225_downloadaddr, si3_t225_dsize); DPRINT((0, DBG_DOWNLOAD, "si%d: jet_bootstrap: nbytes %d -> %x\n", id->id_unit, si3_t225_bsize, si3_t225_bootloadaddr)); si_bcopy(si3_t225_bootstrap, maddr + si3_t225_bootloadaddr, si3_t225_bsize); } else { DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n", id->id_unit, si2_z280_dsize)); si_bcopy(si2_z280_download, maddr + si2_z280_downloadaddr, si2_z280_dsize); } /* Now start the CPU */ switch (sc->sc_type) { #if NEISA > 0 case SIEISA: /* modify the download code to tell it that it's on an EISA */ *(maddr + 0x42) = 1; outb(sc->sc_eisa_iobase + 2, (sc->sc_eisa_irq << 4) | 4); (void)inb(sc->sc_eisa_iobase + 3); /* reset interrupt */ break; #endif case SIPCI: /* modify the download code to tell it that it's on a PCI */ *(maddr+0x42) = 1; *(maddr+SIPCIRESET) = 1; *(maddr+SIPCIINTCL) = 0; break; case SIJETPCI: *(maddr+SIJETRESET) = 0; *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN; break; case SIJETISA: *(maddr+SIJETRESET) = 0; switch (sc->sc_irq) { case IRQ9: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0x90; break; case IRQ10: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xa0; break; case IRQ11: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xb0; break; case IRQ12: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xc0; break; case IRQ15: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xf0; break; } break; case SIHOST: *(maddr+SIRESET_CL) = 0; *(maddr+SIINTCL_CL) = 0; break; case SIHOST2: *(maddr+SIPLRESET) = 0x10; switch (sc->sc_irq) { case IRQ11: *(maddr+SIPLIRQ11) = 0x10; break; case IRQ12: *(maddr+SIPLIRQ12) = 0x10; break; case IRQ15: *(maddr+SIPLIRQ15) = 0x10; break; } *(maddr+SIPLIRQCLR) = 0x10; break; default: /* this should _REALLY_ never happen */ printf("si%d: Uh, it was supported a second ago...\n", unit); return 0; } DELAY(1000000); /* wait around for a second */ regp = (struct si_reg *)maddr; y = 0; /* wait max of 5 sec for init OK */ while (regp->initstat == 0 && y++ < 10) { DELAY(500000); } switch (regp->initstat) { case 0: printf("si%d: startup timeout - aborting\n", unit); sc->sc_type = SIEMPTY; return 0; case 1: if (SI_ISJET(sc->sc_type)) { /* set throttle to 100 times per second */ regp->int_count = JET_INT_COUNT; /* rx_intr_count is a NOP in Jet */ } else { /* set throttle to 125 times per second */ regp->int_count = INT_COUNT; /* rx intr max of 25 times per second */ regp->rx_int_count = RXINT_COUNT; } regp->int_pending = 0; /* no intr pending */ regp->int_scounter = 0; /* reset counter */ break; case 0xff: /* * No modules found, so give up on this one. */ printf("si%d: %s - no ports found\n", unit, si_type[sc->sc_type]); return 0; default: printf("si%d: download code version error - initstat %x\n", unit, regp->initstat); return 0; } /* * First time around the ports just count them in order * to allocate some memory. */ nport = 0; modp = (struct si_module *)(maddr + 0x80); for (;;) { DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp)); switch (modp->sm_type) { case TA4: DPRINT((0, DBG_DOWNLOAD, "si%d: Found old TA4 module, 4 ports\n", unit)); x = 4; break; case TA8: DPRINT((0, DBG_DOWNLOAD, "si%d: Found old TA8 module, 8 ports\n", unit)); x = 8; break; case TA4_ASIC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found ASIC TA4 module, 4 ports\n", unit)); x = 4; break; case TA8_ASIC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found ASIC TA8 module, 8 ports\n", unit)); x = 8; break; case MTA: DPRINT((0, DBG_DOWNLOAD, "si%d: Found CD1400 module, 8 ports\n", unit)); x = 8; break; case SXDC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found SXDC module, 8 ports\n", unit)); x = 8; break; default: printf("si%d: unknown module type %d\n", unit, modp->sm_type); goto try_next; } /* this was limited in firmware and is also a driver issue */ if ((nport + x) > SI_MAXPORTPERCARD) { printf("si%d: extra ports ignored\n", unit); goto try_next; } nport += x; si_Nports += x; si_Nmodules++; try_next: if (modp->sm_next == 0) break; modp = (struct si_module *) (maddr + (unsigned)(modp->sm_next & 0x7fff)); } sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport, M_DEVBUF, M_NOWAIT); if (sc->sc_ports == 0) { mem_fail: printf("si%d: fail to malloc memory for port structs\n", unit); return 0; } bzero(sc->sc_ports, sizeof(struct si_port) * nport); sc->sc_nport = nport; /* * allocate tty structures for ports */ tp = (struct tty *)malloc(sizeof(*tp) * nport, M_DEVBUF, M_NOWAIT); if (tp == 0) goto mem_fail; bzero(tp, sizeof(*tp) * nport); si_tty = tp; /* * Scan round the ports again, this time initialising. */ pp = sc->sc_ports; nmodule = 0; modp = (struct si_module *)(maddr + 0x80); uart_type = 1000; /* arbitary, > uchar_max */ for (;;) { switch (modp->sm_type) { case TA4: nport = 4; break; case TA8: nport = 8; break; case TA4_ASIC: nport = 4; break; case TA8_ASIC: nport = 8; break; case MTA: nport = 8; break; case SXDC: nport = 8; break; default: goto try_next2; } nmodule++; ccbp = (struct si_channel *)((char *)modp + 0x100); if (uart_type == 1000) uart_type = ccbp->type; else if (uart_type != ccbp->type) printf("si%d: Warning: module %d mismatch! (%d%s != %d%s)\n", unit, nmodule, ccbp->type, si_modulename(sc->sc_type, ccbp->type), uart_type, si_modulename(sc->sc_type, uart_type)); for (x = 0; x < nport; x++, pp++, ccbp++) { pp->sp_ccb = ccbp; /* save the address */ pp->sp_tty = tp++; pp->sp_pend = IDLE_CLOSE; pp->sp_state = 0; /* internal flag */ pp->sp_dtr_wait = 3 * hz; pp->sp_iin.c_iflag = TTYDEF_IFLAG; pp->sp_iin.c_oflag = TTYDEF_OFLAG; pp->sp_iin.c_cflag = TTYDEF_CFLAG; pp->sp_iin.c_lflag = TTYDEF_LFLAG; termioschars(&pp->sp_iin); pp->sp_iin.c_ispeed = pp->sp_iin.c_ospeed = TTYDEF_SPEED;; pp->sp_iout = pp->sp_iin; } try_next2: if (modp->sm_next == 0) { printf("si%d: card: %s, ports: %d, modules: %d, type: %d%s\n", unit, sc->sc_typename, sc->sc_nport, nmodule, uart_type, si_modulename(sc->sc_type, uart_type)); break; } modp = (struct si_module *) (maddr + (unsigned)(modp->sm_next & 0x7fff)); } if (done_chartimes == 0) { for (spt = chartimes ; spt->sp_speed != -1; spt++) { if ((spt->sp_code /= hz) == 0) spt->sp_code = 1; } done_chartimes = 1; } #ifdef DEVFS /* path name devsw minor type uid gid perm*/ for ( x = 0; x < sc->sc_nport; x++ ) { /* sync with the manuals that start at 1 */ y = x + 1 + id->id_unit * (1 << SI_CARDSHIFT); sc->devfs_token[x].ttya = devfs_add_devswf( &si_cdevsw, x, DV_CHR, 0, 0, 0600, "ttyA%02d", y); sc->devfs_token[x].cuaa = devfs_add_devswf( &si_cdevsw, x + 0x00080, DV_CHR, 0, 0, 0600, "cuaA%02d", y); sc->devfs_token[x].ttyi = devfs_add_devswf( &si_cdevsw, x + 0x10000, DV_CHR, 0, 0, 0600, "ttyiA%02d", y); sc->devfs_token[x].cuai = devfs_add_devswf( &si_cdevsw, x + 0x10080, DV_CHR, 0, 0, 0600, "cuaiA%02d", y); sc->devfs_token[x].ttyl = devfs_add_devswf( &si_cdevsw, x + 0x20000, DV_CHR, 0, 0, 0600, "ttylA%02d", y); sc->devfs_token[x].cual = devfs_add_devswf( &si_cdevsw, x + 0x20080, DV_CHR, 0, 0, 0600, "cualA%02d", y); } sc->control_token = devfs_add_devswf(&si_cdevsw, 0x40000, DV_CHR, 0, 0, 0600, "si_control"); #endif return (1); } static int siopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { int oldspl, error; int card, port; register struct si_softc *sc; register struct tty *tp; volatile struct si_channel *ccbp; struct si_port *pp; int mynor = minor(dev); /* quickly let in /dev/si_control */ if (IS_CONTROLDEV(mynor)) { if ((error = suser(p->p_ucred, &p->p_acflag))) return(error); return(0); } card = SI_CARD(mynor); if (card >= NSI) return (ENXIO); sc = &si_softc[card]; if (sc->sc_type == SIEMPTY) { DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: type %s??\n", card, sc->sc_typename)); return(ENXIO); } port = SI_PORT(mynor); if (port >= sc->sc_nport) { DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: nports %d\n", card, sc->sc_nport)); return(ENXIO); } #ifdef POLL /* * We've now got a device, so start the poller. */ if (init_finished == 0) { timeout(si_poll, (caddr_t)0L, si_pollrate); init_finished = 1; } #endif /* initial/lock device */ if (IS_STATE(mynor)) { return(0); } pp = sc->sc_ports + port; tp = pp->sp_tty; /* the "real" tty */ ccbp = pp->sp_ccb; /* Find control block */ DPRINT((pp, DBG_ENTRY|DBG_OPEN, "siopen(%x,%x,%x,%x)\n", dev, flag, mode, p)); oldspl = spltty(); /* Keep others out */ error = 0; open_top: while (pp->sp_state & SS_DTR_OFF) { error = tsleep(&pp->sp_dtr_wait, TTIPRI|PCATCH, "sidtr", 0); if (error != 0) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialised. * handle conflicts. */ if (IS_CALLOUT(mynor)) { if (!pp->sp_active_out) { error = EBUSY; goto out; } } else { if (pp->sp_active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&pp->sp_active_out, TTIPRI|PCATCH, "sibi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { DPRINT((pp, DBG_OPEN|DBG_FAIL, "already open and EXCLUSIVE set\n")); error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Avoid sleep... :-) */ DPRINT((pp, DBG_OPEN, "first open\n")); tp->t_oproc = si_start; tp->t_param = siparam; tp->t_dev = dev; tp->t_termios = mynor & SI_CALLOUT_MASK ? pp->sp_iout : pp->sp_iin; (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); ++pp->sp_wopeners; /* in case of sleep in siparam */ error = siparam(tp, &tp->t_termios); --pp->sp_wopeners; if (error != 0) goto out; /* XXX: we should goto_top if siparam slept */ - ttsetwater(tp); - /* set initial DCD state */ pp->sp_last_hi_ip = ccbp->hi_ip; if ((pp->sp_last_hi_ip & IP_DCD) || IS_CALLOUT(mynor)) { (*linesw[tp->t_line].l_modem)(tp, 1); } } /* whoops! we beat the close! */ if (pp->sp_state & SS_CLOSING) { /* try and stop it from proceeding to bash the hardware */ pp->sp_state &= ~SS_CLOSING; } /* * Wait for DCD if necessary */ if (!(tp->t_state & TS_CARR_ON) && !IS_CALLOUT(mynor) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++pp->sp_wopeners; DPRINT((pp, DBG_OPEN, "sleeping for carrier\n")); error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sidcd", 0); --pp->sp_wopeners; if (error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); si_disc_optim(tp, &tp->t_termios, pp); if (tp->t_state & TS_ISOPEN && IS_CALLOUT(mynor)) pp->sp_active_out = TRUE; pp->sp_state |= SS_OPEN; /* made it! */ out: splx(oldspl); DPRINT((pp, DBG_OPEN, "leaving siopen\n")); if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0) sihardclose(pp); return(error); } static int siclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register struct si_port *pp; register struct tty *tp; int oldspl; int error = 0; int mynor = minor(dev); if (IS_SPECIAL(mynor)) return(0); oldspl = spltty(); pp = MINOR2PP(mynor); tp = pp->sp_tty; DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "siclose(%x,%x,%x,%x) sp_state:%x\n", dev, flag, mode, p, pp->sp_state)); /* did we sleep and loose a race? */ if (pp->sp_state & SS_CLOSING) { /* error = ESOMETING? */ goto out; } /* begin race detection.. */ pp->sp_state |= SS_CLOSING; si_write_enable(pp, 0); /* block writes for ttywait() */ /* THIS MAY SLEEP IN TTYWAIT!!! */ (*linesw[tp->t_line].l_close)(tp, flag); si_write_enable(pp, 1); /* did we sleep and somebody started another open? */ if (!(pp->sp_state & SS_CLOSING)) { /* error = ESOMETING? */ goto out; } /* ok. we are now still on the right track.. nuke the hardware */ if (pp->sp_state & SS_LSTART) { untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); pp->sp_state &= ~SS_LSTART; } sistop(tp, FREAD | FWRITE); sihardclose(pp); ttyclose(tp); pp->sp_state &= ~SS_OPEN; out: DPRINT((pp, DBG_CLOSE|DBG_EXIT, "close done, returning\n")); splx(oldspl); return(error); } static void sihardclose(pp) struct si_port *pp; { int oldspl; struct tty *tp; volatile struct si_channel *ccbp; oldspl = spltty(); tp = pp->sp_tty; ccbp = pp->sp_ccb; /* Find control block */ if (tp->t_cflag & HUPCL || (!pp->sp_active_out && !(ccbp->hi_ip & IP_DCD) && !(pp->sp_iin.c_cflag && CLOCAL)) || !(tp->t_state & TS_ISOPEN)) { (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); (void) si_command(pp, FCLOSE, SI_NOWAIT); if (pp->sp_dtr_wait != 0) { timeout(sidtrwakeup, pp, pp->sp_dtr_wait); pp->sp_state |= SS_DTR_OFF; } } pp->sp_active_out = FALSE; wakeup((caddr_t)&pp->sp_active_out); wakeup(TSA_CARR_ON(tp)); splx(oldspl); } /* * called at splsoftclock()... */ static void sidtrwakeup(chan) void *chan; { struct si_port *pp; int oldspl; oldspl = spltty(); pp = (struct si_port *)chan; pp->sp_state &= ~SS_DTR_OFF; wakeup(&pp->sp_dtr_wait); splx(oldspl); } /* * User level stuff - read and write */ static int siread(dev, uio, flag) register dev_t dev; struct uio *uio; int flag; { register struct tty *tp; int mynor = minor(dev); if (IS_SPECIAL(mynor)) { DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_READ, "siread(CONTROLDEV!!)\n")); return(ENODEV); } tp = MINOR2TP(mynor); DPRINT((TP2PP(tp), DBG_ENTRY|DBG_READ, "siread(%x,%x,%x)\n", dev, uio, flag)); return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { register struct si_port *pp; register struct tty *tp; int error = 0; int mynor = minor(dev); int oldspl; if (IS_SPECIAL(mynor)) { DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_WRITE, "siwrite(CONTROLDEV!!)\n")); return(ENODEV); } pp = MINOR2PP(mynor); tp = pp->sp_tty; DPRINT((pp, DBG_WRITE, "siwrite(%x,%x,%x)\n", dev, uio, flag)); oldspl = spltty(); /* * If writes are currently blocked, wait on the "real" tty */ while (pp->sp_state & SS_BLOCKWRITE) { pp->sp_state |= SS_WAITWRITE; DPRINT((pp, DBG_WRITE, "in siwrite, wait for SS_BLOCKWRITE to clear\n")); if ((error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH, "siwrite", tp->t_timeout))) { if (error == EWOULDBLOCK) error = EIO; goto out; } } error = (*linesw[tp->t_line].l_write)(tp, uio, flag); out: splx(oldspl); return (error); } static struct tty * sidevtotty(dev_t dev) { struct si_port *pp; int mynor = minor(dev); struct si_softc *sc = &si_softc[SI_CARD(mynor)]; if (IS_SPECIAL(mynor)) return(NULL); if (SI_PORT(mynor) >= sc->sc_nport) return(NULL); pp = MINOR2PP(mynor); return (pp->sp_tty); } static int siioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct si_port *pp; register struct tty *tp; int error; int mynor = minor(dev); int oldspl; int blocked = 0; #if defined(COMPAT_43) u_long oldcmd; struct termios term; #endif if (IS_SI_IOCTL(cmd)) return(si_Sioctl(dev, cmd, data, flag, p)); pp = MINOR2PP(mynor); tp = pp->sp_tty; DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%x,%lx,%x,%x)\n", dev, cmd, data, flag)); if (IS_STATE(mynor)) { struct termios *ct; switch (mynor & SI_STATE_MASK) { case SI_INIT_STATE_MASK: ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin; break; case SI_LOCK_STATE_MASK: ct = IS_CALLOUT(mynor) ? &pp->sp_lout : &pp->sp_lin; break; default: return (ENODEV); } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } /* * Do the old-style ioctl compat routines... */ #if defined(COMPAT_43) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif /* * Do the initial / lock state business */ if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & SI_CALLOUT_MASK ? &pp->sp_lout : &pp->sp_lin; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } /* * Block user-level writes to give the ttywait() * a chance to completely drain for commands * that require the port to be in a quiescent state. */ switch (cmd) { case TIOCSETAW: case TIOCSETAF: case TIOCDRAIN: #ifdef COMPAT_43 case TIOCSETP: #endif blocked++; /* block writes for ttywait() and siparam() */ si_write_enable(pp, 0); } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) goto out; oldspl = spltty(); error = ttioctl(tp, cmd, data, flag); si_disc_optim(tp, &tp->t_termios, pp); if (error != ENOIOCTL) goto outspl; switch (cmd) { case TIOCSBRK: si_command(pp, SBREAK, SI_WAIT); break; case TIOCCBRK: si_command(pp, EBREAK, SI_WAIT); break; case TIOCSDTR: (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); break; case TIOCCDTR: (void) si_modem(pp, SET, 0); break; case TIOCMSET: (void) si_modem(pp, SET, *(int *)data); break; case TIOCMBIS: (void) si_modem(pp, BIS, *(int *)data); break; case TIOCMBIC: (void) si_modem(pp, BIC, *(int *)data); break; case TIOCMGET: *(int *)data = si_modem(pp, GET, 0); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { goto outspl; } pp->sp_dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = pp->sp_dtr_wait * 100 / hz; break; default: error = ENOTTY; } error = 0; outspl: splx(oldspl); out: DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error)); if (blocked) si_write_enable(pp, 1); return(error); } /* * Handle the Specialix ioctls. All MUST be called via the CONTROL device */ static int si_Sioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct si_softc *xsc; register struct si_port *xpp; volatile struct si_reg *regp; struct si_tcsi *dp; struct si_pstat *sps; int *ip, error = 0; int oldspl; int card, port; int mynor = minor(dev); DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%x,%lx,%x,%x)\n", dev, cmd, data, flag)); #if 1 DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT)); DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB)); DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY)); #endif if (!IS_CONTROLDEV(mynor)) { DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n")); return(ENODEV); } oldspl = spltty(); /* better safe than sorry */ ip = (int *)data; #define SUCHECK if ((error = suser(p->p_ucred, &p->p_acflag))) goto out switch (cmd) { case TCSIPORTS: *ip = si_Nports; goto out; case TCSIMODULES: *ip = si_Nmodules; goto out; case TCSISDBG_ALL: SUCHECK; si_debug = *ip; goto out; case TCSIGDBG_ALL: *ip = si_debug; goto out; default: /* * Check that a controller for this port exists */ /* may also be a struct si_pstat, a superset of si_tcsi */ dp = (struct si_tcsi *)data; sps = (struct si_pstat *)data; card = dp->tc_card; xsc = &si_softc[card]; /* check.. */ if (card < 0 || card >= NSI || xsc->sc_type == SIEMPTY) { error = ENOENT; goto out; } /* * And check that a port exists */ port = dp->tc_port; if (port < 0 || port >= xsc->sc_nport) { error = ENOENT; goto out; } xpp = xsc->sc_ports + port; regp = (struct si_reg *)xsc->sc_maddr; } switch (cmd) { case TCSIDEBUG: #ifdef SI_DEBUG SUCHECK; if (xpp->sp_debug) xpp->sp_debug = 0; else { xpp->sp_debug = DBG_ALL; DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n", (xpp->sp_debug&DBG_ALL)?"ON":"OFF")); } break; #else error = ENODEV; goto out; #endif case TCSISDBG_LEVEL: case TCSIGDBG_LEVEL: #ifdef SI_DEBUG if (cmd == TCSIGDBG_LEVEL) { dp->tc_dbglvl = xpp->sp_debug; } else { SUCHECK; xpp->sp_debug = dp->tc_dbglvl; } break; #else error = ENODEV; goto out; #endif case TCSIGRXIT: dp->tc_int = regp->rx_int_count; break; case TCSIRXIT: SUCHECK; regp->rx_int_count = dp->tc_int; break; case TCSIGIT: dp->tc_int = regp->int_count; break; case TCSIIT: SUCHECK; regp->int_count = dp->tc_int; break; case TCSISTATE: dp->tc_int = xpp->sp_ccb->hi_ip; break; /* these next three use a different structure */ case TCSI_PORT: SUCHECK; si_bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport)); break; case TCSI_CCB: SUCHECK; si_bcopy((char *)xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb)); break; case TCSI_TTY: SUCHECK; si_bcopy(xpp->sp_tty, &sps->tc_tty, sizeof(sps->tc_tty)); break; default: error = EINVAL; goto out; } out: splx(oldspl); return(error); /* success */ } /* * siparam() : Configure line params * called at spltty(); * this may sleep, does not flush, nor wait for drain, nor block writes * caller must arrange this if it's important.. */ static int siparam(tp, t) register struct tty *tp; register struct termios *t; { register struct si_port *pp = TP2PP(tp); volatile struct si_channel *ccbp; int oldspl, cflag, iflag, oflag, lflag; int error = 0; /* shutup gcc */ int ispeed = 0; /* shutup gcc */ int ospeed = 0; /* shutup gcc */ BYTE val; DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t)); cflag = t->c_cflag; iflag = t->c_iflag; oflag = t->c_oflag; lflag = t->c_lflag; DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n", oflag, cflag, iflag, lflag)); /* XXX - if Jet host and SXDC module, use extended baud rates */ /* if not hung up.. */ if (t->c_ospeed != 0) { /* translate baud rate to firmware values */ ospeed = ttspeedtab(t->c_ospeed, bdrates); ispeed = t->c_ispeed ? ttspeedtab(t->c_ispeed, bdrates) : ospeed; /* enforce legit baud rate */ if (ospeed < 0 || ispeed < 0) return (EINVAL); } oldspl = spltty(); ccbp = pp->sp_ccb; /* ========== set hi_break ========== */ val = 0; if (iflag & IGNBRK) /* Breaks */ val |= BR_IGN; if (iflag & BRKINT) /* Interrupt on break? */ val |= BR_INT; if (iflag & PARMRK) /* Parity mark? */ val |= BR_PARMRK; if (iflag & IGNPAR) /* Ignore chars with parity errors? */ val |= BR_PARIGN; ccbp->hi_break = val; /* ========== set hi_csr ========== */ /* if not hung up.. */ if (t->c_ospeed != 0) { /* Set I/O speeds */ val = (ispeed << 4) | ospeed; } ccbp->hi_csr = val; /* ========== set hi_mr2 ========== */ val = 0; if (cflag & CSTOPB) /* Stop bits */ val |= MR2_2_STOP; else val |= MR2_1_STOP; /* * Enable H/W RTS/CTS handshaking. The default TA/MTA is * a DCE, hence the reverse sense of RTS and CTS */ /* Output Flow - RTS must be raised before data can be sent */ if (cflag & CCTS_OFLOW) val |= MR2_RTSCONT; ccbp->hi_mr2 = val; /* ========== set hi_mr1 ========== */ val = 0; if (!(cflag & PARENB)) /* Parity */ val |= MR1_NONE; else val |= MR1_WITH; if (cflag & PARODD) val |= MR1_ODD; if ((cflag & CS8) == CS8) { /* 8 data bits? */ val |= MR1_8_BITS; } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ val |= MR1_7_BITS; } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ val |= MR1_6_BITS; } else { /* Must be 5 */ val |= MR1_5_BITS; } /* * Enable H/W RTS/CTS handshaking. The default TA/MTA is * a DCE, hence the reverse sense of RTS and CTS */ /* Input Flow - CTS is raised when port is ready to receive data */ if (cflag & CRTS_IFLOW) val |= MR1_CTSCONT; ccbp->hi_mr1 = val; /* ========== set hi_mask ========== */ val = 0xff; if ((cflag & CS8) == CS8) { /* 8 data bits? */ val &= 0xFF; } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ val &= 0x7F; } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ val &= 0x3F; } else { /* Must be 5 */ val &= 0x1F; } if (iflag & ISTRIP) val &= 0x7F; ccbp->hi_mask = val; /* ========== set hi_prtcl ========== */ val = 0; /* Monitor DCD etc. if a modem */ if (!(cflag & CLOCAL)) val |= SP_DCEN; if (iflag & IXANY) val |= SP_TANY; if (iflag & IXON) val |= SP_TXEN; if (iflag & IXOFF) val |= SP_RXEN; if (iflag & INPCK) val |= SP_PAEN; ccbp->hi_prtcl = val; /* ========== set hi_{rx|tx}{on|off} ========== */ /* XXX: the card TOTALLY shields us from the flow control... */ ccbp->hi_txon = t->c_cc[VSTART]; ccbp->hi_txoff = t->c_cc[VSTOP]; ccbp->hi_rxon = t->c_cc[VSTART]; ccbp->hi_rxoff = t->c_cc[VSTOP]; /* ========== send settings to the card ========== */ /* potential sleep here */ if (ccbp->hi_stat == IDLE_CLOSE) /* Not yet open */ si_command(pp, LOPEN, SI_WAIT); /* open it */ else si_command(pp, CONFIG, SI_WAIT); /* change params */ /* ========== set DTR etc ========== */ /* Hangup if ospeed == 0 */ if (t->c_ospeed == 0) { (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); } else { /* * If the previous speed was 0, may need to re-enable * the modem signals */ (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); } DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n", ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break)); splx(oldspl); return(error); } /* * Enable or Disable the writes to this channel... * "state" -> enabled = 1; disabled = 0; */ static void si_write_enable(pp, state) register struct si_port *pp; int state; { int oldspl; oldspl = spltty(); if (state) { pp->sp_state &= ~SS_BLOCKWRITE; if (pp->sp_state & SS_WAITWRITE) { pp->sp_state &= ~SS_WAITWRITE; /* thunder away! */ wakeup((caddr_t)pp); } } else { pp->sp_state |= SS_BLOCKWRITE; } splx(oldspl); } /* * Set/Get state of modem control lines. * Due to DCE-like behaviour of the adapter, some signals need translation: * TIOCM_DTR DSR * TIOCM_RTS CTS */ static int si_modem(pp, cmd, bits) struct si_port *pp; enum si_mctl cmd; int bits; { volatile struct si_channel *ccbp; int x; DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits)); ccbp = pp->sp_ccb; /* Find channel address */ switch (cmd) { case GET: x = ccbp->hi_ip; bits = TIOCM_LE; if (x & IP_DCD) bits |= TIOCM_CAR; if (x & IP_DTR) bits |= TIOCM_DTR; if (x & IP_RTS) bits |= TIOCM_RTS; if (x & IP_RI) bits |= TIOCM_RI; return(bits); case SET: ccbp->hi_op &= ~(OP_DSR|OP_CTS); /* fall through */ case BIS: x = 0; if (bits & TIOCM_DTR) x |= OP_DSR; if (bits & TIOCM_RTS) x |= OP_CTS; ccbp->hi_op |= x; break; case BIC: if (bits & TIOCM_DTR) ccbp->hi_op &= ~OP_DSR; if (bits & TIOCM_RTS) ccbp->hi_op &= ~OP_CTS; } return 0; } /* * Handle change of modem state */ static void si_modem_state(pp, tp, hi_ip) register struct si_port *pp; register struct tty *tp; register int hi_ip; { /* if a modem dev */ if (hi_ip & IP_DCD) { if ( !(pp->sp_last_hi_ip & IP_DCD)) { DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n", tp->t_line)); (void)(*linesw[tp->t_line].l_modem)(tp, 1); } } else { if (pp->sp_last_hi_ip & IP_DCD) { DPRINT((pp, DBG_INTR, "modem carr off\n")); if ((*linesw[tp->t_line].l_modem)(tp, 0)) (void) si_modem(pp, SET, 0); } } pp->sp_last_hi_ip = hi_ip; } /* * Poller to catch missed interrupts. * * Note that the SYSV Specialix drivers poll at 100 times per second to get * better response. We could really use a "periodic" version timeout(). :-) */ #ifdef POLL static void si_poll(void *nothing) { register struct si_softc *sc; register int i; volatile struct si_reg *regp; register struct si_port *pp; int lost, oldspl, port; DPRINT((0, DBG_POLL, "si_poll()\n")); oldspl = spltty(); if (in_intr) goto out; lost = 0; for (i=0; isc_type == SIEMPTY) continue; regp = (struct si_reg *)sc->sc_maddr; /* * See if there has been a pending interrupt for 2 seconds * or so. The test (int_scounter >= 200) won't correspond * to 2 seconds if int_count gets changed. */ if (regp->int_pending != 0) { if (regp->int_scounter >= 200 && regp->initstat == 1) { printf("si%d: lost intr\n", i); lost++; } } else { regp->int_scounter = 0; } /* * gripe about no input flow control.. */ pp = sc->sc_ports; for (port = 0; port < sc->sc_nport; pp++, port++) { if (pp->sp_delta_overflows > 0) { printf("si%d: %d tty level buffer overflows\n", i, pp->sp_delta_overflows); pp->sp_delta_overflows = 0; } } } if (lost || si_realpoll) si_intr(-1); /* call intr with fake vector */ out: splx(oldspl); timeout(si_poll, (caddr_t)0L, si_pollrate); } #endif /* ifdef POLL */ /* * The interrupt handler polls ALL ports on ALL adapters each time * it is called. */ static BYTE si_rxbuf[SI_BUFFERSIZE]; /* input staging area */ static BYTE si_txbuf[SI_BUFFERSIZE]; /* output staging area */ static void si_intr(int unit) { register struct si_softc *sc; register struct si_port *pp; volatile struct si_channel *ccbp; register struct tty *tp; volatile caddr_t maddr; BYTE op, ip; int x, card, port, n, i, isopen; volatile BYTE *z; BYTE c; DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "si_intr(%d)\n", unit)); if (in_intr) { if (unit < 0) /* should never happen */ printf("si%d: Warning poll entered during interrupt\n", unit); else printf("si%d: Warning interrupt handler re-entered\n", unit); return; } in_intr = 1; /* * When we get an int we poll all the channels and do ALL pending * work, not just the first one we find. This allows all cards to * share the same vector. * * XXX - But if we're sharing the vector with something that's NOT * a SI/XIO/SX card, we may be making more work for ourselves. */ for (card = 0; card < NSI; card++) { sc = &si_softc[card]; if (sc->sc_type == SIEMPTY) continue; /* * First, clear the interrupt */ switch(sc->sc_type) { case SIHOST: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; /* flag nothing pending */ *(maddr+SIINTCL) = 0x00; /* Set IRQ clear */ *(maddr+SIINTCL_CL) = 0x00; /* Clear IRQ clear */ break; case SIHOST2: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPLIRQCLR) = 0x00; *(maddr+SIPLIRQCLR) = 0x10; break; #if NPCI > 0 case SIPCI: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPCIINTCL) = 0x0; break; case SIJETPCI: /* fall through to JETISA case */ #endif case SIJETISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIJETINTCL) = 0x0; break; #if NEISA > 0 case SIEISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; (void)inb(sc->sc_eisa_iobase + 3); break; #endif case SIEMPTY: default: continue; } ((volatile struct si_reg *)maddr)->int_scounter = 0; /* * check each port */ for (pp = sc->sc_ports, port=0; port < sc->sc_nport; pp++, port++) { ccbp = pp->sp_ccb; tp = pp->sp_tty; /* * See if a command has completed ? */ if (ccbp->hi_stat != pp->sp_pend) { DPRINT((pp, DBG_INTR, "si_intr hi_stat = 0x%x, pend = %d\n", ccbp->hi_stat, pp->sp_pend)); switch(pp->sp_pend) { case LOPEN: case MPEND: case MOPEN: case CONFIG: case SBREAK: case EBREAK: pp->sp_pend = ccbp->hi_stat; /* sleeping in si_command */ wakeup(&pp->sp_state); break; default: pp->sp_pend = ccbp->hi_stat; } } /* * Continue on if it's closed */ if (ccbp->hi_stat == IDLE_CLOSE) { continue; } /* * Do modem state change if not a local device */ si_modem_state(pp, tp, ccbp->hi_ip); /* * Check to see if we should 'receive' characters. */ if (tp->t_state & TS_CONNECTED && tp->t_state & TS_ISOPEN) isopen = 1; else isopen = 0; /* * Do input break processing */ if (ccbp->hi_state & ST_BREAK) { if (isopen) { (*linesw[tp->t_line].l_rint)(TTY_BI, tp); } ccbp->hi_state &= ~ST_BREAK; /* A Bit iffy this */ DPRINT((pp, DBG_INTR, "si_intr break\n")); } /* * Do RX stuff - if not open then dump any characters. * XXX: This is VERY messy and needs to be cleaned up. * * XXX: can we leave data in the host adapter buffer * when the clists are full? That may be dangerous * if the user cannot get an interrupt signal through. */ more_rx: /* XXX Sorry. the nesting was driving me bats! :-( */ if (!isopen) { ccbp->hi_rxopos = ccbp->hi_rxipos; goto end_rx; } /* * If the tty input buffers are blocked, stop emptying * the incoming buffers and let the auto flow control * assert.. */ if (tp->t_state & TS_TBLOCK) { goto end_rx; } /* * Process read characters if not skipped above */ op = ccbp->hi_rxopos; ip = ccbp->hi_rxipos; c = ip - op; if (c == 0) { goto end_rx; } n = c & 0xff; if (n > 250) n = 250; DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", n, op, ip)); /* * Suck characters out of host card buffer into the * "input staging buffer" - so that we dont leave the * host card in limbo while we're possibly echoing * characters and possibly flushing input inside the * ldisc l_rint() routine. */ if (n <= SI_BUFFERSIZE - op) { DPRINT((pp, DBG_INTR, "\tsingle copy\n")); z = ccbp->hi_rxbuf + op; si_bcopy((caddr_t)z, si_rxbuf, n); op += n; } else { x = SI_BUFFERSIZE - op; DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x)); z = ccbp->hi_rxbuf + op; si_bcopy((caddr_t)z, si_rxbuf, x); DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", n - x)); z = ccbp->hi_rxbuf; si_bcopy((caddr_t)z, si_rxbuf + x, n - x); op += n; } /* clear collected characters from buffer */ ccbp->hi_rxopos = op; DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", n, op, ip)); /* * at this point... * n = number of chars placed in si_rxbuf */ /* * Avoid the grotesquely inefficient lineswitch * routine (ttyinput) in "raw" mode. It usually * takes about 450 instructions (that's without * canonical processing or echo!). slinput is * reasonably fast (usually 40 instructions * plus call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { /* block if the driver supports it */ if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER && (tp->t_cflag & CRTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += n; tk_rawcc += n; tp->t_rawcc += n; pp->sp_delta_overflows += b_to_q((char *)si_rxbuf, n, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; si_start(tp); } } else { /* * It'd be nice to not have to go through the * function call overhead for each char here. * It'd be nice to block input it, saving a * loop here and the call/return overhead. */ for(x = 0; x < n; x++) { i = si_rxbuf[x]; if ((*linesw[tp->t_line].l_rint)(i, tp) == -1) { pp->sp_delta_overflows++; } /* * doesn't seem to be much point doing * this here.. this driver has no * softtty processing! ?? */ if (pp->sp_hotchar && i == pp->sp_hotchar) { setsofttty(); } } } goto more_rx; /* try for more until RXbuf is empty */ end_rx: /* XXX: Again, sorry about the gotos.. :-) */ /* * Do TX stuff */ (*linesw[tp->t_line].l_start)(tp); } /* end of for (all ports on this controller) */ } /* end of for (all controllers) */ in_intr = 0; DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "end si_intr(%d)\n", unit)); } /* * Nudge the transmitter... * * XXX: I inherited some funny code here. It implies the host card only * interrupts when the transmit buffer reaches the low-water-mark, and does * not interrupt when it's actually hits empty. In some cases, we have * processes waiting for complete drain, and we need to simulate an interrupt * about when we think the buffer is going to be empty (and retry if not). * I really am not certain about this... I *need* the hardware manuals. */ static void si_start(tp) register struct tty *tp; { struct si_port *pp; volatile struct si_channel *ccbp; register struct clist *qp; BYTE ipos; int nchar; int oldspl, count, n, amount, buffer_full; oldspl = spltty(); qp = &tp->t_outq; pp = TP2PP(tp); DPRINT((pp, DBG_ENTRY|DBG_START, "si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n", tp, tp->t_state, pp->sp_state, qp->c_cc)); if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) goto out; buffer_full = 0; ccbp = pp->sp_ccb; count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; DPRINT((pp, DBG_START, "count %d\n", (BYTE)count)); while ((nchar = qp->c_cc) > 0) { if ((BYTE)count >= 255) { buffer_full++; break; } amount = min(nchar, (255 - (BYTE)count)); ipos = (unsigned int)ccbp->hi_txipos; n = q_to_b(&tp->t_outq, si_txbuf, amount); /* will it fit in one lump? */ if ((SI_BUFFERSIZE - ipos) >= n) { si_bcopy(si_txbuf, (char *)&ccbp->hi_txbuf[ipos], n); } else { si_bcopy(si_txbuf, (char *)&ccbp->hi_txbuf[ipos], SI_BUFFERSIZE - ipos); si_bcopy(si_txbuf + (SI_BUFFERSIZE - ipos), (char *)&ccbp->hi_txbuf[0], n - (SI_BUFFERSIZE - ipos)); } ccbp->hi_txipos += n; count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; } if (count != 0 && nchar == 0) { tp->t_state |= TS_BUSY; } else { tp->t_state &= ~TS_BUSY; } /* wakeup time? */ ttwwakeup(tp); DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n", (BYTE)count, nchar, tp->t_state)); if (tp->t_state & TS_BUSY) { int time; time = ttspeedtab(tp->t_ospeed, chartimes); if (time > 0) { if (time < nchar) time = nchar / time; else time = 2; } else { DPRINT((pp, DBG_START, "bad char time value! %d\n", time)); time = hz/10; } if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) { untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); } else { pp->sp_state |= SS_LSTART; } DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time)); pp->lstart_ch = timeout(si_lstart, (caddr_t)pp, time); } out: splx(oldspl); DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n")); } /* * Note: called at splsoftclock from the timeout code * This has to deal with two things... cause wakeups while waiting for * tty drains on last process exit, and call l_start at about the right * time for protocols like ppp. */ static void si_lstart(void *arg) { register struct si_port *pp = arg; register struct tty *tp; int oldspl; DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n", pp, pp->sp_state)); oldspl = spltty(); if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) { splx(oldspl); return; } pp->sp_state &= ~SS_LSTART; pp->sp_state |= SS_INLSTART; tp = pp->sp_tty; /* deal with the process exit case */ ttwwakeup(tp); /* nudge protocols - eg: ppp */ (*linesw[tp->t_line].l_start)(tp); pp->sp_state &= ~SS_INLSTART; splx(oldspl); } /* * Stop output on a line. called at spltty(); */ void sistop(tp, rw) register struct tty *tp; int rw; { volatile struct si_channel *ccbp; struct si_port *pp; pp = TP2PP(tp); ccbp = pp->sp_ccb; DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "sistop(%x,%x)\n", tp, rw)); /* XXX: must check (rw & FWRITE | FREAD) etc flushing... */ if (rw & FWRITE) { /* what level are we meant to be flushing anyway? */ if (tp->t_state & TS_BUSY) { si_command(TP2PP(tp), WFLUSH, SI_NOWAIT); tp->t_state &= ~TS_BUSY; ttwwakeup(tp); /* Bruce???? */ } } #if 1 /* XXX: this doesn't work right yet.. */ /* XXX: this may have been failing because we used to call l_rint() * while we were looping based on these two counters. Now, we collect * the data and then loop stuffing it into l_rint(), making this * useless. Should we cause this to blow away the staging buffer? */ if (rw & FREAD) { ccbp->hi_rxopos = ccbp->hi_rxipos; } #endif } /* * Issue a command to the host card CPU. */ static void si_command(pp, cmd, waitflag) struct si_port *pp; /* port control block (local) */ int cmd; int waitflag; { int oldspl; volatile struct si_channel *ccbp = pp->sp_ccb; int x; DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n", pp, cmd, waitflag, ccbp->hi_stat)); oldspl = spltty(); /* Keep others out */ /* wait until it's finished what it was doing.. */ /* XXX: sits in IDLE_BREAK until something disturbs it or break * is turned off. */ while((x = ccbp->hi_stat) != IDLE_OPEN && x != IDLE_CLOSE && x != IDLE_BREAK && x != cmd) { if (in_intr) { /* Prevent sleep in intr */ DPRINT((pp, DBG_PARAM, "cmd intr collision - completing %d\trequested %d\n", x, cmd)); splx(oldspl); return; } else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, "sicmd1", 1)) { splx(oldspl); return; } } /* it should now be in IDLE_{OPEN|CLOSE|BREAK}, or "cmd" */ /* if there was a pending command, cause a state-change wakeup */ switch(pp->sp_pend) { case LOPEN: case MPEND: case MOPEN: case CONFIG: case SBREAK: case EBREAK: wakeup(&pp->sp_state); break; default: break; } pp->sp_pend = cmd; /* New command pending */ ccbp->hi_stat = cmd; /* Post it */ if (waitflag) { if (in_intr) { /* If in interrupt handler */ DPRINT((pp, DBG_PARAM, "attempt to sleep in si_intr - cmd req %d\n", cmd)); splx(oldspl); return; } else while(ccbp->hi_stat != IDLE_OPEN && ccbp->hi_stat != IDLE_BREAK) { if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, "sicmd2", 0)) break; } } splx(oldspl); } static void si_disc_optim(tp, t, pp) struct tty *tp; struct termios *t; struct si_port *pp; { /* * XXX can skip a lot more cases if Smarts. Maybe * (IGNCR | ISTRIP | IXON) in c_iflag. But perhaps we * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state. */ if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; pp->sp_hotchar = linesw[tp->t_line].l_hotchar; DPRINT((pp, DBG_OPTIM, "bypass: %s, hotchar: %x\n", (tp->t_state & TS_CAN_BYPASS_L_RINT) ? "on" : "off", pp->sp_hotchar)); } #ifdef SI_DEBUG static void #ifdef __STDC__ si_dprintf(struct si_port *pp, int flags, const char *fmt, ...) #else si_dprintf(pp, flags, fmt, va_alist) struct si_port *pp; int flags; char *fmt; #endif { va_list ap; if ((pp == NULL && (si_debug&flags)) || (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) { if (pp != NULL) printf("%ci%d(%d): ", 's', (int)SI_CARD(pp->sp_tty->t_dev), (int)SI_PORT(pp->sp_tty->t_dev)); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } } static char * si_mctl2str(cmd) enum si_mctl cmd; { switch (cmd) { case GET: return("GET"); case SET: return("SET"); case BIS: return("BIS"); case BIC: return("BIC"); } return("BAD"); } #endif /* DEBUG */ static char * si_modulename(host_type, uart_type) int host_type, uart_type; { switch (host_type) { /* Z280 based cards */ #if NEISA > 0 case SIEISA: #endif case SIHOST2: case SIHOST: #if NPCI > 0 case SIPCI: #endif switch (uart_type) { case 0: return(" (XIO)"); case 1: return(" (SI)"); } break; /* T225 based hosts */ #if NPCI > 0 case SIJETPCI: #endif case SIJETISA: switch (uart_type) { case 0: return(" (SI)"); case 40: return(" (XIO)"); case 72: return(" (SXDC)"); } break; } return(""); } static si_devsw_installed = 0; static void si_drvinit(void *unused) { dev_t dev; if (!si_devsw_installed) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&si_cdevsw, NULL); si_devsw_installed = 1; } } SYSINIT(sidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,si_drvinit,NULL) diff --git a/sys/dev/sio/sio.c b/sys/dev/sio/sio.c index c36de1726369..ab219502e475 100644 --- a/sys/dev/sio/sio.c +++ b/sys/dev/sio/sio.c @@ -1,3040 +1,3041 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.211 1998/08/19 04:17:37 bde Exp $ + * $Id: sio.c,v 1.212 1998/08/20 05:12:48 bde Exp $ */ #include "opt_comconsole.h" #include "opt_compat.h" #include "opt_ddb.h" #include "opt_devfs.h" #include "opt_sio.h" #include "sio.h" #include "pnp.h" #ifndef EXTRA_SIO #if NPNP > 0 #define EXTRA_SIO 2 #else #define EXTRA_SIO 0 #endif #endif #define NSIOTOT (NSIO + EXTRA_SIO) /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. * * Changes for PC-Card integration: * - Added PC-Card driver table and handlers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #ifdef COM_ESP #include #endif #include #include "card.h" #if NCARD > 0 #include #include #endif #if NPNP > 0 #include #endif #ifdef SMP #define disable_intr() COM_DISABLE_INTR() #define enable_intr() COM_ENABLE_INTR() #endif /* SMP */ #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RS_IBUFSIZE 256 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) #define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ #define COM_CONSOLE(dev) ((dev)->id_flags & 0x10) #define COM_FORCECONSOLE(dev) ((dev)->id_flags & 0x20) #define COM_LLCONSOLE(dev) ((dev)->id_flags & 0x40) #define COM_LOSESOUTINTS(dev) ((dev)->id_flags & 0x08) #define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) #define COM_ST16650A(dev) ((dev)->id_flags & 0x20000) #define COM_C_NOPROBE (0x40000) #define COM_NOPROBE(dev) ((dev)->id_flags & COM_C_NOPROBE) #define COM_C_IIR_TXRDYBUG (0x80000) #define COM_IIR_TXRDYBUG(dev) ((dev)->id_flags & COM_C_IIR_TXRDYBUG) #define COM_FIFOSIZE(dev) (((dev)->id_flags & 0xff000000) >> 24) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * siostop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ #define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_int id_flags; /* Copy isa device falgas */ u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ #ifdef COM_ESP bool_t esp; /* is this unit a hayes esp board? */ #endif u_char extra_state; /* more flag bits, separate for order trick */ u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t gone; /* hardware disappeared */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ Port_t data_port; /* i/o ports */ #ifdef COM_ESP Port_t esp_port; #endif Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; Port_t intr_ctl_port; /* Ports of IIR register */ struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; #ifdef DEVFS void *devfs_token_ttyd; void *devfs_token_ttyl; void *devfs_token_ttyi; void *devfs_token_cuaa; void *devfs_token_cual; void *devfs_token_cuai; #endif }; #ifdef COM_ESP static int espattach __P((struct isa_device *isdp, struct com_s *com, Port_t esp_port)); #endif static int sioattach __P((struct isa_device *dev)); static timeout_t siobusycheck; static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); static void siointr1 __P((struct com_s *com)); static int commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static swihand_t siopoll; static int sioprobe __P((struct isa_device *dev)); static void siosettimeout __P((void)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com)); #ifdef DSI_SOFT_MODEM static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr)); #endif /* DSI_SOFT_MODEM */ static char driver_name[] = "sio"; /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIOTOT]; #define com_addr(unit) (p_com_addr[unit]) struct isa_driver siodriver = { sioprobe, sioattach, driver_name }; static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static d_stop_t siostop; static d_devtotty_t siodevtotty; -#define CDEV_MAJOR 28 -static struct cdevsw sio_cdevsw = { +#define CDEV_MAJOR 28 +static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, ttpoll, nommap, NULL, driver_name, - NULL, -1, + NULL, -1, nodump, nopsize, + D_TTY, }; static int comconsole = -1; static volatile speed_t comdefaultrate = CONSPEED; static u_int com_events; /* input chars + weighted output completions */ static Port_t siocniobase; static bool_t sio_registered; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); #if 0 /* XXX */ static struct tty *sio_tty[NSIOTOT]; #else static struct tty sio_tty[NSIOTOT]; #endif static const int nsio_tty = NSIOTOT; static struct speedtab comspeedtab[] = { { 0, 0 }, { 50, COMBRD(50) }, { 75, COMBRD(75) }, { 110, COMBRD(110) }, { 134, COMBRD(134) }, { 150, COMBRD(150) }, { 200, COMBRD(200) }, { 300, COMBRD(300) }, { 600, COMBRD(600) }, { 1200, COMBRD(1200) }, { 1800, COMBRD(1800) }, { 2400, COMBRD(2400) }, { 4800, COMBRD(4800) }, { 9600, COMBRD(9600) }, { 19200, COMBRD(19200) }, { 38400, COMBRD(38400) }, { 57600, COMBRD(57600) }, { 115200, COMBRD(115200) }, { -1, -1 } }; #ifdef COM_ESP /* XXX configure this properly. */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; #endif /* * handle sysctl read/write requests for console speed * * In addition to setting comdefaultrate for I/O through /dev/console, * also set the initial and lock values for the /dev/ttyXX device * if there is one associated with the console. Finally, if the /dev/tty * device has already been open, change the speed on the open running port * itself. */ static int sysctl_machdep_comdefaultrate SYSCTL_HANDLER_ARGS { int error, s; speed_t newspeed; struct com_s *com; struct tty *tp; newspeed = comdefaultrate; error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); if (error || !req->newptr) return (error); comdefaultrate = newspeed; if (comconsole < 0) /* serial console not selected? */ return (0); com = com_addr(comconsole); if (!com) return (ENXIO); /* * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX * (note, the lock rates really are boolean -- if non-zero, disallow * speed changes) */ com->it_in.c_ispeed = com->it_in.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_out.c_ispeed = com->it_out.c_ospeed = com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; /* * if we're open, change the running rate too */ tp = com->tp; if (tp && (tp->t_state & TS_ISOPEN)) { tp->t_termios.c_ispeed = tp->t_termios.c_ospeed = comdefaultrate; s = spltty(); error = comparam(tp, &tp->t_termios); splx(s); } return error; } SYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_machdep_comdefaultrate, "I", ""); #if NCARD > 0 /* * PC-Card (PCMCIA) specific code. */ static int sioinit __P((struct pccard_devinfo *)); static void siounload __P((struct pccard_devinfo *)); static int card_intr __P((struct pccard_devinfo *)); static struct pccard_device sio_info = { driver_name, sioinit, siounload, card_intr, 0, /* Attributes - presently unused */ &tty_imask /* Interrupt mask for device */ /* XXX - Should this also include net_imask? */ }; DATA_SET(pccarddrv_set, sio_info); /* * Initialize the device - called from Slot manager. */ int sioinit(struct pccard_devinfo *devi) { /* validate unit number. */ if (devi->isahd.id_unit >= (NSIOTOT)) return(ENODEV); /* Make sure it isn't already probed. */ if (com_addr(devi->isahd.id_unit)) return(EBUSY); /* It's already probed as serial by Upper */ devi->isahd.id_flags |= COM_C_NOPROBE; /* * Probe the device. If a value is returned, the * device was found at the location. */ if (sioprobe(&devi->isahd) == 0) return(ENXIO); if (sioattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * siounload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void siounload(struct pccard_devinfo *devi) { struct com_s *com; if (!devi) { printf("NULL devi in siounload\n"); return; } com = com_addr(devi->isahd.id_unit); if (!com) { printf("NULL com in siounload\n"); return; } if (!com->iobase) { printf("sio%d already unloaded!\n",devi->isahd.id_unit); return; } if (com->tp && (com->tp->t_state & TS_ISOPEN)) { com->gone = 1; printf("sio%d: unload\n", devi->isahd.id_unit); com->tp->t_gen++; ttyclose(com->tp); ttwakeup(com->tp); ttwwakeup(com->tp); } else { com_addr(com->unit) = NULL; bzero(com, sizeof *com); free(com,M_TTYS); printf("sio%d: unload,gone\n", devi->isahd.id_unit); } } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(struct pccard_devinfo *devi) { struct com_s *com; COM_LOCK(); com = com_addr(devi->isahd.id_unit); if (com && !com->gone) siointr1(com_addr(devi->isahd.id_unit)); COM_UNLOCK(); return(1); } #endif /* NCARD > 0 */ static int sioprobe(dev) struct isa_device *dev; { static bool_t already_init; bool_t failures[10]; int fn; struct isa_device *idev; Port_t iobase; intrmask_t irqmap[4]; intrmask_t irqs; u_char mcr_image; int result; struct isa_device *xdev; if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ for (xdev = isa_devtab_tty; xdev->id_driver != NULL; xdev++) if (xdev->id_driver == &siodriver && xdev->id_enabled) outb(xdev->id_iobase + com_mcr, 0); already_init = TRUE; } if (COM_LLCONSOLE(dev)) { printf("sio%d: reserved for low-level i/o\n", dev->id_unit); return (0); } /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(dev)) { idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); dev->id_irq = 0; idev = dev; } if (!COM_NOTAST4(dev)) { outb(idev->id_iobase + com_scr, idev->id_irq ? 0x80 : 0); mcr_image = 0; } } #endif /* COM_MULTIPORT */ if (idev->id_irq == 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = dev->id_iobase; /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ disable_intr(); /* EXTRA DELAY? */ /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ if (iobase == siocniobase) DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); else { outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); outb(iobase + com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff); outb(iobase + com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8); outb(iobase + com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); } /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); outb(iobase + com_ier, 0); DELAY(1000); /* XXX */ irqmap[0] = isa_irq_pending(); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ outb(iobase + com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); /* * It's a definitly Serial PCMCIA(16550A), but still be required * for IIR_TXRDY implementation ( Palido 321s, DC-1S... ) */ if ( COM_NOPROBE(dev) ) { /* Reading IIR register twice */ for ( fn = 0; fn < 2; fn ++ ) { DELAY(10000); failures[6] = inb(iobase + com_iir); } /* Check IIR_TXRDY clear ? */ result = IO_COMSIZE; if ( failures[6] & IIR_TXRDY ) { /* Nop, Double check with clearing IER */ outb(iobase + com_ier, 0); if ( inb(iobase + com_iir) & IIR_NOPEND ) { /* Ok. we're familia this gang */ dev->id_flags |= COM_C_IIR_TXRDYBUG; /* Set IIR_TXRDYBUG */ } else { /* Unknow, Just omit this chip.. XXX*/ result = 0; } } else { /* OK. this is well-known guys */ dev->id_flags &= ~COM_C_IIR_TXRDYBUG; /*Clear IIR_TXRDYBUG*/ } outb(iobase + com_cfcr, CFCR_8BITS); enable_intr(); return (iobase == siocniobase ? IO_COMSIZE : result); } /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; failures[1] = inb(iobase + com_ier) - IER_ETXRDY; failures[2] = inb(iobase + com_mcr) - mcr_image; DELAY(10000); /* Some internal modems need this time */ irqmap[1] = isa_irq_pending(); failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; DELAY(1000); /* XXX */ irqmap[2] = isa_irq_pending(); failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* * Turn off all device interrupts and check that they go off properly. * Leave MCR_IENABLE alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving at) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); DELAY(1000); /* XXX */ irqmap[3] = isa_irq_pending(); failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; enable_intr(); irqs = irqmap[1] & ~irqmap[0]; if (idev->id_irq != 0 && (idev->id_irq & irqs) == 0) printf( "sio%d: configured irq %d not in bitmap of probed irqs %#x\n", dev->id_unit, ffs(idev->id_irq) - 1, irqs); if (bootverbose) printf("sio%d: irq maps: %#x %#x %#x %#x\n", dev->id_unit, irqmap[0], irqmap[1], irqmap[2], irqmap[3]); result = IO_COMSIZE; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { outb(iobase + com_mcr, 0); result = 0; if (bootverbose) { printf("sio%d: probe failed test(s):", dev->id_unit); for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) printf(" %d", fn); printf("\n"); } break; } return (iobase == siocniobase ? IO_COMSIZE : result); } #ifdef COM_ESP static int espattach(isdp, com, esp_port) struct isa_device *isdp; struct com_s *com; Port_t esp_port; { u_char dips; u_char val; /* * Check the ESP-specific I/O port to see if we're an ESP * card. If not, return failure immediately. */ if ((inb(esp_port) & 0xf3) == 0) { printf(" port 0x%x is not an ESP board?\n", esp_port); return (0); } /* * We've got something that claims to be a Hayes ESP card. * Let's hope so. */ /* Get the dip-switch configuration */ outb(esp_port + ESP_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP_STATUS1); /* * Bits 0,1 of dips say which COM port we are. */ if (com->iobase == likely_com_ports[dips & 0x03]) printf(" : ESP"); else { printf(" esp_port has com %d\n", dips & 0x03); return (0); } /* * Check for ESP version 2.0 or later: bits 4,5,6 = 010. */ outb(esp_port + ESP_CMD1, ESP_GETTEST); val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP_STATUS2); if ((val & 0x70) < 0x20) { printf("-old (%o)", val & 0x70); return (0); } /* * Check for ability to emulate 16550: bit 7 == 1 */ if ((dips & 0x80) == 0) { printf(" slave"); return (0); } /* * Okay, we seem to be a Hayes ESP card. Whee. */ com->esp = TRUE; com->esp_port = esp_port; return (1); } #endif /* COM_ESP */ static int sioattach(isdp) struct isa_device *isdp; { struct com_s *com; dev_t dev; #ifdef COM_ESP Port_t *espp; #endif Port_t iobase; int s; int unit; isdp->id_ri_flags |= RI_FAST; iobase = isdp->id_iobase; unit = isdp->id_unit; com = malloc(sizeof *com, M_TTYS, M_NOWAIT); if (com == NULL) return (0); /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->unit = unit; com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->loses_outints = COM_LOSESOUTINTS(isdp) != 0; com->no_irq = isdp->id_irq == 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->iobase = iobase; com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; com->intr_ctl_port = iobase + com_ier; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; com->lt_out.c_ispeed = com->lt_out.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; } else com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; termioschars(&com->it_in); com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifdef DSI_SOFT_MODEM if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) { printf(" Digicom Systems, Inc. SoftModem"); goto determined_type; } #endif /* DSI_SOFT_MODEM */ #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(isdp) && !COM_IIR_TXRDYBUG(isdp)) #else if (!COM_IIR_TXRDYBUG(isdp)) #endif { u_char scr; u_char scr1; u_char scr2; scr = inb(iobase + com_scr); outb(iobase + com_scr, 0xa5); scr1 = inb(iobase + com_scr); outb(iobase + com_scr, 0x5a); scr2 = inb(iobase + com_scr); outb(iobase + com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250"); goto determined_type; } } outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); DELAY(100); com->st16650a = 0; switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_RX_LOW: printf(" 16450"); break; case FIFO_RX_MEDL: printf(" 16450?"); break; case FIFO_RX_MEDH: printf(" 16550?"); break; case FIFO_RX_HIGH: if (COM_NOFIFO(isdp)) { printf(" 16550A fifo disabled"); } else { com->hasfifo = TRUE; if (COM_ST16650A(isdp)) { com->st16650a = 1; com->tx_fifo_size = 32; printf(" ST16650A"); } else { com->tx_fifo_size = COM_FIFOSIZE(isdp); printf(" 16550A"); } } #ifdef COM_ESP for (espp = likely_esp_ports; *espp != 0; espp++) if (espattach(isdp, com, *espp)) { com->tx_fifo_size = 1024; break; } #endif if (!com->st16650a) { if (!com->tx_fifo_size) com->tx_fifo_size = 16; else printf(" lookalike with %d bytes FIFO", com->tx_fifo_size); } break; } #ifdef COM_ESP if (com->esp) { /* * Set 16550 compatibility mode. * We don't use the ESP_MODE_SCALE bit to increase the * fifo trigger levels because we can't handle large * bursts of input. * XXX flow control should be set in comparam(), not here. */ outb(com->esp_port + ESP_CMD1, ESP_SETMODE); outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); /* Set RTS/CTS flow control. */ outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); /* Set flow-control levels. */ outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP_CMD2, HIBYTE(768)); outb(com->esp_port + ESP_CMD2, LOBYTE(768)); outb(com->esp_port + ESP_CMD2, HIBYTE(512)); outb(com->esp_port + ESP_CMD2, LOBYTE(512)); } #endif /* COM_ESP */ outb(iobase + com_fifo, 0); determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(isdp)) { com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(isdp)) printf(" master"); printf(")"); com->no_irq = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ if (unit == comconsole) printf(", console"); if ( COM_IIR_TXRDYBUG(isdp) ) printf(" with a bogus IIR_TXRDY register"); printf("\n"); s = spltty(); com_addr(unit) = com; splx(s); if (!sio_registered) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &sio_cdevsw, NULL); register_swi(SWI_TTY, siopoll); sio_registered = TRUE; } #ifdef DEVFS com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, unit, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_LOCK_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); #endif com->id_flags = isdp->id_flags; /* Heritate id_flags for later */ return (1); } static int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIOTOT || (com = com_addr(unit)) == NULL) return (ENXIO); if (com->gone) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; tp->t_ififosize = 2 * RS_IBUFSIZE; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ iobase = com->iobase; if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); /* * XXX the delays are for superstitious * historical reasons. It must be less than * the character time at the maximum * supported speed (87 usec at 115200 bps * 8N1). Otherwise we might loop endlessly * if data is streaming in. We used to use * delays of 100. That usually worked * because DELAY(100) used to usually delay * for about 85 usec instead of 100. */ DELAY(50); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(50); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); if (COM_IIR_TXRDYBUG(com)) { outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS | IER_EMSC); } else { outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); } enable_intr(); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); if (com_addr(unit) == NULL) return (ENXIO); --com->wopeners; if (error != 0 || com->gone) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); if (com->gone) { printf("sio%d: gone\n", com->unit); s = spltty(); com_addr(com->unit) = 0; bzero(tp,sizeof *tp); bzero(com,sizeof *com); free(com,M_TTYS); splx(s); } return (0); } static void comhardclose(com) struct com_s *com; { Port_t iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty(); com->poll = FALSE; com->poll_output = FALSE; com->do_timestamp = FALSE; com->do_dcd_timestamp = FALSE; outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); { outb(iobase + com_ier, 0); tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { (void)commctl(com, TIOCM_DTR, DMBIC); if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ outb(iobase + com_fifo, 0); } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; int unit; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } static void siobusycheck(chan) void *chan; { struct com_s *com; int s; com = (struct com_s *)chan; /* * Clear TS_BUSY if low-level output is complete. * spl locking is sufficient because siointr1() does not set CS_BUSY. * If siointr1() clears CS_BUSY after we look at it, then we'll get * called again. Reading the line status port outside of siointr1() * is safe because CS_BUSY is clear so there are no output interrupts * to lose. */ s = spltty(); if (com->state & CS_BUSY) com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) == (LSR_TSRE | LSR_TXRDY)) { com->tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); com->extra_state &= ~CSE_BUSYCHECK; } else timeout(siobusycheck, com, hz / 100); splx(s); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } void siointr(unit) int unit; { #ifndef COM_MULTIPORT COM_LOCK(); siointr1(com_addr(unit)); COM_UNLOCK(); #else /* COM_MULTIPORT */ struct com_s *com; bool_t possibly_more_intrs; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ COM_LOCK(); do { possibly_more_intrs = FALSE; for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); /* * XXX COM_LOCK(); * would it work here, or be counter-productive? */ if (com != NULL && !com->gone && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } /* XXX COM_UNLOCK(); */ } } while (possibly_more_intrs); COM_UNLOCK(); #endif /* COM_MULTIPORT */ } static void siointr1(com) struct com_s *com; { u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; u_char int_ident; u_char int_ctl; u_char int_ctl_new; int_ctl = inb(com->intr_ctl_port); int_ctl_new = int_ctl; while (!com->gone) { line_status = inb(com->line_status_port); /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { /* * Don't store BI if IGNBRK or FE/PE if IGNPAR. * Otherwise, push the work to a higher level * (to handle PARMRK) if we're bypassing. * Otherwise, convert BI/FE and PE+INPCK to 0. * * This makes bypassing work right in the * usual "raw" case (IGNBRK set, and IGNPAR * and INPCK clear). * * Note: BI together with FE/PE means just BI. */ if (line_status & LSR_BI) { #if defined(DDB) && defined(BREAK_TO_DEBUGGER) if (com->unit == comconsole) { breakpoint(); goto cont; } #endif if (com->tp == NULL || com->tp->t_iflag & IGNBRK) goto cont; } else { if (com->tp == NULL || com->tp->t_iflag & IGNPAR) goto cont; } if (com->tp->t_state & TS_CAN_BYPASS_L_RINT && (line_status & (LSR_BI | LSR_FE) || com->tp->t_iflag & INPCK)) recv_data = 0; } ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; schedsofttty(); #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } cont: /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ line_status = inb(com->line_status_port) & 0x7F; } /* modem status change? (always check before doing output) */ modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } /* output queued and everything ready? */ if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { ioptr = com->obufq.l_head; if (com->tx_fifo_size > 1) { u_int ocount; ocount = com->obufq.l_tail - ioptr; if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do outb(com->data_port, *ioptr++); while (--ocount != 0); } else { outb(com->data_port, *ioptr++); ++com->bytes_out; } com->obufq.l_head = ioptr; if (COM_IIR_TXRDYBUG(com)) { int_ctl_new = int_ctl | IER_ETXRDY; } if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ if ( COM_IIR_TXRDYBUG(com) ) { int_ctl_new = int_ctl & ~IER_ETXRDY; } com->state &= ~CS_BUSY; } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; setsofttty(); /* handle at high level ASAP */ } } if ( COM_IIR_TXRDYBUG(com) && (int_ctl != int_ctl_new)) { outb(com->intr_ctl_port, int_ctl_new); } } /* finished? */ #ifndef COM_MULTIPORT if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int sioioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) int oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); if (com->gone) return (ENODEV); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); #ifdef DSI_SOFT_MODEM /* * Download micro-code to Digicom modem. */ case TIOCDSIMICROCODE: { u_long l; u_char *p,*pi; pi = (u_char*)(*(caddr_t*)data); error = copyin(pi,&l,sizeof l); if(error) {return error;}; pi += sizeof l; p = malloc(l,M_TEMP,M_NOWAIT); if(!p) {return ENOBUFS;} error = copyin(pi,p,l); if(error) {free(p,M_TEMP); return error;}; if(error = LoadSoftModem( MINOR_TO_UNIT(mynor),iobase,l,p)) {free(p,M_TEMP); return error;} free(p,M_TEMP); return(0); } #endif /* DSI_SOFT_MODEM */ default: return (ENOTTY); } } tp = com->tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return (error); s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, com); if (error != ENOIOCTL) { splx(s); return (error); } switch (cmd) { case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } static void siopoll() { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < NSIOTOT; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL || com->gone) { /* * Discard any events related to never-opened or * going-away devices. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; enable_intr(); if (!(com->state & CS_BUSY) && !(com->extra_state & CSE_BUSYCHECK)) { timeout(siobusycheck, com, hz / 100); com->extra_state |= CSE_BUSYCHECK; } (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) continue; /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; int divisor; u_char dlbh; u_char dlbl; int error; Port_t iobase; int s; int unit; int txtimeout; /* do historical conversions */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; /* check requested parameters */ divisor = ttspeedtab(t->c_ospeed, comspeedtab); if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); if (divisor == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); cflag = t->c_cflag; switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo && divisor != 0) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. */ com->fifo_image = t->c_ospeed <= 4800 ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH; #ifdef COM_ESP /* * The Hayes ESP card needs the fifo DMA mode bit set * in compatibility mode. If not, it will interrupt * for each character received. */ if (com->esp) com->fifo_image |= FIFO_DMA_MODE; #endif outb(iobase + com_fifo, com->fifo_image); } /* * Some UARTs lock up if the divisor latch registers are selected * while the UART is doing output (they refuse to transmit anything * more until given a hard reset). Fix this by stopping filling * the device buffers and waiting for them to drain. Reading the * line status port outside of siointr1() might lose some receiver * error bits, but that is acceptable here. */ disable_intr(); retry: com->state &= ~CS_TTGO; txtimeout = tp->t_timeout; enable_intr(); while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) { tp->t_state |= TS_SO_OCOMPLETE; error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, "siotx", hz / 100); if ( txtimeout != 0 && (!error || error == EAGAIN) && (txtimeout -= hz / 100) <= 0 ) error = EIO; if (com->gone) error = ENODEV; if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); com->state |= CS_TTGO; enable_intr(); } splx(s); return (error); } } disable_intr(); /* very important while com_data is hidden */ /* * XXX - clearing CS_TTGO is not sufficient to stop further output, * because siopoll() calls comstart() which usually sets it again * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be * sufficient, for similar reasons. */ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) goto retry; if (divisor != 0) { outb(iobase + com_cfcr, cfcr | CFCR_DLAB); /* * Only set the divisor registers if they would change, * since on some 16550 incompatibles (UMC8669F), setting * them while input is arriving them loses sync until * data stops arriving. */ dlbl = divisor & 0xFF; if (inb(iobase + com_dlbl) != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = (u_int) divisor >> 8; if (inb(iobase + com_dlbh) != dlbh) outb(iobase + com_dlbh, dlbh); } outb(iobase + com_cfcr, com->cfcr_image = cfcr); if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) { if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x40); } com->state |= CS_RTS_IFLOW; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x40); } } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x80); } } else { if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x80); } } outb(iobase + com_cfcr, com->cfcr_image); /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); enable_intr(); splx(s); comstart(tp); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); disable_intr(); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; } enable_intr(); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; } enable_intr(); } tp->t_state |= TS_BUSY; } disable_intr(); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ enable_intr(); ttwwakeup(tp); splx(s); } static void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (com->gone) return; disable_intr(); if (rw & FWRITE) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_XMT_RST | com->fifo_image); com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_RCV_RST | com->fifo_image); com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); comstart(tp); } static struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIOTOT) return (NULL); return (&sio_tty[unit]); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { int mcr; int msr; if (how == DMGET) { bits = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) bits |= TIOCM_DTR; if (mcr & MCR_RTS) bits |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= MCR_DTR; if (bits & TIOCM_RTS) mcr |= MCR_RTS; if (com->gone) return(0); disable_intr(); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= mcr); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~mcr); break; } enable_intr(); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN && !com->gone) { someopen = TRUE; if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); if (com != NULL && !com->gone && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < NSIOTOT; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } static void disc_optim(tp, t, com) struct tty *tp; struct termios *t; struct com_s *com; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; com->hotchar = linesw[tp->t_line].l_hotchar; } /* * Following are all routines needed for SIO to act as console */ #include struct siocnstate { u_char dlbl; u_char dlbh; u_char ier; u_char cfcr; u_char mcr; }; static speed_t siocngetspeed __P((Port_t, struct speedtab *)); static void siocnclose __P((struct siocnstate *sp)); static void siocnopen __P((struct siocnstate *sp)); static void siocntxwait __P((void)); static void siocntxwait() { int timo; /* * Wait for any pending transmission to finish. Required to avoid * the UART lockup bug when the speed is changed, and for normal * transmits. */ timo = 100000; while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY) && --timo != 0) ; } /* * Read the serial port specified and try to figure out what speed * it's currently running at. We're assuming the serial port has * been initialized and is basicly idle. This routine is only intended * to be run at system startup. * * If the value read from the serial port doesn't make sense, return 0. */ static speed_t siocngetspeed(iobase, table) Port_t iobase; struct speedtab *table; { int code; u_char dlbh; u_char dlbl; u_char cfcr; cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); dlbl = inb(iobase + com_dlbl); dlbh = inb(iobase + com_dlbh); outb(iobase + com_cfcr, cfcr); code = dlbh << 8 | dlbl; for ( ; table->sp_speed != -1; table++) if (table->sp_code == code) return (table->sp_speed); return 0; /* didn't match anything sane */ } static void siocnopen(sp) struct siocnstate *sp; { int divisor; u_char dlbh; u_char dlbl; Port_t iobase; /* * Save all the device control registers except the fifo register * and set our default ones (cs8 -parenb speed=comdefaultrate). * We can't save the fifo register since it is read-only. */ iobase = siocniobase; sp->ier = inb(iobase + com_ier); outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ siocntxwait(); sp->cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); sp->dlbl = inb(iobase + com_dlbl); sp->dlbh = inb(iobase + com_dlbh); /* * Only set the divisor registers if they would change, since on * some 16550 incompatibles (Startech), setting them clears the * data input register. This also reduces the effects of the * UMC8669F bug. */ divisor = ttspeedtab(comdefaultrate, comspeedtab); dlbl = divisor & 0xFF; if (sp->dlbl != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = (u_int) divisor >> 8; if (sp->dlbh != dlbh) outb(iobase + com_dlbh, dlbh); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp) struct siocnstate *sp; { Port_t iobase; /* * Restore the device control registers. */ siocntxwait(); iobase = siocniobase; outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); if (sp->dlbl != inb(iobase + com_dlbl)) outb(iobase + com_dlbl, sp->dlbl); if (sp->dlbh != inb(iobase + com_dlbh)) outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } void siocnprobe(cp) struct consdev *cp; { speed_t boot_speed; u_char cfcr; struct isa_device *dvp; int s; struct siocnstate sp; /* * Find our first enabled console, if any. If it is a high-level * console device, then initialize it and return successfully. * If it is a low-level console device, then initialize it and * return unsuccessfully. It must be initialized in both cases * for early use by console drivers and debuggers. Initializing * the hardware is not necessary in all cases, since the i/o * routines initialize it on the fly, but it is necessary if * input might arrive while the hardware is switched back to an * uninitialized state. We can't handle multiple console devices * yet because our low-level routines don't take a device arg. * We trust the user to set the console flags properly so that we * don't need to probe. */ cp->cn_pri = CN_DEAD; for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++) if (dvp->id_driver == &siodriver && dvp->id_enabled && COM_CONSOLE(dvp)) { siocniobase = dvp->id_iobase; s = spltty(); if (boothowto & RB_SERIAL) { boot_speed = siocngetspeed(siocniobase, comspeedtab); if (boot_speed) comdefaultrate = boot_speed; } /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siocniobase + com_cfcr); outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); outb(siocniobase + com_dlbl, COMBRD(comdefaultrate) & 0xff); outb(siocniobase + com_dlbh, (u_int) COMBRD(comdefaultrate) >> 8); outb(siocniobase + com_cfcr, cfcr); siocnopen(&sp); splx(s); if (!COM_LLCONSOLE(dvp)) { cp->cn_dev = makedev(CDEV_MAJOR, dvp->id_unit); cp->cn_pri = COM_FORCECONSOLE(dvp) || boothowto & RB_SERIAL ? CN_REMOTE : CN_NORMAL; } break; } } void siocninit(cp) struct consdev *cp; { comconsole = DEV_TO_UNIT(cp->cn_dev); } int siocncheckc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = -1; siocnclose(&sp); splx(s); return (c); } int siocngetc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp); splx(s); return (c); } void siocnputc(dev, c) dev_t dev; int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp); siocntxwait(); outb(siocniobase + com_data, c); siocnclose(&sp); splx(s); } #ifdef DSI_SOFT_MODEM /* * The magic code to download microcode to a "Connection 14.4+Fax" * modem from Digicom Systems Inc. Very magic. */ #define DSI_ERROR(str) { ptr = str; goto error; } static int LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr) { int int_c,int_k; int data_0188, data_0187; /* * First see if it is a DSI SoftModem */ if(!((inb(base_io+7) ^ inb(base_io+7)) & 0x80)) return ENODEV; data_0188 = inb(base_io+4); data_0187 = inb(base_io+3); outb(base_io+3,0x80); outb(base_io+4,0x0C); outb(base_io+0,0x31); outb(base_io+1,0x8C); outb(base_io+7,0x10); outb(base_io+7,0x19); if(0x18 != (inb(base_io+7) & 0x1A)) DSI_ERROR("dsp bus not granted"); if(0x01 != (inb(base_io+7) & 0x01)) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x01 != (inb(base_io+7) & 0x01)) DSI_ERROR("program mem not granted"); } int_c = 0; while(1) { if(int_c >= 7 || size <= 0x1800) break; for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; int_c++; } if(size > 0x1800) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; while(size > 0x1800) { for(int_k = 0 ; int_k < 0xC00; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; } if(size < 0x1800) { for(int_k=0;int_k 0) { if(int_c == 7) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } else { for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } } outb(base_io+7,0x11); outb(base_io+7,3); outb(base_io+4,data_0188 & 0xfb); outb(base_io+3,data_0187); return 0; error: printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",unit,ptr); outb(base_io+7,0x00); \ outb(base_io+3,data_0187); \ outb(base_io+4,data_0188); \ return EIO; } #endif /* DSI_SOFT_MODEM */ /* * support PnP cards if we are using 'em */ #if NPNP > 0 static struct siopnp_ids { u_long vend_id; char *id_str; } siopnp_ids[] = { { 0x5015f435, "MOT1550"}, { 0x8113b04e, "Supra1381"}, { 0x9012b04e, "Supra1290"}, { 0x11007256, "USR0011"}, { 0x30207256, "USR2030"}, { 0 } }; static char *siopnp_probe(u_long csn, u_long vend_id); static void siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); static u_long nsiopnp = NSIO; static struct pnp_device siopnp = { "siopnp", siopnp_probe, siopnp_attach, &nsiopnp, &tty_imask }; DATA_SET (pnpdevice_set, siopnp); static char * siopnp_probe(u_long csn, u_long vend_id) { struct siopnp_ids *ids; char *s = NULL; for(ids = siopnp_ids; ids->vend_id != 0; ids++) { if (vend_id == ids->vend_id) { s = ids->id_str; break; } } if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); if (d.enable == 0 || d.flags & 1) { printf("CSN %d is disabled.\n", csn); return (NULL); } } return (s); } static void siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d; struct isa_device *dvp; if (dev->id_unit >= NSIOTOT) return; if (read_pnp_parms(&d, 0) == 0) { printf("failed to read pnp parms\n"); return; } write_pnp_parms(&d, 0); enable_pnp_card(); dev->id_iobase = d.port[0]; dev->id_irq = (1 << d.irq[0]); dev->id_intr = siointr; dev->id_ri_flags = RI_FAST; dev->id_drq = -1; if (dev->id_driver == NULL) { dev->id_driver = &siodriver; dvp = find_isadev(isa_devtab_tty, &siodriver, 0); if (dvp != NULL) dev->id_id = dvp->id_id; } if ((dev->id_alive = sioprobe(dev)) != 0) sioattach(dev); else printf("sio%d: probe failed\n", dev->id_unit); } #endif diff --git a/sys/dev/syscons/syscons.c b/sys/dev/syscons/syscons.c index 831f56b7d417..d99ed3b06418 100644 --- a/sys/dev/syscons/syscons.c +++ b/sys/dev/syscons/syscons.c @@ -1,5412 +1,5414 @@ /*- * Copyright (c) 1992-1997 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: syscons.c,v 1.274 1998/08/14 06:32:03 sos Exp $ + * $Id: syscons.c,v 1.275 1998/08/18 07:36:47 sos Exp $ */ #include "sc.h" #include "apm.h" #include "opt_ddb.h" #include "opt_devfs.h" #include "opt_syscons.h" #if NSC > 0 #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(MAXCONS) #define MAXCONS 16 #endif #if !defined(SC_MAX_HISTORY_SIZE) #define SC_MAX_HISTORY_SIZE (1000 * MAXCONS) #endif #if !defined(SC_HISTORY_SIZE) #define SC_HISTORY_SIZE (ROW * 4) #endif #if (SC_HISTORY_SIZE * MAXCONS) > SC_MAX_HISTORY_SIZE #undef SC_MAX_HISTORY_SIZE #define SC_MAX_HISTORY_SIZE (SC_HISTORY_SIZE * MAXCONS) #endif #if !defined(SC_MOUSE_CHAR) #define SC_MOUSE_CHAR (0xd0) #endif #define COLD 0 #define WARM 1 #define VESA_MODE(x) ((x) >= M_VESA_BASE) #define MODE_MAP_SIZE (M_VGA_CG320 + 1) #define MODE_PARAM_SIZE 64 #define DEFAULT_BLANKTIME (5*60) /* 5 minutes */ #define MAX_BLANKTIME (7*24*60*60) /* 7 days!? */ /* for backward compatibility */ #define OLD_CONS_MOUSECTL _IOWR('c', 10, old_mouse_info_t) typedef struct old_mouse_data { int x; int y; int buttons; } old_mouse_data_t; typedef struct old_mouse_info { int operation; union { struct old_mouse_data data; struct mouse_mode mode; } u; } old_mouse_info_t; /* XXX use sc_bcopy where video memory is concerned */ extern void generic_bcopy(const void *, void *, size_t); static default_attr user_default = { (FG_LIGHTGREY | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static default_attr kernel_default = { (FG_WHITE | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static scr_stat main_console; static scr_stat *console[MAXCONS]; #ifdef DEVFS static void *sc_devfs_token[MAXCONS]; static void *sc_vga_devfs_token; static void *sc_mouse_devfs_token; static void *sc_console_devfs_token; #endif scr_stat *cur_console; static scr_stat *new_scp, *old_scp; static term_stat kernel_console; static default_attr *current_default; static int flags = 0; static int sc_port = IO_KBD; static KBDC sc_kbdc = NULL; static char init_done = COLD; static u_short sc_buffer[ROW*COL]; static char shutdown_in_progress = FALSE; static char font_loading_in_progress = FALSE; static char switch_in_progress = FALSE; static char write_in_progress = FALSE; static char blink_in_progress = FALSE; static int blinkrate = 0; u_int crtc_addr = MONO_BASE; char crtc_type = KD_MONO; char crtc_vga = FALSE; static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0; static u_char accents = 0; static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0; static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab); static int delayed_next_scr = FALSE; static long scrn_blank_time = 0; /* screen saver timeout value */ int scrn_blanked = 0; /* screen saver active flag */ static long scrn_time_stamp; static int saver_mode = CONS_LKM_SAVER; /* LKM/user saver */ static int run_scrn_saver = FALSE; /* should run the saver? */ static int scrn_idle = FALSE; /* about to run the saver */ u_char scr_map[256]; u_char scr_rmap[256]; char *video_mode_ptr = NULL; static int bios_video_mode; /* video mode # set by BIOS */ int fonts_loaded = 0 #ifdef STD8X16FONT | FONT_16 #endif ; char font_8[256*8]; char font_14[256*14]; #ifdef STD8X16FONT extern #endif unsigned char font_16[256*16]; char palette[256*3]; static char *mode_map[MODE_MAP_SIZE]; static char vgaregs[MODE_PARAM_SIZE]; static char vgaregs2[MODE_PARAM_SIZE]; static int rows_offset = 1; static char *cut_buffer; static int cut_buffer_size; static int mouse_level = 0; /* sysmouse protocol level */ static mousestatus_t mouse_status = { 0, 0, 0, 0, 0, 0 }; static u_short mouse_and_mask[16] = { 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80, 0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000 }; static u_short mouse_or_mask[16] = { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800, 0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000 }; static int extra_history_size = SC_MAX_HISTORY_SIZE - SC_HISTORY_SIZE * MAXCONS; static void none_saver(int blank) { } static void (*current_saver)(int blank) = none_saver; static void (*default_saver)(int blank) = none_saver; int (*sc_user_ioctl)(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) = NULL; static int sticky_splash = FALSE; /* OS specific stuff */ #ifdef not_yet_done #define VIRTUAL_TTY(x) (sccons[x] = ttymalloc(sccons[x])) struct CONSOLE_TTY (sccons[MAXCONS] = ttymalloc(sccons[MAXCONS])) struct MOUSE_TTY (sccons[MAXCONS+1] = ttymalloc(sccons[MAXCONS+1])) struct tty *sccons[MAXCONS+2]; #else #define VIRTUAL_TTY(x) &sccons[x] #define CONSOLE_TTY &sccons[MAXCONS] #define MOUSE_TTY &sccons[MAXCONS+1] static struct tty sccons[MAXCONS+2]; #endif #define SC_MOUSE 128 #define SC_CONSOLE 255 #define MONO_BUF pa_to_va(0xB0000) #define CGA_BUF pa_to_va(0xB8000) u_short *Crtat; static const int nsccons = MAXCONS+2; #define WRAPHIST(scp, pointer, offset)\ ((scp->history) + ((((pointer) - (scp->history)) + (scp->history_size)\ + (offset)) % (scp->history_size))) #define ISSIGVALID(sig) ((sig) > 0 && (sig) < NSIG) /* this should really be in `rtc.h' */ #define RTC_EQUIPMENT 0x14 /* prototypes */ static int scattach(struct isa_device *dev); static int scparam(struct tty *tp, struct termios *t); static int scprobe(struct isa_device *dev); static int scvidprobe(int unit, int flags); static int sckbdprobe(int unit, int flags); static void scstart(struct tty *tp); static void scmousestart(struct tty *tp); static void scinit(void); static void scshutdown(int howto, void *arg); static void map_mode_table(char *map[], char *table, int max); static int map_mode_num(int mode); static char *get_mode_param(scr_stat *scp, int mode); static u_int scgetc(u_int flags); #define SCGETC_CN 1 #define SCGETC_NONBLOCK 2 static void sccnupdate(scr_stat *scp); static scr_stat *get_scr_stat(dev_t dev); static scr_stat *alloc_scp(void); static void init_scp(scr_stat *scp); static void sc_bcopy(scr_stat *scp, u_short *p, int from, int to, int mark); static int get_scr_num(void); static timeout_t scrn_timer; static void scrn_update(scr_stat *scp, int show_cursor); static void stop_scrn_saver(void (*saver)(int)); static int wait_scrn_saver_stop(void); static void clear_screen(scr_stat *scp); static int switch_scr(scr_stat *scp, u_int next_scr); static void exchange_scr(void); static void move_crsr(scr_stat *scp, int x, int y); static void scan_esc(scr_stat *scp, u_char c); static void draw_cursor_image(scr_stat *scp); static void remove_cursor_image(scr_stat *scp); static void ansi_put(scr_stat *scp, u_char *buf, int len); static u_char *get_fstr(u_int c, u_int *len); static void history_to_screen(scr_stat *scp); static int history_up_line(scr_stat *scp); static int history_down_line(scr_stat *scp); static int mask2attr(struct term_stat *term); static void set_keyboard(int command, int data); static void update_leds(int which); static void set_vgaregs(char *modetable); static void read_vgaregs(char *buf); #define COMP_IDENTICAL 0 #define COMP_SIMILAR 1 #define COMP_DIFFERENT 2 static int comp_vgaregs(u_char *buf1, u_char *buf2); static void dump_vgaregs(u_char *buf); #define PARAM_BUFSIZE 6 static void set_font_mode(u_char *buf); static void set_normal_mode(u_char *buf); static void set_destructive_cursor(scr_stat *scp); static void set_mouse_pos(scr_stat *scp); static int skip_spc_right(scr_stat *scp, u_short *p); static int skip_spc_left(scr_stat *scp, u_short *p); static void mouse_cut(scr_stat *scp); static void mouse_cut_start(scr_stat *scp); static void mouse_cut_end(scr_stat *scp); static void mouse_cut_word(scr_stat *scp); static void mouse_cut_line(scr_stat *scp); static void mouse_cut_extend(scr_stat *scp); static void mouse_paste(scr_stat *scp); static void draw_mouse_image(scr_stat *scp); static void remove_mouse_image(scr_stat *scp); static void draw_cutmarking(scr_stat *scp); static void remove_cutmarking(scr_stat *scp); static void save_palette(void); static void do_bell(scr_stat *scp, int pitch, int duration); static timeout_t blink_screen; #ifdef SC_SPLASH_SCREEN static void scsplash_init(void); static void scsplash(int show); #define scsplash_stick(stick) (sticky_splash = (stick)) #else #define scsplash_stick(stick) #endif struct isa_driver scdriver = { scprobe, scattach, "sc", 1 }; static d_open_t scopen; static d_close_t scclose; static d_read_t scread; static d_write_t scwrite; static d_ioctl_t scioctl; static d_devtotty_t scdevtotty; static d_mmap_t scmmap; -#define CDEV_MAJOR 12 -static struct cdevsw scdevsw = { +#define CDEV_MAJOR 12 +static struct cdevsw sc_cdevsw = { scopen, scclose, scread, scwrite, scioctl, nullstop, noreset, scdevtotty, - ttpoll, scmmap, nostrategy, "sc", NULL, -1 }; + ttpoll, scmmap, nostrategy, "sc", + NULL, -1, nodump, nopsize, + D_TTY, +}; /* * These functions need to be before calls to them so they can be inlined. */ static void draw_cursor_image(scr_stat *scp) { u_short cursor_image, *ptr = Crtat + (scp->cursor_pos - scp->scr_buf); u_short prev_image; if (VESA_MODE(scp->mode)) { sc_bcopy(scp, scp->scr_buf, scp->cursor_pos - scp->scr_buf, scp->cursor_pos - scp->scr_buf, 1); return; } /* do we have a destructive cursor ? */ if (flags & CHAR_CURSOR) { prev_image = scp->cursor_saveunder; cursor_image = *ptr & 0x00ff; if (cursor_image == DEAD_CHAR) cursor_image = prev_image & 0x00ff; cursor_image |= *(scp->cursor_pos) & 0xff00; scp->cursor_saveunder = cursor_image; /* update the cursor bitmap if the char under the cursor has changed */ if (prev_image != cursor_image) set_destructive_cursor(scp); /* modify cursor_image */ if (!(flags & BLINK_CURSOR)||((flags & BLINK_CURSOR)&&(blinkrate & 4))){ /* * When the mouse pointer is at the same position as the cursor, * the cursor bitmap needs to be updated even if the char under * the cursor hasn't changed, because the mouse pionter may * have moved by a few dots within the cursor cel. */ if ((prev_image == cursor_image) && (cursor_image != *(scp->cursor_pos))) set_destructive_cursor(scp); cursor_image &= 0xff00; cursor_image |= DEAD_CHAR; } } else { cursor_image = (*(ptr) & 0x00ff) | *(scp->cursor_pos) & 0xff00; scp->cursor_saveunder = cursor_image; if (!(flags & BLINK_CURSOR)||((flags & BLINK_CURSOR)&&(blinkrate & 4))){ if ((cursor_image & 0x7000) == 0x7000) { cursor_image &= 0x8fff; if(!(cursor_image & 0x0700)) cursor_image |= 0x0700; } else { cursor_image |= 0x7000; if ((cursor_image & 0x0700) == 0x0700) cursor_image &= 0xf0ff; } } } *ptr = cursor_image; } static void remove_cursor_image(scr_stat *scp) { if (VESA_MODE(scp->mode)) sc_bcopy(scp, scp->scr_buf, scp->cursor_oldpos - scp->scr_buf, scp->cursor_oldpos - scp->scr_buf, 0); else *(Crtat + (scp->cursor_oldpos - scp->scr_buf)) = scp->cursor_saveunder; } static void move_crsr(scr_stat *scp, int x, int y) { if (x < 0) x = 0; if (y < 0) y = 0; if (x >= scp->xsize) x = scp->xsize-1; if (y >= scp->ysize) y = scp->ysize-1; scp->xpos = x; scp->ypos = y; scp->cursor_pos = scp->scr_buf + scp->ypos * scp->xsize + scp->xpos; } static int scprobe(struct isa_device *dev) { if (!scvidprobe(dev->id_unit, dev->id_flags)) { if (bootverbose) printf("sc%d: no video adapter is found.\n", dev->id_unit); return (0); } sc_port = dev->id_iobase; if (sckbdprobe(dev->id_unit, dev->id_flags)) return (IO_KBDSIZE); else return ((dev->id_flags & DETECT_KBD) ? 0 : IO_KBDSIZE); } /* probe video adapters, return TRUE if found */ static int scvidprobe(int unit, int flags) { /* * XXX don't try to `printf' anything here, the console may not have * been configured yet. */ u_short volatile *cp; u_short was; u_int pa; u_int segoff; /* do this test only once */ if (init_done != COLD) return (Crtat != 0); /* * Finish defaulting crtc variables for a mono screen. Crtat is a * bogus common variable so that it can be shared with pcvt, so it * can't be statically initialized. XXX. */ Crtat = (u_short *)MONO_BUF; crtc_type = KD_MONO; /* If CGA memory seems to work, switch to color. */ cp = (u_short *)CGA_BUF; was = *cp; *cp = (u_short) 0xA55A; bios_video_mode = *(u_char *)pa_to_va(0x449); if (bootinfo.bi_vesa == 0x102) { bios_video_mode = bootinfo.bi_vesa; Crtat = (u_short *)pa_to_va(0xA0000); crtc_addr = COLOR_BASE; crtc_type = KD_VGA; bzero(Crtat, 800*600/8); } else if (*cp == 0xA55A) { Crtat = (u_short *)CGA_BUF; crtc_addr = COLOR_BASE; crtc_type = KD_CGA; } else { cp = Crtat; was = *cp; *cp = (u_short) 0xA55A; if (*cp != 0xA55A) { /* no screen at all, bail out */ Crtat = 0; return FALSE; } } *cp = was; if (!VESA_MODE(bios_video_mode)) { /* * Check rtc and BIOS date area. * XXX: don't use BIOSDATA_EQUIPMENT, it is not a dead copy * of RTC_EQUIPMENT. The bit 4 and 5 of the ETC_EQUIPMENT are * zeros for EGA and VGA. However, the EGA/VGA BIOS will set * these bits in BIOSDATA_EQUIPMENT according to the monitor * type detected. */ switch ((rtcin(RTC_EQUIPMENT) >> 4) & 3) { /* bit 4 and 5 */ case 0: /* EGA/VGA, or nothing */ crtc_type = KD_EGA; /* the color adapter may be in the 40x25 mode... XXX */ break; case 1: /* CGA 40x25 */ /* switch to the 80x25 mode? XXX */ /* FALL THROUGH */ case 2: /* CGA 80x25 */ /* `crtc_type' has already been set... */ /* crtc_type = KD_CGA; */ break; case 3: /* MDA */ /* `crtc_type' has already been set... */ /* crtc_type = KD_MONO; */ break; } /* is this a VGA or higher ? */ outb(crtc_addr, 7); if (inb(crtc_addr) == 7) { crtc_type = KD_VGA; crtc_vga = TRUE; read_vgaregs(vgaregs); /* Get the BIOS video mode pointer */ segoff = *(u_int *)pa_to_va(0x4a8); pa = ((segoff & 0xffff0000) >> 12) + (segoff & 0xffff); if (ISMAPPED(pa, sizeof(u_int))) { segoff = *(u_int *)pa_to_va(pa); pa = ((segoff & 0xffff0000) >> 12) + (segoff & 0xffff); if (ISMAPPED(pa, MODE_PARAM_SIZE)) video_mode_ptr = (char *)pa_to_va(pa); } } } return TRUE; } /* probe the keyboard, return TRUE if found */ static int sckbdprobe(int unit, int flags) { int codeset; int c = -1; int m; int res, id; sc_kbdc = kbdc_open(sc_port); if (!kbdc_lock(sc_kbdc, TRUE)) { /* driver error? */ printf("sc%d: unable to lock the controller.\n", unit); return ((flags & DETECT_KBD) ? FALSE : TRUE); } /* discard anything left after UserConfig */ empty_both_buffers(sc_kbdc, 10); /* save the current keyboard controller command byte */ m = kbdc_get_device_mask(sc_kbdc) & ~KBD_KBD_CONTROL_BITS; c = get_controller_command_byte(sc_kbdc); if (c == -1) { /* CONTROLLER ERROR */ printf("sc%d: unable to get the current command byte value.\n", unit); goto fail; } if (bootverbose) printf("sc%d: the current keyboard controller command byte %04x\n", unit, c); #if 0 /* override the keyboard lock switch */ c |= KBD_OVERRIDE_KBD_LOCK; #endif /* * The keyboard may have been screwed up by the boot block. * We may just be able to recover from error by testing the controller * and the keyboard port. The controller command byte needs to be saved * before this recovery operation, as some controllers seem to set * the command byte to particular values. */ test_controller(sc_kbdc); test_kbd_port(sc_kbdc); /* enable the keyboard port, but disable the keyboard intr. */ if (!set_controller_command_byte(sc_kbdc, KBD_KBD_CONTROL_BITS, KBD_ENABLE_KBD_PORT | KBD_DISABLE_KBD_INT)) { /* CONTROLLER ERROR * there is very little we can do... */ printf("sc%d: unable to set the command byte.\n", unit); goto fail; } /* * Check if we have an XT keyboard before we attempt to reset it. * The procedure assumes that the keyboard and the controller have * been set up properly by BIOS and have not been messed up * during the boot process. */ codeset = -1; if (flags & XT_KEYBD) /* the user says there is a XT keyboard */ codeset = 1; #ifdef DETECT_XT_KEYBOARD else if ((c & KBD_TRANSLATION) == 0) { /* SET_SCANCODE_SET is not always supported; ignore error */ if (send_kbd_command_and_data(sc_kbdc, KBDC_SET_SCANCODE_SET, 0) == KBD_ACK) codeset = read_kbd_data(sc_kbdc); } if (bootverbose) printf("sc%d: keyboard scancode set %d\n", unit, codeset); #endif /* DETECT_XT_KEYBOARD */ if (flags & KBD_NORESET) { write_kbd_command(sc_kbdc, KBDC_ECHO); if (read_kbd_data(sc_kbdc) != KBD_ECHO) { empty_both_buffers(sc_kbdc, 10); test_controller(sc_kbdc); test_kbd_port(sc_kbdc); if (bootverbose) printf("sc%d: failed to get response from the keyboard.\n", unit); goto fail; } } else { /* reset keyboard hardware */ if (!reset_kbd(sc_kbdc)) { /* KEYBOARD ERROR * Keyboard reset may fail either because the keyboard doen't * exist, or because the keyboard doesn't pass the self-test, * or the keyboard controller on the motherboard and the keyboard * somehow fail to shake hands. It is just possible, particularly * in the last case, that the keyoard controller may be left * in a hung state. test_controller() and test_kbd_port() appear * to bring the keyboard controller back (I don't know why and * how, though.) */ empty_both_buffers(sc_kbdc, 10); test_controller(sc_kbdc); test_kbd_port(sc_kbdc); /* We could disable the keyboard port and interrupt... but, * the keyboard may still exist (see above). */ if (bootverbose) printf("sc%d: failed to reset the keyboard.\n", unit); goto fail; } } /* * Allow us to set the XT_KEYBD flag in UserConfig so that keyboards * such as those on the IBM ThinkPad laptop computers can be used * with the standard console driver. */ if (codeset == 1) { if (send_kbd_command_and_data( sc_kbdc, KBDC_SET_SCANCODE_SET, codeset) == KBD_ACK) { /* XT kbd doesn't need scan code translation */ c &= ~KBD_TRANSLATION; } else { /* KEYBOARD ERROR * The XT kbd isn't usable unless the proper scan code set * is selected. */ printf("sc%d: unable to set the XT keyboard mode.\n", unit); goto fail; } } /* enable the keyboard port and intr. */ if (!set_controller_command_byte(sc_kbdc, KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK, (c & (KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK)) | KBD_ENABLE_KBD_PORT | KBD_ENABLE_KBD_INT)) { /* CONTROLLER ERROR * This is serious; we are left with the disabled keyboard intr. */ printf("sc%d: unable to enable the keyboard port and intr.\n", unit); goto fail; } /* Get the ID of the keyboard, if any */ empty_kbd_buffer(sc_kbdc, 5); res = send_kbd_command(sc_kbdc, KBDC_SEND_DEV_ID); if (res == KBD_ACK) { /* 10ms delay */ DELAY(10000); id = (read_kbd_data(sc_kbdc) << 8) | read_kbd_data(sc_kbdc); if (bootverbose) printf("sc%d: keyboard device ID: %04x\n", unit, id); } kbdc_set_device_mask(sc_kbdc, m | KBD_KBD_CONTROL_BITS), kbdc_lock(sc_kbdc, FALSE); return TRUE; fail: if (c != -1) /* try to restore the command byte as before, if possible */ set_controller_command_byte(sc_kbdc, 0xff, c); kbdc_set_device_mask(sc_kbdc, (flags & DETECT_KBD) ? m : m | KBD_KBD_CONTROL_BITS); kbdc_lock(sc_kbdc, FALSE); return FALSE; } #if NAPM > 0 static int scresume(void *dummy) { shfts = ctls = alts = agrs = metas = accents = 0; return 0; } #endif static int scattach(struct isa_device *dev) { scr_stat *scp; dev_t cdev = makedev(CDEV_MAJOR, 0); char *p; #ifdef DEVFS int vc; #endif scinit(); flags = dev->id_flags; if (crtc_type != KD_VGA || VESA_MODE(bios_video_mode)) flags &= ~CHAR_CURSOR; scp = console[0]; if (crtc_type == KD_VGA) { cut_buffer_size = scp->xsize * scp->ysize + 1; cut_buffer = (char *)malloc(cut_buffer_size, M_DEVBUF, M_NOWAIT); if (cut_buffer != NULL) cut_buffer[0] = '\0'; } scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); /* copy temporary buffer to final buffer */ bcopy(sc_buffer, scp->scr_buf, scp->xsize * scp->ysize * sizeof(u_short)); scp->cursor_pos = scp->cursor_oldpos = scp->scr_buf + scp->xpos + scp->ypos * scp->xsize; scp->mouse_pos = scp->mouse_oldpos = scp->scr_buf + ((scp->mouse_ypos/scp->font_size)*scp->xsize + scp->mouse_xpos/8); /* initialize history buffer & pointers */ scp->history_head = scp->history_pos = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); if (scp->history_head != NULL) bzero(scp->history_head, scp->history_size*sizeof(u_short)); scp->history = scp->history_head; /* initialize cursor stuff */ if (!(scp->status & UNKNOWN_MODE)) draw_cursor_image(scp); /* get screen update going */ scrn_timer((void *)TRUE); update_leds(scp->status); if ((crtc_type == KD_VGA) && bootverbose) { printf("sc%d: BIOS video mode:%d\n", dev->id_unit, bios_video_mode); printf("sc%d: VGA registers upon power-up\n", dev->id_unit); dump_vgaregs(vgaregs); printf("sc%d: video mode:%d\n", dev->id_unit, scp->mode); printf("sc%d: VGA registers in BIOS for mode:%d\n", dev->id_unit, scp->mode); dump_vgaregs(vgaregs2); p = get_mode_param(scp, scp->mode); if (p != NULL) { printf("sc%d: VGA registers to be used for mode:%d\n", dev->id_unit, scp->mode); dump_vgaregs(p); } printf("sc%d: rows_offset:%d\n", dev->id_unit, rows_offset); } if ((crtc_type == KD_VGA) && !VESA_MODE(bios_video_mode) && (video_mode_ptr == NULL)) printf("sc%d: WARNING: video mode switching is only partially supported\n", dev->id_unit); printf("sc%d: ", dev->id_unit); switch(crtc_type) { case KD_VGA: if (VESA_MODE(bios_video_mode)) printf("Graphics display (VESA mode = 0x%x)", bios_video_mode); else if (crtc_addr == MONO_BASE) printf("VGA mono"); else printf("VGA color"); break; case KD_EGA: if (crtc_addr == MONO_BASE) printf("EGA mono"); else printf("EGA color"); break; case KD_CGA: printf("CGA"); break; case KD_MONO: case KD_HERCULES: default: printf("MDA/hercules"); break; } printf(" <%d virtual consoles, flags=0x%x>\n", MAXCONS, flags); #if NAPM > 0 scp->r_hook.ah_fun = scresume; scp->r_hook.ah_arg = NULL; scp->r_hook.ah_name = "system keyboard"; scp->r_hook.ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME , &scp->r_hook); #endif at_shutdown(scshutdown, NULL, SHUTDOWN_PRE_SYNC); - cdevsw_add(&cdev, &scdevsw, NULL); + cdevsw_add(&cdev, &sc_cdevsw, NULL); #ifdef DEVFS for (vc = 0; vc < MAXCONS; vc++) - sc_devfs_token[vc] = devfs_add_devswf(&scdevsw, vc, DV_CHR, + sc_devfs_token[vc] = devfs_add_devswf(&sc_cdevsw, vc, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyv%r", vc); sc_vga_devfs_token = devfs_link(sc_devfs_token[0], "vga"); - sc_mouse_devfs_token = devfs_add_devswf(&scdevsw, SC_MOUSE, DV_CHR, + sc_mouse_devfs_token = devfs_add_devswf(&sc_cdevsw, SC_MOUSE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "sysmouse"); - sc_console_devfs_token = devfs_add_devswf(&scdevsw, SC_CONSOLE, DV_CHR, + sc_console_devfs_token = devfs_add_devswf(&sc_cdevsw, SC_CONSOLE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "consolectl"); #endif return 0; } struct tty *scdevtotty(dev_t dev) { int unit = minor(dev); if (init_done == COLD) return(NULL); if (unit == SC_CONSOLE) return CONSOLE_TTY; if (unit == SC_MOUSE) return MOUSE_TTY; if (unit >= MAXCONS || unit < 0) return(NULL); return VIRTUAL_TTY(unit); } int scopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); tp->t_oproc = (minor(dev) == SC_MOUSE) ? scmousestart : scstart; tp->t_param = scparam; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { ttychars(tp); /* Use the current setting of the <-- key as default VERASE. */ /* If the Delete key is preferable, an stty is necessary */ tp->t_cc[VERASE] = key_map.key[0x0e].map[0]; tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; scparam(tp, &tp->t_termios); - ttsetwater(tp); (*linesw[tp->t_line].l_modem)(tp, 1); if (minor(dev) == SC_MOUSE) mouse_level = 0; /* XXX */ } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) return(EBUSY); if (minor(dev) < MAXCONS && !console[minor(dev)]) { console[minor(dev)] = alloc_scp(); } if (minor(dev)t_winsize.ws_col && !tp->t_winsize.ws_row) { tp->t_winsize.ws_col = console[minor(dev)]->xsize; tp->t_winsize.ws_row = console[minor(dev)]->ysize; } return ((*linesw[tp->t_line].l_open)(dev, tp)); } int scclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); struct scr_stat *scp; if (!tp) return(ENXIO); if (minor(dev) < MAXCONS) { scp = get_scr_stat(tp->t_dev); if (scp->status & SWITCH_WAIT_ACQ) wakeup((caddr_t)&scp->smode); #if not_yet_done if (scp == &main_console) { scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; } else { free(scp->scr_buf, M_DEVBUF); if (scp->history != NULL) { free(scp->history, M_DEVBUF); if (scp->history_size / scp->xsize > imax(SC_HISTORY_SIZE, scp->ysize)) extra_history_size += scp->history_size / scp->xsize - imax(SC_HISTORY_SIZE, scp->ysize); } free(scp, M_DEVBUF); console[minor(dev)] = NULL; } #else scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; #endif } spltty(); (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); spl0(); return(0); } int scread(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int scwrite(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_write)(tp, uio, flag)); } void scintr(int unit) { static struct tty *cur_tty; int c, len; u_char *cp; /* * Loop while there is still input to get from the keyboard. * I don't think this is nessesary, and it doesn't fix * the Xaccel-2.1 keyboard hang, but it can't hurt. XXX */ while ((c = scgetc(SCGETC_NONBLOCK)) != NOKEY) { cur_tty = VIRTUAL_TTY(get_scr_num()); if (!(cur_tty->t_state & TS_ISOPEN)) if (!((cur_tty = CONSOLE_TTY)->t_state & TS_ISOPEN)) continue; switch (c & 0xff00) { case 0x0000: /* normal key */ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case FKEY: /* function key, return string */ if (cp = get_fstr((u_int)c, (u_int *)&len)) { while (len-- > 0) (*linesw[cur_tty->t_line].l_rint)(*cp++ & 0xFF, cur_tty); } break; case MKEY: /* meta is active, prepend ESC */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)('[', cur_tty); (*linesw[cur_tty->t_line].l_rint)('Z', cur_tty); break; } } if (cur_console->status & MOUSE_ENABLED) { cur_console->status &= ~MOUSE_VISIBLE; remove_mouse_image(cur_console); } } static int scparam(struct tty *tp, struct termios *t) { tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; return 0; } int scioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { int error; u_int i; struct tty *tp; scr_stat *scp; u_short *usp; char *mp; int s; tp = scdevtotty(dev); if (!tp) return ENXIO; scp = get_scr_stat(tp->t_dev); /* If there is a user_ioctl function call that first */ if (sc_user_ioctl) { if (error = (*sc_user_ioctl)(dev, cmd, data, flag, p)) return error; } switch (cmd) { /* process console hardware related ioctl's */ case GIO_ATTR: /* get current attributes */ *(int*)data = (scp->term.cur_attr >> 8) & 0xFF; return 0; case GIO_COLOR: /* is this a color console ? */ if (crtc_addr == COLOR_BASE) *(int*)data = 1; else *(int*)data = 0; return 0; case CONS_CURRENT: /* get current adapter type */ *(int *)data = crtc_type; return 0; case CONS_GET: /* get current video mode */ *(int*)data = scp->mode; return 0; case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ if (*(int *)data < 0 || *(int *)data > MAX_BLANKTIME) return EINVAL; s = spltty(); scrn_blank_time = *(int *)data; run_scrn_saver = (scrn_blank_time != 0); splx(s); return 0; case CONS_CURSORTYPE: /* set cursor type blink/noblink */ if ((*(int*)data) & 0x01) flags |= BLINK_CURSOR; else flags &= ~BLINK_CURSOR; if ((*(int*)data) & 0x02) { if (crtc_type != KD_VGA || VESA_MODE(bios_video_mode)) return ENXIO; flags |= CHAR_CURSOR; } else flags &= ~CHAR_CURSOR; /* * The cursor shape is global property; all virtual consoles * are affected. Update the cursor in the current console... */ if (!(cur_console->status & UNKNOWN_MODE)) { remove_cursor_image(cur_console); if (flags & CHAR_CURSOR) set_destructive_cursor(cur_console); draw_cursor_image(cur_console); } return 0; case CONS_BELLTYPE: /* set bell type sound/visual */ if ((*(int *)data) & 0x01) flags |= VISUAL_BELL; else flags &= ~VISUAL_BELL; if ((*(int *)data) & 0x02) flags |= QUIET_BELL; else flags &= ~QUIET_BELL; return 0; case CONS_HISTORY: /* set history size */ if (*(int *)data > 0) { int lines; /* buffer size to allocate */ int lines0; /* current buffer size */ lines = imax(*(int *)data, scp->ysize); lines0 = (scp->history != NULL) ? scp->history_size / scp->xsize : scp->ysize; /* * syscons unconditionally allocates buffers upto SC_HISTORY_SIZE * lines or scp->ysize lines, whichever is larger. A value * greater than that is allowed, subject to extra_history_size. */ if (lines > imax(lines0, SC_HISTORY_SIZE) + extra_history_size) return EINVAL; if (cur_console->status & BUFFER_SAVED) return EBUSY; usp = scp->history; scp->history = NULL; if (usp != NULL) free(usp, M_DEVBUF); scp->history_size = lines * scp->xsize; /* * extra_history_size += * (lines0 > imax(SC_HISTORY_SIZE, scp->ysize)) ? * lines0 - imax(SC_HISTORY_SIZE, scp->ysize)) : 0; * extra_history_size -= * (lines > imax(SC_HISTORY_SIZE, scp->ysize)) ? * lines - imax(SC_HISTORY_SIZE, scp->ysize)) : 0; * lines0 >= ysize && lines >= ysize... Hey, the above can be * reduced to the following... */ extra_history_size += imax(lines0, SC_HISTORY_SIZE) - imax(lines, SC_HISTORY_SIZE); usp = (u_short *)malloc(scp->history_size * sizeof(u_short), M_DEVBUF, M_WAITOK); bzero(usp, scp->history_size * sizeof(u_short)); scp->history_head = scp->history_pos = usp; scp->history = usp; return 0; } else return EINVAL; case CONS_MOUSECTL: /* control mouse arrow */ case OLD_CONS_MOUSECTL: { /* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */ static butmap[8] = { MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON1UP, 0, }; mouse_info_t *mouse = (mouse_info_t*)data; mouse_info_t buf; if (crtc_type != KD_VGA || VESA_MODE(bios_video_mode)) return ENODEV; if (cmd == OLD_CONS_MOUSECTL) { static unsigned char swapb[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; old_mouse_info_t *old_mouse = (old_mouse_info_t *)data; mouse = &buf; mouse->operation = old_mouse->operation; switch (mouse->operation) { case MOUSE_MODE: mouse->u.mode = old_mouse->u.mode; break; case MOUSE_SHOW: case MOUSE_HIDE: break; case MOUSE_MOVEABS: case MOUSE_MOVEREL: case MOUSE_ACTION: mouse->u.data.x = old_mouse->u.data.x; mouse->u.data.y = old_mouse->u.data.y; mouse->u.data.z = 0; mouse->u.data.buttons = swapb[old_mouse->u.data.buttons & 0x7]; break; case MOUSE_GETINFO: old_mouse->u.data.x = scp->mouse_xpos; old_mouse->u.data.y = scp->mouse_ypos; old_mouse->u.data.buttons = swapb[scp->mouse_buttons & 0x7]; break; default: return EINVAL; } } switch (mouse->operation) { case MOUSE_MODE: if (ISSIGVALID(mouse->u.mode.signal)) { scp->mouse_signal = mouse->u.mode.signal; scp->mouse_proc = p; scp->mouse_pid = p->p_pid; } else { scp->mouse_signal = 0; scp->mouse_proc = NULL; scp->mouse_pid = 0; } return 0; case MOUSE_SHOW: if (!(scp->status & MOUSE_ENABLED)) { scp->status |= (MOUSE_ENABLED | MOUSE_VISIBLE); scp->mouse_oldpos = scp->mouse_pos; mark_all(scp); return 0; } else return EINVAL; break; case MOUSE_HIDE: if (scp->status & MOUSE_ENABLED) { scp->status &= ~(MOUSE_ENABLED | MOUSE_VISIBLE); mark_all(scp); return 0; } else return EINVAL; break; case MOUSE_MOVEABS: scp->mouse_xpos = mouse->u.data.x; scp->mouse_ypos = mouse->u.data.y; set_mouse_pos(scp); break; case MOUSE_MOVEREL: scp->mouse_xpos += mouse->u.data.x; scp->mouse_ypos += mouse->u.data.y; set_mouse_pos(scp); break; case MOUSE_GETINFO: mouse->u.data.x = scp->mouse_xpos; mouse->u.data.y = scp->mouse_ypos; mouse->u.data.z = 0; mouse->u.data.buttons = scp->mouse_buttons; return 0; case MOUSE_ACTION: case MOUSE_MOTION_EVENT: /* this should maybe only be settable from /dev/consolectl SOS */ /* send out mouse event on /dev/sysmouse */ mouse_status.dx += mouse->u.data.x; mouse_status.dy += mouse->u.data.y; mouse_status.dz += mouse->u.data.z; if (mouse->operation == MOUSE_ACTION) mouse_status.button = mouse->u.data.buttons; mouse_status.flags |= ((mouse->u.data.x || mouse->u.data.y || mouse->u.data.z) ? MOUSE_POSCHANGED : 0) | (mouse_status.obutton ^ mouse_status.button); if (mouse_status.flags == 0) return 0; if (cur_console->status & MOUSE_ENABLED) cur_console->status |= MOUSE_VISIBLE; if ((MOUSE_TTY)->t_state & TS_ISOPEN) { u_char buf[MOUSE_SYS_PACKETSIZE]; int j; /* the first five bytes are compatible with MouseSystems' */ buf[0] = MOUSE_MSC_SYNC | butmap[mouse_status.button & MOUSE_STDBUTTONS]; j = imax(imin(mouse->u.data.x, 255), -256); buf[1] = j >> 1; buf[3] = j - buf[1]; j = -imax(imin(mouse->u.data.y, 255), -256); buf[2] = j >> 1; buf[4] = j - buf[2]; for (j = 0; j < MOUSE_MSC_PACKETSIZE; j++) (*linesw[(MOUSE_TTY)->t_line].l_rint)(buf[j],MOUSE_TTY); if (mouse_level >= 1) { /* extended part */ j = imax(imin(mouse->u.data.z, 127), -128); buf[5] = (j >> 1) & 0x7f; buf[6] = (j - (j >> 1)) & 0x7f; /* buttons 4-10 */ buf[7] = (~mouse_status.button >> 3) & 0x7f; for (j = MOUSE_MSC_PACKETSIZE; j < MOUSE_SYS_PACKETSIZE; j++) (*linesw[(MOUSE_TTY)->t_line].l_rint)(buf[j],MOUSE_TTY); } } if (cur_console->mouse_signal) { cur_console->mouse_buttons = mouse->u.data.buttons; /* has controlling process died? */ if (cur_console->mouse_proc && (cur_console->mouse_proc != pfind(cur_console->mouse_pid))){ cur_console->mouse_signal = 0; cur_console->mouse_proc = NULL; cur_console->mouse_pid = 0; } else psignal(cur_console->mouse_proc, cur_console->mouse_signal); } else if (mouse->operation == MOUSE_ACTION && cut_buffer != NULL) { /* process button presses */ if ((cur_console->mouse_buttons ^ mouse->u.data.buttons) && !(cur_console->status & UNKNOWN_MODE)) { cur_console->mouse_buttons = mouse->u.data.buttons; if (cur_console->mouse_buttons & MOUSE_BUTTON1DOWN) mouse_cut_start(cur_console); else mouse_cut_end(cur_console); if (cur_console->mouse_buttons & MOUSE_BUTTON2DOWN || cur_console->mouse_buttons & MOUSE_BUTTON3DOWN) mouse_paste(cur_console); } } if (mouse->u.data.x != 0 || mouse->u.data.y != 0) { cur_console->mouse_xpos += mouse->u.data.x; cur_console->mouse_ypos += mouse->u.data.y; set_mouse_pos(cur_console); } break; case MOUSE_BUTTON_EVENT: if ((mouse->u.event.id & MOUSE_BUTTONS) == 0) return EINVAL; if (mouse->u.event.value < 0) return EINVAL; if (mouse->u.event.value > 0) { cur_console->mouse_buttons |= mouse->u.event.id; mouse_status.button |= mouse->u.event.id; } else { cur_console->mouse_buttons &= ~mouse->u.event.id; mouse_status.button &= ~mouse->u.event.id; } mouse_status.flags |= mouse_status.obutton ^ mouse_status.button; if (mouse_status.flags == 0) return 0; if (cur_console->status & MOUSE_ENABLED) cur_console->status |= MOUSE_VISIBLE; if ((MOUSE_TTY)->t_state & TS_ISOPEN) { u_char buf[8]; int i; buf[0] = MOUSE_MSC_SYNC | butmap[mouse_status.button & MOUSE_STDBUTTONS]; buf[7] = (~mouse_status.button >> 3) & 0x7f; buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0; for (i = 0; i < ((mouse_level >= 1) ? MOUSE_SYS_PACKETSIZE : MOUSE_MSC_PACKETSIZE); i++) (*linesw[(MOUSE_TTY)->t_line].l_rint)(buf[i],MOUSE_TTY); } if (cur_console->mouse_signal) { if (cur_console->mouse_proc && (cur_console->mouse_proc != pfind(cur_console->mouse_pid))){ cur_console->mouse_signal = 0; cur_console->mouse_proc = NULL; cur_console->mouse_pid = 0; } else psignal(cur_console->mouse_proc, cur_console->mouse_signal); break; } if ((cur_console->status & UNKNOWN_MODE) || (cut_buffer == NULL)) break; switch (mouse->u.event.id) { case MOUSE_BUTTON1DOWN: switch (mouse->u.event.value % 4) { case 0: /* up */ mouse_cut_end(cur_console); break; case 1: mouse_cut_start(cur_console); break; case 2: mouse_cut_word(cur_console); break; case 3: mouse_cut_line(cur_console); break; } break; case MOUSE_BUTTON2DOWN: switch (mouse->u.event.value) { case 0: /* up */ break; default: mouse_paste(cur_console); break; } break; case MOUSE_BUTTON3DOWN: switch (mouse->u.event.value) { case 0: /* up */ if (!(cur_console->mouse_buttons & MOUSE_BUTTON1DOWN)) mouse_cut_end(cur_console); break; default: mouse_cut_extend(cur_console); break; } break; } break; default: return EINVAL; } /* make screensaver happy */ scsplash_stick(FALSE); run_scrn_saver = FALSE; return 0; } /* MOUSE_XXX: /dev/sysmouse ioctls */ case MOUSE_GETHWINFO: /* get device information */ { mousehw_t *hw = (mousehw_t *)data; if (tp != MOUSE_TTY) return ENOTTY; hw->buttons = 10; /* XXX unknown */ hw->iftype = MOUSE_IF_SYSMOUSE; hw->type = MOUSE_MOUSE; hw->model = MOUSE_MODEL_GENERIC; hw->hwid = 0; return 0; } case MOUSE_GETMODE: /* get protocol/mode */ { mousemode_t *mode = (mousemode_t *)data; if (tp != MOUSE_TTY) return ENOTTY; mode->level = mouse_level; switch (mode->level) { case 0: /* at this level, sysmouse emulates MouseSystems protocol */ mode->protocol = MOUSE_PROTO_MSC; mode->rate = -1; /* unknown */ mode->resolution = -1; /* unknown */ mode->accelfactor = 0; /* disabled */ mode->packetsize = MOUSE_MSC_PACKETSIZE; mode->syncmask[0] = MOUSE_MSC_SYNCMASK; mode->syncmask[1] = MOUSE_MSC_SYNC; break; case 1: /* at this level, sysmouse uses its own protocol */ mode->protocol = MOUSE_PROTO_SYSMOUSE; mode->rate = -1; mode->resolution = -1; mode->accelfactor = 0; mode->packetsize = MOUSE_SYS_PACKETSIZE; mode->syncmask[0] = MOUSE_SYS_SYNCMASK; mode->syncmask[1] = MOUSE_SYS_SYNC; break; } return 0; } case MOUSE_SETMODE: /* set protocol/mode */ { mousemode_t *mode = (mousemode_t *)data; if (tp != MOUSE_TTY) return ENOTTY; if ((mode->level < 0) || (mode->level > 1)) return EINVAL; mouse_level = mode->level; return 0; } case MOUSE_GETLEVEL: /* get operation level */ if (tp != MOUSE_TTY) return ENOTTY; *(int *)data = mouse_level; return 0; case MOUSE_SETLEVEL: /* set operation level */ if (tp != MOUSE_TTY) return ENOTTY; if ((*(int *)data < 0) || (*(int *)data > 1)) return EINVAL; mouse_level = *(int *)data; return 0; case MOUSE_GETSTATUS: /* get accumulated mouse events */ if (tp != MOUSE_TTY) return ENOTTY; s = spltty(); *(mousestatus_t *)data = mouse_status; mouse_status.flags = 0; mouse_status.obutton = mouse_status.button; mouse_status.dx = 0; mouse_status.dy = 0; mouse_status.dz = 0; splx(s); return 0; #if notyet case MOUSE_GETVARS: /* get internal mouse variables */ case MOUSE_SETVARS: /* set internal mouse variables */ if (tp != MOUSE_TTY) return ENOTTY; return ENODEV; #endif case MOUSE_READSTATE: /* read status from the device */ case MOUSE_READDATA: /* read data from the device */ if (tp != MOUSE_TTY) return ENOTTY; return ENODEV; case CONS_GETINFO: /* get current (virtual) console info */ { vid_info_t *ptr = (vid_info_t*)data; if (ptr->size == sizeof(struct vid_info)) { ptr->m_num = get_scr_num(); ptr->mv_col = scp->xpos; ptr->mv_row = scp->ypos; ptr->mv_csz = scp->xsize; ptr->mv_rsz = scp->ysize; ptr->mv_norm.fore = (scp->term.std_color & 0x0f00)>>8; ptr->mv_norm.back = (scp->term.std_color & 0xf000)>>12; ptr->mv_rev.fore = (scp->term.rev_color & 0x0f00)>>8; ptr->mv_rev.back = (scp->term.rev_color & 0xf000)>>12; ptr->mv_grfc.fore = 0; /* not supported */ ptr->mv_grfc.back = 0; /* not supported */ ptr->mv_ovscan = scp->border; ptr->mk_keylock = scp->status & LOCK_KEY_MASK; return 0; } return EINVAL; } case CONS_GETVERS: /* get version number */ *(int*)data = 0x200; /* version 2.0 */ return 0; case CONS_IDLE: /* see if the screen has been idle */ *(int *)data = (scrn_idle && !(cur_console->status & UNKNOWN_MODE)); return 0; case CONS_SAVERMODE: /* set saver mode */ switch(*(int *)data) { case CONS_USR_SAVER: /* if a LKM screen saver is running, it will eventually stop... */ saver_mode = *(int *)data; scsplash_stick(TRUE); break; case CONS_LKM_SAVER: saver_mode = *(int *)data; break; default: return EINVAL; } return 0; case CONS_SAVERSTART: /* immediately start/stop the screen saver */ /* * Note that this ioctl does not guarantee the screen saver * actually starts or stops. It merely attempts to do so... */ s = spltty(); run_scrn_saver = (*(int *)data != 0); if (run_scrn_saver) scrn_time_stamp -= scrn_blank_time; splx(s); return 0; /* VGA TEXT MODES */ case SW_VGA_C40x25: case SW_VGA_C80x25: case SW_VGA_M80x25: case SW_VGA_C80x30: case SW_VGA_M80x30: case SW_VGA_C80x50: case SW_VGA_M80x50: case SW_VGA_C80x60: case SW_VGA_M80x60: case SW_B40x25: case SW_C40x25: case SW_B80x25: case SW_C80x25: case SW_ENH_B40x25: case SW_ENH_C40x25: case SW_ENH_B80x25: case SW_ENH_C80x25: case SW_ENH_B80x43: case SW_ENH_C80x43: case SW_EGAMONO80x25: if (crtc_type != KD_VGA) return ENODEV; mp = get_mode_param(scp, cmd & 0xff); if (mp == NULL) return ENODEV; s = spltty(); if ((error = wait_scrn_saver_stop())) { splx(s); return error; } scp->status &= ~MOUSE_VISIBLE; remove_cutmarking(scp); if (scp->history != NULL) i = imax(scp->history_size / scp->xsize - imax(SC_HISTORY_SIZE, scp->ysize), 0); else i = 0; switch (cmd & 0xff) { case M_VGA_C80x60: case M_VGA_M80x60: if (!(fonts_loaded & FONT_8)) { splx(s); return EINVAL; } /* * This is a kludge to fend off scrn_update() while we * muck around with scp. XXX */ scp->status |= UNKNOWN_MODE; scp->xsize = 80; scp->ysize = 60; scp->font_size = 8; break; case M_VGA_C80x50: case M_VGA_M80x50: if (!(fonts_loaded & FONT_8)) { splx(s); return EINVAL; } scp->status |= UNKNOWN_MODE; scp->xsize = 80; scp->ysize = 50; scp->font_size = 8; break; case M_ENH_B80x43: case M_ENH_C80x43: if (!(fonts_loaded & FONT_8)) { splx(s); return EINVAL; } scp->status |= UNKNOWN_MODE; scp->xsize = 80; scp->ysize = 43; scp->font_size = 8; break; case M_VGA_C80x30: case M_VGA_M80x30: scp->status |= UNKNOWN_MODE; scp->xsize = 80; scp->ysize = 30; scp->font_size = mp[2]; break; case M_ENH_C40x25: case M_ENH_B40x25: case M_ENH_C80x25: case M_ENH_B80x25: case M_EGAMONO80x25: if (!(fonts_loaded & FONT_14)) { splx(s); return EINVAL; } /* FALL THROUGH */ default: if ((cmd & 0xff) > M_VGA_CG320) { splx(s); return EINVAL; } scp->status |= UNKNOWN_MODE; scp->xsize = mp[0]; scp->ysize = mp[1] + rows_offset; scp->font_size = mp[2]; break; } scp->mode = cmd & 0xff; scp->xpixel = scp->xsize * 8; scp->ypixel = scp->ysize * scp->font_size; free(scp->scr_buf, M_DEVBUF); scp->scr_buf = (u_short *) malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_WAITOK); /* move the text cursor to the home position */ move_crsr(scp, 0, 0); /* move the mouse cursor at the center of the screen */ scp->mouse_xpos = scp->xpixel / 2; scp->mouse_ypos = scp->ypixel / 2; scp->mouse_pos = scp->mouse_oldpos = scp->scr_buf + (scp->mouse_ypos / scp->font_size) * scp->xsize + scp->mouse_xpos / 8; /* allocate a larger cut buffer if necessary */ if ((cut_buffer == NULL) || (cut_buffer_size < scp->xsize * scp->ysize + 1)) { if (cut_buffer != NULL) free(cut_buffer, M_DEVBUF); cut_buffer_size = scp->xsize * scp->ysize + 1; cut_buffer = (char *)malloc(cut_buffer_size, M_DEVBUF, M_NOWAIT); if (cut_buffer != NULL) cut_buffer[0] = '\0'; } splx(s); usp = scp->history; scp->history = NULL; if (usp != NULL) { free(usp, M_DEVBUF); extra_history_size += i; } scp->history_size = imax(SC_HISTORY_SIZE, scp->ysize) * scp->xsize; usp = (u_short *)malloc(scp->history_size * sizeof(u_short), M_DEVBUF, M_NOWAIT); if (usp != NULL) bzero(usp, scp->history_size * sizeof(u_short)); scp->history_head = scp->history_pos = usp; scp->history = usp; if (scp == cur_console) set_mode(scp); clear_screen(scp); scp->status &= ~UNKNOWN_MODE; if (tp->t_winsize.ws_col != scp->xsize || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; /* GRAPHICS MODES */ case SW_BG320: case SW_BG640: case SW_CG320: case SW_CG320_D: case SW_CG640_E: case SW_CG640x350: case SW_ENH_CG640: case SW_BG640x480: case SW_CG640x480: case SW_VGA_CG320: if (crtc_type != KD_VGA) return ENODEV; mp = get_mode_param(scp, cmd & 0xff); if (mp == NULL) return ENODEV; s = spltty(); if ((error = wait_scrn_saver_stop())) { splx(s); return error; } scp->status &= ~MOUSE_VISIBLE; remove_cutmarking(scp); scp->status |= UNKNOWN_MODE; /* graphics mode */ scp->mode = cmd & 0xFF; scp->xpixel = mp[0] * 8; scp->ypixel = (mp[1] + rows_offset) * mp[2]; scp->font_size = FONT_NONE; /* move the mouse cursor at the center of the screen */ scp->mouse_xpos = scp->xpixel / 2; scp->mouse_ypos = scp->ypixel / 2; splx(s); if (scp == cur_console) set_mode(scp); /* clear_graphics();*/ if (tp->t_winsize.ws_xpixel != scp->xpixel || tp->t_winsize.ws_ypixel != scp->ypixel) { tp->t_winsize.ws_xpixel = scp->xpixel; tp->t_winsize.ws_ypixel = scp->ypixel; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; case SW_VGA_MODEX: if (crtc_type != KD_VGA) return ENODEV; mp = get_mode_param(scp, cmd & 0xff); if (mp == NULL) return ENODEV; s = spltty(); if ((error = wait_scrn_saver_stop())) { splx(s); return error; } scp->status &= ~MOUSE_VISIBLE; remove_cutmarking(scp); scp->status |= UNKNOWN_MODE; /* graphics mode */ scp->mode = cmd & 0xFF; scp->xpixel = 320; scp->ypixel = 240; scp->font_size = FONT_NONE; splx(s); if (scp == cur_console) set_mode(scp); /* clear_graphics();*/ if (tp->t_winsize.ws_xpixel != scp->xpixel || tp->t_winsize.ws_ypixel != scp->ypixel) { tp->t_winsize.ws_xpixel = scp->xpixel; tp->t_winsize.ws_ypixel = scp->ypixel; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; case VT_SETMODE: /* set screen switcher mode */ { struct vt_mode *mode; mode = (struct vt_mode *)data; if (ISSIGVALID(mode->relsig) && ISSIGVALID(mode->acqsig) && ISSIGVALID(mode->frsig)) { bcopy(data, &scp->smode, sizeof(struct vt_mode)); if (scp->smode.mode == VT_PROCESS) { scp->proc = p; scp->pid = scp->proc->p_pid; } return 0; } else return EINVAL; } case VT_GETMODE: /* get screen switcher mode */ bcopy(&scp->smode, data, sizeof(struct vt_mode)); return 0; case VT_RELDISP: /* screen switcher ioctl */ switch(*data) { case VT_FALSE: /* user refuses to release screen, abort */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { old_scp->status &= ~SWITCH_WAIT_REL; switch_in_progress = FALSE; return 0; } return EINVAL; case VT_TRUE: /* user has released screen, go on */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { scp->status &= ~SWITCH_WAIT_REL; exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; return 0; } return EINVAL; case VT_ACKACQ: /* acquire acknowledged, switch completed */ if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) { scp->status &= ~SWITCH_WAIT_ACQ; switch_in_progress = FALSE; return 0; } return EINVAL; default: return EINVAL; } /* NOT REACHED */ case VT_OPENQRY: /* return free virtual console */ for (i = 0; i < MAXCONS; i++) { tp = VIRTUAL_TTY(i); if (!(tp->t_state & TS_ISOPEN)) { *data = i + 1; return 0; } } return EINVAL; case VT_ACTIVATE: /* switch to screen *data */ return switch_scr(scp, (*data) - 1); case VT_WAITACTIVE: /* wait for switch to occur */ if (*data > MAXCONS || *data < 0) return EINVAL; if (minor(dev) == (*data) - 1) return 0; if (*data == 0) { if (scp == cur_console) return 0; } else scp = console[(*data) - 1]; while ((error=tsleep((caddr_t)&scp->smode, PZERO|PCATCH, "waitvt", 0)) == ERESTART) ; return error; case VT_GETACTIVE: *data = get_scr_num()+1; return 0; case KDENABIO: /* allow io operations */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return error; if (securelevel > 0) return EPERM; p->p_md.md_regs->tf_eflags |= PSL_IOPL; return 0; case KDDISABIO: /* disallow io operations (default) */ p->p_md.md_regs->tf_eflags &= ~PSL_IOPL; return 0; case KDSETMODE: /* set current mode of this (virtual) console */ switch (*data) { case KD_TEXT: /* switch to TEXT (known) mode */ /* restore fonts & palette ! */ if (crtc_type == KD_VGA) { if (!VESA_MODE(scp->mode)) { #if 0 /* * FONT KLUDGE * Don't load fonts for now... XXX */ if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); #endif } load_palette(palette); } /* move hardware cursor out of the way */ outb(crtc_addr, 14); outb(crtc_addr + 1, 0xff); outb(crtc_addr, 15); outb(crtc_addr + 1, 0xff); /* FALL THROUGH */ case KD_TEXT1: /* switch to TEXT (known) mode */ s = spltty(); if ((error = wait_scrn_saver_stop())) { splx(s); return error; } scp->status &= ~MOUSE_VISIBLE; remove_cutmarking(scp); scp->status |= UNKNOWN_MODE; splx(s); /* no restore fonts & palette */ if (crtc_type == KD_VGA) set_mode(scp); scp->status &= ~UNKNOWN_MODE; clear_screen(scp); return 0; case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */ s = spltty(); if ((error = wait_scrn_saver_stop())) { splx(s); return error; } scp->status &= ~MOUSE_VISIBLE; remove_cutmarking(scp); scp->status |= UNKNOWN_MODE; splx(s); return 0; default: return EINVAL; } /* NOT REACHED */ case KDGETMODE: /* get current mode of this (virtual) console */ *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT; return 0; case KDSBORDER: /* set border color of this (virtual) console */ scp->border = *data; if (scp == cur_console) set_border(scp->border); return 0; case KDSKBSTATE: /* set keyboard state (locks) */ if (*data >= 0 && *data <= LOCK_KEY_MASK) { scp->status &= ~LOCK_KEY_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGKBSTATE: /* get keyboard state (locks) */ *data = scp->status & LOCK_KEY_MASK; return 0; case KDSETRAD: /* set keyboard repeat & delay rates */ if (*data & 0x80) return EINVAL; if (sc_kbdc != NULL) set_keyboard(KBDC_SET_TYPEMATIC, *data); return 0; case KDSKBMODE: /* set keyboard mode */ switch (*data) { case K_RAW: /* switch to RAW scancode mode */ scp->status &= ~KBD_CODE_MODE; scp->status |= KBD_RAW_MODE; return 0; case K_CODE: /* switch to CODE mode */ scp->status &= ~KBD_RAW_MODE; scp->status |= KBD_CODE_MODE; return 0; case K_XLATE: /* switch to XLT ascii mode */ if (scp == cur_console && scp->status & KBD_RAW_MODE) shfts = ctls = alts = agrs = metas = accents = 0; scp->status &= ~(KBD_RAW_MODE | KBD_CODE_MODE); return 0; default: return EINVAL; } /* NOT REACHED */ case KDGKBMODE: /* get keyboard mode */ *data = (scp->status & KBD_RAW_MODE) ? K_RAW : ((scp->status & KBD_CODE_MODE) ? K_CODE : K_XLATE); return 0; case KDMKTONE: /* sound the bell */ if (*(int*)data) do_bell(scp, (*(int*)data)&0xffff, (((*(int*)data)>>16)&0xffff)*hz/1000); else do_bell(scp, scp->bell_pitch, scp->bell_duration); return 0; case KIOCSOUND: /* make tone (*data) hz */ if (scp == cur_console) { if (*(int*)data) { int pitch = timer_freq / *(int*)data; /* set command for counter 2, 2 byte write */ if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) return EBUSY; /* set pitch */ outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, (pitch>>8)); /* enable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) | 3); } else { /* disable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) & 0xFC); release_timer2(); } } return 0; case KDGKBTYPE: /* get keyboard type */ *data = 0; /* type not known (yet) */ return 0; case KDSETLED: /* set keyboard LED status */ if (*data >= 0 && *data <= LED_MASK) { scp->status &= ~LED_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGETLED: /* get keyboard LED status */ *data = scp->status & LED_MASK; return 0; case GETFKEY: /* get functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(&fkey_tab[ptr->keynum].str, ptr->keydef, fkey_tab[ptr->keynum].len); ptr->flen = fkey_tab[ptr->keynum].len; return 0; } else return EINVAL; case SETFKEY: /* set functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(ptr->keydef, &fkey_tab[ptr->keynum].str, min(ptr->flen, MAXFK)); fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK); return 0; } else return EINVAL; case GIO_SCRNMAP: /* get output translation table */ bcopy(&scr_map, data, sizeof(scr_map)); return 0; case PIO_SCRNMAP: /* set output translation table */ bcopy(data, &scr_map, sizeof(scr_map)); for (i=0; imode) && !(cur_console->status & UNKNOWN_MODE) && (cur_console->font_size < 14)) { copy_font(LOAD, FONT_8, font_8); if (flags & CHAR_CURSOR) set_destructive_cursor(cur_console); } return 0; case GIO_FONT8x8: /* get 8x8 dot font */ if (crtc_type != KD_VGA) return ENXIO; if (fonts_loaded & FONT_8) { bcopy(font_8, data, 8*256); return 0; } else return ENXIO; case PIO_FONT8x14: /* set 8x14 dot font */ if (crtc_type != KD_VGA) return ENXIO; bcopy(data, font_14, 14*256); fonts_loaded |= FONT_14; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x14. */ if (!VESA_MODE(cur_console->mode) && !(cur_console->status & UNKNOWN_MODE) && (cur_console->font_size >= 14) && (cur_console->font_size < 16)) { copy_font(LOAD, FONT_14, font_14); if (flags & CHAR_CURSOR) set_destructive_cursor(cur_console); } return 0; case GIO_FONT8x14: /* get 8x14 dot font */ if (crtc_type != KD_VGA) return ENXIO; if (fonts_loaded & FONT_14) { bcopy(font_14, data, 14*256); return 0; } else return ENXIO; case PIO_FONT8x16: /* set 8x16 dot font */ if (crtc_type != KD_VGA) return ENXIO; bcopy(data, font_16, 16*256); fonts_loaded |= FONT_16; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x16. */ if (!VESA_MODE(cur_console->mode) && !(cur_console->status & UNKNOWN_MODE) && (cur_console->font_size >= 16)) { copy_font(LOAD, FONT_16, font_16); if (flags & CHAR_CURSOR) set_destructive_cursor(cur_console); } return 0; case GIO_FONT8x16: /* get 8x16 dot font */ if (crtc_type != KD_VGA) return ENXIO; if (fonts_loaded & FONT_16) { bcopy(font_16, data, 16*256); return 0; } else return ENXIO; default: break; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return(error); error = ttioctl(tp, cmd, data, flag); if (error != ENOIOCTL) return(error); return(ENOTTY); } static void scstart(struct tty *tp) { struct clist *rbp; int s, len; u_char buf[PCBURST]; scr_stat *scp = get_scr_stat(tp->t_dev); if (scp->status & SLKED || blink_in_progress) return; /* XXX who repeats the call when the above flags are cleared? */ s = spltty(); if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { tp->t_state |= TS_BUSY; rbp = &tp->t_outq; while (rbp->c_cc) { len = q_to_b(rbp, buf, PCBURST); splx(s); ansi_put(scp, buf, len); s = spltty(); } tp->t_state &= ~TS_BUSY; ttwwakeup(tp); } splx(s); } static void scmousestart(struct tty *tp) { struct clist *rbp; int s; u_char buf[PCBURST]; s = spltty(); if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { tp->t_state |= TS_BUSY; rbp = &tp->t_outq; while (rbp->c_cc) { q_to_b(rbp, buf, PCBURST); } tp->t_state &= ~TS_BUSY; ttwwakeup(tp); } splx(s); } void sccnprobe(struct consdev *cp) { struct isa_device *dvp; /* * Take control if we are the highest priority enabled display device. */ dvp = find_display(); if (dvp == NULL || dvp->id_driver != &scdriver) { cp->cn_pri = CN_DEAD; return; } if (!scvidprobe(dvp->id_unit, dvp->id_flags)) { cp->cn_pri = CN_DEAD; return; } /* initialize required fields */ cp->cn_dev = makedev(CDEV_MAJOR, SC_CONSOLE); cp->cn_pri = CN_INTERNAL; sc_kbdc = kbdc_open(sc_port); } void sccninit(struct consdev *cp) { scinit(); } void sccnputc(dev_t dev, int c) { u_char buf[1]; int s; scr_stat *scp = console[0]; term_stat save = scp->term; scp->term = kernel_console; current_default = &kernel_default; if (scp == cur_console && !(scp->status & UNKNOWN_MODE)) remove_cursor_image(scp); buf[0] = c; ansi_put(scp, buf, 1); kernel_console = scp->term; current_default = &user_default; scp->term = save; s = spltty(); /* block scintr and scrn_timer */ sccnupdate(scp); splx(s); } int sccngetc(dev_t dev) { int s = spltty(); /* block scintr and scrn_timer while we poll */ int c; /* * Stop the screen saver and update the screen if necessary. * What if we have been running in the screen saver code... XXX */ scsplash_stick(FALSE); run_scrn_saver = FALSE; sccnupdate(cur_console); c = scgetc(SCGETC_CN); splx(s); return(c); } int sccncheckc(dev_t dev) { int s = spltty(); /* block scintr and scrn_timer while we poll */ int c; scsplash_stick(FALSE); run_scrn_saver = FALSE; sccnupdate(cur_console); c = scgetc(SCGETC_CN | SCGETC_NONBLOCK); splx(s); return(c == NOKEY ? -1 : c); /* c == -1 can't happen */ } static void sccnupdate(scr_stat *scp) { /* this is a cut-down version of scrn_timer()... */ if (font_loading_in_progress) return; if (panicstr) { scsplash_stick(FALSE); run_scrn_saver = FALSE; } if (!run_scrn_saver) scrn_idle = FALSE; if ((saver_mode != CONS_LKM_SAVER) || !scrn_idle) if (scrn_blanked > 0) stop_scrn_saver(current_saver); if (scp != cur_console || blink_in_progress || switch_in_progress) return; if ((scp->status & UNKNOWN_MODE) == 0 && scrn_blanked <= 0) scrn_update(scp, TRUE); } static scr_stat *get_scr_stat(dev_t dev) { int unit = minor(dev); if (unit == SC_CONSOLE) return console[0]; if (unit >= MAXCONS || unit < 0) return(NULL); return console[unit]; } static int get_scr_num() { int i = 0; while ((i < MAXCONS) && (cur_console != console[i])) i++; return i < MAXCONS ? i : 0; } static void scrn_timer(void *arg) { struct timeval tv; scr_stat *scp; int s; /* don't do anything when we are touching font */ if (font_loading_in_progress) { if (arg) timeout(scrn_timer, (void *)TRUE, hz / 10); return; } s = spltty(); /* * With release 2.1 of the Xaccel server, the keyboard is left * hanging pretty often. Apparently an interrupt from the * keyboard is lost, and I don't know why (yet). * This ugly hack calls scintr if input is ready for the keyboard * and conveniently hides the problem. XXX */ /* Try removing anything stuck in the keyboard controller; whether * it's a keyboard scan code or mouse data. `scintr()' doesn't * read the mouse data directly, but `kbdio' routines will, as a * side effect. */ if (kbdc_lock(sc_kbdc, TRUE)) { /* * We have seen the lock flag is not set. Let's reset the flag early; * otherwise `update_led()' failes which may want the lock * during `scintr()'. */ kbdc_lock(sc_kbdc, FALSE); if (kbdc_data_ready(sc_kbdc)) scintr(0); } /* should we stop the screen saver? */ getmicrouptime(&tv); if (panicstr || shutdown_in_progress) { scsplash_stick(FALSE); run_scrn_saver = FALSE; } if (run_scrn_saver) { scrn_idle = (tv.tv_sec > scrn_time_stamp + scrn_blank_time); } else { scrn_time_stamp = tv.tv_sec; scrn_idle = FALSE; if (scrn_blank_time > 0) run_scrn_saver = TRUE; } if ((saver_mode != CONS_LKM_SAVER) || !scrn_idle) if (scrn_blanked > 0) stop_scrn_saver(current_saver); /* should we just return ? */ if (blink_in_progress || switch_in_progress) { if (arg) timeout(scrn_timer, (void *)TRUE, hz / 10); splx(s); return; } /* Update the screen */ scp = cur_console; if ((scp->status & UNKNOWN_MODE) == 0 && scrn_blanked <= 0) scrn_update(scp, TRUE); /* should we activate the screen saver? */ if ((saver_mode == CONS_LKM_SAVER) && scrn_idle) if ((scp->status & UNKNOWN_MODE) == 0 || scrn_blanked > 0) (*current_saver)(TRUE); if (arg) timeout(scrn_timer, (void *)TRUE, hz / 25); splx(s); } static void scrn_update(scr_stat *scp, int show_cursor) { /* update screen image */ if (scp->start <= scp->end) sc_bcopy(scp, scp->scr_buf, scp->start, scp->end, 0); /* we are not to show the cursor and the mouse pointer... */ if (!show_cursor) { scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; return; } /* update "pseudo" mouse pointer image */ if (scp->status & MOUSE_VISIBLE) { /* did mouse move since last time ? */ if (scp->status & MOUSE_MOVED) { /* do we need to remove old mouse pointer image ? */ if (scp->mouse_cut_start != NULL || (scp->mouse_pos-scp->scr_buf) <= scp->start || (scp->mouse_pos+scp->xsize + 1 - scp->scr_buf) >= scp->end) { remove_mouse_image(scp); } scp->status &= ~MOUSE_MOVED; draw_mouse_image(scp); } else { /* mouse didn't move, has it been overwritten ? */ if ((scp->mouse_pos+scp->xsize + 1 - scp->scr_buf) >= scp->start && (scp->mouse_pos - scp->scr_buf) <= scp->end) { draw_mouse_image(scp); } } } /* update cursor image */ if (scp->status & CURSOR_ENABLED) { /* did cursor move since last time ? */ if (scp->cursor_pos != scp->cursor_oldpos) { /* do we need to remove old cursor image ? */ if ((scp->cursor_oldpos - scp->scr_buf) < scp->start || ((scp->cursor_oldpos - scp->scr_buf) > scp->end)) { remove_cursor_image(scp); } scp->cursor_oldpos = scp->cursor_pos; draw_cursor_image(scp); } else { /* cursor didn't move, has it been overwritten ? */ if (scp->cursor_pos - scp->scr_buf >= scp->start && scp->cursor_pos - scp->scr_buf <= scp->end) { draw_cursor_image(scp); } else { /* if its a blinking cursor, we may have to update it */ if (flags & BLINK_CURSOR) draw_cursor_image(scp); } } blinkrate++; } if (scp->mouse_cut_start != NULL) draw_cutmarking(scp); scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; } int add_scrn_saver(void (*this_saver)(int)) { #ifdef SC_SPLASH_SCREEN if (current_saver == scsplash) { scsplash_stick(FALSE); stop_scrn_saver(scsplash); } #endif if (current_saver != default_saver) return EBUSY; current_saver = this_saver; saver_mode = CONS_LKM_SAVER; run_scrn_saver = (scrn_blank_time > 0); return 0; } int remove_scrn_saver(void (*this_saver)(int)) { if (current_saver != this_saver) return EINVAL; /* * In order to prevent `current_saver' from being called by * the timeout routine `scrn_timer()' while we manipulate * the saver list, we shall set `current_saver' to `none_saver' * before stopping the current saver, rather than blocking by `splXX()'. */ current_saver = none_saver; if (scrn_blanked > 0) stop_scrn_saver(this_saver); if (scrn_blanked > 0) return EBUSY; /* XXX */ current_saver = default_saver; return 0; } static void stop_scrn_saver(void (*saver)(int)) { (*saver)(FALSE); run_scrn_saver = FALSE; /* the screen saver may have chosen not to stop after all... */ if (scrn_blanked > 0) return; mark_all(cur_console); if (delayed_next_scr) switch_scr(cur_console, delayed_next_scr - 1); wakeup((caddr_t)&scrn_blanked); } static int wait_scrn_saver_stop(void) { int error = 0; run_scrn_saver = FALSE; while (scrn_blanked > 0) { error = tsleep((caddr_t)&scrn_blanked, PZERO | PCATCH, "scrsav", 0); run_scrn_saver = FALSE; if (error != ERESTART) break; } return error; } static void clear_screen(scr_stat *scp) { move_crsr(scp, 0, 0); scp->cursor_oldpos = scp->cursor_pos; fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize * scp->ysize); mark_all(scp); remove_cutmarking(scp); } static int switch_scr(scr_stat *scp, u_int next_scr) { /* delay switch if actively updating screen */ if (scrn_blanked > 0 || write_in_progress || blink_in_progress) { scsplash_stick(FALSE); run_scrn_saver = FALSE; delayed_next_scr = next_scr+1; return 0; } if (switch_in_progress && (cur_console->proc != pfind(cur_console->pid))) switch_in_progress = FALSE; if (next_scr >= MAXCONS || switch_in_progress || (cur_console->smode.mode == VT_AUTO && cur_console->status & UNKNOWN_MODE)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } /* is the wanted virtual console open ? */ if (next_scr) { struct tty *tp = VIRTUAL_TTY(next_scr); if (!(tp->t_state & TS_ISOPEN)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } } switch_in_progress = TRUE; old_scp = cur_console; new_scp = console[next_scr]; wakeup((caddr_t)&new_scp->smode); if (new_scp == old_scp) { switch_in_progress = FALSE; delayed_next_scr = FALSE; return 0; } /* has controlling process died? */ if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid))) old_scp->smode.mode = VT_AUTO; if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid))) new_scp->smode.mode = VT_AUTO; /* check the modes and switch appropriately */ if (old_scp->smode.mode == VT_PROCESS) { old_scp->status |= SWITCH_WAIT_REL; psignal(old_scp->proc, old_scp->smode.relsig); } else { exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; } return 0; } static void exchange_scr(void) { move_crsr(old_scp, old_scp->xpos, old_scp->ypos); cur_console = new_scp; if (old_scp->mode != new_scp->mode || (old_scp->status & UNKNOWN_MODE)){ if (crtc_type == KD_VGA) set_mode(new_scp); } move_crsr(new_scp, new_scp->xpos, new_scp->ypos); if (!(new_scp->status & UNKNOWN_MODE) && (flags & CHAR_CURSOR)) set_destructive_cursor(new_scp); if ((old_scp->status & UNKNOWN_MODE) && crtc_type == KD_VGA) load_palette(palette); if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE || old_scp->status & KBD_CODE_MODE || new_scp->status & KBD_CODE_MODE) shfts = ctls = alts = agrs = metas = accents = 0; set_border(new_scp->border); update_leds(new_scp->status); delayed_next_scr = FALSE; mark_all(new_scp); if (new_scp->mode == 0x102) { bzero(Crtat, 800*600/8); } } static void scan_esc(scr_stat *scp, u_char c) { static u_char ansi_col[16] = {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15}; int i, n; u_short *src, *dst, count; if (scp->term.esc == 1) { /* seen ESC */ switch (c) { case '7': /* Save cursor position */ scp->saved_xpos = scp->xpos; scp->saved_ypos = scp->ypos; break; case '8': /* Restore saved cursor position */ if (scp->saved_xpos >= 0 && scp->saved_ypos >= 0) move_crsr(scp, scp->saved_xpos, scp->saved_ypos); break; case '[': /* Start ESC [ sequence */ scp->term.esc = 2; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'M': /* Move cursor up 1 line, scroll if at top */ if (scp->ypos > 0) move_crsr(scp, scp->xpos, scp->ypos - 1); else { bcopy(scp->scr_buf, scp->scr_buf + scp->xsize, (scp->ysize - 1) * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize); mark_all(scp); } break; #if notyet case 'Q': scp->term.esc = 4; return; #endif case 'c': /* Clear screen & home */ clear_screen(scp); break; case '(': /* iso-2022: designate 94 character set to G0 */ scp->term.esc = 5; return; } } else if (scp->term.esc == 2) { /* seen ESC [ */ if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case '=': scp->term.esc = 3; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'A': /* up n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos - n); break; case 'B': /* down n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'C': /* right n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'D': /* left n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos - n, scp->ypos); break; case 'E': /* cursor to start of line n lines down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos + n); break; case 'F': /* cursor to start of line n lines up */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos - n); break; case 'f': /* Cursor move */ case 'H': if (scp->term.num_param == 0) move_crsr(scp, 0, 0); else if (scp->term.num_param == 2) move_crsr(scp, scp->term.param[1] - 1, scp->term.param[0] - 1); break; case 'J': /* Clear all or part of display */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of display */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, scp->scr_buf + scp->xsize * scp->ysize - scp->cursor_pos); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->xsize * scp->ysize); remove_cutmarking(scp); break; case 1: /* clear from beginning of display to cursor */ fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, 0); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); remove_cutmarking(scp); break; case 2: /* clear entire display */ fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize * scp->ysize); mark_all(scp); remove_cutmarking(scp); break; } break; case 'K': /* Clear all or part of line */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of line */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, scp->xsize - scp->xpos); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + scp->xsize - scp->xpos); break; case 1: /* clear from beginning of line to cursor */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos - scp->xpos, scp->xpos + 1); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); break; case 2: /* clear entire line */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos - scp->xpos, scp->xsize); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, (scp->ypos + 1) * scp->xsize); break; } break; case 'L': /* Insert n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; src = scp->scr_buf + scp->ypos * scp->xsize; dst = src + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopy(src, dst, count * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], src, n * scp->xsize); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->xsize * scp->ysize); break; case 'M': /* Delete n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; dst = scp->scr_buf + scp->ypos * scp->xsize; src = dst + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopy(src, dst, count * scp->xsize * sizeof(u_short)); src = dst + count * scp->xsize; fillw(scp->term.cur_color | scr_map[0x20], src, n * scp->xsize); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->xsize * scp->ysize); break; case 'P': /* Delete n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; dst = scp->cursor_pos; src = dst + n; count = scp->xsize - (scp->xpos + n); bcopy(src, dst, count * sizeof(u_short)); src = dst + count; fillw(scp->term.cur_color | scr_map[0x20], src, n); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n + count); break; case '@': /* Insert n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; src = scp->cursor_pos; dst = src + n; count = scp->xsize - (scp->xpos + n); bcopy(src, dst, count * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], src, n); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n + count); break; case 'S': /* scroll up n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopy(scp->scr_buf + (scp->xsize * n), scp->scr_buf, scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - n), scp->xsize * n); mark_all(scp); break; case 'T': /* scroll down n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopy(scp->scr_buf, scp->scr_buf + (scp->xsize * n), scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize * n); mark_all(scp); break; case 'X': /* erase n characters in line */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, n); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n); break; case 'Z': /* move n tabs backwards */ n = scp->term.param[0]; if (n < 1) n = 1; if ((i = scp->xpos & 0xf8) == scp->xpos) i -= 8*n; else i -= 8*(n-1); if (i < 0) i = 0; move_crsr(scp, i, scp->ypos); break; case '`': /* move cursor to column n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, n - 1, scp->ypos); break; case 'a': /* move cursor n columns to the right */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'd': /* move cursor to row n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, n - 1); break; case 'e': /* move cursor n rows down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'm': /* change attribute */ if (scp->term.num_param == 0) { scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color; break; } for (i = 0; i < scp->term.num_param; i++) { switch (n = scp->term.param[i]) { case 0: /* back to normal */ scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color; break; case 1: /* bold */ scp->term.attr_mask |= BOLD_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 4: /* underline */ scp->term.attr_mask |= UNDERLINE_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 5: /* blink */ scp->term.attr_mask |= BLINK_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 7: /* reverse video */ scp->term.attr_mask |= REVERSE_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 30: case 31: /* set fg color */ case 32: case 33: case 34: case 35: case 36: case 37: scp->term.attr_mask |= FOREGROUND_CHANGED; scp->term.cur_color = (scp->term.cur_color&0xF000) | (ansi_col[(n-30)&7]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 40: case 41: /* set bg color */ case 42: case 43: case 44: case 45: case 46: case 47: scp->term.attr_mask |= BACKGROUND_CHANGED; scp->term.cur_color = (scp->term.cur_color&0x0F00) | (ansi_col[(n-40)&7]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; } } break; case 's': /* Save cursor position */ scp->saved_xpos = scp->xpos; scp->saved_ypos = scp->ypos; break; case 'u': /* Restore saved cursor position */ if (scp->saved_xpos >= 0 && scp->saved_ypos >= 0) move_crsr(scp, scp->saved_xpos, scp->saved_ypos); break; case 'x': if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* reset attributes */ scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color = current_default->std_color; scp->term.rev_color = current_default->rev_color; break; case 1: /* set ansi background */ scp->term.attr_mask &= ~BACKGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; case 2: /* set ansi foreground */ scp->term.attr_mask &= ~FOREGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 3: /* set ansi attribute directly */ scp->term.attr_mask &= ~(FOREGROUND_CHANGED|BACKGROUND_CHANGED); scp->term.cur_color = scp->term.std_color = (scp->term.param[1]&0xFF)<<8; scp->term.cur_attr = mask2attr(&scp->term); break; case 5: /* set ansi reverse video background */ scp->term.rev_color = (scp->term.rev_color & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; case 6: /* set ansi reverse video foreground */ scp->term.rev_color = (scp->term.rev_color & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 7: /* set ansi reverse video directly */ scp->term.rev_color = (scp->term.param[1]&0xFF)<<8; scp->term.cur_attr = mask2attr(&scp->term); break; } break; case 'z': /* switch to (virtual) console n */ if (scp->term.num_param == 1) switch_scr(scp, scp->term.param[0]); break; } } else if (scp->term.esc == 3) { /* seen ESC [0-9]+ = */ if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case 'A': /* set display border color */ if (scp->term.num_param == 1) { scp->border=scp->term.param[0] & 0xff; if (scp == cur_console) set_border(scp->border); } break; case 'B': /* set bell pitch and duration */ if (scp->term.num_param == 2) { scp->bell_pitch = scp->term.param[0]; scp->bell_duration = scp->term.param[1]; } break; case 'C': /* set cursor type & shape */ if (scp->term.num_param == 1) { if (scp->term.param[0] & 0x01) flags |= BLINK_CURSOR; else flags &= ~BLINK_CURSOR; if ((scp->term.param[0] & 0x02) && crtc_type == KD_VGA && !VESA_MODE(bios_video_mode)) flags |= CHAR_CURSOR; else flags &= ~CHAR_CURSOR; } else if (scp->term.num_param == 2) { scp->cursor_start = scp->term.param[0] & 0x1F; scp->cursor_end = scp->term.param[1] & 0x1F; } /* * The cursor shape is global property; all virtual consoles * are affected. Update the cursor in the current console... */ if (!(cur_console->status & UNKNOWN_MODE)) { remove_cursor_image(cur_console); if (crtc_type == KD_VGA && (flags & CHAR_CURSOR)) set_destructive_cursor(cur_console); draw_cursor_image(cur_console); } break; case 'F': /* set ansi foreground */ if (scp->term.num_param == 1) { scp->term.attr_mask &= ~FOREGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'G': /* set ansi background */ if (scp->term.num_param == 1) { scp->term.attr_mask &= ~BACKGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'H': /* set ansi reverse video foreground */ if (scp->term.num_param == 1) { scp->term.rev_color = (scp->term.rev_color & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'I': /* set ansi reverse video background */ if (scp->term.num_param == 1) { scp->term.rev_color = (scp->term.rev_color & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); scp->term.cur_attr = mask2attr(&scp->term); } break; } } #if notyet else if (scp->term.esc == 4) { /* seen ESC Q */ /* to be filled */ } #endif else if (scp->term.esc == 5) { /* seen ESC ( */ switch (c) { case 'B': /* iso-2022: desginate ASCII into G0 */ break; /* other items to be filled */ default: break; } } scp->term.esc = 0; } static void ansi_put(scr_stat *scp, u_char *buf, int len) { u_char *ptr = buf; /* make screensaver happy */ if (!sticky_splash && scp == cur_console) run_scrn_saver = FALSE; write_in_progress++; outloop: if (scp->term.esc) { scan_esc(scp, *ptr++); len--; } else if (PRINTABLE(*ptr)) { /* Print only printables */ int cnt = len <= (scp->xsize-scp->xpos) ? len : (scp->xsize-scp->xpos); u_short cur_attr = scp->term.cur_attr; u_short *cursor_pos = scp->cursor_pos; do { /* * gcc-2.6.3 generates poor (un)sign extension code. Casting the * pointers in the following to volatile should have no effect, * but in fact speeds up this inner loop from 26 to 18 cycles * (+ cache misses) on i486's. */ #define UCVP(ucp) ((u_char volatile *)(ucp)) *cursor_pos++ = UCVP(scr_map)[*UCVP(ptr)] | cur_attr; ptr++; cnt--; } while (cnt && PRINTABLE(*ptr)); len -= (cursor_pos - scp->cursor_pos); scp->xpos += (cursor_pos - scp->cursor_pos); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, cursor_pos - scp->scr_buf); scp->cursor_pos = cursor_pos; if (scp->xpos >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } else { switch(*ptr) { case 0x07: do_bell(scp, scp->bell_pitch, scp->bell_duration); break; case 0x08: /* non-destructive backspace */ if (scp->cursor_pos > scp->scr_buf) { mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos--; mark_for_update(scp, scp->cursor_pos - scp->scr_buf); if (scp->xpos > 0) scp->xpos--; else { scp->xpos += scp->xsize - 1; scp->ypos--; } } break; case 0x09: /* non-destructive tab */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos += (8 - scp->xpos % 8u); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); if ((scp->xpos += (8 - scp->xpos % 8u)) >= scp->xsize) { scp->xpos = 0; scp->ypos++; } break; case 0x0a: /* newline, same pos */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos += scp->xsize; mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->ypos++; break; case 0x0c: /* form feed, clears screen */ clear_screen(scp); break; case 0x0d: /* return, return to pos 0 */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos -= scp->xpos; mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->xpos = 0; break; case 0x1b: /* start escape sequence */ scp->term.esc = 1; scp->term.num_param = 0; break; } ptr++; len--; } /* do we have to scroll ?? */ if (scp->cursor_pos >= scp->scr_buf + scp->ysize * scp->xsize) { remove_cutmarking(scp); if (scp->history) { bcopy(scp->scr_buf, scp->history_head, scp->xsize * sizeof(u_short)); scp->history_head += scp->xsize; if (scp->history_head + scp->xsize > scp->history + scp->history_size) scp->history_head = scp->history; } bcopy(scp->scr_buf + scp->xsize, scp->scr_buf, scp->xsize * (scp->ysize - 1) * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - 1), scp->xsize); scp->cursor_pos -= scp->xsize; scp->ypos--; mark_all(scp); } if (len) goto outloop; write_in_progress--; if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } static void scinit(void) { u_int hw_cursor; u_int i; if (init_done != COLD) return; init_done = WARM; /* * Ensure a zero start address. This is mainly to recover after * switching from pcvt using userconfig(). The registers are w/o * for old hardware so it's too hard to relocate the active screen * memory. */ outb(crtc_addr, 12); outb(crtc_addr + 1, 0); outb(crtc_addr, 13); outb(crtc_addr + 1, 0); /* extract cursor location */ outb(crtc_addr, 14); hw_cursor = inb(crtc_addr + 1) << 8; outb(crtc_addr, 15); hw_cursor |= inb(crtc_addr + 1); /* * Validate cursor location. It may be off the screen. Then we must * not use it for the initial buffer offset. */ if (hw_cursor >= ROW * COL) hw_cursor = (ROW - 1) * COL; /* move hardware cursor out of the way */ outb(crtc_addr, 14); outb(crtc_addr + 1, 0xff); outb(crtc_addr, 15); outb(crtc_addr + 1, 0xff); /* set up the first console */ current_default = &user_default; console[0] = &main_console; init_scp(console[0]); cur_console = console[0]; /* discard the video mode table if we are not familiar with it... */ if (video_mode_ptr) { bzero(mode_map, sizeof(mode_map)); bcopy(video_mode_ptr + MODE_PARAM_SIZE*console[0]->mode, vgaregs2, sizeof(vgaregs2)); switch (comp_vgaregs(vgaregs, video_mode_ptr + MODE_PARAM_SIZE*console[0]->mode)) { case COMP_IDENTICAL: map_mode_table(mode_map, video_mode_ptr, M_VGA_CG320 + 1); /* * This is a kludge for Toshiba DynaBook SS433 whose BIOS video * mode table entry has the actual # of rows at the offset 1; * BIOSes from other manufacturers store the # of rows - 1 there. * XXX */ rows_offset = vgaregs[1] + 1 - video_mode_ptr[MODE_PARAM_SIZE*console[0]->mode + 1]; break; case COMP_SIMILAR: map_mode_table(mode_map, video_mode_ptr, M_VGA_CG320 + 1); mode_map[console[0]->mode] = vgaregs; rows_offset = vgaregs[1] + 1 - video_mode_ptr[MODE_PARAM_SIZE*console[0]->mode + 1]; vgaregs[1] -= rows_offset - 1; break; case COMP_DIFFERENT: default: video_mode_ptr = NULL; mode_map[console[0]->mode] = vgaregs; rows_offset = 1; break; } } /* copy screen to temporary buffer */ if (!VESA_MODE(console[0]->mode)) generic_bcopy(Crtat, sc_buffer, console[0]->xsize * console[0]->ysize * sizeof(u_short)); console[0]->scr_buf = console[0]->mouse_pos = console[0]->mouse_oldpos = sc_buffer; console[0]->cursor_pos = console[0]->cursor_oldpos = sc_buffer + hw_cursor; console[0]->cursor_saveunder = *console[0]->cursor_pos; console[0]->xpos = hw_cursor % COL; console[0]->ypos = hw_cursor / COL; for (i=1; ismode.mode == VT_AUTO && console[0]->smode.mode == VT_AUTO) switch_scr(cur_console, 0); shutdown_in_progress = TRUE; } static void map_mode_table(char *map[], char *table, int max) { int i; for(i = 0; i < max; ++i) map[i] = table + i*MODE_PARAM_SIZE; for(; i < MODE_MAP_SIZE; ++i) map[i] = NULL; } static int map_mode_num(int mode) { static struct { int from; int to; } mode_map[] = { { M_ENH_B80x43, M_ENH_B80x25 }, { M_ENH_C80x43, M_ENH_C80x25 }, { M_VGA_M80x30, M_VGA_M80x25 }, { M_VGA_C80x30, M_VGA_C80x25 }, { M_VGA_M80x50, M_VGA_M80x25 }, { M_VGA_C80x50, M_VGA_C80x25 }, { M_VGA_M80x60, M_VGA_M80x25 }, { M_VGA_C80x60, M_VGA_C80x25 }, { M_VGA_MODEX, M_VGA_CG320 }, }; int i; for (i = 0; i < sizeof(mode_map)/sizeof(mode_map[0]); ++i) { if (mode_map[i].from == mode) return mode_map[i].to; } return mode; } static char *get_mode_param(scr_stat *scp, int mode) { if (mode >= MODE_MAP_SIZE) mode = map_mode_num(mode); if (mode < MODE_MAP_SIZE) return mode_map[mode]; else return NULL; } static scr_stat *alloc_scp() { scr_stat *scp; scp = (scr_stat *)malloc(sizeof(scr_stat), M_DEVBUF, M_WAITOK); init_scp(scp); scp->scr_buf = scp->cursor_pos = scp->cursor_oldpos = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_WAITOK); scp->mouse_pos = scp->mouse_oldpos = scp->scr_buf + ((scp->mouse_ypos/scp->font_size)*scp->xsize + scp->mouse_xpos/8); scp->history_head = scp->history_pos = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_WAITOK); bzero(scp->history_head, scp->history_size*sizeof(u_short)); scp->history = scp->history_head; /* SOS if (crtc_type == KD_VGA && video_mode_ptr) set_mode(scp); */ clear_screen(scp); scp->cursor_saveunder = *scp->cursor_pos; return scp; } static void init_scp(scr_stat *scp) { switch(crtc_type) { case KD_VGA: if (VESA_MODE(bios_video_mode)) scp->mode = bios_video_mode; else if (crtc_addr == MONO_BASE) scp->mode = M_VGA_M80x25; else scp->mode = M_VGA_C80x25; scp->font_size = 16; break; case KD_CGA: if (crtc_addr == MONO_BASE) scp->mode = M_B80x25; else scp->mode = M_C80x25; scp->font_size = 8; break; case KD_EGA: if (crtc_addr == MONO_BASE) scp->mode = M_B80x25; else scp->mode = M_C80x25; scp->font_size = 14; break; case KD_MONO: case KD_HERCULES: default: scp->mode = M_EGAMONO80x25; scp->font_size = 14; break; } scp->initial_mode = scp->mode; scp->xsize = COL; scp->ysize = ROW; scp->xpixel = scp->xsize * 8; scp->ypixel = scp->ysize * scp->font_size; scp->xpos = scp->ypos = 0; scp->saved_xpos = scp->saved_ypos = -1; scp->start = scp->xsize * scp->ysize; scp->end = 0; scp->term.esc = 0; scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color = current_default->std_color; scp->term.rev_color = current_default->rev_color; scp->border = BG_BLACK; scp->cursor_start = *(char *)pa_to_va(0x461); scp->cursor_end = *(char *)pa_to_va(0x460); scp->mouse_xpos = scp->xsize*8/2; scp->mouse_ypos = scp->ysize*scp->font_size/2; scp->mouse_cut_start = scp->mouse_cut_end = NULL; scp->mouse_signal = 0; scp->mouse_pid = 0; scp->mouse_proc = NULL; scp->bell_pitch = BELL_PITCH; scp->bell_duration = BELL_DURATION; scp->status = (*(char *)pa_to_va(0x417) & 0x20) ? NLKED : 0; scp->status |= CURSOR_ENABLED; scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; scp->history_head = scp->history_pos = scp->history = NULL; scp->history_size = imax(SC_HISTORY_SIZE, scp->ysize) * scp->xsize; } static u_char *get_fstr(u_int c, u_int *len) { u_int i; if (!(c & FKEY)) return(NULL); i = (c & 0xFF) - F_FN; if (i > n_fkey_tab) return(NULL); *len = fkey_tab[i].len; return(fkey_tab[i].str); } static void history_to_screen(scr_stat *scp) { int i; for (i=0; iysize; i++) bcopy(scp->history + (((scp->history_pos - scp->history) + scp->history_size-((i+1)*scp->xsize))%scp->history_size), scp->scr_buf + (scp->xsize * (scp->ysize-1 - i)), scp->xsize * sizeof(u_short)); mark_all(scp); } static int history_up_line(scr_stat *scp) { if (WRAPHIST(scp, scp->history_pos, -(scp->xsize*scp->ysize)) != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, -scp->xsize); history_to_screen(scp); return 0; } else return -1; } static int history_down_line(scr_stat *scp) { if (scp->history_pos != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, scp->xsize); history_to_screen(scp); return 0; } else return -1; } /* * scgetc(flags) - get character from keyboard. * If flags & SCGETC_CN, then avoid harmful side effects. * If flags & SCGETC_NONBLOCK, then wait until a key is pressed, else * return NOKEY if there is nothing there. */ static u_int scgetc(u_int flags) { struct key_t *key; u_char scancode, keycode; u_int state, action; int c; static u_char esc_flag = 0, compose = 0; static u_int chr = 0; next_code: /* first see if there is something in the keyboard port */ if (flags & SCGETC_NONBLOCK) { c = read_kbd_data_no_wait(sc_kbdc); if (c == -1) return(NOKEY); } else { do { c = read_kbd_data(sc_kbdc); } while(c == -1); } scancode = (u_char)c; /* make screensaver happy */ if (!(scancode & 0x80)) { scsplash_stick(FALSE); run_scrn_saver = FALSE; } if (!(flags & SCGETC_CN)) { /* do the /dev/random device a favour */ add_keyboard_randomness(scancode); if (cur_console->status & KBD_RAW_MODE) return scancode; } keycode = scancode & 0x7F; switch (esc_flag) { case 0x00: /* normal scancode */ switch(scancode) { case 0xB8: /* left alt (compose key) */ if (compose) { compose = 0; if (chr > 255) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); chr = 0; } } break; case 0x38: if (!compose) { compose = 1; chr = 0; } break; case 0xE0: case 0xE1: esc_flag = scancode; goto next_code; } break; case 0xE0: /* 0xE0 prefix */ esc_flag = 0; switch (keycode) { case 0x1C: /* right enter key */ keycode = 0x59; break; case 0x1D: /* right ctrl key */ keycode = 0x5A; break; case 0x35: /* keypad divide key */ keycode = 0x5B; break; case 0x37: /* print scrn key */ keycode = 0x5C; break; case 0x38: /* right alt key (alt gr) */ keycode = 0x5D; break; case 0x47: /* grey home key */ keycode = 0x5E; break; case 0x48: /* grey up arrow key */ keycode = 0x5F; break; case 0x49: /* grey page up key */ keycode = 0x60; break; case 0x4B: /* grey left arrow key */ keycode = 0x61; break; case 0x4D: /* grey right arrow key */ keycode = 0x62; break; case 0x4F: /* grey end key */ keycode = 0x63; break; case 0x50: /* grey down arrow key */ keycode = 0x64; break; case 0x51: /* grey page down key */ keycode = 0x65; break; case 0x52: /* grey insert key */ keycode = 0x66; break; case 0x53: /* grey delete key */ keycode = 0x67; break; /* the following 3 are only used on the MS "Natural" keyboard */ case 0x5b: /* left Window key */ keycode = 0x69; break; case 0x5c: /* right Window key */ keycode = 0x6a; break; case 0x5d: /* menu key */ keycode = 0x6b; break; default: /* ignore everything else */ goto next_code; } break; case 0xE1: /* 0xE1 prefix */ esc_flag = 0; if (keycode == 0x1D) esc_flag = 0x1D; goto next_code; /* NOT REACHED */ case 0x1D: /* pause / break */ esc_flag = 0; if (keycode != 0x45) goto next_code; keycode = 0x68; break; } if (!(flags & SCGETC_CN) && (cur_console->status & KBD_CODE_MODE)) return (keycode | (scancode & 0x80)); /* if scroll-lock pressed allow history browsing */ if (cur_console->history && cur_console->status & SLKED) { int i; cur_console->status &= ~CURSOR_ENABLED; if (!(cur_console->status & BUFFER_SAVED)) { cur_console->status |= BUFFER_SAVED; cur_console->history_save = cur_console->history_head; /* copy screen into top of history buffer */ for (i=0; iysize; i++) { bcopy(cur_console->scr_buf + (cur_console->xsize * i), cur_console->history_head, cur_console->xsize * sizeof(u_short)); cur_console->history_head += cur_console->xsize; if (cur_console->history_head + cur_console->xsize > cur_console->history + cur_console->history_size) cur_console->history_head=cur_console->history; } cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); } switch (scancode) { case 0x47: /* home key */ cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); goto next_code; case 0x4F: /* end key */ cur_console->history_pos = WRAPHIST(cur_console, cur_console->history_head, cur_console->xsize*cur_console->ysize); history_to_screen(cur_console); goto next_code; case 0x48: /* up arrow key */ if (history_up_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case 0x50: /* down arrow key */ if (history_down_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case 0x49: /* page up key */ for (i=0; iysize; i++) if (history_up_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; case 0x51: /* page down key */ for (i=0; iysize; i++) if (history_down_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; } } if (compose) { switch (scancode) { /* key pressed process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ chr = (scancode - 0x40) + chr*10; goto next_code; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ chr = (scancode - 0x47) + chr*10; goto next_code; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ chr = (scancode - 0x4E) + chr*10; goto next_code; case 0x52: /* keypad 0 */ chr *= 10; goto next_code; /* key release, no interest here */ case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ case 0xD2: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (chr) { compose = chr = 0; do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; } break; } } state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0)); if ((!agrs && (cur_console->status & ALKED)) || (agrs && !(cur_console->status & ALKED))) keycode += ALTGR_OFFSET; key = &key_map.key[keycode]; if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED)) || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) ) state ^= 1; /* Check for make/break */ action = key->map[state]; if (scancode & 0x80) { /* key released */ if (key->spcl & (0x80>>state)) { switch (action) { case LSH: shfts &= ~1; break; case RSH: shfts &= ~2; break; case LCTR: ctls &= ~1; break; case RCTR: ctls &= ~2; break; case LALT: alts &= ~1; break; case RALT: alts &= ~2; break; case NLK: nlkcnt = 0; break; case CLK: clkcnt = 0; break; case SLK: slkcnt = 0; break; case ASH: agrs = 0; break; case ALK: alkcnt = 0; break; case META: metas = 0; break; } } if (chr && !compose) { action = chr; chr = 0; return(action); } } else { /* key pressed */ if (key->spcl & (0x80>>state)) { switch (action) { /* LOCKING KEYS */ case NLK: if (!nlkcnt) { nlkcnt++; if (cur_console->status & NLKED) cur_console->status &= ~NLKED; else cur_console->status |= NLKED; update_leds(cur_console->status); } break; case CLK: if (!clkcnt) { clkcnt++; if (cur_console->status & CLKED) cur_console->status &= ~CLKED; else cur_console->status |= CLKED; update_leds(cur_console->status); } break; case SLK: if (!slkcnt) { slkcnt++; if (cur_console->status & SLKED) { cur_console->status &= ~SLKED; if (cur_console->status & BUFFER_SAVED){ int i; u_short *ptr = cur_console->history_save; for (i=0; iysize; i++) { bcopy(ptr, cur_console->scr_buf + (cur_console->xsize*i), cur_console->xsize * sizeof(u_short)); ptr += cur_console->xsize; if (ptr + cur_console->xsize > cur_console->history + cur_console->history_size) ptr = cur_console->history; } cur_console->status &= ~BUFFER_SAVED; cur_console->history_head=cur_console->history_save; cur_console->status |= CURSOR_ENABLED; mark_all(cur_console); } scstart(VIRTUAL_TTY(get_scr_num())); } else cur_console->status |= SLKED; update_leds(cur_console->status); } break; case ALK: if (!alkcnt) { alkcnt++; if (cur_console->status & ALKED) cur_console->status &= ~ALKED; else cur_console->status |= ALKED; update_leds(cur_console->status); } break; /* NON-LOCKING KEYS */ case NOP: break; case SPSC: /* force activatation/deactivation of the screen saver */ accents = 0; if (scrn_blanked <= 0) { run_scrn_saver = TRUE; scrn_time_stamp -= scrn_blank_time; } #ifdef SC_SPLASH_SCREEN if (cold) { /* * While devices are being probed, the screen saver need * to be invoked explictly. XXX */ if (scrn_blanked > 0) { scsplash_stick(FALSE); stop_scrn_saver(current_saver); } else { if ((cur_console->status & UNKNOWN_MODE) == 0) { scsplash_stick(TRUE); (*current_saver)(TRUE); } } } #endif break; case RBT: #ifndef SC_DISABLE_REBOOT accents = 0; shutdown_nice(); #endif break; case SUSP: #if NAPM > 0 accents = 0; apm_suspend(PMST_SUSPEND); #endif break; case STBY: #if NAPM > 0 accents = 0; apm_suspend(PMST_STANDBY); #endif break; case DBG: #ifdef DDB /* try to switch to console 0 */ accents = 0; /* * TRY to make sure the screen saver is stopped, * and the screen is updated before switching to * the vty0. */ scrn_timer((void *)FALSE); if (cur_console->smode.mode == VT_AUTO && console[0]->smode.mode == VT_AUTO) switch_scr(cur_console, 0); Debugger("manual escape to debugger"); #else printf("No debugger in kernel\n"); #endif break; case LSH: shfts |= 1; break; case RSH: shfts |= 2; break; case LCTR: ctls |= 1; break; case RCTR: ctls |= 2; break; case LALT: alts |= 1; break; case RALT: alts |= 2; break; case ASH: agrs = 1; break; case META: metas = 1; break; case NEXT: { int next, this = get_scr_num(); accents = 0; for (next = this+1; next != this; next = (next+1)%MAXCONS) { struct tty *tp = VIRTUAL_TTY(next); if (tp->t_state & TS_ISOPEN) { switch_scr(cur_console, next); break; } } } break; case BTAB: accents = 0; return(BKEY); default: if (action >= F_ACC && action <= L_ACC) { /* turn it into an index */ action -= F_ACC - 1; if ((action > accent_map.n_accs) || (accent_map.acc[action - 1].accchar == 0)) { /* * The index is out of range or pointing to an * empty entry. */ accents = 0; do_bell(cur_console, BELL_PITCH, BELL_DURATION); } /* * If the same accent key has been hit twice, * produce the accent char itself. */ if (action == accents) { action = accent_map.acc[accents - 1].accchar; accents = 0; if (metas) action |= MKEY; return (action); } /* remember the index and wait for the next key stroke */ accents = action; break; } if (accents > 0) { accents = 0; do_bell(cur_console, BELL_PITCH, BELL_DURATION); } if (action >= F_SCR && action <= L_SCR) { switch_scr(cur_console, action - F_SCR); break; } if (action >= F_FN && action <= L_FN) action |= FKEY; return(action); } } else { if (accents) { struct acc_t *acc; int i; acc = &accent_map.acc[accents - 1]; accents = 0; /* * If the accent key is followed by the space key, * produce the accent char itself. */ if (action == ' ') { action = acc->accchar; if (metas) action |= MKEY; return (action); } for (i = 0; i < NUM_ACCENTCHARS; ++i) { if (acc->map[i][0] == 0) /* end of the map entry */ break; if (acc->map[i][0] == action) { action = acc->map[i][1]; if (metas) action |= MKEY; return (action); } } do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; } if (metas) action |= MKEY; return(action); } } goto next_code; } int scmmap(dev_t dev, int offset, int nprot) { if (offset > 0x20000 - PAGE_SIZE) return -1; return i386_btop((VIDEOMEM + offset)); } /* * Calculate hardware attributes word using logical attributes mask and * hardware colors */ static int mask2attr(struct term_stat *term) { int attr, mask = term->attr_mask; if (mask & REVERSE_ATTR) { attr = ((mask & FOREGROUND_CHANGED) ? ((term->cur_color & 0xF000) >> 4) : (term->rev_color & 0x0F00)) | ((mask & BACKGROUND_CHANGED) ? ((term->cur_color & 0x0F00) << 4) : (term->rev_color & 0xF000)); } else attr = term->cur_color; /* XXX: underline mapping for Hercules adapter can be better */ if (mask & (BOLD_ATTR | UNDERLINE_ATTR)) attr ^= 0x0800; if (mask & BLINK_ATTR) attr ^= 0x8000; return attr; } static void set_keyboard(int command, int data) { int s; if (sc_kbdc == NULL) return; /* prevent the timeout routine from polling the keyboard */ if (!kbdc_lock(sc_kbdc, TRUE)) return; /* disable the keyboard and mouse interrupt */ s = spltty(); #if 0 c = get_controller_command_byte(sc_kbdc); if ((c == -1) || !set_controller_command_byte(sc_kbdc, kbdc_get_device_mask(sc_kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ kbdc_lock(sc_kbdc, FALSE); splx(s); return; } /* * Now that the keyboard controller is told not to generate * the keyboard and mouse interrupts, call `splx()' to allow * the other tty interrupts. The clock interrupt may also occur, * but the timeout routine (`scrn_timer()') will be blocked * by the lock flag set via `kbdc_lock()' */ splx(s); #endif if (send_kbd_command_and_data(sc_kbdc, command, data) != KBD_ACK) send_kbd_command(sc_kbdc, KBDC_ENABLE_KBD); #if 0 /* restore the interrupts */ if (!set_controller_command_byte(sc_kbdc, kbdc_get_device_mask(sc_kbdc), c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { /* CONTROLLER ERROR */ } #else splx(s); #endif kbdc_lock(sc_kbdc, FALSE); } static void update_leds(int which) { static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* replace CAPS led with ALTGR led for ALTGR keyboards */ if (key_map.n_keys > ALTGR_OFFSET) { if (which & ALKED) which |= CLKED; else which &= ~CLKED; } set_keyboard(KBDC_SET_LEDS, xlate_leds[which & LED_MASK]); } void set_mode(scr_stat *scp) { char special_modetable[MODE_PARAM_SIZE]; char *mp; int s; int i; if (scp != cur_console) return; /* * even if mode switching is disabled, we can change back * to the initial mode or the custom mode based on the initial * mode if we have saved register values upon start-up. */ mp = get_mode_param(scp, scp->mode); if (mp == NULL) return; bcopy(mp, &special_modetable, sizeof(special_modetable)); /* setup video hardware for the given mode */ switch (scp->mode) { case M_VGA_C80x60: case M_VGA_M80x60: special_modetable[2] = 0x08; special_modetable[19] = 0x47; goto special_480l; case M_VGA_C80x30: case M_VGA_M80x30: special_modetable[19] = 0x4f; special_480l: special_modetable[9] |= 0xc0; special_modetable[16] = 0x08; special_modetable[17] = 0x3e; special_modetable[26] = 0xea; special_modetable[28] = 0xdf; special_modetable[31] = 0xe7; special_modetable[32] = 0x04; goto setup_mode; case M_ENH_C80x43: case M_ENH_B80x43: special_modetable[28] = 87; goto special_80x50; case M_VGA_C80x50: case M_VGA_M80x50: special_80x50: special_modetable[2] = 8; special_modetable[19] = 7; goto setup_mode; case M_VGA_C40x25: case M_VGA_C80x25: case M_VGA_M80x25: case M_B40x25: case M_C40x25: case M_B80x25: case M_C80x25: case M_ENH_B40x25: case M_ENH_C40x25: case M_ENH_B80x25: case M_ENH_C80x25: case M_EGAMONO80x25: setup_mode: set_vgaregs(special_modetable); scp->font_size = special_modetable[2]; /* set font type (size) */ if (scp->font_size < 14) { if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); i = 0x0a; /* font 2 */ } else if (scp->font_size >= 16) { if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); i = 0x00; /* font 0 */ } else { if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); i = 0x05; /* font 1 */ } /* * FONT KLUDGE: * This is an interim kludge to display correct font. * Always use the font page #0 on the video plane 2. * Somehow we cannot show the font in other font pages on * some video cards... XXX */ i = 0x00; s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); outb(TSIDX, 0x03); outb(TSREG, i); outb(TSIDX, 0x00); outb(TSREG, 0x03); splx(s); if (flags & CHAR_CURSOR) set_destructive_cursor(scp); mark_all(scp); break; case M_VGA_MODEX: /* "unchain" the VGA mode */ special_modetable[5-1+0x04] &= 0xf7; special_modetable[5-1+0x04] |= 0x04; /* turn off doubleword mode */ special_modetable[10+0x14] &= 0xbf; /* turn off word adressing */ special_modetable[10+0x17] |= 0x40; /* set logical screen width */ special_modetable[10+0x13] = 80; /* set 240 lines */ special_modetable[10+0x11] = 0x2c; special_modetable[10+0x06] = 0x0d; special_modetable[10+0x07] = 0x3e; special_modetable[10+0x10] = 0xea; special_modetable[10+0x11] = 0xac; special_modetable[10+0x12] = 0xdf; special_modetable[10+0x15] = 0xe7; special_modetable[10+0x16] = 0x06; /* set vertical sync polarity to reflect aspect ratio */ special_modetable[9] = 0xe3; goto setup_grmode; case M_BG320: case M_CG320: case M_BG640: case M_CG320_D: case M_CG640_E: case M_CG640x350: case M_ENH_CG640: case M_BG640x480: case M_CG640x480: case M_VGA_CG320: setup_grmode: set_vgaregs(special_modetable); scp->font_size = FONT_NONE; break; default: /* call user defined function XXX */ break; } /* set border color for this (virtual) console */ set_border(scp->border); return; } void set_border(u_char color) { switch (crtc_type) { case KD_EGA: case KD_VGA: inb(crtc_addr + 6); /* reset flip-flop */ outb(ATC, 0x31); outb(ATC, color); break; case KD_CGA: outb(crtc_addr + 5, color & 0x0f); /* color select register */ break; case KD_MONO: case KD_HERCULES: default: break; } } static void set_vgaregs(char *modetable) { int i, s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ for (i=0; i<4; i++) { /* program sequencer */ outb(TSIDX, i+1); outb(TSREG, modetable[i+5]); } outb(MISC, modetable[9]); /* set dot-clock */ outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ outb(crtc_addr, 0x11); outb(crtc_addr+1, inb(crtc_addr+1) & 0x7F); for (i=0; i<25; i++) { /* program crtc */ outb(crtc_addr, i); if (i == 14 || i == 15) /* no hardware cursor */ outb(crtc_addr+1, 0xff); else outb(crtc_addr+1, modetable[i+10]); } inb(crtc_addr+6); /* reset flip-flop */ for (i=0; i<20; i++) { /* program attribute ctrl */ outb(ATC, i); outb(ATC, modetable[i+35]); } for (i=0; i<9; i++) { /* program graph data ctrl */ outb(GDCIDX, i); outb(GDCREG, modetable[i+55]); } inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ splx(s); } static void read_vgaregs(char *buf) { int i, j; int s; bzero(buf, MODE_PARAM_SIZE); s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ for (i=0, j=5; i<4; i++) { outb(TSIDX, i+1); buf[j++] = inb(TSREG); } buf[9] = inb(MISC + 10); /* dot-clock */ outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ for (i=0, j=10; i<25; i++) { /* crtc */ outb(crtc_addr, i); buf[j++] = inb(crtc_addr+1); } for (i=0, j=35; i<20; i++) { /* attribute ctrl */ inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, i); buf[j++] = inb(ATC + 1); } for (i=0, j=55; i<9; i++) { /* graph data ctrl */ outb(GDCIDX, i); buf[j++] = inb(GDCREG); } inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ buf[0] = *(char *)pa_to_va(0x44a); /* COLS */ buf[1] = *(char *)pa_to_va(0x484); /* ROWS */ buf[2] = *(char *)pa_to_va(0x485); /* POINTS */ buf[3] = *(char *)pa_to_va(0x44c); buf[4] = *(char *)pa_to_va(0x44d); splx(s); } static int comp_vgaregs(u_char *buf1, u_char *buf2) { static struct { u_char mask; } params[MODE_PARAM_SIZE] = { 0xff, 0x00, 0xff, /* COLS, ROWS, POINTS */ 0xff, 0xff, /* page length */ 0xfe, 0xff, 0xff, 0xff, /* sequencer registers */ 0xf3, /* misc register */ 0xff, 0xff, 0xff, 0x7f, 0xff, /* CRTC */ 0xff, 0xff, 0xff, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* attribute controller registers */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, /* GDC register */ 0xff, 0xff, 0xff, 0xff, }; int identical = TRUE; int i; for (i = 0; i < sizeof(params)/sizeof(params[0]); ++i) { if (params[i].mask == 0) /* don't care */ continue; if ((buf1[i] & params[i].mask) != (buf2[i] & params[i].mask)) return COMP_DIFFERENT; if (buf1[i] != buf2[i]) identical = FALSE; } return (identical) ? COMP_IDENTICAL : COMP_SIMILAR; #if 0 for(i = 0; i < 20; ++i) { if (*buf1++ != *buf2++) return COMP_DIFFERENT; } buf1 += 2; /* skip the cursor shape */ buf2 += 2; for(i = 22; i < 24; ++i) { if (*buf1++ != *buf2++) return COMP_DIFFERENT; } buf1 += 2; /* skip the cursor position */ buf2 += 2; for(i = 26; i < MODE_PARAM_SIZE; ++i) { if (*buf1++ != *buf2++) return COMP_DIFFERENT; } return COMP_IDENTICAL; #endif } static void dump_vgaregs(u_char *buf) { int i; for(i = 0; i < MODE_PARAM_SIZE;) { printf("%02x ", buf[i]); if ((++i % 16) == 0) printf("\n"); } } static void set_font_mode(u_char *buf) { int s = splhigh(); font_loading_in_progress = TRUE; /* save register values */ outb(TSIDX, 0x02); buf[0] = inb(TSREG); outb(TSIDX, 0x04); buf[1] = inb(TSREG); outb(GDCIDX, 0x04); buf[2] = inb(GDCREG); outb(GDCIDX, 0x05); buf[3] = inb(GDCREG); outb(GDCIDX, 0x06); buf[4] = inb(GDCREG); inb(crtc_addr + 6); outb(ATC, 0x10); buf[5] = inb(ATC + 1); /* setup vga for loading fonts */ inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x10); outb(ATC, buf[5] & ~0x01); inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ #if SLOW_VGA #ifndef SC_BAD_FLICKER outb(TSIDX, 0x00); outb(TSREG, 0x01); #endif outb(TSIDX, 0x02); outb(TSREG, 0x04); outb(TSIDX, 0x04); outb(TSREG, 0x07); #ifndef SC_BAD_FLICKER outb(TSIDX, 0x00); outb(TSREG, 0x03); #endif outb(GDCIDX, 0x04); outb(GDCREG, 0x02); outb(GDCIDX, 0x05); outb(GDCREG, 0x00); outb(GDCIDX, 0x06); outb(GDCREG, 0x04); #else #ifndef SC_BAD_FLICKER outw(TSIDX, 0x0100); #endif outw(TSIDX, 0x0402); outw(TSIDX, 0x0704); #ifndef SC_BAD_FLICKER outw(TSIDX, 0x0300); #endif outw(GDCIDX, 0x0204); outw(GDCIDX, 0x0005); outw(GDCIDX, 0x0406); /* addr = a0000, 64kb */ #endif splx(s); } static void set_normal_mode(u_char *buf) { char *modetable; int s = splhigh(); /* setup vga for normal operation mode again */ inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x10); outb(ATC, buf[5]); inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ #if SLOW_VGA #ifndef SC_BAD_FLICKER outb(TSIDX, 0x00); outb(TSREG, 0x01); #endif outb(TSIDX, 0x02); outb(TSREG, buf[0]); outb(TSIDX, 0x04); outb(TSREG, buf[1]); #ifndef SC_BAD_FLICKER outb(TSIDX, 0x00); outb(TSREG, 0x03); #endif outb(GDCIDX, 0x04); outb(GDCREG, buf[2]); outb(GDCIDX, 0x05); outb(GDCREG, buf[3]); if (crtc_addr == MONO_BASE) { outb(GDCIDX, 0x06); outb(GDCREG,(buf[4] & 0x03) | 0x08); } else { outb(GDCIDX, 0x06); outb(GDCREG,(buf[4] & 0x03) | 0x0c); } #else #ifndef SC_BAD_FLICKER outw(TSIDX, 0x0100); #endif outw(TSIDX, 0x0002 | (buf[0] << 8)); outw(TSIDX, 0x0004 | (buf[1] << 8)); #ifndef SC_BAD_FLICKER outw(TSIDX, 0x0300); #endif outw(GDCIDX, 0x0004 | (buf[2] << 8)); outw(GDCIDX, 0x0005 | (buf[3] << 8)); if (crtc_addr == MONO_BASE) outw(GDCIDX, 0x0006 | (((buf[4] & 0x03) | 0x08)<<8)); else outw(GDCIDX, 0x0006 | (((buf[4] & 0x03) | 0x0c)<<8)); #endif font_loading_in_progress = FALSE; splx(s); } void copy_font(int operation, int font_type, char* font_image) { int ch, line, segment, fontsize; u_char buf[PARAM_BUFSIZE]; u_char val; switch (font_type) { default: case FONT_8: segment = 0x8000; fontsize = 8; break; case FONT_14: segment = 0x4000; fontsize = 14; break; case FONT_16: segment = 0x0000; fontsize = 16; break; } /* * FONT KLUDGE * Always use the font page #0. XXX */ segment = 0x0000; outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */ outb(TSIDX, 0x01); outb(TSREG, val | 0x20); set_font_mode(buf); for (ch=0; ch < 256; ch++) for (line=0; line < fontsize; line++) if (operation) *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line) = font_image[(ch*fontsize)+line]; else font_image[(ch*fontsize)+line] = *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line); set_normal_mode(buf); outb(TSIDX, 0x01); outb(TSREG, val & 0xDF); /* enable screen */ } static void set_destructive_cursor(scr_stat *scp) { u_char buf[PARAM_BUFSIZE]; u_char cursor[32]; caddr_t address; int i; char *font_buffer; if (scp->font_size < 14) { font_buffer = font_8; address = (caddr_t)VIDEOMEM + 0x8000; } else if (scp->font_size >= 16) { font_buffer = font_16; address = (caddr_t)VIDEOMEM; } else { font_buffer = font_14; address = (caddr_t)VIDEOMEM + 0x4000; } /* * FONT KLUDGE * Always use the font page #0. XXX */ address = (caddr_t)VIDEOMEM; if (scp->status & MOUSE_VISIBLE) { if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR) bcopy(&scp->mouse_cursor[0], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR + 1) bcopy(&scp->mouse_cursor[32], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR + 2) bcopy(&scp->mouse_cursor[64], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR + 3) bcopy(&scp->mouse_cursor[96], cursor, scp->font_size); else bcopy(font_buffer+((scp->cursor_saveunder & 0xff)*scp->font_size), cursor, scp->font_size); } else bcopy(font_buffer + ((scp->cursor_saveunder & 0xff) * scp->font_size), cursor, scp->font_size); for (i=0; i<32; i++) if ((i >= scp->cursor_start && i <= scp->cursor_end) || (scp->cursor_start >= scp->font_size && i == scp->font_size - 1)) cursor[i] |= 0xff; #if 1 while (!(inb(crtc_addr+6) & 0x08)) /* wait for vertical retrace */ ; #endif set_font_mode(buf); generic_bcopy(cursor, (char *)pa_to_va(address) + DEAD_CHAR * 32, 32); set_normal_mode(buf); } static void set_mouse_pos(scr_stat *scp) { static int last_xpos = -1, last_ypos = -1; if (scp->mouse_xpos < 0) scp->mouse_xpos = 0; if (scp->mouse_ypos < 0) scp->mouse_ypos = 0; if (scp->status & UNKNOWN_MODE) { if (scp->mouse_xpos > scp->xpixel-1) scp->mouse_xpos = scp->xpixel-1; if (scp->mouse_ypos > scp->ypixel-1) scp->mouse_ypos = scp->ypixel-1; return; } if (scp->mouse_xpos > (scp->xsize*8)-1) scp->mouse_xpos = (scp->xsize*8)-1; if (scp->mouse_ypos > (scp->ysize*scp->font_size)-1) scp->mouse_ypos = (scp->ysize*scp->font_size)-1; if (scp->mouse_xpos != last_xpos || scp->mouse_ypos != last_ypos) { scp->status |= MOUSE_MOVED; scp->mouse_pos = scp->scr_buf + ((scp->mouse_ypos/scp->font_size)*scp->xsize + scp->mouse_xpos/8); if ((scp->status & MOUSE_VISIBLE) && (scp->status & MOUSE_CUTTING)) mouse_cut(scp); } } #define isspace(c) (((c) & 0xff) == ' ') static int skip_spc_right(scr_stat *scp, u_short *p) { int i; for (i = (p - scp->scr_buf) % scp->xsize; i < scp->xsize; ++i) { if (!isspace(*p)) break; ++p; } return i; } static int skip_spc_left(scr_stat *scp, u_short *p) { int i; for (i = (p-- - scp->scr_buf) % scp->xsize - 1; i >= 0; --i) { if (!isspace(*p)) break; --p; } return i; } static void mouse_cut(scr_stat *scp) { u_short *end; u_short *p; int i = 0; int j = 0; scp->mouse_cut_end = (scp->mouse_pos >= scp->mouse_cut_start) ? scp->mouse_pos + 1 : scp->mouse_pos; end = (scp->mouse_cut_start > scp->mouse_cut_end) ? scp->mouse_cut_start : scp->mouse_cut_end; for (p = (scp->mouse_cut_start > scp->mouse_cut_end) ? scp->mouse_cut_end : scp->mouse_cut_start; p < end; ++p) { cut_buffer[i] = *p & 0xff; /* remember the position of the last non-space char */ if (!isspace(cut_buffer[i++])) j = i; /* trim trailing blank when crossing lines */ if (((p - scp->scr_buf) % scp->xsize) == (scp->xsize - 1)) { cut_buffer[j++] = '\r'; i = j; } } cut_buffer[i] = '\0'; /* scan towards the end of the last line */ --p; for (i = (p - scp->scr_buf) % scp->xsize; i < scp->xsize; ++i) { if (!isspace(*p)) break; ++p; } /* if there is nothing but blank chars, trim them, but mark towards eol */ if (i >= scp->xsize) { if (scp->mouse_cut_start > scp->mouse_cut_end) scp->mouse_cut_start = p; else scp->mouse_cut_end = p; cut_buffer[j++] = '\r'; cut_buffer[j] = '\0'; } mark_for_update(scp, scp->mouse_cut_start - scp->scr_buf); mark_for_update(scp, scp->mouse_cut_end - scp->scr_buf); } static void mouse_cut_start(scr_stat *scp) { int i; if (scp->status & MOUSE_VISIBLE) { if (scp->mouse_pos == scp->mouse_cut_start && scp->mouse_cut_start == scp->mouse_cut_end - 1) { cut_buffer[0] = '\0'; remove_cutmarking(scp); } else if (skip_spc_right(scp, scp->mouse_pos) >= scp->xsize) { /* if the pointer is on trailing blank chars, mark towards eol */ i = skip_spc_left(scp, scp->mouse_pos) + 1; scp->mouse_cut_start = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize) * scp->xsize + i; scp->mouse_cut_end = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize + 1) * scp->xsize; cut_buffer[0] = '\r'; cut_buffer[1] = '\0'; scp->status |= MOUSE_CUTTING; } else { scp->mouse_cut_start = scp->mouse_pos; scp->mouse_cut_end = scp->mouse_cut_start + 1; cut_buffer[0] = *scp->mouse_cut_start & 0xff; cut_buffer[1] = '\0'; scp->status |= MOUSE_CUTTING; } mark_all(scp); /* delete all other screens cut markings */ for (i=0; istatus & MOUSE_VISIBLE) { scp->status &= ~MOUSE_CUTTING; } } static void mouse_cut_word(scr_stat *scp) { u_short *p; u_short *sol; u_short *eol; int i; /* * Because we don't have locale information in the kernel, * we only distinguish space char and non-space chars. Punctuation * chars, symbols and other regular chars are all treated alike. */ if (scp->status & MOUSE_VISIBLE) { sol = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize) * scp->xsize; eol = sol + scp->xsize; if (isspace(*scp->mouse_pos)) { for (p = scp->mouse_pos; p >= sol; --p) if (!isspace(*p)) break; scp->mouse_cut_start = ++p; for (p = scp->mouse_pos; p < eol; ++p) if (!isspace(*p)) break; scp->mouse_cut_end = p; } else { for (p = scp->mouse_pos; p >= sol; --p) if (isspace(*p)) break; scp->mouse_cut_start = ++p; for (p = scp->mouse_pos; p < eol; ++p) if (isspace(*p)) break; scp->mouse_cut_end = p; } for (i = 0, p = scp->mouse_cut_start; p < scp->mouse_cut_end; ++p) cut_buffer[i++] = *p & 0xff; cut_buffer[i] = '\0'; scp->status |= MOUSE_CUTTING; } } static void mouse_cut_line(scr_stat *scp) { u_short *p; int i; if (scp->status & MOUSE_VISIBLE) { scp->mouse_cut_start = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize) * scp->xsize; scp->mouse_cut_end = scp->mouse_cut_start + scp->xsize; for (i = 0, p = scp->mouse_cut_start; p < scp->mouse_cut_end; ++p) cut_buffer[i++] = *p & 0xff; cut_buffer[i++] = '\r'; cut_buffer[i] = '\0'; scp->status |= MOUSE_CUTTING; } } static void mouse_cut_extend(scr_stat *scp) { if ((scp->status & MOUSE_VISIBLE) && !(scp->status & MOUSE_CUTTING) && (scp->mouse_cut_start != NULL)) { mouse_cut(scp); scp->status |= MOUSE_CUTTING; } } static void mouse_paste(scr_stat *scp) { if (scp->status & MOUSE_VISIBLE) { struct tty *tp; u_char *ptr = cut_buffer; tp = VIRTUAL_TTY(get_scr_num()); while (*ptr) (*linesw[tp->t_line].l_rint)(scr_rmap[*ptr++], tp); } } static void draw_mouse_image(scr_stat *scp) { caddr_t address; int i; char *font_buffer; u_char buf[PARAM_BUFSIZE]; u_short buffer[32]; u_short xoffset, yoffset; u_short *crt_pos = Crtat + (scp->mouse_pos - scp->scr_buf); int font_size = scp->font_size; if (font_size < 14) { font_buffer = font_8; address = (caddr_t)VIDEOMEM + 0x8000; } else if (font_size >= 16) { font_buffer = font_16; address = (caddr_t)VIDEOMEM; } else { font_buffer = font_14; address = (caddr_t)VIDEOMEM + 0x4000; } /* * FONT KLUDGE * Always use the font page #0. XXX */ address = (caddr_t)VIDEOMEM; xoffset = scp->mouse_xpos % 8; yoffset = scp->mouse_ypos % font_size; /* prepare mousepointer char's bitmaps */ bcopy(font_buffer + ((*(scp->mouse_pos) & 0xff) * font_size), &scp->mouse_cursor[0], font_size); bcopy(font_buffer + ((*(scp->mouse_pos+1) & 0xff) * font_size), &scp->mouse_cursor[32], font_size); bcopy(font_buffer + ((*(scp->mouse_pos+scp->xsize) & 0xff) * font_size), &scp->mouse_cursor[64], font_size); bcopy(font_buffer + ((*(scp->mouse_pos+scp->xsize+1) & 0xff) * font_size), &scp->mouse_cursor[96], font_size); for (i=0; imouse_cursor[i]<<8 | scp->mouse_cursor[i+32]; buffer[i+font_size]=scp->mouse_cursor[i+64]<<8|scp->mouse_cursor[i+96]; } /* now and-or in the mousepointer image */ for (i=0; i<16; i++) { buffer[i+yoffset] = ( buffer[i+yoffset] & ~(mouse_and_mask[i] >> xoffset)) | (mouse_or_mask[i] >> xoffset); } for (i=0; imouse_cursor[i] = (buffer[i] & 0xff00) >> 8; scp->mouse_cursor[i+32] = buffer[i] & 0xff; scp->mouse_cursor[i+64] = (buffer[i+font_size] & 0xff00) >> 8; scp->mouse_cursor[i+96] = buffer[i+font_size] & 0xff; } scp->mouse_oldpos = scp->mouse_pos; #if 1 /* wait for vertical retrace to avoid jitter on some videocards */ while (!(inb(crtc_addr+6) & 0x08)) /* idle */ ; #endif set_font_mode(buf); generic_bcopy(scp->mouse_cursor, (char *)pa_to_va(address) + SC_MOUSE_CHAR * 32, 128); set_normal_mode(buf); *(crt_pos) = (*(scp->mouse_pos) & 0xff00) | SC_MOUSE_CHAR; *(crt_pos+scp->xsize) = (*(scp->mouse_pos + scp->xsize) & 0xff00) | (SC_MOUSE_CHAR + 2); if (scp->mouse_xpos < (scp->xsize-1)*8) { *(crt_pos + 1) = (*(scp->mouse_pos + 1) & 0xff00) | (SC_MOUSE_CHAR + 1); *(crt_pos+scp->xsize + 1) = (*(scp->mouse_pos + scp->xsize + 1) & 0xff00) | (SC_MOUSE_CHAR + 3); } mark_for_update(scp, scp->mouse_pos - scp->scr_buf); mark_for_update(scp, scp->mouse_pos + scp->xsize + 1 - scp->scr_buf); } static void remove_mouse_image(scr_stat *scp) { u_short *crt_pos = Crtat + (scp->mouse_oldpos - scp->scr_buf); *(crt_pos) = *(scp->mouse_oldpos); *(crt_pos+1) = *(scp->mouse_oldpos+1); *(crt_pos+scp->xsize) = *(scp->mouse_oldpos+scp->xsize); *(crt_pos+scp->xsize+1) = *(scp->mouse_oldpos+scp->xsize+1); mark_for_update(scp, scp->mouse_oldpos - scp->scr_buf); mark_for_update(scp, scp->mouse_oldpos + scp->xsize + 1 - scp->scr_buf); } static void draw_cutmarking(scr_stat *scp) { u_short *ptr; u_short och, nch; for (ptr=scp->scr_buf; ptr<=(scp->scr_buf+(scp->xsize*scp->ysize)); ptr++) { nch = och = *(Crtat + (ptr - scp->scr_buf)); /* are we outside the selected area ? */ if ( ptr < (scp->mouse_cut_start > scp->mouse_cut_end ? scp->mouse_cut_end : scp->mouse_cut_start) || ptr >= (scp->mouse_cut_start > scp->mouse_cut_end ? scp->mouse_cut_start : scp->mouse_cut_end)) { if (ptr != scp->cursor_pos) nch = (och & 0xff) | (*ptr & 0xff00); } else { /* are we clear of the cursor image ? */ if (ptr != scp->cursor_pos) nch = (och & 0x88ff) | (*ptr & 0x7000)>>4 | (*ptr & 0x0700)<<4; else { if (flags & CHAR_CURSOR) nch = (och & 0x88ff)|(*ptr & 0x7000)>>4|(*ptr & 0x0700)<<4; else if (!(flags & BLINK_CURSOR)) nch = (och & 0xff) | (*ptr & 0xff00); } } if (nch != och) *(Crtat + (ptr - scp->scr_buf)) = nch; } } static void remove_cutmarking(scr_stat *scp) { scp->mouse_cut_start = scp->mouse_cut_end = NULL; scp->status &= ~MOUSE_CUTTING; mark_all(scp); } static void save_palette(void) { int i; outb(PALRADR, 0x00); for (i=0x00; i<0x300; i++) palette[i] = inb(PALDATA); inb(crtc_addr+6); /* reset flip/flop */ } void load_palette(char *palette) { int i; outb(PIXMASK, 0xFF); /* no pixelmask */ outb(PALWADR, 0x00); for (i=0x00; i<0x300; i++) outb(PALDATA, palette[i]); inb(crtc_addr+6); /* reset flip/flop */ outb(ATC, 0x20); /* enable palette */ } static void do_bell(scr_stat *scp, int pitch, int duration) { if (cold || shutdown_in_progress) return; if (scp != cur_console && (flags & QUIET_BELL)) return; if (flags & VISUAL_BELL) { if (blink_in_progress) return; blink_in_progress = 4; if (scp != cur_console) blink_in_progress += 2; blink_screen(cur_console); } else { if (scp != cur_console) pitch *= 2; sysbeep(pitch, duration); } } static void blink_screen(void *arg) { scr_stat *scp = arg; if ((scp->status & UNKNOWN_MODE) || (blink_in_progress <= 1)) { blink_in_progress = FALSE; mark_all(scp); if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } else { if (blink_in_progress & 1) fillw(kernel_default.std_color | scr_map[0x20], Crtat, scp->xsize * scp->ysize); else fillw(kernel_default.rev_color | scr_map[0x20], Crtat, scp->xsize * scp->ysize); blink_in_progress--; timeout(blink_screen, scp, hz / 10); } } void sc_bcopy(scr_stat *scp, u_short *p, int from, int to, int mark) { if (!VESA_MODE(scp->mode)) { generic_bcopy(p+from, Crtat+from, (to-from+1)*sizeof (u_short)); } else if (scp->mode == 0x102) { u_char *d, *e; int i,j; if (mark) mark = 255; d = (u_char *)Crtat; d += 10 + 6*16*100 + (from%80) + 16*100*(from/80); for (i = from ; i <= to ; i++) { e = d; for (j = 0 ; j < 16; j++) { *e = mark^font_16[(p[i]&0xff)*16+j]; e+=100; } d++; if ((i % 80) == 79) d += 20 + 15*100; } } } #ifdef SC_SPLASH_SCREEN static void scsplash_init(void) { /* * We currently assume the splash screen always use * VGA_CG320 mode and abort installation if this mode is not * supported with this video card. XXX */ if (crtc_type != KD_VGA || get_mode_param(cur_console, M_VGA_CG320) == NULL) return; if (splash_load() == 0 && add_scrn_saver(scsplash) == 0) { default_saver = scsplash; scrn_blank_time = DEFAULT_BLANKTIME; run_scrn_saver = TRUE; if (!(boothowto & RB_CONFIG)) { scsplash_stick(TRUE); scsplash(TRUE); } } } static void scsplash(int show) { if (show) splash(TRUE); else if (!sticky_splash) splash(FALSE); } #endif /* SC_SPLASH_SCREEN */ #endif /* NSC */ diff --git a/sys/gnu/i386/isa/dgb.c b/sys/gnu/i386/isa/dgb.c index 9537c12055c5..aac272582aa2 100644 --- a/sys/gnu/i386/isa/dgb.c +++ b/sys/gnu/i386/isa/dgb.c @@ -1,2312 +1,2313 @@ /*- - * dgb.c $Id: dgb.c,v 1.38 1998/08/12 23:44:22 brian Exp $ + * dgb.c $Id: dgb.c,v 1.39 1998/08/16 01:21:49 bde Exp $ * * Digiboard driver. * * Stage 1. "Better than nothing". * Stage 2. "Gee, it works!". * * Based on sio driver by Bruce Evans and on Linux driver by Troy * De Jongh or * which is under GNU General Public License version 2 so this driver * is forced to be under GPL 2 too. * * Written by Serge Babkin, * Joint Stock Commercial Bank "Chelindbank" * (Chelyabinsk, Russia) * babkin@hq.icb.chel.su * * Assorted hacks to make it more functional and working under 3.0-current. * Fixed broken routines to prevent processes hanging on closed (thanks * to Bruce for his patience and assistance). Thanks also to Maxim Bolotin * for his patches which did most of the work to get this * running under 2.2/3.0-current. * Implemented ioctls: TIOCMSDTRWAIT, TIOCMGDTRWAIT, TIOCTIMESTAMP & * TIOCDCDTIMESTAMP. * Sysctl debug flag is now a bitflag, to filter noise during debugging. * David L. Nugent */ #include "opt_compat.h" #include "opt_devfs.h" #include "dgb.h" #if NDGB > 0 /* Helg: i.e.25 times per sec board will be polled */ #define POLLSPERSEC 25 /* How many charactes can we write to input tty rawq */ #define DGB_IBUFSIZE (TTYHOG-100) /* the overall number of ports controlled by this driver */ #ifndef NDGBPORTS # define NDGBPORTS (NDGB*16) #endif #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #include #include #define DGB_DEBUG /* Enable debugging info via sysctl */ #include #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define UNIT_MASK 0x30000 #define PORT_MASK 0x1F #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) (((mynor) & UNIT_MASK)>>16) #define MINOR_TO_PORT(mynor) ((mynor) & PORT_MASK) /* types. XXX - should be elsewhere */ typedef u_char bool_t; /* boolean */ /* digiboard port structure */ struct dgb_p { bool_t status; u_char unit; /* board unit number */ u_char pnum; /* port number */ u_char omodem; /* FEP output modem status */ u_char imodem; /* FEP input modem status */ u_char modemfake; /* Modem values to be forced */ u_char modem; /* Force values */ u_char hflow; u_char dsr; u_char dcd; u_char stopc; u_char startc; u_char stopca; u_char startca; u_char fepstopc; u_char fepstartc; u_char fepstopca; u_char fepstartca; u_char txwin; u_char rxwin; ushort fepiflag; ushort fepcflag; ushort fepoflag; ushort txbufhead; ushort txbufsize; ushort rxbufhead; ushort rxbufsize; int close_delay; int count; int blocked_open; int event; int asyncflags; u_long statusflags; u_char *txptr; u_char *rxptr; volatile struct board_chan *brdchan; struct tty *tty; bool_t active_out; /* nonzero if the callout device is open */ u_int wopeners; /* # processes waiting for DCD in open() */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; /* flags of state, are used in sleep() too */ u_char closing; /* port is being closed now */ u_char draining; /* port is being drained now */ u_char used; /* port is being used now */ u_char mustdrain; /* data must be waited to drain in dgbparam() */ #ifdef DEVFS struct { void *tty; void *ttyi; void *ttyl; void *cua; void *cuai; void *cual; } devfs_token; #endif }; /* Digiboard per-board structure */ struct dgb_softc { /* struct board_info */ u_char status; /* status: DISABLED/ENABLED */ u_char unit; /* unit number */ u_char type; /* type of card: PCXE, PCXI, PCXEVE */ u_char altpin; /* do we need alternate pin setting ? */ int numports; /* number of ports on card */ int port; /* I/O port */ u_char *vmem; /* virtual memory address */ long pmem; /* physical memory address */ int mem_seg; /* internal memory segment */ struct dgb_p *ports; /* pointer to array of port descriptors */ struct tty *ttys; /* pointer to array of TTY structures */ volatile struct global_data *mailbox; }; static struct dgb_softc dgb_softc[NDGB]; static struct dgb_p dgb_ports[NDGBPORTS]; static struct tty dgb_tty[NDGBPORTS]; /* * The public functions in the com module ought to be declared in a com-driver * system header. */ /* Interrupt handling entry points. */ static void dgbpoll __P((void *unit_c)); /* Device switch entry points. */ #define dgbreset noreset #define dgbmmap nommap #define dgbstrategy nostrategy static int dgbattach __P((struct isa_device *dev)); static int dgbprobe __P((struct isa_device *dev)); static void fepcmd(struct dgb_p *port, unsigned cmd, unsigned op1, unsigned op2, unsigned ncmds, unsigned bytecmd); static void dgbstart __P((struct tty *tp)); static int dgbparam __P((struct tty *tp, struct termios *t)); static void dgbhardclose __P((struct dgb_p *port)); static void dgb_drain_or_flush __P((struct dgb_p *port)); static int dgbdrain __P((struct dgb_p *port)); static void dgb_pause __P((void *chan)); static void wakeflush __P((void *p)); static void disc_optim __P((struct tty *tp, struct termios *t)); struct isa_driver dgbdriver = { dgbprobe, dgbattach, "dgb",0 }; static d_open_t dgbopen; static d_close_t dgbclose; static d_read_t dgbread; static d_write_t dgbwrite; static d_ioctl_t dgbioctl; static d_stop_t dgbstop; static d_devtotty_t dgbdevtotty; -#define CDEV_MAJOR 58 -static struct cdevsw dgb_cdevsw = - { dgbopen, dgbclose, dgbread, dgbwrite, /*58*/ - dgbioctl, dgbstop, noreset, dgbdevtotty, /* dgb */ - ttpoll, nommap, NULL, "dgb", NULL, -1 }; +#define CDEV_MAJOR 58 +static struct cdevsw dgb_cdevsw = { + dgbopen, dgbclose, dgbread, dgbwrite, + dgbioctl, dgbstop, noreset, dgbdevtotty, + ttpoll, nommap, NULL, "dgb", + NULL, -1, nodump, nopsize, + D_TTY, +}; static speed_t dgbdefaultrate = TTYDEF_SPEED; static struct speedtab dgbspeedtab[] = { 0, FEP_B0, /* old (sysV-like) Bx codes */ 50, FEP_B50, 75, FEP_B75, 110, FEP_B110, 134, FEP_B134, 150, FEP_B150, 200, FEP_B200, 300, FEP_B300, 600, FEP_B600, 1200, FEP_B1200, 1800, FEP_B1800, 2400, FEP_B2400, 4800, FEP_B4800, 9600, FEP_B9600, 19200, FEP_B19200, 38400, FEP_B38400, 57600, (FEP_FASTBAUD|FEP_B50), /* B50 & fast baud table */ 115200, (FEP_FASTBAUD|FEP_B110), /* B100 & fast baud table */ -1, -1 }; static struct dbgflagtbl { tcflag_t in_mask; tcflag_t in_val; tcflag_t out_val; } dgb_cflags[] = { { PARODD, PARODD, FEP_PARODD }, { PARENB, PARENB, FEP_PARENB }, { CSTOPB, CSTOPB, FEP_CSTOPB }, { CSIZE, CS5, FEP_CS6 }, { CSIZE, CS6, FEP_CS6 }, { CSIZE, CS7, FEP_CS7 }, { CSIZE, CS8, FEP_CS8 }, { CLOCAL, CLOCAL, FEP_CLOCAL }, { (tcflag_t)-1 } }, dgb_iflags[] = { { IGNBRK, IGNBRK, FEP_IGNBRK }, { BRKINT, BRKINT, FEP_BRKINT }, { IGNPAR, IGNPAR, FEP_IGNPAR }, { PARMRK, PARMRK, FEP_PARMRK }, { INPCK, INPCK, FEP_INPCK }, { ISTRIP, ISTRIP, FEP_ISTRIP }, { IXON, IXON, FEP_IXON }, { IXOFF, IXOFF, FEP_IXOFF }, { IXANY, IXANY, FEP_IXANY }, { (tcflag_t)-1 } }, dgb_flow[] = { { CRTSCTS, CRTSCTS, CTS|RTS }, { CRTSCTS, CCTS_OFLOW, CTS }, { CRTSCTS, CRTS_IFLOW, RTS }, { (tcflag_t)-1 } }; /* xlat bsd termios flags to dgb sys-v style */ static tcflag_t dgbflags(struct dbgflagtbl *tbl, tcflag_t input) { tcflag_t output = 0; int i; for (i=0; tbl[i].in_mask != (tcflag_t)-1; i++) { if ((input & tbl[i].in_mask) == tbl[i].in_val) output |= tbl[i].out_val; } return output; } #ifdef DGB_DEBUG static int dgbdebug=0; SYSCTL_INT(_debug, OID_AUTO, dgb_debug, CTLFLAG_RW, &dgbdebug, 0, ""); #endif static __inline int setwin __P((struct dgb_softc *sc, unsigned addr)); static __inline int setinitwin __P((struct dgb_softc *sc, unsigned addr)); static __inline void hidewin __P((struct dgb_softc *sc)); static __inline void towin __P((struct dgb_softc *sc, int win)); /*Helg: to allow recursive dgb...() calls */ typedef struct { /* If we were called and don't want to disturb we need: */ int port; /* write to this port */ u_char data; /* this data on exit */ /* or DATA_WINOFF to close memory window on entry */ } BoardMemWinState; /* so several channels and even boards can coexist */ #define DATA_WINOFF 0 static BoardMemWinState bmws; /* return current memory window state and close window */ static BoardMemWinState bmws_get(void) { BoardMemWinState bmwsRet=bmws; if(bmws.data!=DATA_WINOFF) outb(bmws.port, bmws.data=DATA_WINOFF); return bmwsRet; } /* restore memory window state */ static void bmws_set(BoardMemWinState ws) { if(ws.data != bmws.data || ws.port!=bmws.port ) { if(bmws.data!=DATA_WINOFF) outb(bmws.port,DATA_WINOFF); if(ws.data!=DATA_WINOFF) outb(ws.port, ws.data); bmws=ws; } } static __inline int setwin(sc,addr) struct dgb_softc *sc; unsigned int addr; { if(sc->type==PCXEVE) { outb(bmws.port=sc->port+1, bmws.data=FEPWIN|(addr>>13)); DPRINT3(DB_WIN,"dgb%d: switched to window 0x%x\n",sc->unit,addr>>13); return (addr & 0x1FFF); } else { outb(bmws.port=sc->port,bmws.data=FEPMEM); return addr; } } static __inline int setinitwin(sc,addr) struct dgb_softc *sc; unsigned int addr; { if(sc->type==PCXEVE) { outb(bmws.port=sc->port+1, bmws.data=FEPWIN|(addr>>13)); DPRINT3(DB_WIN,"dgb%d: switched to window 0x%x\n",sc->unit,addr>>13); return (addr & 0x1FFF); } else { outb(bmws.port=sc->port,bmws.data=inb(sc->port)|FEPMEM); return addr; } } static __inline void hidewin(sc) struct dgb_softc *sc; { bmws.data=0; if(sc->type==PCXEVE) outb(bmws.port=sc->port+1, bmws.data); else outb(bmws.port=sc->port, bmws.data); } static __inline void towin(sc,win) struct dgb_softc *sc; int win; { if(sc->type==PCXEVE) { outb(bmws.port=sc->port+1, bmws.data=win); } else { outb(bmws.port=sc->port,bmws.data=FEPMEM); } } static int dgbprobe(dev) struct isa_device *dev; { struct dgb_softc *sc= &dgb_softc[dev->id_unit]; int i, v, t; u_long win_size; /* size of vizible memory window */ u_char *mem; int addr; int unit=dev->id_unit; sc->unit=dev->id_unit; sc->port=dev->id_iobase; if(dev->id_flags & DGBFLAG_ALTPIN) sc->altpin=1; else sc->altpin=0; /* left 24 bits only (ISA address) */ sc->pmem=((intptr_t)(void *)dev->id_maddr & 0xFFFFFF); DPRINT4(DB_INFO,"dgb%d: port 0x%x mem 0x%lx\n",unit,sc->port,sc->pmem); outb(sc->port, FEPRST); sc->status=DISABLED; for(i=0; i< 1000; i++) { DELAY(1); if( (inb(sc->port) & FEPMASK) == FEPRST ) { sc->status=ENABLED; DPRINT3(DB_EXCEPT,"dgb%d: got reset after %d us\n",unit,i); break; } } if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dgb%d: failed to respond\n",dev->id_unit); return 0; } /* check type of card and get internal memory characteristics */ v=inb(sc->port); if( v & 0x1 ) { switch( v&0x30 ) { case 0: sc->mem_seg=0xF000; win_size=0x10000; printf("dgb%d: PC/Xi 64K\n",dev->id_unit); break; case 0x10: sc->mem_seg=0xE000; win_size=0x20000; printf("dgb%d: PC/Xi 128K\n",dev->id_unit); break; case 0x20: sc->mem_seg=0xC000; win_size=0x40000; printf("dgb%d: PC/Xi 256K\n",dev->id_unit); break; default: /* case 0x30: */ sc->mem_seg=0x8000; win_size=0x80000; printf("dgb%d: PC/Xi 512K\n",dev->id_unit); break; } sc->type=PCXI; } else { outb(sc->port, 1); v=inb(sc->port); if( v & 0x1 ) { printf("dgb%d: PC/Xm isn't supported\n",dev->id_unit); sc->status=DISABLED; return 0; } sc->mem_seg=0xF000; if(dev->id_flags==DGBFLAG_NOWIN || ( v&0xC0 )==0) { win_size=0x10000; printf("dgb%d: PC/Xe 64K\n",dev->id_unit); sc->type=PCXE; } else { win_size=0x2000; printf("dgb%d: PC/Xe 64/8K (windowed)\n",dev->id_unit); sc->type=PCXEVE; if((u_long)sc->pmem & ~0xFFE000) { printf("dgb%d: warning: address 0x%lx truncated to 0x%lx\n", dev->id_unit, sc->pmem, sc->pmem & 0xFFE000); dev->id_maddr= (u_char *)(void *)(intptr_t)( sc->pmem & 0xFFE000 ); } } } /* save size of vizible memory segment */ dev->id_msize=win_size; /* map memory */ dev->id_maddr=sc->vmem=pmap_mapdev(sc->pmem,dev->id_msize); outb(sc->port, FEPCLR); /* drop RESET */ hidewin(sc); /* Helg: to set initial bmws state */ return 4; /* we need I/O space of 4 ports */ } static int dgbattach(dev) struct isa_device *dev; { int unit=dev->id_unit; struct dgb_softc *sc= &dgb_softc[dev->id_unit]; int i, t; u_char *mem; u_char *ptr; int addr; struct dgb_p *port; volatile struct board_chan *bc; struct global_data *gd; int shrinkmem; int nfails; ushort *pstat; int lowwater; static int nports=0; #ifdef DEVFS char suffix; #endif if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dbg%d: try to attach a disabled card\n",unit); return 0; } mem=sc->vmem; DPRINT3(DB_INFO,"dgb%d: internal memory segment 0x%x\n",unit,sc->mem_seg); outb(sc->port, FEPRST); DELAY(1); for(i=0; (inb(sc->port) & FEPMASK) != FEPRST ; i++) { if(i>10000) { printf("dgb%d: 1st reset failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } DELAY(1); } DPRINT3(DB_INFO,"dgb%d: got reset after %d us\n",unit,i); /* for PCXEVE set up interrupt and base address */ if(sc->type==PCXEVE) { t=(((u_long)sc->pmem>>8) & 0xFFE0) | 0x10 /* enable windowing */; /* IRQ isn't used */ outb(sc->port+2,t & 0xFF); outb(sc->port+3,t>>8); } else if(sc->type==PCXE) { t=(((u_long)sc->pmem>>8) & 0xFFE0) /* disable windowing */; outb(sc->port+2,t & 0xFF); outb(sc->port+3,t>>8); } if(sc->type==PCXI || sc->type==PCXE) { outb(sc->port, FEPRST|FEPMEM); DELAY(1); for(i=0; (inb(sc->port) & FEPMASK) != (FEPRST|FEPMEM) ; i++) { if(i>10000) { printf("dgb%d: 2nd reset failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } DELAY(1); } DPRINT3(DB_INFO,"dgb%d: got memory after %d us\n",unit,i); } mem=sc->vmem; /* very short memory test */ addr=setinitwin(sc,BOTWIN); *(u_long *)(mem+addr) = 0xA55A3CC3; if(*(u_long *)(mem+addr)!=0xA55A3CC3) { printf("dgb%d: 1st memory test failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } addr=setinitwin(sc,TOPWIN); *(u_long *)(mem+addr) = 0x5AA5C33C; if(*(u_long *)(mem+addr)!=0x5AA5C33C) { printf("dgb%d: 2nd memory test failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } addr=setinitwin(sc,BIOSCODE+((0xF000-sc->mem_seg)<<4)); *(u_long *)(mem+addr) = 0x5AA5C33C; if(*(u_long *)(mem+addr)!=0x5AA5C33C) { printf("dgb%d: 3rd (BIOS) memory test failed\n",dev->id_unit); } addr=setinitwin(sc,MISCGLOBAL); for(i=0; i<16; i++) { mem[addr+i]=0; } if(sc->type==PCXI || sc->type==PCXE) { addr=BIOSCODE+((0xF000-sc->mem_seg)<<4); DPRINT3(DB_INFO,"dgb%d: BIOS local address=0x%x\n",unit,addr); ptr= mem+addr; for(i=0; i=5) { printf("dgb%d: 4th memory test (BIOS load) fails\n",unit); break; } } outb(sc->port,FEPMEM); for(i=0; (inb(sc->port) & FEPMASK) != FEPMEM ; i++) { if(i>10000) { printf("dgb%d: BIOS start failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } DELAY(1); } DPRINT3(DB_INFO,"dgb%d: reset dropped after %d us\n",unit,i); for(i=0; i<200000; i++) { if( *((ushort *)(mem+MISCGLOBAL)) == *((ushort *)"GD") ) goto load_fep; DELAY(1); } printf("dgb%d: BIOS download failed\n",dev->id_unit); DPRINT4(DB_EXCEPT,"dgb%d: code=0x%x must be 0x%x\n", dev->id_unit, *((ushort *)(mem+MISCGLOBAL)), *((ushort *)"GD")); sc->status=DISABLED; hidewin(sc); return 0; } if(sc->type==PCXEVE) { /* set window 7 */ outb(sc->port+1,0xFF); ptr= mem+(BIOSCODE & 0x1FFF); for(i=0; i=5) { printf("dgb%d: 4th memory test (BIOS load) fails\n",unit); break; } } outb(sc->port,FEPCLR); setwin(sc,0); for(i=0; (inb(sc->port) & FEPMASK) != FEPCLR ; i++) { if(i>10000) { printf("dgb%d: BIOS start failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } DELAY(1); } DPRINT3(DB_INFO,"dgb%d: reset dropped after %d us\n",unit,i); addr=setwin(sc,MISCGLOBAL); for(i=0; i<200000; i++) { if(*(ushort *)(mem+addr)== *(ushort *)"GD") goto load_fep; DELAY(1); } printf("dgb%d: BIOS download failed\n",dev->id_unit); DPRINT5(DB_EXCEPT,"dgb%d: Error#(0x%x,0x%x) code=0x%x\n", dev->id_unit, *(ushort *)(mem+0xC12), *(ushort *)(mem+0xC14), *(ushort *)(mem+MISCGLOBAL)); sc->status=DISABLED; hidewin(sc); return 0; } load_fep: DPRINT2(DB_INFO,"dgb%d: BIOS loaded\n",dev->id_unit); addr=setwin(sc,FEPCODE); ptr= mem+addr; for(i=0; imem_seg+FEPCODESEG; *(ushort *)(mem+addr+ 4)=0; *(ushort *)(mem+addr+ 6)=FEPCODESEG; *(ushort *)(mem+addr+ 8)=0; *(ushort *)(mem+addr+10)=pcxx_ncook; outb(sc->port,FEPMEM|FEPINT); /* send interrupt to BIOS */ outb(sc->port,FEPMEM); for(i=0; *(ushort *)(mem+addr)!=0; i++) { if(i>200000) { printf("dgb%d: FEP code download failed\n",unit); DPRINT3(DB_EXCEPT,"dgb%d: code=0x%x must be 0\n", unit, *(ushort *)(mem+addr)); sc->status=DISABLED; hidewin(sc); return 0; } } DPRINT2(DB_INFO,"dgb%d: FEP code loaded\n",unit); *(ushort *)(mem+setwin(sc,FEPSTAT))=0; addr=setwin(sc,MBOX); *(ushort *)(mem+addr+0)=1; *(ushort *)(mem+addr+2)=FEPCODESEG; *(ushort *)(mem+addr+4)=0x4; outb(sc->port,FEPINT); /* send interrupt to BIOS */ outb(sc->port,FEPCLR); addr=setwin(sc,FEPSTAT); for(i=0; *(ushort *)(mem+addr)!= *(ushort *)"OS"; i++) { if(i>200000) { printf("dgb%d: FEP/OS start failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } } DPRINT2(DB_INFO,"dgb%d: FEP/OS started\n",dev->id_unit); sc->numports= *(ushort *)(mem+setwin(sc,NPORT)); printf("dgb%d: %d ports\n",unit,sc->numports); if(sc->numports > MAX_DGB_PORTS) { printf("dgb%d: too many ports\n",unit); sc->status=DISABLED; hidewin(sc); return 0; } if(nports+sc->numports>NDGBPORTS) { printf("dgb%d: only %d ports are usable\n", unit, NDGBPORTS-nports); sc->numports=NDGBPORTS-nports; } /* allocate port and tty structures */ sc->ports=&dgb_ports[nports]; sc->ttys=&dgb_tty[nports]; nports+=sc->numports; addr=setwin(sc,PORTBASE); pstat=(ushort *)(mem+addr); for(i=0; inumports && pstat[i]; i++) if(pstat[i]) sc->ports[i].status=ENABLED; else { sc->ports[i].status=DISABLED; printf("dgb%d: port%d is broken\n", unit, i); } /* We should now init per-port structures */ bc=(volatile struct board_chan *)(mem + CHANSTRUCT); sc->mailbox=(volatile struct global_data *)(mem + FEP_GLOBAL); if(sc->numports<3) shrinkmem=1; else shrinkmem=0; for(i=0; inumports; i++, bc++) { port= &sc->ports[i]; port->tty=&sc->ttys[i]; port->unit=unit; port->brdchan=bc; if(sc->altpin) { port->dsr=CD; port->dcd=DSR; } else { port->dcd=CD; port->dsr=DSR; } port->pnum=i; if(shrinkmem) { DPRINT2(DB_INFO,"dgb%d: shrinking memory\n",unit); fepcmd(port, SETBUFFER, 32, 0, 0, 0); shrinkmem=0; } if(sc->type!=PCXEVE) { port->txptr=mem+((bc->tseg-sc->mem_seg)<<4); port->rxptr=mem+((bc->rseg-sc->mem_seg)<<4); port->txwin=port->rxwin=0; } else { port->txptr=mem+( ((bc->tseg-sc->mem_seg)<<4) & 0x1FFF ); port->rxptr=mem+( ((bc->rseg-sc->mem_seg)<<4) & 0x1FFF ); port->txwin=FEPWIN | ((bc->tseg-sc->mem_seg)>>9); port->rxwin=FEPWIN | ((bc->rseg-sc->mem_seg)>>9); } port->txbufhead=0; port->rxbufhead=0; port->txbufsize=bc->tmax+1; port->rxbufsize=bc->rmax+1; lowwater= (port->txbufsize>=2000) ? 1024 : (port->txbufsize/2); setwin(sc,0); fepcmd(port, STXLWATER, lowwater, 0, 10, 0); fepcmd(port, SRXLWATER, port->rxbufsize/4, 0, 10, 0); fepcmd(port, SRXHWATER, 3*port->rxbufsize/4, 0, 10, 0); bc->edelay=100; bc->idata=1; port->startc=bc->startc; port->startca=bc->startca; port->stopc=bc->stopc; port->stopca=bc->stopca; /*port->close_delay=50;*/ port->close_delay=3 * hz; port->do_timestamp=0; port->do_dcd_timestamp=0; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ port->it_in.c_iflag = TTYDEF_IFLAG; port->it_in.c_oflag = TTYDEF_OFLAG; port->it_in.c_cflag = TTYDEF_CFLAG; port->it_in.c_lflag = TTYDEF_LFLAG; termioschars(&port->it_in); port->it_in.c_ispeed = port->it_in.c_ospeed = dgbdefaultrate; port->it_out = port->it_in; #ifdef DEVFS /* MAX_DGB_PORTS is 32 => [0-9a-v] */ suffix = i < 10 ? '0' + i : 'a' + i - 10; port->devfs_token.tty = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyD%d%c", unit, suffix); port->devfs_token.ttyi = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i+32, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyiD%d%c", unit, suffix); port->devfs_token.ttyl = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i+64, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttylD%d%c", unit, suffix); port->devfs_token.cua = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i+128, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaD%d%c", unit, suffix); port->devfs_token.cuai = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i+160, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaiD%d%c", unit, suffix); port->devfs_token.cual = devfs_add_devswf(&dgb_cdevsw, (unit*32)+i+192, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cualD%d%c", unit, suffix); #endif } hidewin(sc); /* register the polling function */ timeout(dgbpoll, (void *)unit, hz/POLLSPERSEC); return 1; } /* ARGSUSED */ static int dgbopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct dgb_softc *sc; struct tty *tp; int unit; int mynor; int pnum; struct dgb_p *port; int s,cs; int error; volatile struct board_chan *bc; error=0; mynor=minor(dev); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); if(unit >= NDGB) { DPRINT2(DB_EXCEPT,"dgb%d: try to open a nonexisting card\n",unit); return ENXIO; } sc=&dgb_softc[unit]; if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dgb%d: try to open a disabled card\n",unit); return ENXIO; } if(pnum>=sc->numports) { DPRINT3(DB_EXCEPT,"dgb%d: try to open non-existing port %d\n",unit,pnum); return ENXIO; } if(mynor & CONTROL_MASK) return 0; tp=&sc->ttys[pnum]; port=&sc->ports[pnum]; bc=port->brdchan; open_top: s=spltty(); while(port->closing) { error=tsleep(&port->closing, TTOPRI|PCATCH, "dgocl", 0); if(error) { DPRINT4(DB_OPEN,"dgb%d: port%d: tsleep(dgocl) error=%d\n",unit,pnum,error); goto out; } } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!port->active_out) { error = EBUSY; DPRINT4(DB_OPEN,"dgb%d: port%d: BUSY error=%d\n",unit,pnum,error); goto out; } } else { if (port->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; DPRINT4(DB_OPEN,"dgb%d: port%d: BUSY error=%d\n",unit,pnum,error); goto out; } error = tsleep(&port->active_out, TTIPRI | PCATCH, "dgbi", 0); if (error != 0) { DPRINT4(DB_OPEN,"dgb%d: port%d: tsleep(dgbi) error=%d\n", unit,pnum,error); goto out; } splx(s); goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc=dgbstart; tp->t_param=dgbparam; tp->t_dev=dev; tp->t_termios= (mynor & CALLOUT_MASK) ? port->it_out : port->it_in; cs=splclock(); setwin(sc,0); port->imodem=bc->mstat; bc->rout=bc->rin; /* clear input queue */ bc->idata=1; #ifdef PRINT_BUFSIZE printf("dgb buffers tx=%x:%x rx=%x:%x\n",bc->tseg,bc->tmax,bc->rseg,bc->rmax); #endif hidewin(sc); splx(cs); port->wopeners++; error=dgbparam(tp, &tp->t_termios); port->wopeners--; if(error!=0) { DPRINT4(DB_OPEN,"dgb%d: port%d: dgbparam error=%d\n",unit,pnum,error); goto out; } - ttsetwater(tp); - /* handle fake DCD for callout devices */ /* and initial DCD */ if( (port->imodem & port->dcd) || mynor & CALLOUT_MASK ) linesw[tp->t_line].l_modem(tp,1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++port->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "dgdcd", 0); --port->wopeners; if (error != 0) { DPRINT4(DB_OPEN,"dgb%d: port%d: tsleep(dgdcd) error=%d\n",unit,pnum,error); goto out; } splx(s); goto open_top; } error = linesw[tp->t_line].l_open(dev, tp); disc_optim(tp,&tp->t_termios); DPRINT4(DB_OPEN,"dgb%d: port%d: l_open error=%d\n",unit,pnum,error); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) port->active_out = TRUE; port->used=1; /* If any port is open (i.e. the open() call is completed for it) * the device is busy */ out: disc_optim(tp,&tp->t_termios); splx(s); if( !(tp->t_state & TS_ISOPEN) && port->wopeners==0 ) dgbhardclose(port); DPRINT4(DB_OPEN,"dgb%d: port%d: open() returns %d\n",unit,pnum,error); return error; } /*ARGSUSED*/ static int dgbclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { int mynor; struct tty *tp; int unit, pnum; struct dgb_softc *sc; struct dgb_p *port; int s; int i; mynor=minor(dev); if(mynor & CONTROL_MASK) return 0; unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); sc=&dgb_softc[unit]; tp=&sc->ttys[pnum]; port=sc->ports+pnum; DPRINT3(DB_CLOSE,"dgb%d: port%d: closing\n",unit,pnum); DPRINT3(DB_CLOSE,"dgb%d: port%d: draining port\n",unit,pnum); dgb_drain_or_flush(port); s=spltty(); port->closing=1; DPRINT3(DB_CLOSE,"dgb%d: port%d: closing line disc\n",unit,pnum); linesw[tp->t_line].l_close(tp,flag); disc_optim(tp,&tp->t_termios); DPRINT3(DB_CLOSE,"dgb%d: port%d: hard closing\n",unit,pnum); dgbhardclose(port); DPRINT3(DB_CLOSE,"dgb%d: port%d: closing tty\n",unit,pnum); ttyclose(tp); port->closing=0; wakeup(&port->closing); port->used=0; /* mark the card idle when all ports are closed */ for(i=0; inumports; i++) if(sc->ports[i].used) break; splx(s); DPRINT3(DB_CLOSE,"dgb%d: port%d: closed\n",unit,pnum); wakeup(TSA_CARR_ON(tp)); wakeup(&port->active_out); port->active_out=0; DPRINT3(DB_CLOSE,"dgb%d: port%d: close exit\n",unit,pnum); return 0; } static void dgbhardclose(port) struct dgb_p *port; { struct dgb_softc *sc=&dgb_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int cs; cs=splclock(); port->do_timestamp = 0; setwin(sc,0); bc->idata=0; bc->iempty=0; bc->ilow=0; if(port->tty->t_cflag & HUPCL) { port->omodem &= ~(RTS|DTR); fepcmd(port, SETMODEM, 0, DTR|RTS, 0, 1); } hidewin(sc); splx(cs); timeout(dgb_pause, &port->brdchan, hz/2); tsleep(&port->brdchan, TTIPRI | PCATCH, "dgclo", 0); } static void dgb_pause(chan) void *chan; { wakeup((caddr_t)chan); } static int dgbread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int error, unit, pnum; mynor=minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); tp=&dgb_softc[unit].ttys[pnum]; error=linesw[tp->t_line].l_read(tp, uio, flag); DPRINT4(DB_RD,"dgb%d: port%d: read() returns %d\n",unit,pnum,error); return error; } static int dgbwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int error, unit, pnum; mynor=minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); tp=&dgb_softc[unit].ttys[pnum]; error=linesw[tp->t_line].l_write(tp, uio, flag); DPRINT4(DB_WR,"dgb%d: port%d: write() returns %d\n",unit,pnum,error); return error; } static void dgbpoll(unit_c) void *unit_c; { int unit=(int)unit_c; int pnum; struct dgb_p *port; struct dgb_softc *sc=&dgb_softc[unit]; int head, tail; u_char *eventbuf; int event, mstat, lstat; volatile struct board_chan *bc; struct tty *tp; int rhead, rtail; int whead, wtail; int size; int c=0; u_char *ptr; int ocount; int ibuf_full,obuf_full; BoardMemWinState ws=bmws_get(); if(sc->status==DISABLED) { printf("dgb%d: polling of disabled board stopped\n",unit); return; } setwin(sc,0); head=sc->mailbox->ein; tail=sc->mailbox->eout; while(head!=tail) { if(head >= FEP_IMAX-FEP_ISTART || tail >= FEP_IMAX-FEP_ISTART || (head|tail) & 03 ) { printf("dgb%d: event queue's head or tail is wrong! hd=%d,tl=%d\n", unit,head,tail); break; } eventbuf=sc->vmem+tail+FEP_ISTART; pnum=eventbuf[0]; event=eventbuf[1]; mstat=eventbuf[2]; lstat=eventbuf[3]; port=&sc->ports[pnum]; bc=port->brdchan; tp=&sc->ttys[pnum]; if(pnum>=sc->numports || port->status==DISABLED) { printf("dgb%d: port%d: got event on nonexisting port\n",unit,pnum); } else if(port->used || port->wopeners>0 ) { int wrapmask=port->rxbufsize-1; if( !(event & ALL_IND) ) printf("dgb%d: port%d: ? event 0x%x mstat 0x%x lstat 0x%x\n", unit, pnum, event, mstat, lstat); if(event & DATA_IND) { DPRINT3(DB_DATA,"dgb%d: port%d: DATA_IND\n",unit,pnum); rhead=bc->rin & wrapmask; rtail=bc->rout & wrapmask; if( !(tp->t_cflag & CREAD) || !port->used ) { bc->rout=rhead; goto end_of_data; } if(bc->orun) { printf("dgb%d: port%d: overrun\n", unit, pnum); bc->orun=0; } if(!(tp->t_state & TS_ISOPEN)) goto end_of_data; for(ibuf_full=FALSE;rhead!=rtail && !ibuf_full;) { DPRINT5(DB_RXDATA,"dgb%d: port%d: p rx head=%d tail=%d\n", unit,pnum,rhead,rtail); if(rhead>rtail) size=rhead-rtail; else size=port->rxbufsize-rtail; ptr=port->rxptr+rtail; /* Helg: */ if( tp->t_rawq.c_cc + size > DGB_IBUFSIZE ) { size=DGB_IBUFSIZE-tp->t_rawq.c_cc; DPRINT1(DB_RXDATA,"*"); ibuf_full=TRUE; } if(size) { if (tp->t_state & TS_CAN_BYPASS_L_RINT) { DPRINT1(DB_RXDATA,"!"); towin(sc,port->rxwin); tk_nin += size; tk_rawcc += size; tp->t_rawcc += size; b_to_q(ptr,size,&tp->t_rawq); setwin(sc,0); } else { int i=size; unsigned char chr; do { towin(sc,port->rxwin); chr= *ptr++; hidewin(sc); (*linesw[tp->t_line].l_rint)(chr, tp); } while (--i > 0 ); setwin(sc,0); } } rtail= (rtail + size) & wrapmask; bc->rout=rtail; rhead=bc->rin & wrapmask; hidewin(sc); ttwakeup(tp); setwin(sc,0); } end_of_data: ; } if(event & MODEMCHG_IND) { DPRINT3(DB_MODEM,"dgb%d: port%d: MODEMCHG_IND\n",unit,pnum); port->imodem=mstat; if(mstat & port->dcd) { hidewin(sc); linesw[tp->t_line].l_modem(tp,1); setwin(sc,0); wakeup(TSA_CARR_ON(tp)); } else { hidewin(sc); linesw[tp->t_line].l_modem(tp,0); setwin(sc,0); if( port->draining) { port->draining=0; wakeup(&port->draining); } } } if(event & BREAK_IND) { if((tp->t_state & TS_ISOPEN) && (tp->t_iflag & IGNBRK)) { DPRINT3(DB_BREAK,"dgb%d: port%d: BREAK_IND\n",unit,pnum); hidewin(sc); linesw[tp->t_line].l_rint(TTY_BI, tp); setwin(sc,0); } } /* Helg: with output flow control */ if(event & (LOWTX_IND | EMPTYTX_IND) ) { DPRINT3(DB_TXDATA,"dgb%d: port%d: LOWTX_IND or EMPTYTX_IND\n",unit,pnum); if( (event & EMPTYTX_IND ) && tp->t_outq.c_cc==0 && port->draining) { port->draining=0; wakeup(&port->draining); bc->ilow=0; bc->iempty=0; } else { int wrapmask=port->txbufsize-1; for(obuf_full=FALSE; tp->t_outq.c_cc!=0 && !obuf_full; ) { int s; /* add "last-minute" data to write buffer */ if(!(tp->t_state & TS_BUSY)) { hidewin(sc); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ ttwwakeup(tp); #else if(tp->t_outq.c_cc <= tp->t_lowat) { if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } /* selwakeup(&tp->t_wsel); */ } #endif setwin(sc,0); } s=spltty(); whead=bc->tin & wrapmask; wtail=bc->tout & wrapmask; if(wheadtxbufsize-whead; if(wtail==0) size--; } if(size==0) { DPRINT5(DB_WR,"dgb: head=%d tail=%d size=%d full=%d\n", whead,wtail,size,obuf_full); bc->iempty=1; bc->ilow=1; obuf_full=TRUE; splx(s); break; } towin(sc,port->txwin); ocount=q_to_b(&tp->t_outq, port->txptr+whead, size); whead+=ocount; setwin(sc,0); bc->tin=whead; bc->tin=whead & wrapmask; splx(s); } if(obuf_full) { DPRINT1(DB_WR," +BUSY\n"); tp->t_state|=TS_BUSY; } else { DPRINT1(DB_WR," -BUSY\n"); hidewin(sc); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ /* should clear TS_BUSY before ttwwakeup */ if(tp->t_state & TS_BUSY) { tp->t_state &= ~TS_BUSY; linesw[tp->t_line].l_start(tp); ttwwakeup(tp); } #else if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } tp->t_state &= ~TS_BUSY; #endif setwin(sc,0); } } end_of_buffer: ; } bc->idata=1; /* require event on incoming data */ } else { bc=port->brdchan; DPRINT4(DB_EXCEPT,"dgb%d: port%d: got event 0x%x on closed port\n", unit,pnum,event); bc->rout=bc->rin; bc->idata=bc->iempty=bc->ilow=0; } tail= (tail+4) & (FEP_IMAX-FEP_ISTART-4); } sc->mailbox->eout=tail; bmws_set(ws); timeout(dgbpoll, unit_c, hz/POLLSPERSEC); } static int dgbioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct dgb_softc *sc; int unit, pnum; struct dgb_p *port; int mynor; struct tty *tp; volatile struct board_chan *bc; int error; int s,cs; int tiocm_xxx; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) u_long oldcmd; struct termios term; #endif BoardMemWinState ws=bmws_get(); mynor=minor(dev); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); sc=&dgb_softc[unit]; port=&sc->ports[pnum]; tp=&sc->ttys[pnum]; bc=port->brdchan; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &port->it_out : &port->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { DPRINT6(DB_PARAM,"dgb%d: port%d: dgbioctl-ISNOW c=0x%x i=0x%x l=0x%x\n",unit,pnum,term.c_cflag,term.c_iflag,term.c_lflag); } oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in; DPRINT6(DB_PARAM,"dgb%d: port%d: dgbioctl-TOSET c=0x%x i=0x%x l=0x%x\n",unit,pnum,dt->c_cflag,dt->c_iflag,dt->c_lflag); dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } if(cmd==TIOCSTOP) { cs=splclock(); setwin(sc,0); fepcmd(port, PAUSETX, 0, 0, 0, 0); bmws_set(ws); splx(cs); return 0; } else if(cmd==TIOCSTART) { cs=splclock(); setwin(sc,0); fepcmd(port, RESUMETX, 0, 0, 0, 0); bmws_set(ws); splx(cs); return 0; } if(cmd==TIOCSETAW || cmd==TIOCSETAF) port->mustdrain=1; error = linesw[tp->t_line].l_ioctl(tp, cmd, data, flag, p); if (error != ENOIOCTL) return error; s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp,&tp->t_termios); port->mustdrain=0; if (error != ENOIOCTL) { splx(s); if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { DPRINT6(DB_PARAM,"dgb%d: port%d: dgbioctl-RES c=0x%x i=0x%x l=0x%x\n",unit,pnum,tp->t_cflag,tp->t_iflag,tp->t_lflag); } return error; } switch (cmd) { case TIOCSBRK: /* Helg: commented */ /* error=dgbdrain(port);*/ if(error!=0) { splx(s); return error; } cs=splclock(); setwin(sc,0); /* now it sends 250 millisecond break because I don't know */ /* how to send an infinite break */ fepcmd(port, SENDBREAK, 250, 0, 10, 0); hidewin(sc); splx(cs); break; case TIOCCBRK: /* now it's empty */ break; case TIOCSDTR: DPRINT3(DB_MODEM,"dgb%d: port%d: set DTR\n",unit,pnum); port->omodem |= DTR; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS, 0, 1); if( !(bc->mstat & DTR) ) { DPRINT3(DB_MODEM,"dgb%d: port%d: DTR is off\n",unit,pnum); } hidewin(sc); splx(cs); break; case TIOCCDTR: DPRINT3(DB_MODEM,"dgb%d: port%d: reset DTR\n",unit,pnum); port->omodem &= ~DTR; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); if( bc->mstat & DTR ) { DPRINT3(DB_MODEM,"dgb%d: port%d: DTR is on\n",unit,pnum); } hidewin(sc); splx(cs); break; case TIOCMSET: if(*(int *)data & TIOCM_DTR) port->omodem |=DTR; else port->omodem &=~DTR; if(*(int *)data & TIOCM_RTS) port->omodem |=RTS; else port->omodem &=~RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMBIS: if(*(int *)data & TIOCM_DTR) port->omodem |=DTR; if(*(int *)data & TIOCM_RTS) port->omodem |=RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMBIC: if(*(int *)data & TIOCM_DTR) port->omodem &=~DTR; if(*(int *)data & TIOCM_RTS) port->omodem &=~RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMGET: setwin(sc,0); port->imodem=bc->mstat; hidewin(sc); tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */ DPRINT3(DB_MODEM,"dgb%d: port%d: modem stat -- ",unit,pnum); if (port->imodem & DTR) { DPRINT1(DB_MODEM,"DTR "); tiocm_xxx |= TIOCM_DTR; } if (port->imodem & RTS) { DPRINT1(DB_MODEM,"RTS "); tiocm_xxx |= TIOCM_RTS; } if (port->imodem & CTS) { DPRINT1(DB_MODEM,"CTS "); tiocm_xxx |= TIOCM_CTS; } if (port->imodem & port->dcd) { DPRINT1(DB_MODEM,"DCD "); tiocm_xxx |= TIOCM_CD; } if (port->imodem & port->dsr) { DPRINT1(DB_MODEM,"DSR "); tiocm_xxx |= TIOCM_DSR; } if (port->imodem & RI) { DPRINT1(DB_MODEM,"RI "); tiocm_xxx |= TIOCM_RI; } *(int *)data = tiocm_xxx; DPRINT1(DB_MODEM,"--\n"); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } port->close_delay = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = port->close_delay * 100 / hz; break; case TIOCTIMESTAMP: port->do_timestamp = TRUE; *(struct timeval *)data = port->timestamp; break; case TIOCDCDTIMESTAMP: port->do_dcd_timestamp = TRUE; *(struct timeval *)data = port->dcd_timestamp; break; default: bmws_set(ws); splx(s); return ENOTTY; } bmws_set(ws); splx(s); return 0; } static void wakeflush(p) void *p; { struct dgb_p *port=p; wakeup(&port->draining); } /* wait for the output to drain */ static int dgbdrain(port) struct dgb_p *port; { struct dgb_softc *sc=&dgb_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int error; int head, tail; BoardMemWinState ws=bmws_get(); setwin(sc,0); bc->iempty=1; tail=bc->tout; head=bc->tin; while(tail!=head) { DPRINT5(DB_WR,"dgb%d: port%d: drain: head=%d tail=%d\n", port->unit, port->pnum, head, tail); hidewin(sc); port->draining=1; timeout(wakeflush,port, hz); error=tsleep(&port->draining, TTIPRI | PCATCH, "dgdrn", 0); port->draining=0; setwin(sc,0); if (error != 0) { DPRINT4(DB_WR,"dgb%d: port%d: tsleep(dgdrn) error=%d\n", port->unit,port->pnum,error); bc->iempty=0; bmws_set(ws); return error; } tail=bc->tout; head=bc->tin; } DPRINT5(DB_WR,"dgb%d: port%d: drain: head=%d tail=%d\n", port->unit, port->pnum, head, tail); bmws_set(ws); return 0; } /* wait for the output to drain */ /* or simply clear the buffer it it's stopped */ static void dgb_drain_or_flush(port) struct dgb_p *port; { struct tty *tp=port->tty; struct dgb_softc *sc=&dgb_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int error; int lasttail; int head, tail; setwin(sc,0); lasttail=-1; bc->iempty=1; tail=bc->tout; head=bc->tin; while(tail!=head /* && tail!=lasttail */ ) { DPRINT5(DB_WR,"dgb%d: port%d: flush: head=%d tail=%d\n", port->unit, port->pnum, head, tail); /* if there is no carrier simply clean the buffer */ if( !(tp->t_state & TS_CARR_ON) ) { bc->tout=bc->tin=0; bc->iempty=0; hidewin(sc); return; } hidewin(sc); port->draining=1; timeout(wakeflush,port, hz); error=tsleep(&port->draining, TTIPRI | PCATCH, "dgfls", 0); port->draining=0; setwin(sc,0); if (error != 0) { DPRINT4(DB_WR,"dgb%d: port%d: tsleep(dgfls) error=%d\n", port->unit,port->pnum,error); /* silently clean the buffer */ bc->tout=bc->tin=0; bc->iempty=0; hidewin(sc); return; } lasttail=tail; tail=bc->tout; head=bc->tin; } hidewin(sc); DPRINT5(DB_WR,"dgb%d: port%d: flush: head=%d tail=%d\n", port->unit, port->pnum, head, tail); } static int dgbparam(tp, t) struct tty *tp; struct termios *t; { int dev=tp->t_dev; int mynor=minor(dev); int unit=MINOR_TO_UNIT(dev); int pnum=MINOR_TO_PORT(dev); struct dgb_softc *sc=&dgb_softc[unit]; struct dgb_p *port=&sc->ports[pnum]; volatile struct board_chan *bc=port->brdchan; int cflag; int head; int mval; int iflag; int hflow; int s,cs; BoardMemWinState ws=bmws_get(); DPRINT6(DB_PARAM,"dgb%d: port%d: dgbparm c=0x%x i=0x%x l=0x%x\n",unit,pnum,t->c_cflag,t->c_iflag,t->c_lflag); if(port->mustdrain) { DPRINT3(DB_PARAM,"dgb%d: port%d: must call dgbdrain()\n",unit,pnum); dgbdrain(port); } cflag=ttspeedtab(t->c_ospeed, dgbspeedtab); if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; if (cflag < 0 /* || cflag > 0 && t->c_ispeed != t->c_ospeed */) { DPRINT4(DB_PARAM,"dgb%d: port%d: invalid cflag=0%o\n",unit,pnum,cflag); return (EINVAL); } cs=splclock(); setwin(sc,0); if(cflag==0) { /* hangup */ DPRINT3(DB_PARAM,"dgb%d: port%d: hangup\n",unit,pnum); head=bc->rin; bc->rout=head; head=bc->tin; fepcmd(port, STOUT, (unsigned)head, 0, 0, 0); mval= port->omodem & ~(DTR|RTS); } else { cflag |= dgbflags(dgb_cflags, t->c_cflag); if(cflag!=port->fepcflag) { port->fepcflag=cflag; DPRINT5(DB_PARAM,"dgb%d: port%d: set cflag=0x%x c=0x%x\n", unit,pnum,cflag,t->c_cflag&~CRTSCTS); fepcmd(port, SETCTRLFLAGS, (unsigned)cflag, 0, 0, 0); } mval= port->omodem | (DTR|RTS); } iflag=dgbflags(dgb_iflags, t->c_iflag); if(iflag!=port->fepiflag) { port->fepiflag=iflag; DPRINT5(DB_PARAM,"dgb%d: port%d: set iflag=0x%x c=0x%x\n",unit,pnum,iflag,t->c_iflag); fepcmd(port, SETIFLAGS, (unsigned)iflag, 0, 0, 0); } bc->mint=port->dcd; hflow=dgbflags(dgb_flow, t->c_cflag); if(hflow!=port->hflow) { port->hflow=hflow; DPRINT5(DB_PARAM,"dgb%d: port%d: set hflow=0x%x f=0x%x\n",unit,pnum,hflow,t->c_cflag&CRTSCTS); fepcmd(port, SETHFLOW, (unsigned)hflow, 0xff, 0, 1); } if(port->omodem != mval) { DPRINT5(DB_PARAM,"dgb%d: port%d: setting modem parameters 0x%x was 0x%x\n", unit,pnum,mval,port->omodem); port->omodem=mval; fepcmd(port, SETMODEM, (unsigned)mval, RTS|DTR, 0, 1); } if(port->fepstartc!=t->c_cc[VSTART] || port->fepstopc!=t->c_cc[VSTOP]) { DPRINT5(DB_PARAM,"dgb%d: port%d: set startc=%d, stopc=%d\n",unit,pnum,t->c_cc[VSTART],t->c_cc[VSTOP]); port->fepstartc=t->c_cc[VSTART]; port->fepstopc=t->c_cc[VSTOP]; fepcmd(port, SONOFFC, port->fepstartc, port->fepstopc, 0, 1); } bmws_set(ws); splx(cs); return 0; } static void dgbstart(tp) struct tty *tp; { int unit; int pnum; struct dgb_p *port; struct dgb_softc *sc; volatile struct board_chan *bc; int head, tail; int size, ocount; int s; int wmask; BoardMemWinState ws=bmws_get(); unit=MINOR_TO_UNIT(minor(tp->t_dev)); pnum=MINOR_TO_PORT(minor(tp->t_dev)); sc=&dgb_softc[unit]; port=&sc->ports[pnum]; bc=port->brdchan; wmask=port->txbufsize-1; s=spltty(); while( tp->t_outq.c_cc!=0 ) { int cs; #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ ttwwakeup(tp); #else if(tp->t_outq.c_cc <= tp->t_lowat) { if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } /*selwakeup(&tp->t_wsel);*/ } #endif cs=splclock(); setwin(sc,0); head=bc->tin & wmask; do { tail=bc->tout; } while (tail != bc->tout); tail=bc->tout & wmask; DPRINT5(DB_WR,"dgb%d: port%d: s tx head=%d tail=%d\n",unit,pnum,head,tail); #ifdef LEAVE_FREE_CHARS if(tail>head) { size=tail-head-LEAVE_FREE_CHARS; if (size <0) size==0; } else { size=port->txbufsize-head; if(tail+port->txbufsize < head) size==0; } } #else if(tail>head) size=tail-head-1; else { size=port->txbufsize-head/*-1*/; if(tail==0) size--; } #endif if(size==0) { bc->iempty=1; bc->ilow=1; splx(cs); bmws_set(ws); tp->t_state|=TS_BUSY; splx(s); return; } towin(sc,port->txwin); ocount=q_to_b(&tp->t_outq, port->txptr+head, size); head+=ocount; if(head>=port->txbufsize) head-=port->txbufsize; setwin(sc,0); bc->tin=head; DPRINT5(DB_WR,"dgb%d: port%d: tx avail=%d count=%d\n",unit,pnum,size,ocount); hidewin(sc); splx(cs); } bmws_set(ws); splx(s); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ if(tp->t_state & TS_BUSY) { tp->t_state&=~TS_BUSY; linesw[tp->t_line].l_start(tp); ttwwakeup(tp); } #else if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } tp->t_state&=~TS_BUSY; #endif } void dgbstop(tp, rw) struct tty *tp; int rw; { int unit; int pnum; struct dgb_p *port; struct dgb_softc *sc; volatile struct board_chan *bc; int head; int s; BoardMemWinState ws=bmws_get(); unit=MINOR_TO_UNIT(minor(tp->t_dev)); pnum=MINOR_TO_PORT(minor(tp->t_dev)); sc=&dgb_softc[unit]; port=&sc->ports[pnum]; bc=port->brdchan; DPRINT3(DB_WR,"dgb%d: port%d: stop\n",port->unit, port->pnum); s = spltty(); setwin(sc,0); if (rw & FWRITE) { /* clear output queue */ bc->tout=bc->tin=0; bc->ilow=0;bc->iempty=0; } if (rw & FREAD) { /* clear input queue */ bc->rout=bc->rin; bc->idata=1; } hidewin(sc); bmws_set(ws); splx(s); dgbstart(tp); } struct tty * dgbdevtotty(dev) dev_t dev; { int mynor, pnum, unit; struct dgb_softc *sc; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NDGB) return (NULL); pnum = MINOR_TO_PORT(mynor); sc = &dgb_softc[unit]; if (pnum >= sc->numports) return (NULL); return (&sc->ttys[pnum]); } static void fepcmd(port, cmd, op1, op2, ncmds, bytecmd) struct dgb_p *port; unsigned cmd, op1, op2, ncmds, bytecmd; { struct dgb_softc *sc=&dgb_softc[port->unit]; u_char *mem=sc->vmem; unsigned tail, head; int count, n; if(port->status==DISABLED) { printf("dgb%d: port%d: FEP command on disabled port\n", port->unit, port->pnum); return; } /* setwin(sc,0); Require this to be set by caller */ head=sc->mailbox->cin; if(head>=(FEP_CMAX-FEP_CSTART) || (head & 3)) { printf("dgb%d: port%d: wrong pointer head of command queue : 0x%x\n", port->unit, port->pnum, head); return; } mem[head+FEP_CSTART+0]=cmd; mem[head+FEP_CSTART+1]=port->pnum; if(bytecmd) { mem[head+FEP_CSTART+2]=op1; mem[head+FEP_CSTART+3]=op2; } else { mem[head+FEP_CSTART+2]=op1&0xff; mem[head+FEP_CSTART+3]=(op1>>8)&0xff; } DPRINT7(DB_FEP,"dgb%d: port%d: %s cmd=0x%x op1=0x%x op2=0x%x\n", port->unit, port->pnum, (bytecmd)?"byte":"word", cmd, mem[head+FEP_CSTART+2], mem[head+FEP_CSTART+3]); head=(head+4) & (FEP_CMAX-FEP_CSTART-4); sc->mailbox->cin=head; count=FEPTIMEOUT; while (count-- != 0) { head=sc->mailbox->cin; tail=sc->mailbox->cout; n = (head-tail) & (FEP_CMAX-FEP_CSTART-4); if(n <= ncmds * (sizeof(ushort)*4)) return; } printf("dgb%d(%d): timeout on FEP cmd=0x%x\n", port->unit, port->pnum, cmd); } static void disc_optim(tp, t) struct tty *tp; struct termios *t; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; } static dgb_devsw_installed = 0; static void dgb_drvinit(void *unused) { dev_t dev; if( ! dgb_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&dgb_cdevsw, NULL); dgb_devsw_installed = 1; } } SYSINIT(dgbdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,dgb_drvinit,NULL) #endif /* NDGB > 0 */ diff --git a/sys/gnu/i386/isa/dgm.c b/sys/gnu/i386/isa/dgm.c index 487b1aa73847..eca931ae8639 100644 --- a/sys/gnu/i386/isa/dgm.c +++ b/sys/gnu/i386/isa/dgm.c @@ -1,2120 +1,2121 @@ /*- - * $Id: dgm.c,v 1.3 1998/08/12 17:38:09 bde Exp $ + * $Id: dgm.c,v 1.4 1998/08/16 01:21:49 bde Exp $ * * This driver and the associated header files support the ISA PC/Xem * Digiboards. Its evolutionary roots are described below. * Jack O'Neill * * Digiboard driver. * * Stage 1. "Better than nothing". * Stage 2. "Gee, it works!". * * Based on sio driver by Bruce Evans and on Linux driver by Troy * De Jongh or * which is under GNU General Public License version 2 so this driver * is forced to be under GPL 2 too. * * Written by Serge Babkin, * Joint Stock Commercial Bank "Chelindbank" * (Chelyabinsk, Russia) * babkin@hq.icb.chel.su * * Assorted hacks to make it more functional and working under 3.0-current. * Fixed broken routines to prevent processes hanging on closed (thanks * to Bruce for his patience and assistance). Thanks also to Maxim Bolotin * for his patches which did most of the work to get this * running under 2.2/3.0-current. * Implemented ioctls: TIOCMSDTRWAIT, TIOCMGDTRWAIT, TIOCTIMESTAMP & * TIOCDCDTIMESTAMP. * Sysctl debug flag is now a bitflag, to filter noise during debugging. * David L. Nugent */ #include "opt_compat.h" #include "opt_devfs.h" #include "dgm.h" #if NDGM > 0 /* Helg: i.e.25 times per sec board will be polled */ #define POLLSPERSEC 25 /* How many charactes can we write to input tty rawq */ #define DGB_IBUFSIZE (TTYHOG-100) /* the overall number of ports controlled by this driver */ #ifndef NDGMPORTS # define NDGMPORTS (NDGM*64) #endif #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #include #define CALLOUT_MASK 0x40000 #define CONTROL_MASK 0xC0 #define CONTROL_INIT_STATE 0x40 #define CONTROL_LOCK_STATE 0x80 #define UNIT_MASK 0x30000 #define PORT_MASK 0x3F #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) (((mynor) & UNIT_MASK)>>16) #define MINOR_TO_PORT(mynor) ((mynor) & PORT_MASK) /* types. XXX - should be elsewhere */ typedef u_char bool_t; /* boolean */ /* digiboard port structure */ struct dgm_p { bool_t status; u_char unit; /* board unit number */ u_char pnum; /* port number */ u_char omodem; /* FEP output modem status */ u_char imodem; /* FEP input modem status */ u_char modemfake; /* Modem values to be forced */ u_char modem; /* Force values */ u_char hflow; u_char dsr; u_char dcd; u_char stopc; u_char startc; u_char stopca; u_char startca; u_char fepstopc; u_char fepstartc; u_char fepstopca; u_char fepstartca; u_char txwin; u_char rxwin; ushort fepiflag; ushort fepcflag; ushort fepoflag; ushort txbufhead; ushort txbufsize; ushort rxbufhead; ushort rxbufsize; int close_delay; int count; int blocked_open; int event; int asyncflags; u_long statusflags; u_char *txptr; u_char *rxptr; volatile struct board_chan *brdchan; struct tty *tty; bool_t active_out; /* nonzero if the callout device is open */ u_int wopeners; /* # processes waiting for DCD in open() */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; /* flags of state, are used in sleep() too */ u_char closing; /* port is being closed now */ u_char draining; /* port is being drained now */ u_char used; /* port is being used now */ u_char mustdrain; /* data must be waited to drain in dgmparam() */ #ifdef DEVFS struct { void *tty; void *ttyi; void *ttyl; void *cua; void *cuai; void *cual; } devfs_token; #endif }; /* Digiboard per-board structure */ struct dgm_softc { /* struct board_info */ u_char status; /* status: DISABLED/ENABLED */ u_char unit; /* unit number */ u_char type; /* type of card: PCXE, PCXI, PCXEVE */ u_char altpin; /* do we need alternate pin setting ? */ int numports; /* number of ports on card */ int port; /* I/O port */ u_char *vmem; /* virtual memory address */ long pmem; /* physical memory address */ int mem_seg; /* internal memory segment */ struct dgm_p *ports; /* pointer to array of port descriptors */ struct tty *ttys; /* pointer to array of TTY structures */ volatile struct global_data *mailbox; }; static struct dgm_softc dgm_softc[NDGM]; static struct dgm_p dgm_ports[NDGMPORTS]; static struct tty dgm_tty[NDGMPORTS]; int fi(void); /* * The public functions in the com module ought to be declared in a com-driver * system header. */ /* Interrupt handling entry points. */ static void dgmpoll __P((void *unit_c)); /* Device switch entry points. */ #define dgmreset noreset #define dgmmmap nommap #define dgmstrategy nostrategy static int dgmattach __P((struct isa_device *dev)); static int dgmprobe __P((struct isa_device *dev)); static void fepcmd(struct dgm_p *port, unsigned cmd, unsigned op1, unsigned op2, unsigned ncmds, unsigned bytecmd); static void dgmstart __P((struct tty *tp)); static int dgmparam __P((struct tty *tp, struct termios *t)); static void dgmhardclose __P((struct dgm_p *port)); static void dgm_drain_or_flush __P((struct dgm_p *port)); static int dgmdrain __P((struct dgm_p *port)); static void dgm_pause __P((void *chan)); static void wakeflush __P((void *p)); static void disc_optim __P((struct tty *tp, struct termios *t)); struct isa_driver dgmdriver = { dgmprobe, dgmattach, "dgm",0 }; static d_open_t dgmopen; static d_close_t dgmclose; static d_read_t dgmread; static d_write_t dgmwrite; static d_ioctl_t dgmioctl; static d_stop_t dgmstop; static d_devtotty_t dgmdevtotty; -#define CDEV_MAJOR 101 -static struct cdevsw dgm_cdevsw = - { dgmopen, dgmclose, dgmread, dgmwrite, - dgmioctl, dgmstop, noreset, dgmdevtotty, /* dgm */ - ttpoll, nommap, NULL, "dgm", NULL, -1 }; +#define CDEV_MAJOR 101 +static struct cdevsw dgm_cdevsw = { + dgmopen, dgmclose, dgmread, dgmwrite, + dgmioctl, dgmstop, noreset, dgmdevtotty, + ttpoll, nommap, NULL, "dgm", + NULL, -1, nodump, nopsize, + D_TTY, +}; static speed_t dgmdefaultrate = TTYDEF_SPEED; static struct speedtab dgmspeedtab[] = { 0, FEP_B0, /* old (sysV-like) Bx codes */ 50, FEP_B50, 75, FEP_B75, 110, FEP_B110, 134, FEP_B134, 150, FEP_B150, 200, FEP_B200, 300, FEP_B300, 600, FEP_B600, 1200, FEP_B1200, 1800, FEP_B1800, 2400, FEP_B2400, 4800, FEP_B4800, 9600, FEP_B9600, 19200, FEP_B19200, 38400, FEP_B38400, 57600, (FEP_FASTBAUD|FEP_B50), /* B50 & fast baud table */ 115200, (FEP_FASTBAUD|FEP_B110), /* B100 & fast baud table */ -1, -1 }; static struct dbgflagtbl { tcflag_t in_mask; tcflag_t in_val; tcflag_t out_val; } dgm_cflags[] = { { PARODD, PARODD, FEP_PARODD }, { PARENB, PARENB, FEP_PARENB }, { CSTOPB, CSTOPB, FEP_CSTOPB }, { CSIZE, CS5, FEP_CS6 }, { CSIZE, CS6, FEP_CS6 }, { CSIZE, CS7, FEP_CS7 }, { CSIZE, CS8, FEP_CS8 }, { CLOCAL, CLOCAL, FEP_CLOCAL }, { (tcflag_t)-1 } }, dgm_iflags[] = { { IGNBRK, IGNBRK, FEP_IGNBRK }, { BRKINT, BRKINT, FEP_BRKINT }, { IGNPAR, IGNPAR, FEP_IGNPAR }, { PARMRK, PARMRK, FEP_PARMRK }, { INPCK, INPCK, FEP_INPCK }, { ISTRIP, ISTRIP, FEP_ISTRIP }, { IXON, IXON, FEP_IXON }, { IXOFF, IXOFF, FEP_IXOFF }, { IXANY, IXANY, FEP_IXANY }, { (tcflag_t)-1 } }, dgm_flow[] = { { CRTSCTS, CRTSCTS, CTS|RTS }, { CRTSCTS, CCTS_OFLOW, CTS }, { CRTSCTS, CRTS_IFLOW, RTS }, { (tcflag_t)-1 } }; /* xlat bsd termios flags to dgm sys-v style */ static tcflag_t dgmflags(struct dbgflagtbl *tbl, tcflag_t input) { tcflag_t output = 0; int i; for (i=0; tbl[i].in_mask != (tcflag_t)-1; i++) { if ((input & tbl[i].in_mask) == tbl[i].in_val) output |= tbl[i].out_val; } return output; } static int dgmdebug=0; SYSCTL_INT(_debug, OID_AUTO, dgm_debug, CTLFLAG_RW, &dgmdebug, 0, ""); static __inline int setwin __P((struct dgm_softc *sc, unsigned addr)); static __inline void hidewin __P((struct dgm_softc *sc)); static __inline void towin __P((struct dgm_softc *sc, int win)); /*Helg: to allow recursive dgm...() calls */ typedef struct { /* If we were called and don't want to disturb we need: */ int port; /* write to this port */ u_char data; /* this data on exit */ /* or DATA_WINOFF to close memory window on entry */ } BoardMemWinState; /* so several channels and even boards can coexist */ #define DATA_WINOFF 0 static BoardMemWinState bmws; /* return current memory window state and close window */ static BoardMemWinState bmws_get(void) { BoardMemWinState bmwsRet=bmws; if(bmws.data!=DATA_WINOFF) outb(bmws.port, bmws.data=DATA_WINOFF); return bmwsRet; } /* restore memory window state */ static void bmws_set(BoardMemWinState ws) { if(ws.data != bmws.data || ws.port!=bmws.port ) { if(bmws.data!=DATA_WINOFF) outb(bmws.port,DATA_WINOFF); if(ws.data!=DATA_WINOFF) outb(ws.port, ws.data); bmws=ws; } } static __inline int setwin(sc,addr) struct dgm_softc *sc; unsigned int addr; { outb(bmws.port=sc->port+1,bmws.data=FEPWIN|(addr >> 15)); return (addr & 0x7FFF); } static __inline void hidewin(sc) struct dgm_softc *sc; { bmws.data=0; outb(bmws.port=sc->port+1, bmws.data); } static __inline void towin(sc,win) struct dgm_softc *sc; int win; { outb(bmws.port=sc->port+1, bmws.data=win); } static int dgmprobe(dev) struct isa_device *dev; { struct dgm_softc *sc= &dgm_softc[dev->id_unit]; int i, v, t; u_char *mem; int addr; int unit=dev->id_unit; sc->unit=dev->id_unit; sc->port=dev->id_iobase; if(dev->id_flags & DGBFLAG_ALTPIN) sc->altpin=1; else sc->altpin=0; /* left 24 bits only (ISA address) */ sc->pmem=((intptr_t)(void *)dev->id_maddr & 0xFFFFFF); DPRINT4(DB_INFO,"dgm%d: port 0x%x mem 0x%lx\n",unit,sc->port,sc->pmem); outb(sc->port, FEPRST); sc->status=DISABLED; for(i=0; i< 1000; i++) { DELAY(1); if( (inb(sc->port) & FEPMASK) == FEPRST ) { sc->status=ENABLED; DPRINT3(DB_EXCEPT,"dgm%d: got reset after %d us\n",unit,i); break; } } if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dgm%d: failed to respond\n",dev->id_unit); return 0; } /* check type of card and get internal memory characteristics */ v=inb(sc->port); printf("dgm%d: PC/Xem\n",dev->id_unit); sc->type=PCXEM; sc->mem_seg=0x8000; /* save size of vizible memory segment */ /* all PCXEMs have a 32k window size */ dev->id_msize=0x8000; /* map memory */ dev->id_maddr=sc->vmem=pmap_mapdev(sc->pmem,dev->id_msize); outb(sc->port, FEPCLR); /* drop RESET */ hidewin(sc); /* Helg: to set initial bmws state */ return 4; /* we need I/O space of 4 ports */ } static int dgmattach(dev) struct isa_device *dev; { int unit=dev->id_unit; struct dgm_softc *sc= &dgm_softc[dev->id_unit]; int i, t; u_char *mem; u_char *ptr; int addr; struct dgm_p *port; volatile struct board_chan *bc; struct global_data *gd; int shrinkmem; int nfails; ushort *pstat; int lowwater; static int nports=0; char ch; int stuff; if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dbg%d: try to attach a disabled card\n",unit); return 0; } mem=sc->vmem; DPRINT3(DB_INFO,"dgm%d: internal memory segment 0x%x\n",unit,sc->mem_seg); outb(sc->port, FEPRST); DELAY(1); for(i=0; (inb(sc->port) & FEPMASK) != FEPRST ; i++) { if(i>10000) { printf("dgm%d: 1st reset failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } DELAY(1); } DPRINT3(DB_INFO,"dgm%d: got reset after %d us\n",unit,i); t=(((u_long)sc->pmem>>8)) /* disable windowing */; outb(sc->port+2,t & 0xFF); outb(sc->port+3,t>>8); mem=sc->vmem; /* very short memory test */ addr=setwin(sc,BOTWIN); *(u_long *)(mem+addr) = 0xA55A3CC3; if(*(u_long *)(mem+addr)!=0xA55A3CC3) { printf("dgm%d: 1st memory test failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } addr=setwin(sc,TOPWIN); *(u_long *)(mem+addr) = 0x5AA5C33C; if(*(u_long *)(mem+addr)!=0x5AA5C33C) { printf("dgm%d: 2nd memory test failed\n",dev->id_unit); sc->status=DISABLED; hidewin(sc); return 0; } addr=setwin(sc,BIOSCODE+((0xF000-sc->mem_seg)<<4)); *(u_long *)(mem+addr) = 0x5AA5C33C; if(*(u_long *)(mem+addr)!=0x5AA5C33C) { printf("dgm%d: 3rd (BIOS) memory test failed\n",dev->id_unit); } addr=setwin(sc,MISCGLOBAL); for(i=0; i<16; i++) { mem[addr+i]=0; } addr=setwin(sc,BIOSOFFSET); ptr=mem+addr; for(i=0; ptrid_msize; i++){ *ptr++ = pcem_bios[i]; } ptr=mem+BIOSOFFSET; for(i=0; ptrid_msize; i++){ if(*ptr++ != pcem_bios[i]){ printf("Low BIOS load failed\n"); sc->status = DISABLED; hidewin(sc); return 0; } } addr=setwin(sc,dev->id_msize); ptr =mem+addr; for(;i < pcem_nbios; i++){ *ptr++ = pcem_bios[i]; } ptr=mem; for(i = dev->id_msize - BIOSOFFSET; i < pcem_nbios; i++){ if(*ptr++ != pcem_bios[i]){ printf("High BIOS load failed\n"); sc->status = DISABLED; hidewin(sc); return 0; } } printf("dgm%d: DigiBIOS loaded, initializing",dev->id_unit); addr=setwin(sc,0); *(u_int *)(mem+addr+0)=0x0bf00401; *(u_int *)(mem+addr+4)=0; *(ushort *)(mem+addr+0xc00)=0; outb(sc->port, 0); for(i = 0;*(u_char *)(mem+addr+ 0xc00) != 0x47;i++){ DELAY(10000); if(i> 3000){ printf("\nBIOS initialize failed(1)\n"); sc->status = DISABLED; hidewin(sc); return 0; } } if(*(u_char *)(mem+addr+ 0xc01) != 0x44){ printf("\nBIOS initialize failed(2)\n"); sc->status = DISABLED; hidewin(sc); return 0; } printf(", DigiBIOS running\n"); DELAY(10000); addr=setwin(sc,BIOSOFFSET); ptr=mem+addr; for(i=0; istatus = DISABLED; hidewin(sc); return 0; } } printf("dgm%d: FEP/OS loaded, initializing",dev->id_unit); addr=setwin(sc,0); *(ushort *)(mem+addr+0xd20)=0; *(u_int *)(mem+addr+0xc34)=0xbfc01004; *(u_int *)(mem+addr+0xc30)=0x3L; outb(sc->port,0); for(i = 0;*(u_char *)(mem+addr+ 0xd20) != 'O';i++){ DELAY(10000); if(i> 3000){ printf("\nFEP/OS initialize failed(1)\n"); sc->status = DISABLED; hidewin(sc); return 0; } } if(*(u_char *)(mem+addr+ 0xd21) != 'S'){ printf("\nFEP/OS initialize failed(2)\n"); sc->status = DISABLED; hidewin(sc); return 0; } printf(", FEP/OS running\n"); sc->numports= *(ushort *)(mem+setwin(sc,NPORT)); printf("dgm%d: %d ports\n",unit,sc->numports); if(sc->numports > MAX_DGM_PORTS) { printf("dgm%d: too many ports\n",unit); sc->status=DISABLED; hidewin(sc); return 0; } if(nports+sc->numports>NDGMPORTS) { printf("dgm%d: only %d ports are usable\n", unit, NDGMPORTS-nports); sc->numports=NDGMPORTS-nports; } /* allocate port and tty structures */ sc->ports=&dgm_ports[nports]; sc->ttys=&dgm_tty[nports]; nports+=sc->numports; for(i=0; inumports; i++) sc->ports[i].status = ENABLED; /* We should now init per-port structures */ setwin(sc,0); bc=(volatile struct board_chan *)(mem + CHANSTRUCT); sc->mailbox=(volatile struct global_data *)(mem + FEP_GLOBAL); if(sc->numports<3) shrinkmem=1; else shrinkmem=0; for(i=0; inumports; i++, bc++) { port= &sc->ports[i]; port->tty=&sc->ttys[i]; port->unit=unit; port->brdchan=bc; port->dcd=CD; port->dsr=DSR; port->pnum=i; if(shrinkmem) { DPRINT2(DB_INFO,"dgm%d: shrinking memory\n",unit); fepcmd(port, SETBUFFER, 32, 0, 0, 0); shrinkmem=0; } /* HERE */ port->txptr=mem+( ((bc->tseg)<<4) & 0x7FFF ); port->rxptr=mem+( ((bc->rseg)<<4) & 0x7FFF ); port->txwin=FEPWIN | ((bc->tseg)>>11); port->rxwin=FEPWIN | ((bc->rseg)>>11); port->txbufhead=0; port->rxbufhead=0; port->txbufsize=bc->tmax+1; port->rxbufsize=bc->rmax+1; lowwater= (port->txbufsize>=2000) ? 1024 : (port->txbufsize/2); setwin(sc,0); fepcmd(port, STXLWATER, lowwater, 0, 10, 0); fepcmd(port, SRXLWATER, port->rxbufsize/4, 0, 10, 0); fepcmd(port, SRXHWATER, 3*port->rxbufsize/4, 0, 10, 0); bc->edelay=100; bc->idata=1; port->startc=bc->startc; port->startca=bc->startca; port->stopc=bc->stopc; port->stopca=bc->stopca; /*port->close_delay=50;*/ port->close_delay=3 * hz; port->do_timestamp=0; port->do_dcd_timestamp=0; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ port->it_in.c_iflag = TTYDEF_IFLAG; port->it_in.c_oflag = TTYDEF_OFLAG; port->it_in.c_cflag = TTYDEF_CFLAG; port->it_in.c_lflag = TTYDEF_LFLAG; termioschars(&port->it_in); port->it_in.c_ispeed = port->it_in.c_ospeed = dgmdefaultrate; port->it_out = port->it_in; #ifdef DEVFS port->devfs_token.tty = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyM%d%x", unit, i + 0xa0); port->devfs_token.ttyi = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i+64, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyiM%d%x", unit, i + 0xa0); port->devfs_token.ttyl = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i+128, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttylM%d%x", unit, i + 0xa0); port->devfs_token.cua = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i+262144, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaM%d%x", unit, i + 0xa0); port->devfs_token.cuai = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i+262208, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaiM%d%x", unit, i + 0xa0); port->devfs_token.cual = devfs_add_devswf(&dgm_cdevsw, (unit*65536)+i+262272, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cualM%d%x", unit, i + 0xa0); #endif } hidewin(sc); /* register the polling function */ timeout(dgmpoll, (void *)unit, hz/POLLSPERSEC); return 1; } /* ARGSUSED */ static int dgmopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct dgm_softc *sc; struct tty *tp; int unit; int mynor; int pnum; struct dgm_p *port; int s,cs; int error; volatile struct board_chan *bc; error=0; mynor=minor(dev); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); if(unit >= NDGM) { DPRINT2(DB_EXCEPT,"dgm%d: try to open a nonexisting card\n",unit); return ENXIO; } sc=&dgm_softc[unit]; if(sc->status!=ENABLED) { DPRINT2(DB_EXCEPT,"dgm%d: try to open a disabled card\n",unit); return ENXIO; } if(pnum>=sc->numports) { DPRINT3(DB_EXCEPT,"dgm%d: try to open non-existing port %d\n",unit,pnum); return ENXIO; } if(mynor & CONTROL_MASK) return 0; tp=&sc->ttys[pnum]; port=&sc->ports[pnum]; bc=port->brdchan; open_top: s=spltty(); while(port->closing) { error=tsleep(&port->closing, TTOPRI|PCATCH, "dgocl", 0); if(error) { DPRINT4(DB_OPEN,"dgm%d: port%d: tsleep(dgocl) error=%d\n",unit,pnum,error); goto out; } } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!port->active_out) { error = EBUSY; DPRINT4(DB_OPEN,"dgm%d: port%d: BUSY error=%d\n",unit,pnum,error); goto out; } } else { if (port->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; DPRINT4(DB_OPEN,"dgm%d: port%d: BUSY error=%d\n",unit,pnum,error); goto out; } error = tsleep(&port->active_out, TTIPRI | PCATCH, "dgmi", 0); if (error != 0) { DPRINT4(DB_OPEN,"dgm%d: port%d: tsleep(dgmi) error=%d\n", unit,pnum,error); goto out; } splx(s); goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc=dgmstart; tp->t_param=dgmparam; tp->t_dev=dev; tp->t_termios= (mynor & CALLOUT_MASK) ? port->it_out : port->it_in; cs=splclock(); setwin(sc,0); port->imodem=bc->mstat; bc->rout=bc->rin; /* clear input queue */ bc->idata=1; #ifdef PRINT_BUFSIZE printf("dgm buffers tx=%x:%x rx=%x:%x\n",bc->tseg,bc->tmax,bc->rseg,bc->rmax); #endif hidewin(sc); splx(cs); port->wopeners++; error=dgmparam(tp, &tp->t_termios); port->wopeners--; if(error!=0) { DPRINT4(DB_OPEN,"dgm%d: port%d: dgmparam error=%d\n",unit,pnum,error); goto out; } - ttsetwater(tp); - /* handle fake DCD for callout devices */ /* and initial DCD */ if( (port->imodem & port->dcd) || mynor & CALLOUT_MASK ) linesw[tp->t_line].l_modem(tp,1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++port->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "dgdcd", 0); --port->wopeners; if (error != 0) { DPRINT4(DB_OPEN,"dgm%d: port%d: tsleep(dgdcd) error=%d\n",unit,pnum,error); goto out; } splx(s); goto open_top; } error = linesw[tp->t_line].l_open(dev, tp); disc_optim(tp,&tp->t_termios); DPRINT4(DB_OPEN,"dgm%d: port%d: l_open error=%d\n",unit,pnum,error); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) port->active_out = TRUE; port->used=1; /* If any port is open (i.e. the open() call is completed for it) * the device is busy */ out: disc_optim(tp,&tp->t_termios); splx(s); if( !(tp->t_state & TS_ISOPEN) && port->wopeners==0 ) dgmhardclose(port); DPRINT4(DB_OPEN,"dgm%d: port%d: open() returns %d\n",unit,pnum,error); return error; } /*ARGSUSED*/ static int dgmclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { int mynor; struct tty *tp; int unit, pnum; struct dgm_softc *sc; struct dgm_p *port; int s; int i; mynor=minor(dev); if(mynor & CONTROL_MASK) return 0; unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); sc=&dgm_softc[unit]; tp=&sc->ttys[pnum]; port=sc->ports+pnum; DPRINT3(DB_CLOSE,"dgm%d: port%d: closing\n",unit,pnum); DPRINT3(DB_CLOSE,"dgm%d: port%d: draining port\n",unit,pnum); dgm_drain_or_flush(port); s=spltty(); port->closing=1; DPRINT3(DB_CLOSE,"dgm%d: port%d: closing line disc\n",unit,pnum); linesw[tp->t_line].l_close(tp,flag); disc_optim(tp,&tp->t_termios); DPRINT3(DB_CLOSE,"dgm%d: port%d: hard closing\n",unit,pnum); dgmhardclose(port); DPRINT3(DB_CLOSE,"dgm%d: port%d: closing tty\n",unit,pnum); ttyclose(tp); port->closing=0; wakeup(&port->closing); port->used=0; /* mark the card idle when all ports are closed */ for(i=0; inumports; i++) if(sc->ports[i].used) break; splx(s); DPRINT3(DB_CLOSE,"dgm%d: port%d: closed\n",unit,pnum); wakeup(TSA_CARR_ON(tp)); wakeup(&port->active_out); port->active_out=0; DPRINT3(DB_CLOSE,"dgm%d: port%d: close exit\n",unit,pnum); return 0; } static void dgmhardclose(port) struct dgm_p *port; { struct dgm_softc *sc=&dgm_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int cs; cs=splclock(); port->do_timestamp = 0; setwin(sc,0); bc->idata=0; bc->iempty=0; bc->ilow=0; if(port->tty->t_cflag & HUPCL) { port->omodem &= ~(RTS|DTR); fepcmd(port, SETMODEM, 0, DTR|RTS, 0, 1); } hidewin(sc); splx(cs); timeout(dgm_pause, &port->brdchan, hz/2); tsleep(&port->brdchan, TTIPRI | PCATCH, "dgclo", 0); } static void dgm_pause(chan) void *chan; { wakeup((caddr_t)chan); } static int dgmread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int error, unit, pnum; mynor=minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); tp=&dgm_softc[unit].ttys[pnum]; error=linesw[tp->t_line].l_read(tp, uio, flag); DPRINT4(DB_RD,"dgm%d: port%d: read() returns %d\n",unit,pnum,error); return error; } static int dgmwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int error, unit, pnum; mynor=minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); tp=&dgm_softc[unit].ttys[pnum]; error=linesw[tp->t_line].l_write(tp, uio, flag); DPRINT4(DB_WR,"dgm%d: port%d: write() returns %d\n",unit,pnum,error); return error; } static void dgmpoll(unit_c) void *unit_c; { int unit=(int)unit_c; int pnum; struct dgm_p *port; struct dgm_softc *sc=&dgm_softc[unit]; int head, tail; u_char *eventbuf; int event, mstat, lstat; volatile struct board_chan *bc; struct tty *tp; int rhead, rtail; int whead, wtail; int size; int c=0; u_char *ptr; int ocount; int ibuf_full,obuf_full; BoardMemWinState ws=bmws_get(); if(sc->status==DISABLED) { printf("dgm%d: polling of disabled board stopped\n",unit); return; } setwin(sc,0); head=sc->mailbox->ein; tail=sc->mailbox->eout; while(head!=tail) { if(head >= FEP_IMAX-FEP_ISTART || tail >= FEP_IMAX-FEP_ISTART || (head|tail) & 03 ) { printf("dgm%d: event queue's head or tail is wrong! hd=%d,tl=%d\n", unit,head,tail); break; } eventbuf=sc->vmem+tail+FEP_ISTART; pnum=eventbuf[0]; event=eventbuf[1]; mstat=eventbuf[2]; lstat=eventbuf[3]; port=&sc->ports[pnum]; bc=port->brdchan; tp=&sc->ttys[pnum]; if(pnum>=sc->numports || port->status==DISABLED) { printf("dgm%d: port%d: got event on nonexisting port\n",unit,pnum); } else if(port->used || port->wopeners>0 ) { int wrapmask=port->rxbufsize-1; if( !(event & ALL_IND) ) printf("dgm%d: port%d: ? event 0x%x mstat 0x%x lstat 0x%x\n", unit, pnum, event, mstat, lstat); if(event & DATA_IND) { DPRINT3(DB_DATA,"dgm%d: port%d: DATA_IND\n",unit,pnum); rhead=bc->rin & wrapmask; rtail=bc->rout & wrapmask; if( !(tp->t_cflag & CREAD) || !port->used ) { bc->rout=rhead; goto end_of_data; } if(bc->orun) { printf("dgm%d: port%d: overrun\n", unit, pnum); bc->orun=0; } if(!(tp->t_state & TS_ISOPEN)) goto end_of_data; for(ibuf_full=FALSE;rhead!=rtail && !ibuf_full;) { DPRINT5(DB_RXDATA,"dgm%d: port%d: p rx head=%d tail=%d\n", unit,pnum,rhead,rtail); if(rhead>rtail) size=rhead-rtail; else size=port->rxbufsize-rtail; ptr=port->rxptr+rtail; /* Helg: */ if( tp->t_rawq.c_cc + size > DGB_IBUFSIZE ) { size=DGB_IBUFSIZE-tp->t_rawq.c_cc; DPRINT1(DB_RXDATA,"*"); ibuf_full=TRUE; } if(size) { if (tp->t_state & TS_CAN_BYPASS_L_RINT) { DPRINT1(DB_RXDATA,"!"); towin(sc,port->rxwin); tk_nin += size; tk_rawcc += size; tp->t_rawcc += size; b_to_q(ptr,size,&tp->t_rawq); setwin(sc,0); } else { int i=size; unsigned char chr; do { towin(sc,port->rxwin); chr= *ptr++; hidewin(sc); (*linesw[tp->t_line].l_rint)(chr, tp); } while (--i > 0 ); setwin(sc,0); } } rtail= (rtail + size) & wrapmask; bc->rout=rtail; rhead=bc->rin & wrapmask; hidewin(sc); ttwakeup(tp); setwin(sc,0); } end_of_data: ; } if(event & MODEMCHG_IND) { DPRINT3(DB_MODEM,"dgm%d: port%d: MODEMCHG_IND\n",unit,pnum); port->imodem=mstat; if(mstat & port->dcd) { hidewin(sc); linesw[tp->t_line].l_modem(tp,1); setwin(sc,0); wakeup(TSA_CARR_ON(tp)); } else { hidewin(sc); linesw[tp->t_line].l_modem(tp,0); setwin(sc,0); if( port->draining) { port->draining=0; wakeup(&port->draining); } } } if(event & BREAK_IND) { if((tp->t_state & TS_ISOPEN) && (tp->t_iflag & IGNBRK)) { DPRINT3(DB_BREAK,"dgm%d: port%d: BREAK_IND\n",unit,pnum); hidewin(sc); linesw[tp->t_line].l_rint(TTY_BI, tp); setwin(sc,0); } } /* Helg: with output flow control */ if(event & (LOWTX_IND | EMPTYTX_IND) ) { DPRINT3(DB_TXDATA,"dgm%d: port%d: LOWTX_IND or EMPTYTX_IND\n",unit,pnum); if( (event & EMPTYTX_IND ) && tp->t_outq.c_cc==0 && port->draining) { port->draining=0; wakeup(&port->draining); bc->ilow=0; bc->iempty=0; } else { int wrapmask=port->txbufsize-1; for(obuf_full=FALSE; tp->t_outq.c_cc!=0 && !obuf_full; ) { int s; /* add "last-minute" data to write buffer */ if(!(tp->t_state & TS_BUSY)) { hidewin(sc); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ ttwwakeup(tp); #else if(tp->t_outq.c_cc <= tp->t_lowat) { if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } /* selwakeup(&tp->t_wsel); */ } #endif setwin(sc,0); } s=spltty(); whead=bc->tin & wrapmask; wtail=bc->tout & wrapmask; if(wheadtxbufsize-whead; if(wtail==0) size--; } if(size==0) { DPRINT5(DB_WR,"dgm: head=%d tail=%d size=%d full=%d\n", whead,wtail,size,obuf_full); bc->iempty=1; bc->ilow=1; obuf_full=TRUE; splx(s); break; } towin(sc,port->txwin); ocount=q_to_b(&tp->t_outq, port->txptr+whead, size); whead+=ocount; setwin(sc,0); bc->tin=whead; bc->tin=whead & wrapmask; splx(s); } if(obuf_full) { DPRINT1(DB_WR," +BUSY\n"); tp->t_state|=TS_BUSY; } else { DPRINT1(DB_WR," -BUSY\n"); hidewin(sc); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ /* should clear TS_BUSY before ttwwakeup */ if(tp->t_state & TS_BUSY) { tp->t_state &= ~TS_BUSY; linesw[tp->t_line].l_start(tp); ttwwakeup(tp); } #else if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } tp->t_state &= ~TS_BUSY; #endif setwin(sc,0); } } end_of_buffer: ; } bc->idata=1; /* require event on incoming data */ } else { bc=port->brdchan; DPRINT4(DB_EXCEPT,"dgm%d: port%d: got event 0x%x on closed port\n", unit,pnum,event); bc->rout=bc->rin; bc->idata=bc->iempty=bc->ilow=0; } tail= (tail+4) & (FEP_IMAX-FEP_ISTART-4); } sc->mailbox->eout=tail; bmws_set(ws); timeout(dgmpoll, unit_c, hz/POLLSPERSEC); } static int dgmioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct dgm_softc *sc; int unit, pnum; struct dgm_p *port; int mynor; struct tty *tp; volatile struct board_chan *bc; int error; int s,cs; int tiocm_xxx; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) u_long oldcmd; struct termios term; #endif BoardMemWinState ws=bmws_get(); mynor=minor(dev); unit=MINOR_TO_UNIT(mynor); pnum=MINOR_TO_PORT(mynor); sc=&dgm_softc[unit]; port=&sc->ports[pnum]; tp=&sc->ttys[pnum]; bc=port->brdchan; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &port->it_out : &port->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { DPRINT6(DB_PARAM,"dgm%d: port%d: dgmioctl-ISNOW c=0x%x i=0x%x l=0x%x\n",unit,pnum,term.c_cflag,term.c_iflag,term.c_lflag); } oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &port->lt_out : &port->lt_in; DPRINT6(DB_PARAM,"dgm%d: port%d: dgmioctl-TOSET c=0x%x i=0x%x l=0x%x\n",unit,pnum,dt->c_cflag,dt->c_iflag,dt->c_lflag); dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } if(cmd==TIOCSTOP) { cs=splclock(); setwin(sc,0); fepcmd(port, PAUSETX, 0, 0, 0, 0); bmws_set(ws); splx(cs); return 0; } else if(cmd==TIOCSTART) { cs=splclock(); setwin(sc,0); fepcmd(port, RESUMETX, 0, 0, 0, 0); bmws_set(ws); splx(cs); return 0; } if(cmd==TIOCSETAW || cmd==TIOCSETAF) port->mustdrain=1; error = linesw[tp->t_line].l_ioctl(tp, cmd, data, flag, p); if (error != ENOIOCTL) return error; s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp,&tp->t_termios); port->mustdrain=0; if (error != ENOIOCTL) { splx(s); if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { DPRINT6(DB_PARAM,"dgm%d: port%d: dgmioctl-RES c=0x%x i=0x%x l=0x%x\n",unit,pnum,tp->t_cflag,tp->t_iflag,tp->t_lflag); } return error; } switch (cmd) { case TIOCSBRK: /* Helg: commented */ /* error=dgmdrain(port);*/ if(error!=0) { splx(s); return error; } cs=splclock(); setwin(sc,0); /* now it sends 250 millisecond break because I don't know */ /* how to send an infinite break */ fepcmd(port, SENDBREAK, 250, 0, 10, 0); hidewin(sc); splx(cs); break; case TIOCCBRK: /* now it's empty */ break; case TIOCSDTR: DPRINT3(DB_MODEM,"dgm%d: port%d: set DTR\n",unit,pnum); port->omodem |= DTR; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS, 0, 1); if( !(bc->mstat & DTR) ) { DPRINT3(DB_MODEM,"dgm%d: port%d: DTR is off\n",unit,pnum); } hidewin(sc); splx(cs); break; case TIOCCDTR: DPRINT3(DB_MODEM,"dgm%d: port%d: reset DTR\n",unit,pnum); port->omodem &= ~DTR; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); if( bc->mstat & DTR ) { DPRINT3(DB_MODEM,"dgm%d: port%d: DTR is on\n",unit,pnum); } hidewin(sc); splx(cs); break; case TIOCMSET: if(*(int *)data & TIOCM_DTR) port->omodem |=DTR; else port->omodem &=~DTR; if(*(int *)data & TIOCM_RTS) port->omodem |=RTS; else port->omodem &=~RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMBIS: if(*(int *)data & TIOCM_DTR) port->omodem |=DTR; if(*(int *)data & TIOCM_RTS) port->omodem |=RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMBIC: if(*(int *)data & TIOCM_DTR) port->omodem &=~DTR; if(*(int *)data & TIOCM_RTS) port->omodem &=~RTS; cs=splclock(); setwin(sc,0); fepcmd(port, SETMODEM, port->omodem, RTS|DTR, 0, 1); hidewin(sc); splx(cs); break; case TIOCMGET: setwin(sc,0); port->imodem=bc->mstat; hidewin(sc); tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */ DPRINT3(DB_MODEM,"dgm%d: port%d: modem stat -- ",unit,pnum); if (port->imodem & DTR) { DPRINT1(DB_MODEM,"DTR "); tiocm_xxx |= TIOCM_DTR; } if (port->imodem & RTS) { DPRINT1(DB_MODEM,"RTS "); tiocm_xxx |= TIOCM_RTS; } if (port->imodem & CTS) { DPRINT1(DB_MODEM,"CTS "); tiocm_xxx |= TIOCM_CTS; } if (port->imodem & port->dcd) { DPRINT1(DB_MODEM,"DCD "); tiocm_xxx |= TIOCM_CD; } if (port->imodem & port->dsr) { DPRINT1(DB_MODEM,"DSR "); tiocm_xxx |= TIOCM_DSR; } if (port->imodem & RI) { DPRINT1(DB_MODEM,"RI "); tiocm_xxx |= TIOCM_RI; } *(int *)data = tiocm_xxx; DPRINT1(DB_MODEM,"--\n"); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } port->close_delay = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = port->close_delay * 100 / hz; break; case TIOCTIMESTAMP: port->do_timestamp = TRUE; *(struct timeval *)data = port->timestamp; break; case TIOCDCDTIMESTAMP: port->do_dcd_timestamp = TRUE; *(struct timeval *)data = port->dcd_timestamp; break; default: bmws_set(ws); splx(s); return ENOTTY; } bmws_set(ws); splx(s); return 0; } static void wakeflush(p) void *p; { struct dgm_p *port=p; wakeup(&port->draining); } /* wait for the output to drain */ static int dgmdrain(port) struct dgm_p *port; { struct dgm_softc *sc=&dgm_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int error; int head, tail; BoardMemWinState ws=bmws_get(); setwin(sc,0); bc->iempty=1; tail=bc->tout; head=bc->tin; while(tail!=head) { DPRINT5(DB_WR,"dgm%d: port%d: drain: head=%d tail=%d\n", port->unit, port->pnum, head, tail); hidewin(sc); port->draining=1; timeout(wakeflush,port, hz); error=tsleep(&port->draining, TTIPRI | PCATCH, "dgdrn", 0); port->draining=0; setwin(sc,0); if (error != 0) { DPRINT4(DB_WR,"dgm%d: port%d: tsleep(dgdrn) error=%d\n", port->unit,port->pnum,error); bc->iempty=0; bmws_set(ws); return error; } tail=bc->tout; head=bc->tin; } DPRINT5(DB_WR,"dgm%d: port%d: drain: head=%d tail=%d\n", port->unit, port->pnum, head, tail); bmws_set(ws); return 0; } /* wait for the output to drain */ /* or simply clear the buffer it it's stopped */ static void dgm_drain_or_flush(port) struct dgm_p *port; { struct tty *tp=port->tty; struct dgm_softc *sc=&dgm_softc[port->unit]; volatile struct board_chan *bc=port->brdchan; int error; int lasttail; int head, tail; setwin(sc,0); lasttail=-1; bc->iempty=1; tail=bc->tout; head=bc->tin; while(tail!=head /* && tail!=lasttail */ ) { DPRINT5(DB_WR,"dgm%d: port%d: flush: head=%d tail=%d\n", port->unit, port->pnum, head, tail); /* if there is no carrier simply clean the buffer */ if( !(tp->t_state & TS_CARR_ON) ) { bc->tout=bc->tin=0; bc->iempty=0; hidewin(sc); return; } hidewin(sc); port->draining=1; timeout(wakeflush,port, hz); error=tsleep(&port->draining, TTIPRI | PCATCH, "dgfls", 0); port->draining=0; setwin(sc,0); if (error != 0) { DPRINT4(DB_WR,"dgm%d: port%d: tsleep(dgfls) error=%d\n", port->unit,port->pnum,error); /* silently clean the buffer */ bc->tout=bc->tin=0; bc->iempty=0; hidewin(sc); return; } lasttail=tail; tail=bc->tout; head=bc->tin; } hidewin(sc); DPRINT5(DB_WR,"dgm%d: port%d: flush: head=%d tail=%d\n", port->unit, port->pnum, head, tail); } static int dgmparam(tp, t) struct tty *tp; struct termios *t; { int dev=tp->t_dev; int mynor=minor(dev); int unit=MINOR_TO_UNIT(dev); int pnum=MINOR_TO_PORT(dev); struct dgm_softc *sc=&dgm_softc[unit]; struct dgm_p *port=&sc->ports[pnum]; volatile struct board_chan *bc=port->brdchan; int cflag; int head; int mval; int iflag; int hflow; int s,cs; BoardMemWinState ws=bmws_get(); DPRINT6(DB_PARAM,"dgm%d: port%d: dgmparm c=0x%x i=0x%x l=0x%x\n",unit,pnum,t->c_cflag,t->c_iflag,t->c_lflag); if(port->mustdrain) { DPRINT3(DB_PARAM,"dgm%d: port%d: must call dgmdrain()\n",unit,pnum); dgmdrain(port); } cflag=ttspeedtab(t->c_ospeed, dgmspeedtab); if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; if (cflag < 0 /* || cflag > 0 && t->c_ispeed != t->c_ospeed */) { DPRINT4(DB_PARAM,"dgm%d: port%d: invalid cflag=0%o\n",unit,pnum,cflag); return (EINVAL); } cs=splclock(); setwin(sc,0); if(cflag==0) { /* hangup */ DPRINT3(DB_PARAM,"dgm%d: port%d: hangup\n",unit,pnum); head=bc->rin; bc->rout=head; head=bc->tin; fepcmd(port, STOUT, (unsigned)head, 0, 0, 0); mval= port->omodem & ~(DTR|RTS); } else { cflag |= dgmflags(dgm_cflags, t->c_cflag); if(cflag!=port->fepcflag) { port->fepcflag=cflag; DPRINT5(DB_PARAM,"dgm%d: port%d: set cflag=0x%x c=0x%x\n", unit,pnum,cflag,t->c_cflag&~CRTSCTS); fepcmd(port, SETCTRLFLAGS, (unsigned)cflag, 0, 0, 0); } mval= port->omodem | (DTR|RTS); } iflag=dgmflags(dgm_iflags, t->c_iflag); if(iflag!=port->fepiflag) { port->fepiflag=iflag; DPRINT5(DB_PARAM,"dgm%d: port%d: set iflag=0x%x c=0x%x\n",unit,pnum,iflag,t->c_iflag); fepcmd(port, SETIFLAGS, (unsigned)iflag, 0, 0, 0); } bc->mint=port->dcd; hflow=dgmflags(dgm_flow, t->c_cflag); if(hflow!=port->hflow) { port->hflow=hflow; DPRINT5(DB_PARAM,"dgm%d: port%d: set hflow=0x%x f=0x%x\n",unit,pnum,hflow,t->c_cflag&CRTSCTS); fepcmd(port, SETHFLOW, (unsigned)hflow, 0xff, 0, 1); } if(port->omodem != mval) { DPRINT5(DB_PARAM,"dgm%d: port%d: setting modem parameters 0x%x was 0x%x\n", unit,pnum,mval,port->omodem); port->omodem=mval; fepcmd(port, SETMODEM, (unsigned)mval, RTS|DTR, 0, 1); } if(port->fepstartc!=t->c_cc[VSTART] || port->fepstopc!=t->c_cc[VSTOP]) { DPRINT5(DB_PARAM,"dgm%d: port%d: set startc=%d, stopc=%d\n",unit,pnum,t->c_cc[VSTART],t->c_cc[VSTOP]); port->fepstartc=t->c_cc[VSTART]; port->fepstopc=t->c_cc[VSTOP]; fepcmd(port, SONOFFC, port->fepstartc, port->fepstopc, 0, 1); } bmws_set(ws); splx(cs); return 0; } static void dgmstart(tp) struct tty *tp; { int unit; int pnum; struct dgm_p *port; struct dgm_softc *sc; volatile struct board_chan *bc; int head, tail; int size, ocount; int s; int wmask; BoardMemWinState ws=bmws_get(); unit=MINOR_TO_UNIT(minor(tp->t_dev)); pnum=MINOR_TO_PORT(minor(tp->t_dev)); sc=&dgm_softc[unit]; port=&sc->ports[pnum]; bc=port->brdchan; wmask=port->txbufsize-1; s=spltty(); while( tp->t_outq.c_cc!=0 ) { int cs; #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ ttwwakeup(tp); #else if(tp->t_outq.c_cc <= tp->t_lowat) { if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } /*selwakeup(&tp->t_wsel);*/ } #endif cs=splclock(); setwin(sc,0); head=bc->tin & wmask; /*HERE*/ do { tail=bc->tout; } while (tail != bc->tout); tail=bc->tout & wmask; DPRINT5(DB_WR,"dgm%d: port%d: s tx head=%d tail=%d\n",unit,pnum,head,tail); #ifdef LEAVE_FREE_CHARS if(tail>head) { size=tail-head-LEAVE_FREE_CHARS; if (size <0) size==0; } else { size=port->txbufsize-head; if(tail+port->txbufsize < head) size==0; } } #else if(tail>head) size=tail-head-1; else { size=port->txbufsize-head/*-1*/; if(tail==0) size--; } #endif if(size==0) { bc->iempty=1; bc->ilow=1; splx(cs); bmws_set(ws); tp->t_state|=TS_BUSY; splx(s); return; } towin(sc,port->txwin); ocount=q_to_b(&tp->t_outq, port->txptr+head, size); head+=ocount; if(head>=port->txbufsize) head-=port->txbufsize; setwin(sc,0); bc->tin=head; DPRINT5(DB_WR,"dgm%d: port%d: tx avail=%d count=%d\n",unit,pnum,size,ocount); hidewin(sc); splx(cs); } bmws_set(ws); splx(s); #ifndef TS_ASLEEP /* post 2.0.5 FreeBSD */ if(tp->t_state & TS_BUSY) { tp->t_state&=~TS_BUSY; linesw[tp->t_line].l_start(tp); ttwwakeup(tp); } #else if(tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } tp->t_state&=~TS_BUSY; #endif } void dgmstop(tp, rw) struct tty *tp; int rw; { int unit; int pnum; struct dgm_p *port; struct dgm_softc *sc; volatile struct board_chan *bc; int head; int s; BoardMemWinState ws=bmws_get(); unit=MINOR_TO_UNIT(minor(tp->t_dev)); pnum=MINOR_TO_PORT(minor(tp->t_dev)); sc=&dgm_softc[unit]; port=&sc->ports[pnum]; bc=port->brdchan; DPRINT3(DB_WR,"dgm%d: port%d: stop\n",port->unit, port->pnum); s = spltty(); setwin(sc,0); if (rw & FWRITE) { /* clear output queue */ bc->tout=bc->tin=0; bc->ilow=0;bc->iempty=0; } if (rw & FREAD) { /* clear input queue */ bc->rout=bc->rin; bc->idata=1; } hidewin(sc); bmws_set(ws); splx(s); dgmstart(tp); } struct tty * dgmdevtotty(dev) dev_t dev; { int mynor, pnum, unit; struct dgm_softc *sc; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NDGM) return (NULL); pnum = MINOR_TO_PORT(mynor); sc = &dgm_softc[unit]; if (pnum >= sc->numports) return (NULL); return (&sc->ttys[pnum]); } static void fepcmd(port, cmd, op1, op2, ncmds, bytecmd) struct dgm_p *port; unsigned cmd, op1, op2, ncmds, bytecmd; { struct dgm_softc *sc=&dgm_softc[port->unit]; u_char *mem=sc->vmem; unsigned tail, head; int count, n; if(port->status==DISABLED) { printf("dgm%d: port%d: FEP command on disabled port\n", port->unit, port->pnum); return; } /* setwin(sc,0); Require this to be set by caller */ head=sc->mailbox->cin; if(head>=(FEP_CMAX-FEP_CSTART) || (head & 3)) { printf("dgm%d: port%d: wrong pointer head of command queue : 0x%x\n", port->unit, port->pnum, head); return; } mem[head+FEP_CSTART+0]=cmd; mem[head+FEP_CSTART+1]=port->pnum; if(bytecmd) { mem[head+FEP_CSTART+2]=op1; mem[head+FEP_CSTART+3]=op2; } else { mem[head+FEP_CSTART+2]=op1&0xff; mem[head+FEP_CSTART+3]=(op1>>8)&0xff; } DPRINT7(DB_FEP,"dgm%d: port%d: %s cmd=0x%x op1=0x%x op2=0x%x\n", port->unit, port->pnum, (bytecmd)?"byte":"word", cmd, mem[head+FEP_CSTART+2], mem[head+FEP_CSTART+3]); head=(head+4) & (FEP_CMAX-FEP_CSTART-4); sc->mailbox->cin=head; count=FEPTIMEOUT; while (count-- != 0) { head=sc->mailbox->cin; tail=sc->mailbox->cout; n = (head-tail) & (FEP_CMAX-FEP_CSTART-4); if(n <= ncmds * (sizeof(ushort)*4)) return; } printf("dgm%d(%d): timeout on FEP cmd=0x%x\n", port->unit, port->pnum, cmd); } static void disc_optim(tp, t) struct tty *tp; struct termios *t; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; } static dgm_devsw_installed = 0; static void dgm_drvinit(void *unused) { dev_t dev; if( ! dgm_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&dgm_cdevsw, NULL); dgm_devsw_installed = 1; } } SYSINIT(dgmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,dgm_drvinit,NULL) int fi(){ return 0; } #endif /* NDGM > 0 */ diff --git a/sys/i386/i386/cons.c b/sys/i386/i386/cons.c index fceb4dd92449..1482c6185557 100644 --- a/sys/i386/i386/cons.c +++ b/sys/i386/i386/cons.c @@ -1,446 +1,449 @@ /* * Copyright (c) 1988 University of Utah. * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)cons.c 7.2 (Berkeley) 5/9/91 - * $Id: cons.c,v 1.57 1998/03/28 10:32:56 bde Exp $ + * $Id: cons.c,v 1.58 1998/06/07 17:09:58 dfr Exp $ */ #include "opt_devfs.h" #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #include #include #include #include #include #include /* XXX this should be config(8)ed. */ #include "sc.h" #include "vt.h" #include "sio.h" static struct consdev constab[] = { #if NSC > 0 { sccnprobe, sccninit, sccngetc, sccncheckc, sccnputc }, #endif #if NVT > 0 { pccnprobe, pccninit, pccngetc, pccncheckc, pccnputc }, #endif #if NSIO > 0 { siocnprobe, siocninit, siocngetc, siocncheckc, siocnputc }, #endif { 0 }, }; static d_open_t cnopen; static d_close_t cnclose; static d_read_t cnread; static d_write_t cnwrite; static d_ioctl_t cnioctl; static d_poll_t cnpoll; -#define CDEV_MAJOR 0 -static struct cdevsw cn_cdevsw = - { cnopen, cnclose, cnread, cnwrite, /*0*/ - cnioctl, nullstop, nullreset, nodevtotty,/* console */ - cnpoll, nommap, NULL, "console", NULL, -1 }; +#define CDEV_MAJOR 0 +static struct cdevsw cn_cdevsw = { + cnopen, cnclose, cnread, cnwrite, + cnioctl, nullstop, nullreset, nodevtotty, + cnpoll, nommap, NULL, "console", + NULL, -1, nodump, nopsize, + D_TTY, +}; static dev_t cn_dev_t; /* seems to be never really used */ SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLTYPE_OPAQUE|CTLFLAG_RD, &cn_dev_t, sizeof cn_dev_t, "T,dev_t", ""); static int cn_mute; int cons_unavail = 0; /* XXX: * physical console not available for * input (i.e., it is in graphics mode) */ static u_char cn_is_open; /* nonzero if logical console is open */ static int openmode, openflag; /* how /dev/console was openned */ static u_char cn_phys_is_open; /* nonzero if physical device is open */ static d_close_t *cn_phys_close; /* physical device close function */ static d_open_t *cn_phys_open; /* physical device open function */ static struct consdev *cn_tab; /* physical console device info */ static struct tty *cn_tp; /* physical console tty struct */ #ifdef DEVFS static void *cn_devfs_token; /* represents the devfs entry */ #endif /* DEVFS */ void cninit() { struct consdev *best_cp, *cp; /* * Find the first console with the highest priority. */ best_cp = NULL; for (cp = constab; cp->cn_probe; cp++) { (*cp->cn_probe)(cp); if (cp->cn_pri > CN_DEAD && (best_cp == NULL || cp->cn_pri > best_cp->cn_pri)) best_cp = cp; } /* * Check if we should mute the console (for security reasons perhaps) * It can be changes dynamically using sysctl kern.consmute * once we are up and going. * */ cn_mute = ((boothowto & (RB_MUTE |RB_SINGLE |RB_VERBOSE |RB_ASKNAME |RB_CONFIG)) == RB_MUTE); /* * If no console, give up. */ if (best_cp == NULL) { cn_tab = best_cp; return; } /* * Initialize console, then attach to it. This ordering allows * debugging using the previous console, if any. * XXX if there was a previous console, then its driver should * be informed when we forget about it. */ (*best_cp->cn_init)(best_cp); cn_tab = best_cp; } void cninit_finish() { struct cdevsw *cdp; if ((cn_tab == NULL) || cn_mute) return; /* * Hook the open and close functions. */ cdp = cdevsw[major(cn_tab->cn_dev)]; cn_phys_close = cdp->d_close; cdp->d_close = cnclose; cn_phys_open = cdp->d_open; cdp->d_open = cnopen; cn_tp = (*cdp->d_devtotty)(cn_tab->cn_dev); cn_dev_t = cn_tp->t_dev; } static void cnuninit(void) { struct cdevsw *cdp; if (cn_tab == NULL) return; /* * Unhook the open and close functions. */ cdp = cdevsw[major(cn_tab->cn_dev)]; cdp->d_close = cn_phys_close; cn_phys_close = NULL; cdp->d_open = cn_phys_open; cn_phys_open = NULL; cn_tp = NULL; cn_dev_t = 0; } /* * User has changed the state of the console muting. * This may require us to open or close the device in question. */ static int sysctl_kern_consmute SYSCTL_HANDLER_ARGS { int error; int ocn_mute; ocn_mute = cn_mute; error = sysctl_handle_int(oidp, &cn_mute, 0, req); if((error == 0) && (cn_tab != NULL) && (req->newptr != NULL)) { if(ocn_mute && !cn_mute) { /* * going from muted to unmuted.. open the physical dev * if the console has been openned */ cninit_finish(); if(cn_is_open) /* XXX curproc is not what we want really */ error = cnopen(cn_dev_t, openflag, openmode, curproc); /* if it failed, back it out */ if ( error != 0) cnuninit(); } else if (!ocn_mute && cn_mute) { /* * going from unmuted to muted.. close the physical dev * if it's only open via /dev/console */ if(cn_is_open) error = cnclose(cn_dev_t, openflag, openmode, curproc); if ( error == 0) cnuninit(); } if (error != 0) { /* * back out the change if there was an error */ cn_mute = ocn_mute; } } return (error); } SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW, 0, sizeof cn_mute, sysctl_kern_consmute, "I", ""); static int cnopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { dev_t cndev, physdev; int retval = 0; if (cn_tab == NULL) return (0); cndev = cn_tab->cn_dev; physdev = (major(dev) == major(cndev) ? dev : cndev); /* * If mute is active, then non console opens don't get here * so we don't need to check for that. They * bypass this and go straight to the device. */ if(!cn_mute) retval = (*cn_phys_open)(physdev, flag, mode, p); if (retval == 0) { /* * check if we openned it via /dev/console or * via the physical entry (e.g. /dev/sio0). */ if (dev == cndev) cn_phys_is_open = 1; else if (physdev == cndev) { openmode = mode; openflag = flag; cn_is_open = 1; } } return (retval); } static int cnclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { dev_t cndev; if (cn_tab == NULL) return (0); cndev = cn_tab->cn_dev; /* * act appropriatly depending on whether it's /dev/console * or the pysical device (e.g. /dev/sio) that's being closed. * in either case, don't actually close the device unless * both are closed. */ if (dev == cndev) { /* the physical device is about to be closed */ cn_phys_is_open = 0; if (cn_is_open) { if (cn_tp) { /* perform a ttyhalfclose() */ /* reset session and proc group */ cn_tp->t_pgrp = NULL; cn_tp->t_session = NULL; } return (0); } } else if (major(dev) != major(cndev)) { /* the logical console is about to be closed */ cn_is_open = 0; if (cn_phys_is_open) return (0); dev = cndev; } if(cn_phys_close) return ((*cn_phys_close)(dev, flag, mode, p)); return (0); } static int cnread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { if ((cn_tab == NULL) || cn_mute) return (0); dev = cn_tab->cn_dev; return ((*cdevsw[major(dev)]->d_read)(dev, uio, flag)); } static int cnwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { if ((cn_tab == NULL) || cn_mute) { uio->uio_resid = 0; /* dump the data */ return (0); } if (constty) dev = constty->t_dev; else dev = cn_tab->cn_dev; return ((*cdevsw[major(dev)]->d_write)(dev, uio, flag)); } static int cnioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { int error; if ((cn_tab == NULL) || cn_mute) return (0); /* * Superuser can always use this to wrest control of console * output from the "virtual" console. */ if (cmd == TIOCCONS && constty) { error = suser(p->p_ucred, (u_short *) NULL); if (error) return (error); constty = NULL; return (0); } dev = cn_tab->cn_dev; return ((*cdevsw[major(dev)]->d_ioctl)(dev, cmd, data, flag, p)); } static int cnpoll(dev, events, p) dev_t dev; int events; struct proc *p; { if ((cn_tab == NULL) || cn_mute) return (1); dev = cn_tab->cn_dev; return ((*cdevsw[major(dev)]->d_poll)(dev, events, p)); } int cngetc() { int c; if ((cn_tab == NULL) || cn_mute) return (-1); c = (*cn_tab->cn_getc)(cn_tab->cn_dev); if (c == '\r') c = '\n'; /* console input is always ICRNL */ return (c); } int cncheckc() { if ((cn_tab == NULL) || cn_mute) return (-1); return ((*cn_tab->cn_checkc)(cn_tab->cn_dev)); } void cnputc(c) register int c; { if ((cn_tab == NULL) || cn_mute) return; if (c) { if (c == '\n') (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); (*cn_tab->cn_putc)(cn_tab->cn_dev, c); } } static cn_devsw_installed = 0; static void cn_drvinit(void *unused) { dev_t dev; if( ! cn_devsw_installed ) { dev = makedev(CDEV_MAJOR,0); cdevsw_add(&dev,&cn_cdevsw,NULL); cn_devsw_installed = 1; #ifdef DEVFS cn_devfs_token = devfs_add_devswf(&cn_cdevsw, 0, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "console"); #endif } } SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL) diff --git a/sys/i386/isa/cx.c b/sys/i386/isa/cx.c index deadd56fd531..b4fb32460fb7 100644 --- a/sys/i386/isa/cx.c +++ b/sys/i386/isa/cx.c @@ -1,980 +1,982 @@ /* * Cronyx-Sigma adapter driver for FreeBSD. * Supports PPP/HDLC protocol in synchronous mode, * and asyncronous channels with full modem control. * * Copyright (C) 1994 Cronyx Ltd. * Author: Serge Vakulenko, * * This software is distributed with NO WARRANTIES, not even the implied * warranties for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * Authors grant any other persons or organisations permission to use * or modify this software as long as this message is kept with the software, * all derivative works or modified versions. * * Version 1.9, Wed Oct 4 18:58:15 MSK 1995 */ #undef DEBUG #include "cx.h" #if NCX > 0 #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ # if __FreeBSD__ < 2 # include # define RB_GETC(q) getc(q) # endif #endif #ifdef __bsdi__ # include # include # define tsleep(tp,pri,msg,x) ((tp)->t_state |= TS_WOPEN,\ ttysleep (tp, (caddr_t)&tp->t_rawq, pri, msg, x)) #endif #if !defined (__FreeBSD__) || __FreeBSD__ >= 2 # define t_out t_outq # define RB_LEN(q) ((q).c_cc) # define RB_GETC(q) getc(&q) #ifndef TSA_CARR_ON /* FreeBSD 2.x before not long after 2.0.5 */ # define TSA_CARR_ON(tp) tp # define TSA_OLOWAT(q) ((caddr_t)&(q)->t_out) #endif #endif #include #include /* XXX imported from if_cx.c. */ void cxswitch (cx_chan_t *c, cx_soft_opt_t new); /* XXX exported. */ void cxmint (cx_chan_t *c); int cxrinta (cx_chan_t *c); void cxtinta (cx_chan_t *c); timeout_t cxtimeout; #ifdef DEBUG # define print(s) printf s #else # define print(s) {/*void*/} #endif #define DMABUFSZ (6*256) /* buffer size */ #define BYTE *(unsigned char*)& #define UNIT(u) ((u) & 077) #define UNIT_CTL 077 extern cx_board_t cxboard [NCX]; /* adapter state structures */ extern cx_chan_t *cxchan [NCX*NCHAN]; /* unit to channel struct pointer */ #if __FreeBSD__ >= 2 static struct tty cx_tty [NCX*NCHAN]; /* tty data */ static d_open_t cxopen; static d_close_t cxclose; static d_read_t cxread; static d_write_t cxwrite; static d_ioctl_t cxioctl; static d_stop_t cxstop; static d_devtotty_t cxdevtotty; -# define CDEV_MAJOR 42 - -/* Don't make this static. if_cx.c uses it. */ -struct cdevsw cx_cdevsw = - { cxopen, cxclose, cxread, cxwrite, /*42*/ - cxioctl, cxstop, nullreset, cxdevtotty,/* cronyx */ - ttpoll, nommap, NULL, "cx", NULL, -1 }; +#define CDEV_MAJOR 42 +/* Don't make this static, since if_cx.c uses it. */ +struct cdevsw cx_cdevsw = { + cxopen, cxclose, cxread, cxwrite, + cxioctl, cxstop, nullreset, cxdevtotty, + ttpoll, nommap, NULL, "cx", + NULL, -1, nodump, nopsize, + D_TTY, +}; #else struct tty *cx_tty [NCX*NCHAN]; /* tty data */ #endif static void cxoproc (struct tty *tp); static int cxparam (struct tty *tp, struct termios *t); int cxopen (dev_t dev, int flag, int mode, struct proc *p) { int unit = UNIT (dev); cx_chan_t *c = cxchan[unit]; unsigned short port; struct tty *tp; int error = 0; if (unit == UNIT_CTL) { print (("cx: cxopen /dev/cronyx\n")); return (0); } if (unit >= NCX*NCHAN || !c || c->type==T_NONE) return (ENXIO); port = c->chip->port; print (("cx%d.%d: cxopen unit=%d\n", c->board->num, c->num, unit)); if (c->mode != M_ASYNC) return (EBUSY); if (! c->ttyp) { #ifdef __FreeBSD__ #if __FreeBSD__ >= 2 c->ttyp = &cx_tty[unit]; #else c->ttyp = cx_tty[unit] = ttymalloc (cx_tty[unit]); #endif #else MALLOC (cx_tty[unit], struct tty*, sizeof (struct tty), M_DEVBUF, M_WAITOK); bzero (cx_tty[unit], sizeof (*cx_tty[unit])); c->ttyp = cx_tty[unit]; #endif c->ttyp->t_oproc = cxoproc; c->ttyp->t_param = cxparam; } #ifdef __bsdi__ if (! c->ttydev) { MALLOC (c->ttydev, struct ttydevice_tmp*, sizeof (struct ttydevice_tmp), M_DEVBUF, M_WAITOK); bzero (c->ttydev, sizeof (*c->ttydev)); strcpy (c->ttydev->tty_name, "cx"); c->ttydev->tty_unit = unit; c->ttydev->tty_base = unit; c->ttydev->tty_count = 1; c->ttydev->tty_ttys = c->ttyp; tty_attach (c->ttydev); } #endif tp = c->ttyp; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE) && p->p_ucred->cr_uid != 0) return (EBUSY); if (! (tp->t_state & TS_ISOPEN)) { ttychars (tp); if (tp->t_ispeed == 0) { #ifdef __bsdi__ tp->t_termios = deftermios; #else tp->t_iflag = 0; tp->t_oflag = 0; tp->t_lflag = 0; tp->t_cflag = CREAD | CS8 | HUPCL; tp->t_ispeed = c->rxbaud; tp->t_ospeed = c->txbaud; #endif } cxparam (tp, &tp->t_termios); ttsetwater (tp); } spltty (); if (! (tp->t_state & TS_ISOPEN)) { /* * Compute optimal receiver buffer length. * The best choice is rxbaud/400. * Make it even, to avoid byte-wide DMA transfers. * -------------------------- * Baud rate Buffer length * -------------------------- * 300 4 * 1200 4 * 9600 24 * 19200 48 * 38400 96 * 57600 192 * 115200 288 * -------------------------- */ int rbsz = (c->rxbaud + 800 - 1) / 800 * 2; if (rbsz < 4) rbsz = 4; else if (rbsz > DMABUFSZ) rbsz = DMABUFSZ; /* Initialize channel, enable receiver. */ cx_cmd (port, CCR_INITCH | CCR_ENRX); cx_cmd (port, CCR_INITCH | CCR_ENRX); /* Start receiver. */ outw (ARBCNT(port), rbsz); outw (BRBCNT(port), rbsz); outw (ARBSTS(port), BSTS_OWN24); outw (BRBSTS(port), BSTS_OWN24); /* Enable interrupts. */ outb (IER(port), IER_RXD | IER_RET | IER_TXD | IER_MDM); cx_chan_dtr (c, 1); cx_chan_rts (c, 1); } if (cx_chan_cd (c)) (*linesw[tp->t_line].l_modem)(tp, 1); if (! (flag & O_NONBLOCK)) { /* Lock the channel against cxconfig while we are * waiting for carrier. */ c->sopt.lock = 1; while (!(tp->t_cflag & CLOCAL) && !(tp->t_state & TS_CARR_ON)) if ((error = tsleep (TSA_CARR_ON(tp), TTIPRI | PCATCH, "cxdcd", 0))) break; c->sopt.lock = 0; /* Unlock the channel. */ } print (("cx%d.%d: cxopen done csr=%b\n", c->board->num, c->num, inb(CSR(c->chip->port)), CSRA_BITS)); spl0 (); if (error) return (error); #if __FreeBSD__ >= 2 error = (*linesw[tp->t_line].l_open) (dev, tp); #else error = (*linesw[tp->t_line].l_open) (dev, tp, 0); #endif return (error); } int cxclose (dev_t dev, int flag, int mode, struct proc *p) { int unit = UNIT (dev); cx_chan_t *c = cxchan[unit]; struct tty *tp; int s; if (unit == UNIT_CTL) return (0); tp = c->ttyp; (*linesw[tp->t_line].l_close) (tp, flag); /* Disable receiver. * Transmitter continues sending the queued data. */ s = spltty (); outb (CAR(c->chip->port), c->num & 3); outb (IER(c->chip->port), IER_TXD | IER_MDM); cx_cmd (c->chip->port, CCR_DISRX); /* Clear DTR and RTS. */ if ((tp->t_cflag & HUPCL) || ! (tp->t_state & TS_ISOPEN)) { cx_chan_dtr (c, 0); cx_chan_rts (c, 0); } /* Stop sending break. */ if (c->brk == BRK_SEND) { c->brk = BRK_STOP; if (! (tp->t_state & TS_BUSY)) cxoproc (tp); } splx (s); ttyclose (tp); return (0); } int cxread (dev_t dev, struct uio *uio, int flag) { int unit = UNIT (dev); struct tty *tp; if (unit == UNIT_CTL) return (EIO); tp = cxchan[unit]->ttyp; return ((*linesw[tp->t_line].l_read) (tp, uio, flag)); } int cxwrite (dev_t dev, struct uio *uio, int flag) { int unit = UNIT (dev); struct tty *tp; if (unit == UNIT_CTL) return (EIO); tp = cxchan[unit]->ttyp; return ((*linesw[tp->t_line].l_write) (tp, uio, flag)); } int cxioctl (dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { int unit = UNIT (dev); cx_chan_t *c, *m; cx_stat_t *st; struct tty *tp; int error, s; unsigned char msv; struct ifnet *master; if (unit == UNIT_CTL) { /* Process an ioctl request on /dev/cronyx */ cx_options_t *o = (cx_options_t*) data; if (o->board >= NCX || o->channel >= NCHAN) return (EINVAL); c = &cxboard[o->board].chan[o->channel]; if (c->type == T_NONE) return (ENXIO); switch (cmd) { default: return (EINVAL); case CXIOCSETMODE: print (("cx%d.%d: CXIOCSETMODE\n", o->board, o->channel)); if (c->type == T_NONE) return (EINVAL); if (c->type == T_ASYNC && o->mode != M_ASYNC) return (EINVAL); if (o->mode == M_ASYNC) switch (c->type) { case T_SYNC_RS232: case T_SYNC_V35: case T_SYNC_RS449: return (EINVAL); } /* Somebody is waiting for carrier? */ if (c->sopt.lock) return (EBUSY); /* /dev/ttyXX is already opened by someone? */ if (c->mode == M_ASYNC && c->ttyp && (c->ttyp->t_state & TS_ISOPEN)) return (EBUSY); /* Network interface is up? */ if (c->mode != M_ASYNC && (c->ifp->if_flags & IFF_UP)) return (EBUSY); /* Find the master interface. */ master = *o->master ? ifunit (o->master) : c->ifp; if (! master) return (EINVAL); m = cxchan[master->if_unit]; /* Leave the previous master queue. */ if (c->master != c->ifp) { cx_chan_t *p = cxchan[c->master->if_unit]; for (; p; p=p->slaveq) if (p->slaveq == c) p->slaveq = c->slaveq; } /* Set up new master. */ c->master = master; c->slaveq = 0; /* Join the new master queue. */ if (c->master != c->ifp) { c->slaveq = m->slaveq; m->slaveq = c; } c->mode = o->mode; c->rxbaud = o->rxbaud; c->txbaud = o->txbaud; c->opt = o->opt; c->aopt = o->aopt; c->hopt = o->hopt; c->bopt = o->bopt; c->xopt = o->xopt; switch (c->num) { case 0: c->board->if0type = o->iftype; break; case 8: c->board->if8type = o->iftype; break; } s = spltty (); cxswitch (c, o->sopt); cx_setup_chan (c); outb (IER(c->chip->port), 0); splx (s); break; case CXIOCGETSTAT: st = (cx_stat_t*) data; st->rintr = c->stat->rintr; st->tintr = c->stat->tintr; st->mintr = c->stat->mintr; st->ibytes = c->stat->ibytes; st->ipkts = c->stat->ipkts; st->ierrs = c->stat->ierrs; st->obytes = c->stat->obytes; st->opkts = c->stat->opkts; st->oerrs = c->stat->oerrs; break; case CXIOCGETMODE: print (("cx%d.%d: CXIOCGETMODE\n", o->board, o->channel)); o->type = c->type; o->mode = c->mode; o->rxbaud = c->rxbaud; o->txbaud = c->txbaud; o->opt = c->opt; o->aopt = c->aopt; o->hopt = c->hopt; o->bopt = c->bopt; o->xopt = c->xopt; o->sopt = c->sopt; switch (c->num) { case 0: o->iftype = c->board->if0type; break; case 8: o->iftype = c->board->if8type; break; } if (c->master != c->ifp) sprintf (o->master, "%s%d", c->master->if_name, c->master->if_unit); else *o->master = 0; break; } return (0); } c = cxchan[unit]; tp = c->ttyp; if (! tp) return (EINVAL); #if __FreeBSD__ >= 2 error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag, p); #else error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag); #endif if (error != ENOIOCTL) return (error); error = ttioctl (tp, cmd, data, flag); if (error != ENOIOCTL) return (error); s = spltty (); switch (cmd) { default: splx (s); return (ENOTTY); case TIOCSBRK: /* Start sending line break */ c->brk = BRK_SEND; if (! (tp->t_state & TS_BUSY)) cxoproc (tp); break; case TIOCCBRK: /* Stop sending line break */ c->brk = BRK_STOP; if (! (tp->t_state & TS_BUSY)) cxoproc (tp); break; case TIOCSDTR: /* Set DTR */ cx_chan_dtr (c, 1); break; case TIOCCDTR: /* Clear DTR */ cx_chan_dtr (c, 0); break; case TIOCMSET: /* Set DTR/RTS */ cx_chan_dtr (c, (*(int*)data & TIOCM_DTR) ? 1 : 0); cx_chan_rts (c, (*(int*)data & TIOCM_RTS) ? 1 : 0); break; case TIOCMBIS: /* Add DTR/RTS */ if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 1); if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 1); break; case TIOCMBIC: /* Clear DTR/RTS */ if (*(int*)data & TIOCM_DTR) cx_chan_dtr (c, 0); if (*(int*)data & TIOCM_RTS) cx_chan_rts (c, 0); break; case TIOCMGET: /* Get modem status */ msv = inb (MSVR(c->chip->port)); *(int*)data = TIOCM_LE; /* always enabled while open */ if (msv & MSV_DSR) *(int*)data |= TIOCM_DSR; if (msv & MSV_CTS) *(int*)data |= TIOCM_CTS; if (msv & MSV_CD) *(int*)data |= TIOCM_CD; if (c->dtr) *(int*)data |= TIOCM_DTR; if (c->rts) *(int*)data |= TIOCM_RTS; break; } splx (s); return (0); } /* * Fill transmitter buffer with data. */ static void cxout (cx_chan_t *c, char b) { unsigned char *buf, *p, sym; unsigned short port = c->chip->port, len = 0, cnt_port, sts_port; struct tty *tp = c->ttyp; if (! tp) return; /* Choose the buffer. */ if (b == 'A') { buf = c->atbuf; cnt_port = ATBCNT(port); sts_port = ATBSTS(port); } else { buf = c->btbuf; cnt_port = BTBCNT(port); sts_port = BTBSTS(port); } /* Is it busy? */ if (inb (sts_port) & BSTS_OWN24) { tp->t_state |= TS_BUSY; return; } switch (c->brk) { case BRK_SEND: *buf++ = 0; /* extended transmit command */ *buf++ = 0x81; /* send break */ *buf++ = 0; /* extended transmit command */ *buf++ = 0x82; /* insert delay */ *buf++ = 250; /* 1/4 of second */ *buf++ = 0; /* extended transmit command */ *buf++ = 0x82; /* insert delay */ *buf++ = 250; /* + 1/4 of second */ len = 8; c->brk = BRK_IDLE; break; case BRK_STOP: *buf++ = 0; /* extended transmit command */ *buf++ = 0x83; /* stop break */ len = 2; c->brk = BRK_IDLE; break; case BRK_IDLE: p = buf; if (tp->t_iflag & IXOFF) while (RB_LEN (tp->t_out) && pt_out); /* Send XON/XOFF out of band. */ if (sym == tp->t_cc[VSTOP]) { outb (STCR(port), STC_SNDSPC|STC_SSPC_2); continue; } if (sym == tp->t_cc[VSTART]) { outb (STCR(port), STC_SNDSPC|STC_SSPC_1); continue; } /* Duplicate NULLs in ETC mode. */ if (! sym) *p++ = 0; *p++ = sym; } else while (RB_LEN (tp->t_out) && pt_out); /* Duplicate NULLs in ETC mode. */ if (! sym) *p++ = 0; *p++ = sym; } len = p - buf; break; } /* Start transmitter. */ if (len) { outw (cnt_port, len); outb (sts_port, BSTS_INTR | BSTS_OWN24); c->stat->obytes += len; tp->t_state |= TS_BUSY; print (("cx%d.%d: out %d bytes to %c\n", c->board->num, c->num, len, b)); } } void cxoproc (struct tty *tp) { int unit = UNIT (tp->t_dev); cx_chan_t *c = cxchan[unit]; unsigned short port = c->chip->port; int s = spltty (); /* Set current channel number */ outb (CAR(port), c->num & 3); if (! (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))) { /* Start transmitter. */ if (! (inb (CSR(port)) & CSRA_TXEN)) cx_cmd (port, CCR_ENTX); /* Determine the buffer order. */ if (inb (DMABSTS(port)) & DMABSTS_NTBUF) { cxout (c, 'B'); cxout (c, 'A'); } else { cxout (c, 'A'); cxout (c, 'B'); } } #ifndef TS_ASLEEP /* FreeBSD some time after 2.0.5 */ ttwwakeup(tp); #else if (RB_LEN (tp->t_out) <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(TSA_OLOWAT(tp)); } selwakeup(&tp->t_wsel); } #endif splx (s); } static int cxparam (struct tty *tp, struct termios *t) { int unit = UNIT (tp->t_dev); cx_chan_t *c = cxchan[unit]; unsigned short port = c->chip->port; int clock, period, s; cx_cor1_async_t cor1; if (t->c_ospeed == 0) { /* Clear DTR and RTS. */ s = spltty (); cx_chan_dtr (c, 0); cx_chan_rts (c, 0); splx (s); print (("cx%d.%d: cxparam (hangup)\n", c->board->num, c->num)); return (0); } print (("cx%d.%d: cxparam\n", c->board->num, c->num)); /* Check requested parameters. */ if (t->c_ospeed < 300 || t->c_ospeed > 256*1024) return(EINVAL); if (t->c_ispeed && (t->c_ispeed < 300 || t->c_ispeed > 256*1024)) return(EINVAL); #ifdef __bsdi__ /* CLOCAL flag set -- wakeup everybody who waits for CD. */ /* FreeBSD does this themselves. */ if (! (tp->t_cflag & CLOCAL) && (t->c_cflag & CLOCAL)) wakeup ((caddr_t) &tp->t_rawq); #endif /* And copy them to tty and channel structures. */ c->rxbaud = tp->t_ispeed = t->c_ispeed; c->txbaud = tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; /* Set character length and parity mode. */ BYTE cor1 = 0; switch (t->c_cflag & CSIZE) { default: case CS8: cor1.charlen = 7; break; case CS7: cor1.charlen = 6; break; case CS6: cor1.charlen = 5; break; case CS5: cor1.charlen = 4; break; } if (t->c_cflag & PARENB) { cor1.parmode = PARM_NORMAL; cor1.ignpar = 0; cor1.parity = (t->c_cflag & PARODD) ? PAR_ODD : PAR_EVEN; } else { cor1.parmode = PARM_NOPAR; cor1.ignpar = 1; } /* Enable/disable hardware CTS. */ c->aopt.cor2.ctsae = (t->c_cflag & CRTSCTS) ? 1 : 0; /* Handle DSR as CTS. */ c->aopt.cor2.dsrae = (t->c_cflag & CRTSCTS) ? 1 : 0; /* Enable extended transmit command mode. * Unfortunately, there is no other method for sending break. */ c->aopt.cor2.etc = 1; /* Enable/disable hardware XON/XOFF. */ c->aopt.cor2.ixon = (t->c_iflag & IXON) ? 1 : 0; c->aopt.cor2.ixany = (t->c_iflag & IXANY) ? 1 : 0; /* Set the number of stop bits. */ if (t->c_cflag & CSTOPB) c->aopt.cor3.stopb = STOPB_2; else c->aopt.cor3.stopb = STOPB_1; /* Disable/enable passing XON/XOFF chars to the host. */ c->aopt.cor3.scde = (t->c_iflag & IXON) ? 1 : 0; c->aopt.cor3.flowct = (t->c_iflag & IXON) ? FLOWCC_NOTPASS : FLOWCC_PASS; c->aopt.schr1 = t->c_cc[VSTART]; /* XON */ c->aopt.schr2 = t->c_cc[VSTOP]; /* XOFF */ /* Set current channel number. */ s = spltty (); outb (CAR(port), c->num & 3); /* Set up receiver clock values. */ cx_clock (c->chip->oscfreq, c->rxbaud, &clock, &period); c->opt.rcor.clk = clock; outb (RCOR(port), BYTE c->opt.rcor); outb (RBPR(port), period); /* Set up transmitter clock values. */ cx_clock (c->chip->oscfreq, c->txbaud, &clock, &period); c->opt.tcor.clk = clock; c->opt.tcor.ext1x = 0; outb (TCOR(port), BYTE c->opt.tcor); outb (TBPR(port), period); outb (COR2(port), BYTE c->aopt.cor2); outb (COR3(port), BYTE c->aopt.cor3); outb (SCHR1(port), c->aopt.schr1); outb (SCHR2(port), c->aopt.schr2); if (BYTE c->aopt.cor1 != BYTE cor1) { BYTE c->aopt.cor1 = BYTE cor1; outb (COR1(port), BYTE c->aopt.cor1); /* Any change to COR1 require reinitialization. */ /* Unfortunately, it may cause transmitter glitches... */ cx_cmd (port, CCR_INITCH); } splx (s); return (0); } struct tty *cxdevtotty (dev_t dev) { int unit = UNIT(dev); if (unit == UNIT_CTL || unit >= NCX*NCHAN) return (0); return (cxchan[unit]->ttyp); } /* * Stop output on a line */ void cxstop (struct tty *tp, int flag) { cx_chan_t *c = cxchan[UNIT(tp->t_dev)]; unsigned short port = c->chip->port; int s = spltty (); if (tp->t_state & TS_BUSY) { print (("cx%d.%d: cxstop\n", c->board->num, c->num)); /* Set current channel number */ outb (CAR(port), c->num & 3); /* Stop transmitter */ cx_cmd (port, CCR_DISTX); } splx (s); } /* * Handle receive interrupts, including receive errors and * receive timeout interrupt. */ int cxrinta (cx_chan_t *c) { unsigned short port = c->chip->port; unsigned short len = 0, risr = inw (RISR(port)), reoir = 0; struct tty *tp = c->ttyp; /* Compute optimal receiver buffer length. */ int rbsz = (c->rxbaud + 800 - 1) / 800 * 2; if (rbsz < 4) rbsz = 4; else if (rbsz > DMABUFSZ) rbsz = DMABUFSZ; if (risr & RISA_TIMEOUT) { unsigned long rcbadr = (unsigned short) inw (RCBADRL(port)) | (long) inw (RCBADRU(port)) << 16; unsigned char *buf = 0; unsigned short cnt_port = 0, sts_port = 0; if (rcbadr >= c->brphys && rcbadr < c->brphys+DMABUFSZ) { buf = c->brbuf; len = rcbadr - c->brphys; cnt_port = BRBCNT(port); sts_port = BRBSTS(port); } else if (rcbadr >= c->arphys && rcbadr < c->arphys+DMABUFSZ) { buf = c->arbuf; len = rcbadr - c->arphys; cnt_port = ARBCNT(port); sts_port = ARBSTS(port); } else printf ("cx%d.%d: timeout: invalid buffer address\n", c->board->num, c->num); if (len) { print (("cx%d.%d: async receive timeout (%d bytes), risr=%b, arbsts=%b, brbsts=%b\n", c->board->num, c->num, len, risr, RISA_BITS, inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS)); c->stat->ibytes += len; if (tp && (tp->t_state & TS_ISOPEN)) { int i; int (*rint)(int, struct tty *) = linesw[tp->t_line].l_rint; for (i=0; iboard->num, c->num, risr, RISA_BITS, inb (ARBSTS(port)), BSTS_BITS, inb (BRBSTS(port)), BSTS_BITS)); if (risr & RIS_BUSERR) { printf ("cx%d.%d: receive bus error\n", c->board->num, c->num); ++c->stat->ierrs; } if (risr & (RIS_OVERRUN | RISA_PARERR | RISA_FRERR | RISA_BREAK)) { int err = 0; if (risr & RISA_PARERR) err |= TTY_PE; if (risr & RISA_FRERR) err |= TTY_FE; #ifdef TTY_OE if (risr & RIS_OVERRUN) err |= TTY_OE; #endif #ifdef TTY_BI if (risr & RISA_BREAK) err |= TTY_BI; #endif print (("cx%d.%d: receive error %x\n", c->board->num, c->num, err)); if (tp && (tp->t_state & TS_ISOPEN)) (*linesw[tp->t_line].l_rint) (err, tp); ++c->stat->ierrs; } /* Discard exception characters. */ if ((risr & RISA_SCMASK) && (tp->t_iflag & IXON)) reoir |= REOI_DISCEXC; /* Handle received data. */ if ((risr & RIS_EOBUF) && tp && (tp->t_state & TS_ISOPEN)) { int (*rint)(int, struct tty *) = linesw[tp->t_line].l_rint; unsigned char *buf; int i; len = (risr & RIS_BB) ? inw(BRBCNT(port)) : inw(ARBCNT(port)); print (("cx%d.%d: async: %d bytes received\n", c->board->num, c->num, len)); c->stat->ibytes += len; buf = (risr & RIS_BB) ? c->brbuf : c->arbuf; for (i=0; ittyp; unsigned short port = c->chip->port; unsigned char tisr = inb (TISR(port)); print (("cx%d.%d: async transmit interrupt, tisr=%b, atbsts=%b, btbsts=%b\n", c->board->num, c->num, tisr, TIS_BITS, inb (ATBSTS(port)), BSTS_BITS, inb (BTBSTS(port)), BSTS_BITS)); if (tisr & TIS_BUSERR) { printf ("cx%d.%d: transmit bus error\n", c->board->num, c->num); ++c->stat->oerrs; } else if (tisr & TIS_UNDERRUN) { printf ("cx%d.%d: transmit underrun error\n", c->board->num, c->num); ++c->stat->oerrs; } if (tp) { tp->t_state &= ~(TS_BUSY | TS_FLUSH); if (tp->t_line) (*linesw[tp->t_line].l_start) (tp); else cxoproc (tp); } } /* * Handle modem interrupt. */ void cxmint (cx_chan_t *c) { unsigned short port = c->chip->port; unsigned char misr = inb (MISR(port)); unsigned char msvr = inb (MSVR(port)); struct tty *tp = c->ttyp; if (c->mode != M_ASYNC) { printf ("cx%d.%d: unexpected modem interrupt, misr=%b, msvr=%b\n", c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS); return; } print (("cx%d.%d: modem interrupt, misr=%b, msvr=%b\n", c->board->num, c->num, misr, MIS_BITS, msvr, MSV_BITS)); /* Ignore DSR events. */ /* Ignore RTC/CTS events, handled by hardware. */ /* Handle carrier detect/loss. */ if (tp && (misr & MIS_CCD)) (*linesw[tp->t_line].l_modem) (tp, (msvr & MSV_CD) != 0); } /* * Recover after lost transmit interrupts. */ void cxtimeout (void *a) { cx_board_t *b; cx_chan_t *c; struct tty *tp; int s; for (b=cxboard; bchan; cchan+NCHAN; ++c) { tp = c->ttyp; if (c->type==T_NONE || c->mode!=M_ASYNC || !tp) continue; s = spltty (); if (tp->t_state & TS_BUSY) { tp->t_state &= ~TS_BUSY; if (tp->t_line) (*linesw[tp->t_line].l_start) (tp); else cxoproc (tp); } splx (s); } timeout (cxtimeout, 0, hz*5); } #if defined(__FreeBSD__) && (__FreeBSD__ > 1 ) static cx_devsw_installed = 0; static void cx_drvinit(void *unused) { dev_t dev; if( ! cx_devsw_installed ) { dev = makedev(CDEV_MAJOR,0); cdevsw_add(&dev,&cx_cdevsw,NULL); cx_devsw_installed = 1; } } SYSINIT(cxdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cx_drvinit,NULL) #endif #endif /* NCX */ diff --git a/sys/i386/isa/cy.c b/sys/i386/isa/cy.c index d10b91158d05..3f765678f0c1 100644 --- a/sys/i386/isa/cy.c +++ b/sys/i386/isa/cy.c @@ -1,2615 +1,2616 @@ /*- * cyclades cyclom-y serial driver * Andrew Herbert , 17 August 1993 * * Copyright (c) 1993 Andrew Herbert. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name Andrew Herbert may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: cy.c,v 1.69 1998/08/19 04:17:38 bde Exp $ + * $Id: cy.c,v 1.70 1998/08/20 05:21:50 bde Exp $ */ #include "opt_compat.h" #include "opt_devfs.h" #include "cy.h" /* * TODO: * Implement BREAK. * Fix overflows when closing line. * Atomic COR change. * Consoles. */ /* * Temporary compile-time configuration options. */ #define RxFifoThreshold (CD1400_RX_FIFO_SIZE / 2) /* Number of chars in the receiver FIFO before an * an interrupt is generated. Should depend on * line speed. Needs to be about 6 on a 486DX33 * for 4 active ports at 115200 bps. Why doesn't * 10 work? */ #define PollMode /* Use polling-based irq service routine, not the * hardware svcack lines. Must be defined for * Cyclom-16Y boards. Less efficient for Cyclom-8Ys, * and stops 4 * 115200 bps from working. */ #undef Smarts /* Enable slightly more CD1400 intelligence. Mainly * the output CR/LF processing, plus we can avoid a * few checks usually done in ttyinput(). * * XXX not fully implemented, and not particularly * worthwhile. */ #undef CyDebug /* Include debugging code (not very expensive). */ /* These will go away. */ #undef SOFT_CTS_OFLOW #define SOFT_HOTCHAR #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #ifdef SMP #define disable_intr() COM_DISABLE_INTR() #define enable_intr() COM_ENABLE_INTR() #endif /* SMP */ /* * Dictionary so that I can name everything *sio* or *com* to compare with * sio.c. There is also lots of ugly formatting and unnecessary ifdefs to * simplify the comparision. These will go away. */ #define LSR_BI CD1400_RDSR_BREAK #define LSR_FE CD1400_RDSR_FE #define LSR_OE CD1400_RDSR_OE #define LSR_PE CD1400_RDSR_PE #define MCR_DTR CD1400_MSVR2_DTR #define MCR_RTS CD1400_MSVR1_RTS #define MSR_CTS CD1400_MSVR2_CTS #define MSR_DCD CD1400_MSVR2_CD #define MSR_DSR CD1400_MSVR2_DSR #define MSR_RI CD1400_MSVR2_RI #define NSIO (NCY * CY_MAX_PORTS) #define comconsole cyconsole #define comdefaultrate cydefaultrate #define com_events cy_events #define comhardclose cyhardclose #define commctl cymctl #define comparam cyparam #define comspeed cyspeed #define comstart cystart #define comwakeup cywakeup #define nsio_tty ncy_tty #define p_com_addr p_cy_addr #define sioattach cyattach #define sioclose cyclose #define siodevtotty cydevtotty #define siodriver cydriver #define siodtrwakeup cydtrwakeup #define siointr cyintr #define siointr1 cyintr1 #define sioioctl cyioctl #define sioopen cyopen #define siopoll cypoll #define sioprobe cyprobe #define sioread cyread #define siosettimeout cysettimeout #define siostop cystop #define siowrite cywrite #define sio_registered cy_registered #define sio_timeout cy_timeout #define sio_timeout_handle cy_timeout_handle #define sio_timeouts_until_log cy_timeouts_until_log #define sio_tty cy_tty #define CY_MAX_PORTS (CD1400_NO_OF_CHANNELS * CY_MAX_CD1400s) /* We encode the cyclom unit number (cyu) in spare bits in the IVR's. */ #define CD1400_xIVR_CHAN_SHIFT 3 #define CD1400_xIVR_CHAN 0x1F #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RS_IBUFSIZE 256 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) (((mynor) >> 16) * CY_MAX_PORTS \ | (((mynor) & 0xff) & ~MINOR_MAGIC_MASK)) /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * siostop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_char bool_t; /* boolean */ typedef u_char volatile *cy_addr; /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ #if 0 u_char cfcr_image; /* copy of value written to CFCR */ u_char fifo_image; /* copy of value written to FIFO */ #endif u_char gfrcr_image; /* copy of value read from GFRCR */ #if 0 bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t loses_outints; /* nonzero if device loses output interrupts */ #endif u_char mcr_dtr; /* MCR bit that is wired to DTR */ u_char mcr_image; /* copy of value written to MCR */ u_char mcr_rts; /* MCR bit that is wired to RTS */ #if 0 #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ #endif int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ #if 0 u_int tx_fifo_size; #endif u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ int cy_align; /* index for register alignment */ cy_addr cy_iobase; /* base address of this port's cyclom */ cy_addr iobase; /* base address of this port's cd1400 */ int mcr_rts_reg; /* cd1400 reg number of reg holding mcr_rts */ struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; u_int recv_exception; /* exception chars received */ u_int mdm; /* modem signal changes */ #ifdef CyDebug u_int start_count; /* no. of calls to comstart() */ u_int start_real; /* no. of calls that did something */ #endif u_char channel_control;/* CD1400 CCR control command shadow */ u_char cor[3]; /* CD1400 COR1-3 shadows */ u_char intr_enable; /* CD1400 SRER shadow */ /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; #ifdef DEVFS void *devfs_token_ttyd; void *devfs_token_ttyl; void *devfs_token_ttyi; void *devfs_token_cuaa; void *devfs_token_cual; void *devfs_token_cuai; #endif }; /* PCI driver entry point. */ int cyattach_common __P((cy_addr cy_iobase, int cy_align)); static int cy_units __P((cy_addr cy_iobase, int cy_align)); static int sioattach __P((struct isa_device *dev)); static void cd1400_channel_cmd __P((cy_addr iobase, int cmd, int cy_align)); static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); #if 0 static void siointr1 __P((struct com_s *com)); #endif static int commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static swihand_t siopoll; static int sioprobe __P((struct isa_device *dev)); static void siosettimeout __P((void)); static int comspeed __P((speed_t speed, u_long cy_clock, int *prescaler_io)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com)); #ifdef CyDebug void cystatus __P((int unit)); #endif static char driver_name[] = "cy"; /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) struct isa_driver siodriver = { sioprobe, sioattach, driver_name }; static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static d_stop_t siostop; static d_devtotty_t siodevtotty; -#define CDEV_MAJOR 48 -static struct cdevsw sio_cdevsw = { +#define CDEV_MAJOR 48 +static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, ttpoll, nommap, NULL, driver_name, - NULL, -1, + NULL, -1, nodump, nopsize, + D_TTY, }; static int comconsole = -1; static speed_t comdefaultrate = TTYDEF_SPEED; static u_int com_events; /* input chars + weighted output completions */ static bool_t sio_registered; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); #if 0 /* XXX */ static struct tty *sio_tty[NSIO]; #else static struct tty sio_tty[NSIO]; #endif static const int nsio_tty = NSIO; #ifdef CyDebug static u_int cd_inbs; static u_int cy_inbs; static u_int cd_outbs; static u_int cy_outbs; static u_int cy_svrr_probes; static u_int cy_timeouts; #endif static int cy_chip_offset[] = { 0x0000, 0x0400, 0x0800, 0x0c00, 0x0200, 0x0600, 0x0a00, 0x0e00, }; static int cy_nr_cd1400s[NCY]; static int cy_total_devices; #undef RxFifoThreshold static int volatile RxFifoThreshold = (CD1400_RX_FIFO_SIZE / 2); static int sioprobe(dev) struct isa_device *dev; { cy_addr iobase; iobase = (cy_addr)dev->id_maddr; /* Cyclom-16Y hardware reset (Cyclom-8Ys don't care) */ cd_inb(iobase, CY16_RESET, 0); /* XXX? */ DELAY(500); /* wait for the board to get its act together */ /* this is needed to get the board out of reset */ cd_outb(iobase, CY_CLEAR_INTR, 0, 0); DELAY(500); return (cy_units(iobase, 0) == 0 ? 0 : -1); } static int cy_units(cy_iobase, cy_align) cy_addr cy_iobase; int cy_align; { int cyu; u_char firmware_version; int i; cy_addr iobase; for (cyu = 0; cyu < CY_MAX_CD1400s; ++cyu) { iobase = cy_iobase + (cy_chip_offset[cyu] << cy_align); /* wait for chip to become ready for new command */ for (i = 0; i < 10; i++) { DELAY(50); if (!cd_inb(iobase, CD1400_CCR, cy_align)) break; } /* clear the GFRCR register */ cd_outb(iobase, CD1400_GFRCR, cy_align, 0); /* issue a reset command */ cd_outb(iobase, CD1400_CCR, cy_align, CD1400_CCR_CMDRESET | CD1400_CCR_FULLRESET); /* wait for the CD1400 to initialize itself */ for (i = 0; i < 200; i++) { DELAY(50); /* retrieve firmware version */ firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align); if ((firmware_version & 0xf0) == 0x40) break; } /* * Anything in the 0x40-0x4F range is fine. * If one CD1400 is bad then we don't support higher * numbered good ones on this board. */ if ((firmware_version & 0xf0) != 0x40) break; } return (cyu); } static int sioattach(isdp) struct isa_device *isdp; { int adapter; adapter = cyattach_common((cy_addr) isdp->id_maddr, 0); if (adapter < 0) return (0); /* * XXX * This kludge is to allow ISA/PCI device specifications in the * kernel config file to be in any order. */ if (isdp->id_unit != adapter) { printf("cy%d: attached as cy%d\n", isdp->id_unit, adapter); isdp->id_unit = adapter; /* XXX */ } isdp->id_ri_flags |= RI_FAST; return (1); } int cyattach_common(cy_iobase, cy_align) cy_addr cy_iobase; int cy_align; { int adapter; int cyu; dev_t dev; u_char firmware_version; cy_addr iobase; int ncyu; int unit; adapter = cy_total_devices; if ((u_int)adapter >= NCY) { printf( "cy%d: can't attach adapter: insufficient cy devices configured\n", adapter); return (-1); } ncyu = cy_units(cy_iobase, cy_align); if (ncyu == 0) return (-1); cy_nr_cd1400s[adapter] = ncyu; cy_total_devices++; unit = adapter * CY_MAX_PORTS; for (cyu = 0; cyu < ncyu; ++cyu) { int cdu; iobase = (cy_addr) (cy_iobase + (cy_chip_offset[cyu] << cy_align)); firmware_version = cd_inb(iobase, CD1400_GFRCR, cy_align); /* Set up a receive timeout period of than 1+ ms. */ cd_outb(iobase, CD1400_PPR, cy_align, howmany(CY_CLOCK(firmware_version) / CD1400_PPR_PRESCALER, 1000)); for (cdu = 0; cdu < CD1400_NO_OF_CHANNELS; ++cdu, ++unit) { struct com_s *com; int s; com = malloc(sizeof *com, M_DEVBUF, M_NOWAIT); if (com == NULL) break; bzero(com, sizeof *com); com->unit = unit; com->gfrcr_image = firmware_version; if (CY_RTS_DTR_SWAPPED(firmware_version)) { com->mcr_dtr = MCR_RTS; com->mcr_rts = MCR_DTR; com->mcr_rts_reg = CD1400_MSVR2; } else { com->mcr_dtr = MCR_DTR; com->mcr_rts = MCR_RTS; com->mcr_rts_reg = CD1400_MSVR1; } com->dtr_wait = 3 * hz; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->cy_align = cy_align; com->cy_iobase = cy_iobase; com->iobase = iobase; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; } termioschars(&com->it_in); com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; com->it_out = com->it_in; s = spltty(); com_addr(unit) = com; splx(s); if (!sio_registered) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &sio_cdevsw, NULL); register_swi(SWI_TTY, siopoll); sio_registered = TRUE; } #ifdef DEVFS com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, unit, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyc%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyic%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_LOCK_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttylc%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuac%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaic%r%r", adapter, unit % CY_MAX_PORTS); com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cualc%r%r", adapter, unit % CY_MAX_PORTS); #endif } } /* ensure an edge for the next interrupt */ cd_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); return (adapter); } static int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; cy_addr iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "cydtr", 0); if (error != 0) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "cybi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; tp->t_ififosize = 2 * RS_IBUFSIZE; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; #if 0 (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; #endif ++com->wopeners; iobase = com->iobase; /* reset this channel */ cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); cd1400_channel_cmd(iobase, CD1400_CCR_CMDRESET, com->cy_align); /* * Resetting disables the transmitter and receiver as well as * flushing the fifos so some of our cached state becomes * invalid. The documentation suggests that all registers * for the current channel are reset to defaults, but * apparently none are. We wouldn't want DTR cleared. */ com->channel_control = 0; /* Encode per-board unit in LIVR for access in intr routines. */ cd_outb(iobase, CD1400_LIVR, com->cy_align, (unit & CD1400_xIVR_CHAN) << CD1400_xIVR_CHAN_SHIFT); /* * raise dtr and generally set things up correctly. this * has the side-effect of selecting the appropriate cd1400 * channel, to help us with subsequent channel control stuff */ error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ #if 0 if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); DELAY(100); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(100); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); enable_intr(); #else /* !0 */ /* XXX raise RTS too */ (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); disable_intr(); com->prev_modem_status = com->last_modem_status = cd_inb(iobase, CD1400_MSVR2, com->cy_align); cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable = CD1400_SRER_MDMCH | CD1400_SRER_RXDATA); enable_intr(); #endif /* 0 */ /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "cybi" * instead of "cydcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "cydcd", 0); --com->wopeners; if (error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); #ifdef broken /* session holds a ref to the tty; can't deallocate */ ttyfree(tp); com->tp = sio_tty[unit] = NULL; #endif return (0); } static void comhardclose(com) struct com_s *com; { cy_addr iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty(); #if 0 com->poll = FALSE; com->poll_output = FALSE; #endif com->do_timestamp = 0; cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); #if 0 outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); #endif { #if 0 outb(iobase + com_ier, 0); #else disable_intr(); cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable = 0); enable_intr(); #endif tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { (void)commctl(com, TIOCM_DTR, DMBIC); /* Disable receiver (leave transmitter enabled). */ com->channel_control = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN | CD1400_CCR_RCVDIS; cd1400_channel_cmd(iobase, com->channel_control, com->cy_align); if (com->dtr_wait != 0) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } #if 0 if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ outb(iobase + com_fifo, 0); } #endif com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); tp = com_addr(MINOR_TO_UNIT(mynor))->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; #ifdef Smarts /* XXX duplicate ttwrite(), but without so much output processing on * CR & LF chars. Hardly worth the effort, given that high-throughput * sessions are raw anyhow. */ #else return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); #endif } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } void siointr(unit) int unit; { int baseu; int cy_align; cy_addr cy_iobase; int cyu; cy_addr iobase; u_char status; COM_LOCK(); /* XXX could this be placed down lower in the loop? */ baseu = unit * CY_MAX_PORTS; cy_align = com_addr(baseu)->cy_align; cy_iobase = com_addr(baseu)->cy_iobase; /* check each CD1400 in turn */ for (cyu = 0; cyu < cy_nr_cd1400s[unit]; ++cyu) { iobase = (cy_addr) (cy_iobase + (cy_chip_offset[cyu] << cy_align)); /* poll to see if it has any work */ status = cd_inb(iobase, CD1400_SVRR, cy_align); if (status == 0) continue; #ifdef CyDebug ++cy_svrr_probes; #endif /* service requests as appropriate, giving priority to RX */ if (status & CD1400_SVRR_RXRDY) { struct com_s *com; u_int count; u_char *ioptr; u_char line_status; u_char recv_data; u_char serv_type; #ifdef PollMode u_char save_car; u_char save_rir; #endif #ifdef PollMode save_rir = cd_inb(iobase, CD1400_RIR, cy_align); save_car = cd_inb(iobase, CD1400_CAR, cy_align); /* enter rx service */ cd_outb(iobase, CD1400_CAR, cy_align, save_rir); serv_type = cd_inb(iobase, CD1400_RIVR, cy_align); com = com_addr(baseu + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #else /* ack receive service */ serv_type = cy_inb(iobase, CY8_SVCACKR); com = com_addr(baseu + + ((serv_type >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif if (serv_type & CD1400_RIVR_EXCEPTION) { ++com->recv_exception; line_status = cd_inb(iobase, CD1400_RDSR, cy_align); /* break/unnattached error bits or real input? */ recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifndef SOFT_HOTCHAR if (line_status & CD1400_RDSR_SPECIAL && com->hotchar != 0) setsofttty(); #endif #if 1 /* XXX "intelligent" PFO error handling would break O error handling */ if (line_status & (LSR_PE|LSR_FE|LSR_BI)) { /* Don't store PE if IGNPAR and BI if IGNBRK, this hack allows "raw" tty optimization works even if IGN* is set. */ if ( com->tp == NULL || !(com->tp->t_state & TS_ISOPEN) || (line_status & (LSR_PE|LSR_FE)) && (com->tp->t_iflag & IGNPAR) || (line_status & LSR_BI) && (com->tp->t_iflag & IGNBRK)) goto cont; if ( (line_status & (LSR_PE|LSR_FE)) && (com->tp->t_state & TS_CAN_BYPASS_L_RINT) && ((line_status & LSR_FE) || (line_status & LSR_PE) && (com->tp->t_iflag & INPCK))) recv_data = 0; } #endif /* 1 */ ++com->bytes_in; #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, cy_align, com->mcr_image &= ~com->mcr_rts); #endif if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } goto cont; } else { int ifree; count = cd_inb(iobase, CD1400_RDCR, cy_align); if (!count) goto cont; com->bytes_in += count; ioptr = com->iptr; ifree = com->ibufend - ioptr; if (count > ifree) { count -= ifree; com_events += ifree; if (ifree != 0) { if (com->do_timestamp) microtime(&com->timestamp); do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = 0; ++ioptr; } while (--ifree != 0); } com->delta_error_counts [CE_INTERRUPT_BUF_OVERFLOW] += count; do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif } while (--count != 0); } else { if (com->do_timestamp) microtime(&com->timestamp); if (ioptr <= com->ihighwater && ioptr + count > com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, cy_align, com->mcr_image &= ~com->mcr_rts); #endif com_events += count; do { recv_data = cd_inb(iobase, CD1400_RDSR, cy_align); #ifdef SOFT_HOTCHAR if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = 0; ++ioptr; } while (--count != 0); } com->iptr = ioptr; } cont: /* terminate service context */ #ifdef PollMode cd_outb(iobase, CD1400_RIR, cy_align, save_rir & ~(CD1400_RIR_RDIREQ | CD1400_RIR_RBUSY)); cd_outb(iobase, CD1400_CAR, cy_align, save_car); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } if (status & CD1400_SVRR_MDMCH) { struct com_s *com; u_char modem_status; #ifdef PollMode u_char save_car; u_char save_mir; #else u_char vector; #endif #ifdef PollMode save_mir = cd_inb(iobase, CD1400_MIR, cy_align); save_car = cd_inb(iobase, CD1400_CAR, cy_align); /* enter modem service */ cd_outb(iobase, CD1400_CAR, cy_align, save_mir); com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS + (save_mir & CD1400_MIR_CHAN)); #else /* ack modem service */ vector = cy_inb(iobase, CY8_SVCACKM); com = com_addr(baseu + ((vector >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif ++com->mdm; modem_status = cd_inb(iobase, CD1400_MSVR2, cy_align); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } #ifdef SOFT_CTS_OFLOW /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) { com->state |= CS_ODEVREADY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) && !(com->intr_enable & CD1400_SRER_TXRDY)) cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } else { com->state &= ~CS_ODEVREADY; if (com->intr_enable & CD1400_SRER_TXRDY) cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } } #endif } /* terminate service context */ #ifdef PollMode cd_outb(iobase, CD1400_MIR, cy_align, save_mir & ~(CD1400_MIR_RDIREQ | CD1400_MIR_RBUSY)); cd_outb(iobase, CD1400_CAR, cy_align, save_car); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } if (status & CD1400_SVRR_TXRDY) { struct com_s *com; #ifdef PollMode u_char save_car; u_char save_tir; #else u_char vector; #endif #ifdef PollMode save_tir = cd_inb(iobase, CD1400_TIR, cy_align); save_car = cd_inb(iobase, CD1400_CAR, cy_align); /* enter tx service */ cd_outb(iobase, CD1400_CAR, cy_align, save_tir); com = com_addr(baseu + cyu * CD1400_NO_OF_CHANNELS + (save_tir & CD1400_TIR_CHAN)); #else /* ack transmit service */ vector = cy_inb(iobase, CY8_SVCACKT); com = com_addr(baseu + ((vector >> CD1400_xIVR_CHAN_SHIFT) & CD1400_xIVR_CHAN)); #endif if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { u_char *ioptr; u_int ocount; ioptr = com->obufq.l_head; ocount = com->obufq.l_tail - ioptr; if (ocount > CD1400_TX_FIFO_SIZE) ocount = CD1400_TX_FIFO_SIZE; com->bytes_out += ocount; do cd_outb(iobase, CD1400_TDR, cy_align, *ioptr++); while (--ocount != 0); com->obufq.l_head = ioptr; if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ com->state &= ~CS_BUSY; cd_outb(iobase, CD1400_SRER, cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; /* handle at high level ASAP */ setsofttty(); } } } /* terminate service context */ #ifdef PollMode cd_outb(iobase, CD1400_TIR, cy_align, save_tir & ~(CD1400_TIR_RDIREQ | CD1400_TIR_RBUSY)); cd_outb(iobase, CD1400_CAR, cy_align, save_car); #else cd_outb(iobase, CD1400_EOSRR, cy_align, 0); #endif } } /* ensure an edge for the next interrupt */ cd_outb(cy_iobase, CY_CLEAR_INTR, cy_align, 0); schedsofttty(); COM_UNLOCK(); } #if 0 static void siointr1(com) struct com_s *com; { } #endif static int sioioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; cy_addr iobase; int mynor; int s; struct tty *tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) int oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } tp = com->tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return (error); s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, com); if (error != ENOIOCTL) { splx(s); return (error); } cd_outb(iobase, CD1400_CAR, com->cy_align, MINOR_TO_UNIT(mynor) & CD1400_CAR_CHAN); switch (cmd) { #if 0 case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; #endif /* 0 */ case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } static void siopoll() { int unit; #ifdef CyDebug ++cy_timeouts; #endif if (com_events == 0) return; repeat: for (unit = 0; unit < NSIO; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; cy_addr iobase; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL) { /* * XXX forget any events related to closed devices * (actually never opened devices) so that we don't * loop. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); if (incc != 0) log(LOG_DEBUG, "sio%d: %d events for device with no tp\n", unit, incc); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) && !(tp->t_state & TS_TBLOCK)) #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else iobase = com->iobase, cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN), cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image |= com->mcr_rts); #endif enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; if (!(com->state & CS_BUSY)) com->tp->t_state &= ~TS_BUSY; enable_intr(); (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) continue; /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { int bits; int cflag; struct com_s *com; u_char cor_change; u_long cy_clock; int idivisor; int iflag; cy_addr iobase; int iprescaler; int itimeout; int odivisor; int oprescaler; u_char opt; int s; int unit; /* do historical conversions */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); /* check requested parameters */ cy_clock = CY_CLOCK(com->gfrcr_image); idivisor = comspeed(t->c_ispeed, cy_clock, &iprescaler); if (idivisor < 0) return (EINVAL); odivisor = comspeed(t->c_ospeed, cy_clock, &oprescaler); if (odivisor < 0) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ iobase = com->iobase; s = spltty(); cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); if (odivisor == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); if (idivisor != 0) { cd_outb(iobase, CD1400_RBPR, com->cy_align, idivisor); cd_outb(iobase, CD1400_RCOR, com->cy_align, iprescaler); } if (odivisor != 0) { cd_outb(iobase, CD1400_TBPR, com->cy_align, odivisor); cd_outb(iobase, CD1400_TCOR, com->cy_align, oprescaler); } /* * channel control * receiver enable * transmitter enable (always set) */ cflag = t->c_cflag; opt = CD1400_CCR_CMDCHANCTL | CD1400_CCR_XMTEN | (cflag & CREAD ? CD1400_CCR_RCVEN : CD1400_CCR_RCVDIS); if (opt != com->channel_control) { com->channel_control = opt; cd1400_channel_cmd(iobase, opt, com->cy_align); } #ifdef Smarts /* set special chars */ /* XXX if one is _POSIX_VDISABLE, can't use some others */ if (t->c_cc[VSTOP] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR1, com->cy_align, t->c_cc[VSTOP]); if (t->c_cc[VSTART] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR2, com->cy_align, t->c_cc[VSTART]); if (t->c_cc[VINTR] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR3, com->cy_align, t->c_cc[VINTR]); if (t->c_cc[VSUSP] != _POSIX_VDISABLE) cd_outb(iobase, CD1400_SCHR4, com->cy_align, t->c_cc[VSUSP]); #endif /* * set channel option register 1 - * parity mode * stop bits * char length */ opt = 0; /* parity */ if (cflag & PARENB) { if (cflag & PARODD) opt |= CD1400_COR1_PARODD; opt |= CD1400_COR1_PARNORMAL; } iflag = t->c_iflag; if (!(iflag & INPCK)) opt |= CD1400_COR1_NOINPCK; bits = 1 + 1; /* stop bits */ if (cflag & CSTOPB) { ++bits; opt |= CD1400_COR1_STOP2; } /* char length */ switch (cflag & CSIZE) { case CS5: bits += 5; opt |= CD1400_COR1_CS5; break; case CS6: bits += 6; opt |= CD1400_COR1_CS6; break; case CS7: bits += 7; opt |= CD1400_COR1_CS7; break; default: bits += 8; opt |= CD1400_COR1_CS8; break; } cor_change = 0; if (opt != com->cor[0]) { cor_change |= CD1400_CCR_COR1; cd_outb(iobase, CD1400_COR1, com->cy_align, com->cor[0] = opt); } /* * Set receive time-out period, normally to max(one char time, 5 ms). */ if (t->c_ispeed == 0) itimeout = cd_inb(iobase, CD1400_RTPR, com->cy_align); else { itimeout = (1000 * bits + t->c_ispeed - 1) / t->c_ispeed; #ifdef SOFT_HOTCHAR #define MIN_RTP 1 #else #define MIN_RTP 5 #endif if (itimeout < MIN_RTP) itimeout = MIN_RTP; } if (!(t->c_lflag & ICANON) && t->c_cc[VMIN] != 0 && t->c_cc[VTIME] != 0 && t->c_cc[VTIME] * 10 > itimeout) itimeout = t->c_cc[VTIME] * 10; if (itimeout > 255) itimeout = 255; cd_outb(iobase, CD1400_RTPR, com->cy_align, itimeout); /* * set channel option register 2 - * flow control */ opt = 0; #ifdef Smarts if (iflag & IXANY) opt |= CD1400_COR2_IXANY; if (iflag & IXOFF) opt |= CD1400_COR2_IXOFF; #endif #ifndef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_COR2_CCTS_OFLOW; #endif if (opt != com->cor[1]) { cor_change |= CD1400_CCR_COR2; cd_outb(iobase, CD1400_COR2, com->cy_align, com->cor[1] = opt); } /* * set channel option register 3 - * receiver FIFO interrupt threshold * flow control */ opt = RxFifoThreshold; #ifdef Smarts if (t->c_lflag & ICANON) opt |= CD1400_COR3_SCD34; /* detect INTR & SUSP chars */ if (iflag & IXOFF) /* detect and transparently handle START and STOP chars */ opt |= CD1400_COR3_FCT | CD1400_COR3_SCD12; #endif if (opt != com->cor[2]) { cor_change |= CD1400_CCR_COR3; cd_outb(iobase, CD1400_COR3, com->cy_align, com->cor[2] = opt); } /* notify the CD1400 if COR1-3 have changed */ if (cor_change) cd1400_channel_cmd(iobase, CD1400_CCR_CMDCORCHG | cor_change, com->cy_align); /* * set channel option register 4 - * CR/NL processing * break processing * received exception processing */ opt = 0; if (iflag & IGNCR) opt |= CD1400_COR4_IGNCR; #ifdef Smarts /* * we need a new ttyinput() for this, as we don't want to * have ICRNL && INLCR being done in both layers, or to have * synchronisation problems */ if (iflag & ICRNL) opt |= CD1400_COR4_ICRNL; if (iflag & INLCR) opt |= CD1400_COR4_INLCR; #endif if (iflag & IGNBRK) opt |= CD1400_COR4_IGNBRK; if (!(iflag & BRKINT)) opt |= CD1400_COR4_NOBRKINT; #if 0 /* XXX using this "intelligence" breaks reporting of overruns. */ if (iflag & IGNPAR) opt |= CD1400_COR4_PFO_DISCARD; else { if (iflag & PARMRK) opt |= CD1400_COR4_PFO_ESC; else opt |= CD1400_COR4_PFO_NUL; } #else opt |= CD1400_COR4_PFO_EXCEPTION; #endif cd_outb(iobase, CD1400_COR4, com->cy_align, opt); /* * set channel option register 5 - */ opt = 0; if (iflag & ISTRIP) opt |= CD1400_COR5_ISTRIP; if (t->c_iflag & IEXTEN) /* enable LNEXT (e.g. ctrl-v quoting) handling */ opt |= CD1400_COR5_LNEXT; #ifdef Smarts if (t->c_oflag & ONLCR) opt |= CD1400_COR5_ONLCR; if (t->c_oflag & OCRNL) opt |= CD1400_COR5_OCRNL; #endif cd_outb(iobase, CD1400_COR5, com->cy_align, opt); /* * We always generate modem status change interrupts for CD changes. * Among other things, this is necessary to track TS_CARR_ON for * pstat to print even when the driver doesn't care. CD changes * should be rare so interrupts for them are not worth extra code to * avoid. We avoid interrupts for other modem status changes (except * for CTS changes when SOFT_CTS_OFLOW is configured) since this is * simplest and best. */ /* * set modem change option register 1 * generate modem interrupts on which 1 -> 0 input transitions * also controls auto-DTR output flow-control, which we don't use */ opt = CD1400_MCOR1_CDzd; #ifdef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_MCOR1_CTSzd; #endif cd_outb(iobase, CD1400_MCOR1, com->cy_align, opt); /* * set modem change option register 2 * generate modem interrupts on specific 0 -> 1 input transitions */ opt = CD1400_MCOR2_CDod; #ifdef SOFT_CTS_OFLOW if (cflag & CCTS_OFLOW) opt |= CD1400_MCOR2_CTSod; #endif cd_outb(iobase, CD1400_MCOR2, com->cy_align, opt); /* * XXX should have done this long ago, but there is too much state * to change all atomically. */ disable_intr(); com->state &= ~CS_TTGO; if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) { com->state |= CS_RTS_IFLOW; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image |= com->mcr_rts); #endif } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; #ifdef SOFT_CTS_OFLOW com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; } #endif /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); #if 0 /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); #endif if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { if (!(com->intr_enable & CD1400_SRER_TXRDY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } else { if (com->intr_enable & CD1400_SRER_TXRDY) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } enable_intr(); splx(s); comstart(tp); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; cy_addr iobase; int s; #ifdef CyDebug bool_t started; #endif int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); #ifdef CyDebug ++com->start_count; started = FALSE; #endif disable_intr(); cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); if (tp->t_state & TS_TTSTOP) { com->state &= ~CS_TTGO; if (com->intr_enable & CD1400_SRER_TXRDY) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable &= ~CD1400_SRER_TXRDY); } else { com->state |= CS_TTGO; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY) && !(com->intr_enable & CD1400_SRER_TXRDY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & com->mcr_rts && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image &= ~com->mcr_rts); #endif } else { if (!(com->mcr_image & com->mcr_rts) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) #if 0 outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); #else cd_outb(iobase, com->mcr_rts_reg, com->cy_align, com->mcr_image |= com->mcr_rts); #endif } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { #ifdef CyDebug started = TRUE; #endif com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } enable_intr(); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { #ifdef CyDebug started = TRUE; #endif com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; if (com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) cd_outb(iobase, CD1400_SRER, com->cy_align, com->intr_enable |= CD1400_SRER_TXRDY); } enable_intr(); } tp->t_state |= TS_BUSY; } #ifdef CyDebug if (started) ++com->start_real; #endif #if 0 disable_intr(); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ enable_intr(); #endif ttwwakeup(tp); splx(s); } static void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); disable_intr(); if (rw & FWRITE) { com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); comstart(tp); /* XXX should clear h/w fifos too. */ } static struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO) return (NULL); return (&sio_tty[unit]); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { cy_addr iobase; int mcr; int msr; iobase = com->iobase; if (how == DMGET) { if (com->channel_control & CD1400_CCR_RCVEN) bits |= TIOCM_LE; mcr = com->mcr_image; if (mcr & com->mcr_dtr) bits |= TIOCM_DTR; if (mcr & com->mcr_rts) /* XXX wired on for Cyclom-8Ys */ bits |= TIOCM_RTS; /* * We must read the modem status from the hardware because * we don't generate modem status change interrupts for all * changes, so com->prev_modem_status is not guaranteed to * be up to date. This is safe, unlike for sio, because * reading the status register doesn't clear pending modem * status change interrupts. */ msr = cd_inb(iobase, CD1400_MSVR2, com->cy_align); if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; if (msr & MSR_RI) /* XXX not connected except for Cyclom-16Y? */ bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= com->mcr_dtr; if (bits & TIOCM_RTS) mcr |= com->mcr_rts; disable_intr(); switch (how) { case DMSET: com->mcr_image = mcr; cd_outb(iobase, CD1400_MSVR1, com->cy_align, mcr); cd_outb(iobase, CD1400_MSVR2, com->cy_align, mcr); break; case DMBIS: com->mcr_image = mcr = com->mcr_image | mcr; cd_outb(iobase, CD1400_MSVR1, com->cy_align, mcr); cd_outb(iobase, CD1400_MSVR2, com->cy_align, mcr); break; case DMBIC: com->mcr_image = mcr = com->mcr_image & ~mcr; cd_outb(iobase, CD1400_MSVR1, com->cy_align, mcr); cd_outb(iobase, CD1400_MSVR2, com->cy_align, mcr); break; } enable_intr(); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN) { someopen = TRUE; #if 0 if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } #endif } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); #if 0 /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIO; ++unit) { com = com_addr(unit); if (com != NULL && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } #endif /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < NSIO; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "cy%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } static void disc_optim(tp, t, com) struct tty *tp; struct termios *t; struct com_s *com; { #ifndef SOFT_HOTCHAR cy_addr iobase; u_char opt; #endif /* * XXX can skip a lot more cases if Smarts. Maybe * (IGNCR | ISTRIP | IXON) in c_iflag. But perhaps we * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state. */ if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; com->hotchar = linesw[tp->t_line].l_hotchar; #ifndef SOFT_HOTCHAR iobase = com->iobase; cd_outb(iobase, CD1400_CAR, com->cy_align, com->unit & CD1400_CAR_CHAN); opt = com->cor[2] & ~CD1400_COR3_SCD34; if (com->hotchar != 0) { cd_outb(iobase, CD1400_SCHR3, com->cy_align, com->hotchar); cd_outb(iobase, CD1400_SCHR4, com->cy_align, com->hotchar); opt |= CD1400_COR3_SCD34; } if (opt != com->cor[2]) { cd_outb(iobase, CD1400_COR3, com->cy_align, com->cor[2] = opt); cd1400_channel_cmd(com->iobase, CD1400_CCR_CMDCORCHG | CD1400_CCR_COR3, com->cy_align); } #endif } #ifdef Smarts /* standard line discipline input routine */ int cyinput(c, tp) int c; struct tty *tp; { /* XXX duplicate ttyinput(), but without the IXOFF/IXON/ISTRIP/IPARMRK * bits, as they are done by the CD1400. Hardly worth the effort, * given that high-throughput sessions are raw anyhow. */ } #endif /* Smarts */ static int comspeed(speed, cy_clock, prescaler_io) speed_t speed; u_long cy_clock; int *prescaler_io; { int actual; int error; int divider; int prescaler; int prescaler_unit; if (speed == 0) return (0); if (speed < 0 || speed > 150000) return (-1); /* determine which prescaler to use */ for (prescaler_unit = 4, prescaler = 2048; prescaler_unit; prescaler_unit--, prescaler >>= 2) { if (cy_clock / prescaler / speed > 63) break; } divider = (cy_clock / prescaler * 2 / speed + 1) / 2; /* round off */ if (divider > 255) divider = 255; actual = cy_clock/prescaler/divider; /* 10 times error in percent: */ error = ((actual - (long)speed) * 2000 / (long)speed + 1) / 2; /* 3.0% max error tolerance */ if (error < -30 || error > 30) return (-1); #if 0 printf("prescaler = %d (%d)\n", prescaler, prescaler_unit); printf("divider = %d (%x)\n", divider, divider); printf("actual = %d\n", actual); printf("error = %d\n", error); #endif *prescaler_io = prescaler_unit; return (divider); } static void cd1400_channel_cmd(iobase, cmd, cy_align) cy_addr iobase; int cmd; int cy_align; { /* XXX hsu@clinet.fi: This is always more dependent on ISA bus speed, as the card is probed every round? Replaced delaycount with 8k. Either delaycount has to be implemented in FreeBSD or more sensible way of doing these should be implemented. DELAY isn't enough here. */ u_int maxwait = 5 * 8 * 1024; /* approx. 5 ms */ /* wait for processing of previous command to complete */ while (cd_inb(iobase, CD1400_CCR, cy_align) && maxwait--) ; if (!maxwait) log(LOG_ERR, "cy: channel command timeout (%d loops) - arrgh\n", 5 * 8 * 1024); cd_outb(iobase, CD1400_CCR, cy_align, cmd); } #ifdef CyDebug /* useful in ddb */ void cystatus(unit) int unit; { struct com_s *com; cy_addr iobase; u_int ocount; struct tty *tp; com = com_addr(unit); printf("info for channel %d\n", unit); printf("------------------\n"); printf("total cyclom service probes:\t%d\n", cy_svrr_probes); printf("calls to upper layer:\t\t%d\n", cy_timeouts); if (com == NULL) return; iobase = com->iobase; printf("\n"); printf("cd1400 base address:\\tt%p\n", iobase); cd_outb(iobase, CD1400_CAR, com->cy_align, unit & CD1400_CAR_CHAN); printf("saved channel_control:\t\t0x%02x\n", com->channel_control); printf("saved cor1-3:\t\t\t0x%02x 0x%02x 0x%02x\n", com->cor[0], com->cor[1], com->cor[2]); printf("service request enable reg:\t0x%02x (0x%02x cached)\n", cd_inb(iobase, CD1400_SRER, com->cy_align), com->intr_enable); printf("service request register:\t0x%02x\n", cd_inb(iobase, CD1400_SVRR, com->cy_align)); printf("modem status:\t\t\t0x%02x (0x%02x cached)\n", cd_inb(iobase, CD1400_MSVR2, com->cy_align), com->prev_modem_status); printf("rx/tx/mdm interrupt registers:\t0x%02x 0x%02x 0x%02x\n", cd_inb(iobase, CD1400_RIR, com->cy_align), cd_inb(iobase, CD1400_TIR, com->cy_align), cd_inb(iobase, CD1400_MIR, com->cy_align)); printf("\n"); printf("com state:\t\t\t0x%02x\n", com->state); printf("calls to comstart():\t\t%d (%d useful)\n", com->start_count, com->start_real); printf("rx buffer chars free:\t\t%d\n", com->iptr - com->ibuf); ocount = 0; if (com->obufs[0].l_queued) ocount += com->obufs[0].l_tail - com->obufs[0].l_head; if (com->obufs[1].l_queued) ocount += com->obufs[1].l_tail - com->obufs[1].l_head; printf("tx buffer chars:\t\t%u\n", ocount); printf("received chars:\t\t\t%d\n", com->bytes_in); printf("received exceptions:\t\t%d\n", com->recv_exception); printf("modem signal deltas:\t\t%d\n", com->mdm); printf("transmitted chars:\t\t%d\n", com->bytes_out); printf("\n"); tp = com->tp; if (tp != NULL) { printf("tty state:\t\t\t0x%08x\n", tp->t_state); printf( "upper layer queue lengths:\t%d raw, %d canon, %d output\n", tp->t_rawq.c_cc, tp->t_canq.c_cc, tp->t_outq.c_cc); } else printf("tty state:\t\t\tclosed\n"); } #endif /* CyDebug */ diff --git a/sys/i386/isa/istallion.c b/sys/i386/isa/istallion.c index c11fc16b01b5..6dfe7f9526a0 100644 --- a/sys/i386/isa/istallion.c +++ b/sys/i386/isa/istallion.c @@ -1,3871 +1,3871 @@ /*****************************************************************************/ /* * istallion.c -- stallion intelligent multiport serial driver. * * Copyright (c) 1994-1996 Greg Ungerer (gerg@stallion.oz.au). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Greg Ungerer. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: istallion.c,v 1.19 1998/06/07 17:10:42 dfr Exp $ + * $Id: istallion.c,v 1.20 1998/08/16 01:21:49 bde Exp $ */ /*****************************************************************************/ #include "opt_compat.h" #define TTYDEFCHARS 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*****************************************************************************/ /* * Define the version level of the kernel - so we can compile in the * appropriate bits of code. By default this will compile for a 2.1 * level kernel. */ #define VFREEBSD 220 #if VFREEBSD >= 220 #define STATIC static #else #define STATIC #endif /*****************************************************************************/ /* * Define different board types. Not all of the following board types * are supported by this driver. But I will use the standard "assigned" * board numbers. Currently supported boards are abbreviated as: * ECP = EasyConnection 8/64, ONB = ONboard, BBY = Brumby and * STAL = Stallion. */ #define BRD_UNKNOWN 0 #define BRD_STALLION 1 #define BRD_BRUMBY4 2 #define BRD_ONBOARD2 3 #define BRD_ONBOARD 4 #define BRD_BRUMBY8 5 #define BRD_BRUMBY16 6 #define BRD_ONBOARDE 7 #define BRD_ONBOARD32 9 #define BRD_ONBOARD2_32 10 #define BRD_ONBOARDRS 11 #define BRD_EASYIO 20 #define BRD_ECH 21 #define BRD_ECHMC 22 #define BRD_ECP 23 #define BRD_ECPE 24 #define BRD_ECPMC 25 #define BRD_ECHPCI 26 #define BRD_BRUMBY BRD_BRUMBY4 /*****************************************************************************/ /* * Define important driver limitations. */ #define STL_MAXBRDS 8 #define STL_MAXPANELS 4 #define STL_PORTSPERPANEL 16 #define STL_PORTSPERBRD 64 #define STL_MAXCHANS STL_PORTSPERBRD /* * Define the important minor number break down bits. These have been * chosen to be "compatable" with the standard sio driver minor numbers. * Extra high bits are used to distinguish between boards and also for * really high port numbers (> 32). */ #define STL_CALLOUTDEV 0x80 #define STL_CTRLLOCK 0x40 #define STL_CTRLINIT 0x20 #define STL_CTRLDEV (STL_CTRLLOCK | STL_CTRLINIT) #define STL_MEMDEV 0x07000000 #define STL_DEFSPEED 9600 #define STL_DEFCFLAG (CS8 | CREAD | HUPCL) /*****************************************************************************/ /* * Define our local driver identity first. Set up stuff to deal with * all the local structures required by a serial tty driver. */ static char stli_drvname[] = "stli"; static char const stli_longdrvname[] = "Stallion Multiport Serial Driver"; static char const stli_drvversion[] = "1.0.0"; static int stli_nrbrds = 0; static int stli_doingtimeout = 0; static char *__file__ = /*__FILE__*/ "istallion.c"; /* * Define some macros to use to class define boards. */ #define BRD_ISA 0x1 #define BRD_EISA 0x2 #define BRD_MCA 0x4 #define BRD_PCI 0x8 static unsigned char stli_stliprobed[STL_MAXBRDS]; /*****************************************************************************/ /* * Define a set of structures to hold all the board/panel/port info * for our ports. These will be dynamically allocated as required at * driver initialization time. */ /* * Port and board structures to hold status info about each object. * The board structure contains pointers to structures for each port * connected to it. Panels are not distinguished here, since * communication with the slave board will always be on a per port * basis. */ typedef struct { struct tty tty; int portnr; int panelnr; int brdnr; int ioaddr; int callout; int devnr; int dtrwait; int dotimestamp; int waitopens; int hotchar; int rc; int argsize; void *argp; unsigned int state; unsigned int sigs; struct termios initintios; struct termios initouttios; struct termios lockintios; struct termios lockouttios; struct timeval timestamp; asysigs_t asig; unsigned long addr; unsigned long rxlost; unsigned long rxoffset; unsigned long txoffset; unsigned long pflag; unsigned int rxsize; unsigned int txsize; unsigned char reqidx; unsigned char reqbit; unsigned char portidx; unsigned char portbit; } stliport_t; /* * Use a structure of function pointers to do board level operations. * These include, enable/disable, paging shared memory, interrupting, etc. */ typedef struct stlibrd { int brdnr; int brdtype; int unitid; int state; int nrpanels; int nrports; int nrdevs; unsigned int iobase; unsigned long paddr; void *vaddr; int memsize; int pagesize; int hostoffset; int slaveoffset; int bitsize; int confbits; void (*init)(struct stlibrd *brdp); void (*enable)(struct stlibrd *brdp); void (*reenable)(struct stlibrd *brdp); void (*disable)(struct stlibrd *brdp); void (*intr)(struct stlibrd *brdp); void (*reset)(struct stlibrd *brdp); char *(*getmemptr)(struct stlibrd *brdp, unsigned long offset, int line); int panels[STL_MAXPANELS]; int panelids[STL_MAXPANELS]; stliport_t *ports[STL_PORTSPERBRD]; } stlibrd_t; static stlibrd_t *stli_brds[STL_MAXBRDS]; static int stli_shared = 0; /* * Keep a local char buffer for processing chars into the LD. We * do this to avoid copying from the boards shared memory one char * at a time. */ static int stli_rxtmplen; static stliport_t *stli_rxtmpport; static char stli_rxtmpbuf[TTYHOG]; /* * Define global stats structures. Not used often, and can be re-used * for each stats call. */ static comstats_t stli_comstats; static combrd_t stli_brdstats; static asystats_t stli_cdkstats; /* * Per board state flags. Used with the state field of the board struct. * Not really much here... All we need to do is keep track of whether * the board has been detected, and whether it is actully running a slave * or not. */ #define BST_FOUND 0x1 #define BST_STARTED 0x2 /* * Define the set of port state flags. These are marked for internal * state purposes only, usually to do with the state of communications * with the slave. They need to be updated atomically. */ #define ST_INITIALIZING 0x1 #define ST_INITIALIZED 0x2 #define ST_OPENING 0x4 #define ST_CLOSING 0x8 #define ST_CMDING 0x10 #define ST_RXING 0x20 #define ST_TXBUSY 0x40 #define ST_DOFLUSHRX 0x80 #define ST_DOFLUSHTX 0x100 #define ST_DOSIGS 0x200 #define ST_GETSIGS 0x400 #define ST_DTRWAIT 0x800 /* * Define an array of board names as printable strings. Handy for * referencing boards when printing trace and stuff. */ static char *stli_brdnames[] = { "Unknown", "Stallion", "Brumby", "ONboard-MC", "ONboard", "Brumby", "Brumby", "ONboard-EI", (char *) NULL, "ONboard", "ONboard-MC", "ONboard-MC", (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, "EasyIO", "EC8/32-AT", "EC8/32-MC", "EC8/64-AT", "EC8/64-EI", "EC8/64-MC", "EC8/32-PCI", }; /*****************************************************************************/ /* * Hardware configuration info for ECP boards. These defines apply * to the directly accessable io ports of the ECP. There is a set of * defines for each ECP board type, ISA, EISA and MCA. */ #define ECP_IOSIZE 4 #define ECP_MEMSIZE (128 * 1024) #define ECP_ATPAGESIZE (4 * 1024) #define ECP_EIPAGESIZE (64 * 1024) #define ECP_MCPAGESIZE (4 * 1024) #define STL_EISAID 0x8c4e /* * Important defines for the ISA class of ECP board. */ #define ECP_ATIREG 0 #define ECP_ATCONFR 1 #define ECP_ATMEMAR 2 #define ECP_ATMEMPR 3 #define ECP_ATSTOP 0x1 #define ECP_ATINTENAB 0x10 #define ECP_ATENABLE 0x20 #define ECP_ATDISABLE 0x00 #define ECP_ATADDRMASK 0x3f000 #define ECP_ATADDRSHFT 12 /* * Important defines for the EISA class of ECP board. */ #define ECP_EIIREG 0 #define ECP_EIMEMARL 1 #define ECP_EICONFR 2 #define ECP_EIMEMARH 3 #define ECP_EIENABLE 0x1 #define ECP_EIDISABLE 0x0 #define ECP_EISTOP 0x4 #define ECP_EIEDGE 0x00 #define ECP_EILEVEL 0x80 #define ECP_EIADDRMASKL 0x00ff0000 #define ECP_EIADDRSHFTL 16 #define ECP_EIADDRMASKH 0xff000000 #define ECP_EIADDRSHFTH 24 #define ECP_EIBRDENAB 0xc84 #define ECP_EISAID 0x4 /* * Important defines for the Micro-channel class of ECP board. * (It has a lot in common with the ISA boards.) */ #define ECP_MCIREG 0 #define ECP_MCCONFR 1 #define ECP_MCSTOP 0x20 #define ECP_MCENABLE 0x80 #define ECP_MCDISABLE 0x00 /* * Hardware configuration info for ONboard and Brumby boards. These * defines apply to the directly accessable io ports of these boards. */ #define ONB_IOSIZE 16 #define ONB_MEMSIZE (64 * 1024) #define ONB_ATPAGESIZE (64 * 1024) #define ONB_MCPAGESIZE (64 * 1024) #define ONB_EIMEMSIZE (128 * 1024) #define ONB_EIPAGESIZE (64 * 1024) /* * Important defines for the ISA class of ONboard board. */ #define ONB_ATIREG 0 #define ONB_ATMEMAR 1 #define ONB_ATCONFR 2 #define ONB_ATSTOP 0x4 #define ONB_ATENABLE 0x01 #define ONB_ATDISABLE 0x00 #define ONB_ATADDRMASK 0xff0000 #define ONB_ATADDRSHFT 16 #define ONB_HIMEMENAB 0x02 /* * Important defines for the EISA class of ONboard board. */ #define ONB_EIIREG 0 #define ONB_EIMEMARL 1 #define ONB_EICONFR 2 #define ONB_EIMEMARH 3 #define ONB_EIENABLE 0x1 #define ONB_EIDISABLE 0x0 #define ONB_EISTOP 0x4 #define ONB_EIEDGE 0x00 #define ONB_EILEVEL 0x80 #define ONB_EIADDRMASKL 0x00ff0000 #define ONB_EIADDRSHFTL 16 #define ONB_EIADDRMASKH 0xff000000 #define ONB_EIADDRSHFTH 24 #define ONB_EIBRDENAB 0xc84 #define ONB_EISAID 0x1 /* * Important defines for the Brumby boards. They are pretty simple, * there is not much that is programmably configurable. */ #define BBY_IOSIZE 16 #define BBY_MEMSIZE (64 * 1024) #define BBY_PAGESIZE (16 * 1024) #define BBY_ATIREG 0 #define BBY_ATCONFR 1 #define BBY_ATSTOP 0x4 /* * Important defines for the Stallion boards. They are pretty simple, * there is not much that is programmably configurable. */ #define STAL_IOSIZE 16 #define STAL_MEMSIZE (64 * 1024) #define STAL_PAGESIZE (64 * 1024) /* * Define the set of status register values for EasyConnection panels. * The signature will return with the status value for each panel. From * this we can determine what is attached to the board - before we have * actually down loaded any code to it. */ #define ECH_PNLSTATUS 2 #define ECH_PNL16PORT 0x20 #define ECH_PNLIDMASK 0x07 #define ECH_PNLINTRPEND 0x80 /* * Define some macros to do things to the board. Even those these boards * are somewhat related there is often significantly different ways of * doing some operation on it (like enable, paging, reset, etc). So each * board class has a set of functions which do the commonly required * operations. The macros below basically just call these functions, * generally checking for a NULL function - which means that the board * needs nothing done to it to achieve this operation! */ #define EBRDINIT(brdp) \ if (brdp->init != NULL) \ (* brdp->init)(brdp) #define EBRDENABLE(brdp) \ if (brdp->enable != NULL) \ (* brdp->enable)(brdp); #define EBRDDISABLE(brdp) \ if (brdp->disable != NULL) \ (* brdp->disable)(brdp); #define EBRDINTR(brdp) \ if (brdp->intr != NULL) \ (* brdp->intr)(brdp); #define EBRDRESET(brdp) \ if (brdp->reset != NULL) \ (* brdp->reset)(brdp); #define EBRDGETMEMPTR(brdp,offset) \ (* brdp->getmemptr)(brdp, offset, __LINE__) /* * Define the maximal baud rate. */ #define STL_MAXBAUD 230400 /*****************************************************************************/ /* * Define macros to extract a brd and port number from a minor number. * This uses the extended minor number range in the upper 2 bytes of * the device number. This gives us plenty of minor numbers to play * with... */ #define MKDEV2BRD(m) (((m) & 0x00700000) >> 20) #define MKDEV2PORT(m) (((m) & 0x1f) | (((m) & 0x00010000) >> 11)) /* * Define some handy local macros... */ #ifndef MIN #define MIN(a,b) (((a) <= (b)) ? (a) : (b)) #endif /*****************************************************************************/ /* * Declare all those functions in this driver! First up is the set of * externally visible functions. */ static int stliprobe(struct isa_device *idp); static int stliattach(struct isa_device *idp); STATIC d_open_t stliopen; STATIC d_close_t stliclose; STATIC d_read_t stliread; STATIC d_write_t stliwrite; STATIC d_ioctl_t stliioctl; STATIC d_stop_t stlistop; #if VFREEBSD >= 220 STATIC d_devtotty_t stlidevtotty; #else struct tty *stlidevtotty(dev_t dev); #endif /* * Internal function prototypes. */ static stliport_t *stli_dev2port(dev_t dev); static int stli_chksharemem(void); static int stli_isaprobe(struct isa_device *idp); static int stli_eisaprobe(struct isa_device *idp); static int stli_mcaprobe(struct isa_device *idp); static int stli_brdinit(stlibrd_t *brdp); static int stli_brdattach(stlibrd_t *brdp); static int stli_initecp(stlibrd_t *brdp); static int stli_initonb(stlibrd_t *brdp); static int stli_initports(stlibrd_t *brdp); static int stli_startbrd(stlibrd_t *brdp); static void stli_poll(void *arg); static __inline void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp); static __inline int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp); static __inline void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp); static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts); static long stli_mktiocm(unsigned long sigvalue); static void stli_rxprocess(stlibrd_t *brdp, stliport_t *portp); static void stli_flush(stliport_t *portp, int flag); static void stli_start(struct tty *tp); static int stli_param(struct tty *tp, struct termios *tiosp); static void stli_ttyoptim(stliport_t *portp, struct termios *tiosp); static void stli_dtrwakeup(void *arg); static int stli_initopen(stliport_t *portp); static int stli_shutdownclose(stliport_t *portp); static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait); static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait); static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback); static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback); static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp); static int stli_memrw(dev_t dev, struct uio *uiop, int flag); static int stli_memioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p); static int stli_getbrdstats(caddr_t data); static int stli_getportstats(stliport_t *portp, caddr_t data); static int stli_clrportstats(stliport_t *portp, caddr_t data); static stliport_t *stli_getport(int brdnr, int panelnr, int portnr); static void stli_ecpinit(stlibrd_t *brdp); static void stli_ecpenable(stlibrd_t *brdp); static void stli_ecpdisable(stlibrd_t *brdp); static void stli_ecpreset(stlibrd_t *brdp); static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_ecpintr(stlibrd_t *brdp); static void stli_ecpeiinit(stlibrd_t *brdp); static void stli_ecpeienable(stlibrd_t *brdp); static void stli_ecpeidisable(stlibrd_t *brdp); static void stli_ecpeireset(stlibrd_t *brdp); static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_ecpmcenable(stlibrd_t *brdp); static void stli_ecpmcdisable(stlibrd_t *brdp); static void stli_ecpmcreset(stlibrd_t *brdp); static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_onbinit(stlibrd_t *brdp); static void stli_onbenable(stlibrd_t *brdp); static void stli_onbdisable(stlibrd_t *brdp); static void stli_onbreset(stlibrd_t *brdp); static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_onbeinit(stlibrd_t *brdp); static void stli_onbeenable(stlibrd_t *brdp); static void stli_onbedisable(stlibrd_t *brdp); static void stli_onbereset(stlibrd_t *brdp); static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_bbyinit(stlibrd_t *brdp); static void stli_bbyreset(stlibrd_t *brdp); static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line); static void stli_stalinit(stlibrd_t *brdp); static void stli_stalreset(stlibrd_t *brdp); static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line); /*****************************************************************************/ /* * Declare the driver isa structure. */ struct isa_driver stlidriver = { stliprobe, stliattach, stli_drvname }; /*****************************************************************************/ #if VFREEBSD >= 220 /* * FreeBSD-2.2+ kernel linkage. */ #define CDEV_MAJOR 75 - -static struct cdevsw stli_cdevsw = - { stliopen, stliclose, stliread, stliwrite, - stliioctl, stlistop, noreset, stlidevtotty, - ttpoll, nommap, NULL, stli_drvname, - NULL, -1 }; +static struct cdevsw stli_cdevsw = { + stliopen, stliclose, stliread, stliwrite, + stliioctl, stlistop, noreset, stlidevtotty, + ttpoll, nommap, NULL, stli_drvname, + NULL, -1, nodump, nopsize, + D_TTY, +}; static stli_devsw_installed = 0; static void stli_drvinit(void *unused) { dev_t dev; if (! stli_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &stli_cdevsw, NULL); stli_devsw_installed = 1; } } SYSINIT(sidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,stli_drvinit,NULL) #endif /*****************************************************************************/ static stlibrd_t *stli_brdalloc(void) { stlibrd_t *brdp; brdp = (stlibrd_t *) malloc(sizeof(stlibrd_t), M_TTYS, M_NOWAIT); if (brdp == (stlibrd_t *) NULL) { printf("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlibrd_t)); return((stlibrd_t *) NULL); } bzero(brdp, sizeof(stlibrd_t)); return(brdp); } /*****************************************************************************/ /* * Find an available internal board number (unit number). The problem * is that the same unit numbers can be assigned to different class * boards - but we only want to maintain one setup board structures. */ static int stli_findfreeunit(void) { int i; for (i = 0; (i < STL_MAXBRDS); i++) if (stli_brds[i] == (stlibrd_t *) NULL) break; return((i >= STL_MAXBRDS) ? -1 : i); } /*****************************************************************************/ /* * Try and determine the ISA board type. Hopefully the board * configuration entry will help us out, using the flags field. * If not, we may ne be able to determine the board type... */ static int stli_isaprobe(struct isa_device *idp) { int btype; #if DEBUG printf("stli_isaprobe(idp=%x): unit=%d iobase=%x flags=%x\n", (int) idp, idp->id_unit, idp->id_iobase, idp->id_flags); #endif switch (idp->id_flags) { case BRD_STALLION: case BRD_BRUMBY4: case BRD_BRUMBY8: case BRD_BRUMBY16: case BRD_ONBOARD: case BRD_ONBOARD32: case BRD_ECP: btype = idp->id_flags; break; default: btype = 0; break; } return(btype); } /*****************************************************************************/ /* * Probe for an EISA board type. We should be able to read the EISA ID, * that will tell us if a board is present or not... */ static int stli_eisaprobe(struct isa_device *idp) { int btype, eid; #if DEBUG printf("stli_eisaprobe(idp=%x): unit=%d iobase=%x flags=%x\n", (int) idp, idp->id_unit, idp->id_iobase, idp->id_flags); #endif /* * Firstly check if this is an EISA system. Do this by probing for * the system board EISA ID. If this is not an EISA system then * don't bother going any further! */ outb(0xc80, 0xff); if (inb(0xc80) == 0xff) return(0); /* * Try and read the EISA ID from the board at specified address. * If one is present it will tell us the board type as well. */ outb((idp->id_iobase + 0xc80), 0xff); eid = inb(idp->id_iobase + 0xc80); eid |= inb(idp->id_iobase + 0xc81) << 8; if (eid != STL_EISAID) return(0); btype = 0; eid = inb(idp->id_iobase + 0xc82); if (eid == ECP_EISAID) btype = BRD_ECPE; else if (eid == ONB_EISAID) btype = BRD_ONBOARDE; outb((idp->id_iobase + 0xc84), 0x1); return(btype); } /*****************************************************************************/ /* * Probe for an MCA board type. Not really sure how to do this yet, * so for now just use the supplied flag specifier as board type... */ static int stli_mcaprobe(struct isa_device *idp) { int btype; #if DEBUG printf("stli_mcaprobe(idp=%x): unit=%d iobase=%x flags=%x\n", (int) idp, idp->id_unit, idp->id_iobase, idp->id_flags); #endif switch (idp->id_flags) { case BRD_ONBOARD2: case BRD_ONBOARD2_32: case BRD_ONBOARDRS: case BRD_ECHMC: case BRD_ECPMC: btype = idp->id_flags; break; default: btype = 0; break; } return(0); } /*****************************************************************************/ /* * Probe for a board. This is involved, since we need to enable the * shared memory region to see if the board is really there or not... */ static int stliprobe(struct isa_device *idp) { stlibrd_t *brdp; int btype, bclass; #if DEBUG printf("stliprobe(idp=%x): unit=%d iobase=%x flags=%x\n", (int) idp, idp->id_unit, idp->id_iobase, idp->id_flags); #endif if (idp->id_unit > STL_MAXBRDS) return(0); /* * First up determine what bus type of board we might be dealing * with. It is easy to separate out the ISA from the EISA and MCA * boards, based on their IO addresses. We may not be able to tell * the EISA and MCA apart on IO address alone... */ bclass = 0; if ((idp->id_iobase > 0) && (idp->id_iobase < 0x400)) { bclass |= BRD_ISA; } else { /* ONboard2 range */ if ((idp->id_iobase >= 0x700) && (idp->id_iobase < 0x900)) bclass |= BRD_MCA; /* EC-MCA ranges */ if ((idp->id_iobase >= 0x7000) && (idp->id_iobase < 0x7400)) bclass |= BRD_MCA; if ((idp->id_iobase >= 0x8000) && (idp->id_iobase < 0xc000)) bclass |= BRD_MCA; /* EISA board range */ if ((idp->id_iobase & ~0xf000) == 0) bclass |= BRD_EISA; } if ((bclass == 0) || (idp->id_iobase == 0)) return(0); /* * Based on the board bus type, try and figure out what it might be... */ btype = 0; if (bclass & BRD_ISA) btype = stli_isaprobe(idp); if ((btype == 0) && (bclass & BRD_EISA)) btype = stli_eisaprobe(idp); if ((btype == 0) && (bclass & BRD_MCA)) btype = stli_mcaprobe(idp); if (btype == 0) return(0); /* * Go ahead and try probing for the shared memory region now. * This way we will really know if the board is here... */ if ((brdp = stli_brdalloc()) == (stlibrd_t *) NULL) return(0); brdp->brdnr = stli_findfreeunit(); brdp->brdtype = btype; brdp->unitid = idp->id_unit; brdp->iobase = idp->id_iobase; brdp->vaddr = idp->id_maddr; brdp->paddr = vtophys(idp->id_maddr); #if DEBUG printf("%s(%d): btype=%x unit=%d brd=%d io=%x mem=%x(%x)\n", __file__, __LINE__, btype, brdp->unitid, brdp->brdnr, brdp->iobase, brdp->paddr, brdp->vaddr); #endif stli_stliprobed[idp->id_unit] = brdp->brdnr; stli_brdinit(brdp); if ((brdp->state & BST_FOUND) == 0) { stli_brds[brdp->brdnr] = (stlibrd_t *) NULL; return(0); } stli_nrbrds++; return(1); } /*****************************************************************************/ /* * Allocate resources for and initialize a board. */ static int stliattach(struct isa_device *idp) { stlibrd_t *brdp; int brdnr; #if DEBUG printf("stliattach(idp=%x): unit=%d iobase=%x\n", idp, idp->id_unit, idp->id_iobase); #endif brdnr = stli_stliprobed[idp->id_unit]; brdp = stli_brds[brdnr]; if (brdp == (stlibrd_t *) NULL) return(0); if (brdp->state & BST_FOUND) stli_brdattach(brdp); return(1); } /*****************************************************************************/ STATIC int stliopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp; stliport_t *portp; int error, callout, x; #if DEBUG printf("stliopen(dev=%x,flag=%x,mode=%x,p=%x)\n", (int) dev, flag, mode, (int) p); #endif /* * Firstly check if the supplied device number is a valid device. */ if (dev & STL_MEMDEV) return(0); portp = stli_dev2port(dev); if (portp == (stliport_t *) NULL) return(ENXIO); tp = &portp->tty; callout = minor(dev) & STL_CALLOUTDEV; error = 0; x = spltty(); stliopen_restart: /* * Wait here for the DTR drop timeout period to expire. */ while (portp->state & ST_DTRWAIT) { error = tsleep(&portp->dtrwait, (TTIPRI | PCATCH), "stlidtr", 0); if (error) goto stliopen_end; } /* * If the port is in its raw hardware initialization phase, then * hold up here 'till it is done. */ while (portp->state & (ST_INITIALIZING | ST_CLOSING)) { error = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (error) goto stliopen_end; } /* * We have a valid device, so now we check if it is already open. * If not then initialize the port hardware and set up the tty * struct as required. */ if ((tp->t_state & TS_ISOPEN) == 0) { tp->t_oproc = stli_start; tp->t_param = stli_param; tp->t_dev = dev; tp->t_termios = callout ? portp->initouttios : portp->initintios; stli_initopen(portp); wakeup(&portp->state); - ttsetwater(tp); if ((portp->sigs & TIOCM_CD) || callout) (*linesw[tp->t_line].l_modem)(tp, 1); } else { if (callout) { if (portp->callout == 0) { error = EBUSY; goto stliopen_end; } } else { if (portp->callout != 0) { if (flag & O_NONBLOCK) { error = EBUSY; goto stliopen_end; } error = tsleep(&portp->callout, (TTIPRI | PCATCH), "stlicall", 0); if (error) goto stliopen_end; goto stliopen_restart; } } if ((tp->t_state & TS_XCLUDE) && (p->p_ucred->cr_uid != 0)) { error = EBUSY; goto stliopen_end; } } /* * If this port is not the callout device and we do not have carrier * then we need to sleep, waiting for it to be asserted. */ if (((tp->t_state & TS_CARR_ON) == 0) && !callout && ((tp->t_cflag & CLOCAL) == 0) && ((flag & O_NONBLOCK) == 0)) { portp->waitopens++; error = tsleep(TSA_CARR_ON(tp), (TTIPRI | PCATCH), "stlidcd",0); portp->waitopens--; if (error) goto stliopen_end; goto stliopen_restart; } /* * Open the line discipline. */ error = (*linesw[tp->t_line].l_open)(dev, tp); stli_ttyoptim(portp, &tp->t_termios); if ((tp->t_state & TS_ISOPEN) && callout) portp->callout = 1; /* * If for any reason we get to here and the port is not actually * open then close of the physical hardware - no point leaving it * active when the open failed... */ stliopen_end: splx(x); if (((tp->t_state & TS_ISOPEN) == 0) && (portp->waitopens == 0)) stli_shutdownclose(portp); return(error); } /*****************************************************************************/ STATIC int stliclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp; stliport_t *portp; int x; #if DEBUG printf("stliclose(dev=%x,flag=%x,mode=%x,p=%x)\n", dev, flag, mode, p); #endif if (dev & STL_MEMDEV) return(0); portp = stli_dev2port(dev); if (portp == (stliport_t *) NULL) return(ENXIO); tp = &portp->tty; x = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); stli_ttyoptim(portp, &tp->t_termios); stli_shutdownclose(portp); ttyclose(tp); splx(x); return(0); } /*****************************************************************************/ STATIC int stliread(dev_t dev, struct uio *uiop, int flag) { stliport_t *portp; #if DEBUG printf("stliread(dev=%x,uiop=%x,flag=%x)\n", dev, uiop, flag); #endif if (dev & STL_MEMDEV) return(stli_memrw(dev, uiop, flag)); portp = stli_dev2port(dev); if (portp == (stliport_t *) NULL) return(ENODEV); return((*linesw[portp->tty.t_line].l_read)(&portp->tty, uiop, flag)); } /*****************************************************************************/ #if VFREEBSD >= 220 STATIC void stlistop(struct tty *tp, int rw) { #if DEBUG printf("stlistop(tp=%x,rw=%x)\n", (int) tp, rw); #endif stli_flush((stliport_t *) tp, rw); } #else STATIC int stlistop(struct tty *tp, int rw) { #if DEBUG printf("stlistop(tp=%x,rw=%x)\n", (int) tp, rw); #endif stli_flush((stliport_t *) tp, rw); return(0); } #endif /*****************************************************************************/ STATIC struct tty *stlidevtotty(dev_t dev) { #if DEBUG printf("stlidevtotty(dev=%x)\n", dev); #endif return((struct tty *) stli_dev2port(dev)); } /*****************************************************************************/ STATIC int stliwrite(dev_t dev, struct uio *uiop, int flag) { stliport_t *portp; #if DEBUG printf("stliwrite(dev=%x,uiop=%x,flag=%x)\n", dev, uiop, flag); #endif if (dev & STL_MEMDEV) return(stli_memrw(dev, uiop, flag)); portp = stli_dev2port(dev); if (portp == (stliport_t *) NULL) return(ENODEV); return((*linesw[portp->tty.t_line].l_write)(&portp->tty, uiop, flag)); } /*****************************************************************************/ STATIC int stliioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct termios *newtios, *localtios; struct tty *tp; stlibrd_t *brdp; stliport_t *portp; long arg; int error, i, x; #if DEBUG printf("stliioctl(dev=%x,cmd=%x,data=%x,flag=%x,p=%x)\n", dev, cmd, data, flag, p); #endif dev = minor(dev); if (dev & STL_MEMDEV) return(stli_memioctl(dev, cmd, data, flag, p)); portp = stli_dev2port(dev); if (portp == (stliport_t *) NULL) return(ENODEV); if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL) return(ENODEV); tp = &portp->tty; error = 0; /* * First up handle ioctls on the control devices. */ if (dev & STL_CTRLDEV) { if ((dev & STL_CTRLDEV) == STL_CTRLINIT) localtios = (dev & STL_CALLOUTDEV) ? &portp->initouttios : &portp->initintios; else if ((dev & STL_CTRLDEV) == STL_CTRLLOCK) localtios = (dev & STL_CALLOUTDEV) ? &portp->lockouttios : &portp->lockintios; else return(ENODEV); switch (cmd) { case TIOCSETA: if ((error = suser(p->p_ucred, &p->p_acflag)) == 0) *localtios = *((struct termios *) data); break; case TIOCGETA: *((struct termios *) data) = *localtios; break; case TIOCGETD: *((int *) data) = TTYDISC; break; case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); break; default: error = ENOTTY; break; } return(error); } /* * Deal with 4.3 compatability issues if we have too... */ #if defined(COMPAT_43) || defined(COMPAT_SUNOS) if (1) { struct termios tios; int oldcmd; tios = tp->t_termios; oldcmd = cmd; if ((error = ttsetcompat(tp, &cmd, data, &tios))) return(error); if (cmd != oldcmd) data = (caddr_t) &tios; } #endif /* * Carry out some pre-cmd processing work first... * Hmmm, not so sure we want this, disable for now... */ if ((cmd == TIOCSETA) || (cmd == TIOCSETAW) || (cmd == TIOCSETAF)) { newtios = (struct termios *) data; localtios = (dev & STL_CALLOUTDEV) ? &portp->lockouttios : &portp->lockintios; newtios->c_iflag = (tp->t_iflag & localtios->c_iflag) | (newtios->c_iflag & ~localtios->c_iflag); newtios->c_oflag = (tp->t_oflag & localtios->c_oflag) | (newtios->c_oflag & ~localtios->c_oflag); newtios->c_cflag = (tp->t_cflag & localtios->c_cflag) | (newtios->c_cflag & ~localtios->c_cflag); newtios->c_lflag = (tp->t_lflag & localtios->c_lflag) | (newtios->c_lflag & ~localtios->c_lflag); for (i = 0; (i < NCCS); i++) { if (localtios->c_cc[i] != 0) newtios->c_cc[i] = tp->t_cc[i]; } if (localtios->c_ispeed != 0) newtios->c_ispeed = tp->t_ispeed; if (localtios->c_ospeed != 0) newtios->c_ospeed = tp->t_ospeed; } /* * Call the line discipline and the common command processing to * process this command (if they can). */ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return(error); x = spltty(); error = ttioctl(tp, cmd, data, flag); stli_ttyoptim(portp, &tp->t_termios); if (error != ENOIOCTL) { splx(x); return(error); } error = 0; /* * Process local commands here. These are all commands that only we * can take care of (they all rely on actually doing something special * to the actual hardware). */ switch (cmd) { case TIOCSBRK: arg = BREAKON; error = stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(unsigned long), 0); break; case TIOCCBRK: arg = BREAKOFF; error = stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(unsigned long), 0); break; case TIOCSDTR: stli_mkasysigs(&portp->asig, 1, -1); error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); break; case TIOCCDTR: stli_mkasysigs(&portp->asig, 0, -1); error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); break; case TIOCMSET: i = *((int *) data); stli_mkasysigs(&portp->asig, ((i & TIOCM_DTR) ? 1 : 0), ((i & TIOCM_RTS) ? 1 : 0)); error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); break; case TIOCMBIS: i = *((int *) data); stli_mkasysigs(&portp->asig, ((i & TIOCM_DTR) ? 1 : -1), ((i & TIOCM_RTS) ? 1 : -1)); error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); break; case TIOCMBIC: i = *((int *) data); stli_mkasysigs(&portp->asig, ((i & TIOCM_DTR) ? 0 : -1), ((i & TIOCM_RTS) ? 0 : -1)); error = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); break; case TIOCMGET: if ((error = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0) break; portp->sigs = stli_mktiocm(portp->asig.sigvalue); *((int *) data) = (portp->sigs | TIOCM_LE); break; case TIOCMSDTRWAIT: if ((error = suser(p->p_ucred, &p->p_acflag)) == 0) portp->dtrwait = *((int *) data) * hz / 100; break; case TIOCMGDTRWAIT: *((int *) data) = portp->dtrwait * 100 / hz; break; case TIOCTIMESTAMP: portp->dotimestamp = 1; *((struct timeval *) data) = portp->timestamp; break; default: error = ENOTTY; break; } splx(x); return(error); } /*****************************************************************************/ /* * Convert the specified minor device number into a port struct * pointer. Return NULL if the device number is not a valid port. */ STATIC stliport_t *stli_dev2port(dev_t dev) { stlibrd_t *brdp; brdp = stli_brds[MKDEV2BRD(dev)]; if (brdp == (stlibrd_t *) NULL) return((stliport_t *) NULL); if ((brdp->state & BST_STARTED) == 0) return((stliport_t *) NULL); return(brdp->ports[MKDEV2PORT(dev)]); } /*****************************************************************************/ /* * Carry out first open operations on a port. This involves a number of * commands to be sent to the slave. We need to open the port, set the * notification events, set the initial port settings, get and set the * initial signal values. We sleep and wait in between each one. But * this still all happens pretty quickly. */ static int stli_initopen(stliport_t *portp) { stlibrd_t *brdp; asynotify_t nt; asyport_t aport; int rc; #if DEBUG printf("stli_initopen(portp=%x)\n", (int) portp); #endif if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL) return(ENXIO); if (portp->state & ST_INITIALIZED) return(0); portp->state |= ST_INITIALIZED; if ((rc = stli_rawopen(brdp, portp, 0, 1)) < 0) return(rc); bzero(&nt, sizeof(asynotify_t)); nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK); nt.signal = SG_DCD; if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt, sizeof(asynotify_t), 0)) < 0) return(rc); stli_mkasyport(portp, &aport, &portp->tty.t_termios); if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0)) < 0) return(rc); portp->state |= ST_GETSIGS; if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig, sizeof(asysigs_t), 1)) < 0) return(rc); if (portp->state & ST_GETSIGS) { portp->sigs = stli_mktiocm(portp->asig.sigvalue); portp->state &= ~ST_GETSIGS; } stli_mkasysigs(&portp->asig, 1, 1); if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0)) < 0) return(rc); return(0); } /*****************************************************************************/ /* * Shutdown the hardware of a port. */ static int stli_shutdownclose(stliport_t *portp) { stlibrd_t *brdp; struct tty *tp; int x; #if DEBUG printf("stli_shutdownclose(portp=%x): brdnr=%d panelnr=%d portnr=%d\n", portp, portp->brdnr, portp->panelnr, portp->portnr); #endif if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL) return(ENXIO); tp = &portp->tty; stli_rawclose(brdp, portp, 0, 0); stli_flush(portp, (FWRITE | FREAD)); if (tp->t_cflag & HUPCL) { x = spltty(); stli_mkasysigs(&portp->asig, 0, 0); if (portp->state & ST_CMDING) { portp->state |= ST_DOSIGS; } else { stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig, sizeof(asysigs_t), 0); } splx(x); if (portp->dtrwait != 0) { portp->state |= ST_DTRWAIT; timeout(stli_dtrwakeup, portp, portp->dtrwait); } } portp->callout = 0; portp->state &= ~ST_INITIALIZED; wakeup(&portp->callout); wakeup(TSA_CARR_ON(tp)); return(0); } /*****************************************************************************/ /* * Clear the DTR waiting flag, and wake up any sleepers waiting for * DTR wait period to finish. */ static void stli_dtrwakeup(void *arg) { stliport_t *portp; portp = (stliport_t *) arg; portp->state &= ~ST_DTRWAIT; wakeup(&portp->dtrwait); } /*****************************************************************************/ /* * Send an open message to the slave. This will sleep waiting for the * acknowledgement, so must have user context. We need to co-ordinate * with close events here, since we don't want open and close events * to overlap. */ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait) { volatile cdkhdr_t *hdrp; volatile cdkctrl_t *cp; volatile unsigned char *bits; int rc, x; #if DEBUG printf("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait); #endif x = spltty(); /* * Slave is already closing this port. This can happen if a hangup * occurs on this port. So we must wait until it is complete. The * order of opens and closes may not be preserved across shared * memory, so we must wait until it is complete. */ while (portp->state & ST_CLOSING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { splx(x); return(rc); } } /* * Everything is ready now, so write the open message into shared * memory. Once the message is in set the service bits to say that * this port wants service. */ EBRDENABLE(brdp); cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; cp->openarg = arg; cp->open = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); if (wait == 0) { splx(x); return(0); } /* * Slave is in action, so now we must wait for the open acknowledgment * to come back. */ rc = 0; portp->state |= ST_OPENING; while (portp->state & ST_OPENING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { splx(x); return(rc); } } splx(x); if ((rc == 0) && (portp->rc != 0)) rc = EIO; return(rc); } /*****************************************************************************/ /* * Send a close message to the slave. Normally this will sleep waiting * for the acknowledgement, but if wait parameter is 0 it will not. If * wait is true then must have user context (to sleep). */ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait) { volatile cdkhdr_t *hdrp; volatile cdkctrl_t *cp; volatile unsigned char *bits; int rc, x; #if DEBUG printf("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n", (int) brdp, (int) portp, (int) arg, wait); #endif x = spltty(); /* * Slave is already closing this port. This can happen if a hangup * occurs on this port. */ if (wait) { while (portp->state & ST_CLOSING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { splx(x); return(rc); } } } /* * Write the close command into shared memory. */ EBRDENABLE(brdp); cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; cp->closearg = arg; cp->close = 1; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; *bits |= portp->portbit; EBRDDISABLE(brdp); portp->state |= ST_CLOSING; if (wait == 0) { splx(x); return(0); } /* * Slave is in action, so now we must wait for the open acknowledgment * to come back. */ rc = 0; while (portp->state & ST_CLOSING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { splx(x); return(rc); } } splx(x); if ((rc == 0) && (portp->rc != 0)) rc = EIO; return(rc); } /*****************************************************************************/ /* * Send a command to the slave and wait for the response. This must * have user context (it sleeps). This routine is generic in that it * can send any type of command. Its purpose is to wait for that command * to complete (as opposed to initiating the command then returning). */ static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback) { int rc, x; #if DEBUG printf("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d," "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback); #endif x = spltty(); while (portp->state & ST_CMDING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { splx(x); return(rc); } } stli_sendcmd(brdp, portp, cmd, arg, size, copyback); while (portp->state & ST_CMDING) { rc = tsleep(&portp->state, (TTIPRI | PCATCH), "stliraw", 0); if (rc) { splx(x); return(rc); } } splx(x); if (portp->rc != 0) return(EIO); return(0); } /*****************************************************************************/ /* * Start (or continue) the transfer of TX data on this port. If the * port is not currently busy then load up the interrupt ring queue * buffer and kick of the transmitter. If the port is running low on * TX data then refill the ring queue. This routine is also used to * activate input flow control! */ static void stli_start(struct tty *tp) { volatile cdkasy_t *ap; volatile cdkhdr_t *hdrp; volatile unsigned char *bits; unsigned char *shbuf; stliport_t *portp; stlibrd_t *brdp; unsigned int len, stlen, head, tail, size; int count, x; portp = (stliport_t *) tp; #if DEBUG printf("stli_start(tp=%x): brdnr=%d portnr=%d\n", (int) tp, portp->brdnr, portp->portnr); #endif x = spltty(); #if VFREEBSD == 205 /* * Check if the output cooked clist buffers are near empty, wake up * the line discipline to fill it up. */ if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(&tp->t_outq); } selwakeup(&tp->t_wsel); } #endif if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { splx(x); return; } /* * Copy data from the clists into the interrupt ring queue. This will * require at most 2 copys... What we do is calculate how many chars * can fit into the ring queue, and how many can fit in 1 copy. If after * the first copy there is still more room then do the second copy. */ if (tp->t_outq.c_cc != 0) { brdp = stli_brds[portp->brdnr]; if (brdp == (stlibrd_t *) NULL) { splx(x); return; } EBRDENABLE(brdp); ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); head = (unsigned int) ap->txq.head; tail = (unsigned int) ap->txq.tail; if (tail != ((unsigned int) ap->txq.tail)) tail = (unsigned int) ap->txq.tail; size = portp->txsize; if (head >= tail) { len = size - (head - tail) - 1; stlen = size - head; } else { len = tail - head - 1; stlen = len; } count = 0; shbuf = (char *) EBRDGETMEMPTR(brdp, portp->txoffset); if (len > 0) { stlen = MIN(len, stlen); count = q_to_b(&tp->t_outq, (shbuf + head), stlen); len -= count; head += count; if (head >= size) { head = 0; if (len > 0) { stlen = q_to_b(&tp->t_outq, shbuf, len); head += stlen; count += stlen; } } } ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); ap->txq.head = head; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; *bits |= portp->portbit; portp->state |= ST_TXBUSY; tp->t_state |= TS_BUSY; EBRDDISABLE(brdp); } #if VFREEBSD != 205 /* * Do any writer wakeups. */ ttwwakeup(tp); #endif splx(x); } /*****************************************************************************/ /* * Send a new port configuration to the slave. */ static int stli_param(struct tty *tp, struct termios *tiosp) { stlibrd_t *brdp; stliport_t *portp; asyport_t aport; int x, rc; portp = (stliport_t *) tp; if ((brdp = stli_brds[portp->brdnr]) == (stlibrd_t *) NULL) return(ENXIO); x = spltty(); stli_mkasyport(portp, &aport, tiosp); /* can we sleep here? */ rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0); stli_ttyoptim(portp, tiosp); splx(x); return(rc); } /*****************************************************************************/ /* * Flush characters from the lower buffer. We may not have user context * so we cannot sleep waiting for it to complete. Also we need to check * if there is chars for this port in the TX cook buffer, and flush them * as well. */ static void stli_flush(stliport_t *portp, int flag) { stlibrd_t *brdp; unsigned long ftype; int x; #if DEBUG printf("stli_flush(portp=%x,flag=%x)\n", (int) portp, flag); #endif if (portp == (stliport_t *) NULL) return; if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds)) return; brdp = stli_brds[portp->brdnr]; if (brdp == (stlibrd_t *) NULL) return; x = spltty(); if (portp->state & ST_CMDING) { portp->state |= (flag & FWRITE) ? ST_DOFLUSHTX : 0; portp->state |= (flag & FREAD) ? ST_DOFLUSHRX : 0; } else { ftype = (flag & FWRITE) ? FLUSHTX : 0; ftype |= (flag & FREAD) ? FLUSHRX : 0; portp->state &= ~(ST_DOFLUSHTX | ST_DOFLUSHRX); stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(unsigned long), 0); } if ((flag & FREAD) && (stli_rxtmpport == portp)) stli_rxtmplen = 0; splx(x); } /*****************************************************************************/ /* * Generic send command routine. This will send a message to the slave, * of the specified type with the specified argument. Must be very * carefull of data that will be copied out from shared memory - * containing command results. The command completion is all done from * a poll routine that does not have user coontext. Therefore you cannot * copy back directly into user space, or to the kernel stack of a * process. This routine does not sleep, so can be called from anywhere, * and must be called with interrupt locks set. */ static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback) { volatile cdkhdr_t *hdrp; volatile cdkctrl_t *cp; volatile unsigned char *bits; #if DEBUG printf("stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d," "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd, (int) arg, size, copyback); #endif if (portp->state & ST_CMDING) { printf("STALLION: command already busy, cmd=%x!\n", (int) cmd); return; } EBRDENABLE(brdp); cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl; if (size > 0) { bcopy(arg, (void *) &(cp->args[0]), size); if (copyback) { portp->argp = arg; portp->argsize = size; } } cp->status = 0; cp->cmd = cmd; hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset + portp->portidx; *bits |= portp->portbit; portp->state |= ST_CMDING; EBRDDISABLE(brdp); } /*****************************************************************************/ /* * Read data from shared memory. This assumes that the shared memory * is enabled and that interrupts are off. Basically we just empty out * the shared memory buffer into the tty buffer. Must be carefull to * handle the case where we fill up the tty buffer, but still have * more chars to unload. */ static void stli_rxprocess(stlibrd_t *brdp, stliport_t *portp) { volatile cdkasyrq_t *rp; volatile char *shbuf; struct tty *tp; unsigned int head, tail, size; unsigned int len, stlen, i; int ch; #if DEBUG printf("stli_rxprocess(brdp=%x,portp=%d)\n", (int) brdp, (int) portp); #endif tp = &portp->tty; if ((tp->t_state & TS_ISOPEN) == 0) { stli_flush(portp, FREAD); return; } if (tp->t_state & TS_TBLOCK) return; rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq; head = (unsigned int) rp->head; if (head != ((unsigned int) rp->head)) head = (unsigned int) rp->head; tail = (unsigned int) rp->tail; size = portp->rxsize; if (head >= tail) { len = head - tail; stlen = len; } else { len = size - (tail - head); stlen = size - tail; } if (len == 0) return; shbuf = (volatile char *) EBRDGETMEMPTR(brdp, portp->rxoffset); /* * If we can bypass normal LD processing then just copy direct * from board shared memory into the tty buffers. */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (((tp->t_rawq.c_cc + len) >= TTYHOG) && ((tp->t_cflag & CRTS_IFLOW) || (tp->t_iflag & IXOFF)) && ((tp->t_state & TS_TBLOCK) == 0)) { ch = TTYHOG - tp->t_rawq.c_cc - 1; len = (ch > 0) ? ch : 0; stlen = MIN(stlen, len); tp->t_state |= TS_TBLOCK; } i = b_to_q((char *) (shbuf + tail), stlen, &tp->t_rawq); tail += stlen; len -= stlen; if (tail >= size) { tail = 0; i += b_to_q((char *) shbuf, len, &tp->t_rawq); tail += len; } portp->rxlost += i; ttwakeup(tp); rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq; rp->tail = tail; } else { /* * Copy the data from board shared memory into a local * memory buffer. Then feed them from here into the LD. * We don't want to go into board shared memory one char * at a time, it is too slow... */ if (len > TTYHOG) { len = TTYHOG - 1; stlen = min(len, stlen); } stli_rxtmpport = portp; stli_rxtmplen = len; bcopy((char *) (shbuf + tail), &stli_rxtmpbuf[0], stlen); len -= stlen; if (len > 0) bcopy((char *) shbuf, &stli_rxtmpbuf[stlen], len); for (i = 0; (i < stli_rxtmplen); i++) { ch = (unsigned char) stli_rxtmpbuf[i]; (*linesw[tp->t_line].l_rint)(ch, tp); } EBRDENABLE(brdp); rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq; if (stli_rxtmplen == 0) { head = (unsigned int) rp->head; if (head != ((unsigned int) rp->head)) head = (unsigned int) rp->head; tail = head; } else { tail += i; if (tail >= size) tail -= size; } rp->tail = tail; stli_rxtmpport = (stliport_t *) NULL; stli_rxtmplen = 0; } portp->state |= ST_RXING; } /*****************************************************************************/ /* * Set up and carry out any delayed commands. There is only a small set * of slave commands that can be done "off-level". So it is not too * difficult to deal with them as a special case here. */ static __inline void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp) { int cmd; if (portp->state & ST_DOSIGS) { if ((portp->state & ST_DOFLUSHTX) && (portp->state & ST_DOFLUSHRX)) cmd = A_SETSIGNALSF; else if (portp->state & ST_DOFLUSHTX) cmd = A_SETSIGNALSFTX; else if (portp->state & ST_DOFLUSHRX) cmd = A_SETSIGNALSFRX; else cmd = A_SETSIGNALS; portp->state &= ~(ST_DOFLUSHTX | ST_DOFLUSHRX | ST_DOSIGS); bcopy((void *) &portp->asig, (void *) &(cp->args[0]), sizeof(asysigs_t)); cp->status = 0; cp->cmd = cmd; portp->state |= ST_CMDING; } else if ((portp->state & ST_DOFLUSHTX) || (portp->state & ST_DOFLUSHRX)) { cmd = ((portp->state & ST_DOFLUSHTX) ? FLUSHTX : 0); cmd |= ((portp->state & ST_DOFLUSHRX) ? FLUSHRX : 0); portp->state &= ~(ST_DOFLUSHTX | ST_DOFLUSHRX); bcopy((void *) &cmd, (void *) &(cp->args[0]), sizeof(int)); cp->status = 0; cp->cmd = A_FLUSH; portp->state |= ST_CMDING; } } /*****************************************************************************/ /* * Host command service checking. This handles commands or messages * coming from the slave to the host. Must have board shared memory * enabled and interrupts off when called. Notice that by servicing the * read data last we don't need to change the shared memory pointer * during processing (which is a slow IO operation). * Return value indicates if this port is still awaiting actions from * the slave (like open, command, or even TX data being sent). If 0 * then port is still busy, otherwise the port request bit flag is * returned. */ static __inline int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp) { volatile cdkasy_t *ap; volatile cdkctrl_t *cp; asynotify_t nt; unsigned long oldsigs; unsigned int head, tail; int rc, donerx; #if DEBUG printf("stli_hostcmd(brdp=%x,portp=%x)\n", (int) brdp, (int) portp); #endif ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); cp = &ap->ctrl; /* * Check if we are waiting for an open completion message. */ if (portp->state & ST_OPENING) { rc = (int) cp->openarg; if ((cp->open == 0) && (rc != 0)) { if (rc > 0) rc--; cp->openarg = 0; portp->rc = rc; portp->state &= ~ST_OPENING; wakeup(&portp->state); } } /* * Check if we are waiting for a close completion message. */ if (portp->state & ST_CLOSING) { rc = (int) cp->closearg; if ((cp->close == 0) && (rc != 0)) { if (rc > 0) rc--; cp->closearg = 0; portp->rc = rc; portp->state &= ~ST_CLOSING; wakeup(&portp->state); } } /* * Check if we are waiting for a command completion message. We may * need to copy out the command results associated with this command. */ if (portp->state & ST_CMDING) { rc = cp->status; if ((cp->cmd == 0) && (rc != 0)) { if (rc > 0) rc--; if (portp->argp != (void *) NULL) { bcopy((void *) &(cp->args[0]), portp->argp, portp->argsize); portp->argp = (void *) NULL; } cp->status = 0; portp->rc = rc; portp->state &= ~ST_CMDING; stli_dodelaycmd(portp, cp); wakeup(&portp->state); } } /* * Check for any notification messages ready. This includes lots of * different types of events - RX chars ready, RX break received, * TX data low or empty in the slave, modem signals changed state. * Must be extremely carefull if we call to the LD, it may call * other routines of ours that will disable the memory... * Something else we need to be carefull of is race conditions on * marking the TX as empty... */ donerx = 0; if (ap->notify) { struct tty *tp; nt = ap->changed; ap->notify = 0; tp = &portp->tty; if (nt.signal & SG_DCD) { oldsigs = portp->sigs; portp->sigs = stli_mktiocm(nt.sigvalue); portp->state &= ~ST_GETSIGS; (*linesw[tp->t_line].l_modem)(tp, (portp->sigs & TIOCM_CD)); EBRDENABLE(brdp); } if (nt.data & DT_RXBUSY) { donerx++; stli_rxprocess(brdp, portp); } if (nt.data & DT_RXBREAK) { (*linesw[tp->t_line].l_rint)(TTY_BI, tp); EBRDENABLE(brdp); } if (nt.data & DT_TXEMPTY) { ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); head = (unsigned int) ap->txq.head; tail = (unsigned int) ap->txq.tail; if (tail != ((unsigned int) ap->txq.tail)) tail = (unsigned int) ap->txq.tail; head = (head >= tail) ? (head - tail) : portp->txsize - (tail - head); if (head == 0) { portp->state &= ~ST_TXBUSY; tp->t_state &= ~TS_BUSY; } } if (nt.data & (DT_TXEMPTY | DT_TXLOW)) { (*linesw[tp->t_line].l_start)(tp); EBRDENABLE(brdp); } } /* * It might seem odd that we are checking for more RX chars here. * But, we need to handle the case where the tty buffer was previously * filled, but we had more characters to pass up. The slave will not * send any more RX notify messages until the RX buffer has been emptied. * But it will leave the service bits on (since the buffer is not empty). * So from here we can try to process more RX chars. */ if ((!donerx) && (portp->state & ST_RXING)) { portp->state &= ~ST_RXING; stli_rxprocess(brdp, portp); } return((portp->state & (ST_OPENING | ST_CLOSING | ST_CMDING | ST_TXBUSY | ST_RXING)) ? 0 : 1); } /*****************************************************************************/ /* * Service all ports on a particular board. Assumes that the boards * shared memory is enabled, and that the page pointer is pointed * at the cdk header structure. */ static __inline void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp) { stliport_t *portp; unsigned char hostbits[(STL_MAXCHANS / 8) + 1]; unsigned char slavebits[(STL_MAXCHANS / 8) + 1]; unsigned char *slavep; int bitpos, bitat, bitsize; int channr, nrdevs, slavebitchange; bitsize = brdp->bitsize; nrdevs = brdp->nrdevs; /* * Check if slave wants any service. Basically we try to do as * little work as possible here. There are 2 levels of service * bits. So if there is nothing to do we bail early. We check * 8 service bits at a time in the inner loop, so we can bypass * the lot if none of them want service. */ bcopy((((unsigned char *) hdrp) + brdp->hostoffset), &hostbits[0], bitsize); bzero(&slavebits[0], bitsize); slavebitchange = 0; for (bitpos = 0; (bitpos < bitsize); bitpos++) { if (hostbits[bitpos] == 0) continue; channr = bitpos * 8; bitat = 0x1; for (; (channr < nrdevs); channr++, bitat <<=1) { if (hostbits[bitpos] & bitat) { portp = brdp->ports[(channr - 1)]; if (stli_hostcmd(brdp, portp)) { slavebitchange++; slavebits[bitpos] |= bitat; } } } } /* * If any of the ports are no longer busy then update them in the * slave request bits. We need to do this after, since a host port * service may initiate more slave requests... */ if (slavebitchange) { hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); slavep = ((unsigned char *) hdrp) + brdp->slaveoffset; for (bitpos = 0; (bitpos < bitsize); bitpos++) { if (slavebits[bitpos]) slavep[bitpos] &= ~slavebits[bitpos]; } } } /*****************************************************************************/ /* * Driver poll routine. This routine polls the boards in use and passes * messages back up to host when neccesary. This is actually very * CPU efficient, since we will always have the kernel poll clock, it * adds only a few cycles when idle (since board service can be * determined very easily), but when loaded generates no interrupts * (with their expensive associated context change). */ static void stli_poll(void *arg) { volatile cdkhdr_t *hdrp; stlibrd_t *brdp; int brdnr, x; x = spltty(); /* * Check each board and do any servicing required. */ for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) { brdp = stli_brds[brdnr]; if (brdp == (stlibrd_t *) NULL) continue; if ((brdp->state & BST_STARTED) == 0) continue; EBRDENABLE(brdp); hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); if (hdrp->hostreq) stli_brdpoll(brdp, hdrp); EBRDDISABLE(brdp); } splx(x); timeout(stli_poll, 0, 1); } /*****************************************************************************/ /* * Translate the termios settings into the port setting structure of * the slave. */ static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp) { #if DEBUG printf("stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n", (int) portp, (int) pp, (int) tiosp); #endif bzero(pp, sizeof(asyport_t)); /* * Start of by setting the baud, char size, parity and stop bit info. */ if (tiosp->c_ispeed == 0) tiosp->c_ispeed = tiosp->c_ospeed; if ((tiosp->c_ospeed < 0) || (tiosp->c_ospeed > STL_MAXBAUD)) tiosp->c_ospeed = STL_MAXBAUD; pp->baudout = tiosp->c_ospeed; pp->baudin = pp->baudout; switch (tiosp->c_cflag & CSIZE) { case CS5: pp->csize = 5; break; case CS6: pp->csize = 6; break; case CS7: pp->csize = 7; break; default: pp->csize = 8; break; } if (tiosp->c_cflag & CSTOPB) pp->stopbs = PT_STOP2; else pp->stopbs = PT_STOP1; if (tiosp->c_cflag & PARENB) { if (tiosp->c_cflag & PARODD) pp->parity = PT_ODDPARITY; else pp->parity = PT_EVENPARITY; } else { pp->parity = PT_NOPARITY; } if (tiosp->c_iflag & ISTRIP) pp->iflag |= FI_ISTRIP; /* * Set up any flow control options enabled. */ if (tiosp->c_iflag & IXON) { pp->flow |= F_IXON; if (tiosp->c_iflag & IXANY) pp->flow |= F_IXANY; } if (tiosp->c_iflag & IXOFF) pp->flow |= F_IXOFF; if (tiosp->c_cflag & CCTS_OFLOW) pp->flow |= F_CTSFLOW; if (tiosp->c_cflag & CRTS_IFLOW) pp->flow |= F_RTSFLOW; pp->startin = tiosp->c_cc[VSTART]; pp->stopin = tiosp->c_cc[VSTOP]; pp->startout = tiosp->c_cc[VSTART]; pp->stopout = tiosp->c_cc[VSTOP]; /* * Set up the RX char marking mask with those RX error types we must * catch. We can get the slave to help us out a little here, it will * ignore parity errors and breaks for us, and mark parity errors in * the data stream. */ if (tiosp->c_iflag & IGNPAR) pp->iflag |= FI_IGNRXERRS; if (tiosp->c_iflag & IGNBRK) pp->iflag |= FI_IGNBREAK; if (tiosp->c_iflag & (INPCK | PARMRK)) pp->iflag |= FI_1MARKRXERRS; /* * Transfer any persistent flags into the asyport structure. */ pp->pflag = portp->pflag; } /*****************************************************************************/ /* * Construct a slave signals structure for setting the DTR and RTS * signals as specified. */ static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts) { #if DEBUG printf("stli_mkasysigs(sp=%x,dtr=%d,rts=%d)\n", (int) sp, dtr, rts); #endif bzero(sp, sizeof(asysigs_t)); if (dtr >= 0) { sp->signal |= SG_DTR; sp->sigvalue |= ((dtr > 0) ? SG_DTR : 0); } if (rts >= 0) { sp->signal |= SG_RTS; sp->sigvalue |= ((rts > 0) ? SG_RTS : 0); } } /*****************************************************************************/ /* * Convert the signals returned from the slave into a local TIOCM type * signals value. We keep them localy in TIOCM format. */ static long stli_mktiocm(unsigned long sigvalue) { long tiocm; #if DEBUG printf("stli_mktiocm(sigvalue=%x)\n", (int) sigvalue); #endif tiocm = 0; tiocm |= ((sigvalue & SG_DCD) ? TIOCM_CD : 0); tiocm |= ((sigvalue & SG_CTS) ? TIOCM_CTS : 0); tiocm |= ((sigvalue & SG_RI) ? TIOCM_RI : 0); tiocm |= ((sigvalue & SG_DSR) ? TIOCM_DSR : 0); tiocm |= ((sigvalue & SG_DTR) ? TIOCM_DTR : 0); tiocm |= ((sigvalue & SG_RTS) ? TIOCM_RTS : 0); return(tiocm); } /*****************************************************************************/ /* * Enable l_rint processing bypass mode if tty modes allow it. */ static void stli_ttyoptim(stliport_t *portp, struct termios *tiosp) { struct tty *tp; tp = &portp->tty; if (((tiosp->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR)) == 0) && (((tiosp->c_iflag & BRKINT) == 0) || (tiosp->c_iflag & IGNBRK)) && (((tiosp->c_iflag & PARMRK) == 0) || ((tiosp->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))) && ((tiosp->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) ==0) && (linesw[tp->t_line].l_rint == ttyinput)) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; portp->hotchar = linesw[tp->t_line].l_hotchar; } /*****************************************************************************/ /* * All panels and ports actually attached have been worked out. All * we need to do here is set up the appropriate per port data structures. */ static int stli_initports(stlibrd_t *brdp) { stliport_t *portp; int i, panelnr, panelport; #if DEBUG printf("stli_initports(brdp=%x)\n", (int) brdp); #endif for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) { portp = (stliport_t *) malloc(sizeof(stliport_t), M_TTYS, M_NOWAIT); if (portp == (stliport_t *) NULL) { printf("STALLION: failed to allocate port structure\n"); continue; } bzero(portp, sizeof(stliport_t)); portp->portnr = i; portp->brdnr = brdp->brdnr; portp->panelnr = panelnr; portp->initintios.c_ispeed = STL_DEFSPEED; portp->initintios.c_ospeed = STL_DEFSPEED; portp->initintios.c_cflag = STL_DEFCFLAG; portp->initintios.c_iflag = 0; portp->initintios.c_oflag = 0; portp->initintios.c_lflag = 0; bcopy(&ttydefchars[0], &portp->initintios.c_cc[0], sizeof(portp->initintios.c_cc)); portp->initouttios = portp->initintios; portp->dtrwait = 3 * hz; panelport++; if (panelport >= brdp->panels[panelnr]) { panelport = 0; panelnr++; } brdp->ports[i] = portp; } return(0); } /*****************************************************************************/ /* * All the following routines are board specific hardware operations. */ static void stli_ecpinit(stlibrd_t *brdp) { unsigned long memconf; #if DEBUG printf("stli_ecpinit(brdp=%d)\n", (int) brdp); #endif outb((brdp->iobase + ECP_ATCONFR), ECP_ATSTOP); DELAY(10); outb((brdp->iobase + ECP_ATCONFR), ECP_ATDISABLE); DELAY(100); memconf = (brdp->paddr & ECP_ATADDRMASK) >> ECP_ATADDRSHFT; outb((brdp->iobase + ECP_ATMEMAR), memconf); } /*****************************************************************************/ static void stli_ecpenable(stlibrd_t *brdp) { #if DEBUG printf("stli_ecpenable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ECP_ATCONFR), ECP_ATENABLE); } /*****************************************************************************/ static void stli_ecpdisable(stlibrd_t *brdp) { #if DEBUG printf("stli_ecpdisable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ECP_ATCONFR), ECP_ATDISABLE); } /*****************************************************************************/ static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; unsigned char val; #if DEBUG printf("stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { ptr = (char *) brdp->vaddr + (offset % ECP_ATPAGESIZE); val = (unsigned char) (offset / ECP_ATPAGESIZE); } outb((brdp->iobase + ECP_ATMEMPR), val); return(ptr); } /*****************************************************************************/ static void stli_ecpreset(stlibrd_t *brdp) { #if DEBUG printf("stli_ecpreset(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ECP_ATCONFR), ECP_ATSTOP); DELAY(10); outb((brdp->iobase + ECP_ATCONFR), ECP_ATDISABLE); DELAY(500); } /*****************************************************************************/ static void stli_ecpintr(stlibrd_t *brdp) { #if DEBUG printf("stli_ecpintr(brdp=%x)\n", (int) brdp); #endif outb(brdp->iobase, 0x1); } /*****************************************************************************/ /* * The following set of functions act on ECP EISA boards. */ static void stli_ecpeiinit(stlibrd_t *brdp) { unsigned long memconf; #if DEBUG printf("stli_ecpeiinit(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ECP_EIBRDENAB), 0x1); outb((brdp->iobase + ECP_EICONFR), ECP_EISTOP); DELAY(10); outb((brdp->iobase + ECP_EICONFR), ECP_EIDISABLE); DELAY(500); memconf = (brdp->paddr & ECP_EIADDRMASKL) >> ECP_EIADDRSHFTL; outb((brdp->iobase + ECP_EIMEMARL), memconf); memconf = (brdp->paddr & ECP_EIADDRMASKH) >> ECP_EIADDRSHFTH; outb((brdp->iobase + ECP_EIMEMARH), memconf); } /*****************************************************************************/ static void stli_ecpeienable(stlibrd_t *brdp) { outb((brdp->iobase + ECP_EICONFR), ECP_EIENABLE); } /*****************************************************************************/ static void stli_ecpeidisable(stlibrd_t *brdp) { outb((brdp->iobase + ECP_EICONFR), ECP_EIDISABLE); } /*****************************************************************************/ static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; unsigned char val; #if DEBUG printf("stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { ptr = (char *) brdp->vaddr + (offset % ECP_EIPAGESIZE); if (offset < ECP_EIPAGESIZE) val = ECP_EIENABLE; else val = ECP_EIENABLE | 0x40; } outb((brdp->iobase + ECP_EICONFR), val); return(ptr); } /*****************************************************************************/ static void stli_ecpeireset(stlibrd_t *brdp) { outb((brdp->iobase + ECP_EICONFR), ECP_EISTOP); DELAY(10); outb((brdp->iobase + ECP_EICONFR), ECP_EIDISABLE); DELAY(500); } /*****************************************************************************/ /* * The following set of functions act on ECP MCA boards. */ static void stli_ecpmcenable(stlibrd_t *brdp) { outb((brdp->iobase + ECP_MCCONFR), ECP_MCENABLE); } /*****************************************************************************/ static void stli_ecpmcdisable(stlibrd_t *brdp) { outb((brdp->iobase + ECP_MCCONFR), ECP_MCDISABLE); } /*****************************************************************************/ static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; unsigned char val; if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { ptr = (char *) brdp->vaddr + (offset % ECP_MCPAGESIZE); val = ((unsigned char) (offset / ECP_MCPAGESIZE)) | ECP_MCENABLE; } outb((brdp->iobase + ECP_MCCONFR), val); return(ptr); } /*****************************************************************************/ static void stli_ecpmcreset(stlibrd_t *brdp) { outb((brdp->iobase + ECP_MCCONFR), ECP_MCSTOP); DELAY(10); outb((brdp->iobase + ECP_MCCONFR), ECP_MCDISABLE); DELAY(500); } /*****************************************************************************/ /* * The following routines act on ONboards. */ static void stli_onbinit(stlibrd_t *brdp) { unsigned long memconf; int i; #if DEBUG printf("stli_onbinit(brdp=%d)\n", (int) brdp); #endif outb((brdp->iobase + ONB_ATCONFR), ONB_ATSTOP); DELAY(10); outb((brdp->iobase + ONB_ATCONFR), ONB_ATDISABLE); for (i = 0; (i < 1000); i++) DELAY(1000); memconf = (brdp->paddr & ONB_ATADDRMASK) >> ONB_ATADDRSHFT; outb((brdp->iobase + ONB_ATMEMAR), memconf); outb(brdp->iobase, 0x1); DELAY(1000); } /*****************************************************************************/ static void stli_onbenable(stlibrd_t *brdp) { #if DEBUG printf("stli_onbenable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_ATCONFR), (ONB_ATENABLE | brdp->confbits)); } /*****************************************************************************/ static void stli_onbdisable(stlibrd_t *brdp) { #if DEBUG printf("stli_onbdisable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_ATCONFR), (ONB_ATDISABLE | brdp->confbits)); } /*****************************************************************************/ static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; #if DEBUG printf("stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; } else { ptr = (char *) brdp->vaddr + (offset % ONB_ATPAGESIZE); } return(ptr); } /*****************************************************************************/ static void stli_onbreset(stlibrd_t *brdp) { int i; #if DEBUG printf("stli_onbreset(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_ATCONFR), ONB_ATSTOP); DELAY(10); outb((brdp->iobase + ONB_ATCONFR), ONB_ATDISABLE); for (i = 0; (i < 1000); i++) DELAY(1000); } /*****************************************************************************/ /* * The following routines act on ONboard EISA. */ static void stli_onbeinit(stlibrd_t *brdp) { unsigned long memconf; int i; #if DEBUG printf("stli_onbeinit(brdp=%d)\n", (int) brdp); #endif outb((brdp->iobase + ONB_EIBRDENAB), 0x1); outb((brdp->iobase + ONB_EICONFR), ONB_EISTOP); DELAY(10); outb((brdp->iobase + ONB_EICONFR), ONB_EIDISABLE); for (i = 0; (i < 1000); i++) DELAY(1000); memconf = (brdp->paddr & ONB_EIADDRMASKL) >> ONB_EIADDRSHFTL; outb((brdp->iobase + ONB_EIMEMARL), memconf); memconf = (brdp->paddr & ONB_EIADDRMASKH) >> ONB_EIADDRSHFTH; outb((brdp->iobase + ONB_EIMEMARH), memconf); outb(brdp->iobase, 0x1); DELAY(1000); } /*****************************************************************************/ static void stli_onbeenable(stlibrd_t *brdp) { #if DEBUG printf("stli_onbeenable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_EICONFR), ONB_EIENABLE); } /*****************************************************************************/ static void stli_onbedisable(stlibrd_t *brdp) { #if DEBUG printf("stli_onbedisable(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_EICONFR), ONB_EIDISABLE); } /*****************************************************************************/ static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; unsigned char val; #if DEBUG printf("stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n", (int) brdp, (int) offset, line); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { ptr = (char *) brdp->vaddr + (offset % ONB_EIPAGESIZE); if (offset < ONB_EIPAGESIZE) val = ONB_EIENABLE; else val = ONB_EIENABLE | 0x40; } outb((brdp->iobase + ONB_EICONFR), val); return(ptr); } /*****************************************************************************/ static void stli_onbereset(stlibrd_t *brdp) { int i; #if DEBUG printf("stli_onbereset(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + ONB_EICONFR), ONB_EISTOP); DELAY(10); outb((brdp->iobase + ONB_EICONFR), ONB_EIDISABLE); for (i = 0; (i < 1000); i++) DELAY(1000); } /*****************************************************************************/ /* * The following routines act on Brumby boards. */ static void stli_bbyinit(stlibrd_t *brdp) { int i; #if DEBUG printf("stli_bbyinit(brdp=%d)\n", (int) brdp); #endif outb((brdp->iobase + BBY_ATCONFR), BBY_ATSTOP); DELAY(10); outb((brdp->iobase + BBY_ATCONFR), 0); for (i = 0; (i < 1000); i++) DELAY(1000); outb(brdp->iobase, 0x1); DELAY(1000); } /*****************************************************************************/ static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; unsigned char val; #if DEBUG printf("stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; val = 0; } else { ptr = (char *) brdp->vaddr + (offset % BBY_PAGESIZE); val = (unsigned char) (offset / BBY_PAGESIZE); } outb((brdp->iobase + BBY_ATCONFR), val); return(ptr); } /*****************************************************************************/ static void stli_bbyreset(stlibrd_t *brdp) { int i; #if DEBUG printf("stli_bbyreset(brdp=%x)\n", (int) brdp); #endif outb((brdp->iobase + BBY_ATCONFR), BBY_ATSTOP); DELAY(10); outb((brdp->iobase + BBY_ATCONFR), 0); for (i = 0; (i < 1000); i++) DELAY(1000); } /*****************************************************************************/ /* * The following routines act on original old Stallion boards. */ static void stli_stalinit(stlibrd_t *brdp) { int i; #if DEBUG printf("stli_stalinit(brdp=%d)\n", (int) brdp); #endif outb(brdp->iobase, 0x1); for (i = 0; (i < 1000); i++) DELAY(1000); } /*****************************************************************************/ static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line) { void *ptr; #if DEBUG printf("stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp, (int) offset); #endif if (offset > brdp->memsize) { printf("STALLION: shared memory pointer=%x out of range at " "line=%d(%d), brd=%d\n", (int) offset, line, __LINE__, brdp->brdnr); ptr = 0; } else { ptr = (char *) brdp->vaddr + (offset % STAL_PAGESIZE); } return(ptr); } /*****************************************************************************/ static void stli_stalreset(stlibrd_t *brdp) { volatile unsigned long *vecp; int i; #if DEBUG printf("stli_stalreset(brdp=%x)\n", (int) brdp); #endif vecp = (volatile unsigned long *) ((char *) brdp->vaddr + 0x30); *vecp = 0xffff0000; outb(brdp->iobase, 0); for (i = 0; (i < 1000); i++) DELAY(1000); } /*****************************************************************************/ /* * Try to find an ECP board and initialize it. This handles only ECP * board types. */ static int stli_initecp(stlibrd_t *brdp) { cdkecpsig_t sig; cdkecpsig_t *sigsp; unsigned int status, nxtid; int panelnr; #if DEBUG printf("stli_initecp(brdp=%x)\n", (int) brdp); #endif /* * Do a basic sanity check on the IO and memory addresses. */ if ((brdp->iobase == 0) || (brdp->paddr == 0)) return(EINVAL); /* * Based on the specific board type setup the common vars to access * and enable shared memory. Set all board specific information now * as well. */ switch (brdp->brdtype) { case BRD_ECP: brdp->memsize = ECP_MEMSIZE; brdp->pagesize = ECP_ATPAGESIZE; brdp->init = stli_ecpinit; brdp->enable = stli_ecpenable; brdp->reenable = stli_ecpenable; brdp->disable = stli_ecpdisable; brdp->getmemptr = stli_ecpgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpreset; break; case BRD_ECPE: brdp->memsize = ECP_MEMSIZE; brdp->pagesize = ECP_EIPAGESIZE; brdp->init = stli_ecpeiinit; brdp->enable = stli_ecpeienable; brdp->reenable = stli_ecpeienable; brdp->disable = stli_ecpeidisable; brdp->getmemptr = stli_ecpeigetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpeireset; break; case BRD_ECPMC: brdp->memsize = ECP_MEMSIZE; brdp->pagesize = ECP_MCPAGESIZE; brdp->init = NULL; brdp->enable = stli_ecpmcenable; brdp->reenable = stli_ecpmcenable; brdp->disable = stli_ecpmcdisable; brdp->getmemptr = stli_ecpmcgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_ecpmcreset; break; default: return(EINVAL); } /* * The per-board operations structure is all setup, so now lets go * and get the board operational. Firstly initialize board configuration * registers. */ EBRDINIT(brdp); /* * Now that all specific code is set up, enable the shared memory and * look for the a signature area that will tell us exactly what board * this is, and what it is connected to it. */ EBRDENABLE(brdp); sigsp = (cdkecpsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR); bcopy(sigsp, &sig, sizeof(cdkecpsig_t)); EBRDDISABLE(brdp); #if 0 printf("%s(%d): sig-> magic=%x rom=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n", __file__, __LINE__, (int) sig.magic, sig.romver, sig.panelid[0], (int) sig.panelid[1], (int) sig.panelid[2], (int) sig.panelid[3], (int) sig.panelid[4], (int) sig.panelid[5], (int) sig.panelid[6], (int) sig.panelid[7]); #endif if (sig.magic != ECP_MAGIC) return(ENXIO); /* * Scan through the signature looking at the panels connected to the * board. Calculate the total number of ports as we go. */ for (panelnr = 0, nxtid = 0; (panelnr < STL_MAXPANELS); panelnr++) { status = sig.panelid[nxtid]; if ((status & ECH_PNLIDMASK) != nxtid) break; brdp->panelids[panelnr] = status; if (status & ECH_PNL16PORT) { brdp->panels[panelnr] = 16; brdp->nrports += 16; nxtid += 2; } else { brdp->panels[panelnr] = 8; brdp->nrports += 8; nxtid++; } brdp->nrpanels++; } brdp->state |= BST_FOUND; return(0); } /*****************************************************************************/ /* * Try to find an ONboard, Brumby or Stallion board and initialize it. * This handles only these board types. */ static int stli_initonb(stlibrd_t *brdp) { cdkonbsig_t sig; cdkonbsig_t *sigsp; int i; #if DEBUG printf("stli_initonb(brdp=%x)\n", (int) brdp); #endif /* * Do a basic sanity check on the IO and memory addresses. */ if ((brdp->iobase == 0) || (brdp->paddr == 0)) return(EINVAL); /* * Based on the specific board type setup the common vars to access * and enable shared memory. Set all board specific information now * as well. */ switch (brdp->brdtype) { case BRD_ONBOARD: case BRD_ONBOARD32: case BRD_ONBOARD2: case BRD_ONBOARD2_32: case BRD_ONBOARDRS: brdp->memsize = ONB_MEMSIZE; brdp->pagesize = ONB_ATPAGESIZE; brdp->init = stli_onbinit; brdp->enable = stli_onbenable; brdp->reenable = stli_onbenable; brdp->disable = stli_onbdisable; brdp->getmemptr = stli_onbgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_onbreset; brdp->confbits = (brdp->paddr > 0x100000) ? ONB_HIMEMENAB : 0; break; case BRD_ONBOARDE: brdp->memsize = ONB_EIMEMSIZE; brdp->pagesize = ONB_EIPAGESIZE; brdp->init = stli_onbeinit; brdp->enable = stli_onbeenable; brdp->reenable = stli_onbeenable; brdp->disable = stli_onbedisable; brdp->getmemptr = stli_onbegetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_onbereset; break; case BRD_BRUMBY4: case BRD_BRUMBY8: case BRD_BRUMBY16: brdp->memsize = BBY_MEMSIZE; brdp->pagesize = BBY_PAGESIZE; brdp->init = stli_bbyinit; brdp->enable = NULL; brdp->reenable = NULL; brdp->disable = NULL; brdp->getmemptr = stli_bbygetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_bbyreset; break; case BRD_STALLION: brdp->memsize = STAL_MEMSIZE; brdp->pagesize = STAL_PAGESIZE; brdp->init = stli_stalinit; brdp->enable = NULL; brdp->reenable = NULL; brdp->disable = NULL; brdp->getmemptr = stli_stalgetmemptr; brdp->intr = stli_ecpintr; brdp->reset = stli_stalreset; break; default: return(EINVAL); } /* * The per-board operations structure is all setup, so now lets go * and get the board operational. Firstly initialize board configuration * registers. */ EBRDINIT(brdp); /* * Now that all specific code is set up, enable the shared memory and * look for the a signature area that will tell us exactly what board * this is, and how many ports. */ EBRDENABLE(brdp); sigsp = (cdkonbsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR); bcopy(sigsp, &sig, sizeof(cdkonbsig_t)); EBRDDISABLE(brdp); #if 0 printf("%s(%d): sig-> magic=%x:%x:%x:%x romver=%x amask=%x:%x:%x\n", __file__, __LINE__, sig.magic0, sig.magic1, sig.magic2, sig.magic3, sig.romver, sig.amask0, sig.amask1, sig.amask2); #endif if ((sig.magic0 != ONB_MAGIC0) || (sig.magic1 != ONB_MAGIC1) || (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3)) return(ENXIO); /* * Scan through the signature alive mask and calculate how many ports * there are on this board. */ brdp->nrpanels = 1; if (sig.amask1) { brdp->nrports = 32; } else { for (i = 0; (i < 16); i++) { if (((sig.amask0 << i) & 0x8000) == 0) break; } brdp->nrports = i; } brdp->panels[0] = brdp->nrports; brdp->state |= BST_FOUND; return(0); } /*****************************************************************************/ /* * Start up a running board. This routine is only called after the * code has been down loaded to the board and is operational. It will * read in the memory map, and get the show on the road... */ static int stli_startbrd(stlibrd_t *brdp) { volatile cdkhdr_t *hdrp; volatile cdkmem_t *memp; volatile cdkasy_t *ap; stliport_t *portp; int portnr, nrdevs, i, rc, x; #if DEBUG printf("stli_startbrd(brdp=%x)\n", (int) brdp); #endif rc = 0; x = spltty(); EBRDENABLE(brdp); hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR); nrdevs = hdrp->nrdevs; #if 0 printf("%s(%d): CDK version %d.%d.%d --> nrdevs=%d memp=%x hostp=%x " "slavep=%x\n", __file__, __LINE__, hdrp->ver_release, hdrp->ver_modification, hdrp->ver_fix, nrdevs, (int) hdrp->memp, (int) hdrp->hostp, (int) hdrp->slavep); #endif if (nrdevs < (brdp->nrports + 1)) { printf("STALLION: slave failed to allocate memory for all " "devices, devices=%d\n", nrdevs); brdp->nrports = nrdevs - 1; } brdp->nrdevs = nrdevs; brdp->hostoffset = hdrp->hostp - CDK_CDKADDR; brdp->slaveoffset = hdrp->slavep - CDK_CDKADDR; brdp->bitsize = (nrdevs + 7) / 8; memp = (volatile cdkmem_t *) (void *) (uintptr_t) hdrp->memp; if (((uintptr_t) (void *) memp) > brdp->memsize) { printf("STALLION: corrupted shared memory region?\n"); rc = EIO; goto stli_donestartup; } memp = (volatile cdkmem_t *) EBRDGETMEMPTR(brdp, (uintptr_t) (void *) memp); if (memp->dtype != TYP_ASYNCTRL) { printf("STALLION: no slave control device found\n"); rc = EIO; goto stli_donestartup; } memp++; /* * Cycle through memory allocation of each port. We are guaranteed to * have all ports inside the first page of slave window, so no need to * change pages while reading memory map. */ for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++, memp++) { if (memp->dtype != TYP_ASYNC) break; portp = brdp->ports[portnr]; if (portp == (stliport_t *) NULL) break; portp->devnr = i; portp->addr = memp->offset; portp->reqidx = (unsigned char) (i * 8 / nrdevs); portp->reqbit = (unsigned char) (0x1 << portp->reqidx); portp->portidx = (unsigned char) (i / 8); portp->portbit = (unsigned char) (0x1 << (i % 8)); } hdrp->slavereq = 0xff; /* * For each port setup a local copy of the RX and TX buffer offsets * and sizes. We do this separate from the above, because we need to * move the shared memory page... */ for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++) { portp = brdp->ports[portnr]; if (portp == (stliport_t *) NULL) break; if (portp->addr == 0) break; ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr); if (ap != (volatile cdkasy_t *) NULL) { portp->rxsize = ap->rxq.size; portp->txsize = ap->txq.size; portp->rxoffset = ap->rxq.offset; portp->txoffset = ap->txq.offset; } } stli_donestartup: EBRDDISABLE(brdp); splx(x); if (rc == 0) brdp->state |= BST_STARTED; if (stli_doingtimeout == 0) { timeout(stli_poll, 0, 1); stli_doingtimeout++; } return(rc); } /*****************************************************************************/ /* * Probe and initialize the specified board. */ static int stli_brdinit(stlibrd_t *brdp) { #if DEBUG printf("stli_brdinit(brdp=%x)\n", (int) brdp); #endif stli_brds[brdp->brdnr] = brdp; switch (brdp->brdtype) { case BRD_ECP: case BRD_ECPE: case BRD_ECPMC: stli_initecp(brdp); break; case BRD_ONBOARD: case BRD_ONBOARDE: case BRD_ONBOARD2: case BRD_ONBOARD32: case BRD_ONBOARD2_32: case BRD_ONBOARDRS: case BRD_BRUMBY4: case BRD_BRUMBY8: case BRD_BRUMBY16: case BRD_STALLION: stli_initonb(brdp); break; case BRD_EASYIO: case BRD_ECH: case BRD_ECHMC: case BRD_ECHPCI: printf("STALLION: %s board type not supported in this driver\n", stli_brdnames[brdp->brdtype]); return(ENODEV); default: printf("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype); return(ENODEV); } return(0); } /*****************************************************************************/ /* * Finish off the remaining initialization for a board. */ static int stli_brdattach(stlibrd_t *brdp) { #if DEBUG printf("stli_brdattach(brdp=%x)\n", (int) brdp); #endif #if 0 if ((brdp->state & BST_FOUND) == 0) { printf("STALLION: %s board not found, unit=%d io=%x mem=%x\n", stli_brdnames[brdp->brdtype], brdp->brdnr, brdp->iobase, (int) brdp->paddr); return(ENXIO); } #endif stli_initports(brdp); printf("stli%d: %s (driver version %s), unit=%d nrpanels=%d " "nrports=%d\n", brdp->unitid, stli_brdnames[brdp->brdtype], stli_drvversion, brdp->brdnr, brdp->nrpanels, brdp->nrports); return(0); } /*****************************************************************************/ /* * Check for possible shared memory sharing between boards. * FIX: need to start this optimization somewhere... */ static int stli_chksharemem() { stlibrd_t *brdp, *nxtbrdp; int i, j; #if DEBUG printf("stli_chksharemem()\n"); #endif /* * All found boards are initialized. Now for a little optimization, if * no boards are sharing the "shared memory" regions then we can just * leave them all enabled. This is in fact the usual case. */ stli_shared = 0; if (stli_nrbrds > 1) { for (i = 0; (i < stli_nrbrds); i++) { brdp = stli_brds[i]; if (brdp == (stlibrd_t *) NULL) continue; for (j = i + 1; (j < stli_nrbrds); j++) { nxtbrdp = stli_brds[j]; if (nxtbrdp == (stlibrd_t *) NULL) continue; if ((brdp->paddr >= nxtbrdp->paddr) && (brdp->paddr <= (nxtbrdp->paddr + nxtbrdp->memsize - 1))) { stli_shared++; break; } } } } if (stli_shared == 0) { for (i = 0; (i < stli_nrbrds); i++) { brdp = stli_brds[i]; if (brdp == (stlibrd_t *) NULL) continue; if (brdp->state & BST_FOUND) { EBRDENABLE(brdp); brdp->enable = NULL; brdp->disable = NULL; } } } return(0); } /*****************************************************************************/ /* * Return the board stats structure to user app. */ static int stli_getbrdstats(caddr_t data) { stlibrd_t *brdp; int i; #if DEBUG printf("stli_getbrdstats(data=%x)\n", data); #endif stli_brdstats = *((combrd_t *) data); if (stli_brdstats.brd >= STL_MAXBRDS) return(-ENODEV); brdp = stli_brds[stli_brdstats.brd]; if (brdp == (stlibrd_t *) NULL) return(-ENODEV); bzero(&stli_brdstats, sizeof(combrd_t)); stli_brdstats.brd = brdp->brdnr; stli_brdstats.type = brdp->brdtype; stli_brdstats.hwid = 0; stli_brdstats.state = brdp->state; stli_brdstats.ioaddr = brdp->iobase; stli_brdstats.memaddr = brdp->paddr; stli_brdstats.nrpanels = brdp->nrpanels; stli_brdstats.nrports = brdp->nrports; for (i = 0; (i < brdp->nrpanels); i++) { stli_brdstats.panels[i].panel = i; stli_brdstats.panels[i].hwid = brdp->panelids[i]; stli_brdstats.panels[i].nrports = brdp->panels[i]; } *((combrd_t *) data) = stli_brdstats; return(0); } /*****************************************************************************/ /* * Resolve the referenced port number into a port struct pointer. */ static stliport_t *stli_getport(int brdnr, int panelnr, int portnr) { stlibrd_t *brdp; int i; if ((brdnr < 0) || (brdnr >= STL_MAXBRDS)) return((stliport_t *) NULL); brdp = stli_brds[brdnr]; if (brdp == (stlibrd_t *) NULL) return((stliport_t *) NULL); for (i = 0; (i < panelnr); i++) portnr += brdp->panels[i]; if ((portnr < 0) || (portnr >= brdp->nrports)) return((stliport_t *) NULL); return(brdp->ports[portnr]); } /*****************************************************************************/ /* * Return the port stats structure to user app. A NULL port struct * pointer passed in means that we need to find out from the app * what port to get stats for (used through board control device). */ static int stli_getportstats(stliport_t *portp, caddr_t data) { stlibrd_t *brdp; int rc; if (portp == (stliport_t *) NULL) { stli_comstats = *((comstats_t *) data); portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port); if (portp == (stliport_t *) NULL) return(-ENODEV); } brdp = stli_brds[portp->brdnr]; if (brdp == (stlibrd_t *) NULL) return(-ENODEV); if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS, &stli_cdkstats, sizeof(asystats_t), 1)) < 0) return(rc); stli_comstats.brd = portp->brdnr; stli_comstats.panel = portp->panelnr; stli_comstats.port = portp->portnr; stli_comstats.state = portp->state; /*stli_comstats.flags = portp->flags;*/ stli_comstats.ttystate = portp->tty.t_state; stli_comstats.cflags = portp->tty.t_cflag; stli_comstats.iflags = portp->tty.t_iflag; stli_comstats.oflags = portp->tty.t_oflag; stli_comstats.lflags = portp->tty.t_lflag; stli_comstats.txtotal = stli_cdkstats.txchars; stli_comstats.rxtotal = stli_cdkstats.rxchars + stli_cdkstats.ringover; stli_comstats.txbuffered = stli_cdkstats.txringq; stli_comstats.rxbuffered = stli_cdkstats.rxringq; stli_comstats.rxoverrun = stli_cdkstats.overruns; stli_comstats.rxparity = stli_cdkstats.parity; stli_comstats.rxframing = stli_cdkstats.framing; stli_comstats.rxlost = stli_cdkstats.ringover + portp->rxlost; stli_comstats.rxbreaks = stli_cdkstats.rxbreaks; stli_comstats.txbreaks = stli_cdkstats.txbreaks; stli_comstats.txxon = stli_cdkstats.txstart; stli_comstats.txxoff = stli_cdkstats.txstop; stli_comstats.rxxon = stli_cdkstats.rxstart; stli_comstats.rxxoff = stli_cdkstats.rxstop; stli_comstats.rxrtsoff = stli_cdkstats.rtscnt / 2; stli_comstats.rxrtson = stli_cdkstats.rtscnt - stli_comstats.rxrtsoff; stli_comstats.modem = stli_cdkstats.dcdcnt; stli_comstats.hwid = stli_cdkstats.hwid; stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals); *((comstats_t *) data) = stli_comstats;; return(0); } /*****************************************************************************/ /* * Clear the port stats structure. We also return it zeroed out... */ static int stli_clrportstats(stliport_t *portp, caddr_t data) { stlibrd_t *brdp; int rc; if (portp == (stliport_t *) NULL) { stli_comstats = *((comstats_t *) data); portp = stli_getport(stli_comstats.brd, stli_comstats.panel, stli_comstats.port); if (portp == (stliport_t *) NULL) return(-ENODEV); } brdp = stli_brds[portp->brdnr]; if (brdp == (stlibrd_t *) NULL) return(-ENODEV); if ((rc = stli_cmdwait(brdp, portp, A_CLEARSTATS, 0, 0, 0)) < 0) return(rc); portp->rxlost = 0; bzero(&stli_comstats, sizeof(comstats_t)); stli_comstats.brd = portp->brdnr; stli_comstats.panel = portp->panelnr; stli_comstats.port = portp->portnr; *((comstats_t *) data) = stli_comstats;; return(0); } /*****************************************************************************/ /* * Code to handle an "staliomem" read and write operations. This device * is the contents of the board shared memory. It is used for down * loading the slave image (and debugging :-) */ STATIC int stli_memrw(dev_t dev, struct uio *uiop, int flag) { stlibrd_t *brdp; void *memptr; int brdnr, size, n, error, x; #if DEBUG printf("stli_memrw(dev=%x,uiop=%x,flag=%x)\n", (int) dev, (int) uiop, flag); #endif brdnr = dev & 0x7; brdp = stli_brds[brdnr]; if (brdp == (stlibrd_t *) NULL) return(ENODEV); if (brdp->state == 0) return(ENODEV); if (uiop->uio_offset >= brdp->memsize) return(0); error = 0; size = brdp->memsize - uiop->uio_offset; x = spltty(); EBRDENABLE(brdp); while (size > 0) { memptr = (void *) EBRDGETMEMPTR(brdp, uiop->uio_offset); n = MIN(size, (brdp->pagesize - (((unsigned long) uiop->uio_offset) % brdp->pagesize))); error = uiomove(memptr, n, uiop); if ((uiop->uio_resid == 0) || error) break; } EBRDDISABLE(brdp); splx(x); return(error); } /*****************************************************************************/ /* * The "staliomem" device is also required to do some special operations * on the board. We need to be able to send an interrupt to the board, * reset it, and start/stop it. */ static int stli_memioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { stlibrd_t *brdp; int brdnr, rc; #if DEBUG printf("stli_memioctl(dev=%x,cmd=%x,data=%x,flag=%x)\n", (int) dev, cmd, (int) data, flag); #endif brdnr = dev & 0x7; brdp = stli_brds[brdnr]; if (brdp == (stlibrd_t *) NULL) return(ENODEV); if (brdp->state == 0) return(ENODEV); rc = 0; switch (cmd) { case STL_BINTR: EBRDINTR(brdp); break; case STL_BSTART: rc = stli_startbrd(brdp); break; case STL_BSTOP: brdp->state &= ~BST_STARTED; break; case STL_BRESET: brdp->state &= ~BST_STARTED; EBRDRESET(brdp); if (stli_shared == 0) { if (brdp->reenable != NULL) (* brdp->reenable)(brdp); } break; case COM_GETPORTSTATS: rc = stli_getportstats((stliport_t *) NULL, data); break; case COM_CLRPORTSTATS: rc = stli_clrportstats((stliport_t *) NULL, data); break; case COM_GETBRDSTATS: rc = stli_getbrdstats(data); break; default: rc = ENOTTY; break; } return(rc); } /*****************************************************************************/ diff --git a/sys/i386/isa/pcvt/pcvt_drv.c b/sys/i386/isa/pcvt/pcvt_drv.c index 097af1da6e18..90758b2cf945 100644 --- a/sys/i386/isa/pcvt/pcvt_drv.c +++ b/sys/i386/isa/pcvt/pcvt_drv.c @@ -1,1638 +1,1639 @@ /* * Copyright (c) 1992, 1995 Hellmuth Michaelis and Joerg Wunsch. * * Copyright (c) 1992, 1993 Brian Dunford-Shore and Scott Turner. * * Copyright (c) 1993 Charles Hannum. * * All rights reserved. * * Parts of this code regarding the NetBSD interface were written * by Charles Hannum. * * This code is derived from software contributed to Berkeley by * William Jolitz and Don Ahn. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by * Hellmuth Michaelis, Brian Dunford-Shore, Joerg Wunsch, Scott Turner * and Charles Hannum. * 4. The name authors may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * @(#)pcvt_drv.c, 3.20, Last Edit-Date: [Sun Apr 2 19:09:19 1995] * */ /*---------------------------------------------------------------------------* * * pcvt_drv.c VT220 Driver Main Module / OS - Interface * --------------------------------------------------------- * -hm ------------ Release 3.00 -------------- * -hm integrating NetBSD-current patches * -hm adding ttrstrt() proto for NetBSD 0.9 * -hm kernel/console output cursor positioning fixed * -hm kernel/console output switches optional to screen 0 * -hm FreeBSD 1.1 porting * -hm the NetBSD 0.9 compiler detected a nondeclared var which was * NOT detected by neither the NetBSD-current nor FreeBSD 1.x! * -hm including Michael's keyboard fifo code * -hm Joergs patch for FreeBSD tty-malloc code * -hm adjustments for NetBSD-current * -hm FreeBSD bugfix from Joerg re timeout/untimeout casts * -jw including Thomas Gellekum's FreeBSD 1.1.5 patch * -hm adjusting #if's for NetBSD-current * -hm applying Joerg's patch for FreeBSD 2.0 * -hm patch from Onno & Martin for NetBSD-current (post 1.0) * -hm some adjustments for NetBSD 1.0 * -hm NetBSD PR #400: screen size report for new session * -hm patch from Rafael Boni/Lon Willett for NetBSD-current * -hm bell patch from Thomas Eberhardt for NetBSD * -hm multiple X server bugfixes from Lon Willett * -hm patch from joerg - pcdevtotty for FreeBSD pre-2.1 * -hm delay patch from Martin Husemann after port-i386 ml-discussion * -jw add some code to provide more FreeBSD pre-2.1 support * *---------------------------------------------------------------------------*/ #include "vt.h" #if NVT > 0 #include "opt_devfs.h" #define EXTERN /* allocate mem */ #include /* global include */ #ifdef DEVFS #include #if !defined(MAXCONS) #define MAXCONS 16 #endif static void *pcvt_devfs_token[MAXCONS]; #endif /*DEVFS*/ #if PCVT_FREEBSD >= 200 #include #else #include "machine/stdarg.h" #endif extern int getchar __P((void)); #if PCVT_NETBSD extern u_short *Crtat; #endif /* PCVT_NETBSD */ static unsigned __debug = 0; /*0xffe */ static __color; static nrow; static void vgapelinit(void); /* read initial VGA DAC palette */ #if defined XSERVER && !PCVT_USL_VT_COMPAT static int pcvt_xmode_set(int on, struct proc *p); /* initialize for X mode */ #endif /* XSERVER && !PCVT_USL_VT_COMPAT */ static d_open_t pcopen; static d_close_t pcclose; static d_read_t pcread; static d_write_t pcwrite; static d_ioctl_t pcioctl; static d_devtotty_t pcdevtotty; static d_mmap_t pcmmap; -#define CDEV_MAJOR 12 -static struct cdevsw pcdevsw = { +#define CDEV_MAJOR 12 +static struct cdevsw pc_cdevsw = { pcopen, pcclose, pcread, pcwrite, pcioctl, nullstop, noreset, pcdevtotty, - ttpoll, pcmmap, nostrategy, "vt", NULL, -1 + ttpoll, pcmmap, nostrategy, "vt", + NULL, -1, nodump, nopsize, + D_TTY, }; #if PCVT_FREEBSD > 205 struct tty * pcdevtotty(Dev_t dev) { return get_pccons(dev); } #endif /* PCVT_FREEBSD > 205 */ #if PCVT_NETBSD > 100 /* NetBSD-current Feb 20 1995 */ int pcprobe(struct device *parent, void *match, void *aux) #else #if PCVT_NETBSD > 9 int pcprobe(struct device *parent, struct device *self, void *aux) #else int pcprobe(struct isa_device *dev) #endif /* PCVT_NETBSD > 9 */ #endif /* PCVT_NETBSD > 100 */ { #ifdef _I386_ISA_KBDIO_H_ kbdc = kbdc_open(IO_KBD); if(kbdc == NULL) { reset_keyboard = 0; return 1; } reset_keyboard = 1; /* it's now safe to do kbd reset */ #endif /* _I386_ISA_KBDIO_H_ */ kbd_code_init(); #if PCVT_NETBSD > 9 ((struct isa_attach_args *)aux)->ia_iosize = 16; return 1; #else #if PCVT_NETBSD || PCVT_FREEBSD return (16); #else return 1; #endif /* PCVT_NETBSD || PCVT_FREEBSD */ #endif /* PCVT_NETBSD > 9 */ } #if PCVT_NETBSD > 9 void pcattach(struct device *parent, struct device *self, void *aux) { struct isa_attach_args *ia = aux; static struct intrhand vthand; #else int pcattach(struct isa_device *dev) { #endif /* PCVT_NETBSD > 9 */ #ifdef DEVFS int vt; #endif /*DEVFS*/ int i; vt_coldmalloc(); /* allocate memory for screens */ #if PCVT_NETBSD || PCVT_FREEBSD #if PCVT_NETBSD > 9 printf(": "); #else printf("vt%d: ", dev->id_unit); #endif /* PCVT_NETBSD > 9 */ switch(adaptor_type) { case MDA_ADAPTOR: printf("mda"); break; case CGA_ADAPTOR: printf("cga"); break; case EGA_ADAPTOR: printf("ega"); break; case VGA_ADAPTOR: printf("%s, ", (char *)vga_string(vga_type)); if(can_do_132col) printf("80/132 col"); else printf("80 col"); vgapelinit(); break; default: printf("unknown"); break; } if(color == 0) printf(", mono"); else printf(", color"); printf(", %d scr, ", totalscreens); switch(keyboard_type) { case KB_AT: printf("at-"); break; case KB_MFII: printf("mf2-"); break; default: printf("unknown "); break; } printf("kbd, [R%s]\n", PCVT_REL); #if PCVT_NETBSD || (PCVT_FREEBSD > 110 && PCVT_FREEBSD < 200) for(i = 0; i < totalscreens; i++) { #if PCVT_NETBSD pc_tty[i] = ttymalloc(); vs[i].vs_tty = pc_tty[i]; #else /* !PCVT_NETBSD */ pccons[i] = ttymalloc(pccons[i]); vs[i].vs_tty = pccons[i]; #endif /* PCVT_NETBSD */ } #if PCVT_EMU_MOUSE #if PCVT_NETBSD pc_tty[totalscreens] = ttymalloc(); /* the mouse emulator tty */ #else /* !PCVT_NETBSD */ /* the mouse emulator tty */ pc_tty[totalscreens] = ttymalloc(pccons[totalscreens]); #endif /* PCVT_NETBSD */ #endif /* PCVT_EMU_MOUSE */ #if PCVT_NETBSD pcconsp = pc_tty[0]; #else /* !PCVT_NETBSD */ pcconsp = pccons[0]; #endif /* PCVT_NETBSD */ #endif /* #if PCVT_NETBSD || (PCVT_FREEBSD > 110 && PCVT_FREEBSD < 200) */ #else /* !PCVT_NETBSD && !PCVT_FREEBSD*/ switch(adaptor_type) { case MDA_ADAPTOR: printf(" ", PCVT_REL); #endif /* PCVT_NETBSD || PCVT_FREEBSD */ #if !PCVT_NETBSD && !(PCVT_FREEBSD > 110 && PCVT_FREEBSD < 200) for(i = 0; i < totalscreens; i++) vs[i].vs_tty = &pccons[i]; #endif /* !PCVT_NETBSD && !(PCVT_FREEBSD > 110 && PCVT_FREEBSD < 200) */ async_update(UPDATE_START); /* start asynchronous updates */ #if PCVT_FREEBSD > 205 { dev_t dev = makedev(CDEV_MAJOR, 0); - cdevsw_add(&dev, &pcdevsw, NULL); + cdevsw_add(&dev, &pc_cdevsw, NULL); } #ifdef DEVFS for(vt = 0; vt < MAXCONS; vt++) { pcvt_devfs_token[vt] = - devfs_add_devswf(&pcdevsw, vt, + devfs_add_devswf(&pc_cdevsw, vt, DV_CHR, 0, 0, 0600, "ttyv%r", vt ); } #endif DEVFS #endif /* PCVT_FREEBSD > 205 */ #if PCVT_NETBSD > 9 vthand.ih_fun = pcrint; vthand.ih_arg = 0; vthand.ih_level = IPL_TTY; #if (PCVT_NETBSD > 100) && defined(IST_EDGE) intr_establish(ia->ia_irq, IST_EDGE, &vthand); #else /* PCVT_NETBSD > 100 */ intr_establish(ia->ia_irq, &vthand); #endif /* PCVT_NETBSD > 100 */ #else /* PCVT_NETBSD > 9 */ return 1; #endif /* PCVT_NETBSD > 9 */ } /* had a look at the friedl driver */ #if !PCVT_NETBSD struct tty * get_pccons(Dev_t dev) { register int i = minor(dev); #if PCVT_EMU_MOUSE if(i == totalscreens) #if !(PCVT_FREEBSD > 110 && PCVT_FREEBSD < 200) return(&pccons[i]); #else return(pccons[i]); #endif /* !(PCVT_FREEBSD > 110 && PCVT_FREEBSD < 200) */ #endif /* PCVT_EMU_MOUSE */ if(i >= PCVT_NSCREENS) return(NULL); #if !(PCVT_FREEBSD > 110 && PCVT_FREEBSD < 200) return(&pccons[i]); #else return(pccons[i]); #endif } #else struct tty * get_pccons(Dev_t dev) { register int i = minor(dev); #if PCVT_EMU_MOUSE if(i == totalscreens) return(pc_tty[i]); #endif /* PCVT_EMU_MOUSE */ if(i >= PCVT_NSCREENS) return(NULL); return(pc_tty[i]); } #endif /* !PCVT_NETBSD */ /*---------------------------------------------------------------------------* * /dev/ttyc0, /dev/ttyc1, etc. *---------------------------------------------------------------------------*/ int pcopen(Dev_t dev, int flag, int mode, struct proc *p) { register struct tty *tp; register struct video_state *vsx; int s, retval; int winsz = 0; int i = minor(dev); #if PCVT_EMU_MOUSE if(i == totalscreens) vsx = 0; else #endif /* PCVT_EMU_MOUSE */ vsx = &vs[i]; if((tp = get_pccons(dev)) == NULL) return ENXIO; #if PCVT_EMU_MOUSE if(i == totalscreens) { if(mouse.opened == 0) mouse.buttons = mouse.extendedseen = mouse.breakseen = mouse.lastmove.tv_sec = 0; mouse.minor = i; mouse.opened++; } else #endif /* PCVT_EMU_MOUSE */ vsx->openf++; tp->t_oproc = pcstart; tp->t_param = pcparam; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) == 0) { #ifdef TS_WOPEN /* not (FreeBSD-1.1.5 or FreeBSD some time after 2.0.5) */ tp->t_state |= TS_WOPEN; #endif ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; pcparam(tp, &tp->t_termios); - ttsetwater(tp); (*linesw[tp->t_line].l_modem)(tp, 1); /* fake connection */ winsz = 1; /* set winsize later */ } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) return (EBUSY); #if PCVT_NETBSD || (PCVT_FREEBSD >= 200) retval = ((*linesw[tp->t_line].l_open)(dev, tp)); #else retval = ((*linesw[tp->t_line].l_open)(dev, tp, flag)); #endif /* PCVT_NETBSD || (PCVT_FREEBSD >= 200) */ if(winsz == 1) { /* * The line discipline has clobbered t_winsize if TS_ISOPEN * was clear. (NetBSD PR #400 from Bill Sommerfeld) * We have to do this after calling the open routine, because * it does some other things in other/older *BSD releases -hm */ s = spltty(); tp->t_winsize.ws_col = vsx->maxcol; tp->t_winsize.ws_row = vsx->screen_rows; tp->t_winsize.ws_xpixel = (vsx->maxcol == 80)? 720: 1056; tp->t_winsize.ws_ypixel = 400; splx(s); } return(retval); } int pcclose(Dev_t dev, int flag, int mode, struct proc *p) { register struct tty *tp; register struct video_state *vsx; int i = minor(dev); #if PCVT_EMU_MOUSE if(i == totalscreens) vsx = 0; else #endif /* PCVT_EMU_MOUSE */ vsx = &vs[i]; if((tp = get_pccons(dev)) == NULL) return ENXIO; (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); #if PCVT_EMU_MOUSE if(i == totalscreens) mouse.opened = 0; else #endif /* PCVT_EMU_MOUSE */ vsx->openf = 0; #if PCVT_USL_VT_COMPAT #if PCVT_EMU_MOUSE if(i == totalscreens) return (0); #endif /* PCVT_EMU_MOUSE */ reset_usl_modes(vsx); #endif /* PCVT_USL_VT_COMPAT */ return(0); } int pcread(Dev_t dev, struct uio *uio, int flag) { register struct tty *tp; if((tp = get_pccons(dev)) == NULL) return ENXIO; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int pcwrite(Dev_t dev, struct uio *uio, int flag) { register struct tty *tp; if((tp = get_pccons(dev)) == NULL) return ENXIO; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } int pcioctl(Dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { register error; register struct tty *tp; if((tp = get_pccons(dev)) == NULL) return(ENXIO); /* note that some ioctl's are global, e.g. KBSTPMAT: There is * only one keyboard and different repeat rates for instance between * sessions are a suspicious wish. If you really need this make the * appropriate variables arrays */ #if PCVT_EMU_MOUSE if(minor(dev) == totalscreens) { if((error = mouse_ioctl(dev, cmd, data)) >= 0) return error; goto do_standard; } #endif /* PCVT_EMU_MOUSE */ #ifdef XSERVER #if PCVT_USL_VT_COMPAT if((error = usl_vt_ioctl(dev, cmd, data, flag, p)) >= 0) return error; /* * just for compatibility: * XFree86 < 2.0 and SuperProbe still might use it * * NB: THIS IS A HACK! Do not use it unless you explicitly need. * Especially, since the vty is not put into process-controlled * mode (this would require the application to co-operate), any * attempts to switch vtys while this kind of X mode is active * may cause serious trouble. */ switch(cmd) { case CONSOLE_X_MODE_ON: { int i; if((error = usl_vt_ioctl(dev, KDENABIO, 0, flag, p)) > 0) return error; i = KD_GRAPHICS; if((error = usl_vt_ioctl(dev, KDSETMODE, (caddr_t)&i, flag, p)) > 0) return error; i = K_RAW; error = usl_vt_ioctl(dev, KDSKBMODE, (caddr_t)&i, flag, p); return error; } case CONSOLE_X_MODE_OFF: { int i; (void)usl_vt_ioctl(dev, KDDISABIO, 0, flag, p); i = KD_TEXT; (void)usl_vt_ioctl(dev, KDSETMODE, (caddr_t)&i, flag, p); i = K_XLATE; (void)usl_vt_ioctl(dev, KDSKBMODE, (caddr_t)&i, flag, p); return 0; } case CONSOLE_X_BELL: /* * If `data' is non-null, the first int value denotes * the pitch, the second a duration. Otherwise, behaves * like BEL. */ if (data) { #if PCVT_NETBSD sysbeep(((int *)data)[0], ((int *)data)[1] * hz / 1000); #else /* PCVT_NETBSD */ sysbeep(PCVT_SYSBEEPF / ((int *)data)[0], ((int *)data)[1] * hz / 3000); #endif /* PCVT_NETBSD */ } else { sysbeep(PCVT_SYSBEEPF / 1493, hz / 4); } return (0); default: /* fall through */ ; } #else /* PCVT_USL_VT_COMPAT */ switch(cmd) { case CONSOLE_X_MODE_ON: return pcvt_xmode_set(1, p); case CONSOLE_X_MODE_OFF: return pcvt_xmode_set(0, p); case CONSOLE_X_BELL: /* * If `data' is non-null, the first int value denotes * the pitch, the second a duration. Otherwise, behaves * like BEL. */ if (data) { #if PCVT_NETBSD sysbeep(((int *)data)[0], ((int *)data)[1] * hz / 1000); #else /* PCVT_NETBSD */ sysbeep(PCVT_SYSBEEPF / ((int *)data)[0], ((int *)data)[1] * hz / 3000); #endif /* PCVT_NETBSD */ } else { sysbeep(PCVT_SYSBEEPF / 1493, hz / 4); } return (0); default: /* fall through */ ; } #endif /* PCVT_USL_VT_COMPAT */ #endif /* XSERVER */ if((error = kbdioctl(dev,cmd,data,flag)) >= 0) return error; if((error = vgaioctl(dev,cmd,data,flag)) >= 0) return error; #if PCVT_EMU_MOUSE do_standard: #endif #if PCVT_NETBSD > 9 || PCVT_FREEBSD >= 200 if((error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p)) != ENOIOCTL) return (error); #else if((error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag)) >= 0) return(error); #endif /* PCVT_NETBSD > 9 || PCVT_FREEBSD >= 200 */ #if PCVT_NETBSD > 9 if((error = ttioctl(tp, cmd, data, flag, p)) >= 0) return (error); #else if((error = ttioctl(tp, cmd, data, flag)) != ENOIOCTL) return (error); #endif /* PCVT_NETBSD > 9 */ return (ENOTTY); } int pcmmap(Dev_t dev, int offset, int nprot) { if (offset > 0x20000 - PAGE_SIZE) return -1; return i386_btop((0xa0000 + offset)); } /*---------------------------------------------------------------------------* * * handle a keyboard receive interrupt * * NOTE: the keyboard is multiplexed by means of "pcconsp" * between virtual screens. pcconsp - switching is done in * the vgapage() routine * *---------------------------------------------------------------------------*/ #if PCVT_KBD_FIFO u_char pcvt_kbd_fifo[PCVT_KBD_FIFO_SZ]; static int pcvt_kbd_wptr = 0; int pcvt_kbd_rptr = 0; short pcvt_kbd_count= 0; static u_char pcvt_timeout_scheduled = 0; static void pcvt_timeout(void *arg) { u_char *cp; #if PCVT_SLOW_INTERRUPT int s; #endif pcvt_timeout_scheduled = 0; #if PCVT_SCREENSAVER pcvt_scrnsv_reset(); #endif /* PCVT_SCREENSAVER */ while (pcvt_kbd_count) { if (((cp = sgetc(1)) != 0) && (vs[current_video_screen].openf)) { #if PCVT_NULLCHARS if(*cp == '\0') { /* pass a NULL character */ (*linesw[pcconsp->t_line].l_rint)('\0', pcconsp); } /* XXX */ else #endif /* PCVT_NULLCHARS */ while (*cp) (*linesw[pcconsp->t_line].l_rint)(*cp++ & 0xff, pcconsp); } PCVT_DISABLE_INTR (); if (!pcvt_kbd_count) pcvt_timeout_scheduled = 0; PCVT_ENABLE_INTR (); } return; } #endif void pcrint(int unit) { #if PCVT_KBD_FIFO u_char dt; u_char ret = -1; # if PCVT_SLOW_INTERRUPT int s; # endif # ifdef _I386_ISA_KBDIO_H_ int c; # endif #else /* !PCVT_KBD_FIFO */ u_char *cp; #endif /* PCVT_KBD_FIFO */ /* * in case the keyboard was not plugged in while booting, kbdc * was set to NULL at that time. When a keyboard IRQ occurs and * kbdc is NULL, the keyboard was probably reconnected to the * keyboard controller and we have to initialize the keyboard. */ if(kbdc == NULL) { kbdc = kbdc_open(IO_KBD); if(kbdc == NULL) { reset_keyboard = 0; return; } reset_keyboard = 1; kbd_code_init(); } #if PCVT_SCREENSAVER pcvt_scrnsv_reset(); #endif /* PCVT_SCREENSAVER */ #if PCVT_KBD_FIFO if (kbd_polling) { sgetc(1); return; } # ifndef _I386_ISA_KBDIO_H_ while (inb(CONTROLLER_CTRL) & STATUS_OUTPBF) /* check 8042 buffer */ { ret = 1; /* got something */ PCVT_KBD_DELAY(); /* 7 us delay */ dt = inb(CONTROLLER_DATA); /* get it 8042 data */ # else while ((c = read_kbd_data_no_wait(kbdc)) != -1) { ret = 1; /* got something */ dt = c; # endif /* _I386_ISA_KBDIO_H_ */ if (pcvt_kbd_count >= PCVT_KBD_FIFO_SZ) /* fifo overflow ? */ { log (LOG_WARNING, "pcvt: keyboard buffer overflow\n"); } else { pcvt_kbd_fifo[pcvt_kbd_wptr++] = dt; /* data -> fifo */ PCVT_DISABLE_INTR (); /* XXX necessary ? */ pcvt_kbd_count++; /* update fifo count */ PCVT_ENABLE_INTR (); if (pcvt_kbd_wptr >= PCVT_KBD_FIFO_SZ) pcvt_kbd_wptr = 0; /* wraparound pointer */ } } if (ret == 1) /* got data from keyboard ? */ { if (!pcvt_timeout_scheduled) /* if not already active .. */ { PCVT_DISABLE_INTR (); pcvt_timeout_scheduled = 1; /* flag active */ timeout(pcvt_timeout, NULL, hz / 100); /* fire off */ PCVT_ENABLE_INTR (); } } #else /* !PCVT_KBD_FIFO */ if((cp = sgetc(1)) == 0) return; if (kbd_polling) return; if(!(vs[current_video_screen].openf)) /* XXX was vs[minor(dev)] */ return; #if PCVT_NULLCHARS if(*cp == '\0') { /* pass a NULL character */ (*linesw[pcconsp->t_line].l_rint)('\0', pcconsp); return; } #endif /* PCVT_NULLCHARS */ while (*cp) (*linesw[pcconsp->t_line].l_rint)(*cp++ & 0xff, pcconsp); #endif /* PCVT_KBD_FIFO */ } #if PCVT_NETBSD || PCVT_FREEBSD >= 200 void pcstart(register struct tty *tp) { register struct clist *rbp; int s, len; u_char buf[PCVT_PCBURST]; s = spltty(); if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) goto out; tp->t_state |= TS_BUSY; splx(s); async_update(UPDATE_KERN); rbp = &tp->t_outq; /* * Call q_to_b() at spltty() to ensure that the queue is empty when * the loop terminates. */ s = spltty(); while (len = q_to_b(rbp, buf, PCVT_PCBURST)) { /* * We need to do this outside spl since it could be fairly * expensive and we don't want our serial ports to overflow. */ splx(s); sput(&buf[0], 0, len, minor(tp->t_dev)); s = spltty(); } tp->t_state &= ~TS_BUSY; #ifndef TS_ASLEEP /* FreeBSD some time after 2.0.5 */ ttwwakeup(tp); #else if (rbp->c_cc <= tp->t_lowat) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup((caddr_t)rbp); } selwakeup(&tp->t_wsel); } #endif out: splx(s); } void pcstop(struct tty *tp, int flag) { } #else /* PCVT_NETBSD || PCVT_FREEBSD >= 200 */ void pcstart(struct tty *tp) { int s; unsigned char c; s = spltty(); if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) { goto out; } for(;;) { #if !(PCVT_FREEBSD > 114) #if !(PCVT_FREEBSD > 111) if (RB_LEN(&tp->t_out) <= tp->t_lowat) #else if (RB_LEN(tp->t_out) <= tp->t_lowat) #endif { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; #if !(PCVT_FREEBSD > 111) wakeup((caddr_t)&tp->t_out); #else wakeup((caddr_t)tp->t_out); #endif } if (tp->t_wsel) { selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); tp->t_wsel = 0; tp->t_state &= ~TS_WCOLL; } } #else /* PCVT_FREEBSD > 114 */ if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) { ttwwakeup(tp); } #endif /* !PCVT_FREEBSD > 114 */ #if !(PCVT_FREEBSD > 111) if (RB_LEN(&tp->t_out) == 0) #else if (RB_LEN(tp->t_out) == 0) #endif { goto out; } #if !(PCVT_FREEBSD > 111) c = getc(&tp->t_out); #else c = getc(tp->t_out); #endif tp->t_state |= TS_BUSY; /* patch from Frank Maclachlan */ splx(s); sput(&c, 0, 1, minor(tp->t_dev)); spltty(); tp->t_state &= ~TS_BUSY; /* patch from Frank Maclachlan */ } out: splx(s); } #endif /* PCVT_NETBSD || PCVT_FREEBSD >= 200 */ /*---------------------------------------------------------------------------* * /dev/console *---------------------------------------------------------------------------*/ #if !PCVT_NETBSD /* has moved to cons.c in netbsd-current */ void consinit() /* init for kernel messages during boot */ { } #endif /* PCVT_NETBSD */ #if PCVT_FREEBSD > 205 void #else int #endif pccnprobe(struct consdev *cp) { struct isa_device *dvp; #ifdef _I386_ISA_KBDIO_H_ kbdc = kbdc_open(IO_KBD); /* * Don't reset the keyboard via `kbdio' just yet. * The system clock has not been calibrated... */ reset_keyboard = 0; #if PCVT_SCANSET == 2 /* * Turn off scancode translation early so that UserConfig * and DDB can read the keyboard. */ empty_both_buffers(kbdc, 10); set_controller_command_byte(kbdc, KBD_TRANSLATION, 0); #endif /* PCVT_SCANSET == 2 */ #endif /* _I386_ISA_KBDIO_H_ */ /* * Take control if we are the highest priority enabled display device. */ dvp = find_display(); if (dvp == NULL || dvp->id_driver != &vtdriver) { cp->cn_pri = CN_DEAD; return; } /* initialize required fields */ cp->cn_dev = makedev(CDEV_MAJOR, 0); cp->cn_pri = CN_INTERNAL; #if !PCVT_NETBSD #if !(PCVT_FREEBSD > 110 && PCVT_FREEBSD < 200) cp->cn_tp = &pccons[0]; #else cp->cn_tp = pccons[0]; #endif /* !(PCVT_FREEBSD > 110 && PCVT_FREEBSD < 200) */ #endif /* !PCVT_NETBSD */ #if PCVT_FREEBSD <= 205 return 1; #endif } #if PCVT_FREEBSD > 205 void #else int #endif pccninit(struct consdev *cp) { pcvt_is_console = 1; #if PCVT_FREEBSD <= 205 return 0; #endif } #if PCVT_FREEBSD > 205 void #else int #endif pccnputc(Dev_t dev, U_char c) { #if PCVT_SW0CNOUTP if(current_video_screen != 0) { #if !PCVT_USL_VT_COMPAT vgapage(0); #else switch_screen(0, 0); #endif /* !PCVT_USL_VT_COMPAT */ } #endif /* PCVT_SW0CNOUTP */ if (c == '\n') sput("\r", 1, 1, 0); sput((char *) &c, 1, 1, 0); async_update(UPDATE_KERN); #if PCVT_FREEBSD <= 205 return 0; #endif } int pccngetc(Dev_t dev) { register int s; static u_char *cp, cbuf[4]; /* Temp buf for multi-char key sequence. */ register u_char c; #ifdef XSERVER #if !PCVT_USL_VT_COMPAT if (pcvt_xmode) return 0; #else /* !PCVT_USL_VT_COMPAT */ if (pcvt_kbd_raw) return 0; #endif /* !PCVT_USL_VT_COMPAT */ #endif /* XSERVER */ if (cp && *cp) /* * We still have a pending key sequence, e.g. * from an arrow key. Deliver this one first. */ return (*cp++); s = spltty(); /* block pcrint while we poll */ kbd_polling = 1; cp = sgetc(0); kbd_polling = 0; splx(s); c = *cp++; if (c && *cp) { /* Preserve the multi-char sequence for the next call. */ bcopy(cp, cbuf, 3); /* take care for a trailing '\0' */ cp = cbuf; } else cp = 0; #if ! (PCVT_FREEBSD >= 201) /* this belongs to cons.c */ if (c == '\r') c = '\n'; #endif /* ! (PCVT_FREEBSD >= 201) */ return c; } #if PCVT_FREEBSD >= 200 int pccncheckc(Dev_t dev) { char *cp; int x = spltty(); kbd_polling = 1; cp = sgetc(1); kbd_polling = 0; splx(x); return (cp == NULL ? -1 : *cp); } #endif /* PCVT_FREEBSD >= 200 */ #if PCVT_NETBSD >= 100 void pccnpollc(Dev_t dev, int on) { kbd_polling = on; if (!on) { register int s; /* * If disabling polling, make sure there are no bytes left in * the FIFO, holding up the interrupt line. Otherwise we * won't get any further interrupts. */ s = spltty(); pcrint(); splx(s); } } #endif /* PCVT_NETBSD >= 100 */ /*---------------------------------------------------------------------------* * Set line parameters *---------------------------------------------------------------------------*/ int pcparam(struct tty *tp, struct termios *t) { register int cflag = t->c_cflag; /* and copy to tty */ tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = cflag; return(0); } /* special characters */ #define bs 8 #define lf 10 #define cr 13 #define cntlc 3 #define del 0177 #define cntld 4 int getchar(void) { u_char thechar; int x; kbd_polling = 1; x = splhigh(); sput(">", 1, 1, 0); async_update(UPDATE_KERN); thechar = *(sgetc(0)); kbd_polling = 0; splx(x); switch (thechar) { default: if (thechar >= ' ') sput(&thechar, 1, 1, 0); return(thechar); case cr: case lf: sput("\r\n", 1, 2, 0); return(lf); case bs: case del: sput("\b \b", 1, 3, 0); return(thechar); case cntlc: sput("^C\r\n", 1, 4, 0) ; cpu_reset(); case cntld: sput("^D\r\n", 1, 4, 0) ; return(0); } } #define DPAUSE 1 void dprintf(unsigned flgs, const char *fmt, ...) { va_list ap; if((flgs&__debug) > DPAUSE) { __color = ffs(flgs&__debug)+1; va_start(ap,fmt); vprintf(fmt, ap); va_end(ap); if (flgs & DPAUSE || nrow%24 == 23) { int x; x = splhigh(); if(nrow%24 == 23) nrow = 0; (void)sgetc(0); splx(x); } } __color = 0; } /*----------------------------------------------------------------------* * read initial VGA palette (as stored by VGA ROM BIOS) into * palette save area *----------------------------------------------------------------------*/ void vgapelinit(void) { register unsigned idx; register struct rgb *val; /* first, read all and store to first screen's save buffer */ for(idx = 0, val = vs[0].palette; idx < NVGAPEL; idx++, val++) vgapaletteio(idx, val, 0 /* read it */); /* now, duplicate for remaining screens */ for(idx = 1; idx < PCVT_NSCREENS; idx++) bcopy(vs[0].palette, vs[idx].palette, NVGAPEL * sizeof(struct rgb)); } #if defined XSERVER && !PCVT_USL_VT_COMPAT /*----------------------------------------------------------------------* * initialize for X mode * i.e.: grant current process (the X server) all IO privileges, * and mark in static variable so other hooks can test for it, * save all loaded fonts and screen pages to pageable buffers; * if parameter `on' is false, the same procedure is done reverse. *----------------------------------------------------------------------*/ static int pcvt_xmode_set(int on, struct proc *p) { static unsigned char *saved_fonts[NVGAFONTS]; #if PCVT_SCREENSAVER static unsigned saved_scrnsv_tmo = 0; #endif /* PCVT_SCREENSAVER */ #if (PCVT_NETBSD > 9) || (PCVT_FREEBSD > 102) struct trapframe *fp; #else struct syscframe *fp; #endif /* PCVT_NETBSD > 9 */ int error, i; /* X will only run on VGA and Hercules adaptors */ if(adaptor_type != VGA_ADAPTOR && adaptor_type != MDA_ADAPTOR) return (EINVAL); #if PCVT_NETBSD > 9 fp = (struct trapframe *)p->p_regs; #else fp = (struct syscframe *)p->p_regs; #endif /* PCVT_NETBSD > 9 */ if(on) { /* * Test whether the calling process has super-user privileges * and we're in insecure mode. * This prevents us from granting the potential security hole * `IO priv' to insufficiently privileged processes. */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); if (securelevel > 0) return (EPERM); if(pcvt_xmode) return 0; pcvt_xmode = pcvt_kbd_raw = 1; for(i = 0; i < totalfonts; i++) { if(vgacs[i].loaded) { saved_fonts[i] = (unsigned char *) malloc(32 * 256, M_DEVBUF, M_WAITOK); if(saved_fonts[i] == 0) { printf( "pcvt_xmode_set: no font buffer available\n"); return (EAGAIN); } else { vga_move_charset(i, saved_fonts[i], 1); } } else { saved_fonts[i] = 0; } } #if PCVT_SCREENSAVER if(saved_scrnsv_tmo = scrnsv_timeout) pcvt_set_scrnsv_tmo(0); /* turn it off */ #endif /* PCVT_SCREENSAVER */ async_update(UPDATE_STOP); /* turn off */ /* disable text output and save screen contents */ /* video board memory -> kernel memory */ bcopy(vsp->Crtat, vsp->Memory, vsp->screen_rowsize * vsp->maxcol * CHR); vsp->Crtat = vsp->Memory; /* operate in memory now */ #ifndef _I386_ISA_KBDIO_H_ #if PCVT_SCANSET == 2 /* put keyboard to return ancient PC scan codes */ kbc_8042cmd(CONTR_WRITE); #if PCVT_USEKBDSEC /* security enabled */ outb(CONTROLLER_DATA, (COMMAND_SYSFLG|COMMAND_IRQEN|COMMAND_PCSCAN)); #else /* security disabled */ outb(CONTROLLER_DATA, (COMMAND_INHOVR|COMMAND_SYSFLG|COMMAND_IRQEN|COMMAND_PCSCAN)); #endif /* PCVT_USEKBDSEC */ #endif /* PCVT_SCANSET == 2 */ #else /* _I386_ISA_KBDIO_H_ */ #if PCVT_SCANSET == 2 /* put keyboard to return ancient PC scan codes */ set_controller_command_byte(kbdc, KBD_TRANSLATION, KBD_TRANSLATION); #endif /* PCVT_SCANSET == 2 */ #endif /* !_I386_ISA_KBDIO_H_ */ #if PCVT_NETBSD > 9 fp->tf_eflags |= PSL_IOPL; #else fp->sf_eflags |= PSL_IOPL; #endif /* PCVT_NETBSD > 9 */ } else { if(!pcvt_xmode) /* verify if in X */ return 0; pcvt_xmode = pcvt_kbd_raw = 0; for(i = 0; i < totalfonts; i++) { if(saved_fonts[i]) { vga_move_charset(i, saved_fonts[i], 0); free(saved_fonts[i], M_DEVBUF); saved_fonts[i] = 0; } } #if PCVT_NETBSD > 9 fp->tf_eflags &= ~PSL_IOPL; #else fp->sf_eflags &= ~PSL_IOPL; #endif /* PCVT_NETBSD > 9 */ #if PCVT_SCREENSAVER if(saved_scrnsv_tmo) pcvt_set_scrnsv_tmo(saved_scrnsv_tmo); #endif /* PCVT_SCREENSAVER */ #ifndef _I386_ISA_KBDIO_H_ #if PCVT_SCANSET == 2 kbc_8042cmd(CONTR_WRITE); #if PCVT_USEKBDSEC /* security enabled */ outb(CONTROLLER_DATA, (COMMAND_SYSFLG|COMMAND_IRQEN)); #else /* security disabled */ outb(CONTROLLER_DATA, (COMMAND_INHOVR|COMMAND_SYSFLG|COMMAND_IRQEN)); #endif /* PCVT_USEKBDSEC */ #endif /* PCVT_SCANSET == 2 */ #else /* _I386_ISA_KBDIO_H_ */ #if PCVT_SCANSET == 2 set_controller_command_byte(kbdc, KBD_TRANSLATION, 0); #endif /* PCVT_SCANSET == 2 */ #endif /* !_I386_ISA_KBDIO_H_ */ if(adaptor_type == MDA_ADAPTOR) { /* * Due to the fact that HGC registers are write-only, * the Xserver can only make guesses about the state * the HGC adaptor has been before turning on X mode. * Thus, the display must be re-enabled now, and the * cursor shape and location restored. */ outb(GN_DMCNTLM, 0x28); /* enable display, text mode */ outb(addr_6845, CRTC_CURSORH); /* select high register */ outb(addr_6845+1, ((vsp->Crtat + vsp->cur_offset) - Crtat) >> 8); outb(addr_6845, CRTC_CURSORL); /* select low register */ outb(addr_6845+1, ((vsp->Crtat + vsp->cur_offset) - Crtat)); outb(addr_6845, CRTC_CURSTART); /* select high register */ outb(addr_6845+1, vsp->cursor_start); outb(addr_6845, CRTC_CUREND); /* select low register */ outb(addr_6845+1, vsp->cursor_end); } /* restore screen and re-enable text output */ /* kernel memory -> video board memory */ bcopy(vsp->Memory, Crtat, vsp->screen_rowsize * vsp->maxcol * CHR); vsp->Crtat = Crtat; /* operate on-screen now */ /* set crtc screen memory start address */ outb(addr_6845, CRTC_STARTADRH); outb(addr_6845+1, (vsp->Crtat - Crtat) >> 8); outb(addr_6845, CRTC_STARTADRL); outb(addr_6845+1, (vsp->Crtat - Crtat)); async_update(UPDATE_START); } return 0; } #endif /* XSERVER && !PCVT_USL_VT_COMPAT */ #endif /* NVT > 0 */ /*-------------------------- E O F -------------------------------------*/ diff --git a/sys/i386/isa/rc.c b/sys/i386/isa/rc.c index 021f089cea73..5f845c6fbc02 100644 --- a/sys/i386/isa/rc.c +++ b/sys/i386/isa/rc.c @@ -1,1507 +1,1508 @@ /* * Copyright (C) 1995 by Pavel Antonov, Moscow, Russia. * Copyright (C) 1995 by Andrey A. Chernov, Moscow, Russia. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * SDL Communications Riscom/8 (based on Cirrus Logic CL-CD180) driver * */ #include "rc.h" #if NRC > 0 #include "opt_devfs.h" /*#define RCDEBUG*/ #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #include /* Prototypes */ static int rcprobe __P((struct isa_device *)); static int rcattach __P((struct isa_device *)); #define rcin(port) RC_IN (nec, port) #define rcout(port,v) RC_OUT (nec, port, v) #define WAITFORCCR(u,c) rc_wait0(nec, (u), (c), __LINE__) #define CCRCMD(u,c,cmd) WAITFORCCR((u), (c)); rcout(CD180_CCR, (cmd)) #define RC_IBUFSIZE 256 #define RB_I_HIGH_WATER (TTYHOG - 2 * RC_IBUFSIZE) #define RC_OBUFSIZE 512 #define RC_IHIGHWATER (3 * RC_IBUFSIZE / 4) #define INPUT_FLAGS_SHIFT (2 * RC_IBUFSIZE) #define LOTS_OF_EVENTS 64 #define RC_FAKEID 0x10 #define RC_PROBED 1 #define RC_ATTACHED 2 #define GET_UNIT(dev) (minor(dev) & 0x3F) #define CALLOUT(dev) (minor(dev) & 0x80) /* For isa routines */ struct isa_driver rcdriver = { rcprobe, rcattach, "rc" }; static d_open_t rcopen; static d_close_t rcclose; static d_read_t rcread; static d_write_t rcwrite; static d_ioctl_t rcioctl; static d_stop_t rcstop; static d_devtotty_t rcdevtotty; -#define CDEV_MAJOR 63 -static struct cdevsw rc_cdevsw = - { rcopen, rcclose, rcread, rcwrite, /*63*/ - rcioctl, rcstop, noreset, rcdevtotty,/* rc */ - ttpoll, nommap, NULL, "rc", NULL, -1 }; +#define CDEV_MAJOR 63 +static struct cdevsw rc_cdevsw = { + rcopen, rcclose, rcread, rcwrite, + rcioctl, rcstop, noreset, rcdevtotty, + ttpoll, nommap, NULL, "rc", + NULL, -1, nodump, nopsize, + D_TTY, +}; /* Per-board structure */ static struct rc_softc { u_int rcb_probed; /* 1 - probed, 2 - attached */ u_int rcb_addr; /* Base I/O addr */ u_int rcb_unit; /* unit # */ u_char rcb_dtr; /* DTR status */ struct rc_chans *rcb_baserc; /* base rc ptr */ } rc_softc[NRC]; /* Per-channel structure */ static struct rc_chans { struct rc_softc *rc_rcb; /* back ptr */ u_short rc_flags; /* Misc. flags */ int rc_chan; /* Channel # */ u_char rc_ier; /* intr. enable reg */ u_char rc_msvr; /* modem sig. status */ u_char rc_cor2; /* options reg */ u_char rc_pendcmd; /* special cmd pending */ u_int rc_dtrwait; /* dtr timeout */ u_int rc_dcdwaits; /* how many waits DCD in open */ u_char rc_hotchar; /* end packed optimize */ struct tty *rc_tp; /* tty struct */ u_char *rc_iptr; /* Chars input buffer */ u_char *rc_hiwat; /* hi-water mark */ u_char *rc_bufend; /* end of buffer */ u_char *rc_optr; /* ptr in output buf */ u_char *rc_obufend; /* end of output buf */ u_char rc_ibuf[4 * RC_IBUFSIZE]; /* input buffer */ u_char rc_obuf[RC_OBUFSIZE]; /* output buffer */ #ifdef DEVFS void *devfs_token; #endif } rc_chans[NRC * CD180_NCHAN]; static int rc_scheduled_event = 0; /* for pstat -t */ static struct tty rc_tty[NRC * CD180_NCHAN]; static const int nrc_tty = NRC * CD180_NCHAN; /* Flags */ #define RC_DTR_OFF 0x0001 /* DTR wait, for close/open */ #define RC_ACTOUT 0x0002 /* Dial-out port active */ #define RC_RTSFLOW 0x0004 /* RTS flow ctl enabled */ #define RC_CTSFLOW 0x0008 /* CTS flow ctl enabled */ #define RC_DORXFER 0x0010 /* RXFER event planned */ #define RC_DOXXFER 0x0020 /* XXFER event planned */ #define RC_MODCHG 0x0040 /* Modem status changed */ #define RC_OSUSP 0x0080 /* Output suspended */ #define RC_OSBUSY 0x0100 /* start() routine in progress */ #define RC_WAS_BUFOVFL 0x0200 /* low-level buffer ovferflow */ #define RC_WAS_SILOVFL 0x0400 /* silo buffer overflow */ #define RC_SEND_RDY 0x0800 /* ready to send */ /* Table for translation of RCSR status bits to internal form */ static int rc_rcsrt[16] = { 0, TTY_OE, TTY_FE, TTY_FE|TTY_OE, TTY_PE, TTY_PE|TTY_OE, TTY_PE|TTY_FE, TTY_PE|TTY_FE|TTY_OE, TTY_BI, TTY_BI|TTY_OE, TTY_BI|TTY_FE, TTY_BI|TTY_FE|TTY_OE, TTY_BI|TTY_PE, TTY_BI|TTY_PE|TTY_OE, TTY_BI|TTY_PE|TTY_FE, TTY_BI|TTY_PE|TTY_FE|TTY_OE }; /* Static prototypes */ static void rc_hwreset __P((int, int, unsigned int)); static int rc_test __P((int, int)); static void rc_discard_output __P((struct rc_chans *)); static void rc_hardclose __P((struct rc_chans *)); static int rc_modctl __P((struct rc_chans *, int, int)); static void rc_start __P((struct tty *)); static int rc_param __P((struct tty *, struct termios *)); static swihand_t rcpoll; static void rc_reinit __P((struct rc_softc *)); #ifdef RCDEBUG static void printrcflags(); #endif static timeout_t rc_dtrwakeup; static timeout_t rc_wakeup; static void disc_optim __P((struct tty *tp, struct termios *t, struct rc_chans *)); static void rc_wait0 __P((int nec, int unit, int chan, int line)); /**********************************************/ /* Quick device probing */ static int rcprobe(dvp) struct isa_device *dvp; { int irq = ffs(dvp->id_irq) - 1; register int nec = dvp->id_iobase; if (dvp->id_unit > NRC) return 0; if (!RC_VALIDADDR(nec)) { printf("rc%d: illegal base address %x\n", dvp->id_unit, nec); return 0; } if (!RC_VALIDIRQ(irq)) { printf("rc%d: illegal IRQ value %d\n", dvp->id_unit, irq); return 0; } rcout(CD180_PPRL, 0x22); /* Random values to Prescale reg. */ rcout(CD180_PPRH, 0x11); if (rcin(CD180_PPRL) != 0x22 || rcin(CD180_PPRH) != 0x11) return 0; /* Now, test the board more thoroughly, with diagnostic */ if (rc_test(nec, dvp->id_unit)) return 0; rc_softc[dvp->id_unit].rcb_probed = RC_PROBED; return 0xF; } static int rcattach(dvp) struct isa_device *dvp; { register int chan, nec = dvp->id_iobase; struct rc_softc *rcb = &rc_softc[dvp->id_unit]; struct rc_chans *rc = &rc_chans[dvp->id_unit * CD180_NCHAN]; static int rc_started = 0; struct tty *tp; /* Thorooughly test the device */ if (rcb->rcb_probed != RC_PROBED) return 0; rcb->rcb_addr = nec; rcb->rcb_dtr = 0; rcb->rcb_baserc = rc; /*rcb->rcb_chipid = 0x10 + dvp->id_unit;*/ printf("rc%d: %d chans, firmware rev. %c\n", dvp->id_unit, CD180_NCHAN, (rcin(CD180_GFRCR) & 0xF) + 'A'); for (chan = 0; chan < CD180_NCHAN; chan++, rc++) { rc->rc_rcb = rcb; rc->rc_chan = chan; rc->rc_iptr = rc->rc_ibuf; rc->rc_bufend = &rc->rc_ibuf[RC_IBUFSIZE]; rc->rc_hiwat = &rc->rc_ibuf[RC_IHIGHWATER]; rc->rc_flags = rc->rc_ier = rc->rc_msvr = 0; rc->rc_cor2 = rc->rc_pendcmd = 0; rc->rc_optr = rc->rc_obufend = rc->rc_obuf; rc->rc_dtrwait = 3 * hz; rc->rc_dcdwaits= 0; rc->rc_hotchar = 0; tp = rc->rc_tp = &rc_tty[chan]; ttychars(tp); tp->t_lflag = tp->t_iflag = tp->t_oflag = 0; tp->t_cflag = TTYDEF_CFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; #ifdef DEVFS /* FIX THIS to reflect real devices */ rc->devfs_token = devfs_add_devswf(&rc_cdevsw, (dvp->id_unit * CD180_NCHAN) + chan, DV_CHR, 0, 0, 0600, "rc%d.%d", dvp->id_unit, chan); #endif } rcb->rcb_probed = RC_ATTACHED; if (!rc_started) { register_swi(SWI_TTY, rcpoll); rc_wakeup((void *)NULL); rc_started = 0; } return 1; } /* RC interrupt handling */ void rcintr(unit) int unit; { register struct rc_softc *rcb = &rc_softc[unit]; register struct rc_chans *rc; register int nec, resid; register u_char val, iack, bsr, ucnt, *optr; int good_data, t_state; if (rcb->rcb_probed != RC_ATTACHED) { printf("rc%d: bogus interrupt\n", unit); return; } nec = rcb->rcb_addr; bsr = ~(rcin(RC_BSR)); if (!(bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT))) { printf("rc%d: extra interrupt\n", unit); rcout(CD180_EOIR, 0); return; } while (bsr & (RC_BSR_TOUT|RC_BSR_RXINT|RC_BSR_TXINT|RC_BSR_MOINT)) { #ifdef RCDEBUG_DETAILED printf("rc%d: intr (%02x) %s%s%s%s\n", unit, bsr, (bsr & RC_BSR_TOUT)?"TOUT ":"", (bsr & RC_BSR_RXINT)?"RXINT ":"", (bsr & RC_BSR_TXINT)?"TXINT ":"", (bsr & RC_BSR_MOINT)?"MOINT":""); #endif if (bsr & RC_BSR_TOUT) { printf("rc%d: hardware failure, reset board\n", unit); rcout(RC_CTOUT, 0); rc_reinit(rcb); return; } if (bsr & RC_BSR_RXINT) { iack = rcin(RC_PILR_RX); good_data = (iack == (GIVR_IT_RGDI | RC_FAKEID)); if (!good_data && iack != (GIVR_IT_REI | RC_FAKEID)) { printf("rc%d: fake rxint: %02x\n", unit, iack); goto more_intrs; } rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH); t_state = rc->rc_tp->t_state; /* Do RTS flow control stuff */ if ( (rc->rc_flags & RC_RTSFLOW) || !(t_state & TS_ISOPEN) ) { if ( ( !(t_state & TS_ISOPEN) || (t_state & TS_TBLOCK) ) && (rc->rc_msvr & MSVR_RTS) ) rcout(CD180_MSVR, rc->rc_msvr &= ~MSVR_RTS); else if (!(rc->rc_msvr & MSVR_RTS)) rcout(CD180_MSVR, rc->rc_msvr |= MSVR_RTS); } ucnt = rcin(CD180_RDCR) & 0xF; resid = 0; if (t_state & TS_ISOPEN) { /* check for input buffer overflow */ if ((rc->rc_iptr + ucnt) >= rc->rc_bufend) { resid = ucnt; ucnt = rc->rc_bufend - rc->rc_iptr; resid -= ucnt; if (!(rc->rc_flags & RC_WAS_BUFOVFL)) { rc->rc_flags |= RC_WAS_BUFOVFL; rc_scheduled_event++; } } optr = rc->rc_iptr; /* check foor good data */ if (good_data) { while (ucnt-- > 0) { val = rcin(CD180_RDR); optr[0] = val; optr[INPUT_FLAGS_SHIFT] = 0; optr++; rc_scheduled_event++; if (val != 0 && val == rc->rc_hotchar) setsofttty(); } } else { /* Store also status data */ while (ucnt-- > 0) { iack = rcin(CD180_RCSR); if (iack & RCSR_Timeout) break; if ( (iack & RCSR_OE) && !(rc->rc_flags & RC_WAS_SILOVFL)) { rc->rc_flags |= RC_WAS_SILOVFL; rc_scheduled_event++; } val = rcin(CD180_RDR); /* Don't store PE if IGNPAR and BREAK if IGNBRK, this hack allows "raw" tty optimization works even if IGN* is set. */ if ( !(iack & (RCSR_PE|RCSR_FE|RCSR_Break)) || (!(iack & (RCSR_PE|RCSR_FE)) || !(rc->rc_tp->t_iflag & IGNPAR)) && (!(iack & RCSR_Break) || !(rc->rc_tp->t_iflag & IGNBRK))) { if ( (iack & (RCSR_PE|RCSR_FE)) && (t_state & TS_CAN_BYPASS_L_RINT) && ((iack & RCSR_FE) || (iack & RCSR_PE) && (rc->rc_tp->t_iflag & INPCK))) val = 0; else if (val != 0 && val == rc->rc_hotchar) setsofttty(); optr[0] = val; optr[INPUT_FLAGS_SHIFT] = iack; optr++; rc_scheduled_event++; } } } rc->rc_iptr = optr; rc->rc_flags |= RC_DORXFER; } else resid = ucnt; /* Clear FIFO if necessary */ while (resid-- > 0) { if (!good_data) iack = rcin(CD180_RCSR); else iack = 0; if (iack & RCSR_Timeout) break; (void) rcin(CD180_RDR); } goto more_intrs; } if (bsr & RC_BSR_MOINT) { iack = rcin(RC_PILR_MODEM); if (iack != (GIVR_IT_MSCI | RC_FAKEID)) { printf("rc%d: fake moint: %02x\n", unit, iack); goto more_intrs; } rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH); iack = rcin(CD180_MCR); rc->rc_msvr = rcin(CD180_MSVR); rcout(CD180_MCR, 0); #ifdef RCDEBUG printrcflags(rc, "moint"); #endif if (rc->rc_flags & RC_CTSFLOW) { if (rc->rc_msvr & MSVR_CTS) rc->rc_flags |= RC_SEND_RDY; else rc->rc_flags &= ~RC_SEND_RDY; } else rc->rc_flags |= RC_SEND_RDY; if ((iack & MCR_CDchg) && !(rc->rc_flags & RC_MODCHG)) { rc_scheduled_event += LOTS_OF_EVENTS; rc->rc_flags |= RC_MODCHG; setsofttty(); } goto more_intrs; } if (bsr & RC_BSR_TXINT) { iack = rcin(RC_PILR_TX); if (iack != (GIVR_IT_TDI | RC_FAKEID)) { printf("rc%d: fake txint: %02x\n", unit, iack); goto more_intrs; } rc = rcb->rcb_baserc + ((rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH); if ( (rc->rc_flags & RC_OSUSP) || !(rc->rc_flags & RC_SEND_RDY) ) goto more_intrs; /* Handle breaks and other stuff */ if (rc->rc_pendcmd) { rcout(CD180_COR2, rc->rc_cor2 |= COR2_ETC); rcout(CD180_TDR, CD180_C_ESC); rcout(CD180_TDR, rc->rc_pendcmd); rcout(CD180_COR2, rc->rc_cor2 &= ~COR2_ETC); rc->rc_pendcmd = 0; goto more_intrs; } optr = rc->rc_optr; resid = rc->rc_obufend - optr; if (resid > CD180_NFIFO) resid = CD180_NFIFO; while (resid-- > 0) rcout(CD180_TDR, *optr++); rc->rc_optr = optr; /* output completed? */ if (optr >= rc->rc_obufend) { rcout(CD180_IER, rc->rc_ier &= ~IER_TxRdy); #ifdef RCDEBUG printf("rc%d/%d: output completed\n", unit, rc->rc_chan); #endif if (!(rc->rc_flags & RC_DOXXFER)) { rc_scheduled_event += LOTS_OF_EVENTS; rc->rc_flags |= RC_DOXXFER; setsofttty(); } } } more_intrs: rcout(CD180_EOIR, 0); /* end of interrupt */ rcout(RC_CTOUT, 0); bsr = ~(rcin(RC_BSR)); } } /* Feed characters to output buffer */ static void rc_start(tp) register struct tty *tp; { register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)]; register int nec = rc->rc_rcb->rcb_addr, s; if (rc->rc_flags & RC_OSBUSY) return; s = spltty(); rc->rc_flags |= RC_OSBUSY; disable_intr(); if (tp->t_state & TS_TTSTOP) rc->rc_flags |= RC_OSUSP; else rc->rc_flags &= ~RC_OSUSP; /* Do RTS flow control stuff */ if ( (rc->rc_flags & RC_RTSFLOW) && (tp->t_state & TS_TBLOCK) && (rc->rc_msvr & MSVR_RTS) ) { rcout(CD180_CAR, rc->rc_chan); rcout(CD180_MSVR, rc->rc_msvr &= ~MSVR_RTS); } else if (!(rc->rc_msvr & MSVR_RTS)) { rcout(CD180_CAR, rc->rc_chan); rcout(CD180_MSVR, rc->rc_msvr |= MSVR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) goto out; #ifdef RCDEBUG printrcflags(rc, "rcstart"); #endif ttwwakeup(tp); #ifdef RCDEBUG printf("rcstart: outq = %d obuf = %d\n", tp->t_outq.c_cc, rc->rc_obufend - rc->rc_optr); #endif if (tp->t_state & TS_BUSY) goto out; /* output still in progress ... */ if (tp->t_outq.c_cc > 0) { u_int ocnt; tp->t_state |= TS_BUSY; ocnt = q_to_b(&tp->t_outq, rc->rc_obuf, sizeof rc->rc_obuf); disable_intr(); rc->rc_optr = rc->rc_obuf; rc->rc_obufend = rc->rc_optr + ocnt; enable_intr(); if (!(rc->rc_ier & IER_TxRdy)) { #ifdef RCDEBUG printf("rc%d/%d: rcstart enable txint\n", rc->rc_rcb->rcb_unit, rc->rc_chan); #endif rcout(CD180_CAR, rc->rc_chan); rcout(CD180_IER, rc->rc_ier |= IER_TxRdy); } } out: rc->rc_flags &= ~RC_OSBUSY; (void) splx(s); } /* Handle delayed events. */ void rcpoll() { register struct rc_chans *rc; register struct rc_softc *rcb; register u_char *tptr, *eptr; register struct tty *tp; register int chan, icnt, nec, unit; if (rc_scheduled_event == 0) return; repeat: for (unit = 0; unit < NRC; unit++) { rcb = &rc_softc[unit]; rc = rcb->rcb_baserc; nec = rc->rc_rcb->rcb_addr; for (chan = 0; chan < CD180_NCHAN; rc++, chan++) { tp = rc->rc_tp; #ifdef RCDEBUG if (rc->rc_flags & (RC_DORXFER|RC_DOXXFER|RC_MODCHG| RC_WAS_BUFOVFL|RC_WAS_SILOVFL)) printrcflags(rc, "rcevent"); #endif if (rc->rc_flags & RC_WAS_BUFOVFL) { disable_intr(); rc->rc_flags &= ~RC_WAS_BUFOVFL; rc_scheduled_event--; enable_intr(); printf("rc%d/%d: interrupt-level buffer overflow\n", unit, chan); } if (rc->rc_flags & RC_WAS_SILOVFL) { disable_intr(); rc->rc_flags &= ~RC_WAS_SILOVFL; rc_scheduled_event--; enable_intr(); printf("rc%d/%d: silo overflow\n", unit, chan); } if (rc->rc_flags & RC_MODCHG) { disable_intr(); rc->rc_flags &= ~RC_MODCHG; rc_scheduled_event -= LOTS_OF_EVENTS; enable_intr(); (*linesw[tp->t_line].l_modem)(tp, !!(rc->rc_msvr & MSVR_CD)); } if (rc->rc_flags & RC_DORXFER) { disable_intr(); rc->rc_flags &= ~RC_DORXFER; eptr = rc->rc_iptr; if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) tptr = &rc->rc_ibuf[RC_IBUFSIZE]; else tptr = rc->rc_ibuf; icnt = eptr - tptr; if (icnt > 0) { if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) { rc->rc_iptr = rc->rc_ibuf; rc->rc_bufend = &rc->rc_ibuf[RC_IBUFSIZE]; rc->rc_hiwat = &rc->rc_ibuf[RC_IHIGHWATER]; } else { rc->rc_iptr = &rc->rc_ibuf[RC_IBUFSIZE]; rc->rc_bufend = &rc->rc_ibuf[2 * RC_IBUFSIZE]; rc->rc_hiwat = &rc->rc_ibuf[RC_IBUFSIZE + RC_IHIGHWATER]; } if ( (rc->rc_flags & RC_RTSFLOW) && (tp->t_state & TS_ISOPEN) && !(tp->t_state & TS_TBLOCK) && !(rc->rc_msvr & MSVR_RTS) ) { rcout(CD180_CAR, chan); rcout(CD180_MSVR, rc->rc_msvr |= MSVR_RTS); } rc_scheduled_event -= icnt; } enable_intr(); if (icnt <= 0 || !(tp->t_state & TS_ISOPEN)) goto done1; if ( (tp->t_state & TS_CAN_BYPASS_L_RINT) && !(tp->t_state & TS_LOCAL)) { if ((tp->t_rawq.c_cc + icnt) >= RB_I_HIGH_WATER && ((rc->rc_flags & RC_RTSFLOW) || (tp->t_iflag & IXOFF)) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += icnt; tk_rawcc += icnt; tp->t_rawcc += icnt; if (b_to_q(tptr, icnt, &tp->t_rawq)) printf("rc%d/%d: tty-level buffer overflow\n", unit, chan); ttwakeup(tp); if ((tp->t_state & TS_TTSTOP) && ((tp->t_iflag & IXANY) || (tp->t_cc[VSTART] == tp->t_cc[VSTOP]))) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; rc_start(tp); } } else { for (; tptr < eptr; tptr++) (*linesw[tp->t_line].l_rint) (tptr[0] | rc_rcsrt[tptr[INPUT_FLAGS_SHIFT] & 0xF], tp); } done1: ; } if (rc->rc_flags & RC_DOXXFER) { disable_intr(); rc_scheduled_event -= LOTS_OF_EVENTS; rc->rc_flags &= ~RC_DOXXFER; rc->rc_tp->t_state &= ~TS_BUSY; enable_intr(); (*linesw[tp->t_line].l_start)(tp); } } if (rc_scheduled_event == 0) break; } if (rc_scheduled_event >= LOTS_OF_EVENTS) goto repeat; } static void rcstop(tp, rw) register struct tty *tp; int rw; { register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)]; u_char *tptr, *eptr; #ifdef RCDEBUG printf("rc%d/%d: rcstop %s%s\n", rc->rc_rcb->rcb_unit, rc->rc_chan, (rw & FWRITE)?"FWRITE ":"", (rw & FREAD)?"FREAD":""); #endif if (rw & FWRITE) rc_discard_output(rc); disable_intr(); if (rw & FREAD) { rc->rc_flags &= ~RC_DORXFER; eptr = rc->rc_iptr; if (rc->rc_bufend == &rc->rc_ibuf[2 * RC_IBUFSIZE]) { tptr = &rc->rc_ibuf[RC_IBUFSIZE]; rc->rc_iptr = &rc->rc_ibuf[RC_IBUFSIZE]; } else { tptr = rc->rc_ibuf; rc->rc_iptr = rc->rc_ibuf; } rc_scheduled_event -= eptr - tptr; } if (tp->t_state & TS_TTSTOP) rc->rc_flags |= RC_OSUSP; else rc->rc_flags &= ~RC_OSUSP; enable_intr(); } static int rcopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register struct rc_chans *rc; register struct tty *tp; int unit, nec, s, error = 0; unit = GET_UNIT(dev); if (unit >= NRC * CD180_NCHAN) return ENXIO; if (rc_softc[unit / CD180_NCHAN].rcb_probed != RC_ATTACHED) return ENXIO; rc = &rc_chans[unit]; tp = rc->rc_tp; nec = rc->rc_rcb->rcb_addr; #ifdef RCDEBUG printf("rc%d/%d: rcopen: dev %x\n", rc->rc_rcb->rcb_unit, unit, dev); #endif s = spltty(); again: while (rc->rc_flags & RC_DTR_OFF) { error = tsleep(&(rc->rc_dtrwait), TTIPRI | PCATCH, "rcdtr", 0); if (error != 0) goto out; } if (tp->t_state & TS_ISOPEN) { if (CALLOUT(dev)) { if (!(rc->rc_flags & RC_ACTOUT)) { error = EBUSY; goto out; } } else { if (rc->rc_flags & RC_ACTOUT) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } if (error = tsleep(&rc->rc_rcb, TTIPRI|PCATCH, "rcbi", 0)) goto out; goto again; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { tp->t_oproc = rc_start; tp->t_param = rc_param; tp->t_dev = dev; if (CALLOUT(dev)) tp->t_cflag |= CLOCAL; else tp->t_cflag &= ~CLOCAL; error = rc_param(tp, &tp->t_termios); if (error) goto out; (void) rc_modctl(rc, TIOCM_RTS|TIOCM_DTR, DMSET); - ttsetwater(tp); - if ((rc->rc_msvr & MSVR_CD) || CALLOUT(dev)) (*linesw[tp->t_line].l_modem)(tp, 1); } if (!(tp->t_state & TS_CARR_ON) && !CALLOUT(dev) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { rc->rc_dcdwaits++; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "rcdcd", 0); rc->rc_dcdwaits--; if (error != 0) goto out; goto again; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, rc); if ((tp->t_state & TS_ISOPEN) && CALLOUT(dev)) rc->rc_flags |= RC_ACTOUT; out: (void) splx(s); if(rc->rc_dcdwaits == 0 && !(tp->t_state & TS_ISOPEN)) rc_hardclose(rc); return error; } static int rcclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register struct rc_chans *rc; register struct tty *tp; int s, unit = GET_UNIT(dev); if (unit >= NRC * CD180_NCHAN) return ENXIO; rc = &rc_chans[unit]; tp = rc->rc_tp; #ifdef RCDEBUG printf("rc%d/%d: rcclose dev %x\n", rc->rc_rcb->rcb_unit, unit, dev); #endif s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, rc); rcstop(tp, FREAD | FWRITE); rc_hardclose(rc); ttyclose(tp); splx(s); return 0; } static void rc_hardclose(rc) register struct rc_chans *rc; { register int s, nec = rc->rc_rcb->rcb_addr; register struct tty *tp = rc->rc_tp; s = spltty(); rcout(CD180_CAR, rc->rc_chan); /* Disable rx/tx intrs */ rcout(CD180_IER, rc->rc_ier = 0); if ( (tp->t_cflag & HUPCL) || !(rc->rc_flags & RC_ACTOUT) && !(rc->rc_msvr & MSVR_CD) && !(tp->t_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN) ) { CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_ResetChan); WAITFORCCR(rc->rc_rcb->rcb_unit, rc->rc_chan); (void) rc_modctl(rc, TIOCM_RTS, DMSET); if (rc->rc_dtrwait) { timeout(rc_dtrwakeup, rc, rc->rc_dtrwait); rc->rc_flags |= RC_DTR_OFF; } } rc->rc_flags &= ~RC_ACTOUT; wakeup((caddr_t) &rc->rc_rcb); /* wake bi */ wakeup(TSA_CARR_ON(tp)); (void) splx(s); } /* Read from line */ static int rcread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct tty *tp = rc_chans[GET_UNIT(dev)].rc_tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } /* Write to line */ static int rcwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct tty *tp = rc_chans[GET_UNIT(dev)].rc_tp; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } /* Reset the bastard */ static void rc_hwreset(unit, nec, chipid) register int unit, nec; unsigned int chipid; { CCRCMD(unit, -1, CCR_HWRESET); /* Hardware reset */ DELAY(20000); WAITFORCCR(unit, -1); rcout(RC_CTOUT, 0); /* Clear timeout */ rcout(CD180_GIVR, chipid); rcout(CD180_GICR, 0); /* Set Prescaler Registers (1 msec) */ rcout(CD180_PPRL, ((RC_OSCFREQ + 999) / 1000) & 0xFF); rcout(CD180_PPRH, ((RC_OSCFREQ + 999) / 1000) >> 8); /* Initialize Priority Interrupt Level Registers */ rcout(CD180_PILR1, RC_PILR_MODEM); rcout(CD180_PILR2, RC_PILR_TX); rcout(CD180_PILR3, RC_PILR_RX); /* Reset DTR */ rcout(RC_DTREG, ~0); } /* Set channel parameters */ static int rc_param(tp, ts) register struct tty *tp; struct termios *ts; { register struct rc_chans *rc = &rc_chans[GET_UNIT(tp->t_dev)]; register int nec = rc->rc_rcb->rcb_addr; int idivs, odivs, s, val, cflag, iflag, lflag, inpflow; if ( ts->c_ospeed < 0 || ts->c_ospeed > 76800 || ts->c_ispeed < 0 || ts->c_ispeed > 76800 ) return (EINVAL); if (ts->c_ispeed == 0) ts->c_ispeed = ts->c_ospeed; odivs = RC_BRD(ts->c_ospeed); idivs = RC_BRD(ts->c_ispeed); s = spltty(); /* Select channel */ rcout(CD180_CAR, rc->rc_chan); /* If speed == 0, hangup line */ if (ts->c_ospeed == 0) { CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_ResetChan); WAITFORCCR(rc->rc_rcb->rcb_unit, rc->rc_chan); (void) rc_modctl(rc, TIOCM_DTR, DMBIC); } tp->t_state &= ~TS_CAN_BYPASS_L_RINT; cflag = ts->c_cflag; iflag = ts->c_iflag; lflag = ts->c_lflag; if (idivs > 0) { rcout(CD180_RBPRL, idivs & 0xFF); rcout(CD180_RBPRH, idivs >> 8); } if (odivs > 0) { rcout(CD180_TBPRL, odivs & 0xFF); rcout(CD180_TBPRH, odivs >> 8); } /* set timeout value */ if (ts->c_ispeed > 0) { int itm = ts->c_ispeed > 2400 ? 5 : 10000 / ts->c_ispeed + 1; if ( !(lflag & ICANON) && ts->c_cc[VMIN] != 0 && ts->c_cc[VTIME] != 0 && ts->c_cc[VTIME] * 10 > itm) itm = ts->c_cc[VTIME] * 10; rcout(CD180_RTPR, itm <= 255 ? itm : 255); } switch (cflag & CSIZE) { case CS5: val = COR1_5BITS; break; case CS6: val = COR1_6BITS; break; case CS7: val = COR1_7BITS; break; default: case CS8: val = COR1_8BITS; break; } if (cflag & PARENB) { val |= COR1_NORMPAR; if (cflag & PARODD) val |= COR1_ODDP; if (!(cflag & INPCK)) val |= COR1_Ignore; } else val |= COR1_Ignore; if (cflag & CSTOPB) val |= COR1_2SB; rcout(CD180_COR1, val); /* Set FIFO threshold */ val = ts->c_ospeed <= 4800 ? 1 : CD180_NFIFO / 2; inpflow = 0; if ( (iflag & IXOFF) && ( ts->c_cc[VSTOP] != _POSIX_VDISABLE && ( ts->c_cc[VSTART] != _POSIX_VDISABLE || (iflag & IXANY) ) ) ) { inpflow = 1; val |= COR3_SCDE|COR3_FCT; } rcout(CD180_COR3, val); /* Initialize on-chip automatic flow control */ val = 0; rc->rc_flags &= ~(RC_CTSFLOW|RC_SEND_RDY); if (cflag & CCTS_OFLOW) { rc->rc_flags |= RC_CTSFLOW; val |= COR2_CtsAE; } else rc->rc_flags |= RC_SEND_RDY; if (tp->t_state & TS_TTSTOP) rc->rc_flags |= RC_OSUSP; else rc->rc_flags &= ~RC_OSUSP; if (cflag & CRTS_IFLOW) rc->rc_flags |= RC_RTSFLOW; else rc->rc_flags &= ~RC_RTSFLOW; if (inpflow) { if (ts->c_cc[VSTART] != _POSIX_VDISABLE) rcout(CD180_SCHR1, ts->c_cc[VSTART]); rcout(CD180_SCHR2, ts->c_cc[VSTOP]); val |= COR2_TxIBE; if (iflag & IXANY) val |= COR2_IXM; } rcout(CD180_COR2, rc->rc_cor2 = val); CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); disc_optim(tp, ts, rc); /* modem ctl */ val = cflag & CLOCAL ? 0 : MCOR1_CDzd; if (cflag & CCTS_OFLOW) val |= MCOR1_CTSzd; rcout(CD180_MCOR1, val); val = cflag & CLOCAL ? 0 : MCOR2_CDod; if (cflag & CCTS_OFLOW) val |= MCOR2_CTSod; rcout(CD180_MCOR2, val); /* enable i/o and interrupts */ CCRCMD(rc->rc_rcb->rcb_unit, rc->rc_chan, CCR_XMTREN | ((cflag & CREAD) ? CCR_RCVREN : CCR_RCVRDIS)); WAITFORCCR(rc->rc_rcb->rcb_unit, rc->rc_chan); rc->rc_ier = cflag & CLOCAL ? 0 : IER_CD; if (cflag & CCTS_OFLOW) rc->rc_ier |= IER_CTS; if (cflag & CREAD) rc->rc_ier |= IER_RxData; if (tp->t_state & TS_BUSY) rc->rc_ier |= IER_TxRdy; if (ts->c_ospeed != 0) rc_modctl(rc, TIOCM_DTR, DMBIS); if ((cflag & CCTS_OFLOW) && (rc->rc_msvr & MSVR_CTS)) rc->rc_flags |= RC_SEND_RDY; rcout(CD180_IER, rc->rc_ier); (void) splx(s); return 0; } /* Re-initialize board after bogus interrupts */ static void rc_reinit(rcb) struct rc_softc *rcb; { register struct rc_chans *rc, *rce; register int nec; nec = rcb->rcb_addr; rc_hwreset(rcb->rcb_unit, nec, RC_FAKEID); rc = &rc_chans[rcb->rcb_unit * CD180_NCHAN]; rce = rc + CD180_NCHAN; for (; rc < rce; rc++) (void) rc_param(rc->rc_tp, &rc->rc_tp->t_termios); } static int rcioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; int flag; caddr_t data; struct proc *p; { register struct rc_chans *rc = &rc_chans[GET_UNIT(dev)]; register int s, error; struct tty *tp = rc->rc_tp; error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return (error); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, rc); if (error != ENOIOCTL) return (error); s = spltty(); switch (cmd) { case TIOCSBRK: rc->rc_pendcmd = CD180_C_SBRK; break; case TIOCCBRK: rc->rc_pendcmd = CD180_C_EBRK; break; case TIOCSDTR: (void) rc_modctl(rc, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void) rc_modctl(rc, TIOCM_DTR, DMBIC); break; case TIOCMGET: *(int *) data = rc_modctl(rc, 0, DMGET); break; case TIOCMSET: (void) rc_modctl(rc, *(int *) data, DMSET); break; case TIOCMBIC: (void) rc_modctl(rc, *(int *) data, DMBIC); break; case TIOCMBIS: (void) rc_modctl(rc, *(int *) data, DMBIS); break; case TIOCMSDTRWAIT: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } rc->rc_dtrwait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = rc->rc_dtrwait * 100 / hz; break; default: (void) splx(s); return ENOTTY; } (void) splx(s); return 0; } /* Modem control routines */ static int rc_modctl(rc, bits, cmd) register struct rc_chans *rc; int bits, cmd; { register int nec = rc->rc_rcb->rcb_addr; u_char *dtr = &rc->rc_rcb->rcb_dtr, msvr; rcout(CD180_CAR, rc->rc_chan); switch (cmd) { case DMSET: rcout(RC_DTREG, (bits & TIOCM_DTR) ? ~(*dtr |= 1 << rc->rc_chan) : ~(*dtr &= ~(1 << rc->rc_chan))); msvr = rcin(CD180_MSVR); if (bits & TIOCM_RTS) msvr |= MSVR_RTS; else msvr &= ~MSVR_RTS; if (bits & TIOCM_DTR) msvr |= MSVR_DTR; else msvr &= ~MSVR_DTR; rcout(CD180_MSVR, msvr); break; case DMBIS: if (bits & TIOCM_DTR) rcout(RC_DTREG, ~(*dtr |= 1 << rc->rc_chan)); msvr = rcin(CD180_MSVR); if (bits & TIOCM_RTS) msvr |= MSVR_RTS; if (bits & TIOCM_DTR) msvr |= MSVR_DTR; rcout(CD180_MSVR, msvr); break; case DMGET: bits = TIOCM_LE; msvr = rc->rc_msvr = rcin(CD180_MSVR); if (msvr & MSVR_RTS) bits |= TIOCM_RTS; if (msvr & MSVR_CTS) bits |= TIOCM_CTS; if (msvr & MSVR_DSR) bits |= TIOCM_DSR; if (msvr & MSVR_DTR) bits |= TIOCM_DTR; if (msvr & MSVR_CD) bits |= TIOCM_CD; if (~rcin(RC_RIREG) & (1 << rc->rc_chan)) bits |= TIOCM_RI; return bits; case DMBIC: if (bits & TIOCM_DTR) rcout(RC_DTREG, ~(*dtr &= ~(1 << rc->rc_chan))); msvr = rcin(CD180_MSVR); if (bits & TIOCM_RTS) msvr &= ~MSVR_RTS; if (bits & TIOCM_DTR) msvr &= ~MSVR_DTR; rcout(CD180_MSVR, msvr); break; } rc->rc_msvr = rcin(CD180_MSVR); return 0; } /* Test the board. */ int rc_test(nec, unit) register int nec; int unit; { int chan = 0; int i = 0, rcnt, old_level; unsigned int iack, chipid; unsigned short divs; static u_char ctest[] = "\377\125\252\045\244\0\377"; #define CTLEN 8 #define ERR(s) { \ printf("rc%d: ", unit); printf s ; printf("\n"); \ (void) splx(old_level); return 1; } struct rtest { u_char txbuf[CD180_NFIFO]; /* TX buffer */ u_char rxbuf[CD180_NFIFO]; /* RX buffer */ int rxptr; /* RX pointer */ int txptr; /* TX pointer */ } tchans[CD180_NCHAN]; old_level = spltty(); chipid = RC_FAKEID; /* First, reset board to inital state */ rc_hwreset(unit, nec, chipid); divs = RC_BRD(19200); /* Initialize channels */ for (chan = 0; chan < CD180_NCHAN; chan++) { /* Select and reset channel */ rcout(CD180_CAR, chan); CCRCMD(unit, chan, CCR_ResetChan); WAITFORCCR(unit, chan); /* Set speed */ rcout(CD180_RBPRL, divs & 0xFF); rcout(CD180_RBPRH, divs >> 8); rcout(CD180_TBPRL, divs & 0xFF); rcout(CD180_TBPRH, divs >> 8); /* set timeout value */ rcout(CD180_RTPR, 0); /* Establish local loopback */ rcout(CD180_COR1, COR1_NOPAR | COR1_8BITS | COR1_1SB); rcout(CD180_COR2, COR2_LLM); rcout(CD180_COR3, CD180_NFIFO); CCRCMD(unit, chan, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); CCRCMD(unit, chan, CCR_RCVREN | CCR_XMTREN); WAITFORCCR(unit, chan); rcout(CD180_MSVR, MSVR_RTS); /* Fill TXBUF with test data */ for (i = 0; i < CD180_NFIFO; i++) { tchans[chan].txbuf[i] = ctest[i]; tchans[chan].rxbuf[i] = 0; } tchans[chan].txptr = tchans[chan].rxptr = 0; /* Now, start transmit */ rcout(CD180_IER, IER_TxMpty|IER_RxData); } /* Pseudo-interrupt poll stuff */ for (rcnt = 10000; rcnt-- > 0; rcnt--) { i = ~(rcin(RC_BSR)); if (i & RC_BSR_TOUT) ERR(("BSR timeout bit set\n")) else if (i & RC_BSR_TXINT) { iack = rcin(RC_PILR_TX); if (iack != (GIVR_IT_TDI | chipid)) ERR(("Bad TX intr ack (%02x != %02x)\n", iack, GIVR_IT_TDI | chipid)); chan = (rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH; /* If no more data to transmit, disable TX intr */ if (tchans[chan].txptr >= CD180_NFIFO) { iack = rcin(CD180_IER); rcout(CD180_IER, iack & ~IER_TxMpty); } else { for (iack = tchans[chan].txptr; iack < CD180_NFIFO; iack++) rcout(CD180_TDR, tchans[chan].txbuf[iack]); tchans[chan].txptr = iack; } rcout(CD180_EOIR, 0); } else if (i & RC_BSR_RXINT) { u_char ucnt; iack = rcin(RC_PILR_RX); if (iack != (GIVR_IT_RGDI | chipid) && iack != (GIVR_IT_REI | chipid)) ERR(("Bad RX intr ack (%02x != %02x)\n", iack, GIVR_IT_RGDI | chipid)) chan = (rcin(CD180_GICR) & GICR_CHAN) >> GICR_LSH; ucnt = rcin(CD180_RDCR) & 0xF; while (ucnt-- > 0) { iack = rcin(CD180_RCSR); if (iack & RCSR_Timeout) break; if (iack & 0xF) ERR(("Bad char chan %d (RCSR = %02X)\n", chan, iack)) if (tchans[chan].rxptr > CD180_NFIFO) ERR(("Got extra chars chan %d\n", chan)) tchans[chan].rxbuf[tchans[chan].rxptr++] = rcin(CD180_RDR); } rcout(CD180_EOIR, 0); } rcout(RC_CTOUT, 0); for (iack = chan = 0; chan < CD180_NCHAN; chan++) if (tchans[chan].rxptr >= CD180_NFIFO) iack++; if (iack == CD180_NCHAN) break; } for (chan = 0; chan < CD180_NCHAN; chan++) { /* Select and reset channel */ rcout(CD180_CAR, chan); CCRCMD(unit, chan, CCR_ResetChan); } if (!rcnt) ERR(("looses characters during local loopback\n")) /* Now, check data */ for (chan = 0; chan < CD180_NCHAN; chan++) for (i = 0; i < CD180_NFIFO; i++) if (ctest[i] != tchans[chan].rxbuf[i]) ERR(("data mismatch chan %d ptr %d (%d != %d)\n", chan, i, ctest[i], tchans[chan].rxbuf[i])) (void) splx(old_level); return 0; } #ifdef RCDEBUG static void printrcflags(rc, comment) struct rc_chans *rc; char *comment; { u_short f = rc->rc_flags; register int nec = rc->rc_rcb->rcb_addr; printf("rc%d/%d: %s flags: %s%s%s%s%s%s%s%s%s%s%s%s\n", rc->rc_rcb->rcb_unit, rc->rc_chan, comment, (f & RC_DTR_OFF)?"DTR_OFF " :"", (f & RC_ACTOUT) ?"ACTOUT " :"", (f & RC_RTSFLOW)?"RTSFLOW " :"", (f & RC_CTSFLOW)?"CTSFLOW " :"", (f & RC_DORXFER)?"DORXFER " :"", (f & RC_DOXXFER)?"DOXXFER " :"", (f & RC_MODCHG) ?"MODCHG " :"", (f & RC_OSUSP) ?"OSUSP " :"", (f & RC_OSBUSY) ?"OSBUSY " :"", (f & RC_WAS_BUFOVFL) ?"BUFOVFL " :"", (f & RC_WAS_SILOVFL) ?"SILOVFL " :"", (f & RC_SEND_RDY) ?"SEND_RDY":""); rcout(CD180_CAR, rc->rc_chan); printf("rc%d/%d: msvr %02x ier %02x ccsr %02x\n", rc->rc_rcb->rcb_unit, rc->rc_chan, rcin(CD180_MSVR), rcin(CD180_IER), rcin(CD180_CCSR)); } #endif /* RCDEBUG */ static struct tty * rcdevtotty(dev) dev_t dev; { int unit; unit = GET_UNIT(dev); if (unit >= NRC * CD180_NCHAN) return NULL; return (&rc_tty[unit]); } static void rc_dtrwakeup(chan) void *chan; { struct rc_chans *rc; rc = (struct rc_chans *)chan; rc->rc_flags &= ~RC_DTR_OFF; wakeup(&rc->rc_dtrwait); } static void rc_discard_output(rc) struct rc_chans *rc; { disable_intr(); if (rc->rc_flags & RC_DOXXFER) { rc_scheduled_event -= LOTS_OF_EVENTS; rc->rc_flags &= ~RC_DOXXFER; } rc->rc_optr = rc->rc_obufend; rc->rc_tp->t_state &= ~TS_BUSY; enable_intr(); ttwwakeup(rc->rc_tp); } static void rc_wakeup(chan) void *chan; { timeout(rc_wakeup, (caddr_t)NULL, 1); if (rc_scheduled_event != 0) { int s; s = splsofttty(); rcpoll(); splx(s); } } static void disc_optim(tp, t, rc) struct tty *tp; struct termios *t; struct rc_chans *rc; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; rc->rc_hotchar = linesw[tp->t_line].l_hotchar; } static void rc_wait0(nec, unit, chan, line) int nec, unit, chan, line; { int rcnt; for (rcnt = 50; rcnt && rcin(CD180_CCR); rcnt--) DELAY(30); if (rcnt == 0) printf("rc%d/%d: channel command timeout, rc.c line: %d\n", unit, chan, line); } static rc_devsw_installed = 0; static void rc_drvinit(void *unused) { dev_t dev; if( ! rc_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&rc_cdevsw, NULL); rc_devsw_installed = 1; } } SYSINIT(rcdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,rc_drvinit,NULL) #endif /* NRC */ diff --git a/sys/i386/isa/rp.c b/sys/i386/isa/rp.c index e26c46c9482e..847660e70c92 100644 --- a/sys/i386/isa/rp.c +++ b/sys/i386/isa/rp.c @@ -1,2059 +1,2058 @@ /* * Copyright (c) Comtrol Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted prodived that the follwoing conditions * are met. * 1. Redistributions of source code must retain the above copyright * notive, this list of conditions and the following disclainer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials prodided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Comtrol Corporation. * 4. The name of Comtrol Corporation may not be used to endorse or * promote products derived from this software without specific * prior written permission. * * THIS SOFTWARE IS PROVIDED BY COMTROL CORPORATION ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL COMTROL CORPORATION BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * rp.c - for RocketPort FreeBSD */ #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #define ROCKET_C #include #include #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif static Byte_t RData[RDATASIZE] = { 0x00, 0x09, 0xf6, 0x82, 0x02, 0x09, 0x86, 0xfb, 0x04, 0x09, 0x00, 0x0a, 0x06, 0x09, 0x01, 0x0a, 0x08, 0x09, 0x8a, 0x13, 0x0a, 0x09, 0xc5, 0x11, 0x0c, 0x09, 0x86, 0x85, 0x0e, 0x09, 0x20, 0x0a, 0x10, 0x09, 0x21, 0x0a, 0x12, 0x09, 0x41, 0xff, 0x14, 0x09, 0x82, 0x00, 0x16, 0x09, 0x82, 0x7b, 0x18, 0x09, 0x8a, 0x7d, 0x1a, 0x09, 0x88, 0x81, 0x1c, 0x09, 0x86, 0x7a, 0x1e, 0x09, 0x84, 0x81, 0x20, 0x09, 0x82, 0x7c, 0x22, 0x09, 0x0a, 0x0a }; static Byte_t RRegData[RREGDATASIZE]= { 0x00, 0x09, 0xf6, 0x82, /* 00: Stop Rx processor */ 0x08, 0x09, 0x8a, 0x13, /* 04: Tx software flow control */ 0x0a, 0x09, 0xc5, 0x11, /* 08: XON char */ 0x0c, 0x09, 0x86, 0x85, /* 0c: XANY */ 0x12, 0x09, 0x41, 0xff, /* 10: Rx mask char */ 0x14, 0x09, 0x82, 0x00, /* 14: Compare/Ignore #0 */ 0x16, 0x09, 0x82, 0x7b, /* 18: Compare #1 */ 0x18, 0x09, 0x8a, 0x7d, /* 1c: Compare #2 */ 0x1a, 0x09, 0x88, 0x81, /* 20: Interrupt #1 */ 0x1c, 0x09, 0x86, 0x7a, /* 24: Ignore/Replace #1 */ 0x1e, 0x09, 0x84, 0x81, /* 28: Interrupt #2 */ 0x20, 0x09, 0x82, 0x7c, /* 2c: Ignore/Replace #2 */ 0x22, 0x09, 0x0a, 0x0a /* 30: Rx FIFO Enable */ }; static CONTROLLER_T sController[CTL_SIZE] = { {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}}, {-1,-1,0,0,0,0,0,0,0,0,0,{0,0,0,0},{0,0,0,0},{-1,-1,-1,-1},{0,0,0,0}} }; #if 0 /* IRQ number to MUDBAC register 2 mapping */ Byte_t sIRQMap[16] = { 0,0,0,0x10,0x20,0x30,0,0,0,0x40,0x50,0x60,0x70,0,0,0x80 }; #endif static Byte_t sBitMapClrTbl[8] = { 0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f }; static Byte_t sBitMapSetTbl[8] = { 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 }; /*************************************************************************** Function: sInitController Purpose: Initialization of controller global registers and controller structure. Call: sInitController(CtlP,CtlNum,MudbacIO,AiopIOList,AiopIOListSize, IRQNum,Frequency,PeriodicOnly) CONTROLLER_T *CtlP; Ptr to controller structure int CtlNum; Controller number ByteIO_t MudbacIO; Mudbac base I/O address. ByteIO_t *AiopIOList; List of I/O addresses for each AIOP. This list must be in the order the AIOPs will be found on the controller. Once an AIOP in the list is not found, it is assumed that there are no more AIOPs on the controller. int AiopIOListSize; Number of addresses in AiopIOList int IRQNum; Interrupt Request number. Can be any of the following: 0: Disable global interrupts 3: IRQ 3 4: IRQ 4 5: IRQ 5 9: IRQ 9 10: IRQ 10 11: IRQ 11 12: IRQ 12 15: IRQ 15 Byte_t Frequency: A flag identifying the frequency of the periodic interrupt, can be any one of the following: FREQ_DIS - periodic interrupt disabled FREQ_137HZ - 137 Hertz FREQ_69HZ - 69 Hertz FREQ_34HZ - 34 Hertz FREQ_17HZ - 17 Hertz FREQ_9HZ - 9 Hertz FREQ_4HZ - 4 Hertz If IRQNum is set to 0 the Frequency parameter is overidden, it is forced to a value of FREQ_DIS. int PeriodicOnly: TRUE if all interrupts except the periodic interrupt are to be blocked. FALSE is both the periodic interrupt and other channel interrupts are allowed. If IRQNum is set to 0 the PeriodicOnly parameter is overidden, it is forced to a value of FALSE. Return: int: Number of AIOPs on the controller, or CTLID_NULL if controller initialization failed. Comments: If periodic interrupts are to be disabled but AIOP interrupts are allowed, set Frequency to FREQ_DIS and PeriodicOnly to FALSE. If interrupts are to be completely disabled set IRQNum to 0. Setting Frequency to FREQ_DIS and PeriodicOnly to TRUE is an invalid combination. This function performs initialization of global interrupt modes, but it does not actually enable global interrupts. To enable and disable global interrupts use functions sEnGlobalInt() and sDisGlobalInt(). Enabling of global interrupts is normally not done until all other initializations are complete. Even if interrupts are globally enabled, they must also be individually enabled for each channel that is to generate interrupts. Warnings: No range checking on any of the parameters is done. No context switches are allowed while executing this function. After this function all AIOPs on the controller are disabled, they can be enabled with sEnAiop(). */ int sInitController( CONTROLLER_T *CtlP, int CtlNum, ByteIO_t MudbacIO, ByteIO_t *AiopIOList, int AiopIOListSize, int IRQNum, Byte_t Frequency, int PeriodicOnly) { int i; ByteIO_t io; CtlP->CtlNum = CtlNum; CtlP->BusType = isISA; CtlP->CtlID = CTLID_0001; /* controller release 1 */ CtlP->MBaseIO = MudbacIO; CtlP->MReg1IO = MudbacIO + 1; CtlP->MReg2IO = MudbacIO + 2; CtlP->MReg3IO = MudbacIO + 3; #if 1 CtlP->MReg2 = 0; /* interrupt disable */ CtlP->MReg3 = 0; /* no periodic interrupts */ #else if(sIRQMap[IRQNum] == 0) /* interrupts globally disabled */ { CtlP->MReg2 = 0; /* interrupt disable */ CtlP->MReg3 = 0; /* no periodic interrupts */ } else { CtlP->MReg2 = sIRQMap[IRQNum]; /* set IRQ number */ CtlP->MReg3 = Frequency; /* set frequency */ if(PeriodicOnly) /* periodic interrupt only */ { CtlP->MReg3 |= PERIODIC_ONLY; } } #endif sOutB(CtlP->MReg2IO,CtlP->MReg2); sOutB(CtlP->MReg3IO,CtlP->MReg3); sControllerEOI(CtlP); /* clear EOI if warm init */ /* Init AIOPs */ CtlP->NumAiop = 0; for(i=0; i < AiopIOListSize; i++) { io = AiopIOList[i]; CtlP->AiopIO[i] = (WordIO_t)io; CtlP->AiopIntChanIO[i] = io + _INT_CHAN; sOutB(CtlP->MReg2IO,CtlP->MReg2 | (i & 0x03)); /* AIOP index */ sOutB(MudbacIO,(Byte_t)(io >> 6)); /* set up AIOP I/O in MUDBAC */ sEnAiop(CtlP,i); /* enable the AIOP */ CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ { sDisAiop(CtlP,i); /* disable AIOP */ break; /* done looking for AIOPs */ } CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */ sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE); /* clock prescaler */ sOutB(io + _INDX_DATA,CLOCK_PRESC); CtlP->NumAiop++; /* bump count of AIOPs */ sDisAiop(CtlP,i); /* disable AIOP */ } if(CtlP->NumAiop == 0) return(-1); else return(CtlP->NumAiop); } int sPCIInitController( CONTROLLER_T *CtlP, int CtlNum, ByteIO_t *AiopIOList, int AiopIOListSize, int IRQNum, Byte_t Frequency, int PeriodicOnly) { int i; ByteIO_t io; CtlP->CtlNum = CtlNum; CtlP->BusType = isPCI; CtlP->CtlID = CTLID_0001; /* controller release 1 */ CtlP->PCIIO = (WordIO_t)((ByteIO_t)AiopIOList[0] + _PCI_INT_FUNC); sPCIControllerEOI(CtlP); /* Init AIOPs */ CtlP->NumAiop = 0; for(i=0; i < AiopIOListSize; i++) { io = AiopIOList[i]; CtlP->AiopIO[i] = (WordIO_t)io; CtlP->AiopIntChanIO[i] = io + _INT_CHAN; CtlP->AiopID[i] = sReadAiopID(io); /* read AIOP ID */ if(CtlP->AiopID[i] == AIOPID_NULL) /* if AIOP does not exist */ { break; /* done looking for AIOPs */ } CtlP->AiopNumChan[i] = sReadAiopNumChan((WordIO_t)io); /* num channels in AIOP */ sOutW((WordIO_t)io + _INDX_ADDR,_CLK_PRE); /* clock prescaler */ sOutB(io + _INDX_DATA,CLOCK_PRESC); CtlP->NumAiop++; /* bump count of AIOPs */ } if(CtlP->NumAiop == 0) return(-1); else return(CtlP->NumAiop); } /*************************************************************************** Function: sReadAiopID Purpose: Read the AIOP idenfication number directly from an AIOP. Call: sReadAiopID(io) ByteIO_t io: AIOP base I/O address Return: int: Flag AIOPID_XXXX if a valid AIOP is found, where X is replace by an identifying number. Flag AIOPID_NULL if no valid AIOP is found Warnings: No context switches are allowed while executing this function. */ int sReadAiopID(ByteIO_t io) { Byte_t AiopID; /* ID byte from AIOP */ sOutB(io + _CMD_REG,RESET_ALL); /* reset AIOP */ sOutB(io + _CMD_REG,0x0); AiopID = sInB(io + _CHN_STAT0) & 0x07; if(AiopID == 0x06) return(1); else /* AIOP does not exist */ return(-1); } /*************************************************************************** Function: sReadAiopNumChan Purpose: Read the number of channels available in an AIOP directly from an AIOP. Call: sReadAiopNumChan(io) WordIO_t io: AIOP base I/O address Return: int: The number of channels available Comments: The number of channels is determined by write/reads from identical offsets within the SRAM address spaces for channels 0 and 4. If the channel 4 space is mirrored to channel 0 it is a 4 channel AIOP, otherwise it is an 8 channel. Warnings: No context switches are allowed while executing this function. */ int sReadAiopNumChan(WordIO_t io) { Word_t x; sOutDW((DWordIO_t)io + _INDX_ADDR,0x12340000L); /* write to chan 0 SRAM */ sOutW(io + _INDX_ADDR,0); /* read from SRAM, chan 0 */ x = sInW(io + _INDX_DATA); sOutW(io + _INDX_ADDR,0x4000); /* read from SRAM, chan 4 */ if(x != sInW(io + _INDX_DATA)) /* if different must be 8 chan */ return(8); else return(4); } /*************************************************************************** Function: sInitChan Purpose: Initialization of a channel and channel structure Call: sInitChan(CtlP,ChP,AiopNum,ChanNum) CONTROLLER_T *CtlP; Ptr to controller structure CHANNEL_T *ChP; Ptr to channel structure int AiopNum; AIOP number within controller int ChanNum; Channel number within AIOP Return: int: TRUE if initialization succeeded, FALSE if it fails because channel number exceeds number of channels available in AIOP. Comments: This function must be called before a channel can be used. Warnings: No range checking on any of the parameters is done. No context switches are allowed while executing this function. */ int sInitChan( CONTROLLER_T *CtlP, CHANNEL_T *ChP, int AiopNum, int ChanNum) { int i; WordIO_t AiopIO; WordIO_t ChIOOff; Byte_t *ChR; Word_t ChOff; static Byte_t R[4]; if(ChanNum >= CtlP->AiopNumChan[AiopNum]) return(FALSE); /* exceeds num chans in AIOP */ /* Channel, AIOP, and controller identifiers */ ChP->CtlP = CtlP; ChP->ChanID = CtlP->AiopID[AiopNum]; ChP->AiopNum = AiopNum; ChP->ChanNum = ChanNum; /* Global direct addresses */ AiopIO = CtlP->AiopIO[AiopNum]; ChP->Cmd = (ByteIO_t)AiopIO + _CMD_REG; ChP->IntChan = (ByteIO_t)AiopIO + _INT_CHAN; ChP->IntMask = (ByteIO_t)AiopIO + _INT_MASK; ChP->IndexAddr = (DWordIO_t)AiopIO + _INDX_ADDR; ChP->IndexData = AiopIO + _INDX_DATA; /* Channel direct addresses */ ChIOOff = AiopIO + ChP->ChanNum * 2; ChP->TxRxData = ChIOOff + _TD0; ChP->ChanStat = ChIOOff + _CHN_STAT0; ChP->TxRxCount = ChIOOff + _FIFO_CNT0; ChP->IntID = (ByteIO_t)AiopIO + ChP->ChanNum + _INT_ID0; /* Initialize the channel from the RData array */ for(i=0; i < RDATASIZE; i+=4) { R[0] = RData[i]; R[1] = RData[i+1] + 0x10 * ChanNum; R[2] = RData[i+2]; R[3] = RData[i+3]; sOutDW(ChP->IndexAddr,*((DWord_t *)&R[0])); } ChR = ChP->R; for(i=0; i < RREGDATASIZE; i+=4) { ChR[i] = RRegData[i]; ChR[i+1] = RRegData[i+1] + 0x10 * ChanNum; ChR[i+2] = RRegData[i+2]; ChR[i+3] = RRegData[i+3]; } /* Indexed registers */ ChOff = (Word_t)ChanNum * 0x1000; ChP->BaudDiv[0] = (Byte_t)(ChOff + _BAUD); ChP->BaudDiv[1] = (Byte_t)((ChOff + _BAUD) >> 8); ChP->BaudDiv[2] = (Byte_t)BRD9600; ChP->BaudDiv[3] = (Byte_t)(BRD9600 >> 8); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->BaudDiv[0]); ChP->TxControl[0] = (Byte_t)(ChOff + _TX_CTRL); ChP->TxControl[1] = (Byte_t)((ChOff + _TX_CTRL) >> 8); ChP->TxControl[2] = 0; ChP->TxControl[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); ChP->RxControl[0] = (Byte_t)(ChOff + _RX_CTRL); ChP->RxControl[1] = (Byte_t)((ChOff + _RX_CTRL) >> 8); ChP->RxControl[2] = 0; ChP->RxControl[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxEnables[0] = (Byte_t)(ChOff + _TX_ENBLS); ChP->TxEnables[1] = (Byte_t)((ChOff + _TX_ENBLS) >> 8); ChP->TxEnables[2] = 0; ChP->TxEnables[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxEnables[0]); ChP->TxCompare[0] = (Byte_t)(ChOff + _TXCMP1); ChP->TxCompare[1] = (Byte_t)((ChOff + _TXCMP1) >> 8); ChP->TxCompare[2] = 0; ChP->TxCompare[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxCompare[0]); ChP->TxReplace1[0] = (Byte_t)(ChOff + _TXREP1B1); ChP->TxReplace1[1] = (Byte_t)((ChOff + _TXREP1B1) >> 8); ChP->TxReplace1[2] = 0; ChP->TxReplace1[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace1[0]); ChP->TxReplace2[0] = (Byte_t)(ChOff + _TXREP2); ChP->TxReplace2[1] = (Byte_t)((ChOff + _TXREP2) >> 8); ChP->TxReplace2[2] = 0; ChP->TxReplace2[3] = 0; sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxReplace2[0]); ChP->TxFIFOPtrs = ChOff + _TXF_OUTP; ChP->TxFIFO = ChOff + _TX_FIFO; sOutB(ChP->Cmd,(Byte_t)ChanNum | RESTXFCNT); /* apply reset Tx FIFO count */ sOutB(ChP->Cmd,(Byte_t)ChanNum); /* remove reset Tx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ sOutW(ChP->IndexData,0); ChP->RxFIFOPtrs = ChOff + _RXF_OUTP; ChP->RxFIFO = ChOff + _RX_FIFO; sOutB(ChP->Cmd,(Byte_t)ChanNum | RESRXFCNT); /* apply reset Rx FIFO count */ sOutB(ChP->Cmd,(Byte_t)ChanNum); /* remove reset Rx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */ sOutW(ChP->IndexData,0); sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ sOutW(ChP->IndexData,0); ChP->TxPrioCnt = ChOff + _TXP_CNT; sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioCnt); sOutB(ChP->IndexData,0); ChP->TxPrioPtr = ChOff + _TXP_PNTR; sOutW((WordIO_t)ChP->IndexAddr,ChP->TxPrioPtr); sOutB(ChP->IndexData,0); ChP->TxPrioBuf = ChOff + _TXP_BUF; sEnRxProcessor(ChP); /* start the Rx processor */ return(TRUE); } /*************************************************************************** Function: sStopRxProcessor Purpose: Stop the receive processor from processing a channel. Call: sStopRxProcessor(ChP) CHANNEL_T *ChP; Ptr to channel structure Comments: The receive processor can be started again with sStartRxProcessor(). This function causes the receive processor to skip over the stopped channel. It does not stop it from processing other channels. Warnings: No context switches are allowed while executing this function. Do not leave the receive processor stopped for more than one character time. After calling this function a delay of 4 uS is required to ensure that the receive processor is no longer processing this channel. */ void sStopRxProcessor(CHANNEL_T *ChP) { Byte_t R[4]; R[0] = ChP->R[0]; R[1] = ChP->R[1]; R[2] = 0x0a; R[3] = ChP->R[3]; sOutDW(ChP->IndexAddr,*(DWord_t *)&R[0]); } /*************************************************************************** Function: sFlushRxFIFO Purpose: Flush the Rx FIFO Call: sFlushRxFIFO(ChP) CHANNEL_T *ChP; Ptr to channel structure Return: void Comments: To prevent data from being enqueued or dequeued in the Tx FIFO while it is being flushed the receive processor is stopped and the transmitter is disabled. After these operations a 4 uS delay is done before clearing the pointers to allow the receive processor to stop. These items are handled inside this function. Warnings: No context switches are allowed while executing this function. */ void sFlushRxFIFO(CHANNEL_T *ChP) { int i; Byte_t Ch; /* channel number within AIOP */ int RxFIFOEnabled; /* TRUE if Rx FIFO enabled */ if(sGetRxCnt(ChP) == 0) /* Rx FIFO empty */ return; /* don't need to flush */ RxFIFOEnabled = FALSE; if(ChP->R[0x32] == 0x08) /* Rx FIFO is enabled */ { RxFIFOEnabled = TRUE; sDisRxFIFO(ChP); /* disable it */ for(i=0; i < 2000/200; i++) /* delay 2 uS to allow proc to disable FIFO*/ sInB(ChP->IntChan); /* depends on bus i/o timing */ } sGetChanStatus(ChP); /* clear any pending Rx errors in chan stat */ Ch = (Byte_t)sGetChanNum(ChP); sOutB(ChP->Cmd,Ch | RESRXFCNT); /* apply reset Rx FIFO count */ sOutB(ChP->Cmd,Ch); /* remove reset Rx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs); /* clear Rx out ptr */ sOutW(ChP->IndexData,0); sOutW((WordIO_t)ChP->IndexAddr,ChP->RxFIFOPtrs + 2); /* clear Rx in ptr */ sOutW(ChP->IndexData,0); if(RxFIFOEnabled) sEnRxFIFO(ChP); /* enable Rx FIFO */ } /*************************************************************************** Function: sFlushTxFIFO Purpose: Flush the Tx FIFO Call: sFlushTxFIFO(ChP) CHANNEL_T *ChP; Ptr to channel structure Return: void Comments: To prevent data from being enqueued or dequeued in the Tx FIFO while it is being flushed the receive processor is stopped and the transmitter is disabled. After these operations a 4 uS delay is done before clearing the pointers to allow the receive processor to stop. These items are handled inside this function. Warnings: No context switches are allowed while executing this function. */ void sFlushTxFIFO(CHANNEL_T *ChP) { int i; Byte_t Ch; /* channel number within AIOP */ int TxEnabled; /* TRUE if transmitter enabled */ if(sGetTxCnt(ChP) == 0) /* Tx FIFO empty */ return; /* don't need to flush */ TxEnabled = FALSE; if(ChP->TxControl[3] & TX_ENABLE) { TxEnabled = TRUE; sDisTransmit(ChP); /* disable transmitter */ } sStopRxProcessor(ChP); /* stop Rx processor */ for(i = 0; i < 4000/200; i++) /* delay 4 uS to allow proc to stop */ sInB(ChP->IntChan); /* depends on bus i/o timing */ Ch = (Byte_t)sGetChanNum(ChP); sOutB(ChP->Cmd,Ch | RESTXFCNT); /* apply reset Tx FIFO count */ sOutB(ChP->Cmd,Ch); /* remove reset Tx FIFO count */ sOutW((WordIO_t)ChP->IndexAddr,ChP->TxFIFOPtrs); /* clear Tx in/out ptrs */ sOutW(ChP->IndexData,0); if(TxEnabled) sEnTransmit(ChP); /* enable transmitter */ sStartRxProcessor(ChP); /* restart Rx processor */ } /*************************************************************************** Function: sWriteTxPrioByte Purpose: Write a byte of priority transmit data to a channel Call: sWriteTxPrioByte(ChP,Data) CHANNEL_T *ChP; Ptr to channel structure Byte_t Data; The transmit data byte Return: int: 1 if the bytes is successfully written, otherwise 0. Comments: The priority byte is transmitted before any data in the Tx FIFO. Warnings: No context switches are allowed while executing this function. */ int sWriteTxPrioByte(CHANNEL_T *ChP, Byte_t Data) { Byte_t DWBuf[4]; /* buffer for double word writes */ Word_t *WordPtr; /* must be far because Win SS != DS */ register DWordIO_t IndexAddr; if(sGetTxCnt(ChP) > 1) /* write it to Tx priority buffer */ { IndexAddr = ChP->IndexAddr; sOutW((WordIO_t)IndexAddr,ChP->TxPrioCnt); /* get priority buffer status */ if(sInB((ByteIO_t)ChP->IndexData) & PRI_PEND) /* priority buffer busy */ return(0); /* nothing sent */ WordPtr = (Word_t *)(&DWBuf[0]); *WordPtr = ChP->TxPrioBuf; /* data byte address */ DWBuf[2] = Data; /* data byte value */ sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */ *WordPtr = ChP->TxPrioCnt; /* Tx priority count address */ DWBuf[2] = PRI_PEND + 1; /* indicate 1 byte pending */ DWBuf[3] = 0; /* priority buffer pointer */ sOutDW(IndexAddr,*((DWord_t *)(&DWBuf[0]))); /* write it out */ } else /* write it to Tx FIFO */ { sWriteTxByte(sGetTxRxDataIO(ChP),Data); } return(1); /* 1 byte sent */ } /*************************************************************************** Function: sEnInterrupts Purpose: Enable one or more interrupts for a channel Call: sEnInterrupts(ChP,Flags) CHANNEL_T *ChP; Ptr to channel structure Word_t Flags: Interrupt enable flags, can be any combination of the following flags: TXINT_EN: Interrupt on Tx FIFO empty RXINT_EN: Interrupt on Rx FIFO at trigger level (see sSetRxTrigger()) SRCINT_EN: Interrupt on SRC (Special Rx Condition) MCINT_EN: Interrupt on modem input change CHANINT_EN: Allow channel interrupt signal to the AIOP's Interrupt Channel Register. Return: void Comments: If an interrupt enable flag is set in Flags, that interrupt will be enabled. If an interrupt enable flag is not set in Flags, that interrupt will not be changed. Interrupts can be disabled with function sDisInterrupts(). This function sets the appropriate bit for the channel in the AIOP's Interrupt Mask Register if the CHANINT_EN flag is set. This allows this channel's bit to be set in the AIOP's Interrupt Channel Register. Interrupts must also be globally enabled before channel interrupts will be passed on to the host. This is done with function sEnGlobalInt(). In some cases it may be desirable to disable interrupts globally but enable channel interrupts. This would allow the global interrupt status register to be used to determine which AIOPs need service. */ void sEnInterrupts(CHANNEL_T *ChP,Word_t Flags) { Byte_t Mask; /* Interrupt Mask Register */ ChP->RxControl[2] |= ((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxControl[2] |= ((Byte_t)Flags & TXINT_EN); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); if(Flags & CHANINT_EN) { Mask = sInB(ChP->IntMask) | sBitMapSetTbl[ChP->ChanNum]; sOutB(ChP->IntMask,Mask); } } /*************************************************************************** Function: sDisInterrupts Purpose: Disable one or more interrupts for a channel Call: sDisInterrupts(ChP,Flags) CHANNEL_T *ChP; Ptr to channel structure Word_t Flags: Interrupt flags, can be any combination of the following flags: TXINT_EN: Interrupt on Tx FIFO empty RXINT_EN: Interrupt on Rx FIFO at trigger level (see sSetRxTrigger()) SRCINT_EN: Interrupt on SRC (Special Rx Condition) MCINT_EN: Interrupt on modem input change CHANINT_EN: Disable channel interrupt signal to the AIOP's Interrupt Channel Register. Return: void Comments: If an interrupt flag is set in Flags, that interrupt will be disabled. If an interrupt flag is not set in Flags, that interrupt will not be changed. Interrupts can be enabled with function sEnInterrupts(). This function clears the appropriate bit for the channel in the AIOP's Interrupt Mask Register if the CHANINT_EN flag is set. This blocks this channel's bit from being set in the AIOP's Interrupt Channel Register. */ void sDisInterrupts(CHANNEL_T *ChP,Word_t Flags) { Byte_t Mask; /* Interrupt Mask Register */ ChP->RxControl[2] &= ~((Byte_t)Flags & (RXINT_EN | SRCINT_EN | MCINT_EN)); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->RxControl[0]); ChP->TxControl[2] &= ~((Byte_t)Flags & TXINT_EN); sOutDW(ChP->IndexAddr,*(DWord_t *)&ChP->TxControl[0]); if(Flags & CHANINT_EN) { Mask = sInB(ChP->IntMask) & sBitMapClrTbl[ChP->ChanNum]; sOutB(ChP->IntMask,Mask); } } /********************************************************************* Begin FreeBsd-specific driver code **********************************************************************/ static int rpprobe __P((struct isa_device *)); static int rpattach __P((struct isa_device *)); static char* rp_pciprobe(pcici_t tag, pcidi_t type); static void rp_pciattach(pcici_t tag, int unit); static u_long rp_pcicount; static struct pci_device rp_pcidevice = { "rp", rp_pciprobe, rp_pciattach, &rp_pcicount, NULL }; DATA_SET (pcidevice_set, rp_pcidevice); static timeout_t rpdtrwakeup; struct isa_driver rpdriver = { rpprobe, rpattach, "rp" }; -#define CDEV_MAJOR 81 - static char driver_name[] = "rp"; static d_open_t rpopen; static d_close_t rpclose; static d_read_t rpread; static d_write_t rpwrite; static d_ioctl_t rpioctl; static d_stop_t rpstop; static d_devtotty_t rpdevtotty; -static struct cdevsw rp_cdevsw = - { rpopen, rpclose, rpread, rpwrite, - rpioctl, rpstop, noreset, rpdevtotty, - ttpoll, nommap, NULL, driver_name, - NULL, -1}; +#define CDEV_MAJOR 81 +static struct cdevsw rp_cdevsw = { + rpopen, rpclose, rpread, rpwrite, + rpioctl, rpstop, noreset, rpdevtotty, + ttpoll, nommap, NULL, driver_name, + NULL, -1, nodump, nopsize, + D_TTY, +}; static int rp_controller_port = 0; static int rp_num_ports_open = 0; static int rp_timeout; static int ndevs = 0; static int minor_to_unit[128]; static struct tty rp_tty[128]; static int rp_num_ports[4]; /* Number of ports on each controller */ #define _INLINE_ __inline #define POLL_INTERVAL 1 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_UNIT(dev) (MINOR_TO_UNIT(minor(dev)) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_MAGIC(dev) ((minor(dev)) & ~MINOR_MAGIC_MASK) #define IS_CALLOUT(dev) (minor(dev) & CALLOUT_MASK) #define IS_CONTROL(dev) (minor(dev) & CONTROL_MASK) #define RP_ISMULTIPORT(dev) ((dev)->id_flags & 0x1) #define RP_MPMASTER(dev) (((dev)->id_flags >> 8) & 0xff) #define RP_NOTAST4(dev) ((dev)->id_flags & 0x04) static struct rp_port *p_rp_addr[4]; static struct rp_port *p_rp_table[MAX_RP_PORTS]; #define rp_addr(unit) (p_rp_addr[unit]) #define rp_table(port) (p_rp_table[port]) /* * The top-level routines begin here */ int rpselect __P((dev_t, int, struct proc *)); static int rpparam __P((struct tty *, struct termios *)); static void rpstart __P((struct tty *)); static void rphardclose __P((struct rp_port *)); #define rpmap nomap #define rpreset noreset #define rpstrategy nostrategy static void rp_disc_optim __P((struct tty *tp, struct termios *t, struct rp_port *rp)); static _INLINE_ void rp_do_receive(struct rp_port *rp, struct tty *tp, CHANNEL_t *cp, unsigned int ChanStatus) { int spl; unsigned int CharNStat; int ToRecv, wRecv, ch; ToRecv = sGetRxCnt(cp); if(ToRecv == 0) return; /* If status indicates there are errored characters in the FIFO, then enter status mode (a word in FIFO holds characters and status) */ if(ChanStatus & (RXFOVERFL | RXBREAK | RXFRAME | RXPARITY)) { if(!(ChanStatus & STATMODE)) { ChanStatus |= STATMODE; sEnRxStatusMode(cp); } } /* if we previously entered status mode then read down the FIFO one word at a time, pulling apart the character and the status. Update error counters depending on status. */ if(ChanStatus & STATMODE) { while(ToRecv) { if(tp->t_state & TS_TBLOCK) { break; } CharNStat = sInW(sGetTxRxDataIO(cp)); ch = CharNStat & 0xff; if((CharNStat & STMBREAK) || (CharNStat & STMFRAMEH)) ch |= TTY_FE; else if (CharNStat & STMPARITYH) ch |= TTY_PE; else if (CharNStat & STMRCVROVRH) rp->rp_overflows++; (*linesw[tp->t_line].l_rint)(ch, tp); ToRecv--; } /* After emtying FIFO in status mode, turn off status mode */ if(sGetRxCnt(cp) == 0) sDisRxStatusMode(cp); } else { while (ToRecv) { if(tp->t_state & TS_TBLOCK) { break; } ch = (u_char) sInB(sGetTxRxDataIO(cp)); spl = spltty(); (*linesw[tp->t_line].l_rint)(ch, tp); splx(spl); ToRecv--; } } } static _INLINE_ void rp_handle_port(struct rp_port *rp) { CHANNEL_t *cp; struct tty *tp; unsigned int IntMask, ChanStatus; int oldcts, ToRecv; if(!rp) return; cp = &rp->rp_channel; tp = rp->rp_tty; IntMask = sGetChanIntID(cp); IntMask = IntMask & rp->rp_intmask; ChanStatus = sGetChanStatus(cp); if(IntMask & RXF_TRIG) if(!(tp->t_state & TS_TBLOCK) && (tp->t_state & TS_CARR_ON) && (tp->t_state & TS_ISOPEN)) { rp_do_receive(rp, tp, cp, ChanStatus); } if(IntMask & DELTA_CD) { if(ChanStatus & CD_ACT) { if(!(tp->t_state & TS_CARR_ON) ) { (void)(*linesw[tp->t_line].l_modem)(tp, 1); } } else { if((tp->t_state & TS_CARR_ON)) { (void)(*linesw[tp->t_line].l_modem)(tp, 0); if((*linesw[tp->t_line].l_modem)(tp, 0) == 0) { rphardclose(rp); } } } } /* oldcts = rp->rp_cts; rp->rp_cts = ((ChanStatus & CTS_ACT) != 0); if(oldcts != rp->rp_cts) { printf("CTS change (now %s)... on port %d\n", rp->rp_cts ? "on" : "off", rp->rp_port); } */ } static void rp_do_poll(void *not_used) { CONTROLLER_t *ctl; struct rp_port *rp; struct tty *tp; int unit, aiop, ch, line, count; unsigned char CtlMask, AiopMask; for(unit = 0; unit <= ndevs; unit++) { rp = rp_addr(unit); ctl = rp->rp_ctlp; if(ctl->BusType == isPCI) CtlMask = sPCIGetControllerIntStatus(ctl); else CtlMask = sGetControllerIntStatus(ctl); for(aiop=0; CtlMask; CtlMask >>=1, aiop++) { if(CtlMask & 1) { AiopMask = sGetAiopIntStatus(ctl, aiop); for(ch = 0; AiopMask; AiopMask >>=1, ch++) { if(AiopMask & 1) { line = (unit << 5) | (aiop << 3) | ch; rp = rp_table(line); rp_handle_port(rp); } } } } for(line = 0, rp = rp_addr(unit); line < rp_num_ports[unit]; line++, rp++) { tp = rp->rp_tty; if((tp->t_state & TS_BUSY) && (tp->t_state & TS_ISOPEN)) { count = sGetTxCnt(&rp->rp_channel); if(count == 0) tp->t_state &= ~(TS_BUSY); if(!(tp->t_state & TS_TTSTOP) && (count <= rp->rp_restart)) { (*linesw[tp->t_line].l_start)(tp); } } } } if(rp_num_ports_open) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); } static char* rp_pciprobe(pcici_t tag, pcidi_t type) { int vendor_id; vendor_id = type & 0xffff; switch(vendor_id) case 0x11fe: return("rp"); return(NULL); } static int rpprobe(dev) struct isa_device *dev; { struct isa_device *idev; int controller, unit; int i, aiop, num_aiops; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; CONTROLLER_t *ctlp; unit = dev->id_unit; if (dev->id_unit >= 4) { printf("rpprobe: unit number %d invalid.\n", dev->id_unit); return 1; } printf("probing for RocketPort(ISA) unit %d\n", unit); if (rp_controller_port) controller = rp_controller_port; else { controller = dev->id_iobase + 0x40; } for (aiop=0; aiopid_iobase + (aiop * 0x400); ctlp = sCtlNumToCtlPtr(dev->id_unit); num_aiops = sInitController(ctlp, dev->id_unit, controller + ((unit-rp_pcicount)*0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); if (num_aiops <= 0) { printf("board%d init failed\n", unit); return 0; } if (rp_controller_port) { dev->id_msize = 64; } else { dev->id_msize = 68; rp_controller_port = controller; } dev->id_irq = 0; return 1; } static void rp_pciattach(pcici_t tag, int unit) { dev_t rp_dev; int success, rpmajor, oldspl; u_short iobase; int num_ports, num_chan, num_aiops; int aiop, chan, port; int ChanStatus, line, i, count; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; struct rp_port *rp; struct tty *tty; CONTROLLER_t *ctlp; char status; success = pci_map_port(tag, 0x10, &iobase); if(!success) printf("ioaddr mapping failed for RocketPort(PCI)\n"); for(aiop=0; aiop < MAX_AIOPS_PER_BOARD; aiop++) aiopio[aiop] = iobase + (aiop * 0x40); ctlp = sCtlNumToCtlPtr(unit); num_aiops = sPCIInitController(ctlp, unit, aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); num_ports = 0; for(aiop=0; aiop < num_aiops; aiop++) { sResetAiopByNum(ctlp, aiop); num_ports += sGetAiopNumChan(ctlp, aiop); } printf("RocketPort%d = %d ports\n", unit, num_ports); rp_num_ports[unit] = num_ports; rp = (struct rp_port *) malloc(sizeof(struct rp_port) * num_ports, M_TTYS, M_NOWAIT); if(rp == 0) { printf("rp_attach: Could not malloc rp_ports structures\n"); return; } count = unit * 32; /* board times max ports per card SG */ for(i=count;i < (count + rp_num_ports[unit]);i++) minor_to_unit[i] = unit; bzero(rp, sizeof(struct rp_port) * num_ports); tty = (struct tty *) malloc(sizeof(struct tty) * num_ports, M_TTYS, M_NOWAIT); if(tty == 0) { printf("rp_attach: Could not malloc tty structures\n"); return; } bzero(tty, sizeof(struct tty) * num_ports); oldspl = spltty(); rp_addr(unit) = rp; splx(oldspl); rp_dev = makedev(CDEV_MAJOR, unit); cdevsw_add(&rp_dev, &rp_cdevsw, NULL); port = 0; for(aiop=0; aiop < num_aiops; aiop++) { num_chan = sGetAiopNumChan(ctlp, aiop); for(chan=0; chan < num_chan; chan++, port++, rp++, tty++) { rp->rp_tty = tty; rp->rp_port = port; rp->rp_ctlp = ctlp; rp->rp_unit = unit; rp->rp_chan = chan; rp->rp_aiop = aiop; tty->t_line = 0; /* tty->t_termios = deftermios; */ rp->dtr_wait = 3 * hz; rp->it_in.c_iflag = 0; rp->it_in.c_oflag = 0; rp->it_in.c_cflag = TTYDEF_CFLAG; rp->it_in.c_lflag = 0; termioschars(&rp->it_in); /* termioschars(&tty->t_termios); */ rp->it_in.c_ispeed = rp->it_in.c_ospeed = TTYDEF_SPEED; rp->it_out = rp->it_in; rp->rp_intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; ChanStatus = sGetChanStatus(&rp->rp_channel); if(sInitChan(ctlp, &rp->rp_channel, aiop, chan) == 0) { printf("RocketPort sInitChan(%d, %d, %d) failed \n", unit, aiop, chan); return; } ChanStatus = sGetChanStatus(&rp->rp_channel); rp->rp_cts = (ChanStatus & CTS_ACT) != 0; line = (unit << 5) | (aiop << 3) | chan; rp_table(line) = rp; /* devfs_add_devswf(&rp_cdevsw, port, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyR%r", port); devfs_add_devswf(&rp_cdevsw, port | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyRi%r", port); */ } } } static int rpattach(dev) struct isa_device *dev; { struct isa_device *idev; dev_t rp_dev; int iobase, unit, rpmajor, oldspl; int num_ports, num_chan, num_aiops; int aiop, chan, port; int ChanStatus, line, i, count; unsigned int aiopio[MAX_AIOPS_PER_BOARD]; struct rp_port *rp; struct tty *tty; CONTROLLER_t *ctlp; char status; iobase = dev->id_iobase; unit = dev->id_unit; ndevs = unit; for(aiop=0; aiop < MAX_AIOPS_PER_BOARD; aiop++) aiopio[aiop] = iobase + (aiop * 0x400); ctlp = sCtlNumToCtlPtr(unit); num_aiops = sInitController(ctlp, unit, rp_controller_port + ((unit-rp_pcicount) * 0x400), aiopio, MAX_AIOPS_PER_BOARD, 0, FREQ_DIS, 0); num_ports = 0; for(aiop=0; aiop < num_aiops; aiop++) { sResetAiopByNum(ctlp, aiop); sEnAiop(ctlp, aiop); num_ports += sGetAiopNumChan(ctlp, aiop); } printf("RocketPort%d = %d ports\n", unit, num_ports); rp_num_ports[unit] = num_ports; rp = (struct rp_port *) malloc(sizeof(struct rp_port) * num_ports, M_TTYS, M_NOWAIT); if(rp == 0) { printf("rp_attach: Could not malloc rp_ports structures\n"); return(0); } count = unit * 32; /* board # times max ports per card SG */ for(i=count;i < (count + rp_num_ports[unit]);i++) minor_to_unit[i] = unit; bzero(rp, sizeof(struct rp_port) * num_ports); tty = (struct tty *) malloc(sizeof(struct tty) * num_ports, M_TTYS, M_NOWAIT); if(tty == 0) { printf("rp_attach: Could not malloc tty structures\n"); return(0); } bzero(tty, sizeof(struct tty) * num_ports); oldspl = spltty(); rp_addr(unit) = rp; splx(oldspl); rp_dev = makedev(CDEV_MAJOR, unit); cdevsw_add(&rp_dev, &rp_cdevsw, NULL); port = 0; for(aiop=0; aiop < num_aiops; aiop++) { num_chan = sGetAiopNumChan(ctlp, aiop); for(chan=0; chan < num_chan; chan++, port++, rp++, tty++) { rp->rp_tty = tty; rp->rp_port = port; rp->rp_ctlp = ctlp; rp->rp_unit = unit; rp->rp_chan = chan; rp->rp_aiop = aiop; tty->t_line = 0; /* tty->t_termios = deftermios; */ rp->dtr_wait = 3 * hz; rp->it_in.c_iflag = 0; rp->it_in.c_oflag = 0; rp->it_in.c_cflag = TTYDEF_CFLAG; rp->it_in.c_lflag = 0; termioschars(&rp->it_in); /* termioschars(&tty->t_termios); */ rp->it_in.c_ispeed = rp->it_in.c_ospeed = TTYDEF_SPEED; rp->it_out = rp->it_in; rp->rp_intmask = RXF_TRIG | TXFIFO_MT | SRC_INT | DELTA_CD | DELTA_CTS | DELTA_DSR; ChanStatus = sGetChanStatus(&rp->rp_channel); if(sInitChan(ctlp, &rp->rp_channel, aiop, chan) == 0) { printf("RocketPort sInitChan(%d, %d, %d) failed \n", unit, aiop, chan); return(0); } ChanStatus = sGetChanStatus(&rp->rp_channel); rp->rp_cts = (ChanStatus & CTS_ACT) != 0; line = (unit << 5) | (aiop << 3) | chan; rp_table(line) = rp; } } idev = find_isadev(isa_devtab_tty, &rpdriver, RP_MPMASTER(dev) + rp_pcicount); if(idev == NULL) { printf("rp%d: master device %d not configured\n", dev->id_unit, RP_MPMASTER(dev)); } /* printf("COOL!! Device is found!!\n"); for(rpmajor=0;rpmajor> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; if(IS_CONTROL(dev)) return(0); rp = rp_addr(unit) + port; /* rp->rp_tty = &rp_tty[rp->rp_port]; */ tp = rp->rp_tty; oldspl = spltty(); open_top: while(rp->state & ~SET_DTR) { error = tsleep(&rp->dtr_wait, TTIPRI | PCATCH, "rpdtr", 0); if(error != 0) goto out; } if(tp->t_state & TS_ISOPEN) { if(IS_CALLOUT(dev)) { if(!rp->active_out) { error = EBUSY; goto out; } } else { if(rp->active_out) { if(flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&rp->active_out, TTIPRI | PCATCH, "rpbi", 0); if(error != 0) goto out; goto open_top; } } if(tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { splx(oldspl); return(EBUSY); } } else { tp->t_dev = dev; tp->t_param = rpparam; tp->t_oproc = rpstart; tp->t_line = 0; tp->t_termios = IS_CALLOUT(dev) ? rp->it_out : rp->it_in; flags = 0; flags |= SET_RTS; flags |= SET_DTR; rp->rp_channel.TxControl[3] = ((rp->rp_channel.TxControl[3] & ~(SET_RTS | SET_DTR)) | flags); sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); sSetRxTrigger(&rp->rp_channel, TRIG_1); sDisRxStatusMode(&rp->rp_channel); sFlushRxFIFO(&rp->rp_channel); sFlushTxFIFO(&rp->rp_channel); sEnInterrupts(&rp->rp_channel, (TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN)); sSetRxTrigger(&rp->rp_channel, TRIG_1); sDisRxStatusMode(&rp->rp_channel); sClrTxXOFF(&rp->rp_channel); /* sDisRTSFlowCtl(&rp->rp_channel); sDisCTSFlowCtl(&rp->rp_channel); */ sDisTxSoftFlowCtl(&rp->rp_channel); sStartRxProcessor(&rp->rp_channel); sEnRxFIFO(&rp->rp_channel); sEnTransmit(&rp->rp_channel); /* sSetDTR(&rp->rp_channel); sSetRTS(&rp->rp_channel); */ ++rp->wopeners; error = rpparam(tp, &tp->t_termios); --rp->wopeners; if(error != 0) { splx(oldspl); return(error); } - ttsetwater(tp); - rp_num_ports_open++; IntMask = sGetChanIntID(&rp->rp_channel); IntMask = IntMask & rp->rp_intmask; ChanStatus = sGetChanStatus(&rp->rp_channel); if((IntMask & DELTA_CD) || IS_CALLOUT(dev)) { if((ChanStatus & CD_ACT) || IS_CALLOUT(dev)) { (void)(*linesw[tp->t_line].l_modem)(tp, 1); } } if(rp_num_ports_open == 1) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); } if(!(flag&O_NONBLOCK) && !(tp->t_cflag&CLOCAL) && !(tp->t_state & TS_CARR_ON) && !(IS_CALLOUT(dev))) { ++rp->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "rpdcd", 0); --rp->wopeners; if(error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); rp_disc_optim(tp, &tp->t_termios, rp); if(tp->t_state & TS_ISOPEN && IS_CALLOUT(dev)) rp->active_out = TRUE; /* if(rp_num_ports_open == 1) timeout(rp_do_poll, (void *)NULL, POLL_INTERVAL); */ out: splx(oldspl); if(!(tp->t_state & TS_ISOPEN) && rp->wopeners == 0) { rphardclose(rp); } return(error); } int rpclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { int oldspl, unit, mynor, umynor, port, status, i; /* SG */ struct rp_port *rp; struct tty *tp; CHANNEL_t *cp; umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(0); rp = rp_addr(unit) + port; cp = &rp->rp_channel; tp = rp->rp_tty; oldspl = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); rp_disc_optim(tp, &tp->t_termios, rp); rpstop(tp, FREAD | FWRITE); rphardclose(rp); tp->t_state &= ~TS_BUSY; ttyclose(tp); splx(oldspl); return(0); } static void rphardclose(struct rp_port *rp) { int status, oldspl, mynor; struct tty *tp; CHANNEL_t *cp; cp = &rp->rp_channel; tp = rp->rp_tty; mynor = MINOR_MAGIC(tp->t_dev); sFlushRxFIFO(cp); sFlushTxFIFO(cp); sDisTransmit(cp); sDisInterrupts(cp, TXINT_EN|MCINT_EN|RXINT_EN|SRCINT_EN|CHANINT_EN); sDisRTSFlowCtl(cp); sDisCTSFlowCtl(cp); sDisTxSoftFlowCtl(cp); sClrTxXOFF(cp); if(tp->t_cflag&HUPCL || !(tp->t_state&TS_ISOPEN) || !rp->active_out) { sClrDTR(cp); } if(IS_CALLOUT(tp->t_dev)) { sClrDTR(cp); } if(rp->dtr_wait != 0) { timeout(rpdtrwakeup, rp, rp->dtr_wait); rp->state |= ~SET_DTR; } rp->active_out = FALSE; wakeup(&rp->active_out); wakeup(TSA_CARR_ON(tp)); } static int rpread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct rp_port *rp; struct tty *tp; int unit, i, mynor, umynor, port, error = 0; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(ENODEV); rp = rp_addr(unit) + port; tp = rp->rp_tty; error = (*linesw[tp->t_line].l_read)(tp, uio, flag); return(error); } static int rpwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct rp_port *rp; struct tty *tp; int unit, i, mynor, port, umynor, error = 0; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(ENODEV); rp = rp_addr(unit) + port; tp = rp->rp_tty; while(rp->rp_disable_writes) { rp->rp_waiting = 1; if(error = ttysleep(tp, (caddr_t)rp, TTOPRI|PCATCH, "rp_write", 0)) { return(error); } } error = (*linesw[tp->t_line].l_write)(tp, uio, flag); return error; } static void rpdtrwakeup(void *chan) { struct rp_port *rp; rp = (struct rp_port *)chan; rp->state &= SET_DTR; wakeup(&rp->dtr_wait); } int rpioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct rp_port *rp; CHANNEL_t *cp; struct tty *tp; int unit, mynor, port, umynor; /* SG */ int oldspl, cflag, iflag, oflag, lflag; int i, error = 0; char status; int arg, flags, result, ChanStatus; int oldcmd; struct termios term, *t; umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; if(IS_CONTROL(dev)) { struct termios *ct; switch (IS_CONTROL(dev)) { case CONTROL_INIT_STATE: ct = IS_CALLOUT(dev) ? &rp->it_out : &rp->it_in; break; case CONTROL_LOCK_STATE: ct = IS_CALLOUT(dev) ? &rp->lt_out : &rp->lt_in; break; default: return(ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if(error != 0) return(error); *ct = *(struct termios *)data; return(0); case TIOCGETA: *(struct termios *)data = *ct; return(0); case TIOCGETD: *(int *)data = TTYDISC; return(0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return(0); default: return(ENOTTY); } } tp = rp->rp_tty; cp = &rp->rp_channel; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if(error != 0) return(error); if(cmd != oldcmd) { data = (caddr_t)&term; } #endif if((cmd == TIOCSETA) || (cmd == TIOCSETAW) || (cmd == TIOCSETAF)) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = IS_CALLOUT(dev) ? &rp->lt_out : &rp->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for(cc = 0; cc < NCCS; ++cc) if(lt->c_cc[cc] = tp->t_cc[cc]) dt->c_cc[cc] = tp->t_cc[cc]; if(lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if(lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } t = &tp->t_termios; error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if(error != ENOIOCTL) { return(error); } oldspl = spltty(); flags = rp->rp_channel.TxControl[3]; error = ttioctl(tp, cmd, data, flag); flags = rp->rp_channel.TxControl[3]; rp_disc_optim(tp, &tp->t_termios, rp); if(error != ENOIOCTL) { splx(oldspl); return(error); } switch(cmd) { case TIOCSBRK: sSendBreak(&rp->rp_channel); break; case TIOCCBRK: sClrBreak(&rp->rp_channel); break; case TIOCSDTR: sSetDTR(&rp->rp_channel); sSetRTS(&rp->rp_channel); break; case TIOCCDTR: sClrDTR(&rp->rp_channel); break; case TIOCMSET: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] = ((rp->rp_channel.TxControl[3] & ~(SET_RTS | SET_DTR)) | flags); sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMBIS: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] |= flags; sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMBIC: arg = *(int *) data; flags = 0; if(arg & TIOCM_RTS) flags |= SET_RTS; if(arg & TIOCM_DTR) flags |= SET_DTR; rp->rp_channel.TxControl[3] &= ~flags; sOutDW(rp->rp_channel.IndexAddr, *(DWord_t *) &(rp->rp_channel.TxControl[0])); break; case TIOCMGET: ChanStatus = sGetChanStatusLo(&rp->rp_channel); flags = rp->rp_channel.TxControl[3]; result = TIOCM_LE; /* always on while open for some reason */ result |= (((flags & SET_DTR) ? TIOCM_DTR : 0) | ((flags & SET_RTS) ? TIOCM_RTS : 0) | ((ChanStatus & CD_ACT) ? TIOCM_CAR : 0) | ((ChanStatus & DSR_ACT) ? TIOCM_DSR : 0) | ((ChanStatus & CTS_ACT) ? TIOCM_CTS : 0)); if(rp->rp_channel.RxControl[2] & RTSFC_EN) { result |= TIOCM_RTS; } *(int *)data = result; break; case TIOCMSDTRWAIT: error = suser(p->p_ucred, &p->p_acflag); if(error != 0) { splx(oldspl); return(error); } rp->dtr_wait = *(int *)data * hz/100; break; case TIOCMGDTRWAIT: *(int *)data = rp->dtr_wait * 100/hz; break; default: splx(oldspl); return ENOTTY; } splx(oldspl); return(0); } static struct speedtab baud_table[] = { B0, 0, B50, BRD50, B75, BRD75, B110, BRD110, B134, BRD134, B150, BRD150, B200, BRD200, B300, BRD300, B600, BRD600, B1200, BRD1200, B1800, BRD1800, B2400, BRD2400, B4800, BRD4800, B9600, BRD9600, B19200, BRD19200, B38400, BRD38400, B7200, BRD7200, B14400, BRD14400, B57600, BRD57600, B76800, BRD76800, B115200, BRD115200, B230400, BRD230400, -1, -1 }; static int rpparam(tp, t) struct tty *tp; struct termios *t; { struct rp_port *rp; CHANNEL_t *cp; int unit, i, mynor, port, umynor; /* SG */ int oldspl, cflag, iflag, oflag, lflag; int ospeed, flags; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; oldspl = spltty(); cflag = t->c_cflag; iflag = t->c_iflag; oflag = t->c_oflag; lflag = t->c_lflag; ospeed = ttspeedtab(t->c_ispeed, baud_table); if(ospeed < 0 || t->c_ispeed != t->c_ospeed) return(EINVAL); tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = cflag; tp->t_iflag = iflag; tp->t_oflag = oflag; tp->t_lflag = lflag; if(t->c_ospeed == 0) { sClrDTR(cp); return(0); } rp->rp_fifo_lw = ((t->c_ospeed*2) / 1000) +1; /* Set baud rate ----- we only pay attention to ispeed */ sSetDTR(cp); sSetRTS(cp); sSetBaud(cp, ospeed); if(cflag & CSTOPB) { sSetStop2(cp); } else { sSetStop1(cp); } if(cflag & PARENB) { sEnParity(cp); if(cflag & PARODD) { sSetOddParity(cp); } else { sSetEvenParity(cp); } } else { sDisParity(cp); } if((cflag & CSIZE) == CS8) { sSetData8(cp); rp->rp_imask = 0xFF; } else { sSetData7(cp); rp->rp_imask = 0x7F; } if(iflag & ISTRIP) { rp->rp_imask &= 0x7F; } if(cflag & CLOCAL) { rp->rp_intmask &= ~DELTA_CD; } else { rp->rp_intmask |= DELTA_CD; } /* Put flow control stuff here */ if(cflag & CCTS_OFLOW) { sEnCTSFlowCtl(cp); } else { sDisCTSFlowCtl(cp); } if(cflag & CRTS_IFLOW) { rp->rp_rts_iflow = 1; } else { rp->rp_rts_iflow = 0; } if(cflag & CRTS_IFLOW) { sEnRTSFlowCtl(cp); } else { sDisRTSFlowCtl(cp); } rp_disc_optim(tp, t, rp); if((cflag & CLOCAL) || (sGetChanStatusLo(cp) & CD_ACT)) { tp->t_state |= TS_CARR_ON; wakeup(TSA_CARR_ON(tp)); } /* tp->t_state |= TS_CAN_BYPASS_L_RINT; flags = rp->rp_channel.TxControl[3]; if(flags & SET_DTR) else if(flags & SET_RTS) else */ splx(oldspl); return(0); } static void rp_disc_optim(tp, t, rp) struct tty *tp; struct termios *t; struct rp_port *rp; { if(!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) &&(!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) &&(!(t->c_iflag & PARMRK) ||(t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; } static void rpstart(tp) struct tty *tp; { struct rp_port *rp; CHANNEL_t *cp; struct clist *qp; int unit, i, mynor, port, umynor; /* SG */ char status, ch, flags; int spl, xmit_fifo_room; int count, ToRecv; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; flags = rp->rp_channel.TxControl[3]; spl = spltty(); if(tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(spl); return; } if(rp->rp_xmit_stopped) { sEnTransmit(cp); rp->rp_xmit_stopped = 0; } count = sGetTxCnt(cp); if(tp->t_outq.c_cc == 0) { if((tp->t_state & TS_BUSY) && (count == 0)) { tp->t_state &= ~TS_BUSY; } ttwwakeup(tp); splx(spl); return; } xmit_fifo_room = TXFIFO_SIZE - sGetTxCnt(cp); qp = &tp->t_outq; count = 0; if(xmit_fifo_room > 0 && qp->c_cc > 0) { tp->t_state |= TS_BUSY; } while(xmit_fifo_room > 0 && qp->c_cc > 0) { ch = getc(qp); sOutB(sGetTxRxDataIO(cp), ch); xmit_fifo_room--; count++; } rp->rp_restart = (qp->c_cc > 0) ? rp->rp_fifo_lw : 0; ttwwakeup(tp); splx(spl); } static void rpstop(tp, flag) register struct tty *tp; int flag; { struct rp_port *rp; CHANNEL_t *cp; struct clist *qp; int unit, mynor, port, umynor; /* SG */ char status, ch; int spl, xmit_fifo_room; int i, count; umynor = (((minor(tp->t_dev) >> 16) -1) * 32); /* SG */ port = (minor(tp->t_dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; rp = rp_addr(unit) + port; cp = &rp->rp_channel; spl = spltty(); if(tp->t_state & TS_BUSY) { if((tp->t_state&TS_TTSTOP) == 0) { sFlushTxFIFO(cp); } else { if(rp->rp_xmit_stopped == 0) { sDisTransmit(cp); rp->rp_xmit_stopped = 1; } } } splx(spl); rpstart(tp); } int rpselect(dev, flag, p) dev_t dev; int flag; struct proc *p; { return(0); } struct tty * rpdevtotty(dev_t dev) { struct rp_port *rp; int unit, i, port, mynor, umynor; /* SG */ umynor = (((minor(dev) >> 16) -1) * 32); /* SG */ port = (minor(dev) & 0x1f); /* SG */ mynor = (port + umynor); /* SG */ unit = minor_to_unit[mynor]; /* SG */ if(IS_CONTROL(dev)) return(NULL); rp = rp_addr(unit) + port; return(rp->rp_tty); } diff --git a/sys/i386/isa/si.c b/sys/i386/isa/si.c index 4b37f61e3a98..8b7dc26e3c0c 100644 --- a/sys/i386/isa/si.c +++ b/sys/i386/isa/si.c @@ -1,2824 +1,2824 @@ /* * Device driver for Specialix range (SI/XIO) of serial line multiplexors. * * Copyright (C) 1990, 1992, 1998 Specialix International, * Copyright (C) 1993, Andy Rutter * Copyright (C) 1995, Peter Wemm * * Originally derived from: SunOS 4.x version * Ported from BSDI version to FreeBSD by Peter Wemm. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notices, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notices, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Andy Rutter of * Advanced Methods and Tools Ltd. based on original information * from Specialix International. * 4. Neither the name of Advanced Methods and Tools, nor Specialix * International may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHORS BE LIABLE. * - * $Id: si.c,v 1.74 1998/08/16 00:57:07 bde Exp $ + * $Id: si.c,v 1.75 1998/08/16 01:04:48 bde Exp $ */ #ifndef lint static const char si_copyright1[] = "@(#) Copyright (C) Specialix International, 1990,1992,1998", si_copyright2[] = "@(#) Copyright (C) Andy Rutter 1993", si_copyright3[] = "@(#) Copyright (C) Peter Wemm 1995"; #endif /* not lint */ #include "opt_compat.h" #include "opt_debug_si.h" #include "opt_devfs.h" #include #include #if defined(COMPAT_43) || defined(COMPAT_SUNOS) #include #endif #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #include #include #include #include #include #include "pci.h" #if NPCI > 0 #include #endif #include "eisa.h" #if NEISA > 0 #include #include #endif #include "si.h" /* * This device driver is designed to interface the Specialix International * SI, XIO and SX range of serial multiplexor cards to FreeBSD on an ISA, * EISA or PCI bus machine. * * The controller is interfaced to the host via dual port RAM * and an interrupt. * * The code for the Host 1 (very old ISA cards) has not been tested. */ #define POLL /* turn on poller to scan for lost interrupts */ #define REALPOLL /* on each poll, scan for work regardless */ #define POLLHZ (hz/10) /* 10 times per second */ #define SI_I_HIGH_WATER (TTYHOG - 2 * SI_BUFFERSIZE) #define INT_COUNT 25000 /* max of 125 ints per second */ #define JET_INT_COUNT 100 /* max of 100 ints per second */ #define RXINT_COUNT 1 /* one rxint per 10 milliseconds */ enum si_mctl { GET, SET, BIS, BIC }; static void si_command __P((struct si_port *, int, int)); static int si_modem __P((struct si_port *, enum si_mctl, int)); static void si_write_enable __P((struct si_port *, int)); static int si_Sioctl __P((dev_t, u_long, caddr_t, int, struct proc *)); static void si_start __P((struct tty *)); static timeout_t si_lstart; static void si_disc_optim __P((struct tty *tp, struct termios *t, struct si_port *pp)); static void sihardclose __P((struct si_port *pp)); static void sidtrwakeup __P((void *chan)); static int siparam __P((struct tty *, struct termios *)); static int siprobe __P((struct isa_device *id)); static int siattach __P((struct isa_device *id)); static void si_modem_state __P((struct si_port *pp, struct tty *tp, int hi_ip)); static void si_intr __P((int unit)); static char * si_modulename __P((int host_type, int uart_type)); struct isa_driver sidriver = { siprobe, siattach, "si" }; static u_long sipcieisacount = 0; #if NPCI > 0 static char *sipciprobe __P((pcici_t, pcidi_t)); static void sipciattach __P((pcici_t, int)); static struct pci_device sipcidev = { "si", sipciprobe, sipciattach, &sipcieisacount, NULL, }; DATA_SET (pcidevice_set, sipcidev); #endif #if NEISA > 0 static int si_eisa_probe __P((void)); static int si_eisa_attach __P((struct eisa_device *ed)); static struct eisa_driver si_eisa_driver = { "si", si_eisa_probe, si_eisa_attach, NULL, &sipcieisacount, }; DATA_SET(eisadriver_set, si_eisa_driver); #endif static d_open_t siopen; static d_close_t siclose; static d_read_t siread; static d_write_t siwrite; static d_ioctl_t siioctl; static d_stop_t sistop; static d_devtotty_t sidevtotty; -#define CDEV_MAJOR 68 -static struct cdevsw si_cdevsw = - { siopen, siclose, siread, siwrite, /*68*/ - siioctl, sistop, noreset, sidevtotty,/* si */ - ttpoll, nommap, NULL, "si", NULL, -1 }; - +#define CDEV_MAJOR 68 +static struct cdevsw si_cdevsw = { + siopen, siclose, siread, siwrite, + siioctl, sistop, noreset, sidevtotty, + ttpoll, nommap, NULL, "si", + NULL, -1, nodump, nopsize, + D_TTY, +}; #ifdef SI_DEBUG /* use: ``options "SI_DEBUG"'' in your config file */ static void si_dprintf __P((struct si_port *pp, int flags, const char *fmt, ...)); static char *si_mctl2str __P((enum si_mctl cmd)); #define DPRINT(x) si_dprintf x #else #define DPRINT(x) /* void */ #endif static int si_Nports; static int si_Nmodules; static int si_debug = 0; /* data, not bss, so it's patchable */ SYSCTL_INT(_machdep, OID_AUTO, si_debug, CTLFLAG_RW, &si_debug, 0, ""); static struct tty *si_tty; /* where the firmware lives; defined in si2_z280.c and si3_t225.c */ /* old: si2_z280.c */ extern unsigned char si2_z280_download[]; extern unsigned short si2_z280_downloadaddr; extern int si2_z280_dsize; /* new: si3_t225.c */ extern unsigned char si3_t225_download[]; extern unsigned short si3_t225_downloadaddr; extern int si3_t225_dsize; extern unsigned char si3_t225_bootstrap[]; extern unsigned short si3_t225_bootloadaddr; extern int si3_t225_bsize; struct si_softc { int sc_type; /* adapter type */ char *sc_typename; /* adapter type string */ struct si_port *sc_ports; /* port structures for this card */ caddr_t sc_paddr; /* physical addr of iomem */ caddr_t sc_maddr; /* kvaddr of iomem */ int sc_nport; /* # ports on this card */ int sc_irq; /* copy of attach irq */ #if NEISA > 0 int sc_eisa_iobase; /* EISA io port address */ int sc_eisa_irq; /* EISA irq number */ #endif #ifdef DEVFS struct { void *ttya; void *cuaa; void *ttyl; void *cual; void *ttyi; void *cuai; } devfs_token[32]; /* what is the max per card? */ void *control_token; #endif }; static struct si_softc si_softc[NSI]; /* up to 4 elements */ #ifndef B2000 /* not standard, but the hardware knows it. */ # define B2000 2000 #endif static struct speedtab bdrates[] = { B75, CLK75, /* 0x0 */ B110, CLK110, /* 0x1 */ B150, CLK150, /* 0x3 */ B300, CLK300, /* 0x4 */ B600, CLK600, /* 0x5 */ B1200, CLK1200, /* 0x6 */ B2000, CLK2000, /* 0x7 */ B2400, CLK2400, /* 0x8 */ B4800, CLK4800, /* 0x9 */ B9600, CLK9600, /* 0xb */ B19200, CLK19200, /* 0xc */ B38400, CLK38400, /* 0x2 (out of order!) */ B57600, CLK57600, /* 0xd */ B115200, CLK110, /* 0x1 (dupe!, 110 baud on "si") */ -1, -1 }; /* populated with approx character/sec rates - translated at card * initialisation time to chars per tick of the clock */ static int done_chartimes = 0; static struct speedtab chartimes[] = { B75, 8, B110, 11, B150, 15, B300, 30, B600, 60, B1200, 120, B2000, 200, B2400, 240, B4800, 480, B9600, 960, B19200, 1920, B38400, 3840, B57600, 5760, B115200, 11520, -1, -1 }; static volatile int in_intr = 0; /* Inside interrupt handler? */ #ifdef POLL static int si_pollrate; /* in addition to irq */ static int si_realpoll; /* poll HW on timer */ SYSCTL_INT(_machdep, OID_AUTO, si_pollrate, CTLFLAG_RW, &si_pollrate, 0, ""); SYSCTL_INT(_machdep, OID_AUTO, si_realpoll, CTLFLAG_RW, &si_realpoll, 0, ""); static int init_finished = 0; static void si_poll __P((void *)); #endif /* * Array of adapter types and the corresponding RAM size. The order of * entries here MUST match the ordinal of the adapter type. */ static char *si_type[] = { "EMPTY", "SIHOST", "SIMCA", /* FreeBSD does not support Microchannel */ "SIHOST2", "SIEISA", "SIPCI", "SXPCI", "SXISA", }; #if NPCI > 0 static char * sipciprobe(configid, deviceid) pcici_t configid; pcidi_t deviceid; { switch (deviceid) { case 0x400011cb: return("Specialix SI/XIO PCI host card"); break; case 0x200011cb: if (pci_conf_read(configid, SIJETSSIDREG) == 0x020011cb) return("Specialix SX PCI host card"); else return NULL; break; default: return NULL; } /*NOTREACHED*/ } void sipciattach(configid, unit) pcici_t configid; int unit; { struct isa_device id; vm_offset_t vaddr,paddr; u_long mapval = 0; /* shut up gcc, should not be needed */ switch ( pci_conf_read(configid, 0) >> 16 ) { case 0x4000: si_softc[unit].sc_type = SIPCI; mapval = SIPCIBADR; break; case 0x2000: si_softc[unit].sc_type = SIJETPCI; mapval = SIJETBADR; break; } if (!pci_map_mem(configid, mapval, &vaddr, &paddr)) { printf("si%d: couldn't map memory\n", unit); } /* * We're cheating here a little bit. The argument to an ISA * interrupt routine is the unit number. The argument to a * PCI interrupt handler is a void *, but we're simply going * to be lazy and hand it the unit number. */ if (!pci_map_int(configid, (pci_inthand_t *) si_intr, (void *)unit, &tty_imask)) { printf("si%d: couldn't map interrupt\n", unit); } si_softc[unit].sc_typename = si_type[si_softc[unit].sc_type]; /* * More cheating: We're going to dummy up a struct isa_device * and call the other attach routine. We don't really have to * fill in very much of the structure, since we filled in a * little of the soft state already. */ id.id_unit = unit; id.id_maddr = (caddr_t) vaddr; siattach(&id); } #endif #if NEISA > 0 static const char *si_eisa_match __P((eisa_id_t id)); static const char * si_eisa_match(id) eisa_id_t id; { if (id == SIEISADEVID) return ("Specialix SI/XIO EISA host card"); return (NULL); } static int si_eisa_probe(void) { struct eisa_device *ed = NULL; int count, irq; for (count=0; (ed = eisa_match_dev(ed, si_eisa_match)) != NULL; count++) { u_long port,maddr; port = (ed->ioconf.slot * EISA_SLOT_SIZE) + SIEISABASE; eisa_add_iospace(ed, port, SIEISAIOSIZE, RESVADDR_NONE); maddr = (inb(port+1) << 24) | (inb(port) << 16); irq = ((inb(port+2) >> 4) & 0xf); eisa_add_mspace(ed, maddr, SIEISA_MEMSIZE, RESVADDR_NONE); eisa_add_intr(ed, irq); eisa_registerdev(ed, &si_eisa_driver); count++; } return count; } static int si_eisa_attach(ed) struct eisa_device *ed; { struct isa_device id; resvaddr_t *maddr,*iospace; u_int irq; struct si_softc *sc; sc = &si_softc[ed->unit]; sc->sc_type = SIEISA; sc->sc_typename = si_type[sc->sc_type]; if ((iospace = ed->ioconf.ioaddrs.lh_first) == NULL) { printf("si%d: no iospace??\n", ed->unit); return -1; } sc->sc_eisa_iobase = iospace->addr; irq = ((inb(iospace->addr + 2) >> 4) & 0xf); sc->sc_eisa_irq = irq; if ((maddr = ed->ioconf.maddrs.lh_first) == NULL) { printf("si%d: where am I??\n", ed->unit); return -1; } eisa_reg_start(ed); if (eisa_reg_iospace(ed, iospace)) { printf("si%d: failed to register iospace 0x%x\n", ed->unit, iospace); return -1; } if (eisa_reg_mspace(ed, maddr)) { printf("si%d: failed to register memspace 0x%x\n", ed->unit, maddr); return -1; } /* * We're cheating here a little bit. The argument to an ISA * interrupt routine is the unit number. The argument to a * EISA interrupt handler is a void *, but we're simply going * to be lazy and hand it the unit number. */ if (eisa_reg_intr(ed, irq, (void (*)(void *)) si_intr, (void *)(intptr_t)(ed->unit), &tty_imask, 1)) { printf("si%d: failed to register interrupt %d\n", ed->unit, irq); return -1; } eisa_reg_end(ed); if (eisa_enable_intr(ed, irq)) { return -1; } /* * More cheating: We're going to dummy up a struct isa_device * and call the other attach routine. We don't really have to * fill in very much of the structure, since we filled in a * little of the soft state already. */ id.id_unit = ed->unit; id.id_maddr = (caddr_t) pmap_mapdev(maddr->addr, SIEISA_MEMSIZE); return (siattach(&id)); } #endif /* Look for a valid board at the given mem addr */ static int siprobe(id) struct isa_device *id; { struct si_softc *sc; int type; u_int i, ramsize; volatile BYTE was, *ux; volatile unsigned char *maddr; unsigned char *paddr; si_pollrate = POLLHZ; /* default 10 per second */ #ifdef REALPOLL si_realpoll = 1; /* scan always */ #endif maddr = id->id_maddr; /* virtual address... */ paddr = (caddr_t)vtophys(id->id_maddr); /* physical address... */ DPRINT((0, DBG_AUTOBOOT, "si%d: probe at virtual=0x%x physical=0x%x\n", id->id_unit, id->id_maddr, paddr)); /* * this is a lie, but it's easier than trying to handle caching * and ram conflicts in the >1M and <16M region. */ if ((caddr_t)paddr < (caddr_t)IOM_BEGIN || (caddr_t)paddr >= (caddr_t)IOM_END) { printf("si%d: iomem (%p) out of range\n", id->id_unit, (void *)paddr); return(0); } if (id->id_unit >= NSI) { /* THIS IS IMPOSSIBLE */ return(0); } if (((u_int)paddr & 0x7fff) != 0) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: iomem (%x) not on 32k boundary\n", id->id_unit, paddr)); return(0); } if (si_softc[id->id_unit].sc_typename) { /* EISA or PCI has taken this unit, choose another */ for (i=0; i < NSI; i++) { if (si_softc[i].sc_typename == NULL) { id->id_unit = i; break; } } if (i >= NSI) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: cannot realloc unit\n", id->id_unit)); return (0); } } for (i=0; i < NSI; i++) { sc = &si_softc[i]; if ((caddr_t)sc->sc_paddr == (caddr_t)paddr) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: iomem (%x) already configured to si%d\n", id->id_unit, sc->sc_paddr, i)); return(0); } } /* Is there anything out there? (0x17 is just an arbitrary number) */ *maddr = 0x17; if (*maddr != 0x17) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: 0x17 check fail at phys 0x%x\n", id->id_unit, paddr)); fail: return(0); } /* * Let's look first for a JET ISA card, since that's pretty easy * * All jet hosts are supposed to have this string in the IDROM, * but it's not worth checking on self-IDing busses like PCI. */ { unsigned char *jet_chk_str = "JET HOST BY KEV#"; for (i = 0; i < strlen(jet_chk_str); i++) if (jet_chk_str[i] != *(maddr + SIJETIDSTR + 2 * i)) goto try_mk2; } DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: JET first check - 0x%x\n", id->id_unit, (*(maddr+SIJETIDBASE)))); if (*(maddr+SIJETIDBASE) != (SISPLXID&0xff)) goto try_mk2; DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: JET second check - 0x%x\n", id->id_unit, (*(maddr+SIJETIDBASE+2)))); if (*(maddr+SIJETIDBASE+2) != ((SISPLXID&0xff00)>>8)) goto try_mk2; /* It must be a Jet ISA or RIO card */ DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: JET id check - 0x%x\n", id->id_unit, (*(maddr+SIUNIQID)))); if ((*(maddr+SIUNIQID) & 0xf0) !=0x20) goto try_mk2; /* It must be a Jet ISA SI/XIO card */ *(maddr + SIJETCONFIG) = 0; type = SIJETISA; ramsize = SIJET_RAMSIZE; goto got_card; /* * OK, now to see if whatever responded is really an SI card. * Try for a MK II next (SIHOST2) */ try_mk2: for (i = SIPLSIG; i < SIPLSIG + 8; i++) if ((*(maddr+i) & 7) != (~(BYTE)i & 7)) goto try_mk1; /* It must be an SIHOST2 */ *(maddr + SIPLRESET) = 0; *(maddr + SIPLIRQCLR) = 0; *(maddr + SIPLIRQSET) = 0x10; type = SIHOST2; ramsize = SIHOST2_RAMSIZE; goto got_card; /* * Its not a MK II, so try for a MK I (SIHOST) */ try_mk1: *(maddr+SIRESET) = 0x0; /* reset the card */ *(maddr+SIINTCL) = 0x0; /* clear int */ *(maddr+SIRAM) = 0x17; if (*(maddr+SIRAM) != (BYTE)0x17) goto fail; *(maddr+0x7ff8) = 0x17; if (*(maddr+0x7ff8) != (BYTE)0x17) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: 0x17 check fail at phys 0x%x = 0x%x\n", id->id_unit, paddr+0x77f8, *(maddr+0x77f8))); goto fail; } /* It must be an SIHOST (maybe?) - there must be a better way XXX */ type = SIHOST; ramsize = SIHOST_RAMSIZE; got_card: DPRINT((0, DBG_AUTOBOOT, "si%d: found type %d card, try memory test\n", id->id_unit, type)); /* Try the acid test */ ux = maddr + SIRAM; for (i = 0; i < ramsize; i++, ux++) *ux = (BYTE)(i&0xff); ux = maddr + SIRAM; for (i = 0; i < ramsize; i++, ux++) { if ((was = *ux) != (BYTE)(i&0xff)) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: match fail at phys 0x%x, was %x should be %x\n", id->id_unit, paddr + i, was, i&0xff)); goto fail; } } /* clear out the RAM */ ux = maddr + SIRAM; for (i = 0; i < ramsize; i++) *ux++ = 0; ux = maddr + SIRAM; for (i = 0; i < ramsize; i++) { if ((was = *ux++) != 0) { DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: clear fail at phys 0x%x, was %x\n", id->id_unit, paddr + i, was)); goto fail; } } /* * Success, we've found a valid board, now fill in * the adapter structure. */ switch (type) { case SIHOST2: if ((id->id_irq & (IRQ11|IRQ12|IRQ15)) == 0) { bad_irq: DPRINT((0, DBG_AUTOBOOT|DBG_FAIL, "si%d: bad IRQ value - %d\n", id->id_unit, id->id_irq)); return(0); } id->id_msize = SIHOST2_MEMSIZE; break; case SIHOST: if ((id->id_irq & (IRQ11|IRQ12|IRQ15)) == 0) { goto bad_irq; } id->id_msize = SIHOST_MEMSIZE; break; case SIJETISA: if ((id->id_irq & (IRQ9|IRQ10|IRQ11|IRQ12|IRQ15)) == 0) { goto bad_irq; } id->id_msize = SIJETISA_MEMSIZE; break; case SIMCA: /* MCA */ default: printf("si%d: %s not supported\n", id->id_unit, si_type[type]); return(0); } id->id_intr = (inthand2_t *)si_intr; /* set here instead of config */ si_softc[id->id_unit].sc_type = type; si_softc[id->id_unit].sc_typename = si_type[type]; return(-1); /* -1 == found */ } /* * We have to make an 8 bit version of bcopy, since some cards can't * deal with 32 bit I/O */ #if 1 static void si_bcopy(const void *src, void *dst, size_t len) { while (len--) *(((u_char *)dst)++) = *(((u_char *)src)++); } #else #define si_bcopy bcopy #endif /* * Attach the device. Initialize the card. * * This routine also gets called by the EISA and PCI attach routines. * It presumes that the softstate for the unit has had had its type field * and the EISA specific stuff filled in, as well as the kernel virtual * base address and the unit number of the isa_device struct. */ static int siattach(id) struct isa_device *id; { int unit = id->id_unit; struct si_softc *sc = &si_softc[unit]; struct si_port *pp; volatile struct si_channel *ccbp; volatile struct si_reg *regp; volatile caddr_t maddr; struct si_module *modp; struct tty *tp; struct speedtab *spt; int nmodule, nport, x, y; int uart_type; DPRINT((0, DBG_AUTOBOOT, "si%d: siattach\n", id->id_unit)); sc->sc_paddr = (caddr_t)vtophys(id->id_maddr); sc->sc_maddr = id->id_maddr; sc->sc_irq = id->id_irq; DPRINT((0, DBG_AUTOBOOT, "si%d: type: %s paddr: %x maddr: %x\n", unit, sc->sc_typename, sc->sc_paddr, sc->sc_maddr)); sc->sc_ports = NULL; /* mark as uninitialised */ maddr = sc->sc_maddr; /* Stop the CPU first so it won't stomp around while we load */ switch (sc->sc_type) { #if NEISA > 0 case SIEISA: outb(sc->sc_eisa_iobase + 2, sc->sc_eisa_irq << 4); break; #endif #if NPCI > 0 case SIPCI: *(maddr+SIPCIRESET) = 0; break; case SIJETPCI: /* fall through to JET ISA */ #endif case SIJETISA: *(maddr+SIJETCONFIG) = 0; break; case SIHOST2: *(maddr+SIPLRESET) = 0; break; case SIHOST: *(maddr+SIRESET) = 0; break; default: /* this should never happen */ printf("si%d: unsupported configuration\n", unit); return 0; break; } /* OK, now lets download the download code */ if (SI_ISJET(sc->sc_type)) { DPRINT((0, DBG_DOWNLOAD, "si%d: jet_download: nbytes %d\n", id->id_unit, si3_t225_dsize)); si_bcopy(si3_t225_download, maddr + si3_t225_downloadaddr, si3_t225_dsize); DPRINT((0, DBG_DOWNLOAD, "si%d: jet_bootstrap: nbytes %d -> %x\n", id->id_unit, si3_t225_bsize, si3_t225_bootloadaddr)); si_bcopy(si3_t225_bootstrap, maddr + si3_t225_bootloadaddr, si3_t225_bsize); } else { DPRINT((0, DBG_DOWNLOAD, "si%d: si_download: nbytes %d\n", id->id_unit, si2_z280_dsize)); si_bcopy(si2_z280_download, maddr + si2_z280_downloadaddr, si2_z280_dsize); } /* Now start the CPU */ switch (sc->sc_type) { #if NEISA > 0 case SIEISA: /* modify the download code to tell it that it's on an EISA */ *(maddr + 0x42) = 1; outb(sc->sc_eisa_iobase + 2, (sc->sc_eisa_irq << 4) | 4); (void)inb(sc->sc_eisa_iobase + 3); /* reset interrupt */ break; #endif case SIPCI: /* modify the download code to tell it that it's on a PCI */ *(maddr+0x42) = 1; *(maddr+SIPCIRESET) = 1; *(maddr+SIPCIINTCL) = 0; break; case SIJETPCI: *(maddr+SIJETRESET) = 0; *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN; break; case SIJETISA: *(maddr+SIJETRESET) = 0; switch (sc->sc_irq) { case IRQ9: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0x90; break; case IRQ10: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xa0; break; case IRQ11: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xb0; break; case IRQ12: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xc0; break; case IRQ15: *(maddr+SIJETCONFIG) = SIJETBUSEN|SIJETIRQEN|0xf0; break; } break; case SIHOST: *(maddr+SIRESET_CL) = 0; *(maddr+SIINTCL_CL) = 0; break; case SIHOST2: *(maddr+SIPLRESET) = 0x10; switch (sc->sc_irq) { case IRQ11: *(maddr+SIPLIRQ11) = 0x10; break; case IRQ12: *(maddr+SIPLIRQ12) = 0x10; break; case IRQ15: *(maddr+SIPLIRQ15) = 0x10; break; } *(maddr+SIPLIRQCLR) = 0x10; break; default: /* this should _REALLY_ never happen */ printf("si%d: Uh, it was supported a second ago...\n", unit); return 0; } DELAY(1000000); /* wait around for a second */ regp = (struct si_reg *)maddr; y = 0; /* wait max of 5 sec for init OK */ while (regp->initstat == 0 && y++ < 10) { DELAY(500000); } switch (regp->initstat) { case 0: printf("si%d: startup timeout - aborting\n", unit); sc->sc_type = SIEMPTY; return 0; case 1: if (SI_ISJET(sc->sc_type)) { /* set throttle to 100 times per second */ regp->int_count = JET_INT_COUNT; /* rx_intr_count is a NOP in Jet */ } else { /* set throttle to 125 times per second */ regp->int_count = INT_COUNT; /* rx intr max of 25 times per second */ regp->rx_int_count = RXINT_COUNT; } regp->int_pending = 0; /* no intr pending */ regp->int_scounter = 0; /* reset counter */ break; case 0xff: /* * No modules found, so give up on this one. */ printf("si%d: %s - no ports found\n", unit, si_type[sc->sc_type]); return 0; default: printf("si%d: download code version error - initstat %x\n", unit, regp->initstat); return 0; } /* * First time around the ports just count them in order * to allocate some memory. */ nport = 0; modp = (struct si_module *)(maddr + 0x80); for (;;) { DPRINT((0, DBG_DOWNLOAD, "si%d: ccb addr 0x%x\n", unit, modp)); switch (modp->sm_type) { case TA4: DPRINT((0, DBG_DOWNLOAD, "si%d: Found old TA4 module, 4 ports\n", unit)); x = 4; break; case TA8: DPRINT((0, DBG_DOWNLOAD, "si%d: Found old TA8 module, 8 ports\n", unit)); x = 8; break; case TA4_ASIC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found ASIC TA4 module, 4 ports\n", unit)); x = 4; break; case TA8_ASIC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found ASIC TA8 module, 8 ports\n", unit)); x = 8; break; case MTA: DPRINT((0, DBG_DOWNLOAD, "si%d: Found CD1400 module, 8 ports\n", unit)); x = 8; break; case SXDC: DPRINT((0, DBG_DOWNLOAD, "si%d: Found SXDC module, 8 ports\n", unit)); x = 8; break; default: printf("si%d: unknown module type %d\n", unit, modp->sm_type); goto try_next; } /* this was limited in firmware and is also a driver issue */ if ((nport + x) > SI_MAXPORTPERCARD) { printf("si%d: extra ports ignored\n", unit); goto try_next; } nport += x; si_Nports += x; si_Nmodules++; try_next: if (modp->sm_next == 0) break; modp = (struct si_module *) (maddr + (unsigned)(modp->sm_next & 0x7fff)); } sc->sc_ports = (struct si_port *)malloc(sizeof(struct si_port) * nport, M_DEVBUF, M_NOWAIT); if (sc->sc_ports == 0) { mem_fail: printf("si%d: fail to malloc memory for port structs\n", unit); return 0; } bzero(sc->sc_ports, sizeof(struct si_port) * nport); sc->sc_nport = nport; /* * allocate tty structures for ports */ tp = (struct tty *)malloc(sizeof(*tp) * nport, M_DEVBUF, M_NOWAIT); if (tp == 0) goto mem_fail; bzero(tp, sizeof(*tp) * nport); si_tty = tp; /* * Scan round the ports again, this time initialising. */ pp = sc->sc_ports; nmodule = 0; modp = (struct si_module *)(maddr + 0x80); uart_type = 1000; /* arbitary, > uchar_max */ for (;;) { switch (modp->sm_type) { case TA4: nport = 4; break; case TA8: nport = 8; break; case TA4_ASIC: nport = 4; break; case TA8_ASIC: nport = 8; break; case MTA: nport = 8; break; case SXDC: nport = 8; break; default: goto try_next2; } nmodule++; ccbp = (struct si_channel *)((char *)modp + 0x100); if (uart_type == 1000) uart_type = ccbp->type; else if (uart_type != ccbp->type) printf("si%d: Warning: module %d mismatch! (%d%s != %d%s)\n", unit, nmodule, ccbp->type, si_modulename(sc->sc_type, ccbp->type), uart_type, si_modulename(sc->sc_type, uart_type)); for (x = 0; x < nport; x++, pp++, ccbp++) { pp->sp_ccb = ccbp; /* save the address */ pp->sp_tty = tp++; pp->sp_pend = IDLE_CLOSE; pp->sp_state = 0; /* internal flag */ pp->sp_dtr_wait = 3 * hz; pp->sp_iin.c_iflag = TTYDEF_IFLAG; pp->sp_iin.c_oflag = TTYDEF_OFLAG; pp->sp_iin.c_cflag = TTYDEF_CFLAG; pp->sp_iin.c_lflag = TTYDEF_LFLAG; termioschars(&pp->sp_iin); pp->sp_iin.c_ispeed = pp->sp_iin.c_ospeed = TTYDEF_SPEED;; pp->sp_iout = pp->sp_iin; } try_next2: if (modp->sm_next == 0) { printf("si%d: card: %s, ports: %d, modules: %d, type: %d%s\n", unit, sc->sc_typename, sc->sc_nport, nmodule, uart_type, si_modulename(sc->sc_type, uart_type)); break; } modp = (struct si_module *) (maddr + (unsigned)(modp->sm_next & 0x7fff)); } if (done_chartimes == 0) { for (spt = chartimes ; spt->sp_speed != -1; spt++) { if ((spt->sp_code /= hz) == 0) spt->sp_code = 1; } done_chartimes = 1; } #ifdef DEVFS /* path name devsw minor type uid gid perm*/ for ( x = 0; x < sc->sc_nport; x++ ) { /* sync with the manuals that start at 1 */ y = x + 1 + id->id_unit * (1 << SI_CARDSHIFT); sc->devfs_token[x].ttya = devfs_add_devswf( &si_cdevsw, x, DV_CHR, 0, 0, 0600, "ttyA%02d", y); sc->devfs_token[x].cuaa = devfs_add_devswf( &si_cdevsw, x + 0x00080, DV_CHR, 0, 0, 0600, "cuaA%02d", y); sc->devfs_token[x].ttyi = devfs_add_devswf( &si_cdevsw, x + 0x10000, DV_CHR, 0, 0, 0600, "ttyiA%02d", y); sc->devfs_token[x].cuai = devfs_add_devswf( &si_cdevsw, x + 0x10080, DV_CHR, 0, 0, 0600, "cuaiA%02d", y); sc->devfs_token[x].ttyl = devfs_add_devswf( &si_cdevsw, x + 0x20000, DV_CHR, 0, 0, 0600, "ttylA%02d", y); sc->devfs_token[x].cual = devfs_add_devswf( &si_cdevsw, x + 0x20080, DV_CHR, 0, 0, 0600, "cualA%02d", y); } sc->control_token = devfs_add_devswf(&si_cdevsw, 0x40000, DV_CHR, 0, 0, 0600, "si_control"); #endif return (1); } static int siopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { int oldspl, error; int card, port; register struct si_softc *sc; register struct tty *tp; volatile struct si_channel *ccbp; struct si_port *pp; int mynor = minor(dev); /* quickly let in /dev/si_control */ if (IS_CONTROLDEV(mynor)) { if ((error = suser(p->p_ucred, &p->p_acflag))) return(error); return(0); } card = SI_CARD(mynor); if (card >= NSI) return (ENXIO); sc = &si_softc[card]; if (sc->sc_type == SIEMPTY) { DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: type %s??\n", card, sc->sc_typename)); return(ENXIO); } port = SI_PORT(mynor); if (port >= sc->sc_nport) { DPRINT((0, DBG_OPEN|DBG_FAIL, "si%d: nports %d\n", card, sc->sc_nport)); return(ENXIO); } #ifdef POLL /* * We've now got a device, so start the poller. */ if (init_finished == 0) { timeout(si_poll, (caddr_t)0L, si_pollrate); init_finished = 1; } #endif /* initial/lock device */ if (IS_STATE(mynor)) { return(0); } pp = sc->sc_ports + port; tp = pp->sp_tty; /* the "real" tty */ ccbp = pp->sp_ccb; /* Find control block */ DPRINT((pp, DBG_ENTRY|DBG_OPEN, "siopen(%x,%x,%x,%x)\n", dev, flag, mode, p)); oldspl = spltty(); /* Keep others out */ error = 0; open_top: while (pp->sp_state & SS_DTR_OFF) { error = tsleep(&pp->sp_dtr_wait, TTIPRI|PCATCH, "sidtr", 0); if (error != 0) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialised. * handle conflicts. */ if (IS_CALLOUT(mynor)) { if (!pp->sp_active_out) { error = EBUSY; goto out; } } else { if (pp->sp_active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&pp->sp_active_out, TTIPRI|PCATCH, "sibi", 0); if (error != 0) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { DPRINT((pp, DBG_OPEN|DBG_FAIL, "already open and EXCLUSIVE set\n")); error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Avoid sleep... :-) */ DPRINT((pp, DBG_OPEN, "first open\n")); tp->t_oproc = si_start; tp->t_param = siparam; tp->t_dev = dev; tp->t_termios = mynor & SI_CALLOUT_MASK ? pp->sp_iout : pp->sp_iin; (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); ++pp->sp_wopeners; /* in case of sleep in siparam */ error = siparam(tp, &tp->t_termios); --pp->sp_wopeners; if (error != 0) goto out; /* XXX: we should goto_top if siparam slept */ - ttsetwater(tp); - /* set initial DCD state */ pp->sp_last_hi_ip = ccbp->hi_ip; if ((pp->sp_last_hi_ip & IP_DCD) || IS_CALLOUT(mynor)) { (*linesw[tp->t_line].l_modem)(tp, 1); } } /* whoops! we beat the close! */ if (pp->sp_state & SS_CLOSING) { /* try and stop it from proceeding to bash the hardware */ pp->sp_state &= ~SS_CLOSING; } /* * Wait for DCD if necessary */ if (!(tp->t_state & TS_CARR_ON) && !IS_CALLOUT(mynor) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++pp->sp_wopeners; DPRINT((pp, DBG_OPEN, "sleeping for carrier\n")); error = tsleep(TSA_CARR_ON(tp), TTIPRI|PCATCH, "sidcd", 0); --pp->sp_wopeners; if (error != 0) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); si_disc_optim(tp, &tp->t_termios, pp); if (tp->t_state & TS_ISOPEN && IS_CALLOUT(mynor)) pp->sp_active_out = TRUE; pp->sp_state |= SS_OPEN; /* made it! */ out: splx(oldspl); DPRINT((pp, DBG_OPEN, "leaving siopen\n")); if (!(tp->t_state & TS_ISOPEN) && pp->sp_wopeners == 0) sihardclose(pp); return(error); } static int siclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register struct si_port *pp; register struct tty *tp; int oldspl; int error = 0; int mynor = minor(dev); if (IS_SPECIAL(mynor)) return(0); oldspl = spltty(); pp = MINOR2PP(mynor); tp = pp->sp_tty; DPRINT((pp, DBG_ENTRY|DBG_CLOSE, "siclose(%x,%x,%x,%x) sp_state:%x\n", dev, flag, mode, p, pp->sp_state)); /* did we sleep and loose a race? */ if (pp->sp_state & SS_CLOSING) { /* error = ESOMETING? */ goto out; } /* begin race detection.. */ pp->sp_state |= SS_CLOSING; si_write_enable(pp, 0); /* block writes for ttywait() */ /* THIS MAY SLEEP IN TTYWAIT!!! */ (*linesw[tp->t_line].l_close)(tp, flag); si_write_enable(pp, 1); /* did we sleep and somebody started another open? */ if (!(pp->sp_state & SS_CLOSING)) { /* error = ESOMETING? */ goto out; } /* ok. we are now still on the right track.. nuke the hardware */ if (pp->sp_state & SS_LSTART) { untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); pp->sp_state &= ~SS_LSTART; } sistop(tp, FREAD | FWRITE); sihardclose(pp); ttyclose(tp); pp->sp_state &= ~SS_OPEN; out: DPRINT((pp, DBG_CLOSE|DBG_EXIT, "close done, returning\n")); splx(oldspl); return(error); } static void sihardclose(pp) struct si_port *pp; { int oldspl; struct tty *tp; volatile struct si_channel *ccbp; oldspl = spltty(); tp = pp->sp_tty; ccbp = pp->sp_ccb; /* Find control block */ if (tp->t_cflag & HUPCL || (!pp->sp_active_out && !(ccbp->hi_ip & IP_DCD) && !(pp->sp_iin.c_cflag && CLOCAL)) || !(tp->t_state & TS_ISOPEN)) { (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); (void) si_command(pp, FCLOSE, SI_NOWAIT); if (pp->sp_dtr_wait != 0) { timeout(sidtrwakeup, pp, pp->sp_dtr_wait); pp->sp_state |= SS_DTR_OFF; } } pp->sp_active_out = FALSE; wakeup((caddr_t)&pp->sp_active_out); wakeup(TSA_CARR_ON(tp)); splx(oldspl); } /* * called at splsoftclock()... */ static void sidtrwakeup(chan) void *chan; { struct si_port *pp; int oldspl; oldspl = spltty(); pp = (struct si_port *)chan; pp->sp_state &= ~SS_DTR_OFF; wakeup(&pp->sp_dtr_wait); splx(oldspl); } /* * User level stuff - read and write */ static int siread(dev, uio, flag) register dev_t dev; struct uio *uio; int flag; { register struct tty *tp; int mynor = minor(dev); if (IS_SPECIAL(mynor)) { DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_READ, "siread(CONTROLDEV!!)\n")); return(ENODEV); } tp = MINOR2TP(mynor); DPRINT((TP2PP(tp), DBG_ENTRY|DBG_READ, "siread(%x,%x,%x)\n", dev, uio, flag)); return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { register struct si_port *pp; register struct tty *tp; int error = 0; int mynor = minor(dev); int oldspl; if (IS_SPECIAL(mynor)) { DPRINT((0, DBG_ENTRY|DBG_FAIL|DBG_WRITE, "siwrite(CONTROLDEV!!)\n")); return(ENODEV); } pp = MINOR2PP(mynor); tp = pp->sp_tty; DPRINT((pp, DBG_WRITE, "siwrite(%x,%x,%x)\n", dev, uio, flag)); oldspl = spltty(); /* * If writes are currently blocked, wait on the "real" tty */ while (pp->sp_state & SS_BLOCKWRITE) { pp->sp_state |= SS_WAITWRITE; DPRINT((pp, DBG_WRITE, "in siwrite, wait for SS_BLOCKWRITE to clear\n")); if ((error = ttysleep(tp, (caddr_t)pp, TTOPRI|PCATCH, "siwrite", tp->t_timeout))) { if (error == EWOULDBLOCK) error = EIO; goto out; } } error = (*linesw[tp->t_line].l_write)(tp, uio, flag); out: splx(oldspl); return (error); } static struct tty * sidevtotty(dev_t dev) { struct si_port *pp; int mynor = minor(dev); struct si_softc *sc = &si_softc[SI_CARD(mynor)]; if (IS_SPECIAL(mynor)) return(NULL); if (SI_PORT(mynor) >= sc->sc_nport) return(NULL); pp = MINOR2PP(mynor); return (pp->sp_tty); } static int siioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct si_port *pp; register struct tty *tp; int error; int mynor = minor(dev); int oldspl; int blocked = 0; #if defined(COMPAT_43) u_long oldcmd; struct termios term; #endif if (IS_SI_IOCTL(cmd)) return(si_Sioctl(dev, cmd, data, flag, p)); pp = MINOR2PP(mynor); tp = pp->sp_tty; DPRINT((pp, DBG_ENTRY|DBG_IOCTL, "siioctl(%x,%lx,%x,%x)\n", dev, cmd, data, flag)); if (IS_STATE(mynor)) { struct termios *ct; switch (mynor & SI_STATE_MASK) { case SI_INIT_STATE_MASK: ct = IS_CALLOUT(mynor) ? &pp->sp_iout : &pp->sp_iin; break; case SI_LOCK_STATE_MASK: ct = IS_CALLOUT(mynor) ? &pp->sp_lout : &pp->sp_lin; break; default: return (ENODEV); } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); default: return (ENOTTY); } } /* * Do the old-style ioctl compat routines... */ #if defined(COMPAT_43) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif /* * Do the initial / lock state business */ if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & SI_CALLOUT_MASK ? &pp->sp_lout : &pp->sp_lin; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } /* * Block user-level writes to give the ttywait() * a chance to completely drain for commands * that require the port to be in a quiescent state. */ switch (cmd) { case TIOCSETAW: case TIOCSETAF: case TIOCDRAIN: #ifdef COMPAT_43 case TIOCSETP: #endif blocked++; /* block writes for ttywait() and siparam() */ si_write_enable(pp, 0); } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) goto out; oldspl = spltty(); error = ttioctl(tp, cmd, data, flag); si_disc_optim(tp, &tp->t_termios, pp); if (error != ENOIOCTL) goto outspl; switch (cmd) { case TIOCSBRK: si_command(pp, SBREAK, SI_WAIT); break; case TIOCCBRK: si_command(pp, EBREAK, SI_WAIT); break; case TIOCSDTR: (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); break; case TIOCCDTR: (void) si_modem(pp, SET, 0); break; case TIOCMSET: (void) si_modem(pp, SET, *(int *)data); break; case TIOCMBIS: (void) si_modem(pp, BIS, *(int *)data); break; case TIOCMBIC: (void) si_modem(pp, BIC, *(int *)data); break; case TIOCMGET: *(int *)data = si_modem(pp, GET, 0); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { goto outspl; } pp->sp_dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = pp->sp_dtr_wait * 100 / hz; break; default: error = ENOTTY; } error = 0; outspl: splx(oldspl); out: DPRINT((pp, DBG_IOCTL|DBG_EXIT, "siioctl ret %d\n", error)); if (blocked) si_write_enable(pp, 1); return(error); } /* * Handle the Specialix ioctls. All MUST be called via the CONTROL device */ static int si_Sioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct si_softc *xsc; register struct si_port *xpp; volatile struct si_reg *regp; struct si_tcsi *dp; struct si_pstat *sps; int *ip, error = 0; int oldspl; int card, port; int mynor = minor(dev); DPRINT((0, DBG_ENTRY|DBG_IOCTL, "si_Sioctl(%x,%lx,%x,%x)\n", dev, cmd, data, flag)); #if 1 DPRINT((0, DBG_IOCTL, "TCSI_PORT=%x\n", TCSI_PORT)); DPRINT((0, DBG_IOCTL, "TCSI_CCB=%x\n", TCSI_CCB)); DPRINT((0, DBG_IOCTL, "TCSI_TTY=%x\n", TCSI_TTY)); #endif if (!IS_CONTROLDEV(mynor)) { DPRINT((0, DBG_IOCTL|DBG_FAIL, "not called from control device!\n")); return(ENODEV); } oldspl = spltty(); /* better safe than sorry */ ip = (int *)data; #define SUCHECK if ((error = suser(p->p_ucred, &p->p_acflag))) goto out switch (cmd) { case TCSIPORTS: *ip = si_Nports; goto out; case TCSIMODULES: *ip = si_Nmodules; goto out; case TCSISDBG_ALL: SUCHECK; si_debug = *ip; goto out; case TCSIGDBG_ALL: *ip = si_debug; goto out; default: /* * Check that a controller for this port exists */ /* may also be a struct si_pstat, a superset of si_tcsi */ dp = (struct si_tcsi *)data; sps = (struct si_pstat *)data; card = dp->tc_card; xsc = &si_softc[card]; /* check.. */ if (card < 0 || card >= NSI || xsc->sc_type == SIEMPTY) { error = ENOENT; goto out; } /* * And check that a port exists */ port = dp->tc_port; if (port < 0 || port >= xsc->sc_nport) { error = ENOENT; goto out; } xpp = xsc->sc_ports + port; regp = (struct si_reg *)xsc->sc_maddr; } switch (cmd) { case TCSIDEBUG: #ifdef SI_DEBUG SUCHECK; if (xpp->sp_debug) xpp->sp_debug = 0; else { xpp->sp_debug = DBG_ALL; DPRINT((xpp, DBG_IOCTL, "debug toggled %s\n", (xpp->sp_debug&DBG_ALL)?"ON":"OFF")); } break; #else error = ENODEV; goto out; #endif case TCSISDBG_LEVEL: case TCSIGDBG_LEVEL: #ifdef SI_DEBUG if (cmd == TCSIGDBG_LEVEL) { dp->tc_dbglvl = xpp->sp_debug; } else { SUCHECK; xpp->sp_debug = dp->tc_dbglvl; } break; #else error = ENODEV; goto out; #endif case TCSIGRXIT: dp->tc_int = regp->rx_int_count; break; case TCSIRXIT: SUCHECK; regp->rx_int_count = dp->tc_int; break; case TCSIGIT: dp->tc_int = regp->int_count; break; case TCSIIT: SUCHECK; regp->int_count = dp->tc_int; break; case TCSISTATE: dp->tc_int = xpp->sp_ccb->hi_ip; break; /* these next three use a different structure */ case TCSI_PORT: SUCHECK; si_bcopy(xpp, &sps->tc_siport, sizeof(sps->tc_siport)); break; case TCSI_CCB: SUCHECK; si_bcopy((char *)xpp->sp_ccb, &sps->tc_ccb, sizeof(sps->tc_ccb)); break; case TCSI_TTY: SUCHECK; si_bcopy(xpp->sp_tty, &sps->tc_tty, sizeof(sps->tc_tty)); break; default: error = EINVAL; goto out; } out: splx(oldspl); return(error); /* success */ } /* * siparam() : Configure line params * called at spltty(); * this may sleep, does not flush, nor wait for drain, nor block writes * caller must arrange this if it's important.. */ static int siparam(tp, t) register struct tty *tp; register struct termios *t; { register struct si_port *pp = TP2PP(tp); volatile struct si_channel *ccbp; int oldspl, cflag, iflag, oflag, lflag; int error = 0; /* shutup gcc */ int ispeed = 0; /* shutup gcc */ int ospeed = 0; /* shutup gcc */ BYTE val; DPRINT((pp, DBG_ENTRY|DBG_PARAM, "siparam(%x,%x)\n", tp, t)); cflag = t->c_cflag; iflag = t->c_iflag; oflag = t->c_oflag; lflag = t->c_lflag; DPRINT((pp, DBG_PARAM, "OFLAG 0x%x CFLAG 0x%x IFLAG 0x%x LFLAG 0x%x\n", oflag, cflag, iflag, lflag)); /* XXX - if Jet host and SXDC module, use extended baud rates */ /* if not hung up.. */ if (t->c_ospeed != 0) { /* translate baud rate to firmware values */ ospeed = ttspeedtab(t->c_ospeed, bdrates); ispeed = t->c_ispeed ? ttspeedtab(t->c_ispeed, bdrates) : ospeed; /* enforce legit baud rate */ if (ospeed < 0 || ispeed < 0) return (EINVAL); } oldspl = spltty(); ccbp = pp->sp_ccb; /* ========== set hi_break ========== */ val = 0; if (iflag & IGNBRK) /* Breaks */ val |= BR_IGN; if (iflag & BRKINT) /* Interrupt on break? */ val |= BR_INT; if (iflag & PARMRK) /* Parity mark? */ val |= BR_PARMRK; if (iflag & IGNPAR) /* Ignore chars with parity errors? */ val |= BR_PARIGN; ccbp->hi_break = val; /* ========== set hi_csr ========== */ /* if not hung up.. */ if (t->c_ospeed != 0) { /* Set I/O speeds */ val = (ispeed << 4) | ospeed; } ccbp->hi_csr = val; /* ========== set hi_mr2 ========== */ val = 0; if (cflag & CSTOPB) /* Stop bits */ val |= MR2_2_STOP; else val |= MR2_1_STOP; /* * Enable H/W RTS/CTS handshaking. The default TA/MTA is * a DCE, hence the reverse sense of RTS and CTS */ /* Output Flow - RTS must be raised before data can be sent */ if (cflag & CCTS_OFLOW) val |= MR2_RTSCONT; ccbp->hi_mr2 = val; /* ========== set hi_mr1 ========== */ val = 0; if (!(cflag & PARENB)) /* Parity */ val |= MR1_NONE; else val |= MR1_WITH; if (cflag & PARODD) val |= MR1_ODD; if ((cflag & CS8) == CS8) { /* 8 data bits? */ val |= MR1_8_BITS; } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ val |= MR1_7_BITS; } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ val |= MR1_6_BITS; } else { /* Must be 5 */ val |= MR1_5_BITS; } /* * Enable H/W RTS/CTS handshaking. The default TA/MTA is * a DCE, hence the reverse sense of RTS and CTS */ /* Input Flow - CTS is raised when port is ready to receive data */ if (cflag & CRTS_IFLOW) val |= MR1_CTSCONT; ccbp->hi_mr1 = val; /* ========== set hi_mask ========== */ val = 0xff; if ((cflag & CS8) == CS8) { /* 8 data bits? */ val &= 0xFF; } else if ((cflag & CS7) == CS7) { /* 7 data bits? */ val &= 0x7F; } else if ((cflag & CS6) == CS6) { /* 6 data bits? */ val &= 0x3F; } else { /* Must be 5 */ val &= 0x1F; } if (iflag & ISTRIP) val &= 0x7F; ccbp->hi_mask = val; /* ========== set hi_prtcl ========== */ val = 0; /* Monitor DCD etc. if a modem */ if (!(cflag & CLOCAL)) val |= SP_DCEN; if (iflag & IXANY) val |= SP_TANY; if (iflag & IXON) val |= SP_TXEN; if (iflag & IXOFF) val |= SP_RXEN; if (iflag & INPCK) val |= SP_PAEN; ccbp->hi_prtcl = val; /* ========== set hi_{rx|tx}{on|off} ========== */ /* XXX: the card TOTALLY shields us from the flow control... */ ccbp->hi_txon = t->c_cc[VSTART]; ccbp->hi_txoff = t->c_cc[VSTOP]; ccbp->hi_rxon = t->c_cc[VSTART]; ccbp->hi_rxoff = t->c_cc[VSTOP]; /* ========== send settings to the card ========== */ /* potential sleep here */ if (ccbp->hi_stat == IDLE_CLOSE) /* Not yet open */ si_command(pp, LOPEN, SI_WAIT); /* open it */ else si_command(pp, CONFIG, SI_WAIT); /* change params */ /* ========== set DTR etc ========== */ /* Hangup if ospeed == 0 */ if (t->c_ospeed == 0) { (void) si_modem(pp, BIC, TIOCM_DTR|TIOCM_RTS); } else { /* * If the previous speed was 0, may need to re-enable * the modem signals */ (void) si_modem(pp, SET, TIOCM_DTR|TIOCM_RTS); } DPRINT((pp, DBG_PARAM, "siparam, complete: MR1 %x MR2 %x HI_MASK %x PRTCL %x HI_BREAK %x\n", ccbp->hi_mr1, ccbp->hi_mr2, ccbp->hi_mask, ccbp->hi_prtcl, ccbp->hi_break)); splx(oldspl); return(error); } /* * Enable or Disable the writes to this channel... * "state" -> enabled = 1; disabled = 0; */ static void si_write_enable(pp, state) register struct si_port *pp; int state; { int oldspl; oldspl = spltty(); if (state) { pp->sp_state &= ~SS_BLOCKWRITE; if (pp->sp_state & SS_WAITWRITE) { pp->sp_state &= ~SS_WAITWRITE; /* thunder away! */ wakeup((caddr_t)pp); } } else { pp->sp_state |= SS_BLOCKWRITE; } splx(oldspl); } /* * Set/Get state of modem control lines. * Due to DCE-like behaviour of the adapter, some signals need translation: * TIOCM_DTR DSR * TIOCM_RTS CTS */ static int si_modem(pp, cmd, bits) struct si_port *pp; enum si_mctl cmd; int bits; { volatile struct si_channel *ccbp; int x; DPRINT((pp, DBG_ENTRY|DBG_MODEM, "si_modem(%x,%s,%x)\n", pp, si_mctl2str(cmd), bits)); ccbp = pp->sp_ccb; /* Find channel address */ switch (cmd) { case GET: x = ccbp->hi_ip; bits = TIOCM_LE; if (x & IP_DCD) bits |= TIOCM_CAR; if (x & IP_DTR) bits |= TIOCM_DTR; if (x & IP_RTS) bits |= TIOCM_RTS; if (x & IP_RI) bits |= TIOCM_RI; return(bits); case SET: ccbp->hi_op &= ~(OP_DSR|OP_CTS); /* fall through */ case BIS: x = 0; if (bits & TIOCM_DTR) x |= OP_DSR; if (bits & TIOCM_RTS) x |= OP_CTS; ccbp->hi_op |= x; break; case BIC: if (bits & TIOCM_DTR) ccbp->hi_op &= ~OP_DSR; if (bits & TIOCM_RTS) ccbp->hi_op &= ~OP_CTS; } return 0; } /* * Handle change of modem state */ static void si_modem_state(pp, tp, hi_ip) register struct si_port *pp; register struct tty *tp; register int hi_ip; { /* if a modem dev */ if (hi_ip & IP_DCD) { if ( !(pp->sp_last_hi_ip & IP_DCD)) { DPRINT((pp, DBG_INTR, "modem carr on t_line %d\n", tp->t_line)); (void)(*linesw[tp->t_line].l_modem)(tp, 1); } } else { if (pp->sp_last_hi_ip & IP_DCD) { DPRINT((pp, DBG_INTR, "modem carr off\n")); if ((*linesw[tp->t_line].l_modem)(tp, 0)) (void) si_modem(pp, SET, 0); } } pp->sp_last_hi_ip = hi_ip; } /* * Poller to catch missed interrupts. * * Note that the SYSV Specialix drivers poll at 100 times per second to get * better response. We could really use a "periodic" version timeout(). :-) */ #ifdef POLL static void si_poll(void *nothing) { register struct si_softc *sc; register int i; volatile struct si_reg *regp; register struct si_port *pp; int lost, oldspl, port; DPRINT((0, DBG_POLL, "si_poll()\n")); oldspl = spltty(); if (in_intr) goto out; lost = 0; for (i=0; isc_type == SIEMPTY) continue; regp = (struct si_reg *)sc->sc_maddr; /* * See if there has been a pending interrupt for 2 seconds * or so. The test (int_scounter >= 200) won't correspond * to 2 seconds if int_count gets changed. */ if (regp->int_pending != 0) { if (regp->int_scounter >= 200 && regp->initstat == 1) { printf("si%d: lost intr\n", i); lost++; } } else { regp->int_scounter = 0; } /* * gripe about no input flow control.. */ pp = sc->sc_ports; for (port = 0; port < sc->sc_nport; pp++, port++) { if (pp->sp_delta_overflows > 0) { printf("si%d: %d tty level buffer overflows\n", i, pp->sp_delta_overflows); pp->sp_delta_overflows = 0; } } } if (lost || si_realpoll) si_intr(-1); /* call intr with fake vector */ out: splx(oldspl); timeout(si_poll, (caddr_t)0L, si_pollrate); } #endif /* ifdef POLL */ /* * The interrupt handler polls ALL ports on ALL adapters each time * it is called. */ static BYTE si_rxbuf[SI_BUFFERSIZE]; /* input staging area */ static BYTE si_txbuf[SI_BUFFERSIZE]; /* output staging area */ static void si_intr(int unit) { register struct si_softc *sc; register struct si_port *pp; volatile struct si_channel *ccbp; register struct tty *tp; volatile caddr_t maddr; BYTE op, ip; int x, card, port, n, i, isopen; volatile BYTE *z; BYTE c; DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "si_intr(%d)\n", unit)); if (in_intr) { if (unit < 0) /* should never happen */ printf("si%d: Warning poll entered during interrupt\n", unit); else printf("si%d: Warning interrupt handler re-entered\n", unit); return; } in_intr = 1; /* * When we get an int we poll all the channels and do ALL pending * work, not just the first one we find. This allows all cards to * share the same vector. * * XXX - But if we're sharing the vector with something that's NOT * a SI/XIO/SX card, we may be making more work for ourselves. */ for (card = 0; card < NSI; card++) { sc = &si_softc[card]; if (sc->sc_type == SIEMPTY) continue; /* * First, clear the interrupt */ switch(sc->sc_type) { case SIHOST: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; /* flag nothing pending */ *(maddr+SIINTCL) = 0x00; /* Set IRQ clear */ *(maddr+SIINTCL_CL) = 0x00; /* Clear IRQ clear */ break; case SIHOST2: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPLIRQCLR) = 0x00; *(maddr+SIPLIRQCLR) = 0x10; break; #if NPCI > 0 case SIPCI: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIPCIINTCL) = 0x0; break; case SIJETPCI: /* fall through to JETISA case */ #endif case SIJETISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; *(maddr+SIJETINTCL) = 0x0; break; #if NEISA > 0 case SIEISA: maddr = sc->sc_maddr; ((volatile struct si_reg *)maddr)->int_pending = 0; (void)inb(sc->sc_eisa_iobase + 3); break; #endif case SIEMPTY: default: continue; } ((volatile struct si_reg *)maddr)->int_scounter = 0; /* * check each port */ for (pp = sc->sc_ports, port=0; port < sc->sc_nport; pp++, port++) { ccbp = pp->sp_ccb; tp = pp->sp_tty; /* * See if a command has completed ? */ if (ccbp->hi_stat != pp->sp_pend) { DPRINT((pp, DBG_INTR, "si_intr hi_stat = 0x%x, pend = %d\n", ccbp->hi_stat, pp->sp_pend)); switch(pp->sp_pend) { case LOPEN: case MPEND: case MOPEN: case CONFIG: case SBREAK: case EBREAK: pp->sp_pend = ccbp->hi_stat; /* sleeping in si_command */ wakeup(&pp->sp_state); break; default: pp->sp_pend = ccbp->hi_stat; } } /* * Continue on if it's closed */ if (ccbp->hi_stat == IDLE_CLOSE) { continue; } /* * Do modem state change if not a local device */ si_modem_state(pp, tp, ccbp->hi_ip); /* * Check to see if we should 'receive' characters. */ if (tp->t_state & TS_CONNECTED && tp->t_state & TS_ISOPEN) isopen = 1; else isopen = 0; /* * Do input break processing */ if (ccbp->hi_state & ST_BREAK) { if (isopen) { (*linesw[tp->t_line].l_rint)(TTY_BI, tp); } ccbp->hi_state &= ~ST_BREAK; /* A Bit iffy this */ DPRINT((pp, DBG_INTR, "si_intr break\n")); } /* * Do RX stuff - if not open then dump any characters. * XXX: This is VERY messy and needs to be cleaned up. * * XXX: can we leave data in the host adapter buffer * when the clists are full? That may be dangerous * if the user cannot get an interrupt signal through. */ more_rx: /* XXX Sorry. the nesting was driving me bats! :-( */ if (!isopen) { ccbp->hi_rxopos = ccbp->hi_rxipos; goto end_rx; } /* * If the tty input buffers are blocked, stop emptying * the incoming buffers and let the auto flow control * assert.. */ if (tp->t_state & TS_TBLOCK) { goto end_rx; } /* * Process read characters if not skipped above */ op = ccbp->hi_rxopos; ip = ccbp->hi_rxipos; c = ip - op; if (c == 0) { goto end_rx; } n = c & 0xff; if (n > 250) n = 250; DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", n, op, ip)); /* * Suck characters out of host card buffer into the * "input staging buffer" - so that we dont leave the * host card in limbo while we're possibly echoing * characters and possibly flushing input inside the * ldisc l_rint() routine. */ if (n <= SI_BUFFERSIZE - op) { DPRINT((pp, DBG_INTR, "\tsingle copy\n")); z = ccbp->hi_rxbuf + op; si_bcopy((caddr_t)z, si_rxbuf, n); op += n; } else { x = SI_BUFFERSIZE - op; DPRINT((pp, DBG_INTR, "\tdouble part 1 %d\n", x)); z = ccbp->hi_rxbuf + op; si_bcopy((caddr_t)z, si_rxbuf, x); DPRINT((pp, DBG_INTR, "\tdouble part 2 %d\n", n - x)); z = ccbp->hi_rxbuf; si_bcopy((caddr_t)z, si_rxbuf + x, n - x); op += n; } /* clear collected characters from buffer */ ccbp->hi_rxopos = op; DPRINT((pp, DBG_INTR, "n = %d, op = %d, ip = %d\n", n, op, ip)); /* * at this point... * n = number of chars placed in si_rxbuf */ /* * Avoid the grotesquely inefficient lineswitch * routine (ttyinput) in "raw" mode. It usually * takes about 450 instructions (that's without * canonical processing or echo!). slinput is * reasonably fast (usually 40 instructions * plus call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { /* block if the driver supports it */ if (tp->t_rawq.c_cc + n >= SI_I_HIGH_WATER && (tp->t_cflag & CRTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += n; tk_rawcc += n; tp->t_rawcc += n; pp->sp_delta_overflows += b_to_q((char *)si_rxbuf, n, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; si_start(tp); } } else { /* * It'd be nice to not have to go through the * function call overhead for each char here. * It'd be nice to block input it, saving a * loop here and the call/return overhead. */ for(x = 0; x < n; x++) { i = si_rxbuf[x]; if ((*linesw[tp->t_line].l_rint)(i, tp) == -1) { pp->sp_delta_overflows++; } /* * doesn't seem to be much point doing * this here.. this driver has no * softtty processing! ?? */ if (pp->sp_hotchar && i == pp->sp_hotchar) { setsofttty(); } } } goto more_rx; /* try for more until RXbuf is empty */ end_rx: /* XXX: Again, sorry about the gotos.. :-) */ /* * Do TX stuff */ (*linesw[tp->t_line].l_start)(tp); } /* end of for (all ports on this controller) */ } /* end of for (all controllers) */ in_intr = 0; DPRINT((0, (unit < 0) ? DBG_POLL:DBG_INTR, "end si_intr(%d)\n", unit)); } /* * Nudge the transmitter... * * XXX: I inherited some funny code here. It implies the host card only * interrupts when the transmit buffer reaches the low-water-mark, and does * not interrupt when it's actually hits empty. In some cases, we have * processes waiting for complete drain, and we need to simulate an interrupt * about when we think the buffer is going to be empty (and retry if not). * I really am not certain about this... I *need* the hardware manuals. */ static void si_start(tp) register struct tty *tp; { struct si_port *pp; volatile struct si_channel *ccbp; register struct clist *qp; BYTE ipos; int nchar; int oldspl, count, n, amount, buffer_full; oldspl = spltty(); qp = &tp->t_outq; pp = TP2PP(tp); DPRINT((pp, DBG_ENTRY|DBG_START, "si_start(%x) t_state %x sp_state %x t_outq.c_cc %d\n", tp, tp->t_state, pp->sp_state, qp->c_cc)); if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) goto out; buffer_full = 0; ccbp = pp->sp_ccb; count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; DPRINT((pp, DBG_START, "count %d\n", (BYTE)count)); while ((nchar = qp->c_cc) > 0) { if ((BYTE)count >= 255) { buffer_full++; break; } amount = min(nchar, (255 - (BYTE)count)); ipos = (unsigned int)ccbp->hi_txipos; n = q_to_b(&tp->t_outq, si_txbuf, amount); /* will it fit in one lump? */ if ((SI_BUFFERSIZE - ipos) >= n) { si_bcopy(si_txbuf, (char *)&ccbp->hi_txbuf[ipos], n); } else { si_bcopy(si_txbuf, (char *)&ccbp->hi_txbuf[ipos], SI_BUFFERSIZE - ipos); si_bcopy(si_txbuf + (SI_BUFFERSIZE - ipos), (char *)&ccbp->hi_txbuf[0], n - (SI_BUFFERSIZE - ipos)); } ccbp->hi_txipos += n; count = (int)ccbp->hi_txipos - (int)ccbp->hi_txopos; } if (count != 0 && nchar == 0) { tp->t_state |= TS_BUSY; } else { tp->t_state &= ~TS_BUSY; } /* wakeup time? */ ttwwakeup(tp); DPRINT((pp, DBG_START, "count %d, nchar %d, tp->t_state 0x%x\n", (BYTE)count, nchar, tp->t_state)); if (tp->t_state & TS_BUSY) { int time; time = ttspeedtab(tp->t_ospeed, chartimes); if (time > 0) { if (time < nchar) time = nchar / time; else time = 2; } else { DPRINT((pp, DBG_START, "bad char time value! %d\n", time)); time = hz/10; } if ((pp->sp_state & (SS_LSTART|SS_INLSTART)) == SS_LSTART) { untimeout(si_lstart, (caddr_t)pp, pp->lstart_ch); } else { pp->sp_state |= SS_LSTART; } DPRINT((pp, DBG_START, "arming lstart, time=%d\n", time)); pp->lstart_ch = timeout(si_lstart, (caddr_t)pp, time); } out: splx(oldspl); DPRINT((pp, DBG_EXIT|DBG_START, "leave si_start()\n")); } /* * Note: called at splsoftclock from the timeout code * This has to deal with two things... cause wakeups while waiting for * tty drains on last process exit, and call l_start at about the right * time for protocols like ppp. */ static void si_lstart(void *arg) { register struct si_port *pp = arg; register struct tty *tp; int oldspl; DPRINT((pp, DBG_ENTRY|DBG_LSTART, "si_lstart(%x) sp_state %x\n", pp, pp->sp_state)); oldspl = spltty(); if ((pp->sp_state & SS_OPEN) == 0 || (pp->sp_state & SS_LSTART) == 0) { splx(oldspl); return; } pp->sp_state &= ~SS_LSTART; pp->sp_state |= SS_INLSTART; tp = pp->sp_tty; /* deal with the process exit case */ ttwwakeup(tp); /* nudge protocols - eg: ppp */ (*linesw[tp->t_line].l_start)(tp); pp->sp_state &= ~SS_INLSTART; splx(oldspl); } /* * Stop output on a line. called at spltty(); */ void sistop(tp, rw) register struct tty *tp; int rw; { volatile struct si_channel *ccbp; struct si_port *pp; pp = TP2PP(tp); ccbp = pp->sp_ccb; DPRINT((TP2PP(tp), DBG_ENTRY|DBG_STOP, "sistop(%x,%x)\n", tp, rw)); /* XXX: must check (rw & FWRITE | FREAD) etc flushing... */ if (rw & FWRITE) { /* what level are we meant to be flushing anyway? */ if (tp->t_state & TS_BUSY) { si_command(TP2PP(tp), WFLUSH, SI_NOWAIT); tp->t_state &= ~TS_BUSY; ttwwakeup(tp); /* Bruce???? */ } } #if 1 /* XXX: this doesn't work right yet.. */ /* XXX: this may have been failing because we used to call l_rint() * while we were looping based on these two counters. Now, we collect * the data and then loop stuffing it into l_rint(), making this * useless. Should we cause this to blow away the staging buffer? */ if (rw & FREAD) { ccbp->hi_rxopos = ccbp->hi_rxipos; } #endif } /* * Issue a command to the host card CPU. */ static void si_command(pp, cmd, waitflag) struct si_port *pp; /* port control block (local) */ int cmd; int waitflag; { int oldspl; volatile struct si_channel *ccbp = pp->sp_ccb; int x; DPRINT((pp, DBG_ENTRY|DBG_PARAM, "si_command(%x,%x,%d): hi_stat 0x%x\n", pp, cmd, waitflag, ccbp->hi_stat)); oldspl = spltty(); /* Keep others out */ /* wait until it's finished what it was doing.. */ /* XXX: sits in IDLE_BREAK until something disturbs it or break * is turned off. */ while((x = ccbp->hi_stat) != IDLE_OPEN && x != IDLE_CLOSE && x != IDLE_BREAK && x != cmd) { if (in_intr) { /* Prevent sleep in intr */ DPRINT((pp, DBG_PARAM, "cmd intr collision - completing %d\trequested %d\n", x, cmd)); splx(oldspl); return; } else if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, "sicmd1", 1)) { splx(oldspl); return; } } /* it should now be in IDLE_{OPEN|CLOSE|BREAK}, or "cmd" */ /* if there was a pending command, cause a state-change wakeup */ switch(pp->sp_pend) { case LOPEN: case MPEND: case MOPEN: case CONFIG: case SBREAK: case EBREAK: wakeup(&pp->sp_state); break; default: break; } pp->sp_pend = cmd; /* New command pending */ ccbp->hi_stat = cmd; /* Post it */ if (waitflag) { if (in_intr) { /* If in interrupt handler */ DPRINT((pp, DBG_PARAM, "attempt to sleep in si_intr - cmd req %d\n", cmd)); splx(oldspl); return; } else while(ccbp->hi_stat != IDLE_OPEN && ccbp->hi_stat != IDLE_BREAK) { if (ttysleep(pp->sp_tty, (caddr_t)&pp->sp_state, TTIPRI|PCATCH, "sicmd2", 0)) break; } } splx(oldspl); } static void si_disc_optim(tp, t, pp) struct tty *tp; struct termios *t; struct si_port *pp; { /* * XXX can skip a lot more cases if Smarts. Maybe * (IGNCR | ISTRIP | IXON) in c_iflag. But perhaps we * shouldn't skip if (TS_CNTTB | TS_LNCH) is set in t_state. */ if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; pp->sp_hotchar = linesw[tp->t_line].l_hotchar; DPRINT((pp, DBG_OPTIM, "bypass: %s, hotchar: %x\n", (tp->t_state & TS_CAN_BYPASS_L_RINT) ? "on" : "off", pp->sp_hotchar)); } #ifdef SI_DEBUG static void #ifdef __STDC__ si_dprintf(struct si_port *pp, int flags, const char *fmt, ...) #else si_dprintf(pp, flags, fmt, va_alist) struct si_port *pp; int flags; char *fmt; #endif { va_list ap; if ((pp == NULL && (si_debug&flags)) || (pp != NULL && ((pp->sp_debug&flags) || (si_debug&flags)))) { if (pp != NULL) printf("%ci%d(%d): ", 's', (int)SI_CARD(pp->sp_tty->t_dev), (int)SI_PORT(pp->sp_tty->t_dev)); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } } static char * si_mctl2str(cmd) enum si_mctl cmd; { switch (cmd) { case GET: return("GET"); case SET: return("SET"); case BIS: return("BIS"); case BIC: return("BIC"); } return("BAD"); } #endif /* DEBUG */ static char * si_modulename(host_type, uart_type) int host_type, uart_type; { switch (host_type) { /* Z280 based cards */ #if NEISA > 0 case SIEISA: #endif case SIHOST2: case SIHOST: #if NPCI > 0 case SIPCI: #endif switch (uart_type) { case 0: return(" (XIO)"); case 1: return(" (SI)"); } break; /* T225 based hosts */ #if NPCI > 0 case SIJETPCI: #endif case SIJETISA: switch (uart_type) { case 0: return(" (SI)"); case 40: return(" (XIO)"); case 72: return(" (SXDC)"); } break; } return(""); } static si_devsw_installed = 0; static void si_drvinit(void *unused) { dev_t dev; if (!si_devsw_installed) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev,&si_cdevsw, NULL); si_devsw_installed = 1; } } SYSINIT(sidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,si_drvinit,NULL) diff --git a/sys/i386/isa/sio.c b/sys/i386/isa/sio.c index c36de1726369..ab219502e475 100644 --- a/sys/i386/isa/sio.c +++ b/sys/i386/isa/sio.c @@ -1,3040 +1,3041 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.211 1998/08/19 04:17:37 bde Exp $ + * $Id: sio.c,v 1.212 1998/08/20 05:12:48 bde Exp $ */ #include "opt_comconsole.h" #include "opt_compat.h" #include "opt_ddb.h" #include "opt_devfs.h" #include "opt_sio.h" #include "sio.h" #include "pnp.h" #ifndef EXTRA_SIO #if NPNP > 0 #define EXTRA_SIO 2 #else #define EXTRA_SIO 0 #endif #endif #define NSIOTOT (NSIO + EXTRA_SIO) /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. * * Changes for PC-Card integration: * - Added PC-Card driver table and handlers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #ifdef COM_ESP #include #endif #include #include "card.h" #if NCARD > 0 #include #include #endif #if NPNP > 0 #include #endif #ifdef SMP #define disable_intr() COM_DISABLE_INTR() #define enable_intr() COM_ENABLE_INTR() #endif /* SMP */ #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RS_IBUFSIZE 256 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) #define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ #define COM_CONSOLE(dev) ((dev)->id_flags & 0x10) #define COM_FORCECONSOLE(dev) ((dev)->id_flags & 0x20) #define COM_LLCONSOLE(dev) ((dev)->id_flags & 0x40) #define COM_LOSESOUTINTS(dev) ((dev)->id_flags & 0x08) #define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) #define COM_ST16650A(dev) ((dev)->id_flags & 0x20000) #define COM_C_NOPROBE (0x40000) #define COM_NOPROBE(dev) ((dev)->id_flags & COM_C_NOPROBE) #define COM_C_IIR_TXRDYBUG (0x80000) #define COM_IIR_TXRDYBUG(dev) ((dev)->id_flags & COM_C_IIR_TXRDYBUG) #define COM_FIFOSIZE(dev) (((dev)->id_flags & 0xff000000) >> 24) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * siostop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ #define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_int id_flags; /* Copy isa device falgas */ u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ #ifdef COM_ESP bool_t esp; /* is this unit a hayes esp board? */ #endif u_char extra_state; /* more flag bits, separate for order trick */ u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t gone; /* hardware disappeared */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ Port_t data_port; /* i/o ports */ #ifdef COM_ESP Port_t esp_port; #endif Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; Port_t intr_ctl_port; /* Ports of IIR register */ struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; #ifdef DEVFS void *devfs_token_ttyd; void *devfs_token_ttyl; void *devfs_token_ttyi; void *devfs_token_cuaa; void *devfs_token_cual; void *devfs_token_cuai; #endif }; #ifdef COM_ESP static int espattach __P((struct isa_device *isdp, struct com_s *com, Port_t esp_port)); #endif static int sioattach __P((struct isa_device *dev)); static timeout_t siobusycheck; static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); static void siointr1 __P((struct com_s *com)); static int commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static swihand_t siopoll; static int sioprobe __P((struct isa_device *dev)); static void siosettimeout __P((void)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com)); #ifdef DSI_SOFT_MODEM static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr)); #endif /* DSI_SOFT_MODEM */ static char driver_name[] = "sio"; /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIOTOT]; #define com_addr(unit) (p_com_addr[unit]) struct isa_driver siodriver = { sioprobe, sioattach, driver_name }; static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static d_stop_t siostop; static d_devtotty_t siodevtotty; -#define CDEV_MAJOR 28 -static struct cdevsw sio_cdevsw = { +#define CDEV_MAJOR 28 +static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, ttpoll, nommap, NULL, driver_name, - NULL, -1, + NULL, -1, nodump, nopsize, + D_TTY, }; static int comconsole = -1; static volatile speed_t comdefaultrate = CONSPEED; static u_int com_events; /* input chars + weighted output completions */ static Port_t siocniobase; static bool_t sio_registered; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); #if 0 /* XXX */ static struct tty *sio_tty[NSIOTOT]; #else static struct tty sio_tty[NSIOTOT]; #endif static const int nsio_tty = NSIOTOT; static struct speedtab comspeedtab[] = { { 0, 0 }, { 50, COMBRD(50) }, { 75, COMBRD(75) }, { 110, COMBRD(110) }, { 134, COMBRD(134) }, { 150, COMBRD(150) }, { 200, COMBRD(200) }, { 300, COMBRD(300) }, { 600, COMBRD(600) }, { 1200, COMBRD(1200) }, { 1800, COMBRD(1800) }, { 2400, COMBRD(2400) }, { 4800, COMBRD(4800) }, { 9600, COMBRD(9600) }, { 19200, COMBRD(19200) }, { 38400, COMBRD(38400) }, { 57600, COMBRD(57600) }, { 115200, COMBRD(115200) }, { -1, -1 } }; #ifdef COM_ESP /* XXX configure this properly. */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; #endif /* * handle sysctl read/write requests for console speed * * In addition to setting comdefaultrate for I/O through /dev/console, * also set the initial and lock values for the /dev/ttyXX device * if there is one associated with the console. Finally, if the /dev/tty * device has already been open, change the speed on the open running port * itself. */ static int sysctl_machdep_comdefaultrate SYSCTL_HANDLER_ARGS { int error, s; speed_t newspeed; struct com_s *com; struct tty *tp; newspeed = comdefaultrate; error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); if (error || !req->newptr) return (error); comdefaultrate = newspeed; if (comconsole < 0) /* serial console not selected? */ return (0); com = com_addr(comconsole); if (!com) return (ENXIO); /* * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX * (note, the lock rates really are boolean -- if non-zero, disallow * speed changes) */ com->it_in.c_ispeed = com->it_in.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_out.c_ispeed = com->it_out.c_ospeed = com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; /* * if we're open, change the running rate too */ tp = com->tp; if (tp && (tp->t_state & TS_ISOPEN)) { tp->t_termios.c_ispeed = tp->t_termios.c_ospeed = comdefaultrate; s = spltty(); error = comparam(tp, &tp->t_termios); splx(s); } return error; } SYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_machdep_comdefaultrate, "I", ""); #if NCARD > 0 /* * PC-Card (PCMCIA) specific code. */ static int sioinit __P((struct pccard_devinfo *)); static void siounload __P((struct pccard_devinfo *)); static int card_intr __P((struct pccard_devinfo *)); static struct pccard_device sio_info = { driver_name, sioinit, siounload, card_intr, 0, /* Attributes - presently unused */ &tty_imask /* Interrupt mask for device */ /* XXX - Should this also include net_imask? */ }; DATA_SET(pccarddrv_set, sio_info); /* * Initialize the device - called from Slot manager. */ int sioinit(struct pccard_devinfo *devi) { /* validate unit number. */ if (devi->isahd.id_unit >= (NSIOTOT)) return(ENODEV); /* Make sure it isn't already probed. */ if (com_addr(devi->isahd.id_unit)) return(EBUSY); /* It's already probed as serial by Upper */ devi->isahd.id_flags |= COM_C_NOPROBE; /* * Probe the device. If a value is returned, the * device was found at the location. */ if (sioprobe(&devi->isahd) == 0) return(ENXIO); if (sioattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * siounload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void siounload(struct pccard_devinfo *devi) { struct com_s *com; if (!devi) { printf("NULL devi in siounload\n"); return; } com = com_addr(devi->isahd.id_unit); if (!com) { printf("NULL com in siounload\n"); return; } if (!com->iobase) { printf("sio%d already unloaded!\n",devi->isahd.id_unit); return; } if (com->tp && (com->tp->t_state & TS_ISOPEN)) { com->gone = 1; printf("sio%d: unload\n", devi->isahd.id_unit); com->tp->t_gen++; ttyclose(com->tp); ttwakeup(com->tp); ttwwakeup(com->tp); } else { com_addr(com->unit) = NULL; bzero(com, sizeof *com); free(com,M_TTYS); printf("sio%d: unload,gone\n", devi->isahd.id_unit); } } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(struct pccard_devinfo *devi) { struct com_s *com; COM_LOCK(); com = com_addr(devi->isahd.id_unit); if (com && !com->gone) siointr1(com_addr(devi->isahd.id_unit)); COM_UNLOCK(); return(1); } #endif /* NCARD > 0 */ static int sioprobe(dev) struct isa_device *dev; { static bool_t already_init; bool_t failures[10]; int fn; struct isa_device *idev; Port_t iobase; intrmask_t irqmap[4]; intrmask_t irqs; u_char mcr_image; int result; struct isa_device *xdev; if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ for (xdev = isa_devtab_tty; xdev->id_driver != NULL; xdev++) if (xdev->id_driver == &siodriver && xdev->id_enabled) outb(xdev->id_iobase + com_mcr, 0); already_init = TRUE; } if (COM_LLCONSOLE(dev)) { printf("sio%d: reserved for low-level i/o\n", dev->id_unit); return (0); } /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(dev)) { idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); dev->id_irq = 0; idev = dev; } if (!COM_NOTAST4(dev)) { outb(idev->id_iobase + com_scr, idev->id_irq ? 0x80 : 0); mcr_image = 0; } } #endif /* COM_MULTIPORT */ if (idev->id_irq == 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = dev->id_iobase; /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ disable_intr(); /* EXTRA DELAY? */ /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ if (iobase == siocniobase) DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); else { outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); outb(iobase + com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff); outb(iobase + com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8); outb(iobase + com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); } /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); outb(iobase + com_ier, 0); DELAY(1000); /* XXX */ irqmap[0] = isa_irq_pending(); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ outb(iobase + com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); /* * It's a definitly Serial PCMCIA(16550A), but still be required * for IIR_TXRDY implementation ( Palido 321s, DC-1S... ) */ if ( COM_NOPROBE(dev) ) { /* Reading IIR register twice */ for ( fn = 0; fn < 2; fn ++ ) { DELAY(10000); failures[6] = inb(iobase + com_iir); } /* Check IIR_TXRDY clear ? */ result = IO_COMSIZE; if ( failures[6] & IIR_TXRDY ) { /* Nop, Double check with clearing IER */ outb(iobase + com_ier, 0); if ( inb(iobase + com_iir) & IIR_NOPEND ) { /* Ok. we're familia this gang */ dev->id_flags |= COM_C_IIR_TXRDYBUG; /* Set IIR_TXRDYBUG */ } else { /* Unknow, Just omit this chip.. XXX*/ result = 0; } } else { /* OK. this is well-known guys */ dev->id_flags &= ~COM_C_IIR_TXRDYBUG; /*Clear IIR_TXRDYBUG*/ } outb(iobase + com_cfcr, CFCR_8BITS); enable_intr(); return (iobase == siocniobase ? IO_COMSIZE : result); } /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; failures[1] = inb(iobase + com_ier) - IER_ETXRDY; failures[2] = inb(iobase + com_mcr) - mcr_image; DELAY(10000); /* Some internal modems need this time */ irqmap[1] = isa_irq_pending(); failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; DELAY(1000); /* XXX */ irqmap[2] = isa_irq_pending(); failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* * Turn off all device interrupts and check that they go off properly. * Leave MCR_IENABLE alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving at) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); DELAY(1000); /* XXX */ irqmap[3] = isa_irq_pending(); failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; enable_intr(); irqs = irqmap[1] & ~irqmap[0]; if (idev->id_irq != 0 && (idev->id_irq & irqs) == 0) printf( "sio%d: configured irq %d not in bitmap of probed irqs %#x\n", dev->id_unit, ffs(idev->id_irq) - 1, irqs); if (bootverbose) printf("sio%d: irq maps: %#x %#x %#x %#x\n", dev->id_unit, irqmap[0], irqmap[1], irqmap[2], irqmap[3]); result = IO_COMSIZE; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { outb(iobase + com_mcr, 0); result = 0; if (bootverbose) { printf("sio%d: probe failed test(s):", dev->id_unit); for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) printf(" %d", fn); printf("\n"); } break; } return (iobase == siocniobase ? IO_COMSIZE : result); } #ifdef COM_ESP static int espattach(isdp, com, esp_port) struct isa_device *isdp; struct com_s *com; Port_t esp_port; { u_char dips; u_char val; /* * Check the ESP-specific I/O port to see if we're an ESP * card. If not, return failure immediately. */ if ((inb(esp_port) & 0xf3) == 0) { printf(" port 0x%x is not an ESP board?\n", esp_port); return (0); } /* * We've got something that claims to be a Hayes ESP card. * Let's hope so. */ /* Get the dip-switch configuration */ outb(esp_port + ESP_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP_STATUS1); /* * Bits 0,1 of dips say which COM port we are. */ if (com->iobase == likely_com_ports[dips & 0x03]) printf(" : ESP"); else { printf(" esp_port has com %d\n", dips & 0x03); return (0); } /* * Check for ESP version 2.0 or later: bits 4,5,6 = 010. */ outb(esp_port + ESP_CMD1, ESP_GETTEST); val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP_STATUS2); if ((val & 0x70) < 0x20) { printf("-old (%o)", val & 0x70); return (0); } /* * Check for ability to emulate 16550: bit 7 == 1 */ if ((dips & 0x80) == 0) { printf(" slave"); return (0); } /* * Okay, we seem to be a Hayes ESP card. Whee. */ com->esp = TRUE; com->esp_port = esp_port; return (1); } #endif /* COM_ESP */ static int sioattach(isdp) struct isa_device *isdp; { struct com_s *com; dev_t dev; #ifdef COM_ESP Port_t *espp; #endif Port_t iobase; int s; int unit; isdp->id_ri_flags |= RI_FAST; iobase = isdp->id_iobase; unit = isdp->id_unit; com = malloc(sizeof *com, M_TTYS, M_NOWAIT); if (com == NULL) return (0); /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->unit = unit; com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->loses_outints = COM_LOSESOUTINTS(isdp) != 0; com->no_irq = isdp->id_irq == 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->iobase = iobase; com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; com->intr_ctl_port = iobase + com_ier; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; com->lt_out.c_ispeed = com->lt_out.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; } else com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; termioschars(&com->it_in); com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifdef DSI_SOFT_MODEM if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) { printf(" Digicom Systems, Inc. SoftModem"); goto determined_type; } #endif /* DSI_SOFT_MODEM */ #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(isdp) && !COM_IIR_TXRDYBUG(isdp)) #else if (!COM_IIR_TXRDYBUG(isdp)) #endif { u_char scr; u_char scr1; u_char scr2; scr = inb(iobase + com_scr); outb(iobase + com_scr, 0xa5); scr1 = inb(iobase + com_scr); outb(iobase + com_scr, 0x5a); scr2 = inb(iobase + com_scr); outb(iobase + com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250"); goto determined_type; } } outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); DELAY(100); com->st16650a = 0; switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_RX_LOW: printf(" 16450"); break; case FIFO_RX_MEDL: printf(" 16450?"); break; case FIFO_RX_MEDH: printf(" 16550?"); break; case FIFO_RX_HIGH: if (COM_NOFIFO(isdp)) { printf(" 16550A fifo disabled"); } else { com->hasfifo = TRUE; if (COM_ST16650A(isdp)) { com->st16650a = 1; com->tx_fifo_size = 32; printf(" ST16650A"); } else { com->tx_fifo_size = COM_FIFOSIZE(isdp); printf(" 16550A"); } } #ifdef COM_ESP for (espp = likely_esp_ports; *espp != 0; espp++) if (espattach(isdp, com, *espp)) { com->tx_fifo_size = 1024; break; } #endif if (!com->st16650a) { if (!com->tx_fifo_size) com->tx_fifo_size = 16; else printf(" lookalike with %d bytes FIFO", com->tx_fifo_size); } break; } #ifdef COM_ESP if (com->esp) { /* * Set 16550 compatibility mode. * We don't use the ESP_MODE_SCALE bit to increase the * fifo trigger levels because we can't handle large * bursts of input. * XXX flow control should be set in comparam(), not here. */ outb(com->esp_port + ESP_CMD1, ESP_SETMODE); outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); /* Set RTS/CTS flow control. */ outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); /* Set flow-control levels. */ outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP_CMD2, HIBYTE(768)); outb(com->esp_port + ESP_CMD2, LOBYTE(768)); outb(com->esp_port + ESP_CMD2, HIBYTE(512)); outb(com->esp_port + ESP_CMD2, LOBYTE(512)); } #endif /* COM_ESP */ outb(iobase + com_fifo, 0); determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(isdp)) { com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(isdp)) printf(" master"); printf(")"); com->no_irq = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ if (unit == comconsole) printf(", console"); if ( COM_IIR_TXRDYBUG(isdp) ) printf(" with a bogus IIR_TXRDY register"); printf("\n"); s = spltty(); com_addr(unit) = com; splx(s); if (!sio_registered) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &sio_cdevsw, NULL); register_swi(SWI_TTY, siopoll); sio_registered = TRUE; } #ifdef DEVFS com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, unit, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_LOCK_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); #endif com->id_flags = isdp->id_flags; /* Heritate id_flags for later */ return (1); } static int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIOTOT || (com = com_addr(unit)) == NULL) return (ENXIO); if (com->gone) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; tp->t_ififosize = 2 * RS_IBUFSIZE; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ iobase = com->iobase; if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); /* * XXX the delays are for superstitious * historical reasons. It must be less than * the character time at the maximum * supported speed (87 usec at 115200 bps * 8N1). Otherwise we might loop endlessly * if data is streaming in. We used to use * delays of 100. That usually worked * because DELAY(100) used to usually delay * for about 85 usec instead of 100. */ DELAY(50); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(50); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); if (COM_IIR_TXRDYBUG(com)) { outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS | IER_EMSC); } else { outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); } enable_intr(); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); if (com_addr(unit) == NULL) return (ENXIO); --com->wopeners; if (error != 0 || com->gone) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); if (com->gone) { printf("sio%d: gone\n", com->unit); s = spltty(); com_addr(com->unit) = 0; bzero(tp,sizeof *tp); bzero(com,sizeof *com); free(com,M_TTYS); splx(s); } return (0); } static void comhardclose(com) struct com_s *com; { Port_t iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty(); com->poll = FALSE; com->poll_output = FALSE; com->do_timestamp = FALSE; com->do_dcd_timestamp = FALSE; outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); { outb(iobase + com_ier, 0); tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { (void)commctl(com, TIOCM_DTR, DMBIC); if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ outb(iobase + com_fifo, 0); } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; int unit; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } static void siobusycheck(chan) void *chan; { struct com_s *com; int s; com = (struct com_s *)chan; /* * Clear TS_BUSY if low-level output is complete. * spl locking is sufficient because siointr1() does not set CS_BUSY. * If siointr1() clears CS_BUSY after we look at it, then we'll get * called again. Reading the line status port outside of siointr1() * is safe because CS_BUSY is clear so there are no output interrupts * to lose. */ s = spltty(); if (com->state & CS_BUSY) com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) == (LSR_TSRE | LSR_TXRDY)) { com->tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); com->extra_state &= ~CSE_BUSYCHECK; } else timeout(siobusycheck, com, hz / 100); splx(s); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } void siointr(unit) int unit; { #ifndef COM_MULTIPORT COM_LOCK(); siointr1(com_addr(unit)); COM_UNLOCK(); #else /* COM_MULTIPORT */ struct com_s *com; bool_t possibly_more_intrs; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ COM_LOCK(); do { possibly_more_intrs = FALSE; for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); /* * XXX COM_LOCK(); * would it work here, or be counter-productive? */ if (com != NULL && !com->gone && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } /* XXX COM_UNLOCK(); */ } } while (possibly_more_intrs); COM_UNLOCK(); #endif /* COM_MULTIPORT */ } static void siointr1(com) struct com_s *com; { u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; u_char int_ident; u_char int_ctl; u_char int_ctl_new; int_ctl = inb(com->intr_ctl_port); int_ctl_new = int_ctl; while (!com->gone) { line_status = inb(com->line_status_port); /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { /* * Don't store BI if IGNBRK or FE/PE if IGNPAR. * Otherwise, push the work to a higher level * (to handle PARMRK) if we're bypassing. * Otherwise, convert BI/FE and PE+INPCK to 0. * * This makes bypassing work right in the * usual "raw" case (IGNBRK set, and IGNPAR * and INPCK clear). * * Note: BI together with FE/PE means just BI. */ if (line_status & LSR_BI) { #if defined(DDB) && defined(BREAK_TO_DEBUGGER) if (com->unit == comconsole) { breakpoint(); goto cont; } #endif if (com->tp == NULL || com->tp->t_iflag & IGNBRK) goto cont; } else { if (com->tp == NULL || com->tp->t_iflag & IGNPAR) goto cont; } if (com->tp->t_state & TS_CAN_BYPASS_L_RINT && (line_status & (LSR_BI | LSR_FE) || com->tp->t_iflag & INPCK)) recv_data = 0; } ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; schedsofttty(); #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } cont: /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ line_status = inb(com->line_status_port) & 0x7F; } /* modem status change? (always check before doing output) */ modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } /* output queued and everything ready? */ if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { ioptr = com->obufq.l_head; if (com->tx_fifo_size > 1) { u_int ocount; ocount = com->obufq.l_tail - ioptr; if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do outb(com->data_port, *ioptr++); while (--ocount != 0); } else { outb(com->data_port, *ioptr++); ++com->bytes_out; } com->obufq.l_head = ioptr; if (COM_IIR_TXRDYBUG(com)) { int_ctl_new = int_ctl | IER_ETXRDY; } if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ if ( COM_IIR_TXRDYBUG(com) ) { int_ctl_new = int_ctl & ~IER_ETXRDY; } com->state &= ~CS_BUSY; } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; setsofttty(); /* handle at high level ASAP */ } } if ( COM_IIR_TXRDYBUG(com) && (int_ctl != int_ctl_new)) { outb(com->intr_ctl_port, int_ctl_new); } } /* finished? */ #ifndef COM_MULTIPORT if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int sioioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) int oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); if (com->gone) return (ENODEV); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); #ifdef DSI_SOFT_MODEM /* * Download micro-code to Digicom modem. */ case TIOCDSIMICROCODE: { u_long l; u_char *p,*pi; pi = (u_char*)(*(caddr_t*)data); error = copyin(pi,&l,sizeof l); if(error) {return error;}; pi += sizeof l; p = malloc(l,M_TEMP,M_NOWAIT); if(!p) {return ENOBUFS;} error = copyin(pi,p,l); if(error) {free(p,M_TEMP); return error;}; if(error = LoadSoftModem( MINOR_TO_UNIT(mynor),iobase,l,p)) {free(p,M_TEMP); return error;} free(p,M_TEMP); return(0); } #endif /* DSI_SOFT_MODEM */ default: return (ENOTTY); } } tp = com->tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return (error); s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, com); if (error != ENOIOCTL) { splx(s); return (error); } switch (cmd) { case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } static void siopoll() { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < NSIOTOT; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL || com->gone) { /* * Discard any events related to never-opened or * going-away devices. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; enable_intr(); if (!(com->state & CS_BUSY) && !(com->extra_state & CSE_BUSYCHECK)) { timeout(siobusycheck, com, hz / 100); com->extra_state |= CSE_BUSYCHECK; } (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) continue; /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; int divisor; u_char dlbh; u_char dlbl; int error; Port_t iobase; int s; int unit; int txtimeout; /* do historical conversions */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; /* check requested parameters */ divisor = ttspeedtab(t->c_ospeed, comspeedtab); if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); if (divisor == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); cflag = t->c_cflag; switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo && divisor != 0) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. */ com->fifo_image = t->c_ospeed <= 4800 ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH; #ifdef COM_ESP /* * The Hayes ESP card needs the fifo DMA mode bit set * in compatibility mode. If not, it will interrupt * for each character received. */ if (com->esp) com->fifo_image |= FIFO_DMA_MODE; #endif outb(iobase + com_fifo, com->fifo_image); } /* * Some UARTs lock up if the divisor latch registers are selected * while the UART is doing output (they refuse to transmit anything * more until given a hard reset). Fix this by stopping filling * the device buffers and waiting for them to drain. Reading the * line status port outside of siointr1() might lose some receiver * error bits, but that is acceptable here. */ disable_intr(); retry: com->state &= ~CS_TTGO; txtimeout = tp->t_timeout; enable_intr(); while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) { tp->t_state |= TS_SO_OCOMPLETE; error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, "siotx", hz / 100); if ( txtimeout != 0 && (!error || error == EAGAIN) && (txtimeout -= hz / 100) <= 0 ) error = EIO; if (com->gone) error = ENODEV; if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); com->state |= CS_TTGO; enable_intr(); } splx(s); return (error); } } disable_intr(); /* very important while com_data is hidden */ /* * XXX - clearing CS_TTGO is not sufficient to stop further output, * because siopoll() calls comstart() which usually sets it again * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be * sufficient, for similar reasons. */ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) goto retry; if (divisor != 0) { outb(iobase + com_cfcr, cfcr | CFCR_DLAB); /* * Only set the divisor registers if they would change, * since on some 16550 incompatibles (UMC8669F), setting * them while input is arriving them loses sync until * data stops arriving. */ dlbl = divisor & 0xFF; if (inb(iobase + com_dlbl) != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = (u_int) divisor >> 8; if (inb(iobase + com_dlbh) != dlbh) outb(iobase + com_dlbh, dlbh); } outb(iobase + com_cfcr, com->cfcr_image = cfcr); if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) { if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x40); } com->state |= CS_RTS_IFLOW; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x40); } } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x80); } } else { if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x80); } } outb(iobase + com_cfcr, com->cfcr_image); /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); enable_intr(); splx(s); comstart(tp); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); disable_intr(); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; } enable_intr(); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; } enable_intr(); } tp->t_state |= TS_BUSY; } disable_intr(); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ enable_intr(); ttwwakeup(tp); splx(s); } static void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (com->gone) return; disable_intr(); if (rw & FWRITE) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_XMT_RST | com->fifo_image); com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_RCV_RST | com->fifo_image); com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); comstart(tp); } static struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIOTOT) return (NULL); return (&sio_tty[unit]); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { int mcr; int msr; if (how == DMGET) { bits = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) bits |= TIOCM_DTR; if (mcr & MCR_RTS) bits |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= MCR_DTR; if (bits & TIOCM_RTS) mcr |= MCR_RTS; if (com->gone) return(0); disable_intr(); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= mcr); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~mcr); break; } enable_intr(); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN && !com->gone) { someopen = TRUE; if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); if (com != NULL && !com->gone && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < NSIOTOT; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } static void disc_optim(tp, t, com) struct tty *tp; struct termios *t; struct com_s *com; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; com->hotchar = linesw[tp->t_line].l_hotchar; } /* * Following are all routines needed for SIO to act as console */ #include struct siocnstate { u_char dlbl; u_char dlbh; u_char ier; u_char cfcr; u_char mcr; }; static speed_t siocngetspeed __P((Port_t, struct speedtab *)); static void siocnclose __P((struct siocnstate *sp)); static void siocnopen __P((struct siocnstate *sp)); static void siocntxwait __P((void)); static void siocntxwait() { int timo; /* * Wait for any pending transmission to finish. Required to avoid * the UART lockup bug when the speed is changed, and for normal * transmits. */ timo = 100000; while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY) && --timo != 0) ; } /* * Read the serial port specified and try to figure out what speed * it's currently running at. We're assuming the serial port has * been initialized and is basicly idle. This routine is only intended * to be run at system startup. * * If the value read from the serial port doesn't make sense, return 0. */ static speed_t siocngetspeed(iobase, table) Port_t iobase; struct speedtab *table; { int code; u_char dlbh; u_char dlbl; u_char cfcr; cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); dlbl = inb(iobase + com_dlbl); dlbh = inb(iobase + com_dlbh); outb(iobase + com_cfcr, cfcr); code = dlbh << 8 | dlbl; for ( ; table->sp_speed != -1; table++) if (table->sp_code == code) return (table->sp_speed); return 0; /* didn't match anything sane */ } static void siocnopen(sp) struct siocnstate *sp; { int divisor; u_char dlbh; u_char dlbl; Port_t iobase; /* * Save all the device control registers except the fifo register * and set our default ones (cs8 -parenb speed=comdefaultrate). * We can't save the fifo register since it is read-only. */ iobase = siocniobase; sp->ier = inb(iobase + com_ier); outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ siocntxwait(); sp->cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); sp->dlbl = inb(iobase + com_dlbl); sp->dlbh = inb(iobase + com_dlbh); /* * Only set the divisor registers if they would change, since on * some 16550 incompatibles (Startech), setting them clears the * data input register. This also reduces the effects of the * UMC8669F bug. */ divisor = ttspeedtab(comdefaultrate, comspeedtab); dlbl = divisor & 0xFF; if (sp->dlbl != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = (u_int) divisor >> 8; if (sp->dlbh != dlbh) outb(iobase + com_dlbh, dlbh); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp) struct siocnstate *sp; { Port_t iobase; /* * Restore the device control registers. */ siocntxwait(); iobase = siocniobase; outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); if (sp->dlbl != inb(iobase + com_dlbl)) outb(iobase + com_dlbl, sp->dlbl); if (sp->dlbh != inb(iobase + com_dlbh)) outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } void siocnprobe(cp) struct consdev *cp; { speed_t boot_speed; u_char cfcr; struct isa_device *dvp; int s; struct siocnstate sp; /* * Find our first enabled console, if any. If it is a high-level * console device, then initialize it and return successfully. * If it is a low-level console device, then initialize it and * return unsuccessfully. It must be initialized in both cases * for early use by console drivers and debuggers. Initializing * the hardware is not necessary in all cases, since the i/o * routines initialize it on the fly, but it is necessary if * input might arrive while the hardware is switched back to an * uninitialized state. We can't handle multiple console devices * yet because our low-level routines don't take a device arg. * We trust the user to set the console flags properly so that we * don't need to probe. */ cp->cn_pri = CN_DEAD; for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++) if (dvp->id_driver == &siodriver && dvp->id_enabled && COM_CONSOLE(dvp)) { siocniobase = dvp->id_iobase; s = spltty(); if (boothowto & RB_SERIAL) { boot_speed = siocngetspeed(siocniobase, comspeedtab); if (boot_speed) comdefaultrate = boot_speed; } /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siocniobase + com_cfcr); outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); outb(siocniobase + com_dlbl, COMBRD(comdefaultrate) & 0xff); outb(siocniobase + com_dlbh, (u_int) COMBRD(comdefaultrate) >> 8); outb(siocniobase + com_cfcr, cfcr); siocnopen(&sp); splx(s); if (!COM_LLCONSOLE(dvp)) { cp->cn_dev = makedev(CDEV_MAJOR, dvp->id_unit); cp->cn_pri = COM_FORCECONSOLE(dvp) || boothowto & RB_SERIAL ? CN_REMOTE : CN_NORMAL; } break; } } void siocninit(cp) struct consdev *cp; { comconsole = DEV_TO_UNIT(cp->cn_dev); } int siocncheckc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = -1; siocnclose(&sp); splx(s); return (c); } int siocngetc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp); splx(s); return (c); } void siocnputc(dev, c) dev_t dev; int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp); siocntxwait(); outb(siocniobase + com_data, c); siocnclose(&sp); splx(s); } #ifdef DSI_SOFT_MODEM /* * The magic code to download microcode to a "Connection 14.4+Fax" * modem from Digicom Systems Inc. Very magic. */ #define DSI_ERROR(str) { ptr = str; goto error; } static int LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr) { int int_c,int_k; int data_0188, data_0187; /* * First see if it is a DSI SoftModem */ if(!((inb(base_io+7) ^ inb(base_io+7)) & 0x80)) return ENODEV; data_0188 = inb(base_io+4); data_0187 = inb(base_io+3); outb(base_io+3,0x80); outb(base_io+4,0x0C); outb(base_io+0,0x31); outb(base_io+1,0x8C); outb(base_io+7,0x10); outb(base_io+7,0x19); if(0x18 != (inb(base_io+7) & 0x1A)) DSI_ERROR("dsp bus not granted"); if(0x01 != (inb(base_io+7) & 0x01)) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x01 != (inb(base_io+7) & 0x01)) DSI_ERROR("program mem not granted"); } int_c = 0; while(1) { if(int_c >= 7 || size <= 0x1800) break; for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; int_c++; } if(size > 0x1800) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; while(size > 0x1800) { for(int_k = 0 ; int_k < 0xC00; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; } if(size < 0x1800) { for(int_k=0;int_k 0) { if(int_c == 7) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } else { for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } } outb(base_io+7,0x11); outb(base_io+7,3); outb(base_io+4,data_0188 & 0xfb); outb(base_io+3,data_0187); return 0; error: printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",unit,ptr); outb(base_io+7,0x00); \ outb(base_io+3,data_0187); \ outb(base_io+4,data_0188); \ return EIO; } #endif /* DSI_SOFT_MODEM */ /* * support PnP cards if we are using 'em */ #if NPNP > 0 static struct siopnp_ids { u_long vend_id; char *id_str; } siopnp_ids[] = { { 0x5015f435, "MOT1550"}, { 0x8113b04e, "Supra1381"}, { 0x9012b04e, "Supra1290"}, { 0x11007256, "USR0011"}, { 0x30207256, "USR2030"}, { 0 } }; static char *siopnp_probe(u_long csn, u_long vend_id); static void siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); static u_long nsiopnp = NSIO; static struct pnp_device siopnp = { "siopnp", siopnp_probe, siopnp_attach, &nsiopnp, &tty_imask }; DATA_SET (pnpdevice_set, siopnp); static char * siopnp_probe(u_long csn, u_long vend_id) { struct siopnp_ids *ids; char *s = NULL; for(ids = siopnp_ids; ids->vend_id != 0; ids++) { if (vend_id == ids->vend_id) { s = ids->id_str; break; } } if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); if (d.enable == 0 || d.flags & 1) { printf("CSN %d is disabled.\n", csn); return (NULL); } } return (s); } static void siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d; struct isa_device *dvp; if (dev->id_unit >= NSIOTOT) return; if (read_pnp_parms(&d, 0) == 0) { printf("failed to read pnp parms\n"); return; } write_pnp_parms(&d, 0); enable_pnp_card(); dev->id_iobase = d.port[0]; dev->id_irq = (1 << d.irq[0]); dev->id_intr = siointr; dev->id_ri_flags = RI_FAST; dev->id_drq = -1; if (dev->id_driver == NULL) { dev->id_driver = &siodriver; dvp = find_isadev(isa_devtab_tty, &siodriver, 0); if (dvp != NULL) dev->id_id = dvp->id_id; } if ((dev->id_alive = sioprobe(dev)) != 0) sioattach(dev); else printf("sio%d: probe failed\n", dev->id_unit); } #endif diff --git a/sys/i386/isa/stallion.c b/sys/i386/isa/stallion.c index d12befc770c0..8ec866d289a1 100644 --- a/sys/i386/isa/stallion.c +++ b/sys/i386/isa/stallion.c @@ -1,3118 +1,3119 @@ /*****************************************************************************/ /* * stallion.c -- stallion multiport serial driver. * * Copyright (c) 1995-1996 Greg Ungerer (gerg@stallion.oz.au). * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Greg Ungerer. * 4. Neither the name of the author nor the names of any co-contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: stallion.c,v 1.19 1998/04/15 17:45:50 bde Exp $ + * $Id: stallion.c,v 1.20 1998/06/07 17:11:00 dfr Exp $ */ /*****************************************************************************/ #define TTYDEFCHARS 1 #include "opt_compat.h" #include #include #include #include #include #include #include #include #include #include #include #include "pci.h" #if NPCI > 0 #include #include #endif /*****************************************************************************/ /* * Define the version level of the kernel - so we can compile in the * appropriate bits of code. By default this will compile for a 2.1 * level kernel. */ #define VFREEBSD 220 #if VFREEBSD >= 220 #define STATIC static #else #define STATIC #endif /*****************************************************************************/ /* * Define different board types. At the moment I have only declared * those boards that this driver supports. But I will use the standard * "assigned" board numbers. In the future this driver will support * some of the other Stallion boards. Currently supported boards are * abbreviated as EIO = EasyIO and ECH = EasyConnection 8/32. */ #define BRD_EASYIO 20 #define BRD_ECH 21 #define BRD_ECHMC 22 #define BRD_ECHPCI 26 /* * When using the BSD "config" stuff there is no easy way to specifiy * a secondary IO address region. So it is hard wired here. Also the * shared interrupt information is hard wired here... */ static unsigned int stl_ioshared = 0x280; static unsigned int stl_irqshared = 0; /*****************************************************************************/ /* * Define important driver limitations. */ #define STL_MAXBRDS 8 #define STL_MAXPANELS 4 #define STL_PORTSPERPANEL 16 #define STL_PORTSPERBRD 64 /* * Define the important minor number break down bits. These have been * chosen to be "compatable" with the standard sio driver minor numbers. * Extra high bits are used to distinguish between boards. */ #define STL_CALLOUTDEV 0x80 #define STL_CTRLLOCK 0x40 #define STL_CTRLINIT 0x20 #define STL_CTRLDEV (STL_CTRLLOCK | STL_CTRLINIT) #define STL_MEMDEV 0x07000000 #define STL_DEFSPEED 9600 #define STL_DEFCFLAG (CS8 | CREAD | HUPCL) /* * I haven't really decided (or measured) what buffer sizes give * a good balance between performance and memory usage. These seem * to work pretty well... */ #define STL_RXBUFSIZE 2048 #define STL_TXBUFSIZE 2048 #define STL_TXBUFLOW (STL_TXBUFSIZE / 4) #define STL_RXBUFHIGH (3 * STL_RXBUFSIZE / 4) /*****************************************************************************/ /* * Define our local driver identity first. Set up stuff to deal with * all the local structures required by a serial tty driver. */ static const char stl_drvname[] = "stl"; static const char stl_longdrvname[] = "Stallion Multiport Serial Driver"; static const char stl_drvversion[] = "1.0.0"; static int stl_brdprobed[STL_MAXBRDS]; static int stl_nrbrds = 0; static int stl_doingtimeout = 0; static const char __file__[] = /*__FILE__*/ "stallion.c"; /* * Define global stats structures. Not used often, and can be * re-used for each stats call. */ static combrd_t stl_brdstats; static comstats_t stl_comstats; /*****************************************************************************/ /* * Define a set of structures to hold all the board/panel/port info * for our ports. These will be dynamically allocated as required. */ /* * Define a ring queue structure for each port. This will hold the * TX data waiting to be output. Characters are fed into this buffer * from the line discipline (or even direct from user space!) and * then fed into the UARTs during interrupts. Will use a clasic ring * queue here for this. The good thing about this type of ring queue * is that the head and tail pointers can be updated without interrupt * protection - since "write" code only needs to change the head, and * interrupt code only needs to change the tail. */ typedef struct { char *buf; char *endbuf; char *head; char *tail; } stlrq_t; /* * Port, panel and board structures to hold status info about each. * The board structure contains pointers to structures for each panel * connected to it, and in turn each panel structure contains pointers * for each port structure for each port on that panel. Note that * the port structure also contains the board and panel number that it * is associated with, this makes it (fairly) easy to get back to the * board/panel info for a port. Also note that the tty struct is at * the top of the structure, this is important, since the code uses * this fact to get the port struct pointer from the tty struct * pointer! */ typedef struct { struct tty tty; int portnr; int panelnr; int brdnr; int ioaddr; int uartaddr; int pagenr; int callout; int brklen; int dtrwait; int dotimestamp; int waitopens; int hotchar; unsigned int state; unsigned int hwid; unsigned int sigs; unsigned int rxignoremsk; unsigned int rxmarkmsk; unsigned long clk; struct termios initintios; struct termios initouttios; struct termios lockintios; struct termios lockouttios; struct timeval timestamp; comstats_t stats; stlrq_t tx; stlrq_t rx; stlrq_t rxstatus; } stlport_t; typedef struct { int panelnr; int brdnr; int pagenr; int nrports; int iobase; unsigned int hwid; unsigned int ackmask; stlport_t *ports[STL_PORTSPERPANEL]; } stlpanel_t; typedef struct { int brdnr; int brdtype; int unitid; int state; int nrpanels; int nrports; int irq; int irqtype; unsigned int ioaddr1; unsigned int ioaddr2; unsigned int iostatus; unsigned int ioctrl; unsigned int ioctrlval; unsigned int hwid; unsigned long clk; stlpanel_t *panels[STL_MAXPANELS]; stlport_t *ports[STL_PORTSPERBRD]; } stlbrd_t; static stlbrd_t *stl_brds[STL_MAXBRDS]; /* * Per board state flags. Used with the state field of the board struct. * Not really much here yet! */ #define BRD_FOUND 0x1 /* * Define the port structure state flags. These set of flags are * modified at interrupt time - so setting and reseting them needs * to be atomic. */ #define ASY_TXLOW 0x1 #define ASY_RXDATA 0x2 #define ASY_DCDCHANGE 0x4 #define ASY_DTRWAIT 0x8 #define ASY_RTSFLOW 0x10 #define ASY_RTSFLOWMODE 0x20 #define ASY_CTSFLOWMODE 0x40 #define ASY_ACTIVE (ASY_TXLOW | ASY_RXDATA | ASY_DCDCHANGE) /* * Define an array of board names as printable strings. Handy for * referencing boards when printing trace and stuff. */ static char *stl_brdnames[] = { (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, (char *) NULL, "EasyIO", "EC8/32-AT", "EC8/32-MC", (char *) NULL, (char *) NULL, (char *) NULL, "EC8/32-PCI", }; /*****************************************************************************/ /* * Hardware ID bits for the EasyIO and ECH boards. These defines apply * to the directly accessable io ports of these boards (not the cd1400 * uarts - they are in scd1400.h). */ #define EIO_8PORTRS 0x04 #define EIO_4PORTRS 0x05 #define EIO_8PORTDI 0x00 #define EIO_8PORTM 0x06 #define EIO_IDBITMASK 0x07 #define EIO_INTRPEND 0x08 #define EIO_INTEDGE 0x00 #define EIO_INTLEVEL 0x08 #define ECH_ID 0xa0 #define ECH_IDBITMASK 0xe0 #define ECH_BRDENABLE 0x08 #define ECH_BRDDISABLE 0x00 #define ECH_INTENABLE 0x01 #define ECH_INTDISABLE 0x00 #define ECH_INTLEVEL 0x02 #define ECH_INTEDGE 0x00 #define ECH_INTRPEND 0x01 #define ECH_BRDRESET 0x01 #define ECHMC_INTENABLE 0x01 #define ECHMC_BRDRESET 0x02 #define ECH_PNLSTATUS 2 #define ECH_PNL16PORT 0x20 #define ECH_PNLIDMASK 0x07 #define ECH_PNLINTRPEND 0x80 #define ECH_ADDR2MASK 0x1e0 #define EIO_CLK 25000000 #define EIO_CLK8M 20000000 #define ECH_CLK EIO_CLK /* * Define the offsets within the register bank for all io registers. * These io address offsets are common to both the EIO and ECH. */ #define EREG_ADDR 0 #define EREG_DATA 4 #define EREG_RXACK 5 #define EREG_TXACK 6 #define EREG_MDACK 7 #define EREG_BANKSIZE 8 /* * Define the PCI vendor and device id for ECH8/32-PCI. */ #define STL_PCIDEVID 0xd001100b /* * Define the vector mapping bits for the programmable interrupt board * hardware. These bits encode the interrupt for the board to use - it * is software selectable (except the EIO-8M). */ static unsigned char stl_vecmap[] = { 0xff, 0xff, 0xff, 0x04, 0x06, 0x05, 0xff, 0x07, 0xff, 0xff, 0x00, 0x02, 0x01, 0xff, 0xff, 0x03 }; /* * Set up enable and disable macros for the ECH boards. They require * the secondary io address space to be activated and deactivated. * This way all ECH boards can share their secondary io region. * If this is an ECH-PCI board then also need to set the page pointer * to point to the correct page. */ #define BRDENABLE(brdnr,pagenr) \ if (stl_brds[(brdnr)]->brdtype == BRD_ECH) \ outb(stl_brds[(brdnr)]->ioctrl, \ (stl_brds[(brdnr)]->ioctrlval | ECH_BRDENABLE));\ else if (stl_brds[(brdnr)]->brdtype == BRD_ECHPCI) \ outb(stl_brds[(brdnr)]->ioctrl, (pagenr)); #define BRDDISABLE(brdnr) \ if (stl_brds[(brdnr)]->brdtype == BRD_ECH) \ outb(stl_brds[(brdnr)]->ioctrl, \ (stl_brds[(brdnr)]->ioctrlval | ECH_BRDDISABLE)); /* * Define the cd1400 baud rate clocks. These are used when calculating * what clock and divisor to use for the required baud rate. Also * define the maximum baud rate allowed, and the default base baud. */ static int stl_cd1400clkdivs[] = { CD1400_CLK0, CD1400_CLK1, CD1400_CLK2, CD1400_CLK3, CD1400_CLK4 }; #define STL_MAXBAUD 230400 /*****************************************************************************/ /* * Define macros to extract a brd and port number from a minor number. * This uses the extended minor number range in the upper 2 bytes of * the device number. This gives us plenty of minor numbers to play * with... */ #define MKDEV2BRD(m) (((m) & 0x00700000) >> 20) #define MKDEV2PORT(m) (((m) & 0x1f) | (((m) & 0x00010000) >> 11)) /* * Define some handy local macros... */ #ifndef MIN #define MIN(a,b) (((a) <= (b)) ? (a) : (b)) #endif /*****************************************************************************/ /* * Declare all those functions in this driver! First up is the set of * externally visible functions. */ static int stlprobe(struct isa_device *idp); static int stlattach(struct isa_device *idp); STATIC d_open_t stlopen; STATIC d_close_t stlclose; STATIC d_read_t stlread; STATIC d_write_t stlwrite; STATIC d_ioctl_t stlioctl; STATIC d_stop_t stlstop; #if VFREEBSD >= 220 STATIC d_devtotty_t stldevtotty; #else struct tty *stldevtotty(dev_t dev); #endif /* * Internal function prototypes. */ static stlport_t *stl_dev2port(dev_t dev); static int stl_findfreeunit(void); static int stl_rawopen(stlport_t *portp); static int stl_rawclose(stlport_t *portp); static int stl_param(struct tty *tp, struct termios *tiosp); static void stl_start(struct tty *tp); static void stl_ttyoptim(stlport_t *portp, struct termios *tiosp); static void stl_dotimeout(void); static void stl_poll(void *arg); static void stl_rxprocess(stlport_t *portp); static void stl_dtrwakeup(void *arg); static int stl_brdinit(stlbrd_t *brdp); static int stl_initeio(stlbrd_t *brdp); static int stl_initech(stlbrd_t *brdp); static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp); static __inline void stl_txisr(stlpanel_t *panelp, int ioaddr); static __inline void stl_rxisr(stlpanel_t *panelp, int ioaddr); static __inline void stl_mdmisr(stlpanel_t *panelp, int ioaddr); static void stl_setreg(stlport_t *portp, int regnr, int value); static int stl_getreg(stlport_t *portp, int regnr); static int stl_updatereg(stlport_t *portp, int regnr, int value); static int stl_getsignals(stlport_t *portp); static void stl_setsignals(stlport_t *portp, int dtr, int rts); static void stl_flowcontrol(stlport_t *portp, int hw, int sw); static void stl_ccrwait(stlport_t *portp); static void stl_enablerxtx(stlport_t *portp, int rx, int tx); static void stl_startrxtx(stlport_t *portp, int rx, int tx); static void stl_disableintrs(stlport_t *portp); static void stl_sendbreak(stlport_t *portp, long len); static void stl_flush(stlport_t *portp, int flag); static int stl_memioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p); static int stl_getbrdstats(caddr_t data); static int stl_getportstats(stlport_t *portp, caddr_t data); static int stl_clrportstats(stlport_t *portp, caddr_t data); static stlport_t *stl_getport(int brdnr, int panelnr, int portnr); #if NPCI > 0 static char *stlpciprobe(pcici_t tag, pcidi_t type); static void stlpciattach(pcici_t tag, int unit); static void stlpciintr(void * arg); #endif /*****************************************************************************/ /* * Declare the driver isa structure. */ struct isa_driver stldriver = { stlprobe, stlattach, "stl" }; /*****************************************************************************/ #if NPCI > 0 /* * Declare the driver pci structure. */ static unsigned long stl_count; static struct pci_device stlpcidriver = { "stl", stlpciprobe, stlpciattach, &stl_count, NULL, }; DATA_SET (pcidevice_set, stlpcidriver); #endif /*****************************************************************************/ #if VFREEBSD >= 220 /* * FreeBSD-2.2+ kernel linkage. */ #define CDEV_MAJOR 72 - -static struct cdevsw stl_cdevsw = - { stlopen, stlclose, stlread, stlwrite, - stlioctl, stlstop, noreset, stldevtotty, - ttpoll, nommap, NULL, "stl", NULL, -1 }; +static struct cdevsw stl_cdevsw = { + stlopen, stlclose, stlread, stlwrite, + stlioctl, stlstop, noreset, stldevtotty, + ttpoll, nommap, NULL, "stl", + NULL, -1, nodump, nopsize, + D_TTY, +}; static stl_devsw_installed = 0; static void stl_drvinit(void *unused) { dev_t dev; if (! stl_devsw_installed ) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &stl_cdevsw, NULL); stl_devsw_installed = 1; } } SYSINIT(sidev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,stl_drvinit,NULL) #endif /*****************************************************************************/ /* * Probe for some type of EasyIO or EasyConnection 8/32 board at * the supplied address. All we do is check if we can find the * board ID for the board... (Note, PCI boards not checked here, * they are done in the stlpciprobe() routine). */ static int stlprobe(struct isa_device *idp) { unsigned int status; #if DEBUG printf("stlprobe(idp=%x): unit=%d iobase=%x\n", (int) idp, idp->id_unit, idp->id_iobase); #endif if (idp->id_unit > STL_MAXBRDS) return(0); status = inb(idp->id_iobase + 1); if ((status & ECH_IDBITMASK) == ECH_ID) { stl_brdprobed[idp->id_unit] = BRD_ECH; return(1); } status = inb(idp->id_iobase + 2); switch (status & EIO_IDBITMASK) { case EIO_8PORTRS: case EIO_8PORTM: case EIO_8PORTDI: case EIO_4PORTRS: stl_brdprobed[idp->id_unit] = BRD_EASYIO; return(1); default: break; } return(0); } /*****************************************************************************/ /* * Find an available internal board number (unit number). The problem * is that the same unit numbers can be assigned to different boards * detected during the ISA and PCI initialization phases. */ static int stl_findfreeunit() { int i; for (i = 0; (i < STL_MAXBRDS); i++) if (stl_brds[i] == (stlbrd_t *) NULL) break; return((i >= STL_MAXBRDS) ? -1 : i); } /*****************************************************************************/ /* * Allocate resources for and initialize the specified board. */ static int stlattach(struct isa_device *idp) { stlbrd_t *brdp; #if DEBUG printf("stlattach(idp=%x): unit=%d iobase=%x\n", idp, idp->id_unit, idp->id_iobase); #endif brdp = (stlbrd_t *) malloc(sizeof(stlbrd_t), M_TTYS, M_NOWAIT); if (brdp == (stlbrd_t *) NULL) { printf("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t)); return(0); } bzero(brdp, sizeof(stlbrd_t)); if ((brdp->brdnr = stl_findfreeunit()) < 0) { printf("STALLION: too many boards found, max=%d\n", STL_MAXBRDS); return(0); } if (brdp->brdnr >= stl_nrbrds) stl_nrbrds = brdp->brdnr + 1; brdp->unitid = idp->id_unit; brdp->brdtype = stl_brdprobed[idp->id_unit]; brdp->ioaddr1 = idp->id_iobase; brdp->ioaddr2 = stl_ioshared; brdp->irq = ffs(idp->id_irq) - 1; brdp->irqtype = stl_irqshared; stl_brdinit(brdp); return(1); } /*****************************************************************************/ #if NPCI > 0 /* * Probe specifically for the PCI boards. We need to be a little * carefull here, since it looks sort like a Nat Semi IDE chip... */ char *stlpciprobe(pcici_t tag, pcidi_t type) { unsigned long class; #if DEBUG printf("stlpciprobe(tag=%x,type=%x)\n", (int) &tag, (int) type); #endif switch (type) { case STL_PCIDEVID: break; default: return((char *) NULL); } class = pci_conf_read(tag, PCI_CLASS_REG); if ((class & PCI_CLASS_MASK) == PCI_CLASS_MASS_STORAGE) return((char *) NULL); return("Stallion EasyConnection 8/32-PCI"); } /*****************************************************************************/ /* * Allocate resources for and initialize the specified PCI board. */ void stlpciattach(pcici_t tag, int unit) { stlbrd_t *brdp; #if DEBUG printf("stlpciattach(tag=%x,unit=%x)\n", (int) &tag, unit); #endif brdp = (stlbrd_t *) malloc(sizeof(stlbrd_t), M_TTYS, M_NOWAIT); if (brdp == (stlbrd_t *) NULL) { printf("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlbrd_t)); return; } bzero(brdp, sizeof(stlbrd_t)); if ((unit < 0) || (unit > STL_MAXBRDS)) { printf("STALLION: bad PCI board unit number=%d\n", unit); return; } /* * Allocate us a new driver unique unit number. */ if ((brdp->brdnr = stl_findfreeunit()) < 0) { printf("STALLION: too many boards found, max=%d\n", STL_MAXBRDS); return; } if (brdp->brdnr >= stl_nrbrds) stl_nrbrds = brdp->brdnr + 1; brdp->unitid = 0; brdp->brdtype = BRD_ECHPCI; brdp->ioaddr1 = ((unsigned int) pci_conf_read(tag, 0x14)) & 0xfffc; brdp->ioaddr2 = ((unsigned int) pci_conf_read(tag, 0x10)) & 0xfffc; brdp->irq = ((int) pci_conf_read(tag, 0x3c)) & 0xff; brdp->irqtype = 0; if (pci_map_int(tag, stlpciintr, (void *) NULL, &tty_imask) == 0) { printf("STALLION: failed to map interrupt irq=%d for unit=%d\n", brdp->irq, brdp->brdnr); return; } #if 0 printf("%s(%d): ECH-PCI iobase=%x iopage=%x irq=%d\n", __file__, __LINE__, brdp->ioaddr2, brdp->ioaddr1, brdp->irq); #endif stl_brdinit(brdp); } #endif /*****************************************************************************/ STATIC int stlopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp; stlport_t *portp; int error, callout, x; #if DEBUG printf("stlopen(dev=%x,flag=%x,mode=%x,p=%x)\n", (int) dev, flag, mode, (int) p); #endif /* * Firstly check if the supplied device number is a valid device. */ if (dev & STL_MEMDEV) return(0); portp = stl_dev2port(dev); if (portp == (stlport_t *) NULL) return(ENXIO); tp = &portp->tty; callout = minor(dev) & STL_CALLOUTDEV; error = 0; x = spltty(); stlopen_restart: /* * Wait here for the DTR drop timeout period to expire. */ while (portp->state & ASY_DTRWAIT) { error = tsleep(&portp->dtrwait, (TTIPRI | PCATCH), "stldtr", 0); if (error) goto stlopen_end; } /* * We have a valid device, so now we check if it is already open. * If not then initialize the port hardware and set up the tty * struct as required. */ if ((tp->t_state & TS_ISOPEN) == 0) { tp->t_oproc = stl_start; tp->t_param = stl_param; tp->t_dev = dev; tp->t_termios = callout ? portp->initouttios : portp->initintios; stl_rawopen(portp); - ttsetwater(tp); if ((portp->sigs & TIOCM_CD) || callout) (*linesw[tp->t_line].l_modem)(tp, 1); } else { if (callout) { if (portp->callout == 0) { error = EBUSY; goto stlopen_end; } } else { if (portp->callout != 0) { if (flag & O_NONBLOCK) { error = EBUSY; goto stlopen_end; } error = tsleep(&portp->callout, (TTIPRI | PCATCH), "stlcall", 0); if (error) goto stlopen_end; goto stlopen_restart; } } if ((tp->t_state & TS_XCLUDE) && (p->p_ucred->cr_uid != 0)) { error = EBUSY; goto stlopen_end; } } /* * If this port is not the callout device and we do not have carrier * then we need to sleep, waiting for it to be asserted. */ if (((tp->t_state & TS_CARR_ON) == 0) && !callout && ((tp->t_cflag & CLOCAL) == 0) && ((flag & O_NONBLOCK) == 0)) { portp->waitopens++; error = tsleep(TSA_CARR_ON(tp), (TTIPRI | PCATCH), "stldcd", 0); portp->waitopens--; if (error) goto stlopen_end; goto stlopen_restart; } /* * Open the line discipline. */ error = (*linesw[tp->t_line].l_open)(dev, tp); stl_ttyoptim(portp, &tp->t_termios); if ((tp->t_state & TS_ISOPEN) && callout) portp->callout = 1; /* * If for any reason we get to here and the port is not actually * open then close of the physical hardware - no point leaving it * active when the open failed... */ stlopen_end: splx(x); if (((tp->t_state & TS_ISOPEN) == 0) && (portp->waitopens == 0)) stl_rawclose(portp); return(error); } /*****************************************************************************/ STATIC int stlclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp; stlport_t *portp; int x; #if DEBUG printf("stlclose(dev=%x,flag=%x,mode=%x,p=%x)\n", dev, flag, mode, p); #endif if (dev & STL_MEMDEV) return(0); portp = stl_dev2port(dev); if (portp == (stlport_t *) NULL) return(ENXIO); tp = &portp->tty; x = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); stl_ttyoptim(portp, &tp->t_termios); stl_rawclose(portp); ttyclose(tp); splx(x); return(0); } /*****************************************************************************/ STATIC int stlread(dev_t dev, struct uio *uiop, int flag) { stlport_t *portp; #if DEBUG printf("stlread(dev=%x,uiop=%x,flag=%x)\n", dev, uiop, flag); #endif portp = stl_dev2port(dev); if (portp == (stlport_t *) NULL) return(ENODEV); return((*linesw[portp->tty.t_line].l_read)(&portp->tty, uiop, flag)); } /*****************************************************************************/ #if VFREEBSD >= 220 STATIC void stlstop(struct tty *tp, int rw) { #if DEBUG printf("stlstop(tp=%x,rw=%x)\n", (int) tp, rw); #endif stl_flush((stlport_t *) tp, rw); } #else STATIC int stlstop(struct tty *tp, int rw) { #if DEBUG printf("stlstop(tp=%x,rw=%x)\n", (int) tp, rw); #endif stl_flush((stlport_t *) tp, rw); return(0); } #endif /*****************************************************************************/ STATIC struct tty *stldevtotty(dev_t dev) { #if DEBUG printf("stldevtotty(dev=%x)\n", dev); #endif return((struct tty *) stl_dev2port(dev)); } /*****************************************************************************/ STATIC int stlwrite(dev_t dev, struct uio *uiop, int flag) { stlport_t *portp; #if DEBUG printf("stlwrite(dev=%x,uiop=%x,flag=%x)\n", dev, uiop, flag); #endif portp = stl_dev2port(dev); if (portp == (stlport_t *) NULL) return(ENODEV); return((*linesw[portp->tty.t_line].l_write)(&portp->tty, uiop, flag)); } /*****************************************************************************/ STATIC int stlioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { struct termios *newtios, *localtios; struct tty *tp; stlport_t *portp; int error, i, x; #if DEBUG printf("stlioctl(dev=%x,cmd=%x,data=%x,flag=%x,p=%x)\n", dev, cmd, data, flag, p); #endif dev = minor(dev); if (dev & STL_MEMDEV) return(stl_memioctl(dev, cmd, data, flag, p)); portp = stl_dev2port(dev); if (portp == (stlport_t *) NULL) return(ENODEV); tp = &portp->tty; error = 0; /* * First up handle ioctls on the control devices. */ if (dev & STL_CTRLDEV) { if ((dev & STL_CTRLDEV) == STL_CTRLINIT) localtios = (dev & STL_CALLOUTDEV) ? &portp->initouttios : &portp->initintios; else if ((dev & STL_CTRLDEV) == STL_CTRLLOCK) localtios = (dev & STL_CALLOUTDEV) ? &portp->lockouttios : &portp->lockintios; else return(ENODEV); switch (cmd) { case TIOCSETA: if ((error = suser(p->p_ucred, &p->p_acflag)) == 0) *localtios = *((struct termios *) data); break; case TIOCGETA: *((struct termios *) data) = *localtios; break; case TIOCGETD: *((int *) data) = TTYDISC; break; case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); break; default: error = ENOTTY; break; } return(error); } /* * Deal with 4.3 compatability issues if we have too... */ #if defined(COMPAT_43) || defined(COMPAT_SUNOS) if (1) { struct termios tios; int oldcmd; tios = tp->t_termios; oldcmd = cmd; if ((error = ttsetcompat(tp, &cmd, data, &tios))) return(error); if (cmd != oldcmd) data = (caddr_t) &tios; } #endif /* * Carry out some pre-cmd processing work first... * Hmmm, not so sure we want this, disable for now... */ if ((cmd == TIOCSETA) || (cmd == TIOCSETAW) || (cmd == TIOCSETAF)) { newtios = (struct termios *) data; localtios = (dev & STL_CALLOUTDEV) ? &portp->lockouttios : &portp->lockintios; newtios->c_iflag = (tp->t_iflag & localtios->c_iflag) | (newtios->c_iflag & ~localtios->c_iflag); newtios->c_oflag = (tp->t_oflag & localtios->c_oflag) | (newtios->c_oflag & ~localtios->c_oflag); newtios->c_cflag = (tp->t_cflag & localtios->c_cflag) | (newtios->c_cflag & ~localtios->c_cflag); newtios->c_lflag = (tp->t_lflag & localtios->c_lflag) | (newtios->c_lflag & ~localtios->c_lflag); for (i = 0; (i < NCCS); i++) { if (localtios->c_cc[i] != 0) newtios->c_cc[i] = tp->t_cc[i]; } if (localtios->c_ispeed != 0) newtios->c_ispeed = tp->t_ispeed; if (localtios->c_ospeed != 0) newtios->c_ospeed = tp->t_ospeed; } /* * Call the line discipline and the common command processing to * process this command (if they can). */ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return(error); x = spltty(); error = ttioctl(tp, cmd, data, flag); stl_ttyoptim(portp, &tp->t_termios); if (error != ENOIOCTL) { splx(x); return(error); } error = 0; /* * Process local commands here. These are all commands that only we * can take care of (they all rely on actually doing something special * to the actual hardware). */ switch (cmd) { case TIOCSBRK: stl_sendbreak(portp, -1); break; case TIOCCBRK: stl_sendbreak(portp, -2); break; case TIOCSDTR: stl_setsignals(portp, 1, -1); break; case TIOCCDTR: stl_setsignals(portp, 0, -1); break; case TIOCMSET: i = *((int *) data); stl_setsignals(portp, ((i & TIOCM_DTR) ? 1 : 0), ((i & TIOCM_RTS) ? 1 : 0)); break; case TIOCMBIS: i = *((int *) data); stl_setsignals(portp, ((i & TIOCM_DTR) ? 1 : -1), ((i & TIOCM_RTS) ? 1 : -1)); break; case TIOCMBIC: i = *((int *) data); stl_setsignals(portp, ((i & TIOCM_DTR) ? 0 : -1), ((i & TIOCM_RTS) ? 0 : -1)); break; case TIOCMGET: *((int *) data) = (stl_getsignals(portp) | TIOCM_LE); break; case TIOCMSDTRWAIT: if ((error = suser(p->p_ucred, &p->p_acflag)) == 0) portp->dtrwait = *((int *) data) * hz / 100; break; case TIOCMGDTRWAIT: *((int *) data) = portp->dtrwait * 100 / hz; break; case TIOCTIMESTAMP: portp->dotimestamp = 1; *((struct timeval *) data) = portp->timestamp; break; default: error = ENOTTY; break; } splx(x); return(error); } /*****************************************************************************/ /* * Convert the specified minor device number into a port struct * pointer. Return NULL if the device number is not a valid port. */ STATIC stlport_t *stl_dev2port(dev_t dev) { stlbrd_t *brdp; brdp = stl_brds[MKDEV2BRD(dev)]; if (brdp == (stlbrd_t *) NULL) return((stlport_t *) NULL); return(brdp->ports[MKDEV2PORT(dev)]); } /*****************************************************************************/ /* * Initialize the port hardware. This involves enabling the transmitter * and receiver, setting the port configuration, and setting the initial * signal state. */ static int stl_rawopen(stlport_t *portp) { #if DEBUG printf("stl_rawopen(portp=%x): brdnr=%d panelnr=%d portnr=%d\n", portp, portp->brdnr, portp->panelnr, portp->portnr); #endif stl_param(&portp->tty, &portp->tty.t_termios); portp->sigs = stl_getsignals(portp); stl_setsignals(portp, 1, 1); stl_enablerxtx(portp, 1, 1); stl_startrxtx(portp, 1, 0); return(0); } /*****************************************************************************/ /* * Shutdown the hardware of a port. Disable its transmitter and * receiver, and maybe drop signals if appropriate. */ static int stl_rawclose(stlport_t *portp) { struct tty *tp; #if DEBUG printf("stl_rawclose(portp=%x): brdnr=%d panelnr=%d portnr=%d\n", portp, portp->brdnr, portp->panelnr, portp->portnr); #endif tp = &portp->tty; stl_disableintrs(portp); stl_enablerxtx(portp, 0, 0); stl_flush(portp, (FWRITE | FREAD)); if (tp->t_cflag & HUPCL) { stl_setsignals(portp, 0, 0); if (portp->dtrwait != 0) { portp->state |= ASY_DTRWAIT; timeout(stl_dtrwakeup, portp, portp->dtrwait); } } portp->callout = 0; portp->brklen = 0; portp->state &= ~(ASY_ACTIVE | ASY_RTSFLOW); wakeup(&portp->callout); wakeup(TSA_CARR_ON(tp)); return(0); } /*****************************************************************************/ /* * Clear the DTR waiting flag, and wake up any sleepers waiting for * DTR wait period to finish. */ static void stl_dtrwakeup(void *arg) { stlport_t *portp; portp = (stlport_t *) arg; portp->state &= ~ASY_DTRWAIT; wakeup(&portp->dtrwait); } /*****************************************************************************/ /* * Start (or continue) the transfer of TX data on this port. If the * port is not currently busy then load up the interrupt ring queue * buffer and kick of the transmitter. If the port is running low on * TX data then refill the ring queue. This routine is also used to * activate input flow control! */ static void stl_start(struct tty *tp) { stlport_t *portp; unsigned int len, stlen; char *head, *tail; int count, x; portp = (stlport_t *) tp; #if DEBUG printf("stl_start(tp=%x): brdnr=%d portnr=%d\n", (int) tp, portp->brdnr, portp->portnr); #endif x = spltty(); /* * Check if the ports input has been blocked, and take appropriate action. * Not very often do we really need to do anything, so make it quick. */ if (tp->t_state & TS_TBLOCK) { if ((portp->state & ASY_RTSFLOW) == 0) stl_flowcontrol(portp, 0, -1); } else { if (portp->state & ASY_RTSFLOW) stl_flowcontrol(portp, 1, -1); } #if VFREEBSD == 205 /* * Check if the output cooked clist buffers are near empty, wake up * the line discipline to fill it up. */ if (tp->t_outq.c_cc <= tp->t_lowat) { if (tp->t_state & TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; wakeup(&tp->t_outq); } selwakeup(&tp->t_wsel); } #endif if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { splx(x); return; } /* * Copy data from the clists into the interrupt ring queue. This will * require at most 2 copys... What we do is calculate how many chars * can fit into the ring queue, and how many can fit in 1 copy. If after * the first copy there is still more room then do the second copy. * The beauty of this type of ring queue is that we do not need to * spl protect our-selves, since we only ever update the head pointer, * and the interrupt routine only ever updates the tail pointer. */ if (tp->t_outq.c_cc != 0) { head = portp->tx.head; tail = portp->tx.tail; if (head >= tail) { len = STL_TXBUFSIZE - (head - tail) - 1; stlen = portp->tx.endbuf - head; } else { len = tail - head - 1; stlen = len; } if (len > 0) { stlen = MIN(len, stlen); count = q_to_b(&tp->t_outq, head, stlen); len -= count; head += count; if (head >= portp->tx.endbuf) { head = portp->tx.buf; if (len > 0) { stlen = q_to_b(&tp->t_outq, head, len); head += stlen; count += stlen; } } portp->tx.head = head; if (count > 0) stl_startrxtx(portp, -1, 1); } /* * If we sent something, make sure we are called again. */ tp->t_state |= TS_BUSY; } #if VFREEBSD != 205 /* * Do any writer wakeups. */ ttwwakeup(tp); #endif splx(x); } /*****************************************************************************/ static void stl_flush(stlport_t *portp, int flag) { char *head, *tail; int len, x; #if DEBUG printf("stl_flush(portp=%x,flag=%x)\n", (int) portp, flag); #endif if (portp == (stlport_t *) NULL) return; x = spltty(); if (flag & FWRITE) { BRDENABLE(portp->brdnr, portp->pagenr); stl_setreg(portp, CAR, (portp->portnr & 0x03)); stl_ccrwait(portp); stl_setreg(portp, CCR, CCR_TXFLUSHFIFO); stl_ccrwait(portp); portp->tx.tail = portp->tx.head; BRDDISABLE(portp->brdnr); } /* * The only thing to watch out for when flushing the read side is * the RX status buffer. The interrupt code relys on the status * bytes as being zeroed all the time (it does not bother setting * a good char status to 0, it expects that it already will be). * We also need to un-flow the RX channel if flow control was * active. */ if (flag & FREAD) { head = portp->rx.head; tail = portp->rx.tail; if (head != tail) { if (head >= tail) { len = head - tail; } else { len = portp->rx.endbuf - tail; bzero(portp->rxstatus.buf, (head - portp->rx.buf)); } bzero((tail + STL_RXBUFSIZE), len); portp->rx.tail = head; } if ((portp->state & ASY_RTSFLOW) && ((portp->tty.t_state & TS_TBLOCK) == 0)) stl_flowcontrol(portp, 1, -1); } splx(x); } /*****************************************************************************/ /* * These functions get/set/update the registers of the cd1400 UARTs. * Access to the cd1400 registers is via an address/data io port pair. * (Maybe should make this inline...) */ static int stl_getreg(stlport_t *portp, int regnr) { outb(portp->ioaddr, (regnr + portp->uartaddr)); return(inb(portp->ioaddr + EREG_DATA)); } /*****************************************************************************/ static void stl_setreg(stlport_t *portp, int regnr, int value) { outb(portp->ioaddr, (regnr + portp->uartaddr)); outb((portp->ioaddr + EREG_DATA), value); } /*****************************************************************************/ static int stl_updatereg(stlport_t *portp, int regnr, int value) { outb(portp->ioaddr, (regnr + portp->uartaddr)); if (inb(portp->ioaddr + EREG_DATA) != value) { outb((portp->ioaddr + EREG_DATA), value); return(1); } return(0); } /*****************************************************************************/ /* * Wait for the command register to be ready. We will poll this, since * it won't usually take too long to be ready, and it is only really * used for non-critical actions. */ static void stl_ccrwait(stlport_t *portp) { int i; for (i = 0; (i < CCR_MAXWAIT); i++) { if (stl_getreg(portp, CCR) == 0) { return; } } printf("STALLION: cd1400 device not responding, brd=%d panel=%d" "port=%d\n", portp->brdnr, portp->panelnr, portp->portnr); } /*****************************************************************************/ /* * Transmit interrupt handler. This has gotta be fast! Handling TX * chars is pretty simple, stuff as many as possible from the TX buffer * into the cd1400 FIFO. Must also handle TX breaks here, since they * are embedded as commands in the data stream. Oh no, had to use a goto! * This could be optimized more, will do when I get time... * In practice it is possible that interrupts are enabled but that the * port has been hung up. Need to handle not having any TX buffer here, * this is done by using the side effect that head and tail will also * be NULL if the buffer has been freed. */ static __inline void stl_txisr(stlpanel_t *panelp, int ioaddr) { stlport_t *portp; int len, stlen; char *head, *tail; unsigned char ioack, srer; #if DEBUG printf("stl_txisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); #endif ioack = inb(ioaddr + EREG_TXACK); if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPTX)) { printf("STALLION: bad TX interrupt ack value=%x\n", ioack); return; } portp = panelp->ports[(ioack >> 3)]; /* * Unfortunately we need to handle breaks in the data stream, since * this is the only way to generate them on the cd1400. Do it now if * a break is to be sent. Some special cases here: brklen is -1 then * start sending an un-timed break, if brklen is -2 then stop sending * an un-timed break, if brklen is -3 then we have just sent an * un-timed break and do not want any data to go out, if brklen is -4 * then a break has just completed so clean up the port settings. */ if (portp->brklen != 0) { if (portp->brklen >= -1) { outb(ioaddr, (TDR + portp->uartaddr)); outb((ioaddr + EREG_DATA), ETC_CMD); outb((ioaddr + EREG_DATA), ETC_STARTBREAK); if (portp->brklen > 0) { outb((ioaddr + EREG_DATA), ETC_CMD); outb((ioaddr + EREG_DATA), ETC_DELAY); outb((ioaddr + EREG_DATA), portp->brklen); outb((ioaddr + EREG_DATA), ETC_CMD); outb((ioaddr + EREG_DATA), ETC_STOPBREAK); portp->brklen = -4; } else { portp->brklen = -3; } } else if (portp->brklen == -2) { outb(ioaddr, (TDR + portp->uartaddr)); outb((ioaddr + EREG_DATA), ETC_CMD); outb((ioaddr + EREG_DATA), ETC_STOPBREAK); portp->brklen = -4; } else if (portp->brklen == -3) { outb(ioaddr, (SRER + portp->uartaddr)); srer = inb(ioaddr + EREG_DATA); srer &= ~(SRER_TXDATA | SRER_TXEMPTY); outb((ioaddr + EREG_DATA), srer); } else { outb(ioaddr, (COR2 + portp->uartaddr)); outb((ioaddr + EREG_DATA), (inb(ioaddr + EREG_DATA) & ~COR2_ETC)); portp->brklen = 0; } goto stl_txalldone; } head = portp->tx.head; tail = portp->tx.tail; len = (head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head)); if ((len == 0) || ((len < STL_TXBUFLOW) && ((portp->state & ASY_TXLOW) == 0))) { portp->state |= ASY_TXLOW; stl_dotimeout(); } if (len == 0) { outb(ioaddr, (SRER + portp->uartaddr)); srer = inb(ioaddr + EREG_DATA); if (srer & SRER_TXDATA) { srer = (srer & ~SRER_TXDATA) | SRER_TXEMPTY; } else { srer &= ~(SRER_TXDATA | SRER_TXEMPTY); portp->tty.t_state &= ~TS_BUSY; } outb((ioaddr + EREG_DATA), srer); } else { len = MIN(len, CD1400_TXFIFOSIZE); portp->stats.txtotal += len; stlen = MIN(len, (portp->tx.endbuf - tail)); outb(ioaddr, (TDR + portp->uartaddr)); outsb((ioaddr + EREG_DATA), tail, stlen); len -= stlen; tail += stlen; if (tail >= portp->tx.endbuf) tail = portp->tx.buf; if (len > 0) { outsb((ioaddr + EREG_DATA), tail, len); tail += len; } portp->tx.tail = tail; } stl_txalldone: outb(ioaddr, (EOSRR + portp->uartaddr)); outb((ioaddr + EREG_DATA), 0); } /*****************************************************************************/ /* * Receive character interrupt handler. Determine if we have good chars * or bad chars and then process appropriately. Good chars are easy * just shove the lot into the RX buffer and set all status bytes to 0. * If a bad RX char then process as required. This routine needs to be * fast! */ static __inline void stl_rxisr(stlpanel_t *panelp, int ioaddr) { stlport_t *portp; struct tty *tp; unsigned int ioack, len, buflen, stlen; unsigned char status; char ch; char *head, *tail; static char unwanted[CD1400_RXFIFOSIZE]; #if DEBUG printf("stl_rxisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); #endif ioack = inb(ioaddr + EREG_RXACK); if ((ioack & panelp->ackmask) != 0) { printf("STALLION: bad RX interrupt ack value=%x\n", ioack); return; } portp = panelp->ports[(ioack >> 3)]; tp = &portp->tty; /* * First up, caluclate how much room there is in the RX ring queue. * We also want to keep track of the longest possible copy length, * this has to allow for the wrapping of the ring queue. */ head = portp->rx.head; tail = portp->rx.tail; if (head >= tail) { buflen = STL_RXBUFSIZE - (head - tail) - 1; stlen = portp->rx.endbuf - head; } else { buflen = tail - head - 1; stlen = buflen; } /* * Check if the input buffer is near full. If so then we should take * some flow control action... It is very easy to do hardware and * software flow control from here since we have the port selected on * the UART. */ if (buflen <= (STL_RXBUFSIZE - STL_RXBUFHIGH)) { if (((portp->state & ASY_RTSFLOW) == 0) && (portp->state & ASY_RTSFLOWMODE)) { portp->state |= ASY_RTSFLOW; stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) & 0xf0)); stl_setreg(portp, MSVR2, 0); portp->stats.rxrtsoff++; } } /* * OK we are set, process good data... If the RX ring queue is full * just chuck the chars - don't leave them in the UART. */ if ((ioack & ACK_TYPMASK) == ACK_TYPRXGOOD) { outb(ioaddr, (RDCR + portp->uartaddr)); len = inb(ioaddr + EREG_DATA); if (buflen == 0) { outb(ioaddr, (RDSR + portp->uartaddr)); insb((ioaddr + EREG_DATA), &unwanted[0], len); portp->stats.rxlost += len; portp->stats.rxtotal += len; } else { len = MIN(len, buflen); portp->stats.rxtotal += len; stlen = MIN(len, stlen); if (len > 0) { outb(ioaddr, (RDSR + portp->uartaddr)); insb((ioaddr + EREG_DATA), head, stlen); head += stlen; if (head >= portp->rx.endbuf) { head = portp->rx.buf; len -= stlen; insb((ioaddr + EREG_DATA), head, len); head += len; } } } } else if ((ioack & ACK_TYPMASK) == ACK_TYPRXBAD) { outb(ioaddr, (RDSR + portp->uartaddr)); status = inb(ioaddr + EREG_DATA); ch = inb(ioaddr + EREG_DATA); if (status & ST_BREAK) portp->stats.rxbreaks++; if (status & ST_FRAMING) portp->stats.rxframing++; if (status & ST_PARITY) portp->stats.rxparity++; if (status & ST_OVERRUN) portp->stats.rxoverrun++; if (status & ST_SCHARMASK) { if ((status & ST_SCHARMASK) == ST_SCHAR1) portp->stats.txxon++; if ((status & ST_SCHARMASK) == ST_SCHAR2) portp->stats.txxoff++; goto stl_rxalldone; } if ((portp->rxignoremsk & status) == 0) { if ((tp->t_state & TS_CAN_BYPASS_L_RINT) && ((status & ST_FRAMING) || ((status & ST_PARITY) && (tp->t_iflag & INPCK)))) ch = 0; if ((portp->rxmarkmsk & status) == 0) status = 0; *(head + STL_RXBUFSIZE) = status; *head++ = ch; if (head >= portp->rx.endbuf) head = portp->rx.buf; } } else { printf("STALLION: bad RX interrupt ack value=%x\n", ioack); return; } portp->rx.head = head; portp->state |= ASY_RXDATA; stl_dotimeout(); stl_rxalldone: outb(ioaddr, (EOSRR + portp->uartaddr)); outb((ioaddr + EREG_DATA), 0); } /*****************************************************************************/ /* * Modem interrupt handler. The is called when the modem signal line * (DCD) has changed state. Leave most of the work to the off-level * processing routine. */ static __inline void stl_mdmisr(stlpanel_t *panelp, int ioaddr) { stlport_t *portp; unsigned int ioack; unsigned char misr; #if DEBUG printf("stl_mdmisr(panelp=%x,ioaddr=%x)\n", (int) panelp, ioaddr); #endif ioack = inb(ioaddr + EREG_MDACK); if (((ioack & panelp->ackmask) != 0) || ((ioack & ACK_TYPMASK) != ACK_TYPMDM)) { printf("STALLION: bad MODEM interrupt ack value=%x\n", ioack); return; } portp = panelp->ports[(ioack >> 3)]; outb(ioaddr, (MISR + portp->uartaddr)); misr = inb(ioaddr + EREG_DATA); if (misr & MISR_DCD) { portp->state |= ASY_DCDCHANGE; portp->stats.modem++; stl_dotimeout(); } outb(ioaddr, (EOSRR + portp->uartaddr)); outb((ioaddr + EREG_DATA), 0); } /*****************************************************************************/ /* * Interrupt handler for EIO and ECH boards. This code ain't all that * pretty, but the idea is to make it as fast as possible. This code is * well suited to be assemblerized :-) We don't use the general purpose * register access functions here, for speed we will go strait to the * io register. */ void stlintr(int unit) { stlbrd_t *brdp; stlpanel_t *panelp; unsigned char svrtype; int i, panelnr, iobase; int cnt; #if DEBUG printf("stlintr(unit=%d)\n", unit); #endif cnt = 0; panelp = (stlpanel_t *) NULL; for (i = 0; (i < stl_nrbrds); ) { if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL) { i++; continue; } if (brdp->state == 0) { i++; continue; } /* * The following section of code handles the subtle differences * between board types. It is sort of similar, but different * enough to handle each separately. */ if (brdp->brdtype == BRD_EASYIO) { if ((inb(brdp->iostatus) & EIO_INTRPEND) == 0) { i++; continue; } panelp = brdp->panels[0]; iobase = panelp->iobase; outb(iobase, SVRR); svrtype = inb(iobase + EREG_DATA); if (brdp->nrports > 4) { outb(iobase, (SVRR + 0x80)); svrtype |= inb(iobase + EREG_DATA); } } else if (brdp->brdtype == BRD_ECH) { if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) { i++; continue; } outb(brdp->ioctrl, (brdp->ioctrlval | ECH_BRDENABLE)); for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { panelp = brdp->panels[panelnr]; iobase = panelp->iobase; if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) break; if (panelp->nrports > 8) { iobase += 0x8; if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) break; } } if (panelnr >= brdp->nrpanels) { i++; continue; } outb(iobase, SVRR); svrtype = inb(iobase + EREG_DATA); outb(iobase, (SVRR + 0x80)); svrtype |= inb(iobase + EREG_DATA); } else if (brdp->brdtype == BRD_ECHPCI) { iobase = brdp->ioaddr2; for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { panelp = brdp->panels[panelnr]; outb(brdp->ioctrl, panelp->pagenr); if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) break; if (panelp->nrports > 8) { outb(brdp->ioctrl, (panelp->pagenr + 1)); if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) break; } } if (panelnr >= brdp->nrpanels) { i++; continue; } outb(iobase, SVRR); svrtype = inb(iobase + EREG_DATA); outb(iobase, (SVRR + 0x80)); svrtype |= inb(iobase + EREG_DATA); } else if (brdp->brdtype == BRD_ECHMC) { if ((inb(brdp->iostatus) & ECH_INTRPEND) == 0) { i++; continue; } for (panelnr = 0; (panelnr < brdp->nrpanels); panelnr++) { panelp = brdp->panels[panelnr]; iobase = panelp->iobase; if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) break; if (panelp->nrports > 8) { iobase += 0x8; if (inb(iobase + ECH_PNLSTATUS) & ECH_PNLINTRPEND) break; } } if (panelnr >= brdp->nrpanels) { i++; continue; } outb(iobase, SVRR); svrtype = inb(iobase + EREG_DATA); outb(iobase, (SVRR + 0x80)); svrtype |= inb(iobase + EREG_DATA); } else { printf("STALLION: unknown board type=%x\n", brdp->brdtype); i++; continue; } /* * We have determined what type of service is required for a * port. From here on in the service of a port is the same no * matter what the board type... */ if (svrtype & SVRR_RX) stl_rxisr(panelp, iobase); if (svrtype & SVRR_TX) stl_txisr(panelp, iobase); if (svrtype & SVRR_MDM) stl_mdmisr(panelp, iobase); if (brdp->brdtype == BRD_ECH) outb(brdp->ioctrl, (brdp->ioctrlval | ECH_BRDDISABLE)); } } /*****************************************************************************/ #if NPCI > 0 static void stlpciintr(void *arg) { stlintr(0); } #endif /*****************************************************************************/ /* * If we haven't scheduled a timeout then do it, some port needs high * level processing. */ static void stl_dotimeout() { #if DEBUG printf("stl_dotimeout()\n"); #endif if (stl_doingtimeout == 0) { timeout(stl_poll, 0, 1); stl_doingtimeout++; } } /*****************************************************************************/ /* * Service "software" level processing. Too slow or painfull to be done * at real hardware interrupt time. This way we might also be able to * do some service on other waiting ports as well... */ static void stl_poll(void *arg) { stlbrd_t *brdp; stlport_t *portp; struct tty *tp; int brdnr, portnr, rearm, x; #if DEBUG printf("stl_poll()\n"); #endif stl_doingtimeout = 0; rearm = 0; x = spltty(); for (brdnr = 0; (brdnr < stl_nrbrds); brdnr++) { if ((brdp = stl_brds[brdnr]) == (stlbrd_t *) NULL) continue; for (portnr = 0; (portnr < brdp->nrports); portnr++) { if ((portp = brdp->ports[portnr]) == (stlport_t *) NULL) continue; if ((portp->state & ASY_ACTIVE) == 0) continue; tp = &portp->tty; if (portp->state & ASY_RXDATA) stl_rxprocess(portp); if (portp->state & ASY_DCDCHANGE) { portp->state &= ~ASY_DCDCHANGE; portp->sigs = stl_getsignals(portp); (*linesw[tp->t_line].l_modem)(tp, (portp->sigs & TIOCM_CD)); } if (portp->state & ASY_TXLOW) { portp->state &= ~ASY_TXLOW; (*linesw[tp->t_line].l_start)(tp); } if (portp->state & ASY_ACTIVE) rearm++; } } splx(x); if (rearm) stl_dotimeout(); } /*****************************************************************************/ /* * Process the RX data that has been buffered up in the RX ring queue. */ static void stl_rxprocess(stlport_t *portp) { struct tty *tp; unsigned int len, stlen, lostlen; char *head, *tail; char status; int ch; #if DEBUG printf("stl_rxprocess(portp=%x): brdnr=%d portnr=%d\n", (int) portp, portp->brdnr, portp->portnr); #endif tp = &portp->tty; portp->state &= ~ASY_RXDATA; if ((tp->t_state & TS_ISOPEN) == 0) { stl_flush(portp, FREAD); return; } /* * Calculate the amount of data in the RX ring queue. Also calculate * the largest single copy size... */ head = portp->rx.head; tail = portp->rx.tail; if (head >= tail) { len = head - tail; stlen = len; } else { len = STL_RXBUFSIZE - (tail - head); stlen = portp->rx.endbuf - tail; } if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (len > 0) { if (((tp->t_rawq.c_cc + len) >= TTYHOG) && ((portp->state & ASY_RTSFLOWMODE) || (tp->t_iflag & IXOFF)) && ((tp->t_state & TS_TBLOCK) == 0)) { ch = TTYHOG - tp->t_rawq.c_cc - 1; len = (ch > 0) ? ch : 0; stlen = MIN(stlen, len); ttyblock(tp); } lostlen = b_to_q(tail, stlen, &tp->t_rawq); tail += stlen; len -= stlen; if (tail >= portp->rx.endbuf) { tail = portp->rx.buf; lostlen += b_to_q(tail, len, &tp->t_rawq); tail += len; } portp->stats.rxlost += lostlen; ttwakeup(tp); portp->rx.tail = tail; } } else { while (portp->rx.tail != head) { ch = (unsigned char) *(portp->rx.tail); if (status = *(portp->rx.tail + STL_RXBUFSIZE)) { *(portp->rx.tail + STL_RXBUFSIZE) = 0; if (status & ST_BREAK) ch |= TTY_BI; if (status & ST_FRAMING) ch |= TTY_FE; if (status & ST_PARITY) ch |= TTY_PE; if (status & ST_OVERRUN) ch |= TTY_OE; } (*linesw[tp->t_line].l_rint)(ch, tp); if (portp->rx.tail == head) break; if (++(portp->rx.tail) >= portp->rx.endbuf) portp->rx.tail = portp->rx.buf; } } if (head != portp->rx.tail) portp->state |= ASY_RXDATA; /* * If we where flow controled then maybe the buffer is low enough that * we can re-activate it. */ if ((portp->state & ASY_RTSFLOW) && ((tp->t_state & TS_TBLOCK) == 0)) stl_flowcontrol(portp, 1, -1); } /*****************************************************************************/ /* * Set up the cd1400 registers for a port based on the termios port * settings. */ static int stl_param(struct tty *tp, struct termios *tiosp) { stlport_t *portp; unsigned int clkdiv; unsigned char cor1, cor2, cor3; unsigned char cor4, cor5, ccr; unsigned char srer, sreron, sreroff; unsigned char mcor1, mcor2, rtpr; unsigned char clk, div; int x; portp = (stlport_t *) tp; #if DEBUG printf("stl_param(tp=%x,tiosp=%x): brdnr=%d portnr=%d\n", (int) tp, (int) tiosp, portp->brdnr, portp->portnr); #endif cor1 = 0; cor2 = 0; cor3 = 0; cor4 = 0; cor5 = 0; ccr = 0; rtpr = 0; clk = 0; div = 0; mcor1 = 0; mcor2 = 0; sreron = 0; sreroff = 0; /* * Set up the RX char ignore mask with those RX error types we * can ignore. We could have used some special modes of the cd1400 * UART to help, but it is better this way because we can keep stats * on the number of each type of RX exception event. */ portp->rxignoremsk = 0; if (tiosp->c_iflag & IGNPAR) portp->rxignoremsk |= (ST_PARITY | ST_FRAMING | ST_OVERRUN); if (tiosp->c_iflag & IGNBRK) portp->rxignoremsk |= ST_BREAK; portp->rxmarkmsk = ST_OVERRUN; if (tiosp->c_iflag & (INPCK | PARMRK)) portp->rxmarkmsk |= (ST_PARITY | ST_FRAMING); if (tiosp->c_iflag & BRKINT) portp->rxmarkmsk |= ST_BREAK; /* * Go through the char size, parity and stop bits and set all the * option registers appropriately. */ switch (tiosp->c_cflag & CSIZE) { case CS5: cor1 |= COR1_CHL5; break; case CS6: cor1 |= COR1_CHL6; break; case CS7: cor1 |= COR1_CHL7; break; default: cor1 |= COR1_CHL8; break; } if (tiosp->c_cflag & CSTOPB) cor1 |= COR1_STOP2; else cor1 |= COR1_STOP1; if (tiosp->c_cflag & PARENB) { if (tiosp->c_cflag & PARODD) cor1 |= (COR1_PARENB | COR1_PARODD); else cor1 |= (COR1_PARENB | COR1_PAREVEN); } else { cor1 |= COR1_PARNONE; } if (tiosp->c_iflag & ISTRIP) cor5 |= COR5_ISTRIP; /* * Set the RX FIFO threshold at 6 chars. This gives a bit of breathing * space for hardware flow control and the like. This should be set to * VMIN. Also here we will set the RX data timeout to 10ms - this should * really be based on VTIME... */ cor3 |= FIFO_RXTHRESHOLD; rtpr = 2; /* * Calculate the baud rate timers. For now we will just assume that * the input and output baud are the same. Could have used a baud * table here, but this way we can generate virtually any baud rate * we like! */ if (tiosp->c_ispeed == 0) tiosp->c_ispeed = tiosp->c_ospeed; if ((tiosp->c_ospeed < 0) || (tiosp->c_ospeed > STL_MAXBAUD)) return(EINVAL); if (tiosp->c_ospeed > 0) { for (clk = 0; (clk < CD1400_NUMCLKS); clk++) { clkdiv = ((portp->clk / stl_cd1400clkdivs[clk]) / tiosp->c_ospeed); if (clkdiv < 0x100) break; } div = (unsigned char) clkdiv; } /* * Check what form of modem signaling is required and set it up. */ if ((tiosp->c_cflag & CLOCAL) == 0) { mcor1 |= MCOR1_DCD; mcor2 |= MCOR2_DCD; sreron |= SRER_MODEM; } /* * Setup cd1400 enhanced modes if we can. In particular we want to * handle as much of the flow control as possbile automatically. As * well as saving a few CPU cycles it will also greatly improve flow * control reliablilty. */ if (tiosp->c_iflag & IXON) { cor2 |= COR2_TXIBE; cor3 |= COR3_SCD12; if (tiosp->c_iflag & IXANY) cor2 |= COR2_IXM; } if (tiosp->c_cflag & CCTS_OFLOW) cor2 |= COR2_CTSAE; if (tiosp->c_cflag & CRTS_IFLOW) mcor1 |= FIFO_RTSTHRESHOLD; /* * All cd1400 register values calculated so go through and set them * all up. */ #if DEBUG printf("SETPORT: portnr=%d panelnr=%d brdnr=%d\n", portp->portnr, portp->panelnr, portp->brdnr); printf(" cor1=%x cor2=%x cor3=%x cor4=%x cor5=%x\n", cor1, cor2, cor3, cor4, cor5); printf(" mcor1=%x mcor2=%x rtpr=%x sreron=%x sreroff=%x\n", mcor1, mcor2, rtpr, sreron, sreroff); printf(" tcor=%x tbpr=%x rcor=%x rbpr=%x\n", clk, div, clk, div); printf(" schr1=%x schr2=%x schr3=%x schr4=%x\n", tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP], tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]); #endif x = spltty(); BRDENABLE(portp->brdnr, portp->pagenr); stl_setreg(portp, CAR, (portp->portnr & 0x3)); srer = stl_getreg(portp, SRER); stl_setreg(portp, SRER, 0); ccr += stl_updatereg(portp, COR1, cor1); ccr += stl_updatereg(portp, COR2, cor2); ccr += stl_updatereg(portp, COR3, cor3); if (ccr) { stl_ccrwait(portp); stl_setreg(portp, CCR, CCR_CORCHANGE); } stl_setreg(portp, COR4, cor4); stl_setreg(portp, COR5, cor5); stl_setreg(portp, MCOR1, mcor1); stl_setreg(portp, MCOR2, mcor2); if (tiosp->c_ospeed == 0) { stl_setreg(portp, MSVR1, 0); } else { stl_setreg(portp, MSVR1, MSVR1_DTR); stl_setreg(portp, TCOR, clk); stl_setreg(portp, TBPR, div); stl_setreg(portp, RCOR, clk); stl_setreg(portp, RBPR, div); } stl_setreg(portp, SCHR1, tiosp->c_cc[VSTART]); stl_setreg(portp, SCHR2, tiosp->c_cc[VSTOP]); stl_setreg(portp, SCHR3, tiosp->c_cc[VSTART]); stl_setreg(portp, SCHR4, tiosp->c_cc[VSTOP]); stl_setreg(portp, RTPR, rtpr); mcor1 = stl_getreg(portp, MSVR1); if (mcor1 & MSVR1_DCD) portp->sigs |= TIOCM_CD; else portp->sigs &= ~TIOCM_CD; stl_setreg(portp, SRER, ((srer & ~sreroff) | sreron)); BRDDISABLE(portp->brdnr); portp->state &= ~(ASY_RTSFLOWMODE | ASY_CTSFLOWMODE); portp->state |= ((tiosp->c_cflag & CRTS_IFLOW) ? ASY_RTSFLOWMODE : 0); portp->state |= ((tiosp->c_cflag & CCTS_OFLOW) ? ASY_CTSFLOWMODE : 0); stl_ttyoptim(portp, tiosp); splx(x); return(0); } /*****************************************************************************/ /* * Action the flow control as required. The hw and sw args inform the * routine what flow control methods it should try. */ static void stl_flowcontrol(stlport_t *portp, int hw, int sw) { unsigned char *head, *tail; int len, hwflow, x; #if DEBUG printf("stl_flowcontrol(portp=%x,hw=%d,sw=%d)\n", (int) portp, hw, sw); #endif hwflow = -1; if (portp->state & ASY_RTSFLOWMODE) { if (hw == 0) { if ((portp->state & ASY_RTSFLOW) == 0) hwflow = 0; } else if (hw > 0) { if (portp->state & ASY_RTSFLOW) { head = portp->rx.head; tail = portp->rx.tail; len = (head >= tail) ? (head - tail) : (STL_RXBUFSIZE - (tail - head)); if (len < STL_RXBUFHIGH) hwflow = 1; } } } /* * We have worked out what to do, if anything. So now apply it to the * UART port. */ if (hwflow >= 0) { x = spltty(); BRDENABLE(portp->brdnr, portp->pagenr); stl_setreg(portp, CAR, (portp->portnr & 0x03)); if (hwflow == 0) { portp->state |= ASY_RTSFLOW; stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) & 0xf0)); stl_setreg(portp, MSVR2, 0); portp->stats.rxrtsoff++; } else if (hwflow > 0) { portp->state &= ~ASY_RTSFLOW; stl_setreg(portp, MSVR2, MSVR2_RTS); stl_setreg(portp, MCOR1, (stl_getreg(portp, MCOR1) | FIFO_RTSTHRESHOLD)); portp->stats.rxrtson++; } BRDDISABLE(portp->brdnr); splx(x); } } /*****************************************************************************/ /* * Set the state of the DTR and RTS signals. */ static void stl_setsignals(stlport_t *portp, int dtr, int rts) { unsigned char msvr1, msvr2; int x; #if DEBUG printf("stl_setsignals(portp=%x,dtr=%d,rts=%d)\n", (int) portp, dtr, rts); #endif msvr1 = 0; msvr2 = 0; if (dtr > 0) msvr1 = MSVR1_DTR; if (rts > 0) msvr2 = MSVR2_RTS; x = spltty(); BRDENABLE(portp->brdnr, portp->pagenr); stl_setreg(portp, CAR, (portp->portnr & 0x03)); if (rts >= 0) stl_setreg(portp, MSVR2, msvr2); if (dtr >= 0) stl_setreg(portp, MSVR1, msvr1); BRDDISABLE(portp->brdnr); splx(x); } /*****************************************************************************/ /* * Get the state of the signals. */ static int stl_getsignals(stlport_t *portp) { unsigned char msvr1, msvr2; int sigs, x; #if DEBUG printf("stl_getsignals(portp=%x)\n", (int) portp); #endif x = spltty(); BRDENABLE(portp->brdnr, portp->pagenr); stl_setreg(portp, CAR, (portp->portnr & 0x3)); msvr1 = stl_getreg(portp, MSVR1); msvr2 = stl_getreg(portp, MSVR2); BRDDISABLE(portp->brdnr); splx(x); sigs = 0; sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0; sigs |= (msvr1 & MSVR1_CTS) ? TIOCM_CTS : 0; sigs |= (msvr1 & MSVR1_RI) ? TIOCM_RI : 0; sigs |= (msvr1 & MSVR1_DSR) ? TIOCM_DSR : 0; sigs |= (msvr1 & MSVR1_DTR) ? TIOCM_DTR : 0; sigs |= (msvr2 & MSVR2_RTS) ? TIOCM_RTS : 0; return(sigs); } /*****************************************************************************/ /* * Enable or disable the Transmitter and/or Receiver. */ static void stl_enablerxtx(stlport_t *portp, int rx, int tx) { unsigned char ccr; int x; #if DEBUG printf("stl_enablerxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); #endif ccr = 0; if (tx == 0) ccr |= CCR_TXDISABLE; else if (tx > 0) ccr |= CCR_TXENABLE; if (rx == 0) ccr |= CCR_RXDISABLE; else if (rx > 0) ccr |= CCR_RXENABLE; x = spltty(); BRDENABLE(portp->brdnr, portp->pagenr); stl_setreg(portp, CAR, (portp->portnr & 0x03)); stl_ccrwait(portp); stl_setreg(portp, CCR, ccr); stl_ccrwait(portp); BRDDISABLE(portp->brdnr); splx(x); } /*****************************************************************************/ /* * Start or stop the Transmitter and/or Receiver. */ static void stl_startrxtx(stlport_t *portp, int rx, int tx) { unsigned char sreron, sreroff; int x; #if DEBUG printf("stl_startrxtx(portp=%x,rx=%d,tx=%d)\n", (int) portp, rx, tx); #endif sreron = 0; sreroff = 0; if (tx == 0) sreroff |= (SRER_TXDATA | SRER_TXEMPTY); else if (tx == 1) sreron |= SRER_TXDATA; else if (tx >= 2) sreron |= SRER_TXEMPTY; if (rx == 0) sreroff |= SRER_RXDATA; else if (rx > 0) sreron |= SRER_RXDATA; x = spltty(); BRDENABLE(portp->brdnr, portp->pagenr); stl_setreg(portp, CAR, (portp->portnr & 0x3)); stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~sreroff) | sreron)); BRDDISABLE(portp->brdnr); if (tx > 0) portp->tty.t_state |= TS_BUSY; splx(x); } /*****************************************************************************/ /* * Disable all interrupts from this port. */ static void stl_disableintrs(stlport_t *portp) { int x; #if DEBUG printf("stl_disableintrs(portp=%x)\n", (int) portp); #endif x = spltty(); BRDENABLE(portp->brdnr, portp->pagenr); stl_setreg(portp, CAR, (portp->portnr & 0x3)); stl_setreg(portp, SRER, 0); BRDDISABLE(portp->brdnr); splx(x); } /*****************************************************************************/ static void stl_sendbreak(stlport_t *portp, long len) { int x; #if DEBUG printf("stl_sendbreak(portp=%x,len=%d)\n", (int) portp, (int) len); #endif x = spltty(); BRDENABLE(portp->brdnr, portp->pagenr); stl_setreg(portp, CAR, (portp->portnr & 0x3)); stl_setreg(portp, COR2, (stl_getreg(portp, COR2) | COR2_ETC)); stl_setreg(portp, SRER, ((stl_getreg(portp, SRER) & ~SRER_TXDATA) | SRER_TXEMPTY)); BRDDISABLE(portp->brdnr); if (len > 0) { len = len / 5; portp->brklen = (len > 255) ? 255 : len; } else { portp->brklen = len; } splx(x); portp->stats.txbreaks++; } /*****************************************************************************/ /* * Enable l_rint processing bypass mode if tty modes allow it. */ static void stl_ttyoptim(stlport_t *portp, struct termios *tiosp) { struct tty *tp; tp = &portp->tty; if (((tiosp->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR)) == 0) && (((tiosp->c_iflag & BRKINT) == 0) || (tiosp->c_iflag & IGNBRK)) && (((tiosp->c_iflag & PARMRK) == 0) || ((tiosp->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK))) && ((tiosp->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) ==0) && (linesw[tp->t_line].l_rint == ttyinput)) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; portp->hotchar = linesw[tp->t_line].l_hotchar; } /*****************************************************************************/ /* * Try and find and initialize all the ports on a panel. We don't care * what sort of board these ports are on - since the port io registers * are almost identical when dealing with ports. */ static int stl_initports(stlbrd_t *brdp, stlpanel_t *panelp) { stlport_t *portp; unsigned int chipmask; unsigned int gfrcr; int nrchips, uartaddr, ioaddr; int i, j; #if DEBUG printf("stl_initports(panelp=%x)\n", (int) panelp); #endif BRDENABLE(panelp->brdnr, panelp->pagenr); /* * Check that each chip is present and started up OK. */ chipmask = 0; nrchips = panelp->nrports / CD1400_PORTS; for (i = 0; (i < nrchips); i++) { if (brdp->brdtype == BRD_ECHPCI) { outb(brdp->ioctrl, (panelp->pagenr + (i >> 1))); ioaddr = panelp->iobase; } else { ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 1)); } uartaddr = (i & 0x01) ? 0x080 : 0; outb(ioaddr, (GFRCR + uartaddr)); outb((ioaddr + EREG_DATA), 0); outb(ioaddr, (CCR + uartaddr)); outb((ioaddr + EREG_DATA), CCR_RESETFULL); outb((ioaddr + EREG_DATA), CCR_RESETFULL); outb(ioaddr, (GFRCR + uartaddr)); for (j = 0; (j < CCR_MAXWAIT); j++) { gfrcr = inb(ioaddr + EREG_DATA); if ((gfrcr > 0x40) && (gfrcr < 0x60)) break; } if (j >= CCR_MAXWAIT) { printf("STALLION: cd1400 not responding, brd=%d " "panel=%d chip=%d\n", panelp->brdnr, panelp->panelnr, i); continue; } chipmask |= (0x1 << i); outb(ioaddr, (PPR + uartaddr)); outb((ioaddr + EREG_DATA), PPR_SCALAR); } /* * All cd1400's are initialized (if found!). Now go through and setup * each ports data structures. Also init the LIVR register of cd1400 * for each port. */ ioaddr = panelp->iobase; for (i = 0; (i < panelp->nrports); i++) { if (brdp->brdtype == BRD_ECHPCI) { outb(brdp->ioctrl, (panelp->pagenr + (i >> 3))); ioaddr = panelp->iobase; } else { ioaddr = panelp->iobase + (EREG_BANKSIZE * (i >> 3)); } if ((chipmask & (0x1 << (i / 4))) == 0) continue; portp = (stlport_t *) malloc(sizeof(stlport_t), M_TTYS, M_NOWAIT); if (portp == (stlport_t *) NULL) { printf("STALLION: failed to allocate port memory " "(size=%d)\n", sizeof(stlport_t)); break; } bzero(portp, sizeof(stlport_t)); portp->portnr = i; portp->brdnr = panelp->brdnr; portp->panelnr = panelp->panelnr; portp->clk = brdp->clk; portp->ioaddr = ioaddr; portp->uartaddr = (i & 0x4) << 5; portp->pagenr = panelp->pagenr + (i >> 3); portp->hwid = stl_getreg(portp, GFRCR); stl_setreg(portp, CAR, (i & 0x3)); stl_setreg(portp, LIVR, (i << 3)); panelp->ports[i] = portp; j = STL_TXBUFSIZE + (2 * STL_RXBUFSIZE); portp->tx.buf = (char *) malloc(j, M_TTYS, M_NOWAIT); if (portp->tx.buf == (char *) NULL) { printf("STALLION: failed to allocate buffer memory " "(size=%d)\n", j); break; } portp->tx.endbuf = portp->tx.buf + STL_TXBUFSIZE; portp->tx.head = portp->tx.buf; portp->tx.tail = portp->tx.buf; portp->rx.buf = portp->tx.buf + STL_TXBUFSIZE; portp->rx.endbuf = portp->rx.buf + STL_RXBUFSIZE; portp->rx.head = portp->rx.buf; portp->rx.tail = portp->rx.buf; portp->rxstatus.buf = portp->rx.buf + STL_RXBUFSIZE; portp->rxstatus.endbuf = portp->rxstatus.buf + STL_RXBUFSIZE; portp->rxstatus.head = portp->rxstatus.buf; portp->rxstatus.tail = portp->rxstatus.buf; bzero(portp->rxstatus.head, STL_RXBUFSIZE); portp->initintios.c_ispeed = STL_DEFSPEED; portp->initintios.c_ospeed = STL_DEFSPEED; portp->initintios.c_cflag = STL_DEFCFLAG; portp->initintios.c_iflag = 0; portp->initintios.c_oflag = 0; portp->initintios.c_lflag = 0; bcopy(&ttydefchars[0], &portp->initintios.c_cc[0], sizeof(portp->initintios.c_cc)); portp->initouttios = portp->initintios; portp->dtrwait = 3 * hz; } BRDDISABLE(panelp->brdnr); return(0); } /*****************************************************************************/ /* * Try to find and initialize an EasyIO board. */ static int stl_initeio(stlbrd_t *brdp) { stlpanel_t *panelp; unsigned int status; #if DEBUG printf("stl_initeio(brdp=%x)\n", (int) brdp); #endif brdp->ioctrl = brdp->ioaddr1 + 1; brdp->iostatus = brdp->ioaddr1 + 2; brdp->clk = EIO_CLK; status = inb(brdp->iostatus); switch (status & EIO_IDBITMASK) { case EIO_8PORTM: brdp->clk = EIO_CLK8M; /* fall thru */ case EIO_8PORTRS: case EIO_8PORTDI: brdp->nrports = 8; break; case EIO_4PORTRS: brdp->nrports = 4; break; default: return(ENODEV); } /* * Check that the supplied IRQ is good and then use it to setup the * programmable interrupt bits on EIO board. Also set the edge/level * triggered interrupt bit. */ if ((brdp->irq < 0) || (brdp->irq > 15) || (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { printf("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); return(EINVAL); } outb(brdp->ioctrl, (stl_vecmap[brdp->irq] | ((brdp->irqtype) ? EIO_INTLEVEL : EIO_INTEDGE))); panelp = (stlpanel_t *) malloc(sizeof(stlpanel_t), M_TTYS, M_NOWAIT); if (panelp == (stlpanel_t *) NULL) { printf("STALLION: failed to allocate memory (size=%d)\n", sizeof(stlpanel_t)); return(ENOMEM); } bzero(panelp, sizeof(stlpanel_t)); panelp->brdnr = brdp->brdnr; panelp->panelnr = 0; panelp->nrports = brdp->nrports; panelp->iobase = brdp->ioaddr1; panelp->hwid = status; brdp->panels[0] = panelp; brdp->nrpanels = 1; brdp->hwid = status; brdp->state |= BRD_FOUND; return(0); } /*****************************************************************************/ /* * Try to find an ECH board and initialize it. This code is capable of * dealing with all types of ECH board. */ static int stl_initech(stlbrd_t *brdp) { stlpanel_t *panelp; unsigned int status, nxtid; int panelnr, ioaddr, i; #if DEBUG printf("stl_initech(brdp=%x)\n", (int) brdp); #endif /* * Set up the initial board register contents for boards. This varys a * bit between the different board types. So we need to handle each * separately. Also do a check that the supplied IRQ is good. */ if (brdp->brdtype == BRD_ECH) { brdp->ioctrl = brdp->ioaddr1 + 1; brdp->iostatus = brdp->ioaddr1 + 1; status = inb(brdp->iostatus); if ((status & ECH_IDBITMASK) != ECH_ID) return(ENODEV); brdp->hwid = status; if ((brdp->irq < 0) || (brdp->irq > 15) || (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { printf("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); return(EINVAL); } status = ((brdp->ioaddr2 & ECH_ADDR2MASK) >> 1); status |= (stl_vecmap[brdp->irq] << 1); outb(brdp->ioaddr1, (status | ECH_BRDRESET)); brdp->ioctrlval = ECH_INTENABLE | ((brdp->irqtype) ? ECH_INTLEVEL : ECH_INTEDGE); outb(brdp->ioctrl, (brdp->ioctrlval | ECH_BRDENABLE)); outb(brdp->ioaddr1, status); } else if (brdp->brdtype == BRD_ECHMC) { brdp->ioctrl = brdp->ioaddr1 + 0x20; brdp->iostatus = brdp->ioctrl; status = inb(brdp->iostatus); if ((status & ECH_IDBITMASK) != ECH_ID) return(ENODEV); brdp->hwid = status; if ((brdp->irq < 0) || (brdp->irq > 15) || (stl_vecmap[brdp->irq] == (unsigned char) 0xff)) { printf("STALLION: invalid irq=%d for brd=%d\n", brdp->irq, brdp->brdnr); return(EINVAL); } outb(brdp->ioctrl, ECHMC_BRDRESET); outb(brdp->ioctrl, ECHMC_INTENABLE); } else if (brdp->brdtype == BRD_ECHPCI) { brdp->ioctrl = brdp->ioaddr1 + 2; } brdp->clk = ECH_CLK; /* * Scan through the secondary io address space looking for panels. * As we find'em allocate and initialize panel structures for each. */ ioaddr = brdp->ioaddr2; panelnr = 0; nxtid = 0; for (i = 0; (i < STL_MAXPANELS); i++) { if (brdp->brdtype == BRD_ECHPCI) { outb(brdp->ioctrl, nxtid); ioaddr = brdp->ioaddr2; } status = inb(ioaddr + ECH_PNLSTATUS); if ((status & ECH_PNLIDMASK) != nxtid) break; panelp = (stlpanel_t *) malloc(sizeof(stlpanel_t), M_TTYS, M_NOWAIT); if (panelp == (stlpanel_t *) NULL) { printf("STALLION: failed to allocate memory" "(size=%d)\n", sizeof(stlpanel_t)); break; } bzero(panelp, sizeof(stlpanel_t)); panelp->brdnr = brdp->brdnr; panelp->panelnr = panelnr; panelp->iobase = ioaddr; panelp->pagenr = nxtid; panelp->hwid = status; if (status & ECH_PNL16PORT) { if ((brdp->nrports + 16) > 32) break; panelp->nrports = 16; panelp->ackmask = 0x80; brdp->nrports += 16; ioaddr += (EREG_BANKSIZE * 2); nxtid += 2; } else { panelp->nrports = 8; panelp->ackmask = 0xc0; brdp->nrports += 8; ioaddr += EREG_BANKSIZE; nxtid++; } brdp->panels[panelnr++] = panelp; brdp->nrpanels++; if (ioaddr >= (brdp->ioaddr2 + 0x20)) break; } if (brdp->brdtype == BRD_ECH) outb(brdp->ioctrl, (brdp->ioctrlval | ECH_BRDDISABLE)); brdp->state |= BRD_FOUND; return(0); } /*****************************************************************************/ /* * Initialize and configure the specified board. This firstly probes * for the board, if it is found then the board is initialized and * then all its ports are initialized as well. */ static int stl_brdinit(stlbrd_t *brdp) { stlpanel_t *panelp; int i, j, k; #if DEBUG printf("stl_brdinit(brdp=%x): unit=%d type=%d io1=%x io2=%x irq=%d\n", (int) brdp, brdp->brdnr, brdp->brdtype, brdp->ioaddr1, brdp->ioaddr2, brdp->irq); #endif switch (brdp->brdtype) { case BRD_EASYIO: stl_initeio(brdp); break; case BRD_ECH: case BRD_ECHMC: case BRD_ECHPCI: stl_initech(brdp); break; default: printf("STALLION: unit=%d is unknown board type=%d\n", brdp->brdnr, brdp->brdtype); return(ENODEV); } stl_brds[brdp->brdnr] = brdp; if ((brdp->state & BRD_FOUND) == 0) { #if 0 printf("STALLION: %s board not found, unit=%d io=%x irq=%d\n", stl_brdnames[brdp->brdtype], brdp->brdnr, brdp->ioaddr1, brdp->irq); #endif return(ENODEV); } for (i = 0, k = 0; (i < STL_MAXPANELS); i++) { panelp = brdp->panels[i]; if (panelp != (stlpanel_t *) NULL) { stl_initports(brdp, panelp); for (j = 0; (j < panelp->nrports); j++) brdp->ports[k++] = panelp->ports[j]; } } printf("stl%d: %s (driver version %s) unit=%d nrpanels=%d nrports=%d\n", brdp->unitid, stl_brdnames[brdp->brdtype], stl_drvversion, brdp->brdnr, brdp->nrpanels, brdp->nrports); return(0); } /*****************************************************************************/ /* * Return the board stats structure to user app. */ static int stl_getbrdstats(caddr_t data) { stlbrd_t *brdp; stlpanel_t *panelp; int i; stl_brdstats = *((combrd_t *) data); if (stl_brdstats.brd >= STL_MAXBRDS) return(-ENODEV); brdp = stl_brds[stl_brdstats.brd]; if (brdp == (stlbrd_t *) NULL) return(-ENODEV); bzero(&stl_brdstats, sizeof(combrd_t)); stl_brdstats.brd = brdp->brdnr; stl_brdstats.type = brdp->brdtype; stl_brdstats.hwid = brdp->hwid; stl_brdstats.state = brdp->state; stl_brdstats.ioaddr = brdp->ioaddr1; stl_brdstats.ioaddr2 = brdp->ioaddr2; stl_brdstats.irq = brdp->irq; stl_brdstats.nrpanels = brdp->nrpanels; stl_brdstats.nrports = brdp->nrports; for (i = 0; (i < brdp->nrpanels); i++) { panelp = brdp->panels[i]; stl_brdstats.panels[i].panel = i; stl_brdstats.panels[i].hwid = panelp->hwid; stl_brdstats.panels[i].nrports = panelp->nrports; } *((combrd_t *) data) = stl_brdstats;; return(0); } /*****************************************************************************/ /* * Resolve the referenced port number into a port struct pointer. */ static stlport_t *stl_getport(int brdnr, int panelnr, int portnr) { stlbrd_t *brdp; stlpanel_t *panelp; if ((brdnr < 0) || (brdnr >= STL_MAXBRDS)) return((stlport_t *) NULL); brdp = stl_brds[brdnr]; if (brdp == (stlbrd_t *) NULL) return((stlport_t *) NULL); if ((panelnr < 0) || (panelnr >= brdp->nrpanels)) return((stlport_t *) NULL); panelp = brdp->panels[panelnr]; if (panelp == (stlpanel_t *) NULL) return((stlport_t *) NULL); if ((portnr < 0) || (portnr >= panelp->nrports)) return((stlport_t *) NULL); return(panelp->ports[portnr]); } /*****************************************************************************/ /* * Return the port stats structure to user app. A NULL port struct * pointer passed in means that we need to find out from the app * what port to get stats for (used through board control device). */ static int stl_getportstats(stlport_t *portp, caddr_t data) { unsigned char *head, *tail; if (portp == (stlport_t *) NULL) { stl_comstats = *((comstats_t *) data); portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port); if (portp == (stlport_t *) NULL) return(-ENODEV); } portp->stats.state = portp->state; /*portp->stats.flags = portp->flags;*/ portp->stats.hwid = portp->hwid; portp->stats.ttystate = portp->tty.t_state; portp->stats.cflags = portp->tty.t_cflag; portp->stats.iflags = portp->tty.t_iflag; portp->stats.oflags = portp->tty.t_oflag; portp->stats.lflags = portp->tty.t_lflag; head = portp->tx.head; tail = portp->tx.tail; portp->stats.txbuffered = ((head >= tail) ? (head - tail) : (STL_TXBUFSIZE - (tail - head))); head = portp->rx.head; tail = portp->rx.tail; portp->stats.rxbuffered = (head >= tail) ? (head - tail) : (STL_RXBUFSIZE - (tail - head)); portp->stats.signals = (unsigned long) stl_getsignals(portp); *((comstats_t *) data) = portp->stats; return(0); } /*****************************************************************************/ /* * Clear the port stats structure. We also return it zeroed out... */ static int stl_clrportstats(stlport_t *portp, caddr_t data) { if (portp == (stlport_t *) NULL) { stl_comstats = *((comstats_t *) data); portp = stl_getport(stl_comstats.brd, stl_comstats.panel, stl_comstats.port); if (portp == (stlport_t *) NULL) return(-ENODEV); } bzero(&portp->stats, sizeof(comstats_t)); portp->stats.brd = portp->brdnr; portp->stats.panel = portp->panelnr; portp->stats.port = portp->portnr; *((comstats_t *) data) = stl_comstats; return(0); } /*****************************************************************************/ /* * The "staliomem" device is used for stats collection in this driver. */ static int stl_memioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { stlbrd_t *brdp; int brdnr, rc; #if DEBUG printf("stl_memioctl(dev=%x,cmd=%x,data=%x,flag=%x)\n", (int) dev, cmd, (int) data, flag); #endif brdnr = dev & 0x7; brdp = stl_brds[brdnr]; if (brdp == (stlbrd_t *) NULL) return(ENODEV); if (brdp->state == 0) return(ENODEV); rc = 0; switch (cmd) { case COM_GETPORTSTATS: rc = stl_getportstats((stlport_t *) NULL, data); break; case COM_CLRPORTSTATS: rc = stl_clrportstats((stlport_t *) NULL, data); break; case COM_GETBRDSTATS: rc = stl_getbrdstats(data); break; default: rc = ENOTTY; break; } return(rc); } /*****************************************************************************/ diff --git a/sys/i386/isa/syscons.c b/sys/i386/isa/syscons.c index 831f56b7d417..d99ed3b06418 100644 --- a/sys/i386/isa/syscons.c +++ b/sys/i386/isa/syscons.c @@ -1,5412 +1,5414 @@ /*- * Copyright (c) 1992-1997 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer * in this position and unchanged. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software withough specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: syscons.c,v 1.274 1998/08/14 06:32:03 sos Exp $ + * $Id: syscons.c,v 1.275 1998/08/18 07:36:47 sos Exp $ */ #include "sc.h" #include "apm.h" #include "opt_ddb.h" #include "opt_devfs.h" #include "opt_syscons.h" #if NSC > 0 #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(MAXCONS) #define MAXCONS 16 #endif #if !defined(SC_MAX_HISTORY_SIZE) #define SC_MAX_HISTORY_SIZE (1000 * MAXCONS) #endif #if !defined(SC_HISTORY_SIZE) #define SC_HISTORY_SIZE (ROW * 4) #endif #if (SC_HISTORY_SIZE * MAXCONS) > SC_MAX_HISTORY_SIZE #undef SC_MAX_HISTORY_SIZE #define SC_MAX_HISTORY_SIZE (SC_HISTORY_SIZE * MAXCONS) #endif #if !defined(SC_MOUSE_CHAR) #define SC_MOUSE_CHAR (0xd0) #endif #define COLD 0 #define WARM 1 #define VESA_MODE(x) ((x) >= M_VESA_BASE) #define MODE_MAP_SIZE (M_VGA_CG320 + 1) #define MODE_PARAM_SIZE 64 #define DEFAULT_BLANKTIME (5*60) /* 5 minutes */ #define MAX_BLANKTIME (7*24*60*60) /* 7 days!? */ /* for backward compatibility */ #define OLD_CONS_MOUSECTL _IOWR('c', 10, old_mouse_info_t) typedef struct old_mouse_data { int x; int y; int buttons; } old_mouse_data_t; typedef struct old_mouse_info { int operation; union { struct old_mouse_data data; struct mouse_mode mode; } u; } old_mouse_info_t; /* XXX use sc_bcopy where video memory is concerned */ extern void generic_bcopy(const void *, void *, size_t); static default_attr user_default = { (FG_LIGHTGREY | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static default_attr kernel_default = { (FG_WHITE | BG_BLACK) << 8, (FG_BLACK | BG_LIGHTGREY) << 8 }; static scr_stat main_console; static scr_stat *console[MAXCONS]; #ifdef DEVFS static void *sc_devfs_token[MAXCONS]; static void *sc_vga_devfs_token; static void *sc_mouse_devfs_token; static void *sc_console_devfs_token; #endif scr_stat *cur_console; static scr_stat *new_scp, *old_scp; static term_stat kernel_console; static default_attr *current_default; static int flags = 0; static int sc_port = IO_KBD; static KBDC sc_kbdc = NULL; static char init_done = COLD; static u_short sc_buffer[ROW*COL]; static char shutdown_in_progress = FALSE; static char font_loading_in_progress = FALSE; static char switch_in_progress = FALSE; static char write_in_progress = FALSE; static char blink_in_progress = FALSE; static int blinkrate = 0; u_int crtc_addr = MONO_BASE; char crtc_type = KD_MONO; char crtc_vga = FALSE; static u_char shfts = 0, ctls = 0, alts = 0, agrs = 0, metas = 0; static u_char accents = 0; static u_char nlkcnt = 0, clkcnt = 0, slkcnt = 0, alkcnt = 0; static const u_int n_fkey_tab = sizeof(fkey_tab) / sizeof(*fkey_tab); static int delayed_next_scr = FALSE; static long scrn_blank_time = 0; /* screen saver timeout value */ int scrn_blanked = 0; /* screen saver active flag */ static long scrn_time_stamp; static int saver_mode = CONS_LKM_SAVER; /* LKM/user saver */ static int run_scrn_saver = FALSE; /* should run the saver? */ static int scrn_idle = FALSE; /* about to run the saver */ u_char scr_map[256]; u_char scr_rmap[256]; char *video_mode_ptr = NULL; static int bios_video_mode; /* video mode # set by BIOS */ int fonts_loaded = 0 #ifdef STD8X16FONT | FONT_16 #endif ; char font_8[256*8]; char font_14[256*14]; #ifdef STD8X16FONT extern #endif unsigned char font_16[256*16]; char palette[256*3]; static char *mode_map[MODE_MAP_SIZE]; static char vgaregs[MODE_PARAM_SIZE]; static char vgaregs2[MODE_PARAM_SIZE]; static int rows_offset = 1; static char *cut_buffer; static int cut_buffer_size; static int mouse_level = 0; /* sysmouse protocol level */ static mousestatus_t mouse_status = { 0, 0, 0, 0, 0, 0 }; static u_short mouse_and_mask[16] = { 0xc000, 0xe000, 0xf000, 0xf800, 0xfc00, 0xfe00, 0xff00, 0xff80, 0xfe00, 0x1e00, 0x1f00, 0x0f00, 0x0f00, 0x0000, 0x0000, 0x0000 }; static u_short mouse_or_mask[16] = { 0x0000, 0x4000, 0x6000, 0x7000, 0x7800, 0x7c00, 0x7e00, 0x6800, 0x0c00, 0x0c00, 0x0600, 0x0600, 0x0000, 0x0000, 0x0000, 0x0000 }; static int extra_history_size = SC_MAX_HISTORY_SIZE - SC_HISTORY_SIZE * MAXCONS; static void none_saver(int blank) { } static void (*current_saver)(int blank) = none_saver; static void (*default_saver)(int blank) = none_saver; int (*sc_user_ioctl)(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) = NULL; static int sticky_splash = FALSE; /* OS specific stuff */ #ifdef not_yet_done #define VIRTUAL_TTY(x) (sccons[x] = ttymalloc(sccons[x])) struct CONSOLE_TTY (sccons[MAXCONS] = ttymalloc(sccons[MAXCONS])) struct MOUSE_TTY (sccons[MAXCONS+1] = ttymalloc(sccons[MAXCONS+1])) struct tty *sccons[MAXCONS+2]; #else #define VIRTUAL_TTY(x) &sccons[x] #define CONSOLE_TTY &sccons[MAXCONS] #define MOUSE_TTY &sccons[MAXCONS+1] static struct tty sccons[MAXCONS+2]; #endif #define SC_MOUSE 128 #define SC_CONSOLE 255 #define MONO_BUF pa_to_va(0xB0000) #define CGA_BUF pa_to_va(0xB8000) u_short *Crtat; static const int nsccons = MAXCONS+2; #define WRAPHIST(scp, pointer, offset)\ ((scp->history) + ((((pointer) - (scp->history)) + (scp->history_size)\ + (offset)) % (scp->history_size))) #define ISSIGVALID(sig) ((sig) > 0 && (sig) < NSIG) /* this should really be in `rtc.h' */ #define RTC_EQUIPMENT 0x14 /* prototypes */ static int scattach(struct isa_device *dev); static int scparam(struct tty *tp, struct termios *t); static int scprobe(struct isa_device *dev); static int scvidprobe(int unit, int flags); static int sckbdprobe(int unit, int flags); static void scstart(struct tty *tp); static void scmousestart(struct tty *tp); static void scinit(void); static void scshutdown(int howto, void *arg); static void map_mode_table(char *map[], char *table, int max); static int map_mode_num(int mode); static char *get_mode_param(scr_stat *scp, int mode); static u_int scgetc(u_int flags); #define SCGETC_CN 1 #define SCGETC_NONBLOCK 2 static void sccnupdate(scr_stat *scp); static scr_stat *get_scr_stat(dev_t dev); static scr_stat *alloc_scp(void); static void init_scp(scr_stat *scp); static void sc_bcopy(scr_stat *scp, u_short *p, int from, int to, int mark); static int get_scr_num(void); static timeout_t scrn_timer; static void scrn_update(scr_stat *scp, int show_cursor); static void stop_scrn_saver(void (*saver)(int)); static int wait_scrn_saver_stop(void); static void clear_screen(scr_stat *scp); static int switch_scr(scr_stat *scp, u_int next_scr); static void exchange_scr(void); static void move_crsr(scr_stat *scp, int x, int y); static void scan_esc(scr_stat *scp, u_char c); static void draw_cursor_image(scr_stat *scp); static void remove_cursor_image(scr_stat *scp); static void ansi_put(scr_stat *scp, u_char *buf, int len); static u_char *get_fstr(u_int c, u_int *len); static void history_to_screen(scr_stat *scp); static int history_up_line(scr_stat *scp); static int history_down_line(scr_stat *scp); static int mask2attr(struct term_stat *term); static void set_keyboard(int command, int data); static void update_leds(int which); static void set_vgaregs(char *modetable); static void read_vgaregs(char *buf); #define COMP_IDENTICAL 0 #define COMP_SIMILAR 1 #define COMP_DIFFERENT 2 static int comp_vgaregs(u_char *buf1, u_char *buf2); static void dump_vgaregs(u_char *buf); #define PARAM_BUFSIZE 6 static void set_font_mode(u_char *buf); static void set_normal_mode(u_char *buf); static void set_destructive_cursor(scr_stat *scp); static void set_mouse_pos(scr_stat *scp); static int skip_spc_right(scr_stat *scp, u_short *p); static int skip_spc_left(scr_stat *scp, u_short *p); static void mouse_cut(scr_stat *scp); static void mouse_cut_start(scr_stat *scp); static void mouse_cut_end(scr_stat *scp); static void mouse_cut_word(scr_stat *scp); static void mouse_cut_line(scr_stat *scp); static void mouse_cut_extend(scr_stat *scp); static void mouse_paste(scr_stat *scp); static void draw_mouse_image(scr_stat *scp); static void remove_mouse_image(scr_stat *scp); static void draw_cutmarking(scr_stat *scp); static void remove_cutmarking(scr_stat *scp); static void save_palette(void); static void do_bell(scr_stat *scp, int pitch, int duration); static timeout_t blink_screen; #ifdef SC_SPLASH_SCREEN static void scsplash_init(void); static void scsplash(int show); #define scsplash_stick(stick) (sticky_splash = (stick)) #else #define scsplash_stick(stick) #endif struct isa_driver scdriver = { scprobe, scattach, "sc", 1 }; static d_open_t scopen; static d_close_t scclose; static d_read_t scread; static d_write_t scwrite; static d_ioctl_t scioctl; static d_devtotty_t scdevtotty; static d_mmap_t scmmap; -#define CDEV_MAJOR 12 -static struct cdevsw scdevsw = { +#define CDEV_MAJOR 12 +static struct cdevsw sc_cdevsw = { scopen, scclose, scread, scwrite, scioctl, nullstop, noreset, scdevtotty, - ttpoll, scmmap, nostrategy, "sc", NULL, -1 }; + ttpoll, scmmap, nostrategy, "sc", + NULL, -1, nodump, nopsize, + D_TTY, +}; /* * These functions need to be before calls to them so they can be inlined. */ static void draw_cursor_image(scr_stat *scp) { u_short cursor_image, *ptr = Crtat + (scp->cursor_pos - scp->scr_buf); u_short prev_image; if (VESA_MODE(scp->mode)) { sc_bcopy(scp, scp->scr_buf, scp->cursor_pos - scp->scr_buf, scp->cursor_pos - scp->scr_buf, 1); return; } /* do we have a destructive cursor ? */ if (flags & CHAR_CURSOR) { prev_image = scp->cursor_saveunder; cursor_image = *ptr & 0x00ff; if (cursor_image == DEAD_CHAR) cursor_image = prev_image & 0x00ff; cursor_image |= *(scp->cursor_pos) & 0xff00; scp->cursor_saveunder = cursor_image; /* update the cursor bitmap if the char under the cursor has changed */ if (prev_image != cursor_image) set_destructive_cursor(scp); /* modify cursor_image */ if (!(flags & BLINK_CURSOR)||((flags & BLINK_CURSOR)&&(blinkrate & 4))){ /* * When the mouse pointer is at the same position as the cursor, * the cursor bitmap needs to be updated even if the char under * the cursor hasn't changed, because the mouse pionter may * have moved by a few dots within the cursor cel. */ if ((prev_image == cursor_image) && (cursor_image != *(scp->cursor_pos))) set_destructive_cursor(scp); cursor_image &= 0xff00; cursor_image |= DEAD_CHAR; } } else { cursor_image = (*(ptr) & 0x00ff) | *(scp->cursor_pos) & 0xff00; scp->cursor_saveunder = cursor_image; if (!(flags & BLINK_CURSOR)||((flags & BLINK_CURSOR)&&(blinkrate & 4))){ if ((cursor_image & 0x7000) == 0x7000) { cursor_image &= 0x8fff; if(!(cursor_image & 0x0700)) cursor_image |= 0x0700; } else { cursor_image |= 0x7000; if ((cursor_image & 0x0700) == 0x0700) cursor_image &= 0xf0ff; } } } *ptr = cursor_image; } static void remove_cursor_image(scr_stat *scp) { if (VESA_MODE(scp->mode)) sc_bcopy(scp, scp->scr_buf, scp->cursor_oldpos - scp->scr_buf, scp->cursor_oldpos - scp->scr_buf, 0); else *(Crtat + (scp->cursor_oldpos - scp->scr_buf)) = scp->cursor_saveunder; } static void move_crsr(scr_stat *scp, int x, int y) { if (x < 0) x = 0; if (y < 0) y = 0; if (x >= scp->xsize) x = scp->xsize-1; if (y >= scp->ysize) y = scp->ysize-1; scp->xpos = x; scp->ypos = y; scp->cursor_pos = scp->scr_buf + scp->ypos * scp->xsize + scp->xpos; } static int scprobe(struct isa_device *dev) { if (!scvidprobe(dev->id_unit, dev->id_flags)) { if (bootverbose) printf("sc%d: no video adapter is found.\n", dev->id_unit); return (0); } sc_port = dev->id_iobase; if (sckbdprobe(dev->id_unit, dev->id_flags)) return (IO_KBDSIZE); else return ((dev->id_flags & DETECT_KBD) ? 0 : IO_KBDSIZE); } /* probe video adapters, return TRUE if found */ static int scvidprobe(int unit, int flags) { /* * XXX don't try to `printf' anything here, the console may not have * been configured yet. */ u_short volatile *cp; u_short was; u_int pa; u_int segoff; /* do this test only once */ if (init_done != COLD) return (Crtat != 0); /* * Finish defaulting crtc variables for a mono screen. Crtat is a * bogus common variable so that it can be shared with pcvt, so it * can't be statically initialized. XXX. */ Crtat = (u_short *)MONO_BUF; crtc_type = KD_MONO; /* If CGA memory seems to work, switch to color. */ cp = (u_short *)CGA_BUF; was = *cp; *cp = (u_short) 0xA55A; bios_video_mode = *(u_char *)pa_to_va(0x449); if (bootinfo.bi_vesa == 0x102) { bios_video_mode = bootinfo.bi_vesa; Crtat = (u_short *)pa_to_va(0xA0000); crtc_addr = COLOR_BASE; crtc_type = KD_VGA; bzero(Crtat, 800*600/8); } else if (*cp == 0xA55A) { Crtat = (u_short *)CGA_BUF; crtc_addr = COLOR_BASE; crtc_type = KD_CGA; } else { cp = Crtat; was = *cp; *cp = (u_short) 0xA55A; if (*cp != 0xA55A) { /* no screen at all, bail out */ Crtat = 0; return FALSE; } } *cp = was; if (!VESA_MODE(bios_video_mode)) { /* * Check rtc and BIOS date area. * XXX: don't use BIOSDATA_EQUIPMENT, it is not a dead copy * of RTC_EQUIPMENT. The bit 4 and 5 of the ETC_EQUIPMENT are * zeros for EGA and VGA. However, the EGA/VGA BIOS will set * these bits in BIOSDATA_EQUIPMENT according to the monitor * type detected. */ switch ((rtcin(RTC_EQUIPMENT) >> 4) & 3) { /* bit 4 and 5 */ case 0: /* EGA/VGA, or nothing */ crtc_type = KD_EGA; /* the color adapter may be in the 40x25 mode... XXX */ break; case 1: /* CGA 40x25 */ /* switch to the 80x25 mode? XXX */ /* FALL THROUGH */ case 2: /* CGA 80x25 */ /* `crtc_type' has already been set... */ /* crtc_type = KD_CGA; */ break; case 3: /* MDA */ /* `crtc_type' has already been set... */ /* crtc_type = KD_MONO; */ break; } /* is this a VGA or higher ? */ outb(crtc_addr, 7); if (inb(crtc_addr) == 7) { crtc_type = KD_VGA; crtc_vga = TRUE; read_vgaregs(vgaregs); /* Get the BIOS video mode pointer */ segoff = *(u_int *)pa_to_va(0x4a8); pa = ((segoff & 0xffff0000) >> 12) + (segoff & 0xffff); if (ISMAPPED(pa, sizeof(u_int))) { segoff = *(u_int *)pa_to_va(pa); pa = ((segoff & 0xffff0000) >> 12) + (segoff & 0xffff); if (ISMAPPED(pa, MODE_PARAM_SIZE)) video_mode_ptr = (char *)pa_to_va(pa); } } } return TRUE; } /* probe the keyboard, return TRUE if found */ static int sckbdprobe(int unit, int flags) { int codeset; int c = -1; int m; int res, id; sc_kbdc = kbdc_open(sc_port); if (!kbdc_lock(sc_kbdc, TRUE)) { /* driver error? */ printf("sc%d: unable to lock the controller.\n", unit); return ((flags & DETECT_KBD) ? FALSE : TRUE); } /* discard anything left after UserConfig */ empty_both_buffers(sc_kbdc, 10); /* save the current keyboard controller command byte */ m = kbdc_get_device_mask(sc_kbdc) & ~KBD_KBD_CONTROL_BITS; c = get_controller_command_byte(sc_kbdc); if (c == -1) { /* CONTROLLER ERROR */ printf("sc%d: unable to get the current command byte value.\n", unit); goto fail; } if (bootverbose) printf("sc%d: the current keyboard controller command byte %04x\n", unit, c); #if 0 /* override the keyboard lock switch */ c |= KBD_OVERRIDE_KBD_LOCK; #endif /* * The keyboard may have been screwed up by the boot block. * We may just be able to recover from error by testing the controller * and the keyboard port. The controller command byte needs to be saved * before this recovery operation, as some controllers seem to set * the command byte to particular values. */ test_controller(sc_kbdc); test_kbd_port(sc_kbdc); /* enable the keyboard port, but disable the keyboard intr. */ if (!set_controller_command_byte(sc_kbdc, KBD_KBD_CONTROL_BITS, KBD_ENABLE_KBD_PORT | KBD_DISABLE_KBD_INT)) { /* CONTROLLER ERROR * there is very little we can do... */ printf("sc%d: unable to set the command byte.\n", unit); goto fail; } /* * Check if we have an XT keyboard before we attempt to reset it. * The procedure assumes that the keyboard and the controller have * been set up properly by BIOS and have not been messed up * during the boot process. */ codeset = -1; if (flags & XT_KEYBD) /* the user says there is a XT keyboard */ codeset = 1; #ifdef DETECT_XT_KEYBOARD else if ((c & KBD_TRANSLATION) == 0) { /* SET_SCANCODE_SET is not always supported; ignore error */ if (send_kbd_command_and_data(sc_kbdc, KBDC_SET_SCANCODE_SET, 0) == KBD_ACK) codeset = read_kbd_data(sc_kbdc); } if (bootverbose) printf("sc%d: keyboard scancode set %d\n", unit, codeset); #endif /* DETECT_XT_KEYBOARD */ if (flags & KBD_NORESET) { write_kbd_command(sc_kbdc, KBDC_ECHO); if (read_kbd_data(sc_kbdc) != KBD_ECHO) { empty_both_buffers(sc_kbdc, 10); test_controller(sc_kbdc); test_kbd_port(sc_kbdc); if (bootverbose) printf("sc%d: failed to get response from the keyboard.\n", unit); goto fail; } } else { /* reset keyboard hardware */ if (!reset_kbd(sc_kbdc)) { /* KEYBOARD ERROR * Keyboard reset may fail either because the keyboard doen't * exist, or because the keyboard doesn't pass the self-test, * or the keyboard controller on the motherboard and the keyboard * somehow fail to shake hands. It is just possible, particularly * in the last case, that the keyoard controller may be left * in a hung state. test_controller() and test_kbd_port() appear * to bring the keyboard controller back (I don't know why and * how, though.) */ empty_both_buffers(sc_kbdc, 10); test_controller(sc_kbdc); test_kbd_port(sc_kbdc); /* We could disable the keyboard port and interrupt... but, * the keyboard may still exist (see above). */ if (bootverbose) printf("sc%d: failed to reset the keyboard.\n", unit); goto fail; } } /* * Allow us to set the XT_KEYBD flag in UserConfig so that keyboards * such as those on the IBM ThinkPad laptop computers can be used * with the standard console driver. */ if (codeset == 1) { if (send_kbd_command_and_data( sc_kbdc, KBDC_SET_SCANCODE_SET, codeset) == KBD_ACK) { /* XT kbd doesn't need scan code translation */ c &= ~KBD_TRANSLATION; } else { /* KEYBOARD ERROR * The XT kbd isn't usable unless the proper scan code set * is selected. */ printf("sc%d: unable to set the XT keyboard mode.\n", unit); goto fail; } } /* enable the keyboard port and intr. */ if (!set_controller_command_byte(sc_kbdc, KBD_KBD_CONTROL_BITS | KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK, (c & (KBD_TRANSLATION | KBD_OVERRIDE_KBD_LOCK)) | KBD_ENABLE_KBD_PORT | KBD_ENABLE_KBD_INT)) { /* CONTROLLER ERROR * This is serious; we are left with the disabled keyboard intr. */ printf("sc%d: unable to enable the keyboard port and intr.\n", unit); goto fail; } /* Get the ID of the keyboard, if any */ empty_kbd_buffer(sc_kbdc, 5); res = send_kbd_command(sc_kbdc, KBDC_SEND_DEV_ID); if (res == KBD_ACK) { /* 10ms delay */ DELAY(10000); id = (read_kbd_data(sc_kbdc) << 8) | read_kbd_data(sc_kbdc); if (bootverbose) printf("sc%d: keyboard device ID: %04x\n", unit, id); } kbdc_set_device_mask(sc_kbdc, m | KBD_KBD_CONTROL_BITS), kbdc_lock(sc_kbdc, FALSE); return TRUE; fail: if (c != -1) /* try to restore the command byte as before, if possible */ set_controller_command_byte(sc_kbdc, 0xff, c); kbdc_set_device_mask(sc_kbdc, (flags & DETECT_KBD) ? m : m | KBD_KBD_CONTROL_BITS); kbdc_lock(sc_kbdc, FALSE); return FALSE; } #if NAPM > 0 static int scresume(void *dummy) { shfts = ctls = alts = agrs = metas = accents = 0; return 0; } #endif static int scattach(struct isa_device *dev) { scr_stat *scp; dev_t cdev = makedev(CDEV_MAJOR, 0); char *p; #ifdef DEVFS int vc; #endif scinit(); flags = dev->id_flags; if (crtc_type != KD_VGA || VESA_MODE(bios_video_mode)) flags &= ~CHAR_CURSOR; scp = console[0]; if (crtc_type == KD_VGA) { cut_buffer_size = scp->xsize * scp->ysize + 1; cut_buffer = (char *)malloc(cut_buffer_size, M_DEVBUF, M_NOWAIT); if (cut_buffer != NULL) cut_buffer[0] = '\0'; } scp->scr_buf = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_NOWAIT); /* copy temporary buffer to final buffer */ bcopy(sc_buffer, scp->scr_buf, scp->xsize * scp->ysize * sizeof(u_short)); scp->cursor_pos = scp->cursor_oldpos = scp->scr_buf + scp->xpos + scp->ypos * scp->xsize; scp->mouse_pos = scp->mouse_oldpos = scp->scr_buf + ((scp->mouse_ypos/scp->font_size)*scp->xsize + scp->mouse_xpos/8); /* initialize history buffer & pointers */ scp->history_head = scp->history_pos = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_NOWAIT); if (scp->history_head != NULL) bzero(scp->history_head, scp->history_size*sizeof(u_short)); scp->history = scp->history_head; /* initialize cursor stuff */ if (!(scp->status & UNKNOWN_MODE)) draw_cursor_image(scp); /* get screen update going */ scrn_timer((void *)TRUE); update_leds(scp->status); if ((crtc_type == KD_VGA) && bootverbose) { printf("sc%d: BIOS video mode:%d\n", dev->id_unit, bios_video_mode); printf("sc%d: VGA registers upon power-up\n", dev->id_unit); dump_vgaregs(vgaregs); printf("sc%d: video mode:%d\n", dev->id_unit, scp->mode); printf("sc%d: VGA registers in BIOS for mode:%d\n", dev->id_unit, scp->mode); dump_vgaregs(vgaregs2); p = get_mode_param(scp, scp->mode); if (p != NULL) { printf("sc%d: VGA registers to be used for mode:%d\n", dev->id_unit, scp->mode); dump_vgaregs(p); } printf("sc%d: rows_offset:%d\n", dev->id_unit, rows_offset); } if ((crtc_type == KD_VGA) && !VESA_MODE(bios_video_mode) && (video_mode_ptr == NULL)) printf("sc%d: WARNING: video mode switching is only partially supported\n", dev->id_unit); printf("sc%d: ", dev->id_unit); switch(crtc_type) { case KD_VGA: if (VESA_MODE(bios_video_mode)) printf("Graphics display (VESA mode = 0x%x)", bios_video_mode); else if (crtc_addr == MONO_BASE) printf("VGA mono"); else printf("VGA color"); break; case KD_EGA: if (crtc_addr == MONO_BASE) printf("EGA mono"); else printf("EGA color"); break; case KD_CGA: printf("CGA"); break; case KD_MONO: case KD_HERCULES: default: printf("MDA/hercules"); break; } printf(" <%d virtual consoles, flags=0x%x>\n", MAXCONS, flags); #if NAPM > 0 scp->r_hook.ah_fun = scresume; scp->r_hook.ah_arg = NULL; scp->r_hook.ah_name = "system keyboard"; scp->r_hook.ah_order = APM_MID_ORDER; apm_hook_establish(APM_HOOK_RESUME , &scp->r_hook); #endif at_shutdown(scshutdown, NULL, SHUTDOWN_PRE_SYNC); - cdevsw_add(&cdev, &scdevsw, NULL); + cdevsw_add(&cdev, &sc_cdevsw, NULL); #ifdef DEVFS for (vc = 0; vc < MAXCONS; vc++) - sc_devfs_token[vc] = devfs_add_devswf(&scdevsw, vc, DV_CHR, + sc_devfs_token[vc] = devfs_add_devswf(&sc_cdevsw, vc, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyv%r", vc); sc_vga_devfs_token = devfs_link(sc_devfs_token[0], "vga"); - sc_mouse_devfs_token = devfs_add_devswf(&scdevsw, SC_MOUSE, DV_CHR, + sc_mouse_devfs_token = devfs_add_devswf(&sc_cdevsw, SC_MOUSE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "sysmouse"); - sc_console_devfs_token = devfs_add_devswf(&scdevsw, SC_CONSOLE, DV_CHR, + sc_console_devfs_token = devfs_add_devswf(&sc_cdevsw, SC_CONSOLE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "consolectl"); #endif return 0; } struct tty *scdevtotty(dev_t dev) { int unit = minor(dev); if (init_done == COLD) return(NULL); if (unit == SC_CONSOLE) return CONSOLE_TTY; if (unit == SC_MOUSE) return MOUSE_TTY; if (unit >= MAXCONS || unit < 0) return(NULL); return VIRTUAL_TTY(unit); } int scopen(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); tp->t_oproc = (minor(dev) == SC_MOUSE) ? scmousestart : scstart; tp->t_param = scparam; tp->t_dev = dev; if (!(tp->t_state & TS_ISOPEN)) { ttychars(tp); /* Use the current setting of the <-- key as default VERASE. */ /* If the Delete key is preferable, an stty is necessary */ tp->t_cc[VERASE] = key_map.key[0x0e].map[0]; tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; scparam(tp, &tp->t_termios); - ttsetwater(tp); (*linesw[tp->t_line].l_modem)(tp, 1); if (minor(dev) == SC_MOUSE) mouse_level = 0; /* XXX */ } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) return(EBUSY); if (minor(dev) < MAXCONS && !console[minor(dev)]) { console[minor(dev)] = alloc_scp(); } if (minor(dev)t_winsize.ws_col && !tp->t_winsize.ws_row) { tp->t_winsize.ws_col = console[minor(dev)]->xsize; tp->t_winsize.ws_row = console[minor(dev)]->ysize; } return ((*linesw[tp->t_line].l_open)(dev, tp)); } int scclose(dev_t dev, int flag, int mode, struct proc *p) { struct tty *tp = scdevtotty(dev); struct scr_stat *scp; if (!tp) return(ENXIO); if (minor(dev) < MAXCONS) { scp = get_scr_stat(tp->t_dev); if (scp->status & SWITCH_WAIT_ACQ) wakeup((caddr_t)&scp->smode); #if not_yet_done if (scp == &main_console) { scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; } else { free(scp->scr_buf, M_DEVBUF); if (scp->history != NULL) { free(scp->history, M_DEVBUF); if (scp->history_size / scp->xsize > imax(SC_HISTORY_SIZE, scp->ysize)) extra_history_size += scp->history_size / scp->xsize - imax(SC_HISTORY_SIZE, scp->ysize); } free(scp, M_DEVBUF); console[minor(dev)] = NULL; } #else scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; #endif } spltty(); (*linesw[tp->t_line].l_close)(tp, flag); ttyclose(tp); spl0(); return(0); } int scread(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_read)(tp, uio, flag)); } int scwrite(dev_t dev, struct uio *uio, int flag) { struct tty *tp = scdevtotty(dev); if (!tp) return(ENXIO); return((*linesw[tp->t_line].l_write)(tp, uio, flag)); } void scintr(int unit) { static struct tty *cur_tty; int c, len; u_char *cp; /* * Loop while there is still input to get from the keyboard. * I don't think this is nessesary, and it doesn't fix * the Xaccel-2.1 keyboard hang, but it can't hurt. XXX */ while ((c = scgetc(SCGETC_NONBLOCK)) != NOKEY) { cur_tty = VIRTUAL_TTY(get_scr_num()); if (!(cur_tty->t_state & TS_ISOPEN)) if (!((cur_tty = CONSOLE_TTY)->t_state & TS_ISOPEN)) continue; switch (c & 0xff00) { case 0x0000: /* normal key */ (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case FKEY: /* function key, return string */ if (cp = get_fstr((u_int)c, (u_int *)&len)) { while (len-- > 0) (*linesw[cur_tty->t_line].l_rint)(*cp++ & 0xFF, cur_tty); } break; case MKEY: /* meta is active, prepend ESC */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)(c & 0xFF, cur_tty); break; case BKEY: /* backtab fixed sequence (esc [ Z) */ (*linesw[cur_tty->t_line].l_rint)(0x1b, cur_tty); (*linesw[cur_tty->t_line].l_rint)('[', cur_tty); (*linesw[cur_tty->t_line].l_rint)('Z', cur_tty); break; } } if (cur_console->status & MOUSE_ENABLED) { cur_console->status &= ~MOUSE_VISIBLE; remove_mouse_image(cur_console); } } static int scparam(struct tty *tp, struct termios *t) { tp->t_ispeed = t->c_ispeed; tp->t_ospeed = t->c_ospeed; tp->t_cflag = t->c_cflag; return 0; } int scioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) { int error; u_int i; struct tty *tp; scr_stat *scp; u_short *usp; char *mp; int s; tp = scdevtotty(dev); if (!tp) return ENXIO; scp = get_scr_stat(tp->t_dev); /* If there is a user_ioctl function call that first */ if (sc_user_ioctl) { if (error = (*sc_user_ioctl)(dev, cmd, data, flag, p)) return error; } switch (cmd) { /* process console hardware related ioctl's */ case GIO_ATTR: /* get current attributes */ *(int*)data = (scp->term.cur_attr >> 8) & 0xFF; return 0; case GIO_COLOR: /* is this a color console ? */ if (crtc_addr == COLOR_BASE) *(int*)data = 1; else *(int*)data = 0; return 0; case CONS_CURRENT: /* get current adapter type */ *(int *)data = crtc_type; return 0; case CONS_GET: /* get current video mode */ *(int*)data = scp->mode; return 0; case CONS_BLANKTIME: /* set screen saver timeout (0 = no saver) */ if (*(int *)data < 0 || *(int *)data > MAX_BLANKTIME) return EINVAL; s = spltty(); scrn_blank_time = *(int *)data; run_scrn_saver = (scrn_blank_time != 0); splx(s); return 0; case CONS_CURSORTYPE: /* set cursor type blink/noblink */ if ((*(int*)data) & 0x01) flags |= BLINK_CURSOR; else flags &= ~BLINK_CURSOR; if ((*(int*)data) & 0x02) { if (crtc_type != KD_VGA || VESA_MODE(bios_video_mode)) return ENXIO; flags |= CHAR_CURSOR; } else flags &= ~CHAR_CURSOR; /* * The cursor shape is global property; all virtual consoles * are affected. Update the cursor in the current console... */ if (!(cur_console->status & UNKNOWN_MODE)) { remove_cursor_image(cur_console); if (flags & CHAR_CURSOR) set_destructive_cursor(cur_console); draw_cursor_image(cur_console); } return 0; case CONS_BELLTYPE: /* set bell type sound/visual */ if ((*(int *)data) & 0x01) flags |= VISUAL_BELL; else flags &= ~VISUAL_BELL; if ((*(int *)data) & 0x02) flags |= QUIET_BELL; else flags &= ~QUIET_BELL; return 0; case CONS_HISTORY: /* set history size */ if (*(int *)data > 0) { int lines; /* buffer size to allocate */ int lines0; /* current buffer size */ lines = imax(*(int *)data, scp->ysize); lines0 = (scp->history != NULL) ? scp->history_size / scp->xsize : scp->ysize; /* * syscons unconditionally allocates buffers upto SC_HISTORY_SIZE * lines or scp->ysize lines, whichever is larger. A value * greater than that is allowed, subject to extra_history_size. */ if (lines > imax(lines0, SC_HISTORY_SIZE) + extra_history_size) return EINVAL; if (cur_console->status & BUFFER_SAVED) return EBUSY; usp = scp->history; scp->history = NULL; if (usp != NULL) free(usp, M_DEVBUF); scp->history_size = lines * scp->xsize; /* * extra_history_size += * (lines0 > imax(SC_HISTORY_SIZE, scp->ysize)) ? * lines0 - imax(SC_HISTORY_SIZE, scp->ysize)) : 0; * extra_history_size -= * (lines > imax(SC_HISTORY_SIZE, scp->ysize)) ? * lines - imax(SC_HISTORY_SIZE, scp->ysize)) : 0; * lines0 >= ysize && lines >= ysize... Hey, the above can be * reduced to the following... */ extra_history_size += imax(lines0, SC_HISTORY_SIZE) - imax(lines, SC_HISTORY_SIZE); usp = (u_short *)malloc(scp->history_size * sizeof(u_short), M_DEVBUF, M_WAITOK); bzero(usp, scp->history_size * sizeof(u_short)); scp->history_head = scp->history_pos = usp; scp->history = usp; return 0; } else return EINVAL; case CONS_MOUSECTL: /* control mouse arrow */ case OLD_CONS_MOUSECTL: { /* MOUSE_BUTTON?DOWN -> MOUSE_MSC_BUTTON?UP */ static butmap[8] = { MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON2UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON3UP, MOUSE_MSC_BUTTON1UP | MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON2UP, MOUSE_MSC_BUTTON1UP, 0, }; mouse_info_t *mouse = (mouse_info_t*)data; mouse_info_t buf; if (crtc_type != KD_VGA || VESA_MODE(bios_video_mode)) return ENODEV; if (cmd == OLD_CONS_MOUSECTL) { static unsigned char swapb[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; old_mouse_info_t *old_mouse = (old_mouse_info_t *)data; mouse = &buf; mouse->operation = old_mouse->operation; switch (mouse->operation) { case MOUSE_MODE: mouse->u.mode = old_mouse->u.mode; break; case MOUSE_SHOW: case MOUSE_HIDE: break; case MOUSE_MOVEABS: case MOUSE_MOVEREL: case MOUSE_ACTION: mouse->u.data.x = old_mouse->u.data.x; mouse->u.data.y = old_mouse->u.data.y; mouse->u.data.z = 0; mouse->u.data.buttons = swapb[old_mouse->u.data.buttons & 0x7]; break; case MOUSE_GETINFO: old_mouse->u.data.x = scp->mouse_xpos; old_mouse->u.data.y = scp->mouse_ypos; old_mouse->u.data.buttons = swapb[scp->mouse_buttons & 0x7]; break; default: return EINVAL; } } switch (mouse->operation) { case MOUSE_MODE: if (ISSIGVALID(mouse->u.mode.signal)) { scp->mouse_signal = mouse->u.mode.signal; scp->mouse_proc = p; scp->mouse_pid = p->p_pid; } else { scp->mouse_signal = 0; scp->mouse_proc = NULL; scp->mouse_pid = 0; } return 0; case MOUSE_SHOW: if (!(scp->status & MOUSE_ENABLED)) { scp->status |= (MOUSE_ENABLED | MOUSE_VISIBLE); scp->mouse_oldpos = scp->mouse_pos; mark_all(scp); return 0; } else return EINVAL; break; case MOUSE_HIDE: if (scp->status & MOUSE_ENABLED) { scp->status &= ~(MOUSE_ENABLED | MOUSE_VISIBLE); mark_all(scp); return 0; } else return EINVAL; break; case MOUSE_MOVEABS: scp->mouse_xpos = mouse->u.data.x; scp->mouse_ypos = mouse->u.data.y; set_mouse_pos(scp); break; case MOUSE_MOVEREL: scp->mouse_xpos += mouse->u.data.x; scp->mouse_ypos += mouse->u.data.y; set_mouse_pos(scp); break; case MOUSE_GETINFO: mouse->u.data.x = scp->mouse_xpos; mouse->u.data.y = scp->mouse_ypos; mouse->u.data.z = 0; mouse->u.data.buttons = scp->mouse_buttons; return 0; case MOUSE_ACTION: case MOUSE_MOTION_EVENT: /* this should maybe only be settable from /dev/consolectl SOS */ /* send out mouse event on /dev/sysmouse */ mouse_status.dx += mouse->u.data.x; mouse_status.dy += mouse->u.data.y; mouse_status.dz += mouse->u.data.z; if (mouse->operation == MOUSE_ACTION) mouse_status.button = mouse->u.data.buttons; mouse_status.flags |= ((mouse->u.data.x || mouse->u.data.y || mouse->u.data.z) ? MOUSE_POSCHANGED : 0) | (mouse_status.obutton ^ mouse_status.button); if (mouse_status.flags == 0) return 0; if (cur_console->status & MOUSE_ENABLED) cur_console->status |= MOUSE_VISIBLE; if ((MOUSE_TTY)->t_state & TS_ISOPEN) { u_char buf[MOUSE_SYS_PACKETSIZE]; int j; /* the first five bytes are compatible with MouseSystems' */ buf[0] = MOUSE_MSC_SYNC | butmap[mouse_status.button & MOUSE_STDBUTTONS]; j = imax(imin(mouse->u.data.x, 255), -256); buf[1] = j >> 1; buf[3] = j - buf[1]; j = -imax(imin(mouse->u.data.y, 255), -256); buf[2] = j >> 1; buf[4] = j - buf[2]; for (j = 0; j < MOUSE_MSC_PACKETSIZE; j++) (*linesw[(MOUSE_TTY)->t_line].l_rint)(buf[j],MOUSE_TTY); if (mouse_level >= 1) { /* extended part */ j = imax(imin(mouse->u.data.z, 127), -128); buf[5] = (j >> 1) & 0x7f; buf[6] = (j - (j >> 1)) & 0x7f; /* buttons 4-10 */ buf[7] = (~mouse_status.button >> 3) & 0x7f; for (j = MOUSE_MSC_PACKETSIZE; j < MOUSE_SYS_PACKETSIZE; j++) (*linesw[(MOUSE_TTY)->t_line].l_rint)(buf[j],MOUSE_TTY); } } if (cur_console->mouse_signal) { cur_console->mouse_buttons = mouse->u.data.buttons; /* has controlling process died? */ if (cur_console->mouse_proc && (cur_console->mouse_proc != pfind(cur_console->mouse_pid))){ cur_console->mouse_signal = 0; cur_console->mouse_proc = NULL; cur_console->mouse_pid = 0; } else psignal(cur_console->mouse_proc, cur_console->mouse_signal); } else if (mouse->operation == MOUSE_ACTION && cut_buffer != NULL) { /* process button presses */ if ((cur_console->mouse_buttons ^ mouse->u.data.buttons) && !(cur_console->status & UNKNOWN_MODE)) { cur_console->mouse_buttons = mouse->u.data.buttons; if (cur_console->mouse_buttons & MOUSE_BUTTON1DOWN) mouse_cut_start(cur_console); else mouse_cut_end(cur_console); if (cur_console->mouse_buttons & MOUSE_BUTTON2DOWN || cur_console->mouse_buttons & MOUSE_BUTTON3DOWN) mouse_paste(cur_console); } } if (mouse->u.data.x != 0 || mouse->u.data.y != 0) { cur_console->mouse_xpos += mouse->u.data.x; cur_console->mouse_ypos += mouse->u.data.y; set_mouse_pos(cur_console); } break; case MOUSE_BUTTON_EVENT: if ((mouse->u.event.id & MOUSE_BUTTONS) == 0) return EINVAL; if (mouse->u.event.value < 0) return EINVAL; if (mouse->u.event.value > 0) { cur_console->mouse_buttons |= mouse->u.event.id; mouse_status.button |= mouse->u.event.id; } else { cur_console->mouse_buttons &= ~mouse->u.event.id; mouse_status.button &= ~mouse->u.event.id; } mouse_status.flags |= mouse_status.obutton ^ mouse_status.button; if (mouse_status.flags == 0) return 0; if (cur_console->status & MOUSE_ENABLED) cur_console->status |= MOUSE_VISIBLE; if ((MOUSE_TTY)->t_state & TS_ISOPEN) { u_char buf[8]; int i; buf[0] = MOUSE_MSC_SYNC | butmap[mouse_status.button & MOUSE_STDBUTTONS]; buf[7] = (~mouse_status.button >> 3) & 0x7f; buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0; for (i = 0; i < ((mouse_level >= 1) ? MOUSE_SYS_PACKETSIZE : MOUSE_MSC_PACKETSIZE); i++) (*linesw[(MOUSE_TTY)->t_line].l_rint)(buf[i],MOUSE_TTY); } if (cur_console->mouse_signal) { if (cur_console->mouse_proc && (cur_console->mouse_proc != pfind(cur_console->mouse_pid))){ cur_console->mouse_signal = 0; cur_console->mouse_proc = NULL; cur_console->mouse_pid = 0; } else psignal(cur_console->mouse_proc, cur_console->mouse_signal); break; } if ((cur_console->status & UNKNOWN_MODE) || (cut_buffer == NULL)) break; switch (mouse->u.event.id) { case MOUSE_BUTTON1DOWN: switch (mouse->u.event.value % 4) { case 0: /* up */ mouse_cut_end(cur_console); break; case 1: mouse_cut_start(cur_console); break; case 2: mouse_cut_word(cur_console); break; case 3: mouse_cut_line(cur_console); break; } break; case MOUSE_BUTTON2DOWN: switch (mouse->u.event.value) { case 0: /* up */ break; default: mouse_paste(cur_console); break; } break; case MOUSE_BUTTON3DOWN: switch (mouse->u.event.value) { case 0: /* up */ if (!(cur_console->mouse_buttons & MOUSE_BUTTON1DOWN)) mouse_cut_end(cur_console); break; default: mouse_cut_extend(cur_console); break; } break; } break; default: return EINVAL; } /* make screensaver happy */ scsplash_stick(FALSE); run_scrn_saver = FALSE; return 0; } /* MOUSE_XXX: /dev/sysmouse ioctls */ case MOUSE_GETHWINFO: /* get device information */ { mousehw_t *hw = (mousehw_t *)data; if (tp != MOUSE_TTY) return ENOTTY; hw->buttons = 10; /* XXX unknown */ hw->iftype = MOUSE_IF_SYSMOUSE; hw->type = MOUSE_MOUSE; hw->model = MOUSE_MODEL_GENERIC; hw->hwid = 0; return 0; } case MOUSE_GETMODE: /* get protocol/mode */ { mousemode_t *mode = (mousemode_t *)data; if (tp != MOUSE_TTY) return ENOTTY; mode->level = mouse_level; switch (mode->level) { case 0: /* at this level, sysmouse emulates MouseSystems protocol */ mode->protocol = MOUSE_PROTO_MSC; mode->rate = -1; /* unknown */ mode->resolution = -1; /* unknown */ mode->accelfactor = 0; /* disabled */ mode->packetsize = MOUSE_MSC_PACKETSIZE; mode->syncmask[0] = MOUSE_MSC_SYNCMASK; mode->syncmask[1] = MOUSE_MSC_SYNC; break; case 1: /* at this level, sysmouse uses its own protocol */ mode->protocol = MOUSE_PROTO_SYSMOUSE; mode->rate = -1; mode->resolution = -1; mode->accelfactor = 0; mode->packetsize = MOUSE_SYS_PACKETSIZE; mode->syncmask[0] = MOUSE_SYS_SYNCMASK; mode->syncmask[1] = MOUSE_SYS_SYNC; break; } return 0; } case MOUSE_SETMODE: /* set protocol/mode */ { mousemode_t *mode = (mousemode_t *)data; if (tp != MOUSE_TTY) return ENOTTY; if ((mode->level < 0) || (mode->level > 1)) return EINVAL; mouse_level = mode->level; return 0; } case MOUSE_GETLEVEL: /* get operation level */ if (tp != MOUSE_TTY) return ENOTTY; *(int *)data = mouse_level; return 0; case MOUSE_SETLEVEL: /* set operation level */ if (tp != MOUSE_TTY) return ENOTTY; if ((*(int *)data < 0) || (*(int *)data > 1)) return EINVAL; mouse_level = *(int *)data; return 0; case MOUSE_GETSTATUS: /* get accumulated mouse events */ if (tp != MOUSE_TTY) return ENOTTY; s = spltty(); *(mousestatus_t *)data = mouse_status; mouse_status.flags = 0; mouse_status.obutton = mouse_status.button; mouse_status.dx = 0; mouse_status.dy = 0; mouse_status.dz = 0; splx(s); return 0; #if notyet case MOUSE_GETVARS: /* get internal mouse variables */ case MOUSE_SETVARS: /* set internal mouse variables */ if (tp != MOUSE_TTY) return ENOTTY; return ENODEV; #endif case MOUSE_READSTATE: /* read status from the device */ case MOUSE_READDATA: /* read data from the device */ if (tp != MOUSE_TTY) return ENOTTY; return ENODEV; case CONS_GETINFO: /* get current (virtual) console info */ { vid_info_t *ptr = (vid_info_t*)data; if (ptr->size == sizeof(struct vid_info)) { ptr->m_num = get_scr_num(); ptr->mv_col = scp->xpos; ptr->mv_row = scp->ypos; ptr->mv_csz = scp->xsize; ptr->mv_rsz = scp->ysize; ptr->mv_norm.fore = (scp->term.std_color & 0x0f00)>>8; ptr->mv_norm.back = (scp->term.std_color & 0xf000)>>12; ptr->mv_rev.fore = (scp->term.rev_color & 0x0f00)>>8; ptr->mv_rev.back = (scp->term.rev_color & 0xf000)>>12; ptr->mv_grfc.fore = 0; /* not supported */ ptr->mv_grfc.back = 0; /* not supported */ ptr->mv_ovscan = scp->border; ptr->mk_keylock = scp->status & LOCK_KEY_MASK; return 0; } return EINVAL; } case CONS_GETVERS: /* get version number */ *(int*)data = 0x200; /* version 2.0 */ return 0; case CONS_IDLE: /* see if the screen has been idle */ *(int *)data = (scrn_idle && !(cur_console->status & UNKNOWN_MODE)); return 0; case CONS_SAVERMODE: /* set saver mode */ switch(*(int *)data) { case CONS_USR_SAVER: /* if a LKM screen saver is running, it will eventually stop... */ saver_mode = *(int *)data; scsplash_stick(TRUE); break; case CONS_LKM_SAVER: saver_mode = *(int *)data; break; default: return EINVAL; } return 0; case CONS_SAVERSTART: /* immediately start/stop the screen saver */ /* * Note that this ioctl does not guarantee the screen saver * actually starts or stops. It merely attempts to do so... */ s = spltty(); run_scrn_saver = (*(int *)data != 0); if (run_scrn_saver) scrn_time_stamp -= scrn_blank_time; splx(s); return 0; /* VGA TEXT MODES */ case SW_VGA_C40x25: case SW_VGA_C80x25: case SW_VGA_M80x25: case SW_VGA_C80x30: case SW_VGA_M80x30: case SW_VGA_C80x50: case SW_VGA_M80x50: case SW_VGA_C80x60: case SW_VGA_M80x60: case SW_B40x25: case SW_C40x25: case SW_B80x25: case SW_C80x25: case SW_ENH_B40x25: case SW_ENH_C40x25: case SW_ENH_B80x25: case SW_ENH_C80x25: case SW_ENH_B80x43: case SW_ENH_C80x43: case SW_EGAMONO80x25: if (crtc_type != KD_VGA) return ENODEV; mp = get_mode_param(scp, cmd & 0xff); if (mp == NULL) return ENODEV; s = spltty(); if ((error = wait_scrn_saver_stop())) { splx(s); return error; } scp->status &= ~MOUSE_VISIBLE; remove_cutmarking(scp); if (scp->history != NULL) i = imax(scp->history_size / scp->xsize - imax(SC_HISTORY_SIZE, scp->ysize), 0); else i = 0; switch (cmd & 0xff) { case M_VGA_C80x60: case M_VGA_M80x60: if (!(fonts_loaded & FONT_8)) { splx(s); return EINVAL; } /* * This is a kludge to fend off scrn_update() while we * muck around with scp. XXX */ scp->status |= UNKNOWN_MODE; scp->xsize = 80; scp->ysize = 60; scp->font_size = 8; break; case M_VGA_C80x50: case M_VGA_M80x50: if (!(fonts_loaded & FONT_8)) { splx(s); return EINVAL; } scp->status |= UNKNOWN_MODE; scp->xsize = 80; scp->ysize = 50; scp->font_size = 8; break; case M_ENH_B80x43: case M_ENH_C80x43: if (!(fonts_loaded & FONT_8)) { splx(s); return EINVAL; } scp->status |= UNKNOWN_MODE; scp->xsize = 80; scp->ysize = 43; scp->font_size = 8; break; case M_VGA_C80x30: case M_VGA_M80x30: scp->status |= UNKNOWN_MODE; scp->xsize = 80; scp->ysize = 30; scp->font_size = mp[2]; break; case M_ENH_C40x25: case M_ENH_B40x25: case M_ENH_C80x25: case M_ENH_B80x25: case M_EGAMONO80x25: if (!(fonts_loaded & FONT_14)) { splx(s); return EINVAL; } /* FALL THROUGH */ default: if ((cmd & 0xff) > M_VGA_CG320) { splx(s); return EINVAL; } scp->status |= UNKNOWN_MODE; scp->xsize = mp[0]; scp->ysize = mp[1] + rows_offset; scp->font_size = mp[2]; break; } scp->mode = cmd & 0xff; scp->xpixel = scp->xsize * 8; scp->ypixel = scp->ysize * scp->font_size; free(scp->scr_buf, M_DEVBUF); scp->scr_buf = (u_short *) malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_WAITOK); /* move the text cursor to the home position */ move_crsr(scp, 0, 0); /* move the mouse cursor at the center of the screen */ scp->mouse_xpos = scp->xpixel / 2; scp->mouse_ypos = scp->ypixel / 2; scp->mouse_pos = scp->mouse_oldpos = scp->scr_buf + (scp->mouse_ypos / scp->font_size) * scp->xsize + scp->mouse_xpos / 8; /* allocate a larger cut buffer if necessary */ if ((cut_buffer == NULL) || (cut_buffer_size < scp->xsize * scp->ysize + 1)) { if (cut_buffer != NULL) free(cut_buffer, M_DEVBUF); cut_buffer_size = scp->xsize * scp->ysize + 1; cut_buffer = (char *)malloc(cut_buffer_size, M_DEVBUF, M_NOWAIT); if (cut_buffer != NULL) cut_buffer[0] = '\0'; } splx(s); usp = scp->history; scp->history = NULL; if (usp != NULL) { free(usp, M_DEVBUF); extra_history_size += i; } scp->history_size = imax(SC_HISTORY_SIZE, scp->ysize) * scp->xsize; usp = (u_short *)malloc(scp->history_size * sizeof(u_short), M_DEVBUF, M_NOWAIT); if (usp != NULL) bzero(usp, scp->history_size * sizeof(u_short)); scp->history_head = scp->history_pos = usp; scp->history = usp; if (scp == cur_console) set_mode(scp); clear_screen(scp); scp->status &= ~UNKNOWN_MODE; if (tp->t_winsize.ws_col != scp->xsize || tp->t_winsize.ws_row != scp->ysize) { tp->t_winsize.ws_col = scp->xsize; tp->t_winsize.ws_row = scp->ysize; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; /* GRAPHICS MODES */ case SW_BG320: case SW_BG640: case SW_CG320: case SW_CG320_D: case SW_CG640_E: case SW_CG640x350: case SW_ENH_CG640: case SW_BG640x480: case SW_CG640x480: case SW_VGA_CG320: if (crtc_type != KD_VGA) return ENODEV; mp = get_mode_param(scp, cmd & 0xff); if (mp == NULL) return ENODEV; s = spltty(); if ((error = wait_scrn_saver_stop())) { splx(s); return error; } scp->status &= ~MOUSE_VISIBLE; remove_cutmarking(scp); scp->status |= UNKNOWN_MODE; /* graphics mode */ scp->mode = cmd & 0xFF; scp->xpixel = mp[0] * 8; scp->ypixel = (mp[1] + rows_offset) * mp[2]; scp->font_size = FONT_NONE; /* move the mouse cursor at the center of the screen */ scp->mouse_xpos = scp->xpixel / 2; scp->mouse_ypos = scp->ypixel / 2; splx(s); if (scp == cur_console) set_mode(scp); /* clear_graphics();*/ if (tp->t_winsize.ws_xpixel != scp->xpixel || tp->t_winsize.ws_ypixel != scp->ypixel) { tp->t_winsize.ws_xpixel = scp->xpixel; tp->t_winsize.ws_ypixel = scp->ypixel; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; case SW_VGA_MODEX: if (crtc_type != KD_VGA) return ENODEV; mp = get_mode_param(scp, cmd & 0xff); if (mp == NULL) return ENODEV; s = spltty(); if ((error = wait_scrn_saver_stop())) { splx(s); return error; } scp->status &= ~MOUSE_VISIBLE; remove_cutmarking(scp); scp->status |= UNKNOWN_MODE; /* graphics mode */ scp->mode = cmd & 0xFF; scp->xpixel = 320; scp->ypixel = 240; scp->font_size = FONT_NONE; splx(s); if (scp == cur_console) set_mode(scp); /* clear_graphics();*/ if (tp->t_winsize.ws_xpixel != scp->xpixel || tp->t_winsize.ws_ypixel != scp->ypixel) { tp->t_winsize.ws_xpixel = scp->xpixel; tp->t_winsize.ws_ypixel = scp->ypixel; pgsignal(tp->t_pgrp, SIGWINCH, 1); } return 0; case VT_SETMODE: /* set screen switcher mode */ { struct vt_mode *mode; mode = (struct vt_mode *)data; if (ISSIGVALID(mode->relsig) && ISSIGVALID(mode->acqsig) && ISSIGVALID(mode->frsig)) { bcopy(data, &scp->smode, sizeof(struct vt_mode)); if (scp->smode.mode == VT_PROCESS) { scp->proc = p; scp->pid = scp->proc->p_pid; } return 0; } else return EINVAL; } case VT_GETMODE: /* get screen switcher mode */ bcopy(&scp->smode, data, sizeof(struct vt_mode)); return 0; case VT_RELDISP: /* screen switcher ioctl */ switch(*data) { case VT_FALSE: /* user refuses to release screen, abort */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { old_scp->status &= ~SWITCH_WAIT_REL; switch_in_progress = FALSE; return 0; } return EINVAL; case VT_TRUE: /* user has released screen, go on */ if (scp == old_scp && (scp->status & SWITCH_WAIT_REL)) { scp->status &= ~SWITCH_WAIT_REL; exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; return 0; } return EINVAL; case VT_ACKACQ: /* acquire acknowledged, switch completed */ if (scp == new_scp && (scp->status & SWITCH_WAIT_ACQ)) { scp->status &= ~SWITCH_WAIT_ACQ; switch_in_progress = FALSE; return 0; } return EINVAL; default: return EINVAL; } /* NOT REACHED */ case VT_OPENQRY: /* return free virtual console */ for (i = 0; i < MAXCONS; i++) { tp = VIRTUAL_TTY(i); if (!(tp->t_state & TS_ISOPEN)) { *data = i + 1; return 0; } } return EINVAL; case VT_ACTIVATE: /* switch to screen *data */ return switch_scr(scp, (*data) - 1); case VT_WAITACTIVE: /* wait for switch to occur */ if (*data > MAXCONS || *data < 0) return EINVAL; if (minor(dev) == (*data) - 1) return 0; if (*data == 0) { if (scp == cur_console) return 0; } else scp = console[(*data) - 1]; while ((error=tsleep((caddr_t)&scp->smode, PZERO|PCATCH, "waitvt", 0)) == ERESTART) ; return error; case VT_GETACTIVE: *data = get_scr_num()+1; return 0; case KDENABIO: /* allow io operations */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return error; if (securelevel > 0) return EPERM; p->p_md.md_regs->tf_eflags |= PSL_IOPL; return 0; case KDDISABIO: /* disallow io operations (default) */ p->p_md.md_regs->tf_eflags &= ~PSL_IOPL; return 0; case KDSETMODE: /* set current mode of this (virtual) console */ switch (*data) { case KD_TEXT: /* switch to TEXT (known) mode */ /* restore fonts & palette ! */ if (crtc_type == KD_VGA) { if (!VESA_MODE(scp->mode)) { #if 0 /* * FONT KLUDGE * Don't load fonts for now... XXX */ if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); #endif } load_palette(palette); } /* move hardware cursor out of the way */ outb(crtc_addr, 14); outb(crtc_addr + 1, 0xff); outb(crtc_addr, 15); outb(crtc_addr + 1, 0xff); /* FALL THROUGH */ case KD_TEXT1: /* switch to TEXT (known) mode */ s = spltty(); if ((error = wait_scrn_saver_stop())) { splx(s); return error; } scp->status &= ~MOUSE_VISIBLE; remove_cutmarking(scp); scp->status |= UNKNOWN_MODE; splx(s); /* no restore fonts & palette */ if (crtc_type == KD_VGA) set_mode(scp); scp->status &= ~UNKNOWN_MODE; clear_screen(scp); return 0; case KD_GRAPHICS: /* switch to GRAPHICS (unknown) mode */ s = spltty(); if ((error = wait_scrn_saver_stop())) { splx(s); return error; } scp->status &= ~MOUSE_VISIBLE; remove_cutmarking(scp); scp->status |= UNKNOWN_MODE; splx(s); return 0; default: return EINVAL; } /* NOT REACHED */ case KDGETMODE: /* get current mode of this (virtual) console */ *data = (scp->status & UNKNOWN_MODE) ? KD_GRAPHICS : KD_TEXT; return 0; case KDSBORDER: /* set border color of this (virtual) console */ scp->border = *data; if (scp == cur_console) set_border(scp->border); return 0; case KDSKBSTATE: /* set keyboard state (locks) */ if (*data >= 0 && *data <= LOCK_KEY_MASK) { scp->status &= ~LOCK_KEY_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGKBSTATE: /* get keyboard state (locks) */ *data = scp->status & LOCK_KEY_MASK; return 0; case KDSETRAD: /* set keyboard repeat & delay rates */ if (*data & 0x80) return EINVAL; if (sc_kbdc != NULL) set_keyboard(KBDC_SET_TYPEMATIC, *data); return 0; case KDSKBMODE: /* set keyboard mode */ switch (*data) { case K_RAW: /* switch to RAW scancode mode */ scp->status &= ~KBD_CODE_MODE; scp->status |= KBD_RAW_MODE; return 0; case K_CODE: /* switch to CODE mode */ scp->status &= ~KBD_RAW_MODE; scp->status |= KBD_CODE_MODE; return 0; case K_XLATE: /* switch to XLT ascii mode */ if (scp == cur_console && scp->status & KBD_RAW_MODE) shfts = ctls = alts = agrs = metas = accents = 0; scp->status &= ~(KBD_RAW_MODE | KBD_CODE_MODE); return 0; default: return EINVAL; } /* NOT REACHED */ case KDGKBMODE: /* get keyboard mode */ *data = (scp->status & KBD_RAW_MODE) ? K_RAW : ((scp->status & KBD_CODE_MODE) ? K_CODE : K_XLATE); return 0; case KDMKTONE: /* sound the bell */ if (*(int*)data) do_bell(scp, (*(int*)data)&0xffff, (((*(int*)data)>>16)&0xffff)*hz/1000); else do_bell(scp, scp->bell_pitch, scp->bell_duration); return 0; case KIOCSOUND: /* make tone (*data) hz */ if (scp == cur_console) { if (*(int*)data) { int pitch = timer_freq / *(int*)data; /* set command for counter 2, 2 byte write */ if (acquire_timer2(TIMER_16BIT|TIMER_SQWAVE)) return EBUSY; /* set pitch */ outb(TIMER_CNTR2, pitch); outb(TIMER_CNTR2, (pitch>>8)); /* enable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) | 3); } else { /* disable counter 2 output to speaker */ outb(IO_PPI, inb(IO_PPI) & 0xFC); release_timer2(); } } return 0; case KDGKBTYPE: /* get keyboard type */ *data = 0; /* type not known (yet) */ return 0; case KDSETLED: /* set keyboard LED status */ if (*data >= 0 && *data <= LED_MASK) { scp->status &= ~LED_MASK; scp->status |= *data; if (scp == cur_console) update_leds(scp->status); return 0; } return EINVAL; case KDGETLED: /* get keyboard LED status */ *data = scp->status & LED_MASK; return 0; case GETFKEY: /* get functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(&fkey_tab[ptr->keynum].str, ptr->keydef, fkey_tab[ptr->keynum].len); ptr->flen = fkey_tab[ptr->keynum].len; return 0; } else return EINVAL; case SETFKEY: /* set functionkey string */ if (*(u_short*)data < n_fkey_tab) { fkeyarg_t *ptr = (fkeyarg_t*)data; bcopy(ptr->keydef, &fkey_tab[ptr->keynum].str, min(ptr->flen, MAXFK)); fkey_tab[ptr->keynum].len = min(ptr->flen, MAXFK); return 0; } else return EINVAL; case GIO_SCRNMAP: /* get output translation table */ bcopy(&scr_map, data, sizeof(scr_map)); return 0; case PIO_SCRNMAP: /* set output translation table */ bcopy(data, &scr_map, sizeof(scr_map)); for (i=0; imode) && !(cur_console->status & UNKNOWN_MODE) && (cur_console->font_size < 14)) { copy_font(LOAD, FONT_8, font_8); if (flags & CHAR_CURSOR) set_destructive_cursor(cur_console); } return 0; case GIO_FONT8x8: /* get 8x8 dot font */ if (crtc_type != KD_VGA) return ENXIO; if (fonts_loaded & FONT_8) { bcopy(font_8, data, 8*256); return 0; } else return ENXIO; case PIO_FONT8x14: /* set 8x14 dot font */ if (crtc_type != KD_VGA) return ENXIO; bcopy(data, font_14, 14*256); fonts_loaded |= FONT_14; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x14. */ if (!VESA_MODE(cur_console->mode) && !(cur_console->status & UNKNOWN_MODE) && (cur_console->font_size >= 14) && (cur_console->font_size < 16)) { copy_font(LOAD, FONT_14, font_14); if (flags & CHAR_CURSOR) set_destructive_cursor(cur_console); } return 0; case GIO_FONT8x14: /* get 8x14 dot font */ if (crtc_type != KD_VGA) return ENXIO; if (fonts_loaded & FONT_14) { bcopy(font_14, data, 14*256); return 0; } else return ENXIO; case PIO_FONT8x16: /* set 8x16 dot font */ if (crtc_type != KD_VGA) return ENXIO; bcopy(data, font_16, 16*256); fonts_loaded |= FONT_16; /* * FONT KLUDGE * Always use the font page #0. XXX * Don't load if the current font size is not 8x16. */ if (!VESA_MODE(cur_console->mode) && !(cur_console->status & UNKNOWN_MODE) && (cur_console->font_size >= 16)) { copy_font(LOAD, FONT_16, font_16); if (flags & CHAR_CURSOR) set_destructive_cursor(cur_console); } return 0; case GIO_FONT8x16: /* get 8x16 dot font */ if (crtc_type != KD_VGA) return ENXIO; if (fonts_loaded & FONT_16) { bcopy(font_16, data, 16*256); return 0; } else return ENXIO; default: break; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return(error); error = ttioctl(tp, cmd, data, flag); if (error != ENOIOCTL) return(error); return(ENOTTY); } static void scstart(struct tty *tp) { struct clist *rbp; int s, len; u_char buf[PCBURST]; scr_stat *scp = get_scr_stat(tp->t_dev); if (scp->status & SLKED || blink_in_progress) return; /* XXX who repeats the call when the above flags are cleared? */ s = spltty(); if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { tp->t_state |= TS_BUSY; rbp = &tp->t_outq; while (rbp->c_cc) { len = q_to_b(rbp, buf, PCBURST); splx(s); ansi_put(scp, buf, len); s = spltty(); } tp->t_state &= ~TS_BUSY; ttwwakeup(tp); } splx(s); } static void scmousestart(struct tty *tp) { struct clist *rbp; int s; u_char buf[PCBURST]; s = spltty(); if (!(tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP))) { tp->t_state |= TS_BUSY; rbp = &tp->t_outq; while (rbp->c_cc) { q_to_b(rbp, buf, PCBURST); } tp->t_state &= ~TS_BUSY; ttwwakeup(tp); } splx(s); } void sccnprobe(struct consdev *cp) { struct isa_device *dvp; /* * Take control if we are the highest priority enabled display device. */ dvp = find_display(); if (dvp == NULL || dvp->id_driver != &scdriver) { cp->cn_pri = CN_DEAD; return; } if (!scvidprobe(dvp->id_unit, dvp->id_flags)) { cp->cn_pri = CN_DEAD; return; } /* initialize required fields */ cp->cn_dev = makedev(CDEV_MAJOR, SC_CONSOLE); cp->cn_pri = CN_INTERNAL; sc_kbdc = kbdc_open(sc_port); } void sccninit(struct consdev *cp) { scinit(); } void sccnputc(dev_t dev, int c) { u_char buf[1]; int s; scr_stat *scp = console[0]; term_stat save = scp->term; scp->term = kernel_console; current_default = &kernel_default; if (scp == cur_console && !(scp->status & UNKNOWN_MODE)) remove_cursor_image(scp); buf[0] = c; ansi_put(scp, buf, 1); kernel_console = scp->term; current_default = &user_default; scp->term = save; s = spltty(); /* block scintr and scrn_timer */ sccnupdate(scp); splx(s); } int sccngetc(dev_t dev) { int s = spltty(); /* block scintr and scrn_timer while we poll */ int c; /* * Stop the screen saver and update the screen if necessary. * What if we have been running in the screen saver code... XXX */ scsplash_stick(FALSE); run_scrn_saver = FALSE; sccnupdate(cur_console); c = scgetc(SCGETC_CN); splx(s); return(c); } int sccncheckc(dev_t dev) { int s = spltty(); /* block scintr and scrn_timer while we poll */ int c; scsplash_stick(FALSE); run_scrn_saver = FALSE; sccnupdate(cur_console); c = scgetc(SCGETC_CN | SCGETC_NONBLOCK); splx(s); return(c == NOKEY ? -1 : c); /* c == -1 can't happen */ } static void sccnupdate(scr_stat *scp) { /* this is a cut-down version of scrn_timer()... */ if (font_loading_in_progress) return; if (panicstr) { scsplash_stick(FALSE); run_scrn_saver = FALSE; } if (!run_scrn_saver) scrn_idle = FALSE; if ((saver_mode != CONS_LKM_SAVER) || !scrn_idle) if (scrn_blanked > 0) stop_scrn_saver(current_saver); if (scp != cur_console || blink_in_progress || switch_in_progress) return; if ((scp->status & UNKNOWN_MODE) == 0 && scrn_blanked <= 0) scrn_update(scp, TRUE); } static scr_stat *get_scr_stat(dev_t dev) { int unit = minor(dev); if (unit == SC_CONSOLE) return console[0]; if (unit >= MAXCONS || unit < 0) return(NULL); return console[unit]; } static int get_scr_num() { int i = 0; while ((i < MAXCONS) && (cur_console != console[i])) i++; return i < MAXCONS ? i : 0; } static void scrn_timer(void *arg) { struct timeval tv; scr_stat *scp; int s; /* don't do anything when we are touching font */ if (font_loading_in_progress) { if (arg) timeout(scrn_timer, (void *)TRUE, hz / 10); return; } s = spltty(); /* * With release 2.1 of the Xaccel server, the keyboard is left * hanging pretty often. Apparently an interrupt from the * keyboard is lost, and I don't know why (yet). * This ugly hack calls scintr if input is ready for the keyboard * and conveniently hides the problem. XXX */ /* Try removing anything stuck in the keyboard controller; whether * it's a keyboard scan code or mouse data. `scintr()' doesn't * read the mouse data directly, but `kbdio' routines will, as a * side effect. */ if (kbdc_lock(sc_kbdc, TRUE)) { /* * We have seen the lock flag is not set. Let's reset the flag early; * otherwise `update_led()' failes which may want the lock * during `scintr()'. */ kbdc_lock(sc_kbdc, FALSE); if (kbdc_data_ready(sc_kbdc)) scintr(0); } /* should we stop the screen saver? */ getmicrouptime(&tv); if (panicstr || shutdown_in_progress) { scsplash_stick(FALSE); run_scrn_saver = FALSE; } if (run_scrn_saver) { scrn_idle = (tv.tv_sec > scrn_time_stamp + scrn_blank_time); } else { scrn_time_stamp = tv.tv_sec; scrn_idle = FALSE; if (scrn_blank_time > 0) run_scrn_saver = TRUE; } if ((saver_mode != CONS_LKM_SAVER) || !scrn_idle) if (scrn_blanked > 0) stop_scrn_saver(current_saver); /* should we just return ? */ if (blink_in_progress || switch_in_progress) { if (arg) timeout(scrn_timer, (void *)TRUE, hz / 10); splx(s); return; } /* Update the screen */ scp = cur_console; if ((scp->status & UNKNOWN_MODE) == 0 && scrn_blanked <= 0) scrn_update(scp, TRUE); /* should we activate the screen saver? */ if ((saver_mode == CONS_LKM_SAVER) && scrn_idle) if ((scp->status & UNKNOWN_MODE) == 0 || scrn_blanked > 0) (*current_saver)(TRUE); if (arg) timeout(scrn_timer, (void *)TRUE, hz / 25); splx(s); } static void scrn_update(scr_stat *scp, int show_cursor) { /* update screen image */ if (scp->start <= scp->end) sc_bcopy(scp, scp->scr_buf, scp->start, scp->end, 0); /* we are not to show the cursor and the mouse pointer... */ if (!show_cursor) { scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; return; } /* update "pseudo" mouse pointer image */ if (scp->status & MOUSE_VISIBLE) { /* did mouse move since last time ? */ if (scp->status & MOUSE_MOVED) { /* do we need to remove old mouse pointer image ? */ if (scp->mouse_cut_start != NULL || (scp->mouse_pos-scp->scr_buf) <= scp->start || (scp->mouse_pos+scp->xsize + 1 - scp->scr_buf) >= scp->end) { remove_mouse_image(scp); } scp->status &= ~MOUSE_MOVED; draw_mouse_image(scp); } else { /* mouse didn't move, has it been overwritten ? */ if ((scp->mouse_pos+scp->xsize + 1 - scp->scr_buf) >= scp->start && (scp->mouse_pos - scp->scr_buf) <= scp->end) { draw_mouse_image(scp); } } } /* update cursor image */ if (scp->status & CURSOR_ENABLED) { /* did cursor move since last time ? */ if (scp->cursor_pos != scp->cursor_oldpos) { /* do we need to remove old cursor image ? */ if ((scp->cursor_oldpos - scp->scr_buf) < scp->start || ((scp->cursor_oldpos - scp->scr_buf) > scp->end)) { remove_cursor_image(scp); } scp->cursor_oldpos = scp->cursor_pos; draw_cursor_image(scp); } else { /* cursor didn't move, has it been overwritten ? */ if (scp->cursor_pos - scp->scr_buf >= scp->start && scp->cursor_pos - scp->scr_buf <= scp->end) { draw_cursor_image(scp); } else { /* if its a blinking cursor, we may have to update it */ if (flags & BLINK_CURSOR) draw_cursor_image(scp); } } blinkrate++; } if (scp->mouse_cut_start != NULL) draw_cutmarking(scp); scp->end = 0; scp->start = scp->xsize*scp->ysize - 1; } int add_scrn_saver(void (*this_saver)(int)) { #ifdef SC_SPLASH_SCREEN if (current_saver == scsplash) { scsplash_stick(FALSE); stop_scrn_saver(scsplash); } #endif if (current_saver != default_saver) return EBUSY; current_saver = this_saver; saver_mode = CONS_LKM_SAVER; run_scrn_saver = (scrn_blank_time > 0); return 0; } int remove_scrn_saver(void (*this_saver)(int)) { if (current_saver != this_saver) return EINVAL; /* * In order to prevent `current_saver' from being called by * the timeout routine `scrn_timer()' while we manipulate * the saver list, we shall set `current_saver' to `none_saver' * before stopping the current saver, rather than blocking by `splXX()'. */ current_saver = none_saver; if (scrn_blanked > 0) stop_scrn_saver(this_saver); if (scrn_blanked > 0) return EBUSY; /* XXX */ current_saver = default_saver; return 0; } static void stop_scrn_saver(void (*saver)(int)) { (*saver)(FALSE); run_scrn_saver = FALSE; /* the screen saver may have chosen not to stop after all... */ if (scrn_blanked > 0) return; mark_all(cur_console); if (delayed_next_scr) switch_scr(cur_console, delayed_next_scr - 1); wakeup((caddr_t)&scrn_blanked); } static int wait_scrn_saver_stop(void) { int error = 0; run_scrn_saver = FALSE; while (scrn_blanked > 0) { error = tsleep((caddr_t)&scrn_blanked, PZERO | PCATCH, "scrsav", 0); run_scrn_saver = FALSE; if (error != ERESTART) break; } return error; } static void clear_screen(scr_stat *scp) { move_crsr(scp, 0, 0); scp->cursor_oldpos = scp->cursor_pos; fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize * scp->ysize); mark_all(scp); remove_cutmarking(scp); } static int switch_scr(scr_stat *scp, u_int next_scr) { /* delay switch if actively updating screen */ if (scrn_blanked > 0 || write_in_progress || blink_in_progress) { scsplash_stick(FALSE); run_scrn_saver = FALSE; delayed_next_scr = next_scr+1; return 0; } if (switch_in_progress && (cur_console->proc != pfind(cur_console->pid))) switch_in_progress = FALSE; if (next_scr >= MAXCONS || switch_in_progress || (cur_console->smode.mode == VT_AUTO && cur_console->status & UNKNOWN_MODE)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } /* is the wanted virtual console open ? */ if (next_scr) { struct tty *tp = VIRTUAL_TTY(next_scr); if (!(tp->t_state & TS_ISOPEN)) { do_bell(scp, BELL_PITCH, BELL_DURATION); return EINVAL; } } switch_in_progress = TRUE; old_scp = cur_console; new_scp = console[next_scr]; wakeup((caddr_t)&new_scp->smode); if (new_scp == old_scp) { switch_in_progress = FALSE; delayed_next_scr = FALSE; return 0; } /* has controlling process died? */ if (old_scp->proc && (old_scp->proc != pfind(old_scp->pid))) old_scp->smode.mode = VT_AUTO; if (new_scp->proc && (new_scp->proc != pfind(new_scp->pid))) new_scp->smode.mode = VT_AUTO; /* check the modes and switch appropriately */ if (old_scp->smode.mode == VT_PROCESS) { old_scp->status |= SWITCH_WAIT_REL; psignal(old_scp->proc, old_scp->smode.relsig); } else { exchange_scr(); if (new_scp->smode.mode == VT_PROCESS) { new_scp->status |= SWITCH_WAIT_ACQ; psignal(new_scp->proc, new_scp->smode.acqsig); } else switch_in_progress = FALSE; } return 0; } static void exchange_scr(void) { move_crsr(old_scp, old_scp->xpos, old_scp->ypos); cur_console = new_scp; if (old_scp->mode != new_scp->mode || (old_scp->status & UNKNOWN_MODE)){ if (crtc_type == KD_VGA) set_mode(new_scp); } move_crsr(new_scp, new_scp->xpos, new_scp->ypos); if (!(new_scp->status & UNKNOWN_MODE) && (flags & CHAR_CURSOR)) set_destructive_cursor(new_scp); if ((old_scp->status & UNKNOWN_MODE) && crtc_type == KD_VGA) load_palette(palette); if (old_scp->status & KBD_RAW_MODE || new_scp->status & KBD_RAW_MODE || old_scp->status & KBD_CODE_MODE || new_scp->status & KBD_CODE_MODE) shfts = ctls = alts = agrs = metas = accents = 0; set_border(new_scp->border); update_leds(new_scp->status); delayed_next_scr = FALSE; mark_all(new_scp); if (new_scp->mode == 0x102) { bzero(Crtat, 800*600/8); } } static void scan_esc(scr_stat *scp, u_char c) { static u_char ansi_col[16] = {0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15}; int i, n; u_short *src, *dst, count; if (scp->term.esc == 1) { /* seen ESC */ switch (c) { case '7': /* Save cursor position */ scp->saved_xpos = scp->xpos; scp->saved_ypos = scp->ypos; break; case '8': /* Restore saved cursor position */ if (scp->saved_xpos >= 0 && scp->saved_ypos >= 0) move_crsr(scp, scp->saved_xpos, scp->saved_ypos); break; case '[': /* Start ESC [ sequence */ scp->term.esc = 2; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'M': /* Move cursor up 1 line, scroll if at top */ if (scp->ypos > 0) move_crsr(scp, scp->xpos, scp->ypos - 1); else { bcopy(scp->scr_buf, scp->scr_buf + scp->xsize, (scp->ysize - 1) * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize); mark_all(scp); } break; #if notyet case 'Q': scp->term.esc = 4; return; #endif case 'c': /* Clear screen & home */ clear_screen(scp); break; case '(': /* iso-2022: designate 94 character set to G0 */ scp->term.esc = 5; return; } } else if (scp->term.esc == 2) { /* seen ESC [ */ if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case '=': scp->term.esc = 3; scp->term.last_param = -1; for (i = scp->term.num_param; i < MAX_ESC_PAR; i++) scp->term.param[i] = 1; scp->term.num_param = 0; return; case 'A': /* up n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos - n); break; case 'B': /* down n rows */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'C': /* right n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'D': /* left n columns */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos - n, scp->ypos); break; case 'E': /* cursor to start of line n lines down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos + n); break; case 'F': /* cursor to start of line n lines up */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, 0, scp->ypos - n); break; case 'f': /* Cursor move */ case 'H': if (scp->term.num_param == 0) move_crsr(scp, 0, 0); else if (scp->term.num_param == 2) move_crsr(scp, scp->term.param[1] - 1, scp->term.param[0] - 1); break; case 'J': /* Clear all or part of display */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of display */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, scp->scr_buf + scp->xsize * scp->ysize - scp->cursor_pos); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->xsize * scp->ysize); remove_cutmarking(scp); break; case 1: /* clear from beginning of display to cursor */ fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, 0); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); remove_cutmarking(scp); break; case 2: /* clear entire display */ fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize * scp->ysize); mark_all(scp); remove_cutmarking(scp); break; } break; case 'K': /* Clear all or part of line */ if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* clear form cursor to end of line */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, scp->xsize - scp->xpos); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + scp->xsize - scp->xpos); break; case 1: /* clear from beginning of line to cursor */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos - scp->xpos, scp->xpos + 1); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); break; case 2: /* clear entire line */ fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos - scp->xpos, scp->xsize); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, (scp->ypos + 1) * scp->xsize); break; } break; case 'L': /* Insert n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; src = scp->scr_buf + scp->ypos * scp->xsize; dst = src + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopy(src, dst, count * scp->xsize * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], src, n * scp->xsize); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->xsize * scp->ysize); break; case 'M': /* Delete n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize - scp->ypos) n = scp->ysize - scp->ypos; dst = scp->scr_buf + scp->ypos * scp->xsize; src = dst + n * scp->xsize; count = scp->ysize - (scp->ypos + n); bcopy(src, dst, count * scp->xsize * sizeof(u_short)); src = dst + count * scp->xsize; fillw(scp->term.cur_color | scr_map[0x20], src, n * scp->xsize); mark_for_update(scp, scp->ypos * scp->xsize); mark_for_update(scp, scp->xsize * scp->ysize); break; case 'P': /* Delete n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; dst = scp->cursor_pos; src = dst + n; count = scp->xsize - (scp->xpos + n); bcopy(src, dst, count * sizeof(u_short)); src = dst + count; fillw(scp->term.cur_color | scr_map[0x20], src, n); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n + count); break; case '@': /* Insert n chars */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; src = scp->cursor_pos; dst = src + n; count = scp->xsize - (scp->xpos + n); bcopy(src, dst, count * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], src, n); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n + count); break; case 'S': /* scroll up n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopy(scp->scr_buf + (scp->xsize * n), scp->scr_buf, scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - n), scp->xsize * n); mark_all(scp); break; case 'T': /* scroll down n lines */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->ysize) n = scp->ysize; bcopy(scp->scr_buf, scp->scr_buf + (scp->xsize * n), scp->xsize * (scp->ysize - n) * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf, scp->xsize * n); mark_all(scp); break; case 'X': /* erase n characters in line */ n = scp->term.param[0]; if (n < 1) n = 1; if (n > scp->xsize - scp->xpos) n = scp->xsize - scp->xpos; fillw(scp->term.cur_color | scr_map[0x20], scp->cursor_pos, n); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, scp->cursor_pos - scp->scr_buf + n); break; case 'Z': /* move n tabs backwards */ n = scp->term.param[0]; if (n < 1) n = 1; if ((i = scp->xpos & 0xf8) == scp->xpos) i -= 8*n; else i -= 8*(n-1); if (i < 0) i = 0; move_crsr(scp, i, scp->ypos); break; case '`': /* move cursor to column n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, n - 1, scp->ypos); break; case 'a': /* move cursor n columns to the right */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos + n, scp->ypos); break; case 'd': /* move cursor to row n */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, n - 1); break; case 'e': /* move cursor n rows down */ n = scp->term.param[0]; if (n < 1) n = 1; move_crsr(scp, scp->xpos, scp->ypos + n); break; case 'm': /* change attribute */ if (scp->term.num_param == 0) { scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color; break; } for (i = 0; i < scp->term.num_param; i++) { switch (n = scp->term.param[i]) { case 0: /* back to normal */ scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color; break; case 1: /* bold */ scp->term.attr_mask |= BOLD_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 4: /* underline */ scp->term.attr_mask |= UNDERLINE_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 5: /* blink */ scp->term.attr_mask |= BLINK_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 7: /* reverse video */ scp->term.attr_mask |= REVERSE_ATTR; scp->term.cur_attr = mask2attr(&scp->term); break; case 30: case 31: /* set fg color */ case 32: case 33: case 34: case 35: case 36: case 37: scp->term.attr_mask |= FOREGROUND_CHANGED; scp->term.cur_color = (scp->term.cur_color&0xF000) | (ansi_col[(n-30)&7]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 40: case 41: /* set bg color */ case 42: case 43: case 44: case 45: case 46: case 47: scp->term.attr_mask |= BACKGROUND_CHANGED; scp->term.cur_color = (scp->term.cur_color&0x0F00) | (ansi_col[(n-40)&7]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; } } break; case 's': /* Save cursor position */ scp->saved_xpos = scp->xpos; scp->saved_ypos = scp->ypos; break; case 'u': /* Restore saved cursor position */ if (scp->saved_xpos >= 0 && scp->saved_ypos >= 0) move_crsr(scp, scp->saved_xpos, scp->saved_ypos); break; case 'x': if (scp->term.num_param == 0) n = 0; else n = scp->term.param[0]; switch (n) { case 0: /* reset attributes */ scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color = current_default->std_color; scp->term.rev_color = current_default->rev_color; break; case 1: /* set ansi background */ scp->term.attr_mask &= ~BACKGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; case 2: /* set ansi foreground */ scp->term.attr_mask &= ~FOREGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 3: /* set ansi attribute directly */ scp->term.attr_mask &= ~(FOREGROUND_CHANGED|BACKGROUND_CHANGED); scp->term.cur_color = scp->term.std_color = (scp->term.param[1]&0xFF)<<8; scp->term.cur_attr = mask2attr(&scp->term); break; case 5: /* set ansi reverse video background */ scp->term.rev_color = (scp->term.rev_color & 0x0F00) | (ansi_col[(scp->term.param[1])&0x0F]<<12); scp->term.cur_attr = mask2attr(&scp->term); break; case 6: /* set ansi reverse video foreground */ scp->term.rev_color = (scp->term.rev_color & 0xF000) | (ansi_col[(scp->term.param[1])&0x0F]<<8); scp->term.cur_attr = mask2attr(&scp->term); break; case 7: /* set ansi reverse video directly */ scp->term.rev_color = (scp->term.param[1]&0xFF)<<8; scp->term.cur_attr = mask2attr(&scp->term); break; } break; case 'z': /* switch to (virtual) console n */ if (scp->term.num_param == 1) switch_scr(scp, scp->term.param[0]); break; } } else if (scp->term.esc == 3) { /* seen ESC [0-9]+ = */ if (c >= '0' && c <= '9') { if (scp->term.num_param < MAX_ESC_PAR) { if (scp->term.last_param != scp->term.num_param) { scp->term.last_param = scp->term.num_param; scp->term.param[scp->term.num_param] = 0; } else scp->term.param[scp->term.num_param] *= 10; scp->term.param[scp->term.num_param] += c - '0'; return; } } scp->term.num_param = scp->term.last_param + 1; switch (c) { case ';': if (scp->term.num_param < MAX_ESC_PAR) return; break; case 'A': /* set display border color */ if (scp->term.num_param == 1) { scp->border=scp->term.param[0] & 0xff; if (scp == cur_console) set_border(scp->border); } break; case 'B': /* set bell pitch and duration */ if (scp->term.num_param == 2) { scp->bell_pitch = scp->term.param[0]; scp->bell_duration = scp->term.param[1]; } break; case 'C': /* set cursor type & shape */ if (scp->term.num_param == 1) { if (scp->term.param[0] & 0x01) flags |= BLINK_CURSOR; else flags &= ~BLINK_CURSOR; if ((scp->term.param[0] & 0x02) && crtc_type == KD_VGA && !VESA_MODE(bios_video_mode)) flags |= CHAR_CURSOR; else flags &= ~CHAR_CURSOR; } else if (scp->term.num_param == 2) { scp->cursor_start = scp->term.param[0] & 0x1F; scp->cursor_end = scp->term.param[1] & 0x1F; } /* * The cursor shape is global property; all virtual consoles * are affected. Update the cursor in the current console... */ if (!(cur_console->status & UNKNOWN_MODE)) { remove_cursor_image(cur_console); if (crtc_type == KD_VGA && (flags & CHAR_CURSOR)) set_destructive_cursor(cur_console); draw_cursor_image(cur_console); } break; case 'F': /* set ansi foreground */ if (scp->term.num_param == 1) { scp->term.attr_mask &= ~FOREGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'G': /* set ansi background */ if (scp->term.num_param == 1) { scp->term.attr_mask &= ~BACKGROUND_CHANGED; scp->term.cur_color = scp->term.std_color = (scp->term.std_color & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'H': /* set ansi reverse video foreground */ if (scp->term.num_param == 1) { scp->term.rev_color = (scp->term.rev_color & 0xF000) | ((scp->term.param[0] & 0x0F) << 8); scp->term.cur_attr = mask2attr(&scp->term); } break; case 'I': /* set ansi reverse video background */ if (scp->term.num_param == 1) { scp->term.rev_color = (scp->term.rev_color & 0x0F00) | ((scp->term.param[0] & 0x0F) << 12); scp->term.cur_attr = mask2attr(&scp->term); } break; } } #if notyet else if (scp->term.esc == 4) { /* seen ESC Q */ /* to be filled */ } #endif else if (scp->term.esc == 5) { /* seen ESC ( */ switch (c) { case 'B': /* iso-2022: desginate ASCII into G0 */ break; /* other items to be filled */ default: break; } } scp->term.esc = 0; } static void ansi_put(scr_stat *scp, u_char *buf, int len) { u_char *ptr = buf; /* make screensaver happy */ if (!sticky_splash && scp == cur_console) run_scrn_saver = FALSE; write_in_progress++; outloop: if (scp->term.esc) { scan_esc(scp, *ptr++); len--; } else if (PRINTABLE(*ptr)) { /* Print only printables */ int cnt = len <= (scp->xsize-scp->xpos) ? len : (scp->xsize-scp->xpos); u_short cur_attr = scp->term.cur_attr; u_short *cursor_pos = scp->cursor_pos; do { /* * gcc-2.6.3 generates poor (un)sign extension code. Casting the * pointers in the following to volatile should have no effect, * but in fact speeds up this inner loop from 26 to 18 cycles * (+ cache misses) on i486's. */ #define UCVP(ucp) ((u_char volatile *)(ucp)) *cursor_pos++ = UCVP(scr_map)[*UCVP(ptr)] | cur_attr; ptr++; cnt--; } while (cnt && PRINTABLE(*ptr)); len -= (cursor_pos - scp->cursor_pos); scp->xpos += (cursor_pos - scp->cursor_pos); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); mark_for_update(scp, cursor_pos - scp->scr_buf); scp->cursor_pos = cursor_pos; if (scp->xpos >= scp->xsize) { scp->xpos = 0; scp->ypos++; } } else { switch(*ptr) { case 0x07: do_bell(scp, scp->bell_pitch, scp->bell_duration); break; case 0x08: /* non-destructive backspace */ if (scp->cursor_pos > scp->scr_buf) { mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos--; mark_for_update(scp, scp->cursor_pos - scp->scr_buf); if (scp->xpos > 0) scp->xpos--; else { scp->xpos += scp->xsize - 1; scp->ypos--; } } break; case 0x09: /* non-destructive tab */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos += (8 - scp->xpos % 8u); mark_for_update(scp, scp->cursor_pos - scp->scr_buf); if ((scp->xpos += (8 - scp->xpos % 8u)) >= scp->xsize) { scp->xpos = 0; scp->ypos++; } break; case 0x0a: /* newline, same pos */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos += scp->xsize; mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->ypos++; break; case 0x0c: /* form feed, clears screen */ clear_screen(scp); break; case 0x0d: /* return, return to pos 0 */ mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->cursor_pos -= scp->xpos; mark_for_update(scp, scp->cursor_pos - scp->scr_buf); scp->xpos = 0; break; case 0x1b: /* start escape sequence */ scp->term.esc = 1; scp->term.num_param = 0; break; } ptr++; len--; } /* do we have to scroll ?? */ if (scp->cursor_pos >= scp->scr_buf + scp->ysize * scp->xsize) { remove_cutmarking(scp); if (scp->history) { bcopy(scp->scr_buf, scp->history_head, scp->xsize * sizeof(u_short)); scp->history_head += scp->xsize; if (scp->history_head + scp->xsize > scp->history + scp->history_size) scp->history_head = scp->history; } bcopy(scp->scr_buf + scp->xsize, scp->scr_buf, scp->xsize * (scp->ysize - 1) * sizeof(u_short)); fillw(scp->term.cur_color | scr_map[0x20], scp->scr_buf + scp->xsize * (scp->ysize - 1), scp->xsize); scp->cursor_pos -= scp->xsize; scp->ypos--; mark_all(scp); } if (len) goto outloop; write_in_progress--; if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } static void scinit(void) { u_int hw_cursor; u_int i; if (init_done != COLD) return; init_done = WARM; /* * Ensure a zero start address. This is mainly to recover after * switching from pcvt using userconfig(). The registers are w/o * for old hardware so it's too hard to relocate the active screen * memory. */ outb(crtc_addr, 12); outb(crtc_addr + 1, 0); outb(crtc_addr, 13); outb(crtc_addr + 1, 0); /* extract cursor location */ outb(crtc_addr, 14); hw_cursor = inb(crtc_addr + 1) << 8; outb(crtc_addr, 15); hw_cursor |= inb(crtc_addr + 1); /* * Validate cursor location. It may be off the screen. Then we must * not use it for the initial buffer offset. */ if (hw_cursor >= ROW * COL) hw_cursor = (ROW - 1) * COL; /* move hardware cursor out of the way */ outb(crtc_addr, 14); outb(crtc_addr + 1, 0xff); outb(crtc_addr, 15); outb(crtc_addr + 1, 0xff); /* set up the first console */ current_default = &user_default; console[0] = &main_console; init_scp(console[0]); cur_console = console[0]; /* discard the video mode table if we are not familiar with it... */ if (video_mode_ptr) { bzero(mode_map, sizeof(mode_map)); bcopy(video_mode_ptr + MODE_PARAM_SIZE*console[0]->mode, vgaregs2, sizeof(vgaregs2)); switch (comp_vgaregs(vgaregs, video_mode_ptr + MODE_PARAM_SIZE*console[0]->mode)) { case COMP_IDENTICAL: map_mode_table(mode_map, video_mode_ptr, M_VGA_CG320 + 1); /* * This is a kludge for Toshiba DynaBook SS433 whose BIOS video * mode table entry has the actual # of rows at the offset 1; * BIOSes from other manufacturers store the # of rows - 1 there. * XXX */ rows_offset = vgaregs[1] + 1 - video_mode_ptr[MODE_PARAM_SIZE*console[0]->mode + 1]; break; case COMP_SIMILAR: map_mode_table(mode_map, video_mode_ptr, M_VGA_CG320 + 1); mode_map[console[0]->mode] = vgaregs; rows_offset = vgaregs[1] + 1 - video_mode_ptr[MODE_PARAM_SIZE*console[0]->mode + 1]; vgaregs[1] -= rows_offset - 1; break; case COMP_DIFFERENT: default: video_mode_ptr = NULL; mode_map[console[0]->mode] = vgaregs; rows_offset = 1; break; } } /* copy screen to temporary buffer */ if (!VESA_MODE(console[0]->mode)) generic_bcopy(Crtat, sc_buffer, console[0]->xsize * console[0]->ysize * sizeof(u_short)); console[0]->scr_buf = console[0]->mouse_pos = console[0]->mouse_oldpos = sc_buffer; console[0]->cursor_pos = console[0]->cursor_oldpos = sc_buffer + hw_cursor; console[0]->cursor_saveunder = *console[0]->cursor_pos; console[0]->xpos = hw_cursor % COL; console[0]->ypos = hw_cursor / COL; for (i=1; ismode.mode == VT_AUTO && console[0]->smode.mode == VT_AUTO) switch_scr(cur_console, 0); shutdown_in_progress = TRUE; } static void map_mode_table(char *map[], char *table, int max) { int i; for(i = 0; i < max; ++i) map[i] = table + i*MODE_PARAM_SIZE; for(; i < MODE_MAP_SIZE; ++i) map[i] = NULL; } static int map_mode_num(int mode) { static struct { int from; int to; } mode_map[] = { { M_ENH_B80x43, M_ENH_B80x25 }, { M_ENH_C80x43, M_ENH_C80x25 }, { M_VGA_M80x30, M_VGA_M80x25 }, { M_VGA_C80x30, M_VGA_C80x25 }, { M_VGA_M80x50, M_VGA_M80x25 }, { M_VGA_C80x50, M_VGA_C80x25 }, { M_VGA_M80x60, M_VGA_M80x25 }, { M_VGA_C80x60, M_VGA_C80x25 }, { M_VGA_MODEX, M_VGA_CG320 }, }; int i; for (i = 0; i < sizeof(mode_map)/sizeof(mode_map[0]); ++i) { if (mode_map[i].from == mode) return mode_map[i].to; } return mode; } static char *get_mode_param(scr_stat *scp, int mode) { if (mode >= MODE_MAP_SIZE) mode = map_mode_num(mode); if (mode < MODE_MAP_SIZE) return mode_map[mode]; else return NULL; } static scr_stat *alloc_scp() { scr_stat *scp; scp = (scr_stat *)malloc(sizeof(scr_stat), M_DEVBUF, M_WAITOK); init_scp(scp); scp->scr_buf = scp->cursor_pos = scp->cursor_oldpos = (u_short *)malloc(scp->xsize*scp->ysize*sizeof(u_short), M_DEVBUF, M_WAITOK); scp->mouse_pos = scp->mouse_oldpos = scp->scr_buf + ((scp->mouse_ypos/scp->font_size)*scp->xsize + scp->mouse_xpos/8); scp->history_head = scp->history_pos = (u_short *)malloc(scp->history_size*sizeof(u_short), M_DEVBUF, M_WAITOK); bzero(scp->history_head, scp->history_size*sizeof(u_short)); scp->history = scp->history_head; /* SOS if (crtc_type == KD_VGA && video_mode_ptr) set_mode(scp); */ clear_screen(scp); scp->cursor_saveunder = *scp->cursor_pos; return scp; } static void init_scp(scr_stat *scp) { switch(crtc_type) { case KD_VGA: if (VESA_MODE(bios_video_mode)) scp->mode = bios_video_mode; else if (crtc_addr == MONO_BASE) scp->mode = M_VGA_M80x25; else scp->mode = M_VGA_C80x25; scp->font_size = 16; break; case KD_CGA: if (crtc_addr == MONO_BASE) scp->mode = M_B80x25; else scp->mode = M_C80x25; scp->font_size = 8; break; case KD_EGA: if (crtc_addr == MONO_BASE) scp->mode = M_B80x25; else scp->mode = M_C80x25; scp->font_size = 14; break; case KD_MONO: case KD_HERCULES: default: scp->mode = M_EGAMONO80x25; scp->font_size = 14; break; } scp->initial_mode = scp->mode; scp->xsize = COL; scp->ysize = ROW; scp->xpixel = scp->xsize * 8; scp->ypixel = scp->ysize * scp->font_size; scp->xpos = scp->ypos = 0; scp->saved_xpos = scp->saved_ypos = -1; scp->start = scp->xsize * scp->ysize; scp->end = 0; scp->term.esc = 0; scp->term.attr_mask = NORMAL_ATTR; scp->term.cur_attr = scp->term.cur_color = scp->term.std_color = current_default->std_color; scp->term.rev_color = current_default->rev_color; scp->border = BG_BLACK; scp->cursor_start = *(char *)pa_to_va(0x461); scp->cursor_end = *(char *)pa_to_va(0x460); scp->mouse_xpos = scp->xsize*8/2; scp->mouse_ypos = scp->ysize*scp->font_size/2; scp->mouse_cut_start = scp->mouse_cut_end = NULL; scp->mouse_signal = 0; scp->mouse_pid = 0; scp->mouse_proc = NULL; scp->bell_pitch = BELL_PITCH; scp->bell_duration = BELL_DURATION; scp->status = (*(char *)pa_to_va(0x417) & 0x20) ? NLKED : 0; scp->status |= CURSOR_ENABLED; scp->pid = 0; scp->proc = NULL; scp->smode.mode = VT_AUTO; scp->history_head = scp->history_pos = scp->history = NULL; scp->history_size = imax(SC_HISTORY_SIZE, scp->ysize) * scp->xsize; } static u_char *get_fstr(u_int c, u_int *len) { u_int i; if (!(c & FKEY)) return(NULL); i = (c & 0xFF) - F_FN; if (i > n_fkey_tab) return(NULL); *len = fkey_tab[i].len; return(fkey_tab[i].str); } static void history_to_screen(scr_stat *scp) { int i; for (i=0; iysize; i++) bcopy(scp->history + (((scp->history_pos - scp->history) + scp->history_size-((i+1)*scp->xsize))%scp->history_size), scp->scr_buf + (scp->xsize * (scp->ysize-1 - i)), scp->xsize * sizeof(u_short)); mark_all(scp); } static int history_up_line(scr_stat *scp) { if (WRAPHIST(scp, scp->history_pos, -(scp->xsize*scp->ysize)) != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, -scp->xsize); history_to_screen(scp); return 0; } else return -1; } static int history_down_line(scr_stat *scp) { if (scp->history_pos != scp->history_head) { scp->history_pos = WRAPHIST(scp, scp->history_pos, scp->xsize); history_to_screen(scp); return 0; } else return -1; } /* * scgetc(flags) - get character from keyboard. * If flags & SCGETC_CN, then avoid harmful side effects. * If flags & SCGETC_NONBLOCK, then wait until a key is pressed, else * return NOKEY if there is nothing there. */ static u_int scgetc(u_int flags) { struct key_t *key; u_char scancode, keycode; u_int state, action; int c; static u_char esc_flag = 0, compose = 0; static u_int chr = 0; next_code: /* first see if there is something in the keyboard port */ if (flags & SCGETC_NONBLOCK) { c = read_kbd_data_no_wait(sc_kbdc); if (c == -1) return(NOKEY); } else { do { c = read_kbd_data(sc_kbdc); } while(c == -1); } scancode = (u_char)c; /* make screensaver happy */ if (!(scancode & 0x80)) { scsplash_stick(FALSE); run_scrn_saver = FALSE; } if (!(flags & SCGETC_CN)) { /* do the /dev/random device a favour */ add_keyboard_randomness(scancode); if (cur_console->status & KBD_RAW_MODE) return scancode; } keycode = scancode & 0x7F; switch (esc_flag) { case 0x00: /* normal scancode */ switch(scancode) { case 0xB8: /* left alt (compose key) */ if (compose) { compose = 0; if (chr > 255) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); chr = 0; } } break; case 0x38: if (!compose) { compose = 1; chr = 0; } break; case 0xE0: case 0xE1: esc_flag = scancode; goto next_code; } break; case 0xE0: /* 0xE0 prefix */ esc_flag = 0; switch (keycode) { case 0x1C: /* right enter key */ keycode = 0x59; break; case 0x1D: /* right ctrl key */ keycode = 0x5A; break; case 0x35: /* keypad divide key */ keycode = 0x5B; break; case 0x37: /* print scrn key */ keycode = 0x5C; break; case 0x38: /* right alt key (alt gr) */ keycode = 0x5D; break; case 0x47: /* grey home key */ keycode = 0x5E; break; case 0x48: /* grey up arrow key */ keycode = 0x5F; break; case 0x49: /* grey page up key */ keycode = 0x60; break; case 0x4B: /* grey left arrow key */ keycode = 0x61; break; case 0x4D: /* grey right arrow key */ keycode = 0x62; break; case 0x4F: /* grey end key */ keycode = 0x63; break; case 0x50: /* grey down arrow key */ keycode = 0x64; break; case 0x51: /* grey page down key */ keycode = 0x65; break; case 0x52: /* grey insert key */ keycode = 0x66; break; case 0x53: /* grey delete key */ keycode = 0x67; break; /* the following 3 are only used on the MS "Natural" keyboard */ case 0x5b: /* left Window key */ keycode = 0x69; break; case 0x5c: /* right Window key */ keycode = 0x6a; break; case 0x5d: /* menu key */ keycode = 0x6b; break; default: /* ignore everything else */ goto next_code; } break; case 0xE1: /* 0xE1 prefix */ esc_flag = 0; if (keycode == 0x1D) esc_flag = 0x1D; goto next_code; /* NOT REACHED */ case 0x1D: /* pause / break */ esc_flag = 0; if (keycode != 0x45) goto next_code; keycode = 0x68; break; } if (!(flags & SCGETC_CN) && (cur_console->status & KBD_CODE_MODE)) return (keycode | (scancode & 0x80)); /* if scroll-lock pressed allow history browsing */ if (cur_console->history && cur_console->status & SLKED) { int i; cur_console->status &= ~CURSOR_ENABLED; if (!(cur_console->status & BUFFER_SAVED)) { cur_console->status |= BUFFER_SAVED; cur_console->history_save = cur_console->history_head; /* copy screen into top of history buffer */ for (i=0; iysize; i++) { bcopy(cur_console->scr_buf + (cur_console->xsize * i), cur_console->history_head, cur_console->xsize * sizeof(u_short)); cur_console->history_head += cur_console->xsize; if (cur_console->history_head + cur_console->xsize > cur_console->history + cur_console->history_size) cur_console->history_head=cur_console->history; } cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); } switch (scancode) { case 0x47: /* home key */ cur_console->history_pos = cur_console->history_head; history_to_screen(cur_console); goto next_code; case 0x4F: /* end key */ cur_console->history_pos = WRAPHIST(cur_console, cur_console->history_head, cur_console->xsize*cur_console->ysize); history_to_screen(cur_console); goto next_code; case 0x48: /* up arrow key */ if (history_up_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case 0x50: /* down arrow key */ if (history_down_line(cur_console)) do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; case 0x49: /* page up key */ for (i=0; iysize; i++) if (history_up_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; case 0x51: /* page down key */ for (i=0; iysize; i++) if (history_down_line(cur_console)) { do_bell(cur_console, BELL_PITCH, BELL_DURATION); break; } goto next_code; } } if (compose) { switch (scancode) { /* key pressed process it */ case 0x47: case 0x48: case 0x49: /* keypad 7,8,9 */ chr = (scancode - 0x40) + chr*10; goto next_code; case 0x4B: case 0x4C: case 0x4D: /* keypad 4,5,6 */ chr = (scancode - 0x47) + chr*10; goto next_code; case 0x4F: case 0x50: case 0x51: /* keypad 1,2,3 */ chr = (scancode - 0x4E) + chr*10; goto next_code; case 0x52: /* keypad 0 */ chr *= 10; goto next_code; /* key release, no interest here */ case 0xC7: case 0xC8: case 0xC9: /* keypad 7,8,9 */ case 0xCB: case 0xCC: case 0xCD: /* keypad 4,5,6 */ case 0xCF: case 0xD0: case 0xD1: /* keypad 1,2,3 */ case 0xD2: /* keypad 0 */ goto next_code; case 0x38: /* left alt key */ break; default: if (chr) { compose = chr = 0; do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; } break; } } state = (shfts ? 1 : 0 ) | (2 * (ctls ? 1 : 0)) | (4 * (alts ? 1 : 0)); if ((!agrs && (cur_console->status & ALKED)) || (agrs && !(cur_console->status & ALKED))) keycode += ALTGR_OFFSET; key = &key_map.key[keycode]; if ( ((key->flgs & FLAG_LOCK_C) && (cur_console->status & CLKED)) || ((key->flgs & FLAG_LOCK_N) && (cur_console->status & NLKED)) ) state ^= 1; /* Check for make/break */ action = key->map[state]; if (scancode & 0x80) { /* key released */ if (key->spcl & (0x80>>state)) { switch (action) { case LSH: shfts &= ~1; break; case RSH: shfts &= ~2; break; case LCTR: ctls &= ~1; break; case RCTR: ctls &= ~2; break; case LALT: alts &= ~1; break; case RALT: alts &= ~2; break; case NLK: nlkcnt = 0; break; case CLK: clkcnt = 0; break; case SLK: slkcnt = 0; break; case ASH: agrs = 0; break; case ALK: alkcnt = 0; break; case META: metas = 0; break; } } if (chr && !compose) { action = chr; chr = 0; return(action); } } else { /* key pressed */ if (key->spcl & (0x80>>state)) { switch (action) { /* LOCKING KEYS */ case NLK: if (!nlkcnt) { nlkcnt++; if (cur_console->status & NLKED) cur_console->status &= ~NLKED; else cur_console->status |= NLKED; update_leds(cur_console->status); } break; case CLK: if (!clkcnt) { clkcnt++; if (cur_console->status & CLKED) cur_console->status &= ~CLKED; else cur_console->status |= CLKED; update_leds(cur_console->status); } break; case SLK: if (!slkcnt) { slkcnt++; if (cur_console->status & SLKED) { cur_console->status &= ~SLKED; if (cur_console->status & BUFFER_SAVED){ int i; u_short *ptr = cur_console->history_save; for (i=0; iysize; i++) { bcopy(ptr, cur_console->scr_buf + (cur_console->xsize*i), cur_console->xsize * sizeof(u_short)); ptr += cur_console->xsize; if (ptr + cur_console->xsize > cur_console->history + cur_console->history_size) ptr = cur_console->history; } cur_console->status &= ~BUFFER_SAVED; cur_console->history_head=cur_console->history_save; cur_console->status |= CURSOR_ENABLED; mark_all(cur_console); } scstart(VIRTUAL_TTY(get_scr_num())); } else cur_console->status |= SLKED; update_leds(cur_console->status); } break; case ALK: if (!alkcnt) { alkcnt++; if (cur_console->status & ALKED) cur_console->status &= ~ALKED; else cur_console->status |= ALKED; update_leds(cur_console->status); } break; /* NON-LOCKING KEYS */ case NOP: break; case SPSC: /* force activatation/deactivation of the screen saver */ accents = 0; if (scrn_blanked <= 0) { run_scrn_saver = TRUE; scrn_time_stamp -= scrn_blank_time; } #ifdef SC_SPLASH_SCREEN if (cold) { /* * While devices are being probed, the screen saver need * to be invoked explictly. XXX */ if (scrn_blanked > 0) { scsplash_stick(FALSE); stop_scrn_saver(current_saver); } else { if ((cur_console->status & UNKNOWN_MODE) == 0) { scsplash_stick(TRUE); (*current_saver)(TRUE); } } } #endif break; case RBT: #ifndef SC_DISABLE_REBOOT accents = 0; shutdown_nice(); #endif break; case SUSP: #if NAPM > 0 accents = 0; apm_suspend(PMST_SUSPEND); #endif break; case STBY: #if NAPM > 0 accents = 0; apm_suspend(PMST_STANDBY); #endif break; case DBG: #ifdef DDB /* try to switch to console 0 */ accents = 0; /* * TRY to make sure the screen saver is stopped, * and the screen is updated before switching to * the vty0. */ scrn_timer((void *)FALSE); if (cur_console->smode.mode == VT_AUTO && console[0]->smode.mode == VT_AUTO) switch_scr(cur_console, 0); Debugger("manual escape to debugger"); #else printf("No debugger in kernel\n"); #endif break; case LSH: shfts |= 1; break; case RSH: shfts |= 2; break; case LCTR: ctls |= 1; break; case RCTR: ctls |= 2; break; case LALT: alts |= 1; break; case RALT: alts |= 2; break; case ASH: agrs = 1; break; case META: metas = 1; break; case NEXT: { int next, this = get_scr_num(); accents = 0; for (next = this+1; next != this; next = (next+1)%MAXCONS) { struct tty *tp = VIRTUAL_TTY(next); if (tp->t_state & TS_ISOPEN) { switch_scr(cur_console, next); break; } } } break; case BTAB: accents = 0; return(BKEY); default: if (action >= F_ACC && action <= L_ACC) { /* turn it into an index */ action -= F_ACC - 1; if ((action > accent_map.n_accs) || (accent_map.acc[action - 1].accchar == 0)) { /* * The index is out of range or pointing to an * empty entry. */ accents = 0; do_bell(cur_console, BELL_PITCH, BELL_DURATION); } /* * If the same accent key has been hit twice, * produce the accent char itself. */ if (action == accents) { action = accent_map.acc[accents - 1].accchar; accents = 0; if (metas) action |= MKEY; return (action); } /* remember the index and wait for the next key stroke */ accents = action; break; } if (accents > 0) { accents = 0; do_bell(cur_console, BELL_PITCH, BELL_DURATION); } if (action >= F_SCR && action <= L_SCR) { switch_scr(cur_console, action - F_SCR); break; } if (action >= F_FN && action <= L_FN) action |= FKEY; return(action); } } else { if (accents) { struct acc_t *acc; int i; acc = &accent_map.acc[accents - 1]; accents = 0; /* * If the accent key is followed by the space key, * produce the accent char itself. */ if (action == ' ') { action = acc->accchar; if (metas) action |= MKEY; return (action); } for (i = 0; i < NUM_ACCENTCHARS; ++i) { if (acc->map[i][0] == 0) /* end of the map entry */ break; if (acc->map[i][0] == action) { action = acc->map[i][1]; if (metas) action |= MKEY; return (action); } } do_bell(cur_console, BELL_PITCH, BELL_DURATION); goto next_code; } if (metas) action |= MKEY; return(action); } } goto next_code; } int scmmap(dev_t dev, int offset, int nprot) { if (offset > 0x20000 - PAGE_SIZE) return -1; return i386_btop((VIDEOMEM + offset)); } /* * Calculate hardware attributes word using logical attributes mask and * hardware colors */ static int mask2attr(struct term_stat *term) { int attr, mask = term->attr_mask; if (mask & REVERSE_ATTR) { attr = ((mask & FOREGROUND_CHANGED) ? ((term->cur_color & 0xF000) >> 4) : (term->rev_color & 0x0F00)) | ((mask & BACKGROUND_CHANGED) ? ((term->cur_color & 0x0F00) << 4) : (term->rev_color & 0xF000)); } else attr = term->cur_color; /* XXX: underline mapping for Hercules adapter can be better */ if (mask & (BOLD_ATTR | UNDERLINE_ATTR)) attr ^= 0x0800; if (mask & BLINK_ATTR) attr ^= 0x8000; return attr; } static void set_keyboard(int command, int data) { int s; if (sc_kbdc == NULL) return; /* prevent the timeout routine from polling the keyboard */ if (!kbdc_lock(sc_kbdc, TRUE)) return; /* disable the keyboard and mouse interrupt */ s = spltty(); #if 0 c = get_controller_command_byte(sc_kbdc); if ((c == -1) || !set_controller_command_byte(sc_kbdc, kbdc_get_device_mask(sc_kbdc), KBD_DISABLE_KBD_PORT | KBD_DISABLE_KBD_INT | KBD_DISABLE_AUX_PORT | KBD_DISABLE_AUX_INT)) { /* CONTROLLER ERROR */ kbdc_lock(sc_kbdc, FALSE); splx(s); return; } /* * Now that the keyboard controller is told not to generate * the keyboard and mouse interrupts, call `splx()' to allow * the other tty interrupts. The clock interrupt may also occur, * but the timeout routine (`scrn_timer()') will be blocked * by the lock flag set via `kbdc_lock()' */ splx(s); #endif if (send_kbd_command_and_data(sc_kbdc, command, data) != KBD_ACK) send_kbd_command(sc_kbdc, KBDC_ENABLE_KBD); #if 0 /* restore the interrupts */ if (!set_controller_command_byte(sc_kbdc, kbdc_get_device_mask(sc_kbdc), c & (KBD_KBD_CONTROL_BITS | KBD_AUX_CONTROL_BITS))) { /* CONTROLLER ERROR */ } #else splx(s); #endif kbdc_lock(sc_kbdc, FALSE); } static void update_leds(int which) { static u_char xlate_leds[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; /* replace CAPS led with ALTGR led for ALTGR keyboards */ if (key_map.n_keys > ALTGR_OFFSET) { if (which & ALKED) which |= CLKED; else which &= ~CLKED; } set_keyboard(KBDC_SET_LEDS, xlate_leds[which & LED_MASK]); } void set_mode(scr_stat *scp) { char special_modetable[MODE_PARAM_SIZE]; char *mp; int s; int i; if (scp != cur_console) return; /* * even if mode switching is disabled, we can change back * to the initial mode or the custom mode based on the initial * mode if we have saved register values upon start-up. */ mp = get_mode_param(scp, scp->mode); if (mp == NULL) return; bcopy(mp, &special_modetable, sizeof(special_modetable)); /* setup video hardware for the given mode */ switch (scp->mode) { case M_VGA_C80x60: case M_VGA_M80x60: special_modetable[2] = 0x08; special_modetable[19] = 0x47; goto special_480l; case M_VGA_C80x30: case M_VGA_M80x30: special_modetable[19] = 0x4f; special_480l: special_modetable[9] |= 0xc0; special_modetable[16] = 0x08; special_modetable[17] = 0x3e; special_modetable[26] = 0xea; special_modetable[28] = 0xdf; special_modetable[31] = 0xe7; special_modetable[32] = 0x04; goto setup_mode; case M_ENH_C80x43: case M_ENH_B80x43: special_modetable[28] = 87; goto special_80x50; case M_VGA_C80x50: case M_VGA_M80x50: special_80x50: special_modetable[2] = 8; special_modetable[19] = 7; goto setup_mode; case M_VGA_C40x25: case M_VGA_C80x25: case M_VGA_M80x25: case M_B40x25: case M_C40x25: case M_B80x25: case M_C80x25: case M_ENH_B40x25: case M_ENH_C40x25: case M_ENH_B80x25: case M_ENH_C80x25: case M_EGAMONO80x25: setup_mode: set_vgaregs(special_modetable); scp->font_size = special_modetable[2]; /* set font type (size) */ if (scp->font_size < 14) { if (fonts_loaded & FONT_8) copy_font(LOAD, FONT_8, font_8); i = 0x0a; /* font 2 */ } else if (scp->font_size >= 16) { if (fonts_loaded & FONT_16) copy_font(LOAD, FONT_16, font_16); i = 0x00; /* font 0 */ } else { if (fonts_loaded & FONT_14) copy_font(LOAD, FONT_14, font_14); i = 0x05; /* font 1 */ } /* * FONT KLUDGE: * This is an interim kludge to display correct font. * Always use the font page #0 on the video plane 2. * Somehow we cannot show the font in other font pages on * some video cards... XXX */ i = 0x00; s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); outb(TSIDX, 0x03); outb(TSREG, i); outb(TSIDX, 0x00); outb(TSREG, 0x03); splx(s); if (flags & CHAR_CURSOR) set_destructive_cursor(scp); mark_all(scp); break; case M_VGA_MODEX: /* "unchain" the VGA mode */ special_modetable[5-1+0x04] &= 0xf7; special_modetable[5-1+0x04] |= 0x04; /* turn off doubleword mode */ special_modetable[10+0x14] &= 0xbf; /* turn off word adressing */ special_modetable[10+0x17] |= 0x40; /* set logical screen width */ special_modetable[10+0x13] = 80; /* set 240 lines */ special_modetable[10+0x11] = 0x2c; special_modetable[10+0x06] = 0x0d; special_modetable[10+0x07] = 0x3e; special_modetable[10+0x10] = 0xea; special_modetable[10+0x11] = 0xac; special_modetable[10+0x12] = 0xdf; special_modetable[10+0x15] = 0xe7; special_modetable[10+0x16] = 0x06; /* set vertical sync polarity to reflect aspect ratio */ special_modetable[9] = 0xe3; goto setup_grmode; case M_BG320: case M_CG320: case M_BG640: case M_CG320_D: case M_CG640_E: case M_CG640x350: case M_ENH_CG640: case M_BG640x480: case M_CG640x480: case M_VGA_CG320: setup_grmode: set_vgaregs(special_modetable); scp->font_size = FONT_NONE; break; default: /* call user defined function XXX */ break; } /* set border color for this (virtual) console */ set_border(scp->border); return; } void set_border(u_char color) { switch (crtc_type) { case KD_EGA: case KD_VGA: inb(crtc_addr + 6); /* reset flip-flop */ outb(ATC, 0x31); outb(ATC, color); break; case KD_CGA: outb(crtc_addr + 5, color & 0x0f); /* color select register */ break; case KD_MONO: case KD_HERCULES: default: break; } } static void set_vgaregs(char *modetable) { int i, s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ for (i=0; i<4; i++) { /* program sequencer */ outb(TSIDX, i+1); outb(TSREG, modetable[i+5]); } outb(MISC, modetable[9]); /* set dot-clock */ outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ outb(crtc_addr, 0x11); outb(crtc_addr+1, inb(crtc_addr+1) & 0x7F); for (i=0; i<25; i++) { /* program crtc */ outb(crtc_addr, i); if (i == 14 || i == 15) /* no hardware cursor */ outb(crtc_addr+1, 0xff); else outb(crtc_addr+1, modetable[i+10]); } inb(crtc_addr+6); /* reset flip-flop */ for (i=0; i<20; i++) { /* program attribute ctrl */ outb(ATC, i); outb(ATC, modetable[i+35]); } for (i=0; i<9; i++) { /* program graph data ctrl */ outb(GDCIDX, i); outb(GDCREG, modetable[i+55]); } inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ splx(s); } static void read_vgaregs(char *buf) { int i, j; int s; bzero(buf, MODE_PARAM_SIZE); s = splhigh(); outb(TSIDX, 0x00); outb(TSREG, 0x01); /* stop sequencer */ for (i=0, j=5; i<4; i++) { outb(TSIDX, i+1); buf[j++] = inb(TSREG); } buf[9] = inb(MISC + 10); /* dot-clock */ outb(TSIDX, 0x00); outb(TSREG, 0x03); /* start sequencer */ for (i=0, j=10; i<25; i++) { /* crtc */ outb(crtc_addr, i); buf[j++] = inb(crtc_addr+1); } for (i=0, j=35; i<20; i++) { /* attribute ctrl */ inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, i); buf[j++] = inb(ATC + 1); } for (i=0, j=55; i<9; i++) { /* graph data ctrl */ outb(GDCIDX, i); buf[j++] = inb(GDCREG); } inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ buf[0] = *(char *)pa_to_va(0x44a); /* COLS */ buf[1] = *(char *)pa_to_va(0x484); /* ROWS */ buf[2] = *(char *)pa_to_va(0x485); /* POINTS */ buf[3] = *(char *)pa_to_va(0x44c); buf[4] = *(char *)pa_to_va(0x44d); splx(s); } static int comp_vgaregs(u_char *buf1, u_char *buf2) { static struct { u_char mask; } params[MODE_PARAM_SIZE] = { 0xff, 0x00, 0xff, /* COLS, ROWS, POINTS */ 0xff, 0xff, /* page length */ 0xfe, 0xff, 0xff, 0xff, /* sequencer registers */ 0xf3, /* misc register */ 0xff, 0xff, 0xff, 0x7f, 0xff, /* CRTC */ 0xff, 0xff, 0xff, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x7f, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* attribute controller registers */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, /* GDC register */ 0xff, 0xff, 0xff, 0xff, }; int identical = TRUE; int i; for (i = 0; i < sizeof(params)/sizeof(params[0]); ++i) { if (params[i].mask == 0) /* don't care */ continue; if ((buf1[i] & params[i].mask) != (buf2[i] & params[i].mask)) return COMP_DIFFERENT; if (buf1[i] != buf2[i]) identical = FALSE; } return (identical) ? COMP_IDENTICAL : COMP_SIMILAR; #if 0 for(i = 0; i < 20; ++i) { if (*buf1++ != *buf2++) return COMP_DIFFERENT; } buf1 += 2; /* skip the cursor shape */ buf2 += 2; for(i = 22; i < 24; ++i) { if (*buf1++ != *buf2++) return COMP_DIFFERENT; } buf1 += 2; /* skip the cursor position */ buf2 += 2; for(i = 26; i < MODE_PARAM_SIZE; ++i) { if (*buf1++ != *buf2++) return COMP_DIFFERENT; } return COMP_IDENTICAL; #endif } static void dump_vgaregs(u_char *buf) { int i; for(i = 0; i < MODE_PARAM_SIZE;) { printf("%02x ", buf[i]); if ((++i % 16) == 0) printf("\n"); } } static void set_font_mode(u_char *buf) { int s = splhigh(); font_loading_in_progress = TRUE; /* save register values */ outb(TSIDX, 0x02); buf[0] = inb(TSREG); outb(TSIDX, 0x04); buf[1] = inb(TSREG); outb(GDCIDX, 0x04); buf[2] = inb(GDCREG); outb(GDCIDX, 0x05); buf[3] = inb(GDCREG); outb(GDCIDX, 0x06); buf[4] = inb(GDCREG); inb(crtc_addr + 6); outb(ATC, 0x10); buf[5] = inb(ATC + 1); /* setup vga for loading fonts */ inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x10); outb(ATC, buf[5] & ~0x01); inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ #if SLOW_VGA #ifndef SC_BAD_FLICKER outb(TSIDX, 0x00); outb(TSREG, 0x01); #endif outb(TSIDX, 0x02); outb(TSREG, 0x04); outb(TSIDX, 0x04); outb(TSREG, 0x07); #ifndef SC_BAD_FLICKER outb(TSIDX, 0x00); outb(TSREG, 0x03); #endif outb(GDCIDX, 0x04); outb(GDCREG, 0x02); outb(GDCIDX, 0x05); outb(GDCREG, 0x00); outb(GDCIDX, 0x06); outb(GDCREG, 0x04); #else #ifndef SC_BAD_FLICKER outw(TSIDX, 0x0100); #endif outw(TSIDX, 0x0402); outw(TSIDX, 0x0704); #ifndef SC_BAD_FLICKER outw(TSIDX, 0x0300); #endif outw(GDCIDX, 0x0204); outw(GDCIDX, 0x0005); outw(GDCIDX, 0x0406); /* addr = a0000, 64kb */ #endif splx(s); } static void set_normal_mode(u_char *buf) { char *modetable; int s = splhigh(); /* setup vga for normal operation mode again */ inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x10); outb(ATC, buf[5]); inb(crtc_addr+6); /* reset flip-flop */ outb(ATC, 0x20); /* enable palette */ #if SLOW_VGA #ifndef SC_BAD_FLICKER outb(TSIDX, 0x00); outb(TSREG, 0x01); #endif outb(TSIDX, 0x02); outb(TSREG, buf[0]); outb(TSIDX, 0x04); outb(TSREG, buf[1]); #ifndef SC_BAD_FLICKER outb(TSIDX, 0x00); outb(TSREG, 0x03); #endif outb(GDCIDX, 0x04); outb(GDCREG, buf[2]); outb(GDCIDX, 0x05); outb(GDCREG, buf[3]); if (crtc_addr == MONO_BASE) { outb(GDCIDX, 0x06); outb(GDCREG,(buf[4] & 0x03) | 0x08); } else { outb(GDCIDX, 0x06); outb(GDCREG,(buf[4] & 0x03) | 0x0c); } #else #ifndef SC_BAD_FLICKER outw(TSIDX, 0x0100); #endif outw(TSIDX, 0x0002 | (buf[0] << 8)); outw(TSIDX, 0x0004 | (buf[1] << 8)); #ifndef SC_BAD_FLICKER outw(TSIDX, 0x0300); #endif outw(GDCIDX, 0x0004 | (buf[2] << 8)); outw(GDCIDX, 0x0005 | (buf[3] << 8)); if (crtc_addr == MONO_BASE) outw(GDCIDX, 0x0006 | (((buf[4] & 0x03) | 0x08)<<8)); else outw(GDCIDX, 0x0006 | (((buf[4] & 0x03) | 0x0c)<<8)); #endif font_loading_in_progress = FALSE; splx(s); } void copy_font(int operation, int font_type, char* font_image) { int ch, line, segment, fontsize; u_char buf[PARAM_BUFSIZE]; u_char val; switch (font_type) { default: case FONT_8: segment = 0x8000; fontsize = 8; break; case FONT_14: segment = 0x4000; fontsize = 14; break; case FONT_16: segment = 0x0000; fontsize = 16; break; } /* * FONT KLUDGE * Always use the font page #0. XXX */ segment = 0x0000; outb(TSIDX, 0x01); val = inb(TSREG); /* disable screen */ outb(TSIDX, 0x01); outb(TSREG, val | 0x20); set_font_mode(buf); for (ch=0; ch < 256; ch++) for (line=0; line < fontsize; line++) if (operation) *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line) = font_image[(ch*fontsize)+line]; else font_image[(ch*fontsize)+line] = *(char *)pa_to_va(VIDEOMEM+(segment)+(ch*32)+line); set_normal_mode(buf); outb(TSIDX, 0x01); outb(TSREG, val & 0xDF); /* enable screen */ } static void set_destructive_cursor(scr_stat *scp) { u_char buf[PARAM_BUFSIZE]; u_char cursor[32]; caddr_t address; int i; char *font_buffer; if (scp->font_size < 14) { font_buffer = font_8; address = (caddr_t)VIDEOMEM + 0x8000; } else if (scp->font_size >= 16) { font_buffer = font_16; address = (caddr_t)VIDEOMEM; } else { font_buffer = font_14; address = (caddr_t)VIDEOMEM + 0x4000; } /* * FONT KLUDGE * Always use the font page #0. XXX */ address = (caddr_t)VIDEOMEM; if (scp->status & MOUSE_VISIBLE) { if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR) bcopy(&scp->mouse_cursor[0], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR + 1) bcopy(&scp->mouse_cursor[32], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR + 2) bcopy(&scp->mouse_cursor[64], cursor, scp->font_size); else if ((scp->cursor_saveunder & 0xff) == SC_MOUSE_CHAR + 3) bcopy(&scp->mouse_cursor[96], cursor, scp->font_size); else bcopy(font_buffer+((scp->cursor_saveunder & 0xff)*scp->font_size), cursor, scp->font_size); } else bcopy(font_buffer + ((scp->cursor_saveunder & 0xff) * scp->font_size), cursor, scp->font_size); for (i=0; i<32; i++) if ((i >= scp->cursor_start && i <= scp->cursor_end) || (scp->cursor_start >= scp->font_size && i == scp->font_size - 1)) cursor[i] |= 0xff; #if 1 while (!(inb(crtc_addr+6) & 0x08)) /* wait for vertical retrace */ ; #endif set_font_mode(buf); generic_bcopy(cursor, (char *)pa_to_va(address) + DEAD_CHAR * 32, 32); set_normal_mode(buf); } static void set_mouse_pos(scr_stat *scp) { static int last_xpos = -1, last_ypos = -1; if (scp->mouse_xpos < 0) scp->mouse_xpos = 0; if (scp->mouse_ypos < 0) scp->mouse_ypos = 0; if (scp->status & UNKNOWN_MODE) { if (scp->mouse_xpos > scp->xpixel-1) scp->mouse_xpos = scp->xpixel-1; if (scp->mouse_ypos > scp->ypixel-1) scp->mouse_ypos = scp->ypixel-1; return; } if (scp->mouse_xpos > (scp->xsize*8)-1) scp->mouse_xpos = (scp->xsize*8)-1; if (scp->mouse_ypos > (scp->ysize*scp->font_size)-1) scp->mouse_ypos = (scp->ysize*scp->font_size)-1; if (scp->mouse_xpos != last_xpos || scp->mouse_ypos != last_ypos) { scp->status |= MOUSE_MOVED; scp->mouse_pos = scp->scr_buf + ((scp->mouse_ypos/scp->font_size)*scp->xsize + scp->mouse_xpos/8); if ((scp->status & MOUSE_VISIBLE) && (scp->status & MOUSE_CUTTING)) mouse_cut(scp); } } #define isspace(c) (((c) & 0xff) == ' ') static int skip_spc_right(scr_stat *scp, u_short *p) { int i; for (i = (p - scp->scr_buf) % scp->xsize; i < scp->xsize; ++i) { if (!isspace(*p)) break; ++p; } return i; } static int skip_spc_left(scr_stat *scp, u_short *p) { int i; for (i = (p-- - scp->scr_buf) % scp->xsize - 1; i >= 0; --i) { if (!isspace(*p)) break; --p; } return i; } static void mouse_cut(scr_stat *scp) { u_short *end; u_short *p; int i = 0; int j = 0; scp->mouse_cut_end = (scp->mouse_pos >= scp->mouse_cut_start) ? scp->mouse_pos + 1 : scp->mouse_pos; end = (scp->mouse_cut_start > scp->mouse_cut_end) ? scp->mouse_cut_start : scp->mouse_cut_end; for (p = (scp->mouse_cut_start > scp->mouse_cut_end) ? scp->mouse_cut_end : scp->mouse_cut_start; p < end; ++p) { cut_buffer[i] = *p & 0xff; /* remember the position of the last non-space char */ if (!isspace(cut_buffer[i++])) j = i; /* trim trailing blank when crossing lines */ if (((p - scp->scr_buf) % scp->xsize) == (scp->xsize - 1)) { cut_buffer[j++] = '\r'; i = j; } } cut_buffer[i] = '\0'; /* scan towards the end of the last line */ --p; for (i = (p - scp->scr_buf) % scp->xsize; i < scp->xsize; ++i) { if (!isspace(*p)) break; ++p; } /* if there is nothing but blank chars, trim them, but mark towards eol */ if (i >= scp->xsize) { if (scp->mouse_cut_start > scp->mouse_cut_end) scp->mouse_cut_start = p; else scp->mouse_cut_end = p; cut_buffer[j++] = '\r'; cut_buffer[j] = '\0'; } mark_for_update(scp, scp->mouse_cut_start - scp->scr_buf); mark_for_update(scp, scp->mouse_cut_end - scp->scr_buf); } static void mouse_cut_start(scr_stat *scp) { int i; if (scp->status & MOUSE_VISIBLE) { if (scp->mouse_pos == scp->mouse_cut_start && scp->mouse_cut_start == scp->mouse_cut_end - 1) { cut_buffer[0] = '\0'; remove_cutmarking(scp); } else if (skip_spc_right(scp, scp->mouse_pos) >= scp->xsize) { /* if the pointer is on trailing blank chars, mark towards eol */ i = skip_spc_left(scp, scp->mouse_pos) + 1; scp->mouse_cut_start = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize) * scp->xsize + i; scp->mouse_cut_end = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize + 1) * scp->xsize; cut_buffer[0] = '\r'; cut_buffer[1] = '\0'; scp->status |= MOUSE_CUTTING; } else { scp->mouse_cut_start = scp->mouse_pos; scp->mouse_cut_end = scp->mouse_cut_start + 1; cut_buffer[0] = *scp->mouse_cut_start & 0xff; cut_buffer[1] = '\0'; scp->status |= MOUSE_CUTTING; } mark_all(scp); /* delete all other screens cut markings */ for (i=0; istatus & MOUSE_VISIBLE) { scp->status &= ~MOUSE_CUTTING; } } static void mouse_cut_word(scr_stat *scp) { u_short *p; u_short *sol; u_short *eol; int i; /* * Because we don't have locale information in the kernel, * we only distinguish space char and non-space chars. Punctuation * chars, symbols and other regular chars are all treated alike. */ if (scp->status & MOUSE_VISIBLE) { sol = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize) * scp->xsize; eol = sol + scp->xsize; if (isspace(*scp->mouse_pos)) { for (p = scp->mouse_pos; p >= sol; --p) if (!isspace(*p)) break; scp->mouse_cut_start = ++p; for (p = scp->mouse_pos; p < eol; ++p) if (!isspace(*p)) break; scp->mouse_cut_end = p; } else { for (p = scp->mouse_pos; p >= sol; --p) if (isspace(*p)) break; scp->mouse_cut_start = ++p; for (p = scp->mouse_pos; p < eol; ++p) if (isspace(*p)) break; scp->mouse_cut_end = p; } for (i = 0, p = scp->mouse_cut_start; p < scp->mouse_cut_end; ++p) cut_buffer[i++] = *p & 0xff; cut_buffer[i] = '\0'; scp->status |= MOUSE_CUTTING; } } static void mouse_cut_line(scr_stat *scp) { u_short *p; int i; if (scp->status & MOUSE_VISIBLE) { scp->mouse_cut_start = scp->scr_buf + ((scp->mouse_pos - scp->scr_buf) / scp->xsize) * scp->xsize; scp->mouse_cut_end = scp->mouse_cut_start + scp->xsize; for (i = 0, p = scp->mouse_cut_start; p < scp->mouse_cut_end; ++p) cut_buffer[i++] = *p & 0xff; cut_buffer[i++] = '\r'; cut_buffer[i] = '\0'; scp->status |= MOUSE_CUTTING; } } static void mouse_cut_extend(scr_stat *scp) { if ((scp->status & MOUSE_VISIBLE) && !(scp->status & MOUSE_CUTTING) && (scp->mouse_cut_start != NULL)) { mouse_cut(scp); scp->status |= MOUSE_CUTTING; } } static void mouse_paste(scr_stat *scp) { if (scp->status & MOUSE_VISIBLE) { struct tty *tp; u_char *ptr = cut_buffer; tp = VIRTUAL_TTY(get_scr_num()); while (*ptr) (*linesw[tp->t_line].l_rint)(scr_rmap[*ptr++], tp); } } static void draw_mouse_image(scr_stat *scp) { caddr_t address; int i; char *font_buffer; u_char buf[PARAM_BUFSIZE]; u_short buffer[32]; u_short xoffset, yoffset; u_short *crt_pos = Crtat + (scp->mouse_pos - scp->scr_buf); int font_size = scp->font_size; if (font_size < 14) { font_buffer = font_8; address = (caddr_t)VIDEOMEM + 0x8000; } else if (font_size >= 16) { font_buffer = font_16; address = (caddr_t)VIDEOMEM; } else { font_buffer = font_14; address = (caddr_t)VIDEOMEM + 0x4000; } /* * FONT KLUDGE * Always use the font page #0. XXX */ address = (caddr_t)VIDEOMEM; xoffset = scp->mouse_xpos % 8; yoffset = scp->mouse_ypos % font_size; /* prepare mousepointer char's bitmaps */ bcopy(font_buffer + ((*(scp->mouse_pos) & 0xff) * font_size), &scp->mouse_cursor[0], font_size); bcopy(font_buffer + ((*(scp->mouse_pos+1) & 0xff) * font_size), &scp->mouse_cursor[32], font_size); bcopy(font_buffer + ((*(scp->mouse_pos+scp->xsize) & 0xff) * font_size), &scp->mouse_cursor[64], font_size); bcopy(font_buffer + ((*(scp->mouse_pos+scp->xsize+1) & 0xff) * font_size), &scp->mouse_cursor[96], font_size); for (i=0; imouse_cursor[i]<<8 | scp->mouse_cursor[i+32]; buffer[i+font_size]=scp->mouse_cursor[i+64]<<8|scp->mouse_cursor[i+96]; } /* now and-or in the mousepointer image */ for (i=0; i<16; i++) { buffer[i+yoffset] = ( buffer[i+yoffset] & ~(mouse_and_mask[i] >> xoffset)) | (mouse_or_mask[i] >> xoffset); } for (i=0; imouse_cursor[i] = (buffer[i] & 0xff00) >> 8; scp->mouse_cursor[i+32] = buffer[i] & 0xff; scp->mouse_cursor[i+64] = (buffer[i+font_size] & 0xff00) >> 8; scp->mouse_cursor[i+96] = buffer[i+font_size] & 0xff; } scp->mouse_oldpos = scp->mouse_pos; #if 1 /* wait for vertical retrace to avoid jitter on some videocards */ while (!(inb(crtc_addr+6) & 0x08)) /* idle */ ; #endif set_font_mode(buf); generic_bcopy(scp->mouse_cursor, (char *)pa_to_va(address) + SC_MOUSE_CHAR * 32, 128); set_normal_mode(buf); *(crt_pos) = (*(scp->mouse_pos) & 0xff00) | SC_MOUSE_CHAR; *(crt_pos+scp->xsize) = (*(scp->mouse_pos + scp->xsize) & 0xff00) | (SC_MOUSE_CHAR + 2); if (scp->mouse_xpos < (scp->xsize-1)*8) { *(crt_pos + 1) = (*(scp->mouse_pos + 1) & 0xff00) | (SC_MOUSE_CHAR + 1); *(crt_pos+scp->xsize + 1) = (*(scp->mouse_pos + scp->xsize + 1) & 0xff00) | (SC_MOUSE_CHAR + 3); } mark_for_update(scp, scp->mouse_pos - scp->scr_buf); mark_for_update(scp, scp->mouse_pos + scp->xsize + 1 - scp->scr_buf); } static void remove_mouse_image(scr_stat *scp) { u_short *crt_pos = Crtat + (scp->mouse_oldpos - scp->scr_buf); *(crt_pos) = *(scp->mouse_oldpos); *(crt_pos+1) = *(scp->mouse_oldpos+1); *(crt_pos+scp->xsize) = *(scp->mouse_oldpos+scp->xsize); *(crt_pos+scp->xsize+1) = *(scp->mouse_oldpos+scp->xsize+1); mark_for_update(scp, scp->mouse_oldpos - scp->scr_buf); mark_for_update(scp, scp->mouse_oldpos + scp->xsize + 1 - scp->scr_buf); } static void draw_cutmarking(scr_stat *scp) { u_short *ptr; u_short och, nch; for (ptr=scp->scr_buf; ptr<=(scp->scr_buf+(scp->xsize*scp->ysize)); ptr++) { nch = och = *(Crtat + (ptr - scp->scr_buf)); /* are we outside the selected area ? */ if ( ptr < (scp->mouse_cut_start > scp->mouse_cut_end ? scp->mouse_cut_end : scp->mouse_cut_start) || ptr >= (scp->mouse_cut_start > scp->mouse_cut_end ? scp->mouse_cut_start : scp->mouse_cut_end)) { if (ptr != scp->cursor_pos) nch = (och & 0xff) | (*ptr & 0xff00); } else { /* are we clear of the cursor image ? */ if (ptr != scp->cursor_pos) nch = (och & 0x88ff) | (*ptr & 0x7000)>>4 | (*ptr & 0x0700)<<4; else { if (flags & CHAR_CURSOR) nch = (och & 0x88ff)|(*ptr & 0x7000)>>4|(*ptr & 0x0700)<<4; else if (!(flags & BLINK_CURSOR)) nch = (och & 0xff) | (*ptr & 0xff00); } } if (nch != och) *(Crtat + (ptr - scp->scr_buf)) = nch; } } static void remove_cutmarking(scr_stat *scp) { scp->mouse_cut_start = scp->mouse_cut_end = NULL; scp->status &= ~MOUSE_CUTTING; mark_all(scp); } static void save_palette(void) { int i; outb(PALRADR, 0x00); for (i=0x00; i<0x300; i++) palette[i] = inb(PALDATA); inb(crtc_addr+6); /* reset flip/flop */ } void load_palette(char *palette) { int i; outb(PIXMASK, 0xFF); /* no pixelmask */ outb(PALWADR, 0x00); for (i=0x00; i<0x300; i++) outb(PALDATA, palette[i]); inb(crtc_addr+6); /* reset flip/flop */ outb(ATC, 0x20); /* enable palette */ } static void do_bell(scr_stat *scp, int pitch, int duration) { if (cold || shutdown_in_progress) return; if (scp != cur_console && (flags & QUIET_BELL)) return; if (flags & VISUAL_BELL) { if (blink_in_progress) return; blink_in_progress = 4; if (scp != cur_console) blink_in_progress += 2; blink_screen(cur_console); } else { if (scp != cur_console) pitch *= 2; sysbeep(pitch, duration); } } static void blink_screen(void *arg) { scr_stat *scp = arg; if ((scp->status & UNKNOWN_MODE) || (blink_in_progress <= 1)) { blink_in_progress = FALSE; mark_all(scp); if (delayed_next_scr) switch_scr(scp, delayed_next_scr - 1); } else { if (blink_in_progress & 1) fillw(kernel_default.std_color | scr_map[0x20], Crtat, scp->xsize * scp->ysize); else fillw(kernel_default.rev_color | scr_map[0x20], Crtat, scp->xsize * scp->ysize); blink_in_progress--; timeout(blink_screen, scp, hz / 10); } } void sc_bcopy(scr_stat *scp, u_short *p, int from, int to, int mark) { if (!VESA_MODE(scp->mode)) { generic_bcopy(p+from, Crtat+from, (to-from+1)*sizeof (u_short)); } else if (scp->mode == 0x102) { u_char *d, *e; int i,j; if (mark) mark = 255; d = (u_char *)Crtat; d += 10 + 6*16*100 + (from%80) + 16*100*(from/80); for (i = from ; i <= to ; i++) { e = d; for (j = 0 ; j < 16; j++) { *e = mark^font_16[(p[i]&0xff)*16+j]; e+=100; } d++; if ((i % 80) == 79) d += 20 + 15*100; } } } #ifdef SC_SPLASH_SCREEN static void scsplash_init(void) { /* * We currently assume the splash screen always use * VGA_CG320 mode and abort installation if this mode is not * supported with this video card. XXX */ if (crtc_type != KD_VGA || get_mode_param(cur_console, M_VGA_CG320) == NULL) return; if (splash_load() == 0 && add_scrn_saver(scsplash) == 0) { default_saver = scsplash; scrn_blank_time = DEFAULT_BLANKTIME; run_scrn_saver = TRUE; if (!(boothowto & RB_CONFIG)) { scsplash_stick(TRUE); scsplash(TRUE); } } } static void scsplash(int show) { if (show) splash(TRUE); else if (!sticky_splash) splash(FALSE); } #endif /* SC_SPLASH_SCREEN */ #endif /* NSC */ diff --git a/sys/isa/sio.c b/sys/isa/sio.c index c36de1726369..ab219502e475 100644 --- a/sys/isa/sio.c +++ b/sys/isa/sio.c @@ -1,3040 +1,3041 @@ /*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.211 1998/08/19 04:17:37 bde Exp $ + * $Id: sio.c,v 1.212 1998/08/20 05:12:48 bde Exp $ */ #include "opt_comconsole.h" #include "opt_compat.h" #include "opt_ddb.h" #include "opt_devfs.h" #include "opt_sio.h" #include "sio.h" #include "pnp.h" #ifndef EXTRA_SIO #if NPNP > 0 #define EXTRA_SIO 2 #else #define EXTRA_SIO 0 #endif #endif #define NSIOTOT (NSIO + EXTRA_SIO) /* * Serial driver, based on 386BSD-0.1 com driver. * Mostly rewritten to use pseudo-DMA. * Works for National Semiconductor NS8250-NS16550AF UARTs. * COM driver, based on HP dca driver. * * Changes for PC-Card integration: * - Added PC-Card driver table and handlers */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif #include #include #include #include #include #include #ifdef COM_ESP #include #endif #include #include "card.h" #if NCARD > 0 #include #include #endif #if NPNP > 0 #include #endif #ifdef SMP #define disable_intr() COM_DISABLE_INTR() #define enable_intr() COM_ENABLE_INTR() #endif /* SMP */ #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RS_IBUFSIZE 256 #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 #define CONTROL_INIT_STATE 0x20 #define CONTROL_LOCK_STATE 0x40 #define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) #define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) #define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) #ifdef COM_MULTIPORT /* checks in flags for multiport and which is multiport "master chip" * for a given card */ #define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) #define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ #define COM_CONSOLE(dev) ((dev)->id_flags & 0x10) #define COM_FORCECONSOLE(dev) ((dev)->id_flags & 0x20) #define COM_LLCONSOLE(dev) ((dev)->id_flags & 0x40) #define COM_LOSESOUTINTS(dev) ((dev)->id_flags & 0x08) #define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) #define COM_ST16650A(dev) ((dev)->id_flags & 0x20000) #define COM_C_NOPROBE (0x40000) #define COM_NOPROBE(dev) ((dev)->id_flags & COM_C_NOPROBE) #define COM_C_IIR_TXRDYBUG (0x80000) #define COM_IIR_TXRDYBUG(dev) ((dev)->id_flags & COM_C_IIR_TXRDYBUG) #define COM_FIFOSIZE(dev) (((dev)->id_flags & 0xff000000) >> 24) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ /* * Input buffer watermarks. * The external device is asked to stop sending when the buffer exactly reaches * high water, or when the high level requests it. * The high level is notified immediately (rather than at a later clock tick) * when this watermark is reached. * The buffer size is chosen so the watermark should almost never be reached. * The low watermark is invisibly 0 since the buffer is always emptied all at * once. */ #define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) /* * com state bits. * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher * than the other bits so that they can be tested as a group without masking * off the low bits. * * The following com and tty flags correspond closely: * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and * siostop()) * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). */ #define CS_BUSY 0x80 /* output in progress */ #define CS_TTGO 0x40 /* output not stopped by XOFF */ #define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ #define CS_CHECKMSR 1 /* check of MSR scheduled */ #define CS_CTS_OFLOW 2 /* use CTS output flow control */ #define CS_DTR_OFF 0x10 /* DTR held off */ #define CS_ODONE 4 /* output completed */ #define CS_RTS_IFLOW 8 /* use RTS input flow control */ #define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 "interrupt-level buffer overflow", #define CE_TTY_BUF_OVERFLOW 2 "tty-level buffer overflow", }; #define CE_NTYPES 3 #define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) /* types. XXX - should be elsewhere */ typedef u_int Port_t; /* hardware port */ typedef u_char bool_t; /* boolean */ /* queue of linear buffers */ struct lbq { u_char *l_head; /* next char to process */ u_char *l_tail; /* one past the last char to process */ struct lbq *l_next; /* next in queue */ bool_t l_queued; /* nonzero if queued */ }; /* com device structure */ struct com_s { u_int id_flags; /* Copy isa device falgas */ u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ #ifdef COM_ESP bool_t esp; /* is this unit a hayes esp board? */ #endif u_char extra_state; /* more flag bits, separate for order trick */ u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ bool_t no_irq; /* nonzero if irq is not attached */ bool_t gone; /* hardware disappeared */ bool_t poll; /* nonzero if polling is required */ bool_t poll_output; /* nonzero if polling for output is required */ int unit; /* unit number */ int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ u_int tx_fifo_size; u_int wopeners; /* # processes waiting for DCD in open() */ /* * The high level of the driver never reads status registers directly * because there would be too many side effects to handle conveniently. * Instead, it reads copies of the registers stored here by the * interrupt handler. */ u_char last_modem_status; /* last MSR read by intr handler */ u_char prev_modem_status; /* last MSR handled by high level */ u_char hotchar; /* ldisc-specific char to be handled ASAP */ u_char *ibuf; /* start of input buffer */ u_char *ibufend; /* end of input buffer */ u_char *ihighwater; /* threshold in input buffer */ u_char *iptr; /* next free spot in input buffer */ struct lbq obufq; /* head of queue of output buffers */ struct lbq obufs[2]; /* output buffers */ Port_t data_port; /* i/o ports */ #ifdef COM_ESP Port_t esp_port; #endif Port_t int_id_port; Port_t iobase; Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; Port_t intr_ctl_port; /* Ports of IIR register */ struct tty *tp; /* cross reference */ /* Initial state. */ struct termios it_in; /* should be in struct tty */ struct termios it_out; /* Lock state. */ struct termios lt_in; /* should be in struct tty */ struct termios lt_out; bool_t do_timestamp; bool_t do_dcd_timestamp; struct timeval timestamp; struct timeval dcd_timestamp; u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; u_long error_counts[CE_NTYPES]; /* * Ping-pong input buffers. The extra factor of 2 in the sizes is * to allow for an error byte for each input byte. */ #define CE_INPUT_OFFSET RS_IBUFSIZE u_char ibuf1[2 * RS_IBUFSIZE]; u_char ibuf2[2 * RS_IBUFSIZE]; /* * Data area for output buffers. Someday we should build the output * buffer queue without copying data. */ u_char obuf1[256]; u_char obuf2[256]; #ifdef DEVFS void *devfs_token_ttyd; void *devfs_token_ttyl; void *devfs_token_ttyi; void *devfs_token_cuaa; void *devfs_token_cual; void *devfs_token_cuai; #endif }; #ifdef COM_ESP static int espattach __P((struct isa_device *isdp, struct com_s *com, Port_t esp_port)); #endif static int sioattach __P((struct isa_device *dev)); static timeout_t siobusycheck; static timeout_t siodtrwakeup; static void comhardclose __P((struct com_s *com)); static void siointr1 __P((struct com_s *com)); static int commctl __P((struct com_s *com, int bits, int how)); static int comparam __P((struct tty *tp, struct termios *t)); static swihand_t siopoll; static int sioprobe __P((struct isa_device *dev)); static void siosettimeout __P((void)); static void comstart __P((struct tty *tp)); static timeout_t comwakeup; static void disc_optim __P((struct tty *tp, struct termios *t, struct com_s *com)); #ifdef DSI_SOFT_MODEM static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr)); #endif /* DSI_SOFT_MODEM */ static char driver_name[] = "sio"; /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIOTOT]; #define com_addr(unit) (p_com_addr[unit]) struct isa_driver siodriver = { sioprobe, sioattach, driver_name }; static d_open_t sioopen; static d_close_t sioclose; static d_read_t sioread; static d_write_t siowrite; static d_ioctl_t sioioctl; static d_stop_t siostop; static d_devtotty_t siodevtotty; -#define CDEV_MAJOR 28 -static struct cdevsw sio_cdevsw = { +#define CDEV_MAJOR 28 +static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, ttpoll, nommap, NULL, driver_name, - NULL, -1, + NULL, -1, nodump, nopsize, + D_TTY, }; static int comconsole = -1; static volatile speed_t comdefaultrate = CONSPEED; static u_int com_events; /* input chars + weighted output completions */ static Port_t siocniobase; static bool_t sio_registered; static int sio_timeout; static int sio_timeouts_until_log; static struct callout_handle sio_timeout_handle = CALLOUT_HANDLE_INITIALIZER(&sio_timeout_handle); #if 0 /* XXX */ static struct tty *sio_tty[NSIOTOT]; #else static struct tty sio_tty[NSIOTOT]; #endif static const int nsio_tty = NSIOTOT; static struct speedtab comspeedtab[] = { { 0, 0 }, { 50, COMBRD(50) }, { 75, COMBRD(75) }, { 110, COMBRD(110) }, { 134, COMBRD(134) }, { 150, COMBRD(150) }, { 200, COMBRD(200) }, { 300, COMBRD(300) }, { 600, COMBRD(600) }, { 1200, COMBRD(1200) }, { 1800, COMBRD(1800) }, { 2400, COMBRD(2400) }, { 4800, COMBRD(4800) }, { 9600, COMBRD(9600) }, { 19200, COMBRD(19200) }, { 38400, COMBRD(38400) }, { 57600, COMBRD(57600) }, { 115200, COMBRD(115200) }, { -1, -1 } }; #ifdef COM_ESP /* XXX configure this properly. */ static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; #endif /* * handle sysctl read/write requests for console speed * * In addition to setting comdefaultrate for I/O through /dev/console, * also set the initial and lock values for the /dev/ttyXX device * if there is one associated with the console. Finally, if the /dev/tty * device has already been open, change the speed on the open running port * itself. */ static int sysctl_machdep_comdefaultrate SYSCTL_HANDLER_ARGS { int error, s; speed_t newspeed; struct com_s *com; struct tty *tp; newspeed = comdefaultrate; error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); if (error || !req->newptr) return (error); comdefaultrate = newspeed; if (comconsole < 0) /* serial console not selected? */ return (0); com = com_addr(comconsole); if (!com) return (ENXIO); /* * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX * (note, the lock rates really are boolean -- if non-zero, disallow * speed changes) */ com->it_in.c_ispeed = com->it_in.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_out.c_ispeed = com->it_out.c_ospeed = com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; /* * if we're open, change the running rate too */ tp = com->tp; if (tp && (tp->t_state & TS_ISOPEN)) { tp->t_termios.c_ispeed = tp->t_termios.c_ospeed = comdefaultrate; s = spltty(); error = comparam(tp, &tp->t_termios); splx(s); } return error; } SYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, 0, 0, sysctl_machdep_comdefaultrate, "I", ""); #if NCARD > 0 /* * PC-Card (PCMCIA) specific code. */ static int sioinit __P((struct pccard_devinfo *)); static void siounload __P((struct pccard_devinfo *)); static int card_intr __P((struct pccard_devinfo *)); static struct pccard_device sio_info = { driver_name, sioinit, siounload, card_intr, 0, /* Attributes - presently unused */ &tty_imask /* Interrupt mask for device */ /* XXX - Should this also include net_imask? */ }; DATA_SET(pccarddrv_set, sio_info); /* * Initialize the device - called from Slot manager. */ int sioinit(struct pccard_devinfo *devi) { /* validate unit number. */ if (devi->isahd.id_unit >= (NSIOTOT)) return(ENODEV); /* Make sure it isn't already probed. */ if (com_addr(devi->isahd.id_unit)) return(EBUSY); /* It's already probed as serial by Upper */ devi->isahd.id_flags |= COM_C_NOPROBE; /* * Probe the device. If a value is returned, the * device was found at the location. */ if (sioprobe(&devi->isahd) == 0) return(ENXIO); if (sioattach(&devi->isahd) == 0) return(ENXIO); return(0); } /* * siounload - unload the driver and clear the table. * XXX TODO: * This is usually called when the card is ejected, but * can be caused by a modunload of a controller driver. * The idea is to reset the driver's view of the device * and ensure that any driver entry points such as * read and write do not hang. */ static void siounload(struct pccard_devinfo *devi) { struct com_s *com; if (!devi) { printf("NULL devi in siounload\n"); return; } com = com_addr(devi->isahd.id_unit); if (!com) { printf("NULL com in siounload\n"); return; } if (!com->iobase) { printf("sio%d already unloaded!\n",devi->isahd.id_unit); return; } if (com->tp && (com->tp->t_state & TS_ISOPEN)) { com->gone = 1; printf("sio%d: unload\n", devi->isahd.id_unit); com->tp->t_gen++; ttyclose(com->tp); ttwakeup(com->tp); ttwwakeup(com->tp); } else { com_addr(com->unit) = NULL; bzero(com, sizeof *com); free(com,M_TTYS); printf("sio%d: unload,gone\n", devi->isahd.id_unit); } } /* * card_intr - Shared interrupt called from * front end of PC-Card handler. */ static int card_intr(struct pccard_devinfo *devi) { struct com_s *com; COM_LOCK(); com = com_addr(devi->isahd.id_unit); if (com && !com->gone) siointr1(com_addr(devi->isahd.id_unit)); COM_UNLOCK(); return(1); } #endif /* NCARD > 0 */ static int sioprobe(dev) struct isa_device *dev; { static bool_t already_init; bool_t failures[10]; int fn; struct isa_device *idev; Port_t iobase; intrmask_t irqmap[4]; intrmask_t irqs; u_char mcr_image; int result; struct isa_device *xdev; if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused * port with its MCR_IENABLE gate open will inhibit interrupts * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ for (xdev = isa_devtab_tty; xdev->id_driver != NULL; xdev++) if (xdev->id_driver == &siodriver && xdev->id_enabled) outb(xdev->id_iobase + com_mcr, 0); already_init = TRUE; } if (COM_LLCONSOLE(dev)) { printf("sio%d: reserved for low-level i/o\n", dev->id_unit); return (0); } /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this * register and prepare to leave MCR_IENABLE clear in the mcr. * Otherwise, prepare to set MCR_IENABLE in the mcr. * Point idev to the device struct giving the correct id_irq. * This is the struct for the master device if there is one. */ idev = dev; mcr_image = MCR_IENABLE; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(dev)) { idev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(dev)); if (idev == NULL) { printf("sio%d: master device %d not configured\n", dev->id_unit, COM_MPMASTER(dev)); dev->id_irq = 0; idev = dev; } if (!COM_NOTAST4(dev)) { outb(idev->id_iobase + com_scr, idev->id_irq ? 0x80 : 0); mcr_image = 0; } } #endif /* COM_MULTIPORT */ if (idev->id_irq == 0) mcr_image = 0; bzero(failures, sizeof failures); iobase = dev->id_iobase; /* * We don't want to get actual interrupts, just masked ones. * Interrupts from this line should already be masked in the ICU, * but mask them in the processor as well in case there are some * (misconfigured) shared interrupts. */ disable_intr(); /* EXTRA DELAY? */ /* * Initialize the speed and the word size and wait long enough to * drain the maximum of 16 bytes of junk in device output queues. * The speed is undefined after a master reset and must be set * before relying on anything related to output. There may be * junk after a (very fast) soft reboot and (apparently) after * master reset. * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ if (iobase == siocniobase) DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); else { outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); outb(iobase + com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff); outb(iobase + com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8); outb(iobase + com_cfcr, CFCR_8BITS); DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); } /* * Enable the interrupt gate and disable device interupts. This * should leave the device driving the interrupt line low and * guarantee an edge trigger if an interrupt can be generated. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); outb(iobase + com_ier, 0); DELAY(1000); /* XXX */ irqmap[0] = isa_irq_pending(); /* * Attempt to set loopback mode so that we can send a null byte * without annoying any external device. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); /* * Attempt to generate an output interrupt. On 8250's, setting * IER_ETXRDY generates an interrupt independent of the current * setting and independent of whether the THR is empty. On 16450's, * setting IER_ETXRDY generates an interrupt independent of the * current setting. On 16550A's, setting IER_ETXRDY only * generates an interrupt when IER_ETXRDY is not already set. */ outb(iobase + com_ier, IER_ETXRDY); /* * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate * an interrupt. They'd better generate one for actually doing * output. Loopback may be broken on the same incompatibles but * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); /* * Turn off loopback mode so that the interrupt gate works again * (MCR_IENABLE was hidden). This should leave the device driving * an interrupt line high. It doesn't matter if the interrupt * line oscillates while we are not looking at it, since interrupts * are disabled. */ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); /* * It's a definitly Serial PCMCIA(16550A), but still be required * for IIR_TXRDY implementation ( Palido 321s, DC-1S... ) */ if ( COM_NOPROBE(dev) ) { /* Reading IIR register twice */ for ( fn = 0; fn < 2; fn ++ ) { DELAY(10000); failures[6] = inb(iobase + com_iir); } /* Check IIR_TXRDY clear ? */ result = IO_COMSIZE; if ( failures[6] & IIR_TXRDY ) { /* Nop, Double check with clearing IER */ outb(iobase + com_ier, 0); if ( inb(iobase + com_iir) & IIR_NOPEND ) { /* Ok. we're familia this gang */ dev->id_flags |= COM_C_IIR_TXRDYBUG; /* Set IIR_TXRDYBUG */ } else { /* Unknow, Just omit this chip.. XXX*/ result = 0; } } else { /* OK. this is well-known guys */ dev->id_flags &= ~COM_C_IIR_TXRDYBUG; /*Clear IIR_TXRDYBUG*/ } outb(iobase + com_cfcr, CFCR_8BITS); enable_intr(); return (iobase == siocniobase ? IO_COMSIZE : result); } /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them * (the values happen to be all distinct - this is good for * avoiding false positive tests from bus echoes). * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ /* EXTRA DELAY? */ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; failures[1] = inb(iobase + com_ier) - IER_ETXRDY; failures[2] = inb(iobase + com_mcr) - mcr_image; DELAY(10000); /* Some internal modems need this time */ irqmap[1] = isa_irq_pending(); failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; DELAY(1000); /* XXX */ irqmap[2] = isa_irq_pending(); failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* * Turn off all device interrupts and check that they go off properly. * Leave MCR_IENABLE alone. For ports without a master port, it gates * the OUT2 output of the UART to * the ICU input. Closing the gate would give a floating ICU input * (unless there is another device driving at) and spurious interrupts. * (On the system that this was first tested on, the input floats high * and gives a (masked) interrupt as soon as the gate is closed.) */ outb(iobase + com_ier, 0); outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ failures[7] = inb(iobase + com_ier); DELAY(1000); /* XXX */ irqmap[3] = isa_irq_pending(); failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; enable_intr(); irqs = irqmap[1] & ~irqmap[0]; if (idev->id_irq != 0 && (idev->id_irq & irqs) == 0) printf( "sio%d: configured irq %d not in bitmap of probed irqs %#x\n", dev->id_unit, ffs(idev->id_irq) - 1, irqs); if (bootverbose) printf("sio%d: irq maps: %#x %#x %#x %#x\n", dev->id_unit, irqmap[0], irqmap[1], irqmap[2], irqmap[3]); result = IO_COMSIZE; for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) { outb(iobase + com_mcr, 0); result = 0; if (bootverbose) { printf("sio%d: probe failed test(s):", dev->id_unit); for (fn = 0; fn < sizeof failures; ++fn) if (failures[fn]) printf(" %d", fn); printf("\n"); } break; } return (iobase == siocniobase ? IO_COMSIZE : result); } #ifdef COM_ESP static int espattach(isdp, com, esp_port) struct isa_device *isdp; struct com_s *com; Port_t esp_port; { u_char dips; u_char val; /* * Check the ESP-specific I/O port to see if we're an ESP * card. If not, return failure immediately. */ if ((inb(esp_port) & 0xf3) == 0) { printf(" port 0x%x is not an ESP board?\n", esp_port); return (0); } /* * We've got something that claims to be a Hayes ESP card. * Let's hope so. */ /* Get the dip-switch configuration */ outb(esp_port + ESP_CMD1, ESP_GETDIPS); dips = inb(esp_port + ESP_STATUS1); /* * Bits 0,1 of dips say which COM port we are. */ if (com->iobase == likely_com_ports[dips & 0x03]) printf(" : ESP"); else { printf(" esp_port has com %d\n", dips & 0x03); return (0); } /* * Check for ESP version 2.0 or later: bits 4,5,6 = 010. */ outb(esp_port + ESP_CMD1, ESP_GETTEST); val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ val = inb(esp_port + ESP_STATUS2); if ((val & 0x70) < 0x20) { printf("-old (%o)", val & 0x70); return (0); } /* * Check for ability to emulate 16550: bit 7 == 1 */ if ((dips & 0x80) == 0) { printf(" slave"); return (0); } /* * Okay, we seem to be a Hayes ESP card. Whee. */ com->esp = TRUE; com->esp_port = esp_port; return (1); } #endif /* COM_ESP */ static int sioattach(isdp) struct isa_device *isdp; { struct com_s *com; dev_t dev; #ifdef COM_ESP Port_t *espp; #endif Port_t iobase; int s; int unit; isdp->id_ri_flags |= RI_FAST; iobase = isdp->id_iobase; unit = isdp->id_unit; com = malloc(sizeof *com, M_TTYS, M_NOWAIT); if (com == NULL) return (0); /* * sioprobe() has initialized the device registers as follows: * o cfcr = CFCR_8BITS. * It is most important that CFCR_DLAB is off, so that the * data port is not hidden when we enable interrupts. * o ier = 0. * Interrupts are only enabled when the line is open. * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible * interrupt control register or the config specifies no irq. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ bzero(com, sizeof *com); com->unit = unit; com->cfcr_image = CFCR_8BITS; com->dtr_wait = 3 * hz; com->loses_outints = COM_LOSESOUTINTS(isdp) != 0; com->no_irq = isdp->id_irq == 0; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; com->obufs[0].l_head = com->obuf1; com->obufs[1].l_head = com->obuf2; com->iobase = iobase; com->data_port = iobase + com_data; com->int_id_port = iobase + com_iir; com->modem_ctl_port = iobase + com_mcr; com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; com->intr_ctl_port = iobase + com_ier; /* * We don't use all the flags from since they * are only relevant for logins. It's important to have echo off * initially so that the line doesn't start blathering before the * echo flag can be turned off. */ com->it_in.c_iflag = 0; com->it_in.c_oflag = 0; com->it_in.c_cflag = TTYDEF_CFLAG; com->it_in.c_lflag = 0; if (unit == comconsole) { com->it_in.c_iflag = TTYDEF_IFLAG; com->it_in.c_oflag = TTYDEF_OFLAG; com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; com->lt_out.c_ispeed = com->lt_out.c_ospeed = com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; } else com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; termioschars(&com->it_in); com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); #ifdef DSI_SOFT_MODEM if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) { printf(" Digicom Systems, Inc. SoftModem"); goto determined_type; } #endif /* DSI_SOFT_MODEM */ #ifdef COM_MULTIPORT if (!COM_ISMULTIPORT(isdp) && !COM_IIR_TXRDYBUG(isdp)) #else if (!COM_IIR_TXRDYBUG(isdp)) #endif { u_char scr; u_char scr1; u_char scr2; scr = inb(iobase + com_scr); outb(iobase + com_scr, 0xa5); scr1 = inb(iobase + com_scr); outb(iobase + com_scr, 0x5a); scr2 = inb(iobase + com_scr); outb(iobase + com_scr, scr); if (scr1 != 0xa5 || scr2 != 0x5a) { printf(" 8250"); goto determined_type; } } outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); DELAY(100); com->st16650a = 0; switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_RX_LOW: printf(" 16450"); break; case FIFO_RX_MEDL: printf(" 16450?"); break; case FIFO_RX_MEDH: printf(" 16550?"); break; case FIFO_RX_HIGH: if (COM_NOFIFO(isdp)) { printf(" 16550A fifo disabled"); } else { com->hasfifo = TRUE; if (COM_ST16650A(isdp)) { com->st16650a = 1; com->tx_fifo_size = 32; printf(" ST16650A"); } else { com->tx_fifo_size = COM_FIFOSIZE(isdp); printf(" 16550A"); } } #ifdef COM_ESP for (espp = likely_esp_ports; *espp != 0; espp++) if (espattach(isdp, com, *espp)) { com->tx_fifo_size = 1024; break; } #endif if (!com->st16650a) { if (!com->tx_fifo_size) com->tx_fifo_size = 16; else printf(" lookalike with %d bytes FIFO", com->tx_fifo_size); } break; } #ifdef COM_ESP if (com->esp) { /* * Set 16550 compatibility mode. * We don't use the ESP_MODE_SCALE bit to increase the * fifo trigger levels because we can't handle large * bursts of input. * XXX flow control should be set in comparam(), not here. */ outb(com->esp_port + ESP_CMD1, ESP_SETMODE); outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); /* Set RTS/CTS flow control. */ outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); /* Set flow-control levels. */ outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); outb(com->esp_port + ESP_CMD2, HIBYTE(768)); outb(com->esp_port + ESP_CMD2, LOBYTE(768)); outb(com->esp_port + ESP_CMD2, HIBYTE(512)); outb(com->esp_port + ESP_CMD2, LOBYTE(512)); } #endif /* COM_ESP */ outb(iobase + com_fifo, 0); determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(isdp)) { com->multiport = TRUE; printf(" (multiport"); if (unit == COM_MPMASTER(isdp)) printf(" master"); printf(")"); com->no_irq = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ if (unit == comconsole) printf(", console"); if ( COM_IIR_TXRDYBUG(isdp) ) printf(" with a bogus IIR_TXRDY register"); printf("\n"); s = spltty(); com_addr(unit) = com; splx(s); if (!sio_registered) { dev = makedev(CDEV_MAJOR, 0); cdevsw_add(&dev, &sio_cdevsw, NULL); register_swi(SWI_TTY, siopoll); sio_registered = TRUE; } #ifdef DEVFS com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, unit, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyd%r", unit); com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_INIT_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyid%r", unit); com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, unit | CONTROL_LOCK_STATE, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "ttyld%r", unit); com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaa%r", unit); com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuaia%r", unit); com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuala%r", unit); #endif com->id_flags = isdp->id_flags; /* Heritate id_flags for later */ return (1); } static int sioopen(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; int unit; mynor = minor(dev); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIOTOT || (com = com_addr(unit)) == NULL) return (ENXIO); if (com->gone) return (ENXIO); if (mynor & CONTROL_MASK) return (0); #if 0 /* XXX */ tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); #else tp = com->tp = &sio_tty[unit]; #endif s = spltty(); /* * We jump to this label after all non-interrupted sleeps to pick * up any changes of the device state. */ open_top: while (com->state & CS_DTR_OFF) { error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; } if (tp->t_state & TS_ISOPEN) { /* * The device is open, so everything has been initialized. * Handle conflicts. */ if (mynor & CALLOUT_MASK) { if (!com->active_out) { error = EBUSY; goto out; } } else { if (com->active_out) { if (flag & O_NONBLOCK) { error = EBUSY; goto out; } error = tsleep(&com->active_out, TTIPRI | PCATCH, "siobi", 0); if (com_addr(unit) == NULL) return (ENXIO); if (error != 0 || com->gone) goto out; goto open_top; } } if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { error = EBUSY; goto out; } } else { /* * The device isn't open, so there are no conflicts. * Initialize it. Initialization is done twice in many * cases: to preempt sleeping callin opens if we are * callout, and to complete a callin open after DCD rises. */ tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; tp->t_termios = mynor & CALLOUT_MASK ? com->it_out : com->it_in; tp->t_ififosize = 2 * RS_IBUFSIZE; tp->t_ispeedwat = (speed_t)-1; tp->t_ospeedwat = (speed_t)-1; (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); com->poll = com->no_irq; com->poll_output = com->loses_outints; ++com->wopeners; error = comparam(tp, &tp->t_termios); --com->wopeners; if (error != 0) goto out; /* * XXX we should goto open_top if comparam() slept. */ iobase = com->iobase; if (com->hasfifo) { /* * (Re)enable and drain fifos. * * Certain SMC chips cause problems if the fifos * are enabled while input is ready. Turn off the * fifo if necessary to clear the input. We test * the input ready bit after enabling the fifos * since we've already enabled them in comparam() * and to handle races between enabling and fresh * input. */ while (TRUE) { outb(iobase + com_fifo, FIFO_RCV_RST | FIFO_XMT_RST | com->fifo_image); /* * XXX the delays are for superstitious * historical reasons. It must be less than * the character time at the maximum * supported speed (87 usec at 115200 bps * 8N1). Otherwise we might loop endlessly * if data is streaming in. We used to use * delays of 100. That usually worked * because DELAY(100) used to usually delay * for about 85 usec instead of 100. */ DELAY(50); if (!(inb(com->line_status_port) & LSR_RXRDY)) break; outb(iobase + com_fifo, 0); DELAY(50); (void) inb(com->data_port); } } disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); if (COM_IIR_TXRDYBUG(com)) { outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS | IER_EMSC); } else { outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); } enable_intr(); /* * Handle initial DCD. Callout devices get a fake initial * DCD (trapdoor DCD). If we are callout, then any sleeping * callin opens get woken up and resume sleeping on "siobi" * instead of "siodcd". */ /* * XXX `mynor & CALLOUT_MASK' should be * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where * TRAPDOOR_CARRIER is the default initial state for callout * devices and SOFT_CARRIER is like CLOCAL except it hides * the true carrier. */ if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) (*linesw[tp->t_line].l_modem)(tp, 1); } /* * Wait for DCD if necessary. */ if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { ++com->wopeners; error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); if (com_addr(unit) == NULL) return (ENXIO); --com->wopeners; if (error != 0 || com->gone) goto out; goto open_top; } error = (*linesw[tp->t_line].l_open)(dev, tp); disc_optim(tp, &tp->t_termios, com); if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) com->active_out = TRUE; siosettimeout(); out: splx(s); if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) comhardclose(com); return (error); } static int sioclose(dev, flag, mode, p) dev_t dev; int flag; int mode; struct proc *p; { struct com_s *com; int mynor; int s; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (0); com = com_addr(MINOR_TO_UNIT(mynor)); tp = com->tp; s = spltty(); (*linesw[tp->t_line].l_close)(tp, flag); disc_optim(tp, &tp->t_termios, com); siostop(tp, FREAD | FWRITE); comhardclose(com); ttyclose(tp); siosettimeout(); splx(s); if (com->gone) { printf("sio%d: gone\n", com->unit); s = spltty(); com_addr(com->unit) = 0; bzero(tp,sizeof *tp); bzero(com,sizeof *com); free(com,M_TTYS); splx(s); } return (0); } static void comhardclose(com) struct com_s *com; { Port_t iobase; int s; struct tty *tp; int unit; unit = com->unit; iobase = com->iobase; s = spltty(); com->poll = FALSE; com->poll_output = FALSE; com->do_timestamp = FALSE; com->do_dcd_timestamp = FALSE; outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); { outb(iobase + com_ier, 0); tp = com->tp; if (tp->t_cflag & HUPCL /* * XXX we will miss any carrier drop between here and the * next open. Perhaps we should watch DCD even when the * port is closed; it is not sufficient to check it at * the next open because it might go up and down while * we're not watching. */ || !com->active_out && !(com->prev_modem_status & MSR_DCD) && !(com->it_in.c_cflag & CLOCAL) || !(tp->t_state & TS_ISOPEN)) { (void)commctl(com, TIOCM_DTR, DMBIC); if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { timeout(siodtrwakeup, com, com->dtr_wait); com->state |= CS_DTR_OFF; } } } if (com->hasfifo) { /* * Disable fifos so that they are off after controlled * reboots. Some BIOSes fail to detect 16550s when the * fifos are enabled. */ outb(iobase + com_fifo, 0); } com->active_out = FALSE; wakeup(&com->active_out); wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ splx(s); } static int sioread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; int unit; struct tty *tp; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } static int siowrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { int mynor; struct tty *tp; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (ENODEV); unit = MINOR_TO_UNIT(mynor); if (com_addr(unit)->gone) return (ENODEV); tp = com_addr(unit)->tp; /* * (XXX) We disallow virtual consoles if the physical console is * a serial port. This is in case there is a display attached that * is not the console. In that situation we don't need/want the X * server taking over the console. */ if (constty != NULL && unit == comconsole) constty = NULL; return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } static void siobusycheck(chan) void *chan; { struct com_s *com; int s; com = (struct com_s *)chan; /* * Clear TS_BUSY if low-level output is complete. * spl locking is sufficient because siointr1() does not set CS_BUSY. * If siointr1() clears CS_BUSY after we look at it, then we'll get * called again. Reading the line status port outside of siointr1() * is safe because CS_BUSY is clear so there are no output interrupts * to lose. */ s = spltty(); if (com->state & CS_BUSY) com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) == (LSR_TSRE | LSR_TXRDY)) { com->tp->t_state &= ~TS_BUSY; ttwwakeup(com->tp); com->extra_state &= ~CSE_BUSYCHECK; } else timeout(siobusycheck, com, hz / 100); splx(s); } static void siodtrwakeup(chan) void *chan; { struct com_s *com; com = (struct com_s *)chan; com->state &= ~CS_DTR_OFF; wakeup(&com->dtr_wait); } void siointr(unit) int unit; { #ifndef COM_MULTIPORT COM_LOCK(); siointr1(com_addr(unit)); COM_UNLOCK(); #else /* COM_MULTIPORT */ struct com_s *com; bool_t possibly_more_intrs; /* * Loop until there is no activity on any port. This is necessary * to get an interrupt edge more than to avoid another interrupt. * If the IRQ signal is just an OR of the IRQ signals from several * devices, then the edge from one may be lost because another is * on. */ COM_LOCK(); do { possibly_more_intrs = FALSE; for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); /* * XXX COM_LOCK(); * would it work here, or be counter-productive? */ if (com != NULL && !com->gone && (inb(com->int_id_port) & IIR_IMASK) != IIR_NOPEND) { siointr1(com); possibly_more_intrs = TRUE; } /* XXX COM_UNLOCK(); */ } } while (possibly_more_intrs); COM_UNLOCK(); #endif /* COM_MULTIPORT */ } static void siointr1(com) struct com_s *com; { u_char line_status; u_char modem_status; u_char *ioptr; u_char recv_data; u_char int_ident; u_char int_ctl; u_char int_ctl_new; int_ctl = inb(com->intr_ctl_port); int_ctl_new = int_ctl; while (!com->gone) { line_status = inb(com->line_status_port); /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ if (!(line_status & LSR_RXRDY)) recv_data = 0; else recv_data = inb(com->data_port); if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { /* * Don't store BI if IGNBRK or FE/PE if IGNPAR. * Otherwise, push the work to a higher level * (to handle PARMRK) if we're bypassing. * Otherwise, convert BI/FE and PE+INPCK to 0. * * This makes bypassing work right in the * usual "raw" case (IGNBRK set, and IGNPAR * and INPCK clear). * * Note: BI together with FE/PE means just BI. */ if (line_status & LSR_BI) { #if defined(DDB) && defined(BREAK_TO_DEBUGGER) if (com->unit == comconsole) { breakpoint(); goto cont; } #endif if (com->tp == NULL || com->tp->t_iflag & IGNBRK) goto cont; } else { if (com->tp == NULL || com->tp->t_iflag & IGNPAR) goto cont; } if (com->tp->t_state & TS_CAN_BYPASS_L_RINT && (line_status & (LSR_BI | LSR_FE) || com->tp->t_iflag & INPCK)) recv_data = 0; } ++com->bytes_in; if (com->hotchar != 0 && recv_data == com->hotchar) setsofttty(); ioptr = com->iptr; if (ioptr >= com->ibufend) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { if (com->do_timestamp) microtime(&com->timestamp); ++com_events; schedsofttty(); #if 0 /* for testing input latency vs efficiency */ if (com->iptr - com->ibuf == 8) setsofttty(); #endif ioptr[0] = recv_data; ioptr[CE_INPUT_OFFSET] = line_status; com->iptr = ++ioptr; if (ioptr == com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); if (line_status & LSR_OE) CE_RECORD(com, CE_OVERRUN); } cont: /* * "& 0x7F" is to avoid the gcc-1.40 generating a slow * jump from the top of the loop to here */ line_status = inb(com->line_status_port) & 0x7F; } /* modem status change? (always check before doing output) */ modem_status = inb(com->modem_status_port); if (modem_status != com->last_modem_status) { if (com->do_dcd_timestamp && !(com->last_modem_status & MSR_DCD) && modem_status & MSR_DCD) microtime(&com->dcd_timestamp); /* * Schedule high level to handle DCD changes. Note * that we don't use the delta bits anywhere. Some * UARTs mess them up, and it's easy to remember the * previous bits and calculate the delta. */ com->last_modem_status = modem_status; if (!(com->state & CS_CHECKMSR)) { com_events += LOTS_OF_EVENTS; com->state |= CS_CHECKMSR; setsofttty(); } /* handle CTS change immediately for crisp flow ctl */ if (com->state & CS_CTS_OFLOW) { if (modem_status & MSR_CTS) com->state |= CS_ODEVREADY; else com->state &= ~CS_ODEVREADY; } } /* output queued and everything ready? */ if (line_status & LSR_TXRDY && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { ioptr = com->obufq.l_head; if (com->tx_fifo_size > 1) { u_int ocount; ocount = com->obufq.l_tail - ioptr; if (ocount > com->tx_fifo_size) ocount = com->tx_fifo_size; com->bytes_out += ocount; do outb(com->data_port, *ioptr++); while (--ocount != 0); } else { outb(com->data_port, *ioptr++); ++com->bytes_out; } com->obufq.l_head = ioptr; if (COM_IIR_TXRDYBUG(com)) { int_ctl_new = int_ctl | IER_ETXRDY; } if (ioptr >= com->obufq.l_tail) { struct lbq *qp; qp = com->obufq.l_next; qp->l_queued = FALSE; qp = qp->l_next; if (qp != NULL) { com->obufq.l_head = qp->l_head; com->obufq.l_tail = qp->l_tail; com->obufq.l_next = qp; } else { /* output just completed */ if ( COM_IIR_TXRDYBUG(com) ) { int_ctl_new = int_ctl & ~IER_ETXRDY; } com->state &= ~CS_BUSY; } if (!(com->state & CS_ODONE)) { com_events += LOTS_OF_EVENTS; com->state |= CS_ODONE; setsofttty(); /* handle at high level ASAP */ } } if ( COM_IIR_TXRDYBUG(com) && (int_ctl != int_ctl_new)) { outb(com->intr_ctl_port, int_ctl_new); } } /* finished? */ #ifndef COM_MULTIPORT if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) #endif /* COM_MULTIPORT */ return; } } static int sioioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { struct com_s *com; int error; Port_t iobase; int mynor; int s; struct tty *tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) int oldcmd; struct termios term; #endif mynor = minor(dev); com = com_addr(MINOR_TO_UNIT(mynor)); if (com->gone) return (ENODEV); iobase = com->iobase; if (mynor & CONTROL_MASK) { struct termios *ct; switch (mynor & CONTROL_MASK) { case CONTROL_INIT_STATE: ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; break; case CONTROL_LOCK_STATE: ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; break; default: return (ENODEV); /* /dev/nodev */ } switch (cmd) { case TIOCSETA: error = suser(p->p_ucred, &p->p_acflag); if (error != 0) return (error); *ct = *(struct termios *)data; return (0); case TIOCGETA: *(struct termios *)data = *ct; return (0); case TIOCGETD: *(int *)data = TTYDISC; return (0); case TIOCGWINSZ: bzero(data, sizeof(struct winsize)); return (0); #ifdef DSI_SOFT_MODEM /* * Download micro-code to Digicom modem. */ case TIOCDSIMICROCODE: { u_long l; u_char *p,*pi; pi = (u_char*)(*(caddr_t*)data); error = copyin(pi,&l,sizeof l); if(error) {return error;}; pi += sizeof l; p = malloc(l,M_TEMP,M_NOWAIT); if(!p) {return ENOBUFS;} error = copyin(pi,p,l); if(error) {free(p,M_TEMP); return error;}; if(error = LoadSoftModem( MINOR_TO_UNIT(mynor),iobase,l,p)) {free(p,M_TEMP); return error;} free(p,M_TEMP); return(0); } #endif /* DSI_SOFT_MODEM */ default: return (ENOTTY); } } tp = com->tp; #if defined(COMPAT_43) || defined(COMPAT_SUNOS) term = tp->t_termios; oldcmd = cmd; error = ttsetcompat(tp, &cmd, data, &term); if (error != 0) return (error); if (cmd != oldcmd) data = (caddr_t)&term; #endif if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { int cc; struct termios *dt = (struct termios *)data; struct termios *lt = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; dt->c_iflag = (tp->t_iflag & lt->c_iflag) | (dt->c_iflag & ~lt->c_iflag); dt->c_oflag = (tp->t_oflag & lt->c_oflag) | (dt->c_oflag & ~lt->c_oflag); dt->c_cflag = (tp->t_cflag & lt->c_cflag) | (dt->c_cflag & ~lt->c_cflag); dt->c_lflag = (tp->t_lflag & lt->c_lflag) | (dt->c_lflag & ~lt->c_lflag); for (cc = 0; cc < NCCS; ++cc) if (lt->c_cc[cc] != 0) dt->c_cc[cc] = tp->t_cc[cc]; if (lt->c_ispeed != 0) dt->c_ispeed = tp->t_ispeed; if (lt->c_ospeed != 0) dt->c_ospeed = tp->t_ospeed; } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error != ENOIOCTL) return (error); s = spltty(); error = ttioctl(tp, cmd, data, flag); disc_optim(tp, &tp->t_termios, com); if (error != ENOIOCTL) { splx(s); return (error); } switch (cmd) { case TIOCSBRK: outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); break; case TIOCCBRK: outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: (void)commctl(com, TIOCM_DTR, DMBIS); break; case TIOCCDTR: (void)commctl(com, TIOCM_DTR, DMBIC); break; /* * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The * changes get undone on the next call to comparam(). */ case TIOCMSET: (void)commctl(com, *(int *)data, DMSET); break; case TIOCMBIS: (void)commctl(com, *(int *)data, DMBIS); break; case TIOCMBIC: (void)commctl(com, *(int *)data, DMBIC); break; case TIOCMGET: *(int *)data = commctl(com, 0, DMGET); break; case TIOCMSDTRWAIT: /* must be root since the wait applies to following logins */ error = suser(p->p_ucred, &p->p_acflag); if (error != 0) { splx(s); return (error); } com->dtr_wait = *(int *)data * hz / 100; break; case TIOCMGDTRWAIT: *(int *)data = com->dtr_wait * 100 / hz; break; case TIOCTIMESTAMP: com->do_timestamp = TRUE; *(struct timeval *)data = com->timestamp; break; case TIOCDCDTIMESTAMP: com->do_dcd_timestamp = TRUE; *(struct timeval *)data = com->dcd_timestamp; break; default: splx(s); return (ENOTTY); } splx(s); return (0); } static void siopoll() { int unit; if (com_events == 0) return; repeat: for (unit = 0; unit < NSIOTOT; ++unit) { u_char *buf; struct com_s *com; u_char *ibuf; int incc; struct tty *tp; com = com_addr(unit); if (com == NULL) continue; tp = com->tp; if (tp == NULL || com->gone) { /* * Discard any events related to never-opened or * going-away devices. */ disable_intr(); incc = com->iptr - com->ibuf; com->iptr = com->ibuf; if (com->state & CS_CHECKMSR) { incc += LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; } com_events -= incc; enable_intr(); continue; } /* switch the role of the low-level input buffers */ if (com->iptr == (ibuf = com->ibuf)) { buf = NULL; /* not used, but compiler can't tell */ incc = 0; } else { buf = ibuf; disable_intr(); incc = com->iptr - buf; com_events -= incc; if (ibuf == com->ibuf1) ibuf = com->ibuf2; else ibuf = com->ibuf1; com->ibufend = ibuf + RS_IBUFSIZE; com->ihighwater = ibuf + RS_IHIGHWATER; com->iptr = ibuf; /* * There is now room for another low-level buffer full * of input, so enable RTS if it is now disabled and * there is room in the high-level buffer. */ if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & MCR_RTS) && !(tp->t_state & TS_TBLOCK)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); enable_intr(); com->ibuf = ibuf; } if (com->state & CS_CHECKMSR) { u_char delta_modem_status; disable_intr(); delta_modem_status = com->last_modem_status ^ com->prev_modem_status; com->prev_modem_status = com->last_modem_status; com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); if (delta_modem_status & MSR_DCD) (*linesw[tp->t_line].l_modem) (tp, com->prev_modem_status & MSR_DCD); } if (com->state & CS_ODONE) { disable_intr(); com_events -= LOTS_OF_EVENTS; com->state &= ~CS_ODONE; enable_intr(); if (!(com->state & CS_BUSY) && !(com->extra_state & CSE_BUSYCHECK)) { timeout(siobusycheck, com, hz / 100); com->extra_state |= CSE_BUSYCHECK; } (*linesw[tp->t_line].l_start)(tp); } if (incc <= 0 || !(tp->t_state & TS_ISOPEN) || !(tp->t_cflag & CREAD)) continue; /* * Avoid the grotesquely inefficient lineswitch routine * (ttyinput) in "raw" mode. It usually takes about 450 * instructions (that's without canonical processing or echo!). * slinput is reasonably fast (usually 40 instructions plus * call overhead). */ if (tp->t_state & TS_CAN_BYPASS_L_RINT) { if (tp->t_rawq.c_cc + incc > tp->t_ihiwat && (com->state & CS_RTS_IFLOW || tp->t_iflag & IXOFF) && !(tp->t_state & TS_TBLOCK)) ttyblock(tp); tk_nin += incc; tk_rawcc += incc; tp->t_rawcc += incc; com->delta_error_counts[CE_TTY_BUF_OVERFLOW] += b_to_q((char *)buf, incc, &tp->t_rawq); ttwakeup(tp); if (tp->t_state & TS_TTSTOP && (tp->t_iflag & IXANY || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { tp->t_state &= ~TS_TTSTOP; tp->t_lflag &= ~FLUSHO; comstart(tp); } } else { do { u_char line_status; int recv_data; line_status = (u_char) buf[CE_INPUT_OFFSET]; recv_data = (u_char) *buf++; if (line_status & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { if (line_status & LSR_BI) recv_data |= TTY_BI; if (line_status & LSR_FE) recv_data |= TTY_FE; if (line_status & LSR_OE) recv_data |= TTY_OE; if (line_status & LSR_PE) recv_data |= TTY_PE; } (*linesw[tp->t_line].l_rint)(recv_data, tp); } while (--incc > 0); } if (com_events == 0) break; } if (com_events >= LOTS_OF_EVENTS) goto repeat; } static int comparam(tp, t) struct tty *tp; struct termios *t; { u_int cfcr; int cflag; struct com_s *com; int divisor; u_char dlbh; u_char dlbl; int error; Port_t iobase; int s; int unit; int txtimeout; /* do historical conversions */ if (t->c_ispeed == 0) t->c_ispeed = t->c_ospeed; /* check requested parameters */ divisor = ttspeedtab(t->c_ospeed, comspeedtab); if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); iobase = com->iobase; s = spltty(); if (divisor == 0) (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ else (void)commctl(com, TIOCM_DTR, DMBIS); cflag = t->c_cflag; switch (cflag & CSIZE) { case CS5: cfcr = CFCR_5BITS; break; case CS6: cfcr = CFCR_6BITS; break; case CS7: cfcr = CFCR_7BITS; break; default: cfcr = CFCR_8BITS; break; } if (cflag & PARENB) { cfcr |= CFCR_PENAB; if (!(cflag & PARODD)) cfcr |= CFCR_PEVEN; } if (cflag & CSTOPB) cfcr |= CFCR_STOPB; if (com->hasfifo && divisor != 0) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and * the total latency is less than about 30 msec. These * latencies are reasonable for humans. Serial comms * protocols shouldn't expect anything better since modem * latencies are larger. */ com->fifo_image = t->c_ospeed <= 4800 ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH; #ifdef COM_ESP /* * The Hayes ESP card needs the fifo DMA mode bit set * in compatibility mode. If not, it will interrupt * for each character received. */ if (com->esp) com->fifo_image |= FIFO_DMA_MODE; #endif outb(iobase + com_fifo, com->fifo_image); } /* * Some UARTs lock up if the divisor latch registers are selected * while the UART is doing output (they refuse to transmit anything * more until given a hard reset). Fix this by stopping filling * the device buffers and waiting for them to drain. Reading the * line status port outside of siointr1() might lose some receiver * error bits, but that is acceptable here. */ disable_intr(); retry: com->state &= ~CS_TTGO; txtimeout = tp->t_timeout; enable_intr(); while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) { tp->t_state |= TS_SO_OCOMPLETE; error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, "siotx", hz / 100); if ( txtimeout != 0 && (!error || error == EAGAIN) && (txtimeout -= hz / 100) <= 0 ) error = EIO; if (com->gone) error = ENODEV; if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); com->state |= CS_TTGO; enable_intr(); } splx(s); return (error); } } disable_intr(); /* very important while com_data is hidden */ /* * XXX - clearing CS_TTGO is not sufficient to stop further output, * because siopoll() calls comstart() which usually sets it again * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be * sufficient, for similar reasons. */ if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY)) goto retry; if (divisor != 0) { outb(iobase + com_cfcr, cfcr | CFCR_DLAB); /* * Only set the divisor registers if they would change, * since on some 16550 incompatibles (UMC8669F), setting * them while input is arriving them loses sync until * data stops arriving. */ dlbl = divisor & 0xFF; if (inb(iobase + com_dlbl) != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = (u_int) divisor >> 8; if (inb(iobase + com_dlbh) != dlbh) outb(iobase + com_dlbh, dlbh); } outb(iobase + com_cfcr, com->cfcr_image = cfcr); if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; if (cflag & CRTS_IFLOW) { if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x40); } com->state |= CS_RTS_IFLOW; /* * If CS_RTS_IFLOW just changed from off to on, the change * needs to be propagated to MCR_RTS. This isn't urgent, * so do it later by calling comstart() instead of repeating * a lot of code from comstart() here. */ } else if (com->state & CS_RTS_IFLOW) { com->state &= ~CS_RTS_IFLOW; /* * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS * on here, since comstart() won't do it later. */ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x40); } } /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? * Now has 10+ msec latency, while CTS flow has 50- usec latency. */ com->state |= CS_ODEVREADY; com->state &= ~CS_CTS_OFLOW; if (cflag & CCTS_OFLOW) { com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x80); } } else { if (com->st16650a) { outb(iobase + com_cfcr, 0xbf); outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x80); } } outb(iobase + com_cfcr, com->cfcr_image); /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); /* * Recover from fiddling with CS_TTGO. We used to call siointr1() * unconditionally, but that defeated the careful discarding of * stale input in sioopen(). */ if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); enable_intr(); splx(s); comstart(tp); return (0); } static void comstart(tp) struct tty *tp; { struct com_s *com; int s; int unit; unit = DEV_TO_UNIT(tp->t_dev); com = com_addr(unit); s = spltty(); disable_intr(); if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; if (tp->t_state & TS_TBLOCK) { if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); } else { if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { ttwwakeup(tp); splx(s); return; } if (tp->t_outq.c_cc != 0) { struct lbq *qp; struct lbq *next; if (!com->obufs[0].l_queued) { com->obufs[0].l_tail = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, sizeof com->obuf1); com->obufs[0].l_next = NULL; com->obufs[0].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[0]; } else { com->obufq.l_head = com->obufs[0].l_head; com->obufq.l_tail = com->obufs[0].l_tail; com->obufq.l_next = &com->obufs[0]; com->state |= CS_BUSY; } enable_intr(); } if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { com->obufs[1].l_tail = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, sizeof com->obuf2); com->obufs[1].l_next = NULL; com->obufs[1].l_queued = TRUE; disable_intr(); if (com->state & CS_BUSY) { qp = com->obufq.l_next; while ((next = qp->l_next) != NULL) qp = next; qp->l_next = &com->obufs[1]; } else { com->obufq.l_head = com->obufs[1].l_head; com->obufq.l_tail = com->obufs[1].l_tail; com->obufq.l_next = &com->obufs[1]; com->state |= CS_BUSY; } enable_intr(); } tp->t_state |= TS_BUSY; } disable_intr(); if (com->state >= (CS_BUSY | CS_TTGO)) siointr1(com); /* fake interrupt to start output */ enable_intr(); ttwwakeup(tp); splx(s); } static void siostop(tp, rw) struct tty *tp; int rw; { struct com_s *com; com = com_addr(DEV_TO_UNIT(tp->t_dev)); if (com->gone) return; disable_intr(); if (rw & FWRITE) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_XMT_RST | com->fifo_image); com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) com_events -= LOTS_OF_EVENTS; com->state &= ~(CS_ODONE | CS_BUSY); com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ if (!com->esp) #endif /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_RCV_RST | com->fifo_image); com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } enable_intr(); comstart(tp); } static struct tty * siodevtotty(dev) dev_t dev; { int mynor; int unit; mynor = minor(dev); if (mynor & CONTROL_MASK) return (NULL); unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIOTOT) return (NULL); return (&sio_tty[unit]); } static int commctl(com, bits, how) struct com_s *com; int bits; int how; { int mcr; int msr; if (how == DMGET) { bits = TIOCM_LE; /* XXX - always enabled while open */ mcr = com->mcr_image; if (mcr & MCR_DTR) bits |= TIOCM_DTR; if (mcr & MCR_RTS) bits |= TIOCM_RTS; msr = com->prev_modem_status; if (msr & MSR_CTS) bits |= TIOCM_CTS; if (msr & MSR_DCD) bits |= TIOCM_CD; if (msr & MSR_DSR) bits |= TIOCM_DSR; /* * XXX - MSR_RI is naturally volatile, and we make MSR_TERI * more volatile by reading the modem status a lot. Perhaps * we should latch both bits until the status is read here. */ if (msr & (MSR_RI | MSR_TERI)) bits |= TIOCM_RI; return (bits); } mcr = 0; if (bits & TIOCM_DTR) mcr |= MCR_DTR; if (bits & TIOCM_RTS) mcr |= MCR_RTS; if (com->gone) return(0); disable_intr(); switch (how) { case DMSET: outb(com->modem_ctl_port, com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); break; case DMBIS: outb(com->modem_ctl_port, com->mcr_image |= mcr); break; case DMBIC: outb(com->modem_ctl_port, com->mcr_image &= ~mcr); break; } enable_intr(); return (0); } static void siosettimeout() { struct com_s *com; bool_t someopen; int unit; /* * Set our timeout period to 1 second if no polled devices are open. * Otherwise set it to max(1/200, 1/hz). * Enable timeouts iff some device is open. */ untimeout(comwakeup, (void *)NULL, sio_timeout_handle); sio_timeout = hz; someopen = FALSE; for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); if (com != NULL && com->tp != NULL && com->tp->t_state & TS_ISOPEN && !com->gone) { someopen = TRUE; if (com->poll || com->poll_output) { sio_timeout = hz > 200 ? hz / 200 : 1; break; } } } if (someopen) { sio_timeouts_until_log = hz / sio_timeout; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); } else { /* Flush error messages, if any. */ sio_timeouts_until_log = 1; comwakeup((void *)NULL); untimeout(comwakeup, (void *)NULL, sio_timeout_handle); } } static void comwakeup(chan) void *chan; { struct com_s *com; int unit; sio_timeout_handle = timeout(comwakeup, (void *)NULL, sio_timeout); /* * Recover from lost output interrupts. * Poll any lines that don't use interrupts. */ for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); if (com != NULL && !com->gone && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { disable_intr(); siointr1(com); enable_intr(); } } /* * Check for and log errors, but not too often. */ if (--sio_timeouts_until_log > 0) return; sio_timeouts_until_log = hz / sio_timeout; for (unit = 0; unit < NSIOTOT; ++unit) { int errnum; com = com_addr(unit); if (com == NULL) continue; if (com->gone) continue; for (errnum = 0; errnum < CE_NTYPES; ++errnum) { u_int delta; u_long total; disable_intr(); delta = com->delta_error_counts[errnum]; com->delta_error_counts[errnum] = 0; enable_intr(); if (delta == 0) continue; total = com->error_counts[errnum] += delta; log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } } } static void disc_optim(tp, t, com) struct tty *tp; struct termios *t; struct com_s *com; { if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) && (!(t->c_iflag & PARMRK) || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) && linesw[tp->t_line].l_rint == ttyinput) tp->t_state |= TS_CAN_BYPASS_L_RINT; else tp->t_state &= ~TS_CAN_BYPASS_L_RINT; com->hotchar = linesw[tp->t_line].l_hotchar; } /* * Following are all routines needed for SIO to act as console */ #include struct siocnstate { u_char dlbl; u_char dlbh; u_char ier; u_char cfcr; u_char mcr; }; static speed_t siocngetspeed __P((Port_t, struct speedtab *)); static void siocnclose __P((struct siocnstate *sp)); static void siocnopen __P((struct siocnstate *sp)); static void siocntxwait __P((void)); static void siocntxwait() { int timo; /* * Wait for any pending transmission to finish. Required to avoid * the UART lockup bug when the speed is changed, and for normal * transmits. */ timo = 100000; while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) != (LSR_TSRE | LSR_TXRDY) && --timo != 0) ; } /* * Read the serial port specified and try to figure out what speed * it's currently running at. We're assuming the serial port has * been initialized and is basicly idle. This routine is only intended * to be run at system startup. * * If the value read from the serial port doesn't make sense, return 0. */ static speed_t siocngetspeed(iobase, table) Port_t iobase; struct speedtab *table; { int code; u_char dlbh; u_char dlbl; u_char cfcr; cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | cfcr); dlbl = inb(iobase + com_dlbl); dlbh = inb(iobase + com_dlbh); outb(iobase + com_cfcr, cfcr); code = dlbh << 8 | dlbl; for ( ; table->sp_speed != -1; table++) if (table->sp_code == code) return (table->sp_speed); return 0; /* didn't match anything sane */ } static void siocnopen(sp) struct siocnstate *sp; { int divisor; u_char dlbh; u_char dlbl; Port_t iobase; /* * Save all the device control registers except the fifo register * and set our default ones (cs8 -parenb speed=comdefaultrate). * We can't save the fifo register since it is read-only. */ iobase = siocniobase; sp->ier = inb(iobase + com_ier); outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ siocntxwait(); sp->cfcr = inb(iobase + com_cfcr); outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); sp->dlbl = inb(iobase + com_dlbl); sp->dlbh = inb(iobase + com_dlbh); /* * Only set the divisor registers if they would change, since on * some 16550 incompatibles (Startech), setting them clears the * data input register. This also reduces the effects of the * UMC8669F bug. */ divisor = ttspeedtab(comdefaultrate, comspeedtab); dlbl = divisor & 0xFF; if (sp->dlbl != dlbl) outb(iobase + com_dlbl, dlbl); dlbh = (u_int) divisor >> 8; if (sp->dlbh != dlbh) outb(iobase + com_dlbh, dlbh); outb(iobase + com_cfcr, CFCR_8BITS); sp->mcr = inb(iobase + com_mcr); /* * We don't want interrupts, but must be careful not to "disable" * them by clearing the MCR_IENABLE bit, since that might cause * an interrupt by floating the IRQ line. */ outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); } static void siocnclose(sp) struct siocnstate *sp; { Port_t iobase; /* * Restore the device control registers. */ siocntxwait(); iobase = siocniobase; outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); if (sp->dlbl != inb(iobase + com_dlbl)) outb(iobase + com_dlbl, sp->dlbl); if (sp->dlbh != inb(iobase + com_dlbh)) outb(iobase + com_dlbh, sp->dlbh); outb(iobase + com_cfcr, sp->cfcr); /* * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. */ outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); outb(iobase + com_ier, sp->ier); } void siocnprobe(cp) struct consdev *cp; { speed_t boot_speed; u_char cfcr; struct isa_device *dvp; int s; struct siocnstate sp; /* * Find our first enabled console, if any. If it is a high-level * console device, then initialize it and return successfully. * If it is a low-level console device, then initialize it and * return unsuccessfully. It must be initialized in both cases * for early use by console drivers and debuggers. Initializing * the hardware is not necessary in all cases, since the i/o * routines initialize it on the fly, but it is necessary if * input might arrive while the hardware is switched back to an * uninitialized state. We can't handle multiple console devices * yet because our low-level routines don't take a device arg. * We trust the user to set the console flags properly so that we * don't need to probe. */ cp->cn_pri = CN_DEAD; for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++) if (dvp->id_driver == &siodriver && dvp->id_enabled && COM_CONSOLE(dvp)) { siocniobase = dvp->id_iobase; s = spltty(); if (boothowto & RB_SERIAL) { boot_speed = siocngetspeed(siocniobase, comspeedtab); if (boot_speed) comdefaultrate = boot_speed; } /* * Initialize the divisor latch. We can't rely on * siocnopen() to do this the first time, since it * avoids writing to the latch if the latch appears * to have the correct value. Also, if we didn't * just read the speed from the hardware, then we * need to set the speed in hardware so that * switching it later is null. */ cfcr = inb(siocniobase + com_cfcr); outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); outb(siocniobase + com_dlbl, COMBRD(comdefaultrate) & 0xff); outb(siocniobase + com_dlbh, (u_int) COMBRD(comdefaultrate) >> 8); outb(siocniobase + com_cfcr, cfcr); siocnopen(&sp); splx(s); if (!COM_LLCONSOLE(dvp)) { cp->cn_dev = makedev(CDEV_MAJOR, dvp->id_unit); cp->cn_pri = COM_FORCECONSOLE(dvp) || boothowto & RB_SERIAL ? CN_REMOTE : CN_NORMAL; } break; } } void siocninit(cp) struct consdev *cp; { comconsole = DEV_TO_UNIT(cp->cn_dev); } int siocncheckc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); if (inb(iobase + com_lsr) & LSR_RXRDY) c = inb(iobase + com_data); else c = -1; siocnclose(&sp); splx(s); return (c); } int siocngetc(dev) dev_t dev; { int c; Port_t iobase; int s; struct siocnstate sp; iobase = siocniobase; s = spltty(); siocnopen(&sp); while (!(inb(iobase + com_lsr) & LSR_RXRDY)) ; c = inb(iobase + com_data); siocnclose(&sp); splx(s); return (c); } void siocnputc(dev, c) dev_t dev; int c; { int s; struct siocnstate sp; s = spltty(); siocnopen(&sp); siocntxwait(); outb(siocniobase + com_data, c); siocnclose(&sp); splx(s); } #ifdef DSI_SOFT_MODEM /* * The magic code to download microcode to a "Connection 14.4+Fax" * modem from Digicom Systems Inc. Very magic. */ #define DSI_ERROR(str) { ptr = str; goto error; } static int LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr) { int int_c,int_k; int data_0188, data_0187; /* * First see if it is a DSI SoftModem */ if(!((inb(base_io+7) ^ inb(base_io+7)) & 0x80)) return ENODEV; data_0188 = inb(base_io+4); data_0187 = inb(base_io+3); outb(base_io+3,0x80); outb(base_io+4,0x0C); outb(base_io+0,0x31); outb(base_io+1,0x8C); outb(base_io+7,0x10); outb(base_io+7,0x19); if(0x18 != (inb(base_io+7) & 0x1A)) DSI_ERROR("dsp bus not granted"); if(0x01 != (inb(base_io+7) & 0x01)) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x01 != (inb(base_io+7) & 0x01)) DSI_ERROR("program mem not granted"); } int_c = 0; while(1) { if(int_c >= 7 || size <= 0x1800) break; for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; int_c++; } if(size > 0x1800) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < 0x800; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; while(size > 0x1800) { for(int_k = 0 ; int_k < 0xC00; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } size -= 0x1800; } if(size < 0x1800) { for(int_k=0;int_k 0) { if(int_c == 7) { outb(base_io+7,0x18); outb(base_io+7,0x19); if(0x00 != (inb(base_io+7) & 0x01)) DSI_ERROR("program data not granted"); for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+1,*ptr++); outb(base_io+2,0); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } else { for(int_k = 0 ; int_k < size/3; int_k++) { outb(base_io+0,*ptr++); outb(base_io+1,*ptr++); outb(base_io+2,*ptr++); } } } outb(base_io+7,0x11); outb(base_io+7,3); outb(base_io+4,data_0188 & 0xfb); outb(base_io+3,data_0187); return 0; error: printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",unit,ptr); outb(base_io+7,0x00); \ outb(base_io+3,data_0187); \ outb(base_io+4,data_0188); \ return EIO; } #endif /* DSI_SOFT_MODEM */ /* * support PnP cards if we are using 'em */ #if NPNP > 0 static struct siopnp_ids { u_long vend_id; char *id_str; } siopnp_ids[] = { { 0x5015f435, "MOT1550"}, { 0x8113b04e, "Supra1381"}, { 0x9012b04e, "Supra1290"}, { 0x11007256, "USR0011"}, { 0x30207256, "USR2030"}, { 0 } }; static char *siopnp_probe(u_long csn, u_long vend_id); static void siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev); static u_long nsiopnp = NSIO; static struct pnp_device siopnp = { "siopnp", siopnp_probe, siopnp_attach, &nsiopnp, &tty_imask }; DATA_SET (pnpdevice_set, siopnp); static char * siopnp_probe(u_long csn, u_long vend_id) { struct siopnp_ids *ids; char *s = NULL; for(ids = siopnp_ids; ids->vend_id != 0; ids++) { if (vend_id == ids->vend_id) { s = ids->id_str; break; } } if (s) { struct pnp_cinfo d; read_pnp_parms(&d, 0); if (d.enable == 0 || d.flags & 1) { printf("CSN %d is disabled.\n", csn); return (NULL); } } return (s); } static void siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) { struct pnp_cinfo d; struct isa_device *dvp; if (dev->id_unit >= NSIOTOT) return; if (read_pnp_parms(&d, 0) == 0) { printf("failed to read pnp parms\n"); return; } write_pnp_parms(&d, 0); enable_pnp_card(); dev->id_iobase = d.port[0]; dev->id_irq = (1 << d.irq[0]); dev->id_intr = siointr; dev->id_ri_flags = RI_FAST; dev->id_drq = -1; if (dev->id_driver == NULL) { dev->id_driver = &siodriver; dvp = find_isadev(isa_devtab_tty, &siodriver, 0); if (dvp != NULL) dev->id_id = dvp->id_id; } if ((dev->id_alive = sioprobe(dev)) != 0) sioattach(dev); else printf("sio%d: probe failed\n", dev->id_unit); } #endif diff --git a/sys/kern/tty_cons.c b/sys/kern/tty_cons.c index fceb4dd92449..1482c6185557 100644 --- a/sys/kern/tty_cons.c +++ b/sys/kern/tty_cons.c @@ -1,446 +1,449 @@ /* * Copyright (c) 1988 University of Utah. * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)cons.c 7.2 (Berkeley) 5/9/91 - * $Id: cons.c,v 1.57 1998/03/28 10:32:56 bde Exp $ + * $Id: cons.c,v 1.58 1998/06/07 17:09:58 dfr Exp $ */ #include "opt_devfs.h" #include #ifdef DEVFS #include #endif /*DEVFS*/ #include #include #include #include #include #include #include #include #include #include /* XXX this should be config(8)ed. */ #include "sc.h" #include "vt.h" #include "sio.h" static struct consdev constab[] = { #if NSC > 0 { sccnprobe, sccninit, sccngetc, sccncheckc, sccnputc }, #endif #if NVT > 0 { pccnprobe, pccninit, pccngetc, pccncheckc, pccnputc }, #endif #if NSIO > 0 { siocnprobe, siocninit, siocngetc, siocncheckc, siocnputc }, #endif { 0 }, }; static d_open_t cnopen; static d_close_t cnclose; static d_read_t cnread; static d_write_t cnwrite; static d_ioctl_t cnioctl; static d_poll_t cnpoll; -#define CDEV_MAJOR 0 -static struct cdevsw cn_cdevsw = - { cnopen, cnclose, cnread, cnwrite, /*0*/ - cnioctl, nullstop, nullreset, nodevtotty,/* console */ - cnpoll, nommap, NULL, "console", NULL, -1 }; +#define CDEV_MAJOR 0 +static struct cdevsw cn_cdevsw = { + cnopen, cnclose, cnread, cnwrite, + cnioctl, nullstop, nullreset, nodevtotty, + cnpoll, nommap, NULL, "console", + NULL, -1, nodump, nopsize, + D_TTY, +}; static dev_t cn_dev_t; /* seems to be never really used */ SYSCTL_OPAQUE(_machdep, CPU_CONSDEV, consdev, CTLTYPE_OPAQUE|CTLFLAG_RD, &cn_dev_t, sizeof cn_dev_t, "T,dev_t", ""); static int cn_mute; int cons_unavail = 0; /* XXX: * physical console not available for * input (i.e., it is in graphics mode) */ static u_char cn_is_open; /* nonzero if logical console is open */ static int openmode, openflag; /* how /dev/console was openned */ static u_char cn_phys_is_open; /* nonzero if physical device is open */ static d_close_t *cn_phys_close; /* physical device close function */ static d_open_t *cn_phys_open; /* physical device open function */ static struct consdev *cn_tab; /* physical console device info */ static struct tty *cn_tp; /* physical console tty struct */ #ifdef DEVFS static void *cn_devfs_token; /* represents the devfs entry */ #endif /* DEVFS */ void cninit() { struct consdev *best_cp, *cp; /* * Find the first console with the highest priority. */ best_cp = NULL; for (cp = constab; cp->cn_probe; cp++) { (*cp->cn_probe)(cp); if (cp->cn_pri > CN_DEAD && (best_cp == NULL || cp->cn_pri > best_cp->cn_pri)) best_cp = cp; } /* * Check if we should mute the console (for security reasons perhaps) * It can be changes dynamically using sysctl kern.consmute * once we are up and going. * */ cn_mute = ((boothowto & (RB_MUTE |RB_SINGLE |RB_VERBOSE |RB_ASKNAME |RB_CONFIG)) == RB_MUTE); /* * If no console, give up. */ if (best_cp == NULL) { cn_tab = best_cp; return; } /* * Initialize console, then attach to it. This ordering allows * debugging using the previous console, if any. * XXX if there was a previous console, then its driver should * be informed when we forget about it. */ (*best_cp->cn_init)(best_cp); cn_tab = best_cp; } void cninit_finish() { struct cdevsw *cdp; if ((cn_tab == NULL) || cn_mute) return; /* * Hook the open and close functions. */ cdp = cdevsw[major(cn_tab->cn_dev)]; cn_phys_close = cdp->d_close; cdp->d_close = cnclose; cn_phys_open = cdp->d_open; cdp->d_open = cnopen; cn_tp = (*cdp->d_devtotty)(cn_tab->cn_dev); cn_dev_t = cn_tp->t_dev; } static void cnuninit(void) { struct cdevsw *cdp; if (cn_tab == NULL) return; /* * Unhook the open and close functions. */ cdp = cdevsw[major(cn_tab->cn_dev)]; cdp->d_close = cn_phys_close; cn_phys_close = NULL; cdp->d_open = cn_phys_open; cn_phys_open = NULL; cn_tp = NULL; cn_dev_t = 0; } /* * User has changed the state of the console muting. * This may require us to open or close the device in question. */ static int sysctl_kern_consmute SYSCTL_HANDLER_ARGS { int error; int ocn_mute; ocn_mute = cn_mute; error = sysctl_handle_int(oidp, &cn_mute, 0, req); if((error == 0) && (cn_tab != NULL) && (req->newptr != NULL)) { if(ocn_mute && !cn_mute) { /* * going from muted to unmuted.. open the physical dev * if the console has been openned */ cninit_finish(); if(cn_is_open) /* XXX curproc is not what we want really */ error = cnopen(cn_dev_t, openflag, openmode, curproc); /* if it failed, back it out */ if ( error != 0) cnuninit(); } else if (!ocn_mute && cn_mute) { /* * going from unmuted to muted.. close the physical dev * if it's only open via /dev/console */ if(cn_is_open) error = cnclose(cn_dev_t, openflag, openmode, curproc); if ( error == 0) cnuninit(); } if (error != 0) { /* * back out the change if there was an error */ cn_mute = ocn_mute; } } return (error); } SYSCTL_PROC(_kern, OID_AUTO, consmute, CTLTYPE_INT|CTLFLAG_RW, 0, sizeof cn_mute, sysctl_kern_consmute, "I", ""); static int cnopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { dev_t cndev, physdev; int retval = 0; if (cn_tab == NULL) return (0); cndev = cn_tab->cn_dev; physdev = (major(dev) == major(cndev) ? dev : cndev); /* * If mute is active, then non console opens don't get here * so we don't need to check for that. They * bypass this and go straight to the device. */ if(!cn_mute) retval = (*cn_phys_open)(physdev, flag, mode, p); if (retval == 0) { /* * check if we openned it via /dev/console or * via the physical entry (e.g. /dev/sio0). */ if (dev == cndev) cn_phys_is_open = 1; else if (physdev == cndev) { openmode = mode; openflag = flag; cn_is_open = 1; } } return (retval); } static int cnclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { dev_t cndev; if (cn_tab == NULL) return (0); cndev = cn_tab->cn_dev; /* * act appropriatly depending on whether it's /dev/console * or the pysical device (e.g. /dev/sio) that's being closed. * in either case, don't actually close the device unless * both are closed. */ if (dev == cndev) { /* the physical device is about to be closed */ cn_phys_is_open = 0; if (cn_is_open) { if (cn_tp) { /* perform a ttyhalfclose() */ /* reset session and proc group */ cn_tp->t_pgrp = NULL; cn_tp->t_session = NULL; } return (0); } } else if (major(dev) != major(cndev)) { /* the logical console is about to be closed */ cn_is_open = 0; if (cn_phys_is_open) return (0); dev = cndev; } if(cn_phys_close) return ((*cn_phys_close)(dev, flag, mode, p)); return (0); } static int cnread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { if ((cn_tab == NULL) || cn_mute) return (0); dev = cn_tab->cn_dev; return ((*cdevsw[major(dev)]->d_read)(dev, uio, flag)); } static int cnwrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { if ((cn_tab == NULL) || cn_mute) { uio->uio_resid = 0; /* dump the data */ return (0); } if (constty) dev = constty->t_dev; else dev = cn_tab->cn_dev; return ((*cdevsw[major(dev)]->d_write)(dev, uio, flag)); } static int cnioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { int error; if ((cn_tab == NULL) || cn_mute) return (0); /* * Superuser can always use this to wrest control of console * output from the "virtual" console. */ if (cmd == TIOCCONS && constty) { error = suser(p->p_ucred, (u_short *) NULL); if (error) return (error); constty = NULL; return (0); } dev = cn_tab->cn_dev; return ((*cdevsw[major(dev)]->d_ioctl)(dev, cmd, data, flag, p)); } static int cnpoll(dev, events, p) dev_t dev; int events; struct proc *p; { if ((cn_tab == NULL) || cn_mute) return (1); dev = cn_tab->cn_dev; return ((*cdevsw[major(dev)]->d_poll)(dev, events, p)); } int cngetc() { int c; if ((cn_tab == NULL) || cn_mute) return (-1); c = (*cn_tab->cn_getc)(cn_tab->cn_dev); if (c == '\r') c = '\n'; /* console input is always ICRNL */ return (c); } int cncheckc() { if ((cn_tab == NULL) || cn_mute) return (-1); return ((*cn_tab->cn_checkc)(cn_tab->cn_dev)); } void cnputc(c) register int c; { if ((cn_tab == NULL) || cn_mute) return; if (c) { if (c == '\n') (*cn_tab->cn_putc)(cn_tab->cn_dev, '\r'); (*cn_tab->cn_putc)(cn_tab->cn_dev, c); } } static cn_devsw_installed = 0; static void cn_drvinit(void *unused) { dev_t dev; if( ! cn_devsw_installed ) { dev = makedev(CDEV_MAJOR,0); cdevsw_add(&dev,&cn_cdevsw,NULL); cn_devsw_installed = 1; #ifdef DEVFS cn_devfs_token = devfs_add_devswf(&cn_cdevsw, 0, DV_CHR, UID_ROOT, GID_WHEEL, 0600, "console"); #endif } } SYSINIT(cndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,cn_drvinit,NULL) diff --git a/sys/kern/tty_pty.c b/sys/kern/tty_pty.c index 1f430e253ec0..214f103bb345 100644 --- a/sys/kern/tty_pty.c +++ b/sys/kern/tty_pty.c @@ -1,828 +1,832 @@ /* * Copyright (c) 1982, 1986, 1989, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tty_pty.c 8.4 (Berkeley) 2/20/95 - * $Id: tty_pty.c,v 1.52 1998/06/07 17:11:43 dfr Exp $ + * $Id: tty_pty.c,v 1.53 1998/07/15 12:18:30 bde Exp $ */ /* * Pseudo-teletype Driver * (Actually two drivers, requiring two entries in 'cdevsw') */ #include "pty.h" /* XXX */ #include "opt_compat.h" #include "opt_devfs.h" #include #include #if defined(COMPAT_43) || defined(COMPAT_SUNOS) #include #endif #include #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ #ifdef notyet static void ptyattach __P((int n)); #endif static void ptsstart __P((struct tty *tp)); static void ptcwakeup __P((struct tty *tp, int flag)); static d_open_t ptsopen; static d_close_t ptsclose; static d_read_t ptsread; static d_write_t ptswrite; static d_ioctl_t ptyioctl; static d_stop_t ptsstop; static d_devtotty_t ptydevtotty; static d_open_t ptcopen; static d_close_t ptcclose; static d_read_t ptcread; static d_write_t ptcwrite; static d_poll_t ptcpoll; -#define CDEV_MAJOR_S 5 -#define CDEV_MAJOR_C 6 -static struct cdevsw pts_cdevsw = - { ptsopen, ptsclose, ptsread, ptswrite, /*5*/ - ptyioctl, ptsstop, nullreset, ptydevtotty,/* ttyp */ - ttpoll, nommap, NULL, "pts", NULL, -1 }; - -static struct cdevsw ptc_cdevsw = - { ptcopen, ptcclose, ptcread, ptcwrite, /*6*/ - ptyioctl, nullstop, nullreset, ptydevtotty,/* ptyp */ - ptcpoll, nommap, NULL, "ptc", NULL, -1 }; - +#define CDEV_MAJOR_S 5 +static struct cdevsw pts_cdevsw = { + ptsopen, ptsclose, ptsread, ptswrite, + ptyioctl, ptsstop, nullreset, ptydevtotty, + ttpoll, nommap, NULL, "pts", + NULL, -1, nodump, nopsize, + D_TTY, +}; + +#define CDEV_MAJOR_C 6 +static struct cdevsw ptc_cdevsw = { + ptcopen, ptcclose, ptcread, ptcwrite, + ptyioctl, nullstop, nullreset, ptydevtotty, + ptcpoll, nommap, NULL, "ptc", + NULL, -1, nodump, nopsize, + D_TTY, +}; #if NPTY == 1 #undef NPTY #define NPTY 32 /* crude XXX */ #warning You have only one pty defined, redefining to 32. #endif #ifdef DEVFS #define MAXUNITS (8 * 32) static void *devfs_token_pts[MAXUNITS]; static void *devfs_token_ptc[MAXUNITS]; static const char jnames[] = "pqrsPQRS"; #if NPTY > MAXUNITS #undef NPTY #define NPTY MAXUNITS #warning Can't have more than 256 pty's with DEVFS defined. #endif #endif #define BUFSIZ 100 /* Chunk size iomoved to/from user */ /* * pts == /dev/tty[pqrsPQRS][0123456789abcdefghijklmnopqrstuv] * ptc == /dev/pty[pqrsPQRS][0123456789abcdefghijklmnopqrstuv] */ static struct tty pt_tty[NPTY]; /* XXX */ static struct pt_ioctl { int pt_flags; struct selinfo pt_selr, pt_selw; u_char pt_send; u_char pt_ucntl; } pt_ioctl[NPTY]; /* XXX */ static int npty = NPTY; /* for pstat -t */ #define PF_PKT 0x08 /* packet mode */ #define PF_STOPPED 0x10 /* user told stopped */ #define PF_REMOTE 0x20 /* remote and flow controlled input */ #define PF_NOSTOP 0x40 #define PF_UCNTL 0x80 /* user control mode */ #ifdef notyet /* * Establish n (or default if n is 1) ptys in the system. * * XXX cdevsw & pstat require the array `pty[]' to be an array */ static void ptyattach(n) int n; { char *mem; register u_long ntb; #define DEFAULT_NPTY 32 /* maybe should allow 0 => none? */ if (n <= 1) n = DEFAULT_NPTY; ntb = n * sizeof(struct tty); mem = malloc(ntb + ALIGNBYTES + n * sizeof(struct pt_ioctl), M_DEVBUF, M_WAITOK); pt_tty = (struct tty *)mem; mem = (char *)ALIGN(mem + ntb); pt_ioctl = (struct pt_ioctl *)mem; npty = n; } #endif /*ARGSUSED*/ static int ptsopen(dev, flag, devtype, p) dev_t dev; int flag, devtype; struct proc *p; { register struct tty *tp; int error; if (minor(dev) >= npty) return (ENXIO); tp = &pt_tty[minor(dev)]; if ((tp->t_state & TS_ISOPEN) == 0) { ttychars(tp); /* Set up default chars */ tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; tp->t_lflag = TTYDEF_LFLAG; tp->t_cflag = TTYDEF_CFLAG; tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; - ttsetwater(tp); /* would be done in xxparam() */ } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0) return (EBUSY); if (tp->t_oproc) /* Ctrlr still around. */ (void)(*linesw[tp->t_line].l_modem)(tp, 1); while ((tp->t_state & TS_CARR_ON) == 0) { if (flag&FNONBLOCK) break; error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, "ptsopn", 0); if (error) return (error); } error = (*linesw[tp->t_line].l_open)(dev, tp); if (error == 0) ptcwakeup(tp, FREAD|FWRITE); return (error); } static int ptsclose(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { register struct tty *tp; int err; tp = &pt_tty[minor(dev)]; err = (*linesw[tp->t_line].l_close)(tp, flag); ptsstop(tp, FREAD|FWRITE); (void) ttyclose(tp); return (err); } static int ptsread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct proc *p = curproc; register struct tty *tp = &pt_tty[minor(dev)]; register struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; int error = 0; again: if (pti->pt_flags & PF_REMOTE) { while (isbackground(p, tp)) { if ((p->p_sigignore & sigmask(SIGTTIN)) || (p->p_sigmask & sigmask(SIGTTIN)) || p->p_pgrp->pg_jobc == 0 || p->p_flag & P_PPWAIT) return (EIO); pgsignal(p->p_pgrp, SIGTTIN, 1); error = ttysleep(tp, &lbolt, TTIPRI | PCATCH, "ptsbg", 0); if (error) return (error); } if (tp->t_canq.c_cc == 0) { if (flag & IO_NDELAY) return (EWOULDBLOCK); error = ttysleep(tp, TSA_PTS_READ(tp), TTIPRI | PCATCH, "ptsin", 0); if (error) return (error); goto again; } while (tp->t_canq.c_cc > 1 && uio->uio_resid > 0) if (ureadc(getc(&tp->t_canq), uio) < 0) { error = EFAULT; break; } if (tp->t_canq.c_cc == 1) (void) getc(&tp->t_canq); if (tp->t_canq.c_cc) return (error); } else if (tp->t_oproc) error = (*linesw[tp->t_line].l_read)(tp, uio, flag); ptcwakeup(tp, FWRITE); return (error); } /* * Write to pseudo-tty. * Wakeups of controlling tty will happen * indirectly, when tty driver calls ptsstart. */ static int ptswrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { register struct tty *tp; tp = &pt_tty[minor(dev)]; if (tp->t_oproc == 0) return (EIO); return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); } /* * Start output on pseudo-tty. * Wake up process selecting or sleeping for input from controlling tty. */ static void ptsstart(tp) struct tty *tp; { register struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)]; if (tp->t_state & TS_TTSTOP) return; if (pti->pt_flags & PF_STOPPED) { pti->pt_flags &= ~PF_STOPPED; pti->pt_send = TIOCPKT_START; } ptcwakeup(tp, FREAD); } static void ptcwakeup(tp, flag) struct tty *tp; int flag; { struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)]; if (flag & FREAD) { selwakeup(&pti->pt_selr); wakeup(TSA_PTC_READ(tp)); } if (flag & FWRITE) { selwakeup(&pti->pt_selw); wakeup(TSA_PTC_WRITE(tp)); } } static int ptcopen(dev, flag, devtype, p) dev_t dev; int flag, devtype; struct proc *p; { register struct tty *tp; struct pt_ioctl *pti; if (minor(dev) >= npty) return (ENXIO); tp = &pt_tty[minor(dev)]; if (tp->t_oproc) return (EIO); tp->t_oproc = ptsstart; #ifdef sun4c tp->t_stop = ptsstop; #endif (void)(*linesw[tp->t_line].l_modem)(tp, 1); tp->t_lflag &= ~EXTPROC; pti = &pt_ioctl[minor(dev)]; pti->pt_flags = 0; pti->pt_send = 0; pti->pt_ucntl = 0; return (0); } static int ptcclose(dev, flags, fmt, p) dev_t dev; int flags; int fmt; struct proc *p; { register struct tty *tp; tp = &pt_tty[minor(dev)]; (void)(*linesw[tp->t_line].l_modem)(tp, 0); /* * XXX MDMBUF makes no sense for ptys but would inhibit the above * l_modem(). CLOCAL makes sense but isn't supported. Special * l_modem()s that ignore carrier drop make no sense for ptys but * may be in use because other parts of the line discipline make * sense for ptys. Recover by doing everything that a normal * ttymodem() would have done except for sending a SIGHUP. */ if (tp->t_state & TS_ISOPEN) { tp->t_state &= ~(TS_CARR_ON | TS_CONNECTED); tp->t_state |= TS_ZOMBIE; ttyflush(tp, FREAD | FWRITE); } tp->t_oproc = 0; /* mark closed */ return (0); } static int ptcread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { register struct tty *tp = &pt_tty[minor(dev)]; struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; char buf[BUFSIZ]; int error = 0, cc; /* * We want to block until the slave * is open, and there's something to read; * but if we lost the slave or we're NBIO, * then return the appropriate error instead. */ for (;;) { if (tp->t_state&TS_ISOPEN) { if (pti->pt_flags&PF_PKT && pti->pt_send) { error = ureadc((int)pti->pt_send, uio); if (error) return (error); if (pti->pt_send & TIOCPKT_IOCTL) { cc = min(uio->uio_resid, sizeof(tp->t_termios)); uiomove((caddr_t)&tp->t_termios, cc, uio); } pti->pt_send = 0; return (0); } if (pti->pt_flags&PF_UCNTL && pti->pt_ucntl) { error = ureadc((int)pti->pt_ucntl, uio); if (error) return (error); pti->pt_ucntl = 0; return (0); } if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0) break; } if ((tp->t_state & TS_CONNECTED) == 0) return (0); /* EOF */ if (flag & IO_NDELAY) return (EWOULDBLOCK); error = tsleep(TSA_PTC_READ(tp), TTIPRI | PCATCH, "ptcin", 0); if (error) return (error); } if (pti->pt_flags & (PF_PKT|PF_UCNTL)) error = ureadc(0, uio); while (uio->uio_resid > 0 && error == 0) { cc = q_to_b(&tp->t_outq, buf, min(uio->uio_resid, BUFSIZ)); if (cc <= 0) break; error = uiomove(buf, cc, uio); } ttwwakeup(tp); return (error); } static void ptsstop(tp, flush) register struct tty *tp; int flush; { struct pt_ioctl *pti = &pt_ioctl[minor(tp->t_dev)]; int flag; /* note: FLUSHREAD and FLUSHWRITE already ok */ if (flush == 0) { flush = TIOCPKT_STOP; pti->pt_flags |= PF_STOPPED; } else pti->pt_flags &= ~PF_STOPPED; pti->pt_send |= flush; /* change of perspective */ flag = 0; if (flush & FREAD) flag |= FWRITE; if (flush & FWRITE) flag |= FREAD; ptcwakeup(tp, flag); } static int ptcpoll(dev, events, p) dev_t dev; int events; struct proc *p; { register struct tty *tp = &pt_tty[minor(dev)]; struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; int revents = 0; int s; if ((tp->t_state & TS_CONNECTED) == 0) return (seltrue(dev, events, p) | POLLHUP); /* * Need to block timeouts (ttrstart). */ s = spltty(); if (events & (POLLIN | POLLRDNORM)) if ((tp->t_state & TS_ISOPEN) && ((tp->t_outq.c_cc && (tp->t_state & TS_TTSTOP) == 0) || ((pti->pt_flags & PF_PKT) && pti->pt_send) || ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl))) revents |= events & (POLLIN | POLLRDNORM); if (events & (POLLOUT | POLLWRNORM)) if (tp->t_state & TS_ISOPEN && ((pti->pt_flags & PF_REMOTE) ? (tp->t_canq.c_cc == 0) : ((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG - 2) || (tp->t_canq.c_cc == 0 && (tp->t_iflag & ICANON))))) revents |= events & (POLLOUT | POLLWRNORM); if (events & POLLHUP) if ((tp->t_state & TS_CARR_ON) == 0) revents |= POLLHUP; if (revents == 0) { if (events & (POLLIN | POLLRDNORM)) selrecord(p, &pti->pt_selr); if (events & (POLLOUT | POLLWRNORM)) selrecord(p, &pti->pt_selw); } splx(s); return (revents); } static int ptcwrite(dev, uio, flag) dev_t dev; register struct uio *uio; int flag; { register struct tty *tp = &pt_tty[minor(dev)]; register u_char *cp = 0; register int cc = 0; u_char locbuf[BUFSIZ]; int cnt = 0; struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; int error = 0; again: if ((tp->t_state&TS_ISOPEN) == 0) goto block; if (pti->pt_flags & PF_REMOTE) { if (tp->t_canq.c_cc) goto block; while ((uio->uio_resid > 0 || cc > 0) && tp->t_canq.c_cc < TTYHOG - 1) { if (cc == 0) { cc = min(uio->uio_resid, BUFSIZ); cc = min(cc, TTYHOG - 1 - tp->t_canq.c_cc); cp = locbuf; error = uiomove((caddr_t)cp, cc, uio); if (error) return (error); /* check again for safety */ if ((tp->t_state & TS_ISOPEN) == 0) { /* adjust as usual */ uio->uio_resid += cc; return (EIO); } } if (cc > 0) { cc = b_to_q((char *)cp, cc, &tp->t_canq); /* * XXX we don't guarantee that the canq size * is >= TTYHOG, so the above b_to_q() may * leave some bytes uncopied. However, space * is guaranteed for the null terminator if * we don't fail here since (TTYHOG - 1) is * not a multiple of CBSIZE. */ if (cc > 0) break; } } /* adjust for data copied in but not written */ uio->uio_resid += cc; (void) putc(0, &tp->t_canq); ttwakeup(tp); wakeup(TSA_PTS_READ(tp)); return (0); } while (uio->uio_resid > 0 || cc > 0) { if (cc == 0) { cc = min(uio->uio_resid, BUFSIZ); cp = locbuf; error = uiomove((caddr_t)cp, cc, uio); if (error) return (error); /* check again for safety */ if ((tp->t_state & TS_ISOPEN) == 0) { /* adjust for data copied in but not written */ uio->uio_resid += cc; return (EIO); } } while (cc > 0) { if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 && (tp->t_canq.c_cc > 0 || !(tp->t_iflag&ICANON))) { wakeup(TSA_HUP_OR_INPUT(tp)); goto block; } (*linesw[tp->t_line].l_rint)(*cp++, tp); cnt++; cc--; } cc = 0; } return (0); block: /* * Come here to wait for slave to open, for space * in outq, or space in rawq, or an empty canq. */ if ((tp->t_state & TS_CONNECTED) == 0) { /* adjust for data copied in but not written */ uio->uio_resid += cc; return (EIO); } if (flag & IO_NDELAY) { /* adjust for data copied in but not written */ uio->uio_resid += cc; if (cnt == 0) return (EWOULDBLOCK); return (0); } error = tsleep(TSA_PTC_WRITE(tp), TTOPRI | PCATCH, "ptcout", 0); if (error) { /* adjust for data copied in but not written */ uio->uio_resid += cc; return (error); } goto again; } static struct tty * ptydevtotty(dev) dev_t dev; { if (minor(dev) >= npty) return (NULL); return &pt_tty[minor(dev)]; } /*ARGSUSED*/ static int ptyioctl(dev, cmd, data, flag, p) dev_t dev; u_long cmd; caddr_t data; int flag; struct proc *p; { register struct tty *tp = &pt_tty[minor(dev)]; register struct pt_ioctl *pti = &pt_ioctl[minor(dev)]; register u_char *cc = tp->t_cc; int stop, error; /* * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG. * ttywflush(tp) will hang if there are characters in the outq. */ if (cmd == TIOCEXT) { /* * When the EXTPROC bit is being toggled, we need * to send an TIOCPKT_IOCTL if the packet driver * is turned on. */ if (*(int *)data) { if (pti->pt_flags & PF_PKT) { pti->pt_send |= TIOCPKT_IOCTL; ptcwakeup(tp, FREAD); } tp->t_lflag |= EXTPROC; } else { if ((tp->t_lflag & EXTPROC) && (pti->pt_flags & PF_PKT)) { pti->pt_send |= TIOCPKT_IOCTL; ptcwakeup(tp, FREAD); } tp->t_lflag &= ~EXTPROC; } return(0); } else if (cdevsw[major(dev)]->d_open == ptcopen) switch (cmd) { case TIOCGPGRP: /* * We avoid calling ttioctl on the controller since, * in that case, tp must be the controlling terminal. */ *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0; return (0); case TIOCPKT: if (*(int *)data) { if (pti->pt_flags & PF_UCNTL) return (EINVAL); pti->pt_flags |= PF_PKT; } else pti->pt_flags &= ~PF_PKT; return (0); case TIOCUCNTL: if (*(int *)data) { if (pti->pt_flags & PF_PKT) return (EINVAL); pti->pt_flags |= PF_UCNTL; } else pti->pt_flags &= ~PF_UCNTL; return (0); case TIOCREMOTE: if (*(int *)data) pti->pt_flags |= PF_REMOTE; else pti->pt_flags &= ~PF_REMOTE; ttyflush(tp, FREAD|FWRITE); return (0); #ifdef COMPAT_43 case TIOCSETP: case TIOCSETN: #endif case TIOCSETD: case TIOCSETA: case TIOCSETAW: case TIOCSETAF: ndflush(&tp->t_outq, tp->t_outq.c_cc); break; case TIOCSIG: if (*(unsigned int *)data >= NSIG || *(unsigned int *)data == 0) return(EINVAL); if ((tp->t_lflag&NOFLSH) == 0) ttyflush(tp, FREAD|FWRITE); pgsignal(tp->t_pgrp, *(unsigned int *)data, 1); if ((*(unsigned int *)data == SIGINFO) && ((tp->t_lflag&NOKERNINFO) == 0)) ttyinfo(tp); return(0); } error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); if (error == ENOIOCTL) error = ttioctl(tp, cmd, data, flag); if (error == ENOIOCTL) { if (pti->pt_flags & PF_UCNTL && (cmd & ~0xff) == UIOCCMD(0)) { if (cmd & 0xff) { pti->pt_ucntl = (u_char)cmd; ptcwakeup(tp, FREAD); } return (0); } error = ENOTTY; } /* * If external processing and packet mode send ioctl packet. */ if ((tp->t_lflag&EXTPROC) && (pti->pt_flags & PF_PKT)) { switch(cmd) { case TIOCSETA: case TIOCSETAW: case TIOCSETAF: #ifdef COMPAT_43 case TIOCSETP: case TIOCSETN: #endif #if defined(COMPAT_43) || defined(COMPAT_SUNOS) case TIOCSETC: case TIOCSLTC: case TIOCLBIS: case TIOCLBIC: case TIOCLSET: #endif pti->pt_send |= TIOCPKT_IOCTL; ptcwakeup(tp, FREAD); default: break; } } stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s')) && CCEQ(cc[VSTART], CTRL('q')); if (pti->pt_flags & PF_NOSTOP) { if (stop) { pti->pt_send &= ~TIOCPKT_NOSTOP; pti->pt_send |= TIOCPKT_DOSTOP; pti->pt_flags &= ~PF_NOSTOP; ptcwakeup(tp, FREAD); } } else { if (!stop) { pti->pt_send &= ~TIOCPKT_DOSTOP; pti->pt_send |= TIOCPKT_NOSTOP; pti->pt_flags |= PF_NOSTOP; ptcwakeup(tp, FREAD); } } return (error); } static int ptc_devsw_installed; static void ptc_drvinit __P((void *unused)); static void ptc_drvinit(unused) void *unused; { #ifdef DEVFS int i,j,k; #endif dev_t dev; if( ! ptc_devsw_installed ) { dev = makedev(CDEV_MAJOR_S, 0); cdevsw_add(&dev, &pts_cdevsw, NULL); dev = makedev(CDEV_MAJOR_C, 0); cdevsw_add(&dev, &ptc_cdevsw, NULL); ptc_devsw_installed = 1; #ifdef DEVFS for ( i = 0 ; i #include #include #include #include #include #include #include #ifdef DEVFS #include #endif /*DEVFS*/ static d_open_t cttyopen; static d_read_t cttyread; static d_write_t cttywrite; static d_ioctl_t cttyioctl; static d_poll_t cttypoll; -#define CDEV_MAJOR 1 -/* Don't make static, fdesc_vnops uses this. */ -struct cdevsw ctty_cdevsw = - { cttyopen, nullclose, cttyread, cttywrite, /*1*/ - cttyioctl, nullstop, nullreset, nodevtotty,/* tty */ - cttypoll, nommap, NULL, "ctty", NULL, -1 }; - +#define CDEV_MAJOR 1 +/* Don't make this static, since fdesc_vnops uses it. */ +struct cdevsw ctty_cdevsw = { + cttyopen, nullclose, cttyread, cttywrite, + cttyioctl, nullstop, nullreset, nodevtotty, + cttypoll, nommap, NULL, "ctty", + NULL, -1, nodump, nopsize, + D_TTY, +}; #define cttyvp(p) ((p)->p_flag & P_CONTROLT ? (p)->p_session->s_ttyvp : NULL) /*ARGSUSED*/ static int cttyopen(dev, flag, mode, p) dev_t dev; int flag, mode; struct proc *p; { struct vnode *ttyvp = cttyvp(p); int error; if (ttyvp == NULL) return (ENXIO); vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY, p); #ifdef PARANOID /* * Since group is tty and mode is 620 on most terminal lines * and since sessions protect terminals from processes outside * your session, this check is probably no longer necessary. * Since it inhibits setuid root programs that later switch * to another user from accessing /dev/tty, we have decided * to delete this test. (mckusick 5/93) */ error = VOP_ACCESS(ttyvp, (flag&FREAD ? VREAD : 0) | (flag&FWRITE ? VWRITE : 0), p->p_ucred, p); if (!error) #endif /* PARANOID */ error = VOP_OPEN(ttyvp, flag, NOCRED, p); VOP_UNLOCK(ttyvp, 0, p); return (error); } /*ARGSUSED*/ static int cttyread(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct proc *p = uio->uio_procp; register struct vnode *ttyvp = cttyvp(p); int error; if (ttyvp == NULL) return (EIO); vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_READ(ttyvp, uio, flag, NOCRED); VOP_UNLOCK(ttyvp, 0, p); return (error); } /*ARGSUSED*/ static int cttywrite(dev, uio, flag) dev_t dev; struct uio *uio; int flag; { struct proc *p = uio->uio_procp; struct vnode *ttyvp = cttyvp(uio->uio_procp); int error; if (ttyvp == NULL) return (EIO); vn_lock(ttyvp, LK_EXCLUSIVE | LK_RETRY, p); error = VOP_WRITE(ttyvp, uio, flag, NOCRED); VOP_UNLOCK(ttyvp, 0, p); return (error); } /*ARGSUSED*/ static int cttyioctl(dev, cmd, addr, flag, p) dev_t dev; u_long cmd; caddr_t addr; int flag; struct proc *p; { struct vnode *ttyvp = cttyvp(p); if (ttyvp == NULL) return (EIO); if (cmd == TIOCSCTTY) /* don't allow controlling tty to be set */ return EINVAL; /* to controlling tty -- infinite recursion */ if (cmd == TIOCNOTTY) { if (!SESS_LEADER(p)) { p->p_flag &= ~P_CONTROLT; return (0); } else return (EINVAL); } return (VOP_IOCTL(ttyvp, cmd, addr, flag, NOCRED, p)); } /*ARGSUSED*/ static int cttypoll(dev, events, p) dev_t dev; int events; struct proc *p; { struct vnode *ttyvp = cttyvp(p); if (ttyvp == NULL) /* try operation to get EOF/failure */ return (seltrue(dev, events, p)); return (VOP_POLL(ttyvp, events, p->p_ucred, p)); } static int ctty_devsw_installed; #ifdef DEVFS static void *ctty_devfs_token; #endif static void ctty_drvinit __P((void *unused)); static void ctty_drvinit(unused) void *unused; { dev_t dev; if( ! ctty_devsw_installed ) { dev = makedev(CDEV_MAJOR,0); cdevsw_add(&dev,&ctty_cdevsw,NULL); ctty_devsw_installed = 1; #ifdef DEVFS ctty_devfs_token = devfs_add_devswf(&ctty_cdevsw, 0, DV_CHR, 0, 0, 0666, "tty"); #endif } } SYSINIT(cttydev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ctty_drvinit,NULL)