|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
thread.c
Abstract:
Environment:
kernel mode only
Notes:
Revision History:
6-20-99 : created
--*/
#include "common.h"
#ifdef ALLOC_PRAGMA
#endif
// non paged functions
// USBPORT_CreateWorkerThread
// USBPORT_WorkerThreadStart
// USBPORT_SignalWorker
//NOTE perhaps one thread for all drivers will be enough
// we need to research this
// BUGBUG
// not a WDM function, see if we can do a runtime detect
/*
NTKERNELAPI LONG KeSetBasePriorityThread ( IN PKTHREAD Thread, IN LONG Increment );
VOID USBPORT_SetBasePriorityThread( PKTHREAD Thread, LONG Increment ) { //KeSetBasePriorityThread(Thread, Increment);
} */
VOID USBPORT_WorkerThread( PVOID StartContext ) /*++
Routine Description:
start the worker thread
Arguments:
Return Value:
none
--*/ { PDEVICE_EXTENSION devExt; PDEVICE_OBJECT fdoDeviceObject; KIRQL irql;
fdoDeviceObject = StartContext; GET_DEVICE_EXT(devExt, fdoDeviceObject); ASSERT_FDOEXT(devExt);
devExt->Fdo.WorkerPkThread = KeGetCurrentThread(); // priority setting optimal for suspend/resume
// increment by 7, value suggested by perf team
//USBPORT_SetBasePriorityThread(devExt->Fdo.WorkerPkThread, 7);
// hurry up and wait
do {
LARGE_INTEGER t1, t2;
KeQuerySystemTime(&t1);
KeWaitForSingleObject( &devExt->Fdo.WorkerThreadEvent, Suspended, KernelMode, FALSE, NULL);
KeQuerySystemTime(&t2); // deltaT in 100ns units 10 of these per ms
// div by 10000 to get ms
// compute how long we were idle
devExt->Fdo.StatWorkIdleTime = (ULONG) ((t2.QuadPart - t1.QuadPart) / 10000);
// see if we have work to do
LOGENTRY(NULL, fdoDeviceObject, LOG_NOISY, 'wakW', 0, 0, devExt->Fdo.StatWorkIdleTime);
// if someone is setting the event we stall here, the event will
// be signalled and we will reset it. This is OK because we
// have not done any work yet
KeAcquireSpinLock(&devExt->Fdo.WorkerThreadSpin.sl, &irql); // if someone sets the event they will stall here, until we reset
// the event -- it will cause us to loop around again but that is
// no big deal.
KeResetEvent(&devExt->Fdo.WorkerThreadEvent); KeReleaseSpinLock(&devExt->Fdo.WorkerThreadSpin.sl, irql); // now doing work
// at this point once work is complete we will wait until someone
// else signals
// don't do work unless we are started
if (TEST_FLAG(devExt->Fdo.MpStateFlags, MP_STATE_STARTED)) { USBPORT_DoSetPowerD0(fdoDeviceObject);
// BUGBUG HP ia64 fix
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_SIGNAL_RH)) { PDEVICE_OBJECT usb2Fdo; PDEVICE_EXTENSION usb2DevExt;
usb2Fdo = USBPORT_FindUSB2Controller(fdoDeviceObject);
GET_DEVICE_EXT(usb2DevExt, usb2Fdo); ASSERT_FDOEXT(usb2DevExt);
USBPORT_DoRootHubCallback(fdoDeviceObject, usb2Fdo); CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_SIGNAL_RH);
// allow 2.0 controller to suspend
InterlockedDecrement(&usb2DevExt->Fdo.PendingRhCallback); LOGENTRY(NULL, fdoDeviceObject, LOG_PNP, 'prh-', 0, 0, usb2DevExt->Fdo.PendingRhCallback); }
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_CATC_TRAP)) { USBPORT_EndTransmitTriggerPacket(fdoDeviceObject); }
USBPORT_Worker(fdoDeviceObject); }
} while (!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_KILL_THREAD));
// cancel any wake irp we may have pending
USBPORT_DisarmHcForWake(fdoDeviceObject);
LOGENTRY(NULL, fdoDeviceObject, LOG_MISC, 'Ttrm', 0, 0, 0);
// kill ourselves
PsTerminateSystemThread(STATUS_SUCCESS);
}
VOID USBPORT_TerminateWorkerThread( PDEVICE_OBJECT FdoDeviceObject ) /*++
Routine Description:
Terminate the USBPORT Worker thread synchronously
Arguments:
Return Value:
none
--*/ { PDEVICE_EXTENSION devExt; NTSTATUS status; PVOID threadObject; KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt);
if (!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_THREAD_INIT)) { return; }
// signal our thread to terminate
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Tthr', 0, 0, 0); SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_KILL_THREAD);
// reference it so it won't go away before
// we wait for it to finish
status = ObReferenceObjectByHandle(devExt->Fdo.WorkerThreadHandle, SYNCHRONIZE, NULL, KernelMode, &threadObject, NULL);
USBPORT_ASSERT(NT_SUCCESS(status))
// signal worker takes the spinlock so on the off chance that
// there is work being done this will stall
USBPORT_SignalWorker(FdoDeviceObject);
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'ThWt', 0, 0, status); // wait for thread to finish
KeWaitForSingleObject( threadObject, Executive, KernelMode, FALSE, NULL);
ObDereferenceObject(threadObject); ZwClose(devExt->Fdo.WorkerThreadHandle); devExt->Fdo.WorkerThreadHandle = NULL;
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'TthD', 0, 0, 0);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_THREAD_INIT);
}
NTSTATUS USBPORT_CreateWorkerThread( PDEVICE_OBJECT FdoDeviceObject ) /*++
Routine Description:
Create the USBPORT Worker thread
Arguments:
Return Value:
NTSTATUS
--*/ { NTSTATUS ntStatus; PDEVICE_EXTENSION devExt;
GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt);
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_KILL_THREAD);
// initialize to NOT signaled
// we initialize here because the we may signal
// the event before the thread starts if we get
// an interrupt.
KeInitializeEvent(&devExt->Fdo.WorkerThreadEvent, NotificationEvent, FALSE);
ntStatus = PsCreateSystemThread(&devExt->Fdo.WorkerThreadHandle, THREAD_ALL_ACCESS, NULL, (HANDLE)0L, NULL, USBPORT_WorkerThread, FdoDeviceObject);
if (NT_SUCCESS(ntStatus)) { SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_THREAD_INIT); }
LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'crTH', 0, 0, ntStatus);
return ntStatus; }
VOID USBPORT_SignalWorker( PDEVICE_OBJECT FdoDeviceObject ) /*++
Routine Description:
Signal that there is work to do.
Arguments:
Return Value:
None.
--*/ { PDEVICE_EXTENSION devExt; KIRQL irql;
GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt);
devExt->Fdo.StatWorkSignalCount++;
KeAcquireSpinLock(&devExt->Fdo.WorkerThreadSpin.sl, &irql); LOGENTRY(NULL, FdoDeviceObject, LOG_NOISY, 'sigW', FdoDeviceObject, 0, 0); KeSetEvent(&devExt->Fdo.WorkerThreadEvent, 1, FALSE); KeReleaseSpinLock(&devExt->Fdo.WorkerThreadSpin.sl, irql); }
VOID USBPORT_PowerWork( PVOID Context ) /*++
Routine Description:
Arguments:
Return Value:
None.
--*/ { PUSB_POWER_WORK powerWork = Context;
USBPORT_DoSetPowerD0(powerWork->FdoDeviceObject);
DECREMENT_PENDING_REQUEST_COUNT(powerWork->FdoDeviceObject, NULL);
FREE_POOL(powerWork->FdoDeviceObject, powerWork); }
VOID USBPORT_QueuePowerWorkItem( PDEVICE_OBJECT FdoDeviceObject ) /*++
Routine Description:
Arguments:
Return Value:
None.
--*/ { PUSB_POWER_WORK powerWork;
ALLOC_POOL_Z(powerWork, NonPagedPool, sizeof(*powerWork));
// if the allocation fails the power work will be
// deferred to our worker thread, this workitem is
// just an optimization
if (powerWork != NULL) { ExInitializeWorkItem(&powerWork->QueueItem, USBPORT_PowerWork, powerWork); powerWork->FdoDeviceObject = FdoDeviceObject;
INCREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL); ExQueueWorkItem(&powerWork->QueueItem, CriticalWorkQueue);
} }
VOID USBPORT_DoSetPowerD0( PDEVICE_OBJECT FdoDeviceObject ) /*++
Routine Description:
Arguments:
Return Value:
None.
--*/ { KIRQL irql; PDEVICE_EXTENSION devExt; ULONG controllerDisarmTime;
GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt);
KeAcquireSpinLock(&devExt->Fdo.PowerSpin.sl, &irql); // see if we need to power on
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_NEED_SET_POWER_D0)) {
#ifdef XPSE
LARGE_INTEGER dt, t1, t2; #endif
CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_NEED_SET_POWER_D0); KeReleaseSpinLock(&devExt->Fdo.PowerSpin.sl, irql);
#ifdef XPSE
// compute time to thread signal and wake
KeQuerySystemTime(&t1); dt.QuadPart = t1.QuadPart - devExt->Fdo.ThreadResumeTimeStart.QuadPart;
devExt->Fdo.ThreadResumeTime = (ULONG) (dt.QuadPart/10000);
USBPORT_KdPrint((1, "(%x) ThreadResumeTime %d ms \n", devExt, devExt->Fdo.ThreadResumeTime)); #endif
// synchronously cancel the wake irp we have
// in PCI so we don't get a completeion while
// we power up.
KeQuerySystemTime(&t1); USBPORT_DisarmHcForWake(FdoDeviceObject); KeQuerySystemTime(&t2); dt.QuadPart = t2.QuadPart - t1.QuadPart;
controllerDisarmTime = (ULONG) (dt.QuadPart/10000); USBPORT_KdPrint((1, "(%x) ControllerDisarmTime %d ms \n", devExt, controllerDisarmTime));
#ifdef XPSE
// time the hw resume/start
KeQuerySystemTime(&t1); #endif
// The goal here is to wait for the USB2 and its CCs to start
// then make sure that the 20 controller holds the shared port
// semaphore
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_OFF)) { USBPORT_TurnControllerOn(FdoDeviceObject); USBPORT_SynchronizeControllersResume(FdoDeviceObject);
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC)) { // if this is a CC then power the ports here
// the USB 2 controller holds the semaphore on
// return from USBPORT_SynchronizeControllersResume
USBPORT_KdPrint((1, " >power-chirp CC ports (on)\n")); USBPORT_RootHub_PowerAndChirpAllCcPorts(FdoDeviceObject); } } else { // complete the power irp, the controller is on
// but is still 'suspended'
USBPORT_RestoreController(FdoDeviceObject); USBPORT_SynchronizeControllersResume(FdoDeviceObject); }
#ifdef XPSE
// compute time to start controller
KeQuerySystemTime(&t2); dt.QuadPart = t2.QuadPart - t1.QuadPart;
devExt->Fdo.ControllerResumeTime = (ULONG) (dt.QuadPart/10000);
USBPORT_KdPrint((1, "(%x) ControllerResumeTime %d ms \n", devExt, devExt->Fdo.ControllerResumeTime));
// compute time to S0;
KeQuerySystemTime(&t2); dt.QuadPart = t2.QuadPart - devExt->Fdo.S0ResumeTimeStart.QuadPart;
devExt->Fdo.S0ResumeTime = (ULONG) (dt.QuadPart/10000);
USBPORT_KdPrint((1, "(%x) D0ResumeTime %d ms \n", devExt, devExt->Fdo.D0ResumeTime)); USBPORT_KdPrint((1, "(%x) S0ResumeTime %d ms \n", devExt, devExt->Fdo.S0ResumeTime)); #endif
if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_RESUME_SIGNALLING)) { CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_RESUME_SIGNALLING); USBPORT_HcQueueWakeDpc(FdoDeviceObject); } } else { KeReleaseSpinLock(&devExt->Fdo.PowerSpin.sl, irql); }
}
VOID USBPORT_SynchronizeControllersResume( PDEVICE_OBJECT FdoDeviceObject ) /*++
Routine Description:
Synchronize the USB 2 controllers with companions.
This routines blocks all dependent controllers unt their hardware is restored. At that point it takes the CC lock for the USB 2 controller and allows all the controllers to resume.
The CC lock protects the shared port registers from simultaneous access.
Arguments:
Return Value:
None.
The USB 2 controller holds the CC lock on return from this function
--*/ { PDEVICE_EXTENSION devExt; PDEVICE_OBJECT usb2Fdo;
ASSERT_PASSIVE();
GET_DEVICE_EXT(devExt, FdoDeviceObject); ASSERT_FDOEXT(devExt);
LOGENTRY(NULL, FdoDeviceObject, LOG_RH, 'SYN2', FdoDeviceObject, 0, 0);
if (USBPORT_IS_USB20(devExt)) { usb2Fdo = FdoDeviceObject; } else { usb2Fdo = USBPORT_FindUSB2Controller(FdoDeviceObject); }
// may get NULL if no 2.0 controller registered
// don't wait if not CCs or other controllers
if (usb2Fdo) { PDEVICE_EXTENSION usb2DevExt, rhDevExt;
LOGENTRY(NULL, FdoDeviceObject, LOG_RH, 'u2cc', FdoDeviceObject, usb2Fdo, 0);
GET_DEVICE_EXT(usb2DevExt, usb2Fdo); ASSERT_FDOEXT(usb2DevExt);
GET_DEVICE_EXT(rhDevExt, usb2DevExt->Fdo.RootHubPdo); ASSERT_PDOEXT(rhDevExt);
// sync with the CC if this is a USB 2 controller
// note that we only grab the CC lock if the root
// hub PDO is enabled since it is released only
// when the root hub is set to D0 -- this will never
// happen if the rh is disabled
if (USBPORT_IS_USB20(devExt) && !TEST_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_REMOVED)) {
KeWaitForSingleObject(&usb2DevExt->Fdo.CcLock, Executive, KernelMode, FALSE, NULL); USBPORT_ASSERT(!TEST_FDO_FLAG(usb2DevExt, USBPORT_FDOFLAG_CC_LOCK)); SET_FDO_FLAG(usb2DevExt, USBPORT_FDOFLAG_CC_LOCK); LOGENTRY(NULL, FdoDeviceObject, LOG_RH, 'grcc', FdoDeviceObject, usb2Fdo, 0);
USBPORT_KdPrint((1, " >power 20 (on) %x\n", FdoDeviceObject)); }
InterlockedDecrement(&usb2DevExt->Fdo.DependentControllers);
// at this point any of the dependent controllers can continue
do { USBPORT_Wait(FdoDeviceObject, 10);
// sync with the CC if this is a USB 2 controller
// note that we only grab the CC lock if the root
// hub PDO is enabled since it is released only
// when the root hub is set to D0 -- this will never
// happen if the rh is disabled
} while (usb2DevExt->Fdo.DependentControllers);
LOGENTRY(NULL, FdoDeviceObject, LOG_RH, 'u2GO', FdoDeviceObject, usb2Fdo, 0);
}
}
|