DrvSCSIHost.cpp revision ad27e1d5e48ca41245120c331cc88b50464813ce
2N/A/* $Id$ */
2N/A/** @file
2N/A * VBox storage drivers: Host SCSI access driver.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (C) 2006-2010 Oracle Corporation
2N/A *
2N/A * This file is part of VirtualBox Open Source Edition (OSE), as
2N/A * available from http://www.virtualbox.org. This file is free software;
2N/A * you can redistribute it and/or modify it under the terms of the GNU
2N/A * General Public License (GPL) as published by the Free Software
2N/A * Foundation, in version 2 as it comes in the "COPYING" file of the
2N/A * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
2N/A * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
2N/A */
2N/A
2N/A/*******************************************************************************
2N/A* Header Files *
2N/A*******************************************************************************/
2N/A//#define DEBUG
2N/A#define LOG_GROUP LOG_GROUP_DRV_SCSIHOST
2N/A#include <VBox/pdmdrv.h>
2N/A#include <VBox/pdmifs.h>
2N/A#include <VBox/pdmthread.h>
2N/A#include <VBox/scsi.h>
2N/A#include <iprt/assert.h>
2N/A#include <iprt/file.h>
2N/A#include <iprt/mem.h>
2N/A#include <iprt/req.h>
2N/A#include <iprt/string.h>
2N/A#include <iprt/uuid.h>
2N/A
2N/A#if defined(RT_OS_LINUX)
2N/A# include <limits.h>
2N/A# include <scsi/sg.h>
2N/A# include <sys/ioctl.h>
2N/A#endif
2N/A
2N/A#include "../Builtins.h"
2N/A
2N/A/**
2N/A * SCSI driver instance data.
2N/A *
2N/A * @implements PDMISCSICONNECTOR
2N/A */
2N/Atypedef struct DRVSCSIHOST
2N/A{
2N/A /** Pointer driver instance. */
2N/A PPDMDRVINS pDrvIns;
2N/A
2N/A /** Pointer to the SCSI port interface of the device above. */
2N/A PPDMISCSIPORT pDevScsiPort;
2N/A /** The SCSI connector interface . */
2N/A PDMISCSICONNECTOR ISCSIConnector;
2N/A
2N/A /** Path to the device file. */
2N/A char *pszDevicePath;
2N/A /** Handle to the device. */
2N/A RTFILE DeviceFile;
2N/A
2N/A /** The dedicated I/O thread. */
2N/A PPDMTHREAD pAsyncIOThread;
2N/A /** Queue for passing the requests to the thread. */
2N/A PRTREQQUEUE pQueueRequests;
2N/A} DRVSCSIHOST, *PDRVSCSIHOST;
2N/A
2N/A/** Converts a pointer to DRVSCSIHOST::ISCSIConnecotr to a PDRVSCSIHOST. */
2N/A#define PDMISCSICONNECTOR_2_DRVSCSIHOST(pInterface) ( (PDRVSCSIHOST)((uintptr_t)pInterface - RT_OFFSETOF(DRVSCSIHOST, ISCSIConnector)) )
2N/A
2N/A#ifdef DEBUG
2N/A/**
2N/A * Dumps a SCSI request structure for debugging purposes.
2N/A *
2N/A * @returns nothing.
2N/A * @param pRequest Pointer to the request to dump.
2N/A */
2N/Astatic void drvscsihostDumpScsiRequest(PPDMSCSIREQUEST pRequest)
2N/A{
2N/A Log(("Dump for pRequest=%#p Command: %s\n", pRequest, SCSICmdText(pRequest->pbCDB[0])));
2N/A Log(("cbCDB=%u\n", pRequest->cbCDB));
2N/A for (uint32_t i = 0; i < pRequest->cbCDB; i++)
2N/A Log(("pbCDB[%u]=%#x\n", i, pRequest->pbCDB[i]));
2N/A Log(("cbScatterGather=%u\n", pRequest->cbScatterGather));
2N/A Log(("cScatterGatherEntries=%u\n", pRequest->cScatterGatherEntries));
2N/A /* Print all scatter gather entries. */
2N/A for (uint32_t i = 0; i < pRequest->cScatterGatherEntries; i++)
2N/A {
2N/A Log(("ScatterGatherEntry[%u].cbSeg=%u\n", i, pRequest->paScatterGatherHead[i].cbSeg));
2N/A Log(("ScatterGatherEntry[%u].pvSeg=%#p\n", i, pRequest->paScatterGatherHead[i].pvSeg));
2N/A }
2N/A Log(("pvUser=%#p\n", pRequest->pvUser));
2N/A}
2N/A#endif
2N/A
2N/A/**
2N/A * Copy the content of a buffer to a scatter gather list
2N/A * copying only the amount of data which fits into the
2N/A * scatter gather list.
2N/A *
2N/A * @returns VBox status code.
2N/A * @param pRequest Pointer to the request which contains the S/G list entries.
2N/A * @param pvBuf Pointer to the buffer which should be copied.
2N/A * @param cbBuf Size of the buffer.
2N/A */
2N/Astatic int drvscsihostScatterGatherListCopyFromBuffer(PPDMSCSIREQUEST pRequest, void *pvBuf, size_t cbBuf)
2N/A{
2N/A unsigned cSGEntry = 0;
2N/A PRTSGSEG pSGEntry = &pRequest->paScatterGatherHead[cSGEntry];
2N/A uint8_t *pu8Buf = (uint8_t *)pvBuf;
2N/A
2N/A while (cSGEntry < pRequest->cScatterGatherEntries)
2N/A {
2N/A size_t cbToCopy = (cbBuf < pSGEntry->cbSeg) ? cbBuf : pSGEntry->cbSeg;
2N/A
2N/A memcpy(pSGEntry->pvSeg, pu8Buf, cbToCopy);
2N/A
2N/A cbBuf -= cbToCopy;
2N/A /* We finished. */
2N/A if (!cbBuf)
2N/A break;
2N/A
2N/A /* Advance the buffer. */
2N/A pu8Buf += cbToCopy;
2N/A
2N/A /* Go to the next entry in the list. */
2N/A pSGEntry++;
2N/A cSGEntry++;
2N/A }
2N/A
2N/A return VINF_SUCCESS;
2N/A}
2N/A
2N/A/**
2N/A * Set the sense and advanced sense key in the buffer for error conditions.
2N/A *
2N/A * @returns nothing.
2N/A * @param pRequest Pointer to the request which contains the sense buffer.
2N/A * @param uSCSISenseKey The sense key to set.
2N/A * @param uSCSIASC The advanced sense key to set.
2N/A */
2N/ADECLINLINE(void) drvscsiCmdError(PPDMSCSIREQUEST pRequest, uint8_t uSCSISenseKey, uint8_t uSCSIASC)
2N/A{
2N/A AssertMsg(pRequest->cbSenseBuffer >= 2, ("Sense buffer is not big enough\n"));
2N/A AssertMsg(pRequest->pbSenseBuffer, ("Sense buffer pointer is NULL\n"));
2N/A pRequest->pbSenseBuffer[0] = uSCSISenseKey;
2N/A pRequest->pbSenseBuffer[1] = uSCSIASC;
2N/A}
2N/A
2N/A/**
2N/A * Sets the sense key for a status good condition.
2N/A *
2N/A * @returns nothing.
2N/A * @param pRequest Pointer to the request which contains the sense buffer.
2N/A */
2N/ADECLINLINE(void) drvscsihostCmdOk(PPDMSCSIREQUEST pRequest)
2N/A{
2N/A AssertMsg(pRequest->cbSenseBuffer >= 2, ("Sense buffer is not big enough\n"));
2N/A AssertMsg(pRequest->pbSenseBuffer, ("Sense buffer pointer is NULL\n"));
2N/A pRequest->pbSenseBuffer[0] = SCSI_SENSE_NONE;
2N/A pRequest->pbSenseBuffer[1] = SCSI_ASC_NONE;
2N/A}
2N/A
2N/A/**
2N/A * Returns the transfer direction of the given command
2N/A * in case the device does not provide this info.
2N/A *
2N/A * @returns transfer direction of the command.
2N/A * SCSIHOSTTXDIR_NONE if no data is transferred.
2N/A * SCSIHOSTTXDIR_FROM_DEVICE if the data is read from the device.
2N/A * SCSIHOSTTXDIR_TO_DEVICE if the data is written to the device.
2N/A * @param uCommand The command byte.
2N/A */
2N/Astatic unsigned drvscsihostGetTransferDirectionFromCommand(uint8_t uCommand)
2N/A{
2N/A switch (uCommand)
2N/A {
2N/A case SCSI_INQUIRY:
2N/A case SCSI_REPORT_LUNS:
2N/A case SCSI_MODE_SENSE_6:
2N/A case SCSI_READ_TOC_PMA_ATIP:
2N/A case SCSI_READ_CAPACITY:
2N/A case SCSI_MODE_SENSE_10:
2N/A case SCSI_GET_EVENT_STATUS_NOTIFICATION:
2N/A case SCSI_GET_CONFIGURATION:
2N/A case SCSI_READ_10:
2N/A case SCSI_READ_12:
2N/A case SCSI_READ_BUFFER:
2N/A case SCSI_READ_BUFFER_CAPACITY:
2N/A case SCSI_READ_DISC_INFORMATION:
2N/A case SCSI_READ_DVD_STRUCTURE:
2N/A case SCSI_READ_FORMAT_CAPACITIES:
2N/A case SCSI_READ_SUBCHANNEL:
2N/A case SCSI_READ_TRACK_INFORMATION:
2N/A case SCSI_READ_CD:
2N/A case SCSI_READ_CD_MSF:
2N/A return PDMSCSIREQUESTTXDIR_FROM_DEVICE;
2N/A case SCSI_TEST_UNIT_READY:
2N/A case SCSI_PREVENT_ALLOW_MEDIUM_REMOVAL:
2N/A case SCSI_START_STOP_UNIT:
2N/A return PDMSCSIREQUESTTXDIR_NONE;
2N/A case SCSI_WRITE_10:
2N/A case SCSI_WRITE_12:
2N/A case SCSI_WRITE_BUFFER:
2N/A return PDMSCSIREQUESTTXDIR_TO_DEVICE;
2N/A default:
2N/A AssertMsgFailed(("Command not known %#x\n", uCommand));
2N/A }
2N/A
2N/A /* We should never get here in debug mode. */
2N/A AssertMsgFailed(("Impossible to get here!!!\n"));
2N/A return PDMSCSIREQUESTTXDIR_NONE; /* to make compilers happy. */
2N/A}
2N/A
2N/Astatic int drvscsihostProcessRequestOne(PDRVSCSIHOST pThis, PPDMSCSIREQUEST pRequest)
2N/A{
2N/A int rc = VINF_SUCCESS;
2N/A unsigned uTxDir;
2N/A
2N/A LogFlowFunc(("Entered\n"));
2N/A
2N/A#ifdef DEBUG
2N/A drvscsihostDumpScsiRequest(pRequest);
2N/A#endif
2N/A
2N/A /* We implement only one device. */
2N/A if (pRequest->uLogicalUnit != 0)
2N/A {
2N/A switch (pRequest->pbCDB[0])
2N/A {
2N/A case SCSI_INQUIRY:
2N/A {
2N/A SCSIINQUIRYDATA ScsiInquiryReply;
2N/A
2N/A memset(&ScsiInquiryReply, 0, sizeof(ScsiInquiryReply));
2N/A
2N/A ScsiInquiryReply.u5PeripheralDeviceType = SCSI_INQUIRY_DATA_PERIPHERAL_DEVICE_TYPE_UNKNOWN;
2N/A ScsiInquiryReply.u3PeripheralQualifier = SCSI_INQUIRY_DATA_PERIPHERAL_QUALIFIER_NOT_CONNECTED_NOT_SUPPORTED;
2N/A drvscsihostScatterGatherListCopyFromBuffer(pRequest, &ScsiInquiryReply, sizeof(SCSIINQUIRYDATA));
2N/A drvscsihostCmdOk(pRequest);
2N/A break;
2N/A }
2N/A default:
2N/A AssertMsgFailed(("Command not implemented for attached device\n"));
2N/A drvscsiCmdError(pRequest, SCSI_SENSE_ILLEGAL_REQUEST, SCSI_ASC_NONE);
2N/A }
2N/A }
2N/A else
2N/A {
2N/A#if defined(RT_OS_LINUX)
2N/A sg_io_hdr_t ScsiIoReq;
2N/A sg_iovec_t *paSG = NULL;
2N/A
2N/A /* Setup SCSI request. */
2N/A memset(&ScsiIoReq, 0, sizeof(sg_io_hdr_t));
2N/A ScsiIoReq.interface_id = 'S';
2N/A
2N/A if (pRequest->uDataDirection == PDMSCSIREQUESTTXDIR_UNKNOWN)
2N/A uTxDir = drvscsihostGetTransferDirectionFromCommand(pRequest->pbCDB[0]);
2N/A else
2N/A uTxDir = pRequest->uDataDirection;
2N/A
2N/A if (uTxDir == PDMSCSIREQUESTTXDIR_NONE)
2N/A ScsiIoReq.dxfer_direction = SG_DXFER_NONE;
2N/A else if (uTxDir == PDMSCSIREQUESTTXDIR_TO_DEVICE)
2N/A ScsiIoReq.dxfer_direction = SG_DXFER_TO_DEV;
2N/A else if (uTxDir == PDMSCSIREQUESTTXDIR_FROM_DEVICE)
2N/A ScsiIoReq.dxfer_direction = SG_DXFER_FROM_DEV;
2N/A else
2N/A AssertMsgFailed(("Invalid transfer direction %u\n", uTxDir));
2N/A
2N/A ScsiIoReq.cmd_len = pRequest->cbCDB;
2N/A ScsiIoReq.mx_sb_len = pRequest->cbSenseBuffer;
2N/A ScsiIoReq.dxfer_len = pRequest->cbScatterGather;
2N/A
2N/A if (pRequest->cScatterGatherEntries > 0)
2N/A {
2N/A if (pRequest->cScatterGatherEntries == 1)
2N/A {
2N/A ScsiIoReq.iovec_count = 0;
2N/A ScsiIoReq.dxferp = pRequest->paScatterGatherHead[0].pvSeg;
2N/A }
2N/A else
2N/A {
2N/A ScsiIoReq.iovec_count = pRequest->cScatterGatherEntries;
2N/A
2N/A paSG = (sg_iovec_t *)RTMemAllocZ(pRequest->cScatterGatherEntries * sizeof(sg_iovec_t));
2N/A AssertPtrReturn(paSG, VERR_NO_MEMORY);
2N/A
2N/A for (unsigned i = 0; i < pRequest->cScatterGatherEntries; i++)
2N/A {
2N/A paSG[i].iov_base = pRequest->paScatterGatherHead[i].pvSeg;
2N/A paSG[i].iov_len = pRequest->paScatterGatherHead[i].cbSeg;
2N/A }
2N/A ScsiIoReq.dxferp = paSG;
2N/A }
2N/A }
2N/A
2N/A ScsiIoReq.cmdp = pRequest->pbCDB;
2N/A ScsiIoReq.sbp = pRequest->pbSenseBuffer;
2N/A ScsiIoReq.timeout = UINT_MAX;
2N/A ScsiIoReq.flags |= SG_FLAG_DIRECT_IO;
2N/A
2N/A /** Issue command. */
2N/A rc = ioctl(pThis->DeviceFile, SG_IO, &ScsiIoReq);
2N/A if (rc < 0)
2N/A {
2N/A AssertMsgFailed(("Ioctl failed with rc=%d\n", rc));
2N/A }
2N/A
2N/A /* Request processed successfully. */
2N/A Log(("Command successfully processed\n"));
2N/A if (ScsiIoReq.iovec_count > 0)
2N/A RTMemFree(paSG);
2N/A#endif
2N/A }
2N/A /* Notify device that request finished. */
2N/A rc = pThis->pDevScsiPort->pfnSCSIRequestCompleted(pThis->pDevScsiPort, pRequest, SCSI_STATUS_OK, false, VINF_SUCCESS);
2N/A AssertMsgRC(rc, ("Notifying device above failed rc=%Rrc\n", rc));
2N/A
2N/A return rc;
2N/A
2N/A}
2N/A
2N/A/**
2N/A * Request function to wakeup the thread.
2N/A *
2N/A * @returns VWRN_STATE_CHANGED.
2N/A */
2N/Astatic int drvscsihostAsyncIOLoopWakeupFunc(void)
2N/A{
2N/A return VWRN_STATE_CHANGED;
2N/A}
2N/A
2N/A/**
2N/A * The thread function which processes the requests asynchronously.
2N/A *
2N/A * @returns VBox status code.
2N/A * @param pDrvIns Pointer to the device instance data.
2N/A * @param pThread Pointer to the thread instance data.
2N/A */
2N/Astatic int drvscsihostAsyncIOLoop(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
2N/A{
2N/A int rc = VINF_SUCCESS;
2N/A PDRVSCSIHOST pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSIHOST);
2N/A
2N/A LogFlowFunc(("Entering async IO loop.\n"));
2N/A
2N/A if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
2N/A return VINF_SUCCESS;
2N/A
2N/A while (pThread->enmState == PDMTHREADSTATE_RUNNING)
2N/A {
2N/A rc = RTReqProcess(pThis->pQueueRequests, RT_INDEFINITE_WAIT);
2N/A AssertMsg(rc == VWRN_STATE_CHANGED, ("Left RTReqProcess and error code is not VWRN_STATE_CHANGED rc=%Rrc\n", rc));
2N/A }
2N/A
2N/A return VINF_SUCCESS;
2N/A}
2N/A
2N/Astatic int drvscsihostAsyncIOLoopWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
2N/A{
2N/A int rc;
2N/A PDRVSCSIHOST pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSIHOST);
2N/A PRTREQ pReq;
2N/A
2N/A AssertMsgReturn(pThis->pQueueRequests, ("pQueueRequests is NULL\n"), VERR_INVALID_STATE);
2N/A
2N/A rc = RTReqCall(pThis->pQueueRequests, &pReq, 10000 /* 10 sec. */, (PFNRT)drvscsihostAsyncIOLoopWakeupFunc, 0);
2N/A AssertMsgRC(rc, ("Inserting request into queue failed rc=%Rrc\n"));
2N/A
2N/A return rc;
2N/A}
2N/A
2N/A/* -=-=-=-=- ISCSIConnector -=-=-=-=- */
2N/A
2N/A/** @copydoc PDMISCSICONNECTOR::pfnSCSIRequestSend. */
2N/Astatic DECLCALLBACK(int) drvscsihostRequestSend(PPDMISCSICONNECTOR pInterface, PPDMSCSIREQUEST pSCSIRequest)
2N/A{
2N/A int rc;
2N/A PDRVSCSIHOST pThis = PDMISCSICONNECTOR_2_DRVSCSIHOST(pInterface);
2N/A PRTREQ pReq;
2N/A
2N/A AssertMsgReturn(pThis->pQueueRequests, ("pQueueRequests is NULL\n"), VERR_INVALID_STATE);
2N/A
2N/A rc = RTReqCallEx(pThis->pQueueRequests, &pReq, 0, RTREQFLAGS_NO_WAIT, (PFNRT)drvscsihostProcessRequestOne, 2, pThis, pSCSIRequest);
2N/A AssertMsgReturn(RT_SUCCESS(rc), ("Inserting request into queue failed rc=%Rrc\n", rc), rc);
2N/A
2N/A return VINF_SUCCESS;
2N/A}
2N/A
2N/A/* -=-=-=-=- PDMIBASE -=-=-=-=- */
2N/A
2N/A/**
2N/A * @interface_method_impl{PDMIBASE,pfnQueryInterface}
2N/A */
2N/Astatic DECLCALLBACK(void *) drvscsihostQueryInterface(PPDMIBASE pInterface, const char *pszIID)
2N/A{
2N/A PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
2N/A PDRVSCSIHOST pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSIHOST);
2N/A
2N/A PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
2N/A PDMIBASE_RETURN_INTERFACE(pszIID, PDMISCSICONNECTOR, &pThis->ISCSIConnector);
2N/A return NULL;
2N/A}
2N/A
2N/A/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
2N/A
2N/A/**
2N/A * Destruct a driver instance.
2N/A *
2N/A * Most VM resources are freed by the VM. This callback is provided so that any non-VM
2N/A * resources can be freed correctly.
2N/A *
2N/A * @param pDrvIns The driver instance data.
2N/A */
2N/Astatic DECLCALLBACK(void) drvscsihostDestruct(PPDMDRVINS pDrvIns)
2N/A{
2N/A PDRVSCSIHOST pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSIHOST);
2N/A PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
2N/A
2N/A if (pThis->DeviceFile != NIL_RTFILE)
2N/A RTFileClose(pThis->DeviceFile);
2N/A
2N/A if (pThis->pszDevicePath)
2N/A MMR3HeapFree(pThis->pszDevicePath);
2N/A
2N/A if (pThis->pQueueRequests)
2N/A {
2N/A int rc = RTReqDestroyQueue(pThis->pQueueRequests);
2N/A AssertMsgRC(rc, ("Failed to destroy queue rc=%Rrc\n", rc));
2N/A }
2N/A
2N/A}
2N/A
2N/A/**
2N/A * Construct a block driver instance.
2N/A *
2N/A * @copydoc FNPDMDRVCONSTRUCT
2N/A */
2N/Astatic DECLCALLBACK(int) drvscsihostConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
2N/A{
2N/A PDRVSCSIHOST pThis = PDMINS_2_DATA(pDrvIns, PDRVSCSIHOST);
2N/A LogFlowFunc(("pDrvIns=%#p pCfg=%#p\n", pDrvIns, pCfg));
2N/A PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
2N/A
2N/A /*
2N/A * Read the configuration.
2N/A */
2N/A if (!CFGMR3AreValuesValid(pCfg, "DevicePath\0"))
2N/A return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
2N/A N_("Invalid configuration for host scsi access driver"));
2N/A
2N/A /*
2N/A * Initialize interfaces.
2N/A */
2N/A pDrvIns->IBase.pfnQueryInterface = drvscsihostQueryInterface;
2N/A pThis->ISCSIConnector.pfnSCSIRequestSend = drvscsihostRequestSend;
2N/A pThis->pDrvIns = pDrvIns;
2N/A pThis->DeviceFile = NIL_RTFILE;
2N/A
2N/A /* Query the SCSI port interface above. */
2N/A pThis->pDevScsiPort = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISCSIPORT);
2N/A AssertMsgReturn(pThis->pDevScsiPort, ("Missing SCSI port interface above\n"), VERR_PDM_MISSING_INTERFACE);
2N/A
2N/A /* Create request queue. */
2N/A int rc = RTReqCreateQueue(&pThis->pQueueRequests);
2N/A AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create request queue rc=%Rrc\n"), rc);
2N/A
2N/A /* Open the device. */
2N/A rc = CFGMR3QueryStringAlloc(pCfg, "DevicePath", &pThis->pszDevicePath);
2N/A if (RT_FAILURE(rc))
2N/A return PDMDRV_SET_ERROR(pDrvIns, rc,
2N/A N_("Configuration error: Failed to get the \"DevicePath\" value"));
2N/A
2N/A rc = RTFileOpen(&pThis->DeviceFile, pThis->pszDevicePath, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
2N/A if (RT_FAILURE(rc))
2N/A return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
2N/A N_("DrvSCSIHost#%d: Failed to open device '%s'"), pDrvIns->iInstance, pThis->pszDevicePath);
2N/A
2N/A /* Create I/O thread. */
2N/A rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pAsyncIOThread, pThis, drvscsihostAsyncIOLoop,
2N/A drvscsihostAsyncIOLoopWakeup, 0, RTTHREADTYPE_IO, "SCSI async IO");
2N/A AssertMsgReturn(RT_SUCCESS(rc), ("Failed to create async I/O thread rc=%Rrc\n"), rc);
2N/A
2N/A return VINF_SUCCESS;
2N/A}
2N/A
2N/A/**
2N/A * SCSI driver registration record.
2N/A */
2N/Aconst PDMDRVREG g_DrvSCSIHost =
2N/A{
2N/A /* u32Version */
2N/A PDM_DRVREG_VERSION,
2N/A /* szName */
2N/A "SCSIHost",
2N/A /* szRCMod */
2N/A "",
2N/A /* szR0Mod */
2N/A "",
2N/A /* pszDescription */
2N/A "Host SCSI driver.",
2N/A /* fFlags */
2N/A PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
2N/A /* fClass. */
2N/A PDM_DRVREG_CLASS_SCSI,
2N/A /* cMaxInstances */
2N/A ~0,
2N/A /* cbInstance */
2N/A sizeof(DRVSCSIHOST),
2N/A /* pfnConstruct */
2N/A drvscsihostConstruct,
2N/A /* pfnDestruct */
2N/A drvscsihostDestruct,
2N/A /* pfnRelocate */
2N/A NULL,
2N/A /* pfnIOCtl */
2N/A NULL,
2N/A /* pfnPowerOn */
2N/A NULL,
2N/A /* pfnReset */
2N/A NULL,
2N/A /* pfnSuspend */
2N/A NULL,
2N/A /* pfnResume */
2N/A NULL,
2N/A /* pfnAttach */
2N/A NULL,
2N/A /* pfnDetach */
2N/A NULL,
2N/A /* pfnPowerOff */
2N/A NULL,
2N/A /* pfnSoftReset */
2N/A NULL,
2N/A /* u32EndVersion */
2N/A PDM_DRVREG_VERSION
2N/A};
2N/A
2N/A