/*++ Copyright (c) 1997-2000 Microsoft Corporation Module Name: enum.c Abstract: This module provides the functions related to device enumeration. Author: Andy Thornton (andrewth) 20-Oct-97 Revision History: --*/ #include "mfp.h" #pragma hdrstop #include #include #include NTSTATUS MfBuildChildRequirements( IN PMF_CHILD_EXTENSION Child, OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList ); NTSTATUS MfBuildDeviceID( IN PMF_PARENT_EXTENSION Parent, OUT PWSTR *DeviceID ); NTSTATUS MfBuildInstanceID( IN PMF_CHILD_EXTENSION Child, OUT PWSTR *InstanceID ); NTSTATUS MfBuildResourceMap( IN PUCHAR Data, IN ULONG Length, OUT PMF_RESOURCE_MAP *ResourceMap ); NTSTATUS MfBuildVaryingResourceMap( IN PMF_REGISTRY_VARYING_RESOURCE_MAP RegistryMap, IN ULONG Length, OUT PMF_VARYING_RESOURCE_MAP *ResourceMap ); NTSTATUS MfEnumRegistryChild( IN HANDLE ParentHandle, IN ULONG Index, IN OUT PMF_DEVICE_INFO Info ); NTSTATUS MfEnumerate( IN PMF_PARENT_EXTENSION Parent ); NTSTATUS MfEnumerateFromInterface( IN PMF_PARENT_EXTENSION Parent ); NTSTATUS MfEnumerateFromRegistry( IN PMF_PARENT_EXTENSION Parent ); BOOLEAN MfIsChildEnumeratedAlready( PMF_PARENT_EXTENSION Parent, PUNICODE_STRING childName ); BOOLEAN MfIsResourceShared( IN PMF_PARENT_EXTENSION Parent, IN UCHAR Index, IN ULONG Offset, IN ULONG Size ); NTSTATUS MfParentResourceToChildRequirement( IN PMF_PARENT_EXTENSION Parent, IN PMF_CHILD_EXTENSION Child, IN UCHAR Index, IN ULONG Offset OPTIONAL, IN ULONG Size OPTIONAL, OUT PIO_RESOURCE_DESCRIPTOR Requirement ); NTSTATUS MfValidateDeviceInfo( IN PMF_PARENT_EXTENSION Parent, IN PMF_DEVICE_INFO DeviceInfo ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, MfBuildChildRequirements) #pragma alloc_text(PAGE, MfBuildDeviceID) #pragma alloc_text(PAGE, MfBuildInstanceID) #pragma alloc_text(PAGE, MfBuildResourceMap) #pragma alloc_text(PAGE, MfBuildVaryingResourceMap) #pragma alloc_text(PAGE, MfEnumRegistryChild) #pragma alloc_text(PAGE, MfEnumerate) #pragma alloc_text(PAGE, MfEnumerateFromInterface) #pragma alloc_text(PAGE, MfEnumerateFromRegistry) #pragma alloc_text(PAGE, MfIsResourceShared) #pragma alloc_text(PAGE, MfParentResourceToChildRequirement) #endif NTSTATUS MfBuildResourceMap( IN PUCHAR Data, IN ULONG Length, OUT PMF_RESOURCE_MAP *ResourceMap ) /*++ Routine Description: Constructs an MF_RESOURCE_MAP from information returned from the registry Arguments: Data - The raw REG_BINARY data from the registry Length - Length in bytes of Data ResourceMap - On success a pointer to the resource map. Memory should be freed using ExFreePool when no longer required Return Value: Status code indicating the success or otherwise of the operation. --*/ { PMF_RESOURCE_MAP resourceMap; // // Allocate the resource map structure, add space for a count // resourceMap = ExAllocatePoolWithTag(PagedPool, sizeof(MF_RESOURCE_MAP) + Length - 1, MF_RESOURCE_MAP_TAG ); if (!resourceMap) { return STATUS_INSUFFICIENT_RESOURCES; } // // Fill it in // resourceMap->Count = Length; RtlCopyMemory(&resourceMap->Resources, Data, Length); // // Hand it back to the caller // *ResourceMap = resourceMap; return STATUS_SUCCESS; } NTSTATUS MfBuildVaryingResourceMap( IN PMF_REGISTRY_VARYING_RESOURCE_MAP RegistryMap, IN ULONG Length, OUT PMF_VARYING_RESOURCE_MAP *ResourceMap ) /*++ Routine Description: Constructs an MF_VARYING_RESOURCE_MAP from information returned from the registry Arguments: RegistryMap - The raw REG_BINARY data from the registry Length - Length in bytes of RegistryMap ResourceMap - On success a pointer to the resource map. Memory should be freed using ExFreePool when no longer required Return Value: Status code indicating the success or otherwise of the operation. --*/ { PMF_VARYING_RESOURCE_MAP resourceMap; PMF_VARYING_RESOURCE_ENTRY current; PMF_REGISTRY_VARYING_RESOURCE_MAP currentRegistry; ULONG count; if (Length % sizeof(MF_REGISTRY_VARYING_RESOURCE_MAP) != 0) { return STATUS_INVALID_PARAMETER; } count = Length / sizeof(MF_REGISTRY_VARYING_RESOURCE_MAP); // // Allocate the resource map structure // resourceMap = ExAllocatePoolWithTag(PagedPool, sizeof(MF_VARYING_RESOURCE_MAP) + (count-1) * sizeof(MF_VARYING_RESOURCE_ENTRY), MF_VARYING_MAP_TAG ); if (!resourceMap) { return STATUS_INSUFFICIENT_RESOURCES; } // // Fill it in // resourceMap->Count = count; // // Translate the registry data into an aligned internal format // current = resourceMap->Resources; currentRegistry = RegistryMap; while (count--) { current->ResourceIndex = currentRegistry->ResourceIndex; current->Offset = currentRegistry->Offset; current->Size = currentRegistry->Size; currentRegistry++; current++; } // // Hand it back to the caller // *ResourceMap = resourceMap; return STATUS_SUCCESS; } NTSTATUS MfEnumRegistryChild( IN HANDLE ParentHandle, IN ULONG Index, IN OUT PMF_DEVICE_INFO Info ) /*++ Routine Description: Initialized an MF_DEVICE_INFO from information stored in the registry. Arguments: ParentHandle - Handle to the registry key under which the data is stored Index - Index of the subkey to use Info - Pointer to the device info that should be filled in Return Value: Status code indicating the success or otherwise of the operation. --*/ { NTSTATUS status; PMF_REGISTRY_VARYING_RESOURCE_MAP varyingMap = NULL; PUCHAR resourceMap = NULL; ULONG varyingMapSize = 0, resourceMapSize = 0, stringSize = 0; BOOLEAN gotId = FALSE; ASSERT(ParentHandle && Info); // // Retrieve the data - we must have a HardwareID and/or CompatibleID // status = MfGetRegistryValue(ParentHandle, L"HardwareID", REG_MULTI_SZ, MF_GETREG_SZ_TO_MULTI_SZ, &stringSize, &Info->HardwareID.Buffer ); if (NT_SUCCESS(status)) { gotId = TRUE; } else if (status != STATUS_OBJECT_NAME_NOT_FOUND) { goto cleanup; } ASSERT(stringSize <= MAXUSHORT); if (stringSize <= MAXUSHORT) { Info->HardwareID.Length = (USHORT)stringSize; Info->HardwareID.MaximumLength = Info->HardwareID.Length; } else { status = STATUS_INVALID_PARAMETER; goto cleanup; } // // ... CompatibleID ... // stringSize = 0; status = MfGetRegistryValue(ParentHandle, L"CompatibleID", REG_MULTI_SZ, MF_GETREG_SZ_TO_MULTI_SZ, &stringSize, &Info->CompatibleID.Buffer ); if (NT_SUCCESS(status)) { gotId = TRUE; } else if (status != STATUS_OBJECT_NAME_NOT_FOUND) { goto cleanup; } ASSERT(stringSize <= MAXUSHORT); if (stringSize <= MAXUSHORT) { Info->CompatibleID.Length = (USHORT)stringSize; Info->CompatibleID.MaximumLength = Info->CompatibleID.Length; } else { status = STATUS_INVALID_PARAMETER; goto cleanup; } // // Now check that we have got an ID - if we don't then fail // if (!gotId) { status = STATUS_UNSUCCESSFUL; goto cleanup; } // // ...ResourceMap... // status = MfGetRegistryValue(ParentHandle, L"ResourceMap", REG_BINARY, 0, // flags &resourceMapSize, &resourceMap ); if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_NOT_FOUND) { goto cleanup; } // // If we have a resource map the store it in our device info // if (resourceMap) { status = MfBuildResourceMap(resourceMap, resourceMapSize, &Info->ResourceMap ); ExFreePool(resourceMap); resourceMap = NULL; if (!NT_SUCCESS(status)) { goto cleanup; } } // // ...VaryingResourceMap... // status = MfGetRegistryValue(ParentHandle, L"VaryingResourceMap", REG_BINARY, 0, // flags &varyingMapSize, &varyingMap ); if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_NOT_FOUND) { goto cleanup; } if (varyingMap) { status = MfBuildVaryingResourceMap(varyingMap, varyingMapSize, &Info->VaryingResourceMap ); ExFreePool(varyingMap); varyingMap = NULL; if (!NT_SUCCESS(status)) { goto cleanup; } } // // ...MfFlags // status = MfGetRegistryValue(ParentHandle, L"MFFlags", REG_DWORD, 0, // flags NULL, (PVOID) &Info->MfFlags ); if (!NT_SUCCESS(status) && status != STATUS_OBJECT_NAME_NOT_FOUND) { goto cleanup; } return STATUS_SUCCESS; cleanup: MfFreeDeviceInfo(Info); // // If any of the values were of the wrong type then this is an invalid // MF entry. // if (status == STATUS_OBJECT_TYPE_MISMATCH) { status = STATUS_INVALID_PARAMETER; } return status; } NTSTATUS MfEnumerate( IN PMF_PARENT_EXTENSION Parent ) /*++ Routine Description: Allocates and initialies the Children list of PDOs for this MF device. First from the registry and then by querying an MF_ENUMERATION_INTERFACE from its PDO. Arguments: Parent - The MF device that should be enumerated Return Value: Status code indicating the success or otherwise of the operation. --*/ { NTSTATUS status; PMF_CHILD_EXTENSION current, next; // // Try to get our children from the registry // status = MfEnumerateFromRegistry(Parent); if (!NT_SUCCESS(status)) { // // STATUS_UNSUCCESSFUL indicates that there wasn't any MF information // in the registry // if (status == STATUS_UNSUCCESSFUL) { // // See if our parent has an MF_ENUMERATION_INTERFACE for us... // status = MfEnumerateFromInterface(Parent); } } return status; } NTSTATUS MfEnumerateFromRegistry( IN PMF_PARENT_EXTENSION Parent ) /*++ Routine Description: Allocates and initialies the Children list of PDOs for this MF device by looking in the registry Arguments: Parent - The MF device that should be enumerated Return Value: Status code indicating the success or otherwise of the operation. STATUS_UNSUCCESSFUL indicates that no MF information was found in the registry. --*/ { NTSTATUS status; HANDLE parentHandle = NULL, childHandle = NULL; ULONG index = 0; UNICODE_STRING childName; PDEVICE_OBJECT pdo; PMF_CHILD_EXTENSION child; MF_DEVICE_INFO info; ASSERT(!(Parent->Common.DeviceState & MF_DEVICE_ENUMERATED)); // // Open the "Device Parameters" key for our PDO and see what the INF file // put there. // status = IoOpenDeviceRegistryKey(Parent->PhysicalDeviceObject, PLUGPLAY_REGKEY_DEVICE, KEY_READ, &parentHandle ); if (!NT_SUCCESS(status)) { goto cleanup; } ASSERT(parentHandle); // // Iterate over keys // for (;;) { // // Open the child key for this info // status = MfGetSubkeyByIndex(parentHandle, index, KEY_READ, &childHandle, &childName ); if (status == STATUS_NO_MORE_ENTRIES) { if (IsListEmpty(&Parent->Children)) { // // There wern't any children - fail // status = STATUS_UNSUCCESSFUL; goto cleanup; } // // We've found all the children // break; } if (!NT_SUCCESS(status)) { goto cleanup; } RtlZeroMemory(&info, sizeof(info)); if (!MfIsChildEnumeratedAlready(Parent, &childName)) { info.Name = childName; // // Query the registry for the info // status = MfEnumRegistryChild(childHandle, index, &info); if (!NT_SUCCESS(status)) { goto cleanup; } status = MfValidateDeviceInfo(Parent,&info); if (!NT_SUCCESS(status)) { ASSERT(FALSE); goto cleanup; } status = MfCreatePdo(Parent, &pdo); if (NT_SUCCESS(status)) { child = (PMF_CHILD_EXTENSION) pdo->DeviceExtension; child->Info = info; child->Common.DeviceState |= MF_DEVICE_ENUMERATED; } else { MfFreeDeviceInfo(&info); } } else { RtlFreeUnicodeString(&childName); } ZwClose(childHandle); index++; } ZwClose(parentHandle); return STATUS_SUCCESS; cleanup: if (parentHandle) { ZwClose(parentHandle); } if (childHandle) { ZwClose(childHandle); } return status; } NTSTATUS MfEnumerateFromInterface( IN PMF_PARENT_EXTENSION Parent ) /*++ Routine Description: Allocates and initialies the Children list of PDOs for this MF device by querying its pdo for an interface Arguments: Parent - The MF device that should be enumerated Return Value: Status code indicating the success or otherwise of the operation. --*/ { NTSTATUS status; IO_STACK_LOCATION request; MF_ENUMERATION_INTERFACE interface; PDEVICE_OBJECT pdo; PMF_CHILD_EXTENSION child; MF_DEVICE_INFO info; ULONG index = 0; // // Send a query interface IRP to our parent's PDO // RtlZeroMemory(&request, sizeof(IO_STACK_LOCATION)); RtlZeroMemory(&interface, sizeof(MF_ENUMERATION_INTERFACE)); request.MajorFunction = IRP_MJ_PNP; request.MinorFunction = IRP_MN_QUERY_INTERFACE; request.Parameters.QueryInterface.InterfaceType = &GUID_MF_ENUMERATION_INTERFACE; request.Parameters.QueryInterface.Size = sizeof(MF_ENUMERATION_INTERFACE); request.Parameters.QueryInterface.Version = 1; request.Parameters.QueryInterface.Interface = (PINTERFACE) &interface; status = MfSendPnpIrp(Parent->PhysicalDeviceObject, &request, NULL); if (!NT_SUCCESS(status)) { goto cleanup; } FOR_ALL_IN_LIST(MF_CHILD_EXTENSION, &Parent->Children, child) { child->Common.DeviceState &= ~MF_DEVICE_ENUMERATED; } for (;;) { RtlZeroMemory(&info, sizeof(info)); // // Query the interface for the info // status = interface.EnumerateChild(interface.Context, index, &info ); if (!NT_SUCCESS(status)) { if (status == STATUS_NO_MORE_ENTRIES) { if (IsListEmpty(&Parent->Children)) { // // There wern't any children - fail // status = STATUS_UNSUCCESSFUL; goto cleanup; } status = STATUS_SUCCESS; break; } else { goto cleanup; } } status = MfValidateDeviceInfo(Parent,&info); if (!NT_SUCCESS(status)) { goto cleanup; } if (!MfIsChildEnumeratedAlready(Parent, &info.Name)) { // // Create a device object // status = MfCreatePdo(Parent, &pdo); if (NT_SUCCESS(status)) { child = (PMF_CHILD_EXTENSION) pdo->DeviceExtension; child->Info = info; child->Common.DeviceState |= MF_DEVICE_ENUMERATED; } else { MfFreeDeviceInfo(&info); } } else { child->Common.DeviceState |= MF_DEVICE_ENUMERATED; MfFreeDeviceInfo(&info); } index++; } interface.InterfaceDereference(interface.Context); return STATUS_SUCCESS; cleanup: return status; } NTSTATUS MfBuildDeviceID( IN PMF_PARENT_EXTENSION Parent, OUT PWSTR *DeviceID ) /*++ Routine Description: Constructs a device ID for the parent device Arguments: Parent - Parent the device ID should be constructed for DeviceID - On success the device id Return Value: Status code indicating the success or otherwise of the operation. --*/ { #define MF_ENUMERATOR_STRING L"MF\\" NTSTATUS status; PWSTR source, destination, id = NULL; ULONG idSize; PWCHAR deviceID; SIZE_T dummy; idSize = sizeof(MF_ENUMERATOR_STRING) + Parent->DeviceID.Length; id = ExAllocatePoolWithTag(PagedPool, idSize, MF_DEVICE_ID_TAG ); if (!id) { status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } // // First copy the enumerator prefix // the deviceID parameter points to the end of // this string. // if (FAILED(StringCbCopyEx(id, // Destination idSize, // Destination buffer size MF_ENUMERATOR_STRING, // Source &deviceID, // Ptr to end of the copy &dummy, // bytes remaining in destination 0 // Flags ))) { ASSERT(FALSE); status = STATUS_INVALID_PARAMETER; goto cleanup; } // // Now concatenate the device ID of the parent // to this enumerator prefix. This means that // the deviceID variable points to the beginning of // this portion of the string. StringCbCatN guarantees // NULL termination of the string. // if (FAILED(StringCbCatN(id, idSize, Parent->DeviceID.Buffer, Parent->DeviceID.Length ))) { ASSERT(FALSE); status = STATUS_INVALID_PARAMETER; goto cleanup; } // // replace each occurence of '\' in the device ID // portion with '#' // while (*deviceID != UNICODE_NULL) { ASSERT(*deviceID != L' '); if (*deviceID == L'\\') { *deviceID = L'#'; } deviceID++; } *DeviceID = id; return STATUS_SUCCESS; cleanup: if (id) { ExFreePool(id); } *DeviceID = NULL; return status; } NTSTATUS MfBuildInstanceID( IN PMF_CHILD_EXTENSION Child, OUT PWSTR *InstanceID ) /*++ Routine Description: Constructs a instance ID for this child Arguments: Child - Child the ID should be constructed for DeviceID - On success the device id Return Value: Status code indicating the success or otherwise of the operation. --*/ { NTSTATUS status; PWSTR current, id = NULL; ULONG idSize; PWCHAR instancePtr; idSize = Child->Parent->InstanceID.Length + sizeof(L'#') + Child->Info.Name.Length + sizeof(UNICODE_NULL); id = ExAllocatePoolWithTag(PagedPool, idSize, MF_INSTANCE_ID_TAG ); if (!id) { status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } // // Copy the parents instance ID... // if (FAILED(StringCbCopyN(id, idSize, Child->Parent->InstanceID.Buffer, Child->Parent->InstanceID.Length ))) { ASSERT(FALSE); status = STATUS_INVALID_PARAMETER; goto cleanup; } // // ...then the '#'... // if (FAILED(StringCbCat(id, idSize, L"#"))) { ASSERT(FALSE); status = STATUS_INVALID_PARAMETER; goto cleanup; } // // ...the child name... // if (FAILED(StringCbCatN(id, idSize, Child->Info.Name.Buffer, Child->Info.Name.Length ))) { ASSERT(FALSE); status = STATUS_INVALID_PARAMETER; goto cleanup; } *InstanceID = id; return STATUS_SUCCESS; cleanup: if (id) { ExFreePool(id); } *InstanceID = NULL; return status; } BOOLEAN MfIsResourceShared( IN PMF_PARENT_EXTENSION Parent, IN UCHAR Index, IN ULONG Offset, IN ULONG Size ) /*++ Routine Description: Determines if the Parent resource of Index has been requested by more than one child, in which case the children wanting that resource should claim it shared. Arguments: Parent - The parent device of the MF subtree. Index - The index of the parent resources we are interested in. Return Value: TRUE if the resource is shared, FALSE otherwise --*/ { PMF_CHILD_EXTENSION current; PUCHAR resource; PMF_VARYING_RESOURCE_ENTRY varyingResource; PLIST_ENTRY currentEntry; BOOLEAN result = FALSE; ULONG refCount = 0; // // Iterate through the list of children in the parent // MfAcquireChildrenLock(Parent); for (currentEntry = Parent->Children.Flink; currentEntry != &Parent->Children; currentEntry = currentEntry->Flink) { current = CONTAINING_RECORD(currentEntry, MF_CHILD_EXTENSION, ListEntry); // // Iterate through the array of descriptors // if (current->Info.ResourceMap) { FOR_ALL_IN_ARRAY(current->Info.ResourceMap->Resources, current->Info.ResourceMap->Count, resource) { if (*resource == Index) { refCount++; if (refCount > 1) { result = TRUE; goto out; } } } } if (current->Info.VaryingResourceMap) { FOR_ALL_IN_ARRAY(current->Info.VaryingResourceMap->Resources, current->Info.VaryingResourceMap->Count, varyingResource) { // // If indexes are the same and ranges overlap, we have a reference // if ((varyingResource->ResourceIndex == Index) && ( ( Size == 0) || ( varyingResource->Offset >= Offset && varyingResource->Offset < Offset + Size) || ( Offset >= varyingResource->Offset && Offset < varyingResource->Offset + varyingResource->Size))) { refCount++; if (refCount > 1) { result = TRUE; goto out; } } } } } out: MfReleaseChildrenLock(Parent); return result; } NTSTATUS MfBuildChildRequirements( IN PMF_CHILD_EXTENSION Child, OUT PIO_RESOURCE_REQUIREMENTS_LIST *RequirementsList ) /*++ Routine Description: Constructs a requirements list for Child based on the resources allocated to the childs parent Arguments: Child - Child the requirements list is to be built for RequirementsList - On success a pointer to the list Return Value: Status code indicating the success or otherwise of the operation. --*/ { NTSTATUS status; ULONG size, count = 0; PIO_RESOURCE_REQUIREMENTS_LIST requirements = NULL; PIO_RESOURCE_DESCRIPTOR descriptor; PCHAR resource; PMF_VARYING_RESOURCE_ENTRY varyingResource; // // Check if we have a resource list. If not, then MF has been // loaded on device that doesn't consume resources. As a result, // the children can't consume resources either. // if (Child->Parent->ResourceList == NULL) { *RequirementsList = NULL; return STATUS_SUCCESS; } // // Calculate the size of the resource list // if (Child->Info.VaryingResourceMap) { count += Child->Info.VaryingResourceMap->Count; } if (Child->Info.ResourceMap) { count += Child->Info.ResourceMap->Count; } // // Allocate the buffer // size = sizeof(IO_RESOURCE_REQUIREMENTS_LIST) + (count-1) * sizeof(IO_RESOURCE_DESCRIPTOR); requirements = ExAllocatePoolWithTag(PagedPool, size, MF_CHILD_REQUIREMENTS_TAG ); if (!requirements) { status = STATUS_INSUFFICIENT_RESOURCES; goto cleanup; } // // Build the list // RtlZeroMemory(requirements, size); requirements->ListSize = size; requirements->InterfaceType = Child->Parent->ResourceList->List[0].InterfaceType; requirements->BusNumber = Child->Parent->ResourceList->List[0].BusNumber; requirements->AlternativeLists = 1; requirements->List[0].Version = MF_CM_RESOURCE_VERSION; requirements->List[0].Revision = MF_CM_RESOURCE_REVISION; requirements->List[0].Count = count; descriptor = requirements->List[0].Descriptors; if (Child->Info.ResourceMap) { FOR_ALL_IN_ARRAY(Child->Info.ResourceMap->Resources, Child->Info.ResourceMap->Count, resource) { status = MfParentResourceToChildRequirement(Child->Parent, Child, *resource, 0, 0, descriptor ); if (!NT_SUCCESS(status)) { goto cleanup; } descriptor++; } } if (Child->Info.VaryingResourceMap) { FOR_ALL_IN_ARRAY(Child->Info.VaryingResourceMap->Resources, Child->Info.VaryingResourceMap->Count, varyingResource) { status = MfParentResourceToChildRequirement(Child->Parent, Child, varyingResource->ResourceIndex, varyingResource->Offset, varyingResource->Size, descriptor ); if (!NT_SUCCESS(status)) { goto cleanup; } descriptor++; } } *RequirementsList = requirements; return STATUS_SUCCESS; cleanup: *RequirementsList = NULL; if (requirements) { ExFreePool(requirements); } return status; } NTSTATUS MfParentResourceToChildRequirement( IN PMF_PARENT_EXTENSION Parent, IN PMF_CHILD_EXTENSION Child, IN UCHAR Index, IN ULONG Offset OPTIONAL, IN ULONG Size OPTIONAL, OUT PIO_RESOURCE_DESCRIPTOR Requirement ) /*++ Routine Description: This function build an requirements descriptor for a resource the parent is started with. Arguments: Parent - The parent device of the MF subtree. Index - The index of the parent resources we are interested in. Offset - The offset within the parent resource of the requirement. This is actually used as an index into a table stored in the parent resource list describing the mapping from this given offset to the real offset to be used. This allows for varying resource maps to access the same offset within the same resource and get a different requirement. If Size == 0, this is ignored. Size - The length of the requirement. If set to 0, it is assumed to be the length of the parent resource. Requirement - Pointer to a descriptor that should be filled in Return Value: Success or otherwise of the operation --*/ { NTSTATUS status; CM_PARTIAL_RESOURCE_DESCRIPTOR resource; PMF_RESOURCE_TYPE resType; ULONG effectiveOffset; ULONGLONG resourceStart; ULONG dummyLength; ASSERT(Parent->ResourceList->Count == 1); // // Bounds check the index // if (Index > Parent->ResourceList->List[0].PartialResourceList.Count) { if (Child->Info.MfFlags & MF_FLAGS_FILL_IN_UNKNOWN_RESOURCE) { // // Fill in a null resource list // RtlZeroMemory(Requirement, sizeof(IO_RESOURCE_DESCRIPTOR)); Requirement->Type = CmResourceTypeNull; return STATUS_SUCCESS; } return STATUS_INVALID_PARAMETER; } RtlCopyMemory(&resource, &Parent->ResourceList->List[0].PartialResourceList.PartialDescriptors[Index], sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)); // // Find the appropriate resource type for the resource -> requirement // function if this is an arbitrated resource // if (!(resource.Type & CmResourceTypeNonArbitrated)) { resType = MfFindResourceType(resource.Type); if (!resType) { DEBUG_MSG(1, ("Unknown resource type %i at parent index 0x%x\n", resource.Type, Index )); return STATUS_INVALID_PARAMETER; } // // update the resource with the correct offset and length // if size == 0 we assume it is optional and don't do the update // if (Size) { status = resType->UnpackResource(&resource, &resourceStart, &dummyLength); if (!NT_SUCCESS(status)) { return status; } status = resType->UpdateResource(&resource, resourceStart+Offset, Size ); if (!NT_SUCCESS(status)) { return status; } } // // Convert the resource to a requirement // status = resType->RequirementFromResource(&resource, Requirement); if (!NT_SUCCESS(status)) { return status; } // // Update the share disposition if necessary // if (MfIsResourceShared(Parent, Index, Offset, Size)) { Requirement->ShareDisposition = CmResourceShareShared; } } else { // // This is a non-arbitrated resource so it is modled after a device // private, just copy the data // Requirement->Type = resource.Type; Requirement->ShareDisposition = resource.ShareDisposition; Requirement->Flags = resource.Flags; Requirement->u.DevicePrivate.Data[0] = resource.u.DevicePrivate.Data[0]; Requirement->u.DevicePrivate.Data[1] = resource.u.DevicePrivate.Data[1]; Requirement->u.DevicePrivate.Data[2] = resource.u.DevicePrivate.Data[2]; } return STATUS_SUCCESS; } BOOLEAN MfIsChildEnumeratedAlready( PMF_PARENT_EXTENSION Parent, PUNICODE_STRING ChildName ) /*++ Routine Description: This function checks whether a child with this name has already been enumerated. Arguments: Parent - The parent device of the MF subtree. ChildName - unicode string to compare to existing child names Return Value: TRUE or FALSE --*/ { PMF_CHILD_EXTENSION currentChild; PLIST_ENTRY currentEntry; BOOLEAN result = FALSE; for (currentEntry = Parent->Children.Flink; currentEntry != &Parent->Children; currentEntry = currentEntry->Flink) { currentChild = CONTAINING_RECORD(currentEntry, MF_CHILD_EXTENSION, ListEntry); // // Comparison is case-sensitive because there is no reason it // shouldn't be. // if (RtlEqualUnicodeString(¤tChild->Info.Name, ChildName, FALSE)) { result = TRUE; break; } } return result; } NTSTATUS MfValidateDeviceInfo( IN PMF_PARENT_EXTENSION Parent, IN PMF_DEVICE_INFO DeviceInfo ) /*++ Routine Description: This routine validates that an MF_DEVICE_INFO structure read either from an interface or the registry is valid. Arguments: Parent - The parent device for which the MF_DEVICE_INFO structure represents a child. DeviceInfo - The structure to validate. Return Value: STATUS_SUCCESS if validation was successful. STATUS_UNSUCCESSFUL otherwise. --*/ { ULONG i, parentResCount, parentResLength; PCM_PARTIAL_RESOURCE_DESCRIPTOR parentResources; PMF_VARYING_RESOURCE_ENTRY varyingResource; if (Parent->ResourceList) { parentResCount = Parent->ResourceList->List[0].PartialResourceList.Count; parentResources = Parent->ResourceList->List[0].PartialResourceList.PartialDescriptors; } else { // // The parent has no resources, so the device better not have // any resource maps. // if (DeviceInfo->ResourceMap || DeviceInfo->VaryingResourceMap) { return STATUS_UNSUCCESSFUL; } } // // Make sure that each entry in the resource map points to a valid resource // of the parent device. // if (DeviceInfo->ResourceMap) { for (i=0; iResourceMap->Count; i++) { if (DeviceInfo->ResourceMap->Resources[i] >= parentResCount) { return STATUS_UNSUCCESSFUL; } } } // // Make sure that each entry in the varying resource map points to a valid // resource of the parent device, and additionally, make sure that the // offset/length in the varying resource map entry are a subset of the // corresponding resource in the parent device. // if (DeviceInfo->VaryingResourceMap) { for (i=0; iVaryingResourceMap->Count; i++) { varyingResource = &DeviceInfo->VaryingResourceMap->Resources[i]; if (varyingResource->ResourceIndex >= parentResCount) { return STATUS_UNSUCCESSFUL; } parentResLength = parentResources[varyingResource->ResourceIndex].u.Generic.Length; if ((varyingResource->Offset >= parentResLength) || (varyingResource->Size > parentResLength) || ((varyingResource->Offset + varyingResource->Size) > parentResLength)) { return STATUS_UNSUCCESSFUL; } } } return STATUS_SUCCESS; }