/*++ Copyright (c) 1995-2000 Microsoft Corporation Module Name: devres2.c Abstract: Utility routines for resource matching Author: Jamie Hunter (jamiehun) 9-July-1998 --*/ #include "precomp.h" #pragma hdrstop PRDE_LIST pRDEList_Alloc() /*++ Routine Description: Allocates a list-entry node Arguments: Return Value: PRDE_LIST entry --*/ { PRDE_LIST Node; Node = (PRDE_LIST)MyMalloc(sizeof(RDE_LIST)); if (Node == NULL) { return NULL; } Node->Prev = Node; Node->Next = Node; Node->Entry = NULL; return Node; } VOID pRDEList_AddHead( IN OUT PRDE_LIST pList, IN PRDE_LIST Node ) /*++ Routine Description: Adds a node to head of list Arguments: pList = pointer to list Node = node to add Return Value: none --*/ { MYASSERT(Node); MYASSERT(Node->Prev == Node); MYASSERT(Node->Next == Node); MYASSERT(pList); Node->Prev = pList; Node->Next = pList->Next; Node->Next->Prev = Node; pList->Next = Node; // Head } VOID pRDEList_AddTail( IN OUT PRDE_LIST pList, IN PRDE_LIST Node ) /*++ Routine Description: Adds a node to tail of list Arguments: pList = pointer to list Node = node to add Return Value: none --*/ { MYASSERT(Node); MYASSERT(Node->Prev == Node); MYASSERT(Node->Next == Node); MYASSERT(pList); Node->Next = pList; Node->Prev = pList->Prev; Node->Prev->Next = Node; pList->Prev = Node; // Tail } VOID pRDEList_Remove( IN PRDE_LIST Node ) /*++ Routine Description: Removes a node from list that node is member of Arguments: Node = node to remove Return Value: none --*/ { MYASSERT(Node); if (Node->Prev == Node && Node->Next == Node) { // // already removed // return; } Node->Prev->Next = Node->Next; Node->Next->Prev = Node->Prev; Node->Next = Node; Node->Prev = Node; } PRDE_LIST pRDEList_Find( IN PRDE_LIST pList, IN PRESDES_ENTRY pItem ) /*++ Routine Description: Looks for pItem in pList Arguments: pList = list to search pItem = item to search for Return Value: Node entry, or NULL --*/ { PRDE_LIST Node; MYASSERT(pList); MYASSERT(pItem); Node = pList->Next; // head while (Node != pList) { if (Node->Entry == pItem) { return Node; } Node = Node->Next; } return NULL; } VOID pRDEList_Destroy( IN PRDE_LIST pList ) /*++ Routine Description: Destroy pList and everything on pList Arguments: pList = list to destroy Return Value: Node entry, or NULL --*/ { PRDE_LIST Node,Next; if (pList == NULL) { return; } Node = pList; // head do { Next = Node->Next; MyFree(Node); // this will free pList first, and then everything else on same list Node = Next; } while (Node != pList); } BOOL pGetMatchingRange( IN ULONG64 ulKnownValue, IN ULONG64 ulKnownLen, IN LPBYTE pData, IN RESOURCEID ResType, OUT PULONG pRange, OUT PBOOL pExact, OUT PULONG pFlags ) /*++ Routine Description: Finds range index for resource inside ResDes Arguments: ulKnownValue - base address ulKnownLen - length of resources pData/ResType - resource data we're comparing with pRange - output range index pExact - output true if there is only one range pFlags - output flags from matching range Return Value: BOOL if match --*/ { PGENERIC_RESOURCE pGenRes = NULL; ULONG64 ulValue = 0, ulLen = 0, ulEnd = 0; ULONG ulFlags = 0, i; pGenRes = (PGENERIC_RESOURCE)pData; for (i = 0; i < pGenRes->GENERIC_Header.GENERIC_Count; i++) { pGetRangeValues(pData, ResType, i, &ulValue, &ulLen, &ulEnd, NULL, &ulFlags); if (ulLen != ulKnownLen) { continue; } if ((ulKnownValue >= ulValue) && ((ulKnownValue + ulLen - 1) <= ulEnd)) { if (pRange != NULL) { *pRange = i; } // // consider exact case // if (pExact != NULL) { if (pGenRes->GENERIC_Header.GENERIC_Count==1 && ulValue == ulKnownValue && (ulKnownValue + ulLen - 1) == ulEnd) { *pExact = TRUE; } } if (pFlags != NULL) { // // want nearest flags // *pFlags = ulFlags; } return TRUE; } } if (pRange != NULL) { *pRange = 0; } return FALSE; } ULONG pTryMatch( IN OUT PRESDES_ENTRY pKnown, IN OUT PRDE_LIST pResList, IN OUT PULONG pDepth ) /*++ Routine Description: Looks for best match of remaining requirements in remaining available resources returns number of matched requirements Arguments: pKnown - requirements list (what is remaining) pResList - list of available resources Return Value: Node entry, or NULL --*/ { ULONG ThisBest = 0; ULONG MyBest = 0; PRDE_LIST pIterator = NULL; PRESDES_ENTRY pRange; ULONG64 ulValue, ulLen, ulEnd; ULONG ulFlags; BOOL Exact = FALSE; ULONG Best = 0; PRDE_LIST pBestRes = NULL; BOOL BadMatch = FALSE; BOOL Prune = FALSE; BOOL NoRemaining = TRUE; MYASSERT(pDepth); *pDepth = 0; if (pKnown == NULL) { // // end recursion // return 0; } pKnown->CrossLink = NULL; // // we're looking for a match in pResList for pKnown // case (1) get a "Best" for if we decide not to match // case (2) get a "Best" for exact match, and chose between (1) and (2) if one exists // case (3) get a "Best" for each possible range match and choose best between (1) and all (3) // // // consider case(1) - what the results would be if we wasn't able to fit anything in // //Best = pTryMatch(pKnown->Next,pResList,pDepth); //pBestRes = NULL; pGetHdrValues(pKnown->ResDesData, pKnown->ResDesType, &ulValue, &ulLen, &ulEnd, &ulFlags); // // consider case(2) and (3) together // for(pIterator = pResList->Next;pIterator!=pResList;pIterator = pIterator->Next) { // // iterate through remaining resources // pRange = pIterator->Entry; if (pRange == NULL) { // // this has been used // continue; } if (pRange->ResDesType != pKnown->ResDesType) { // // not the kind of resource i'm looking for // continue; } NoRemaining = FALSE; if(pGetMatchingRange(ulValue, ulLen,pRange->ResDesData, pRange->ResDesType,NULL,&Exact,NULL)) { pIterator->Entry = NULL; // eliminate this range ThisBest = pTryMatch(pKnown->Next,pResList,pDepth); // match the rest, with us using this resource-range pIterator->Entry = pRange; if ((ThisBest > Best) || (pBestRes == NULL)) { // // the current best match (or first match if pBestRes == NULL) // pKnown->CrossLink = pRange; pBestRes = pIterator; Best = ThisBest; MyBest = 1; BadMatch = FALSE; } else { // // need to re-do best match // BadMatch = TRUE; } if (Exact || (*pDepth == ThisBest)) { // // prune - we're either exact, or got a perfect match // Prune = TRUE; goto Final; } } } if (NoRemaining) { // // I have no resources remaining I can use - consider this as good as a match // but we need to continue up the tree // Best = pTryMatch(pKnown->Next,pResList,pDepth); // match the rest, with us using this resource-range MyBest = TRUE; BadMatch = FALSE; goto Final; } // // if we get here we've: // (1) found a flexable match, but wasn't able to match everyone above us, or // (2) not found any match // note that if last best was n with us matching and this best is n+1 without // then we don't lose the best // consider if overall we'd do any better if we gave up our resources to someone else // if((pBestRes == NULL) || ((Best+MyBest) < *pDepth)) { // // if we had a match, only worth checking if we could increase best by more than MyBest // note that *pDepth is only valid if pBestRes != NULL // ThisBest = pTryMatch(pKnown->Next,pResList,pDepth); if ((ThisBest > (Best+MyBest)) || (pBestRes == NULL)) { // // the current best match // pKnown->CrossLink = NULL; pBestRes = NULL; Best = ThisBest; MyBest = 0; BadMatch = FALSE; } else { // // need to re-do best match // BadMatch = TRUE; } } Final: if (BadMatch) { // // We had a bad-match since our last good match // if (pBestRes) { pRange = pBestRes->Entry; // the range we determined was our best bet pBestRes->Entry = NULL; // eliminate this range Best = pTryMatch(pKnown->Next,pResList,pDepth); // match the rest, with us using this resource-range pBestRes->Entry = pRange; pKnown->CrossLink = pRange; MyBest = 1; } else { Best = pTryMatch(pKnown->Next,pResList,pDepth); // match the rest, with us not using this resource range pKnown->CrossLink = NULL; MyBest = 0; } } // // if we found a match, we've saved it in pKnown->CrossLink // return Best+0 if it's better we don't fit our resource in, Best+1 otherwise // returns *pDepth = Best+1 if everyone (me up) fit their resources in // (*pDepth)++; // increment to include me return Best+MyBest; // MyBest = 1 if I managed to match my resource (or no resources left), Best = everyone to the right of me } ULONG pMergeResDesDataLists( IN OUT PRESDES_ENTRY pKnown, IN OUT PRESDES_ENTRY pTest, OUT PULONG pMatchCount ) /*++ Routine Description: Map entries in pKnown into pTest as best as possible Arguments: pKnown - list of known values pTest - list of ranges pMatchCount - set to be number of resources matched Return Value: NO_LC_MATCH if no correlation (not a single known matches) LC_MATCH_SUPERSET if at least one known matches, but some known's don't LC_MATCH_SUBSET if all known matches, but there are some range-items unmatched LC_MATCH if all known matches and all range-items match ORDERED_LC_MATCH if match, and match is in order pKnown->CrossLink entries point to matching entries in pTest pTest->CrossLink entries point to matching entries in pKnown --*/ { PRDE_LIST pResList = NULL; PRDE_LIST Node; PRESDES_ENTRY pKnownEntry; PRESDES_ENTRY pTestEntry; ULONG Success = NO_LC_MATCH; ULONG Depth = 0; BOOL SomeKnownMatched = FALSE; BOOL SomeKnownUnMatched = FALSE; BOOL SomeTestMatched = FALSE; BOOL SomeTestUnMatched = FALSE; BOOL Ordered = TRUE; ULONG MatchCount = 0; if (pKnown == NULL) { goto Final; } // // reset // for(pKnownEntry = pKnown; pKnownEntry != NULL ;pKnownEntry = pKnownEntry->Next) { pKnownEntry->CrossLink = NULL; } for(pTestEntry = pTest; pTestEntry != NULL ;pTestEntry = pTestEntry->Next) { pTestEntry->CrossLink = NULL; } pResList = pRDEList_Alloc(); if (pResList == NULL) { goto Final; } // // make all resources available // this gives us a work list without destroying original list // for(pTestEntry = pTest; pTestEntry != NULL ;pTestEntry = pTestEntry->Next) { Node = pRDEList_Alloc(); if (Node == NULL) { goto Final; } Node->Entry = pTestEntry; pRDEList_AddTail(pResList,Node); } MatchCount = pTryMatch(pKnown,pResList,&Depth); if (MatchCount ==0) { // // no match // goto Final; } // // pKnown now has it's Cross-Link's set to determine success of this match // // consider NO_LC_MATCH, LC_MATCH_SUPERSET and ORDERED_LC_MATCH cases // pKnownEntry = pKnown; pTestEntry = pTest; while (pKnownEntry) { if (pKnownEntry->CrossLink == NULL) { SomeKnownUnMatched = TRUE; } else { SomeKnownMatched = TRUE; // we have at least one matched pKnownEntry->CrossLink->CrossLink = pKnownEntry; // cross-link test entries if (pKnownEntry->CrossLink != pTestEntry) { Ordered = FALSE; // ordered compare lost } else { pTestEntry = pTestEntry->Next; // goto next test for ordered } } pKnownEntry = pKnownEntry->Next; } if (Ordered && pTestEntry != NULL) { Ordered = FALSE; } if (SomeKnownUnMatched) { if (SomeKnownMatched) { Success = LC_MATCH_SUPERSET; } goto Final; } if (Ordered) { Success = ORDERED_LC_MATCH; goto Final; } // // consider between LC_MATCH_SUBSET and LC_MATCH // pTestEntry = pTest; while (pTestEntry) { if (pTestEntry->CrossLink == NULL) { // // the first NULL CrossLink entry makes Known a Subset of Test Success = LC_MATCH_SUBSET; goto Final; } pTestEntry = pTestEntry->Next; } // // if we get here, there is an exact match // Success = LC_MATCH; Final: pRDEList_Destroy(pResList); if (pMatchCount != NULL) { *pMatchCount = MatchCount; } return Success; } ULONG pCompareLogConf( IN LOG_CONF KnownLogConf, IN LOG_CONF TestLogConf, IN HMACHINE hMachine, OUT PULONG pMatchCount ) /*++ Routine Description: This routine compares two log confs and returns info about how well they match. This simply uses the pMergeResDesDataLists function to get match status Arguments: KnownLogConf = First log conf to compare (fixed values) TestConfType = Second log conf to compare (range values) hMachine = Machine to compare on pMatchCount = number of resources matched Return Value: As pMergeResDesDataLists --*/ { PRESDES_ENTRY pKnownResList = NULL, pTestResList = NULL; ULONG Status; // // Retrieve the resources for each log conf // if (!pGetResDesDataList(KnownLogConf, &pKnownResList, TRUE,hMachine)) { if (pMatchCount) { *pMatchCount = 0; } return NO_LC_MATCH; } if (!pGetResDesDataList(TestLogConf, &pTestResList, TRUE,hMachine)) { pDeleteResDesDataList(pKnownResList); if (pMatchCount) { *pMatchCount = 0; } return NO_LC_MATCH; } Status = pMergeResDesDataLists(pKnownResList,pTestResList,pMatchCount); pDeleteResDesDataList(pKnownResList); pDeleteResDesDataList(pTestResList); return Status; } BOOL pFindMatchingAllocConfig( IN LPDMPROP_DATA lpdmpd ) { HWND hDlg = lpdmpd->hDlg; ULONG ulBasicLC = 0; ULONG ulBasicCount = 0; BOOL bFoundCorrectLC = FALSE; LOG_CONF LogConf; ULONG lastMatchStatus = NO_LC_MATCH, bestMatchStatus = NO_LC_MATCH; UINT lastMatchCount; UINT bestMatchCount = 0; HMACHINE hMachine; hMachine = pGetMachine(lpdmpd); lpdmpd->MatchingLC = (LOG_CONF)0; lpdmpd->MatchingLCType = BASIC_LOG_CONF; // // Load the values associated with the allocated config in the list box, // but associate each with the resource requirements descriptor that it // originated from. To do this, we have to match the allocated config // with the basic/filtered config it is based on. // // NOTE: if we got here, then we know that an known config of some kind // exists (passed in as param) and that at least one basic/filtered config // exists. Further more, we know that the combobox has already been // filled in with a list of any basic/filtered configs and the lc handle // associated with them. // ulBasicCount = (ULONG)SendDlgItemMessage(hDlg,IDC_DEVRES_LOGCONFIGLIST,CB_GETCOUNT, (WPARAM)0,(LPARAM)0); if (ulBasicCount == (ULONG)LB_ERR) { return FALSE; } for (ulBasicLC = 0 ; ulBasicLC < ulBasicCount; ulBasicLC++) { // // Retrieve the log conf handle // LogConf = (LOG_CONF)SendDlgItemMessage(hDlg, IDC_DEVRES_LOGCONFIGLIST, CB_GETITEMDATA, ulBasicLC, 0L); if (LogConf != 0) { // // Determine how good a match this requirements list is. // lastMatchStatus = pCompareLogConf(lpdmpd->CurrentLC, LogConf,hMachine,&lastMatchCount); if ((lastMatchStatus > bestMatchStatus) || ((bestMatchStatus == lastMatchStatus) && lastMatchCount > bestMatchCount)) { bestMatchCount = lastMatchCount; bestMatchStatus =lastMatchStatus; lpdmpd->MatchingLC = LogConf; } } } if (bestMatchStatus == NO_LC_MATCH || bestMatchStatus == LC_MATCH_SUBSET) { // // this doesn't follow any valid config // return FALSE; } lpdmpd->dwFlags &= ~(DMPROP_FLAG_PARTIAL_MATCH|DMPROP_FLAG_MATCH_OUT_OF_ORDER); if (bestMatchStatus != ORDERED_LC_MATCH) { // // If match status isn't ORDERED_LC_MATCH, then ordering of the resource descriptors // didn't match up. Set a flag to indicate this, so that later on we'll know to handle // this specially. // lpdmpd->dwFlags |= DMPROP_FLAG_MATCH_OUT_OF_ORDER; } else if (bestMatchStatus < LC_MATCH) { // // match is partial // lpdmpd->dwFlags |= DMPROP_FLAG_PARTIAL_MATCH; } return TRUE; } // LoadMatchingAllocConfig BOOL pGetMatchingResDes( IN ULONG64 ulKnownValue, IN ULONG64 ulKnownLen, IN ULONG64 ulKnownEnd, IN RESOURCEID ResType, IN LOG_CONF MatchingLogConf, OUT PRES_DES pMatchingResDes, IN HMACHINE hMachine ) /*++ Routine Description: This returns a res des that matches the specified values. used by pSaveCustomResSettings Arguments: ulKnownValue Starting resource value to match against ulKnownLen Length of resource to match against ResType Type of resource to match against MatchnigLogConf Log conf to retreive potential matching res des from pMatchingResDes Supplies a pointer that on return contains a matching res des if any. Return Value: None. --*/ { CONFIGRET Status = CR_SUCCESS; RESOURCEID Res; RES_DES ResDes, ResDesTemp; ULONG64 ulValue = 0, ulLen = 0, ulEnd = 0; ULONG ulSize, ulFlags = 0, i; PGENERIC_RESOURCE pGenRes; BOOL bMatch = FALSE; LPBYTE pData = NULL; // // The MatchingLogConf is a requirements list. Loop through each res des // in the matching log conf until we find a res des that matches the // known res des values. // Status = CM_Get_Next_Res_Des_Ex(&ResDes, MatchingLogConf, ResType, &Res, 0,hMachine); while (Status == CR_SUCCESS) { // // Get res des data // if (CM_Get_Res_Des_Data_Size_Ex(&ulSize, ResDes, CM_RESDES_WIDTH_64,hMachine) != CR_SUCCESS) { CM_Free_Res_Des_Handle(ResDes); break; } pData = MyMalloc(ulSize); if (pData == NULL) { CM_Free_Res_Des_Handle(ResDes); break; } if (CM_Get_Res_Des_Data_Ex(ResDes, pData, ulSize, DEVRES_WIDTH_FLAGS,hMachine) != CR_SUCCESS) { CM_Free_Res_Des_Handle(ResDes); MyFree(pData); break; } if(pGetMatchingRange(ulKnownValue,ulKnownLen,pData,ResType,NULL,NULL,NULL)) { *pMatchingResDes = ResDes; bMatch = TRUE; MyFree(pData); goto MatchFound; } // // Get next res des in log conf // ResDesTemp = ResDes; Status = CM_Get_Next_Res_Des_Ex(&ResDes, ResDesTemp, ResType, &Res, 0,hMachine); CM_Free_Res_Des_Handle(ResDesTemp); MyFree(pData); } MatchFound: return bMatch; } // GetMatchingResDes // // NTRAID#166214-2000/08/19-JamieHun Conflict Supression Hack // this stuff needs to be fixed proper // PTSTR pGetRegString( IN HKEY hKey, IN PCTSTR regval ) /*++ Routine Description: Obtain and return a registry string allocated by MyMalloc return NULL if we couldn't retrieve string Arguments: hKey - key to retrieve string from regval - value to retrieve Return Value: copy of registry string, may be free'd with MyFree --*/ { DWORD dwSize; DWORD dwType; PTSTR pSz; LONG res; dwType = 0; dwSize = 0; res = RegQueryValueEx(hKey,regval,NULL,&dwType,(PBYTE)NULL,&dwSize); if (res != ERROR_SUCCESS) { return NULL; } if (dwType != REG_SZ) { return NULL; } pSz = MyMalloc(dwSize); if (pSz == NULL) { return NULL; } res = RegQueryValueEx(hKey,regval,NULL,&dwType,(PBYTE)pSz,&dwSize); if (res != ERROR_SUCCESS) { MyFree(pSz); return NULL; } return pSz; } VOID pFillCETags( IN PCONFLICT_EXCEPTIONS pExceptions, IN PCE_TAGS pTags, PTSTR pSz ) /*++ Routine Description: parse a list of tags into CE_TAGS structure adding the strings into the string table en-route note that this structure will be flexable and allow ',' or ';' seperator however when used in Exceptions string, we've already eliminated any ';' format is: ,, or ;; Arguments: pExceptions - context information pTags - tag structure to fill in Return Value: none --*/ { static CE_TAGS DummyEntry = { -1 }; // if we write a new string, negative size count means this isn't a devnode entry MYASSERT(pTags->nTags == 0); while(pSz[0] && pTags->nTags < MAX_CE_TAGS) { if(pSz[0]==TEXT(',')||pSz[0]==TEXT(';')||pSz[0]<=TEXT(' ')) { pSz++; } else { PTSTR pOldSz = pSz; PTSTR pLastSpace = NULL; LONG id; while (pSz[0] && pSz[0]!=TEXT(';')&& pSz[0]!=TEXT(',')) { if (pSz[0]<=TEXT(' ')) { if (pLastSpace==NULL) { pLastSpace = pSz; } } else { pLastSpace = NULL; } pSz++; } // // pSz points to '\0', ';' or ',' // pLastSpace points to any trailing WS // pOldSz points to start of string // if(pLastSpace==NULL) { pLastSpace = pSz; } if (pSz[0]) { pSz++; } pLastSpace[0]=TEXT('\0'); // // pSz points to next string, pOldSz points to this string // add string to string table, place in list of tags // id = pSetupStringTableAddStringEx(pExceptions->ceTagMap,pOldSz,STRTAB_CASE_INSENSITIVE|STRTAB_BUFFER_WRITEABLE,&DummyEntry,sizeof(DummyEntry)); if (id>=0) { pTags->Tag[pTags->nTags++] = id; } } } } PCE_ENTRY pScanConflictExceptionEntry( IN PCONFLICT_EXCEPTIONS pExceptions, PTSTR pSz ) /*++ Routine Description: obtains conflict exception info from string format is: (1) - always ignore tag for any type of conflict (2) : - ignore tag for resource type (3) @x: - IRQ/DMA - specfic (4) @x-y: - IO/MEM - range are a comma-sep list of tags ,, Arguments: pExceptions - context information pSz - string to parse Return Value: CE_ENTRY structure if this is a valid descriptor --*/ { PTSTR brk; PCE_ENTRY pEntry; TCHAR rt[5]; int c; while (pSz[0] && pSz[0]<=TEXT(' ')) { pSz++; } if (!pSz[0]) { return NULL; } pEntry = MyMalloc(sizeof(CE_ENTRY)); if (pEntry == NULL) { return NULL; } ZeroMemory(pEntry,sizeof(CE_ENTRY)); brk = _tcschr(pSz,TEXT(':')); if(!brk) { // // treat as tags only // pEntry->resType = ResType_None; } else { // // determine resource type // for(c=0;_istalpha(pSz[0]) && c<(sizeof(rt)/sizeof(TCHAR)-1);c++,pSz++) { rt[c] = (TCHAR)_totupper(pSz[0]); } rt[c] = 0; while (pSz[0] && pSz[0]<=TEXT(' ')) { pSz++; } if (pSz[0]!=TEXT(':') && pSz[0]!=TEXT('@')) { MyFree(pEntry); return NULL; } else if (_tcscmp(rt,CE_RES_IO)==0) { pEntry->resType = ResType_IO; } else if (_tcscmp(rt,CE_RES_MEM)==0) { pEntry->resType = ResType_Mem; } else if (_tcscmp(rt,CE_RES_IRQ)==0) { pEntry->resType = ResType_IRQ; } else if (_tcscmp(rt,CE_RES_DMA)==0) { pEntry->resType = ResType_DMA; } else { MyFree(pEntry); return NULL; } if (pSz[0]!=TEXT('@')) { // // no range follows // pEntry->resStart = (ULONG64)0; pEntry->resEnd = (ULONG64)(-1); } else { // // @x[-y]: // ULONG x; ULONG y; PTSTR i; pSz++; // past @ while (pSz[0] && pSz[0]<=TEXT(' ')) { pSz++; } i = pSz; x = _tcstoul(pSz,&i,0); if (i==pSz) { MyFree(pEntry); return NULL; } pSz = i; while (pSz[0] && pSz[0]<=TEXT(' ')) { pSz++; } if (pSz[0]==TEXT('-')) { // // -y // pSz++; while (pSz[0] && pSz[0]<=TEXT(' ')) { pSz++; } i = pSz; y = _tcstoul(pSz,&i,0); if (i==pSz || yresStart = x; pEntry->resEnd = y; } if (pSz[0] != TEXT(':')) { MyFree(pEntry); return NULL; } pSz ++; // skip past colon } // // at this point, expect a list of tags // each tag terminated by a comma // pFillCETags(pExceptions,&pEntry->tags,pSz); if (!pEntry->tags.nTags) { MyFree(pEntry); return NULL; } return pEntry; } PCONFLICT_EXCEPTIONS pLoadConflictExceptions( IN LPDMPROP_DATA lpdmpd ) /*++ Routine Description: Load the string "ResourcePickerExceptions" if any create a context structure for eliminating false conflicts this is one huge hack. Arguments: lpdmpd - Context data Return Value: CONFLICT_EXCEPTIONS structure, if "active" contains a string table and a list of resource exceptions --*/ { PCONFLICT_EXCEPTIONS pExceptions; PCE_ENTRY pEntry; BOOL bStatus; HKEY hDevRegKey; PTSTR pSz; PTSTR pScanSz; PTSTR pOldSz; PCE_TAGS pTags; // // we always create the structure, so we will avoid looking for registry info every time // pExceptions = MyMalloc(sizeof(CONFLICT_EXCEPTIONS)); if (pExceptions == NULL) { return NULL; } ZeroMemory(pExceptions,sizeof(CONFLICT_EXCEPTIONS)); hDevRegKey = SetupDiOpenDevRegKey(lpdmpd->hDevInfo,lpdmpd->lpdi,DICS_FLAG_GLOBAL,0,DIREG_DRV,KEY_READ); if (hDevRegKey == INVALID_HANDLE_VALUE) { // // can't find key, no conflict elimination // return pExceptions; } pSz = pGetRegString(hDevRegKey,REGSTR_VAL_RESOURCE_PICKER_EXCEPTIONS); RegCloseKey(hDevRegKey); if(pSz == NULL) { // // can't find key, no conflict elimination // return pExceptions; } // // now parse the string creating our context to eliminate false conflicts // pExceptions->ceTagMap = pSetupStringTableInitializeEx(sizeof(CE_TAGS),0); if (pExceptions->ceTagMap == NULL) { MyFree(pSz); return pExceptions; } pScanSz = pSz; while (pScanSz[0]) { if (pScanSz[0] == TEXT(';')) { pScanSz ++; } else { pOldSz = pScanSz; while (pScanSz[0] && pScanSz[0] != TEXT(';')) { pScanSz++; } if (pScanSz[0]) { pScanSz[0] = 0; pScanSz++; } pEntry = pScanConflictExceptionEntry(pExceptions,pOldSz); if (pEntry) { pEntry->Next = pExceptions->exceptions; pExceptions->exceptions = pEntry; } } } MyFree(pSz); return pExceptions; } VOID pFreeConflictExceptions( IN PCONFLICT_EXCEPTIONS pExceptions ) /*++ Routine Description: Releases memory used by PCONFLICT_EXCEPTIONS Arguments: pExceptions structure to release Return Value: None. --*/ { // // free the list // while (pExceptions->exceptions) { PCE_ENTRY pEntry = pExceptions->exceptions; pExceptions->exceptions = pEntry->Next; MyFree(pEntry); } // // free the string table // if (pExceptions->ceTagMap) { pSetupStringTableDestroy(pExceptions->ceTagMap); } MyFree(pExceptions); } BOOL pIsConflictException( IN LPDMPROP_DATA lpdmpd, IN PCONFLICT_EXCEPTIONS pExceptions, IN DEVINST devConflict, IN PCTSTR resDesc, IN RESOURCEID resType, IN ULONG64 resValue, IN ULONG64 resLength ) /*++ Routine Description: Load the string "ResourcePickerExceptions" if any create a context structure for eliminating false conflicts this is one huge hack. Arguments: lpdmpd - Context data pExceptions - Cache of information devConflict - DEVINST that's shown to be conflicting with us, -1 if "unavailable" (tag = *) resType - type of resource that we tested resValue - value of resource that we tested resLength - length of resource that we tested Return Value: TRUE if this is an exception --*/ { HMACHINE hMachine; TCHAR DevNodeName[MAX_DEVNODE_ID_LEN]; CE_TAGS tags; PCE_ENTRY pEntry; LONG tagent; LONG n,m; ULONG64 resEnd = resValue+(resLength-1); PTSTR pSz; HKEY hKey; // // if we're not doing any exceptions, get out ASAP // if (pExceptions->exceptions == NULL) { return FALSE; } hMachine = pGetMachine(lpdmpd); // // handle "reserved" case first // if (devConflict != -1) { // // obtain device instance string // if(CM_Get_Device_ID_Ex(devConflict,DevNodeName,MAX_DEVNODE_ID_LEN,0,hMachine)!=CR_SUCCESS) { devConflict = -1; } } if (devConflict == -1) { if (resDesc && resDesc[0]) { lstrcpy(DevNodeName,resDesc); } else { lstrcpy(DevNodeName,CE_TAG_RESERVED); } } else { } // // is this a brand-new devnodename ? // tags.nTags = 0; tagent = pSetupStringTableLookUpStringEx(pExceptions->ceTagMap,DevNodeName,STRTAB_CASE_INSENSITIVE|STRTAB_BUFFER_WRITEABLE,&tags,sizeof(tags)); if(tagent<0 || tags.nTags<0) { // // this particular devnode hasn't been processed before, ouch time // ZeroMemory(&tags,sizeof(tags)); // default reserved case if (devConflict != -1) { // // we need to get regkey for this devnode // I could do this via setupapi, or cfgmgr // for efficiency, I'm going latter route // if(CM_Open_DevNode_Key_Ex(devConflict, KEY_READ, 0, RegDisposition_OpenExisting, &hKey, CM_REGISTRY_SOFTWARE, hMachine) == CR_SUCCESS) { pSz = pGetRegString(hKey,REGSTR_VAL_RESOURCE_PICKER_TAGS); RegCloseKey(hKey); if (pSz) { // // now fill in tags // pFillCETags(pExceptions,&tags,pSz); MyFree(pSz); } } } // // now write this back into the string table // this time, non-negative nTags indicates we've processed this once // we will re-write the extra-data // tagent = pSetupStringTableAddStringEx(pExceptions->ceTagMap,DevNodeName,STRTAB_CASE_INSENSITIVE|STRTAB_BUFFER_WRITEABLE|STRTAB_NEW_EXTRADATA,&tags,sizeof(tags)); } if (tagent<0) { // // if this happens, we have other problems (out of memory) // return FALSE; } // // go through our list of exceptions // an exception that mentions tagent, or any of tags associated with tagent (in tags) is rejected // the policy in this routine is make this an exception if we can // for(pEntry=pExceptions->exceptions;pEntry;pEntry=pEntry->Next) { if (pEntry->resType != ResType_None) { // // we need to validate the resource // if (pEntry->resType != resType || pEntry->resStart > resValue || pEntry->resEnd < resEnd) { continue; } } for (n=0;ntags.nTags;n++) { if (pEntry->tags.Tag[n] == tagent) { MYTRACE((DPFLTR_ERROR_LEVEL, TEXT("Setup: Eliminated false conflict with %s type=%u, start=0x%08x, len=0x%08x\n"),DevNodeName,resType,resValue,resLength)); return TRUE; // hit (devnode itself, where devnode may also be "*") } for (m=0;mtags.Tag[n] == tags.Tag[m]) { MYTRACE((DPFLTR_ERROR_LEVEL, TEXT("Setup: Eliminated false conflict with %s (via tag %s) type=%u, start=0x%08x, len=0x%08x\n"),DevNodeName,pStringTableStringFromId(pExceptions->ceTagMap,tags.Tag[m]),resType,resValue,resLength)); return TRUE; // hit on one of associated tags } } } } return FALSE; }