/***************************************************************************** * property.cpp - property support ***************************************************************************** * Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved. */ #include "private.h" /***************************************************************************** * Functions */ #pragma code_seg("PAGE") /***************************************************************************** * PcHandlePropertyWithTable() ***************************************************************************** * Uses a property table to handle a property request IOCTL. */ PORTCLASSAPI NTSTATUS NTAPI PcHandlePropertyWithTable ( IN PIRP pIrp , IN ULONG ulPropertySetsCount , IN const KSPROPERTY_SET* pKsPropertySet , IN PPROPERTY_CONTEXT pPropertyContext ) { ASSERT(pIrp); ASSERT(pPropertyContext); pIrp->Tail.Overlay.DriverContext[3] = pPropertyContext; NTSTATUS ntStatus = KsPropertyHandler ( pIrp, ulPropertySetsCount, pKsPropertySet ); return ntStatus; } /***************************************************************************** * PcDispatchProperty() ***************************************************************************** * Dispatch a property via a PCPROPERTY_ITEM entry. */ PORTCLASSAPI NTSTATUS NTAPI PcDispatchProperty ( IN PIRP pIrp OPTIONAL, IN PPROPERTY_CONTEXT pPropertyContext, IN const KSPROPERTY_SET * pKsPropertySet OPTIONAL, IN ULONG ulIdentifierSize, IN PKSIDENTIFIER pKsIdentifier, IN OUT PULONG pulDataSize, IN OUT PVOID pvData OPTIONAL ) { PAGED_CODE(); ASSERT(pPropertyContext); ASSERT(pKsIdentifier); ASSERT(pulDataSize); PPCPROPERTY_REQUEST pPcPropertyRequest = new(NonPagedPool,'rPcP') PCPROPERTY_REQUEST; NTSTATUS ntStatus; if (! pPcPropertyRequest) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } else { // // Copy target information from the context structure. // pPcPropertyRequest->MajorTarget = pPropertyContext->pUnknownMajorTarget; pPcPropertyRequest->MinorTarget = pPropertyContext->pUnknownMinorTarget; pPcPropertyRequest->Node = pPropertyContext->ulNodeId; // // Determine the value size. // pPcPropertyRequest->ValueSize = *pulDataSize; // // If the node number is in the instance data, extract it. // TODO: Remove this when node objects rule. // if ( (pKsIdentifier->Flags & KSPROPERTY_TYPE_TOPOLOGY) && (pPcPropertyRequest->Node == ULONG(-1)) ) { // // Get the node id and remaining instance. // if (ulIdentifierSize < sizeof(KSP_NODE)) { delete pPcPropertyRequest; return STATUS_INVALID_BUFFER_SIZE; } PKSP_NODE pKsPNode = PKSP_NODE(pKsIdentifier); pPcPropertyRequest->Node = pKsPNode->NodeId; pPcPropertyRequest->InstanceSize = ulIdentifierSize - sizeof(KSP_NODE); pPcPropertyRequest->Instance = ( (pPcPropertyRequest->InstanceSize == 0) ? NULL : PVOID(pKsPNode + 1) ); } else { // // No node in the instance...get generic instance if any. // pPcPropertyRequest->InstanceSize = ulIdentifierSize - sizeof(KSIDENTIFIER); pPcPropertyRequest->Instance = ( (pPcPropertyRequest->InstanceSize == 0) ? NULL : PVOID(pKsIdentifier + 1) ); } if (pKsPropertySet) { ASSERT(pKsPropertySet->PropertyItem); // // Find the property item in the KS-style list. // #if (DBG) ULONG dbgCount = pKsPropertySet->PropertiesCount; #endif for ( const KSPROPERTY_ITEM *pKsPropertyItem = pKsPropertySet->PropertyItem ; pKsPropertyItem->PropertyId != pKsIdentifier->Id ; pKsPropertyItem++ ) { ASSERT(--dbgCount); } // // The property item is stashed in the Relations field if this is not // a node property. If it is, we have to find the node property in // the original list. // pPcPropertyRequest->PropertyItem = PPCPROPERTY_ITEM(pKsPropertyItem->Relations); } else { // // No KS set was supplied. We need to look in the original list // associated with the node. // pPcPropertyRequest->PropertyItem = NULL; } if (! pPcPropertyRequest->PropertyItem) { PPCFILTER_DESCRIPTOR pPcFilterDescriptor = pPropertyContext->pPcFilterDescriptor; if ( pPcFilterDescriptor && ( pPcPropertyRequest->Node < pPcFilterDescriptor->NodeCount ) && pPcFilterDescriptor-> Nodes[pPcPropertyRequest->Node].AutomationTable ) { // // Valid node...search the original property item list. // const PCAUTOMATION_TABLE *pPcAutomationTable = pPcFilterDescriptor-> Nodes[pPcPropertyRequest->Node].AutomationTable; const PCPROPERTY_ITEM *pPcPropertyItem = pPcAutomationTable->Properties; for (ULONG ul = pPcAutomationTable->PropertyCount; ul--; ) { if ( IsEqualGUIDAligned ( *pPcPropertyItem->Set , pKsIdentifier->Set ) && (pPcPropertyItem->Id == pKsIdentifier->Id) ) { pPcPropertyRequest->PropertyItem = pPcPropertyItem; break; } pPcPropertyItem = PPCPROPERTY_ITEM ( PBYTE(pPcPropertyItem) + pPcAutomationTable->PropertyItemSize ); } } else { // // The node ID was invalid. // ntStatus = STATUS_NOT_FOUND; } } // // Call the handler if we have a property item with a handler. // if ( pPcPropertyRequest->PropertyItem && pPcPropertyRequest->PropertyItem->Handler ) { pPcPropertyRequest->Verb = pKsIdentifier->Flags; pPcPropertyRequest->Value = pvData; pPcPropertyRequest->Irp = pIrp; // // Call the handler. // ntStatus = pPcPropertyRequest->PropertyItem->Handler ( pPcPropertyRequest ); *pulDataSize = pPcPropertyRequest->ValueSize; } else { ntStatus = STATUS_NOT_FOUND; } // // Delete the request structure unless we are pending. // if (ntStatus != STATUS_PENDING) { delete pPcPropertyRequest; } else { // // Only requests with IRPs can be pending. // ASSERT(pIrp); } } return ntStatus; } /***************************************************************************** * PropertyItemPropertyHandler() ***************************************************************************** * KS-sytle property handler that handles all properties using the * PCPROPERTY_ITEM mechanism. */ NTSTATUS PropertyItemPropertyHandler ( IN PIRP pIrp, IN PKSIDENTIFIER pKsIdentifier, IN OUT PVOID pvData OPTIONAL ) { PAGED_CODE(); ASSERT(pIrp); ASSERT(pKsIdentifier); // // Extract various things from the IRP and dispatch the property. // PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(pIrp); ULONG ulDataSize = irpSp->Parameters.DeviceIoControl.OutputBufferLength; NTSTATUS ntStatus = PcDispatchProperty ( pIrp , PPROPERTY_CONTEXT(pIrp->Tail.Overlay.DriverContext[3]) , KSPROPERTY_SET_IRP_STORAGE(pIrp) , irpSp->Parameters.DeviceIoControl.InputBufferLength , pKsIdentifier , &ulDataSize , pvData ); // // Inform the caller of the resulting status and size. // Pending IRPs must be IoMarkIrpPending()ed before the dispatch routine // returns. // if ((ntStatus != STATUS_PENDING) && !NT_ERROR(ntStatus)) { pIrp->IoStatus.Information = ulDataSize; } return ntStatus; } /***************************************************************************** * PcCompletePendingPropertyRequest() ***************************************************************************** * Completes a pending property request. */ PORTCLASSAPI NTSTATUS NTAPI PcCompletePendingPropertyRequest ( IN PPCPROPERTY_REQUEST PropertyRequest, IN NTSTATUS NtStatus ) { ASSERT(PropertyRequest); ASSERT(NtStatus != STATUS_PENDING); // // Validate Parameters. // if (NULL == PropertyRequest) { _DbgPrintF(DEBUGLVL_TERSE, ("PcCompletePendingPropertyRequest : Invalid Parameter.")); return STATUS_INVALID_PARAMETER; } if (!NT_ERROR(NtStatus)) { PropertyRequest->Irp->IoStatus.Information = PropertyRequest->ValueSize; } PropertyRequest->Irp->IoStatus.Status = NtStatus; IoCompleteRequest(PropertyRequest->Irp,IO_NO_INCREMENT); delete PropertyRequest; return STATUS_SUCCESS; } /***************************************************************************** * PcFreePropertyTable() ***************************************************************************** * Frees allocated memory in a PROPERTY_TABLE structure. */ PORTCLASSAPI void NTAPI PcFreePropertyTable ( IN PPROPERTY_TABLE PropertyTable ) { _DbgPrintF(DEBUGLVL_VERBOSE,("PcFreePropertyTable")); PAGED_CODE(); ASSERT(PropertyTable); ASSERT((!PropertyTable->PropertySets) == (!PropertyTable->PropertySetCount)); // PropertySets and PropertySetCount must be non-NULL/non-zero, or NULL/zero ASSERT(PropertyTable->StaticSets == (!PropertyTable->StaticItems)); // StaticSets and StaticItems must be TRUE/NULL, or FALSE/non-null PBOOLEAN staticItem = PropertyTable->StaticItems; if (staticItem) { PKSPROPERTY_SET propertySet = PropertyTable->PropertySets; if (propertySet) { for( ULONG count = PropertyTable->PropertySetCount; count--; propertySet++, staticItem++) { if ((! *staticItem) && propertySet->PropertyItem) { ExFreePool(PVOID(propertySet->PropertyItem)); } } } ExFreePool(PropertyTable->StaticItems); PropertyTable->StaticItems = NULL; } if (PropertyTable->PropertySets && !PropertyTable->StaticSets) { PropertyTable->PropertySetCount = 0; ExFreePool(PropertyTable->PropertySets); PropertyTable->PropertySets = NULL; } PropertyTable->StaticSets = TRUE; } /***************************************************************************** * PcAddToPropertyTable() ***************************************************************************** * Adds a PROPERTY_ITEM property table to a PROPERTY_TABLE structure. */ PORTCLASSAPI NTSTATUS NTAPI PcAddToPropertyTable ( IN OUT PPROPERTY_TABLE PropertyTable, IN ULONG PropertyItemCount, IN const PCPROPERTY_ITEM * PropertyItems, IN ULONG PropertyItemSize, IN BOOLEAN NodeTable ) { PAGED_CODE(); ASSERT(PropertyTable); ASSERT(PropertyItems); ASSERT(PropertyItemSize >= sizeof(PCPROPERTY_ITEM)); _DbgPrintF(DEBUGLVL_VERBOSE,("PcAddToEventTable")); #define ADVANCE(item) (item = PPCPROPERTY_ITEM(PBYTE(item) + PropertyItemSize)) ASSERT((!PropertyTable->PropertySets) == (!PropertyTable->PropertySetCount)); // values must be non-NULL/non-zero, or NULL/zero. // // Determine how many sets we will end up with. // ULONG setCount = PropertyTable->PropertySetCount; const PCPROPERTY_ITEM *item = PropertyItems; for (ULONG count = PropertyItemCount; count--; ADVANCE(item)) { BOOLEAN countThis = TRUE; // // See if it's already in the table. // PKSPROPERTY_SET propertySet = PropertyTable->PropertySets; for ( ULONG count2 = PropertyTable->PropertySetCount; count2--; propertySet++ ) { if (IsEqualGUIDAligned(*item->Set,*propertySet->Set)) { countThis = FALSE; break; } } if (countThis) { // // See if it's appeared in the list previously. // for ( const PCPROPERTY_ITEM *prevItem = PropertyItems; prevItem != item; ADVANCE(prevItem) ) { if (IsEqualGUIDAligned(*item->Set,*prevItem->Set)) { countThis = FALSE; break; } } } if (countThis) { setCount++; } } NTSTATUS ntStatus = STATUS_SUCCESS; // // Make a new set table // ASSERT(setCount); ASSERT(setCount >= PropertyTable->PropertySetCount); // // Allocate memory required for the set table. // PKSPROPERTY_SET newTable = PKSPROPERTY_SET ( ExAllocatePoolWithTag ( PagedPool, sizeof(KSPROPERTY_SET) * setCount, 'tScP' ) ); // // Allocate memory for the static items flags. // PBOOLEAN newStaticItems = NULL; if (newTable) { newStaticItems = PBOOLEAN ( ExAllocatePoolWithTag ( PagedPool, sizeof(BOOLEAN) * setCount, 'bScP' ) ); if (! newStaticItems) { ExFreePool(newTable); newTable = NULL; } } if (newTable) { // // Initialize the new set table. // RtlZeroMemory ( PVOID(newTable), sizeof(KSPROPERTY_SET) * setCount ); if (PropertyTable->PropertySetCount != 0) { RtlCopyMemory ( PVOID(newTable), PVOID(PropertyTable->PropertySets), sizeof(KSPROPERTY_SET) * PropertyTable->PropertySetCount ); } // // Initialize the new static items flags. // RtlFillMemory ( PVOID(newStaticItems), sizeof(BOOLEAN) * setCount, 0xff ); if (PropertyTable->StaticItems && PropertyTable->PropertySetCount) { // // Flags existed before...copy them. // RtlCopyMemory ( PVOID(newStaticItems), PVOID(PropertyTable->StaticItems), sizeof(BOOLEAN) * PropertyTable->PropertySetCount ); } // // Assign set GUIDs to the new set entries. // PKSPROPERTY_SET addHere = newTable + PropertyTable->PropertySetCount; const PCPROPERTY_ITEM *item2 = PropertyItems; for (ULONG count = PropertyItemCount; count--; ADVANCE(item2)) { BOOLEAN addThis = TRUE; // // See if it's already in the table. // for( PKSPROPERTY_SET propertySet = newTable; propertySet != addHere; propertySet++) { if (IsEqualGUIDAligned(*item2->Set,*propertySet->Set)) { addThis = FALSE; break; } } if (addThis) { addHere->Set = item2->Set; addHere++; } } ASSERT(addHere == newTable + setCount); // // Free old allocated tables. // if (PropertyTable->PropertySets && (!PropertyTable->StaticSets)) { ExFreePool(PropertyTable->PropertySets); } if (PropertyTable->StaticItems) { ExFreePool(PropertyTable->StaticItems); } // // Install the new tables. // PropertyTable->PropertySetCount = setCount; PropertyTable->PropertySets = newTable; PropertyTable->StaticSets = FALSE; PropertyTable->StaticItems = newStaticItems; } else { // if allocations fail, return error and // keep sets and items as they were. ntStatus = STATUS_INSUFFICIENT_RESOURCES; } // // Now we have a property set table that contains all the sets we need. // if (NT_SUCCESS(ntStatus)) { // // For each set... // PKSPROPERTY_SET propertySet = PropertyTable->PropertySets; PBOOLEAN staticItem = PropertyTable->StaticItems; for ( ULONG count = PropertyTable->PropertySetCount; count--; propertySet++, staticItem++ ) { // // Check to see how many new items we have. // ULONG itemCount = propertySet->PropertiesCount; const PCPROPERTY_ITEM *item2 = PropertyItems; for (ULONG count2 = PropertyItemCount; count2--; ADVANCE(item2)) { if (IsEqualGUIDAligned(*item2->Set,*propertySet->Set)) { itemCount++; } } ASSERT(itemCount >= propertySet->PropertiesCount); if (itemCount != propertySet->PropertiesCount) { // // Allocate memory required for the items table. // PKSPROPERTY_ITEM newTable2 = PKSPROPERTY_ITEM ( ExAllocatePoolWithTag ( PagedPool, sizeof(KSPROPERTY_ITEM) * itemCount, 'iScP' ) ); if (! newTable2) { ntStatus = STATUS_INSUFFICIENT_RESOURCES; break; } // // Initialize the table. // RtlZeroMemory ( PVOID(newTable2), sizeof(KSPROPERTY_ITEM) * itemCount ); if (propertySet->PropertiesCount) { RtlCopyMemory ( PVOID(newTable2), PVOID(propertySet->PropertyItem), sizeof(KSPROPERTY_ITEM) * propertySet->PropertiesCount ); } // // Create the new items. // PKSPROPERTY_ITEM addHere = newTable2 + propertySet->PropertiesCount; item2 = PropertyItems; for (count2 = PropertyItemCount; count2--; ADVANCE(item2)) { if (IsEqualGUIDAligned(*item2->Set,*propertySet->Set)) { addHere->PropertyId = item2->Id; addHere->GetPropertyHandler = ( (item2->Flags & PCPROPERTY_ITEM_FLAG_GET) ? PropertyItemPropertyHandler : NULL ); addHere->MinProperty = sizeof(KSPROPERTY); addHere->MinData = 0; addHere->SetPropertyHandler = ( (item2->Flags & PCPROPERTY_ITEM_FLAG_SET) ? PropertyItemPropertyHandler : NULL ); addHere->Values = NULL; addHere->RelationsCount = 0; addHere->Relations = ( NodeTable ? NULL : PKSPROPERTY(item2) // Secret hack! ); addHere->SupportHandler = ( (item2->Flags & PCPROPERTY_ITEM_FLAG_BASICSUPPORT) ? PropertyItemPropertyHandler : NULL ); addHere->SerializedSize = ( (item2->Flags & PCPROPERTY_ITEM_FLAG_SERIALIZE) ? ULONG(-1) : 0 ); addHere++; } } ASSERT(addHere == newTable2 + itemCount); // // Free old allocated table. // if (propertySet->PropertyItem && ! *staticItem) { ExFreePool(PVOID(propertySet->PropertyItem)); } // // Install the new tables. // propertySet->PropertiesCount = itemCount; propertySet->PropertyItem = newTable2; *staticItem = FALSE; } } } return ntStatus; } /***************************************************************************** * GenerateFormatFromRange() ***************************************************************************** * Determine the particular format, based on the intersection of these * two specific data ranges. First ask the miniport, then fall back to * our own algorithm if the miniport doesn't handle it. */ NTSTATUS GenerateFormatFromRange ( IN PIRP Irp, IN ULONG PinId, IN PKSDATARANGE MyDataRange, IN PKSDATARANGE ClientDataRange, IN ULONG OutputBufferLength, OUT PVOID ResultantFormat OPTIONAL, OUT PULONG ResultantFormatLength) { BOOLEAN bSpecifier; NTSTATUS Status; ULONG RequiredSize; PPROPERTY_CONTEXT pPropertyContext = PPROPERTY_CONTEXT(Irp->Tail.Overlay.DriverContext[3]); ASSERT(pPropertyContext); PSUBDEVICE pSubdevice = pPropertyContext->pSubdevice; ASSERT(pSubdevice); // // Give the miniport a chance to fill in the format structure // Status = pSubdevice->DataRangeIntersection (PinId, ClientDataRange, MyDataRange, OutputBufferLength, ResultantFormat, ResultantFormatLength); // // return if the miniport filled out the structure. // if (Status != STATUS_NOT_IMPLEMENTED) { return Status; } // // In case the miniport didn't implement the DataRangeIntersection, // we provide a default handler here. // // // Check if there is a wildcard somewhere. // if (IsEqualGUIDAligned (ClientDataRange->MajorFormat, KSDATAFORMAT_TYPE_WILDCARD) || IsEqualGUIDAligned (ClientDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD) || IsEqualGUIDAligned (ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WILDCARD)) { // If the miniport exposed a WILDCARD, then it must implement an // intersection handler, because it must provide the specific // ideal matching data range for that WILDCARD. // return STATUS_NO_MATCH; } if (!IsEqualGUIDAligned (ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_NONE)) { // // The miniport did not resolve this format. Let's handle the specifiers // that we know. // if (!IsEqualGUIDAligned (ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND) && !IsEqualGUIDAligned (ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) { return STATUS_NO_MATCH; } bSpecifier = TRUE; // // The specifier here defines the format of ClientDataRange // and the format expected to be returned in ResultantFormat. // if (IsEqualGUIDAligned (ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND)) { RequiredSize = sizeof (KSDATAFORMAT_DSOUND); } else { RequiredSize = sizeof (KSDATAFORMAT_WAVEFORMATEX); } } else { bSpecifier = FALSE; RequiredSize = sizeof (KSDATAFORMAT); } // // Validate return buffer size, if the request is only for the // size of the resultant structure, return it now. // if (!OutputBufferLength) { *ResultantFormatLength = RequiredSize; return STATUS_BUFFER_OVERFLOW; } else if (OutputBufferLength < RequiredSize) { return STATUS_BUFFER_TOO_SMALL; } // There was a specifier ... if (bSpecifier) { PKSDATARANGE_AUDIO myAudioRange,clientAudioRange; PKSDATAFORMAT resultantFormat; PWAVEFORMATEX resultantWaveFormatEx; myAudioRange = (PKSDATARANGE_AUDIO) MyDataRange; clientAudioRange = (PKSDATARANGE_AUDIO) ClientDataRange; resultantFormat = (PKSDATAFORMAT)ResultantFormat; // // Fill out the dataformat and other general fields. // *resultantFormat = *ClientDataRange; resultantFormat->FormatSize = RequiredSize; *ResultantFormatLength = RequiredSize; // // Fill out the DSOUND specific structure // if (IsEqualGUIDAligned (ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND)) { PKSDATAFORMAT_DSOUND resultantDSoundFormat; resultantDSoundFormat = (PKSDATAFORMAT_DSOUND)ResultantFormat; _DbgPrintF (DEBUGLVL_VERBOSE, ("returning KSDATAFORMAT_DSOUND format intersection")); // // DSound format capabilities are not expressed // this way in KS, so we express no capabilities. // resultantDSoundFormat->BufferDesc.Flags = 0 ; resultantDSoundFormat->BufferDesc.Control = 0 ; resultantWaveFormatEx = &resultantDSoundFormat->BufferDesc.WaveFormatEx; } else { _DbgPrintF (DEBUGLVL_VERBOSE, ("returning KSDATAFORMAT_WAVEFORMATEX format intersection")); resultantWaveFormatEx = (PWAVEFORMATEX)((PKSDATAFORMAT)ResultantFormat + 1); } // // Return a format that intersects the given audio range, // using our maximum support as the "best" format. // resultantWaveFormatEx->wFormatTag = WAVE_FORMAT_PCM; resultantWaveFormatEx->nChannels = (USHORT) min ( myAudioRange->MaximumChannels,clientAudioRange->MaximumChannels); resultantWaveFormatEx->nSamplesPerSec = min ( myAudioRange->MaximumSampleFrequency,clientAudioRange->MaximumSampleFrequency); resultantWaveFormatEx->wBitsPerSample = (USHORT) min ( myAudioRange->MaximumBitsPerSample,clientAudioRange->MaximumBitsPerSample); resultantWaveFormatEx->nBlockAlign = (resultantWaveFormatEx->wBitsPerSample * resultantWaveFormatEx->nChannels) / 8; resultantWaveFormatEx->nAvgBytesPerSec = (resultantWaveFormatEx->nSamplesPerSec * resultantWaveFormatEx->nBlockAlign); resultantWaveFormatEx->cbSize = 0; ((PKSDATAFORMAT) ResultantFormat)->SampleSize = resultantWaveFormatEx->nBlockAlign; _DbgPrintF (DEBUGLVL_VERBOSE, ("Channels = %d", resultantWaveFormatEx->nChannels)); _DbgPrintF (DEBUGLVL_VERBOSE, ("Samples/sec = %d", resultantWaveFormatEx->nSamplesPerSec)); _DbgPrintF (DEBUGLVL_VERBOSE, ("Bits/sample = %d", resultantWaveFormatEx->wBitsPerSample)); } else { // There was no specifier. Return only the KSDATAFORMAT structure. // // Copy the data format structure. // _DbgPrintF (DEBUGLVL_VERBOSE, ("returning default format intersection")); RtlCopyMemory (ResultantFormat, ClientDataRange, sizeof (KSDATAFORMAT)); *ResultantFormatLength = sizeof (KSDATAFORMAT); } return STATUS_SUCCESS; } /***************************************************************************** * ValidateTypeAndSpecifier() ***************************************************************************** * Find the data range that best matches the client's data range, given our * entire list of data ranges. This might include wildcard-based ranges. * */ NTSTATUS ValidateTypeAndSpecifier( IN PIRP Irp, IN ULONG PinId, IN PKSDATARANGE ClientDataRange, IN ULONG MyDataRangesCount, IN const PKSDATARANGE * MyDataRanges, IN ULONG OutputBufferLength, OUT PVOID ResultantFormat, OUT PULONG ResultantFormatLength ) { NTSTATUS ntStatus; PKSDATARANGE aClientDataRange; // // Check the size of the structure. // if (ClientDataRange->FormatSize < sizeof (KSDATARANGE)) { _DbgPrintF (DEBUGLVL_TERSE, ("Validating ClientDataRange: size < KSDATARANGE!")); return STATUS_INVALID_PARAMETER; } // // We default to no match. // ntStatus = STATUS_NO_MATCH; // // Go through all the dataranges in the validate list until we get a SUCCESS. // for (; MyDataRangesCount--; MyDataRanges++) { PKSDATARANGE myDataRange = *MyDataRanges; // // Verify that the correct major format, subformat and specifier (or wildcards) // are in the intersection. // if ((!IsEqualGUIDAligned(ClientDataRange->MajorFormat,myDataRange->MajorFormat) && !IsEqualGUIDAligned(ClientDataRange->MajorFormat,KSDATAFORMAT_TYPE_WILDCARD)) || (!IsEqualGUIDAligned(ClientDataRange->SubFormat,myDataRange->SubFormat) && !IsEqualGUIDAligned(ClientDataRange->SubFormat,KSDATAFORMAT_SUBTYPE_WILDCARD)) || (!IsEqualGUIDAligned(ClientDataRange->Specifier,myDataRange->Specifier) && !IsEqualGUIDAligned(ClientDataRange->Specifier,KSDATAFORMAT_SPECIFIER_WILDCARD))) { continue; // no match and no WILDCARD, try the next one } // // if not WILDCARD, then we ask the miniport to match this one exactly, // else we manufacture a range and ask the miniport for a match from that. // aClientDataRange = ClientDataRange; // assume no WILDCARD for now, we ask the miniport to match this // // Handle the wildcards. // if (IsEqualGUIDAligned (ClientDataRange->MajorFormat,KSDATAFORMAT_TYPE_WILDCARD) || IsEqualGUIDAligned (ClientDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD) || IsEqualGUIDAligned (ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WILDCARD)) { // // We pass a faked datarange for the specifiers we know or we pass to the // miniport it's own datarange. // // We know the specifiers waveformatex and dsound. // if (IsEqualGUIDAligned (myDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) || IsEqualGUIDAligned (myDataRange->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND)) { KSDATARANGE_AUDIO dr; // // Take the complete datarange from the driver. // dr.DataRange = *myDataRange; // // Fill in a HUGE data range (it asked for WILDCARD, after all!) // dr.MaximumChannels = 0x1FFF0; dr.MinimumBitsPerSample = 1; dr.MaximumBitsPerSample = 0x1FFF0; dr.MinimumSampleFrequency = 1; dr.MaximumSampleFrequency = 0x1FFFFFF0; aClientDataRange = (PKSDATARANGE)&dr; } else { // // We don't know this non-wave format (in the list of formats we supposedly support). // The miniport specified this format, so it is OK to pass it down. // aClientDataRange = myDataRange; } } // // Make sure KSDATARANGE_AUDIO is used, then see if there is a possible match. // if (IsEqualGUIDAligned (aClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) || IsEqualGUIDAligned (aClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND)) { if (aClientDataRange->FormatSize < sizeof (KSDATARANGE_AUDIO)) { // // The datarange structure passed has no KSDATARANGE_AUDIO. // _DbgPrintF (DEBUGLVL_TERSE, ("Validating PCM ValidDataRange: size < KSDATARANGE_AUDIO!")); continue; // not large enough, try the next one } // // Verify that we have an intersection with the specified format and // our audio format dictated by our specific requirements. // _DbgPrintF (DEBUGLVL_VERBOSE, ("validating KSDATARANGE_AUDIO")); if ((((PKSDATARANGE_AUDIO)aClientDataRange)->MinimumSampleFrequency > ((PKSDATARANGE_AUDIO)myDataRange)->MaximumSampleFrequency) || (((PKSDATARANGE_AUDIO)aClientDataRange)->MaximumSampleFrequency < ((PKSDATARANGE_AUDIO)myDataRange)->MinimumSampleFrequency) || (((PKSDATARANGE_AUDIO)aClientDataRange)->MinimumBitsPerSample > ((PKSDATARANGE_AUDIO)myDataRange)->MaximumBitsPerSample) || (((PKSDATARANGE_AUDIO)aClientDataRange)->MaximumBitsPerSample < ((PKSDATARANGE_AUDIO)myDataRange)->MinimumBitsPerSample)) { continue; } } ntStatus = GenerateFormatFromRange (Irp, PinId, myDataRange, aClientDataRange, OutputBufferLength, ResultantFormat, ResultantFormatLength); if ( NT_SUCCESS(ntStatus) || (ntStatus == STATUS_BUFFER_OVERFLOW) || (ntStatus == STATUS_BUFFER_TOO_SMALL)) { break; // We found a good one, or we failed. // Either way we must leave. } } return ntStatus; } /***************************************************************************** * PinIntersectHandler() ***************************************************************************** * This function is a data range callback for use with * PropertyHandler_PinIntersection */ NTSTATUS PinIntersectHandler ( IN PIRP Irp, IN PKSP_PIN Pin, IN PKSDATARANGE DataRange, OUT PVOID Data ) { NTSTATUS Status; ULONG OutputBufferLength; PAGED_CODE(); ASSERT(Irp); ASSERT(Pin); ASSERT(DataRange); PPROPERTY_CONTEXT pPropertyContext = PPROPERTY_CONTEXT(Irp->Tail.Overlay.DriverContext[3]); ASSERT(pPropertyContext); PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor = pPropertyContext->pSubdeviceDescriptor; ASSERT(pSubdeviceDescriptor); ASSERT(pSubdeviceDescriptor->PinDescriptors); ASSERT(Pin->PinId < pSubdeviceDescriptor->PinCount); PKSPIN_DESCRIPTOR pinDescriptor = &pSubdeviceDescriptor->PinDescriptors[Pin->PinId]; ASSERT(pinDescriptor); _DbgPrintF(DEBUGLVL_VERBOSE,("[PinIntersectHandler]")); OutputBufferLength = IoGetCurrentIrpStackLocation( Irp )-> Parameters.DeviceIoControl.OutputBufferLength; Status = ValidateTypeAndSpecifier( Irp, Pin->PinId, DataRange, pinDescriptor->DataRangesCount, pinDescriptor->DataRanges, OutputBufferLength, Data, PULONG(&Irp->IoStatus.Information) ); if (!NT_SUCCESS( Status )) { _DbgPrintF( DEBUGLVL_VERBOSE, ("ValidateTypeAndSpecifier() returned %08x", Status) ); } return Status; } /***************************************************************************** * PinPhysicalConnection() ***************************************************************************** * Handles physical connection property access for the pin. */ static NTSTATUS PinPhysicalConnection ( IN PIRP Irp, IN PKSP_PIN Pin, OUT PVOID Data ) { PAGED_CODE(); ASSERT(Irp); ASSERT(Pin); PPROPERTY_CONTEXT pPropertyContext = PPROPERTY_CONTEXT(Irp->Tail.Overlay.DriverContext[3]); ASSERT(pPropertyContext); PSUBDEVICE Subdevice = pPropertyContext->pSubdevice; ASSERT(Subdevice); PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp); ASSERT(irpSp); ASSERT(irpSp->DeviceObject); PDEVICE_CONTEXT deviceContext = PDEVICE_CONTEXT(irpSp->DeviceObject->DeviceExtension); NTSTATUS ntStatus = STATUS_NOT_FOUND; ULONG outPin; PUNICODE_STRING outUnicodeString = NULL; for ( PLIST_ENTRY entry = deviceContext->PhysicalConnectionList.Flink; entry != &deviceContext->PhysicalConnectionList; entry = entry->Flink ) { PPHYSICALCONNECTION connection = PPHYSICALCONNECTION(entry); if ( (connection->FromSubdevice == Subdevice) && (connection->FromPin == Pin->PinId) ) { outPin = connection->ToPin; if (connection->ToString) { outUnicodeString = connection->ToString; } else { ULONG ulIndex = SubdeviceIndex(irpSp->DeviceObject,connection->ToSubdevice); if (ulIndex != ULONG(-1)) { ntStatus = STATUS_SUCCESS; outUnicodeString = &deviceContext->SymbolicLinkNames[ulIndex]; } } break; } else if ( (connection->ToSubdevice == Subdevice) && (connection->ToPin == Pin->PinId) ) { outPin = connection->FromPin; if (connection->FromString) { outUnicodeString = connection->FromString; } else { ULONG ulIndex = SubdeviceIndex(irpSp->DeviceObject,connection->FromSubdevice); if (ulIndex != ULONG(-1)) { ntStatus = STATUS_SUCCESS; outUnicodeString = &deviceContext->SymbolicLinkNames[ulIndex]; } } break; } } if (!outUnicodeString) { ntStatus = STATUS_NOT_FOUND; } if (NT_SUCCESS(ntStatus)) { ULONG outSize; outSize = FIELD_OFFSET(KSPIN_PHYSICALCONNECTION,SymbolicLinkName[0]); outSize += (outUnicodeString->Length + sizeof(UNICODE_NULL)); // // Validate return buffer size. // ULONG outputBufferLength = IoGetCurrentIrpStackLocation(Irp)-> Parameters.DeviceIoControl.OutputBufferLength; if (!outputBufferLength) { Irp->IoStatus.Information = outSize; ntStatus = STATUS_BUFFER_OVERFLOW; } else if (outputBufferLength < outSize) { ntStatus = STATUS_BUFFER_TOO_SMALL; } else { PKSPIN_PHYSICALCONNECTION out = PKSPIN_PHYSICALCONNECTION(Data); out->Size = outSize; out->Pin = outPin; RtlCopyMemory ( out->SymbolicLinkName, outUnicodeString->Buffer, outUnicodeString->Length ); out->SymbolicLinkName[outUnicodeString->Length / sizeof(WCHAR)] = 0; Irp->IoStatus.Information = outSize; } } return ntStatus; } /***************************************************************************** * PinCountHandler() ***************************************************************************** * Handles pin count access for the pin. */ void PinCountHandler ( IN PPROPERTY_CONTEXT pPropertyContext, IN ULONG pinId ) { PAGED_CODE(); ASSERT(pPropertyContext); PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor = pPropertyContext->pSubdeviceDescriptor; ASSERT(pSubdeviceDescriptor); PSUBDEVICE Subdevice = pPropertyContext->pSubdevice; ASSERT(Subdevice); Subdevice->PinCount( pinId, &(pSubdeviceDescriptor->PinInstances[pinId].FilterNecessary), &(pPropertyContext->pulPinInstanceCounts[pinId]), &(pSubdeviceDescriptor->PinInstances[pinId].FilterPossible), &(pSubdeviceDescriptor->PinInstances[pinId].GlobalCurrent), &(pSubdeviceDescriptor->PinInstances[pinId].GlobalPossible) ); } /***************************************************************************** * PcPinPropertyHandler() ***************************************************************************** * Property handler for pin properties on the filter. */ PORTCLASSAPI NTSTATUS NTAPI PcPinPropertyHandler ( IN PIRP pIrp, IN PKSP_PIN pKsPPin, IN OUT PVOID pvData ) { PAGED_CODE(); ASSERT(pIrp); ASSERT(pKsPPin); PPROPERTY_CONTEXT pPropertyContext = PPROPERTY_CONTEXT(pIrp->Tail.Overlay.DriverContext[3]); ASSERT(pPropertyContext); PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor = pPropertyContext->pSubdeviceDescriptor; ASSERT(pSubdeviceDescriptor); NTSTATUS ntStatus = STATUS_UNSUCCESSFUL; if ( (pKsPPin->Property.Id != KSPROPERTY_PIN_CTYPES) && (pKsPPin->PinId >= pSubdeviceDescriptor->PinCount) ) { ntStatus = STATUS_INVALID_PARAMETER; } else { switch (pKsPPin->Property.Id) { case KSPROPERTY_PIN_CTYPES: case KSPROPERTY_PIN_DATAFLOW: case KSPROPERTY_PIN_DATARANGES: case KSPROPERTY_PIN_INTERFACES: case KSPROPERTY_PIN_MEDIUMS: case KSPROPERTY_PIN_COMMUNICATION: case KSPROPERTY_PIN_CATEGORY: case KSPROPERTY_PIN_NAME: ntStatus = KsPinPropertyHandler ( pIrp, PKSPROPERTY(pKsPPin), pvData, pSubdeviceDescriptor->PinCount, pSubdeviceDescriptor->PinDescriptors ); break; case KSPROPERTY_PIN_DATAINTERSECTION: ntStatus = KsPinDataIntersection ( pIrp, pKsPPin, pvData, pSubdeviceDescriptor->PinCount, pSubdeviceDescriptor->PinDescriptors, PinIntersectHandler ); break; case KSPROPERTY_PIN_CINSTANCES: if (pPropertyContext->pulPinInstanceCounts) { PinCountHandler(pPropertyContext,pKsPPin->PinId); PKSPIN_CINSTANCES(pvData)->PossibleCount = pSubdeviceDescriptor->PinInstances[pKsPPin->PinId].FilterPossible; PKSPIN_CINSTANCES(pvData)->CurrentCount = pPropertyContext->pulPinInstanceCounts[pKsPPin->PinId]; pIrp->IoStatus.Information = sizeof(KSPIN_CINSTANCES); ntStatus = STATUS_SUCCESS; } break; case KSPROPERTY_PIN_GLOBALCINSTANCES: if (pPropertyContext->pulPinInstanceCounts) { PinCountHandler(pPropertyContext,pKsPPin->PinId); } PKSPIN_CINSTANCES(pvData)->PossibleCount = pSubdeviceDescriptor->PinInstances[pKsPPin->PinId].GlobalPossible; PKSPIN_CINSTANCES(pvData)->CurrentCount = pSubdeviceDescriptor->PinInstances[pKsPPin->PinId].GlobalCurrent; pIrp->IoStatus.Information = sizeof(KSPIN_CINSTANCES); ntStatus = STATUS_SUCCESS; break; case KSPROPERTY_PIN_NECESSARYINSTANCES: if (pPropertyContext->pulPinInstanceCounts) { PinCountHandler(pPropertyContext,pKsPPin->PinId); *PULONG(pvData) = pSubdeviceDescriptor->PinInstances[pKsPPin->PinId].FilterNecessary; pIrp->IoStatus.Information = sizeof(ULONG); ntStatus = STATUS_SUCCESS; } break; case KSPROPERTY_PIN_PHYSICALCONNECTION: ntStatus = PinPhysicalConnection ( pIrp, pKsPPin, pvData ); break; default: ntStatus = STATUS_NOT_FOUND; break; } } return ntStatus; }