2372 lines
77 KiB
C
2372 lines
77 KiB
C
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
async.c
|
|
|
|
Abstract:
|
|
This module manages interrupts from the HCD
|
|
and contains all Defered Procedure Calls.
|
|
|
|
Environment:
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
1996-03-14: created kenray
|
|
|
|
--*/
|
|
|
|
#include "openhci.h"
|
|
|
|
VOID OpenHCI_CancelTDsForED(PHCD_ENDPOINT_DESCRIPTOR ED);
|
|
VOID OpenHCI_CompleteUsbdTransferRequest(PHCD_URB, USBD_STATUS, NTSTATUS,
|
|
BOOLEAN);
|
|
VOID OpenHCI_ProcessDoneQueue(PHCD_DEVICE_DATA, ULONG);
|
|
BOOLEAN OpenHCI_ProcessDoneAsyncTD(PHCD_DEVICE_DATA,
|
|
PHCD_TRANSFER_DESCRIPTOR,
|
|
PHCD_ENDPOINT,
|
|
PHC_TRANSFER_DESCRIPTOR,
|
|
NTSTATUS *,
|
|
USBD_STATUS *,
|
|
PHCD_URB,
|
|
BOOLEAN);
|
|
|
|
BOOLEAN OpenHCI_ProcessDoneIsoTD(PHCD_DEVICE_DATA,
|
|
PHCD_TRANSFER_DESCRIPTOR,
|
|
PHCD_ENDPOINT,
|
|
PHC_TRANSFER_DESCRIPTOR,
|
|
NTSTATUS *,
|
|
USBD_STATUS *,
|
|
PHCD_URB,
|
|
BOOLEAN);
|
|
|
|
|
|
|
|
BOOLEAN
|
|
OpenHCI_InterruptService(
|
|
IN PKINTERRUPT Interrupt,
|
|
IN VOID *ServiceContext
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Process all interrupts potentially from the USB.
|
|
Stop the Host Controller from signalling interrupt
|
|
and receive all time critical information.
|
|
Currently the master interrupt is disabled (HC will not signal)
|
|
until the DPC is completed.
|
|
|
|
Arguments:
|
|
Ye old standard interrutp arguments.
|
|
We hope that the service context is really a Device Object.
|
|
|
|
Return Value:
|
|
Bool : (Is the interrupt is for us?)
|
|
|
|
--*/
|
|
{
|
|
ULONG ContextInfo, Temp, Frame;
|
|
PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT) ServiceContext;
|
|
PHCD_DEVICE_DATA DeviceData;
|
|
PHC_OPERATIONAL_REGISTER HC;
|
|
|
|
DeviceData = (PHCD_DEVICE_DATA) DeviceObject->DeviceExtension;
|
|
HC = DeviceData->HC;
|
|
|
|
if (DeviceData->CurrentDevicePowerState != PowerDeviceD0) {
|
|
// we cannot be generating interrupts unless we are
|
|
// in D0
|
|
|
|
#ifdef WIN98
|
|
{
|
|
ULONG status;
|
|
|
|
Temp = READ_REGISTER_ULONG (&HC->HcInterruptEnable);
|
|
|
|
status = (READ_REGISTER_ULONG(&HC->HcInterruptStatus) & Temp);
|
|
if (DeviceData->CurrentDevicePowerState == PowerDeviceD2 &&
|
|
status & HcInt_ResumeDetected) {
|
|
goto OHCI_ACK_RESUME;
|
|
}
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef WIN98
|
|
OHCI_ACK_RESUME:
|
|
#endif
|
|
|
|
Temp = READ_REGISTER_ULONG (&HC->HcInterruptEnable);
|
|
|
|
ContextInfo = (READ_REGISTER_ULONG (&HC->HcInterruptStatus) & Temp);
|
|
if (0 == ContextInfo) {
|
|
return FALSE; /* Not our interrupt */
|
|
}
|
|
|
|
if (0xFFFFFFFF == ContextInfo) {
|
|
return FALSE; /* Our device is not there! */
|
|
}
|
|
|
|
if (! (Temp & HcInt_MasterInterruptEnable)) {
|
|
return FALSE;
|
|
}
|
|
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_ISR_NOISE,
|
|
("'take interrupt status: 0x%x\n", ContextInfo));
|
|
|
|
// any interrupt resets the idle counter
|
|
DeviceData->IdleTime = 0;
|
|
|
|
#if 0
|
|
if (DeviceData->HcFlags & HC_FLAG_USE_HYDRA_HACK) {
|
|
//hydra requires us to acknowlege the writeback done queue interrupt
|
|
//here
|
|
|
|
if (ContextInfo & HcInt_WritebackDoneHead) {
|
|
if (ContextInfo & HcInt_StartOfFrame) {
|
|
|
|
//TEST_TRAP();
|
|
// don't ack the done queue int
|
|
ContextInfo &= ~HcInt_WritebackDoneHead;
|
|
|
|
OHCI_ASSERT(DeviceData->LastHccaDoneHead == 0);
|
|
DeviceData->LastHccaDoneHead = DeviceData->HCCA->HccaDoneHead;
|
|
OHCI_ASSERT(DeviceData->LastHccaDoneHead != 0);
|
|
|
|
DeviceData->HCCA->HccaDoneHead = 0;
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptStatus,
|
|
HcInt_WritebackDoneHead); /* ack inq */
|
|
|
|
// enable SOF interrupt
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptEnable,
|
|
HcInt_StartOfFrame);
|
|
} else {
|
|
//TEST_TRAP();
|
|
// enable SOF interrupt
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptEnable,
|
|
HcInt_StartOfFrame);
|
|
// don't ack the done queue int
|
|
ContextInfo &= ~HcInt_WritebackDoneHead;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (DeviceData->HcFlags & HC_FLAG_IDLE) {
|
|
OpenHCI_KdPrint((1, "'interrupt while controller is idle %x\n", ContextInfo));
|
|
#if DBG
|
|
// should only see the rh status change
|
|
if ((ContextInfo & ~(HcInt_RootHubStatusChange | HcInt_ResumeDetected))
|
|
#if FAKEPORTCHANGE
|
|
&& !DeviceData->FakePortChange
|
|
#endif
|
|
)
|
|
{
|
|
TEST_TRAP();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/* Our interrupt, prevent HC from doing it to us again til we're finished */
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptDisable, HcInt_MasterInterruptEnable);
|
|
|
|
DeviceData->CurrentHcControl.ul |= DeviceData->ListEnablesAtNextSOF.ul;
|
|
DeviceData->ListEnablesAtNextSOF.ul = 0;
|
|
WRITE_REGISTER_ULONG(&HC->HcControl.ul, DeviceData->CurrentHcControl.ul);
|
|
|
|
#if 0
|
|
if (HC->HcControlHeadED) {
|
|
WRITE_REGISTER_ULONG(&HC->HcCommandStatus.ul, HcCmd_ControlListFilled);
|
|
}
|
|
if (HC->HcBulkHeadED) {
|
|
WRITE_REGISTER_ULONG(&HC->HcCommandStatus.ul, HcCmd_BulkListFilled);
|
|
}
|
|
#endif
|
|
// Note: The NEC controller is broken, writing a zero to the
|
|
// CommandStatus register for HcCmd_ControlListFilled or
|
|
// HcCmd_BulkListFilled will disable the list (in violation
|
|
// of the OHCI spec)
|
|
{
|
|
ULONG listFilled = 0;
|
|
|
|
Temp = READ_REGISTER_ULONG (&HC->HcControlHeadED);
|
|
if (Temp) {
|
|
listFilled |= HcCmd_ControlListFilled;
|
|
}
|
|
Temp = READ_REGISTER_ULONG (&HC->HcBulkHeadED);
|
|
if (Temp) {
|
|
listFilled |= HcCmd_BulkListFilled;
|
|
}
|
|
//LOGENTRY(G, 'ENAL', listFilled, 0, 0);
|
|
WRITE_REGISTER_ULONG(&HC->HcCommandStatus.ul, listFilled);
|
|
}
|
|
|
|
Frame = Get32BitFrameNumber(DeviceData);
|
|
if (ContextInfo & (HcInt_SchedulingOverrun
|
|
| HcInt_WritebackDoneHead
|
|
| HcInt_StartOfFrame
|
|
| HcInt_FrameNumberOverflow))
|
|
ContextInfo |= HcInt_MasterInterruptEnable; /* flag for EOF irq's */
|
|
|
|
// Today we ack but otherwise ignore a SchedulingOverrun interrupt.
|
|
// In the future we may want to do something as suggested in section
|
|
// 5.2.10.1 of the OpenHCI specification.
|
|
//
|
|
if (ContextInfo & HcInt_SchedulingOverrun)
|
|
{
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptStatus,
|
|
HcInt_SchedulingOverrun); /* ack int */
|
|
|
|
ContextInfo &= ~HcInt_SchedulingOverrun;
|
|
}
|
|
|
|
/* Check for Frame Number Overflow The following insures that the 32 bit
|
|
* frame never runs backward. */
|
|
|
|
if (ContextInfo & HcInt_FrameNumberOverflow) {
|
|
DeviceData->FrameHighPart
|
|
+= 0x10000 - (0x8000 & (DeviceData->HCCA->HccaFrameNumber
|
|
^ DeviceData->FrameHighPart));
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptStatus,
|
|
HcInt_FrameNumberOverflow);
|
|
|
|
ContextInfo &= ~HcInt_FrameNumberOverflow;
|
|
#if DBG
|
|
Temp = Get32BitFrameNumber(DeviceData);
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_ISR_NOISE,
|
|
("'Frame number advance: 0x%x\n", Temp));
|
|
#endif
|
|
}
|
|
/* Construct a DPC with all the goodies and schedule it. */
|
|
DeviceData->IsrDpc_Context.Frame = Frame;
|
|
DeviceData->IsrDpc_Context.ContextInfo = ContextInfo;
|
|
KeInsertQueueDpc(&DeviceData->IsrDPC, NULL, (PVOID) DeviceData);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
OpenHCI_IsrDPC(
|
|
PKDPC Dpc,
|
|
PVOID DeviceObjectPtr,
|
|
PVOID LaGarbage,
|
|
PVOID DeviceDataPtr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
KIRQL oldIrql;
|
|
PHC_OPERATIONAL_REGISTER HC;
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
PHCD_ENDPOINT endpoint;
|
|
ULONG frame, ContextInfo, DoneQueue;
|
|
PHCD_DEVICE_DATA deviceData = DeviceDataPtr;
|
|
ULONG HcDoneHead;
|
|
|
|
frame = deviceData->IsrDpc_Context.Frame;
|
|
ContextInfo = deviceData->IsrDpc_Context.ContextInfo;
|
|
HC = deviceData->HC;
|
|
|
|
KeAcquireSpinLockAtDpcLevel(&deviceData->HcFlagSpin);
|
|
|
|
#if DBG
|
|
OHCI_ASSERT((deviceData->HcFlags & HC_FLAG_IN_DPC) == 0);
|
|
deviceData->HcFlags |= HC_FLAG_IN_DPC;
|
|
#endif
|
|
|
|
if (deviceData->HcFlags & HC_FLAG_IDLE) {
|
|
|
|
OpenHCI_KdPrint((1, "'controller going off idle\n"));
|
|
LOGENTRY(G, 'OFFi', deviceData, 0, 0);
|
|
|
|
// not idle anymore
|
|
deviceData->HcFlags &= ~HC_FLAG_IDLE;
|
|
if (!KeSynchronizeExecution(deviceData->InterruptObject,
|
|
OpenHCI_StartController,
|
|
deviceData)) {
|
|
TRAP(); //something has gone terribly wrong
|
|
}
|
|
}
|
|
KeReleaseSpinLockFromDpcLevel(&deviceData->HcFlagSpin);
|
|
|
|
OpenHCI_KdPrintDD(deviceData, OHCI_DBG_ISR_TRACE,
|
|
("'OpenHCI_IsrDpc, DD 0x%x, ContextInfo: 0x%x \n",
|
|
deviceData, ContextInfo));
|
|
|
|
if (ContextInfo & HcInt_UnrecoverableError) {
|
|
/*
|
|
* Here the controller is hung!
|
|
*/
|
|
OpenHCI_KdPrint((0, "'Controller: Unrecoverable error!\n"));
|
|
|
|
LOGENTRY(G, 'HC!!', deviceData, HC, ContextInfo);
|
|
|
|
#if DBG
|
|
KeAcquireSpinLockAtDpcLevel(&deviceData->HcFlagSpin);
|
|
deviceData->HcFlags &= ~HC_FLAG_IN_DPC;
|
|
KeReleaseSpinLockFromDpcLevel(&deviceData->HcFlagSpin);
|
|
#endif
|
|
|
|
// If we're not checking for a hung HC in OpenHCI_DeadmanDPC()
|
|
// go ahead and reset the host controller now.
|
|
//
|
|
if ( !(deviceData->HcFlags & HC_FLAG_HUNG_CHECK_ENABLE) )
|
|
{
|
|
OpenHCI_ResurrectHC(deviceData);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#if DBG
|
|
if (ContextInfo & HcInt_ResumeDetected) {
|
|
/* Resume has been requested by a device on the USB. HCD must wait
|
|
* 20ms then put controller in the UsbOperational state. */
|
|
// LARGE_INTEGER time;
|
|
|
|
// time.QuadPart = 20 * 10000 * -1;
|
|
/* Time is in units of 100-nanoseconds. Neg vals are relative time. */
|
|
|
|
// KeSetTimer(&deviceData->ResumeTimer, time, &deviceData->ResumeDPC);
|
|
|
|
// Note: Nothing to do here accept possibly complete the waitwake irp
|
|
// the root hub will handle resume signalling on the appropriate port
|
|
|
|
LOGENTRY(G, 'reHC', deviceData, 0, ContextInfo);
|
|
#ifdef MAX_DEBUG
|
|
TEST_TRAP();
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Process the Done Queue
|
|
*/
|
|
|
|
//
|
|
// If the HC froze, first process any TDs that completed during the
|
|
// last frame in which the TD froze.
|
|
//
|
|
|
|
HcDoneHead = InterlockedExchange(&deviceData->FrozenHcDoneHead, 0);
|
|
|
|
if (HcDoneHead)
|
|
{
|
|
OpenHCI_ProcessDoneQueue(deviceData, HcDoneHead);
|
|
}
|
|
|
|
#if 0
|
|
if (ContextInfo & HcInt_WritebackDoneHead &&
|
|
!(deviceData->HcFlags & HC_FLAG_USE_HYDRA_HACK)) {
|
|
|
|
// should not get here is hydra fix is enabled
|
|
OHCI_ASSERT(!(deviceData->HcFlags & HC_FLAG_USE_HYDRA_HACK));
|
|
#else
|
|
if (ContextInfo & HcInt_WritebackDoneHead) {
|
|
#endif
|
|
|
|
DoneQueue = deviceData->HCCA->HccaDoneHead;
|
|
LOGENTRY(G, 'donQ', deviceData, DoneQueue, ContextInfo);
|
|
|
|
if (DoneQueue == 0)
|
|
{
|
|
//
|
|
// We got an HcInt_WritebackDoneHead but the HCCA->HccaDoneHead
|
|
// was zero. Apparently the HC failed to properly update the
|
|
// HCCA->HccaDoneHead. Try to recover the lost done head.
|
|
//
|
|
DoneQueue = FindLostDoneHead(deviceData);
|
|
LOGENTRY(G, 'Lost', deviceData, DoneQueue, ContextInfo);
|
|
OHCI_ASSERT(DoneQueue != 0);
|
|
}
|
|
|
|
deviceData->HCCA->HccaDoneHead = 0;
|
|
LOGENTRY(G, 'dnQZ', deviceData, DoneQueue, ContextInfo);
|
|
|
|
OpenHCI_KdPrintDD(deviceData,
|
|
OHCI_DBG_ISR_TRACE, ("'Checking WritebackDoneHead\n"));
|
|
|
|
if (DoneQueue) {
|
|
OpenHCI_ProcessDoneQueue(deviceData, (DoneQueue & 0xFFFFfffe));
|
|
//
|
|
// Note: No interrupts can come in while processing the done queue.
|
|
//
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
if (deviceData->LastHccaDoneHead) {
|
|
// should only get here if hydra fix is enabled
|
|
// TEST_TRAP();
|
|
OHCI_ASSERT(deviceData->HcFlags & HC_FLAG_USE_HYDRA_HACK);
|
|
|
|
DoneQueue = deviceData->LastHccaDoneHead;
|
|
deviceData->LastHccaDoneHead = 0;
|
|
|
|
LOGENTRY(G, 'dnQ2', deviceData, DoneQueue, ContextInfo);
|
|
OpenHCI_ProcessDoneQueue(deviceData, (DoneQueue & 0xFFFFfffe));
|
|
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Process Root Hub changes
|
|
*/
|
|
|
|
if (ContextInfo & HcInt_RootHubStatusChange
|
|
#if FAKEPORTCHANGE
|
|
|| deviceData->FakePortChange
|
|
#endif
|
|
) {
|
|
|
|
OpenHCI_KdPrintDD(deviceData, OHCI_DBG_ISR_TRACE,
|
|
("' ROOT HUB STATUS CHANGE ! \n"));
|
|
/*
|
|
* EmulateRootHubInterruptXfer will complete a
|
|
* HCD_TRANSFER_DESCRIPTOR which we then pass to ProcessDoneQueue to
|
|
* emulate an HC completion.
|
|
*/
|
|
|
|
LOGENTRY(G, 'RHxf', deviceData, 0, ContextInfo);
|
|
if (deviceData->RootHubInterrupt) {
|
|
EmulateRootHubInterruptXfer(deviceData, HC);
|
|
} else {
|
|
// no root hub data, root hub is closed
|
|
// disable the interrupt and ack the change
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptDisable,
|
|
HcInt_RootHubStatusChange);
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptStatus,
|
|
HcInt_RootHubStatusChange);
|
|
|
|
}
|
|
|
|
/*
|
|
* clear the RootHubStatusChange bit in context info
|
|
* we have already cleared the status and/or disabled
|
|
* the interrupt if necessary.
|
|
*/
|
|
|
|
ContextInfo &= ~HcInt_RootHubStatusChange;
|
|
}
|
|
|
|
if (ContextInfo & HcInt_OwnershipChange) {
|
|
OpenHCI_KdPrintDD(deviceData, OHCI_DBG_ISR_INFO,
|
|
("'OpenHCI_IsrDpc: Ownership CHANGE!\n"));
|
|
|
|
LOGENTRY(G, 'OWnr', deviceData, 0, ContextInfo);
|
|
/* Only SMM drivers need implement this. */
|
|
TEST_TRAP(); // No code path for this.
|
|
}
|
|
|
|
|
|
/*
|
|
* interrupt for SOF
|
|
*/
|
|
|
|
if (ContextInfo & HcInt_StartOfFrame) {
|
|
|
|
//LOGENTRY(G, 'SOFi', deviceData, 0, ContextInfo);
|
|
|
|
/*
|
|
* we asked for an interrupt on the next SOF,
|
|
* now that we've gotten one we can disable again
|
|
*/
|
|
|
|
#if 0
|
|
if (!(deviceData->HcFlags & HC_FLAG_USE_HYDRA_HACK))
|
|
#endif
|
|
{
|
|
ContextInfo &= ~HcInt_StartOfFrame;
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptDisable, HcInt_StartOfFrame);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We've complete the actual service of the HC interrupts, now we must
|
|
* deal with the effects.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Look for things on the PausedEDRestart list.
|
|
*
|
|
* Any ED that is on the pusedRestart list will be removed, its TDs
|
|
* canceled and then restarted.
|
|
*
|
|
*/
|
|
|
|
frame = Get32BitFrameNumber(deviceData);
|
|
|
|
KeAcquireSpinLock(&deviceData->PausedSpin, &oldIrql);
|
|
|
|
while (!IsListEmpty(&deviceData->PausedEDRestart))
|
|
{
|
|
OpenHCI_KdPrintDD(deviceData,
|
|
OHCI_DBG_ISR_TRACE, ("'Checking paused restart list\n"));
|
|
ed = CONTAINING_RECORD(deviceData->PausedEDRestart.Flink,
|
|
HCD_ENDPOINT_DESCRIPTOR,
|
|
PausedLink);
|
|
endpoint = ed->Endpoint;
|
|
ASSERT_ENDPOINT(endpoint);
|
|
LOGENTRY(G, 'pasR', deviceData, endpoint, ed);
|
|
|
|
if ((LONG) ed->ReclamationFrame - (LONG) frame > 0) {
|
|
/*
|
|
* Now is the wrong time to remove this entry. but most likely
|
|
* the very next frame will be so we will ask for an interrupt
|
|
* at next SOF
|
|
*/
|
|
|
|
ContextInfo |= HcInt_StartOfFrame;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* remove the ed from the paused list and reset
|
|
* the state.
|
|
*/
|
|
RemoveEntryList(&ed->PausedLink);
|
|
|
|
if ((ed->HcED.HeadP & HcEDHeadP_HALT) || ed->HcED.sKip) {
|
|
KeReleaseSpinLock(&deviceData->PausedSpin, oldIrql);
|
|
} else {
|
|
// HC has not paused the ep yet, put it back on the list
|
|
TEST_TRAP();
|
|
InsertTailList(&deviceData->PausedEDRestart, &ed->PausedLink);
|
|
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* cancel the current transfers queued to this ed
|
|
*/
|
|
OpenHCI_CancelTDsForED(ed);
|
|
|
|
KeAcquireSpinLock(&deviceData->PausedSpin, &oldIrql);
|
|
}
|
|
KeReleaseSpinLock(&deviceData->PausedSpin, oldIrql);
|
|
|
|
/*
|
|
* This code processed our reclimation lists ie EDs that
|
|
* we need to free
|
|
*/
|
|
|
|
if (ContextInfo & HcInt_MasterInterruptEnable) {
|
|
// Do we have an "end of Frame" type interrupt? //
|
|
|
|
ULONG newControlED = 0;
|
|
ULONG newBulkED = 0;
|
|
ULONG currentControlED
|
|
= READ_REGISTER_ULONG(&HC->HcControlCurrentED);
|
|
ULONG currentBulkED
|
|
= READ_REGISTER_ULONG(&HC->HcBulkCurrentED);
|
|
KeSynch_HcControl context;
|
|
|
|
//
|
|
// If of course either the control or bulk list is not stalled, then
|
|
// the current ED pointer will continue to advance. Here we are
|
|
// making the ASSUMPTION that the only way an ED from either of these
|
|
// two lists made it onto the ReclamationList was by stalling their
|
|
// respective Control or Bulk list. In that case the Current ED
|
|
// pointer will remain quite happily the same number until the
|
|
// control or bulk list is restarted.
|
|
//
|
|
|
|
|
|
/* Look for things on the StalledEDReclamation list */
|
|
KeAcquireSpinLock(&deviceData->ReclamationSpin, &oldIrql);
|
|
while (!IsListEmpty(&deviceData->StalledEDReclamation)) {
|
|
OpenHCI_KdPrintDD(deviceData, OHCI_DBG_ISR_TRACE,
|
|
("'Checking Stalled Reclamation list\n"));
|
|
ed = CONTAINING_RECORD(deviceData->StalledEDReclamation.Flink,
|
|
HCD_ENDPOINT_DESCRIPTOR,
|
|
Link);
|
|
|
|
LOGENTRY(G, 'REcs', deviceData, ed, 0);
|
|
ASSERT(NULL == ed->Endpoint);
|
|
/*
|
|
* The only way that this ED could have gotten itself on the
|
|
* reclamation list was for it to have been placed there by a
|
|
* RemoveEDForEndpoint call. This would have severed the
|
|
* Endpoint.
|
|
*/
|
|
|
|
RemoveEntryList(&ed->Link);
|
|
KeReleaseSpinLock(&deviceData->ReclamationSpin, oldIrql);
|
|
|
|
if (ed->PhysicalAddress == currentControlED) {
|
|
newControlED = currentControlED = ed->HcED.NextED;
|
|
} else if (ed->PhysicalAddress == currentBulkED) {
|
|
newBulkED = currentBulkED = ed->HcED.NextED;
|
|
}
|
|
|
|
OpenHCI_Free_HcdED(deviceData, ed);
|
|
|
|
// Need to enable SOF interrupts to generate an interrupt
|
|
// so that HcControl is updated with ListEnablesAtNextSOF
|
|
// to turn BLE and CLE back on after the next SOF.
|
|
//
|
|
ContextInfo |= HcInt_StartOfFrame;
|
|
|
|
KeAcquireSpinLock(&deviceData->ReclamationSpin, &oldIrql);
|
|
}
|
|
KeReleaseSpinLock(&deviceData->ReclamationSpin, oldIrql);
|
|
|
|
if (newControlED) {
|
|
LOGENTRY(G, 'nQ1S', deviceData, newControlED, newBulkED);
|
|
WRITE_REGISTER_ULONG(&HC->HcControlCurrentED, newControlED);
|
|
}
|
|
if (newBulkED) {
|
|
LOGENTRY(G, 'nQ2S', deviceData, newControlED, newBulkED);
|
|
WRITE_REGISTER_ULONG(&HC->HcBulkCurrentED, newBulkED);
|
|
}
|
|
/* Restart both queues. */
|
|
|
|
context.DeviceData = DeviceDataPtr;
|
|
context.NewHcControl.ul = HcCtrl_ControlListEnable
|
|
| HcCtrl_BulkListEnable;
|
|
|
|
KeSynchronizeExecution(deviceData->InterruptObject,
|
|
OpenHCI_ListEnablesAtNextSOF,
|
|
&context);
|
|
}
|
|
|
|
frame = Get32BitFrameNumber(deviceData);
|
|
|
|
/* Look for things on the runningReclamationList */
|
|
KeAcquireSpinLock(&deviceData->ReclamationSpin, &oldIrql);
|
|
while (!IsListEmpty(&deviceData->RunningEDReclamation)) {
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
OpenHCI_KdPrintDD(deviceData, OHCI_DBG_ISR_TRACE,
|
|
("'Checking running reclamation list\n"));
|
|
|
|
ed = CONTAINING_RECORD(deviceData->RunningEDReclamation.Flink,
|
|
HCD_ENDPOINT_DESCRIPTOR,
|
|
Link);
|
|
if ((LONG) ed->ReclamationFrame - (LONG) frame > 0) {
|
|
ContextInfo |= HcInt_StartOfFrame;
|
|
|
|
/*
|
|
* We need to remove this ED from the list, but now is not the
|
|
* appropriate time. Most likely, however, the very next Frame
|
|
* will be the correct time, so reenable the start of frame
|
|
* interrupt.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
LOGENTRY(G, 'REcr', deviceData, ed, 0);
|
|
ASSERT(NULL == ed->Endpoint);
|
|
/* The only way that this bad boy could have gotten itself on the
|
|
* reclamation list was for it to have been placed there by a
|
|
* RemoveEDForEndpoint call. THis would have severed the Endpoint. */
|
|
|
|
RemoveEntryList(&ed->Link);
|
|
KeReleaseSpinLock(&deviceData->ReclamationSpin, oldIrql);
|
|
|
|
OpenHCI_Free_HcdED(deviceData, ed);
|
|
KeAcquireSpinLock(&deviceData->ReclamationSpin, &oldIrql);
|
|
}
|
|
KeReleaseSpinLock(&deviceData->ReclamationSpin, oldIrql);
|
|
|
|
|
|
// Loop thru the list of active endpoints and see if we have a deferred
|
|
// work to do.
|
|
|
|
KeAcquireSpinLock(&deviceData->HcDmaSpin, &oldIrql);
|
|
|
|
// attempt to lock out access to ep worker
|
|
deviceData->HcDma++;
|
|
|
|
//LOGENTRY(G, 'pEPl', 0, 0, deviceData->HcDma);
|
|
|
|
if (deviceData->HcDma) {
|
|
|
|
// EP worker is busy, bail
|
|
deviceData->HcDma--;
|
|
KeReleaseSpinLock(&deviceData->HcDmaSpin, oldIrql);
|
|
goto Openhci_ISRDPC_Done;
|
|
}
|
|
|
|
if (IsListEmpty(&deviceData->ActiveEndpointList)) {
|
|
|
|
// queue is empty, bail
|
|
//LOGENTRY(G, 'EPmt', 0, 0, deviceData->HcDma);
|
|
deviceData->HcDma--;
|
|
KeReleaseSpinLock(&deviceData->HcDmaSpin, oldIrql);
|
|
goto Openhci_ISRDPC_Done;
|
|
|
|
}
|
|
|
|
// at this point we have exclusive access to ep worker
|
|
|
|
do {
|
|
|
|
PHCD_ENDPOINT endpoint;
|
|
PLIST_ENTRY entry;
|
|
|
|
LOGENTRY(G, 'epLS', &deviceData->ActiveEndpointList, 0, 0);
|
|
|
|
entry = RemoveHeadList(&deviceData->ActiveEndpointList);
|
|
|
|
// use DMA spin to serialize access to the list
|
|
|
|
endpoint = CONTAINING_RECORD(entry,
|
|
HCD_ENDPOINT,
|
|
EndpointListEntry);
|
|
|
|
LOGENTRY(G, 'gtEP', endpoint, 0, 0);
|
|
CLR_EPFLAG(endpoint, EP_IN_ACTIVE_LIST);
|
|
KeReleaseSpinLock(&deviceData->HcDmaSpin, oldIrql);
|
|
|
|
OpenHCI_EndpointWorker(endpoint);
|
|
|
|
KeAcquireSpinLock(&deviceData->HcDmaSpin, &oldIrql);
|
|
|
|
} while (!IsListEmpty(&deviceData->ActiveEndpointList));
|
|
|
|
LOGENTRY(G, 'pEPd', 0, 0, 0);
|
|
|
|
// release ep worker
|
|
deviceData->HcDma--;
|
|
KeReleaseSpinLock(&deviceData->HcDmaSpin, oldIrql);
|
|
|
|
Openhci_ISRDPC_Done:
|
|
|
|
OpenHCI_KdPrintDD(deviceData,
|
|
OHCI_DBG_ISR_TRACE, ("'Exit Isr DPC routine\n"));
|
|
|
|
#if DBG
|
|
//
|
|
// As soon as interrupts are reenabled, the interrupt service routine
|
|
// may execute and queue another call to the DPC. This routine must
|
|
// be prepared to be reentered as soon as interrupts are reenabled.
|
|
// So this routine should not do anything that cannot handle being
|
|
// reentered after interrupts are reenabled.
|
|
//
|
|
KeAcquireSpinLockAtDpcLevel(&deviceData->HcFlagSpin);
|
|
deviceData->HcFlags &= ~HC_FLAG_IN_DPC;
|
|
KeReleaseSpinLockFromDpcLevel(&deviceData->HcFlagSpin);
|
|
#endif
|
|
|
|
// Acknowledge the interrupts we handled and reenable interrupts.
|
|
//
|
|
ContextInfo |= HcInt_MasterInterruptEnable;
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptStatus, ContextInfo);
|
|
WRITE_REGISTER_ULONG(&HC->HcInterruptEnable, ContextInfo);
|
|
}
|
|
|
|
|
|
VOID
|
|
OpenHCI_Free_HcdED(
|
|
PHCD_DEVICE_DATA DeviceData,
|
|
PHCD_ENDPOINT_DESCRIPTOR Ed
|
|
)
|
|
/*
|
|
* Place the corresponding ED back onto the Free Descriptors list.
|
|
* Presumably the only reason the ED got here was because it was
|
|
* FIRST sent to RemoveEDForEndpoint which freed all of the
|
|
* outstanding TD's, INCLUDING the stub TD.
|
|
*
|
|
* This of course means that the pointers to TD's are invalid.
|
|
* SECOND the ED was placed on the reclamation list and the
|
|
* DPCforIRQ moved it here.
|
|
|
|
* This function is only called in the DPC for Irq. If another
|
|
* function wishes to free an endpoint it must call RemoveEDForEndpoint,
|
|
* which will add the ED to the reclamation list to be freed on the
|
|
* next Start O Frame.
|
|
*/
|
|
|
|
{
|
|
OHCI_ASSERT(Ed);
|
|
OHCI_ASSERT(NULL == Ed->Endpoint);
|
|
OHCI_ASSERT((Ed->HcED.HeadP & ~0X0F) == Ed->HcED.TailP);
|
|
|
|
LOGENTRY(G, 'frED', DeviceData, Ed, 0);
|
|
|
|
OHCI_ASSERT((Ed->PhysicalAddress & (PAGE_SIZE-1)) ==
|
|
((ULONG_PTR)Ed & (PAGE_SIZE-1)));
|
|
|
|
OHCI_ASSERT(Ed->Flags == (TD_FLAG_INUSE | TD_FLAG_IS_ED));
|
|
Ed->Flags = 0;
|
|
|
|
ExInterlockedPushEntryList(&DeviceData->FreeDescriptorList,
|
|
(PSINGLE_LIST_ENTRY) Ed,
|
|
&DeviceData->DescriptorsSpin);
|
|
}
|
|
|
|
|
|
VOID
|
|
OpenHCI_CancelTDsForED(
|
|
PHCD_ENDPOINT_DESCRIPTOR Ed
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Given an ED search through the list of TDs and weed out those with
|
|
problems.
|
|
|
|
It is ASSUMED that if the ED got to this list that either
|
|
it has been paused (the sKip bit is set & a new SOF has occured)
|
|
or it has been halted by the HC.
|
|
|
|
It is possible that at the time of cancel of TD's for this endpoint
|
|
that some of the canceled TDs had already made it to the done queue.
|
|
We catch that by setting the TD Status field to .
|
|
That way, when we are processing the DoneQueue, we can skip over those
|
|
TDs.
|
|
|
|
Note: This routine was originally called ProcessPausedED.
|
|
|
|
Aguments:
|
|
ED - the ED that is inspected.
|
|
|
|
--*/
|
|
{
|
|
PHCD_ENDPOINT endpoint;
|
|
PHCD_DEVICE_DATA DeviceData;
|
|
PHC_OPERATIONAL_REGISTER HC;
|
|
PHCD_URB urb;
|
|
PHCD_TRANSFER_DESCRIPTOR td;
|
|
PHCD_TRANSFER_DESCRIPTOR last = NULL;
|
|
PHCD_TRANSFER_DESCRIPTOR *previous;
|
|
BOOLEAN B4Head = TRUE;
|
|
ULONG physicalHeadP;
|
|
KIRQL oldIrql;
|
|
LIST_ENTRY CancelList;
|
|
PIRP AbortIrp;
|
|
|
|
endpoint = Ed->Endpoint;
|
|
ASSERT_ENDPOINT(endpoint);
|
|
OHCI_ASSERT(NULL != endpoint);
|
|
OHCI_ASSERT((Ed->HcED.HeadP & HcEDHeadP_HALT) || Ed->HcED.sKip);
|
|
OHCI_ASSERT(Ed->PauseFlag == HCD_ED_PAUSE_NEEDED);
|
|
|
|
if (endpoint->TrueTail) {
|
|
LOGENTRY(G, 'cTTl', endpoint->TrueTail,
|
|
endpoint->HcdTailP, endpoint);
|
|
|
|
// Bump the software tail pointer to the true tail for this transfer
|
|
//
|
|
endpoint->HcdTailP = endpoint->TrueTail;
|
|
endpoint->TrueTail = NULL;
|
|
|
|
// Bump the hardware tail pointer to the true tail for this transfer
|
|
//
|
|
endpoint->HcdED->HcED.TailP = endpoint->HcdTailP->PhysicalAddress;
|
|
}
|
|
|
|
//
|
|
// The endpoint has been stopped, we need to walk thru
|
|
// the list of HW TDs and remove any that are associated
|
|
// canceled transfers.
|
|
//
|
|
|
|
DeviceData = endpoint->DeviceData;
|
|
HC = DeviceData->HC;
|
|
|
|
InitializeListHead(&CancelList);
|
|
|
|
KeAcquireSpinLock(&DeviceData->PausedSpin, &oldIrql);
|
|
|
|
CancelTDsOneMoreTime:
|
|
|
|
Ed->PauseFlag = HCD_ED_PAUSE_PROCESSING;
|
|
|
|
KeReleaseSpinLock(&DeviceData->PausedSpin, oldIrql);
|
|
|
|
|
|
// lock down the endpoint
|
|
OpenHCI_LockAndCheckEndpoint(endpoint,
|
|
NULL,
|
|
NULL,
|
|
&oldIrql);
|
|
|
|
LOGENTRY(G, 'xxED', DeviceData, endpoint, Ed);
|
|
|
|
td = endpoint->HcdHeadP;
|
|
previous = &endpoint->HcdHeadP;
|
|
LOGENTRY(G, 'xED1', DeviceData, td, previous);
|
|
/* aka the location where the previous TD holds the current TD. */
|
|
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_TRACE,
|
|
("'Calling Cancel TD's For ED %x %x hd = %x\n",
|
|
endpoint->HcdHeadP,
|
|
endpoint->HcdTailP,
|
|
&endpoint->HcdHeadP));
|
|
|
|
physicalHeadP = (Ed->HcED.HeadP & ~HcEDHeadP_FLAGS);
|
|
while (td != endpoint->HcdTailP) {
|
|
|
|
LOGENTRY(G, 'xEDy', td->PhysicalAddress, td, physicalHeadP);
|
|
if (physicalHeadP == td->PhysicalAddress) {
|
|
// aka is The first TD on the Enpoint list of TD's is the
|
|
// same as the first HC_TD on the Host Controller list
|
|
// of HC_TD's?
|
|
// The HC could have processed some TD's that we have yet to
|
|
// process.
|
|
//
|
|
B4Head = FALSE;
|
|
}
|
|
|
|
//
|
|
// get the urb associated with this TD
|
|
//
|
|
|
|
urb = td->UsbdRequest;
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_NOISE,
|
|
("'Endpoint:%x ED:%x TD:%x trans:%x stat:%x abort:%x\n",
|
|
endpoint, Ed, td,
|
|
&urb->HcdUrbCommonTransfer,
|
|
urb->UrbHeader.Status,
|
|
endpoint->EpFlags));
|
|
|
|
LOGENTRY(G, 'xREQ', DeviceData, urb, td);
|
|
|
|
//
|
|
// either this is a specific request to cancel or
|
|
// we are aborting all transfers for the ED
|
|
//
|
|
if ((USBD_STATUS_CANCELING == urb->UrbHeader.Status) ||
|
|
endpoint->EpFlags & EP_ABORT) {
|
|
|
|
PHC_TRANSFER_DESCRIPTOR hcTD;
|
|
struct _URB_HCD_COMMON_TRANSFER *transfer;
|
|
|
|
OpenHCI_KdPrintDD(DeviceData,
|
|
OHCI_DBG_TD_NOISE, ("'Killing TD\n"));
|
|
|
|
LOGENTRY(G, 'xxTD', DeviceData, urb, td);
|
|
OHCI_ASSERT(!td->Canceled);
|
|
|
|
RemoveEntryList(&td->RequestList);
|
|
td->Canceled = TRUE;
|
|
td->Endpoint = NULL;
|
|
|
|
transfer = &urb->HcdUrbCommonTransfer;
|
|
hcTD = &td->HcTD;
|
|
|
|
// Only update transfer count if transfer count is non-zero
|
|
//
|
|
if (td->TransferCount)
|
|
{
|
|
td->TransferCount -=
|
|
/* have we gone further than a page? */
|
|
((((hcTD->BE ^ hcTD->CBP) & ~OHCI_PAGE_SIZE_MASK)
|
|
? OHCI_PAGE_SIZE : 0) +
|
|
/* minus the data buffer not used */
|
|
((hcTD->BE & OHCI_PAGE_SIZE_MASK) -
|
|
(hcTD->CBP & OHCI_PAGE_SIZE_MASK)+1));
|
|
|
|
LOGENTRY(G, 'xfB2', hcTD->BE & OHCI_PAGE_SIZE_MASK,
|
|
hcTD->CBP & OHCI_PAGE_SIZE_MASK,
|
|
td->TransferCount);
|
|
|
|
transfer->TransferBufferLength += td->TransferCount;
|
|
}
|
|
|
|
if (IsListEmpty(&urb->HcdUrbCommonTransfer.hca.HcdListEntry2))
|
|
{
|
|
// AKA all of the TD's for this URB have been delt with.
|
|
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_TRACE,
|
|
("'Return canceled off hardware\n"));
|
|
|
|
LOGENTRY(G, 'CANu', DeviceData, urb, td);
|
|
|
|
// Put it on the cancel list
|
|
//
|
|
if (!urb->HcdUrbCommonTransfer.hca.HcdListEntry.Flink)
|
|
{
|
|
LOGENTRY(G, 'canA', DeviceData, urb, td);
|
|
|
|
InsertTailList(&CancelList,
|
|
&urb->HcdUrbCommonTransfer.hca.HcdListEntry);
|
|
}
|
|
}
|
|
|
|
*previous = td->NextHcdTD;
|
|
// link around the TD
|
|
|
|
if (NULL == last) {
|
|
//
|
|
// For bulk and interrupt endpoints, TDs are queued with a
|
|
// dataToggle == 00b. After the first data packet is
|
|
// successfully transferred, the MSb of dataToggle is set to
|
|
// indicate that the LSb indicates the next toggle value.
|
|
// The toggleCarry of the ED is only updated with the dataToggle
|
|
// of the TD when the TD makes it to the doneQueue. Manually
|
|
// update the ED with the toggle of the last TD that transferred
|
|
// data when a transfer is cancelled.
|
|
//
|
|
if (td->HcTD.Toggle == 3) {
|
|
Ed->HcED.HeadP = (td->HcTD.NextTD & ~HcEDHeadP_FLAGS) |
|
|
HcEDHeadP_CARRY;
|
|
} else if (td->HcTD.Toggle == 2) {
|
|
Ed->HcED.HeadP = (td->HcTD.NextTD & ~HcEDHeadP_FLAGS);
|
|
} else {
|
|
Ed->HcED.HeadP = ((td->HcTD.NextTD & ~HcEDHeadP_FLAGS) |
|
|
(Ed->HcED.HeadP & HcEDHeadP_CARRY));
|
|
}
|
|
LOGENTRY(G, 'BMPh', DeviceData, td->HcTD.Toggle, 0);
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_NOISE,
|
|
("'cancelTDsForED bumping HeadP\n"));
|
|
} else {
|
|
last->HcTD.NextTD = td->HcTD.NextTD;
|
|
LOGENTRY(G, 'BMPn', DeviceData, 0, 0);
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_NOISE,
|
|
("'cancelTDsForED not bumping HeadP\n"));
|
|
}
|
|
|
|
//
|
|
// If the TD is not yet on the Done queue,
|
|
// aka the HC has yet to see it, then Free it now.
|
|
// Otherwise flag it so that the Process Done Queue
|
|
// routine can free it later
|
|
//
|
|
if (!B4Head) {
|
|
OpenHCI_Free_HcdTD(DeviceData, td);
|
|
}
|
|
} else {
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_NOISE,
|
|
("'TD Spared, next = %x\n", &td->NextHcdTD));
|
|
LOGENTRY(G, 'skTD', DeviceData, td, 0);
|
|
|
|
previous = &td->NextHcdTD;
|
|
if (!B4Head) {
|
|
last = td;
|
|
}
|
|
}
|
|
td = *previous;
|
|
}
|
|
|
|
LOGENTRY(G, 'uTal', DeviceData, Ed->HcED.TailP,
|
|
endpoint->HcdTailP->PhysicalAddress);
|
|
|
|
Ed->HcED.TailP = endpoint->HcdTailP->PhysicalAddress;
|
|
|
|
OpenHCI_UnlockEndpoint(endpoint,
|
|
oldIrql);
|
|
|
|
// Now complete all of the transfers which we put it on the cancel list.
|
|
//
|
|
while (!IsListEmpty(&CancelList))
|
|
{
|
|
PLIST_ENTRY entry;
|
|
|
|
entry = RemoveHeadList(&CancelList);
|
|
|
|
urb = CONTAINING_RECORD(entry,
|
|
HCD_URB,
|
|
HcdUrbCommonTransfer.hca.HcdListEntry);
|
|
|
|
LOGENTRY(G, 'CANq', DeviceData, urb, 0);
|
|
|
|
//
|
|
// We may be canceling a request that is still in the
|
|
// cancelable state (if we got here because the abortAll
|
|
// flag was set) in that case we take the cancel spinlock
|
|
// and set the cancel routine
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
IoSetCancelRoutine(urb->HcdUrbCommonTransfer.hca.HcdIrp, NULL);
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
OpenHCI_CompleteUsbdTransferRequest(urb,
|
|
USBD_STATUS_CANCELED,
|
|
STATUS_CANCELLED,
|
|
TRUE);
|
|
}
|
|
|
|
//
|
|
// If we completed any requests above, it might have triggered the
|
|
// cancelling of additional requests on the same endpoint. Process
|
|
// cancelling the TDs for this ED one more time, if necessary.
|
|
//
|
|
|
|
KeAcquireSpinLock(&DeviceData->PausedSpin, &oldIrql);
|
|
|
|
if (Ed->PauseFlag == HCD_ED_PAUSE_NEEDED)
|
|
{
|
|
goto CancelTDsOneMoreTime;
|
|
}
|
|
else
|
|
{
|
|
Ed->PauseFlag = HCD_ED_PAUSE_NOT_PAUSED;
|
|
|
|
if (endpoint->EpFlags & EP_FREE)
|
|
{
|
|
KeReleaseSpinLock(&DeviceData->PausedSpin, oldIrql);
|
|
|
|
RemoveEDForEndpoint(endpoint);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* restart the ED
|
|
*/
|
|
Ed->HcED.HeadP = (endpoint->HcdHeadP->PhysicalAddress
|
|
| (Ed->HcED.HeadP & HcEDHeadP_CARRY))
|
|
& ~HcEDHeadP_HALT;
|
|
|
|
Ed->HcED.sKip = FALSE;
|
|
|
|
//
|
|
// tell the HC we have something on the ED lists
|
|
//
|
|
ENABLE_LIST(HC, endpoint)
|
|
|
|
KeReleaseSpinLock(&DeviceData->PausedSpin, oldIrql);
|
|
}
|
|
}
|
|
|
|
// Complete the abort irp now if we have one
|
|
//
|
|
KeAcquireSpinLock(&DeviceData->PausedSpin, &oldIrql);
|
|
|
|
AbortIrp = endpoint->AbortIrp;
|
|
|
|
endpoint->AbortIrp = NULL;
|
|
|
|
CLR_EPFLAG(endpoint, EP_ABORT);
|
|
|
|
KeReleaseSpinLock(&DeviceData->PausedSpin, oldIrql);
|
|
|
|
if (AbortIrp != NULL)
|
|
{
|
|
LOGENTRY(G, 'cABR', 0, endpoint, AbortIrp);
|
|
|
|
OpenHCI_CompleteIrp(DeviceData->DeviceObject, AbortIrp, STATUS_SUCCESS);
|
|
}
|
|
|
|
LOGENTRY(G, 'xED>', DeviceData, endpoint, 0);
|
|
}
|
|
|
|
|
|
VOID
|
|
OpenHCI_CompleteUsbdTransferRequest(
|
|
PHCD_URB Urb,
|
|
USBD_STATUS UsbdStatus,
|
|
NTSTATUS Completion,
|
|
BOOLEAN RequestWasOnHardware
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This urb in the posible linked list of urbs has finished.
|
|
First return its resources back to the system.
|
|
|
|
Then (if it is the last of the urbs in the list) inform
|
|
the original caller by completing the IRP.
|
|
|
|
Arguments:
|
|
The request that will be completed.
|
|
The completion code for the IRP.
|
|
--*/
|
|
{
|
|
struct _URB_HCD_COMMON_TRANSFER *transfer =
|
|
&Urb->HcdUrbCommonTransfer;
|
|
PMDL mdl = transfer->TransferBufferMDL;
|
|
PHCD_ENDPOINT endpoint = transfer->hca.HcdEndpoint;
|
|
PHCD_DEVICE_DATA DeviceData = endpoint->DeviceData;
|
|
LONG epStatus;
|
|
|
|
ASSERT_ENDPOINT(endpoint);
|
|
|
|
LOGENTRY(G, 'cmpt', DeviceData, endpoint, Urb);
|
|
|
|
// The transfer only has map registers allocated if it made it through
|
|
// IoAllocateAdapterChannel() to OpenHCI_QueueGeneralRequest(), in which
|
|
// case the status should no longer be HCD_PENDING_STATUS_QUEUED.
|
|
// Don't call IoFlushAdapterBuffers() and IoFreeMapRegisters() if the
|
|
// request has not made it through IoAllocateAdapterChannel() yet.
|
|
//
|
|
if (NULL != mdl &&
|
|
transfer->Status != HCD_PENDING_STATUS_QUEUED)
|
|
{
|
|
ULONG NumberMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES
|
|
(MmGetMdlVirtualAddress(mdl),
|
|
MmGetMdlByteCount(mdl));
|
|
|
|
LOGENTRY(G, 'flsh', DeviceData, mdl, NumberMapRegisters);
|
|
IoFlushAdapterBuffers(
|
|
DeviceData->AdapterObject, // BusMaster card
|
|
transfer->TransferBufferMDL,
|
|
transfer->hca.HcdExtension, // The MapRegisterBase
|
|
(char *) MmGetMdlVirtualAddress(transfer->TransferBufferMDL),
|
|
transfer->TransferBufferLength,
|
|
(BOOLEAN) !
|
|
(transfer->TransferFlags & USBD_TRANSFER_DIRECTION_IN));
|
|
|
|
LOGENTRY(G, 'frmp', DeviceData, mdl, NumberMapRegisters);
|
|
IoFreeMapRegisters(DeviceData->AdapterObject,
|
|
transfer->hca.HcdExtension, //MmGetMdlVirtualAddress(mdl),
|
|
NumberMapRegisters);
|
|
} else {
|
|
LOGENTRY(G, 'frmZ', DeviceData, transfer->Status, 0);
|
|
// IoFreeMapRegisters(DeviceData->AdapterObject,
|
|
// NULL,/* VirtualAddress */
|
|
// 0); /* Number of map registers */
|
|
}
|
|
|
|
if (RequestWasOnHardware) {
|
|
epStatus = InterlockedDecrement(&endpoint->EndpointStatus);
|
|
LOGENTRY(G, 'dcS1', 0, endpoint, epStatus );
|
|
#if DBG
|
|
if (endpoint->MaxRequest == 1) {
|
|
// in limit xfer mode we should always be 0 here.
|
|
OHCI_ASSERT(epStatus == 0);
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
epStatus = endpoint->EndpointStatus;
|
|
LOGENTRY(G, 'epS1', 0, endpoint, epStatus );
|
|
}
|
|
OHCI_ASSERT(epStatus >= 0);
|
|
|
|
LOGENTRY(G, 'epck', DeviceData, endpoint->MaxRequest, epStatus);
|
|
if (endpoint->MaxRequest > epStatus) {
|
|
/*
|
|
* If there is space in the hardware TD queue (There is always room
|
|
* for two entries) then pop whatever is on the top of the software
|
|
* queue and place it and the hardware queue as a TD: aka start it.
|
|
*
|
|
* EndpointStatus is now zero based, not -1 based so the rest of the
|
|
* comment below is not quite correct...
|
|
*
|
|
* Why not (InterlockedDecrement < 1) as the test you might ask... If
|
|
* there is only one transfer turned into TD's, then EndpointStatus
|
|
* is 0, and the decrement makes it -1. In this case we know we do
|
|
* not need to call EndpointWorker. There is nothing on the software
|
|
* queue. If there were, than when it was actually added to the
|
|
* queue, it would have noticed that there was room to convert it
|
|
* into actual TD's (making EndpointStatus = 1).
|
|
*/
|
|
|
|
OpenHCI_ProcessEndpoint(DeviceData, endpoint);
|
|
}
|
|
|
|
//
|
|
// update the status
|
|
//
|
|
|
|
ASSERT(transfer->Status == HCD_PENDING_STATUS_SUBMITTED ||
|
|
transfer->Status == HCD_PENDING_STATUS_QUEUED ||
|
|
transfer->Status == USBD_STATUS_CANCELING);
|
|
|
|
LOGENTRY(G, 'cmST', DeviceData, transfer, UsbdStatus);
|
|
transfer->Status = UsbdStatus;
|
|
|
|
ASSERT(transfer->UrbLink == NULL);
|
|
|
|
transfer->Status = UsbdStatus;
|
|
|
|
OpenHCI_CompleteIrp(endpoint->DeviceData->DeviceObject,
|
|
transfer->hca.HcdIrp,
|
|
Completion);
|
|
}
|
|
|
|
|
|
VOID
|
|
OpenHCI_CancelTransfer(
|
|
PDEVICE_OBJECT UsbDeviceObject,
|
|
PIRP Irp
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Remove from the software and hardware queues any and all URB's and TD's
|
|
associated with this Irp. Mark as cancelling each URB from this Irp,
|
|
they may be linked, and then put the endpoint into a paused state. The
|
|
function, Cancel TDs for ED will then remove all TD associated with a
|
|
cancelling URB.
|
|
|
|
STATUS_PENDING_QUEUED means we put it in the endpoint QUEUE
|
|
STATUS_PENDING_SUBMITTED means we programmed it to the hardware
|
|
|
|
Arguments:
|
|
Device: The device Object for which the Irp was destined.
|
|
Irp: The doomed IRP.
|
|
|
|
--*/
|
|
{
|
|
PHCD_DEVICE_DATA DeviceData;
|
|
PHCD_ENDPOINT endpoint;
|
|
KIRQL oldIrql;
|
|
struct _URB_HCD_COMMON_TRANSFER *transfer;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PUSBD_EXTENSION de;
|
|
BOOLEAN wasOnHardware = FALSE;
|
|
BOOLEAN wasQueued = TRUE;
|
|
|
|
//
|
|
// first we need our extension
|
|
//
|
|
|
|
de = UsbDeviceObject->DeviceExtension;
|
|
if (de->TrueDeviceExtension == de) {
|
|
deviceObject = UsbDeviceObject;
|
|
} else {
|
|
de = de->TrueDeviceExtension;
|
|
deviceObject = de->HcdDeviceObject;
|
|
}
|
|
|
|
DeviceData = (PHCD_DEVICE_DATA) deviceObject->DeviceExtension;
|
|
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_CANCEL_TRACE,
|
|
("'Cancel Transfer irp = %x\n", Irp));
|
|
|
|
ASSERT(TRUE == Irp->Cancel);
|
|
|
|
transfer = &((PHCD_URB) URB_FROM_IRP(Irp))->HcdUrbCommonTransfer;
|
|
|
|
endpoint = transfer->hca.HcdEndpoint;
|
|
ASSERT_ENDPOINT(endpoint);
|
|
|
|
LOGENTRY(G, 'CANx', DeviceData, Irp, endpoint);
|
|
IoSetCancelRoutine(Irp, NULL);
|
|
|
|
// lock down the endpoint
|
|
OpenHCI_LockAndCheckEndpoint(endpoint,
|
|
NULL,
|
|
NULL,
|
|
&oldIrql);
|
|
|
|
//
|
|
// now find all transfers linked to this irp and
|
|
// cancel them, 'transfer' points to the first one.
|
|
//
|
|
|
|
while (transfer) {
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_CANCEL_INFO,
|
|
("'Canceling URB trans 0x%x End 0x%x \n",
|
|
transfer, endpoint));
|
|
|
|
LOGENTRY(G, 'CAur', Irp, transfer, endpoint);
|
|
|
|
ASSERT((URB_FUNCTION_CONTROL_TRANSFER == transfer->Function) ||
|
|
(URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER == transfer->Function) ||
|
|
(URB_FUNCTION_ISOCH_TRANSFER == transfer->Function));
|
|
|
|
// Here we are assuming that all URBs on a single IRP have are
|
|
// desten for the same endpoint.
|
|
ASSERT(transfer->hca.HcdEndpoint == endpoint);
|
|
|
|
//
|
|
// cancel this transfer
|
|
//
|
|
if (transfer->Status == HCD_PENDING_STATUS_QUEUED) {
|
|
//
|
|
// See if the request is still queued and we need to remove it
|
|
//
|
|
if (transfer->hca.HcdListEntry.Flink != NULL)
|
|
{
|
|
// pull it off our queue
|
|
//
|
|
LOGENTRY(G, 'caQR', Irp, transfer, endpoint);
|
|
|
|
OHCI_ASSERT(transfer->hca.HcdListEntry.Blink != NULL);
|
|
|
|
RemoveEntryList(&transfer->hca.HcdListEntry);
|
|
|
|
transfer->hca.HcdListEntry.Flink = NULL;
|
|
transfer->hca.HcdListEntry.Blink = NULL;
|
|
}
|
|
else
|
|
{
|
|
// The request no longer appears to be queued on the
|
|
// endpoint.
|
|
//
|
|
LOGENTRY(G, 'caNR', Irp, transfer, endpoint);
|
|
|
|
wasQueued = FALSE;
|
|
}
|
|
|
|
} else if (transfer->Status == HCD_PENDING_STATUS_SUBMITTED) {
|
|
//
|
|
// this transfer urb is on the hardware
|
|
//
|
|
LOGENTRY(G, 'caPR', Irp, transfer, endpoint);
|
|
transfer->Status = USBD_STATUS_CANCELING;
|
|
|
|
// put it on the cancel list before we release with
|
|
// the endpoint, onec we release the endpoint
|
|
// CaneclTDsforED can pick it up and cancel it
|
|
wasOnHardware = TRUE;
|
|
|
|
OHCI_ASSERT(transfer->hca.HcdListEntry.Flink == NULL);
|
|
LOGENTRY(G, 'cXAQ', Irp, transfer, transfer->Status);
|
|
|
|
// CancelTDsFor ED will put this on the cancel list
|
|
// when the TDS have been removed from the EP
|
|
//InsertTailList(&endpoint->CancelList,
|
|
// &transfer->hca.HcdListEntry);
|
|
}
|
|
|
|
if (transfer->UrbLink) {
|
|
transfer = &transfer->UrbLink->HcdUrbCommonTransfer;
|
|
} else {
|
|
transfer = NULL;
|
|
}
|
|
}
|
|
|
|
OpenHCI_UnlockEndpoint(endpoint,
|
|
oldIrql);
|
|
|
|
//
|
|
// the transfer is now removed from the endpoint
|
|
// it is safe to let things run now
|
|
|
|
LOGENTRY(G, 'Rcsp', DeviceData, Irp, Irp->CancelIrql);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
// now complete the transfers that were queued
|
|
|
|
if (wasOnHardware) {
|
|
// note that if the transfer was on the hardware it may be complet
|
|
// by now so we cannot touch it
|
|
LOGENTRY(G, 'WAhw', endpoint, 0, 0);
|
|
|
|
// this will cause cancelTDs for ED to pick it up and
|
|
// cancel it
|
|
OpenHCI_PauseED(endpoint);
|
|
|
|
} else if (wasQueued) {
|
|
|
|
transfer = &((PHCD_URB) URB_FROM_IRP(Irp))->HcdUrbCommonTransfer;
|
|
|
|
while (transfer) {
|
|
|
|
LOGENTRY(G, 'caDN', Irp, transfer, transfer->Status);
|
|
|
|
if (transfer->Status == HCD_PENDING_STATUS_QUEUED) {
|
|
|
|
struct _URB_HCD_COMMON_TRANSFER *nextTransfer;
|
|
|
|
LOGENTRY(G, 'caQD', Irp, transfer, transfer->Status);
|
|
|
|
// check the link before we complete
|
|
if (transfer->UrbLink) {
|
|
nextTransfer = &transfer->UrbLink->HcdUrbCommonTransfer;
|
|
} else {
|
|
nextTransfer = NULL;
|
|
}
|
|
|
|
OpenHCI_CompleteUsbdTransferRequest((PHCD_URB) transfer,
|
|
USBD_STATUS_CANCELED,
|
|
STATUS_CANCELLED,
|
|
FALSE);
|
|
transfer = nextTransfer;
|
|
|
|
} else if (transfer->Status == USBD_STATUS_CANCELING) {
|
|
//
|
|
// at least one of the urbs for this Irp is on the hardware
|
|
// so we'll need to stop the ed and remove the TDs.
|
|
//
|
|
TRAP();
|
|
// sould not get here
|
|
|
|
} else {
|
|
// the transfer completed while we were canecling
|
|
//
|
|
// we need to complete it here
|
|
//
|
|
struct _URB_HCD_COMMON_TRANSFER *nextTransfer;
|
|
|
|
|
|
LOGENTRY(G, 'CALc', 0, transfer, transfer->Status);
|
|
// check the link before we complete
|
|
if (transfer->UrbLink) {
|
|
nextTransfer = &transfer->UrbLink->HcdUrbCommonTransfer;
|
|
} else {
|
|
nextTransfer = NULL;
|
|
}
|
|
|
|
OpenHCI_CompleteUsbdTransferRequest((PHCD_URB) transfer,
|
|
USBD_STATUS_CANCELED,
|
|
STATUS_CANCELLED,
|
|
FALSE);
|
|
transfer = nextTransfer;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
OpenHCI_KdPrintDD(DeviceData,
|
|
OHCI_DBG_CANCEL_TRACE, ("'Exit Cancel Trans\n"));
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
OpenHCI_ProcessDoneTD(
|
|
PHCD_DEVICE_DATA DeviceData,
|
|
PHCD_TRANSFER_DESCRIPTOR Td,
|
|
BOOLEAN FreeTD
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parameters
|
|
|
|
--*/
|
|
{
|
|
PHCD_URB urb;
|
|
struct _URB_HCD_COMMON_TRANSFER *transfer;
|
|
PHCD_ENDPOINT endpoint;
|
|
PHC_TRANSFER_DESCRIPTOR hcTD;
|
|
PHC_ENDPOINT_DESCRIPTOR hcED;
|
|
USBD_STATUS usbdStatus;
|
|
BOOLEAN complete = FALSE;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
KIRQL oldIrql;
|
|
|
|
hcTD = &Td->HcTD;
|
|
|
|
LOGENTRY(G, 'TDdn', DeviceData, Td, 0);
|
|
|
|
if (Td->Canceled) {
|
|
// td has been marked canceled -- this means it was
|
|
// already processed by cancel, all we need to do is
|
|
// free it.
|
|
// (See the CancelTDsForED routine)
|
|
LOGENTRY(G, 'frCA', DeviceData, Td, 0);
|
|
if (FreeTD) {
|
|
OpenHCI_Free_HcdTD(DeviceData, Td);
|
|
}
|
|
return;
|
|
}
|
|
|
|
OHCI_ASSERT(Td->UsbdRequest != MAGIC_SIG);
|
|
|
|
urb = Td->UsbdRequest;
|
|
endpoint = Td->Endpoint;
|
|
transfer = &urb->HcdUrbCommonTransfer;
|
|
|
|
LOGENTRY(G, 'TDde', endpoint, urb, transfer);
|
|
|
|
ASSERT_ENDPOINT(endpoint);
|
|
OHCI_ASSERT(urb);
|
|
OHCI_ASSERT(transfer);
|
|
OHCI_ASSERT(TD_NOREQUEST_SIG != urb);
|
|
|
|
// process the completed TD
|
|
|
|
ed = endpoint->HcdED;
|
|
hcED = &ed->HcED;
|
|
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_NOISE,
|
|
("'Endpoint: %x ED: %x\n", endpoint, ed));
|
|
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_TRACE,
|
|
("'TD Done, code: %x, Directn: %x, addr: %d, end#: %d\n",
|
|
hcTD->ConditionCode, hcTD->Direction,
|
|
hcED->FunctionAddress, hcED->EndpointNumber));
|
|
|
|
if (endpoint->Type == USB_ENDPOINT_TYPE_ISOCHRONOUS) {
|
|
complete =
|
|
OpenHCI_ProcessDoneIsoTD(DeviceData,
|
|
Td,
|
|
endpoint,
|
|
hcTD,
|
|
&status,
|
|
&usbdStatus,
|
|
urb,
|
|
FreeTD);
|
|
} else {
|
|
complete =
|
|
OpenHCI_ProcessDoneAsyncTD(DeviceData,
|
|
Td,
|
|
endpoint,
|
|
hcTD,
|
|
&status,
|
|
&usbdStatus,
|
|
urb,
|
|
FreeTD);
|
|
}
|
|
|
|
if (complete) {
|
|
|
|
//
|
|
// urb associated with this TD is complete
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
|
|
//
|
|
// now that we have cleared the cancel routine
|
|
// it is safe to modify the status field of
|
|
// the urb.
|
|
//
|
|
|
|
IoSetCancelRoutine(transfer->hca.HcdIrp, NULL);
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
// now comlete the urb
|
|
LOGENTRY(G, 'urbC', urb, status, usbdStatus);
|
|
OpenHCI_CompleteUsbdTransferRequest(urb,
|
|
usbdStatus,
|
|
status,
|
|
TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
OpenHCI_ProcessDoneQueue(
|
|
PHCD_DEVICE_DATA DeviceData,
|
|
ULONG physHcTD
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Periodically the HC places the list of TD's that is has completed
|
|
on to the DoneList.
|
|
This routine, called by Dpc for ISR, walks that list, which must
|
|
be reversed, and finishes any processing.
|
|
|
|
There are packets that completed normally, Ins and outs.
|
|
There are also canceled TDs,
|
|
|
|
Parameters
|
|
|
|
physHcTD - Hcca Done Head pointer (logical address)
|
|
|
|
--*/
|
|
{
|
|
PHCD_TRANSFER_DESCRIPTOR td, tdList = NULL;
|
|
PHC_OPERATIONAL_REGISTER HC;
|
|
KIRQL oldIrql;
|
|
#if DBG
|
|
//PCHAR Buffer;
|
|
#endif
|
|
BOOLEAN complete = FALSE;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
OpenHCI_KdPrintDD(DeviceData,
|
|
OHCI_DBG_TD_TRACE, ("'Phys TD:%x\n", physHcTD));
|
|
|
|
if (0 == physHcTD) {
|
|
//
|
|
// nothing to do
|
|
//
|
|
LOGENTRY(G, 'idle', DeviceData, 0, 0);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// OK we have some TDs on the list,
|
|
// process them
|
|
|
|
HC = DeviceData->HC;
|
|
|
|
KeAcquireSpinLock(&DeviceData->PageListSpin, &oldIrql);
|
|
|
|
do {
|
|
td = OpenHCI_LogDesc_to_PhyDesc(DeviceData, physHcTD);
|
|
|
|
LOGENTRY(G, 'dnTD', DeviceData, td, physHcTD);
|
|
OHCI_ASSERT(td);
|
|
//
|
|
// If TD comes back from LogDesc to PhyDesc as zero then
|
|
// this was not a HcTD corresponding to a know HcdTD.
|
|
// The controller has given us a bogus hardware address for HcTD.
|
|
//
|
|
if (td == NULL)
|
|
{
|
|
// Something is not quite right, hope we haven't lost some TDs.
|
|
//
|
|
break;
|
|
}
|
|
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_NOISE,
|
|
("'---Virt TD %x from done list: %x\n",
|
|
td, physHcTD));
|
|
physHcTD = td->HcTD.NextTD;
|
|
|
|
//
|
|
// Since the HC places TD's into the done list
|
|
// in the reverse order in which they were processed,
|
|
// we nee to reverse this list.
|
|
//
|
|
// We do this by bastadizing the NextTD which used
|
|
// to hold logical addresses.
|
|
// This ASSUMES that a ULONG is the same size as a pointer.
|
|
//
|
|
LOGENTRY(G, 'dnT1', td, td->SortNext, (ULONG_PTR) tdList);
|
|
LOGENTRY(G, 'dnTn', td, physHcTD, 0);
|
|
td->SortNext = (ULONG_PTR) tdList;
|
|
tdList = td;
|
|
|
|
} while (physHcTD);
|
|
|
|
KeReleaseSpinLock(&DeviceData->PageListSpin, oldIrql);
|
|
|
|
//
|
|
// tdList is now a linked list of completed TDs in the
|
|
// order of completion, walk the list processing each one
|
|
|
|
while (NULL != tdList) {
|
|
td = tdList;
|
|
|
|
/* bastardizing */
|
|
tdList = (PHCD_TRANSFER_DESCRIPTOR) td->SortNext;
|
|
|
|
OpenHCI_ProcessDoneTD(DeviceData, td, TRUE);
|
|
|
|
} /* while tdList */
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
OpenHCI_ProcessDoneAsyncTD(
|
|
PHCD_DEVICE_DATA DeviceData,
|
|
PHCD_TRANSFER_DESCRIPTOR Td,
|
|
PHCD_ENDPOINT Endpoint,
|
|
PHC_TRANSFER_DESCRIPTOR HcTD,
|
|
NTSTATUS *NtStatus,
|
|
USBD_STATUS *UsbdStatus,
|
|
PHCD_URB Urb,
|
|
BOOLEAN FreeTD
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parameters
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN control;
|
|
struct _URB_HCD_COMMON_TRANSFER *transfer;
|
|
BOOLEAN complete = FALSE;
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
PHCD_TRANSFER_DESCRIPTOR tn, nextTd;
|
|
PLIST_ENTRY entry;
|
|
PHC_OPERATIONAL_REGISTER HC;
|
|
|
|
transfer = &Urb->HcdUrbCommonTransfer;
|
|
ed = Endpoint->HcdED;
|
|
HC = DeviceData->HC;
|
|
|
|
control = Endpoint->Type == USB_ENDPOINT_TYPE_CONTROL;
|
|
|
|
OHCI_ASSERT(USB_ENDPOINT_TYPE_ISOCHRONOUS != Endpoint->Type);
|
|
|
|
if (HcTD->CBP) {
|
|
//
|
|
// A value of 0 here indicates a zero length data packet
|
|
// or that all bytes have been transfered.
|
|
//
|
|
// The buffer is only spec'ed for length up to two 4K pages.
|
|
// (BE is the physical address of the last byte in the
|
|
// TD buffer. CBP is the current byte pointer)
|
|
//
|
|
// TransferCount is intailized to the number of bytes to transfer,
|
|
// we need to subtract the difference between the end and
|
|
// current ptr (ie end-current = bytes not transferred) and
|
|
// update the TransferCount.
|
|
|
|
// transfer count should never go negative
|
|
// TransferCount will be zero on the status
|
|
// phase of a control transfer so we skip
|
|
// the calculation
|
|
|
|
if (Td->TransferCount) {
|
|
Td->TransferCount -=
|
|
/* have we gone further than a page? */
|
|
((((HcTD->BE ^ HcTD->CBP) & ~OHCI_PAGE_SIZE_MASK)
|
|
? OHCI_PAGE_SIZE : 0) +
|
|
/* minus the data buffer not used */
|
|
((HcTD->BE & OHCI_PAGE_SIZE_MASK) -
|
|
(HcTD->CBP & OHCI_PAGE_SIZE_MASK)+1));
|
|
}
|
|
LOGENTRY(G, 'xfrB', HcTD->BE & OHCI_PAGE_SIZE_MASK,
|
|
HcTD->CBP & OHCI_PAGE_SIZE_MASK,
|
|
Td->TransferCount);
|
|
}
|
|
if (!control ||
|
|
HcTDDirection_Setup != HcTD->Direction) {
|
|
|
|
// data phase of a control transfer or a bulk/int
|
|
// data transfer
|
|
LOGENTRY(G, 'BIdt', Td, transfer, Td->TransferCount);
|
|
|
|
if (transfer->TransferBufferMDL) {
|
|
|
|
transfer->TransferBufferLength += Td->TransferCount;
|
|
|
|
#if DBG
|
|
if (0 == Td->TransferCount) {
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_ERROR,
|
|
("'TD no data %x %x\n", Td->HcTD.CBP, Td->HcTD.BE));
|
|
}
|
|
#endif
|
|
//
|
|
// ASSERT (TD->TransferCount);
|
|
// TransferCount could be zero but only if the TD returned in
|
|
// error.
|
|
//
|
|
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_NOISE,
|
|
("'Data Transferred 0x%x bytes \n", Td->TransferCount));
|
|
LOGENTRY(G, 'xfrT', DeviceData, Td->TransferCount, 0);
|
|
|
|
#if 0
|
|
{
|
|
PUCHAR buffer =
|
|
MmGetSystemAddressForMdl(transfer->TransferBufferMDL);
|
|
|
|
//
|
|
// Print out contents of buffer received.
|
|
//
|
|
if ((trans->TransferFlags & USBD_TRANSFER_DIRECTION_IN) &&
|
|
(USB_ENDPOINT_TYPE_CONTROL == Endpoint->Type)) {
|
|
ULONG j;
|
|
OpenHCI_KdPrintDD(DeviceData,
|
|
OHCI_DBG_TD_NOISE, ("'Buffer: "));
|
|
for (j = 0;
|
|
(j < trans->TransferBufferLength) && (j < 16);
|
|
j++) {
|
|
if (DeviceData->DebugLevel & OHCI_DBG_TD_NOISE) {
|
|
DbgPrint("'%02.2x ", ((unsigned int) *(Buffer + j)));
|
|
}
|
|
}
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_NOISE, ("'\n"));
|
|
}
|
|
}
|
|
#endif
|
|
} /* trans->TransferBufferMDL */
|
|
}
|
|
|
|
// If EP_ONE_TD is set for the endpoint then the buffer Rounding
|
|
// bit will be set for every TD, regardless of whether or not the
|
|
// USBD_SHORT_TRANSFER_OK is set. If the buffer Rounding bit is
|
|
// set then the condition code will be NoError even if there is
|
|
// a short transfer. If there is a short transfer, CBP will be
|
|
// non-zero. If a short transfer occurs when EP_ONE_TD is set
|
|
// and there isn't any other error, pretend there was a DataUnderrun
|
|
// error so the TD error handling code will remove all of the
|
|
// remaining TDs for the transfer. If USBD_SHORT_TRANSFER_OK is set,
|
|
// the TD error handling code will then pretend the pretend
|
|
// DataUnderrun error didn't occur.
|
|
//
|
|
if ((Endpoint->EpFlags & EP_ONE_TD) &&
|
|
(HcCC_NoError == HcTD->ConditionCode) &&
|
|
(HcTD->CBP != 0))
|
|
{
|
|
LOGENTRY(G, 'Shrt', HcTD->Control, Td->HcTD.CBP, Td->HcTD.BE);
|
|
|
|
ed->HcED.HeadP |= HcEDHeadP_HALT;
|
|
|
|
HcTD->ConditionCode = HcCC_DataUnderrun;
|
|
}
|
|
|
|
//
|
|
// check for errors
|
|
//
|
|
|
|
if (HcCC_NoError == HcTD->ConditionCode) {
|
|
|
|
//
|
|
// TD completed without error, remove it from
|
|
// the USBD_REQUEST list, if USBD_REQUEST list
|
|
// is now empty, then complete.
|
|
//
|
|
|
|
Endpoint->HcdHeadP = Td->NextHcdTD;
|
|
// Remove the TD from the HCD list of TDs
|
|
//
|
|
// Note: Currently we only have one irq DPC routine running
|
|
// at a time. This means that the CancelTDsForED and
|
|
// ProcessDoneQ cannot run concurrently. For this reason,
|
|
// we do not need to worry with moving the Endpoint's head
|
|
// pointer to TD's.
|
|
//
|
|
// If these routines could run concurrently then DoneQ
|
|
// could test for canceled. The Cancle routine could then
|
|
// notice that a TD was in the DoneQ and mark it for cancel
|
|
// then a canceled TD would be here. The RemoveListEntry
|
|
// call would fail as would the CompleteUsbdRequest.
|
|
//
|
|
//
|
|
// Note also that because the linked list of SoftwareTD's
|
|
// is only a singly linked list (Endpoint->HcdHeadP = TD->Next)
|
|
// We cannot receive TDs out of order.
|
|
//
|
|
|
|
RemoveEntryList(&Td->RequestList);
|
|
|
|
if (IsListEmpty(&transfer->hca.HcdListEntry2)) {
|
|
//
|
|
// no more TD's for this URB.
|
|
//
|
|
complete = TRUE;
|
|
*UsbdStatus = USBD_STATUS_SUCCESS;
|
|
*NtStatus = STATUS_SUCCESS;
|
|
LOGENTRY(G, 'xfrC', DeviceData, transfer, 0);
|
|
|
|
if (Endpoint->TrueTail) {
|
|
// this transfer complete, set true tail ptr to
|
|
// NULL (trueTail is only used when we limit the
|
|
// endpoint to one active TD)
|
|
OHCI_ASSERT(Endpoint->TrueTail == Endpoint->HcdHeadP);
|
|
Endpoint->TrueTail = NULL;
|
|
}
|
|
|
|
if (FreeTD) {
|
|
OpenHCI_Free_HcdTD(DeviceData, Td);
|
|
}
|
|
|
|
} else {
|
|
LOGENTRY(G, 'xfrP', DeviceData, transfer, Endpoint->TrueTail);
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_TRACE,
|
|
("'No Complete IRP more TD pending\n"));
|
|
|
|
// see if we need to update the tail ptr
|
|
if (Endpoint->TrueTail) {
|
|
nextTd = Td->NextHcdTD;
|
|
//HcED headp should be pointing to this TD
|
|
OHCI_ASSERT((ed->HcED.HeadP & ~HcEDHeadP_FLAGS) == nextTd->PhysicalAddress);
|
|
LOGENTRY(G, 'upTL', Endpoint->TrueTail, Endpoint, nextTd);
|
|
|
|
// The nextTD should not be the same as the TrueTail TD.
|
|
// If it was, then that should mean that there are no TDs
|
|
// left for this URB and the IsListEmpty() above should
|
|
// have been true and we should not be in this else clause.
|
|
//
|
|
OHCI_ASSERT(nextTd != Endpoint->TrueTail);
|
|
|
|
// We have already asserted that the HeadP is the same
|
|
// as nextTD at this point. Bump nextTD to the next one
|
|
// for the new TailP.
|
|
//
|
|
nextTd = nextTd->NextHcdTD;
|
|
|
|
LOGENTRY(G, 'upT2', Endpoint->TrueTail, Endpoint, nextTd);
|
|
Endpoint->HcdTailP = nextTd;
|
|
ed->HcED.TailP = nextTd->PhysicalAddress;
|
|
ENABLE_LIST(HC, Endpoint)
|
|
}
|
|
|
|
if (FreeTD) {
|
|
OpenHCI_Free_HcdTD(DeviceData, Td);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
* TD completed with an error, remove it and
|
|
* the TDs for the same request, set appropriate
|
|
* status in USBD_REQUEST and then complete it.
|
|
*
|
|
* TWO SPECIAL CASES:
|
|
* (1)
|
|
* DataUnderrun for Bulk or Interrupt and
|
|
* ShortXferOK. do not report error to USBD
|
|
* and restart the endpoint.
|
|
* (2)
|
|
* DataUnderrun on Control and ShortXferOK.
|
|
* The final status TD for the Request should
|
|
* not be canceled, the Request should not be
|
|
* completed, and the endpoint should be
|
|
* restarted.
|
|
*
|
|
* NOTE:
|
|
* In all error cases the endpoint
|
|
* has been halted by controller.
|
|
*/
|
|
|
|
LOGENTRY(G, 'tERR', DeviceData, Td, Td->HcTD.ConditionCode);
|
|
|
|
// The ED better be Halted because we're going to mess with
|
|
// the ED HeadP and TailP pointers
|
|
//
|
|
ASSERT(ed->HcED.HeadP & HcEDHeadP_HALT);
|
|
|
|
if (Endpoint->TrueTail) {
|
|
LOGENTRY(G, 'eTTl', Endpoint->TrueTail,
|
|
Endpoint->HcdTailP, Endpoint);
|
|
|
|
// Bump the software tail pointer to the true tail for this transfer
|
|
//
|
|
Endpoint->HcdTailP = Endpoint->TrueTail;
|
|
Endpoint->TrueTail = NULL;
|
|
|
|
// Bump the hardware tail pointer to the true tail for this transfer
|
|
//
|
|
Endpoint->HcdED->HcED.TailP = Endpoint->HcdTailP->PhysicalAddress;
|
|
}
|
|
|
|
for (tn = Endpoint->HcdHeadP;
|
|
tn != Endpoint->HcdTailP;
|
|
tn = tn->NextHcdTD) {
|
|
LOGENTRY(G, 'bump', DeviceData, Td, tn);
|
|
//
|
|
// We want to flush out to the end of this current URB
|
|
// request since there could still be TD's linked together
|
|
// for the the current URB.
|
|
// Move tn until it points to the first TD that we
|
|
// wish to leave ``on the hardware''.
|
|
//
|
|
if ((Urb != tn->UsbdRequest) || /* another request */
|
|
((HcCC_DataUnderrun == Td->HcTD.ConditionCode)
|
|
&& (USBD_SHORT_TRANSFER_OK & transfer->TransferFlags)
|
|
&& (Td->HcTD.Direction != tn->HcTD.Direction))) {
|
|
// ^^^^ Here we have the status TD for a Short
|
|
// control
|
|
// Transfer. We still need run this last TD.
|
|
LOGENTRY(G, 'stpB', DeviceData, Td, Td->HcTD.ConditionCode);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Bump the software head pointer over all TD's until tn
|
|
//
|
|
Endpoint->HcdHeadP = tn;
|
|
|
|
// Bump the hardware head pointer over all TD's until tn, preserving
|
|
// the current ED Halted and toggle Carry bits.
|
|
//
|
|
ed->HcED.HeadP = tn->PhysicalAddress
|
|
| (ed->HcED.HeadP & HcEDHeadP_FLAGS);
|
|
|
|
// all TDs unlinked, now we just need to free them
|
|
while (!IsListEmpty(&transfer->hca.HcdListEntry2)) {
|
|
|
|
entry = RemoveHeadList(&transfer->hca.HcdListEntry2);
|
|
tn = CONTAINING_RECORD(entry,
|
|
HCD_TRANSFER_DESCRIPTOR,
|
|
RequestList);
|
|
if ((tn != Td) &&
|
|
(tn != Endpoint->HcdHeadP) &&
|
|
FreeTD) {
|
|
OpenHCI_Free_HcdTD(DeviceData, tn);
|
|
}
|
|
}
|
|
|
|
// if we are still pointing to the current Urb
|
|
// then this is a status phase for a short
|
|
// control transfer (ShortTransferOK).
|
|
if (Endpoint->HcdHeadP->UsbdRequest == Urb) {
|
|
TEST_TRAP();
|
|
// We should use the status of this last TD (the status td)
|
|
// to return in the URB, so place this TD back onto the list
|
|
// for this endpoint and allow things to run normally.
|
|
|
|
LOGENTRY(G, 'putB', DeviceData, Td, transfer);
|
|
InsertTailList(&transfer->hca.HcdListEntry2,
|
|
&Endpoint->HcdHeadP->RequestList);
|
|
} else {
|
|
//
|
|
// This transfer is done
|
|
//
|
|
// NOTE: we do not modify the status in the urb yet because
|
|
// the cancel routine looks at this value to determine
|
|
// what action to take.
|
|
//
|
|
|
|
if (HcCC_DataUnderrun == Td->HcTD.ConditionCode) {
|
|
//
|
|
// Behave the same way here as UHCD does. If the
|
|
// SHORT_TRANSFER_OK flag is set, ignore the DataUnderrun
|
|
// error and return USBD_STATUS_SUCCESS, else return
|
|
// USBD_STATUS_ERROR_SHORT_TRANSFER. In either case
|
|
// the endpoint is not left in the Halted state.
|
|
//
|
|
if (USBD_SHORT_TRANSFER_OK & transfer->TransferFlags) {
|
|
|
|
LOGENTRY(G, 'shOK', DeviceData, Td, transfer);
|
|
|
|
*UsbdStatus = USBD_STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
LOGENTRY(G, 'shNO', DeviceData, Td, transfer);
|
|
|
|
*UsbdStatus = USBD_STATUS_ERROR_SHORT_TRANSFER;
|
|
}
|
|
|
|
// Clear the Halted bit in the ED
|
|
//
|
|
ed->HcED.HeadP &= ~HcEDHeadP_HALT;
|
|
|
|
// Tell the HC we have something on the ED lists
|
|
//
|
|
ENABLE_LIST(HC, Endpoint);
|
|
|
|
} else {
|
|
|
|
*UsbdStatus = (Td->HcTD.ConditionCode | 0xC0000000);
|
|
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_ERROR,
|
|
("'Done queue: TD error: 0x%x CBP: 0x%x BE: 0x%x\n",
|
|
HcTD->ConditionCode,
|
|
HcTD->CBP,
|
|
HcTD->BE));
|
|
|
|
//#if DBG
|
|
// if (5 != Td->HcTD.ConditionCode) {
|
|
// TRAP(); // Code trap requested by WDM lab
|
|
// }
|
|
//#endif
|
|
}
|
|
|
|
if (FreeTD) {
|
|
OpenHCI_Free_HcdTD(DeviceData, Td);
|
|
}
|
|
complete = TRUE;
|
|
|
|
*NtStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
if ((USB_ENDPOINT_TYPE_CONTROL == Endpoint->Type) &&
|
|
(ed->HcED.HeadP & HcEDHeadP_HALT)) {
|
|
//
|
|
// For Control Endpoints, we always clear the
|
|
// halt condition automatically.
|
|
//
|
|
|
|
LOGENTRY(G, 'clrH', DeviceData, Td, transfer);
|
|
ed->HcED.HeadP &= ~HcEDHeadP_HALT;
|
|
|
|
ENABLE_LIST(HC, Endpoint);
|
|
}
|
|
} // error
|
|
|
|
return complete;
|
|
}
|
|
|
|
|
|
USBD_STATUS
|
|
OpenHCI_ProcessHWPacket(
|
|
struct _URB_ISOCH_TRANSFER *Iso,
|
|
PHCD_TRANSFER_DESCRIPTOR Td,
|
|
ULONG Idx,
|
|
ULONG LastFrame
|
|
)
|
|
{
|
|
struct _USBD_ISO_PACKET_DESCRIPTOR *pkt;
|
|
PHC_OFFSET_PSW psw;
|
|
USBD_STATUS err;
|
|
ULONG length;
|
|
|
|
pkt = &(Iso->IsoPacket[Idx + Td->BaseIsocURBOffset]);
|
|
psw = &(Td->HcTD.Packet[Idx]);
|
|
//
|
|
// always return whatever size we got
|
|
// unless the error was Not_accessed
|
|
//
|
|
if (Iso->TransferFlags & USBD_TRANSFER_DIRECTION_IN) {
|
|
length = (psw->PSW & HcPSW_RETURN_SIZE);
|
|
} else {
|
|
// compute the length requested
|
|
length = Iso->IsoPacket[Idx + Td->BaseIsocURBOffset].Length;
|
|
}
|
|
|
|
err = (psw->PSW & HcPSW_CONDITION_CODE_MASK)
|
|
>> HcPSW_CONDITION_CODE_SHIFT;
|
|
|
|
LOGENTRY(G, 'PKTs',
|
|
err,
|
|
(psw->PSW & HcPSW_CONDITION_CODE_MASK)
|
|
>> HcPSW_CONDITION_CODE_SHIFT,
|
|
length);
|
|
|
|
switch(err) {
|
|
case HcCC_DataUnderrun :
|
|
// not a full packet,
|
|
// data underrun is OK, we don't fail the
|
|
// urb but we do return the status for the
|
|
// packet.
|
|
//
|
|
pkt->Status = err;
|
|
err = USBD_STATUS_SUCCESS;
|
|
LOGENTRY(G, 'ISOu', err, Idx, 0);
|
|
break;
|
|
case HcCC_NotAccessed:
|
|
length = 0;
|
|
break;
|
|
}
|
|
|
|
if (err) {
|
|
//
|
|
// we have a legitamate error
|
|
//
|
|
err |= 0xC0000000;
|
|
pkt->Status = err;
|
|
LOGENTRY(G, 'ISO!', err, Idx, 0);
|
|
}
|
|
|
|
// set return length
|
|
|
|
if (Iso->TransferFlags & USBD_TRANSFER_DIRECTION_IN) {
|
|
// return the length for in transfers
|
|
pkt->Length = length;
|
|
}
|
|
|
|
// update length with whatever we got
|
|
Iso->TransferBufferLength +=
|
|
length;
|
|
|
|
LOGENTRY(G, 'isoL', length, Iso->TransferBufferLength, 0);
|
|
|
|
if (err == USBD_STATUS_SUCCESS) {
|
|
pkt->Status = USBD_STATUS_SUCCESS;
|
|
} else {
|
|
Iso->ErrorCount++;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
OpenHCI_ProcessDoneIsoTD(
|
|
PHCD_DEVICE_DATA DeviceData,
|
|
PHCD_TRANSFER_DESCRIPTOR Td,
|
|
PHCD_ENDPOINT Endpoint,
|
|
PHC_TRANSFER_DESCRIPTOR HcTD,
|
|
NTSTATUS *NtStatus,
|
|
USBD_STATUS *UsbdStatus,
|
|
PHCD_URB Urb,
|
|
BOOLEAN FreeTD
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Parameters
|
|
|
|
--*/
|
|
{
|
|
struct _URB_HCD_COMMON_TRANSFER *transfer;
|
|
struct _URB_ISOCH_TRANSFER *iso;
|
|
BOOLEAN complete = FALSE;
|
|
PHCD_ENDPOINT_DESCRIPTOR ed;
|
|
BOOLEAN gotError = FALSE;
|
|
ULONG frames, j, lengthCurrent;
|
|
USBD_STATUS status;
|
|
|
|
|
|
iso = (PVOID) transfer = (PVOID) &Urb->HcdUrbCommonTransfer;
|
|
ed = Endpoint->HcdED;
|
|
|
|
OHCI_ASSERT(USB_ENDPOINT_TYPE_ISOCHRONOUS == Endpoint->Type);
|
|
|
|
// Frames is one less than the number of PSW's
|
|
// in this TD.
|
|
frames = (Td->HcTD.Control & HcTDControl_FRAME_COUNT_MASK)
|
|
>> HcTDControl_FRAME_COUNT_SHIFT;
|
|
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_NOISE,
|
|
("'--------Done q: ISO pkt!\n"));
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_NOISE,
|
|
("' Starting Frame: %x Frame Cnt: %x Cur Frame: %x\n",
|
|
Td->HcTD.StartingFrame,
|
|
Td->HcTD.FrameCount + 1,
|
|
Get32BitFrameNumber(DeviceData)));
|
|
|
|
LOGENTRY(M, 'IsoD', Td, transfer, 0);
|
|
|
|
*NtStatus = STATUS_SUCCESS;
|
|
*UsbdStatus = USBD_STATUS_SUCCESS;
|
|
|
|
// Remove the TD from the HCD list of TDs
|
|
Endpoint->HcdHeadP = Td->NextHcdTD;
|
|
|
|
// walk through the TD and update the packet entries
|
|
// in the urb
|
|
OHCI_ASSERT(frames <= 7);
|
|
|
|
for (j = 0; j < frames+1; j++) {
|
|
status = OpenHCI_ProcessHWPacket(iso, Td, j, frames);
|
|
if (USBD_ERROR(status)) {
|
|
gotError = TRUE;
|
|
}
|
|
}
|
|
|
|
if (gotError) {
|
|
OpenHCI_KdPrintDD(DeviceData, OHCI_DBG_TD_ERROR,
|
|
("'Done queue: ISOC TD error: 0x%x CBP: 0x%x BE: 0x%x\n",
|
|
HcTD->ConditionCode,
|
|
HcTD->CBP,
|
|
HcTD->BE));
|
|
LOGENTRY(M, 'IsoE', Td->HcTD.ConditionCode,
|
|
Td->HcTD.CBP,
|
|
Td->HcTD.BE);
|
|
// TEST_TRAP();
|
|
//
|
|
// For Isoc and Control Endpoints, we always clear the halt
|
|
// condition automatically.
|
|
//
|
|
if (ed->HcED.HeadP & HcEDHeadP_HALT) {
|
|
ed->HcED.HeadP &= ~HcEDHeadP_HALT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// the TD is done
|
|
//
|
|
|
|
//
|
|
// flush buffers or this TD, we flush up to the offset+length
|
|
// of the last packet in this TD
|
|
//
|
|
lengthCurrent = iso->IsoPacket[frames + Td->BaseIsocURBOffset].Offset +
|
|
iso->IsoPacket[frames + Td->BaseIsocURBOffset].Length;
|
|
|
|
OHCI_ASSERT(transfer->TransferBufferMDL);
|
|
|
|
RemoveEntryList(&Td->RequestList);
|
|
if (FreeTD) {
|
|
OpenHCI_Free_HcdTD(DeviceData, Td);
|
|
}
|
|
|
|
LOGENTRY(M, 'Iso>', lengthCurrent,
|
|
transfer,
|
|
&transfer->hca.HcdListEntry2);
|
|
|
|
if (IsListEmpty(&transfer->hca.HcdListEntry2)) {
|
|
// AKA no more TD's for this URB.
|
|
if (iso->ErrorCount == iso->NumberOfPackets) {
|
|
// all errors set error code for urb
|
|
*UsbdStatus = USBD_STATUS_ISOCH_REQUEST_FAILED;
|
|
}
|
|
LOGENTRY(M, 'IsoC', *UsbdStatus,
|
|
transfer->TransferBufferLength,
|
|
iso->ErrorCount);
|
|
|
|
//
|
|
// zero out the length field for OUT transfers
|
|
//
|
|
if (USBD_TRANSFER_DIRECTION(iso->TransferFlags) ==
|
|
USBD_TRANSFER_DIRECTION_OUT) {
|
|
for (j=0; j< iso->NumberOfPackets; j++) {
|
|
iso->IsoPacket[j].Length = 0;
|
|
}
|
|
}
|
|
|
|
*NtStatus = STATUS_SUCCESS;
|
|
complete = TRUE;
|
|
}
|
|
|
|
return complete;
|
|
}
|