/*++

Copyright (c) 1991  Microsoft Corporation
All rights reserved

Module Name:

    rangesup.c

Abstract:

    Supplies support function for dealing with SUPPORTED_RANGEs.

Author:

    Ken Reneris (kenr) March-27-1995

Environment:

    Kernel mode only.

Revision History:


*/

#include "halp.h"

#define STATIC

STATIC ULONG
HalpSortRanges (
    IN PSUPPORTED_RANGE     pRange1
    );

typedef struct tagNRParams {
    PIO_RESOURCE_DESCRIPTOR     InDesc;
    PIO_RESOURCE_DESCRIPTOR     OutDesc;
    PSUPPORTED_RANGE            CurrentPosition;
    LONGLONG                    Base;
    LONGLONG                    Limit;
    UCHAR                       DescOpt;
    BOOLEAN                     AnotherListPending;
} NRPARAMS, *PNRPARAMS;

STATIC PIO_RESOURCE_DESCRIPTOR
HalpGetNextSupportedRange (
    IN LONGLONG             MinimumAddress,
    IN LONGLONG             MaximumAddress,
    IN OUT PNRPARAMS        PNRParams
    );

//
// These following functions are usable at to initialize
// the supported_ranges information for a bus handler.
//    HalpMergeRanges           - merges two bus supported ranges
//    HalpMergeRangeList        - merges two single supported ranges lists
//    HalpCopyRanges            - copy a bus supported ranges to a new supported ranges structure
//    HalpAddRangeList          - adds a supported range list to another
//    HalpAddRange              - adds a single range to a supported range list
//    HalpRemoveRanges          - removes all ranges from one buses supported ranges from another
//    HalpRemoveRangeList       - removes all ranges in one supported range list from another
//    HalpRemoveRange           - removes a single range from a supported range list
//    HalpAllocateNewRangeList  - allocates a new, "blank" bus supported ranges structure
//    HalpFreeRangeList         - frees an entire bus supported ranges
//
//    HalpConsolidateRanges     - cleans up a supported ranges structure to be ready for usage
//
//
// These functions are used to intersect a buses supported ranges
// to an IO_RESOURCE_REQUIREMENTS_LIST:
//    HaliAdjustResourceListRange
//
// These functions are used internal to this module:
//    HalpSortRanges
//    HalpGetNextSupportedRange
//


#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,HalpMergeRanges)
#pragma alloc_text(INIT,HalpMergeRangeList)
#pragma alloc_text(INIT,HalpCopyRanges)
#pragma alloc_text(INIT,HalpAddRangeList)
#pragma alloc_text(INIT,HalpAddRange)
#pragma alloc_text(INIT,HalpRemoveRanges)
#pragma alloc_text(INIT,HalpRemoveRangeList)
#pragma alloc_text(INIT,HalpRemoveRange)
#pragma alloc_text(INIT,HalpConsolidateRanges)
#pragma alloc_text(PAGE,HalpAllocateNewRangeList)
#pragma alloc_text(PAGE,HalpFreeRangeList)
#pragma alloc_text(PAGE,HaliAdjustResourceListRange)
#pragma alloc_text(PAGE,HalpSortRanges)
#pragma alloc_text(PAGE,HalpGetNextSupportedRange)
#endif

#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("PAGECONST")
#endif
struct {
    ULONG       Offset;
} const HalpRangeList[] = {
    FIELD_OFFSET (SUPPORTED_RANGES, IO),
    FIELD_OFFSET (SUPPORTED_RANGES, Memory),
    FIELD_OFFSET (SUPPORTED_RANGES, PrefetchMemory),
    FIELD_OFFSET (SUPPORTED_RANGES, Dma),
    0,
    };
#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif

#define RANGE_LIST(a,i) ((PSUPPORTED_RANGE) ((PUCHAR) a + HalpRangeList[i].Offset))


PSUPPORTED_RANGES
HalpMergeRanges (
    IN PSUPPORTED_RANGES    Parent,
    IN PSUPPORTED_RANGES    Child
    )
/*++
Routine Description:

    This function produces a NewList which is a subset of all overlapping
    ranges in Parent and Child for all range lists.

    The resulting SystemBaseAddresses and SystemAddressSpaces are taken
    from the Child supported ranges.

    Note: Resulting list needs consolidated

--*/
{
    PSUPPORTED_RANGES   NewList;
    PSUPPORTED_RANGES   List1;

    NewList = HalpAllocateNewRangeList();

    HalpMergeRangeList (&NewList->IO,     &Parent->IO,     &Child->IO);
    HalpMergeRangeList (&NewList->Dma,    &Parent->Dma,    &Child->Dma);
    HalpMergeRangeList (&NewList->Memory, &Parent->Memory, &Child->Memory);

    List1  = HalpAllocateNewRangeList();
    HalpAddRangeList (&List1->Memory, &Parent->Memory);
    HalpAddRangeList (&List1->Memory, &Parent->PrefetchMemory);
    HalpMergeRangeList (&NewList->PrefetchMemory, &List1->Memory, &Child->PrefetchMemory);
    HalpFreeRangeList (List1);

    return NewList;
}


VOID
HalpMergeRangeList (
    OUT PSUPPORTED_RANGE    NewList,
    IN PSUPPORTED_RANGE     Parent,
    IN PSUPPORTED_RANGE     Child
    )
/*++
Routine Description:

    Completes NewList to be a subset of all overlapping
    ranges in the Parent and Child list.

    The resulting SystemBaseAddresses and SystemAddressSpaces are
    taken from the Child supported ranges.

    Note: Resulting list needs consolidated

--*/
{
    BOOLEAN             HeadCompleted;
    PSUPPORTED_RANGE    List1, List2;
    LONGLONG            Base, Limit;

    HeadCompleted  = FALSE;

    for (List1 = Parent; List1; List1 = List1->Next) {
        for (List2 = Child; List2; List2 = List2->Next) {

            Base  = List1->Base;
            Limit = List1->Limit;

            //
            // Clip to range supported by List2
            //

            if (Base < List2->Base) {
                Base = List2->Base;
            }

            if (Limit > List2->Limit) {
                Limit = List2->Limit;
            }

            //
            // If valid range, add it
            //

            if (Base <= Limit) {
                if (HeadCompleted) {
                    NewList->Next = ExAllocatePoolWithTag (
                                        SPRANGEPOOL,
                                        sizeof (SUPPORTED_RANGE),
                                        HAL_POOL_TAG
                                        );
                    RtlZeroMemory (NewList->Next, sizeof (SUPPORTED_RANGE));
                    NewList = NewList->Next;
                    NewList->Next = NULL;
                }

                HeadCompleted  = TRUE;
                NewList->Base  = Base;
                NewList->Limit = Limit;
                NewList->SystemBase = List2->SystemBase;
                NewList->SystemAddressSpace = List2->SystemAddressSpace;
            }
        }
    }
}

PSUPPORTED_RANGES
HalpCopyRanges (
    PSUPPORTED_RANGES     Source
    )
/*++
Routine Description:

    Builds a copy of the Source list to the destination list.
    Note that an invalid entry lands at the begining of the copy, but
    that's OK - it will be pulled out at consolidation time.

    Note: Resulting list needs consolidated

--*/
{
    PSUPPORTED_RANGES   Dest;
    ULONG               i;

    Dest = HalpAllocateNewRangeList ();

    for (i=0; HalpRangeList[i].Offset; i++) {
        HalpAddRangeList (RANGE_LIST(Dest, i), RANGE_LIST(Source, i));
    }

    return Dest;
}

VOID
HalpAddRangeList (
    IN OUT PSUPPORTED_RANGE DRange,
    OUT PSUPPORTED_RANGE    SRange
    )
/*++
Routine Description:

    Adds ranges from SRange to DRange.

--*/
{
    while (SRange) {
        HalpAddRange (
            DRange,
            SRange->SystemAddressSpace,
            SRange->SystemBase,
            SRange->Base,
            SRange->Limit
            );

        SRange = SRange->Next;
    }
}


VOID
HalpAddRange (
    PSUPPORTED_RANGE    HRange,
    ULONG               AddressSpace,
    LONGLONG            SystemBase,
    LONGLONG            Base,
    LONGLONG            Limit
    )
/*++
Routine Description:

    Adds a range to the supported list.  Here we just add the range, if it's
    a duplicate it will be removed later at consolidation time.

--*/
{
    PSUPPORTED_RANGE  Range;

    Range = ExAllocatePoolWithTag (
                SPRANGEPOOL,
                sizeof (SUPPORTED_RANGE),
                HAL_POOL_TAG
                );
    RtlZeroMemory (Range, sizeof (SUPPORTED_RANGE));
    Range->Next  = HRange->Next;
    HRange->Next = Range;

    Range->Base = Base;
    Range->Limit = Limit;
    Range->SystemBase = SystemBase;
    Range->SystemAddressSpace = AddressSpace;
}

VOID
HalpRemoveRanges (
    IN OUT PSUPPORTED_RANGES    Minuend,
    IN PSUPPORTED_RANGES        Subtrahend
    )
/*++
Routine Description:

    Returns a list where all ranges from Subtrahend are removed from Minuend.

    Note: Resulting list needs consolidated

--*/
{

    HalpRemoveRangeList (&Minuend->IO,       &Subtrahend->IO);
    HalpRemoveRangeList (&Minuend->Dma,      &Subtrahend->Dma);
    HalpRemoveRangeList (&Minuend->Memory,   &Subtrahend->Memory);
    HalpRemoveRangeList (&Minuend->Memory,   &Subtrahend->PrefetchMemory);
    HalpRemoveRangeList (&Minuend->PrefetchMemory, &Subtrahend->PrefetchMemory);
    HalpRemoveRangeList (&Minuend->PrefetchMemory, &Subtrahend->Memory);
}

VOID
HalpRemoveRangeList (
    IN OUT PSUPPORTED_RANGE Minuend,
    IN PSUPPORTED_RANGE     Subtrahend
    )
/*++
Routine Description:

    Removes all ranges from Subtrahend from Minuend

    ranges in Source1 and Source1 list

--*/
{
    while (Subtrahend) {

        HalpRemoveRange (
            Minuend,
            Subtrahend->Base,
            Subtrahend->Limit
        );

        Subtrahend = Subtrahend->Next;
    }
}


VOID
HalpRemoveRange (
    PSUPPORTED_RANGE    HRange,
    LONGLONG            Base,
    LONGLONG            Limit
    )
/*++
Routine Description:

    Removes the range Base-Limit from the the HRange list

    Note: The returned list needs consolidated, as some entries
    may be turned into "null ranges".

--*/
{
    PSUPPORTED_RANGE    Range;

    //
    // If range isn't a range at all, then nothing to remove
    //

    if (Limit < Base) {
        return ;
    }


    //
    // Clip any area not to include this range
    //

    for (Range = HRange; Range; Range = Range->Next) {

        if (Range->Limit < Range->Base) {
            continue;
        }

        if (Range->Base < Base) {
            if (Range->Limit >= Base  &&  Range->Limit <= Limit) {
                // truncate
                Range->Limit = Base - 1;
            }

            if (Range->Limit > Limit) {

                //
                // Target area is contained totally within this area.
                // Split into two ranges
                //

                HalpAddRange (
                    HRange,
                    Range->SystemAddressSpace,
                    Range->SystemBase,
                    Limit + 1,
                    Range->Limit
                    );

                Range->Limit = Base - 1;

            }
        } else {
            // Range->Base >= Base
            if (Range->Base <= Limit) {
                if (Range->Limit <= Limit) {
                    //
                    // This range is totally within the target area.  Remove it.
                    // (make it invalid - it will get remove when colsolidated)
                    //

                    Range->Base  = 1;
                    Range->Limit = 0;

                } else {
                    // Bump begining
                    Range->Base = Limit + 1;
                }
            }
        }
    }
}

PSUPPORTED_RANGES
HalpConsolidateRanges (
    IN OUT PSUPPORTED_RANGES   Ranges
    )
/*++
Routine Description:

    Cleans the Range list.   Consolidates overlapping ranges, removes
    ranges which don't have any size, etc...

    The returned Ranges list is a clean as possible, and is now ready
    to be used.

--*/
{
    PSUPPORTED_RANGE    RangeList, List1, List2;
    LONGLONG            Base, Limit, SystemBase;
    ULONG               i, AddressSpace;
    LONGLONG            l;

    ASSERT (Ranges != NULL);

    for (i=0; HalpRangeList[i].Offset; i++) {
        RangeList = RANGE_LIST(Ranges, i);

        //
        // Sort the list by base address
        //

        for (List1 = RangeList; List1; List1 = List1->Next) {
            for (List2 = List1->Next; List2; List2 = List2->Next) {
                if (List2->Base < List1->Base) {
                    Base = List1->Base;
                    Limit = List1->Limit;
                    SystemBase = List1->SystemBase;
                    AddressSpace = List1->SystemAddressSpace;

                    List1->Base = List2->Base;
                    List1->Limit = List2->Limit;
                    List1->SystemBase = List2->SystemBase;
                    List1->SystemAddressSpace = List2->SystemAddressSpace;

                    List2->Base = Base;
                    List2->Limit = Limit;
                    List2->SystemBase = SystemBase;
                    List2->SystemAddressSpace = AddressSpace;
                }
            }
        }

        //
        // Check for adjacent/overlapping ranges and combined them
        //

        List1 = RangeList;
        while (List1  &&  List1->Next) {

            if (List1->Limit < List1->Base) {
                //
                // This range's limit is less then it's base.  This
                // entry doesn't reprent anything uasable, remove it.
                //

                List2 = List1->Next;

                List1->Next = List2->Next;
                List1->Base = List2->Base;
                List1->Limit = List2->Limit;
                List1->SystemBase = List2->SystemBase;
                List1->SystemAddressSpace = List2->SystemAddressSpace;

                ExFreePool (List2);
                continue;
            }

            l = List1->Limit + 1;
            if (l > List1->Limit  &&  l >= List1->Next->Base &&
                (List1->SystemBase == List1->Next->SystemBase)) {

                //
                // Overlapping.  Combine them.
                //

                List2 = List1->Next;
                List1->Next = List2->Next;
                if (List2->Limit > List1->Limit) {
                    List1->Limit = List2->Limit;
                    ASSERT (List1->SystemAddressSpace == List2->SystemAddressSpace);
                }

                ExFreePool (List2);
                continue ;
            }

            List1 = List1->Next;
        }

        //
        // If the last range is invalid, and it's not the only
        // thing in the list - remove it
        //

        if (List1 != RangeList  &&  List1->Limit < List1->Base) {
            for (List2=RangeList; List2->Next != List1; List2 = List2->Next) ;
            List2->Next = NULL;
            ExFreePool (List1);
        }
    }

    return Ranges;
}


PSUPPORTED_RANGES
HalpAllocateNewRangeList (
    VOID
    )
/*++

Routine Description:

    Allocates a range list

--*/
{
    PSUPPORTED_RANGES   RangeList;
    ULONG               i;

    RangeList = (PSUPPORTED_RANGES) ExAllocatePoolWithTag (
                                        SPRANGEPOOL,
                                        sizeof (SUPPORTED_RANGES),
                                        HAL_POOL_TAG
                                        );
    RtlZeroMemory (RangeList, sizeof (SUPPORTED_RANGES));
    RangeList->Version = BUS_SUPPORTED_RANGE_VERSION;

    for (i=0; HalpRangeList[i].Offset; i++) {
        // Limit set to zero, set initial base to 1
        RANGE_LIST(RangeList, i)->Base = 1;
    }
    return RangeList;
}


VOID
HalpFreeRangeList (
    PSUPPORTED_RANGES   Ranges
    )
/*++

Routine Description:

    Frees a range list which was allocated via HalpAllocateNewRangeList, and
    extended / modified via the generic support functions.


--*/
{
    PSUPPORTED_RANGE    Entry, NextEntry;
    ULONG               i;

    for (i=0; HalpRangeList[i].Offset; i++) {
        Entry = RANGE_LIST(Ranges, i)->Next;

        while (Entry) {
            NextEntry = Entry->Next;
            ExFreePool (Entry);
            Entry = NextEntry;
        }
    }

    ExFreePool (Ranges);
}


#if DBG
STATIC VOID
HalpDisplayAddressRange (
    PSUPPORTED_RANGE    Address,
    PUCHAR              String
    )
/*++

Routine Description:

    Debugging code.  Used only by HalpDisplayAllBusRanges

--*/
{
    ULONG       i;

    i = 0;
    while (Address) {
        if (i == 0) {
            DbgPrint (String);
            i = 3;
        }

        i -= 1;
        DbgPrint (" %x:%08x - %x:%08x ",
            (ULONG) (Address->Base >> 32),
            (ULONG) (Address->Base),
            (ULONG) (Address->Limit >> 32),
            (ULONG) (Address->Limit)
            );

        Address = Address->Next;
    }
}

VOID
HalpDisplayAllBusRanges (
    VOID
    )
/*++

Routine Description:

    Debugging code.  Displays the current supported range information
    for all the registered buses in the system.

--*/
{
    PSUPPORTED_RANGES   Addresses;
    PBUS_HANDLER        Bus;
    PUCHAR              p;
    ULONG               i, j;

    DbgPrint ("\nHAL - dumping all supported bus ranges");

    for (i=0; i < MaximumInterfaceType; i++) {
        for (j=0; Bus = HaliHandlerForBus (i, j); j++) {
            Addresses = Bus->BusAddresses;
            if (Addresses) {
                p = NULL;
                switch (Bus->InterfaceType) {
                    case Internal:  p = "Internal";     break;
                    case Isa:       p = "Isa";          break;
                    case Eisa:      p = "Eisa";         break;
                    case PCIBus:    p = "PCI";          break;
                }
                if (p) {
                    DbgPrint ("\n%s %d", p, Bus->BusNumber);
                } else {
                    DbgPrint ("\nBus-%d %d", Bus->InterfaceType, Bus->BusNumber);
                }
                HalpDisplayAddressRange (&Addresses->IO,            "\n  IO......:");
                HalpDisplayAddressRange (&Addresses->Memory,        "\n  Memory..:");
                HalpDisplayAddressRange (&Addresses->PrefetchMemory,"\n  PFMemory:");
                HalpDisplayAddressRange (&Addresses->Dma,           "\n  Dma.....:");
                DbgPrint ("\n");
            }
        }
    }
}
#endif

NTSTATUS
HaliAdjustResourceListRange (
    IN PSUPPORTED_RANGES                    SRanges,
    IN PSUPPORTED_RANGE                     InterruptRange,
    IN OUT PIO_RESOURCE_REQUIREMENTS_LIST   *pResourceList
    )
/*++

Routine Description:

    This functions takes an IO_RESOURCE_REQUIREMENT_LIST and
    adjusts it such that all ranges in the list fit in the
    ranges specified by SRanges & InterruptRange.

    This function is used by some HALs to clip the possible
    settings to be contained on what the particular bus supports
    in reponse to a HalAdjustResourceList call.

Arguments:

    SRanges         - Valid IO, Memory, Prefetch Memory, and DMA ranges.
    InterruptRange  - Valid InterruptRanges

    pResourceList   - The resource requirements list which needs to
                      be adjusted to only contain the ranges as
                      described by SRanges & InterruptRange.

Return Value:

    STATUS_SUCCESS or an appropiate error return.

--*/
{
    PIO_RESOURCE_REQUIREMENTS_LIST  InCompleteList, OutCompleteList;
    PIO_RESOURCE_LIST               InResourceList, OutResourceList;
    PIO_RESOURCE_DESCRIPTOR         HeadOutDesc, SetDesc;
    NRPARAMS                        Pos;
    ULONG                           len, alt, cnt, i;
    ULONG                           icnt;

    //
    // Sanity check
    //

    if (!SRanges  ||  SRanges->Version != BUS_SUPPORTED_RANGE_VERSION) {
        return STATUS_INVALID_PARAMETER;
    }

    //
    // If SupportedRanges aren't sorted, sort them and get the
    // number of ranges for each type
    //

    if (!SRanges->Sorted) {
        SRanges->NoIO = HalpSortRanges (&SRanges->IO);
        SRanges->NoMemory = HalpSortRanges (&SRanges->Memory);
        SRanges->NoPrefetchMemory = HalpSortRanges (&SRanges->PrefetchMemory);
        SRanges->NoDma = HalpSortRanges (&SRanges->Dma);
        SRanges->Sorted = TRUE;
    }

    icnt = HalpSortRanges (InterruptRange);

    InCompleteList = *pResourceList;
    len = InCompleteList->ListSize;

    //
    // Scan input list - verify revision #'s, and increase len varible
    // by amount output list may increase.
    //

    i = 1;
    InResourceList = InCompleteList->List;
    for (alt=0; alt < InCompleteList->AlternativeLists; alt++) {
        if (InResourceList->Version != 1 || InResourceList->Revision < 1) {
            return STATUS_INVALID_PARAMETER;
        }

        Pos.InDesc  = InResourceList->Descriptors;
        for (cnt = InResourceList->Count; cnt; cnt--) {
            switch (Pos.InDesc->Type) {
                case CmResourceTypeInterrupt:  i += icnt;           break;
                case CmResourceTypePort:       i += SRanges->NoIO;  break;
                case CmResourceTypeDma:        i += SRanges->NoDma; break;

                case CmResourceTypeMemory:
                    i += SRanges->NoMemory;
                    if (Pos.InDesc->Flags & CM_RESOURCE_MEMORY_PREFETCHABLE) {
                        i += SRanges->NoPrefetchMemory;
                    }
                    break;

                default:
                    return STATUS_INVALID_PARAMETER;
            }

            // take one off for the original which is already accounted for in 'len'
            i -= 1;

            // Next descriptor
            Pos.InDesc++;
        }

        // Next Resource List
        InResourceList  = (PIO_RESOURCE_LIST) Pos.InDesc;
    }
    len += i * sizeof (IO_RESOURCE_DESCRIPTOR);

    //
    // Allocate output list
    //

    OutCompleteList = (PIO_RESOURCE_REQUIREMENTS_LIST)
                            ExAllocatePoolWithTag (PagedPool,
                                                   len,
                                                   ' laH');

    if (!OutCompleteList) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory (OutCompleteList, len);

    //
    // Walk each ResourceList and build output structure
    //

    InResourceList   = InCompleteList->List;
    *OutCompleteList = *InCompleteList;
    OutResourceList  = OutCompleteList->List;

    for (alt=0; alt < InCompleteList->AlternativeLists; alt++) {
        OutResourceList->Version  = 1;
        OutResourceList->Revision = 1;

        Pos.InDesc  = InResourceList->Descriptors;
        Pos.OutDesc = OutResourceList->Descriptors;
        HeadOutDesc = Pos.OutDesc;

        for (cnt = InResourceList->Count; cnt; cnt--) {

            //
            // Limit desctiptor to be with the buses supported ranges
            //

            Pos.DescOpt = Pos.InDesc->Option;
            Pos.AnotherListPending = FALSE;

            switch (Pos.InDesc->Type) {
                case CmResourceTypePort:

                    //
                    // Get supported IO ranges
                    //

                    Pos.CurrentPosition = &SRanges->IO;
                    do {
                        SetDesc = HalpGetNextSupportedRange (
                                    Pos.InDesc->u.Port.MinimumAddress.QuadPart,
                                    Pos.InDesc->u.Port.MaximumAddress.QuadPart,
                                    &Pos
                                    );

                        if (SetDesc) {
                            SetDesc->u.Port.MinimumAddress.QuadPart = Pos.Base;
                            SetDesc->u.Port.MaximumAddress.QuadPart = Pos.Limit;
                        }

                    } while (SetDesc) ;
                    break;

                case CmResourceTypeInterrupt:
                    //
                    // Get supported Interrupt ranges
                    //

                    Pos.CurrentPosition = InterruptRange;
                    do {
                        SetDesc = HalpGetNextSupportedRange (
                                    Pos.InDesc->u.Interrupt.MinimumVector,
                                    Pos.InDesc->u.Interrupt.MaximumVector,
                                    &Pos
                                    );

                        if (SetDesc) {
                            SetDesc->u.Interrupt.MinimumVector = (ULONG) Pos.Base;
                            SetDesc->u.Interrupt.MaximumVector = (ULONG) Pos.Limit;
                        }
                    } while (SetDesc) ;
                    break;

                case CmResourceTypeMemory:
                    //
                    // Get supported memory ranges
                    //

                    if (Pos.InDesc->Flags & CM_RESOURCE_MEMORY_PREFETCHABLE) {

                        //
                        // This is a Prefetchable range.
                        // First add in any supported prefetchable ranges, then
                        // add in any regualer supported ranges
                        //

                        Pos.AnotherListPending = TRUE;
                        Pos.CurrentPosition = &SRanges->PrefetchMemory;

                        do {
                            SetDesc = HalpGetNextSupportedRange (
                                        Pos.InDesc->u.Memory.MinimumAddress.QuadPart,
                                        Pos.InDesc->u.Memory.MaximumAddress.QuadPart,
                                        &Pos
                                        );

                            if (SetDesc) {
                                SetDesc->u.Memory.MinimumAddress.QuadPart = Pos.Base;
                                SetDesc->u.Memory.MaximumAddress.QuadPart = Pos.Limit;
                                SetDesc->Option |= IO_RESOURCE_PREFERRED;
                            }
                        } while (SetDesc) ;

                        Pos.AnotherListPending = FALSE;
                    }

                    //
                    // Add in supported bus memory ranges
                    //

                    Pos.CurrentPosition = &SRanges->Memory;
                    do {
                        SetDesc = HalpGetNextSupportedRange (
                                        Pos.InDesc->u.Memory.MinimumAddress.QuadPart,
                                        Pos.InDesc->u.Memory.MaximumAddress.QuadPart,
                                        &Pos
                                        );
                        if (SetDesc) {
                            SetDesc->u.Memory.MinimumAddress.QuadPart = Pos.Base;
                            SetDesc->u.Memory.MaximumAddress.QuadPart = Pos.Limit;
                        }
                    } while (SetDesc);
                    break;

                case CmResourceTypeDma:
                    //
                    // Get supported DMA ranges
                    //

                    Pos.CurrentPosition = &SRanges->Dma;
                    do {
                        SetDesc = HalpGetNextSupportedRange (
                                    Pos.InDesc->u.Dma.MinimumChannel,
                                    Pos.InDesc->u.Dma.MaximumChannel,
                                    &Pos
                                    );

                        if (SetDesc) {
                            SetDesc->u.Dma.MinimumChannel = (ULONG) Pos.Base;
                            SetDesc->u.Dma.MaximumChannel = (ULONG) Pos.Limit;
                        }
                    } while (SetDesc) ;
                    break;

#if DBG
                default:
                    DbgPrint ("HalAdjustResourceList: Unkown resource type\n");
                    break;
#endif
            }

            //
            // Next descriptor
            //

            Pos.InDesc++;
        }

        OutResourceList->Count = (ULONG)(Pos.OutDesc - HeadOutDesc);

        //
        // Next Resource List
        //

        InResourceList  = (PIO_RESOURCE_LIST) Pos.InDesc;
        OutResourceList = (PIO_RESOURCE_LIST) Pos.OutDesc;
    }

    //
    // Free input list, and return output list
    //

    ExFreePool (InCompleteList);

    OutCompleteList->ListSize = (ULONG) ((PUCHAR) OutResourceList - (PUCHAR) OutCompleteList);
    *pResourceList = OutCompleteList;
    return STATUS_SUCCESS;
}


STATIC PIO_RESOURCE_DESCRIPTOR
HalpGetNextSupportedRange (
    IN LONGLONG             MinimumAddress,
    IN LONGLONG             MaximumAddress,
    IN OUT PNRPARAMS        Pos
    )
/*++

Routine Description:

    Support function for HaliAdjustResourceListRange.
    Returns the next supported range in the area passed in.

Arguments:

    MinimumAddress
    MaximumAddress  - Min & Max address of a range which needs
                      to be clipped to match that of the supported
                      ranges of the current bus.

    Pos             - describes the current postion

Return Value:

    NULL is no more returned ranges

    Otherwise, the IO_RESOURCE_DESCRIPTOR which needs to be set
    with the matching range returned in Pos.

--*/
{
    LONGLONG        Base, Limit;

    //
    // Find next range which is supported
    //

    Base  = MinimumAddress;
    Limit = MaximumAddress;

    while (Pos->CurrentPosition) {
        Pos->Base  = Base;
        Pos->Limit = Limit;

        //
        // Clip to current range
        //

        if (Pos->Base < Pos->CurrentPosition->Base) {
            Pos->Base = Pos->CurrentPosition->Base;
        }

        if (Pos->Limit > Pos->CurrentPosition->Limit) {
            Pos->Limit = Pos->CurrentPosition->Limit;
        }

        //
        // set position to next range
        //

        Pos->CurrentPosition = Pos->CurrentPosition->Next;

        //
        // If valid range, return it
        //

        if (Pos->Base <= Pos->Limit) {
            *Pos->OutDesc = *Pos->InDesc;
            Pos->OutDesc->Option = Pos->DescOpt;

            //
            // next descriptor (if any) is an alternative
            // to the descriptor being returned now
            //

            Pos->OutDesc += 1;
            Pos->DescOpt |= IO_RESOURCE_ALTERNATIVE;
            return Pos->OutDesc - 1;
        }
    }


    //
    // There's no overlapping range.  If this descriptor is
    // not an alternative and this descriptor is not going to
    // be processed by another range list, then return
    // a descriptor which can't be satisified.
    //

    if (!(Pos->DescOpt & IO_RESOURCE_ALTERNATIVE) &&
        Pos->AnotherListPending == FALSE) {
#if DBG
        DbgPrint ("HAL: returning impossible range\n");
#endif
        Pos->Base  = MinimumAddress;
        Pos->Limit = Pos->Base - 1;
        if (Pos->Base == 0) {       // if wrapped, fix it
            Pos->Base  = 1;
            Pos->Limit = 0;
        }

        *Pos->OutDesc = *Pos->InDesc;
        Pos->OutDesc->Option = Pos->DescOpt;

        Pos->OutDesc += 1;
        Pos->DescOpt |= IO_RESOURCE_ALTERNATIVE;
        return Pos->OutDesc - 1;
    }

    //
    // No range found (or no more ranges)
    //

    return NULL;
}

STATIC ULONG
HalpSortRanges (
    IN PSUPPORTED_RANGE     RangeList
    )
/*++

Routine Description:

    Support function for HaliAdjustResourceListRange.
    Sorts a supported range list into decending order.

Arguments:

    pRange  - List to sort

Return Value:

--*/
{
    ULONG               cnt;
    LONGLONG            hldBase, hldLimit, hldSystemBase;
    PSUPPORTED_RANGE    Range1, Range2;

    //
    // Sort it
    //

    for (Range1 = RangeList; Range1; Range1 = Range1->Next) {
        for (Range2 = Range1->Next; Range2; Range2 = Range2->Next) {

            if (Range2->Base > Range1->Base) {
                hldBase  = Range1->Base;
                hldLimit = Range1->Limit;
                hldSystemBase = Range1->SystemBase;

                Range1->Base  = Range2->Base;
                Range1->Limit = Range2->Limit;
                Range1->SystemBase = Range2->SystemBase;

                Range2->Base  = hldBase;
                Range2->Limit = hldLimit;
                Range2->SystemBase = hldSystemBase;
            }
        }
    }

    //
    // Count the number of ranges
    //

    cnt = 0;
    for (Range1 = RangeList; Range1; Range1 = Range1->Next) {
        cnt += 1;
    }

    return cnt;
}