/*++
*******************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved				*
* 																										*
* This software contains proprietary and confidential information of Digi		*
* International Inc.  By accepting transfer of this copy, Recipient agrees		*
* to retain this software in confidence, to prevent disclosure to others,		*
* and to make no use of this software other than that for which it was			*
* delivered.  This is an unpublished copyrighted work of Digi International	*
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	*
* prohibited.																						*
* 																										*
* Restricted Rights Legend																		*
*																										*
* Use, duplication, or disclosure by the Government is subject to					*
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in			*
* Technical Data and Computer Software clause at DFARS 252.227-7031 or			*
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -				*
* Restricted Rights at 48 CFR 52.227-19, as applicable.								*
*																										*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343				*
*																										*
*******************************************************************************

Module Name:

   digires.c

Abstract:

   This module contains support routines for accessing and reporting physical 
   adapter resources to NT.

Author:

   Michael Zarns

--*/


#include "digilib.h"


// Define ALLOW_ISA_MEMORY_OVERLAP to allow isa cards to share the same memory window.
#define ALLOW_ISA_MEMORY_OVERLAP

// Define PCI_DEBUG to display 
#define PCI_DEBUG


///////////////////////////////////////////////////////////////////////////////
//
// Global Data
//

STATIC LIST_ENTRY gResourceList;
STATIC BOOLEAN    gResourceListInitialized = FALSE;


///////////////////////////////////////////////////////////////////////////////
//
// Constants and Typedefs
//

// Boolean test whether we're at the end of the list
#define LIST_END(ListHead,Entry)  ( (Entry) == &(ListHead) )

// Get next entry in list
#define GET_NEXT(pEntry)          ( (pEntry)->Flink )


typedef struct DIGI_ADAPTER_RESOURCES
{
	LIST_ENTRY ListEntry;
	INTERFACE_TYPE BusType;
	ULONG BusNumber;
	ULONG CountPartials;
	CM_PARTIAL_RESOURCE_DESCRIPTOR Partials[1];
} DIGI_ADAPTER_RESOURCES, *PDIGI_ADAPTER_RESOURCES;



///////////////////////////////////////////////////////////////////////////////
//
// Internal Routines
//

STATIC BOOLEAN 
DigiDetectConflict( IN PDIGI_ADAPTER_RESOURCES pResource )
/*++

Description:

	NT won't detect conflicts among adapters supported by this driver.  This
	routine compares resources for the specified adapter with all those for
	previously reported adapters.

Parameters:

	PDIGI_ADAPTER_RESOURCES pResource
		Adapter resources struct.  Compare resources requested by this adapter
		with those of all previously reported adapters.

Return Value:

	TRUE if conflict detected.

--*/
{
	ULONG i;

	// Test each resource requested by this adapter
	for ( i = 0; i < pResource->CountPartials; i++ )
	{
		PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartial;
		PLIST_ENTRY pListEntry;

		pPartial = &pResource->Partials[i];

		// Run through entire list of adapter resources (gResourceList) looking
		// for conflicts.

		for ( pListEntry = GET_NEXT( &gResourceList ); 
		      !LIST_END( gResourceList, pListEntry ); 
		      pListEntry = GET_NEXT( pListEntry ) )
	   {
			PDIGI_ADAPTER_RESOURCES pOther;
			ULONG j;

			pOther = CONTAINING_RECORD( pListEntry, DIGI_ADAPTER_RESOURCES, ListEntry );
			
			for ( j = 0; j < pOther->CountPartials; j++ )
			{
				PCM_PARTIAL_RESOURCE_DESCRIPTOR pOtherPartial;
				pOtherPartial = &pOther->Partials[j];

				if ( pPartial->Type == pOtherPartial->Type )
				{
					switch ( pPartial->Type )
					{
						case CmResourceTypePort:
							if ( pPartial->u.Port.Start.QuadPart == 
							     pOtherPartial->u.Port.Start.QuadPart )
								// Conflict!
								return( TRUE );
							break;

						case CmResourceTypeMemory:
							if ( pPartial->u.Memory.Start.QuadPart == 
							     pOtherPartial->u.Memory.Start.QuadPart )
								// Memory overlaps!  We support memory sharing on isa/eisa
								// bus only.
#ifdef ALLOW_ISA_MEMORY_OVERLAP
								if ( ( pResource->BusType != Isa && pResource->BusType != Eisa ) ||
								     ( pOther->BusType != Isa && pOther->BusType != Eisa ) )
#endif
									return( TRUE );
							break;

						case CmResourceTypeInterrupt:
							if ( pPartial->u.Interrupt.Level == 
							     pOtherPartial->u.Interrupt.Level )
								// Conflict!
								return( TRUE );
							break;

						default:
 							DigiDump( DEBUG_ERROR, ( "DigiDetectConflict: unknown resource type!\n" ) );
							break;
					}
				}
			}
	   }
	}
	return( FALSE );        
}


STATIC ULONG 
CountResourceListEntries()
{
	PLIST_ENTRY pListEntry;
	ULONG Count = 0;

	// Run through gResourceList counting entries

	for ( pListEntry = GET_NEXT( &gResourceList ); 
	      !LIST_END( gResourceList, pListEntry ); 
	      pListEntry = GET_NEXT( pListEntry ) )
		Count++;

	return( Count );
}


STATIC ULONG 
CountResources()
{
	PLIST_ENTRY pListEntry;
	PDIGI_ADAPTER_RESOURCES pResources;
	ULONG Count = 0;

	// Run through gResourceList counting reported resources.  
	for ( pListEntry = GET_NEXT( &gResourceList ); 
	      !LIST_END( gResourceList, pListEntry ); 
	      pListEntry = GET_NEXT( pListEntry ) )
   {
		pResources = CONTAINING_RECORD( pListEntry, DIGI_ADAPTER_RESOURCES, ListEntry );
		Count += pResources->CountPartials;
   }

	return( Count );
}


STATIC NTSTATUS
BuildResourceList( 
	OUT PCM_RESOURCE_LIST *pList,
	OUT PULONG pLength
	)
/*++

Description:

	Builds a CM_RESOURCE_LIST including all resources for all adapters reported
	thus far.

	Alloc's space for the list, which must be freed by the calling routine!

--*/
{
	PLIST_ENTRY pListEntry;
	LIST_ENTRY ProcessedList;
	PDIGI_ADAPTER_RESOURCES pResources;
	PCM_FULL_RESOURCE_DESCRIPTOR pFull;
	PCM_RESOURCE_LIST pMyList;
	ULONG MaxLength;

	// We write to these locations, they must be valid pointers!
	ASSERT( pList && pLength );

	// We output the length of CM_RESOURCE_LIST structure.  Initialize it.
	*pLength = 0;

	MaxLength = 
		sizeof( CM_RESOURCE_LIST ) +
		sizeof( CM_FULL_RESOURCE_DESCRIPTOR ) * 
			// subtract 1 because one FULL is already included in CM_RESOURCE_LIST
			( CountResourceListEntries() - 1 ) +    
		sizeof( CM_PARTIAL_RESOURCE_DESCRIPTOR ) * 
			// subtract because one PARTIAL is already included in each FULL descriptor
			( CountResources() - CountResourceListEntries() );

	pMyList = DigiAllocMem( PagedPool, MaxLength );

	if ( !pMyList )
		return( STATUS_INSUFFICIENT_RESOURCES );

	// Output CM_RESOURCE_LIST to be passed into IoReportResources
	RtlZeroMemory( pMyList, MaxLength );

	// Init temp list.  Processed entries are temporarily moved to this list.  
	// Once all entries have been processed, they're all moved back to the normal 
	// list, gResourceList.
	InitializeListHead( &ProcessedList );

	//
	// Fill in the CM_RESOURCE list
	//
	
	pMyList->Count = 0;
	pFull = pMyList->List;

	//
	// Work through the adapter resource list (gResourceList) until all entries
	// have been processed.  When processed, an entry's resources will be added
	// to the CM_RESOURCE_LIST and the entry itself will be temporarily moved 
	// to the local list, ProcessedList.
	//

	while ( !IsListEmpty( &gResourceList ) )
	{
		PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartial;

		// Each time through this loop, we add all resources for adapters on a 
		// single bus (identified by BusType and BusNumber).

		pListEntry = GET_NEXT( &gResourceList );
		pResources = CONTAINING_RECORD( pListEntry, DIGI_ADAPTER_RESOURCES, ListEntry );

		// Fill in the FULL_RESOURCE_DESCRIPTOR
		pFull->InterfaceType = pResources->BusType;
		pFull->BusNumber = pResources->BusNumber;
		pMyList->Count++;

		// Fill in the PARTIAL_RESOURCE_LIST
		pFull->PartialResourceList.Version = 0;
		pFull->PartialResourceList.Revision = 0;
		
		pPartial = pFull->PartialResourceList.PartialDescriptors;

		// Find all adapter resource entries with the same BusType and BusNumber

		while ( !LIST_END( gResourceList, pListEntry ) )
		{
			PLIST_ENTRY pNext;

			pResources = CONTAINING_RECORD( pListEntry, 
							DIGI_ADAPTER_RESOURCES, 
							ListEntry );
			pNext = GET_NEXT( pListEntry );

			if ( ( pResources->BusType == pFull->InterfaceType ) && 
			     ( pResources->BusNumber == pFull->BusNumber ) )
			{
				ULONG i;

				// Add PARTIAL_RESOURCE_DESCRIPTORs

				for ( i = 0; i < pResources->CountPartials; i++ )
				{
					*pPartial++ = pResources->Partials[i];
					pFull->PartialResourceList.Count++;
				}

				// Move entry to Processed list
				RemoveEntryList( pListEntry );
				InsertTailList( &ProcessedList, pListEntry );
			}
			pListEntry = pNext;
		}
		// Start next FULL_RESOURCE_DESCRIPTOR immediately after last 
		// PARTIAL_RESOURCE_DESCRIPTOR.
		pFull = (PVOID)pPartial;
	}

	*pLength = (ULONG)((char *)pFull - (char *)pMyList);
	*pList = pMyList;

	ASSERT( IsListEmpty( &gResourceList ) );

	// Move all entries back onto gResourceList

	while ( !IsListEmpty( &ProcessedList ) )
	{
		PLIST_ENTRY pEntry;
		pEntry = RemoveHeadList( &ProcessedList );
		InsertTailList( &gResourceList, pEntry );
	}

#ifdef PCI_DEBUG
{
	USHORT frd;
	PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartial;

	DigiDump( DEBUG_DIGILIB, ( "ResourceList:\n"
	                           "   Count %d\n", pMyList->Count ) );

	// Init pointer to first FULL_RESOURCE_DESCRIPTOR in list

	pFull = pMyList->List;

	for ( frd = 0; frd < pMyList->Count; frd++ )
	{
		ULONG prd;

		DigiDump( DEBUG_DIGILIB, ( "   List[%d]\n"
		                           "      InterfaceType 0x%x\n"
		                           "      BusNumber 0x%x\n"
		                           "      PartialResourceList\n"
		                           "         Version 0x%x\n"
		                           "         Revision 0x%x\n"
		                           "         Count 0x%x\n",
		                           frd, pFull->InterfaceType, pFull->BusNumber,
		                           pFull->PartialResourceList.Version,
		                           pFull->PartialResourceList.Revision,
		                           pFull->PartialResourceList.Count ) );
		
		// Init pointer to first Partial descriptor
		pPartial = pFull->PartialResourceList.PartialDescriptors;

		for ( prd = 0; prd < pFull->PartialResourceList.Count; prd++ )
		{
			DigiDump( DEBUG_DIGILIB, ( "         PartialDescriptor[%d]\n"
			                           "            Type 0x%x\n"
			                           "            ShareDisposition 0x%x\n"
			                           "            Flags 0x%x\n",
			                           prd, pPartial->Type,
			                           pPartial->ShareDisposition,
			                           pPartial->Flags ) );
			switch ( pPartial->Type )
			{
				case CmResourceTypePort:
					DigiDump( DEBUG_DIGILIB, ( "            Port\n"
					                           "               Start  0x%x%x\n"
					                           "               Length 0x%x\n",
					                           pPartial->u.Port.Start.HighPart,
					                           pPartial->u.Port.Start.LowPart,
					                           pPartial->u.Port.Length ) );
					break;

				case CmResourceTypeMemory:
					DigiDump( DEBUG_DIGILIB, ( "            Memory\n"
					                           "               Start  0x%x%x\n"
					                           "               Length 0x%x\n",
					                           pPartial->u.Memory.Start.HighPart,
					                           pPartial->u.Memory.Start.LowPart,
					                           pPartial->u.Memory.Length ) );
					break;

				case CmResourceTypeInterrupt:
					DigiDump( DEBUG_DIGILIB, ( "            Interrupt\n"
					                           "               Level    0x%x\n"
					                           "               Vector   0x%x\n"
					                           "               Affinity 0x%x\n",
					                           pPartial->u.Interrupt.Level,
					                           pPartial->u.Interrupt.Vector,
					                           pPartial->u.Interrupt.Affinity ) );
					break;

				default:
					DigiDump( DEBUG_DIGILIB, ( "            Unknown type!\n" ) );
					break;
			}
			pPartial++;
		}
		// Next FULL_RESOURCE_DESCRIPTOR starts immediately after.
		pFull = (PVOID) pPartial;
	}
}
#endif
	return( STATUS_SUCCESS );
}	// BuildResourceList


///////////////////////////////////////////////////////////////////////////////
//
// Public Routines
//


NTSTATUS
DigiReportResources( 
	IN  PDRIVER_OBJECT    pDriverObject,
	IN  INTERFACE_TYPE    BusType,
	IN  ULONG             BusNumber,
	IN  PPHYSICAL_ADDRESS pIoBase,
	IN  ULONG             IoLength,
	IN  PPHYSICAL_ADDRESS pMemoryBase,
	IN  ULONG             MemoryLength,
	IN  ULONG             IrqLevel,
	IN  ULONG             IrqVector,
	IN  KAFFINITY         IrqAffinity,
	IN  KINTERRUPT_MODE   IrqMode,
	OUT PBOOLEAN          pbConflictDetected,
	OUT PHANDLE           phResources
	)
/*++

Description:

	Reports resources for an adapter.  This should be called *before* any
	attempting to use the resources to prevent conflicts from crashing the
	system.  When done with the resources, the driver should call 
	DigiUnreportResources.  Values should be the same values passed into
	HalTranslateBusAddress and HalGetInterruptVector.  See DDK 
	documentation for IoReportResourceUsage for more details.

Parameters:

	DriverObject
		Driver object value passed into DriverEntry.  Required for call to
		IoReportResourceUsage.

	BusType
		Bus type for this adapter.  Could be Isa, MicroChannel, PCIBus.

	BusNumber
		System assigned bus number for this bus of the given BusType.

	pIoBase
		Pointer to the IO port base address for the adapter.

	IoLength
		Length in bytes of IO space used starting at IoBase.

	pMemoryBase
		Pointer to the shared memory base address for the adapter.

	MemoryLength;
		Length in bytes of the shared memory starting at MemoryBase.

	IrqLevel
		Specifies the bus-specific interrupt level.  Zero if not using
		interrupts.

	IrqVector
		Specifies the bus-specific interrupt vector.  Ignored if IrqLevel
		is zero.

	IrqAffinity
		Specifies the processor affinity for this interrupt.  Ignored if
		IrqLevel is zero.

	IrqMode
		Specifies either LevelSensitive (i.e. MCA) or Latched (i.e. ISA).

	pbConflictDetected
		Address of BOOLEAN to write whether conflict was detected or not if
		routine returns STATUS_SUCCESS.

	hResources
		Address to write HANDLE of resources object if routine returns 
		STATUS_SUCCESS and pbConflictDetected is false.  Otherwise, returns NULL.

Return Value:

	STATUS_SUCCESS, 
	STATUS_INVALID_PARAMETER, or 
	STATUS_INSUFFICIENT_RESOURCES.

--*/
{
	// Use these to build our DIGI_ADAPTER_RESOURCES struct.
	PDIGI_ADAPTER_RESOURCES pResources;
	ULONG CountPartials;
	ULONG ResourcesLength;
	NTSTATUS Status;

	// Use these to build an NT CM_RESOURCE_LIST
	PCM_RESOURCE_LIST pCmResourceList;
	ULONG ListLength;

	// 
	// Sanity check params
	//

	if ( !pDriverObject || !pbConflictDetected || !phResources )
		return( STATUS_INVALID_PARAMETER );

	if ( ( !pIoBase ) || 
	     ( !pIoBase->HighPart && !pIoBase->LowPart ) || 
	     ( !IoLength ) )
		return( STATUS_INVALID_PARAMETER );

	if ( ( !pMemoryBase ) || 
	     ( !pMemoryBase->HighPart && !pMemoryBase->LowPart ) || 
	     ( !MemoryLength ) )
		return( STATUS_INVALID_PARAMETER );

	//
	// Initialize the list if this is the first time using it
	//
	
	if ( FALSE == gResourceListInitialized )
	{
		InitializeListHead( &gResourceList );
		gResourceListInitialized = TRUE;
	}

	//
	// Alloc adapter resources struct and fill it in
	//

	// One partial for each resource: io, mem, and irq (if used)
	CountPartials   = ( IrqLevel ? 3 : 2 );
	ResourcesLength = sizeof( DIGI_ADAPTER_RESOURCES ) + 
	                  ( CountPartials - 1 ) *
	                  sizeof( CM_PARTIAL_RESOURCE_DESCRIPTOR );

	pResources = DigiAllocMem( NonPagedPool, ResourcesLength );
	
	if ( !pResources )
		return( STATUS_INSUFFICIENT_RESOURCES );

	RtlZeroMemory( pResources, ResourcesLength );

	pResources->BusType       = BusType;
	pResources->BusNumber     = BusNumber;
	pResources->CountPartials = CountPartials;

	pResources->Partials[0].Type             = CmResourceTypePort;
	pResources->Partials[0].ShareDisposition = CmResourceShareDeviceExclusive;
	pResources->Partials[0].Flags            = CM_RESOURCE_PORT_IO;
	pResources->Partials[0].u.Port.Start     = *pIoBase;
	pResources->Partials[0].u.Port.Length    = IoLength;
	
	pResources->Partials[1].Type             = CmResourceTypeMemory;
	pResources->Partials[1].ShareDisposition = CmResourceShareDriverExclusive;
	pResources->Partials[1].Flags            = CM_RESOURCE_MEMORY_READ_WRITE;
	pResources->Partials[1].u.Memory.Start   = *pMemoryBase;
	pResources->Partials[1].u.Memory.Length  = MemoryLength;

	if ( IrqLevel )
	{
		pResources->Partials[2].Type                 = CmResourceTypeInterrupt;
		pResources->Partials[2].ShareDisposition     = CmResourceShareDriverExclusive;
		pResources->Partials[2].Flags                = ( IrqMode == Latched ) ? 
			                                            CM_RESOURCE_INTERRUPT_LATCHED : 
			                                            CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
		pResources->Partials[2].u.Interrupt.Level    = IrqLevel;
		pResources->Partials[2].u.Interrupt.Vector   = IrqVector;
		pResources->Partials[2].u.Interrupt.Affinity = IrqAffinity;
	}

	// NT doesn't watch for conflicts between resources we register.  We must
	// do that on our own.
	if( DigiDetectConflict( pResources ) )
	{
		// This adapter's resources conflict with a previously reported adapter!
		DigiFreeMem( pResources );
		*phResources = NULL;
		*pbConflictDetected = TRUE;
		return( STATUS_SUCCESS );
	}

	//
	// Insert struct in list
	//

	InsertTailList( &gResourceList, &pResources->ListEntry );

	//
	// Report resources by building a CM_RESOURCE_LIST and passing it
	// to IoReportResourceUsage.
	//

	Status = BuildResourceList( &pCmResourceList, &ListLength );

	if ( !NT_SUCCESS( Status ) )
		return( Status ); 
		
	if ( !pCmResourceList )
		return( STATUS_INSUFFICIENT_RESOURCES );

   IoReportResourceUsage( NULL, pDriverObject, pCmResourceList, ListLength,
                          NULL, NULL, 0, FALSE, pbConflictDetected );
	
	DigiFreeMem( pCmResourceList );

	if ( *pbConflictDetected )
	{
		RemoveEntryList( &pResources->ListEntry );
		DigiFreeMem( pResources );
		*phResources = NULL;
	}
	else
		*phResources = pResources;

	return( STATUS_SUCCESS );
}	// DigiReportResources


NTSTATUS
DigiUnreportResources( 
	IN PDRIVER_OBJECT pDriverObject,
	IN HANDLE hResources
	)
/*++

Description:

	Unreports resources for an adapter.  Use this to free resources previously
	reported.

Parameters:

	pDriverObject
		Driver object value passed into DriverEntry.  Required for call to
		IoReportResourceUsage.

	hResources
		Handle of adapter resources to release.  This is the handle returned from
		DigiReportResources.

Return Value:

	STATUS_SUCCESS, or
	STATUS_INVALID_PARAMETER.

--*/
{
	PDIGI_ADAPTER_RESOURCES pResources = hResources;
	PCM_RESOURCE_LIST pCmResourceList;
	BOOLEAN ConflictDetected;
	ULONG ListLength;
	NTSTATUS Status;

	if ( !hResources )
		return( STATUS_INVALID_PARAMETER );

	RemoveEntryList( &pResources->ListEntry );

	DigiFreeMem( pResources );

	//
	// Report resources by building a CM_RESOURCE_LIST and passing it
	// to IoReportResourceUsage.
	//

	Status = BuildResourceList( &pCmResourceList, &ListLength );

	if ( !NT_SUCCESS( Status ) )
		return( Status ); 
		
	if ( !pCmResourceList )
		return( STATUS_INSUFFICIENT_RESOURCES );

   IoReportResourceUsage( NULL, pDriverObject, pCmResourceList, ListLength,
                          NULL, NULL, 0, FALSE, &ConflictDetected );
	
	// Any conflicts should already have been eliminated!
	ASSERT( !ConflictDetected );

	DigiFreeMem( pCmResourceList );

	return( STATUS_SUCCESS );
}

#if !NT5_BUILD

NTSTATUS
DigiAssignPciSlotResources( 
	IN PDRIVER_OBJECT       pDriverObject,
	IN PUNICODE_STRING      pRegistryPath,
	IN ULONG                BusNumber,
	IN ULONG                SlotNumber,
	OUT PHANDLE             phResources,
	OUT PDIGI_RESOURCE_LIST pResourceList )
/*++

Description:

	Wrapper around HalAssignSlotResources.  This wrapper keeps track of the 
	resources assigned alongside DigiReportResourceUsage.  This allows us to
	support PCI and ISA cards together, and ensure that they all get reported.

Parameters:

	pDriverObject
		Driver object value passed into DriverEntry.  Required for call to
		HalAssignSlotResources.

	pRegistryPath
		Pointer to unicode string which defines this adapter's services registry
		key.

	BusNumber
		System assigned bus number for this PCI bus.

	SlotNumber
		PCI slot number for this card.

	phResources
		Handle for this adapter's resources.  Must be passed into 
		DigiUnreportResources to free up these resources.

	pResourceList
		Output structure containing all resources for the card if successful.

Return Value:

	STATUS_SUCCESS if successful.

--*/
{
	NTSTATUS Status;
	PCM_RESOURCE_LIST pCmResourceList;
	USHORT i, iMem;
	ULONG ListLength;
	BOOLEAN bConflictDetected;

	// Use these to build our DIGI_ADAPTER_RESOURCES struct.
	PDIGI_ADAPTER_RESOURCES pResources;
	ULONG CountPartials;
	ULONG ResourcesLength;

	// Sanity check params
	if ( pDriverObject == NULL || 
	     pRegistryPath == NULL ||
	     phResources   == NULL || 
	     pResourceList == NULL )
		return( STATUS_INVALID_PARAMETER );

	// Initialize the list if this is the first time using it
	if ( FALSE == gResourceListInitialized )
	{
		InitializeListHead( &gResourceList );
		gResourceListInitialized = TRUE;
	}

	Status = HalAssignSlotResources( pRegistryPath, NULL, pDriverObject, NULL, 
	                                 PCIBus, BusNumber, SlotNumber, &pCmResourceList );

	if ( !NT_SUCCESS( Status ) )
		// failed 
		return( Status );

	//
	// OK, we got resources assigned.  Stuff the settings into our resources
	// structure and save them.
	//

	CountPartials = pCmResourceList->List[0].PartialResourceList.Count;
	ResourcesLength = sizeof( DIGI_ADAPTER_RESOURCES ) + ( CountPartials - 1 ) * sizeof( CM_PARTIAL_RESOURCE_DESCRIPTOR );

	pResources = DigiAllocMem( NonPagedPool, ResourcesLength );
	if ( !pResources )
		return( STATUS_INSUFFICIENT_RESOURCES );

	RtlZeroMemory( pResources, ResourcesLength );

	// Sanity check
	ASSERT( pCmResourceList->List[0].InterfaceType == PCIBus );
	ASSERT( pCmResourceList->List[0].BusNumber == BusNumber ); 

	pResources->BusType       = PCIBus;
	pResources->BusNumber     = BusNumber;
	pResources->CountPartials = CountPartials;

	RtlZeroMemory( pResourceList, sizeof *pResourceList );
	pResourceList->BusType = PCIBus;
	pResourceList->BusNumber = BusNumber;

	// Keep track of multiple memory and io locations
	iMem = 0;
	for ( i = 0; i < CountPartials; i++ )
	{
		// Copy over partial resource descriptor
		pResources->Partials[i] = 
			pCmResourceList->List[0].PartialResourceList.PartialDescriptors[i];

		// notify caller which resources were assigned
		switch ( pResources->Partials[i].Type )
		{
			case CmResourceTypePort:
				pResourceList->Io[iMem].RawType     = pResources->Partials[i].Type;
				pResourceList->Io[iMem].BaseAddress = pResources->Partials[i].u.Port.Start;
				pResourceList->Io[iMem].Length      = pResources->Partials[i].u.Port.Length;
				HalTranslateBusAddress( PCIBus, BusNumber, pResourceList->Io[iMem].BaseAddress, &pResourceList->Io[iMem].AddressSpace, &pResourceList->Io[iMem].BaseAddressTranslated );
				iMem++;
				break;

			case CmResourceTypeMemory:
				pResourceList->Io[iMem].RawType     = pResources->Partials[i].Type;
				pResourceList->Io[iMem].BaseAddress = pResources->Partials[i].u.Memory.Start;
				pResourceList->Io[iMem].Length      = pResources->Partials[i].u.Memory.Length;
				HalTranslateBusAddress( PCIBus, BusNumber, pResourceList->Io[iMem].BaseAddress, &pResourceList->Io[iMem].AddressSpace, &pResourceList->Io[iMem].BaseAddressTranslated );
				iMem++;
				break;

			case CmResourceTypeInterrupt:
				pResourceList->Irq.Level    = pResources->Partials[i].u.Interrupt.Level;
				pResourceList->Irq.Vector   = pResources->Partials[i].u.Interrupt.Vector;
				pResourceList->Irq.Affinity = pResources->Partials[i].u.Interrupt.Affinity;
				pResourceList->Irq.Mode = pResources->Partials[i].Flags == CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive;
				break;

			default:
				ASSERT( 0 && "DigiRes: Unknown resource type\n" );
				break;
		}
	}

	// We're done with the resource list created by HalAssignSlotResources
	ExFreePool( pCmResourceList );

	//
	// Add this adapter's resources to our global list of adapter resources.
	//

	InsertTailList( &gResourceList, &pResources->ListEntry );

	//
	// The earlier call to AssignSlotResources overwrote our driver's list of
	// reported resources, thus erasing any resources reported for any other
	// adapters.  Renew the list by re-reporting *all* resources.
	//

	BuildResourceList( &pCmResourceList, &ListLength );

   IoReportResourceUsage( NULL,
			  pDriverObject,
			  pCmResourceList,
			  ListLength,
			  NULL,
			  NULL,
			  0,
			  FALSE,
			  &bConflictDetected );
	
	// There should be no conflicts here.  We should have handled those the
	// first time we tried to report those resources.
	ASSERT( !bConflictDetected );

	DigiFreeMem( pCmResourceList );

	*phResources = pResources;

	return( STATUS_SUCCESS );
}

#else

NTSTATUS
DigiAssignPnpResources( 
	IN PCM_RESOURCE_LIST    pResources,
	IN PCM_RESOURCE_LIST    pResourcesTranslated,
	OUT PDIGI_RESOURCE_LIST pResourceList )
/*++

Description:

	Pull resource assignments out of NT5 PNP style resource lists.

Parameters:

	pResources
		NT5 PNP StartDevice IRP's AllocatedResources parameter.  These are physical
		addresses.

	pResourcesTranslated
		NT5 PNP StartDevice IRP's AllocatedResourcesTranslated parameter.  These are
		translated addresses, if translation is needed appropriate for the bustype.

	phResources
		Handle for this adapter's resources.  Must be passed into 
		DigiUnreportResources to free up these resources.

	pResourceList
		Output structure containing all resources for the card if successful.

Return Value:

	STATUS_SUCCESS if successful.

--*/
{
	NTSTATUS Status;
	USHORT i, iMem;
	ULONG ListLength;
	BOOLEAN bConflictDetected;
	ULONG CountPartials;

	// Sanity check params
	if ( !pResources || !pResourcesTranslated || !pResourceList )
		return( STATUS_INVALID_PARAMETER );

	CountPartials = pResources->List[0].PartialResourceList.Count;
	if ( CountPartials != pResourcesTranslated->List[0].PartialResourceList.Count )
	{
		DigiDump( DEBUG_WARN, ("*** Resources count 0x%x different from ResourcesTranslated count 0x%x! ***\n", 
		                    CountPartials, pResourcesTranslated->List[0].PartialResourceList.Count ) );
		return STATUS_UNSUCCESSFUL;
	}

	// Fill in the digi resource list
	RtlZeroMemory( pResourceList, sizeof *pResourceList );

	ASSERT( pResources->List[0].InterfaceType == pResourcesTranslated->List[0].InterfaceType );
	pResourceList->BusType   = pResources->List[0].InterfaceType;

	ASSERT( pResources->List[0].BusNumber == pResourcesTranslated->List[0].BusNumber );
	pResourceList->BusNumber = pResources->List[0].BusNumber;

	iMem = 0;
	for ( i = 0; i < CountPartials; i++ )
	{
		PCM_PARTIAL_RESOURCE_DESCRIPTOR pRaw = &pResources->List[0].PartialResourceList.PartialDescriptors[i];
		PCM_PARTIAL_RESOURCE_DESCRIPTOR pTranslated = &pResourcesTranslated->List[0].PartialResourceList.PartialDescriptors[i];

		switch ( pTranslated->Type )
		{
			case CmResourceTypePort:
				pResourceList->Io[iMem].RawType = pRaw->Type;
				pResourceList->Io[iMem].AddressSpace = CM_RESOURCE_PORT_IO;
				pResourceList->Io[iMem].BaseAddress  = pRaw->u.Port.Start;
				pResourceList->Io[iMem].BaseAddressTranslated = pTranslated->u.Port.Start;
				ASSERT( pRaw->u.Port.Length == pTranslated->u.Port.Length );
				pResourceList->Io[iMem].Length = pRaw->u.Port.Length;
				iMem++;
				break;

			case CmResourceTypeMemory:
				pResourceList->Io[iMem].RawType = pRaw->Type;
				pResourceList->Io[iMem].AddressSpace = CM_RESOURCE_PORT_MEMORY;
				pResourceList->Io[iMem].BaseAddress  = pRaw->u.Memory.Start;
				pResourceList->Io[iMem].BaseAddressTranslated = pTranslated->u.Memory.Start;
				ASSERT( pRaw->u.Memory.Length == pTranslated->u.Memory.Length );
				pResourceList->Io[iMem].Length = pRaw->u.Memory.Length;
				iMem++;
				break;

			case CmResourceTypeInterrupt:
				pResourceList->Irq.Level    = pTranslated->u.Interrupt.Level;
				pResourceList->Irq.RawLevel = pRaw->u.Interrupt.Level;
				pResourceList->Irq.Vector   = pTranslated->u.Interrupt.Vector;
				pResourceList->Irq.Affinity = pTranslated->u.Interrupt.Affinity;
				pResourceList->Irq.Mode = pTranslated->Flags == CM_RESOURCE_INTERRUPT_LATCHED ? Latched : LevelSensitive;
				break;

			case CmResourceTypeDevicePrivate:
				break;

			default:
				ASSERT( 0 && "DigiRes: Unknown resource type\n" );
				break;
		}
	}

	return( STATUS_SUCCESS );
}

#if DBG

VOID
DigiDumpResourceList( 
	IN PCM_RESOURCE_LIST pResourceList
	)
{
	if ( pResourceList )
	{
		PCM_FULL_RESOURCE_DESCRIPTOR pFull = pResourceList->List;
		USHORT frd;

		DigiDump( DEBUG_WARN, ( "DigilibResourceList:\n"
		                     "   Count %d\n", pResourceList->Count ) );

		if ( pFull )
		{
			for ( frd = 0; frd < pResourceList->Count; frd++ )
			{
				PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartial;
				ULONG prd;

				DigiDump( DEBUG_WARN, ( "   List[%d]\n"
				                     "      InterfaceType 0x%x\n"
				                     "      BusNumber 0x%x\n"
				                     "      PartialResourceList\n"
				                     "         Version 0x%x\n"
				                     "         Revision 0x%x\n"
				                     "         Count 0x%x\n",
				                     frd, pFull->InterfaceType, pFull->BusNumber,
				                     pFull->PartialResourceList.Version,
				                     pFull->PartialResourceList.Revision,
				                     pFull->PartialResourceList.Count ) );
				
				// Walk through all partial descriptors
				pPartial = pFull->PartialResourceList.PartialDescriptors;
				for ( prd = 0; prd < pFull->PartialResourceList.Count; prd++ )
				{
					DigiDump( DEBUG_WARN, ( "         PartialDescriptor[%d]\n"
					                     "            Type 0x%x\n"
					                     "            ShareDisposition 0x%x\n"
					                     "            Flags 0x%x\n",
					                     prd, pPartial->Type,
					                     pPartial->ShareDisposition,
					                     pPartial->Flags ) );
					switch ( pPartial->Type )
					{
						case CmResourceTypePort:
							DigiDump( DEBUG_WARN, ( "            Port\n"
							                     "               Start  0x%x%x\n"
							                     "               Length 0x%x\n",
							                     pPartial->u.Port.Start.HighPart,
							                     pPartial->u.Port.Start.LowPart,
							                     pPartial->u.Port.Length ) );
							break;

						case CmResourceTypeMemory:
							DigiDump( DEBUG_WARN, ( "            Memory\n"
							                     "               Start  0x%x%x\n"
							                     "               Length 0x%x\n",
							                     pPartial->u.Memory.Start.HighPart,
							                     pPartial->u.Memory.Start.LowPart,
							                     pPartial->u.Memory.Length ) );
							break;

						case CmResourceTypeInterrupt:
							DigiDump( DEBUG_WARN, ( "            Interrupt\n"
							                      "               Level    0x%x\n"
							                      "               Vector   0x%x\n"
							                      "               Affinity 0x%x\n",
							                      pPartial->u.Interrupt.Level,
							                      pPartial->u.Interrupt.Vector,
							                      pPartial->u.Interrupt.Affinity ) );
							break;

#if NT5_BUILD
						case CmResourceTypeDevicePrivate:
							DigiDump( DEBUG_WARN, ( "            DevicePrivate\n"
							                      "               Data[0] 0x%x\n"
							                      "               Data[1] 0x%x\n"
							                      "               Data[2] 0x%x\n",
							                      pPartial->u.DevicePrivate.Data[0],
							                      pPartial->u.DevicePrivate.Data[1],
							                      pPartial->u.DevicePrivate.Data[2] ) );
							break;
#endif

						default:
							DigiDump( DEBUG_WARN, ( "            Unknown type!\n" ) );
							break;
					}
					pPartial++;
				}
				// Next FULL_RESOURCE_DESCRIPTOR starts immediately after.
				pFull = (PVOID) pPartial;
			}
		}
	}
}

#endif

#endif

