return MM_STATUS_SUCCESS;
}
+LPMIXERLINE_EXT
+MMixerGetMixerLineContainingNodeId(
+ IN LPMIXER_INFO MixerInfo,
+ IN ULONG NodeID)
+{
+ PLIST_ENTRY Entry, ControlEntry;
+ LPMIXERLINE_EXT MixerLineSrc;
+ LPMIXERCONTROL_EXT MixerControl;
+
+ /* get first entry */
+ Entry = MixerInfo->LineList.Flink;
+
+ while(Entry != &MixerInfo->LineList)
+ {
+ MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
+
+ ControlEntry = MixerLineSrc->ControlsList.Flink;
+ while(ControlEntry != &MixerLineSrc->ControlsList)
+ {
+ MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry);
+ if (MixerControl->NodeID == NodeID)
+ {
+ return MixerLineSrc;
+ }
+ ControlEntry = ControlEntry->Flink;
+ }
+ Entry = Entry->Flink;
+ }
+
+ return NULL;
+}
+
+VOID
+MMixerGetLowestLogicalTopologyPinOffsetFromArray(
+ IN ULONG LogicalPinArrayCount,
+ IN PULONG LogicalPinArray,
+ OUT PULONG PinOffset)
+{
+ ULONG Index;
+ ULONG LowestId = 0;
+
+ for(Index = 1; Index < LogicalPinArrayCount; Index++)
+ {
+ if (LogicalPinArray[Index] != MAXULONG)
+ {
+ /* sanity check: logical pin id must be unique */
+ ASSERT(LogicalPinArray[Index] != LogicalPinArray[LowestId]);
+ }
+
+ if (LogicalPinArray[Index] < LogicalPinArray[LowestId])
+ LowestId = Index;
+ }
+
+ /* store result */
+ *PinOffset = LowestId;
+}
+
VOID
MMixerFreeMixerInfo(
IN PMIXER_CONTEXT MixerContext,
MixerContext->Free((PVOID)MixerInfo);
}
+
+LPMIXER_DATA
+MMixerGetMixerDataByDeviceHandle(
+ IN PMIXER_CONTEXT MixerContext,
+ IN HANDLE hDevice)
+{
+ LPMIXER_DATA MixerData;
+ PLIST_ENTRY Entry;
+ PMIXER_LIST MixerList;
+
+ /* get mixer list */
+ MixerList = (PMIXER_LIST)MixerContext->MixerContext;
+
+ if (!MixerList->MixerDataCount)
+ return NULL;
+
+ Entry = MixerList->MixerData.Flink;
+
+ while(Entry != &MixerList->MixerData)
+ {
+ MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
+
+ if (MixerData->hDevice == hDevice)
+ return MixerData;
+
+ /* move to next mixer entry */
+ Entry = Entry->Flink;
+ }
+ return NULL;
+}
+
+
LPMIXER_INFO
MMixerGetMixerInfoByIndex(
IN PMIXER_CONTEXT MixerContext,
PLIST_ENTRY Entry;
PEVENT_NOTIFICATION_ENTRY NotificationEntry;
- /* enumerate list and add a notification entry */
- Entry = MixerInfo->LineList.Flink;
+ /* enumerate list and perform notification */
+ Entry = MixerInfo->EventList.Flink;
while(Entry != &MixerInfo->EventList)
{
/* get notification entry offset */
return Status;
}
+MIXER_STATUS
+MMixerSetGetMuxControlDetails(
+ IN PMIXER_CONTEXT MixerContext,
+ IN LPMIXER_INFO MixerInfo,
+ IN ULONG NodeId,
+ IN ULONG bSet,
+ IN ULONG Flags,
+ IN LPMIXERCONTROL_EXT MixerControl,
+ IN LPMIXERCONTROLDETAILS MixerControlDetails,
+ IN LPMIXERLINE_EXT MixerLine)
+{
+ MIXER_STATUS Status;
+ PULONG LogicalNodes, ConnectedNodes;
+ ULONG LogicalNodesCount, ConnectedNodesCount, Index, CurLogicalPinOffset, BytesReturned, OldLogicalPinOffset;
+ LPMIXER_DATA MixerData;
+ LPMIXERCONTROLDETAILS_LISTTEXTW ListText;
+ LPMIXERCONTROLDETAILS_BOOLEAN Values;
+ LPMIXERLINE_EXT SourceLine;
+ KSNODEPROPERTY Request;
+
+ DPRINT("MixerControlDetails %p\n", MixerControlDetails);
+ DPRINT("bSet %lx\n", bSet);
+ DPRINT("Flags %lx\n", Flags);
+ DPRINT("NodeId %lu\n", MixerControl->NodeID);
+ DPRINT("MixerControlDetails dwControlID %lu\n", MixerControlDetails->dwControlID);
+ DPRINT("MixerControlDetails cChannels %lu\n", MixerControlDetails->cChannels);
+ DPRINT("MixerControlDetails cMultipleItems %lu\n", MixerControlDetails->cMultipleItems);
+ DPRINT("MixerControlDetails cbDetails %lu\n", MixerControlDetails->cbDetails);
+ DPRINT("MixerControlDetails paDetails %p\n", MixerControlDetails->paDetails);
+
+ if (MixerControl->Control.fdwControl & MIXERCONTROL_CONTROLF_UNIFORM)
+ {
+ /* control acts uniform */
+ if (MixerControlDetails->cChannels != 1)
+ {
+ /* expected 1 channel */
+ DPRINT1("Expected 1 channel but got %lu\n", MixerControlDetails->cChannels);
+ return MM_STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ /* check if multiple items match */
+ if (MixerControlDetails->cMultipleItems != MixerControl->Control.cMultipleItems)
+ {
+ DPRINT1("MultipleItems mismatch %lu expected %lu\n", MixerControlDetails->cMultipleItems, MixerControl->Control.cMultipleItems);
+ return MM_STATUS_UNSUCCESSFUL;
+ }
+
+ if (bSet)
+ {
+ if ((Flags & MIXER_SETCONTROLDETAILSF_QUERYMASK) == MIXER_SETCONTROLDETAILSF_CUSTOM)
+ {
+ /* tell me when this is hit */
+ ASSERT(FALSE);
+ }
+ else if ((Flags & (MIXER_SETCONTROLDETAILSF_VALUE | MIXER_SETCONTROLDETAILSF_CUSTOM)) == MIXER_SETCONTROLDETAILSF_VALUE)
+ {
+ /* sanity check */
+ ASSERT(bSet == TRUE);
+ ASSERT(MixerControlDetails->cbDetails == sizeof(MIXERCONTROLDETAILS_BOOLEAN));
+
+ Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
+ CurLogicalPinOffset = MAXULONG;
+ for(Index = 0; Index < MixerControlDetails->cMultipleItems; Index++)
+ {
+ if (Values[Index].fValue)
+ {
+ /* mux can only activate one line at a time */
+ ASSERT(CurLogicalPinOffset == MAXULONG);
+ CurLogicalPinOffset = Index;
+ }
+ }
+
+ /* setup request */
+ Request.NodeId = NodeId;
+ Request.Reserved = 0;
+ Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
+ Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
+ Request.Property.Set = KSPROPSETID_Audio;
+
+ /* perform getting source */
+ Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
+ if (Status != MM_STATUS_SUCCESS)
+ {
+ /* failed to get source */
+ return Status;
+ }
+
+ DPRINT("OldLogicalPinOffset %lu CurLogicalPinOffset %lu\n", OldLogicalPinOffset, CurLogicalPinOffset);
+
+ if (OldLogicalPinOffset == CurLogicalPinOffset)
+ {
+ /* cannot be unselected */
+ return MM_STATUS_UNSUCCESSFUL;
+ }
+
+ /* perform setting source */
+ Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_SET;
+ Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &CurLogicalPinOffset, sizeof(ULONG), &BytesReturned);
+ if (Status != MM_STATUS_SUCCESS)
+ {
+ /* failed to set source */
+ return Status;
+ }
+
+ /* notify control change */
+ MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID );
+
+ return Status;
+ }
+ }
+ else
+ {
+ if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_VALUE)
+ {
+ /* setup request */
+ Request.NodeId = NodeId;
+ Request.Reserved = 0;
+ Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
+ Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
+ Request.Property.Set = KSPROPSETID_Audio;
+
+ /* perform getting source */
+ Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
+ if (Status != MM_STATUS_SUCCESS)
+ {
+ /* failed to get source */
+ return Status;
+ }
+
+ /* gets the corresponding mixer data */
+ MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
+
+ /* sanity check */
+ ASSERT(MixerData);
+ ASSERT(MixerData->Topology);
+ ASSERT(MixerData->MixerInfo == MixerInfo);
+
+ /* get logical pin nodes */
+ MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
+
+ /* sanity check */
+ ASSERT(LogicalNodesCount == MixerControlDetails->cMultipleItems);
+ ASSERT(LogicalNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
+
+ Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
+ for(Index = 0; Index < ConnectedNodesCount; Index++)
+ {
+ /* getting logical pin offset */
+ MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
+
+ if (CurLogicalPinOffset == OldLogicalPinOffset)
+ {
+ /* mark index as active */
+ Values[Index].fValue = TRUE;
+ }
+ else
+ {
+ /* index not active */
+ Values[Index].fValue = FALSE;
+ }
+
+ /* mark offset as consumed */
+ LogicalNodes[CurLogicalPinOffset] = MAXULONG;
+ }
+
+ /* cleanup */
+ MixerContext->Free(LogicalNodes);
+
+ /* done */
+ return MM_STATUS_SUCCESS;
+ }
+ else if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_LISTTEXT)
+ {
+ /* sanity check */
+ ASSERT(bSet == FALSE);
+
+ /* gets the corresponding mixer data */
+ MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
+
+ /* sanity check */
+ ASSERT(MixerData);
+ ASSERT(MixerData->Topology);
+ ASSERT(MixerData->MixerInfo == MixerInfo);
+
+ /* now allocate logical pin array */
+ Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes);
+ if (Status != MM_STATUS_SUCCESS)
+ {
+ /* no memory */
+ return MM_STATUS_NO_MEMORY;
+ }
+
+ /* allocate connected node array */
+ Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &ConnectedNodes);
+ if (Status != MM_STATUS_SUCCESS)
+ {
+ /* no memory */
+ MixerContext->Free(LogicalNodes);
+ return MM_STATUS_NO_MEMORY;
+ }
+
+ /* get logical pin nodes */
+ MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
+
+ /* get connected nodes */
+ MMixerGetNextNodesFromNodeIndex(MixerContext, MixerData->Topology, MixerControl->NodeID, TRUE, &ConnectedNodesCount, ConnectedNodes);
+
+ /* sanity check */
+ ASSERT(ConnectedNodesCount == LogicalNodesCount);
+ ASSERT(ConnectedNodesCount == MixerControlDetails->cMultipleItems);
+ ASSERT(ConnectedNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
+
+ ListText = (LPMIXERCONTROLDETAILS_LISTTEXTW)MixerControlDetails->paDetails;
+
+ for(Index = 0; Index < ConnectedNodesCount; Index++)
+ {
+ /* getting logical pin offset */
+ MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
+
+ /* get mixer line with that node */
+ SourceLine = MMixerGetMixerLineContainingNodeId(MixerInfo, ConnectedNodes[CurLogicalPinOffset]);
+
+ /* sanity check */
+ ASSERT(SourceLine);
+
+ DPRINT1("PinOffset %lu LogicalPin %lu NodeId %lu LineName %S\n", CurLogicalPinOffset, LogicalNodes[CurLogicalPinOffset], ConnectedNodes[CurLogicalPinOffset], SourceLine->Line.szName);
+
+ /* copy details */
+ ListText[Index].dwParam1 = SourceLine->Line.dwLineID;
+ ListText[Index].dwParam2 = SourceLine->Line.dwComponentType;
+ MixerContext->Copy(ListText[Index].szName, SourceLine->Line.szName, (wcslen(SourceLine->Line.szName) + 1) * sizeof(WCHAR));
+
+ /* mark offset as consumed */
+ LogicalNodes[CurLogicalPinOffset] = MAXULONG;
+ }
+
+ /* cleanup */
+ MixerContext->Free(LogicalNodes);
+ MixerContext->Free(ConnectedNodes);
+
+ /* done */
+ return MM_STATUS_SUCCESS;
+ }
+ }
+
+ return MM_STATUS_NOT_IMPLEMENTED;
+}
+
MIXER_STATUS
MMixerSetGetVolumeControlDetails(
IN PMIXER_CONTEXT MixerContext,