diff options
Diffstat (limited to 'sys/i386/isa/sio.c')
| -rw-r--r-- | sys/i386/isa/sio.c | 811 |
1 files changed, 416 insertions, 395 deletions
diff --git a/sys/i386/isa/sio.c b/sys/i386/isa/sio.c index 7eb02ff8e5b6..4cee4fe8c60f 100644 --- a/sys/i386/isa/sio.c +++ b/sys/i386/isa/sio.c @@ -31,15 +31,16 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.12 1993/10/16 13:46:18 rgrimes Exp $ + * $Id: sio.c,v 1.28 1994/02/07 18:37:21 ache Exp $ */ #include "sio.h" #if NSIO > 0 /* - * COM driver, based on HP dca driver. + * 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. */ #include "param.h" #include "systm.h" @@ -58,21 +59,19 @@ #include "i386/isa/comreg.h" #include "i386/isa/ic/ns16550.h" -#undef CRTS_IFLOW -#define CRTS_IFLOW CRTSCTS /* XXX, CCTS_OFLOW = CRTSCTS already */ +#define FAKE_DCD(unit) ((unit) == comconsole) #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 COM_CALLOUTMASK 0x80 /* for both minor and dev */ #define UNIT(x) (minor(x) & COM_UNITMASK) #define CALLOUT(x) (minor(x) & COM_CALLOUTMASK) #endif /* COM_BIDIR */ @@ -84,9 +83,22 @@ #define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) #define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) #endif /* COM_MULTIPORT */ +#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) + +#ifndef FIFO_TRIGGER +/* + * This driver is fast enough to work with any value and for high values + * to be only slightly more efficient. Low values may be better because + * they give lower latency. + * TODO: always use low values for low speeds. Mouse movements are jerky + * if more than one packet arrives at once. The low speeds used for + * serial mice help avoid this, but not if (large) fifos are enabled. + */ +#define FIFO_TRIGGER FIFO_TRIGGER_14 +#endif #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ -#define schedsoftcom() (ipending |= 1 << 4) /* XXX */ +#define setsofttty() (ipending |= 1 << 4) /* XXX */ /* * Input buffer watermarks. @@ -136,7 +148,6 @@ static char *error_desc[] = { /* 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 */ @@ -145,7 +156,6 @@ struct com_s { 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_? */ @@ -155,6 +165,8 @@ struct com_s { #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ + int dtr_wait; /* time to hold DTR down on close (* 1/HZ) */ + u_int tx_fifo_size; /* * The high level of the driver never reads status registers directly @@ -197,7 +209,6 @@ struct com_s { 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 @@ -209,9 +220,6 @@ 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)); @@ -220,48 +228,24 @@ 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)); +void siopoll __P((void)); int sioread __P((Dev_t dev, struct uio *uio, int ioflag)); +int sioselect __P((Dev_t dev, int rw, struct proc *p)); 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 void comintr1 __P((struct com_s *com)); +static void 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)); -static int tiocm2mcr __P((int)); +static void comstart __P((struct tty *tp)); +static void comwakeup __P((caddr_t chan, int ticks)); +static int tiocm_xxx2mcr __P((int tiocm_xxx)); /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; @@ -355,6 +339,11 @@ sioprobe(dev) disable_intr(); /* + * Initialize the speed so that any junk in the THR or output fifo will + * be transmitted in a known time. (There may be lots of junk after a + * soft reboot, and output interrupts don't work right after a master + * reset, at least for 16550s. (The speed is undefined after MR, but + * MR empties the THR and the TSR so it's not clear why this matters)). * 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 @@ -362,16 +351,23 @@ sioprobe(dev) * 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_DLAB); + outb(iobase + com_dlbl, COMBRD(9600) & 0xff); + outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); 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, 0); /* ensure edge on next intr */ outb(iobase + com_ier, IER_ETXRDY); /* generate interrupt */ + DELAY((16 + 1) * 9600 / 10); /* enough to drain 16 bytes */ if ( inb(iobase + com_cfcr) != CFCR_8BITS || inb(iobase + com_ier) != IER_ETXRDY || inb(iobase + com_mcr) != MCR_IENABLE +#ifndef COM_MULTIPORT /* XXX - need to do more to enable interrupts */ + || !isa_irq_pending(dev) +#endif || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY || isa_irq_pending(dev) - || (inb(iobase + com_iir) & IIR_IMASK) != IIR_NOPEND) + || !(inb(iobase + com_iir) & IIR_NOPEND)) result = 0; /* @@ -386,15 +382,16 @@ sioprobe(dev) 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) + || !(inb(iobase + com_iir) & IIR_NOPEND)) result = 0; + if (result == 0) + outb(iobase + com_mcr, 0); enable_intr(); - return (result); } -static int /* XXX - should be void */ +static int sioattach(isdp) struct isa_device *isdp; { @@ -402,9 +399,6 @@ sioattach(isdp) 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; @@ -428,9 +422,8 @@ sioattach(isdp) com = &com_structs[unit]; com->cfcr_image = CFCR_8BITS; com->mcr_image = MCR_IENABLE; -#if 0 - com->softDCD = TRUE; -#endif + com->dtr_wait = 200; + com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; com->ihighwater = com->ibuf1 + RS_IHIGHWATER; @@ -441,49 +434,53 @@ sioattach(isdp) 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); printf("sio%d: type", unit); #ifdef COM_MULTIPORT - if (0); -#else - if (scr1 != 0xa5 || scr2 != 0x5a) - printf(" <8250>"); + if (!COM_ISMULTIPORT(isdp)) #endif - 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: + { + 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_TRIGGER_14); + DELAY(100); + switch (inb(com->int_id_port) & 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: + printf(" 16550A"); + if (COM_NOFIFO(isdp)) { + printf(" fifo software disabled"); + } else { com->hasfifo = TRUE; - printf(" <16550A>"); - break; + com->tx_fifo_size = 16; } - outb(iobase + com_fifo, 0); + break; } + outb(iobase + com_fifo, 0); +determined_type: ; + #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(isdp)) { struct isa_device *masterdev; @@ -495,13 +492,12 @@ sioattach(isdp) * as appropriate. YYY See your manual */ /* enable only common interrupt for port */ - outb(iobase + com_mcr, 0); + outb(com->modem_ctl_port, com->mcr_image = 0); masterdev = find_isadev(isa_devtab_tty, &siodriver, COM_MPMASTER(isdp)); - outb(masterdev->id_iobase+com_scr, 0x80); - } - else + outb(masterdev->id_iobase + com_scr, 0x80); + } else com->multiport = FALSE; #endif /* COM_MULTIPORT */ printf("\n"); @@ -517,33 +513,26 @@ sioattach(isdp) * Print prefix of device name, * let kgdb_connect print the rest. */ - printf("com%d: ", unit); + printf("sio%d: ", unit); kgdb_connect(1); - } - else - printf("com%d: kgdb enabled\n", unit); + } else + printf("sio%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) { + if (unit == comconsole) comconsinit = FALSE; - com->softDCD = TRUE; - } com_addr(unit) = com; - splx(s); - if (!comwakeup_started) { - comwakeup(); + comwakeup((caddr_t) NULL, 0); comwakeup_started = TRUE; } - return (1); } @@ -555,19 +544,22 @@ sioopen(dev, flag, mode, p) int mode; struct proc *p; { +#ifdef COM_BIDIR + bool_t callout; +#endif /* COM_BIDIR */ 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 */ + int unit; + + unit = UNIT(dev); 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 */ + callout = CALLOUT(dev); if (callout && !(com->bidir)) return (ENXIO); #endif /* COM_BIDIR */ @@ -588,7 +580,6 @@ bidir_open_top: } else { /* it's ours. lock it down, and set it up */ com->active_out = TRUE; - com->softDCD = TRUE; } } else { if (com->active_out) { @@ -599,9 +590,9 @@ bidir_open_top: return (EBUSY); } else { /* wait for it... */ - error = tsleep(&com->active_out, + error = tsleep((caddr_t)&com->active_out, TTIPRI|PCATCH, - "comoth", + "siooth", 0); /* if there was an error, take off. */ if (error != 0) { @@ -611,33 +602,32 @@ bidir_open_top: /* else take it from the top */ goto bidir_open_top; } - } else if (com->last_modem_status & MSR_DCD) { + } else if (com->prev_modem_status & MSR_DCD + || FAKE_DCD(unit)) { /* 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); + /* XXX - bring up RTS earlier? */ + commctl(com, MCR_DTR | MCR_RTS, DMSET); outb(com->iobase + com_ier, IER_EMSC); + /* wait for it... */ - error = tsleep(&com->active_in, + error = tsleep((caddr_t)&com->active_in, TTIPRI|PCATCH, - "comdcd", + "siodcd", 0); - /* if not active, turn DTR & RTS off */ - if (!com->active) - (void) commctl(com, MCR_DTR, DMBIC); + /* if not active, turn intrs and DTR off */ + if (!com->active) { + outb(com->iobase + com_ier, 0); + commctl(com, MCR_DTR, DMBIC); + } /* if there was an error, take off. */ if (error != 0) { @@ -670,21 +660,41 @@ bidir_open_top: */ tp->t_iflag = 0; tp->t_oflag = 0; +#ifdef COMCONSOLE + if (unit == comconsole) + tp->t_oflag = TTYDEF_OFLAG; +#endif tp->t_cflag = CREAD | CS8 | HUPCL; tp->t_lflag = 0; tp->t_ispeed = tp->t_ospeed = comdefaultrate; } - (void) commctl(com, MCR_DTR | MCR_RTS, DMSET); + + /* + * XXX the full state after a first open() needs to be + * programmable and separate for callin and callout. + */ +#ifdef COM_BIDIR + if (com->bidir) { + if (callout) + tp->t_cflag |= CLOCAL; + else + tp->t_cflag &= ~CLOCAL; + } +#endif + + 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) + if (com->hasfifo) { /* (re)enable and drain FIFO */ - outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER_14 + outb(iobase + com_fifo, FIFO_ENABLE | FIFO_TRIGGER | FIFO_RCV_RST | FIFO_XMT_RST); + DELAY(100); + } + disable_intr(); (void) inb(com->line_status_port); (void) inb(com->data_port); com->last_modem_status = @@ -692,10 +702,9 @@ bidir_open_top: outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS | IER_EMSC); enable_intr(); - if (com->softDCD || com->prev_modem_status & MSR_DCD) + if (com->prev_modem_status & MSR_DCD || FAKE_DCD(unit)) tp->t_state |= TS_CARR_ON; - } - else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + } else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { splx(s); return (EBUSY); } @@ -705,9 +714,9 @@ bidir_open_top: * but it's certain we have a carrier now, so * don't spend any time on it now. */ - && !(com->bidir) + && !(com->bidir) #endif /* COM_BIDIR */ - && !(tp->t_state & TS_CARR_ON)) { + && !(tp->t_state & TS_CARR_ON)) { tp->t_state |= TS_WOPEN; error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH, ttopen, 0); @@ -717,7 +726,7 @@ bidir_open_top: out: splx(s); if (error == 0) - error = (*linesw[tp->t_line].l_open)(dev, tp); + error = (*linesw[tp->t_line].l_open)(dev, tp, 0); #ifdef COM_BIDIR /* wakeup sleepers */ @@ -726,16 +735,14 @@ out: /* * XXX - the next step was once not done, so interrupts, DTR and RTS - * remainded hot if the process was killed while it was sleeping + * remained 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); @@ -760,29 +767,44 @@ sioclose(dev, flag, mode, p) return (0); } -void +static void comhardclose(com) struct com_s *com; { Port_t iobase; int s; struct tty *tp; + int unit; s = spltty(); iobase = com->iobase; outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); + unit = com - &com_structs[0]; #ifdef KGDB - /* do not disable interrupts if debugging */ - if (kgdb_dev != makedev(commajor, com - &com_structs[0])) + /* do not disable interrupts or hang up if debugging */ + if (kgdb_dev != makedev(commajor, unit)) #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, MCR_RTS, DMSET); + tp = com->tp; + if (tp->t_cflag & HUPCL || tp->t_state & TS_WOPEN + || !(com->prev_modem_status & MSR_DCD) && !FAKE_DCD(unit) + || !(tp->t_state & TS_ISOPEN)) { + commctl(com, MCR_RTS, DMSET); + if (com->dtr_wait != 0) + /* + * Uninterruptible sleep since we want to + * wait a fixed time. + * XXX - delay in open() (if necessary), + * not here (always). + */ + tsleep((caddr_t)&com->dtr_wait, TTIPRI, + "sioclose", com->dtr_wait); + } + } + #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); @@ -827,49 +849,48 @@ 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; +#ifndef COM_MULTIPORT com = com_addr(unit); + if (com != NULL) + comintr1(com); #else /* COM_MULTIPORT */ - int i; - bool_t donesomething; + 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. + */ 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. + possibly_more_intrs = FALSE; + for (unit = 0; unit < NSIO; ++unit) { + com = com_addr(unit); + if (com != NULL + && !(inb(com->int_id_port) & IIR_NOPEND)) { + /* + * XXX call comintr1() instead of here from + * comwakeup(). The interrupt edge problem + * only exists for real interrupts. */ -enable_intr(); -disable_intr(); - donesomething = comintr1(com); + comintr1(com); + possibly_more_intrs = TRUE; } } - } while (donesomething); - return; + } while (possibly_more_intrs); +#endif /* COM_MULTIPORT */ } - -bool_t -comintr1(struct com_s *com) + +static void +comintr1(com) + 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); @@ -877,9 +898,6 @@ comintr1(struct com_s *com) /* 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 @@ -899,6 +917,10 @@ comintr1(struct com_s *com) CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); else { ++com_events; +#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; @@ -906,6 +928,9 @@ comintr1(struct com_s *com) && com->state & CS_RTS_IFLOW) outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); + /* XXX - move this out of isr */ + if (line_status & LSR_OE) + CE_RECORD(com, CE_OVERRUN); } /* @@ -924,14 +949,11 @@ comintr1(struct com_s *com) * 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(); + setsofttty(); } /* handle CTS change immediately for crisp flow ctl */ @@ -946,40 +968,52 @@ comintr1(struct com_s *com) /* 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); + if (com->tx_fifo_size > 1) { + u_int ocount; + + ocount = com->obufend - 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->optr = ++ioptr; + } + 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 */ + setsofttty(); /* handle at high level ASAP */ } } /* finished? */ - if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) -#ifdef COM_MULTIPORT - return (donesomething); -#else - return; +#ifndef COM_MULTIPORT + if (inb(com->int_id_port) & IIR_NOPEND) #endif /* COM_MULTIPORT */ + return; } } static int -tiocm2mcr(data) - int data; +tiocm_xxx2mcr(tiocm_xxx) + int tiocm_xxx; { - register m = 0; - if (data & TIOCM_DTR) m |= MCR_DTR; - if (data & TIOCM_RTS) m |= MCR_RTS; - return m; + int mcr; + + mcr = 0; + if (tiocm_xxx & TIOCM_DTR) + mcr |= MCR_DTR; + if (tiocm_xxx & TIOCM_RTS) + mcr |= MCR_RTS; + return (mcr); } + int sioioctl(dev, cmd, data, flag, p) dev_t dev; @@ -991,7 +1025,10 @@ sioioctl(dev, cmd, data, flag, p) struct com_s *com; int error; Port_t iobase; + int mcr; + int msr; int s; + int tiocm_xxx; struct tty *tp; com = com_addr(UNIT(dev)); @@ -1000,6 +1037,13 @@ sioioctl(dev, cmd, data, flag, p) if (error >= 0) return (error); error = ttioctl(tp, cmd, data, flag); + +#ifdef COM_BIDIR + /* XXX: plug security hole while sticky bits not yet implemented */ + if (com->bidir && com->active_in && p->p_ucred->cr_uid != 0) + tp->t_cflag &= ~CLOCAL; +#endif + if (error >= 0) return (error); @@ -1013,52 +1057,68 @@ sioioctl(dev, cmd, data, flag, p) outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); break; case TIOCSDTR: - (void) commctl(com, MCR_DTR, DMBIS); + commctl(com, MCR_DTR, DMBIS); break; case TIOCCDTR: - (void) commctl(com, MCR_DTR, DMBIC); + commctl(com, MCR_DTR, DMBIC); break; case TIOCMSET: - (void) commctl(com, tiocm2mcr(*(int *)data), DMSET); + commctl(com, tiocm_xxx2mcr(*(int *)data), DMSET); break; case TIOCMBIS: - (void) commctl(com, tiocm2mcr(*(int *)data), DMBIS); + commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIS); break; case TIOCMBIC: - (void) commctl(com, tiocm2mcr(*(int *)data), DMBIC); + commctl(com, tiocm_xxx2mcr(*(int *)data), DMBIC); break; case TIOCMGET: - { - register int bits = 0, mode; - - mode = commctl(com, 0, DMGET); - if (inb(com->iobase+com_ier)) bits |= TIOCM_LE; /* XXX */ - if (mode & MSR_DCD) bits |= TIOCM_CD; - if (mode & MSR_CTS) bits |= TIOCM_CTS; - if (mode & MSR_DSR) bits |= TIOCM_DSR; - if (mode & (MCR_DTR<<8)) bits |= TIOCM_DTR; - if (mode & (MCR_RTS<<8)) bits |= TIOCM_RTS; - if (mode & (MSR_RI|MSR_TERI)) bits |= TIOCM_RI; - *(int *)data = bits; - } + tiocm_xxx = TIOCM_LE; /* XXX - always enabled while open */ + mcr = com->mcr_image; + if (mcr & MCR_DTR) + tiocm_xxx |= TIOCM_DTR; + if (mcr & MCR_RTS) + tiocm_xxx |= TIOCM_RTS; + msr = com->prev_modem_status; + if (msr & MSR_CTS) + tiocm_xxx |= TIOCM_CTS; + if (msr & MSR_DCD) + tiocm_xxx |= TIOCM_CD; + if (msr & MSR_DSR) + tiocm_xxx |= 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)) + tiocm_xxx |= TIOCM_RI; + *(int *)data = tiocm_xxx; break; #ifdef COM_BIDIR case TIOCMSBIDIR: /* must be root to set bidir. capability */ - if (p->p_ucred->cr_uid != 0) + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) { + splx(s); return(EPERM); + } - /* if it's the console, can't do it */ - if (UNIT(dev) == comconsole) + /* if it's the console, can't do it (XXX why?) */ + if (UNIT(dev) == comconsole) { + splx(s); return(ENOTTY); + } - /* can't do the next, for obvious reasons... +#if 0 + /* XXX - 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); */ + if (com->active) { + splx(s); + return(EBUSY); + } +#endif com->bidir = *(int *)data; break; @@ -1066,6 +1126,25 @@ sioioctl(dev, cmd, data, flag, p) *(int *)data = com->bidir; break; #endif /* COM_BIDIR */ + 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(EPERM); + } + + /* if it's the console, can't do it (XXX why?) */ + if (UNIT(dev) == comconsole) { + splx(s); + return(ENOTTY); + } + + com->dtr_wait = *(int *)data; + break; + case TIOCMGDTRWAIT: + *(int *)data = com->dtr_wait; + break; default: splx(s); return (ENOTTY); @@ -1093,11 +1172,10 @@ comflush(com) com->tp->t_state &= ~TS_BUSY; } -static void -compoll() +void +siopoll() { static bool_t awake = FALSE; - struct com_s *com; int s; int unit; @@ -1113,7 +1191,8 @@ compoll() s = spltty(); repeat: for (unit = 0; unit < NSIO; ++unit) { - u_char *buf; + u_char *buf; + struct com_s *com; u_char *ibuf; int incc; struct tty *tp; @@ -1124,9 +1203,10 @@ repeat: tp = com->tp; /* switch the role of the low-level input buffers */ - if (com->iptr == (ibuf = com->ibuf)) + if (com->iptr == (ibuf = com->ibuf)) { + buf = NULL; /* not used, but compiler can't tell */ incc = 0; - else { + } else { buf = ibuf; disable_intr(); incc = com->iptr - buf; @@ -1145,7 +1225,7 @@ repeat: * there is room in the high-level buffer. */ if (!(com->mcr_image & MCR_RTS) - && !(tp->t_state & TS_RTSBLOCK)) + && !(tp->t_state & TS_RTS_IFLOW)) outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); enable_intr(); @@ -1162,49 +1242,33 @@ repeat: com_events -= LOTS_OF_EVENTS; com->state &= ~CS_CHECKMSR; enable_intr(); - if (delta_modem_status & MSR_DCD && - unit != comconsole) { -#ifdef COM_BIDIR + if (delta_modem_status & MSR_DCD && !FAKE_DCD(unit)) { if (com->prev_modem_status & MSR_DCD) { (*linesw[tp->t_line].l_modem)(tp, 1); - com->softDCD = FALSE; +#ifdef COM_BIDIR 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); - enable_intr(); - } + } else + (*linesw[tp->t_line].l_modem)(tp, 0); } } /* XXX */ if (TRUE) { u_int delta; - u_int delta_error_counts[CE_NTYPES]; int errnum; u_long total; + for (errnum = 0; errnum < CE_NTYPES; ++errnum) { disable_intr(); - bcopy(com->delta_error_counts, delta_error_counts, - sizeof delta_error_counts); - bzero(com->delta_error_counts, - sizeof delta_error_counts); + delta = com->delta_error_counts[errnum]; + com->delta_error_counts[errnum] = 0; 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", + "sio%d: %u more %s%s (total %lu)\n", unit, delta, error_desc[errnum], delta == 1 ? "" : "s", total); } @@ -1222,13 +1286,13 @@ repeat: continue; if (com->state & CS_RTS_IFLOW && RB_LEN(&tp->t_raw) + incc >= RB_I_HIGH_WATER - && !(tp->t_state & TS_RTSBLOCK) + && !(tp->t_state & TS_RTS_IFLOW) /* * 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; + tp->t_state |= TS_RTS_IFLOW; ttstart(tp); } /* @@ -1258,8 +1322,7 @@ repeat: tp->t_lflag &= ~FLUSHO; ttstart(tp); } - } - else { + } else { do { u_char line_status; int recv_data; @@ -1305,7 +1368,9 @@ comparam(tp, t) /* check requested parameters */ divisor = ttspeedtab(t->c_ospeed, comspeedtab); - if (divisor < 0 || t->c_ispeed != 0 && t->c_ispeed != t->c_ospeed) + if (t->c_ispeed == 0) + t->c_ispeed = t->c_ospeed; + if (divisor < 0 || t->c_ispeed != t->c_ospeed) return (EINVAL); /* parameters are OK, convert them to the com struct and the device */ @@ -1313,11 +1378,10 @@ comparam(tp, t) com = com_addr(unit); iobase = com->iobase; s = spltty(); - if (divisor == 0) { - (void) commctl(com, MCR_RTS, DMSET); /* hang up line */ - splx(s); - return (0); - } + if (divisor == 0) + commctl(com, MCR_DTR, DMBIC); /* hang up line */ + else + commctl(com, MCR_DTR, DMBIS); cflag = t->c_cflag; switch (cflag & CSIZE) { case CS5: @@ -1350,12 +1414,13 @@ comparam(tp, t) * error bits, but that is acceptable here. */ disable_intr(); +retry: 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); + "sioparam", 1); if (error != 0 && error != EAGAIN) { if (!(tp->t_state & TS_TTSTOP)) { disable_intr(); @@ -1368,9 +1433,22 @@ comparam(tp, t) } 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); + + /* + * 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); + 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; @@ -1383,24 +1461,32 @@ comparam(tp, t) * 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)) + if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; } + /* + * Recover from fiddling with CS_TTGO. We used to call siointr() + * unconditionally, but that defeated the careful discarding of + * stale input in sioopen(). + * + * XXX sioopen() is not careful waiting for carrier for the callout + * case. + */ + if (com->state >= (CS_BUSY | CS_TTGO)) + comintr1(com); + enable_intr(); - siointr(unit); /* recover from fiddling with CS_TTGO */ splx(s); return (0); } -static int /* XXX - should be void */ +static void comstart(tp) struct tty *tp; { @@ -1416,15 +1502,12 @@ comstart(tp) com->state &= ~CS_TTGO; else com->state |= CS_TTGO; - if (tp->t_state & TS_RTSBLOCK) { + if (tp->t_state & TS_RTS_IFLOW) { 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) { - tp->t_state &= ~TS_RTSBLOCK; + } 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)) @@ -1442,22 +1525,20 @@ comstart(tp) } if (com->ocount != 0) { disable_intr(); - siointr(unit); + comintr1(com); enable_intr(); - } - else if (RB_LEN(&tp->t_out) != 0) { + } 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 */ + comintr1(com); /* fake interrupt to start output */ enable_intr(); } out: splx(s); - return (1); } void @@ -1471,6 +1552,10 @@ siostop(tp, rw) if (rw & FWRITE) comflush(com); disable_intr(); + if (rw & FREAD) { + com_events -= (com->iptr - com->ibuf); + com->iptr = com->ibuf; + } if (tp->t_state & TS_TTSTOP) com->state &= ~CS_TTGO; else @@ -1478,7 +1563,20 @@ siostop(tp, rw) enable_intr(); } -static int +int +sioselect(dev, rw, p) + dev_t dev; + int rw; + struct proc *p; +{ +#ifdef COM_BIDIR + return ttselect(dev & ~COM_CALLOUTMASK, rw, p); +#else + return ttselect(dev, rw, p); +#endif +} + +static void commctl(com, bits, how) struct com_s *com; int bits; @@ -1487,90 +1585,55 @@ commctl(com, bits, 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); + outb(com->modem_ctl_port, + com->mcr_image = bits | (com->mcr_image & 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 | (com->mcr_image << 8); + outb(com->modem_ctl_port, com->mcr_image &= ~bits); break; } enable_intr(); - return (bits); } static void -comwakeup() +comwakeup(chan, ticks) + caddr_t chan; + int ticks; { - struct com_s *com; int unit; - timeout((timeout_func_t) comwakeup, (caddr_t) NULL, 1); + timeout(comwakeup, (caddr_t) NULL, hz / 100); if (com_events != 0) - /* schedule compoll() to run when the cpl allows */ - schedsoftcom(); + /* schedule siopoll() to run when the cpl allows */ + setsofttty(); /* recover from lost output interrupts */ for (unit = 0; unit < NSIO; ++unit) { + struct com_s *com; + com = com_addr(unit); if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) { disable_intr(); - siointr(unit); + comintr1(com); enable_intr(); } } + return; } 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(); } +softsio1() +{ + siopoll(); +} /* - * Following are all routines needed for COM to act as console + * Following are all routines needed for SIO to act as console * XXX - not tested in this version + * XXX - i386/cons.c only knows about the com driver (NCOM and not NSIO) * XXX - check that the corresponding serial interrupts are never enabled */ #include "i386/i386/cons.h" @@ -1583,11 +1646,11 @@ siocnprobe(cp) /* locate the major number */ for (commajor = 0; commajor < nchrdev; commajor++) - if (cdevsw[commajor].d_open == (bogus_open_t) sioopen) + if (cdevsw[commajor].d_open == sioopen) break; /* XXX: ick */ - unit = CONUNIT; + unit = UNIT(CONUNIT); com_addr(unit) = &com_structs[unit]; com_addr(unit)->iobase = CONADDR; @@ -1595,7 +1658,6 @@ siocnprobe(cp) /* 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 @@ -1627,17 +1689,21 @@ cominit(unit, rate) 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_dlbl, rate & 0xFF); + outb(iobase + com_dlbh, rate >> 8); outb(iobase + com_cfcr, CFCR_8BITS); + outb(iobase + com_fifo, + FIFO_ENABLE | FIFO_TRIGGER | FIFO_RCV_RST | FIFO_XMT_RST); + DELAY(100); + (void) inb(iobase + com_lsr); + (void) inb(iobase + com_data); + (void) inb(iobase + com_msr); /* * 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); } @@ -1675,7 +1741,7 @@ siocnputc(dev, c) if (dev != kgdb_dev) #endif if (!comconsinit) { - (void) cominit(UNIT(dev), comdefaultrate); + cominit(UNIT(dev), comdefaultrate); comconsinit = TRUE; } /* wait for any pending transmission to finish */ @@ -1692,49 +1758,4 @@ siocnputc(dev, c) 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 */ |
