Serial Passthrough
This commit is contained in:
@@ -11,13 +11,16 @@
|
||||
# Authors: David Hrdlička, <hrdlickadavid@outlook.com>
|
||||
#
|
||||
# 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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
357
src/device/serial_passthrough.c
Normal file
357
src/device/serial_passthrough.c
Normal file
@@ -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 <webmaster@6th-dimension.com>,
|
||||
* Jasmine Iwanek <jasmine@iwanek.co.uk>
|
||||
*
|
||||
* Copyright 2021 Andreas J. Reichel.
|
||||
* Copyright 2021-2022 Jasmine Iwanek.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
#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
|
||||
};
|
||||
Reference in New Issue
Block a user