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

964 lines
23 KiB
C

/*++
Copyright (c) 1995,1996 Microsoft Corporation
:ts=4
Module Name:
hub.c
Abstract:
The UHC driver for USB, this module contains the root hub
interface code.
Environment:
kernel mode only
Notes:
Revision History:
2-08-96 : created
--*/
#include "wdm.h"
#include "stdarg.h"
#include "stdio.h"
#include "usbdi.h"
#include "hcdi.h"
#include "uhcd.h"
typedef struct _ROOT_HUB_TIMER {
KTIMER Timer;
KDPC Dpc;
PDEVICE_OBJECT DeviceObject;
PROOTHUB_TIMER_ROUTINE TimerRoutine;
PVOID Context;
} ROOT_HUB_TIMER, *PROOT_HUB_TIMER;
NTSTATUS
UHCD_RootHub_OpenEndpoint(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PHCD_URB Urb
)
/*++
Routine Description:
This function is called at Dispatch level
to process commands bound for the root hub.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Urb - urb for this request
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension;
PUHCD_ENDPOINT endpoint;
PUSB_ENDPOINT_DESCRIPTOR endpointDescriptor;
UHCD_KdPrint((2, "'enter UHCD_RootHub_OpenEndpoint\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
LOGENTRY(LOG_MISC, 'rhOE', deviceExtension, 0, 0);
endpoint = (PUHCD_ENDPOINT) GETHEAP(NonPagedPool,
sizeof(UHCD_ENDPOINT));
if (endpoint) {
//
// initialize endpoint structures, state variables.
//
// start endpoint initialization
RtlZeroMemory(endpoint, sizeof(*endpoint));
endpoint->Sig = SIG_EP;
SET_EPFLAG(endpoint, EPFLAG_ROOT_HUB);
endpointDescriptor = Urb->HcdUrbOpenEndpoint.EndpointDescriptor;
endpoint->MaxRequests = MAX_REQUESTS(endpointDescriptor,
endpoint->EndpointFlags);
ntStatus = UHCD_FinishInitializeEndpoint(DeviceObject,
endpoint,
endpointDescriptor,
Urb);
if (NT_SUCCESS(ntStatus)) {
Urb->HcdUrbOpenEndpoint.HcdEndpoint = endpoint;
URB_HEADER(Urb).Status = USBD_STATUS_SUCCESS;
//
// if this is the interrupt endpoint start a timer Dpc to be called
// based on the endpoint polling interval.
//
if (endpoint->Type == USB_ENDPOINT_TYPE_INTERRUPT) {
LARGE_INTEGER dueTime;
LONG period;
endpoint->Interval = endpointDescriptor->bInterval;
UHCD_ASSERT(endpoint->Interval != 0);
deviceExtension->RootHubInterruptEndpoint = endpoint;
KeInitializeTimer(&deviceExtension->RootHubPollTimer);
KeInitializeDpc(&deviceExtension->RootHubPollDpc,
UHCD_RootHubPollDpc,
DeviceObject);
deviceExtension->RootHubPollTimerInitialized = TRUE;
dueTime.QuadPart = -10000 * endpoint->Interval;
period = 100; //every 100 ms
UHCD_KdPrint((2, "'UHCD Poll Interval = (0x%x) %x %x\n",
endpoint->Interval, dueTime.LowPart, dueTime.HighPart));
KeSetTimerEx(&deviceExtension->RootHubPollTimer,
dueTime,
period,
&deviceExtension->RootHubPollDpc);
}
} else {
RETHEAP(endpoint);
}
}
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
UHCD_KdPrint((2, "'exit UHCD_RootHub_OpenEndpoint (%x)\n", ntStatus));
return ntStatus;
}
NTSTATUS
UHCD_RootHub_CloseEndpoint(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PHCD_URB Urb
)
/*++
Routine Description:
This function is called at Dispatch level
to process commands bound for the root hub.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Urb - urb for this request
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension;
PUHCD_ENDPOINT endpoint;
UHCD_KdPrint((2, "'enter UHCD_RootHub_CloseEndpoint\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
endpoint = Urb->HcdUrbCloseEndpoint.HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
if (endpoint->ActiveTransfers[0]) {
URB_HEADER(Urb).Status = USBD_STATUS_ERROR_BUSY;
} else {
if (endpoint == deviceExtension->RootHubInterruptEndpoint) {
// closing the interrupt endpoint,
// this means the root hub is stopped
deviceExtension->RootHubDeviceAddress = USB_DEFAULT_DEVICE_ADDRESS;
// if the timer fires before we cancel it then this
// will stop the polling process
deviceExtension->RootHubInterruptEndpoint = NULL;
// if this is the interrupt endpoint kill the timer here
KeCancelTimer(&deviceExtension->RootHubPollTimer);
}
RETHEAP(endpoint);
URB_HEADER(Urb).Status = USBD_STATUS_SUCCESS;
}
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, NULL);
UHCD_KdPrint((2, "'exit UHCD_RootHub_CloseEndpoint (%x)\n", ntStatus));
return ntStatus;
}
NTSTATUS
UHCD_RootHub_ControlTransfer(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PHCD_URB Urb
)
/*++
Routine Description:
This function is called at Dispatch level
to process commands bound for the root hub.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Urb - urb for this request
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension;
PUHCD_ENDPOINT endpoint;
RHSTATUS rootHubReturnCode;
KIRQL irql;
PVOID mappedSystemVa;
UHCD_KdPrint((2, "'enter UHCD_RootHub_ControlTransfer\n"));
INCREMENT_PENDING_URB_COUNT(Irp);
IoAcquireCancelSpinLock(&irql);
if (Irp->Cancel) {
TEST_TRAP();
//BUGBUG Irp was canceled
IoReleaseCancelSpinLock(irql);
UHCD_CompleteIrp(DeviceObject, Irp, STATUS_CANCELLED, 0, Urb);
goto UHCD_RootHub_ControlTransfer_Done;
} else {
IoSetCancelRoutine(Irp, NULL);
IoReleaseCancelSpinLock(irql);
}
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
endpoint = HCD_AREA(Urb).HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
// NOTE:
// should not get control transfers for anything but
// the default pipe
//
UHCD_ASSERT(endpoint->EndpointAddress == 0);
//BUGBUG no support for linked URBs yet
UHCD_ASSERT(Urb->HcdUrbCommonTransfer.UrbLink == NULL);
if (endpoint->EndpointAddress != 0) {
URB_HEADER(Urb).Status = USBD_STATUS_INVALID_PARAMETER;
} else {
//
// convert transfer buffer from MDL
//
mappedSystemVa =
Urb->HcdUrbCommonTransfer.TransferBufferLength ?
Urb->HcdUrbCommonTransfer.TransferBufferMDL->MappedSystemVa :
NULL;
//UHCD_KdPrint((2, "' Mapped systemVa = 0x%x \n", mappedSystemVa));
rootHubReturnCode =
RootHub_Endpoint0(deviceExtension->RootHub,
(PRH_SETUP)Urb->HcdUrbCommonTransfer.Extension.u.SetupPacket,
(PUCHAR) &deviceExtension->RootHubDeviceAddress,
mappedSystemVa,
&Urb->HcdUrbCommonTransfer.TransferBufferLength);
switch (rootHubReturnCode) {
case RH_SUCCESS:
URB_HEADER(Urb).Status = USBD_STATUS_SUCCESS;
#if DBG
// restore the original transfer buffer address
Urb->HcdUrbCommonTransfer.TransferBuffer =
mappedSystemVa;
#endif
break;
case RH_NAK:
case RH_STALL:
// return stall error and set the endpoint state to
// stalled on the host side
if (endpoint->EndpointFlags & EPFLAG_NO_HALT) {
URB_HEADER(Urb).Status =
USBD_STATUS(USBD_STATUS_STALL_PID)
| USBD_STATUS_ERROR;
} else {
SET_EPFLAG(endpoint, EPFLAG_HOST_HALTED);
URB_HEADER(Urb).Status = USBD_STATUS_STALL_PID;
}
//
// if we get here it is probably a bug in the root hub
// code or the hub driver.
//
UHCD_KdTrap(("Root hub stalled request\n"));
}
}
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, Urb);
UHCD_RootHub_ControlTransfer_Done:
UHCD_KdPrint((2, "'exit UHCD_RootHub_ControlTransfer (%x)\n", ntStatus));
return ntStatus;
}
NTSTATUS
UHCD_RootHub_InterruptTransfer(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PHCD_URB Urb
)
/*++
Routine Description:
This function is called at Dispatch level
to process commands bound for the root hub.
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Urb - urb for this request
Return Value:
NT status code.
--*/
{
NTSTATUS ntStatus = STATUS_SUCCESS;
PDEVICE_EXTENSION deviceExtension;
PUHCD_ENDPOINT endpoint;
KIRQL irql;
PHCD_EXTENSION urbWork;
UHCD_KdPrint((2, "'enter UHCD_RootHub_InterruptTransfer\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
INCREMENT_PENDING_URB_COUNT(Irp);
endpoint = HCD_AREA(Urb).HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
ASSERT(HCD_AREA(Urb).HcdExtension != NULL);
urbWork = HCD_AREA(Urb).HcdExtension;
// set interrupt transfer to active.
urbWork->Flags |= UHCD_TRANSFER_ACTIVE;
//
// BUGBUG
// we only allow one transfer outstanding
// at a time here.
//
if (endpoint->ActiveTransfers[0] != NULL) {
TEST_TRAP();
URB_HEADER(Urb).Status = USBD_STATUS_REQUEST_FAILED;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, Urb);
} else if (Urb->HcdUrbCommonTransfer.UrbLink != NULL) {
TEST_TRAP();
URB_HEADER(Urb).Status = USBD_STATUS_INVALID_PARAMETER;
UHCD_CompleteIrp(DeviceObject, Irp, ntStatus, 0, Urb);
} else {
endpoint->ActiveTransfers[0] = Urb;
URB_HEADER(Urb).Status = UHCD_STATUS_PENDING_CURRENT;
//
// set up a cancel routine
//
IoAcquireCancelSpinLock(&irql);
if (Irp->Cancel) {
TEST_TRAP();
//BUGBUG Irp was canceled
IoReleaseCancelSpinLock(irql);
// call cancel routine
UHCD_RootHub_InterruptTransferCancel(DeviceObject, Irp);
goto UHCD_RootHub_InterruptTransfer_Done;
} else {
IoSetCancelRoutine(Irp, UHCD_RootHub_InterruptTransferCancel);
IoReleaseCancelSpinLock(irql);
}
ntStatus = STATUS_PENDING;
UHCD_KdPrint((2, "'Pending transfer for root hub\n"));
Irp->IoStatus.Status = ntStatus;
Irp->IoStatus.Information = 0;
IoMarkIrpPending(Irp);
}
UHCD_RootHub_InterruptTransfer_Done:
UHCD_KdPrint((2, "'exit UHCD_RootHub_InterruptTransfer (%x)\n", ntStatus));
return ntStatus;
}
VOID
UHCD_RootHubPoll(
IN PDEVICE_OBJECT DeviceObject,
IN PUHCD_ENDPOINT Endpoint
)
/*++
Routine Description:
This function is called at DPC level from the ISR DPC routine
to process transfers queued to for the root hub.
Arguments:
DeviceObject - pointer to a device object
Return Value:
NT status code.
--*/
{
PHCD_URB urb;
PDEVICE_EXTENSION deviceExtension;
PIRP irp;
RHSTATUS rootHubReturnCode;
BOOLEAN completeIt = TRUE;
KIRQL irql;
PVOID mappedSystemVa;
NTSTATUS status = STATUS_SUCCESS;
//UHCD_KdPrint((2, "'enter UHCD_RootHub_Poll\n"));
deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
//
// See if we need to poll an endpoint for the
// root hub.
//
// if the current PM state is not 'ON' we will not poll the endpoint
// it means the HC has not fully resumed yet.
//
if (Endpoint &&
deviceExtension->CurrentDevicePowerState == PowerDeviceD0) {
// Yes, poll
ASSERT_ENDPOINT(Endpoint);
urb = Endpoint->ActiveTransfers[0];
// see if we have an abort request
if ((Endpoint->EndpointFlags & EPFLAG_ABORT_PENDING_TRANSFERS) && urb) {
CLR_EPFLAG(Endpoint, EPFLAG_ABORT_PENDING_TRANSFERS);
urb->HcdUrbCommonTransfer.Status =
USBD_STATUS_CANCELED;
completeIt = TRUE;
// BUGBUG do we need the error bit set here?
} else if (urb) {
//
// convert transfer buffer from MDL
//
if (urb->HcdUrbCommonTransfer.TransferBufferLength != 0) {
urb->HcdUrbCommonTransfer.TransferBufferMDL->MdlFlags
|= MDL_MAPPING_CAN_FAIL;
mappedSystemVa =
MmGetSystemAddressForMdl(urb->HcdUrbCommonTransfer
.TransferBufferMDL);
urb->HcdUrbCommonTransfer.TransferBufferMDL->MdlFlags
&= ~MDL_MAPPING_CAN_FAIL;
if (mappedSystemVa == NULL) {
rootHubReturnCode = RH_STALL;
status = STATUS_INSUFFICIENT_RESOURCES;
goto UHCD_RootHubPollError;
}
} else{
mappedSystemVa = NULL;
}
//UHCD_KdPrint((2, "' Mapped systemVa = 0x%x \n", mappedSystemVa));
rootHubReturnCode =
RootHub_Endpoint1(deviceExtension->RootHub, mappedSystemVa,
&urb->HcdUrbCommonTransfer
.TransferBufferLength);
//
// set urb error code if necessary
//
switch (rootHubReturnCode) {
case RH_SUCCESS:
urb->HcdUrbCommonTransfer.Status = USBD_STATUS_SUCCESS;
#if DBG
// restore the original transfer buffer address
urb->HcdUrbCommonTransfer.TransferBuffer =
mappedSystemVa;
#endif
break;
case RH_STALL:
if (Endpoint->EndpointFlags & EPFLAG_NO_HALT) {
// if we dont halt clear th ehalt bit on
// the error code
URB_HEADER(urb).Status =
USBD_STATUS(USBD_STATUS_STALL_PID) | USBD_STATUS_ERROR;
} else {
SET_EPFLAG(Endpoint, EPFLAG_HOST_HALTED);
URB_HEADER(urb).Status = USBD_STATUS_STALL_PID;
}
//
// if we get here it is probably a bug in the root hub code
//
UHCD_KdTrap(("root hub stalled request\n"));
break;
case RH_NAK:
// NAK, don't complete request
completeIt = FALSE;
break;
default:
UHCD_KdTrap(("bogus return code from RootHub_Endpoint1\n"));
}
} else {
// no transfer, same as NAK
completeIt = FALSE;
}
UHCD_RootHubPollError:;
if (completeIt) {
UHCD_ASSERT(urb != NULL);
//
// Remove the Irp from the cancelable state
// before passing it to the Root Hub Code.
//
irp = HCD_AREA(urb).HcdIrp;
IoAcquireCancelSpinLock(&irql);
if (irp->Cancel) {
TEST_TRAP();
//BUGBUG Irp was canceled
IoReleaseCancelSpinLock(irql);
UHCD_RootHub_InterruptTransferCancel(DeviceObject, irp);
} else {
IoSetCancelRoutine(irp, NULL);
IoReleaseCancelSpinLock(irql);
//
// have data or error, complete the irp
//
// BUGBUG we are not supporting queued
// transfers for the hub driver.
Endpoint->ActiveTransfers[0] = NULL;
UHCD_ASSERT(irp != NULL);
UHCD_CompleteIrp(DeviceObject,
irp,
status,
0,
urb);
}
}
}
//UHCD_KdPrint((2, "'exit UHCD_RootHub_Poll\n"));
return;
}
//
// Root Hub Services
//
// These services are provided to the root
// hub code by UHCD.
//
USHORT
UHCD_RootHub_ReadPort(
IN PROOTHUB_PORT HubPort
)
/*++
Routine Description:
Arguments:
Return Value:
returns the value of the requested hub
register for a given controller identified
by HcdPtr.
--*/
{
PDEVICE_EXTENSION deviceExtension;
USHORT dataVal;
deviceExtension = HubPort->DeviceObject->DeviceExtension;
dataVal = READ_PORT_USHORT(
(PUSHORT) (deviceExtension->DeviceRegisters[0] +
HubPort->Address));
// UHCD_KdPrint((2, "'RooHub -- read port %x\n", dataVal));
return dataVal;
}
//BUGBUG need a return code here
VOID
UHCD_RootHub_Timer(
IN PVOID HcdPtr,
IN LONG WaitTime,
IN PROOTHUB_TIMER_ROUTINE RootHubTimerRoutine,
IN PVOID TimerContext
)
/*++
Routine Description:
This Routine can only be called at passive level,
it iniializes a timer object and starts the timer
for the root hub code.
Arguments:
HcdPtr - pointer passed to the root hub code during
initialization.
WaitTime - time to wait before signaling (in ms)
RootHubTimerRoutine - root hub function to call
when timer expires.
TimerContext - context pointer passed to RootHubTimerRoutine
when timer expires.
Return Value:
None
--*/
{
PDEVICE_OBJECT deviceObject = HcdPtr;
PDEVICE_EXTENSION deviceExtension;
LARGE_INTEGER dueTime;
PROOT_HUB_TIMER rootHubTimer;
ULONG timerIncerent;
deviceExtension = deviceObject->DeviceExtension;
rootHubTimer = GETHEAP(NonPagedPool, sizeof(ROOT_HUB_TIMER));
// compute timeout value (convert millisec to nanosec)
UHCD_ASSERT(WaitTime == 10); // BUGBUG root hub should always use 10ms
timerIncerent = KeQueryTimeIncrement() - 1;
dueTime.HighPart = -1;
// round up to the next highest timer increment
dueTime.LowPart = -1 * (10000 * WaitTime + timerIncerent);
if (rootHubTimer) {
UHCD_KdPrint((2, "'roothub timer set %d (%x %x)\n", WaitTime,
dueTime.LowPart, dueTime.HighPart));
rootHubTimer->DeviceObject = deviceObject;
rootHubTimer->TimerRoutine = RootHubTimerRoutine;
rootHubTimer->Context = TimerContext;
deviceExtension->RootHubTimersActive++;
//BUGBUG Timer DPCs not working with NTKERN
//#ifdef NTKERN
//#pragma message ("warning: using workaround for bugs in ntkern")
// (VOID) KeDelayExecutionThread(KernelMode,
// FALSE,
// &dueTime);
// UHCD_RootHubTimerDpc(&rootHubTimer->Dpc,
// rootHubTimer,
// NULL,
// NULL);
//#else
KeInitializeTimer(&rootHubTimer->Timer);
KeInitializeDpc(&rootHubTimer->Dpc,
UHCD_RootHubTimerDpc,
rootHubTimer);
KeSetTimer(&rootHubTimer->Timer,
dueTime,
&rootHubTimer->Dpc);
//#endif
}
}
VOID
UHCD_RootHubTimerDpc(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This routine runs at DISPATCH_LEVEL IRQL.
Arguments:
Dpc - Pointer to the DPC object.
DeferredContext - supplies the RootHubTimer.
SystemArgument1 - not used.
SystemArgument2 - not used.
Return Value:
None.
--*/
{
PROOT_HUB_TIMER rootHubTimer = DeferredContext;
PDEVICE_EXTENSION deviceExtension;
UHCD_KdPrint((2, "'enter roothub timer Dpc\n"));
deviceExtension = rootHubTimer->DeviceObject->DeviceExtension;
//
// call the root hub code callback
//
rootHubTimer->TimerRoutine(rootHubTimer->Context);
UHCD_ASSERT(deviceExtension->RootHubTimersActive != 0);
deviceExtension->RootHubTimersActive--;
RETHEAP(rootHubTimer);
}
VOID
UHCD_RootHub_WritePort(
IN PROOTHUB_PORT HubPort,
IN USHORT DataVal
)
/*++
Routine Description:
This routine is called by the root hub code to perform
writes to a specific HC register.
Arguments:
Return Value:
None.
--*/
{
PDEVICE_EXTENSION deviceExtension;
deviceExtension = HubPort->DeviceObject->DeviceExtension;
//
// mask off bits 13:15 (see UHCI design guide)
//
DataVal &= 0x1fff;
// UHCD_KdPrint((2, "'RooHub -- write port %x\n", DataVal));
WRITE_PORT_USHORT(
(PUSHORT) (deviceExtension->DeviceRegisters[0] +
HubPort->Address), DataVal);
}
VOID
UHCD_RootHub_InterruptTransferCancel(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This function is called to cancel at interrupt
transfer request pending in the root hub code .
Arguments:
DeviceObject - pointer to a device object
Irp - pointer to an I/O Request Packet
Return Value:
NT status code.
--*/
{
PHCD_URB urb;
PUHCD_ENDPOINT endpoint;
UHCD_KdPrint((2, "'enter UHCD_RootHub_InterruptTransferCancel\n"));
IoReleaseCancelSpinLock(Irp->CancelIrql);
urb = (PHCD_URB) URB_FROM_IRP(Irp);
// BUGBUG we are not supporting queued
// transfers for the hub driver.
endpoint = HCD_AREA(urb).HcdEndpoint;
ASSERT_ENDPOINT(endpoint);
endpoint->ActiveTransfers[0] = NULL;
UHCD_CompleteIrp(DeviceObject,
Irp,
STATUS_CANCELLED,
0,
urb);
UHCD_KdPrint((2, "'exit UHCD_RootHub_InterruptTransferCancel\n"));
}
VOID
UHCD_RootHubPollDpc(
IN PKDPC Dpc,
IN PVOID DeferredContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This routine runs at DISPATCH_LEVEL IRQL.
Arguments:
Dpc - Pointer to the DPC object.
DeferredContext - supplies the device object.
SystemArgument1 - not used.
SystemArgument2 - not used.
Return Value:
None.
--*/
{
PDEVICE_OBJECT deviceObject = DeferredContext;
PUHCD_ENDPOINT endpoint;
PDEVICE_EXTENSION deviceExtension;
deviceExtension = deviceObject->DeviceExtension;
endpoint = deviceExtension->RootHubInterruptEndpoint;
// LOGENTRY(LOG_MISC, 'rhpX', deviceExtension, 0, 0);
if (deviceExtension->InterruptObject) {
UHCD_IsrDpc(
NULL,
DeferredContext,
NULL,
NULL);
}
UHCD_CheckIdle(deviceObject);
if (endpoint == NULL ||
(deviceExtension->HcFlags & HCFLAG_HCD_STOPPED)) {
// root hub has been closed, no more polling
// please.
LOGENTRY(LOG_MISC, 'rhpS', deviceExtension, 0,
deviceExtension->HcFlags);
return;
}
ASSERT_ENDPOINT(endpoint);
UHCD_RootHubPoll(deviceObject, endpoint);
}