summaryrefslogtreecommitdiff
path: root/src/VBox/VMM/VMMR3/PDMUsb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/VMM/VMMR3/PDMUsb.cpp')
-rw-r--r--src/VBox/VMM/VMMR3/PDMUsb.cpp235
1 files changed, 187 insertions, 48 deletions
diff --git a/src/VBox/VMM/VMMR3/PDMUsb.cpp b/src/VBox/VMM/VMMR3/PDMUsb.cpp
index 179ba44d..0e39f052 100644
--- a/src/VBox/VMM/VMMR3/PDMUsb.cpp
+++ b/src/VBox/VMM/VMMR3/PDMUsb.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2006-2007 Oracle Corporation
+ * Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -28,6 +28,7 @@
#include <VBox/vmm/vmm.h>
#include <VBox/sup.h>
#include <VBox/vmm/vm.h>
+#include <VBox/vmm/uvm.h>
#include <VBox/version.h>
#include <VBox/err.h>
@@ -225,7 +226,8 @@ static DECLCALLBACK(int) pdmR3UsbReg_Register(PCPDMUSBREGCB pCallbacks, PCPDMUSB
&& pdmR3IsValidName(pReg->szName),
("Invalid name '%.s'\n", sizeof(pReg->szName), pReg->szName),
VERR_PDM_INVALID_USB_REGISTRATION);
- AssertMsgReturn(pReg->fFlags == 0, ("fFlags=%#x\n", pReg->fFlags), VERR_PDM_INVALID_USB_REGISTRATION);
+ AssertMsgReturn((pReg->fFlags & ~(PDM_USBREG_HIGHSPEED_CAPABLE)) == 0,
+ ("fFlags=%#x\n", pReg->fFlags), VERR_PDM_INVALID_USB_REGISTRATION);
AssertMsgReturn(pReg->cMaxInstances > 0,
("Max instances %u! (USB Device %s)\n", pReg->cMaxInstances, pReg->szName),
VERR_PDM_INVALID_USB_REGISTRATION);
@@ -459,21 +461,22 @@ static int pdmR3UsbFindHub(PVM pVM, uint32_t iUsbVersion, PPDMUSBHUB *ppHub)
* @param pUsbDev The USB device emulation.
* @param iInstance -1 if not called by pdmR3UsbInstantiateDevices().
* @param pUuid The UUID for this device.
- * @param pInstanceNode The instance CFGM node. NULL if not called by pdmR3UsbInstantiateDevices().
- * @param ppConfig Pointer to the device configuration pointer. This is set to NULL if inserted
+ * @param ppInstanceNode Pointer to the device instance pointer. This is set to NULL if inserted
* into the tree or cleaned up.
*
- * In the pdmR3UsbInstantiateDevices() case (pInstanceNode != NULL) this is
- * the actual config node and will not be cleaned up.
+ * In the pdmR3UsbInstantiateDevices() case (iInstance != -1) this is
+ * the actual instance node and will not be cleaned up.
*
* @parma iUsbVersion The USB version preferred by the device.
*/
static int pdmR3UsbCreateDevice(PVM pVM, PPDMUSBHUB pHub, PPDMUSB pUsbDev, int iInstance, PCRTUUID pUuid,
- PCFGMNODE pInstanceNode, PCFGMNODE *ppConfig, uint32_t iUsbVersion)
+ PCFGMNODE *ppInstanceNode, uint32_t iUsbVersion)
{
- const bool fAtRuntime = pInstanceNode == NULL;
+ const bool fAtRuntime = iInstance == -1;
int rc;
- NOREF(iUsbVersion);
+
+ AssertPtrReturn(ppInstanceNode, VERR_INVALID_POINTER);
+ AssertPtrReturn(*ppInstanceNode, VERR_INVALID_POINTER);
/*
* If not called by pdmR3UsbInstantiateDevices(), we'll have to fix
@@ -488,8 +491,12 @@ static int pdmR3UsbCreateDevice(PVM pVM, PPDMUSBHUB pHub, PPDMUSB pUsbDev, int i
}
/* The instance node and number. */
- if (!pInstanceNode)
+ PCFGMNODE pInstanceToDelete = NULL;
+ PCFGMNODE pInstanceNode = NULL;
+ if (fAtRuntime)
{
+ /** @todo r=bird: This code is bogus as it ASSUMES that all USB devices are
+ * capable of infinite number of instances. */
for (unsigned c = 0; c < _2M; c++)
{
iInstance = pUsbDev->iNextInstance++;
@@ -498,31 +505,27 @@ static int pdmR3UsbCreateDevice(PVM pVM, PPDMUSBHUB pHub, PPDMUSB pUsbDev, int i
break;
}
AssertRCReturn(rc, rc);
+
+ rc = CFGMR3ReplaceSubTree(pInstanceNode, *ppInstanceNode);
+ AssertRCReturn(rc, rc);
+ *ppInstanceNode = NULL;
+ pInstanceToDelete = pInstanceNode;
}
else
{
Assert(iInstance >= 0);
if (iInstance >= (int)pUsbDev->iNextInstance)
pUsbDev->iNextInstance = iInstance + 1;
+ pInstanceNode = *ppInstanceNode;
}
- /* The instance config node. */
- PCFGMNODE pConfigToDelete = NULL;
- PCFGMNODE pConfig = NULL;
- if (!ppConfig || !*ppConfig)
+ /* Make sure the instance config node exists. */
+ PCFGMNODE pConfig = CFGMR3GetChild(pInstanceNode, "Config");
+ if (!pConfig)
{
rc = CFGMR3InsertNode(pInstanceNode, "Config", &pConfig);
AssertRCReturn(rc, rc);
}
- else if (fAtRuntime)
- {
- rc = CFGMR3InsertSubTree(pInstanceNode, "Config", *ppConfig, &pConfig);
- AssertRCReturn(rc, rc);
- *ppConfig = NULL;
- pConfigToDelete = pConfig;
- }
- else
- pConfig = *ppConfig;
Assert(CFGMR3GetChild(pInstanceNode, "Config") == pConfig);
/* The global device config node. */
@@ -532,7 +535,7 @@ static int pdmR3UsbCreateDevice(PVM pVM, PPDMUSBHUB pHub, PPDMUSB pUsbDev, int i
rc = CFGMR3InsertNode(pDevNode, "GlobalConfig", &pGlobalConfig);
if (RT_FAILURE(rc))
{
- CFGMR3RemoveNode(pConfigToDelete);
+ CFGMR3RemoveNode(pInstanceToDelete);
AssertRCReturn(rc, rc);
}
}
@@ -548,7 +551,7 @@ static int pdmR3UsbCreateDevice(PVM pVM, PPDMUSBHUB pHub, PPDMUSB pUsbDev, int i
{
AssertMsgFailed(("Failed to allocate %d bytes of instance data for USB device '%s'. rc=%Rrc\n",
cb, pUsbDev->pReg->szName, rc));
- CFGMR3RemoveNode(pConfigToDelete);
+ CFGMR3RemoveNode(pInstanceToDelete);
return rc;
}
@@ -562,12 +565,15 @@ static int pdmR3UsbCreateDevice(PVM pVM, PPDMUSBHUB pHub, PPDMUSB pUsbDev, int i
pUsbIns->Internal.s.pVM = pVM;
//pUsbIns->Internal.s.pLuns = NULL;
pUsbIns->Internal.s.pCfg = pInstanceNode;
- pUsbIns->Internal.s.pCfgDelete = pConfigToDelete;
+ pUsbIns->Internal.s.pCfgDelete = pInstanceToDelete;
pUsbIns->Internal.s.pCfgGlobal = pGlobalConfig;
pUsbIns->Internal.s.Uuid = *pUuid;
//pUsbIns->Internal.s.pHub = NULL;
pUsbIns->Internal.s.iPort = UINT32_MAX; /* to be determined. */
- pUsbIns->Internal.s.fVMSuspended = true;
+ /* Set the flag accordingly.
+ * Oherwise VMPowerOff, VMSuspend will not be called for devices attached at runtime.
+ */
+ pUsbIns->Internal.s.fVMSuspended = !fAtRuntime;
//pUsbIns->Internal.s.pfnAsyncNotify = NULL;
pUsbIns->pHlpR3 = &g_pdmR3UsbHlp;
pUsbIns->pReg = pUsbDev->pReg;
@@ -578,6 +584,7 @@ static int pdmR3UsbCreateDevice(PVM pVM, PPDMUSBHUB pHub, PPDMUSB pUsbDev, int i
pUsbIns->pszName = RTStrDup(pUsbDev->pReg->szName);
//pUsbIns->fTracing = 0;
pUsbIns->idTracing = ++pVM->pdm.s.idTracingOther;
+ pUsbIns->iUsbHubVersion = iUsbVersion;
/*
* Link it into all the lists.
@@ -799,11 +806,15 @@ int pdmR3UsbInstantiateDevices(PVM pVM)
}
CFGMR3SetRestrictedRoot(pConfigNode);
- /** @todo
- * Figure out the USB version from the USB device registration and the configuration.
+ /*
+ * Every device must support USB 1.x hubs; optionally, high-speed USB 2.0 hubs
+ * might be also supported. This determines where to attach the device.
*/
uint32_t iUsbVersion = VUSB_STDVER_11;
+ if (paUsbDevs[i].pUsbDev->pReg->fFlags & PDM_USBREG_HIGHSPEED_CAPABLE)
+ iUsbVersion |= VUSB_STDVER_20;
+
/*
* Find a suitable hub with free ports.
*/
@@ -816,12 +827,18 @@ int pdmR3UsbInstantiateDevices(PVM pVM)
}
/*
+ * This is how we inform the device what speed it's communicating at, and hence
+ * which descriptors it should present to the guest.
+ */
+ iUsbVersion &= pHub->fVersions;
+
+ /*
* Create and attach the device.
*/
RTUUID Uuid;
rc = RTUuidCreate(&Uuid);
AssertRCReturn(rc, rc);
- rc = pdmR3UsbCreateDevice(pVM, pHub, paUsbDevs[i].pUsbDev, paUsbDevs[i].iInstance, &Uuid, paUsbDevs[i].pNode, &pConfigNode, iUsbVersion);
+ rc = pdmR3UsbCreateDevice(pVM, pHub, paUsbDevs[i].pUsbDev, paUsbDevs[i].iInstance, &Uuid, &paUsbDevs[i].pNode, iUsbVersion);
if (RT_FAILURE(rc))
return rc;
} /* for device instances */
@@ -831,13 +848,84 @@ int pdmR3UsbInstantiateDevices(PVM pVM)
/**
+ * Creates an emulated USB device instance at runtime.
+ *
+ * This will find an appropriate HUB for the USB device
+ * and try instantiate the emulated device.
+ *
+ * @returns VBox status code.
+ * @param pUVM The user mode VM handle.
+ * @param pszDeviceName The name of the PDM device to instantiate.
+ * @param pInstanceNode The instance CFGM node.
+ * @param pUuid The UUID to be associated with the device.
+ *
+ * @thread EMT
+ */
+VMMR3DECL(int) PDMR3UsbCreateEmulatedDevice(PUVM pUVM, const char *pszDeviceName, PCFGMNODE pInstanceNode, PCRTUUID pUuid)
+{
+ /*
+ * Validate input.
+ */
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ PVM pVM = pUVM->pVM;
+ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_EMT_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
+ AssertPtrReturn(pszDeviceName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pInstanceNode, VERR_INVALID_POINTER);
+
+ /*
+ * Find the device.
+ */
+ PPDMUSB pUsbDev = pdmR3UsbLookup(pVM, pszDeviceName);
+ if (!pUsbDev)
+ {
+ LogRel(("PDMR3UsbCreateEmulatedDevice: The '%s' device wasn't found\n", pszDeviceName));
+ return VERR_PDM_NO_USBPROXY;
+ }
+
+ /*
+ * Every device must support USB 1.x hubs; optionally, high-speed USB 2.0 hubs
+ * might be also supported. This determines where to attach the device.
+ */
+ uint32_t iUsbVersion = VUSB_STDVER_11;
+ if (pUsbDev->pReg->fFlags & PDM_USBREG_HIGHSPEED_CAPABLE)
+ iUsbVersion |= VUSB_STDVER_20;
+
+ /*
+ * Find a suitable hub with free ports.
+ */
+ PPDMUSBHUB pHub;
+ int rc = pdmR3UsbFindHub(pVM, iUsbVersion, &pHub);
+ if (RT_FAILURE(rc))
+ {
+ Log(("pdmR3UsbFindHub: failed %Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * This is how we inform the device what speed it's communicating at, and hence
+ * which descriptors it should present to the guest.
+ */
+ iUsbVersion &= pHub->fVersions;
+
+ /*
+ * Create and attach the device.
+ */
+ rc = pdmR3UsbCreateDevice(pVM, pHub, pUsbDev, -1, pUuid, &pInstanceNode, iUsbVersion);
+ AssertRCReturn(rc, rc);
+
+ return rc;
+}
+
+
+/**
* Creates a USB proxy device instance.
*
* This will find an appropriate HUB for the USB device, create the necessary CFGM stuff
* and try instantiate the proxy device.
*
* @returns VBox status code.
- * @param pVM Pointer to the VM.
+ * @param pUVM The user mode VM handle.
* @param pUuid The UUID to be associated with the device.
* @param fRemote Whether it's a remove or local device.
* @param pszAddress The address string.
@@ -845,13 +933,16 @@ int pdmR3UsbInstantiateDevices(PVM pVM)
* @param iUsbVersion The preferred USB version.
* @param fMaskedIfs The interfaces to hide from the guest.
*/
-VMMR3DECL(int) PDMR3USBCreateProxyDevice(PVM pVM, PCRTUUID pUuid, bool fRemote, const char *pszAddress, void *pvBackend,
+VMMR3DECL(int) PDMR3UsbCreateProxyDevice(PUVM pUVM, PCRTUUID pUuid, bool fRemote, const char *pszAddress, void *pvBackend,
uint32_t iUsbVersion, uint32_t fMaskedIfs)
{
/*
* Validate input.
*/
- VM_ASSERT_EMT(pVM);
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ PVM pVM = pUVM->pVM;
+ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
+ VM_ASSERT_EMT_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
AssertPtrReturn(pszAddress, VERR_INVALID_POINTER);
AssertReturn( iUsbVersion == VUSB_STDVER_20
@@ -863,7 +954,7 @@ VMMR3DECL(int) PDMR3USBCreateProxyDevice(PVM pVM, PCRTUUID pUuid, bool fRemote,
PPDMUSB pUsbDev = pdmR3UsbLookup(pVM, "USBProxy");
if (!pUsbDev)
{
- LogRel(("PDMR3USBCreateProxyDevice: The USBProxy device class wasn't found\n"));
+ LogRel(("PDMR3UsbCreateProxyDevice: The USBProxy device class wasn't found\n"));
return VERR_PDM_NO_USBPROXY;
}
@@ -879,12 +970,14 @@ VMMR3DECL(int) PDMR3USBCreateProxyDevice(PVM pVM, PCRTUUID pUuid, bool fRemote,
}
/*
- * Create the CFGM configuration node.
+ * Create the CFGM instance node.
*/
- PCFGMNODE pConfig = CFGMR3CreateTree(pVM);
- AssertReturn(pConfig, VERR_NO_MEMORY);
+ PCFGMNODE pInstance = CFGMR3CreateTree(pUVM);
+ AssertReturn(pInstance, VERR_NO_MEMORY);
do /* break loop */
{
+ PCFGMNODE pConfig;
+ rc = CFGMR3InsertNode(pInstance, "Config", &pConfig); AssertRCBreak(rc);
rc = CFGMR3InsertString(pConfig, "Address", pszAddress); AssertRCBreak(rc);
char szUuid[RTUUID_STR_LENGTH];
rc = RTUuidToStr(pUuid, &szUuid[0], sizeof(szUuid)); AssertRCBreak(rc);
@@ -897,17 +990,17 @@ VMMR3DECL(int) PDMR3USBCreateProxyDevice(PVM pVM, PCRTUUID pUuid, bool fRemote,
} while (0); /* break loop */
if (RT_FAILURE(rc))
{
- CFGMR3RemoveNode(pConfig);
- LogRel(("PDMR3USBCreateProxyDevice: failed to setup CFGM config, rc=%Rrc\n", rc));
+ CFGMR3RemoveNode(pInstance);
+ LogRel(("PDMR3UsbCreateProxyDevice: failed to setup CFGM config, rc=%Rrc\n", rc));
return rc;
}
/*
- * Finally, try create it.
+ * Finally, try to create it.
*/
- rc = pdmR3UsbCreateDevice(pVM, pHub, pUsbDev, -1, pUuid, NULL, &pConfig, iUsbVersion);
- if (RT_FAILURE(rc) && pConfig)
- CFGMR3RemoveNode(pConfig);
+ rc = pdmR3UsbCreateDevice(pVM, pHub, pUsbDev, -1, pUuid, &pInstance, iUsbVersion);
+ if (RT_FAILURE(rc) && pInstance)
+ CFGMR3RemoveNode(pInstance);
return rc;
}
@@ -953,6 +1046,9 @@ static void pdmR3UsbDestroyDevice(PVM pVM, PPDMUSBINS pUsbIns)
TMR3TimerDestroyUsb(pVM, pUsbIns);
//SSMR3DeregisterUsb(pVM, pUsbIns, NULL, 0);
pdmR3ThreadDestroyUsb(pVM, pUsbIns);
+#ifdef VBOX_WITH_PDM_ASYNC_COMPLETION
+ pdmR3AsyncCompletionTemplateDestroyUsb(pVM, pUsbIns);
+#endif
/*
* Unlink it.
@@ -1009,18 +1105,20 @@ static void pdmR3UsbDestroyDevice(PVM pVM, PPDMUSBINS pUsbIns)
* Detaches and destroys a USB device.
*
* @returns VBox status code.
- * @param pVM Pointer to the VM.
+ * @param pUVM The user mode VM handle.
* @param pUuid The UUID associated with the device to detach.
* @thread EMT
*/
-VMMR3DECL(int) PDMR3USBDetachDevice(PVM pVM, PCRTUUID pUuid)
+VMMR3DECL(int) PDMR3UsbDetachDevice(PUVM pUVM, PCRTUUID pUuid)
{
/*
* Validate input.
*/
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
+ PVM pVM = pUVM->pVM;
+ VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
VM_ASSERT_EMT(pVM);
AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
- AssertPtrReturn(pVM, VERR_INVALID_POINTER);
/*
* Search the global list for it.
@@ -1064,10 +1162,13 @@ VMMR3DECL(int) PDMR3USBDetachDevice(PVM pVM, PCRTUUID pUuid)
* Checks if there are any USB hubs attached.
*
* @returns true / false accordingly.
- * @param pVM Pointer to the VM.
+ * @param pUVM The user mode VM handle.
*/
-VMMR3DECL(bool) PDMR3USBHasHub(PVM pVM)
+VMMR3DECL(bool) PDMR3UsbHasHub(PUVM pUVM)
{
+ UVM_ASSERT_VALID_EXT_RETURN(pUVM, false);
+ PVM pVM = pUVM->pVM;
+ VM_ASSERT_VALID_EXT_RETURN(pVM, false);
return pVM->pdm.s.pUsbHubs != NULL;
}
@@ -1440,6 +1541,32 @@ static DECLCALLBACK(void) pdmR3UsbHlp_AsyncNotificationCompleted(PPDMUSBINS pUsb
}
+/** @interface_method_impl{PDMUSBHLP,pfnVMGetSuspendReason} */
+static DECLCALLBACK(VMSUSPENDREASON) pdmR3UsbHlp_VMGetSuspendReason(PPDMUSBINS pUsbIns)
+{
+ PDMUSB_ASSERT_USBINS(pUsbIns);
+ PVM pVM = pUsbIns->Internal.s.pVM;
+ VM_ASSERT_EMT(pVM);
+ VMSUSPENDREASON enmReason = VMR3GetSuspendReason(pVM->pUVM);
+ LogFlow(("pdmR3UsbHlp_VMGetSuspendReason: caller='%s'/%d: returns %d\n",
+ pUsbIns->pReg->szName, pUsbIns->iInstance, enmReason));
+ return enmReason;
+}
+
+
+/** @interface_method_impl{PDMUSBHLP,pfnVMGetResumeReason} */
+static DECLCALLBACK(VMRESUMEREASON) pdmR3UsbHlp_VMGetResumeReason(PPDMUSBINS pUsbIns)
+{
+ PDMUSB_ASSERT_USBINS(pUsbIns);
+ PVM pVM = pUsbIns->Internal.s.pVM;
+ VM_ASSERT_EMT(pVM);
+ VMRESUMEREASON enmReason = VMR3GetResumeReason(pVM->pUVM);
+ LogFlow(("pdmR3UsbHlp_VMGetResumeReason: caller='%s'/%d: returns %d\n",
+ pUsbIns->pReg->szName, pUsbIns->iInstance, enmReason));
+ return enmReason;
+}
+
+
/**
* The USB device helper structure.
*/
@@ -1463,6 +1590,18 @@ const PDMUSBHLP g_pdmR3UsbHlp =
pdmR3UsbHlp_ThreadCreate,
pdmR3UsbHlp_SetAsyncNotification,
pdmR3UsbHlp_AsyncNotificationCompleted,
+ pdmR3UsbHlp_VMGetSuspendReason,
+ pdmR3UsbHlp_VMGetResumeReason,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
PDM_USBHLP_VERSION
};