221 lines
6.6 KiB
C
221 lines
6.6 KiB
C
|
|
/*
|
||
|
|
* 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
|
||
|
|
* Copyright 2021-2022 Jasmine Iwanek
|
||
|
|
*/
|
||
|
|
|
||
|
|
#define _XOPEN_SOURCE 500
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <fcntl.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdint.h>
|
||
|
|
|
||
|
|
#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 <windows.h>
|
||
|
|
|
||
|
|
#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);
|
||
|
|
}
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|