/********************************************************************************
**    Copyright (c) 1998-2000 Microsoft Corporation. All Rights Reserved.
**
**       Portions Copyright (c) 1998-1999 Intel Corporation
**
********************************************************************************/

// Every debug output has "Modulname text"
static char STR_MODULENAME[] = "ICH Common: ";

#include "common.h"


/*****************************************************************************
 * Static Members
 *****************************************************************************
 */

//
// This is the register cache including registry names and default values. The
// first WORD contains the register value and the second WORD contains a flag.
// Currently, we only set SHREG_INVALID if we have to read the register at
// startup (that's true when there is no constant default value for the
// register).  Note that we cache the registers only to prevent read access to
// the AC97 CoDec during runtime, because this is slow (40us).
// We only set SHREG_INIT if we want to set the register to default at driver
// startup.  If needed, the third field contains the registry name and the
// forth field contains a default value that is used when there is no registry
// entry.
// The flag SHREG_NOCACHE is used when we don't want to cache the register
// at all.  This is neccessary for status registers and sample rate registers.
//
tAC97Registers CAdapterCommon::m_stAC97Registers[] =
{
{0x0000, SHREG_INVALID, NULL,               0},         // AC97REG_RESET
{0x8000, SHREG_INIT,    L"MasterVolume",    0x0000},    // AC97REG_MASTER_VOLUME
{0x8000, SHREG_INIT,    L"HeadphoneVolume", 0x0000},    // AC97REG_HPHONE_VOLUME
{0x8000, SHREG_INIT,    L"MonooutVolume",   0x0000},    // AC97REG_MMONO_VOLUME
{0x0F0F, SHREG_INIT,    L"ToneControls",    0x0F0F},    // AC97REG_MASTER_TONE
{0x0000, SHREG_INVALID |
         SHREG_INIT,    L"BeepVolume",      0x0000},    // AC97REG_BEEP_VOLUME
{0x8008, SHREG_INIT,    L"PhoneVolume",     0x8008},    // AC97REG_PHONE_VOLUME
{0x8008, SHREG_INIT,    L"MicVolume",       0x8008},    // AC97REG_MIC_VOLUME
{0x8808, SHREG_INIT,    L"LineInVolume",    0x0808},    // AC97REG_LINE_IN_VOLUME
{0x8808, SHREG_INIT,    L"CDVolume",        0x0808},    // AC97REG_CD_VOLUME
{0x8808, SHREG_INIT,    L"VideoVolume",     0x0808},    // AC97REG_VIDEO_VOLUME
{0x8808, SHREG_INIT,    L"AUXVolume",       0x0808},    // AC97REG_AUX_VOLUME
{0x8808, SHREG_INIT,    L"WaveOutVolume",   0x0808},    // AC97REG_PCM_OUT_VOLUME
{0x0000, SHREG_INIT,    L"RecordSelect",    0x0404},    // AC97REG_RECORD_SELECT
{0x8000, SHREG_INIT,    L"RecordGain",      0x0000},    // AC97REG_RECORD_GAIN
{0x8000, SHREG_INIT,    L"RecordGainMic",   0x0000},    // AC97REG_RECORD_GAIN_MIC
{0x0000, SHREG_INIT,    L"GeneralPurpose",  0x0000},    // AC97REG_GENERAL
{0x0000, SHREG_INIT,    L"3DControl",       0x0000},    // AC97REG_3D_CONTROL
{0x0000, SHREG_NOCACHE, NULL,               0},         // AC97REG_RESERVED
{0x0000, SHREG_NOCACHE |
         SHREG_INIT,    L"PowerDown",       0},         // AC97REG_POWERDOWN
// AC97-2.0 registers
{0x0000, SHREG_INVALID, NULL,               0},         // AC97REG_EXT_AUDIO_ID
{0x0000, SHREG_NOCACHE |
         SHREG_INIT,    L"ExtAudioCtrl",    0x4001},    // AC97REG_EXT_AUDIO_CTRL
{0xBB80, SHREG_NOCACHE, NULL,               0},         // AC97REG_FRONT_SAMPLERATE
{0xBB80, SHREG_NOCACHE, NULL,               0},         // AC97REG_SURROUND_SAMPLERATE
{0xBB80, SHREG_NOCACHE, NULL,               0},         // AC97REG_LFE_SAMPLERATE
{0xBB80, SHREG_NOCACHE, NULL,               0},         // AC97REG_RECORD_SAMPLERATE
{0xBB80, SHREG_NOCACHE, NULL,               0},         // AC97REG_MIC_SAMPLERATE
{0x8080, SHREG_INIT,    L"CenterLFEVolume", 0x0000},    // AC97REG_CENTER_LFE_VOLUME
{0x8080, SHREG_INIT,    L"SurroundVolume",  0x0000},    // AC97REG_SURROUND_VOLUME
{0x0000, SHREG_NOCACHE, NULL,               0}          // AC97REG_RESERVED2

// We leave the other values blank.  There would be a huge gap with 31
// elements that are currently unused, and then there would be 2 other
// (used) values, the vendor IDs.  We just force a read from the vendor 
// IDs in the end of ProbeHWConfig to fill the cache.
};


//
// This is the hardware configuration information.  The first struct is for 
// nodes, which we default to FALSE.  The second struct is for Pins, which 
// contains the configuration (FALSE) and the registry string which is the
// reason for making a static struct so we can just fill in the name.
//
tHardwareConfig CAdapterCommon::m_stHardwareConfig =
{
    // Nodes
    {{FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE},
     {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE}, {FALSE},
     {FALSE}},
    // Pins
    {{FALSE, L"DisablePCBeep"},     // PINC_PCBEEP_PRESENT
     {FALSE, L"DisablePhone"},      // PINC_PHONE_PRESENT
     {FALSE, L"DisableMic2"},       // PINC_MIC2_PRESENT
     {FALSE, L"DisableVideo"},      // PINC_VIDEO_PRESENT
     {FALSE, L"DisableAUX"},        // PINC_AUX_PRESENT
     {FALSE, L"DisableHeadphone"},  // PINC_HPOUT_PRESENT
     {FALSE, L"DisableMonoOut"},    // PINC_MONOOUT_PRESENT
     {FALSE, L"DisableMicIn"},      // PINC_MICIN_PRESENT
     {FALSE, L"DisableMic"},        // PINC_MIC_PRESENT
     {FALSE, L"DisableLineIn"},     // PINC_LINEIN_PRESENT
     {FALSE, L"DisableCD"},         // PINC_CD_PRESENT
     {FALSE, L"DisableSurround"},   // PINC_SURROUND_PRESENT
     {FALSE, L"DisableCenterLFE"}}  // PINC_CENTER_LFE_PRESENT
};


#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);

    DOUT (DBG_PRINT, ("[NewAdapterCommon]"));

    STD_CREATE_BODY_
    (
        CAdapterCommon,
        Unknown,
        UnknownOuter,
        PoolType,
        PADAPTERCOMMON
    );
}   


/*****************************************************************************
 * CAdapterCommon::Init
 *****************************************************************************
 * Initialize the adapter common object -> initialize and probe HW.
 * Pass only checked resources.
 */
STDMETHODIMP_(NTSTATUS) CAdapterCommon::Init
(
    IN  PRESOURCELIST   ResourceList,
    IN  PDEVICE_OBJECT  DeviceObject
)
{
    PAGED_CODE ();

    ASSERT (ResourceList);
    ASSERT (DeviceObject);

    NTSTATUS ntStatus = STATUS_SUCCESS;

    DOUT (DBG_PRINT, ("[CAdapterCommon::Init]"));
        
    //
    // Set the topology pointer to NULL.
    //
    m_Topology = NULL;
    
    //
    // Save the device object
    //
    m_pDeviceObject = DeviceObject;

    //
    // Get the base address for the AC97 codec and bus master.
    //
    ASSERT (ResourceList->FindTranslatedPort (0));
    m_pCodecBase = (PUSHORT)ResourceList->FindTranslatedPort (0)->
                           u.Port.Start.QuadPart;

    ASSERT (ResourceList->FindTranslatedPort (1));
    m_pBusMasterBase = (PUCHAR)ResourceList->FindTranslatedPort (1)->
                              u.Port.Start.QuadPart;

    DOUT (DBG_SYSINFO, ("Configuration:\n"
                        "   Bus Master = 0x%X\n"
                        "   Codec      = 0x%X",
                        m_pBusMasterBase, m_pCodecBase));

    //
    // Set m_bDirectRead to TRUE so that all AC97 register read and
    // writes are going directly to the HW
    //
    m_bDirectRead = TRUE;

    //
    // Initialize the hardware.
    //
    ntStatus = InitAC97 ();
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;
    
    //
    // Probe hardware configuration
    //
    ntStatus = ProbeHWConfig ();
    if (!NT_SUCCESS (ntStatus))
    {
        DOUT (DBG_ERROR, ("Probing of hardware configuration failed!"));
        return ntStatus;
    }

    //
    // Now, every AC97 read access goes to the cache.
    //
    m_bDirectRead = FALSE;

    //
    // Restore the AC97 registers now.
    //
#if (DBG)
    DumpConfig ();
#endif
    ntStatus = SetAC97Default ();

    //
    // Initialize the device state.
    //
    m_PowerState = PowerDeviceD0;

    return ntStatus;
}


/*****************************************************************************
 * CAdapterCommon::~CAdapterCommon
 *****************************************************************************
 * Destructor.
 */
CAdapterCommon::~CAdapterCommon ()
{
    PAGED_CODE ();

    DOUT (DBG_PRINT, ("[CAdapterCommon::~CAdapterCommon]"));
}


#if (DBG)
/*****************************************************************************
 * CAdapterCommon::DumpConfig
 *****************************************************************************
 * Dumps the HW configuration for the AC97 codec.
 */
void CAdapterCommon::DumpConfig (void)
{
    PAGED_CODE ();

    DOUT (DBG_PRINT, ("[CAdapterCommon::DumpConfig]"));

    //
    // Print debug output for MICIN.
    //
    if (GetPinConfig (PINC_MICIN_PRESENT))
    {
        DOUT (DBG_PROBE, ("MICIN found"));
    }
    else
    {
        DOUT (DBG_PROBE, ("No MICIN found"));
    }

    //
    // Print debug output for tone controls.
    //
    if (GetNodeConfig (NODEC_TONE_PRESENT))
    {
        DOUT (DBG_PROBE, ("Tone controls found"));
    }
    else
    {
        DOUT (DBG_PROBE, ("No tone controls found"));
    }

    //
    // Print debug output for mono out.
    //
    if (!GetPinConfig (PINC_MONOOUT_PRESENT))
    {
        DOUT (DBG_PROBE, ("No mono out found"));
    }

    //
    // Print debug output for headphones.
    //
    if (!GetPinConfig (PINC_HPOUT_PRESENT))
    {
        DOUT (DBG_PROBE, ("No headphone out found"));
    }

    //
    // Print debug output for loudness.
    //
    if (GetNodeConfig (NODEC_LOUDNESS_PRESENT))
    {
        DOUT (DBG_PROBE, ("Loudness found"));
    }
    else
    {
        DOUT (DBG_PROBE, ("No Loudness found"));
    }

    //
    // Print debug output for 3D.
    //
    if (GetNodeConfig (NODEC_3D_PRESENT))
    {
        DOUT (DBG_PROBE, ("3D controls found"));
    }
    else
    {
        DOUT (DBG_PROBE, ("No 3D controls found"));
    }

    //
    // Print debug output for pc beep.
    //
    if (GetPinConfig (PINC_PCBEEP_PRESENT))
    {
        DOUT (DBG_PROBE, ("PC beep found"));
    }
    else
    {
        DOUT (DBG_PROBE, ("No PC beep found"));
    }

    //
    // Print debug output for phone line (or mono line input).
    //
    if (GetPinConfig (PINC_PHONE_PRESENT))
    {
        DOUT (DBG_PROBE, ("Phone found"));
    }
    else
    {
        DOUT (DBG_PROBE, ("No Phone found"));
    }

    //
    // Print debug output for video.
    //
    if (GetPinConfig (PINC_VIDEO_PRESENT))
    {
        DOUT (DBG_PROBE, ("Video in found"));
    }
    else
    {
        DOUT (DBG_PROBE, ("No Video in found"));
    }

    //
    // Print debug output for AUX.
    //
    if (GetPinConfig (PINC_AUX_PRESENT))
    {
        DOUT (DBG_PROBE, ("AUX in found"));
    }
    else
    {
        DOUT (DBG_PROBE, ("No AUX in found"));
    }

    //
    // Print debug output for second miorophone.
    //
    if (GetPinConfig (PINC_MIC2_PRESENT))
    {
        DOUT (DBG_PROBE, ("MIC2 found"));
    }
    else
    {
        DOUT (DBG_PROBE, ("No MIC2 found"));
    }
    
    //
    // Print debug output for 3D stuff.
    //
    if (GetNodeConfig (NODEC_3D_PRESENT))
    {
        if (GetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE))
        {
            DOUT (DBG_PROBE, ("Adjustable 3D center control found"));
        }
        if (GetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE))
        {
            DOUT (DBG_PROBE, ("Nonadjustable 3D depth control found"));
        }
    }

    //
    // Print debug output for quality of master volume.
    //
    if (GetNodeConfig (NODEC_6BIT_MASTER_VOLUME))
    {
        DOUT (DBG_PROBE, ("6bit master out found"));
    }
    else
    {
        DOUT (DBG_PROBE, ("5bit master out found"));
    }

    //
    // Print debug output for quality of headphones volume.
    //
    if (GetPinConfig (PINC_HPOUT_PRESENT))
    {
        if (GetNodeConfig (NODEC_6BIT_HPOUT_VOLUME))
        {
            DOUT (DBG_PROBE, ("6bit headphone out found"));
        }
        else
        {
            DOUT (DBG_PROBE, ("5bit headphone out found"));
        }
    }

    //
    // Print debug output for quality of mono out volume.
    //
    if (GetPinConfig (PINC_MONOOUT_PRESENT))
    {
        if (GetNodeConfig (NODEC_6BIT_MONOOUT_VOLUME))
        {
            DOUT (DBG_PROBE, ("6bit mono out found"));
        }
        else
        {
            DOUT (DBG_PROBE, ("5bit mono out found"));
        }
    }

    //
    // Print sample rate information.
    //
    if (GetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED))
    {
        DOUT (DBG_PROBE, ("PCM variable sample rate supported"));
    }
    else
    {
        DOUT (DBG_PROBE, ("only 48KHz PCM supported"));
    }

    //
    // Print double rate information.
    //
    if (GetNodeConfig (NODEC_PCM_DOUBLERATE_SUPPORTED))
    {
        DOUT (DBG_PROBE, ("PCM double sample rate supported"));
    }

    //
    // Print mic rate information.
    //
    if (GetNodeConfig (NODEC_MIC_VARIABLERATE_SUPPORTED))
    {
        DOUT (DBG_PROBE, ("MIC variable sample rate supported"));
    }
    else
    {
        DOUT (DBG_PROBE, ("only 48KHz MIC supported"));
    }

    // print DAC information
    if (GetNodeConfig (NODEC_CENTER_DAC_PRESENT))
    {
        DOUT (DBG_PROBE, ("center DAC found"));
    }
    if (GetNodeConfig (NODEC_SURROUND_DAC_PRESENT))
    {
        DOUT (DBG_PROBE, ("surround DAC found"));
    }
    if (GetNodeConfig (NODEC_LFE_DAC_PRESENT))
    {
        DOUT (DBG_PROBE, ("LFE DAC found"));
    }
}
#endif

/*****************************************************************************
 * CAdapterCommon::NonDelegatingQueryInterface
 *****************************************************************************
 * Obtains an interface.  This function works just like a COM QueryInterface
 * call and is used if the object is not being aggregated.
 * We basically just check any GUID we know and return this object in case we
 * know it.
 */
STDMETHODIMP_(NTSTATUS) CAdapterCommon::NonDelegatingQueryInterface
(
    IN  REFIID  Interface,
    OUT PVOID * Object
)
{
    PAGED_CODE ();

    ASSERT (Object);

    DOUT (DBG_PRINT, ("[CAdapterCommon::NonDelegatingQueryInterface]"));

    // Is it IID_IUnknown?
    if (IsEqualGUIDAligned (Interface, IID_IUnknown))
    {
        *Object = (PVOID)(PUNKNOWN)(PADAPTERCOMMON)this;
    }
    else
    // or IID_IAdapterCommon ...
    if (IsEqualGUIDAligned (Interface, IID_IAdapterCommon))
    {
        *Object = (PVOID)(PADAPTERCOMMON)this;
    }
    else
    // or IID_IAdapterPowerManagement ...
    if (IsEqualGUIDAligned (Interface, IID_IAdapterPowerManagement))
    {
        *Object = (PVOID)(PADAPTERPOWERMANAGEMENT)this;
    }
    else
    {
        // nothing found, must be an unknown interface.
        *Object = NULL;
        return STATUS_INVALID_PARAMETER;
    }

    //
    // We reference the interface for the caller.
    //
    ((PUNKNOWN)*Object)->AddRef ();
    return STATUS_SUCCESS;
}

/*****************************************************************************
 * CAdapterCommon::InitAC97
 *****************************************************************************
 * Initialize the ICH (without hosing the modem if it got installed first).
 */
NTSTATUS CAdapterCommon::InitAC97 (void)
{
    PAGED_CODE ();
    
    DOUT (DBG_PRINT, ("[CAdapterCommon::InitAC97]"));

    //
    // First check if there is an AC link to the primary CoDec.
    //
    NTSTATUS ntStatus = PrimaryCodecReady ();
    if (NT_SUCCESS (ntStatus))
    {
        //
        // Second, reset this primary CoDec; If this is a AMC97 CoDec, only
        // the audio registers are reset. If this is a MC97 CoDec, the CoDec
        // should ignore the reset (according to the spec).
        //
        WriteCodecRegister (AC97REG_RESET, 0x00, -1);
        
        ntStatus = PowerUpCodec ();
    }
    else
    {
        DOUT (DBG_ERROR, ("Initialization of AC97 CoDec failed."));
    }

    return ntStatus;
}

/*****************************************************************************
 * CAdapterCommon::Check6thBitSupport
 *****************************************************************************
 * Probes for 6th bit volume control support.
 * The passed parameters are the AC97 register that has the volume control and
 * the node config that should be set in this case.
 */
NTSTATUS CAdapterCommon::Check6thBitSupport
(
    IN AC97Register AC97Reg,
    IN TopoNodeConfig Config
)
{
    NTSTATUS    ntStatus;
    WORD        wCodecReg;
    WORD        wOriginal;

    // Read the current value.
    ntStatus = ReadCodecRegister (AC97Reg, &wOriginal);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;
    
    // Write the 6th bit; for mono controls we write 0x20, for stereo
    // controls 0x2020.
    ntStatus = WriteCodecRegister (AC97Reg,
                    (AC97Reg == AC97REG_MMONO_VOLUME) ? 0x0020 : 0x2020, -1);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    // And read back.
    ntStatus = ReadCodecRegister (AC97Reg, &wCodecReg);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    // Check return. For mono 0x20 and for stereo 0x2020.
    if (((wCodecReg & 0x0020) && (AC97Reg == AC97REG_MMONO_VOLUME)) ||
        (wCodecReg & 0x2020))
    {
        SetNodeConfig (Config, TRUE);
    }
    else
    {
        SetNodeConfig (Config, FALSE);
    }

    // Restore original value.
    WriteCodecRegister (AC97Reg, wOriginal, -1);

    return ntStatus;
}

/*****************************************************************************
 * CAdapterCommon::ProbeHWConfig
 *****************************************************************************
 * Probes the hardware configuration.
 * If this function returns with an error, then the configuration is not
 * complete! Probing the registers is done by reading them (and comparing with
 * the HW default value) or when the default is unknown, writing to them and
 * reading back + restoring.
 * Additionally, we read the registry so that a HW vendor can overwrite (means
 * disable) found registers in case the adapter (e.g. video) is not visible to
 * the user (he can't plug in a video audio there).
 *
 * This is a very long function with all of the error checking!
 */
NTSTATUS CAdapterCommon::ProbeHWConfig (void)
{
    PAGED_CODE ();

    NTSTATUS ntStatus = STATUS_SUCCESS;
    DWORD    dwGlobalStatus;
    WORD     wCodecID;
    WORD     wCodecReg;

    DOUT (DBG_PRINT, ("[CAdapterCommon::ProbeHWConfig]"));

    //
    // Wait for the whatever 97 to complete reset and establish a link.
    //
    ntStatus = PrimaryCodecReady ();
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    //
    // Master volume is one of the supported registers on an AC97
    //
    ntStatus = ReadCodecRegister (AC97REG_MASTER_VOLUME, &wCodecReg);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    // Default is x8000.
    if (wCodecReg != 0x8000)
        return STATUS_NO_SUCH_DEVICE;

    //
    // This gives us information about the AC97 CoDec
    //
    ntStatus = ReadCodecRegister (AC97REG_RESET, &wCodecID);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    //
    // Fill out the configuration stuff.
    //

    SetPinConfig (PINC_MICIN_PRESENT, wCodecID & 0x0001);
    
    // Check if OEM wants to disable MIC record line.
    if (DisableAC97Pin (PINC_MICIN_PRESENT))
        SetPinConfig (PINC_MICIN_PRESENT, FALSE);

    // If we still have MIC record line, enable the DAC in ext. audio register.
    if (GetPinConfig (PINC_MICIN_PRESENT))
        // Enable ADC MIC.
        WriteCodecRegister (AC97REG_EXT_AUDIO_CTRL, 0, 0x4000);
    else
        // Disable ADC MIC.
        WriteCodecRegister (AC97REG_EXT_AUDIO_CTRL, 0x4000, 0x4000);

    //
    // Continue setting configuration information.
    //

    SetNodeConfig (NODEC_TONE_PRESENT, wCodecID & 0x0004);
    SetNodeConfig (NODEC_SIMUL_STEREO_PRESENT, wCodecID & 0x0008);
    SetPinConfig (PINC_HPOUT_PRESENT, wCodecID & 0x0010);
    
    // Check if OEM wants to disable headphone output.
    if (DisableAC97Pin (PINC_HPOUT_PRESENT))
        SetPinConfig (PINC_HPOUT_PRESENT, FALSE);

    SetNodeConfig (NODEC_LOUDNESS_PRESENT, wCodecID & 0x0020);
    SetNodeConfig (NODEC_3D_PRESENT, wCodecID & 0x7C00);

    //
    // Test for the input pins that are always there but could be disabled
    // by the HW vender
    //

    // Check if OEM wants to disable mic input.
    SetPinConfig (PINC_MIC_PRESENT, !DisableAC97Pin (PINC_MIC_PRESENT));
    
    // Check if OEM wants to disable line input.
    SetPinConfig (PINC_LINEIN_PRESENT, !DisableAC97Pin (PINC_LINEIN_PRESENT));

    // Check if OEM wants to disable CD input.
    SetPinConfig (PINC_CD_PRESENT, !DisableAC97Pin (PINC_CD_PRESENT));


    //
    // For the rest, we have to probe the registers.
    //

    //
    // Test for Mono out.
    //
    ntStatus = ReadCodecRegister (AC97REG_MMONO_VOLUME, &wCodecReg);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    // Default is x8000.
    SetPinConfig (PINC_MONOOUT_PRESENT, (wCodecReg == 0x8000));

    // Check if OEM wants to disable mono output.
    if (DisableAC97Pin (PINC_MONOOUT_PRESENT))
        SetPinConfig (PINC_MONOOUT_PRESENT, FALSE);

    //
    // Test for PC beeper support.
    //
    ntStatus = ReadCodecRegister (AC97REG_BEEP_VOLUME, &wCodecReg);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    // default is x0 or x8000. If it's 0x8000 then we know for sure that the
    // CoDec has a PcBeep, otherwise we have to check the register
    if (wCodecReg == 0x8000)
        SetPinConfig (PINC_PCBEEP_PRESENT, TRUE);
    else if (!wCodecReg)
    {
        // mute the pc beeper.
        ntStatus = WriteCodecRegister (AC97REG_BEEP_VOLUME, 0x8000, -1);
        if (!NT_SUCCESS (ntStatus))
            return ntStatus;

        // read back
        ntStatus = ReadCodecRegister (AC97REG_BEEP_VOLUME, &wCodecReg);
        if (!NT_SUCCESS (ntStatus))
            return ntStatus;

        if (wCodecReg == 0x8000)
        {
            // yep, we have support.
            SetPinConfig (PINC_PCBEEP_PRESENT, TRUE);
            // reset to default value.
            WriteCodecRegister (AC97REG_BEEP_VOLUME, 0x0, -1);
        }
        else
            // nope, not present
            SetPinConfig (PINC_PCBEEP_PRESENT, FALSE);
    }
    else
        // any other value then 0x0 and 0x8000.
        SetPinConfig (PINC_PCBEEP_PRESENT, FALSE);

    // Check if OEM wants to disable beeper support.
    if (DisableAC97Pin (PINC_PCBEEP_PRESENT))
        SetPinConfig (PINC_PCBEEP_PRESENT, FALSE);

    //
    // Test for phone support.
    //
    ntStatus = ReadCodecRegister (AC97REG_PHONE_VOLUME, &wCodecReg);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    // Default is x8008.
    SetPinConfig (PINC_PHONE_PRESENT, (wCodecReg == 0x8008));

    // Check if OEM wants to disable phone input.
    if (DisableAC97Pin (PINC_PHONE_PRESENT))
        SetPinConfig (PINC_PHONE_PRESENT, FALSE);

    //
    // Test for video support.
    //
    ntStatus = ReadCodecRegister (AC97REG_VIDEO_VOLUME, &wCodecReg);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    // Default is x8808.
    SetPinConfig (PINC_VIDEO_PRESENT, (wCodecReg == 0x8808));

    // Check if OEM wants to disable video input.
    if (DisableAC97Pin (PINC_VIDEO_PRESENT))
        SetPinConfig (PINC_VIDEO_PRESENT, FALSE);

    //
    // Test for Aux support.
    //
    ntStatus = ReadCodecRegister (AC97REG_AUX_VOLUME, &wCodecReg);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    // Default is 0x8808.
    SetPinConfig (PINC_AUX_PRESENT, (wCodecReg == 0x8808));

    // Check if OEM wants to disable aux input.
    if (DisableAC97Pin (PINC_AUX_PRESENT))
        SetPinConfig (PINC_AUX_PRESENT, FALSE);

    //
    // Test for Mic2 source.
    //
    ntStatus = ReadCodecRegister (AC97REG_GENERAL, &wCodecReg);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    // Test for Mic2 select bit.
    if (wCodecReg & 0x0100)
        SetPinConfig (PINC_MIC2_PRESENT, TRUE);
    else
    {
        // Select Mic2 as source.
        ntStatus = WriteCodecRegister (AC97REG_GENERAL, 0x0100, 0x0100);
        if (!NT_SUCCESS (ntStatus))
            return ntStatus;

        // Read back.
        ntStatus = ReadCodecRegister (AC97REG_GENERAL, &wCodecReg);
        if (!NT_SUCCESS (ntStatus))
            return ntStatus;

        if (wCodecReg & 0x0100)
        {
            // Yep, we have support so set it to the default value.
            SetPinConfig (PINC_MIC2_PRESENT, TRUE);
            // reset to default value.
            WriteCodecRegister (AC97REG_GENERAL, 0, 0x0100);
        }
        else
            SetPinConfig (PINC_MIC2_PRESENT, FALSE);
    }

    // Check if OEM wants to disable mic2 input.
    if (DisableAC97Pin (PINC_MIC2_PRESENT))
        SetPinConfig (PINC_MIC2_PRESENT, FALSE);

    //
    // Test the 3D controls.
    //
    if (GetNodeConfig (NODEC_3D_PRESENT))
    {
        //
        // First test for fixed 3D controls. Write default value ...
        //
        ntStatus = WriteCodecRegister (AC97REG_3D_CONTROL, 0, -1);
        if (!NT_SUCCESS (ntStatus))
            return ntStatus;

        // Read 3D register. Default is 0 when adjustable, otherwise it is
        // a fixed value.
        ntStatus = ReadCodecRegister (AC97REG_3D_CONTROL, &wCodecReg);
        if (!NT_SUCCESS (ntStatus))
            return ntStatus;

        //
        // Check center and depth separately.
        //

        // For center
        SetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE, !(wCodecReg & 0x0F00));

        // For depth
        SetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE, !(wCodecReg & 0x000F));

        //
        // Test for adjustable controls.
        //
        WriteCodecRegister (AC97REG_3D_CONTROL, 0x0A0A, -1);

        // Read 3D register. Now it should be 0x0A0A for adjustable controls,
        // otherwise it is a fixed control or simply not there.
        ReadCodecRegister (AC97REG_3D_CONTROL, &wCodecReg);

        // Restore the default value
        WriteCodecRegister (AC97REG_3D_CONTROL, 0, -1);

        // Check the center control for beeing adjustable
        if (GetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE) &&
            (wCodecReg & 0x0F00) != 0x0A00)
        {
            SetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE, FALSE);
        }
        
        // Check the depth control for beeing adjustable
        if (GetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE) &&
            (wCodecReg & 0x000F) != 0x000A)
        {
            SetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE, FALSE);
        }
    }

    //
    // Check for 6th bit support in volume controls.  To check the 6th bit, 
    // we first have to write a value (with 6th bit set) and then read it 
    // back.  After that, we should restore the register to its default value.
    //

    //
    // Start with the master volume.
    //
    Check6thBitSupport (AC97REG_MASTER_VOLUME, NODEC_6BIT_MASTER_VOLUME);

    //
    // Check for a headphone volume control.
    //
    if (GetPinConfig (PINC_HPOUT_PRESENT))
    {
        Check6thBitSupport (AC97REG_HPHONE_VOLUME, NODEC_6BIT_HPOUT_VOLUME);
    }

    //
    // Mono out there?
    //
    if (GetPinConfig (PINC_MONOOUT_PRESENT))
    {
        Check6thBitSupport (AC97REG_MMONO_VOLUME, NODEC_6BIT_MONOOUT_VOLUME);
    }

    //
    // Get extended AC97 V2.0 information
    //
    ntStatus = ReadCodecRegister (AC97REG_EXT_AUDIO_ID, &wCodecReg);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    //
    // Store the information
    //
    SetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED, wCodecReg & 0x0001);
    SetNodeConfig (NODEC_PCM_DOUBLERATE_SUPPORTED, wCodecReg & 0x0002);
    SetNodeConfig (NODEC_MIC_VARIABLERATE_SUPPORTED, wCodecReg & 0x0008);
    SetNodeConfig (NODEC_CENTER_DAC_PRESENT, wCodecReg & 0x0040);
    SetNodeConfig (NODEC_SURROUND_DAC_PRESENT, wCodecReg & 0x0080);
    SetNodeConfig (NODEC_LFE_DAC_PRESENT, wCodecReg & 0x0100);

    //
    // In case we have some features get some more information and program
    // the codec.
    //
    if (wCodecReg)
    {
        //
        // Enable variable sample rate in the control register and disable
        // double rate. Also enable all DACs.
        //
        WriteCodecRegister (AC97REG_EXT_AUDIO_CTRL, wCodecReg & 0x0009, 0x380B);

        //
        // Check for codecs that have only one sample rate converter. These
        // codecs will stick registers AC97REG_FRONT_SAMPLERATE and
        // AC97REG_RECORD_SAMPLERATE together.
        //
        if (GetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED))
        {
            // The default of the sample rate registers should be 0xBB80.
            WriteCodecRegister (AC97REG_FRONT_SAMPLERATE, 0xBB80, 0xFFFF);

            // Write 44.1KHz into record VSR, then check playback again.
            WriteCodecRegister (AC97REG_RECORD_SAMPLERATE, 0xAC44, 0xFFFF);
            ntStatus = ReadCodecRegister (AC97REG_FRONT_SAMPLERATE, &wCodecReg);
            WriteCodecRegister (AC97REG_RECORD_SAMPLERATE, 0xBB80, 0xFFFF);
            if (!NT_SUCCESS (ntStatus))
                return ntStatus;

            //
            // Set the flag accordingly
            //
            SetNodeConfig (NODEC_PCM_VSR_INDEPENDENT_RATES, (wCodecReg == 0xBB80));
        }

        //
        // Check multichanel support on the ICH.
        //
        if (GetNodeConfig (NODEC_SURROUND_DAC_PRESENT))
        {
            dwGlobalStatus = ReadBMControlRegister32 (GLOB_STA);
            
            //
            // Codec supports >2 chanel, does ICH too?
            //
            if ((GetNodeConfig (NODEC_CENTER_DAC_PRESENT) ||
                GetNodeConfig (NODEC_LFE_DAC_PRESENT)) &&
                (dwGlobalStatus & GLOB_STA_MC6))
            {
                SetPinConfig (PINC_CENTER_LFE_PRESENT, TRUE);
            }
            else
            {
                SetPinConfig (PINC_CENTER_LFE_PRESENT, FALSE);
            }

            //
            // Do we support at least 4 channels?
            //
            SetPinConfig (PINC_SURROUND_PRESENT, (dwGlobalStatus & GLOB_STA_MC4));
        }
        else
        {
            //
            // Only 2 channel (stereo) support.
            //
            SetPinConfig (PINC_CENTER_LFE_PRESENT, FALSE);
            SetPinConfig (PINC_SURROUND_PRESENT, FALSE);
        }
    }

    // Check if OEM wants to disable surround output.
    if (DisableAC97Pin (PINC_SURROUND_PRESENT))
        SetPinConfig (PINC_SURROUND_PRESENT, FALSE);
    
    // Check if OEM wants to disable center and LFE output.
    if (DisableAC97Pin (PINC_CENTER_LFE_PRESENT))
        SetPinConfig (PINC_CENTER_LFE_PRESENT, FALSE);

    //
    // Check the 6th bit support for the additional channels.
    //
    if (GetPinConfig (PINC_SURROUND_PRESENT))
        Check6thBitSupport (AC97REG_SURROUND_VOLUME, NODEC_6BIT_SURROUND_VOLUME);
    
    if (GetPinConfig (PINC_CENTER_LFE_PRESENT))
        Check6thBitSupport (AC97REG_CENTER_LFE_VOLUME, NODEC_6BIT_CENTER_LFE_VOLUME);

    //
    // We read these registers because they are dependent on the codec.
    //
    ReadCodecRegister (AC97REG_VENDOR_ID1, &wCodecReg);
    ReadCodecRegister (AC97REG_VENDOR_ID2, &wCodecReg);

    return STATUS_SUCCESS;
}


/*****************************************************************************
 * CAdapterCommon::AcquireCodecSemiphore
 *****************************************************************************
 * Acquires the AC97 semiphore.  This can not be called at dispatch level
 * because it can timeout if a lower IRQL thread has the semaphore.
 */
NTSTATUS CAdapterCommon::AcquireCodecSemiphore ()
{
    PAGED_CODE ();

    DOUT (DBG_PRINT, ("[CAdapterCommon::AcquireCodecSemiphore]"));

    ULONG ulCount = 0;
    while (READ_PORT_UCHAR (m_pBusMasterBase + CAS) & CAS_CAS)
    {
        //
        // Do we want to give up??
        //
        if (ulCount++ > 100)
        {
            DOUT (DBG_ERROR, ("Cannot acquire semaphore."));
            return STATUS_IO_TIMEOUT;
        }

        //
        // Let's wait a little, 40us and then try again.
        //
        KeStallExecutionProcessor (40L);
    }

    return STATUS_SUCCESS;
}


/*****************************************************************************
 * CAdapterCommon::ReadCodecRegister
 *****************************************************************************
 * Reads a AC97 register. Don't call at PASSIVE_LEVEL.
 */
STDMETHODIMP_(NTSTATUS) CAdapterCommon::ReadCodecRegister
(
    IN  AC97Register reg,
    OUT PWORD wData
)
{
    PAGED_CODE ();
    
    ASSERT (wData);
    ASSERT (reg < AC97REG_INVALID);    // audio can only be in the primary codec

    NTSTATUS ntStatus;
    ULONG    Status;

    DOUT (DBG_PRINT, ("[CAdapterCommon::ReadCodecRegister]"));
    
    //
    // Check if we have to access the HW directly.
    //
    if (m_bDirectRead || (m_stAC97Registers[reg].wFlags & SHREG_INVALID) ||
        (m_stAC97Registers[reg].wFlags & SHREG_NOCACHE))
    {
        //
        // Grab the codec access semiphore.
        //
        ntStatus = AcquireCodecSemiphore ();
        if (!NT_SUCCESS (ntStatus))
        {
            DOUT (DBG_ERROR, ("ReadCodecRegister couldn't acquire the semiphore"
                " for reg. %s", reg <= AC97REG_RESERVED2 ? RegStrings[reg] :
                                reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" :
                                reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" :
                                "REG_INVALID"));
            return ntStatus;
        }

        //
        // Read the data.
        //
        *wData = READ_PORT_USHORT (m_pCodecBase + reg);

        //
        // Check to see if the read was successful.
        //
        Status = READ_PORT_ULONG ((PULONG)(m_pBusMasterBase + GLOB_STA));
        if (Status & GLOB_STA_RCS)
        {
            //
            // clear the timeout bit
            //
            WRITE_PORT_ULONG ((PULONG)(m_pBusMasterBase + GLOB_STA), Status);
            *wData = 0;
            DOUT (DBG_ERROR, ("ReadCodecRegister timed out for register %s",
                    reg <= AC97REG_RESERVED2 ? RegStrings[reg] :
                    reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" :
                    reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" :
                    "REG_INVALID"));
            return STATUS_IO_TIMEOUT;
        } 
        
        //
        // Clear invalid flag
        //
        m_stAC97Registers[reg].wCache = *wData;
        m_stAC97Registers[reg].wFlags &= ~SHREG_INVALID;
        
        DOUT (DBG_REGS, ("AC97READ: %s = 0x%04x (HW)",
                reg <= AC97REG_RESERVED2 ? RegStrings[reg] :
                reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" :
                reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : 
                "REG_INVALID", *wData));
    }
    else
    {
        //
        // Otherwise, use the value in the cache.
        //
        *wData = m_stAC97Registers[reg].wCache;
        DOUT (DBG_REGS, ("AC97READ: %s = 0x%04x (C)",
                reg <= AC97REG_RESERVED2 ? RegStrings[reg] :
                reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" :
                reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : 
                "REG_INVALID", *wData));
    }

    return STATUS_SUCCESS;
}


/*****************************************************************************
 * CAdapterCommon::WriteCodecRegister
 *****************************************************************************
 * Writes to a AC97 register.  This can only be done at passive level because
 * the AcquireCodecSemiphore call could fail!
 */
STDMETHODIMP_(NTSTATUS) CAdapterCommon::WriteCodecRegister
(
    IN  AC97Register reg,
    IN  WORD wData,
    IN  WORD wMask
)
{
    PAGED_CODE ();
    
    ASSERT (reg < AC97REG_INVALID);    // audio can only be in the primary codec

    WORD TempData = 0;
    NTSTATUS ntStatus = STATUS_SUCCESS;

    DOUT (DBG_PRINT, ("[CAdapterCommon::WriteCodecRegister]"));

    //
    // No mask?  Could happen when you try to prg. left channel of a
    // mono volume.
    //
    if (!wMask)
        return STATUS_SUCCESS;

    //
    // Check to see if we are only writing specific bits.  If so, we want
    // to leave some bits in the register alone.
    //
    if (wMask != 0xffff)
    {
        //
        // Read the current register contents.
        //
        ntStatus = ReadCodecRegister (reg, &TempData);
        if (!NT_SUCCESS (ntStatus))
        {
            DOUT (DBG_ERROR, ("WriteCodecRegiser read for mask failed"));
            return ntStatus;
        }

        //
        // Do the masking.
        //
        TempData &= ~wMask;
        TempData |= (wMask & wData);
    }
    else
    {
        TempData = wData;
    }
    

    //
    // Grab the codec access semiphore.
    //
    ntStatus = AcquireCodecSemiphore ();
    if (!NT_SUCCESS (ntStatus))
    {
        DOUT (DBG_ERROR, ("WriteCodecRegister failed for register %s",
            reg <= AC97REG_RESERVED2 ? RegStrings[reg] :
            reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" :
            reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : "REG_INVALID"));
        return ntStatus;
    }
            
    //
    // Write the data.
    //
    WRITE_PORT_USHORT (m_pCodecBase + reg, TempData);

    //
    // Update cache.
    //
    m_stAC97Registers[reg].wCache = TempData;
    
    DOUT (DBG_REGS, ("AC97WRITE: %s -> 0x%04x", 
                   reg <= AC97REG_RESERVED2 ? RegStrings[reg] :
                   reg == AC97REG_VENDOR_ID1 ? "REG_VENDOR_ID1" :
                   reg == AC97REG_VENDOR_ID2 ? "REG_VENDOR_ID2" : 
                   "REG_INVALID", TempData));
    
    
    return STATUS_SUCCESS;
}


/*****************************************************************************
 * CAdapterCommon::PrimaryCodecReady
 *****************************************************************************
 * Checks whether the primary codec is present and ready.  This may take
 * awhile if we are bringing it up from a cold reset so give it a second
 * before giving up.
 */
NTSTATUS CAdapterCommon::PrimaryCodecReady (void)
{
    PAGED_CODE ();

    DOUT (DBG_PRINT, ("[CAdapterCommon::PrimaryCodecReady]"));

    
    //
    // Enable the AC link and raise the reset line.
    //
    DWORD dwRegValue = ReadBMControlRegister32 (GLOB_CNT);
    
    // If someone enabled GPI Interrupt Enable, then he hopefully handles that
    // too.
    dwRegValue = (dwRegValue | GLOB_CNT_COLD) & ~(GLOB_CNT_ACLOFF | GLOB_CNT_PRIE);
    WriteBMControlRegister (GLOB_CNT, dwRegValue);

    //
    // Wait for the Codec to be ready.
    //
    ULONG           WaitCycles = 200;
    LARGE_INTEGER   WaitTime = RtlConvertLongToLargeInteger (-50000);   // wait 5000us (5ms) relative

    do
    {
        if (READ_PORT_ULONG ((PULONG)(m_pBusMasterBase + GLOB_STA)) & 
            GLOB_STA_PCR)
        {
            return STATUS_SUCCESS;
        }

        KeDelayExecutionThread (KernelMode, FALSE, &WaitTime);
    } while (WaitCycles--);

    DOUT (DBG_ERROR, ("PrimaryCodecReady timed out!"));
    return STATUS_IO_TIMEOUT;
}


/*****************************************************************************
 * CAdapterCommon::PowerUpCodec
 *****************************************************************************
 * Sets the Codec to the highest power state and waits until the Codec reports
 * that the power state is reached.
 */
NTSTATUS CAdapterCommon::PowerUpCodec (void)
{
    PAGED_CODE ();

    WORD        wCodecReg;
    NTSTATUS    ntStatus;

    DOUT (DBG_PRINT, ("[CAdapterCommon::PowerUpCodec]"));

    //
    // Power up the Codec.
    //
    WriteCodecRegister (AC97REG_POWERDOWN, 0x00, -1);

    //
    // Wait for the Codec to be powered up.
    //
    ULONG           WaitCycles = 200;
    LARGE_INTEGER   WaitTime = RtlConvertLongToLargeInteger (-50000);   // wait 5000us (5ms) relative

    do
    {
        //
        // Read the power management register.
        //
        ntStatus = ReadCodecRegister (AC97REG_POWERDOWN, &wCodecReg);
        if (!NT_SUCCESS (ntStatus))
        {
            wCodecReg = 0;      // Will cause an error.
            break;
        }

        //
        // Check the power state. Should be ready.
        //
        if ((wCodecReg & 0x0f) == 0x0f)
            break;

        //
        // Let's wait a little, 5ms and then try again.
        //
        KeDelayExecutionThread (KernelMode, FALSE, &WaitTime);
    } while (WaitCycles--);

    // Check if we timed out.
    if ((wCodecReg & 0x0f) != 0x0f)
    {
        DOUT (DBG_ERROR, ("PowerUpCodec timed out. CoDec not powered up."));
        ntStatus = STATUS_DEVICE_NOT_READY;
    }

    return ntStatus;
}


/*****************************************************************************
 * CAdapterCommon::ProgramSampleRate
 *****************************************************************************
 * Programs the sample rate. If the rate cannot be programmed, the routine
 * restores the register and returns STATUS_UNSUCCESSFUL.
 * We don't handle double rate sample rates here, because the Intel ICH con-
 * troller cannot serve CoDecs with double rate or surround sound. If you want
 * to modify this driver for another AC97 controller, then you might want to
 * change this function too.
 */
STDMETHODIMP_(NTSTATUS) CAdapterCommon::ProgramSampleRate
(
    IN  AC97Register    Register,
    IN  DWORD           dwSampleRate
)
{
    PAGED_CODE ();

    WORD     wOldRateReg, wCodecReg;
    NTSTATUS ntStatus;

    DOUT (DBG_PRINT, ("[CAdapterCommon::ProgramSampleRate]"));

    //
    // Check if we support variable sample rate.
    //
    switch(Register)
    {
        case AC97REG_MIC_SAMPLERATE:
            //
            // Variable sample rate supported?
            //
            if (GetNodeConfig (NODEC_MIC_VARIABLERATE_SUPPORTED))
            {
                // Range supported?
                if (dwSampleRate > 48000ul)
                {
                    // Not possible.
                    DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate));
                    return STATUS_NOT_SUPPORTED;
                }
            }
            else
            {
                // Only 48000KHz possible.
                if (dwSampleRate != 48000ul)
                {
                    DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate));
                    return STATUS_NOT_SUPPORTED;
                }

                return STATUS_SUCCESS;
            }
            break;

        case AC97REG_FRONT_SAMPLERATE:
        case AC97REG_SURROUND_SAMPLERATE:
        case AC97REG_LFE_SAMPLERATE:
        case AC97REG_RECORD_SAMPLERATE:
            //
            // Variable sample rate supported?
            //
            if (GetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED))
            {
                //
                // Check range supported
                //
                if (dwSampleRate > 48000ul)
                {
                    DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate));
                    return STATUS_NOT_SUPPORTED;
                }
            }
            else
            {
                // Only 48KHz possible.
                if (dwSampleRate != 48000ul)
                {
                    DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate));
                    return STATUS_NOT_SUPPORTED;
                }

                return STATUS_SUCCESS;
            }
            break;

        default:
            DOUT (DBG_ERROR, ("Invalid sample rate register!"));
            return STATUS_UNSUCCESSFUL;
    }

    
    //
    // Save the old sample rate register.
    //
    ntStatus = ReadCodecRegister (Register, &wOldRateReg);
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;
    
    //
    // program the rate.
    //
    ntStatus = WriteCodecRegister (Register, (WORD)dwSampleRate, -1);
    if (!NT_SUCCESS (ntStatus))
    {
        DOUT (DBG_ERROR, ("Cannot program sample rate."));
        return ntStatus;
    }

    //
    // Read it back.
    //
    ntStatus = ReadCodecRegister (Register, &wCodecReg);
    if (!NT_SUCCESS (ntStatus))
    {
        DOUT (DBG_ERROR, ("Cannot read sample rate."));
        return ntStatus;
    }

    //
    // Validate.
    //
    if (wCodecReg != dwSampleRate)
    {
        //
        // restore sample rate and ctrl register.
        //
        WriteCodecRegister (Register, wOldRateReg, -1);
        
        DOUT (DBG_VSR, ("Samplerate %d not supported", dwSampleRate));
        return STATUS_NOT_SUPPORTED;
    }
    
    DOUT (DBG_VSR, ("Samplerate changed to %d.", dwSampleRate));
    return STATUS_SUCCESS;
}


/*****************************************************************************
 * CAdapterCommon::PowerChangeState
 *****************************************************************************
 * Change power state for the device.  We handle the codec, PowerChangeNotify 
 * in the wave miniport handles the DMA registers.
 */
STDMETHODIMP_(void) CAdapterCommon::PowerChangeState
(
    IN  POWER_STATE NewState
)
{
    PAGED_CODE ();

    NTSTATUS ntStatus = STATUS_SUCCESS;

    DOUT (DBG_PRINT, ("[CAdapterCommon::PowerChangeNotify]"));

    //
    // Check to see if this is the current power state.
    //
    if (NewState.DeviceState == m_PowerState)
    {
        DOUT (DBG_POWER, ("New device state equals old state."));
        return;
    }

    //
    // Check the new device state.
    //
    if ((NewState.DeviceState < PowerDeviceD0) ||
        (NewState.DeviceState > PowerDeviceD3))
    {
        DOUT (DBG_ERROR, ("Unknown device state: D%d.", 
             (ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0));
        return;
    }

    DOUT (DBG_POWER, ("Changing state to D%d.", (ULONG)NewState.DeviceState -
                    (ULONG)PowerDeviceD0));

    //
    // Switch on new state.
    //
    switch (NewState.DeviceState)
    {
        case PowerDeviceD0:
            //
            // If we are coming from D2 or D3 we have to restore the registers cause
            // there might have been a power loss.
            //
            if ((m_PowerState == PowerDeviceD3) || (m_PowerState == PowerDeviceD2))
            {
                //
                // Reset AD3 to indicate that we are now awake.
                // Because the system has only one power irp at a time, we are sure
                // that the modem driver doesn't get called while we are restoring
                // power.
                //
                WriteBMControlRegister (GLOB_STA,
                    ReadBMControlRegister32 (GLOB_STA) & ~GLOB_STA_AD3);

                //
                // Restore codec registers.
                //
                ntStatus = RestoreCodecRegisters ();
            }
            else        // We are coming from power state D1
            {
                ntStatus = PowerUpCodec ();
            }

            // Print error code.
            if (!NT_SUCCESS (ntStatus))
            {
                DOUT (DBG_ERROR, ("PowerChangeState failed to restore the codec."));
            }
            break;
    
        case PowerDeviceD1:
            //
            // This sleep state is the lowest latency sleep state with respect
            // to the latency time required to return to D0. 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.
            //
    
            // Let's power down the DAC/ADC's and analog mixer.
            WriteCodecRegister (AC97REG_POWERDOWN, 0x0700, -1);
            break;

        case PowerDeviceD2:
        case PowerDeviceD3:
            //
            // This is a full hibernation state and is the longest latency sleep
            // state. In this modes the power could be removed or reduced that
            // much that the AC97 controller looses information, so we save
            // whatever we have to save.
            //

            //
            // Powerdown ADC, DAC, Mixer, Vref, HP amp, and Exernal Amp but not
            // AC-link and Clk
            //
            WriteCodecRegister (AC97REG_POWERDOWN, 0xCF00, -1);

            //
            // Only in D3 mode we set the AD3 bit and evtl. shut off the AC link.
            //
            if (NewState.DeviceState == PowerDeviceD3)
            {
                //
                // Set the AD3 bit.
                //
                ULONG ulReg = ReadBMControlRegister32 (GLOB_STA);
                WriteBMControlRegister (GLOB_STA, ulReg | GLOB_STA_AD3);
                
                //
                // We check if the modem is sleeping. If it is, we can shut off the
                // AC link also. We shut off the AC link also if the modem is not
                // there.
                //
                if ((ulReg & GLOB_STA_MD3) || !(ulReg & GLOB_STA_SCR))
                {
                    // Set Codec to super sleep
                    WriteCodecRegister (AC97REG_POWERDOWN, 0xFF00, -1);
    
                    // Disable the AC-link signals
                    ulReg = ReadBMControlRegister32 (GLOB_CNT);
                    WriteBMControlRegister (GLOB_CNT, (ulReg | GLOB_CNT_ACLOFF) & ~GLOB_CNT_COLD);
                }
            }
            break;
    }
    
    //
    // 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;
    DOUT (DBG_POWER, ("Entering D%d", (ULONG)m_PowerState -
                      (ULONG)PowerDeviceD0));
}


/*****************************************************************************
 * CAdapterCommon::QueryPowerChangeState
 *****************************************************************************
 * Query to see if the device can change to this power state
 */
STDMETHODIMP_(NTSTATUS) CAdapterCommon::QueryPowerChangeState
(
    IN  POWER_STATE NewState
)
{
    PAGED_CODE ();

    DOUT (DBG_PRINT, ("[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.

    // check the new state being requested
    switch (NewState.DeviceState)
    {
        case PowerDeviceD0:
        case PowerDeviceD1:
        case PowerDeviceD2:
        case PowerDeviceD3:
            return STATUS_SUCCESS;
        
        default:
            DOUT (DBG_ERROR, ("Unknown device state: D%d.", 
                 (ULONG)NewState.DeviceState - (ULONG)PowerDeviceD0));
            return STATUS_NOT_IMPLEMENTED;
    }
}


/*****************************************************************************
 * 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.
 * If the driver modifies these mappings then the driver is not allowed to
 * change the mapping to a weaker power state (e.g. from S1->D3 to S1->D1).
 * 
 */
STDMETHODIMP_(NTSTATUS) CAdapterCommon::QueryDeviceCapabilities
(
    IN  PDEVICE_CAPABILITIES PowerDeviceCaps
)
{
    PAGED_CODE ();

    DOUT (DBG_PRINT, ("[CAdapterCommon::QueryDeviceCapabilities]"));

    return STATUS_SUCCESS;
}


/*****************************************************************************
 * CAdapterCommon::RestoreAC97Registers
 *****************************************************************************
 * Preset the AC97 registers with default values. The routine first checks if
 * There are registry entries for the default values. If not, we have hard
 * coded values too ;)
 */
NTSTATUS CAdapterCommon::SetAC97Default (void)
{
    PAGED_CODE ();
    
    PREGISTRYKEY    DriverKey;
    PREGISTRYKEY    SettingsKey;
    UNICODE_STRING  sKeyName;
    ULONG           ulDisposition;
    ULONG           ulResultLength;
    PVOID           KeyInfo = NULL;

    DOUT (DBG_PRINT, ("[CAdapterCommon::SetAC97Default]"));
    
    // open the driver registry key
    NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey,        // IRegistryKey
                                          NULL,              // OuterUnknown
                                          DriverRegistryKey, // Registry key type
                                          KEY_READ,          // Access flags
                                          m_pDeviceObject,   // Device object
                                          NULL,              // Subdevice
                                          NULL,              // ObjectAttributes
                                          0,                 // Create options
                                          NULL);             // Disposition
    if (NT_SUCCESS (ntStatus))
    {
        // make a unicode string for the subkey name
        RtlInitUnicodeString (&sKeyName, L"Settings");

        // open the settings subkey
        ntStatus = DriverKey->NewSubKey (&SettingsKey,            // Subkey
                                         NULL,                    // OuterUnknown
                                         KEY_READ,                // Access flags
                                         &sKeyName,               // Subkey name
                                         REG_OPTION_NON_VOLATILE, // Create options
                                         &ulDisposition);

        if (NT_SUCCESS (ntStatus))
        {
            // allocate data to hold key info
            KeyInfo = ExAllocatePool (PagedPool,
                                      sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
                                      sizeof(WORD));
            if (NULL != KeyInfo)
            {
                // loop through all mixer settings
                for (AC97Register i = AC97REG_RESET; i <= AC97REG_RESERVED2;
                    i = (AC97Register)(i + 1))
                {
                    if (m_stAC97Registers[i].wFlags & SHREG_INIT)
                    {
                        // init key name
                        RtlInitUnicodeString (&sKeyName,
                                              m_stAC97Registers[i].sRegistryName);
    
                        // query the value key
                        ntStatus = SettingsKey->QueryValueKey (&sKeyName,
                                        KeyValuePartialInformation,
                                        KeyInfo,
                                        sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
                                            sizeof(WORD),
                                        &ulResultLength);
                        if (NT_SUCCESS (ntStatus))
                        {
                            PKEY_VALUE_PARTIAL_INFORMATION PartialInfo =
                                        (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo;

                            if (PartialInfo->DataLength == sizeof(WORD))
                            {
                                // set mixer register to registry value
                                WriteCodecRegister
                                    (i, *(PWORD)PartialInfo->Data, -1);
                            }
                            else    // write the hard coded default
                            {
                                // if key access failed, set to default
                                WriteCodecRegister
                                    (i, m_stAC97Registers[i].wWantedDefault, -1);
                            }
                        }
                        else  // write the hard coded default
                        {
                            // if key access failed, set to default
                            WriteCodecRegister
                                (i, m_stAC97Registers[i].wWantedDefault, -1);
                        }
                    }
                }

                // we want to return status success even if the last QueryValueKey
                // failed.
                ntStatus = STATUS_SUCCESS;

                // free the key info
                ExFreePool (KeyInfo);
            }

            // release the settings key
            SettingsKey->Release ();
        }

        // release the driver key
        DriverKey->Release ();
    }


    // in case we did not query the registry (cause of lack of resources)
    // restore default values and return insufficient resources.
    if (!NT_SUCCESS (ntStatus) || !KeyInfo)
    {
        // copy hard coded default settings
        for (AC97Register i = AC97REG_RESET; i < AC97REG_RESERVED2;
             i = (AC97Register)(i + 1))
        {
            if (m_stAC97Registers[i].wFlags & SHREG_INIT)
            {
                WriteCodecRegister (i, m_stAC97Registers[i].wWantedDefault, -1);
            }
        }

        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
    }

    return ntStatus;
}


/*****************************************************************************
 * CAdapterCommon::DisableAC97Pin
 *****************************************************************************
 * Returns TRUE when the HW vendor wants to disable the pin. A disabled pin is
 * not shown to the user (means it is not included in the topology). The
 * reason for doing this could be that some of the input lines like Aux or
 * Video are not available to the user (to plug in something) but the codec
 * can handle those lines.
 */
BOOL CAdapterCommon::DisableAC97Pin
(
    IN  TopoPinConfig pin
)
{
    PAGED_CODE ();

    PREGISTRYKEY    DriverKey;
    PREGISTRYKEY    SettingsKey;
    UNICODE_STRING  sKeyName;
    ULONG           ulDisposition;
    ULONG           ulResultLength;
    PVOID           KeyInfo = NULL;
    BOOL            bDisable = FALSE;

    DOUT (DBG_PRINT, ("[CAdapterCommon::DisableAC97Pin]"));
    
    // open the driver registry key
    NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey,        // IRegistryKey
                                          NULL,              // OuterUnknown
                                          DriverRegistryKey, // Registry key type
                                          KEY_READ,          // Access flags
                                          m_pDeviceObject,   // Device object
                                          NULL,              // Subdevice
                                          NULL,              // ObjectAttributes
                                          0,                 // Create options
                                          NULL);             // Disposition
    if (NT_SUCCESS (ntStatus))
    {
        // make a unicode string for the subkey name
        RtlInitUnicodeString (&sKeyName, L"Settings");

        // open the settings subkey
        ntStatus = DriverKey->NewSubKey (&SettingsKey,            // Subkey
                                         NULL,                    // OuterUnknown
                                         KEY_READ,                // Access flags
                                         &sKeyName,               // Subkey name
                                         REG_OPTION_NON_VOLATILE, // Create options
                                         &ulDisposition);

        if (NT_SUCCESS (ntStatus))
        {
            // allocate data to hold key info
            KeyInfo = ExAllocatePool (PagedPool,
                                      sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
                                      sizeof(BYTE));
            if (NULL != KeyInfo)
            {
                // init key name
                RtlInitUnicodeString (&sKeyName, m_stHardwareConfig.
                                            Pins[pin].sRegistryName);
    
                // query the value key
                ntStatus = SettingsKey->QueryValueKey (&sKeyName,
                                   KeyValuePartialInformation,
                                   KeyInfo,
                                   sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
                                        sizeof(BYTE),
                                   &ulResultLength );
                if (NT_SUCCESS (ntStatus))
                {
                    PKEY_VALUE_PARTIAL_INFORMATION PartialInfo =
                                (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo;

                    if (PartialInfo->DataLength == sizeof(BYTE))
                    {
                        // store the value
                        if (*(PBYTE)PartialInfo->Data)
                            bDisable = TRUE;
                        else
                            bDisable = FALSE;
                    }
                }

                // free the key info
                ExFreePool (KeyInfo);
            }

            // release the settings key
            SettingsKey->Release ();
        }

        // release the driver key
        DriverKey->Release ();
    }

    // if one of the stuff above fails we return the default, which is FALSE.
    return bDisable;
}


/*****************************************************************************
 * CAdapterCommon::RestoreCodecRegisters
 *****************************************************************************
 * write back cached mixer values to codec registers
 */
NTSTATUS CAdapterCommon::RestoreCodecRegisters (void)
{
    PAGED_CODE ();

    DOUT (DBG_PRINT, ("[CAdapterCommon::RestoreCodecRegisters]"));

    //
    // Initialize the AC97 codec.
    //
    NTSTATUS ntStatus = InitAC97 ();
    if (!NT_SUCCESS (ntStatus))
        return ntStatus;

    //
    // Restore all codec registers.  Failure is not critical.
    //
    for (AC97Register i = AC97REG_MASTER_VOLUME; i < AC97REG_RESERVED2; 
        i = (AC97Register)(i + 1))
    {
        WriteCodecRegister (i, m_stAC97Registers[i].wCache, -1);
    }

    return STATUS_SUCCESS;
}

/*****************************************************************************
 * CAdapterCommon::ReadChannelConfigDefault
 *****************************************************************************
 * This function reads the default channel config from the registry. The
 * registry entry "ChannelConfig" is set every every time we get a
 * KSPROPERTY_AUDIO_CHANNEL_CONFIG for the DAC node.
 * In case the key doesn't exist we assume a channel config of stereo speakers,
 * cause that is the default of DSOUND.
 */
STDMETHODIMP_(void) CAdapterCommon::ReadChannelConfigDefault
(
    PDWORD  pdwChannelConfig,
    PWORD   pwChannels
)
{
    PAGED_CODE ();

    PREGISTRYKEY    DriverKey;
    PREGISTRYKEY    SettingsKey;
    UNICODE_STRING  sKeyName;
    ULONG           ulDisposition;
    ULONG           ulResultLength;
    PVOID           KeyInfo = NULL;

    DOUT (DBG_PRINT, ("[CAdapterCommon::ReadChannelConfigDefault]"));
    
    // This is the default: 2 speakers, stereo.
    *pdwChannelConfig = KSAUDIO_SPEAKER_STEREO;
    *pwChannels = 2;
    
    // open the driver registry key
    NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey,        // IRegistryKey
                                          NULL,              // OuterUnknown
                                          DriverRegistryKey, // Registry key type
                                          KEY_READ,          // Access flags
                                          m_pDeviceObject,   // Device object
                                          NULL,              // Subdevice
                                          NULL,              // ObjectAttributes
                                          0,                 // Create options
                                          NULL);             // Disposition
    if (NT_SUCCESS (ntStatus))
    {
        // make a unicode string for the subkey name
        RtlInitUnicodeString (&sKeyName, L"Settings");

        // open the settings subkey
        ntStatus = DriverKey->NewSubKey (&SettingsKey,            // Subkey
                                         NULL,                    // OuterUnknown
                                         KEY_READ,                // Access flags
                                         &sKeyName,               // Subkey name
                                         REG_OPTION_NON_VOLATILE, // Create options
                                         &ulDisposition);

        if (NT_SUCCESS (ntStatus))
        {
            // allocate data to hold key info
            KeyInfo = ExAllocatePool (PagedPool,
                                      sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
                                      sizeof(DWORD));
            if (NULL != KeyInfo)
            {
                // init key name
                RtlInitUnicodeString (&sKeyName, L"ChannelConfig");
    
                // query the value key
                ntStatus = SettingsKey->QueryValueKey (&sKeyName,
                                   KeyValuePartialInformation,
                                   KeyInfo,
                                   sizeof(KEY_VALUE_PARTIAL_INFORMATION) +
                                        sizeof(DWORD),
                                   &ulResultLength );
                if (NT_SUCCESS (ntStatus))
                {
                    PKEY_VALUE_PARTIAL_INFORMATION PartialInfo =
                                (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo;

                    if (PartialInfo->DataLength == sizeof(DWORD))
                    {
                        switch (*(PLONG)PartialInfo->Data)
                        {
                        case KSAUDIO_SPEAKER_QUAD:
                        case KSAUDIO_SPEAKER_SURROUND:
                            if (GetPinConfig (PINC_SURROUND_PRESENT))
                            {
                                *pdwChannelConfig = *(PDWORD)PartialInfo->Data;
                                *pwChannels = 4;
                            }
                            break;

                        case KSAUDIO_SPEAKER_5POINT1:
                            if (GetPinConfig (PINC_SURROUND_PRESENT) &&
                                GetPinConfig (PINC_CENTER_LFE_PRESENT))
                            {
                                *pdwChannelConfig = *(PDWORD)PartialInfo->Data;
                                *pwChannels = 6;
                            }
                            break;
                        }
                    }
                }

                // free the key info
                ExFreePool (KeyInfo);
            }

            // release the settings key
            SettingsKey->Release ();
        }

        // release the driver key
        DriverKey->Release ();
    }
}

/*****************************************************************************
 * CAdapterCommon::WriteChannelConfigDefault
 *****************************************************************************
 * This function writes the default channel config to the registry. The
 * registry entry "ChannelConfig" is set every every time we get a
 * KSPROPERTY_AUDIO_CHANNEL_CONFIG for the DAC node.
 */
STDMETHODIMP_(void) CAdapterCommon::WriteChannelConfigDefault (DWORD dwChannelConfig)
{
    PAGED_CODE ();

    PREGISTRYKEY    DriverKey;
    PREGISTRYKEY    SettingsKey;
    UNICODE_STRING  sKeyName;
    ULONG           ulDisposition;

    DOUT (DBG_PRINT, ("[CAdapterCommon::WriteChannelConfigDefault]"));
    
    // open the driver registry key
    NTSTATUS ntStatus = PcNewRegistryKey (&DriverKey,        // IRegistryKey
                                          NULL,              // OuterUnknown
                                          DriverRegistryKey, // Registry key type
                                          KEY_WRITE,         // Access flags
                                          m_pDeviceObject,   // Device object
                                          NULL,              // Subdevice
                                          NULL,              // ObjectAttributes
                                          0,                 // Create options
                                          NULL);             // Disposition
    if (NT_SUCCESS (ntStatus))
    {
        // make a unicode string for the subkey name
        RtlInitUnicodeString (&sKeyName, L"Settings");

        // open the settings subkey
        ntStatus = DriverKey->NewSubKey (&SettingsKey,            // Subkey
                                         NULL,                    // OuterUnknown
                                         KEY_WRITE,               // Access flags
                                         &sKeyName,               // Subkey name
                                         REG_OPTION_NON_VOLATILE, // Create options
                                         &ulDisposition);

        if (NT_SUCCESS (ntStatus))
        {
            // init key name
            RtlInitUnicodeString (&sKeyName, L"ChannelConfig");

            // query the value key
            ntStatus = SettingsKey->SetValueKey (&sKeyName,
                                                 REG_DWORD,
                                                 &dwChannelConfig,
                                                 sizeof (DWORD));
            if (!NT_SUCCESS (ntStatus))
            {
                DOUT (DBG_ERROR, ("Could not write the ChannelConfig to registry."));
            }

            // release the settings key
            SettingsKey->Release ();
        }

        // release the driver key
        DriverKey->Release ();
    }
}

/*****************************************************************************
 * Non paged code begins here
 *****************************************************************************
 */ 

#pragma code_seg()
/*****************************************************************************
 * CAdapterCommon::WriteBMControlRegister
 *****************************************************************************
 * Writes a byte (UCHAR) to BusMaster Control register.
 */
STDMETHODIMP_(void) CAdapterCommon::WriteBMControlRegister
(
    IN  ULONG ulOffset,
    IN  UCHAR ucValue
)
{
    DOUT (DBG_PRINT, ("[CAdapterCommon::WriteBMControlRegister] (UCHAR)"));
    
    WRITE_PORT_UCHAR ((PUCHAR)(m_pBusMasterBase + ulOffset), ucValue);

    DOUT (DBG_REGS, ("WriteBMControlRegister wrote 0x%2x to 0x%4x.", 
                   ucValue, m_pBusMasterBase + ulOffset));
}

/*****************************************************************************
 * CAdapterCommon::WriteBMControlRegister
 *****************************************************************************
 * Writes a word (USHORT) to BusMaster Control register.
 */
STDMETHODIMP_(void) CAdapterCommon::WriteBMControlRegister
(
    IN  ULONG ulOffset,
    IN  USHORT usValue
)
{
    DOUT (DBG_PRINT, ("[CAdapterCommon::WriteBMControlRegister (USHORT)]"));

    WRITE_PORT_USHORT ((PUSHORT)(m_pBusMasterBase + ulOffset), usValue);

    DOUT (DBG_REGS, ("WriteBMControlRegister wrote 0x%4x to 0x%4x", 
                   usValue, m_pBusMasterBase + ulOffset));
}

/*****************************************************************************
 * CAdapterCommon::WriteBMControlRegister
 *****************************************************************************
 * Writes a DWORD (ULONG) to BusMaster Control register.
 */
STDMETHODIMP_(void) CAdapterCommon::WriteBMControlRegister
(
    IN  ULONG ulOffset,
    IN  ULONG ulValue
)
{
    DOUT (DBG_PRINT, ("[CAdapterCommon::WriteBMControlRegister (ULONG)]"));

    WRITE_PORT_ULONG ((PULONG)(m_pBusMasterBase + ulOffset), ulValue);

    DOUT (DBG_REGS, ("WriteBMControlRegister wrote 0x%8x to 0x%4x.", 
                   ulValue, m_pBusMasterBase + ulOffset));
}

/*****************************************************************************
 * CAdapterCommon::ReadBMControlRegister8
 *****************************************************************************
 * Read a byte (UCHAR) from BusMaster Control register.
 */
STDMETHODIMP_(UCHAR) CAdapterCommon::ReadBMControlRegister8
(
    IN  ULONG ulOffset
)
{
    UCHAR ucValue = UCHAR(-1);

    DOUT (DBG_PRINT, ("[CAdapterCommon::ReadBMControlRegister8]"));

    ucValue = READ_PORT_UCHAR ((PUCHAR)(m_pBusMasterBase + ulOffset));

    DOUT (DBG_REGS, ("ReadBMControlRegister read 0x%2x from 0x%4x.", ucValue,
                   m_pBusMasterBase + ulOffset));

    return ucValue;
}

/*****************************************************************************
 * CAdapterCommon::ReadBMControlRegister16
 *****************************************************************************
 * Read a word (USHORT) from BusMaster Control register.
 */
STDMETHODIMP_(USHORT) CAdapterCommon::ReadBMControlRegister16
(
    IN  ULONG ulOffset
)
{
    USHORT usValue = USHORT(-1);

    DOUT (DBG_PRINT, ("[CAdapterCommon::ReadBMControlRegister16]"));

    usValue = READ_PORT_USHORT ((PUSHORT)(m_pBusMasterBase + ulOffset));

    DOUT (DBG_REGS, ("ReadBMControlRegister read 0x%4x = 0x%4x", usValue,
                   m_pBusMasterBase + ulOffset));

    return usValue;
}

/*****************************************************************************
 * CAdapterCommon::ReadBMControlRegister32
 *****************************************************************************
 * Read a dword (ULONG) from BusMaster Control register.
 */
STDMETHODIMP_(ULONG) CAdapterCommon::ReadBMControlRegister32
(
    IN  ULONG ulOffset
)
{
    ULONG ulValue = ULONG(-1);

    DOUT (DBG_PRINT, ("[CAdapterCommon::ReadBMControlRegister32]"));

    ulValue = READ_PORT_ULONG ((PULONG)(m_pBusMasterBase + ulOffset));

    DOUT (DBG_REGS, ("ReadBMControlRegister read 0x%8x = 0x%4x", ulValue,
                   m_pBusMasterBase + ulOffset));

    return ulValue;
}