AmendHub

jcs

/

subtext

/

amendments

/

88

serial: Start on serial modem support


jcs made amendment 88 10 months 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