/***************************************************************************** * porthelp.cpp - WDM Streaming port class driver port helper functions ***************************************************************************** * Copyright (c) 1996-2000 Microsoft Corporation. All rights reserved. */ #include "private.h" /***************************************************************************** * Functions. */ #pragma code_seg("PAGE") static KSPIN_MEDIUM PinMediums[] = { { STATICGUIDOF(KSMEDIUMSETID_Standard), KSMEDIUM_STANDARD_DEVIO, 0 } }; #define UPTOQUAD(x) (((x)+7)&~7) /***************************************************************************** * PrivateHeap ***************************************************************************** * Class for managing a private heap. */ class PrivateHeap { private: PBYTE m_pbTop; PBYTE m_pbCurrent; ULONG m_ulSize; public: PrivateHeap(void) : m_pbTop(NULL), m_pbCurrent(NULL), m_ulSize(NULL) { } // // Increase the number of bytes that will be allocated for the heap. // ULONG Reserve(ULONG ulBytes) { ASSERT(! m_pbTop); ASSERT(! m_pbCurrent); m_ulSize += UPTOQUAD(ulBytes); return m_ulSize; } // // Allocate memory for the private heap from a pool. // NTSTATUS AllocateFromPool(POOL_TYPE poolType,ULONG ulTag) { ASSERT(! m_pbTop); ASSERT(! m_pbCurrent); ASSERT(m_ulSize); m_pbTop = new(poolType,ulTag) BYTE[m_ulSize]; if (! m_pbTop) { return STATUS_INSUFFICIENT_RESOURCES; } m_pbCurrent = m_pbTop; return STATUS_SUCCESS; } // // Allocate memory from the heap. // PVOID Alloc(ULONG ulSize) { ASSERT(ulSize); ASSERT(m_pbTop); ASSERT(m_pbCurrent); ASSERT(m_pbCurrent + UPTOQUAD(ulSize) <= m_pbTop + m_ulSize); PVOID pvResult = PVOID(m_pbCurrent); m_pbCurrent += UPTOQUAD(ulSize); return pvResult; } // // Determine the amount of space remaining in the heap. // ULONG BytesRemaining(void) { ASSERT(m_pbTop); ASSERT(m_pbCurrent); ASSERT(m_pbCurrent <= m_pbTop + m_ulSize); return ULONG((m_pbTop + m_ulSize) - m_pbCurrent); } }; /***************************************************************************** * ::new() ***************************************************************************** * New function for creating objects with private heap. */ inline PVOID operator new ( size_t iSize, PrivateHeap& privateHeap ) { return privateHeap.Alloc(ULONG(iSize)); } /***************************************************************************** * MeasureDataRanges() ***************************************************************************** * Determine how much a set of data ranges will expand as a result * of cloning WAVEFORMATEX ranges into identical DSOUND ranges. * * As of WinME, we also clone non-PCM ranges. */ static ULONG MeasureDataRanges ( IN PrivateHeap * pPrivateHeap OPTIONAL, IN ULONG ulDataRangeCountIn, IN KSDATARANGE *const * ppKsDataRangeIn ) { ULONG ulNewDataRangeCount = ulDataRangeCountIn; for (ULONG ul = ulDataRangeCountIn; ul--; ) { ASSERT(ppKsDataRangeIn); if ( ( (*ppKsDataRangeIn)->FormatSize >= sizeof(KSDATAFORMAT_WAVEFORMATEX) ) && IsEqualGUIDAligned ( (*ppKsDataRangeIn)->MajorFormat, KSDATAFORMAT_TYPE_AUDIO ) && IsEqualGUIDAligned ( (*ppKsDataRangeIn)->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX ) ) { ulNewDataRangeCount++; if (pPrivateHeap) { pPrivateHeap->Reserve((*ppKsDataRangeIn)->FormatSize); } } ppKsDataRangeIn++; } if (pPrivateHeap && (ulNewDataRangeCount != ulDataRangeCountIn)) { pPrivateHeap->Reserve(ulNewDataRangeCount * sizeof(PKSDATARANGE)); } return ulNewDataRangeCount; } /***************************************************************************** * CloneDataRanges() ***************************************************************************** * Expand data ranges to include DSound formats. */ static const PKSDATARANGE * CloneDataRanges ( IN PrivateHeap& privateHeap, OUT PULONG pulDataRangeCountOut, IN ULONG ulDataRangeCountIn, IN KSDATARANGE *const * ppKsDataRangeIn ) { ASSERT(pulDataRangeCountOut); // // Determine how many data ranges there will be and how much space will be // required for the new ones. // ULONG ulDataRangeCountOut = MeasureDataRanges(NULL,ulDataRangeCountIn,ppKsDataRangeIn); const PKSDATARANGE *ppKsDataRangeOut; if (ulDataRangeCountOut == ulDataRangeCountIn) { // // No new data ranges. Use the array we were given. // ppKsDataRangeOut = ppKsDataRangeIn; } else { // // Allocate some space for the new array. // ppKsDataRangeOut = new(privateHeap) PKSDATARANGE[ulDataRangeCountOut]; // // Build the new array. // PKSDATARANGE *ppKsDataRange = (PKSDATARANGE *) ppKsDataRangeOut; while (ulDataRangeCountIn--) { ASSERT(ppKsDataRangeIn); // // All the data ranges get copied. // *ppKsDataRange++ = *ppKsDataRangeIn; // // Check for WaveFormatEx datarange // This includes non-PCM subformats.... // if ( ( (*ppKsDataRangeIn)->FormatSize >= sizeof(KSDATAFORMAT_WAVEFORMATEX) ) && IsEqualGUIDAligned ( (*ppKsDataRangeIn)->MajorFormat, KSDATAFORMAT_TYPE_AUDIO ) && IsEqualGUIDAligned ( (*ppKsDataRangeIn)->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX ) ) { // // WaveFormatEx datarange will require DSound clone. // Allocate memory for it and copy. // *ppKsDataRange = PKSDATARANGE ( new(privateHeap) BYTE[(*ppKsDataRangeIn)->FormatSize] ); RtlCopyMemory ( *ppKsDataRange, *ppKsDataRangeIn, (*ppKsDataRangeIn)->FormatSize ); // // Update the specifier. // (*ppKsDataRange++)->Specifier = KSDATAFORMAT_SPECIFIER_DSOUND; } // // Increment input position // ppKsDataRangeIn++; } } *pulDataRangeCountOut = ulDataRangeCountOut; return ppKsDataRangeOut; } /***************************************************************************** * PcCreateSubdeviceDescriptor() ***************************************************************************** * Creates a subdevice descriptor. */ PORTCLASSAPI NTSTATUS NTAPI PcCreateSubdeviceDescriptor ( IN PPCFILTER_DESCRIPTOR pPcFilterDescriptor, IN ULONG CategoriesCount, IN GUID * Categories, IN ULONG StreamInterfacesCount, IN PKSPIN_INTERFACE StreamInterfaces, IN ULONG FilterPropertySetCount, IN PKSPROPERTY_SET FilterPropertySets, IN ULONG FilterEventSetCount, IN PKSEVENT_SET FilterEventSets, IN ULONG PinPropertySetCount, IN PKSPROPERTY_SET PinPropertySets, IN ULONG PinEventSetCount, IN PKSEVENT_SET PinEventSets, OUT PSUBDEVICE_DESCRIPTOR * OutDescriptor ) { PAGED_CODE(); ASSERT(pPcFilterDescriptor); ASSERT(OutDescriptor); NTSTATUS ntStatus = STATUS_SUCCESS; // // Calculate how much memory we will need. // PrivateHeap privateHeap; privateHeap.Reserve(sizeof(SUBDEVICE_DESCRIPTOR)); privateHeap.Reserve(sizeof(KSTOPOLOGY)); privateHeap.Reserve(sizeof(KSPIN_DESCRIPTOR) * pPcFilterDescriptor->PinCount); privateHeap.Reserve(sizeof(PIN_CINSTANCES) * pPcFilterDescriptor->PinCount); privateHeap.Reserve(sizeof(PROPERTY_TABLE) * pPcFilterDescriptor->PinCount); privateHeap.Reserve(sizeof(EVENT_TABLE) * pPcFilterDescriptor->PinCount); if (pPcFilterDescriptor->NodeCount) { privateHeap.Reserve(sizeof(GUID) * pPcFilterDescriptor->NodeCount); privateHeap.Reserve(sizeof(GUID) * pPcFilterDescriptor->NodeCount); } const PCPIN_DESCRIPTOR *pPcPinDescriptor = pPcFilterDescriptor->Pins; for (ULONG ul = pPcFilterDescriptor->PinCount; ul--; ) { if (pPcPinDescriptor->KsPinDescriptor.DataRanges) { MeasureDataRanges( &privateHeap, pPcPinDescriptor->KsPinDescriptor.DataRangesCount, pPcPinDescriptor->KsPinDescriptor.DataRanges ); pPcPinDescriptor = PPCPIN_DESCRIPTOR( PBYTE(pPcPinDescriptor) + pPcFilterDescriptor->PinSize ); } else { ntStatus = STATUS_RANGE_NOT_FOUND; // DataRanges field is NULL break; // Don't even bother, just exit } } if (NT_SUCCESS(ntStatus)) // if fail above, fall through the rest { // // Allocate the required memory. // ntStatus = privateHeap.AllocateFromPool(PagedPool,'pFcP'); } if (NT_SUCCESS(ntStatus)) { PSUBDEVICE_DESCRIPTOR descr = new(privateHeap) SUBDEVICE_DESCRIPTOR; // // Set up pointers into one big chunk of memory. // descr->PinCount = pPcFilterDescriptor->PinCount; descr->Topology = new(privateHeap) KSTOPOLOGY; descr->PinDescriptors = new(privateHeap) KSPIN_DESCRIPTOR[descr->PinCount]; descr->PinInstances = new(privateHeap) PIN_CINSTANCES[descr->PinCount]; descr->PinPropertyTables = new(privateHeap) PROPERTY_TABLE[descr->PinCount]; descr->PinEventTables = new(privateHeap) EVENT_TABLE[descr->PinCount]; if (pPcFilterDescriptor->NodeCount) { descr->Topology->TopologyNodes = new(privateHeap) GUID[pPcFilterDescriptor->NodeCount]; descr->Topology->TopologyNodesNames = new(privateHeap) GUID[pPcFilterDescriptor->NodeCount]; } else { descr->Topology->TopologyNodes = NULL; descr->Topology->TopologyNodesNames = NULL; } // // Prefer the categories from the filter descriptor. // if (pPcFilterDescriptor->CategoryCount != 0) { descr->Topology->CategoriesCount = pPcFilterDescriptor->CategoryCount; descr->Topology->Categories = pPcFilterDescriptor->Categories; } else { descr->Topology->CategoriesCount = CategoriesCount; descr->Topology->Categories = Categories; } descr->Topology->TopologyNodesCount = pPcFilterDescriptor->NodeCount; descr->Topology->TopologyConnectionsCount = pPcFilterDescriptor->ConnectionCount; descr->Topology->TopologyConnections = pPcFilterDescriptor->Connections; // // Initialize filter properties. // descr->FilterPropertyTable.PropertySetCount = FilterPropertySetCount; descr->FilterPropertyTable.PropertySets = FilterPropertySets; descr->FilterPropertyTable.StaticSets = TRUE; // // Initialize filter events. // descr->FilterEventTable.EventSetCount = FilterEventSetCount; descr->FilterEventTable.EventSets = FilterEventSets; descr->FilterEventTable.StaticSets = TRUE; // // Copy node type and name and merge node properties and events. // const PCNODE_DESCRIPTOR *pPcNodeDescriptor = pPcFilterDescriptor->Nodes; GUID *pGuidType = (GUID *) descr->Topology->TopologyNodes; GUID *pGuidName = (GUID *) descr->Topology->TopologyNodesNames; for (ULONG node = pPcFilterDescriptor->NodeCount; node--; ) { *pGuidType++ = *pPcNodeDescriptor->Type; if (pPcNodeDescriptor->Name) { *pGuidName++ = *pPcNodeDescriptor->Name; } else { *pGuidName++ = *pPcNodeDescriptor->Type; } if ( (pPcNodeDescriptor->AutomationTable) && (pPcNodeDescriptor->AutomationTable->PropertyCount) ) { PcAddToPropertyTable ( &descr->FilterPropertyTable, pPcNodeDescriptor->AutomationTable->PropertyCount, pPcNodeDescriptor->AutomationTable->Properties, pPcNodeDescriptor->AutomationTable->PropertyItemSize, TRUE ); } if ( (pPcNodeDescriptor->AutomationTable) && (pPcNodeDescriptor->AutomationTable->EventCount) ) { PcAddToEventTable ( &descr->FilterEventTable, pPcNodeDescriptor->AutomationTable->EventCount, pPcNodeDescriptor->AutomationTable->Events, pPcNodeDescriptor->AutomationTable->EventItemSize, TRUE ); } pPcNodeDescriptor = PPCNODE_DESCRIPTOR ( PBYTE(pPcNodeDescriptor) + pPcFilterDescriptor->NodeSize ); } // // Merge filter properties. // if ( (pPcFilterDescriptor->AutomationTable) && (pPcFilterDescriptor->AutomationTable->PropertyCount) ) { PcAddToPropertyTable ( &descr->FilterPropertyTable, pPcFilterDescriptor->AutomationTable->PropertyCount, pPcFilterDescriptor->AutomationTable->Properties, pPcFilterDescriptor->AutomationTable->PropertyItemSize, FALSE ); } // // Merge filter events. // if ( (pPcFilterDescriptor->AutomationTable) && (pPcFilterDescriptor->AutomationTable->EventCount) ) { PcAddToEventTable ( &descr->FilterEventTable, pPcFilterDescriptor->AutomationTable->EventCount, pPcFilterDescriptor->AutomationTable->Events, pPcFilterDescriptor->AutomationTable->EventItemSize, FALSE ); } // // Do per-pin stuff. // PPROPERTY_TABLE pt = descr->PinPropertyTables; PEVENT_TABLE et = descr->PinEventTables; PKSPIN_DESCRIPTOR p = descr->PinDescriptors; PPIN_CINSTANCES i = descr->PinInstances; pPcPinDescriptor = PPCPIN_DESCRIPTOR(pPcFilterDescriptor->Pins); for ( ULONG pin = 0; pin < pPcFilterDescriptor->PinCount; pin++ ) { // // Find a pin that has the same property set. // PPROPERTY_TABLE twinPt = descr->PinPropertyTables; PPCPIN_DESCRIPTOR pPcPinDescriptorTwin = PPCPIN_DESCRIPTOR(pPcFilterDescriptor->Pins); for ( ULONG twinPin = 0; twinPin < pin; twinPin++, twinPt++ ) { if ( ( pPcPinDescriptor->AutomationTable == pPcPinDescriptorTwin->AutomationTable ) || ( pPcPinDescriptor->AutomationTable && pPcPinDescriptorTwin->AutomationTable && ( pPcPinDescriptor->AutomationTable->PropertyCount == pPcPinDescriptorTwin->AutomationTable->PropertyCount ) && ( pPcPinDescriptor->AutomationTable->Properties == pPcPinDescriptorTwin->AutomationTable->Properties ) && ( pPcPinDescriptor->AutomationTable->PropertyItemSize == pPcPinDescriptorTwin->AutomationTable->PropertyItemSize ) ) ) { *pt = *twinPt; break; } pPcPinDescriptorTwin = PPCPIN_DESCRIPTOR ( PBYTE(pPcPinDescriptorTwin) + pPcFilterDescriptor->PinSize ); } // // Create a new table if we have to. // if (twinPin == pin) { pt->PropertySetCount = PinPropertySetCount; pt->PropertySets = PinPropertySets; pt->StaticSets = TRUE; if ( (pPcPinDescriptor->AutomationTable) && (pPcPinDescriptor->AutomationTable->PropertyCount) ) { PcAddToPropertyTable ( pt, pPcPinDescriptor->AutomationTable->PropertyCount, pPcPinDescriptor->AutomationTable->Properties, pPcPinDescriptor->AutomationTable->PropertyItemSize, FALSE ); } const PCNODE_DESCRIPTOR *pPcNodeDescriptor2 = pPcFilterDescriptor->Nodes; for (ULONG node = pPcFilterDescriptor->NodeCount; node--; ) { if ( (pPcNodeDescriptor2->AutomationTable) && (pPcNodeDescriptor2->AutomationTable->PropertyCount) ) { PcAddToPropertyTable ( pt, pPcNodeDescriptor2->AutomationTable->PropertyCount, pPcNodeDescriptor2->AutomationTable->Properties, pPcNodeDescriptor2->AutomationTable->PropertyItemSize, TRUE ); } pPcNodeDescriptor2 = PPCNODE_DESCRIPTOR ( PBYTE(pPcNodeDescriptor2) + pPcFilterDescriptor->NodeSize ); } } pt++; // // Find a pin that has the same event set. // PEVENT_TABLE twinEt = descr->PinEventTables; pPcPinDescriptorTwin = PPCPIN_DESCRIPTOR(pPcFilterDescriptor->Pins); for ( ULONG twinEPin = 0; twinEPin < pin; twinEPin++, twinEt++ ) { if ( ( pPcPinDescriptor->AutomationTable == pPcPinDescriptorTwin->AutomationTable ) || ( pPcPinDescriptor->AutomationTable && pPcPinDescriptorTwin->AutomationTable && ( pPcPinDescriptor->AutomationTable->EventCount == pPcPinDescriptorTwin->AutomationTable->EventCount ) && ( pPcPinDescriptor->AutomationTable->Events == pPcPinDescriptorTwin->AutomationTable->Events ) && ( pPcPinDescriptor->AutomationTable->EventItemSize == pPcPinDescriptorTwin->AutomationTable->EventItemSize ) ) ) { *et = *twinEt; break; } pPcPinDescriptorTwin = PPCPIN_DESCRIPTOR ( PBYTE(pPcPinDescriptorTwin) + pPcFilterDescriptor->PinSize ); } // // Create a new table if we have to. // if (twinEPin == pin) { et->EventSetCount = PinEventSetCount; et->EventSets = PinEventSets; et->StaticSets = TRUE; if ( (pPcPinDescriptor->AutomationTable) && (pPcPinDescriptor->AutomationTable->EventCount) ) { PcAddToEventTable ( et, pPcPinDescriptor->AutomationTable->EventCount, pPcPinDescriptor->AutomationTable->Events, pPcPinDescriptor->AutomationTable->EventItemSize, FALSE ); } const PCNODE_DESCRIPTOR *pPcNodeDescriptor2 = pPcFilterDescriptor->Nodes; for( ULONG node = pPcFilterDescriptor->NodeCount; node--; ) { if ( (pPcNodeDescriptor2->AutomationTable) && (pPcNodeDescriptor2->AutomationTable->EventCount) ) { PcAddToEventTable ( et, pPcNodeDescriptor2->AutomationTable->EventCount, pPcNodeDescriptor2->AutomationTable->Events, pPcNodeDescriptor2->AutomationTable->EventItemSize, TRUE ); } pPcNodeDescriptor2 = PPCNODE_DESCRIPTOR( PBYTE(pPcNodeDescriptor2) + pPcFilterDescriptor->NodeSize ); } } et++; // // Copy the KS descriptor. // *p = pPcPinDescriptor->KsPinDescriptor; // // Provide default mediums if necessary. // if (p->Mediums == NULL) { p->MediumsCount = SIZEOF_ARRAY(PinMediums); p->Mediums = PinMediums; } // // Massage the data ranges. // p->DataRanges = CloneDataRanges ( privateHeap, &p->DataRangesCount, pPcPinDescriptor->KsPinDescriptor.DataRangesCount, pPcPinDescriptor->KsPinDescriptor.DataRanges ); // // Provide default interfaces if necessary. // if ( (p->Communication & KSPIN_COMMUNICATION_BOTH) && (p->Interfaces == NULL) ) { p->InterfacesCount = StreamInterfacesCount; p->Interfaces = StreamInterfaces; } p++; i->FilterPossible = pPcPinDescriptor->MaxFilterInstanceCount; i->FilterNecessary = pPcPinDescriptor->MinFilterInstanceCount; i->GlobalPossible = pPcPinDescriptor->MaxGlobalInstanceCount; i->GlobalCurrent = 0; i++; pPcPinDescriptor = PPCPIN_DESCRIPTOR ( PBYTE(pPcPinDescriptor) + pPcFilterDescriptor->PinSize ); } *OutDescriptor = descr; ASSERT(privateHeap.BytesRemaining() == 0); } return ntStatus; } /***************************************************************************** * PcDeleteSubdeviceDescriptor() ***************************************************************************** * Deletes a subdevice descriptor. */ PORTCLASSAPI void NTAPI PcDeleteSubdeviceDescriptor ( IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor ) { // // Free allocated memory for filter property and event tables. // PcFreePropertyTable(&pSubdeviceDescriptor->FilterPropertyTable); PcFreeEventTable(&pSubdeviceDescriptor->FilterEventTable); // // Free allocated memory for pin property tables. // PPROPERTY_TABLE pPropertyTable = pSubdeviceDescriptor->PinPropertyTables; for (ULONG ul = pSubdeviceDescriptor->PinCount; ul--; pPropertyTable++) { // // Find and clear any references to the same property set. // for ( PPROPERTY_TABLE pPropertyTableTwin = ( pSubdeviceDescriptor->PinPropertyTables + ( pSubdeviceDescriptor->PinCount - 1 ) ) ; pPropertyTableTwin != pPropertyTable ; pPropertyTableTwin-- ) { if ( pPropertyTableTwin->PropertySets == pPropertyTable->PropertySets ) { pPropertyTableTwin->PropertySetCount = 0; pPropertyTableTwin->PropertySets = NULL; pPropertyTableTwin->StaticSets = TRUE; pPropertyTableTwin->StaticItems = NULL; } } PcFreePropertyTable(pPropertyTable); } // // Free allocated memory for pin event tables. // PEVENT_TABLE pEventTable = pSubdeviceDescriptor->PinEventTables; for (ul = pSubdeviceDescriptor->PinCount; ul--; pEventTable++) { // // Find and clear any references to the same event set. // for ( PEVENT_TABLE pEventTableTwin = ( pSubdeviceDescriptor->PinEventTables + ( pSubdeviceDescriptor->PinCount - 1 ) ) ; pEventTableTwin != pEventTable ; pEventTableTwin-- ) { if ( pEventTableTwin->EventSets == pEventTable->EventSets ) { pEventTableTwin->EventSetCount = 0; pEventTableTwin->EventSets = NULL; pEventTableTwin->StaticSets = TRUE; pEventTableTwin->StaticItems = NULL; } } PcFreeEventTable(pEventTable); } // // The rest is one big chunk. // delete [] PBYTE(pSubdeviceDescriptor); } /***************************************************************************** * PcValidateConnectRequest() ***************************************************************************** * Validate attempt to create a pin. */ PORTCLASSAPI NTSTATUS NTAPI PcValidateConnectRequest ( IN PIRP pIrp , IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor , OUT PKSPIN_CONNECT * ppKsPinConnect ) { PAGED_CODE(); ASSERT(pIrp); ASSERT(pSubdeviceDescriptor); ASSERT(ppKsPinConnect); NTSTATUS ntStatus = KsValidateConnectRequest ( pIrp , pSubdeviceDescriptor->PinCount , pSubdeviceDescriptor->PinDescriptors , ppKsPinConnect ); return ntStatus; } /***************************************************************************** * PcValidatePinCount() ***************************************************************************** * Validate pin count. */ PORTCLASSAPI NTSTATUS NTAPI PcValidatePinCount ( IN ULONG ulPinId , IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor , IN PULONG pulPinInstanceCounts ) { PAGED_CODE(); ASSERT(pSubdeviceDescriptor); ASSERT(pulPinInstanceCounts); NTSTATUS ntStatus = STATUS_SUCCESS; if ( ( pSubdeviceDescriptor->PinInstances[ulPinId].GlobalCurrent < pSubdeviceDescriptor->PinInstances[ulPinId].GlobalPossible ) || ( pulPinInstanceCounts[ulPinId] < pSubdeviceDescriptor->PinInstances[ulPinId].FilterPossible ) ) { pSubdeviceDescriptor->PinInstances[ulPinId].GlobalCurrent++; pulPinInstanceCounts[ulPinId]++; _DbgPrintF( DEBUGLVL_VERBOSE, ( "Create pin %d: global=%d local=%d" , ulPinId , pSubdeviceDescriptor->PinInstances[ulPinId].GlobalCurrent , pulPinInstanceCounts[ulPinId] )); } else { // TODO: What code? ntStatus = STATUS_UNSUCCESSFUL; } return ntStatus; } /***************************************************************************** * PcValidateDeviceContext() ***************************************************************************** * Probes DeviceContext for writing. */ PORTCLASSAPI NTSTATUS NTAPI PcValidateDeviceContext ( IN PDEVICE_CONTEXT pDeviceContext, IN PIRP pIrp ) { PAGED_CODE(); NTSTATUS ntStatus = STATUS_SUCCESS; if (NULL == pDeviceContext) { _DbgPrintF(DEBUGLVL_TERSE, ("PcValidateDeviceContext : pDeviceContext = NULL")); return STATUS_INVALID_PARAMETER; } // validate the pointers if we don't trust the client // /* // ISSUE ALPERS 2000/12/20 - The Probe call is disabled because it always generates an exception. // Therefore it is disabled. if (KernelMode != pIrp->RequestorMode) { __try { ProbeForRead( pDeviceContext, sizeof(*pDeviceContext), sizeof(BYTE)); } __except (EXCEPTION_EXECUTE_HANDLER) { ntStatus = GetExceptionCode(); _DbgPrintF(DEBUGLVL_TERSE, ("PcValidateDeviceContext : ProbeForWrite failed %X", ntStatus)); } } */ if (NT_SUCCESS(ntStatus)) { if (PORTCLS_DEVICE_EXTENSION_SIGNATURE != pDeviceContext->Signature ) { ntStatus = STATUS_INVALID_PARAMETER; _DbgPrintF(DEBUGLVL_TERSE, ("PcValidateDeviceContext : Invalid Extension Signature")); } } return ntStatus; } // PcValidateDeviceContext /***************************************************************************** * PcTerminateConnection() ***************************************************************************** * Decrement instance counts associated with a pin. */ PORTCLASSAPI void NTAPI PcTerminateConnection ( IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor , IN PULONG pulPinInstanceCounts , IN ULONG ulPinId ) { PAGED_CODE(); ASSERT(pSubdeviceDescriptor); ASSERT(pulPinInstanceCounts); ASSERT(ulPinId <= pSubdeviceDescriptor->PinCount); pSubdeviceDescriptor->PinInstances[ulPinId].GlobalCurrent--; pulPinInstanceCounts[ulPinId]--; _DbgPrintF( DEBUGLVL_VERBOSE, ( "Delete pin %d: global=%d local=%d" , ulPinId , pSubdeviceDescriptor->PinInstances[ulPinId].GlobalCurrent , pulPinInstanceCounts[ulPinId] )); } /***************************************************************************** * PcVerifyFilterIsReady() ***************************************************************************** * Verify necessary pins are connected. */ PORTCLASSAPI NTSTATUS NTAPI PcVerifyFilterIsReady ( IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor , IN PULONG pulPinInstanceCounts ) { PAGED_CODE(); ASSERT(pSubdeviceDescriptor); ASSERT(pulPinInstanceCounts); NTSTATUS ntStatus = STATUS_SUCCESS; for ( ULONG ulPinId = 0 ; ulPinId < pSubdeviceDescriptor->PinCount ; ulPinId++ ) { if ( pulPinInstanceCounts[ulPinId] < pSubdeviceDescriptor->PinInstances[ulPinId].FilterNecessary ) { // TODO: What code? ntStatus = STATUS_UNSUCCESSFUL; break; } } return ntStatus; } #define END_NONE 0 #define END_FROM 1 #define END_TO 2 #define END_BOTH 3 /***************************************************************************** * FindConnectionToPin() ***************************************************************************** * Find a connection that connects to a given node or filter pin. * * ulNode - node number of KSFILTER_NODE * ulConnection - in: connection to start with * out: found connection or ULONG(-1) if not found * * return - 0 for no connection found * END_FROM for outgoing connection * END_TO for incoming connection */ ULONG FindConnectionToPin ( IN ULONG ulNode, IN ULONG ulPin, IN PKSTOPOLOGY pKsTopology, IN OUT PULONG ulConnection, OUT PKSTOPOLOGY_CONNECTION * ppKsTopologyConnection OPTIONAL ) { ASSERT(pKsTopology); ASSERT(ulConnection); ASSERT(*ulConnection < pKsTopology->TopologyConnectionsCount); ULONG ulEnd; PKSTOPOLOGY_CONNECTION pKsTopologyConnection = PKSTOPOLOGY_CONNECTION ( &pKsTopology->TopologyConnections[*ulConnection] ); while (1) { ASSERT(*ulConnection <= pKsTopology->TopologyConnectionsCount); if (*ulConnection == pKsTopology->TopologyConnectionsCount) { ulEnd = END_NONE; *ulConnection = ULONG(-1); pKsTopologyConnection = NULL; break; } else if ( (pKsTopologyConnection->FromNode == ulNode) && (pKsTopologyConnection->FromNodePin == ulPin) ) { ulEnd = END_FROM; break; } else if ( (pKsTopologyConnection->ToNode == ulNode) && (pKsTopologyConnection->ToNodePin == ulPin) ) { ulEnd = END_TO; break; } (*ulConnection)++; pKsTopologyConnection++; } if (ppKsTopologyConnection) { *ppKsTopologyConnection = pKsTopologyConnection; } return ulEnd; } /***************************************************************************** * FindConnectionToNode() ***************************************************************************** * Find a connection that connects to a given node or to the filter. * * ulNode - node number of KSFILTER_NODE * ulEnd - 0 for any direction * END_FROM for outgoing connection * END_TO for incoming connection * ulConnection - in: connection to start with * out: found connection or ULONG(-1) if not found * * return - 0 for no connection found * END_FROM for outgoing connection * END_TO for incoming connection */ ULONG FindConnectionToNode ( IN ULONG ulNode, IN ULONG ulEnd, IN PKSTOPOLOGY pKsTopology, IN OUT PULONG ulConnection, OUT PKSTOPOLOGY_CONNECTION * ppKsTopologyConnection OPTIONAL ) { ASSERT(pKsTopology); ASSERT ( (ulNode == KSFILTER_NODE) || (ulNode < pKsTopology->TopologyNodesCount) ); ASSERT(ulConnection); ASSERT(*ulConnection < pKsTopology->TopologyConnectionsCount); PKSTOPOLOGY_CONNECTION pKsTopologyConnection = PKSTOPOLOGY_CONNECTION ( &pKsTopology->TopologyConnections[*ulConnection] ); while (1) { ASSERT(*ulConnection <= pKsTopology->TopologyConnectionsCount); if (*ulConnection == pKsTopology->TopologyConnectionsCount) { ulEnd = END_NONE; *ulConnection = ULONG(-1); pKsTopologyConnection = NULL; break; } else if ( (pKsTopologyConnection->FromNode == ulNode) && (ulEnd != END_TO) ) { ulEnd = END_FROM; break; } else if ( (pKsTopologyConnection->ToNode == ulNode) && (ulEnd != END_FROM) ) { ulEnd = END_TO; break; } (*ulConnection)++; pKsTopologyConnection++; } if (ppKsTopologyConnection) { *ppKsTopologyConnection = pKsTopologyConnection; } return ulEnd; } /***************************************************************************** * NodeIsTransform() ***************************************************************************** * Determines if a node is a transform. KSFILTER_NODE is handled (FALSE). */ BOOLEAN NodeIsTransform ( IN ULONG ulNode, IN PKSTOPOLOGY pKsTopology ) { ASSERT(pKsTopology); ASSERT ( (ulNode == KSFILTER_NODE) || (ulNode < pKsTopology->TopologyNodesCount) ); ULONG ulEnd = END_NONE; if (ulNode != KSFILTER_NODE) { PKSTOPOLOGY_CONNECTION pKsTopologyConnection = PKSTOPOLOGY_CONNECTION(pKsTopology->TopologyConnections); for ( ULONG ul = pKsTopology->TopologyConnectionsCount; ul--; pKsTopologyConnection++ ) { if (pKsTopologyConnection->FromNode == ulNode) { ulEnd += END_FROM; if ( (ulEnd != END_FROM) && (ulEnd != END_BOTH) ) { break; } } if (pKsTopologyConnection->ToNode == ulNode) { ulEnd += END_TO; if ( (ulEnd != END_TO) && (ulEnd != END_BOTH) ) { break; } } } } return ulEnd == END_BOTH; } /***************************************************************************** * NodeAtThisEnd() ***************************************************************************** * Node at indicated end of the connection. */ inline ULONG NodeAtThisEnd ( IN ULONG ulEnd, IN PKSTOPOLOGY_CONNECTION pKsTopologyConnection ) { return ( (ulEnd == END_FROM) ? pKsTopologyConnection->FromNode : pKsTopologyConnection->ToNode ); } /***************************************************************************** * NodeAtOtherEnd() ***************************************************************************** * Node at opposite end of the connection. */ inline ULONG NodeAtOtherEnd ( IN ULONG ulEnd, IN PKSTOPOLOGY_CONNECTION pKsTopologyConnection ) { return ( (ulEnd == END_FROM) ? pKsTopologyConnection->ToNode : pKsTopologyConnection->FromNode ); } /***************************************************************************** * PcCaptureFormat() ***************************************************************************** * Capture a data format in an allocated buffer, possibly changing offensive * formats. */ PORTCLASSAPI NTSTATUS NTAPI PcCaptureFormat ( OUT PKSDATAFORMAT * ppKsDataFormatOut, IN PKSDATAFORMAT pKsDataFormatIn, IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor, IN ULONG ulPinId ) { ASSERT(ppKsDataFormatOut); ASSERT(pKsDataFormatIn); ASSERT(pSubdeviceDescriptor); ASSERT(ulPinId < pSubdeviceDescriptor->PinCount); NTSTATUS ntStatus = STATUS_SUCCESS; if( (pKsDataFormatIn->FormatSize >= sizeof(KSDATAFORMAT_DSOUND)) && IsEqualGUIDAligned( pKsDataFormatIn->MajorFormat, KSDATAFORMAT_TYPE_AUDIO ) && IsEqualGUIDAligned( pKsDataFormatIn->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND ) ) { // // This is the dread DSound format. Check to see if we have the // required topology and convert to WaveFormatEx if we do. // // Note: DSound format does not have to be PCM.... // PKSDATAFORMAT_DSOUND pKsDataFormatDSound = PKSDATAFORMAT_DSOUND(pKsDataFormatIn); // // Fail if the client has asked for a software buffer. // if ( pKsDataFormatDSound->BufferDesc.Flags & KSDSOUND_BUFFER_LOCSOFTWARE ) { _DbgPrintF(DEBUGLVL_TERSE,("PcCaptureFormat Failed because client requested software buffer.")); return STATUS_INVALID_PARAMETER; } // // Find a connection involving the filter pin. // ULONG ulConnection = 0; PKSTOPOLOGY_CONNECTION pKsTopologyConnection; ULONG ulEnd = FindConnectionToPin ( KSFILTER_NODE, ulPinId, pSubdeviceDescriptor->Topology, &ulConnection, &pKsTopologyConnection ); // // Trace the topology until we find a non-transform or all the // required nodes have been found. Position notification is // always supported. // ULONG ulMissing = ( pKsDataFormatDSound->BufferDesc.Control & ~KSDSOUND_BUFFER_CTRL_POSITIONNOTIFY ); while (ulMissing && ulEnd) { // // Found a connection. Follow the topology. // ULONG ulNode = NodeAtOtherEnd(ulEnd,pKsTopologyConnection); if (! NodeIsTransform(ulNode,pSubdeviceDescriptor->Topology)) { // // The new node is not a simple transform (1 in, 1 out). // break; } // // Drop 'missing' bits as appropriate based on the node GUID. // ASSERT(ulNode < pSubdeviceDescriptor->Topology->TopologyNodesCount); const GUID *pGuid = &pSubdeviceDescriptor->Topology->TopologyNodes[ulNode]; if (IsEqualGUIDAligned(*pGuid,KSNODETYPE_3D_EFFECTS)) { ulMissing &=~ KSDSOUND_BUFFER_CTRL_3D; } else if (IsEqualGUIDAligned(*pGuid,KSNODETYPE_SRC)) { ulMissing &=~ KSDSOUND_BUFFER_CTRL_FREQUENCY; } else if ( IsEqualGUIDAligned(*pGuid,KSNODETYPE_SUPERMIX) || IsEqualGUIDAligned(*pGuid,KSNODETYPE_VOLUME) ) { ulMissing &=~ KSDSOUND_BUFFER_CTRL_PAN; ulMissing &=~ KSDSOUND_BUFFER_CTRL_VOLUME; } // // Find the next connection in line. // ulConnection = 0; ulEnd = FindConnectionToNode ( ulNode, ulEnd, pSubdeviceDescriptor->Topology, &ulConnection, &pKsTopologyConnection ); } // // Make sure no nodes were missing. // if (! ulMissing) { // // We have the capabilities required. Build the new format. // ULONG ulSize = ( sizeof(KSDATAFORMAT_WAVEFORMATEX) + ( pKsDataFormatIn->FormatSize - sizeof(KSDATAFORMAT_DSOUND) ) ); *ppKsDataFormatOut = PKSDATAFORMAT ( ExAllocatePoolWithTag ( PagedPool, ulSize, 'fDcP' ) ); if (*ppKsDataFormatOut) { // // Copy KSDATAFORMAT part. // RtlCopyMemory ( *ppKsDataFormatOut, pKsDataFormatIn, sizeof(KSDATAFORMAT) ); // // Copy WAVEFORMATEX part including appended stuff. // RtlCopyMemory ( *ppKsDataFormatOut + 1, &pKsDataFormatDSound->BufferDesc.WaveFormatEx, ulSize - sizeof(KSDATAFORMAT) ); // // Adjust size and specifier. // (*ppKsDataFormatOut)->FormatSize = ulSize; (*ppKsDataFormatOut)->Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; } else { _DbgPrintF(DEBUGLVL_TERSE,("PcCaptureFormat Failed to allocate memory for format.")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } else { _DbgPrintF(DEBUGLVL_VERBOSE,("PcCaptureFormat Failed due to lack of feature support (0x%08x).",ulMissing)); // // Don't have the required nodes...fail. // ntStatus = STATUS_INVALID_PARAMETER; } } else { _DbgPrintF(DEBUGLVL_VERBOSE,("PcCaptureFormat Format captured as-is.")); // // Some other format. Just capture it. // *ppKsDataFormatOut = PKSDATAFORMAT(ExAllocatePoolWithTag(PagedPool, pKsDataFormatIn->FormatSize, 'fDcP')); if (*ppKsDataFormatOut) { RtlCopyMemory ( *ppKsDataFormatOut, pKsDataFormatIn, pKsDataFormatIn->FormatSize ); } else { _DbgPrintF(DEBUGLVL_TERSE,("PcCaptureFormat Failed to allocate memory for format.")); ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } // check to verify SampleSize is set properly on waveformatex formats if( NT_SUCCESS(ntStatus) && (pKsDataFormatIn->FormatSize >= sizeof(KSDATAFORMAT_WAVEFORMATEX)) && IsEqualGUIDAligned((*ppKsDataFormatOut)->MajorFormat,KSDATAFORMAT_TYPE_AUDIO) && IsEqualGUIDAligned((*ppKsDataFormatOut)->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX)) { PKSDATAFORMAT_WAVEFORMATEX pWaveFormat = PKSDATAFORMAT_WAVEFORMATEX(*ppKsDataFormatOut); if( 0 == pWaveFormat->DataFormat.SampleSize ) { pWaveFormat->DataFormat.SampleSize = pWaveFormat->WaveFormatEx.nBlockAlign; } } return ntStatus; } /***************************************************************************** * PcAcquireFormatResources() ***************************************************************************** * Acquire resources specified in a format. */ PORTCLASSAPI void NTAPI PcAcquireFormatResources ( IN PKSDATAFORMAT pKsDataFormatIn, IN PSUBDEVICE_DESCRIPTOR pSubdeviceDescriptor, IN ULONG ulPinId, IN PPROPERTY_CONTEXT pPropertyContext ) { ASSERT(pKsDataFormatIn); ASSERT(pSubdeviceDescriptor); ASSERT(ulPinId < pSubdeviceDescriptor->PinCount); ASSERT(pPropertyContext); KSP_NODE ksPNode; ksPNode.Property.Set = KSPROPSETID_TopologyNode; ksPNode.Property.Id = KSPROPERTY_TOPOLOGYNODE_ENABLE; ksPNode.Property.Flags = KSPROPERTY_TYPE_SET | KSPROPERTY_TYPE_TOPOLOGY; ksPNode.NodeId = 0; // Fill in later ksPNode.Reserved = 0; if ( (pKsDataFormatIn->FormatSize >= sizeof(KSDATAFORMAT_DSOUND)) && IsEqualGUIDAligned ( pKsDataFormatIn->MajorFormat, KSDATAFORMAT_TYPE_AUDIO ) && IsEqualGUIDAligned ( pKsDataFormatIn->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND ) ) { // // This is the dreaded DSound format. Turn on all the nodes // that are specified in the caps bits. // PKSDATAFORMAT_DSOUND pKsDataFormatDSound = PKSDATAFORMAT_DSOUND(pKsDataFormatIn); // // Find a connection involving the filter pin. // ULONG ulConnection = 0; PKSTOPOLOGY_CONNECTION pKsTopologyConnection; ULONG ulEnd = FindConnectionToPin ( KSFILTER_NODE, ulPinId, pSubdeviceDescriptor->Topology, &ulConnection, &pKsTopologyConnection ); // // Trace the topology until we find a non-transform or all the // required nodes have been found. Position notification is // always supported. // ULONG ulMissing = ( pKsDataFormatDSound->BufferDesc.Control & ( KSDSOUND_BUFFER_CTRL_3D | KSDSOUND_BUFFER_CTRL_FREQUENCY ) ); while (ulMissing && ulEnd) { // // Found a connection. Follow the topology. // ULONG ulNode = NodeAtOtherEnd(ulEnd,pKsTopologyConnection); if (! NodeIsTransform(ulNode,pSubdeviceDescriptor->Topology)) { // // The new node is not a simple transform (1 in, 1 out). // break; } // // Turn on nodes as appropriate based on the node GUID. // ASSERT(ulNode < pSubdeviceDescriptor->Topology->TopologyNodesCount); const GUID *pGuid = &pSubdeviceDescriptor->Topology->TopologyNodes[ulNode]; if (IsEqualGUIDAligned(*pGuid,KSNODETYPE_3D_EFFECTS)) { if (ulMissing & KSDSOUND_BUFFER_CTRL_3D) { // // Turn on the 3D node. // ULONG ulPropertyValue = TRUE; ULONG ulPropertyValueSize = sizeof(ULONG); ksPNode.NodeId = ulNode; PcDispatchProperty ( NULL // pIrp , pPropertyContext , NULL // pKsPropertySet , sizeof(KSP_NODE) , &ksPNode.Property , &ulPropertyValueSize , &ulPropertyValue ); ulMissing &=~ KSDSOUND_BUFFER_CTRL_3D; } } else if (IsEqualGUIDAligned(*pGuid,KSNODETYPE_SRC)) { if (ulMissing & KSDSOUND_BUFFER_CTRL_FREQUENCY) { // // Turn on the SRC node. // ULONG ulPropertyValue = TRUE; ULONG ulPropertyValueSize = sizeof(ULONG); ksPNode.NodeId = ulNode; PcDispatchProperty ( NULL // pIrp , pPropertyContext , NULL // pKsPropertySet , sizeof(KSP_NODE) , &ksPNode.Property , &ulPropertyValueSize , &ulPropertyValue ); ulMissing &=~ KSDSOUND_BUFFER_CTRL_FREQUENCY; } } // // Find the next connection in line. // ulConnection = 0; ulEnd = FindConnectionToNode ( ulNode, ulEnd, pSubdeviceDescriptor->Topology, &ulConnection, &pKsTopologyConnection ); } } } #pragma code_seg() /***************************************************************************** * PcRegisterIoTimeout() ***************************************************************************** * Registers an IoTimeout callback. */ PORTCLASSAPI NTSTATUS NTAPI PcRegisterIoTimeout ( IN PDEVICE_OBJECT pDeviceObject, IN PIO_TIMER_ROUTINE pTimerRoutine, IN PVOID pContext ) { KIRQL OldIrql; PTIMEOUTCALLBACK TimeoutCallback; NTSTATUS ntStatus = STATUS_SUCCESS; ASSERT(pDeviceObject); ASSERT(pTimerRoutine); ASSERT( PASSIVE_LEVEL == KeGetCurrentIrql() ); // // Validate Parameters. // if (NULL == pDeviceObject || NULL == pTimerRoutine || NULL == pDeviceObject->DeviceExtension) { _DbgPrintF(DEBUGLVL_TERSE, ("PcRegisterIoTimeout : Invalid Parameter")); return STATUS_INVALID_PARAMETER; } // get the device context PDEVICE_CONTEXT pDeviceContext = PDEVICE_CONTEXT(pDeviceObject->DeviceExtension); // allocate a timeout callback structure -- 'PcTc' TimeoutCallback = PTIMEOUTCALLBACK(ExAllocatePoolWithTag( NonPagedPool, sizeof(TIMEOUTCALLBACK),'cTcP' )); if( TimeoutCallback ) { // initialize the entry TimeoutCallback->TimerRoutine = pTimerRoutine; TimeoutCallback->Context = pContext; // grab the list spin lock KeAcquireSpinLock( &(pDeviceContext->TimeoutLock), &OldIrql ); // walk the list to see if the entry is already registered if( !IsListEmpty( &(pDeviceContext->TimeoutList) ) ) { PLIST_ENTRY ListEntry; PTIMEOUTCALLBACK pCallback; for( ListEntry = pDeviceContext->TimeoutList.Flink; ListEntry != &(pDeviceContext->TimeoutList); ListEntry = ListEntry->Flink ) { pCallback = (PTIMEOUTCALLBACK) CONTAINING_RECORD( ListEntry, TIMEOUTCALLBACK, ListEntry ); if( (pCallback->TimerRoutine == pTimerRoutine) && (pCallback->Context == pContext) ) { // entry already exists in the list, so fail this request ntStatus = STATUS_UNSUCCESSFUL; } } } if( NT_SUCCESS(ntStatus) ) { // add the entry to the list InsertTailList( &(pDeviceContext->TimeoutList), &(TimeoutCallback->ListEntry) ); } else { // free the entry ExFreePool( TimeoutCallback ); } // release the spin lock KeReleaseSpinLock( &(pDeviceContext->TimeoutLock), OldIrql ); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } return ntStatus; } /***************************************************************************** * PcUnregisterIoTimeout() ***************************************************************************** * Unregisters an IoTimeout callback. */ PORTCLASSAPI NTSTATUS NTAPI PcUnregisterIoTimeout ( IN PDEVICE_OBJECT pDeviceObject, IN PIO_TIMER_ROUTINE pTimerRoutine, IN PVOID pContext ) { KIRQL OldIrql; ASSERT(pDeviceObject); ASSERT(pTimerRoutine); ASSERT( PASSIVE_LEVEL == KeGetCurrentIrql() ); // // Validate Parameters. // if (NULL == pDeviceObject || NULL == pTimerRoutine || NULL == pDeviceObject->DeviceExtension) { _DbgPrintF(DEBUGLVL_TERSE, ("PcUnregisterIoTimeout : Invalid Parameter")); return STATUS_INVALID_PARAMETER; } // get the device context PDEVICE_CONTEXT pDeviceContext = PDEVICE_CONTEXT(pDeviceObject->DeviceExtension); // grab the spin lock KeAcquireSpinLock( &(pDeviceContext->TimeoutLock), &OldIrql ); // walk the list if( !IsListEmpty( &(pDeviceContext->TimeoutList) ) ) { PLIST_ENTRY ListEntry; PTIMEOUTCALLBACK pCallback; for( ListEntry = pDeviceContext->TimeoutList.Flink; ListEntry != &(pDeviceContext->TimeoutList); ListEntry = ListEntry->Flink ) { pCallback = (PTIMEOUTCALLBACK) CONTAINING_RECORD( ListEntry, TIMEOUTCALLBACK, ListEntry ); if( (pCallback->TimerRoutine == pTimerRoutine) && (pCallback->Context == pContext) ) { RemoveEntryList(ListEntry); KeReleaseSpinLock( &(pDeviceContext->TimeoutLock), OldIrql ); ExFreePool(pCallback); return STATUS_SUCCESS; } } } // release the spinlock KeReleaseSpinLock( &(pDeviceContext->TimeoutLock), OldIrql ); return STATUS_NOT_FOUND; }