TMXR: Packet, Loopback and Half-Duplex enhancements

- Added halfduplex mode for network connections and corrected modem signal DSR to reflect connection status (vs original attach status), and DCD follows DSR (except in halfduplex mode where it follows CTS).

- Enhance tmxr_set_get_modem_bits to also return the modem DTR and RTS state.

- Separate RTS from DTR when manipulating modem state bits

- Minor fixes to loopback functionality after direct testing with the first loopback client device (DMC11).

- Fix clearing of break input buffer now that input buffers are dynamically allocated

- Changed Modem bit logic to have CTS reflect RTS as expected by devices which may expect this.

- Changed receive buffers to be dynamically allocated and the same size as transmit buffers when transmit buffers are non-default sized.

- Added TMXR line attach in loopback mode.  Fixed loopback buffer management

- Added loopback support to TMXR lines

- Added functioning connect poll capability to revised DMC

- Added connection destination display to connection status even when a connection has not yet been established.

- Added extended packet sending and receiving semantics to TMXR allowing for an optional frame byte to exist between length prefixed data packets
This commit is contained in:
Mark Pizzolato 2013-11-20 12:18:02 -08:00
parent 2e85e74699
commit 00b77c8533
2 changed files with 501 additions and 67 deletions

View file

@ -67,13 +67,21 @@
tmxr_reset_ln - reset line (drops Telnet/tcp and serial connections) tmxr_reset_ln - reset line (drops Telnet/tcp and serial connections)
tmxr_detach_ln - reset line and close per line listener and outgoing destination tmxr_detach_ln - reset line and close per line listener and outgoing destination
tmxr_getc_ln - get character for line tmxr_getc_ln - get character for line
tmxr_get_packet_ln - get packet from line
tmxr_get_packet_ln_ex - get packet from line with separater byte
tmxr_poll_rx - poll receive tmxr_poll_rx - poll receive
tmxr_putc_ln - put character for line tmxr_putc_ln - put character for line
tmxr_put_packet_ln - put packet on line
tmxr_put_packet_ln_ex - put packet on line with separator byte
tmxr_poll_tx - poll transmit tmxr_poll_tx - poll transmit
tmxr_send_buffered_data - transmit buffered data tmxr_send_buffered_data - transmit buffered data
tmxr_set_modem_control_passthru - enable modem control on a multiplexer tmxr_set_modem_control_passthru - enable modem control on a multiplexer
tmxr_clear_modem_control_passthru - disable modem control on a multiplexer tmxr_clear_modem_control_passthru - disable modem control on a multiplexer
tmxr_set_get_modem_bits - set and/or get a line modem bits tmxr_set_get_modem_bits - set and/or get a line modem bits
tmxr_set_line_loopback - enable or disable loopback mode on a line
tmxr_get_line_loopback - returns the current loopback status of a line
tmxr_set_line_halfduplex - enable or disable halfduplex mode on a line
tmxr_get_line_halfduplex - returns the current halfduplex status of a line
tmxr_set_config_line - set port speed, character size, parity and stop bits tmxr_set_config_line - set port speed, character size, parity and stop bits
tmxr_open_master - open master connection tmxr_open_master - open master connection
tmxr_close_master - close master connection tmxr_close_master - close master connection
@ -94,6 +102,8 @@
tmxr_dscln - disconnect line (SET routine) tmxr_dscln - disconnect line (SET routine)
tmxr_rqln - number of available characters for line tmxr_rqln - number of available characters for line
tmxr_tqln - number of buffered characters for line tmxr_tqln - number of buffered characters for line
tmxr_tpqln - number of buffered packet characters for line
tmxr_tpbusyln - transmit packet busy status for line
tmxr_set_lnorder - set line connection order tmxr_set_lnorder - set line connection order
tmxr_show_lnorder - show line connection order tmxr_show_lnorder - show line connection order
tmxr_show_summ - show connection summary tmxr_show_summ - show connection summary
@ -435,10 +445,9 @@ static void tmxr_init_line (TMLN *lp)
lp->tsta = 0; /* init telnet state */ lp->tsta = 0; /* init telnet state */
lp->xmte = 1; /* enable transmit */ lp->xmte = 1; /* enable transmit */
lp->dstb = 0; /* default bin mode */ lp->dstb = 0; /* default bin mode */
lp->rxbpr = lp->rxbpi = lp->rxcnt = 0; /* init receive indexes */ lp->rxbpr = lp->rxbpi = lp->rxcnt = lp->rxpcnt = 0; /* init receive indexes */
if (!lp->txbfd || lp->notelnet) /* if not buffered telnet */ if (!lp->txbfd || lp->notelnet) /* if not buffered telnet */
lp->txbpr = lp->txbpi = lp->txcnt = 0; /* init transmit indexes */ lp->txbpr = lp->txbpi = lp->txcnt = lp->txpcnt = 0; /* init transmit indexes */
memset (lp->rbr, 0, sizeof(lp->rbr)); /* clear break status array */
lp->txdrp = 0; lp->txdrp = 0;
if (lp->modem_control) { if (lp->modem_control) {
lp->modembits &= ~TMXR_MDM_INCOMING; lp->modembits &= ~TMXR_MDM_INCOMING;
@ -447,8 +456,27 @@ if (lp->modem_control) {
if ((!lp->mp->buffered) && (!lp->txbfd)) { if ((!lp->mp->buffered) && (!lp->txbfd)) {
lp->txbfd = 0; lp->txbfd = 0;
lp->txbsz = TMXR_MAXBUF; lp->txbsz = TMXR_MAXBUF;
}
lp->txb = (char *)realloc (lp->txb, lp->txbsz); lp->txb = (char *)realloc (lp->txb, lp->txbsz);
lp->rxbsz = TMXR_MAXBUF;
lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
}
if (lp->loopback) {
lp->lpbsz = lp->rxbsz;
lp->lpb = (char *)realloc(lp->lpb, lp->lpbsz);
lp->lpbcnt = lp->lpbpi = lp->lpbpr = 0;
}
if (lp->rxpb) {
lp->rxpboffset = lp->rxpbsize = 0;
free (lp->rxpb);
lp->rxpb = NULL;
}
if (lp->txpb) {
lp->txpbsize = lp->txppsize = lp->txppoffset = 0;
free (lp->txpb);
lp->txpb = NULL;
}
memset (lp->rbr, 0, lp->rxbsz); /* clear break status array */
return; return;
} }
@ -492,6 +520,7 @@ if (!mp->buffered) {
lp->txbpi = 0; /* init buf pointers */ lp->txbpi = 0; /* init buf pointers */
lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf)); lp->txbpr = (int32)(lp->txbsz - strlen (msgbuf));
lp->rxcnt = lp->txcnt = lp->txdrp = 0; /* init counters */ lp->rxcnt = lp->txcnt = lp->txdrp = 0; /* init counters */
lp->rxpcnt = lp->txpcnt = 0;
} }
else else
if (lp->txcnt > lp->txbsz) if (lp->txcnt > lp->txbsz)
@ -532,6 +561,61 @@ tmxr_linemsgf (lp, "\nDisconnected from the %s simulator\n\n", sim_name);/* repo
return; return;
} }
static int32 loop_write (TMLN *lp, char *buf, int32 length)
{
int32 written = 0;
while (length) {
int32 chunksize;
int32 loopfree = lp->lpbsz - lp->lpbcnt;
if (loopfree == 0)
break;
if (loopfree < length)
length = loopfree;
if (lp->lpbpi >= lp->lpbpr)
chunksize = lp->lpbsz - lp->lpbpi;
else
chunksize = lp->lpbpr - lp->lpbpi;
if (chunksize > length)
chunksize = length;
memcpy (&lp->lpb[lp->lpbpi], buf, chunksize);
buf += chunksize;
length -= chunksize;
written += chunksize;
lp->lpbpi = (lp->lpbpi + chunksize) % lp->lpbsz;
}
lp->lpbcnt += written;
return written;
}
static int32 loop_read (TMLN *lp, char *buf, int32 bufsize)
{
int32 bytesread = 0;
while (bufsize > 0) {
int32 chunksize;
int32 loopused = lp->lpbcnt;
if (loopused < bufsize)
bufsize = loopused;
if (loopused == 0)
break;
if (lp->lpbpi > lp->lpbpr)
chunksize = lp->lpbpi - lp->lpbpr;
else
chunksize = lp->lpbsz - lp->lpbpr;
if (chunksize > bufsize)
chunksize = bufsize;
memcpy (buf, &lp->lpb[lp->lpbpr], chunksize);
buf += chunksize;
bufsize -= chunksize;
bytesread += chunksize;
lp->lpbpr = (lp->lpbpr + chunksize) % lp->lpbsz;
}
lp->lpbcnt -= bytesread;
return bytesread;
}
/* Read from a line. /* Read from a line.
@ -549,6 +633,8 @@ static int32 tmxr_read (TMLN *lp, int32 length)
{ {
int32 i = lp->rxbpi; int32 i = lp->rxbpi;
if (lp->loopback)
return loop_read (lp, &(lp->rxb[i]), length);
if (lp->serport) /* serial port connection? */ if (lp->serport) /* serial port connection? */
return sim_read_serial (lp->serport, &(lp->rxb[i]), length, &(lp->rbr[i])); return sim_read_serial (lp->serport, &(lp->rxb[i]), length, &(lp->rbr[i]));
else /* Telnet connection */ else /* Telnet connection */
@ -568,6 +654,9 @@ static int32 tmxr_write (TMLN *lp, int32 length)
int32 written; int32 written;
int32 i = lp->txbpr; int32 i = lp->txbpr;
if (lp->loopback)
return loop_write (lp, &(lp->txb[i]), length);
if (lp->serport) /* serial port connection? */ if (lp->serport) /* serial port connection? */
return sim_write_serial (lp->serport, &(lp->txb[i]), length); return sim_write_serial (lp->serport, &(lp->txb[i]), length);
@ -787,6 +876,8 @@ if (lp->destination || lp->port || lp->txlogname) {
sprintf (growstring(&tptr, 12 + strlen (lp->port)), ",%s%s", lp->port, (lp->mp->notelnet != lp->notelnet) ? (lp->notelnet ? ";notelnet" : ";telnet") : ""); sprintf (growstring(&tptr, 12 + strlen (lp->port)), ",%s%s", lp->port, (lp->mp->notelnet != lp->notelnet) ? (lp->notelnet ? ";notelnet" : ";telnet") : "");
if (lp->txlogname) if (lp->txlogname)
sprintf (growstring(&tptr, 12 + strlen (lp->txlogname)), ",Log=%s", lp->txlogname); sprintf (growstring(&tptr, 12 + strlen (lp->txlogname)), ",Log=%s", lp->txlogname);
if (lp->loopback)
sprintf (growstring(&tptr, 12 ), ",Loopback");
} }
if (*tptr == '\0') { if (*tptr == '\0') {
free (tptr); free (tptr);
@ -795,6 +886,19 @@ if (*tptr == '\0') {
return tptr; return tptr;
} }
/*
Set the connection polling interval
*/
t_stat tmxr_connection_poll_interval (TMXR *mp, uint32 seconds)
{
if (0 == seconds)
return SCPE_ARG;
mp->poll_interval = seconds;
return SCPE_OK;
}
/* Poll for new connection /* Poll for new connection
Called from unit service routine to test for new connection Called from unit service routine to test for new connection
@ -833,6 +937,9 @@ if (mp->last_poll_time == 0) { /* first poll initializa
if (!uptr) /* Attached ? */ if (!uptr) /* Attached ? */
return -1; /* No connections are possinle! */ return -1; /* No connections are possinle! */
if (mp->poll_interval == 0) /* Assure reasonable polling interval */
mp->poll_interval = TMXR_DEFAULT_CONNECT_POLL_INTERVAL;
if (!(uptr->dynflags & TMUF_NOASYNCH)) { /* if asynch not disabled */ if (!(uptr->dynflags & TMUF_NOASYNCH)) { /* if asynch not disabled */
uptr->dynflags |= UNIT_TM_POLL; /* tag as polling unit */ uptr->dynflags |= UNIT_TM_POLL; /* tag as polling unit */
sim_cancel (uptr); sim_cancel (uptr);
@ -847,7 +954,7 @@ if (mp->last_poll_time == 0) { /* first poll initializa
} }
} }
if ((poll_time - mp->last_poll_time) < TMXR_CONNECT_POLL_INTERVAL) if ((poll_time - mp->last_poll_time) < mp->poll_interval*1000)
return -1; /* too soon to try */ return -1; /* too soon to try */
srand((unsigned int)poll_time); srand((unsigned int)poll_time);
@ -876,7 +983,8 @@ if (mp->master) {
lp = mp->ldsc + i; /* get pointer to line descriptor */ lp = mp->ldsc + i; /* get pointer to line descriptor */
if ((lp->conn == FALSE) && /* is the line available? */ if ((lp->conn == FALSE) && /* is the line available? */
(lp->destination == NULL) && (lp->destination == NULL) &&
(lp->master == 0)) (lp->master == 0) &&
(lp->ser_connect_pending == FALSE))
break; /* yes, so stop search */ break; /* yes, so stop search */
} }
@ -909,6 +1017,19 @@ for (i = 0; i < mp->lines; i++) { /* check each line in se
int j, r = rand(); int j, r = rand();
lp = mp->ldsc + i; /* get pointer to line descriptor */ lp = mp->ldsc + i; /* get pointer to line descriptor */
/* Check for pending serial port connection notification */
if (lp->ser_connect_pending) {
lp->ser_connect_pending = FALSE;
lp->conn = TRUE;
return i;
}
/* Don't service network connections for loopbacked lines */
if (lp->loopback)
continue;
/* If two simulators are configured with symmetric virtual null modem /* If two simulators are configured with symmetric virtual null modem
cables pointing at each other, there may be a problem establishing cables pointing at each other, there may be a problem establishing
a connection if both systems happen to be checking for the success a connection if both systems happen to be checking for the success
@ -930,7 +1051,7 @@ for (i = 0; i < mp->lines; i++) { /* check each line in se
lp->conn = TRUE; /* record connection */ lp->conn = TRUE; /* record connection */
lp->sock = lp->connecting; /* it now looks normal */ lp->sock = lp->connecting; /* it now looks normal */
lp->connecting = 0; lp->connecting = 0;
lp->ipad = realloc (lp->ipad, 1+strlen (lp->destination)); lp->ipad = (char *)realloc (lp->ipad, 1+strlen (lp->destination));
strcpy (lp->ipad, lp->destination); strcpy (lp->ipad, lp->destination);
lp->cnms = sim_os_msec (); lp->cnms = sim_os_msec ();
sim_getnames_sock (lp->sock, &sockname, &peername); sim_getnames_sock (lp->sock, &sockname, &peername);
@ -1009,14 +1130,6 @@ for (i = 0; i < mp->lines; i++) { /* check each line in se
break; break;
} }
/* Check for pending serial port connection notification */
if (lp->ser_connect_pending) {
lp->ser_connect_pending = FALSE;
lp->conn = TRUE;
return i;
}
/* Check for needed outgoing connection initiation */ /* Check for needed outgoing connection initiation */
if (lp->destination && (!lp->sock) && (!lp->connecting) && (!lp->serport) && if (lp->destination && (!lp->sock) && (!lp->connecting) && (!lp->serport) &&
@ -1199,11 +1312,12 @@ return tmxr_clear_modem_control_passthru_state (mp, FALSE);
Output: Output:
incoming_bits if non NULL, returns the current stat of DCD, incoming_bits if non NULL, returns the current stat of DCD,
RNG, CTS and DSR RNG, CTS and DSR along with the current state
of DTR and RTS
Implementation note: Implementation note:
If a line is connected to a serial port, then these valus affect If a line is connected to a serial port, then these values affect
and reflect the state of the serial port. If the line is connected and reflect the state of the serial port. If the line is connected
to a network socket (or could be) then the network session state is to a network socket (or could be) then the network session state is
set, cleared and/or returned. set, cleared and/or returned.
@ -1221,23 +1335,39 @@ if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) || /* Assure only settable bits
before_modem_bits = lp->modembits; before_modem_bits = lp->modembits;
lp->modembits |= bits_to_set; lp->modembits |= bits_to_set;
lp->modembits &= ~(bits_to_clear | TMXR_MDM_INCOMING); lp->modembits &= ~(bits_to_clear | TMXR_MDM_INCOMING);
if ((lp->sock) || (lp->serport)) { if ((lp->sock) || (lp->serport) || (lp->loopback)) {
if (lp->modembits & TMXR_MDM_DTR) if (lp->modembits & TMXR_MDM_DTR) {
incoming_state = TMXR_MDM_DCD | TMXR_MDM_CTS | TMXR_MDM_DSR; incoming_state = TMXR_MDM_DSR;
else if (lp->modembits & TMXR_MDM_RTS)
incoming_state = TMXR_MDM_RNG | TMXR_MDM_DCD | TMXR_MDM_CTS | TMXR_MDM_DSR; incoming_state |= TMXR_MDM_CTS;
if (lp->halfduplex) {
if (incoming_state & TMXR_MDM_CTS)
incoming_state |= TMXR_MDM_DCD;
} }
else else
incoming_state = (lp->mp && lp->mp->master) ? (TMXR_MDM_CTS | TMXR_MDM_DSR) : 0; incoming_state |= TMXR_MDM_DCD;
}
else
incoming_state = TMXR_MDM_RNG | TMXR_MDM_DCD | TMXR_MDM_DSR;
}
else
incoming_state = 0;
lp->modembits |= incoming_state; lp->modembits |= incoming_state;
if (sim_deb && lp->mp && lp->mp->dptr) { if (sim_deb && lp->mp && lp->mp->dptr) {
sim_debug_bits (TMXR_DBG_MDM, lp->mp->dptr, tmxr_modem_bits, before_modem_bits, lp->modembits, FALSE); sim_debug_bits (TMXR_DBG_MDM, lp->mp->dptr, tmxr_modem_bits, before_modem_bits, lp->modembits, FALSE);
sim_debug (TMXR_DBG_MDM, lp->mp->dptr, " - Line %d - %p\n", (int)(lp-lp->mp->ldsc), lp->txb); sim_debug (TMXR_DBG_MDM, lp->mp->dptr, " - Line %d - %p\n", (int)(lp-lp->mp->ldsc), lp->txb);
} }
if (incoming_bits) if (incoming_bits)
*incoming_bits = incoming_state; *incoming_bits = lp->modembits;
if (lp->mp && lp->modem_control) { /* This API ONLY works on modem_control enabled multiplexer lines */ if (lp->mp && lp->modem_control) { /* This API ONLY works on modem_control enabled multiplexer lines */
if (bits_to_set | bits_to_clear) { /* Anything to do? */ if (bits_to_set | bits_to_clear) { /* Anything to do? */
if (lp->loopback) {
if ((lp->modembits ^ before_modem_bits) & TMXR_MDM_DTR) { /* DTR changed? */
lp->ser_connect_pending = (lp->modembits & TMXR_MDM_DTR);
lp->conn = !(lp->modembits & TMXR_MDM_DTR);
}
return SCPE_OK;
}
if (lp->serport) if (lp->serport)
return sim_control_serial (lp->serport, bits_to_set, bits_to_clear, incoming_bits); return sim_control_serial (lp->serport, bits_to_set, bits_to_clear, incoming_bits);
if ((lp->sock) || (lp->connecting)) { if ((lp->sock) || (lp->connecting)) {
@ -1258,11 +1388,80 @@ if (lp->mp && lp->modem_control) { /* This API ONLY works on mo
} }
return SCPE_OK; return SCPE_OK;
} }
if (lp->serport) if ((lp->serport) && (!lp->loopback))
sim_control_serial (lp->serport, 0, 0, incoming_bits); sim_control_serial (lp->serport, 0, 0, incoming_bits);
return SCPE_IERR; return SCPE_IERR;
} }
/* Enable or Disable loopback mode on a line
Inputs:
lp - the line to change
enable_loopback - enable or disable flag
Output:
none
Implementation note:
1) When enabling loopback mode, this API will disconnect any currently
connected TCP or Serial session.
2) When disabling loopback mode, prior network connections and/or
serial port connections will be restored.
*/
t_stat tmxr_set_line_loopback (TMLN *lp, t_bool enable_loopback)
{
if (lp->loopback == (enable_loopback != FALSE))
return SCPE_OK; /* Nothing to do */
lp->loopback = (enable_loopback != FALSE);
if (lp->loopback) {
lp->lpbsz = lp->rxbsz;
lp->lpb = (char *)realloc(lp->lpb, lp->lpbsz);
lp->lpbcnt = lp->lpbpi = lp->lpbpr = 0;
if (!lp->conn)
lp->ser_connect_pending = TRUE;
}
else {
free (lp->lpb);
lp->lpb = NULL;
lp->lpbsz = 0;
}
return SCPE_OK;
}
t_bool tmxr_get_line_loopback (TMLN *lp)
{
return (lp->loopback != FALSE);
}
/* Enable or Disable halfduplex mode on a line
Inputs:
lp - the line to change
enable_halfduplex - enable or disable flag
Output:
none
When a network connected line is in halfduplex mode, DCD modem signal
track with CTS. When not in halfduplex mode the DCD modem signal for
network connected lines tracks with DSR.
*/
t_stat tmxr_set_line_halfduplex (TMLN *lp, t_bool enable_halfduplex)
{
if (lp->halfduplex == (enable_halfduplex != FALSE))
return SCPE_OK; /* Nothing to do */
lp->halfduplex = (enable_halfduplex != FALSE);
return SCPE_OK;
}
t_bool tmxr_get_line_halfduplex (TMLN *lp)
{
return (lp->halfduplex != FALSE);
}
t_stat tmxr_set_config_line (TMLN *lp, char *config) t_stat tmxr_set_config_line (TMLN *lp, char *config)
{ {
t_stat r; t_stat r;
@ -1321,6 +1520,65 @@ tmxr_debug_return(lp, val);
return val; return val;
} }
/* Get packet from specific line
Inputs:
*lp = pointer to terminal line descriptor
**pbuf = pointer to pointer of packet contents
*psize = pointer to packet size
frame_byte - byte which separates packets in the tcp stream
(0 means no separation character)
Output:
SCPE_LOST link state lost
SCPE_OK Packet returned OR no packet available
Implementation notes:
1. If a packet is not yet available, then the pbuf address returned is
NULL, but success (SCPE_OK) is returned
*/
t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize)
{
return tmxr_get_packet_ln_ex (lp, pbuf, psize, 0);
}
t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte)
{
int32 c;
size_t pktsize;
size_t fc_size = (frame_byte ? 1 : 0);
while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
if (lp->rxpboffset + 1 > lp->rxpbsize) {
lp->rxpbsize += 512;
lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize);
}
if ((lp->rxpboffset == 0) && (fc_size) && (c != frame_byte)) {
tmxr_debug (TMXR_DBG_PRCV, lp, "Received Unexpected Framing Byte", (char *)&lp->rxpb[lp->rxpboffset], 1);
continue;
}
lp->rxpb[lp->rxpboffset] = c & 0xFF;
lp->rxpboffset += 1;
if (lp->rxpboffset >= (2 + fc_size)) {
pktsize = (lp->rxpb[0+fc_size] << 8) | lp->rxpb[1+fc_size];
if (pktsize == (lp->rxpboffset - 2)) {
++lp->rxpcnt;
*pbuf = &lp->rxpb[2+fc_size];
*psize = pktsize;
lp->rxpboffset = 0;
tmxr_debug (TMXR_DBG_PRCV, lp, "Received Packet", (char *)&lp->rxpb[2+fc_size], pktsize);
return SCPE_OK;
}
}
}
*pbuf = NULL;
*psize = 0;
if (lp->conn)
return SCPE_OK;
return SCPE_LOST;
}
/* Poll for input /* Poll for input
@ -1337,16 +1595,17 @@ TMLN *lp;
tmxr_debug_trace (mp, "tmxr_poll_rx()"); tmxr_debug_trace (mp, "tmxr_poll_rx()");
for (i = 0; i < mp->lines; i++) { /* loop thru lines */ for (i = 0; i < mp->lines; i++) { /* loop thru lines */
lp = mp->ldsc + i; /* get line desc */ lp = mp->ldsc + i; /* get line desc */
if (!(lp->sock || lp->serport) || !lp->rcve) /* skip if not connected */ if (!(lp->sock || lp->serport || lp->loopback) ||
!(lp->rcve)) /* skip if not connected */
continue; continue;
nbytes = 0; nbytes = 0;
if (lp->rxbpi == 0) /* need input? */ if (lp->rxbpi == 0) /* need input? */
nbytes = tmxr_read (lp, /* yes, read */ nbytes = tmxr_read (lp, /* yes, read */
TMXR_MAXBUF - TMXR_GUARD); /* leave spc for Telnet cruft */ lp->rxbsz - TMXR_GUARD); /* leave spc for Telnet cruft */
else if (lp->tsta) /* in Telnet seq? */ else if (lp->tsta) /* in Telnet seq? */
nbytes = tmxr_read (lp, /* yes, read to end */ nbytes = tmxr_read (lp, /* yes, read to end */
TMXR_MAXBUF - lp->rxbpi); lp->rxbsz - lp->rxbpi);
if (nbytes < 0) { /* line error? */ if (nbytes < 0) { /* line error? */
if (!lp->txbfd || lp->notelnet) if (!lp->txbfd || lp->notelnet)
@ -1481,7 +1740,7 @@ return;
int32 tmxr_rqln (TMLN *lp) int32 tmxr_rqln (TMLN *lp)
{ {
return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? TMXR_MAXBUF: 0)); return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? lp->rxbsz: 0));
} }
@ -1527,6 +1786,58 @@ if ((lp->txbfd && !lp->notelnet) || (TXBUF_AVAIL(lp) > 1)) {/* room for char (+
return SCPE_STALL; /* char not sent */ return SCPE_STALL; /* char not sent */
} }
/* Store packet in line buffer
Inputs:
*lp = pointer to line descriptor
*buf = pointer to packet data
size = size of packet
frame_char = inter-packet franing character (0 means no frame character)
Outputs:
status = ok, connection lost, or stall
Implementation notea:
1. If the line is not connected, SCPE_LOST is returned.
2. If prior packet transmission still in progress, SCPE_STALL is
returned and no packet data is stored. The caller must retry later.
*/
t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size)
{
return tmxr_put_packet_ln_ex (lp, buf, size, 0);
}
t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 frame_byte)
{
t_stat r;
size_t fc_size = (frame_byte ? 1 : 0);
if (!lp->conn)
return SCPE_LOST;
if (lp->txppoffset < lp->txppsize) {
tmxr_debug (TMXR_DBG_PXMT, lp, "Skipped Sending Packet - Transmit Busy", (char *)&lp->txpb[3], size);
return SCPE_STALL;
}
if (lp->txpbsize < size + 2 + fc_size) {
lp->txpbsize = size + 2 + fc_size;
lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize);
}
lp->txpb[0] = frame_byte;
lp->txpb[0+fc_size] = (size >> 8) & 0xFF;
lp->txpb[1+fc_size] = size & 0xFF;
memcpy (lp->txpb + 2 + fc_size, buf, size);
lp->txppsize = size + 2 + fc_size;
lp->txppoffset = 0;
tmxr_debug (TMXR_DBG_PXMT, lp, "Sending Packet", (char *)&lp->txpb[2+fc_size], size);
++lp->txpcnt;
while ((lp->txppoffset < lp->txppsize) &&
(SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
++lp->txppoffset;
tmxr_send_buffered_data (lp);
return lp->conn ? SCPE_OK : SCPE_LOST;
}
/* Poll for output /* Poll for output
Inputs: Inputs:
@ -1572,6 +1883,7 @@ return;
int32 tmxr_send_buffered_data (TMLN *lp) int32 tmxr_send_buffered_data (TMLN *lp)
{ {
int32 nbytes, sbytes; int32 nbytes, sbytes;
t_stat r;
tmxr_debug_trace_line (lp, "tmxr_send_buffered_data()"); tmxr_debug_trace_line (lp, "tmxr_send_buffered_data()");
nbytes = tmxr_tqln(lp); /* avail bytes */ nbytes = tmxr_tqln(lp); /* avail bytes */
@ -1591,6 +1903,7 @@ if (nbytes) { /* >0? write */
} }
if (sbytes < 0) { /* I/O Error? */ if (sbytes < 0) { /* I/O Error? */
lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */ lp->txbpi = lp->txbpr = 0; /* Drop the data we already know we can't send */
lp->rxpboffset = lp->txppoffset = lp->txppsize = 0;/* Drop the data we already know we can't send */
tmxr_close_ln (lp); /* close line/port on error */ tmxr_close_ln (lp); /* close line/port on error */
return nbytes; /* done now. */ return nbytes; /* done now. */
} }
@ -1606,7 +1919,13 @@ if (nbytes) { /* >0? write */
} }
} }
} /* end if nbytes */ } /* end if nbytes */
return nbytes; while ((lp->txppoffset < lp->txppsize) && /* buffered packet data? */
(lp->txbsz > nbytes) && /* and room in xmt buffer */
(SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
++lp->txppoffset;
if ((nbytes == 0) && (tmxr_tqln(lp) > 0))
return tmxr_send_buffered_data (lp);
return tmxr_tqln(lp) + tmxr_tpqln(lp);
} }
@ -1617,6 +1936,20 @@ int32 tmxr_tqln (TMLN *lp)
return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? lp->txbsz: 0)); return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? lp->txbsz: 0));
} }
/* Return count of buffered packet characters for line */
int32 tmxr_tpqln (TMLN *lp)
{
return (lp->txppsize - lp->txppoffset);
}
/* Return transmit packet busy status for line */
t_bool tmxr_tpbusyln (TMLN *lp)
{
return (0 != (lp->txppsize - lp->txppoffset));
}
static void _mux_detach_line (TMLN *lp, t_bool close_listener, t_bool close_connecting) static void _mux_detach_line (TMLN *lp, t_bool close_listener, t_bool close_connecting)
{ {
if (close_listener && lp->master) { if (close_listener && lp->master) {
@ -1648,6 +1981,7 @@ if (lp->serport) { /* close current serial connection *
free (lp->destination); free (lp->destination);
lp->destination = NULL; lp->destination = NULL;
} }
tmxr_set_line_loopback (lp, FALSE);
} }
t_stat tmxr_detach_ln (TMLN *lp) t_stat tmxr_detach_ln (TMLN *lp)
@ -1678,7 +2012,7 @@ char tbuf[CBUFSIZE], listen[CBUFSIZE], destination[CBUFSIZE],
SOCKET sock; SOCKET sock;
SERHANDLE serport; SERHANDLE serport;
char *tptr = cptr; char *tptr = cptr;
t_bool nolog, notelnet, listennotelnet, unbuffered, modem_control; t_bool nolog, notelnet, listennotelnet, unbuffered, modem_control, loopback;
TMLN *lp; TMLN *lp;
t_stat r = SCPE_ARG; t_stat r = SCPE_ARG;
@ -1696,7 +2030,7 @@ while (*tptr) {
memset(buffered, '\0', sizeof(buffered)); memset(buffered, '\0', sizeof(buffered));
memset(port, '\0', sizeof(port)); memset(port, '\0', sizeof(port));
memset(option, '\0', sizeof(option)); memset(option, '\0', sizeof(option));
nolog = notelnet = listennotelnet = unbuffered = FALSE; nolog = notelnet = listennotelnet = unbuffered = loopback = FALSE;
if (line != -1) if (line != -1)
notelnet = listennotelnet = mp->notelnet; notelnet = listennotelnet = mp->notelnet;
modem_control = mp->modem_control; modem_control = mp->modem_control;
@ -1724,6 +2058,12 @@ while (*tptr) {
strncpy(logfiletmpl, cptr, sizeof(logfiletmpl)-1); strncpy(logfiletmpl, cptr, sizeof(logfiletmpl)-1);
continue; continue;
} }
if (0 == MATCH_CMD (gbuf, "LOOPBACK")) {
if ((NULL != cptr) && ('\0' != *cptr))
return SCPE_2MARG;
loopback = TRUE;
continue;
}
if ((0 == MATCH_CMD (gbuf, "NOBUFFERED")) || if ((0 == MATCH_CMD (gbuf, "NOBUFFERED")) ||
(0 == MATCH_CMD (gbuf, "UNBUFFERED"))) { (0 == MATCH_CMD (gbuf, "UNBUFFERED"))) {
if ((NULL != cptr) && ('\0' != *cptr)) if ((NULL != cptr) && ('\0' != *cptr))
@ -1857,6 +2197,9 @@ while (*tptr) {
mp->buffered = 0; mp->buffered = 0;
for (i = 0; i < mp->lines; i++) { /* default line buffers */ for (i = 0; i < mp->lines; i++) { /* default line buffers */
lp = mp->ldsc + i; lp = mp->ldsc + i;
lp->rxbsz = TMXR_MAXBUF;
lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
lp->txbsz = TMXR_MAXBUF; lp->txbsz = TMXR_MAXBUF;
lp->txb = (char *)realloc(lp->txb, lp->txbsz); lp->txb = (char *)realloc(lp->txb, lp->txbsz);
lp->txbfd = lp->txbpi = lp->txbpr = 0; lp->txbfd = lp->txbpi = lp->txbpr = 0;
@ -1871,6 +2214,9 @@ while (*tptr) {
lp->txbfd = 1; lp->txbfd = 1;
lp->txb = (char *)realloc(lp->txb, lp->txbsz); lp->txb = (char *)realloc(lp->txb, lp->txbsz);
lp->txbpi = lp->txbpr = 0; lp->txbpi = lp->txbpr = 0;
lp->rxbsz = mp->buffered;
lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
} }
} }
if (nolog) { if (nolog) {
@ -1920,6 +2266,17 @@ while (*tptr) {
lp->sock = 0; /* clear the socket */ lp->sock = 0; /* clear the socket */
} }
} }
if (loopback) {
if (mp->lines > 1)
return SCPE_ARG; /* ambiguous */
printf ("Operating in loopback mode\n");
if (sim_log)
fprintf (sim_log, "Operating in loopback mode\n");
for (i = 0; i < mp->lines; i++) {
lp = mp->ldsc + i;
tmxr_set_line_loopback (lp, loopback);
}
}
if (destination[0]) { if (destination[0]) {
if (mp->lines > 1) if (mp->lines > 1)
return SCPE_ARG; /* ambiguous */ return SCPE_ARG; /* ambiguous */
@ -1933,7 +2290,7 @@ while (*tptr) {
free (lp->mp->port); free (lp->mp->port);
lp->mp->port = NULL; lp->mp->port = NULL;
} }
lp->destination = malloc(1+strlen(destination)); lp->destination = (char *)malloc(1+strlen(destination));
strcpy (lp->destination, destination); strcpy (lp->destination, destination);
lp->mp = mp; lp->mp = mp;
lp->serport = serport; lp->serport = serport;
@ -1952,14 +2309,17 @@ while (*tptr) {
sock = sim_connect_sock (destination, "localhost", NULL); sock = sim_connect_sock (destination, "localhost", NULL);
if (sock != INVALID_SOCKET) { if (sock != INVALID_SOCKET) {
_mux_detach_line (lp, FALSE, TRUE); _mux_detach_line (lp, FALSE, TRUE);
lp->destination = malloc(1+strlen(destination)); lp->destination = (char *)malloc(1+strlen(destination));
strcpy (lp->destination, destination); strcpy (lp->destination, destination);
lp->mp = mp; lp->mp = mp;
if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) {
lp->connecting = sock; lp->connecting = sock;
lp->ipad = malloc (1 + strlen (lp->destination)); lp->ipad = (char *)malloc (1 + strlen (lp->destination));
strcpy (lp->ipad, lp->destination); strcpy (lp->ipad, lp->destination);
}
else
sim_close_sock (sock, FALSE);
lp->notelnet = notelnet; lp->notelnet = notelnet;
lp->cnms = sim_os_msec (); /* record time of connection */
tmxr_init_line (lp); /* init the line state */ tmxr_init_line (lp); /* init the line state */
return SCPE_OK; return SCPE_OK;
} }
@ -1989,12 +2349,18 @@ while (*tptr) {
lp->txbsz = TMXR_MAXBUF; lp->txbsz = TMXR_MAXBUF;
lp->txb = (char *)realloc (lp->txb, lp->txbsz); lp->txb = (char *)realloc (lp->txb, lp->txbsz);
lp->txbfd = lp->txbpi = lp->txbpr = 0; lp->txbfd = lp->txbpi = lp->txbpr = 0;
lp->rxbsz = lp->txbsz;
lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
} }
if (buffered[0]) { if (buffered[0]) {
lp->txbsz = atoi(buffered); lp->txbsz = atoi(buffered);
lp->txbfd = 1; lp->txbfd = 1;
lp->txb = (char *)realloc (lp->txb, lp->txbsz); lp->txb = (char *)realloc (lp->txb, lp->txbsz);
lp->txbpi = lp->txbpr = 0; lp->txbpi = lp->txbpr = 0;
lp->rxbsz = lp->txbsz;
lp->rxb = (char *)realloc(lp->rxb, lp->rxbsz);
lp->rbr = (char *)realloc(lp->rbr, lp->rxbsz);
} }
if (nolog) { if (nolog) {
free(lp->txlogname); free(lp->txlogname);
@ -2028,7 +2394,7 @@ while (*tptr) {
serport = sim_open_serial (destination, lp, &r); serport = sim_open_serial (destination, lp, &r);
if (serport != INVALID_HANDLE) { if (serport != INVALID_HANDLE) {
_mux_detach_line (lp, TRUE, TRUE); _mux_detach_line (lp, TRUE, TRUE);
lp->destination = malloc(1+strlen(destination)); lp->destination = (char *)malloc(1+strlen(destination));
strcpy (lp->destination, destination); strcpy (lp->destination, destination);
lp->serport = serport; lp->serport = serport;
lp->ser_connect_pending = TRUE; lp->ser_connect_pending = TRUE;
@ -2046,19 +2412,28 @@ while (*tptr) {
sock = sim_connect_sock (destination, "localhost", NULL); sock = sim_connect_sock (destination, "localhost", NULL);
if (sock != INVALID_SOCKET) { if (sock != INVALID_SOCKET) {
_mux_detach_line (lp, FALSE, TRUE); _mux_detach_line (lp, FALSE, TRUE);
lp->destination = malloc(1+strlen(destination)); lp->destination = (char *)malloc(1+strlen(destination));
strcpy (lp->destination, destination); strcpy (lp->destination, destination);
if (!lp->modem_control || (lp->modembits & TMXR_MDM_DTR)) {
lp->connecting = sock; lp->connecting = sock;
lp->ipad = malloc (1 + strlen (lp->destination)); lp->ipad = (char *)malloc (1 + strlen (lp->destination));
strcpy (lp->ipad, lp->destination); strcpy (lp->ipad, lp->destination);
}
else
sim_close_sock (sock, FALSE);
lp->notelnet = notelnet; lp->notelnet = notelnet;
lp->cnms = sim_os_msec (); /* record time of connection */
tmxr_init_line (lp); /* init the line state */ tmxr_init_line (lp); /* init the line state */
} }
else else
return SCPE_ARG; return SCPE_ARG;
} }
} }
if (loopback) {
tmxr_set_line_loopback (lp, loopback);
printf ("Line %d operating in loopback mode\n", line);
if (sim_log)
fprintf (sim_log, "Line %d operating in loopback mode\n", line);
}
lp->modem_control = modem_control; lp->modem_control = modem_control;
r = SCPE_OK; r = SCPE_OK;
} }
@ -2188,9 +2563,9 @@ pthread_setschedparam (pthread_self(), sched_policy, &sched_priority);
sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - starting\n"); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_poll() - starting\n");
units = calloc(FD_SETSIZE, sizeof(*units)); units = (UNIT **)calloc(FD_SETSIZE, sizeof(*units));
activated = calloc(FD_SETSIZE, sizeof(*activated)); activated = (UNIT **)calloc(FD_SETSIZE, sizeof(*activated));
sockets = calloc(FD_SETSIZE, sizeof(*sockets)); sockets = (SOCKET *)calloc(FD_SETSIZE, sizeof(*sockets));
timeout_usec = 1000000; timeout_usec = 1000000;
pthread_mutex_lock (&sim_tmxr_poll_lock); pthread_mutex_lock (&sim_tmxr_poll_lock);
pthread_cond_signal (&sim_tmxr_startup_cond); /* Signal we're ready to go */ pthread_cond_signal (&sim_tmxr_startup_cond); /* Signal we're ready to go */
@ -2407,9 +2782,9 @@ pthread_setschedparam (pthread_self(), sched_policy, &sched_priority);
sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - starting\n"); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - starting\n");
units = calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*units)); units = (UNIT **)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*units));
activated = calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*activated)); activated = (UNIT **)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*activated));
serports = calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*serports)); serports = (SERHANDLE *)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*serports));
timeout_usec = 1000000; timeout_usec = 1000000;
pthread_mutex_lock (&sim_tmxr_poll_lock); pthread_mutex_lock (&sim_tmxr_poll_lock);
pthread_cond_signal (&sim_tmxr_serial_startup_cond); /* Signal we're ready to go */ pthread_cond_signal (&sim_tmxr_serial_startup_cond); /* Signal we're ready to go */
@ -2623,8 +2998,8 @@ pthread_setschedparam (pthread_self(), sched_policy, &sched_priority);
sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - starting\n"); sim_debug (TMXR_DBG_ASY, dptr, "_tmxr_serial_poll() - starting\n");
lines = calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*lines)); lines = (TMLN **)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*lines));
threads = calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*threads)); threads = (pthread_t *)calloc(MAXIMUM_WAIT_OBJECTS, sizeof(*threads));
pthread_mutex_lock (&sim_tmxr_poll_lock); pthread_mutex_lock (&sim_tmxr_poll_lock);
pthread_cond_signal (&sim_tmxr_serial_startup_cond); /* Signal we're ready to go */ pthread_cond_signal (&sim_tmxr_serial_startup_cond); /* Signal we're ready to go */
pthread_cond_init (&sim_serial_line_startup_cond, NULL); pthread_cond_init (&sim_serial_line_startup_cond, NULL);
@ -2863,6 +3238,8 @@ else {
fprintf (st, " - Unit: %s", sim_uname (lp->uptr)); fprintf (st, " - Unit: %s", sim_uname (lp->uptr));
if (mp->modem_control != lp->modem_control) if (mp->modem_control != lp->modem_control)
fprintf(st, ", ModemControl=%s", lp->modem_control ? "enabled" : "disabled"); fprintf(st, ", ModemControl=%s", lp->modem_control ? "enabled" : "disabled");
if (lp->loopback)
fprintf(st, ", Loopback");
fprintf (st, "\n"); fprintf (st, "\n");
} }
if ((!lp->sock) && (!lp->connecting) && (!lp->serport) && (!lp->master)) { if ((!lp->sock) && (!lp->connecting) && (!lp->serport) && (!lp->master)) {
@ -2924,6 +3301,10 @@ for (i = 0; i < mp->lines; i++) { /* loop thru conn */
} }
free (lp->txb); free (lp->txb);
lp->txb = NULL; lp->txb = NULL;
free (lp->rxb);
lp->rxb = NULL;
free (lp->rbr);
lp->rbr = NULL;
lp->modembits = 0; lp->modembits = 0;
} }
@ -3142,6 +3523,17 @@ else {
fprintf (st, "When symmetric virtual connections are configured, incoming connections\n"); fprintf (st, "When symmetric virtual connections are configured, incoming connections\n");
fprintf (st, "on the specified listening port are checked to assure that they actually\n"); fprintf (st, "on the specified listening port are checked to assure that they actually\n");
fprintf (st, "come from the specified connection destination host system.\n\n"); fprintf (st, "come from the specified connection destination host system.\n\n");
if (single_line) { /* Single Line Multiplexer */
fprintf (st, "The %s device can be attached in LOOPBACK mode:\n\n", dptr->name);
fprintf (st, " sim> ATTACH %s Loopback\n\n", dptr->name);
}
else {
fprintf (st, "A line on the %s device can be attached in LOOPBACK mode:\n\n", dptr->name);
fprintf (st, " sim> ATTACH %s Line=n,Loopback\n\n", dptr->name);
}
fprintf (st, "When operating in LOOPBACK mode, all outgoing data arrives as input and\n");
fprintf (st, "outgoing modem signals (if enabled) (DTR and RTS) are reflected in the\n");
fprintf (st, "incoming modem signals (DTR->(DCD and DSR), RTS->CTS)\n\n");
if (single_line) /* Single Line Multiplexer */ if (single_line) /* Single Line Multiplexer */
fprintf (st, "The connection configured for the %s device is unconfigured by:\n\n", dptr->name); fprintf (st, "The connection configured for the %s device is unconfigured by:\n\n", dptr->name);
else else
@ -3285,6 +3677,9 @@ if ((lp->sock) || (lp->connecting)) { /* tcp connection? */
else /* incoming connection */ else /* incoming connection */
fprintf (st, "Connection from IP address %s\n", lp->ipad); fprintf (st, "Connection from IP address %s\n", lp->ipad);
} }
else
if (lp->destination) /* remote connection? */
fprintf (st, "Connecting to remote port %s\n", lp->destination);/* print port name */
if (lp->sock) { if (lp->sock) {
char *sockname, *peername; char *sockname, *peername;
@ -3344,12 +3739,15 @@ else {
fprintf (st, "\n"); fprintf (st, "\n");
fprintf (st, " input (%s)", (lp->rcve? enab: dsab)); fprintf (st, " input (%s)", (lp->rcve? enab: dsab));
if (lp->rxcnt) if (lp->rxcnt)
fprintf (st, " queued/total = %d/%d", fprintf (st, " queued/total = %d/%d", tmxr_rqln (lp), lp->rxcnt);
tmxr_rqln (lp), lp->rxcnt); if (lp->rxpcnt)
fprintf (st, " packets = %d", lp->rxpcnt);
fprintf (st, "\n output (%s)", (lp->xmte? enab: dsab)); fprintf (st, "\n output (%s)", (lp->xmte? enab: dsab));
if (lp->txcnt || lp->txbpi) if (lp->txcnt || lp->txbpi)
fprintf (st, " queued/total = %d/%d", fprintf (st, " queued/total = %d/%d", tmxr_tqln (lp), lp->txcnt);
tmxr_tqln (lp), lp->txcnt); if (lp->txpcnt || tmxr_tpqln (lp))
fprintf (st, " packet data queued/packets sent = %d/%d",
tmxr_tpqln (lp), lp->txpcnt);
fprintf (st, "\n"); fprintf (st, "\n");
} }
if (lp->txbfd) if (lp->txbfd)
@ -3858,7 +4256,7 @@ if ((lp->mp->dptr) && (dbits & lp->mp->dptr->dctrl)) {
if (tmxr_debug_buf) if (tmxr_debug_buf)
tmxr_debug_buf[tmxr_debug_buf_used] = '\0'; tmxr_debug_buf[tmxr_debug_buf_used] = '\0';
if (!lp->notelnet) { if (lp->notelnet) {
int same, group, sidx, oidx; int same, group, sidx, oidx;
char outbuf[80], strbuf[18]; char outbuf[80], strbuf[18];
static char hex[] = "0123456789ABCDEF"; static char hex[] = "0123456789ABCDEF";

View file

@ -64,15 +64,17 @@ typedef int SERHANDLE;
#define TMXR_GUARD 12 /* buffer guard */ #define TMXR_GUARD 12 /* buffer guard */
#define TMXR_DTR_DROP_TIME 500 /* milliseconds to drop DTR for 'pseudo' modem control */ #define TMXR_DTR_DROP_TIME 500 /* milliseconds to drop DTR for 'pseudo' modem control */
#define TMXR_CONNECT_POLL_INTERVAL 1000 /* milliseconds between connection polls */ #define TMXR_DEFAULT_CONNECT_POLL_INTERVAL 1 /* seconds between connection polls */
#define TMXR_DBG_XMT 0x010000 /* Debug Transmit Data */ #define TMXR_DBG_XMT 0x0010000 /* Debug Transmit Data */
#define TMXR_DBG_RCV 0x020000 /* Debug Received Data */ #define TMXR_DBG_RCV 0x0020000 /* Debug Received Data */
#define TMXR_DBG_RET 0x040000 /* Debug Returned Received Data */ #define TMXR_DBG_RET 0x0040000 /* Debug Returned Received Data */
#define TMXR_DBG_MDM 0x080000 /* Debug Modem Signals */ #define TMXR_DBG_MDM 0x0080000 /* Debug Modem Signals */
#define TMXR_DBG_CON 0x100000 /* Debug Connection Activities */ #define TMXR_DBG_CON 0x0100000 /* Debug Connection Activities */
#define TMXR_DBG_ASY 0x200000 /* Debug Asynchronous Activities */ #define TMXR_DBG_ASY 0x0200000 /* Debug Asynchronous Activities */
#define TMXR_DBG_TRC 0x400000 /* Debug trace routine calls */ #define TMXR_DBG_TRC 0x0400000 /* Debug trace routine calls */
#define TMXR_DBG_PXMT 0x0800000 /* Debug Transmit Packet Data */
#define TMXR_DBG_PRCV 0x1000000 /* Debug Received Packet Data */
/* Modem Control Bits */ /* Modem Control Bits */
@ -95,6 +97,11 @@ typedef int SERHANDLE;
typedef struct tmln TMLN; typedef struct tmln TMLN;
typedef struct tmxr TMXR; typedef struct tmxr TMXR;
struct loopbuf {
int32 bpr; /* xmt buf remove */
int32 bpi; /* xmt buf insert */
int32 size;
};
struct tmln { struct tmln {
int conn; /* line connected flag */ int conn; /* line connected flag */
@ -111,10 +118,13 @@ struct tmln {
t_bool notelnet; /* raw binary data (no telnet interpretation) */ t_bool notelnet; /* raw binary data (no telnet interpretation) */
int32 rxbpr; /* rcv buf remove */ int32 rxbpr; /* rcv buf remove */
int32 rxbpi; /* rcv buf insert */ int32 rxbpi; /* rcv buf insert */
int32 rxbsz; /* rcv buffer size */
int32 rxcnt; /* rcv count */ int32 rxcnt; /* rcv count */
int32 rxpcnt; /* rcv packet count */
int32 txbpr; /* xmt buf remove */ int32 txbpr; /* xmt buf remove */
int32 txbpi; /* xmt buf insert */ int32 txbpi; /* xmt buf insert */
int32 txcnt; /* xmt count */ int32 txcnt; /* xmt count */
int32 txpcnt; /* xmt packet count */
int32 txdrp; /* xmt drop count */ int32 txdrp; /* xmt drop count */
int32 txbsz; /* xmt buffer size */ int32 txbsz; /* xmt buffer size */
int32 txbfd; /* xmt buffered flag */ int32 txbfd; /* xmt buffered flag */
@ -123,15 +133,29 @@ struct tmln {
FILE *txlog; /* xmt log file */ FILE *txlog; /* xmt log file */
FILEREF *txlogref; /* xmt log file reference */ FILEREF *txlogref; /* xmt log file reference */
char *txlogname; /* xmt log file name */ char *txlogname; /* xmt log file name */
char rxb[TMXR_MAXBUF]; /* rcv buffer */ char *rxb; /* rcv buffer */
char rbr[TMXR_MAXBUF]; /* rcv break */ char *rbr; /* rcv break */
char *txb; /* xmt buffer */ char *txb; /* xmt buffer */
uint8 *rxpb; /* rcv packet buffer */
uint32 rxpbsize; /* rcv packet buffer size */
uint32 rxpboffset; /* rcv packet buffer offset */
uint8 *txpb; /* xmt packet buffer */
uint32 txpbsize; /* xmt packet buffer size */
uint32 txppsize; /* xmt packet packet size */
uint32 txppoffset; /* xmt packet buffer offset */
TMXR *mp; /* back pointer to mux */ TMXR *mp; /* back pointer to mux */
char *serconfig; /* line config */ char *serconfig; /* line config */
SERHANDLE serport; /* serial port handle */ SERHANDLE serport; /* serial port handle */
t_bool ser_connect_pending; /* serial connection notice pending */ t_bool ser_connect_pending; /* serial connection notice pending */
SOCKET connecting; /* Outgoing socket while connecting */ SOCKET connecting; /* Outgoing socket while connecting */
char *destination; /* Outgoing destination address:port */ char *destination; /* Outgoing destination address:port */
t_bool loopback; /* Line in loopback mode */
t_bool halfduplex; /* Line in half-duplex mode */
int32 lpbpr; /* loopback buf remove */
int32 lpbpi; /* loopback buf insert */
int32 lpbcnt; /* loopback buf used count */
int32 lpbsz; /* loopback buffer size */
char *lpb; /* loopback buffer */
UNIT *uptr; /* input polling unit (default to mp->uptr) */ UNIT *uptr; /* input polling unit (default to mp->uptr) */
UNIT *o_uptr; /* output polling unit (default to lp->uptr)*/ UNIT *o_uptr; /* output polling unit (default to lp->uptr)*/
}; };
@ -148,6 +172,7 @@ struct tmxr {
int32 txcount; /* count of transmit bytes */ int32 txcount; /* count of transmit bytes */
int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */ int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */
int32 sessions; /* count of tcp connections received */ int32 sessions; /* count of tcp connections received */
uint32 poll_interval; /* frequency of connection polls (seconds) */
uint32 last_poll_time; /* time of last connection poll */ uint32 last_poll_time; /* time of last connection poll */
t_bool notelnet; /* default telnet capability for incoming connections */ t_bool notelnet; /* default telnet capability for incoming connections */
t_bool modem_control; /* multiplexer supports modem control behaviors */ t_bool modem_control; /* multiplexer supports modem control behaviors */
@ -157,12 +182,17 @@ int32 tmxr_poll_conn (TMXR *mp);
t_stat tmxr_reset_ln (TMLN *lp); t_stat tmxr_reset_ln (TMLN *lp);
t_stat tmxr_detach_ln (TMLN *lp); t_stat tmxr_detach_ln (TMLN *lp);
int32 tmxr_getc_ln (TMLN *lp); int32 tmxr_getc_ln (TMLN *lp);
t_stat tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, size_t *psize);
t_stat tmxr_get_packet_ln_ex (TMLN *lp, const uint8 **pbuf, size_t *psize, uint8 frame_byte);
void tmxr_poll_rx (TMXR *mp); void tmxr_poll_rx (TMXR *mp);
t_stat tmxr_putc_ln (TMLN *lp, int32 chr); t_stat tmxr_putc_ln (TMLN *lp, int32 chr);
t_stat tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size);
t_stat tmxr_put_packet_ln_ex (TMLN *lp, const uint8 *buf, size_t size, uint8 frame_byte);
void tmxr_poll_tx (TMXR *mp); void tmxr_poll_tx (TMXR *mp);
int32 tmxr_send_buffered_data (TMLN *lp); int32 tmxr_send_buffered_data (TMLN *lp);
t_stat tmxr_open_master (TMXR *mp, char *cptr); t_stat tmxr_open_master (TMXR *mp, char *cptr);
t_stat tmxr_close_master (TMXR *mp); t_stat tmxr_close_master (TMXR *mp);
t_stat tmxr_connection_poll_interval (TMXR *mp, uint32 seconds);
t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, char *cptr, t_bool async); t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, char *cptr, t_bool async);
t_stat tmxr_detach (TMXR *mp, UNIT *uptr); t_stat tmxr_detach (TMXR *mp, UNIT *uptr);
t_stat tmxr_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat tmxr_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
@ -170,6 +200,10 @@ char *tmxr_line_attach_string(TMLN *lp);
t_stat tmxr_set_modem_control_passthru (TMXR *mp); t_stat tmxr_set_modem_control_passthru (TMXR *mp);
t_stat tmxr_clear_modem_control_passthru (TMXR *mp); t_stat tmxr_clear_modem_control_passthru (TMXR *mp);
t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits); t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits);
t_stat tmxr_set_line_loopback (TMLN *lp, t_bool enable_loopback);
t_bool tmxr_get_line_loopback (TMLN *lp);
t_stat tmxr_set_line_halfduplex (TMLN *lp, t_bool enable_loopback);
t_bool tmxr_get_line_halfduplex (TMLN *lp);
t_stat tmxr_set_config_line (TMLN *lp, char *config); t_stat tmxr_set_config_line (TMLN *lp, char *config);
t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll); t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll);
t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll); t_stat tmxr_set_line_output_unit (TMXR *mp, int line, UNIT *uptr_poll);
@ -187,6 +221,8 @@ t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat tmxr_dscln (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tmxr_dscln (UNIT *uptr, int32 val, char *cptr, void *desc);
int32 tmxr_rqln (TMLN *lp); int32 tmxr_rqln (TMLN *lp);
int32 tmxr_tqln (TMLN *lp); int32 tmxr_tqln (TMLN *lp);
int32 tmxr_tpqln (TMLN *lp);
t_bool tmxr_tpbusyln (TMLN *lp);
t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, char *cptr, void *desc); t_stat tmxr_set_lnorder (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, void *desc);