TMXR: Fix MUX transmit scheduling when buffer is empty

Also:
- Enhance SHOW MUX to support "SHOW MUX <dev>"
- Fix potential null pointer dereference with unusual ATTACH combinations
- Allow BPS factor to change without specifying an explicit speed.
This commit is contained in:
Mark Pizzolato 2018-06-23 11:30:02 -07:00
parent c73f56cc5a
commit d3da865e9b
2 changed files with 140 additions and 100 deletions

2
scp.c
View file

@ -1381,7 +1381,7 @@ static const char simh_help[] =
"+sh{ow} <unit> {arg,...} show unit parameters\n" "+sh{ow} <unit> {arg,...} show unit parameters\n"
"+sh{ow} ethernet show ethernet devices\n" "+sh{ow} ethernet show ethernet devices\n"
"+sh{ow} serial show serial devices\n" "+sh{ow} serial show serial devices\n"
"+sh{ow} multiplexer show open multiplexer devices\n" "+sh{ow} multiplexer {dev} show open multiplexer device info\n"
#if defined(USE_SIM_VIDEO) #if defined(USE_SIM_VIDEO)
"+sh{ow} video show video capabilities\n" "+sh{ow} video show video capabilities\n"
#endif #endif

View file

@ -872,6 +872,8 @@ if (mp->port) /* copy port */
sprintf (growstring(&tptr, 13 + strlen (mp->port)), "%s%s", mp->port, mp->notelnet ? ";notelnet" : ""); sprintf (growstring(&tptr, 13 + strlen (mp->port)), "%s%s", mp->port, mp->notelnet ? ";notelnet" : "");
if (mp->logfiletmpl[0]) /* logfile info */ if (mp->logfiletmpl[0]) /* logfile info */
sprintf (growstring(&tptr, 7 + strlen (mp->logfiletmpl)), ",Log=%s", mp->logfiletmpl); sprintf (growstring(&tptr, 7 + strlen (mp->logfiletmpl)), ",Log=%s", mp->logfiletmpl);
if (mp->buffered)
sprintf (growstring(&tptr, 10 + 10), ",Buffered=%d", mp->buffered);
while ((*tptr == ',') || (*tptr == ' ')) while ((*tptr == ',') || (*tptr == ' '))
memmove (tptr, tptr+1, strlen(tptr+1)+1); memmove (tptr, tptr+1, strlen(tptr+1)+1);
for (i=0; i<mp->lines; ++i) { for (i=0; i<mp->lines; ++i) {
@ -2483,21 +2485,25 @@ t_stat tmxr_set_line_speed (TMLN *lp, CONST char *speed)
UNIT *uptr; UNIT *uptr;
CONST char *cptr; CONST char *cptr;
t_stat r; t_stat r;
uint32 rxbps;
if (!speed || !*speed) if (!speed || !*speed)
return SCPE_2FARG; return SCPE_2FARG;
if (_tmln_speed_delta (speed) < 0) if (_tmln_speed_delta (speed) < 0)
return SCPE_ARG; return SCPE_ARG;
lp->rxbps = (uint32)strtotv (speed, &cptr, 10); rxbps = (uint32)strtotv (speed, &cptr, 10);
if (*cptr == '*') { if (*cptr == '*') {
uint32 bpsfactor = (uint32) get_uint (cptr+1, 10, 32, &r); uint32 bpsfactor = (uint32) get_uint (cptr+1, 10, 32, &r);
if (r != SCPE_OK) if (r != SCPE_OK)
return r; return r;
lp->bpsfactor = bpsfactor; lp->bpsfactor = bpsfactor;
if (speed == cptr) /* just changing bps factor? */
return SCPE_OK; /* Done now */
} }
lp->rxbps = rxbps; /* use supplied speed */
if ((lp->serport) && (lp->bpsfactor != 0.0)) if ((lp->serport) && (lp->bpsfactor != 0.0))
lp->bpsfactor = 1.0; lp->bpsfactor = 1.0; /* Ignore bps factor for serial ports */
lp->rxdeltausecs = (uint32)(_tmln_speed_delta (speed) / lp->bpsfactor); lp->rxdeltausecs = (uint32)(_tmln_speed_delta (speed) / lp->bpsfactor);
lp->rxnexttime = 0.0; lp->rxnexttime = 0.0;
uptr = lp->uptr; uptr = lp->uptr;
@ -3073,6 +3079,8 @@ t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll)
{ {
if ((line < 0) || (line >= mp->lines)) if ((line < 0) || (line >= mp->lines))
return SCPE_ARG; return SCPE_ARG;
if (mp->ldsc[line].uptr)
mp->ldsc[line].uptr->dynflags &= ~UNIT_TM_POLL;
mp->ldsc[line].uptr = uptr_poll; mp->ldsc[line].uptr = uptr_poll;
return SCPE_OK; return SCPE_OK;
} }
@ -3104,6 +3112,8 @@ t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll)
{ {
if ((line < 0) || (line >= mp->lines)) if ((line < 0) || (line >= mp->lines))
return SCPE_ARG; return SCPE_ARG;
if (mp->ldsc[line].o_uptr)
mp->ldsc[line].o_uptr->dynflags &= ~UNIT_TM_POLL;
mp->ldsc[line].o_uptr = uptr_poll; mp->ldsc[line].o_uptr = uptr_poll;
return SCPE_OK; return SCPE_OK;
} }
@ -3857,11 +3867,14 @@ int32 i;
if (mp->dptr == NULL) /* has device been set? */ if (mp->dptr == NULL) /* has device been set? */
mp->dptr = find_dev_from_unit (uptr); /* no, so set device now */ mp->dptr = find_dev_from_unit (uptr); /* no, so set device now */
mp->uptr = uptr; /* save unit for polling */ if (mp->uptr == NULL) /* has polling unit been set? */
mp->uptr = uptr; /* save unit for polling */
r = tmxr_open_master (mp, cptr); /* open master socket */ r = tmxr_open_master (mp, cptr); /* open master socket */
if (r != SCPE_OK) /* error? */ if (r != SCPE_OK) /* error? */
return r; return r;
uptr->filename = tmxr_mux_attach_string (uptr->filename, mp);/* save */ uptr->filename = tmxr_mux_attach_string (uptr->filename, mp);/* save */
if (uptr->filename == NULL) /* avoid dangling NULL pointer */
uptr->filename = (char *)calloc (1, 1); /* provide an emptry string */
uptr->flags = uptr->flags | UNIT_ATT; /* no more errors */ uptr->flags = uptr->flags | UNIT_ATT; /* no more errors */
uptr->tmxr = (void *)mp; uptr->tmxr = (void *)mp;
if ((mp->lines > 1) || if ((mp->lines > 1) ||
@ -3910,105 +3923,128 @@ if (tmxr_open_device_count)
return SCPE_OK; return SCPE_OK;
} }
t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc)
{
int i, j;
if (0 == tmxr_open_device_count) t_stat tmxr_show_open_device (FILE* st, TMXR *mp)
{
int j;
TMLN *lp;
UNIT *o_uptr = mp->ldsc[0].o_uptr;
UNIT *uptr = mp->ldsc[0].uptr;
char *attach;
fprintf(st, "Multiplexer device: %s", (mp->dptr ? sim_dname (mp->dptr) : ""));
if (mp->lines > 1) {
fprintf(st, ", ");
tmxr_show_lines(st, NULL, 0, mp);
}
if (mp->packet)
fprintf(st, ", Packet");
if (mp->datagram)
fprintf(st, ", UDP");
if (mp->notelnet)
fprintf(st, ", Telnet=disabled");
if (mp->modem_control)
fprintf(st, ", ModemControl=enabled");
if (mp->buffered)
fprintf(st, ", Buffered=%d", mp->buffered);
for (j = 1; j < mp->lines; j++)
if (o_uptr != mp->ldsc[j].o_uptr)
break;
if (j == mp->lines)
fprintf(st, ", Output Unit: %s", sim_uname (o_uptr));
for (j = 1; j < mp->lines; j++)
if (uptr != mp->ldsc[j].uptr)
break;
if (j == mp->lines) {
fprintf(st, ",\n Input Polling Unit: %s", sim_uname (uptr));
if (uptr != mp->uptr)
fprintf(st, ", Connection Polling Unit: %s", sim_uname (mp->uptr));
}
attach = tmxr_mux_attach_string (NULL, mp);
if (attach)
fprintf(st, ",\n attached to %s, ", attach);
free (attach);
tmxr_show_summ(st, NULL, 0, mp);
fprintf(st, ", sessions=%d", mp->sessions);
if (mp->lines == 1) {
if (mp->ldsc->rxbps) {
fprintf(st, ", Speed=%d", mp->ldsc->rxbps);
if (mp->ldsc->bpsfactor != 1.0)
fprintf(st, "*%.0f", mp->ldsc->bpsfactor);
fprintf(st, " bps");
}
}
fprintf(st, "\n");
if (mp->ring_start_time) {
fprintf (st, " incoming Connection from: %s ringing for %d milliseconds\n", mp->ring_ipad, sim_os_msec () - mp->ring_start_time);
}
for (j = 0; j < mp->lines; j++) {
lp = mp->ldsc + j;
if (mp->lines > 1) {
if (lp->dptr && (mp->dptr != lp->dptr))
fprintf (st, "Device: %s ", sim_dname(lp->dptr));
fprintf (st, "Line: %d", j);
if (lp->conn == TMXR_LINE_DISABLED)
fprintf (st, " - Disabled");
if (mp->notelnet != lp->notelnet)
fprintf (st, " - %stelnet", lp->notelnet ? "no" : "");
if (lp->uptr && (lp->uptr != lp->mp->uptr))
fprintf (st, " - Unit: %s", sim_uname (lp->uptr));
if ((lp->o_uptr != o_uptr) && lp->o_uptr && (lp->o_uptr != lp->mp->uptr) && (lp->o_uptr != lp->uptr))
fprintf (st, " - Output Unit: %s", sim_uname (lp->o_uptr));
if (mp->modem_control != lp->modem_control)
fprintf(st, ", ModemControl=%s", lp->modem_control ? "enabled" : "disabled");
if (lp->loopback)
fprintf(st, ", Loopback");
if (lp->rxbps) {
fprintf(st, ", Speed=%d", lp->rxbps);
if (lp->bpsfactor != 1.0)
fprintf(st, "*%.0f", lp->bpsfactor);
fprintf(st, " bps");
}
else {
if (lp->bpsfactor != 1.0)
fprintf(st, ", Speed=*%.0f bps", lp->bpsfactor);
}
fprintf (st, "\n");
}
if ((!lp->sock) && (!lp->connecting) && (!lp->serport) && (!lp->master)) {
if ((lp->modem_control) || (lp->txbfd))
tmxr_fconns (st, lp, -1);
continue;
}
tmxr_fconns (st, lp, -1);
tmxr_fstats (st, lp, -1);
}
return SCPE_OK;
}
t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* cptr)
{
int i;
char gbuf[CBUFSIZE];
cptr = get_glyph (cptr, gbuf, 0);
if (*cptr)
return SCPE_2MARG;
if ((0 == tmxr_open_device_count) &&
(gbuf[0] == '\0'))
fprintf(st, "No Attached Multiplexer Devices\n"); fprintf(st, "No Attached Multiplexer Devices\n");
else { else {
for (i=0; i<tmxr_open_device_count; ++i) { for (i=0; i<tmxr_open_device_count; ++i) {
TMXR *mp = tmxr_open_devices[i]; TMXR *mp = tmxr_open_devices[i];
TMLN *lp;
UNIT *o_uptr = mp->ldsc[0].o_uptr;
UNIT *uptr = mp->ldsc[0].uptr;
char *attach;
fprintf(st, "Multiplexer device: %s", (mp->dptr ? sim_dname (mp->dptr) : "")); if ((gbuf[0] == '\0') ||
if (mp->lines > 1) { (0 == strcmp (gbuf, mp->dptr->name))) {
fprintf(st, ", "); tmxr_show_open_device (st, mp);
tmxr_show_lines(st, NULL, 0, mp); if (gbuf[0] != '\0')
}
if (mp->packet)
fprintf(st, ", Packet");
if (mp->datagram)
fprintf(st, ", UDP");
if (mp->notelnet)
fprintf(st, ", Telnet=disabled");
if (mp->modem_control)
fprintf(st, ", ModemControl=enabled");
if (mp->buffered)
fprintf(st, ", Buffered=%d", mp->buffered);
for (j = 1; j < mp->lines; j++)
if (o_uptr != mp->ldsc[j].o_uptr)
break; break;
if (j == mp->lines)
fprintf(st, ", Output Unit: %s", sim_uname (o_uptr));
for (j = 1; j < mp->lines; j++)
if (uptr != mp->ldsc[j].uptr)
break;
if (j == mp->lines) {
fprintf(st, ",\n Input Polling Unit: %s", sim_uname (uptr));
if (uptr != mp->uptr)
fprintf(st, ", Connection Polling Unit: %s", sim_uname (mp->uptr));
}
attach = tmxr_mux_attach_string (NULL, mp);
if (attach)
fprintf(st, ",\n attached to %s, ", attach);
free (attach);
tmxr_show_summ(st, NULL, 0, mp);
fprintf(st, ", sessions=%d", mp->sessions);
if (mp->lines == 1) {
if (mp->ldsc->rxbps) {
fprintf(st, ", Speed=%d", mp->ldsc->rxbps);
if (mp->ldsc->bpsfactor != 1.0)
fprintf(st, "*%.0f", mp->ldsc->bpsfactor);
fprintf(st, " bps");
}
}
fprintf(st, "\n");
if (mp->ring_start_time) {
fprintf (st, " incoming Connection from: %s ringing for %d milliseconds\n", mp->ring_ipad, sim_os_msec () - mp->ring_start_time);
}
for (j = 0; j < mp->lines; j++) {
lp = mp->ldsc + j;
if (mp->lines > 1) {
if (lp->dptr && (mp->dptr != lp->dptr))
fprintf (st, "Device: %s ", sim_dname(lp->dptr));
fprintf (st, "Line: %d", j);
if (lp->conn == TMXR_LINE_DISABLED)
fprintf (st, " - Disabled");
if (mp->notelnet != lp->notelnet)
fprintf (st, " - %stelnet", lp->notelnet ? "no" : "");
if (lp->uptr && (lp->uptr != lp->mp->uptr))
fprintf (st, " - Unit: %s", sim_uname (lp->uptr));
if ((lp->o_uptr != o_uptr) && lp->o_uptr && (lp->o_uptr != lp->mp->uptr) && (lp->o_uptr != lp->uptr))
fprintf (st, " - Output Unit: %s", sim_uname (lp->o_uptr));
if (mp->modem_control != lp->modem_control)
fprintf(st, ", ModemControl=%s", lp->modem_control ? "enabled" : "disabled");
if (lp->loopback)
fprintf(st, ", Loopback");
if (lp->rxbps) {
fprintf(st, ", Speed=%d", lp->rxbps);
if (lp->bpsfactor != 1.0)
fprintf(st, "*%.0f", lp->bpsfactor);
fprintf(st, " bps");
}
else {
if (lp->bpsfactor != 1.0)
fprintf(st, ", Speed=*%.0f bps", lp->bpsfactor);
}
fprintf (st, "\n");
}
if ((!lp->sock) && (!lp->connecting) && (!lp->serport) && (!lp->master)) {
if ((lp->modem_control) || (lp->txbfd))
tmxr_fconns (st, lp, -1);
continue;
}
tmxr_fconns (st, lp, -1);
tmxr_fstats (st, lp, -1);
} }
} }
if ((gbuf[0] != '\0') &&
(i == tmxr_open_device_count))
return sim_messagef (SCPE_ARG, "Multiplexer device %s not found or attached\n", gbuf);
} }
return SCPE_OK; return SCPE_OK;
} }
@ -4146,13 +4182,17 @@ for (i=0; i<mp->lines; i++) {
if ((lp->conn || lp->txbfd) && /* Connected (or buffered)? */ if ((lp->conn || lp->txbfd) && /* Connected (or buffered)? */
(uptr == lp->o_uptr) && /* output completion unit? */ (uptr == lp->o_uptr) && /* output completion unit? */
(lp->txbps)) { /* while rate limiting */ (lp->txbps)) { /* while rate limiting */
if ((tmxr_tqln(lp)) && /* pending output data? */ if ((tmxr_tqln(lp)) && /* pending output data */
(lp->txnexttime < sim_gtime_now)) (lp->txnexttime < sim_gtime_now))/* that can be transmitted now? */
tmxr_send_buffered_data (lp);/* flush it */ tmxr_send_buffered_data (lp);/* flush it */
if (lp->txnexttime > sim_gtime_now) if (tmxr_tqln(lp) == 0) /* no pending output data */
due = (int32)(lp->txnexttime - sim_gtime_now); due = interval; /* No rush */
else else {
due = sim_processing_event ? 1 : 0; /* avoid potential infinite loop if called from service routine */ if (lp->txnexttime > sim_gtime_now)
due = (int32)(lp->txnexttime - sim_gtime_now);
else
due = sim_processing_event ? 1 : 0; /* avoid potential infinite loop if called from service routine */
}
sooner = MIN(sooner, due); sooner = MIN(sooner, due);
} }
} }