/**
 * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
 */
PDMBOTHCBDECL(int) drvDedicatedNicUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
{
    PDRVDEDICATEDNIC  pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp));
    STAM_PROFILE_START(&pThis->StatTransmit, a);

    AssertPtr(pSgBuf);
    Assert(pSgBuf->fFlags == (PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1));
    Assert(pSgBuf->cbUsed <= pSgBuf->cbAvailable);
#ifdef IN_RING0
    Assert(pSgBuf == &pThis->XmitSg);
#endif
    Assert(PDMCritSectIsOwner(&pThis->XmitLock));

#ifdef IN_RING0
    /*
     * Tell the driver to send the packet.
     */

    return VERR_INTERNAL_ERROR_4;

#else  /* IN_RING3 */
    /*
     * Call ring-0 to start the transfer.
     */
    int rc = PDMDrvHlpCallR0(pThis->pDrvInsR3, DRVDEDICATEDNICR0OP_SEND, pSgBuf->cbUsed);
    if (RT_FAILURE(rc) && rc != VERR_NET_DOWN)
        rc = VERR_NET_NO_BUFFER_SPACE;
    pSgBuf->fFlags = 0;
    return rc;
#endif /* IN_RING3 */
}
/**
 * @interface_method_impl{PDMIBASE,pfnReadStatus}
 */
static DECLCALLBACK(int) drvHostParallelReadStatus(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t *pfReg)
{
    PDRVHOSTPARALLEL    pThis   = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
    int rc = VINF_SUCCESS;
    int rcLnx = 0;
    uint8_t fReg = 0;
# ifndef  VBOX_WITH_WIN_PARPORT_SUP
    rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPRSTATUS, &fReg);
    if (RT_UNLIKELY(rcLnx < 0))
        rc = RTErrConvertFromErrno(errno);
    else
    {
        LogFlowFunc(("fReg=%#x\n", fReg));
        *pfReg = fReg;
    }
# else /* VBOX_WITH_WIN_PARPORT_SUP */
    *pfReg = 0; /* Intialize the buffer. */
    if (pThis->fParportAvail)
    {
        LogFlowFunc(("calling R0 to read status from parallel port\n"));
        rc = PDMDrvHlpCallR0(pThis->CTX_SUFF(pDrvIns), DRVHOSTPARALLELR0OP_READSTATUS, 0);
        AssertRC(rc);
        *pfReg = pThis->u8ReadInStatus;
    }
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
    return rc;
}
/**
 * @interface_method_impl{PDMDRVREG,pfnPowerOn}
 */
static DECLCALLBACK(void) drvR3DedicatedNicPowerOn(PPDMDRVINS pDrvIns)
{
    LogFlow(("drvR3DedicatedNicPowerOn\n"));
    PDRVDEDICATEDNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVDEDICATEDNIC);

    int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_RESUME, 0);
    AssertRC(rc);
}
/**
 * @interface_method_impl{PDMDRVREG,pfnDestruct}
 */
static DECLCALLBACK(void) drvR3DedicatedNicDestruct(PPDMDRVINS pDrvIns)
{
    LogFlow(("drvR3DedicatedNicDestruct\n"));
    PDRVDEDICATEDNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVDEDICATEDNIC);
    PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);

    if (pThis->pIfPortR0)
    {
        int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_TERM, 0);
        AssertRC(rc);;
    }
}
/**
 * @interface_method_impl{PDMIBASE,pfnRead}
 */
static DECLCALLBACK(int) drvHostParallelRead(PPDMIHOSTPARALLELCONNECTOR pInterface, void *pvBuf, size_t cbRead, PDMPARALLELPORTMODE enmMode)
{
    PDRVHOSTPARALLEL    pThis   = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
    int rc = VINF_SUCCESS;

# ifndef VBOX_WITH_WIN_PARPORT_SUP
    int rcLnx = 0;
    LogFlowFunc(("pvBuf=%#p cbRead=%d\n", pvBuf, cbRead));

    rc = drvHostParallelSetMode(pThis, enmMode);
    if (RT_FAILURE(rc))
        return rc;

    if (enmMode == PDM_PARALLEL_PORT_MODE_SPP)
    {
        /* Set the data lines directly. */
        rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPWDATA, pvBuf);
    }
    else
    {
        /* Use write interface. */
        rcLnx = read(RTFileToNative(pThis->hFileDevice), pvBuf, cbRead);
    }
    if (RT_UNLIKELY(rcLnx < 0))
        rc = RTErrConvertFromErrno(errno);
# else  /* VBOX_WITH_WIN_PARPORT_SUP */
    if (pThis->fParportAvail)
    {
        *((uint8_t*)(pvBuf)) = 0; /* Initialize the buffer. */
        for (size_t i = 0; i < cbRead; i++)
        {
            LogFlowFunc(("calling R0 to read from parallel port\n"));
            int rc = PDMDrvHlpCallR0(pThis->CTX_SUFF(pDrvIns), DRVHOSTPARALLELR0OP_READ, 0);
            AssertRC(rc);
            *((uint8_t *)pvBuf + i) = (uint8_t)pThis->u8ReadIn;
        }
    }
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
    return rc;
}
/** @copydoc PDMICHARCONNECTOR::pfnWrite */
static DECLCALLBACK(int) drvHostParallelWrite(PPDMIHOSTPARALLELCONNECTOR pInterface, const void *pvBuf, size_t cbWrite, PDMPARALLELPORTMODE enmMode)
{
    PPDMDRVINS          pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
    //PDRVHOSTPARALLEL    pThis   = PDMINS_2_DATA(pDrvIns, PDRVHOSTPARALLEL);
    PDRVHOSTPARALLEL    pThis   = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
    int rc = VINF_SUCCESS;
    int rcLnx = 0;

    LogFlowFunc(("pvBuf=%#p cbWrite=%d\n", pvBuf, cbWrite));

    rc = drvHostParallelSetMode(pThis, enmMode);
    if (RT_FAILURE(rc))
        return rc;
# ifndef VBOX_WITH_WIN_PARPORT_SUP
    if (enmMode == PDM_PARALLEL_PORT_MODE_SPP)
    {
        /* Set the data lines directly. */
        rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPWDATA, pvBuf);
    }
    else
    {
        /* Use write interface. */
        rcLnx = write(RTFileToNative(pThis->hFileDevice), pvBuf, cbWrite);
    }
    if (RT_UNLIKELY(rcLnx < 0))
        rc = RTErrConvertFromErrno(errno);
# else /* VBOX_WITH_WIN_PARPORT_SUP */
    if (pThis->fParportAvail)
    {
        for (size_t i = 0; i < cbWrite; i++)
        {
            uint64_t u64Data = (uint8_t) *((uint8_t *)(pvBuf) + i);
            LogFlowFunc(("calling R0 to write to parallel port, data=%#x\n", u64Data));
            rc = PDMDrvHlpCallR0(pThis->CTX_SUFF(pDrvIns), DRVHOSTPARALLELR0OP_WRITE, u64Data);
            AssertRC(rc);
        }
    }
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
    return rc;
}
/**
 * @interface_method_impl{PDMIBASE,pfnWriteControl}
 */
static DECLCALLBACK(int) drvHostParallelWriteControl(PPDMIHOSTPARALLELCONNECTOR pInterface, uint8_t fReg)
{
    PDRVHOSTPARALLEL    pThis   = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
    int rc = VINF_SUCCESS;
    int rcLnx = 0;

    LogFlowFunc(("fReg=%#x\n", fReg));
# ifndef VBOX_WITH_WIN_PARPORT_SUP
    rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPWCONTROL, &fReg);
    if (RT_UNLIKELY(rcLnx < 0))
        rc = RTErrConvertFromErrno(errno);
# else /* VBOX_WITH_WIN_PARPORT_SUP */
    uint64_t u64Data;
    u64Data = (uint8_t)fReg;
    if (pThis->fParportAvail)
    {
        LogFlowFunc(("calling R0 to write CTRL, data=%#x\n", u64Data));
        rc = PDMDrvHlpCallR0(pThis->CTX_SUFF(pDrvIns), DRVHOSTPARALLELR0OP_WRITECONTROL, u64Data);
        AssertRC(rc);
    }
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
    return rc;
}
static DECLCALLBACK(int) drvHostParallelSetPortDirection(PPDMIHOSTPARALLELCONNECTOR pInterface, bool fForward)
{
    PDRVHOSTPARALLEL    pThis   = RT_FROM_MEMBER(pInterface, DRVHOSTPARALLEL, CTX_SUFF(IHostParallelConnector));
    int rc = VINF_SUCCESS;
    int iMode = 0;
    if (!fForward)
        iMode = 1;
# ifndef VBOX_WITH_WIN_PARPORT_SUP
    int rcLnx = ioctl(RTFileToNative(pThis->hFileDevice), PPDATADIR, &iMode);
    if (RT_UNLIKELY(rcLnx < 0))
        rc = RTErrConvertFromErrno(errno);

# else /* VBOX_WITH_WIN_PARPORT_SUP */
    uint64_t u64Data;
    u64Data = (uint8_t)iMode;
    if (pThis->fParportAvail)
    {
        LogFlowFunc(("calling R0 to write CTRL, data=%#x\n", u64Data));
        rc = PDMDrvHlpCallR0(pThis->CTX_SUFF(pDrvIns), DRVHOSTPARALLELR0OP_SETPORTDIRECTION, u64Data);
        AssertRC(rc);
    }
# endif /* VBOX_WITH_WIN_PARPORT_SUP */
    return rc;
}