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

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. dmtimer.c
  5. Abstract:
  6. This module implements our 'deadman' timer DPC.
  7. This is our general purpose timer we use to deal with
  8. situations where the controller is not giving us
  9. interrupts.
  10. examples:
  11. root hub polling.
  12. dead controller detection
  13. Environment:
  14. kernel mode only
  15. Notes:
  16. Revision History:
  17. 1-1-00 : created
  18. --*/
  19. #include "common.h"
  20. // paged functions
  21. #ifdef ALLOC_PRAGMA
  22. #pragma alloc_text(PAGE, USBPORT_StartDM_Timer)
  23. #endif
  24. // non paged functions
  25. // USBPORT_DM_TimerDpc
  26. // USBPORT_StopDM_Timer
  27. VOID
  28. USBPORT_DM_TimerDpc(
  29. PKDPC Dpc,
  30. PVOID DeferredContext,
  31. PVOID SystemArgument1,
  32. PVOID SystemArgument2
  33. )
  34. /*++
  35. Routine Description:
  36. This routine runs at DISPATCH_LEVEL IRQL.
  37. Arguments:
  38. Dpc - Pointer to the DPC object.
  39. DeferredContext - supplies FdoDeviceObject.
  40. SystemArgument1 - not used.
  41. SystemArgument2 - not used.
  42. Return Value:
  43. None.
  44. --*/
  45. {
  46. PDEVICE_OBJECT fdoDeviceObject = DeferredContext;
  47. PDEVICE_EXTENSION devExt;
  48. BOOLEAN setTimer;
  49. KIRQL irql;
  50. GET_DEVICE_EXT(devExt, fdoDeviceObject);
  51. ASSERT_FDOEXT(devExt);
  52. #if DBG
  53. {
  54. LARGE_INTEGER t;
  55. KeQuerySystemTime(&t);
  56. LOGENTRY(NULL, fdoDeviceObject, LOG_NOISY, 'dmTM', fdoDeviceObject,
  57. t.LowPart, 0);
  58. }
  59. #endif
  60. // if stop fires it will stall here
  61. // if stop is running we stall here
  62. USBPORT_ACQUIRE_DM_LOCK(devExt, irql);
  63. #ifdef XPSE
  64. // poll while suspended
  65. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_SUSPENDED) &&
  66. TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_POLL_IN_SUSPEND)) {
  67. MP_CheckController(devExt);
  68. if (!TEST_FDO_FLAG(devExt,USBPORT_FDOFLAG_CONTROLLER_GONE)) {
  69. MP_PollController(devExt);
  70. }
  71. }
  72. #endif
  73. USBPORT_SynchronizeControllersStart(fdoDeviceObject);
  74. // skip timer is set when we are in low power
  75. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_SKIP_TIMER_WORK)) {
  76. // some work we should always do
  77. // for an upcomming bug fix
  78. USBPORT_BadRequestFlush(fdoDeviceObject, FALSE);
  79. } else {
  80. MP_CheckController(devExt);
  81. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_POLL_CONTROLLER) &&
  82. !TEST_FDO_FLAG(devExt,USBPORT_FDOFLAG_CONTROLLER_GONE)) {
  83. MP_PollController(devExt);
  84. }
  85. // call the ISR worker here in the event that the controller
  86. // is unable to generate interrupts
  87. USBPORT_IsrDpcWorker(fdoDeviceObject, FALSE);
  88. USBPORT_TimeoutAllEndpoints(fdoDeviceObject);
  89. // invalidate all isochronous endpoints
  90. // flush async requests
  91. USBPORT_BadRequestFlush(fdoDeviceObject, FALSE);
  92. }
  93. setTimer = TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_ENABLED);
  94. USBPORT_RELEASE_DM_LOCK(devExt, irql);
  95. if (setTimer) {
  96. ULONG timerIncerent;
  97. LONG dueTime;
  98. timerIncerent = KeQueryTimeIncrement() - 1;
  99. // round up to the next highest timer increment
  100. dueTime= -1 *
  101. (MILLISECONDS_TO_100_NS_UNITS(devExt->Fdo.DM_TimerInterval) + timerIncerent);
  102. KeSetTimer(&devExt->Fdo.DM_Timer,
  103. RtlConvertLongToLargeInteger(dueTime),
  104. &devExt->Fdo.DM_TimerDpc);
  105. INCREMENT_PENDING_REQUEST_COUNT(fdoDeviceObject, NULL);
  106. }
  107. // this timer is done
  108. DECREMENT_PENDING_REQUEST_COUNT(fdoDeviceObject, NULL);
  109. }
  110. // BUGBUG HP ia64 fix
  111. VOID
  112. USBPORT_DoRootHubCallback(
  113. PDEVICE_OBJECT FdoDeviceObject,
  114. PDEVICE_OBJECT Usb2Fdo
  115. )
  116. /*++
  117. Routine Description:
  118. Execute the root hub notifaction callback -- called from the
  119. worker thread
  120. Arguments:
  121. FdoDeviceObject - FDO device object for a USB 1.1 controller
  122. that may be a CC
  123. Return Value:
  124. None.
  125. --*/
  126. {
  127. PDEVICE_EXTENSION devExt, rhDevExt;
  128. PRH_INIT_CALLBACK cb;
  129. PVOID context;
  130. KIRQL irql;
  131. GET_DEVICE_EXT(devExt, FdoDeviceObject);
  132. ASSERT_FDOEXT(devExt);
  133. GET_DEVICE_EXT(rhDevExt, devExt->Fdo.RootHubPdo);
  134. ASSERT_PDOEXT(rhDevExt);
  135. KeAcquireSpinLock(&devExt->Fdo.HcSyncSpin.sl, &irql);
  136. USBPORT_ASSERT(rhDevExt->Pdo.HubInitCallback != NULL);
  137. LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'syCB', rhDevExt, 0, 0);
  138. context = rhDevExt->Pdo.HubInitContext;
  139. cb = rhDevExt->Pdo.HubInitCallback;
  140. rhDevExt->Pdo.HubInitCallback = NULL;
  141. rhDevExt->Pdo.HubInitContext = NULL;
  142. KeReleaseSpinLock(&devExt->Fdo.HcSyncSpin.sl, irql);
  143. // root hub list should be empty
  144. #if DBG
  145. {
  146. PHCD_ENDPOINT ep = rhDevExt->Pdo.RootHubInterruptEndpoint;
  147. USBPORT_ASSERT(IsListEmpty(&ep->ActiveList));
  148. USBPORT_ASSERT(IsListEmpty(&ep->PendingList));
  149. }
  150. #endif
  151. // perform High speed chirp now. The EHCI driver is started and the CC
  152. // must also be started since the root hub has registered a callback
  153. // this will prevent the controller from going into suspend right away
  154. // for lack of devices.
  155. if (Usb2Fdo) {
  156. PDEVICE_EXTENSION usb2DevExt;
  157. GET_DEVICE_EXT(usb2DevExt, Usb2Fdo);
  158. ASSERT_FDOEXT(usb2DevExt);
  159. // note in .NET this function takes the CC FDO
  160. // and aquires the CC lock
  161. // the USB 2 controller may already be in suspend if the CC root hub is
  162. // being added later, for this case we skip the chirp.
  163. if (!TEST_FDO_FLAG(usb2DevExt, USBPORT_FDOFLAG_SUSPENDED)) {
  164. USBPORT_RootHub_PowerAndChirpAllCcPorts(FdoDeviceObject);
  165. }
  166. }
  167. cb(context);
  168. }
  169. VOID
  170. USBPORT_SynchronizeControllersStart(
  171. PDEVICE_OBJECT FdoDeviceObject
  172. )
  173. /*++
  174. Routine Description:
  175. see if it is OK to start a controllers root hub
  176. Arguments:
  177. FdoDeviceObject - FDO device object for a USB 1.1 controller
  178. that may be a CC
  179. Return Value:
  180. None.
  181. --*/
  182. {
  183. PDEVICE_EXTENSION devExt, rhDevExt;
  184. KIRQL irql;
  185. PRH_INIT_CALLBACK cb;
  186. PVOID context;
  187. BOOLEAN okToStart;
  188. PDEVICE_OBJECT usb2Fdo = NULL;
  189. PDEVICE_EXTENSION usb2DevExt;
  190. GET_DEVICE_EXT(devExt, FdoDeviceObject);
  191. ASSERT_FDOEXT(devExt);
  192. // skip if we don't have a root hub
  193. if (devExt->Fdo.RootHubPdo == NULL) {
  194. return;
  195. }
  196. GET_DEVICE_EXT(rhDevExt, devExt->Fdo.RootHubPdo);
  197. ASSERT_PDOEXT(rhDevExt);
  198. // if no callback registered skip the whole process
  199. if (rhDevExt->Pdo.HubInitCallback == NULL) {
  200. return;
  201. }
  202. // if callback pending on worker thread skip
  203. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_SIGNAL_RH)) {
  204. return;
  205. }
  206. LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'syn1', FdoDeviceObject, 0, 0);
  207. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_IS_CC)) {
  208. okToStart = FALSE;
  209. //
  210. // we need to find the 2.0 master controller,
  211. // if the hub has started then it is OK to
  212. // start.
  213. usb2Fdo = USBPORT_FindUSB2Controller(FdoDeviceObject);
  214. LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'syn2', 0, 0, usb2Fdo);
  215. if (usb2Fdo != NULL) {
  216. GET_DEVICE_EXT(usb2DevExt, usb2Fdo);
  217. ASSERT_FDOEXT(usb2DevExt);
  218. if (TEST_FLAG(usb2DevExt->PnpStateFlags, USBPORT_PNP_STARTED)) {
  219. LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'syn3', 0, 0, usb2Fdo);
  220. okToStart = TRUE;
  221. }
  222. }
  223. // is companion but check if OK to bypass the wait
  224. if (TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_CC_ENUM_OK)) {
  225. okToStart = TRUE;
  226. }
  227. } else {
  228. // not a CC, it is OK to start immediatly
  229. LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'syn4', 0, 0, 0);
  230. okToStart = TRUE;
  231. }
  232. // check for a start-hub callback notification. If we have
  233. // one then notify the hub that it is OK
  234. if (okToStart) {
  235. if (usb2Fdo) {
  236. GET_DEVICE_EXT(usb2DevExt, usb2Fdo);
  237. // signal the worker to chirp and do the callback
  238. SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_SIGNAL_RH);
  239. InterlockedIncrement(&usb2DevExt->Fdo.PendingRhCallback);
  240. LOGENTRY(NULL, FdoDeviceObject, LOG_PNP, 'prh+', 0, 0,
  241. usb2DevExt->Fdo.PendingRhCallback);
  242. USBPORT_SignalWorker(FdoDeviceObject);
  243. } else {
  244. // no 2.0 controller just callback now
  245. USBPORT_DoRootHubCallback(FdoDeviceObject, NULL);
  246. }
  247. }
  248. }
  249. VOID
  250. USBPORT_BadRequestFlush(
  251. PDEVICE_OBJECT FdoDeviceObject,
  252. BOOLEAN ForceFlush
  253. )
  254. /*++
  255. Routine Description:
  256. Asynchronously flush bad requests from client drivers
  257. Arguments:
  258. Return Value:
  259. None.
  260. --*/
  261. {
  262. PDEVICE_EXTENSION devExt;
  263. KIRQL irql;
  264. GET_DEVICE_EXT(devExt, FdoDeviceObject);
  265. ASSERT_FDOEXT(devExt);
  266. // The purpose of this async flush is to emulate the Win2k
  267. // behavior where we put tranfers on the HW to devices that
  268. // had been removed and let them time out.
  269. //
  270. // origanlly I used 5 here to fix problems with hid drivers
  271. // on win2k+usb20, we may need to change this depending on
  272. // which OS we are running on, we want use a smaller value on
  273. // XP since HID (the major offender) has been fixed and many
  274. // of our other in house class drivers support hot remove
  275. // better.
  276. #define BAD_REQUEST_FLUSH 0
  277. devExt->Fdo.BadRequestFlush++;
  278. if (devExt->Fdo.BadRequestFlush > devExt->Fdo.BadReqFlushThrottle ||
  279. ForceFlush) {
  280. devExt->Fdo.BadRequestFlush = 0;
  281. // flush and complete any 'bad parameter' requests
  282. ACQUIRE_BADREQUEST_LOCK(FdoDeviceObject, irql);
  283. while (1) {
  284. PLIST_ENTRY listEntry;
  285. PIRP irp;
  286. PUSB_IRP_CONTEXT irpContext;
  287. NTSTATUS ntStatus;
  288. if (IsListEmpty(&devExt->Fdo.BadRequestList)) {
  289. break;
  290. }
  291. listEntry = RemoveHeadList(&devExt->Fdo.BadRequestList);
  292. //irp = (PIRP) CONTAINING_RECORD(
  293. // listEntry,
  294. // struct _IRP,
  295. // Tail.Overlay.ListEntry);
  296. irpContext = (PUSB_IRP_CONTEXT) CONTAINING_RECORD(
  297. listEntry,
  298. struct _USB_IRP_CONTEXT,
  299. ListEntry);
  300. ASSERT_IRP_CONTEXT(irpContext);
  301. irp = irpContext->Irp;
  302. if (irp->Cancel) {
  303. ntStatus = STATUS_CANCELLED;
  304. } else {
  305. ntStatus = STATUS_DEVICE_NOT_CONNECTED;
  306. }
  307. RELEASE_BADREQUEST_LOCK(FdoDeviceObject, irql);
  308. // cancel routine did not run
  309. LOGENTRY(NULL, FdoDeviceObject, LOG_IRPS, 'cpBA', irp, irpContext, 0);
  310. USBPORT_CompleteIrp(devExt->Fdo.RootHubPdo, irp,
  311. ntStatus, 0);
  312. FREE_POOL(FdoDeviceObject, irpContext);
  313. ACQUIRE_BADREQUEST_LOCK(FdoDeviceObject, irql);
  314. }
  315. RELEASE_BADREQUEST_LOCK(FdoDeviceObject, irql);
  316. }
  317. }
  318. VOID
  319. USBPORT_StartDM_Timer(
  320. PDEVICE_OBJECT FdoDeviceObject,
  321. LONG MillisecondInterval
  322. )
  323. /*++
  324. Routine Description:
  325. Inialize and start the timer
  326. Arguments:
  327. FdoDeviceObject - DeviceObject of the controller to stop
  328. MillisecondInterval -
  329. Return Value:
  330. None.
  331. --*/
  332. {
  333. PDEVICE_EXTENSION devExt;
  334. LONG dueTime;
  335. ULONG timerIncerent;
  336. PAGED_CODE();
  337. GET_DEVICE_EXT(devExt, FdoDeviceObject);
  338. ASSERT_FDOEXT(devExt);
  339. timerIncerent = KeQueryTimeIncrement() - 1;
  340. // remember interval for repeated use
  341. devExt->Fdo.DM_TimerInterval = MillisecondInterval;
  342. // round up to the next highest timer increment
  343. dueTime= -1 * (MILLISECONDS_TO_100_NS_UNITS(MillisecondInterval) +
  344. timerIncerent);
  345. USBPORT_KdPrint((1, " DM timer (100ns) = %d\n", dueTime));
  346. SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_ENABLED);
  347. // we consider the timer a pending request while it is
  348. // scheduled
  349. INCREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
  350. KeInitializeTimer(&devExt->Fdo.DM_Timer);
  351. KeInitializeDpc(&devExt->Fdo.DM_TimerDpc,
  352. USBPORT_DM_TimerDpc,
  353. FdoDeviceObject);
  354. KeSetTimer(&devExt->Fdo.DM_Timer,
  355. RtlConvertLongToLargeInteger(dueTime),
  356. &devExt->Fdo.DM_TimerDpc);
  357. SET_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_INIT);
  358. }
  359. VOID
  360. USBPORT_StopDM_Timer(
  361. PDEVICE_OBJECT FdoDeviceObject
  362. )
  363. /*++
  364. Routine Description:
  365. stop the timer
  366. Arguments:
  367. FdoDeviceObject - DeviceObject of the controller to stop
  368. Return Value:
  369. None.
  370. --*/
  371. {
  372. PDEVICE_EXTENSION devExt;
  373. KIRQL irql;
  374. BOOLEAN inQueue;
  375. GET_DEVICE_EXT(devExt, FdoDeviceObject);
  376. ASSERT_FDOEXT(devExt);
  377. if (!TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_INIT)) {
  378. // timer never started so bypass the stop
  379. return;
  380. }
  381. // if timer fires it will stall here
  382. // if timer is running we will stall here
  383. USBPORT_ACQUIRE_DM_LOCK(devExt, irql);
  384. USBPORT_ASSERT(TEST_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_ENABLED));
  385. LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'kilT', FdoDeviceObject, 0, 0);
  386. CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_ENABLED);
  387. // timer will no longer re-schedule
  388. USBPORT_RELEASE_DM_LOCK(devExt, irql);
  389. // if there is a timer in the queue, remove it
  390. inQueue = KeCancelTimer(&devExt->Fdo.DM_Timer);
  391. if (inQueue) {
  392. // it was queue, so dereference now
  393. LOGENTRY(NULL, FdoDeviceObject, LOG_MISC, 'klIQ', FdoDeviceObject, 0, 0);
  394. DECREMENT_PENDING_REQUEST_COUNT(FdoDeviceObject, NULL);
  395. }
  396. CLEAR_FDO_FLAG(devExt, USBPORT_FDOFLAG_DM_TIMER_INIT);
  397. }