VBoxUsbRt.cpp revision 6594fa9834a20895966c8226893b08f5273c5d4d
/* $Id$ */
/** @file
* VBox USB R0 runtime
*/
/*
* Copyright (C) 2011 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.
*/
#include "VBoxUsbCmn.h"
#include "../cmn/VBoxUsbIdc.h"
#include "../cmn/VBoxUsbTool.h"
#include <VBox/usblib-win.h>
#define _USBD_
#define USBD_DEFAULT_PIPE_TRANSFER 0x00000008
#define VBOXUSB_MAGIC 0xABCF1423
typedef struct VBOXUSB_URB_CONTEXT
{
typedef struct VBOXUSB_SETUP
{
{
if (bRc)
{
}
else
{
}
return bRc;
}
{
if (bRc)
{
}
else
{
}
return bRc;
}
{
}
{
/* we just reuse the standard usb tooling for simplicity here */
NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(g_VBoxUsbGlobals.RtIdc.pDevice, uCtl, pvBuffer, NULL);
return Status;
}
static NTSTATUS vboxUsbRtIdcInit()
{
NTSTATUS Status = IoGetDeviceObjectPointer(&UniName, FILE_ALL_ACCESS, &g_VBoxUsbGlobals.RtIdc.pFile, &g_VBoxUsbGlobals.RtIdc.pDevice);
if (NT_SUCCESS(Status))
{
if (NT_SUCCESS(Status))
{
return STATUS_SUCCESS;
AssertFailed();
}
else
{
AssertFailed();
}
/* this will as well dereference the dev obj */
}
else
{
AssertFailed();
}
return Status;
}
static VOID vboxUsbRtIdcTerm()
{
}
{
if (!NT_SUCCESS(Status))
return Status;
return STATUS_SUCCESS;
}
{
return Status;
}
{
return vboxUsbRtIdcInit();
}
{
}
{
NULL, /* IN PUNICODE_STRING ReferenceString OPTIONAL */
if (NT_SUCCESS(Status))
{
if (NT_SUCCESS(Status))
{
return STATUS_SUCCESS;
}
if (NT_SUCCESS(tmpStatus))
{
}
}
return Status;
}
/**
* Free cached USB device/configuration descriptors
*
* @param pDevExt USB DevExt pointer
*/
{
{
}
for (ULONG i = 0; i < VBOXUSBRT_MAX_CFGS; ++i)
{
{
}
}
}
/**
* Free per-device interface info
*
* @param pDevExt USB DevExt pointer
* @param fAbortPipes If true, also abort any open pipes
*/
{
unsigned i;
unsigned j;
/*
* Free old interface info
*/
{
{
{
if (fAbortPipes)
{
{
Log(("Aborting Pipe %d handle %x address %x\n", j,
VBoxUsbToolPipeClear(pDevExt->pLowerDO, pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo->Pipes[j].PipeHandle, FALSE);
}
}
}
}
}
}
{
}
{
return STATUS_SUCCESS;
if (NT_SUCCESS(Status))
{
}
return Status;
}
{
return Status;
}
{
// uint32_t uTotalLength;
// unsigned i;
/* Read device descriptor */
{
Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDevExt->Rt.devdescr, sizeof (USB_DEVICE_DESCRIPTOR), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, RT_INDEFINITE_WAIT);
if (NT_SUCCESS(Status))
{
PUSB_CONFIGURATION_DESCRIPTOR pDr = (PUSB_CONFIGURATION_DESCRIPTOR)vboxUsbMemAlloc(sizeof (USB_CONFIGURATION_DESCRIPTOR));
if (pDr)
{
UCHAR i = 0;
{
Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDr, sizeof (USB_CONFIGURATION_DESCRIPTOR), USB_CONFIGURATION_DESCRIPTOR_TYPE, i, 0, RT_INDEFINITE_WAIT);
if (!NT_SUCCESS(Status))
{
break;
}
{
break;
}
Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDevExt->Rt.cfgdescr[i], uTotalLength, USB_CONFIGURATION_DESCRIPTOR_TYPE, i, 0, RT_INDEFINITE_WAIT);
if (!NT_SUCCESS(Status))
{
break;
}
}
if (NT_SUCCESS(Status))
return Status;
/* recources will be freed in vboxUsbRtFreeCachedDescriptors below */
}
}
}
/* shoud be only on fail here */
return Status;
}
{
do
{
if (!pFObj)
{
AssertFailed();
break;
}
if ( !pDev
{
AssertFailed();
break;
}
{
AssertFailed();
break;
}
if (NT_SUCCESS(Status))
{
}
} while (0);
return Status;
}
{
{
}
else
{
AssertFailed();
}
return STATUS_SUCCESS;
}
{
PUSB_DEVICE_DESCRIPTOR pDr = (PUSB_DEVICE_DESCRIPTOR)vboxUsbMemAllocZ(sizeof (USB_DEVICE_DESCRIPTOR));
if (pDr)
{
Status = VBoxUsbToolGetDescriptor(pDevExt->pLowerDO, pDr, sizeof(*pDr), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, RT_INDEFINITE_WAIT);
if (NT_SUCCESS(Status))
{
if (pDr->iSerialNumber
#ifdef DEBUG
#endif
)
{
int langId;
if (NT_SUCCESS(Status))
{
Status = VBoxUsbToolGetStringDescriptorA(pDevExt->pLowerDO, pDevExt->Rt.szSerial, sizeof (pDevExt->Rt.szSerial), pDr->iSerialNumber, langId, RT_INDEFINITE_WAIT);
}
else
{
}
}
}
}
return Status;
}
{
/* don't check for owner since this request is allowed for non-owners as well */
{
if (NT_SUCCESS(Status))
{
}
}
else
{
}
return Status;
}
{
do
{
if (!pFObj)
{
AssertFailed();
break;
}
{
AssertFailed();
break;
}
{
AssertFailed();
break;
}
Status = VBoxUsbToolIoInternalCtlSendSync(pDevExt->pLowerDO, IOCTL_INTERNAL_USB_RESET_PORT, NULL, NULL);
} while (0);
return Status;
}
static PUSB_CONFIGURATION_DESCRIPTOR vboxUsbRtFindConfigDesc(PVBOXUSBDEV_EXT pDevExt, uint8_t uConfiguration)
{
for (ULONG i = 0; i < VBOXUSBRT_MAX_CFGS; ++i)
{
{
{
break;
}
}
}
return pCfgDr;
}
{
uint32_t i;
if (!uConfiguration)
{
pUrb = VBoxUsbToolUrbAllocZ(URB_FUNCTION_SELECT_CONFIGURATION, sizeof (struct _URB_SELECT_CONFIGURATION));
if(!pUrb)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
{
}
else
{
AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbPost failed Status (0x%x), usb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
}
return Status;
}
if (!pCfgDr)
{
return STATUS_INVALID_PARAMETER;
}
PUSBD_INTERFACE_LIST_ENTRY pIfLe = (PUSBD_INTERFACE_LIST_ENTRY)vboxUsbMemAllocZ((pCfgDr->bNumInterfaces + 1) * sizeof(USBD_INTERFACE_LIST_ENTRY));
if (!pIfLe)
{
return STATUS_INSUFFICIENT_RESOURCES;
}
for (i = 0; i < pCfgDr->bNumInterfaces; i++)
{
pIfLe[i].InterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(pCfgDr, pCfgDr, i, 0, -1, -1, -1);
if (!pIfLe[i].InterfaceDescriptor)
{
break;
}
}
if (NT_SUCCESS(Status))
{
if (pUrb)
{
{
pDevExt->Rt.pVBIfaceInfo = (VBOXUSB_IFACE_INFO*)vboxUsbMemAllocZ(pDevExt->Rt.uNumInterfaces * sizeof (VBOXUSB_IFACE_INFO));
{
{
size_t uTotalIfaceInfoLength = RT_OFFSETOF(struct _USBD_INTERFACE_INFORMATION, Pipes[RT_MAX(pIfLe[i].Interface->NumberOfPipes, 1)]);
pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo = (PUSBD_INTERFACE_INFORMATION)vboxUsbMemAlloc(uTotalIfaceInfoLength);
{
break;
}
{
pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo = (VBOXUSB_PIPE_INFO *)vboxUsbMemAlloc(pIfLe[i].Interface->NumberOfPipes * sizeof(VBOXUSB_PIPE_INFO));
{
break;
}
}
else
{
}
RtlCopyMemory(pDevExt->Rt.pVBIfaceInfo[i].pInterfaceInfo, pIfLe[i].Interface, uTotalIfaceInfoLength);
{
pDevExt->Rt.pVBIfaceInfo[i].pPipeInfo[j].EndpointAddress = pIfLe[i].Interface->Pipes[j].EndpointAddress;
}
}
// if (NT_SUCCESS(Status))
// {
//
// }
}
else
{
}
}
else
{
AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbPost failed Status (0x%x), usb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
}
}
else
{
}
}
return Status;
}
{
do
{
if (!pFObj)
{
AssertFailed();
break;
}
{
AssertFailed();
break;
}
if ( !pCfg
{
break;
}
} while (0);
return Status;
}
static NTSTATUS vboxUsbRtSetInterface(PVBOXUSBDEV_EXT pDevExt, uint32_t InterfaceNumber, int AlternateSetting)
{
{
return STATUS_INVALID_PARAMETER;
}
{
return STATUS_INVALID_PARAMETER;
}
if (!pCfgDr)
{
return STATUS_INVALID_PARAMETER;
}
PUSB_INTERFACE_DESCRIPTOR pIfDr = USBD_ParseConfigurationDescriptorEx(pCfgDr, pCfgDr, InterfaceNumber, AlternateSetting, -1, -1, -1);
if (!pIfDr)
{
AssertMsgFailed((__FUNCTION__": invalid interface %d or alternate setting %d\n", InterfaceNumber, AlternateSetting));
return STATUS_UNSUCCESSFUL;
}
if (!pUrb)
{
return STATUS_NO_MEMORY;
}
/*
* Free old interface and pipe info, allocate new again
*/
{
/* Clear pipes associated with the interface, else Windows may hang. */
{
VBoxUsbToolPipeClear(pDevExt->pLowerDO, pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo->Pipes[i].PipeHandle, FALSE);
}
}
{
}
pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo = (PUSBD_INTERFACE_INFORMATION)vboxUsbMemAlloc(uTotalIfaceInfoLength);
{
if (pIfDr->bNumEndpoints > 0)
{
pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo = (VBOXUSB_PIPE_INFO*)vboxUsbMemAlloc(pIfDr->bNumEndpoints * sizeof(VBOXUSB_PIPE_INFO));
{
AssertMsgFailed(("VBoxUSBSetInterface: ExAllocatePool failed!\n"));
}
}
else
{
}
if (NT_SUCCESS(Status))
{
UsbBuildSelectInterfaceRequest(pUrb, uUrbSize, pDevExt->Rt.hConfiguration, InterfaceNumber, AlternateSetting);
{
memcpy(pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pInterfaceInfo, pIfInfo, GET_USBD_INTERFACE_SIZE(pIfDr->bNumEndpoints));
{
pDevExt->Rt.pVBIfaceInfo[InterfaceNumber].pPipeInfo[i].EndpointAddress = pIfInfo->Pipes[i].EndpointAddress;
}
}
else
{
AssertMsgFailed((__FUNCTION__": VBoxUsbToolUrbPost failed Status (0x%x) usb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
}
}
}
else
{
AssertMsgFailed(("VBoxUSBSetInterface: ExAllocatePool failed!\n"));
}
return Status;
}
{
do
{
if (!pFObj)
{
AssertFailed();
break;
}
{
AssertFailed();
break;
}
if ( !pIf
{
break;
}
} while (0);
return Status;
}
{
{
{
/* Note that bit 7 determines pipe direction, but is still significant
* because endpoints may be numbered like 0x01, 0x81, 0x02, 0x82 etc.
*/
}
}
return 0;
}
{
{
{
}
}
return NULL;
}
static NTSTATUS vboxUsbRtClearEndpoint(PVBOXUSBDEV_EXT pDevExt, uint32_t EndPointAddress, bool fReset)
{
NTSTATUS Status = VBoxUsbToolPipeClear(pDevExt->pLowerDO, vboxUsbRtGetPipeHandle(pDevExt, EndPointAddress), fReset);
if (!NT_SUCCESS(Status))
{
}
return Status;
}
{
do
{
if (!pFObj)
{
AssertFailed();
break;
}
{
AssertFailed();
break;
}
if ( !pCe
{
break;
}
} while (0);
return Status;
}
{
do
{
if (!pFObj)
{
AssertFailed();
break;
}
{
AssertFailed();
break;
}
if ( !pCe
{
break;
}
} while (0);
return Status;
}
{
if (!pvContext)
{
return STATUS_CONTINUE_COMPLETION;
}
{
return STATUS_CONTINUE_COMPLETION;
}
{
if (pDevExt)
return STATUS_CONTINUE_COMPLETION;
}
if (Status == STATUS_SUCCESS)
{
{
case USBD_STATUS_CRC:
break;
case USBD_STATUS_SUCCESS:
break;
case USBD_STATUS_STALL_PID:
break;
default:
break;
}
switch(pContext->ulTransferType)
{
case USBSUP_TRANSFER_TYPE_MSG:
{
/* QUSB_TRANSFER_TYPE_MSG is a control transfer, but it is special
* the first 8 bytes of the buffer is the setup packet so the real
* data length is therefore urb->len - 8
*/
}
break;
break;
)
{
/* If we don't use the USBD_SHORT_TRANSFER_OK flag, the returned buffer lengths are
* wrong for short transfers (always a multiple of max packet size?). So we just figure
* out if this was a data underrun on our own.
*/
}
break;
default:
break;
}
}
else
{
Log((__FUNCTION__": URB failed Status (0x%x) urb Status (0x%x)\n", Status, pUrb->UrbHeader.Status));
#ifdef DEBUG
switch(pContext->ulTransferType)
{
case USBSUP_TRANSFER_TYPE_MSG:
break;
break;
break;
}
#endif
{
case USBD_STATUS_CRC:
break;
case USBD_STATUS_STALL_PID:
break;
case USBD_STATUS_DEVICE_GONE:
break;
case ((USBD_STATUS)0xC0010000L): // USBD_STATUS_CANCELED - too bad usbdi.h and usb.h aren't consistent!
// TODO: What the heck are we really supposed to do here?
break;
case USBD_STATUS_BAD_START_FRAME: // This one really shouldn't happen
break;
default:
break;
}
}
// For isochronous transfers, always update the individual packets
{
{
{
case USBD_STATUS_SUCCESS:
break;
case USBD_STATUS_NOT_ACCESSED:
break;
default:
break;
}
}
}
return STATUS_CONTINUE_COMPLETION;
}
{
{
}
else
do
{
if (!pContext)
{
break;
}
{
hPipe = vboxUsbRtGetPipeHandle(pDevExt, pUrbInfo->ep | ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? 0x80 : 0x00));
if (!hPipe)
{
AssertMsgFailed((__FUNCTION__": vboxUsbRtGetPipeHandle failed for endpoint (0x%x)\n", pUrbInfo->ep));
break;
}
}
if (!pMdlBuf)
{
AssertMsgFailed((__FUNCTION__": IoAllocateMdl failed for buffer (0x%p) length (%d)\n", pUrbInfo->buf, pUrbInfo->len));
break;
}
{
}
{
Status = GetExceptionCode();
break;
}
/* For some reason, passing a MDL in the URB does not work reliably. Notably
* the iPhone when used with iTunes fails.
*/
if (!pBuffer)
{
break;
}
{
case USBSUP_TRANSFER_TYPE_MSG:
{
pUrb->UrbControlTransfer.TransferFlags = ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT);
if (!hPipe)
{
/* QUSB_TRANSFER_TYPE_MSG is a control transfer, but it is special
* the first 8 bytes of the buffer is the setup packet so the real
* data length is therefore pUrb->len - 8
*/
memcpy(pUrb->UrbControlTransfer.SetupPacket, pBuffer, min(sizeof (pUrb->UrbControlTransfer.SetupPacket), pUrbInfo->len));
else
pUrb->UrbControlTransfer.TransferBuffer = (uint8_t *)pBuffer + sizeof(pUrb->UrbControlTransfer.SetupPacket);
}
else
{
}
break;
}
{
VBOXUSB_PIPE_INFO *pPipeInfo = vboxUsbRtGetPipeInfo(pDevExt, pUrbInfo->ep | ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? 0x80 : 0x00));
{
/* Can happen if the isoc request comes in too early or late. */
break;
}
pUrb->UrbIsochronousTransfer.TransferFlags = ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT);
{
}
/* We have to schedule the URBs ourselves. There is an ASAP flag but
* almost completely useless.
*/
iFrame += 2;
break;
}
{
pUrb->UrbBulkOrInterruptTransfer.TransferFlags = ((pUrbInfo->dir == USBSUP_DIRECTION_IN) ? USBD_TRANSFER_DIRECTION_IN : USBD_TRANSFER_DIRECTION_OUT);
break;
}
default:
{
AssertFailed();
break;
}
}
if (!NT_SUCCESS(Status))
{
break;
}
return STATUS_PENDING;
} while (0);
if (pMdlBuf)
{
}
if (pContext)
return Status;
}
{
do
{
if (!pFObj)
{
AssertFailed();
break;
}
{
AssertFailed();
break;
}
if ( !pUrbInfo
{
break;
}
} while (0);
return Status;
}
{
return STATUS_SUCCESS;
}
{
{
}
else
{
}
return Status;
}
{
return STATUS_INVALID_DEVICE_REQUEST;
}
{
if (!pFObj)
{
AssertFailed();
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
{
return STATUS_SUCCESS;
}
{
{
{
}
{
}
case SUPUSB_IOCTL_GET_DEVICE:
{
}
case SUPUSB_IOCTL_USB_RESET:
{
}
{
}
{
}
{
}
{
}
case SUPUSB_IOCTL_SEND_URB:
{
}
{
}
case SUPUSB_IOCTL_GET_VERSION:
{
}
default:
{
}
}
}