Windows NT 4.0 source code leak
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.

1307 lines
41 KiB

4 years ago
  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. control.c
  5. Abstract:
  6. User-mode -> Kernel-mode PnP Manager control routines.
  7. Author:
  8. Lonny McMichael (lonnym) 02/14/95
  9. Revision History:
  10. --*/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. //
  14. // Global driver object that is used by calls to NtPlugPlayControl
  15. // with control type of PlugPlayControlDetectResourceConflict.
  16. //
  17. PDRIVER_OBJECT driverObject = NULL;
  18. #ifdef _PNP_POWER_
  19. //
  20. // Define the context structure for the PiDevicePathToServiceInstance
  21. // callback routine.
  22. //
  23. typedef struct _PI_DEVPATH_TO_SVCINST_CONTEXT {
  24. NTSTATUS ReturnStatus;
  25. PUNICODE_STRING DevicePath;
  26. UNICODE_STRING DeviceInstanceMatch;
  27. } PI_DEVPATH_TO_SVCINST_CONTEXT, *PPI_DEVPATH_TO_SVCINST_CONTEXT;
  28. //
  29. // Prototype utility functions internal to this file.
  30. //
  31. BOOLEAN
  32. PiDevicePathToServiceInstance(
  33. IN HANDLE DeviceInstanceHandle,
  34. IN PUNICODE_STRING DeviceInstancePath,
  35. IN OUT PVOID Context
  36. );
  37. #endif // _PNP_POWER_
  38. NTSTATUS
  39. PiGenerateLegacyDeviceInstance(
  40. IN PUNICODE_STRING ServiceKeyName,
  41. OUT PWSTR DeviceInstance,
  42. IN ULONG DeviceInstanceLength,
  43. OUT PULONG RequiredLength
  44. );
  45. NTSTATUS
  46. PiDetectResourceConflict(
  47. IN PCM_RESOURCE_LIST ResourceList,
  48. IN ULONG ResourceListSize
  49. );
  50. #ifdef ALLOC_PRAGMA
  51. #pragma alloc_text(PAGE, NtPlugPlayControl)
  52. #pragma alloc_text(PAGE, PiGenerateLegacyDeviceInstance)
  53. #pragma alloc_text(PAGE, PiDetectResourceConflict)
  54. #ifdef _PNP_POWER_
  55. #pragma alloc_text(PAGE, PiQueryRemoveDevice)
  56. #pragma alloc_text(PAGE, PiRemoveDevice)
  57. #pragma alloc_text(PAGE, PiCancelRemoveDevice)
  58. #pragma alloc_text(PAGE, PiAddDevice)
  59. #pragma alloc_text(PAGE, PiEjectDevice)
  60. #pragma alloc_text(PAGE, PiUnlockDevice)
  61. #pragma alloc_text(PAGE, PiQueryDeviceCapabilities)
  62. #pragma alloc_text(PAGE, PiGetDevicePathInformation)
  63. #endif // _PNP_POWER_
  64. #endif // ALLOC_PRAGMA
  65. NTSTATUS
  66. NtPlugPlayControl(
  67. IN PLUGPLAY_CONTROL_CLASS PnPControlClass,
  68. IN OUT PVOID PnPControlData,
  69. IN ULONG PnPControlDataLength,
  70. OUT PULONG RequiredLength OPTIONAL
  71. )
  72. /*++
  73. Routine Description:
  74. This Plug and Play Manager API provides a mechanism for the user-mode
  75. PnP Manager to control the activity of its kernel-mode counterpart.
  76. Arguments:
  77. PnPControlClass - Specifies what action to perform.
  78. PnPControlData - Supplies a pointer to data specific to this action.
  79. PnPControlDataLength - Specifies the size, in bytes, of the buffer pointed
  80. to by PnPControlData
  81. RequiredLength - Optional pointer to a variable the receives the actual
  82. size required to store the output data in
  83. PnPControlData.
  84. Return Value:
  85. NT status code indicating success or failure. Set of possible return
  86. values includes the following:
  87. STATUS_SUCCESS - normal, successful completion.
  88. STATUS_INVALID_PARAMETER_1 - The PnPControlClass parameter did not
  89. specify a valid control class.
  90. STATUS_INVALID_PARAMETER_MIX - The value of the PnPControlDataLength
  91. parameter did not match the length required for the control
  92. class requested by the PnPControlClass parameter.
  93. STATUS_BUFFER_TOO_SMALL - The size of the supplied output buffer is not
  94. large enough to hold the output generated by this control class.
  95. STATUS_ACCESS_VIOLATION - One of the following pointers specified
  96. an invalid address: (1) the PnPControlData buffer pointer,
  97. (2) some pointer contained in the PnPControlData buffer, or
  98. (3) the RequiredLength pointer
  99. STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources exist
  100. for this request to complete.
  101. --*/
  102. {
  103. NTSTATUS Status;
  104. KPROCESSOR_MODE PreviousMode;
  105. PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceControlData;
  106. PPLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA LegacyDevGenData;
  107. PPLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA DeviceResourceData;
  108. #ifdef _PNP_POWER_
  109. PPLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA DeviceCapabilitiesData;
  110. PPLUGPLAY_CONTROL_DEVICE_PATH_DATA DevicePathData;
  111. ULONG ReturnBufferSize;
  112. #endif
  113. try {
  114. //
  115. // Get previous processor mode and probe arguments if necessary.
  116. //
  117. PreviousMode = KeGetPreviousMode();
  118. if(PreviousMode != KernelMode) {
  119. ProbeForWrite(PnPControlData, PnPControlDataLength, sizeof(ULONG));
  120. if(ARGUMENT_PRESENT(RequiredLength)) {
  121. ProbeForWriteUlong(RequiredLength);
  122. }
  123. }
  124. switch(PnPControlClass) {
  125. case PlugPlayControlQueryRemoveDevice:
  126. case PlugPlayControlRemoveDevice:
  127. case PlugPlayControlCancelRemoveDevice:
  128. case PlugPlayControlAddDevice:
  129. case PlugPlayControlEjectDevice:
  130. case PlugPlayControlUnlockDevice:
  131. case PlugPlayControlEnumerateDevice:
  132. #ifndef _PNP_POWER_
  133. return STATUS_NOT_IMPLEMENTED;
  134. #endif
  135. case PlugPlayControlRegisterNewDevice:
  136. case PlugPlayControlDeregisterDevice:
  137. //
  138. // Validate buffer for all control classes using a
  139. // PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA structure.
  140. //
  141. if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)) {
  142. return STATUS_INVALID_PARAMETER_MIX;
  143. }
  144. DeviceControlData = (PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)PnPControlData;
  145. if(PreviousMode != KernelMode) {
  146. ProbeForRead(DeviceControlData->DeviceInstance.Buffer,
  147. DeviceControlData->DeviceInstance.Length,
  148. sizeof(WCHAR)
  149. );
  150. }
  151. //
  152. // Since this structure is a fixed size, store RequiredLength now, if necessary,
  153. // so we don't have to do it for each class separately.
  154. //
  155. if(ARGUMENT_PRESENT(RequiredLength)) {
  156. *RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA);
  157. }
  158. break;
  159. case PlugPlayControlQueryDeviceCapabilities:
  160. #ifndef _PNP_POWER_
  161. return STATUS_NOT_IMPLEMENTED;
  162. #else
  163. //
  164. // Validate buffer for all control classes using a
  165. // PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA structure.
  166. //
  167. if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA)) {
  168. return STATUS_INVALID_PARAMETER_MIX;
  169. }
  170. DeviceCapabilitiesData =
  171. (PPLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA)PnPControlData;
  172. if(PreviousMode != KernelMode) {
  173. ProbeForRead(DeviceCapabilitiesData->DeviceInstance.Buffer,
  174. DeviceCapabilitiesData->DeviceInstance.Length,
  175. sizeof(WCHAR)
  176. );
  177. }
  178. #endif
  179. break;
  180. case PlugPlayControlGetDevicePathInformation:
  181. #ifndef _PNP_POWER_
  182. return STATUS_NOT_IMPLEMENTED;
  183. #else
  184. //
  185. // Validate buffer for all control classes using a
  186. // PLUGPLAY_CONTROL_DEVICE_PATH_DATA structure.
  187. //
  188. if(PnPControlDataLength < sizeof(PLUGPLAY_CONTROL_DEVICE_PATH_DATA)) {
  189. return STATUS_INVALID_PARAMETER_MIX;
  190. }
  191. DevicePathData = (PPLUGPLAY_CONTROL_DEVICE_PATH_DATA)PnPControlData;
  192. if(PreviousMode != KernelMode) {
  193. ProbeForRead(DevicePathData->DevicePath.Buffer,
  194. DevicePathData->DevicePath.Length,
  195. sizeof(WCHAR)
  196. );
  197. }
  198. #endif
  199. break;
  200. case PlugPlayControlGenerateLegacyDevice:
  201. //
  202. // Validate buffer for all control classes using a
  203. // PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA structure.
  204. //
  205. if(PnPControlDataLength < sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA)) {
  206. return STATUS_INVALID_PARAMETER_MIX;
  207. }
  208. LegacyDevGenData = (PPLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA)PnPControlData;
  209. if(PreviousMode != KernelMode) {
  210. ProbeForRead(LegacyDevGenData->ServiceName.Buffer,
  211. LegacyDevGenData->ServiceName.Length,
  212. sizeof(WCHAR)
  213. );
  214. }
  215. break;
  216. case PlugPlayControlDetectResourceConflict:
  217. //
  218. // Determine whether resource list specified in buffer
  219. // is avaiable (not conflicting with other devices).
  220. //
  221. if(PnPControlDataLength != sizeof(PLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA)) {
  222. return STATUS_INVALID_PARAMETER_MIX;
  223. }
  224. DeviceResourceData = (PPLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA)PnPControlData;
  225. if(PreviousMode != KernelMode) {
  226. ProbeForRead(DeviceResourceData->DeviceInstance.Buffer,
  227. DeviceResourceData->DeviceInstance.Length,
  228. sizeof(WCHAR)
  229. );
  230. ProbeForRead(DeviceResourceData->ResourceList,
  231. DeviceResourceData->ResourceListSize,
  232. sizeof(UCHAR)
  233. );
  234. }
  235. //
  236. // Since this structure is a fixed size, store RequiredLength now, if necessary,
  237. // so we don't have to do it for each class separately.
  238. //
  239. if(ARGUMENT_PRESENT(RequiredLength)) {
  240. *RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_RESOURCE_DATA);
  241. }
  242. break;
  243. default:
  244. //
  245. // Invalid control class.
  246. //
  247. return STATUS_INVALID_PARAMETER_1;
  248. }
  249. //
  250. // Now invoke the proper routine for this control class.
  251. //
  252. switch(PnPControlClass) {
  253. case PlugPlayControlQueryRemoveDevice:
  254. #ifndef _PNP_POWER_
  255. return STATUS_NOT_IMPLEMENTED;
  256. #else
  257. Status = PiQueryRemoveDevice(&(DeviceControlData->DeviceInstance),
  258. &(DeviceControlData->Status)
  259. );
  260. #endif
  261. break;
  262. case PlugPlayControlRemoveDevice:
  263. #ifndef _PNP_POWER_
  264. return STATUS_NOT_IMPLEMENTED;
  265. #else
  266. Status = PiRemoveDevice(&(DeviceControlData->DeviceInstance),
  267. &(DeviceControlData->Status)
  268. );
  269. #endif
  270. break;
  271. case PlugPlayControlCancelRemoveDevice:
  272. #ifndef _PNP_POWER_
  273. return STATUS_NOT_IMPLEMENTED;
  274. #else
  275. Status = PiCancelRemoveDevice(&(DeviceControlData->DeviceInstance),
  276. &(DeviceControlData->Status)
  277. );
  278. #endif
  279. break;
  280. case PlugPlayControlAddDevice:
  281. #ifndef _PNP_POWER_
  282. return STATUS_NOT_IMPLEMENTED;
  283. #else
  284. Status = PiAddDevice(&(DeviceControlData->DeviceInstance),
  285. &(DeviceControlData->Status)
  286. );
  287. #endif
  288. break;
  289. case PlugPlayControlEjectDevice:
  290. #ifndef _PNP_POWER_
  291. return STATUS_NOT_IMPLEMENTED;
  292. #else
  293. Status = PiEjectDevice(&(DeviceControlData->DeviceInstance),
  294. &(DeviceControlData->Status)
  295. );
  296. #endif
  297. break;
  298. case PlugPlayControlUnlockDevice:
  299. #ifndef _PNP_POWER_
  300. return STATUS_NOT_IMPLEMENTED;
  301. #else
  302. Status = PiUnlockDevice(&(DeviceControlData->DeviceInstance),
  303. &(DeviceControlData->Status)
  304. );
  305. #endif
  306. break;
  307. case PlugPlayControlQueryDeviceCapabilities:
  308. #ifndef _PNP_POWER_
  309. return STATUS_NOT_IMPLEMENTED;
  310. #else
  311. //
  312. // BUGBUG (lonnym): need to add argument for slot capabilities once it
  313. // is defined by KenR.
  314. //
  315. Status = PiQueryDeviceCapabilities(&(DeviceCapabilitiesData->DeviceInstance)
  316. );
  317. if(ARGUMENT_PRESENT(RequiredLength)) {
  318. *RequiredLength = sizeof(PLUGPLAY_CONTROL_DEVICE_CAPABILITIES_DATA);
  319. }
  320. #endif
  321. break;
  322. case PlugPlayControlGetDevicePathInformation:
  323. #ifndef _PNP_POWER_
  324. return STATUS_NOT_IMPLEMENTED;
  325. #else
  326. Status = PiGetDevicePathInformation(
  327. &(DevicePathData->DevicePath),
  328. DevicePathData->ServiceName,
  329. PnPControlDataLength -
  330. FIELD_OFFSET(PLUGPLAY_CONTROL_DEVICE_PATH_DATA, ServiceName),
  331. &ReturnBufferSize,
  332. &(DevicePathData->ServiceNameLength),
  333. &(DevicePathData->DeviceInstanceOffset),
  334. &(DevicePathData->DeviceInstanceLength),
  335. &(DevicePathData->ServiceInstanceOrdinal)
  336. );
  337. if(ARGUMENT_PRESENT(RequiredLength)) {
  338. if(NT_SUCCESS(Status) || Status == STATUS_BUFFER_TOO_SMALL) {
  339. *RequiredLength = ReturnBufferSize +
  340. FIELD_OFFSET(PLUGPLAY_CONTROL_DEVICE_PATH_DATA, ServiceName);
  341. }
  342. }
  343. #endif
  344. break;
  345. case PlugPlayControlRegisterNewDevice:
  346. Status = PpDeviceRegistration(&DeviceControlData->DeviceInstance, TRUE);
  347. DeviceControlData->Status = Status;
  348. break;
  349. case PlugPlayControlDeregisterDevice:
  350. Status = PpDeviceRegistration(&DeviceControlData->DeviceInstance, FALSE);
  351. DeviceControlData->Status = Status;
  352. break;
  353. case PlugPlayControlEnumerateDevice:
  354. //
  355. // BUGBUG (lonnym): This is where a call to PiEnumerateDevice() should go.
  356. // This API was previously the NT API, NtEnumerateBus.
  357. //
  358. Status = STATUS_NOT_IMPLEMENTED;
  359. break;
  360. case PlugPlayControlGenerateLegacyDevice:
  361. Status = PiGenerateLegacyDeviceInstance(
  362. &LegacyDevGenData->ServiceName,
  363. LegacyDevGenData->DeviceInstance,
  364. PnPControlDataLength -
  365. FIELD_OFFSET(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA, DeviceInstance),
  366. &LegacyDevGenData->DeviceInstanceLength
  367. );
  368. if(ARGUMENT_PRESENT(RequiredLength)) {
  369. if(NT_SUCCESS(Status) || (Status == STATUS_BUFFER_TOO_SMALL)) {
  370. *RequiredLength = LegacyDevGenData->DeviceInstanceLength +
  371. sizeof(PLUGPLAY_CONTROL_LEGACY_DEVGEN_DATA);
  372. }
  373. }
  374. break;
  375. case PlugPlayControlDetectResourceConflict:
  376. Status = PiDetectResourceConflict(DeviceResourceData->ResourceList,
  377. DeviceResourceData->ResourceListSize);
  378. DeviceResourceData->Status = Status;
  379. break;
  380. }
  381. } except(EXCEPTION_EXECUTE_HANDLER) {
  382. Status = GetExceptionCode();
  383. }
  384. return Status;
  385. }
  386. NTSTATUS
  387. PiGenerateLegacyDeviceInstance(
  388. IN PUNICODE_STRING ServiceKeyName,
  389. OUT PWSTR DeviceInstance,
  390. IN ULONG DeviceInstanceLength,
  391. OUT PULONG RequiredLength
  392. )
  393. /*++
  394. Routine Description:
  395. This routine creates a new instance node under System\Enum\Root\LEGACY_<Name>
  396. key and all the required default value entries. Also a value entry under
  397. Service\ServiceKeyName\Enum is created to point to the newly created madeup
  398. entry. A handle and the keyname of the new key are returned to caller.
  399. Caller must free the unicode string when he is done with it.
  400. Arguments:
  401. ServiceKeyName - Supplies a pointer to the name of the subkey in the
  402. system service list (HKEY_LOCAL_MACHINE\CurrentControlSet\Services)
  403. that caused the driver to load.
  404. DeviceInstance - Supplies a pointer to the character buffer that receives the
  405. newly-generated device instance name.
  406. DeviceInstanceLength - Supplies the size, in bytes, of the DeviceInstance
  407. buffer.
  408. RequiredLength - Supplies a pointer to a variable that receives the size,
  409. in bytes (excluding terminating NULL) of the device instance name stored
  410. in the buffer.
  411. Return Value:
  412. A NTSTATUS code.
  413. If the Lengacy Device Instance exists already, this function returns sucessful.
  414. --*/
  415. {
  416. NTSTATUS status;
  417. HANDLE handle;
  418. ULONG junk;
  419. BOOLEAN isPlugPlayDriver = FALSE;
  420. PKEY_VALUE_FULL_INFORMATION keyValueInformation;
  421. UNICODE_STRING tempUnicodeString;
  422. KeEnterCriticalRegion();
  423. ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
  424. status = IopOpenServiceEnumKeys(ServiceKeyName,
  425. KEY_READ,
  426. &handle,
  427. NULL,
  428. TRUE
  429. );
  430. if (NT_SUCCESS(status)) {
  431. //
  432. // Check whether this madeup key should be created. It must be a legacy driver
  433. // to create the madeup key.
  434. //
  435. status = IopGetRegistryValue(handle, REGSTR_VALUE_PLUGPLAY_SERVICE_TYPE, &keyValueInformation);
  436. ZwClose(handle);
  437. if (NT_SUCCESS(status)) {
  438. if ((keyValueInformation->Type == REG_DWORD) &&
  439. (keyValueInformation->DataLength >= sizeof(ULONG))) {
  440. isPlugPlayDriver = TRUE;
  441. status = STATUS_INVALID_PARAMETER_2;
  442. }
  443. ExFreePool(keyValueInformation);
  444. }
  445. if (!isPlugPlayDriver) {
  446. status = IopCreateMadeupNode(ServiceKeyName,
  447. &handle,
  448. &tempUnicodeString,
  449. &junk,
  450. TRUE
  451. );
  452. if (NT_SUCCESS(status)) {
  453. //
  454. // We have successfully retrieved the newly-generated device instance name.
  455. // Now store it in the supplied buffer.
  456. //
  457. ZwClose(handle);
  458. *RequiredLength = tempUnicodeString.Length;
  459. if(DeviceInstanceLength >= tempUnicodeString.Length + sizeof(UNICODE_NULL)) {
  460. try {
  461. RtlMoveMemory(DeviceInstance,
  462. tempUnicodeString.Buffer,
  463. tempUnicodeString.Length + sizeof(UNICODE_NULL)
  464. );
  465. } except(EXCEPTION_EXECUTE_HANDLER) {
  466. status = GetExceptionCode();
  467. }
  468. } else {
  469. status = STATUS_BUFFER_TOO_SMALL;
  470. }
  471. RtlFreeUnicodeString(&tempUnicodeString);
  472. }
  473. }
  474. }
  475. ExReleaseResource(&PpRegistryDeviceResource);
  476. KeLeaveCriticalRegion();
  477. return status;
  478. }
  479. #ifdef _PNP_POWER_
  480. NTSTATUS
  481. PiQueryRemoveDevice(
  482. IN PUNICODE_STRING DeviceInstance,
  483. OUT PNTSTATUS ReturnedStatus
  484. )
  485. /*++
  486. Routine Description:
  487. This routine queries a device driver for whether a particular device
  488. instance can be removed.
  489. Arguments:
  490. DeviceInstance - Supplies the path in the registry (relative to
  491. HKLM\System\Enum) to the device instance being query-removed.
  492. ReturnedStatus - If the function is successful, this receives the
  493. NT status code returned by the driver in response to the
  494. query-remove.
  495. Return Value:
  496. NT status code indicating success or failure of this routine. If
  497. STATUS_SUCCESS, then ReturnedStatus must be checked to determine
  498. the driver's response to the request.
  499. --*/
  500. {
  501. return STATUS_NOT_IMPLEMENTED;
  502. }
  503. NTSTATUS
  504. PiRemoveDevice(
  505. IN PUNICODE_STRING DeviceInstance,
  506. OUT PNTSTATUS ReturnedStatus
  507. )
  508. /*++
  509. Routine Description:
  510. This routine causes a device driver to remove a particular device
  511. instance from the system.
  512. Arguments:
  513. DeviceInstance - Supplies the path in the registry (relative to
  514. HKLM\System\Enum) to the device instance to be removed.
  515. ReturnedStatus - If the function is successful, this receives the
  516. NT status code returned by the driver in response to the
  517. remove request.
  518. Return Value:
  519. NT status code indicating success or failure of this routine. If
  520. STATUS_SUCCESS, then ReturnedStatus must be checked to determine
  521. the driver's response to the request.
  522. --*/
  523. {
  524. return STATUS_NOT_IMPLEMENTED;
  525. }
  526. NTSTATUS
  527. PiCancelRemoveDevice(
  528. IN PUNICODE_STRING DeviceInstance,
  529. OUT PNTSTATUS ReturnedStatus
  530. )
  531. /*++
  532. Routine Description:
  533. This routine cancels a previously-submitted request to query-remove
  534. a device. It should only be called if the device instance was
  535. previously successfully query-removed.
  536. Arguments:
  537. DeviceInstance - Supplies the path in the registry (relative to
  538. HKLM\System\Enum) to the device instance for which a remove
  539. request is to be cancelled.
  540. ReturnedStatus - If the function is successful, this receives the
  541. NT status code returned by the driver in response to the
  542. remove cancel request.
  543. Return Value:
  544. NT status code indicating success or failure of this routine. If
  545. STATUS_SUCCESS, then ReturnedStatus must be checked to determine
  546. the driver's response to the request.
  547. --*/
  548. {
  549. return STATUS_NOT_IMPLEMENTED;
  550. }
  551. NTSTATUS
  552. PiAddDevice(
  553. IN PUNICODE_STRING DeviceInstance,
  554. OUT PNTSTATUS ReturnedStatus
  555. )
  556. /*++
  557. Routine Description:
  558. This routine causes a device driver to add a new device instance
  559. (i.e., create a new device object).
  560. Arguments:
  561. DeviceInstance - Supplies the path in the registry (relative to
  562. HKLM\System\Enum) to the new device instance to be added.
  563. ReturnedStatus - If the function is successful, this receives the
  564. NT status code returned by the driver in response to the
  565. add device request.
  566. Return Value:
  567. NT status code indicating success or failure of this routine. If
  568. STATUS_SUCCESS, then ReturnedStatus must be checked to determine
  569. the driver's response to the request.
  570. --*/
  571. {
  572. return STATUS_NOT_IMPLEMENTED;
  573. }
  574. NTSTATUS
  575. PiEjectDevice(
  576. IN PUNICODE_STRING DeviceInstance,
  577. OUT PNTSTATUS ReturnedStatus
  578. )
  579. /*++
  580. Routine Description:
  581. This routine causes the specified device instance to be ejected (if
  582. the device is in a slot that supports soft-eject).
  583. Arguments:
  584. DeviceInstance - Supplies the path in the registry (relative to
  585. HKLM\System\Enum) to the device instance to be ejected.
  586. ReturnedStatus - If the function is successful, this receives the
  587. NT status code returned by the HAL bus extender that controls
  588. the bus where this device is located.
  589. Return Value:
  590. NT status code indicating success or failure of this routine. If
  591. STATUS_SUCCESS, then ReturnedStatus must be checked to determine
  592. the HAL bus extender's response to the request.
  593. --*/
  594. {
  595. return STATUS_NOT_IMPLEMENTED;
  596. }
  597. NTSTATUS
  598. PiUnlockDevice(
  599. IN PUNICODE_STRING DeviceInstance,
  600. OUT PNTSTATUS ReturnedStatus
  601. )
  602. /*++
  603. Routine Description:
  604. This routine causes the specified device instance to be unlocked
  605. for removal (if the device is in a slot that supports slot locking).
  606. Arguments:
  607. DeviceInstance - Supplies the path in the registry (relative to
  608. HKLM\System\Enum) to the device instance to be unlocked.
  609. ReturnedStatus - If the function is successful, this receives the
  610. NT status code returned by the HAL bus extender that controls
  611. the bus where this device is located.
  612. Return Value:
  613. NT status code indicating success or failure of this routine. If
  614. STATUS_SUCCESS, then ReturnedStatus must be checked to determine
  615. the HAL bus extender's response to the request.
  616. --*/
  617. {
  618. return STATUS_NOT_IMPLEMENTED;
  619. }
  620. //
  621. // BUGBUG (lonnym): un-comment the 2nd parameter to the following
  622. // routine once KenR defines the SLOT_CAPABILITIES structure.
  623. //
  624. NTSTATUS
  625. PiQueryDeviceCapabilities(
  626. IN PUNICODE_STRING DeviceInstance
  627. // ,OUT PSLOT_CAPABILITIES Capabilities
  628. )
  629. /*++
  630. Routine Description:
  631. This routine returns the capabilities of a particular device (e.g.,
  632. is the device soft-ejectable?).
  633. (Note that while these capabilities are really an attribute of the
  634. particular bus slot on which the device resides, the user-mode PnP
  635. manager does not track bus slots--only devices. Therefore, from
  636. user-mode, these attributes are associated with a device instance,
  637. and the kernel-mode PnP manager makes the association between device
  638. instances and actual bus slots.)
  639. Arguments:
  640. DeviceInstance - Supplies the path in the registry (relative to
  641. HKLM\System\Enum) to the device instance whose capabilities
  642. are to be determined.
  643. Capabilities - Pointer to a buffer that receives the capabilities of
  644. the slot in which this device instance is located.
  645. Return Value:
  646. NT status code indicating success or failure of this routine.
  647. --*/
  648. {
  649. return STATUS_NOT_IMPLEMENTED;
  650. }
  651. NTSTATUS
  652. PiGetDevicePathInformation(
  653. IN PUNICODE_STRING DevicePath,
  654. OUT PWCHAR ServiceName,
  655. IN ULONG BufferLength,
  656. OUT PULONG ReturnLength,
  657. OUT PULONG ServiceNameLength,
  658. OUT PULONG DeviceInstanceOffset,
  659. OUT PULONG DeviceInstanceLength,
  660. OUT PULONG ServiceInstanceOrdinal OPTIONAL
  661. )
  662. /*++
  663. Routine Description:
  664. This routine takes as input an NT device path, and returns the
  665. corresponding service name and (if possible) device instance it
  666. is associated with. It also optionally returns the device instance's
  667. service ordinal as listed under the service entry's volatile Enum subkey.
  668. Arguments:
  669. DevicePath - Supplies the NT device path
  670. ServiceName - Pointer to a character buffer that receives both
  671. the ServiceName _and_ DeviceInstance strings. The
  672. ServiceName will be stored at the beginning of the buffer,
  673. and the DeviceInstance string (if applicable) will be stored
  674. at offset DeviceInstanceOffset in the buffer. Both strings will
  675. be NULL-terminated.
  676. BufferLength - Supplies the length, in bytes, of the buffer pointed
  677. to by ServiceName.
  678. ReturnLength - Receives the size, in bytes, required to store
  679. both strings in the ServiceName buffer. If the buffer isn't
  680. large enough, then no data will be stored in it, and this value
  681. will indicate the size necessary to store the data.
  682. ServiceNameLength - Receives the length, in bytes, of the ServiceName
  683. string stored in the ServiceName buffer (not including terminating
  684. NULL).
  685. DeviceInstanceOffset - If applicable, this value will receive the offset
  686. from the beginning of the ServiceName buffer (in characters) where the
  687. DeviceInstance string is located. If no device instance is associated
  688. with this device path (e.g., the device path was created by a legacy
  689. driver), then this value will be set to zero.
  690. DeviceInstanceLength - Receives the length, in bytes, of the DeviceInstance
  691. string stored in the ServiceName buffer (not including terminating
  692. NULL). If there is no associated device instance, then this value will
  693. be set to zero.
  694. ServiceInstanceOrdinal - If specified, receives the ordinal of the
  695. device instance within the service's volatile Enum list. If there is no
  696. associated device instance (i.e., DeviceInstanceOffset and
  697. DeviceInstanceLength are zero), then this value is set to PLUGPLAY_NO_INSTANCE.
  698. Return Value:
  699. NT status code indicating success or failure of this routine.
  700. --*/
  701. {
  702. NTSTATUS Status;
  703. PFILE_OBJECT FileObject;
  704. PUNICODE_STRING DriverServiceName;
  705. HANDLE ServiceEnumHandle;
  706. PI_DEVPATH_TO_SVCINST_CONTEXT DevPathToSvcInstContext;
  707. ULONG ReturnedServiceOrdinal, DevInstStringLength;
  708. //
  709. // Get a pointer to a file object for the specified device so that we can
  710. // retrieve the controlling service name from its driver object.
  711. //
  712. Status = PiGetDeviceObjectFilePointer(DevicePath,
  713. &FileObject
  714. );
  715. if(!NT_SUCCESS(Status)) {
  716. return Status;
  717. }
  718. DriverServiceName = &(FileObject->DeviceObject->DriverObject->DriverExtension->ServiceKeyName);
  719. //
  720. // The driver object may not have an associated service name, so only search
  721. // for a service instance if it does.
  722. //
  723. if(DriverServiceName->Length) {
  724. //
  725. // Now search through each device instance listed under the service
  726. // entry's volatile Enum subkey, looking for a match in one of the
  727. // NtDevicePaths REG_MULTI_SZ value entries.
  728. //
  729. DevPathToSvcInstContext.ReturnStatus = STATUS_SUCCESS;
  730. DevPathToSvcInstContext.DevicePath = DevicePath;
  731. RtlInitUnicodeString(&(DevPathToSvcInstContext.DeviceInstanceMatch), NULL);
  732. //
  733. // We need to acquire the PnP device registry resource for shared (read) access.
  734. //
  735. KeEnterCriticalRegion();
  736. ExAcquireResourceShared(&PpRegistryDeviceResource, TRUE);
  737. Status = IopApplyFunctionToServiceInstances(NULL,
  738. DriverServiceName,
  739. KEY_READ,
  740. TRUE,
  741. PiDevicePathToServiceInstance,
  742. &DevPathToSvcInstContext,
  743. &ReturnedServiceOrdinal
  744. );
  745. ExReleaseResource(&PpRegistryDeviceResource);
  746. KeLeaveCriticalRegion();
  747. if(NT_SUCCESS(Status) &&
  748. NT_SUCCESS(Status = DevPathToSvcInstContext.ReturnStatus)) {
  749. DevInstStringLength = DevPathToSvcInstContext.DeviceInstanceMatch.Length;
  750. } else {
  751. goto PrepareForReturn1;
  752. }
  753. } else {
  754. DevInstStringLength = 0;
  755. }
  756. //
  757. // Now we know the size of the string buffer required, so check to make sure
  758. // the buffer we were given is large enough, and if so, fill it with the
  759. // string(s).
  760. //
  761. *ReturnLength = DriverServiceName->Length + DevInstStringLength
  762. + ((DevInstStringLength) ? 2 : 1) * sizeof(WCHAR);
  763. if(BufferLength < *ReturnLength) {
  764. Status = STATUS_BUFFER_TOO_SMALL;
  765. goto PrepareForReturn2;
  766. }
  767. *ServiceNameLength = DriverServiceName->Length;
  768. if(*ServiceNameLength) {
  769. RtlMoveMemory(ServiceName,
  770. DriverServiceName->Buffer,
  771. *ServiceNameLength
  772. );
  773. }
  774. ServiceName[CB_TO_CWC(*ServiceNameLength)] = UNICODE_NULL;
  775. if(*DeviceInstanceLength = DevInstStringLength) {
  776. RtlMoveMemory(&(ServiceName[*DeviceInstanceOffset = CB_TO_CWC(*ServiceNameLength) + 1]),
  777. DevPathToSvcInstContext.DeviceInstanceMatch.Buffer,
  778. *DeviceInstanceLength
  779. );
  780. ServiceName[*DeviceInstanceOffset + CB_TO_CWC(*DeviceInstanceLength)] = UNICODE_NULL;
  781. } else {
  782. *DeviceInstanceOffset = 0;
  783. }
  784. if(ARGUMENT_PRESENT(ServiceInstanceOrdinal)) {
  785. *ServiceInstanceOrdinal = DevInstStringLength ? ReturnedServiceOrdinal
  786. : PLUGPLAY_NO_INSTANCE;
  787. }
  788. PrepareForReturn2:
  789. if(DevInstStringLength) {
  790. ExFreePool(DevPathToSvcInstContext.DeviceInstanceMatch.Buffer);
  791. }
  792. PrepareForReturn1:
  793. ObDereferenceObject(FileObject);
  794. return Status;
  795. }
  796. BOOLEAN
  797. PiDevicePathToServiceInstance(
  798. IN HANDLE DeviceInstanceHandle,
  799. IN PUNICODE_STRING DeviceInstancePath,
  800. IN OUT PVOID Context
  801. )
  802. /*++
  803. Routine Description:
  804. This routine is a callback function for IopApplyFunctionToServiceInstances.
  805. It is called for each device instance key referenced by a service instance
  806. value under the specified service's volatile Enum subkey. Its purpose is to
  807. determine whether this device instance corresponds to a specified NT device
  808. path (as registered in the device instance's NtDevicePaths REG_MULTI_SZ list).
  809. NOTE: The PnP device-specific registry resource must be acquired for shared
  810. (read) access before invoking this routine.
  811. Arguments:
  812. DeviceInstanceHandle - Supplies a handle to the current device instance key.
  813. The access to this key is that specified in the call to
  814. IopApplyFunctionToServiceInstances.
  815. DeviceInstancePath - Supplies the registry path (relative to HKLM\System\Enum)
  816. to this device instance.
  817. Context - Supplies a pointer to a PI_DEVPATH_TO_SVCINST_CONTEXT structure with
  818. the following fields:
  819. NTSTATUS ReturnStatus - Fill this in with the NT error status code if an
  820. error occurs. This is assumed to be initialized to STATUS_SUCCESS
  821. when this routine is called.
  822. PUNICODE_STRING DevicePath - Supplies the NT device path that we're looking
  823. for.
  824. UNICODE_STRING DeviceInstanceMatch - If the current device instance corresponds
  825. to the specified NT device path, then fill this unicode string in with
  826. the device instance path. The caller is responsible for freeing the
  827. (PagedPool) memory allocated for the unicode string buffer.
  828. Return Value:
  829. TRUE to continue the enumeration.
  830. FALSE to abort it. If the current device instance corresponds to the NT device
  831. path being searched for, this function should return FALSE to terminate the
  832. search.
  833. --*/
  834. {
  835. PPI_DEVPATH_TO_SVCINST_CONTEXT DevPathToSvcInstContext;
  836. NTSTATUS Status;
  837. PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
  838. PUNICODE_STRING DevicePathList;
  839. ULONG DevicePathCount, i;
  840. DevPathToSvcInstContext = (PPI_DEVPATH_TO_SVCINST_CONTEXT)Context;
  841. //
  842. // Retrieve the NtDevicePath REG_MULTI_SZ list from the device instance key.
  843. //
  844. Status = IopGetRegistryValue(DeviceInstanceHandle,
  845. REGSTR_VALUE_NTDEVICEPATHS,
  846. &KeyValueInformation
  847. );
  848. if(!NT_SUCCESS(Status)) {
  849. //
  850. // Ignore this device instance and continue search
  851. //
  852. return TRUE;
  853. }
  854. Status = IopRegMultiSzToUnicodeStrings(KeyValueInformation,
  855. &DevicePathList,
  856. &DevicePathCount
  857. );
  858. ExFreePool(KeyValueInformation);
  859. if(!NT_SUCCESS(Status)) {
  860. //
  861. // An error here is most likely STATUS_INSUFFICIENT_RESOURCES, which is
  862. // severe enough to abort the search.
  863. //
  864. DevPathToSvcInstContext->ReturnStatus = Status;
  865. return FALSE;
  866. }
  867. //
  868. // Now, search the device path list, looking for a match.
  869. //
  870. for(i = 0; i < DevicePathCount; i++) {
  871. if(RtlEqualUnicodeString(&(DevicePathList[i]),
  872. DevPathToSvcInstContext->DevicePath,
  873. TRUE)) {
  874. //
  875. // We found a match, so store a copy of the device instance path string
  876. // in the DeviceInstanceMatch field of the context structure.
  877. //
  878. if(!IopConcatenateUnicodeStrings(&(DevPathToSvcInstContext->DeviceInstanceMatch),
  879. DeviceInstancePath,
  880. NULL
  881. )) {
  882. DevPathToSvcInstContext->ReturnStatus = STATUS_INSUFFICIENT_RESOURCES;
  883. }
  884. break;
  885. }
  886. }
  887. IopFreeUnicodeStringList(DevicePathList, DevicePathCount);
  888. return (i == DevicePathCount) ? TRUE : FALSE;
  889. }
  890. #endif // _PNP_POWER_
  891. NTSTATUS
  892. PiDetectResourceConflict(
  893. IN PCM_RESOURCE_LIST ResourceList,
  894. IN ULONG ResourceListSize
  895. )
  896. /*++
  897. Routine Description:
  898. This routine is invoked to test whether the specified resource
  899. list conflicts with any already assigned resources.
  900. Arguments:
  901. ResourceList - Specifies a resource list buffer.
  902. ResourceListSize - Specifies the size of the resource list buffer.
  903. Return Value:
  904. The function value is an NTSTATUS value; STATUS_SUCCESS indicates
  905. that the resources do not conflict, STATUS_INSUFFICIENT_RESOURCES
  906. indicates that the resource conflict with already assigned
  907. resources (or some other NTSTATUS value may indicate a different
  908. internal error).
  909. --*/
  910. {
  911. OBJECT_ATTRIBUTES objectAttributes;
  912. HANDLE handle;
  913. PWSTR buffer;
  914. NTSTATUS status;
  915. UNICODE_STRING DriverName;
  916. ULONG i;
  917. BOOLEAN bConflictDetected = FALSE, bTemp;
  918. CM_RESOURCE_LIST EmptyResourceList;
  919. if (driverObject == NULL) {
  920. //
  921. // Driver object has not been created yet, do that now.
  922. //
  923. RtlInitUnicodeString(&DriverName, L"\\Device\\PlugPlay");
  924. //
  925. // Begin by creating the permanent driver object.
  926. //
  927. InitializeObjectAttributes(&objectAttributes,
  928. &DriverName,
  929. OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
  930. (HANDLE)NULL,
  931. (PSECURITY_DESCRIPTOR)NULL);
  932. //
  933. // Specify "KernelMode" here since it refers to the source of
  934. // the objectAttributes buffer, not the previous operating system
  935. // mode.
  936. //
  937. status = ObCreateObject(KernelMode,
  938. IoDriverObjectType,
  939. &objectAttributes,
  940. KernelMode,
  941. (PVOID)NULL,
  942. (ULONG)(sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION)),
  943. 0,
  944. 0,
  945. (PVOID)&driverObject);
  946. if (!NT_SUCCESS(status)) {
  947. return status;
  948. }
  949. //
  950. // Initialize the driver object.
  951. //
  952. RtlZeroMemory(driverObject,
  953. sizeof(DRIVER_OBJECT) + sizeof(DRIVER_EXTENSION));
  954. driverObject->DriverExtension = (PDRIVER_EXTENSION)(driverObject + 1);
  955. driverObject->DriverExtension->DriverObject = driverObject;
  956. for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++) {
  957. driverObject->MajorFunction[i] = NULL; // OK???
  958. }
  959. driverObject->Type = IO_TYPE_DRIVER;
  960. driverObject->Size = sizeof(DRIVER_OBJECT);
  961. driverObject->DriverInit = NULL;
  962. //
  963. // Insert the driver object into the object table.
  964. //
  965. status = ObInsertObject(driverObject,
  966. NULL,
  967. FILE_READ_DATA,
  968. 0,
  969. (PVOID *)NULL,
  970. &handle);
  971. if (!NT_SUCCESS(status)) {
  972. ObMakeTemporaryObject(driverObject); //?
  973. ObDereferenceObject(driverObject); //?
  974. return status;
  975. }
  976. //
  977. // Save the name of the driver so that it can be easily located by functions
  978. // such as error logging.
  979. //
  980. buffer = ExAllocatePool(PagedPool, DriverName.MaximumLength + 2);
  981. if (buffer) {
  982. driverObject->DriverName.Buffer = buffer;
  983. driverObject->DriverName.MaximumLength = DriverName.MaximumLength;
  984. driverObject->DriverName.Length = DriverName.Length;
  985. RtlCopyMemory(driverObject->DriverName.Buffer,
  986. DriverName.Buffer,
  987. DriverName.MaximumLength);
  988. buffer[DriverName.Length >> 1] = (WCHAR) '\0';
  989. }
  990. }
  991. //
  992. // Attempt to acquire the resource, if successful, we know the
  993. // resource is avaiable, overwise assume it conflicts with another
  994. // devices resource's.
  995. //
  996. status = IoReportResourceUsage(NULL,
  997. driverObject,
  998. ResourceList,
  999. ResourceListSize,
  1000. NULL,
  1001. NULL,
  1002. 0,
  1003. TRUE,
  1004. &bConflictDetected);
  1005. if (NT_SUCCESS(status)) {
  1006. //
  1007. // Clear any resources that might have been assigned to my fake device.
  1008. //
  1009. RtlZeroMemory(&EmptyResourceList, sizeof(CM_RESOURCE_LIST));
  1010. IoReportResourceUsage(NULL,
  1011. driverObject,
  1012. &EmptyResourceList,
  1013. sizeof(CM_RESOURCE_LIST),
  1014. NULL,
  1015. NULL,
  1016. 0,
  1017. TRUE,
  1018. &bTemp);
  1019. }
  1020. if (NT_SUCCESS(status) && bConflictDetected) {
  1021. status = STATUS_INSUFFICIENT_RESOURCES;
  1022. }
  1023. return status;
  1024. }