diff options
Diffstat (limited to 'sys/dev/sio/sio.c')
-rw-r--r-- | sys/dev/sio/sio.c | 1721 |
1 files changed, 1721 insertions, 0 deletions
diff --git a/sys/dev/sio/sio.c b/sys/dev/sio/sio.c new file mode 100644 index 000000000000..38503fd783bb --- /dev/null +++ b/sys/dev/sio/sio.c @@ -0,0 +1,1721 @@ +/*- + * 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. + * + * @(#)sio.c 7.5 (Berkeley) 5/16/91 + * + * 27 May 93 Bruce Evans From com-0.2 package, fast interrupt + * com port driver. + * 27 May 93 Guido van Rooij Ported in Chris Demetriou's BIDIR + * code, add multiport support. + * 27 May 93 Rodney W. Grimes I then renamed it to sio.c for putting + * into the patch kit. Added in sioselect + * from com.c. Added port 4 support. + */ +static char rcsid[] = "$Header: /usr/bill/working/sys/i386/isa/RCS/com.c,v 1.2 92/01/21 14:34:11 william Exp $"; + +#include "sio.h" +#if NSIO > 0 +/* + * COM driver, based on HP dca driver. + * Mostly rewritten to use pseudo-DMA. + * Works for National Semiconductor NS8250-NS16550AF UARTs. + */ +#include "param.h" +#include "systm.h" +#include "ioctl.h" +#include "tty.h" +#include "proc.h" +#include "user.h" +#include "conf.h" +#include "file.h" +#include "uio.h" +#include "kernel.h" +#include "syslog.h" + +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/comreg.h" +#include "i386/isa/ic/ns16550.h" + +#undef CRTS_IFLOW +#define CRTS_IFLOW CRTSCTS /* XXX, CCTS_OFLOW = CRTSCTS already */ +#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ +#define RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE) +#define RB_I_LOW_WATER ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8) +#define RS_IBUFSIZE 256 +#define TS_RTSBLOCK TS_TBLOCK /* XXX */ +#define TTY_BI TTY_FE /* XXX */ +#define TTY_OE TTY_PE /* XXX */ +#ifndef COM_BIDIR +#define UNIT(x) (minor(x)) /* XXX */ +#else /* COM_BIDIR */ +#define COM_UNITMASK 0x7f +#define COM_CALLOUTMASK 0x80 + +#define UNIT(x) (minor(x) & COM_UNITMASK) +#define CALLOUT(x) (minor(x) & COM_CALLOUTMASK) +#endif /* COM_BIDIR */ + +#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) +#endif /* COM_MULTIPORT */ + +#define com_scr 7 /* scratch register for 16450-16550 (R/W) */ +#define schedsoftcom() (ipending |= 1 << 4) /* XXX */ + +/* + * 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: + * TS_BUSY = CS_BUSY (maintained by comstart() and comflush()) + * CS_TTGO = ~TS_TTSTOP (maintained by comstart() and siostop()) + * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) + * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) + * TS_FLUSH is not used. + * Bug: I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. + */ +#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_ODONE 4 /* output completed */ +#define CS_RTS_IFLOW 8 /* use RTS input flow control */ + +static char *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 int Bool_t; /* promoted boolean */ +typedef u_char bool_t; /* boolean */ + +/* com device structure */ +struct com_s { + u_char state; /* miscellaneous flag bits */ + u_char cfcr_image; /* copy of value written to CFCR */ + bool_t hasfifo; /* nonzero for 16550 UARTs */ + u_char mcr_image; /* copy of value written to MCR */ + bool_t softDCD; /* nonzero for faked carrier detect */ +#ifdef COM_BIDIR + bool_t bidir; /* is this unit bidirectional? */ + bool_t active; /* is the port active _at all_? */ + bool_t active_in; /* is the incoming port in use? */ + bool_t active_out; /* is the outgoing port in use? */ +#endif /* COM_BIDIR */ +#ifdef COM_MULTIPORT + bool_t multiport; /* is this unit part of a multiport device? */ +#endif /* COM_MULTIPORT */ + + /* + * 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 *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 */ + + u_char *obufend; /* end of output buffer */ + int ocount; /* original count for current output */ + u_char *optr; /* next char to output */ + + Port_t data_port; /* i/o ports */ + Port_t int_id_port; + Port_t iobase; + Port_t modem_ctl_port; + Port_t line_status_port; + Port_t modem_status_port; + + struct tty *tp; /* cross reference */ + + u_long bytes_in; /* statistics */ + u_long bytes_out; + u_int delta_error_counts[CE_NTYPES]; + u_int 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]; +}; + + +/* + * These functions in the com module ought to be declared (with a prototype) + * in a com-driver system header. The void ones may need to be int to match + * ancient devswitch declarations, but they don't actually return anything. + */ +#define Dev_t int /* promoted dev_t */ +struct consdev; + +int sioclose __P((Dev_t dev, int fflag, int devtype, + struct proc *p)); +void siointr __P((int unit)); +#ifdef COM_MULTIPORT +bool_t comintr1 __P((struct com_s *com)); +#endif /* COM_MULTIPORT */ +int sioioctl __P((Dev_t dev, int cmd, caddr_t data, + int fflag, struct proc *p)); +int siocngetc __P((Dev_t dev)); +void siocninit __P((struct consdev *cp)); +void siocnprobe __P((struct consdev *cp)); +void siocnputc __P((Dev_t dev, int c)); +int sioopen __P((Dev_t dev, int oflags, int devtype, + struct proc *p)); +/* + * sioopen gets compared to the d_open entry in struct cdevsw. d_open and + * other functions are declared in <sys/conf.h> with short types like dev_t + * in the prototype. Such declarations are broken because they vary with + * __P (significantly in theory - the compiler is allowed to push a short + * arg if it has seen the prototype; insignificantly in practice - gcc + * doesn't push short args and it would be slower on 386's to do so). + * + * Also, most of the device switch functions are still declared old-style + * so they take a Dev_t arg and shorten it to a dev_t. It would be simpler + * and faster if dev_t's were always promoted (to ints or whatever) as + * early as possible. + * + * Until <sys/conf.h> is fixed, we cast sioopen to the following `wrong' type + * when comparing it to the d_open entry just to avoid compiler warnings. + */ +typedef int (*bogus_open_t) __P((dev_t dev, int oflags, int devtype, + struct proc *p)); +int sioread __P((Dev_t dev, struct uio *uio, int ioflag)); +void siostop __P((struct tty *tp, int rw)); +int siowrite __P((Dev_t dev, struct uio *uio, int ioflag)); +void softsio0 __P((void)); +void softsio1 __P((void)); +void softsio2 __P((void)); +void softsio3 __P((void)); +void softsio4 __P((void)); +void softsio5 __P((void)); +void softsio6 __P((void)); +void softsio7 __P((void)); +void softsio8 __P((void)); + +static int sioattach __P((struct isa_device *dev)); +static void comflush __P((struct com_s *com)); +static void comhardclose __P((struct com_s *com)); +static void cominit __P((int unit, int rate)); +static int commctl __P((struct com_s *com, int bits, int how)); +static int comparam __P((struct tty *tp, struct termios *t)); +static int sioprobe __P((struct isa_device *dev)); +static void compoll __P((void)); +static int comstart __P((struct tty *tp)); +static void comwakeup __P((void)); + +/* 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]) + +static struct com_s com_structs[NSIO]; + +struct isa_driver siodriver = { + sioprobe, sioattach, "sio" +}; + +#ifdef COMCONSOLE +static int comconsole = COMCONSOLE; +#else +static int comconsole = -1; +#endif +static bool_t comconsinit; +static speed_t comdefaultrate = TTYDEF_SPEED; +static u_int com_events; /* input chars + weighted output completions */ +static int commajor; +struct tty sio_tty[NSIO]; +extern struct tty *constty; +extern u_int ipending; /* XXX */ +extern int tk_nin; /* XXX */ +extern int tk_rawcc; /* XXX */ + +#ifdef KGDB +#include "machine/remote-sl.h" + +extern int kgdb_dev; +extern int kgdb_rate; +extern int kgdb_debug_init; +#endif + +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 +}; + +/* XXX - configure this list */ +static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; + +static int +sioprobe(dev) + struct isa_device *dev; +{ + static bool_t already_init; + Port_t *com_ptr; + Port_t iobase; + int result; + + 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. + */ + for (com_ptr = likely_com_ports; + com_ptr < &likely_com_ports[sizeof likely_com_ports + / sizeof likely_com_ports[0]]; + ++com_ptr) + outb(*com_ptr + com_mcr, 0); + already_init = TRUE; + } + iobase = dev->id_iobase; + result = 1; + + /* + * 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(); + + /* + * Enable output interrupts (only) and check the following: + * 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. + */ + outb(iobase + com_cfcr, CFCR_8BITS); /* ensure IER is addressed */ + outb(iobase + com_mcr, MCR_IENABLE); /* open gate early */ + outb(iobase + com_ier, 0); /* ensure edge on next intr */ + outb(iobase + com_ier, IER_ETXRDY); /* generate interrupt */ + if ( inb(iobase + com_cfcr) != CFCR_8BITS + || inb(iobase + com_ier) != IER_ETXRDY + || inb(iobase + com_mcr) != MCR_IENABLE + || !isa_irq_pending(dev) + || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY + || isa_irq_pending(dev) + || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND) + result = 0; + + /* + * Turn off all device interrupts and check that they go off properly. + * Leave MCR_IENABLE set. 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_mcr, MCR_IENABLE); /* dummy to avoid bus echo */ + if ( inb(iobase + com_ier) != 0 + || isa_irq_pending(dev) + || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND) + result = 0; + + enable_intr(); + + return (result); +} + +static int /* XXX - should be void */ +sioattach(isdp) + struct isa_device *isdp; +{ + struct com_s *com; + static bool_t comwakeup_started = FALSE; + Port_t iobase; + int s; + u_char scr; + u_char scr1; + u_char scr2; + int unit; + + iobase = isdp->id_iobase; + unit = isdp->id_unit; + if (unit == comconsole) + DELAY(1000); /* XXX */ + s = spltty(); + + /* + * 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. + * Keeping MCR_DTR and MCR_RTS off might stop the external + * device from sending before we are ready. + */ + + com = &com_structs[unit]; + com->cfcr_image = CFCR_8BITS; + com->mcr_image = MCR_IENABLE; +#if 0 + com->softDCD = TRUE; +#endif + com->iptr = com->ibuf = com->ibuf1; + com->ibufend = com->ibuf1 + RS_IBUFSIZE; + com->ihighwater = com->ibuf1 + RS_IHIGHWATER; + com->iobase = iobase; + com->data_port = iobase + com_data; + com->int_id_port = iobase + com_iir; + com->modem_ctl_port = iobase + com_mcr; + com->line_status_port = iobase + com_lsr; + com->modem_status_port = iobase + com_msr; + com->tp = &sio_tty[unit]; +#ifdef COM_BIDIR + /* + * if bidirectional ports possible, clear the bidir port info; + */ + com->bidir = FALSE; + com->active = FALSE; + com->active_in = com->active_out = FALSE; +#endif /* COM_BIDIR */ + + /* attempt to determine UART type */ + 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>"); + else { + outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14); + DELAY(100); + switch (inb(iobase + com_iir) & IIR_FIFO_MASK) { + case FIFO_TRIGGER_1: + printf(" <16450>"); + break; + case FIFO_TRIGGER_4: + printf(" <16450?>"); + break; + case FIFO_TRIGGER_8: + printf(" <16550?>"); + break; + case FIFO_TRIGGER_14: + com->hasfifo = TRUE; + printf(" <16550A>"); + break; + } + outb(iobase + com_fifo, 0); + } +#ifdef COM_MULTIPORT + if (COM_ISMULTIPORT(isdp)) { + struct isa_device *masterdev; + + com->multiport = TRUE; + printf(" (multiport)"); + + /* set the master's common-interrupt-enable reg., + * as appropriate. YYY See your manual + */ + /* enable only common interrupt for port */ + outb(iobase + com_mcr, 0); + + masterdev = find_isadev(isa_devtab_tty, &siodriver, + COM_MPMASTER(isdp)); + outb(masterdev->id_iobase+com_scr, 0x80); + } + else + com->multiport = FALSE; +#endif /* COM_MULTIPORT */ + +#ifdef KGDB + if (kgdb_dev == makedev(commajor, unit)) { + if (comconsole == unit) + kgdb_dev = -1; /* can't debug over console port */ + else { + cominit(unit, kgdb_rate); + if (kgdb_debug_init) { + /* + * Print prefix of device name, + * let kgdb_connect print the rest. + */ + printf("com%d: ", unit); + kgdb_connect(1); + } + else + printf("com%d: kgdb enabled\n", unit); + } + } +#endif + + /* + * Need to reset baud rate, etc. of next print so reset comconsinit. + * Also make sure console is always "hardwired" + */ + if (unit == comconsole) { + comconsinit = FALSE; + com->softDCD = TRUE; + } + + com_addr(unit) = com; + + splx(s); + + if (!comwakeup_started) { + comwakeup(); + comwakeup_started = TRUE; + } + + return (1); +} + +/* ARGSUSED */ +int +sioopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct com_s *com; + int error = 0; + Port_t iobase; + int s; + struct tty *tp; + int unit = UNIT(dev); +#ifdef COM_BIDIR + bool_t callout = CALLOUT(dev); +#endif /* COM_BIDIR */ + if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) + return (ENXIO); +#ifdef COM_BIDIR + /* if it's a callout device, and bidir not possible on that dev, die */ + if (callout && !(com->bidir)) + return (ENXIO); +#endif /* COM_BIDIR */ + + tp = com->tp; + s = spltty(); + +#ifdef COM_BIDIR + +bidir_open_top: + /* if it's bidirectional, we've gotta deal with it... */ + if (com->bidir) { + if (callout) { + if (com->active_in) { + /* it's busy. die */ + splx(s); + return (EBUSY); + } else { + /* it's ours. lock it down, and set it up */ + com->active_out = TRUE; + com->softDCD = TRUE; + } + } else { + if (com->active_out) { + /* it's busy, outgoing. wait, if possible */ + if (flag & O_NONBLOCK) { + /* can't wait; bail */ + splx(s); + return (EBUSY); + } else { + /* wait for it... */ + error = tsleep(&com->active_out, + TTIPRI|PCATCH, + "comoth", + 0); + /* if there was an error, take off. */ + if (error != 0) { + splx(s); + return (error); + } + /* else take it from the top */ + goto bidir_open_top; + } + } else if (com->last_modem_status & MSR_DCD) { + /* there's a carrier on the line; we win */ + com->active_in = TRUE; + com->softDCD = FALSE; + } else { + /* there is no carrier on the line */ + if (flag & O_NONBLOCK) { + /* can't wait; let it open */ + com->active_in = TRUE; + com->softDCD = FALSE; + } else { + /* put DTR & RTS up */ + /* NOTE: cgd'sdriver used the ier register + * to enable/disable interrupts. This one + * uses both ier and IENABLE in the mcr. + */ + (void) commctl(com, MCR_DTR | MCR_RTS, DMSET); + outb(com->iobase + com_ier, IER_EMSC); + /* wait for it... */ + error = tsleep(&com->active_in, + TTIPRI|PCATCH, + "comdcd", + 0); + + /* if not active, turn DTR & RTS off */ + if (!com->active) + (void) commctl(com, MCR_DTR | MCR_RTS, DMBIC); + + /* if there was an error, take off. */ + if (error != 0) { + splx(s); + return (error); + } + /* else take it from the top */ + goto bidir_open_top; + } + } + } + } + + com->active = TRUE; +#endif /* COM_BIDIR */ + + tp->t_oproc = comstart; + tp->t_param = comparam; + tp->t_dev = dev; + if (!(tp->t_state & TS_ISOPEN)) { + tp->t_state |= TS_WOPEN; + ttychars(tp); + if (tp->t_ispeed == 0) { + /* + * We no longer use the flags from <sys/ttydefaults.h> + * since those 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. It's useful to have clocal on + * initially so that "stty changed-defaults </dev/comx" + * doesn't hang waiting for carrier. + */ + tp->t_iflag = 0; + tp->t_oflag = 0; + tp->t_cflag = CREAD | CS8 | CLOCAL; + tp->t_lflag = 0; + tp->t_ispeed = tp->t_ospeed = comdefaultrate; + } + (void) commctl(com, MCR_DTR | MCR_RTS, DMSET); + error = comparam(tp, &tp->t_termios); + if (error != 0) + goto out; + ttsetwater(tp); + iobase = com->iobase; + disable_intr(); + if (com->hasfifo) + /* (re)enable and drain FIFO */ + outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14 + | FIFO_RCV_RST | FIFO_XMT_RST); + (void) inb(com->line_status_port); + (void) inb(com->data_port); + com->last_modem_status = + com->prev_modem_status = inb(com->modem_status_port); + outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS + | IER_EMSC); + enable_intr(); + if (com->softDCD || com->prev_modem_status & MSR_DCD) + tp->t_state |= TS_CARR_ON; + } + else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + splx(s); + return (EBUSY); + } + while (!(flag & O_NONBLOCK) && !(tp->t_cflag & CLOCAL) +#ifdef COM_BIDIR + /* We went through a lot of trouble to open it, + * but it's certain we have a carrier now, so + * don't spend any time on it now. + */ + && !(com->bidir) +#endif /* COM_BIDIR */ + && !(tp->t_state & TS_CARR_ON)) { + tp->t_state |= TS_WOPEN; + error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH, + ttopen, 0); + if (error != 0) + break; + } +out: + splx(s); + if (error == 0) + error = (*linesw[tp->t_line].l_open)(dev, tp); + +#ifdef COM_BIDIR + /* wakeup sleepers */ + wakeup((caddr_t) &com->active_in); +#endif /* COM_BIDIR */ + + /* + * XXX - the next step was once not done, so interrupts, DTR and RTS + * remainded hot if the process was killed while it was sleeping + * waiting for carrier. Now there is the opposite problem. If several + * processes are sleeping waiting for carrier on the same line and one + * is killed, interrupts are turned off so the other processes will + * never see the carrier rise. + */ + if (error != 0 && !(tp->t_state & TS_ISOPEN)) +{ + comhardclose(com); +} + tp->t_state &= ~TS_WOPEN; + + return (error); +} + +/*ARGSUSED*/ +int +sioclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct com_s *com; + struct tty *tp; + + com = com_addr(UNIT(dev)); + tp = com->tp; + (*linesw[tp->t_line].l_close)(tp, flag); + comhardclose(com); + ttyclose(tp); + return (0); +} + +void +comhardclose(com) + struct com_s *com; +{ + Port_t iobase; + int s; + struct tty *tp; + + s = spltty(); + iobase = com->iobase; + outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); +#ifdef KGDB + /* do not disable interrupts if debugging */ + if (kgdb_dev != makedev(commajor, com - &com_structs[0])) +#endif + outb(iobase + com_ier, 0); + tp = com->tp; + if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN + || !(tp->t_state & TS_ISOPEN)) + (void) commctl(com, 0, DMSET); +#ifdef COM_BIDIR + com->active = com->active_in = com->active_out = FALSE; + com->softDCD = FALSE; + + /* wakeup sleepers who are waiting for out to finish */ + wakeup((caddr_t) &com->active_out); +#endif /* COM_BIDIR */ + + splx(s); +} + +int +sioread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + struct tty *tp = com_addr(UNIT(dev))->tp; + + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} + +int +siowrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int unit = UNIT(dev); + struct tty *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 && unit == comconsole) + constty = NULL; + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); +} + +void +siointr(unit) + int unit; +{ + struct com_s *com; +#ifndef COM_MULTIPORT + u_char line_status; + u_char modem_status; + u_char *ioptr; + u_char recv_data; + + com = com_addr(unit); +#else /* COM_MULTIPORT */ + int i; + bool_t donesomething; + + do { + donesomething = FALSE; + for(i=0;i<NSIO;i++) { + com=com_addr(i); + if(com != NULL) { + /* XXX When siointr is called + * to start output, maybe + * it should be changed to a + * call to comintr1. Doesn't + * seem a good idea: interrupts + * are disabled all the time. + */ +enable_intr(); +disable_intr(); + donesomething = comintr1(com); + } + } + } while (donesomething); + return; +} + +bool_t +comintr1(struct com_s *com) +{ + u_char line_status; + u_char modem_status; + u_char *ioptr; + u_char recv_data; + bool_t donesomething; + + donesomething = FALSE; +#endif /* COM_MULTIPORT */ + + while (TRUE) { + 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? */ +#ifdef COM_MULTIPORT + donesomething = TRUE; +#endif /* COM_MULTIPORT */ + if (!(line_status & LSR_RXRDY)) + recv_data = 0; + else + recv_data = inb(com->data_port); + ++com->bytes_in; +#ifdef KGDB + /* trap into kgdb? (XXX - needs testing and optim) */ + if (recv_data == FRAME_END + && !(com->tp->t_state & TS_ISOPEN) + && kgdb_dev == makedev(commajor, unit)) { + kgdb_connect(0); + continue; + } +#endif /* KGDB */ + ioptr = com->iptr; + if (ioptr >= com->ibufend) + CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); + else { + ++com_events; + 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); + } + + /* + * "& 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) { + /* + * 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. + */ +#ifdef COM_MULTIPORT + donesomething = TRUE; +#endif /* COM_MULTIPORT */ + com->last_modem_status = modem_status; + if (!(com->state & CS_CHECKMSR)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_CHECKMSR; + schedsoftcom(); + } + + /* 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_ODEVREADY | CS_BUSY | CS_TTGO)) { +#ifdef COM_MULTIPORT + donesomething = TRUE; +#endif /* COM_MULTIPORT */ + ioptr = com->optr; + outb(com->data_port, *ioptr); + ++com->bytes_out; + com->optr = ++ioptr; + if (ioptr >= com->obufend) { + /* output just completed */ + com_events += LOTS_OF_EVENTS; + com->state ^= (CS_ODONE | CS_BUSY); + schedsoftcom(); /* handle at high level ASAP */ + } + } + + /* finished? */ + if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) +#ifdef COM_MULTIPORT + return (donesomething); +#else + return; +#endif /* COM_MULTIPORT */ + } +} + +int +sioioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct com_s *com; + int error; + Port_t iobase; + int s; + struct tty *tp; + + com = com_addr(UNIT(dev)); + tp = com->tp; + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); + if (error >= 0) + return (error); + error = ttioctl(tp, cmd, data, flag); + if (error >= 0) + return (error); + + iobase = com->iobase; + s = spltty(); + 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, MCR_DTR | MCR_RTS, DMBIS); + break; + case TIOCCDTR: + (void) commctl(com, MCR_DTR | MCR_RTS, DMBIC); + break; + 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; +#ifdef COM_BIDIR + case TIOCMSBIDIR: + /* must be root to set bidir. capability */ + if (p->p_ucred->cr_uid != 0) + return(EPERM); + + /* if it's the console, can't do it */ + if (UNIT(dev) == comconsole) + return(ENOTTY); + + /* can't do the next, for obvious reasons... + * but there are problems to be looked at... + */ + + /* if the port is active, don't do it */ + /* if (com->active) + return(EBUSY); */ + + com->bidir = *(int *)data; + break; + case TIOCMGBIDIR: + *(int *)data = com->bidir; + break; +#endif /* COM_BIDIR */ + default: + splx(s); + return (ENOTTY); + } + splx(s); + return (0); +} + +/* cancel pending output */ +static void +comflush(com) + struct com_s *com; +{ + struct ringb *rbp; + + disable_intr(); + if (com->state & CS_ODONE) + com_events -= LOTS_OF_EVENTS; + com->state &= ~(CS_ODONE | CS_BUSY); + enable_intr(); + rbp = &com->tp->t_out; + rbp->rb_hd += com->ocount; + rbp->rb_hd = RB_ROLLOVER(rbp, rbp->rb_hd); + com->ocount = 0; + com->tp->t_state &= ~TS_BUSY; +} + +static void +compoll() +{ + static bool_t awake = FALSE; + struct com_s *com; + int s; + int unit; + + if (com_events == 0) + return; + disable_intr(); + if (awake) { + enable_intr(); + return; + } + awake = TRUE; + enable_intr(); + s = spltty(); +repeat: + for (unit = 0; unit < NSIO; ++unit) { + u_char *buf; + u_char *ibuf; + int incc; + struct tty *tp; + + com = com_addr(unit); + if (com == NULL) + continue; + tp = com->tp; + + /* switch the role of the low-level input buffers */ + if (com->iptr == (ibuf = com->ibuf)) + 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->mcr_image & MCR_RTS) + && !(tp->t_state & TS_RTSBLOCK)) + 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 && + unit != comconsole) { +#ifdef COM_BIDIR + if (com->prev_modem_status & MSR_DCD) { + (*linesw[tp->t_line].l_modem)(tp, 1); + com->softDCD = FALSE; + wakeup((caddr_t) &com->active_in); + } +#else + if (com->prev_modem_status & MSR_DCD) + (*linesw[tp->t_line].l_modem)(tp, 1); +#endif /* COM_BIDIR */ + else if ((*linesw[tp->t_line].l_modem)(tp, 0) + == 0) { + disable_intr(); + outb(com->modem_ctl_port, + com->mcr_image + &= ~(MCR_DTR | MCR_RTS)); + enable_intr(); + } + } + } + + /* XXX */ + if (TRUE) { + u_int delta; + u_int delta_error_counts[CE_NTYPES]; + int errnum; + u_long total; + + disable_intr(); + bcopy(com->delta_error_counts, delta_error_counts, + sizeof delta_error_counts); + bzero(com->delta_error_counts, + sizeof delta_error_counts); + enable_intr(); + for (errnum = 0; errnum < CE_NTYPES; ++errnum) { + delta = delta_error_counts[errnum]; + if (delta != 0) { + total = + com->error_counts[errnum] += delta; + log(LOG_WARNING, + "com%d: %u more %s%s (total %lu)\n", + unit, delta, error_desc[errnum], + delta == 1 ? "" : "s", total); + } + } + } + if (com->state & CS_ODONE) { + comflush(com); + /* XXX - why isn't the table used for t_line == 0? */ + if (tp->t_line != 0) + (*linesw[tp->t_line].l_start)(tp); + else + comstart(tp); + } + if (incc <= 0 || !(tp->t_state & TS_ISOPEN)) + continue; + if (com->state & CS_RTS_IFLOW + && RB_LEN(&tp->t_raw) + incc >= RB_I_HIGH_WATER + && !(tp->t_state & TS_RTSBLOCK) + /* + * XXX - need RTS flow control for all line disciplines. + * Only have it in standard one now. + */ + && linesw[tp->t_line].l_rint == ttyinput) { + tp->t_state |= TS_RTSBLOCK; + ttstart(tp); + } + /* + * 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_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP + | IXOFF | IXON)) + && !(tp->t_lflag & (ECHO | ECHONL | ICANON | IEXTEN | ISIG + | PENDIN)) + && !(tp->t_state & (TS_CNTTB | TS_LNCH)) + && linesw[tp->t_line].l_rint == ttyinput) { + tk_nin += incc; + tk_rawcc += incc; + tp->t_rawcc += incc; + com->delta_error_counts[CE_TTY_BUF_OVERFLOW] + += incc - rb_write(&tp->t_raw, (char *) buf, + incc); + 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; + ttstart(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; + splx(s); + awake = FALSE; +} + +static int +comparam(tp, t) + struct tty *tp; + struct termios *t; +{ + u_int cfcr; + int cflag; + struct com_s *com; + int divisor; + int error; + Port_t iobase; + int s; + int unit; + + /* check requested parameters */ + divisor = ttspeedtab(t->c_ospeed, comspeedtab); + if (divisor < 0 || t->c_ispeed != 0 && t->c_ispeed != t->c_ospeed) + return (EINVAL); + + /* parameters are OK, convert them to the com struct and the device */ + unit = UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + s = spltty(); + if (divisor == 0) { + (void) commctl(com, 0, DMSET); /* hang up line */ + splx(s); + return (0); + } + 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; + + /* + * 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 siointr() might lose some receiver + * error bits, but that is acceptable here. + */ + disable_intr(); + com->state &= ~CS_TTGO; + enable_intr(); + while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) + != (LSR_TSRE | LSR_TXRDY)) { + error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH, + "comparam", 1); + 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 */ + outb(iobase + com_cfcr, cfcr | CFCR_DLAB); + outb(iobase + com_dlbl, divisor & 0xFF); + outb(iobase + com_dlbh, (u_int) divisor >> 8); + outb(iobase + com_cfcr, com->cfcr_image = cfcr); + if (!(tp->t_state & TS_TTSTOP)) + com->state |= CS_TTGO; + if (cflag & CRTS_IFLOW) + com->state |= CS_RTS_IFLOW; /* XXX - secondary changes? */ + else + com->state &= ~CS_RTS_IFLOW; + + /* + * Set up state to handle output flow control. + * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? + * Now has 16+ msec latency, while CTS flow has 50- usec latency. + * Note that DCD flow control stupidly uses the same state flag + * (TS_TTSTOP) as XON/XOFF flow control. + */ + com->state &= ~CS_CTS_OFLOW; + com->state |= CS_ODEVREADY; + if (cflag & CCTS_OFLOW) { + com->state |= CS_CTS_OFLOW; + if (!(com->prev_modem_status & MSR_CTS)) + com->state &= ~CS_ODEVREADY; + } + + enable_intr(); + siointr(unit); /* recover from fiddling with CS_TTGO */ + splx(s); + return (0); +} + +static int /* XXX - should be void */ +comstart(tp) + struct tty *tp; +{ + struct com_s *com; + int s; + int unit; + + unit = 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_RTSBLOCK) { + 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) + outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); + } + enable_intr(); + if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) + goto out; + if (RB_LEN(&tp->t_out) <= tp->t_lowat) { + if (tp->t_state & TS_ASLEEP) { + tp->t_state &= ~TS_ASLEEP; + wakeup((caddr_t)&tp->t_out); + } + if (tp->t_wsel) { + selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); + tp->t_wsel = 0; + tp->t_state &= ~TS_WCOLL; + } + } + if (com->ocount != 0) { + disable_intr(); + siointr(unit); + enable_intr(); + } + else if (RB_LEN(&tp->t_out) != 0) { + tp->t_state |= TS_BUSY; + com->ocount = RB_CONTIGGET(&tp->t_out); + disable_intr(); + com->obufend = (com->optr = (u_char *) tp->t_out.rb_hd) + + com->ocount; + com->state |= CS_BUSY; + siointr(unit); /* fake interrupt to start output */ + enable_intr(); + } +out: + splx(s); + return (1); +} + +void +siostop(tp, rw) + struct tty *tp; + int rw; +{ + struct com_s *com; + + com = com_addr(UNIT(tp->t_dev)); + if (rw & FWRITE) + comflush(com); + disable_intr(); + if (tp->t_state & TS_TTSTOP) + com->state &= ~CS_TTGO; + else + com->state |= CS_TTGO; + enable_intr(); +} + +static int +commctl(com, bits, how) + struct com_s *com; + int bits; + int how; +{ + disable_intr(); + switch (how) { + case DMSET: +#ifdef COM_MULTIPORT + /* YYY maybe your card doesn't want IENABLE to be reset? */ + if(com->multiport) + outb(com->modem_ctl_port, + com->mcr_image = bits); + else +#endif /* COM_MULTIPORT */ + outb(com->modem_ctl_port, + com->mcr_image = bits | MCR_IENABLE); + break; + case DMBIS: + outb(com->modem_ctl_port, com->mcr_image |= bits); + break; + case DMBIC: +#ifdef COM_MULTIPORT + /* YYY maybe your card doesn't want IENABLE to be reset? */ + if(com->multiport) + outb(com->modem_ctl_port, + com->mcr_image &= ~(bits)); + else +#endif /* COM_MULTIPORT */ + outb(com->modem_ctl_port, + com->mcr_image &= ~(bits & ~MCR_IENABLE)); + break; + case DMGET: + bits = com->prev_modem_status; + break; + } + enable_intr(); + return (bits); +} + +static void +comwakeup() +{ + struct com_s *com; + int unit; + + timeout((timeout_func_t) comwakeup, (caddr_t) NULL, 1); + if (com_events != 0) + /* schedule compoll() to run when the cpl allows */ + schedsoftcom(); + + /* recover from lost output interrupts */ + for (unit = 0; unit < NSIO; ++unit) { + com = com_addr(unit); + if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) { + disable_intr(); + siointr(unit); + enable_intr(); + } + } +} + +void +softsio0() { compoll(); } + +void +softsio1() { compoll(); } + +void +softsio2() { compoll(); } + +void +softsio3() { compoll(); } + +void +softsio4() { compoll(); } + +void +softsio5() { compoll(); } + +void +softsio6() { compoll(); } + +void +softsio7() { compoll(); } + +void +softsio8() { compoll(); } + +/* + * Following are all routines needed for COM to act as console + * XXX - not tested in this version + * XXX - check that the corresponding serial interrupts are never enabled + */ +#include "i386/i386/cons.h" + +void +siocnprobe(cp) + struct consdev *cp; +{ + int unit; + + /* locate the major number */ + for (commajor = 0; commajor < nchrdev; commajor++) + if (cdevsw[commajor].d_open == (bogus_open_t) sioopen) + break; + + /* XXX: ick */ + unit = CONUNIT; + com_addr(unit) = &com_structs[unit]; + com_addr(unit)->iobase = CONADDR; + + /* make sure hardware exists? XXX */ + + /* initialize required fields */ + cp->cn_dev = makedev(commajor, unit); + cp->cn_tp = &sio_tty[unit]; +#ifdef COMCONSOLE + cp->cn_pri = CN_REMOTE; /* Force a serial port console */ +#else + cp->cn_pri = CN_NORMAL; +#endif +} + +void +siocninit(cp) + struct consdev *cp; +{ + int unit; + + unit = UNIT(cp->cn_dev); + cominit(unit, comdefaultrate); + comconsole = unit; + comconsinit = TRUE; +} + +static void +cominit(unit, rate) + int unit; + int rate; +{ + Port_t iobase; + int s; + + iobase = com_addr(unit)->iobase; + s = splhigh(); + outb(iobase + com_cfcr, CFCR_DLAB); + rate = ttspeedtab(comdefaultrate, comspeedtab); + outb(iobase + com_data, rate & 0xFF); + outb(iobase + com_ier, rate >> 8); + outb(iobase + com_cfcr, CFCR_8BITS); + + /* + * XXX - fishy to enable interrupts and then poll. + * It shouldn't be necessary to ready the iir. + */ + outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); + outb(iobase + com_fifo, + FIFO_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST | FIFO_TRIGGER_14); + (void) inb(iobase + com_iir); + splx(s); +} + +int +siocngetc(dev) + dev_t dev; +{ + int c; + Port_t iobase; + int s; + + iobase = com_addr(UNIT(dev))->iobase; + s = splhigh(); + while (!(inb(iobase + com_lsr) & LSR_RXRDY)) + ; + c = inb(iobase + com_data); + (void) inb(iobase + com_iir); + splx(s); + return (c); +} + +void +siocnputc(dev, c) + dev_t dev; + int c; +{ + Port_t iobase; + int s; + int timo; + + iobase = com_addr(UNIT(dev))->iobase; + s = splhigh(); +#ifdef KGDB + if (dev != kgdb_dev) +#endif + if (!comconsinit) { + (void) cominit(UNIT(dev), comdefaultrate); + comconsinit = TRUE; + } + /* wait for any pending transmission to finish */ + timo = 50000; + while (!(inb(iobase + com_lsr) & LSR_TXRDY) && --timo) + ; + outb(iobase + com_data, c); + /* wait for this transmission to complete */ + timo = 1500000; + while (!(inb(iobase + com_lsr) & LSR_TXRDY) && --timo) + ; + /* clear any interrupts generated by this transmission */ + (void) inb(iobase + com_iir); + splx(s); +} + +/* + * 10 Feb 93 Jordan K. Hubbard Added select code + * 27 May 93 Rodney W. Grimes Stole the select code from com.c.pl5 + */ + +int +sioselect(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ + register struct tty *tp = &sio_tty[UNIT(dev)]; + int nread; + int s = spltty(); + struct proc *selp; + + switch (rw) { + + case FREAD: + nread = ttnread(tp); + if (nread > 0 || + ((tp->t_cflag&CLOCAL) == 0 && (tp->t_state&TS_CARR_ON) == 0)) + goto win; + if (tp->t_rsel && (selp = pfind(tp->t_rsel)) && selp->p_wchan == (caddr_t)&selwait) + tp->t_state |= TS_RCOLL; + else + tp->t_rsel = p->p_pid; + break; + + case FWRITE: + if (RB_LEN(&tp->t_out) <= tp->t_lowat) + goto win; + if (tp->t_wsel && (selp = pfind(tp->t_wsel)) && selp->p_wchan == (caddr_t)&selwait) + tp->t_state |= TS_WCOLL; + else + tp->t_wsel = p->p_pid; + break; + } + splx(s); + return (0); + win: + splx(s); + return (1); +} + +#endif /* NSIO > 0 */ |