Files
86Box/src/qt/win_serial_passthrough.c

240 lines
7.0 KiB
C
Raw Normal View History

2023-02-14 20:37:58 -05:00
/*
* 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 <webmaster@6th-dimension.com>,
* Jasmine Iwanek <jasmine@iwanek.co.uk>
*
* Copyright 2021 Andreas J. Reichel
2025-06-19 21:39:09 -04:00
* Copyright 2021-2025 Jasmine Iwanek
2023-02-14 20:37:58 -05:00
*/
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <wchar.h>
2023-02-14 20:37:58 -05:00
#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 <86box/ui.h>
2023-02-14 20:37:58 -05:00
#include <windows.h>
#define LOG_PREFIX "serial_passthrough: "
void
2023-08-21 20:25:16 -04:00
plat_serpt_close(void *priv)
2023-02-14 20:37:58 -05:00
{
2023-08-21 20:25:16 -04:00
serial_passthrough_t *dev = (serial_passthrough_t *) priv;
2023-02-14 20:37:58 -05:00
2023-08-21 20:25:16 -04:00
#if 0
fclose(dev->master_fd);
#endif
2023-02-14 20:37:58 -05:00
FlushFileBuffers((HANDLE) dev->master_fd);
2025-06-19 21:39:09 -04:00
if (dev->mode == SERPT_MODE_NPIPE_SRV)
2023-02-14 20:37:58 -05:00
DisconnectNamedPipe((HANDLE) dev->master_fd);
2025-06-19 21:39:09 -04:00
else if (dev->mode == SERPT_MODE_HOSTSER) {
2023-02-28 23:24:58 -05:00
SetCommState((HANDLE) dev->master_fd, (DCB *) dev->backend_priv);
2023-02-14 20:37:58 -05:00
free(dev->backend_priv);
}
CloseHandle((HANDLE) dev->master_fd);
}
static void
plat_serpt_write_vcon(serial_passthrough_t *dev, uint8_t data)
{
2023-08-21 20:25:16 -04:00
#if 0
fd_set wrfds;
int res;
#endif
2023-02-14 20:37:58 -05:00
/* We cannot use select here, this would block the hypervisor! */
2023-08-21 20:25:16 -04:00
#if 0
FD_ZERO(&wrfds);
FD_SET(ctx->master_fd, &wrfds);
2023-02-14 20:37:58 -05:00
2023-08-21 20:25:16 -04:00
res = select(ctx->master_fd + 1, NULL, &wrfds, NULL, NULL);
2023-02-14 20:37:58 -05:00
2023-08-21 20:25:16 -04:00
if (res <= 0)
return;
#endif
2023-02-14 20:37:58 -05:00
/* just write it out */
2023-08-21 20:25:16 -04:00
#if 0
fwrite(dev->master_fd, &data, 1);
#endif
2023-02-14 20:37:58 -05:00
DWORD bytesWritten = 0;
WriteFile((HANDLE) dev->master_fd, &data, 1, &bytesWritten, NULL);
}
void
2023-08-21 20:25:16 -04:00
plat_serpt_set_params(void *priv)
2023-02-14 20:37:58 -05:00
{
2023-08-21 20:25:16 -04:00
const serial_passthrough_t *dev = (serial_passthrough_t *) priv;
2023-02-28 23:24:58 -05:00
if (dev->mode == SERPT_MODE_HOSTSER) {
DCB serialattr = { 0 };
2023-02-28 23:24:58 -05:00
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) {
2023-05-11 03:02:36 -04:00
serialattr.Parity = MARKPARITY + !!(dev->serial->lcr & 0x10);
2023-02-28 23:24:58 -05:00
} else {
2023-05-11 03:02:36 -04:00
serialattr.Parity = ODDPARITY + !!(dev->serial->lcr & 0x10);
2023-02-28 23:24:58 -05:00
}
}
SetCommState((HANDLE) dev->master_fd, &serialattr);
2023-02-14 20:37:58 -05:00
#undef BAUDRATE_RANGE
}
}
void
2023-08-21 20:25:16 -04:00
plat_serpt_write(void *priv, uint8_t data)
2023-02-14 20:37:58 -05:00
{
2023-08-21 20:25:16 -04:00
serial_passthrough_t *dev = (serial_passthrough_t *) priv;
2023-02-14 20:37:58 -05:00
switch (dev->mode) {
2025-06-19 21:39:09 -04:00
case SERPT_MODE_NPIPE_SRV:
case SERPT_MODE_NPIPE_CLNT:
2023-02-14 20:37:58 -05:00
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
2023-08-21 20:25:16 -04:00
plat_serpt_read(void *priv, uint8_t *data)
2023-02-14 20:37:58 -05:00
{
2023-08-21 20:25:16 -04:00
serial_passthrough_t *dev = (serial_passthrough_t *) priv;
2023-02-14 20:37:58 -05:00
int res = 0;
switch (dev->mode) {
2025-06-19 21:39:09 -04:00
case SERPT_MODE_NPIPE_SRV:
case SERPT_MODE_NPIPE_CLNT:
2023-02-14 20:37:58 -05:00
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 };
strncpy(ascii_pipe_name, dev->named_pipe, sizeof(ascii_pipe_name));
ascii_pipe_name[1023] = '\0';
dev->master_fd = (intptr_t) CreateNamedPipeA(ascii_pipe_name, PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT, 1, 65536, 65536, NMPWAIT_USE_DEFAULT_WAIT, NULL);
2023-02-14 20:37:58 -05:00
if (dev->master_fd == (intptr_t) INVALID_HANDLE_VALUE) {
wchar_t errorMsg[1024] = { 0 };
wchar_t finalMsg[1024] = { 0 };
2023-02-28 23:24:58 -05:00
DWORD error = GetLastError();
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), errorMsg, 1024, NULL);
swprintf(finalMsg, 1024, L"Named Pipe (server, named_pipe=\"%hs\", port=COM%d): %ls\n", ascii_pipe_name, dev->port + 1, errorMsg);
ui_msgbox(MBX_ERROR | MBX_FATAL, finalMsg);
2023-02-14 20:37:58 -05:00
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
};
2023-02-28 23:24:58 -05:00
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, FILE_FLAG_WRITE_THROUGH, NULL);
2023-02-14 20:37:58 -05:00
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;
}
2023-02-28 23:24:58 -05:00
GetCommState((HANDLE) dev->master_fd, serialattr);
2023-02-14 20:37:58 -05:00
dev->backend_priv = serialattr;
return 1;
}
int
2023-08-21 20:25:16 -04:00
plat_serpt_open_device(void *priv)
2023-02-14 20:37:58 -05:00
{
2023-08-21 20:25:16 -04:00
serial_passthrough_t *dev = (serial_passthrough_t *) priv;
2023-02-14 20:37:58 -05:00
switch (dev->mode) {
2025-06-19 21:39:09 -04:00
case SERPT_MODE_NPIPE_SRV:
if (open_pseudo_terminal(dev))
2023-02-14 20:37:58 -05:00
return 0;
break;
case SERPT_MODE_HOSTSER:
2025-06-19 21:39:09 -04:00
if (open_host_serial_port(dev))
2023-02-14 20:37:58 -05:00
return 0;
2025-06-19 21:39:09 -04:00
break;
2023-02-14 20:37:58 -05:00
default:
break;
}
return 1;
}