windows-nt/Source/XPSP1/NT/drivers/wdm/usb/hcd/uhcd/urb.c
2020-09-26 16:20:57 +08:00

2478 lines
69 KiB
C

/*++
Copyright (c) 1995,1996 Microsoft Corporation
:ts=4
Module Name:
urb.c
Abstract:
The module manages transactions on the USB.
Environment:
kernel mode only
Notes:
Revision History:
11-01-95 : created
04-28-96 : linked urb support & cancel support
--*/
#include "wdm.h"
#include "stdarg.h"
#include "stdio.h"
#include "usbdi.h"
#include "hcdi.h"
#include "uhcd.h"
#define COMPUTE_HCD_EXTENSION_SIZE(urb) (sizeof(HCD_EXTENSION) + \
(urb->HcdUrbCommonTransfer.TransferBufferLength / PAGE_SIZE + 1) \
* sizeof(UHCD_LOGICAL_ADDRESS))
#define UHCD_IS_TRANSFER(urb) (((urb)->UrbHeader.Function == URB_FUNCTION_CONTROL_TRANSFER || \
(urb)->UrbHeader.Function == URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER ||\
(urb)->UrbHeader.Function == URB_FUNCTION_ISOCH_TRANSFER) \
? TRUE : FALSE)
// calc the maxrequests based on endpoint type nad
// flag options
UCHAR
MAX_REQUESTS(
IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor,
IN ULONG EpFlags
)
{
if (EpFlags & EPFLAG_DBL_BUFFER) {
return 1;
}
switch (USB_ENDPOINT_TYPE_MASK &
EndpointDescriptor->bmAttributes) {
case USB_ENDPOINT_TYPE_BULK:
return 2;
//return 1;
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
return 3;
default:
return 1;
}
}
#if 0
ULONG
UHCD_CountPageCrossings(
IN ULONG MaxRequests,
IN ULONG MaxTransferSize
)
/*++
Routine Description:
Completes an I/O Request
Arguments:
Return Value:
maximum number of possible page crossings
--*/
{
ULONG pageCrossings;
pageCrossings = (MaxTransferSize + PAGE_SIZE) / PAGE_SIZE;
if (MaxRequests>1) {
pageCrossings *= 2;
}
// now allocate space for packet buffers
UHCD_KdPrint((2, "'UHCD_CountPageCrossings, max transfer size 0x%x -- page crossings = 0x%x\n",
MaxTransferSize, pageCrossings));
UHCD_ASSERT(pageCrossings > 0);
return pageCrossings;
}
#endif
VOID
UHCD_CompleteIrp(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN NTSTATUS ntStatus,
IN ULONG Information,
IN PHCD_URB Urb
)
/*++
Routine Description:
Completes an I/O Request
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet to complete
ntStatus - status code to set int the IRP when completed
Information -
Urb - root transfer urb if this Irp is associated with
a transfer.
Return Value:
--*/
{
KIRQL irql;
UCHAR flags = 0;
STARTPROC("cIrp");
UHCD_KdPrint((2, "'UHCD_CompleteIrp status = %x\n", ntStatus));
LOGENTRY(LOG_MISC, 'ICan', Urb, 0, Irp);
if (Urb) {
//
// if we have any working space free it now
//
if (UHCD_IS_TRANSFER(Urb) && HCD_AREA(Urb).HcdExtension) {
PHCD_EXTENSION urbWork;
urbWork = HCD_AREA(Urb).HcdExtension;
// remember the flags
flags = urbWork->Flags;
RETHEAP(HCD_AREA(Urb).HcdExtension);
HCD_AREA(Urb).HcdExtension = NULL;
}
//
// Decrement the urb counter that we keep in our stack location for
// the irp, when it goes to zero -- complete it
//
//
// one Urb completed
//
DECREMENT_PENDING_URB_COUNT(Irp);
if (PENDING_URB_COUNT(Irp)) {
//
// stall completion until all Urbs are done.
//
TEST_TRAP();
return;
} else {
IoAcquireCancelSpinLock(&irql);
if (Irp->Cancel) {
LOGENTRY(LOG_MISC, 'irpX', flags, 0, Irp);
// note that the cancel routine will only mess
// with the urbs on the active list -- any active
// urbs should have been removed from the active
// list before calling this routine
//
// Irp associated with this transfer has
// been canceled.
//
// The cancel routine will complete the Irp
// unless there are active transfers.
//
if (flags & UHCD_TRANSFER_ACTIVE) {
IoSetCancelRoutine(Irp, NULL);
IoReleaseCancelSpinLock(irql);
//
// if the io has already started the we must
// complete the Irp with STATUS_CANCELLED here.
//
ntStatus = STATUS_CANCELLED;
Information = 0;
goto UHCD_CompleteIrp_CompleteRequest;
}
IoReleaseCancelSpinLock(irql);
return;
} else {
//
// Irp is no longer cancelable
//
LOGENTRY(LOG_MISC, 'NCan', flags, 0, Irp);
IoSetCancelRoutine(Irp, NULL);
IoReleaseCancelSpinLock(irql);
//
// Pending bit should be cleared
//
UHCD_ASSERT(!USBD_PENDING(Urb->HcdUrbCommonTransfer.Status));
}
}
}
UHCD_CompleteIrp_CompleteRequest:
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = Information;
LOGENTRY(LOG_MISC, 'irpC', Irp, DeviceObject, Urb);
USBD_CompleteRequest(Irp,
IO_NO_INCREMENT);
ENDPROC("cIrp");
}
NTSTATUS
UHCD_URB_Dispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Process URBs from the dispatch routine.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Return Value:
--*/
{
PHCD_URB urb;
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension;
PUHCD_ENDPOINT endpoint = NULL;
ULONG siz, cnt;
ULONG numTDs;
UHCD_KdPrint((2, "'enter UHCD_URB_Dispatch \n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
urb = (PHCD_URB) URB_FROM_IRP(Irp);
if (deviceExtension->CurrentDevicePowerState != PowerDeviceD0 ||
deviceExtension->HcFlags & HCFLAG_HCD_SHUTDOWN) {
//
// someone is submitting requests while the
// HC is suspended or OFF, we will just fail them
//
UHCD_KdPrint
((0, "'Warning: UHCD got a request while not in D0 in shutdown\n"));
ntStatus = STATUS_DEVICE_NOT_READY;
URB_HEADER(urb).Status = USBD_STATUS_REQUEST_FAILED;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
return ntStatus;
}
switch(URB_HEADER(urb).Function) {
//
// These commands get queued to the startio routine
// for processing.
//
case URB_FUNCTION_HCD_OPEN_ENDPOINT:
#ifdef ROOT_HUB
if (urb->HcdUrbOpenEndpoint.DeviceAddress ==
deviceExtension->RootHubDeviceAddress) {
// This routine will either complete
// the irp or mark it pending.
ntStatus = UHCD_RootHub_OpenEndpoint(DeviceObject,
Irp,
urb);
break;
}
#endif
//
// Grow the common buffer pool based on how much
// memory we'll need for transfers
//
// Well need enough for one set of TDs plus
// a common buffer for each possible page crossing
// within a trasnsfer.
// first allocate space for the TDs
// for now use a global value
//
// allocate the endpoint structures here while we are
// at passive level
//
endpoint = (PUHCD_ENDPOINT) GETHEAP(NonPagedPool,
sizeof(UHCD_ENDPOINT));
if (endpoint) {
// start endpoint initialization
RtlZeroMemory(endpoint, sizeof(*endpoint));
endpoint->Sig = SIG_EP;
// check for bulk and iso special options
// we only support double buffering for bulk-IN
// we only support fast iso for iso-OUT
if (urb->HcdUrbOpenEndpoint.HcdEndpointFlags &
USBD_EP_FLAG_DOUBLE_BUFFER) {
if (USB_ENDPOINT_DIRECTION_IN(
urb->HcdUrbOpenEndpoint.EndpointDescriptor->bEndpointAddress)
&&
(USB_ENDPOINT_TYPE_MASK &
urb->HcdUrbOpenEndpoint.EndpointDescriptor->bmAttributes)
==
USB_ENDPOINT_TYPE_BULK) {
SET_EPFLAG(endpoint, EPFLAG_DBL_BUFFER);
} else { // bugbug error here?
UHCD_KdPrint((1, "'WARNING: Cannot double buffer this endpoint\n"));
}
}
#ifdef FAST_ISO
if (urb->HcdUrbOpenEndpoint.HcdEndpointFlags &
USBD_EP_FLAG_FAST_ISO) {
if (USB_ENDPOINT_DIRECTION_OUT(
urb->HcdUrbOpenEndpoint.EndpointDescriptor->bEndpointAddress)
&&
(USB_ENDPOINT_TYPE_MASK &
urb->HcdUrbOpenEndpoint.EndpointDescriptor->bmAttributes)
==
USB_ENDPOINT_TYPE_ISOCHRONOUS) {
SET_EPFLAG(endpoint, EPFLAG_FAST_ISO);
} else { // bugbug error here?
UHCD_KdPrint((1, "'WARNING: Cannot fast-iso this endpoint\n"));
}
}
#endif
urb->HcdUrbOpenEndpoint.HcdEndpoint = endpoint;
urb->HcdUrbOpenEndpoint.ScheduleOffset = 0;
numTDs =
endpoint->TDCount = UHCD_GetNumTDsPerEndoint((UCHAR) (USB_ENDPOINT_TYPE_MASK &
urb->HcdUrbOpenEndpoint.EndpointDescriptor->bmAttributes));
UHCD_ASSERT(TD_LIST_SIZE(numTDs) <= UHCD_LARGE_COMMON_BUFFER_SIZE);
if (endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) {
// do the double buffer init
ntStatus = UHCD_InitializeNoDMAEndpoint(DeviceObject,
endpoint);
}
if (NT_SUCCESS(ntStatus)) {
endpoint->MaxRequests =
MAX_REQUESTS(urb->HcdUrbOpenEndpoint.EndpointDescriptor,
endpoint->EndpointFlags);
UHCD_KdPrint((2, "'MaxRequests = %d\n", endpoint->MaxRequests));
// init transfer sequence numbers
endpoint->NextXferId = 0;
endpoint->CurrentXferId = 0;
if (endpoint->EndpointFlags & EPFLAG_FAST_ISO) {
// do the double buffer init
UHCD_KdPrint((1, "'Using Fast ISO for Endpoint\n"));
ntStatus = UHCD_FinishInitializeEndpoint(DeviceObject,
endpoint,
urb->HcdUrbOpenEndpoint.EndpointDescriptor,
urb);
if (NT_SUCCESS(ntStatus)) {
ntStatus = UHCD_InitializeFastIsoEndpoint(DeviceObject,
endpoint);
}
} else {
for (cnt=0; cnt< endpoint->MaxRequests; cnt++) {
if ((UHCD_AllocateHardwareDescriptors(DeviceObject,
&endpoint->HardwareDescriptorList[cnt],
endpoint->TDCount))) {
PUCHAR descriptors;
ULONG i;
descriptors = endpoint->HardwareDescriptorList[cnt]->MemoryDescriptor->VirtualAddress;
// Initialize the queue head for this endpoint
// use the TD on the first list
if (cnt == 0) {
endpoint->QueueHead = (PHW_QUEUE_HEAD) descriptors;
// save our endpoint in the QueueHead
endpoint->QueueHead->Endpoint = endpoint;
}
//
// the TDs we'll need to service this endpoint
//
endpoint->SlotTDList[cnt] = (PUHCD_TD_LIST) (descriptors + sizeof(HW_QUEUE_HEAD));
// BUGBUG possibly move this to allocate TD code
for (i=0; i<= endpoint->TDCount; i++) {
// one time init stuff
// for isoch TDs.
endpoint->SlotTDList[cnt]->TDs[i].Frame = 0;
endpoint->SlotTDList[cnt]->TDs[i].Urb = NULL;
}
} else {
RETHEAP(endpoint);
endpoint = NULL;
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
} // for
// point to first list
if (endpoint) {
endpoint->TDList = endpoint->SlotTDList[0];
}
}
}
} else {
// failed to alloc endpoint structure
UHCD_KdTrap(("UHCD failed to alloc endpoint structure\n"));
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
}
#if DBG
UHCD_KdPrint((2, "'Open Endpoint, max transfer size 0x%x \n",
urb->HcdUrbOpenEndpoint.MaxTransferSize));
// transfers >64k are valid
// if (urb->HcdUrbOpenEndpoint.MaxTransferSize >
// 1024*64) {
// // bigger than 64k, probably a bug.
// UHCD_KdPrint((2, "'Open Endpoint, max transfer size 0x%x \n",
// urb->HcdUrbOpenEndpoint.MaxTransferSize));
// TEST_TRAP();
// }
#endif
if (NT_SUCCESS(ntStatus)) {
//
// take the opportunity to grow our pool
// in necessary
//
// BUGBUG possibly use max_packet size as a hint
UHCD_MoreCommonBuffers(DeviceObject);
URB_HEADER(urb).Status = UHCD_STATUS_PENDING_STARTIO;
ntStatus = STATUS_PENDING;
UHCD_KdPrint((2, "'Queue Irp To StartIo\n"));
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject,
Irp,
0,
UHCD_StartIoCancel);
} else {
URB_HEADER(urb).Status = USBD_STATUS_NO_MEMORY;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
}
break;
case URB_FUNCTION_CONTROL_TRANSFER:
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
// this will activate the rolover interrupts
deviceExtension->XferIdleTime = 0;
case URB_FUNCTION_HCD_CLOSE_ENDPOINT:
if (URB_HEADER(urb).Function == URB_FUNCTION_HCD_CLOSE_ENDPOINT) {
endpoint = urb->HcdUrbCloseEndpoint.HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
} else {
//#if DBG
// UHCD_KdPrint((2, "'originalTransfer Buffer = 0x%x \n",
// urb->HcdUrbCommonTransfer.TransferBuffer));
// // Trash the TransferBuffer field - we only use MDLs
// urb->HcdUrbCommonTransfer.TransferBuffer = (PVOID)-1;
//#endif
// allocate some working space and attach it to
// this urb
endpoint = HCD_AREA(urb).HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
if (urb->HcdUrbCommonTransfer.TransferBufferLength >
endpoint->MaxTransferSize) {
ntStatus = STATUS_INVALID_PARAMETER;
URB_HEADER(urb).Status = USBD_STATUS_INVALID_PARAMETER;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
break;
}
siz = COMPUTE_HCD_EXTENSION_SIZE(urb);
HCD_AREA(urb).HcdExtension =
GETHEAP(NonPagedPool, siz);
if (HCD_AREA(urb).HcdExtension == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
URB_HEADER(urb).Status = USBD_STATUS_NO_MEMORY;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
break;
}
RtlZeroMemory(HCD_AREA(urb).HcdExtension, siz);
}
#ifdef ROOT_HUB
if (endpoint->EndpointFlags & EPFLAG_ROOT_HUB) {
// These routines will either complete
// the irp or mark it pending.
switch (URB_HEADER(urb).Function) {
case URB_FUNCTION_CONTROL_TRANSFER:
ntStatus = UHCD_RootHub_ControlTransfer(DeviceObject,
Irp,
urb);
// note: URB and IRP may be gone
break;
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
ntStatus = UHCD_RootHub_InterruptTransfer(DeviceObject,
Irp,
urb);
// note: URB and IRP may be gone
break;
case URB_FUNCTION_HCD_CLOSE_ENDPOINT:
ntStatus = UHCD_RootHub_CloseEndpoint(DeviceObject,
Irp,
urb);
break;
default:
//BUGBUG could just complete it with an error
//here
// this is probably a bug in the hub driver
UHCD_KdTrap(("Bogus transfer request to root hub\n"));
}
break;
}
#endif
URB_HEADER(urb).Status = UHCD_STATUS_PENDING_STARTIO;
ntStatus = STATUS_PENDING;
UHCD_KdPrint((2, "'Queue Irp To StartIo\n"));
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject,
Irp,
0,
UHCD_StartIoCancel);
break;
case URB_FUNCTION_ISOCH_TRANSFER:
//
// validate max transfer size
// this will activate the rollover interrupts
UHCD_WakeIdle(DeviceObject);
endpoint = HCD_AREA(urb).HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
// don't ref TDList
endpoint->TDList = (PVOID) -1;
if (urb->HcdUrbCommonTransfer.TransferBufferLength >
endpoint->MaxTransferSize) {
ntStatus = STATUS_INVALID_PARAMETER;
URB_HEADER(urb).Status = USBD_STATUS_INVALID_PARAMETER;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
break;
}
// allocate some working space and attach it to
// this urb
siz = COMPUTE_HCD_EXTENSION_SIZE(urb);
HCD_AREA(urb).HcdExtension =
GETHEAP(NonPagedPool, siz);
if (HCD_AREA(urb).HcdExtension == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
URB_HEADER(urb).Status = USBD_STATUS_NO_MEMORY;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
break;
}
RtlZeroMemory(HCD_AREA(urb).HcdExtension, siz);
if (HCD_AREA(urb).HcdExtension == NULL) {
UHCD_KdBreak((2, "'Unable to allocate working space for isoch transfer\n"));
URB_HEADER(urb).Status = USBD_STATUS_NO_MEMORY;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
break;
}
URB_HEADER(urb).Status = UHCD_STATUS_PENDING_STARTIO;
ntStatus = STATUS_PENDING;
UHCD_KdPrint((2, "'Queue Irp To StartIo\n"));
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoMarkIrpPending(Irp);
#if DBG
{
UHCD_KdPrint((2, "'cf = %x",
UHCD_GetCurrentFrame(DeviceObject)));
}
#endif
if (endpoint->EndpointFlags & EPFLAG_FAST_ISO) {
KIRQL irql;
KeRaiseIrql(DISPATCH_LEVEL, &irql);
UHCD_Transfer_StartIo(DeviceObject, Irp);
KeLowerIrql(irql);
} else {
IoStartPacket(DeviceObject,
Irp,
0,
UHCD_StartIoCancel);
}
break;
case URB_FUNCTION_GET_CURRENT_FRAME_NUMBER:
// this will activate the rollover interrupts
UHCD_WakeIdle(DeviceObject);
urb->UrbGetCurrentFrameNumber.FrameNumber =
UHCD_GetCurrentFrame(DeviceObject);
LOGENTRY(LOG_MISC, 'gcfR',
Irp, urb, urb->UrbGetCurrentFrameNumber.FrameNumber);
URB_HEADER(urb).Status = USBD_STATUS_SUCCESS;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
break;
case URB_FUNCTION_HCD_GET_ENDPOINT_STATE:
case URB_FUNCTION_HCD_SET_ENDPOINT_STATE:
URB_HEADER(urb).Status = UHCD_STATUS_PENDING_STARTIO;
ntStatus = STATUS_PENDING;
UHCD_KdPrint((2, "'Queue Irp To StartIo\n"));
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject,
Irp,
0,
UHCD_StartIoCancel);
break;
case URB_FUNCTION_HCD_ABORT_ENDPOINT:
endpoint = urb->HcdUrbAbortEndpoint.HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
//
// Mark the endpoint so that we abort all the transfers the
// next time we process it.
//
SET_EPFLAG(endpoint,
EPFLAG_ABORT_PENDING_TRANSFERS | EPFLAG_ABORT_ACTIVE_TRANSFERS);
URB_HEADER(urb).Status = UHCD_STATUS_PENDING_STARTIO;
ntStatus = STATUS_PENDING;
UHCD_KdPrint((2, "'Queue Irp To StartIo\n"));
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoMarkIrpPending(Irp);
IoStartPacket(DeviceObject,
Irp,
0,
UHCD_StartIoCancel);
break;
case URB_FUNCTION_SET_FRAME_LENGTH:
{
CHAR sofModify;
//get the current value
sofModify = (CHAR) (READ_PORT_UCHAR(SOF_MODIFY_REG(deviceExtension)));
sofModify += (CHAR) urb->UrbSetFrameLength.FrameLengthDelta;
WRITE_PORT_UCHAR(SOF_MODIFY_REG(deviceExtension), (UCHAR) sofModify);
URB_HEADER(urb).Status = USBD_STATUS_SUCCESS;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
UHCD_KdPrint((2, "'Irp Completed in dispatch\n"));
}
break;
case URB_FUNCTION_GET_FRAME_LENGTH:
{
CHAR sofModify;
//get the current value
sofModify = (CHAR) (READ_PORT_UCHAR(SOF_MODIFY_REG(deviceExtension)));
urb->UrbGetFrameLength.FrameNumber = UHCD_GetCurrentFrame(DeviceObject);
urb->UrbGetFrameLength.FrameLength = UHCD_12MHZ_SOF + sofModify;
URB_HEADER(urb).Status = USBD_STATUS_SUCCESS;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
UHCD_KdPrint((2, "'Irp Completed in dispatch\n"));
}
break;
default:
UHCD_KdPrint((2, "'UHCD_URB_Dispatch -- invalid URB function (%x)\n",
URB_HEADER(urb).Function));
URB_HEADER(urb).Status = USBD_STATUS_INVALID_URB_FUNCTION;
ntStatus = STATUS_INVALID_PARAMETER;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
}
UHCD_KdPrint((2, "'exit UHCD_URB_Dispatch (%x)\n", ntStatus));
return ntStatus;
}
VOID
UHCD_StartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Process URBs from the startIo routine.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Return Value:
--*/
{
PHCD_URB urb;
KIRQL irql;
UHCD_KdPrint((2, "'enter UHCD_StartIo\n"));
//
// see if the Irp was canceled
//
urb = URB_FROM_IRP(Irp);
IoAcquireCancelSpinLock(&irql);
IoSetCancelRoutine(Irp, NULL);
if (Irp->Cancel) {
TEST_TRAP();
Irp->IoStatus.Status = STATUS_CANCELLED;
IoReleaseCancelSpinLock(irql);
IoCompleteRequest(Irp, IO_NO_INCREMENT);
IoStartNextPacket(DeviceObject, FALSE);
} else {
if (UHCD_IS_TRANSFER(urb)) {
IoSetCancelRoutine(Irp, UHCD_TransferCancel);
} else {
IoSetCancelRoutine(Irp, NULL);
}
IoReleaseCancelSpinLock(irql);
switch(URB_HEADER(urb).Function) {
case URB_FUNCTION_HCD_OPEN_ENDPOINT:
UHCD_OpenEndpoint_StartIo(DeviceObject, Irp);
break;
case URB_FUNCTION_HCD_CLOSE_ENDPOINT:
LOGENTRY(LOG_MISC, 'CLEP',
(PUHCD_ENDPOINT)urb->HcdUrbAbortEndpoint.HcdEndpoint, 0,
0);
UHCD_CloseEndpoint_StartIo(DeviceObject, Irp);
break;
case URB_FUNCTION_ISOCH_TRANSFER:
case URB_FUNCTION_CONTROL_TRANSFER:
case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
UHCD_Transfer_StartIo(DeviceObject, Irp);
break;
case URB_FUNCTION_HCD_ABORT_ENDPOINT:
// walk the pending transfer list, since the
// enpoint is halted no new transfers will
// be dequeued by EndpointWorker and the only
// way to clear the HALT is thru
// GetSetEndpointState_StartIo so it is safe to
// mess with the list.
{
PIRP irp;
PHCD_URB urbtmp;
PUHCD_ENDPOINT endpoint;
PLIST_ENTRY listEntry;
KIRQL irql;
endpoint = (PUHCD_ENDPOINT) urb->HcdUrbAbortEndpoint.HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
UHCD_KdPrint((2, "'Aborting endpoint %x flags = %x\n",
endpoint, endpoint->EndpointFlags));
LOGENTRY(LOG_MISC, 'ABRP', endpoint, 0, 0);
LOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'lck4');
while (!IsListEmpty(&endpoint->PendingTransferList)) {
listEntry = RemoveHeadList(&endpoint->PendingTransferList);
urbtmp = (PHCD_URB) CONTAINING_RECORD(listEntry,
struct _URB_HCD_COMMON_TRANSFER,
hca.HcdListEntry);
UNLOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'ulk4');
UHCD_KdPrint((2, "'Aborting urb = %x\n", urbtmp));
URB_HEADER(urbtmp).Status = USBD_STATUS_CANCELED;
//
// complete the request
//
irp = HCD_AREA(urbtmp).HcdIrp;
LOGENTRY(LOG_MISC, 'ABRc', endpoint, irp, 0);
UHCD_CompleteIrp(DeviceObject,
irp,
STATUS_CANCELLED,
0,
urbtmp);
LOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'lck5');
}
UNLOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'ulk5');
// do cleanup for fast iso
if (endpoint->EndpointFlags & EPFLAG_FAST_ISO) {
ULONG f;
for (f=0; f < FRAME_LIST_SIZE; f++) {
UHCD_CleanupFastIsoTD(DeviceObject,
endpoint,
f,
FALSE);
}
}
}
//
// we have already set the abortPendingTransfers and
// abortActiveTransfers flags in the dispatch
// routine. Now we need to request an interrupt
// so that any active transfers will be cleaned
// up.
//
UHCD_RequestInterrupt(DeviceObject, -2);
IoStartNextPacket(DeviceObject, FALSE);
URB_HEADER(urb).Status = USBD_STATUS_SUCCESS;
UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, NULL);
UHCD_KdPrint((2, "'UHCD Abort Endpoint Request Complete\n"));
break;
case URB_FUNCTION_HCD_GET_ENDPOINT_STATE:
case URB_FUNCTION_HCD_SET_ENDPOINT_STATE:
UHCD_GetSetEndpointState_StartIo(DeviceObject, Irp);
break;
default:
UHCD_KdPrint((2, "'UHCD_URB_StartIo -- invalid URB function (%x)\n", URB_HEADER(urb).Function));
URB_HEADER(urb).Status = USBD_STATUS_INVALID_URB_FUNCTION;
UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, NULL);
}
}
UHCD_KdPrint((2, "'exit UHCD_StartIo\n"));
}
VOID
UHCD_OpenEndpoint_StartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Create a UHCD endpoint, this function is called from UHCD_StartIo to
create a new endpoint structure.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Return Value:
--*/
{
PHCD_URB urb;
PDEVICE_EXTENSION deviceExtension;
PUHCD_ENDPOINT endpoint;
PUSB_ENDPOINT_DESCRIPTOR endpointDescriptor;
KIRQL irql;
NTSTATUS ntStatus;
BOOLEAN haveBW;
ULONG offset;
UHCD_KdPrint((2, "'enter UHCD_OpenEndpoint_StartIo\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
//
// extract a pointer to the URB
//
urb = URB_FROM_IRP(Irp);
//
// make sure the length of the urb is what we expect
//
if (urb->HcdUrbOpenEndpoint.Length != sizeof(struct _URB_HCD_OPEN_ENDPOINT)) {
URB_HEADER(urb).Status = USBD_STATUS_INVALID_PARAMETER;
ntStatus = STATUS_INVALID_PARAMETER;
goto UHCD_OpenEndpoint_StartIo_Done;
}
//
// information about the endpoint comes from the USB endpoint
// descriptor passed in the URB.
//
endpointDescriptor = urb->HcdUrbOpenEndpoint.EndpointDescriptor;
URB_HEADER(urb).Status = USBD_STATUS_SUCCESS;
//
// Allocate resources for the endpoint, this includes an endpoint
// handle that will be passed to us in subsequent transfer requests
//
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
endpoint = urb->HcdUrbOpenEndpoint.HcdEndpoint;
UHCD_ASSERT(endpoint != NULL);
ASSERT_ENDPOINT(endpoint);
if (endpoint) {
//
// initialize endpoint structures, state variables, queue head, ...
//
// bugbug -- may not need to call this for fastIiso
ntStatus = UHCD_FinishInitializeEndpoint(DeviceObject,
endpoint,
endpointDescriptor,
urb);
if (!NT_SUCCESS(ntStatus)) {
RETHEAP(endpoint);
goto UHCD_OpenEndpoint_StartIo_Done;
}
UHCD_KdPrint((2, "'Open Endpoint:\n"));
UHCD_KdPrint((2, "'Type = (%d) \n", endpoint->Type));
#if DBG
switch (endpoint->Type) {
case USB_ENDPOINT_TYPE_CONTROL:
UHCD_KdPrint((2, "'-control\n"));
break;
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
UHCD_KdPrint((2, "'-iso\n"));
break;
case USB_ENDPOINT_TYPE_BULK:
UHCD_KdPrint((2, "'-bulk\n"));
break;
case USB_ENDPOINT_TYPE_INTERRUPT:
UHCD_KdPrint((2, "'-interrupt\n"));
}
if (USB_ENDPOINT_DIRECTION_IN(endpointDescriptor->bEndpointAddress)) {
UHCD_KdPrint((2, "'IN\n"));
} else {
UHCD_KdPrint((2, "'OUT\n"));
}
#endif
UHCD_KdPrint((2, "'EP Address = %d\n", endpoint->EndpointAddress));
UHCD_KdPrint((2, "'DEV Address = %d\n", endpoint->DeviceAddress));
UHCD_KdPrint((2, "'MaxPacket = %d\n", endpoint->MaxPacketSize));
UHCD_KdPrint((2, "'Interval: Requested = %d, Selected = %d\n", endpointDescriptor->bInterval,
endpoint->Interval));
//
// Now attempt to allocate the bandwidth we'll need to
// open this endpoint
//
if (endpoint->Type == USB_ENDPOINT_TYPE_INTERRUPT) {
ULONG i;
// check bw available in all locations and
// pick the least loaded frame
offset = 0;
for (i=0; i< endpoint->Interval; i++) {
if (deviceExtension->BwTable[i] > deviceExtension->BwTable[offset]) {
offset = i;
}
}
haveBW = UHCD_AllocateBandwidth(DeviceObject,
endpoint,
offset);
if (!haveBW) {
//
// could not use the least loaded frame, just grab the
// first frame we can fit in.
//
for (offset=0; offset<endpoint->Interval; offset++) {
haveBW = UHCD_AllocateBandwidth(DeviceObject,
endpoint,
offset);
if (haveBW) {
UHCD_KdPrint((2, "'using offset %d\n", offset));
break;
}
}
}
urb->HcdUrbOpenEndpoint.ScheduleOffset = offset;
} else {
offset = 0;
haveBW = UHCD_AllocateBandwidth(DeviceObject,
endpoint,
offset);
}
if (!haveBW) { // no offset
ULONG cnt;
//
// insufficient bandwidth to open this
// endpoint.
//
URB_HEADER(urb).Status = USBD_STATUS_NO_BANDWIDTH;
UHCD_KdPrint((0, "'warning: no bandwidth for endpoint\n"));
for (cnt=0; cnt< endpoint->MaxRequests; cnt++) {
UHCD_FreeHardwareDescriptors(DeviceObject,
endpoint->HardwareDescriptorList[cnt]);
}
RETHEAP(endpoint);
goto UHCD_OpenEndpoint_StartIo_Done;
}
if (endpoint->EndpointFlags & EPFLAG_FAST_ISO) {
goto UHCD_OpenEndpoint_StartIo_Done;
}
//
// put the endpoint in to our list of active endpoints
//
// BUGBUG may want to wait for the first transfer to
// do this.
//
LOCK_ENDPOINT_LIST(deviceExtension, irql);
if (deviceExtension->EndpointListBusy) {
// if the endpoint list is busy we have to put the endpoint
// on a lookaside list so that the ISRDPC can add it later
InsertHeadList(&deviceExtension->EndpointLookAsideList, &endpoint->ListEntry);
} else {
InsertHeadList(&deviceExtension->EndpointList, &endpoint->ListEntry);
}
UNLOCK_ENDPOINT_LIST(deviceExtension, irql);
//
// Put this Queue Head in the Schedule.
//
//
// This routine will insert the queue head in the proper place
// in the schedule and add the endpoint to the endpoint list
// so that completed transfers will be detected.
//
KeAcquireSpinLock(&deviceExtension->HcScheduleSpin, &irql);
UHCD_InsertQueueHeadInSchedule(DeviceObject,
endpoint,
endpoint->QueueHead,
offset); // no offset
// clear the idle flag just in case it got set
CLR_EPFLAG(endpoint, EPFLAG_IDLE);
KeReleaseSpinLock(&deviceExtension->HcScheduleSpin, irql);
//
// return the endpoint handle
//
urb->HcdUrbOpenEndpoint.HcdEndpoint = endpoint;
ntStatus = STATUS_SUCCESS;
} /* if endpoint */
//
// Complete the IRP, status is in the status field of the URB
//
UHCD_KdPrint((2, "'exit UHCD_OpenEndpoint_StartIo (URB STATUS = %x)\n", URB_HEADER(urb).Status ));
UHCD_OpenEndpoint_StartIo_Done:
#if DBG
//
// sanity check our buffer pools
//
// UHCD_BufferPoolCheck(DeviceObject);
#endif
IoStartNextPacket(DeviceObject, FALSE);
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
return;
}
VOID
UHCD_CloseEndpoint_StartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Free a UHCD endpoint, if there are any pending transfers for this
endpoint this routine should fail.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Return Value:
--*/
{
PHCD_URB urb;
PUHCD_ENDPOINT endpoint;
BOOLEAN outstandingTransfers = FALSE;
PDEVICE_EXTENSION deviceExtension;
ULONG i;
KIRQL irql;
BOOLEAN removed;
UHCD_KdPrint((2, "'enter UHCD_CloseEndpoint_StartIo\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
urb = URB_FROM_IRP(Irp);
endpoint = urb->HcdUrbCloseEndpoint.HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
if (endpoint->EndpointFlags & EPFLAG_FAST_ISO) {
UHCD_UnInitializeFastIsoEndpoint(DeviceObject,
endpoint);
UHCD_FreeBandwidth(DeviceObject, endpoint, endpoint->Offset);
}
//
// if there are any pending transfers fail this request
//
LOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'lck4');
//
// Do we have any transfers pending?
//
outstandingTransfers = !IsListEmpty(&endpoint->PendingTransferList);
for (i=0; !outstandingTransfers && i < endpoint->MaxRequests; i++) {
//
// no outstanding transfers in the queue, check the active list
// -- if some transfers get retired while we walk the list that
// is OK.
//
if (endpoint->ActiveTransfers[i] != NULL) {
outstandingTransfers = TRUE;
}
}
UNLOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'ulk4');
if (outstandingTransfers) {
//
// If we have outstanding transfers then we fail the
// request
//
URB_HEADER(urb).Status = USBD_STATUS_ERROR_BUSY;
} else {
//
// Remove the endpoint Queue from the schedule
//
KeAcquireSpinLock(&deviceExtension->HcScheduleSpin, &irql);
removed = UHCD_RemoveQueueHeadFromSchedule(DeviceObject,
endpoint,
endpoint->QueueHead,
TRUE);
KeReleaseSpinLock(&deviceExtension->HcScheduleSpin, irql);
// stop 'NoDMA' transfers
if (endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) {
UHCD_StopNoDMATransfer(DeviceObject,
endpoint);
}
//
// Put the endpoint on a queue to be freed at after
// the next frame has executed
//
// At this point the hardware links have been updated to remove this
// endpoint.
//
// we note the frame when it is safe to retire the endpoint so that
// the queue head may be freed safely by the ISR DPC routine next time
// we take an interrupt.
endpoint->FrameToClose = UHCD_GetCurrentFrame(DeviceObject)+2;
// BUGBUG this needs to be protected from the ISR DPC
// routine where these things are actually freed.
// queue it to be released
if (removed) {
InsertTailList(&deviceExtension->ClosedEndpointList, &endpoint->ListEntry);
}
URB_HEADER(urb).Status = USBD_STATUS_SUCCESS;
}
IoStartNextPacket(DeviceObject, FALSE);
UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, NULL);
UHCD_KdPrint((2, "'exit UHCD_CloseEndpoint_StartIo\n"));
}
VOID
UHCD_InsertQueueHeadInSchedule(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint,
IN PHW_QUEUE_HEAD QueueHead,
IN ULONG Offset
)
/*++
Routine Description:
Inserts an initialized queue head into the schedule
Queue head schedule looks like this:
PQH = persistant queue head
CQH = control Queue Head
IQH = Interrupt Queue Head
BQH = Bulk Queue Head
The persistant queue head software links
look like this:
|>---->---->-|
| <- <- | prev
BQH PQH CQH
| -> -> | next
|<----<----<-|
IQH->IQH->PQH
Hardware links look like this:
PQH->CQH->BQH->| (reclimaton on)
|-<-|
or
PQH->CQH->BQH->| (reclimation off)
|<-T-| (T BIT SET)
Iso/Interrupt hardware links:
ISO->IQH->PQH
Arguments:
DeviceObject - device object for this controller.
Endpoint - endpoint this Queue Head belongs to.
QueueHead - queue head to insert in schedule.
Return Value:
--*/
{
PDEVICE_EXTENSION deviceExtension;
PHW_QUEUE_HEAD persistantQueueHead, queueHeadForFrame,
nextQueueHead, prevQueueHead;
#ifdef RECLAIM_BW
PHW_QUEUE_HEAD firstBulkQueueHead;
#endif
ULONG i, interval;
BOOLEAN fixFrameList = FALSE;
UHCD_ASSERT(!(Endpoint->EndpointFlags & EPFLAG_ED_IN_SCHEDULE));
UHCD_KdPrint((2, "'enter InsertQueueHeadInSchedule\n"));
ASSERT_ENDPOINT(Endpoint);
ASSERT_QUEUE_HEAD(QueueHead);
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
persistantQueueHead = deviceExtension->PersistantQueueHead;
switch(Endpoint->Type) {
case USB_ENDPOINT_TYPE_CONTROL:
LOGENTRY(LOG_MISC, 'Cep+', Endpoint, 0, DeviceObject);
//
// before
//
// a b
// -PQH<->CQH-
// |---<->---|
//
// after
//
// a b
// -PQH<->CQH(new)<->CQH-
// |-------<->----------|
//
// insert in list
QueueHead->Next = persistantQueueHead->Next; //point to b
QueueHead->Prev = persistantQueueHead; //point to a
QueueHead->Next->Prev = QueueHead; // item b points back to new
// PQH points to the new element
QueueHead->Prev->Next = QueueHead; // item a points to new
// Fix up hardware links
QueueHead->HW_HLink = persistantQueueHead->HW_HLink;
// we are in the chain just after the Persistant Queue Head
// this line activates the QH
persistantQueueHead->HW_HLink =
QueueHead->PhysicalAddress;
break;
case USB_ENDPOINT_TYPE_INTERRUPT:
LOGENTRY(LOG_MISC, 'Iep+', Endpoint, QueueHead, DeviceObject);
QueueHead->Next = 0;
QueueHead->Prev = 0;
interval = Endpoint->Interval;
UHCD_ASSERT(Offset < interval);
UHCD_ASSERT(interval != 0);
LOGENTRY(LOG_MISC, 'Iep+',
Endpoint,
interval,
&deviceExtension->InterruptSchedule[0]);
// MAX_INTERVAL is the maximum polling interval we support in UHCD (power of 2).
// the requested polling intervals is always rounded to the next lowest power of 2.
// We keep an array (InterruptSchedule) of MAX_INTERVAL with the virtual addresses
// of the queue heads in the schedule. The size of InterruptSchedule is always a
// multiple of MAX_FRAME. i.e. InterruptSchedule contains the virtual addresses of
// queue heads for the first MAX_INTERVAL frames in the schedule.
UHCD_ASSERT(interval<=MAX_INTERVAL);
UHCD_ASSERT(Offset<interval);
UHCD_ASSERT(MAX_INTERVAL % interval == 0);
for (i=Offset; i<MAX_INTERVAL; i+=interval) {
// select the queue head
queueHeadForFrame = deviceExtension->InterruptSchedule[i];
LOGENTRY(LOG_MISC, 'Iqhf',
0, //queueHeadForFrame->Endpoint->Interval,
queueHeadForFrame,
i);
// find the appropriate place to add ourselves
if (queueHeadForFrame == persistantQueueHead ||
interval >= queueHeadForFrame->Endpoint->Interval) {
// if the first entry is the persistant queue head or our polling
// interval is greater or equal than this queue head then we just
// add in front of this one, we become the root node for this
// frame.
LOGENTRY(LOG_MISC, 'I>SC', 0, QueueHead, i);
deviceExtension->InterruptSchedule[i] = QueueHead;
fixFrameList = TRUE;
if (QueueHead->Next == 0) {
QueueHead->Next = queueHeadForFrame;
// update hardware link here
QueueHead->HW_HLink = queueHeadForFrame->PhysicalAddress;
} else {
// ounce the next pointer is updated
// it should never change
UHCD_ASSERT(QueueHead->Next == queueHeadForFrame);
}
} else {
// if our polling interval is less than the current queue
// head we need to insert in the proper position
LOGENTRY(LOG_MISC, 'I<SC', queueHeadForFrame, QueueHead, i);
nextQueueHead = queueHeadForFrame;
do {
prevQueueHead = nextQueueHead;
nextQueueHead = nextQueueHead->Next;
} while (nextQueueHead != persistantQueueHead &&
nextQueueHead->Endpoint->Interval > interval);
LOGENTRY(LOG_MISC, 'I<SQ', nextQueueHead,
QueueHead, prevQueueHead);
UHCD_ASSERT(nextQueueHead != 0);
// link in to the chain
if (QueueHead->Next == 0) {
QueueHead->Next = nextQueueHead;
// update hardware link here
QueueHead->HW_HLink = nextQueueHead->PhysicalAddress;
} else {
UHCD_ASSERT(QueueHead->Next == nextQueueHead ||
nextQueueHead == QueueHead);
}
prevQueueHead->Next = QueueHead;
// update hardware link here
prevQueueHead->HW_HLink = QueueHead->PhysicalAddress;
}
//
// repeat the process until we
// have visited all possible nodes for this queue head
//
}
// now update the physical frame list based on the virtual list
// if we have modified it.
if (fixFrameList) {
UHCD_CopyInterruptScheduleToFrameList(DeviceObject);
}
break;
case USB_ENDPOINT_TYPE_BULK:
//
// before
//
// b a
// -BQH<->PQH-
// |---<->---|
//
// after
//
// b a
// -BQH<->BQH(new)<->PQH-
// |-------<->----------|
//
// We need to add this endpoint to the tail of the
// persistant queue
//
LOGENTRY(LOG_MISC, 'Bep+', Endpoint, 0, DeviceObject);
// set up queue head fields
UHCD_KdPrint((2, "'bulk QH = %x, %x\n", QueueHead, Endpoint));
// insert in list
QueueHead->Next = persistantQueueHead; // point to a
QueueHead->Prev = persistantQueueHead->Prev; // point to b
// first item points back to us
QueueHead->Prev->Next = QueueHead; // item b points to new
// head points to the element
// select the old tail element
prevQueueHead = persistantQueueHead->Prev; // remember b
persistantQueueHead->Prev = QueueHead; // item a points to new
#ifdef RECLAIM_BW
//
// Fix up hardware links
//
// BUGBUG
// NOTE: we are only reclaiming bandwidth for bulk right
// now.
//
if (deviceExtension->SteppingVersion == UHCD_B0_STEP) {
//TEST_TRAP();
//
// BW reclimation, point back to the first bulk queue head
// with no T bit set
//
// walk the list and find the first bulk
// queue head
firstBulkQueueHead = persistantQueueHead;
do {
PUHCD_ENDPOINT endpoint;
endpoint = firstBulkQueueHead->Endpoint;
if (endpoint &&
endpoint->Type == USB_ENDPOINT_TYPE_BULK) {
break;
}
firstBulkQueueHead = firstBulkQueueHead->Next;
} while (firstBulkQueueHead != persistantQueueHead);
UHCD_ASSERT(firstBulkQueueHead != persistantQueueHead);
QueueHead->HW_HLink = firstBulkQueueHead->PhysicalAddress;
deviceExtension->HcFlags |= HCFLAG_BWRECLIMATION_ENABLED;
} else {
// Fix up hardware links
// points to the control queue head with T bit set
QueueHead->HW_HLink = prevQueueHead->HW_HLink;
UHCD_ASSERT(QueueHead->HW_HLink & UHCD_CF_TERMINATE);
}
#else
// Fix up hardware links
// points to the control queue head with T bit set
QueueHead->HW_HLink = prevQueueHead->HW_HLink;
UHCD_ASSERT(QueueHead->HW_HLink & UHCD_CF_TERMINATE);
#endif
// we are in the chain just before the PersistantQueueHead
// this line activates the QH
prevQueueHead->HW_HLink =
QueueHead->PhysicalAddress;
break;
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
LOGENTRY(LOG_MISC, 'Sep+', Endpoint, 0, DeviceObject);
break;
default:
// invalid endpoint type, probably a bug
UHCD_KdTrap(
("UHCD_InsertQueueHeadInSchedule inavlid endpoint type\n"));
}
SET_EPFLAG(Endpoint, EPFLAG_ED_IN_SCHEDULE);
UHCD_KdPrint((2, "'exit InsertQueueHeadInSchedule\n"));
return;
}
BOOLEAN
UHCD_RemoveQueueHeadFromSchedule(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint,
IN PHW_QUEUE_HEAD QueueHead,
IN BOOLEAN RemoveFromEPList
)
/*++
Routine Description:
Removes a queue head from the schedule
Arguments:
DeviceObject - device object for this controller.
Endpoint - endpoint this Queue Head belongs to.
QueueHead - queue head to remove.
Return Value:
returns TRUE if EP was removed from ep list
--*/
{
PDEVICE_EXTENSION deviceExtension;
PHW_QUEUE_HEAD persistantQueueHead, queueHeadForFrame,
nextQueueHead, prevQueueHead;
BOOLEAN fixFrameList = FALSE;
ULONG i;
HW_DESCRIPTOR_PHYSICAL_ADDRESS physicalAddress;
KIRQL irql;
BOOLEAN removed = FALSE;
UHCD_KdPrint((2, "'enter RemoveQueueHeadFromSchedule\n"));
ASSERT_ENDPOINT(Endpoint);
if (Endpoint->EndpointFlags & EPFLAG_FAST_ISO) {
// nothing to remove
return FALSE;
}
ASSERT_QUEUE_HEAD(QueueHead);
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
persistantQueueHead = deviceExtension->PersistantQueueHead;
//
// Remove the Queue Head from the endpoint list
//
if (RemoveFromEPList) {
LOCK_ENDPOINT_LIST(deviceExtension, irql);
if (deviceExtension->EndpointListBusy) {
// mark the entry so we can remove it later
SET_EPFLAG(Endpoint, EPFLAG_EP_CLOSED);
} else {
RemoveEntryList(&Endpoint->ListEntry);
removed = TRUE;
}
UNLOCK_ENDPOINT_LIST(deviceExtension, irql);
}
if (!(Endpoint->EndpointFlags & EPFLAG_ED_IN_SCHEDULE)) {
LOGENTRY(LOG_MISC, 'NISc', Endpoint, 0, DeviceObject);
return removed;
}
switch(Endpoint->Type) {
case USB_ENDPOINT_TYPE_CONTROL:
case USB_ENDPOINT_TYPE_BULK:
// set up queue head fields
prevQueueHead = QueueHead->Prev;
nextQueueHead = QueueHead->Next;
// unlink software links
prevQueueHead->Next = QueueHead->Next;
nextQueueHead->Prev = QueueHead->Prev;
if ((QueueHead->HW_HLink & UHCD_DESCRIPTOR_PTR_MASK)
==
(QueueHead->PhysicalAddress & UHCD_DESCRIPTOR_PTR_MASK)) {
//
// Queue head points to itself, this means it is
// the only bulk queue in the list.
//
// This will only happen if we have BW reclaimation
// is enabled.
//
physicalAddress =
deviceExtension->PersistantQueueHead->PhysicalAddress;
SET_T_BIT(physicalAddress);
UHCD_ASSERT(physicalAddress & UHCD_CF_QUEUE);
prevQueueHead->HW_HLink = physicalAddress;
} else {
// Fix up hardware link
prevQueueHead->HW_HLink = QueueHead->HW_HLink;
}
break;
case USB_ENDPOINT_TYPE_INTERRUPT:
//
// Brute force method:
// Walk every frame in the InterruptSchedule and update
// any node that references this queue head.
//
for (i=0; i<MAX_INTERVAL; i++) {
queueHeadForFrame = deviceExtension->InterruptSchedule[i];
if (queueHeadForFrame == QueueHead) {
// Queue Head was root node for this frame
deviceExtension->InterruptSchedule[i] = QueueHead->Next;
fixFrameList = TRUE;
} else {
while (queueHeadForFrame != persistantQueueHead &&
queueHeadForFrame->Endpoint->Interval >=
QueueHead->Endpoint->Interval) {
if (queueHeadForFrame->Next == QueueHead) {
// found a link to our queue head,
// remove it
queueHeadForFrame->Next = QueueHead->Next;
// unlink from Hardware Queue
queueHeadForFrame->HW_HLink = QueueHead->HW_HLink;
}
queueHeadForFrame = queueHeadForFrame->Next;
}
}
if (fixFrameList) {
UHCD_CopyInterruptScheduleToFrameList(DeviceObject);
}
}
break;
case USB_ENDPOINT_TYPE_ISOCHRONOUS:
LOGENTRY(LOG_MISC, 'Sep-', Endpoint, 0, DeviceObject);
break;
default:
// Invalid endpoint type, probably a bug
UHCD_KdTrap(
("UHCD_RemoveQueueHeadFromSchedule inavlid endpoint type\n"));
}
CLR_EPFLAG(Endpoint,
EPFLAG_ED_IN_SCHEDULE);
UHCD_KdPrint((2, "'exit RemoveQueueHeadFromSchedule\n"));
return removed;
}
VOID
UHCD_CopyInterruptScheduleToFrameList(
IN PDEVICE_OBJECT DeviceObject
)
/*++
Routine Description:
Transfers the virtual interrupt schedule to the frame list
Arguments:
DeviceObject - device object for this controller.
Return Value:
None.
--*/
{
PDEVICE_EXTENSION deviceExtension;
ULONG i;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
for (i=0; i < FRAME_LIST_SIZE; i++) {
if (i == 0 || i == FRAME_LIST_SIZE-1)
deviceExtension->TriggerTDList->TDs[i == 0 ? 0 : 1].HW_Link =
deviceExtension->InterruptSchedule[i % MAX_INTERVAL]->PhysicalAddress;
else {
ULONG currentTdCopy, currentTd;
PUHCD_ENDPOINT endpoint;
currentTd =
*( ((PULONG) (deviceExtension->FrameListVirtualAddress)+i));
currentTdCopy =
*( ((PULONG) (deviceExtension->FrameListCopyVirtualAddress)+i));
endpoint = UHCD_GetLastFastIsoEndpoint(DeviceObject);
// have fast iso?
if (endpoint) {
PFAST_ISO_DATA fastIsoData;
PHW_TRANSFER_DESCRIPTOR transferDescriptor;
// fast iso TDs are present, we will need to insert
// the interrupt schedule after these TDs.
fastIsoData = &endpoint->FastIsoData;
transferDescriptor = (PHW_TRANSFER_DESCRIPTOR)
(fastIsoData->IsoTDListVa + (i*32));
transferDescriptor->HW_Link =
deviceExtension->InterruptSchedule[i % MAX_INTERVAL]->PhysicalAddress;
} else {
// no fast iso -- just update the schedule
*( ((PULONG) (deviceExtension->FrameListCopyVirtualAddress)+i) ) =
deviceExtension->InterruptSchedule[i % MAX_INTERVAL]->PhysicalAddress;
// if the currentTd == the copy then we don't have any iso
// tds in the schedule so it is safe to update the schedule directly
if (currentTd == currentTdCopy) {
*( ((PULONG) (deviceExtension->FrameListVirtualAddress)+i) ) =
deviceExtension->InterruptSchedule[i % MAX_INTERVAL]->PhysicalAddress;
}
}
}
}
}
VOID
UHCD_StartIoCancel(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PHCD_URB urb;
//
// Irp has not been processed by StartIo yet
//
LOGENTRY(LOG_MISC, 'sioC', Irp, 0, DeviceObject);
if (DeviceObject->CurrentIrp == Irp) {
LOGENTRY(LOG_MISC, 'curI', Irp, 0, DeviceObject);
IoReleaseCancelSpinLock(Irp->CancelIrql);
} else {
LOGENTRY(LOG_MISC, 'Ncur', Irp, 0, DeviceObject);
if (KeRemoveEntryDeviceQueue(&DeviceObject->DeviceQueue,
&Irp->Tail.Overlay.DeviceQueueEntry)) {
LOGENTRY(LOG_MISC, 'YDVQ', Irp, 0, DeviceObject);
TEST_TRAP();
urb = (PHCD_URB) URB_FROM_IRP(Irp);
IoReleaseCancelSpinLock(Irp->CancelIrql);
while (urb) {
URB_HEADER(urb).Status = USBD_STATUS_CANCELED;
if (UHCD_IS_TRANSFER(urb)) {
urb = urb->HcdUrbCommonTransfer.UrbLink;
} else {
break;
}
}
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
USBD_CompleteRequest(Irp,
IO_NO_INCREMENT);
} else {
LOGENTRY(LOG_MISC, 'NDVQ', Irp, 0, DeviceObject);
TEST_TRAP();
IoReleaseCancelSpinLock(Irp->CancelIrql);
}
}
}
VOID
UHCD_GetSetEndpointState_StartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Change or report state of an endpoint
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Return Value:
--*/
{
PUHCD_ENDPOINT endpoint;
PHCD_URB urb;
BOOLEAN outstandingTransfers;
ULONG i;
KIRQL irql;
urb = (PHCD_URB) URB_FROM_IRP(Irp);
endpoint = (PUHCD_ENDPOINT) urb->HcdUrbEndpointState.HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
//
// Do we have any transfers pending?
//
irql = KeGetCurrentIrql();
LOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'lck5');
outstandingTransfers = !IsListEmpty(&endpoint->PendingTransferList);
#if DBG
if (outstandingTransfers) {
UHCD_KdPrint((2, "'GET_ENDPOINT_STATE ep has pending transfers\n"));
}
#endif
for (i=0; !outstandingTransfers && i < endpoint->MaxRequests; i++) {
//
// no outstanding transfers in the queue, check the active list
// -- if some transfers get retired while we walk the list that
// is OK.
//
if (endpoint->ActiveTransfers[i] != NULL) {
UHCD_KdPrint((2, "'GETSET_ENDPOINT_STATE ep has active transfers\n"));
outstandingTransfers = TRUE;
}
}
UNLOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'ulk5');
switch (urb->HcdUrbEndpointState.Function) {
case URB_FUNCTION_HCD_GET_ENDPOINT_STATE:
urb->HcdUrbEndpointState.HcdEndpointState = 0;
if (endpoint->EndpointFlags & EPFLAG_HOST_HALTED) {
UHCD_KdPrint((2, "'GET_ENDPOINT_STATE host halted\n"));
urb->HcdUrbEndpointState.HcdEndpointState |=
HCD_ENDPOINT_HALTED;
}
if (outstandingTransfers) {
urb->HcdUrbEndpointState.HcdEndpointState |=
HCD_ENDPOINT_TRANSFERS_QUEUED;
}
URB_HEADER(urb).Status = USBD_STATUS_SUCCESS;
UHCD_KdPrint((2, "'GET_ENDPOINT_STATE state = %x\n",
urb->HcdUrbEndpointState.HcdEndpointState));
break;
case URB_FUNCTION_HCD_SET_ENDPOINT_STATE:
if (!outstandingTransfers) {
LOGENTRY(LOG_MISC, 'cla2', endpoint, 0, 0);
CLR_EPFLAG(endpoint,
EPFLAG_ABORT_PENDING_TRANSFERS |
EPFLAG_ABORT_ACTIVE_TRANSFERS);
}
UHCD_KdPrint((2, "'Set Enpoint State flags = %x\n", endpoint->EndpointFlags));
if (endpoint->EndpointFlags & (EPFLAG_ABORT_ACTIVE_TRANSFERS |
EPFLAG_ABORT_PENDING_TRANSFERS)) {
//fail the request
IoStartNextPacket(DeviceObject, FALSE);
// let USBD map the error for us
URB_HEADER(urb).Status = USBD_STATUS_ERROR_BUSY;
UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, NULL);
return;
}
if (endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) {
// stop streaming data
UHCD_StopNoDMATransfer(DeviceObject,
endpoint);
}
//
// restore virgin status to the pipe
//
SET_EPFLAG(endpoint, EPFLAG_VIRGIN);
//
// set the data toggle back to 0
//
if (urb->HcdUrbEndpointState.HcdEndpointState &
HCD_ENDPOINT_RESET_DATA_TOGGLE) {
endpoint->DataToggle = 0;
}
if (!(urb->HcdUrbEndpointState.HcdEndpointState & HCD_ENDPOINT_HALTED)) {
//
// halt bit cleared, reset the endpoint.
//
LOGENTRY(LOG_MISC, 'cla3', endpoint, 0, 0);
CLR_EPFLAG(endpoint,
EPFLAG_ABORT_PENDING_TRANSFERS |
EPFLAG_ABORT_ACTIVE_TRANSFERS |
EPFLAG_HOST_HALTED);
//
// Start any transfers in the pending queue
//
if (endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) {
// transfers will re-start on the next interrupt
UHCD_RequestInterrupt(DeviceObject, -2);
} else {
UHCD_EndpointWorker(DeviceObject, endpoint);
}
}
URB_HEADER(urb).Status = USBD_STATUS_SUCCESS;
break;
default:
// unknown command, probably a bug
UHCD_KdTrap(("Bogus Endpoint state command\n"));
}
IoStartNextPacket(DeviceObject, FALSE);
UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, NULL);
}
NTSTATUS
UHCD_FinishInitializeEndpoint(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint,
IN PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor,
IN PHCD_URB Urb
)
/*++
Routine Description:
Change or report state of an endpoint
Arguments:
Endpoint - endpoint structure to initilaize
EndpointDescriptor - pointer to the USB endpoint descriptor
for this endpoint.
Urb - urb associated with the open request
Return Value:
--*/
{
UCHAR tmp;
ULONG i;
NTSTATUS ntStatus = STATUS_SUCCESS;
// note that some fields are already initilaized
ASSERT_ENDPOINT(Endpoint);
Endpoint->Type = USB_ENDPOINT_TYPE_MASK & EndpointDescriptor->bmAttributes;
Endpoint->EndpointAddress = EndpointDescriptor->bEndpointAddress;
Endpoint->MaxPacketSize = EndpointDescriptor->wMaxPacketSize;
Endpoint->DeviceAddress = (UCHAR) Urb->HcdUrbOpenEndpoint.DeviceAddress;
Endpoint->LastPacketDataToggle =
Endpoint->DataToggle = 0;
Endpoint->Interval = 0;
Endpoint->IdleTime = 0;
SET_EPFLAG(Endpoint, EPFLAG_VIRGIN);
SET_EPFLAG(Endpoint, EPFLAG_INIT);
Endpoint->MaxTransferSize = Urb->HcdUrbOpenEndpoint.MaxTransferSize;
// No DMA endpoint ?
#if DBG
if (Endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) {
// client idicates that transfers will be mostly short
// in this case we will turn off short packet detect
// and double buffer all transfers to reduce the overhead of
// having to program each transfer to the hardware separately
UHCD_KdPrint((1, "'Client requesting double buffering EP = %x\n", Endpoint));
}
#endif
#if DBG
if (!(Endpoint->EndpointFlags & EPFLAG_ROOT_HUB)) {
UHCD_ASSERT(Endpoint->TDCount == UHCD_GetNumTDsPerEndoint(Endpoint->Type));
}
#endif
if (Urb->HcdUrbOpenEndpoint.HcdEndpointFlags & USBD_EP_FLAG_LOWSPEED) {
SET_EPFLAG(Endpoint, EPFLAG_LOWSPEED);
}
if (Urb->HcdUrbOpenEndpoint.HcdEndpointFlags & USBD_EP_FLAG_NEVERHALT) {
SET_EPFLAG(Endpoint, EPFLAG_NO_HALT);
}
KeInitializeSpinLock(&Endpoint->ActiveListSpin);
KeInitializeSpinLock(&Endpoint->PendingListSpin);
#if DBG
Endpoint->AccessPendingList = 0;
Endpoint->AccessActiveList = 0;
#endif
UHCD_KdPrint((2, "'MaxRequests = %d\n", Endpoint->MaxRequests));
UHCD_ASSERT(Endpoint->MaxRequests == MAX_REQUESTS(EndpointDescriptor,
Endpoint->EndpointFlags));
// Select the highest interval we support <= the requested interval.
// note: an interval of zero gets a oeriod of MAX_INTERVAL
tmp = EndpointDescriptor->bInterval;
Endpoint->Interval = MAX_INTERVAL;
if (EndpointDescriptor->bInterval > MAX_INTERVAL ||
EndpointDescriptor->bInterval == 0) {
tmp |= MAX_INTERVAL;
}
while ((Endpoint->Interval & tmp) == 0) {
Endpoint->Interval >>= 1;
}
//
// make sure that iso endpoints have an
// interval of 1
//
if (Endpoint->Type == USB_ENDPOINT_TYPE_ISOCHRONOUS) {
Endpoint->Interval = 1; //iso endpoints have a period of 1
}
InitializeListHead(&Endpoint->PendingTransferList);
for (i=0; i<UHCD_MAX_ACTIVE_TRANSFERS; i++) {
Endpoint->ActiveTransfers[i] = NULL;
}
return ntStatus;
}
USHORT
UHCD_GetNumTDsPerEndoint(
IN UCHAR EndpointType
)
/*++
Routine Description:
Return the number of TDs to use for this endpoint based on type
Arguments:
Return Value:
--*/
{
// use max TDs always for bulk to get max thru-put regardless
// of packet size.
// Historical Note:
// this is a change from Win98gold and Win98se,
// originally we only enabled this if MAX_PACKET was 64 to save
// memory for slower devices -- but vendors bitched about it so
// now we enable it regardless of packet size.
if (EndpointType == USB_ENDPOINT_TYPE_ISOCHRONOUS ||
EndpointType == USB_ENDPOINT_TYPE_BULK) {
return MAX_TDS_PER_ENDPOINT;
} else {
return MIN_TDS_PER_ENDPOINT;
}
}
VOID
UHCD_BW_Reclimation(
IN PDEVICE_OBJECT DeviceObject,
IN BOOLEAN Enable
)
/*++
Routine Description:
Turn on/off BW reclimation for the Bulk Queues
Arguments:
Return Value:
None.
--*/
{
PDEVICE_EXTENSION deviceExtension;
PHW_QUEUE_HEAD persistantQueueHead, firstBulkQueueHead,
lastBulkQueueHead;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
persistantQueueHead = deviceExtension->PersistantQueueHead;
if ((deviceExtension->HcFlags & HCFLAG_BWRECLIMATION_ENABLED)
== Enable) {
// no state change just return;
return;
}
LOGENTRY(LOG_MISC, 'BRCL', deviceExtension, Enable, DeviceObject);
//
// BW reclimation, point back to the first bulk queue head
// with no T bit set
//
// walk the list and find the first bulk
// queue head
firstBulkQueueHead = persistantQueueHead;
do {
PUHCD_ENDPOINT endpoint;
endpoint = firstBulkQueueHead->Endpoint;
if (endpoint &&
endpoint->Type == USB_ENDPOINT_TYPE_BULK) {
break;
}
firstBulkQueueHead = firstBulkQueueHead->Next;
} while (firstBulkQueueHead != persistantQueueHead);
if (firstBulkQueueHead != persistantQueueHead) {
// no bulk endpoints
PHW_QUEUE_HEAD next;
PUHCD_ENDPOINT endpoint;
lastBulkQueueHead = firstBulkQueueHead;
do {
next = lastBulkQueueHead->Next;
endpoint = next->Endpoint;
if (endpoint == NULL ||
endpoint->Type != USB_ENDPOINT_TYPE_BULK) {
break;
}
lastBulkQueueHead = next;
} while (1);
if (Enable) {
//clear the T-bit to enable reclimation
LOGENTRY(LOG_MISC, 'BRC+', lastBulkQueueHead, 0, DeviceObject);
CLEAR_T_BIT(lastBulkQueueHead->HW_HLink);
deviceExtension->HcFlags |= HCFLAG_BWRECLIMATION_ENABLED;
} else {
//set the T-bit to disable reclimation
LOGENTRY(LOG_MISC, 'BRC-', lastBulkQueueHead, 0, DeviceObject);
SET_T_BIT(lastBulkQueueHead->HW_HLink);
deviceExtension->HcFlags &= ~HCFLAG_BWRECLIMATION_ENABLED;
}
}
}