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

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;
}