Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3432 lines
102 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
device.c
Abstract:
This module creates "Devices" on the bus for
bus emuerators like the hub driver
Environment:
kernel mode only
Notes:
Revision History:
6-20-99 : created
--*/
#include "common.h"
// paged functions
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, USBPORT_FreeUsbAddress)
#pragma alloc_text(PAGE, USBPORT_AllocateUsbAddress)
#pragma alloc_text(PAGE, USBPORT_SendCommand)
#endif
// non paged functions
//USBPORT_ValidateDeviceHandle
//USBPORT_RemoveDeviceHandle
//USBPORT_AddDeviceHandle
//USBPORT_ValidatePipeHandle
//USBPORT_OpenEndpoint
//USBPORT_CloseEndpoint
//USBPORT_CreateDevice
//USBPORT_RemoveDevice
//USBPORT_InitializeDevice
//USBPORT_RemovePipeHandle
//USBPORT_AddPipeHandle
//USBPORT_LazyCloseEndpoint
//USBPORT_FlushClosedEndpointList
/*
Handle validation routines, we keep a list
of valid handles and match the ones passed
in with our list.
Access to the device handle list for
//USBPORT_CreateDevice
//USBPORT_RemoveDevice
//USBPORT_InitializeDevice
is serialized with a global semaphore so we don't
need a spinlock.
BUGBUG
We may be able to use try/except block
here but I'm not sure it works at all IRQL
and on all platforms
*/
BOOLEAN
USBPORT_ValidateDeviceHandle(
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_DEVICE_HANDLE DeviceHandle,
BOOLEAN ReferenceUrb
)
/*++
Routine Description:
returns true if a device handle is valid
Arguments:
Return Value:
TRUE is handle is valid
--*/
{
BOOLEAN found = FALSE;
PLIST_ENTRY listEntry;
PDEVICE_EXTENSION devExt;
KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
KeAcquireSpinLock(&devExt->Fdo.DevHandleListSpin.sl, &irql);
if (DeviceHandle == NULL) {
// NULL is obviously not valid
goto USBPORT_ValidateDeviceHandle_Done;
}
listEntry = &devExt->Fdo.DeviceHandleList;
if (!IsListEmpty(listEntry)) {
listEntry = devExt->Fdo.DeviceHandleList.Flink;
}
while (listEntry != &devExt->Fdo.DeviceHandleList) {
PUSBD_DEVICE_HANDLE nextHandle;
nextHandle = (PUSBD_DEVICE_HANDLE) CONTAINING_RECORD(
listEntry,
struct _USBD_DEVICE_HANDLE,
ListEntry);
listEntry = nextHandle->ListEntry.Flink;
if (nextHandle == DeviceHandle) {
found = TRUE;
if (ReferenceUrb) {
InterlockedIncrement(&DeviceHandle->PendingUrbs);
}
break;
}
}
USBPORT_ValidateDeviceHandle_Done:
#if DBG
if (!found) {
// USBPORT_KdPrint((1, "'bad device handle %x\n", DeviceHandle));
DEBUG_BREAK();
}
#endif
KeReleaseSpinLock(&devExt->Fdo.DevHandleListSpin.sl, irql);
return found;
}
VOID
USBPORT_RemoveDeviceHandle(
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_DEVICE_HANDLE DeviceHandle
)
/*++
Routine Description:
Arguments:
Return Value:
TRUE is handle is valid
--*/
{
PDEVICE_EXTENSION devExt;
KIRQL irql;
ASSERT_DEVICE_HANDLE(DeviceHandle);
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
LOGENTRY(NULL,
FdoDeviceObject, LOG_MISC, 'remD', DeviceHandle, 0, 0);
// synchronize with the validation function,
// NOTE: we don't synchornize with the ADD function becuause it
// is already serialized
USBPORT_InterlockedRemoveEntryList(&DeviceHandle->ListEntry,
&devExt->Fdo.DevHandleListSpin.sl);
}
ULONG
USBPORT_GetDeviceCount(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
counts devices on the BUS
Arguments:
Return Value:
number of devices (including the root hub)
--*/
{
PLIST_ENTRY listEntry;
PDEVICE_EXTENSION devExt;
KIRQL irql;
ULONG deviceCount = 0;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
KeAcquireSpinLock(&devExt->Fdo.DevHandleListSpin.sl, &irql);
listEntry = &devExt->Fdo.DeviceHandleList;
if (!IsListEmpty(listEntry)) {
listEntry = devExt->Fdo.DeviceHandleList.Flink;
}
while (listEntry != &devExt->Fdo.DeviceHandleList) {
PUSBD_DEVICE_HANDLE nextHandle;
nextHandle = (PUSBD_DEVICE_HANDLE) CONTAINING_RECORD(
listEntry,
struct _USBD_DEVICE_HANDLE,
ListEntry);
deviceCount++;
listEntry = nextHandle->ListEntry.Flink;
}
KeReleaseSpinLock(&devExt->Fdo.DevHandleListSpin.sl, irql);
return deviceCount;
}
VOID
USBPORT_AddDeviceHandle(
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_DEVICE_HANDLE DeviceHandle
)
/*++
Routine Description:
adds a device handle to our internal list
Arguments:
Return Value:
TRUE is handle is valid
--*/
{
PDEVICE_EXTENSION devExt;
ASSERT_DEVICE_HANDLE(DeviceHandle);
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'addD', DeviceHandle, 0, 0);
InsertTailList(&devExt->Fdo.DeviceHandleList,
&DeviceHandle->ListEntry);
}
VOID
USBPORT_RemovePipeHandle(
PUSBD_DEVICE_HANDLE DeviceHandle,
PUSBD_PIPE_HANDLE_I PipeHandle
)
/*++
Routine Description:
Removes a pipe handle from our list of 'valid handles'
Arguments:
Return Value:
none
--*/
{
USBPORT_ASSERT(PipeHandle->ListEntry.Flink != NULL &&
PipeHandle->ListEntry.Blink != NULL);
RemoveEntryList(&PipeHandle->ListEntry);
PipeHandle->ListEntry.Flink = NULL;
PipeHandle->ListEntry.Blink = NULL;
}
VOID
USBPORT_AddPipeHandle(
PUSBD_DEVICE_HANDLE DeviceHandle,
PUSBD_PIPE_HANDLE_I PipeHandle
)
/*++
Routine Description:
adds a pipe handle to our internal list
Arguments:
Return Value:
TRUE is handle is valid
--*/
{
ASSERT_DEVICE_HANDLE(DeviceHandle);
USBPORT_ASSERT(PipeHandle->ListEntry.Flink == NULL &&
PipeHandle->ListEntry.Blink == NULL);
InsertTailList(&DeviceHandle->PipeHandleList,
&PipeHandle->ListEntry);
}
BOOLEAN
USBPORT_ValidatePipeHandle(
PUSBD_DEVICE_HANDLE DeviceHandle,
PUSBD_PIPE_HANDLE_I PipeHandle
)
/*++
Routine Description:
returns true if a device handle is valid
Arguments:
Return Value:
TRUE is handle is valid
--*/
{
BOOLEAN found = FALSE;
PLIST_ENTRY listEntry;
ASSERT_DEVICE_HANDLE(DeviceHandle);
listEntry = &DeviceHandle->PipeHandleList;
if (!IsListEmpty(listEntry)) {
listEntry = DeviceHandle->PipeHandleList.Flink;
}
while (listEntry != &DeviceHandle->PipeHandleList) {
PUSBD_PIPE_HANDLE_I nextHandle;
nextHandle = (PUSBD_PIPE_HANDLE_I) CONTAINING_RECORD(
listEntry,
struct _USBD_PIPE_HANDLE_I,
ListEntry);
listEntry = nextHandle->ListEntry.Flink;
if (nextHandle == PipeHandle) {
found = TRUE;
break;
}
}
#if DBG
if (!found) {
USBPORT_KdPrint((1, "'bad pipe handle %x\n", PipeHandle));
DEBUG_BREAK();
}
#endif
return found;
}
NTSTATUS
USBPORT_SendCommand(
PUSBD_DEVICE_HANDLE DeviceHandle,
PDEVICE_OBJECT FdoDeviceObject,
PUSB_DEFAULT_PIPE_SETUP_PACKET SetupPacket,
PVOID Buffer,
ULONG BufferLength,
PULONG BytesReturned,
USBD_STATUS *UsbdStatus
)
/*++
Routine Description:
Send a standard USB command on the default pipe.
essentially what we do here is build a control
transfer and queue it directly
Arguments:
DeviceHandle - ptr to USBPORT device structure the command will be sent to
DeviceObject -
RequestCode -
WValue - wValue for setup packet
WIndex - wIndex for setup packet
WLength - wLength for setup packet
Buffer - Input/Output Buffer for command
BufferLength - Length of Input/Output buffer.
BytesReturned - pointer to ulong to copy number of bytes
returned (optional)
UsbStatus - USBPORT status code returned in the URB.
Return Value:
STATUS_SUCCESS if successful,
STATUS_UNSUCCESSFUL otherwise
--*/
{
NTSTATUS ntStatus;
PTRANSFER_URB urb = NULL;
PUSBD_PIPE_HANDLE_I defaultPipe = &(DeviceHandle->DefaultPipe);
PDEVICE_EXTENSION devExt;
USBD_STATUS usbdStatus = USBD_STATUS_SUCCESS;
KEVENT event;
PAGED_CODE();
USBPORT_KdPrint((2, "'enter USBPORT_SendCommand\n"));
ASSERT_DEVICE_HANDLE(DeviceHandle);
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_ASSERT((USHORT)BufferLength == SetupPacket->wLength);
LOGENTRY(defaultPipe->Endpoint,
FdoDeviceObject, LOG_MISC, 'SENc', 0, 0, 0);
KeInitializeEvent(&event,
NotificationEvent,
FALSE);
ALLOC_POOL_Z(urb, NonPagedPool,
sizeof(struct _TRANSFER_URB));
if (urb) {
InterlockedIncrement(&DeviceHandle->PendingUrbs);
ntStatus = STATUS_SUCCESS;
usbdStatus = USBD_STATUS_SUCCESS;
urb->Hdr.Length = sizeof(struct _TRANSFER_URB);
urb->Hdr.Function = URB_FUNCTION_CONTROL_TRANSFER;
RtlCopyMemory(urb->u.SetupPacket,
SetupPacket,
8);
urb->TransferFlags = USBD_SHORT_TRANSFER_OK;
urb->UsbdPipeHandle = defaultPipe;
urb->Hdr.UsbdDeviceHandle = DeviceHandle;
urb->Hdr.UsbdFlags = 0;
// USBPORT is responsible for setting the transfer direction
//
// TRANSFER direction is implied in the command
if (SetupPacket->bmRequestType.Dir == BMREQUEST_DEVICE_TO_HOST) {
USBPORT_SET_TRANSFER_DIRECTION_IN(urb->TransferFlags);
} else {
USBPORT_SET_TRANSFER_DIRECTION_OUT(urb->TransferFlags);
}
urb->TransferBufferLength = BufferLength;
urb->TransferBuffer = Buffer;
urb->TransferBufferMDL = NULL;
if (urb->TransferBufferLength != 0) {
if ((urb->TransferBufferMDL =
IoAllocateMdl(urb->TransferBuffer,
urb->TransferBufferLength,
FALSE,
FALSE,
NULL)) == NULL) {
usbdStatus = USBD_STATUS_INSUFFICIENT_RESOURCES;
// map the error
ntStatus = USBPORT_SetUSBDError(NULL, usbdStatus);
} else {
SET_FLAG(urb->Hdr.UsbdFlags, USBPORT_REQUEST_MDL_ALLOCATED);
MmBuildMdlForNonPagedPool(
urb->TransferBufferMDL);
}
}
LOGENTRY(defaultPipe->Endpoint, FdoDeviceObject,
LOG_MISC, 'sndC',
urb->TransferBufferLength,
SetupPacket->bmRequestType.B,
SetupPacket->bRequest);
USBPORT_KdPrint((2,
"'SendCommand cmd = 0x%x 0x%x buffer = 0x%x length = 0x%x direction = 0x%x\n",
SetupPacket->bmRequestType.B,
SetupPacket->bRequest,
urb->TransferBuffer,
urb->TransferBufferLength,
urb->TransferFlags));
// queue the transfer
if (NT_SUCCESS(ntStatus)) {
usbdStatus = USBPORT_AllocTransfer(FdoDeviceObject,
urb,
NULL,
NULL,
&event,
5000);
if (USBD_SUCCESS(usbdStatus)) {
// do the transfer, 5 second timeout
// match the decrement in queue transferurb
InterlockedIncrement(&DeviceHandle->PendingUrbs);
USBPORT_QueueTransferUrb(urb);
LOGENTRY(NULL, FdoDeviceObject,
LOG_MISC, 'sWTt', 0, 0, 0);
// wait for completion
KeWaitForSingleObject(&event,
Suspended,
KernelMode,
FALSE,
NULL);
LOGENTRY(NULL, FdoDeviceObject,
LOG_MISC, 'sWTd', 0, 0, 0);
// map the error
usbdStatus = urb->Hdr.Status;
}
ntStatus =
SET_USBD_ERROR(urb, usbdStatus);
if (BytesReturned) {
*BytesReturned = urb->TransferBufferLength;
}
if (UsbdStatus) {
*UsbdStatus = usbdStatus;
}
}
// free the transfer URB
InterlockedDecrement(&DeviceHandle->PendingUrbs);
FREE_POOL(FdoDeviceObject, urb);
} else {
if (UsbdStatus) {
*UsbdStatus = USBD_STATUS_INSUFFICIENT_RESOURCES;
ntStatus = USBPORT_SetUSBDError(NULL, *UsbdStatus);
} else {
ntStatus = USBPORT_SetUSBDError(NULL, USBD_STATUS_INSUFFICIENT_RESOURCES);
}
}
//LOGENTRY(defaultPipe->Endpoint,
// FdoDeviceObject, LOG_MISC, 'SENd', 0, ntStatus, usbdStatus);
USBPORT_KdPrint((2, "'exit USBPORT_SendCommand 0x%x\n", ntStatus));
return ntStatus;
}
NTSTATUS
USBPORT_PokeEndpoint(
PDEVICE_OBJECT FdoDeviceObject,
PHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
This function closes an existing endpoint in the miniport,
frees the common buffer the reopens it with new requirements
and parameters.
This function is synchronous and assumes no active transfers
are pending for the endpoint.
This function is currently used to grow the transfer parameters
on interrupt and control endpoints and to change the address of
the default control endpoint
NOTES:
1. for now we assume no changes to bw allocation
2. new parameters are set before the call in the endpoint
structure
Arguments:
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus;
ENDPOINT_REQUIREMENTS requirements;
USB_MINIPORT_STATUS mpStatus;
PDEVICE_EXTENSION devExt;
PUSBPORT_COMMON_BUFFER commonBuffer;
LONG busy;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
// close the endpoint in the miniport
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'poke', Endpoint, 0, 0);
// mark the endpoint busy so that we don't poll it until
// we open it again
do {
busy = InterlockedIncrement(&Endpoint->Busy);
if (!busy) {
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'pNby', 0, Endpoint, busy);
break;
}
// defer processing
InterlockedDecrement(&Endpoint->Busy);
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'pbsy', 0, Endpoint, busy);
USBPORT_Wait(FdoDeviceObject, 1);
} while (busy != 0);
ACQUIRE_ENDPOINT_LOCK(Endpoint, FdoDeviceObject, 'Ley0');
MP_SetEndpointState(devExt, Endpoint, ENDPOINT_REMOVE);
RELEASE_ENDPOINT_LOCK(Endpoint, FdoDeviceObject, 'Uey0');
USBPORT_Wait(FdoDeviceObject, 2);
// enpoint should now be out of the schedule
// zero miniport data
RtlZeroMemory(&Endpoint->MiniportEndpointData[0],
REGISTRATION_PACKET(devExt).EndpointDataSize);
// free the old miniport common buffer
if (Endpoint->CommonBuffer) {
USBPORT_HalFreeCommonBuffer(FdoDeviceObject,
Endpoint->CommonBuffer);
Endpoint->CommonBuffer = NULL;
}
MP_QueryEndpointRequirements(devExt,
Endpoint,
&requirements);
// alloc new common buffer
// save the requirements
USBPORT_ASSERT(Endpoint->Parameters.TransferType != Bulk);
USBPORT_ASSERT(Endpoint->Parameters.TransferType != Isochronous);
USBPORT_KdPrint((1, "'(POKE) miniport requesting %d bytes\n",
requirements.MinCommonBufferBytes));
// allocate common buffer for this endpoint
if (requirements.MinCommonBufferBytes) {
commonBuffer =
USBPORT_HalAllocateCommonBuffer(FdoDeviceObject,
requirements.MinCommonBufferBytes);
} else {
commonBuffer = NULL;
}
if (commonBuffer == NULL &&
requirements.MinCommonBufferBytes) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
Endpoint->CommonBuffer = NULL;
} else {
Endpoint->CommonBuffer = commonBuffer;
ntStatus = STATUS_SUCCESS;
}
// this check is redundant but it keeps
// prefast happy
if (Endpoint->CommonBuffer &&
commonBuffer) {
Endpoint->Parameters.CommonBufferVa =
commonBuffer->MiniportVa;
Endpoint->Parameters.CommonBufferPhys =
commonBuffer->MiniportPhys;
Endpoint->Parameters.CommonBufferBytes =
commonBuffer->MiniportLength;
}
if (NT_SUCCESS(ntStatus)) {
MP_OpenEndpoint(devExt, Endpoint, mpStatus);
// in this UNIQUE situation this API is not allowed
// (and should not) fail
USBPORT_ASSERT(mpStatus == USBMP_STATUS_SUCCESS);
// we need to sync the endpoint state with
// the miniport, when first opened the miniport
// puts the endpoint in status HALT.
ACQUIRE_ENDPOINT_LOCK(Endpoint, FdoDeviceObject, 'LeK0');
ACQUIRE_STATECHG_LOCK(FdoDeviceObject, Endpoint);
if (Endpoint->CurrentState == ENDPOINT_ACTIVE) {
RELEASE_STATECHG_LOCK(FdoDeviceObject, Endpoint);
MP_SetEndpointState(devExt, Endpoint, ENDPOINT_ACTIVE);
} else {
RELEASE_STATECHG_LOCK(FdoDeviceObject, Endpoint);
}
RELEASE_ENDPOINT_LOCK(Endpoint, FdoDeviceObject, 'LeK0');
}
InterlockedDecrement(&Endpoint->Busy);
return ntStatus;
}
VOID
USBPORT_WaitActive(
PDEVICE_OBJECT FdoDeviceObject,
PHCD_ENDPOINT Endpoint
)
{
MP_ENDPOINT_STATE currentState;
ASSERT_ENDPOINT(Endpoint);
do {
ACQUIRE_ENDPOINT_LOCK(Endpoint, FdoDeviceObject, 'LeH0');
currentState = USBPORT_GetEndpointState(Endpoint);
RELEASE_ENDPOINT_LOCK(Endpoint, FdoDeviceObject, 'UeH0');
LOGENTRY(Endpoint,
FdoDeviceObject, LOG_XFERS, 'watA', Endpoint,
currentState, 0);
if (currentState == ENDPOINT_ACTIVE) {
// quick release
break;
}
ASSERT_PASSIVE();
USBPORT_Wait(FdoDeviceObject, 1);
} while (currentState != ENDPOINT_ACTIVE);
}
NTSTATUS
USBPORT_OpenEndpoint(
PUSBD_DEVICE_HANDLE DeviceHandle,
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_PIPE_HANDLE_I PipeHandle,
OUT USBD_STATUS *ReturnUsbdStatus,
BOOLEAN IsDefaultPipe
)
/*++
Routine Description:
open an endpoint on a USB device.
This function creates (initializes) endpoints and
hooks it to a pipehandle
Arguments:
DeviceHandle - data describes the device this endpoint is on.
DeviceObject - USBPORT device object.
ReturnUsbdStatus - OPTIONAL
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus;
PDEVICE_EXTENSION devExt;
PHCD_ENDPOINT endpoint;
USBD_STATUS usbdStatus;
ULONG siz;
BOOLEAN gotBw;
USB_HIGH_SPEED_MAXPACKET muxPacket;
extern ULONG USB2LIB_EndpointContextSize;
// this function is not pagable because we raise irql
// we should be at passive level
ASSERT_PASSIVE();
// devhandle should have been validated
// before we get here
ASSERT_DEVICE_HANDLE(DeviceHandle);
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
siz = sizeof(*endpoint) + REGISTRATION_PACKET(devExt).EndpointDataSize;
if (USBPORT_IS_USB20(devExt)) {
siz += USB2LIB_EndpointContextSize;
}
LOGENTRY(NULL, FdoDeviceObject,
LOG_PNP, 'opE+', PipeHandle, siz,
REGISTRATION_PACKET(devExt).EndpointDataSize);
// allocate the endoint
// * begin special case
// check for a no bandwidth endpoint ie max_oacket = 0
// if so return success and set the endpoint pointer
// in the pipe handle to a dummy value
if (PipeHandle->EndpointDescriptor.wMaxPacketSize == 0) {
USBPORT_AddPipeHandle(DeviceHandle,
PipeHandle);
PipeHandle->Endpoint = USB_BAD_PTR;
ntStatus = STATUS_SUCCESS;
SET_FLAG(PipeHandle->PipeStateFlags, USBPORT_PIPE_ZERO_BW);
CLEAR_FLAG(PipeHandle->PipeStateFlags, USBPORT_PIPE_STATE_CLOSED);
goto USBPORT_OpenEndpoint_Done;
}
// * end special case
ALLOC_POOL_Z(endpoint, NonPagedPool, siz);
if (endpoint) {
endpoint->Sig = SIG_ENDPOINT;
endpoint->Flags = 0;
endpoint->EndpointRef = 0;
endpoint->Busy = -1;
endpoint->FdoDeviceObject = FdoDeviceObject;
endpoint->DeviceHandle = DeviceHandle;
// USBPORT_ResetEndpointIdle(endpoint);
endpoint->Tt = DeviceHandle->Tt;
if (endpoint->Tt != NULL) {
ASSERT_TT(endpoint->Tt);
ExInterlockedInsertTailList(&DeviceHandle->Tt->EndpointList,
&endpoint->TtLink,
&devExt->Fdo.TtEndpointListSpin.sl);
}
if (USBPORT_IS_USB20(devExt)) {
PUCHAR pch;
pch = (PUCHAR) &endpoint->MiniportEndpointData[0];
pch += REGISTRATION_PACKET(devExt).EndpointDataSize;
endpoint->Usb2LibEpContext = pch;
} else {
endpoint->Usb2LibEpContext = USB_BAD_PTR;
}
#if DBG
USBPORT_LogAlloc(&endpoint->Log, 1);
#endif
LOGENTRY(endpoint, FdoDeviceObject,
LOG_PNP, 'ope+', PipeHandle, siz,
REGISTRATION_PACKET(devExt).EndpointDataSize);
// initialize the endpoint
InitializeListHead(&endpoint->ActiveList);
InitializeListHead(&endpoint->CancelList);
InitializeListHead(&endpoint->PendingList);
InitializeListHead(&endpoint->AbortIrpList);
USBPORT_InitializeSpinLock(&endpoint->ListSpin, 'EPL+', 'EPL-');
USBPORT_InitializeSpinLock(&endpoint->StateChangeSpin, 'SCL+', 'SCL-');
// extract some information from the
// descriptor
endpoint->Parameters.DeviceAddress =
DeviceHandle->DeviceAddress;
if (endpoint->Tt != NULL) {
ASSERT_TT(endpoint->Tt);
endpoint->Parameters.TtDeviceAddress =
endpoint->Tt->DeviceAddress;
} else {
endpoint->Parameters.TtDeviceAddress = 0xFFFF;
}
endpoint->Parameters.TtPortNumber =
DeviceHandle->TtPortNumber;
muxPacket.us = PipeHandle->EndpointDescriptor.wMaxPacketSize;
endpoint->Parameters.MuxPacketSize =
muxPacket.MaxPacket;
endpoint->Parameters.TransactionsPerMicroframe =
muxPacket.HSmux+1;
endpoint->Parameters.MaxPacketSize =
muxPacket.MaxPacket * (muxPacket.HSmux+1);
endpoint->Parameters.EndpointAddress =
PipeHandle->EndpointDescriptor.bEndpointAddress;
if ((PipeHandle->EndpointDescriptor.bmAttributes &
USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_ISOCHRONOUS) {
#ifdef ISO_LOG
USBPORT_LogAlloc(&endpoint->IsoLog, 4);
#endif
endpoint->Parameters.TransferType = Isochronous;
} else if ((PipeHandle->EndpointDescriptor.bmAttributes &
USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK) {
endpoint->Parameters.TransferType = Bulk;
} else if ((PipeHandle->EndpointDescriptor.bmAttributes &
USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT) {
endpoint->Parameters.TransferType = Interrupt;
} else {
USBPORT_ASSERT((PipeHandle->EndpointDescriptor.bmAttributes &
USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_CONTROL);
endpoint->Parameters.TransferType = Control;
}
// check for low speed
endpoint->Parameters.DeviceSpeed = DeviceHandle->DeviceSpeed;
// Set max transfer size based on transfer type
//
// Note: that the MaximumTransferSize set by the
// client driver in the pipe information structure
// is no longer used.
switch(endpoint->Parameters.TransferType) {
case Interrupt:
// this allows clients to put down larger
// interrupt buffers if they want without
// taking a perf hit. For some reason
// printers do this.
// bugbug research the praticality of splitting
// interrupt transfers for the miniports this may
// significantly reduce the memory allocated by
// he uhci miniport
endpoint->Parameters.MaxTransferSize = 1024;
//endpoint->Parameters.MaxPacketSize;
break;
case Control:
// 4k
// node that the old win2k 4k usb stack does not actually
// handle transfers larger than this correctly.
endpoint->Parameters.MaxTransferSize = 1024*4;
// set the default to 64k if this is not the control endpoint
if (endpoint->Parameters.EndpointAddress != 0) {
// the COMPAQ guys test this
endpoint->Parameters.MaxTransferSize = 1024*64;
}
break;
case Bulk:
// 64k default
endpoint->Parameters.MaxTransferSize = 1024*64;
break;
case Isochronous:
// there is no reason to have a limit here
// choose a really large default
endpoint->Parameters.MaxTransferSize = 0x01000000;
break;
}
endpoint->Parameters.Period = 0;
// compute period required
if (endpoint->Parameters.TransferType == Interrupt) {
UCHAR tmp;
UCHAR hsInterval;
if (endpoint->Parameters.DeviceSpeed == HighSpeed) {
// normalize the high speed period to microframes
// for USB 20 the period specifies a power of 2
// ie period = 2^(hsInterval-1)
hsInterval = PipeHandle->EndpointDescriptor.bInterval;
if (hsInterval) {
hsInterval--;
}
// hsInterval must be 0..5
if (hsInterval > 5) {
hsInterval = 5;
}
tmp = 1<<hsInterval;
} else {
tmp = PipeHandle->EndpointDescriptor.bInterval;
}
// this code finds the first interval
// <= USBPORT_MAX_INTEP_POLLING_INTERVAL
// valid intervals are:
// 1, 2, 4, 8, 16, 32(USBPORT_MAX_INTEP_POLLING_INTERVAL)
// Initialize Period, may be adjusted down
endpoint->Parameters.Period = USBPORT_MAX_INTEP_POLLING_INTERVAL;
if ((tmp != 0) && (tmp < USBPORT_MAX_INTEP_POLLING_INTERVAL)) {
// bInterval is in range. Adjust Period down if necessary.
if ((endpoint->Parameters.DeviceSpeed == LowSpeed) &&
(tmp < 8)) {
// bInterval is not valid for LowSpeed, cap Period at 8
endpoint->Parameters.Period = 8;
} else {
// Adjust Period down to greatest power of 2 less than or
// equal to bInterval.
while ((endpoint->Parameters.Period & tmp) == 0) {
endpoint->Parameters.Period >>= 1;
}
}
}
//!!!
//if (endpoint->Parameters.DeviceSpeed == LowSpeed) {
// TEST_TRAP();
// endpoint->Parameters.Period = 1;
//}
//!!!
endpoint->Parameters.MaxPeriod =
endpoint->Parameters.Period;
}
if (endpoint->Parameters.TransferType == Isochronous) {
endpoint->Parameters.Period = 1;
}
if (IS_ROOT_HUB(DeviceHandle)) {
SET_FLAG(endpoint->Flags, EPFLAG_ROOTHUB);
}
if (USB_ENDPOINT_DIRECTION_IN(
PipeHandle->EndpointDescriptor.bEndpointAddress)) {
endpoint->Parameters.TransferDirection = In;
} else {
endpoint->Parameters.TransferDirection = Out;
}
if (USBPORT_IS_USB20(devExt)) {
// call the engine and attempt to allocate the necessary
// bus time for this endoint
gotBw = USBPORT_AllocateBandwidthUSB20(FdoDeviceObject, endpoint);
//!!!
//if (endpoint->Parameters.DeviceSpeed == LowSpeed) {
// TEST_TRAP();
// endpoint->Parameters.InterruptScheduleMask = 0x10; //sMask;
// endpoint->Parameters.SplitCompletionMask = 0xc1; //cMask;
//}
//!!!
} else {
// * USB 1.1
endpoint->Parameters.Bandwidth =
USBPORT_CalculateUsbBandwidth(FdoDeviceObject, endpoint);
// caclualte the best schedule position
gotBw = USBPORT_AllocateBandwidthUSB11(FdoDeviceObject, endpoint);
}
if (gotBw) {
if (IsDefaultPipe ||
endpoint->Parameters.TransferType == Isochronous) {
// iso and default pipes do not halt on errors
// ie they do not require a resetpipe
endpoint->Parameters.EndpointFlags |= EP_PARM_FLAG_NOHALT;
}
ntStatus = STATUS_SUCCESS;
} else {
LOGENTRY(endpoint,
FdoDeviceObject, LOG_PNP, 'noBW', endpoint, 0, 0);
// no bandwidth error
ntStatus = USBPORT_SetUSBDError(NULL, USBD_STATUS_NO_BANDWIDTH);
if (ReturnUsbdStatus != NULL) {
*ReturnUsbdStatus = USBD_STATUS_NO_BANDWIDTH;
}
}
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
if (NT_SUCCESS(ntStatus)) {
// now do the open
if (TEST_FLAG(endpoint->Flags, EPFLAG_ROOTHUB)) {
PDEVICE_EXTENSION rhDevExt;
GET_DEVICE_EXT(rhDevExt, devExt->Fdo.RootHubPdo);
ASSERT_PDOEXT(rhDevExt);
// opens for the root hub device
// are not passed to the miniport
usbdStatus = USBD_STATUS_SUCCESS;
endpoint->EpWorkerFunction =
USBPORT_RootHub_EndpointWorker;
// successful open the enpoint defaults
// to active
endpoint->NewState =
endpoint->CurrentState = ENDPOINT_ACTIVE;
// track the hub interrupt endpoint
if (endpoint->Parameters.TransferType == Interrupt) {
rhDevExt->Pdo.RootHubInterruptEndpoint =
endpoint;
}
} else {
USB_MINIPORT_STATUS mpStatus;
PUSBPORT_COMMON_BUFFER commonBuffer;
ENDPOINT_REQUIREMENTS requirements;
ULONG ordinal;
// find out what we will need from the
// miniport for this endpoint
MP_QueryEndpointRequirements(devExt,
endpoint, &requirements);
// adjust maxtransfer based on miniport
// feedback.
switch (endpoint->Parameters.TransferType) {
case Bulk:
case Interrupt:
LOGENTRY(endpoint,
FdoDeviceObject, LOG_MISC, 'MaxT', endpoint,
requirements.MaximumTransferSize, 0);
EP_MAX_TRANSFER(endpoint) =
requirements.MaximumTransferSize;
break;
}
ordinal = USBPORT_SelectOrdinal(FdoDeviceObject,
endpoint);
USBPORT_KdPrint((1, "'miniport requesting %d bytes\n",
requirements.MinCommonBufferBytes));
// allocate common buffer for this endpoint
if (requirements.MinCommonBufferBytes) {
commonBuffer =
USBPORT_HalAllocateCommonBuffer(FdoDeviceObject,
requirements.MinCommonBufferBytes);
} else {
commonBuffer = NULL;
}
if (commonBuffer == NULL &&
requirements.MinCommonBufferBytes) {
mpStatus = USBMP_STATUS_NO_RESOURCES;
endpoint->CommonBuffer = NULL;
} else {
ULONG mpOptionFlags;
mpOptionFlags = REGISTRATION_PACKET(devExt).OptionFlags;
endpoint->CommonBuffer = commonBuffer;
if (commonBuffer != NULL) {
endpoint->Parameters.CommonBufferVa =
commonBuffer->MiniportVa;
endpoint->Parameters.CommonBufferPhys =
commonBuffer->MiniportPhys;
endpoint->Parameters.CommonBufferBytes =
commonBuffer->MiniportLength;
}
endpoint->Parameters.Ordinal = ordinal;
// call open request to minport
MP_OpenEndpoint(devExt, endpoint, mpStatus);
// note that once we call open this enpoint
// may show up on the Attention list
// set our internal flags based on what the
// miniport passed back
// if (endpoint->Parameters.EndpointFlags & EP_PARM_FLAG_DMA) {
SET_FLAG(endpoint->Flags, EPFLAG_MAP_XFERS);
if (TEST_FLAG(mpOptionFlags, USB_MINIPORT_OPT_NO_PNP_RESOURCES)) {
// no mapping for the virtual bus
CLEAR_FLAG(endpoint->Flags, EPFLAG_MAP_XFERS);
SET_FLAG(endpoint->Flags, EPFLAG_VBUS);
}
SET_FLAG(endpoint->Flags, EPFLAG_VIRGIN);
endpoint->EpWorkerFunction =
USBPORT_DmaEndpointWorker;
// } else {
// non-dma endpoint
// TEST_TRAP();
// }
}
// successful open the enpoint defaults
// to pause, we need to move it to active
if (mpStatus == USBMP_STATUS_SUCCESS) {
ACQUIRE_ENDPOINT_LOCK(endpoint, FdoDeviceObject, 'LeF0');
// initialize endpoint state machine
endpoint->CurrentState = ENDPOINT_PAUSE;
endpoint->NewState = ENDPOINT_PAUSE;
endpoint->CurrentStatus = ENDPOINT_STATUS_RUN;
USBPORT_SetEndpointState(endpoint, ENDPOINT_ACTIVE);
RELEASE_ENDPOINT_LOCK(endpoint, FdoDeviceObject, 'UeF0');
// Wait for endpoint to go active. The reason here
// is that an iso driver (usbaudio) will immediatly
// send transfers to the endpoint, these tranfers are
// marked with a transmission frame on submission but
// will have to wait until the endpoint is active to
// be programmed, hence they arrive in the miniport
// too late on slower systems.
USBPORT_WaitActive(FdoDeviceObject,
endpoint);
}
usbdStatus = MPSTATUS_TO_USBSTATUS(mpStatus);
}
// convert usb status to nt status
ntStatus = USBPORT_SetUSBDError(NULL, usbdStatus);
if (ReturnUsbdStatus != NULL) {
*ReturnUsbdStatus = usbdStatus;
}
}
if (NT_SUCCESS(ntStatus)) {
USBPORT_AddPipeHandle(DeviceHandle,
PipeHandle);
// track the endpoint
ExInterlockedInsertTailList(&devExt->Fdo.GlobalEndpointList,
&endpoint->GlobalLink,
&devExt->Fdo.EndpointListSpin.sl);
PipeHandle->Endpoint = endpoint;
CLEAR_FLAG(PipeHandle->PipeStateFlags, USBPORT_PIPE_STATE_CLOSED);
} else {
if (endpoint) {
if (endpoint->Tt != NULL) {
ASSERT_TT(endpoint->Tt);
USBPORT_InterlockedRemoveEntryList(&endpoint->TtLink,
&devExt->Fdo.TtEndpointListSpin.sl);
}
USBPORT_LogFree(FdoDeviceObject, &endpoint->Log);
UNSIG(endpoint);
FREE_POOL(FdoDeviceObject, endpoint);
}
}
USBPORT_OpenEndpoint_Done:
return ntStatus;
}
VOID
USBPORT_CloseEndpoint(
PUSBD_DEVICE_HANDLE DeviceHandle,
PDEVICE_OBJECT FdoDeviceObject,
PHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
Close an Endpoint
Arguments:
DeviceHandle - ptr to USBPORT device data structure.
DeviceObject - USBPORT device object.
Return Value:
STATUS_SUCCESS if successful,
STATUS_UNSUCCESSFUL otherwise
--*/
{
NTSTATUS ntStatus = 0;
PURB urb;
PDEVICE_EXTENSION devExt;
USB_MINIPORT_STATUS mpStatus;
KIRQL irql;
BOOLEAN stallClose;
LONG busy;
// should have been validated before we
// get here
ASSERT_DEVICE_HANDLE(DeviceHandle);
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
// we should have no requests queued to the
// endpoint
LOGENTRY(Endpoint, FdoDeviceObject,
LOG_MISC, 'clEP', Endpoint, 0, 0);
// USBPORT_ResetEndpointIdle(Endpoint);
// remove from our 'Active' lists
KeAcquireSpinLock(&devExt->Fdo.EndpointListSpin.sl, &irql);
if (TEST_FLAG(Endpoint->Flags, EPFLAG_ROOTHUB) &&
Endpoint->Parameters.TransferType == Interrupt) {
KIRQL rhIrql;
PDEVICE_EXTENSION rhDevExt;
// remove references to th eroot hub
ACQUIRE_ROOTHUB_LOCK(FdoDeviceObject, rhIrql);
// we should have a root hub pdo since we are closing
// an endpoint associated with it.
USBPORT_ASSERT(devExt->Fdo.RootHubPdo != NULL);
GET_DEVICE_EXT(rhDevExt, devExt->Fdo.RootHubPdo);
ASSERT_PDOEXT(rhDevExt);
rhDevExt->Pdo.RootHubInterruptEndpoint = NULL;
RELEASE_ROOTHUB_LOCK(FdoDeviceObject, rhIrql);
}
KeReleaseSpinLock(&devExt->Fdo.EndpointListSpin.sl, irql);
// The client is locked out at this point ie he can't access the
// pipe tied to the endpoint. We need to wait for any outstanding
// stuff to complete -- including any state changes, after which
// we can ask the coreworker to remove the endpoint.
// the endpoint lock protects the lists -- which need to be
// empty
do {
stallClose = FALSE;
ACQUIRE_ENDPOINT_LOCK(Endpoint, FdoDeviceObject, 'LeD1');
if (!IsListEmpty(&Endpoint->PendingList)) {
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'stc1', Endpoint, 0, 0);
stallClose = TRUE;
}
if (!IsListEmpty(&Endpoint->ActiveList)) {
LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'stc2', Endpoint, 0, 0);
stallClose = TRUE;
}
if (!IsListEmpty(&Endpoint->CancelList)) {
LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'stc3', Endpoint, 0, 0);
stallClose = TRUE;
}
if (!IsListEmpty(&Endpoint->AbortIrpList)) {
LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'stc4', Endpoint, 0, 0);
stallClose = TRUE;
}
if (Endpoint->EndpointRef) {
LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'stc6', Endpoint, 0, 0);
stallClose = TRUE;
}
ACQUIRE_STATECHG_LOCK(FdoDeviceObject, Endpoint);
if (Endpoint->CurrentState !=
Endpoint->NewState) {
LOGENTRY(Endpoint, FdoDeviceObject, LOG_XFERS, 'stc5', Endpoint, 0, 0);
stallClose = TRUE;
}
RELEASE_STATECHG_LOCK(FdoDeviceObject, Endpoint);
RELEASE_ENDPOINT_LOCK(Endpoint, FdoDeviceObject, 'UeD1');
// last check...
// synchronize with worker
// we just need to wait for worker to finish if it is running
// it should not pick up and run again unless it has stuff to
// do -- in which case stallClose will already be set.
busy = InterlockedIncrement(&Endpoint->Busy);
if (busy) {
// defer processing
LOGENTRY(Endpoint,
FdoDeviceObject, LOG_XFERS, 'clby', 0, Endpoint, 0);
stallClose = TRUE;
}
InterlockedDecrement(&Endpoint->Busy);
if (stallClose) {
LOGENTRY(Endpoint,
FdoDeviceObject, LOG_XFERS, 'stlC', 0, Endpoint, 0);
USBPORT_Wait(FdoDeviceObject, 1);
}
} while (stallClose);
LOGENTRY(Endpoint,
FdoDeviceObject, LOG_XFERS, 'CLdn', 0, Endpoint, 0);
// unlink ref to device handle since it will be removed when
// all endpoints are closed
Endpoint->DeviceHandle = NULL;
// lock the endpoint & set the state to remove and
// free the bw
if (USBPORT_IS_USB20(devExt)) {
PTRANSACTION_TRANSLATOR tt;
USBPORT_FreeBandwidthUSB20(FdoDeviceObject, Endpoint);
KeAcquireSpinLock(&devExt->Fdo.TtEndpointListSpin.sl, &irql);
tt = Endpoint->Tt;
if (tt != NULL) {
ASSERT_TT(tt);
USBPORT_ASSERT(Endpoint->TtLink.Flink != NULL);
USBPORT_ASSERT(Endpoint->TtLink.Blink != NULL);
RemoveEntryList(&Endpoint->TtLink);
Endpoint->TtLink.Flink = NULL;
Endpoint->TtLink.Blink = NULL;
if (TEST_FLAG(tt->TtFlags, USBPORT_TTFLAG_REMOVED) &&
IsListEmpty(&tt->EndpointList)) {
ULONG i, bandwidth;
USBPORT_UpdateAllocatedBwTt(tt);
// alloc new
bandwidth = tt->MaxAllocedBw;
for (i=0; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i++) {
devExt->Fdo.BandwidthTable[i] += bandwidth;
}
// last endpoint free the TT if it is marked gone
FREE_POOL(FdoDeviceObject, tt);
}
}
KeReleaseSpinLock(&devExt->Fdo.TtEndpointListSpin.sl, irql);
} else {
USBPORT_FreeBandwidthUSB11(FdoDeviceObject, Endpoint);
}
ACQUIRE_ENDPOINT_LOCK(Endpoint, FdoDeviceObject, 'LeD0');
USBPORT_SetEndpointState(Endpoint, ENDPOINT_REMOVE);
RELEASE_ENDPOINT_LOCK(Endpoint, FdoDeviceObject, 'UeD0');
// the endpoint will be freed when it reaches the 'REMOVE'
// state
// endpointWorker will be signalled when a frame has passed
// at that time the endpoint will be moved to the closed list
// and the worker thread will be signalled to flush the closed
// endpoints ie free the common buffer.
USBPORT_SignalWorker(FdoDeviceObject);
return;
}
VOID
USBPORT_ClosePipe(
PUSBD_DEVICE_HANDLE DeviceHandle,
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_PIPE_HANDLE_I PipeHandle
)
/*++
Routine Description:
Close a USB pipe and the endpoint associated with it
This is a synchronous operation that waits for all
transfers associated with the pipe to be completed.
Arguments:
DeviceHandle - ptr to USBPORT device data structure.
DeviceObject - USBPORT device object.
PipeHandle - USBPORT pipe handle associated with the endpoint.
Return Value:
STATUS_SUCCESS if successful,
STATUS_UNSUCCESSFUL otherwise
--*/
{
NTSTATUS ntStatus = 0;
PDEVICE_EXTENSION devExt;
// should have beed validated before we
// get here
ASSERT_DEVICE_HANDLE(DeviceHandle);
ASSERT_PIPE_HANDLE(PipeHandle);
LOGENTRY(NULL, FdoDeviceObject,
LOG_MISC, 'clPI', PipeHandle, 0, 0);
if (PipeHandle->PipeStateFlags & USBPORT_PIPE_STATE_CLOSED) {
// already closed
// generally when a pertially open interface needs to
// be closed due to an error
USBPORT_ASSERT(PipeHandle->ListEntry.Flink == NULL &&
PipeHandle->ListEntry.Blink == NULL);
return;
}
// invalidate the pipe
USBPORT_RemovePipeHandle(DeviceHandle,
PipeHandle);
SET_FLAG(PipeHandle->PipeStateFlags, USBPORT_PIPE_STATE_CLOSED);
// at this point the client will be unable to queue
// any transfers to this pipe or endpoint
// BUGBUG flush tranfers and wait, this also includes waiting
// for any state changes to complete
LOGENTRY(NULL, FdoDeviceObject,
LOG_MISC, 'pipW', PipeHandle, 0, 0);
// KeWait(PipeEvent) {
// }
// now 'close' the endpoint
if (TEST_FLAG(PipeHandle->PipeStateFlags, USBPORT_PIPE_ZERO_BW)) {
CLEAR_FLAG(PipeHandle->PipeStateFlags, USBPORT_PIPE_ZERO_BW);
} else {
USBPORT_CloseEndpoint(DeviceHandle,
FdoDeviceObject,
PipeHandle->Endpoint);
}
}
VOID
USBPORT_FlushClosedEndpointList(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
walk the "closed" endpoint list and close any endpoints
that are ready.
endpoints are placed on the 'closed' list when they reach the
removed state.
Arguments:
Return Value:
--*/
{
PDEVICE_EXTENSION devExt;
PLIST_ENTRY listEntry;
PHCD_ENDPOINT endpoint;
KIRQL irql;
BOOLEAN closed = TRUE;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
LOGENTRY(NULL, FdoDeviceObject,
LOG_NOISY, 'fCLO', FdoDeviceObject, 0, 0);
// stall any closes
KeAcquireSpinLock(&devExt->Fdo.EpClosedListSpin.sl, &irql);
while (!IsListEmpty(&devExt->Fdo.EpClosedList) &&
closed) {
listEntry = RemoveHeadList(&devExt->Fdo.EpClosedList);
endpoint = (PHCD_ENDPOINT) CONTAINING_RECORD(
listEntry,
struct _HCD_ENDPOINT,
ClosedLink);
LOGENTRY(NULL, FdoDeviceObject,
LOG_PNP, 'fclo', endpoint, 0, 0);
ASSERT_ENDPOINT(endpoint);
USBPORT_ASSERT(endpoint->CurrentState == ENDPOINT_CLOSED);
endpoint->ClosedLink.Flink = NULL;
endpoint->ClosedLink.Blink = NULL;
KeReleaseSpinLock(&devExt->Fdo.EpClosedListSpin.sl, irql);
// if we are unable to close now we must bail so the
// worker function can run
closed = USBPORT_LazyCloseEndpoint(FdoDeviceObject, endpoint);
KeAcquireSpinLock(&devExt->Fdo.EpClosedListSpin.sl, &irql);
}
KeReleaseSpinLock(&devExt->Fdo.EpClosedListSpin.sl, irql);
}
BOOLEAN
USBPORT_LazyCloseEndpoint(
PDEVICE_OBJECT FdoDeviceObject,
PHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
Close an Endpoint. Put the endpoint on our list
of endpoints-to-close and wakeup the worker thread.
Arguments:
Return Value:
returns true if closed
--*/
{
PDEVICE_EXTENSION devExt;
KIRQL irql;
BOOLEAN closed;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
LOGENTRY(NULL, FdoDeviceObject,
LOG_XFERS, 'frEP', Endpoint, 0, 0);
// endpoint is no longer on the global list, now we just need
// to make sure no one has a reference to it before we delete
// it.
// The endpoint may have been invalidated ie on the Attention
// List before being removed from the global list to avoid this
// potential conlict we check here until both the busy flag is
// -1 (meaning coreworker is thru) AND AttendLink is NULL
// if it is busy we put it back on the closed list
if (IS_ON_ATTEND_LIST(Endpoint) ||
Endpoint->Busy != -1) {
// still have work to do, put the endpoint back on
// the close list
KeAcquireSpinLock(&devExt->Fdo.EndpointListSpin.sl, &irql);
LOGENTRY(NULL, FdoDeviceObject, LOG_XFERS, 'CLOr', 0, Endpoint, 0);
// it is OK to be on the attention list and the closed
// list
USBPORT_ASSERT(Endpoint->ClosedLink.Flink == NULL);
USBPORT_ASSERT(Endpoint->ClosedLink.Blink == NULL);
ExInterlockedInsertTailList(&devExt->Fdo.EpClosedList,
&Endpoint->ClosedLink,
&devExt->Fdo.EpClosedListSpin.sl);
KeReleaseSpinLock(&devExt->Fdo.EndpointListSpin.sl, irql);
closed = FALSE;
} else {
// remove from global list
KeAcquireSpinLock(&devExt->Fdo.EndpointListSpin.sl, &irql);
RemoveEntryList(&Endpoint->GlobalLink);
Endpoint->GlobalLink.Flink = NULL;
Endpoint->GlobalLink.Blink = NULL;
KeReleaseSpinLock(&devExt->Fdo.EndpointListSpin.sl, irql);
// free endpoint memory
if (Endpoint->CommonBuffer) {
USBPORT_HalFreeCommonBuffer(FdoDeviceObject,
Endpoint->CommonBuffer);
}
USBPORT_LogFree(FdoDeviceObject, &Endpoint->Log);
#ifdef ISO_LOG
USBPORT_LogFree(FdoDeviceObject, &Endpoint->IsoLog);
#endif
UNSIG(Endpoint);
FREE_POOL(FdoDeviceObject, Endpoint);
closed = TRUE;
}
return closed;
}
VOID
USBPORT_FreeUsbAddress(
PDEVICE_OBJECT FdoDeviceObject,
USHORT DeviceAddress
)
/*++
Routine Description:
Arguments:
Return Value:
Valid USB address (1..127) to use for this device,
returns 0 if no device address available.
--*/
{
PDEVICE_EXTENSION devExt;
USHORT address = 0, i, j;
ULONG bit;
PAGED_CODE();
// we should never see a free to device address 0
USBPORT_ASSERT(DeviceAddress != 0);
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
for (j=0; j<4; j++) {
bit = 1;
for (i=0; i<32; i++) {
address = (USHORT)(j*32+i);
if (address == DeviceAddress) {
devExt->Fdo.AddressList[j] &= ~bit;
goto USBPORT_FreeUsbAddress_Done;
}
bit = bit<<1;
}
}
USBPORT_FreeUsbAddress_Done:
USBPORT_KdPrint((3, "'USBPORT free Address %d\n", address));
}
USHORT
USBPORT_AllocateUsbAddress(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Arguments:
Return Value:
Valid USB address (1..127) to use for this device,
returns 0 if no device address available.
--*/
{
PDEVICE_EXTENSION devExt;
USHORT address, i, j;
ULONG bit;
PAGED_CODE();
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
address = 0;
for (j=0; j<4; j++) {
bit = 1;
for (i=0; i<32; i++) {
if (!(devExt->Fdo.AddressList[j] & bit)) {
devExt->Fdo.AddressList[j] |= bit;
address = (USHORT)(j*32+i);
goto USBPORT_AllocateUsbAddress_Done;
}
bit = bit<<1;
}
}
// no free addresses?
USBPORT_ASSERT(0);
USBPORT_AllocateUsbAddress_Done:
USBPORT_KdPrint((3, "'USBPORT assigning Address %d\n", address));
return address;
}
NTSTATUS
USBPORT_InitializeHsHub(
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_DEVICE_HANDLE HubDeviceHandle,
ULONG TtCount
)
/*++
Routine Description:
Service exported for use by the hub driver
This service initializes a high speed hub
Arguments:
HubDeviceHandle - DeviceHandle for the creating USB Hub
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus;
ULONG i;
LOGENTRY(NULL, FdoDeviceObject,
LOG_MISC, 'ihsb', 0, HubDeviceHandle, TtCount);
// hub driver might pass us NULL if it could not
// retrieve a device handle
if (HubDeviceHandle == NULL) {
return STATUS_INVALID_PARAMETER;
}
ASSERT_DEVICE_HANDLE(HubDeviceHandle)
USBPORT_ASSERT(HubDeviceHandle->DeviceSpeed == HighSpeed);
if (IS_ROOT_HUB(HubDeviceHandle)) {
// no TTs for the root hub yet
return STATUS_SUCCESS;
}
USBPORT_ASSERT(HubDeviceHandle->DeviceDescriptor.bDeviceClass ==
USB_DEVICE_CLASS_HUB);
USBPORT_ASSERT(TEST_FLAG(HubDeviceHandle->DeviceFlags,
USBPORT_DEVICEFLAG_HSHUB));
for (i=0; i< TtCount; i++) {
ntStatus = USBPORT_InitializeTT(FdoDeviceObject,
HubDeviceHandle,
(USHORT)i+1);
if(!NT_SUCCESS(ntStatus)) {
break;
}
}
HubDeviceHandle->TtCount = TtCount;
return ntStatus;
}
NTSTATUS
USBPORT_CreateDevice(
PUSBD_DEVICE_HANDLE *DeviceHandle,
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_DEVICE_HANDLE HubDeviceHandle,
USHORT PortStatus,
USHORT PortNumber
)
/*++
Routine Description:
Service exported for use by the hub driver
Called for each new device on the USB bus, this function sets
up the internal data structures we need to keep track of the
device and assigns it an address.
Arguments:
DeviceHandle - ptr to return the ptr to the new device structure
created by this routine
DeviceObject - USBPORT device object for the USB bus this device is on.
HubDeviceHandle - DeviceHandle for the creating USB Hub
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus;
PUSBD_DEVICE_HANDLE deviceHandle;
PUSBD_PIPE_HANDLE_I defaultPipe;
PDEVICE_EXTENSION devExt;
ULONG bytesReturned = 0;
PUCHAR data = NULL;
BOOLEAN open = FALSE;
ULONG dataSize;
PTRANSACTION_TRANSLATOR tt = NULL;
USHORT ttPort;
PAGED_CODE();
USBPORT_KdPrint((2, "'CreateDevice\n"));
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
//
// first validate the deviceHandle for the creating hub, we need
// this information for USB 1.1 devices behind a USB 2.0 hub.
//
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'crD>', HubDeviceHandle,
PortNumber, PortStatus);
// NOTE: this actually locks all device handles
LOCK_DEVICE(HubDeviceHandle, FdoDeviceObject);
if (!USBPORT_ValidateDeviceHandle(FdoDeviceObject,
HubDeviceHandle,
FALSE)) {
// this is most likely a bug in the hub driver
DEBUG_BREAK();
UNLOCK_DEVICE(DeviceHandle, FdoDeviceObject);
// fail the create if the hubs device handle is bugus
// chances are that the device handle is bad becuse the
// device is gone.
return STATUS_DEVICE_NOT_CONNECTED;
}
// start at the port for this device,
// if this is a 1.1 device in a 1.1 hub
// downstream of a 2.0 hub then we need
// the port number from the 1.1 hub
ttPort = PortNumber;
// port status tells us the type of device we are dealing with
if (USBPORT_IS_USB20(devExt) &&
!TEST_FLAG(PortStatus, PORT_STATUS_HIGH_SPEED)) {
// walk upstream until we reach a USB 2.0 hub
// this hub will conatin the appropriate TT
tt = USBPORT_GetTt(FdoDeviceObject,
HubDeviceHandle,
&ttPort);
}
UNLOCK_DEVICE(DeviceHandle, FdoDeviceObject);
ALLOC_POOL_Z(deviceHandle, NonPagedPool,
sizeof(USBD_DEVICE_HANDLE));
*DeviceHandle = NULL;
if (deviceHandle == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
LOGENTRY(NULL,
FdoDeviceObject, LOG_MISC, 'CRED', 0, 0, deviceHandle);
deviceHandle->PendingUrbs = 0;
deviceHandle->HubDeviceHandle = HubDeviceHandle;
deviceHandle->ConfigurationHandle = NULL;
deviceHandle->DeviceAddress = USB_DEFAULT_DEVICE_ADDRESS;
//deviceHandle->DeviceBandwidth = 0;
if (PortStatus & PORT_STATUS_LOW_SPEED) {
deviceHandle->DeviceSpeed = LowSpeed;
} else if (PortStatus & PORT_STATUS_HIGH_SPEED) {
deviceHandle->DeviceSpeed = HighSpeed;
} else {
deviceHandle->DeviceSpeed = FullSpeed;
}
deviceHandle->Sig = SIG_DEVICE_HANDLE;
// port number is maps to a specific tt but hub fw
// has to make sense of this.
deviceHandle->TtPortNumber = ttPort;
deviceHandle->Tt = tt;
LOCK_DEVICE(deviceHandle, FdoDeviceObject);
// buffer for our descriptor, one packet
data = (PUCHAR) &deviceHandle->DeviceDescriptor;
dataSize = sizeof(deviceHandle->DeviceDescriptor);
// **
// We need to talk to the device, first we open the default pipe
// using the defined max packet size (defined by USB spec as 8
// bytes until device receives the GET_DESCRIPTOR (device) command).
// We set the address get the device descriptor then close the pipe
// and re-open it with the correct max packet size.
// **
#define USB_DEFAULT_LS_MAX_PACKET 8
//
// open the default pipe for the device
//
defaultPipe = &deviceHandle->DefaultPipe;
if (deviceHandle->DeviceSpeed == LowSpeed) {
INITIALIZE_DEFAULT_PIPE(*defaultPipe, USB_DEFAULT_LS_MAX_PACKET);
} else {
INITIALIZE_DEFAULT_PIPE(*defaultPipe, USB_DEFAULT_MAX_PACKET);
}
InitializeListHead(&deviceHandle->PipeHandleList);
InitializeListHead(&deviceHandle->TtList);
ntStatus = USBPORT_OpenEndpoint(deviceHandle,
FdoDeviceObject,
defaultPipe,
NULL,
TRUE);
open = NT_SUCCESS(ntStatus);
bytesReturned = 0;
if (NT_SUCCESS(ntStatus)) {
//
// Configure the default pipe for this device and assign the
// device an address
//
// NOTE: if this operation fails it means that we have a device
// that will respond to the default endpoint and we can't change
// it.
// we have no choice but to disable the port on the hub this
// device is attached to.
//
//
// Get information about the device
//
USB_DEFAULT_PIPE_SETUP_PACKET setupPacket;
PUCHAR tmpDevDescBuf;
// Would you believe that there exist some devices that get confused
// if the very first Get Device Descriptor request does not have a
// wLength value of 0x40 even though the device only has a 0x12 byte
// Device Descriptor to return? Any change to the way devices have
// always been enumerated since the being of USB 1.0 time can cause
// bizarre consequences. Use a wLength value of 0x40 for the very
// first Get Device Descriptor request.
ALLOC_POOL_Z(tmpDevDescBuf, NonPagedPool,
USB_DEFAULT_MAX_PACKET);
if (tmpDevDescBuf == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
} else {
// setup packet for get device descriptor
USBPORT_INIT_SETUP_PACKET(setupPacket,
USB_REQUEST_GET_DESCRIPTOR, // bRequest
BMREQUEST_DEVICE_TO_HOST, // Dir
BMREQUEST_TO_DEVICE, // Recipient
BMREQUEST_STANDARD, // Type
USB_DESCRIPTOR_MAKE_TYPE_AND_INDEX(USB_DEVICE_DESCRIPTOR_TYPE, 0), // wValue
0, // wIndex
USB_DEFAULT_MAX_PACKET); // wLength
ntStatus = USBPORT_SendCommand(deviceHandle,
FdoDeviceObject,
&setupPacket,
tmpDevDescBuf,
USB_DEFAULT_MAX_PACKET,
&bytesReturned,
NULL);
// NOTE:
// at this point we only have the first 8 bytes of the
// device descriptor.
RtlCopyMemory(data, tmpDevDescBuf, dataSize);
FREE_POOL(FdoDeviceObject, tmpDevDescBuf);
}
}
// some devices babble so we ignore the error
// on this transaction if we got enough data
if (bytesReturned == 8 && !NT_SUCCESS(ntStatus)) {
USBPORT_KdPrint((1,
"'Error returned from get device descriptor -- ignored\n"));
ntStatus = STATUS_SUCCESS;
}
// validate the max packet value and descriptor
// we need at least eight bytes a value of zero
// in max packet is bogus
if (NT_SUCCESS(ntStatus) &&
(bytesReturned >= 8) &&
(deviceHandle->DeviceDescriptor.bLength >= sizeof(USB_DEVICE_DESCRIPTOR)) &&
(deviceHandle->DeviceDescriptor.bDescriptorType == USB_DEVICE_DESCRIPTOR_TYPE) &&
((deviceHandle->DeviceDescriptor.bMaxPacketSize0 == 0x08) ||
(deviceHandle->DeviceDescriptor.bMaxPacketSize0 == 0x10) ||
(deviceHandle->DeviceDescriptor.bMaxPacketSize0 == 0x20) ||
(deviceHandle->DeviceDescriptor.bMaxPacketSize0 == 0x40))) {
USBPORT_AddDeviceHandle(FdoDeviceObject, deviceHandle);
*DeviceHandle = deviceHandle;
} else {
PUCHAR p = (PUCHAR)&deviceHandle->DeviceDescriptor;
// print a big debug message
USBPORT_KdPrint((0, "'CREATEDEVICE failed enumeration %08X %02X\n",
ntStatus, bytesReturned));
USBPORT_KdPrint((0, "'%02X %02X %02X %02X %02X %02X %02X %02X"
" %02X %02X %02X %02X %02X %02X %02X %02X"
" %02X %02X\n",
p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],
p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15],
p[16],p[17]));
USBPORT_DebugClient((
"Bad Device Detected\n"));
DEBUG_BREAK();
//
// something went wrong, if we assigned any resources to
// the default pipe then we free them before we get out.
//
// we need to signal to the parent hub that this
// port is to be be disabled we will do this by
// returning an error.
ntStatus = STATUS_DEVICE_DATA_ERROR;
// if we opened a pipe close it
if (open) {
USBPORT_ClosePipe(deviceHandle,
FdoDeviceObject,
defaultPipe);
}
}
UNLOCK_DEVICE(deviceHandle, FdoDeviceObject);
if (!NT_SUCCESS(ntStatus)) {
UNSIG(deviceHandle);
FREE_POOL(FdoDeviceObject, deviceHandle);
}
}
USBPORT_CreateDevice_Done:
CATC_TRAP_ERROR(FdoDeviceObject, ntStatus);
LOGENTRY(NULL,
FdoDeviceObject, LOG_MISC, 'creD', 0, 0, ntStatus);
USBPORT_ENUMLOG(FdoDeviceObject, 'cdev', ntStatus, 0);
return ntStatus;
}
NTSTATUS
USBPORT_RemoveDevice(
PUSBD_DEVICE_HANDLE DeviceHandle,
PDEVICE_OBJECT FdoDeviceObject,
ULONG Flags
)
/*++
Routine Description:
Service exported for use by the hub driver
Called for each device on the USB bus that needs to be removed.
This routine frees the device handle and the address assigned
to the device.
Some new tricks here:
When this function is called it is asumed the client driver has
received the REMOVE irp and passed it on to the bus driver. We
remove the device handle from our list, this will cause any new
transfers submitted by the driver to be failed. Any current
transfers the driver has will be completed with error.
Once all transfer are flushed for all the endpoints we will close
the endpoints and free the device handle (ie) noone has any references
to it anymore.
This should -- in theory -- prevent bad drivers from crashing in usbport
or the miniport if they send requests after a remove.
Arguments:
DeviceHandle - ptr to device data structure created by class driver
in USBPORT_CreateDevice.
FdoDeviceObject - USBPORT device object for the USB bus this device is on.
Flags -
USBD_KEEP_DEVICE_DATA
USBD_MARK_DEVICE_BUSY - we don't use this one
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus;
PDEVICE_EXTENSION devExt;
PUSBD_PIPE_HANDLE_I defaultPipe;
USBD_STATUS usbdStatus;
if (Flags & USBD_KEEP_DEVICE_DATA) {
// keep data means keep the handle valid
return STATUS_SUCCESS;
}
if (Flags & USBD_MARK_DEVICE_BUSY) {
// This means stop accepting requests. Only used by USBHUB when
// handling a IOCTL_INTERNAL_USB_RESET_PORT request?? Need to do
// anything special here?? Need to keep the handle valid since it
// will be used to restore the device after the reset.
//
// GlenS note to JD: review this.
//
return STATUS_SUCCESS;
}
GET_DEVICE_EXT(devExt, FdoDeviceObject);
// assume success
ntStatus = STATUS_SUCCESS;
LOCK_DEVICE(DeviceHandle, FdoDeviceObject);
if (!USBPORT_ValidateDeviceHandle(FdoDeviceObject,
DeviceHandle,
FALSE)) {
// this is most likely a bug in the hub
// driver
DEBUG_BREAK();
UNLOCK_DEVICE(DeviceHandle, FdoDeviceObject);
// chances are that the device handle is bad becuse the
// device is gone.
return STATUS_DEVICE_NOT_CONNECTED;
}
LOGENTRY(NULL,
FdoDeviceObject, LOG_PNP, 'REMV', DeviceHandle, 0, 0);
// handle is no longer on our lists so all attempts
// to submit urbs by the client driver will now fail
USBPORT_RemoveDeviceHandle(FdoDeviceObject,
DeviceHandle);
SET_FLAG(DeviceHandle->DeviceFlags,
USBPORT_DEVICEFLAG_REMOVED);
USBPORT_AbortAllTransfers(FdoDeviceObject,
DeviceHandle);
// wait for any refs from non-transfer URBs to drain
while (InterlockedDecrement(&DeviceHandle->PendingUrbs) >= 0) {
LOGENTRY(NULL,
FdoDeviceObject, LOG_PNP, 'dPUR', DeviceHandle, 0,
DeviceHandle->PendingUrbs);
InterlockedIncrement(&DeviceHandle->PendingUrbs);
USBPORT_Wait(FdoDeviceObject, 100);
}
//
// make sure and clean up any open pipe handles
// the device may have
//
if (DeviceHandle->ConfigurationHandle) {
USBPORT_InternalCloseConfiguration(DeviceHandle,
FdoDeviceObject,
0);
}
defaultPipe = &DeviceHandle->DefaultPipe;
// we should aways have a default pipe, this will free
// the endpoint
USBPORT_ClosePipe(DeviceHandle,
FdoDeviceObject,
defaultPipe);
if (DeviceHandle->DeviceAddress != USB_DEFAULT_DEVICE_ADDRESS) {
USBPORT_FreeUsbAddress(FdoDeviceObject, DeviceHandle->DeviceAddress);
}
//
// free any Tt handles associated with this device handle
//
while (!IsListEmpty(&DeviceHandle->TtList)) {
PTRANSACTION_TRANSLATOR tt;
PLIST_ENTRY listEntry;
KIRQL irql;
listEntry = RemoveHeadList(&DeviceHandle->TtList);
tt = (PTRANSACTION_TRANSLATOR) CONTAINING_RECORD(
listEntry,
struct _TRANSACTION_TRANSLATOR,
TtLink);
ASSERT_TT(tt);
KeAcquireSpinLock(&devExt->Fdo.TtEndpointListSpin.sl, &irql);
SET_FLAG(tt->TtFlags, USBPORT_TTFLAG_REMOVED);
if (IsListEmpty(&tt->EndpointList)) {
ULONG i, bandwidth;
USBPORT_UpdateAllocatedBwTt(tt);
// alloc new
bandwidth = tt->MaxAllocedBw;
for (i=0; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i++) {
devExt->Fdo.BandwidthTable[i] += bandwidth;
}
FREE_POOL(FdoDeviceObject, tt);
}
KeReleaseSpinLock(&devExt->Fdo.TtEndpointListSpin.sl, irql);
}
UNLOCK_DEVICE(DeviceHandle, FdoDeviceObject);
if (!IS_ROOT_HUB(DeviceHandle)) {
ASSERT_DEVICE_HANDLE(DeviceHandle);
FREE_POOL(FdoDeviceObject, DeviceHandle);
}
return ntStatus;
}
NTSTATUS
USBPORT_InitializeDevice(
PUSBD_DEVICE_HANDLE DeviceHandle,
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
Service exported for use by the hub driver
Called for each device on the USB bus that needs to be initialized.
This routine allocates an address and assigns it to the device.
NOTE: on entry the the device descriptor in DeviceHandle is expected to
contain at least the first 8 bytes of the device descriptor, this
information is used to open the default pipe.
On Error the DeviceHandle structure is freed.
Arguments:
DeviceHandle - ptr to device data structure created by class driver
from a call to USBPORT_CreateDevice.
DeviceObject - USBPORT device object for the USB bus this device is on.
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus;
PUSBD_PIPE_HANDLE_I defaultPipe;
USHORT address;
PDEVICE_EXTENSION devExt;
USB_DEFAULT_PIPE_SETUP_PACKET setupPacket;
PAGED_CODE();
USBPORT_KdPrint((2, "'InitializeDevice\n"));
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_ASSERT(DeviceHandle != NULL);
LOCK_DEVICE(DeviceHandle, FdoDeviceObject);
defaultPipe = &DeviceHandle->DefaultPipe;
// assume success
ntStatus = STATUS_SUCCESS;
//
// Assign Address to the device
//
address = USBPORT_AllocateUsbAddress(FdoDeviceObject);
USBPORT_KdPrint((2, "'SetAddress, assigning 0x%x address\n", address));
LOGENTRY(NULL,
FdoDeviceObject, LOG_MISC, 'ADRa', DeviceHandle, 0, address);
USBPORT_ASSERT(DeviceHandle->DeviceAddress == USB_DEFAULT_DEVICE_ADDRESS);
// setup packet for set_address
USBPORT_INIT_SETUP_PACKET(setupPacket,
USB_REQUEST_SET_ADDRESS, // bRequest
BMREQUEST_HOST_TO_DEVICE, // Dir
BMREQUEST_TO_DEVICE, // Recipient
BMREQUEST_STANDARD, // Type
address, // wValue
0, // wIndex
0); // wLength
ntStatus = USBPORT_SendCommand(DeviceHandle,
FdoDeviceObject,
&setupPacket,
NULL,
0,
NULL,
NULL);
DeviceHandle->DeviceAddress = address;
if (NT_SUCCESS(ntStatus)) {
USB_MINIPORT_STATUS mpStatus;
//
// done with addressing process...
//
// poke the endpoint zero to the new address and
// the true max packet size for the default control.
// endpoint.
//
defaultPipe->Endpoint->Parameters.MaxPacketSize =
DeviceHandle->DeviceDescriptor.bMaxPacketSize0;
defaultPipe->Endpoint->Parameters.DeviceAddress = address;
//MP_PokeEndpoint(devExt, defaultPipe->Endpoint, mpStatus);
//ntStatus = MPSTATUS_TO_NTSTATUS(mpStatus);
ntStatus = USBPORT_PokeEndpoint(FdoDeviceObject, defaultPipe->Endpoint);
}
if (NT_SUCCESS(ntStatus)) {
ULONG bytesReturned;
USB_DEFAULT_PIPE_SETUP_PACKET setupPacket2;
// 10ms delay to allow devices to respond after
// the setaddress command
USBPORT_Wait(FdoDeviceObject, 10);
//
// Fetch the device descriptor again, this time
// get the whole thing.
//
// setup packet for get device descriptor
USBPORT_INIT_SETUP_PACKET(setupPacket2,
USB_REQUEST_GET_DESCRIPTOR, // bRequest
BMREQUEST_DEVICE_TO_HOST, // Dir
BMREQUEST_TO_DEVICE, // Recipient
BMREQUEST_STANDARD, // Type
USB_DESCRIPTOR_MAKE_TYPE_AND_INDEX(USB_DEVICE_DESCRIPTOR_TYPE, 0), // wValue
0, // wIndex
sizeof(DeviceHandle->DeviceDescriptor)); // wLength
ntStatus =
USBPORT_SendCommand(DeviceHandle,
FdoDeviceObject,
&setupPacket2,
(PUCHAR) &DeviceHandle->DeviceDescriptor,
sizeof(DeviceHandle->DeviceDescriptor),
&bytesReturned,
NULL);
if (NT_SUCCESS(ntStatus) &&
(bytesReturned != sizeof(USB_DEVICE_DESCRIPTOR)) ||
(DeviceHandle->DeviceDescriptor.bLength < sizeof(USB_DEVICE_DESCRIPTOR)) ||
(DeviceHandle->DeviceDescriptor.bDescriptorType != USB_DEVICE_DESCRIPTOR_TYPE) ||
((DeviceHandle->DeviceDescriptor.bMaxPacketSize0 != 0x08) &&
(DeviceHandle->DeviceDescriptor.bMaxPacketSize0 != 0x10) &&
(DeviceHandle->DeviceDescriptor.bMaxPacketSize0 != 0x20) &&
(DeviceHandle->DeviceDescriptor.bMaxPacketSize0 != 0x40))) {
// print a big debug message
USBPORT_KdPrint((0, "'InitializeDevice failed enumeration\n"));
ntStatus = STATUS_DEVICE_DATA_ERROR;
}
}
if (NT_SUCCESS(ntStatus)) {
if (DeviceHandle->DeviceSpeed == HighSpeed &&
DeviceHandle->DeviceDescriptor.bDeviceClass ==
USB_DEVICE_CLASS_HUB) {
// note that this is a hs hub, these require special
// handling because of the TTs
SET_FLAG(DeviceHandle->DeviceFlags, USBPORT_DEVICEFLAG_HSHUB);
}
UNLOCK_DEVICE(DeviceHandle, FdoDeviceObject);
} else {
//
// something went wrong, if we assigned any resources to
// the default pipe then we free them before we get out.
//
// we need to signal to the parent hub that this
// port is to be be disabled we will do this by
// returning an error.
// if we got here then we know the default
// endpoint is open
DEBUG_BREAK();
USBPORT_ClosePipe(DeviceHandle,
FdoDeviceObject,
defaultPipe);
if (DeviceHandle->DeviceAddress != USB_DEFAULT_DEVICE_ADDRESS) {
USBPORT_FreeUsbAddress(FdoDeviceObject, DeviceHandle->DeviceAddress);
}
UNLOCK_DEVICE(DeviceHandle, FdoDeviceObject);
// this device handle is no longer valid
USBPORT_RemoveDeviceHandle(FdoDeviceObject, DeviceHandle);
FREE_POOL(FdoDeviceObject, DeviceHandle);
}
LOGENTRY(NULL,
FdoDeviceObject, LOG_MISC, 'iniD', DeviceHandle, 0, ntStatus);
CATC_TRAP_ERROR(FdoDeviceObject, ntStatus);
USBPORT_ENUMLOG(FdoDeviceObject, 'idev', ntStatus, 0);
return ntStatus;
}
NTSTATUS
USBPORT_GetUsbDescriptor(
PUSBD_DEVICE_HANDLE DeviceHandle,
PDEVICE_OBJECT FdoDeviceObject,
UCHAR DescriptorType,
PUCHAR DescriptorBuffer,
PULONG DescriptorBufferLength
)
/*++
Routine Description:
Arguments:
DeviceHandle - ptr to device data structure created by class driver
from a call to USBPORT_CreateDevice.
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus;
PUSBD_PIPE_HANDLE_I defaultPipe;
PDEVICE_EXTENSION devExt;
USB_DEFAULT_PIPE_SETUP_PACKET setupPacket;
USBPORT_INIT_SETUP_PACKET(setupPacket,
USB_REQUEST_GET_DESCRIPTOR, // bRequest
BMREQUEST_DEVICE_TO_HOST, // Dir
BMREQUEST_TO_DEVICE, // Recipient
BMREQUEST_STANDARD, // Type
USB_DESCRIPTOR_MAKE_TYPE_AND_INDEX(DescriptorType, 0), // wValue
0, // wIndex
*DescriptorBufferLength); // wLength
ntStatus =
USBPORT_SendCommand(DeviceHandle,
FdoDeviceObject,
&setupPacket,
DescriptorBuffer,
*DescriptorBufferLength,
DescriptorBufferLength,
NULL);
return ntStatus;
}
BOOLEAN
USBPORT_DeviceHasQueuedTransfers(
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_DEVICE_HANDLE DeviceHandle
)
/*++
Routine Description:
Returns TRUE device has queued transfers
Arguments:
Return Value:
True if device has transfers queued transfers
--*/
{
PDEVICE_EXTENSION devExt;
BOOLEAN hasTransfers = FALSE;
PLIST_ENTRY listEntry;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
ASSERT_DEVICE_HANDLE(DeviceHandle);
listEntry = &DeviceHandle->PipeHandleList;
if (!IsListEmpty(listEntry)) {
listEntry = DeviceHandle->PipeHandleList.Flink;
}
while (listEntry != &DeviceHandle->PipeHandleList) {
PUSBD_PIPE_HANDLE_I nextHandle;
nextHandle = (PUSBD_PIPE_HANDLE_I) CONTAINING_RECORD(
listEntry,
struct _USBD_PIPE_HANDLE_I,
ListEntry);
ASSERT_PIPE_HANDLE(nextHandle);
listEntry = nextHandle->ListEntry.Flink;
if (!TEST_FLAG(nextHandle->PipeStateFlags, USBPORT_PIPE_ZERO_BW) &&
USBPORT_EndpointHasQueuedTransfers(FdoDeviceObject,
nextHandle->Endpoint)) {
hasTransfers = TRUE;
break;
}
}
return hasTransfers;
}
VOID
USBPORT_AbortAllTransfers(
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_DEVICE_HANDLE DeviceHandle
)
/*++
Routine Description:
abort all pending transfers associated with a device handle.
This function is synchronous -- it is called after the device
handle is removed from our tables so no new transfers can be
posted.
The idea here is to complete any transfers that may still be
pending when the device is removed in case the client driver
neglected to.
On entry to this function the device is locked.
Arguments:
DeviceHandle - ptr to device data structure created by class driver
in USBPORT_CreateDevice.
FdoDeviceObject - USBPORT device object for the USB bus this device is on.
Return Value:
NT status code.
--*/
{
PLIST_ENTRY listEntry;
ASSERT_DEVICE_HANDLE(DeviceHandle);
listEntry = &DeviceHandle->PipeHandleList;
if (!IsListEmpty(listEntry)) {
listEntry = DeviceHandle->PipeHandleList.Flink;
}
while (listEntry != &DeviceHandle->PipeHandleList) {
PUSBD_PIPE_HANDLE_I nextHandle;
nextHandle = (PUSBD_PIPE_HANDLE_I) CONTAINING_RECORD(
listEntry,
struct _USBD_PIPE_HANDLE_I,
ListEntry);
ASSERT_PIPE_HANDLE(nextHandle);
listEntry = nextHandle->ListEntry.Flink;
if (!TEST_FLAG(nextHandle->PipeStateFlags, USBPORT_PIPE_ZERO_BW)) {
SET_FLAG(nextHandle->Endpoint->Flags, EPFLAG_DEVICE_GONE);
USBPORT_AbortEndpoint(FdoDeviceObject,
nextHandle->Endpoint,
NULL);
USBPORT_FlushMapTransferList(FdoDeviceObject);
}
}
// This gaurantees that no transfers are in our lists or
// in the miniport when we remove the device.
// NOTE: If a driver passed a remove with transfers still pending
// we still may crash but this should happen in the offending
// driver.
// NOTE 2: The whistler hub driver will remove the device early
// (on connect change) so this code will be hit legit-ly in
// this case.
// now wait for queues to empty
while (USBPORT_DeviceHasQueuedTransfers(FdoDeviceObject, DeviceHandle)) {
// wait, then check again
USBPORT_Wait(FdoDeviceObject, 100);
}
}
NTSTATUS
USBPORT_CloneDevice(
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_DEVICE_HANDLE OldDeviceHandle,
PUSBD_DEVICE_HANDLE NewDeviceHandle
)
/*++
Routine Description:
Service exported for use by the hub driver
Arguments:
NewDeviceHandle - ptr to device data structure created by class driver
in USBPORT_CreateDevice.
OldDeviceHandle - ptr to device data structure created by class driver
in USBPORT_CreateDevice.
FdoDeviceObject - USBPORT device object for the USB bus this device is on.
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus;
PDEVICE_EXTENSION devExt;
USBD_STATUS usbdStatus;
USB_DEFAULT_PIPE_SETUP_PACKET setupPacket;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
// assume success
ntStatus = STATUS_SUCCESS;
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Cln>',
OldDeviceHandle, NewDeviceHandle, 0);
USBPORT_KdPrint((1,"'Cloning Device\n"));
DEBUG_BREAK();
LOCK_DEVICE(NewDeviceHandle, FdoDeviceObject);
// make sure we have two valid device handles
if (!USBPORT_ValidateDeviceHandle(FdoDeviceObject,
OldDeviceHandle,
FALSE)) {
// this is most likely a bug in the hub
// driver
DEBUG_BREAK();
UNLOCK_DEVICE(NewDeviceHandle, FdoDeviceObject);
// chances are that the device handle is bad becuse the
// device is gone.
return STATUS_DEVICE_NOT_CONNECTED;
}
if (!USBPORT_ValidateDeviceHandle(FdoDeviceObject,
NewDeviceHandle,
FALSE)) {
// this is most likely a bug in the hub
// driver
DEBUG_BREAK();
UNLOCK_DEVICE(NewDeviceHandle, FdoDeviceObject);
// chances are that the device handle is bad becuse the
// device is gone.
return STATUS_DEVICE_NOT_CONNECTED;
}
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Cln+',
OldDeviceHandle, NewDeviceHandle, 0);
// There are two cases where this API is called:
// case 1 - the device driver has requested a reset of the device.
// In this event the device has returned to the unconfigured state
// and has been re-addressed with the 'NewDeviceHandle'
//
// case 2 - the controller has been shut off -- thanks to power
// management. In this case the device is also in the unconfigured
// state and associated with the 'NewDeviceHandle' device handle
// make sure the 'new device' is unconfigured
USBPORT_ASSERT(NewDeviceHandle->ConfigurationHandle == NULL);
#ifdef XPSE
// before performing the clone operation remove the device handle
// and wait for any pending URBs to drain
USBPORT_RemoveDeviceHandle(FdoDeviceObject,
OldDeviceHandle);
USBPORT_AbortAllTransfers(FdoDeviceObject,
OldDeviceHandle);
// wait for any refs from non-transfer URBs to drain
while (InterlockedDecrement(&OldDeviceHandle->PendingUrbs) >= 0) {
LOGENTRY(NULL,
FdoDeviceObject, LOG_PNP, 'dPR2', OldDeviceHandle, 0,
OldDeviceHandle->PendingUrbs);
InterlockedIncrement(&OldDeviceHandle->PendingUrbs);
USBPORT_Wait(FdoDeviceObject, 100);
}
#endif
// make sure we are dealing with the same device
if (RtlCompareMemory(&NewDeviceHandle->DeviceDescriptor,
&OldDeviceHandle->DeviceDescriptor,
sizeof(OldDeviceHandle->DeviceDescriptor)) !=
sizeof(OldDeviceHandle->DeviceDescriptor)) {
ntStatus = STATUS_UNSUCCESSFUL;
goto USBPORT_CloneDevice_FreeOldDevice;
}
// clone the config
NewDeviceHandle->ConfigurationHandle =
OldDeviceHandle->ConfigurationHandle;
if (OldDeviceHandle->ConfigurationHandle != NULL) {
// set the device to the previous configuration,
// Send the 'set configuration' command.
USBPORT_INIT_SETUP_PACKET(setupPacket,
USB_REQUEST_SET_CONFIGURATION, // bRequest
BMREQUEST_HOST_TO_DEVICE, // Dir
BMREQUEST_TO_DEVICE, // Recipient
BMREQUEST_STANDARD, // Type
NewDeviceHandle->ConfigurationHandle->\
ConfigurationDescriptor->bConfigurationValue, // wValue
0, // wIndex
0); // wLength
USBPORT_SendCommand(NewDeviceHandle,
FdoDeviceObject,
&setupPacket,
NULL,
0,
NULL,
&usbdStatus);
USBPORT_KdPrint((2,"' SendCommand, SetConfiguration returned 0x%x\n", usbdStatus));
if (USBD_ERROR(usbdStatus)) {
USBPORT_KdPrint((1, "failed to 'set' the configuration on a clone\n"));
//
// the set_config failed, this can happen if the device has been
// removed or if the device has lost its brains.
// We continue with the cloning process for the endpoints so they
// will be properly freed when the 'new' device handle is
// eventually removed.
//
ntStatus = SET_USBD_ERROR(NULL, usbdStatus);
}
}
// clone any alternate interface settings, since we restore the pipes to
// the state at the time of hibernate they may be associated with
// particular alternate interfaces
// walk the interface chain
if (OldDeviceHandle->ConfigurationHandle != NULL &&
NT_SUCCESS(ntStatus)) {
PUSBD_CONFIG_HANDLE cfgHandle;
PLIST_ENTRY listEntry;
PUSBD_INTERFACE_HANDLE_I iHandle;
cfgHandle = NewDeviceHandle->ConfigurationHandle;
GET_HEAD_LIST(cfgHandle->InterfaceHandleList, listEntry);
while (listEntry &&
listEntry != &cfgHandle->InterfaceHandleList) {
// extract the handle from this entry
iHandle = (PUSBD_INTERFACE_HANDLE_I) CONTAINING_RECORD(
listEntry,
struct _USBD_INTERFACE_HANDLE_I,
InterfaceLink);
ASSERT_INTERFACE(iHandle);
// see if we currently have an alt setting selected
if (iHandle->HasAlternateSettings) {
NTSTATUS status;
//
// If we have alternate settings we need
// to send the set interface command.
//
USBPORT_INIT_SETUP_PACKET(setupPacket,
USB_REQUEST_SET_INTERFACE, // bRequest
BMREQUEST_HOST_TO_DEVICE, // Dir
BMREQUEST_TO_INTERFACE, // Recipient
BMREQUEST_STANDARD, // Type
iHandle->InterfaceDescriptor.bAlternateSetting, // wValue
iHandle->InterfaceDescriptor.bInterfaceNumber, // wIndex
0); // wLength
status = USBPORT_SendCommand(NewDeviceHandle,
FdoDeviceObject,
&setupPacket,
NULL,
0,
NULL,
&usbdStatus);
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'sIF2',
0,
iHandle->InterfaceDescriptor.bAlternateSetting,
iHandle->InterfaceDescriptor.bInterfaceNumber);
}
listEntry = iHandle->InterfaceLink.Flink;
}
}
// clone the TT and TT related data
if (TEST_FLAG(NewDeviceHandle->DeviceFlags, USBPORT_DEVICEFLAG_HSHUB)) {
// remove the TT entries from the old handle and add them
// to the new handle
while (!IsListEmpty(&OldDeviceHandle->TtList)) {
PTRANSACTION_TRANSLATOR tt;
PLIST_ENTRY listEntry;
listEntry = RemoveTailList(&OldDeviceHandle->TtList);
USBPORT_ASSERT(listEntry != NULL);
tt = (PTRANSACTION_TRANSLATOR) CONTAINING_RECORD(
listEntry,
struct _TRANSACTION_TRANSLATOR,
TtLink);
ASSERT_TT(tt);
tt->DeviceAddress = NewDeviceHandle->DeviceAddress;
InsertHeadList(&NewDeviceHandle->TtList, &tt->TtLink);
}
NewDeviceHandle->TtCount = OldDeviceHandle->TtCount;
}
// copy the pipe handle list, for each pipe we will need to re-open
// the endpoint or re-init the endpoint.
//
// if the device did not loose its brains then all we need to do
// is update the host controllers idea of what the endpoint address is.
// this has the added advantage of allowing a reset even when transfers
// are queued to the HW although we don't allow that.
while (!IsListEmpty(&OldDeviceHandle->PipeHandleList)) {
PHCD_ENDPOINT endpoint;
PLIST_ENTRY listEntry = OldDeviceHandle->PipeHandleList.Flink;
PUSBD_PIPE_HANDLE_I pipeHandle;
PTRANSACTION_TRANSLATOR transactionTranslator = NULL;
// see if we are dealing with a TT
if (NewDeviceHandle->Tt != NULL) {
transactionTranslator = NewDeviceHandle->Tt;
ASSERT_TT(transactionTranslator);
}
pipeHandle = (PUSBD_PIPE_HANDLE_I) CONTAINING_RECORD(
listEntry,
struct _USBD_PIPE_HANDLE_I,
ListEntry);
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'CLNE', pipeHandle, 0, 0);
ASSERT_PIPE_HANDLE(pipeHandle);
USBPORT_RemovePipeHandle(OldDeviceHandle,
pipeHandle);
// we need to special case the default pipe because it
// is embedded in the DeviceHandle.
//
// Since NewDeviceHandle is a newly created device
// the endpoint associated with it is valid, so is
// the one for the 'OldDeviceHandle'
if (pipeHandle != &OldDeviceHandle->DefaultPipe) {
USB_MINIPORT_STATUS mpStatus;
USBPORT_AddPipeHandle(NewDeviceHandle, pipeHandle);
// skip re-init for sero bw endpoints becuase we have
// no endpoint structure -- these are ghost endpoints
if (!TEST_FLAG(pipeHandle->PipeStateFlags, USBPORT_PIPE_ZERO_BW)) {
endpoint = pipeHandle->Endpoint;
ASSERT_ENDPOINT(endpoint);
endpoint->DeviceHandle = NewDeviceHandle;
endpoint->Parameters.DeviceAddress =
NewDeviceHandle->DeviceAddress;
if (TEST_FLAG(endpoint->Flags, EPFLAG_NUKED)) {
// re-open
ENDPOINT_REQUIREMENTS requirements;
if (transactionTranslator != NULL) {
endpoint->Parameters.TtDeviceAddress =
transactionTranslator->DeviceAddress;
}
// call open request to minport, all the endpoint
// structures are still valid we just need to re-add
// it to the schedule.
RtlZeroMemory(&endpoint->MiniportEndpointData[0],
REGISTRATION_PACKET(devExt).EndpointDataSize);
RtlZeroMemory(endpoint->Parameters.CommonBufferVa,
endpoint->Parameters.CommonBufferBytes);
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'clRO', pipeHandle,
endpoint, 0);
// query requirements (although they should not change)
// just in case the miniport does some initialization here
MP_QueryEndpointRequirements(devExt,
endpoint, &requirements);
MP_OpenEndpoint(devExt, endpoint, mpStatus);
// in this UNIQUE situation this API is not allowed
// (and should not) fail
USBPORT_ASSERT(mpStatus == USBMP_STATUS_SUCCESS);
CLEAR_FLAG(endpoint->Flags, EPFLAG_NUKED);
// gone flag is set when we abort transfers
CLEAR_FLAG(endpoint->Flags, EPFLAG_DEVICE_GONE);
// we need to sync the endpoint state with
// the miniport, when first opened the miniport
// puts the endpoint in status HALT.
ACQUIRE_ENDPOINT_LOCK(endpoint, FdoDeviceObject, 'LeK0');
// initialize endpoint state machine
//if (endpoint->CurrentStatus == ENDPOINT_STATUS_RUN) {
// MP_SetEndpointStatus(devExt, endpoint, ENDPOINT_STATUS_RUN);
//}
if (endpoint->CurrentState == ENDPOINT_ACTIVE) {
MP_SetEndpointState(devExt, endpoint, ENDPOINT_ACTIVE);
}
RELEASE_ENDPOINT_LOCK(endpoint, FdoDeviceObject, 'UeK0');
} else {
// if this device has an associated TT then
// we will need to do a little more here
if (transactionTranslator != NULL) {
endpoint->Parameters.TtDeviceAddress =
transactionTranslator->DeviceAddress;
}
// this endpoint is already in the schedule,
// poke-it with the new address.
MP_PokeEndpoint(devExt, endpoint, mpStatus);
// in this UNIQUE situation this API is not allowed
// (and should not) fail
USBPORT_ASSERT(mpStatus == USBMP_STATUS_SUCCESS);
// The endpoint on the device should have had its data
// toggle reset back to Data0 so reset the data toggle
// on the host endpoint to match.
//
MP_SetEndpointDataToggle(devExt, endpoint, 0);
// clear halt status
MP_SetEndpointStatus(devExt, endpoint, ENDPOINT_STATUS_RUN);
}
}
}
}
// the pipes and config have been cloned, the final step is to free the
// 'OldDeviceData' ie the old handle.
// put the old 'default' pipe back on the list before
// we close it
USBPORT_AddPipeHandle(OldDeviceHandle,
&OldDeviceHandle->DefaultPipe);
USBPORT_CloneDevice_FreeOldDevice:
#ifndef XPSE
USBPORT_RemoveDeviceHandle(FdoDeviceObject,
OldDeviceHandle);
USBPORT_AbortAllTransfers(FdoDeviceObject,
OldDeviceHandle);
#endif
// we should aways have a default pipe, this will free
// the endpoint
USBPORT_ClosePipe(OldDeviceHandle,
FdoDeviceObject,
&OldDeviceHandle->DefaultPipe);
if (OldDeviceHandle->DeviceAddress != USB_DEFAULT_DEVICE_ADDRESS) {
USBPORT_FreeUsbAddress(FdoDeviceObject, OldDeviceHandle->DeviceAddress);
}
UNLOCK_DEVICE(NewDeviceHandle, FdoDeviceObject);
FREE_POOL(FdoDeviceObject, OldDeviceHandle);
return ntStatus;
}
PTRANSACTION_TRANSLATOR
USBPORT_GetTt(
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_DEVICE_HANDLE HubDeviceHandle,
PUSHORT PortNumber
)
/*++
Routine Description:
Walk upstream until we find the first high speed device
Arguments:
Return Value:
ttDeviceAddress
--*/
{
PDEVICE_EXTENSION devExt;
PTRANSACTION_TRANSLATOR tt = NULL;
PLIST_ENTRY listEntry;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
do {
if (HubDeviceHandle->DeviceSpeed == UsbHighSpeed) {
if (HubDeviceHandle->TtCount > 1) {
GET_HEAD_LIST(HubDeviceHandle->TtList, listEntry);
while (listEntry != NULL &&
listEntry != &HubDeviceHandle->TtList) {
tt = (PTRANSACTION_TRANSLATOR) CONTAINING_RECORD(
listEntry,
struct _TRANSACTION_TRANSLATOR,
TtLink);
ASSERT_TT(tt);
if (tt->Port == *PortNumber) {
break;
}
listEntry = tt->TtLink.Flink;
tt = NULL;
}
} else {
// single TT, use the one tt structure regardless of port
GET_HEAD_LIST(HubDeviceHandle->TtList, listEntry);
tt = (PTRANSACTION_TRANSLATOR) CONTAINING_RECORD(
listEntry,
struct _TRANSACTION_TRANSLATOR,
TtLink);
ASSERT_TT(tt);
}
// we should have selected a tt
USBPORT_ASSERT(tt != NULL);
break;
} else {
*PortNumber = HubDeviceHandle->TtPortNumber;
}
HubDeviceHandle = HubDeviceHandle->HubDeviceHandle;
ASSERT_DEVICE_HANDLE(HubDeviceHandle);
} while (HubDeviceHandle != NULL);
USBPORT_KdPrint((1, "TtPortNumber %d\n",
*PortNumber));
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'gTTa', HubDeviceHandle,
*PortNumber, tt);
return tt;
}
NTSTATUS
USBPORT_InitializeTT(
PDEVICE_OBJECT FdoDeviceObject,
PUSBD_DEVICE_HANDLE HubDeviceHandle,
USHORT Port
)
/*++
Routine Description:
Initialze the TT table used to track this hub
Arguments:
Return Value:
nt status code
--*/
{
PDEVICE_EXTENSION devExt;
PTRANSACTION_TRANSLATOR transactionTranslator;
USHORT siz;
extern ULONG USB2LIB_TtContextSize;
NTSTATUS ntStatus;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
USBPORT_ASSERT(USBPORT_IS_USB20(devExt));
siz = sizeof(TRANSACTION_TRANSLATOR) +
USB2LIB_TtContextSize;
ALLOC_POOL_Z(transactionTranslator, NonPagedPool, siz);
if (transactionTranslator != NULL) {
ULONG i;
ULONG bandwidth;
transactionTranslator->Sig = SIG_TT;
transactionTranslator->DeviceAddress =
HubDeviceHandle->DeviceAddress;
transactionTranslator->Port = Port;
transactionTranslator->PdoDeviceObject =
devExt->Fdo.RootHubPdo;
// each translator is a virtual 1.1 bus
transactionTranslator->TotalBusBandwidth =
USB_11_BUS_BANDWIDTH;
InitializeListHead(&transactionTranslator->EndpointList);
for (i=0; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i++) {
transactionTranslator->BandwidthTable[i] =
transactionTranslator->TotalBusBandwidth -
transactionTranslator->TotalBusBandwidth/10;
}
// reserve the basic 10% from the parent bus
USBPORT_UpdateAllocatedBwTt(transactionTranslator);
// alloc new
bandwidth = transactionTranslator->MaxAllocedBw;
for (i=0; i<USBPORT_MAX_INTEP_POLLING_INTERVAL; i++) {
devExt->Fdo.BandwidthTable[i] -= bandwidth;
}
USB2LIB_InitTt(devExt->Fdo.Usb2LibHcContext,
&transactionTranslator->Usb2LibTtContext);
InsertTailList(&HubDeviceHandle->TtList,
&transactionTranslator->TtLink);
ntStatus = STATUS_SUCCESS;
} else {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
return ntStatus;
}