/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: conflist.c Abstract: This module contains the API routines that manage conflict list reporting CM_Query_Resource_Conflict_List CM_Free_Resource_Conflict_Handle CM_Get_Resource_Conflict_Count CM_Get_Resource_Conflict_Details Author: Jamie Hunter (jamiehun) 4-14-1998 Environment: User mode only. Revision History: April-14-1998 jamiehun Addition of NT extended resource-conflict functions --*/ // // includes // #include "precomp.h" #pragma hdrstop #include "cfgi.h" typedef struct _CONFLICT_LIST_HEADER { HMACHINE Machine; // indicates relevent machine PPLUGPLAY_CONTROL_CONFLICT_LIST ConflictInfo; // data obtained via UMPNPMGR ULONG Signature; // marks this structure as a handle } CONFLICT_LIST_HEADER, *PCONFLICT_LIST_HEADER; // // Private prototypes // BOOL ValidateConfListHandle( PCONFLICT_LIST_HEADER pConfList ); VOID FreeConfListHandle( PCONFLICT_LIST_HEADER pConfList ); // // private prototypes from resdes.c // CONFIGRET CreateResDesHandle( PRES_DES prdResDes, DEVINST dnDevInst, ULONG ulLogType, ULONG ulLogTag, ULONG ulResType, ULONG ulResTag ); BOOL ValidateResDesHandle( PPrivate_Res_Des_Handle pResDes ); CONFIGRET Get32bitResDesFrom64bitResDes( IN RESOURCEID ResourceID, IN PCVOID ResData64, IN ULONG ResLen64, OUT PVOID * ResData32, OUT ULONG * ResLen32 ); // // private prototypes from logconf.c // CONFIGRET CreateLogConfHandle( PLOG_CONF plcLogConf, DEVINST dnDevInst, ULONG ulLogType, ULONG ulLogTag ); BOOL ValidateLogConfHandle( PPrivate_Log_Conf_Handle pLogConf ); // // API functions // CMAPI CONFIGRET WINAPI CM_Query_Resource_Conflict_List( OUT PCONFLICT_LIST pclConflictList, IN DEVINST dnDevInst, IN RESOURCEID ResourceID, IN PCVOID ResourceData, IN ULONG ResourceLen, IN ULONG ulFlags, IN HMACHINE hMachine ) /*++ Routine Description: Retrieves conflict list returns a handle for list Arguments: pclConflictList - holds returned conflict list handle dnDevInst Device we want to allocate a resource for ResourceID Type of resource, ResType_xxxx ResourceData Resource specific data ResourceLen length of ResourceData ulFlags Width of certain variable-size resource descriptor structure fields, where applicable. Currently, the following flags are defined: CM_RESDES_WIDTH_32 or CM_RESDES_WIDTH_64 If no flags are specified, the width of the variable-sized resource data supplied is assumed to be that native to the platform of the caller. hMachine - optional machine to query Return Value: CM status value --*/ { CONFIGRET Status = CR_SUCCESS; WCHAR DeviceID[MAX_DEVICE_ID_LEN]; PVOID hStringTable = NULL; handle_t hBinding = NULL; PPLUGPLAY_CONTROL_CONFLICT_LIST pConfList1 = NULL; PPLUGPLAY_CONTROL_CONFLICT_LIST pConfList2 = NULL; PCONFLICT_LIST_HEADER pConfListHeader = NULL; ULONG ConfListSize1; ULONG ConfListSize2; ULONG ulLen = MAX_DEVICE_ID_LEN; BOOL Success; PVOID ResourceData32 = NULL; ULONG ResourceLen32 = 0; try { // // validate parameters // if (dnDevInst == 0) { Status = CR_INVALID_DEVINST; goto Clean0; } if (INVALID_FLAGS(ulFlags, CM_RESDES_WIDTH_BITS)) { Status = CR_INVALID_FLAG; goto Clean0; } if ((ulFlags & CM_RESDES_WIDTH_32) && (ulFlags & CM_RESDES_WIDTH_64)) { Status = CR_INVALID_FLAG; goto Clean0; } #ifdef _WIN64 if ((ulFlags & CM_RESDES_WIDTH_BITS) == CM_RESDES_WIDTH_DEFAULT) { ulFlags |= CM_RESDES_WIDTH_64; } #endif // _WIN64 if (ulFlags & CM_RESDES_WIDTH_32) { ulFlags &= ~CM_RESDES_WIDTH_BITS; } if (pclConflictList == NULL) { Status = CR_INVALID_POINTER; goto Clean0; } if (ResourceData == NULL || ResourceLen == 0) { Status = CR_INVALID_POINTER; goto Clean0; } #if 0 if (ResourceID > ResType_MAX) { // ClassSpecific not allowed Status = CR_INVALID_RESOURCEID; goto Clean0; } #endif if (ResourceID == ResType_All) { Status = CR_INVALID_RESOURCEID; // can't specify All on a detect goto Clean0; } // // Initialize parameters // *pclConflictList = 0; // // setup rpc binding handle and string table handle // if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) { Status = CR_FAILURE; goto Clean0; } // // Make sure the server can support the client's 64-bit resdes request. // Only server versions 0x0501 and greater support CM_RESDES_WIDTH_64. // if (ulFlags & CM_RESDES_WIDTH_64) { if (!CM_Is_Version_Available_Ex((WORD)0x0501, hMachine)) { // // Server can only support 32-bit resdes. Have the client // convert the caller's 64-bit resdes to a 32-bit resdes for the // server. // ulFlags &= ~CM_RESDES_WIDTH_BITS; Status = Get32bitResDesFrom64bitResDes(ResourceID,ResourceData,ResourceLen,&ResourceData32,&ResourceLen32); if(Status != CR_SUCCESS) { goto Clean0; } if(ResourceData32) { ResourceData = ResourceData32; ResourceLen = ResourceLen32; } } } // // retreive device instance string that corresponds to dnDevInst // Success = pSetupStringTableStringFromIdEx(hStringTable, dnDevInst,DeviceID,&ulLen); if (Success == FALSE || INVALID_DEVINST(DeviceID)) { Status = CR_INVALID_DEVINST; goto Clean0; } pConfListHeader = (PCONFLICT_LIST_HEADER)pSetupMalloc(sizeof(CONFLICT_LIST_HEADER)); if (pConfListHeader == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } // // estimate size required to hold one conflict // ConfListSize1 = sizeof(PLUGPLAY_CONTROL_CONFLICT_LIST)+ // header + one entry sizeof(PLUGPLAY_CONTROL_CONFLICT_STRINGS)+ // strings marker (sizeof(WCHAR)*MAX_DEVICE_ID_LEN); // enough space for one string pConfList1 = (PPLUGPLAY_CONTROL_CONFLICT_LIST)pSetupMalloc(ConfListSize1); if (pConfList1 == NULL) { Status = CR_OUT_OF_MEMORY; goto Clean0; } // // first try // // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_QueryResConfList( hBinding, // rpc binding handle DeviceID, // device id string ResourceID, // resource type (LPBYTE)ResourceData, // actual res des data ResourceLen, // size in bytes of ResourceData (LPBYTE)pConfList1, // buffer ConfListSize1, // size of buffer ulFlags); // currently zero } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_QueryResConfList (first pass) caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept if (Status != CR_SUCCESS) { goto Clean0; // quit for any error } if (pConfList1->ConflictsCounted > pConfList1->ConflictsListed) { // // need more space, multiple conflict // ConfListSize2 = pConfList1->RequiredBufferSize; pConfList2 = (PPLUGPLAY_CONTROL_CONFLICT_LIST)pSetupMalloc(ConfListSize2); if (pConfList2 != NULL) { // // Try to use this instead // // // No special privileges are required by the server // RpcTryExcept { // // call rpc service entry point // Status = PNP_QueryResConfList( hBinding, // rpc binding handle DeviceID, // device id string ResourceID, // resource type (LPBYTE)ResourceData, // actual res des data ResourceLen, // size in bytes of ResourceData (LPBYTE)pConfList2, // buffer ConfListSize2, // size of buffer ulFlags); // currently zero } RpcExcept (I_RpcExceptionFilter(RpcExceptionCode())) { KdPrintEx((DPFLTR_PNPMGR_ID, DBGF_ERRORS, "PNP_QueryResConfList (second pass) caused an exception (%d)\n", RpcExceptionCode())); Status = MapRpcExceptionToCR(RpcExceptionCode()); } RpcEndExcept if (Status != CR_SUCCESS) { // // if we got error second time, but first time success // use what we got on first attempt // (I can't see this happening, but Murphey says it can) // pSetupFree(pConfList2); Status = CR_SUCCESS; } else { // // use second attempt // pSetupFree(pConfList1); pConfList1 = pConfList2; ConfListSize1 = ConfListSize2; } // // either way, we've deleted a buffer // pConfList2 = NULL; } } if(ConfListSize1 > pConfList1->RequiredBufferSize) { // // we can release some of the buffer we requested // ConfListSize2 = pConfList1->RequiredBufferSize; pConfList2 = (PPLUGPLAY_CONTROL_CONFLICT_LIST)pSetupRealloc(pConfList1,ConfListSize2); if(pConfList2) { // // success, we managed to save space // pConfList1 = pConfList2; ConfListSize1 = ConfListSize2; pConfList2 = NULL; } } // // if we get here, we have a successfully valid handle // pConfListHeader->Signature = CM_PRIVATE_CONFLIST_SIGNATURE; pConfListHeader->Machine = hMachine; pConfListHeader->ConflictInfo = pConfList1; *pclConflictList = (ULONG_PTR)pConfListHeader; pConfList1 = NULL; pConfListHeader = NULL; Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } // // cleanup // if (pConfListHeader != NULL) { pSetupFree(pConfListHeader); } if (pConfList1 != NULL) { pSetupFree(pConfList1); } if (pConfList2 != NULL) { pSetupFree(pConfList2); } if (ResourceData32) { pSetupFree(ResourceData32); } return Status; } // CM_Query_Resource_Conflict_List CMAPI CONFIGRET WINAPI CM_Free_Resource_Conflict_Handle( IN CONFLICT_LIST clConflictList ) /*++ Routine Description: Free's a conflict-list handle Arguments: clConflictList - handle of conflict list to free Return Value: CM status value --*/ { CONFIGRET Status = CR_SUCCESS; PCONFLICT_LIST_HEADER pConfList = NULL; try { // // Validate parameters // pConfList = (PCONFLICT_LIST_HEADER)clConflictList; if (!ValidateConfListHandle(pConfList)) { Status = CR_INVALID_CONFLICT_LIST; goto Clean0; } FreeConfListHandle(pConfList); Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Free_Resource_Conflict_Handle CMAPI CONFIGRET WINAPI CM_Get_Resource_Conflict_Count( IN CONFLICT_LIST clConflictList, OUT PULONG pulCount ) /*++ Routine Description: Retrieves number of conflicts from list Arguments: clConflictList - handle of conflict list pulCount - filled with number of conflicts (0 if no conflicts) Return Value: CM status value --*/ { CONFIGRET Status = CR_SUCCESS; PCONFLICT_LIST_HEADER pConfList; try { // // Validate parameters // pConfList = (PCONFLICT_LIST_HEADER)clConflictList; if (!ValidateConfListHandle(pConfList)) { Status = CR_INVALID_CONFLICT_LIST; goto Clean0; } if (pulCount == NULL) { Status = CR_INVALID_POINTER; goto Clean0; } // // return count parameter // that can be used to iterate conflicts // *pulCount = pConfList->ConflictInfo->ConflictsListed; Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Free_Resource_Conflict_Handle CMAPI CONFIGRET WINAPI CM_Get_Resource_Conflict_DetailsW( IN CONFLICT_LIST clConflictList, IN ULONG ulIndex, IN OUT PCONFLICT_DETAILS_W pConflictDetails ) /*++ Routine Description: Retrieves conflict details for a specific conflict Arguments: clConflictList - handle of conflict list ulIndex - index of conflict to query, 0 to count-1 where count is obtained from CM_Get_Resource_Conflict_Count pConflictDetails - structure to be filled with conflict details must have CD_ulSize & CD_ulFlags initialized before calling function eg: pConflictDetails->CD_ulSize = sizeof(CONFLICT_DETAILS) pConflictDetails->CD_ulFlags = CM_CDMASK_ALL Return Value: CM status value --*/ { CONFIGRET Status = CR_SUCCESS; PCONFLICT_LIST_HEADER pConfList; PPLUGPLAY_CONTROL_CONFLICT_ENTRY pConfEntry; PWCHAR pString; ULONG ulFlags; PPLUGPLAY_CONTROL_CONFLICT_STRINGS ConfStrings; PVOID hStringTable = NULL; handle_t hBinding = NULL; HMACHINE hMachine = NULL; DEVINST dnDevInst; ULONG ulSize; try { // // Validate parameters // pConfList = (PCONFLICT_LIST_HEADER)clConflictList; if (!ValidateConfListHandle(pConfList)) { Status = CR_INVALID_CONFLICT_LIST; goto Clean0; } if (pConflictDetails == NULL) { Status = CR_INVALID_POINTER; goto Clean0; } if(pConflictDetails->CD_ulSize != sizeof(CONFLICT_DETAILS_W)) { // // currently only one structure size supported // Status = CR_INVALID_STRUCTURE_SIZE; goto Clean0; } if (INVALID_FLAGS(pConflictDetails->CD_ulMask, CM_CDMASK_VALID)) { // // CM_CDMASK_VALID describes the bits that are supported // Status = CR_INVALID_FLAG; goto Clean0; } if (pConflictDetails->CD_ulMask == 0) { // // must want something // Status = CR_INVALID_FLAG; goto Clean0; } if(ulIndex >= pConfList->ConflictInfo->ConflictsListed) { // // validate index // Status = CR_INVALID_INDEX; goto Clean0; } hMachine = (HMACHINE)(pConfList->Machine); // // setup rpc binding handle and string table handle // if (!PnPGetGlobalHandles(hMachine, &hStringTable, &hBinding)) { Status = CR_FAILURE; goto Clean0; } ConfStrings = (PPLUGPLAY_CONTROL_CONFLICT_STRINGS) &(pConfList->ConflictInfo->ConflictEntry[pConfList->ConflictInfo->ConflictsListed]); pConfEntry = pConfList->ConflictInfo->ConflictEntry + ulIndex; // the string for this entry pString = ConfStrings->DeviceInstanceStrings + pConfEntry->DeviceInstance; // // init requested parameters // ulFlags = pConflictDetails->CD_ulMask; pConflictDetails->CD_ulMask = 0; if (IS_FLAG_SET(ulFlags , CM_CDMASK_DEVINST)) { pConflictDetails->CD_dnDevInst = 0; } if (IS_FLAG_SET(ulFlags , CM_CDMASK_RESDES)) { pConflictDetails->CD_rdResDes = 0; } if (IS_FLAG_SET(ulFlags , CM_CDMASK_FLAGS)) { pConflictDetails->CD_ulFlags = 0; } if (IS_FLAG_SET(ulFlags , CM_CDMASK_DESCRIPTION)) { pConflictDetails->CD_szDescription[0] = 0; } // // fill in requested parameters // if (IS_FLAG_SET(ulFlags , CM_CDMASK_DEVINST)) { if ((pString == NULL) || (pString[0] == L'\0') || (IS_FLAG_SET(pConfEntry->DeviceFlags,PNP_CE_LEGACY_DRIVER))) { // // not a valid dev-instance // dnDevInst = (DEVINST)-1; } else { // // lookup DeviceID // ASSERT(pString && *pString && IsLegalDeviceId(pString)); dnDevInst = (DEVINST)pSetupStringTableAddString(hStringTable, pString, STRTAB_CASE_SENSITIVE); if (dnDevInst == (DEVINST)(-1)) { Status = CR_OUT_OF_MEMORY; // probably out of memory goto Clean0; } } pConflictDetails->CD_dnDevInst = dnDevInst; pConflictDetails->CD_ulMask |= CM_CDMASK_DEVINST; } if (IS_FLAG_SET(ulFlags , CM_CDMASK_RESDES)) { // // not implemented yet // pConflictDetails->CD_rdResDes = 0; } if (IS_FLAG_SET(ulFlags , CM_CDMASK_FLAGS)) { // // convert flags // pConflictDetails->CD_ulFlags = 0; if (IS_FLAG_SET(pConfEntry->DeviceFlags,PNP_CE_LEGACY_DRIVER)) { // // description describes a driver, not a device // pConflictDetails->CD_ulFlags |= CM_CDFLAGS_DRIVER; } if (IS_FLAG_SET(pConfEntry->DeviceFlags,PNP_CE_ROOT_OWNED)) { // // resource is owned by root device // pConflictDetails->CD_ulFlags |= CM_CDFLAGS_ROOT_OWNED; } if ((IS_FLAG_SET(pConfEntry->DeviceFlags,PNP_CE_TRANSLATE_FAILED)) || (IS_FLAG_SET(pConfEntry->DeviceFlags,PNP_CE_ROOT_OWNED))) { // // resource cannot be allocated, but no descriptive text // pConflictDetails->CD_ulFlags |= CM_CDFLAGS_RESERVED; } } if (IS_FLAG_SET(ulFlags , CM_CDMASK_DESCRIPTION)) { if ((pString == NULL) || (pString[0] == L'\0') || (IS_FLAG_SET(pConfEntry->DeviceFlags,PNP_CE_LEGACY_DRIVER))) { // // copy string directly, specifies legacy driver (or nothing for unavailable) // we allow truncation while copying, though this shouldn't happen. // if (FAILED(StringCchCopyEx( pConflictDetails->CD_szDescription, SIZECHARS(pConflictDetails->CD_szDescription), pString, NULL, NULL, STRSAFE_IGNORE_NULLS))) { // // possible error cases are handled by StringCchCopyEx // (i.e. truncation, NULL pString), but we pretend to check // the return here to make PREFAST happy. // NOTHING; } } else { // // copy a descriptive name for P&P device // ASSERT(pString && *pString && IsLegalDeviceId(pString)); dnDevInst = (DEVINST)pSetupStringTableAddString(hStringTable, pString, STRTAB_CASE_SENSITIVE); if (dnDevInst == (DEVINST)(-1)) { Status = CR_OUT_OF_MEMORY; // probably out of memory goto Clean0; } ulSize = sizeof(pConflictDetails->CD_szDescription); if (CM_Get_DevNode_Registry_Property_ExW(dnDevInst, CM_DRP_FRIENDLYNAME, NULL, (LPBYTE)(pConflictDetails->CD_szDescription), &ulSize, 0,hMachine) != CR_SUCCESS) { ulSize = sizeof(pConflictDetails->CD_szDescription); if (CM_Get_DevNode_Registry_Property_ExW(dnDevInst, CM_DRP_DEVICEDESC, NULL, (LPBYTE)(pConflictDetails->CD_szDescription), &ulSize, 0,hMachine) != CR_SUCCESS) { // // unknown // pConflictDetails->CD_szDescription[0] = 0; } } } pConflictDetails->CD_ulMask |= CM_CDMASK_DESCRIPTION; } Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Get_Resource_Conflict_DetailsW BOOL ValidateConfListHandle( PCONFLICT_LIST_HEADER pConfList ) /*++ Routine Description: Validates a conflict-list handle pConfList must not be null, and must contain a valid signature Arguments: pConfList - handle to conflict list Return Value: TRUE if valid, FALSE if not valid --*/ { // // validate parameters // if (pConfList == NULL) { return FALSE; } // // check for the private conflict list signature // if (pConfList->Signature != CM_PRIVATE_CONFLIST_SIGNATURE) { return FALSE; } return TRUE; } // ValidateConfListHandle VOID FreeConfListHandle( PCONFLICT_LIST_HEADER pConfList ) /*++ Routine Description: Releases memory allocated for Conflict List Makes sure Signature is invalid Arguments: pConfList - valid handle to conflict list Return Value: none --*/ { if(pConfList != NULL) { pConfList->Signature = 0; if(pConfList->ConflictInfo) { pSetupFree(pConfList->ConflictInfo); } pSetupFree(pConfList); } return; } // FreeConfListHandle //------------------------------------------------------------------- // ANSI STUBS //------------------------------------------------------------------- CMAPI CONFIGRET WINAPI CM_Get_Resource_Conflict_DetailsA( IN CONFLICT_LIST clConflictList, IN ULONG ulIndex, IN OUT PCONFLICT_DETAILS_A pConflictDetails ) /*++ Routine Description: Ansi version of CM_Get_Resource_Conflict_DetailsW --*/ { CONFLICT_DETAILS_W detailsW; CONFIGRET Status = CR_SUCCESS; ULONG ulAnsiLength; try { // // Validate parameters we need for Ansi part // further validation occurs in Wide-char part // if (pConflictDetails == NULL) { Status = CR_INVALID_POINTER; goto Clean0; } if(pConflictDetails->CD_ulSize != sizeof(CONFLICT_DETAILS_A)) { // // currently only one structure size supported // Status = CR_INVALID_STRUCTURE_SIZE; goto Clean0; } if (INVALID_FLAGS(pConflictDetails->CD_ulMask, CM_CDMASK_VALID)) { // // CM_CDMASK_VALID describes the bits that are supported // Status = CR_INVALID_FLAG; goto Clean0; } if (pConflictDetails->CD_ulMask == 0) { // // must want something // Status = CR_INVALID_FLAG; goto Clean0; } ZeroMemory(&detailsW,sizeof(detailsW)); detailsW.CD_ulSize = sizeof(detailsW); detailsW.CD_ulMask = pConflictDetails->CD_ulMask; Status = CM_Get_Resource_Conflict_DetailsW(clConflictList,ulIndex,&detailsW); if (Status != CR_SUCCESS) { goto Clean0; } // // copy details // pConflictDetails->CD_ulMask = detailsW.CD_ulMask; if (IS_FLAG_SET(detailsW.CD_ulMask , CM_CDMASK_DEVINST)) { pConflictDetails->CD_dnDevInst = detailsW.CD_dnDevInst; } if (IS_FLAG_SET(detailsW.CD_ulMask , CM_CDMASK_RESDES)) { pConflictDetails->CD_rdResDes = detailsW.CD_rdResDes; } if (IS_FLAG_SET(detailsW.CD_ulMask , CM_CDMASK_FLAGS)) { pConflictDetails->CD_ulFlags = detailsW.CD_ulFlags; } if (IS_FLAG_SET(detailsW.CD_ulMask , CM_CDMASK_DESCRIPTION)) { pConflictDetails->CD_szDescription[0] = 0; // // need to convery from UNICODE to ANSI // ulAnsiLength = MAX_PATH; Status = PnPUnicodeToMultiByte(detailsW.CD_szDescription, MAX_PATH*sizeof(WCHAR), pConflictDetails->CD_szDescription, &ulAnsiLength); if (Status != CR_SUCCESS) { // // error occurred // Status = CR_FAILURE; } } Clean0: NOTHING; } except(EXCEPTION_EXECUTE_HANDLER) { Status = CR_FAILURE; } return Status; } // CM_Get_Resource_Conflict_DetailsA