Leaked source code of windows server 2003
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.
 
 
 
 
 
 

570 lines
14 KiB

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
dmtimer.c
Abstract:
This module implements our 'deadman' timer DPC.
This is our general purpose timer we use to deal with
situations where the controller is not giving us
interrupts.
examples:
root hub polling.
dead controller detection
Environment:
kernel mode only
Notes:
Revision History:
1-1-00 : created
--*/
#include "common.h"
// paged functions
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, USBPORT_StartDM_Timer)
#endif
// non paged functions
// USBPORT_DM_TimerDpc
// USBPORT_StopDM_Timer
VOID
USBPORT_DM_TimerDpc(
PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2
)
/*++
Routine Description:
This routine runs at DISPATCH_LEVEL IRQL.
Arguments:
Dpc - Pointer to the DPC object.
DeferredContext - supplies FdoDeviceObject.
SystemArgument1 - not used.
SystemArgument2 - not used.
Return Value:
None.
--*/
{
PDEVICE_OBJECT fdoDeviceObject = DeferredContext;
PDEVICE_EXTENSION devExt;
BOOLEAN setTimer;
KIRQL irql;
GET_DEVICE_EXT(devExt, fdoDeviceObject);
ASSERT_FDOEXT(devExt);
#if DBG
{
LARGE_INTEGER t;
KeQuerySystemTime(&t);
LOGENTRY(NULL, fdoDeviceObject, LOG_NOISY, 'dmTM', fdoDeviceObject,
t.LowPart, 0);
}
#endif
// if stop fires it will stall here
// if stop is running we stall here
USBPORT_ACQUIRE_DM_LOCK(devExt, irql);
#ifdef XPSE
// poll while suspended
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_SUSPENDED) &&
TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_POLL_IN_SUSPEND)) {
MP_CheckController(devExt);
if (!TEST_FDO_FLAG(devExt,USBPORT_FDOFLAG_CONTROLLER_GONE)) {
MP_PollController(devExt);
}
}
#endif
USBPORT_SynchronizeControllersStart(fdoDeviceObject);
// skip timer is set when we are in low power
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_SKIP_TIMER_WORK)) {
// some work we should always do
// for an upcomming bug fix
USBPORT_BadRequestFlush(fdoDeviceObject, FALSE);
} else {
MP_CheckController(devExt);
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_POLL_CONTROLLER) &&
!TEST_FDO_FLAG(devExt,USBPORT_FDOFLAG_CONTROLLER_GONE)) {
MP_PollController(devExt);
}
// call the ISR worker here in the event that the controller
// is unable to generate interrupts
USBPORT_IsrDpcWorker(fdoDeviceObject, FALSE);
USBPORT_TimeoutAllEndpoints(fdoDeviceObject);
// invalidate all isochronous endpoints
// flush async requests
USBPORT_BadRequestFlush(fdoDeviceObject, FALSE);
}
setTimer = TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_ENABLED);
USBPORT_RELEASE_DM_LOCK(devExt, irql);
if (setTimer) {
ULONG timerIncerent;
LONG dueTime;
timerIncerent = KeQueryTimeIncrement() - 1;
// round up to the next highest timer increment
dueTime= -1 *
(MILLISECONDS_TO_100_NS_UNITS(devExt->Fdo.DM_TimerInterval) + timerIncerent);
KeSetTimer(&devExt->Fdo.DM_Timer,
RtlConvertLongToLargeInteger(dueTime),
&devExt->Fdo.DM_TimerDpc);
INCREMENT_PENDING_REQUEST_COUNT(fdoDeviceObject, NULL);
}
// this timer is done
DECREMENT_PENDING_REQUEST_COUNT(fdoDeviceObject, NULL);
}
// BUGBUG HP ia64 fix
VOID
USBPORT_DoRootHubCallback(
PDEVICE_OBJECT FdoDeviceObject,
PDEVICE_OBJECT Usb2Fdo
)
/*++
Routine Description:
Execute the root hub notifaction callback -- called from the
worker thread
Arguments:
FdoDeviceObject - FDO device object for a USB 1.1 controller
that may be a CC
Return Value:
None.
--*/
{
PDEVICE_EXTENSION devExt, rhDevExt;
PRH_INIT_CALLBACK cb;
PVOID context;
KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
GET_DEVICE_EXT(rhDevExt, devExt->Fdo.RootHubPdo);
ASSERT_PDOEXT(rhDevExt);
KeAcquireSpinLock(&devExt->Fdo.HcSyncSpin.sl, &irql);
USBPORT_ASSERT(rhDevExt->Pdo.HubInitCallback != NULL);
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'syCB', rhDevExt, 0, 0);
context = rhDevExt->Pdo.HubInitContext;
cb = rhDevExt->Pdo.HubInitCallback;
rhDevExt->Pdo.HubInitCallback = NULL;
rhDevExt->Pdo.HubInitContext = NULL;
KeReleaseSpinLock(&devExt->Fdo.HcSyncSpin.sl, irql);
// root hub list should be empty
#if DBG
{
PHCD_ENDPOINT ep = rhDevExt->Pdo.RootHubInterruptEndpoint;
USBPORT_ASSERT(IsListEmpty(&ep->ActiveList));
USBPORT_ASSERT(IsListEmpty(&ep->PendingList));
}
#endif
// perform High speed chirp now. The EHCI driver is started and the CC
// must also be started since the root hub has registered a callback
// this will prevent the controller from going into suspend right away
// for lack of devices.
if (Usb2Fdo) {
PDEVICE_EXTENSION usb2DevExt;
GET_DEVICE_EXT(usb2DevExt, Usb2Fdo);
ASSERT_FDOEXT(usb2DevExt);
// note in .NET this function takes the CC FDO
// and aquires the CC lock
// the USB 2 controller may already be in suspend if the CC root hub is
// being added later, for this case we skip the chirp.
if (!TEST_FDO_FLAG(usb2DevExt, USBPORT_FDOFLAG_SUSPENDED)) {
USBPORT_RootHub_PowerAndChirpAllCcPorts(FdoDeviceObject);
}
}
cb(context);
}
VOID
USBPORT_SynchronizeControllersStart(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
see if it is OK to start a controllers root hub
Arguments:
FdoDeviceObject - FDO device object for a USB 1.1 controller
that may be a CC
Return Value:
None.
--*/
{
PDEVICE_EXTENSION devExt, rhDevExt;
KIRQL irql;
PRH_INIT_CALLBACK cb;
PVOID context;
BOOLEAN okToStart;
PDEVICE_OBJECT usb2Fdo = NULL;
PDEVICE_EXTENSION usb2DevExt;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
// skip if we don't have a root hub
if (devExt->Fdo.RootHubPdo == NULL) {
return;
}
GET_DEVICE_EXT(rhDevExt, devExt->Fdo.RootHubPdo);
ASSERT_PDOEXT(rhDevExt);
// if no callback registered skip the whole process
if (rhDevExt->Pdo.HubInitCallback == NULL) {
return;
}
// if callback pending on worker thread skip
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_SIGNAL_RH)) {
return;
}
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'syn1', FdoDeviceObject, 0, 0);
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC)) {
okToStart = FALSE;
//
// we need to find the 2.0 master controller,
// if the hub has started then it is OK to
// start.
usb2Fdo = USBPORT_FindUSB2Controller(FdoDeviceObject);
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'syn2', 0, 0, usb2Fdo);
if (usb2Fdo != NULL) {
GET_DEVICE_EXT(usb2DevExt, usb2Fdo);
ASSERT_FDOEXT(usb2DevExt);
if (TEST_FLAG(usb2DevExt->PnpStateFlags, USBPORT_PNP_STARTED)) {
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'syn3', 0, 0, usb2Fdo);
okToStart = TRUE;
}
}
// is companion but check if OK to bypass the wait
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_CC_ENUM_OK)) {
okToStart = TRUE;
}
} else {
// not a CC, it is OK to start immediatly
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'syn4', 0, 0, 0);
okToStart = TRUE;
}
// check for a start-hub callback notification. If we have
// one then notify the hub that it is OK
if (okToStart) {
if (usb2Fdo) {
GET_DEVICE_EXT(usb2DevExt, usb2Fdo);
// signal the worker to chirp and do the callback
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_SIGNAL_RH);
InterlockedIncrement(&usb2DevExt->Fdo.PendingRhCallback);
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'prh+', 0, 0,
usb2DevExt->Fdo.PendingRhCallback);
USBPORT_SignalWorker(FdoDeviceObject);
} else {
// no 2.0 controller just callback now
USBPORT_DoRootHubCallback(FdoDeviceObject, NULL);
}
}
}
VOID
USBPORT_BadRequestFlush(
PDEVICE_OBJECT FdoDeviceObject,
BOOLEAN ForceFlush
)
/*++
Routine Description:
Asynchronously flush bad requests from client drivers
Arguments:
Return Value:
None.
--*/
{
PDEVICE_EXTENSION devExt;
KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
// The purpose of this async flush is to emulate the Win2k
// behavior where we put tranfers on the HW to devices that
// had been removed and let them time out.
//
// origanlly I used 5 here to fix problems with hid drivers
// on win2k+usb20, we may need to change this depending on
// which OS we are running on, we want use a smaller value on
// XP since HID (the major offender) has been fixed and many
// of our other in house class drivers support hot remove
// better.
#define BAD_REQUEST_FLUSH 0
devExt->Fdo.BadRequestFlush++;
if (devExt->Fdo.BadRequestFlush > devExt->Fdo.BadReqFlushThrottle ||
ForceFlush) {
devExt->Fdo.BadRequestFlush = 0;
// flush and complete any 'bad parameter' requests
ACQUIRE_BADREQUEST_LOCK(FdoDeviceObject, irql);
while (1) {
PLIST_ENTRY listEntry;
PIRP irp;
PUSB_IRP_CONTEXT irpContext;
NTSTATUS ntStatus;
if (IsListEmpty(&devExt->Fdo.BadRequestList)) {
break;
}
listEntry = RemoveHeadList(&devExt->Fdo.BadRequestList);
//irp = (PIRP) CONTAINING_RECORD(
// listEntry,
// struct _IRP,
// Tail.Overlay.ListEntry);
irpContext = (PUSB_IRP_CONTEXT) CONTAINING_RECORD(
listEntry,
struct _USB_IRP_CONTEXT,
ListEntry);
ASSERT_IRP_CONTEXT(irpContext);
irp = irpContext->Irp;
if (irp->Cancel) {
ntStatus = STATUS_CANCELLED;
} else {
ntStatus = STATUS_DEVICE_NOT_CONNECTED;
}
RELEASE_BADREQUEST_LOCK(FdoDeviceObject, irql);
// cancel routine did not run
LOGENTRY(NULL, FdoDeviceObject, LOG_IRPS, 'cpBA', irp, irpContext, 0);
USBPORT_CompleteIrp(devExt->Fdo.RootHubPdo, irp,
ntStatus, 0);
FREE_POOL(FdoDeviceObject, irpContext);
ACQUIRE_BADREQUEST_LOCK(FdoDeviceObject, irql);
}
RELEASE_BADREQUEST_LOCK(FdoDeviceObject, irql);
}
}
VOID
USBPORT_StartDM_Timer(
PDEVICE_OBJECT FdoDeviceObject,
LONG MillisecondInterval
)
/*++
Routine Description:
Inialize and start the timer
Arguments:
FdoDeviceObject - DeviceObject of the controller to stop
MillisecondInterval -
Return Value:
None.
--*/
{
PDEVICE_EXTENSION devExt;
LONG dueTime;
ULONG timerIncerent;
PAGED_CODE();
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
timerIncerent = KeQueryTimeIncrement() - 1;
// remember interval for repeated use
devExt->Fdo.DM_TimerInterval = MillisecondInterval;
// round up to the next highest timer increment
dueTime= -1 * (MILLISECONDS_TO_100_NS_UNITS(MillisecondInterval) +
timerIncerent);
USBPORT_KdPrint((1, " DM timer (100ns) = %d\n", dueTime));
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_ENABLED);
// we consider the timer a pending request while it is
// scheduled
INCREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
KeInitializeTimer(&devExt->Fdo.DM_Timer);
KeInitializeDpc(&devExt->Fdo.DM_TimerDpc,
USBPORT_DM_TimerDpc,
FdoDeviceObject);
KeSetTimer(&devExt->Fdo.DM_Timer,
RtlConvertLongToLargeInteger(dueTime),
&devExt->Fdo.DM_TimerDpc);
SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_INIT);
}
VOID
USBPORT_StopDM_Timer(
PDEVICE_OBJECT FdoDeviceObject
)
/*++
Routine Description:
stop the timer
Arguments:
FdoDeviceObject - DeviceObject of the controller to stop
Return Value:
None.
--*/
{
PDEVICE_EXTENSION devExt;
KIRQL irql;
BOOLEAN inQueue;
GET_DEVICE_EXT(devExt, FdoDeviceObject);
ASSERT_FDOEXT(devExt);
if (!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_INIT)) {
// timer never started so bypass the stop
return;
}
// if timer fires it will stall here
// if timer is running we will stall here
USBPORT_ACQUIRE_DM_LOCK(devExt, irql);
USBPORT_ASSERT(TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_ENABLED));
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'kilT', FdoDeviceObject, 0, 0);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_ENABLED);
// timer will no longer re-schedule
USBPORT_RELEASE_DM_LOCK(devExt, irql);
// if there is a timer in the queue, remove it
inQueue = KeCancelTimer(&devExt->Fdo.DM_Timer);
if (inQueue) {
// it was queue, so dereference now
LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'klIQ', FdoDeviceObject, 0, 0);
DECREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
}
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_INIT);
}