www.pudn.com > CMIDriver-1.1.1-src.zip > minwave.cpp


/* 
Copyright (c) 2006-2007 dogbert  
All rights reserved. 
 
Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions 
are met: 
1. Redistributions of source code must retain the above copyright 
   notice, this list of conditions and the following disclaimer. 
2. Redistributions in binary form must reproduce the above copyright 
   notice, this list of conditions and the following disclaimer in the 
   documentation and/or other materials provided with the distribution. 
3. The name of the author may not be used to endorse or promote products 
   derived from this software without specific prior written permission. 
 
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
*/ 
 
#include "minwave.hpp" 
#include "minwavetables.hpp" 
#include "ntddk.h" 
 
#pragma code_seg("PAGE") 
 
NTSTATUS CreateMiniportWaveCMI(PUNKNOWN *Unknown, REFCLSID, PUNKNOWN UnknownOuter, POOL_TYPE PoolType) 
{ 
	PAGED_CODE(); 
	ASSERT(Unknown); 
#ifdef WAVERT 
	STD_CREATE_BODY_(CMiniportWaveCMI,Unknown,UnknownOuter,PoolType,PMINIPORTWAVERT); 
#else 
	STD_CREATE_BODY_(CMiniportWaveCMI,Unknown,UnknownOuter,PoolType,PMINIPORTWAVECYCLIC); 
#endif 
} 
 
NTSTATUS CMiniportWaveCMI::processResources(PRESOURCELIST resourceList) 
{ 
	PAGED_CODE(); 
	ASSERT (resourceList); 
	DBGPRINT(("CMiniportWaveCMI[%p]::ProcessResources(%p)", this, resourceList)); 
 
	if (resourceList->NumberOfInterrupts() < 1) { 
		DBGPRINT(("Unknown configuration for wave miniport")); 
		return STATUS_DEVICE_CONFIGURATION_ERROR; 
	} 
	return STATUS_SUCCESS; 
} 
 
#ifndef WAVERT 
NTSTATUS CMiniportWaveCMI::newDMAChannel(PDMACHANNEL *dmaChannel, UInt32 bufferLength) 
{ 
	PAGED_CODE(); 
	ASSERT(dmaChannel); 
	DBGPRINT(("CMiniportWaveCMI[%p]::newDMAChannel(%p)", this, dmaChannel)); 
 
	NTSTATUS ntStatus; 
 
	ntStatus = Port->NewMasterDmaChannel(dmaChannel, NULL, NULL, bufferLength, TRUE, FALSE, (DMA_WIDTH)(-1), (DMA_SPEED)(-1)); 
	if (NT_SUCCESS(ntStatus)) { 
		ULONG  lDMABufferLength = bufferLength; 
		do { 
			ntStatus = (*dmaChannel)->AllocateBuffer(lDMABufferLength,NULL); 
			lDMABufferLength >>= 1; 
		} while (!NT_SUCCESS(ntStatus) && (lDMABufferLength > (PAGE_SIZE / 2))); 
	} 
	return ntStatus; 
} 
#endif 
 
//generic crap 
STDMETHODIMP CMiniportWaveCMI::NonDelegatingQueryInterface(REFIID Interface, PVOID *Object) 
{ 
	PAGED_CODE(); 
	ASSERT(Object); 
	DBGPRINT(("CMiniportWaveCMI[%p]::NonDelegatingQueryInterface")); 
 
	if (IsEqualGUIDAligned(Interface,IID_IUnknown)) { 
#ifdef WAVERT 
		*Object = PVOID(PUNKNOWN(PMINIPORTWAVERT(this))); 
#else 
		*Object = PVOID(PUNKNOWN(PMINIPORTWAVECYCLIC(this))); 
#endif 
	} else if (IsEqualGUIDAligned(Interface,IID_IMiniport)) { 
		*Object = PVOID(PMINIPORT(this)); 
#ifdef WAVERT 
	} else if (IsEqualGUIDAligned(Interface,IID_IMiniportWaveRT)) { 
		*Object = PVOID(PMINIPORTWAVERT(this)); 
#else 
	} else if (IsEqualGUIDAligned(Interface,IID_IMiniportWaveCyclic)) { 
		*Object = PVOID(PMINIPORTWAVECYCLIC(this)); 
#endif 
	} else { 
		*Object = NULL; 
	} 
 
	if (*Object) { 
		// We reference the interface for the caller. 
		PUNKNOWN(*Object)->AddRef(); 
		return STATUS_SUCCESS; 
	} 
 
	return STATUS_INVALID_PARAMETER; 
} 
 
CMiniportWaveCMI::~CMiniportWaveCMI(void) 
{ 
	PAGED_CODE(); 
	DBGPRINT(("CMiniportWaveCMI[%p]::~CMiniportWaveCMI", this)); 
 
	SaveChannelConfig(); //or not. during system shutdown, this doesn't seem to work. 
 
	if (CMIAdapter) { 
		CMIAdapter->Release(); 
		CMIAdapter = NULL; 
	} 
 
	for (int i=0;i<3;i++) { 
#ifndef WAVERT 
		if (DMAChannel[i]) { 
			DMAChannel[i]->Release(); 
			DMAChannel[i] = NULL; 
		} 
#endif 
    	if (isStreamRunning[i]) { 
			isStreamRunning[i] = false; 
			stream[i]->Release(); 
			stream[i] = NULL; 
		} 
	} 
 
	if (Port) { 
		Port->Release(); 
		Port = NULL; 
	} 
} 
 
#ifdef WAVERT 
STDMETHODIMP CMiniportWaveCMI::Init(PUNKNOWN UnknownAdapter, PRESOURCELIST ResourceList, PPORTWAVERT Port_) 
#else 
STDMETHODIMP CMiniportWaveCMI::Init(PUNKNOWN UnknownAdapter, PRESOURCELIST ResourceList, PPORTWAVECYCLIC Port_) 
#endif 
{ 
	PAGED_CODE(); 
 
	ASSERT(UnknownAdapter); 
	ASSERT(ResourceList); 
	ASSERT(Port_); 
 
	DBGPRINT(("CMiniportWaveCMI[%p]::Init(%p, %p, %p)", this, UnknownAdapter, ResourceList, Port_)); 
 
	Port = Port_; 
	Port->AddRef(); 
 
	NTSTATUS ntStatus = UnknownAdapter->QueryInterface(IID_ICMIAdapter, (PVOID *) &CMIAdapter); 
    if (!NT_SUCCESS(ntStatus)) { 
	    DBGPRINT(("QueryInterface(CMIAdapter) failed")); 
		return ntStatus; 
	} 
 
	//check for Vista, set the AC3 stuff accordingly 
	if (IoIsWdmVersionAvailable(0x06,0x00)) { 
		WavePinDataRangesAC3Stream[1].MinimumSampleFrequency = MIN_SAMPLE_RATE; 
		WavePinDataRangesAC3Stream[1].MaximumSampleFrequency = MAX_SAMPLE_RATE; 
		WavePinDataRangesAC3Stream[1].DataRange.SubFormat    = KSDATAFORMAT_SUBTYPE_PCM; 
		WavePinDataRangesAC3Stream[1].DataRange.Specifier    = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; 
	} 
 
	cm = CMIAdapter->getCMI8738Info(); 
	cm->regFUNCTRL0  = 0; 
	cm->WaveMiniport = this; 
 
	LoadChannelConfig(); 
 
	for (int i=0;i<3;i++) 
	{ 
		isStreamRunning[i] = false; 
 
#ifndef WAVERT 
		ntStatus = newDMAChannel(&DMAChannel[i], MAXLEN_DMA_BUFFER); 
		if (!NT_SUCCESS(ntStatus)) { 
			DBGPRINT(("NewDmaChannel() failed")); 
			return ntStatus; 
		} 
#endif 
	} 
 
	KeInitializeMutex(&mutex, 1); 
 
	return processResources(ResourceList); 
} 
 
#ifdef WAVERT 
STDMETHODIMP_(NTSTATUS) CMiniportWaveCMI::GetDeviceDescription(PDEVICE_DESCRIPTION OutDeviceDescriptor) 
{ 
	PAGED_CODE(); 
	ASSERT(OutDeviceDescriptor); 
	DBGPRINT(("CMiniportWaveCMI[%p]::GetDeviceDescription(%p)", this, OutDeviceDescriptor)); 
 
	RtlZeroMemory(OutDeviceDescriptor, sizeof(DEVICE_DESCRIPTION)); 
	OutDeviceDescriptor->ScatterGather     = false; 
	OutDeviceDescriptor->Master            = true; 
	OutDeviceDescriptor->Dma32BitAddresses = true; 
	OutDeviceDescriptor->InterfaceType     = PCIBus; 
	OutDeviceDescriptor->MaximumLength     = MAXLEN_DMA_BUFFER-2; 
 
	return STATUS_SUCCESS; 
} 
#endif 
 
STDMETHODIMP CMiniportWaveCMI::GetDescription(PPCFILTER_DESCRIPTOR *OutFilterDescriptor) 
{ 
    PAGED_CODE(); 
    ASSERT(OutFilterDescriptor); 
    DBGPRINT(("CMiniportWaveCMI[%p]::GetDescription(%p)", this, OutFilterDescriptor)); 
 
    *OutFilterDescriptor = &WaveMiniportFilterDescriptor; 
 
    return STATUS_SUCCESS; 
} 
 
NTSTATUS CMiniportWaveCMI::LoadChannelConfig() 
{ 
	PREGISTRYKEY       DriverKey; 
	PREGISTRYKEY       SettingsKey; 
    UNICODE_STRING     KeyName; 
    DWORD              Value, ResultLength; 
	PVOID              KeyInfo; 
 
    DBGPRINT(("CMiniportWaveCMI::LoadChannelConfig")); 
 
	if ((!CMIAdapter) || (!(CMIAdapter->getDeviceObject()))) { 
		return STATUS_UNSUCCESSFUL; 
	} 
 
	NTSTATUS ntStatus = PcNewRegistryKey(&DriverKey, NULL, DriverRegistryKey, KEY_ALL_ACCESS, CMIAdapter->getDeviceObject(), NULL, NULL, 0, NULL); 
 
	if(!NT_SUCCESS(ntStatus)) { 
		DBGPRINT(("PcNewRegistryKey() failed")); 
		return STATUS_UNSUCCESSFUL; 
	} 
 
	RtlInitUnicodeString(&KeyName, L"Settings"); 
 
	ntStatus = DriverKey->NewSubKey(&SettingsKey, NULL, KEY_ALL_ACCESS, &KeyName, REG_OPTION_NON_VOLATILE, NULL); 
	if(!NT_SUCCESS(ntStatus)) { 
		DBGPRINT(("DriverKey->NewSubKey() failed")); 
		return STATUS_UNSUCCESSFUL; 
	} 
 
	KeyInfo = ExAllocatePoolWithTag(PagedPool, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD), 'gnaa'); 
	if (KeyInfo) { 
		RtlInitUnicodeString(&KeyName, L"ChannelCount"); 
		ntStatus = SettingsKey->QueryValueKey(&KeyName, KeyValuePartialInformation, KeyInfo, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD), &ResultLength); 
		if (NT_SUCCESS (ntStatus)) { 
        	PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo; 
			if (PartialInfo->DataLength == sizeof(DWORD)) { 
				requestedChannelCount = (*(PLONG)PartialInfo->Data); 
			} 
		} else { 
			requestedChannelCount = 2; 
		} 
 
		RtlInitUnicodeString(&KeyName, L"ChannelMask"); 
		ntStatus = SettingsKey->QueryValueKey(&KeyName, KeyValuePartialInformation, KeyInfo, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD), &ResultLength); 
		if (NT_SUCCESS (ntStatus)) { 
        	PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo; 
			if (PartialInfo->DataLength == sizeof(DWORD)) { 
				requestedChannelMask = (*(PLONG)PartialInfo->Data); 
			} 
		} else { 
			requestedChannelMask = KSAUDIO_SPEAKER_STEREO; 
		} 
	} 
	ExFreePoolWithTag(KeyInfo,'gnaa'); 
 
	SettingsKey->Release(); 
	DriverKey->Release(); 
 
	return STATUS_SUCCESS; 
} 
 
NTSTATUS CMiniportWaveCMI::SaveChannelConfig() 
{ 
	PREGISTRYKEY       DriverKey; 
	PREGISTRYKEY       SettingsKey; 
    UNICODE_STRING     KeyName; 
    DWORD              Value; 
    DBGPRINT(("CMiniportWaveCMI::SaveChannelConfig")); 
 
	if ((!CMIAdapter) || (!(CMIAdapter->getDeviceObject()))) { 
		return STATUS_UNSUCCESSFUL; 
	} 
 
	NTSTATUS ntStatus = PcNewRegistryKey(&DriverKey, NULL, DriverRegistryKey, KEY_ALL_ACCESS, CMIAdapter->getDeviceObject(), NULL, NULL, 0, NULL); 
 
	if(!NT_SUCCESS(ntStatus)) { 
		DBGPRINT(("PcNewRegistryKey() failed")); 
		return STATUS_UNSUCCESSFUL; 
	} 
 
	RtlInitUnicodeString(&KeyName, L"Settings"); 
 
	ntStatus = DriverKey->NewSubKey(&SettingsKey, NULL, KEY_ALL_ACCESS, &KeyName, REG_OPTION_NON_VOLATILE, NULL); 
	if(!NT_SUCCESS(ntStatus)) { 
		DBGPRINT(("DriverKey->NewSubKey() failed")); 
		return STATUS_UNSUCCESSFUL; 
	} 
 
	Value = requestedChannelCount; 
	RtlInitUnicodeString(&KeyName, L"ChannelCount"); 
	ntStatus = SettingsKey->SetValueKey(&KeyName, REG_DWORD, PVOID(&Value), sizeof(DWORD)); 
	if (!NT_SUCCESS(ntStatus)) { 
		DBGPRINT(("SetValueKey() failed")); 
	} 
	Value = requestedChannelMask; 
	RtlInitUnicodeString(&KeyName, L"ChannelMask"); 
	ntStatus = SettingsKey->SetValueKey(&KeyName, REG_DWORD, PVOID(&Value), sizeof(DWORD)); 
	if (!NT_SUCCESS(ntStatus)) { 
		DBGPRINT(("SetValueKey() failed")); 
	} 
 
	SettingsKey->Release(); 
	DriverKey->Release(); 
 
	return STATUS_SUCCESS; 
} 
 
NTSTATUS CMiniportWaveCMI::isFormatAllowed(UInt32 sampleRate, BOOLEAN multiChan, BOOLEAN AC3) 
{ 
	PAGED_CODE(); 
	ASSERT(sampleRate); 
	DBGPRINT(("CMiniportWaveCMI[%p]::isFormatAllowed(%d, %d, %d)", this, sampleRate, multiChan, AC3)); 
 
	if (multiChan) { 
		switch (sampleRate) { 
			case 44100: if (cm->formatMask & FMT_441_MULTI_PCM) return STATUS_SUCCESS; break; 
			case 48000: if (cm->formatMask & FMT_480_MULTI_PCM) return STATUS_SUCCESS; break; 
			case 88200: if (cm->formatMask & FMT_882_MULTI_PCM) return STATUS_SUCCESS; break; 
			case 96000: if (cm->formatMask & FMT_960_MULTI_PCM) return STATUS_SUCCESS; break; 
		} 
		return STATUS_INVALID_PARAMETER; 
	} 
	if (AC3) { 
		switch (sampleRate) { 
			case 44100: if (cm->formatMask & FMT_441_DOLBY) return STATUS_SUCCESS; break; 
			case 48000: if (cm->formatMask & FMT_480_DOLBY) return STATUS_SUCCESS; break; 
			case 88200: if (cm->formatMask & FMT_882_DOLBY) return STATUS_SUCCESS; break; 
			case 96000: if (cm->formatMask & FMT_960_DOLBY) return STATUS_SUCCESS; break; 
		} 
		return STATUS_INVALID_PARAMETER; 
	} 
	switch (sampleRate) { 
		case 44100: if (cm->formatMask & FMT_441_PCM) return STATUS_SUCCESS; break; 
		case 48000: if (cm->formatMask & FMT_480_PCM) return STATUS_SUCCESS; break; 
		case 88200: if (cm->formatMask & FMT_882_PCM) return STATUS_SUCCESS; break; 
		case 96000: if (cm->formatMask & FMT_960_PCM) return STATUS_SUCCESS; break; 
	} 
	return STATUS_INVALID_PARAMETER; 
} 
 
NTSTATUS CMiniportWaveCMI::validateFormat(PKSDATAFORMAT format, ULONG PinID, BOOLEAN capture) 
{ 
	PAGED_CODE(); 
	ASSERT(format); 
	DBGPRINT(("CMiniportWaveCMI[%p]::validateFormat(%p, %d, %d)", this, format, PinID, capture)); 
 
	PWAVEFORMATEX waveFormat = PWAVEFORMATEX(format + 1); 
	DBGPRINT(("---channels: %d, resolution: %d, sample rate: %d, pin: %d, formatMask: %x", waveFormat->nChannels, waveFormat->wBitsPerSample, waveFormat->nSamplesPerSec, PinID, cm->formatMask)); 
 
//WaveFormatEx 
	if  ( ( format->FormatSize >= sizeof(KSDATAFORMAT_WAVEFORMATEX)) 
	  && IsEqualGUIDAligned(format->MajorFormat,KSDATAFORMAT_TYPE_AUDIO) 
	  && IsEqualGUIDAligned(format->Specifier,KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) ) { 
		switch (EXTRACT_WAVEFORMATEX_ID(&format->SubFormat)) { 
			case WAVE_FORMAT_PCM: 
				if ((PinID != PIN_WAVE_RENDER_SINK) && (PinID != PIN_WAVE_CAPTURE_SOURCE) && (PinID != -1)) { 
					if ((PinID == PIN_WAVE_AC3_RENDER_SINK) && !IoIsWdmVersionAvailable(6,0)) { 
						return STATUS_INVALID_PARAMETER; 
					} 
				} 
 
				if ( ((waveFormat->wBitsPerSample == 16) || (waveFormat->wBitsPerSample == 24)) 
				  && ((waveFormat->nSamplesPerSec == 44100) || (waveFormat->nSamplesPerSec == 48000) || (waveFormat->nSamplesPerSec == 88200) ||  (waveFormat->nSamplesPerSec == 96000)) 
				  && (waveFormat->nChannels == 2) ) { 
					if ((capture) && (waveFormat->nSamplesPerSec > 48000) ) { 
						return STATUS_INVALID_PARAMETER; 
					} 
					return isFormatAllowed(waveFormat->nSamplesPerSec, FALSE, FALSE); 
				} 
				if ( (waveFormat->wBitsPerSample == 16) 
				  && ((waveFormat->nChannels >= 4) && (waveFormat->nChannels <= cm->maxChannels)) 
				  && ((waveFormat->nSamplesPerSec == 44100) || (waveFormat->nSamplesPerSec == 48000)) ) { 
#if OUT_CHANNEL == 1 
					if ((PinID == PIN_WAVE_RENDER_SINK) || (PinID == -1)) { 
						return isFormatAllowed(waveFormat->nSamplesPerSec, TRUE, FALSE); 
					} 
#else 
					return STATUS_INVALID_PARAMETER; 
#endif 
				} 
				break; 
			case WAVE_FORMAT_DOLBY_AC3_SPDIF: 
				if ((PinID != PIN_WAVE_AC3_RENDER_SINK) && (PinID != -1)) { 
					return STATUS_INVALID_PARAMETER; 
				} 
				if ( ((waveFormat->wBitsPerSample >= MIN_BITS_PER_SAMPLE_AC3) && (waveFormat->wBitsPerSample <= MAX_BITS_PER_SAMPLE_AC3)) 
				  && ((waveFormat->nSamplesPerSec >= MIN_SAMPLE_RATE_AC3) && (waveFormat->nSamplesPerSec <= MAX_SAMPLE_RATE_AC3)) 
				  && (waveFormat->nChannels == MAX_CHANNELS_AC3) ) { 
					return isFormatAllowed(waveFormat->nSamplesPerSec, FALSE, TRUE); 
				} 
				break; 
		} 
	} 
 
	return STATUS_INVALID_PARAMETER; 
} 
 
// Tests a data range intersection 
STDMETHODIMP CMiniportWaveCMI::DataRangeIntersection(ULONG PinId, PKSDATARANGE ClientDataRange, PKSDATARANGE MyDataRange, ULONG OutputBufferLength, PVOID ResultantFormat, PULONG ResultantFormatLength) 
{ 
	PAGED_CODE(); 
	DBGPRINT(("CMiniportWaveCMI[%p]::DataRangeIntersection(%d, %p, %p, %d, %p, %p)", this, PinId, ClientDataRange, MyDataRange, OutputBufferLength, ResultantFormat, ResultantFormatLength)); 
 
	if (PinId == PIN_WAVE_AC3_RENDER_SINK) { 
		bool isAC3Pin = true; 
		// Under Windows 2000 and XP, the client's DataRange should be AC3 only. 
		// The AC3 pin is the SPDIF pin in Windows Vista, so 2ch stereo is going to be allowed. 
 
		if (!IsEqualGUIDAligned(ClientDataRange->MajorFormat, KSDATAFORMAT_TYPE_AUDIO) 
		  && !IsEqualGUIDAligned(ClientDataRange->MajorFormat, KSDATAFORMAT_TYPE_WILDCARD)) { 
			return STATUS_NO_MATCH; 
		} 
 
 
		if (!IsEqualGUIDAligned(ClientDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF) 
		  && !IsEqualGUIDAligned(ClientDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD)) { 
			// check for Vista 
			isAC3Pin = false; 
			if (IoIsWdmVersionAvailable(0x06,0x00)) { 
				if (!IsEqualGUIDAligned(ClientDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_PCM) 
				  && !IsEqualGUIDAligned(ClientDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD)) { 
					return STATUS_NO_MATCH; 
				} 
			} else { 
				return STATUS_NO_MATCH; 
			} 
		} 
 
		if (IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) 
		 || IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WILDCARD)) { 
			*ResultantFormatLength = sizeof(KSDATAFORMAT_WAVEFORMATEX); 
		} else 
		if (IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND)) { 
			*ResultantFormatLength = sizeof(KSDATAFORMAT_DSOUND); 
		} else { 
			return STATUS_NO_MATCH; 
		} 
 
		// Validate return buffer size, if the request is only for the 
		// size of the resultant structure, return it now. 
		if (!OutputBufferLength) { 
			*ResultantFormatLength = sizeof(KSDATAFORMAT_WAVEFORMATEX); 
			return STATUS_BUFFER_OVERFLOW; 
		} else 
		if (OutputBufferLength < sizeof(KSDATAFORMAT_WAVEFORMATEX)) { 
			return STATUS_BUFFER_TOO_SMALL; 
		} 
 
		PKSDATAFORMAT_WAVEFORMATEX  resultantFormatWFX = (PKSDATAFORMAT_WAVEFORMATEX) ResultantFormat; 
		PWAVEFORMATEX               pWaveFormatEx; 
 
		// Return the best (only) available format. 
		resultantFormatWFX->DataFormat.FormatSize = *ResultantFormatLength; 
		resultantFormatWFX->DataFormat.Flags      = 0; 
		resultantFormatWFX->DataFormat.SampleSize = 4; // must match nBlockAlign 
		resultantFormatWFX->DataFormat.Reserved	  = 0; 
 
		resultantFormatWFX->DataFormat.MajorFormat  = KSDATAFORMAT_TYPE_AUDIO; 
		INIT_WAVEFORMATEX_GUID(&resultantFormatWFX->DataFormat.SubFormat, WAVE_FORMAT_DOLBY_AC3_SPDIF); 
 
		// Extra space for the DSound specifier 
		if (IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND)) { 
 
			PKSDATAFORMAT_DSOUND  resultantFormatDSound = (PKSDATAFORMAT_DSOUND)ResultantFormat; 
 
			resultantFormatDSound->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_DSOUND; 
 
			// DSound format capabilities are not expressed 
			// this way in KS, so we express no capabilities. 
			resultantFormatDSound->BufferDesc.Flags   = 0 ; 
			resultantFormatDSound->BufferDesc.Control = 0 ; 
 
			pWaveFormatEx = &resultantFormatDSound->BufferDesc.WaveFormatEx; 
		} else { 
		// WAVEFORMATEX or WILDCARD (WAVEFORMATEX) 
			resultantFormatWFX->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; 
 
			pWaveFormatEx = (PWAVEFORMATEX)((PKSDATAFORMAT)resultantFormatWFX + 1); 
		} 
 
		pWaveFormatEx->nChannels	   = 2; 
		pWaveFormatEx->wBitsPerSample  = 16; // SPDIF 
		pWaveFormatEx->cbSize          = 0; 
		if (isAC3Pin) { 
			pWaveFormatEx->wFormatTag      = WAVE_FORMAT_DOLBY_AC3_SPDIF; 
			pWaveFormatEx->nSamplesPerSec  = 48000; 
		} else { 
			pWaveFormatEx->wFormatTag      = WAVE_FORMAT_PCM; 
			pWaveFormatEx->nSamplesPerSec  = min( ((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumSampleFrequency, MAX_SAMPLE_RATE); 
		} 
		pWaveFormatEx->nBlockAlign     = pWaveFormatEx->nChannels * pWaveFormatEx->wBitsPerSample / 8; 
		pWaveFormatEx->nAvgBytesPerSec = pWaveFormatEx->nSamplesPerSec * pWaveFormatEx->nBlockAlign; 
 
		return STATUS_SUCCESS; 
	} 
	if ((PinId == PIN_WAVE_RENDER_SINK) || (PinId == PIN_WAVE_CAPTURE_SINK)) { 
 
		if (!IsEqualGUIDAligned(ClientDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_PCM) && 
          !IsEqualGUIDAligned(ClientDataRange->SubFormat, KSDATAFORMAT_SUBTYPE_WILDCARD)) { 
			return STATUS_NO_MATCH; 
		} 
 
		if (!IsEqualGUIDAligned(ClientDataRange->MajorFormat, KSDATAFORMAT_TYPE_AUDIO) && 
          !IsEqualGUIDAligned(ClientDataRange->MajorFormat, KSDATAFORMAT_TYPE_WILDCARD)) { 
			return STATUS_NO_MATCH; 
		} 
 
		if (IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WAVEFORMATEX) || 
		  IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_WILDCARD)) { 
			*ResultantFormatLength = sizeof(KSDATAFORMAT_WAVEFORMATEX); 
		} else 
		if (IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND)) { 
			*ResultantFormatLength = sizeof(KSDATAFORMAT_DSOUND); 
		} else { 
			return STATUS_NO_MATCH; 
		} 
 
 
		ULONG sampleRate   = 0; 
		ULONG nMaxChannels = min(((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumChannels, requestedChannelCount); 
 
		// check for Vista 
		if (IoIsWdmVersionAvailable(6,0) && (PinId == PIN_WAVE_RENDER_SINK)) { 
			nMaxChannels = ((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumChannels; 
		} 
		if (nMaxChannels & 0x01) { 
			nMaxChannels--; 
		} 
		if (!nMaxChannels) { 
			return STATUS_NO_MATCH; 
		} 
 
		if (isStreamRunning[PCM_OUT_STREAM]) { 
			sampleRate = stream[PCM_OUT_STREAM]->currentSampleRate; 
		} else 
		if (isStreamRunning[PCM_IN_STREAM]) { 
			sampleRate = stream[PCM_IN_STREAM]->currentSampleRate; 
		} 
		if (sampleRate == 0) { 
			if ((nMaxChannels > 2) && (((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumSampleFrequency > MAX_SAMPLE_RATE_MULTI)) { 
				sampleRate = MAX_SAMPLE_RATE_MULTI; 
			} else 
			if ((nMaxChannels == 2) && (((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumSampleFrequency > MAX_SAMPLE_RATE)) { 
				sampleRate = MAX_SAMPLE_RATE; 
			} else { 
				sampleRate = ((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumSampleFrequency; 
			} 
		} 
 
		if ((((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumSampleFrequency < sampleRate) 
		  || (((PKSDATARANGE_AUDIO)ClientDataRange)->MinimumSampleFrequency > sampleRate)) { 
			return STATUS_NO_MATCH; 
		} 
 
		if (PinId == PIN_WAVE_RENDER_SINK) { 
			if (!OutputBufferLength) { 
				*ResultantFormatLength = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX); 
				return STATUS_BUFFER_OVERFLOW; 
			} else 
			if (OutputBufferLength < sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX)) { 
				return STATUS_BUFFER_TOO_SMALL; 
			} 
 
			if (((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumChannels < 2) { 
				DBGPRINT(("[[DataRangeIntersection] mono format not supported")); 
				return STATUS_NO_MATCH; 
			} 
 
			PWAVEFORMATPCMEX WaveFormat = (PWAVEFORMATPCMEX)((PKSDATAFORMAT)ResultantFormat + 1); 
    	    if (IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND)) { 
				return STATUS_NOT_SUPPORTED; 
			} 
			*(PKSDATAFORMAT)ResultantFormat = *MyDataRange; 
			((PKSDATAFORMAT)ResultantFormat)->FormatSize = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX); 
 
 
			WaveFormat->Format.wFormatTag      = WAVE_FORMAT_EXTENSIBLE; 
			WaveFormat->SubFormat              = KSDATAFORMAT_SUBTYPE_PCM; 
			WaveFormat->Format.nChannels       = (WORD)nMaxChannels; 
			WaveFormat->Format.wBitsPerSample  = 16; 
			WaveFormat->Format.nBlockAlign     = (WaveFormat->Format.wBitsPerSample >> 3) * WaveFormat->Format.nChannels; 
			WaveFormat->Format.nAvgBytesPerSec = WaveFormat->Format.nSamplesPerSec * WaveFormat->Format.nBlockAlign; 
			WaveFormat->Format.cbSize          = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); 
			WaveFormat->Format.nSamplesPerSec  = sampleRate; 
			WaveFormat->Samples.wValidBitsPerSample = WaveFormat->Format.wBitsPerSample; 
			switch (nMaxChannels) { 
				case 8: WaveFormat->dwChannelMask = KSAUDIO_SPEAKER_7POINT1; break; 
				case 6: WaveFormat->dwChannelMask = KSAUDIO_SPEAKER_5POINT1; break; 
				case 4: WaveFormat->dwChannelMask = KSAUDIO_SPEAKER_QUAD;    break; 
				case 2: WaveFormat->dwChannelMask = KSAUDIO_SPEAKER_STEREO;  break; 
			} 
			if (nMaxChannels == requestedChannelCount) { 
				WaveFormat->dwChannelMask = requestedChannelMask; 
			} 
			((PKSDATAFORMAT)ResultantFormat)->SampleSize = WaveFormat->Format.nBlockAlign; 
 
			*ResultantFormatLength = sizeof(KSDATAFORMAT) + sizeof(WAVEFORMATPCMEX); 
			DBGPRINT(("[DataRangeIntersection] MultiChannel Renderer: SampleRate: %d, ClientDataRange->MaxChans: %d, Channels: %d, BitPerSample: %d, BlockAlign: %d, AvgBytesPerSec: %d, ChannelMask: %08X", WaveFormat->Format.nSamplesPerSec, ((PKSDATARANGE_AUDIO)ClientDataRange)->MaximumChannels, WaveFormat->Format.nChannels, WaveFormat->Format.wBitsPerSample, WaveFormat->Format.nBlockAlign, WaveFormat->Format.nAvgBytesPerSec, WaveFormat->dwChannelMask)); 
		} else 
		if (PinId == PIN_WAVE_CAPTURE_SINK) { 
			PKSDATAFORMAT_WAVEFORMATEX  resultantFormatWFX; 
			PWAVEFORMATEX               pWaveFormatEx; 
 
			if (!OutputBufferLength) { 
				*ResultantFormatLength = sizeof(KSDATAFORMAT_WAVEFORMATEX); 
				return STATUS_BUFFER_OVERFLOW; 
			} else 
			if (OutputBufferLength < sizeof(KSDATAFORMAT_WAVEFORMATEX)) { 
				return STATUS_BUFFER_TOO_SMALL; 
			} 
 
			if (nMaxChannels > 2) { 
				nMaxChannels = 2; 
			} 
 
			resultantFormatWFX = (PKSDATAFORMAT_WAVEFORMATEX) ResultantFormat; 
			resultantFormatWFX->DataFormat.FormatSize   = *ResultantFormatLength; 
			resultantFormatWFX->DataFormat.Flags        = 0; 
			resultantFormatWFX->DataFormat.SampleSize   = 4; 
			resultantFormatWFX->DataFormat.Reserved     = 0; 
			resultantFormatWFX->DataFormat.MajorFormat  = KSDATAFORMAT_TYPE_AUDIO; 
			INIT_WAVEFORMATEX_GUID(&resultantFormatWFX->DataFormat.SubFormat, WAVE_FORMAT_PCM); 
 
    	    if (IsEqualGUIDAligned(ClientDataRange->Specifier, KSDATAFORMAT_SPECIFIER_DSOUND)) { 
				PKSDATAFORMAT_DSOUND resultantFormatDSound; 
				resultantFormatDSound = (PKSDATAFORMAT_DSOUND)ResultantFormat; 
				resultantFormatDSound->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_DSOUND; 
				resultantFormatDSound->BufferDesc.Flags   = 0 ; 
				resultantFormatDSound->BufferDesc.Control = 0 ; 
				pWaveFormatEx = &resultantFormatDSound->BufferDesc.WaveFormatEx; 
			} else { 
				resultantFormatWFX->DataFormat.Specifier = KSDATAFORMAT_SPECIFIER_WAVEFORMATEX; 
				pWaveFormatEx = (PWAVEFORMATEX)((PKSDATAFORMAT)resultantFormatWFX + 1); 
			} 
			pWaveFormatEx->wFormatTag      = WAVE_FORMAT_PCM; 
			pWaveFormatEx->nChannels       = nMaxChannels; 
			pWaveFormatEx->nSamplesPerSec  = sampleRate; 
			pWaveFormatEx->wBitsPerSample  = 16; 
			pWaveFormatEx->cbSize          = 0; 
			pWaveFormatEx->nBlockAlign     = 4; 
			pWaveFormatEx->nAvgBytesPerSec = 192000; 
		} 
		return STATUS_SUCCESS; 
	} 
	return STATUS_NO_MATCH; 
} 
 
//from IMiniportWaveCyclic::NewStream() 
#ifdef WAVERT 
STDMETHODIMP CMiniportWaveCMI::NewStream(PMINIPORTWAVERTSTREAM *OutStream, PPORTWAVERTSTREAM OuterUnknown, ULONG PinID, BOOLEAN Capture, PKSDATAFORMAT DataFormat) 
#else 
STDMETHODIMP CMiniportWaveCMI::NewStream(PMINIPORTWAVECYCLICSTREAM *OutStream, PUNKNOWN OuterUnknown, POOL_TYPE PoolType, ULONG PinID, BOOLEAN Capture, PKSDATAFORMAT DataFormat, PDMACHANNEL* OutDmaChannel, PSERVICEGROUP* OutServiceGroup) 
#endif 
{ 
	PAGED_CODE(); 
	ASSERT(OutStream); 
	ASSERT(DataFormat); 
#ifdef WAVERT 
	DBGPRINT(("CMiniportWaveCMI[%p]::NewStream(%p, %p, %d, %d, %p)", this, OutStream, OuterUnknown, PinID, Capture, DataFormat)); 
#else 
	ASSERT(OutDmaChannel); 
	ASSERT(OutServiceGroup); 
	DBGPRINT(("CMiniportWaveCMI[%p]::NewStream(%p, %p, %p, %d, %d, %p, %p, %p)", this, OutStream, OuterUnknown, PoolType, PinID, Capture, DataFormat, OutDmaChannel, OutServiceGroup)); 
#endif 
 
	NTSTATUS      ntStatus    = STATUS_SUCCESS; 
	PWAVEFORMATEX waveFormat  = PWAVEFORMATEX(DataFormat + 1); 
	UInt32        streamIndex = PCM_OUT_STREAM; 
 
	ntStatus = validateFormat(DataFormat, PinID, Capture); 
	if (!NT_SUCCESS(ntStatus)) { 
		DBGPRINT(("invalid stream format")); 
		return STATUS_UNSUCCESSFUL; 
	} 
 
	CMIAdapter->setUInt8Bit(REG_MIXER1, EN_SPDI2DAC); 
 
	if (Capture) { 
		streamIndex = PCM_IN_STREAM; 
	} else if (WAVE_FORMAT_DOLBY_AC3_SPDIF == EXTRACT_WAVEFORMATEX_ID(&DataFormat->SubFormat)) { 
		streamIndex = AC3_OUT_STREAM; 
	} 
 
	// make sure the hardware is not already in use 
	if (isStreamRunning[streamIndex]) { 
		DBGPRINT(("Stream %d running, exiting...", streamIndex)); 
   		return STATUS_UNSUCCESSFUL; 
	} 
	if ((streamIndex == AC3_OUT_STREAM) && isStreamRunning[PCM_OUT_STREAM]) { 
   		stream[PCM_OUT_STREAM]->SetState(KSSTATE_STOP_AC3); 
	} 
	if ((streamIndex == PCM_OUT_STREAM) && isStreamRunning[AC3_OUT_STREAM]) { 
   		return STATUS_UNSUCCESSFUL; 
	} 
 
	DBGPRINT(("---StreamNo: %d, Bits: %d, Sample Rate: %d, Channels: %d, AC3: %d", streamIndex, 
		waveFormat->wBitsPerSample, waveFormat->nSamplesPerSec, waveFormat->nChannels, 
		(WAVE_FORMAT_DOLBY_AC3_SPDIF == EXTRACT_WAVEFORMATEX_ID(&DataFormat->SubFormat)))); 
 
	// the DAC and ADC can only run at the same sample rate simultaneously 
	if ((streamIndex == PCM_IN_STREAM) && isStreamRunning[PCM_OUT_STREAM]) { 
   		if (waveFormat->nSamplesPerSec != stream[PCM_OUT_STREAM]->currentSampleRate) { 
   			return STATUS_UNSUCCESSFUL; 
		} 
	} 
	if ((streamIndex == PCM_OUT_STREAM) && isStreamRunning[PCM_IN_STREAM]) { 
   		if (waveFormat->nSamplesPerSec != stream[PCM_IN_STREAM]->currentSampleRate) { 
   			return STATUS_UNSUCCESSFUL; 
		} 
	} 
 
	// instantiate a stream 
#ifdef WAVERT 
	ntStatus = CreateMiniportWaveStreamCMI(&stream[streamIndex], OuterUnknown, NonPagedPool); 
#else 
	ntStatus = CreateMiniportWaveStreamCMI(&stream[streamIndex], OuterUnknown, PoolType); 
#endif 
	if (!NT_SUCCESS (ntStatus)) { 
		DBGPRINT(("Failed to create stream")); 
		return ntStatus; 
	} 
 
	// initialize it 
#ifdef WAVERT 
	ntStatus = stream[streamIndex]->Init(this, streamIndex, Capture, DataFormat, OuterUnknown); 
#else 
	ntStatus = stream[streamIndex]->Init(this, streamIndex, Capture, DataFormat, DMAChannel[streamIndex], OutServiceGroup); 
#endif 
	if (!NT_SUCCESS(ntStatus)) { 
		DBGPRINT(("Failed to init stream")); 
		stream[streamIndex]->Release(); 
		stream[streamIndex] = NULL; 
		*OutStream          = NULL; 
#ifndef WAVERT 
		*OutServiceGroup    = NULL; 
		*OutDmaChannel      = NULL; 
#endif 
		return ntStatus; 
	} 
 
#ifdef WAVERT 
//this has been referenced in CreateMiniportWaveStreamCMI() already 
	*OutStream = (PMINIPORTWAVERTSTREAM)stream[streamIndex]; 
#else 
	*OutDmaChannel = DMAChannel[streamIndex]; 
	DMAChannel[streamIndex]->AddRef(); 
	*OutStream = (PMINIPORTWAVECYCLICSTREAM)stream[streamIndex]; 
#endif 
 
	return ntStatus; 
} 
 
static NTSTATUS PropertyHandler_ChannelConfig(PPCPROPERTY_REQUEST PropertyRequest) 
{ 
	PAGED_CODE(); 
	ASSERT(PropertyRequest); 
	DBGPRINT(("[PropertyHandler_ChannelConfig]")); 
 
#ifdef WAVERT 
	CMiniportWaveCMI *that = (CMiniportWaveCMI *) ((PMINIPORTWAVERT)PropertyRequest->MajorTarget); 
#else 
	CMiniportWaveCMI *that = (CMiniportWaveCMI *) ((PMINIPORTWAVECYCLIC)PropertyRequest->MajorTarget); 
#endif 
 
	if (PropertyRequest->Node == KSNODE_WAVE_DAC) { 
 
		if (PropertyRequest->ValueSize == 0) { 
			PropertyRequest->ValueSize = sizeof(LONG); 
			return STATUS_BUFFER_OVERFLOW; 
		} else if (PropertyRequest->ValueSize < sizeof (LONG)) { 
			PropertyRequest->ValueSize = 0; 
			return STATUS_BUFFER_TOO_SMALL; 
		} 
 
		if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET) { 
			*(PLONG)PropertyRequest->Value = that->requestedChannelMask; 
			PropertyRequest->ValueSize = sizeof(ULONG); 
			return STATUS_SUCCESS; 
		} else if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET) { 
			if (*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_7POINT1) { 
				that->requestedChannelMask =  *(PLONG)PropertyRequest->Value; 
				that->requestedChannelCount = 8; 
				return STATUS_SUCCESS; 
			} 
			if (*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_5POINT1) { 
				that->requestedChannelMask =  *(PLONG)PropertyRequest->Value; 
				that->requestedChannelCount = 6; 
				return STATUS_SUCCESS; 
			} 
			if ((*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_QUAD) || (*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_SURROUND)) { 
				that->requestedChannelMask =  *(PLONG)PropertyRequest->Value; 
				that->requestedChannelCount = 4; 
				return STATUS_SUCCESS; 
			} 
			if (*(PLONG)PropertyRequest->Value == KSAUDIO_SPEAKER_STEREO) { 
				that->requestedChannelMask =  *(PLONG)PropertyRequest->Value; 
				that->requestedChannelCount = 2; 
				return STATUS_SUCCESS; 
			} 
		} else if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT) { 
			PULONG AccessFlags = PULONG(PropertyRequest->Value); 
			*AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_SET; 
			PropertyRequest->ValueSize = sizeof(ULONG); 
			return STATUS_SUCCESS; 
		} 
	} 
	return STATUS_INVALID_PARAMETER; 
} 
 
/////////////////////////// 
 
NTSTATUS CreateMiniportWaveStreamCMI(CMiniportWaveStreamCMI  **MiniportWaveStreamCMI, PUNKNOWN pUnknownOuter, POOL_TYPE PoolType) 
{ 
	PAGED_CODE(); 
	DBGPRINT(("CreateMiniportWaveStreamCMI")); 
 
#ifdef WAVERT 
	*MiniportWaveStreamCMI = new (PoolType, 'gnaa') CMiniportWaveStreamCMI(NULL); 
#else 
	*MiniportWaveStreamCMI = new (PoolType, 'gnaa') CMiniportWaveStreamCMI(pUnknownOuter); 
#endif 
	if (*MiniportWaveStreamCMI) { 
		(*MiniportWaveStreamCMI)->AddRef(); 
		return STATUS_SUCCESS; 
	} 
 
	return STATUS_INSUFFICIENT_RESOURCES; 
} 
 
NTSTATUS CMiniportWaveStreamCMI::prepareStream() 
{ 
	PAGED_CODE(); 
	DBGPRINT(("CMiniportWaveStreamCMI[%p]::prepareStream()", this)); 
	DBGPRINT(("---streamIndex: %d, channelNumber: %d", streamIndex, channelNumber)); 
 
	NTSTATUS ntStatus; 
	UInt8    reg; 
	UInt32   val; 
 
	if (state == KSSTATE_RUN) { 
		return STATUS_INVALID_DEVICE_REQUEST; 
	} 
 
	if (!(Miniport->cm)) { 
		DBGPRINT(("Miniport not set")); 
		return STATUS_INVALID_DEVICE_REQUEST; 
	} 
 
	enableSPDIF = ((currentSampleRate == 44100 || currentSampleRate == 48000 || currentSampleRate == 88200 || currentSampleRate == 96000) && 
		           ((currentResolution == 16) || (currentResolution == 24)) && (currentChannelCount == 2)) && 
		           (Miniport->cm->enableSPDIFOut); 
 
	ntStatus = setupSPDIFPlayback(enableSPDIF); 
	if (!NT_SUCCESS(ntStatus)) { 
		return ntStatus; 
	} 
 
	if (!isCaptureStream) { 
		ntStatus = setDACChannels(); 
		if (!NT_SUCCESS(ntStatus)) { 
			return ntStatus; 
		} 
	} 
 
	KeWaitForSingleObject(&Miniport->mutex, Executive, KernelMode, false, NULL); 
 
	val = channelNumber ? ADC_CH1 : ADC_CH0; 
	if (isCaptureStream) { 
		Miniport->cm->regFUNCTRL0 |= val;  // 1->Recording 
	} else { 
		Miniport->cm->regFUNCTRL0 &= ~val; // 0->Playback 
	} 
	Miniport->CMIAdapter->writeUInt32(REG_FUNCTRL0, Miniport->cm->regFUNCTRL0); 
 
	//set sampling frequency 
	val = Miniport->CMIAdapter->readUInt32(REG_FUNCTRL1); 
	if ((currentSampleRate == 88200) || (currentSampleRate == 44100)) { 
		if (channelNumber) { 
			val &= ~SFC_CH1_MASK; 
			val |= SFC_44K_CH1; 
		} else { 
			val &= ~SFC_CH0_MASK; 
			val |= SFC_44K_CH0; 
		} 
	} else if ((currentSampleRate == 96000) || (currentSampleRate == 48000)) { 
		if (channelNumber) { 
			val &= ~SFC_CH1_MASK; 
			val |= SFC_48K_CH1; 
		} else { 
			val &= ~SFC_CH0_MASK; 
			val |= SFC_48K_CH0; 
		} 
	} else { 
			KeReleaseMutex(&Miniport->mutex, FALSE); 
			return STATUS_INVALID_DEVICE_REQUEST; 
	} 
	Miniport->CMIAdapter->writeUInt32(REG_FUNCTRL1, val); 
 
	//set resolution 
	val = Miniport->CMIAdapter->readUInt32(REG_CHFORMAT); 
	if (channelNumber) { 
		val |= FORMAT_CH1; 
	} else { 
		val |= FORMAT_CH0; 
	} 
	Miniport->CMIAdapter->writeUInt32(REG_CHFORMAT, val); 
 
	KeReleaseMutex(&Miniport->mutex, false); 
 
	return STATUS_SUCCESS; 
} 
 
NTSTATUS CMiniportWaveStreamCMI::setDACChannels() 
{ 
	PAGED_CODE(); 
	DBGPRINT(("CMiniportWaveStreamCMI[%p]::setDACChannels()", this)); 
	NTSTATUS ntStatus = STATUS_SUCCESS; 
 
	if (currentChannelCount > 2) { 
		if (Miniport->cm->maxChannels < currentChannelCount) { 
			return STATUS_INVALID_DEVICE_REQUEST; 
		} 
		if ((currentResolution != 16) || (currentChannelCount < 2)) { 
			return STATUS_INVALID_DEVICE_REQUEST; 
		} 
#if OUT_CHANNEL == 0 
		return STATUS_INVALID_DEVICE_REQUEST; 
#endif 
		KeWaitForSingleObject(&Miniport->mutex, Executive, KernelMode, FALSE, NULL); 
		Miniport->CMIAdapter->setUInt32Bit(REG_LEGACY, DWORD_MAPPING); 
		Miniport->CMIAdapter->setUInt32Bit(REG_MISCCTRL, XCHG_DAC); 
 
		switch (currentChannelCount) { 
			case 4: 
				Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, EN_4CH_CH1); 
				Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, EN_5CH_CH1); 
				Miniport->CMIAdapter->clearUInt32Bit(REG_LEGACY, EN_6CH_CH1); 
				Miniport->CMIAdapter->clearUInt32Bit(REG_MISCCTRL, EN_CENTER); 
				break; 
			case 6: 
				Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, EN_4CH_CH1); 
				Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, EN_5CH_CH1); 
				Miniport->CMIAdapter->setUInt32Bit(REG_LEGACY, EN_6CH_CH1); 
				Miniport->CMIAdapter->setUInt32Bit(REG_MISCCTRL, EN_CENTER); 
				break; 
			case 8: 
				if (Miniport->cm->chipVersion == 68) { 
					Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, EN_4CH_CH1); 
					Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, EN_5CH_CH1); 
					Miniport->CMIAdapter->setUInt32Bit(REG_LEGACY, EN_6CH_CH1); 
					Miniport->CMIAdapter->setUInt32Bit(REG_MISCCTRL, EN_CENTER); 
					Miniport->CMIAdapter->setUInt32Bit(REG_MISCCTRL2, EN_8CH_CH1); 
					break; 
				} else { 
					ntStatus = STATUS_INVALID_DEVICE_REQUEST; 
				} 
			default: 
				ntStatus = STATUS_INVALID_DEVICE_REQUEST; 
		} 
		KeReleaseMutex(&Miniport->mutex, FALSE); 
	} else { 
		if (Miniport->cm->canMultiChannel) { 
			KeWaitForSingleObject(&Miniport->mutex, Executive, KernelMode, FALSE, NULL); 
			Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, EN_5CH_CH1 | EN_4CH_CH1); 
			Miniport->CMIAdapter->clearUInt32Bit(REG_LEGACY, EN_6CH_CH1 | DWORD_MAPPING); 
			Miniport->CMIAdapter->clearUInt32Bit(REG_MISCCTRL, EN_CENTER); 
			Miniport->CMIAdapter->clearUInt32Bit(REG_MISCCTRL, XCHG_DAC); 
			if (Miniport->cm->chipVersion == 68) { 
				Miniport->CMIAdapter->clearUInt32Bit(REG_MISCCTRL2, EN_8CH_CH1); 
			} 
			KeReleaseMutex(&Miniport->mutex, FALSE); 
		} 
	} 
	return ntStatus; 
} 
 
NTSTATUS CMiniportWaveStreamCMI::setupSPDIFPlayback(bool enableSPDIF) 
{ 
	PAGED_CODE(); 
	DBGPRINT(("CMiniportWaveStreamCMI[%p]::setupSPDIFPlayback(%d)", this, enableSPDIF)); 
 
	NTSTATUS ntStatus; 
 
	KeWaitForSingleObject(&Miniport->mutex, Executive, KernelMode, false, NULL); 
 
	if (enableSPDIF) { 
		Miniport->CMIAdapter->setUInt32Bit(REG_LEGACY, EN_SPDIF_OUT); 
		Miniport->CMIAdapter->setUInt32Bit(REG_FUNCTRL1, SPDO2DAC); 
#if OUT_CHANNEL == 0 
		Miniport->CMIAdapter->setUInt32Bit(REG_FUNCTRL1, SPDF_0); 
#else 
		Miniport->CMIAdapter->setUInt32Bit(REG_FUNCTRL1, SPDF_1); 
#endif 
		setupAC3Passthru(); 
 
		if ( (currentSampleRate == 48000) || (currentSampleRate == 96000) ) { 
			Miniport->CMIAdapter->setUInt32Bit(REG_MISCCTRL, EN_SPDIF_48); 
		} else { 
			Miniport->CMIAdapter->clearUInt32Bit(REG_MISCCTRL, EN_SPDIF_48); 
		} 
 
		if (currentSampleRate == 96000) { 
#if OUT_CHANNEL == 0 
			Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, SPD96_CH0); 
#else 
			Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, SPD96_CH1); 
#endif 
			Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, DBLSPDS); 
		} else if (currentSampleRate == 88200) { 
#if OUT_CHANNEL == 0 
			Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, SPD88_CH0); 
#else 
			Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, SPD88_CH1); 
#endif 
			Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, DBLSPDS); 
		} else { 
#if OUT_CHANNEL == 0 
			Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, SPD88_CH0 | SPD96_CH0); 
#else 
			Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, SPD88_CH1 | SPD96_CH1); 
#endif 
			Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, DBLSPDS); 
		} 
 
	} else { 
		Miniport->CMIAdapter->clearUInt32Bit(REG_LEGACY, EN_SPDIF_OUT); 
		Miniport->CMIAdapter->clearUInt32Bit(REG_FUNCTRL1, SPDO2DAC); 
#if OUT_CHANNEL == 0 
		Miniport->CMIAdapter->clearUInt32Bit(REG_FUNCTRL1, SPDF_0); 
#else 
		Miniport->CMIAdapter->clearUInt32Bit(REG_FUNCTRL1, SPDF_1); 
#endif 
#if OUT_CHANNEL == 0 
		Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, SPD88_CH0 | SPD96_CH0); 
#else 
		Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, SPD88_CH1 | SPD96_CH1); 
#endif 
		Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, DBLSPDS); 
		setupAC3Passthru(); 
	} 
 
	KeReleaseMutex(&Miniport->mutex, false); 
	return STATUS_SUCCESS; 
} 
 
NTSTATUS CMiniportWaveStreamCMI::setupAC3Passthru() 
{ 
	PAGED_CODE(); 
	DBGPRINT(("CMiniportWaveStreamCMI[%p]::setupAC3Passthru()")); 
 
	if (enableAC3Passthru) { 
		Miniport->CMIAdapter->writeUInt8(REG_MIXER1, Miniport->CMIAdapter->readUInt8(REG_MIXER1) | MUTE_WAVE); 
 
		Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, EN_SPDO_AC3_1); 
		Miniport->CMIAdapter->setUInt32Bit(REG_MISCCTRL, EN_SPDO_AC3_2); 
 
		if (Miniport->cm->canAC3HW) { 
			Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, EN_SPDO_AC3_3); 
			Miniport->CMIAdapter->clearUInt32Bit(REG_MISCCTRL, SPD32SEL); 
			if (Miniport->cm->chipVersion >= 39) { 
				Miniport->CMIAdapter->clearUInt8Bit(REG_MIXER1, EN_SPDI2DAC); 
			} 
		} else { 
			Miniport->CMIAdapter->setUInt32Bit(REG_MISCCTRL, SPD32SEL); 
			if (Miniport->cm->chipVersion == 33) { 
				if (currentSampleRate >= 48000) { 
#if OUT_CHANNEL == 0 
					 Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, SPD96_CH0); 
#else 
					 Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, SPD96_CH1); 
#endif 
				} else { 
#if OUT_CHANNEL == 0 
					 Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, SPD96_CH0); 
#else 
					 Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, SPD96_CH1); 
#endif 
				} 
			} 
		} 
	} else { 
		Miniport->CMIAdapter->setUInt8Bit(REG_MIXER1, EN_SPDI2DAC); 
 
		Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, EN_SPDO_AC3_1); 
		Miniport->CMIAdapter->clearUInt32Bit(REG_MISCCTRL, EN_SPDO_AC3_2); 
 
		if (Miniport->cm->canAC3HW) { 
			Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, EN_SPDO_AC3_3); 
			if (currentResolution > 16) { 
				Miniport->CMIAdapter->setUInt32Bit(REG_MISCCTRL, SPD32SEL); 
				Miniport->CMIAdapter->setUInt32Bit(REG_CHFORMAT, SPD24SEL); 
			} else { 
				Miniport->CMIAdapter->clearUInt32Bit(REG_MISCCTRL, SPD32SEL); 
				Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, SPD24SEL); 
			} 
		} else { 
			Miniport->CMIAdapter->clearUInt32Bit(REG_MISCCTRL, SPD32SEL); 
#if OUT_CHANNEL == 0 
			Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, SPD96_CH0); 
#else 
			Miniport->CMIAdapter->clearUInt32Bit(REG_CHFORMAT, SPD96_CH1); 
#endif 
		} 
	} 
	return STATUS_SUCCESS; 
} 
 
CMiniportWaveStreamCMI::~CMiniportWaveStreamCMI(void) 
{ 
	PAGED_CODE(); 
 
	DBGPRINT(("CMiniportWaveStreamCMI[%p]::~CMiniportWaveStreamCMI", this)); 
 
#ifdef WAVERT 
	if (Port) { 
		Port->Release(); 
		Port = NULL; 
	} 
#else 
	if (DMAChannel) { 
		DMAChannel->Release(); 
		DMAChannel = NULL; 
	} 
 
	if (ServiceGroup) { 
		ServiceGroup->Release(); 
		ServiceGroup = NULL; 
	} 
#endif 
 
	Miniport->isStreamRunning[streamIndex] = false; 
 
	if ((streamIndex == AC3_OUT_STREAM) && Miniport->isStreamRunning[PCM_OUT_STREAM]) { 
		KSSTATE temp = Miniport->stream[PCM_OUT_STREAM]->state; 
		Miniport->stream[PCM_OUT_STREAM]->state = KSSTATE_STOP; 
		Miniport->stream[PCM_OUT_STREAM]->prepareStream(); 
		Miniport->stream[PCM_OUT_STREAM]->SetState(KSSTATE_ACQUIRE); 
		Miniport->stream[PCM_OUT_STREAM]->state = temp; 
		Miniport->stream[PCM_OUT_STREAM]->SetState(KSSTATE_RUN_AC3); 
	} 
 
	if (Miniport) { 
		Miniport->Release(); 
		Miniport = NULL; 
    } 
} 
 
STDMETHODIMP CMiniportWaveStreamCMI::NonDelegatingQueryInterface(REFIID Interface, PVOID *Object) 
{ 
	PAGED_CODE(); 
	ASSERT(Object); 
	DBGPRINT(("CMiniportWaveStreamCMI[%p]::NonDelegatingQueryInterface(%p, %p)", this, Interface, Object)); 
 
	if (IsEqualGUIDAligned(Interface,IID_IUnknown)) { 
		*Object = PVOID(PUNKNOWN(PMINIPORTWAVECYCLICSTREAM(this))); 
#ifdef WAVERT 
	} else if (IsEqualGUIDAligned(Interface,IID_IMiniportWaveRTStream)) { 
		*Object = PVOID(PMINIPORTWAVERTSTREAM(this)); 
#else 
	} else if (IsEqualGUIDAligned(Interface,IID_IMiniportWaveCyclicStream)) { 
		*Object = PVOID(PMINIPORTWAVECYCLICSTREAM(this)); 
#endif 
	} else if (IsEqualGUIDAligned (Interface, IID_IDrmAudioStream)) { 
		*Object = (PVOID)(PDRMAUDIOSTREAM(this)); 
	} else { 
		*Object = NULL; 
	} 
 
	if (*Object) { 
		PUNKNOWN(*Object)->AddRef(); 
		return STATUS_SUCCESS; 
	} 
 
	return STATUS_INVALID_PARAMETER; 
} 
 
#ifdef WAVERT 
NTSTATUS CMiniportWaveStreamCMI::Init(CMiniportWaveCMI* Miniport_, UInt32 streamIndex_, bool isCaptureStream_, PKSDATAFORMAT DataFormat, PPORTWAVERTSTREAM Port_) 
#else 
NTSTATUS CMiniportWaveStreamCMI::Init(CMiniportWaveCMI* Miniport_, UInt32 streamIndex_, bool isCaptureStream_, PKSDATAFORMAT DataFormat, PDMACHANNEL DMAChannel_, PSERVICEGROUP* OutServiceGroup) 
#endif 
{ 
	PAGED_CODE(); 
	ASSERT(Miniport_); 
	ASSERT(DataFormat); 
 
	NTSTATUS ntStatus; 
 
#ifdef WAVERT 
    ASSERT(Port_); 
	DBGPRINT(("CMiniportWaveStreamCMI[%p]::Init(%p, %d, %d, %p, %p)", this, Miniport_, streamIndex_, isCaptureStream_, DataFormat, Port_)); 
	Port = Port_; 
	Port->AddRef(); 
#else 
	DBGPRINT(("CMiniportWaveStreamCMI[%p]::Init(%p, %d, %d, %p, %p, %p)", this, Miniport_, streamIndex_, isCaptureStream_, DataFormat, DMAChannel_, OutServiceGroup)); 
	DMAChannel = DMAChannel_; 
	DMAChannel->AddRef(); 
#endif 
 
	Miniport = Miniport_; 
	Miniport->AddRef(); 
 
	streamIndex     = streamIndex_; 
	isCaptureStream = isCaptureStream_; 
	state           = KSSTATE_STOP; 
 
	if ( (streamIndex == PCM_OUT_STREAM) || (streamIndex == AC3_OUT_STREAM) ) { 
		channelNumber = OUT_CHANNEL; 
	} else { 
		channelNumber = IN_CHANNEL; 
	} 
 
#ifndef WAVERT 
	ntStatus = PcNewServiceGroup(&ServiceGroup,NULL); 
	if (!NT_SUCCESS(ntStatus)) { 
		DBGPRINT(("PcNewServiceGroup() or NewMasterDmaChannel() failed")); 
		return ntStatus; 
	} 
	*OutServiceGroup = ServiceGroup; 
	ServiceGroup->AddRef(); 
#endif 
 
	ntStatus = SetFormat(DataFormat); 
	if (!NT_SUCCESS(ntStatus)) { 
		DBGPRINT(("SetFormat() failed")); 
		return ntStatus; 
	} 
 
	Miniport->isStreamRunning[streamIndex] = true; 
 
	return ntStatus; 
} 
 
NTSTATUS CMiniportWaveStreamCMI::SetFormat(PKSDATAFORMAT Format) 
{ 
	PAGED_CODE(); 
	DBGPRINT(("CMiniportWaveStreamCMI[%p]::SetFormat(%p)", this, Format)); 
	PWAVEFORMATEX waveFormat = PWAVEFORMATEX(Format + 1); 
	NTSTATUS ntStatus = Miniport->validateFormat(Format, -1, isCaptureStream); 
	if (!NT_SUCCESS(ntStatus)) { 
		return ntStatus; 
	} 
	// the DAC and ADC can only run at the same sample rate simultaneously 
	if ((streamIndex == PCM_IN_STREAM) && Miniport->isStreamRunning[PCM_OUT_STREAM]) { 
   		if (waveFormat->nSamplesPerSec != Miniport->stream[PCM_OUT_STREAM]->currentSampleRate) { 
   			return STATUS_UNSUCCESSFUL; 
		} 
	} 
	if ((streamIndex == PCM_IN_STREAM) && Miniport->isStreamRunning[AC3_OUT_STREAM]) { 
   		if (waveFormat->nSamplesPerSec != Miniport->stream[AC3_OUT_STREAM]->currentSampleRate) { 
   			return STATUS_UNSUCCESSFUL; 
		} 
	} 
	if ((streamIndex == PCM_OUT_STREAM) && Miniport->isStreamRunning[PCM_IN_STREAM]) { 
   		if (waveFormat->nSamplesPerSec != Miniport->stream[PCM_IN_STREAM]->currentSampleRate) { 
   			return STATUS_UNSUCCESSFUL; 
		} 
	} 
	if ((streamIndex == PCM_OUT_STREAM) && Miniport->isStreamRunning[AC3_OUT_STREAM]) { 
		return STATUS_UNSUCCESSFUL; 
	} 
 
	KeWaitForSingleObject(&Miniport->mutex, Executive, KernelMode, false, NULL); 
	currentSampleRate   = waveFormat->nSamplesPerSec; 
	currentChannelCount = waveFormat->nChannels; 
	currentResolution   = waveFormat->wBitsPerSample; 
	enableAC3Passthru   = (WAVE_FORMAT_DOLBY_AC3_SPDIF == EXTRACT_WAVEFORMATEX_ID(&Format->SubFormat)); 
	KeReleaseMutex(&Miniport->mutex, false); 
	ntStatus = prepareStream(); 
 
	return ntStatus; 
} 
 
// DRM crap - we're supposed to disable every digital interface here 
STDMETHODIMP_(NTSTATUS) CMiniportWaveStreamCMI::SetContentId(ULONG contentId, PCDRMRIGHTS drmRights) 
{ 
	PAGED_CODE(); 
	DBGPRINT(("CMiniportWaveStreamCMI[%p]::SetContentId(%d, %p)", this, contentId, drmRights)); 
 
	return STATUS_SUCCESS; 
} 
 
#ifdef WAVERT 
 
STDMETHODIMP_(NTSTATUS) CMiniportWaveStreamCMI::AllocateAudioBuffer(ULONG size, PMDL *userModeBuffer, ULONG *bufferSize, ULONG *bufferOffset, MEMORY_CACHING_TYPE *cacheType) 
{ 
    PAGED_CODE(); 
 
    PHYSICAL_ADDRESS    low; 
    PHYSICAL_ADDRESS    high; 
    DBGPRINT(("CMiniportWaveStreamCMI[%p]::AllocateAudioBuffer(%x, %p, %p, %p, %p)", this, size, userModeBuffer, bufferSize, bufferOffset, cacheType)); 
 
    if (size <= size % (currentChannelCount * 2)) { 
        return STATUS_UNSUCCESSFUL; 
    } 
    size -= size % (currentChannelCount * 2); 
 
    if (size == 0) { 
        return STATUS_UNSUCCESSFUL; 
    } 
 
    low.QuadPart = 0; 
    high.HighPart = 0, high.LowPart = MAXULONG; 
 
    audioBufferMDL = Port->AllocateContiguousPagesForMdl(low, high, size); 
 
    if (!audioBufferMDL) { 
		DBGPRINT(("AllocateContiguousPagesForMdl() failed")); 
		return STATUS_UNSUCCESSFUL; 
	} 
 
	dmaAddress = Port->GetPhysicalPageAddress(audioBufferMDL, 0).LowPart; 
	dmaMemorySize = size; 
 
	*userModeBuffer = audioBufferMDL; 
	*bufferSize = size; 
	*bufferOffset = 0; 
	*cacheType = MmCached; 
 
	return STATUS_SUCCESS; 
} 
 
 
STDMETHODIMP_(VOID) CMiniportWaveStreamCMI::FreeAudioBuffer(PMDL Mdl, ULONG Size) 
{ 
	PAGED_CODE(); 
    DBGPRINT(("CMiniportWaveStreamCMI[%p]::FreeAudioBuffer(%p, %x)", this, Mdl, Size)); 
 
	Port->FreePagesFromMdl(Mdl); 
	audioBufferMDL = NULL; 
	dmaAddress     = 0; 
	dmaMemorySize  = 0; 
} 
 
STDMETHODIMP_(void) CMiniportWaveStreamCMI::GetHWLatency(PKSRTAUDIO_HWLATENCY hwLatency) { 
    PAGED_CODE(); 
    DBGPRINT(("CMiniportWaveStreamCMI[%p]::GetHWLatency(%p)", this, hwLatency)); 
	hwLatency->FifoSize     = 32; 
	hwLatency->ChipsetDelay = 0; 
	hwLatency->CodecDelay   = 4; 
} 
 
STDMETHODIMP_(NTSTATUS) CMiniportWaveStreamCMI::GetPositionRegister(PKSRTAUDIO_HWREGISTER hwRegister) 
{ 
    PAGED_CODE(); 
    DBGPRINT(("CMiniportWaveStreamCMI[%p]::GetPositionRegister(%p)", this, hwRegister)); 
 
    return STATUS_UNSUCCESSFUL; 
} 
 
STDMETHODIMP_(NTSTATUS) CMiniportWaveStreamCMI::GetClockRegister(PKSRTAUDIO_HWREGISTER hwRegister) 
{ 
    PAGED_CODE(); 
    DBGPRINT(("CMiniportWaveStreamCMI[%p]::GetClockRegister(%p)", this, hwRegister)); 
 
    return STATUS_UNSUCCESSFUL; 
} 
 
#endif // WAVERT 
 
#pragma code_seg() 
 
STDMETHODIMP CMiniportWaveStreamCMI::SetState(KSSTATE NewState) 
{ 
	DBGPRINT(("CMiniportWaveStreamCMI[%p]::SetState(%d)", this, NewState)); 
 
	UInt32 inthld, chen, reset, pause; 
	UInt8  reg; 
 
	inthld = EN_CH0_INT << channelNumber; 
	chen   = EN_CH0     << channelNumber; 
	reset  = RST_CH0    << channelNumber; 
	pause  = PAUSE_CH0  << channelNumber; 
 
	NTSTATUS ntStatus = STATUS_SUCCESS; 
 
	if ((streamIndex == PCM_OUT_STREAM) && Miniport->isStreamRunning[AC3_OUT_STREAM]) { 
		return STATUS_INVALID_PARAMETER; 
	} 
 
	if (NewState == KSSTATE_RUN_AC3) { 
		NewState = state; 
		state = KSSTATE_STOP; 
	} 
 
	// STOP -> ACQUIRE -> PAUSE -> PLAY -> PAUSE -> ACQUIRE -> STOP 
	if (state != NewState) { 
		switch (NewState) { 
			case KSSTATE_ACQUIRE: 
				DBGPRINT(("---KSSTATE_ACQUIRE: previous state: %d", state)); 
				if (state == KSSTATE_PAUSE) { 
					break; 
				} 
 
#ifdef WAVERT 
				if ((dmaMemorySize == 0) || (dmaAddress == 0)) { 
					return STATUS_UNSUCCESSFUL; 
				} 
				dmaSize = (dmaMemorySize / (2 * (currentResolution >> 3)) ); 
				periodSize = dmaSize; 
				DBGPRINT(("---dmaAddress: %x, dmaMemorySize: %x, dmaSize: %x", dmaAddress, dmaMemorySize, dmaSize)); 
#else 
				if (currentResolution == 24) { 
					dmaSize = (DMAChannel->BufferSize() / (2 * (24 >> 3)) ); 
				} else { 
					dmaSize = (DMAChannel->BufferSize() / (2 * (currentResolution >> 3)) ); 
				} 
#endif 
				DBGPRINT(("---streamIndex: %d, channelNumber: %d", streamIndex, channelNumber)); 
				DBGPRINT(("---SampleRate: %d, Resolution: %d, Channels: %d", currentSampleRate, currentResolution, currentChannelCount)); 
 
				if (periodSize > dmaSize) { 
					periodSize = dmaSize; 
				} 
 
				// set address of the DMA buffer 
				KeWaitForSingleObject(&Miniport->mutex, Executive, KernelMode, FALSE, NULL); 
				reg = channelNumber ? REG_CH1_FRAME1 : REG_CH0_FRAME1; 
#ifdef WAVERT 
				Miniport->CMIAdapter->writeUInt32(reg, dmaAddress); 
#else 
				Miniport->CMIAdapter->writeUInt32(reg, DMAChannel->PhysicalAddress().u.LowPart); 
#endif 
				// count of samples 
				reg = channelNumber ? REG_CH1_FRAME2 : REG_CH0_FRAME2; 
				Miniport->CMIAdapter->writeUInt16(reg, dmaSize-1); 
				Miniport->CMIAdapter->writeUInt16(reg + 2, periodSize-1); 
				DBGPRINT(("---DMA Size:   0x%04X, Period Size: 0x%04X, enableSPDIFIn: %d", dmaSize, periodSize, Miniport->cm->enableSPDIFIn)); 
				if (isCaptureStream && (Miniport->cm->enableSPDIFIn)) { 
#if OUT_CHANNEL==0 
					Miniport->CMIAdapter->setUInt32Bit(REG_FUNCTRL1, SPDF_1); 
#else 
					Miniport->CMIAdapter->setUInt32Bit(REG_FUNCTRL1, SPDF_0); 
#endif 
				} 
				KeReleaseMutex(&Miniport->mutex, false); 
				break; 
 
			case KSSTATE_PAUSE: 
				DBGPRINT(("---KSSTATE_PAUSE: previous state: %d", state)); 
				if (state == KSSTATE_RUN) { 
					KeWaitForSingleObject(&Miniport->mutex, Executive, KernelMode, false, NULL); 
					Miniport->cm->regFUNCTRL0 |= pause; 
					Miniport->CMIAdapter->writeUInt32(REG_FUNCTRL0, Miniport->cm->regFUNCTRL0); 
					KeReleaseMutex(&Miniport->mutex, FALSE); 
				} 
				if (state == KSSTATE_STOP) { 
					KeWaitForSingleObject(&Miniport->mutex, Executive, KernelMode, false, NULL); 
					Miniport->cm->regFUNCTRL0 &= ~pause; 
					Miniport->CMIAdapter->writeUInt32(REG_FUNCTRL0, Miniport->cm->regFUNCTRL0); 
					KeReleaseMutex(&Miniport->mutex, false); 
				} 
				break; 
 
			case KSSTATE_RUN: 
				DBGPRINT(("---KSSTATE_RUN: previous state: %d", state)); 
 
				KeWaitForSingleObject(&Miniport->mutex, Executive, KernelMode, FALSE, NULL); 
				// set interrupt 
				Miniport->CMIAdapter->setUInt32Bit(REG_INTHLDCLR, inthld); 
				Miniport->cm->regFUNCTRL0 &= ~pause; 
				Miniport->cm->regFUNCTRL0 |= chen; 
				// and enable the channel 
				Miniport->CMIAdapter->writeUInt32(REG_FUNCTRL0, Miniport->cm->regFUNCTRL0); 
 
				DBGPRINT(("---FUNCTRL0:   0x%08X", Miniport->cm->regFUNCTRL0)); 
				DBGPRINT(("---FUNCTRL1:   0x%08X", Miniport->CMIAdapter->readUInt32(REG_FUNCTRL1))); 
				DBGPRINT(("---CHFORMAT:   0x%08X", Miniport->CMIAdapter->readUInt32(REG_CHFORMAT))); 
				DBGPRINT(("---LEGACYCTRL: 0x%08X", Miniport->CMIAdapter->readUInt32(REG_LEGACY))); 
				DBGPRINT(("---MISCCTRL:   0x%08X", Miniport->CMIAdapter->readUInt32(REG_MISCCTRL))); 
				DBGPRINT(("---MIX1:       0x%02X", Miniport->CMIAdapter->readUInt8(REG_MIXER1))); 
				DBGPRINT(("---MIX2:       0x%02X", Miniport->CMIAdapter->readUInt8(REG_MIXER2))); 
				DBGPRINT(("---MIX3:       0x%02X", Miniport->CMIAdapter->readUInt8(REG_MIXER3))); 
 
				KeReleaseMutex(&Miniport->mutex, false); 
				break; 
 
			case KSSTATE_STOP_AC3: 
			case KSSTATE_STOP: 
				DBGPRINT(("---KSSTATE_STOP: previous state: %d", state)); 
				KeWaitForSingleObject(&Miniport->mutex, Executive, KernelMode, FALSE, NULL); 
				// clear interrupt 
				Miniport->CMIAdapter->clearUInt32Bit(REG_INTHLDCLR, inthld); 
				Miniport->cm->regFUNCTRL0 &= ~chen; 
				// reset 
				Miniport->CMIAdapter->writeUInt32(REG_FUNCTRL0, Miniport->cm->regFUNCTRL0 | reset); 
				Miniport->CMIAdapter->writeUInt32(REG_FUNCTRL0, Miniport->cm->regFUNCTRL0 & ~reset); 
				if (isCaptureStream && (Miniport->cm->enableSPDIFIn)) { 
#if OUT_CHANNEL==0 
					Miniport->CMIAdapter->clearUInt32Bit(REG_FUNCTRL1, SPDF_1); 
#else 
					Miniport->CMIAdapter->clearUInt32Bit(REG_FUNCTRL1, SPDF_0); 
#endif 
				} 
				KeReleaseMutex(&Miniport->mutex, FALSE); 
				break; 
		} 
		if (NewState != KSSTATE_STOP_AC3) { 
			state = NewState; 
		} 
	} 
	return ntStatus; 
} 
 
#ifdef WAVERT 
STDMETHODIMP CMiniportWaveStreamCMI::GetPosition(PKSAUDIO_POSITION Position) 
{ 
	ASSERT(Position); 
 
	UInt32 reg; 
 
	if ((state == KSSTATE_RUN) && (dmaAddress)) { 
		reg = (channelNumber) ? REG_CH1_FRAME1 : REG_CH0_FRAME1; 
		Position->PlayOffset = Miniport->CMIAdapter->readUInt32(reg) - dmaAddress; 
        Position->WriteOffset = Position->PlayOffset + currentChannelCount * 2 * 8; 
	} else { 
        Position->PlayOffset = 0; 
        Position->WriteOffset = 0; 
	} 
 
	return STATUS_SUCCESS; 
} 
 
#else //WaveCyclic 
STDMETHODIMP CMiniportWaveStreamCMI::GetPosition(PULONG Position) 
{ 
	ASSERT(Position); 
 
	UInt32 reg; 
 
	if ((DMAChannel) && (state == KSSTATE_RUN)) { 
#if 0 
// this implementation messes with SPDIF-in recording 
        reg = (channelNumber) ? REG_CH1_FRAME2 : REG_CH0_FRAME2; 
        *Position = dmaSize - (Miniport->CMIAdapter->readUInt16(reg)-1); 
        *Position *= 2 * (currentResolution >> 3); 
#else 
		reg = (channelNumber) ? REG_CH1_FRAME1 : REG_CH0_FRAME1; 
		*Position = Miniport->CMIAdapter->readUInt32(reg); 
		if (*Position > DMAChannel->PhysicalAddress().u.LowPart) { 
			*Position -= DMAChannel->PhysicalAddress().u.LowPart; 
		} else { 
			*Position = 0; 
		} 
#endif 
	} else { 
		*Position = 0; 
	} 
 
	return STATUS_SUCCESS; 
} 
 
STDMETHODIMP_(ULONG) CMiniportWaveStreamCMI::SetNotificationFreq(ULONG Interval, PULONG FramingSize) 
{ 
	Miniport->notificationInterval = Interval; 
 
	if (state == KSSTATE_RUN) { 
		return 0; 
	} 
	periodSize   = Interval * currentSampleRate/1000; 
	*FramingSize = periodSize*currentChannelCount*(currentResolution >> 3); 
 
	KeWaitForSingleObject(&Miniport->mutex, Executive, KernelMode, FALSE, NULL); 
	Miniport->CMIAdapter->writeUInt16((channelNumber ? REG_CH1_FRAME2 : REG_CH0_FRAME2) + 2, periodSize-1); 
	KeReleaseMutex(&Miniport->mutex, FALSE); 
 
	DBGPRINT(("periodSize: %x, FramingSize: %x", periodSize, *FramingSize)); 
	return Interval; 
} 
 
STDMETHODIMP CMiniportWaveStreamCMI::NormalizePhysicalPosition(OUT PLONGLONG PhysicalPosition) 
{ 
	*PhysicalPosition = (_100NS_UNITS_PER_SECOND / currentChannelCount * (currentResolution >> 3) * *PhysicalPosition) / currentSampleRate; 
	return STATUS_SUCCESS; 
} 
 
 
STDMETHODIMP_(void) CMiniportWaveStreamCMI::Silence(PVOID Buffer, ULONG ByteCount) 
{ 
	RtlFillMemory(Buffer, ByteCount, 0x00); 
} 
 
#endif //WAVERT 
 
STDMETHODIMP_(void) CMiniportWaveCMI::ServiceWaveISR(UInt32 streamIndex) 
{ 
#ifndef WAVERT 
	if ((streamIndex == PCM_OUT_STREAM) && isStreamRunning[AC3_OUT_STREAM]) { 
		streamIndex = AC3_OUT_STREAM; 
	} 
	if (Port && stream[streamIndex]->ServiceGroup) { 
		Port->Notify(stream[streamIndex]->ServiceGroup); 
	} 
#endif 
}