jcs
/subtext
/amendments
/88
serial: Start on serial modem support
jcs made amendment 88 over 2 years ago
--- db.c Tue Mar 1 13:32:17 2022
+++ db.c Thu Mar 17 15:34:14 2022
@@ -192,6 +192,8 @@ db_migrate(struct db *tdb, short is_new)
sprintf(tdb->config.location, "Springfield");
sprintf(tdb->config.hostname, "bbs.example.com");
tdb->config.telnet_port = 23;
+ tdb->config.modem_port = 0;
+ sprintf(tdb->config.modem_init, "ATQ0V1S0=2");
db_config_save(tdb);
/* create a default sysop user */
--- db.h Tue Mar 1 13:20:42 2022
+++ db.h Sat Mar 5 21:52:32 2022
@@ -55,6 +55,8 @@ struct config {
char location[64];
char hostname[32];
short telnet_port;
+ short modem_port;
+ char modem_init[96];
};
struct user {
--- main.c Mon Feb 14 15:12:40 2022
+++ main.c Thu Mar 17 15:34:59 2022
@@ -20,6 +20,7 @@
#include "subtext.h"
#include "console.h"
#include "db.h"
+#include "serial_local.h"
#include "session.h"
#include "settings.h"
#include "telnet.h"
@@ -92,10 +93,14 @@ main(void)
if (db->config.telnet_port)
telnet_init();
-
+ if (db->config.modem_port)
+ serial_init();
+
while (!quitting) {
if (db->config.telnet_port)
telnet_idle();
+ if (db->config.modem_port)
+ serial_idle();
uthread_coordinate();
@@ -213,6 +218,7 @@ handle_exit(void)
}
telnet_atexit();
+ serial_atexit();
db_close(db);
}
--- serial.c Thu Mar 17 15:29:37 2022
+++ serial.c Thu Mar 17 15:29:37 2022
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2021-2022 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * On the Macintosh Plus, the RS422 serial ports don't have the GPIn pin
+ * connected like later models do to the Carrier Detect pin on the DCE.
+ * This means we can't properly detect when the call drops, other than
+ * just looking for "NO CARRIER" in the input stream.
+ *
+ * We do have the ability to assert and de-assert DTR so we can forcefully
+ * hangup a call from our end and then reset the modem to put it into a
+ * known good state in preparation for a new call.
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <Serial.h>
+#include <Devices.h>
+
+#include "serial_local.h"
+#include "session.h"
+#include "subtext.h"
+#include "util.h"
+
+static bool serial_initialized = false;
+static short serial_out_refnum = 0, serial_in_refnum = 0;
+static char serial_input_buf[512];
+static char serial_input_line_buf[128];
+static size_t serial_input_line_buf_len = 0;
+
+struct serial_node {
+ short id;
+ short state;
+ unsigned short obuflen;
+ unsigned char obuf[512];
+ unsigned char ibuf[64];
+ unsigned short ibuflen;
+ struct session *session;
+};
+
+void serial_reset(void);
+void serial_printf(const char *format, ...);
+char * serial_get_line(void);
+short serial_input(struct session *session);
+short serial_output(struct session *session);
+void serial_close(struct session *session);
+
+struct node_funcs serial_node_funcs = {
+ NULL,
+ serial_input,
+ serial_output,
+ serial_close
+};
+
+void
+serial_init(void)
+{
+ Str32 in_port = "\p.AIn";
+ Str32 out_port = "\p.AOut";
+ SerShk flags = { 0 };
+ short error;
+
+ if (db->config.modem_port == 0)
+ return;
+
+ if (db->config.modem_port == 2) {
+ /* printer */
+ in_port[2] = 'B';
+ out_port[2] = 'B';
+ }
+
+ if ((error = OpenDriver(out_port, &serial_out_refnum)) != 0)
+ panic("OpenDriver(%s) failed: %d", PtoCstr(out_port), error);
+ if ((error = OpenDriver(in_port, &serial_in_refnum)) != 0)
+ panic("OpenDriver(%s) failed: %d", PtoCstr(in_port), error);
+
+ /* 8N1 */
+ if ((error = SerReset(serial_out_refnum,
+ baud19200 + data8 + noParity + stop10)) != 0)
+ panic("SerReset(out) failed: %d", error);
+ if ((error = SerReset(serial_in_refnum,
+ baud19200 + data8 + noParity + stop10)) != 0)
+ panic("SerReset(in) failed: %d", error);
+
+ SerSetBuf(serial_in_refnum, (Ptr)&serial_input_buf,
+ sizeof(serial_input_buf));
+
+ flags.fXOn = true;
+ flags.fInX = true;
+ flags.xOn = 0x11; /* ^Q */
+ flags.xOff = 0x13; /* ^S */
+ flags.fDTR = 1;
+ /* use Control instead of SerHShake to control fDTR */
+ if ((error = Control(serial_out_refnum, 14, &flags)) != 0)
+ panic("Control failed: %d", error);
+
+ serial_reset();
+}
+
+void
+serial_reset(void)
+{
+ char *resp;
+ long m;
+
+ /* reset */
+ serial_hangup();
+ serial_printf("ATZ\r\n");
+ Delay(60L, &m);
+ while (serial_get_line() != NULL)
+ ;
+
+ /* disable echo */
+ serial_printf("ATE0\r\n");
+ while (serial_get_line() != NULL)
+ ;
+
+ /* initialize */
+ serial_printf("%s\r\n", db->config.modem_init);
+ resp = serial_get_line();
+ if (resp[0] != 'O' || resp[1] != 'K')
+ warn("bad response to modem init: \"%s\"", resp);
+}
+
+void
+serial_hangup(void)
+{
+ /* de-assert DTR */
+ Control(serial_out_refnum, 18, NULL);
+}
+
+void
+serial_printf(const char *format, ...)
+{
+ static char serial_printf_tbuf[256];
+ ParamBlockRec pbr = { 0 };
+ va_list ap;
+ size_t len, n;
+ long one;
+
+ va_start(ap, format);
+ len = vsnprintf(serial_printf_tbuf, sizeof(serial_printf_tbuf),
+ format, ap);
+ va_end(ap);
+
+ pbr.ioParam.ioRefNum = serial_out_refnum;
+ pbr.ioParam.ioBuffer = (Ptr)&serial_printf_tbuf;
+ pbr.ioParam.ioReqCount = len;
+ PBWrite(&pbr, false);
+#if 0
+ for (n = 0; n < len; n++) {
+ one = 1;
+ FSWrite(serial_out_refnum, &one, &serial_printf_tbuf[n]);
+ }
+#endif
+}
+
+char *
+serial_get_line(void)
+{
+ static char serial_input_cur_line_buf[sizeof(serial_input_line_buf)];
+ size_t n;
+ long len, rem;
+
+ /* append as much new data as we can fit */
+ if (serial_input_line_buf_len < sizeof(serial_input_line_buf)) {
+ SerGetBuf(serial_in_refnum, &len);
+ if (len) {
+ n = sizeof(serial_input_line_buf) - serial_input_line_buf_len;
+ if (len > n)
+ len = n;
+ FSRead(serial_in_refnum, &len,
+ &serial_input_line_buf + serial_input_line_buf_len);
+ serial_input_line_buf_len += len;
+ }
+ }
+
+find_line:
+ for (n = 0; n < serial_input_line_buf_len; n++) {
+ if (serial_input_line_buf[n] == '\r' ||
+ serial_input_line_buf[n] == '\n') {
+ if (n > 0)
+ memcpy(serial_input_cur_line_buf,
+ serial_input_line_buf, n);
+ serial_input_cur_line_buf[n] = '\0';
+
+ /* eat any trailing newlines */
+ while (n + 1 < serial_input_line_buf_len &&
+ (serial_input_line_buf[n + 1] == '\r' ||
+ serial_input_line_buf[n + 1] == '\n'))
+ n++;
+
+ /* shift remaining data down */
+ rem = serial_input_line_buf_len - n - 1;
+ if (rem > 0)
+ memmove(serial_input_line_buf,
+ serial_input_line_buf + n + 1, rem);
+ else if (rem < 0)
+ panic("bogus serial input remaining %ld", rem);
+ serial_input_line_buf_len = rem;
+
+ /* skip blank lines */
+ if (serial_input_cur_line_buf[0] == '\0')
+ goto find_line;
+
+ return (char *)&serial_input_cur_line_buf;
+ }
+ }
+
+ return NULL;
+}
+
+void
+serial_atexit(void)
+{
+ SerSetBuf(serial_in_refnum, (Ptr)&serial_input_buf, 0);
+ if (serial_in_refnum)
+ CloseDriver(serial_in_refnum);
+ if (serial_out_refnum)
+ CloseDriver(serial_out_refnum);
+}
+
+void
+serial_idle(void)
+{
+ SerStaRec status;
+ long count, i;
+ char inbuf[64];
+
+ SerStatus(serial_in_refnum, &status);
+ if (status.ctsHold != 0)
+ ;//warn("CTS set");
+ if (status.xOffHold != 0)
+ warn("xoff set");
+ if (status.cumErrs != 0)
+ warn("break!");
+
+ SerGetBuf(serial_in_refnum, &count);
+ if (count > 0) {
+ if (count > sizeof(inbuf))
+ count = sizeof(inbuf);
+ FSRead(serial_in_refnum, &count, &inbuf);
+ for (i = 0; i < count; i++) {
+ if (inbuf[i] == '\0' || inbuf[i] == '\r')
+ inbuf[i] = ' ';
+ }
+ inbuf[count] = '\0';
+ warn("read from serial: %s", inbuf);
+ }
+}
+
+short
+serial_input(struct session *session)
+{
+ return 0;
+}
+
+short
+serial_output(struct session *session)
+{
+ return 0;
+}
+
+void
+serial_close(struct session *session)
+{
+}
--- serial_local.h Sun Mar 6 16:40:03 2022
+++ serial_local.h Sun Mar 6 16:40:03 2022
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2022 joshua stein <jcs@jcs.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef __SERIAL_LOCAL_H__
+#define __SERIAL_LOCAL_H__
+
+void serial_init(void);
+void serial_idle(void);
+
+void serial_atexit(void);
+void serial_hangup(void);
+
+#endif
--- settings.c Mon Feb 21 11:34:41 2022
+++ settings.c Sat Mar 5 21:43:12 2022
@@ -54,8 +54,16 @@ struct config_field {
SETTINGS_HOSTNAME_ID },
{ "Telnet Port", CONFIG_TYPE_SHORT,
offsetof(struct config, telnet_port),
- 1, 65535,
- SETTINGS_TELNET_PORT_ID }
+ 0, 65535,
+ SETTINGS_TELNET_PORT_ID },
+ { "Modem Port", CONFIG_TYPE_SHORT,
+ offsetof(struct config, modem_port),
+ 0, 2,
+ SETTINGS_MODEM_PORT_ID },
+ { "Modem Init String", CONFIG_TYPE_STRING,
+ offsetof(struct config, modem_init),
+ 1, member_size(struct config, modem_init),
+ SETTINGS_MODEM_INIT_ID }
};
struct view_editor {
--- subtext.h Thu Feb 10 15:56:15 2022
+++ subtext.h Sat Mar 5 21:26:32 2022
@@ -55,6 +55,8 @@
#define SETTINGS_LOCATION_ID 5
#define SETTINGS_HOSTNAME_ID 6
#define SETTINGS_TELNET_PORT_ID 7
+#define SETTINGS_MODEM_PORT_ID 8
+#define SETTINGS_MODEM_INIT_ID 9
#define SETTINGS_EDIT_VIEW_DLOG_ID 131