1881 lines
61 KiB
C
1881 lines
61 KiB
C
/*++
|
|
|
|
Copyright (c) 1995,1996 Microsoft Corporation
|
|
:ts=4
|
|
|
|
Module Name:
|
|
|
|
async.c
|
|
|
|
Abstract:
|
|
|
|
This module manages bulk, interrupt & control type
|
|
transactions on the USB.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
11-01-95 : created
|
|
|
|
--*/
|
|
|
|
#include "wdm.h"
|
|
#include "stdarg.h"
|
|
#include "stdio.h"
|
|
|
|
|
|
#include "usbdi.h"
|
|
#include "hcdi.h"
|
|
#include "uhcd.h"
|
|
|
|
// handle control transfers with this code
|
|
#define CONTROL 1
|
|
|
|
#define CONTROL_TRANSFER(ep) ((ep)->Type == USB_ENDPOINT_TYPE_CONTROL)
|
|
|
|
// true if we'll need more TDs than are available to setup this
|
|
// request
|
|
#define ASYNC_TRANSFER_OVERFLOW(needed, ep, xtra) (BOOLEAN)(needed > ep->TDCount - xtra)
|
|
|
|
#define UHCD_RESET_TD_LIST(ep) \
|
|
{ \
|
|
HW_DESCRIPTOR_PHYSICAL_ADDRESS td; \
|
|
td = (ep)->TDList->TDs[0].PhysicalAddress; \
|
|
SET_T_BIT(td); \
|
|
(ep)->QueueHead->HW_VLink = td; \
|
|
}
|
|
|
|
USBD_STATUS
|
|
UHCD_MapTDError(
|
|
PDEVICE_EXTENSION DeviceExtension,
|
|
ULONG Td_Status,
|
|
ULONG ActualLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Maps Error from TD.
|
|
|
|
1. STALL+BABBLE indicates that the td buffer was too small to
|
|
hold all the data ie buffer overrun.
|
|
2. STALL if onlt stall bit is set then we recieved a stall PID
|
|
3. CRC_TIMEOUT+STALL indicates the device is not responding
|
|
4. CRC_TIMEOUT with no data indicates no response
|
|
5. CRC_TIMEOUT with data indicates CRC error
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
usb status will be returned if transfer is complete.
|
|
|
|
--*/
|
|
{
|
|
USBD_STATUS status;
|
|
// Translate the TD status field to a USBD error code
|
|
|
|
if (Td_Status == 0x3f) {
|
|
// all bits on means software error
|
|
status = USBD_STATUS_NO_MEMORY;
|
|
UHCD_KdBreak((2, "'no mem\n"));
|
|
DeviceExtension->Stats.SWErrorCount++;
|
|
goto UHCD_MapTDError_Done;
|
|
}
|
|
|
|
if (Td_Status & TD_STATUS_BABBLE) {
|
|
UHCD_KdBreak((2, "'babble\n"));
|
|
DeviceExtension->FrameBabbleRecoverTD->Active = 1;
|
|
}
|
|
|
|
if (Td_Status == (TD_STATUS_STALL | TD_STATUS_BABBLE)) {
|
|
status = USBD_STATUS_BUFFER_OVERRUN;
|
|
DeviceExtension->Stats.BufferOverrunErrorCount++;
|
|
} else if (Td_Status == TD_STATUS_STALL) {
|
|
// if only the the stall bit is set in the TD then
|
|
// we have a stall pid
|
|
UHCD_KdBreak((2, "'stall 1\n"));
|
|
DeviceExtension->Stats.StallPidCount++;
|
|
status = USBD_STATUS_STALL_PID;
|
|
} else if (Td_Status == (TD_STATUS_CRC_TIMEOUT | TD_STATUS_STALL)) {
|
|
// stall and timeout bit indicates device not responding
|
|
UHCD_KdBreak((2, "'stall 2\n"));
|
|
DeviceExtension->Stats.TimeoutErrorCount++;
|
|
status = USBD_STATUS_DEV_NOT_RESPONDING;
|
|
} else if (Td_Status == TD_STATUS_CRC_TIMEOUT &&
|
|
ActualLength != 0) {
|
|
status = USBD_STATUS_CRC;
|
|
DeviceExtension->Stats.CrcErrorCount++;
|
|
} else if (Td_Status == TD_STATUS_CRC_TIMEOUT &&
|
|
ActualLength == 0) {
|
|
status = USBD_STATUS_DEV_NOT_RESPONDING;
|
|
DeviceExtension->Stats.TimeoutErrorCount++;
|
|
} else if (Td_Status == TD_STATUS_FIFO) {
|
|
status = USBD_STATUS_DATA_OVERRUN;
|
|
DeviceExtension->Stats.DataOverrunErrorCount++;
|
|
} else {
|
|
status = USBD_STATUS_INTERNAL_HC_ERROR;
|
|
DeviceExtension->Stats.InternalHcErrorCount++;
|
|
}
|
|
|
|
UHCD_MapTDError_Done:
|
|
|
|
LOGENTRY(LOG_MISC, 'MAPe', Td_Status, status, 0);
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// queue is busy if the T bit is not set in the HW link pointed to by the queue head
|
|
//
|
|
|
|
__inline VOID
|
|
UHCD_InitializeAsyncTD(
|
|
IN PUHCD_ENDPOINT Endpoint,
|
|
IN PHW_TRANSFER_DESCRIPTOR TransferDescriptor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize a TD for transfer use, initializes all
|
|
fields possibly changed by execution of the TD.
|
|
|
|
Arguments:
|
|
|
|
TransferDescriptor - TD to recycle
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
TransferDescriptor->PID = 0;
|
|
TransferDescriptor->Isochronous = 0;
|
|
TransferDescriptor->InterruptOnComplete = 0;
|
|
TransferDescriptor->Active = 1;
|
|
TransferDescriptor->ActualLength = 0;
|
|
TransferDescriptor->StatusField = 0;
|
|
// set based on field in endpoint
|
|
TransferDescriptor->LowSpeedControl =
|
|
(Endpoint->EndpointFlags & EPFLAG_LOWSPEED) ? 1 : 0;
|
|
TransferDescriptor->ReservedMBZ = 0;
|
|
// All bits on
|
|
TransferDescriptor->ErrorCounter = 3;
|
|
CLEAR_T_BIT(TransferDescriptor->HW_Link);
|
|
}
|
|
|
|
|
|
__inline
|
|
BOOLEAN
|
|
UHCD_QueueBusy(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint,
|
|
OUT PULONG QueueTD
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine if a particular queue head is 'busy' ie
|
|
still processing TDs
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE if busy, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN busy = FALSE;
|
|
ULONG i, active;
|
|
HW_DESCRIPTOR_PHYSICAL_ADDRESS currentLink, vLink;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
// slot = urbWork->Slot;
|
|
// UHCD_ASSERT(Endpoint->TDList == Endpoint->SlotTDList[slot]);
|
|
|
|
vLink = Endpoint->QueueHead->HW_VLink;
|
|
|
|
LOGENTRY(LOG_MISC, 'QBSY', vLink, 0, 0);
|
|
|
|
if (!(vLink & UHCD_CF_TERMINATE)) {
|
|
// T-bit not set see if the current TD has errored out
|
|
|
|
//
|
|
// locate the TD that the queue head is currently
|
|
// pointing to.
|
|
//
|
|
currentLink =
|
|
vLink & UHCD_DESCRIPTOR_PTR_MASK;
|
|
|
|
for (i=0; i<Endpoint->TDCount; i++) {
|
|
active = Endpoint->TDList->TDs[i].Active;
|
|
if (currentLink ==
|
|
(Endpoint->TDList->TDs[i].PhysicalAddress &
|
|
UHCD_DESCRIPTOR_PTR_MASK)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
LOGENTRY(LOG_MISC, 'Qlnk', Endpoint, currentLink, i);
|
|
LOGENTRY(LOG_MISC, 'Qlk2', Endpoint->QueueHead->HW_VLink, 0, 0);
|
|
UHCD_ASSERT(currentLink == (Endpoint->TDList->TDs[i].PhysicalAddress &
|
|
UHCD_DESCRIPTOR_PTR_MASK));
|
|
|
|
//
|
|
// Check the queue head, if it is busy then no processing
|
|
// will be performed at this time.
|
|
//
|
|
|
|
busy = TRUE;
|
|
|
|
if (!active) {
|
|
|
|
//
|
|
// Queue head points to an inactive TD we need to check
|
|
// for one of the follwing cases
|
|
//
|
|
// 1. Short packet detected on an IN with a B0 stepping
|
|
// version of the host controller.
|
|
//
|
|
// 2. The TD completed with an error.
|
|
//
|
|
// 3. Queue header update problem.
|
|
//
|
|
|
|
LOGENTRY(LOG_MISC, 'Qsts', deviceExtension->SteppingVersion,
|
|
UHCD_USB_TO_SYSTEM_BUFFER_LENGTH(Endpoint->TDList->TDs[i].ActualLength),
|
|
&Endpoint->TDList->TDs[i]);
|
|
|
|
// check error
|
|
if ((Endpoint->TDList->TDs[i].StatusField != 0) ||
|
|
// check short packet
|
|
(UHCD_USB_TO_SYSTEM_BUFFER_LENGTH(Endpoint->TDList->TDs[i].ActualLength) <
|
|
UHCD_USB_TO_SYSTEM_BUFFER_LENGTH(Endpoint->TDList->TDs[i].MaxLength)
|
|
&& Endpoint->TDList->TDs[i].PID == USB_IN_PID &&
|
|
deviceExtension->SteppingVersion >= UHCD_B0_STEP)) {
|
|
|
|
// (UHCD_USB_TO_SYSTEM_BUFFER_LENGTH(Endpoint->TDList->TDs[i].ActualLength) <
|
|
// Endpoint->MaxPacketSize && Endpoint->TDList->TDs[i].PID == USB_IN_PID &&
|
|
// deviceExtension->SteppingVersion >= UHCD_B0_STEP)) {
|
|
|
|
UHCD_ASSERT((Endpoint->QueueHead->HW_VLink &
|
|
UHCD_DESCRIPTOR_PTR_MASK) ==
|
|
(Endpoint->TDList->TDs[i].PhysicalAddress &
|
|
UHCD_DESCRIPTOR_PTR_MASK));
|
|
|
|
#if DBG
|
|
if (Endpoint->TDList->TDs[i].StatusField) {
|
|
LOGENTRY(LOG_MISC, 'Qerr', 0,
|
|
Endpoint->TDList->TDs[i].StatusField,
|
|
&Endpoint->TDList->TDs[i]);
|
|
|
|
// TEST_TRAP();
|
|
} else {
|
|
// TEST_TRAP();
|
|
|
|
LOGENTRY(LOG_MISC, 'Qsh2', Endpoint->MaxPacketSize,
|
|
UHCD_USB_TO_SYSTEM_BUFFER_LENGTH(Endpoint->TDList->TDs[i].ActualLength),
|
|
&Endpoint->TDList->TDs[i]);
|
|
}
|
|
#endif
|
|
//
|
|
// queue head is stopped
|
|
//
|
|
busy = FALSE;
|
|
} else {
|
|
HW_DESCRIPTOR_PHYSICAL_ADDRESS linkNow;
|
|
// the td we are point to is not active and
|
|
// has no error status, now we check for
|
|
// queue header update problem ie is the queue
|
|
// stuck?
|
|
//
|
|
linkNow = Endpoint->QueueHead->HW_VLink;
|
|
|
|
LOGENTRY(LOG_MISC, 'QHp?',
|
|
vLink,
|
|
linkNow,
|
|
Endpoint->TDList->TDs[i].HW_Link);
|
|
|
|
if (linkNow & UHCD_CF_TERMINATE) {
|
|
// pointing at a descriptor with the T bit,
|
|
// indicate the queue is not busy
|
|
busy = FALSE;
|
|
} else if ((linkNow & UHCD_DESCRIPTOR_PTR_MASK) ==
|
|
(vLink & UHCD_DESCRIPTOR_PTR_MASK)) {
|
|
|
|
// bump the current TD int the queue head to the next TD
|
|
// manually
|
|
|
|
LOGENTRY(LOG_MISC, 'QHp!',
|
|
vLink,
|
|
linkNow,
|
|
Endpoint->TDList->TDs[i].HW_Link);
|
|
|
|
UHCD_ASSERT((linkNow & UHCD_DESCRIPTOR_PTR_MASK)
|
|
== (Endpoint->TDList->TDs[i].PhysicalAddress & UHCD_DESCRIPTOR_PTR_MASK));
|
|
|
|
Endpoint->QueueHead->HW_VLink =
|
|
Endpoint->TDList->TDs[i].HW_Link;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (QueueTD) {
|
|
*QueueTD = i;
|
|
}
|
|
return busy;
|
|
}
|
|
|
|
|
|
__inline
|
|
BOOLEAN
|
|
UHCD_PrepareAsyncDataPacket(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PHW_TRANSFER_DESCRIPTOR TransferDescriptor,
|
|
IN PUHCD_ENDPOINT Endpoint,
|
|
IN PHCD_URB Urb,
|
|
IN BOOLEAN TransferOverflow,
|
|
IN BOOLEAN ZeroLengthTransfer,
|
|
IN BOOLEAN InitializeTransfer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Prepare a data packet for an async transfer.
|
|
|
|
Arguments:
|
|
|
|
TransferDescriptor -
|
|
|
|
Endpoint - endpoint associated with this transfer.
|
|
|
|
Urb - pointer to URB Request for this transfer.
|
|
|
|
Status - pointer to USBD status, will be filled in if transfer
|
|
is complete.
|
|
|
|
TransferOverflow - boolean flag indicates that we needed more
|
|
TDs than we have.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PHCD_EXTENSION urbWork = HCD_AREA(Urb).HcdExtension;
|
|
BOOLEAN status = FALSE;
|
|
BOOLEAN setToggle = TRUE;
|
|
USHORT packetSize;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// tracks the nth packet in this transfer
|
|
//
|
|
|
|
if (InitializeTransfer &&
|
|
// if this is init for multiple
|
|
// slot endpoint then don't
|
|
// mess with the data toggle
|
|
// until the xfer is active
|
|
|
|
Endpoint->MaxRequests > 1) {
|
|
setToggle = FALSE;
|
|
}
|
|
|
|
urbWork->PacketsProcessed++;
|
|
|
|
LOGENTRY(LOG_MISC, 'Pasy', urbWork->TransferOffset, urbWork, TransferDescriptor);
|
|
|
|
#if DBG
|
|
if (!ZeroLengthTransfer) {
|
|
UHCD_ASSERT(urbWork->TransferOffset < Urb->HcdUrbCommonTransfer.TransferBufferLength);
|
|
}
|
|
#endif
|
|
//
|
|
// possibly re-using this TD
|
|
//
|
|
|
|
UHCD_InitializeAsyncTD(Endpoint, TransferDescriptor);
|
|
|
|
if (setToggle) {
|
|
urbWork->Flags |= UHCD_TOGGLE_READY;
|
|
TransferDescriptor->RetryToggle = Endpoint->DataToggle;
|
|
Endpoint->DataToggle ^=1;
|
|
}
|
|
|
|
#if DBG
|
|
// Use this field to detect if we
|
|
// process the same TD twice
|
|
TransferDescriptor->Frame = 0;
|
|
#endif
|
|
|
|
TransferDescriptor->PID = DATA_DIRECTION_IN(Urb) ? USB_IN_PID : USB_OUT_PID;
|
|
|
|
if (DATA_DIRECTION_IN(Urb)) {
|
|
|
|
if (deviceExtension->SteppingVersion < UHCD_B0_STEP) {
|
|
|
|
//
|
|
// Direction is IN, we'll need an interrupt an T bit
|
|
// set on every packet to check for short packet.
|
|
//
|
|
// The B0 step does not have this problem
|
|
//
|
|
|
|
TransferDescriptor->InterruptOnComplete = 1;
|
|
SET_T_BIT(TransferDescriptor->HW_Link);
|
|
} else {
|
|
// TEST_TRAP();
|
|
TransferDescriptor->ShortPacketDetect = 1;
|
|
}
|
|
}
|
|
|
|
if (TransferOverflow) {
|
|
|
|
//
|
|
// if we need more descriptors than we
|
|
// have then so we'll set an interrupt on a middle packet to
|
|
// give us a chance to prepare more.
|
|
//
|
|
|
|
// lets try every 4th packet
|
|
if (urbWork->PacketsProcessed % 4 == 0) {
|
|
TransferDescriptor->InterruptOnComplete = 1;
|
|
}
|
|
}
|
|
|
|
// get the part of the buffer this TD represents
|
|
|
|
// The urbWork structure contains a list of logical addresses we got
|
|
// from IoMapTransfer -- these are the valid physical addresses we will
|
|
// give to the host controller.
|
|
|
|
//
|
|
// compute the packet size for this packet
|
|
//
|
|
|
|
if (urbWork->TransferOffset + Endpoint->MaxPacketSize
|
|
<= Urb->HcdUrbCommonTransfer.TransferBufferLength) {
|
|
packetSize = Endpoint->MaxPacketSize;
|
|
} else {
|
|
packetSize = (USHORT)(Urb->HcdUrbCommonTransfer.TransferBufferLength -
|
|
urbWork->TransferOffset);
|
|
}
|
|
|
|
if (ZeroLengthTransfer) {
|
|
TransferDescriptor->PacketBuffer =
|
|
urbWork->LogicalAddressList[0].LogicalAddress;
|
|
TransferDescriptor->MaxLength =
|
|
UHCD_SYSTEM_TO_USB_BUFFER_LENGTH(packetSize);
|
|
LOGENTRY(LOG_MISC, 'zpak', TransferDescriptor->PacketBuffer,
|
|
packetSize, 0);
|
|
status = TRUE;
|
|
} else if (TransferDescriptor->PacketBuffer =
|
|
UHCD_GetPacketBuffer(DeviceObject,
|
|
Endpoint,
|
|
Urb,
|
|
urbWork,
|
|
urbWork->TransferOffset,
|
|
packetSize)) {
|
|
urbWork->TransferOffset += packetSize;
|
|
|
|
LOGENTRY(LOG_MISC, 'Pbuf', TransferDescriptor->PacketBuffer, packetSize, urbWork->TransferOffset);
|
|
UHCD_ASSERT(urbWork->TransferOffset <= Urb->HcdUrbCommonTransfer.TransferBufferLength);
|
|
|
|
TransferDescriptor->MaxLength =
|
|
UHCD_SYSTEM_TO_USB_BUFFER_LENGTH(packetSize);
|
|
status = TRUE;
|
|
}
|
|
|
|
LOG_TD('daTD', TransferDescriptor);
|
|
|
|
UHCD_KdPrint((2, "'**TD for BULK/INT/CONTROL DATA packet\n"));
|
|
UHCD_Debug_DumpTD(TransferDescriptor);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
USBD_STATUS
|
|
UHCD_InitializeAsyncTransfer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint,
|
|
IN PHCD_URB Urb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the TDs needed by the hardware
|
|
to process this request, called from Transfer_StartIo.
|
|
The transfer list for this URB should be ready for
|
|
processing before returning from this routine.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to a device object.
|
|
|
|
Endpoint - Endpoint associated with this Urb.
|
|
|
|
Urb - pointer to URB Request Packet for this transfer.
|
|
|
|
Return Value:
|
|
|
|
Usbd status code.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
SHORT i, xtra, slot;
|
|
USBD_STATUS usbStatus = USBD_STATUS_SUCCESS;
|
|
PUHCD_TD_LIST tDList;
|
|
#if DBG
|
|
SHORT count;
|
|
#endif
|
|
USHORT dataDescriptorsNeeded;
|
|
PHCD_EXTENSION urbWork = HCD_AREA(Urb).HcdExtension;
|
|
PHW_QUEUE_HEAD queueHead;
|
|
|
|
UHCD_KdPrint((2, "'enter UHCD_InitializeAsyncTransfer\n"));
|
|
|
|
ASSERT_ENDPOINT(Endpoint);
|
|
UHCD_ASSERT(Endpoint == HCD_AREA(Urb).HcdEndpoint);
|
|
|
|
//
|
|
// if we have already been initialized or the queue
|
|
// is in use then just exit.
|
|
//
|
|
|
|
queueHead = Endpoint->QueueHead;
|
|
if ((urbWork->Flags & UHCD_TRANSFER_INITIALIZED ||
|
|
queueHead->Flags) &&
|
|
!(urbWork->Flags & UHCD_TRANSFER_DEFER)) {
|
|
goto UHCD_InitializeAsyncTransfer_Done;
|
|
}
|
|
|
|
//
|
|
// note that we have initialized
|
|
//
|
|
|
|
urbWork->Flags |= UHCD_TRANSFER_INITIALIZED;
|
|
queueHead->Flags |= UHCD_QUEUE_IN_USE;
|
|
|
|
LOGENTRY(LOG_MISC, 'Iasx', Endpoint, Urb, DeviceObject);
|
|
|
|
#ifdef CONTROL
|
|
if (CONTROL_TRANSFER(Endpoint)) {
|
|
LOGENTRY(LOG_MISC, 'Ctrl', Endpoint, Urb, DeviceObject);
|
|
// data toggle must be 0 for setup
|
|
// BUGBUG reset data toggle in ENDPOINT
|
|
Endpoint->DataToggle = 0;
|
|
}
|
|
#endif
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Set up the TDs we will need to do this transfer
|
|
//
|
|
|
|
// do some general init stuff first,
|
|
// TDs form a circular list
|
|
slot = urbWork->Slot;
|
|
tDList = Endpoint->SlotTDList[slot];
|
|
|
|
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]);
|
|
}
|
|
|
|
// current descriptor is first packet
|
|
Endpoint->CurrentTDIdx[slot] = 0;
|
|
|
|
// No tail descriptor yet
|
|
Endpoint->LastTDInTransferIdx[slot] = -1;
|
|
|
|
// if we have data to send or receive break it up into TDs,
|
|
// do this until we run out of TDs or we finish the buffer
|
|
|
|
// first, calculate how many data descriptors we will need
|
|
// based on the transfer buffer length
|
|
dataDescriptorsNeeded = (USHORT) (Urb->HcdUrbCommonTransfer.TransferBufferLength /
|
|
Endpoint->MaxPacketSize);
|
|
|
|
if ((ULONG)(dataDescriptorsNeeded)*Endpoint->MaxPacketSize < Urb->HcdUrbCommonTransfer.TransferBufferLength) {
|
|
dataDescriptorsNeeded++;
|
|
}
|
|
|
|
// Initialize some endpoint fields
|
|
|
|
urbWork->TransferOffset = 0;
|
|
urbWork->BytesTransferred = 0;
|
|
urbWork->PacketsProcessed = 0;
|
|
|
|
//points to first available TD
|
|
Endpoint->LastTDPreparedIdx[slot] = 0;
|
|
|
|
LOGENTRY(LOG_MISC, 'XfrB', Urb, dataDescriptorsNeeded, Urb->HcdUrbCommonTransfer.TransferBufferLength);
|
|
|
|
#ifdef CONTROL
|
|
if (CONTROL_TRANSFER(Endpoint)) {
|
|
// note that we'll need two extra TDs (for setup and status).
|
|
xtra = 2;
|
|
|
|
// Build a setup packet if necessary.
|
|
|
|
UHCD_ASSERT(Endpoint->MaxRequests == 1);
|
|
UHCD_PrepareSetupPacket(&tDList->TDs[Endpoint->LastTDPreparedIdx[slot]],
|
|
Endpoint,
|
|
Urb);
|
|
|
|
// point to next available TD
|
|
|
|
Endpoint->LastTDPreparedIdx[slot]++;
|
|
|
|
} else {
|
|
#endif
|
|
|
|
xtra = 0;
|
|
#ifdef CONTROL
|
|
}
|
|
#endif
|
|
|
|
LOGENTRY(LOG_MISC, 'LBuf', 0, 0, urbWork->LogicalAddressList[0].LogicalAddress);
|
|
|
|
//
|
|
// Begin preparing Data TDs, Endpoint->LastTDPreparedIdx points
|
|
// to the first available TD. Loop until we use up all the available
|
|
// TDs or we finish off the client buffer.
|
|
//
|
|
#if DBG
|
|
count = 0;
|
|
#endif
|
|
|
|
// remember the data toggle when we set up
|
|
urbWork->DataToggle = Endpoint->DataToggle;
|
|
|
|
while (Endpoint->LastTDPreparedIdx[slot]<Endpoint->TDCount) {
|
|
|
|
if (Urb->HcdUrbCommonTransfer.TransferBufferLength == 0 &&
|
|
!CONTROL_TRANSFER(Endpoint)) {
|
|
//
|
|
// special case the zero transfer
|
|
//
|
|
TEST_TRAP();
|
|
dataDescriptorsNeeded = 1;
|
|
if (!UHCD_PrepareAsyncDataPacket(DeviceObject,
|
|
&tDList->TDs[Endpoint->LastTDPreparedIdx[slot]],
|
|
Endpoint,
|
|
Urb,
|
|
// no overflow
|
|
FALSE,
|
|
TRUE,
|
|
// init
|
|
TRUE)) {
|
|
// an error occurred forming the packet
|
|
// bail out now
|
|
TEST_TRAP();
|
|
usbStatus = USBD_STATUS_NO_MEMORY;
|
|
goto UHCD_InitializeAsyncTransfer_Done;
|
|
}
|
|
Endpoint->LastTDPreparedIdx[slot]++;
|
|
break;
|
|
}
|
|
|
|
if (urbWork->TransferOffset < Urb->HcdUrbCommonTransfer.TransferBufferLength ) {
|
|
|
|
if (!UHCD_PrepareAsyncDataPacket(DeviceObject,
|
|
&tDList->TDs[Endpoint->LastTDPreparedIdx[slot]],
|
|
Endpoint,
|
|
Urb,
|
|
ASYNC_TRANSFER_OVERFLOW(dataDescriptorsNeeded, Endpoint, xtra),
|
|
FALSE,
|
|
// init
|
|
TRUE)) {
|
|
// an error occurred forming the packet
|
|
// bail out now
|
|
TEST_TRAP();
|
|
usbStatus = USBD_STATUS_NO_MEMORY;
|
|
goto UHCD_InitializeAsyncTransfer_Done;
|
|
}
|
|
|
|
Endpoint->LastTDPreparedIdx[slot]++;
|
|
#if DBG
|
|
count++;
|
|
#endif
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
LOGENTRY(LOG_MISC, 'dTDs', Endpoint, count, dataDescriptorsNeeded);
|
|
#endif
|
|
|
|
//
|
|
// We have more data than descriptors, save some state information
|
|
// so we can continue the process later.
|
|
//
|
|
|
|
if (ASYNC_TRANSFER_OVERFLOW(dataDescriptorsNeeded, Endpoint, xtra)) {
|
|
// set the T-bit for the last TD we were able to set up
|
|
// set the interrupt bit so we can prepare more TDs
|
|
|
|
LOGENTRY(LOG_MISC, 'Ovrf', Endpoint, dataDescriptorsNeeded, xtra);
|
|
|
|
// LastTDPreparedIdx points to the last TD in the set
|
|
Endpoint->LastTDPreparedIdx[slot] = Endpoint->TDCount-1;
|
|
Endpoint->TDList->TDs[Endpoint->LastTDPreparedIdx[slot]].InterruptOnComplete = 1;
|
|
SET_T_BIT(tDList->TDs[Endpoint->LastTDPreparedIdx[slot]].HW_Link);
|
|
} else {
|
|
// All the data fit, mark the tail so we know
|
|
// when we are done.
|
|
#ifdef CONTROL
|
|
if (CONTROL_TRANSFER(Endpoint)) {
|
|
Endpoint->LastTDPreparedIdx[slot] =
|
|
Endpoint->LastTDInTransferIdx[slot] = dataDescriptorsNeeded+1;
|
|
|
|
UHCD_PrepareStatusPacket(&tDList->TDs[Endpoint->LastTDInTransferIdx[slot]],
|
|
Endpoint,
|
|
Urb);
|
|
|
|
} else {
|
|
#endif
|
|
Endpoint->LastTDPreparedIdx[slot] =
|
|
Endpoint->LastTDInTransferIdx[slot] = dataDescriptorsNeeded-1;
|
|
|
|
#ifdef CONTROL
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Set the IOC bit for this and T bit for the last TD in the
|
|
// transfer
|
|
//
|
|
|
|
UHCD_KdPrint((2, "'IOC bit set for TD %x\n",
|
|
&tDList->TDs[Endpoint->LastTDInTransferIdx[slot]]));
|
|
|
|
tDList->TDs[Endpoint->LastTDInTransferIdx[slot]].InterruptOnComplete = 1;
|
|
SET_T_BIT(tDList->TDs[Endpoint->LastTDInTransferIdx[slot]].HW_Link);
|
|
}
|
|
|
|
//
|
|
// at this point...
|
|
// LastTDPreparedIdx points to the last TD we set up for this transfer
|
|
// LastTDInTransferIdx points to the last TD in the set or -1 if the transfer
|
|
// required more TDs than we had.
|
|
// CurrentTDIdx points to the first active TD in the set
|
|
//
|
|
|
|
UHCD_InitializeAsyncTransfer_Done:
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_InitializeAsyncTransfer\n"));
|
|
|
|
return usbStatus;
|
|
}
|
|
|
|
|
|
USBD_STATUS
|
|
UHCD_ProcessAsyncTransfer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint,
|
|
IN PHCD_URB Urb,
|
|
IN OUT PBOOLEAN Completed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks to see if an async transfer is complete.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to a device object.
|
|
|
|
Endpoint - endpoint to check for completed transfers.
|
|
|
|
Urb - ptr to URB to process.
|
|
|
|
Completed - TRUE if this transfer is complete, Status set to proper
|
|
error code.
|
|
|
|
Return Value:
|
|
|
|
usb status will be returned if transfer is complete.
|
|
--*/
|
|
{
|
|
BOOLEAN resumed = FALSE;
|
|
LONG i, queueTD, slot;
|
|
PHW_TRANSFER_DESCRIPTOR transferDescriptor;
|
|
BOOLEAN prepareMoreTDs = FALSE;
|
|
PHCD_EXTENSION urbWork = HCD_AREA(Urb).HcdExtension;
|
|
USBD_STATUS usbStatus = Urb->HcdUrbCommonTransfer.Status;
|
|
PHW_QUEUE_HEAD queueHead;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
STARTPROC("Pas+");
|
|
// UHCD_KdPrint((2, "'enter UHCD_ProcessAsyncTransfer\n"));
|
|
|
|
ASSERT_ENDPOINT(Endpoint);
|
|
*Completed = FALSE;
|
|
queueHead = Endpoint->QueueHead;
|
|
slot = urbWork->Slot;
|
|
|
|
// can only process one TD list at a time
|
|
LOGENTRY(LOG_MISC, 'Pasx', Endpoint->LastPacketDataToggle, slot, Urb);
|
|
LOGENTRY(LOG_MISC, 'Pas1', Endpoint->TDList, slot, Endpoint->SlotTDList[slot]);
|
|
LOGENTRY(LOG_MISC, 'PaEO', Endpoint, Endpoint->EndpointFlags, queueHead);
|
|
#if DBG
|
|
switch(Endpoint->Type) {
|
|
case USB_ENDPOINT_TYPE_CONTROL:
|
|
LOGENTRY(LOG_MISC, 'Pctr', 0, 0, queueHead);
|
|
break;
|
|
case USB_ENDPOINT_TYPE_BULK:
|
|
LOGENTRY(LOG_MISC, 'Pblk', 0, 0, queueHead);
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
deviceExtension=DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// if we marked the transfer canceling the go ahead
|
|
// and completed it now.
|
|
//
|
|
|
|
if (Urb->HcdUrbCommonTransfer.Status == UHCD_STATUS_PENDING_CANCELING) {
|
|
|
|
// set the data toggle based on the last packet completed
|
|
Endpoint->DataToggle =
|
|
Endpoint->LastPacketDataToggle ^1;
|
|
|
|
LOGENTRY(LOG_MISC, 'PxxC',
|
|
Endpoint->LastPacketDataToggle, Endpoint->DataToggle, Urb);
|
|
*Completed = TRUE;
|
|
usbStatus = USBD_STATUS_CANCELED;
|
|
|
|
goto UHCD_ProcessAsyncTransfer_done;
|
|
}
|
|
|
|
//
|
|
// see if the endpoint has been aborted, if so stop this transfer and
|
|
// wait unitl the next frame to complete it.
|
|
//
|
|
|
|
if ((Endpoint->EndpointFlags & EPFLAG_ABORT_ACTIVE_TRANSFERS) ||
|
|
Urb->HcdUrbCommonTransfer.Status == UHCD_STATUS_PENDING_XXX) {
|
|
LOGENTRY(LOG_MISC, 'Pxxx', 0, slot, Urb);
|
|
UHCD_RESET_TD_LIST(Endpoint);
|
|
UHCD_RequestInterrupt(DeviceObject, -2);
|
|
Urb->HcdUrbCommonTransfer.Status =
|
|
UHCD_STATUS_PENDING_CANCELING;
|
|
usbStatus = Urb->HcdUrbCommonTransfer.Status;
|
|
goto UHCD_ProcessAsyncTransfer_done;
|
|
}
|
|
|
|
//
|
|
// if queue is busy or endpoint stalled then no processing will be performed
|
|
// at this time
|
|
//
|
|
|
|
if (Endpoint->EndpointFlags & EPFLAG_HOST_HALTED) {
|
|
goto UHCD_ProcessAsyncTransfer_done;
|
|
}
|
|
|
|
// process an active transfer, only one can be current
|
|
|
|
UHCD_ASSERT(Endpoint->TDList == Endpoint->SlotTDList[slot]);
|
|
|
|
if (UHCD_QueueBusy(DeviceObject, Endpoint, &queueTD)) {
|
|
//#if 0
|
|
LOGENTRY(LOG_MISC, 'PRqh', Endpoint, queueTD, 0);
|
|
|
|
// **
|
|
// Code to process a queue head that the hardware is
|
|
// currently accessing.
|
|
// **
|
|
//
|
|
// Queue head is busy but we can still process and
|
|
// set up more TDs
|
|
//
|
|
|
|
// attempt some processing now...
|
|
//
|
|
// scan through the retired TDs between current TD and the TD
|
|
// that the queue head is pointing at, we should only encounter
|
|
// IN and OUT TDs that have completed successfully
|
|
|
|
i = Endpoint->CurrentTDIdx[slot];
|
|
// currently pointed to by the queue head
|
|
|
|
while (i != queueTD) {
|
|
|
|
LOGENTRY(LOG_MISC, 'QuTD', Endpoint->CurrentTDIdx[slot], i, queueTD);
|
|
|
|
if (i == Endpoint->LastTDInTransferIdx[slot]) {
|
|
// if this is the last TD let the
|
|
// process routine handle it.
|
|
break;
|
|
}
|
|
|
|
transferDescriptor = &Endpoint->TDList->TDs[i];
|
|
|
|
UHCD_ASSERT(transferDescriptor->Active == 0);
|
|
UHCD_ASSERT(transferDescriptor->StatusField == 0);
|
|
|
|
if (transferDescriptor->PID == USB_IN_PID ||
|
|
transferDescriptor->PID == USB_OUT_PID) {
|
|
urbWork->BytesTransferred += UHCD_USB_TO_SYSTEM_BUFFER_LENGTH(transferDescriptor->ActualLength);
|
|
LOGENTRY(LOG_MISC, 'reTD', transferDescriptor,
|
|
transferDescriptor->Frame, Urb->HcdUrbCommonTransfer.TransferBufferLength);
|
|
Endpoint->LastPacketDataToggle =
|
|
(UCHAR)transferDescriptor->RetryToggle;
|
|
UHCD_ASSERT(transferDescriptor->Frame == 0);
|
|
#if DBG
|
|
transferDescriptor->Frame = 1;
|
|
#endif
|
|
UHCD_ASSERT(urbWork->BytesTransferred <=
|
|
Urb->HcdUrbCommonTransfer.TransferBufferLength);
|
|
}
|
|
|
|
i = NEXT_TD(i, Endpoint);
|
|
}
|
|
|
|
//Endpoint->CurrentTDIdx = queueTD;
|
|
Endpoint->CurrentTDIdx[slot] = (SHORT)i;
|
|
//#endif
|
|
if (Endpoint->LastTDInTransferIdx[slot] == -1) {
|
|
|
|
//
|
|
// This was an OVERFLOW transfer
|
|
// ie we didn't have enough TDs to satisfy the request.
|
|
//
|
|
usbStatus = UHCD_PrepareMoreAsyncTDs(DeviceObject,
|
|
Endpoint,
|
|
Urb,
|
|
TRUE);
|
|
if (USBD_ERROR(usbStatus)) {
|
|
//
|
|
// if we get an error preparing more TDs
|
|
// then we'll need to abort the transfer
|
|
//
|
|
TEST_TRAP();
|
|
UHCD_RESET_TD_LIST(Endpoint);
|
|
UHCD_RequestInterrupt(DeviceObject, -2);
|
|
Urb->HcdUrbCommonTransfer.Status =
|
|
UHCD_STATUS_PENDING_CANCELING;
|
|
}
|
|
}
|
|
|
|
goto UHCD_ProcessAsyncTransfer_done;
|
|
}
|
|
|
|
LOGENTRY(LOG_MISC, 'Pasx', Endpoint, Endpoint->CurrentTDIdx[slot], DeviceObject);
|
|
|
|
//
|
|
// If we get here the queue is not busy.
|
|
//
|
|
//
|
|
// Scan our active TDs starting with 'CurrentTDIdx' stop as soon as we find
|
|
// a TD that is still active or we find that the STATUS TD is complete
|
|
//
|
|
//
|
|
|
|
// Start at the last TD that had not completed
|
|
i = Endpoint->CurrentTDIdx[slot];
|
|
for (;;) {
|
|
|
|
|
|
|
|
//
|
|
// This loop terminates on the following conditions:
|
|
// 1. An active TD is encountered.
|
|
// 2. The last TD in the transfer is processed.
|
|
// 3. An non-zero status value is encountered on
|
|
// a completed TD.
|
|
// 4. The last TD that had been set up for the
|
|
// transfer is complete.
|
|
|
|
transferDescriptor = &Endpoint->TDList->TDs[i];
|
|
|
|
LOGENTRY(LOG_MISC, 'ckTD', i, transferDescriptor,
|
|
Endpoint->CurrentTDIdx[slot]);
|
|
|
|
//
|
|
// Did this TD complete?
|
|
//
|
|
|
|
UHCD_KdPrint((2, "'checking TD %x\n", transferDescriptor));
|
|
|
|
if (transferDescriptor->Active == 0) {
|
|
|
|
LOG_TD('acTD', (PULONG) transferDescriptor);
|
|
|
|
UHCD_KdPrint((2, "'TD %x completed\n", transferDescriptor));
|
|
UHCD_Debug_DumpTD(transferDescriptor);
|
|
|
|
Endpoint->LastPacketDataToggle = (UCHAR)transferDescriptor->RetryToggle;
|
|
LOGENTRY(LOG_MISC, 'LPdt', Endpoint,
|
|
Endpoint->LastPacketDataToggle, 0);
|
|
|
|
//
|
|
// Yes, TD completed figure out what to do
|
|
//
|
|
|
|
if (transferDescriptor->StatusField != 0) {
|
|
// we got an error, map the status code and retire
|
|
// this transfer
|
|
*Completed = TRUE;
|
|
usbStatus = UHCD_MapTDError(deviceExtension, transferDescriptor->StatusField,
|
|
UHCD_USB_TO_SYSTEM_BUFFER_LENGTH(transferDescriptor->ActualLength));
|
|
|
|
// Point the queue head at the first TD with the T-Bit set.
|
|
// NOTE: we won't get here if the TD is marked with status NAK
|
|
// because the active bit is still set.
|
|
|
|
UHCD_ASSERT_QSTOPPED(DeviceObject, Endpoint, NULL);
|
|
UHCD_RESET_TD_LIST(Endpoint);
|
|
|
|
LOGENTRY(LOG_MISC, 'Stal', Endpoint, transferDescriptor->StatusField, usbStatus);
|
|
|
|
UHCD_KdBreak((2, "'Stall\n"));
|
|
break;
|
|
}
|
|
|
|
//
|
|
// No Error, update bytes transferred for this
|
|
// packet if it was data.
|
|
//
|
|
if (transferDescriptor->PID == USB_IN_PID ||
|
|
transferDescriptor->PID == USB_OUT_PID) {
|
|
urbWork->BytesTransferred += UHCD_USB_TO_SYSTEM_BUFFER_LENGTH(transferDescriptor->ActualLength);
|
|
UHCD_ASSERT(transferDescriptor->Frame == 0);
|
|
#if DBG
|
|
transferDescriptor->Frame = 1;
|
|
#endif
|
|
UHCD_ASSERT(urbWork->BytesTransferred <=
|
|
Urb->HcdUrbCommonTransfer.TransferBufferLength);
|
|
}
|
|
|
|
//
|
|
// Check to see if we are done with the transfer.
|
|
//
|
|
|
|
if (i == Endpoint->LastTDInTransferIdx[slot]) {
|
|
|
|
//
|
|
// This is the last TD in the transfer, complete now.
|
|
//
|
|
|
|
// point the queue head at the first TD with the T-Bit set
|
|
UHCD_ASSERT_QSTOPPED(DeviceObject, Endpoint, NULL);
|
|
UHCD_RESET_TD_LIST(Endpoint);
|
|
*Completed = TRUE;
|
|
usbStatus = USBD_STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Short packets cause the transfer to complete.
|
|
//
|
|
|
|
if (transferDescriptor->ActualLength != transferDescriptor->MaxLength) {
|
|
|
|
//
|
|
// We have a short transfer.
|
|
//
|
|
|
|
LOGENTRY(LOG_MISC, 'Shrt',
|
|
transferDescriptor,
|
|
transferDescriptor->ActualLength,
|
|
transferDescriptor->MaxLength);
|
|
|
|
#ifdef CONTROL
|
|
#if DBG
|
|
//
|
|
// test handling short transfer_ok with control transfer
|
|
//
|
|
if (CONTROL_TRANSFER(Endpoint) &&
|
|
!(Urb->HcdUrbCommonTransfer.TransferFlags & USBD_SHORT_TRANSFER_OK)) {
|
|
TEST_TRAP();
|
|
}
|
|
#endif //DBG
|
|
if (CONTROL_TRANSFER(Endpoint) &&
|
|
Urb->HcdUrbCommonTransfer.TransferFlags & USBD_SHORT_TRANSFER_OK) {
|
|
|
|
//
|
|
// If this is a control transfer then we need to advance
|
|
// to the status phase
|
|
//
|
|
|
|
if (Endpoint->LastTDInTransferIdx[slot] == -1) {
|
|
// status phase has not been set up yet
|
|
// do it now
|
|
|
|
Endpoint->LastTDInTransferIdx[slot] = (SHORT) NEXT_TD(i, Endpoint);
|
|
|
|
UHCD_PrepareStatusPacket(&Endpoint->TDList->TDs[Endpoint->LastTDInTransferIdx[slot]],
|
|
Endpoint,
|
|
Urb);
|
|
|
|
SET_T_BIT(Endpoint->TDList->TDs[Endpoint->LastTDInTransferIdx[slot]].HW_Link);
|
|
}
|
|
|
|
// make the status p hase the current TD
|
|
i = Endpoint->CurrentTDIdx[slot] =
|
|
Endpoint->LastTDInTransferIdx[slot];
|
|
|
|
// just point the queue head at the status packet
|
|
// and go!
|
|
queueHead->HW_VLink =
|
|
Endpoint->TDList->TDs[Endpoint->LastTDInTransferIdx[slot]].PhysicalAddress;
|
|
|
|
LOGENTRY(LOG_MISC, 'ShSt',
|
|
queueHead,
|
|
&Endpoint->TDList->TDs[Endpoint->LastTDInTransferIdx[slot]],
|
|
0);
|
|
// Go to the top of the loop in case it completed,
|
|
// we'll still get the interrupt but we may be able
|
|
// to finish the transfer sooner.
|
|
|
|
// note that we resumed the queue head
|
|
// so we don't resume it agian.
|
|
resumed = TRUE;
|
|
continue;
|
|
} else {
|
|
#endif
|
|
//
|
|
// Short packet and not a control transfer or control transfer
|
|
// and short transfer is to be treated as an error, just complete
|
|
// the transfer now.
|
|
//
|
|
|
|
UHCD_ASSERT_QSTOPPED(DeviceObject, Endpoint, NULL);
|
|
UHCD_RESET_TD_LIST(Endpoint);
|
|
// adjust data toggle
|
|
Endpoint->DataToggle =
|
|
(UCHAR)transferDescriptor->RetryToggle;
|
|
Endpoint->DataToggle ^=1;
|
|
|
|
|
|
*Completed = TRUE;
|
|
//check the SHORT_TRANSFER_OK flag
|
|
if (Urb->HcdUrbCommonTransfer.TransferFlags & USBD_SHORT_TRANSFER_OK) {
|
|
usbStatus = USBD_STATUS_SUCCESS;
|
|
} else {
|
|
TEST_TRAP();
|
|
usbStatus = USBD_STATUS_ERROR_SHORT_TRANSFER;
|
|
}
|
|
break;
|
|
#ifdef CONTROL
|
|
}
|
|
#endif
|
|
//
|
|
// end of short packet detection.
|
|
//
|
|
}
|
|
|
|
//
|
|
// Done with the TD but not with the transfer, advance our
|
|
// index to the current TD.
|
|
//
|
|
|
|
Endpoint->CurrentTDIdx[slot] = NEXT_TD(Endpoint->CurrentTDIdx[slot], Endpoint);
|
|
|
|
//
|
|
// see if we need to prepare more TDs
|
|
//
|
|
|
|
LOGENTRY(LOG_MISC, 'chkM', i, Endpoint->LastTDPreparedIdx[slot],
|
|
Endpoint->LastTDInTransferIdx[slot]);
|
|
|
|
if (i == Endpoint->LastTDPreparedIdx[slot] &&
|
|
Endpoint->LastTDInTransferIdx[slot] == -1) {
|
|
//
|
|
// This was the last TD prepared for an OVERLOW transfer
|
|
// ie we didn't have enough TDs to satifsy the request.
|
|
//
|
|
// This is when we prepare more TDs.
|
|
//
|
|
usbStatus = UHCD_PrepareMoreAsyncTDs(DeviceObject,
|
|
Endpoint,
|
|
Urb,
|
|
FALSE);
|
|
if (USBD_ERROR(usbStatus)) {
|
|
// an error occurred preparing more TDs
|
|
// terminate the transfer now
|
|
TEST_TRAP();
|
|
// assert that the T-BIT is still set in the QH.
|
|
UHCD_ASSERT_QSTOPPED(DeviceObject, Endpoint, NULL);
|
|
UHCD_RESET_TD_LIST(Endpoint);
|
|
*Completed = TRUE;
|
|
goto UHCD_ProcessAsyncTransfer_done;
|
|
}
|
|
|
|
}
|
|
// end active == 0
|
|
} else {
|
|
|
|
//
|
|
// This TD is still active, stop processing TDs now.
|
|
//
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// advance to the next TD in the list
|
|
//
|
|
|
|
i = NEXT_TD(i, Endpoint);
|
|
|
|
} // end for (;;)
|
|
|
|
if (!*Completed && !resumed) {
|
|
// NOTE that if the QH is busy we
|
|
// should not get here
|
|
|
|
// make sure the queue head is still stopped
|
|
UHCD_ASSERT_QSTOPPED(DeviceObject, Endpoint, NULL);
|
|
|
|
LOGENTRY(LOG_MISC, 'rsum', Endpoint, Endpoint->CurrentTDIdx[slot],
|
|
&Endpoint->TDList->TDs[Endpoint->CurrentTDIdx[slot]]);
|
|
|
|
//
|
|
// We get here if the transfer has not completed yet but the
|
|
// queue head is stopped, this is caused by one of the following
|
|
// conditions:
|
|
//
|
|
// 1. The last TD that could be set up for a transfer completed
|
|
// and we had to set up more.
|
|
//
|
|
// 2. An IN Transfer completed for BULK or INT and it was not a
|
|
// short packet and it was not the last TD in the transfer and
|
|
// short packet detection is not enabled on the HC.
|
|
//
|
|
// In any case we'll need to resume the Queue head, we point
|
|
// the queue head at the current TD and go.
|
|
|
|
queueHead->HW_VLink =
|
|
Endpoint->TDList->TDs[Endpoint->CurrentTDIdx[slot]].PhysicalAddress;
|
|
|
|
}
|
|
|
|
UHCD_ProcessAsyncTransfer_done:
|
|
|
|
if (*Completed) {
|
|
|
|
queueHead->Flags &= ~UHCD_QUEUE_IN_USE;
|
|
|
|
// note that we don't activate the next transfer if we
|
|
// are in an abort scenario
|
|
if (Endpoint->MaxRequests > 1) {
|
|
|
|
//
|
|
// transfer completed, so queue is no longer in use
|
|
// try to start the next transfer here.
|
|
//
|
|
|
|
UHCD_ASSERT_QSTOPPED(DeviceObject, Endpoint, NULL);
|
|
|
|
//
|
|
// BUGBUG if MaxRequets is > 2 then we'll need some kind of sequence
|
|
// number so we can start the transfers in the right order.
|
|
// Since we have only two now the one that we are not completing
|
|
// is the next one to start.
|
|
//
|
|
|
|
// get the next ready transfer based on seq number
|
|
for (i=0; i< Endpoint->MaxRequests; i++) {
|
|
PHCD_URB localUrb;
|
|
PHCD_EXTENSION localWork;
|
|
UCHAR nextXfer = Endpoint->CurrentXferId+1;
|
|
|
|
localUrb = Endpoint->ActiveTransfers[i];
|
|
|
|
if (localUrb) {
|
|
localWork = HCD_AREA(localUrb).HcdExtension;
|
|
|
|
LOGENTRY(LOG_MISC, 'Cnxt', Endpoint->CurrentXferId, nextXfer,
|
|
localWork->XferId);
|
|
|
|
if (nextXfer == localWork->XferId) {
|
|
// this is the next transfer
|
|
LOGENTRY(LOG_MISC, 'NXTx', localUrb, nextXfer, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (i == Endpoint->MaxRequests) {
|
|
// no xfers available
|
|
LOGENTRY(LOG_MISC, 'NoXF', 0, Endpoint->CurrentXferId, i);
|
|
|
|
} else {
|
|
PHCD_EXTENSION localWork;
|
|
PHCD_URB localUrb;
|
|
|
|
//
|
|
// This will start the next active transfer
|
|
// for the endpoint.
|
|
//
|
|
|
|
UHCD_ASSERT(Endpoint->ActiveTransfers[i]);
|
|
|
|
localUrb = Endpoint->ActiveTransfers[i];
|
|
localWork = HCD_AREA(localUrb).HcdExtension;
|
|
|
|
UHCD_ASSERT(i == localWork->Slot);
|
|
#if DBG
|
|
if (Urb->HcdUrbCommonTransfer.Status != UHCD_STATUS_PENDING_CANCELING) {
|
|
UHCD_ASSERT(localWork->Flags & UHCD_TRANSFER_DEFER);
|
|
}
|
|
#endif
|
|
|
|
// now we need to set up the queue head
|
|
// BUGBUG -- we currently don't handle look ahead
|
|
// ie if this transfer was already linked
|
|
//
|
|
// This is where we would check.
|
|
|
|
// before linking in this transfer we
|
|
// need to fixup the data toggle based
|
|
// on the current toggle for the ED
|
|
|
|
|
|
UHCD_FixupDataToggle(DeviceObject,
|
|
Endpoint,
|
|
localUrb);
|
|
|
|
//UHCD_ASSERT((Endpoint->CurrentXferId+(UCHAR)1) == localWork->XferId);
|
|
|
|
// update the endpoints TDList
|
|
// slot id corresponds to TD list
|
|
LOGENTRY(LOG_MISC, 'mkC2', Endpoint->CurrentXferId, localWork->Slot,
|
|
localWork->XferId);
|
|
|
|
Endpoint->TDList =
|
|
Endpoint->SlotTDList[i];
|
|
|
|
LOGENTRY(LOG_MISC, 'NXgo', Endpoint->TDList, localWork->Slot,
|
|
Endpoint->TDList->TDs[0].PhysicalAddress);
|
|
|
|
Endpoint->QueueHead->HW_VLink =
|
|
Endpoint->TDList->TDs[0].PhysicalAddress;
|
|
|
|
// this enables the xfer to be processed
|
|
localWork->Flags &= ~UHCD_TRANSFER_DEFER;
|
|
}
|
|
|
|
}
|
|
// NOT USED
|
|
#if 0
|
|
else {
|
|
|
|
//
|
|
// Low speed control endpoints share a single queue head.
|
|
//
|
|
// If the endpoint is lowspeed control then we need to start
|
|
// the next control transfer on this queue head.
|
|
//
|
|
// If another low speed control queue head is waiting we will
|
|
// pick it up when we process the rest of the endpoint list
|
|
// for this interrupt. If the next control queue head is before
|
|
// us in the endpoint list then we will ask for an interrupt next
|
|
// frame so that we can start it.
|
|
//
|
|
|
|
if (Endpoint->LowSpeed) {
|
|
TEST_TRAP();
|
|
UHCD_RequestInterrupt(DeviceObject, -2);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// UHCD_KdPrint((2, "'exit UHCD_ProcessAsyncTransfer %d status = %x\n', completed, *Status));
|
|
|
|
ENDPROC("Pas-");
|
|
|
|
return usbStatus;
|
|
}
|
|
|
|
|
|
USBD_STATUS
|
|
UHCD_PrepareMoreAsyncTDs(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint,
|
|
IN PHCD_URB Urb,
|
|
IN BOOLEAN Busy
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to a device object.
|
|
|
|
Endpoint - endpoint to check for completed transfers.
|
|
|
|
Urb - ptr to URB to process.
|
|
|
|
Status - pointer to USBD status, will be filled in if transfer
|
|
is complete.
|
|
|
|
Busy - indicates the stae of the queue head
|
|
|
|
Return Value:
|
|
|
|
TRUE if this transfer is complete, Status set to proper
|
|
error code.
|
|
|
|
--*/
|
|
{
|
|
ULONG count = 0;
|
|
SHORT i, slot;
|
|
SHORT start, oldLastTDPreparedIdx;
|
|
PHCD_EXTENSION urbWork = HCD_AREA(Urb).HcdExtension;
|
|
USBD_STATUS usbStatus = USBD_STATUS_SUCCESS;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
|
|
STARTPROC("Mas+");
|
|
//
|
|
// One of two conditions get us in to this routine:
|
|
//
|
|
// 1. The queue is stopped, waiting for us to prepare
|
|
// more TDs for an overflow transfer: Busy = FALSE
|
|
// && CurrentTDIdx is pointing at the first TD after
|
|
// the last TD prepared.
|
|
//
|
|
// 2. The queue is not stopped but we may be able to set
|
|
// up some more TDs
|
|
//
|
|
|
|
|
|
// can only process one TD list at a time
|
|
slot = urbWork->Slot;
|
|
UHCD_ASSERT(Endpoint->TDList == Endpoint->SlotTDList[slot]);
|
|
|
|
|
|
UHCD_KdPrint((2, "'enter UHCD_PrepareMoreAsyncTDs\n"));
|
|
|
|
deviceExtension = DeviceObject->DeviceExtension;
|
|
|
|
//
|
|
// Pick up where we left off, keep building TDs until we
|
|
// use up the client buffer or run out of TDs.
|
|
//
|
|
|
|
//
|
|
// Start at the first TD available after the last one prepared.
|
|
//
|
|
oldLastTDPreparedIdx = Endpoint->LastTDPreparedIdx[slot];
|
|
i = NEXT_TD(Endpoint->LastTDPreparedIdx[slot], Endpoint);
|
|
// Remember where we started...
|
|
start = i;
|
|
|
|
LOGENTRY(LOG_MISC, 'pmTD', Endpoint->LastTDPreparedIdx[slot],
|
|
Endpoint->LastTDInTransferIdx[slot], Busy);
|
|
|
|
if (Busy) {
|
|
|
|
// We want to avoid doing this if the number of free TDs is not worth
|
|
// the effort -- otherwise we'll end up with the T-Bit and ioc bit set
|
|
// for every packet.
|
|
// --
|
|
// Do a quick scan of the TDs if we have at least 4 inactive then go
|
|
// ahead and try
|
|
|
|
SHORT j, x = 0;
|
|
|
|
for (j=0; j<Endpoint->TDCount; j++) {
|
|
if (!Endpoint->TDList->TDs[j].Active) {
|
|
x++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// x = the most TDs we can set up
|
|
//
|
|
if (x <= 3) {
|
|
goto UHCD_PrepareMoreAsyncTDs_Done;
|
|
}
|
|
|
|
LOGENTRY(LOG_MISC, 'frTD', x, Endpoint->QueueHead->HW_VLink, 0);
|
|
}
|
|
#if DBG
|
|
else {
|
|
UHCD_ASSERT(i == Endpoint->CurrentTDIdx[slot]);
|
|
// if we are not Busy then we got here because the T-bit was set
|
|
// this means that currentTD should be the first new TD we set
|
|
// up, and all TDs for this transfer have been processed.
|
|
|
|
// assert that the T-bit is set on the queue head
|
|
}
|
|
#endif
|
|
|
|
do {
|
|
|
|
LOGENTRY(LOG_MISC, 'mrTD', i, Endpoint->CurrentTDIdx,
|
|
Endpoint->LastTDPreparedIdx[slot]);
|
|
//
|
|
// If the Busy flag is set then the currentTD has not been
|
|
// processed yet so we need to stop if we hit it.
|
|
//
|
|
|
|
if (Busy && i == Endpoint->CurrentTDIdx[slot]) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// we should never encounter an active TD
|
|
//
|
|
UHCD_ASSERT(Endpoint->TDList->TDs[i].Active == 0);
|
|
|
|
//
|
|
// See if we have consumed the client buffer, if so then we are
|
|
// done, mark this TD as the last one and stop preparing TDs.
|
|
//
|
|
|
|
if (urbWork->TransferOffset < Urb->HcdUrbCommonTransfer.TransferBufferLength ) {
|
|
UHCD_KdPrint((2, "'offset = %x\n", urbWork->TransferOffset));
|
|
if (UHCD_PrepareAsyncDataPacket(DeviceObject,
|
|
&Endpoint->TDList->TDs[i],
|
|
Endpoint,
|
|
Urb,
|
|
TRUE,
|
|
FALSE,
|
|
// not init
|
|
FALSE)) {
|
|
|
|
Endpoint->LastTDPreparedIdx[slot] = i;
|
|
count++;
|
|
} else {
|
|
//
|
|
// error occurred forming packet, this will
|
|
// complete the transfer.
|
|
//
|
|
TEST_TRAP();
|
|
usbStatus = USBD_STATUS_NO_MEMORY;
|
|
goto UHCD_PrepareMoreAsyncTDs_Done;
|
|
}
|
|
} else {
|
|
#ifdef CONTROL
|
|
|
|
//
|
|
// Done with client buffer, if this is a control
|
|
// transfer then we'll need to do the status packet
|
|
//
|
|
|
|
if (CONTROL_TRANSFER(Endpoint)) {
|
|
UHCD_PrepareStatusPacket(&Endpoint->TDList->TDs[i],
|
|
Endpoint,
|
|
Urb);
|
|
|
|
Endpoint->LastTDPreparedIdx[slot] = i;
|
|
count++;
|
|
}
|
|
#endif
|
|
//
|
|
// Last TD in the transfer is the last one we set up,
|
|
// the current TD should be set to the first one we set up.
|
|
//
|
|
|
|
Endpoint->LastTDInTransferIdx[slot] =
|
|
Endpoint->LastTDPreparedIdx[slot];
|
|
|
|
//
|
|
// Set the T-bit and the IOC bit for the last TD in the transfer
|
|
//
|
|
// NOTE: for non-B0 systems the IOC bit and T-bit will be set on every
|
|
// packet for IN transfers.
|
|
//
|
|
|
|
SET_T_BIT(Endpoint->TDList->TDs[Endpoint->LastTDInTransferIdx[slot]].HW_Link);
|
|
Endpoint->TDList->TDs[Endpoint->LastTDInTransferIdx[slot]].InterruptOnComplete = 1;
|
|
|
|
if (!Busy) {
|
|
// if the queue head was stopped resume at the first TD we
|
|
// set up.
|
|
Endpoint->CurrentTDIdx[slot] = start;
|
|
}
|
|
break;
|
|
#ifdef CONTROL
|
|
}
|
|
#endif
|
|
|
|
i = NEXT_TD(Endpoint->LastTDPreparedIdx[slot], Endpoint);
|
|
|
|
//
|
|
// stop when we get to the TD we started at or we hit
|
|
// the current TD.
|
|
//
|
|
// if we were called to set up TDs while the endpoint is still busy
|
|
// then it is possible we'll run in to the current TD.
|
|
//
|
|
|
|
} while (i != start && i != Endpoint->CurrentTDIdx[slot]);
|
|
|
|
//
|
|
// We may not have finished setting up all the TDs for the transfer,
|
|
// if this is the case we'll need to set the T-Bit and IOC bit on the
|
|
// last TD we were able to prepare.
|
|
//
|
|
|
|
if (count && Endpoint->LastTDInTransferIdx[slot] == -1) {
|
|
SET_T_BIT(Endpoint->TDList->TDs[Endpoint->LastTDPreparedIdx[slot]].HW_Link);
|
|
Endpoint->TDList->TDs[Endpoint->LastTDPreparedIdx[slot]].InterruptOnComplete = 1;
|
|
|
|
// check to see if we finished the client buffer
|
|
// ie client buffer finished with the last TD we prepared.
|
|
if (urbWork->TransferOffset == Urb->HcdUrbCommonTransfer.TransferBufferLength &&
|
|
!CONTROL_TRANSFER(Endpoint)) {
|
|
Endpoint->LastTDInTransferIdx[slot] = Endpoint->LastTDPreparedIdx[slot];
|
|
}
|
|
}
|
|
|
|
|
|
if (Busy && count) {
|
|
// attempt to clear the old T-bit from the lastTD prepared
|
|
// we may not get it in time but if we do we'll avoid stopping
|
|
// the queue.
|
|
|
|
if (deviceExtension->SteppingVersion >= UHCD_B0_STEP ||
|
|
DATA_DIRECTION_OUT(Urb)) {
|
|
CLEAR_T_BIT(Endpoint->TDList->TDs[oldLastTDPreparedIdx].HW_Link);
|
|
// hit this if we ever actually set up more TDs while the Queue head
|
|
// is busy
|
|
}
|
|
}
|
|
|
|
//
|
|
// NOTE:
|
|
// Caller is responsible for resuming the QH at currentTDIdx.
|
|
//
|
|
UHCD_PrepareMoreAsyncTDs_Done:
|
|
|
|
UHCD_KdPrint((2, "'exit UHCD_PrepareMoreAsyncTDs\n"));
|
|
ENDPROC("Mas-");
|
|
|
|
return usbStatus;
|
|
}
|
|
|
|
|
|
HW_DESCRIPTOR_PHYSICAL_ADDRESS
|
|
UHCD_GetPacketBuffer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint,
|
|
IN PHCD_URB Urb,
|
|
IN PHCD_EXTENSION UrbWork,
|
|
IN ULONG Offset,
|
|
IN ULONG PacketSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compute the packet buffer physical address we will give to the
|
|
host controller based on the current offset in the transfer.
|
|
We also check if the packet crosses a page boundry if so this
|
|
routine returns an address of a region of memory used to
|
|
double-buffer the packet.
|
|
|
|
Arguments:
|
|
|
|
PacketSize - size of the packet we are dealing with
|
|
|
|
Offset - is the start position in the transfer for this
|
|
packet
|
|
|
|
Return Value:
|
|
|
|
Physical address of a packet buffer we can give to the UHC hardware.
|
|
If the packet requires double buffering and no memory is available
|
|
the 0 is returned for the hw_address.
|
|
|
|
--*/
|
|
{
|
|
ULONG i, start = 0, end = 0;
|
|
HW_DESCRIPTOR_PHYSICAL_ADDRESS hw_address = 0;
|
|
|
|
STARTPROC("Gpb+");
|
|
|
|
ASSERT_ENDPOINT(Endpoint);
|
|
//
|
|
// Offset is the start position in the transfer
|
|
// buffer of the packet we must prepare.
|
|
//
|
|
LOGENTRY(LOG_MISC, 'GPBx', UrbWork,
|
|
UrbWork->NumberOfLogicalAddresses,
|
|
0);
|
|
|
|
for (i=0; i< UrbWork->NumberOfLogicalAddresses; i++) {
|
|
|
|
// first find the base logical address associated with
|
|
// this packet
|
|
|
|
LOGENTRY(LOG_MISC, 'GPBf', &UrbWork->LogicalAddressList[i],
|
|
Offset,
|
|
UrbWork->LogicalAddressList[i].Length);
|
|
|
|
start = end;
|
|
end += UrbWork->LogicalAddressList[i].Length;
|
|
if (Offset < end) {
|
|
//
|
|
// found the logical address range that this packet
|
|
// starts in.
|
|
//
|
|
LOGENTRY(LOG_MISC, 'GPBm', end, PacketSize, Offset);
|
|
|
|
if (Offset + PacketSize <= end) {
|
|
//
|
|
// if the whole packet fits within the
|
|
// region associated with this logical
|
|
// address then we are OK -- just return the
|
|
// physical address.
|
|
//
|
|
|
|
hw_address =
|
|
UrbWork->LogicalAddressList[i].LogicalAddress +
|
|
Offset - start;
|
|
|
|
UHCD_ASSERT(UrbWork->LogicalAddressList[i].PacketMemoryDescriptor == NULL);
|
|
} else {
|
|
|
|
//
|
|
// packet crosses page boundry, get one of our
|
|
// packet buffers
|
|
//
|
|
LOGENTRY(LOG_MISC, 'PAK!', 0, Offset, PacketSize);
|
|
|
|
UrbWork->LogicalAddressList[i].PacketMemoryDescriptor =
|
|
UHCD_AllocateCommonBuffer(DeviceObject,
|
|
Endpoint->MaxPacketSize);
|
|
|
|
if (UrbWork->LogicalAddressList[i].PacketMemoryDescriptor) {
|
|
// if this is an out then we need to copy the data in
|
|
// to the packet buffer
|
|
UrbWork->LogicalAddressList[i].PacketOffset = Offset;
|
|
if (DATA_DIRECTION_OUT(Urb)) {
|
|
//TEST_TRAP();
|
|
|
|
LOGENTRY(LOG_MISC, 'DBpk', UrbWork->LogicalAddressList[i].PacketMemoryDescriptor->VirtualAddress,
|
|
(PUCHAR)UrbWork->SystemAddressForMdl +
|
|
UrbWork->LogicalAddressList[i].PacketOffset,
|
|
PacketSize);
|
|
RtlCopyMemory(UrbWork->LogicalAddressList[i].PacketMemoryDescriptor->VirtualAddress,
|
|
(PUCHAR) UrbWork->SystemAddressForMdl +
|
|
UrbWork->LogicalAddressList[i].PacketOffset,
|
|
PacketSize);
|
|
}
|
|
hw_address =
|
|
UrbWork->LogicalAddressList[i].PacketMemoryDescriptor->LogicalAddress;
|
|
}
|
|
#if DBG
|
|
else {
|
|
// NOTE:
|
|
// failure here should cause the transfer to be completed with error,
|
|
// we will return 0 for the hw_address;
|
|
TEST_TRAP();
|
|
}
|
|
#endif //DBG
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
UHCD_ASSERT(i < UrbWork->NumberOfLogicalAddresses);
|
|
|
|
LOGENTRY(LOG_MISC, 'GPB0', hw_address, 0, 0);
|
|
|
|
ENDPROC("Gpb-");
|
|
|
|
return hw_address;
|
|
}
|
|
|
|
|
|
VOID
|
|
UHCD_FixupDataToggle(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PUHCD_ENDPOINT Endpoint,
|
|
IN PHCD_URB Urb
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a TDList that is already set up, fxuo the data toggle
|
|
based of the current EP data toggle
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - pointer to a device object.
|
|
|
|
Endpoint - Endpoint associated with this Urb.
|
|
|
|
Urb - pointer to URB Request Packet for this transfer.
|
|
|
|
Return Value:
|
|
|
|
Usbd status code.
|
|
|
|
--*/
|
|
{
|
|
// PDEVICE_EXTENSION deviceExtension;
|
|
SHORT i, slot, start;
|
|
PUHCD_TD_LIST tDList;
|
|
PHCD_EXTENSION urbWork = HCD_AREA(Urb).HcdExtension;
|
|
|
|
UHCD_KdPrint((2, "'enter UHCD_FixupDataToggle\n"));
|
|
|
|
ASSERT_ENDPOINT(Endpoint);
|
|
UHCD_ASSERT(Endpoint == HCD_AREA(Urb).HcdEndpoint);
|
|
UHCD_ASSERT(!(urbWork->Flags & UHCD_TOGGLE_READY));
|
|
|
|
// do some general init stuff first,
|
|
// TDs form a circular list
|
|
slot = urbWork->Slot;
|
|
tDList = Endpoint->SlotTDList[slot];
|
|
|
|
//UHCD_ASSERT(urbWork->Flags & UHCD_TRANSFER_DEFER);
|
|
UHCD_ASSERT(urbWork->Flags & UHCD_TRANSFER_INITIALIZED);
|
|
|
|
start = i = Endpoint->CurrentTDIdx[slot];
|
|
do {
|
|
|
|
tDList->TDs[i].RetryToggle = Endpoint->DataToggle;
|
|
Endpoint->DataToggle ^=1;
|
|
|
|
if (i == Endpoint->LastTDInTransferIdx[slot]) {
|
|
// if this is the last TD the we are done
|
|
break;
|
|
}
|
|
|
|
i = NEXT_TD(i, Endpoint);
|
|
} while (i != start);
|
|
|
|
urbWork->Flags |= UHCD_TOGGLE_READY;
|
|
|
|
}
|