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.

622 lines
16 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. thread.c
  5. Abstract:
  6. Environment:
  7. kernel mode only
  8. Notes:
  9. Revision History:
  10. 6-20-99 : created
  11. --*/
  12. #include "common.h"
  13. #ifdef ALLOC_PRAGMA
  14. #endif
  15. // non paged functions
  16. // USBPORT_CreateWorkerThread
  17. // USBPORT_WorkerThreadStart
  18. // USBPORT_SignalWorker
  19. //NOTE perhaps one thread for all drivers will be enough
  20. // we need to research this
  21. // BUGBUG
  22. // not a WDM function, see if we can do a runtime detect
  23. /*
  24. NTKERNELAPI
  25. LONG
  26. KeSetBasePriorityThread (
  27. IN PKTHREAD Thread,
  28. IN LONG Increment
  29. );
  30. VOID
  31. USBPORT_SetBasePriorityThread(
  32. PKTHREAD Thread,
  33. LONG Increment
  34. )
  35. {
  36. //KeSetBasePriorityThread(Thread, Increment);
  37. }
  38. */
  39. VOID
  40. USBPORT_WorkerThread(
  41. PVOID StartContext
  42. )
  43. /*++
  44. Routine Description:
  45. start the worker thread
  46. Arguments:
  47. Return Value:
  48. none
  49. --*/
  50. {
  51. PDEVICE_EXTENSION devExt;
  52. PDEVICE_OBJECT fdoDeviceObject;
  53. KIRQL irql;
  54. fdoDeviceObject = StartContext;
  55. GET_DEVICE_EXT(devExt, fdoDeviceObject);
  56. ASSERT_FDOEXT(devExt);
  57. devExt->Fdo.WorkerPkThread = KeGetCurrentThread();
  58. // priority setting optimal for suspend/resume
  59. // increment by 7, value suggested by perf team
  60. //USBPORT_SetBasePriorityThread(devExt->Fdo.WorkerPkThread, 7);
  61. // hurry up and wait
  62. do {
  63. LARGE_INTEGER t1, t2;
  64. KeQuerySystemTime(&t1);
  65. KeWaitForSingleObject(
  66. &devExt->Fdo.WorkerThreadEvent,
  67. Suspended,
  68. KernelMode,
  69. FALSE,
  70. NULL);
  71. KeQuerySystemTime(&t2);
  72. // deltaT in 100ns units 10 of these per ms
  73. // div by 10000 to get ms
  74. // compute how long we were idle
  75. devExt->Fdo.StatWorkIdleTime =
  76. (ULONG) ((t2.QuadPart - t1.QuadPart) / 10000);
  77. // see if we have work to do
  78. LOGENTRY(NULL, fdoDeviceObject, LOG_NOISY, 'wakW', 0, 0,
  79. devExt->Fdo.StatWorkIdleTime);
  80. // if someone is setting the event we stall here, the event will
  81. // be signalled and we will reset it. This is OK because we
  82. // have not done any work yet
  83. KeAcquireSpinLock(&devExt->Fdo.WorkerThreadSpin.sl, &irql);
  84. // if someone sets the event they will stall here, until we reset
  85. // the event -- it will cause us to loop around again but that is
  86. // no big deal.
  87. KeResetEvent(&devExt->Fdo.WorkerThreadEvent);
  88. KeReleaseSpinLock(&devExt->Fdo.WorkerThreadSpin.sl, irql);
  89. // now doing work
  90. // at this point once work is complete we will wait until someone
  91. // else signals
  92. // don't do work unless we are started
  93. if (TEST_FLAG(devExt->Fdo.MpStateFlags, MP_STATE_STARTED)) {
  94. USBPORT_DoSetPowerD0(fdoDeviceObject);
  95. // BUGBUG HP ia64 fix
  96. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_SIGNAL_RH)) {
  97. PDEVICE_OBJECT usb2Fdo;
  98. PDEVICE_EXTENSION usb2DevExt;
  99. usb2Fdo = USBPORT_FindUSB2Controller(fdoDeviceObject);
  100. GET_DEVICE_EXT(usb2DevExt, usb2Fdo);
  101. ASSERT_FDOEXT(usb2DevExt);
  102. USBPORT_DoRootHubCallback(fdoDeviceObject, usb2Fdo);
  103. CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_SIGNAL_RH);
  104. // allow 2.0 controller to suspend
  105. InterlockedDecrement(&usb2DevExt->Fdo.PendingRhCallback);
  106. LOGENTRY(NULL, fdoDeviceObject, LOG_PNP, 'prh-', 0, 0,
  107. usb2DevExt->Fdo.PendingRhCallback);
  108. }
  109. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_CATC_TRAP)) {
  110. USBPORT_EndTransmitTriggerPacket(fdoDeviceObject);
  111. }
  112. USBPORT_Worker(fdoDeviceObject);
  113. }
  114. } while (!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_KILL_THREAD));
  115. // cancel any wake irp we may have pending
  116. USBPORT_DisarmHcForWake(fdoDeviceObject);
  117. LOGENTRY(NULL, fdoDeviceObject, LOG_MISC, 'Ttrm', 0, 0, 0);
  118. // kill ourselves
  119. PsTerminateSystemThread(STATUS_SUCCESS);
  120. }
  121. VOID
  122. USBPORT_TerminateWorkerThread(
  123. PDEVICE_OBJECT FdoDeviceObject
  124. )
  125. /*++
  126. Routine Description:
  127. Terminate the USBPORT Worker thread synchronously
  128. Arguments:
  129. Return Value:
  130. none
  131. --*/
  132. {
  133. PDEVICE_EXTENSION devExt;
  134. NTSTATUS status;
  135. PVOID threadObject;
  136. KIRQL irql;
  137. GET_DEVICE_EXT(devExt, FdoDeviceObject);
  138. ASSERT_FDOEXT(devExt);
  139. if (!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_THREAD_INIT)) {
  140. return;
  141. }
  142. // signal our thread to terminate
  143. LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'Tthr', 0, 0, 0);
  144. SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_KILL_THREAD);
  145. // reference it so it won't go away before
  146. // we wait for it to finish
  147. status = ObReferenceObjectByHandle(devExt->Fdo.WorkerThreadHandle,
  148. SYNCHRONIZE,
  149. NULL,
  150. KernelMode,
  151. &threadObject,
  152. NULL);
  153. USBPORT_ASSERT(NT_SUCCESS(status))
  154. // signal worker takes the spinlock so on the off chance that
  155. // there is work being done this will stall
  156. USBPORT_SignalWorker(FdoDeviceObject);
  157. LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'ThWt', 0, 0, status);
  158. // wait for thread to finish
  159. KeWaitForSingleObject(
  160. threadObject,
  161. Executive,
  162. KernelMode,
  163. FALSE,
  164. NULL);
  165. ObDereferenceObject(threadObject);
  166. ZwClose(devExt->Fdo.WorkerThreadHandle);
  167. devExt->Fdo.WorkerThreadHandle = NULL;
  168. LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'TthD', 0, 0, 0);
  169. CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_THREAD_INIT);
  170. }
  171. NTSTATUS
  172. USBPORT_CreateWorkerThread(
  173. PDEVICE_OBJECT FdoDeviceObject
  174. )
  175. /*++
  176. Routine Description:
  177. Create the USBPORT Worker thread
  178. Arguments:
  179. Return Value:
  180. NTSTATUS
  181. --*/
  182. {
  183. NTSTATUS ntStatus;
  184. PDEVICE_EXTENSION devExt;
  185. GET_DEVICE_EXT(devExt, FdoDeviceObject);
  186. ASSERT_FDOEXT(devExt);
  187. CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_KILL_THREAD);
  188. // initialize to NOT signaled
  189. // we initialize here because the we may signal
  190. // the event before the thread starts if we get
  191. // an interrupt.
  192. KeInitializeEvent(&devExt->Fdo.WorkerThreadEvent,
  193. NotificationEvent,
  194. FALSE);
  195. ntStatus =
  196. PsCreateSystemThread(&devExt->Fdo.WorkerThreadHandle,
  197. THREAD_ALL_ACCESS,
  198. NULL,
  199. (HANDLE)0L,
  200. NULL,
  201. USBPORT_WorkerThread,
  202. FdoDeviceObject);
  203. if (NT_SUCCESS(ntStatus)) {
  204. SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_THREAD_INIT);
  205. }
  206. LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'crTH', 0, 0, ntStatus);
  207. return ntStatus;
  208. }
  209. VOID
  210. USBPORT_SignalWorker(
  211. PDEVICE_OBJECT FdoDeviceObject
  212. )
  213. /*++
  214. Routine Description:
  215. Signal that there is work to do.
  216. Arguments:
  217. Return Value:
  218. None.
  219. --*/
  220. {
  221. PDEVICE_EXTENSION devExt;
  222. KIRQL irql;
  223. GET_DEVICE_EXT(devExt, FdoDeviceObject);
  224. ASSERT_FDOEXT(devExt);
  225. devExt->Fdo.StatWorkSignalCount++;
  226. KeAcquireSpinLock(&devExt->Fdo.WorkerThreadSpin.sl, &irql);
  227. LOGENTRY(NULL, FdoDeviceObject, LOG_NOISY, 'sigW', FdoDeviceObject, 0, 0);
  228. KeSetEvent(&devExt->Fdo.WorkerThreadEvent,
  229. 1,
  230. FALSE);
  231. KeReleaseSpinLock(&devExt->Fdo.WorkerThreadSpin.sl, irql);
  232. }
  233. VOID
  234. USBPORT_PowerWork(
  235. PVOID Context
  236. )
  237. /*++
  238. Routine Description:
  239. Arguments:
  240. Return Value:
  241. None.
  242. --*/
  243. {
  244. PUSB_POWER_WORK powerWork = Context;
  245. USBPORT_DoSetPowerD0(powerWork->FdoDeviceObject);
  246. DECREMENT_PENDING_REQUEST_COUNT(powerWork->FdoDeviceObject, NULL);
  247. FREE_POOL(powerWork->FdoDeviceObject, powerWork);
  248. }
  249. VOID
  250. USBPORT_QueuePowerWorkItem(
  251. PDEVICE_OBJECT FdoDeviceObject
  252. )
  253. /*++
  254. Routine Description:
  255. Arguments:
  256. Return Value:
  257. None.
  258. --*/
  259. {
  260. PUSB_POWER_WORK powerWork;
  261. ALLOC_POOL_Z(powerWork, NonPagedPool, sizeof(*powerWork));
  262. // if the allocation fails the power work will be
  263. // deferred to our worker thread, this workitem is
  264. // just an optimization
  265. if (powerWork != NULL) {
  266. ExInitializeWorkItem(&powerWork->QueueItem,
  267. USBPORT_PowerWork,
  268. powerWork);
  269. powerWork->FdoDeviceObject = FdoDeviceObject;
  270. INCREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
  271. ExQueueWorkItem(&powerWork->QueueItem,
  272. CriticalWorkQueue);
  273. }
  274. }
  275. VOID
  276. USBPORT_DoSetPowerD0(
  277. PDEVICE_OBJECT FdoDeviceObject
  278. )
  279. /*++
  280. Routine Description:
  281. Arguments:
  282. Return Value:
  283. None.
  284. --*/
  285. {
  286. KIRQL irql;
  287. PDEVICE_EXTENSION devExt;
  288. ULONG controllerDisarmTime;
  289. GET_DEVICE_EXT(devExt, FdoDeviceObject);
  290. ASSERT_FDOEXT(devExt);
  291. KeAcquireSpinLock(&devExt->Fdo.PowerSpin.sl, &irql);
  292. // see if we need to power on
  293. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_NEED_SET_POWER_D0)) {
  294. #ifdef XPSE
  295. LARGE_INTEGER dt, t1, t2;
  296. #endif
  297. CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_NEED_SET_POWER_D0);
  298. KeReleaseSpinLock(&devExt->Fdo.PowerSpin.sl, irql);
  299. #ifdef XPSE
  300. // compute time to thread signal and wake
  301. KeQuerySystemTime(&t1);
  302. dt.QuadPart = t1.QuadPart - devExt->Fdo.ThreadResumeTimeStart.QuadPart;
  303. devExt->Fdo.ThreadResumeTime = (ULONG) (dt.QuadPart/10000);
  304. USBPORT_KdPrint((1, "(%x) ThreadResumeTime %d ms \n",
  305. devExt, devExt->Fdo.ThreadResumeTime));
  306. #endif
  307. // synchronously cancel the wake irp we have
  308. // in PCI so we don't get a completeion while
  309. // we power up.
  310. KeQuerySystemTime(&t1);
  311. USBPORT_DisarmHcForWake(FdoDeviceObject);
  312. KeQuerySystemTime(&t2);
  313. dt.QuadPart = t2.QuadPart - t1.QuadPart;
  314. controllerDisarmTime = (ULONG) (dt.QuadPart/10000);
  315. USBPORT_KdPrint((1, "(%x) ControllerDisarmTime %d ms \n",
  316. devExt, controllerDisarmTime));
  317. #ifdef XPSE
  318. // time the hw resume/start
  319. KeQuerySystemTime(&t1);
  320. #endif
  321. // The goal here is to wait for the USB2 and its CCs to start
  322. // then make sure that the 20 controller holds the shared port
  323. // semaphore
  324. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_OFF)) {
  325. USBPORT_TurnControllerOn(FdoDeviceObject);
  326. USBPORT_SynchronizeControllersResume(FdoDeviceObject);
  327. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC)) {
  328. // if this is a CC then power the ports here
  329. // the USB 2 controller holds the semaphore on
  330. // return from USBPORT_SynchronizeControllersResume
  331. USBPORT_KdPrint((1, " >power-chirp CC ports (on)\n"));
  332. USBPORT_RootHub_PowerAndChirpAllCcPorts(FdoDeviceObject);
  333. }
  334. } else {
  335. // complete the power irp, the controller is on
  336. // but is still 'suspended'
  337. USBPORT_RestoreController(FdoDeviceObject);
  338. USBPORT_SynchronizeControllersResume(FdoDeviceObject);
  339. }
  340. #ifdef XPSE
  341. // compute time to start controller
  342. KeQuerySystemTime(&t2);
  343. dt.QuadPart = t2.QuadPart - t1.QuadPart;
  344. devExt->Fdo.ControllerResumeTime = (ULONG) (dt.QuadPart/10000);
  345. USBPORT_KdPrint((1, "(%x) ControllerResumeTime %d ms \n",
  346. devExt, devExt->Fdo.ControllerResumeTime));
  347. // compute time to S0;
  348. KeQuerySystemTime(&t2);
  349. dt.QuadPart = t2.QuadPart - devExt->Fdo.S0ResumeTimeStart.QuadPart;
  350. devExt->Fdo.S0ResumeTime = (ULONG) (dt.QuadPart/10000);
  351. USBPORT_KdPrint((1, "(%x) D0ResumeTime %d ms \n", devExt,
  352. devExt->Fdo.D0ResumeTime));
  353. USBPORT_KdPrint((1, "(%x) S0ResumeTime %d ms \n", devExt,
  354. devExt->Fdo.S0ResumeTime));
  355. #endif
  356. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_RESUME_SIGNALLING)) {
  357. CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_RESUME_SIGNALLING);
  358. USBPORT_HcQueueWakeDpc(FdoDeviceObject);
  359. }
  360. } else {
  361. KeReleaseSpinLock(&devExt->Fdo.PowerSpin.sl, irql);
  362. }
  363. }
  364. VOID
  365. USBPORT_SynchronizeControllersResume(
  366. PDEVICE_OBJECT FdoDeviceObject
  367. )
  368. /*++
  369. Routine Description:
  370. Synchronize the USB 2 controllers with companions.
  371. This routines blocks all dependent controllers unt their
  372. hardware is restored. At that point it takes the CC lock
  373. for the USB 2 controller and allows all the controllers to
  374. resume.
  375. The CC lock protects the shared port registers from simultaneous
  376. access.
  377. Arguments:
  378. Return Value:
  379. None.
  380. The USB 2 controller holds the CC lock on return from this function
  381. --*/
  382. {
  383. PDEVICE_EXTENSION devExt;
  384. PDEVICE_OBJECT usb2Fdo;
  385. ASSERT_PASSIVE();
  386. GET_DEVICE_EXT(devExt, FdoDeviceObject);
  387. ASSERT_FDOEXT(devExt);
  388. LOGENTRY(NULL, FdoDeviceObject, LOG_RH, 'SYN2', FdoDeviceObject, 0, 0);
  389. if (USBPORT_IS_USB20(devExt)) {
  390. usb2Fdo = FdoDeviceObject;
  391. } else {
  392. usb2Fdo = USBPORT_FindUSB2Controller(FdoDeviceObject);
  393. }
  394. // may get NULL if no 2.0 controller registered
  395. // don't wait if not CCs or other controllers
  396. if (usb2Fdo) {
  397. PDEVICE_EXTENSION usb2DevExt, rhDevExt;
  398. LOGENTRY(NULL, FdoDeviceObject, LOG_RH, 'u2cc', FdoDeviceObject,
  399. usb2Fdo, 0);
  400. GET_DEVICE_EXT(usb2DevExt, usb2Fdo);
  401. ASSERT_FDOEXT(usb2DevExt);
  402. GET_DEVICE_EXT(rhDevExt, usb2DevExt->Fdo.RootHubPdo);
  403. ASSERT_PDOEXT(rhDevExt);
  404. // sync with the CC if this is a USB 2 controller
  405. // note that we only grab the CC lock if the root
  406. // hub PDO is enabled since it is released only
  407. // when the root hub is set to D0 -- this will never
  408. // happen if the rh is disabled
  409. if (USBPORT_IS_USB20(devExt) &&
  410. !TEST_FLAG(rhDevExt->PnpStateFlags, USBPORT_PNP_REMOVED)) {
  411. KeWaitForSingleObject(&usb2DevExt->Fdo.CcLock,
  412. Executive,
  413. KernelMode,
  414. FALSE,
  415. NULL);
  416. USBPORT_ASSERT(!TEST_FDO_FLAG(usb2DevExt,
  417. USBPORT_FDOFLAG_CC_LOCK));
  418. SET_FDO_FLAG(usb2DevExt, USBPORT_FDOFLAG_CC_LOCK);
  419. LOGENTRY(NULL, FdoDeviceObject, LOG_RH, 'grcc', FdoDeviceObject,
  420. usb2Fdo, 0);
  421. USBPORT_KdPrint((1, " >power 20 (on) %x\n",
  422. FdoDeviceObject));
  423. }
  424. InterlockedDecrement(&usb2DevExt->Fdo.DependentControllers);
  425. // at this point any of the dependent controllers can continue
  426. do {
  427. USBPORT_Wait(FdoDeviceObject, 10);
  428. // sync with the CC if this is a USB 2 controller
  429. // note that we only grab the CC lock if the root
  430. // hub PDO is enabled since it is released only
  431. // when the root hub is set to D0 -- this will never
  432. // happen if the rh is disabled
  433. } while (usb2DevExt->Fdo.DependentControllers);
  434. LOGENTRY(NULL, FdoDeviceObject, LOG_RH, 'u2GO', FdoDeviceObject,
  435. usb2Fdo, 0);
  436. }
  437. }