/*++

Copyright (c) 1992-1999  Microsoft Corporation

Module Name:

    registry.c

Abstract:

    Registry support for the video port driver.

Author:

    Andre Vachon (andreva) 01-Mar-1992

Environment:

    kernel mode only

Notes:

Revision History:

--*/

#include "videoprt.h"


//
// Local routines.
//

BOOLEAN
CheckIoEnabled(
    PVOID HwDeviceExtension,
    ULONG NumAccessRanges,
    PVIDEO_ACCESS_RANGE AccessRanges
    );

ULONG
GetCmResourceListSize(
    PCM_RESOURCE_LIST CmResourceList
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,VpGetFlags)
#pragma alloc_text(PAGE,VideoPortGetAccessRanges)
#pragma alloc_text(PAGE,VideoPortVerifyAccessRanges)
#pragma alloc_text(PAGE,CheckIoEnabled)
#pragma alloc_text(PAGE,VpAppendToRequirementsList)
#pragma alloc_text(PAGE,VpIsLegacyAccessRange)
#pragma alloc_text(PAGE,GetCmResourceListSize)
#pragma alloc_text(PAGE,VpRemoveFromResourceList)
#pragma alloc_text(PAGE,VpIsVgaResource)
#endif

NTSTATUS
VpGetFlags(
    PUNICODE_STRING RegistryPath,
    PVIDEO_HW_INITIALIZATION_DATA HwInitializationData,
    PULONG Flags
    )

/*++

Routine Description:

    Checks for the existance of the PnP key/value in the device's
    registry path.

Return Value:

    TRUE if the flag exists, FALSE otherwise.

--*/

{
    PWSTR    Path;
    NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
    ULONG    pnpEnabled = 0;
    ULONG    legacyDetect = 0;
    ULONG    defaultValue = 0;
    ULONG    bootDriver = 0;
    ULONG    reportDevice = 0;
    PWSTR    Table[] = {L"\\Vga", L"\\VgaSave", NULL};
    PWSTR    SubStr, *Item = Table;
    ULONG    Len;

    *Flags = 0;

    Path = ExAllocatePoolWithTag(PagedPool,
                                 RegistryPath->Length + sizeof(UNICODE_NULL),
                                 POOL_TAG);

    if (Path)
    {
        RtlCopyMemory(Path,
                      RegistryPath->Buffer,
                      RegistryPath->Length);

        *(Path + (RegistryPath->Length / sizeof(UNICODE_NULL))) = UNICODE_NULL;

        pVideoDebugPrint((1, "PnP path: %ws\n", Path));

        legacyDetect = FALSE;
        reportDevice = FALSE;

        //
        // If the PnP Entry points are present, then we will treat this
        // driver as a PnP driver.
        //

        if ( (HwInitializationData->HwInitDataSize >=
              FIELD_OFFSET(VIDEO_HW_INITIALIZATION_DATA, HwQueryInterface)) &&
             (HwInitializationData->HwSetPowerState != NULL)                &&
             (HwInitializationData->HwGetPowerState != NULL)                &&
             (HwInitializationData->HwGetVideoChildDescriptor != NULL) )
        {
            pVideoDebugPrint((1, "videoprt: The miniport is a PnP miniport."));

            pnpEnabled = TRUE;
        }

        //
        // REPORT_DEVICE is only valid if PNP_ENABLED is true.
        //
        // We don't want to report a device to the PnP system if
        // we don't have a PnP driver.
        //

        if (!pnpEnabled)
        {
            reportDevice = 0;
        }

        *Flags = (pnpEnabled   ? PNP_ENABLED   : 0) |
                 (legacyDetect ? LEGACY_DETECT : 0) |
                 (reportDevice ? REPORT_DEVICE : 0);

        //
        // Free the memory we allocated above.
        //

        ExFreePool(Path);


        //
        // Determine if the current miniport is the VGA miniport.
        //

        while (*Item) {

            Len = wcslen(*Item);

            SubStr = RegistryPath->Buffer + (RegistryPath->Length / 2) - Len;

            if (!_wcsnicmp(SubStr, *Item, Len)) {

                pVideoDebugPrint((1, "This IS the vga miniport\n"));
                *Flags |= VGA_DRIVER;
                break;
            }

            Item++;
        }

        pVideoDebugPrint((1, "Flags = %d\n", *Flags));

        ntStatus = STATUS_SUCCESS;
    }

    return ntStatus;
}


VIDEOPORT_API
VP_STATUS
VideoPortGetAccessRanges(
    PVOID HwDeviceExtension,
    ULONG NumRequestedResources,
    PIO_RESOURCE_DESCRIPTOR RequestedResources OPTIONAL,
    ULONG NumAccessRanges,
    PVIDEO_ACCESS_RANGE AccessRanges,
    PVOID VendorId,
    PVOID DeviceId,
    PULONG Slot
    )

/*++

Routine Description:

    Walk the appropriate bus to get device information.
    Search for the appropriate device ID.
    Appropriate resources will be returned and automatically stored in the
    resourcemap.

Arguments:

    HwDeviceExtension - Points to the miniport driver's device extension.

    NumRequestedResources - Number of entries in the RequestedResources array.

    RequestedResources - Optional pointer to an array ofRequestedResources
        the miniport driver wants to access.

    NumAccessRanges - Maximum number of access ranges that can be returned
        by the function.

    AccessRanges - Array of access ranges that will be returned to the driver.

    VendorId - Pointer to the vendor ID. On PCI, this is a pointer to a 16 bit
        word.

    DeviceId - Pointer to the Device ID. On PCI, this is a pointer to a 16 bit
        word.

    Slot - Pointer to the starting slot number for this search.

Return Value:

    ERROR_MORE_DATA if the AccessRange structure is not large enough for the
       PCI config info.
    ERROR_DEV_NOT_EXIST is the card is not found.

    NO_ERROR if the function succeded.

--*/

{
    PFDO_EXTENSION fdoExtension = GET_FDO_EXT(HwDeviceExtension);

    UNICODE_STRING unicodeString;
    ULONG i;
    ULONG j;

    PCM_RESOURCE_LIST cmResourceList;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR cmResourceDescriptor;


    VP_STATUS status;
    UCHAR bShare;

    PPCI_SLOT_NUMBER slotData = (PPCI_SLOT_NUMBER)Slot;

    //
    // This is the miniport drivers slot. Allocate the
    // resources.
    //

    // Hack Add extra R so the Device0 key does not get created as volatile
    // a screw up the subsequent driver install.

    *(LPWSTR) (((PUCHAR)fdoExtension->DriverRegistryPath) +
               fdoExtension->DriverRegistryPathLength) = L'R';

    RtlInitUnicodeString(&unicodeString, fdoExtension->DriverRegistryPath);

    //
    // Assert drivers do set those parameters properly
    //

#if DBG

    if ((NumRequestedResources == 0) != (RequestedResources == NULL)) {

        pVideoDebugPrint((0, "VideoPortGetDeviceResources: Parameters for requested resource are inconsistent\n"));

    }

#endif

    //
    // An empty requested resource list means we want to automatic behavoir.
    // Just call the HAL to get all the information
    //

    if (NumRequestedResources == 0) {

        //
        // If a PnP driver is requesting resources, then return what the
        // system passed in to us.
        //

        cmResourceList = fdoExtension->AllocatedResources;

        //
        // Return the slot number to the device.
        //

        if (Slot) {
            *Slot = fdoExtension->SlotNumber;
        }

        if (cmResourceList) {
#if DBG
            DumpResourceList(cmResourceList);
#endif
            status = NO_ERROR;

        } else {

            //
            // The system should always pass us resources.
            //

            ASSERT(FALSE);
            status = ERROR_INVALID_PARAMETER;
        }

    } else {

        PIO_RESOURCE_REQUIREMENTS_LIST requestedResources;
        ULONG requestedResourceSize;
        NTSTATUS ntStatus;

        status = NO_ERROR;

        //
        // The caller has specified some resources.
        // Lets call IoAssignResources with that and see what comes back.
        //

        requestedResourceSize = sizeof(IO_RESOURCE_REQUIREMENTS_LIST) +
                                   ((NumRequestedResources - 1) *
                                   sizeof(IO_RESOURCE_DESCRIPTOR));

        requestedResources = ExAllocatePoolWithTag(PagedPool,
                                                   requestedResourceSize,
                                                   POOL_TAG);

        if (requestedResources) {

            RtlZeroMemory(requestedResources, requestedResourceSize);

            requestedResources->ListSize = requestedResourceSize;
            requestedResources->InterfaceType = fdoExtension->AdapterInterfaceType;
            requestedResources->BusNumber = fdoExtension->SystemIoBusNumber;
            requestedResources->SlotNumber = slotData->u.bits.DeviceNumber;
            requestedResources->AlternativeLists = 1;

            requestedResources->List[0].Version  = 1;
            requestedResources->List[0].Revision = 1;
            requestedResources->List[0].Count    = NumRequestedResources;

            RtlMoveMemory(&(requestedResources->List[0].Descriptors[0]),
                          RequestedResources,
                          NumRequestedResources * sizeof(IO_RESOURCE_DESCRIPTOR));

            ntStatus = IoAssignResources(&unicodeString,
                                         &VideoClassName,
                                         fdoExtension->FunctionalDeviceObject->DriverObject,
                                         fdoExtension->FunctionalDeviceObject,
                                         requestedResources,
                                         &cmResourceList);

            ExFreePool(requestedResources);

            if (!NT_SUCCESS(ntStatus)) {

                status = ERROR_INVALID_PARAMETER;

            }

        } else {

            status = ERROR_NOT_ENOUGH_MEMORY;

        }

    }

    if (status == NO_ERROR) {

        VIDEO_ACCESS_RANGE TempRange;

        //
        // We now have a valid cmResourceList.
        // Lets translate it back to access ranges so the driver
        // only has to deal with one type of list.
        //

        //
        // NOTE: The resources have already been reported at this point in
        // time.
        //

        //
        // Walk resource list to update configuration information.
        //

        for (i = 0, j = 0;
             (i < cmResourceList->List->PartialResourceList.Count) &&
                 (status == NO_ERROR);
             i++) {

            //
            // Get resource descriptor.
            //

            cmResourceDescriptor =
                &cmResourceList->List->PartialResourceList.PartialDescriptors[i];

            //
            // Get the share disposition
            //

            if (cmResourceDescriptor->ShareDisposition == CmResourceShareShared) {

                bShare = 1;

            } else {

                bShare = 0;

            }

            switch (cmResourceDescriptor->Type) {

            case CmResourceTypePort:
            case CmResourceTypeMemory:

                //
                // common part
                //

                TempRange.RangeLength =
                    cmResourceDescriptor->u.Memory.Length;
                TempRange.RangeStart =
                    cmResourceDescriptor->u.Memory.Start;
                TempRange.RangeVisible = 0;
                TempRange.RangeShareable = bShare;
                TempRange.RangePassive = 0;

                //
                // separate part
                //

                if (cmResourceDescriptor->Type == CmResourceTypePort) {
                    TempRange.RangeInIoSpace = 1;
                } else {
                    TempRange.RangeInIoSpace = 0;
                }

                //
                // See if we need to return the resource to the driver.
                //

                if (!VpIsLegacyAccessRange(fdoExtension, &TempRange)) {

                    if (j == NumAccessRanges) {

                        status = ERROR_MORE_DATA;
                        break;

                    } else {

                        //
                        // Only modify the AccessRange array if we are writing
                        // valid data.
                        //

                        AccessRanges[j] = TempRange;
                        j++;
                    }

                }

                break;

            case CmResourceTypeInterrupt:

                fdoExtension->MiniportConfigInfo->BusInterruptVector =
                    cmResourceDescriptor->u.Interrupt.Vector;
                fdoExtension->MiniportConfigInfo->BusInterruptLevel =
                    cmResourceDescriptor->u.Interrupt.Level;
                fdoExtension->MiniportConfigInfo->InterruptShareable =
                    bShare;

                break;

            case CmResourceTypeDma:

                fdoExtension->MiniportConfigInfo->DmaChannel =
                    cmResourceDescriptor->u.Dma.Channel;
                fdoExtension->MiniportConfigInfo->DmaPort =
                    cmResourceDescriptor->u.Dma.Port;
                fdoExtension->MiniportConfigInfo->DmaShareable =
                    bShare;

                break;

            default:

                pVideoDebugPrint((1, "VideoPortGetAccessRanges: Unknown descriptor type %x\n",
                                 cmResourceDescriptor->Type ));

                break;

            }

        }
    }

    // Hack remove extra R

    *(LPWSTR) (((PUCHAR)fdoExtension->DriverRegistryPath) +
               fdoExtension->DriverRegistryPathLength) = UNICODE_NULL;


#if DBG

    if (status == NO_ERROR)
    {
        //
        // Indicates resources have been mapped properly
        //

        InterlockedIncrement(&VPResourcesReported);
    }

#endif

    return status;

} // VideoPortGetDeviceResources()

BOOLEAN
VpIsVgaResource(
    PVIDEO_ACCESS_RANGE AccessRange
    )

/*++

Routine Description:

    Indicates whether the given access range is a vga access range.

Arguments:

    AccessRange - The access range to examine.

Returns:

    TRUE if it is a VGA access range,
    FALSE otherwise.

Notes:

    This routine does not take into account the length of the access range.

--*/

{
    if (AccessRange->RangeInIoSpace) {

        ULONGLONG Port = AccessRange->RangeStart.QuadPart;

        if (((Port >= 0x3b0) && (Port <= 0x3bb)) ||
            ((Port >= 0x3c0) && (Port <= 0x3df))) {

            return TRUE;

        }

    } else {

        if (AccessRange->RangeStart.QuadPart == 0xa0000) {
            return TRUE;
        }
    }

    return FALSE;
}

NTSTATUS
VpAppendToRequirementsList(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementList,
    IN ULONG NumAccessRanges,
    IN PVIDEO_ACCESS_RANGE AccessRanges
    )

/*++

Routine Description:

    Builds a IoResourceRequirementsList for a given set of access ranges.

Arguments:

    ResourceList - Pointer to location of the requirments list.  Modified
        on completion to point to a new requirements list.

    NumAccessRanges - Number of access ranges in list.

    AccessRanges - List of resources.


Returns:

    STATUS_SUCCESS if successful, otherwise a status code.

Notes:

    This function free's the memory used by the original resource list,
    and allocates a new buffer for the appended resources list.

--*/

{
    PIO_RESOURCE_REQUIREMENTS_LIST OriginalRequirementList = *RequirementList;
    PIO_RESOURCE_DESCRIPTOR pioDescript;
    ULONG RequirementListSize;
    ULONG OriginalListSize;
    ULONG RequirementCount;
    ULONG i;

    RequirementCount = OriginalRequirementList->List[0].Count;
    OriginalListSize = OriginalRequirementList->ListSize;

    RequirementListSize = OriginalListSize +
                              NumAccessRanges * sizeof(IO_RESOURCE_DESCRIPTOR);

    *RequirementList =
        (PIO_RESOURCE_REQUIREMENTS_LIST) ExAllocatePool(PagedPool,
                                                        RequirementListSize);

    //
    // Return NULL if the structure could not be allocated.
    // Otherwise, fill it out.
    //

    if (*RequirementList == NULL) {

        *RequirementList = OriginalRequirementList;
        return STATUS_INSUFFICIENT_RESOURCES;

    } else {

        //
        // Copy the original resource list into the new one.
        //

        memcpy(*RequirementList, OriginalRequirementList, OriginalListSize);

        //
        // Free the original list
        //

        ExFreePool(OriginalRequirementList);

        //
        // Point to first free entry in requirements list
        //

        pioDescript =
            &((*RequirementList)->List[0].Descriptors[(*RequirementList)->List[0].Count]);

        //
        // For each entry in the access range, fill in an entry in the
        // resource list
        //

        for (i = 0; i < NumAccessRanges; i++) {

            //
            // We will never claim 0xC0000.
            //

            if ((AccessRanges->RangeStart.LowPart == 0xC0000) &&
                (AccessRanges->RangeInIoSpace == FALSE))
            {
                AccessRanges++;
                continue;
            }

            if (AccessRanges->RangeLength == 0) {

                AccessRanges++;
                continue;
            }

            //
            // Watch to see if the VGA resources get added to the
            // requirements list.  If so set a flag so that we know
            // we don't need to reclaim VGA resources in FindAdapter.
            //

            if (VpIsVgaResource(AccessRanges)) {
                DeviceOwningVga = DeviceObject;
            }

            if (AccessRanges->RangeInIoSpace) {
                pioDescript->Type = CmResourceTypePort;
                pioDescript->Flags = CM_RESOURCE_PORT_IO;

                //
                // Disable 10_BIT_DECODE.  This is causing problems for the
                // PnP folks.  If someone has bad hardware, we'll just
                // require them to report all the passive port explicitly.
                //
                //if (VpIsVgaResource(AccessRanges)) {
                //
                //    pioDescript->Flags |= CM_RESOURCE_PORT_10_BIT_DECODE;
                //}

            } else {

                pioDescript->Type = CmResourceTypeMemory;
                pioDescript->Flags = CM_RESOURCE_MEMORY_READ_WRITE;
            }

            if (AccessRanges->RangePassive & VIDEO_RANGE_PASSIVE_DECODE) {
                pioDescript->Flags |= CM_RESOURCE_PORT_PASSIVE_DECODE;
            }

            if (AccessRanges->RangePassive & VIDEO_RANGE_10_BIT_DECODE) {
                pioDescript->Flags |= CM_RESOURCE_PORT_10_BIT_DECODE;
            }

            pioDescript->ShareDisposition =
                    (AccessRanges->RangeShareable ?
                        CmResourceShareShared :
                        CmResourceShareDeviceExclusive);

            pioDescript->Option = IO_RESOURCE_PREFERRED;
            pioDescript->u.Memory.MinimumAddress = AccessRanges->RangeStart;
            pioDescript->u.Memory.MaximumAddress.QuadPart =
                                                   AccessRanges->RangeStart.QuadPart +
                                                   AccessRanges->RangeLength - 1;
            pioDescript->u.Memory.Alignment = 1;
            pioDescript->u.Memory.Length = AccessRanges->RangeLength;

            pioDescript++;
            AccessRanges++;
            RequirementCount++;
        }

        //
        // Update number of elements in list.
        //

        (*RequirementList)->List[0].Count = RequirementCount;
        (*RequirementList)->ListSize = RequirementListSize;
    }

    return STATUS_SUCCESS;
}



VP_STATUS
VideoPortVerifyAccessRanges(
    PVOID HwDeviceExtension,
    ULONG NumAccessRanges,
    PVIDEO_ACCESS_RANGE AccessRanges
    )

/*++

Routine Description:

    VideoPortVerifyAccessRanges


Arguments:

    HwDeviceExtension - Points to the miniport driver's device extension.

    NumAccessRanges - Number of entries in the AccessRanges array.

    AccessRanges - Pointer to an array of AccessRanges the miniport driver
        wants to access.

Return Value:

    ERROR_INVALID_PARAMETER in an error occured
    NO_ERROR if the call completed successfully

Environment:

    This routine cannot be called from a miniport routine synchronized with
    VideoPortSynchronizeRoutine or from an ISR.

--*/

{
    NTSTATUS status;
    BOOLEAN conflict;
    PFDO_EXTENSION fdoExtension = GET_FDO_EXT(HwDeviceExtension);

    //
    // If the device is not enabled then we won't allow the miniport
    // to claim resources for it.
    //

    if (!CheckIoEnabled(
            HwDeviceExtension,
            NumAccessRanges,
            AccessRanges)) {

        return ERROR_INVALID_PARAMETER;
    }

    //
    // All resources not passed in during the START_DEVICE irp should
    // be claimed on the FDO.  We will strip out the PDO resources
    // in pVideoPortReportResourceList if the miniport driver tries
    // to verify ranges acquired through VideoPortGetAccessRanges.
    //

    status = STATUS_SUCCESS;
    conflict = FALSE;

    if ((NT_SUCCESS(status)) && (!conflict)) {

#if DBG

        //
        // Indicates resources have been mapped properly
        //

        InterlockedIncrement(&VPResourcesReported);

#endif

        //
        // Track the resources owned by the VGA driver.
        //

        if (fdoExtension->Flags & VGA_DRIVER) {

            if (VgaAccessRanges != AccessRanges) {

                ULONG Size = NumAccessRanges * sizeof(VIDEO_ACCESS_RANGE);

                if (VgaAccessRanges) {
                    ExFreePool(VgaAccessRanges);
                    VgaAccessRanges = NULL;
                    NumVgaAccessRanges = 0;
                }

                if (NumAccessRanges) {
                    VgaAccessRanges = ExAllocatePoolWithTag(PagedPool, Size, POOL_TAG);

                    if (VgaAccessRanges) {
                        memcpy(VgaAccessRanges, AccessRanges, Size);
                        NumVgaAccessRanges = NumAccessRanges;
                    }
                }
            }
        }

        return NO_ERROR;

    } else {

        return ERROR_INVALID_PARAMETER;

    }

} // end VideoPortVerifyAccessRanges()

BOOLEAN
CheckIoEnabled(
    PVOID HwDeviceExtension,
    ULONG NumAccessRanges,
    PVIDEO_ACCESS_RANGE AccessRanges
    )

/*++

Routine Description:

    This routine ensures that IO is actually enabled if claiming
    IO ranges.

Arguments:

    HwDeviceExtension - Points to the miniport driver's device extension.

    NumAccessRanges - Number of entries in the AccessRanges array.

    AccessRanges - Pointer to an array of AccessRanges the miniport driver
        wants to access.

Return Value:

    TRUE if our IO access checks pass,
    FALSE otherwise.

--*/

{
    PFDO_EXTENSION fdoExtension = GET_FDO_EXT(HwDeviceExtension);

    if (fdoExtension->AdapterInterfaceType == PCIBus) {

        //
        // Check to see if there are any IO ranges in the
        // list or resources.
        //

        ULONG i;
        USHORT Command;

        //
        // Get the PCI Command register for this device.
        //

        VideoPortGetBusData(
            HwDeviceExtension,
            PCIConfiguration,
            0,
            &Command,
            FIELD_OFFSET(PCI_COMMON_CONFIG, Command),
            sizeof(USHORT));

        for (i=0; i<NumAccessRanges; i++) {

            if (AccessRanges[i].RangeInIoSpace) {

                if (!(Command & PCI_ENABLE_IO_SPACE))
                    return FALSE;

            } else {

                if (!(Command & PCI_ENABLE_MEMORY_SPACE))
                    return FALSE;
            }
        }

        return TRUE;

    } else {

        //
        // Non-pci devices will always decode IO operations.
        //

        return TRUE;
    }
}

BOOLEAN
VpIsLegacyAccessRange(
    PFDO_EXTENSION fdoExtension,
    PVIDEO_ACCESS_RANGE AccessRange
    )

/*++

Routine Description:

    This return determines whether a given access range is
    included in the list of legacy access ranges.

Arguments:

    fdoExtension - The FDO extension for the device using the access range.

    AccessRange - The access range to look for in the resource list.

Returns:

    TRUE if the given access range is included in the list of reported
    legacy resources, FALSE otherwise.

--*/

{
    ULONG i;
    PVIDEO_ACCESS_RANGE CurrResource;

    if (fdoExtension->HwLegacyResourceList) {

        CurrResource = fdoExtension->HwLegacyResourceList;

        for (i=0; i<fdoExtension->HwLegacyResourceCount; i++) {

            if ((CurrResource->RangeStart.QuadPart ==
                 AccessRange->RangeStart.QuadPart) &&
                (CurrResource->RangeLength == AccessRange->RangeLength)) {

                return TRUE;
            }

            CurrResource++;
        }
    }

    return FALSE;
}

ULONG
GetCmResourceListSize(
    PCM_RESOURCE_LIST CmResourceList
    )

/*++

Routine Description:

    Get the size in bytes of a CmResourceList.

Arguments:

    CmResourceList - The list for which to get the size.

Returns:

    Size in bytes of the CmResourceList.

--*/

{
    PCM_FULL_RESOURCE_DESCRIPTOR    pcmFull;
    PCM_PARTIAL_RESOURCE_LIST       pcmPartial;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR pcmDescript;
    ULONG i, j;

    pcmFull = &(CmResourceList->List[0]);
    for (i=0; i<CmResourceList->Count; i++) {

        pcmPartial = &(pcmFull->PartialResourceList);
        pcmDescript = &(pcmPartial->PartialDescriptors[0]);
        pcmDescript += pcmPartial->Count;
        pcmFull = (PCM_FULL_RESOURCE_DESCRIPTOR) pcmDescript;
    }

    return (ULONG)(((ULONG_PTR)pcmFull) - ((ULONG_PTR)CmResourceList));
}

PCM_RESOURCE_LIST
VpRemoveFromResourceList(
    PCM_RESOURCE_LIST OriginalList,
    ULONG NumAccessRanges,
    PVIDEO_ACCESS_RANGE AccessRanges
    )

/*++

Routine Description:

    Creates a new CmResourceList with the given access ranges
    removed.

Arguments:

    OriginalList - The original CmResourceList to operate on.

    NumAccessRanges - The number of entries in the remove list.

    AccessRanges - The list of ranges which should be removed from
        the list.

Returns:

    A pointer to the new CmResourceList.

Notes:

    The caller is responsible for freeing the memory returned by this
    function.

--*/

{
    PCM_RESOURCE_LIST FilteredList;
    ULONG Size = GetCmResourceListSize(OriginalList);
    ULONG remainingLength;
    ULONG ResourcesRemoved;

    FilteredList = ExAllocatePoolWithTag(PagedPool, Size, POOL_TAG);

    if (FilteredList) {

        ULONG i, j, k;
        PCM_FULL_RESOURCE_DESCRIPTOR    pcmFull;
        PCM_PARTIAL_RESOURCE_LIST       pcmPartial;
        PCM_PARTIAL_RESOURCE_DESCRIPTOR pcmDescript;

        //
        // Make a copy of the original list.
        //

        memcpy(FilteredList, OriginalList, Size);
        remainingLength = Size - sizeof(CM_RESOURCE_LIST);

        pcmFull = &(FilteredList->List[0]);
        for (i=0; i<FilteredList->Count; i++) {

            pcmPartial = &(pcmFull->PartialResourceList);
            pcmDescript = &(pcmPartial->PartialDescriptors[0]);

            ResourcesRemoved = 0;

            for (j=0; j<pcmPartial->Count; j++) {

                //
                // See if the current resource is in our legacy list.
                //

                for (k=0; k<NumAccessRanges; k++) {

                    if ((pcmDescript->u.Memory.Start.LowPart ==
                         AccessRanges[k].RangeStart.LowPart) &&
                        (AccessRanges[k].RangeStart.LowPart != 0xC0000)) {

                        //
                        // Remove the resource.
                        //

                        memmove(pcmDescript,
                                pcmDescript + 1,
                                remainingLength);

                        pcmDescript--;
                        ResourcesRemoved++;

                        break;
                    }
                }

                remainingLength -= sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
                pcmDescript++;
            }

            //
            // Update the resource count in the partial resource list
            //

            pcmPartial->Count -= ResourcesRemoved;
            if (pcmPartial->Count == 0) {
                FilteredList->Count--;
            }

            remainingLength -= sizeof(CM_PARTIAL_RESOURCE_LIST);
            pcmFull = (PCM_FULL_RESOURCE_DESCRIPTOR) pcmDescript;
        }

    } else {

        //
        // Make sure we always return a list.
        //

        ASSERT(FALSE);
        FilteredList = OriginalList;
    }

    return FilteredList;
}
