diff --git a/src/86box.c b/src/86box.c index fb96ed87e..11d5c1ff8 100644 --- a/src/86box.c +++ b/src/86box.c @@ -19,11 +19,14 @@ * Copyright 2017-2020 Fred N. van Kempen. * Copyright 2021 Laci bá' * Copyright 2021 dob205 + * Copyright 2021 Andreas J. Reichel. + * Copyright 2021-2022 Jasmine Iwanek. */ #include #include #include #include +#include #include #include #include @@ -68,6 +71,7 @@ #include <86box/isartc.h> #include <86box/lpt.h> #include <86box/serial.h> +#include <86box/serial_passthrough.h> #include <86box/keyboard.h> #include <86box/mouse.h> #include <86box/gameport.h> @@ -162,6 +166,7 @@ int video_filter_method = 1; /* (C) video */ int video_vsync = 0; /* (C) video */ int video_framerate = -1; /* (C) video */ char video_shader[512] = { '\0' }; /* (C) video */ +bool serial_passthrough_enabled[SERIAL_MAX] = { 0, 0, 0, 0 }; /* (C) activation and kind of pass-through for serial ports */ int bugger_enabled = 0; /* (C) enable ISAbugger */ int postcard_enabled = 0; /* (C) enable POST card */ int isamem_type[ISAMEM_MAX] = { 0, 0, 0, 0 }; /* (C) enable ISA mem cards */ @@ -1038,6 +1043,7 @@ pc_reset_hard_init(void) /* Reset and reconfigure the serial ports. */ serial_standalone_init(); + serial_passthrough_init(); /* Reset and reconfigure the Sound Card layer. */ sound_card_reset(); diff --git a/src/config.c b/src/config.c index a0f9a71f0..056a00887 100644 --- a/src/config.c +++ b/src/config.c @@ -20,6 +20,8 @@ * Copyright 2016-2019 Miran Grca. * Copyright 2017-2019 Fred N. van Kempen. * Copyright 2018-2019 David Hrdlička. + * Copyright 2021 Andreas J. Reichel. + * Copyright 2021-2022 Jasmine Iwanek. * * NOTE: Forcing config files to be in Unicode encoding breaks * it on Windows XP, and possibly also Vista. Use the @@ -54,6 +56,8 @@ #include <86box/fdc.h> #include <86box/fdc_ext.h> #include <86box/gameport.h> +#include <86box/serial.h> +#include <86box/serial_passthrough.h> #include <86box/machine.h> #include <86box/mouse.h> #include <86box/thread.h> @@ -81,6 +85,7 @@ static ini_t config; static int backwards_compat = 0; static int backwards_compat2 = 0; +#define ENABLE_CONFIG_LOG 1 #ifdef ENABLE_CONFIG_LOG int config_do_log = ENABLE_CONFIG_LOG; @@ -834,6 +839,8 @@ load_ports(void) char temp[512]; int c, d; + memset(temp, 0, sizeof(temp)); + for (c = 0; c < SERIAL_MAX; c++) { sprintf(temp, "serial%d_enabled", c + 1); com_ports[c].enabled = !!ini_section_get_int(cat, temp, (c >= 2) ? 0 : 1); @@ -843,6 +850,12 @@ load_ports(void) p = (char *) ini_section_get_string(cat, temp, "none"); com_ports[c].device = com_device_get_from_internal_name(p); */ + + sprintf(temp, "serial%d_passthrough_enabled", c + 1); + serial_passthrough_enabled[c] = !!ini_section_get_int(cat, temp, 0); + + if (serial_passthrough_enabled[c]) + config_log("Serial Port %d: passthrough enabled.\n\n", c+1); } for (c = 0; c < PARALLEL_MAX; c++) { @@ -2458,6 +2471,12 @@ save_ports(void) ini_section_set_string(cat, temp, (char *) com_device_get_internal_name(com_ports[c].device)); */ + + if (com_ports[c].enabled) + if (serial_passthrough_enabled[c]) { + sprintf(temp, "serial%d_passthrough_enabled", c + 1); + ini_section_set_int(cat, temp, 1); + } } for (c = 0; c < PARALLEL_MAX; c++) { diff --git a/src/device.c b/src/device.c index 12d06580a..52d2bb18e 100644 --- a/src/device.c +++ b/src/device.c @@ -18,6 +18,8 @@ * Copyright 2017-2019 Fred N. van Kempen. * Copyright 2016-2019 Miran Grca. * Copyright 2008-2019 Sarah Walker. + * Copyright 2021 Andreas J. Reichel. + * Copyright 2021-2022 Jasmine Iwanek. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -91,6 +93,7 @@ device_set_context(device_context_t *c, const device_t *d, int inst) memset(c, 0, sizeof(device_context_t)); c->dev = d; + c->instance = inst; if (inst) { sprintf(c->name, "%s #%i", d->name, inst); @@ -556,6 +559,12 @@ device_force_redraw(void) } } +const int +device_get_instance(void) +{ + return device_current.instance; +} + const char * device_get_config_string(const char *s) { diff --git a/src/device/CMakeLists.txt b/src/device/CMakeLists.txt index ed8464647..41b9930c6 100644 --- a/src/device/CMakeLists.txt +++ b/src/device/CMakeLists.txt @@ -11,13 +11,16 @@ # Authors: David Hrdlička, # # Copyright 2020-2021 David Hrdlička. +# Copyright 2021 Andreas J. Reichel. +# Copyright 2021-2022 Jasmine Iwanek. # add_library(dev OBJECT bugger.c cassette.c cartridge.c hasp.c hwm.c hwm_lm75.c hwm_lm78.c hwm_gl518sm.c hwm_vt82c686.c ibm_5161.c isamem.c isartc.c ../lpt.c pci_bridge.c postcard.c serial.c clock_ics9xxx.c isapnp.c i2c.c i2c_gpio.c smbus_piix4.c smbus_ali7101.c keyboard.c keyboard_xt.c keyboard_at.c - mouse.c mouse_bus.c mouse_serial.c mouse_ps2.c phoenix_486_jumper.c) + mouse.c mouse_bus.c mouse_serial.c mouse_ps2.c phoenix_486_jumper.c + serial_passthrough.c) if(ISAMEM_RAMPAGE) target_compile_definitions(dev PRIVATE USE_ISAMEM_RAMPAGE) diff --git a/src/device/serial.c b/src/device/serial.c index dad2ed7f5..7d90a0fe8 100644 --- a/src/device/serial.c +++ b/src/device/serial.c @@ -93,6 +93,8 @@ serial_transmit_period(serial_t *dev) /* Bit period based on DLAB. */ dev->transmit_period = (16000000.0 * ddlab) / dev->clock_src; + if (dev->sd && dev->sd->transmit_period_callback) + dev->sd->transmit_period_callback(dev, dev->sd->priv, dev->transmit_period); } void @@ -161,7 +163,7 @@ write_fifo(serial_t *dev, uint8_t dat) dev->lsr |= 0x01; dev->int_status |= SERIAL_INT_RECEIVE; } - if (dev->rcvr_fifo_pos < 15) + if (dev->rcvr_fifo_pos < (dev->rcvr_fifo_len - 1)) dev->rcvr_fifo_pos++; else dev->rcvr_fifo_full = 1; @@ -175,6 +177,8 @@ write_fifo(serial_t *dev, uint8_t dat) dev->dat = dat; dev->lsr |= 0x01; dev->int_status |= SERIAL_INT_RECEIVE; + if (dev->lsr & 0x02) + dev->int_status |= SERIAL_INT_LSR; serial_update_ints(dev); } } @@ -311,6 +315,22 @@ serial_timeout_timer(void *priv) serial_update_ints(dev); } +void +serial_device_timeout(void *priv) +{ + serial_t *dev = (serial_t *) priv; + +#ifdef ENABLE_SERIAL_LOG + serial_log("serial_device_timeout()\n"); +#endif + + if (!dev->fifo_enabled) { + dev->lsr |= 0x10; + dev->int_status |= SERIAL_INT_LSR; + serial_update_ints(dev); + } +} + static void serial_update_speed(serial_t *dev) { @@ -331,6 +351,63 @@ serial_reset_fifo(serial_t *dev) dev->rcvr_fifo_full = 0; } +void +serial_set_dsr(serial_t *dev, uint8_t enabled) +{ + if (dev->mctrl & 0x10) + return; + + dev->msr &= ~0x2; + dev->msr |= !!((dev->msr & 0x20) ^ (enabled << 5)) << 1; + dev->msr &= ~0x20; + dev->msr |= (!!enabled) << 5; + dev->msr_set &= ~0x20; + dev->msr_set |= (!!enabled) << 5; + + if (dev->msr & 0x2) { + dev->int_status |= SERIAL_INT_MSR; + serial_update_ints(dev); + } +} + +void +serial_set_cts(serial_t *dev, uint8_t enabled) +{ + if (dev->mctrl & 0x10) + return; + + dev->msr &= ~0x1; + dev->msr |= !!((dev->msr & 0x10) ^ (enabled << 4)); + dev->msr &= ~0x10; + dev->msr |= (!!enabled) << 4; + dev->msr_set &= ~0x10; + dev->msr_set |= (!!enabled) << 4; + + if (dev->msr & 0x1) { + dev->int_status |= SERIAL_INT_MSR; + serial_update_ints(dev); + } +} + +void +serial_set_dcd(serial_t *dev, uint8_t enabled) +{ + if (dev->mctrl & 0x10) + return; + + dev->msr &= ~0x8; + dev->msr |= !!((dev->msr & 0x80) ^ (enabled << 7)); + dev->msr &= ~0x80; + dev->msr |= (!!enabled) << 7; + dev->msr_set &= ~0x80; + dev->msr_set |= (!!enabled) << 7; + + if (dev->msr & 0x8) { + dev->int_status |= SERIAL_INT_MSR; + serial_update_ints(dev); + } +} + void serial_set_clock_src(serial_t *dev, double clock_src) { @@ -431,7 +508,7 @@ serial_write(uint16_t addr, uint8_t val, void *p) case 3: old = dev->lcr; dev->lcr = val; - if ((old ^ val) & 0x0f) { + if ((old ^ val) & 0x3f) { /* Data bits + start bit. */ dev->bits = ((dev->lcr & 0x03) + 5) + 1; /* Stop bits. */ @@ -444,11 +521,14 @@ serial_write(uint16_t addr, uint8_t val, void *p) serial_transmit_period(dev); serial_update_speed(dev); + + if (dev->sd && dev->sd->lcr_callback) + dev->sd->lcr_callback(dev, dev->sd->priv, dev->lcr); } break; case 4: if ((val & 2) && !(dev->mctrl & 2)) { - if (dev->sd->rcr_callback) + if (dev->sd && dev->sd->rcr_callback) dev->sd->rcr_callback(dev, dev->sd->priv); } if (!(val & 8) && (dev->mctrl & 8)) @@ -487,7 +567,7 @@ serial_write(uint16_t addr, uint8_t val, void *p) serial_update_ints(dev); break; case 6: - dev->msr = val; + dev->msr = (val & 0xF0) | (dev->msr & 0x0F); if (dev->msr & 0x0f) dev->int_status |= SERIAL_INT_MSR; serial_update_ints(dev); @@ -521,11 +601,15 @@ serial_read(uint16_t addr, void *p) ret = dev->rcvr_fifo[0]; dev->rcvr_fifo_full = 0; + + for (i = 1; i < 16; i++) + dev->rcvr_fifo[i - 1] = dev->rcvr_fifo[i]; + + dev->rcvr_fifo_pos--; + if (dev->rcvr_fifo_pos > 0) { - for (i = 1; i < 16; i++) - dev->rcvr_fifo[i - 1] = dev->rcvr_fifo[i]; serial_log("FIFO position %i: read %02X, next %02X\n", dev->rcvr_fifo_pos, ret, dev->rcvr_fifo[0]); - dev->rcvr_fifo_pos--; + /* At least one byte remains to be read, start the timeout timer so that a timeout is indicated in case of no read. */ timer_on_auto(&dev->timeout_timer, 4.0 * dev->bits * dev->transmit_period); @@ -571,7 +655,7 @@ serial_read(uint16_t addr, void *p) serial_update_ints(dev); break; case 6: - ret = dev->msr; + ret = dev->msr | dev->msr_set; dev->msr &= ~0x0f; dev->int_status &= ~SERIAL_INT_MSR; serial_update_ints(dev); @@ -630,9 +714,30 @@ serial_attach(int port, { serial_device_t *sd = &serial_devices[port]; - sd->rcr_callback = rcr_callback; - sd->dev_write = dev_write; - sd->priv = priv; + sd->rcr_callback = rcr_callback; + sd->dev_write = dev_write; + sd->transmit_period_callback = NULL; + sd->lcr_callback = NULL; + sd->priv = priv; + + return sd->serial; +} + +serial_t * +serial_attach_ex(int port, + void (*rcr_callback)(struct serial_s *serial, void *p), + void (*dev_write)(struct serial_s *serial, void *p, uint8_t data), + void (*transmit_period_callback)(struct serial_s *serial, void *p, double transmit_period), + void (*lcr_callback)(struct serial_s *serial, void *p, uint8_t data_bits), + void *priv) +{ + serial_device_t *sd = &serial_devices[port]; + + sd->rcr_callback = rcr_callback; + sd->dev_write = dev_write; + sd->transmit_period_callback = transmit_period_callback; + sd->lcr_callback = lcr_callback; + sd->priv = priv; return sd->serial; } diff --git a/src/device/serial_passthrough.c b/src/device/serial_passthrough.c new file mode 100644 index 000000000..6025f264a --- /dev/null +++ b/src/device/serial_passthrough.c @@ -0,0 +1,357 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Implementation of Serial passthrough device. + * + * + * Authors: Andreas J. Reichel , + * Jasmine Iwanek + * + * Copyright 2021 Andreas J. Reichel. + * Copyright 2021-2022 Jasmine Iwanek. + */ + +#include +#include +#include +#include +#include +#include +#define HAVE_STDARG_H +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/timer.h> +#include <86box/serial.h> +#include <86box/serial_passthrough.h> +#include <86box/plat_serial_passthrough.h> + +#define ENABLE_SERIAL_PASSTHROUGH_LOG 1 +#ifdef ENABLE_SERIAL_PASSTHROUGH_LOG +int serial_passthrough_do_log = ENABLE_SERIAL_PASSTHROUGH_LOG; + +static void +serial_passthrough_log(const char *fmt, ...) +{ + va_list ap; + + if (serial_passthrough_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +# define serial_passthrough_log(fmt, ...) +#endif + +void +serial_passthrough_init(void) +{ + int c; + + for (c = 0; c < SERIAL_MAX; c++) { + if (serial_passthrough_enabled[c]) { + /* Instance n for COM n */ + device_add_inst(&serial_passthrough_device, c + 1); + } + } +} + +static void +serial_passthrough_timers_off(serial_passthrough_t *dev) +{ + timer_stop(&dev->host_to_serial_timer); +} + +static void +serial_passthrough_write(serial_t *s, void *priv, uint8_t val) +{ + plat_serpt_write(priv, val); +} + +static void +host_to_serial_cb(void *priv) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) priv; + + uint8_t byte; + + /* write_fifo has no failure indication, but if we write to fast, the host + * can never fetch the bytes in time, so check if the fifo is full if in + * fifo mode or if lsr has bit 0 set if not in fifo mode */ + if ((dev->serial->type >= SERIAL_16550) && dev->serial->fifo_enabled) { + if (dev->serial->rcvr_fifo_full) { + goto no_write_to_machine; + } + } else { + if (dev->serial->lsr & 1) { + goto no_write_to_machine; + } + } + if (plat_serpt_read(dev, &byte)) { + // printf("got byte %02X\n", byte); + serial_write_fifo(dev->serial, byte); + // serial_set_dsr(dev->serial, 1); + } +no_write_to_machine: + // serial_device_timeout(dev->serial); + timer_on_auto(&dev->host_to_serial_timer, (1000000.0 / dev->baudrate) * (double) dev->bits); +} + +static void +serial_passthrough_rcr_cb(struct serial_s *serial, void *priv) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) priv; + + timer_stop(&dev->host_to_serial_timer); + /* FIXME: do something to dev->baudrate */ + timer_on_auto(&dev->host_to_serial_timer, (1000000.0 / dev->baudrate) * (double) dev->bits); + // serial_clear_fifo(dev->serial); +} + +static void +serial_passthrough_speed_changed(void *priv) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) priv; + + timer_stop(&dev->host_to_serial_timer); + /* FIXME: do something to dev->baudrate */ + timer_on_auto(&dev->host_to_serial_timer, (1000000.0 / dev->baudrate) * (double) dev->bits); + // serial_clear_fifo(dev->serial); +} + +static void +serial_passthrough_dev_close(void *priv) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) priv; + + /* Detach passthrough device from COM port */ + if (dev && dev->serial && dev->serial->sd) + memset(dev->serial->sd, 0, sizeof(serial_device_t)); + + plat_serpt_close(dev); + free(dev); +} + +void +serial_passthrough_transmit_period(serial_t *serial, void *p, double transmit_period) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) p; + + if (dev->mode != SERPT_MODE_HOSTSER) + return; + dev->baudrate = 1000000.0 / (transmit_period); + + serial_passthrough_speed_changed(p); + plat_serpt_set_params(dev); +} + +void +serial_passthrough_lcr_callback(serial_t *serial, void *p, uint8_t lcr) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) p; + + if (dev->mode != SERPT_MODE_HOSTSER) + return; + dev->bits = serial->bits; + dev->data_bits = ((lcr & 0x03) + 5); + serial_passthrough_speed_changed(p); + plat_serpt_set_params(dev); +} + +/* Initialize the device for use by the user. */ +static void * +serial_passthrough_dev_init(const device_t *info) +{ + serial_passthrough_t *dev; + + dev = (serial_passthrough_t *) malloc(sizeof(serial_passthrough_t)); + memset(dev, 0, sizeof(serial_passthrough_t)); + dev->mode = device_get_config_int("mode"); + + dev->port = device_get_instance() - 1; + dev->baudrate = device_get_config_int("baudrate"); + dev->data_bits = device_get_config_int("data_bits"); + + /* Attach passthrough device to a COM port */ + dev->serial = serial_attach_ex(dev->port, serial_passthrough_rcr_cb, + serial_passthrough_write, serial_passthrough_transmit_period, serial_passthrough_lcr_callback, dev); + + strncpy(dev->host_serial_path, device_get_config_string("host_serial_path"), 1024); + + serial_passthrough_log("%s: port=COM%d\n", info->name, dev->port + 1); + serial_passthrough_log("%s: baud=%f\n", info->name, dev->baudrate); + serial_passthrough_log("%s: mode=%s\n", info->name, serpt_mode_names[dev->mode]); + + if (plat_serpt_open_device(dev)) { + serial_passthrough_log("%s: not running\n", info->name); + return NULL; + } + serial_passthrough_log("%s: running\n", info->name); + + memset(&dev->host_to_serial_timer, 0, sizeof(pc_timer_t)); + timer_add(&dev->host_to_serial_timer, host_to_serial_cb, dev, 1); + serial_set_cts(dev->serial, 1); + serial_set_dsr(dev->serial, 1); + serial_set_dcd(dev->serial, 1); + + /* 1 start bit + data bits + stop bits (no parity assumed) */ + dev->bits = 1 + device_get_config_int("data_bits") + device_get_config_int("stop_bits"); + + /* Return our private data to the I/O layer. */ + return dev; +} + +const char *serpt_mode_names[SERPT_MODES_MAX] = { + [SERPT_MODE_VCON] = "vcon", + [SERPT_MODE_TCPSRV] = "tcpsrv", + [SERPT_MODE_TCPCLNT] = "tcpclnt", + [SERPT_MODE_HOSTSER] = "hostser", +}; + +// clang-format off +static const device_config_t serial_passthrough_config[] = { + { + .name = "mode", + .description = "Passthrough Mode", + .type = CONFIG_SELECTION, + .default_string = "", + .default_int = 0, + .file_filter = "", + .spinner = { 0 }, + .selection = { +#ifdef _WIN32 + { + .description = "Named Pipe (Server)", + .value = SERPT_MODE_VCON + }, +#if 0 /* TODO */ + { + .description = "Named Pipe (Client)", + .value = SERPT_MODE_VCON + }, +#endif +#else + { + .description = "Pseudo Terminal/Virtual Console", + .value = SERPT_MODE_VCON + }, +#endif +#if 0 /* TODO */ + { + .description = "TCP Server", + .value = SERPT_MODE_TCPSRV + }, + { + .description = "TCP Client", + .value = SERPT_MODE_TCPCLNT + }, +#endif + { + .description = "Host Serial Passthrough", + .value = SERPT_MODE_HOSTSER + }, + { + .description = "" + } + } + }, + { + .name = "host_serial_path", + .description = "Host Serial Device", + .type = CONFIG_SERPORT, + .default_string = "", + .file_filter = NULL, + .spinner = {}, + .selection = {} + }, + { + .name = "data_bits", + .description = "Data bits", + .type = CONFIG_SELECTION, + .default_string = "8", + .default_int = 8, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { +#if 0 /* Mentioned by WFW 3.1x, not supported, atleast on Linux */ + { .description = "4", .value = 4 }, +#endif + { .description = "5", .value = 5 }, + { .description = "6", .value = 6 }, + { .description = "7", .value = 7 }, + { .description = "8", .value = 8 } + } + }, + { + .name = "stop_bits", + .description = "Stop bits", + .type = CONFIG_SELECTION, + .default_string = "1", + .default_int = 1, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { + { .description = "1", .value = 1 }, +#if 0 + { .description = "1.5", .value = 1.5 }, +#endif + { .description = "2", .value = 2 } + } + }, + { + .name = "baudrate", + .description = "Baud Rate of Passthrough", + .type = CONFIG_SELECTION, + .default_string = "", + .default_int = 115200, + .file_filter = NULL, + .spinner = { 0 }, + .selection = { +#if 0 + { .description = "256000", .value = 256000 }, + { .description = "128000", .value = 128000 }, +#endif + { .description = "115200", .value = 115200 }, + { .description = "57600", .value = 57600 }, + { .description = "56000", .value = 56000 }, + { .description = "38400", .value = 38400 }, + { .description = "19200", .value = 19200 }, + { .description = "14400", .value = 14400 }, + { .description = "9600", .value = 9600 }, + { .description = "7200", .value = 7200 }, + { .description = "4800", .value = 4800 }, + { .description = "2400", .value = 2400 }, + { .description = "1800", .value = 1800 }, + { .description = "1200", .value = 1200 }, + { .description = "600", .value = 600 }, + { .description = "300", .value = 300 }, + { .description = "150", .value = 150 }, +#if 0 + { .description = "134.5", .value = 134.5 }, +#endif + { .description = "110", .value = 110 }, + { .description = "75", .value = 75 } + } + }, + { .name = "", .description = "", .type = CONFIG_END } +}; +// clang-format on + +const device_t serial_passthrough_device = { + .name = "Serial Passthrough Device", + .flags = 0, + .local = 0, + .init = serial_passthrough_dev_init, + .close = serial_passthrough_dev_close, + .reset = NULL, + { .poll = NULL }, + .speed_changed = serial_passthrough_speed_changed, + .force_redraw = NULL, + .config = serial_passthrough_config +}; diff --git a/src/include/86box/device.h b/src/include/86box/device.h index 79cbf4d2a..877ef660a 100644 --- a/src/include/86box/device.h +++ b/src/include/86box/device.h @@ -17,6 +17,8 @@ * Copyright 2017-2019 Fred N. van Kempen. * Copyright 2016-2019 Miran Grca. * Copyright 2008-2019 Sarah Walker. + * Copyright 2021 Andreas J. Reichel. + * Copyright 2021-2022 Jasmine Iwanek. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,6 +54,7 @@ #define CONFIG_MAC 9 #define CONFIG_MIDI_IN 10 #define CONFIG_BIOS 11 +#define CONFIG_SERPORT 12 enum { DEVICE_PCJR = 2, /* requires an IBM PCjr */ @@ -107,7 +110,7 @@ typedef struct { int default_int; const char *file_filter; const device_config_spinner_t spinner; - const device_config_selection_t selection[16]; + const device_config_selection_t selection[32]; const device_config_bios_t bios[32]; } device_config_t; @@ -134,6 +137,7 @@ typedef struct _device_ { typedef struct { const device_t *dev; char name[2048]; + int instance; } device_context_t; #ifdef __cplusplus @@ -178,6 +182,7 @@ extern void device_set_config_hex16(const char *s, int val); extern void device_set_config_hex20(const char *s, int val); extern void device_set_config_mac(const char *s, int val); extern const char *device_get_config_string(const char *name); +extern const int device_get_instance(void); #define device_get_config_bios device_get_config_string extern char *device_get_internal_name(const device_t *d); diff --git a/src/include/86box/plat_serial_passthrough.h b/src/include/86box/plat_serial_passthrough.h new file mode 100644 index 000000000..e9df0584e --- /dev/null +++ b/src/include/86box/plat_serial_passthrough.h @@ -0,0 +1,38 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Definitions for platform specific serial to host passthrough. + * + * + * Authors: Andreas J. Reichel , + * Jasmine Iwanek + * + * Copyright 2021 Andreas J. Reichel. + * Copyright 2021-2022 Jasmine Iwanek. + */ + +#ifndef PLAT_SERIAL_PASSTHROUGH_H +#define PLAT_SERIAL_PASSTHROUGH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void plat_serpt_write(void *p, uint8_t data); +extern int plat_serpt_read(void *p, uint8_t *data); +extern int plat_serpt_open_device(void *p); +extern void plat_serpt_close(void *p); +extern void plat_serpt_set_params(void *p); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/include/86box/resource.h b/src/include/86box/resource.h index 967430216..397443299 100644 --- a/src/include/86box/resource.h +++ b/src/include/86box/resource.h @@ -247,6 +247,10 @@ #define IDC_CHECK_PARALLEL2 1089 #define IDC_CHECK_PARALLEL3 1090 #define IDC_CHECK_PARALLEL4 1091 +#define IDC_CHECK_SERIAL_PASS1 1092 +#define IDC_CHECK_SERIAL_PASS2 1093 +#define IDC_CHECK_SERIAL_PASS3 1094 +#define IDC_CHECK_SERIAL_PASS4 1095 #define IDC_OTHER_PERIPH 1110 /* storage controllers config */ #define IDC_COMBO_HDC 1111 @@ -364,6 +368,10 @@ #define IDC_CONFIGURE_NET4 1321 #define IDC_CONFIGURE_MIDI_OUT 1322 #define IDC_CONFIGURE_MIDI_IN 1323 +#define IDC_CONFIGURE_SERIAL_PASS1 1324 +#define IDC_CONFIGURE_SERIAL_PASS2 1325 +#define IDC_CONFIGURE_SERIAL_PASS3 1326 +#define IDC_CONFIGURE_SERIAL_PASS4 1327 #define IDC_JOY1 1330 #define IDC_JOY2 1331 #define IDC_JOY3 1332 diff --git a/src/include/86box/serial.h b/src/include/86box/serial.h index ae48ee09f..62b4c9a5f 100644 --- a/src/include/86box/serial.h +++ b/src/include/86box/serial.h @@ -53,7 +53,7 @@ typedef struct serial_s { dat, int_status, scratch, fcr, irq, type, inst, transmit_enabled, fifo_enabled, rcvr_fifo_len, bits, data_bits, - baud_cycles, rcvr_fifo_full, txsr, pad; + baud_cycles, rcvr_fifo_full, txsr, pad, msr_set; uint16_t dlab, base_address; @@ -70,6 +70,8 @@ typedef struct serial_s { typedef struct serial_device_s { void (*rcr_callback)(struct serial_s *serial, void *p); void (*dev_write)(struct serial_s *serial, void *p, uint8_t data); + void (*lcr_callback)(struct serial_s *serial, void *p, uint8_t lcr); + void (*transmit_period_callback)(struct serial_s *serial, void *p, double transmit_period); void *priv; serial_t *serial; } serial_device_t; @@ -84,6 +86,12 @@ extern serial_t *serial_attach(int port, void (*rcr_callback)(struct serial_s *serial, void *p), void (*dev_write)(struct serial_s *serial, void *p, uint8_t data), void *priv); +extern serial_t *serial_attach_ex(int port, + void (*rcr_callback)(struct serial_s *serial, void *p), + void (*dev_write)(struct serial_s *serial, void *p, uint8_t data), + void (*transmit_period_callback)(struct serial_s *serial, void *p, double transmit_period), + void (*lcr_callback)(struct serial_s *serial, void *p, uint8_t data_bits), + void *priv); extern void serial_remove(serial_t *dev); extern void serial_set_type(serial_t *dev, int type); extern void serial_setup(serial_t *dev, uint16_t addr, uint8_t irq); @@ -93,6 +101,11 @@ extern void serial_set_next_inst(int ni); extern void serial_standalone_init(void); extern void serial_set_clock_src(serial_t *dev, double clock_src); extern void serial_reset_port(serial_t *dev); +extern void serial_device_timeout(void *priv); + +extern void serial_set_cts(serial_t *dev, uint8_t enabled); +extern void serial_set_dsr(serial_t *dev, uint8_t enabled); +extern void serial_set_dcd(serial_t *dev, uint8_t enabled); extern const device_t ns8250_device; extern const device_t ns8250_pcjr_device; diff --git a/src/include/86box/serial_passthrough.h b/src/include/86box/serial_passthrough.h new file mode 100644 index 000000000..20c97fa03 --- /dev/null +++ b/src/include/86box/serial_passthrough.h @@ -0,0 +1,61 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Definition of Serial passthrough device. + * + * + * Authors: Andreas J. Reichel , + * Jasmine Iwanek + * + * Copyright 2021 Andreas J. Reichel. + * Copyright 2021-2022 Jasmine Iwanek. + */ + +#ifndef SERIAL_PASSTHROUGH_H +#define SERIAL_PASSTHROUGH_H + +#include +#include + +#include <86box/86box.h> +#include <86box/device.h> +#include <86box/timer.h> +#include <86box/serial.h> + +enum serial_passthrough_mode { + SERPT_MODE_VCON, /*Named Pipe (Server) / Pseudo Terminal/Virtual Console */ + SERPT_MODE_TCPSRV, /* TCP Server (TODO) */ + SERPT_MODE_TCPCLNT, /* TCP Client (TODO) */ + SERPT_MODE_HOSTSER, /* Host Serial Passthrough */ + SERPT_MODES_MAX, +}; + +extern const char *serpt_mode_names[SERPT_MODES_MAX]; + +typedef struct serial_passthrough_s { + enum serial_passthrough_mode mode; + pc_timer_t host_to_serial_timer; + pc_timer_t serial_to_host_timer; + serial_t *serial; + double baudrate; + uint8_t bits, data_bits; + uint8_t port; + uint8_t data; + char slave_pt[32]; /* used for pseudo term name of slave side */ + intptr_t master_fd; /* file desc for master pseudo terminal or + * socket or alike */ + char host_serial_path[1024]; /* Path to TTY/host serial port on the host */ + void *backend_priv; /* Private platform backend data */ +} serial_passthrough_t; + +extern bool serial_passthrough_enabled[SERIAL_MAX]; +extern const device_t serial_passthrough_device; + +extern void serial_passthrough_init(void); + +#endif diff --git a/src/include/86box/win_opengl.h b/src/include/86box/win_opengl.h index 80d3695ca..d354131ef 100644 --- a/src/include/86box/win_opengl.h +++ b/src/include/86box/win_opengl.h @@ -17,7 +17,7 @@ #define WIN_OPENGL_H #define UNICODE -#include +#include extern int opengl_init(HWND hwnd); extern int opengl_pause(void); diff --git a/src/include/discord_game_sdk.h b/src/include/discord_game_sdk.h index 62ea8418e..0d3da6dea 100644 --- a/src/include/discord_game_sdk.h +++ b/src/include/discord_game_sdk.h @@ -9,7 +9,7 @@ #define _DISCORD_GAME_SDK_H_ #ifdef _WIN32 -#include +#include #include #endif diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 7b3dfc929..1cb26a239 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -221,6 +221,12 @@ if(WIN32 AND NOT MINGW) target_sources(plat PRIVATE ../win/win_opendir.c) endif() +if(WIN32) + target_sources(plat PRIVATE ../win/win_serial_passthrough.c) +else() + target_sources(plat PRIVATE ../unix/unix_serial_passthrough.c) +endif() + if (APPLE) target_sources(ui PRIVATE macos_event_filter.mm) if(MOLTENVK) diff --git a/src/qt/qt_deviceconfig.cpp b/src/qt/qt_deviceconfig.cpp index 257238316..bf7d4bbb0 100644 --- a/src/qt/qt_deviceconfig.cpp +++ b/src/qt/qt_deviceconfig.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include extern "C" { #include <86box/86box.h> @@ -40,6 +42,10 @@ extern "C" { #include "qt_filefield.hpp" #include "qt_models_common.hpp" +#ifdef Q_OS_LINUX +# include +# include +#endif DeviceConfig::DeviceConfig(QWidget *parent) : QDialog(parent) @@ -53,6 +59,40 @@ DeviceConfig::~DeviceConfig() delete ui; } +static QStringList +EnumerateSerialDevices() +{ + QStringList serialDevices, ttyEntries; + QByteArray devstr(1024, 0); +#ifdef Q_OS_LINUX + QDir class_dir("/sys/class/tty/"); + QDir dev_dir("/dev/"); + ttyEntries = class_dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::System, QDir::SortFlag::Name); + for (int i = 0; i < ttyEntries.size(); i++) { + if (class_dir.exists(ttyEntries[i] + "/device/driver/") && dev_dir.exists(ttyEntries[i]) + && QFileInfo(dev_dir.canonicalPath() + '/' + ttyEntries[i]).isReadable() + && QFileInfo(dev_dir.canonicalPath() + '/' + ttyEntries[i]).isWritable()) { + serialDevices.push_back("/dev/" + ttyEntries[i]); + } + } +#endif +#ifdef Q_OS_WINDOWS + QSettings comPorts("HKEY_LOCAL_MACHINE\\HARDWARE\\DEVICEMAP\\SERIALCOMM", QSettings::NativeFormat, nullptr); + for (int i = 0; i < comPorts.childKeys().length(); i++) { + serialDevices.push_back(QString("\\\\.\\") + comPorts.value(comPorts.childKeys()[i]).toString()); + } +#endif +#ifdef Q_OS_MACOS + QDir dev_dir("/dev/"); + dev_dir.setNameFilters({ "tty.*", "cu.*" }); + QDir::Filters serial_dev_flags = QDir::Files | QDir::NoSymLinks | QDir::Readable | QDir::Writable | QDir::NoDotAndDotDot | QDir::System; + for (const auto &device : dev_dir.entryInfoList(serial_dev_flags, QDir::SortFlag::Name)) { + serialDevices.push_back(device.canonicalFilePath()); + } +#endif + return serialDevices; +} + void DeviceConfig::ConfigureDevice(const _device_ *device, int instance, Settings *settings) { @@ -205,6 +245,27 @@ DeviceConfig::ConfigureDevice(const _device_ *device, int instance, Settings *se dc.ui->formLayout->addRow(config->description, fileField); break; } + case CONFIG_SERPORT: + { + auto *cbox = new QComboBox(); + cbox->setObjectName(config->name); + auto *model = cbox->model(); + int currentIndex = 0; + auto serialDevices = EnumerateSerialDevices(); + char *selected = config_get_string(device_context.name, const_cast(config->name), const_cast(config->default_string)); + + Models::AddEntry(model, "None", -1); + for (int i = 0; i < serialDevices.size(); i++) { + int row = Models::AddEntry(model, serialDevices[i], i); + if (selected == serialDevices[i]) { + currentIndex = row; + } + } + + dc.ui->formLayout->addRow(config->description, cbox); + cbox->setCurrentIndex(currentIndex); + break; + } } ++config; } @@ -236,6 +297,15 @@ DeviceConfig::ConfigureDevice(const _device_ *device, int instance, Settings *se config_set_string(device_context.name, const_cast(config->name), const_cast(config->bios[idx].internal_name)); break; } + case CONFIG_SERPORT: + { + auto *cbox = dc.findChild(config->name); + auto path = cbox->currentText().toUtf8(); + if (path == "None") + path = ""; + config_set_string(device_context.name, const_cast(config->name), path); + break; + } case CONFIG_HEX16: { auto *cbox = dc.findChild(config->name); diff --git a/src/qt/qt_main.cpp b/src/qt/qt_main.cpp index 5bd2d40d3..a003bf59c 100644 --- a/src/qt/qt_main.cpp +++ b/src/qt/qt_main.cpp @@ -43,7 +43,7 @@ Q_IMPORT_PLUGIN(QWindowsVistaStylePlugin) # include "qt_winrawinputfilter.hpp" # include "qt_winmanagerfilter.hpp" # include <86box/win.h> -# include +# include #endif extern "C" { diff --git a/src/qt/qt_settingsports.cpp b/src/qt/qt_settingsports.cpp index a4be440e5..9b19df68d 100644 --- a/src/qt/qt_settingsports.cpp +++ b/src/qt/qt_settingsports.cpp @@ -27,6 +27,7 @@ extern "C" { #include <86box/machine.h> #include <86box/lpt.h> #include <86box/serial.h> +#include <86box/serial_passthrough.h> } #include "qt_deviceconfig.hpp" @@ -66,6 +67,15 @@ SettingsPorts::SettingsPorts(QWidget *parent) auto *checkBox = findChild(QString("checkBoxSerial%1").arg(i + 1)); checkBox->setChecked(com_ports[i].enabled > 0); } + + ui->checkBoxSerialPassThru1->setChecked(serial_passthrough_enabled[0]); + ui->pushButtonSerialPassThru1->setEnabled(serial_passthrough_enabled[0]); + ui->checkBoxSerialPassThru2->setChecked(serial_passthrough_enabled[1]); + ui->pushButtonSerialPassThru2->setEnabled(serial_passthrough_enabled[1]); + ui->checkBoxSerialPassThru3->setChecked(serial_passthrough_enabled[2]); + ui->pushButtonSerialPassThru3->setEnabled(serial_passthrough_enabled[2]); + ui->checkBoxSerialPassThru4->setChecked(serial_passthrough_enabled[3]); + ui->pushButtonSerialPassThru4->setEnabled(serial_passthrough_enabled[3]); } SettingsPorts::~SettingsPorts() @@ -87,6 +97,11 @@ SettingsPorts::save() auto *checkBox = findChild(QString("checkBoxSerial%1").arg(i + 1)); com_ports[i].enabled = checkBox->isChecked() ? 1 : 0; } + + serial_passthrough_enabled[0] = ui->checkBoxSerialPassThru1->isChecked(); + serial_passthrough_enabled[1] = ui->checkBoxSerialPassThru2->isChecked(); + serial_passthrough_enabled[2] = ui->checkBoxSerialPassThru3->isChecked(); + serial_passthrough_enabled[3] = ui->checkBoxSerialPassThru4->isChecked(); } void @@ -112,3 +127,51 @@ SettingsPorts::on_checkBoxParallel4_stateChanged(int state) { ui->comboBoxLpt4->setEnabled(state == Qt::Checked); } + +void +SettingsPorts::on_pushButtonSerialPassThru1_clicked() +{ + DeviceConfig::ConfigureDevice(&serial_passthrough_device, 1, qobject_cast(Settings::settings)); +} + +void +SettingsPorts::on_pushButtonSerialPassThru2_clicked() +{ + DeviceConfig::ConfigureDevice(&serial_passthrough_device, 2, qobject_cast(Settings::settings)); +} + +void +SettingsPorts::on_pushButtonSerialPassThru3_clicked() +{ + DeviceConfig::ConfigureDevice(&serial_passthrough_device, 3, qobject_cast(Settings::settings)); +} + +void +SettingsPorts::on_pushButtonSerialPassThru4_clicked() +{ + DeviceConfig::ConfigureDevice(&serial_passthrough_device, 4, qobject_cast(Settings::settings)); +} + +void +SettingsPorts::on_checkBoxSerialPassThru1_clicked(bool checked) +{ + ui->pushButtonSerialPassThru1->setEnabled(checked); +} + +void +SettingsPorts::on_checkBoxSerialPassThru2_clicked(bool checked) +{ + ui->pushButtonSerialPassThru2->setEnabled(checked); +} + +void +SettingsPorts::on_checkBoxSerialPassThru3_clicked(bool checked) +{ + ui->pushButtonSerialPassThru3->setEnabled(checked); +} + +void +SettingsPorts::on_checkBoxSerialPassThru4_clicked(bool checked) +{ + ui->pushButtonSerialPassThru4->setEnabled(checked); +} diff --git a/src/qt/qt_settingsports.hpp b/src/qt/qt_settingsports.hpp index 5fa88e210..a5129d35f 100644 --- a/src/qt/qt_settingsports.hpp +++ b/src/qt/qt_settingsports.hpp @@ -15,6 +15,30 @@ public: ~SettingsPorts(); void save(); +private slots: + void on_checkBoxSerialPassThru4_clicked(bool checked); + +private slots: + void on_checkBoxSerialPassThru3_clicked(bool checked); + +private slots: + void on_checkBoxSerialPassThru2_clicked(bool checked); + +private slots: + void on_pushButtonSerialPassThru4_clicked(); + +private slots: + void on_pushButtonSerialPassThru3_clicked(); + +private slots: + void on_pushButtonSerialPassThru2_clicked(); + +private slots: + void on_pushButtonSerialPassThru1_clicked(); + +private slots: + void on_checkBoxSerialPassThru1_clicked(bool checked); + private slots: void on_checkBoxParallel3_stateChanged(int arg1); void on_checkBoxParallel2_stateChanged(int arg1); diff --git a/src/qt/qt_settingsports.ui b/src/qt/qt_settingsports.ui index a1fbb47e7..7a338aff5 100644 --- a/src/qt/qt_settingsports.ui +++ b/src/qt/qt_settingsports.ui @@ -13,7 +13,7 @@ Form - + 0 @@ -26,7 +26,7 @@ 0 - + @@ -70,29 +70,8 @@ - + - - - - Serial port 1 - - - - - - - Parallel port 1 - - - - - - - Serial port 2 - - - @@ -100,13 +79,6 @@ - - - - Serial port 3 - - - @@ -114,10 +86,17 @@ - - + + - Serial port 4 + Serial port 3 + + + + + + + Serial port 1 @@ -128,20 +107,117 @@ + + + + Serial port 2 + + + + + + + Parallel port 1 + + + + + + + Serial port 4 + + + - - - - Qt::Vertical + + + + QLayout::SetDefaultConstraint - - - 20 - 40 - - - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Serial port passthrough 3 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Configure + + + + + + + Serial port passthrough 1 + + + + + + + Serial port passthrough 2 + + + + + + + Serial port passthrough 4 + + + + + + + Configure + + + + + + + Configure + + + + + + + Configure + + + + diff --git a/src/qt/qt_winmanagerfilter.cpp b/src/qt/qt_winmanagerfilter.cpp index 0218ae5ba..cdb81fcd5 100644 --- a/src/qt/qt_winmanagerfilter.cpp +++ b/src/qt/qt_winmanagerfilter.cpp @@ -17,7 +17,7 @@ #include "qt_winmanagerfilter.hpp" -#include +#include #include <86box/win.h> bool diff --git a/src/qt/qt_winrawinputfilter.cpp b/src/qt/qt_winrawinputfilter.cpp index 88b723d4c..6690a08eb 100644 --- a/src/qt/qt_winrawinputfilter.cpp +++ b/src/qt/qt_winrawinputfilter.cpp @@ -35,7 +35,7 @@ #include -#include +#include #include <86box/keyboard.h> #include <86box/mouse.h> diff --git a/src/qt/qt_winrawinputfilter.hpp b/src/qt/qt_winrawinputfilter.hpp index 6d23b83b7..b03f6783e 100644 --- a/src/qt/qt_winrawinputfilter.hpp +++ b/src/qt/qt_winrawinputfilter.hpp @@ -37,7 +37,7 @@ #include #include -#include +#include #include diff --git a/src/unix/CMakeLists.txt b/src/unix/CMakeLists.txt index 5fbcf0329..43c730315 100644 --- a/src/unix/CMakeLists.txt +++ b/src/unix/CMakeLists.txt @@ -13,9 +13,11 @@ # # Copyright 2021 Cacodemon345. # Copyright 2021 David Hrdlička. +# Copyright 2021 Andreas J. Reichel. +# Copyright 2021-2022 Jasmine Iwanek. # -add_library(plat OBJECT unix.c) +add_library(plat OBJECT unix.c unix_serial_passthrough.c) if (NOT CPPTHREADS) target_sources(plat PRIVATE unix_thread.c) diff --git a/src/unix/unix_serial_passthrough.c b/src/unix/unix_serial_passthrough.c new file mode 100644 index 000000000..29e4b8317 --- /dev/null +++ b/src/unix/unix_serial_passthrough.c @@ -0,0 +1,314 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Definitions for platform specific serial to host passthrough + * + * + * Authors: Andreas J. Reichel , + * Jasmine Iwanek + * + * Copyright 2021 Andreas J. Reichel. + * Copyright 2021-2022 Jasmine Iwanek. + */ + +#ifndef __APPLE__ +# define _XOPEN_SOURCE 500 +# define _DEFAULT_SOURCE 1 +# define _BSD_SOURCE 1 +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#include <86box/86box.h> +#include <86box/log.h> +#include <86box/plat.h> +#include <86box/device.h> +#include <86box/serial_passthrough.h> +#include <86box/plat_serial_passthrough.h> +#include +#include + +#define LOG_PREFIX "serial_passthrough: " + +int +plat_serpt_read(void *p, uint8_t *data) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) p; + int res; + struct timeval tv; + fd_set rdfds; + + switch (dev->mode) { + case SERPT_MODE_VCON: + case SERPT_MODE_HOSTSER: + FD_ZERO(&rdfds); + FD_SET(dev->master_fd, &rdfds); + tv.tv_sec = 0; + tv.tv_usec = 0; + + res = select(dev->master_fd + 1, &rdfds, NULL, NULL, &tv); + if (res <= 0 || !FD_ISSET(dev->master_fd, &rdfds)) { + return 0; + } + + if (read(dev->master_fd, data, 1) > 0) { + return 1; + } + break; + default: + break; + } + return 0; +} + +void +plat_serpt_close(void *p) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) p; + + if (dev->mode == SERPT_MODE_HOSTSER) { + tcsetattr(dev->master_fd, TCSANOW, (struct termios *) dev->backend_priv); + free(dev->backend_priv); + } + close(dev->master_fd); +} + +static void +plat_serpt_write_vcon(serial_passthrough_t *dev, uint8_t data) +{ + /* fd_set wrfds; + * int res; + */ + + /* We cannot use select here, this would block the hypervisor! */ + /* FD_ZERO(&wrfds); + FD_SET(ctx->master_fd, &wrfds); + + res = select(ctx->master_fd + 1, NULL, &wrfds, NULL, NULL); + + if (res <= 0) { + return; + } + */ + + /* just write it out */ + if (dev->mode == SERPT_MODE_HOSTSER) { + int res = 0; + do { + res = write(dev->master_fd, &data, 1); + } while (res == 0 || (res == -1 && (errno == EAGAIN || res == EWOULDBLOCK))); + } else + write(dev->master_fd, &data, 1); +} + +void +plat_serpt_set_params(void *p) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) p; + + if (dev->mode == SERPT_MODE_HOSTSER) { + struct termios term_attr; + tcgetattr(dev->master_fd, &term_attr); +#define BAUDRATE_RANGE(baud_rate, min, max, val) \ + if (baud_rate >= min && baud_rate < max) { \ + cfsetispeed(&term_attr, val); \ + cfsetospeed(&term_attr, val); \ + } + + BAUDRATE_RANGE(dev->baudrate, 50, 75, B50); + BAUDRATE_RANGE(dev->baudrate, 75, 110, B75); + BAUDRATE_RANGE(dev->baudrate, 110, 134, B110); + BAUDRATE_RANGE(dev->baudrate, 134, 150, B134); + BAUDRATE_RANGE(dev->baudrate, 150, 200, B150); + BAUDRATE_RANGE(dev->baudrate, 200, 300, B200); + BAUDRATE_RANGE(dev->baudrate, 300, 600, B300); + BAUDRATE_RANGE(dev->baudrate, 600, 1200, B600); + BAUDRATE_RANGE(dev->baudrate, 1200, 1800, B1200); + BAUDRATE_RANGE(dev->baudrate, 1800, 2400, B1800); + BAUDRATE_RANGE(dev->baudrate, 2400, 4800, B2400); + BAUDRATE_RANGE(dev->baudrate, 4800, 9600, B4800); + BAUDRATE_RANGE(dev->baudrate, 9600, 19200, B9600); + BAUDRATE_RANGE(dev->baudrate, 19200, 38400, B19200); + BAUDRATE_RANGE(dev->baudrate, 38400, 57600, B38400); + BAUDRATE_RANGE(dev->baudrate, 57600, 115200, B57600); + BAUDRATE_RANGE(dev->baudrate, 115200, 0xFFFFFFFF, B115200); + + term_attr.c_cflag &= CSIZE; + switch (dev->data_bits) { + case 8: + default: + term_attr.c_cflag |= CS8; + break; + case 7: + term_attr.c_cflag |= CS7; + break; + case 6: + term_attr.c_cflag |= CS6; + break; + case 5: + term_attr.c_cflag |= CS5; + break; + } + term_attr.c_cflag &= CSTOPB; + if (dev->serial->lcr & 0x04) + term_attr.c_cflag |= CSTOPB; +#ifdef __APPLE__ + term_attr.c_cflag &= PARENB | PARODD; +#else + term_attr.c_cflag &= PARENB | PARODD | CMSPAR; +#endif + if (dev->serial->lcr & 0x08) { + term_attr.c_cflag |= PARENB; + if (!(dev->serial->lcr & 0x10)) + term_attr.c_cflag |= PARODD; +#ifndef __APPLE__ + if ((dev->serial->lcr & 0x20)) + term_attr.c_cflag |= CMSPAR; +#endif + } + tcsetattr(dev->master_fd, TCSANOW, &term_attr); +#undef BAUDRATE_RANGE + } +} + +void +plat_serpt_write(void *p, uint8_t data) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) p; + + switch (dev->mode) { + case SERPT_MODE_VCON: + case SERPT_MODE_HOSTSER: + plat_serpt_write_vcon(dev, data); + break; + default: + break; + } +} + +static int +open_pseudo_terminal(serial_passthrough_t *dev) +{ + int master_fd = open("/dev/ptmx", O_RDWR | O_NONBLOCK); + char *ptname; + struct termios term_attr_raw; + + if (!master_fd) { + return 0; + } + + /* get name of slave device */ + if (!(ptname = ptsname(master_fd))) { + pclog(LOG_PREFIX "could not get name of slave pseudo terminal"); + close(master_fd); + return 0; + } + memset(dev->slave_pt, 0, sizeof(dev->slave_pt)); + strncpy(dev->slave_pt, ptname, sizeof(dev->slave_pt) - 1); + + fprintf(stderr, LOG_PREFIX "Slave side is %s\n", dev->slave_pt); + + if (grantpt(master_fd)) { + pclog(LOG_PREFIX "error in grantpt()\n"); + close(master_fd); + return 0; + } + + if (unlockpt(master_fd)) { + pclog(LOG_PREFIX "error in unlockpt()\n"); + close(master_fd); + return 0; + } + + tcgetattr(master_fd, &term_attr_raw); + cfmakeraw(&term_attr_raw); + tcsetattr(master_fd, TCSANOW, &term_attr_raw); + + dev->master_fd = master_fd; + + return master_fd; +} + +static int +open_host_serial_port(serial_passthrough_t *dev) +{ + struct termios *term_attr = NULL; + struct termios term_attr_raw = {}; + int fd = open(dev->host_serial_path, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd == -1) { + return 0; + } + + if (!isatty(fd)) { + return 0; + } + + term_attr = calloc(1, sizeof(struct termios)); + if (!term_attr) { + close(fd); + return 0; + } + + if (tcgetattr(fd, term_attr) == -1) { + free(term_attr); + close(fd); + return 0; + } + term_attr_raw = *term_attr; + /* "Raw" mode. */ + cfmakeraw(&term_attr_raw); + term_attr_raw.c_cflag &= CSIZE; + switch (dev->data_bits) { + case 8: + default: + term_attr_raw.c_cflag |= CS8; + break; + case 7: + term_attr_raw.c_cflag |= CS7; + break; + case 6: + term_attr_raw.c_cflag |= CS6; + break; + case 5: + term_attr_raw.c_cflag |= CS5; + break; + } + tcsetattr(fd, TCSANOW, &term_attr_raw); + dev->backend_priv = term_attr; + dev->master_fd = fd; + pclog(LOG_PREFIX "Opened host TTY/serial port %s\n", dev->host_serial_path); + return 1; +} + +int +plat_serpt_open_device(void *p) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) p; + + switch (dev->mode) { + case SERPT_MODE_VCON: + if (!open_pseudo_terminal(dev)) { + return 1; + } + break; + case SERPT_MODE_HOSTSER: + if (!open_host_serial_port(dev)) { + return 1; + } + break; + default: + break; + } + return 0; +} diff --git a/src/win/CMakeLists.txt b/src/win/CMakeLists.txt index a0a4c8e14..604ba9bb0 100644 --- a/src/win/CMakeLists.txt +++ b/src/win/CMakeLists.txt @@ -10,13 +10,14 @@ # # Authors: David Hrdlička, # -# Copyright 2020-2021 David Hrdlička. +# Copyright 2020,2021 David Hrdlička. +# Copyright 2021-2022 Jasmine Iwanek. # enable_language(RC) add_library(plat OBJECT win.c win_dynld.c win_cdrom.c win_keyboard.c - win_mouse.c) + win_mouse.c win_serial_passthrough.c) add_library(ui OBJECT win_ui.c win_icon.c win_stbar.c win_sdl.c win_dialog.c win_about.c win_settings.c win_devconf.c win_snd_gain.c win_specify_dim.c win_new_floppy.c diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 747bbda2d..9dd6af728 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -596,7 +596,7 @@ DEVOBJ := bugger.o cartridge.o cassette.o hasp.o hwm.o hwm_lm75.o hwm_lm78.o hwm mouse.o \ mouse_bus.o \ mouse_serial.o mouse_ps2.o \ - phoenix_486_jumper.o + phoenix_486_jumper.o serial_passthrough.o SIOOBJ := sio_acc3221.o sio_ali5123.o \ sio_f82c710.o sio_82091aa.o sio_fdc37c6xx.o \ @@ -749,7 +749,7 @@ VOODOOOBJ := vid_voodoo.o vid_voodoo_banshee.o \ PLATOBJ := win.o \ win_dynld.o \ win_cdrom.o win_keyboard.o \ - win_mouse.o + win_mouse.o win_serial_passthrough.o UIOBJ := win_ui.o win_icon.o win_stbar.o discord.o \ win_sdl.o win_opengl.o win_opengl_glslp.o glad.o \ diff --git a/src/win/languages/cs-CZ.rc b/src/win/languages/cs-CZ.rc index c888fe954..fe8a8a42b 100644 --- a/src/win/languages/cs-CZ.rc +++ b/src/win/languages/cs-CZ.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Povolit port LPT2" #define STR_PARALLEL3 "Povolit port LPT3" #define STR_PARALLEL4 "Povolit port LPT4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Řadič disku:" #define STR_FDC "Disketový řadič:" diff --git a/src/win/languages/de-DE.rc b/src/win/languages/de-DE.rc index 3768898a5..58d799539 100644 --- a/src/win/languages/de-DE.rc +++ b/src/win/languages/de-DE.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Parallelport 2" #define STR_PARALLEL3 "Parallelport 3" #define STR_PARALLEL4 "Parallelport 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "HDD-Controller:" #define STR_FDC "FD-Controller:" diff --git a/src/win/languages/dialogs.rc b/src/win/languages/dialogs.rc index 5a150324c..1facfb10d 100644 --- a/src/win/languages/dialogs.rc +++ b/src/win/languages/dialogs.rc @@ -453,54 +453,78 @@ BEGIN CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT STR_LPT2, IDT_LPT2, - CFG_HMARGIN, 28, CFG_PANE_LTEXT_PRI_WIDTH, CFG_PANE_LTEXT_HEIGHT + CFG_HMARGIN, 24, CFG_PANE_LTEXT_PRI_WIDTH, CFG_PANE_LTEXT_HEIGHT COMBOBOX IDC_COMBO_LPT2, - CFG_COMBO_BOX_LEFT, 26, CFG_COMBO_NOBTN_WIDTH, CFG_COMBO_HEIGHT, + CFG_COMBO_BOX_LEFT, 22, CFG_COMBO_NOBTN_WIDTH, CFG_COMBO_HEIGHT, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT STR_LPT3, IDT_LPT3, - CFG_HMARGIN, 47, CFG_PANE_LTEXT_PRI_WIDTH, CFG_PANE_LTEXT_HEIGHT + CFG_HMARGIN, 39, CFG_PANE_LTEXT_PRI_WIDTH, CFG_PANE_LTEXT_HEIGHT COMBOBOX IDC_COMBO_LPT3, - CFG_COMBO_BOX_LEFT, 45, CFG_COMBO_NOBTN_WIDTH, CFG_COMBO_HEIGHT, + CFG_COMBO_BOX_LEFT, 37, CFG_COMBO_NOBTN_WIDTH, CFG_COMBO_HEIGHT, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP LTEXT STR_LPT4, IDT_LPT4, - CFG_HMARGIN, 66, CFG_PANE_LTEXT_PRI_WIDTH, CFG_PANE_LTEXT_HEIGHT + CFG_HMARGIN, 54, CFG_PANE_LTEXT_PRI_WIDTH, CFG_PANE_LTEXT_HEIGHT COMBOBOX IDC_COMBO_LPT4, - CFG_COMBO_BOX_LEFT, 64, CFG_COMBO_NOBTN_WIDTH, CFG_COMBO_HEIGHT, + CFG_COMBO_BOX_LEFT, 52, CFG_COMBO_NOBTN_WIDTH, CFG_COMBO_HEIGHT, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP CONTROL STR_SERIAL1, IDC_CHECK_SERIAL1, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, - CFG_HMARGIN, 83, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + CFG_HMARGIN, 71, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT CONTROL STR_SERIAL2, IDC_CHECK_SERIAL2, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, - CFG_HMARGIN, 102, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + CFG_HMARGIN, 86, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT CONTROL STR_SERIAL3, IDC_CHECK_SERIAL3, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, - CFG_HMARGIN, 121, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + CFG_HMARGIN, 101, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT CONTROL STR_SERIAL4, IDC_CHECK_SERIAL4, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, - CFG_HMARGIN, 140, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + CFG_HMARGIN, 116, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT CONTROL STR_PARALLEL1, IDC_CHECK_PARALLEL1, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, - 167, 83, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + 167, 71, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT CONTROL STR_PARALLEL2, IDC_CHECK_PARALLEL2, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, - 167, 102, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + 167, 86, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT CONTROL STR_PARALLEL3, IDC_CHECK_PARALLEL3, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, - 167, 121, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + 167, 101, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT CONTROL STR_PARALLEL4, IDC_CHECK_PARALLEL4, "Button", BS_AUTOCHECKBOX | WS_TABSTOP, - 167, 140, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + 167, 116, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + + CONTROL STR_SERIAL_PASS1, IDC_CHECK_SERIAL_PASS1, + "Button", BS_AUTOCHECKBOX | WS_TABSTOP, + CFG_HMARGIN, 134, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + PUSHBUTTON STR_CONFIGURE, IDC_CONFIGURE_SERIAL_PASS1, + CFG_COMBO_BTN_LEFT, 131, CFG_BTN_WIDTH, CFG_BTN_HEIGHT + + CONTROL STR_SERIAL_PASS2, IDC_CHECK_SERIAL_PASS2, + "Button", BS_AUTOCHECKBOX | WS_TABSTOP, + CFG_HMARGIN, 150, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + PUSHBUTTON STR_CONFIGURE, IDC_CONFIGURE_SERIAL_PASS2, + CFG_COMBO_BTN_LEFT, 147, CFG_BTN_WIDTH, CFG_BTN_HEIGHT + + CONTROL STR_SERIAL_PASS3, IDC_CHECK_SERIAL_PASS3, + "Button", BS_AUTOCHECKBOX | WS_TABSTOP, + CFG_HMARGIN, 165, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + PUSHBUTTON STR_CONFIGURE, IDC_CONFIGURE_SERIAL_PASS3, + CFG_COMBO_BTN_LEFT, 162, CFG_BTN_WIDTH, CFG_BTN_HEIGHT + + CONTROL STR_SERIAL_PASS4, IDC_CHECK_SERIAL_PASS4, + "Button", BS_AUTOCHECKBOX | WS_TABSTOP, + CFG_HMARGIN, 180, CFG_CHECKBOX_PRI_WIDTH, CFG_CHECKBOX_HEIGHT + PUSHBUTTON STR_CONFIGURE, IDC_CONFIGURE_SERIAL_PASS4, + CFG_COMBO_BTN_LEFT, 177, CFG_BTN_WIDTH, CFG_BTN_HEIGHT END DLG_CFG_STORAGE DIALOG DISCARDABLE CFG_PANE_LEFT, CFG_PANE_TOP, CFG_PANE_WIDTH, CFG_PANE_HEIGHT @@ -996,6 +1020,10 @@ END #undef STR_PARALLEL2 #undef STR_PARALLEL3 #undef STR_PARALLEL4 +#undef STR_SERIAL_PASS1 +#undef STR_SERIAL_PASS2 +#undef STR_SERIAL_PASS3 +#undef STR_SERIAL_PASS4 #undef STR_HDC #undef STR_FDC diff --git a/src/win/languages/en-GB.rc b/src/win/languages/en-GB.rc index a16c7977f..005a8fc9c 100644 --- a/src/win/languages/en-GB.rc +++ b/src/win/languages/en-GB.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Parallel port 2" #define STR_PARALLEL3 "Parallel port 3" #define STR_PARALLEL4 "Parallel port 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "HD Controller:" #define STR_FDC "FD Controller:" diff --git a/src/win/languages/en-US.rc b/src/win/languages/en-US.rc index 42070879d..dcda2a1b4 100644 --- a/src/win/languages/en-US.rc +++ b/src/win/languages/en-US.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Parallel port 2" #define STR_PARALLEL3 "Parallel port 3" #define STR_PARALLEL4 "Parallel port 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "HD Controller:" #define STR_FDC "FD Controller:" diff --git a/src/win/languages/es-ES.rc b/src/win/languages/es-ES.rc index 8be2db827..112d5d89e 100644 --- a/src/win/languages/es-ES.rc +++ b/src/win/languages/es-ES.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Puerto paralelo 2" #define STR_PARALLEL3 "Puerto paralelo 3" #define STR_PARALLEL4 "Puerto paralelo 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Controladora HD:" #define STR_FDC "Controladora FD:" diff --git a/src/win/languages/fi-FI.rc b/src/win/languages/fi-FI.rc index 9f2f14187..5494bffc5 100644 --- a/src/win/languages/fi-FI.rc +++ b/src/win/languages/fi-FI.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Rinnakkaisportti 2" #define STR_PARALLEL3 "Rinnakkaisportti 3" #define STR_PARALLEL4 "Rinnakkaisportti 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Kiintolevyohjain:" #define STR_FDC "Levykeohjain:" diff --git a/src/win/languages/fr-FR.rc b/src/win/languages/fr-FR.rc index fcd4d480a..970b4db01 100644 --- a/src/win/languages/fr-FR.rc +++ b/src/win/languages/fr-FR.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Port parallèle 2" #define STR_PARALLEL3 "Port parallèle 3" #define STR_PARALLEL4 "Port parallèle 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Contrôleur HD:" #define STR_FDC "Contrôleur FD:" diff --git a/src/win/languages/hr-HR.rc b/src/win/languages/hr-HR.rc index fe0f9dad5..7afb7b26d 100644 --- a/src/win/languages/hr-HR.rc +++ b/src/win/languages/hr-HR.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Paralelna vrata 2" #define STR_PARALLEL3 "Paralelna vrata 3" #define STR_PARALLEL4 "Paralelna vrata 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Kontroler tvrdog diska:" #define STR_FDC "Kontroler diskete:" diff --git a/src/win/languages/hu-HU.rc b/src/win/languages/hu-HU.rc index f23e3c35a..81fc034c4 100644 --- a/src/win/languages/hu-HU.rc +++ b/src/win/languages/hu-HU.rc @@ -333,6 +333,10 @@ END #define STR_PARALLEL2 "Párhuzamos port 2" #define STR_PARALLEL3 "Párhuzamos port 3" #define STR_PARALLEL4 "Párhuzamos port 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Merevl.-vezérlő:" #define STR_FDC "Floppy-vezérlő:" diff --git a/src/win/languages/it-IT.rc b/src/win/languages/it-IT.rc index 6335c690f..fd0441232 100644 --- a/src/win/languages/it-IT.rc +++ b/src/win/languages/it-IT.rc @@ -329,6 +329,10 @@ END #define STR_PARALLEL2 "Porta parallela 2" #define STR_PARALLEL3 "Porta parallela 3" #define STR_PARALLEL4 "Porta parallela 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Controller HD:" #define STR_FDC "Controller FD:" diff --git a/src/win/languages/ja-JP.rc b/src/win/languages/ja-JP.rc index 1de5e1f42..c1e0ee463 100644 --- a/src/win/languages/ja-JP.rc +++ b/src/win/languages/ja-JP.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "パラレルポート2" #define STR_PARALLEL3 "パラレルポート3" #define STR_PARALLEL4 "パラレルポート4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "HDコントローラー:" #define STR_FDC "FDコントローラー:" diff --git a/src/win/languages/ko-KR.rc b/src/win/languages/ko-KR.rc index b96e21fa1..d43056478 100644 --- a/src/win/languages/ko-KR.rc +++ b/src/win/languages/ko-KR.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "병렬 포트 2" #define STR_PARALLEL3 "병렬 포트 3" #define STR_PARALLEL4 "병렬 포트 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "HD 컨트롤러:" #define STR_FDC "FD 컨트롤러:" diff --git a/src/win/languages/pl-PL.rc b/src/win/languages/pl-PL.rc index e4e6a4963..343ca929b 100644 --- a/src/win/languages/pl-PL.rc +++ b/src/win/languages/pl-PL.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Port równoległy 2" #define STR_PARALLEL3 "Port równoległy 3" #define STR_PARALLEL4 "Port równoległy 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Kontroler dysku twardego:" #define STR_FDC "Kontroler dyskietek:" diff --git a/src/win/languages/pt-BR.rc b/src/win/languages/pt-BR.rc index 7a08080bc..104c3648e 100644 --- a/src/win/languages/pt-BR.rc +++ b/src/win/languages/pt-BR.rc @@ -331,6 +331,10 @@ END #define STR_PARALLEL2 "Porta paralela 2" #define STR_PARALLEL3 "Porta paralela 3" #define STR_PARALLEL4 "Porta paralela 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Controlador HD:" #define STR_FDC "Controlador FD:" diff --git a/src/win/languages/pt-PT.rc b/src/win/languages/pt-PT.rc index 74968da02..31c68fbd0 100644 --- a/src/win/languages/pt-PT.rc +++ b/src/win/languages/pt-PT.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Porta paralela 2" #define STR_PARALLEL3 "Porta paralela 3" #define STR_PARALLEL4 "Porta paralela 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Controlador HD:" #define STR_FDC "Controlador FD:" diff --git a/src/win/languages/ru-RU.rc b/src/win/languages/ru-RU.rc index 790738148..8ae6b20b6 100644 --- a/src/win/languages/ru-RU.rc +++ b/src/win/languages/ru-RU.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Параллельный порт LPT2" #define STR_PARALLEL3 "Параллельный порт LPT3" #define STR_PARALLEL4 "Параллельный порт LPT4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Контроллер HD:" #define STR_FDC "Контроллер FD:" diff --git a/src/win/languages/sl-SI.rc b/src/win/languages/sl-SI.rc index 317cf51b8..f1badf570 100644 --- a/src/win/languages/sl-SI.rc +++ b/src/win/languages/sl-SI.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Paralelna vrata 2" #define STR_PARALLEL3 "Paralelna vrata 3" #define STR_PARALLEL4 "Paralelna vrata 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Krmilnik trdega diska:" #define STR_FDC "Krmilnik disketnika:" diff --git a/src/win/languages/tr-TR.rc b/src/win/languages/tr-TR.rc index 04a0f9abc..a168d2f30 100644 --- a/src/win/languages/tr-TR.rc +++ b/src/win/languages/tr-TR.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Paralel port 2" #define STR_PARALLEL3 "Paralel port 3" #define STR_PARALLEL4 "Paralel port 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "HD Kontrolcüsü:" #define STR_FDC "FD Kontrolcüsü:" diff --git a/src/win/languages/uk-UA.rc b/src/win/languages/uk-UA.rc index d8c5f1106..db2604860 100644 --- a/src/win/languages/uk-UA.rc +++ b/src/win/languages/uk-UA.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "Паралельний порт LPT2" #define STR_PARALLEL3 "Паралельний порт LPT3" #define STR_PARALLEL4 "Паралельний порт LPT4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "Контролер HD:" #define STR_FDC "Контролер FD:" diff --git a/src/win/languages/zh-CN.rc b/src/win/languages/zh-CN.rc index 72d67e880..f72177a81 100644 --- a/src/win/languages/zh-CN.rc +++ b/src/win/languages/zh-CN.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "并口 2" #define STR_PARALLEL3 "并口 3" #define STR_PARALLEL4 "并口 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "硬盘控制器:" #define STR_FDC "软盘控制器:" diff --git a/src/win/languages/zh-TW.rc b/src/win/languages/zh-TW.rc index 9cbb7c70e..6c2d87545 100644 --- a/src/win/languages/zh-TW.rc +++ b/src/win/languages/zh-TW.rc @@ -328,6 +328,10 @@ END #define STR_PARALLEL2 "並列埠 2" #define STR_PARALLEL3 "並列埠 3" #define STR_PARALLEL4 "並列埠 4" +#define STR_SERIAL_PASS1 "Serial port passthrough 1" +#define STR_SERIAL_PASS2 "Serial port passthrough 2" +#define STR_SERIAL_PASS3 "Serial port passthrough 3" +#define STR_SERIAL_PASS4 "Serial port passthrough 4" #define STR_HDC "硬碟控制器:" #define STR_FDC "軟碟控制器:" diff --git a/src/win/win_opengl.c b/src/win/win_opengl.c index 586632aaa..1de065855 100644 --- a/src/win/win_opengl.c +++ b/src/win/win_opengl.c @@ -36,7 +36,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define UNICODE -#include +#include #include #include #include diff --git a/src/win/win_serial_passthrough.c b/src/win/win_serial_passthrough.c new file mode 100644 index 000000000..bf7ce7f98 --- /dev/null +++ b/src/win/win_serial_passthrough.c @@ -0,0 +1,223 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Definitions for platform specific serial to host passthrough + * + * + * Authors: Andreas J. Reichel , + * Jasmine Iwanek + * + * Copyright 2021 Andreas J. Reichel + * Copyright 2021-2022 Jasmine Iwanek + */ + +#define _XOPEN_SOURCE 500 +#include +#include +#include +#include +#include + +#include <86box/86box.h> +#include <86box/log.h> +#include <86box/timer.h> +#include <86box/plat.h> +#include <86box/device.h> +#include <86box/serial_passthrough.h> +#include <86box/plat_serial_passthrough.h> + +#include + +#define LOG_PREFIX "serial_passthrough: " + +void +plat_serpt_close(void *p) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) p; + + // fclose(dev->master_fd); + FlushFileBuffers((HANDLE) dev->master_fd); + if (dev->mode == SERPT_MODE_VCON) + DisconnectNamedPipe((HANDLE) dev->master_fd); + if (dev->mode == SERPT_MODE_HOSTSER) { + SetCommState((HANDLE)dev->master_fd, (DCB*)dev->backend_priv); + free(dev->backend_priv); + } + CloseHandle((HANDLE) dev->master_fd); +} + +static void +plat_serpt_write_vcon(serial_passthrough_t *dev, uint8_t data) +{ + /* fd_set wrfds; + * int res; + */ + + /* We cannot use select here, this would block the hypervisor! */ + /* FD_ZERO(&wrfds); + FD_SET(ctx->master_fd, &wrfds); + + res = select(ctx->master_fd + 1, NULL, &wrfds, NULL, NULL); + + if (res <= 0) { + return; + } + */ + + /* just write it out */ + // fwrite(dev->master_fd, &data, 1); + DWORD bytesWritten = 0; + WriteFile((HANDLE) dev->master_fd, &data, 1, &bytesWritten, NULL); + if (bytesWritten == 0) { + fatal("serial_passthrough: WriteFile pipe write-buffer full!"); + } +} + +void +plat_serpt_set_params(void *p) +{ + serial_passthrough_t *dev = (serial_passthrough_t *)p; + + if (dev->mode == SERPT_MODE_HOSTSER) { + DCB serialattr = {}; + GetCommState((HANDLE)dev->master_fd, &serialattr); +#define BAUDRATE_RANGE(baud_rate, min, max) if (baud_rate >= min && baud_rate < max) { serialattr.BaudRate = min; } + + BAUDRATE_RANGE(dev->baudrate, 110, 300); + BAUDRATE_RANGE(dev->baudrate, 300, 600); + BAUDRATE_RANGE(dev->baudrate, 600, 1200); + BAUDRATE_RANGE(dev->baudrate, 1200, 2400); + BAUDRATE_RANGE(dev->baudrate, 2400, 4800); + BAUDRATE_RANGE(dev->baudrate, 4800, 9600); + BAUDRATE_RANGE(dev->baudrate, 9600, 14400); + BAUDRATE_RANGE(dev->baudrate, 14400, 19200); + BAUDRATE_RANGE(dev->baudrate, 19200, 38400); + BAUDRATE_RANGE(dev->baudrate, 38400, 57600); + BAUDRATE_RANGE(dev->baudrate, 57600, 115200); + BAUDRATE_RANGE(dev->baudrate, 115200, 0xFFFFFFFF); + + serialattr.ByteSize = dev->data_bits; + serialattr.StopBits = (dev->serial->lcr & 0x04) ? TWOSTOPBITS : ONESTOPBIT; + if (!(dev->serial->lcr & 0x08)) { + serialattr.fParity = 0; + serialattr.Parity = NOPARITY; + } else { + serialattr.fParity = 1; + if (dev->serial->lcr & 0x20) { + serialattr.Parity = (MARKPARITY) + !!(dev->serial->lcr & 0x10); + } else { + serialattr.Parity = (ODDPARITY) + !!(dev->serial->lcr & 0x10); + } + } + + SetCommState((HANDLE)dev->master_fd, &serialattr); +#undef BAUDRATE_RANGE + } +} + +void +plat_serpt_write(void *p, uint8_t data) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) p; + + switch (dev->mode) { + case SERPT_MODE_VCON: + case SERPT_MODE_HOSTSER: + plat_serpt_write_vcon(dev, data); + break; + default: + break; + } +} + +uint8_t +plat_serpt_read_vcon(serial_passthrough_t *dev, uint8_t *data) +{ + DWORD bytesRead = 0; + ReadFile((HANDLE) dev->master_fd, data, 1, &bytesRead, NULL); + return !!bytesRead; +} + +int +plat_serpt_read(void *p, uint8_t *data) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) p; + int res = 0; + + switch (dev->mode) { + case SERPT_MODE_VCON: + case SERPT_MODE_HOSTSER: + res = plat_serpt_read_vcon(dev, data); + break; + default: + break; + } + return res; +} + +static int +open_pseudo_terminal(serial_passthrough_t *dev) +{ + char ascii_pipe_name[1024] = { 0 }; + snprintf(ascii_pipe_name, sizeof(ascii_pipe_name), "\\\\.\\pipe\\86Box\\%s", vm_name); + dev->master_fd = (intptr_t) CreateNamedPipeA(ascii_pipe_name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT, 32, 65536, 65536, NMPWAIT_USE_DEFAULT_WAIT, NULL); + if (dev->master_fd == (intptr_t) INVALID_HANDLE_VALUE) { + return 0; + } + pclog("Named Pipe @ %s\n", ascii_pipe_name); + return 1; +} + +static int +open_host_serial_port(serial_passthrough_t *dev) +{ + COMMTIMEOUTS timeouts = { + .ReadIntervalTimeout = MAXDWORD, + .ReadTotalTimeoutConstant = 0, + .ReadTotalTimeoutMultiplier = 0, + .WriteTotalTimeoutMultiplier = 0, + .WriteTotalTimeoutConstant = 1000 + }; + DCB* serialattr = calloc(1, sizeof(DCB)); + if (!serialattr) return 0; + dev->master_fd = (intptr_t) CreateFileA(dev->host_serial_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (dev->master_fd == (intptr_t) INVALID_HANDLE_VALUE) { + free(serialattr); + return 0; + } + if (!SetCommTimeouts((HANDLE) dev->master_fd, &timeouts)) { + pclog(LOG_PREFIX "error setting COM port timeouts.\n"); + CloseHandle((HANDLE) dev->master_fd); + free(serialattr); + return 0; + } + GetCommState((HANDLE)dev->master_fd, serialattr); + dev->backend_priv = serialattr; + return 1; +} + +int +plat_serpt_open_device(void *p) +{ + serial_passthrough_t *dev = (serial_passthrough_t *) p; + + switch (dev->mode) { + case SERPT_MODE_VCON: + if (open_pseudo_terminal(dev)) { + return 0; + } + break; + case SERPT_MODE_HOSTSER: + if (open_host_serial_port(dev)) { + return 0; + } + default: + break; + } + return 1; +} diff --git a/src/win/win_settings.c b/src/win/win_settings.c index 07822f28b..4901b283c 100644 --- a/src/win/win_settings.c +++ b/src/win/win_settings.c @@ -71,6 +71,7 @@ #include <86box/plat.h> #include <86box/ui.h> #include <86box/win.h> +#include <86box/serial_passthrough.h> #include "../disk/minivhd/minivhd.h" #include "../disk/minivhd/minivhd_util.h" @@ -111,6 +112,7 @@ static char temp_pcap_dev[NET_CARD_MAX][128]; /* Ports category */ static int temp_lpt_devices[PARALLEL_MAX]; static int temp_serial[SERIAL_MAX], temp_lpt[PARALLEL_MAX]; +static int temp_serial_passthrough_enabled[SERIAL_MAX]; /* Other peripherals category */ static int temp_fdc_card, temp_hdc, temp_ide_ter, temp_ide_qua, temp_cassette; @@ -358,11 +360,13 @@ win_settings_init(void) /* Ports category */ for (i = 0; i < PARALLEL_MAX; i++) { - temp_lpt_devices[i] = lpt_ports[i].device; - temp_lpt[i] = lpt_ports[i].enabled; + temp_lpt_devices[i] = lpt_ports[i].device; + temp_lpt[i] = lpt_ports[i].enabled; + } + for (i = 0; i < SERIAL_MAX; i++) { + temp_serial[i] = com_ports[i].enabled; + temp_serial_passthrough_enabled[i] = serial_passthrough_enabled[i]; } - for (i = 0; i < SERIAL_MAX; i++) - temp_serial[i] = com_ports[i].enabled; /* Storage devices category */ for (i = 0; i < SCSI_BUS_MAX; i++) @@ -484,8 +488,10 @@ win_settings_changed(void) i = i || (temp_lpt_devices[j] != lpt_ports[j].device); i = i || (temp_lpt[j] != lpt_ports[j].enabled); } - for (j = 0; j < SERIAL_MAX; j++) + for (j = 0; j < SERIAL_MAX; j++) { i = i || (temp_serial[j] != com_ports[j].enabled); + i = i || (temp_serial_passthrough_enabled[i] != serial_passthrough_enabled[i]); + } /* Storage devices category */ for (j = 0; j < SCSI_BUS_MAX; j++) @@ -578,8 +584,10 @@ win_settings_save(void) lpt_ports[i].device = temp_lpt_devices[i]; lpt_ports[i].enabled = temp_lpt[i]; } - for (i = 0; i < SERIAL_MAX; i++) + for (i = 0; i < SERIAL_MAX; i++) { com_ports[i].enabled = temp_serial[i]; + serial_passthrough_enabled[i] = temp_serial_passthrough_enabled[i]; + } /* Storage devices category */ for (i = 0; i < SCSI_BUS_MAX; i++) @@ -1764,8 +1772,10 @@ win_settings_ports_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) settings_enable_window(hdlg, IDC_COMBO_LPT1 + i, temp_lpt[i]); } - for (i = 0; i < SERIAL_MAX; i++) + for (i = 0; i < SERIAL_MAX; i++) { settings_set_check(hdlg, IDC_CHECK_SERIAL1 + i, temp_serial[i]); + settings_set_check(hdlg, IDC_CHECK_SERIAL_PASS1 + i, temp_serial_passthrough_enabled[i]); + } free(lptsTemp); @@ -1790,8 +1800,10 @@ win_settings_ports_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) temp_lpt[i] = settings_get_check(hdlg, IDC_CHECK_PARALLEL1 + i); } - for (i = 0; i < SERIAL_MAX; i++) + for (i = 0; i < SERIAL_MAX; i++) { temp_serial[i] = settings_get_check(hdlg, IDC_CHECK_SERIAL1 + i); + temp_serial_passthrough_enabled[i] = settings_get_check(hdlg, IDC_CHECK_SERIAL_PASS1 + i); + } default: return FALSE; diff --git a/src/win/win_toolbar.c b/src/win/win_toolbar.c index fade8d25e..6b1edefb2 100644 --- a/src/win/win_toolbar.c +++ b/src/win/win_toolbar.c @@ -2,8 +2,8 @@ #include #include #include -#include -#include +#include +#include #include <86box/86box.h> #include <86box/plat.h> #include <86box/resource.h>