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.

924 lines
22 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. utils.c
  5. Abstract:
  6. This module provides general utility functions.
  7. Author:
  8. Andy Thornton (andrewth) 20-Oct-97
  9. Revision History:
  10. --*/
  11. #include "mfp.h"
  12. VOID
  13. MfInitCommonExtension(
  14. PMF_COMMON_EXTENSION Common,
  15. MF_OBJECT_TYPE Type
  16. );
  17. NTSTATUS
  18. MfGetSubkeyByIndex(
  19. IN HANDLE ParentHandle,
  20. IN ULONG Index,
  21. IN ACCESS_MASK Access,
  22. OUT PHANDLE ChildHandle,
  23. OUT PUNICODE_STRING Name
  24. );
  25. NTSTATUS
  26. MfGetRegistryValue(
  27. IN HANDLE Handle,
  28. IN PWSTR Name,
  29. IN ULONG Type,
  30. IN ULONG Flags,
  31. IN OUT PULONG DataLength,
  32. IN OUT PVOID *Data
  33. );
  34. NTSTATUS
  35. MfSendPnpIrp(
  36. IN PDEVICE_OBJECT DeviceObject,
  37. IN PIO_STACK_LOCATION Location,
  38. OUT PULONG_PTR Information OPTIONAL
  39. );
  40. DEVICE_POWER_STATE
  41. MfFindLowestChildPowerState(
  42. IN PMF_PARENT_EXTENSION Parent
  43. );
  44. NTSTATUS
  45. MfPowerRequestCompletion(
  46. IN PDEVICE_OBJECT DeviceObject,
  47. IN UCHAR MinorFunction,
  48. IN POWER_STATE PowerState,
  49. IN PVOID Context,
  50. IN PIO_STATUS_BLOCK IoStatus
  51. );
  52. NTSTATUS
  53. MfSendSetPowerIrp(
  54. IN PDEVICE_OBJECT Target,
  55. IN POWER_STATE State
  56. );
  57. #ifdef ALLOC_PRAGMA
  58. // NOTE: Should see if the rest of the funcs can be moved out of this
  59. // file.
  60. #pragma alloc_text(PAGE, MfInitCommonExtension)
  61. #pragma alloc_text(PAGE, MfGetSubkeyByIndex)
  62. #pragma alloc_text(PAGE, MfGetRegistryValue)
  63. #pragma alloc_text(PAGE, MfSendPnpIrp)
  64. #endif
  65. VOID
  66. MfInitCommonExtension(
  67. PMF_COMMON_EXTENSION Common,
  68. MF_OBJECT_TYPE Type
  69. )
  70. /*++
  71. Routine Description:
  72. This initializes the fields in the common header of the device extension
  73. Arguments:
  74. Common - The common header to initialize
  75. Type - The type of the object being initialized (ie PDO or FDO)
  76. Return Value:
  77. None
  78. --*/
  79. {
  80. Common->Type = Type;
  81. }
  82. VOID
  83. MfFreeDeviceInfo(
  84. PMF_DEVICE_INFO Info
  85. )
  86. {
  87. if (Info->Name.Buffer) {
  88. ExFreePool(Info->Name.Buffer);
  89. Info->Name.Buffer = NULL;
  90. }
  91. if (Info->HardwareID.Buffer) {
  92. ExFreePool(Info->HardwareID.Buffer);
  93. Info->HardwareID.Buffer = NULL;
  94. }
  95. if (Info->CompatibleID.Buffer) {
  96. ExFreePool(Info->CompatibleID.Buffer);
  97. Info->CompatibleID.Buffer = NULL;
  98. }
  99. if (Info->ResourceMap) {
  100. ExFreePool(Info->ResourceMap);
  101. Info->ResourceMap = NULL;
  102. }
  103. if (Info->VaryingResourceMap) {
  104. ExFreePool(Info->VaryingResourceMap);
  105. Info->VaryingResourceMap = NULL;
  106. }
  107. }
  108. NTSTATUS
  109. MfGetSubkeyByIndex(
  110. IN HANDLE ParentHandle,
  111. IN ULONG Index,
  112. IN ACCESS_MASK Access,
  113. OUT PHANDLE ChildHandle,
  114. OUT PUNICODE_STRING Name
  115. )
  116. /*++
  117. Routine Description:
  118. This returns the name and a handle to a subkey given that keys index
  119. Arguments:
  120. ParentHandle - The handle of the key the subkeys are located under
  121. Index - The index of the subkey required
  122. Access - The type of access required to the subkey
  123. ChildHandle - On success contains a handle to the subkey
  124. Name - On success contains the name of the subkey
  125. Return Value:
  126. Status code that indicates whether or not the function was successful.
  127. --*/
  128. {
  129. #define INFO_BUFFER_SIZE sizeof(KEY_BASIC_INFORMATION) + 255*sizeof(WCHAR)
  130. NTSTATUS status;
  131. UCHAR buffer[INFO_BUFFER_SIZE];
  132. PKEY_BASIC_INFORMATION info = (PKEY_BASIC_INFORMATION) buffer;
  133. ULONG resultSize;
  134. HANDLE childHandle;
  135. UNICODE_STRING string = {0};
  136. OBJECT_ATTRIBUTES attributes;
  137. status = ZwEnumerateKey(ParentHandle,
  138. Index,
  139. KeyBasicInformation,
  140. info,
  141. INFO_BUFFER_SIZE,
  142. &resultSize
  143. );
  144. if (!NT_SUCCESS(status)) {
  145. goto cleanup;
  146. }
  147. //
  148. // Copy the name
  149. //
  150. ASSERT(info->NameLength <= MAXUSHORT);
  151. string.Length = (USHORT) info->NameLength;
  152. string.MaximumLength = (USHORT) info->NameLength;
  153. string.Buffer = ExAllocatePoolWithTag(PagedPool,
  154. info->NameLength,
  155. MF_POOL_TAG
  156. );
  157. if (!string.Buffer) {
  158. status = STATUS_INSUFFICIENT_RESOURCES;
  159. goto cleanup;
  160. }
  161. RtlCopyMemory(string.Buffer, info->Name, info->NameLength);
  162. //
  163. // Open the name to get a handle
  164. //
  165. InitializeObjectAttributes(&attributes,
  166. &string,
  167. 0, //Attributes
  168. ParentHandle,
  169. NULL //SecurityDescriptoy
  170. );
  171. status = ZwOpenKey(&childHandle,
  172. Access,
  173. &attributes
  174. );
  175. if (!NT_SUCCESS(status)) {
  176. goto cleanup;
  177. }
  178. DEBUG_MSG(1, ("\tSubkey %wZ\n", &string));
  179. //
  180. // Hand the name back to the caller
  181. //
  182. Name->Buffer = string.Buffer;
  183. Name->Length = string.Length;
  184. Name->MaximumLength = string.MaximumLength;
  185. *ChildHandle = childHandle;
  186. return STATUS_SUCCESS;
  187. cleanup:
  188. if (string.Buffer) {
  189. ExFreePool(string.Buffer);
  190. }
  191. //
  192. // We should never be able to overflow as our buffer is the max size of
  193. // a registry key name
  194. //
  195. ASSERT(status != STATUS_BUFFER_OVERFLOW);
  196. return status;
  197. }
  198. NTSTATUS
  199. MfGetRegistryValue(
  200. IN HANDLE Handle,
  201. IN PWSTR Name,
  202. IN ULONG Type,
  203. IN ULONG Flags,
  204. IN OUT PULONG DataLength,
  205. IN OUT PVOID *Data
  206. )
  207. /*++
  208. Routine Description:
  209. This retrieves a value key from the registry performing type and sanity
  210. checking
  211. Arguments:
  212. Handle - The key the values are located under
  213. Name - The name of the value
  214. Type - The type (REG_*) of the value
  215. DataLength - Points to the length of the data buffer, on success contains
  216. the size of the data
  217. Data - Pointer to pointer to the buffer to return the data in, if points to
  218. NULL a buffer of the right size should be allocated (in this case
  219. DataLength should be 0)
  220. Return Value:
  221. Status code that indicates whether or not the function was successful. If
  222. the type of the object is not Type then we fail with STATUS_OBJECT_TYPE_MISMATCH
  223. --*/
  224. {
  225. #define VALUE_BUFFER_SIZE PAGE_SIZE
  226. NTSTATUS status;
  227. PKEY_VALUE_PARTIAL_INFORMATION info = NULL;
  228. ULONG size = VALUE_BUFFER_SIZE, length;
  229. UNICODE_STRING string;
  230. BOOLEAN convertSzToMultiSz;
  231. PWCHAR stringEnd, remainingBufferPtr;
  232. SIZE_T stringLength, sizeRemaining;
  233. ULONG remainingBufferLength;
  234. PAGED_CODE();
  235. //
  236. // Check parameters.
  237. // For all types but REG_DWORD, which has special rules, if the caller
  238. // provided a buffer, make sure he also provided the size of the buffer.
  239. //
  240. if ((Type != REG_DWORD) &&
  241. (*Data && (!DataLength || !(*DataLength)))) {
  242. ASSERT(FALSE);
  243. status = STATUS_INVALID_PARAMETER;
  244. goto cleanup;
  245. }
  246. RtlInitUnicodeString(&string, Name);
  247. info = ExAllocatePoolWithTag(PagedPool,
  248. VALUE_BUFFER_SIZE,
  249. MF_POOL_TAG
  250. );
  251. if (!info) {
  252. status = STATUS_INSUFFICIENT_RESOURCES;
  253. goto cleanup;
  254. }
  255. while ((status = ZwQueryValueKey(Handle,
  256. &string,
  257. KeyValuePartialInformation,
  258. info,
  259. size,
  260. &size
  261. )) == STATUS_BUFFER_OVERFLOW) {
  262. ExFreePool(info);
  263. info = ExAllocatePoolWithTag(PagedPool,
  264. size,
  265. MF_POOL_TAG
  266. );
  267. if (!info) {
  268. status = STATUS_INSUFFICIENT_RESOURCES;
  269. goto cleanup;
  270. }
  271. }
  272. if (!NT_SUCCESS(status)) {
  273. goto cleanup;
  274. }
  275. convertSzToMultiSz = (Type == REG_MULTI_SZ
  276. && info->Type == REG_SZ
  277. && Flags & MF_GETREG_SZ_TO_MULTI_SZ
  278. );
  279. //
  280. // Make sure the type we got back is what we expected
  281. //
  282. if (info->Type != Type && !convertSzToMultiSz) {
  283. status = STATUS_OBJECT_TYPE_MISMATCH;
  284. goto cleanup;
  285. }
  286. //
  287. // Apply various common sense checks based on the type.
  288. // At the same time, compute the size of the buffer needed
  289. // to store the data.
  290. //
  291. if (info->Type == REG_DWORD) {
  292. //
  293. // If the data is a REG_DWORD then Data is a pointer to a ULONG to store
  294. // the data. This is different behavior than all other data types, so
  295. // exit out after doing this processing.
  296. //
  297. ASSERT(info->DataLength == sizeof(ULONG));
  298. if (info->DataLength < sizeof(ULONG)) {
  299. status = STATUS_INVALID_PARAMETER;
  300. goto cleanup;
  301. }
  302. RtlCopyMemory(Data, info->Data, sizeof(ULONG));
  303. status = STATUS_SUCCESS;
  304. goto cleanup;
  305. } else if (info->Type == REG_SZ) {
  306. if (FAILED(StringCbLength((PWCHAR)info->Data, info->DataLength, &stringLength))) {
  307. status = STATUS_INVALID_PARAMETER;
  308. goto cleanup;
  309. }
  310. ASSERT(stringLength <= MAXULONG);
  311. //
  312. // Account for the necessary NULLs in the
  313. // required buffer size calculation.
  314. //
  315. if (convertSzToMultiSz) {
  316. length = (ULONG)stringLength + 2*sizeof(UNICODE_NULL);
  317. } else {
  318. length = (ULONG)stringLength + sizeof(UNICODE_NULL);
  319. }
  320. } else if (info->Type == REG_MULTI_SZ) {
  321. status = STATUS_INVALID_PARAMETER;
  322. //
  323. // Iterate over the buffer looking for strings.
  324. // Initialize the "effective" size of the buffer to 1 character
  325. // smaller than the buffer actually is, because we always need
  326. // to look one character past the end of the string for the
  327. // second NULL terminator
  328. //
  329. remainingBufferPtr = (PWCHAR)info->Data;
  330. remainingBufferLength = (info->DataLength / sizeof(WCHAR)) - 1;
  331. length = 0;
  332. while (remainingBufferLength) {
  333. //
  334. // First see if we can find a NULL-terminated string
  335. // in the remaining buffer. If not, this is not a MULTI_SZ.
  336. //
  337. if (FAILED(StringCchLength(remainingBufferPtr,
  338. remainingBufferLength,
  339. &stringLength
  340. ))) {
  341. goto cleanup;
  342. }
  343. length += ((ULONG)stringLength+1)*sizeof(WCHAR);
  344. //
  345. // Look for a second NULL terminator after the end of
  346. // the string. If it exists, this is a MULTI_SZ.
  347. //
  348. if (remainingBufferPtr[(ULONG)stringLength+1] == UNICODE_NULL) {
  349. length += sizeof(UNICODE_NULL);
  350. status = STATUS_SUCCESS;
  351. break;
  352. }
  353. //
  354. // If not, advance past the single-null-terminated string
  355. // we did find and try again.
  356. //
  357. remainingBufferLength -= ((ULONG)stringLength+1);
  358. remainingBufferPtr += ((ULONG)stringLength+1);
  359. }
  360. if (!NT_SUCCESS(status)) {
  361. goto cleanup;
  362. }
  363. } else {
  364. length = info->DataLength;
  365. }
  366. //
  367. // Now allocate a buffer if necessary, and copy the data over.
  368. //
  369. if (*Data) {
  370. //
  371. // If the user supplied a buffer then make sure its big enough and use it
  372. // otherwise allocate one.
  373. //
  374. if (*DataLength < length) {
  375. status = STATUS_BUFFER_OVERFLOW;
  376. goto cleanup;
  377. }
  378. } else {
  379. *Data = ExAllocatePoolWithTag(PagedPool,
  380. length,
  381. MF_POOL_TAG
  382. );
  383. if (!*Data) {
  384. status = STATUS_INSUFFICIENT_RESOURCES;
  385. goto cleanup;
  386. }
  387. }
  388. if (convertSzToMultiSz) {
  389. //
  390. // Copy the string into the destination buffer
  391. // and add in the second NULL
  392. //
  393. if (FAILED(StringCbCopyEx((PWCHAR)*Data, // Destination
  394. length, // Destination length
  395. (PWCHAR)info->Data, // Source
  396. &stringEnd, // Ptr to end of copy
  397. &sizeRemaining, // bytes left in the destination
  398. 0
  399. ))) {
  400. ASSERT(FALSE);
  401. status = STATUS_INVALID_PARAMETER;
  402. goto cleanup;
  403. }
  404. //
  405. // In the sanity checking portion of this function,
  406. // we made sure the buffer was big enough for both
  407. // NULLs. ASSERT that it's true here anyway.
  408. //
  409. ASSERT(sizeRemaining >= 2*sizeof(UNICODE_NULL));
  410. *(stringEnd+1) = UNICODE_NULL;
  411. } else {
  412. //
  413. // Nothing special to do. Just copy.
  414. //
  415. RtlCopyMemory(*Data, info->Data, length);
  416. }
  417. *DataLength = length;
  418. status = STATUS_SUCCESS;
  419. cleanup:
  420. if (info) {
  421. ExFreePool(info);
  422. }
  423. return status;
  424. }
  425. NTSTATUS
  426. MfSendPnpIrp(
  427. IN PDEVICE_OBJECT DeviceObject,
  428. IN PIO_STACK_LOCATION Location,
  429. OUT PULONG_PTR Information OPTIONAL
  430. )
  431. /*++
  432. Routine Description:
  433. This builds and send a pnp irp to a device.
  434. Arguments:
  435. DeviceObject - The a device in the device stack the irp is to be sent to -
  436. the top of the device stack is always found and the irp sent there first.
  437. Location - The initial stack location to use - contains the IRP minor code
  438. and any parameters
  439. Information - If provided contains the final value of the irps information
  440. field.
  441. Return Value:
  442. The final status of the completed irp or an error if the irp couldn't be sent
  443. --*/
  444. {
  445. NTSTATUS status;
  446. PIRP irp = NULL;
  447. PIO_STACK_LOCATION irpStack;
  448. PDEVICE_OBJECT targetDevice = NULL;
  449. KEVENT irpCompleted;
  450. IO_STATUS_BLOCK statusBlock;
  451. ASSERT(Location->MajorFunction == IRP_MJ_PNP);
  452. //
  453. // Find out where we are sending the irp
  454. //
  455. targetDevice = IoGetAttachedDeviceReference(DeviceObject);
  456. //
  457. // Get an IRP
  458. //
  459. KeInitializeEvent(&irpCompleted, SynchronizationEvent, FALSE);
  460. irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
  461. targetDevice,
  462. NULL, // Buffer
  463. 0, // Length
  464. 0, // StartingOffset
  465. &irpCompleted,
  466. &statusBlock
  467. );
  468. if (!irp) {
  469. status = STATUS_INSUFFICIENT_RESOURCES;
  470. goto cleanup;
  471. }
  472. irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  473. irp->IoStatus.Information = 0;
  474. //
  475. // Initialize the stack location
  476. //
  477. irpStack = IoGetNextIrpStackLocation(irp);
  478. ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
  479. irpStack->MinorFunction = Location->MinorFunction;
  480. irpStack->Parameters = Location->Parameters;
  481. //
  482. // Call the driver and wait for completion
  483. //
  484. status = IoCallDriver(targetDevice, irp);
  485. if (status == STATUS_PENDING) {
  486. KeWaitForSingleObject(&irpCompleted, Executive, KernelMode, FALSE, NULL);
  487. status = statusBlock.Status;
  488. }
  489. if (!NT_SUCCESS(status)) {
  490. goto cleanup;
  491. }
  492. //
  493. // Return the information
  494. //
  495. if (ARGUMENT_PRESENT(Information)) {
  496. *Information = statusBlock.Information;
  497. }
  498. ObDereferenceObject(targetDevice);
  499. ASSERT(status == STATUS_PENDING || status == statusBlock.Status);
  500. return statusBlock.Status;
  501. cleanup:
  502. if (targetDevice) {
  503. ObDereferenceObject(targetDevice);
  504. }
  505. return status;
  506. }
  507. DEVICE_POWER_STATE
  508. MfUpdateChildrenPowerReferences(
  509. IN PMF_PARENT_EXTENSION Parent,
  510. IN DEVICE_POWER_STATE PreviousPowerState,
  511. IN DEVICE_POWER_STATE NewPowerState
  512. )
  513. /*++
  514. Routine Description:
  515. Calculates the lowest power state the mf parent can be put into
  516. based on the power states of its children.
  517. Arguments:
  518. Parent - The MF parent device
  519. Return Value:
  520. The lowest power state
  521. --*/
  522. {
  523. PMF_CHILD_EXTENSION currentChild;
  524. PLIST_ENTRY currentEntry;
  525. DEVICE_POWER_STATE lowest;
  526. KIRQL oldIrql;
  527. DEBUG_MSG(1,
  528. ("Scanning 0x%08x's childrens power states:\n",
  529. Parent->Self
  530. ));
  531. KeAcquireSpinLock(&Parent->PowerLock, &oldIrql);
  532. //
  533. // ChildrenPowerStates[PowerDeviceUnspecified] will go negative as
  534. // children leave this state. It will never go positive as
  535. // children never re-enter this state.
  536. //
  537. Parent->ChildrenPowerReferences[PreviousPowerState]--;
  538. Parent->ChildrenPowerReferences[NewPowerState]++;
  539. //
  540. // Find the lowest power state
  541. //
  542. for (lowest = PowerDeviceUnspecified; lowest < PowerDeviceMaximum;
  543. lowest++) {
  544. if (Parent->ChildrenPowerReferences[lowest] > 0) {
  545. break;
  546. }
  547. }
  548. KeReleaseSpinLock(&Parent->PowerLock, oldIrql);
  549. if (lowest == PowerDeviceMaximum) {
  550. lowest = PowerDeviceD3;
  551. }
  552. DEBUG_MSG(1, ("Lowest = %s\n", DEVICE_POWER_STRING(lowest)));
  553. return lowest;
  554. }
  555. NTSTATUS
  556. MfPowerRequestCompletion(
  557. IN PDEVICE_OBJECT DeviceObject,
  558. IN UCHAR MinorFunction,
  559. IN POWER_STATE PowerState,
  560. IN PVOID Context,
  561. IN PIO_STATUS_BLOCK IoStatus
  562. )
  563. /*++
  564. Routine Description:
  565. This is the power completion routine for all mf power operations. It copies
  566. the status of the power operation into the context and then sets the completed
  567. event.
  568. Arguments:
  569. As documented for power completion routines.
  570. Return Value:
  571. STATUS_SUCCESS
  572. --*/
  573. {
  574. PMF_POWER_COMPLETION_CONTEXT completion = Context;
  575. completion->Status = IoStatus->Status;
  576. KeSetEvent(&completion->Event, 0, FALSE);
  577. return STATUS_SUCCESS;
  578. }
  579. NTSTATUS
  580. MfSendSetPowerIrp(
  581. IN PDEVICE_OBJECT Target,
  582. IN POWER_STATE State
  583. )
  584. /*++
  585. Routine Description:
  586. This builds and send a IRP_MN_SET_POWER_IRP to a device.
  587. Arguments:
  588. Target - The a device in the device stack the irp is to be sent to -
  589. the top of the device stack is always found and the irp sent there first.
  590. State - The device power state that should be requested.
  591. Return Value:
  592. The final status of the completed irp or an error if the irp couldn't be sent
  593. --*/
  594. {
  595. NTSTATUS status;
  596. MF_POWER_COMPLETION_CONTEXT completion;
  597. KeInitializeEvent(&completion.Event, SynchronizationEvent, FALSE);
  598. DEBUG_MSG(1,
  599. ("Sending SET_POWER to 0x%08x for %s\n",
  600. Target,
  601. DEVICE_POWER_STRING(State.DeviceState)
  602. ));
  603. status = PoRequestPowerIrp(Target,
  604. IRP_MN_SET_POWER,
  605. State,
  606. MfPowerRequestCompletion,
  607. &completion,
  608. NULL
  609. );
  610. if (NT_SUCCESS(status)) {
  611. ASSERT(status == STATUS_PENDING);
  612. KeWaitForSingleObject( &completion.Event, Executive, KernelMode, FALSE, NULL );
  613. status = completion.Status;
  614. }
  615. return status;
  616. }
  617. NTSTATUS
  618. MfUpdateParentPowerState(
  619. IN PMF_PARENT_EXTENSION Parent,
  620. IN DEVICE_POWER_STATE TargetPowerState
  621. )
  622. /*++
  623. Routine Description:
  624. Request Po to send the mf parent device a power irp if we need to
  625. change its power state because of changes to its children power
  626. states.
  627. Arguments:
  628. Parent - The MF parent device
  629. TargetPowerState - The device power state that the parent should
  630. be updated to.
  631. Return Value:
  632. The status of this operation.
  633. --*/
  634. {
  635. NTSTATUS status = STATUS_SUCCESS;
  636. POWER_STATE newState;
  637. //
  638. // If the parent's power state need changing because of a power down or
  639. // power up request that it do so
  640. //
  641. if (Parent->Common.PowerState != TargetPowerState) {
  642. //
  643. // Create and send the power irp and wait for its completion
  644. //
  645. DEBUG_MSG(1,
  646. ("Updating parent power state from %s to %s\n",
  647. DEVICE_POWER_STRING(Parent->Common.PowerState),
  648. DEVICE_POWER_STRING(TargetPowerState)
  649. ));
  650. newState.DeviceState = TargetPowerState;
  651. status = MfSendSetPowerIrp(Parent->Self,
  652. newState);
  653. DEBUG_MSG(1,
  654. ("Power update completed with %s (0x%08x)\n",
  655. STATUS_STRING(status), status
  656. ));
  657. } else {
  658. DEBUG_MSG(1,
  659. ("Parent power already in %s\n",
  660. DEVICE_POWER_STRING(TargetPowerState)
  661. ));
  662. }
  663. return status;
  664. }