/*++

Copyright (c) 1995-2000  Microsoft Corporation

Module Name:

    rlogconf.c

Abstract:

    This module contains the server-side logical configuration APIs.

                  PNP_AddEmptyLogConf
                  PNP_FreeLogConf
                  PNP_GetFirstLogConf
                  PNP_GetNextLogConf
                  PNP_GetLogConfPriority

Author:

    Paula Tomlinson (paulat) 9-27-1995

Environment:

    User-mode only.

Revision History:

    27-Sept-1995     paulat

        Creation and initial implementation.

--*/


//
// includes
//
#include "precomp.h"
#include "umpnpi.h"
#include "umpnpdat.h"


//
// Prototypes used in this routine and in rresdes.c
//
CONFIGRET
GetLogConfData(
    IN  HKEY    hKey,
    IN  ULONG   ulLogConfType,
    OUT PULONG  pulRegDataType,
    OUT LPWSTR  pszValueName,
    OUT LPBYTE  *ppBuffer,
    OUT PULONG  pulBufferSize
    );

PCM_FULL_RESOURCE_DESCRIPTOR
AdvanceResourcePtr(
    IN  PCM_FULL_RESOURCE_DESCRIPTOR pRes
    );

PIO_RESOURCE_LIST
AdvanceRequirementsPtr(
    IN  PIO_RESOURCE_LIST   pReq
    );

//
// Prototypes from rresdes.c
//

BOOL
FindLogConf(
    IN  LPBYTE  pList,
    OUT LPBYTE  *ppLogConf,
    IN  ULONG   RegDataType,
    IN  ULONG   ulTag
    );

PIO_RESOURCE_DESCRIPTOR
AdvanceRequirementsDescriptorPtr(
    IN  PIO_RESOURCE_DESCRIPTOR pReqDesStart,
    IN  ULONG                   ulIncrement,
    IN  ULONG                   ulRemainingRanges,
    OUT PULONG                  pulRangeCount
    );

//
// private prototypes
//

BOOL
MigrateObsoleteDetectionInfo(
    IN LPWSTR   pszDeviceID,
    IN HKEY     hLogConfKey
    );


//
// global data
//
extern HKEY ghEnumKey;      // Key to HKLM\CCC\System\Enum - DO NOT MODIFY



CONFIGRET
PNP_AddEmptyLogConf(
    IN  handle_t   hBinding,
    IN  LPWSTR     pDeviceID,
    IN  ULONG      ulPriority,
    OUT PULONG     pulTag,
    IN  ULONG      ulFlags
   )

/*++

Routine Description:

  This the server-side of an RPC remote call.  This routine adds
  an empty logical configuration.

Arguments:

    hBinding      RPC binding handle.

    pDeviceID     Null-terminated device instance id string.

    ulPriority    Priority for new log conf.

    pulTag        Returns tag that identifies which log config this is.

    ulFlags       Describes type of log conf to add.

Return Value:

   If the specified device instance is valid, it returns CR_SUCCESS,
   otherwise it returns CR_ error code.

--*/

{
    CONFIGRET   Status = CR_SUCCESS;
    HKEY        hKey = NULL;
    WCHAR       szValueName[64];
    LPBYTE      pList = NULL;
    ULONG       Index = 0, ulListSize = 0, ulAddListSize = 0;
    ULONG       RegDataType = 0;

    //------------------------------------------------------------------
    // The BOOT, ALLOC, and FORCED config types are stored in a registry
    // value name of the format XxxConfig and the BASIC, FILTERED, and
    // OVERRIDE configs are stored in a registr value name of the format
    // XxxConfigVector. XxxConfig values contain the actual resource
    // description (REG_RESOURCE_LIST, CM_RESOURCE_LIST) while
    // XxxConfigVector values contain a list of resource requirements
    // (REG_RESOURCE_REQUIREMENTS_LIST, IO_RESOURCE_REQUIREMENTS_LIST).
    //
    // The policy for using the log conf and res des APIs is:
    // - BOOT, ALLOC, and FORCED are defined to only have one log conf.
    // - Although callers always specify a complete XXX_RESOURCE type
    //   structure for the data when adding resource descriptors to
    //   a log conf, I will ignore the resource specific portion of
    //   the XXX_DES structure for FILTERED, BASIC, and OVERRIDE.
    //   Likewise I will ignore any XXX_RANGE structures for ALLOC,
    //   BOOT or FORCED log config types.
    //------------------------------------------------------------------

    try {
        //
        // verify client access
        //
        if (!VerifyClientAccess(hBinding, &gLuidLoadDriverPrivilege)) {
            Status = CR_ACCESS_DENIED;
            goto Clean0;
        }

        //
        // initialize output parameters
        //
        if (!ARGUMENT_PRESENT(pulTag)) {
            Status = CR_INVALID_POINTER;
            goto Clean0;
        } else {
            *pulTag = 0;
        }

        //
        // validate parameters
        //
        if (INVALID_FLAGS(ulFlags, LOG_CONF_BITS | PRIORITY_BIT)) {
            Status = CR_INVALID_FLAG;
            goto Clean0;
        }

        //
        // make sure original caller didn't specify root devnode
        //
        if (!IsLegalDeviceId(pDeviceID) || IsRootDeviceID(pDeviceID)) {
            Status = CR_INVALID_DEVNODE;
            goto Clean0;
        }

        //
        // open a key to the device's LogConf subkey
        //
        Status = OpenLogConfKey(pDeviceID, ulFlags & LOG_CONF_BITS, &hKey);
        if (Status != CR_SUCCESS) {
            goto Clean0;
        }

        MigrateObsoleteDetectionInfo(pDeviceID, hKey);

        //
        // Retrieve log conf data from the registry
        //
        Status = GetLogConfData(hKey, ulFlags & LOG_CONF_BITS,
                                &RegDataType, szValueName,
                                &pList, &ulListSize);

        //-----------------------------------------------------------
        // Specified log conf type contains Resource Data only
        //-----------------------------------------------------------

        if (RegDataType == REG_RESOURCE_LIST) {

            if (Status != CR_SUCCESS || ulListSize == 0) {
                //
                // This is the first log conf of this type: create a new
                // log conf with an empty res des.
                //
                PCM_RESOURCE_LIST pResList = NULL;

                Status = CR_SUCCESS;
                ulListSize = sizeof(CM_RESOURCE_LIST) -
                             sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);

                pList = HeapAlloc(ghPnPHeap, HEAP_ZERO_MEMORY, ulListSize);
                if (pList == NULL) {
                    Status = CR_OUT_OF_MEMORY;
                    goto Clean0;
                }

                //
                // initialize the config list header info
                //
                pResList = (PCM_RESOURCE_LIST)pList;
                pResList->Count = 1;
                pResList->List[0].InterfaceType                = InterfaceTypeUndefined;
                pResList->List[0].BusNumber                    = 0;
                pResList->List[0].PartialResourceList.Version  = NT_RESLIST_VERSION;
                pResList->List[0].PartialResourceList.Revision = NT_RESLIST_REVISION;
                pResList->List[0].PartialResourceList.Count    = 0;

            } else {
                //
                // There is already at least one log conf of this type, so add
                // a new empty log conf to the log conf list (priority ignored)
                //
                PCM_RESOURCE_LIST            pResList = (PCM_RESOURCE_LIST)pList;
                PCM_FULL_RESOURCE_DESCRIPTOR pRes = NULL;

                //
                // realloc the existing log conf list structs to hold another
                // log conf
                //
                ulAddListSize = sizeof(CM_FULL_RESOURCE_DESCRIPTOR) -
                                sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);

                pResList = (PCM_RESOURCE_LIST)HeapReAlloc(ghPnPHeap, 0, pResList,
                                                      ulListSize + ulAddListSize);
                if (pResList == NULL) {
                    Status = CR_OUT_OF_MEMORY;
                    goto Clean0;
                }
                pList = (LPBYTE)pResList;

                //
                // Priorities are ignored for resource lists, so just add any
                // subsequent log confs to the end (they will be ignored by the
                // system anyway).
                //
                pRes = (PCM_FULL_RESOURCE_DESCRIPTOR)(&pResList->List[0]); // first lc
                for (Index = 0; Index < pResList->Count; Index++) {
                    pRes = AdvanceResourcePtr(pRes);        // next lc
                }

                //
                // initialize the new empty log config
                //
                pResList->Count++;
                pRes->InterfaceType                = InterfaceTypeUndefined;
                pRes->BusNumber                    = 0;
                pRes->PartialResourceList.Version  = NT_RESLIST_VERSION;
                pRes->PartialResourceList.Revision = NT_RESLIST_REVISION;
                pRes->PartialResourceList.Count    = 0;

                *pulTag = Index;
            }
        }

        //-----------------------------------------------------------
        // Specified log conf type contains requirements data only
        //-----------------------------------------------------------
        else if (RegDataType == REG_RESOURCE_REQUIREMENTS_LIST) {

            if (Status != CR_SUCCESS || ulListSize == 0) {
                //
                // This is the first log conf of this type: create a new
                // log conf (IO_RESOURCE_LIST) with a single res des
                // (IO_RESOURCE_DESCRIPTOR) for the config data.
                //
                PIO_RESOURCE_REQUIREMENTS_LIST pReqList = NULL;
                PIO_RESOURCE_LIST              pReq = NULL;

                Status = CR_SUCCESS;
                ulListSize = sizeof(IO_RESOURCE_REQUIREMENTS_LIST);

                pReqList = HeapAlloc(ghPnPHeap, HEAP_ZERO_MEMORY, ulListSize);
                if (pReqList == NULL) {
                    Status = CR_OUT_OF_MEMORY;
                    goto Clean0;
                }
                pList = (LPBYTE)pReqList;

                //
                // initialize the config list header info
                //
                // There's room for one IO_RESOURCE_DESCRIPTOR embedded in
                // the IO_RESOURCE_LIST structure and by definition the first
                // one is a ConfigData type descriptor (user-mode always
                // specifies a priority value so we always have a ConfigData
                // struct).
                //
                pReqList->ListSize         = ulListSize;
                pReqList->InterfaceType    = InterfaceTypeUndefined;
                pReqList->BusNumber        = 0;
                pReqList->SlotNumber       = 0;
                pReqList->AlternativeLists = 1;

                pReq = (PIO_RESOURCE_LIST)(&pReqList->List[0]); // first lc
                pReq->Version  = NT_REQLIST_VERSION;
                pReq->Revision = NT_REQLIST_REVISION;
                pReq->Count    = 1;

                pReq->Descriptors[0].Option = IO_RESOURCE_PREFERRED;
                pReq->Descriptors[0].Type = CmResourceTypeConfigData;
                pReq->Descriptors[0].u.ConfigData.Priority = ulPriority;

            } else {
                //
                // There is already at least one log conf of this type, so add
                // a new empty log conf to the log conf list (always at the end)
                //
                PIO_RESOURCE_REQUIREMENTS_LIST pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)pList;
                PIO_RESOURCE_LIST              pReq = NULL;

                //
                // realloc the existing log conf list structs to hold another
                // log conf
                //
                ulAddListSize = sizeof(IO_RESOURCE_LIST);

                pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)HeapReAlloc(ghPnPHeap, 0, pReqList,
                                                                   ulListSize + ulAddListSize);
                if (pReqList == NULL) {
                    Status = CR_OUT_OF_MEMORY;
                    goto Clean0;
                }
                pList = (LPBYTE)pReqList;

                //
                // Skip past all the existing log confs to the new space at the
                // end of the reallocated buffer.
                //
                pReq = (PIO_RESOURCE_LIST)(&pReqList->List[0]); // first lc
                for (Index = 0; Index < pReqList->AlternativeLists; Index++) {
                    pReq = AdvanceRequirementsPtr(pReq);        // next lc
                }

                //
                // initialize the new empty log config (including the embedded
                // ConfigData structure).
                //
                pReqList->AlternativeLists++;
                pReqList->ListSize = ulListSize + ulAddListSize;

                pReq->Version  = NT_REQLIST_VERSION;
                pReq->Revision = NT_REQLIST_REVISION;
                pReq->Count    = 1;

                memset(&pReq->Descriptors[0], 0, sizeof(IO_RESOURCE_DESCRIPTOR));
                pReq->Descriptors[0].Option = IO_RESOURCE_PREFERRED;
                pReq->Descriptors[0].Type = CmResourceTypeConfigData;
                pReq->Descriptors[0].u.ConfigData.Priority = ulPriority;

                *pulTag = Index;
            }

        } else {
            Status = CR_FAILURE;
            goto Clean0;
        }

        //
        // Write out the new/updated log conf list to the registry
        //
        if (RegSetValueEx(hKey, szValueName, 0, RegDataType,
                          pList, ulListSize + ulAddListSize)
                         != ERROR_SUCCESS) {
            Status = CR_REGISTRY_ERROR;
            goto Clean0;
        }

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    if (hKey != NULL) {
        RegCloseKey(hKey);
    }
    if (pList != NULL) {
        HeapFree(ghPnPHeap, 0, pList);
    }

    return Status;

} // PNP_AddEmptyLogConf



CONFIGRET
PNP_FreeLogConf(
    IN handle_t   hBinding,
    IN LPWSTR     pDeviceID,
    IN ULONG      ulType,
    IN ULONG      ulTag,
    IN ULONG      ulFlags
    )

/*++

Routine Description:

  This the server-side of an RPC remote call.  This routine frees a
  logical configuration.

Arguments:

    hBinding      RPC binding handle.

    pDeviceID     Null-terminated device instance id string.

    ulType        Identifies which type of log conf is requested.

    ulTag         Identifies which log conf from the specified type
                  of log conf we want.

    ulFlags       Not used, must be zero.

Return Value:

   If the specified device instance is valid, it returns CR_SUCCESS,
   otherwise it returns CR_ error code.

--*/

{
    CONFIGRET   Status = CR_SUCCESS;
    HKEY        hKey = NULL;
    WCHAR       szValueName[64];
    LPBYTE      pList = NULL, pTemp = NULL, pNext = NULL;
    ULONG       RegDataType = 0, Index = 0, ulListSize = 0, ulSize = 0;

    try {
        //
        // verify client access
        //
        if (!VerifyClientAccess(hBinding, &gLuidLoadDriverPrivilege)) {
            Status = CR_ACCESS_DENIED;
            goto Clean0;
        }

        //
        // validate parameters
        //
        if (INVALID_FLAGS(ulFlags, 0)) {
            Status = CR_INVALID_FLAG;
            goto Clean0;
        }

        //
        // make sure original caller didn't specify root devnode (this
        // can't happen but Win95 does the check anyway)
        //
        if (!IsLegalDeviceId(pDeviceID) || IsRootDeviceID(pDeviceID)) {
            Status = CR_INVALID_DEVNODE;
            goto Clean0;
        }

        //
        // open a key to the device's LogConf subkey
        //
        Status = OpenLogConfKey(pDeviceID, ulType, &hKey);
        if (Status != CR_SUCCESS) {
            //
            // if the device id or LogConf subkey is not in registry,
            // that's okay, by definition the log conf is freed since it
            // doesn't exist
            //
            goto Clean0;
        }

        //
        // Retrieve log conf data from the registry
        //
        Status = GetLogConfData(hKey, ulType,
                                &RegDataType, szValueName,
                                &pList, &ulListSize);

        if (Status != CR_SUCCESS) {
            Status = CR_INVALID_LOG_CONF;
            goto Clean0;
        }

        //
        // If the log conf to free is the one and only log conf of this
        // type then delete the corresponding registry values
        //
        if ((RegDataType == REG_RESOURCE_LIST &&
            ((PCM_RESOURCE_LIST)pList)->Count <= 1) ||
            (RegDataType == REG_RESOURCE_REQUIREMENTS_LIST &&
            ((PIO_RESOURCE_REQUIREMENTS_LIST)pList)->AlternativeLists <= 1)) {

            KdPrintEx((DPFLTR_PNPMGR_ID,
                       DBGF_REGISTRY,
                       "PNP_FreeLogConf: Deleting Value %ws from Device %ws\r\n",
                       szValueName,
                       pDeviceID));

            RegDeleteValue(hKey, szValueName);
            goto Clean0;
        }

        //
        // There are other log confs besides the one to delete, so I'll
        // have to remove the log conf from the data structs and resave
        // to the registry
        //

        //-----------------------------------------------------------
        // Specified log conf type contains Resource Data only
        //-----------------------------------------------------------
        if (RegDataType == REG_RESOURCE_LIST) {

            PCM_RESOURCE_LIST            pResList = (PCM_RESOURCE_LIST)pList;
            PCM_FULL_RESOURCE_DESCRIPTOR pRes = NULL;

            if (ulTag >= pResList->Count) {
                Status = CR_INVALID_LOG_CONF;
                goto Clean0;
            }

            //
            // skip to the log conf to be deleted
            //
            pRes = (PCM_FULL_RESOURCE_DESCRIPTOR)(&pResList->List[0]); // first lc
            for (Index = 0; Index < ulTag; Index++) {
                pRes = AdvanceResourcePtr(pRes);      // next lc
            }

            if (ulTag == pResList->Count-1) {
                //
                // If deleting the last log conf in the list, just truncate it
                //
                ulListSize = (ULONG)((ULONG_PTR)pRes - (ULONG_PTR)pResList);

            } else {
                //
                // Shift remaining log confs (after the log conf to be deleted)
                // up in the list, writing over the log conf to be deleted
                //
                pNext = (LPBYTE)AdvanceResourcePtr(pRes);
                ulSize = ulListSize - (DWORD)((ULONG_PTR)pNext - (ULONG_PTR)pResList);

                pTemp = HeapAlloc(ghPnPHeap, 0, ulSize);
                if (pTemp == NULL) {
                    Status = CR_OUT_OF_MEMORY;
                    goto Clean0;
                }

                memcpy(pTemp, pNext, ulSize);     // save in temp buffer
                memcpy(pRes, pTemp, ulSize);      // copy to deleted lc
                ulListSize -= (DWORD)((ULONG_PTR)pNext - (ULONG_PTR)pRes);
            }

            //
            // update the log conf list header
            //
            pResList->Count--;
        }

        //-----------------------------------------------------------
        // Specified log conf type contains requirements data only
        //-----------------------------------------------------------
        else if (RegDataType == REG_RESOURCE_REQUIREMENTS_LIST) {

            PIO_RESOURCE_REQUIREMENTS_LIST pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)pList;
            PIO_RESOURCE_LIST              pReq = NULL;

            if (ulTag >= pReqList->AlternativeLists) {
                Status = CR_INVALID_LOG_CONF;
                goto Clean0;
            }

            //
            // skip to the log conf to be deleted
            //
            pReq = (PIO_RESOURCE_LIST)(&pReqList->List[0]);    // first lc
            for (Index = 0; Index < ulTag; Index++) {
                pReq = AdvanceRequirementsPtr(pReq);           // next lc
            }

            //
            // If there's any log confs after the log conf that will be deleted,
            // then write them over the top of the log conf we're deleting and
            // truncate any left over data.
            //
            pNext = (LPBYTE)AdvanceRequirementsPtr(pReq);
            if (ulListSize > ((DWORD_PTR)pNext - (DWORD_PTR)pReqList)) {

                ulSize = ulListSize - (DWORD)((ULONG_PTR)pNext - (ULONG_PTR)pReqList);

                pTemp = HeapAlloc(ghPnPHeap, 0, ulSize);
                if (pTemp == NULL) {
                    Status = CR_OUT_OF_MEMORY;
                    goto Clean0;
                }

                memcpy(pTemp, pNext, ulSize);     // save in temp buffer
                memcpy(pReq, pTemp, ulSize);      // copy to deleted lc
                ulListSize -= (DWORD)((ULONG_PTR)pNext - (ULONG_PTR)pReq);

            } else {
                //
                // No log confs trailing the log conf to be deleted so just
                // truncate it.
                //
                ulListSize = (ULONG)((ULONG_PTR)pReq - (ULONG_PTR)pReqList);
            }

            //
            // update the log conf list header
            //
            pReqList->AlternativeLists--;
            pReqList->ListSize = ulListSize;
        }

        //
        // Write out the updated log conf list to the registry
        //
        if (RegSetValueEx(hKey, szValueName, 0, RegDataType, pList,
                          ulListSize) != ERROR_SUCCESS) {
            Status = CR_REGISTRY_ERROR;
            goto Clean0;
        }

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    if (hKey != NULL) {
        RegCloseKey(hKey);
    }
    if (pList != NULL) {
        HeapFree(ghPnPHeap, 0, pList);
    }
    if (pTemp != NULL) {
        HeapFree(ghPnPHeap, 0, pTemp);
    }

    return Status;

} // PNP_FreeLogConf



CONFIGRET
PNP_GetFirstLogConf(
    IN  handle_t   hBinding,
    IN  LPWSTR     pDeviceID,
    IN  ULONG      ulType,
    OUT PULONG     pulTag,
    IN  ULONG      ulFlags
   )

/*++

Routine Description:

  This the server-side of an RPC remote call.  This routine finds the
  first log conf of this type for this devnode.

Arguments:

    hBinding      RPC binding handle, not used.

    pDeviceID     Null-terminated device instance id string.

    ulType        Describes the type of log conf to find.

    pulTag        Returns tag that identifies which log config this is.

    ulFlags       Not used (but may specify LOG_CONF_BITS).

Return Value:

   If the specified device instance is valid, it returns CR_SUCCESS,
   otherwise it returns CR_ error code.

--*/

{
    CONFIGRET   Status = CR_SUCCESS;
    HKEY        hKey = NULL;
    LPBYTE      pList = NULL;
    WCHAR       szValueName[64];
    ULONG       RegDataType = 0, ulListSize = 0;

    UNREFERENCED_PARAMETER(hBinding);

    try {
        //
        // Initialize output parameters. The index of the "first" lc will always
        // be zero as long as at least one lc exists.
        //
        if (!ARGUMENT_PRESENT(pulTag)) {
            Status = CR_INVALID_POINTER;
        } else {
            *pulTag = 0;
        }

        //
        // validate parameters
        //
        if (INVALID_FLAGS(ulFlags, LOG_CONF_BITS)) {
            Status = CR_INVALID_FLAG;
            goto Clean0;
        }

        if (!IsLegalDeviceId(pDeviceID)) {
            Status = CR_INVALID_DEVNODE;
            goto Clean0;
        }

        //
        // open a key to the device's LogConf subkey. If the device id is not
        // in the registry, the devnode doesn't exist and therefore neither
        // does the log conf
        //
        Status = OpenLogConfKey(pDeviceID, ulType, &hKey);
        if (Status != CR_SUCCESS) {
            Status = CR_NO_MORE_LOG_CONF;
            goto Clean0;
        }

        //
        // Migrate any log conf data that might have been written to
        // registry by NT 4.0 Beta I code.
        //
        MigrateObsoleteDetectionInfo(pDeviceID, hKey);

        //
        // Retrieve log conf data from the registry
        //
        Status = GetLogConfData(hKey, ulType,
                                &RegDataType, szValueName,
                                &pList, &ulListSize);

        if (Status != CR_SUCCESS) {
            Status = CR_NO_MORE_LOG_CONF;
            goto Clean0;
        }

        //
        // Specified log conf type contains Resource Data only
        //
        if (RegDataType == REG_RESOURCE_LIST) {

            PCM_RESOURCE_LIST pResList = (PCM_RESOURCE_LIST)pList;

            if (pResList->Count == 0) {
                Status = CR_NO_MORE_LOG_CONF;
                goto Clean0;
            }
        }

        //
        // Specified log conf type contains requirements data only
        //
        else if (RegDataType == REG_RESOURCE_REQUIREMENTS_LIST) {

            PIO_RESOURCE_REQUIREMENTS_LIST pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)pList;

            if (pReqList->AlternativeLists == 0) {
                Status = CR_NO_MORE_LOG_CONF;
                goto Clean0;
            }
        }

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    if (hKey != NULL) {
        RegCloseKey(hKey);
    }
    if (pList != NULL) {
        HeapFree(ghPnPHeap, 0, pList);
    }

    return Status;

} // PNP_GetFirstLogConf



CONFIGRET
PNP_GetNextLogConf(
    IN  handle_t   hBinding,
    IN  LPWSTR     pDeviceID,
    IN  ULONG      ulType,
    IN  ULONG      ulCurrentTag,
    OUT PULONG     pulNextTag,
    IN  ULONG      ulFlags
    )

/*++

Routine Description:

  This the server-side of an RPC remote call.  This routine finds the
  next log conf of this type for this devnode.

Arguments:

    hBinding      RPC binding handle, not used.

    pDeviceID     Null-terminated device instance id string.

    ulType        Specifies what type of log conf to retrieve.

    ulCurrent     Specifies current log conf in the enumeration.

    pulNext       Returns next log conf of this type for this device id.

    ulFlags       Not used, must be zero.

Return Value:

   If the specified device instance is valid, it returns CR_SUCCESS,
   otherwise it returns CR_ error code.

--*/

{
    CONFIGRET   Status = CR_SUCCESS;
    HKEY        hKey = NULL;
    WCHAR       szValueName[64];
    ULONG       RegDataType = 0, ulListSize = 0;
    LPBYTE      pList = NULL;

    UNREFERENCED_PARAMETER(hBinding);

    try {
        //
        // Initialize output parameters
        //
        if (!ARGUMENT_PRESENT(pulNextTag)) {
            Status = CR_INVALID_POINTER;
            goto Clean0;
        } else {
            *pulNextTag = 0;
        }

        //
        // validate parameters
        //
        if (INVALID_FLAGS(ulFlags, 0)) {
            Status = CR_INVALID_FLAG;
            goto Clean0;
        }

        if (!IsLegalDeviceId(pDeviceID)) {
            Status = CR_INVALID_DEVNODE;
            goto Clean0;
        }

        //
        // open a key to the device's LogConf subkey. If the device id is not
        // in the registry, the devnode doesn't exist and therefore neither
        // does the log conf
        //
        Status = OpenLogConfKey(pDeviceID, ulType, &hKey);
        if (Status != CR_SUCCESS) {
            Status = CR_INVALID_LOG_CONF;
            goto Clean0;
        }

        //
        // Retrieve log conf data from the registry
        //
        Status = GetLogConfData(hKey, ulType,
                                &RegDataType, szValueName,
                                &pList, &ulListSize);

        if (Status != CR_SUCCESS) {
           Status = CR_NO_MORE_LOG_CONF;
           goto Clean0;
        }

        //
        // Specified log conf type contains Resource Data only
        //
        if (RegDataType == REG_RESOURCE_LIST) {

            PCM_RESOURCE_LIST pResList = (PCM_RESOURCE_LIST)pList;

            if (ulCurrentTag >= pResList->Count) {
                Status = CR_INVALID_LOG_CONF;
                goto Clean0;
            }

            //
            // Is the "current" log conf the last log conf?
            //
            if (ulCurrentTag == pResList->Count - 1) {
                Status = CR_NO_MORE_LOG_CONF;
                goto Clean0;
            }
        }

        //
        // Specified log conf type contains requirements data only
        //
        else if (RegDataType == REG_RESOURCE_REQUIREMENTS_LIST) {

            PIO_RESOURCE_REQUIREMENTS_LIST pReqList = (PIO_RESOURCE_REQUIREMENTS_LIST)pList;

            if (ulCurrentTag >= pReqList->AlternativeLists) {
                Status = CR_INVALID_LOG_CONF;
                goto Clean0;
            }

            //
            // Is the "current" log conf the last log conf?
            //
            if (ulCurrentTag == pReqList->AlternativeLists - 1) {
                Status = CR_NO_MORE_LOG_CONF;
                goto Clean0;
            }
        }

        //
        // There's at least one more log conf, return the next index value
        //
        *pulNextTag = ulCurrentTag + 1;

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    if (hKey != NULL) {
        RegCloseKey(hKey);
    }
    if (pList != NULL) {
        HeapFree(ghPnPHeap, 0, pList);
    }

    return Status;

} // PNP_GetNextLogConf



CONFIGRET
PNP_GetLogConfPriority(
    IN  handle_t hBinding,
    IN  LPWSTR   pDeviceID,
    IN  ULONG    ulType,
    IN  ULONG    ulTag,
    OUT PULONG   pPriority,
    IN  ULONG    ulFlags
    )

/*++

Routine Description:

  This the server-side of an RPC remote call.  This routine returns the
  priority value assigned to the specified log config.

Arguments:

    hBinding      RPC binding handle, not used.

    pDeviceID     Null-terminated device instance id string.

    ulType        Specifies what type of log conf to retrieve priority for.

    ulCurrent     Specifies current log conf in the enumeration.

    pulNext       Returns priority value of specified log conf.

    ulFlags       Not used, must be zero.

Return Value:

   If the specified device instance is valid, it returns CR_SUCCESS,
   otherwise it returns CR_ error code.

--*/

{
    CONFIGRET   Status = CR_SUCCESS;
    HKEY        hKey = NULL;
    WCHAR       szValueName[64];
    ULONG       RegDataType = 0, ulListSize = 0, index, count;
    LPBYTE      pList = NULL, pLogConf = NULL, pRD = NULL;
    PIO_RESOURCE_REQUIREMENTS_LIST pReqList = NULL;
    PIO_RESOURCE_LIST              pReq = NULL;
    PIO_RESOURCE_DESCRIPTOR        pReqDes = NULL;

    UNREFERENCED_PARAMETER(hBinding);

    try {
        //
        // Initialize output parameters
        //
        if (!ARGUMENT_PRESENT(pPriority)) {
            Status = CR_INVALID_POINTER;
            goto Clean0;
        } else {
            *pPriority = 0;
        }

        //
        // validate parameters
        //
        if (INVALID_FLAGS(ulFlags, 0)) {
            Status = CR_INVALID_FLAG;
            goto Clean0;
        }

        if (!IsLegalDeviceId(pDeviceID)) {
            Status = CR_INVALID_DEVNODE;
            goto Clean0;
        }

        //
        // open a key to the device's LogConf subkey. If the device id is not
        // in the registry, the devnode doesn't exist and therefore neither
        // does the log conf
        //
        Status = OpenLogConfKey(pDeviceID, ulType, &hKey);
        if (Status != CR_SUCCESS) {
            Status = CR_INVALID_LOG_CONF;
            goto Clean0;
        }

        //
        // Retrieve log conf data from the registry
        //
        Status = GetLogConfData(hKey, ulType,
                                &RegDataType, szValueName,
                                &pList, &ulListSize);

        if (Status != CR_SUCCESS) {
           Status = CR_INVALID_LOG_CONF;
           goto Clean0;
        }

        //
        // Priority values are only stored in requirements lists
        //
        if (RegDataType != REG_RESOURCE_REQUIREMENTS_LIST) {
            Status = CR_INVALID_LOG_CONF;
            goto Clean0;
        }

        //
        // Seek to the log conf that matches the log conf tag
        //
        if (!FindLogConf(pList, &pLogConf, RegDataType, ulTag)) {
            Status = CR_NO_SUCH_VALUE;
            goto Clean0;
        }

        //
        // Seek to the ConfigData res des, if any.
        //
        pReq = (PIO_RESOURCE_LIST)pLogConf;
        pReqDes = &pReq->Descriptors[0];        // first rd

        index = 0;
        count = 0;
        while (index < pReq->Count && pReqDes != NULL &&
               pReqDes->Type != CmResourceTypeConfigData) {

            pReqDes = AdvanceRequirementsDescriptorPtr(pReqDes, 1, pReq->Count - index, &count);
            index += count;  // index of actual rd's in the struct
        }

        if (pReqDes == NULL || pReqDes->Type != CmResourceTypeConfigData) {
            //
            // No config data so we can't determine the priority.
            //
            Status = CR_NO_SUCH_VALUE;
            goto Clean0;

        }

        *pPriority = pReqDes->u.ConfigData.Priority;

    Clean0:
        NOTHING;

    } except(EXCEPTION_EXECUTE_HANDLER) {
        Status = CR_FAILURE;
    }

    if (hKey != NULL) {
        RegCloseKey(hKey);
    }
    if (pList != NULL) {
        HeapFree(ghPnPHeap, 0, pList);
    }

    return Status;

} // PNP_GetLogConfPriority




//------------------------------------------------------------------------
// Private Utility Routines
//------------------------------------------------------------------------


CONFIGRET
GetLogConfData(
    IN  HKEY    hKey,
    IN  ULONG   ulLogConfType,
    OUT PULONG  pulRegDataType,
    OUT LPWSTR  pszValueName,
    OUT LPBYTE  *ppBuffer,
    OUT PULONG  pulBufferSize
    )
{
    switch (ulLogConfType) {
        //
        // BOOT, ALLOC, FORCED only have a Config value
        //
        case BOOT_LOG_CONF:
            lstrcpy(pszValueName, pszRegValueBootConfig);
            *pulRegDataType = REG_RESOURCE_LIST;
            break;

        case ALLOC_LOG_CONF:
            lstrcpy(pszValueName, pszRegValueAllocConfig);
            *pulRegDataType = REG_RESOURCE_LIST;
            break;

        case FORCED_LOG_CONF:
            lstrcpy(pszValueName, pszRegValueForcedConfig);
            *pulRegDataType = REG_RESOURCE_LIST;
            break;

        //
        // FILTERED, BASIC, OVERRIDE only have a Vector value
        //
        case FILTERED_LOG_CONF:
            lstrcpy(pszValueName, pszRegValueFilteredVector);
            *pulRegDataType = REG_RESOURCE_REQUIREMENTS_LIST;
            break;

        case BASIC_LOG_CONF:
            lstrcpy(pszValueName, pszRegValueBasicVector);
            *pulRegDataType = REG_RESOURCE_REQUIREMENTS_LIST;
            break;

        case OVERRIDE_LOG_CONF:
            lstrcpy(pszValueName, pszRegValueOverrideVector);
            *pulRegDataType = REG_RESOURCE_REQUIREMENTS_LIST;
            break;

        default:
            return CR_FAILURE;
    }

    //
    // retrieve the Log Conf registry data
    //
    if (RegQueryValueEx(hKey, pszValueName, NULL, NULL, NULL,
                        pulBufferSize) != ERROR_SUCCESS) {
        return CR_INVALID_LOG_CONF;
    }

    *ppBuffer = HeapAlloc(ghPnPHeap, 0, *pulBufferSize);
    if (*ppBuffer == NULL) {
        return CR_OUT_OF_MEMORY;
    }

    if (RegQueryValueEx(hKey, pszValueName, NULL, NULL,
                        (LPBYTE)*ppBuffer, pulBufferSize) != ERROR_SUCCESS) {
        return CR_INVALID_LOG_CONF;
    }

    return CR_SUCCESS;

} // GetLogConfData



PCM_FULL_RESOURCE_DESCRIPTOR
AdvanceResourcePtr(
    IN  PCM_FULL_RESOURCE_DESCRIPTOR pRes
    )
{
    // Given a resource pointer, this routine advances to the beginning
    // of the next resource and returns a pointer to it. I assume that
    // at least one more resource exists in the resource list.

    LPBYTE  p = NULL;
    ULONG   LastResIndex = 0;


    if (pRes == NULL) {
        return NULL;
    }

    //
    // account for the size of the CM_FULL_RESOURCE_DESCRIPTOR
    // (includes the header plus a single imbedded
    // CM_PARTIAL_RESOURCE_DESCRIPTOR struct)
    //
    p = (LPBYTE)pRes + sizeof(CM_FULL_RESOURCE_DESCRIPTOR);

    //
    // account for any resource descriptors in addition to the single
    // imbedded one I've already accounted for (if there aren't any,
    // then I'll end up subtracting off the extra imbedded descriptor
    // from the previous step)
    //
    p += (pRes->PartialResourceList.Count - 1) *
         sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);

    //
    // finally, account for any extra device specific data at the end of
    // the last partial resource descriptor (if any)
    //
    if (pRes->PartialResourceList.Count > 0) {

        LastResIndex = pRes->PartialResourceList.Count - 1;

        if (pRes->PartialResourceList.PartialDescriptors[LastResIndex].Type ==
                  CmResourceTypeDeviceSpecific) {

            p += pRes->PartialResourceList.PartialDescriptors[LastResIndex].
                       u.DeviceSpecificData.DataSize;
        }
    }

    return (PCM_FULL_RESOURCE_DESCRIPTOR)p;

} // AdvanceResourcePtr



PIO_RESOURCE_LIST
AdvanceRequirementsPtr(
    IN  PIO_RESOURCE_LIST   pReq
    )
{
    LPBYTE   p = NULL;

    if (pReq == NULL) {
        return NULL;
    }

    //
    // account for the size of the IO_RESOURCE_LIST (includes header plus
    // a single imbedded IO_RESOURCE_DESCRIPTOR struct)
    //
    p = (LPBYTE)pReq + sizeof(IO_RESOURCE_LIST);

    //
    // account for any requirements descriptors in addition to the single
    // imbedded one I've already accounted for (if there aren't any,
    // then I'll end up subtracting off the extra imbedded descriptor
    // from the previous step)
    //
    p += (pReq->Count - 1) * sizeof(IO_RESOURCE_DESCRIPTOR);

    return (PIO_RESOURCE_LIST)p;

} // AdvanceRequirementsPtr



BOOL
MigrateObsoleteDetectionInfo(
    IN LPWSTR   pszDeviceID,
    IN HKEY     hLogConfKey
    )
{
    LONG    RegStatus = ERROR_SUCCESS;
    HKEY    hKey = NULL;
    ULONG   ulSize = 0;
    LPBYTE  ptr = NULL;
    PCM_RESOURCE_LIST               pResList = NULL;
    PPrivate_Log_Conf               pDetectData = NULL;

    //
    // First, delete any of the log conf pairs that aren't valid any more
    //
    RegDeleteValue(hLogConfKey, TEXT("BootConfigVector"));
    RegDeleteValue(hLogConfKey, TEXT("AllocConfigVector"));
    RegDeleteValue(hLogConfKey, TEXT("ForcedConfigVector"));
    RegDeleteValue(hLogConfKey, TEXT("BasicConfig"));
    RegDeleteValue(hLogConfKey, TEXT("FilteredConfig"));
    RegDeleteValue(hLogConfKey, TEXT("OverrideConfig"));

    //
    // open the device instance key in the registry
    //
    if (RegOpenKeyEx(ghEnumKey, pszDeviceID, 0,
                     KEY_READ | KEY_WRITE, &hKey) != ERROR_SUCCESS) {
        goto Clean0;    // nothing to migrate
    }

    //
    // If there is already a boot log config value then we can't
    // migrate any old detect info
    //
    RegStatus = RegQueryValueEx(hLogConfKey, pszRegValueBootConfig,
                                NULL, NULL, NULL, &ulSize);

    if (RegStatus == ERROR_SUCCESS  &&  ulSize > 0) {
        goto Clean0;    // can't migrate
    }

    //
    // retrieve any old detect signature info
    //
    RegStatus = RegQueryValueEx(hKey, pszRegValueDetectSignature,
                                NULL, NULL, NULL, &ulSize);

    if ((RegStatus != ERROR_SUCCESS) || (ulSize == 0)) {
        goto Clean0;    // nothing to migrate
    }

    pDetectData = (PPrivate_Log_Conf)HeapAlloc(ghPnPHeap, 0, ulSize);

    if (pDetectData == NULL) {
        goto Clean0;    // insufficient memory
    }

    RegStatus = RegQueryValueEx(hKey, pszRegValueDetectSignature,
                                NULL, NULL, (LPBYTE)pDetectData, &ulSize);

    if ((RegStatus != ERROR_SUCCESS) || (ulSize == 0)) {
        goto Clean0;    // nothing to migrate
    }

    //
    // Create an empty boot log conf and add this class specific data
    // to it
    //
    ulSize = pDetectData->LC_CS.CS_Header.CSD_SignatureLength +
             pDetectData->LC_CS.CS_Header.CSD_LegacyDataSize +
             sizeof(GUID);

    pResList = HeapAlloc(ghPnPHeap, HEAP_ZERO_MEMORY, sizeof(CM_RESOURCE_LIST) + ulSize);

    if (pResList == NULL) {
        goto Clean0;    // insufficient memory
    }

    //
    // initialize resource list
    //
    pResList->Count = 1;
    pResList->List[0].InterfaceType                = InterfaceTypeUndefined;
    pResList->List[0].BusNumber                    = 0;
    pResList->List[0].PartialResourceList.Version  = NT_RESLIST_VERSION;
    pResList->List[0].PartialResourceList.Revision = NT_RESLIST_REVISION;
    pResList->List[0].PartialResourceList.Count    = 1;
    pResList->List[0].PartialResourceList.PartialDescriptors[0].Type =
                          CmResourceTypeDeviceSpecific;
    pResList->List[0].PartialResourceList.PartialDescriptors[0].ShareDisposition =
                          CmResourceShareUndetermined;
    pResList->List[0].PartialResourceList.PartialDescriptors[0].Flags =
                          (USHORT)pDetectData->LC_CS.CS_Header.CSD_Flags;
    pResList->List[0].PartialResourceList.PartialDescriptors[0].
                      u.DeviceSpecificData.DataSize = ulSize;
    pResList->List[0].PartialResourceList.PartialDescriptors[0].
                      u.DeviceSpecificData.Reserved1 =
                          pDetectData->LC_CS.CS_Header.CSD_LegacyDataSize;
    pResList->List[0].PartialResourceList.PartialDescriptors[0].
                      u.DeviceSpecificData.Reserved2 =
                          pDetectData->LC_CS.CS_Header.CSD_SignatureLength;

    //
    // copy the legacy and class-specific signature data
    //
    ptr = (LPBYTE)(&pResList->List[0].PartialResourceList.PartialDescriptors[1]);

    memcpy(ptr,
           pDetectData->LC_CS.CS_Header.CSD_Signature +
           pDetectData->LC_CS.CS_Header.CSD_LegacyDataOffset,
           pDetectData->LC_CS.CS_Header.CSD_LegacyDataSize);  // legacy data

    ptr += pDetectData->LC_CS.CS_Header.CSD_LegacyDataSize;

    memcpy(ptr,
           pDetectData->LC_CS.CS_Header.CSD_Signature,
           pDetectData->LC_CS.CS_Header.CSD_SignatureLength); // signature

    ptr += pDetectData->LC_CS.CS_Header.CSD_SignatureLength;

    memcpy(ptr,
           &pDetectData->LC_CS.CS_Header.CSD_ClassGuid,
           sizeof(GUID));                                     // GUID

    //
    // Write out the new/updated log conf list to the registry
    //
    RegSetValueEx(hLogConfKey, pszRegValueBootConfig, 0,
                  REG_RESOURCE_LIST, (LPBYTE)pResList,
                  ulSize + sizeof(CM_RESOURCE_LIST));

    //
    // Delete the old detect signature info
    //
    RegDeleteValue(hKey, pszRegValueDetectSignature);

 Clean0:

    if (hKey != NULL) {
        RegCloseKey(hKey);
    }
    if (pDetectData != NULL) {
        HeapFree(ghPnPHeap, 0, pDetectData);
    }
    if (pResList != NULL) {
        HeapFree(ghPnPHeap, 0, pResList);
    }

    return TRUE;

} // MigrateObsoleteDetectionInfo