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
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);
|
|
|
|
}
|