1817 lines
72 KiB
C++
1817 lines
72 KiB
C++
|
/********************************************************************************
|
||
|
** 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[] = "prophnd: ";
|
||
|
|
||
|
#include <limits.h>
|
||
|
#include "mintopo.h"
|
||
|
|
||
|
// These are the values passed to the property handler in the instance
|
||
|
// parameter that normally represents the channel.
|
||
|
const LONG CHAN_LEFT = 0;
|
||
|
const LONG CHAN_RIGHT = 1;
|
||
|
const LONG CHAN_MASTER = -1;
|
||
|
|
||
|
|
||
|
// paged code goes here.
|
||
|
#pragma code_seg("PAGE")
|
||
|
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CMiniportTopologyICH::SetMultichannelMute
|
||
|
*****************************************************************************
|
||
|
* This function is used to set one of the multichannel mutes.
|
||
|
* It takes the master mono into account when calculating the mute.
|
||
|
* Make sure that you updated the stNodeCache before calling this function.
|
||
|
*/
|
||
|
NTSTATUS CMiniportTopologyICH::SetMultichannelMute
|
||
|
(
|
||
|
IN CMiniportTopologyICH *that,
|
||
|
IN TopoNodes Mute
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
|
BOOL bMute;
|
||
|
|
||
|
// The first calls to SetMultichannelMute could be without valid
|
||
|
// cache information because WDMAUD might currently query the nodes
|
||
|
// (this is at system startup). When WDMAUD queried all nodes then
|
||
|
// all cache information will be valid.
|
||
|
if (that->stNodeCache[NODE_VIRT_MASTERMONO_MUTE].bLeftValid &&
|
||
|
that->stNodeCache[Mute].bLeftValid)
|
||
|
{
|
||
|
// We get the master mono mute and the mute that is to change.
|
||
|
// Then we "or" them and write the value to the register.
|
||
|
bMute = that->stNodeCache[NODE_VIRT_MASTERMONO_MUTE].lLeft ||
|
||
|
that->stNodeCache[Mute].lLeft;
|
||
|
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (Mute),
|
||
|
bMute ? -1 : 0,
|
||
|
that->AdapterCommon->GetNodeMask (Mute));
|
||
|
|
||
|
DOUT (DBG_PROPERTY, ("SET: %s -> 0x%x", NodeStrings[Mute], (int)bMute));
|
||
|
}
|
||
|
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CMiniportTopologyICH::SetMultichannelVolume
|
||
|
*****************************************************************************
|
||
|
* This function is used to set one of the multichannel volumes.
|
||
|
* It takes the master mono into account when calculating the volume.
|
||
|
* Make sure that you updated the stNodeCache before calling this function.
|
||
|
*/
|
||
|
NTSTATUS CMiniportTopologyICH::SetMultichannelVolume
|
||
|
(
|
||
|
IN CMiniportTopologyICH *that,
|
||
|
IN TopoNodes Volume
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS ntStatus = STATUS_SUCCESS;
|
||
|
LONG lMinimum, lMaximum;
|
||
|
ULONG uStep;
|
||
|
LONG lLevel;
|
||
|
WORD wRegister;
|
||
|
|
||
|
// The first calls to SetMultichannelMute could be without valid
|
||
|
// cache information because WDMAUD might currently query the nodes
|
||
|
// (this is at system startup). When WDMAUD queried all nodes then
|
||
|
// all cache information will be valid.
|
||
|
if (that->stNodeCache[NODE_VIRT_MASTERMONO_VOLUME].bLeftValid &&
|
||
|
that->stNodeCache[NODE_VIRT_MASTERMONO_VOLUME].bRightValid &&
|
||
|
that->stNodeCache[Volume].bLeftValid &&
|
||
|
that->stNodeCache[Volume].bRightValid)
|
||
|
{
|
||
|
// We get the master mono volume and the volume that is to change.
|
||
|
// Then we substract master mono from it and write the value to the
|
||
|
// register.
|
||
|
lLevel = that->stNodeCache[Volume].lLeft +
|
||
|
that->stNodeCache[NODE_VIRT_MASTERMONO_VOLUME].lLeft;
|
||
|
|
||
|
// Translate the dB value into a register value.
|
||
|
|
||
|
// Get the registered DB values
|
||
|
ntStatus = GetDBValues (that->AdapterCommon, Volume,
|
||
|
&lMinimum, &lMaximum, &uStep);
|
||
|
if (!NT_SUCCESS(ntStatus))
|
||
|
return ntStatus;
|
||
|
|
||
|
// Check borders.
|
||
|
if (lLevel < lMinimum) lLevel = lMinimum;
|
||
|
if (lLevel > lMaximum) lLevel = lMaximum;
|
||
|
|
||
|
// Calculate the register value
|
||
|
wRegister = (WORD)(((lMaximum + uStep / 2) - lLevel) / uStep) << 8;
|
||
|
|
||
|
// Get the right value too.
|
||
|
lLevel = that->stNodeCache[Volume].lRight +
|
||
|
that->stNodeCache[NODE_VIRT_MASTERMONO_VOLUME].lRight;
|
||
|
|
||
|
// Check borders.
|
||
|
if (lLevel < lMinimum) lLevel = lMinimum;
|
||
|
if (lLevel > lMaximum) lLevel = lMaximum;
|
||
|
|
||
|
// Add it to the register value.
|
||
|
wRegister += (WORD)(((lMaximum + uStep / 2) - lLevel) / uStep);
|
||
|
|
||
|
// Write it.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (Volume),
|
||
|
wRegister,
|
||
|
that->AdapterCommon->GetNodeMask (Volume));
|
||
|
|
||
|
DOUT (DBG_PROPERTY, ("SET: %s -> 0x%x/0x%x", NodeStrings[Volume],
|
||
|
that->stNodeCache[Volume].lLeft +
|
||
|
that->stNodeCache[NODE_VIRT_MASTERMONO_VOLUME].lLeft,
|
||
|
lLevel));
|
||
|
}
|
||
|
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CMiniportTopologyICH::GetDBValues
|
||
|
*****************************************************************************
|
||
|
* This function is used internally and does no parameter checking. The only
|
||
|
* parameter that could be invalid is the node.
|
||
|
* It returns the dB values (means minimum, maximum, step) of the node control,
|
||
|
* mainly for the property call "basic support". Sure, the node must be a
|
||
|
* volume or tone control node, not a mute or mux node.
|
||
|
*/
|
||
|
NTSTATUS CMiniportTopologyICH::GetDBValues
|
||
|
(
|
||
|
IN PADAPTERCOMMON AdapterCommon,
|
||
|
IN TopoNodes Node,
|
||
|
OUT LONG *plMinimum,
|
||
|
OUT LONG *plMaximum,
|
||
|
OUT ULONG *puStep
|
||
|
)
|
||
|
{
|
||
|
DOUT (DBG_PRINT, ("[CMiniportTopologyICH::GetDBValues]"));
|
||
|
|
||
|
// This is going to be simple. Check the node and return the parameters.
|
||
|
switch (Node)
|
||
|
{
|
||
|
// These nodes could have 5bit or 6bit controls, so we first
|
||
|
// have to check this.
|
||
|
case NODE_MASTEROUT_VOLUME:
|
||
|
case NODE_FRONT_VOLUME:
|
||
|
case NODE_HPOUT_VOLUME:
|
||
|
case NODE_SURROUND_VOLUME:
|
||
|
case NODE_CENTER_VOLUME:
|
||
|
case NODE_LFE_VOLUME:
|
||
|
case NODE_VIRT_MONOOUT_VOLUME1:
|
||
|
case NODE_VIRT_MONOOUT_VOLUME2:
|
||
|
// needed for the config query
|
||
|
TopoNodeConfig config;
|
||
|
|
||
|
// which node to query?
|
||
|
config = NODEC_6BIT_MONOOUT_VOLUME;
|
||
|
if ((Node == NODE_MASTEROUT_VOLUME) || (Node == NODE_FRONT_VOLUME))
|
||
|
config = NODEC_6BIT_MASTER_VOLUME;
|
||
|
if (Node == NODE_HPOUT_VOLUME)
|
||
|
config = NODEC_6BIT_HPOUT_VOLUME;
|
||
|
if (Node == NODE_SURROUND_VOLUME)
|
||
|
config = NODEC_6BIT_SURROUND_VOLUME;
|
||
|
if ((Node == NODE_CENTER_VOLUME) || (Node == NODE_LFE_VOLUME))
|
||
|
config = NODEC_6BIT_CENTER_LFE_VOLUME;
|
||
|
|
||
|
// check if we have 6th bit support.
|
||
|
if (AdapterCommon->GetNodeConfig (config))
|
||
|
{
|
||
|
// 6bit control
|
||
|
*plMaximum = 0; // 0 dB
|
||
|
*plMinimum = 0xFFA18000; // -94.5 dB
|
||
|
*puStep = 0x00018000; // 1.5 dB
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// 5bit control
|
||
|
*plMaximum = 0; // 0 dB
|
||
|
*plMinimum = 0xFFD18000; // -46.5 dB
|
||
|
*puStep = 0x00018000; // 1.5 dB
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NODE_VIRT_MASTERMONO_VOLUME:
|
||
|
// This virtual control gets added to the speaker volumes.
|
||
|
// We assume 5-bit volumes.
|
||
|
*plMaximum = 0; // 0 dB
|
||
|
*plMinimum = 0xFFD18000; // -46.5 dB
|
||
|
*puStep = 0x00018000; // 1.5 dB
|
||
|
break;
|
||
|
|
||
|
case NODE_PCBEEP_VOLUME:
|
||
|
*plMaximum = 0; // 0 dB
|
||
|
*plMinimum = 0xFFD30000; // -45 dB
|
||
|
*puStep = 0x00030000; // 3 dB
|
||
|
break;
|
||
|
|
||
|
case NODE_PHONE_VOLUME:
|
||
|
case NODE_MICIN_VOLUME:
|
||
|
case NODE_LINEIN_VOLUME:
|
||
|
case NODE_CD_VOLUME:
|
||
|
case NODE_VIDEO_VOLUME:
|
||
|
case NODE_AUX_VOLUME:
|
||
|
case NODE_WAVEOUT_VOLUME:
|
||
|
*plMaximum = 0x000C0000; // 12 dB
|
||
|
*plMinimum = 0xFFDD8000; // -34.5 dB
|
||
|
*puStep = 0x00018000; // 1.5 dB
|
||
|
break;
|
||
|
|
||
|
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME1:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME2:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME3:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME4:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME5:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME6:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME7:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME8:
|
||
|
case NODE_MIC_VOLUME:
|
||
|
*plMaximum = 0x00168000; // 22.5 dB
|
||
|
*plMinimum = 0; // 0 dB
|
||
|
*puStep = 0x00018000; // 1.5 dB
|
||
|
break;
|
||
|
|
||
|
case NODE_BASS:
|
||
|
case NODE_TREBLE:
|
||
|
*plMaximum = 0x000A8000; // 10.5 dB
|
||
|
*plMinimum = 0xFFF58000; // -10.5 dB
|
||
|
*puStep = 0x00018000; // 1.5 dB
|
||
|
break;
|
||
|
|
||
|
// These nodes can be fixed or variable.
|
||
|
// Normally we won't display a fixed volume slider, but if 3D is
|
||
|
// supported and both sliders are fixed, we have to display one fixed
|
||
|
// slider for the advanced control panel.
|
||
|
case NODE_VIRT_3D_CENTER:
|
||
|
case NODE_VIRT_3D_DEPTH:
|
||
|
if (AdapterCommon->GetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE) &&
|
||
|
(Node == NODE_VIRT_3D_CENTER))
|
||
|
{
|
||
|
*plMaximum = 0x000F0000; // +15 dB
|
||
|
*plMinimum = 0x00000000; // 0 dB
|
||
|
*puStep = 0x00010000; // 1 dB
|
||
|
}
|
||
|
else
|
||
|
if (AdapterCommon->GetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE) &&
|
||
|
(Node == NODE_VIRT_3D_DEPTH))
|
||
|
{
|
||
|
*plMaximum = 0x000F0000; // +15 dB
|
||
|
*plMinimum = 0x00000000; // 0 dB
|
||
|
*puStep = 0x00010000; // 1 dB
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// In case it is fixed, read the value and return it.
|
||
|
WORD wRegister;
|
||
|
|
||
|
// read the register
|
||
|
if (!NT_SUCCESS (AdapterCommon->ReadCodecRegister (
|
||
|
AdapterCommon->GetNodeReg (Node), &wRegister)))
|
||
|
wRegister = 0; // in case we fail.
|
||
|
|
||
|
// mask out the control
|
||
|
wRegister &= AdapterCommon->GetNodeMask (Node);
|
||
|
if (Node == NODE_VIRT_3D_CENTER)
|
||
|
{
|
||
|
wRegister >>= 8;
|
||
|
}
|
||
|
// calculate the dB value.
|
||
|
*plMaximum = (DWORD)(-wRegister) << 16; // fixed value
|
||
|
*plMinimum = (DWORD)(-wRegister) << 16; // fixed value
|
||
|
*puStep = 0x00010000; // 1 dB
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case NODE_INVALID:
|
||
|
default:
|
||
|
// poeser pupe, tu.
|
||
|
DOUT (DBG_ERROR, ("GetDBValues: Invalid node requested."));
|
||
|
return STATUS_INVALID_PARAMETER;
|
||
|
}
|
||
|
|
||
|
return STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CMiniportTopologyICH::PropertyHandler_OnOff
|
||
|
*****************************************************************************
|
||
|
* Accesses a KSAUDIO_ONOFF value property.
|
||
|
* This function (property handler) is called by portcls every time there is a
|
||
|
* get or a set request for the node. The connection between the node type and
|
||
|
* the property handler is made in the automation table which is referenced
|
||
|
* when you register the node.
|
||
|
* We use this property handler for all nodes that have a checkbox, means mute
|
||
|
* controls and the special checkbox controls under advanced properties, which
|
||
|
* are AGC and LOUDNESS.
|
||
|
*/
|
||
|
NTSTATUS CMiniportTopologyICH::PropertyHandler_OnOff
|
||
|
(
|
||
|
IN PPCPROPERTY_REQUEST PropertyRequest
|
||
|
)
|
||
|
{
|
||
|
PAGED_CODE ();
|
||
|
|
||
|
ASSERT (PropertyRequest);
|
||
|
|
||
|
NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
|
||
|
LONG channel;
|
||
|
TopoNodes NodeDef;
|
||
|
// The major target is the object pointer to the topology miniport.
|
||
|
CMiniportTopologyICH *that =
|
||
|
(CMiniportTopologyICH *) PropertyRequest->MajorTarget;
|
||
|
|
||
|
ASSERT (that);
|
||
|
|
||
|
DOUT (DBG_PRINT, ("[CMiniportTopologyICH::PropertyHandler_OnOff]"));
|
||
|
|
||
|
// validate node
|
||
|
if (PropertyRequest->Node == (ULONG)-1)
|
||
|
return ntStatus;
|
||
|
|
||
|
// do the appropriate action for the request.
|
||
|
|
||
|
// we should do a get or a set?
|
||
|
if ((PropertyRequest->Verb & KSPROPERTY_TYPE_GET) ||
|
||
|
(PropertyRequest->Verb & KSPROPERTY_TYPE_SET))
|
||
|
{
|
||
|
// validate parameters
|
||
|
if ((PropertyRequest->InstanceSize < sizeof(LONG)) ||
|
||
|
(PropertyRequest->ValueSize < sizeof(BOOL)))
|
||
|
return ntStatus;
|
||
|
|
||
|
// get channel
|
||
|
channel = *(PLONG)PropertyRequest->Instance;
|
||
|
|
||
|
// check channel types, return when unknown
|
||
|
// as you can see, we have no multichannel support.
|
||
|
if ((channel != CHAN_LEFT) &&
|
||
|
(channel != CHAN_RIGHT) &&
|
||
|
(channel != CHAN_MASTER))
|
||
|
return ntStatus;
|
||
|
|
||
|
// We have only mono mutes or On/Off checkboxes although they might control
|
||
|
// a stereo path. For example, we have a 1-bit mute for CD Volume. This
|
||
|
// mute controls both CD Volume channels.
|
||
|
if (channel == CHAN_RIGHT)
|
||
|
return ntStatus;
|
||
|
|
||
|
// get the buffer
|
||
|
PBOOL OnOff = (PBOOL)PropertyRequest->Value;
|
||
|
|
||
|
// Switch on the node id. This is just for parameter checking.
|
||
|
// If something goes wrong, we will immediately return with
|
||
|
// ntStatus, which is STATUS_INVALID_PARAMETER.
|
||
|
switch (NodeDef = that->TransNodeNrToNodeDef (PropertyRequest->Node))
|
||
|
{
|
||
|
// These are mutes for mono volumes.
|
||
|
case NODE_PCBEEP_MUTE:
|
||
|
case NODE_PHONE_MUTE:
|
||
|
case NODE_MIC_MUTE:
|
||
|
case NODE_MICIN_MUTE:
|
||
|
case NODE_CENTER_MUTE:
|
||
|
case NODE_LFE_MUTE:
|
||
|
case NODE_VIRT_MASTERMONO_MUTE:
|
||
|
// check type
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_MUTE)
|
||
|
return ntStatus;
|
||
|
break;
|
||
|
|
||
|
// Well, this one is a AGC, although there is no _automatic_ gain
|
||
|
// control, but we have a mic boost (which is some kind of manual
|
||
|
// gain control).
|
||
|
// The 3D Bypass is a real fake, but that's how you get check boxes
|
||
|
// on the advanced control panel.
|
||
|
// Both controls are in a mono path.
|
||
|
case NODE_VIRT_WAVEOUT_3D_BYPASS:
|
||
|
case NODE_MIC_BOOST:
|
||
|
// check type
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_AGC)
|
||
|
return ntStatus;
|
||
|
break;
|
||
|
|
||
|
// Simulated Stereo is a AGC control in a stereo path.
|
||
|
case NODE_SIMUL_STEREO:
|
||
|
// check type
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_AGC)
|
||
|
return ntStatus;
|
||
|
break;
|
||
|
|
||
|
// This is a loudness control in a stereo path. We have to check the
|
||
|
// type.
|
||
|
case NODE_LOUDNESS:
|
||
|
// check type
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_LOUDNESS)
|
||
|
return ntStatus;
|
||
|
break;
|
||
|
|
||
|
// For 3D Enable and Mic are exposed as loudness in a mono path.
|
||
|
case NODE_VIRT_3D_ENABLE:
|
||
|
case NODE_MIC_SELECT:
|
||
|
// check type
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_LOUDNESS)
|
||
|
return ntStatus;
|
||
|
break;
|
||
|
|
||
|
// These are mutes in a stereo path.
|
||
|
// Because the HW has only one mute-bit for the stereo channel, we
|
||
|
// expose the mute as mono. this works in current OS and hopefully
|
||
|
// will work in future OS.
|
||
|
case NODE_WAVEOUT_MUTE:
|
||
|
case NODE_LINEIN_MUTE:
|
||
|
case NODE_CD_MUTE:
|
||
|
case NODE_VIDEO_MUTE:
|
||
|
case NODE_AUX_MUTE:
|
||
|
case NODE_MASTEROUT_MUTE:
|
||
|
case NODE_FRONT_MUTE:
|
||
|
case NODE_SURROUND_MUTE:
|
||
|
case NODE_HPOUT_MUTE:
|
||
|
// just check the type.
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_MUTE)
|
||
|
return ntStatus;
|
||
|
break;
|
||
|
|
||
|
case NODE_INVALID:
|
||
|
default:
|
||
|
// Ooops.
|
||
|
DOUT (DBG_ERROR, ("PropertyHandler_OnOff: Invalid node requested."));
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
// Now, do some action!
|
||
|
|
||
|
if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
|
||
|
{
|
||
|
WORD wRegister;
|
||
|
|
||
|
// Read the HW register for the node except NODE_VIRT_MASTERMONO_MUTE,
|
||
|
// since this is pure virtual.
|
||
|
if (NodeDef != NODE_VIRT_MASTERMONO_MUTE)
|
||
|
{
|
||
|
// get the register and read it.
|
||
|
ntStatus = that->AdapterCommon->ReadCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef), &wRegister);
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
// Mask out every unused bit.
|
||
|
wRegister &= that->AdapterCommon->GetNodeMask (NodeDef);
|
||
|
// Store the value.
|
||
|
*OnOff = wRegister ? TRUE : FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Assume no mute for master mono.
|
||
|
*OnOff = FALSE;
|
||
|
}
|
||
|
|
||
|
// When we have cache information then return this instead of the
|
||
|
// calculated value. If we don't, store the calculated value.
|
||
|
if (that->stNodeCache[NodeDef].bLeftValid)
|
||
|
*OnOff = that->stNodeCache[NodeDef].lLeft;
|
||
|
else
|
||
|
{
|
||
|
that->stNodeCache[NodeDef].lLeft = *OnOff;
|
||
|
that->stNodeCache[NodeDef].bLeftValid = -1;
|
||
|
}
|
||
|
|
||
|
PropertyRequest->ValueSize = sizeof(BOOL);
|
||
|
DOUT (DBG_PROPERTY, ("GET: %s = 0x%x", NodeStrings[NodeDef], *OnOff));
|
||
|
|
||
|
// Set the return code here.
|
||
|
ntStatus = STATUS_SUCCESS;
|
||
|
}
|
||
|
else // this must be a set.
|
||
|
{
|
||
|
// First update the node cache.
|
||
|
that->stNodeCache[NodeDef].bLeftValid = -1;
|
||
|
that->stNodeCache[NodeDef].lLeft = (*OnOff) ? TRUE : FALSE;
|
||
|
|
||
|
//
|
||
|
// If we have a master mono, then we have to program the speaker
|
||
|
// mutes a little different.
|
||
|
// Check for master mono (surround or headphone present) and
|
||
|
// if one of the speaker mutes is requested.
|
||
|
//
|
||
|
if ((that->AdapterCommon->GetPinConfig (PINC_SURROUND_PRESENT) ||
|
||
|
that->AdapterCommon->GetPinConfig (PINC_HPOUT_PRESENT)) &&
|
||
|
((NodeDef == NODE_VIRT_MASTERMONO_MUTE) || (NodeDef == NODE_LFE_MUTE) ||
|
||
|
(NodeDef == NODE_CENTER_MUTE) || (NodeDef == NODE_FRONT_MUTE) ||
|
||
|
(NodeDef == NODE_SURROUND_MUTE) || (NodeDef == NODE_HPOUT_MUTE)))
|
||
|
{
|
||
|
//
|
||
|
// For master mono we have to update all speakers.
|
||
|
//
|
||
|
if (NodeDef == NODE_VIRT_MASTERMONO_MUTE)
|
||
|
{
|
||
|
// Update all speaker mutes.
|
||
|
ntStatus = SetMultichannelMute (that, NODE_FRONT_MUTE);
|
||
|
if (that->AdapterCommon->GetPinConfig (PINC_HPOUT_PRESENT))
|
||
|
ntStatus = SetMultichannelMute (that, NODE_HPOUT_MUTE);
|
||
|
if (that->AdapterCommon->GetPinConfig (PINC_SURROUND_PRESENT))
|
||
|
ntStatus = SetMultichannelMute (that, NODE_SURROUND_MUTE);
|
||
|
if (that->AdapterCommon->GetPinConfig (PINC_CENTER_LFE_PRESENT))
|
||
|
{
|
||
|
ntStatus = SetMultichannelMute (that, NODE_CENTER_MUTE);
|
||
|
ntStatus = SetMultichannelMute (that, NODE_LFE_MUTE);
|
||
|
}
|
||
|
}
|
||
|
else // Update the individual speaker mute.
|
||
|
{
|
||
|
ntStatus = SetMultichannelMute (that, NodeDef);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// For all other mutes/checkboxes just write the value to the HW.
|
||
|
//
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef),
|
||
|
(*OnOff) ? -1 : 0,
|
||
|
that->AdapterCommon->GetNodeMask (NodeDef));
|
||
|
}
|
||
|
|
||
|
DOUT (DBG_PROPERTY, ("SET: %s -> 0x%x", NodeStrings[NodeDef], *OnOff));
|
||
|
|
||
|
// ntStatus was set with the write call! whatever this is, return it.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CMiniportTopologyICH::BasicSupportHandler
|
||
|
*****************************************************************************
|
||
|
* Assists in BASICSUPPORT accesses on level properties.
|
||
|
* This function is called internally every time there is a "basic support"
|
||
|
* request on a volume or tone control. The basic support is used to retrieve
|
||
|
* some information about the range of the control (from - to dB, steps) and
|
||
|
* which type of control (tone, volume).
|
||
|
* Basically, this function just calls GetDBValues to get the range information
|
||
|
* and fills the rest of the structure with some constants.
|
||
|
*/
|
||
|
NTSTATUS CMiniportTopologyICH::BasicSupportHandler
|
||
|
(
|
||
|
IN PPCPROPERTY_REQUEST PropertyRequest
|
||
|
)
|
||
|
{
|
||
|
PAGED_CODE ();
|
||
|
|
||
|
ASSERT (PropertyRequest);
|
||
|
|
||
|
DOUT (DBG_PRINT, ("[CMiniportTopologyICH::BasicSupportHandler]"));
|
||
|
|
||
|
NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
||
|
// The major target is the object pointer to the topology miniport.
|
||
|
CMiniportTopologyICH *that =
|
||
|
(CMiniportTopologyICH *) PropertyRequest->MajorTarget;
|
||
|
|
||
|
ASSERT (that);
|
||
|
|
||
|
|
||
|
// if there is enough space for a KSPROPERTY_DESCRIPTION information
|
||
|
if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION)))
|
||
|
{
|
||
|
// we return a KSPROPERTY_DESCRIPTION structure.
|
||
|
PKSPROPERTY_DESCRIPTION PropDesc = (PKSPROPERTY_DESCRIPTION)PropertyRequest->Value;
|
||
|
|
||
|
PropDesc->AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT |
|
||
|
KSPROPERTY_TYPE_GET |
|
||
|
KSPROPERTY_TYPE_SET;
|
||
|
PropDesc->DescriptionSize = sizeof(KSPROPERTY_DESCRIPTION) +
|
||
|
sizeof(KSPROPERTY_MEMBERSHEADER) +
|
||
|
sizeof(KSPROPERTY_STEPPING_LONG);
|
||
|
PropDesc->PropTypeSet.Set = KSPROPTYPESETID_General;
|
||
|
PropDesc->PropTypeSet.Id = VT_I4;
|
||
|
PropDesc->PropTypeSet.Flags = 0;
|
||
|
PropDesc->MembersListCount = 1;
|
||
|
PropDesc->Reserved = 0;
|
||
|
|
||
|
// if return buffer can also hold a range description, return it too
|
||
|
if (PropertyRequest->ValueSize >= (sizeof(KSPROPERTY_DESCRIPTION) +
|
||
|
sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG)))
|
||
|
{
|
||
|
// fill in the members header
|
||
|
PKSPROPERTY_MEMBERSHEADER Members = (PKSPROPERTY_MEMBERSHEADER)(PropDesc + 1);
|
||
|
|
||
|
Members->MembersFlags = KSPROPERTY_MEMBER_STEPPEDRANGES;
|
||
|
Members->MembersSize = sizeof(KSPROPERTY_STEPPING_LONG);
|
||
|
Members->MembersCount = 1;
|
||
|
Members->Flags = 0;
|
||
|
|
||
|
// fill in the stepped range
|
||
|
PKSPROPERTY_STEPPING_LONG Range = (PKSPROPERTY_STEPPING_LONG)(Members + 1);
|
||
|
|
||
|
ntStatus = GetDBValues (that->AdapterCommon,
|
||
|
that->TransNodeNrToNodeDef (PropertyRequest->Node),
|
||
|
&Range->Bounds.SignedMinimum,
|
||
|
&Range->Bounds.SignedMaximum,
|
||
|
&Range->SteppingDelta);
|
||
|
|
||
|
Range->Reserved = 0;
|
||
|
|
||
|
// set the return value size
|
||
|
PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION) +
|
||
|
sizeof(KSPROPERTY_MEMBERSHEADER) +
|
||
|
sizeof(KSPROPERTY_STEPPING_LONG);
|
||
|
|
||
|
DOUT (DBG_PROPERTY, ("BASIC_SUPPORT: %s max=0x%x min=0x%x step=0x%x",
|
||
|
NodeStrings[that->TransNodeNrToNodeDef (PropertyRequest->Node)],
|
||
|
Range->Bounds.SignedMaximum, Range->Bounds.SignedMinimum,
|
||
|
Range->SteppingDelta));
|
||
|
} else
|
||
|
{
|
||
|
// we hadn't enough space for the range information;
|
||
|
// set the return value size
|
||
|
PropertyRequest->ValueSize = sizeof(KSPROPERTY_DESCRIPTION);
|
||
|
}
|
||
|
|
||
|
ntStatus = STATUS_SUCCESS;
|
||
|
}
|
||
|
else if (PropertyRequest->ValueSize >= sizeof(ULONG))
|
||
|
{
|
||
|
// if return buffer can hold a ULONG, return the access flags
|
||
|
PULONG AccessFlags = (PULONG)PropertyRequest->Value;
|
||
|
|
||
|
*AccessFlags = KSPROPERTY_TYPE_BASICSUPPORT |
|
||
|
KSPROPERTY_TYPE_GET |
|
||
|
KSPROPERTY_TYPE_SET;
|
||
|
|
||
|
// set the return value size
|
||
|
PropertyRequest->ValueSize = sizeof(ULONG);
|
||
|
ntStatus = STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
// In case there was not even enough space for a ULONG in the return buffer,
|
||
|
// we fail this request with STATUS_INVALID_DEVICE_REQUEST.
|
||
|
// Any other case will return STATUS_SUCCESS.
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CMiniportTopologyICH::PropertyHandler_Level
|
||
|
*****************************************************************************
|
||
|
* Accesses a KSAUDIO_LEVEL property.
|
||
|
* This function (property handler) is called by portcls every time there is a
|
||
|
* get, set or basic support request for the node. The connection between the
|
||
|
* node type and the property handler is made in the automation table which is
|
||
|
* referenced when you register the node.
|
||
|
* We use this property handler for all volume controls (and virtual volume
|
||
|
* controls for recording).
|
||
|
*/
|
||
|
NTSTATUS CMiniportTopologyICH::PropertyHandler_Level
|
||
|
(
|
||
|
IN PPCPROPERTY_REQUEST PropertyRequest
|
||
|
)
|
||
|
{
|
||
|
PAGED_CODE ();
|
||
|
|
||
|
ASSERT (PropertyRequest);
|
||
|
|
||
|
DOUT (DBG_PRINT, ("[CMiniportTopologyICH::PropertyHandler_Level]"));
|
||
|
|
||
|
NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
|
||
|
TopoNodes NodeDef;
|
||
|
LONG channel;
|
||
|
LONG lMinimum, lMaximum;
|
||
|
ULONG uStep;
|
||
|
// The major target is the object pointer to the topology miniport.
|
||
|
CMiniportTopologyICH *that =
|
||
|
(CMiniportTopologyICH *) PropertyRequest->MajorTarget;
|
||
|
|
||
|
ASSERT (that);
|
||
|
|
||
|
// validate node
|
||
|
if (PropertyRequest->Node == (ULONG)-1)
|
||
|
return ntStatus;
|
||
|
|
||
|
// do the appropriate action for the request.
|
||
|
|
||
|
// we should do a get or a set?
|
||
|
if ((PropertyRequest->Verb & KSPROPERTY_TYPE_GET) ||
|
||
|
(PropertyRequest->Verb & KSPROPERTY_TYPE_SET))
|
||
|
{
|
||
|
// validate parameters
|
||
|
if ((PropertyRequest->InstanceSize < sizeof(LONG)) ||
|
||
|
(PropertyRequest->ValueSize < sizeof(LONG)))
|
||
|
return ntStatus;
|
||
|
|
||
|
// get channel information
|
||
|
channel = *((PLONG)PropertyRequest->Instance);
|
||
|
|
||
|
// check channel types, return when unknown
|
||
|
// as you can see, we have no multichannel support.
|
||
|
if ((channel != CHAN_LEFT) &&
|
||
|
(channel != CHAN_RIGHT) &&
|
||
|
(channel != CHAN_MASTER))
|
||
|
return ntStatus;
|
||
|
|
||
|
// get the buffer
|
||
|
PLONG Level = (PLONG)PropertyRequest->Value;
|
||
|
|
||
|
// Switch on the node id. This is just for parameter checking.
|
||
|
// If something goes wrong, we will immideately return with
|
||
|
// ntStatus, which is STATUS_INVALID_PARAMETER.
|
||
|
switch(NodeDef = that->TransNodeNrToNodeDef (PropertyRequest->Node))
|
||
|
{
|
||
|
// these are mono channels, don't respond to a right channel
|
||
|
// request.
|
||
|
case NODE_PCBEEP_VOLUME:
|
||
|
case NODE_PHONE_VOLUME:
|
||
|
case NODE_MIC_VOLUME:
|
||
|
case NODE_VIRT_MONOOUT_VOLUME1:
|
||
|
case NODE_VIRT_MONOOUT_VOLUME2:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME1:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME7:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME8:
|
||
|
case NODE_MICIN_VOLUME:
|
||
|
case NODE_VIRT_MASTERMONO_VOLUME:
|
||
|
case NODE_CENTER_VOLUME:
|
||
|
case NODE_LFE_VOLUME:
|
||
|
// check type
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_VOLUMELEVEL)
|
||
|
return ntStatus;
|
||
|
// check channel
|
||
|
if (channel == CHAN_RIGHT)
|
||
|
return ntStatus;
|
||
|
// Well, this is a fake for the routine below that should work
|
||
|
// for all nodes ... On AC97 the right channel are the LSBs and
|
||
|
// mono channels have only LSBs used. Windows however thinks that
|
||
|
// mono channels are left channels (only). So we could say here
|
||
|
// we have a right channel request (to prg. the LSBs) instead of
|
||
|
// a left channel request. But we have some controls that are HW-
|
||
|
// stereo, but exposed to the system as mono. These are the virtual
|
||
|
// volume controls in front of the wave-in muxer for the MIC, PHONE
|
||
|
// and MONO MIX signals (see to the switch:
|
||
|
// NODE_VIRT_MASTER_INPUT_VOLUME1, 7 and 8). Saying we have a MASTER
|
||
|
// request makes sure the value is prg. for left and right channel,
|
||
|
// but on HW-mono controls the right channel is prg. only, cause the
|
||
|
// mask in ac97reg.h leads to a 0-mask for left channel prg. which
|
||
|
// just does nothing ;)
|
||
|
channel = CHAN_MASTER;
|
||
|
break;
|
||
|
|
||
|
// These are stereo channels.
|
||
|
case NODE_MASTEROUT_VOLUME:
|
||
|
case NODE_FRONT_VOLUME:
|
||
|
case NODE_SURROUND_VOLUME:
|
||
|
case NODE_HPOUT_VOLUME:
|
||
|
case NODE_LINEIN_VOLUME:
|
||
|
case NODE_CD_VOLUME:
|
||
|
case NODE_VIDEO_VOLUME:
|
||
|
case NODE_AUX_VOLUME:
|
||
|
case NODE_WAVEOUT_VOLUME:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME2:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME3:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME4:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME5:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME6:
|
||
|
// check type
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_VOLUMELEVEL)
|
||
|
return ntStatus;
|
||
|
// check channel; we don't support a get with master
|
||
|
if ((channel == CHAN_MASTER) &&
|
||
|
(PropertyRequest->Verb & KSPROPERTY_TYPE_GET))
|
||
|
return ntStatus;
|
||
|
break;
|
||
|
|
||
|
case NODE_INVALID:
|
||
|
default:
|
||
|
// Ooops
|
||
|
DOUT (DBG_ERROR, ("PropertyHandler_Level: Invalid node requested."));
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
// Now, do some action!
|
||
|
|
||
|
// get the registered dB values.
|
||
|
ntStatus = GetDBValues (that->AdapterCommon, NodeDef, &lMinimum,
|
||
|
&lMaximum, &uStep);
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
|
||
|
// do a get
|
||
|
if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
|
||
|
{
|
||
|
WORD wRegister;
|
||
|
|
||
|
// Read the HW register for the node except NODE_VIRT_MASTERMONO_VOLUME
|
||
|
// since this is pure virtual.
|
||
|
if (NodeDef != NODE_VIRT_MASTERMONO_VOLUME)
|
||
|
{
|
||
|
// Get the register and read it.
|
||
|
ntStatus = that->AdapterCommon->ReadCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef), &wRegister);
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
|
||
|
// mask out every unused bit and rotate.
|
||
|
if (channel == CHAN_LEFT)
|
||
|
{
|
||
|
wRegister = (wRegister & (that->AdapterCommon->GetNodeMask (NodeDef)
|
||
|
& AC97REG_MASK_LEFT)) >> 8;
|
||
|
}
|
||
|
else // here goes mono or stereo-right
|
||
|
{
|
||
|
wRegister &= (that->AdapterCommon->GetNodeMask (NodeDef) &
|
||
|
AC97REG_MASK_RIGHT);
|
||
|
}
|
||
|
|
||
|
// Oops - NODE_PCBEEP_VOLUME doesn't use bit0. We have to adjust.
|
||
|
if (NodeDef == NODE_PCBEEP_VOLUME)
|
||
|
wRegister >>= 1;
|
||
|
|
||
|
// we have to translate the reg to dB.dB value.
|
||
|
|
||
|
switch (NodeDef)
|
||
|
{
|
||
|
// for record, we calculate it reverse.
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME1:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME2:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME3:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME4:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME5:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME6:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME7:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME8:
|
||
|
case NODE_MICIN_VOLUME:
|
||
|
*Level = lMinimum + uStep * wRegister;
|
||
|
break;
|
||
|
default:
|
||
|
*Level = lMaximum - uStep * wRegister;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// For the virtual controls, which are in front of a muxer, there
|
||
|
// is no mute control displayed. But we have a HW mute control, so
|
||
|
// what we do is enabling this mute when the user moves the slider
|
||
|
// down to the bottom and disabling it on every other position.
|
||
|
// We will return a PROP_MOST_NEGATIVE value in case the slider
|
||
|
// is moved to the bottom.
|
||
|
// We do this only for the "mono muxer" since the volume there ranges
|
||
|
// from 0 to -46.5dB. The record volumes only have a range from
|
||
|
// 0 to +22.5dB and we cannot mute them when the slider is down.
|
||
|
if ((NodeDef == NODE_VIRT_MONOOUT_VOLUME1) ||
|
||
|
(NodeDef == NODE_VIRT_MONOOUT_VOLUME2))
|
||
|
{
|
||
|
// read the register again.
|
||
|
ntStatus = that->AdapterCommon->ReadCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef), &wRegister);
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
// return most negative value in case it is checked.
|
||
|
if (wRegister & AC97REG_MASK_MUTE)
|
||
|
*Level = PROP_MOST_NEGATIVE;
|
||
|
}
|
||
|
}
|
||
|
else // This is master mono volume.
|
||
|
{
|
||
|
// Assume 0dB for master mono volume.
|
||
|
*Level = 0;
|
||
|
}
|
||
|
|
||
|
// when we have cache information then return this instead
|
||
|
// of the calculated value. if we don't, store the calculated
|
||
|
// value.
|
||
|
// We do that twice for master because in case we didn't set
|
||
|
// the NodeCache yet it will be set then.
|
||
|
if ((channel == CHAN_LEFT) || (channel == CHAN_MASTER))
|
||
|
{
|
||
|
if (that->stNodeCache[NodeDef].bLeftValid)
|
||
|
*Level = that->stNodeCache[NodeDef].lLeft;
|
||
|
else
|
||
|
{
|
||
|
that->stNodeCache[NodeDef].lLeft = *Level;
|
||
|
that->stNodeCache[NodeDef].bLeftValid = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((channel == CHAN_RIGHT) || (channel == CHAN_MASTER))
|
||
|
{
|
||
|
if (that->stNodeCache[NodeDef].bRightValid)
|
||
|
*Level = that->stNodeCache[NodeDef].lRight;
|
||
|
else
|
||
|
{
|
||
|
that->stNodeCache[NodeDef].lRight = *Level;
|
||
|
that->stNodeCache[NodeDef].bRightValid = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// thats all, good bye.
|
||
|
PropertyRequest->ValueSize = sizeof(LONG);
|
||
|
DOUT (DBG_PROPERTY, ("GET: %s(%s) = 0x%x",NodeStrings[NodeDef],
|
||
|
channel==CHAN_LEFT ? "L" : "R", *Level));
|
||
|
|
||
|
// ntStatus was set with the read call! whatever this is, return it.
|
||
|
}
|
||
|
else // this must be a set
|
||
|
{
|
||
|
WORD wRegister;
|
||
|
LONG lLevel = *Level;
|
||
|
|
||
|
//
|
||
|
// Check borders.
|
||
|
//
|
||
|
// These 2 lines will have a special effect on sndvol32.
|
||
|
// Whenever you move the balance slider on a volume, one channel
|
||
|
// keeps the same and the other volume channel gets descreased.
|
||
|
// With ac97 on recording controls, the default slider position
|
||
|
// is at 0dB and the range of the volume is 0dB till +22.5dB.
|
||
|
// That means that panning (moving the balance slider) is simply
|
||
|
// impossible. If you would store the volume like sndvol gives it
|
||
|
// to you and you return it on a get, then the balance slider
|
||
|
// moves and stays at the position the user wanted it. However,
|
||
|
// if you return the actual volume the balance slider will jump
|
||
|
// back to the position that the HW can do (play with it to see
|
||
|
// how it works).
|
||
|
//
|
||
|
if (lLevel > lMaximum) lLevel = lMaximum;
|
||
|
if (lLevel < lMinimum) lLevel = lMinimum;
|
||
|
|
||
|
// First update the node cache.
|
||
|
if ((channel == CHAN_LEFT) || (channel == CHAN_MASTER))
|
||
|
{
|
||
|
that->stNodeCache[NodeDef].bLeftValid = -1;
|
||
|
that->stNodeCache[NodeDef].lLeft = lLevel;
|
||
|
}
|
||
|
if ((channel == CHAN_RIGHT) || (channel == CHAN_MASTER))
|
||
|
{
|
||
|
that->stNodeCache[NodeDef].bRightValid = -1;
|
||
|
that->stNodeCache[NodeDef].lRight = lLevel;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we have a master mono, then we have to program the speaker
|
||
|
// volumes a little different.
|
||
|
// Check for master mono (surround or headphone present) and
|
||
|
// if one of the speaker volumes is requested.
|
||
|
//
|
||
|
if ((that->AdapterCommon->GetPinConfig (PINC_SURROUND_PRESENT) ||
|
||
|
that->AdapterCommon->GetPinConfig (PINC_HPOUT_PRESENT)) &&
|
||
|
((NodeDef == NODE_VIRT_MASTERMONO_VOLUME) || (NodeDef == NODE_LFE_VOLUME) ||
|
||
|
(NodeDef == NODE_CENTER_VOLUME) || (NodeDef == NODE_FRONT_VOLUME) ||
|
||
|
(NodeDef == NODE_SURROUND_VOLUME) || (NodeDef == NODE_HPOUT_VOLUME)))
|
||
|
{
|
||
|
//
|
||
|
// For master mono we have to update all speaker volumes.
|
||
|
//
|
||
|
if (NodeDef == NODE_VIRT_MASTERMONO_VOLUME)
|
||
|
{
|
||
|
// Update all speaker volumes.
|
||
|
ntStatus = SetMultichannelVolume (that, NODE_FRONT_VOLUME);
|
||
|
if (that->AdapterCommon->GetPinConfig (PINC_HPOUT_PRESENT))
|
||
|
ntStatus = SetMultichannelVolume (that, NODE_HPOUT_VOLUME);
|
||
|
if (that->AdapterCommon->GetPinConfig (PINC_SURROUND_PRESENT))
|
||
|
ntStatus = SetMultichannelVolume (that, NODE_SURROUND_VOLUME);
|
||
|
if (that->AdapterCommon->GetPinConfig (PINC_CENTER_LFE_PRESENT))
|
||
|
{
|
||
|
ntStatus = SetMultichannelVolume (that, NODE_CENTER_VOLUME);
|
||
|
ntStatus = SetMultichannelVolume (that, NODE_LFE_VOLUME);
|
||
|
}
|
||
|
}
|
||
|
else // update the individual speaker volume only.
|
||
|
{
|
||
|
ntStatus = SetMultichannelVolume (that, NodeDef);
|
||
|
}
|
||
|
}
|
||
|
else // This is for all other volumes (or no master mono present).
|
||
|
{
|
||
|
// calculate the dB.dB value.
|
||
|
|
||
|
// The nodes are calculated differently.
|
||
|
switch (NodeDef)
|
||
|
{
|
||
|
// for record controls we calculate it 'reverse'.
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME1:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME2:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME3:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME4:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME5:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME6:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME7:
|
||
|
case NODE_VIRT_MASTER_INPUT_VOLUME8:
|
||
|
// read the wavein selector.
|
||
|
ntStatus = that->AdapterCommon->ReadCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NODE_WAVEIN_SELECT),
|
||
|
&wRegister);
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
|
||
|
// mask out every unused bit.
|
||
|
wRegister &= (that->AdapterCommon->GetNodeMask (
|
||
|
NODE_WAVEIN_SELECT) & AC97REG_MASK_RIGHT);
|
||
|
|
||
|
// check if the volume that we change belongs to the active
|
||
|
// (selected) virtual channel.
|
||
|
// Tricky: If the virtual nodes are not defined consecutively
|
||
|
// this comparision will fail.
|
||
|
if ((NodeDef - NODE_VIRT_MASTER_INPUT_VOLUME1) != wRegister)
|
||
|
return ntStatus;
|
||
|
|
||
|
// fall through for calculation.
|
||
|
|
||
|
case NODE_MICIN_VOLUME:
|
||
|
wRegister = (WORD)(((lLevel + uStep / 2) - lMinimum) / uStep);
|
||
|
break;
|
||
|
|
||
|
case NODE_VIRT_MONOOUT_VOLUME1:
|
||
|
case NODE_VIRT_MONOOUT_VOLUME2:
|
||
|
// read the monoout selector.
|
||
|
ntStatus = that->AdapterCommon->ReadCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NODE_MONOOUT_SELECT),
|
||
|
&wRegister);
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
|
||
|
// mask out every unused bit.
|
||
|
wRegister &= that->AdapterCommon->GetNodeMask (NODE_MONOOUT_SELECT);
|
||
|
|
||
|
// check if the volume that we change belongs to the active
|
||
|
// (selected) virtual channel.
|
||
|
// Note: Monout select is set if we want to prg. MIC (Volume2).
|
||
|
if ((!wRegister && (NodeDef == NODE_VIRT_MONOOUT_VOLUME2)) ||
|
||
|
(wRegister && (NodeDef == NODE_VIRT_MONOOUT_VOLUME1)))
|
||
|
return ntStatus;
|
||
|
|
||
|
// fall through for calculation.
|
||
|
default:
|
||
|
wRegister = (WORD)(((lMaximum + uStep / 2) - lLevel) / uStep);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Oops - NODE_PCBEEP_VOLUME doesn't use bit0. We have to adjust.
|
||
|
if (NodeDef == NODE_PCBEEP_VOLUME)
|
||
|
wRegister <<= 1;
|
||
|
|
||
|
// write the stuff (with mask!).
|
||
|
// Note: mono channels are 'master' here (see fake above).
|
||
|
// this makes sure that left and right channel is prg. for the virt.
|
||
|
// controls. On controls that only have the right channel, the left
|
||
|
// channel programming does nothing cause the mask will be zero.
|
||
|
if ((channel == CHAN_LEFT) || (channel == CHAN_MASTER))
|
||
|
{
|
||
|
// write only left.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef),
|
||
|
wRegister << 8,
|
||
|
that->AdapterCommon->GetNodeMask (NodeDef) & AC97REG_MASK_LEFT);
|
||
|
// immediately return on error
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
if ((channel == CHAN_RIGHT) || (channel == CHAN_MASTER))
|
||
|
{
|
||
|
// write only right.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef),
|
||
|
wRegister,
|
||
|
that->AdapterCommon->GetNodeMask (NodeDef) & AC97REG_MASK_RIGHT);
|
||
|
// immediately return on error
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
// For the virtual controls, which are in front of a muxer, there
|
||
|
// is no mute control displayed. But we have a HW mute control, so
|
||
|
// what we do is enabling this mute when the user moves the slider
|
||
|
// down to the bottom and disabling it on every other position.
|
||
|
// We do this only for the "mono muxer", the recording mutes will
|
||
|
// never be muted.
|
||
|
// Tricky: Master input virtual controls must be defined consecutively.
|
||
|
if ((NodeDef >= NODE_VIRT_MASTER_INPUT_VOLUME1) &&
|
||
|
(NodeDef <= NODE_VIRT_MASTER_INPUT_VOLUME8))
|
||
|
{
|
||
|
// disable the mute; this only works because the mute and volume
|
||
|
// share the same register.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef),
|
||
|
0, AC97REG_MASK_MUTE);
|
||
|
|
||
|
// Just in case.
|
||
|
that->UpdateRecordMute ();
|
||
|
}
|
||
|
|
||
|
if ((NodeDef == NODE_VIRT_MONOOUT_VOLUME1) ||
|
||
|
(NodeDef == NODE_VIRT_MONOOUT_VOLUME2))
|
||
|
{
|
||
|
// these are only mono controls so checking one entry is enough.
|
||
|
if ( that->stNodeCache[NodeDef].bLeftValid &&
|
||
|
(that->stNodeCache[NodeDef].lLeft <= lMinimum))
|
||
|
{
|
||
|
// set the mute; this only works because the mute and volume
|
||
|
// share the same register.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef),
|
||
|
AC97REG_MASK_MUTE, AC97REG_MASK_MUTE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// clear the mute; this only works because the mute and volume
|
||
|
// share the same register.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef),
|
||
|
0, AC97REG_MASK_MUTE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DOUT (DBG_PROPERTY, ("SET: %s(%s) -> 0x%x", NodeStrings[NodeDef],
|
||
|
channel==CHAN_LEFT ? "L" : channel==CHAN_RIGHT ? "R" : "M",
|
||
|
*Level));
|
||
|
|
||
|
// ntStatus was set with the read call! whatever this is, return it.
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
|
||
|
{
|
||
|
ntStatus = BasicSupportHandler (PropertyRequest);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CMiniportTopologyICH::PropertyHandler_Tone
|
||
|
*****************************************************************************
|
||
|
* Accesses a KSAUDIO_TONE property.
|
||
|
* This function (property handler) is called by portcls every time there is a
|
||
|
* get, set or basic support request for the node. The connection between the
|
||
|
* node type and the property handler is made in the automation table which is
|
||
|
* referenced when you register the node.
|
||
|
* We use this property handler for all tone controls displayed at the advanced
|
||
|
* property dialog in sndvol32 and the 3D controls displayed and exposed as
|
||
|
* normal volume controls.
|
||
|
*/
|
||
|
NTSTATUS CMiniportTopologyICH::PropertyHandler_Tone
|
||
|
(
|
||
|
IN PPCPROPERTY_REQUEST PropertyRequest
|
||
|
)
|
||
|
{
|
||
|
PAGED_CODE ();
|
||
|
|
||
|
ASSERT (PropertyRequest);
|
||
|
|
||
|
DOUT (DBG_PRINT, ("[CMiniportTopologyICH::PropertyHandler_Tone]"));
|
||
|
|
||
|
NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
|
||
|
TopoNodes NodeDef;
|
||
|
LONG lMinimum, lMaximum;
|
||
|
ULONG uStep;
|
||
|
// The major target is the object pointer to the topology miniport.
|
||
|
CMiniportTopologyICH *that =
|
||
|
(CMiniportTopologyICH *) PropertyRequest->MajorTarget;
|
||
|
|
||
|
ASSERT (that);
|
||
|
|
||
|
// validate node
|
||
|
if (PropertyRequest->Node == (ULONG)-1)
|
||
|
return ntStatus;
|
||
|
|
||
|
// do the appropriate action for the request.
|
||
|
|
||
|
// we should do a get or a set?
|
||
|
if ((PropertyRequest->Verb & KSPROPERTY_TYPE_GET) ||
|
||
|
(PropertyRequest->Verb & KSPROPERTY_TYPE_SET))
|
||
|
{
|
||
|
// validate parameters
|
||
|
if ((PropertyRequest->InstanceSize < sizeof(LONG)) ||
|
||
|
(PropertyRequest->ValueSize < sizeof(LONG)))
|
||
|
return ntStatus;
|
||
|
|
||
|
// get the buffer
|
||
|
PLONG Level = (PLONG)PropertyRequest->Value;
|
||
|
|
||
|
// Switch on the node id. This is just for parameter checking.
|
||
|
// If something goes wrong, we will immideately return with
|
||
|
// ntStatus, which is STATUS_INVALID_PARAMETER.
|
||
|
switch(NodeDef = that->TransNodeNrToNodeDef (PropertyRequest->Node))
|
||
|
{
|
||
|
case NODE_BASS:
|
||
|
// check type.
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_BASS)
|
||
|
return ntStatus;
|
||
|
break;
|
||
|
|
||
|
case NODE_TREBLE:
|
||
|
// check type.
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_TREBLE)
|
||
|
return ntStatus;
|
||
|
break;
|
||
|
|
||
|
case NODE_VIRT_3D_CENTER:
|
||
|
case NODE_VIRT_3D_DEPTH:
|
||
|
// check 3D control
|
||
|
if (!that->AdapterCommon->GetNodeConfig (NODEC_3D_CENTER_ADJUSTABLE)
|
||
|
&& (NodeDef == NODE_VIRT_3D_CENTER))
|
||
|
return ntStatus;
|
||
|
if (!that->AdapterCommon->GetNodeConfig (NODEC_3D_DEPTH_ADJUSTABLE)
|
||
|
&& (NodeDef == NODE_VIRT_3D_DEPTH))
|
||
|
return ntStatus;
|
||
|
// check type
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_VOLUMELEVEL)
|
||
|
return ntStatus;
|
||
|
// check channel
|
||
|
if (*(PLONG(PropertyRequest->Instance)) == CHAN_RIGHT)
|
||
|
return ntStatus;
|
||
|
break;
|
||
|
|
||
|
case NODE_INVALID:
|
||
|
default:
|
||
|
// Ooops
|
||
|
DOUT (DBG_ERROR, ("PropertyHandler_Tone: Invalid node requested."));
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
// Now, do some action!
|
||
|
|
||
|
// get the registered DB values
|
||
|
ntStatus = GetDBValues (that->AdapterCommon, NodeDef, &lMinimum,
|
||
|
&lMaximum, &uStep);
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
|
||
|
// do a get
|
||
|
if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
|
||
|
{
|
||
|
WORD wRegister;
|
||
|
|
||
|
// first get the stuff.
|
||
|
ntStatus = that->AdapterCommon->ReadCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef), &wRegister);
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
|
||
|
// mask out every unused bit.
|
||
|
wRegister &= that->AdapterCommon->GetNodeMask (NodeDef);
|
||
|
|
||
|
// rotate if bass tone control or 3D center control
|
||
|
if ((NodeDef == NODE_BASS) || (NodeDef == NODE_VIRT_3D_CENTER))
|
||
|
wRegister >>= 8;
|
||
|
|
||
|
// convert from reg to dB.dB value.
|
||
|
if ((NodeDef == NODE_VIRT_3D_CENTER) ||
|
||
|
(NodeDef == NODE_VIRT_3D_DEPTH))
|
||
|
{
|
||
|
// That's for the 3D controls
|
||
|
*Level = lMinimum + uStep * wRegister;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (wRegister == 0x000F)
|
||
|
*Level = 0; // bypass
|
||
|
else
|
||
|
// And that's for the tone controls
|
||
|
*Level = lMaximum - uStep * wRegister;
|
||
|
}
|
||
|
|
||
|
// when we have cache information then return this instead
|
||
|
// of the calculated value. if we don't, store the calculated
|
||
|
// value.
|
||
|
if (that->stNodeCache[NodeDef].bLeftValid)
|
||
|
*Level = that->stNodeCache[NodeDef].lLeft;
|
||
|
else
|
||
|
{
|
||
|
that->stNodeCache[NodeDef].lLeft = *Level;
|
||
|
that->stNodeCache[NodeDef].bLeftValid = -1;
|
||
|
}
|
||
|
|
||
|
// we return a LONG
|
||
|
PropertyRequest->ValueSize = sizeof(LONG);
|
||
|
DOUT (DBG_PROPERTY, ("GET: %s = 0x%x", NodeStrings[NodeDef], *Level));
|
||
|
// ntStatus was set with the read call! whatever this is, return it.
|
||
|
}
|
||
|
else // that must be a set
|
||
|
{
|
||
|
WORD wRegister;
|
||
|
LONG lLevel = *Level;
|
||
|
|
||
|
// calculate the dB.dB value.
|
||
|
// check borders.
|
||
|
if (lLevel > lMaximum) lLevel = lMaximum;
|
||
|
if (lLevel < lMinimum) lLevel = lMinimum;
|
||
|
|
||
|
// write the value to the node cache.
|
||
|
that->stNodeCache[NodeDef].lLeft = *Level;
|
||
|
that->stNodeCache[NodeDef].bLeftValid = -1;
|
||
|
|
||
|
// convert from dB.dB value to reg.
|
||
|
if ((NodeDef == NODE_VIRT_3D_CENTER) ||
|
||
|
(NodeDef == NODE_VIRT_3D_DEPTH))
|
||
|
{
|
||
|
// For 3D controls
|
||
|
wRegister = (WORD)(((lLevel + uStep / 2) - lMinimum) / uStep);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// For tone controls
|
||
|
wRegister = (WORD)(((lMaximum + uStep / 2) - lLevel) / uStep);
|
||
|
// We don't prg. 0dB Bass or 0dB Treble, instead we smartly prg.
|
||
|
// a bypass which is reg. value 0x0F.
|
||
|
if (wRegister == 7) // 0 dB
|
||
|
wRegister = 0x000F; // bypass
|
||
|
}
|
||
|
|
||
|
// rotate if bass tone control or 3D center control
|
||
|
if ((NodeDef == NODE_BASS) || (NodeDef == NODE_VIRT_3D_CENTER))
|
||
|
wRegister <<= 8;
|
||
|
|
||
|
// write the stuff.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef),
|
||
|
wRegister,
|
||
|
that->AdapterCommon->GetNodeMask (NodeDef));
|
||
|
|
||
|
DOUT (DBG_PROPERTY,("SET: %s -> 0x%x", NodeStrings[NodeDef], *Level));
|
||
|
// ntStatus was set with the write call! whatever this is, return in.
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (PropertyRequest->Verb & KSPROPERTY_TYPE_BASICSUPPORT)
|
||
|
{
|
||
|
ntStatus = BasicSupportHandler (PropertyRequest);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CMiniportTopologyICH::PropertyHandler_Ulong
|
||
|
*****************************************************************************
|
||
|
* Accesses a ULONG value property. For MUX and DEMUX.
|
||
|
* This function (property handler) is called by portcls every time there is a
|
||
|
* get, set or basic support request for the node. The connection between the
|
||
|
* node type and the property handler is made in the automation table which is
|
||
|
* referenced when you register the node.
|
||
|
* We use this property handler for all muxer controls.
|
||
|
*/
|
||
|
NTSTATUS CMiniportTopologyICH::PropertyHandler_Ulong
|
||
|
(
|
||
|
IN PPCPROPERTY_REQUEST PropertyRequest
|
||
|
)
|
||
|
{
|
||
|
PAGED_CODE ();
|
||
|
|
||
|
ASSERT (PropertyRequest);
|
||
|
|
||
|
DOUT (DBG_PRINT, ("[CMiniportTopologyICH::PropertyHandler_Ulong]"));
|
||
|
|
||
|
NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
|
||
|
TopoNodes NodeDef;
|
||
|
LONG lMinimum, lMaximum;
|
||
|
ULONG uStep;
|
||
|
// The major target is the object pointer to the topology miniport.
|
||
|
CMiniportTopologyICH *that =
|
||
|
(CMiniportTopologyICH *) PropertyRequest->MajorTarget;
|
||
|
|
||
|
ASSERT (that);
|
||
|
|
||
|
|
||
|
// validate node instance
|
||
|
if (PropertyRequest->Node == (ULONG)-1)
|
||
|
return ntStatus;
|
||
|
|
||
|
// if we should do a get or set.
|
||
|
if ((PropertyRequest->Verb & KSPROPERTY_TYPE_GET) ||
|
||
|
(PropertyRequest->Verb & KSPROPERTY_TYPE_SET))
|
||
|
{
|
||
|
// validate buffer size.
|
||
|
if (PropertyRequest->ValueSize < sizeof(ULONG))
|
||
|
return ntStatus;
|
||
|
|
||
|
// get the pointer to the buffer.
|
||
|
PULONG PropValue = (PULONG)PropertyRequest->Value;
|
||
|
|
||
|
// Switch on the node id. This is just for parameter checking.
|
||
|
// If something goes wrong, we will immideately return with
|
||
|
// ntStatus, which is STATUS_INVALID_PARAMETER.
|
||
|
switch(NodeDef = that->TransNodeNrToNodeDef (PropertyRequest->Node))
|
||
|
{
|
||
|
case NODE_MONOOUT_SELECT:
|
||
|
case NODE_WAVEIN_SELECT:
|
||
|
// check the type
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AUDIO_MUX_SOURCE)
|
||
|
return ntStatus;
|
||
|
break;
|
||
|
|
||
|
case NODE_INVALID:
|
||
|
default:
|
||
|
// Ooops
|
||
|
DOUT (DBG_ERROR, ("PropertyHandler_Tone: Invalid node requested."));
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
// Now do some action!
|
||
|
|
||
|
// should we return the value?
|
||
|
if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
|
||
|
{
|
||
|
WORD wRegister;
|
||
|
|
||
|
// first get the stuff.
|
||
|
ntStatus = that->AdapterCommon->ReadCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef), &wRegister);
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
|
||
|
// mask out every unused bit.
|
||
|
wRegister &= that->AdapterCommon->GetNodeMask (NodeDef);
|
||
|
|
||
|
// calculate the selected pin
|
||
|
if (NodeDef == NODE_MONOOUT_SELECT)
|
||
|
{
|
||
|
// for mono out we have just one bit
|
||
|
if (wRegister)
|
||
|
*PropValue = 2;
|
||
|
else
|
||
|
*PropValue = 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// the wave in muxer is a stereo muxer, so just return the
|
||
|
// right channel (gives values 0-7) and adjust it by adding 1.
|
||
|
*PropValue = (wRegister & AC97REG_MASK_RIGHT) + 1;
|
||
|
}
|
||
|
|
||
|
// we return a LONG
|
||
|
PropertyRequest->ValueSize = sizeof(LONG);
|
||
|
DOUT (DBG_PROPERTY, ("GET: %s = 0x%x", NodeStrings[NodeDef],
|
||
|
*PropValue));
|
||
|
// ntStatus was set with the read call! whatever this is, return it.
|
||
|
}
|
||
|
else // that must be a set
|
||
|
{
|
||
|
TopoNodes VirtNode;
|
||
|
WORD wRegister;
|
||
|
ULONG ulSelect = *PropValue;
|
||
|
LONG lLevel;
|
||
|
|
||
|
// Check the selection first.
|
||
|
if (NodeDef == NODE_MONOOUT_SELECT)
|
||
|
{
|
||
|
if ((ulSelect < 1) || (ulSelect > 2))
|
||
|
return ntStatus; // STATUS_INVALID_PARAMETER
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ((ulSelect < 1) || (ulSelect > 8))
|
||
|
return ntStatus; // STATUS_INVALID_PARAMETER
|
||
|
}
|
||
|
|
||
|
// calculate the register value for programming.
|
||
|
if (NodeDef == NODE_MONOOUT_SELECT)
|
||
|
{
|
||
|
// for mono out we have just one bit
|
||
|
if (ulSelect == 2)
|
||
|
// the mask will make sure we only prg. one bit.
|
||
|
wRegister = -1;
|
||
|
else
|
||
|
// ulSelect == 1
|
||
|
wRegister = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// *257 is the same as: (ulSelect << 8) + ulSelect
|
||
|
wRegister = (WORD)(ulSelect - 1) * 257;
|
||
|
}
|
||
|
|
||
|
// write the stuff.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (NodeDef),
|
||
|
wRegister,
|
||
|
that->AdapterCommon->GetNodeMask (NodeDef));
|
||
|
|
||
|
// Store the virt. node for later use.
|
||
|
// Tricky: Master input virtual controls must be defined consecutively.
|
||
|
if (NodeDef == NODE_MONOOUT_SELECT)
|
||
|
VirtNode = (TopoNodes)(NODE_VIRT_MONOOUT_VOLUME1 + (ulSelect - 1));
|
||
|
else
|
||
|
VirtNode = (TopoNodes)(NODE_VIRT_MASTER_INPUT_VOLUME1 + (ulSelect - 1));
|
||
|
|
||
|
// Virtual controls make our life more complicated. When the user
|
||
|
// changes the input source say from CD to LiniIn, then the system just
|
||
|
// sends a message to the input muxer that the selection changed.
|
||
|
// Cause we have only one HW register for the input muxer, all volumes
|
||
|
// displayed for the user are "virtualized", means they are not there,
|
||
|
// and when the selection changes, we have to prg. the volume of the
|
||
|
// selected input to the HW register. That's what we do now.
|
||
|
|
||
|
// get the registered DB values
|
||
|
ntStatus = GetDBValues (that->AdapterCommon, VirtNode,
|
||
|
&lMinimum, &lMaximum, &uStep);
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
|
||
|
// We can be lazy here and don't check for mono controls. Reason
|
||
|
// is that the level handler writes the volume value for mono
|
||
|
// controls into both the left and right node cache ;))
|
||
|
|
||
|
if (that->stNodeCache[VirtNode].bLeftValid &&
|
||
|
that->stNodeCache[VirtNode].bRightValid)
|
||
|
{
|
||
|
// prg. left channel
|
||
|
lLevel = that->stNodeCache[VirtNode].lLeft;
|
||
|
|
||
|
// calculate the dB.dB value.
|
||
|
if (NodeDef == NODE_MONOOUT_SELECT)
|
||
|
wRegister = (WORD)(((lMaximum + uStep / 2) - lLevel) / uStep);
|
||
|
else
|
||
|
wRegister = (WORD)(((lLevel + uStep / 2) - lMinimum) / uStep);
|
||
|
|
||
|
// write left channel.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (VirtNode),
|
||
|
wRegister << 8,
|
||
|
that->AdapterCommon->GetNodeMask (VirtNode) & AC97REG_MASK_LEFT);
|
||
|
|
||
|
// prg. right channel
|
||
|
lLevel = that->stNodeCache[VirtNode].lRight;
|
||
|
|
||
|
// calculate the dB.dB value.
|
||
|
if (NodeDef == NODE_MONOOUT_SELECT)
|
||
|
wRegister = (WORD)(((lMaximum + uStep / 2) - lLevel) / uStep);
|
||
|
else
|
||
|
wRegister = (WORD)(((lLevel + uStep / 2) - lMinimum) / uStep);
|
||
|
|
||
|
// write right channel.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (VirtNode),
|
||
|
wRegister,
|
||
|
that->AdapterCommon->GetNodeMask (VirtNode) & AC97REG_MASK_RIGHT);
|
||
|
|
||
|
// For the virtual controls, which are in front of a muxer, there
|
||
|
// is no mute control displayed. But we have a HW mute control, so
|
||
|
// what we do is enabling this mute when the user moves the slider
|
||
|
// down to the bottom and disabling it on every other position.
|
||
|
// We do this only for the "mono muxer", the recording mutes will
|
||
|
// never be muted.
|
||
|
if (NodeDef == NODE_WAVEIN_SELECT)
|
||
|
{
|
||
|
// disable the mute; this only works because the mute and volume
|
||
|
// share the same register.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (VirtNode),
|
||
|
0, AC97REG_MASK_MUTE);
|
||
|
|
||
|
that->UpdateRecordMute ();
|
||
|
}
|
||
|
|
||
|
if (NodeDef == NODE_MONOOUT_SELECT)
|
||
|
{
|
||
|
// these are only mono controls so checking one entry is enough.
|
||
|
if ( that->stNodeCache[VirtNode].bLeftValid &&
|
||
|
(that->stNodeCache[VirtNode].lLeft <= lMinimum))
|
||
|
{
|
||
|
// set the mute; this only works because the mute and volume
|
||
|
// share the same register.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (VirtNode),
|
||
|
AC97REG_MASK_MUTE, AC97REG_MASK_MUTE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// clear the mute; this only works because the mute and volume
|
||
|
// share the same register.
|
||
|
ntStatus = that->AdapterCommon->WriteCodecRegister (
|
||
|
that->AdapterCommon->GetNodeReg (VirtNode),
|
||
|
0, AC97REG_MASK_MUTE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
DOUT (DBG_PROPERTY, ("SET: %s -> 0x%x", NodeStrings[NodeDef],
|
||
|
*PropValue));
|
||
|
// ntStatus was set with the write call! whatever this is, return it.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
/*****************************************************************************
|
||
|
* CMiniportTopologyICH::PropertyHandler_CpuResources
|
||
|
*****************************************************************************
|
||
|
* Propcesses a KSPROPERTY_AUDIO_CPU_RESOURCES request
|
||
|
* This property handler is called by the system for every node and every node
|
||
|
* must support this property. Basically, this property is for performance
|
||
|
* monitoring and we just say here that every function we claim to have has HW
|
||
|
* support (which by the way is true).
|
||
|
*/
|
||
|
NTSTATUS CMiniportTopologyICH::PropertyHandler_CpuResources
|
||
|
(
|
||
|
IN PPCPROPERTY_REQUEST PropertyRequest
|
||
|
)
|
||
|
{
|
||
|
PAGED_CODE ();
|
||
|
|
||
|
ASSERT (PropertyRequest);
|
||
|
|
||
|
DOUT (DBG_PRINT, ("[CMiniportTopologyICH::PropertyHandler_CpuResources]"));
|
||
|
|
||
|
CMiniportTopologyICH *that =
|
||
|
(CMiniportTopologyICH *) PropertyRequest->MajorTarget;
|
||
|
NTSTATUS ntStatus = STATUS_INVALID_DEVICE_REQUEST;
|
||
|
|
||
|
ASSERT (that);
|
||
|
|
||
|
// validate node
|
||
|
if (PropertyRequest->Node == (ULONG)-1)
|
||
|
return ntStatus;
|
||
|
|
||
|
// validate the node def.
|
||
|
if (that->TransNodeNrToNodeDef (PropertyRequest->Node) == NODE_INVALID)
|
||
|
return ntStatus;
|
||
|
|
||
|
// we should do a get
|
||
|
if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
|
||
|
{
|
||
|
// just return the flag.
|
||
|
if (PropertyRequest->ValueSize >= sizeof(LONG))
|
||
|
{
|
||
|
*((PLONG)PropertyRequest->Value) = KSAUDIO_CPU_RESOURCES_NOT_HOST_CPU;
|
||
|
PropertyRequest->ValueSize = sizeof(LONG);
|
||
|
ntStatus = STATUS_SUCCESS;
|
||
|
}
|
||
|
else // not enough buffer.
|
||
|
{
|
||
|
ntStatus = STATUS_BUFFER_TOO_SMALL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ntStatus;
|
||
|
}
|
||
|
|
||
|
#ifdef INCLUDE_PRIVATE_PROPERTY
|
||
|
/*****************************************************************************
|
||
|
* CMiniportTopologyICH::PropertyHandler_Private
|
||
|
*****************************************************************************
|
||
|
* This is a private property that returns some AC97 codec features.
|
||
|
* This routine gets called whenever the topology filter gets a property
|
||
|
* request with KSPROSETPID_Private and KSPROPERTY_AC97_FEATURES set. It is not
|
||
|
* a node property but a filter property (you don't have to specify a node).
|
||
|
*/
|
||
|
NTSTATUS CMiniportTopologyICH::PropertyHandler_Private
|
||
|
(
|
||
|
IN PPCPROPERTY_REQUEST PropertyRequest
|
||
|
)
|
||
|
{
|
||
|
PAGED_CODE ();
|
||
|
|
||
|
ASSERT (PropertyRequest);
|
||
|
|
||
|
DOUT (DBG_PRINT, ("[CMiniportTopologyICH::PropertyHandler_Private]"));
|
||
|
|
||
|
NTSTATUS ntStatus = STATUS_INVALID_PARAMETER;
|
||
|
// The major target is the object pointer to the topology miniport.
|
||
|
CMiniportTopologyICH *that =
|
||
|
(CMiniportTopologyICH *) PropertyRequest->MajorTarget;
|
||
|
|
||
|
|
||
|
ASSERT (that);
|
||
|
|
||
|
|
||
|
// We only have a get defined.
|
||
|
if (PropertyRequest->Verb & KSPROPERTY_TYPE_GET)
|
||
|
{
|
||
|
// Check the ID ("function" in "group").
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AC97_FEATURES)
|
||
|
return ntStatus;
|
||
|
|
||
|
// validate buffer size.
|
||
|
if (PropertyRequest->ValueSize < sizeof (tAC97Features))
|
||
|
return ntStatus;
|
||
|
|
||
|
// The "Value" is the out buffer that you pass in DeviceIoControl call.
|
||
|
tAC97Features *pAC97Features = (tAC97Features *) PropertyRequest->Value;
|
||
|
|
||
|
// Check the buffer.
|
||
|
if (!pAC97Features)
|
||
|
return ntStatus;
|
||
|
|
||
|
//
|
||
|
// Fill the AC97Features structure.
|
||
|
//
|
||
|
|
||
|
// Set the volumes.
|
||
|
pAC97Features->MasterVolume = Volume5bit;
|
||
|
if (that->AdapterCommon->GetNodeConfig (NODEC_6BIT_MASTER_VOLUME))
|
||
|
pAC97Features->MasterVolume = Volume6bit;
|
||
|
|
||
|
pAC97Features->HeadphoneVolume = Volume5bit;
|
||
|
if (!that->AdapterCommon->GetPinConfig (PINC_HPOUT_PRESENT))
|
||
|
pAC97Features->HeadphoneVolume = VolumeDisabled;
|
||
|
else if (that->AdapterCommon->GetNodeConfig (NODEC_6BIT_HPOUT_VOLUME))
|
||
|
pAC97Features->HeadphoneVolume = Volume6bit;
|
||
|
|
||
|
pAC97Features->MonoOutVolume = Volume5bit;
|
||
|
if (!that->AdapterCommon->GetPinConfig (PINC_MONOOUT_PRESENT))
|
||
|
pAC97Features->MonoOutVolume = VolumeDisabled;
|
||
|
else if (that->AdapterCommon->GetNodeConfig (NODEC_6BIT_MONOOUT_VOLUME))
|
||
|
pAC97Features->MonoOutVolume = Volume6bit;
|
||
|
|
||
|
// The 18/20bit Resolution information.
|
||
|
WORD wCodecID;
|
||
|
|
||
|
// Read the reset register.
|
||
|
ntStatus = that->AdapterCommon->ReadCodecRegister (AC97REG_RESET, &wCodecID);
|
||
|
if (!NT_SUCCESS (ntStatus))
|
||
|
return ntStatus;
|
||
|
|
||
|
//
|
||
|
// Now check the DAC and ADC resolution.
|
||
|
//
|
||
|
|
||
|
// First the DAC.
|
||
|
pAC97Features->DAC = Resolution16bit;
|
||
|
if (wCodecID & 0x0040)
|
||
|
pAC97Features->DAC = Resolution18bit;
|
||
|
if (wCodecID & 0x0080)
|
||
|
pAC97Features->DAC = Resolution20bit;
|
||
|
|
||
|
// Then the ADC.
|
||
|
pAC97Features->ADC = Resolution16bit;
|
||
|
if (wCodecID & 0x0100)
|
||
|
pAC97Features->ADC = Resolution18bit;
|
||
|
if (wCodecID & 0x0200)
|
||
|
pAC97Features->ADC = Resolution20bit;
|
||
|
|
||
|
// 3D technique
|
||
|
pAC97Features->n3DTechnique = ((wCodecID & 0x7C00) >> 10);
|
||
|
|
||
|
// Set the flag for MicIn.
|
||
|
pAC97Features->bMicInPresent = that->AdapterCommon->
|
||
|
GetPinConfig (PINC_MICIN_PRESENT) ? TRUE : FALSE;
|
||
|
|
||
|
// Variable sample rate info.
|
||
|
pAC97Features->bVSRPCM = that->AdapterCommon->
|
||
|
GetNodeConfig (NODEC_PCM_VARIABLERATE_SUPPORTED) ? TRUE : FALSE;
|
||
|
pAC97Features->bDSRPCM = that->AdapterCommon->
|
||
|
GetNodeConfig (NODEC_PCM_DOUBLERATE_SUPPORTED) ? TRUE : FALSE;
|
||
|
pAC97Features->bVSRMIC = that->AdapterCommon->
|
||
|
GetNodeConfig (NODEC_MIC_VARIABLERATE_SUPPORTED) ? TRUE : FALSE;
|
||
|
|
||
|
// Additional DAC's
|
||
|
pAC97Features->bCenterDAC = that->AdapterCommon->
|
||
|
GetNodeConfig (NODEC_CENTER_DAC_PRESENT) ? TRUE : FALSE;
|
||
|
pAC97Features->bSurroundDAC = that->AdapterCommon->
|
||
|
GetNodeConfig (NODEC_SURROUND_DAC_PRESENT) ? TRUE : FALSE;
|
||
|
pAC97Features->bLFEDAC = that->AdapterCommon->
|
||
|
GetNodeConfig (NODEC_LFE_DAC_PRESENT) ? TRUE : FALSE;
|
||
|
|
||
|
|
||
|
// We filled out the structure.
|
||
|
PropertyRequest->ValueSize = sizeof (tAC97Features);
|
||
|
DOUT (DBG_PROPERTY, ("Get AC97Features succeeded."));
|
||
|
|
||
|
// ntStatus was set with the read call! whatever this is, return it.
|
||
|
}
|
||
|
#ifdef PROPERTY_SHOW_SET
|
||
|
else
|
||
|
{
|
||
|
// Just to show, we have a SET also.
|
||
|
if (PropertyRequest->Verb & KSPROPERTY_TYPE_SET)
|
||
|
{
|
||
|
// This is the only property for a SET.
|
||
|
if (PropertyRequest->PropertyItem->Id != KSPROPERTY_AC97_SAMPLE_SET)
|
||
|
return ntStatus;
|
||
|
|
||
|
// validate buffer size.
|
||
|
if (PropertyRequest->ValueSize < sizeof (DWORD))
|
||
|
return ntStatus;
|
||
|
|
||
|
// Get the pointer to the DWORD.
|
||
|
DWORD *pTimerTick = (DWORD *)PropertyRequest->Value;
|
||
|
|
||
|
// Check the buffer.
|
||
|
if (!pTimerTick)
|
||
|
return ntStatus;
|
||
|
|
||
|
// Print the message.
|
||
|
DOUT (DBG_ALL, ("This computer is already %d ms running Windows!", *pTimerTick));
|
||
|
ntStatus = STATUS_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return ntStatus;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|