diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/VBox/Devices/Makefile.kmk | 44 | ||||
-rw-r--r-- | src/VBox/Devices/Serial/DevSerial.cpp | 1308 | ||||
-rw-r--r-- | src/VBox/Devices/Serial/DevSerialNew.cpp | 530 | ||||
-rw-r--r-- | src/VBox/Devices/Serial/DrvChar.cpp | 392 | ||||
-rw-r--r-- | src/VBox/Devices/Serial/DrvCharNew.cpp | 451 | ||||
-rw-r--r-- | src/VBox/Devices/Serial/DrvHostSerial.cpp | 1419 | ||||
-rw-r--r-- | src/VBox/Devices/Serial/DrvHostSerialNew.cpp | 713 | ||||
-rw-r--r-- | src/VBox/Devices/testcase/Makefile.kmk | 1 | ||||
-rw-r--r-- | src/VBox/Devices/testcase/tstDeviceStructSize.cpp | 13 | ||||
-rw-r--r-- | src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp | 54 |
10 files changed, 721 insertions, 4204 deletions
diff --git a/src/VBox/Devices/Makefile.kmk b/src/VBox/Devices/Makefile.kmk index e6a108794fc..d02c5943d7e 100644 --- a/src/VBox/Devices/Makefile.kmk +++ b/src/VBox/Devices/Makefile.kmk @@ -128,8 +128,7 @@ if !defined(VBOX_ONLY_EXTPACKS) # Goes on almost to the end of the file. $(if $(VBOX_BIOS_DMI_FALLBACK),VBOX_BIOS_DMI_FALLBACK,) \ VBOX_WITH_DMI_CHASSIS \ VBOX_WITH_DMI_OEMSTRINGS \ - $(if $(VBOX_WITH_NEW_LPC_DEVICE),VBOX_WITH_NEW_LPC_DEVICE,) \ - $(if $(VBOX_WITH_NEW_SERIAL),VBOX_WITH_NEW_SERIAL,) + $(if $(VBOX_WITH_NEW_LPC_DEVICE),VBOX_WITH_NEW_LPC_DEVICE,) VBoxDD_DEFS.win = _WIN32_WINNT=0x0510 ifeq ($(KBUILD_TARGET_ARCH),x86) VBoxDD_DEFS.darwin = VBOX_WITH_2X_4GB_ADDR_SPACE @@ -177,6 +176,11 @@ if !defined(VBOX_ONLY_EXTPACKS) # Goes on almost to the end of the file. Network/DrvDedicatedNic.cpp \ PC/DrvACPI.cpp \ PC/DrvAcpiCpu.cpp \ + Serial/DevSerial.cpp \ + Serial/DevOxPcie958.cpp \ + Serial/UartCore.cpp \ + Serial/DrvChar.cpp \ + Serial/DrvHostSerial.cpp \ Serial/DrvNamedPipe.cpp \ Serial/DrvTCP.cpp \ Serial/DrvRawFile.cpp \ @@ -196,22 +200,6 @@ if !defined(VBOX_ONLY_EXTPACKS) # Goes on almost to the end of the file. VBoxDD_SOURCES += Storage/DrvHostFloppy.cpp endif - if defined(VBOX_WITH_NEW_SERIAL) - VBoxDD_SOURCES += \ - Serial/DevSerialNew.cpp \ - Serial/DevOxPcie958.cpp \ - Serial/UartCore.cpp \ - Serial/DrvCharNew.cpp \ - Serial/DrvHostSerialNew.cpp - else - VBoxDD_SOURCES += \ - Serial/DevSerial.cpp \ - Serial/DrvChar.cpp - ifn1of ($(KBUILD_TARGET), os2) - VBoxDD_SOURCES += \ - Serial/DrvHostSerial.cpp - endif - endif ifn1of ($(KBUILD_TARGET), darwin) VBoxDD_SOURCES += Storage/HBDMgmt-generic.cpp @@ -918,6 +906,8 @@ if !defined(VBOX_ONLY_EXTPACKS) # Goes on almost to the end of the file. Storage/DevATA.cpp \ Network/DevPCNet.cpp \ Serial/DevSerial.cpp \ + Serial/DevOxPcie958.cpp \ + Serial/UartCore.cpp \ Parallel/DevParallel.cpp \ VMMDev/VMMDevTesting.cpp @@ -1021,14 +1011,6 @@ if !defined(VBOX_ONLY_EXTPACKS) # Goes on almost to the end of the file. Storage/DevNVMe.cpp endif - if defined(VBOX_WITH_NEW_SERIAL) - VBoxDDRC_SOURCES := \ - $(filter-out Serial/DevSerial.cpp, $(VBoxDDRC_SOURCES)) \ - Serial/DevSerialNew.cpp \ - Serial/DevOxPcie958.cpp \ - Serial/UartCore.cpp - endif - if1of ($(VBOX_LDR_FMT32), pe lx) VBoxDDRC_LIBS = \ $(PATH_STAGE_LIB)/VMMRCBuiltin$(VBOX_SUFF_LIB) \ @@ -1097,6 +1079,8 @@ if !defined(VBOX_ONLY_EXTPACKS) # Goes on almost to the end of the file. Storage/DevATA.cpp \ Network/DevPCNet.cpp \ Serial/DevSerial.cpp \ + Serial/DevOxPcie958.cpp \ + Serial/UartCore.cpp \ Parallel/DevParallel.cpp \ VMMDev/VMMDevTesting.cpp \ Network/DrvIntNet.cpp \ @@ -1198,14 +1182,6 @@ if !defined(VBOX_ONLY_EXTPACKS) # Goes on almost to the end of the file. Storage/DevNVMe.cpp endif - if defined(VBOX_WITH_NEW_SERIAL) - VBoxDDR0_SOURCES := \ - $(filter-out Serial/DevSerial.cpp, $(VBoxDDR0_SOURCES)) \ - Serial/DevSerialNew.cpp \ - Serial/DevOxPcie958.cpp \ - Serial/UartCore.cpp - endif - ifdef VBOX_WITH_HGSMI VBoxDDR0_DEFS += VBOX_WITH_HGSMI endif diff --git a/src/VBox/Devices/Serial/DevSerial.cpp b/src/VBox/Devices/Serial/DevSerial.cpp index ce0baf67f60..b8c90029088 100644 --- a/src/VBox/Devices/Serial/DevSerial.cpp +++ b/src/VBox/Devices/Serial/DevSerial.cpp @@ -1,11 +1,12 @@ /* $Id$ */ /** @file * DevSerial - 16550A UART emulation. - * (taken from hw/serial.c 2010/05/15 with modifications) + * + * The documentation for this device was taken from the PC16550D spec from TI. */ /* - * Copyright (C) 2006-2017 Oracle Corporation + * Copyright (C) 2018 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -16,39 +17,14 @@ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ -/* - * This code is based on: - * - * QEMU 16550A UART emulation - * - * Copyright (c) 2003-2004 Fabrice Bellard - * Copyright (c) 2008 Citrix Systems, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_DEV_SERIAL #include <VBox/vmm/pdmdev.h> +#include <VBox/vmm/pdmserialifs.h> +#include <VBox/vmm/vm.h> #include <iprt/assert.h> #include <iprt/uuid.h> #include <iprt/string.h> @@ -56,116 +32,23 @@ #include <iprt/critsect.h> #include "VBoxDD.h" - -#undef VBOX_SERIAL_PCI /* The PCI variant has lots of problems: wrong IRQ line and wrong IO base assigned. */ - -#ifdef VBOX_SERIAL_PCI -# include <VBox/pci.h> -#endif /* VBOX_SERIAL_PCI */ +#include "UartCore.h" /********************************************************************************************************************************* * Defined Constants And Macros * *********************************************************************************************************************************/ -#define SERIAL_SAVED_STATE_VERSION_16450 3 -#define SERIAL_SAVED_STATE_VERSION_MISSING_BITS 4 -#define SERIAL_SAVED_STATE_VERSION 5 - -#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ - -#define UART_IER_MSI 0x08 /* Enable Modem status interrupt */ -#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */ -#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */ -#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */ - -#define UART_IIR_NO_INT 0x01 /* No interrupts pending */ -#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */ - -#define UART_IIR_MSI 0x00 /* Modem status interrupt */ -#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */ -#define UART_IIR_RDI 0x04 /* Receiver data interrupt */ -#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */ -#define UART_IIR_CTI 0x0C /* Character Timeout Indication */ - -#define UART_IIR_FENF 0x80 /* Fifo enabled, but not functioning */ -#define UART_IIR_FE 0xC0 /* Fifo enabled */ - -/* - * These are the definitions for the Modem Control Register - */ -#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */ -#define UART_MCR_OUT2 0x08 /* Out2 complement */ -#define UART_MCR_OUT1 0x04 /* Out1 complement */ -#define UART_MCR_RTS 0x02 /* RTS complement */ -#define UART_MCR_DTR 0x01 /* DTR complement */ - -/* - * These are the definitions for the Modem Status Register - */ -#define UART_MSR_DCD 0x80 /* Data Carrier Detect */ -#define UART_MSR_RI 0x40 /* Ring Indicator */ -#define UART_MSR_DSR 0x20 /* Data Set Ready */ -#define UART_MSR_CTS 0x10 /* Clear to Send */ -#define UART_MSR_DDCD 0x08 /* Delta DCD */ -#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */ -#define UART_MSR_DDSR 0x02 /* Delta DSR */ -#define UART_MSR_DCTS 0x01 /* Delta CTS */ -#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */ - -#define UART_LSR_TEMT 0x40 /* Transmitter empty */ -#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ -#define UART_LSR_BI 0x10 /* Break interrupt indicator */ -#define UART_LSR_FE 0x08 /* Frame error indicator */ -#define UART_LSR_PE 0x04 /* Parity error indicator */ -#define UART_LSR_OE 0x02 /* Overrun error indicator */ -#define UART_LSR_DR 0x01 /* Receiver data ready */ -#define UART_LSR_INT_ANY 0x1E /* Any of the lsr-interrupt-triggering status bits */ - -/* - * Interrupt trigger levels. - * The byte-counts are for 16550A - in newer UARTs the byte-count for each ITL is higher. - */ -#define UART_FCR_ITL_1 0x00 /* 1 byte ITL */ -#define UART_FCR_ITL_2 0x40 /* 4 bytes ITL */ -#define UART_FCR_ITL_3 0x80 /* 8 bytes ITL */ -#define UART_FCR_ITL_4 0xC0 /* 14 bytes ITL */ - -#define UART_FCR_DMS 0x08 /* DMA Mode Select */ -#define UART_FCR_XFR 0x04 /* XMIT Fifo Reset */ -#define UART_FCR_RFR 0x02 /* RCVR Fifo Reset */ -#define UART_FCR_FE 0x01 /* FIFO Enable */ - -#define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */ - -#define XMIT_FIFO 0 -#define RECV_FIFO 1 -#define MIN_XMIT_RETRY 16 -#define MAX_XMIT_RETRY_TIME 1 /* max time (in seconds) for retrying the character xmit before dropping it */ /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ -struct SerialFifo -{ - uint8_t data[UART_FIFO_LENGTH]; - uint8_t count; - uint8_t itl; - uint8_t tail; - uint8_t head; -}; - /** * Serial device. - * - * @implements PDMIBASE - * @implements PDMICHARPORT */ -typedef struct SerialState +typedef struct DEVSERIAL { - /** Access critical section. */ - PDMCRITSECT CritSect; /** Pointer to the device instance - R3 Ptr. */ PPDMDEVINSR3 pDevInsR3; /** Pointer to the device instance - R0 Ptr. */ @@ -174,743 +57,106 @@ typedef struct SerialState PPDMDEVINSRC pDevInsRC; /** Alignment. */ RTRCPTR Alignment0; - /** LUN\#0: The base interface. */ - PDMIBASE IBase; - /** LUN\#0: The character port interface. */ - PDMICHARPORT ICharPort; - /** Pointer to the attached base driver. */ - R3PTRTYPE(PPDMIBASE) pDrvBase; - /** Pointer to the attached character driver. */ - R3PTRTYPE(PPDMICHARCONNECTOR) pDrvChar; - - RTSEMEVENT ReceiveSem; - PTMTIMERR3 fifo_timeout_timer; - PTMTIMERR3 transmit_timerR3; - PTMTIMERR0 transmit_timerR0; /* currently not used */ - PTMTIMERRC transmit_timerRC; /* currently not used */ - RTRCPTR Alignment1; - SerialFifo recv_fifo; - SerialFifo xmit_fifo; - - uint32_t base; - uint16_t divider; - uint16_t Alignment2[1]; - uint8_t rbr; /**< receive register */ - uint8_t thr; /**< transmit holding register */ - uint8_t tsr; /**< transmit shift register */ - uint8_t ier; /**< interrupt enable register */ - uint8_t iir; /**< interrupt identification register, R/O */ - uint8_t lcr; /**< line control register */ - uint8_t mcr; /**< modem control register */ - uint8_t lsr; /**< line status register, R/O */ - uint8_t msr; /**< modem status register, R/O */ - uint8_t scr; /**< scratch register */ - uint8_t fcr; /**< fifo control register */ - uint8_t fcr_vmstate; - /* NOTE: this hidden state is necessary for tx irq generation as - it can be reset while reading iir */ - int thr_ipending; - int timeout_ipending; - int irq; - int last_break_enable; - /** Counter for retrying xmit */ - int tsr_retry; - int tsr_retry_bound; /**< number of retries before dropping a character */ - int tsr_retry_bound_max; /**< maximum possible tsr_retry_bound value that can be set while dynamic bound adjustment */ - int tsr_retry_bound_min; /**< minimum possible tsr_retry_bound value that can be set while dynamic bound adjustment */ - bool msr_changed; - bool fGCEnabled; + /** Flag whether the R0 portion of this device is enabled. */ bool fR0Enabled; - bool fYieldOnLSRRead; - bool volatile fRecvWaiting; - bool f16550AEnabled; - bool Alignment3[6]; - /** Time it takes to transmit a character */ - uint64_t char_transmit_time; - -#ifdef VBOX_SERIAL_PCI - PDMPCIDEV PciDev; -#endif /* VBOX_SERIAL_PCI */ + /** Flag whether the RC portion of this device is enabled. */ + bool fRCEnabled; + /** Alignment. */ + bool afAlignment1[2]; + /** The IRQ value. */ + uint8_t uIrq; + /** The base I/O port the device is registered at. */ + RTIOPORT PortBase; + + /** The UART core. */ + UARTCORE UartCore; } DEVSERIAL; /** Pointer to the serial device state. */ typedef DEVSERIAL *PDEVSERIAL; #ifndef VBOX_DEVICE_STRUCT_TESTCASE -#ifdef IN_RING3 - -static int serial_can_receive(PDEVSERIAL pThis); -static void serial_receive(PDEVSERIAL pThis, const uint8_t *buf, int size); - -static void fifo_clear(PDEVSERIAL pThis, int fifo) -{ - SerialFifo *f = (fifo) ? &pThis->recv_fifo : &pThis->xmit_fifo; - memset(f->data, 0, UART_FIFO_LENGTH); - f->count = 0; - f->head = 0; - f->tail = 0; -} - -static int fifo_put(PDEVSERIAL pThis, int fifo, uint8_t chr) -{ - SerialFifo *f = (fifo) ? &pThis->recv_fifo : &pThis->xmit_fifo; - - /* Receive overruns do not overwrite FIFO contents. */ - if (fifo == XMIT_FIFO || f->count < UART_FIFO_LENGTH) - { - f->data[f->head++] = chr; - if (f->head == UART_FIFO_LENGTH) - f->head = 0; - } - - if (f->count < UART_FIFO_LENGTH) - f->count++; - else if (fifo == XMIT_FIFO) /* need to at least adjust tail to maintain pipe state consistency */ - ++f->tail; - else if (fifo == RECV_FIFO) - pThis->lsr |= UART_LSR_OE; - - return 1; -} - -static uint8_t fifo_get(PDEVSERIAL pThis, int fifo) -{ - SerialFifo *f = (fifo) ? &pThis->recv_fifo : &pThis->xmit_fifo; - uint8_t c; - - if (f->count == 0) - return 0; - - c = f->data[f->tail++]; - if (f->tail == UART_FIFO_LENGTH) - f->tail = 0; - f->count--; - - return c; -} - -static void serial_update_irq(PDEVSERIAL pThis) -{ - uint8_t tmp_iir = UART_IIR_NO_INT; - - if ( (pThis->ier & UART_IER_RLSI) - && (pThis->lsr & UART_LSR_INT_ANY)) { - tmp_iir = UART_IIR_RLSI; - } else if ((pThis->ier & UART_IER_RDI) && pThis->timeout_ipending) { - /* Note that(pThis->ier & UART_IER_RDI) can mask this interrupt, - * this is not in the specification but is observed on existing - * hardware. */ - tmp_iir = UART_IIR_CTI; - } else if ( (pThis->ier & UART_IER_RDI) - && (pThis->lsr & UART_LSR_DR) - && ( !(pThis->fcr & UART_FCR_FE) - || pThis->recv_fifo.count >= pThis->recv_fifo.itl)) { - tmp_iir = UART_IIR_RDI; - } else if ( (pThis->ier & UART_IER_THRI) - && pThis->thr_ipending) { - tmp_iir = UART_IIR_THRI; - } else if ( (pThis->ier & UART_IER_MSI) - && (pThis->msr & UART_MSR_ANY_DELTA)) { - tmp_iir = UART_IIR_MSI; - } - pThis->iir = tmp_iir | (pThis->iir & 0xF0); - - /** XXX only call the SetIrq function if the state really changes! */ - if (tmp_iir != UART_IIR_NO_INT) { - Log(("serial_update_irq %d 1\n", pThis->irq)); -# ifdef VBOX_SERIAL_PCI - PDMDevHlpPCISetIrqNoWait(pThis->CTX_SUFF(pDevIns), 0, 1); -# else /* !VBOX_SERIAL_PCI */ - PDMDevHlpISASetIrqNoWait(pThis->CTX_SUFF(pDevIns), pThis->irq, 1); -# endif /* !VBOX_SERIAL_PCI */ - } else { - Log(("serial_update_irq %d 0\n", pThis->irq)); -# ifdef VBOX_SERIAL_PCI - PDMDevHlpPCISetIrqNoWait(pThis->CTX_SUFF(pDevIns), 0, 0); -# else /* !VBOX_SERIAL_PCI */ - PDMDevHlpISASetIrqNoWait(pThis->CTX_SUFF(pDevIns), pThis->irq, 0); -# endif /* !VBOX_SERIAL_PCI */ - } -} - -static void serial_tsr_retry_update_parameters(PDEVSERIAL pThis, uint64_t tf) -{ - pThis->tsr_retry_bound_max = RT_MAX((tf * MAX_XMIT_RETRY_TIME) / pThis->char_transmit_time, MIN_XMIT_RETRY); - pThis->tsr_retry_bound_min = RT_MAX(pThis->tsr_retry_bound_max / (1000 * MAX_XMIT_RETRY_TIME), MIN_XMIT_RETRY); - /* for simplicity just reset to max retry count */ - pThis->tsr_retry_bound = pThis->tsr_retry_bound_max; -} - -static void serial_tsr_retry_bound_reached(PDEVSERIAL pThis) -{ - /* this is most likely means we have some backend connection issues */ - /* decrement the retry bound */ - pThis->tsr_retry_bound = RT_MAX(pThis->tsr_retry_bound / (10 * MAX_XMIT_RETRY_TIME), pThis->tsr_retry_bound_min); -} - -static void serial_tsr_retry_succeeded(PDEVSERIAL pThis) -{ - /* success means we have a backend connection working OK, - * set retry bound to its maximum value */ - pThis->tsr_retry_bound = pThis->tsr_retry_bound_max; -} -static void serial_update_parameters(PDEVSERIAL pThis) -{ - int speed, parity, data_bits, stop_bits, frame_size; - - if (pThis->divider == 0) - return; - - frame_size = 1; - if (pThis->lcr & 0x08) { - frame_size++; - if (pThis->lcr & 0x10) - parity = 'E'; - else - parity = 'O'; - } else { - parity = 'N'; - } - if (pThis->lcr & 0x04) - stop_bits = 2; - else - stop_bits = 1; - - data_bits = (pThis->lcr & 0x03) + 5; - frame_size += data_bits + stop_bits; - speed = 115200 / pThis->divider; - uint64_t tf = TMTimerGetFreq(CTX_SUFF(pThis->transmit_timer)); - pThis->char_transmit_time = (tf / speed) * frame_size; - serial_tsr_retry_update_parameters(pThis, tf); - - Log(("speed=%d parity=%c data=%d stop=%d\n", speed, parity, data_bits, stop_bits)); - - if (RT_LIKELY(pThis->pDrvChar)) - pThis->pDrvChar->pfnSetParameters(pThis->pDrvChar, speed, parity, data_bits, stop_bits); -} - -static void serial_xmit(PDEVSERIAL pThis, bool bRetryXmit) -{ - if (pThis->tsr_retry <= 0) { - if (pThis->fcr & UART_FCR_FE) { - pThis->tsr = fifo_get(pThis, XMIT_FIFO); - if (!pThis->xmit_fifo.count) - pThis->lsr |= UART_LSR_THRE; - } else { - pThis->tsr = pThis->thr; - pThis->lsr |= UART_LSR_THRE; - } - } - - if (pThis->mcr & UART_MCR_LOOP) { - /* in loopback mode, say that we just received a char */ - serial_receive(pThis, &pThis->tsr, 1); - } else if ( RT_LIKELY(pThis->pDrvChar) - && RT_FAILURE(pThis->pDrvChar->pfnWrite(pThis->pDrvChar, &pThis->tsr, 1))) { - if ((pThis->tsr_retry >= 0) && ((!bRetryXmit) || (pThis->tsr_retry <= pThis->tsr_retry_bound))) { - if (!pThis->tsr_retry) - pThis->tsr_retry = 1; /* make sure the retry state is always set */ - else if (bRetryXmit) /* do not increase the retry count if the retry is actually caused by next char write */ - pThis->tsr_retry++; - - TMTimerSet(CTX_SUFF(pThis->transmit_timer), TMTimerGet(CTX_SUFF(pThis->transmit_timer)) + pThis->char_transmit_time * 4); - return; - } else { - /* drop this character. */ - pThis->tsr_retry = 0; - serial_tsr_retry_bound_reached(pThis); - } - } - else { - pThis->tsr_retry = 0; - serial_tsr_retry_succeeded(pThis); - } - - if (!(pThis->lsr & UART_LSR_THRE)) - TMTimerSet(CTX_SUFF(pThis->transmit_timer), - TMTimerGet(CTX_SUFF(pThis->transmit_timer)) + pThis->char_transmit_time); - - if (pThis->lsr & UART_LSR_THRE) { - pThis->lsr |= UART_LSR_TEMT; - pThis->thr_ipending = 1; - serial_update_irq(pThis); - } -} - -#endif /* IN_RING3 */ - -static int serial_ioport_write(PDEVSERIAL pThis, uint32_t addr, uint32_t val) -{ -#ifndef IN_RING3 - NOREF(pThis); RT_NOREF_PV(addr); RT_NOREF_PV(val); - return VINF_IOM_R3_IOPORT_WRITE; -#else - addr &= 7; - switch(addr) { - default: - case 0: - if (pThis->lcr & UART_LCR_DLAB) { - pThis->divider = (pThis->divider & 0xff00) | val; - serial_update_parameters(pThis); - } else { - pThis->thr = (uint8_t) val; - if (pThis->fcr & UART_FCR_FE) { - fifo_put(pThis, XMIT_FIFO, pThis->thr); - pThis->thr_ipending = 0; - pThis->lsr &= ~UART_LSR_TEMT; - pThis->lsr &= ~UART_LSR_THRE; - serial_update_irq(pThis); - } else { - pThis->thr_ipending = 0; - pThis->lsr &= ~UART_LSR_THRE; - serial_update_irq(pThis); - } - serial_xmit(pThis, false); - } - break; - case 1: - if (pThis->lcr & UART_LCR_DLAB) { - pThis->divider = (pThis->divider & 0x00ff) | (val << 8); - serial_update_parameters(pThis); - } else { - pThis->ier = val & 0x0f; - if (pThis->lsr & UART_LSR_THRE) { - pThis->thr_ipending = 1; - serial_update_irq(pThis); - } - } - break; - case 2: - if (!pThis->f16550AEnabled) - break; - - val = val & 0xFF; - - if (pThis->fcr == val) - break; - - /* Did the enable/disable flag change? If so, make sure FIFOs get flushed */ - if ((val ^ pThis->fcr) & UART_FCR_FE) - val |= UART_FCR_XFR | UART_FCR_RFR; - - /* FIFO clear */ - if (val & UART_FCR_RFR) { - TMTimerStop(pThis->fifo_timeout_timer); - pThis->timeout_ipending = 0; - fifo_clear(pThis, RECV_FIFO); - } - if (val & UART_FCR_XFR) { - fifo_clear(pThis, XMIT_FIFO); - } - - if (val & UART_FCR_FE) { - pThis->iir |= UART_IIR_FE; - /* Set RECV_FIFO trigger Level */ - switch (val & 0xC0) { - case UART_FCR_ITL_1: - pThis->recv_fifo.itl = 1; - break; - case UART_FCR_ITL_2: - pThis->recv_fifo.itl = 4; - break; - case UART_FCR_ITL_3: - pThis->recv_fifo.itl = 8; - break; - case UART_FCR_ITL_4: - pThis->recv_fifo.itl = 14; - break; - } - } else - pThis->iir &= ~UART_IIR_FE; - - /* Set fcr - or at least the bits in it that are supposed to "stick" */ - pThis->fcr = val & 0xC9; - serial_update_irq(pThis); - break; - case 3: - { - int break_enable; - pThis->lcr = val; - serial_update_parameters(pThis); - break_enable = (val >> 6) & 1; - if (break_enable != pThis->last_break_enable) { - pThis->last_break_enable = break_enable; - if (RT_LIKELY(pThis->pDrvChar)) - { - Log(("serial_ioport_write: Set break %d\n", break_enable)); - int rc = pThis->pDrvChar->pfnSetBreak(pThis->pDrvChar, !!break_enable); - AssertRC(rc); - } - } - } - break; - case 4: - pThis->mcr = val & 0x1f; - if (RT_LIKELY(pThis->pDrvChar)) - { - int rc = pThis->pDrvChar->pfnSetModemLines(pThis->pDrvChar, - !!(pThis->mcr & UART_MCR_RTS), - !!(pThis->mcr & UART_MCR_DTR)); - AssertRC(rc); - } - break; - case 5: - break; - case 6: - break; - case 7: - pThis->scr = val; - break; - } - return VINF_SUCCESS; -#endif -} +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ -static uint32_t serial_ioport_read(PDEVSERIAL pThis, uint32_t addr, int *pRC) -{ - uint32_t ret = ~0U; - - *pRC = VINF_SUCCESS; - - addr &= 7; - switch(addr) { - default: - case 0: - if (pThis->lcr & UART_LCR_DLAB) { - /* DLAB == 1: divisor latch (LS) */ - ret = pThis->divider & 0xff; - } else { -#ifndef IN_RING3 - *pRC = VINF_IOM_R3_IOPORT_READ; -#else - if (pThis->fcr & UART_FCR_FE) { - ret = fifo_get(pThis, RECV_FIFO); - if (pThis->recv_fifo.count == 0) - pThis->lsr &= ~(UART_LSR_DR | UART_LSR_BI); - else - TMTimerSet(pThis->fifo_timeout_timer, - TMTimerGet(pThis->fifo_timeout_timer) + pThis->char_transmit_time * 4); - pThis->timeout_ipending = 0; - } else { - Log(("serial_io_port_read: read 0x%X\n", pThis->rbr)); - ret = pThis->rbr; - pThis->lsr &= ~(UART_LSR_DR | UART_LSR_BI); - } - serial_update_irq(pThis); - if (pThis->fRecvWaiting) - { - pThis->fRecvWaiting = false; - int rc = RTSemEventSignal(pThis->ReceiveSem); - AssertRC(rc); - } -#endif - } - break; - case 1: - if (pThis->lcr & UART_LCR_DLAB) { - /* DLAB == 1: divisor latch (MS) */ - ret = (pThis->divider >> 8) & 0xff; - } else { - ret = pThis->ier; - } - break; - case 2: -#ifndef IN_RING3 - *pRC = VINF_IOM_R3_IOPORT_READ; -#else - ret = pThis->iir; - if ((ret & UART_IIR_ID) == UART_IIR_THRI) { - pThis->thr_ipending = 0; - serial_update_irq(pThis); - } - /* reset msr changed bit */ - pThis->msr_changed = false; -#endif - break; - case 3: - ret = pThis->lcr; - break; - case 4: - ret = pThis->mcr; - break; - case 5: - if ((pThis->lsr & UART_LSR_DR) == 0 && pThis->fYieldOnLSRRead) - { - /* No data available and yielding is enabled, so yield in ring3. */ -#ifndef IN_RING3 - *pRC = VINF_IOM_R3_IOPORT_READ; - break; -#else - RTThreadYield (); -#endif - } - ret = pThis->lsr; - /* Clear break and overrun interrupts */ - if (pThis->lsr & (UART_LSR_BI|UART_LSR_OE)) { -#ifndef IN_RING3 - *pRC = VINF_IOM_R3_IOPORT_READ; -#else - pThis->lsr &= ~(UART_LSR_BI|UART_LSR_OE); - serial_update_irq(pThis); -#endif - } - break; - case 6: - if (pThis->mcr & UART_MCR_LOOP) { - /* in loopback, the modem output pins are connected to the - inputs */ - ret = (pThis->mcr & 0x0c) << 4; - ret |= (pThis->mcr & 0x02) << 3; - ret |= (pThis->mcr & 0x01) << 5; - } else { - ret = pThis->msr; - /* Clear delta bits & msr int after read, if they were set */ - if (pThis->msr & UART_MSR_ANY_DELTA) { -#ifndef IN_RING3 - *pRC = VINF_IOM_R3_IOPORT_READ; -#else - pThis->msr &= 0xF0; - serial_update_irq(pThis); -#endif - } - } - break; - case 7: - ret = pThis->scr; - break; - } - return ret; -} -#ifdef IN_RING3 +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ -static int serial_can_receive(PDEVSERIAL pThis) -{ - if (pThis->fcr & UART_FCR_FE) { - if (pThis->recv_fifo.count < UART_FIFO_LENGTH) - return (pThis->recv_fifo.count <= pThis->recv_fifo.itl) - ? pThis->recv_fifo.itl - pThis->recv_fifo.count : 1; - else - return 0; - } else { - return !(pThis->lsr & UART_LSR_DR); - } -} -static void serial_receive(PDEVSERIAL pThis, const uint8_t *buf, int size) +PDMBOTHCBDECL(void) serialIrqReq(PPDMDEVINS pDevIns, PUARTCORE pUart, unsigned iLUN, int iLvl) { - if (pThis->fcr & UART_FCR_FE) { - int i; - for (i = 0; i < size; i++) { - fifo_put(pThis, RECV_FIFO, buf[i]); - } - pThis->lsr |= UART_LSR_DR; - /* call the timeout receive callback in 4 char transmit time */ - TMTimerSet(pThis->fifo_timeout_timer, TMTimerGet(pThis->fifo_timeout_timer) + pThis->char_transmit_time * 4); - } else { - if (pThis->lsr & UART_LSR_DR) - pThis->lsr |= UART_LSR_OE; - pThis->rbr = buf[0]; - pThis->lsr |= UART_LSR_DR; - } - serial_update_irq(pThis); + RT_NOREF(pUart, iLUN); + PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); + PDMDevHlpISASetIrqNoWait(pDevIns, pThis->uIrq, iLvl); } -/** - * @interface_method_impl{PDMICHARPORT,pfnNotifyRead} - */ -static DECLCALLBACK(int) serialNotifyRead(PPDMICHARPORT pInterface, const void *pvBuf, size_t *pcbRead) -{ - PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ICharPort); - const uint8_t *pu8Buf = (const uint8_t*)pvBuf; - size_t cbRead = *pcbRead; - - PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED); - for (; cbRead > 0; cbRead--, pu8Buf++) - { - if (!serial_can_receive(pThis)) - { - /* If we cannot receive then wait for not more than 250ms. If we still - * cannot receive then the new character will either overwrite rbr - * or it will be dropped at fifo_put(). */ - pThis->fRecvWaiting = true; - PDMCritSectLeave(&pThis->CritSect); - RTSemEventWait(pThis->ReceiveSem, 250); - PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED); - } - serial_receive(pThis, &pu8Buf[0], 1); - } - PDMCritSectLeave(&pThis->CritSect); - return VINF_SUCCESS; -} +/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */ /** - * @@interface_method_impl{PDMICHARPORT,pfnNotifyStatusLinesChanged} + * @callback_method_impl{FNIOMIOPORTOUT} */ -static DECLCALLBACK(int) serialNotifyStatusLinesChanged(PPDMICHARPORT pInterface, uint32_t newStatusLines) +PDMBOTHCBDECL(int) serialIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb) { - PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ICharPort); - uint8_t newMsr = 0; - - Log(("%s: pInterface=%p newStatusLines=%u\n", __FUNCTION__, pInterface, newStatusLines)); - - PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED); - - /* Set new states. */ - if (newStatusLines & PDMICHARPORT_STATUS_LINES_DCD) - newMsr |= UART_MSR_DCD; - if (newStatusLines & PDMICHARPORT_STATUS_LINES_RI) - newMsr |= UART_MSR_RI; - if (newStatusLines & PDMICHARPORT_STATUS_LINES_DSR) - newMsr |= UART_MSR_DSR; - if (newStatusLines & PDMICHARPORT_STATUS_LINES_CTS) - newMsr |= UART_MSR_CTS; - - /* Compare the old and the new states and set the delta bits accordingly. */ - if ((newMsr & UART_MSR_DCD) != (pThis->msr & UART_MSR_DCD)) - newMsr |= UART_MSR_DDCD; - if ((newMsr & UART_MSR_RI) != 0 && (pThis->msr & UART_MSR_RI) == 0) - newMsr |= UART_MSR_TERI; - if ((newMsr & UART_MSR_DSR) != (pThis->msr & UART_MSR_DSR)) - newMsr |= UART_MSR_DDSR; - if ((newMsr & UART_MSR_CTS) != (pThis->msr & UART_MSR_CTS)) - newMsr |= UART_MSR_DCTS; - - pThis->msr = newMsr; - pThis->msr_changed = true; - serial_update_irq(pThis); - - PDMCritSectLeave(&pThis->CritSect); + PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); + RT_NOREF_PV(pvUser); - return VINF_SUCCESS; + return uartRegWrite(&pThis->UartCore, uPort - pThis->PortBase, u32, cb); } /** - * @interface_method_impl{PDMICHARPORT,pfnNotifyBufferFull} - */ -static DECLCALLBACK(int) serialNotifyBufferFull(PPDMICHARPORT pInterface, bool fFull) -{ - RT_NOREF(pInterface, fFull); - return VINF_SUCCESS; -} - - -/** - * @interface_method_impl{PDMICHARPORT,pfnNotifyBreak} + * @callback_method_impl{FNIOMIOPORTIN} */ -static DECLCALLBACK(int) serialNotifyBreak(PPDMICHARPORT pInterface) +PDMBOTHCBDECL(int) serialIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb) { - PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, ICharPort); - - Log(("%s: pInterface=%p\n", __FUNCTION__, pInterface)); - - PDMCritSectEnter(&pThis->CritSect, VERR_PERMISSION_DENIED); - - pThis->lsr |= UART_LSR_BI; - serial_update_irq(pThis); - - PDMCritSectLeave(&pThis->CritSect); + PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); + RT_NOREF_PV(pvUser); - return VINF_SUCCESS; + return uartRegRead(&pThis->UartCore, uPort - pThis->PortBase, pu32, cb); } -/* -=-=-=-=-=-=-=-=- Timer callbacks -=-=-=-=-=-=-=-=- */ +#ifdef IN_RING3 -/** - * @callback_method_impl{FNTMTIMERDEV, Fifo timer function.} - */ -static DECLCALLBACK(void) serialFifoTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser) -{ - RT_NOREF(pDevIns, pTimer); - PDEVSERIAL pThis = (PDEVSERIAL)pvUser; - Assert(PDMCritSectIsOwner(&pThis->CritSect)); - if (pThis->recv_fifo.count) - { - pThis->timeout_ipending = 1; - serial_update_irq(pThis); - } -} /** - * @callback_method_impl{FNTMTIMERDEV, Transmit timer function.} + * Returns the matching UART type from the given string. * - * Just retry to transmit a character. + * @returns UART type based on the given string or UARTTYPE_INVALID if an invalid type was passed. + * @param pszUartType The UART type. */ -static DECLCALLBACK(void) serialTransmitTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser) +static UARTTYPE serialR3GetUartTypeFromString(const char *pszUartType) { - RT_NOREF(pDevIns, pTimer); - PDEVSERIAL pThis = (PDEVSERIAL)pvUser; - Assert(PDMCritSectIsOwner(&pThis->CritSect)); - serial_xmit(pThis, true); -} + if (!RTStrCmp(pszUartType, "16450")) + return UARTTYPE_16450; + else if (!RTStrCmp(pszUartType, "16550A")) + return UARTTYPE_16550A; + else if (!RTStrCmp(pszUartType, "16750")) + return UARTTYPE_16750; -#endif /* IN_RING3 */ - -/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */ - -/** - * @callback_method_impl{FNIOMIOPORTOUT} - */ -PDMBOTHCBDECL(int) serialIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb) -{ - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - int rc; - Assert(PDMCritSectIsOwner(&pThis->CritSect)); - RT_NOREF_PV(pvUser); - - if (cb == 1) - { - Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, uPort, u32)); - rc = serial_ioport_write(pThis, uPort, u32); - } - else - { - AssertMsgFailed(("uPort=%#x cb=%d u32=%#x\n", uPort, cb, u32)); - rc = VINF_SUCCESS; - } - - return rc; + AssertLogRelMsgFailedReturn(("Unknown UART type \"%s\" specified", pszUartType), + UARTTYPE_INVALID); } -/** - * @callback_method_impl{FNIOMIOPORTIN} - */ -PDMBOTHCBDECL(int) serialIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb) -{ - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - int rc; - Assert(PDMCritSectIsOwner(&pThis->CritSect)); - RT_NOREF_PV(pvUser); - - if (cb == 1) - { - *pu32 = serial_ioport_read(pThis, uPort, &rc); - Log2(("%s: port %#06x val %#04x\n", __FUNCTION__, uPort, *pu32)); - } - else - rc = VERR_IOM_IOPORT_UNUSED; - - return rc; -} - -#ifdef IN_RING3 - /* -=-=-=-=-=-=-=-=- Saved State -=-=-=-=-=-=-=-=- */ /** * @callback_method_impl{FNSSMDEVLIVEEXEC} */ -static DECLCALLBACK(int) serialLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass) +static DECLCALLBACK(int) serialR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass) { RT_NOREF(uPass); PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - SSMR3PutS32(pSSM, pThis->irq); - SSMR3PutU32(pSSM, pThis->base); + SSMR3PutU8(pSSM, pThis->uIrq); + SSMR3PutIOPort(pSSM, pThis->PortBase); + SSMR3PutU32(pSSM, pThis->UartCore.enmType); + return VINF_SSM_DONT_CALL_AGAIN; } @@ -918,41 +164,15 @@ static DECLCALLBACK(int) serialLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uin /** * @callback_method_impl{FNSSMDEVSAVEEXEC} */ -static DECLCALLBACK(int) serialSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) +static DECLCALLBACK(int) serialR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) { PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - SSMR3PutU16(pSSM, pThis->divider); - SSMR3PutU8(pSSM, pThis->rbr); - SSMR3PutU8(pSSM, pThis->ier); - SSMR3PutU8(pSSM, pThis->lcr); - SSMR3PutU8(pSSM, pThis->mcr); - SSMR3PutU8(pSSM, pThis->lsr); - SSMR3PutU8(pSSM, pThis->msr); - SSMR3PutU8(pSSM, pThis->scr); - SSMR3PutU8(pSSM, pThis->fcr); /* 16550A */ - SSMR3PutS32(pSSM, pThis->thr_ipending); - SSMR3PutS32(pSSM, pThis->irq); - SSMR3PutS32(pSSM, pThis->last_break_enable); - SSMR3PutU32(pSSM, pThis->base); - SSMR3PutBool(pSSM, pThis->msr_changed); - - /* Version 5, safe everything that might be of importance. Much better than - missing relevant bits! */ - SSMR3PutU8(pSSM, pThis->thr); - SSMR3PutU8(pSSM, pThis->tsr); - SSMR3PutU8(pSSM, pThis->iir); - SSMR3PutS32(pSSM, pThis->timeout_ipending); - TMR3TimerSave(pThis->fifo_timeout_timer, pSSM); - TMR3TimerSave(pThis->transmit_timerR3, pSSM); - SSMR3PutU8(pSSM, pThis->recv_fifo.itl); - SSMR3PutU8(pSSM, pThis->xmit_fifo.itl); - - /* Don't store: - * - the content of the FIFO - * - tsr_retry - */ + SSMR3PutU8( pSSM, pThis->uIrq); + SSMR3PutIOPort(pSSM, pThis->PortBase); + SSMR3PutU32( pSSM, pThis->UartCore.enmType); + uartR3SaveExec(&pThis->UartCore, pSSM); return SSMR3PutU32(pSSM, UINT32_MAX); /* sanity/terminator */ } @@ -960,133 +180,73 @@ static DECLCALLBACK(int) serialSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) /** * @callback_method_impl{FNSSMDEVLOADEXEC} */ -static DECLCALLBACK(int) serialLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass) +static DECLCALLBACK(int) serialR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass) { - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - int32_t iIrq; - uint32_t IOBase; + PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); + uint8_t uIrq; + RTIOPORT PortBase; + UARTTYPE enmType; - AssertMsgReturn(uVersion >= SERIAL_SAVED_STATE_VERSION_16450, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION); + AssertMsgReturn(uVersion >= UART_SAVED_STATE_VERSION_16450, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION); if (uPass != SSM_PASS_FINAL) { - SSMR3GetS32(pSSM, &iIrq); - int rc = SSMR3GetU32(pSSM, &IOBase); + SSMR3GetU8(pSSM, &uIrq); + SSMR3GetIOPort(pSSM, &PortBase); + int rc = SSMR3GetU32(pSSM, (uint32_t *)&enmType); AssertRCReturn(rc, rc); } else { - if (uVersion == SERIAL_SAVED_STATE_VERSION_16450) + int rc = VINF_SUCCESS; + + if (uVersion > UART_SAVED_STATE_VERSION_LEGACY_CODE) { - pThis->f16550AEnabled = false; - LogRel(("Serial#%d: falling back to 16450 mode from load state\n", pDevIns->iInstance)); + SSMR3GetU8( pSSM, &uIrq); + SSMR3GetIOPort(pSSM, &PortBase); + SSMR3GetU32( pSSM, (uint32_t *)&enmType); + rc = uartR3LoadExec(&pThis->UartCore, pSSM, uVersion, uPass, NULL, NULL); } - - SSMR3GetU16(pSSM, &pThis->divider); - SSMR3GetU8(pSSM, &pThis->rbr); - SSMR3GetU8(pSSM, &pThis->ier); - SSMR3GetU8(pSSM, &pThis->lcr); - SSMR3GetU8(pSSM, &pThis->mcr); - SSMR3GetU8(pSSM, &pThis->lsr); - SSMR3GetU8(pSSM, &pThis->msr); - SSMR3GetU8(pSSM, &pThis->scr); - if (uVersion > SERIAL_SAVED_STATE_VERSION_16450) - SSMR3GetU8(pSSM, &pThis->fcr); - SSMR3GetS32(pSSM, &pThis->thr_ipending); - SSMR3GetS32(pSSM, &iIrq); - SSMR3GetS32(pSSM, &pThis->last_break_enable); - SSMR3GetU32(pSSM, &IOBase); - SSMR3GetBool(pSSM, &pThis->msr_changed); - - if (uVersion > SERIAL_SAVED_STATE_VERSION_MISSING_BITS) + else { - SSMR3GetU8(pSSM, &pThis->thr); - SSMR3GetU8(pSSM, &pThis->tsr); - SSMR3GetU8(pSSM, &pThis->iir); - SSMR3GetS32(pSSM, &pThis->timeout_ipending); - TMR3TimerLoad(pThis->fifo_timeout_timer, pSSM); - TMR3TimerLoad(pThis->transmit_timerR3, pSSM); - SSMR3GetU8(pSSM, &pThis->recv_fifo.itl); - SSMR3GetU8(pSSM, &pThis->xmit_fifo.itl); + if (uVersion > UART_SAVED_STATE_VERSION_16450) + enmType = UARTTYPE_16550A; + else + enmType = UARTTYPE_16450; + rc = uartR3LoadExec(&pThis->UartCore, pSSM, uVersion, uPass, &uIrq, &PortBase); } - - /* the marker. */ - uint32_t u32; - int rc = SSMR3GetU32(pSSM, &u32); - if (RT_FAILURE(rc)) - return rc; - AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED); - - if ( (pThis->lsr & UART_LSR_DR) - || pThis->fRecvWaiting) + if (RT_SUCCESS(rc)) { - pThis->fRecvWaiting = false; - rc = RTSemEventSignal(pThis->ReceiveSem); - AssertRC(rc); + /* The marker. */ + uint32_t u32; + rc = SSMR3GetU32(pSSM, &u32); + if (RT_FAILURE(rc)) + return rc; + AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED); } - - /* this isn't strictly necessary but cannot hurt... */ - pThis->pDevInsR3 = pDevIns; - pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns); - pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); } /* * Check the config. */ - if ( pThis->irq != iIrq - || pThis->base != IOBase) + if ( pThis->uIrq != uIrq + || pThis->PortBase != PortBase + || pThis->UartCore.enmType != enmType) return SSMR3SetCfgError(pSSM, RT_SRC_POS, - N_("Config mismatch - saved irq=%#x iobase=%#x; configured irq=%#x iobase=%#x"), - iIrq, IOBase, pThis->irq, pThis->base); + N_("Config mismatch - saved IRQ=%#x PortBase=%#x Type=%d; configured IRQ=%#x PortBase=%#x Type=%d"), + uIrq, PortBase, enmType, pThis->uIrq, pThis->PortBase, pThis->UartCore.enmType); return VINF_SUCCESS; } -#ifdef VBOX_SERIAL_PCI -/* -=-=-=-=-=-=-=-=- PCI Device Callback(s) -=-=-=-=-=-=-=-=- */ - /** - * @callback_method_impl{FNPCIIOREGIONMAP} + * @callback_method_impl{FNSSMDEVLOADDONE} */ -static DECLCALLBACK(int) serialIOPortRegionMap(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion, - RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType) +static DECLCALLBACK(int) serialR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) { - PDEVSERIAL pThis = RT_FROM_MEMBER(pPciDev, DEVSERIAL, PciDev); - int rc = VINF_SUCCESS; - - Assert(enmType == PCI_ADDRESS_SPACE_IO); - Assert(iRegion == 0); - Assert(cb == 8); - AssertMsg(RT_ALIGN(GCPhysAddress, 8) == GCPhysAddress, ("Expected 8 byte alignment. GCPhysAddress=%#x\n", GCPhysAddress)); - - pThis->base = (RTIOPORT)GCPhysAddress; - LogRel(("Serial#%d: mapping I/O at %#06x\n", pThis->pDevIns->iInstance, pThis->base)); - - /* - * Register our port IO handlers. - */ - rc = PDMDevHlpIOPortRegister(pDevIns, (RTIOPORT)GCPhysAddress, 8, (void *)pThis, - serial_io_write, serial_io_read, NULL, NULL, "SERIAL"); - AssertRC(rc); - return rc; -} - -#endif /* VBOX_SERIAL_PCI */ - - -/* -=-=-=-=-=-=-=-=- PDMIBASE on LUN#1 -=-=-=-=-=-=-=-=- */ - -/** - * @interface_method_impl{PDMIBASE,pfnQueryInterface} - */ -static DECLCALLBACK(void *) serialQueryInterface(PPDMIBASE pInterface, const char *pszIID) -{ - PDEVSERIAL pThis = RT_FROM_MEMBER(pInterface, DEVSERIAL, IBase); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARPORT, &pThis->ICharPort); - return NULL; + PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); + return uartR3LoadDone(&pThis->UartCore, pSSM); } @@ -1095,50 +255,20 @@ static DECLCALLBACK(void *) serialQueryInterface(PPDMIBASE pInterface, const cha /** * @interface_method_impl{PDMDEVREG,pfnRelocate} */ -static DECLCALLBACK(void) serialRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta) +static DECLCALLBACK(void) serialR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta) { - RT_NOREF(offDelta); PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); - pThis->transmit_timerRC = TMTimerRCPtr(pThis->transmit_timerR3); + uartR3Relocate(&pThis->UartCore, offDelta); } /** * @interface_method_impl{PDMDEVREG,pfnReset} */ -static DECLCALLBACK(void) serialReset(PPDMDEVINS pDevIns) +static DECLCALLBACK(void) serialR3Reset(PPDMDEVINS pDevIns) { PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - - pThis->rbr = 0; - pThis->ier = 0; - pThis->iir = UART_IIR_NO_INT; - pThis->lcr = 3; /* 8 data bits, no parity, 1 stop bit */ - pThis->lsr = UART_LSR_TEMT | UART_LSR_THRE; - pThis->msr = UART_MSR_DCD | UART_MSR_DSR | UART_MSR_CTS; - /* Default to 9600 baud, 1 start bit, 8 data bits, 1 stop bit, no parity. */ - pThis->divider = 0x0C; - pThis->mcr = UART_MCR_OUT2; - pThis->scr = 0; - pThis->tsr_retry = 0; - uint64_t tf = TMTimerGetFreq(CTX_SUFF(pThis->transmit_timer)); - pThis->char_transmit_time = (tf / 9600) * 10; - serial_tsr_retry_update_parameters(pThis, tf); - - /* Update parameters of the underlying driver to stay in sync. */ - serial_update_parameters(pThis); - - fifo_clear(pThis, RECV_FIFO); - fifo_clear(pThis, XMIT_FIFO); - - pThis->thr_ipending = 0; - pThis->last_break_enable = 0; -# ifdef VBOX_SERIAL_PCI - PDMDevHlpPCISetIrqNoWait(pThis->CTX_SUFF(pDevIns), 0, 0); -# else /* !VBOX_SERIAL_PCI */ - PDMDevHlpISASetIrqNoWait(pThis->CTX_SUFF(pDevIns), pThis->irq, 0); -# endif /* !VBOX_SERIAL_PCI */ + uartR3Reset(&pThis->UartCore); } @@ -1147,30 +277,9 @@ static DECLCALLBACK(void) serialReset(PPDMDEVINS pDevIns) */ static DECLCALLBACK(int) serialR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) { - RT_NOREF(iLUN, fFlags); + RT_NOREF(fFlags); PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - - int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Serial Char"); - if (RT_SUCCESS(rc)) - { - pThis->pDrvChar = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMICHARCONNECTOR); - if (!pThis->pDrvChar) - { - AssertLogRelMsgFailed(("Configuration error: instance %d has no char interface!\n", pDevIns->iInstance)); - return VERR_PDM_MISSING_INTERFACE; - } - } - else if (rc == VERR_PDM_NO_ATTACHED_DRIVER) - { - pThis->pDrvBase = NULL; - pThis->pDrvChar = NULL; - rc = VINF_SUCCESS; - LogRel(("Serial%d: no unit\n", pDevIns->iInstance)); - } - else /* Don't call VMSetError here as we assume that the driver already set an appropriate error */ - LogRel(("Serial%d: Failed to attach to char driver. rc=%Rrc\n", pDevIns->iInstance, rc)); - - return rc; + return uartR3Attach(&pThis->UartCore, iLUN); } @@ -1181,25 +290,19 @@ static DECLCALLBACK(void) serialR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint { RT_NOREF(iLUN, fFlags); PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - - /* Zero out important members. */ - pThis->pDrvBase = NULL; - pThis->pDrvChar = NULL; + uartR3Detach(&pThis->UartCore); } /** * @interface_method_impl{PDMDEVREG,pfnDestruct} */ -static DECLCALLBACK(int) serialDestruct(PPDMDEVINS pDevIns) +static DECLCALLBACK(int) serialR3Destruct(PPDMDEVINS pDevIns) { PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); - RTSemEventDestroy(pThis->ReceiveSem); - pThis->ReceiveSem = NIL_RTSEMEVENT; - - PDMR3CritSectDelete(&pThis->CritSect); + uartR3Destruct(&pThis->UartCore); return VINF_SUCCESS; } @@ -1207,12 +310,10 @@ static DECLCALLBACK(int) serialDestruct(PPDMDEVINS pDevIns) /** * @interface_method_impl{PDMDEVREG,pfnConstruct} */ -static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) +static DECLCALLBACK(int) serialR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) { - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - int rc; - uint16_t io_base; - uint8_t irq_lvl; + PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); + int rc = VINF_SUCCESS; Assert(iInstance < 4); PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); @@ -1224,31 +325,6 @@ static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns, int iInstance, PCFG pThis->pDevInsR3 = pDevIns; pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns); pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); - pThis->ReceiveSem = NIL_RTSEMEVENT; - - /* IBase */ - pThis->IBase.pfnQueryInterface = serialQueryInterface; - - /* ICharPort */ - pThis->ICharPort.pfnNotifyRead = serialNotifyRead; - pThis->ICharPort.pfnNotifyStatusLinesChanged = serialNotifyStatusLinesChanged; - pThis->ICharPort.pfnNotifyBufferFull = serialNotifyBufferFull; - pThis->ICharPort.pfnNotifyBreak = serialNotifyBreak; - -#ifdef VBOX_SERIAL_PCI - /* the PCI device */ - pThis->PciDev.abConfig[0x00] = 0xee; /* Vendor: ??? */ - pThis->PciDev.abConfig[0x01] = 0x80; - pThis->PciDev.abConfig[0x02] = 0x01; /* Device: ??? */ - pThis->PciDev.abConfig[0x03] = 0x01; - pThis->PciDev.abConfig[0x04] = PCI_COMMAND_IOACCESS; - pThis->PciDev.abConfig[0x09] = 0x01; /* Programming interface: 16450 */ - pThis->PciDev.abConfig[0x0a] = 0x00; /* Subclass: Serial controller */ - pThis->PciDev.abConfig[0x0b] = 0x07; /* Class: Communication controller */ - pThis->PciDev.abConfig[0x0e] = 0x00; /* Header type: standard */ - pThis->PciDev.abConfig[0x3c] = irq_lvl; /* preconfigure IRQ number (0 = autoconfig)*/ - pThis->PciDev.abConfig[0x3d] = 1; /* interrupt pin 0 */ -#endif /* VBOX_SERIAL_PCI */ /* * Validate and read the configuration. @@ -1258,14 +334,14 @@ static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns, int iInstance, PCFG "GCEnabled\0" "R0Enabled\0" "YieldOnLSRRead\0" - "Enable16550A\0" + "UartType\0" )) { AssertMsgFailed(("serialConstruct Invalid configuration values\n")); return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES; } - rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fGCEnabled, true); + rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fRCEnabled, true); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"GCEnabled\" value")); @@ -1275,19 +351,21 @@ static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns, int iInstance, PCFG return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"R0Enabled\" value")); - rc = CFGMR3QueryBoolDef(pCfg, "YieldOnLSRRead", &pThis->fYieldOnLSRRead, false); + bool fYieldOnLSRRead = false; + rc = CFGMR3QueryBoolDef(pCfg, "YieldOnLSRRead", &fYieldOnLSRRead, false); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value")); - rc = CFGMR3QueryU8(pCfg, "IRQ", &irq_lvl); + uint8_t uIrq = 0; + rc = CFGMR3QueryU8(pCfg, "IRQ", &uIrq); if (rc == VERR_CFGM_VALUE_NOT_FOUND) { /* Provide sensible defaults. */ if (iInstance == 0) - irq_lvl = 4; + uIrq = 4; else if (iInstance == 1) - irq_lvl = 3; + uIrq = 3; else AssertReleaseFailed(); /* irq_lvl is undefined. */ } @@ -1295,144 +373,100 @@ static DECLCALLBACK(int) serialConstruct(PPDMDEVINS pDevIns, int iInstance, PCFG return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"IRQ\" value")); - rc = CFGMR3QueryU16(pCfg, "IOBase", &io_base); + uint16_t uIoBase = 0; + rc = CFGMR3QueryU16(pCfg, "IOBase", &uIoBase); if (rc == VERR_CFGM_VALUE_NOT_FOUND) { if (iInstance == 0) - io_base = 0x3f8; + uIoBase = 0x3f8; else if (iInstance == 1) - io_base = 0x2f8; + uIoBase = 0x2f8; else - AssertReleaseFailed(); /* io_base is undefined */ + AssertReleaseFailed(); /* uIoBase is undefined */ } else if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"IOBase\" value")); - Log(("DevSerial: instance %d iobase=%04x irq=%d\n", iInstance, io_base, irq_lvl)); - - rc = CFGMR3QueryBoolDef(pCfg, "Enable16550A", &pThis->f16550AEnabled, true); + char *pszUartType; + rc = CFGMR3QueryStringAllocDef(pCfg, "UartType", &pszUartType, "16550A"); if (RT_FAILURE(rc)) return PDMDEV_SET_ERROR(pDevIns, rc, - N_("Configuration error: Failed to get the \"Enable16550A\" value")); + N_("Configuration error: failed to read \"UartType\" as string")); - pThis->irq = irq_lvl; -#ifdef VBOX_SERIAL_PCI - pThis->base = -1; -#else - pThis->base = io_base; -#endif + UARTTYPE enmUartType = serialR3GetUartTypeFromString(pszUartType); - LogRel(("Serial#%d: emulating %s\n", pDevIns->iInstance, pThis->f16550AEnabled ? "16550A" : "16450")); + if (enmUartType != UARTTYPE_INVALID) + LogRel(("Serial#%d: emulating %s (IOBase: %04x IRQ: %u)\n", + pDevIns->iInstance, pszUartType, uIoBase, uIrq)); - /* - * Initialize critical section and the semaphore. Change the default - * critical section to ours so that TM and IOM will enter it before - * calling us. - * - * Note! This must of be done BEFORE creating timers, registering I/O ports - * and other things which might pick up the default CS or end up - * calling back into the device. - */ - rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "Serial#%u", iInstance); - AssertRCReturn(rc, rc); + MMR3HeapFree(pszUartType); - rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect); - AssertRCReturn(rc, rc); + if (enmUartType == UARTTYPE_INVALID) + return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER, + N_("Configuration error: \"UartType\" contains invalid type")); - rc = RTSemEventCreate(&pThis->ReceiveSem); - AssertRCReturn(rc, rc); + pThis->uIrq = uIrq; + pThis->PortBase = uIoBase; /* - * Create the timers. - */ - rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, serialFifoTimer, pThis, - TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "Serial Fifo Timer", - &pThis->fifo_timeout_timer); - AssertRCReturn(rc, rc); - - rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, serialTransmitTimer, pThis, - TMTIMER_FLAGS_DEFAULT_CRIT_SECT, "Serial Transmit Timer", - &pThis->transmit_timerR3); - AssertRCReturn(rc, rc); - pThis->transmit_timerR0 = TMTimerR0Ptr(pThis->transmit_timerR3); - pThis->transmit_timerRC = TMTimerRCPtr(pThis->transmit_timerR3); - -#ifdef VBOX_SERIAL_PCI - /* - * Register the PCI Device and region. + * Init locks, using explicit locking where necessary. */ - rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev); - if (RT_FAILURE(rc)) - return rc; - rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, 8, PCI_ADDRESS_SPACE_IO, serialIOPortRegionMap); + rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns)); if (RT_FAILURE(rc)) return rc; -#else /* !VBOX_SERIAL_PCI */ /* * Register the I/O ports. */ - pThis->base = io_base; - rc = PDMDevHlpIOPortRegister(pDevIns, io_base, 8, 0, - serialIOPortWrite, serialIOPortRead, + rc = PDMDevHlpIOPortRegister(pDevIns, uIoBase, 8, 0, + serialIoPortWrite, serialIoPortRead, NULL, NULL, "SERIAL"); if (RT_FAILURE(rc)) return rc; - if (pThis->fGCEnabled) + PVM pVM = PDMDevHlpGetVM(pDevIns); + RTR0PTR pfnSerialIrqReqR0 = NIL_RTR0PTR; + RTRCPTR pfnSerialIrqReqRC = NIL_RTRCPTR; + + if (pThis->fRCEnabled) { - rc = PDMDevHlpIOPortRegisterRC(pDevIns, io_base, 8, 0, "serialIOPortWrite", - "serialIOPortRead", NULL, NULL, "Serial"); + rc = PDMDevHlpIOPortRegisterRC(pDevIns, uIoBase, 8, 0, "serialIoPortWrite", + "serialIoPortRead", NULL, NULL, "SERIAL"); + if ( RT_SUCCESS(rc) + && VM_IS_RAW_MODE_ENABLED(pVM)) + rc = PDMR3LdrGetSymbolRC(pVM, pDevIns->pReg->szRCMod, "serialIrqReq", &pfnSerialIrqReqRC); + if (RT_FAILURE(rc)) return rc; } if (pThis->fR0Enabled) { - rc = PDMDevHlpIOPortRegisterR0(pDevIns, io_base, 8, 0, "serialIOPortWrite", - "serialIOPortRead", NULL, NULL, "Serial"); + rc = PDMDevHlpIOPortRegisterR0(pDevIns, uIoBase, 8, 0, "serialIoPortWrite", + "serialIoPortRead", NULL, NULL, "SERIAL"); + if (RT_SUCCESS(rc)) + rc = PDMR3LdrGetSymbolR0(pVM, pDevIns->pReg->szR0Mod, "serialIrqReq", &pfnSerialIrqReqR0); + if (RT_FAILURE(rc)) return rc; } -#endif /* !VBOX_SERIAL_PCI */ /* * Saved state. */ - rc = PDMDevHlpSSMRegister3(pDevIns, SERIAL_SAVED_STATE_VERSION, sizeof (*pThis), - serialLiveExec, serialSaveExec, serialLoadExec); + rc = PDMDevHlpSSMRegisterEx(pDevIns, UART_SAVED_STATE_VERSION, sizeof(*pThis), NULL, + NULL, serialR3LiveExec, NULL, + NULL, serialR3SaveExec, NULL, + NULL, serialR3LoadExec, serialR3LoadDone); if (RT_FAILURE(rc)) return rc; - /* - * Attach the char driver and get the interfaces. - */ - rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Serial Char"); - if (RT_SUCCESS(rc)) - { - pThis->pDrvChar = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMICHARCONNECTOR); - if (!pThis->pDrvChar) - { - AssertLogRelMsgFailed(("Configuration error: instance %d has no char interface!\n", iInstance)); - return VERR_PDM_MISSING_INTERFACE; - } - /** @todo provide read notification interface!!!! */ - } - else if (rc == VERR_PDM_NO_ATTACHED_DRIVER) - { - pThis->pDrvBase = NULL; - pThis->pDrvChar = NULL; - LogRel(("Serial%d: no unit\n", iInstance)); - } - else - { - AssertLogRelMsgFailed(("Serial%d: Failed to attach to char driver. rc=%Rrc\n", iInstance, rc)); - /* Don't call VMSetError here as we assume that the driver already set an appropriate error */ - return rc; - } + /* Init the UART core structure. */ + rc = uartR3Init(&pThis->UartCore, pDevIns, enmUartType, 0, + fYieldOnLSRRead ? UART_CORE_YIELD_ON_LSR_READ : 0, serialIrqReq, pfnSerialIrqReqR0, pfnSerialIrqReqRC); - serialReset(pDevIns); + serialR3Reset(pDevIns); return VINF_SUCCESS; } @@ -1461,17 +495,17 @@ const PDMDEVREG g_DeviceSerialPort = /* cbInstance */ sizeof(DEVSERIAL), /* pfnConstruct */ - serialConstruct, + serialR3Construct, /* pfnDestruct */ - serialDestruct, + serialR3Destruct, /* pfnRelocate */ - serialRelocate, + serialR3Relocate, /* pfnMemSetup */ NULL, /* pfnPowerOn */ NULL, /* pfnReset */ - serialReset, + serialR3Reset, /* pfnSuspend */ NULL, /* pfnResume */ diff --git a/src/VBox/Devices/Serial/DevSerialNew.cpp b/src/VBox/Devices/Serial/DevSerialNew.cpp deleted file mode 100644 index b8c90029088..00000000000 --- a/src/VBox/Devices/Serial/DevSerialNew.cpp +++ /dev/null @@ -1,530 +0,0 @@ -/* $Id$ */ -/** @file - * DevSerial - 16550A UART emulation. - * - * The documentation for this device was taken from the PC16550D spec from TI. - */ - -/* - * Copyright (C) 2018 Oracle Corporation - * - * This file is part of VirtualBox Open Source Edition (OSE), as - * available from http://www.virtualbox.org. This file is free software; - * you can redistribute it and/or modify it under the terms of the GNU - * General Public License (GPL) as published by the Free Software - * Foundation, in version 2 as it comes in the "COPYING" file of the - * VirtualBox OSE distribution. VirtualBox OSE is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. - */ - - -/********************************************************************************************************************************* -* Header Files * -*********************************************************************************************************************************/ -#define LOG_GROUP LOG_GROUP_DEV_SERIAL -#include <VBox/vmm/pdmdev.h> -#include <VBox/vmm/pdmserialifs.h> -#include <VBox/vmm/vm.h> -#include <iprt/assert.h> -#include <iprt/uuid.h> -#include <iprt/string.h> -#include <iprt/semaphore.h> -#include <iprt/critsect.h> - -#include "VBoxDD.h" -#include "UartCore.h" - - -/********************************************************************************************************************************* -* Defined Constants And Macros * -*********************************************************************************************************************************/ - - -/********************************************************************************************************************************* -* Structures and Typedefs * -*********************************************************************************************************************************/ - -/** - * Serial device. - */ -typedef struct DEVSERIAL -{ - /** Pointer to the device instance - R3 Ptr. */ - PPDMDEVINSR3 pDevInsR3; - /** Pointer to the device instance - R0 Ptr. */ - PPDMDEVINSR0 pDevInsR0; - /** Pointer to the device instance - RC Ptr. */ - PPDMDEVINSRC pDevInsRC; - /** Alignment. */ - RTRCPTR Alignment0; - /** Flag whether the R0 portion of this device is enabled. */ - bool fR0Enabled; - /** Flag whether the RC portion of this device is enabled. */ - bool fRCEnabled; - /** Alignment. */ - bool afAlignment1[2]; - /** The IRQ value. */ - uint8_t uIrq; - /** The base I/O port the device is registered at. */ - RTIOPORT PortBase; - - /** The UART core. */ - UARTCORE UartCore; -} DEVSERIAL; -/** Pointer to the serial device state. */ -typedef DEVSERIAL *PDEVSERIAL; - -#ifndef VBOX_DEVICE_STRUCT_TESTCASE - - -/********************************************************************************************************************************* -* Global Variables * -*********************************************************************************************************************************/ - - -/********************************************************************************************************************************* -* Internal Functions * -*********************************************************************************************************************************/ - - -PDMBOTHCBDECL(void) serialIrqReq(PPDMDEVINS pDevIns, PUARTCORE pUart, unsigned iLUN, int iLvl) -{ - RT_NOREF(pUart, iLUN); - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - PDMDevHlpISASetIrqNoWait(pDevIns, pThis->uIrq, iLvl); -} - - -/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */ - -/** - * @callback_method_impl{FNIOMIOPORTOUT} - */ -PDMBOTHCBDECL(int) serialIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t u32, unsigned cb) -{ - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - RT_NOREF_PV(pvUser); - - return uartRegWrite(&pThis->UartCore, uPort - pThis->PortBase, u32, cb); -} - - -/** - * @callback_method_impl{FNIOMIOPORTIN} - */ -PDMBOTHCBDECL(int) serialIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT uPort, uint32_t *pu32, unsigned cb) -{ - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - RT_NOREF_PV(pvUser); - - return uartRegRead(&pThis->UartCore, uPort - pThis->PortBase, pu32, cb); -} - - -#ifdef IN_RING3 - - -/** - * Returns the matching UART type from the given string. - * - * @returns UART type based on the given string or UARTTYPE_INVALID if an invalid type was passed. - * @param pszUartType The UART type. - */ -static UARTTYPE serialR3GetUartTypeFromString(const char *pszUartType) -{ - if (!RTStrCmp(pszUartType, "16450")) - return UARTTYPE_16450; - else if (!RTStrCmp(pszUartType, "16550A")) - return UARTTYPE_16550A; - else if (!RTStrCmp(pszUartType, "16750")) - return UARTTYPE_16750; - - AssertLogRelMsgFailedReturn(("Unknown UART type \"%s\" specified", pszUartType), - UARTTYPE_INVALID); -} - - -/* -=-=-=-=-=-=-=-=- Saved State -=-=-=-=-=-=-=-=- */ - -/** - * @callback_method_impl{FNSSMDEVLIVEEXEC} - */ -static DECLCALLBACK(int) serialR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass) -{ - RT_NOREF(uPass); - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - SSMR3PutU8(pSSM, pThis->uIrq); - SSMR3PutIOPort(pSSM, pThis->PortBase); - SSMR3PutU32(pSSM, pThis->UartCore.enmType); - - return VINF_SSM_DONT_CALL_AGAIN; -} - - -/** - * @callback_method_impl{FNSSMDEVSAVEEXEC} - */ -static DECLCALLBACK(int) serialR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) -{ - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - - SSMR3PutU8( pSSM, pThis->uIrq); - SSMR3PutIOPort(pSSM, pThis->PortBase); - SSMR3PutU32( pSSM, pThis->UartCore.enmType); - - uartR3SaveExec(&pThis->UartCore, pSSM); - return SSMR3PutU32(pSSM, UINT32_MAX); /* sanity/terminator */ -} - - -/** - * @callback_method_impl{FNSSMDEVLOADEXEC} - */ -static DECLCALLBACK(int) serialR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass) -{ - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - uint8_t uIrq; - RTIOPORT PortBase; - UARTTYPE enmType; - - AssertMsgReturn(uVersion >= UART_SAVED_STATE_VERSION_16450, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION); - - if (uPass != SSM_PASS_FINAL) - { - SSMR3GetU8(pSSM, &uIrq); - SSMR3GetIOPort(pSSM, &PortBase); - int rc = SSMR3GetU32(pSSM, (uint32_t *)&enmType); - AssertRCReturn(rc, rc); - } - else - { - int rc = VINF_SUCCESS; - - if (uVersion > UART_SAVED_STATE_VERSION_LEGACY_CODE) - { - SSMR3GetU8( pSSM, &uIrq); - SSMR3GetIOPort(pSSM, &PortBase); - SSMR3GetU32( pSSM, (uint32_t *)&enmType); - rc = uartR3LoadExec(&pThis->UartCore, pSSM, uVersion, uPass, NULL, NULL); - } - else - { - if (uVersion > UART_SAVED_STATE_VERSION_16450) - enmType = UARTTYPE_16550A; - else - enmType = UARTTYPE_16450; - rc = uartR3LoadExec(&pThis->UartCore, pSSM, uVersion, uPass, &uIrq, &PortBase); - } - if (RT_SUCCESS(rc)) - { - /* The marker. */ - uint32_t u32; - rc = SSMR3GetU32(pSSM, &u32); - if (RT_FAILURE(rc)) - return rc; - AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED); - } - } - - /* - * Check the config. - */ - if ( pThis->uIrq != uIrq - || pThis->PortBase != PortBase - || pThis->UartCore.enmType != enmType) - return SSMR3SetCfgError(pSSM, RT_SRC_POS, - N_("Config mismatch - saved IRQ=%#x PortBase=%#x Type=%d; configured IRQ=%#x PortBase=%#x Type=%d"), - uIrq, PortBase, enmType, pThis->uIrq, pThis->PortBase, pThis->UartCore.enmType); - - return VINF_SUCCESS; -} - - -/** - * @callback_method_impl{FNSSMDEVLOADDONE} - */ -static DECLCALLBACK(int) serialR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM) -{ - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - return uartR3LoadDone(&pThis->UartCore, pSSM); -} - - -/* -=-=-=-=-=-=-=-=- PDMDEVREG -=-=-=-=-=-=-=-=- */ - -/** - * @interface_method_impl{PDMDEVREG,pfnRelocate} - */ -static DECLCALLBACK(void) serialR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta) -{ - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - uartR3Relocate(&pThis->UartCore, offDelta); -} - - -/** - * @interface_method_impl{PDMDEVREG,pfnReset} - */ -static DECLCALLBACK(void) serialR3Reset(PPDMDEVINS pDevIns) -{ - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - uartR3Reset(&pThis->UartCore); -} - - -/** - * @interface_method_impl{PDMDEVREG,pfnAttach} - */ -static DECLCALLBACK(int) serialR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) -{ - RT_NOREF(fFlags); - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - return uartR3Attach(&pThis->UartCore, iLUN); -} - - -/** - * @interface_method_impl{PDMDEVREG,pfnDetach} - */ -static DECLCALLBACK(void) serialR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags) -{ - RT_NOREF(iLUN, fFlags); - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - uartR3Detach(&pThis->UartCore); -} - - -/** - * @interface_method_impl{PDMDEVREG,pfnDestruct} - */ -static DECLCALLBACK(int) serialR3Destruct(PPDMDEVINS pDevIns) -{ - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns); - - uartR3Destruct(&pThis->UartCore); - return VINF_SUCCESS; -} - - -/** - * @interface_method_impl{PDMDEVREG,pfnConstruct} - */ -static DECLCALLBACK(int) serialR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg) -{ - PDEVSERIAL pThis = PDMINS_2_DATA(pDevIns, PDEVSERIAL); - int rc = VINF_SUCCESS; - - Assert(iInstance < 4); - PDMDEV_CHECK_VERSIONS_RETURN(pDevIns); - - /* - * Initialize the instance data. - * (Do this early or the destructor might choke on something!) - */ - pThis->pDevInsR3 = pDevIns; - pThis->pDevInsR0 = PDMDEVINS_2_R0PTR(pDevIns); - pThis->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns); - - /* - * Validate and read the configuration. - */ - if (!CFGMR3AreValuesValid(pCfg, "IRQ\0" - "IOBase\0" - "GCEnabled\0" - "R0Enabled\0" - "YieldOnLSRRead\0" - "UartType\0" - )) - { - AssertMsgFailed(("serialConstruct Invalid configuration values\n")); - return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES; - } - - rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &pThis->fRCEnabled, true); - if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, - N_("Configuration error: Failed to get the \"GCEnabled\" value")); - - rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &pThis->fR0Enabled, true); - if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, - N_("Configuration error: Failed to get the \"R0Enabled\" value")); - - bool fYieldOnLSRRead = false; - rc = CFGMR3QueryBoolDef(pCfg, "YieldOnLSRRead", &fYieldOnLSRRead, false); - if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, - N_("Configuration error: Failed to get the \"YieldOnLSRRead\" value")); - - uint8_t uIrq = 0; - rc = CFGMR3QueryU8(pCfg, "IRQ", &uIrq); - if (rc == VERR_CFGM_VALUE_NOT_FOUND) - { - /* Provide sensible defaults. */ - if (iInstance == 0) - uIrq = 4; - else if (iInstance == 1) - uIrq = 3; - else - AssertReleaseFailed(); /* irq_lvl is undefined. */ - } - else if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, - N_("Configuration error: Failed to get the \"IRQ\" value")); - - uint16_t uIoBase = 0; - rc = CFGMR3QueryU16(pCfg, "IOBase", &uIoBase); - if (rc == VERR_CFGM_VALUE_NOT_FOUND) - { - if (iInstance == 0) - uIoBase = 0x3f8; - else if (iInstance == 1) - uIoBase = 0x2f8; - else - AssertReleaseFailed(); /* uIoBase is undefined */ - } - else if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, - N_("Configuration error: Failed to get the \"IOBase\" value")); - - char *pszUartType; - rc = CFGMR3QueryStringAllocDef(pCfg, "UartType", &pszUartType, "16550A"); - if (RT_FAILURE(rc)) - return PDMDEV_SET_ERROR(pDevIns, rc, - N_("Configuration error: failed to read \"UartType\" as string")); - - UARTTYPE enmUartType = serialR3GetUartTypeFromString(pszUartType); - - if (enmUartType != UARTTYPE_INVALID) - LogRel(("Serial#%d: emulating %s (IOBase: %04x IRQ: %u)\n", - pDevIns->iInstance, pszUartType, uIoBase, uIrq)); - - MMR3HeapFree(pszUartType); - - if (enmUartType == UARTTYPE_INVALID) - return PDMDEV_SET_ERROR(pDevIns, VERR_INVALID_PARAMETER, - N_("Configuration error: \"UartType\" contains invalid type")); - - pThis->uIrq = uIrq; - pThis->PortBase = uIoBase; - - /* - * Init locks, using explicit locking where necessary. - */ - rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns)); - if (RT_FAILURE(rc)) - return rc; - - /* - * Register the I/O ports. - */ - rc = PDMDevHlpIOPortRegister(pDevIns, uIoBase, 8, 0, - serialIoPortWrite, serialIoPortRead, - NULL, NULL, "SERIAL"); - if (RT_FAILURE(rc)) - return rc; - - PVM pVM = PDMDevHlpGetVM(pDevIns); - RTR0PTR pfnSerialIrqReqR0 = NIL_RTR0PTR; - RTRCPTR pfnSerialIrqReqRC = NIL_RTRCPTR; - - if (pThis->fRCEnabled) - { - rc = PDMDevHlpIOPortRegisterRC(pDevIns, uIoBase, 8, 0, "serialIoPortWrite", - "serialIoPortRead", NULL, NULL, "SERIAL"); - if ( RT_SUCCESS(rc) - && VM_IS_RAW_MODE_ENABLED(pVM)) - rc = PDMR3LdrGetSymbolRC(pVM, pDevIns->pReg->szRCMod, "serialIrqReq", &pfnSerialIrqReqRC); - - if (RT_FAILURE(rc)) - return rc; - } - - if (pThis->fR0Enabled) - { - rc = PDMDevHlpIOPortRegisterR0(pDevIns, uIoBase, 8, 0, "serialIoPortWrite", - "serialIoPortRead", NULL, NULL, "SERIAL"); - if (RT_SUCCESS(rc)) - rc = PDMR3LdrGetSymbolR0(pVM, pDevIns->pReg->szR0Mod, "serialIrqReq", &pfnSerialIrqReqR0); - - if (RT_FAILURE(rc)) - return rc; - } - - /* - * Saved state. - */ - rc = PDMDevHlpSSMRegisterEx(pDevIns, UART_SAVED_STATE_VERSION, sizeof(*pThis), NULL, - NULL, serialR3LiveExec, NULL, - NULL, serialR3SaveExec, NULL, - NULL, serialR3LoadExec, serialR3LoadDone); - if (RT_FAILURE(rc)) - return rc; - - /* Init the UART core structure. */ - rc = uartR3Init(&pThis->UartCore, pDevIns, enmUartType, 0, - fYieldOnLSRRead ? UART_CORE_YIELD_ON_LSR_READ : 0, serialIrqReq, pfnSerialIrqReqR0, pfnSerialIrqReqRC); - - serialR3Reset(pDevIns); - return VINF_SUCCESS; -} - - -/** - * The device registration structure. - */ -const PDMDEVREG g_DeviceSerialPort = -{ - /* u32Version */ - PDM_DEVREG_VERSION, - /* szName */ - "serial", - /* szRCMod */ - "VBoxDDRC.rc", - /* szR0Mod */ - "VBoxDDR0.r0", - /* pszDescription */ - "Serial Communication Port", - /* fFlags */ - PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RC | PDM_DEVREG_FLAGS_R0, - /* fClass */ - PDM_DEVREG_CLASS_SERIAL, - /* cMaxInstances */ - UINT32_MAX, - /* cbInstance */ - sizeof(DEVSERIAL), - /* pfnConstruct */ - serialR3Construct, - /* pfnDestruct */ - serialR3Destruct, - /* pfnRelocate */ - serialR3Relocate, - /* pfnMemSetup */ - NULL, - /* pfnPowerOn */ - NULL, - /* pfnReset */ - serialR3Reset, - /* pfnSuspend */ - NULL, - /* pfnResume */ - NULL, - /* pfnAttach */ - serialR3Attach, - /* pfnDetach */ - serialR3Detach, - /* pfnQueryInterface. */ - NULL, - /* pfnInitComplete */ - NULL, - /* pfnPowerOff */ - NULL, - /* pfnSoftReset */ - NULL, - /* u32VersionEnd */ - PDM_DEVREG_VERSION -}; -#endif /* IN_RING3 */ - -#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */ diff --git a/src/VBox/Devices/Serial/DrvChar.cpp b/src/VBox/Devices/Serial/DrvChar.cpp index 42ca8ce4830..3f3b314f0b0 100644 --- a/src/VBox/Devices/Serial/DrvChar.cpp +++ b/src/VBox/Devices/Serial/DrvChar.cpp @@ -1,16 +1,10 @@ /* $Id$ */ /** @file - * Driver that adapts PDMISTREAM into PDMICHARCONNECTOR / PDMICHARPORT. - * - * Converts synchronous calls (PDMICHARCONNECTOR::pfnWrite, PDMISTREAM::pfnRead) - * into asynchronous ones. - * - * Note that we don't use a send buffer here to be able to handle - * dropping of bytes for xmit at device level. + * Driver that adapts PDMISTREAM into PDMISERIALCONNECTOR / PDMISERIALPORT. */ /* - * Copyright (C) 2006-2017 Oracle Corporation + * Copyright (C) 2006-2018 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -27,6 +21,7 @@ *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_DRV_CHAR #include <VBox/vmm/pdmdrv.h> +#include <VBox/vmm/pdmserialifs.h> #include <iprt/asm.h> #include <iprt/assert.h> #include <iprt/poll.h> @@ -49,37 +44,31 @@ /** * Char driver instance data. * - * @implements PDMICHARCONNECTOR + * @implements PDMISERIALCONNECTOR */ typedef struct DRVCHAR { /** Pointer to the driver instance structure. */ PPDMDRVINS pDrvIns; /** Pointer to the char port interface of the driver/device above us. */ - PPDMICHARPORT pDrvCharPort; + PPDMISERIALPORT pDrvSerialPort; /** Pointer to the stream interface of the driver below us. */ PPDMISTREAM pDrvStream; - /** Our char interface. */ - PDMICHARCONNECTOR ICharConnector; + /** Our serial interface. */ + PDMISERIALCONNECTOR ISerialConnector; /** Flag to notify the receive thread it should terminate. */ volatile bool fShutdown; + /** Flag whether data is available from the device/driver above as notified by the driver. */ + volatile bool fAvailWrExt; + /** Internal copy of the flag which gets reset when there is no data anymore. */ + bool fAvailWrInt; /** I/O thread. */ PPDMTHREAD pThrdIo; - /** Thread to relay read data to the device above without - * blocking send operations. - * @todo: This has to go but needs changes in the interface - * between device and driver. - */ - PPDMTHREAD pThrdRead; - /** Event semaphore for the read relay thread. */ - RTSEMEVENT hEvtSemRead; - /** Critical section protection the send part. */ - RTCRITSECT CritSectSend; - /** Internal send FIFO queue */ - uint8_t volatile u8SendByte; - bool volatile fSending; - uint8_t Alignment[2]; + /** Small send buffer. */ + uint8_t abTxBuf[16]; + /** Amount of data in the buffer. */ + size_t cbTxUsed; /** Receive buffer. */ uint8_t abBuffer[256]; @@ -108,52 +97,99 @@ static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, const ch PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARCONNECTOR, &pThis->ICharConnector); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALCONNECTOR, &pThis->ISerialConnector); return NULL; } -/* -=-=-=-=- ICharConnector -=-=-=-=- */ +/* -=-=-=-=- ISerialConnector -=-=-=-=- */ + /** - * @interface_method_impl{PDMICHARCONNECTOR,pfnWrite} + * @interface_method_impl{PDMISERIALCONNECTOR,pfnDataAvailWrNotify} */ -static DECLCALLBACK(int) drvCharWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite) +static DECLCALLBACK(int) drvCharDataAvailWrNotify(PPDMISERIALCONNECTOR pInterface) { - PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ICharConnector); - const char *pbBuffer = (const char *)pvBuf; + LogFlowFunc(("pInterface=%#p\n", pInterface)); + PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ISerialConnector); + int rc = VINF_SUCCESS; + bool fAvailOld = ASMAtomicXchgBool(&pThis->fAvailWrExt, true); + if (!fAvailOld) + rc = pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream); - LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite)); + return rc; +} - RTCritSectEnter(&pThis->CritSectSend); - for (uint32_t i = 0; i < cbWrite && RT_SUCCESS(rc); i++) - { - if (!ASMAtomicXchgBool(&pThis->fSending, true)) - { - pThis->u8SendByte = pbBuffer[i]; - pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream); - STAM_COUNTER_INC(&pThis->StatBytesWritten); - } - else - rc = VERR_BUFFER_OVERFLOW; - } +/** + * @interface_method_impl{PDMISERIALCONNECTOR,pfnReadRdr} + */ +static DECLCALLBACK(int) drvCharReadRdr(PPDMISERIALCONNECTOR pInterface, void *pvBuf, size_t cbRead, size_t *pcbRead) +{ + LogFlowFunc(("pInterface=%#p pvBuf=%#p cbRead=%zu pcbRead=%#p\n", pInterface, pvBuf, cbRead, pcbRead)); + PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ISerialConnector); + int rc = VINF_SUCCESS; - RTCritSectLeave(&pThis->CritSectSend); + AssertReturn(pThis->cbRemaining, VERR_INVALID_STATE); + size_t cbToRead = RT_MIN(cbRead, pThis->cbRemaining); + memcpy(pvBuf, pThis->pbBuf, cbToRead); + + pThis->pbBuf += cbToRead; + *pcbRead = cbToRead; + size_t cbOld = ASMAtomicSubZ(&pThis->cbRemaining, cbToRead); + if (!(cbOld - cbToRead)) /* Kick the I/O thread to fetch new data. */ + rc = pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream); + STAM_COUNTER_ADD(&pThis->StatBytesRead, cbToRead); + + LogFlowFunc(("-> %Rrc\n", rc)); return rc; } /** - * @interface_method_impl{PDMICHARCONNECTOR,pfnSetParameters} + * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgParams} + */ +static DECLCALLBACK(int) drvCharChgParams(PPDMISERIALCONNECTOR pInterface, uint32_t uBps, + PDMSERIALPARITY enmParity, unsigned cDataBits, + PDMSERIALSTOPBITS enmStopBits) +{ + /* Nothing to do here. */ + RT_NOREF(pInterface, uBps, enmParity, cDataBits, enmStopBits); + return VINF_SUCCESS; +} + + +/** + * @callback_method_impl{PDMISERIALCONNECTOR,pfnChgModemLines} + */ +static DECLCALLBACK(int) drvCharChgModemLines(PPDMISERIALCONNECTOR pInterface, bool fRts, bool fDtr) +{ + /* Nothing to do here. */ + RT_NOREF(pInterface, fRts, fDtr); + return VINF_SUCCESS; +} + + +/** + * @callback_method_impl{PDMISERIALCONNECTOR,pfnChgBrk} */ -static DECLCALLBACK(int) drvCharSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, - unsigned cDataBits, unsigned cStopBits) +static DECLCALLBACK(int) drvCharChgBrk(PPDMISERIALCONNECTOR pInterface, bool fBrk) { - RT_NOREF(pInterface, Bps, chParity, cDataBits, cStopBits); + /* Nothing to do here. */ + RT_NOREF(pInterface, fBrk); + return VINF_SUCCESS; +} - LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits)); + +/** + * @callback_method_impl{PDMISERIALCONNECTOR,pfnQueryStsLines} + */ +static DECLCALLBACK(int) drvCharQueryStsLines(PPDMISERIALCONNECTOR pInterface, uint32_t *pfStsLines) +{ + /* Nothing to do here. */ + *pfStsLines = 0; + RT_NOREF(pInterface); return VINF_SUCCESS; } @@ -179,10 +215,14 @@ static DECLCALLBACK(int) drvCharIoLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) { uint32_t fEvts = 0; + if (!pThis->fAvailWrInt) + pThis->fAvailWrInt = ASMAtomicXchgBool(&pThis->fAvailWrExt, false); + if ( !pThis->cbRemaining && pThis->pDrvStream->pfnRead) fEvts |= RTPOLL_EVT_READ; - if (pThis->fSending) + if ( pThis->fAvailWrInt + || pThis->cbTxUsed) fEvts |= RTPOLL_EVT_WRITE; uint32_t fEvtsRecv = 0; @@ -191,32 +231,46 @@ static DECLCALLBACK(int) drvCharIoLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) { if (fEvtsRecv & RTPOLL_EVT_WRITE) { - RTCritSectEnter(&pThis->CritSectSend); - Assert(pThis->fSending); - - size_t cbProcessed = 1; - uint8_t ch = pThis->u8SendByte; - rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &ch, &cbProcessed); - if (RT_SUCCESS(rc)) + if (pThis->fAvailWrInt) { - ASMAtomicXchgBool(&pThis->fSending, false); - Assert(cbProcessed == 1); + /* Stuff as much data into the TX buffer as we can. */ + size_t cbToFetch = RT_ELEMENTS(pThis->abTxBuf) - pThis->cbTxUsed; + size_t cbFetched = 0; + rc = pThis->pDrvSerialPort->pfnReadWr(pThis->pDrvSerialPort, &pThis->abTxBuf[pThis->cbTxUsed], cbToFetch, + &cbFetched); + AssertRC(rc); + + if (cbFetched > 0) + pThis->cbTxUsed += cbFetched; + else + { + /* There is no data available anymore. */ + pThis->fAvailWrInt = false; + } } - else if (rc == VERR_TIMEOUT) - { - /* Normal case, just means that the stream didn't accept a new - * character before the timeout elapsed. Just retry. */ - /* do not change the rc status here, otherwise the (rc == VERR_TIMEOUT) branch - * in the wait above will never get executed */ - /* rc = VINF_SUCCESS; */ - } - else + if (pThis->cbTxUsed) { - LogRel(("Write failed with %Rrc; skipping\n", rc)); - break; + size_t cbProcessed = pThis->cbTxUsed; + rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &pThis->abTxBuf[0], &cbProcessed); + if (RT_SUCCESS(rc)) + { + pThis->cbTxUsed -= cbProcessed; + if (pThis->cbTxUsed) + { + /* Move the data in the TX buffer to the front to fill the end again. */ + memmove(&pThis->abTxBuf[0], &pThis->abTxBuf[cbProcessed], pThis->cbTxUsed); + } + else + pThis->pDrvSerialPort->pfnDataSentNotify(pThis->pDrvSerialPort); + STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbProcessed); + } + else if (rc != VERR_TIMEOUT) + { + LogRel(("Char#%d: Write failed with %Rrc; skipping\n", pDrvIns->iInstance, rc)); + break; + } } - RTCritSectLeave(&pThis->CritSectSend); } if (fEvtsRecv & RTPOLL_EVT_READ) @@ -231,9 +285,14 @@ static DECLCALLBACK(int) drvCharIoLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) LogFlow(("Read failed with %Rrc\n", rc)); break; } - pThis->pbBuf = &pThis->abBuffer[0]; - ASMAtomicWriteZ(&pThis->cbRemaining, cbRead); - RTSemEventSignal(pThis->hEvtSemRead); /* Wakeup relay thread to continue. */ + + if (cbRead) + { + pThis->pbBuf = &pThis->abBuffer[0]; + ASMAtomicWriteZ(&pThis->cbRemaining, cbRead); + /* Notify the upper device/driver. */ + rc = pThis->pDrvSerialPort->pfnDataAvailRdrNotify(pThis->pDrvSerialPort, cbRead); + } } } else if (rc != VERR_INTERRUPTED) @@ -260,123 +319,21 @@ static DECLCALLBACK(int) drvCharIoLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThr } -static DECLCALLBACK(int) drvCharReadRelayLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) -{ - RT_NOREF(pDrvIns); - PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser; - - if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) - return VINF_SUCCESS; - - int rc = VINF_SUCCESS; - while (pThread->enmState == PDMTHREADSTATE_RUNNING) - { - size_t cbRem = ASMAtomicReadZ(&pThis->cbRemaining); - - /* Block as long as there is nothing to relay. */ - if (!pThis->cbRemaining) - rc = RTSemEventWait(pThis->hEvtSemRead, RT_INDEFINITE_WAIT); - - if (pThread->enmState != PDMTHREADSTATE_RUNNING) - break; - - cbRem = ASMAtomicReadZ(&pThis->cbRemaining); - if (cbRem) - { - /* Send data to guest. */ - size_t cbProcessed = cbRem; - rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pThis->pbBuf, &cbProcessed); - if (RT_SUCCESS(rc)) - { - Assert(cbProcessed); - pThis->pbBuf += cbProcessed; - - /* Wake up the I/o thread so it can read new data to process. */ - cbRem = ASMAtomicSubZ(&pThis->cbRemaining, cbProcessed); - if (cbRem == cbProcessed) - pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream); - STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed); - } - else if (rc == VERR_TIMEOUT) - { - /* Normal case, just means that the guest didn't accept a new - * character before the timeout elapsed. Just retry. */ - rc = VINF_SUCCESS; - } - else - { - LogFlow(("NotifyRead failed with %Rrc\n", rc)); - break; - } - } - } - - return VINF_SUCCESS; -} - - -/** - * Unblock the read relay worker thread so it can respond to a state change. - * - * @returns VBox status code. - * @param pDrvIns The char driver instance. - * @param pThread The worker thread. - */ -static DECLCALLBACK(int) drvCharReadRelayLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) -{ - PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser; - - RT_NOREF(pDrvIns); - return RTSemEventSignal(pThis->hEvtSemRead); -} - - -/** - * @callback_method_impl{PDMICHARCONNECTOR,pfnSetModemLines} - */ -static DECLCALLBACK(int) drvCharSetModemLines(PPDMICHARCONNECTOR pInterface, bool fRequestToSend, bool fDataTerminalReady) -{ - /* Nothing to do here. */ - RT_NOREF(pInterface, fRequestToSend, fDataTerminalReady); - return VINF_SUCCESS; -} - - -/** - * @callback_method_impl{PDMICHARCONNECTOR,pfnSetBreak} - */ -static DECLCALLBACK(int) drvCharSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak) -{ - /* Nothing to do here. */ - RT_NOREF(pInterface, fBreak); - return VINF_SUCCESS; -} - - /* -=-=-=-=- driver interface -=-=-=-=- */ /** - * Destruct a char driver instance. - * - * Most VM resources are freed by the VM. This callback is provided so that - * any non-VM resources can be freed correctly. - * - * @param pDrvIns The driver instance data. + * @interface_method_impl{PDMDEVREG,pfnReset} */ -static DECLCALLBACK(void) drvCharDestruct(PPDMDRVINS pDrvIns) +static DECLCALLBACK(void) drvCharReset(PPDMDRVINS pDrvIns) { - PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR); - LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance)); PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); + PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR); - if (RTCritSectIsInitialized(&pThis->CritSectSend)) - RTCritSectDelete(&pThis->CritSectSend); - - if (pThis->hEvtSemRead != NIL_RTSEMEVENT) - { - RTSemEventDestroy(pThis->hEvtSemRead); - pThis->hEvtSemRead = NIL_RTSEMEVENT; - } + /* Reset TX and RX buffers. */ + pThis->fAvailWrExt = false; + pThis->fAvailWrInt = false; + pThis->cbTxUsed = 0; + pThis->cbRemaining = 0; } @@ -395,61 +352,48 @@ static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, ui /* * Init basic data members and interfaces. */ - pThis->pDrvIns = pDrvIns; - pThis->pThrdIo = NIL_RTTHREAD; - pThis->pThrdRead = NIL_RTTHREAD; - pThis->hEvtSemRead = NIL_RTSEMEVENT; + pThis->pDrvIns = pDrvIns; + pThis->pThrdIo = NIL_RTTHREAD; /* IBase. */ - pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface; - /* ICharConnector. */ - pThis->ICharConnector.pfnWrite = drvCharWrite; - pThis->ICharConnector.pfnSetParameters = drvCharSetParameters; - pThis->ICharConnector.pfnSetModemLines = drvCharSetModemLines; - pThis->ICharConnector.pfnSetBreak = drvCharSetBreak; - - int rc = RTCritSectInit(&pThis->CritSectSend); - if (RT_FAILURE(rc)) - return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, - N_("Char#%d: Failed to create critical section"), pDrvIns->iInstance); + pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface; + /* ISerialConnector. */ + pThis->ISerialConnector.pfnDataAvailWrNotify = drvCharDataAvailWrNotify; + pThis->ISerialConnector.pfnReadRdr = drvCharReadRdr; + pThis->ISerialConnector.pfnChgParams = drvCharChgParams; + pThis->ISerialConnector.pfnChgModemLines = drvCharChgModemLines; + pThis->ISerialConnector.pfnChgBrk = drvCharChgBrk; + pThis->ISerialConnector.pfnQueryStsLines = drvCharQueryStsLines; /* - * Get the ICharPort interface of the above driver/device. + * Get the ISerialPort interface of the above driver/device. */ - pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT); - if (!pThis->pDrvCharPort) - return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("Char#%d has no char port interface above"), pDrvIns->iInstance); + pThis->pDrvSerialPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISERIALPORT); + if (!pThis->pDrvSerialPort) + return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, + N_("Char#%d has no serial port interface above"), pDrvIns->iInstance); /* * Attach driver below and query its stream interface. */ PPDMIBASE pBase; - rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase); + int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase); if (RT_FAILURE(rc)) return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */ pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM); if (!pThis->pDrvStream) - return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, N_("Char#%d has no stream interface below"), pDrvIns->iInstance); - - /* Don't start the receive relay thread if reading is not supported. */ - if (pThis->pDrvStream->pfnRead) - { - rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pThrdRead, pThis, drvCharReadRelayLoop, - drvCharReadRelayLoopWakeup, 0, RTTHREADTYPE_IO, "CharReadRel"); - if (RT_FAILURE(rc)) - return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create read relay thread"), pDrvIns->iInstance); - - rc = RTSemEventCreate(&pThis->hEvtSemRead); - AssertRCReturn(rc, rc); - } + return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, + N_("Char#%d has no stream interface below"), pDrvIns->iInstance); rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pThrdIo, pThis, drvCharIoLoop, drvCharIoLoopWakeup, 0, RTTHREADTYPE_IO, "CharIo"); if (RT_FAILURE(rc)) - return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create send thread"), pDrvIns->iInstance); + return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create I/O thread"), pDrvIns->iInstance); - PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance); - PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance); + PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, + "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance); + PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, + "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance); return VINF_SUCCESS; } @@ -481,7 +425,7 @@ const PDMDRVREG g_DrvChar = /* pfnConstruct */ drvCharConstruct, /* pfnDestruct */ - drvCharDestruct, + NULL, /* pfnRelocate */ NULL, /* pfnIOCtl */ @@ -489,7 +433,7 @@ const PDMDRVREG g_DrvChar = /* pfnPowerOn */ NULL, /* pfnReset */ - NULL, + drvCharReset, /* pfnSuspend */ NULL, /* pfnResume */ diff --git a/src/VBox/Devices/Serial/DrvCharNew.cpp b/src/VBox/Devices/Serial/DrvCharNew.cpp deleted file mode 100644 index 3f3b314f0b0..00000000000 --- a/src/VBox/Devices/Serial/DrvCharNew.cpp +++ /dev/null @@ -1,451 +0,0 @@ -/* $Id$ */ -/** @file - * Driver that adapts PDMISTREAM into PDMISERIALCONNECTOR / PDMISERIALPORT. - */ - -/* - * Copyright (C) 2006-2018 Oracle Corporation - * - * This file is part of VirtualBox Open Source Edition (OSE), as - * available from http://www.virtualbox.org. This file is free software; - * you can redistribute it and/or modify it under the terms of the GNU - * General Public License (GPL) as published by the Free Software - * Foundation, in version 2 as it comes in the "COPYING" file of the - * VirtualBox OSE distribution. VirtualBox OSE is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. - */ - - -/********************************************************************************************************************************* -* Header Files * -*********************************************************************************************************************************/ -#define LOG_GROUP LOG_GROUP_DRV_CHAR -#include <VBox/vmm/pdmdrv.h> -#include <VBox/vmm/pdmserialifs.h> -#include <iprt/asm.h> -#include <iprt/assert.h> -#include <iprt/poll.h> -#include <iprt/stream.h> -#include <iprt/critsect.h> -#include <iprt/semaphore.h> -#include <iprt/uuid.h> - -#include "VBoxDD.h" - - -/********************************************************************************************************************************* -* Defined Constants And Macros * -*********************************************************************************************************************************/ - - -/********************************************************************************************************************************* -* Structures and Typedefs * -*********************************************************************************************************************************/ -/** - * Char driver instance data. - * - * @implements PDMISERIALCONNECTOR - */ -typedef struct DRVCHAR -{ - /** Pointer to the driver instance structure. */ - PPDMDRVINS pDrvIns; - /** Pointer to the char port interface of the driver/device above us. */ - PPDMISERIALPORT pDrvSerialPort; - /** Pointer to the stream interface of the driver below us. */ - PPDMISTREAM pDrvStream; - /** Our serial interface. */ - PDMISERIALCONNECTOR ISerialConnector; - /** Flag to notify the receive thread it should terminate. */ - volatile bool fShutdown; - /** Flag whether data is available from the device/driver above as notified by the driver. */ - volatile bool fAvailWrExt; - /** Internal copy of the flag which gets reset when there is no data anymore. */ - bool fAvailWrInt; - /** I/O thread. */ - PPDMTHREAD pThrdIo; - - /** Small send buffer. */ - uint8_t abTxBuf[16]; - /** Amount of data in the buffer. */ - size_t cbTxUsed; - - /** Receive buffer. */ - uint8_t abBuffer[256]; - /** Number of bytes remaining in the receive buffer. */ - volatile size_t cbRemaining; - /** Current position into the read buffer. */ - uint8_t *pbBuf; - - /** Read/write statistics */ - STAMCOUNTER StatBytesRead; - STAMCOUNTER StatBytesWritten; -} DRVCHAR, *PDRVCHAR; -AssertCompileMemberAlignment(DRVCHAR, StatBytesRead, 8); - - - - -/* -=-=-=-=- IBase -=-=-=-=- */ - -/** - * @interface_method_impl{PDMIBASE,pfnQueryInterface} - */ -static DECLCALLBACK(void *) drvCharQueryInterface(PPDMIBASE pInterface, const char *pszIID) -{ - PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); - PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR); - - PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALCONNECTOR, &pThis->ISerialConnector); - return NULL; -} - - -/* -=-=-=-=- ISerialConnector -=-=-=-=- */ - - -/** - * @interface_method_impl{PDMISERIALCONNECTOR,pfnDataAvailWrNotify} - */ -static DECLCALLBACK(int) drvCharDataAvailWrNotify(PPDMISERIALCONNECTOR pInterface) -{ - LogFlowFunc(("pInterface=%#p\n", pInterface)); - PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ISerialConnector); - - int rc = VINF_SUCCESS; - bool fAvailOld = ASMAtomicXchgBool(&pThis->fAvailWrExt, true); - if (!fAvailOld) - rc = pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream); - - return rc; -} - - -/** - * @interface_method_impl{PDMISERIALCONNECTOR,pfnReadRdr} - */ -static DECLCALLBACK(int) drvCharReadRdr(PPDMISERIALCONNECTOR pInterface, void *pvBuf, size_t cbRead, size_t *pcbRead) -{ - LogFlowFunc(("pInterface=%#p pvBuf=%#p cbRead=%zu pcbRead=%#p\n", pInterface, pvBuf, cbRead, pcbRead)); - PDRVCHAR pThis = RT_FROM_MEMBER(pInterface, DRVCHAR, ISerialConnector); - int rc = VINF_SUCCESS; - - AssertReturn(pThis->cbRemaining, VERR_INVALID_STATE); - size_t cbToRead = RT_MIN(cbRead, pThis->cbRemaining); - memcpy(pvBuf, pThis->pbBuf, cbToRead); - - pThis->pbBuf += cbToRead; - *pcbRead = cbToRead; - size_t cbOld = ASMAtomicSubZ(&pThis->cbRemaining, cbToRead); - if (!(cbOld - cbToRead)) /* Kick the I/O thread to fetch new data. */ - rc = pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream); - STAM_COUNTER_ADD(&pThis->StatBytesRead, cbToRead); - - LogFlowFunc(("-> %Rrc\n", rc)); - return rc; -} - - -/** - * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgParams} - */ -static DECLCALLBACK(int) drvCharChgParams(PPDMISERIALCONNECTOR pInterface, uint32_t uBps, - PDMSERIALPARITY enmParity, unsigned cDataBits, - PDMSERIALSTOPBITS enmStopBits) -{ - /* Nothing to do here. */ - RT_NOREF(pInterface, uBps, enmParity, cDataBits, enmStopBits); - return VINF_SUCCESS; -} - - -/** - * @callback_method_impl{PDMISERIALCONNECTOR,pfnChgModemLines} - */ -static DECLCALLBACK(int) drvCharChgModemLines(PPDMISERIALCONNECTOR pInterface, bool fRts, bool fDtr) -{ - /* Nothing to do here. */ - RT_NOREF(pInterface, fRts, fDtr); - return VINF_SUCCESS; -} - - -/** - * @callback_method_impl{PDMISERIALCONNECTOR,pfnChgBrk} - */ -static DECLCALLBACK(int) drvCharChgBrk(PPDMISERIALCONNECTOR pInterface, bool fBrk) -{ - /* Nothing to do here. */ - RT_NOREF(pInterface, fBrk); - return VINF_SUCCESS; -} - - -/** - * @callback_method_impl{PDMISERIALCONNECTOR,pfnQueryStsLines} - */ -static DECLCALLBACK(int) drvCharQueryStsLines(PPDMISERIALCONNECTOR pInterface, uint32_t *pfStsLines) -{ - /* Nothing to do here. */ - *pfStsLines = 0; - RT_NOREF(pInterface); - return VINF_SUCCESS; -} - - -/* -=-=-=-=- I/O thread -=-=-=-=- */ - -/** - * Send thread loop - pushes data down thru the driver chain. - * - * @returns VBox status code. - * @param pDrvIns The char driver instance. - * @param pThread The worker thread. - */ -static DECLCALLBACK(int) drvCharIoLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) -{ - RT_NOREF(pDrvIns); - PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser; - - if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) - return VINF_SUCCESS; - - while (pThread->enmState == PDMTHREADSTATE_RUNNING) - { - uint32_t fEvts = 0; - - if (!pThis->fAvailWrInt) - pThis->fAvailWrInt = ASMAtomicXchgBool(&pThis->fAvailWrExt, false); - - if ( !pThis->cbRemaining - && pThis->pDrvStream->pfnRead) - fEvts |= RTPOLL_EVT_READ; - if ( pThis->fAvailWrInt - || pThis->cbTxUsed) - fEvts |= RTPOLL_EVT_WRITE; - - uint32_t fEvtsRecv = 0; - int rc = pThis->pDrvStream->pfnPoll(pThis->pDrvStream, fEvts, &fEvtsRecv, RT_INDEFINITE_WAIT); - if (RT_SUCCESS(rc)) - { - if (fEvtsRecv & RTPOLL_EVT_WRITE) - { - if (pThis->fAvailWrInt) - { - /* Stuff as much data into the TX buffer as we can. */ - size_t cbToFetch = RT_ELEMENTS(pThis->abTxBuf) - pThis->cbTxUsed; - size_t cbFetched = 0; - rc = pThis->pDrvSerialPort->pfnReadWr(pThis->pDrvSerialPort, &pThis->abTxBuf[pThis->cbTxUsed], cbToFetch, - &cbFetched); - AssertRC(rc); - - if (cbFetched > 0) - pThis->cbTxUsed += cbFetched; - else - { - /* There is no data available anymore. */ - pThis->fAvailWrInt = false; - } - } - - if (pThis->cbTxUsed) - { - size_t cbProcessed = pThis->cbTxUsed; - rc = pThis->pDrvStream->pfnWrite(pThis->pDrvStream, &pThis->abTxBuf[0], &cbProcessed); - if (RT_SUCCESS(rc)) - { - pThis->cbTxUsed -= cbProcessed; - if (pThis->cbTxUsed) - { - /* Move the data in the TX buffer to the front to fill the end again. */ - memmove(&pThis->abTxBuf[0], &pThis->abTxBuf[cbProcessed], pThis->cbTxUsed); - } - else - pThis->pDrvSerialPort->pfnDataSentNotify(pThis->pDrvSerialPort); - STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbProcessed); - } - else if (rc != VERR_TIMEOUT) - { - LogRel(("Char#%d: Write failed with %Rrc; skipping\n", pDrvIns->iInstance, rc)); - break; - } - } - } - - if (fEvtsRecv & RTPOLL_EVT_READ) - { - AssertPtr(pThis->pDrvStream->pfnRead); - Assert(!pThis->cbRemaining); - - size_t cbRead = sizeof(pThis->abBuffer); - rc = pThis->pDrvStream->pfnRead(pThis->pDrvStream, &pThis->abBuffer[0], &cbRead); - if (RT_FAILURE(rc)) - { - LogFlow(("Read failed with %Rrc\n", rc)); - break; - } - - if (cbRead) - { - pThis->pbBuf = &pThis->abBuffer[0]; - ASMAtomicWriteZ(&pThis->cbRemaining, cbRead); - /* Notify the upper device/driver. */ - rc = pThis->pDrvSerialPort->pfnDataAvailRdrNotify(pThis->pDrvSerialPort, cbRead); - } - } - } - else if (rc != VERR_INTERRUPTED) - LogRelMax(10, ("Char#%d: Polling failed with %Rrc\n", pDrvIns->iInstance, rc)); - } - - return VINF_SUCCESS; -} - - -/** - * Unblock the send worker thread so it can respond to a state change. - * - * @returns VBox status code. - * @param pDrvIns The char driver instance. - * @param pThread The worker thread. - */ -static DECLCALLBACK(int) drvCharIoLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) -{ - PDRVCHAR pThis = (PDRVCHAR)pThread->pvUser; - - RT_NOREF(pDrvIns); - return pThis->pDrvStream->pfnPollInterrupt(pThis->pDrvStream); -} - - -/* -=-=-=-=- driver interface -=-=-=-=- */ - -/** - * @interface_method_impl{PDMDEVREG,pfnReset} - */ -static DECLCALLBACK(void) drvCharReset(PPDMDRVINS pDrvIns) -{ - PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); - PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR); - - /* Reset TX and RX buffers. */ - pThis->fAvailWrExt = false; - pThis->fAvailWrInt = false; - pThis->cbTxUsed = 0; - pThis->cbRemaining = 0; -} - - -/** - * Construct a char driver instance. - * - * @copydoc FNPDMDRVCONSTRUCT - */ -static DECLCALLBACK(int) drvCharConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) -{ - RT_NOREF(pCfg); - PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); - PDRVCHAR pThis = PDMINS_2_DATA(pDrvIns, PDRVCHAR); - LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance)); - - /* - * Init basic data members and interfaces. - */ - pThis->pDrvIns = pDrvIns; - pThis->pThrdIo = NIL_RTTHREAD; - /* IBase. */ - pDrvIns->IBase.pfnQueryInterface = drvCharQueryInterface; - /* ISerialConnector. */ - pThis->ISerialConnector.pfnDataAvailWrNotify = drvCharDataAvailWrNotify; - pThis->ISerialConnector.pfnReadRdr = drvCharReadRdr; - pThis->ISerialConnector.pfnChgParams = drvCharChgParams; - pThis->ISerialConnector.pfnChgModemLines = drvCharChgModemLines; - pThis->ISerialConnector.pfnChgBrk = drvCharChgBrk; - pThis->ISerialConnector.pfnQueryStsLines = drvCharQueryStsLines; - - /* - * Get the ISerialPort interface of the above driver/device. - */ - pThis->pDrvSerialPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISERIALPORT); - if (!pThis->pDrvSerialPort) - return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, - N_("Char#%d has no serial port interface above"), pDrvIns->iInstance); - - /* - * Attach driver below and query its stream interface. - */ - PPDMIBASE pBase; - int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBase); - if (RT_FAILURE(rc)) - return rc; /* Don't call PDMDrvHlpVMSetError here as we assume that the driver already set an appropriate error */ - pThis->pDrvStream = PDMIBASE_QUERY_INTERFACE(pBase, PDMISTREAM); - if (!pThis->pDrvStream) - return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_BELOW, RT_SRC_POS, - N_("Char#%d has no stream interface below"), pDrvIns->iInstance); - - rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pThrdIo, pThis, drvCharIoLoop, - drvCharIoLoopWakeup, 0, RTTHREADTYPE_IO, "CharIo"); - if (RT_FAILURE(rc)) - return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Char#%d cannot create I/O thread"), pDrvIns->iInstance); - - - PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, - "Nr of bytes written", "/Devices/Char%d/Written", pDrvIns->iInstance); - PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, - "Nr of bytes read", "/Devices/Char%d/Read", pDrvIns->iInstance); - - return VINF_SUCCESS; -} - - -/** - * Char driver registration record. - */ -const PDMDRVREG g_DrvChar = -{ - /* u32Version */ - PDM_DRVREG_VERSION, - /* szName */ - "Char", - /* szRCMod */ - "", - /* szR0Mod */ - "", - /* pszDescription */ - "Generic char driver.", - /* fFlags */ - PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT, - /* fClass. */ - PDM_DRVREG_CLASS_CHAR, - /* cMaxInstances */ - ~0U, - /* cbInstance */ - sizeof(DRVCHAR), - /* pfnConstruct */ - drvCharConstruct, - /* pfnDestruct */ - NULL, - /* pfnRelocate */ - NULL, - /* pfnIOCtl */ - NULL, - /* pfnPowerOn */ - NULL, - /* pfnReset */ - drvCharReset, - /* pfnSuspend */ - NULL, - /* pfnResume */ - NULL, - /* pfnAttach */ - NULL, - /* pfnDetach */ - NULL, - /* pfnPowerOff */ - NULL, - /* pfnSoftReset */ - NULL, - /* u32EndVersion */ - PDM_DRVREG_VERSION -}; diff --git a/src/VBox/Devices/Serial/DrvHostSerial.cpp b/src/VBox/Devices/Serial/DrvHostSerial.cpp index ae16a3acd19..ea5c670066f 100644 --- a/src/VBox/Devices/Serial/DrvHostSerial.cpp +++ b/src/VBox/Devices/Serial/DrvHostSerial.cpp @@ -1,10 +1,10 @@ /* $Id$ */ /** @file - * VBox stream I/O devices: Host serial driver + * VBox serial devices: Host serial driver */ /* - * Copyright (C) 2006-2017 Oracle Corporation + * Copyright (C) 2006-2018 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -22,6 +22,7 @@ *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_DRV_HOST_SERIAL #include <VBox/vmm/pdm.h> +#include <VBox/vmm/pdmserialifs.h> #include <VBox/err.h> #include <VBox/log.h> @@ -32,42 +33,7 @@ #include <iprt/pipe.h> #include <iprt/semaphore.h> #include <iprt/uuid.h> - -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) -# include <errno.h> -# ifdef RT_OS_SOLARIS -# include <sys/termios.h> -# else -# include <termios.h> -# endif -# include <sys/types.h> -# include <fcntl.h> -# include <string.h> -# include <unistd.h> -# ifdef RT_OS_DARWIN -# include <sys/select.h> -# else -# include <sys/poll.h> -# endif -# include <sys/ioctl.h> -# include <pthread.h> - -# ifdef RT_OS_LINUX -/* - * TIOCM_LOOP is not defined in the above header files for some reason but in asm/termios.h. - * But inclusion of this file however leads to compilation errors because of redefinition of some - * structs. That's why it is defined here until a better solution is found. - */ -# ifndef TIOCM_LOOP -# define TIOCM_LOOP 0x8000 -# endif -/* For linux custom baudrate code we also need serial_struct */ -# include <linux/serial.h> -# endif /* linux */ - -#elif defined(RT_OS_WINDOWS) -# include <iprt/win/windows.h> -#endif +#include <iprt/serialport.h> #include "VBoxDD.h" @@ -79,76 +45,128 @@ /** * Char driver instance data. * - * @implements PDMICHARCONNECTOR + * @implements PDMISERIALCONNECTOR */ typedef struct DRVHOSTSERIAL { /** Pointer to the driver instance structure. */ PPDMDRVINS pDrvIns; - /** Pointer to the char port interface of the driver/device above us. */ - PPDMICHARPORT pDrvCharPort; - /** Our char interface. */ - PDMICHARCONNECTOR ICharConnector; - /** Receive thread. */ - PPDMTHREAD pRecvThread; - /** Send thread. */ - PPDMTHREAD pSendThread; - /** Status lines monitor thread. */ - PPDMTHREAD pMonitorThread; - /** Send event semaphore */ - RTSEMEVENT SendSem; - + /** Pointer to the serial port interface of the driver/device above us. */ + PPDMISERIALPORT pDrvSerialPort; + /** Our serial interface. */ + PDMISERIALCONNECTOR ISerialConnector; + /** I/O thread. */ + PPDMTHREAD pIoThrd; + /** The serial port handle. */ + RTSERIALPORT hSerialPort; /** the device path */ char *pszDevicePath; -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - /** the device handle */ - RTFILE hDeviceFile; -# ifdef RT_OS_DARWIN - /** The device handle used for reading. - * Used to prevent the read select from blocking the writes. */ - RTFILE hDeviceFileR; -# endif - /** The read end of the control pipe */ - RTPIPE hWakeupPipeR; - /** The write end of the control pipe */ - RTPIPE hWakeupPipeW; - /** The current line status. - * Used by the polling version of drvHostSerialMonitorThread. */ - int fStatusLines; -#elif defined(RT_OS_WINDOWS) - /** the device handle */ - HANDLE hDeviceFile; - /** The event semaphore for waking up the receive thread */ - HANDLE hHaltEventSem; - /** The event semaphore for overlapped receiving */ - HANDLE hEventRecv; - /** For overlapped receiving */ - OVERLAPPED overlappedRecv; - /** The event semaphore for overlapped sending */ - HANDLE hEventSend; - /** For overlapped sending */ - OVERLAPPED overlappedSend; -#endif - - /** Internal send FIFO queue */ - uint8_t volatile u8SendByte; - bool volatile fSending; - uint8_t Alignment[2]; + /** Flag whether data is available from the device/driver above as notified by the driver. */ + volatile bool fAvailWrExt; + /** Internal copy of the flag which gets reset when there is no data anymore. */ + bool fAvailWrInt; + /** Small send buffer. */ + uint8_t abTxBuf[16]; + /** Amount of data in the buffer. */ + size_t cbTxUsed; + + /** The read queue. */ + uint8_t abReadBuf[256]; + /** Current offset to write to next. */ + volatile uint32_t offWrite; + /** Current offset into the read buffer. */ + volatile uint32_t offRead; + /** Current amount of data in the buffer. */ + volatile size_t cbReadBuf; /** Read/write statistics */ STAMCOUNTER StatBytesRead; STAMCOUNTER StatBytesWritten; -#ifdef RT_OS_DARWIN - /** The number of bytes we've dropped because the send queue - * was full. */ - STAMCOUNTER StatSendOverflows; -#endif } DRVHOSTSERIAL, *PDRVHOSTSERIAL; -/** Converts a pointer to DRVCHAR::ICharConnector to a PDRVHOSTSERIAL. */ -#define PDMICHAR_2_DRVHOSTSERIAL(pInterface) RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ICharConnector) +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ + + +/** + * Returns number of bytes free in the read buffer and pointer to the start of the free space + * in the read buffer. + * + * @returns Number of bytes free in the buffer. + * @param pThis The host serial driver instance. + * @param ppv Where to return the pointer if there is still free space. + */ +DECLINLINE(size_t) drvHostSerialReadBufGetWrite(PDRVHOSTSERIAL pThis, void **ppv) +{ + if (ppv) + *ppv = &pThis->abReadBuf[pThis->offWrite]; + + size_t cbFree = sizeof(pThis->abReadBuf) - ASMAtomicReadZ(&pThis->cbReadBuf); + if (cbFree) + cbFree = RT_MIN(cbFree, sizeof(pThis->abReadBuf) - pThis->offWrite); + + return cbFree; +} + + +/** + * Returns number of bytes used in the read buffer and pointer to the next byte to read. + * + * @returns Number of bytes free in the buffer. + * @param pThis The host serial driver instance. + * @param ppv Where to return the pointer to the next data to read. + */ +DECLINLINE(size_t) drvHostSerialReadBufGetRead(PDRVHOSTSERIAL pThis, void **ppv) +{ + if (ppv) + *ppv = &pThis->abReadBuf[pThis->offRead]; + + size_t cbUsed = ASMAtomicReadZ(&pThis->cbReadBuf); + if (cbUsed) + cbUsed = RT_MIN(cbUsed, sizeof(pThis->abReadBuf) - pThis->offRead); + + return cbUsed; +} + + +/** + * Advances the write position of the read buffer by the given amount of bytes. + * + * @returns nothing. + * @param pThis The host serial driver instance. + * @param cbAdv Number of bytes to advance. + */ +DECLINLINE(void) drvHostSerialReadBufWriteAdv(PDRVHOSTSERIAL pThis, size_t cbAdv) +{ + uint32_t offWrite = ASMAtomicReadU32(&pThis->offWrite); + offWrite = (offWrite + cbAdv) % sizeof(pThis->abReadBuf); + ASMAtomicWriteU32(&pThis->offWrite, offWrite); + ASMAtomicAddZ(&pThis->cbReadBuf, cbAdv); +} + + +/** + * Advances the read position of the read buffer by the given amount of bytes. + * + * @returns nothing. + * @param pThis The host serial driver instance. + * @param cbAdv Number of bytes to advance. + */ +DECLINLINE(void) drvHostSerialReadBufReadAdv(PDRVHOSTSERIAL pThis, size_t cbAdv) +{ + uint32_t offRead = ASMAtomicReadU32(&pThis->offRead); + offRead = (offRead + cbAdv) % sizeof(pThis->abReadBuf); + ASMAtomicWriteU32(&pThis->offRead, offRead); + ASMAtomicSubZ(&pThis->cbReadBuf, cbAdv); +} /* -=-=-=-=- IBase -=-=-=-=- */ @@ -162,930 +180,344 @@ static DECLCALLBACK(void *) drvHostSerialQueryInterface(PPDMIBASE pInterface, co PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMICHARCONNECTOR, &pThis->ICharConnector); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALCONNECTOR, &pThis->ISerialConnector); return NULL; } -/* -=-=-=-=- ICharConnector -=-=-=-=- */ +/* -=-=-=-=- ISerialConnector -=-=-=-=- */ -/** @interface_method_impl{PDMICHARCONNECTOR,pfnWrite} */ -static DECLCALLBACK(int) drvHostSerialWrite(PPDMICHARCONNECTOR pInterface, const void *pvBuf, size_t cbWrite) +/** @interface_method_impl{PDMISERIALCONNECTOR,pfnDataAvailWrNotify} */ +static DECLCALLBACK(int) drvHostSerialDataAvailWrNotify(PPDMISERIALCONNECTOR pInterface) { - PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface); - const uint8_t *pbBuffer = (const uint8_t *)pvBuf; - - LogFlow(("%s: pvBuf=%#p cbWrite=%d\n", __FUNCTION__, pvBuf, cbWrite)); + PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); - for (uint32_t i = 0; i < cbWrite; i++) - { - if (ASMAtomicXchgBool(&pThis->fSending, true)) - return VERR_BUFFER_OVERFLOW; + int rc = VINF_SUCCESS; + bool fAvailOld = ASMAtomicXchgBool(&pThis->fAvailWrExt, true); + if (!fAvailOld) + rc = RTSerialPortEvtPollInterrupt(pThis->hSerialPort); - pThis->u8SendByte = pbBuffer[i]; - RTSemEventSignal(pThis->SendSem); - STAM_COUNTER_INC(&pThis->StatBytesWritten); - } - return VINF_SUCCESS; + return rc; } -static DECLCALLBACK(int) drvHostSerialSetParameters(PPDMICHARCONNECTOR pInterface, unsigned Bps, char chParity, unsigned cDataBits, unsigned cStopBits) + +/** + * @interface_method_impl{PDMISERIALCONNECTOR,pfnReadRdr} + */ +static DECLCALLBACK(int) drvHostSerialReadRdr(PPDMISERIALCONNECTOR pInterface, void *pvBuf, + size_t cbRead, size_t *pcbRead) { - PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface); -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - struct termios *termiosSetup; - int baud_rate; -#elif defined(RT_OS_WINDOWS) - LPDCB comSetup; -#endif + PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); + int rc = VINF_SUCCESS; + uint8_t *pbDst = (uint8_t *)pvBuf; + size_t cbReadAll = 0; - LogFlow(("%s: Bps=%u chParity=%c cDataBits=%u cStopBits=%u\n", __FUNCTION__, Bps, chParity, cDataBits, cStopBits)); + do + { + void *pvSrc = NULL; + size_t cbThisRead = RT_MIN(drvHostSerialReadBufGetRead(pThis, &pvSrc), cbRead); + if (cbThisRead) + { + memcpy(pbDst, pvSrc, cbThisRead); + cbRead -= cbThisRead; + pbDst += cbThisRead; + cbReadAll += cbThisRead; + drvHostSerialReadBufReadAdv(pThis, cbThisRead); + } + else + break; + } while (cbRead > 0); -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - termiosSetup = (struct termios *)RTMemTmpAllocZ(sizeof(struct termios)); + *pcbRead = cbReadAll; + /* Kick the I/O thread if there is nothing to read to recalculate the poll flags. */ + if (!drvHostSerialReadBufGetRead(pThis, NULL)) + rc = RTSerialPortEvtPollInterrupt(pThis->hSerialPort); - /* Enable receiver */ - termiosSetup->c_cflag |= (CLOCAL | CREAD); + STAM_COUNTER_ADD(&pThis->StatBytesRead, cbReadAll); + return rc; +} - switch (Bps) - { - case 50: - baud_rate = B50; - break; - case 75: - baud_rate = B75; - break; - case 110: - baud_rate = B110; - break; - case 134: - baud_rate = B134; - break; - case 150: - baud_rate = B150; - break; - case 200: - baud_rate = B200; - break; - case 300: - baud_rate = B300; - break; - case 600: - baud_rate = B600; - break; - case 1200: - baud_rate = B1200; - break; - case 1800: - baud_rate = B1800; - break; - case 2400: - baud_rate = B2400; - break; - case 4800: - baud_rate = B4800; - break; - case 9600: - baud_rate = B9600; - break; - case 19200: - baud_rate = B19200; - break; - case 38400: - baud_rate = B38400; - break; - case 57600: - baud_rate = B57600; - break; - case 115200: - baud_rate = B115200; - break; - default: -#ifdef RT_OS_LINUX - struct serial_struct serialStruct; - if (ioctl(RTFileToNative(pThis->hDeviceFile), TIOCGSERIAL, &serialStruct) != -1) - { - serialStruct.custom_divisor = serialStruct.baud_base / Bps; - if (!serialStruct.custom_divisor) - serialStruct.custom_divisor = 1; - serialStruct.flags &= ~ASYNC_SPD_MASK; - serialStruct.flags |= ASYNC_SPD_CUST; - ioctl(RTFileToNative(pThis->hDeviceFile), TIOCSSERIAL, &serialStruct); - baud_rate = B38400; - } - else - baud_rate = B9600; -#else /* !RT_OS_LINUX */ - baud_rate = B9600; -#endif /* !RT_OS_LINUX */ - } - cfsetispeed(termiosSetup, baud_rate); - cfsetospeed(termiosSetup, baud_rate); +/** + * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgParams} + */ +static DECLCALLBACK(int) drvHostSerialChgParams(PPDMISERIALCONNECTOR pInterface, uint32_t uBps, + PDMSERIALPARITY enmParity, unsigned cDataBits, + PDMSERIALSTOPBITS enmStopBits) +{ + PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); + RTSERIALPORTCFG Cfg; + + Cfg.uBaudRate = uBps; - switch (chParity) + switch (enmParity) { - case 'E': - termiosSetup->c_cflag |= PARENB; + case PDMSERIALPARITY_EVEN: + Cfg.enmParity = RTSERIALPORTPARITY_EVEN; break; - case 'O': - termiosSetup->c_cflag |= (PARENB | PARODD); + case PDMSERIALPARITY_ODD: + Cfg.enmParity = RTSERIALPORTPARITY_ODD; break; - case 'N': + case PDMSERIALPARITY_NONE: + Cfg.enmParity = RTSERIALPORTPARITY_NONE; break; - default: + case PDMSERIALPARITY_MARK: + Cfg.enmParity = RTSERIALPORTPARITY_MARK; + break; + case PDMSERIALPARITY_SPACE: + Cfg.enmParity = RTSERIALPORTPARITY_SPACE; break; + default: + AssertMsgFailed(("Unsupported parity setting %d\n", enmParity)); /* Should not happen. */ + Cfg.enmParity = RTSERIALPORTPARITY_NONE; } switch (cDataBits) { case 5: - termiosSetup->c_cflag |= CS5; + Cfg.enmDataBitCount = RTSERIALPORTDATABITS_5BITS; break; case 6: - termiosSetup->c_cflag |= CS6; + Cfg.enmDataBitCount = RTSERIALPORTDATABITS_6BITS; break; case 7: - termiosSetup->c_cflag |= CS7; + Cfg.enmDataBitCount = RTSERIALPORTDATABITS_7BITS; break; case 8: - termiosSetup->c_cflag |= CS8; + Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS; break; default: - break; + AssertMsgFailed(("Unsupported data bit count %u\n", cDataBits)); /* Should not happen. */ + Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS; } - switch (cStopBits) + switch (enmStopBits) { - case 2: - termiosSetup->c_cflag |= CSTOPB; - default: + case PDMSERIALSTOPBITS_ONE: + Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE; break; - } - - /* set serial port to raw input */ - termiosSetup->c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ECHOK | ISIG | IEXTEN); - - tcsetattr(RTFileToNative(pThis->hDeviceFile), TCSANOW, termiosSetup); - RTMemTmpFree(termiosSetup); - -#ifdef RT_OS_LINUX - /* - * XXX In Linux, if a thread calls tcsetattr while the monitor thread is - * waiting in ioctl for a modem status change then 8250.c wrongly disables - * modem irqs and so the monitor thread never gets released. The workaround - * is to send a signal after each tcsetattr. - */ - if (RT_LIKELY(pThis->pMonitorThread != NULL)) - RTThreadPoke(pThis->pMonitorThread->Thread); -#endif - -#elif defined(RT_OS_WINDOWS) - comSetup = (LPDCB)RTMemTmpAllocZ(sizeof(DCB)); - - comSetup->DCBlength = sizeof(DCB); - - switch (Bps) - { - case 110: - comSetup->BaudRate = CBR_110; - break; - case 300: - comSetup->BaudRate = CBR_300; - break; - case 600: - comSetup->BaudRate = CBR_600; + case PDMSERIALSTOPBITS_ONEPOINTFIVE: + Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE; break; - case 1200: - comSetup->BaudRate = CBR_1200; - break; - case 2400: - comSetup->BaudRate = CBR_2400; - break; - case 4800: - comSetup->BaudRate = CBR_4800; - break; - case 9600: - comSetup->BaudRate = CBR_9600; - break; - case 14400: - comSetup->BaudRate = CBR_14400; - break; - case 19200: - comSetup->BaudRate = CBR_19200; - break; - case 38400: - comSetup->BaudRate = CBR_38400; - break; - case 57600: - comSetup->BaudRate = CBR_57600; - break; - case 115200: - comSetup->BaudRate = CBR_115200; + case PDMSERIALSTOPBITS_TWO: + Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_TWO; break; default: - comSetup->BaudRate = CBR_9600; + AssertMsgFailed(("Unsupported stop bit count %d\n", enmStopBits)); /* Should not happen. */ + Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE; } - comSetup->fBinary = TRUE; - comSetup->fOutxCtsFlow = FALSE; - comSetup->fOutxDsrFlow = FALSE; - comSetup->fDtrControl = DTR_CONTROL_DISABLE; - comSetup->fDsrSensitivity = FALSE; - comSetup->fTXContinueOnXoff = TRUE; - comSetup->fOutX = FALSE; - comSetup->fInX = FALSE; - comSetup->fErrorChar = FALSE; - comSetup->fNull = FALSE; - comSetup->fRtsControl = RTS_CONTROL_DISABLE; - comSetup->fAbortOnError = FALSE; - comSetup->wReserved = 0; - comSetup->XonLim = 5; - comSetup->XoffLim = 5; - comSetup->ByteSize = cDataBits; - - switch (chParity) - { - case 'E': - comSetup->Parity = EVENPARITY; - break; - case 'O': - comSetup->Parity = ODDPARITY; - break; - case 'N': - comSetup->Parity = NOPARITY; - break; - default: - break; - } - - switch (cStopBits) - { - case 1: - comSetup->StopBits = ONESTOPBIT; - break; - case 2: - comSetup->StopBits = TWOSTOPBITS; - break; - default: - break; - } - - comSetup->XonChar = 0; - comSetup->XoffChar = 0; - comSetup->ErrorChar = 0; - comSetup->EofChar = 0; - comSetup->EvtChar = 0; - - SetCommState(pThis->hDeviceFile, comSetup); - RTMemTmpFree(comSetup); -#endif /* RT_OS_WINDOWS */ - - return VINF_SUCCESS; + return RTSerialPortCfgSet(pThis->hSerialPort, &Cfg, NULL); } -/* -=-=-=-=- receive thread -=-=-=-=- */ /** - * Send thread loop. - * - * @returns VINF_SUCCESS. - * @param pDrvIns PDM driver instance data. - * @param pThread The PDM thread data. + * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgModemLines} */ -static DECLCALLBACK(int) drvHostSerialSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) +static DECLCALLBACK(int) drvHostSerialChgModemLines(PPDMISERIALCONNECTOR pInterface, bool fRts, bool fDtr) { - PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); - - if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) - return VINF_SUCCESS; - -#ifdef RT_OS_WINDOWS - /* Make sure that the halt event semaphore is reset. */ - DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0); + PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); - HANDLE haWait[2]; - haWait[0] = pThis->hEventSend; - haWait[1] = pThis->hHaltEventSem; -#endif + uint32_t fClear = 0; + uint32_t fSet = 0; - while (pThread->enmState == PDMTHREADSTATE_RUNNING) - { - int rc = RTSemEventWait(pThis->SendSem, RT_INDEFINITE_WAIT); - AssertRCBreak(rc); + if (fRts) + fSet |= RTSERIALPORT_CHG_STS_LINES_F_RTS; + else + fClear |= RTSERIALPORT_CHG_STS_LINES_F_RTS; - /* - * Write the character to the host device. - */ - while (pThread->enmState == PDMTHREADSTATE_RUNNING) - { - /* copy the send queue so we get a linear buffer with the maximal size. */ - uint8_t ch = pThis->u8SendByte; -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) + if (fDtr) + fSet |= RTSERIALPORT_CHG_STS_LINES_F_DTR; + else + fClear |= RTSERIALPORT_CHG_STS_LINES_F_DTR; - size_t cbWritten; - rc = RTFileWrite(pThis->hDeviceFile, &ch, 1, &cbWritten); - if (rc == VERR_TRY_AGAIN) - cbWritten = 0; - if (cbWritten < 1 && (RT_SUCCESS(rc) || rc == VERR_TRY_AGAIN)) - { - /* ok, block till the device is ready for more (O_NONBLOCK) effect. */ - rc = VINF_SUCCESS; - while (pThread->enmState == PDMTHREADSTATE_RUNNING) - { - /* wait */ - fd_set WrSet; - FD_ZERO(&WrSet); - FD_SET(RTFileToNative(pThis->hDeviceFile), &WrSet); - fd_set XcptSet; - FD_ZERO(&XcptSet); - FD_SET(RTFileToNative(pThis->hDeviceFile), &XcptSet); -# ifdef DEBUG - uint64_t u64Now = RTTimeMilliTS(); -# endif - rc = select(RTFileToNative(pThis->hDeviceFile) + 1, NULL, &WrSet, &XcptSet, NULL); - /** @todo check rc? */ - -# ifdef DEBUG - Log2(("select wait for %dms\n", RTTimeMilliTS() - u64Now)); -# endif - /* try write more */ - rc = RTFileWrite(pThis->hDeviceFile, &ch, 1, &cbWritten); - if (rc == VERR_TRY_AGAIN) - cbWritten = 0; - else if (RT_FAILURE(rc)) - break; - else if (cbWritten >= 1) - break; - rc = VINF_SUCCESS; - } /* wait/write loop */ - } + return RTSerialPortChgStatusLines(pThis->hSerialPort, fClear, fSet); +} -#elif defined(RT_OS_WINDOWS) - /* perform an overlapped write operation. */ - DWORD cbWritten; - memset(&pThis->overlappedSend, 0, sizeof(pThis->overlappedSend)); - pThis->overlappedSend.hEvent = pThis->hEventSend; - if (!WriteFile(pThis->hDeviceFile, &ch, 1, &cbWritten, &pThis->overlappedSend)) - { - dwRet = GetLastError(); - if (dwRet == ERROR_IO_PENDING) - { - /* - * write blocked, wait for completion or wakeup... - */ - dwRet = WaitForMultipleObjects(2, haWait, FALSE, INFINITE); - if (dwRet != WAIT_OBJECT_0) - { - AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event semaphore is set but the thread is still in running state\n")); - break; - } - } - else - rc = RTErrConvertFromWin32(dwRet); - } -#endif /* RT_OS_WINDOWS */ - if (RT_FAILURE(rc)) - { - LogRel(("HostSerial#%d: Serial Write failed with %Rrc; terminating send thread\n", pDrvIns->iInstance, rc)); - return rc; - } - ASMAtomicXchgBool(&pThis->fSending, false); - break; - } /* write loop */ - } +/** + * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgBrk} + */ +static DECLCALLBACK(int) drvHostSerialChgBrk(PPDMISERIALCONNECTOR pInterface, bool fBrk) +{ + PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); - return VINF_SUCCESS; + return RTSerialPortChgBreakCondition(pThis->hSerialPort, fBrk); } + /** - * Unblock the send thread so it can respond to a state change. - * - * @returns a VBox status code. - * @param pDrvIns The driver instance. - * @param pThread The send thread. + * @interface_method_impl{PDMISERIALCONNECTOR,pfnQueryStsLines} */ -static DECLCALLBACK(int) drvHostSerialWakeupSendThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) +static DECLCALLBACK(int) drvHostSerialQueryStsLines(PPDMISERIALCONNECTOR pInterface, uint32_t *pfStsLines) { - RT_NOREF(pThread); - PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); - int rc; - - rc = RTSemEventSignal(pThis->SendSem); - if (RT_FAILURE(rc)) - return rc; - -#ifdef RT_OS_WINDOWS - if (!SetEvent(pThis->hHaltEventSem)) - return RTErrConvertFromWin32(GetLastError()); -#endif + PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); - return VINF_SUCCESS; + return RTSerialPortQueryStatusLines(pThis->hSerialPort, pfStsLines); } -/* -=-=-=-=- receive thread -=-=-=-=- */ + +/* -=-=-=-=- I/O thread -=-=-=-=- */ /** - * Receive thread loop. - * - * This thread pushes data from the host serial device up the driver - * chain toward the serial device. + * I/O thread loop. * * @returns VINF_SUCCESS. * @param pDrvIns PDM driver instance data. * @param pThread The PDM thread data. */ -static DECLCALLBACK(int) drvHostSerialRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) +static DECLCALLBACK(int) drvHostSerialIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) { PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); - uint8_t abBuffer[256]; - uint8_t *pbBuffer = NULL; - size_t cbRemaining = 0; /* start by reading host data */ - int rc = VINF_SUCCESS; - int rcThread = VINF_SUCCESS; if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) return VINF_SUCCESS; -#ifdef RT_OS_WINDOWS - /* Make sure that the halt event semaphore is reset. */ - DWORD dwRet = WaitForSingleObject(pThis->hHaltEventSem, 0); - - HANDLE ahWait[2]; - ahWait[0] = pThis->hEventRecv; - ahWait[1] = pThis->hHaltEventSem; -#endif - while (pThread->enmState == PDMTHREADSTATE_RUNNING) { - if (!cbRemaining) - { - /* Get a block of data from the host serial device. */ - -#if defined(RT_OS_DARWIN) /* poll is broken on x86 darwin, returns POLLNVAL. */ - fd_set RdSet; - FD_ZERO(&RdSet); - FD_SET(RTFileToNative(pThis->hDeviceFileR), &RdSet); - FD_SET(RTPipeToNative(pThis->hWakeupPipeR), &RdSet); - fd_set XcptSet; - FD_ZERO(&XcptSet); - FD_SET(RTFileToNative(pThis->hDeviceFile), &XcptSet); - FD_SET(RTPipeToNative(pThis->hWakeupPipeR), &XcptSet); -# if 1 /* it seems like this select is blocking the write... */ - rc = select(RT_MAX(RTPipeToNative(pThis->hWakeupPipeR), RTFileToNative(pThis->hDeviceFileR)) + 1, - &RdSet, NULL, &XcptSet, NULL); -# else - struct timeval tv = { 0, 1000 }; - rc = select(RTPipeToNative(pThis->hWakeupPipeR), RTFileToNative(pThis->hDeviceFileR) + 1, - &RdSet, NULL, &XcptSet, &tv); -# endif - if (rc == -1) - { - int err = errno; - rcThread = RTErrConvertFromErrno(err); - LogRel(("HostSerial#%d: select failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread)); - break; - } + uint32_t fEvtFlags = RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED | RTSERIALPORT_EVT_F_BREAK_DETECTED; - /* this might have changed in the meantime */ - if (pThread->enmState != PDMTHREADSTATE_RUNNING) - break; - if (rc == 0) - continue; + if (!pThis->fAvailWrInt) + pThis->fAvailWrInt = ASMAtomicXchgBool(&pThis->fAvailWrExt, false); - /* drain the wakeup pipe */ - size_t cbRead; - if ( FD_ISSET(RTPipeToNative(pThis->hWakeupPipeR), &RdSet) - || FD_ISSET(RTPipeToNative(pThis->hWakeupPipeR), &XcptSet)) - { - rc = RTPipeRead(pThis->hWakeupPipeR, abBuffer, 1, &cbRead); - if (RT_FAILURE(rc)) - { - LogRel(("HostSerial#%d: draining the wakeup pipe failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc)); - rcThread = rc; - break; - } - continue; - } + /* Wait until there is room again if there is anyting to send. */ + if ( pThis->fAvailWrInt + || pThis->cbTxUsed) + fEvtFlags |= RTSERIALPORT_EVT_F_DATA_TX; - /* read data from the serial port. */ - rc = RTFileRead(pThis->hDeviceFileR, abBuffer, sizeof(abBuffer), &cbRead); - if (RT_FAILURE(rc)) - { - LogRel(("HostSerial#%d: (1) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc)); - rcThread = rc; - break; - } - cbRemaining = cbRead; - -#elif defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - - size_t cbRead; - struct pollfd aFDs[2]; - aFDs[0].fd = RTFileToNative(pThis->hDeviceFile); - aFDs[0].events = POLLIN; - aFDs[0].revents = 0; - aFDs[1].fd = RTPipeToNative(pThis->hWakeupPipeR); - aFDs[1].events = POLLIN | POLLERR | POLLHUP; - aFDs[1].revents = 0; - rc = poll(aFDs, RT_ELEMENTS(aFDs), -1); - if (rc < 0) - { - int err = errno; - if (err == EINTR) - { - /* - * EINTR errors should be harmless, even if they are not supposed to occur in our setup. - */ - Log(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, err, strerror(err))); - RTThreadYield(); - continue; - } - - rcThread = RTErrConvertFromErrno(err); - LogRel(("HostSerial#%d: poll failed with errno=%d / %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, err, rcThread)); - break; - } - /* this might have changed in the meantime */ - if (pThread->enmState != PDMTHREADSTATE_RUNNING) - break; - if (rc > 0 && aFDs[1].revents) - { - if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL)) - break; - /* notification to terminate -- drain the pipe */ - RTPipeRead(pThis->hWakeupPipeR, &abBuffer, 1, &cbRead); - continue; - } - rc = RTFileRead(pThis->hDeviceFile, abBuffer, sizeof(abBuffer), &cbRead); - if (RT_FAILURE(rc)) - { - /* don't terminate worker thread when data unavailable */ - if (rc == VERR_TRY_AGAIN) - continue; + /* Try to receive more if there is still room. */ + if (drvHostSerialReadBufGetWrite(pThis, NULL) > 0) + fEvtFlags |= RTSERIALPORT_EVT_F_DATA_RX; - LogRel(("HostSerial#%d: (2) Read failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc)); - rcThread = rc; - break; - } - cbRemaining = cbRead; - -#elif defined(RT_OS_WINDOWS) - - DWORD dwEventMask = 0; - DWORD dwNumberOfBytesTransferred; - - memset(&pThis->overlappedRecv, 0, sizeof(pThis->overlappedRecv)); - pThis->overlappedRecv.hEvent = pThis->hEventRecv; - - if (!WaitCommEvent(pThis->hDeviceFile, &dwEventMask, &pThis->overlappedRecv)) + uint32_t fEvtsRecv = 0; + int rc = RTSerialPortEvtPoll(pThis->hSerialPort, fEvtFlags, &fEvtsRecv, RT_INDEFINITE_WAIT); + if (RT_SUCCESS(rc)) + { + if (fEvtsRecv & RTSERIALPORT_EVT_F_DATA_TX) { - dwRet = GetLastError(); - if (dwRet == ERROR_IO_PENDING) + if (pThis->fAvailWrInt) { - dwRet = WaitForMultipleObjects(2, ahWait, FALSE, INFINITE); - if (dwRet != WAIT_OBJECT_0) + /* Stuff as much data into the TX buffer as we can. */ + size_t cbToFetch = RT_ELEMENTS(pThis->abTxBuf) - pThis->cbTxUsed; + size_t cbFetched = 0; + rc = pThis->pDrvSerialPort->pfnReadWr(pThis->pDrvSerialPort, &pThis->abTxBuf[pThis->cbTxUsed], cbToFetch, + &cbFetched); + AssertRC(rc); + + if (cbFetched > 0) + pThis->cbTxUsed += cbFetched; + else { - /* notification to terminate */ - AssertMsg(pThread->enmState != PDMTHREADSTATE_RUNNING, ("The halt event semaphore is set but the thread is still in running state\n")); - break; + /* There is no data available anymore. */ + pThis->fAvailWrInt = false; } } - else - { - rcThread = RTErrConvertFromWin32(dwRet); - LogRel(("HostSerial#%d: Wait failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread)); - break; - } - } - /* this might have changed in the meantime */ - if (pThread->enmState != PDMTHREADSTATE_RUNNING) - break; - /* Check the event */ - if (dwEventMask & EV_RXCHAR) - { - if (!ReadFile(pThis->hDeviceFile, abBuffer, sizeof(abBuffer), &dwNumberOfBytesTransferred, &pThis->overlappedRecv)) + if (pThis->cbTxUsed) { - dwRet = GetLastError(); - if (dwRet == ERROR_IO_PENDING) + size_t cbProcessed = 0; + rc = RTSerialPortWriteNB(pThis->hSerialPort, &pThis->abTxBuf[0], pThis->cbTxUsed, &cbProcessed); + if (RT_SUCCESS(rc)) { - if (GetOverlappedResult(pThis->hDeviceFile, &pThis->overlappedRecv, &dwNumberOfBytesTransferred, TRUE)) - dwRet = NO_ERROR; + pThis->cbTxUsed -= cbProcessed; + if (pThis->cbTxUsed) + { + /* Move the data in the TX buffer to the front to fill the end again. */ + memmove(&pThis->abTxBuf[0], &pThis->abTxBuf[cbProcessed], pThis->cbTxUsed); + } else - dwRet = GetLastError(); + pThis->pDrvSerialPort->pfnDataSentNotify(pThis->pDrvSerialPort); + STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbProcessed); } - if (dwRet != NO_ERROR) + else { - rcThread = RTErrConvertFromWin32(dwRet); - LogRel(("HostSerial#%d: Read failed with error %Rrc; terminating the worker thread.\n", pDrvIns->iInstance, rcThread)); + LogRelMax(10, ("HostSerial#%d: Sending data failed even though the serial port is marked as writeable (rc=%Rrc)\n", + pThis->pDrvIns->iInstance, rc)); break; } } - cbRemaining = dwNumberOfBytesTransferred; } - else if (dwEventMask & EV_BREAK) + + if (fEvtsRecv & RTSERIALPORT_EVT_F_DATA_RX) { - Log(("HostSerial#%d: Detected break\n")); - rc = pThis->pDrvCharPort->pfnNotifyBreak(pThis->pDrvCharPort); + void *pvDst = NULL; + size_t cbToRead = drvHostSerialReadBufGetWrite(pThis, &pvDst); + size_t cbRead = 0; + rc = RTSerialPortReadNB(pThis->hSerialPort, pvDst, cbToRead, &cbRead); + if (RT_SUCCESS(rc)) + { + drvHostSerialReadBufWriteAdv(pThis, cbRead); + /* Notify the device/driver above. */ + rc = pThis->pDrvSerialPort->pfnDataAvailRdrNotify(pThis->pDrvSerialPort, cbRead); + AssertRC(rc); + } + else + LogRelMax(10, ("HostSerial#%d: Reading data failed even though the serial port is marked as readable (rc=%Rrc)\n", + pThis->pDrvIns->iInstance, rc)); } - else + + if (fEvtsRecv & RTSERIALPORT_EVT_F_BREAK_DETECTED) + pThis->pDrvSerialPort->pfnNotifyBrk(pThis->pDrvSerialPort); + + if (fEvtsRecv & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED) { /* The status lines have changed. Notify the device. */ - DWORD dwNewStatusLinesState = 0; - uint32_t uNewStatusLinesState = 0; - - /* Get the new state */ - if (GetCommModemStatus(pThis->hDeviceFile, &dwNewStatusLinesState)) + uint32_t fStsLines = 0; + rc = RTSerialPortQueryStatusLines(pThis->hSerialPort, &fStsLines); + if (RT_SUCCESS(rc)) { - if (dwNewStatusLinesState & MS_RLSD_ON) - uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_DCD; - if (dwNewStatusLinesState & MS_RING_ON) - uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_RI; - if (dwNewStatusLinesState & MS_DSR_ON) - uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_DSR; - if (dwNewStatusLinesState & MS_CTS_ON) - uNewStatusLinesState |= PDMICHARPORT_STATUS_LINES_CTS; - rc = pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, uNewStatusLinesState); + uint32_t fPdmStsLines = 0; + + if (fStsLines & RTSERIALPORT_STS_LINE_DCD) + fPdmStsLines |= PDMISERIALPORT_STS_LINE_DCD; + if (fStsLines & RTSERIALPORT_STS_LINE_RI) + fPdmStsLines |= PDMISERIALPORT_STS_LINE_RI; + if (fStsLines & RTSERIALPORT_STS_LINE_DSR) + fPdmStsLines |= PDMISERIALPORT_STS_LINE_DSR; + if (fStsLines & RTSERIALPORT_STS_LINE_CTS) + fPdmStsLines |= PDMISERIALPORT_STS_LINE_CTS; + + rc = pThis->pDrvSerialPort->pfnNotifyStsLinesChanged(pThis->pDrvSerialPort, fPdmStsLines); if (RT_FAILURE(rc)) { /* Notifying device failed, continue but log it */ - LogRel(("HostSerial#%d: Notifying device failed with error %Rrc; continuing.\n", pDrvIns->iInstance, rc)); + LogRelMax(10, ("HostSerial#%d: Notifying device about changed status lines failed with error %Rrc; continuing.\n", + pDrvIns->iInstance, rc)); } } else - { - /* Getting new state failed, continue but log it */ - LogRel(("HostSerial#%d: Getting status lines state failed with error %Rrc; continuing.\n", pDrvIns->iInstance, RTErrConvertFromWin32(GetLastError()))); - } + LogRelMax(10, ("HostSerial#%d: Getting status lines state failed with error %Rrc; continuing.\n", pDrvIns->iInstance, rc)); } -#endif - Log(("Read %d bytes.\n", cbRemaining)); - pbBuffer = abBuffer; + if (fEvtsRecv & RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED) + LogRel(("HostSerial#%d: Status line monitoring failed at a lower level and is disabled\n", pDrvIns->iInstance)); } - else + else if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED) { - /* Send data to the guest. */ - size_t cbProcessed = cbRemaining; - rc = pThis->pDrvCharPort->pfnNotifyRead(pThis->pDrvCharPort, pbBuffer, &cbProcessed); - if (RT_SUCCESS(rc)) - { - Assert(cbProcessed); Assert(cbProcessed <= cbRemaining); - pbBuffer += cbProcessed; - cbRemaining -= cbProcessed; - STAM_COUNTER_ADD(&pThis->StatBytesRead, cbProcessed); - } - else if (rc == VERR_TIMEOUT) - { - /* Normal case, just means that the guest didn't accept a new - * character before the timeout elapsed. Just retry. */ - rc = VINF_SUCCESS; - } - else - { - LogRel(("HostSerial#%d: NotifyRead failed with %Rrc, terminating the worker thread.\n", pDrvIns->iInstance, rc)); - rcThread = rc; - break; - } + /* Getting interrupted or running into a timeout are no error conditions. */ + rc = VINF_SUCCESS; } } - return rcThread; -} - -/** - * Unblock the receive thread so it can respond to a state change. - * - * @returns a VBox status code. - * @param pDrvIns The driver instance. - * @param pThread The receive thread. - */ -static DECLCALLBACK(int) drvHostSerialWakeupRecvThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) -{ - RT_NOREF(pThread); - PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - size_t cbIgnored; - return RTPipeWrite(pThis->hWakeupPipeW, "", 1, &cbIgnored); - -#elif defined(RT_OS_WINDOWS) - if (!SetEvent(pThis->hHaltEventSem)) - return RTErrConvertFromWin32(GetLastError()); return VINF_SUCCESS; -#else -# error adapt me! -#endif } -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) -/* -=-=-=-=- Monitor thread -=-=-=-=- */ /** - * Monitor thread loop. - * - * This thread monitors the status lines and notifies the device - * if they change. - * - * @returns VINF_SUCCESS. - * @param pDrvIns PDM driver instance data. - * @param pThread The PDM thread data. - */ -static DECLCALLBACK(int) drvHostSerialMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) -{ - PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); - unsigned long const uStatusLinesToCheck = TIOCM_CAR | TIOCM_RNG | TIOCM_DSR | TIOCM_CTS; -#ifdef RT_OS_LINUX - bool fPoll = false; -#endif - - if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) - return VINF_SUCCESS; - - do - { - unsigned int statusLines; - - /* - * Get the status line state. - */ - int rcPsx = ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMGET, &statusLines); - if (rcPsx < 0) - { - PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail", - N_("Ioctl failed for serial host device '%s' (%Rrc). The device will not work properly"), - pThis->pszDevicePath, RTErrConvertFromErrno(errno)); - break; - } - - uint32_t newStatusLine = 0; - - if (statusLines & TIOCM_CAR) - newStatusLine |= PDMICHARPORT_STATUS_LINES_DCD; - if (statusLines & TIOCM_RNG) - newStatusLine |= PDMICHARPORT_STATUS_LINES_RI; - if (statusLines & TIOCM_DSR) - newStatusLine |= PDMICHARPORT_STATUS_LINES_DSR; - if (statusLines & TIOCM_CTS) - newStatusLine |= PDMICHARPORT_STATUS_LINES_CTS; - pThis->pDrvCharPort->pfnNotifyStatusLinesChanged(pThis->pDrvCharPort, newStatusLine); - - if (PDMTHREADSTATE_RUNNING != pThread->enmState) - break; - -# ifdef RT_OS_LINUX - /* - * Wait for status line change. - * - * XXX In Linux, if a thread calls tcsetattr while the monitor thread is - * waiting in ioctl for a modem status change then 8250.c wrongly disables - * modem irqs and so the monitor thread never gets released. The workaround - * is to send a signal after each tcsetattr. - * - * TIOCMIWAIT doesn't work for the DSR line with TIOCM_DSR set - * (see http://lxr.linux.no/#linux+v4.7/drivers/usb/class/cdc-acm.c#L949) - * However as it is possible to query the line state we will not just clear - * the TIOCM_DSR bit from the lines to check but resort to the polling - * approach just like on other hosts. - */ - if (!fPoll) - { - rcPsx = ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMIWAIT, uStatusLinesToCheck); - if (rcPsx < 0 && errno != EINTR) - { - LogRel(("Serial#%u: Failed to wait for status line change with rcPsx=%d errno=%d, switch to polling\n", - pDrvIns->iInstance, rcPsx, errno)); - fPoll = true; - pThis->fStatusLines = statusLines; - } - } - else - { - /* Poll for status line change. */ - if (!((statusLines ^ pThis->fStatusLines) & uStatusLinesToCheck)) - PDMR3ThreadSleep(pThread, 500); /* 0.5 sec */ - pThis->fStatusLines = statusLines; - } -# else - /* Poll for status line change. */ - if (!((statusLines ^ pThis->fStatusLines) & uStatusLinesToCheck)) - PDMR3ThreadSleep(pThread, 500); /* 0.5 sec */ - pThis->fStatusLines = statusLines; -# endif - } while (PDMTHREADSTATE_RUNNING == pThread->enmState); - - return VINF_SUCCESS; -} - -/** - * Unblock the monitor thread so it can respond to a state change. - * We need to execute this code exactly once during initialization. - * But we don't want to block --- therefore this dedicated thread. + * Unblock the send thread so it can respond to a state change. * * @returns a VBox status code. * @param pDrvIns The driver instance. * @param pThread The send thread. */ -static DECLCALLBACK(int) drvHostSerialWakeupMonitorThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) +static DECLCALLBACK(int) drvHostSerialWakeupIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) { -# ifdef RT_OS_LINUX + RT_NOREF(pThread); PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); - int rc = VINF_SUCCESS; - - rc = RTThreadPoke(pThread->Thread); - if (RT_FAILURE(rc)) - PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail", - N_("Suspending serial monitor thread failed for serial device '%s' (%Rrc). The shutdown may take longer than expected"), - pThis->pszDevicePath, RTErrConvertFromErrno(rc)); - -# else /* !RT_OS_LINUX*/ - - /* In polling mode there is nobody to wake up (PDMThread will cancel the sleep). */ - NOREF(pDrvIns); - NOREF(pThread); - -# endif /* RT_OS_LINUX */ - return VINF_SUCCESS; + return RTSerialPortEvtPollInterrupt(pThis->hSerialPort); } -#endif /* RT_OS_LINUX || RT_OS_DARWIN || RT_OS_SOLARIS */ - -/** - * Set the modem lines. - * - * @returns VBox status code - * @param pInterface Pointer to the interface structure. - * @param RequestToSend Set to true if this control line should be made active. - * @param DataTerminalReady Set to true if this control line should be made active. - */ -static DECLCALLBACK(int) drvHostSerialSetModemLines(PPDMICHARCONNECTOR pInterface, bool RequestToSend, bool DataTerminalReady) -{ - PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface); - -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - int modemStateSet = 0; - int modemStateClear = 0; - - if (RequestToSend) - modemStateSet |= TIOCM_RTS; - else - modemStateClear |= TIOCM_RTS; - - if (DataTerminalReady) - modemStateSet |= TIOCM_DTR; - else - modemStateClear |= TIOCM_DTR; - if (modemStateSet) - ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMBIS, &modemStateSet); - - if (modemStateClear) - ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMBIC, &modemStateClear); - -#elif defined(RT_OS_WINDOWS) - if (RequestToSend) - EscapeCommFunction(pThis->hDeviceFile, SETRTS); - else - EscapeCommFunction(pThis->hDeviceFile, CLRRTS); - - if (DataTerminalReady) - EscapeCommFunction(pThis->hDeviceFile, SETDTR); - else - EscapeCommFunction(pThis->hDeviceFile, CLRDTR); - -#endif - - return VINF_SUCCESS; -} - -/** - * Sets the TD line into break condition. - * - * @returns VBox status code. - * @param pInterface Pointer to the interface structure containing the called function pointer. - * @param fBreak Set to true to let the device send a break false to put into normal operation. - * @thread Any thread. - */ -static DECLCALLBACK(int) drvHostSerialSetBreak(PPDMICHARCONNECTOR pInterface, bool fBreak) -{ - PDRVHOSTSERIAL pThis = PDMICHAR_2_DRVHOSTSERIAL(pInterface); - -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - if (fBreak) - ioctl(RTFileToNative(pThis->hDeviceFile), TIOCSBRK); - else - ioctl(RTFileToNative(pThis->hDeviceFile), TIOCCBRK); - -#elif defined(RT_OS_WINDOWS) - if (fBreak) - SetCommBreak(pThis->hDeviceFile); - else - ClearCommBreak(pThis->hDeviceFile); -#endif - - return VINF_SUCCESS; -} /* -=-=-=-=- driver interface -=-=-=-=- */ @@ -1099,49 +531,16 @@ static DECLCALLBACK(int) drvHostSerialSetBreak(PPDMICHARCONNECTOR pInterface, bo */ static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns) { + PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance)); - PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); - - /* Empty the send queue */ - if (pThis->SendSem != NIL_RTSEMEVENT) - { - RTSemEventDestroy(pThis->SendSem); - pThis->SendSem = NIL_RTSEMEVENT; - } - -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - - int rc = RTPipeClose(pThis->hWakeupPipeW); AssertRC(rc); - pThis->hWakeupPipeW = NIL_RTPIPE; - rc = RTPipeClose(pThis->hWakeupPipeR); AssertRC(rc); - pThis->hWakeupPipeR = NIL_RTPIPE; -# if defined(RT_OS_DARWIN) - if (pThis->hDeviceFileR != NIL_RTFILE) - { - if (pThis->hDeviceFileR != pThis->hDeviceFile) - { - rc = RTFileClose(pThis->hDeviceFileR); - AssertRC(rc); - } - pThis->hDeviceFileR = NIL_RTFILE; - } -# endif - if (pThis->hDeviceFile != NIL_RTFILE) + if (pThis->hSerialPort != NIL_RTSERIALPORT) { - rc = RTFileClose(pThis->hDeviceFile); AssertRC(rc); - pThis->hDeviceFile = NIL_RTFILE; + RTSerialPortClose(pThis->hSerialPort); + pThis->hSerialPort = NIL_RTSERIALPORT; } -#elif defined(RT_OS_WINDOWS) - CloseHandle(pThis->hEventRecv); - CloseHandle(pThis->hEventSend); - CancelIo(pThis->hDeviceFile); - CloseHandle(pThis->hDeviceFile); - -#endif - if (pThis->pszDevicePath) { MMR3HeapFree(pThis->pszDevicePath); @@ -1149,6 +548,7 @@ static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns) } } + /** * Construct a char driver instance. * @@ -1164,26 +564,23 @@ static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pC /* * Init basic data members and interfaces. */ -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - pThis->hDeviceFile = NIL_RTFILE; -# ifdef RT_OS_DARWIN - pThis->hDeviceFileR = NIL_RTFILE; -# endif - pThis->hWakeupPipeR = NIL_RTPIPE; - pThis->hWakeupPipeW = NIL_RTPIPE; -#elif defined(RT_OS_WINDOWS) - pThis->hEventRecv = INVALID_HANDLE_VALUE; - pThis->hEventSend = INVALID_HANDLE_VALUE; - pThis->hDeviceFile = INVALID_HANDLE_VALUE; -#endif - pThis->SendSem = NIL_RTSEMEVENT; + pThis->pDrvIns = pDrvIns; + pThis->hSerialPort = NIL_RTSERIALPORT; + pThis->fAvailWrExt = false; + pThis->fAvailWrInt = false; + pThis->cbTxUsed = 0; + pThis->offWrite = 0; + pThis->offRead = 0; + pThis->cbReadBuf = 0; /* IBase. */ - pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface; - /* ICharConnector. */ - pThis->ICharConnector.pfnWrite = drvHostSerialWrite; - pThis->ICharConnector.pfnSetParameters = drvHostSerialSetParameters; - pThis->ICharConnector.pfnSetModemLines = drvHostSerialSetModemLines; - pThis->ICharConnector.pfnSetBreak = drvHostSerialSetBreak; + pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface; + /* ISerialConnector. */ + pThis->ISerialConnector.pfnDataAvailWrNotify = drvHostSerialDataAvailWrNotify; + pThis->ISerialConnector.pfnReadRdr = drvHostSerialReadRdr; + pThis->ISerialConnector.pfnChgParams = drvHostSerialChgParams; + pThis->ISerialConnector.pfnChgModemLines = drvHostSerialChgModemLines; + pThis->ISerialConnector.pfnChgBrk = drvHostSerialChgBrk; + pThis->ISerialConnector.pfnQueryStsLines = drvHostSerialQueryStsLines; /* * Query configuration. @@ -1199,59 +596,21 @@ static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pC /* * Open the device */ -#ifdef RT_OS_WINDOWS - - pThis->hHaltEventSem = CreateEvent(NULL, FALSE, FALSE, NULL); - AssertReturn(pThis->hHaltEventSem != NULL, VERR_NO_MEMORY); - - pThis->hEventRecv = CreateEvent(NULL, FALSE, FALSE, NULL); - AssertReturn(pThis->hEventRecv != NULL, VERR_NO_MEMORY); - - pThis->hEventSend = CreateEvent(NULL, FALSE, FALSE, NULL); - AssertReturn(pThis->hEventSend != NULL, VERR_NO_MEMORY); - - HANDLE hFile = CreateFile(pThis->pszDevicePath, - GENERIC_READ | GENERIC_WRITE, - 0, // must be opened with exclusive access - NULL, // no SECURITY_ATTRIBUTES structure - OPEN_EXISTING, // must use OPEN_EXISTING - FILE_FLAG_OVERLAPPED, // overlapped I/O - NULL); // no template file - if (hFile == INVALID_HANDLE_VALUE) - rc = RTErrConvertFromWin32(GetLastError()); - else + uint32_t fOpenFlags = RTSERIALPORT_OPEN_F_READ + | RTSERIALPORT_OPEN_F_WRITE + | RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING + | RTSERIALPORT_OPEN_F_DETECT_BREAK_CONDITION; + rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags); + if (rc == VERR_NOT_SUPPORTED) { - pThis->hDeviceFile = hFile; - /* for overlapped read */ - if (!SetCommMask(hFile, EV_RXCHAR | EV_CTS | EV_DSR | EV_RING | EV_RLSD)) - { - LogRel(("HostSerial#%d: SetCommMask failed with error %d.\n", pDrvIns->iInstance, GetLastError())); - return VERR_FILE_IO_ERROR; - } - rc = VINF_SUCCESS; + /* + * For certain devices (or pseudo terminals) status line monitoring does not work + * so try again without it. + */ + fOpenFlags &= ~RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING; + rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags); } -#else /* !RT_OS_WINDOWS */ - - uint32_t fOpen = RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE; -# ifdef RT_OS_LINUX - /* This seems to be necessary on some Linux hosts, otherwise we hang here forever. */ - fOpen |= RTFILE_O_NON_BLOCK; -# endif - rc = RTFileOpen(&pThis->hDeviceFile, pThis->pszDevicePath, fOpen); -# ifdef RT_OS_LINUX - /* RTFILE_O_NON_BLOCK not supported? */ - if (rc == VERR_INVALID_PARAMETER) - rc = RTFileOpen(&pThis->hDeviceFile, pThis->pszDevicePath, fOpen & ~RTFILE_O_NON_BLOCK); -# endif -# ifdef RT_OS_DARWIN - if (RT_SUCCESS(rc)) - rc = RTFileOpen(&pThis->hDeviceFileR, pThis->pszDevicePath, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); -# endif - - -#endif /* !RT_OS_WINDOWS */ - if (RT_FAILURE(rc)) { AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pThis->pszDevicePath, rc)); @@ -1276,79 +635,27 @@ static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pC } } - /* Set to non blocking I/O */ -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - - fcntl(RTFileToNative(pThis->hDeviceFile), F_SETFL, O_NONBLOCK); -# ifdef RT_OS_DARWIN - fcntl(RTFileToNative(pThis->hDeviceFileR), F_SETFL, O_NONBLOCK); -# endif - rc = RTPipeCreate(&pThis->hWakeupPipeR, &pThis->hWakeupPipeW, 0 /*fFlags*/); - AssertRCReturn(rc, rc); - -#elif defined(RT_OS_WINDOWS) - - /* Set the COMMTIMEOUTS to get non blocking I/O */ - COMMTIMEOUTS comTimeout; - - comTimeout.ReadIntervalTimeout = MAXDWORD; - comTimeout.ReadTotalTimeoutMultiplier = 0; - comTimeout.ReadTotalTimeoutConstant = 0; - comTimeout.WriteTotalTimeoutMultiplier = 0; - comTimeout.WriteTotalTimeoutConstant = 0; - - SetCommTimeouts(pThis->hDeviceFile, &comTimeout); - -#endif - /* - * Get the ICharPort interface of the above driver/device. + * Get the ISerialPort interface of the above driver/device. */ - pThis->pDrvCharPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMICHARPORT); - if (!pThis->pDrvCharPort) - return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no char port interface above"), pDrvIns->iInstance); + pThis->pDrvSerialPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISERIALPORT); + if (!pThis->pDrvSerialPort) + return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no serial port interface above"), pDrvIns->iInstance); /* - * Create the receive, send and monitor threads plus the related send semaphore. + * Create the I/O thread. */ - rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvHostSerialRecvThread, drvHostSerialWakeupRecvThread, 0, RTTHREADTYPE_IO, "SerRecv"); + rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pIoThrd, pThis, drvHostSerialIoThread, drvHostSerialWakeupIoThread, 0, RTTHREADTYPE_IO, "SerIo"); if (RT_FAILURE(rc)) - return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create receive thread"), pDrvIns->iInstance); - - rc = RTSemEventCreate(&pThis->SendSem); - AssertRC(rc); - - rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSendThread, pThis, drvHostSerialSendThread, drvHostSerialWakeupSendThread, 0, RTTHREADTYPE_IO, "SerSend"); - if (RT_FAILURE(rc)) - return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create send thread"), pDrvIns->iInstance); - -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - /* Linux & darwin needs a separate thread which monitors the status lines. */ - int rcPsx = ioctl(RTFileToNative(pThis->hDeviceFile), TIOCMGET, &pThis->fStatusLines); - if (!rcPsx) - { - rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pMonitorThread, pThis, drvHostSerialMonitorThread, drvHostSerialWakeupMonitorThread, 0, RTTHREADTYPE_IO, "SerMon"); - if (RT_FAILURE(rc)) - return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create monitor thread"), pDrvIns->iInstance); - } - else - { - /* TIOCMGET is not supported for pseudo terminals so just silently skip it. */ - if (errno != ENOTTY) - PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "DrvHostSerialFail", - N_("Trying to get the status lines state failed for serial host device '%s' (%Rrc). The device will not work properly"), - pThis->pszDevicePath, RTErrConvertFromErrno(errno)); - } -#endif + return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create I/O thread"), pDrvIns->iInstance); /* * Register release statistics. */ - PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance); - PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance); -#ifdef RT_OS_DARWIN /* new Write code, not darwin specific. */ - PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatSendOverflows, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, "Nr of bytes overflowed", "/Devices/HostSerial%d/SendOverflow", pDrvIns->iInstance); -#endif + PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, + "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance); + PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, + "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance); return VINF_SUCCESS; } @@ -1362,11 +669,11 @@ const PDMDRVREG g_DrvHostSerial = PDM_DRVREG_VERSION, /* szName */ "Host Serial", - /* szRCMod */ + /* szRCMod */ "", /* szR0Mod */ "", -/* pszDescription */ + /* pszDescription */ "Host serial driver.", /* fFlags */ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT, diff --git a/src/VBox/Devices/Serial/DrvHostSerialNew.cpp b/src/VBox/Devices/Serial/DrvHostSerialNew.cpp deleted file mode 100644 index ea5c670066f..00000000000 --- a/src/VBox/Devices/Serial/DrvHostSerialNew.cpp +++ /dev/null @@ -1,713 +0,0 @@ -/* $Id$ */ -/** @file - * VBox serial devices: Host serial driver - */ - -/* - * Copyright (C) 2006-2018 Oracle Corporation - * - * This file is part of VirtualBox Open Source Edition (OSE), as - * available from http://www.virtualbox.org. This file is free software; - * you can redistribute it and/or modify it under the terms of the GNU - * General Public License (GPL) as published by the Free Software - * Foundation, in version 2 as it comes in the "COPYING" file of the - * VirtualBox OSE distribution. VirtualBox OSE is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. - */ - - - -/********************************************************************************************************************************* -* Header Files * -*********************************************************************************************************************************/ -#define LOG_GROUP LOG_GROUP_DRV_HOST_SERIAL -#include <VBox/vmm/pdm.h> -#include <VBox/vmm/pdmserialifs.h> -#include <VBox/err.h> - -#include <VBox/log.h> -#include <iprt/asm.h> -#include <iprt/assert.h> -#include <iprt/file.h> -#include <iprt/mem.h> -#include <iprt/pipe.h> -#include <iprt/semaphore.h> -#include <iprt/uuid.h> -#include <iprt/serialport.h> - -#include "VBoxDD.h" - - -/********************************************************************************************************************************* -* Structures and Typedefs * -*********************************************************************************************************************************/ - -/** - * Char driver instance data. - * - * @implements PDMISERIALCONNECTOR - */ -typedef struct DRVHOSTSERIAL -{ - /** Pointer to the driver instance structure. */ - PPDMDRVINS pDrvIns; - /** Pointer to the serial port interface of the driver/device above us. */ - PPDMISERIALPORT pDrvSerialPort; - /** Our serial interface. */ - PDMISERIALCONNECTOR ISerialConnector; - /** I/O thread. */ - PPDMTHREAD pIoThrd; - /** The serial port handle. */ - RTSERIALPORT hSerialPort; - /** the device path */ - char *pszDevicePath; - - /** Flag whether data is available from the device/driver above as notified by the driver. */ - volatile bool fAvailWrExt; - /** Internal copy of the flag which gets reset when there is no data anymore. */ - bool fAvailWrInt; - /** Small send buffer. */ - uint8_t abTxBuf[16]; - /** Amount of data in the buffer. */ - size_t cbTxUsed; - - /** The read queue. */ - uint8_t abReadBuf[256]; - /** Current offset to write to next. */ - volatile uint32_t offWrite; - /** Current offset into the read buffer. */ - volatile uint32_t offRead; - /** Current amount of data in the buffer. */ - volatile size_t cbReadBuf; - - /** Read/write statistics */ - STAMCOUNTER StatBytesRead; - STAMCOUNTER StatBytesWritten; -} DRVHOSTSERIAL, *PDRVHOSTSERIAL; - - -/********************************************************************************************************************************* -* Global Variables * -*********************************************************************************************************************************/ - - -/********************************************************************************************************************************* -* Internal Functions * -*********************************************************************************************************************************/ - - -/** - * Returns number of bytes free in the read buffer and pointer to the start of the free space - * in the read buffer. - * - * @returns Number of bytes free in the buffer. - * @param pThis The host serial driver instance. - * @param ppv Where to return the pointer if there is still free space. - */ -DECLINLINE(size_t) drvHostSerialReadBufGetWrite(PDRVHOSTSERIAL pThis, void **ppv) -{ - if (ppv) - *ppv = &pThis->abReadBuf[pThis->offWrite]; - - size_t cbFree = sizeof(pThis->abReadBuf) - ASMAtomicReadZ(&pThis->cbReadBuf); - if (cbFree) - cbFree = RT_MIN(cbFree, sizeof(pThis->abReadBuf) - pThis->offWrite); - - return cbFree; -} - - -/** - * Returns number of bytes used in the read buffer and pointer to the next byte to read. - * - * @returns Number of bytes free in the buffer. - * @param pThis The host serial driver instance. - * @param ppv Where to return the pointer to the next data to read. - */ -DECLINLINE(size_t) drvHostSerialReadBufGetRead(PDRVHOSTSERIAL pThis, void **ppv) -{ - if (ppv) - *ppv = &pThis->abReadBuf[pThis->offRead]; - - size_t cbUsed = ASMAtomicReadZ(&pThis->cbReadBuf); - if (cbUsed) - cbUsed = RT_MIN(cbUsed, sizeof(pThis->abReadBuf) - pThis->offRead); - - return cbUsed; -} - - -/** - * Advances the write position of the read buffer by the given amount of bytes. - * - * @returns nothing. - * @param pThis The host serial driver instance. - * @param cbAdv Number of bytes to advance. - */ -DECLINLINE(void) drvHostSerialReadBufWriteAdv(PDRVHOSTSERIAL pThis, size_t cbAdv) -{ - uint32_t offWrite = ASMAtomicReadU32(&pThis->offWrite); - offWrite = (offWrite + cbAdv) % sizeof(pThis->abReadBuf); - ASMAtomicWriteU32(&pThis->offWrite, offWrite); - ASMAtomicAddZ(&pThis->cbReadBuf, cbAdv); -} - - -/** - * Advances the read position of the read buffer by the given amount of bytes. - * - * @returns nothing. - * @param pThis The host serial driver instance. - * @param cbAdv Number of bytes to advance. - */ -DECLINLINE(void) drvHostSerialReadBufReadAdv(PDRVHOSTSERIAL pThis, size_t cbAdv) -{ - uint32_t offRead = ASMAtomicReadU32(&pThis->offRead); - offRead = (offRead + cbAdv) % sizeof(pThis->abReadBuf); - ASMAtomicWriteU32(&pThis->offRead, offRead); - ASMAtomicSubZ(&pThis->cbReadBuf, cbAdv); -} - - -/* -=-=-=-=- IBase -=-=-=-=- */ - -/** - * @interface_method_impl{PDMIBASE,pfnQueryInterface} - */ -static DECLCALLBACK(void *) drvHostSerialQueryInterface(PPDMIBASE pInterface, const char *pszIID) -{ - PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); - PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); - - PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); - PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALCONNECTOR, &pThis->ISerialConnector); - return NULL; -} - - -/* -=-=-=-=- ISerialConnector -=-=-=-=- */ - -/** @interface_method_impl{PDMISERIALCONNECTOR,pfnDataAvailWrNotify} */ -static DECLCALLBACK(int) drvHostSerialDataAvailWrNotify(PPDMISERIALCONNECTOR pInterface) -{ - PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); - - int rc = VINF_SUCCESS; - bool fAvailOld = ASMAtomicXchgBool(&pThis->fAvailWrExt, true); - if (!fAvailOld) - rc = RTSerialPortEvtPollInterrupt(pThis->hSerialPort); - - return rc; -} - - -/** - * @interface_method_impl{PDMISERIALCONNECTOR,pfnReadRdr} - */ -static DECLCALLBACK(int) drvHostSerialReadRdr(PPDMISERIALCONNECTOR pInterface, void *pvBuf, - size_t cbRead, size_t *pcbRead) -{ - PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); - int rc = VINF_SUCCESS; - uint8_t *pbDst = (uint8_t *)pvBuf; - size_t cbReadAll = 0; - - do - { - void *pvSrc = NULL; - size_t cbThisRead = RT_MIN(drvHostSerialReadBufGetRead(pThis, &pvSrc), cbRead); - if (cbThisRead) - { - memcpy(pbDst, pvSrc, cbThisRead); - cbRead -= cbThisRead; - pbDst += cbThisRead; - cbReadAll += cbThisRead; - drvHostSerialReadBufReadAdv(pThis, cbThisRead); - } - else - break; - } while (cbRead > 0); - - *pcbRead = cbReadAll; - /* Kick the I/O thread if there is nothing to read to recalculate the poll flags. */ - if (!drvHostSerialReadBufGetRead(pThis, NULL)) - rc = RTSerialPortEvtPollInterrupt(pThis->hSerialPort); - - STAM_COUNTER_ADD(&pThis->StatBytesRead, cbReadAll); - return rc; -} - - -/** - * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgParams} - */ -static DECLCALLBACK(int) drvHostSerialChgParams(PPDMISERIALCONNECTOR pInterface, uint32_t uBps, - PDMSERIALPARITY enmParity, unsigned cDataBits, - PDMSERIALSTOPBITS enmStopBits) -{ - PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); - RTSERIALPORTCFG Cfg; - - Cfg.uBaudRate = uBps; - - switch (enmParity) - { - case PDMSERIALPARITY_EVEN: - Cfg.enmParity = RTSERIALPORTPARITY_EVEN; - break; - case PDMSERIALPARITY_ODD: - Cfg.enmParity = RTSERIALPORTPARITY_ODD; - break; - case PDMSERIALPARITY_NONE: - Cfg.enmParity = RTSERIALPORTPARITY_NONE; - break; - case PDMSERIALPARITY_MARK: - Cfg.enmParity = RTSERIALPORTPARITY_MARK; - break; - case PDMSERIALPARITY_SPACE: - Cfg.enmParity = RTSERIALPORTPARITY_SPACE; - break; - default: - AssertMsgFailed(("Unsupported parity setting %d\n", enmParity)); /* Should not happen. */ - Cfg.enmParity = RTSERIALPORTPARITY_NONE; - } - - switch (cDataBits) - { - case 5: - Cfg.enmDataBitCount = RTSERIALPORTDATABITS_5BITS; - break; - case 6: - Cfg.enmDataBitCount = RTSERIALPORTDATABITS_6BITS; - break; - case 7: - Cfg.enmDataBitCount = RTSERIALPORTDATABITS_7BITS; - break; - case 8: - Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS; - break; - default: - AssertMsgFailed(("Unsupported data bit count %u\n", cDataBits)); /* Should not happen. */ - Cfg.enmDataBitCount = RTSERIALPORTDATABITS_8BITS; - } - - switch (enmStopBits) - { - case PDMSERIALSTOPBITS_ONE: - Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE; - break; - case PDMSERIALSTOPBITS_ONEPOINTFIVE: - Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE; - break; - case PDMSERIALSTOPBITS_TWO: - Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_TWO; - break; - default: - AssertMsgFailed(("Unsupported stop bit count %d\n", enmStopBits)); /* Should not happen. */ - Cfg.enmStopBitCount = RTSERIALPORTSTOPBITS_ONE; - } - - return RTSerialPortCfgSet(pThis->hSerialPort, &Cfg, NULL); -} - - -/** - * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgModemLines} - */ -static DECLCALLBACK(int) drvHostSerialChgModemLines(PPDMISERIALCONNECTOR pInterface, bool fRts, bool fDtr) -{ - PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); - - uint32_t fClear = 0; - uint32_t fSet = 0; - - if (fRts) - fSet |= RTSERIALPORT_CHG_STS_LINES_F_RTS; - else - fClear |= RTSERIALPORT_CHG_STS_LINES_F_RTS; - - if (fDtr) - fSet |= RTSERIALPORT_CHG_STS_LINES_F_DTR; - else - fClear |= RTSERIALPORT_CHG_STS_LINES_F_DTR; - - return RTSerialPortChgStatusLines(pThis->hSerialPort, fClear, fSet); -} - - -/** - * @interface_method_impl{PDMISERIALCONNECTOR,pfnChgBrk} - */ -static DECLCALLBACK(int) drvHostSerialChgBrk(PPDMISERIALCONNECTOR pInterface, bool fBrk) -{ - PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); - - return RTSerialPortChgBreakCondition(pThis->hSerialPort, fBrk); -} - - -/** - * @interface_method_impl{PDMISERIALCONNECTOR,pfnQueryStsLines} - */ -static DECLCALLBACK(int) drvHostSerialQueryStsLines(PPDMISERIALCONNECTOR pInterface, uint32_t *pfStsLines) -{ - PDRVHOSTSERIAL pThis = RT_FROM_MEMBER(pInterface, DRVHOSTSERIAL, ISerialConnector); - - return RTSerialPortQueryStatusLines(pThis->hSerialPort, pfStsLines); -} - - -/* -=-=-=-=- I/O thread -=-=-=-=- */ - -/** - * I/O thread loop. - * - * @returns VINF_SUCCESS. - * @param pDrvIns PDM driver instance data. - * @param pThread The PDM thread data. - */ -static DECLCALLBACK(int) drvHostSerialIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) -{ - PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); - - if (pThread->enmState == PDMTHREADSTATE_INITIALIZING) - return VINF_SUCCESS; - - while (pThread->enmState == PDMTHREADSTATE_RUNNING) - { - uint32_t fEvtFlags = RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED | RTSERIALPORT_EVT_F_BREAK_DETECTED; - - if (!pThis->fAvailWrInt) - pThis->fAvailWrInt = ASMAtomicXchgBool(&pThis->fAvailWrExt, false); - - /* Wait until there is room again if there is anyting to send. */ - if ( pThis->fAvailWrInt - || pThis->cbTxUsed) - fEvtFlags |= RTSERIALPORT_EVT_F_DATA_TX; - - /* Try to receive more if there is still room. */ - if (drvHostSerialReadBufGetWrite(pThis, NULL) > 0) - fEvtFlags |= RTSERIALPORT_EVT_F_DATA_RX; - - uint32_t fEvtsRecv = 0; - int rc = RTSerialPortEvtPoll(pThis->hSerialPort, fEvtFlags, &fEvtsRecv, RT_INDEFINITE_WAIT); - if (RT_SUCCESS(rc)) - { - if (fEvtsRecv & RTSERIALPORT_EVT_F_DATA_TX) - { - if (pThis->fAvailWrInt) - { - /* Stuff as much data into the TX buffer as we can. */ - size_t cbToFetch = RT_ELEMENTS(pThis->abTxBuf) - pThis->cbTxUsed; - size_t cbFetched = 0; - rc = pThis->pDrvSerialPort->pfnReadWr(pThis->pDrvSerialPort, &pThis->abTxBuf[pThis->cbTxUsed], cbToFetch, - &cbFetched); - AssertRC(rc); - - if (cbFetched > 0) - pThis->cbTxUsed += cbFetched; - else - { - /* There is no data available anymore. */ - pThis->fAvailWrInt = false; - } - } - - if (pThis->cbTxUsed) - { - size_t cbProcessed = 0; - rc = RTSerialPortWriteNB(pThis->hSerialPort, &pThis->abTxBuf[0], pThis->cbTxUsed, &cbProcessed); - if (RT_SUCCESS(rc)) - { - pThis->cbTxUsed -= cbProcessed; - if (pThis->cbTxUsed) - { - /* Move the data in the TX buffer to the front to fill the end again. */ - memmove(&pThis->abTxBuf[0], &pThis->abTxBuf[cbProcessed], pThis->cbTxUsed); - } - else - pThis->pDrvSerialPort->pfnDataSentNotify(pThis->pDrvSerialPort); - STAM_COUNTER_ADD(&pThis->StatBytesWritten, cbProcessed); - } - else - { - LogRelMax(10, ("HostSerial#%d: Sending data failed even though the serial port is marked as writeable (rc=%Rrc)\n", - pThis->pDrvIns->iInstance, rc)); - break; - } - } - } - - if (fEvtsRecv & RTSERIALPORT_EVT_F_DATA_RX) - { - void *pvDst = NULL; - size_t cbToRead = drvHostSerialReadBufGetWrite(pThis, &pvDst); - size_t cbRead = 0; - rc = RTSerialPortReadNB(pThis->hSerialPort, pvDst, cbToRead, &cbRead); - if (RT_SUCCESS(rc)) - { - drvHostSerialReadBufWriteAdv(pThis, cbRead); - /* Notify the device/driver above. */ - rc = pThis->pDrvSerialPort->pfnDataAvailRdrNotify(pThis->pDrvSerialPort, cbRead); - AssertRC(rc); - } - else - LogRelMax(10, ("HostSerial#%d: Reading data failed even though the serial port is marked as readable (rc=%Rrc)\n", - pThis->pDrvIns->iInstance, rc)); - } - - if (fEvtsRecv & RTSERIALPORT_EVT_F_BREAK_DETECTED) - pThis->pDrvSerialPort->pfnNotifyBrk(pThis->pDrvSerialPort); - - if (fEvtsRecv & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED) - { - /* The status lines have changed. Notify the device. */ - uint32_t fStsLines = 0; - rc = RTSerialPortQueryStatusLines(pThis->hSerialPort, &fStsLines); - if (RT_SUCCESS(rc)) - { - uint32_t fPdmStsLines = 0; - - if (fStsLines & RTSERIALPORT_STS_LINE_DCD) - fPdmStsLines |= PDMISERIALPORT_STS_LINE_DCD; - if (fStsLines & RTSERIALPORT_STS_LINE_RI) - fPdmStsLines |= PDMISERIALPORT_STS_LINE_RI; - if (fStsLines & RTSERIALPORT_STS_LINE_DSR) - fPdmStsLines |= PDMISERIALPORT_STS_LINE_DSR; - if (fStsLines & RTSERIALPORT_STS_LINE_CTS) - fPdmStsLines |= PDMISERIALPORT_STS_LINE_CTS; - - rc = pThis->pDrvSerialPort->pfnNotifyStsLinesChanged(pThis->pDrvSerialPort, fPdmStsLines); - if (RT_FAILURE(rc)) - { - /* Notifying device failed, continue but log it */ - LogRelMax(10, ("HostSerial#%d: Notifying device about changed status lines failed with error %Rrc; continuing.\n", - pDrvIns->iInstance, rc)); - } - } - else - LogRelMax(10, ("HostSerial#%d: Getting status lines state failed with error %Rrc; continuing.\n", pDrvIns->iInstance, rc)); - } - - if (fEvtsRecv & RTSERIALPORT_EVT_F_STATUS_LINE_MONITOR_FAILED) - LogRel(("HostSerial#%d: Status line monitoring failed at a lower level and is disabled\n", pDrvIns->iInstance)); - } - else if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED) - { - /* Getting interrupted or running into a timeout are no error conditions. */ - rc = VINF_SUCCESS; - } - } - - return VINF_SUCCESS; -} - - -/** - * Unblock the send thread so it can respond to a state change. - * - * @returns a VBox status code. - * @param pDrvIns The driver instance. - * @param pThread The send thread. - */ -static DECLCALLBACK(int) drvHostSerialWakeupIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread) -{ - RT_NOREF(pThread); - PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); - - return RTSerialPortEvtPollInterrupt(pThis->hSerialPort); -} - - -/* -=-=-=-=- driver interface -=-=-=-=- */ - -/** - * Destruct a char driver instance. - * - * Most VM resources are freed by the VM. This callback is provided so that - * any non-VM resources can be freed correctly. - * - * @param pDrvIns The driver instance data. - */ -static DECLCALLBACK(void) drvHostSerialDestruct(PPDMDRVINS pDrvIns) -{ - PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); - PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); - LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance)); - - if (pThis->hSerialPort != NIL_RTSERIALPORT) - { - RTSerialPortClose(pThis->hSerialPort); - pThis->hSerialPort = NIL_RTSERIALPORT; - } - - if (pThis->pszDevicePath) - { - MMR3HeapFree(pThis->pszDevicePath); - pThis->pszDevicePath = NULL; - } -} - - -/** - * Construct a char driver instance. - * - * @copydoc FNPDMDRVCONSTRUCT - */ -static DECLCALLBACK(int) drvHostSerialConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) -{ - RT_NOREF1(fFlags); - PDRVHOSTSERIAL pThis = PDMINS_2_DATA(pDrvIns, PDRVHOSTSERIAL); - LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance)); - PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); - - /* - * Init basic data members and interfaces. - */ - pThis->pDrvIns = pDrvIns; - pThis->hSerialPort = NIL_RTSERIALPORT; - pThis->fAvailWrExt = false; - pThis->fAvailWrInt = false; - pThis->cbTxUsed = 0; - pThis->offWrite = 0; - pThis->offRead = 0; - pThis->cbReadBuf = 0; - /* IBase. */ - pDrvIns->IBase.pfnQueryInterface = drvHostSerialQueryInterface; - /* ISerialConnector. */ - pThis->ISerialConnector.pfnDataAvailWrNotify = drvHostSerialDataAvailWrNotify; - pThis->ISerialConnector.pfnReadRdr = drvHostSerialReadRdr; - pThis->ISerialConnector.pfnChgParams = drvHostSerialChgParams; - pThis->ISerialConnector.pfnChgModemLines = drvHostSerialChgModemLines; - pThis->ISerialConnector.pfnChgBrk = drvHostSerialChgBrk; - pThis->ISerialConnector.pfnQueryStsLines = drvHostSerialQueryStsLines; - - /* - * Query configuration. - */ - /* Device */ - int rc = CFGMR3QueryStringAlloc(pCfg, "DevicePath", &pThis->pszDevicePath); - if (RT_FAILURE(rc)) - { - AssertMsgFailed(("Configuration error: query for \"DevicePath\" string returned %Rra.\n", rc)); - return rc; - } - - /* - * Open the device - */ - uint32_t fOpenFlags = RTSERIALPORT_OPEN_F_READ - | RTSERIALPORT_OPEN_F_WRITE - | RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING - | RTSERIALPORT_OPEN_F_DETECT_BREAK_CONDITION; - rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags); - if (rc == VERR_NOT_SUPPORTED) - { - /* - * For certain devices (or pseudo terminals) status line monitoring does not work - * so try again without it. - */ - fOpenFlags &= ~RTSERIALPORT_OPEN_F_SUPPORT_STATUS_LINE_MONITORING; - rc = RTSerialPortOpen(&pThis->hSerialPort, pThis->pszDevicePath, fOpenFlags); - } - - if (RT_FAILURE(rc)) - { - AssertMsgFailed(("Could not open host device %s, rc=%Rrc\n", pThis->pszDevicePath, rc)); - switch (rc) - { - case VERR_ACCESS_DENIED: - return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, -#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) || defined(RT_OS_FREEBSD) - N_("Cannot open host device '%s' for read/write access. Check the permissions " - "of that device ('/bin/ls -l %s'): Most probably you need to be member " - "of the device group. Make sure that you logout/login after changing " - "the group settings of the current user"), -#else - N_("Cannot open host device '%s' for read/write access. Check the permissions " - "of that device"), -#endif - pThis->pszDevicePath, pThis->pszDevicePath); - default: - return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, - N_("Failed to open host device '%s'"), - pThis->pszDevicePath); - } - } - - /* - * Get the ISerialPort interface of the above driver/device. - */ - pThis->pDrvSerialPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISERIALPORT); - if (!pThis->pDrvSerialPort) - return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE, RT_SRC_POS, N_("HostSerial#%d has no serial port interface above"), pDrvIns->iInstance); - - /* - * Create the I/O thread. - */ - rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pIoThrd, pThis, drvHostSerialIoThread, drvHostSerialWakeupIoThread, 0, RTTHREADTYPE_IO, "SerIo"); - if (RT_FAILURE(rc)) - return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("HostSerial#%d cannot create I/O thread"), pDrvIns->iInstance); - - /* - * Register release statistics. - */ - PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesWritten, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, - "Nr of bytes written", "/Devices/HostSerial%d/Written", pDrvIns->iInstance); - PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatBytesRead, STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_BYTES, - "Nr of bytes read", "/Devices/HostSerial%d/Read", pDrvIns->iInstance); - - return VINF_SUCCESS; -} - -/** - * Char driver registration record. - */ -const PDMDRVREG g_DrvHostSerial = -{ - /* u32Version */ - PDM_DRVREG_VERSION, - /* szName */ - "Host Serial", - /* szRCMod */ - "", - /* szR0Mod */ - "", - /* pszDescription */ - "Host serial driver.", - /* fFlags */ - PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT, - /* fClass. */ - PDM_DRVREG_CLASS_CHAR, - /* cMaxInstances */ - ~0U, - /* cbInstance */ - sizeof(DRVHOSTSERIAL), - /* pfnConstruct */ - drvHostSerialConstruct, - /* pfnDestruct */ - drvHostSerialDestruct, - /* pfnRelocate */ - NULL, - /* pfnIOCtl */ - NULL, - /* pfnPowerOn */ - NULL, - /* pfnReset */ - NULL, - /* pfnSuspend */ - NULL, - /* pfnResume */ - NULL, - /* pfnAttach */ - NULL, - /* pfnDetach */ - NULL, - /* pfnPowerOff */ - NULL, - /* pfnSoftReset */ - NULL, - /* u32EndVersion */ - PDM_DRVREG_VERSION -}; - diff --git a/src/VBox/Devices/testcase/Makefile.kmk b/src/VBox/Devices/testcase/Makefile.kmk index b5c38231cd8..4f51b0a5197 100644 --- a/src/VBox/Devices/testcase/Makefile.kmk +++ b/src/VBox/Devices/testcase/Makefile.kmk @@ -48,7 +48,6 @@ VBOX_DEVICES_TESTS_FEATURES = \ $(if $(VBOX_WITH_VMSVGA),VBOX_WITH_VMSVGA,) \ $(if $(VBOX_WITH_WDDM),VBOX_WITH_WDDM,) \ $(if $(VBOX_WITH_XHCI_IMPL),VBOX_WITH_XHCI_IMPL,) \ - $(if $(VBOX_WITH_NEW_SERIAL),VBOX_WITH_NEW_SERIAL,) \ $(VBOX_AUDIO_DEFS) # diff --git a/src/VBox/Devices/testcase/tstDeviceStructSize.cpp b/src/VBox/Devices/testcase/tstDeviceStructSize.cpp index f809a369fdb..ad93e8e1485 100644 --- a/src/VBox/Devices/testcase/tstDeviceStructSize.cpp +++ b/src/VBox/Devices/testcase/tstDeviceStructSize.cpp @@ -109,12 +109,9 @@ #undef LOG_GROUP #include "../Parallel/DevParallel.cpp" #undef LOG_GROUP -#ifndef VBOX_WITH_NEW_SERIAL -# include "../Serial/DevSerial.cpp" -#else -# include "../Serial/DevSerialNew.cpp" -# include "../Serial/DevOxPcie958.cpp" -#endif +#include "../Serial/DevSerial.cpp" +#undef LOG_GROUP +#include "../Serial/DevOxPcie958.cpp" #ifdef VBOX_WITH_AHCI # undef LOG_GROUP # include "../Storage/DevAHCI.cpp" @@ -397,12 +394,8 @@ int main() CHECK_MEMBER_ALIGNMENT(PCNETSTATE, StatMMIOReadRZ, 8); #endif CHECK_MEMBER_ALIGNMENT(PITSTATE, StatPITIrq, 8); -#ifndef VBOX_WITH_NEW_SERIAL - CHECK_MEMBER_ALIGNMENT(SerialState, CritSect, 8); -#else CHECK_MEMBER_ALIGNMENT(DEVSERIAL, UartCore, 8); CHECK_MEMBER_ALIGNMENT(UARTCORE, CritSect, 8); -#endif #ifdef VBOX_WITH_VMSVGA CHECK_SIZE(VMSVGAState, RT_ALIGN_Z(sizeof(VMSVGAState), 8)); CHECK_MEMBER_ALIGNMENT(VGASTATE, svga, 8); diff --git a/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp b/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp index 832e090fe84..f9086b0663f 100644 --- a/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp +++ b/src/VBox/Devices/testcase/tstDeviceStructSizeRC.cpp @@ -108,13 +108,11 @@ #undef LOG_GROUP #include "../Parallel/DevParallel.cpp" #undef LOG_GROUP -#ifndef VBOX_WITH_NEW_SERIAL -# include "../Serial/DevSerial.cpp" -#else -# include "../Serial/DevSerialNew.cpp" -# include "../Serial/DevOxPcie958.cpp" -# include "../Serial/UartCore.h" -#endif +#include "../Serial/DevSerial.cpp" +#undef LOG_GROUP +#include "../Serial/DevOxPcie958.cpp" +#undef LOG_GROUP +#include "../Serial/UartCore.h" #ifdef VBOX_WITH_AHCI # undef LOG_GROUP # include "../Storage/DevAHCI.cpp" @@ -1279,45 +1277,6 @@ int main() GEN_CHECK_OFF(PARALLELPORT, act_fifo_pos_read); #endif -#ifndef VBOX_WITH_NEW_SERIAL - /* Serial/DevSerial.cpp */ - GEN_CHECK_SIZE(SerialState); - GEN_CHECK_OFF(SerialState, CritSect); - GEN_CHECK_OFF(SerialState, pDevInsR3); - GEN_CHECK_OFF(SerialState, pDevInsR0); - GEN_CHECK_OFF(SerialState, pDevInsRC); - GEN_CHECK_OFF(SerialState, IBase); - GEN_CHECK_OFF(SerialState, ICharPort); - GEN_CHECK_OFF(SerialState, pDrvBase); - GEN_CHECK_OFF(SerialState, pDrvChar); - GEN_CHECK_OFF(SerialState, ReceiveSem); - GEN_CHECK_OFF(SerialState, base); - GEN_CHECK_OFF(SerialState, divider); - GEN_CHECK_OFF(SerialState, recv_fifo); - GEN_CHECK_OFF(SerialState, xmit_fifo); - GEN_CHECK_OFF(SerialState, rbr); - GEN_CHECK_OFF(SerialState, thr); - GEN_CHECK_OFF(SerialState, tsr); - GEN_CHECK_OFF(SerialState, ier); - GEN_CHECK_OFF(SerialState, iir); - GEN_CHECK_OFF(SerialState, lcr); - GEN_CHECK_OFF(SerialState, mcr); - GEN_CHECK_OFF(SerialState, lsr); - GEN_CHECK_OFF(SerialState, msr); - GEN_CHECK_OFF(SerialState, scr); - GEN_CHECK_OFF(SerialState, fcr); - GEN_CHECK_OFF(SerialState, fcr_vmstate); - GEN_CHECK_OFF(SerialState, thr_ipending); - GEN_CHECK_OFF(SerialState, timeout_ipending); - GEN_CHECK_OFF(SerialState, irq); - GEN_CHECK_OFF(SerialState, last_break_enable); - GEN_CHECK_OFF(SerialState, tsr_retry); - GEN_CHECK_OFF(SerialState, msr_changed); - GEN_CHECK_OFF(SerialState, fGCEnabled); - GEN_CHECK_OFF(SerialState, fR0Enabled); - GEN_CHECK_OFF(SerialState, fYieldOnLSRRead); - GEN_CHECK_OFF(SerialState, char_transmit_time); -#else /* Serial/UartCore.cpp */ GEN_CHECK_SIZE(UARTCORE); GEN_CHECK_OFF(UARTCORE, CritSect); @@ -1355,7 +1314,7 @@ int main() GEN_CHECK_OFF(UARTCORE, cSymbolXferTicks); GEN_CHECK_OFF(UARTCORE, cbAvailRdr); - /* Serial/DevSerialNew.cpp */ + /* Serial/DevSerial.cpp */ GEN_CHECK_SIZE(DEVSERIAL); GEN_CHECK_OFF(DEVSERIAL, pDevInsR3); GEN_CHECK_OFF(DEVSERIAL, pDevInsR0); @@ -1381,7 +1340,6 @@ int main() GEN_CHECK_OFF(DEVOX958, GCPhysMMIO); GEN_CHECK_OFF(DEVOX958, aUarts); GEN_CHECK_OFF(DEVOX958, aUarts[OX958_UARTS_MAX - 1]); -#endif #ifdef VBOX_WITH_AHCI /* Storage/DevAHCI.cpp */ |