2478 lines
69 KiB
C
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|