xbox-kernel/private/ntos/dd/usb/ohcd/transfer.c
2020-09-30 17:17:25 +02:00

993 lines
30 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
transfer.c
Abstract:
Implementation of functions related to queueing and processing transfer.
However, isochronous transfers support is not in this file, see isoch.c
Environment:
Designed for XBOX.
Notes:
Revision History:
01-20-00 created by Mitchell Dernis (mitchd)
--*/
//
// Pull in OS headers
//
#include <ntos.h>
//
// Setup the debug information for this file (see ..\inc\debug.h)
//
#define MODULE_POOL_TAG 'DCHO'
#include <debug.h>
DEFINE_USB_DEBUG_FUNCTIONS("OHCD");
//
// Pull in usb headers
//
#include <hcdi.h> //Interface between USBD and HCD
#include "ohcd.h" //Private OHCD stuff
//----------------------------------------------------------------------------
// Forward declaration of functions defined and used only this in this module
//----------------------------------------------------------------------------
USHORT
FASTCALL
OHCD_fGetTDsRequired(
PURB Urb,
POHCD_ENDPOINT Endpoint
);
USBD_STATUS
FASTCALL
OHCD_fQueueInterruptTransfer(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN POHCD_ENDPOINT Endpoint,
IN PURB Urb
);
USBD_STATUS
FASTCALL
OHCD_fQueueBulkTransfer(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN PURB Urb
);
USBD_STATUS
FASTCALL
OHCD_fQueueControlTransfer(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN PURB Urb
);
VOID
FASTCALL
OHCD_fProgramTransfer(
POHCD_DEVICE_EXTENSION DeviceExtension,
POHCD_ENDPOINT Endpoint,
PURB Urb
);
ULONG
FASTCALL
OHCD_fMapTransfer(
IN OUT PVOID *CurrentVa,
IN OUT PULONG BytesRemaining,
OUT PULONG BytesMapped
);
//----------------------------------------------------------------------------
// Implementation of function called from other modules:
// OHCD_fQueueTransferRequest
//----------------------------------------------------------------------------
USBD_STATUS
FASTCALL
OHCD_fQueueTransferRequest(
POHCD_DEVICE_EXTENSION DeviceExtension,
PURB Urb
)
/*++
Routine Description:
QueueTransferRequest is the first stage of processing an asynchronous
transfer request. Part of the TD conservation program requires
different queueing depending on endpoint type. In this routine,
we:
1) Calculate TDs required.
2) Dispatch to proper endpoint type determined queue routine.
Arguments:
DeviceExtension - Device extension for our OHCD instance.
HcdUrb - Transfer URB to queue.
Return Value:
STATUS_SUCCESS. Otherwise we will KeBugcheck before returning.
--*/
{
POHCD_ENDPOINT endpoint;
USBD_STATUS status;
KIRQL oldIrql;
USB_DBG_ENTRY_PRINT(("Entering OHCD_fQueueTransferRequest"));
//
// Get the endpoint.
//
endpoint = (POHCD_ENDPOINT)Urb->CommonTransfer.EndpointHandle;
//
// Figure out how many TDs this transfer requires
//
Urb->CommonTransfer.Hca.HcdTDCount = OHCD_fGetTDsRequired(Urb, endpoint);
//
// Synchronize access to queues
//
oldIrql = KeRaiseIrqlToDpcLevel();
//
// Class driver should be smart enough not to submit transfers
// while an endpoint is being closed.
//
ASSERT(!(OHCD_ENDPOINT_FLAG_CLOSING&endpoint->Flags));
//
// We assume that it going to get queue. Since the various
// queue routines may actually get programmed before returning
// here, we want to increment before calling them.
//
endpoint->QueuedUrbCount++;
Urb->CommonTransfer.Hca.HcdUrbFlags = OHCD_URB_FLAG_QUEUED;
Urb->CommonTransfer.Hca.HcdUrbLink = NULL;
//
// Queue the URB. Each type of endpoint has its own queue
//
switch(endpoint->EndpointType)
{
case USB_ENDPOINT_TYPE_INTERRUPT:
ASSERT(URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER==Urb->Header.Function);
status = OHCD_fQueueInterruptTransfer(DeviceExtension, endpoint, Urb);
break;
case USB_ENDPOINT_TYPE_BULK:
ASSERT(URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER==Urb->Header.Function);
status = OHCD_fQueueBulkTransfer(DeviceExtension, Urb);
break;
case USB_ENDPOINT_TYPE_CONTROL:
ASSERT(URB_FUNCTION_CONTROL_TRANSFER==Urb->Header.Function);
status = OHCD_fQueueControlTransfer(DeviceExtension, Urb);
break;
default:
//ISOCH endpoint never expected here.
USB_DBG_ERROR_PRINT(("Unrecognized endpoint type in OHCD_fQueueTransferRequest\nClass driver is probably at fault."));
status = USBD_STATUS_REQUEST_FAILED;
}
//
// If it didn't actually get queued than, we want to decrease
// queue count.
//
if(USBD_ERROR(status))
{
Urb->CommonTransfer.Hca.HcdUrbFlags = 0;
endpoint->QueuedUrbCount--;
}
//
// Done with queues
//
KeLowerIrql(oldIrql);
USB_DBG_EXIT_PRINT(("Exiting OHCD_fQueueTransferRequest"));
return status;
}
USBD_STATUS
FASTCALL
OHCD_fQueueInterruptTransfer(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN POHCD_ENDPOINT Endpoint,
IN PURB Urb
)
{
ASSERT_DISPATCH_LEVEL();
//
// Check that this transfer is a legal transfer
//
if( Urb->BulkOrInterruptTransfer.Hca.HcdTDCount > OHCD_INTERRUPT_TD_QUOTA)
{
return USBD_STATUS_TRANSFER_TOO_LONG;
}
//
// Add URB to endpoints pending queue
//
if(Endpoint->PendingUrbTailP)
{
Endpoint->PendingUrbTailP->CommonTransfer.Hca.HcdUrbLink = Urb;
}else
{
Endpoint->PendingUrbHeadP = Urb;
}
Endpoint->PendingUrbTailP = Urb;
//
// Try programming this transfer
//
OHCD_fProgramInterruptTransfer(DeviceExtension, Endpoint);
return USBD_STATUS_PENDING;
}
VOID
FASTCALL
OHCD_fProgramInterruptTransfer(
POHCD_DEVICE_EXTENSION DeviceExtension,
POHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
This routine is called in two places:
1) When a new URB is placed on the queue for endpoint.
2) When an URB completes.
Interrupt endpoints queue URBs on a per endpoint basis. Each endpoint
has a quota of OHCD_INTERRUPT_TD_QUOTA (aside from the dummy TD).
This routine will program all pending URBs until OHCD_INTERRUPT_TD_QUOTA
TDs are in use.
If an URB is encountered that requires more than OHCD_INTERRUPT_TD_QUOTA
it is failed immediately.
Arguments:
Endpoint - Endpoint to process.
Return Value:
None.
--*/
{
PURB_BULK_OR_INTERRUPT_TRANSFER urb;
USB_DBG_ENTRY_PRINT(("Entry OHCD_ProgramInterruptTransfer"));
ASSERT_DISPATCH_LEVEL();
//
// If there are URBs waiting
// try to program them.
//
while(Endpoint->PendingUrbHeadP)
{
urb = &Endpoint->PendingUrbHeadP->BulkOrInterruptTransfer;
//
// If there are not enough TDs available than break from this loop.
//
if( (urb->Hca.HcdTDCount + Endpoint->TDInUseCount) > OHCD_INTERRUPT_TD_QUOTA)
{
break;
}
//*
//* There are enough TDs to proceed to program this transfer.
//*
//
// Remove URB from queue
//
Endpoint->PendingUrbHeadP = urb->Hca.HcdUrbLink;
if(NULL == Endpoint->PendingUrbHeadP) Endpoint->PendingUrbTailP = NULL;
//
// Mark the TDs as in use
//
Endpoint->TDInUseCount += urb->Hca.HcdTDCount;
//
// Delegate the actual programming process off
// to a routine common to all endpoint types.
//
OHCD_fProgramTransfer(DeviceExtension, Endpoint, (PURB)urb);
}
USB_DBG_EXIT_PRINT(("Exiting OHCD_ProgramInterruptTransfer"));
}
USBD_STATUS
FASTCALL
OHCD_fQueueBulkTransfer(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN PURB Urb
)
{
ASSERT_DISPATCH_LEVEL();
//
// Check that this transfer is a legal transfer
//
if( Urb->BulkOrInterruptTransfer.Hca.HcdTDCount > OHCD_PoolGetBulkQuota())
{
return USBD_STATUS_TRANSFER_TOO_LONG;
}
//
// Add URB to endpoints pending queue (at the tail).
//
if(DeviceExtension->BulkUrbHeadP)
{
DeviceExtension->BulkUrbTailP->CommonTransfer.Hca.HcdUrbLink = Urb;
}else
{
DeviceExtension->BulkUrbHeadP = Urb;
}
DeviceExtension->BulkUrbTailP = Urb;
//
// Try programming this transfer
//
OHCD_fProgramBulkTransfer(DeviceExtension);
return USBD_STATUS_PENDING;
}
VOID
FASTCALL
OHCD_fProgramBulkTransfer(
POHCD_DEVICE_EXTENSION DeviceExtension
)
{
PURB_BULK_OR_INTERRUPT_TRANSFER urb;
USB_DBG_ENTRY_PRINT(("Entry OHCD_ProgramBulkTransfer"));
ASSERT_DISPATCH_LEVEL();
//
// If there are URBs waiting
// try to program them.
//
while(DeviceExtension->BulkUrbHeadP)
{
urb = &DeviceExtension->BulkUrbHeadP->BulkOrInterruptTransfer;
//
// Draw TDs from the Bulk Quota (this does nothing other than mark that
// we will allocate the TDs)
if(!OHCD_PoolDebitBulkTDQuota(urb->Hca.HcdTDCount))
{
break;
}
//*
//* There are enough TDs to proceed to program this transfer.
//*
//
// Remove URB from queue
//
DeviceExtension->BulkUrbHeadP = urb->Hca.HcdUrbLink;
if(NULL == DeviceExtension->BulkUrbHeadP) DeviceExtension->BulkUrbTailP = NULL;
//
// Delegate the actual programming process off
// to a routine common to all endpoint types.
//
OHCD_fProgramTransfer(DeviceExtension, (POHCD_ENDPOINT)urb->EndpointHandle, (PURB)urb);
}
USB_DBG_EXIT_PRINT(("Exiting OHCD_ProgramBulkTransfer"));
}
USBD_STATUS
FASTCALL
OHCD_fQueueControlTransfer(
IN POHCD_DEVICE_EXTENSION DeviceExtension,
IN PURB Urb
)
{
ASSERT_DISPATCH_LEVEL();
//
// Check that this transfer is a legal transfer
//
if( Urb->ControlTransfer.Hca.HcdTDCount > OHCD_PoolGetControlQuota())
{
return USBD_STATUS_TRANSFER_TOO_LONG;
}
//
// Add URB to endpoints pending queue (at the tail)
//
if(DeviceExtension->ControlUrbHeadP)
{
DeviceExtension->ControlUrbTailP->CommonTransfer.Hca.HcdUrbLink = Urb;
}else
{
DeviceExtension->ControlUrbHeadP = Urb;
}
DeviceExtension->ControlUrbTailP = Urb;
//
// Try programming this transfer
//
OHCD_fProgramControlTransfer(DeviceExtension);
return USBD_STATUS_PENDING;
}
VOID
FASTCALL
OHCD_fProgramControlTransfer(
POHCD_DEVICE_EXTENSION DeviceExtension
)
{
PURB_CONTROL_TRANSFER urb;
USB_DBG_ENTRY_PRINT(("Entry OHCD_ProgramControlTransfer"));
ASSERT_DISPATCH_LEVEL();
//
// If there are URBs waiting
// try to program them.
//
while(DeviceExtension->ControlUrbHeadP)
{
urb = &DeviceExtension->ControlUrbHeadP->ControlTransfer;
//
// Draw TDs from the Bulk Quota (this does nothing other than mark that
// we will allocate the TDs)
if(!OHCD_PoolDebitControlTDQuota(urb->Hca.HcdTDCount))
{
break;
}
//*
//* There are enough TDs to proceed to program this transfer.
//*
//
// Remove URB from queue
//
DeviceExtension->ControlUrbHeadP = urb->Hca.HcdUrbLink;
if(NULL == DeviceExtension->ControlUrbHeadP) DeviceExtension->ControlUrbTailP = NULL;
//
// Delegate the actual programming process off
// to a routine common to all endpoint types.
//
OHCD_fProgramTransfer(DeviceExtension, (POHCD_ENDPOINT)urb->EndpointHandle, (PURB)urb);
}
USB_DBG_EXIT_PRINT(("Exiting OHCD_ProgramControlTransfer"));
}
//----------------------------------------------------------------------------
// Implementation of locally declared utility functions:
//
// OHCD_GetTDsRequired
// OHCD_AdapterControl
//----------------------------------------------------------------------------
USHORT
FASTCALL
OHCD_fGetTDsRequired(
PURB Urb,
POHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
Local utilitiy function to calculate number of TDs required
to transmit the data.
Arguments:
Urb - URB for which to calculate required number of TDs
Return Value:
Number of TDs required.
--*/
{
//
// Initialize to zero.
//
USHORT numTDsRequired = 0;
USHORT maxPacket = (USHORT)Endpoint->HcEndpointDescriptor.Control.MaximumPacketSize;
//
// Zero length transfers should never happen, unless it is
// a command on a control pipe.
//
ASSERT(Urb->CommonTransfer.TransferBufferLength || (USB_ENDPOINT_TYPE_CONTROL == Endpoint->EndpointType));
if(Urb->CommonTransfer.TransferBufferLength)
{
//
// How many TD's do we need.
//
numTDsRequired += (USHORT)(Urb->CommonTransfer.TransferBufferLength/maxPacket);
//
// If it wasn't evenly divisible we need to add one.
//
if(Urb->CommonTransfer.TransferBufferLength%maxPacket)
{
numTDsRequired++;
}
}
//
// If it is a control endpoint, there is a setup packet
// and a status packet. We need a TD for each, plus the
// setup and status data each take up 8 bytes. We just
// grab a descritpor block for this. So we need three
// extra blocks.
//
if(USB_ENDPOINT_TYPE_CONTROL == Endpoint->EndpointType)
{
numTDsRequired += 3;
}
return numTDsRequired;
}
VOID
FASTCALL
OHCD_fProgramTransfer(
POHCD_DEVICE_EXTENSION DeviceExtension,
POHCD_ENDPOINT Endpoint,
PURB Urb
)
/*++
Routine Description:
This routine performs the actual work for control, interrupt, and bulk
endpoint. For isochronous endpoints, this routine never gets called.
Algorithm Description:
This routine takes the form of a loop within a loop.
The outer loop calls IoMapTransfer, which will map from
the start of the user buffer to the end of the page.
However, the data that can be transmitted within a single
packet is limited to MaxPacketSize(8,16,32, or 64) which
is device and endpoint dependent.
The inner loop creates these TDs. However, it is also
possible for a single TD to cross a page boundary, so the
loop take into account that the first TD of page may begin
on a previous page.
There are a couple of other little annoyances:
1) If it is a control transfer, we need to
generate a TD for the setup and status packets.
2) At the end we make sure that the endpoint is in order
and that all the flags are set correctly.
Arguments:
HcdUrb - URB for which to calculate required number of TDs
Return Value:
Number of TDs required.
--*/
{
ULONG maxPacketSize = Endpoint->HcEndpointDescriptor.Control.MaximumPacketSize;
PVOID currentVa;
ULONG bytesRemaining = Urb->CommonTransfer.TransferBufferLength;
ULONG bytesMapped = 0;
ULONG currentPa = 0;
UCHAR dataToggle = OHCI_TD_TOGGLE_FROM_ED;
UCHAR hostControllerNumber = (UCHAR)DeviceExtension->HostControllerNumber;
ULONG prevPageResidualPa = 0;
ULONG prevPageResidualBytes = 0;
POHCD_TRANSFER_DESCRIPTOR firstTD;
POHCD_TRANSFER_DESCRIPTOR previousTD = NULL;
POHCD_TRANSFER_DESCRIPTOR currentTD;
USB_DBG_ENTRY_PRINT(("Entering OHCD_ProgramTransfer."));
//
// Traceout some summary stats of the URB
//
USB_DBG_TRACE_PRINT(("Programming DMA for URB @0x%0.8x.", Urb));
USB_DBG_TRACE_PRINT((" TransferBuffer @0x%0.8x", Urb->CommonTransfer.TransferBuffer ));
USB_DBG_TRACE_PRINT((" TransferBufferLength %d bytes", Urb->CommonTransfer.TransferBufferLength ));
USB_DBG_TRACE_PRINT((" MaximumPacketSize %d bytes", maxPacketSize));
USB_DBG_TRACE_PRINT((" TDs Required %d", Urb->CommonTransfer.Hca.HcdTDCount));
USB_DBG_TRACE_PRINT((" Endpoint @0x%0.8x", Endpoint));
USB_DBG_TRACE_PRINT((" Control Endpoint? %s",
(USB_ENDPOINT_TYPE_CONTROL == Endpoint->EndpointType) ? "Yes" : "No"));
//
// We are about to program a TD that was previously queued.
// Adjust the counts that are kept with the Endpoint.
//
Endpoint->QueuedUrbCount--;
Endpoint->ProgrammedUrbCount++;
Urb->CommonTransfer.Hca.HcdUrbFlags &= ~OHCD_URB_FLAG_QUEUED;
Urb->CommonTransfer.Hca.HcdUrbFlags |= OHCD_URB_FLAG_PROGRAMMED;
//
// Setup the TD basics. Allocate the first TD. However, in most cases we can
// recycle the dummy. Reuse the dummy at the tail for the first TD
//
if(Endpoint->HcEndpointDescriptor.TailP)
{
firstTD =
currentTD = OHCD_PoolTDFromPhysicalAddress(Endpoint->HcEndpointDescriptor.TailP);
} else
{
//
// We need to allocate the head TD.
//
firstTD =
currentTD = OHCD_PoolAllocateTD(hostControllerNumber);
//
// Endpoint sure ought to be paused
//
ASSERT(1==Endpoint->HcEndpointDescriptor.Control.Skip);
//
// Write this newly allocated TD to the head
//
WRITE_HEADP(&Endpoint->HcEndpointDescriptor, firstTD->PhysicalAddress)
}
//
// If we are a control interface, handle the setup packet
//
if(USB_ENDPOINT_TYPE_CONTROL == Endpoint->EndpointType)
{
POHCD_SETUP_BUFFER SetupBuffer;
//
// We need a descriptor block to hold the setup packet data
// and the status packet data.
// (we are not really using the TD as a TD, so don't stamp
// it with the hostControllerNumber)
//
SetupBuffer = (POHCD_SETUP_BUFFER) OHCD_PoolAllocateTD(0xFE-hostControllerNumber);
//
// Copy over the setup bytes (8 of them).
// Normally I object to obfuscated code like this,
// but you cannot call RtlCopyMemory from DISPATCH_LEVEL, so I will
// use this trick I saw in the original Win2K driver.
//
*((PLONGLONG)SetupBuffer->Setup) = *((PLONGLONG)&Urb->ControlTransfer.SetupPacket);
//
// We will use the firstTD for the setup packet, so allocate a new TD
// for the first data packet.
//
currentTD = OHCD_PoolAllocateTD(hostControllerNumber);
//
// Fill out the hardware mandated fields
//
firstTD->HcTransferDescriptor.BufferRounding = FALSE;
firstTD->HcTransferDescriptor.Direction_PID = OHCI_TD_DIRECTION_PID_SETUP;
firstTD->HcTransferDescriptor.DelayInterrupt = OHCI_TD_DELAY_INTERRUPT_NONE;
firstTD->HcTransferDescriptor.DataToggle = dataToggle = OHCI_TD_TOGGLE_DATA0;
firstTD->HcTransferDescriptor.ErrorCount = 0;
firstTD->HcTransferDescriptor.ConditionCode = OHCI_CC_NOT_ACCESSED;
firstTD->HcTransferDescriptor.CurrentBufferPointer = SetupBuffer->PhysicalAddress;
firstTD->HcTransferDescriptor.NextTD = currentTD->PhysicalAddress;
firstTD->HcTransferDescriptor.BufferEnd = SetupBuffer->PhysicalAddress + 7;
//
// Now the software fields
//
firstTD->Endpoint = Endpoint;
firstTD->Flags = 0;
firstTD->Type = OHCD_TD_TYPE_SETUP;
firstTD->Bytes = 0;
firstTD->Urb = Urb;
}
//
// Lock the buffer pagesand get the initial value for currentVa,
// if and only if there is a data phase.
//
if(bytesRemaining)
{
#ifdef DVTSNOOPBUG
OHCD_PoolStartDoubleBufferTransfer(Urb);
#endif
MmLockUnlockBufferPages(Urb->CommonTransfer.TransferBuffer, bytesRemaining, FALSE);
currentVa = Urb->CommonTransfer.TransferBuffer;
}else
{
//
// It is a dataless control transfer. Set the direction so that
// the status stage will be an IN. Status is always reverse of
// data, so we set the direction to OUT.
//
Urb->ControlTransfer.TransferDirection = OHCI_TD_DIRECTION_PID_OUT;
}
//
// Now loop until we map the whole user buffer.
//
while(bytesRemaining)
{
//
// Get a physical address
//
currentPa = OHCD_fMapTransfer(
&currentVa,
&bytesRemaining,
&bytesMapped
);
//
// Loop as long as:
// * We have enough buffer mapped to program a whole packet.
// * Or it is the last packet.
//
while(
((bytesMapped + prevPageResidualBytes) >= maxPacketSize) ||
( (0 != bytesMapped) && (0 == bytesRemaining) )
)
{
//
// Fill out TD's beginning and ending buffer
// physical pointers, and update currentPa and bytesMapped
// for next iteration.
//
// The first case, handles a TD that crosses a page boundary
// for non-contiguous physical memory.
//
if(prevPageResidualBytes)
{
ULONG bytesFromCurrentPage;
//
// Record the start of the buffer (which is from the previousPage)
//
currentTD->HcTransferDescriptor.CurrentBufferPointer = prevPageResidualPa;
//
// Find BufferEnd which is on the current page
//
bytesFromCurrentPage = maxPacketSize - prevPageResidualBytes;
if(bytesMapped < bytesFromCurrentPage) bytesFromCurrentPage = bytesMapped;
currentPa += bytesFromCurrentPage;
bytesMapped -= bytesFromCurrentPage;
currentTD->Bytes = (UCHAR)(prevPageResidualBytes + bytesFromCurrentPage);
prevPageResidualBytes = 0;
}
else
{
//
// Record the start of the buffer.
//
currentTD->HcTransferDescriptor.CurrentBufferPointer = currentPa;
//
// Find BufferEnd
//
if(bytesMapped < maxPacketSize)
{
currentPa += bytesMapped;
currentTD->Bytes = (UCHAR)bytesMapped;
bytesMapped = 0;
}
else
{
currentTD->Bytes = (UCHAR)maxPacketSize;
currentPa += maxPacketSize;
bytesMapped -= maxPacketSize;
}
}
//
// Record BufferEnd.
//
currentTD->HcTransferDescriptor.BufferEnd = currentPa - 1;
//
// Setup the rest of the TD
//
currentTD->Endpoint = Endpoint;
currentTD->Flags = 0;
currentTD->Type = OHCD_TD_TYPE_DATA;
currentTD->HcTransferDescriptor.BufferRounding = FALSE;
currentTD->HcTransferDescriptor.ConditionCode = OHCI_CC_NOT_ACCESSED;
dataToggle ^= OHCI_TD_TOGGLE_DATA_MASK;
currentTD->HcTransferDescriptor.DataToggle = dataToggle;
currentTD->HcTransferDescriptor.DelayInterrupt = OHCI_TD_DELAY_INTERRUPT_NONE;
currentTD->HcTransferDescriptor.Direction_PID = Urb->ControlTransfer.TransferDirection;
currentTD->HcTransferDescriptor.ErrorCount = 0;
currentTD->Urb = Urb;
//
// To continue we need to allocate another TD and initialize the
// basic stuff.
//
previousTD = currentTD;
currentTD = OHCD_PoolAllocateTD(hostControllerNumber);
//
// Link the new TD on to the end of the previous one
//
previousTD->HcTransferDescriptor.NextTD = currentTD->PhysicalAddress;
USB_DBG_TRACE_PRINT(("Loop to assign another TD: bytesMapped(i.e. remaining from this page) = %d", bytesMapped));
}
//
// Set the previous page stuff for the next loop
//
prevPageResidualPa = currentPa;
prevPageResidualBytes = bytesMapped;
}
//
// A short packet is OK on last data packet if thed
// caller (who submitted the URB) wants it to be OK.
// Notice above that the default is FALSE. Even if the caller
// allows short packets, the FailedTD routine must be called
// to handle short packets in the middle of a transfer, as the
// extra TD's must be cleaned from the schedule.
//
if(previousTD && Urb->CommonTransfer.ShortTransferOK)
{
previousTD->HcTransferDescriptor.BufferRounding = TRUE;
}
//
// If it is a control endpoint,
// fill out the TD for the status packet.
//
if(USB_ENDPOINT_TYPE_CONTROL == Endpoint->EndpointType)
{
//
// Fill out the Hardware mandated fields
//
currentTD->HcTransferDescriptor.BufferRounding = FALSE;
//
// Status Packet must reverse the direction of the data packets.
//
currentTD->HcTransferDescriptor.Direction_PID = (OHCI_TD_DIRECTION_PID_IN == Urb->CommonTransfer.TransferDirection) ?
OHCI_TD_DIRECTION_PID_OUT : OHCI_TD_DIRECTION_PID_IN;
currentTD->HcTransferDescriptor.DelayInterrupt = Urb->CommonTransfer.InterruptDelay;
currentTD->HcTransferDescriptor.DataToggle = OHCI_TD_TOGGLE_DATA1;
currentTD->HcTransferDescriptor.ErrorCount = 0;
currentTD->HcTransferDescriptor.ConditionCode = OHCI_CC_NOT_ACCESSED;
currentTD->HcTransferDescriptor.CurrentBufferPointer = 0;
currentTD->HcTransferDescriptor.BufferEnd = 0;
//
// Now the software fields
//
currentTD->Endpoint = Endpoint;
currentTD->Flags = OHCD_TD_FLAG_LAST_TD;
currentTD->Type = OHCD_TD_TYPE_STATUS;
currentTD->Urb = Urb;
currentTD->Bytes = 0;
//
// We need a TD for the dummy packet
//
previousTD = currentTD;
currentTD = OHCD_PoolAllocateTD(hostControllerNumber);
previousTD->HcTransferDescriptor.NextTD = currentTD->PhysicalAddress;
}
else
//
// Otherwise we need to signal an interrupt on the last data packet, and mark the
// packet as the last.
//
{
previousTD->HcTransferDescriptor.DelayInterrupt = Urb->CommonTransfer.InterruptDelay;
previousTD->Flags = OHCD_TD_FLAG_LAST_TD;
}
//
// Now fill out the dummy TD
//
currentTD->Type = OHCD_TD_TYPE_DUMMY;
//
// Wipe out the transfer length, we will increment it as stuff completes
//
Urb->CommonTransfer.Hca.HcdOriginalLength = (USHORT)Urb->CommonTransfer.TransferBufferLength;
Urb->CommonTransfer.TransferBufferLength = 0;
//
// Update the tail pointer
//
Endpoint->HcEndpointDescriptor.TailP = currentTD->PhysicalAddress;
//
// make sure that SKip bit is not set on the endpoint
// (unless it is supposed to be paused!)
if(0==Endpoint->PendingPauseCount)
Endpoint->HcEndpointDescriptor.Control.Skip = 0;
//
// Now we need to tell the host controller that we added to the
// end of the TD queue if it is control or bulk
//
if(USB_ENDPOINT_TYPE_CONTROL == Endpoint->EndpointType)
{
WRITE_REGISTER_ULONG((PULONG)&DeviceExtension->OperationalRegisters->HcCommandStatus, HCCS_ControlListFilled);
}
else if(USB_ENDPOINT_TYPE_BULK == Endpoint->EndpointType)
{
WRITE_REGISTER_ULONG((PULONG)&DeviceExtension->OperationalRegisters->HcCommandStatus, HCCS_BulkListFilled);
}
USB_DBG_EXIT_PRINT(("Exiting OHCD_ProgramTransfer."));
return;
}
ULONG
FASTCALL
OHCD_fMapTransfer(
IN OUT PVOID *CurrentVa,
IN OUT PULONG BytesRemaining,
OUT PULONG BytesMapped
)
/*++
This routine is used instead of IoMapTransfer. It works directly on
a buffer, returning one page at a time.
--*/
{
ULONG returnAddress;
//
// Get Physical address, and figure out number
// of bytes to the end of the page.
//
returnAddress = MmGetPhysicalAddress(*CurrentVa);
*BytesMapped = PAGE_SIZE - BYTE_OFFSET(*CurrentVa);
//
// Fix up return values
//
if(*BytesMapped > *BytesRemaining)
{
*BytesMapped = *BytesRemaining;
}
*BytesRemaining -= *BytesMapped;
*CurrentVa = (PVOID)((ULONG)*CurrentVa + *BytesMapped);
return returnAddress;
}