summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Serial/DevSerial.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Serial/DevSerial.cpp')
-rw-r--r--src/VBox/Devices/Serial/DevSerial.cpp545
1 files changed, 545 insertions, 0 deletions
diff --git a/src/VBox/Devices/Serial/DevSerial.cpp b/src/VBox/Devices/Serial/DevSerial.cpp
new file mode 100644
index 00000000..afb7771f
--- /dev/null
+++ b/src/VBox/Devices/Serial/DevSerial.cpp
@@ -0,0 +1,545 @@
+/* $Id: DevSerial.cpp $ */
+/** @file
+ * DevSerial - 16550A UART emulation.
+ *
+ * The documentation for this device was taken from the PC16550D spec from TI.
+ */
+
+/*
+ * Copyright (C) 2018-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_SERIAL
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmserialifs.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"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Shared serial device state.
+ */
+typedef struct DEVSERIAL
+{
+ /** The IRQ value. */
+ uint8_t uIrq;
+ uint8_t bAlignment;
+ /** The base I/O port the device is registered at. */
+ RTIOPORT PortBase;
+ /** The I/O ports registration. */
+ IOMIOPORTHANDLE hIoPorts;
+
+ /** The UART core. */
+ UARTCORE UartCore;
+} DEVSERIAL;
+/** Pointer to the shared serial device state. */
+typedef DEVSERIAL *PDEVSERIAL;
+
+
+/**
+ * Serial device state for ring-3.
+ */
+typedef struct DEVSERIALR3
+{
+ /** The UART core. */
+ UARTCORER3 UartCore;
+} DEVSERIALR3;
+/** Pointer to the serial device state for ring-3. */
+typedef DEVSERIALR3 *PDEVSERIALR3;
+
+
+/**
+ * Serial device state for ring-0.
+ */
+typedef struct DEVSERIALR0
+{
+ /** The UART core. */
+ UARTCORER0 UartCore;
+} DEVSERIALR0;
+/** Pointer to the serial device state for ring-0. */
+typedef DEVSERIALR0 *PDEVSERIALR0;
+
+
+/**
+ * Serial device state for raw-mode.
+ */
+typedef struct DEVSERIALRC
+{
+ /** The UART core. */
+ UARTCORERC UartCore;
+} DEVSERIALRC;
+/** Pointer to the serial device state for raw-mode. */
+typedef DEVSERIALRC *PDEVSERIALRC;
+
+/** The serial device state for the current context. */
+typedef CTX_SUFF(DEVSERIAL) DEVSERIALCC;
+/** Pointer to the serial device state for the current context. */
+typedef CTX_SUFF(PDEVSERIAL) PDEVSERIALCC;
+
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+
+static DECLCALLBACK(void) serialIrqReq(PPDMDEVINS pDevIns, PUARTCORE pUart, unsigned iLUN, int iLvl)
+{
+ RT_NOREF(pUart, iLUN);
+ PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PDMDevHlpISASetIrqNoWait(pDevIns, pThis->uIrq, iLvl);
+}
+
+
+/* -=-=-=-=-=-=-=-=- I/O Port Access Handlers -=-=-=-=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWOUT}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+serialIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
+{
+ PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PDEVSERIALCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVSERIALCC);
+ RT_NOREF_PV(pvUser);
+
+ return uartRegWrite(pDevIns, &pThis->UartCore, &pThisCC->UartCore, offPort, u32, cb);
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWIN}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+serialIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
+{
+ PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PDEVSERIALCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVSERIALCC);
+ RT_NOREF_PV(pvUser);
+
+ return uartRegRead(pDevIns, &pThis->UartCore, &pThisCC->UartCore, offPort, 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)
+{
+ PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ RT_NOREF(uPass);
+
+ pHlp->pfnSSMPutU8(pSSM, pThis->uIrq);
+ pHlp->pfnSSMPutIOPort(pSSM, pThis->PortBase);
+ pHlp->pfnSSMPutU32(pSSM, pThis->UartCore.enmType);
+
+ return VINF_SSM_DONT_CALL_AGAIN;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) serialR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ pHlp->pfnSSMPutU8( pSSM, pThis->uIrq);
+ pHlp->pfnSSMPutIOPort(pSSM, pThis->PortBase);
+ pHlp->pfnSSMPutU32( pSSM, pThis->UartCore.enmType);
+
+ uartR3SaveExec(pDevIns, &pThis->UartCore, pSSM);
+ return pHlp->pfnSSMPutU32(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 = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ uint8_t bIrq;
+ RTIOPORT PortBase;
+ UARTTYPE enmType;
+ int rc;
+
+ AssertMsgReturn(uVersion >= UART_SAVED_STATE_VERSION_16450, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
+ if (uVersion > UART_SAVED_STATE_VERSION_LEGACY_CODE)
+ {
+ pHlp->pfnSSMGetU8( pSSM, &bIrq);
+ pHlp->pfnSSMGetIOPort(pSSM, &PortBase);
+ PDMDEVHLP_SSM_GET_ENUM32_RET(pHlp, pSSM, enmType, UARTTYPE);
+ if (uPass == SSM_PASS_FINAL)
+ {
+ rc = uartR3LoadExec(pDevIns, &pThis->UartCore, pSSM, uVersion, uPass, NULL, NULL);
+ AssertRCReturn(rc, rc);
+ }
+ }
+ else
+ {
+ enmType = uVersion > UART_SAVED_STATE_VERSION_16450 ? UARTTYPE_16550A : UARTTYPE_16450;
+ if (uPass != SSM_PASS_FINAL)
+ {
+ int32_t iIrqTmp;
+ pHlp->pfnSSMGetS32(pSSM, &iIrqTmp);
+ uint32_t uPortBaseTmp;
+ rc = pHlp->pfnSSMGetU32(pSSM, &uPortBaseTmp);
+ AssertRCReturn(rc, rc);
+
+ bIrq = (uint8_t)iIrqTmp;
+ PortBase = (uint32_t)uPortBaseTmp;
+ }
+ else
+ {
+ rc = uartR3LoadExec(pDevIns, &pThis->UartCore, pSSM, uVersion, uPass, &bIrq, &PortBase);
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ /* The marker. */
+ uint32_t u32;
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32);
+ AssertRCReturn(rc, rc);
+ AssertMsgReturn(u32 == UINT32_MAX, ("%#x\n", u32), VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
+ }
+
+ /*
+ * Check the config.
+ */
+ if ( pThis->uIrq != bIrq
+ || pThis->PortBase != PortBase
+ || pThis->UartCore.enmType != enmType)
+ return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS,
+ N_("Config mismatch - saved IRQ=%#x PortBase=%#x Type=%d; configured IRQ=%#x PortBase=%#x Type=%d"),
+ bIrq, 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 = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PDEVSERIALCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVSERIALCC);
+ return uartR3LoadDone(pDevIns, &pThis->UartCore, &pThisCC->UartCore, pSSM);
+}
+
+
+/* -=-=-=-=-=-=-=-=- PDMDEVREG -=-=-=-=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) serialR3Reset(PPDMDEVINS pDevIns)
+{
+ PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PDEVSERIALCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVSERIALCC);
+ uartR3Reset(pDevIns, &pThis->UartCore, &pThisCC->UartCore);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnAttach}
+ */
+static DECLCALLBACK(int) serialR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PDEVSERIALCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVSERIALCC);
+ RT_NOREF(fFlags);
+ AssertReturn(iLUN == 0, VERR_PDM_LUN_NOT_FOUND);
+
+ return uartR3Attach(pDevIns, &pThis->UartCore, &pThisCC->UartCore, iLUN);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDetach}
+ */
+static DECLCALLBACK(void) serialR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PDEVSERIALCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVSERIALCC);
+ RT_NOREF(fFlags);
+ AssertReturnVoid(iLUN == 0);
+
+ uartR3Detach(pDevIns, &pThis->UartCore, &pThisCC->UartCore);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) serialR3Destruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+ PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+
+ uartR3Destruct(pDevIns, &pThis->UartCore);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) serialR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PDEVSERIALCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVSERIALCC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ int rc;
+
+ Assert(iInstance < 4);
+
+ /*
+ * Validate and read the configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "IRQ|IOBase|YieldOnLSRRead|UartType", "");
+
+ bool fYieldOnLSRRead = false;
+ rc = pHlp->pfnCFGMQueryBoolDef(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 = pHlp->pfnCFGMQueryU8(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 = pHlp->pfnCFGMQueryU16(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 szUartType[32];
+ rc = pHlp->pfnCFGMQueryStringDef(pCfg, "UartType", szUartType, sizeof(szUartType), "16550A");
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: failed to read \"UartType\" as string"));
+
+ UARTTYPE enmUartType = serialR3GetUartTypeFromString(szUartType);
+ if (enmUartType != UARTTYPE_INVALID)
+ LogRel(("Serial#%d: emulating %s (IOBase: %04x IRQ: %u)\n", pDevIns->iInstance, szUartType, uIoBase, uIrq));
+ else
+ return PDMDevHlpVMSetError(pDevIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Configuration error: Invalid \"UartType\" type value: %s"), szUartType);
+
+ pThis->uIrq = uIrq;
+ pThis->PortBase = uIoBase;
+
+ /*
+ * Init locks, using explicit locking where necessary.
+ */
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Register the I/O ports.
+ */
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, uIoBase, 8 /*cPorts*/, serialIoPortWrite, serialIoPortRead,
+ "SERIAL", NULL /*paExtDescs*/, &pThis->hIoPorts);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Saved state.
+ */
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, UART_SAVED_STATE_VERSION, sizeof(*pThis), NULL,
+ NULL, serialR3LiveExec, NULL,
+ NULL, serialR3SaveExec, NULL,
+ NULL, serialR3LoadExec, serialR3LoadDone);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Init the UART core structure.
+ */
+ rc = uartR3Init(pDevIns, &pThis->UartCore, &pThisCC->UartCore, enmUartType, 0,
+ fYieldOnLSRRead ? UART_CORE_YIELD_ON_LSR_READ : 0, serialIrqReq);
+ AssertRCReturn(rc, rc);
+
+ serialR3Reset(pDevIns);
+ return VINF_SUCCESS;
+}
+
+#else /* !IN_RING3 */
+
+/**
+ * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+static DECLCALLBACK(int) serialRZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDEVSERIAL pThis = PDMDEVINS_2_DATA(pDevIns, PDEVSERIAL);
+ PDEVSERIALCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVSERIALCC);
+
+ int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, serialIoPortWrite, serialIoPortRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+
+ rc = uartRZInit(&pThisCC->UartCore, serialIrqReq);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_RING3 */
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DeviceSerialPort =
+{
+ /* .u32Version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "serial",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
+ /* .fClass = */ PDM_DEVREG_CLASS_SERIAL,
+ /* .cMaxInstances = */ UINT32_MAX,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(DEVSERIAL),
+ /* .cbInstanceCC = */ sizeof(DEVSERIALCC),
+ /* .cbInstanceRC = */ sizeof(DEVSERIALRC),
+ /* .cMaxPciDevices = */ 0,
+ /* .cMaxMsixVectors = */ 0,
+ /* .pszDescription = */ "Serial Communication Port",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "VBoxDDRC.rc",
+ /* .pszR0Mod = */ "VBoxDDR0.r0",
+ /* .pfnConstruct = */ serialR3Construct,
+ /* .pfnDestruct = */ serialR3Destruct,
+ /* .pfnRelocate = */ NULL,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ serialR3Reset,
+ /* .pfnSuspend = */ NULL,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ serialR3Attach,
+ /* .pfnDetach = */ serialR3Detach,
+ /* .pfnQueryInterface = */ NULL,
+ /* .pfnInitComplete = */ NULL,
+ /* .pfnPowerOff = */ NULL,
+ /* .pfnSoftReset = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RING0)
+ /* .pfnEarlyConstruct = */ NULL,
+ /* .pfnConstruct = */ serialRZConstruct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnFinalDestruct = */ NULL,
+ /* .pfnRequest = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RC)
+ /* .pfnConstruct = */ serialRZConstruct,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#else
+# error "Not in IN_RING3, IN_RING0 or IN_RC!"
+#endif
+ /* .u32VersionEnd = */ PDM_DEVREG_VERSION
+};
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
+