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

1940 lines
52 KiB
C

/*++
Copyright (c) 1995,1996 Microsoft Corporation
:ts=4
Module Name:
transfer.c
Abstract:
The module manages control type transactions on the USB.
Environment:
kernel mode only
Notes:
Revision History:
11-01-95 : created
04-26-96 : linked urb support
--*/
#include "wdm.h"
#include "stdarg.h"
#include "stdio.h"
#include "usbdi.h"
#include "hcdi.h"
#include "uhcd.h"
//
// Use the flag to force all pending transfers to complete
//
typedef USBD_STATUS UHCD_INIT_TRANSFER_ROUTINE(PDEVICE_OBJECT DeviceObject,
PUHCD_ENDPOINT Endpoint,
PHCD_URB Urb);
typedef USBD_STATUS UHCD_PROCESS_TRANSFER_ROUTINE(
PDEVICE_OBJECT DeviceObject,
PUHCD_ENDPOINT Endpoint,
PHCD_URB Urb,
PBOOLEAN Completed);
UHCD_INIT_TRANSFER_ROUTINE UHCD_InitializeAsyncTransfer;
UHCD_PROCESS_TRANSFER_ROUTINE UHCD_ProcessAsyncTransfer;
UHCD_INIT_TRANSFER_ROUTINE UHCD_InitializeIsochTransfer;
UHCD_PROCESS_TRANSFER_ROUTINE UHCD_ProcessIsochTransfer;
typedef struct _UHCD_TRANSFER_DISPATCH_ENTRY {
UHCD_INIT_TRANSFER_ROUTINE *InitializeTransfer;
UHCD_PROCESS_TRANSFER_ROUTINE *ProcessTransfer;
} UHCD_TRANSFER_DISPATCH_ENTRY;
UHCD_TRANSFER_DISPATCH_ENTRY TransferDispatchTable[4] =
{
//Control
UHCD_InitializeAsyncTransfer, UHCD_ProcessAsyncTransfer,
//Isoch
UHCD_InitializeIsochTransfer, UHCD_ProcessIsochTransfer,
//Bulk
UHCD_InitializeAsyncTransfer, UHCD_ProcessAsyncTransfer,
//Interrupt
UHCD_InitializeAsyncTransfer, UHCD_ProcessAsyncTransfer
};
VOID
UHCD_ValidateIsoUrb(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint,
IN OUT PHCD_URB Urb
)
/*++
Routine Description:
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Return Value:
None.
--*/
{
ULONG currentBusFrame;
LONG offset;
PDEVICE_EXTENSION deviceExtension;
BOOLEAN late = FALSE;
UHCD_ASSERT(Endpoint->Type == USB_ENDPOINT_TYPE_ISOCHRONOUS);
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
currentBusFrame = UHCD_GetCurrentFrame(DeviceObject);
LOGENTRY(LOG_ISO,'ISxf', 0, Urb, currentBusFrame);
if (Urb->HcdUrbCommonTransfer.TransferFlags &
USBD_START_ISO_TRANSFER_ASAP) {
if (Endpoint->EndpointFlags & EPFLAG_VIRGIN) {
UHCD_KdPrint((2, "'ASAP flag set for virgin pipe\n"));
// No transfers on this endpoint yet, set the StartFrame
// to the current bus frame, plus a latency factor.
//
Urb->UrbIsochronousTransfer.StartFrame =
currentBusFrame + UHCD_ASAP_LATENCY;
} else {
UHCD_KdPrint((2, "'ASAP flag set for non-virgin pipe\n"));
// There have been transfers on this endpoint already,
// set the StartFrame to the next free frame for this
// endpoint.
//
Urb->UrbIsochronousTransfer.StartFrame =
Endpoint->CurrentFrame;
}
} else {
// If the StartFrame is explicitly specified and there was
// a previous transfer on this endpoint, make sure the specified
// StartFrame does not overlap the last transfer.
//
if (!(Endpoint->EndpointFlags & EPFLAG_VIRGIN)) {
offset = Urb->UrbIsochronousTransfer.StartFrame -
Endpoint->CurrentFrame;
if (offset < 0) {
UHCD_KdPrint((2, "'StartFrame overlap\n"));
URB_HEADER(Urb).Status = USBD_STATUS_BAD_START_FRAME;
}
}
}
// Sanity check that the start frame is within a certain range
// of the current bus frame
UHCD_KdPrint((2, "'currentBusFrame = %d\n", currentBusFrame));
offset = Urb->UrbIsochronousTransfer.StartFrame - currentBusFrame;
if (offset < 0) {
deviceExtension->IsoStats.LateUrbs++;
late = TRUE;
offset*=-1;
// transfer was late count how may packets missed due
// to tardyness
deviceExtension->IsoStats.LateMissedCount += ((USHORT)offset);
}
if (late &&
offset == (LONG) Urb->UrbIsochronousTransfer.NumberOfPackets) {
deviceExtension->IsoStats.StaleUrbs++;
}
if (offset > USBD_ISO_START_FRAME_RANGE) {
UHCD_KdPrint((2, "'StartFrame out of range\n"));
URB_HEADER(Urb).Status = USBD_STATUS_BAD_START_FRAME;
}
// update our iso Stats
if (offset == 0) {
deviceExtension->IsoStats.LateUrbs++;
} else if (offset == 1 && !late) {
// client requests this transfer within 1ms
deviceExtension->IsoStats.TransfersCF_1ms++;
} else if (offset == 2 && !late) {
// client requests this transfer within 2ms
deviceExtension->IsoStats.TransfersCF_2ms++;
} else if (offset <= 5 && !late) {
// client requests this transfer within 5ms
deviceExtension->IsoStats.TransfersCF_5ms++;
}
if (deviceExtension->IsoStats.SmallestUrbPacketCount == 0) {
deviceExtension->IsoStats.SmallestUrbPacketCount =
(USHORT) Urb->UrbIsochronousTransfer.NumberOfPackets;
}
if (Urb->UrbIsochronousTransfer.NumberOfPackets <
deviceExtension->IsoStats.SmallestUrbPacketCount) {
deviceExtension->IsoStats.SmallestUrbPacketCount =
(USHORT) Urb->UrbIsochronousTransfer.NumberOfPackets;
}
if (Urb->UrbIsochronousTransfer.NumberOfPackets >
deviceExtension->IsoStats.LargestUrbPacketCount) {
deviceExtension->IsoStats.LargestUrbPacketCount =
(USHORT) Urb->UrbIsochronousTransfer.NumberOfPackets;
}
}
VOID
UHCD_Transfer_StartIo(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
Called for each transfer request on a control endpoint, from the
UHCD_StartIo routine.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Return Value:
None.
--*/
{
PHCD_URB urb, urbtmp;
PDEVICE_EXTENSION deviceExtension;
PUHCD_ENDPOINT endpoint;
KIRQL irql;
UHCD_KdPrint((2, "'enter UHCD_Transfer_StartIo\n"));
//
// initialize pending count now
//
PENDING_URB_COUNT(Irp) = 0;
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
//
// Get the endpoint
//
urb = URB_FROM_IRP(Irp);
endpoint = HCD_AREA(urb).HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
UHCD_EndpointWakeup(DeviceObject, endpoint);
LOGENTRY(LOG_ISO, 'xSIO', 0, 0, 0);
// If this is an iso transfer then see if we need to set the start frame
//
if (endpoint->Type == USB_ENDPOINT_TYPE_ISOCHRONOUS) {
UHCD_ValidateIsoUrb(DeviceObject,
endpoint,
urb);
if (URB_HEADER(urb).Status == USBD_STATUS_BAD_START_FRAME) {
IoStartNextPacket(DeviceObject, FALSE);
LOGENTRY(LOG_ISO,'BADf', 0, urb, 0);
deviceExtension->IsoStats.BadStartFrame++;
// NOTE: we only allow one urb per iso request
// since we pended the original request bump
// the pending count so we'll complete this request
INCREMENT_PENDING_URB_COUNT(Irp);
UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, urb);
goto UHCD_Transfer_StartIo_Done;
}
// Advance the next free StartFrame for this endpoint to be the
// frame immediately following the last frame of this transfer.
//
endpoint->CurrentFrame = urb->UrbIsochronousTransfer.StartFrame +
urb->UrbIsochronousTransfer.NumberOfPackets;
//
// we lose our virginity when the first transfer starts
//
CLR_EPFLAG(endpoint, EPFLAG_VIRGIN);
} else if (endpoint->Type == USB_ENDPOINT_TYPE_BULK) {
//
// turn on BW reclimation for bulk transfers
//
UHCD_BW_Reclimation(DeviceObject, TRUE);
}
//
// check the endpoint state, if we are stalled we need
// to refuse transfers.
//
if (endpoint->EndpointFlags & EPFLAG_HOST_HALTED) {
//
// mark all urbs submitted with an error
//
urbtmp = urb;
do {
INCREMENT_PENDING_URB_COUNT(Irp);
urbtmp->HcdUrbCommonTransfer.Status =
USBD_STATUS_ENDPOINT_HALTED;
urbtmp = urbtmp->HcdUrbCommonTransfer.UrbLink;
#if DBG
if (urbtmp) {
TEST_TRAP();
}
#endif
} while (urbtmp);
IoStartNextPacket(DeviceObject, FALSE);
UHCD_CompleteIrp(DeviceObject, Irp, STATUS_SUCCESS, 0, urb);
goto UHCD_Transfer_StartIo_Done;
}
if (endpoint->EndpointFlags & EPFLAG_FAST_ISO) {
INCREMENT_PENDING_URB_COUNT(Irp);
UHCD_ProcessFastIsoTransfer(DeviceObject,
endpoint,
Irp,
urb);
IoStartNextPacket(DeviceObject, FALSE);
goto UHCD_Transfer_StartIo_Done;
}
//
// Get exclusive access to the endpoint pending transfer
// queue.
//
LOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'lck0');
//
// Insert all urbs that make up this request into
// the transfer pending queue.
//
do {
INCREMENT_PENDING_URB_COUNT(Irp);
URB_HEADER(urb).Status = UHCD_STATUS_PENDING_QUEUED;
InsertTailList(&endpoint->PendingTransferList,
&HCD_AREA(urb).HcdListEntry);
//#if DBG
// if (urb->HcdUrbCommonTransfer.UrbLink) {
// TEST_TRAP();
// }
//#endif
urb = urb->HcdUrbCommonTransfer.UrbLink;
} while (urb);
//
// release exclusive access to the endpoint pending list
//
UNLOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'ulk0');
//
// call the endpoint worker function to activate
// any transfers if possible.
//
if (endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) {
UHCD_RequestInterrupt(DeviceObject, -2);
} else {
UHCD_EndpointWorker(DeviceObject, endpoint);
}
IoStartNextPacket(DeviceObject, FALSE);
UHCD_Transfer_StartIo_Done:
UHCD_KdPrint((2, "'exit UHCD_Transfer_StartIo\n"));
}
VOID
UHCD_CompleteTransferDPC(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint,
IN LONG Slot
)
/*++
Routine Description:
Process a urb in the active list for an endpoint, complete if
necessary and start more transfers.
This routine is not reentrant for the same endpoint.
Arguments:
DeviceObject - pointer to a device object.
Endpoint - endpoint to check for completed transfers.
Slot - endpoint active slot to process.
Return Value:
--*/
{
PHCD_URB urb, urbtmp;
BOOLEAN completed = FALSE;
ULONG usbdStatus = USBD_STATUS_SUCCESS;
PIRP irp = NULL;
PDEVICE_EXTENSION deviceExtension;
ULONG i;
PVOID currentVa;
PHCD_EXTENSION urbWork;
KIRQL irql;
//UHCD_KdPrint((2, "'enter UHCD_CompleteTransferDPC\n"));
ASSERT_ENDPOINT(Endpoint);
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
//
// Do we have an current transfer for this endpoint?
//
// if no the endpoint is 'idle' on the host end,
// so we just exit.
//
// if yes then we need to see if we have completed
// the transfer set, if we have then we complete the
// urb and call EndpointWorker to start more transfers
//
//
// ** BUGBUG
// ** Optimization
// an optimization we can add here is to remove
// the endpoint from the endpoint list when it goes
// idle -- we would then let the EndpointWorker
// function actuvate it when more transfers get queued.
//
//
// get the Transfer we are intetrested in
//
urb = Endpoint->ActiveTransfers[Slot];
UHCD_ASSERT(urb != NULL);
urbWork = HCD_AREA(urb).HcdExtension;
// check the skip flag
if (Endpoint->EndpointFlags & EPFLAG_ABORT_ACTIVE_TRANSFERS) {
// clear the skip flag if we are in an abort scenario
LOGENTRY(LOG_MISC, 'Cdfr', urb, urbWork, Slot);
urbWork->Flags &= ~UHCD_TRANSFER_DEFER;
}
// check the skip flag
if (urbWork->Flags & UHCD_TRANSFER_DEFER) {
LOGENTRY(LOG_MISC, 'defr', urb, urbWork, Slot);
return;
}
//
// Determine if the current transfer request is finished.
//
UHCD_ASSERT(Endpoint->Type<=USB_ENDPOINT_TYPE_INTERRUPT);
irp = HCD_AREA(urb).HcdIrp;
//
// If the urb is marked completed with an error then don't
// bother calling the process routine.
//
// This will allow the process routine to mark additional
// active urbs for completion.
// This also allows the completion EndpointWorker routine
// to complete any transfers that it could not initilaize.
//
if (USBD_ERROR(urb->HcdUrbCommonTransfer.Status)) {
completed = TRUE;
LOGENTRY(LOG_MISC, 'tErr', urb->HcdUrbCommonTransfer.Status, urb, 0);
} else if (urbWork->Flags & UHCD_TRANSFER_ACTIVE) {
usbdStatus =
(TransferDispatchTable[Endpoint->Type].ProcessTransfer)
(DeviceObject,
Endpoint,
urb,
&completed);
}
if (completed) { // current transfer completed
ULONG userBufferLength;
LOGENTRY(LOG_ISO,'xfrC', Endpoint, urb, usbdStatus);
LOGENTRY(LOG_ISO,'xfr2', urb->HcdUrbCommonTransfer.Status, 0, 0);
UHCD_ASSERT(urbWork->BytesTransferred <=
urb->HcdUrbCommonTransfer.TransferBufferLength);
userBufferLength = urb->HcdUrbCommonTransfer.TransferBufferLength;
urb->HcdUrbCommonTransfer.TransferBufferLength =
urbWork->BytesTransferred;
//
// free the map registers now along with common buffers used to
// double buffer packets.
//
if (urbWork->NumberOfLogicalAddresses) {
currentVa =
MmGetMdlVirtualAddress(urb->HcdUrbCommonTransfer.
TransferBufferMDL);
// Flush the DMA buffer. If this was an IN transfer and the
// transfer was double-buffered (e.g. original buffer was located
// above 4GB on a PAE system) this will flush the DMA buffer back
// to the original transfer buffer.
//
// IoFlushAdapterBuffers() should only be called once per call
// to IoAllocateAdapterChannel()
//
IoFlushAdapterBuffers(deviceExtension->AdapterObject,
urb->HcdUrbCommonTransfer.TransferBufferMDL,
urbWork->MapRegisterBase,
currentVa,
urb->HcdUrbCommonTransfer.TransferBufferLength,
DATA_DIRECTION_OUT(urb));
for (i=0; i<urbWork->NumberOfLogicalAddresses; i++) {
if (urbWork->LogicalAddressList[i].PacketMemoryDescriptor) {
// if this is an IN transfer then update the
// client buffer
ULONG copylen;
if (DATA_DIRECTION_IN(urb)) {
LOGENTRY(LOG_MISC, 'PAKd', Endpoint->MaxPacketSize,
urbWork->LogicalAddressList[i].PacketOffset,
urbWork->LogicalAddressList[i].
PacketMemoryDescriptor->VirtualAddress);
LOGENTRY(LOG_MISC, 'PAKd', 0,
currentVa,
(PUCHAR)urb->HcdUrbCommonTransfer.
TransferBufferMDL->MappedSystemVa);
// make sure we don't overrun the client buffer
copylen = userBufferLength -
urbWork->LogicalAddressList[i].PacketOffset;
if (copylen > Endpoint->MaxPacketSize) {
copylen = Endpoint->MaxPacketSize;
}
RtlCopyMemory((PUCHAR)urbWork->SystemAddressForMdl +
urbWork->LogicalAddressList[i].PacketOffset,
urbWork->LogicalAddressList[i].
PacketMemoryDescriptor->VirtualAddress,
copylen);
}
//
// free the packet buffer if we have one
//
UHCD_FreeCommonBuffer(DeviceObject,
urbWork->LogicalAddressList[i].
PacketMemoryDescriptor);
}
(PUCHAR) currentVa += urbWork->LogicalAddressList[i].Length;
}
IoFreeMapRegisters(deviceExtension->AdapterObject,
urbWork->MapRegisterBase,
urbWork->NumberOfMapRegisters);
if (urbWork->Flags & UHCD_MAPPED_LOCKED_PAGES) {
PMDL mdl;
mdl = urb->HcdUrbCommonTransfer.TransferBufferMDL;
urbWork->Flags &= ~UHCD_MAPPED_LOCKED_PAGES;
if (mdl->MdlFlags &
(MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL)) {
// MmUnmapLockedPages(urbWork->SystemAddressForMdl, mdl);
}
}
}
UHCD_KdPrint((2, "'Transfer Completed\n"));
UHCD_KdPrint((2, "'Status = %x\n", usbdStatus));
UHCD_KdPrint((2, "'TransferBufferLength = %x\n",
urb->HcdUrbCommonTransfer.TransferBufferLength));
deviceExtension->Stats.BytesTransferred +=
urb->HcdUrbCommonTransfer.TransferBufferLength;
deviceExtension->IsoStats.IsoBytesTransferred +=
urb->HcdUrbCommonTransfer.TransferBufferLength;
//
// retire this transfer, give the TransferCancel routine a chance to
// mark it.
//
LOCK_ENDPOINT_ACTIVE_LIST(Endpoint, irql);
Endpoint->ActiveTransfers[Slot] = NULL;
// bump the current xfer count
Endpoint->CurrentXferId++;
UNLOCK_ENDPOINT_ACTIVE_LIST(Endpoint, irql);
//
// check if we have an error.
//
// if an error occurred on this transfer we need to
// retire this transfer along with any other linked
// transfers (urbs) associated with the same irp.
//
if (USBD_ERROR(usbdStatus)) {
if (USBD_HALTED(usbdStatus)) {
//
// error code indicates a condition that should halt
// the endpoint.
//
// check the endpoint state bit, if the endpoint
// is marked for NO_HALT then clear the halt bit
// and proceed to cancel this transfer.
if (Endpoint->EndpointFlags & EPFLAG_NO_HALT) {
//
// clear the halt bit on the usbdStatus code
//
usbdStatus = USBD_STATUS(usbdStatus) | USBD_STATUS_ERROR;
} else {
//
// mark the endpoint as halted, when the client
// sends a reset we'll start processing with the
// next queued transfer.
//
SET_EPFLAG(Endpoint, EPFLAG_HOST_HALTED);
LOGENTRY(LOG_MISC, 'Hhlt', Endpoint, 0, 0);
}
}
//
// complete any additional urbs associated with this irp.
//
LOCK_ENDPOINT_PENDING_LIST(Endpoint, irql, 'lck1');
//
// mark active urbs so that they get retired
//
for (i=0; i<Endpoint->MaxRequests; i++) {
urbtmp = Endpoint->ActiveTransfers[i];
if (urbtmp != NULL &&
HCD_AREA(urbtmp).HcdIrp == irp) {
urbtmp->HcdUrbCommonTransfer.Status =
UHCD_STATUS_PENDING_XXX;
//
// BUGBUG need a way to pass the error thru
//
TEST_TRAP();
}
}
//
// remove urbs associated with this Irp from the pending list
//
urbtmp = UHCD_RemoveQueuedUrbs(DeviceObject, urb, irp);
UNLOCK_ENDPOINT_PENDING_LIST(Endpoint, irql, 'ulk1');
//
// request an interrupt to process any active urbs that
// need canceling
//
UHCD_RequestInterrupt(DeviceObject, -2);
while (urbtmp) {
//
// complete all linked urbs with status
// USBD_CANCELED
//
TEST_TRAP();
urbtmp->HcdUrbCommonTransfer.Status = USBD_STATUS_CANCELED;
UHCD_ASSERT(irp == HCD_AREA(urbtmp).HcdIrp);
UHCD_CompleteIrp(DeviceObject,
irp,
STATUS_SUCCESS,
0,
urbtmp);
urbtmp = urb->HcdUrbCommonTransfer.UrbLink;
}
//
// complete the original request
//
urb->HcdUrbCommonTransfer.Status = usbdStatus;
UHCD_ASSERT(irp != NULL);
UHCD_CompleteIrp(DeviceObject,
irp,
STATUS_SUCCESS,
0,
urb);
if (!(Endpoint->EndpointFlags & EPFLAG_HOST_HALTED)) {
//
// if the endpoint is not halted then advance to
// the next queued transfer.
//
UHCD_EndpointWorker(DeviceObject, Endpoint);
}
goto UHCD_CompleteTransferDPC_Done;
}
//
// since the transfer completed at least one slot is free
// so call EndpointWorker to activate another transfer.
//
UHCD_EndpointWorker(DeviceObject, Endpoint);
//
// Now we complete the irp for the urb transfer request
// that just finished.
//
urb->HcdUrbCommonTransfer.Status = usbdStatus;
UHCD_ASSERT(irp != NULL);
UHCD_CompleteIrp(DeviceObject,
irp,
STATUS_SUCCESS,
0,
urb);
} // completed == TRUE
UHCD_CompleteTransferDPC_Done:
//UHCD_KdPrint((2, "'exit UHCD_CompleteTransferDPC\n"));
return;
}
IO_ALLOCATION_ACTION
UHCD_StartDmaTransfer(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
)
/*++
Routine Description:
Begin a DMA transfer -- this is the adapter control routine called
from IoAllocateAdapterChannel.
Arguments:
Return Value:
see IoAllocateAdapterChannel
--*/
{
PUHCD_ENDPOINT endpoint;
PHCD_URB urb = Context;
ULONG length, lengthMapped = 0;
PHYSICAL_ADDRESS logicalAddress;
PVOID currentVa;
PDEVICE_EXTENSION deviceExtension;
PHCD_EXTENSION urbWork;
KIRQL irql;
UHCD_KdPrint((2, "'enter UHCD_StartDmaTransfer\n"));
UHCD_KdPrint((2, "'TransferBufferMDL = 0x%x Length = 0x%x\n",
urb->HcdUrbCommonTransfer.TransferBufferMDL,
urb->HcdUrbCommonTransfer.TransferBufferLength));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
// allow dma operations now
KeAcquireSpinLock(&deviceExtension->HcDmaSpin, &irql);
UHCD_ASSERT(deviceExtension->HcDma >= 0);
deviceExtension->HcDma--;
LOGENTRY(LOG_MISC, '2DM-', 0, 0, 0);
KeReleaseSpinLock(&deviceExtension->HcDmaSpin, irql);
UHCD_ASSERT(urb->HcdUrbCommonTransfer.TransferBufferMDL != 0);
urbWork = HCD_AREA(urb).HcdExtension;
urbWork->MapRegisterBase = MapRegisterBase;
endpoint = HCD_AREA(urb).HcdEndpoint;
currentVa =
MmGetMdlVirtualAddress(urb->HcdUrbCommonTransfer.TransferBufferMDL);
length = urb->HcdUrbCommonTransfer.TransferBufferLength;
//
// keep calling IoMapTransfer until we get Logical Addresses for
// the entire clinet buffer
//
UHCD_ASSERT(!urbWork->NumberOfLogicalAddresses);
do {
// make sure we don't overrun the work area.
UHCD_ASSERT(urbWork->NumberOfLogicalAddresses <=
(urb->HcdUrbCommonTransfer.
TransferBufferLength / PAGE_SIZE + 1));
// first map the transfer buffer
logicalAddress =
IoMapTransfer(deviceExtension->AdapterObject,
urb->HcdUrbCommonTransfer.TransferBufferMDL,
MapRegisterBase,
currentVa,
&length,
DATA_DIRECTION_OUT(urb));
// save the Logical Address and length
UHCD_KdPrint((2, "'CurrentVa = 0x%x \n", currentVa));
lengthMapped += length;
(PUCHAR)currentVa += length;
UHCD_KdPrint((2, "'IoMapTransfer length = 0x%x log address = 0x%x\n",
length, logicalAddress.LowPart));
//
// update Urb work area with physical buffer addresses
// that the HC can use.
//
urbWork->LogicalAddressList[urbWork->NumberOfLogicalAddresses].
LogicalAddress = logicalAddress.LowPart;
urbWork->LogicalAddressList[urbWork->NumberOfLogicalAddresses].
Length = length;
length = urb->HcdUrbCommonTransfer.TransferBufferLength -
lengthMapped;
urbWork->NumberOfLogicalAddresses++;
} while (lengthMapped != urb->HcdUrbCommonTransfer.TransferBufferLength);
//
// Transfer is now ACTIVE.
//
urbWork->Flags |= UHCD_TRANSFER_MAPPED;
UHCD_BeginTransfer(DeviceObject,
endpoint,
urb,
urbWork->Slot);
UHCD_KdPrint((2, "'exit UHCD_StartDmaTransfer\n"));
return DeallocateObjectKeepRegisters;
}
VOID
UHCD_InitializeDmaTransfer(
IN PDEVICE_OBJECT DeviceObject,
IN PHCD_URB Urb,
IN PUHCD_ENDPOINT Endpoint,
IN LONG Slot,
IN UCHAR XferId
)
/*++
Routine Description:
Sets up a DMA transfer, this routine performs the mapping necessary
for the HC to access the physical memory asssociated with the transfer.
Arguments:
Return Value:
see IoAllocateAdapterChannel
--*/
{
PDEVICE_EXTENSION deviceExtension;
PVOID currentVa;
NTSTATUS ntStatus;
PHCD_EXTENSION urbWork;
UHCD_KdPrint((2, "'enter UHCD_InitializeDmaTransfer\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
urbWork = HCD_AREA(Urb).HcdExtension;
urbWork->Slot = (UCHAR) Slot;
urbWork->XferId = XferId;
if (Urb->HcdUrbCommonTransfer.TransferBufferLength) {
currentVa =
MmGetMdlVirtualAddress(
Urb->HcdUrbCommonTransfer.TransferBufferMDL);
// save the number of map registers in our work area
// since the transferBufferLength may get changed by the
// time the URB completes
urbWork->NumberOfMapRegisters =
ADDRESS_AND_SIZE_TO_SPAN_PAGES(
currentVa,
Urb->HcdUrbCommonTransfer.TransferBufferLength);
UHCD_KdPrint((2, "'NumberOfMapRegistersRequired = 0x%x\n",
urbWork->NumberOfMapRegisters));
KeFlushIoBuffers(Urb->HcdUrbCommonTransfer.TransferBufferMDL,
DATA_DIRECTION_IN(Urb),
TRUE);
// first we'll need to map the MDL for this transfer
LOGENTRY(LOG_MISC, 'AChn', Endpoint,
urbWork->NumberOfMapRegisters, Urb);
ntStatus =
IoAllocateAdapterChannel(deviceExtension->AdapterObject,
DeviceObject,
urbWork->NumberOfMapRegisters,
UHCD_StartDmaTransfer,
Urb);
if (!NT_SUCCESS(ntStatus)) {
//
// if error, mark the transfer complete with error
// now -- the TransferCompleteDPC routine will pick it up
// and complete it.
//
LOGENTRY(LOG_MISC, 'ChnE', Endpoint,
urbWork->NumberOfMapRegisters, ntStatus);
TEST_TRAP();
// BUGBUG do we need another error for this in usbdi.h?
URB_HEADER(Urb).Status = USBD_STATUS_REQUEST_FAILED;
//
// Trigger an interrupt to process the endpoint
//
UHCD_RequestInterrupt(DeviceObject, -2);
}
} else {
KIRQL irql;
//
// zero length transfer means no buffers to map.
// begin the transfer now.
//
// allow dma operations now
KeAcquireSpinLock(&deviceExtension->HcDmaSpin, &irql);
UHCD_ASSERT(deviceExtension->HcDma >= 0);
deviceExtension->HcDma--;
LOGENTRY(LOG_MISC, '1DM-', 0, 0, 0);
KeReleaseSpinLock(&deviceExtension->HcDmaSpin, irql);
//
// Transfer is now ACTIVE.
//
urbWork->Flags |= UHCD_TRANSFER_MAPPED;
UHCD_BeginTransfer(DeviceObject,
Endpoint,
Urb,
Slot);
}
UHCD_KdPrint((2, "'exit UHCD_InitializeDmaTransfer\n"));
}
VOID
UHCD_TransferCancel(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This function is called to cancel a transfer request that has been
processed by the startio routine and is in the pending list for an
endpoint.
The pending transfer queue for the endpoint looks like this:
------------- ------------- ------------- ------------- -------------
|Urb {irp 1}|->|Urb {irp 1}|->|Urb {irp 2}|->|Urb {irp 2}|->|Urb {irp x}|
------------- ------------- ------------- ------------- -------------
| |
remove --------------------------
So if {irp 2} is canceled the we would have to remove multiple urbs
from the chain. The cnacel routine does this, then completes the Irp
with STATUS_CANCELED.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Return Value:
NT status code.
--*/
{
PHCD_URB urb;
PUHCD_ENDPOINT endpoint;
ULONG i;
PDEVICE_OBJECT deviceObject;
KIRQL irql;
UHCD_KdPrint((2, "'enter UHCD_TransferCancel\n"));
{
PUSBD_EXTENSION de;
de = DeviceObject->DeviceExtension;
if (de->TrueDeviceExtension == de) {
deviceObject = DeviceObject;
} else {
de = de->TrueDeviceExtension;
deviceObject = de->HcdDeviceObject;
}
}
LOGENTRY(LOG_MISC, 'TCan', Irp, deviceObject, 0);
UHCD_ASSERT(Irp->Cancel == TRUE);
urb = (PHCD_URB) URB_FROM_IRP(Irp);
endpoint = HCD_AREA(urb).HcdEndpoint;
if (((PHCD_EXTENSION)HCD_AREA(urb).HcdExtension)->Flags
& UHCD_TRANSFER_ACTIVE) {
//
// This request is on the active list, so we just request
// that TransferComplete cancels it for us.
//
urb->HcdUrbCommonTransfer.Status = UHCD_STATUS_PENDING_XXX;
IoReleaseCancelSpinLock(Irp->CancelIrql);
return;
}
IoReleaseCancelSpinLock(Irp->CancelIrql);
LOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'lck2');
urb = UHCD_RemoveQueuedUrbs(deviceObject, urb, Irp);
UNLOCK_ENDPOINT_PENDING_LIST(endpoint, irql, 'ulk2');
//
// complete the urbs we removed from the pending list.
//
while (urb) {
urb->HcdUrbCommonTransfer.Status = USBD_STATUS_CANCELED;
//
// Note: request will not be completed by this routine if
// the cancel flag is set.
//
#if DBG
{
PHCD_EXTENSION urbWork;
urbWork = HCD_AREA(urb).HcdExtension;
UHCD_ASSERT((urbWork->Flags & UHCD_TRANSFER_ACTIVE) == 0);
}
#endif
LOGENTRY(LOG_MISC, 'pCan', Irp, urb, 0);
UHCD_CompleteIrp(deviceObject,
Irp,
STATUS_CANCELLED,
0,
urb);
urb = urb->HcdUrbCommonTransfer.UrbLink;
}
//
// NOTE: UHCD_CompleteIrp
// will not complete the request if the cancel flag is set
//
if (PENDING_URB_COUNT(Irp) == 0) {
//
// All urbs for this request were in the pending
// list so complete the irp now.
//
Irp->IoStatus.Status = STATUS_CANCELLED;
Irp->IoStatus.Information = 0;
USBD_CompleteRequest(Irp,
IO_NO_INCREMENT);
}
UHCD_KdPrint((2, "'exit UHCD_TransferCancel\n"));
}
VOID
UHCD_BeginTransfer(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint,
IN PHCD_URB Urb,
IN ULONG Slot
)
/*++
Routine Description:
This routine actually starts the transfer, it sets up the TDs
and puts them in the schedule.
Arguments:
Return Value:
NT status code.
--*/
{
PHCD_EXTENSION urbWork;
USBD_STATUS usbdStatus;
UHCD_KdPrint((2, "'enter UHCD_BeginTransfer\n"));
ASSERT_ENDPOINT(Endpoint);
UHCD_ASSERT(Endpoint == HCD_AREA(Urb).HcdEndpoint);
UHCD_ASSERT(Endpoint->Type<=USB_ENDPOINT_TYPE_INTERRUPT);
LOGENTRY(LOG_MISC, 'Bxfr', 0, Urb, 0);
urbWork = HCD_AREA(Urb).HcdExtension;
UHCD_ASSERT(urbWork->Flags & UHCD_TRANSFER_MAPPED);
if (Urb->HcdUrbCommonTransfer.TransferBufferLength) {
PMDL mdl;
mdl = Urb->HcdUrbCommonTransfer.TransferBufferMDL;
if (!(mdl->MdlFlags &
(MDL_MAPPED_TO_SYSTEM_VA | MDL_SOURCE_IS_NONPAGED_POOL))) {
urbWork->Flags |= UHCD_MAPPED_LOCKED_PAGES;
}
mdl->MdlFlags |= MDL_MAPPING_CAN_FAIL;
urbWork->SystemAddressForMdl = MmGetSystemAddressForMdl(mdl);
//
// CIMEXCIMEX - handle failure case
//
mdl->MdlFlags &= ~MDL_MAPPING_CAN_FAIL;
ASSERTMSG("UHCD: SystemAddressForMdl -- MDL mapping failed",
urbWork->SystemAddressForMdl != NULL);
} else {
urbWork->SystemAddressForMdl = NULL;
}
// BUGBUG
// verify that the request has not been canceled.
if (Endpoint->Type != USB_ENDPOINT_TYPE_ISOCHRONOUS &&
Endpoint->CurrentXferId != urbWork->XferId &&
Endpoint->MaxRequests >1) {
// set the defer flag
LOGENTRY(LOG_MISC, 'sDFR', Endpoint->MaxRequests, urbWork,
Endpoint->CurrentXferId);
urbWork->Flags |= UHCD_TRANSFER_DEFER;
}
usbdStatus =
(TransferDispatchTable[Endpoint->Type].InitializeTransfer)(
DeviceObject,
Endpoint,
Urb);
UHCD_KdPrint((2, "'New Transfer\n"));
UHCD_KdPrint((2, "'TransferBufferLength = 0x%x\n",
Urb->HcdUrbCommonTransfer.TransferBufferLength ));
UHCD_KdPrint((2, "'TransferFlags = 0x%x\n",
Urb->HcdUrbCommonTransfer.TransferFlags ));
UHCD_KdPrint((2, "'MappedSystemAddress = 0x%x\n",
urbWork->SystemAddressForMdl));
//
// start the transfer by linking the first TD to the
// QUEUE head
//
if (USBD_ERROR(usbdStatus)) {
//
// An error occurred setting up the transfer set an error in the urb
// so the next time the completion routine is called we'll complete
// the request.
//
TEST_TRAP();
URB_HEADER(Urb).Status = usbdStatus;
} else {
// BUGBUG
// this won't work if we have mutiple requests
// for a non-isoch endpoint
//
// this transfer id is now current
//
if (Endpoint->MaxRequests == 1) {
// maxRequests == 1 is the old codepath
UHCD_ASSERT(Endpoint->Type != USB_ENDPOINT_TYPE_ISOCHRONOUS);
UHCD_ASSERT(Endpoint->CurrentXferId == urbWork->XferId);
Endpoint->QueueHead->HW_VLink =
Endpoint->TDList->TDs[0].PhysicalAddress;
} else {
//bugbug leave iso alone for now
if (Endpoint->Type != USB_ENDPOINT_TYPE_ISOCHRONOUS) {
// if this transfer is now current we need to muck with
// the queue head, the TDs are already set up
if (Endpoint->CurrentXferId == urbWork->XferId) {
UHCD_ASSERT(!(urbWork->Flags & UHCD_TRANSFER_DEFER));
// update the endpoints TDList
// slot id corresponds to TD list
LOGENTRY(LOG_MISC, 'mkCU', Endpoint->CurrentXferId, urbWork->Slot,
Urb);
Endpoint->TDList =
Endpoint->SlotTDList[urbWork->Slot];
// fix up the data toggle now if we need to
if (!(urbWork->Flags & UHCD_TOGGLE_READY)) {
UHCD_FixupDataToggle(
DeviceObject,
Endpoint,
Urb);
}
Endpoint->QueueHead->HW_VLink =
Endpoint->TDList->TDs[0].PhysicalAddress;
} else {
// we cant start this transfer becuse it is not
// current yet -- just leave it in the slot for now
LOGENTRY(LOG_MISC, 'xRDY', Endpoint->CurrentXferId, urbWork->Slot,
Endpoint->SlotTDList[urbWork->Slot]);
UHCD_ASSERT(urbWork->Flags & UHCD_TRANSFER_INITIALIZED);
}
}
}
}
//
// Completion routine will now process this transfer.
//
urbWork->Flags |= UHCD_TRANSFER_ACTIVE;
UHCD_KdPrint((2, "'exit UHCD_BeginTransfer\n"));
}
PHCD_URB
UHCD_RemoveQueuedUrbs(
IN PDEVICE_OBJECT DeviceObject,
IN PHCD_URB Urb,
IN PIRP Irp
)
/*++
Routine Description:
Removes Urbs associated with a given irp from the pending queue.
NOTE: The endpoint must be held before calling this routine.
Arguments:
Return Value:
NT status code.
--*/
{
PHCD_URB urb, prevUrb, nextUrb, parentUrb = NULL;
PLIST_ENTRY listEntry, nextUrbLink, prevUrbLink;
PUHCD_ENDPOINT endpoint;
UHCD_KdPrint((2, "'enter UHCD_RemoveQueuedUrbs\n"));
endpoint = HCD_AREA(Urb).HcdEndpoint;
//
// we need to walk through the list of Urbs
// queued for this endpoint, any urbs associated
// with this irp must be removed.
//
ASSERT_ENDPOINT(endpoint);
listEntry = &endpoint->PendingTransferList;
if (!IsListEmpty(listEntry)) {
listEntry = endpoint->PendingTransferList.Flink;
}
while (listEntry != &endpoint->PendingTransferList) {
urb = (PHCD_URB) CONTAINING_RECORD(listEntry,
struct _URB_HCD_COMMON_TRANSFER,
hca.HcdListEntry);
if (HCD_AREA(urb).HcdIrp == Irp) {
parentUrb = urb;
//
// found the first Urb associated with
// this Irp
//
prevUrbLink = HCD_AREA(urb).HcdListEntry.Blink;
//
// Find the last Urb associated with this irp
// so we can remove the whole chain
//
while (urb->HcdUrbCommonTransfer.UrbLink != NULL) {
// yes, we are finished unlinking
urb = urb->HcdUrbCommonTransfer.UrbLink;
}
nextUrbLink = HCD_AREA(urb).HcdListEntry.Flink;
//
// we have found the group of URBs associated with this Irp
// it is now time to remove them.
//
if (nextUrbLink != &endpoint->PendingTransferList) {
// this is not the last one
nextUrb = (PHCD_URB) CONTAINING_RECORD(
nextUrbLink,
struct _URB_HCD_COMMON_TRANSFER,
hca.HcdListEntry);
UHCD_ASSERT(HCD_AREA(nextUrb).HcdIrp != Irp);
HCD_AREA(nextUrb).HcdListEntry.Blink =
prevUrbLink;
} else {
// this is the last one
endpoint->PendingTransferList.Blink =
prevUrbLink;
}
if (prevUrbLink != &endpoint->PendingTransferList) {
prevUrb = (PHCD_URB) CONTAINING_RECORD(
prevUrbLink,
struct _URB_HCD_COMMON_TRANSFER,
hca.HcdListEntry);
UHCD_ASSERT(HCD_AREA(prevUrb).HcdIrp != Irp);
HCD_AREA(prevUrb).HcdListEntry.Flink =
nextUrbLink;
} else {
endpoint->PendingTransferList.Flink =
nextUrbLink;
}
break;
}
listEntry = HCD_AREA(urb).HcdListEntry.Flink;
}
UHCD_KdPrint((2, "'exit UHCD_RemoveQueuedUrbs 0x%x\n", parentUrb));
return parentUrb;
}
VOID
UHCD_EndpointWorker(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
Main worker function for an endpoint.
Called at DPC level - either call the DMA routine or
the non DMA version as appropriate.
Arguments:
Return Value:
None.
--*/
{
if (Endpoint->EndpointFlags & EPFLAG_DBL_BUFFER) {
LOGENTRY(LOG_MISC, 'nDMA', 0, Endpoint, 0);
UHCD_EndpointNoDMAWorker(DeviceObject,
Endpoint);
} else {
LOGENTRY(LOG_MISC, 'yDMA', 0, Endpoint, 0);
UHCD_EndpointDMAWorker(DeviceObject,
Endpoint);
}
}
VOID
UHCD_EndpointDMAWorker(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
Main worker function for an endpoint.
Called at DPC level -- this routine activates any transfers for an
endpoint if possible by removing them from the pending queue and
initializing them.
Since EndpointWorker calls IoMapTransfer it cannot be reentered so
we set a flag to allow it to only run on one processor at a time.
Arguments:
Return Value:
None.
--*/
{
PLIST_ENTRY listEntry;
PHCD_URB urb;
LONG slot, i;
KIRQL irql;
PHCD_EXTENSION urbWork;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
UHCD_KdPrint((2, "'enter UHCD_EndpointWorker\n"));
// see if this endpoint is Halted, if so
// there is no need to check the DMA lock
if (Endpoint->EndpointFlags & EPFLAG_HOST_HALTED) {
//
// if the endpoint is halted then perform no
// processing.
//
#ifdef MAX_DEBUG
TEST_TRAP();
#endif
// allow DMA processing
LOGENTRY(LOG_MISC, 'Hhlt', 0, Endpoint, 0);
goto UHCD_EndpointWorker_Done;
}
UHCD_EndpointWorker_NextTransfer:
// attempt to take the DMA lock
KeAcquireSpinLock(&deviceExtension->HcDmaSpin, &irql);
deviceExtension->HcDma++;
LOGENTRY(LOG_MISC, '3DM+', 0, 0, 0);
if (deviceExtension->HcDma) {
// dma lock is already held, mark the
// endpoint so we process it later
SET_EPFLAG(Endpoint, EPFLAG_HAVE_WORK);
UHCD_ASSERT(deviceExtension->HcDma >= 0);
deviceExtension->HcDma--;
LOGENTRY(LOG_MISC, '4DM-', 0, 0, 0);
KeReleaseSpinLock(&deviceExtension->HcDmaSpin, irql);
LOGENTRY(LOG_MISC, 'Dbz1', 0, Endpoint, 0);
// busy, bail
goto UHCD_EndpointWorker_Done;
}
LOGENTRY(LOG_MISC, 'CHAN', 0, 0, 0);
// got the lock, process the transfer
CLR_EPFLAG(Endpoint, EPFLAG_HAVE_WORK);
KeReleaseSpinLock(&deviceExtension->HcDmaSpin, irql);
LOCK_ENDPOINT_ACTIVE_LIST(Endpoint, irql);
LOGENTRY(LOG_MISC, 'lka1', 0, Endpoint, 0);
//
// find an open slot
//
slot = -1;
for (i=0; i<Endpoint->MaxRequests; i++) {
if (Endpoint->ActiveTransfers[i] == NULL) {
//
// dequeue the next urb we want to start
//
LOCK_ENDPOINT_PENDING_LIST(Endpoint, irql, 'lck3');
if (IsListEmpty(&Endpoint->PendingTransferList)) {
//
// pending list is empty, reset the abort pending
// tranfers bit now -- this will allow a reset.
//
CLR_EPFLAG(Endpoint,
EPFLAG_ABORT_PENDING_TRANSFERS);
} else {
KIRQL cancelIrql;
PIRP pIrp;
IoAcquireCancelSpinLock(&cancelIrql);
pIrp
= HCD_AREA((PHCD_URB)
CONTAINING_RECORD(Endpoint->PendingTransferList.Flink,
struct _URB_HCD_COMMON_TRANSFER,
hca.HcdListEntry)).HcdIrp;
if (pIrp->Cancel) {
//
// Uh-oh, this IRP is being cancelled; we'll let the
// cancel routine finish the job. Leave it on the list.
//
IoReleaseCancelSpinLock(cancelIrql);
UNLOCK_ENDPOINT_PENDING_LIST(Endpoint, irql, 'ulk7');
continue;
}
listEntry = RemoveHeadList(&Endpoint->PendingTransferList);
IoReleaseCancelSpinLock(cancelIrql);
slot = i;
urb = (PHCD_URB) CONTAINING_RECORD(
listEntry,
struct _URB_HCD_COMMON_TRANSFER,
hca.HcdListEntry);
LOGENTRY(LOG_MISC, 'dqXF', urb, slot, 0);
URB_HEADER(urb).Status = UHCD_STATUS_PENDING_CURRENT;
}
UNLOCK_ENDPOINT_PENDING_LIST(Endpoint, irql, 'ulk3');
//
// found a transfer, start it
//
break;
}
}
if (slot != -1) {
//
// we have a transfer, start it.
//
UHCD_KdPrint((2, "'Starting Next Queued Transfer\n"));
//
// AbortPendingTransfers - indicates that all transfers in the
// pending queue should be canceled.
//
// If the abort flag is set then we just complete the queued
// transfer here.
//
if (Endpoint->EndpointFlags & EPFLAG_ABORT_PENDING_TRANSFERS) {
LOGENTRY(LOG_MISC, 'abrP', Endpoint, 0, urb);
// release the DMA lock now
KeAcquireSpinLock(&deviceExtension->HcDmaSpin, &irql);
UHCD_ASSERT(deviceExtension->HcDma >= 0);
deviceExtension->HcDma--;
LOGENTRY(LOG_MISC, '5DM-', 0, 0, 0);
KeReleaseSpinLock(&deviceExtension->HcDmaSpin, irql);
//
// release exclusive access.
//
UNLOCK_ENDPOINT_ACTIVE_LIST(Endpoint, irql);
LOGENTRY(LOG_MISC, 'uka1', 0, Endpoint, 0);
URB_HEADER(urb).Status = USBD_STATUS_CANCELED;
UHCD_CompleteIrp(DeviceObject,
HCD_AREA(urb).HcdIrp,
STATUS_CANCELLED,
0,
urb);
} else {
UCHAR xferId;
//
// Urb is now in the active list.
//
urbWork = HCD_AREA(urb).HcdExtension;
//
// initialize worker flags
//
// give the transfer a sequence number
xferId = Endpoint->NextXferId;
Endpoint->NextXferId++;
Endpoint->ActiveTransfers[slot] = urb;
//
// release exclusive access.
//
UNLOCK_ENDPOINT_ACTIVE_LIST(Endpoint, irql);
LOGENTRY(LOG_MISC, 'uka2', 0, Endpoint, 0);
UHCD_InitializeDmaTransfer(DeviceObject, urb, Endpoint, slot, xferId);
}
// next transfer will attempt to grab the DMA lock again
// if the lock was released by InitializeDMAtransfer
// then we will be able to handle another transfer
goto UHCD_EndpointWorker_NextTransfer;
} else {
//
// no free slots, release the DMA lock
//
KeAcquireSpinLock(&deviceExtension->HcDmaSpin, &irql);
UHCD_ASSERT(deviceExtension->HcDma >= 0);
deviceExtension->HcDma--;
LOGENTRY(LOG_MISC, '6DM-', 0, 0, 0);
KeReleaseSpinLock(&deviceExtension->HcDmaSpin, irql);
}
UNLOCK_ENDPOINT_ACTIVE_LIST(Endpoint, irql);
LOGENTRY(LOG_MISC, 'uka3', 0, Endpoint, 0);
UHCD_EndpointWorker_Done:
UHCD_KdPrint((2, "'exit UHCD_EndpointWorker\n"));
return;
}
VOID
UHCD_EndpointIdle(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
See if this endpoint has been idle for a while.
if so pull it from the schedule
Arguments:
Return Value:
None.
--*/
{
LARGE_INTEGER timeNow;
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
KIRQL irql;
#if DBG
ULONG slot;
#endif
if (Endpoint->EndpointFlags & EPFLAG_IDLE) {
// already idle
#if DBG
for (slot=0; slot<Endpoint->MaxRequests; slot++) {
UHCD_ASSERT(Endpoint->ActiveTransfers[slot] == NULL);
}
#endif
return;
}
if (Endpoint->Type != USB_ENDPOINT_TYPE_BULK) {
// we only idle bulk endpoints
return;
}
KeQuerySystemTime(&timeNow);
if (Endpoint->IdleTime == 0) {
LOGENTRY(LOG_MISC, 'Bid0', Endpoint->IdleTime, 0, Endpoint);
KeQuerySystemTime(&Endpoint->LastIdleTime);
Endpoint->IdleTime = 1;
}
LOGENTRY(LOG_MISC, 'Bid1', Endpoint->IdleTime, 0, Endpoint);
Endpoint->IdleTime +=
(LONG) (timeNow.QuadPart -
Endpoint->LastIdleTime.QuadPart);
Endpoint->LastIdleTime = timeNow;
LOGENTRY(LOG_MISC, 'Bid2', Endpoint->IdleTime, 0, Endpoint);
if (// 10 seconds in 100ns units
Endpoint->IdleTime > 100000000) {
KeAcquireSpinLock(&deviceExtension->HcScheduleSpin, &irql);
SET_EPFLAG(Endpoint, EPFLAG_IDLE);
LOGENTRY(LOG_MISC, 'Bid3', Endpoint->IdleTime, 0, Endpoint);
// pull the ep from the schedule
UHCD_KdPrint((0, "'Bulk Enpoint (%x) going idle\n", Endpoint));
// TEST_TRAP();
#if DBG
for (slot=0; slot<Endpoint->MaxRequests; slot++) {
UHCD_ASSERT(Endpoint->ActiveTransfers[slot] == NULL);
}
UHCD_ASSERT(IsListEmpty(&Endpoint->PendingTransferList));
#endif
UHCD_RemoveQueueHeadFromSchedule(DeviceObject,
Endpoint,
Endpoint->QueueHead,
FALSE);
KeReleaseSpinLock(&deviceExtension->HcScheduleSpin, irql);
}
}
VOID
UHCD_EndpointWakeup(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
Wakeup an idle endpoint
Arguments:
Return Value:
None.
--*/
{
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
KIRQL irql;
Endpoint->IdleTime = 0;
KeAcquireSpinLock(&deviceExtension->HcScheduleSpin, &irql);
if (Endpoint->EndpointFlags & EPFLAG_IDLE) {
UHCD_ASSERT(Endpoint->Type == USB_ENDPOINT_TYPE_BULK);
LOGENTRY(LOG_MISC, 'Ewak', Endpoint, 0, Endpoint->EndpointFlags);
UHCD_KdPrint((0, "'Bulk Enpoint (%x) wakeup\n", Endpoint));
// TEST_TRAP();
UHCD_InsertQueueHeadInSchedule(DeviceObject,
Endpoint,
Endpoint->QueueHead,
0); // no offset
CLR_EPFLAG(Endpoint, EPFLAG_IDLE);
}
KeReleaseSpinLock(&deviceExtension->HcScheduleSpin, irql);
}
#if DBG
VOID
UHCD_LockAccess(
IN PULONG c
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
UHCD_ASSERT(*c == 0);
(*c)++;
}
VOID
UHCD_UnLockAccess(
IN PULONG c
)
/*++
Routine Description:
Arguments:
Return Value:
None.
--*/
{
UHCD_ASSERT(*c>0);
(*c)--;
}
#endif