947 lines
30 KiB
C++
947 lines
30 KiB
C++
/*****************************************************************************
|
|
* common.cpp - Common code used by all the sb16 miniports.
|
|
*****************************************************************************
|
|
* Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved.
|
|
*
|
|
* Implmentation of the common code object. This class deals with interrupts
|
|
* for the device, and is a collection of common code used by all the
|
|
* miniports.
|
|
*/
|
|
|
|
#include "common.h"
|
|
|
|
#define STR_MODULENAME "sb16Adapter: "
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon
|
|
|
|
*****************************************************************************
|
|
* Adapter common object.
|
|
*/
|
|
class CAdapterCommon
|
|
: public IAdapterCommon,
|
|
public IAdapterPowerManagement,
|
|
public CUnknown
|
|
|
|
{
|
|
private:
|
|
PINTERRUPTSYNC m_pInterruptSync;
|
|
PUCHAR m_pWaveBase;
|
|
PWAVEMINIPORTSB16 m_WaveMiniportSB16;
|
|
#ifdef EVENT_SUPPORT
|
|
PTOPOMINIPORTSB16 m_TopoMiniportSB16; // Topology miniport of SB16.
|
|
#endif
|
|
PDEVICE_OBJECT m_pDeviceObject;
|
|
DEVICE_POWER_STATE m_PowerState;
|
|
BYTE MixerSettings[DSP_MIX_MAXREGS];
|
|
|
|
void AcknowledgeIRQ
|
|
( void
|
|
);
|
|
|
|
public:
|
|
DECLARE_STD_UNKNOWN();
|
|
DEFINE_STD_CONSTRUCTOR(CAdapterCommon);
|
|
~CAdapterCommon();
|
|
|
|
/*****************************************************************************
|
|
* IAdapterCommon methods
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS) Init
|
|
(
|
|
IN PRESOURCELIST ResourceList,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
);
|
|
STDMETHODIMP_(PINTERRUPTSYNC) GetInterruptSync
|
|
( void
|
|
);
|
|
STDMETHODIMP_(void) SetWaveMiniport (IN PWAVEMINIPORTSB16 Miniport)
|
|
{
|
|
m_WaveMiniportSB16 = Miniport;
|
|
}
|
|
STDMETHODIMP_(BYTE) ReadController
|
|
( void
|
|
);
|
|
STDMETHODIMP_(BOOLEAN) WriteController
|
|
(
|
|
IN BYTE Value
|
|
);
|
|
STDMETHODIMP_(NTSTATUS) ResetController
|
|
( void
|
|
);
|
|
STDMETHODIMP_(void) MixerRegWrite
|
|
(
|
|
IN BYTE Index,
|
|
IN BYTE Value
|
|
);
|
|
STDMETHODIMP_(BYTE) MixerRegRead
|
|
(
|
|
IN BYTE Index
|
|
);
|
|
STDMETHODIMP_(void) MixerReset
|
|
( void
|
|
);
|
|
STDMETHODIMP RestoreMixerSettingsFromRegistry
|
|
( void
|
|
);
|
|
STDMETHODIMP SaveMixerSettingsToRegistry
|
|
( void
|
|
);
|
|
#ifdef EVENT_SUPPORT
|
|
//
|
|
// The topology miniport needs to tell us the pointer to the Event-interface.
|
|
//
|
|
STDMETHODIMP_(void) SetTopologyMiniport (IN PTOPOMINIPORTSB16 Miniport)
|
|
{
|
|
m_TopoMiniportSB16 = Miniport;
|
|
};
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
* IAdapterPowerManagement implementation
|
|
*
|
|
* This macro is from PORTCLS.H. It lists all the interface's functions.
|
|
*/
|
|
IMP_IAdapterPowerManagement;
|
|
|
|
friend
|
|
NTSTATUS
|
|
InterruptServiceRoutine
|
|
(
|
|
IN PINTERRUPTSYNC InterruptSync,
|
|
IN PVOID DynamicContext
|
|
);
|
|
};
|
|
|
|
static
|
|
MIXERSETTING DefaultMixerSettings[] =
|
|
{
|
|
{ L"LeftMasterVol", DSP_MIX_MASTERVOLIDX_L, 0xD8 },
|
|
{ L"RightMasterVol", DSP_MIX_MASTERVOLIDX_R, 0xD8 },
|
|
{ L"LeftWaveVol", DSP_MIX_VOICEVOLIDX_L, 0xD8 },
|
|
{ L"RightWaveVol", DSP_MIX_VOICEVOLIDX_R, 0xD8 },
|
|
{ L"LeftMidiVol", DSP_MIX_FMVOLIDX_L, 0xD8 },
|
|
{ L"RightMidiVol", DSP_MIX_FMVOLIDX_R, 0xD8 },
|
|
{ L"LeftCDVol", DSP_MIX_CDVOLIDX_L, 0xD8 },
|
|
{ L"RightCDVol", DSP_MIX_CDVOLIDX_R, 0xD8 },
|
|
{ L"LeftLineInVol", DSP_MIX_LINEVOLIDX_L, 0xD8 },
|
|
{ L"RightLineInVol", DSP_MIX_LINEVOLIDX_R, 0xD8 },
|
|
{ L"MicVol", DSP_MIX_MICVOLIDX, 0xD8 },
|
|
{ L"PcSpkrVol", DSP_MIX_SPKRVOLIDX, 0x00 },
|
|
{ L"OutputMixer", DSP_MIX_OUTMIXIDX, 0x1E },
|
|
{ L"LeftInputMixer", DSP_MIX_ADCMIXIDX_L, 0x55 },
|
|
{ L"RightInputMixer", DSP_MIX_ADCMIXIDX_R, 0x2B },
|
|
{ L"LeftInputGain", DSP_MIX_INGAINIDX_L, 0x00 },
|
|
{ L"RightInputGain", DSP_MIX_INGAINIDX_R, 0x00 },
|
|
{ L"LeftOutputGain", DSP_MIX_OUTGAINIDX_L, 0x80 },
|
|
{ L"RightOutputGain", DSP_MIX_OUTGAINIDX_R, 0x80 },
|
|
{ L"MicAGC", DSP_MIX_AGCIDX, 0x01 },
|
|
{ L"LeftTreble", DSP_MIX_TREBLEIDX_L, 0x80 },
|
|
{ L"RightTreble", DSP_MIX_TREBLEIDX_R, 0x80 },
|
|
{ L"LeftBass", DSP_MIX_BASSIDX_L, 0x80 },
|
|
{ L"RightBass", DSP_MIX_BASSIDX_R, 0x80 },
|
|
};
|
|
|
|
|
|
|
|
#pragma code_seg("PAGE")
|
|
|
|
/*****************************************************************************
|
|
* NewAdapterCommon()
|
|
*****************************************************************************
|
|
* Create a new adapter common object.
|
|
*/
|
|
NTSTATUS
|
|
NewAdapterCommon
|
|
(
|
|
OUT PUNKNOWN * Unknown,
|
|
IN REFCLSID,
|
|
IN PUNKNOWN UnknownOuter OPTIONAL,
|
|
IN POOL_TYPE PoolType
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Unknown);
|
|
|
|
STD_CREATE_BODY_
|
|
(
|
|
CAdapterCommon,
|
|
Unknown,
|
|
UnknownOuter,
|
|
PoolType,
|
|
PADAPTERCOMMON
|
|
);
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::Init()
|
|
*****************************************************************************
|
|
* Initialize an adapter common object.
|
|
*/
|
|
NTSTATUS
|
|
CAdapterCommon::
|
|
Init
|
|
(
|
|
IN PRESOURCELIST ResourceList,
|
|
IN PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(ResourceList);
|
|
ASSERT(DeviceObject);
|
|
|
|
//
|
|
// Make sure we have the resources we expect
|
|
//
|
|
if ((ResourceList->NumberOfPorts() < 1) ||
|
|
(ResourceList->NumberOfInterrupts() != 1))
|
|
{
|
|
_DbgPrintF (DEBUGLVL_TERSE, ("unknown configuration; check your code!"));
|
|
// Bail out.
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
m_pDeviceObject = DeviceObject;
|
|
m_WaveMiniportSB16 = NULL;
|
|
#ifdef EVENT_SUPPORT
|
|
m_TopoMiniportSB16 = NULL;
|
|
#endif
|
|
|
|
//
|
|
// Get the base address for the wave device.
|
|
//
|
|
ASSERT(ResourceList->FindTranslatedPort(0));
|
|
m_pWaveBase = (PUCHAR)(ResourceList->FindTranslatedPort(0)->u.Port.Start.QuadPart);
|
|
|
|
//
|
|
// Set initial device power state
|
|
//
|
|
m_PowerState = PowerDeviceD0;
|
|
|
|
//
|
|
// Reset the hardware.
|
|
//
|
|
NTSTATUS ntStatus = ResetController();
|
|
|
|
if(NT_SUCCESS(ntStatus))
|
|
{
|
|
_DbgPrintF(DEBUGLVL_VERBOSE,("ResetController Succeeded"));
|
|
AcknowledgeIRQ();
|
|
|
|
//
|
|
// Hook up the interrupt.
|
|
//
|
|
ntStatus = PcNewInterruptSync( // See portcls.h
|
|
&m_pInterruptSync, // Save object ptr
|
|
NULL, // OuterUnknown(optional).
|
|
ResourceList, // He gets IRQ from ResourceList.
|
|
0, // Resource Index
|
|
InterruptSyncModeNormal // Run ISRs once until we get SUCCESS
|
|
);
|
|
if (NT_SUCCESS(ntStatus) && m_pInterruptSync)
|
|
{ // run this ISR first
|
|
ntStatus = m_pInterruptSync->RegisterServiceRoutine(InterruptServiceRoutine,PVOID(this),FALSE);
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
ntStatus = m_pInterruptSync->Connect();
|
|
}
|
|
|
|
// if we could not connect or register the ISR, release the object.
|
|
if (!NT_SUCCESS (ntStatus))
|
|
{
|
|
m_pInterruptSync->Release();
|
|
m_pInterruptSync = NULL;
|
|
}
|
|
}
|
|
} else
|
|
{
|
|
_DbgPrintF(DEBUGLVL_TERSE,("ResetController Failure"));
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::~CAdapterCommon()
|
|
*****************************************************************************
|
|
* Destructor.
|
|
*/
|
|
CAdapterCommon::
|
|
~CAdapterCommon
|
|
( void
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
_DbgPrintF(DEBUGLVL_VERBOSE,("[CAdapterCommon::~CAdapterCommon]"));
|
|
|
|
if (m_pInterruptSync)
|
|
{
|
|
m_pInterruptSync->Disconnect();
|
|
m_pInterruptSync->Release();
|
|
m_pInterruptSync = NULL;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::NonDelegatingQueryInterface()
|
|
*****************************************************************************
|
|
* Obtains an interface.
|
|
*/
|
|
STDMETHODIMP
|
|
CAdapterCommon::
|
|
NonDelegatingQueryInterface
|
|
(
|
|
REFIID Interface,
|
|
PVOID * Object
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Object);
|
|
|
|
if (IsEqualGUIDAligned(Interface,IID_IUnknown))
|
|
{
|
|
*Object = PVOID(PUNKNOWN(PADAPTERCOMMON(this)));
|
|
}
|
|
else
|
|
if (IsEqualGUIDAligned(Interface,IID_IAdapterCommon))
|
|
{
|
|
*Object = PVOID(PADAPTERCOMMON(this));
|
|
}
|
|
else
|
|
if (IsEqualGUIDAligned(Interface,IID_IAdapterPowerManagement))
|
|
{
|
|
*Object = PVOID(PADAPTERPOWERMANAGEMENT(this));
|
|
}
|
|
else
|
|
{
|
|
*Object = NULL;
|
|
}
|
|
|
|
if (*Object)
|
|
{
|
|
PUNKNOWN(*Object)->AddRef();
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::GetInterruptSync()
|
|
*****************************************************************************
|
|
* Get a pointer to the interrupt synchronization object.
|
|
*/
|
|
STDMETHODIMP_(PINTERRUPTSYNC)
|
|
CAdapterCommon::
|
|
GetInterruptSync
|
|
( void
|
|
)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
return m_pInterruptSync;
|
|
}
|
|
|
|
#pragma code_seg()
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::ReadController()
|
|
*****************************************************************************
|
|
* Read a byte from the controller.
|
|
*/
|
|
STDMETHODIMP_(BYTE)
|
|
CAdapterCommon::
|
|
ReadController
|
|
( void
|
|
)
|
|
{
|
|
BYTE returnValue = BYTE(-1);
|
|
|
|
ASSERT(m_pWaveBase);
|
|
|
|
ULONGLONG startTime = PcGetTimeInterval(0);
|
|
|
|
do {
|
|
if (READ_PORT_UCHAR (m_pWaveBase + DSP_REG_DATAAVAIL) & 0x80)
|
|
{
|
|
returnValue = READ_PORT_UCHAR (m_pWaveBase + DSP_REG_READ);
|
|
}
|
|
} while ((PcGetTimeInterval(startTime) < GTI_MILLISECONDS(100)) &&
|
|
(BYTE(-1) == returnValue));
|
|
|
|
|
|
ASSERT((BYTE(-1) != returnValue) || !"ReadController timeout!");
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::WriteController()
|
|
*****************************************************************************
|
|
* Write a byte to the controller.
|
|
*/
|
|
STDMETHODIMP_(BOOLEAN)
|
|
CAdapterCommon::
|
|
WriteController
|
|
(
|
|
IN BYTE Value
|
|
)
|
|
{
|
|
ASSERT(m_pWaveBase);
|
|
|
|
BOOLEAN returnValue = FALSE;
|
|
ULONGLONG startTime = PcGetTimeInterval(0);
|
|
|
|
do
|
|
{
|
|
BYTE status = READ_PORT_UCHAR (m_pWaveBase + DSP_REG_WRITE);
|
|
|
|
if ((status & 0x80) == 0)
|
|
{
|
|
WRITE_PORT_UCHAR (m_pWaveBase + DSP_REG_WRITE, Value);
|
|
|
|
returnValue = TRUE;
|
|
}
|
|
} while ((PcGetTimeInterval(startTime) < GTI_MILLISECONDS(100)) &&
|
|
! returnValue);
|
|
|
|
ASSERT(returnValue || !"WriteController timeout");
|
|
|
|
return returnValue;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::MixerRegWrite()
|
|
*****************************************************************************
|
|
* Writes a mixer register.
|
|
*/
|
|
STDMETHODIMP_(void)
|
|
CAdapterCommon::
|
|
MixerRegWrite
|
|
(
|
|
IN BYTE Index,
|
|
IN BYTE Value
|
|
)
|
|
{
|
|
ASSERT( m_pWaveBase );
|
|
BYTE actualIndex;
|
|
|
|
// only hit the hardware if we're in an acceptable power state
|
|
if( m_PowerState <= PowerDeviceD1 )
|
|
{
|
|
actualIndex = (BYTE) ((Index < 0x80) ? (Index + DSP_MIX_BASEIDX) : Index);
|
|
|
|
WRITE_PORT_UCHAR (m_pWaveBase + DSP_REG_MIXREG, actualIndex);
|
|
|
|
WRITE_PORT_UCHAR (m_pWaveBase + DSP_REG_MIXDATA, Value);
|
|
}
|
|
|
|
if(Index < DSP_MIX_MAXREGS)
|
|
{
|
|
MixerSettings[Index] = Value;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::MixerRegRead()
|
|
*****************************************************************************
|
|
* Reads a mixer register.
|
|
*/
|
|
STDMETHODIMP_(BYTE)
|
|
CAdapterCommon::
|
|
MixerRegRead
|
|
(
|
|
IN BYTE Index
|
|
)
|
|
{
|
|
if(Index < DSP_MIX_MAXREGS)
|
|
{
|
|
return MixerSettings[Index];
|
|
}
|
|
|
|
//
|
|
// Not in the cache? Read from HW directly.
|
|
//
|
|
// We need to make sure that we can access the HW directly for
|
|
// the volumes that can change externally.
|
|
// This is done here with passing an index outside of the cache.
|
|
// Since the an index=0 is actually DSP_MIX_BASEIDX which is less
|
|
// than the cache size (DSP_MIX_MAXREGS), you can access any volume
|
|
// directly with passing DSP_MIX_BASEIDX + index.
|
|
// You could also pass a flag - but we want to keep the changes
|
|
// minimal - or create a new function like MixerRegReadDirect().
|
|
//
|
|
WRITE_PORT_UCHAR (m_pWaveBase + DSP_REG_MIXREG, Index);
|
|
return READ_PORT_UCHAR (m_pWaveBase + DSP_REG_MIXDATA);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::MixerReset()
|
|
*****************************************************************************
|
|
* Resets the mixer
|
|
*/
|
|
STDMETHODIMP_(void)
|
|
CAdapterCommon::
|
|
MixerReset
|
|
( void
|
|
)
|
|
{
|
|
ASSERT(m_pWaveBase);
|
|
|
|
WRITE_PORT_UCHAR (m_pWaveBase + DSP_REG_MIXREG, DSP_MIX_DATARESETIDX);
|
|
|
|
WRITE_PORT_UCHAR (m_pWaveBase + DSP_REG_MIXDATA, 0);
|
|
|
|
RestoreMixerSettingsFromRegistry();
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::AcknowledgeIRQ()
|
|
*****************************************************************************
|
|
* Acknowledge interrupt request.
|
|
*/
|
|
void
|
|
CAdapterCommon::
|
|
AcknowledgeIRQ
|
|
( void
|
|
)
|
|
{
|
|
ASSERT(m_pWaveBase);
|
|
READ_PORT_UCHAR (m_pWaveBase + DSP_REG_ACK16BIT);
|
|
READ_PORT_UCHAR (m_pWaveBase + DSP_REG_ACK8BIT);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::ResetController()
|
|
*****************************************************************************
|
|
* Resets the controller.
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CAdapterCommon::
|
|
ResetController(void)
|
|
{
|
|
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
|
|
|
|
// write a 1 to the reset bit
|
|
WRITE_PORT_UCHAR (m_pWaveBase + DSP_REG_RESET,1);
|
|
|
|
// wait for at least 3 microseconds
|
|
KeStallExecutionProcessor (5L); // okay, 5us
|
|
|
|
// write a 0 to the reset bit
|
|
WRITE_PORT_UCHAR (m_pWaveBase + DSP_REG_RESET,0);
|
|
|
|
// hang out for 100us
|
|
KeStallExecutionProcessor (100L);
|
|
|
|
// read the controller
|
|
BYTE ReadVal = ReadController ();
|
|
|
|
// check return value
|
|
if( ReadVal == BYTE(0xAA) )
|
|
{
|
|
ntStatus = STATUS_SUCCESS;
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::RestoreMixerSettingsFromRegistry()
|
|
*****************************************************************************
|
|
* Restores the mixer settings based on settings stored in the registry.
|
|
*/
|
|
STDMETHODIMP
|
|
CAdapterCommon::
|
|
RestoreMixerSettingsFromRegistry
|
|
( void
|
|
)
|
|
{
|
|
PREGISTRYKEY DriverKey;
|
|
PREGISTRYKEY SettingsKey;
|
|
|
|
_DbgPrintF(DEBUGLVL_VERBOSE,("[RestoreMixerSettingsFromRegistry]"));
|
|
|
|
// open the driver registry key
|
|
NTSTATUS ntStatus = PcNewRegistryKey( &DriverKey, // IRegistryKey
|
|
NULL, // OuterUnknown
|
|
DriverRegistryKey, // Registry key type
|
|
KEY_ALL_ACCESS, // Access flags
|
|
m_pDeviceObject, // Device object
|
|
NULL, // Subdevice
|
|
NULL, // ObjectAttributes
|
|
0, // Create options
|
|
NULL ); // Disposition
|
|
if(NT_SUCCESS(ntStatus))
|
|
{
|
|
UNICODE_STRING KeyName;
|
|
ULONG Disposition;
|
|
|
|
// make a unicode strong for the subkey name
|
|
RtlInitUnicodeString( &KeyName, L"Settings" );
|
|
|
|
|
|
|
|
// open the settings subkey
|
|
ntStatus = DriverKey->NewSubKey( &SettingsKey, // Subkey
|
|
NULL, // OuterUnknown
|
|
KEY_ALL_ACCESS, // Access flags
|
|
&KeyName, // Subkey name
|
|
REG_OPTION_NON_VOLATILE, // Create options
|
|
&Disposition );
|
|
if(NT_SUCCESS(ntStatus))
|
|
{
|
|
ULONG ResultLength;
|
|
|
|
if(Disposition == REG_CREATED_NEW_KEY)
|
|
{
|
|
// copy default settings
|
|
for(ULONG i = 0; i < SIZEOF_ARRAY(DefaultMixerSettings); i++)
|
|
{
|
|
MixerRegWrite( DefaultMixerSettings[i].RegisterIndex,
|
|
DefaultMixerSettings[i].RegisterSetting );
|
|
}
|
|
} else
|
|
{
|
|
// allocate data to hold key info
|
|
PVOID KeyInfo = ExAllocatePool(PagedPool, sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(DWORD));
|
|
if(NULL != KeyInfo)
|
|
{
|
|
// loop through all mixer settings
|
|
for(UINT i = 0; i < SIZEOF_ARRAY(DefaultMixerSettings); i++)
|
|
{
|
|
// init key name
|
|
RtlInitUnicodeString( &KeyName, DefaultMixerSettings[i].KeyName );
|
|
|
|
// query the value key
|
|
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))
|
|
{
|
|
// set mixer register to registry value
|
|
MixerRegWrite( DefaultMixerSettings[i].RegisterIndex,
|
|
BYTE(*(PDWORD(PartialInfo->Data))) );
|
|
}
|
|
} else
|
|
{
|
|
// if key access failed, set to default
|
|
MixerRegWrite( DefaultMixerSettings[i].RegisterIndex,
|
|
DefaultMixerSettings[i].RegisterSetting );
|
|
}
|
|
}
|
|
|
|
// free the key info
|
|
ExFreePool(KeyInfo);
|
|
} else
|
|
{
|
|
// copy default settings
|
|
for(ULONG i = 0; i < SIZEOF_ARRAY(DefaultMixerSettings); i++)
|
|
{
|
|
MixerRegWrite( DefaultMixerSettings[i].RegisterIndex,
|
|
DefaultMixerSettings[i].RegisterSetting );
|
|
}
|
|
|
|
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
|
|
// release the settings key
|
|
SettingsKey->Release();
|
|
}
|
|
|
|
// release the driver key
|
|
DriverKey->Release();
|
|
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::SaveMixerSettingsToRegistry()
|
|
*****************************************************************************
|
|
* Saves the mixer settings to the registry.
|
|
*/
|
|
STDMETHODIMP
|
|
CAdapterCommon::
|
|
SaveMixerSettingsToRegistry
|
|
( void
|
|
)
|
|
{
|
|
PREGISTRYKEY DriverKey;
|
|
PREGISTRYKEY SettingsKey;
|
|
|
|
_DbgPrintF(DEBUGLVL_VERBOSE,("[SaveMixerSettingsToRegistry]"));
|
|
|
|
// open the driver registry key
|
|
NTSTATUS ntStatus = PcNewRegistryKey( &DriverKey, // IRegistryKey
|
|
NULL, // OuterUnknown
|
|
DriverRegistryKey, // Registry key type
|
|
KEY_ALL_ACCESS, // Access flags
|
|
m_pDeviceObject, // Device object
|
|
NULL, // Subdevice
|
|
NULL, // ObjectAttributes
|
|
0, // Create options
|
|
NULL ); // Disposition
|
|
if(NT_SUCCESS(ntStatus))
|
|
{
|
|
UNICODE_STRING KeyName;
|
|
|
|
// make a unicode strong for the subkey name
|
|
RtlInitUnicodeString( &KeyName, L"Settings" );
|
|
|
|
// open the settings subkey
|
|
ntStatus = DriverKey->NewSubKey( &SettingsKey, // Subkey
|
|
NULL, // OuterUnknown
|
|
KEY_ALL_ACCESS, // Access flags
|
|
&KeyName, // Subkey name
|
|
REG_OPTION_NON_VOLATILE, // Create options
|
|
NULL );
|
|
if(NT_SUCCESS(ntStatus))
|
|
{
|
|
// loop through all mixer settings
|
|
for(UINT i = 0; i < SIZEOF_ARRAY(MixerSettings); i++)
|
|
{
|
|
// init key name
|
|
RtlInitUnicodeString( &KeyName, DefaultMixerSettings[i].KeyName );
|
|
|
|
// set the key
|
|
DWORD KeyValue = DWORD(MixerSettings[DefaultMixerSettings[i].RegisterIndex]);
|
|
ntStatus = SettingsKey->SetValueKey( &KeyName, // Key name
|
|
REG_DWORD, // Key type
|
|
PVOID(&KeyValue),
|
|
sizeof(DWORD) );
|
|
if(!NT_SUCCESS(ntStatus))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// release the settings key
|
|
SettingsKey->Release();
|
|
}
|
|
|
|
// release the driver key
|
|
DriverKey->Release();
|
|
|
|
}
|
|
|
|
return ntStatus;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::PowerChangeState()
|
|
*****************************************************************************
|
|
* Change power state for the device.
|
|
*/
|
|
STDMETHODIMP_(void)
|
|
CAdapterCommon::
|
|
PowerChangeState
|
|
(
|
|
IN POWER_STATE NewState
|
|
)
|
|
{
|
|
UINT i;
|
|
|
|
_DbgPrintF( DEBUGLVL_VERBOSE, ("[CAdapterCommon::PowerChangeState]"));
|
|
|
|
// Is this actually a state change?
|
|
if( NewState.DeviceState != m_PowerState )
|
|
{
|
|
// switch on new state
|
|
switch( NewState.DeviceState )
|
|
{
|
|
case PowerDeviceD0:
|
|
// Insert your code here for entering the full power state (D0).
|
|
// This code may be a function of the current power state. Note that
|
|
// property accesses such as volume and mute changes may occur when
|
|
// the device is in a sleep state (D1-D3) and should be cached in the
|
|
// driver to be restored upon entering D0. However, it should also be
|
|
// noted that new miniport and new streams will only be attempted at
|
|
// D0 -- PortCls will place the device in D0 prior to the NewStream call.
|
|
|
|
// Save the new state. This local value is used to determine when to cache
|
|
// property accesses and when to permit the driver from accessing the hardware.
|
|
m_PowerState = NewState.DeviceState;
|
|
|
|
// restore mixer settings
|
|
for(i = 0; i < DSP_MIX_MAXREGS - 1; i++)
|
|
{
|
|
if( i != DSP_MIX_MICVOLIDX )
|
|
{
|
|
MixerRegWrite( BYTE(i), MixerSettings[i] );
|
|
}
|
|
}
|
|
|
|
if (m_WaveMiniportSB16)
|
|
{
|
|
m_WaveMiniportSB16->RestoreSampleRate();
|
|
}
|
|
break;
|
|
|
|
case PowerDeviceD1:
|
|
// This sleep state is the lowest latency sleep state with respect to the
|
|
// latency time required to return to D0. The driver can still access
|
|
// the hardware in this state if desired. If the driver is not being used
|
|
// an inactivity timer in PortCls will place the driver in this state after
|
|
// a timeout period controllable via the registry.
|
|
|
|
case PowerDeviceD2:
|
|
// This is a medium latency sleep state. In this state the device driver
|
|
// cannot assume that it can touch the hardware so any accesses need to be
|
|
// cached and the hardware restored upon entering D0 (or D1 conceivably).
|
|
|
|
case PowerDeviceD3:
|
|
// This is a full hibernation state and is the longest latency sleep state.
|
|
// The driver cannot access the hardware in this state and must cache any
|
|
// hardware accesses and restore the hardware upon returning to D0 (or D1).
|
|
|
|
// Save the new state.
|
|
m_PowerState = NewState.DeviceState;
|
|
|
|
_DbgPrintF(DEBUGLVL_VERBOSE,(" Entering D%d",ULONG(m_PowerState)-ULONG(PowerDeviceD0)));
|
|
break;
|
|
|
|
default:
|
|
_DbgPrintF(DEBUGLVL_VERBOSE,(" Unknown Device Power State"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::QueryPowerChangeState()
|
|
*****************************************************************************
|
|
* Query to see if the device can
|
|
* change to this power state
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CAdapterCommon::
|
|
QueryPowerChangeState
|
|
(
|
|
IN POWER_STATE NewStateQuery
|
|
)
|
|
{
|
|
_DbgPrintF( DEBUGLVL_TERSE, ("[CAdapterCommon::QueryPowerChangeState]"));
|
|
|
|
// Check here to see of a legitimate state is being requested
|
|
// based on the device state and fail the call if the device/driver
|
|
// cannot support the change requested. Otherwise, return STATUS_SUCCESS.
|
|
// Note: A QueryPowerChangeState() call is not guaranteed to always preceed
|
|
// a PowerChangeState() call.
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* CAdapterCommon::QueryDeviceCapabilities()
|
|
*****************************************************************************
|
|
* Called at startup to get the caps for the device. This structure provides
|
|
* the system with the mappings between system power state and device power
|
|
* state. This typically will not need modification by the driver.
|
|
*
|
|
*/
|
|
STDMETHODIMP_(NTSTATUS)
|
|
CAdapterCommon::
|
|
QueryDeviceCapabilities
|
|
(
|
|
IN PDEVICE_CAPABILITIES PowerDeviceCaps
|
|
)
|
|
{
|
|
_DbgPrintF( DEBUGLVL_TERSE, ("[CAdapterCommon::QueryDeviceCapabilities]"));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* InterruptServiceRoutine()
|
|
*****************************************************************************
|
|
* ISR.
|
|
*/
|
|
NTSTATUS
|
|
InterruptServiceRoutine
|
|
(
|
|
IN PINTERRUPTSYNC InterruptSync,
|
|
IN PVOID DynamicContext
|
|
)
|
|
{
|
|
ASSERT(InterruptSync);
|
|
ASSERT(DynamicContext);
|
|
|
|
CAdapterCommon *that = (CAdapterCommon *) DynamicContext;
|
|
|
|
//
|
|
// We are here because the MPU tried and failed, so
|
|
// must be a wave interrupt.
|
|
//
|
|
ASSERT(that->m_pWaveBase);
|
|
|
|
//
|
|
// Read the Interrupt status register.
|
|
//
|
|
BYTE IntrStatus = that->MixerRegRead (0x82);
|
|
|
|
//
|
|
// In case we really read the interrupt status register, we should
|
|
// also USE it and make sure that we really have a wave interrupt
|
|
// and not something else!
|
|
//
|
|
if (IntrStatus & 0x03) // Voice8 or Voice16 Interrupt
|
|
{
|
|
//
|
|
// Make sure there is a wave miniport.
|
|
//
|
|
if (that->m_WaveMiniportSB16)
|
|
{
|
|
//
|
|
// Tell it it needs to do some work.
|
|
//
|
|
that->m_WaveMiniportSB16->ServiceWaveISR ();
|
|
}
|
|
|
|
//
|
|
// ACK the ISR.
|
|
//
|
|
that->AcknowledgeIRQ();
|
|
}
|
|
|
|
#ifdef EVENT_SUPPORT
|
|
//
|
|
// This code will fire a volume event in case the HW volume has changed.
|
|
//
|
|
else if (IntrStatus & 0x10) // Volume interrupt on C16X-mixers
|
|
{
|
|
//
|
|
// Ack vol interrupt
|
|
//
|
|
IntrStatus &= ~0x10;
|
|
that->MixerRegWrite (0x82, IntrStatus);
|
|
|
|
//
|
|
// Generate an event for the master volume (as an example)
|
|
//
|
|
if (that->m_TopoMiniportSB16)
|
|
{
|
|
that->m_TopoMiniportSB16->ServiceEvent ();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|