//==========================================================================;
//
//	WDMTuner.CPP
//	WDM Tuner MiniDriver. 
//		Philips Tuner. 
//			CATIWDMTuner class implementation.
//  Copyright (c) 1996 - 1997  ATI Technologies Inc.  All Rights Reserved.
//
//==========================================================================;

extern "C"
{
#include "strmini.h"
#include "ksmedia.h"

#include "wdmdebug.h"
}

#include "atitunep.h"
#include "wdmdrv.h"
#include "aticonfg.h"


// #define ATI_TVAUDIO_SUPPORT


/*^^*
 *		AdapterCompleteInitialization()
 * Purpose	: Called when SRB_COMPLETE_UNINITIALIZATION SRB is received.
 *
 * Inputs	:	PHW_STREAM_REQUEST_BLOCK pSrb	: pointer to the current Srb
 *
 * Outputs	: BOOL : returns TRUE
 * Author	: IKLEBANOV
 *^^*/
NTSTATUS CATIWDMTuner::AdapterCompleteInitialization( PHW_STREAM_REQUEST_BLOCK pSrb)
{
    PADAPTER_DATA_EXTENSION pPrivateData = ( PADAPTER_DATA_EXTENSION)( pSrb->HwDeviceExtension);
	PDEVICE_OBJECT pDeviceObject = pPrivateData->PhysicalDeviceObject;
	KSPIN_MEDIUM	mediumKSPin;
	NTSTATUS		ntStatus;
	UINT			nIndex;
	HANDLE			hFolder;
	ULONG			ulInstance;

	ENSURE
	{
		nIndex = 0;

		switch( m_ulNumberOfPins)
		{
			case 2:
				// TVTuner with TVAudio
				::RtlCopyMemory( &m_pTVTunerPinsMediumInfo[nIndex ++], &ATITVTunerVideoOutMedium, sizeof( KSPIN_MEDIUM));
#ifdef ATI_TVAUDIO_SUPPORT
#pragma message ("\n!!! PAY ATTENTION: Tuner PinMedium is compiled with TVAudio support !!!\n")
				::RtlCopyMemory( &m_pTVTunerPinsMediumInfo[nIndex], &ATITVTunerTVAudioOutMedium, sizeof( KSPIN_MEDIUM));
#else
#pragma message ("\n!!! PAY ATTENTION: Tuner PinMedium is compiled without TVAudio support !!!\n")
				::RtlCopyMemory( &m_pTVTunerPinsMediumInfo[nIndex ++], &ATIXBarAudioTunerInMedium, sizeof( KSPIN_MEDIUM));
#endif
				break;

			case 3:
				// TVTuner with TVAudio with separate FM Audio output
				::RtlCopyMemory( &m_pTVTunerPinsMediumInfo[nIndex ++], &ATITVTunerVideoOutMedium, sizeof( KSPIN_MEDIUM));
#ifdef ATI_TVAUDIO_SUPPORT
				::RtlCopyMemory( &m_pTVTunerPinsMediumInfo[nIndex ++], &ATITVTunerTVAudioOutMedium, sizeof( KSPIN_MEDIUM));
#else
				::RtlCopyMemory( &m_pTVTunerPinsMediumInfo[nIndex ++], &ATIXBarAudioTunerInMedium, sizeof( KSPIN_MEDIUM));
#endif

			case 1:
				// it can be FM Tuner only.
				::RtlCopyMemory( &m_pTVTunerPinsMediumInfo[nIndex], &ATITVTunerRadioAudioOutMedium, sizeof( KSPIN_MEDIUM));
				break;
		}

		ulInstance = ::GetDriverInstanceNumber( pDeviceObject);
		hFolder = ::OpenRegistryFolder( pDeviceObject, UNICODE_WDM_REG_PIN_MEDIUMS);

		for( nIndex = 0; nIndex < m_ulNumberOfPins; nIndex ++)
		{
			if( ::ReadPinMediumFromRegistryFolder( hFolder, nIndex, &mediumKSPin))
				::RtlCopyMemory( &m_pTVTunerPinsMediumInfo[nIndex], &mediumKSPin, sizeof( KSPIN_MEDIUM));
			m_pTVTunerPinsMediumInfo[nIndex].Id = ulInstance;

			// all the possible pins exposed are the outputs
			m_pTVTunerPinsDirectionInfo[nIndex] = TRUE;
		}

		if( hFolder != NULL)
			::ZwClose( hFolder);

		ntStatus = StreamClassRegisterFilterWithNoKSPins( \
						pDeviceObject					,		// IN PDEVICE_OBJECT   DeviceObject,
						&KSCATEGORY_TVTUNER,					// IN GUID           * InterfaceClassGUID
						m_ulNumberOfPins,						// IN ULONG            PinCount,
						m_pTVTunerPinsDirectionInfo,			// IN ULONG          * Flags,
						m_pTVTunerPinsMediumInfo,				// IN KSPIN_MEDIUM   * MediumList,
						NULL);									// IN GUID           * CategoryList
		if( !NT_SUCCESS( ntStatus))
			FAIL;

	} END_ENSURE;

	_DebugPrint(( DebugLevelVerbose, "CATIWDMTuner:AdapterCompleteInitialization() ntStatus=%x\n",
		ntStatus));

	return( ntStatus);
}



/*^^*
 *		AdapterUnInitialize()
 * Purpose	: Called when SRB_UNINITIALIZE_DEVICE SRB is received.
 *
 * Inputs	:	PHW_STREAM_REQUEST_BLOCK pSrb	: pointer to the current Srb
 *
 * Outputs	: BOOL : returns TRUE
 * Author	: IKLEBANOV
 *^^*/
BOOL CATIWDMTuner::AdapterUnInitialize( PHW_STREAM_REQUEST_BLOCK pSrb)
{

	_DebugPrint(( DebugLevelVerbose, "CATIWDMTuner:AdapterUnInitialize()\n"));

	// just deallocate the any memory was allocated at run-time
	if( m_pTVTunerPinsMediumInfo != NULL)
	{
		::ExFreePool( m_pTVTunerPinsMediumInfo);
		m_pTVTunerPinsMediumInfo = NULL;
	}

	if( m_pTVTunerPinsDirectionInfo != NULL)
	{
		::ExFreePool( m_pTVTunerPinsDirectionInfo);
		m_pTVTunerPinsDirectionInfo = NULL;
	}

	pSrb->Status = STATUS_SUCCESS;
	return( TRUE);
}


/*^^*
 *		AdapterGetStreamInfo()
 * Purpose	: fills in HW_STREAM_HEADER for StreamClass driver
 *
 * Inputs	: PHW_STREAM_REQUEST_BLOCK pSrb	: pointer to the current Srb
 *
 * Outputs	: BOOL : returns TRUE
 * Author	: IKLEBANOV
 *^^*/
BOOL CATIWDMTuner::AdapterGetStreamInfo( PHW_STREAM_REQUEST_BLOCK pSrb)
{
     // pick up the pointer to the stream header data structure
	PHW_STREAM_HEADER pStreamHeader = ( PHW_STREAM_HEADER)&( pSrb->CommandData.StreamBuffer->StreamHeader);

	// no streams are supported
    DEBUG_ASSERT( pSrb->NumberOfBytesToTransfer >= sizeof( HW_STREAM_HEADER));

	_DebugPrint(( DebugLevelVerbose, "CATIWDMTuner:AdapterGetStreamInfo()\n"));

	m_wdmTunerStreamHeader.NumberOfStreams = 0;
	m_wdmTunerStreamHeader.SizeOfHwStreamInformation = sizeof( HW_STREAM_INFORMATION);
	m_wdmTunerStreamHeader.NumDevPropArrayEntries = 1;
	m_wdmTunerStreamHeader.DevicePropertiesArray = &m_wdmTunerPropertySet;
	m_wdmTunerStreamHeader.NumDevEventArrayEntries = 0;
	m_wdmTunerStreamHeader.DeviceEventsArray = NULL;
	m_wdmTunerStreamHeader.Topology = &m_wdmTunerTopology;

	* pStreamHeader = m_wdmTunerStreamHeader;

	pSrb->Status = STATUS_SUCCESS;
	return( TRUE);
}


/*^^*
 *		AdapterQueryUnload()
 * Purpose	: Called when the class driver is about to unload the MiniDriver
 *				The MiniDriver checks if any open stream left.
 *
 * Inputs	:	PHW_STREAM_REQUEST_BLOCK pSrb	: pointer to the current Srb
 *
 * Outputs	: BOOL : returns TRUE
 * Author	: IKLEBANOV
 *^^*/
BOOL CATIWDMTuner::AdapterQueryUnload( PHW_STREAM_REQUEST_BLOCK pSrb)
{

	_DebugPrint(( DebugLevelVerbose, "CATIWDMTuner:AdapterQueryUnload()\n"));

	pSrb->Status = STATUS_SUCCESS;
	return( TRUE);
}



/*^^*
 *		operator new
 * Purpose	: CATIWDMTuner class overloaded operator new.
 *				Provides placement for a CATIWDMTuner class object from the PADAPTER_DEVICE_EXTENSION
 *				allocated by the StreamClassDriver for the MiniDriver.
 *
 * Inputs	:	UINT size_t			: size of the object to be placed
 *				PVOID pAllocation	: casted pointer to the CWDMTuner allocated data
 *
 * Outputs	: PVOID : pointer of the CATIWDMTuner class object
 * Author	: IKLEBANOV
 *^^*/
PVOID CATIWDMTuner::operator new( UINT size_t, PVOID pAllocation)
{

	if( size_t != sizeof( CATIWDMTuner))
	{
		_DebugPrint(( DebugLevelVerbose, "CATIWDMTuner: operator new() fails\n"));
		return( NULL);
	}
	else
		return( pAllocation);
}



/*^^*
 *		~CATIWDMTuner()
 * Purpose	: CATIWDMTuner class destructor.
 *				Frees the allocated memory.
 *
 * Inputs	: none
 *
 * Outputs	: none
 * Author	: IKLEBANOV
 *^^*/
CATIWDMTuner::~CATIWDMTuner()
{

	_DebugPrint(( DebugLevelVerbose, "CATIWDMTuner:~CATIWDMTuner()\n"));

	if( m_pTVTunerPinsMediumInfo != NULL)
	{
		::ExFreePool( m_pTVTunerPinsMediumInfo);
		m_pTVTunerPinsMediumInfo = NULL;
	}

	if( m_pTVTunerPinsDirectionInfo != NULL)
	{
		::ExFreePool( m_pTVTunerPinsDirectionInfo);
		m_pTVTunerPinsDirectionInfo = NULL;
	}
}



/*^^*
 *		CATIWDMTuner()
 * Purpose	: CATIWDMTuner class constructor.
 *				Performs checking of the hardware presence. Sets the hardware in an initial state.
 *
 * Inputs	:	CI2CScript * pCScript	: pointer to the I2CScript class object
 *				PUINT puiError			: pointer to return a completion error code
 *
 * Outputs	: none
 * Author	: IKLEBANOV
 *^^*/
CATIWDMTuner::CATIWDMTuner( PDEVICE_OBJECT pDeviceObject, CI2CScript * pCScript, PUINT puiErrorCode)
	:m_CATIConfiguration( pDeviceObject, pCScript, puiErrorCode)
{
	UINT uiError;

	_DebugPrint(( DebugLevelVerbose, "CATIWDMTuner:CATIWDMTuner() enter\n"));

	// error code was carried over from ATIConfiguration class constructor
	uiError = * puiErrorCode;

	m_pTVTunerPinsMediumInfo = NULL;
	m_pTVTunerPinsDirectionInfo = NULL;
	m_ulPowerState = PowerDeviceD0;
	
	ENSURE
	{
		if( uiError != WDMMINI_NOERROR)
			// ATIConfiguration Class object was constructed with an error
			FAIL;

		if( pCScript == NULL)
		{
			uiError = WDMMINI_INVALIDPARAM;
			FAIL;
		}

		if( !m_CATIConfiguration.GetTunerConfiguration( &m_uiTunerId, &m_uchTunerI2CAddress) ||
			( !m_uchTunerI2CAddress))
		{
			// there was no hardware information found
			uiError = WDMMINI_NOHARDWARE;
			FAIL;
		}

		// Set tuner capabilities ( RO properties) based upon the TunerId
		if( !SetTunerWDMCapabilities( m_uiTunerId) || ( !m_ulNumberOfPins))
		{
			// there is unsupported hardware was found
			uiError = WDMMINI_UNKNOWNHARDWARE;
			FAIL;
		}

		m_pTVTunerPinsMediumInfo = ( PKSPIN_MEDIUM) \
			::ExAllocatePool( NonPagedPool, sizeof( KSPIN_MEDIUM) * m_ulNumberOfPins);
		if( m_pTVTunerPinsMediumInfo == NULL)
		{
			uiError = WDMMINI_ERROR_MEMORYALLOCATION;
			FAIL;
		}
		
		m_pTVTunerPinsDirectionInfo = ( PBOOL) \
			::ExAllocatePool( NonPagedPool, sizeof( BOOL) * m_ulNumberOfPins);
		if( m_pTVTunerPinsDirectionInfo == NULL)
		{
			uiError = WDMMINI_ERROR_MEMORYALLOCATION;
			FAIL;
		}

		m_pI2CScript = pCScript;

		SetWDMTunerKSProperties();
		SetWDMTunerKSTopology();

		// Set run-time WDM properties at the last
		m_ulVideoStandard = ( m_ulNumberOfStandards == 1) ?
			// unknown standard or the only one
			m_wdmTunerCaps.ulStandardsSupported : 0x0L;
		m_ulTunerInput = 0L;				// unknown input or the only one
		m_ulTuningFrequency = 0L;			// unknown tuning frequency

#ifndef ATI_TVAUDIO_SUPPORT
		{
			// this code is needed to initilaize TVAudio path off the tuner
			// if there is no separate MiniDriver for TVAudio is assumed
			UINT	uiAudioConfiguration;
			UCHAR	uchAudioI2CAddress;

			if( m_CATIConfiguration.GetAudioConfiguration( &uiAudioConfiguration,
														   &uchAudioI2CAddress))
			{
				m_CATIConfiguration.InitializeAudioConfiguration( pCScript,
																  uiAudioConfiguration,
																  uchAudioI2CAddress);
			}
		}
#endif	// ATI_TVAUDIO_SUPPORT

		* puiErrorCode = WDMMINI_NOERROR;

		_DebugPrint(( DebugLevelVerbose, "CATIWDMTuner:CATIWDMTuner() exit\n"));

		return;

	} END_ENSURE;

	* puiErrorCode = uiError;
    _DebugPrint(( DebugLevelError, "CATIWDMTuner:CATIWDMTuner() Error = %x\n", uiError));
}



/*^^*
 *		SetTunerCapabilities()
 * Purpose	:  Sets the capabilities ( RO properties) based upon the Tuner Id
 *
 * Inputs	:	UINT puiTunerId	: Tuner Id
 *
 * Outputs	: returns TRUE, if there is a supported Tuner Id specified;
 *				also sets the following WDM Tuner properities:
 * Author	: IKLEBANOV
 *^^*/
BOOL CATIWDMTuner::SetTunerWDMCapabilities( UINT uiTunerId)
{
	
	::RtlZeroMemory( &m_wdmTunerCaps, sizeof( ATI_KSPROPERTY_TUNER_CAPS));
	m_ulIntermediateFrequency = 0x0L;

	switch( uiTunerId)
	{
		case 0x01:		// NTSC North America
			m_ulNumberOfStandards = 1;
			m_wdmTunerCaps.ulStandardsSupported = KS_AnalogVideo_NTSC_M;
			m_wdmTunerCaps.ulNumberOfInputs = 1;
			m_wdmTunerCaps.ulMinFrequency =  55250000L;
			m_wdmTunerCaps.ulMaxFrequency = 801250000L;
			m_ulIntermediateFrequency = 45750000L;
			m_ulSupportedModes = KSPROPERTY_TUNER_MODE_TV;
			m_ulNumberOfPins = 2;
				break;

		case 0x02:		// NTSC Japan
			m_ulNumberOfStandards = 1;
			m_wdmTunerCaps.ulStandardsSupported = KS_AnalogVideo_NTSC_M;
			m_wdmTunerCaps.ulNumberOfInputs = 1;
			m_wdmTunerCaps.ulMinFrequency =  55250000L;
			m_wdmTunerCaps.ulMaxFrequency = 801250000L;
			m_ulIntermediateFrequency = 45750000L; 
			m_ulSupportedModes = KSPROPERTY_TUNER_MODE_TV;
			m_ulNumberOfPins = 2;
			break;

		case 0x03:		// PAL B/G
			m_ulNumberOfStandards = 1;
			m_wdmTunerCaps.ulStandardsSupported = KS_AnalogVideo_PAL_B;
			m_wdmTunerCaps.ulNumberOfInputs = 1;
			m_wdmTunerCaps.ulMinFrequency =  55250000L;
			m_wdmTunerCaps.ulMaxFrequency = 801250000L;
			m_ulIntermediateFrequency = 45750000L; 
			m_ulSupportedModes = KSPROPERTY_TUNER_MODE_TV;
			m_ulNumberOfPins = 2;
			break;

		case 0x04:		// PAL I
			m_ulNumberOfStandards = 1;
			m_wdmTunerCaps.ulStandardsSupported =	KS_AnalogVideo_PAL_I;
			m_wdmTunerCaps.ulNumberOfInputs = 1;
			m_wdmTunerCaps.ulMinFrequency =  55250000L;
			m_wdmTunerCaps.ulMaxFrequency = 801250000L;
			m_ulIntermediateFrequency = 45750000L; 
			m_ulSupportedModes = KSPROPERTY_TUNER_MODE_TV;
			m_ulNumberOfPins = 2;
			break;

		case 0x05:		// SECAM + PAL B/G
			m_ulNumberOfStandards = 2;
			m_wdmTunerCaps.ulStandardsSupported =	KS_AnalogVideo_PAL_B |
													KS_AnalogVideo_SECAM_L;
			m_wdmTunerCaps.ulNumberOfInputs = 1;
			m_wdmTunerCaps.ulMinFrequency =  55250000L;
			m_wdmTunerCaps.ulMaxFrequency = 801250000L;
			m_ulIntermediateFrequency = 45750000L; 
			m_ulSupportedModes = KSPROPERTY_TUNER_MODE_TV;
			m_ulNumberOfPins = 2;
			break;

		case 0x06:		// NTSC North America + NTSC Japan
			m_ulNumberOfStandards = 1;
			m_wdmTunerCaps.ulStandardsSupported = KS_AnalogVideo_NTSC_M;
			m_wdmTunerCaps.ulNumberOfInputs = 1;
			m_wdmTunerCaps.ulMinFrequency =  55250000L;
			m_wdmTunerCaps.ulMaxFrequency = 801250000L;
			m_ulIntermediateFrequency = 45750000L; 
			m_ulSupportedModes = KSPROPERTY_TUNER_MODE_TV;
			m_ulNumberOfPins = 2;
			break;

		case 0x07:		// PAL D China
			m_ulNumberOfStandards = 1;
			m_wdmTunerCaps.ulStandardsSupported =	KS_AnalogVideo_PAL_D |
													KS_AnalogVideo_SECAM_D;
			m_wdmTunerCaps.ulNumberOfInputs = 1;
			m_wdmTunerCaps.ulMinFrequency =  55250000L;
			m_wdmTunerCaps.ulMaxFrequency = 801250000L;
			m_ulIntermediateFrequency = 45750000L; 
			m_ulSupportedModes = KSPROPERTY_TUNER_MODE_TV;
			m_ulNumberOfPins = 2;
			break;

		case 0x08:		// NTSC North America NEC FM Tuner
			m_ulNumberOfStandards = 1;
			m_wdmTunerCaps.ulStandardsSupported = KS_AnalogVideo_NTSC_M;
			m_wdmTunerCaps.ulNumberOfInputs = 2;
			m_wdmTunerCaps.ulMinFrequency =  55250000L;
			m_wdmTunerCaps.ulMaxFrequency = 801250000L;
			m_ulIntermediateFrequency = 45750000L; 
			m_ulSupportedModes = KSPROPERTY_TUNER_MODE_TV | KSPROPERTY_TUNER_MODE_FM_RADIO;
			m_ulNumberOfPins = 2;
			break;

		default:
			return( FALSE);
	}

	m_ulTunerMode = KSPROPERTY_TUNER_MODE_TV;

	m_wdmTunerCaps.ulTuningGranularity = 62500L;
	m_wdmTunerCaps.ulSettlingTime = 150;
	m_wdmTunerCaps.ulStrategy = KS_TUNER_STRATEGY_PLL;

	return( TRUE);
}



/*^^*
 *		AdapterSetPowerState()
 * Purpose	:	Sets Power Managemeny mode
 *
 * Inputs	:	PHW_STREAM_REQUEST_BLOCK pSrb	: pointer to the current Srb
 *
 * Outputs	:	NTSTATUS as the result of operation
 * Author	:	TOM
 *^^*/
NTSTATUS CATIWDMTuner::AdapterSetPowerState( PHW_STREAM_REQUEST_BLOCK pSrb)
{
    PADAPTER_DATA_EXTENSION pPrivateData = 
		( PADAPTER_DATA_EXTENSION)(( PHW_STREAM_REQUEST_BLOCK)pSrb)->HwDeviceExtension;
	CI2CScript *		pCScript	= &pPrivateData->CScript;
    DEVICE_POWER_STATE	nDeviceState = pSrb->CommandData.DeviceState;
	LARGE_INTEGER		liWakeUpTime;
	NTSTATUS			ntStatus;

	m_pPendingDeviceSrb = pSrb;
	ntStatus = STATUS_ADAPTER_HARDWARE_ERROR;

	switch( nDeviceState)
	{
		case PowerDeviceD0:
		case PowerDeviceD3:
			if( nDeviceState != m_ulPowerState)
			{
				m_CATIConfiguration.SetTunerPowerState( m_pI2CScript,
										( nDeviceState == PowerDeviceD0 ? TRUE : FALSE));

				// if transition form D3 to D0 we have to restore frequency
				if(( nDeviceState == PowerDeviceD0) && ( m_ulPowerState == PowerDeviceD3))
				{
					// we have to wait approx. 10ms for tuner to power up
					liWakeUpTime.QuadPart = ATIHARDWARE_TUNER_WAKEUP_DELAY;
					KeDelayExecutionThread( KernelMode, FALSE, &liWakeUpTime);

					// now we have to restore frequency
					if( SetTunerFrequency( m_ulTuningFrequency))
						ntStatus = STATUS_SUCCESS;
					else
						ntStatus = STATUS_ADAPTER_HARDWARE_ERROR;
				}
				else
					ntStatus = STATUS_SUCCESS;

				m_ulPowerState = nDeviceState;
			}
			else
				ntStatus = STATUS_SUCCESS;
			break;

		case PowerDeviceD1:
		case PowerDeviceD2:
			ntStatus = STATUS_SUCCESS;
			break;

		default:
			ntStatus = STATUS_INVALID_PARAMETER;
			break;
	}

	return( ntStatus);
}
