#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
#include <tchar.h>
#include <stdlib.h>
#include <string.h>
#include <regstr.h>
#include <cfgmgr32.h>
#include "netinfo.h"


// Globals

DEV_INFO *g_pdiDevList;          // Head of device list



/*++

Routine Description: (21) GetDevNodeInfoAndCreateNewDevInfoNode

   Creates new list node, then gets registry and resource information for
   a specific device and copies it into that node. Finally, adds new node
   to beginning of linked list

Arguments:

    dnDevNode:    the device to find information about
    szDevNodeID:  the registry path name of the device
    szEnumBuffer: name of enumerator this device is under

Return Value:                                         
    BOOL: TRUE if function succeeds, FALSE if not

--*/
BOOL GetDevNodeInfoAndCreateNewDevInfoNode(IN DEVNODE dnDevNode,
                                           IN PTCHAR  szDevNodeID,
                                           IN PTCHAR  szEnumBuffer)
{
   LOG_CONF  lcLogConf = 0, lcLogConfNew;
   CONFIGRET cmret, cmret2;

   PDEV_INFO pdiDevInfo=(PDEV_INFO)malloc(sizeof(DEV_INFO));

   int       i;
   BOOL      boolForcedFound = FALSE, boolAllocFound = FALSE;
   USHORT    ushLogConfType[4] = {BOOT_LOG_CONF, ALLOC_LOG_CONF, BASIC_LOG_CONF, FORCED_LOG_CONF};

   if (pdiDevInfo == NULL)
      goto RetFALSE;


   // If this is not a PnP device, skip it
   if (!lstrcmpi(szEnumBuffer, TEXT("Root"))) {
      free(pdiDevInfo);
      goto RetTRUE;

   }

   //
   // Initialize fields inside the node
   //
   if (!InitializeInfoNode(pdiDevInfo, szDevNodeID, dnDevNode)) {
      // This is a device we don't want to list. Skip it
      free(pdiDevInfo);
      goto RetTRUE;
   }

   
   for (i = 0; i < NUM_LOG_CONF_TYPES; i++) {
      // Get logical configuration information
      cmret = CM_Get_First_Log_Conf(&lcLogConfNew,
                                    dnDevNode,
                                    ushLogConfType[i]);

      while (CR_SUCCESS == cmret) {
         lcLogConf = lcLogConfNew;

         if (ALLOC_LOG_CONF == ushLogConfType[i]) {
            boolAllocFound = TRUE;
         }

         if (!(GetResDesList(pdiDevInfo, lcLogConf, ushLogConfType[i]))) {
            goto RetFALSE;
         }

         cmret = CM_Get_Next_Log_Conf(&lcLogConfNew,
                                      lcLogConf,
                                      0);

         cmret2 = CM_Free_Log_Conf_Handle(lcLogConf);

      }
   }

   //
   // If device has no Alloc configurations, skip
   // to the next device
   //
   if (!boolAllocFound) {



      //free(pdiDevInfo);
      //goto RetTRUE;
   }

   //
   // Insert new pdiDevInfo into Linked List of DevNodes
   //
   if (g_pdiDevList == NULL) {
      //
      // DevList is empty
      //
      g_pdiDevList = pdiDevInfo;
   } else {
      //
      // Add new pdiDevInfo to beginning of linked list
      //
      pdiDevInfo->Next = g_pdiDevList;
      g_pdiDevList->Prev = pdiDevInfo;

      g_pdiDevList = pdiDevInfo;
   }

   RetTRUE:
   return TRUE;

   RetFALSE:
   return FALSE;

} 





/*++

Routine Description: (20) ParseEnumerator

   Gets devices listed under enumerator name in registry

Arguments:

    szEnumBuffer: the enumerator name

Return Value:

    BOOL: TRUE if function succeeds, FALSE if not

--*/
BOOL 
ParseEnumerator(IN PTCHAR szEnumBuffer)
{

   PTCHAR    szDevIDBuffer = NULL;
   PTCHAR    szDevNodeID = NULL;
   ULONG     ulDevIDBufferLen = 0, ulCount = 0, ulStart = 0;
   CONFIGRET cmret = CR_SUCCESS;
   DEVNODE   dnDevNode;
   BOOL      bReturn = TRUE;

   //
   // Get buffer length
   //
   cmret = CM_Get_Device_ID_List_Size(&ulDevIDBufferLen,
                                      szEnumBuffer,
                                      CM_GETIDLIST_FILTER_ENUMERATOR);

   if (CR_SUCCESS != cmret) {
      //ErrorLog(20, TEXT("CM_Get_Device_ID_List_Size"), cmret, NULL);
      bReturn = FALSE;
      goto RetFALSE;
   }

   if ((szDevIDBuffer =(PTCHAR) malloc(sizeof(TCHAR) * ulDevIDBufferLen)) == NULL ||
       (szDevNodeID   =(PTCHAR) malloc(sizeof(TCHAR) * ulDevIDBufferLen)) == NULL) {
      bReturn = FALSE;
      goto RetFALSE;
   }

   //
   // Get the Device ID List
   //
   cmret = CM_Get_Device_ID_List(szEnumBuffer,
                                 szDevIDBuffer,
                                 ulDevIDBufferLen,
                                 CM_GETIDLIST_FILTER_ENUMERATOR);

   if (CR_SUCCESS != cmret) {
      //ErrorLog(20, TEXT("CM_Get_Device_ID_List"), cmret, NULL);
      bReturn = FALSE;
      goto RetFALSE;
   }

   //
   // Note that ulDevIDBufferLen is a loose upper bound. The API may have
   // returned a size greater than the actual size of the list of strings.
   //
   for (ulCount = 0; ulCount < ulDevIDBufferLen; ulCount++) {
      ulStart = ulCount;

      if (szDevIDBuffer[ulCount] != '\0') {
         cmret = CM_Locate_DevNode(&dnDevNode,
                                   szDevIDBuffer + ulCount,
                                   CM_LOCATE_DEVNODE_NORMAL);

         //
         // Go to the next substring
         //
         while (szDevIDBuffer[ulCount] != TEXT('\0')) {
            ulCount++;

         }
         // Stop when we reach the double-NULL terminator

         if (szDevIDBuffer[ulCount+1] == TEXT('\0')) {
            ulCount=ulDevIDBufferLen;
            continue;
         }

         if (cmret == CR_SUCCESS) {
            _tcsncpy(szDevNodeID, szDevIDBuffer + ulStart, ulDevIDBufferLen);

            // Found the DevNode, so add its information to the device list
            if (!(GetDevNodeInfoAndCreateNewDevInfoNode(dnDevNode,
                                                        szDevNodeID,
                                                        szEnumBuffer))) {
               bReturn = FALSE;
               goto RetFALSE;
            }
         }
      }
   }

   bReturn = TRUE;
RetFALSE:
      // fix to rajeshm old code. Selective free bug number 137987
      if (NULL != szDevIDBuffer)
         free (szDevIDBuffer);
      if (NULL != szDevNodeID)
         free (szDevNodeID);
   return bReturn;
} 




void 
CollectDevData() {


   CONFIGRET cmret = CR_SUCCESS;
   ULONG     ulIndexNum = 0;
   ULONG     ulEnumBufferLen = 0;
   PTCHAR    szEnumBuffer;

   szEnumBuffer = (PTCHAR) malloc(sizeof(TCHAR) * MAX_DEVNODE_ID_LEN);

   if (NULL == szEnumBuffer)
      return;

   for (ulIndexNum = 0; cmret == CR_SUCCESS; ulIndexNum++) {

      ulEnumBufferLen = MAX_DEVNODE_ID_LEN;
      cmret = CM_Enumerate_Enumerators( ulIndexNum,
                                        szEnumBuffer,
                                        &ulEnumBufferLen,
                                        0);
      if (cmret == CR_SUCCESS)
         ParseEnumerator(szEnumBuffer);
   }

   free (szEnumBuffer);
}



BOOL CopyRegistryLine(IN DEVNODE   dnDevNode,
                      IN ULONG     ulPropertyType,
                      IN PDEV_INFO pdiDevInfo)

/*++

Routine Description: (22) CopyRegistryLine

   Copies one specific string of registry data to new list node

Arguments:

    dnDevNode:      the device to get information about
    ulpropertyType: which registry string to get
    pdiDevInfo:     the new list node

Return Value:

    BOOL: TRUE if function succeeds, FALSE if not

--*/
{
   ULONG     ulRegDataLen = 0, ulRegDataType = 0;
   CONFIGRET cmret = CR_SUCCESS;
   PTCHAR    szRegData = NULL;

   //
   // Get the length of the buffer  don't bother checking return value
   // If RegProperty doesn't exist, we'll just move on
   //
   CM_Get_DevNode_Registry_Property(dnDevNode,
                                    ulPropertyType,
                                    NULL,
                                    NULL,
                                    &ulRegDataLen,
                                    0);

   if (!ulRegDataLen ||
       (szRegData = (PTCHAR) malloc(sizeof(TCHAR) * ulRegDataLen)) == NULL) {
      goto RetFALSE;
   }

   //
   // Now get the registry information
   //
   cmret = CM_Get_DevNode_Registry_Property( dnDevNode,
                                            ulPropertyType,
                                            &ulRegDataType,
                                            szRegData,
                                            &ulRegDataLen,
                                            0);

   if (CR_SUCCESS == cmret) {
      if (!(CopyRegDataToDevInfoNode(pdiDevInfo,
                                     ulPropertyType,
                                     szRegData))) {
         goto RetFALSE;
      }
   }

   if (szRegData == NULL) 
      free (szRegData);
   return TRUE;


   RetFALSE:
   return FALSE;

} /* CopyRegistryLine */




/*++

Routine Description: (23) CopyRegDataToDevInfoNode

   Copies a registry string to a list node

Arguments:

    pdiDevInfo:     the new list node
    ulPropertyType: which registry string to copy
    szRegData:      the data to be copied

Return Value:

    BOOL: TRUE if function succeeds, FALSE if not

--*/
BOOL CopyRegDataToDevInfoNode(IN OUT PDEV_INFO pdiDevInfo,
                              IN     ULONG     ulPropertyType,
                              IN     PTCHAR    szRegData)
{
   if (pdiDevInfo == NULL) {
      goto RetFALSE;
   }

   switch (ulPropertyType) {
   case CM_DRP_DEVICEDESC:

      _tcsncpy(pdiDevInfo->szDescription, szRegData, MAX_PATH );
      break;

   case CM_DRP_HARDWAREID:

      _tcsncpy(pdiDevInfo->szHardwareID, szRegData, MAX_PATH );
      break;

   case CM_DRP_SERVICE:

      _tcsncpy(pdiDevInfo->szService, szRegData, MAX_PATH );
      break;

   case CM_DRP_CLASS:

      _tcsncpy(pdiDevInfo->szClass, szRegData, MAX_PATH );
      break;

   case CM_DRP_MFG:

      _tcsncpy(pdiDevInfo->szManufacturer, szRegData, MAX_PATH );
      break;

   case CM_DRP_CONFIGFLAGS:

      _tcsncpy(pdiDevInfo->szConfigFlags, szRegData, MAX_PATH );
      break;
   }

   return TRUE;

   RetFALSE:
   return FALSE;

} /* CopyRegDataToDevInfoNode */




/*++

Routine Description: (58) InitializeInfoNode

   Initialized fields inside the new node

Arguments:

    pdiDevInfo:  the node
    szDevNodeID: used to find the dnDevNode in the future
    dnDevNode:   the device we're storing information about

Return Value:

    BOOL: TRUE if we should keep this node, FALSE if we should throw it away

--*/
BOOL InitializeInfoNode(IN PDEV_INFO pdiDevInfo,
                        IN PTCHAR    szDevNodeID,
                        IN DEVNODE   dnDevNode)
{
   if (pdiDevInfo) {
      pdiDevInfo->Next = NULL;
      pdiDevInfo->Prev = NULL;

      pdiDevInfo->szDevNodeID[0]    = TEXT('\0');
      pdiDevInfo->szDescription[0]  = TEXT('\0');
      pdiDevInfo->szHardwareID[0]   = TEXT('\0');
      pdiDevInfo->szService[0]      = TEXT('\0');
      pdiDevInfo->szClass[0]        = TEXT('\0');
      pdiDevInfo->szManufacturer[0] = TEXT('\0');
      pdiDevInfo->szConfigFlags[0]  = TEXT('\0');
      pdiDevInfo->szFriendlyName[0] = TEXT('\0');

      pdiDevInfo->boolSavedOrigConfiguration = FALSE;
      pdiDevInfo->boolDisabled = FALSE;

      pdiDevInfo->prddForcedResDesData = NULL;
      pdiDevInfo->prddAllocResDesData  = NULL;
      pdiDevInfo->prddBasicResDesData  = NULL;
      pdiDevInfo->prddBootResDesData   = NULL;

      //
      // Store devNodeID in pdiDevInfo to get handles to devnode in future
      //
      _tcsncpy(pdiDevInfo->szDevNodeID, szDevNodeID, MAX_PATH);

      // Extract information from the registry about this DevNode
      CopyRegistryLine(dnDevNode, CM_DRP_DEVICEDESC,  pdiDevInfo);
      CopyRegistryLine(dnDevNode, CM_DRP_HARDWAREID,  pdiDevInfo);
      CopyRegistryLine(dnDevNode, CM_DRP_SERVICE,     pdiDevInfo);
      CopyRegistryLine(dnDevNode, CM_DRP_CLASS,       pdiDevInfo);
      CopyRegistryLine(dnDevNode, CM_DRP_MFG,         pdiDevInfo);
      CopyRegistryLine(dnDevNode, CM_DRP_CONFIGFLAGS, pdiDevInfo);

      RecordFriendlyName(pdiDevInfo);
   }

   //
   // Check the friendly name to see if we want to throw this node away
   //
   if (strcmp(pdiDevInfo->szFriendlyName, "STORAGE/Volume") == 0 ||
       strcmp(pdiDevInfo->szFriendlyName, "Unknown Device") == 0) {
      return FALSE;
   }

   return TRUE;

} /* InitializeInfoNode */




/*++

Routine Description: (57) RecordFriendlyName

   Finds the best user friendly name for this device

Arguments:

    pdiDevInfo: node containing all possible names

Return Value:

    void

--*/
void 
RecordFriendlyName(IN PDEV_INFO pdiDevInfo)
{
   
   if (pdiDevInfo) {
      
      if (pdiDevInfo->szDescription && pdiDevInfo->szDescription[0] != TEXT('\0'))

         _tcsncpy(pdiDevInfo->szFriendlyName, pdiDevInfo->szDescription, MAX_PATH );
      
      else if (pdiDevInfo->szHardwareID && pdiDevInfo->szHardwareID[0] != TEXT('\0')) 
         
         _tcsncpy(pdiDevInfo->szFriendlyName, pdiDevInfo->szHardwareID, MAX_PATH);
      
      else if (pdiDevInfo->szManufacturer && pdiDevInfo->szManufacturer[0] != TEXT('\0')) 
         
         _tcsncpy(pdiDevInfo->szFriendlyName, pdiDevInfo->szHardwareID, MAX_PATH);
      
      else if (pdiDevInfo->szService && pdiDevInfo->szService[0] != TEXT('\0')) 
         
         _tcsncpy(pdiDevInfo->szFriendlyName, pdiDevInfo->szService, MAX_PATH);
      
      else if (pdiDevInfo->szClass && pdiDevInfo->szClass[0] != TEXT('\0')) 
         
         _tcsncpy(pdiDevInfo->szFriendlyName, pdiDevInfo->szClass, MAX_PATH);
      
      else 
         _tcscpy(pdiDevInfo->szFriendlyName, TEXT("Unknown Device"));
   }
} /* RecordFriendlyName */







/*++

Routine Description: (24) GetResDesList

   Creates new resource data node and copies resource information to that node

Arguments:

    pdiDevInfo:    the list node which will contain the new resource node
    lcLogConf:     the logical configuration information
    ulLogConfType: FORCED, ALLOC, BOOT, or BASIC logical configuration

Return Value:

    BOOL: TRUE if function succeeds, FALSE if not

--*/
BOOL 
GetResDesList(IN OUT PDEV_INFO pdiDevInfo,
              IN     LOG_CONF  lcLogConf,
              IN     ULONG     ulLogConfType) 
{
   CONFIGRET     cmret, cmret2;
   RES_DES       rdResDes = 0, rdResDesNew;
   RESOURCEID    ridResourceID = 0;
   PRES_DES_DATA prddResDesData;

   prddResDesData = (PRES_DES_DATA)malloc(sizeof(RES_DES_DATA));

   if (prddResDesData == NULL)
      goto c0;
   
   prddResDesData->Next = NULL;
   prddResDesData->Prev = NULL;
   prddResDesData->pmresMEMResource = NULL;
   prddResDesData->piresIOResource = NULL;
   prddResDesData->pdresDMAResource = NULL;
   prddResDesData->pqresIRQResource = NULL;


   cmret = CM_Get_Next_Res_Des(&rdResDesNew,
                               lcLogConf,
                               ResType_All,
                               &ridResourceID,
                               0);

   //
   // Go through each resource type and copy data to new node
   //
   while (CR_SUCCESS == cmret) {
      rdResDes = rdResDesNew;

      if (ridResourceID >= ResType_Mem && ridResourceID <= ResType_IRQ) {
         if (!(ProcessResDesInfo(prddResDesData,
                                 rdResDes,
                                 ridResourceID))) {
            goto c1;
         }
      }

      cmret = CM_Get_Next_Res_Des(&rdResDesNew,
                                  rdResDes,
                                  ResType_All,
                                  &ridResourceID,
                                  0);

      cmret2 = CM_Free_Res_Des_Handle(rdResDes);

      if (cmret2 != CR_SUCCESS) {
         //ErrorLog(24, TEXT("CM_Free_Res_Des_Handle"), cmret2, NULL);
      }
   }
   
   // Add the new node to the linked list
   switch (ulLogConfType) {
   case FORCED_LOG_CONF:

      if (!pdiDevInfo->prddForcedResDesData) {
         //
         // This is the first entry into the linked list
         //
         pdiDevInfo->prddForcedResDesData = prddResDesData;
      } else {
         //
         // Add new node to beginning of linked list
         //
         prddResDesData->Next = pdiDevInfo->prddForcedResDesData;
         pdiDevInfo->prddForcedResDesData->Prev = prddResDesData;

         pdiDevInfo->prddForcedResDesData = prddResDesData;
      }
      break;

   case ALLOC_LOG_CONF:

      if (!pdiDevInfo->prddAllocResDesData) {
         //
         // This is the first entry into the linked list
         //
         pdiDevInfo->prddAllocResDesData = prddResDesData;
      } else {
         //
         // Add new node to beginning of linked list
         //
         prddResDesData->Next = pdiDevInfo->prddAllocResDesData;
         pdiDevInfo->prddAllocResDesData->Prev = prddResDesData;

         pdiDevInfo->prddAllocResDesData = prddResDesData;
      }
      break;

   case BASIC_LOG_CONF:

      if (!pdiDevInfo->prddBasicResDesData) {
         //
         // This is the first entry into the linked list
         //
         pdiDevInfo->prddBasicResDesData = prddResDesData;
      } else {
         //
         // Add new node to beginning of linked list
         //
         prddResDesData->Next = pdiDevInfo->prddBasicResDesData;
         pdiDevInfo->prddBasicResDesData->Prev = prddResDesData;

         pdiDevInfo->prddBasicResDesData = prddResDesData;
      }
      break;

   case BOOT_LOG_CONF:

      if (!pdiDevInfo->prddBootResDesData) {
         //
         // This is the first entry into the linked list
         //
         pdiDevInfo->prddBootResDesData = prddResDesData;
      } else {
         //
         // Add new node to beginning of linked list
         //
         prddResDesData->Next = pdiDevInfo->prddBootResDesData;
         pdiDevInfo->prddBootResDesData->Prev = prddResDesData;

         pdiDevInfo->prddBootResDesData = prddResDesData;
      }
      break;
   default:
      goto c1;
   }
   return TRUE;

   c1:
   free(prddResDesData);
   c0:
   return FALSE;

}


/* GetResDestList */

/*++

Routine Description: (25) ProcessResDesInfo

   Gets information for one resource descriptor

Arguments:

    prddResDesData: the new resource data node receiving the info
    rdResDes:       the resource descriptor containing the info
    ridResourceID:  tells the resource type (DMA, IO, MEM, IRQ, or CS)

Return Value:

    BOOL: TRUE if function succeeds, FALSE if not

--*/
BOOL 
ProcessResDesInfo(IN OUT PRES_DES_DATA prddResDesData,
                  IN     RES_DES       rdResDes,
                  IN     RESOURCEID    ridResourceID)
{
   PVOID     pvResDesDataBuffer = NULL;
   ULONG     ulResDesDataBufferLen;
   CONFIGRET cmret;

   cmret = CM_Get_Res_Des_Data_Size(&ulResDesDataBufferLen,
                                    rdResDes,
                                    0);

   if (CR_SUCCESS != cmret) {
      //ErrorLog(25, TEXT("CM_Get_Res_Des_Data_Size"), cmret, NULL);
      goto RetFALSE;
   }

   if ((pvResDesDataBuffer = malloc(sizeof(PVOID) * ulResDesDataBufferLen))
       == NULL) {
//      Log(25, SEV2, TEXT("resDesDataBuffer malloc size of %d failed."),
      //                  ulResDesDataBufferLen);
      goto RetFALSE;
   }

   //
   // Get the data
   //
   cmret = CM_Get_Res_Des_Data(rdResDes,
                               pvResDesDataBuffer,
                               ulResDesDataBufferLen,
                               0);

   if (CR_SUCCESS != cmret) {
      //ErrorLog(25, TEXT("CM_Get_Res_Des_Data"), cmret, NULL);
      goto RetFALSE;
   }

   //
   // Copy data into ResDesData node
   //
   switch (ridResourceID) {
   case ResType_Mem:

      prddResDesData->pmresMEMResource = (PMEM_RESOURCE)pvResDesDataBuffer;
      break;

   case ResType_IO:

      prddResDesData->piresIOResource = (PIO_RESOURCE)pvResDesDataBuffer;
      break;

   case ResType_DMA:

      prddResDesData->pdresDMAResource = (PDMA_RESOURCE)pvResDesDataBuffer;
      break;

   case ResType_IRQ:

      prddResDesData->pqresIRQResource = (PIRQ_RESOURCE)pvResDesDataBuffer;
      break;

   default:

//         Log(25, SEV2, TEXT("Illegal ResourceID - %ul"), ridResourceID);
      goto RetFALSE;
   }

   return TRUE;

   RetFALSE:
   return FALSE;

} /* ProcessResDesInfo */




/*++

Routine Description: (26) UpdateDeviceList

    Frees resource information for all devices and then collects the
    information again

Arguments:

    none (g_pdiDevList is global head of device list)

Return Value:

    BOOL: TRUE if function succeeds, FALSE if not

--*/
BOOL UpdateDeviceList()
{
   PDEV_INFO pdiTmpDevInfo;

   pdiTmpDevInfo = g_pdiDevList;

   //
   // Go through linked list and delete each node's ResDes lists
   //
   while (pdiTmpDevInfo) {
      if (pdiTmpDevInfo->prddForcedResDesData) {
         DeleteResDesDataNode(pdiTmpDevInfo->prddForcedResDesData);
         pdiTmpDevInfo->prddForcedResDesData = NULL;
      }

      if (pdiTmpDevInfo->prddAllocResDesData) {
         DeleteResDesDataNode(pdiTmpDevInfo->prddAllocResDesData);
         pdiTmpDevInfo->prddAllocResDesData = NULL;
      }

      if (pdiTmpDevInfo->prddBasicResDesData) {
         DeleteResDesDataNode(pdiTmpDevInfo->prddBasicResDesData);
         pdiTmpDevInfo->prddBasicResDesData = NULL;
      }

      if (pdiTmpDevInfo->prddBootResDesData) {
         DeleteResDesDataNode(pdiTmpDevInfo->prddBootResDesData);
         pdiTmpDevInfo->prddBootResDesData = NULL;
      }

      pdiTmpDevInfo = pdiTmpDevInfo->Next;
   }

   pdiTmpDevInfo = g_pdiDevList;

   //
   // Recreate the ResDesLists for each node
   //
   while (pdiTmpDevInfo) {
      if (!(RecreateResDesList(pdiTmpDevInfo, FORCED_LOG_CONF)))
         goto RetFALSE;

      if (!(RecreateResDesList(pdiTmpDevInfo, ALLOC_LOG_CONF)))
         goto RetFALSE;

      if (!(RecreateResDesList(pdiTmpDevInfo, BASIC_LOG_CONF)))
         goto RetFALSE;

      if (!(RecreateResDesList(pdiTmpDevInfo, BOOT_LOG_CONF)))
         goto RetFALSE;

      pdiTmpDevInfo = pdiTmpDevInfo->Next;
   }

   return TRUE;

   RetFALSE:
   return FALSE;

} /* UpdateDeviceList */




/*++

Routine Description: (27) DeleteResDesDataNode

    Deletes a string of RES_DES_DATA structures

Arguments:

    prddTmpResDes: the head of the linked list

Return Value:

    void

--*/
void 
DeleteResDesDataNode(IN PRES_DES_DATA prddTmpResDes)
{
   PRES_DES_DATA prddNextResDes;

   while (prddTmpResDes) {
      prddNextResDes = prddTmpResDes->Next;

      free (prddTmpResDes);
      prddTmpResDes = prddNextResDes;
   }

} /* DeleteResDesDataNode */





/*++

Routine Description: (56) CopyDataToLogConf

   Calls CM_Add_Res_Des to add a resDes to a lcLogConf

Arguments:

    lcLogConf:     the lcLogConf receiving the resDes
    ridResType:    ResType_Mem, IO, DMA or IRQ
    pvResData:     the new data
    ulResourceLen: size of the data

Return Value:

    BOOL: TRUE if the CM call succeeds, FALSE if not

--*/
BOOL 
CopyDataToLogConf(IN LOG_CONF   lcLogConf,
                  IN RESOURCEID ridResType,
                  IN PVOID      pvResData,
                  IN ULONG      ulResourceLen)
{
   CONFIGRET cmret;
   RES_DES   rdResDes;

   //
   // Copy the data to the logConf
   //
   cmret = CM_Add_Res_Des(&rdResDes,
                          lcLogConf,
                          ridResType,
                          pvResData,
                          ulResourceLen,
                          0);

   if (CR_SUCCESS != cmret) {

      goto RetFALSE;
   }

   return TRUE;

   RetFALSE:
   return FALSE;

} /* CopyDataToLogConf */



/*++

Routine Description: (28) RecreateResDesList

    Uses CM calls to find ResDes information and creates linked list
    of this information inside of given DEV_INFO

Arguments:

    pdiTmpDevInfo: the node receiving the information
    ulLogConfType: the LogConf type (FORCED_LOG_CONF,
                                     ALLOC_LOG_CONF,
                                     BASIC_LOG_CONF,
                                     BOOT_LOG_CONF)

Return Value:

    BOOL: TRUE if function succeeds, FALSE if not

--*/
BOOL 
RecreateResDesList(IN OUT PDEV_INFO pdiTmpDevInfo,
                   IN     ULONG     ulLogConfType)
{
   CONFIGRET cmret, cmret2;
   DEVNODE   dnDevNode;
   LOG_CONF  lcLogConf, lcLogConfNew;

   //
   // Get handle to the devnode
   //
   cmret = CM_Locate_DevNode(&dnDevNode,
                             pdiTmpDevInfo->szDevNodeID,
                             CM_LOCATE_DEVNODE_NORMAL);

   if (CR_SUCCESS != cmret) {
      //ErrorLog(28, TEXT("CM_Locate_DevNode"), cmret, NULL);
      goto RetFALSE;
   }

   //
   // Get logical configuration information
   //
   cmret = CM_Get_First_Log_Conf(&lcLogConfNew,
                                 dnDevNode,
                                 ulLogConfType);

   while (CR_SUCCESS == cmret) {
      lcLogConf = lcLogConfNew;

      if (!(GetResDesList(pdiTmpDevInfo, lcLogConf, ulLogConfType))) {
         goto RetFALSE;
      }

      cmret = CM_Get_Next_Log_Conf(&lcLogConfNew,
                                   lcLogConf,
                                   0);

      cmret2 = CM_Free_Log_Conf_Handle(lcLogConf);

      if (CR_SUCCESS != cmret2) {
         //ErrorLog(28, TEXT("CM_Free_Log_Conf"), cmret2, NULL);
      }
   }

   return TRUE;

   RetFALSE:
   return FALSE;

} /* RecreateResDesList */





void Cleanup()
{
   PDEV_INFO pdiDevInfo = g_pdiDevList;
   PDEV_INFO pdiNextInfoNode;



   while (pdiDevInfo) {

      pdiNextInfoNode = pdiDevInfo->Next;

      DeleteResDesDataNode(pdiDevInfo->prddForcedResDesData);
      DeleteResDesDataNode(pdiDevInfo->prddAllocResDesData);
      DeleteResDesDataNode(pdiDevInfo->prddBasicResDesData);
      DeleteResDesDataNode(pdiDevInfo->prddBootResDesData);

      free(pdiDevInfo);

      pdiDevInfo = pdiNextInfoNode;
   }

} /* Cleanup */
