/* * 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 #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> #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); } 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 }; strncpy(ascii_pipe_name, dev->named_pipe, 1023); 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) { wchar_t errorMsg[1024] = { 0 }; wchar_t finalMsg[1024] = { 0 }; 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); 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; }