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.

803 lines
17 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. string.Length = (USHORT) info->NameLength;
  151. string.MaximumLength = (USHORT) info->NameLength;
  152. string.Buffer = ExAllocatePoolWithTag(PagedPool,
  153. info->NameLength,
  154. MF_POOL_TAG
  155. );
  156. if (!string.Buffer) {
  157. goto cleanup;
  158. }
  159. RtlCopyMemory(string.Buffer, info->Name, info->NameLength);
  160. //
  161. // Open the name to get a handle
  162. //
  163. InitializeObjectAttributes(&attributes,
  164. &string,
  165. 0, //Attributes
  166. ParentHandle,
  167. NULL //SecurityDescriptoy
  168. );
  169. status = ZwOpenKey(&childHandle,
  170. Access,
  171. &attributes
  172. );
  173. if (!NT_SUCCESS(status)) {
  174. goto cleanup;
  175. }
  176. DEBUG_MSG(1, ("\tSubkey %wZ\n", &string));
  177. //
  178. // Hand the name back to the caller
  179. //
  180. Name->Buffer = string.Buffer;
  181. Name->Length = string.Length;
  182. Name->MaximumLength = string.MaximumLength;
  183. *ChildHandle = childHandle;
  184. return STATUS_SUCCESS;
  185. cleanup:
  186. if (string.Buffer) {
  187. ExFreePool(string.Buffer);
  188. }
  189. //
  190. // We should never be able to overflow as our buffer is the max size of
  191. // a registry key name
  192. //
  193. ASSERT(status != STATUS_BUFFER_OVERFLOW);
  194. return status;
  195. }
  196. NTSTATUS
  197. MfGetRegistryValue(
  198. IN HANDLE Handle,
  199. IN PWSTR Name,
  200. IN ULONG Type,
  201. IN ULONG Flags,
  202. IN OUT PULONG DataLength,
  203. IN OUT PVOID *Data
  204. )
  205. /*++
  206. Routine Description:
  207. This retrieves a value key from the registry performing type and sanity
  208. checking
  209. Arguments:
  210. Handle - The key the values are located under
  211. Name - The name of the value
  212. Type - The type (REG_*) of the value
  213. DataLength - Points to the length of the data buffer, on success contains
  214. the size of the data
  215. Data - Pointer to pointer to the buffer to return the data in, if points to
  216. NULL a buffer of the right size should be allocated (in this case
  217. DataLength should be 0)
  218. Return Value:
  219. Status code that indicates whether or not the function was successful. If
  220. the type of the object is not Type then we fail with STATUS_OBJECT_TYPE_MISMATCH
  221. --*/
  222. {
  223. #define VALUE_BUFFER_SIZE PAGE_SIZE
  224. NTSTATUS status;
  225. PKEY_VALUE_PARTIAL_INFORMATION info = NULL;
  226. ULONG size = VALUE_BUFFER_SIZE, length;
  227. UNICODE_STRING string;
  228. BOOLEAN convertSzToMultiSz;
  229. //
  230. // Check parameters
  231. //
  232. if (*Data && !(*DataLength)) {
  233. status = STATUS_INVALID_PARAMETER;
  234. goto cleanup;
  235. }
  236. RtlInitUnicodeString(&string, Name);
  237. info = ExAllocatePoolWithTag(PagedPool,
  238. VALUE_BUFFER_SIZE,
  239. MF_POOL_TAG
  240. );
  241. if (!info) {
  242. status = STATUS_INSUFFICIENT_RESOURCES;
  243. goto cleanup;
  244. }
  245. while ((status = ZwQueryValueKey(Handle,
  246. &string,
  247. KeyValuePartialInformation,
  248. info,
  249. size,
  250. &size
  251. )) == STATUS_BUFFER_OVERFLOW) {
  252. ExFreePool(info);
  253. info = ExAllocatePoolWithTag(PagedPool,
  254. size,
  255. MF_POOL_TAG
  256. );
  257. if (!info) {
  258. status = STATUS_INSUFFICIENT_RESOURCES;
  259. goto cleanup;
  260. }
  261. }
  262. if (!NT_SUCCESS(status)) {
  263. goto cleanup;
  264. }
  265. convertSzToMultiSz = (Type == REG_MULTI_SZ
  266. && info->Type == REG_SZ
  267. && Flags & MF_GETREG_SZ_TO_MULTI_SZ);
  268. if (convertSzToMultiSz) {
  269. length = info->DataLength + sizeof(UNICODE_NULL);
  270. } else {
  271. length = info->DataLength;
  272. }
  273. //
  274. // Make sure the type we got back is what we expected
  275. //
  276. if (info->Type != Type && !convertSzToMultiSz) {
  277. status = STATUS_OBJECT_TYPE_MISMATCH;
  278. goto cleanup;
  279. }
  280. //
  281. // Apply various common sense checks based on the type.
  282. //
  283. if (Type == REG_DWORD) {
  284. //
  285. // If the data is a REG_DWORD then Data is a pointer to a ULONG to store
  286. // the data
  287. //
  288. *Data = *((PULONG *)info->Data);
  289. } else if (*Data) {
  290. //
  291. // If the user supplied a buffer then make sure its big enough and use it
  292. // otherwise allocate one.
  293. //
  294. if (*DataLength < length) {
  295. status = STATUS_BUFFER_OVERFLOW;
  296. goto cleanup;
  297. }
  298. } else {
  299. *Data = ExAllocatePoolWithTag(PagedPool,
  300. length,
  301. MF_POOL_TAG
  302. );
  303. if (!*Data) {
  304. status = STATUS_INSUFFICIENT_RESOURCES;
  305. goto cleanup;
  306. }
  307. }
  308. RtlCopyMemory(*Data, info->Data, info->DataLength);
  309. if (convertSzToMultiSz) {
  310. PWSTR term = (PWSTR)*Data + (info->DataLength / sizeof(WCHAR));
  311. //
  312. // Add the final null termination
  313. //
  314. *term = UNICODE_NULL;
  315. }
  316. *DataLength = length;
  317. status = STATUS_SUCCESS;
  318. cleanup:
  319. if (info) {
  320. ExFreePool(info);
  321. }
  322. return status;
  323. }
  324. NTSTATUS
  325. MfSendPnpIrp(
  326. IN PDEVICE_OBJECT DeviceObject,
  327. IN PIO_STACK_LOCATION Location,
  328. OUT PULONG_PTR Information OPTIONAL
  329. )
  330. /*++
  331. Routine Description:
  332. This builds and send a pnp irp to a device.
  333. Arguments:
  334. DeviceObject - The a device in the device stack the irp is to be sent to -
  335. the top of the device stack is always found and the irp sent there first.
  336. Location - The initial stack location to use - contains the IRP minor code
  337. and any parameters
  338. Information - If provided contains the final value of the irps information
  339. field.
  340. Return Value:
  341. The final status of the completed irp or an error if the irp couldn't be sent
  342. --*/
  343. {
  344. NTSTATUS status;
  345. PIRP irp = NULL;
  346. PIO_STACK_LOCATION irpStack;
  347. PDEVICE_OBJECT targetDevice = NULL;
  348. KEVENT irpCompleted;
  349. IO_STATUS_BLOCK statusBlock;
  350. ASSERT(Location->MajorFunction == IRP_MJ_PNP);
  351. //
  352. // Find out where we are sending the irp
  353. //
  354. targetDevice = IoGetAttachedDeviceReference(DeviceObject);
  355. //
  356. // Get an IRP
  357. //
  358. KeInitializeEvent(&irpCompleted, SynchronizationEvent, FALSE);
  359. irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
  360. targetDevice,
  361. NULL, // Buffer
  362. 0, // Length
  363. 0, // StartingOffset
  364. &irpCompleted,
  365. &statusBlock
  366. );
  367. if (!irp) {
  368. status = STATUS_INSUFFICIENT_RESOURCES;
  369. goto cleanup;
  370. }
  371. irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
  372. irp->IoStatus.Information = 0;
  373. //
  374. // Initialize the stack location
  375. //
  376. irpStack = IoGetNextIrpStackLocation(irp);
  377. ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
  378. irpStack->MinorFunction = Location->MinorFunction;
  379. irpStack->Parameters = Location->Parameters;
  380. //
  381. // Call the driver and wait for completion
  382. //
  383. status = IoCallDriver(targetDevice, irp);
  384. if (status == STATUS_PENDING) {
  385. KeWaitForSingleObject(&irpCompleted, Executive, KernelMode, FALSE, NULL);
  386. status = statusBlock.Status;
  387. }
  388. if (!NT_SUCCESS(status)) {
  389. goto cleanup;
  390. }
  391. //
  392. // Return the information
  393. //
  394. if (ARGUMENT_PRESENT(Information)) {
  395. *Information = statusBlock.Information;
  396. }
  397. ObDereferenceObject(targetDevice);
  398. ASSERT(status == STATUS_PENDING || status == statusBlock.Status);
  399. return statusBlock.Status;
  400. cleanup:
  401. if (targetDevice) {
  402. ObDereferenceObject(targetDevice);
  403. }
  404. return status;
  405. }
  406. DEVICE_POWER_STATE
  407. MfUpdateChildrenPowerReferences(
  408. IN PMF_PARENT_EXTENSION Parent,
  409. IN DEVICE_POWER_STATE PreviousPowerState,
  410. IN DEVICE_POWER_STATE NewPowerState
  411. )
  412. /*++
  413. Routine Description:
  414. Calculates the lowest power state the mf parent can be put into
  415. based on the power states of its children.
  416. Arguments:
  417. Parent - The MF parent device
  418. Return Value:
  419. The lowest power state
  420. --*/
  421. {
  422. PMF_CHILD_EXTENSION currentChild;
  423. PLIST_ENTRY currentEntry;
  424. DEVICE_POWER_STATE lowest;
  425. KIRQL oldIrql;
  426. DEBUG_MSG(1,
  427. ("Scanning 0x%08x's childrens power states:\n",
  428. Parent->Self
  429. ));
  430. KeAcquireSpinLock(&Parent->PowerLock, &oldIrql);
  431. //
  432. // ChildrenPowerStates[PowerDeviceUnspecified] will go negative as
  433. // children leave this state. It will never go positive as
  434. // children never re-enter this state.
  435. //
  436. Parent->ChildrenPowerReferences[PreviousPowerState]--;
  437. Parent->ChildrenPowerReferences[NewPowerState]++;
  438. //
  439. // Find the lowest power state
  440. //
  441. for (lowest = PowerDeviceUnspecified; lowest < PowerDeviceMaximum;
  442. lowest++) {
  443. if (Parent->ChildrenPowerReferences[lowest] > 0) {
  444. break;
  445. }
  446. }
  447. KeReleaseSpinLock(&Parent->PowerLock, oldIrql);
  448. if (lowest == PowerDeviceMaximum) {
  449. lowest = PowerDeviceD3;
  450. }
  451. DEBUG_MSG(1, ("Lowest = %s\n", DEVICE_POWER_STRING(lowest)));
  452. return lowest;
  453. }
  454. NTSTATUS
  455. MfPowerRequestCompletion(
  456. IN PDEVICE_OBJECT DeviceObject,
  457. IN UCHAR MinorFunction,
  458. IN POWER_STATE PowerState,
  459. IN PVOID Context,
  460. IN PIO_STATUS_BLOCK IoStatus
  461. )
  462. /*++
  463. Routine Description:
  464. This is the power completion routine for all mf power operations. It copies
  465. the status of the power operation into the context and then sets the completed
  466. event.
  467. Arguments:
  468. As documented for power completion routines.
  469. Return Value:
  470. STATUS_SUCCESS
  471. --*/
  472. {
  473. PMF_POWER_COMPLETION_CONTEXT completion = Context;
  474. completion->Status = IoStatus->Status;
  475. KeSetEvent(&completion->Event, 0, FALSE);
  476. return STATUS_SUCCESS;
  477. }
  478. NTSTATUS
  479. MfSendSetPowerIrp(
  480. IN PDEVICE_OBJECT Target,
  481. IN POWER_STATE State
  482. )
  483. /*++
  484. Routine Description:
  485. This builds and send a IRP_MN_SET_POWER_IRP to a device.
  486. Arguments:
  487. Target - The a device in the device stack the irp is to be sent to -
  488. the top of the device stack is always found and the irp sent there first.
  489. State - The device power state that should be requested.
  490. Return Value:
  491. The final status of the completed irp or an error if the irp couldn't be sent
  492. --*/
  493. {
  494. NTSTATUS status;
  495. MF_POWER_COMPLETION_CONTEXT completion;
  496. KeInitializeEvent(&completion.Event, SynchronizationEvent, FALSE);
  497. DEBUG_MSG(1,
  498. ("Sending SET_POWER to 0x%08x for %s\n",
  499. Target,
  500. DEVICE_POWER_STRING(State.DeviceState)
  501. ));
  502. status = PoRequestPowerIrp(Target,
  503. IRP_MN_SET_POWER,
  504. State,
  505. MfPowerRequestCompletion,
  506. &completion,
  507. NULL
  508. );
  509. if (NT_SUCCESS(status)) {
  510. ASSERT(status == STATUS_PENDING);
  511. KeWaitForSingleObject( &completion.Event, Executive, KernelMode, FALSE, NULL );
  512. status = completion.Status;
  513. }
  514. return status;
  515. }
  516. NTSTATUS
  517. MfUpdateParentPowerState(
  518. IN PMF_PARENT_EXTENSION Parent,
  519. IN DEVICE_POWER_STATE TargetPowerState
  520. )
  521. /*++
  522. Routine Description:
  523. Request Po to send the mf parent device a power irp if we need to
  524. change its power state because of changes to its children power
  525. states.
  526. Arguments:
  527. Parent - The MF parent device
  528. TargetPowerState - The device power state that the parent should
  529. be updated to.
  530. Return Value:
  531. The status of this operation.
  532. --*/
  533. {
  534. NTSTATUS status = STATUS_SUCCESS;
  535. POWER_STATE newState;
  536. //
  537. // If the parent's power state need changing because of a power down or
  538. // power up request that it do so
  539. //
  540. if (Parent->Common.PowerState != TargetPowerState) {
  541. //
  542. // Create and send the power irp and wait for its completion
  543. //
  544. DEBUG_MSG(1,
  545. ("Updating parent power state from %s to %s\n",
  546. DEVICE_POWER_STRING(Parent->Common.PowerState),
  547. DEVICE_POWER_STRING(TargetPowerState)
  548. ));
  549. newState.DeviceState = TargetPowerState;
  550. status = MfSendSetPowerIrp(Parent->Self,
  551. newState);
  552. DEBUG_MSG(1,
  553. ("Power update completed with %s (0x%08x)\n",
  554. STATUS_STRING(status), status
  555. ));
  556. } else {
  557. DEBUG_MSG(1,
  558. ("Parent power already in %s\n",
  559. DEVICE_POWER_STRING(TargetPowerState)
  560. ));
  561. }
  562. return status;
  563. }