893 lines
25 KiB
C
893 lines
25 KiB
C
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
:ts=4
|
|
|
|
Module Name:
|
|
|
|
dblbuff.c
|
|
|
|
Abstract:
|
|
|
|
The module manages double buffered bulk transactions on USB.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
2-1-99 : created
|
|
|
|
--*/
|
|
#include "wdm.h"
|
|
#include "stdarg.h"
|
|
#include "stdio.h"
|
|
|
|
|
|
#include "usbdi.h"
|
|
#include "hcdi.h"
|
|
#include "uhcd.h"
|
|
|
|
#if DBG
|
|
extern ULONG UHCD_XferNoise;
|
|
#endif
|
|
|
|
VOID
|
|
UHCD_StartNoDMATransfer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
starts receiving data for the endpoint
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PUHCD_TD_LIST tDList;
|
|
ULONG i;
|
|
HW_DESCRIPTOR_PHYSICAL_ADDRESS physicalAddress;
|
|
|
|
if (Endpoint->EndpointFlags & EPFLAG_NODMA_ON) {
|
|
// already on, bail
|
|
LOGENTRY(LOG_MISC, 'dmaO', Endpoint, 0, 0);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
// current TD is the first TD
|
|
|
|
tDList = Endpoint->SlotTDList[0];
|
|
Endpoint->CurrentTDIdx[0] = 0;
|
|
|
|
physicalAddress = Endpoint->NoDMAPhysicalAddress;
|
|
|
|
LOGENTRY(LOG_MISC, 'dmG1', Endpoint, physicalAddress, tDList);
|
|
|
|
for (i=0; i < Endpoint->TDCount; i++) {
|
|
tDList->TDs[i].Endpoint = Endpoint->EndpointAddress;
|
|
tDList->TDs[i].Address = Endpoint->DeviceAddress;
|
|
tDList->TDs[i].HW_Link =
|
|
tDList->TDs[(i+1) % Endpoint->TDCount].PhysicalAddress;
|
|
UHCD_InitializeAsyncTD(Endpoint, &tDList->TDs[i]);
|
|
|
|
// for now take an interrupt every TD so we can keep up
|
|
tDList->TDs[i].InterruptOnComplete = 1;
|
|
tDList->TDs[i].PID = USB_IN_PID;
|
|
|
|
// set data toggle;
|
|
tDList->TDs[i].RetryToggle = Endpoint->DataToggle;
|
|
Endpoint->DataToggle ^=1;
|
|
|
|
// buffer length is packet size
|
|
tDList->TDs[i].MaxLength =
|
|
UHCD_SYSTEM_TO_USB_BUFFER_LENGTH(Endpoint->MaxPacketSize);
|
|
|
|
// point TDs at our NoDMA buffer;
|
|
tDList->TDs[i].PacketBuffer =
|
|
physicalAddress + 64*i;
|
|
|
|
tDList->TDs[i].ShortPacketDetect = 0;
|
|
|
|
UHCD_KdPrint((2, "'**TD for BULK DBLBUFF packet (%d)\n", i));
|
|
UHCD_Debug_DumpTD(&tDList->TDs[i]);
|
|
}
|
|
|
|
// no T bit the controller will stop at the first in-active TD
|
|
|
|
// mark the endpoint as started
|
|
SET_EPFLAG(Endpoint, EPFLAG_NODMA_ON);
|
|
LOGENTRY(LOG_MISC, 'dmG2', Endpoint, Endpoint->QueueHead, 0);
|
|
|
|
// fire up the transfer loop
|
|
// point QH at the first TD
|
|
|
|
Endpoint->QueueHead->HW_VLink =
|
|
Endpoint->TDList->TDs[0].PhysicalAddress;
|
|
}
|
|
|
|
|
|
VOID
|
|
UHCD_StopNoDMATransfer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
stops receiving data for the endpoint
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
CLR_EPFLAG(Endpoint, EPFLAG_NODMA_ON);
|
|
|
|
LOGENTRY(LOG_MISC, 'dmaX', Endpoint, Endpoint->QueueHead, 0);
|
|
UHCD_KdPrint((0, "'** stopping noDMA transfer\n"));
|
|
|
|
#if DBG
|
|
if (!IsListEmpty(&Endpoint->PendingTransferList)) {
|
|
UHCD_KdPrint((0, "'** stopping noDMA w/ pending transfers\n"));
|
|
}
|
|
if(Endpoint->ActiveTransfers[0] != NULL) {
|
|
UHCD_KdPrint((0, "'** stopping noDMA w/ active transfers\n"));
|
|
}
|
|
|
|
#endif
|
|
|
|
// fixup the TDs point the QH at the first TD
|
|
// and mark it inactive
|
|
|
|
// note that by the time the ep is actually closed more
|
|
// than one frame will have elapsed so it will be safe to
|
|
// remove the QH
|
|
|
|
// preserve the data toglge in case the ep gets another
|
|
// transfer that starts it again
|
|
|
|
Endpoint->DataToggle =
|
|
Endpoint->LastPacketDataToggle;
|
|
|
|
Endpoint->TDList->TDs[0].Active = 0;
|
|
Endpoint->QueueHead->HW_VLink =
|
|
Endpoint->TDList->TDs[0].PhysicalAddress;
|
|
|
|
// so that only startdmatransfer can activate it
|
|
SET_T_BIT(Endpoint->QueueHead->HW_VLink);
|
|
|
|
#if 0
|
|
// DEBUG ONLY
|
|
// see if we have any unclaimed data in the buffer
|
|
{
|
|
LONG i, cnt = 0;
|
|
PHW_TRANSFER_DESCRIPTOR transferDescriptor;
|
|
|
|
i = Endpoint->CurrentTDIdx[0];
|
|
for (;;) {
|
|
|
|
transferDescriptor = &Endpoint->TDList->TDs[i];
|
|
|
|
LOGENTRY(LOG_MISC, 'CKtd', i, transferDescriptor,
|
|
Endpoint->CurrentTDIdx[0]);
|
|
|
|
//
|
|
// Did this TD complete?
|
|
//
|
|
|
|
if (transferDescriptor->Active == 0) {
|
|
cnt++;
|
|
i = NEXT_TD(i, Endpoint);
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
// see if all TDs were processed
|
|
if (i == Endpoint->CurrentTDIdx[0]) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cnt) {
|
|
UHCD_KdPrint((0, "'** abort with %d unprocessed TDs\n", cnt));
|
|
TEST_TRAP();
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
#define RECYCLE_TD(t) \
|
|
(t)->ActualLength = 0;\
|
|
(t)->Active = 1;\
|
|
(t)->StatusField = 0; \
|
|
(t)->ErrorCounter = 3;
|
|
|
|
BOOLEAN
|
|
UHCD_ProcessNoDMATransfer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint,
|
|
IN PHCD_URB Urb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
True if current active transfer is complete
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN complete = FALSE;
|
|
USHORT i, thisTD;
|
|
USBD_STATUS usbdStatus = USBD_STATUS_SUCCESS;
|
|
PUCHAR tdBuffer;
|
|
PHW_TRANSFER_DESCRIPTOR transferDescriptor;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
ULONG remain, length;
|
|
PHCD_EXTENSION urbWork = HCD_AREA(Urb).HcdExtension;
|
|
BOOLEAN processed = FALSE;
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
// see if this transfer has been canceled
|
|
if (Urb->HcdUrbCommonTransfer.Status == UHCD_STATUS_PENDING_XXX) {
|
|
|
|
usbdStatus = USBD_STATUS_CANCELED;
|
|
LOGENTRY(LOG_MISC, 'TCAN', Endpoint, Urb, usbdStatus);
|
|
complete = TRUE;
|
|
goto UHCD_ProcessNoDMATransfer_Done;
|
|
}
|
|
|
|
// walk through the TD list starting from currentTDIdx
|
|
|
|
// stop as soon as:
|
|
// we fill the active client buffer OR
|
|
// we find a short packet OR
|
|
// we find an unprocessed TD
|
|
|
|
// Start at the last TD that had not completed
|
|
i = Endpoint->CurrentTDIdx[0];
|
|
for (;;) {
|
|
|
|
transferDescriptor = &Endpoint->TDList->TDs[i];
|
|
|
|
LOGENTRY(LOG_MISC, 'CKtd', i, transferDescriptor,
|
|
Endpoint->CurrentTDIdx[0]);
|
|
|
|
//
|
|
// Did this TD complete?
|
|
//
|
|
|
|
UHCD_KdPrint((2, "'checking DB TD %x\n", transferDescriptor));
|
|
UHCD_Debug_DumpTD(transferDescriptor);
|
|
|
|
if (transferDescriptor->Active == 0) {
|
|
|
|
LOG_TD('nACT', (PULONG) transferDescriptor);
|
|
processed = TRUE;
|
|
|
|
UHCD_KdPrint((2, "'TD %x completed\n", transferDescriptor));
|
|
UHCD_Debug_DumpTD(transferDescriptor);
|
|
|
|
Endpoint->LastPacketDataToggle = (UCHAR) transferDescriptor->RetryToggle;
|
|
|
|
//
|
|
// Yes, TD completed figure out what to do
|
|
//
|
|
|
|
if (transferDescriptor->StatusField != 0) {
|
|
// we got an error, map the status code and retire
|
|
// this transfer
|
|
|
|
// if appropriate the caller will halt transfers
|
|
// on the endpoint
|
|
|
|
complete = TRUE;
|
|
|
|
usbdStatus = UHCD_MapTDError(deviceExtension, transferDescriptor->StatusField,
|
|
UHCD_USB_TO_SYSTEM_BUFFER_LENGTH(transferDescriptor->ActualLength));
|
|
|
|
LOGENTRY(LOG_MISC, 'Stal', Endpoint,
|
|
transferDescriptor->StatusField, usbdStatus);
|
|
|
|
i = NEXT_TD(i, Endpoint);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// No Error, process the TDs data
|
|
//
|
|
|
|
// should be an IN
|
|
UHCD_ASSERT(transferDescriptor->PID == USB_IN_PID);
|
|
|
|
// see how much room is left
|
|
remain = Urb->HcdUrbCommonTransfer.TransferBufferLength -
|
|
urbWork->BytesTransferred;
|
|
|
|
length =
|
|
UHCD_USB_TO_SYSTEM_BUFFER_LENGTH(transferDescriptor->ActualLength);
|
|
|
|
tdBuffer = Endpoint->NoDMABuffer + 64*i;
|
|
|
|
if (length < remain) {
|
|
|
|
LOGENTRY(LOG_MISC, 'lsTD', 0, transferDescriptor, 0);
|
|
|
|
// copy the data to the client buffer
|
|
RtlCopyMemory((PUCHAR) urbWork->SystemAddressForMdl +
|
|
urbWork->BytesTransferred,
|
|
tdBuffer,
|
|
length);
|
|
|
|
urbWork->BytesTransferred += length;
|
|
|
|
i = NEXT_TD(i, Endpoint);
|
|
|
|
// check for short packet
|
|
// short packets cause the transfer to complete.
|
|
|
|
if (length <
|
|
Endpoint->MaxPacketSize) {
|
|
|
|
LOGENTRY(LOG_MISC, 'sHRT',
|
|
transferDescriptor,
|
|
transferDescriptor->ActualLength,
|
|
transferDescriptor->MaxLength);
|
|
|
|
complete = TRUE;
|
|
|
|
break;
|
|
}
|
|
|
|
} else if (length == remain) {
|
|
|
|
LOGENTRY(LOG_MISC, 'eqTD', 0, transferDescriptor, 0);
|
|
|
|
// this td just fills the client buffer
|
|
RtlCopyMemory((PUCHAR)urbWork->SystemAddressForMdl +
|
|
urbWork->BytesTransferred,
|
|
tdBuffer,
|
|
length);
|
|
urbWork->BytesTransferred += length;
|
|
|
|
complete = TRUE;
|
|
i = NEXT_TD(i, Endpoint);
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
TEST_TRAP();
|
|
// bugbug this is an odd case, not sure how to handle
|
|
// it yet.
|
|
|
|
// normally two transfers cannot span a single packet
|
|
|
|
|
|
// TD data is bigger than space left in client buffer
|
|
// copy what we can
|
|
RtlCopyMemory(urbWork->SystemAddressForMdl,
|
|
tdBuffer,
|
|
remain);
|
|
complete = TRUE;
|
|
|
|
// save the rest of the data for the next pass
|
|
|
|
break;
|
|
}
|
|
|
|
/* active == 0 */
|
|
} else {
|
|
// this TD still active, all done
|
|
LOGENTRY(LOG_MISC, 'acBK', Endpoint, transferDescriptor, 0);
|
|
break;
|
|
}
|
|
|
|
// see if all TDs were processed
|
|
if (i == Endpoint->CurrentTDIdx[0]) {
|
|
UHCD_ASSERT(processed == TRUE);
|
|
break;
|
|
}
|
|
|
|
} /* for ;; */
|
|
|
|
if (processed) {
|
|
// at least one TD completed
|
|
thisTD = Endpoint->CurrentTDIdx[0];
|
|
|
|
UHCD_ASSERT(Endpoint->TDList->TDs[thisTD].Active == 0);
|
|
RECYCLE_TD(&Endpoint->TDList->TDs[thisTD]);
|
|
thisTD = NEXT_TD(thisTD, Endpoint);
|
|
|
|
// set currentTDidx to where we left off
|
|
Endpoint->CurrentTDIdx[0] = i;
|
|
|
|
// recycle the TDs we processed
|
|
for(;;) {
|
|
if (thisTD == Endpoint->CurrentTDIdx[0]) {
|
|
break;
|
|
}
|
|
UHCD_ASSERT(Endpoint->TDList->TDs[thisTD].Active == 0);
|
|
RECYCLE_TD(&Endpoint->TDList->TDs[thisTD]);
|
|
thisTD = NEXT_TD(thisTD, Endpoint);
|
|
}
|
|
}
|
|
|
|
UHCD_KdPrint((2, "'check DB TD done\n"));
|
|
|
|
UHCD_ProcessNoDMATransfer_Done:
|
|
|
|
if (complete) {
|
|
Urb->HcdUrbCommonTransfer.Status = usbdStatus;
|
|
}
|
|
|
|
return complete;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_InitializeNoDMAEndpoint(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set up a No-DMA style (double buffered endpoint)
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
ULONG length;
|
|
|
|
UHCD_KdPrint((2, "'Init No DMA Endpoint\n"));
|
|
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
// we always have work
|
|
SET_EPFLAG(Endpoint, EPFLAG_HAVE_WORK);
|
|
|
|
//
|
|
// first we need a buffer to receive Data, and TDs to
|
|
// point in to it
|
|
//
|
|
|
|
// we should have the max TDs reserved for this endpoint
|
|
UHCD_ASSERT(Endpoint->TDCount == MAX_TDS_PER_ENDPOINT);
|
|
|
|
// length will be largest USB packet (64) * the MAX tds per
|
|
// endpoint (ends up being one page on x86)
|
|
|
|
length = 64 * MAX_TDS_PER_ENDPOINT;
|
|
|
|
Endpoint->NoDMABuffer =
|
|
UHCD_Alloc_NoDMA_Buffer(DeviceObject, Endpoint, length);
|
|
|
|
if (Endpoint->NoDMABuffer == NULL) {
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
UHCD_UnInitializeNoDMAEndpoint(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set up a No-DMA style (Dbl buffered endpoint)
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
LOGENTRY(LOG_MISC, 'unIN', Endpoint, 0, 0);
|
|
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
|
|
|
|
UHCD_ASSERT(!(Endpoint->EndpointFlags & EPFLAG_NODMA_ON));
|
|
|
|
if (Endpoint->NoDMABuffer) {
|
|
UHCD_Free_NoDMA_Buffer(DeviceObject, Endpoint->NoDMABuffer);
|
|
|
|
Endpoint->NoDMABuffer = NULL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
UHCD_EndpointNoDMA_Abort(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Aborts all active and/or pending transfers for a NoDMA endpoint
|
|
|
|
Called at DPC level
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
KIRQL irql;
|
|
//BOOLEAN done = FALSE;
|
|
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
UHCD_ASSERT(Endpoint->EndpointFlags &
|
|
(EPFLAG_ABORT_PENDING_TRANSFERS | EPFLAG_ABORT_ACTIVE_TRANSFERS));
|
|
|
|
if (Endpoint->EndpointFlags & EPFLAG_ABORT_ACTIVE_TRANSFERS) {
|
|
|
|
PHCD_URB urb;
|
|
|
|
urb = Endpoint->ActiveTransfers[0];
|
|
|
|
LOGENTRY(LOG_MISC, 'ABac', urb, 0, Endpoint);
|
|
|
|
if (urb) {
|
|
Endpoint->ActiveTransfers[0] = NULL;
|
|
|
|
URB_HEADER(urb).Status = USBD_STATUS_CANCELED;
|
|
UHCD_CompleteIrp(DeviceObject,
|
|
HCD_AREA(urb).HcdIrp,
|
|
STATUS_CANCELLED,
|
|
0,
|
|
urb);
|
|
|
|
|
|
CLR_EPFLAG(Endpoint,
|
|
EPFLAG_ABORT_ACTIVE_TRANSFERS);
|
|
}
|
|
}
|
|
|
|
if (Endpoint->EndpointFlags & EPFLAG_ABORT_PENDING_TRANSFERS) {
|
|
|
|
BOOLEAN pendingTransfers = TRUE;
|
|
|
|
LOGENTRY(LOG_MISC, 'ABpd', Endpoint, 0, 0);
|
|
|
|
// dequeue all of our pending requests an complete them with status
|
|
// canceled
|
|
|
|
while (pendingTransfers) {
|
|
|
|
PHCD_URB urb;
|
|
PLIST_ENTRY listEntry;
|
|
|
|
LOCK_ENDPOINT_PENDING_LIST(Endpoint, irql, 'lck3');
|
|
|
|
if (IsListEmpty(&Endpoint->PendingTransferList)) {
|
|
|
|
pendingTransfers = FALSE;
|
|
|
|
// clear the abort flag
|
|
CLR_EPFLAG(Endpoint,
|
|
EPFLAG_ABORT_PENDING_TRANSFERS);
|
|
|
|
UNLOCK_ENDPOINT_PENDING_LIST(Endpoint, irql, 'ulk3');
|
|
|
|
} else {
|
|
|
|
listEntry = RemoveHeadList(&Endpoint->PendingTransferList);
|
|
urb = (PHCD_URB) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _URB_HCD_COMMON_TRANSFER,
|
|
hca.HcdListEntry);
|
|
|
|
LOGENTRY(LOG_MISC, 'DQXF', urb, 0, 0);
|
|
|
|
UNLOCK_ENDPOINT_PENDING_LIST(Endpoint, irql, 'ulk3');
|
|
|
|
URB_HEADER(urb).Status = USBD_STATUS_CANCELED;
|
|
UHCD_CompleteIrp(DeviceObject,
|
|
HCD_AREA(urb).HcdIrp,
|
|
STATUS_CANCELLED,
|
|
0,
|
|
urb);
|
|
|
|
// complete this transfer
|
|
}
|
|
|
|
} /* while */
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UHCD_EndpointNoDMAWorker(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Main worker function for an endpoint that does not require DMA.
|
|
|
|
Note: We should never have transfers in the active list for one
|
|
of these endooints.
|
|
|
|
Called at DPC level
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG slot;
|
|
BOOLEAN complete;
|
|
KIRQL irql;
|
|
//BOOLEAN done = FALSE;
|
|
|
|
PDEVICE_EXTENSION deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//UHCD_KdPrint((2, "'enter UHCD_EndpointNoDMAWorker\n"));
|
|
|
|
//
|
|
// some asserts
|
|
//
|
|
|
|
// max request should always be one -- we will manage with just one
|
|
// set of TDs
|
|
UHCD_ASSERT(Endpoint->MaxRequests == 1);
|
|
slot = 0;
|
|
|
|
if (Endpoint->EndpointFlags &
|
|
(EPFLAG_ABORT_PENDING_TRANSFERS | EPFLAG_ABORT_ACTIVE_TRANSFERS)) {
|
|
// client has requested abort
|
|
UHCD_KdPrint((0, "'abort no DMA ep\n"));
|
|
UHCD_EndpointNoDMA_Abort(DeviceObject,
|
|
Endpoint);
|
|
|
|
goto UHCD_EndpointNoDMAWorker_Done;
|
|
}
|
|
|
|
do {
|
|
|
|
// check our pending request list
|
|
|
|
if (IsListEmpty(&Endpoint->PendingTransferList) &&
|
|
Endpoint->ActiveTransfers[slot] == NULL) {
|
|
// client list is empty,
|
|
// no more to do for now
|
|
LOGENTRY(LOG_MISC, 'ndMT', Endpoint, 0, 0);
|
|
break;
|
|
|
|
} else {
|
|
USBD_STATUS usbdStatus;
|
|
PIRP irp;
|
|
PHCD_URB urb;
|
|
PHCD_EXTENSION urbWork;
|
|
|
|
// no active transfer
|
|
// dequeue the next pending one for processing
|
|
|
|
// if the endpoint is not stalled, start up the DMA
|
|
// loop if not running on the first transfer
|
|
|
|
if (!(Endpoint->EndpointFlags & EPFLAG_HOST_HALTED)) {
|
|
LOGENTRY(LOG_MISC, 'ndST', Endpoint, 0, 0);
|
|
UHCD_StartNoDMATransfer(DeviceObject,
|
|
Endpoint);
|
|
}
|
|
|
|
if (Endpoint->ActiveTransfers[slot] == NULL) {
|
|
|
|
PLIST_ENTRY listEntry;
|
|
|
|
// dequeue the next transfer
|
|
LOCK_ENDPOINT_PENDING_LIST(Endpoint, irql, 'lck6');
|
|
|
|
UHCD_ASSERT(!IsListEmpty(&Endpoint->PendingTransferList));
|
|
|
|
|
|
listEntry = RemoveHeadList(&Endpoint->PendingTransferList);
|
|
urb = (PHCD_URB) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _URB_HCD_COMMON_TRANSFER,
|
|
hca.HcdListEntry);
|
|
|
|
LOGENTRY(LOG_MISC, 'ndDQ', Endpoint, urb, 0);
|
|
UHCD_ASSERT(urb);
|
|
|
|
UNLOCK_ENDPOINT_PENDING_LIST(Endpoint, irql, 'ulk6');
|
|
|
|
urbWork = HCD_AREA(urb).HcdExtension;
|
|
UHCD_ASSERT(urbWork);
|
|
|
|
//#if DBG
|
|
// if (UHCD_XferNoise) {
|
|
// UHCD_KdPrint((0, "'db xfer len req =%d\n", urb->HcdUrbCommonTransfer.TransferBufferLength));
|
|
// }
|
|
//#endif
|
|
|
|
// init the work area
|
|
// note: this area is zeroed
|
|
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;
|
|
}
|
|
|
|
urbWork->SystemAddressForMdl =
|
|
MmGetSystemAddressForMdl(mdl);
|
|
|
|
LOGENTRY(LOG_MISC, 'sMDL',
|
|
Endpoint, urb, urbWork->SystemAddressForMdl);
|
|
|
|
} else {
|
|
urbWork->SystemAddressForMdl = NULL;
|
|
TEST_TRAP();
|
|
}
|
|
urbWork->Flags |= UHCD_TRANSFER_ACTIVE;
|
|
|
|
Endpoint->ActiveTransfers[slot] = urb;
|
|
|
|
}
|
|
|
|
//
|
|
// so now we have a client transfer in the active slot
|
|
//
|
|
|
|
UHCD_ASSERT(Endpoint->ActiveTransfers[slot] != NULL);
|
|
urb = Endpoint->ActiveTransfers[slot];
|
|
irp = HCD_AREA(urb).HcdIrp;
|
|
UHCD_ASSERT(irp);
|
|
urbWork = HCD_AREA(urb).HcdExtension;
|
|
|
|
LOGENTRY(LOG_MISC, 'ndPR', Endpoint, urb, 0);
|
|
complete = UHCD_ProcessNoDMATransfer(DeviceObject,
|
|
Endpoint,
|
|
urb);
|
|
|
|
if (complete) {
|
|
|
|
usbdStatus = urb->HcdUrbCommonTransfer.Status;
|
|
|
|
// active transfer is complete
|
|
Endpoint->ActiveTransfers[slot] = NULL;
|
|
|
|
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);
|
|
|
|
// stop streaming from the endpoint,
|
|
// it should be NAKing anyway
|
|
UHCD_StopNoDMATransfer(DeviceObject,
|
|
Endpoint);
|
|
}
|
|
}
|
|
|
|
//
|
|
// complete the original request
|
|
//
|
|
|
|
UHCD_ASSERT(irp != NULL);
|
|
UHCD_CompleteIrp(DeviceObject,
|
|
irp,
|
|
STATUS_SUCCESS,
|
|
0,
|
|
urb);
|
|
|
|
if (Endpoint->EndpointFlags & EPFLAG_HOST_HALTED) {
|
|
|
|
//
|
|
// if the endpoint is halted then stop the stream now
|
|
//
|
|
|
|
UHCD_StopNoDMATransfer(DeviceObject,
|
|
Endpoint);
|
|
break;
|
|
}
|
|
} else {
|
|
// xfer completed with success
|
|
urb->HcdUrbCommonTransfer.Status = usbdStatus;
|
|
urb->HcdUrbCommonTransfer.TransferBufferLength =
|
|
urbWork->BytesTransferred;
|
|
|
|
//#if DBG
|
|
// if (UHCD_XferNoise) {
|
|
// UHCD_KdPrint((0, "'xfer len cpt =%d\n",
|
|
// urb->HcdUrbCommonTransfer.TransferBufferLength));
|
|
// }
|
|
//#endif
|
|
|
|
UHCD_ASSERT(irp != NULL);
|
|
UHCD_CompleteIrp(DeviceObject,
|
|
irp,
|
|
STATUS_SUCCESS,
|
|
0,
|
|
urb);
|
|
}
|
|
} /* xfer complete */
|
|
}
|
|
// cuurent transfer completed, grab the next one and process it
|
|
|
|
} while (complete);
|
|
|
|
UHCD_EndpointNoDMAWorker_Done:
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|