#include "pch.h"
#pragma hdrstop
#include <ncxbase.h>
#include "hwres.h"
#include "kkcwinf.h"
#include "ncreg.h"

extern const WCHAR c_szAfIoAddr[];
extern const WCHAR c_szAfIrq[];
extern const WCHAR c_szAfDma[];
extern const WCHAR c_szAfMem[];
extern const WCHAR c_szBusType[];

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::CHwRes
//
//  Purpose:    Class constructor.  (Variable initialization)
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:      Doesn't do much.  Just some member variable initialization.
//              Bulk of the init work is done in HrInit().
//
CHwRes::CHwRes()
:   m_DevNode(NULL),
    m_pnccItem(NULL),
    m_fInitialized(FALSE),
    m_fHrInitCalled(FALSE),
    m_fDirty(FALSE)
{
    m_vAfDma.InitNotPresent(VALUETYPE_DWORD);
    m_vAfIrq.InitNotPresent(VALUETYPE_DWORD);
    m_vAfMem.InitNotPresent(VALUETYPE_DWORD);
    m_vAfMemEnd.InitNotPresent(VALUETYPE_DWORD);
    m_vAfIo.InitNotPresent(VALUETYPE_DWORD);
    m_vAfIoEnd.InitNotPresent(VALUETYPE_DWORD);
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::HrInit
//
//  Purpose:    Initialize class
//
//  Arguments:
//      hInst    [in]       Handle to our instance.
//      pnccItem [in]       Our INetCfgComponent.
//
//  Returns:    S_OK - init successfull;
//              HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) - no config
//              info for device; E_FAIL - other failure (device not found,etc)
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:      - should initialize all non-ui stuff
//              - initialize configuration and resource lists.
//
HRESULT CHwRes::HrInit(const DEVNODE& devnode)
{
    HRESULT hr = E_FAIL;

    m_fHrInitCalled = TRUE;


    // use the config manager to get devnode
    // Since the devnode might have a problem, e.g. resources not set
    // correctly, we need to retrieve possible phantoms
    //
    CONFIGRET crRet = ERROR_SUCCESS;
    HKEY hkey;

    // We only do work on Isa adapters so we need to get the bustype
    // value from the driver (software) key
    crRet = CM_Open_DevNode_Key(devnode, KEY_READ, 0,
            RegDisposition_OpenExisting, &hkey, CM_REGISTRY_SOFTWARE);

    if (CR_SUCCESS == crRet)
    {
        // Get BusType
        ULONG ulBusType;
        hr = HrRegQueryStringAsUlong(hkey, c_szBusType, c_nBase10,
                &ulBusType);

        RegCloseKey(hkey);

        // If Isa, than we can continue
        if (SUCCEEDED(hr) && (Isa == ulBusType))
        {
            m_DevNode = devnode;
            // read configuration current config info
            hr = HrInitConfigList();
        }
        else
        {
            hr = S_FALSE;
        }

    }

    if (S_OK == hr)
    {
        m_fInitialized = TRUE;
    }

    TraceError("CHwRes::HrInit",
        (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr ||
        S_FALSE == hr) ? S_OK : hr);
    return hr;
}



//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::GetNextElement
//
//  Purpose:    Gets the next (or prev) element in a resource list
//
//  Arguments:
//      pResource [in]       resource list to traverse.
//      ppeList   [out]      the "next" element is returned.
//      fNext     [in]       TRUE - moving fwd in list; FALSE - move bkwrds.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
VOID CHwRes::GetNextElement(PRESOURCE pResource, PVOID *ppeList, BOOL fNext)
{
    UINT size = 0;

    AssertSz(m_fInitialized,
             "GetNextElement called before CHwRes is HrInit'ed");

    Assert(pResource != NULL);
    // get the vector size (dependant on resource type)
    switch (pResource->ResourceType) {
    case ResType_IRQ:
        size = pResource->pIRQList->size();
        break;
    case ResType_DMA:
        size = pResource->pDMAList->size();
        break;
    case ResType_IO:
        size = pResource->pIOList->size();
        break;
    case ResType_Mem:
        size = pResource->pMEMList->size();
        break;
    default:
        Assert(FALSE);
        break;
    }

    // increment/decrement current position within vector
    if (fNext)
    {
        pResource->pos++;
    }
    else
    {
        pResource->pos--;
    }
    // Check for wrapping
    if ((int)(pResource->pos) < 0)
    {
        Assert(pResource->pos == -1);
        Assert(!fNext);  // we're going backwards
        pResource->pos = size-1;
    }
    else if (pResource->pos >= size)
    {
        Assert(pResource->pos == size);
        Assert(fNext);
        pResource->pos = 0;
    }

    // return the current vector element (dependant on res type)
    // REVIEW: do we ever use the element that's gathered below?
    switch (pResource->ResourceType) {
    case ResType_IRQ:
        *ppeList = (*pResource->pIRQList)[pResource->pos];
        break;
    case ResType_DMA:
        *ppeList = (*pResource->pDMAList)[pResource->pos];
        break;
    case ResType_IO:
        *ppeList = (*pResource->pIOList)[pResource->pos];
        break;
    case ResType_Mem:
        *ppeList = (*pResource->pMEMList)[pResource->pos];
        break;
    default:
        Assert(FALSE);
        break;
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::HrInitConfigList
//
//  Purpose:    Initialize m_ConfigList (the internal vector of
//              configurations.)
//
//  Returns:    S_OK - init successful;
//              HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) if no device configs
//              found; E_FAIL otherwise (invalid device, etc.)
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:      "Lists" are actually implemented as STL vectors.
//
HRESULT CHwRes::HrInitConfigList() {
    HRESULT hr = S_OK;
    PCONFIGURATION pConfiguration;
    CONFIGRET ConfigRet;
    LOG_CONF lcCurrent, lcNext;
    UINT iBasicConfig;
    BOOL fFoundConfig = FALSE;

    Assert(NULL != m_DevNode);

    // erase all elements
    m_ConfigList.erase(m_ConfigList.begin(), m_ConfigList.end());

    // Boot Configuration
    if (CM_Get_First_Log_Conf(&lcCurrent, m_DevNode, BOOT_LOG_CONF)
            == CR_SUCCESS)
    {
        TraceTag(ttidNetComm, "Boot config already exists");
        hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS);
    }

    if (SUCCEEDED(hr))
    {
        // Basic Configurations (may be more than one)
        iBasicConfig = 0;
        ConfigRet = CM_Get_First_Log_Conf(&lcCurrent, m_DevNode, BASIC_LOG_CONF);

#ifdef ENABLETRACE
if (CR_SUCCESS != ConfigRet)
{
    TraceTag(ttidNetComm, "CM_Get_First_Log_conf returned %lX", ConfigRet);
}
#endif // ENABLETRACE
        while (CR_SUCCESS == ConfigRet)
        {
            pConfiguration = new CONFIGURATION;

			if (pConfiguration == NULL)
			{
                TraceError("pConfiguration == NULL", E_FAIL);
                goto error;
			}

            pConfiguration->LogConf = lcCurrent;
            pConfiguration->fBoot = FALSE;
            pConfiguration->fAlloc = FALSE;

            if (!FInitResourceList(pConfiguration))
            {
                hr = E_FAIL;
                TraceError("CHwRes::FInitResourceList",hr);
                goto error;
            }

            m_ConfigList.push_back(pConfiguration);

            // Move on to the next basic config
            iBasicConfig++;
            ConfigRet = CM_Get_Next_Log_Conf(&lcNext, lcCurrent, 0);
            lcCurrent = lcNext;
            fFoundConfig = TRUE;
        }

        if (!fFoundConfig)
        {
            TraceTag(ttidNetComm, "No basic configs");

            // if no config entries found, return ERROR_FILE_NOT_FOUND.
            hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
        }
    }

error:
    // ERROR_FILE_NOT_FOUND is an okay error message.
    TraceError("CHwRes::HrInitConfigList",
               (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr ||
                HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr ) ? S_OK : hr);
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::FInitResourceList
//
//  Purpose:    Initializes the resource list for a given logical config.
//
//  Arguments:
//      pConfiguration  [in]    configuration who's resource list is to
//                              be initialized.
//
//  Returns:    TRUE if init sucessful; FALSE otherwise.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:      Requires pConfiguration->LogConf to be initialized.
//
BOOL CHwRes::FInitResourceList(PCONFIGURATION pConfiguration)
{
    RES_DES rdCurrent;
    RES_DES rdNext;
    RESOURCEID ResType;
    UINT iResource;
    #define RESOURCE_BUFFER_SIZE 4096
    BYTE Buffer[RESOURCE_BUFFER_SIZE];
    ULONG ulDataSize;
    RESOURCE * pRes;
    CONFIGRET cr;

    Assert(pConfiguration->LogConf);
    rdCurrent = (RES_DES) pConfiguration->LogConf;
    iResource = 0;
    while ((cr = CM_Get_Next_Res_Des(&rdNext, rdCurrent,
                               ResType_All, &ResType, 0)) == CR_SUCCESS)
    {
        rdCurrent = rdNext;
        // Only process this resource if the ignore bit is not set
        if (ResType_Ignored_Bit != ResType)
        {
            pRes = &(pConfiguration->aResource[iResource]);
            pRes->ResDes = rdCurrent;
            pRes->ResourceType = ResType;
            cr = CM_Get_Res_Des_Data_Size(&ulDataSize, rdCurrent, 0);
            if (CR_SUCCESS != cr)
            {
                TraceTag (ttidDefault, "CM_Get_Res_Des_Data_Size returned 0x%08x", cr);
                goto error;
            }

            AssertSz (ulDataSize, "CM_Get_Res_Des_Data_Size returned 0!");
            AssertSz (ulDataSize <= sizeof(Buffer), "CHwRes::FInitResourceList: buffer is too small.");;

            cr = CM_Get_Res_Des_Data(rdCurrent, Buffer, sizeof(Buffer), 0);
            if (CR_SUCCESS != cr)
            {
                TraceTag (ttidDefault, "CM_Get_Res_Des_Data returned 0x%08x", cr);
                goto error;
            }

            // Depending on the ResType, we have to initialize our resource list.
            switch (ResType)
            {
            case ResType_Mem:
                InitMEMList(&(pRes->pMEMList), (PMEM_RESOURCE)Buffer);
                break;
            case ResType_IO:
                InitIOList(&(pRes->pIOList), (PIO_RESOURCE)Buffer);
                break;
            case ResType_DMA:
                InitDMAList(&(pRes->pDMAList), (PDMA_RESOURCE)Buffer);
                break;
            case ResType_IRQ:
                InitIRQList(&(pRes->pIRQList), (PIRQ_RESOURCE)Buffer);
                break;
            default:
                AssertSz (ResType_None != ResType, "ResType_None hit caught in CHwRes::FInitResourceList.");
                break;
            }
            // set the list position to the first element;
            // applied_pos will get copied to pos when dialog box is created.
            pRes->applied_pos = 0;

            iResource++;
            pConfiguration->cResource = iResource;
            if (iResource >= c_nMaxResCtls)
            {
                break; // we're done.
            }
        }
    } //while
    if ((CR_SUCCESS != cr) && (CR_NO_MORE_RES_DES != cr))
    {
        TraceTag (ttidDefault, "CM_Get_Next_Res_Des returned 0x%08x", cr);
        goto error;
    }

    return TRUE;
error:
    return FALSE;
}


//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::InitIRQList
//
//  Purpose:    Initialize an IRQ resource vector given a config manager
//              resource structure.
//
//  Arguments:
//      ppIRQList    [out]    returns IRQ_LIST that will be created.
//      pIRQResource [in]     IRQ_RESOURCE structure from config manager.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
VOID CHwRes::InitIRQList(PIRQ_LIST* ppIRQList, PIRQ_RESOURCE pIRQResource) {
    PIRQ_DES pIRQHeader;
    PIRQ_RANGE pIRQData;
    PIRQ_LIST_ELEMENT pIRQListElement;

    pIRQHeader = &(pIRQResource->IRQ_Header);
    pIRQData = &(pIRQResource->IRQ_Data[0]);

    // Create a new list.
    *ppIRQList = new IRQ_LIST;

	if (*ppIRQList == NULL)
	{
		return;
	}

    ULONG iData;
    ULONG iIRQ;

    for (iData = 0; iData < pIRQHeader->IRQD_Count; iData++) 
	{
        for (iIRQ = pIRQData[iData].IRQR_Min;
                iIRQ <= pIRQData[iData].IRQR_Max;
                iIRQ++) 
		{
            // For each IRQ that falls within the given range,
            // create new IRQ List Element, populate its fields and insert
            // it into the m_IRQList.
            pIRQListElement = new IRQ_LIST_ELEMENT;

			if (pIRQListElement == NULL)
			{
				continue;
			}

            pIRQListElement->dwIRQ = iIRQ;
            pIRQListElement->fConflict = FALSE;
            pIRQListElement->fAllocated = FALSE;
            (*ppIRQList)->push_back(pIRQListElement);
        }
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::InitDMAList
//
//  Purpose:    Initialize a DMA resource vector given a config manager
//              resource structure.
//
//  Arguments:
//      ppDMAList    [out]    returns DMA_LIST that will be created.
//      pDMAResource [in]     DMA_RESOURCE structure from config manager.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
VOID CHwRes::InitDMAList(PDMA_LIST* ppDMAList, PDMA_RESOURCE pDMAResource) {
    PDMA_DES pDMAHeader;
    PDMA_RANGE pDMAData;
    PDMA_LIST_ELEMENT peDMAList;

    pDMAHeader = &(pDMAResource->DMA_Header);
    pDMAData = &(pDMAResource->DMA_Data[0]);

    // Create a new list.
    *ppDMAList = new DMA_LIST;

	if (*ppDMAList == NULL)
	{
		return;
	}

    ULONG iData;  // index of DMA_Range structure we're looking at.
    ULONG iDMA;   // current DMA number we're adding to the list.

    // Go through all the DMA_Range structures, and all DMAs in the
    // specified range to the list.

    for (iData = 0; iData < pDMAHeader->DD_Count; iData++)
    {
        for (iDMA = pDMAData[iData].DR_Min;
                iDMA <= pDMAData[iData].DR_Max;
                iDMA++)
        {
            // For each DMA that falls within the given range,
            // create new DMA List Element, populate its fields and insert
            // it into the DMAList.
            peDMAList = new DMA_LIST_ELEMENT;

			if (peDMAList == NULL)
			{
				continue;
			}

            peDMAList->dwDMA = iDMA;
            peDMAList->fConflict = FALSE;
            peDMAList->fAllocated = FALSE;
            (*ppDMAList)->push_back(peDMAList);
        }
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::InitMEMList
//
//  Purpose:    Initialize a MEM resource vector given a config manager
//              resource structure.
//
//  Arguments:
//      ppMEMList    [out]    returns MEM_LIST that will be created.
//      pMEMResource [in]     MEM_RESOURCE structure from config manager.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
VOID CHwRes::InitMEMList(PMEM_LIST* ppMEMList, PMEM_RESOURCE pMEMResource) 
{
    PMEM_DES pMEMHeader;
    PMEM_RANGE pMEMData;
    PMEM_LIST_ELEMENT peMEMList;

    // For easy access
    pMEMHeader = &(pMEMResource->MEM_Header);
    pMEMData = pMEMResource->MEM_Data;

    // Create a new list.
    *ppMEMList = new MEM_LIST;

	if (*ppMEMList == NULL)
	{
		return;
	}
	
    ULONG iData;  // index of MEM_Range structure we're looking at.
    DWORDLONG MEMBase;   // current MEM Base we're adding to the list.
    ULONG cMEMBytes;  // number of bytes required.
    DWORDLONG MEMAlign;

    // Go through all the MEM_Range structures, and all MEMs in the
    // specified range to the list.

    for (iData = 0; iData < pMEMHeader->MD_Count; iData++) 
	{
        MEMAlign = pMEMData[iData].MR_Align;
        cMEMBytes = pMEMData[iData].MR_nBytes;

        // do sanity checks
        if (0 == MEMAlign)
        {
            TraceTag(ttidNetComm, "CHwRes::InitMEMList() - Bogus alignment "
                    "field while processing info from Config Manager.");
            break;
        }

        if (0 == cMEMBytes)
        {
            TraceTag(ttidNetComm, "CHwRes::InitMEMList() - Bogus membytes "
                    "field while processing info from Config Manager.");
            break;
        }

        for (MEMBase = pMEMData[iData].MR_Min;
                MEMBase+cMEMBytes-1 <= pMEMData[iData].MR_Max;
                MEMBase += ~MEMAlign + 1) 
		{
            // For each MEM that falls within the given range,
            // create new MEM List Element, populate its fields and insert
            // it into the MEMList.
            peMEMList = new MEM_LIST_ELEMENT;

			if (peMEMList == NULL)
			{
				continue;
			}

            peMEMList->dwMEM_Base = MEMBase;
            peMEMList->dwMEM_End = MEMBase + cMEMBytes - 1;
            peMEMList->fConflict = FALSE;
            peMEMList->fAllocated = FALSE;
            (*ppMEMList)->push_back(peMEMList);

            // Check for wrapping.
            if (MEMBase >= MEMBase + ~MEMAlign + 1)
            {
                TraceTag(ttidError, "Memory base is greater than Memory "
                        "end!!!");
                break;
            }
        }
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::InitIOList
//
//  Purpose:    Initialize an IO resource vector given a config manager
//              resource structure.
//
//  Arguments:
//      ppIOList    [out]    returns IO_LIST that will be created.
//      pIOResource [in]     IO_RESOURCE structure from config manager.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
VOID CHwRes::InitIOList(PIO_LIST* ppIOList, PIO_RESOURCE pIOResource)
{
    PIO_DES pIOHeader;
    PIO_RANGE pIOData;
    PIO_LIST_ELEMENT peIOList;

    // For easy access
    pIOHeader = &(pIOResource->IO_Header);
    pIOData = pIOResource->IO_Data;

    // Create a new list.
    *ppIOList = new IO_LIST;

	if (*ppIOList == NULL)
	{
		return;
	}

    ULONG iData;  // index of IO_Range structure we're looking at.
    DWORDLONG IOBase;   // current IO Base we're adding to the list.
    ULONG cIOBytes;  // number of bytes required.
    DWORDLONG IOAlign;

    // Go through all the IO_Range structures, and all IOs in the
    // specified range to the list.

    for (iData = 0; iData < pIOHeader->IOD_Count; iData++) 
	{
        IOAlign = pIOData[iData].IOR_Align;
        cIOBytes = pIOData[iData].IOR_nPorts;

        // Perform sanity checks.
        if (0 == IOAlign)
        {
            TraceTag(ttidError, "CHwRes::InitIOList - Bogus alignment field "
                    "while processing data from Config Manager.");
            break;
        }

        if (0 == cIOBytes)
        {
            TraceTag(ttidError, "CHwRes::InitIOList - Bogus IObytes field "
                "while processing data from Config Manager.");
            break;
        }

        for (IOBase = pIOData[iData].IOR_Min;
                IOBase+cIOBytes-1 <= pIOData[iData].IOR_Max;
                IOBase += ~IOAlign + 1) 
		{
            // For each IO that falls within the given range,
            // create new IO List Element, populate its fields and insert
            // it into the IOList.
            peIOList = new IO_LIST_ELEMENT;

			if (peIOList == NULL)
			{
				continue;
			}

            peIOList->dwIO_Base = IOBase;
            peIOList->dwIO_End = IOBase + cIOBytes-1;
            peIOList->fConflict = FALSE;
            peIOList->fAllocated = FALSE;
            (*ppIOList)->push_back(peIOList);

            // Check for wrapping.
            if (IOBase >= IOBase + ~IOAlign+1)
            {
                TraceTag(ttidError, "IO base is greater than IO end!!!");
                break;
            }
        }
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::UseAnswerFile
//
//  Purpose:    Reads in settings from answerfile and puts them into m_vAf*
//              member variables.
//
//  Arguments:
//      szAnswerFile  [in]     Path to answerfile.
//      szSection     [in]     Section to read within answerfile
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
VOID CHwRes::UseAnswerFile(const WCHAR * const szAnswerFile, const WCHAR * const szSection) {
    CWInfFile       AnswerFile;
    PCWInfSection   pSection;

    DWORD   dw;

    AssertSz(m_fInitialized,
             "UseAnswerFile called before CHwRes class HrInit'ed");

    // initialize answer file class
	if (AnswerFile.Init() == FALSE)
	{
        AssertSz(FALSE,"CHwRes::UseAnswerFile - Failed to initialize CWInfFile");
		return;
	}

    // Open the answerfile and find the desired section.
    AnswerFile.Open(szAnswerFile);
    pSection = AnswerFile.FindSection(szSection);

    // If the answer file section specified is missing
    // we should skip trying to read
    //
    if (pSection)
    {
        // Get the hardware resource keys
        if (pSection->GetIntValue(c_szAfIoAddr, &dw))
        {
            // set this only if the value isn't obviously wrong (i.e. <= 0)
            if (dw > 0)
            {
                m_vAfIo.SetDword(dw);
                m_vAfIo.SetPresent(TRUE);
            }
        }
        if (pSection->GetIntValue(c_szAfIrq, &dw))
        {
            // set this only if the value isn't obviously wrong (i.e. <= 0)
            if (dw > 0)
            {
                m_vAfIrq.SetDword(dw);
                m_vAfIrq.SetPresent(TRUE);
            }
        }
        if (pSection->GetIntValue(c_szAfDma, &dw))
        {
            // set this only if the value isn't obviously wrong (i.e. <= 0)
            if (dw > 0)
            {
                m_vAfDma.SetDword(dw);
                m_vAfDma.SetPresent(TRUE);
            }
        }
        if (pSection->GetIntValue(c_szAfMem, &dw))
        {
            // set this only if the value isn't obviously wrong (i.e. <= 0)
            if (dw > 0)
            {
                m_vAfMem.SetDword(dw);
                m_vAfMem.SetPresent(TRUE);
            }
        }
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::FValidateAnswerfileSettings
//
//  Purpose:
//      Ensures that the hw resource settings read in from the answerfile
//      are valid.  It will, optionally, raise UI if the properties
//      are invalid.
//
//  Arguments:
//      fDisplayUI    [in]    TRUE, if an error UI is to be displayed if the
//                            answerfile settings are invalid
//
//  Returns:  HRESULT. S_OK if the answerfile settings are valid, S_FALSE if there
//                      are no resources to set, an error code otherwise
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//      Will set the m_vAfMemEnd and m_vAfIoEnd to correspond to
//      m_vAfMem and m_vAfIo.
//
HRESULT
CHwRes::HrValidateAnswerfileSettings(BOOL fDisplayUI)
{
    HRESULT hr = S_OK;

    AssertSz(m_fInitialized, "FValidateAnswerfileSettings called before "
             "CHwRes class HrInit'ed");

    // override current resource settings
    if (!m_vAfDma.IsPresent() &&
        !m_vAfIrq.IsPresent() &&
        !m_vAfIo.IsPresent() &&
        !m_vAfMem.IsPresent())
    {
        // no resources found...
        TraceTag(ttidNetComm, "No Hardware Resources found in answerfile.");
        hr = S_FALSE;
    }
    else
    {
        if (!FValidateAnswerfileResources())
        {
            hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
            TraceError("Error setting adapter resources from "
                    "answerfile.", hr);
        }
        else
        {
            // m_vAfMemEnd and m_vAfIoEnd were set by
            // FValidateAnswerfileResources()
            Assert(FImplies(m_vAfMem.IsPresent(), m_vAfMemEnd.IsPresent()));
            Assert(FImplies(m_vAfIo.IsPresent(), m_vAfIoEnd.IsPresent()));
        }
    }
    TraceError("CHwRes::HrValidateAnswerfileSettings",
        (S_FALSE == hr) ? S_OK : hr);
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::FCommitAnswerfileSettings
//
//  Purpose:
//      Commits (to the config manager) the hw resource settings read
//      in from the Answerfile.
//
//  Returns:    FALSE if there were problems writing the BootConfig entry.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
BOOL CHwRes::FCommitAnswerfileSettings()
{
    AssertSz(m_fInitialized, "FCommitAnswerfileSettings called before "
             "CHwRes class HrInit'ed");

    Assert(FImplies(m_vAfIo.IsPresent(), m_vAfIoEnd.IsPresent()));
    Assert(FImplies(m_vAfMem.IsPresent(), m_vAfMemEnd.IsPresent()));

    // write out forced config entry to the config manager
    BOOL f;
    f = FCreateBootConfig(&m_vAfMem, &m_vAfMemEnd,
                            &m_vAfIo, &m_vAfIo,
                            &m_vAfDma,
                            &m_vAfIrq);
    return f;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::FValidateAnswerfileResources
//
//  Purpose:    Validates the resource requirements read in from Answerfile.
//
//  Arguments:
//      nID       [out]       ID of thingy.
//      fInstall [in]       TRUE if installing, FALSE otherwise.
//      ppv      [in,out]   Old value is freed and this returns new value.
//
//  Returns:    TRUE, if resource requirement are valid.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//      Implementation note:  We have a set of resource requirements from
//      the answerfile (stored in m_vAf*).  This method iterates through
//      every logical config (execept alloc or boot ones) and tests to see
//      if the resource requirements are valid in the logical config.
//      If they are, then we can use the resource requirements.  If they're
//      not all valid in any logical config, then we return FALSE.
//
BOOL CHwRes::FValidateAnswerfileResources()
{
    DWORD dwMemEnd;
    DWORD dwIoEnd;
    BOOL fResourceValid;

    AssertSz(m_fInitialized, "FValidateAnswerfileResources called before "
             "CHwRes class HrInit'ed");

    // Configuration List should be initialized
    Assert(0 != m_ConfigList.size());
    PRESOURCE pResource;
    for (size_t iConfig = 0; iConfig < m_ConfigList.size(); iConfig++)
    {
        // we only want Basic configurations, so skip alloc or boot.
        if (m_ConfigList[iConfig]->fBoot || m_ConfigList[iConfig]->fAlloc)
        {
            continue;
        }

        fResourceValid = TRUE;
        if (m_vAfDma.IsPresent())
        {
            if (!FValidateDMA(m_ConfigList[iConfig], m_vAfDma.GetDword()))
            {
                fResourceValid = FALSE;
            }
        }
        if (m_vAfIrq.IsPresent())
        {
            if (!FValidateIRQ(m_ConfigList[iConfig], m_vAfIrq.GetDword()))
            {
                fResourceValid = FALSE;
            }
        }
        if (m_vAfIo.IsPresent())
        {
            if (!FGetIOEndPortGivenBasePort(m_ConfigList[iConfig],
                                           m_vAfIo.GetDword(), &dwIoEnd))
            {
                m_vAfIoEnd.SetPresent(FALSE);
                fResourceValid = FALSE;
            }
            else
            {
                m_vAfIoEnd.SetDword(dwIoEnd);
                m_vAfIoEnd.SetPresent(TRUE);
            }
        }
        if (m_vAfMem.IsPresent())
        {
            if (!FGetMEMEndGivenBase(m_ConfigList[iConfig],
                                    m_vAfMem.GetDword(), &dwMemEnd))
            {
                m_vAfMemEnd.SetPresent(FALSE);
                fResourceValid = FALSE;
            }
            else
            {
                m_vAfMemEnd.SetDword(dwMemEnd);
                m_vAfMemEnd.SetPresent(TRUE);
            }
        }
        if (fResourceValid) break; // found valid one.
    }
    // something has to be present (otherwise don't call this function!)
    Assert(m_vAfIo.IsPresent() || m_vAfIrq.IsPresent() ||
           m_vAfDma.IsPresent() || m_vAfMem.IsPresent());
    return fResourceValid;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::FCreateBootConfig
//
//  Purpose:    Create and insert a Boot Config entry into the Config
//              Manager.
//
//  Arguments:
//      pvMem     [in]   Memory range base
//      pvMemEnd  [in]   Memory range end
//      pvIo      [in]   Io range base
//      pvIoEnd   [in]   Io range end
//      pvDma     [in]   Dma resource required.
//      pvIrq     [in]   Irq resource required.
//
//  Returns:    TRUE if creation of forced config was successful.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
BOOL CHwRes::FCreateBootConfig(
    CValue * pvMem,
    CValue * pvMemEnd,
    CValue * pvIo,
    CValue * pvIoEnd,
    CValue * pvDma,
    CValue * pvIrq)
{
    DMA_RESOURCE DMARes;
    IO_RESOURCE IORes;
    MEM_RESOURCE MEMRes;
    IRQ_RESOURCE IRQRes;
    LOG_CONF lcLogConf = NULL;

    AssertSz(pvMem && pvMemEnd && pvIo && pvIoEnd && pvDma && pvIrq,
             "One of the pointer parameters passed to CHwRes::FCreate"
             "BootConfig() is null.");
    AssertSz(m_fInitialized, "FCreateBootConfig called before "
             "CHwRes class HrInit'ed");


    TraceTag(ttidNetComm, "In FCreateBootConfig");

    // Create a boot config
    //
    if (CM_Add_Empty_Log_Conf(&lcLogConf, m_DevNode, LCPRI_BOOTCONFIG,
            BOOT_LOG_CONF)
        != CR_SUCCESS)
    {
        TraceTag(ttidNetComm, "Unable to create BOOT_LOG_CONF");
        return FALSE;
    }

    if (pvDma->IsPresent())
    {
        Assert(pvDma->GetDword() > 0);
        // fill out DMAResource structure's header
        ZeroMemory(&DMARes, sizeof(DMARes));
        DMARes.DMA_Header.DD_Count = 0;
        DMARes.DMA_Header.DD_Type = DType_Range;
        DMARes.DMA_Header.DD_Flags = 0;
        DMARes.DMA_Header.DD_Alloc_Chan = pvDma->GetDword();
        // add to boot conf
        CM_Add_Res_Des(NULL, lcLogConf, ResType_DMA, &DMARes,
                       sizeof(DMARes), 0);
        TraceTag(ttidNetComm, "added Dma resource %lX", pvDma->GetDword());
    }
    if (pvIrq->IsPresent())
    {
        Assert(pvIrq->GetDword() > 0);
        // IRQResource structure
        ZeroMemory(&IRQRes, sizeof(IRQRes));
        IRQRes.IRQ_Header.IRQD_Count = 0;
        IRQRes.IRQ_Header.IRQD_Type = IRQType_Range;
        IRQRes.IRQ_Header.IRQD_Flags |=  fIRQD_Edge;
        IRQRes.IRQ_Header.IRQD_Alloc_Num = pvIrq->GetDword();
        IRQRes.IRQ_Header.IRQD_Affinity = 0;
        // add to boot conf
        CM_Add_Res_Des(NULL, lcLogConf, ResType_IRQ, &IRQRes,
                       sizeof(IRQRes), 0);
        TraceTag(ttidNetComm, "added IRQ resource %lX", pvIrq->GetDword());
    }
    if (pvIo->IsPresent()) {
        Assert(pvIo->GetDword() > 0);
        Assert(pvIoEnd->GetDword() > 0);
        ZeroMemory(&IORes, sizeof(IORes));
        IORes.IO_Header.IOD_Count = 0;
        IORes.IO_Header.IOD_Type = IOType_Range;
        IORes.IO_Header.IOD_Alloc_Base = pvIo->GetDword();
        IORes.IO_Header.IOD_Alloc_End = pvIoEnd->GetDword();
        IORes.IO_Header.IOD_DesFlags = fIOD_10_BIT_DECODE;
        // add to boot conf
        CM_Add_Res_Des(NULL, lcLogConf, ResType_IO, &IORes, sizeof(IORes), 0);
        TraceTag(ttidNetComm, "added IO resource %lX-%lX", pvIo->GetDword(),
                pvIoEnd->GetDword());
    }

    if (pvMem->IsPresent()) {
        Assert(pvMem->GetDword() > 0);
        Assert(pvMemEnd->GetDword() > 0);
        ZeroMemory(&MEMRes, sizeof(MEMRes));
        MEMRes.MEM_Header.MD_Count = 0;
        MEMRes.MEM_Header.MD_Type = MType_Range;
        MEMRes.MEM_Header.MD_Alloc_Base = pvMem->GetDword();
        MEMRes.MEM_Header.MD_Alloc_End = pvMemEnd->GetDword();
        MEMRes.MEM_Header.MD_Flags = 0;
        // add to boot conf
        CM_Add_Res_Des(NULL, lcLogConf, ResType_Mem, &MEMRes,
                       sizeof(MEMRes), 0);
        TraceTag(ttidNetComm, "added Memory resource %lX - %lX",
                pvMem->GetDword(), pvMemEnd->GetDword());
    }

    CM_Free_Log_Conf_Handle(lcLogConf);

    return TRUE;
}


//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::FValidateIRQ
//
//  Purpose:    Validates that the IRQ given is valid in the given config.
//
//  Arguments:
//      pConfig  [in]    Config to use.
//      dwIRQ    [in]    irq setting to validate.
//
//  Returns:    TRUE if irq setting is valid.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
BOOL CHwRes::FValidateIRQ(PCONFIGURATION pConfig, ULONG dwIRQ)
{
    PIRQ_LIST pIRQList;

    AssertSz(m_fInitialized, "FValidateIRQ called before "
             "CHwRes class HrInit'ed");

    Assert(pConfig != NULL);
    Assert(dwIRQ > 0);
    // For each IRQ resource in the given config
    //     go through list of valid IRQ looking for given one
    //         if found, return TRUE
    for (size_t iRes = 0; iRes < pConfig->cResource; iRes++)
    {
        if (pConfig->aResource[iRes].ResourceType != ResType_IRQ)
            continue;

        pIRQList = pConfig->aResource[iRes].pIRQList; // for easy access
        for (size_t iIRQ = 0; iIRQ < pIRQList->size(); iIRQ++)
        {
            if ((*pIRQList)[iIRQ]->dwIRQ == dwIRQ)
            {
                return TRUE;  // found it.
            }
        }
    }

    TraceTag(ttidNetComm, "IRQ %lX is not valid for this device", dwIRQ);
    return FALSE;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::FValidateDMA
//
//  Purpose:    Validate that given DMA is valid in given config.
//
//  Arguments:
//      pConfig  [in]   configuration to use
//      dwDMA    [in]   dma setting to validate
//
//  Returns:    TRUE if dma setting is valid
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
BOOL CHwRes::FValidateDMA(PCONFIGURATION pConfig, ULONG dwDMA)
{
    PDMA_LIST pDMAList;

    AssertSz(m_fInitialized, "FValidateDMA called before "
             "CHwRes class HrInit'ed");

    Assert(pConfig != NULL);
    Assert(dwDMA > 0);
    // For each dma resource in the given config
    //     go through list of valid dma looking for given one
    //         if found, return TRUE
    for (size_t iRes = 0; iRes < pConfig->cResource; iRes++)
    {
        if (pConfig->aResource[iRes].ResourceType != ResType_DMA)
            continue;

        pDMAList = pConfig->aResource[iRes].pDMAList; // for easy access
        for (size_t iDMA = 0; iDMA < pDMAList->size(); iDMA++)
        {
            if ((*pDMAList)[iDMA]->dwDMA == dwDMA)
            {
                return TRUE;
            }
        }
    }

    TraceTag(ttidNetComm, "DMA %lX is not valid for this device", dwDMA);
    return FALSE;
}

//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::FGetIOEndPortGivenBasePort
//
//  Purpose:    Get an IO Range given only the BasePort
//
//  Arguments:
//      pConfig  [in]       configuration to use.
//      dwBase   [in]       Io base
//      pdwEnd   [out]      Io end is returned
//
//  Returns:    TRUE if Io base is valid in given config.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
BOOL CHwRes::FGetIOEndPortGivenBasePort(PCONFIGURATION pConfig, DWORD dwBase,
                               DWORD * pdwEnd)
{
    PIO_LIST pIOList;

    AssertSz(m_fInitialized, "FGetIOEndPortGivenBasePort called before "
             "CHwRes class HrInit'ed");

    Assert(pConfig != NULL);
    Assert(dwBase > 0);
    // For each resource in the given configuration
    //     if it's an IO Resource
    //         Go through the list of valid IO resources looking for
    //          a matching base port
    //              if found, then set the corresponding end port, return TRUE;
    for (size_t iRes = 0; iRes < pConfig->cResource; iRes++)
    {
        // ensure we're looking at an IO type
        if (pConfig->aResource[iRes].ResourceType != ResType_IO)
            continue;

        pIOList = pConfig->aResource[iRes].pIOList; // for easy access
        // go through all IO Elements in this list
        for (size_t iIO = 0; iIO < pIOList->size();  iIO++)
        {
            if ((*pIOList)[iIO]->dwIO_Base == dwBase)
            {
                // found matching IO base port
                *pdwEnd = (*pIOList)[iIO]->dwIO_End;
                return TRUE;
            }
        }
    }
    TraceTag(ttidNetComm, "IO %lX is not valid for this device", dwBase);
    return FALSE; // not found
}


//+---------------------------------------------------------------------------
//
//  Member:     CHwRes::FGetMEMEndGivenBase
//
//  Purpose:    Get a MEM range given the Mem base and config.
//
//  Arguments:
//      pConfig  [in]       configuration to use
//      dwBase   [in]       mem base
//      pdwEnd   [out]      mem end is returned.
//
//  Returns:    TRUE if the dwBase is a valid mem setting.
//
//  Author:     t-nabilr   07 Apr 1997
//
//  Notes:
//
BOOL CHwRes::FGetMEMEndGivenBase(PCONFIGURATION pConfig, DWORD dwBase,
                        DWORD * pdwEnd)
{
    PMEM_LIST pMEMList;

    AssertSz(m_fInitialized, "FGetMEMEndGivenBase called before "
             "CHwRes class HrInit'ed");

    Assert(pConfig != NULL);
    Assert(dwBase > 0);
    // For each resource in the given configuration
    //     if it's an MEM Resource
    //         Go through the list of valid MEM resources looking for
    //         a matching base port
    //              if found, then set the corresponding end port,return TRUE;
    for (size_t iRes = 0; iRes < pConfig->cResource; iRes++)
    {
        // ensure we're looking at an MEM type
        if (pConfig->aResource[iRes].ResourceType != ResType_Mem)
            continue;

        pMEMList = pConfig->aResource[iRes].pMEMList; // for easy access
        // go through all MEM Elements in this list
        for (size_t iMEM = 0; iMEM < pMEMList->size();  iMEM++)
        {
            if ((*pMEMList)[iMEM]->dwMEM_Base == dwBase)
            {
                // found matching MEM base addr
                *pdwEnd = (*pMEMList)[iMEM]->dwMEM_End;
                return TRUE;
            }
        }
    }
    TraceTag(ttidNetComm, "Memory %lX is not valid for this device", dwBase);
    return FALSE; // not found
}

//$REVIEW (t-pkoch) this function isn't yet in our custom STL...
//    it can be removed later (when it causes errors)

template<class T> void os_release(vector<T> & v)
{
    for(vector<T>::iterator iterDelete = v.begin() ; iterDelete != v.end() ;
        ++iterDelete)
        delete *iterDelete;
}


CHwRes::~CHwRes()
{
    AssertSz(m_fHrInitCalled, "CHwRes destructor called before "
             "CHwRes::HrInit() called");

    vector<CONFIGURATION *>::iterator ppConfig;
    RESOURCE *  pRes;

    // Delete everything from m_ConfigList.
    for (ppConfig = m_ConfigList.begin(); ppConfig != m_ConfigList.end();
         ppConfig++)
    {
        for (size_t iRes = 0; iRes < (*ppConfig)->cResource; iRes++)
        {
            pRes = &((*ppConfig)->aResource[iRes]);
            switch(pRes->ResourceType)
            {
            case ResType_IRQ:
                os_release(*(pRes->pIRQList));
                delete pRes->pIRQList;
                break;
            case ResType_DMA:
                os_release(*(pRes->pDMAList));
                delete pRes->pDMAList;
                break;
            case ResType_IO:
                os_release(*(pRes->pIOList));
                delete pRes->pIOList;
                break;
            case ResType_Mem:
                os_release(*(pRes->pMEMList));
                delete pRes->pMEMList;
                break;
            }
        }
        delete *ppConfig;
    }


    ReleaseObj(m_pnccItem);
}