Source code of Windows XP (NT5)
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.

895 lines
29 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. device.c
  5. Abstract
  6. Resource management routines for devices and collections
  7. Author:
  8. ervinp
  9. Environment:
  10. Kernel mode only
  11. Revision History:
  12. --*/
  13. #include "pch.h"
  14. #ifdef ALLOC_PRAGMA
  15. #pragma alloc_text(PAGE, HidpStartDevice)
  16. #pragma alloc_text(PAGE, HidpStartCollectionPDO)
  17. #pragma alloc_text(PAGE, AllocDeviceResources)
  18. #pragma alloc_text(PAGE, FreeDeviceResources)
  19. #pragma alloc_text(PAGE, AllocCollectionResources)
  20. #pragma alloc_text(PAGE, FreeCollectionResources)
  21. #pragma alloc_text(PAGE, InitializeCollection)
  22. #pragma alloc_text(PAGE, HidpCleanUpFdo)
  23. #pragma alloc_text(PAGE, HidpRemoveDevice)
  24. #pragma alloc_text(PAGE, HidpRemoveCollection)
  25. #endif
  26. /*
  27. ********************************************************************************
  28. * AllocDeviceResources
  29. ********************************************************************************
  30. *
  31. *
  32. */
  33. NTSTATUS AllocDeviceResources(FDO_EXTENSION *fdoExt)
  34. {
  35. ULONG numCollections;
  36. NTSTATUS status = STATUS_SUCCESS;
  37. PAGED_CODE();
  38. /*
  39. * This will allocate fdoExt->rawReportDescription
  40. */
  41. status = HidpGetDeviceDescriptor(fdoExt);
  42. if (NT_SUCCESS(status)){
  43. /*
  44. * Ask HIDPARSE to fill in the HIDP_DEVICE_DESC for this device.
  45. */
  46. status = HidP_GetCollectionDescription(
  47. fdoExt->rawReportDescription,
  48. fdoExt->rawReportDescriptionLength,
  49. NonPagedPool,
  50. &fdoExt->deviceDesc);
  51. if (NT_SUCCESS(status)){
  52. fdoExt->devDescInitialized = TRUE;
  53. numCollections = fdoExt->deviceDesc.CollectionDescLength;
  54. ASSERT(numCollections);
  55. fdoExt->classCollectionArray = ALLOCATEPOOL(NonPagedPool, numCollections*sizeof(HIDCLASS_COLLECTION));
  56. if (!fdoExt->classCollectionArray){
  57. fdoExt->classCollectionArray = BAD_POINTER;
  58. status = STATUS_INSUFFICIENT_RESOURCES;
  59. } else {
  60. RtlZeroMemory(fdoExt->classCollectionArray, numCollections*sizeof(HIDCLASS_COLLECTION));
  61. }
  62. }
  63. }
  64. else {
  65. fdoExt->rawReportDescription = BAD_POINTER;
  66. }
  67. DBGSUCCESS(status, FALSE)
  68. return status;
  69. }
  70. /*
  71. ********************************************************************************
  72. * FreeDeviceResources
  73. ********************************************************************************
  74. *
  75. *
  76. */
  77. VOID FreeDeviceResources(FDO_EXTENSION *fdoExt)
  78. {
  79. ULONG i;
  80. PAGED_CODE();
  81. for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++) {
  82. FreeCollectionResources(fdoExt, fdoExt->classCollectionArray[i].CollectionNumber);
  83. }
  84. /*
  85. * Free the stuff returned by HIDPARSE's HidP_GetCollectionDescription.
  86. */
  87. if (fdoExt->devDescInitialized){
  88. HidP_FreeCollectionDescription(&fdoExt->deviceDesc);
  89. #if DBG
  90. fdoExt->deviceDesc.CollectionDesc = BAD_POINTER;
  91. fdoExt->deviceDesc.ReportIDs = BAD_POINTER;
  92. #endif
  93. }
  94. fdoExt->deviceDesc.CollectionDescLength = 0;
  95. /*
  96. * Free the raw report descriptor allocated during START_DEVICE by HidpGetDeviceDescriptor().
  97. */
  98. if (ISPTR(fdoExt->rawReportDescription)){
  99. ExFreePool(fdoExt->rawReportDescription);
  100. }
  101. fdoExt->rawReportDescription = BAD_POINTER;
  102. if (ISPTR(fdoExt->classCollectionArray)){
  103. ExFreePool(fdoExt->classCollectionArray);
  104. }
  105. fdoExt->classCollectionArray = BAD_POINTER;
  106. }
  107. /*
  108. ********************************************************************************
  109. * AllocCollectionResources
  110. ********************************************************************************
  111. *
  112. *
  113. */
  114. NTSTATUS AllocCollectionResources(FDO_EXTENSION *fdoExt, ULONG collectionNum)
  115. {
  116. PHIDCLASS_COLLECTION collection;
  117. NTSTATUS status = STATUS_SUCCESS;
  118. PAGED_CODE();
  119. collection = GetHidclassCollection(fdoExt, collectionNum);
  120. if (collection){
  121. ULONG descriptorLen;
  122. descriptorLen = collection->hidCollectionInfo.DescriptorSize;
  123. if (descriptorLen){
  124. collection->phidDescriptor = ALLOCATEPOOL(NonPagedPool, descriptorLen);
  125. if (collection->phidDescriptor){
  126. status = HidpGetCollectionDescriptor(
  127. fdoExt,
  128. collection->CollectionNumber,
  129. collection->phidDescriptor,
  130. &descriptorLen);
  131. }
  132. else {
  133. collection->phidDescriptor = BAD_POINTER;
  134. status = STATUS_INSUFFICIENT_RESOURCES;
  135. }
  136. if (NT_SUCCESS(status)){
  137. ULONG i = collection->CollectionIndex;
  138. ULONG inputLength;
  139. ASSERT(fdoExt->devDescInitialized);
  140. inputLength = fdoExt->deviceDesc.CollectionDesc[i].InputLength;
  141. if (inputLength){
  142. if (collection->hidCollectionInfo.Polled){
  143. collection->cookedInterruptReportBuf = BAD_POINTER;
  144. }
  145. else {
  146. collection->cookedInterruptReportBuf = ALLOCATEPOOL(NonPagedPool, inputLength);
  147. if (!collection->cookedInterruptReportBuf){
  148. status = STATUS_INSUFFICIENT_RESOURCES;
  149. }
  150. }
  151. fdoExt->isOutputOnlyDevice = FALSE;
  152. }
  153. else {
  154. /*
  155. * This is an output-only device (e.g. USB monitor)
  156. */
  157. DBGWARN(("Zero input length -> output-only device."))
  158. collection->cookedInterruptReportBuf = BAD_POINTER;
  159. }
  160. }
  161. }
  162. else {
  163. ASSERT(descriptorLen > 0);
  164. status = STATUS_DEVICE_CONFIGURATION_ERROR;
  165. }
  166. }
  167. else {
  168. status = STATUS_DEVICE_DATA_ERROR;
  169. }
  170. DBGSUCCESS(status, TRUE)
  171. return status;
  172. }
  173. /*
  174. ********************************************************************************
  175. * FreeCollectionResources
  176. ********************************************************************************
  177. *
  178. *
  179. */
  180. VOID FreeCollectionResources(FDO_EXTENSION *fdoExt, ULONG collectionNum)
  181. {
  182. PHIDCLASS_COLLECTION collection;
  183. PAGED_CODE();
  184. collection = GetHidclassCollection(fdoExt, collectionNum);
  185. if (collection){
  186. if (collection->hidCollectionInfo.Polled){
  187. if (ISPTR(collection->savedPolledReportBuf)){
  188. ExFreePool(collection->savedPolledReportBuf);
  189. }
  190. collection->savedPolledReportBuf = BAD_POINTER;
  191. }
  192. else {
  193. if (ISPTR(collection->cookedInterruptReportBuf)){
  194. ExFreePool(collection->cookedInterruptReportBuf);
  195. }
  196. else {
  197. // this is an output-only collection
  198. }
  199. }
  200. collection->cookedInterruptReportBuf = BAD_POINTER;
  201. if (ISPTR(collection->phidDescriptor)){
  202. ExFreePool(collection->phidDescriptor);
  203. }
  204. collection->phidDescriptor = BAD_POINTER;
  205. }
  206. else {
  207. TRAP;
  208. }
  209. }
  210. /*
  211. ********************************************************************************
  212. * InitializeCollection
  213. ********************************************************************************
  214. *
  215. *
  216. */
  217. NTSTATUS InitializeCollection(FDO_EXTENSION *fdoExt, ULONG collectionIndex)
  218. {
  219. PHIDCLASS_COLLECTION collection;
  220. ULONG descriptorBufLen;
  221. NTSTATUS status = STATUS_SUCCESS;
  222. PAGED_CODE();
  223. ASSERT(ISPTR(fdoExt->classCollectionArray));
  224. collection = &fdoExt->classCollectionArray[collectionIndex];
  225. RtlZeroMemory(collection, sizeof(HIDCLASS_COLLECTION));
  226. ASSERT(fdoExt->devDescInitialized);
  227. collection->CollectionNumber = fdoExt->deviceDesc.CollectionDesc[collectionIndex].CollectionNumber;
  228. collection->CollectionIndex = collectionIndex;
  229. InitializeListHead(&collection->FileExtensionList);
  230. KeInitializeSpinLock(&collection->FileExtensionListSpinLock);
  231. KeInitializeSpinLock(&collection->powerEventSpinLock);
  232. KeInitializeSpinLock(&collection->secureReadLock);
  233. collection->secureReadMode = 0;
  234. descriptorBufLen = sizeof(HID_COLLECTION_INFORMATION);
  235. status = HidpGetCollectionInformation( fdoExt,
  236. collection->CollectionNumber,
  237. &collection->hidCollectionInfo,
  238. &descriptorBufLen);
  239. DBGSUCCESS(status, TRUE)
  240. return status;
  241. }
  242. void
  243. HidpGetRemoteWakeEnableState(
  244. PDO_EXTENSION *pdoExt
  245. )
  246. {
  247. HANDLE hKey;
  248. NTSTATUS status;
  249. ULONG tmp;
  250. BOOLEAN wwEnableFound;
  251. hKey = NULL;
  252. wwEnableFound = FALSE;
  253. status = IoOpenDeviceRegistryKey (pdoExt->pdo,
  254. PLUGPLAY_REGKEY_DEVICE,
  255. STANDARD_RIGHTS_ALL,
  256. &hKey);
  257. if (NT_SUCCESS (status)) {
  258. UNICODE_STRING valueName;
  259. ULONG length;
  260. ULONG value;
  261. PKEY_VALUE_FULL_INFORMATION fullInfo;
  262. PAGED_CODE();
  263. RtlInitUnicodeString (&valueName, HIDCLASS_REMOTE_WAKE_ENABLE);
  264. length = sizeof (KEY_VALUE_FULL_INFORMATION)
  265. + valueName.MaximumLength
  266. + sizeof(value);
  267. fullInfo = ExAllocatePool (PagedPool, length);
  268. if (fullInfo) {
  269. status = ZwQueryValueKey (hKey,
  270. &valueName,
  271. KeyValueFullInformation,
  272. fullInfo,
  273. length,
  274. &length);
  275. if (NT_SUCCESS (status)) {
  276. DBGASSERT (sizeof(value) == fullInfo->DataLength,
  277. ("Value data wrong length for REmote wake reg value."),
  278. TRUE);
  279. RtlCopyMemory (&value,
  280. ((PUCHAR) fullInfo) + fullInfo->DataOffset,
  281. fullInfo->DataLength);
  282. pdoExt->remoteWakeEnabled = (value ? TRUE : FALSE);
  283. }
  284. ExFreePool (fullInfo);
  285. }
  286. ZwClose (hKey);
  287. hKey = NULL;
  288. }
  289. }
  290. WMIGUIDREGINFO HidClassWmiGuidList =
  291. {
  292. &GUID_POWER_DEVICE_WAKE_ENABLE,
  293. 1,
  294. 0 // wait wake
  295. };
  296. WMIGUIDREGINFO HidClassFdoWmiGuidList =
  297. {
  298. &GUID_POWER_DEVICE_ENABLE,
  299. 1,
  300. 0
  301. };
  302. /*
  303. ********************************************************************************
  304. * HidpStartCollectionPDO
  305. ********************************************************************************
  306. *
  307. *
  308. */
  309. NTSTATUS HidpStartCollectionPDO(FDO_EXTENSION *fdoExt, PDO_EXTENSION *pdoExt, PIRP Irp)
  310. {
  311. NTSTATUS status = STATUS_SUCCESS;
  312. PAGED_CODE();
  313. /*
  314. * Initialize the collection only if it's not already initialized.
  315. * This is so we don't destroy the FileExtensionList after a STOP/START.
  316. */
  317. if (pdoExt->state == COLLECTION_STATE_UNINITIALIZED){
  318. pdoExt->state = COLLECTION_STATE_INITIALIZED;
  319. }
  320. if (NT_SUCCESS(status)){
  321. PHIDCLASS_COLLECTION collection = GetHidclassCollection(fdoExt, pdoExt->collectionNum);
  322. if (collection){
  323. /*
  324. * If all collection PDOs for this device FDO are initialized,
  325. * figure out the maximum report size and finish starting the device.
  326. */
  327. if (AnyClientPDOsInitialized(fdoExt, TRUE)){
  328. DBGSTATE(fdoExt->state, DEVICE_STATE_START_SUCCESS, FALSE)
  329. /*
  330. * If this is a polled collection,
  331. * start the background polling loop FOR EACH COLLECTION.
  332. * Otherwise, if it's an ordinary interrupt collection,
  333. * start the ping-pong IRPs for it.
  334. */
  335. if (collection->hidCollectionInfo.Polled){
  336. if (HidpSetMaxReportSize(fdoExt)){
  337. ULONG i;
  338. for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++){
  339. PHIDCLASS_COLLECTION ctn;
  340. ctn = &fdoExt->classCollectionArray[i];
  341. /*
  342. * If one of the collections is polled, they
  343. * should ALL be polled.
  344. */
  345. ASSERT(ctn->hidCollectionInfo.Polled);
  346. ctn->PollInterval_msec = DEFAULT_POLL_INTERVAL_MSEC;
  347. /*
  348. * Allocate the buffer for saving the polled device's
  349. * last report. Allocate one more byte than the max
  350. * report size for the device in case we have to
  351. * prepend a report id byte.
  352. */
  353. ctn->savedPolledReportBuf = ALLOCATEPOOL(NonPagedPool, fdoExt->maxReportSize+1);
  354. if (ctn->savedPolledReportBuf){
  355. ctn->polledDataIsStale = TRUE;
  356. StartPollingLoop(fdoExt, ctn, TRUE);
  357. status = STATUS_SUCCESS;
  358. }
  359. else {
  360. ASSERT(ctn->savedPolledReportBuf);
  361. status = STATUS_INSUFFICIENT_RESOURCES;
  362. }
  363. }
  364. }
  365. }
  366. else if (fdoExt->isOutputOnlyDevice){
  367. /*
  368. * Don't start ping-pong IRPs.
  369. */
  370. }
  371. else {
  372. status = HidpStartAllPingPongs(fdoExt);
  373. }
  374. }
  375. if (NT_SUCCESS(status)) {
  376. pdoExt->state = COLLECTION_STATE_RUNNING;
  377. #if DBG
  378. collection->Signature = HIDCLASS_COLLECTION_SIG;
  379. #endif
  380. /*
  381. * Create the 'file-name' used by clients to open this device.
  382. */
  383. HidpCreateSymbolicLink(pdoExt, pdoExt->collectionNum, TRUE, pdoExt->pdo);
  384. if (!pdoExt->MouseOrKeyboard &&
  385. WAITWAKE_SUPPORTED(fdoExt)) {
  386. //
  387. // register for the wait wake guid as well
  388. //
  389. pdoExt->WmiLibInfo.GuidCount = sizeof (HidClassWmiGuidList) /
  390. sizeof (WMIGUIDREGINFO);
  391. ASSERT (1 == pdoExt->WmiLibInfo.GuidCount);
  392. //
  393. // See if the user has enabled remote wake for the device
  394. // PRIOR to registering with WMI.
  395. //
  396. HidpGetRemoteWakeEnableState(pdoExt);
  397. pdoExt->WmiLibInfo.GuidList = &HidClassWmiGuidList;
  398. pdoExt->WmiLibInfo.QueryWmiRegInfo = HidpQueryWmiRegInfo;
  399. pdoExt->WmiLibInfo.QueryWmiDataBlock = HidpQueryWmiDataBlock;
  400. pdoExt->WmiLibInfo.SetWmiDataBlock = HidpSetWmiDataBlock;
  401. pdoExt->WmiLibInfo.SetWmiDataItem = HidpSetWmiDataItem;
  402. pdoExt->WmiLibInfo.ExecuteWmiMethod = NULL;
  403. pdoExt->WmiLibInfo.WmiFunctionControl = NULL;
  404. IoWMIRegistrationControl(pdoExt->pdo, WMIREG_ACTION_REGISTER);
  405. if (SHOULD_SEND_WAITWAKE(pdoExt)) {
  406. HidpCreateRemoteWakeIrp(pdoExt);
  407. }
  408. }
  409. if (AllClientPDOsInitialized(fdoExt, TRUE)){
  410. HidpStartIdleTimeout(fdoExt, TRUE);
  411. }
  412. }
  413. }
  414. else {
  415. status = STATUS_DEVICE_DATA_ERROR;
  416. }
  417. }
  418. DBGSUCCESS(status, FALSE)
  419. return status;
  420. }
  421. /*
  422. ********************************************************************************
  423. * HidpStartDevice
  424. ********************************************************************************
  425. *
  426. *
  427. */
  428. NTSTATUS HidpStartDevice(PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension, PIRP Irp)
  429. {
  430. FDO_EXTENSION *fdoExt;
  431. enum deviceState previousState;
  432. NTSTATUS status;
  433. ULONG i;
  434. PAGED_CODE();
  435. ASSERT(!HidDeviceExtension->isClientPdo);
  436. fdoExt = &HidDeviceExtension->fdoExt;
  437. previousState = fdoExt->state;
  438. fdoExt->state = DEVICE_STATE_STARTING;
  439. /*
  440. * Get the power-state conversion table
  441. */
  442. status = HidpQueryDeviceCapabilities(
  443. HidDeviceExtension->hidExt.PhysicalDeviceObject,
  444. &fdoExt->deviceCapabilities);
  445. if (NT_SUCCESS(status)){
  446. /*
  447. * Alert the rest of the driver stack that the device is starting.
  448. */
  449. IoCopyCurrentIrpStackLocationToNext(Irp);
  450. status = HidpCallDriverSynchronous(fdoExt->fdo, Irp);
  451. if (NT_SUCCESS(status)){
  452. /*
  453. * If we're just resuming from STOP,
  454. * there's nothing else to do;
  455. * otherwise, need to call down the USB stack
  456. * for some info and allocate some resources.
  457. */
  458. if (previousState == DEVICE_STATE_INITIALIZED){
  459. status = AllocDeviceResources(fdoExt);
  460. if (NT_SUCCESS(status)){
  461. /*
  462. * Assume this is an output-only device until we start
  463. * a collection-pdo which handles inputs.
  464. * Only set fdoExt->isOutputOnlyDevice on the first start
  465. * not on a subsequent start following a stop.
  466. */
  467. fdoExt->isOutputOnlyDevice = TRUE;
  468. /*
  469. * Initialize WMI stuff
  470. */
  471. fdoExt->WmiLibInfo.GuidCount = sizeof(HidClassFdoWmiGuidList) /
  472. sizeof (WMIGUIDREGINFO);
  473. fdoExt->WmiLibInfo.GuidList = &HidClassFdoWmiGuidList;
  474. fdoExt->WmiLibInfo.QueryWmiRegInfo = HidpQueryWmiRegInfo;
  475. fdoExt->WmiLibInfo.QueryWmiDataBlock = HidpQueryWmiDataBlock;
  476. fdoExt->WmiLibInfo.SetWmiDataBlock = HidpSetWmiDataBlock;
  477. fdoExt->WmiLibInfo.SetWmiDataItem = HidpSetWmiDataItem;
  478. fdoExt->WmiLibInfo.ExecuteWmiMethod = NULL;
  479. fdoExt->WmiLibInfo.WmiFunctionControl = NULL;
  480. /*
  481. * Allocate all the collection resources before allocating
  482. * the pingpong irps, so that we can set a maximum report
  483. * size.
  484. */
  485. for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++) {
  486. // If one of these fails, we will clean up properly
  487. // in the remove routine, so there's no need to
  488. // bother cleaning up here.
  489. status = InitializeCollection(fdoExt, i);
  490. if (!NT_SUCCESS(status)){
  491. break;
  492. }
  493. status = AllocCollectionResources(fdoExt, fdoExt->deviceDesc.CollectionDesc[i].CollectionNumber);
  494. if (!NT_SUCCESS(status)){
  495. break;
  496. }
  497. }
  498. /*
  499. * We need ot allocate the pingpongs in the fdo start
  500. * routine due to race conditions introduced by selective
  501. * suspend.
  502. */
  503. if (!fdoExt->isOutputOnlyDevice &&
  504. !fdoExt->driverExt->DevicesArePolled) {
  505. status = HidpReallocPingPongIrps(fdoExt, MIN_PINGPONG_IRPS);
  506. }
  507. if (NT_SUCCESS(status)){
  508. /*
  509. * We will have to create an array of PDOs, one for each device class.
  510. * The following call will cause NTKERN to call us back with
  511. * IRP_MN_QUERY_DEVICE_RELATIONS and initialize its collection-PDOs.
  512. */
  513. IoInvalidateDeviceRelations(HidDeviceExtension->hidExt.PhysicalDeviceObject, BusRelations);
  514. }
  515. }
  516. }
  517. else if (previousState == DEVICE_STATE_STOPPED){
  518. //
  519. // Any request that comes in when we are in low power will be
  520. // dealt with at that time
  521. //
  522. DBGSTATE(fdoExt->prevState, DEVICE_STATE_START_SUCCESS, TRUE)
  523. }
  524. else {
  525. TRAP;
  526. status = STATUS_DEVICE_CONFIGURATION_ERROR;
  527. }
  528. }
  529. }
  530. if (NT_SUCCESS(status)){
  531. fdoExt->state = DEVICE_STATE_START_SUCCESS;
  532. #if WIN95_BUILD
  533. /*
  534. * Send down the WaitWake IRP.
  535. * This allows the device to wake up the system.
  536. *
  537. * Note: On Win98 there is no way for a client to
  538. * send a WaitWake IRP. Therefore we initiate
  539. * a WaitWake IRP for every device, not just
  540. * if a client sends us a WaitWake IRP, like on NT.
  541. */
  542. /*
  543. * We could have been suspended, stopped, then started again. In
  544. * this case, we need to not send a WW irp because we already have
  545. * on pending.
  546. */
  547. if (fdoExt->deviceCapabilities.SystemWake > PowerSystemWorking) {
  548. SubmitWaitWakeIrp(HidDeviceExtension);
  549. }
  550. #endif
  551. #if DBG
  552. {
  553. ULONG i;
  554. // Win98 doesn't have good debug extensions
  555. DBGVERBOSE(("Started fdoExt %ph with %d collections: ", fdoExt, fdoExt->deviceDesc.CollectionDescLength))
  556. for (i = 0; i < fdoExt->deviceDesc.CollectionDescLength; i++){
  557. DBGVERBOSE((" - collection #%d: (in=%xh,out=%xh,feature=%xh) usagePage %xh, usage %xh ",
  558. fdoExt->deviceDesc.CollectionDesc[i].CollectionNumber,
  559. fdoExt->deviceDesc.CollectionDesc[i].InputLength,
  560. fdoExt->deviceDesc.CollectionDesc[i].OutputLength,
  561. fdoExt->deviceDesc.CollectionDesc[i].FeatureLength,
  562. fdoExt->deviceDesc.CollectionDesc[i].UsagePage,
  563. fdoExt->deviceDesc.CollectionDesc[i].Usage))
  564. }
  565. }
  566. #endif
  567. }
  568. else {
  569. fdoExt->state = DEVICE_STATE_START_FAILURE;
  570. }
  571. DBGSUCCESS(status, FALSE)
  572. return status;
  573. }
  574. VOID
  575. HidpCleanUpFdo(FDO_EXTENSION *fdoExt)
  576. {
  577. PAGED_CODE();
  578. if (fdoExt->openCount == 0){
  579. /*
  580. * This is the last CLOSE on an alreay-removed device.
  581. *
  582. * Free resources and the FDO name
  583. * (wPdoName that was allocated in HidpAddDevice);
  584. *
  585. */
  586. DequeueFdoExt(fdoExt);
  587. FreeDeviceResources(fdoExt);
  588. RtlFreeUnicodeString(&fdoExt->name);
  589. IoWMIRegistrationControl(fdoExt->fdo, WMIREG_ACTION_DEREGISTER);
  590. /*
  591. * Delete the device-FDO and all collection-PDOs
  592. * Don't touch fdoExt after this.
  593. */
  594. HidpDeleteDeviceObjects(fdoExt);
  595. }
  596. }
  597. /*
  598. ********************************************************************************
  599. * HidpRemoveDevice
  600. ********************************************************************************
  601. *
  602. */
  603. NTSTATUS HidpRemoveDevice(FDO_EXTENSION *fdoExt, IN PIRP Irp)
  604. {
  605. BOOLEAN proceedWithRemove;
  606. NTSTATUS status;
  607. PIRP IdleIrp;
  608. PAGED_CODE();
  609. /*
  610. * All collection-PDOs should have been removed by now,
  611. * but we want to verify this.
  612. * Only allow removal of this device-FDO if all the
  613. * collection-PDOs are removed
  614. * (or if they never got created in the first place).
  615. */
  616. if (fdoExt->prevState == DEVICE_STATE_START_FAILURE){
  617. proceedWithRemove = TRUE;
  618. }
  619. else if (fdoExt->prevState == DEVICE_STATE_STOPPED){
  620. /*
  621. * If a device fails to initialize, it may get
  622. * STOP_DEVICE before being removed, so we want to
  623. * go ahead and remove it without calling
  624. * AllClientPDOsInitialized, which accesses some
  625. * data which may not have been initialized.
  626. * In this case we're never checking for the
  627. * case that the device was initialized successfully,
  628. * then stopped, and then removed without its
  629. * collection-PDOs being removed; but this is an
  630. * illegal case, so we'll just punt on it.
  631. */
  632. proceedWithRemove = TRUE;
  633. }
  634. else if (AllClientPDOsInitialized(fdoExt, FALSE)){
  635. proceedWithRemove = TRUE;
  636. }
  637. else {
  638. /*
  639. * This shouldn't happen -- all the collection-PDOs
  640. * should have been removed before the device-FDO.
  641. */
  642. DBGERR(("State of fdo %x state is %d",fdoExt->fdo,fdoExt->state))
  643. TRAP;
  644. proceedWithRemove = FALSE;
  645. }
  646. if (proceedWithRemove){
  647. PHIDCLASS_DEVICE_EXTENSION HidDeviceExtension =
  648. CONTAINING_RECORD(fdoExt, HIDCLASS_DEVICE_EXTENSION, fdoExt);
  649. DBGASSERT((fdoExt->state == DEVICE_STATE_REMOVING ||
  650. fdoExt->state == DEVICE_STATE_INITIALIZED ||
  651. fdoExt->state == DEVICE_STATE_START_FAILURE),
  652. ("Device is in incorrect state: %x", fdoExt->state),
  653. TRUE)
  654. if (ISPTR(fdoExt->waitWakeIrp)){
  655. IoCancelIrp(fdoExt->waitWakeIrp);
  656. fdoExt->waitWakeIrp = BAD_POINTER;
  657. }
  658. HidpCancelIdleNotification(fdoExt, TRUE);
  659. if (ISPTR(fdoExt->idleNotificationRequest)) {
  660. IoFreeIrp(fdoExt->idleNotificationRequest);
  661. fdoExt->idleNotificationRequest = BAD_POINTER;
  662. }
  663. while (IdleIrp = DequeuePowerDelayedIrp(fdoExt)) {
  664. IdleIrp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
  665. IoCompleteRequest(IdleIrp, IO_NO_INCREMENT);
  666. }
  667. DestroyPingPongs(fdoExt);
  668. /*
  669. * Note: THE ORDER OF THESE ACTIONS IS VERY CRITICAL
  670. */
  671. Irp->IoStatus.Status = STATUS_SUCCESS;
  672. IoSkipCurrentIrpStackLocation (Irp);
  673. status = HidpCallDriver(fdoExt->fdo, Irp);
  674. fdoExt->state = DEVICE_STATE_REMOVED;
  675. DerefDriverExt(fdoExt->driverExt->MinidriverObject);
  676. fdoExt->driverExt = BAD_POINTER;
  677. /*
  678. * After Detach we can no longer send IRPS to this device
  679. * object as it will be GONE!
  680. */
  681. IoDetachDevice(HidDeviceExtension->hidExt.NextDeviceObject);
  682. /*
  683. * If all client handles on this device have been closed,
  684. * destroy the objects and our context for it;
  685. * otherwise, we'll do this when the last client closes
  686. * their handle.
  687. *
  688. * On NT we can only get here if all our creates have been closed, so
  689. * this is unnecessary, but on Win9x, a remove can be sent with valid
  690. * opens against the stack.
  691. *
  692. * Don't touch fdoExt after this.
  693. */
  694. HidpCleanUpFdo(fdoExt);
  695. }
  696. else {
  697. status = STATUS_DEVICE_CONFIGURATION_ERROR;
  698. }
  699. DBGSUCCESS(status, FALSE)
  700. return status;
  701. }
  702. /*
  703. ********************************************************************************
  704. * HidpRemoveCollection
  705. ********************************************************************************
  706. *
  707. */
  708. VOID HidpRemoveCollection(FDO_EXTENSION *fdoExt, PDO_EXTENSION *pdoExt, IN PIRP Irp)
  709. {
  710. PAGED_CODE();
  711. //
  712. // This pdo is no longer available as it has been removed.
  713. // It should still be returned for each Query Device Relations
  714. // IRPS to the HID bus, but it itself should respond to all
  715. // IRPS with STATUS_DELETE_PENDING.
  716. //
  717. if (pdoExt->prevState == COLLECTION_STATE_UNINITIALIZED || // for started pdos
  718. pdoExt->state == COLLECTION_STATE_UNINITIALIZED){ // For unstarted pdos
  719. pdoExt->state = COLLECTION_STATE_UNINITIALIZED;
  720. DBGVERBOSE(("HidpRemoveCollection: collection uninitialized."))
  721. }
  722. else {
  723. ULONG ctnIndx = pdoExt->collectionIndex;
  724. PHIDCLASS_COLLECTION collection = &fdoExt->classCollectionArray[ctnIndx];
  725. ULONG numReportIDs = fdoExt->deviceDesc.ReportIDsLength;
  726. PIRP remoteWakeIrp;
  727. if (!pdoExt->MouseOrKeyboard &&
  728. WAITWAKE_SUPPORTED(fdoExt)) {
  729. //
  730. // Unregister for remote wakeup.
  731. //
  732. IoWMIRegistrationControl (pdoExt->pdo, WMIREG_ACTION_DEREGISTER);
  733. }
  734. remoteWakeIrp = (PIRP)
  735. InterlockedExchangePointer(&pdoExt->remoteWakeIrp, NULL);
  736. if (remoteWakeIrp) {
  737. IoCancelIrp(remoteWakeIrp);
  738. }
  739. pdoExt->state = COLLECTION_STATE_UNINITIALIZED;
  740. /*
  741. * Destroy this collection.
  742. * This will also abort all pending reads on this collection-PDO.
  743. */
  744. HidpDestroyCollection(fdoExt, collection);
  745. }
  746. DBGVERBOSE(("HidpRemoveCollection: removed pdo %ph (refCount=%xh)", pdoExt->pdo, (ULONG)(*(((PUCHAR)pdoExt->pdo)-0x18))))
  747. }