THESE HAVE NOT BEEN THOROUGHLY TESTED - USE AT YOUR OWN RISK!
I've worked with software long enough to know that any good-intentioned changes can have unintended consequences. That being said, with these patches in place, I was able to fire up CCGMS 2021, connect to tcpser using an ACIA and a Userport modem, kill and restart tcpser, and ccgms was able to recover the connection after the second character had been sent to the modem. Previously, you would have to (at the least) disable the ACIA or UserPort RS232 emulation and re-enable whichever one you were using in Vice before it would reconnect to tcpser. If not, you'd just see endless messages in Vice's console about trying to send bytes and the pipe being closed. I know it is closed - open it up again and fire that data down it!
The Vice diff: (I couldn't get any of the auto tools to remove all the .deps folders it created so they kind of got littered in here too. Sorry.)
Code: Select all
Only in vice-3.6.1a/doc/html: vice.texi
diff -ur vice-3.6.1/src/aciacore.c vice-3.6.1a/src/aciacore.c
--- vice-3.6.1/src/aciacore.c 2022-01-23 17:13:24.000000000 -0700
+++ vice-3.6.1a/src/aciacore.c 2022-04-19 12:29:04.095115095 -0600
@@ -1146,10 +1146,14 @@
DEBUG_VERBOSE_LOG_MESSAGE((acia.log, "int_acia_tx(offset=%ld, myclk=%d", offset, myclk));
assert(data == NULL);
-
+
if ((acia.in_tx == ACIA_TX_STATE_DR_WRITTEN) && (acia.fd >= 0)) {
- rs232drv_putc(acia.fd, acia.txdata & acia.datamask);
-
+ int p_res = rs232drv_putc(acia.fd, acia.txdata & acia.datamask);
+
+ if (p_res < 0) { // JF - Failed to send, so close the device as it is dead anyway.
+ rs232drv_close(acia.fd);
+ acia.fd = -1;
+ }
/* tell the status register that the transmit register is empty */
acia.status |= ACIA_SR_BITS_TRANSMIT_DR_EMPTY;
}
@@ -1216,8 +1220,13 @@
if (acia.fd < 0) {
break;
}
+ int g_res = rs232drv_getc(acia.fd, &received_byte);
- if (!rs232drv_getc(acia.fd, &received_byte)) {
+// if (g_res == -1) { // JF
+// rs232drv_close(acia.fd);
+// acia.fd = -1;
+// } else if (g_res == 0) {
+ if (!g_res) {
break;
}
Only in vice-3.6.1/src/arch/android/AnVICE: AndroidManifest.xml
Only in vice-3.6.1a/src/arch/gtk3: .deps
Only in vice-3.6.1a/src/arch/gtk3/novte: .deps
Only in vice-3.6.1/src/arch/gtk3/novte: vtetypebuiltins.h
Only in vice-3.6.1a/src/arch/gtk3/widgets/base: .deps
Only in vice-3.6.1a/src/arch/gtk3/widgets: .deps
Only in vice-3.6.1a/src/arch/headless: .deps
Only in vice-3.6.1a/src/arch/sdl: .deps
Only in vice-3.6.1a/src/arch/shared: .deps
Only in vice-3.6.1a/src/c128: .deps
Only in vice-3.6.1a/src/c64/cart: .deps
Only in vice-3.6.1a/src/c64: .deps
Only in vice-3.6.1/src/c64: psiddrv.h
Only in vice-3.6.1a/src/c64dtv: .deps
Only in vice-3.6.1a/src/cbm2/cart: .deps
Only in vice-3.6.1a/src/cbm2: .deps
Only in vice-3.6.1a/src/core: .deps
Only in vice-3.6.1a/src/crtc: .deps
Only in vice-3.6.1a/src/datasette: .deps
Only in vice-3.6.1/src: debug.h
Only in vice-3.6.1a/src: .deps
Only in vice-3.6.1a/src/diag: .deps
Only in vice-3.6.1a/src/diskimage: .deps
Only in vice-3.6.1a/src/drive: .deps
Only in vice-3.6.1a/src/drive/iec/c64exp: .deps
Only in vice-3.6.1a/src/drive/iec: .deps
Only in vice-3.6.1a/src/drive/iec/plus4exp: .deps
Only in vice-3.6.1a/src/drive/iec128dcr: .deps
Only in vice-3.6.1a/src/drive/iecieee: .deps
Only in vice-3.6.1a/src/drive/ieee: .deps
Only in vice-3.6.1a/src/drive/tcbm: .deps
Only in vice-3.6.1a/src/fileio: .deps
Only in vice-3.6.1a/src/fsdevice: .deps
Only in vice-3.6.1a/src/gfxoutputdrv: .deps
Only in vice-3.6.1a/src/hvsc: .deps
Only in vice-3.6.1a/src/hwsiddrv: .deps
Only in vice-3.6.1a/src/iecbus: .deps
Only in vice-3.6.1a/src/imagecontents: .deps
Only in vice-3.6.1a/src/iodrv: .deps
Only in vice-3.6.1a/src/joyport: .deps
Only in vice-3.6.1a/src/joystickdrv: .deps
Only in vice-3.6.1a/src/lib/linenoise-ng: .deps
Only in vice-3.6.1a/src/lib/p64: .deps
Only in vice-3.6.1a/src/mididrv: .deps
Only in vice-3.6.1a/src/monitor: .deps
Only in vice-3.6.1/src/monitor: mon_lex.c
Only in vice-3.6.1/src/monitor: mon_parse.c
Only in vice-3.6.1/src/monitor: mon_parse.h
Only in vice-3.6.1a/src/parallel: .deps
Only in vice-3.6.1a/src/pet: .deps
Only in vice-3.6.1a/src/plus4/cart: .deps
Only in vice-3.6.1a/src/plus4: .deps
Only in vice-3.6.1a/src/printerdrv: .deps
Only in vice-3.6.1a/src/raster: .deps
Only in vice-3.6.1a/src/resid: .deps
Only in vice-3.6.1/src/resid: wave6581_PS_.h
Only in vice-3.6.1/src/resid: wave6581_PST.h
Only in vice-3.6.1/src/resid: wave6581_P_T.h
Only in vice-3.6.1/src/resid: wave6581__ST.h
Only in vice-3.6.1/src/resid: wave8580_PS_.h
Only in vice-3.6.1/src/resid: wave8580_PST.h
Only in vice-3.6.1/src/resid: wave8580_P_T.h
Only in vice-3.6.1/src/resid: wave8580__ST.h
Only in vice-3.6.1a/src/resid-dtv: .deps
Only in vice-3.6.1a/src/rs232drv: .deps
diff -ur vice-3.6.1/src/rs232drv/rs232net.c vice-3.6.1a/src/rs232drv/rs232net.c
--- vice-3.6.1/src/rs232drv/rs232net.c 2022-01-23 17:13:25.000000000 -0700
+++ vice-3.6.1a/src/rs232drv/rs232net.c 2022-04-19 11:49:21.304559607 -0600
@@ -236,7 +236,9 @@
n = vice_network_send(fds[fd].fd, &b, 1, 0);
if (n < 0) {
log_error(rs232net_log, "Error writing: %d.", vice_network_get_errorcode());
+ rs232net_close(fd); // JF
rs232net_closesocket(fd);
+ // XXX //
return -1;
}
diff -ur vice-3.6.1/src/rs232drv/rsuser.c vice-3.6.1a/src/rs232drv/rsuser.c
--- vice-3.6.1/src/rs232drv/rsuser.c 2022-01-23 17:13:25.000000000 -0700
+++ vice-3.6.1a/src/rs232drv/rsuser.c 2022-04-19 20:48:01.526786118 -0600
@@ -70,7 +70,7 @@
static void int_rsuser(CLOCK offset, void *data);
#undef DEBUG
-
+#define DEBUG
/* #define LOG_MODEM_STATUS */
@@ -560,7 +560,15 @@
c = (buf >> (valid - 9)) & 0xff;
if (fd >= 0) {
LOG_DEBUG(("\"%c\" (%02x).", code[c], code[c]));
- rs232drv_putc(fd, ((uint8_t)(code[c])));
+ int p_res = rs232drv_putc(fd, ((uint8_t)(code[c])));
+
+ if (p_res < 0) { // JF - Failed to send, so close the device as it is dead anyway.
+// rs232drv_close(fd);
+// fd = -1;
+ rsuser_reset();
+ LOG_DEBUG(("Cubeinc Send Fail Detected"));
+ // Try this, and if this doesn't work, try set_up_device(fd, NULL);
+ }
}
}
valid -= 10;
@@ -720,6 +728,7 @@
switch (rxstate) {
case 0:
if ((rsuser_rtsinv ? 0 : RTS_OUT) == rts && fd >= 0 && rs232drv_getc(fd, &rxdata)) {
+ LOG_DEBUG(("\"%c\" (%02x).", code[rxdata], code[rxdata]));
/* byte received, signal startbit on flag */
rxstate++;
if (start_bit_trigger) {
Only in vice-3.6.1a/src/rtc: .deps
Only in vice-3.6.1a/src/samplerdrv: .deps
Only in vice-3.6.1a/src/scpu64: .deps
Only in vice-3.6.1a/src/serial: .deps
Only in vice-3.6.1a/src/sid: .deps
Only in vice-3.6.1a/src/socketdrv: .deps
Only in vice-3.6.1a/src/sounddrv: .deps
Only in vice-3.6.1a/src/tape: .deps
Only in vice-3.6.1a/src/tapeport: .deps
Only in vice-3.6.1a/src/tools/cartconv: .deps
Only in vice-3.6.1a/src/tools/petcat: .deps
Only in vice-3.6.1a/src/userport: .deps
Only in vice-3.6.1a/src/vdc: .deps
Only in vice-3.6.1a/src/vdrive: .deps
Only in vice-3.6.1/src: version.h
Only in vice-3.6.1a/src/vic20/cart: .deps
Only in vice-3.6.1a/src/vic20: .deps
Only in vice-3.6.1/src: vice-version.sh
Only in vice-3.6.1a/src/vicii: .deps
Only in vice-3.6.1a/src/viciisc: .deps
Only in vice-3.6.1a/src/viciivsid: .deps
Only in vice-3.6.1a/src/video: .deps
Code: Select all
diff -ur tcpser-master/src/ip.c tcpser-master-mod/src/ip.c
--- tcpser-master/src/ip.c 2021-04-25 07:47:46.000000000 -0600
+++ tcpser-master-mod/src/ip.c 2022-04-18 20:47:33.641492849 -0600
@@ -114,13 +114,13 @@
/* grab an Internet domain socket */
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
- ELOG(LOG_ERROR, "could not create client socket");
+ ELOG(LOG_ERROR, "Could not create client socket");
return -1;
}
/* connect to PORT on HOST */
if (connect(sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) {
- ELOG(LOG_ERROR, "could not connect to address");
+ ELOG(LOG_ERROR, "Could not connect to address");
return -1;
}
LOG(LOG_INFO, "Connection to %s established", addy);
diff -ur tcpser-master/src/line.c tcpser-master-mod/src/line.c
--- tcpser-master/src/line.c 2021-04-25 07:47:46.000000000 -0600
+++ tcpser-master-mod/src/line.c 2022-04-18 20:45:25.260981811 -0600
@@ -105,7 +105,8 @@
return 0;
}
else {
- LOG(LOG_ALL, "Could not connect to %s", addy);
+ strerror_r(errno, cfg->last_error, 65); // Store the error message here
+ LOG(LOG_ALL, "Could not connect to %s: [%s]", addy, strerror(errno));
cfg->line_data.valid_conn = FALSE;
return -1;
}
diff -ur tcpser-master/src/modem_core.c tcpser-master-mod/src/modem_core.c
--- tcpser-master/src/modem_core.c 2021-04-25 07:47:46.000000000 -0600
+++ tcpser-master-mod/src/modem_core.c 2022-04-19 13:06:19.696187344 -0600
@@ -185,19 +185,27 @@
str[0] = data;
- dce_write(cfg, str, 1);
+ int b = dce_write(cfg, str, 1);
+ if (1 > b) { // Not all bytes were sent - close connection
+
+ }
}
void mdm_write(modem_config *cfg, unsigned char *data, int len)
{
- unsigned char *buf;
- int i;
+ unsigned char *buf = NULL;
+ int i, b, sent = 0, e = 0, fatal = 0;
unsigned int v;
if (cfg->allow_transmit == TRUE) {
if (cfg->parity) {
buf = malloc(len);
+ if (NULL == buf) {
+ ELOG(LOG_FATAL, "Out of memory");
+ exit(-1);
+ }
+
memcpy(buf, data, len);
for (i = 0; i < len; i++) {
@@ -206,12 +214,132 @@
v = gen_parity(v);
buf[i] |= (((cfg->parity >> v)) & 1) << 7;
}
-
- dce_write(cfg, buf, len);
- free(buf);
}
- else
- dce_write(cfg, data, len);
+
+ while (sent < len)
+ {
+ if (cfg->parity) {
+ b = dce_write(cfg, buf + sent, len - sent);
+ } else {
+ b = dce_write(cfg, data+ sent, len - sent);
+ }
+ e = errno;
+ // send() and write() will return the number of bytes they sent, which can be anywhere
+ // from 1 up to len. It is perfectly valid that they may not send all the bytes in one
+ // pass, therefore we must handle those situations appropriately.
+ // If it is detected that the socket descriptor is closed, they will return 0.
+ // In this case, we must close the socket descriptor and clean up. If any other
+ // error is detected (IE: An invalid socket descriptor is given,
+ if (0 == b) { // Socket is closed. Close here and clean up:
+ LOG(LOG_INFO, "Error in DCE connection [%s]", strerror(e));
+ mdm_disconnect(cfg);
+ } else if (0 > b) { // b < 0 -- An error has occurred, though not all are fatal.
+ switch(e)
+ {
+ // -- These errors are not fatal - usually we just call send() again -- //
+
+
+ case EMSGSIZE:
+ /* The socket type requires that message be sent atomically,
+ and the size of the message to be sent made this
+ impossible. */
+ // While this does not indicate a connection error, we must reduce
+ // the size of this message (or drop it entirely) or we will never
+ // get out of this loop.
+ len--; // Decrease the length of data to send until it will fit?
+ // If there is a better way to handle this, I'm keen to learn it...
+// case EAGAIN: // This has the same numeric code as EWOULDBLOCK so compiler complains if both are present.
+ case EWOULDBLOCK:
+ /* EWOULDBLOCK The socket is marked nonblocking and the requested
+ operation would block. POSIX.1-2001 allows either error
+ to be returned for this case, and does not require these
+ constants to have the same value, so a portable
+ application should check for both possibilities.
+
+ EAGAIN (Internet domain datagram sockets) The socket referred to
+ by sockfd had not previously been bound to an address and,
+ upon attempting to bind it to an ephemeral port, it was
+ determined that all port numbers in the ephemeral port
+ range are currently in use. See the discussion of
+ /proc/sys/net/ipv4/ip_local_port_range in ip(7). */
+ case EALREADY:
+ /* Another Fast Open is in progress. */
+ case EINTR:
+ /* A signal occurred before any data was transmitted; see signal(7). */
+ case EISCONN:
+ /* The connection-mode socket was connected already but a
+ recipient was specified. (Now either this error is
+ returned, or the recipient specification is ignored.) */
+ case ENOBUFS:
+ /* The output queue for a network interface was full. This
+ generally indicates that the interface has stopped
+ sending, but may be caused by transient congestion.
+ (Normally, this does not occur in Linux. Packets are just
+ silently dropped when a device queue overflows.) */
+ case ENOMEM:
+ /* No memory available. */
+
+ ELOG(LOG_DEBUG, "Non-fatal error occurred on DCE.");
+ continue; // <- while() loop
+ break; // <- switch()
+
+ // -- These errors mean the socket is no longer operable: -- //
+
+ case EBADF:
+ /* sockfd is not a valid open file descriptor. */
+ case ECONNRESET:
+ /* Connection reset by peer. */
+ case EDESTADDRREQ:
+ /* The socket is not connection-mode, and no peer address is set. */
+ case EFAULT:
+ /* An invalid user space address was specified for an argument. */
+ case ENOTCONN:
+ /* The socket is not connected, and no target has been given. */
+ case ENOTSOCK:
+ /* The file descriptor sockfd does not refer to a socket. */
+ case EOPNOTSUPP:
+ /* Some bit in the flags argument is inappropriate for the
+ socket type. */
+ case EPIPE:
+ /* The local end has been shut down on a connection oriented
+ socket. In this case, the process will also receive a
+ SIGPIPE unless MSG_NOSIGNAL is set. */
+
+ // These errors are fatal - close the socket and exit the program:
+
+ case EACCES:
+ /* (For UNIX domain sockets, which are identified by
+ pathname) Write permission is denied on the destination
+ socket file, or search permission is denied for one of the
+ directories the path prefix. */
+
+ fatal = 1;
+ case EINVAL:
+ /* Invalid argument passed. */
+ fatal = 1;
+
+ ELOG(LOG_ERROR, "Error in DCE connection [%s]", strerror(e));
+ mdm_disconnect(cfg);
+ sent = len; // So we can get out of the while() loop.
+ break; // <- select()
+
+ default: /* Every other scenario */
+ ELOG(LOG_ERROR, "Unhandled error in DCE connection [%s]", strerror(e));
+ break;
+ } // End of switch()
+
+ } else {
+ sent += b;
+ }
+ } // End of while() loop
+ }
+
+ if (buf != NULL) {
+ free(buf);
+ }
+ if (1 == fatal) {
+ LOG(LOG_FATAL, "Fatal Error in DCE connection [%s]", strerror(e));
+ exit(-1); // Is there a more graceful way to shut down?
}
}
@@ -259,7 +387,10 @@
mdm_set_control_lines(cfg);
}
else {
- mdm_disconnect(cfg);
+ // mdm_disconnect(cfg);
+ cfg->cmd_mode = FALSE;
+ mdm_clear_break(cfg);
+ LOG(LOG_INFO, "Returning to call");
}
return 0;
}
@@ -279,6 +410,7 @@
}
mdm_send_response(get_connect_response(speed, cfg->response_code_level), cfg);
+ snprintf(cfg->last_error, 81, "%s", mdm_responses[get_connect_response(speed, cfg->response_code_level)]); // Store last response in the error field
return 0;
}
@@ -434,6 +566,8 @@
cmd = AT_CMD_ERR;
break;
case 'I': // Information.
+ mdm_send_info(cfg);
+ done = TRUE;
break;
case 'L': // Speaker volume
if (num < 1 || num > 3)
@@ -736,3 +870,23 @@
}
return 0;
}
+
+int mdm_send_info(modem_config *cfg)
+{
+ char str[256], *ptr;
+ if (0 != cfg->last_error[0]) {
+ snprintf(str, 256, "[%s] %s", cfg->dialno, cfg->last_error);
+ } else {
+ snprintf(str, 256, "no error in memory");
+ }
+
+ // Convert the entire message to lowercase, otherwise it may read funny in C/G mode.
+ for (ptr = str; *ptr; ++ptr) {
+ *ptr = toupper(*ptr);
+ }
+
+ mdm_write(cfg, (unsigned char *) cfg->crlf, 2);
+ mdm_write(cfg, (unsigned char*) str, strlen(str));
+ mdm_write(cfg, (unsigned char *) cfg->crlf, 2);
+ return 0;
+}
diff -ur tcpser-master/src/modem_core.h tcpser-master-mod/src/modem_core.h
--- tcpser-master/src/modem_core.h 2021-04-25 07:47:46.000000000 -0600
+++ tcpser-master-mod/src/modem_core.h 2022-04-19 12:57:02.394212135 -0600
@@ -46,6 +46,7 @@
#endif
#include "nvt.h"
+#include <ctype.h> // For tolower()
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
@@ -148,6 +149,7 @@
char crlf[3];
int parity;
char pchars[3];
+ char last_error[81]; // For reporting the reason of failed connection attempts.
} modem_config;
int mdm_init(void);
@@ -170,6 +172,7 @@
int mdm_parse_data(modem_config *cfg, char *data, int len);
int mdm_handle_timeout(modem_config *cfg);
int mdm_send_ring(modem_config *cfg);
+int mdm_send_info(modem_config *cfg);
#include "line.h"
#include "shared.h"
diff -ur tcpser-master/src/tcpser.c tcpser-master-mod/src/tcpser.c
--- tcpser-master/src/tcpser.c 2021-04-25 07:47:46.000000000 -0600
+++ tcpser-master-mod/src/tcpser.c 2022-04-18 18:25:06.058113552 -0600
@@ -46,6 +46,8 @@
log_set_level(LOG_FATAL);
+ LOG(LOG_FATAL, "Starting tcpser with mods by Cube Inc");
+
mdm_init();
pb_init();
Cube Inc.