mirror of
https://github.com/freebsd/freebsd-src
synced 2024-11-05 18:22:52 +00:00
Work around FIFO bug in SMC UARTS.
Fix endless loop in siopoll() for an event on a tty with no tty struct. Don't generate unwanted interrupts in the serial console driver. These bugs probably don't matter unless the tty struct is dynamically allocated. Support polled mode. To use it, leave out the irq and the vector in the config file. It only causes extra overhead for open polled ports. The maximum usable speed is approximately 1000 bps for a 16450 and 15000 bps for a 16550. Other cosmetic changes.
This commit is contained in:
parent
a9db861824
commit
b2f6e97280
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=3548
3 changed files with 258 additions and 147 deletions
|
@ -31,7 +31,7 @@
|
|||
* SUCH DAMAGE.
|
||||
*
|
||||
* from: @(#)com.c 7.5 (Berkeley) 5/16/91
|
||||
* $Id: sio.c,v 1.52 1994/09/13 03:30:31 phk Exp $
|
||||
* $Id: sio.c,v 1.53 1994/09/21 19:39:25 davidg Exp $
|
||||
*/
|
||||
|
||||
#include "sio.h"
|
||||
|
@ -58,6 +58,8 @@
|
|||
#include <sys/malloc.h>
|
||||
#include <sys/syslog.h>
|
||||
|
||||
#include <machine/clock.h>
|
||||
|
||||
#include <i386/isa/icu.h> /* XXX just to get at `imen' */
|
||||
#include <i386/isa/isa.h>
|
||||
#include <i386/isa/isa_device.h>
|
||||
|
@ -172,6 +174,8 @@ struct com_s {
|
|||
#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 */
|
||||
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() */
|
||||
|
@ -211,10 +215,8 @@ struct com_s {
|
|||
struct termios lt_in; /* should be in struct tty */
|
||||
struct termios lt_out;
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
bool_t do_timestamp;
|
||||
struct timeval timestamp;
|
||||
#endif
|
||||
|
||||
u_long bytes_in; /* statistics */
|
||||
u_long bytes_out;
|
||||
|
@ -286,9 +288,7 @@ static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr
|
|||
static struct com_s *p_com_addr[NSIO];
|
||||
#define com_addr(unit) (p_com_addr[unit])
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
static struct timeval intr_timestamp;
|
||||
#endif
|
||||
|
||||
struct isa_driver siodriver = {
|
||||
sioprobe, sioattach, "sio"
|
||||
|
@ -371,9 +371,9 @@ sioprobe(dev)
|
|||
}
|
||||
|
||||
/*
|
||||
* If the port is on a multiport card and has a master port,
|
||||
* initialize the common interrupt control register in the
|
||||
* master and prepare to leave MCR_IENABLE clear in the mcr.
|
||||
* 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.
|
||||
|
@ -385,26 +385,19 @@ sioprobe(dev)
|
|||
idev = find_isadev(isa_devtab_tty, &siodriver,
|
||||
COM_MPMASTER(dev));
|
||||
if (idev == NULL) {
|
||||
printf("sio%d: master device %d not found\n",
|
||||
dev->id_unit, COM_MPMASTER(dev));
|
||||
return (0);
|
||||
}
|
||||
if (idev->id_irq == 0) {
|
||||
printf("sio%d: master device %d irq not configured\n",
|
||||
printf("sio%d: master device %d not configured\n",
|
||||
dev->id_unit, COM_MPMASTER(dev));
|
||||
return (0);
|
||||
}
|
||||
if (!COM_NOTAST4(dev)) {
|
||||
outb(idev->id_iobase + com_scr, 0x80);
|
||||
outb(idev->id_iobase + com_scr,
|
||||
idev->id_irq ? 0x80 : 0);
|
||||
mcr_image = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif /* COM_MULTIPORT */
|
||||
if (idev->id_irq == 0) {
|
||||
printf("sio%d: irq not configured\n", dev->id_unit);
|
||||
return (0);
|
||||
}
|
||||
if (idev->id_irq == 0)
|
||||
mcr_image = 0;
|
||||
|
||||
bzero(failures, sizeof failures);
|
||||
iobase = dev->id_iobase;
|
||||
|
@ -472,7 +465,7 @@ sioprobe(dev)
|
|||
* it's unlikely to do more than allow the null byte out.
|
||||
*/
|
||||
outb(iobase + com_data, 0);
|
||||
DELAY((2 + 1) * 9600 / 10);
|
||||
DELAY((1 + 2) * 9600 / 10);
|
||||
|
||||
/*
|
||||
* Turn off loopback mode so that the interrupt gate works again
|
||||
|
@ -497,6 +490,7 @@ sioprobe(dev)
|
|||
if (idev->id_irq != 0)
|
||||
failures[3] = isa_irq_pending(idev) ? 0 : 1;
|
||||
failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY;
|
||||
if (idev->id_irq != 0)
|
||||
failures[5] = isa_irq_pending(idev) ? 1 : 0;
|
||||
failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
|
||||
|
||||
|
@ -512,6 +506,7 @@ sioprobe(dev)
|
|||
outb(iobase + com_ier, 0);
|
||||
outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */
|
||||
failures[7] = inb(iobase + com_ier);
|
||||
if (idev->id_irq != 0)
|
||||
failures[8] = isa_irq_pending(idev) ? 1 : 0;
|
||||
failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
|
||||
|
||||
|
@ -554,13 +549,15 @@ sioattach(isdp)
|
|||
* 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 a master port.
|
||||
* 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->cfcr_image = CFCR_8BITS;
|
||||
com->dtr_wait = 3 * hz;
|
||||
com->no_irq = isdp->id_irq == 0;
|
||||
com->tx_fifo_size = 1;
|
||||
com->iptr = com->ibuf = com->ibuf1;
|
||||
com->ibufend = com->ibuf1 + RS_IBUFSIZE;
|
||||
|
@ -656,6 +653,8 @@ determined_type: ;
|
|||
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 */
|
||||
printf("\n");
|
||||
|
@ -783,6 +782,7 @@ sioopen(dev, flag, mode, p)
|
|||
? com->it_out : com->it_in;
|
||||
commctl(com, MCR_DTR | MCR_RTS, DMSET);
|
||||
com->ftl_max = com->ftl_init;
|
||||
com->poll = com->no_irq;
|
||||
++com->wopeners;
|
||||
error = comparam(tp, &tp->t_termios);
|
||||
--com->wopeners;
|
||||
|
@ -794,12 +794,30 @@ sioopen(dev, flag, mode, p)
|
|||
ttsetwater(tp);
|
||||
iobase = com->iobase;
|
||||
if (com->hasfifo) {
|
||||
/* Drain fifo. */
|
||||
/*
|
||||
* (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_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST
|
||||
| com->ftl);
|
||||
FIFO_RCV_RST | FIFO_XMT_RST
|
||||
| FIFO_ENABLE | com->ftl);
|
||||
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);
|
||||
|
@ -878,9 +896,8 @@ comhardclose(com)
|
|||
unit = DEV_TO_UNIT(com->tp->t_dev);
|
||||
iobase = com->iobase;
|
||||
s = spltty();
|
||||
#ifdef TIOCTIMESTAMP
|
||||
com->poll = FALSE;
|
||||
com->do_timestamp = 0;
|
||||
#endif
|
||||
outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
|
||||
#ifdef KGDB
|
||||
/* do not disable interrupts or hang up if debugging */
|
||||
|
@ -968,7 +985,6 @@ siodtrwakeup(chan)
|
|||
wakeup(&com->dtr_wait);
|
||||
}
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
/* Interrupt routine for timekeeping purposes */
|
||||
void
|
||||
siointrts(unit)
|
||||
|
@ -977,7 +993,6 @@ siointrts(unit)
|
|||
microtime(&intr_timestamp);
|
||||
siointr(unit);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
siointr(unit)
|
||||
|
@ -1020,11 +1035,9 @@ siointr1(com)
|
|||
u_char *ioptr;
|
||||
u_char recv_data;
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
if (com->do_timestamp)
|
||||
/* XXX a little bloat here... */
|
||||
com->timestamp = intr_timestamp;
|
||||
#endif
|
||||
while (TRUE) {
|
||||
line_status = inb(com->line_status_port);
|
||||
|
||||
|
@ -1318,12 +1331,10 @@ sioioctl(dev, cmd, data, flag, p)
|
|||
case TIOCMGDTRWAIT:
|
||||
*(int *)data = com->dtr_wait;
|
||||
break;
|
||||
#ifdef TIOCTIMESTAMP
|
||||
case TIOCTIMESTAMP:
|
||||
com->do_timestamp = TRUE;
|
||||
*(struct timeval *)data = com->timestamp;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
splx(s);
|
||||
return (ENOTTY);
|
||||
|
@ -1364,8 +1375,27 @@ siopoll()
|
|||
if (com == NULL)
|
||||
continue;
|
||||
tp = com->tp;
|
||||
if (tp == NULL)
|
||||
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)) {
|
||||
|
@ -1445,7 +1475,7 @@ siopoll()
|
|||
if (delta == 0 || !(tp->t_state & TS_ISOPEN))
|
||||
continue;
|
||||
total = com->error_counts[errnum] += delta;
|
||||
log(LOG_WARNING,
|
||||
log(LOG_ERR,
|
||||
"sio%d: %u more %s%s (total %lu)\n",
|
||||
unit, delta, error_desc[errnum],
|
||||
delta == 1 ? "" : "s", total);
|
||||
|
@ -1461,7 +1491,7 @@ siopoll()
|
|||
com->ftl -= FIFO_TRIGGER_DELTA;
|
||||
outb(com->iobase + com_fifo,
|
||||
FIFO_ENABLE | com->ftl);
|
||||
log(LOG_WARNING,
|
||||
log(LOG_DEBUG,
|
||||
"sio%d: reduced fifo trigger level to %d\n",
|
||||
unit,
|
||||
ftl_in_bytes[com->ftl
|
||||
|
@ -1815,7 +1845,7 @@ comwakeup(chan)
|
|||
{
|
||||
int unit;
|
||||
|
||||
timeout(comwakeup, (void *)NULL, hz / 100);
|
||||
timeout(comwakeup, (caddr_t)NULL, hz > 200 ? hz / 200 : 1);
|
||||
|
||||
if (com_events != 0) {
|
||||
int s;
|
||||
|
@ -1826,11 +1856,13 @@ comwakeup(chan)
|
|||
}
|
||||
|
||||
/* recover from lost output interrupts */
|
||||
/* poll any lines that don't use interrupts */
|
||||
for (unit = 0; unit < NSIO; ++unit) {
|
||||
struct com_s *com;
|
||||
|
||||
com = com_addr(unit);
|
||||
if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) {
|
||||
if (com != NULL
|
||||
&& (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) {
|
||||
disable_intr();
|
||||
siointr1(com);
|
||||
enable_intr();
|
||||
|
@ -1898,7 +1930,12 @@ siocnopen(sp)
|
|||
outb(iobase + com_dlbh, (u_int) divisor >> 8);
|
||||
outb(iobase + com_cfcr, CFCR_8BITS);
|
||||
sp->mcr = inb(iobase + com_mcr);
|
||||
outb(iobase + com_mcr, MCR_DTR | MCR_RTS);
|
||||
/*
|
||||
* 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
|
||||
|
@ -1917,7 +1954,7 @@ siocnclose(sp)
|
|||
outb(iobase + com_dlbh, sp->dlbh);
|
||||
outb(iobase + com_cfcr, sp->cfcr);
|
||||
/*
|
||||
* XXX damp oscllations of MCR_DTR and MCR_RTS by not restoring them.
|
||||
* 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);
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
* SUCH DAMAGE.
|
||||
*
|
||||
* from: @(#)com.c 7.5 (Berkeley) 5/16/91
|
||||
* $Id: sio.c,v 1.52 1994/09/13 03:30:31 phk Exp $
|
||||
* $Id: sio.c,v 1.53 1994/09/21 19:39:25 davidg Exp $
|
||||
*/
|
||||
|
||||
#include "sio.h"
|
||||
|
@ -58,6 +58,8 @@
|
|||
#include <sys/malloc.h>
|
||||
#include <sys/syslog.h>
|
||||
|
||||
#include <machine/clock.h>
|
||||
|
||||
#include <i386/isa/icu.h> /* XXX just to get at `imen' */
|
||||
#include <i386/isa/isa.h>
|
||||
#include <i386/isa/isa_device.h>
|
||||
|
@ -172,6 +174,8 @@ struct com_s {
|
|||
#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 */
|
||||
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() */
|
||||
|
@ -211,10 +215,8 @@ struct com_s {
|
|||
struct termios lt_in; /* should be in struct tty */
|
||||
struct termios lt_out;
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
bool_t do_timestamp;
|
||||
struct timeval timestamp;
|
||||
#endif
|
||||
|
||||
u_long bytes_in; /* statistics */
|
||||
u_long bytes_out;
|
||||
|
@ -286,9 +288,7 @@ static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr
|
|||
static struct com_s *p_com_addr[NSIO];
|
||||
#define com_addr(unit) (p_com_addr[unit])
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
static struct timeval intr_timestamp;
|
||||
#endif
|
||||
|
||||
struct isa_driver siodriver = {
|
||||
sioprobe, sioattach, "sio"
|
||||
|
@ -371,9 +371,9 @@ sioprobe(dev)
|
|||
}
|
||||
|
||||
/*
|
||||
* If the port is on a multiport card and has a master port,
|
||||
* initialize the common interrupt control register in the
|
||||
* master and prepare to leave MCR_IENABLE clear in the mcr.
|
||||
* 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.
|
||||
|
@ -385,26 +385,19 @@ sioprobe(dev)
|
|||
idev = find_isadev(isa_devtab_tty, &siodriver,
|
||||
COM_MPMASTER(dev));
|
||||
if (idev == NULL) {
|
||||
printf("sio%d: master device %d not found\n",
|
||||
dev->id_unit, COM_MPMASTER(dev));
|
||||
return (0);
|
||||
}
|
||||
if (idev->id_irq == 0) {
|
||||
printf("sio%d: master device %d irq not configured\n",
|
||||
printf("sio%d: master device %d not configured\n",
|
||||
dev->id_unit, COM_MPMASTER(dev));
|
||||
return (0);
|
||||
}
|
||||
if (!COM_NOTAST4(dev)) {
|
||||
outb(idev->id_iobase + com_scr, 0x80);
|
||||
outb(idev->id_iobase + com_scr,
|
||||
idev->id_irq ? 0x80 : 0);
|
||||
mcr_image = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif /* COM_MULTIPORT */
|
||||
if (idev->id_irq == 0) {
|
||||
printf("sio%d: irq not configured\n", dev->id_unit);
|
||||
return (0);
|
||||
}
|
||||
if (idev->id_irq == 0)
|
||||
mcr_image = 0;
|
||||
|
||||
bzero(failures, sizeof failures);
|
||||
iobase = dev->id_iobase;
|
||||
|
@ -472,7 +465,7 @@ sioprobe(dev)
|
|||
* it's unlikely to do more than allow the null byte out.
|
||||
*/
|
||||
outb(iobase + com_data, 0);
|
||||
DELAY((2 + 1) * 9600 / 10);
|
||||
DELAY((1 + 2) * 9600 / 10);
|
||||
|
||||
/*
|
||||
* Turn off loopback mode so that the interrupt gate works again
|
||||
|
@ -497,6 +490,7 @@ sioprobe(dev)
|
|||
if (idev->id_irq != 0)
|
||||
failures[3] = isa_irq_pending(idev) ? 0 : 1;
|
||||
failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY;
|
||||
if (idev->id_irq != 0)
|
||||
failures[5] = isa_irq_pending(idev) ? 1 : 0;
|
||||
failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
|
||||
|
||||
|
@ -512,6 +506,7 @@ sioprobe(dev)
|
|||
outb(iobase + com_ier, 0);
|
||||
outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */
|
||||
failures[7] = inb(iobase + com_ier);
|
||||
if (idev->id_irq != 0)
|
||||
failures[8] = isa_irq_pending(idev) ? 1 : 0;
|
||||
failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
|
||||
|
||||
|
@ -554,13 +549,15 @@ sioattach(isdp)
|
|||
* 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 a master port.
|
||||
* 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->cfcr_image = CFCR_8BITS;
|
||||
com->dtr_wait = 3 * hz;
|
||||
com->no_irq = isdp->id_irq == 0;
|
||||
com->tx_fifo_size = 1;
|
||||
com->iptr = com->ibuf = com->ibuf1;
|
||||
com->ibufend = com->ibuf1 + RS_IBUFSIZE;
|
||||
|
@ -656,6 +653,8 @@ determined_type: ;
|
|||
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 */
|
||||
printf("\n");
|
||||
|
@ -783,6 +782,7 @@ sioopen(dev, flag, mode, p)
|
|||
? com->it_out : com->it_in;
|
||||
commctl(com, MCR_DTR | MCR_RTS, DMSET);
|
||||
com->ftl_max = com->ftl_init;
|
||||
com->poll = com->no_irq;
|
||||
++com->wopeners;
|
||||
error = comparam(tp, &tp->t_termios);
|
||||
--com->wopeners;
|
||||
|
@ -794,12 +794,30 @@ sioopen(dev, flag, mode, p)
|
|||
ttsetwater(tp);
|
||||
iobase = com->iobase;
|
||||
if (com->hasfifo) {
|
||||
/* Drain fifo. */
|
||||
/*
|
||||
* (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_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST
|
||||
| com->ftl);
|
||||
FIFO_RCV_RST | FIFO_XMT_RST
|
||||
| FIFO_ENABLE | com->ftl);
|
||||
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);
|
||||
|
@ -878,9 +896,8 @@ comhardclose(com)
|
|||
unit = DEV_TO_UNIT(com->tp->t_dev);
|
||||
iobase = com->iobase;
|
||||
s = spltty();
|
||||
#ifdef TIOCTIMESTAMP
|
||||
com->poll = FALSE;
|
||||
com->do_timestamp = 0;
|
||||
#endif
|
||||
outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
|
||||
#ifdef KGDB
|
||||
/* do not disable interrupts or hang up if debugging */
|
||||
|
@ -968,7 +985,6 @@ siodtrwakeup(chan)
|
|||
wakeup(&com->dtr_wait);
|
||||
}
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
/* Interrupt routine for timekeeping purposes */
|
||||
void
|
||||
siointrts(unit)
|
||||
|
@ -977,7 +993,6 @@ siointrts(unit)
|
|||
microtime(&intr_timestamp);
|
||||
siointr(unit);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
siointr(unit)
|
||||
|
@ -1020,11 +1035,9 @@ siointr1(com)
|
|||
u_char *ioptr;
|
||||
u_char recv_data;
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
if (com->do_timestamp)
|
||||
/* XXX a little bloat here... */
|
||||
com->timestamp = intr_timestamp;
|
||||
#endif
|
||||
while (TRUE) {
|
||||
line_status = inb(com->line_status_port);
|
||||
|
||||
|
@ -1318,12 +1331,10 @@ sioioctl(dev, cmd, data, flag, p)
|
|||
case TIOCMGDTRWAIT:
|
||||
*(int *)data = com->dtr_wait;
|
||||
break;
|
||||
#ifdef TIOCTIMESTAMP
|
||||
case TIOCTIMESTAMP:
|
||||
com->do_timestamp = TRUE;
|
||||
*(struct timeval *)data = com->timestamp;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
splx(s);
|
||||
return (ENOTTY);
|
||||
|
@ -1364,8 +1375,27 @@ siopoll()
|
|||
if (com == NULL)
|
||||
continue;
|
||||
tp = com->tp;
|
||||
if (tp == NULL)
|
||||
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)) {
|
||||
|
@ -1445,7 +1475,7 @@ siopoll()
|
|||
if (delta == 0 || !(tp->t_state & TS_ISOPEN))
|
||||
continue;
|
||||
total = com->error_counts[errnum] += delta;
|
||||
log(LOG_WARNING,
|
||||
log(LOG_ERR,
|
||||
"sio%d: %u more %s%s (total %lu)\n",
|
||||
unit, delta, error_desc[errnum],
|
||||
delta == 1 ? "" : "s", total);
|
||||
|
@ -1461,7 +1491,7 @@ siopoll()
|
|||
com->ftl -= FIFO_TRIGGER_DELTA;
|
||||
outb(com->iobase + com_fifo,
|
||||
FIFO_ENABLE | com->ftl);
|
||||
log(LOG_WARNING,
|
||||
log(LOG_DEBUG,
|
||||
"sio%d: reduced fifo trigger level to %d\n",
|
||||
unit,
|
||||
ftl_in_bytes[com->ftl
|
||||
|
@ -1815,7 +1845,7 @@ comwakeup(chan)
|
|||
{
|
||||
int unit;
|
||||
|
||||
timeout(comwakeup, (void *)NULL, hz / 100);
|
||||
timeout(comwakeup, (caddr_t)NULL, hz > 200 ? hz / 200 : 1);
|
||||
|
||||
if (com_events != 0) {
|
||||
int s;
|
||||
|
@ -1826,11 +1856,13 @@ comwakeup(chan)
|
|||
}
|
||||
|
||||
/* recover from lost output interrupts */
|
||||
/* poll any lines that don't use interrupts */
|
||||
for (unit = 0; unit < NSIO; ++unit) {
|
||||
struct com_s *com;
|
||||
|
||||
com = com_addr(unit);
|
||||
if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) {
|
||||
if (com != NULL
|
||||
&& (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) {
|
||||
disable_intr();
|
||||
siointr1(com);
|
||||
enable_intr();
|
||||
|
@ -1898,7 +1930,12 @@ siocnopen(sp)
|
|||
outb(iobase + com_dlbh, (u_int) divisor >> 8);
|
||||
outb(iobase + com_cfcr, CFCR_8BITS);
|
||||
sp->mcr = inb(iobase + com_mcr);
|
||||
outb(iobase + com_mcr, MCR_DTR | MCR_RTS);
|
||||
/*
|
||||
* 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
|
||||
|
@ -1917,7 +1954,7 @@ siocnclose(sp)
|
|||
outb(iobase + com_dlbh, sp->dlbh);
|
||||
outb(iobase + com_cfcr, sp->cfcr);
|
||||
/*
|
||||
* XXX damp oscllations of MCR_DTR and MCR_RTS by not restoring them.
|
||||
* 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);
|
||||
|
|
117
sys/isa/sio.c
117
sys/isa/sio.c
|
@ -31,7 +31,7 @@
|
|||
* SUCH DAMAGE.
|
||||
*
|
||||
* from: @(#)com.c 7.5 (Berkeley) 5/16/91
|
||||
* $Id: sio.c,v 1.52 1994/09/13 03:30:31 phk Exp $
|
||||
* $Id: sio.c,v 1.53 1994/09/21 19:39:25 davidg Exp $
|
||||
*/
|
||||
|
||||
#include "sio.h"
|
||||
|
@ -58,6 +58,8 @@
|
|||
#include <sys/malloc.h>
|
||||
#include <sys/syslog.h>
|
||||
|
||||
#include <machine/clock.h>
|
||||
|
||||
#include <i386/isa/icu.h> /* XXX just to get at `imen' */
|
||||
#include <i386/isa/isa.h>
|
||||
#include <i386/isa/isa_device.h>
|
||||
|
@ -172,6 +174,8 @@ struct com_s {
|
|||
#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 */
|
||||
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() */
|
||||
|
@ -211,10 +215,8 @@ struct com_s {
|
|||
struct termios lt_in; /* should be in struct tty */
|
||||
struct termios lt_out;
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
bool_t do_timestamp;
|
||||
struct timeval timestamp;
|
||||
#endif
|
||||
|
||||
u_long bytes_in; /* statistics */
|
||||
u_long bytes_out;
|
||||
|
@ -286,9 +288,7 @@ static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr
|
|||
static struct com_s *p_com_addr[NSIO];
|
||||
#define com_addr(unit) (p_com_addr[unit])
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
static struct timeval intr_timestamp;
|
||||
#endif
|
||||
|
||||
struct isa_driver siodriver = {
|
||||
sioprobe, sioattach, "sio"
|
||||
|
@ -371,9 +371,9 @@ sioprobe(dev)
|
|||
}
|
||||
|
||||
/*
|
||||
* If the port is on a multiport card and has a master port,
|
||||
* initialize the common interrupt control register in the
|
||||
* master and prepare to leave MCR_IENABLE clear in the mcr.
|
||||
* 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.
|
||||
|
@ -385,26 +385,19 @@ sioprobe(dev)
|
|||
idev = find_isadev(isa_devtab_tty, &siodriver,
|
||||
COM_MPMASTER(dev));
|
||||
if (idev == NULL) {
|
||||
printf("sio%d: master device %d not found\n",
|
||||
dev->id_unit, COM_MPMASTER(dev));
|
||||
return (0);
|
||||
}
|
||||
if (idev->id_irq == 0) {
|
||||
printf("sio%d: master device %d irq not configured\n",
|
||||
printf("sio%d: master device %d not configured\n",
|
||||
dev->id_unit, COM_MPMASTER(dev));
|
||||
return (0);
|
||||
}
|
||||
if (!COM_NOTAST4(dev)) {
|
||||
outb(idev->id_iobase + com_scr, 0x80);
|
||||
outb(idev->id_iobase + com_scr,
|
||||
idev->id_irq ? 0x80 : 0);
|
||||
mcr_image = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif /* COM_MULTIPORT */
|
||||
if (idev->id_irq == 0) {
|
||||
printf("sio%d: irq not configured\n", dev->id_unit);
|
||||
return (0);
|
||||
}
|
||||
if (idev->id_irq == 0)
|
||||
mcr_image = 0;
|
||||
|
||||
bzero(failures, sizeof failures);
|
||||
iobase = dev->id_iobase;
|
||||
|
@ -472,7 +465,7 @@ sioprobe(dev)
|
|||
* it's unlikely to do more than allow the null byte out.
|
||||
*/
|
||||
outb(iobase + com_data, 0);
|
||||
DELAY((2 + 1) * 9600 / 10);
|
||||
DELAY((1 + 2) * 9600 / 10);
|
||||
|
||||
/*
|
||||
* Turn off loopback mode so that the interrupt gate works again
|
||||
|
@ -497,6 +490,7 @@ sioprobe(dev)
|
|||
if (idev->id_irq != 0)
|
||||
failures[3] = isa_irq_pending(idev) ? 0 : 1;
|
||||
failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY;
|
||||
if (idev->id_irq != 0)
|
||||
failures[5] = isa_irq_pending(idev) ? 1 : 0;
|
||||
failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
|
||||
|
||||
|
@ -512,6 +506,7 @@ sioprobe(dev)
|
|||
outb(iobase + com_ier, 0);
|
||||
outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */
|
||||
failures[7] = inb(iobase + com_ier);
|
||||
if (idev->id_irq != 0)
|
||||
failures[8] = isa_irq_pending(idev) ? 1 : 0;
|
||||
failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND;
|
||||
|
||||
|
@ -554,13 +549,15 @@ sioattach(isdp)
|
|||
* 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 a master port.
|
||||
* 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->cfcr_image = CFCR_8BITS;
|
||||
com->dtr_wait = 3 * hz;
|
||||
com->no_irq = isdp->id_irq == 0;
|
||||
com->tx_fifo_size = 1;
|
||||
com->iptr = com->ibuf = com->ibuf1;
|
||||
com->ibufend = com->ibuf1 + RS_IBUFSIZE;
|
||||
|
@ -656,6 +653,8 @@ determined_type: ;
|
|||
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 */
|
||||
printf("\n");
|
||||
|
@ -783,6 +782,7 @@ sioopen(dev, flag, mode, p)
|
|||
? com->it_out : com->it_in;
|
||||
commctl(com, MCR_DTR | MCR_RTS, DMSET);
|
||||
com->ftl_max = com->ftl_init;
|
||||
com->poll = com->no_irq;
|
||||
++com->wopeners;
|
||||
error = comparam(tp, &tp->t_termios);
|
||||
--com->wopeners;
|
||||
|
@ -794,12 +794,30 @@ sioopen(dev, flag, mode, p)
|
|||
ttsetwater(tp);
|
||||
iobase = com->iobase;
|
||||
if (com->hasfifo) {
|
||||
/* Drain fifo. */
|
||||
/*
|
||||
* (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_ENABLE | FIFO_RCV_RST | FIFO_XMT_RST
|
||||
| com->ftl);
|
||||
FIFO_RCV_RST | FIFO_XMT_RST
|
||||
| FIFO_ENABLE | com->ftl);
|
||||
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);
|
||||
|
@ -878,9 +896,8 @@ comhardclose(com)
|
|||
unit = DEV_TO_UNIT(com->tp->t_dev);
|
||||
iobase = com->iobase;
|
||||
s = spltty();
|
||||
#ifdef TIOCTIMESTAMP
|
||||
com->poll = FALSE;
|
||||
com->do_timestamp = 0;
|
||||
#endif
|
||||
outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK);
|
||||
#ifdef KGDB
|
||||
/* do not disable interrupts or hang up if debugging */
|
||||
|
@ -968,7 +985,6 @@ siodtrwakeup(chan)
|
|||
wakeup(&com->dtr_wait);
|
||||
}
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
/* Interrupt routine for timekeeping purposes */
|
||||
void
|
||||
siointrts(unit)
|
||||
|
@ -977,7 +993,6 @@ siointrts(unit)
|
|||
microtime(&intr_timestamp);
|
||||
siointr(unit);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
siointr(unit)
|
||||
|
@ -1020,11 +1035,9 @@ siointr1(com)
|
|||
u_char *ioptr;
|
||||
u_char recv_data;
|
||||
|
||||
#ifdef TIOCTIMESTAMP
|
||||
if (com->do_timestamp)
|
||||
/* XXX a little bloat here... */
|
||||
com->timestamp = intr_timestamp;
|
||||
#endif
|
||||
while (TRUE) {
|
||||
line_status = inb(com->line_status_port);
|
||||
|
||||
|
@ -1318,12 +1331,10 @@ sioioctl(dev, cmd, data, flag, p)
|
|||
case TIOCMGDTRWAIT:
|
||||
*(int *)data = com->dtr_wait;
|
||||
break;
|
||||
#ifdef TIOCTIMESTAMP
|
||||
case TIOCTIMESTAMP:
|
||||
com->do_timestamp = TRUE;
|
||||
*(struct timeval *)data = com->timestamp;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
splx(s);
|
||||
return (ENOTTY);
|
||||
|
@ -1364,8 +1375,27 @@ siopoll()
|
|||
if (com == NULL)
|
||||
continue;
|
||||
tp = com->tp;
|
||||
if (tp == NULL)
|
||||
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)) {
|
||||
|
@ -1445,7 +1475,7 @@ siopoll()
|
|||
if (delta == 0 || !(tp->t_state & TS_ISOPEN))
|
||||
continue;
|
||||
total = com->error_counts[errnum] += delta;
|
||||
log(LOG_WARNING,
|
||||
log(LOG_ERR,
|
||||
"sio%d: %u more %s%s (total %lu)\n",
|
||||
unit, delta, error_desc[errnum],
|
||||
delta == 1 ? "" : "s", total);
|
||||
|
@ -1461,7 +1491,7 @@ siopoll()
|
|||
com->ftl -= FIFO_TRIGGER_DELTA;
|
||||
outb(com->iobase + com_fifo,
|
||||
FIFO_ENABLE | com->ftl);
|
||||
log(LOG_WARNING,
|
||||
log(LOG_DEBUG,
|
||||
"sio%d: reduced fifo trigger level to %d\n",
|
||||
unit,
|
||||
ftl_in_bytes[com->ftl
|
||||
|
@ -1815,7 +1845,7 @@ comwakeup(chan)
|
|||
{
|
||||
int unit;
|
||||
|
||||
timeout(comwakeup, (void *)NULL, hz / 100);
|
||||
timeout(comwakeup, (caddr_t)NULL, hz > 200 ? hz / 200 : 1);
|
||||
|
||||
if (com_events != 0) {
|
||||
int s;
|
||||
|
@ -1826,11 +1856,13 @@ comwakeup(chan)
|
|||
}
|
||||
|
||||
/* recover from lost output interrupts */
|
||||
/* poll any lines that don't use interrupts */
|
||||
for (unit = 0; unit < NSIO; ++unit) {
|
||||
struct com_s *com;
|
||||
|
||||
com = com_addr(unit);
|
||||
if (com != NULL && com->state >= (CS_BUSY | CS_TTGO)) {
|
||||
if (com != NULL
|
||||
&& (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) {
|
||||
disable_intr();
|
||||
siointr1(com);
|
||||
enable_intr();
|
||||
|
@ -1898,7 +1930,12 @@ siocnopen(sp)
|
|||
outb(iobase + com_dlbh, (u_int) divisor >> 8);
|
||||
outb(iobase + com_cfcr, CFCR_8BITS);
|
||||
sp->mcr = inb(iobase + com_mcr);
|
||||
outb(iobase + com_mcr, MCR_DTR | MCR_RTS);
|
||||
/*
|
||||
* 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
|
||||
|
@ -1917,7 +1954,7 @@ siocnclose(sp)
|
|||
outb(iobase + com_dlbh, sp->dlbh);
|
||||
outb(iobase + com_cfcr, sp->cfcr);
|
||||
/*
|
||||
* XXX damp oscllations of MCR_DTR and MCR_RTS by not restoring them.
|
||||
* 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);
|
||||
|
|
Loading…
Reference in a new issue