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.

674 lines
21 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. resource.c
  5. Abstract:
  6. Common routines for handling resource requirements
  7. Author:
  8. John Vert (jvert) 10/25/1997
  9. Revision History:
  10. --*/
  11. #include "agplib.h"
  12. PCM_RESOURCE_LIST
  13. ApSplitResourceList(
  14. IN PCM_RESOURCE_LIST ResourceList,
  15. OUT PCM_RESOURCE_LIST *NewResourceList
  16. );
  17. #ifdef ALLOC_PRAGMA
  18. #pragma alloc_text(PAGE, AgpFilterResourceRequirements)
  19. #pragma alloc_text(PAGE, AgpStartTarget)
  20. #pragma alloc_text(PAGE, ApSplitResourceList)
  21. #endif
  22. static BOOLEAN ResourceConflict = FALSE;
  23. NTSTATUS
  24. AgpFilterResourceRequirements(
  25. IN PDEVICE_OBJECT DeviceObject,
  26. IN PIRP Irp,
  27. IN PTARGET_EXTENSION Extension
  28. )
  29. /*++
  30. Routine Description:
  31. Completion routine for IRP_MN_QUERY_RESOURCE_REQUIREMENTS. This adds on the
  32. AGP resource requirements.
  33. Arguments:
  34. DeviceObject - Supplies the device object
  35. Irp - Supplies the IRP_MN_QUERY_RESOURCE_REQUIREMENTS Irp
  36. Extension - Supplies the device extension
  37. Return Value:
  38. NTSTATUS
  39. --*/
  40. {
  41. BOOLEAN SwapDescriptor;
  42. ULONG SwapLength;
  43. ULONG ApertureSize;
  44. NTSTATUS Status;
  45. ULONG AddCount;
  46. PHYSICAL_ADDRESS CurrentBase;
  47. PHYSICAL_ADDRESS MaxAddr;
  48. ULONG CurrentSizeInPages;
  49. PIO_RESOURCE_REQUIREMENTS_LIST OldRequirements;
  50. PIO_RESOURCE_REQUIREMENTS_LIST NewRequirements;
  51. ULONG NewSize;
  52. ULONG Alternative;
  53. PIO_RESOURCE_LIST OldResourceList;
  54. PIO_RESOURCE_LIST NewResourceList;
  55. PIO_RESOURCE_DESCRIPTOR Descriptor;
  56. PIO_STACK_LOCATION IrpSp;
  57. PIO_RESOURCE_LIST ApertureRequirements = NULL;
  58. ULONG i;
  59. PAGED_CODE();
  60. AGPLOG(AGP_NOISE,
  61. ("AgpQueryResourceRequirements - IRP %08lx, resource %08lx\n",
  62. Irp,
  63. Irp->IoStatus.Information));
  64. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  65. //
  66. // Create a new resource requirements list with our current aperture
  67. // settings tacked on the end.
  68. //
  69. OldRequirements = IrpSp->Parameters.FilterResourceRequirements.IoResourceRequirementList;
  70. if (OldRequirements == NULL) {
  71. //STATUS_INVALID_DEVICE_REQUEST
  72. // PNP helpfully passes us a NULL pointer instead of an empty resource list
  73. // when the bridge is disabled. In this case we will ignore this irp and not
  74. // add on our requirements since they are not going to be used anyway.
  75. //
  76. return(STATUS_SUCCESS);
  77. }
  78. //
  79. // Get the current GART aperture.
  80. //
  81. Status = AgpQueryAperture(GET_AGP_CONTEXT(Extension),
  82. &CurrentBase,
  83. &CurrentSizeInPages,
  84. &ApertureRequirements);
  85. if (!NT_SUCCESS(Status)) {
  86. AGPLOG(AGP_CRITICAL,
  87. ("AgpQueryResourceRequirements - AgpQueryAperture %08lx failed %08lx\n",
  88. Extension,
  89. Status));
  90. return(Status);
  91. }
  92. #if !defined(_IA64_)
  93. ASSERT(ApertureRequirements != NULL);
  94. #endif
  95. ApertureSize = (CurrentSizeInPages * PAGE_SIZE);
  96. MaxAddr.QuadPart = CurrentBase.QuadPart + ApertureSize - 1;
  97. AGPLOG(AGP_NOISE,
  98. ("AgpQueryResourceRequirements - aperture at %I64x, length %08lx pages, Requirements %08lx\n",
  99. CurrentBase.QuadPart,
  100. CurrentSizeInPages,
  101. ApertureRequirements));
  102. //
  103. // We will add IO_RESOURCE_DESCRIPTORs to each alternative.
  104. //
  105. // The first one is a private data type marked with our signature. This is
  106. // a marker so that we know which descriptors are ours so we can remove
  107. // them later.
  108. //
  109. // The second is the actual descriptor for the current aperture settings.
  110. // This is marked as preferred.
  111. //
  112. // Following this is the requirements returned from AgpQueryAperture. These
  113. // get marked as alternatives.
  114. //
  115. AddCount = 2;
  116. //
  117. // Enumerate the old list looking for any preferred descriptor that
  118. // conflicts with our preferred settings; if we find one, then the BIOS
  119. // is whack, and we will throw out our preferred descriptor, and let PnP
  120. // choose from our alternates
  121. //
  122. ResourceConflict = FALSE;
  123. OldResourceList = &OldRequirements->List[0];
  124. for (Alternative = 0; Alternative < OldRequirements->AlternativeLists;
  125. Alternative++) {
  126. for (i = 0; i < OldResourceList->Count; i++) {
  127. Descriptor = &OldResourceList->Descriptors[i];
  128. if ((Descriptor->Option == IO_RESOURCE_PREFERRED) &&
  129. (Descriptor->Type == CmResourceTypeMemory)) {
  130. if (((Descriptor->u.Memory.MinimumAddress.QuadPart >=
  131. CurrentBase.QuadPart) &&
  132. (Descriptor->u.Memory.MinimumAddress.QuadPart <=
  133. MaxAddr.QuadPart)) ||
  134. ((Descriptor->u.Memory.MaximumAddress.QuadPart >=
  135. CurrentBase.QuadPart) &&
  136. (Descriptor->u.Memory.MaximumAddress.QuadPart <=
  137. MaxAddr.QuadPart)) ||
  138. ((Descriptor->u.Memory.MinimumAddress.QuadPart <
  139. CurrentBase.QuadPart) &&
  140. (Descriptor->u.Memory.MaximumAddress.QuadPart >
  141. MaxAddr.QuadPart))) {
  142. AGPLOG(AGP_CRITICAL,
  143. ("AgpQueryResourceRequirements - Conflicted "
  144. "resource detected: %I64X - %I64X\n",
  145. Descriptor->u.Memory.MinimumAddress.QuadPart,
  146. Descriptor->u.Memory.MaximumAddress.QuadPart));
  147. //
  148. // This preferred descriptor is in conflic with our AGP
  149. // preferred setting
  150. //
  151. #if defined(_IA64_)
  152. AGPLOG(AGP_CRITICAL, ("Please contact system manufacturer "
  153. "for a BIOS upgrade.\n"));
  154. #else // _IA64_
  155. AddCount = 1;
  156. ResourceConflict = TRUE;
  157. #endif // _IA64_
  158. break;
  159. }
  160. }
  161. }
  162. OldResourceList = (PIO_RESOURCE_LIST)(OldResourceList->Descriptors +
  163. OldResourceList->Count);
  164. }
  165. //
  166. //
  167. // For IA64, PnP cannot reassign the aperture base, so we can only use
  168. // the "preferred" descriptor
  169. //
  170. #if !defined(_IA64_)
  171. AddCount += ApertureRequirements->Count;
  172. #endif // _IA64_
  173. NewSize = OldRequirements->ListSize;
  174. NewSize += sizeof(IO_RESOURCE_DESCRIPTOR) *
  175. (AddCount * OldRequirements->AlternativeLists);
  176. NewRequirements = ExAllocatePool(PagedPool, NewSize);
  177. if (NewRequirements == NULL) {
  178. ExFreePool(ApertureRequirements);
  179. return(STATUS_INSUFFICIENT_RESOURCES);
  180. }
  181. NewRequirements->ListSize = NewSize;
  182. NewRequirements->InterfaceType = OldRequirements->InterfaceType;
  183. NewRequirements->BusNumber = OldRequirements->BusNumber;
  184. NewRequirements->SlotNumber = OldRequirements->SlotNumber;
  185. NewRequirements->AlternativeLists = OldRequirements->AlternativeLists;
  186. //
  187. // Append our requirement to each alternative resource list.
  188. //
  189. NewResourceList = &NewRequirements->List[0];
  190. OldResourceList = &OldRequirements->List[0];
  191. for (Alternative = 0; Alternative < OldRequirements->AlternativeLists; Alternative++) {
  192. //
  193. // Copy the old resource list into the new one.
  194. //
  195. NewResourceList->Version = OldResourceList->Version;
  196. NewResourceList->Revision = OldResourceList->Revision;
  197. NewResourceList->Count = OldResourceList->Count + AddCount;
  198. RtlCopyMemory(&NewResourceList->Descriptors[0],
  199. &OldResourceList->Descriptors[0],
  200. OldResourceList->Count * sizeof(IO_RESOURCE_DESCRIPTOR));
  201. Descriptor = &NewResourceList->Descriptors[OldResourceList->Count];
  202. //
  203. // Append the marker descriptor
  204. //
  205. Descriptor->Option = 0;
  206. Descriptor->Flags = 0;
  207. Descriptor->Type = CmResourceTypeDevicePrivate;
  208. Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
  209. Descriptor->u.DevicePrivate.Data[0] = AgpPrivateResource;
  210. Descriptor->u.DevicePrivate.Data[1] = 1;
  211. ++Descriptor;
  212. //
  213. // Append the new descriptor
  214. //
  215. if (!ResourceConflict) {
  216. Descriptor->Option = IO_RESOURCE_PREFERRED;
  217. Descriptor->Flags = CM_RESOURCE_MEMORY_READ_WRITE |
  218. CM_RESOURCE_MEMORY_PREFETCHABLE;
  219. Descriptor->Type = CmResourceTypeMemory;
  220. Descriptor->ShareDisposition = CmResourceShareDeviceExclusive;
  221. Descriptor->u.Memory.Length = CurrentSizeInPages * PAGE_SIZE;
  222. Descriptor->u.Memory.Alignment = CurrentSizeInPages * PAGE_SIZE;
  223. Descriptor->u.Memory.MinimumAddress = CurrentBase;
  224. Descriptor->u.Memory.MaximumAddress = MaxAddr;
  225. ++Descriptor;
  226. }
  227. //
  228. // Append the alternatives
  229. //
  230. #if !defined(_IA64_)
  231. SwapDescriptor = FALSE;
  232. for (i = 0; i < ApertureRequirements->Count; i++) {
  233. //
  234. // Make sure this descriptor makes sense
  235. //
  236. ASSERT(ApertureRequirements->Descriptors[i].Flags ==
  237. (CM_RESOURCE_MEMORY_READ_WRITE |
  238. CM_RESOURCE_MEMORY_PREFETCHABLE));
  239. ASSERT(ApertureRequirements->Descriptors[i].Type ==
  240. CmResourceTypeMemory);
  241. ASSERT(ApertureRequirements->Descriptors[i].ShareDisposition ==
  242. CmResourceShareDeviceExclusive);
  243. *Descriptor = ApertureRequirements->Descriptors[i];
  244. //
  245. // In this case we nuked our preferred descriptor so mark the
  246. // first alternate as preferred
  247. //
  248. if ((i == 0) && ResourceConflict) {
  249. Descriptor->Option = IO_RESOURCE_PREFERRED;
  250. if (Descriptor->u.Memory.Length != ApertureSize) {
  251. SwapLength = Descriptor->u.Memory.Length;
  252. Descriptor->u.Memory.Length = ApertureSize;
  253. Descriptor->u.Memory.Alignment = ApertureSize;
  254. SwapDescriptor = TRUE;
  255. }
  256. } else {
  257. Descriptor->Option = IO_RESOURCE_ALTERNATIVE;
  258. if (SwapDescriptor) {
  259. if (Descriptor->u.Memory.Length == ApertureSize) {
  260. Descriptor->u.Memory.Length = SwapLength;
  261. Descriptor->u.Memory.Alignment = SwapLength;
  262. SwapDescriptor = FALSE;
  263. }
  264. }
  265. }
  266. ++Descriptor;
  267. }
  268. #endif // _IA64_
  269. //
  270. // Advance to next resource list
  271. //
  272. NewResourceList = (PIO_RESOURCE_LIST)(NewResourceList->Descriptors + NewResourceList->Count);
  273. OldResourceList = (PIO_RESOURCE_LIST)(OldResourceList->Descriptors + OldResourceList->Count);
  274. }
  275. AGPLOG(AGP_NOISE,
  276. ("AgpQueryResourceRequirements - IRP %p, old resources %p, new resources %p\n",
  277. Irp,
  278. OldRequirements,
  279. NewRequirements));
  280. IrpSp->Parameters.FilterResourceRequirements.IoResourceRequirementList = NewRequirements;
  281. Irp->IoStatus.Information = (ULONG_PTR)NewRequirements;
  282. ExFreePool(OldRequirements);
  283. #if !defined(_IA64_)
  284. ExFreePool(ApertureRequirements);
  285. #endif // _IA64_
  286. return(STATUS_SUCCESS);
  287. }
  288. NTSTATUS
  289. AgpQueryResources(
  290. IN PDEVICE_OBJECT DeviceObject,
  291. IN PIRP Irp,
  292. IN PTARGET_EXTENSION Extension
  293. )
  294. /*++
  295. Routine Description:
  296. Completion routine for IRP_MN_QUERY_RESOURCES. This adds on the
  297. AGP resources
  298. Arguments:
  299. DeviceObject - Supplies the device object
  300. Irp - Supplies the IRP_MN_QUERY_RESOURCES Irp
  301. Extension - Supplies the device extension
  302. Return Value:
  303. NTSTATUS
  304. --*/
  305. {
  306. if (Irp->PendingReturned) {
  307. IoMarkIrpPending(Irp);
  308. }
  309. AGPLOG(AGP_NOISE,
  310. ("AgpQueryResources - IRP %08lx, resource %08lx\n",
  311. Irp,
  312. Irp->IoStatus.Information));
  313. return(STATUS_SUCCESS);
  314. }
  315. NTSTATUS
  316. AgpStartTarget(
  317. IN PIRP Irp,
  318. IN PTARGET_EXTENSION Extension
  319. )
  320. /*++
  321. Routine Description:
  322. Filters out the AGP-specific resource requirements on a
  323. IRP_MN_START_DEVICE Irp.
  324. Arguments:
  325. Irp - supplies the IRP_MN_START_DEVICE Irp.
  326. Extension - Supplies the device extension.
  327. Return Value:
  328. NTSTATUS
  329. --*/
  330. {
  331. PIO_STACK_LOCATION irpSp;
  332. PCM_RESOURCE_LIST NewResources;
  333. PCM_RESOURCE_LIST NewResourcesTranslated;
  334. PCM_RESOURCE_LIST AgpAllocatedResources;
  335. PCM_RESOURCE_LIST AgpAllocatedResourcesTranslated;
  336. NTSTATUS Status;
  337. PCM_PARTIAL_RESOURCE_DESCRIPTOR Descriptor;
  338. PAGED_CODE();
  339. irpSp = IoGetCurrentIrpStackLocation(Irp);
  340. AGPLOG(AGP_NOISE,
  341. ("AgpStartTarget - IRP %08lx, resource %08lx\n",
  342. Irp,
  343. Irp->IoStatus.Information));
  344. if (irpSp->Parameters.StartDevice.AllocatedResources != NULL) {
  345. KEVENT event;
  346. //
  347. // Find our private descriptors and split them out into
  348. // our own resource list
  349. //
  350. Extension->Resources = ApSplitResourceList(irpSp->Parameters.StartDevice.AllocatedResources,
  351. &NewResources);
  352. Extension->ResourcesTranslated = ApSplitResourceList(irpSp->Parameters.StartDevice.AllocatedResourcesTranslated,
  353. &NewResourcesTranslated);
  354. ASSERT(Extension->Resources->Count == 1);
  355. ASSERT(Extension->Resources->List[0].PartialResourceList.Count == 1);
  356. Descriptor = &Extension->Resources->List[0].PartialResourceList.PartialDescriptors[0];
  357. ASSERT(Descriptor->Type == CmResourceTypeMemory);
  358. Extension->GartBase = Descriptor->u.Memory.Start;
  359. Extension->GartLengthInPages = Descriptor->u.Memory.Length / PAGE_SIZE;
  360. //
  361. // Set the new GART aperture
  362. //
  363. Status = AgpSetAperture(GET_AGP_CONTEXT(Extension),
  364. Extension->GartBase,
  365. Extension->GartLengthInPages);
  366. ASSERT(NT_SUCCESS(Status));
  367. Irp->IoStatus.Status = Status ;
  368. if (!NT_SUCCESS(Status) ) {
  369. AGPLOG(AGP_CRITICAL,
  370. ("AgpStartTarget - AgpSetAperture to %I64X, %08lx failed %08lx\n",
  371. Extension->GartBase.QuadPart,
  372. Extension->GartLengthInPages * PAGE_SIZE,
  373. Status));
  374. Irp->IoStatus.Status = Status;
  375. IoCompleteRequest(Irp, IO_NO_INCREMENT);
  376. return(Status);
  377. }
  378. KeInitializeEvent(&event, NotificationEvent, FALSE);
  379. //
  380. // Set up the new parameters for the PCI driver.
  381. //
  382. irpSp->Parameters.StartDevice.AllocatedResources = NewResources;
  383. irpSp->Parameters.StartDevice.AllocatedResourcesTranslated = NewResourcesTranslated;
  384. IoCopyCurrentIrpStackLocationToNext(Irp);
  385. IoSetCompletionRoutine(Irp,
  386. AgpSetEventCompletion,
  387. &event,
  388. TRUE,
  389. TRUE,
  390. TRUE);
  391. //
  392. // Pass down the driver stack
  393. //
  394. Status = IoCallDriver(Extension->CommonExtension.AttachedDevice, Irp);
  395. //
  396. // If we did things asynchronously then wait on our event
  397. //
  398. if (Status == STATUS_PENDING) {
  399. //
  400. // We do a KernelMode wait so that our stack where the event is
  401. // doesn't get paged out!
  402. //
  403. KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
  404. Status = Irp->IoStatus.Status;
  405. }
  406. ExFreePool(irpSp->Parameters.StartDevice.AllocatedResources);
  407. ExFreePool(irpSp->Parameters.StartDevice.AllocatedResourcesTranslated);
  408. IoCompleteRequest(Irp, IO_NO_INCREMENT) ;
  409. return Status;
  410. }
  411. //
  412. // The bridge is disabled, we have been passed a NULL pointer
  413. // instead of an empty resource list. There is nothing to do other
  414. // than pass down the irp
  415. //
  416. IoSkipCurrentIrpStackLocation(Irp);
  417. return(IoCallDriver(Extension->CommonExtension.AttachedDevice, Irp));
  418. }
  419. PCM_RESOURCE_LIST
  420. ApSplitResourceList(
  421. IN PCM_RESOURCE_LIST ResourceList,
  422. OUT PCM_RESOURCE_LIST *NewResourceList
  423. )
  424. /*++
  425. Routine Description:
  426. Splits out the AGP-specific resources from a resource list.
  427. Arguments:
  428. ResourceList - Supplies the resource list.
  429. NewResourceList - Returns the new resource list with the AGP-specific
  430. resources stripped out.
  431. Return Value:
  432. Pointer to the AGP-specific resource list
  433. --*/
  434. {
  435. ULONG Size;
  436. ULONG FullCount;
  437. ULONG PartialCount;
  438. PCM_FULL_RESOURCE_DESCRIPTOR Full, NewFull, AgpFull;
  439. PCM_PARTIAL_RESOURCE_DESCRIPTOR Partial, NewPartial, AgpPartial;
  440. PCM_RESOURCE_LIST NewList;
  441. PCM_RESOURCE_LIST AgpList;
  442. ULONG NextAgp=0;
  443. PAGED_CODE();
  444. //
  445. // First walk through the source resource list and figure out how big it
  446. // is. The two resulting resource lists must be smaller than this, so we
  447. // will just allocate them to be that size and not worry about it.
  448. //
  449. Size = sizeof(CM_RESOURCE_LIST) - sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
  450. Full = &ResourceList->List[0];
  451. for (FullCount=0; FullCount<ResourceList->Count; FullCount++) {
  452. Size += sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
  453. PartialCount = Full->PartialResourceList.Count;
  454. Size += (PartialCount-1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
  455. Full = (PCM_FULL_RESOURCE_DESCRIPTOR)(&Full->PartialResourceList.PartialDescriptors[PartialCount]);
  456. }
  457. //
  458. // Allocate two additional lists
  459. //
  460. NewList = ExAllocatePool(PagedPool, Size);
  461. if (NewList == NULL) {
  462. *NewResourceList = NULL;
  463. return(NULL);
  464. }
  465. AgpList = ExAllocatePool(PagedPool, Size);
  466. if (AgpList == NULL) {
  467. ExFreePool(NewList);
  468. *NewResourceList = NULL;
  469. return(NULL);
  470. }
  471. //
  472. // Initialize both new resource lists to have the same number
  473. // of CM_FULL_RESOURCE_DESCRIPTORs. If any turn out to be empty,
  474. // we will adjust the count.
  475. //
  476. NewList->Count = AgpList->Count = ResourceList->Count;
  477. //
  478. // Walk through each CM_FULL_RESOURCE_DESCRIPTOR, copying as we go.
  479. //
  480. Full = &ResourceList->List[0];
  481. NewFull = &NewList->List[0];
  482. AgpFull = &AgpList->List[0];
  483. for (FullCount = 0;FullCount < ResourceList->Count; FullCount++) {
  484. NewFull->InterfaceType = AgpFull->InterfaceType = Full->InterfaceType;
  485. NewFull->BusNumber = AgpFull->BusNumber = Full->BusNumber;
  486. //
  487. // Initialize the partial resource list header
  488. //
  489. NewFull->PartialResourceList.Version = Full->PartialResourceList.Version;
  490. AgpFull->PartialResourceList.Version = Full->PartialResourceList.Version;
  491. NewFull->PartialResourceList.Revision = Full->PartialResourceList.Revision;
  492. AgpFull->PartialResourceList.Revision = Full->PartialResourceList.Revision;
  493. NewFull->PartialResourceList.Count = AgpFull->PartialResourceList.Count = 0;
  494. NewPartial = &NewFull->PartialResourceList.PartialDescriptors[0];
  495. AgpPartial = &AgpFull->PartialResourceList.PartialDescriptors[0];
  496. for (PartialCount = 0; PartialCount < Full->PartialResourceList.Count; PartialCount++) {
  497. Partial = &Full->PartialResourceList.PartialDescriptors[PartialCount];
  498. if ((Partial->Type == CmResourceTypeDevicePrivate) &&
  499. (Partial->u.DevicePrivate.Data[0] == AgpPrivateResource)) {
  500. //
  501. // Found one of our private marker descriptors
  502. //
  503. // For now, the only kind we should see indicates we skip one descriptor
  504. //
  505. ASSERT(NextAgp == 0);
  506. ASSERT(Partial->u.DevicePrivate.Data[1] == 1);
  507. NextAgp = Partial->u.DevicePrivate.Data[1];
  508. ASSERT(PartialCount+NextAgp < Full->PartialResourceList.Count);
  509. } else {
  510. //
  511. // if NextAgp is set, this descriptor goes in the AGP-specific list.
  512. // Otherwise, it goes in the new list.
  513. //
  514. if (NextAgp > 0) {
  515. --NextAgp;
  516. *AgpPartial++ = *Partial;
  517. ++AgpFull->PartialResourceList.Count;
  518. } else {
  519. *NewPartial++ = *Partial;
  520. ++NewFull->PartialResourceList.Count;
  521. }
  522. }
  523. }
  524. //
  525. // Finished this CM_PARTIAL_RESOURCE_LIST, advance to the next CM_FULL_RESOURCE_DESCRIPTOR
  526. //
  527. if (NewFull->PartialResourceList.Count == 0) {
  528. //
  529. // we can just reuse this partial resource descriptor as it is empty
  530. //
  531. --NewList->Count;
  532. } else {
  533. NewFull = (PCM_FULL_RESOURCE_DESCRIPTOR)NewPartial;
  534. }
  535. if (AgpFull->PartialResourceList.Count == 0) {
  536. //
  537. // we can just reuse this partial resource descriptor as it is empty
  538. //
  539. --AgpList->Count;
  540. } else {
  541. AgpFull = (PCM_FULL_RESOURCE_DESCRIPTOR)NewPartial;
  542. }
  543. }
  544. *NewResourceList = NewList;
  545. return(AgpList);
  546. }