You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1365 lines
43 KiB
1365 lines
43 KiB
/*++
|
|
|
|
Copyright (c) 1999, 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
async.c
|
|
|
|
Abstract:
|
|
|
|
miniport transfer code for control, bulk and interrupt
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
|
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
|
|
PURPOSE.
|
|
|
|
Copyright (c) 1999, 2000 Microsoft Corporation. All Rights Reserved.
|
|
|
|
|
|
Revision History:
|
|
|
|
7-20-00 : created, jsenior
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
|
|
//implements the following miniport functions:
|
|
|
|
// non paged
|
|
//UhciInsertQh
|
|
//UhciUnlinkQh
|
|
//UhciMapAsyncTransferToTds
|
|
//UhciQueueTransfer
|
|
//UhciControlTransfer
|
|
//UhciBulkOrInterruptTransfer
|
|
//UhciSetAsyncEndpointState
|
|
//UhciProcessDoneAsyncTd
|
|
//UhciPollAsyncEndpoint
|
|
//UhciAbortAsyncTransfer
|
|
|
|
|
|
VOID
|
|
UhciFixDataToggle(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PHCD_TRANSFER_DESCRIPTOR Td,
|
|
ULONG Toggle
|
|
)
|
|
{
|
|
LOGENTRY(DeviceData, G, '_Fdt', EndpointData, Toggle, 0);
|
|
|
|
//
|
|
// Loop through all the remaining TDs for this
|
|
// endpoint and fix the data toggle.
|
|
//
|
|
while (Td) {
|
|
Td->HwTD.Token.DataToggle = Toggle;
|
|
Toggle = !Toggle;
|
|
Td = Td->NextTd;
|
|
}
|
|
|
|
EndpointData->Toggle = Toggle;
|
|
}
|
|
|
|
|
|
VOID
|
|
UhciInsertQh(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN PHCD_QUEUEHEAD_DESCRIPTOR FirstQh,
|
|
IN PHCD_QUEUEHEAD_DESCRIPTOR LinkQh
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Insert an aync queue head into the HW list.
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR nextQh;
|
|
QH_LINK_POINTER newLink;
|
|
|
|
LOGENTRY(DeviceData, G, '_Ain', 0, FirstQh, LinkQh);
|
|
UHCI_ASSERT(DeviceData, !TEST_FLAG(LinkQh->QhFlags, UHCI_QH_FLAG_IN_SCHEDULE));
|
|
|
|
// ASYNC QUEUE looks like this:
|
|
//
|
|
//
|
|
// |- we insert here
|
|
//|static QH|<->|xfer QH|<->|xfer QH|<->
|
|
// | |
|
|
// ---------------<->--------------
|
|
|
|
// link new qh to the current 'head' ie
|
|
// first transfer QH
|
|
nextQh = FirstQh->NextQh;
|
|
|
|
LinkQh->HwQH.HLink = FirstQh->HwQH.HLink;
|
|
LinkQh->NextQh = nextQh;
|
|
LinkQh->PrevQh = FirstQh;
|
|
|
|
if (nextQh) {
|
|
nextQh->PrevQh = LinkQh;
|
|
} else {
|
|
|
|
// This is the last queuehead. I.e. a bulk queuehead.
|
|
UHCI_ASSERT(DeviceData,
|
|
(LinkQh->HwQH.HLink.HwAddress & ~HW_LINK_FLAGS_MASK) ==
|
|
DeviceData->BulkQueueHead->PhysicalAddress);
|
|
DeviceData->LastBulkQueueHead = LinkQh;
|
|
}
|
|
|
|
// put the new qh at the head of the queue
|
|
newLink.HwAddress = LinkQh->PhysicalAddress;
|
|
newLink.QHTDSelect = 1;
|
|
UHCI_ASSERT(DeviceData, !newLink.Terminate);
|
|
UHCI_ASSERT(DeviceData, !newLink.Reserved);
|
|
FirstQh->HwQH.HLink = newLink;
|
|
FirstQh->NextQh = LinkQh;
|
|
|
|
SET_FLAG(LinkQh->QhFlags, UHCI_QH_FLAG_IN_SCHEDULE);
|
|
}
|
|
|
|
VOID
|
|
UhciUnlinkQh(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN PHCD_QUEUEHEAD_DESCRIPTOR Qh
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove an async queue head from the HW list.
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR nextQh, prevQh;
|
|
|
|
UHCI_ASSERT(DeviceData,
|
|
TEST_FLAG(Qh->QhFlags, UHCI_QH_FLAG_IN_SCHEDULE) ||
|
|
((Qh->PrevQh == Qh) && (Qh->NextQh == Qh)));
|
|
|
|
nextQh = Qh->NextQh;
|
|
prevQh = Qh->PrevQh;
|
|
|
|
// ASYNC QUEUE looks like this:
|
|
//
|
|
//|static QH|->|xfer QH|->|xfer QH|->
|
|
// | |
|
|
// -------------<----------------
|
|
|
|
//
|
|
// Check if this was the last bulk transfer. If so,
|
|
// turn off the bulk bandwidth reclamation.
|
|
//
|
|
if (DeviceData->LastBulkQueueHead == Qh) {
|
|
DeviceData->LastBulkQueueHead = prevQh;
|
|
}
|
|
|
|
// unlink
|
|
LOGENTRY(DeviceData, G, '_Ulk', Qh, prevQh, nextQh);
|
|
prevQh->HwQH.HLink = Qh->HwQH.HLink;
|
|
prevQh->NextQh = nextQh;
|
|
if (nextQh) {
|
|
nextQh->PrevQh = prevQh;
|
|
}
|
|
|
|
// Protect ourselves from calling this function twice.
|
|
Qh->NextQh = Qh->PrevQh = Qh;
|
|
|
|
//
|
|
// If this was a bulk QH, check if bulk bandwidth reclamation
|
|
// is turned on. If so and there's nothing queued, then turn
|
|
// it off. This is for the case where a device has become
|
|
// unresponsive and the transfer is about to be aborted.
|
|
//
|
|
if (Qh->EndpointData->Parameters.TransferType == Bulk &&
|
|
!DeviceData->LastBulkQueueHead->HwQH.HLink.Terminate) {
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
BOOLEAN activeBulkTDs = FALSE;
|
|
|
|
//
|
|
// This loop skips the td that has been inserted for
|
|
// the PIIX4 problem, since it starts with the qh
|
|
// the bulk queuehead is pointing at.
|
|
// If the bulk queuehead is not pointing at anything,
|
|
// then we're fine too, since it will have been
|
|
// turned off already.
|
|
//
|
|
for (qh = DeviceData->BulkQueueHead->NextQh;
|
|
qh;
|
|
qh = qh->NextQh) {
|
|
if (!qh->HwQH.VLink.Terminate) {
|
|
activeBulkTDs = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// qh is pointing at either the first queuehead
|
|
// with transfers pending or the bulk queuehead.
|
|
if (!activeBulkTDs) {
|
|
UHCI_ASSERT(DeviceData, !qh)
|
|
DeviceData->LastBulkQueueHead->HwQH.HLink.Terminate = 1;
|
|
}
|
|
}
|
|
|
|
CLEAR_FLAG(Qh->QhFlags, UHCI_QH_FLAG_IN_SCHEDULE);
|
|
}
|
|
|
|
VOID
|
|
UhciQueueTransfer(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN PENDPOINT_DATA EndpointData,
|
|
IN PHCD_TRANSFER_DESCRIPTOR FirstTd,
|
|
IN PHCD_TRANSFER_DESCRIPTOR LastTd
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Links a bunch of TDs into a queuehead.
|
|
|
|
Arguments:
|
|
|
|
--*/
|
|
{
|
|
UHCI_ASSERT(DeviceData, FirstTd->PhysicalAddress & ~HW_LINK_FLAGS_MASK);
|
|
UHCI_ASSERT(DeviceData, !(FirstTd->PhysicalAddress & HW_LINK_FLAGS_MASK));
|
|
|
|
if (EndpointData->HeadTd) {
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
HW_32BIT_PHYSICAL_ADDRESS curTdPhys;
|
|
|
|
// There's other transfer(s) queued. Add this one behind them.
|
|
UHCI_ASSERT(DeviceData, EndpointData->TailTd);
|
|
EndpointData->TailTd->NextTd = FirstTd;
|
|
EndpointData->TailTd->HwTD.LinkPointer.HwAddress =
|
|
FirstTd->PhysicalAddress;
|
|
|
|
// Get the qh and current td
|
|
qh = EndpointData->QueueHead;
|
|
|
|
curTdPhys = qh->HwQH.VLink.HwAddress & ~HW_LINK_FLAGS_MASK;
|
|
|
|
// If there is nothing on this queuehead, then we may have been
|
|
// unsuccessful in queueing the transfer. Checking the active
|
|
// bit on the td will tell us for sure.
|
|
|
|
LOGENTRY(DeviceData, G, '_tqa', FirstTd, curTdPhys,
|
|
FirstTd->HwTD.Control.Active);
|
|
|
|
LOGENTRY(DeviceData, G, '_ttd', EndpointData->TailTd,
|
|
EndpointData->TailTd->PhysicalAddress,
|
|
EndpointData->TailTd->HwTD.Control.Active);
|
|
|
|
if (FirstTd->HwTD.Control.Active) {
|
|
if ((curTdPhys == EndpointData->TailTd->PhysicalAddress &&
|
|
!EndpointData->TailTd->HwTD.Control.Active)) {
|
|
TD_LINK_POINTER newLink;
|
|
|
|
// Since the prior transfer had already completed when
|
|
// we tried to queue the transfer, we need to add this td
|
|
// directly into the hardware queuehead.
|
|
|
|
// Note that the HC could be in the middle of updating the
|
|
// queuehead's link pointer. That's what the second part of
|
|
// the if statement above is for.
|
|
|
|
// DO NOT call LOGENTRY until we set the queuehead!
|
|
// This would cause a delay that might be bad.
|
|
|
|
newLink.HwAddress = FirstTd->PhysicalAddress;
|
|
newLink.Terminate = 0;
|
|
newLink.QHTDSelect = 0;
|
|
qh->HwQH.VLink = newLink;
|
|
LOGENTRY(DeviceData, G, '_nlk', FirstTd, EndpointData,
|
|
EndpointData->HeadTd);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
// There's no other transfers queued currently.
|
|
SET_QH_TD(DeviceData, EndpointData, FirstTd);
|
|
}
|
|
if (EndpointData->Parameters.TransferType == Bulk) {
|
|
|
|
// Turn bulk bandwidth reclamation back on.
|
|
DeviceData->LastBulkQueueHead->HwQH.HLink.Terminate = 0;
|
|
}
|
|
EndpointData->TailTd = LastTd;
|
|
}
|
|
|
|
ULONG
|
|
UhciMapAsyncTransferToTds(
|
|
PDEVICE_DATA DeviceData,
|
|
PENDPOINT_DATA EndpointData,
|
|
PTRANSFER_CONTEXT TransferContext,
|
|
PHCD_TRANSFER_DESCRIPTOR *FirstDataTd,
|
|
PHCD_TRANSFER_DESCRIPTOR *LastDataTd,
|
|
PTRANSFER_SG_LIST SgList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Maps an asynchronous transfer into the TDs
|
|
required to complete the transfer, including
|
|
any double buffering necessary for page boundaries.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
// indices and offsets
|
|
ULONG sgIdx, sgOffset, i;
|
|
// lengths
|
|
ULONG lengthThisTd, bytesRemaining, mappedNextSg, lengthMapped = 0;
|
|
USHORT maxPacketSize = EndpointData->Parameters.MaxPacketSize;
|
|
// structure pointers
|
|
PTRANSFER_PARAMETERS tp = TransferContext->TransferParameters;
|
|
PASYNC_TRANSFER_BUFFER buffer = NULL;
|
|
PHCD_TRANSFER_DESCRIPTOR lastTd = NULL, td;
|
|
HW_32BIT_PHYSICAL_ADDRESS address;
|
|
UCHAR pid;
|
|
|
|
ULONG toggle;
|
|
BOOLEAN pageCrossing = FALSE;
|
|
BOOLEAN ZeroLengthTransfer = (SgList->SgCount == 0 &&
|
|
EndpointData->Parameters.TransferType != Control);
|
|
|
|
if (EndpointData->Parameters.TransferType == Control) {
|
|
|
|
// Control pipes are bi-directional. Get the direction from the
|
|
// transfer parameters.
|
|
if (TEST_FLAG(tp->TransferFlags, USBD_TRANSFER_DIRECTION_IN)) {
|
|
pid = InPID;
|
|
} else {
|
|
pid = OutPID;
|
|
}
|
|
// THe setup packet is Toggle 0.
|
|
toggle = DataToggle1;
|
|
} else {
|
|
|
|
// All other pipes are uni-directional. Determine
|
|
// the direction from the endpoint address.
|
|
pid = GetPID(EndpointData->Parameters.EndpointAddress);
|
|
// We have to continue the toggle pattern for bulk and interrupt.
|
|
toggle = EndpointData->Toggle;
|
|
}
|
|
// lastTd points to the last data TD or the setup
|
|
// if there was no data.
|
|
|
|
for (i = 0; i<SgList->SgCount || ZeroLengthTransfer; i++) {
|
|
|
|
LOGENTRY(DeviceData, G, '_sgc', SgList->SgCount, i, 0);
|
|
|
|
address = SgList->SgEntry[i].LogicalAddress.Hw32;
|
|
UHCI_ASSERT(DeviceData, address || ZeroLengthTransfer);
|
|
bytesRemaining = SgList->SgEntry[i].Length;
|
|
UHCI_ASSERT(DeviceData, bytesRemaining || ZeroLengthTransfer);
|
|
|
|
LOGENTRY(DeviceData, G, '_sgX', SgList->SgEntry[i].Length, i,
|
|
SgList->SgEntry[i].LogicalAddress.Hw32);
|
|
|
|
if (pageCrossing) {
|
|
|
|
// We have a page crossing here, so this one is double-buffered.
|
|
address += mappedNextSg;
|
|
bytesRemaining -= mappedNextSg;
|
|
}
|
|
mappedNextSg = 0;
|
|
pageCrossing = FALSE;
|
|
while (bytesRemaining || ZeroLengthTransfer) {
|
|
ZeroLengthTransfer = FALSE;
|
|
LOGENTRY(DeviceData, G, '_sg1', bytesRemaining, 0, 0);
|
|
if (bytesRemaining < maxPacketSize) {
|
|
if (i+1 < SgList->SgCount) {
|
|
|
|
// We have to double buffer this TD since it crosses a page
|
|
// boundary. We will always cross a page boundary now.
|
|
LOGENTRY(DeviceData, G, '_sg2', bytesRemaining, 0, 0);
|
|
pageCrossing = TRUE;
|
|
if (SgList->SgEntry[i+1].Length + bytesRemaining >= maxPacketSize) {
|
|
mappedNextSg = maxPacketSize - bytesRemaining;
|
|
lengthThisTd = maxPacketSize;
|
|
} else {
|
|
lengthThisTd = SgList->SgEntry[i+1].Length + bytesRemaining;
|
|
mappedNextSg = SgList->SgEntry[i+1].Length;
|
|
}
|
|
|
|
buffer = (PASYNC_TRANSFER_BUFFER)
|
|
UHCI_ALLOC_DB(DeviceData, EndpointData, FALSE);
|
|
UHCI_ASSERT(DeviceData, buffer);
|
|
UHCI_ASSERT(DeviceData, buffer->Sig == SIG_HCD_ADB);
|
|
UHCI_ASSERT(DeviceData, buffer->PhysicalAddress);
|
|
buffer->SystemAddress = SgList->MdlSystemAddress + lengthMapped;
|
|
UhciKdPrint((DeviceData, 2, "'Double buffer %x address %x offset %x\n", buffer, buffer->SystemAddress, lengthMapped));
|
|
buffer->Size = lengthThisTd;
|
|
UHCI_ASSERT(DeviceData, lengthThisTd <= MAX_ASYNC_PACKET_SIZE);
|
|
if (OutPID == pid) {
|
|
RtlCopyMemory(&buffer->Buffer[0],
|
|
buffer->SystemAddress,
|
|
lengthThisTd);
|
|
}
|
|
// Change the address for the TD
|
|
address = buffer->PhysicalAddress;
|
|
bytesRemaining = 0;
|
|
} else {
|
|
|
|
// Last TD
|
|
LOGENTRY(DeviceData, G, '_sg3', bytesRemaining, 0, 0);
|
|
lengthThisTd = bytesRemaining;
|
|
bytesRemaining = 0;
|
|
}
|
|
} else {
|
|
|
|
// Normal, non-buffered case.
|
|
LOGENTRY(DeviceData, G, '_sg4', bytesRemaining, 0, 0);
|
|
lengthThisTd = maxPacketSize;
|
|
bytesRemaining -= lengthThisTd;
|
|
|
|
UHCI_ASSERT(DeviceData, lengthThisTd <= SgList->SgEntry[i].Length);
|
|
}
|
|
|
|
TransferContext->PendingTds++;
|
|
|
|
//
|
|
// Allocate and initialize an async TD
|
|
//
|
|
td = UHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
INITIALIZE_TD_FOR_TRANSFER(td, TransferContext);
|
|
|
|
td->HwTD.Token.Pid = pid;
|
|
td->HwTD.Token.MaximumLength = MAXIMUM_LENGTH(lengthThisTd);
|
|
td->HwTD.Token.DataToggle = toggle;
|
|
td->HwTD.Control.ShortPacketDetect = 1;
|
|
td->HwTD.Control.ActualLength = MAXIMUM_LENGTH(0);
|
|
td->HwTD.Buffer = address;
|
|
if (pageCrossing) {
|
|
SET_FLAG(td->Flags, TD_FLAG_DOUBLE_BUFFERED);
|
|
td->DoubleBuffer = (PTRANSFER_BUFFER) buffer;
|
|
}
|
|
|
|
address += lengthThisTd;
|
|
lengthMapped += lengthThisTd;
|
|
|
|
if (lastTd) {
|
|
SET_NEXT_TD(lastTd, td);
|
|
} else {
|
|
*FirstDataTd = td;
|
|
}
|
|
lastTd = td;
|
|
toggle = !toggle;
|
|
} // while
|
|
}
|
|
|
|
*LastDataTd = lastTd;
|
|
EndpointData->Toggle = toggle;
|
|
|
|
UHCI_ASSERT(DeviceData, TransferContext->TransferParameters->TransferBufferLength == lengthMapped);
|
|
|
|
return lengthMapped;
|
|
}
|
|
|
|
USB_MINIPORT_STATUS
|
|
UhciControlTransfer(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN PENDPOINT_DATA EndpointData,
|
|
IN PTRANSFER_PARAMETERS TransferParameters,
|
|
IN PTRANSFER_CONTEXT TransferContext,
|
|
IN PTRANSFER_SG_LIST TransferSGList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize a control transfer
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHCD_TRANSFER_DESCRIPTOR lastDataTd, firstDataTd, setupTd, statusTd;
|
|
PASYNC_TRANSFER_BUFFER setupPacket;
|
|
ULONG lengthMapped, dataTDCount = 0;
|
|
|
|
// we have enough tds, program the transfer
|
|
|
|
UhciKdPrint((DeviceData, 2, "'Control transfer on EP %x\n", EndpointData));
|
|
|
|
LOGENTRY(DeviceData, G, '_CTR', EndpointData, TransferParameters, TransferContext);
|
|
|
|
// bugbug should check here in advance to see if there enough
|
|
// TDs if so proceed otherwise return status_busy.
|
|
if (EndpointData->PendingTransfers > 1) {
|
|
DecPendingTransfers(DeviceData, EndpointData);
|
|
return USBMP_STATUS_BUSY;
|
|
}
|
|
|
|
// First prepare a TD for the setup packet. Grab the dummy TD from
|
|
// the tail of the queue.
|
|
TransferContext->PendingTds++;
|
|
setupTd = UHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
INITIALIZE_TD_FOR_TRANSFER(setupTd, TransferContext);
|
|
|
|
// Move setup data into TD (8 chars long).
|
|
// We use a double buffer for this.
|
|
setupTd->DoubleBuffer = UHCI_ALLOC_DB(DeviceData, EndpointData, FALSE);
|
|
setupPacket = (PASYNC_TRANSFER_BUFFER) setupTd->DoubleBuffer;
|
|
RtlCopyMemory(&setupPacket->Buffer[0],
|
|
&TransferParameters->SetupPacket[0],
|
|
8);
|
|
setupTd->HwTD.Buffer = setupPacket->PhysicalAddress;
|
|
SET_FLAG(setupTd->Flags, TD_FLAG_DOUBLE_BUFFERED);
|
|
|
|
setupTd->HwTD.Token.MaximumLength = MAXIMUM_LENGTH(8);
|
|
setupTd->HwTD.Token.Pid = SetupPID;
|
|
// setup stage is always toggle 0
|
|
setupTd->HwTD.Token.DataToggle = DataToggle0;
|
|
|
|
LOGENTRY(DeviceData,
|
|
G, '_set',
|
|
setupTd,
|
|
*((PLONG) &TransferParameters->SetupPacket[0]),
|
|
*((PLONG) &TransferParameters->SetupPacket[4]));
|
|
|
|
// allocate the status phase TD now so we can
|
|
// point the data TDs to it
|
|
TransferContext->PendingTds++;
|
|
statusTd = UHCI_ALLOC_TD(DeviceData, EndpointData);
|
|
INITIALIZE_TD_FOR_TRANSFER(statusTd, TransferContext);
|
|
|
|
// now setup the data phase
|
|
lastDataTd = firstDataTd = NULL;
|
|
lengthMapped =
|
|
UhciMapAsyncTransferToTds(DeviceData,
|
|
EndpointData,
|
|
TransferContext,
|
|
&firstDataTd,
|
|
&lastDataTd,
|
|
TransferSGList);
|
|
|
|
if (firstDataTd && firstDataTd) {
|
|
|
|
// Join the setup to the front and the status to the end.
|
|
SET_NEXT_TD(setupTd, firstDataTd);
|
|
SET_NEXT_TD(lastDataTd, statusTd);
|
|
} else {
|
|
|
|
// Join the setup to the status. No data stage.
|
|
SET_NEXT_TD(setupTd, statusTd);
|
|
}
|
|
|
|
// now do the status phase
|
|
|
|
// no bufferQueueHead
|
|
statusTd->HwTD.Buffer = 0;
|
|
statusTd->HwTD.Token.MaximumLength = MAXIMUM_LENGTH(0);
|
|
// status stage is always toggle 1
|
|
statusTd->HwTD.Token.DataToggle = DataToggle1;
|
|
statusTd->HwTD.Control.InterruptOnComplete = 1;
|
|
SET_FLAG(statusTd->Flags, TD_FLAG_STATUS_TD);
|
|
|
|
// status phase is opposite data dirrection
|
|
if (TEST_FLAG(TransferParameters->TransferFlags, USBD_TRANSFER_DIRECTION_IN)) {
|
|
statusTd->HwTD.Token.Pid = OutPID;
|
|
} else {
|
|
statusTd->HwTD.Token.Pid = InPID;
|
|
}
|
|
|
|
SET_NEXT_TD_NULL(statusTd);
|
|
|
|
// put the request on the hardware queue
|
|
LOGENTRY(DeviceData, G,
|
|
'_Tal', TransferContext->PendingTds, setupTd->PhysicalAddress, setupTd);
|
|
|
|
// Attach the setup TD to the queuehead
|
|
UhciQueueTransfer(DeviceData, EndpointData, setupTd, statusTd);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
USB_MINIPORT_STATUS
|
|
UhciBulkOrInterruptTransfer(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN PENDPOINT_DATA EndpointData,
|
|
IN PTRANSFER_PARAMETERS TransferParameters,
|
|
IN PTRANSFER_CONTEXT TransferContext,
|
|
IN PTRANSFER_SG_LIST TransferSGList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize interrupt or bulk Transfer
|
|
|
|
Arguments:
|
|
|
|
|
|
--*/
|
|
{
|
|
PHCD_TRANSFER_DESCRIPTOR firstTd, lastTd;
|
|
ULONG lengthMapped;
|
|
ULONG maxPacketSize = EndpointData->Parameters.MaxPacketSize;
|
|
ULONG i, numTds;
|
|
|
|
UhciKdPrint((DeviceData, 2, "'BIT transfer on EP %x\n", EndpointData));
|
|
UhciKdPrint((DeviceData, 2, "'BIT transfer length %d\n",
|
|
TransferParameters->TransferBufferLength));
|
|
|
|
LOGENTRY(DeviceData, G, '_BIT', EndpointData, TransferParameters, TransferContext);
|
|
|
|
// Do we have enough free resources?
|
|
for (i = 0, lengthMapped = 0; i < TransferSGList->SgCount; i++) {
|
|
lengthMapped += TransferSGList->SgEntry[i].Length;
|
|
}
|
|
numTds = lengthMapped == 0 ? 1 :
|
|
(lengthMapped + maxPacketSize - 1) / maxPacketSize;
|
|
if (EndpointData->TdCount - EndpointData->TdsUsed < numTds) {
|
|
|
|
// Not enough TDs to do this transfer yet.
|
|
// Tell the port driver to wait.
|
|
UhciKdPrint((DeviceData, 2, "'BIT must wait on EP %x. Not enough tds.\n", EndpointData));
|
|
return USBMP_STATUS_BUSY;
|
|
}
|
|
if (TransferSGList->SgCount > 1 &&
|
|
TransferSGList->SgEntry[0].Length % maxPacketSize != 0) {
|
|
|
|
// We'll need DBs. Do we have enough?
|
|
if (EndpointData->DbCount - EndpointData->DbsUsed <
|
|
(lengthMapped + PAGE_SIZE - 1)/PAGE_SIZE) {
|
|
|
|
// Not enough DBs to do this transfer yet.
|
|
// Tell the port driver to wait.
|
|
UhciKdPrint((DeviceData, 2, "'BIT must wait on EP %x. Not enough dbs.\n", EndpointData));
|
|
return USBMP_STATUS_BUSY;
|
|
}
|
|
}
|
|
|
|
// we have enough tds, program the transfer
|
|
// now setup the data phase
|
|
lastTd = firstTd = NULL;
|
|
lengthMapped =
|
|
UhciMapAsyncTransferToTds(DeviceData,
|
|
EndpointData,
|
|
TransferContext,
|
|
&firstTd,
|
|
&lastTd,
|
|
TransferSGList);
|
|
|
|
UHCI_ASSERT(DeviceData, lastTd && firstTd);
|
|
|
|
lastTd->HwTD.Control.InterruptOnComplete = 1;
|
|
|
|
SET_NEXT_TD_NULL(lastTd);
|
|
|
|
// put the request on the hardware queue
|
|
LOGENTRY(DeviceData, G,
|
|
'_Tal', TransferContext->PendingTds, firstTd->PhysicalAddress, firstTd);
|
|
|
|
// Attach the first TD to the queuehead
|
|
UhciQueueTransfer(DeviceData, EndpointData, firstTd, lastTd);
|
|
|
|
return USBMP_STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
UhciSetAsyncEndpointState(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN PENDPOINT_DATA EndpointData,
|
|
IN MP_ENDPOINT_STATE State
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
ENDPOINT_TRANSFER_TYPE epType;
|
|
ULONG interruptQHIndex;
|
|
|
|
LOGENTRY(DeviceData, G, '_Sas', EndpointData, State, 0);
|
|
|
|
qh = EndpointData->QueueHead;
|
|
|
|
epType = EndpointData->Parameters.TransferType;
|
|
|
|
switch(State) {
|
|
case ENDPOINT_ACTIVE:
|
|
switch (epType) {
|
|
case Interrupt:
|
|
// put queue head in the schedule
|
|
interruptQHIndex = EndpointData->Parameters.ScheduleOffset +
|
|
QH_INTERRUPT_INDEX(EndpointData->Parameters.Period);
|
|
UhciInsertQh(DeviceData,
|
|
DeviceData->InterruptQueueHeads[interruptQHIndex],
|
|
qh);
|
|
break;
|
|
case Control:
|
|
// put queue head in the schedule
|
|
UhciInsertQh(DeviceData, DeviceData->ControlQueueHead, qh);
|
|
break;
|
|
case Bulk:
|
|
// put queue head in the schedule
|
|
UhciInsertQh(DeviceData, DeviceData->BulkQueueHead, qh);
|
|
break;
|
|
default:
|
|
TEST_TRAP()
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ENDPOINT_PAUSE:
|
|
// remove queue head from the schedule
|
|
switch (epType) {
|
|
case Interrupt:
|
|
case Bulk:
|
|
case Control:
|
|
//
|
|
// Just flip the active bits at this point.
|
|
//
|
|
UhciUnlinkQh(DeviceData, qh);
|
|
break;
|
|
default:
|
|
TEST_TRAP()
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case ENDPOINT_REMOVE:
|
|
qh->QhFlags |= UHCI_QH_FLAG_QH_REMOVED;
|
|
|
|
switch (epType) {
|
|
case Interrupt:
|
|
case Bulk:
|
|
case Control:
|
|
// remove from the schedule and
|
|
// free bandwidth
|
|
|
|
// free the bw
|
|
// EndpointData->StaticEd->AllocatedBandwidth -=
|
|
// EndpointData->Parameters.Bandwidth;
|
|
|
|
UhciUnlinkQh(DeviceData, qh);
|
|
break;
|
|
default:
|
|
TEST_TRAP();
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
TEST_TRAP();
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
UhciProcessDoneAsyncTd(
|
|
PDEVICE_DATA DeviceData,
|
|
PHCD_TRANSFER_DESCRIPTOR Td
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
process a completed TD
|
|
|
|
Parameters
|
|
|
|
--*/
|
|
{
|
|
PTRANSFER_CONTEXT transferContext;
|
|
PENDPOINT_DATA endpointData;
|
|
PTRANSFER_PARAMETERS tp;
|
|
USBD_STATUS usbdStatus = USBD_STATUS_SUCCESS;
|
|
ULONG byteCount;
|
|
|
|
transferContext = Td->TransferContext;
|
|
ASSERT_TRANSFER(DeviceData, transferContext);
|
|
|
|
tp = transferContext->TransferParameters;
|
|
transferContext->PendingTds--;
|
|
endpointData = transferContext->EndpointData;
|
|
|
|
if (TEST_FLAG(Td->Flags, TD_FLAG_SKIP)) {
|
|
LOGENTRY(DeviceData, G, '_Ktd', transferContext,
|
|
0,
|
|
Td);
|
|
|
|
goto free_it;
|
|
}
|
|
|
|
if (TEST_FLAG(endpointData->Flags, UHCI_EDFLAG_HALTED)) {
|
|
|
|
// completion status for this TD?
|
|
// since the endpoint halts on error and short packet,
|
|
// the error bits should have been written back to the TD
|
|
// we use these bits to dermine the error
|
|
usbdStatus = UhciGetErrorFromTD(DeviceData,
|
|
Td);
|
|
}
|
|
|
|
LOGENTRY(DeviceData, G, '_Dtd', transferContext,
|
|
usbdStatus,
|
|
Td);
|
|
|
|
// Only count the bytes transferred if we were successful (as per uhcd).
|
|
byteCount = (usbdStatus == USBD_STATUS_SUCCESS) ? ACTUAL_LENGTH(Td->HwTD.Control.ActualLength) : 0;
|
|
|
|
LOGENTRY(DeviceData, G, '_tln', byteCount, 0, 0);
|
|
|
|
if (Td->HwTD.Token.Pid != SetupPID) {
|
|
|
|
// data or status phase of a control transfer or a bulk/int
|
|
// data transfer
|
|
LOGENTRY(DeviceData, G, '_Idt', Td, transferContext, byteCount);
|
|
|
|
transferContext->BytesTransferred += byteCount;
|
|
|
|
}
|
|
|
|
// For double buffered transfers, we now have to copy back
|
|
// if this was an IN transfer.
|
|
//
|
|
if (Td->HwTD.Token.Pid == InPID &&
|
|
TEST_FLAG(Td->Flags, TD_FLAG_DOUBLE_BUFFERED)) {
|
|
PASYNC_TRANSFER_BUFFER buffer = &Td->DoubleBuffer->Async;
|
|
UHCI_ASSERT(DeviceData, TEST_FLAG(buffer->Flags, DB_FLAG_BUSY));
|
|
UhciKdPrint((DeviceData, 2, "'Copy back %x address %x\n", buffer, buffer->SystemAddress));
|
|
RtlCopyMemory(buffer->SystemAddress,
|
|
buffer->Buffer,
|
|
buffer->Size);
|
|
|
|
// tell usbport we double buffered so it can
|
|
// triple buffer if necessary
|
|
USBPORT_NOTIFY_DOUBLEBUFFER(DeviceData,
|
|
tp,
|
|
buffer->SystemAddress,
|
|
buffer->Size);
|
|
}
|
|
|
|
// note that we only set transferContext->UsbdStatus
|
|
// if we find a TD with an error this will cause us to
|
|
// record the last TD with an error as the error for
|
|
// the transfer.
|
|
if (USBD_STATUS_SUCCESS != usbdStatus) {
|
|
|
|
UhciKdPrint((DeviceData, 2, "'Error, usbdstatus %x", usbdStatus));
|
|
|
|
// map the error to code in USBDI.H
|
|
transferContext->UsbdStatus = usbdStatus;
|
|
|
|
LOGENTRY(DeviceData, G, '_tER', transferContext->UsbdStatus, 0, 0);
|
|
}
|
|
|
|
free_it:
|
|
|
|
// mark the TD free
|
|
UHCI_FREE_TD(DeviceData, endpointData, Td);
|
|
|
|
if (transferContext->PendingTds == 0) {
|
|
|
|
// all TDs for this transfer are done
|
|
// clear the HAVE_TRANSFER flag to indicate
|
|
// we can take another
|
|
DecPendingTransfers(DeviceData, endpointData);
|
|
|
|
LOGENTRY(DeviceData, G, '_Cat',
|
|
transferContext->UsbdStatus,
|
|
transferContext,
|
|
transferContext->BytesTransferred);
|
|
|
|
UhciKdPrint((DeviceData, 2, "'Complete transfer w/ usbdstatus %x\n", transferContext->UsbdStatus));
|
|
|
|
USBPORT_COMPLETE_TRANSFER(DeviceData,
|
|
endpointData,
|
|
tp,
|
|
transferContext->UsbdStatus,
|
|
transferContext->BytesTransferred);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
UhciPollAsyncEndpoint(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN PENDPOINT_DATA EndpointData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when the endpoint 'needs attention'
|
|
|
|
The goal here is to determine which TDs, if any,
|
|
have completed and complete any associated transfers.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
PHCD_TRANSFER_DESCRIPTOR td, currentTd;
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
HW_QUEUE_ELEMENT_TD overlay;
|
|
HW_32BIT_PHYSICAL_ADDRESS curTdPhys, tmpPhys;
|
|
ULONG i, j;
|
|
PTRANSFER_CONTEXT transferContext, tmp;
|
|
PTRANSFER_PARAMETERS tp;
|
|
ULONG halted, active;
|
|
BOOLEAN processed;
|
|
|
|
if (TEST_FLAG(EndpointData->Flags, UHCI_EDFLAG_HALTED)) {
|
|
|
|
// Endpoint is halted. Don't do anything.
|
|
return;
|
|
}
|
|
|
|
// get the queue head and current td
|
|
qh = EndpointData->QueueHead;
|
|
|
|
curTdPhys = qh->HwQH.VLink.HwAddress;
|
|
|
|
curTdPhys &= ~HW_LINK_FLAGS_MASK;
|
|
|
|
// now convert the physical 'current' to a virtual address
|
|
currentTd = curTdPhys ? (PHCD_TRANSFER_DESCRIPTOR)
|
|
USBPORT_PHYSICAL_TO_VIRTUAL(curTdPhys,
|
|
DeviceData,
|
|
EndpointData) :
|
|
(PHCD_TRANSFER_DESCRIPTOR) NULL;
|
|
|
|
LOGENTRY(DeviceData, G, '_ctd', curTdPhys, currentTd, EndpointData);
|
|
|
|
// walk the TD list up to the current TD and complete
|
|
// all those TDs
|
|
|
|
for (td = EndpointData->HeadTd; td != currentTd && td; td = td->NextTd) {
|
|
SET_FLAG(td->Flags, TD_FLAG_DONE);
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
|
|
// Is the queuehead pointing to nothing, but there are still
|
|
// tds available to be queued?
|
|
if (td->NextTd &&
|
|
td->NextTd->HwTD.Control.Active) {
|
|
if (!curTdPhys) {
|
|
TD_LINK_POINTER newLink;
|
|
|
|
// A transfer didn't make it onto the hardware because
|
|
// the queuehead's td field wasn't set properly
|
|
// in UhciQueueTransfer.
|
|
|
|
// PERF NOTE: Because we're not making sure that the
|
|
// transfer gets queued immediately, the transfer could
|
|
// be delayed in making it onto the hardware. Better
|
|
// late than never, though...
|
|
|
|
EndpointData->HeadTd = currentTd = td->NextTd;
|
|
|
|
LOGENTRY(DeviceData, G, '_Dly', currentTd, curTdPhys, qh);
|
|
|
|
goto UhciPollAsyncEndpointSetNext;
|
|
} else if (curTdPhys != td->NextTd->PhysicalAddress) {
|
|
LOGENTRY(DeviceData, G, '_QEr', curTdPhys, td->NextTd->PhysicalAddress, td->NextTd);
|
|
|
|
UHCI_ASSERT (DeviceData, FALSE);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
EndpointData->HeadTd = currentTd;
|
|
|
|
if (currentTd) {
|
|
LOGENTRY(DeviceData, G, '_cTD', currentTd,
|
|
curTdPhys,
|
|
currentTd->TransferContext);
|
|
|
|
// If active, get out of here.
|
|
if (currentTd->HwTD.Control.Active) {
|
|
;// fall thru to completing whatever's completed;
|
|
} else if ((currentTd->HwTD.Token.Pid == InPID) &&
|
|
(currentTd->HwTD.Control.Stalled == 1) &&
|
|
(currentTd->HwTD.Control.BabbleDetected == 0) &&
|
|
(currentTd->HwTD.Control.NAKReceived == 0) &&
|
|
(currentTd->HwTD.Control.TimeoutCRC == 1) &&
|
|
(currentTd->HwTD.Control.BitstuffError == 0) &&
|
|
!TEST_FLAG(currentTd->Flags, TD_FLAG_TIMEOUT_ERROR)) {
|
|
|
|
// If this is the first time that the device or hc has been
|
|
// unresponsive, cut it a break and try the transfer again.
|
|
|
|
// Note that we don't check currentTd->HwTD.Control.DataBufferError
|
|
// since a value of:
|
|
// 1 means host controller did not respond to IN data sent by device
|
|
// 0 means device did not NAK IN request.
|
|
|
|
SET_FLAG(currentTd->Flags, TD_FLAG_TIMEOUT_ERROR);
|
|
|
|
currentTd->HwTD.Control.ErrorCount = 3;
|
|
|
|
currentTd->HwTD.Control.Stalled = 0;
|
|
currentTd->HwTD.Control.TimeoutCRC = 0;
|
|
currentTd->HwTD.Control.Active = 1;
|
|
|
|
} else if (currentTd->HwTD.Control.Stalled ||
|
|
currentTd->HwTD.Control.DataBufferError ||
|
|
currentTd->HwTD.Control.BabbleDetected ||
|
|
currentTd->HwTD.Control.TimeoutCRC ||
|
|
currentTd->HwTD.Control.BitstuffError) {
|
|
|
|
SET_FLAG(EndpointData->Flags, UHCI_EDFLAG_HALTED);
|
|
//
|
|
// Error. We need to flush.
|
|
//
|
|
// Flush all completed tds
|
|
//
|
|
// Complete transfer with error.
|
|
// if the endpoint is halted we need to complete
|
|
// the 'current' tarnsfer with an error walk all
|
|
// the tds for the current transfer and mark
|
|
// any that are not done as 'skipped'.
|
|
|
|
UhciKdPrint((DeviceData, 2, "'Error on EP %x\n", EndpointData));
|
|
|
|
LOGENTRY(DeviceData, G, '_erT', qh, currentTd, currentTd->HwTD.Control.ul);
|
|
transferContext = currentTd->TransferContext;
|
|
tp = transferContext->TransferParameters;
|
|
|
|
SET_FLAG(currentTd->Flags, TD_FLAG_DONE);
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
¤tTd->DoneLink);
|
|
// Skip all the remaining TDs in this transfer
|
|
|
|
UHCI_ASSERT(DeviceData, td->TransferContext == transferContext);
|
|
for (td;
|
|
td &&
|
|
td->TransferContext->TransferParameters->SequenceNumber == tp->SequenceNumber;
|
|
td = td->NextTd) {
|
|
|
|
if (!TEST_FLAG(td->Flags, TD_FLAG_DONE)) {
|
|
|
|
LOGENTRY(DeviceData, G, '_skT', qh, 0, td);
|
|
SET_FLAG(td->Flags, (TD_FLAG_DONE | TD_FLAG_SKIP));
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
}
|
|
}
|
|
|
|
if (EndpointData->Parameters.TransferType != Control) {
|
|
|
|
// Loop through all the remaining TDs for this
|
|
// endpoint and fix the data toggle.
|
|
UhciFixDataToggle(
|
|
DeviceData,
|
|
EndpointData,
|
|
td,
|
|
currentTd->HwTD.Token.DataToggle);
|
|
}
|
|
SET_QH_TD(DeviceData, EndpointData, td);
|
|
|
|
} else if (ACTUAL_LENGTH(currentTd->HwTD.Control.ActualLength) <
|
|
ACTUAL_LENGTH(currentTd->HwTD.Token.MaximumLength)) {
|
|
|
|
//
|
|
// Short packet. We need to flush.
|
|
//
|
|
// Flush all completed tds
|
|
//
|
|
// we need to walk all the tds for the current
|
|
// transfer and mark any that are not done as
|
|
// 'skipped'. EXCEPT if the last TD is a status
|
|
// phase of a control transfer, in which case
|
|
// we have to queue that one up.
|
|
//
|
|
tp = currentTd->TransferContext->TransferParameters;
|
|
|
|
UhciKdPrint((DeviceData, 2, "'Short packet on EP %x\n", EndpointData));
|
|
|
|
LOGENTRY(DeviceData, G, '_shP', qh, currentTd, currentTd->HwTD.Control.ul);
|
|
|
|
SET_FLAG(currentTd->Flags, TD_FLAG_DONE);
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
¤tTd->DoneLink);
|
|
|
|
// Skip all the remaining TDs in this transfer up to the status phase
|
|
// If control transfer, queue up the status phase,
|
|
// else go to the next transfer (if there is one).
|
|
for (td;
|
|
td &&
|
|
td->TransferContext->TransferParameters->SequenceNumber == tp->SequenceNumber;
|
|
td = td->NextTd) {
|
|
|
|
if (TEST_FLAG(td->Flags, TD_FLAG_STATUS_TD) &&
|
|
TEST_FLAG(tp->TransferFlags, USBD_SHORT_TRANSFER_OK)) {
|
|
|
|
// Queue up the status phase of the control transfer.
|
|
UHCI_ASSERT(DeviceData, EndpointData->Parameters.TransferType == Control);
|
|
break;
|
|
}
|
|
|
|
if (!TEST_FLAG(td->Flags, TD_FLAG_DONE)) {
|
|
LOGENTRY(DeviceData, G, '_skT', qh, 0, td);
|
|
|
|
SET_FLAG(td->Flags, (TD_FLAG_DONE | TD_FLAG_SKIP));
|
|
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
&td->DoneLink);
|
|
}
|
|
}
|
|
|
|
if (EndpointData->Parameters.TransferType != Control &&
|
|
currentTd->NextTd) {
|
|
|
|
// Loop through all the remaining TDs for this
|
|
// endpoint and fix the data toggle.
|
|
UhciFixDataToggle(
|
|
DeviceData,
|
|
EndpointData,
|
|
td,
|
|
currentTd->NextTd->HwTD.Token.DataToggle);
|
|
}
|
|
|
|
if (!TEST_FLAG(tp->TransferFlags, USBD_SHORT_TRANSFER_OK)) {
|
|
SET_FLAG(EndpointData->Flags, UHCI_EDFLAG_HALTED);
|
|
}
|
|
|
|
// Next transfer or status phase of a control transfer.
|
|
SET_QH_TD(DeviceData, EndpointData, td);
|
|
|
|
} else {
|
|
|
|
// Current td is not active.
|
|
// If we're still pointing to the same td at this point in time,
|
|
// then we're stuck and I have to manually advance the queuehead
|
|
// to the next td.
|
|
LOGENTRY(DeviceData, G, '_nuT', qh, currentTd, td);
|
|
if (curTdPhys == (qh->HwQH.VLink.HwAddress & ~HW_LINK_FLAGS_MASK)) {
|
|
|
|
// HW error. Td pointer for QH is not advancing.
|
|
// Manually advance things.
|
|
SET_FLAG(currentTd->Flags, TD_FLAG_DONE);
|
|
InsertTailList(&EndpointData->DoneTdList,
|
|
¤tTd->DoneLink);
|
|
|
|
EndpointData->HeadTd = currentTd->NextTd;
|
|
qh->HwQH.VLink.HwAddress = currentTd->HwTD.LinkPointer.HwAddress;
|
|
|
|
LOGENTRY(DeviceData, G, '_nu+', qh, currentTd, td);
|
|
}
|
|
}
|
|
} else {
|
|
|
|
// All transfers completed normally
|
|
|
|
UhciPollAsyncEndpointSetNext:
|
|
// Flush all completed tds
|
|
// Complete transfer
|
|
|
|
// set the sw headp to the new current head
|
|
// Next transfer or status phase of a control transfer.
|
|
SET_QH_TD(DeviceData, EndpointData, currentTd);
|
|
}
|
|
|
|
// now flush all completed TDs. Do it in order of completion.
|
|
|
|
while (!IsListEmpty(&EndpointData->DoneTdList)) {
|
|
|
|
PLIST_ENTRY listEntry;
|
|
|
|
listEntry = RemoveHeadList(&EndpointData->DoneTdList);
|
|
|
|
|
|
td = (PHCD_TRANSFER_DESCRIPTOR) CONTAINING_RECORD(
|
|
listEntry,
|
|
struct _HCD_TRANSFER_DESCRIPTOR,
|
|
DoneLink);
|
|
|
|
|
|
if ((td->Flags & (TD_FLAG_XFER | TD_FLAG_DONE)) ==
|
|
(TD_FLAG_XFER | TD_FLAG_DONE)) {
|
|
|
|
UhciProcessDoneAsyncTd(DeviceData, td);
|
|
}
|
|
|
|
}
|
|
#if 0
|
|
// now flush all completed TDs. Do it in order of allocation.
|
|
for (i = (EndpointData->TdsUsed <= (EndpointData->TdLastAllocced+1)) ?
|
|
(EndpointData->TdLastAllocced + 1) - EndpointData->TdsUsed :
|
|
(EndpointData->TdLastAllocced + EndpointData->TdCount + 1) - EndpointData->TdsUsed, j=0;
|
|
j < EndpointData->TdCount;
|
|
j++, i = (i+1 < EndpointData->TdCount) ? i+1 : 0) {
|
|
td = &EndpointData->TdList->Td[i];
|
|
|
|
if ((td->Flags & (TD_FLAG_XFER | TD_FLAG_DONE)) ==
|
|
(TD_FLAG_XFER | TD_FLAG_DONE)) {
|
|
|
|
UhciProcessDoneAsyncTd(DeviceData, td);
|
|
}
|
|
}
|
|
#endif
|
|
// certain types of endpoints do not halt eg control
|
|
// we resume these endpoints here
|
|
if (TEST_FLAG(EndpointData->Flags, UHCI_EDFLAG_NOHALT) &&
|
|
TEST_FLAG(EndpointData->Flags, UHCI_EDFLAG_HALTED)) {
|
|
|
|
LOGENTRY(DeviceData, G, '_clH', qh, 0, 0);
|
|
|
|
UhciSetEndpointStatus(
|
|
DeviceData,
|
|
EndpointData,
|
|
ENDPOINT_STATUS_RUN);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
UhciAbortAsyncTransfer(
|
|
IN PDEVICE_DATA DeviceData,
|
|
IN PENDPOINT_DATA EndpointData,
|
|
IN PTRANSFER_CONTEXT TransferContext,
|
|
OUT PULONG BytesTransferred
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Aborts the specified transfer by freeing all the TDs
|
|
associated with said transfer. The queuehead for this
|
|
transfer will have already been removed from the
|
|
hardware queue when a SetEndpointState (paused) was
|
|
sent by the port driver.
|
|
Note that if another transfer is queued on the same
|
|
endpoint, we need to fix up the list structure. We
|
|
will also fix up any toggle issues on bulk endpoints.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PHCD_TRANSFER_DESCRIPTOR td;
|
|
PHCD_QUEUEHEAD_DESCRIPTOR qh;
|
|
PHCD_TRANSFER_DESCRIPTOR joinTd = NULL;
|
|
BOOLEAN updateHead = FALSE;
|
|
ULONG toggle;
|
|
ULONG i;
|
|
|
|
UhciKdPrint((DeviceData, 2, "'Abort async transfer on EP %x\n", EndpointData));
|
|
|
|
qh = EndpointData->QueueHead;
|
|
|
|
// The endpoint should not be in the schedule
|
|
|
|
LOGENTRY(DeviceData, G, '_Aat', qh, TransferContext, 0);
|
|
UHCI_ASSERT(DeviceData, !TEST_FLAG(qh->QhFlags, UHCI_QH_FLAG_IN_SCHEDULE));
|
|
|
|
// our mission now is to remove all TDs associated with
|
|
// this transfer
|
|
|
|
// get the last known head, we update the head when we process
|
|
// (AKA poll) the endpoint.
|
|
|
|
UHCI_ASSERT(DeviceData, EndpointData->HeadTd);
|
|
|
|
// Find the first TD in the transfer to abort
|
|
for (td = EndpointData->HeadTd; td; td = td->NextTd) {
|
|
if (td->TransferContext == TransferContext) {
|
|
break;
|
|
}
|
|
joinTd = td;
|
|
}
|
|
UHCI_ASSERT(DeviceData, td);
|
|
|
|
// Gonna have to fix up the toggle for bulk.
|
|
toggle = td->HwTD.Token.DataToggle;
|
|
|
|
// Was it the first transfer for this endpoint?
|
|
if (td == EndpointData->HeadTd) {
|
|
|
|
// This was the first queued transfer. Need to update the head.
|
|
updateHead = TRUE;
|
|
}
|
|
|
|
UHCI_ASSERT(DeviceData, td->TransferContext == TransferContext);
|
|
//
|
|
// Loop through all the TDs for this transfer and free
|
|
// them.
|
|
//
|
|
while (td) {
|
|
if (td->TransferContext == TransferContext) {
|
|
LOGENTRY(DeviceData, G, '_abT', qh, 0, td);
|
|
|
|
// if the TD completed we need to track the data
|
|
if (td->HwTD.Control.Active == 0) {
|
|
TEST_TRAP();
|
|
UhciProcessDoneAsyncTd(DeviceData, td);
|
|
} else {
|
|
UHCI_FREE_TD(DeviceData, EndpointData, td);
|
|
}
|
|
} else {
|
|
// We're past the transfer to abort.
|
|
break;
|
|
}
|
|
td = td->NextTd;
|
|
}
|
|
|
|
UhciFixDataToggle(DeviceData, EndpointData, td, toggle);
|
|
|
|
if (updateHead) {
|
|
|
|
// The transfer we removed was the first one.
|
|
SET_QH_TD(DeviceData, EndpointData, td);
|
|
} else {
|
|
|
|
// The transfer we removed was not the first one.
|
|
UHCI_ASSERT(DeviceData, joinTd);
|
|
if (td) {
|
|
|
|
// This was a middle transfer.
|
|
SET_NEXT_TD(joinTd, td);
|
|
} else {
|
|
|
|
// The transfer we removed was the last one.
|
|
EndpointData->TailTd = joinTd;
|
|
SET_NEXT_TD_NULL(joinTd);
|
|
}
|
|
}
|
|
|
|
*BytesTransferred = TransferContext->BytesTransferred;
|
|
}
|