PDP11, VAX: Enhance VH, DZ, DL and DCI devices to provide input rate limiting. Fix #246

Data arriving on simulated serial ports can arrive faster than the OS running
on the simulated system can deliber it to user mode programs.  This happens
when chunks of data are delivered to the mux from a network connection.
This can be due to a file transfer program (kermit) running on the other end
of a network connection and the packet size being delivered exceeds the
simulated OS's type ahead buffer size OR from users who paste arbitrary
blocks of data into a telnet or console session.
This commit is contained in:
Mark Pizzolato 2015-11-19 08:56:06 -08:00
parent 9b45833687
commit 833ba71c3b
5 changed files with 89 additions and 32 deletions

View file

@ -158,7 +158,7 @@ DIB dci_dib = {
2, IVCL (DCI), VEC_AUTO, { &dci_iack, &dco_iack }, IOLN_DC,
};
UNIT dci_unit = { UDATA (&dci_svc, 0, 0), SERIAL_IN_WAIT };
UNIT dci_unit = { UDATA (&dci_svc, 0, 0), TMLN_SPD_9600_BPS };
REG dci_reg[] = {
{ BRDATAD (BUF, dci_buf, DEV_RDX, 8, DCX_LINES, "input control/stats register") },
@ -290,7 +290,7 @@ switch ((PA >> 1) & 03) { /* decode PA<2:1> */
case 01: /* dci buf */
dci_clr_int (ln);
*data = dci_buf[ln];
sim_activate_abs (&dci_unit, dci_unit.wait);
sim_activate_after_abs (&dci_unit, dci_unit.wait);
return SCPE_OK;
case 02: /* dco csr */

View file

@ -123,7 +123,7 @@ DIB dli_dib = {
2, IVCL (DLI), VEC_AUTO, { &dli_iack, &dlo_iack }, IOLN_DL,
};
UNIT dli_unit = { UDATA (&dli_svc, 0, 0), SERIAL_IN_WAIT };
UNIT dli_unit = { UDATA (&dli_svc, 0, 0), TMLN_SPD_9600_BPS };
REG dli_reg[] = {
{ BRDATA (BUF, dli_buf, DEV_RDX, 16, DLX_LINES) },
@ -272,7 +272,7 @@ switch ((PA >> 1) & 03) { /* decode PA<2:1> */
*data = dli_buf[ln] & DLIBUF_RD;
dli_csr[ln] &= ~CSR_DONE; /* clr rcv done */
dli_clr_int (ln, DLI_RCI); /* clr rcv int req */
sim_activate_abs (&dli_unit, dli_unit.wait);
sim_activate_after_abs (&dli_unit, dli_unit.wait);
break;
case 02: /* tto csr */

View file

@ -452,10 +452,12 @@ switch ((PA >> 1) & 03) { /* case on PA<2:1> */
dz_clear (dz, FALSE);
if (data & CSR_MSE) /* MSE? start poll */
sim_clock_coschedule (&dz_unit, tmxr_poll);
else dz_csr[dz] &= ~(CSR_SA | CSR_RDONE | CSR_TRDY);
else
dz_csr[dz] &= ~(CSR_SA | CSR_RDONE | CSR_TRDY);
if ((data & CSR_RIE) == 0) /* RIE = 0? */
dz_clr_rxint (dz);
else if (((dz_csr[dz] & CSR_IE) == 0) && /* RIE 0->1? */
else
if (((dz_csr[dz] & CSR_IE) == 0) && /* RIE 0->1? */
((dz_csr[dz] & CSR_SAE)?
(dz_csr[dz] & CSR_SA): (dz_csr[dz] & CSR_RDONE)))
dz_set_rxint (dz);
@ -472,12 +474,11 @@ switch ((PA >> 1) & 03) { /* case on PA<2:1> */
lp = &dz_ldsc[line]; /* get line desc */
if (dz_lpr[dz] & LPR_RCVE) /* rcv enb? on */
lp->rcve = 1;
else lp->rcve = 0; /* else line off */
if (dz_mctl) {
else
lp->rcve = 0; /* else line off */
sprintf(lineconfig, "%s-%s%s%s", LPR_GETSPD(data), LPR_GETCHARSIZE(data), LPR_GETPARITY(data), LPR_GETSTOPBITS(data));
if (!lp->serconfig || (0 != strcmp(lp->serconfig, lineconfig))) /* config changed? */
tmxr_set_config_line (lp, lineconfig); /* set it */
}
tmxr_poll_rx (&dz_desc); /* poll input */
dz_update_rcvi (); /* update rx intr */
break;
@ -578,7 +579,7 @@ for (i = c = 0; (i < DZ_LINES) && (c == 0); i++) { /* loop thru lines */
if (c & SCPE_BREAK) /* break? frame err */
c = RBUF_VALID | RBUF_FRME;
if (c) /* or in line # */
c = c | (i << RBUF_V_RLINE);
c = (c & RBUF_CHAR) | RBUF_VALID | (i << RBUF_V_RLINE);
} /* end for */
return (uint16)c;
}
@ -608,12 +609,14 @@ for (dz = 0; dz < dz_desc.lines/DZ_LINES; dz++) { /* loop thru muxes */
dz_sae[dz] = 0; /* disable alarm */
}
}
else dz_csr[dz] &= ~CSR_RDONE; /* no, clear done */
else
dz_csr[dz] &= ~CSR_RDONE; /* no, clear done */
if ((dz_csr[dz] & CSR_RIE) && /* int enable */
((dz_csr[dz] & CSR_SAE)?
(dz_csr[dz] & CSR_SA): (dz_csr[dz] & CSR_RDONE)))
dz_set_rxint (dz); /* and alm/done? */
else dz_clr_rxint (dz); /* no, clear int */
else
dz_clr_rxint (dz); /* no, clear int */
}
return;
}

View file

@ -168,9 +168,12 @@ extern int32 tmxr_poll, clk_tps;
#define LPR_M_DIAG (03)
#define LPR_V_CHAR_LGTH (3)
#define LPR_M_CHAR_LGTH (03)
#define LPR_PARITY_ENAB (1 << 5)
#define LPR_EVEN_PARITY (1 << 6)
#define LPR_STOP_CODE (1 << 7)
#define LPR_V_PARITY_ENAB (5)
#define LPR_PARITY_ENAB (1 << LPR_V_PARITY_ENAB)
#define LPR_V_EVEN_PARITY (6)
#define LPR_EVEN_PARITY (1 << LPR_V_EVEN_PARITY)
#define LPR_V_STOP_CODE (7)
#define LPR_STOP_CODE (1 << LPR_V_STOP_CODE)
#define LPR_V_RX_SPEED (8)
#define LPR_M_RX_SPEED (017)
#define LPR_V_TX_SPEED (12)
@ -193,6 +196,17 @@ extern int32 tmxr_poll, clk_tps;
#define RATE_19200 (14)
#define RATE_38400 (15)
static const char *vh_charsizes[] = {"5", "6", "7", "8"};
static const char *vh_baudrates[] = {"50", "75", "110", "134.5", "150", "300", "600", "1200",
"1800", "2000", "2400", "4800", "7200", "9600", "19200", "38400"};
static const char *vh_parity[] = {"N", "N", "E", "O"};
static const char *vh_stopbits[] = {"1", "2", "1", "1.5"};
#define LPR_GETSPD(x) vh_baudrates[((x) >> LPR_V_RX_SPEED) & LPR_M_RX_SPEED]
#define LPR_GETCHARSIZE(x) vh_charsizes[((x) >> LPR_V_CHAR_LGTH) & LPR_M_CHAR_LGTH]
#define LPR_GETPARITY(x) vh_parity[(((x) >> LPR_V_PARITY_ENAB) & 1) | (((x) >> (LPR_V_EVEN_PARITY-1)) & 2)]
#define LPR_GETSTOPBITS(x) vh_stopbits[(((x) >> LPR_V_STOP_CODE) & 1) + (((((x) >> LPR_V_CHAR_LGTH) & LPR_M_CHAR_LGTH) == 5) ? 2 : 0)]
/* Line-Status Register (STAT) */
#define STAT_DHUID (1 << 8) /* mode: 0=DHV, 1=DHU */
@ -338,6 +352,7 @@ static t_stat vh_show_detail (FILE *st, UNIT *uptr, int32 val, void *desc);
static t_stat vh_show_rbuf (FILE *st, UNIT *uptr, int32 val, void *desc);
static t_stat vh_show_txq (FILE *st, UNIT *uptr, int32 val, void *desc);
static t_stat vh_putc (int32 vh, TMLX *lp, int32 chan, int32 data);
static void vh_set_config (TMLX *lp );
static void doDMA (int32 vh, int32 chan);
static t_stat vh_setmode (UNIT *uptr, int32 val, char *cptr, void *desc);
static t_stat vh_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc);
@ -369,6 +384,8 @@ static UNIT vh_unit[VH_MUXES+1] = {
{ UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) },
};
static UNIT *vh_timer_unit;
static const REG vh_reg[] = {
{ BRDATAD (CSR, vh_csr, DEV_RDX, 16, VH_MUXES, "control/status register, boards 0 to 3") },
{ BRDATAD (TIMER, vh_timer, DEV_RDX, 16, VH_MUXES, "controller timeout, boards 0 to 3") },
@ -765,22 +782,31 @@ static void vh_getc ( int32 vh )
TMLX *lp;
for (i = 0; i < (uint32)VH_LINES; i++) {
if (rbuf_idx[vh] >= (FIFO_ALARM-1)) /* close to fifo capacity? */
continue; /* don't bother checking for data */
lp = &vh_parm[(vh * VH_LINES) + i];
while ((c = tmxr_getc_ln (lp->tmln)) != 0) {
if (c & SCPE_BREAK) {
fifo_put (vh, lp,
RBUF_FRAME_ERR | RBUF_PUTLINE (i));
/* BUG: check for overflow above */
} else {
c &= bitmask[(lp->lpr >> LPR_V_CHAR_LGTH) &
LPR_M_CHAR_LGTH];
fifo_put (vh, lp, RBUF_PUTLINE (i) | c);
/* BUG: check for overflow above */
}
}
}
}
static void vh_set_config ( TMLX *lp )
{
char lineconfig[16];
sprintf(lineconfig, "%s-%s%s%s", LPR_GETSPD(lp->lpr), LPR_GETCHARSIZE(lp->lpr), LPR_GETPARITY(lp->lpr), LPR_GETSTOPBITS(lp->lpr));
if (!lp->tmln->serconfig || (0 != strcmp(lp->tmln->serconfig, lineconfig))) /* config changed? */
tmxr_set_config_line (lp->tmln, lineconfig); /* set it */
}
/* I/O dispatch routines */
static t_stat vh_rd ( int32 *data,
@ -929,7 +955,7 @@ static t_stat vh_wr ( int32 ldata,
break;
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1;
sim_clock_coschedule (&vh_unit[1], tmxr_poll);
sim_clock_coschedule (vh_timer_unit, tmxr_poll);
break;
}
if (vh_unit[vh].flags & UNIT_MODEDHU) {
@ -974,7 +1000,7 @@ static t_stat vh_wr ( int32 ldata,
case 2: /* LPR */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1;
sim_clock_coschedule (&vh_unit[1], tmxr_poll);
sim_clock_coschedule (vh_timer_unit, tmxr_poll);
break;
}
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -989,6 +1015,7 @@ static t_stat vh_wr ( int32 ldata,
if (CSR_GETCHAN (vh_csr[vh]) != 0)
data &= ~LPR_DISAB_XRPT;
lp->lpr = data;
vh_set_config (lp);
if (((lp->lpr >> LPR_V_DIAG) & LPR_M_DIAG) == 1) {
fifo_put (vh, lp,
RBUF_DIAG |
@ -1001,7 +1028,7 @@ static t_stat vh_wr ( int32 ldata,
case 3: /* STAT/FIFODATA */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1;
sim_clock_coschedule (&vh_unit[1], tmxr_poll);
sim_clock_coschedule (vh_timer_unit, tmxr_poll);
break;
}
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1025,7 +1052,7 @@ static t_stat vh_wr ( int32 ldata,
case 4: /* LNCTRL */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1;
sim_clock_coschedule (&vh_unit[1], tmxr_poll);
sim_clock_coschedule (vh_timer_unit, tmxr_poll);
break;
}
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1093,7 +1120,7 @@ static t_stat vh_wr ( int32 ldata,
case 5: /* TBUFFAD1 */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1;
sim_clock_coschedule (&vh_unit[1], tmxr_poll);
sim_clock_coschedule (vh_timer_unit, tmxr_poll);
break;
}
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1109,7 +1136,7 @@ static t_stat vh_wr ( int32 ldata,
case 6: /* TBUFFAD2 */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1;
sim_clock_coschedule (&vh_unit[1], tmxr_poll);
sim_clock_coschedule (vh_timer_unit, tmxr_poll);
break;
}
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1130,7 +1157,7 @@ static t_stat vh_wr ( int32 ldata,
case 7: /* TBUFFCT */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1;
sim_clock_coschedule (&vh_unit[1], tmxr_poll);
sim_clock_coschedule (vh_timer_unit, tmxr_poll);
break;
}
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1293,6 +1320,7 @@ static void vh_init_chan ( int32 vh,
lp->lpr = (RATE_9600 << LPR_V_TX_SPEED) |
(RATE_9600 << LPR_V_RX_SPEED) |
(03 << LPR_V_CHAR_LGTH);
vh_set_config ( lp );
lp->lnctrl = 0;
lp->lstat &= ~(STAT_MDL | STAT_DHUID | STAT_RI);
if (vh_unit[vh].flags & UNIT_MODEDHU)
@ -1366,8 +1394,9 @@ static t_stat vh_reset ( DEVICE *dptr )
for (i = 0; i < vh_desc.lines; i++)
vh_parm[i].tmln = &vh_ldsc[i];
vh_dev.numunits = (vh_desc.lines / VH_LINES) + 1;
vh_unit[vh_dev.numunits-1].action = &vh_timersvc;
vh_unit[vh_dev.numunits-1].flags = UNIT_DIS;
vh_timer_unit = &vh_unit[vh_dev.numunits-1];
vh_timer_unit->action = &vh_timersvc;
vh_timer_unit->flags = UNIT_DIS;
for (i = 0; i < vh_desc.lines/VH_LINES; i++) {
/* if Unibus, force DHU mode */
if (UNIBUS)

View file

@ -1886,6 +1886,8 @@ return;
int32 tmxr_rqln (TMLN *lp)
{
if ((lp->rxbps && (lp->rxlasttime + (lp->rxdelta + 500)/1000) > sim_os_msec ()))
return 0;
return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz: 0));
}
@ -3840,6 +3842,29 @@ else {
if (mux)
fprintf (st, "Valid line numbers are from 0 thru %d\n\n", mux->lines-1);
}
if (single_line) { /* Single Line Multiplexer */
fprintf (st, "The input data rate for the $s device can be controlled by\n", dptr->name);
fprintf (st, "specifying SPEED=nnn on the the ATTACH command.\n");
}
else {
fprintf (st, "The input data rate for all lines or a particular line of a the %s\n", dptr->name);
fprintf (st, "device can be controlled by specifying SPEED=nnn on the the ATTACH command.\n");
}
fprintf (st, "SPEED values can be any one of:\n\n");
fprintf (st, " 0 50 75 110 134 150 300 600 1200 1800 2000 2400\n");
fprintf (st, " 3600 4800 7200 9600 19200 38400 57600 76800 115200\n\n");
fprintf (st, "A SPEED value of 0 causes input data to be delivered to the simulated\n");
fprintf (st, "port as fast as it arrives.\n\n");
fprintf (st, "If a simulated multiplexor devices can programmatically set a serial\n");
fprintf (st, "port line speed, the programmatically specified speed will take precidence\n");
fprintf (st, "over any input speed specified on an attach command.\n");
fprintf (st, "Example:\n\n");
fprintf (st, " sim> ATTACH %s 1234,SPEED=2400\n", dptr->name);
if (!single_line)
fprintf (st, " sim> ATTACH %s Line=2,SPEED=2400\n", dptr->name);
fprintf (st, "\n");
fprintf (st, "The SPEED parameter only influences the rate at which data is deliverd\n");
fprintf (st, "into the simulated multiplexor port. Output data rates are unaffected\n\n");
fprintf (st, "An optional serial port configuration string may be present after the port\n");
fprintf (st, "name. If present, it must be separated from the port name with a semicolon\n");
fprintf (st, "and has this form:\n\n");