YAMFsio.c (1.227-1.228: set up input buffering dynamically).

This commit is contained in:
Bruce Evans 1999-02-04 15:54:02 +00:00
parent d95f2350b0
commit 89955e6330
3 changed files with 567 additions and 348 deletions

View file

@ -27,7 +27,7 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: cy.c,v 1.84 1999/01/28 01:59:53 dillon Exp $
* $Id: cy.c,v 1.85 1999/01/30 12:17:33 phk Exp $
*/
#include "opt_compat.h"
@ -131,6 +131,7 @@
#define siodevtotty cydevtotty
#define siodriver cydriver
#define siodtrwakeup cydtrwakeup
#define sioinput cyinput
#define siointr cyintr
#define siointr1 cyintr1
#define sioioctl cyioctl
@ -139,6 +140,7 @@
#define sioprobe cyprobe
#define sioread cyread
#define siosettimeout cysettimeout
#define siosetwater cysetwater
#define siostop cystop
#define siowrite cywrite
#define sio_registered cy_registered
@ -164,7 +166,6 @@
#define ETC_BREAK_ENDED 4
#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
#define RS_IBUFSIZE 256
#define CALLOUT_MASK 0x80
#define CONTROL_MASK 0x60
@ -175,18 +176,6 @@
#define MINOR_TO_UNIT(mynor) (((mynor) >> 16) * CY_MAX_PORTS \
| (((mynor) & 0xff) & ~MINOR_MAGIC_MASK))
/*
* Input buffer watermarks.
* The external device is asked to stop sending when the buffer exactly reaches
* high water, or when the high level requests it.
* The high level is notified immediately (rather than at a later clock tick)
* when this watermark is reached.
* The buffer size is chosen so the watermark should almost never be reached.
* The low watermark is invisibly 0 since the buffer is always emptied all at
* once.
*/
#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
/*
* com state bits.
* (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
@ -284,8 +273,11 @@ struct com_s {
u_char hotchar; /* ldisc-specific char to be handled ASAP */
u_char *ibuf; /* start of input buffer */
u_char *ibufend; /* end of input buffer */
u_char *ibufold; /* old input buffer, to be freed */
u_char *ihighwater; /* threshold in input buffer */
u_char *iptr; /* next free spot in input buffer */
int ibufsize; /* size of ibuf (not include error bytes) */
int ierroff; /* offset of error bytes in ibuf */
struct lbq obufq; /* head of queue of output buffers */
struct lbq obufs[2]; /* output buffers */
@ -326,14 +318,6 @@ struct com_s {
u_char cor[3]; /* CD1400 COR1-3 shadows */
u_char intr_enable; /* CD1400 SRER shadow */
/*
* Ping-pong input buffers. The extra factor of 2 in the sizes is
* to allow for an error byte for each input byte.
*/
#define CE_INPUT_OFFSET RS_IBUFSIZE
u_char ibuf1[2 * RS_IBUFSIZE];
u_char ibuf2[2 * RS_IBUFSIZE];
/*
* Data area for output buffers. Someday we should build the output
* buffer queue without copying data.
@ -363,6 +347,7 @@ static int cd_getreg __P((struct com_s *com, int reg));
static void cd_setreg __P((struct com_s *com, int reg, int val));
static timeout_t siodtrwakeup;
static void comhardclose __P((struct com_s *com));
static void sioinput __P((struct com_s *com));
#if 0
static void siointr1 __P((struct com_s *com));
#endif
@ -371,6 +356,7 @@ static int comparam __P((struct tty *tp, struct termios *t));
static swihand_t siopoll;
static int sioprobe __P((struct isa_device *dev));
static void siosettimeout __P((void));
static int siosetwater __P((struct com_s *com, speed_t speed));
static int comspeed __P((speed_t speed, u_long cy_clock,
int *prescaler_io));
static void comstart __P((struct tty *tp));
@ -592,9 +578,6 @@ cyattach_common(cy_iobase, cy_align)
com->mcr_rts_reg = CD1400_MSVR1;
}
com->dtr_wait = 3 * hz;
com->iptr = com->ibuf = com->ibuf1;
com->ibufend = com->ibuf1 + RS_IBUFSIZE;
com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
com->obufs[0].l_head = com->obuf1;
com->obufs[1].l_head = com->obuf2;
@ -620,6 +603,12 @@ cyattach_common(cy_iobase, cy_align)
com->it_in.c_lflag = TTYDEF_LFLAG;
com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;
}
if (siosetwater(com, com->it_in.c_ispeed) != 0) {
enable_intr();
free(com, M_DEVBUF);
return (0);
}
enable_intr();
termioschars(&com->it_in);
com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;
com->it_out = com->it_in;
@ -745,9 +734,6 @@ open_top:
tp->t_dev = dev;
tp->t_termios = mynor & CALLOUT_MASK
? com->it_out : com->it_in;
tp->t_ififosize = 2 * RS_IBUFSIZE;
tp->t_ispeedwat = (speed_t)-1;
tp->t_ospeedwat = (speed_t)-1;
/* Encode per-board unit in LIVR for access in intr routines. */
cd_setreg(com, CD1400_LIVR,
@ -1028,6 +1014,93 @@ siodtrwakeup(chan)
wakeup(&com->dtr_wait);
}
static void
sioinput(com)
struct com_s *com;
{
u_char *buf;
int incc;
u_char line_status;
int recv_data;
struct tty *tp;
buf = com->ibuf;
tp = com->tp;
if (!(tp->t_state & TS_ISOPEN)) {
com_events -= (com->iptr - com->ibuf);
com->iptr = com->ibuf;
return;
}
if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
/*
* 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).
*/
do {
enable_intr();
incc = com->iptr - buf;
if (tp->t_rawq.c_cc + incc > tp->t_ihiwat
&& (com->state & CS_RTS_IFLOW
|| tp->t_iflag & IXOFF)
&& !(tp->t_state & TS_TBLOCK))
ttyblock(tp);
com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+= b_to_q((char *)buf, incc, &tp->t_rawq);
buf += incc;
tk_nin += incc;
tk_rawcc += incc;
tp->t_rawcc += 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;
comstart(tp);
}
disable_intr();
} while (buf < com->iptr);
} else {
do {
enable_intr();
line_status = buf[com->ierroff];
recv_data = *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);
disable_intr();
} while (buf < com->iptr);
}
com_events -= (com->iptr - com->ibuf);
com->iptr = com->ibuf;
/*
* There is now room for another low-level buffer full of input,
* so enable RTS if it is now disabled and there is room in the
* high-level buffer.
*/
if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) &&
!(tp->t_state & TS_TBLOCK))
#if 0
outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
#else
cd_setreg(com, com->mcr_rts_reg,
com->mcr_image |= com->mcr_rts);
#endif
}
void
siointr(unit)
int unit;
@ -1134,7 +1207,7 @@ siointr(unit)
microtime(&com->timestamp);
++com_events;
ioptr[0] = recv_data;
ioptr[CE_INPUT_OFFSET] = line_status;
ioptr[com->ierroff] = line_status;
com->iptr = ++ioptr;
if (ioptr == com->ihighwater
&& com->state & CS_RTS_IFLOW)
@ -1177,7 +1250,7 @@ siointr(unit)
setsofttty();
#endif
ioptr[0] = recv_data;
ioptr[CE_INPUT_OFFSET] = 0;
ioptr[com->ierroff] = 0;
++ioptr;
} while (--ifree != 0);
}
@ -1217,7 +1290,7 @@ siointr(unit)
setsofttty();
#endif
ioptr[0] = recv_data;
ioptr[CE_INPUT_OFFSET] = 0;
ioptr[com->ierroff] = 0;
++ioptr;
} while (--count != 0);
}
@ -1687,9 +1760,7 @@ siopoll()
return;
repeat:
for (unit = 0; unit < NSIO; ++unit) {
u_char *buf;
struct com_s *com;
u_char *ibuf;
int incc;
struct tty *tp;
@ -1718,43 +1789,11 @@ repeat:
unit, incc);
continue;
}
/* switch the role of the low-level input buffers */
if (com->iptr == (ibuf = com->ibuf)) {
buf = NULL; /* not used, but compiler can't tell */
incc = 0;
} else {
buf = ibuf;
if (com->iptr != com->ibuf) {
disable_intr();
incc = com->iptr - buf;
com_events -= incc;
if (ibuf == com->ibuf1)
ibuf = com->ibuf2;
else
ibuf = com->ibuf1;
com->ibufend = ibuf + RS_IBUFSIZE;
com->ihighwater = ibuf + RS_IHIGHWATER;
com->iptr = ibuf;
/*
* There is now room for another low-level buffer full
* of input, so enable RTS if it is now disabled and
* there is room in the high-level buffer.
*/
if ((com->state & CS_RTS_IFLOW)
&& !(com->mcr_image & com->mcr_rts)
&& !(tp->t_state & TS_TBLOCK))
#if 0
outb(com->modem_ctl_port,
com->mcr_image |= MCR_RTS);
#else
cd_setreg(com, com->mcr_rts_reg,
com->mcr_image |= com->mcr_rts);
#endif
sioinput(com);
enable_intr();
com->ibuf = ibuf;
}
if (com->state & CS_CHECKMSR) {
u_char delta_modem_status;
@ -1791,55 +1830,6 @@ repeat:
enable_intr();
(*linesw[tp->t_line].l_start)(tp);
}
if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
continue;
/*
* Avoid the grotesquely inefficient lineswitch routine
* (ttyinput) in "raw" mode. It usually takes about 450
* instructions (that's without canonical processing or echo!).
* slinput is reasonably fast (usually 40 instructions plus
* call overhead).
*/
if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
if (tp->t_rawq.c_cc + incc > tp->t_ihiwat
&& (com->state & CS_RTS_IFLOW
|| tp->t_iflag & IXOFF)
&& !(tp->t_state & TS_TBLOCK))
ttyblock(tp);
tk_nin += incc;
tk_rawcc += incc;
tp->t_rawcc += incc;
com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+= b_to_q((char *)buf, incc, &tp->t_rawq);
ttwakeup(tp);
if (tp->t_state & TS_TTSTOP
&& (tp->t_iflag & IXANY
|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
tp->t_state &= ~TS_TTSTOP;
tp->t_lflag &= ~FLUSHO;
comstart(tp);
}
} else {
do {
u_char line_status;
int recv_data;
line_status = (u_char) buf[CE_INPUT_OFFSET];
recv_data = (u_char) *buf++;
if (line_status
& (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
if (line_status & LSR_BI)
recv_data |= TTY_BI;
if (line_status & LSR_FE)
recv_data |= TTY_FE;
if (line_status & LSR_OE)
recv_data |= TTY_OE;
if (line_status & LSR_PE)
recv_data |= TTY_PE;
}
(*linesw[tp->t_line].l_rint)(recv_data, tp);
} while (--incc > 0);
}
if (com_events == 0)
break;
}
@ -1890,6 +1880,15 @@ comparam(tp, t)
else
(void)commctl(com, TIOCM_DTR, DMBIS);
/*
* This returns with interrupts disabled so that we can complete
* the speed change atomically.
*/
(void) siosetwater(com, t->c_ispeed);
/* XXX we don't actually change the speed atomically. */
enable_intr();
if (idivisor != 0) {
cd_setreg(com, CD1400_RBPR, idivisor);
cd_setreg(com, CD1400_RCOR, iprescaler);
@ -2204,6 +2203,80 @@ comparam(tp, t)
enable_intr();
splx(s);
comstart(tp);
if (com->ibufold != NULL) {
free(com->ibufold, M_DEVBUF);
com->ibufold = NULL;
}
return (0);
}
static int
siosetwater(com, speed)
struct com_s *com;
speed_t speed;
{
int cp4ticks;
u_char *ibuf;
int ibufsize;
struct tty *tp;
/*
* Make the buffer size large enough to handle a softtty interrupt
* latency of about 2 ticks without loss of throughput or data
* (about 3 ticks if input flow control is not used or not honoured,
* but a bit less for CS5-CS7 modes).
*/
cp4ticks = speed / 10 / hz * 4;
for (ibufsize = 128; ibufsize < cp4ticks;)
ibufsize <<= 1;
if (ibufsize == com->ibufsize) {
disable_intr();
return (0);
}
/*
* Allocate input buffer. The extra factor of 2 in the size is
* to allow for an error byte for each input byte.
*/
ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT);
if (ibuf == NULL) {
disable_intr();
return (ENOMEM);
}
/* Initialize non-critical variables. */
com->ibufold = com->ibuf;
com->ibufsize = ibufsize;
tp = com->tp;
if (tp != NULL) {
tp->t_ififosize = 2 * ibufsize;
tp->t_ispeedwat = (speed_t)-1;
tp->t_ospeedwat = (speed_t)-1;
}
/*
* Read current input buffer, if any. Continue with interrupts
* disabled.
*/
disable_intr();
if (com->iptr != com->ibuf)
sioinput(com);
/*-
* Initialize critical variables, including 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.
*/
com->iptr = com->ibuf = ibuf;
com->ibufend = ibuf + ibufsize;
com->ierroff = ibufsize;
com->ihighwater = ibuf + 3 * ibufsize / 4;
return (0);
}

View file

@ -27,7 +27,7 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: cy.c,v 1.84 1999/01/28 01:59:53 dillon Exp $
* $Id: cy.c,v 1.85 1999/01/30 12:17:33 phk Exp $
*/
#include "opt_compat.h"
@ -131,6 +131,7 @@
#define siodevtotty cydevtotty
#define siodriver cydriver
#define siodtrwakeup cydtrwakeup
#define sioinput cyinput
#define siointr cyintr
#define siointr1 cyintr1
#define sioioctl cyioctl
@ -139,6 +140,7 @@
#define sioprobe cyprobe
#define sioread cyread
#define siosettimeout cysettimeout
#define siosetwater cysetwater
#define siostop cystop
#define siowrite cywrite
#define sio_registered cy_registered
@ -164,7 +166,6 @@
#define ETC_BREAK_ENDED 4
#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
#define RS_IBUFSIZE 256
#define CALLOUT_MASK 0x80
#define CONTROL_MASK 0x60
@ -175,18 +176,6 @@
#define MINOR_TO_UNIT(mynor) (((mynor) >> 16) * CY_MAX_PORTS \
| (((mynor) & 0xff) & ~MINOR_MAGIC_MASK))
/*
* Input buffer watermarks.
* The external device is asked to stop sending when the buffer exactly reaches
* high water, or when the high level requests it.
* The high level is notified immediately (rather than at a later clock tick)
* when this watermark is reached.
* The buffer size is chosen so the watermark should almost never be reached.
* The low watermark is invisibly 0 since the buffer is always emptied all at
* once.
*/
#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
/*
* com state bits.
* (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
@ -284,8 +273,11 @@ struct com_s {
u_char hotchar; /* ldisc-specific char to be handled ASAP */
u_char *ibuf; /* start of input buffer */
u_char *ibufend; /* end of input buffer */
u_char *ibufold; /* old input buffer, to be freed */
u_char *ihighwater; /* threshold in input buffer */
u_char *iptr; /* next free spot in input buffer */
int ibufsize; /* size of ibuf (not include error bytes) */
int ierroff; /* offset of error bytes in ibuf */
struct lbq obufq; /* head of queue of output buffers */
struct lbq obufs[2]; /* output buffers */
@ -326,14 +318,6 @@ struct com_s {
u_char cor[3]; /* CD1400 COR1-3 shadows */
u_char intr_enable; /* CD1400 SRER shadow */
/*
* Ping-pong input buffers. The extra factor of 2 in the sizes is
* to allow for an error byte for each input byte.
*/
#define CE_INPUT_OFFSET RS_IBUFSIZE
u_char ibuf1[2 * RS_IBUFSIZE];
u_char ibuf2[2 * RS_IBUFSIZE];
/*
* Data area for output buffers. Someday we should build the output
* buffer queue without copying data.
@ -363,6 +347,7 @@ static int cd_getreg __P((struct com_s *com, int reg));
static void cd_setreg __P((struct com_s *com, int reg, int val));
static timeout_t siodtrwakeup;
static void comhardclose __P((struct com_s *com));
static void sioinput __P((struct com_s *com));
#if 0
static void siointr1 __P((struct com_s *com));
#endif
@ -371,6 +356,7 @@ static int comparam __P((struct tty *tp, struct termios *t));
static swihand_t siopoll;
static int sioprobe __P((struct isa_device *dev));
static void siosettimeout __P((void));
static int siosetwater __P((struct com_s *com, speed_t speed));
static int comspeed __P((speed_t speed, u_long cy_clock,
int *prescaler_io));
static void comstart __P((struct tty *tp));
@ -592,9 +578,6 @@ cyattach_common(cy_iobase, cy_align)
com->mcr_rts_reg = CD1400_MSVR1;
}
com->dtr_wait = 3 * hz;
com->iptr = com->ibuf = com->ibuf1;
com->ibufend = com->ibuf1 + RS_IBUFSIZE;
com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
com->obufs[0].l_head = com->obuf1;
com->obufs[1].l_head = com->obuf2;
@ -620,6 +603,12 @@ cyattach_common(cy_iobase, cy_align)
com->it_in.c_lflag = TTYDEF_LFLAG;
com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;
}
if (siosetwater(com, com->it_in.c_ispeed) != 0) {
enable_intr();
free(com, M_DEVBUF);
return (0);
}
enable_intr();
termioschars(&com->it_in);
com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;
com->it_out = com->it_in;
@ -745,9 +734,6 @@ open_top:
tp->t_dev = dev;
tp->t_termios = mynor & CALLOUT_MASK
? com->it_out : com->it_in;
tp->t_ififosize = 2 * RS_IBUFSIZE;
tp->t_ispeedwat = (speed_t)-1;
tp->t_ospeedwat = (speed_t)-1;
/* Encode per-board unit in LIVR for access in intr routines. */
cd_setreg(com, CD1400_LIVR,
@ -1028,6 +1014,93 @@ siodtrwakeup(chan)
wakeup(&com->dtr_wait);
}
static void
sioinput(com)
struct com_s *com;
{
u_char *buf;
int incc;
u_char line_status;
int recv_data;
struct tty *tp;
buf = com->ibuf;
tp = com->tp;
if (!(tp->t_state & TS_ISOPEN)) {
com_events -= (com->iptr - com->ibuf);
com->iptr = com->ibuf;
return;
}
if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
/*
* 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).
*/
do {
enable_intr();
incc = com->iptr - buf;
if (tp->t_rawq.c_cc + incc > tp->t_ihiwat
&& (com->state & CS_RTS_IFLOW
|| tp->t_iflag & IXOFF)
&& !(tp->t_state & TS_TBLOCK))
ttyblock(tp);
com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+= b_to_q((char *)buf, incc, &tp->t_rawq);
buf += incc;
tk_nin += incc;
tk_rawcc += incc;
tp->t_rawcc += 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;
comstart(tp);
}
disable_intr();
} while (buf < com->iptr);
} else {
do {
enable_intr();
line_status = buf[com->ierroff];
recv_data = *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);
disable_intr();
} while (buf < com->iptr);
}
com_events -= (com->iptr - com->ibuf);
com->iptr = com->ibuf;
/*
* There is now room for another low-level buffer full of input,
* so enable RTS if it is now disabled and there is room in the
* high-level buffer.
*/
if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) &&
!(tp->t_state & TS_TBLOCK))
#if 0
outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
#else
cd_setreg(com, com->mcr_rts_reg,
com->mcr_image |= com->mcr_rts);
#endif
}
void
siointr(unit)
int unit;
@ -1134,7 +1207,7 @@ siointr(unit)
microtime(&com->timestamp);
++com_events;
ioptr[0] = recv_data;
ioptr[CE_INPUT_OFFSET] = line_status;
ioptr[com->ierroff] = line_status;
com->iptr = ++ioptr;
if (ioptr == com->ihighwater
&& com->state & CS_RTS_IFLOW)
@ -1177,7 +1250,7 @@ siointr(unit)
setsofttty();
#endif
ioptr[0] = recv_data;
ioptr[CE_INPUT_OFFSET] = 0;
ioptr[com->ierroff] = 0;
++ioptr;
} while (--ifree != 0);
}
@ -1217,7 +1290,7 @@ siointr(unit)
setsofttty();
#endif
ioptr[0] = recv_data;
ioptr[CE_INPUT_OFFSET] = 0;
ioptr[com->ierroff] = 0;
++ioptr;
} while (--count != 0);
}
@ -1687,9 +1760,7 @@ siopoll()
return;
repeat:
for (unit = 0; unit < NSIO; ++unit) {
u_char *buf;
struct com_s *com;
u_char *ibuf;
int incc;
struct tty *tp;
@ -1718,43 +1789,11 @@ repeat:
unit, incc);
continue;
}
/* switch the role of the low-level input buffers */
if (com->iptr == (ibuf = com->ibuf)) {
buf = NULL; /* not used, but compiler can't tell */
incc = 0;
} else {
buf = ibuf;
if (com->iptr != com->ibuf) {
disable_intr();
incc = com->iptr - buf;
com_events -= incc;
if (ibuf == com->ibuf1)
ibuf = com->ibuf2;
else
ibuf = com->ibuf1;
com->ibufend = ibuf + RS_IBUFSIZE;
com->ihighwater = ibuf + RS_IHIGHWATER;
com->iptr = ibuf;
/*
* There is now room for another low-level buffer full
* of input, so enable RTS if it is now disabled and
* there is room in the high-level buffer.
*/
if ((com->state & CS_RTS_IFLOW)
&& !(com->mcr_image & com->mcr_rts)
&& !(tp->t_state & TS_TBLOCK))
#if 0
outb(com->modem_ctl_port,
com->mcr_image |= MCR_RTS);
#else
cd_setreg(com, com->mcr_rts_reg,
com->mcr_image |= com->mcr_rts);
#endif
sioinput(com);
enable_intr();
com->ibuf = ibuf;
}
if (com->state & CS_CHECKMSR) {
u_char delta_modem_status;
@ -1791,55 +1830,6 @@ repeat:
enable_intr();
(*linesw[tp->t_line].l_start)(tp);
}
if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
continue;
/*
* Avoid the grotesquely inefficient lineswitch routine
* (ttyinput) in "raw" mode. It usually takes about 450
* instructions (that's without canonical processing or echo!).
* slinput is reasonably fast (usually 40 instructions plus
* call overhead).
*/
if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
if (tp->t_rawq.c_cc + incc > tp->t_ihiwat
&& (com->state & CS_RTS_IFLOW
|| tp->t_iflag & IXOFF)
&& !(tp->t_state & TS_TBLOCK))
ttyblock(tp);
tk_nin += incc;
tk_rawcc += incc;
tp->t_rawcc += incc;
com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+= b_to_q((char *)buf, incc, &tp->t_rawq);
ttwakeup(tp);
if (tp->t_state & TS_TTSTOP
&& (tp->t_iflag & IXANY
|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
tp->t_state &= ~TS_TTSTOP;
tp->t_lflag &= ~FLUSHO;
comstart(tp);
}
} else {
do {
u_char line_status;
int recv_data;
line_status = (u_char) buf[CE_INPUT_OFFSET];
recv_data = (u_char) *buf++;
if (line_status
& (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
if (line_status & LSR_BI)
recv_data |= TTY_BI;
if (line_status & LSR_FE)
recv_data |= TTY_FE;
if (line_status & LSR_OE)
recv_data |= TTY_OE;
if (line_status & LSR_PE)
recv_data |= TTY_PE;
}
(*linesw[tp->t_line].l_rint)(recv_data, tp);
} while (--incc > 0);
}
if (com_events == 0)
break;
}
@ -1890,6 +1880,15 @@ comparam(tp, t)
else
(void)commctl(com, TIOCM_DTR, DMBIS);
/*
* This returns with interrupts disabled so that we can complete
* the speed change atomically.
*/
(void) siosetwater(com, t->c_ispeed);
/* XXX we don't actually change the speed atomically. */
enable_intr();
if (idivisor != 0) {
cd_setreg(com, CD1400_RBPR, idivisor);
cd_setreg(com, CD1400_RCOR, iprescaler);
@ -2204,6 +2203,80 @@ comparam(tp, t)
enable_intr();
splx(s);
comstart(tp);
if (com->ibufold != NULL) {
free(com->ibufold, M_DEVBUF);
com->ibufold = NULL;
}
return (0);
}
static int
siosetwater(com, speed)
struct com_s *com;
speed_t speed;
{
int cp4ticks;
u_char *ibuf;
int ibufsize;
struct tty *tp;
/*
* Make the buffer size large enough to handle a softtty interrupt
* latency of about 2 ticks without loss of throughput or data
* (about 3 ticks if input flow control is not used or not honoured,
* but a bit less for CS5-CS7 modes).
*/
cp4ticks = speed / 10 / hz * 4;
for (ibufsize = 128; ibufsize < cp4ticks;)
ibufsize <<= 1;
if (ibufsize == com->ibufsize) {
disable_intr();
return (0);
}
/*
* Allocate input buffer. The extra factor of 2 in the size is
* to allow for an error byte for each input byte.
*/
ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT);
if (ibuf == NULL) {
disable_intr();
return (ENOMEM);
}
/* Initialize non-critical variables. */
com->ibufold = com->ibuf;
com->ibufsize = ibufsize;
tp = com->tp;
if (tp != NULL) {
tp->t_ififosize = 2 * ibufsize;
tp->t_ispeedwat = (speed_t)-1;
tp->t_ospeedwat = (speed_t)-1;
}
/*
* Read current input buffer, if any. Continue with interrupts
* disabled.
*/
disable_intr();
if (com->iptr != com->ibuf)
sioinput(com);
/*-
* Initialize critical variables, including 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.
*/
com->iptr = com->ibuf = ibuf;
com->ibufend = ibuf + ibufsize;
com->ierroff = ibufsize;
com->ihighwater = ibuf + 3 * ibufsize / 4;
return (0);
}

View file

@ -27,7 +27,7 @@
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: cy.c,v 1.84 1999/01/28 01:59:53 dillon Exp $
* $Id: cy.c,v 1.85 1999/01/30 12:17:33 phk Exp $
*/
#include "opt_compat.h"
@ -131,6 +131,7 @@
#define siodevtotty cydevtotty
#define siodriver cydriver
#define siodtrwakeup cydtrwakeup
#define sioinput cyinput
#define siointr cyintr
#define siointr1 cyintr1
#define sioioctl cyioctl
@ -139,6 +140,7 @@
#define sioprobe cyprobe
#define sioread cyread
#define siosettimeout cysettimeout
#define siosetwater cysetwater
#define siostop cystop
#define siowrite cywrite
#define sio_registered cy_registered
@ -164,7 +166,6 @@
#define ETC_BREAK_ENDED 4
#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */
#define RS_IBUFSIZE 256
#define CALLOUT_MASK 0x80
#define CONTROL_MASK 0x60
@ -175,18 +176,6 @@
#define MINOR_TO_UNIT(mynor) (((mynor) >> 16) * CY_MAX_PORTS \
| (((mynor) & 0xff) & ~MINOR_MAGIC_MASK))
/*
* Input buffer watermarks.
* The external device is asked to stop sending when the buffer exactly reaches
* high water, or when the high level requests it.
* The high level is notified immediately (rather than at a later clock tick)
* when this watermark is reached.
* The buffer size is chosen so the watermark should almost never be reached.
* The low watermark is invisibly 0 since the buffer is always emptied all at
* once.
*/
#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4)
/*
* com state bits.
* (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher
@ -284,8 +273,11 @@ struct com_s {
u_char hotchar; /* ldisc-specific char to be handled ASAP */
u_char *ibuf; /* start of input buffer */
u_char *ibufend; /* end of input buffer */
u_char *ibufold; /* old input buffer, to be freed */
u_char *ihighwater; /* threshold in input buffer */
u_char *iptr; /* next free spot in input buffer */
int ibufsize; /* size of ibuf (not include error bytes) */
int ierroff; /* offset of error bytes in ibuf */
struct lbq obufq; /* head of queue of output buffers */
struct lbq obufs[2]; /* output buffers */
@ -326,14 +318,6 @@ struct com_s {
u_char cor[3]; /* CD1400 COR1-3 shadows */
u_char intr_enable; /* CD1400 SRER shadow */
/*
* Ping-pong input buffers. The extra factor of 2 in the sizes is
* to allow for an error byte for each input byte.
*/
#define CE_INPUT_OFFSET RS_IBUFSIZE
u_char ibuf1[2 * RS_IBUFSIZE];
u_char ibuf2[2 * RS_IBUFSIZE];
/*
* Data area for output buffers. Someday we should build the output
* buffer queue without copying data.
@ -363,6 +347,7 @@ static int cd_getreg __P((struct com_s *com, int reg));
static void cd_setreg __P((struct com_s *com, int reg, int val));
static timeout_t siodtrwakeup;
static void comhardclose __P((struct com_s *com));
static void sioinput __P((struct com_s *com));
#if 0
static void siointr1 __P((struct com_s *com));
#endif
@ -371,6 +356,7 @@ static int comparam __P((struct tty *tp, struct termios *t));
static swihand_t siopoll;
static int sioprobe __P((struct isa_device *dev));
static void siosettimeout __P((void));
static int siosetwater __P((struct com_s *com, speed_t speed));
static int comspeed __P((speed_t speed, u_long cy_clock,
int *prescaler_io));
static void comstart __P((struct tty *tp));
@ -592,9 +578,6 @@ cyattach_common(cy_iobase, cy_align)
com->mcr_rts_reg = CD1400_MSVR1;
}
com->dtr_wait = 3 * hz;
com->iptr = com->ibuf = com->ibuf1;
com->ibufend = com->ibuf1 + RS_IBUFSIZE;
com->ihighwater = com->ibuf1 + RS_IHIGHWATER;
com->obufs[0].l_head = com->obuf1;
com->obufs[1].l_head = com->obuf2;
@ -620,6 +603,12 @@ cyattach_common(cy_iobase, cy_align)
com->it_in.c_lflag = TTYDEF_LFLAG;
com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL;
}
if (siosetwater(com, com->it_in.c_ispeed) != 0) {
enable_intr();
free(com, M_DEVBUF);
return (0);
}
enable_intr();
termioschars(&com->it_in);
com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate;
com->it_out = com->it_in;
@ -745,9 +734,6 @@ open_top:
tp->t_dev = dev;
tp->t_termios = mynor & CALLOUT_MASK
? com->it_out : com->it_in;
tp->t_ififosize = 2 * RS_IBUFSIZE;
tp->t_ispeedwat = (speed_t)-1;
tp->t_ospeedwat = (speed_t)-1;
/* Encode per-board unit in LIVR for access in intr routines. */
cd_setreg(com, CD1400_LIVR,
@ -1028,6 +1014,93 @@ siodtrwakeup(chan)
wakeup(&com->dtr_wait);
}
static void
sioinput(com)
struct com_s *com;
{
u_char *buf;
int incc;
u_char line_status;
int recv_data;
struct tty *tp;
buf = com->ibuf;
tp = com->tp;
if (!(tp->t_state & TS_ISOPEN)) {
com_events -= (com->iptr - com->ibuf);
com->iptr = com->ibuf;
return;
}
if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
/*
* 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).
*/
do {
enable_intr();
incc = com->iptr - buf;
if (tp->t_rawq.c_cc + incc > tp->t_ihiwat
&& (com->state & CS_RTS_IFLOW
|| tp->t_iflag & IXOFF)
&& !(tp->t_state & TS_TBLOCK))
ttyblock(tp);
com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+= b_to_q((char *)buf, incc, &tp->t_rawq);
buf += incc;
tk_nin += incc;
tk_rawcc += incc;
tp->t_rawcc += 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;
comstart(tp);
}
disable_intr();
} while (buf < com->iptr);
} else {
do {
enable_intr();
line_status = buf[com->ierroff];
recv_data = *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);
disable_intr();
} while (buf < com->iptr);
}
com_events -= (com->iptr - com->ibuf);
com->iptr = com->ibuf;
/*
* There is now room for another low-level buffer full of input,
* so enable RTS if it is now disabled and there is room in the
* high-level buffer.
*/
if ((com->state & CS_RTS_IFLOW) && !(com->mcr_image & com->mcr_rts) &&
!(tp->t_state & TS_TBLOCK))
#if 0
outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS);
#else
cd_setreg(com, com->mcr_rts_reg,
com->mcr_image |= com->mcr_rts);
#endif
}
void
siointr(unit)
int unit;
@ -1134,7 +1207,7 @@ siointr(unit)
microtime(&com->timestamp);
++com_events;
ioptr[0] = recv_data;
ioptr[CE_INPUT_OFFSET] = line_status;
ioptr[com->ierroff] = line_status;
com->iptr = ++ioptr;
if (ioptr == com->ihighwater
&& com->state & CS_RTS_IFLOW)
@ -1177,7 +1250,7 @@ siointr(unit)
setsofttty();
#endif
ioptr[0] = recv_data;
ioptr[CE_INPUT_OFFSET] = 0;
ioptr[com->ierroff] = 0;
++ioptr;
} while (--ifree != 0);
}
@ -1217,7 +1290,7 @@ siointr(unit)
setsofttty();
#endif
ioptr[0] = recv_data;
ioptr[CE_INPUT_OFFSET] = 0;
ioptr[com->ierroff] = 0;
++ioptr;
} while (--count != 0);
}
@ -1687,9 +1760,7 @@ siopoll()
return;
repeat:
for (unit = 0; unit < NSIO; ++unit) {
u_char *buf;
struct com_s *com;
u_char *ibuf;
int incc;
struct tty *tp;
@ -1718,43 +1789,11 @@ repeat:
unit, incc);
continue;
}
/* switch the role of the low-level input buffers */
if (com->iptr == (ibuf = com->ibuf)) {
buf = NULL; /* not used, but compiler can't tell */
incc = 0;
} else {
buf = ibuf;
if (com->iptr != com->ibuf) {
disable_intr();
incc = com->iptr - buf;
com_events -= incc;
if (ibuf == com->ibuf1)
ibuf = com->ibuf2;
else
ibuf = com->ibuf1;
com->ibufend = ibuf + RS_IBUFSIZE;
com->ihighwater = ibuf + RS_IHIGHWATER;
com->iptr = ibuf;
/*
* There is now room for another low-level buffer full
* of input, so enable RTS if it is now disabled and
* there is room in the high-level buffer.
*/
if ((com->state & CS_RTS_IFLOW)
&& !(com->mcr_image & com->mcr_rts)
&& !(tp->t_state & TS_TBLOCK))
#if 0
outb(com->modem_ctl_port,
com->mcr_image |= MCR_RTS);
#else
cd_setreg(com, com->mcr_rts_reg,
com->mcr_image |= com->mcr_rts);
#endif
sioinput(com);
enable_intr();
com->ibuf = ibuf;
}
if (com->state & CS_CHECKMSR) {
u_char delta_modem_status;
@ -1791,55 +1830,6 @@ repeat:
enable_intr();
(*linesw[tp->t_line].l_start)(tp);
}
if (incc <= 0 || !(tp->t_state & TS_ISOPEN))
continue;
/*
* Avoid the grotesquely inefficient lineswitch routine
* (ttyinput) in "raw" mode. It usually takes about 450
* instructions (that's without canonical processing or echo!).
* slinput is reasonably fast (usually 40 instructions plus
* call overhead).
*/
if (tp->t_state & TS_CAN_BYPASS_L_RINT) {
if (tp->t_rawq.c_cc + incc > tp->t_ihiwat
&& (com->state & CS_RTS_IFLOW
|| tp->t_iflag & IXOFF)
&& !(tp->t_state & TS_TBLOCK))
ttyblock(tp);
tk_nin += incc;
tk_rawcc += incc;
tp->t_rawcc += incc;
com->delta_error_counts[CE_TTY_BUF_OVERFLOW]
+= b_to_q((char *)buf, incc, &tp->t_rawq);
ttwakeup(tp);
if (tp->t_state & TS_TTSTOP
&& (tp->t_iflag & IXANY
|| tp->t_cc[VSTART] == tp->t_cc[VSTOP])) {
tp->t_state &= ~TS_TTSTOP;
tp->t_lflag &= ~FLUSHO;
comstart(tp);
}
} else {
do {
u_char line_status;
int recv_data;
line_status = (u_char) buf[CE_INPUT_OFFSET];
recv_data = (u_char) *buf++;
if (line_status
& (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) {
if (line_status & LSR_BI)
recv_data |= TTY_BI;
if (line_status & LSR_FE)
recv_data |= TTY_FE;
if (line_status & LSR_OE)
recv_data |= TTY_OE;
if (line_status & LSR_PE)
recv_data |= TTY_PE;
}
(*linesw[tp->t_line].l_rint)(recv_data, tp);
} while (--incc > 0);
}
if (com_events == 0)
break;
}
@ -1890,6 +1880,15 @@ comparam(tp, t)
else
(void)commctl(com, TIOCM_DTR, DMBIS);
/*
* This returns with interrupts disabled so that we can complete
* the speed change atomically.
*/
(void) siosetwater(com, t->c_ispeed);
/* XXX we don't actually change the speed atomically. */
enable_intr();
if (idivisor != 0) {
cd_setreg(com, CD1400_RBPR, idivisor);
cd_setreg(com, CD1400_RCOR, iprescaler);
@ -2204,6 +2203,80 @@ comparam(tp, t)
enable_intr();
splx(s);
comstart(tp);
if (com->ibufold != NULL) {
free(com->ibufold, M_DEVBUF);
com->ibufold = NULL;
}
return (0);
}
static int
siosetwater(com, speed)
struct com_s *com;
speed_t speed;
{
int cp4ticks;
u_char *ibuf;
int ibufsize;
struct tty *tp;
/*
* Make the buffer size large enough to handle a softtty interrupt
* latency of about 2 ticks without loss of throughput or data
* (about 3 ticks if input flow control is not used or not honoured,
* but a bit less for CS5-CS7 modes).
*/
cp4ticks = speed / 10 / hz * 4;
for (ibufsize = 128; ibufsize < cp4ticks;)
ibufsize <<= 1;
if (ibufsize == com->ibufsize) {
disable_intr();
return (0);
}
/*
* Allocate input buffer. The extra factor of 2 in the size is
* to allow for an error byte for each input byte.
*/
ibuf = malloc(2 * ibufsize, M_DEVBUF, M_NOWAIT);
if (ibuf == NULL) {
disable_intr();
return (ENOMEM);
}
/* Initialize non-critical variables. */
com->ibufold = com->ibuf;
com->ibufsize = ibufsize;
tp = com->tp;
if (tp != NULL) {
tp->t_ififosize = 2 * ibufsize;
tp->t_ispeedwat = (speed_t)-1;
tp->t_ospeedwat = (speed_t)-1;
}
/*
* Read current input buffer, if any. Continue with interrupts
* disabled.
*/
disable_intr();
if (com->iptr != com->ibuf)
sioinput(com);
/*-
* Initialize critical variables, including 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.
*/
com->iptr = com->ibuf = ibuf;
com->ibufend = ibuf + ibufsize;
com->ierroff = ibufsize;
com->ihighwater = ibuf + 3 * ibufsize / 4;
return (0);
}