UsbMsd.cpp revision accd9723a56a3efd464569c7db70e2693825a96c
/* $Id$ */
/** @file
* UsbMSD - USB Mass Storage Device Emulation.
*/
/*
* Copyright (C) 2007-2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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_USB_MSD
#include <iprt/critsect.h>
#include <iprt/semaphore.h>
#include "VBoxDD.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** @name USB MSD string IDs
* @{ */
#define USBMSD_STR_ID_MANUFACTURER 1
#define USBMSD_STR_ID_PRODUCT_HD 2
#define USBMSD_STR_ID_PRODUCT_CDROM 3
/** @} */
/** @name USB MSD vendor and product IDs
* @{ */
#define VBOX_USB_VENDOR 0x80EE
#define USBMSD_PID_HD 0x0030
#define USBMSD_PID_CD 0x0031
/** @} */
/** Saved state version. */
#define USB_MSD_SAVED_STATE_VERSION 1
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* USB MSD Command Block Wrapper or CBW. The command block
* itself (CBWCB) contains protocol-specific data (here SCSI).
*/
#pragma pack(1)
typedef struct USBCBW
{
#define USBCBW_DIR_OUT 0
} USBCBW;
#pragma pack()
/** Pointer to a Command Block Wrapper. */
/** Pointer to a const Command Block Wrapper. */
/**
* USB MSD Command Status Wrapper or CSW.
*/
#pragma pack(1)
typedef struct USBCSW
{
#define USBCSW_STATUS_OK UINT8_C(0)
} USBCSW;
#pragma pack()
/** Pointer to a Command Status Wrapper. */
/** Pointer to a const Command Status Wrapper. */
/**
* The USB MSD request state.
*/
typedef enum USBMSDREQSTATE
{
/** Invalid status. */
/** Ready to receive a new SCSI command. */
/** Waiting for the host to supply data. */
/** The SCSI request is being executed by the driver. */
/** Have (more) data for the host. */
/** Waiting to supply status information to the host. */
/** Destroy the request upon completion.
* This is set when the SCSI request doesn't complete before for the device or
* mass storage reset operation times out. USBMSD::pReq will be set to NULL
* and the only reference to this request will be with DrvSCSI. */
/** The end of the valid states. */
/** 32bit blow up hack. */
USBMSDREQSTATE_32BIT_HACK = 0x7fffffff
/**
* A pending USB MSD request.
*/
typedef struct USBMSDREQ
{
/** The state of the request. */
/** The size of the data buffer. */
/** Pointer to the data buffer. */
/** Current buffer offset. */
/** The current Cbw when we're in the pending state. */
/** The current SCSI request. */
/** The scatter-gather segment used by ScsiReq for describing pbBuf. */
/** The sense buffer for the current SCSI request. */
/** The status of a completed SCSI request. */
int iScsiReqStatus;
/** Set if the request structure must be destroyed when the SCSI driver
* completes it. This is used to deal with requests that runs while the
* device is being reset. */
bool fDestoryOnCompletion;
/** Pointer to the USB device instance owning it. */
} USBMSDREQ;
/** Pointer to a USB MSD request. */
typedef USBMSDREQ *PUSBMSDREQ;
/**
* Endpoint status data.
*/
typedef struct USBMSDEP
{
bool fHalted;
} USBMSDEP;
/** Pointer to the endpoint status. */
/**
* A URB queue.
*/
typedef struct USBMSDURBQUEUE
{
/** The head pointer. */
/** Where to insert the next entry. */
/** Pointer to a URB queue. */
typedef USBMSDURBQUEUE *PUSBMSDURBQUEUE;
/** Pointer to a const URB queue. */
typedef USBMSDURBQUEUE const *PCUSBMSDURBQUEUE;
/**
* The USB MSD instance data.
*/
typedef struct USBMSD
{
/** Pointer back to the PDM USB Device instance structure. */
/** Critical section protecting the device state. */
/** The current configuration.
* (0 - default, 1 - the only, i.e configured.) */
#if 0
/** The state of the MSD (state machine).*/
#endif
/** Endpoint 0 is the default control pipe, 1 is the host->dev bulk pipe and 2
* is the dev->host one. */
/** The current request. */
/** Pending to-host queue.
* The URBs waiting here are pending the completion of the current request and
* data or status to become available.
*/
/** Done queue
* The URBs stashed here are waiting to be reaped. */
/** Signalled when adding an URB to the done queue and fHaveDoneQueueWaiter
* is set. */
/** Someone is waiting on the done queue. */
bool fHaveDoneQueueWaiter;
/** Whether to signal the reset semaphore when the current request completes. */
bool fSignalResetSem;
/** Semaphore usbMsdUsbReset waits on when a request is executing at reset
* time. Only signalled when fSignalResetSem is set. */
/** The reset URB.
* This is waiting for SCSI request completion before finishing the reset. */
/** Indicates that PDMUsbHlpAsyncNotificationCompleted should be called when
* the MSD is entering the idle state. */
volatile bool fSignalIdle;
/**
* LUN\#0 data.
*/
struct
{
/** The base interface for LUN\#0. */
/** The SCSI port interface for LUN\#0 */
/** The base interface for the SCSI driver connected to LUN\#0. */
/** The SCSI connector interface for the SCSI driver connected to LUN\#0. */
} Lun0;
} USBMSD;
/** Pointer to the USB MSD instance data. */
/*******************************************************************************
* Global Variables *
*******************************************************************************/
static const PDMUSBDESCCACHESTRING g_aUsbMsdStrings_en_US[] =
{
{ USBMSD_STR_ID_MANUFACTURER, "VirtualBox" },
{ USBMSD_STR_ID_PRODUCT_HD, "USB Harddisk" },
{ USBMSD_STR_ID_PRODUCT_CDROM, "USB CD-ROM" }
};
static const PDMUSBDESCCACHELANG g_aUsbMsdLanguages[] =
{
};
{
{
{
/* .bLength = */ sizeof(VUSBDESCENDPOINT),
/* .bDescriptorType = */ VUSB_DT_ENDPOINT,
/* .bEndpointAddress = */ 0x81 /* ep=1, in */,
/* .bmAttributes = */ 2 /* bulk */,
/* .wMaxPacketSize = */ 64 /* maximum possible */,
/* .bInterval = */ 0 /* not applicable for bulk EP */
},
/* .pvMore = */ NULL,
/* .pvClass = */ NULL,
/* .cbClass = */ 0
},
{
{
/* .bLength = */ sizeof(VUSBDESCENDPOINT),
/* .bDescriptorType = */ VUSB_DT_ENDPOINT,
/* .bEndpointAddress = */ 0x02 /* ep=2, out */,
/* .bmAttributes = */ 2 /* bulk */,
/* .wMaxPacketSize = */ 64 /* maximum possible */,
/* .bInterval = */ 0 /* not applicable for bulk EP */
},
/* .pvMore = */ NULL,
/* .pvClass = */ NULL,
/* .cbClass = */ 0
}
};
{
{
{
/* .bLength = */ sizeof(VUSBDESCENDPOINT),
/* .bDescriptorType = */ VUSB_DT_ENDPOINT,
/* .bEndpointAddress = */ 0x81 /* ep=1, in */,
/* .bmAttributes = */ 2 /* bulk */,
/* .wMaxPacketSize = */ 512 /* HS bulk packet size */,
/* .bInterval = */ 0 /* no NAKs */
},
/* .pvMore = */ NULL,
/* .pvClass = */ NULL,
/* .cbClass = */ 0
},
{
{
/* .bLength = */ sizeof(VUSBDESCENDPOINT),
/* .bDescriptorType = */ VUSB_DT_ENDPOINT,
/* .bEndpointAddress = */ 0x02 /* ep=2, out */,
/* .bmAttributes = */ 2 /* bulk */,
/* .wMaxPacketSize = */ 512 /* HS bulk packet size */,
/* .bInterval = */ 0 /* no NAKs */
},
/* .pvMore = */ NULL,
/* .pvClass = */ NULL,
/* .cbClass = */ 0
}
};
static const VUSBDESCSSEPCOMPANION g_aUsbMsdEpCompanionSS =
{
/* .bLength = */ sizeof(VUSBDESCSSEPCOMPANION),
/* .bDescriptorType = */ VUSB_DT_SS_ENDPOINT_COMPANION,
/* .bMaxBurst = */ 15 /* we can burst all the way */,
/* .bmAttributes = */ 0 /* no streams */,
/* .wBytesPerInterval = */ 0 /* not a periodic endpoint */
};
{
{
{
/* .bLength = */ sizeof(VUSBDESCENDPOINT),
/* .bDescriptorType = */ VUSB_DT_ENDPOINT,
/* .bEndpointAddress = */ 0x81 /* ep=1, in */,
/* .bmAttributes = */ 2 /* bulk */,
/* .wMaxPacketSize = */ 1024 /* SS bulk packet size */,
/* .bInterval = */ 0 /* no NAKs */
},
/* .pvMore = */ NULL,
/* .pvClass = */ &g_aUsbMsdEpCompanionSS,
/* .cbClass = */ sizeof(g_aUsbMsdEpCompanionSS)
},
{
{
/* .bLength = */ sizeof(VUSBDESCENDPOINT),
/* .bDescriptorType = */ VUSB_DT_ENDPOINT,
/* .bEndpointAddress = */ 0x02 /* ep=2, out */,
/* .bmAttributes = */ 2 /* bulk */,
/* .wMaxPacketSize = */ 1024 /* SS bulk packet size */,
/* .bInterval = */ 0 /* no NAKs */
},
/* .pvMore = */ NULL,
/* .pvClass = */ &g_aUsbMsdEpCompanionSS,
/* .cbClass = */ sizeof(g_aUsbMsdEpCompanionSS)
}
};
static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDescFS =
{
{
/* .bLength = */ sizeof(VUSBDESCINTERFACE),
/* .bDescriptorType = */ VUSB_DT_INTERFACE,
/* .bInterfaceNumber = */ 0,
/* .bAlternateSetting = */ 0,
/* .bNumEndpoints = */ 2,
/* .bInterfaceClass = */ 8 /* Mass Storage */,
/* .bInterfaceSubClass = */ 6 /* SCSI transparent command set */,
/* .bInterfaceProtocol = */ 0x50 /* Bulk-Only Transport */,
/* .iInterface = */ 0
},
/* .pvMore = */ NULL,
/* .pvClass = */ NULL,
/* .cbClass = */ 0,
/* .pIAD = */ NULL,
/* .cbIAD = */ 0
};
static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDescHS =
{
{
/* .bLength = */ sizeof(VUSBDESCINTERFACE),
/* .bDescriptorType = */ VUSB_DT_INTERFACE,
/* .bInterfaceNumber = */ 0,
/* .bAlternateSetting = */ 0,
/* .bNumEndpoints = */ 2,
/* .bInterfaceClass = */ 8 /* Mass Storage */,
/* .bInterfaceSubClass = */ 6 /* SCSI transparent command set */,
/* .bInterfaceProtocol = */ 0x50 /* Bulk-Only Transport */,
/* .iInterface = */ 0
},
/* .pvMore = */ NULL,
/* .pvClass = */ NULL,
/* .cbClass = */ 0,
/* .pIAD = */ NULL,
/* .cbIAD = */ 0
};
static const VUSBDESCINTERFACEEX g_UsbMsdInterfaceDescSS =
{
{
/* .bLength = */ sizeof(VUSBDESCINTERFACE),
/* .bDescriptorType = */ VUSB_DT_INTERFACE,
/* .bInterfaceNumber = */ 0,
/* .bAlternateSetting = */ 0,
/* .bNumEndpoints = */ 2,
/* .bInterfaceClass = */ 8 /* Mass Storage */,
/* .bInterfaceSubClass = */ 6 /* SCSI transparent command set */,
/* .bInterfaceProtocol = */ 0x50 /* Bulk-Only Transport */,
/* .iInterface = */ 0
},
/* .pvMore = */ NULL,
/* .pvClass = */ NULL,
/* .cbClass = */ 0,
/* .pIAD = */ NULL,
/* .cbIAD = */ 0
};
static const VUSBINTERFACE g_aUsbMsdInterfacesFS[] =
{
};
static const VUSBINTERFACE g_aUsbMsdInterfacesHS[] =
{
};
static const VUSBINTERFACE g_aUsbMsdInterfacesSS[] =
{
};
static const VUSBDESCCONFIGEX g_UsbMsdConfigDescFS =
{
{
/* .bLength = */ sizeof(VUSBDESCCONFIG),
/* .bDescriptorType = */ VUSB_DT_CONFIG,
/* .wTotalLength = */ 0 /* recalculated on read */,
/* .bConfigurationValue =*/ 1,
/* .iConfiguration = */ 0,
/* .MaxPower = */ 50 /* 100mA */
},
NULL, /* pvMore */
NULL /* pvOriginal */
};
static const VUSBDESCCONFIGEX g_UsbMsdConfigDescHS =
{
{
/* .bLength = */ sizeof(VUSBDESCCONFIG),
/* .bDescriptorType = */ VUSB_DT_CONFIG,
/* .wTotalLength = */ 0 /* recalculated on read */,
/* .bConfigurationValue =*/ 1,
/* .iConfiguration = */ 0,
/* .MaxPower = */ 50 /* 100mA */
},
NULL, /* pvMore */
NULL /* pvOriginal */
};
static const VUSBDESCCONFIGEX g_UsbMsdConfigDescSS =
{
{
/* .bLength = */ sizeof(VUSBDESCCONFIG),
/* .bDescriptorType = */ VUSB_DT_CONFIG,
/* .wTotalLength = */ 0 /* recalculated on read */,
/* .bConfigurationValue =*/ 1,
/* .iConfiguration = */ 0,
/* .MaxPower = */ 50 /* 100mA */
},
NULL, /* pvMore */
NULL /* pvOriginal */
};
static const VUSBDESCDEVICE g_UsbMsdDeviceDesc20 =
{
/* .bLength = */ sizeof(g_UsbMsdDeviceDesc20),
/* .bDescriptorType = */ VUSB_DT_DEVICE,
/* .bcdUsb = */ 0x200, /* USB 2.0 */
/* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
/* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
/* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
/* .bMaxPacketSize0 = */ 64,
/* .idVendor = */ VBOX_USB_VENDOR,
/* .idProduct = */ USBMSD_PID_HD,
/* .bcdDevice = */ 0x0100, /* 1.0 */
/* .iManufacturer = */ USBMSD_STR_ID_MANUFACTURER,
/* .iProduct = */ USBMSD_STR_ID_PRODUCT_HD,
/* .iSerialNumber = */ 0,
/* .bNumConfigurations = */ 1
};
static const VUSBDESCDEVICE g_UsbMsdDeviceDesc30 =
{
/* .bLength = */ sizeof(g_UsbMsdDeviceDesc30),
/* .bDescriptorType = */ VUSB_DT_DEVICE,
/* .bcdUsb = */ 0x300, /* USB 2.0 */
/* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
/* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
/* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
/* .bMaxPacketSize0 = */ 9 /* 512, the only option for USB3. */,
/* .idVendor = */ VBOX_USB_VENDOR,
/* .idProduct = */ USBMSD_PID_HD,
/* .bcdDevice = */ 0x0110, /* 1.10 */
/* .iManufacturer = */ USBMSD_STR_ID_MANUFACTURER,
/* .iProduct = */ USBMSD_STR_ID_PRODUCT_HD,
/* .iSerialNumber = */ 0,
/* .bNumConfigurations = */ 1
};
static const VUSBDEVICEQUALIFIER g_UsbMsdDeviceQualifier =
{
/* .bLength = */ sizeof(g_UsbMsdDeviceQualifier),
/* .bDescriptorType = */ VUSB_DT_DEVICE_QUALIFIER,
/* .bcdUsb = */ 0x200, /* USB 2.0 */
/* .bDeviceClass = */ 0 /* Class specified in the interface desc. */,
/* .bDeviceSubClass = */ 0 /* Subclass specified in the interface desc. */,
/* .bDeviceProtocol = */ 0 /* Protocol specified in the interface desc. */,
/* .bMaxPacketSize0 = */ 64,
/* .bNumConfigurations = */ 1,
/* .bReserved = */ 0
};
static const struct {
} g_UsbMsdBOS =
{
{
/* .bDescriptorType = */ VUSB_DT_BOS,
/* .wTotalLength = */ sizeof(g_UsbMsdBOS),
/* .bNumDeviceCaps = */ 1
},
{
/* .bLength = */ sizeof(VUSBDESCSSDEVCAP),
/* .bDescriptorType = */ VUSB_DT_DEVICE_CAPABILITY,
/* .bDevCapabilityType = */ VUSB_DCT_SUPERSPEED_USB,
/* .bmAttributes = */ 0 /* No LTM. */,
/* .wSpeedsSupported = */ 0xe /* Any speed is good. */,
/* .bFunctionalitySupport = */ 2 /* Want HS at least. */,
/* .bU1DevExitLat = */ 0, /* We are blazingly fast. */
/* .wU2DevExitLat = */ 0
}
};
static const PDMUSBDESCCACHE g_UsbMsdDescCacheFS =
{
/* .pDevice = */ &g_UsbMsdDeviceDesc20,
/* .paConfigs = */ &g_UsbMsdConfigDescFS,
/* .paLanguages = */ g_aUsbMsdLanguages,
/* .fUseCachedDescriptors = */ true,
/* .fUseCachedStringsDescriptors = */ true
};
static const PDMUSBDESCCACHE g_UsbMsdDescCacheHS =
{
/* .pDevice = */ &g_UsbMsdDeviceDesc20,
/* .paConfigs = */ &g_UsbMsdConfigDescHS,
/* .paLanguages = */ g_aUsbMsdLanguages,
/* .fUseCachedDescriptors = */ true,
/* .fUseCachedStringsDescriptors = */ true
};
static const PDMUSBDESCCACHE g_UsbMsdDescCacheSS =
{
/* .pDevice = */ &g_UsbMsdDeviceDesc30,
/* .paConfigs = */ &g_UsbMsdConfigDescSS,
/* .paLanguages = */ g_aUsbMsdLanguages,
/* .fUseCachedDescriptors = */ true,
/* .fUseCachedStringsDescriptors = */ true
};
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Initializes an URB queue.
*
* @param pQueue The URB queue.
*/
{
}
/**
* Inserts an URB at the end of the queue.
*
* @param pQueue The URB queue.
* @param pUrb The URB to insert.
*/
{
}
/**
* Unlinks the head of the queue and returns it.
*
* @returns The head entry.
* @param pQueue The URB queue.
*/
{
if (pUrb)
{
if (!pNext)
else
}
return pUrb;
}
/**
* Removes an URB from anywhere in the queue.
*
* @returns true if found, false if not.
* @param pQueue The URB queue.
* @param pUrb The URB to remove.
*/
{
else
{
while (pCur)
{
{
break;
}
}
if (!pCur)
return false;
}
return true;
}
/**
* Checks if the queue is empty or not.
*
* @returns true if it is, false if it isn't.
* @param pQueue The URB queue.
*/
{
}
/**
* Links an URB into the done queue.
*
* @param pThis The MSD instance.
* @param pUrb The URB.
*/
{
if (pThis->fHaveDoneQueueWaiter)
{
}
}
/**
* Allocates a new request and does basic init.
*
* @returns Pointer to the new request. NULL if we're out of memory.
* @param pUsbIns The instance allocating it.
*/
{
if (pReq)
{
}
else
LogRel(("usbMsdReqAlloc: Out of memory\n"));
return pReq;
}
/**
* Frees a request.
*
* @param pReq The request.
*/
{
/*
* Check the input.
*/
/*
* Invalidate it and free the associated resources.
*/
{
}
}
/**
* Prepares a request for execution or data buffering.
*
* @param pReq The request.
* @param pCbw The SCSI command block wrapper.
*/
{
/* Copy the CBW */
/* Setup the SCSI request. */
}
/**
* Makes sure that there is sufficient buffer space available.
*
* @param pReq
* @param cbBuf The required buffer space.
*/
{
else
{
return false;
}
return true;
}
/**
* Completes the URB with a stalled state, halting the pipe.
*/
{
Log(("usbMsdCompleteStall/#%u: pUrb=%p:%s: %s\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, pszWhy));
/** @todo figure out if the stall is global or pipe-specific or both. */
if (pEp)
else
{
}
return VINF_SUCCESS;
}
/**
* Completes the URB with a OK state.
*/
{
Log(("usbMsdCompleteOk/#%u: pUrb=%p:%s cbData=%#zx\n", pThis->pUsbIns->iInstance, pUrb, pUrb->pszDesc, cbData));
return VINF_SUCCESS;
}
/**
* Reset worker for usbMsdUsbReset, usbMsdUsbSetConfiguration and
* usbMsdUrbHandleDefaultPipe.
*
* @returns VBox status code.
* @param pThis The MSD instance.
* @param pUrb Set when usbMsdUrbHandleDefaultPipe is the
* caller.
* @param fSetConfig Set when usbMsdUsbSetConfiguration is the
* caller.
*/
{
/*
* Wait for the any command currently executing to complete before
* resetting. (We cannot cancel its execution.) How we do this depends
* on the reset method.
*/
if ( pReq
{
/* Don't try to deal with the set config variant nor multiple build-only
mass storage resets. */
{
Log(("usbMsdResetWorker: pResetUrb is already %p:%s - stalling\n", pThis->pResetUrb, pThis->pResetUrb->pszDesc));
}
/* Bulk-Only Mass Storage Reset: Complete the reset on request completion. */
if (pUrb)
{
Log(("usbMsdResetWorker: Setting pResetUrb to %p:%s\n", pThis->pResetUrb, pThis->pResetUrb->pszDesc));
return VINF_SUCCESS;
}
/* Device reset: Wait for up to 10 ms. If it doesn't work, ditch
whoe the request structure. We'll allocate a new one when needed. */
Log(("usbMsdResetWorker: Waiting for completion...\n"));
pThis->fSignalResetSem = true;
pThis->fSignalResetSem = false;
if ( RT_FAILURE(rc)
{
}
}
/*
* Reset the request and device state.
*/
if (pReq)
{
}
/*
* Ditch all pending URBs.
*/
{
}
if (pCurUrb)
{
}
if (pUrb)
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMISCSIPORT,pfnSCSIRequestCompleted}
*/
static DECLCALLBACK(int) usbMsdLun0ScsiRequestCompleted(PPDMISCSIPORT pInterface, PPDMSCSIREQUEST pSCSIRequest,
{
Log(("usbMsdLun0ScsiRequestCompleted: pReq=%p dCBWTag=%#x iScsiReqStatus=%u \n", pReq, pReq->Cbw.dCBWTag, rcCompletion));
{
/*
* Advance the state machine. The state machine is not affected by
* SCSI errors.
*/
{
Log(("usbMsdLun0ScsiRequestCompleted: Entering STATUS\n"));
}
else
{
Log(("usbMsdLun0ScsiRequestCompleted: Entering DATA_TO_HOST\n"));
}
/*
* Deal with pending to-host URBs.
*/
for (;;)
{
if (!pUrb)
break;
/* Process it the normal way. */
}
}
else
{
}
if (pThis->fSignalResetSem)
{
}
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMISCSIPORT,pfnQueryDeviceLocation}
*/
static DECLCALLBACK(int) usbMsdLun0QueryDeviceLocation(PPDMISCSIPORT pInterface, const char **ppcszController,
{
*piLUN = 0;
return VINF_SUCCESS;
}
/**
* @interface_method_impl{PDMIBASE,pfnQueryInterface}
*/
{
return NULL;
}
/**
* Checks if all asynchronous I/O is finished.
*
* Used by usbMsdVMReset, usbMsdVMSuspend and usbMsdVMPowerOff.
*
* @returns true if quiesced, false if busy.
* @param pUsbIns The USB device instance.
*/
{
return false;
return true;
}
/**
* @callback_method_impl{FNPDMDEVASYNCNOTIFY,
* Callback employed by usbMsdVMSuspend and usbMsdVMPowerOff.}
*/
{
return false;
return true;
}
/**
* Common worker for usbMsdVMSuspend and usbMsdVMPowerOff.
*/
{
else
}
/* -=-=-=-=- Saved State -=-=-=-=- */
/**
* @copydoc FNUSBSSMSAVEPREP
*/
{
return VINF_SUCCESS;
}
/**
* @copydoc FNUSBSSMLOADPREP
*/
{
return VINF_SUCCESS;
}
/**
* @copydoc FNUSBSSMLIVEEXEC
*/
{
/* config. */
return VINF_SSM_DONT_CALL_AGAIN;
}
/**
* @copydoc FNUSBSSMSAVEEXEC
*/
{
int rc;
/* The config */
{
{
}
}
}
/**
* @copydoc FNUSBSSMLOADEXEC
*/
static DECLCALLBACK(int) usbMsdLoadExec(PPDMUSBINS pUsbIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
int rc;
/* Verify config. */
bool fInUse;
N_("The %s VM is missing a USB mass storage device. Please make sure the source and target VMs have compatible storage configurations"),
if (uPass == SSM_PASS_FINAL)
{
/* Restore data. */
bool fReqAlloc = false;
if (fReqAlloc)
{
if (pReq)
{
if (cbBuf)
{
{
}
else
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS(rc))
{
/* Setup the rest of the SCSI request. */
}
}
else
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS(rc))
if (RT_FAILURE(rc))
return rc;
}
return VINF_SUCCESS;
}
/**
* @copydoc PDMUSBREG::pfnUrbReap
*/
{
{
/* Wait */
pThis->fHaveDoneQueueWaiter = true;
pThis->fHaveDoneQueueWaiter = false;
}
if (pUrb)
return pUrb;
}
/**
* @copydoc PDMUSBREG::pfnWakeup
*/
{
}
/**
* @copydoc PDMUSBREG::pfnUrbCancel
*/
{
/*
* Remove the URB from the to-host queue and move it onto the done queue.
*/
return VINF_SUCCESS;
}
/**
* Fails an illegal SCSI request.
*
* @returns VBox status code.
* @param pThis The MSD instance data.
* @param pReq The MSD request.
* @param bAsc The ASC for the SCSI_SENSE_ILLEGAL_REQUEST.
* @param bAscq The ASC qualifier.
* @param pszWhy For logging why.
*/
static int usbMsdScsiIllegalRequest(PUSBMSD pThis, PUSBMSDREQ pReq, uint8_t bAsc, uint8_t bAscq, const char *pszWhy)
{
usbMsdLun0ScsiRequestCompleted(&pThis->Lun0.IScsiPort, &pReq->ScsiReq, SCSI_STATUS_CHECK_CONDITION, false, VINF_SUCCESS);
return VINF_SUCCESS;
}
/**
* The SCSI driver doesn't handle SCSI_REQUEST_SENSE but instead
* returns the sense info with the request.
*
*/
{
/* validation */
/* If the previous command succeeded successfully, whip up some sense data. */
&& pReq->ScsiReqSense[0] == 0)
{
#if 0 /** @todo something upsets linux about this stuff. Needs investigation. */
#endif
}
/* Copy the data into the result buffer. */
Log(("usbMsd: SCSI_REQUEST_SENSE - CBWCB[4]=%#x iOldState=%d, %u bytes, raw: %.*Rhxs\n",
pCbw->CBWCB[4], pReq->iScsiReqStatus, pCbw->dCBWDataTransferLength, RT_MAX(1, cbCopy), pReq->ScsiReqSense));
/* Do normal completion. */
usbMsdLun0ScsiRequestCompleted(&pThis->Lun0.IScsiPort, &pReq->ScsiReq, SCSI_STATUS_OK, false, VINF_SUCCESS);
return VINF_SUCCESS;
}
/**
* Wrapper around PDMISCSICONNECTOR::pfnSCSIRequestSend that deals with
* SCSI_REQUEST_SENSE.
*
* @returns VBox status code.
* @param pThis The MSD instance data.
* @param pReq The MSD request.
* @param pszCaller Where we're called from.
*/
{
{
case SCSI_REQUEST_SENSE:
{
}
default:
return pThis->Lun0.pIScsiConnector->pfnSCSIRequestSend(pThis->Lun0.pIScsiConnector, &pReq->ScsiReq);
}
}
/**
* Validates a SCSI request before passing it down to the SCSI driver.
*
* @returns true / false. The request will be completed on failure.
* @param pThis The MSD instance data.
* @param pCbw The USB command block wrapper.
* @param pUrb The URB.
*/
{
{
case SCSI_REQUEST_SENSE:
/** @todo validate this. */
return true;
default:
return true;
}
}
/**
* Handle requests sent to the outbound (to device) bulk pipe.
*/
{
/*
* Stall the request if the pipe is halted.
*/
/*
* Deal with the URB according to the current state.
*/
switch (enmState)
{
case USBMSDREQSTATE_STATUS:
LogFlow(("usbMsdHandleBulkHostToDev: Skipping pending status.\n"));
/* fall thru */
/*
* We're ready to receive a command. Start off by validating the
* incoming request.
*/
case USBMSDREQSTATE_READY:
{
{
}
{
}
Log(("usbMsd: CBW: dCBWTag=%#x dCBWDataTransferLength=%#x bmCBWFlags=%#x bCBWLun=%#x bCBWCBLength=%#x cbData=%#x fShortNotOk=%RTbool\n",
pCbw->dCBWTag, pCbw->dCBWDataTransferLength, pCbw->bmCBWFlags, pCbw->bCBWLun, pCbw->bCBWCBLength, pUrb->cbData, pUrb->fShortNotOk));
{
}
{
}
if (pCbw->bCBWCBLength == 0)
{
}
{
Log(("usbMsd: CBW: Mismatching cbData and bCBWCBLength values: %#x vs. %#x (%#x)\n",
}
{
Log(("usbMsd: CBW: dCBWDataTransferLength is too large: %#x (%u)\n",
}
return VINF_SUCCESS;
/*
* Make sure we've got a request and a sufficient buffer space.
*
* Note! This will make sure the buffer is ZERO as well, thus
* saving us the trouble of clearing the output buffer on
* failure later.
*/
if (!pReq)
{
if (!pReq)
}
/*
* Special case REQUEST SENSE requests, usbMsdReqPrepare will
* trash the sense data otherwise.
*/
else
{
/*
* Prepare the request. Kick it off right away if possible.
*/
{
if (RT_FAILURE(rc))
{
}
}
else
{
Log(("usbMsdHandleBulkHostToDev: Entering DATA_FROM_HOST.\n"));
}
}
}
/*
* Stuff the data into the buffer.
*/
{
{
Log(("usbMsd: Too much data: cbData=%#x offBuf=%#x dCBWDataTransferLength=%#x cbLeft=%#x\n",
}
{
if (RT_FAILURE(rc))
{
}
}
}
/*
* Bad state, stall.
*/
case USBMSDREQSTATE_EXECUTING:
default:
}
}
/**
* Handle requests sent to the inbound (to host) bulk pipe.
*/
{
/*
* Stall the request if the pipe is halted OR if there is no
* pending request yet.
*/
/*
* Deal with the URB according to the state.
*/
{
/*
* We've data left to transfer to the host.
*/
{
else if (pUrb->fShortNotOk)
{
Log(("usbMsd: Requested more data that we've got; cbData=%#x offBuf=%#x dCBWDataTransferLength=%#x cbLeft=%#x\n",
}
{
Log(("usbMsdHandleBulkDevToHost: Entering STATUS\n"));
}
}
/*
* Status transfer.
*/
case USBMSDREQSTATE_STATUS:
{
{
Log(("usbMsd: Unexpected status request size: %#x (expected %#x), fShortNotOK=%RTbool\n", pUrb->cbData, sizeof(USBCSW), pUrb->fShortNotOk));
}
/* Enter a CSW into the URB data buffer. */
: pReq->iScsiReqStatus >= 0
/** @todo the following is not always accurate; VSCSI needs
* to implement residual counts properly! */
else
? 0
Log(("usbMsd: CSW: dCSWTag=%#x bCSWStatus=%d dCSWDataResidue=%#x\n",
Log(("usbMsdHandleBulkDevToHost: Entering READY\n"));
}
/*
* Status request before we've received all (or even any) data.
* Linux 2.4.31 does this sometimes. The recommended behavior is to
* to accept the current data amount and execute the request. (The
* alternative behavior is to stall.)
*/
{
{
}
/* Adjust the request and kick it off. Special case the no-data
case since the SCSI driver doesn't like that. */
{
Log(("usbMsdHandleBulkDevToHost: Entering EXECUTING (offBuf=0x0).\n"));
usbMsdLun0ScsiRequestCompleted(&pThis->Lun0.IScsiPort, &pReq->ScsiReq, SCSI_STATUS_OK, false, VINF_SUCCESS);
return VINF_SUCCESS;
}
if (RT_FAILURE(rc))
{
}
/* fall thru */
}
/*
* The SCSI command is still pending, queue the URB awaiting its
* completion.
*/
case USBMSDREQSTATE_EXECUTING:
return VINF_SUCCESS;
/*
* Bad states, stall.
*/
case USBMSDREQSTATE_READY:
default:
}
}
/**
* Handles request send to the default control pipe.
*/
{
{
{
case VUSB_REQ_GET_DESCRIPTOR:
{
{
}
{
case VUSB_DT_STRING:
break;
case VUSB_DT_DEVICE_QUALIFIER:
Log(("usbMsd: GET_DESCRIPTOR DT_DEVICE_QUALIFIER wValue=%#x wIndex=%#x\n", pSetup->wValue, pSetup->wIndex));
/* Returned data is written after the setup message. */
case VUSB_DT_BOS:
/* Returned data is written after the setup message. */
default:
break;
}
break;
}
case VUSB_REQ_CLEAR_FEATURE:
break;
}
/** @todo implement this. */
Log(("usbMsd: Implement standard request: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
}
/* 3.1 Bulk-Only Mass Storage Reset */
{
Log(("usbMsdHandleDefaultPipe: Bulk-Only Mass Storage Reset\n"));
}
/* 3.2 Get Max LUN, may stall if we like (but we don't). */
{
}
else
{
Log(("usbMsd: Unknown control msg: bmRequestType=%#x bRequest=%#x wValue=%#x wIndex=%#x wLength=%#x\n",
}
return VINF_SUCCESS;
}
/**
* @copydoc PDMUSBREG::pfnQueue
*/
{
LogFlow(("usbMsdQueue/#%u: pUrb=%p:%s EndPt=%#x\n", pUsbIns->iInstance, pUrb, pUrb->pszDesc, pUrb->EndPt));
/*
* Parse on a per end-point basis.
*/
int rc;
{
case 0:
break;
case 0x81:
AssertFailed();
case 0x01:
break;
case 0x02:
break;
default:
break;
}
return rc;
}
/**
* @copydoc PDMUSBREG::pfnUsbClearHaltedEndpoint
*/
{
{
}
return VINF_SUCCESS;
}
/**
* @copydoc PDMUSBREG::pfnUsbSetInterface
*/
static DECLCALLBACK(int) usbMsdUsbSetInterface(PPDMUSBINS pUsbIns, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
{
LogFlow(("usbMsdUsbSetInterface/#%u: bInterfaceNumber=%u bAlternateSetting=%u\n", pUsbIns->iInstance, bInterfaceNumber, bAlternateSetting));
Assert(bAlternateSetting == 0);
return VINF_SUCCESS;
}
/**
* @copydoc PDMUSBREG::pfnUsbSetConfiguration
*/
{
LogFlow(("usbMsdUsbSetConfiguration/#%u: bConfigurationValue=%u\n", pUsbIns->iInstance, bConfigurationValue));
/*
* If the same config is applied more than once, it's a kind of reset.
*/
return VINF_SUCCESS;
}
/**
* @copydoc PDMUSBREG::pfnUsbGetDescriptorCache
*/
{
return &g_UsbMsdDescCacheSS;
return &g_UsbMsdDescCacheHS;
else
return &g_UsbMsdDescCacheFS;
}
/**
* @copydoc PDMUSBREG::pfnUsbReset
*/
{
return rc;
}
/**
* @copydoc PDMUSBREG::pfnVMSuspend
*/
{
}
/**
* @copydoc PDMUSBREG::pfnVMSuspend
*/
{
}
/**
* @copydoc PDMUSBREG::pfnDriverAttach
*/
{
int rc;
("UsbMsd: Device does not support hotplugging\n"));
/* the usual paranoia */
/*
* Try attach the block device and get the interfaces,
* required as well as optional.
*/
if (RT_SUCCESS(rc))
{
/* Get SCSI connector interface. */
AssertMsgReturn(pThis->Lun0.pIScsiConnector, ("Missing SCSI interface below\n"), VERR_PDM_MISSING_INTERFACE);
}
else
if (RT_FAILURE(rc))
{
}
return rc;
}
/**
* @copydoc PDMUSBREG::pfnDriverDetach
*/
{
("UsbMsd: Device does not support hotplugging\n"));
/*
* Zero some important members.
*/
}
/**
* @callback_method_impl{FNPDMDEVASYNCNOTIFY,
* Callback employed by usbMsdVMReset.}
*/
{
return false;
return true;
}
/**
* @interface_method_impl{PDMDEVREG,pfnReset}
*/
{
else
{
}
}
/**
* @copydoc PDMUSBREG::pfnDestruct
*/
{
{
}
{
}
{
}
{
}
}
/**
* @copydoc PDMUSBREG::pfnConstruct
*/
static DECLCALLBACK(int) usbMsdConstruct(PPDMUSBINS pUsbIns, int iInstance, PCFGMNODE pCfg, PCFGMNODE pCfgGlobal)
{
/*
* Perform the basic structure initialization first so the destructor
* will not misbehave.
*/
/*
* Validate and read the configuration.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Attach the SCSI driver.
*/
rc = PDMUsbHlpDriverAttach(pUsbIns, 0 /*iLun*/, &pThis->Lun0.IBase, &pThis->Lun0.pIBase, "SCSI Port");
if (RT_FAILURE(rc))
N_("MSD failed to query the PDMISCSICONNECTOR from the driver below it"));
/*
* Register the saved state data unit.
*/
if (RT_FAILURE(rc))
N_("MSD failed to register SSM save state handlers"));
return VINF_SUCCESS;
}
/**
* The USB Mass Storage Device (MSD) registration record.
*/
{
/* u32Version */
/* szName */
"Msd",
/* pszDescription */
"USB Mass Storage Device, one LUN.",
/* fFlags */
/* cMaxInstances */
~0U,
/* cbInstance */
sizeof(USBMSD),
/* pfnConstruct */
/* pfnDestruct */
/* pfnVMInitComplete */
NULL,
/* pfnVMPowerOn */
NULL,
/* pfnVMReset */
/* pfnVMSuspend */
/* pfnVMResume */
NULL,
/* pfnVMPowerOff */
/* pfnHotPlugged */
NULL,
/* pfnHotUnplugged */
NULL,
/* pfnDriverAttach */
/* pfnDriverDetach */
/* pfnQueryInterface */
NULL,
/* pfnUsbReset */
/* pfnUsbGetCachedDescriptors */
/* pfnUsbSetConfiguration */
/* pfnUsbSetInterface */
/* pfnUsbClearHaltedEndpoint */
/* pfnUrbNew */
NULL/*usbMsdUrbNew*/,
/* pfnQueue */
/* pfnUrbCancel */
/* pfnUrbReap */
/* pfnWakeup */
/* u32TheEnd */
};