- Implement setting / getting volume level
[reactos.git] / reactos / drivers / wdm / audio / legacy / wdmaud / mixer.c
index 2c83c52..4fc4775 100644 (file)
 
 const GUID KSNODETYPE_DAC = {0x507AE360L, 0xC554, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
 const GUID KSNODETYPE_ADC = {0x4D837FE0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSNODETYPE_AGC = {0xE88C9BA0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSNODETYPE_LOUDNESS = {0x41887440L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSNODETYPE_MUTE =     {0x02B223C0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSNODETYPE_TONE =     {0x7607E580L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSNODETYPE_VOLUME =   {0x3A5ACC00L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSNODETYPE_PEAKMETER = {0xa085651e, 0x5f0d, 0x4b36, {0xa8, 0x69, 0xd1, 0x95, 0xd6, 0xab, 0x4b, 0x9e}};
+const GUID KSNODETYPE_MUX =       {0x2CEAF780, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSNODETYPE_STEREO_WIDE = {0xA9E69800L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSNODETYPE_CHORUS =      {0x20173F20L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSNODETYPE_REVERB =      {0xEF0328E0L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSNODETYPE_SUPERMIX =    {0xE573ADC0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSNODETYPE_SUM = {0xDA441A60L, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
+const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
+
+#define DESTINATION_LINE 0xFFFF0000
+
+LPMIXERLINE_EXT
+GetSourceMixerLine(
+    LPMIXER_INFO MixerInfo,
+    DWORD dwSource)
+{
+    PLIST_ENTRY Entry;
+    LPMIXERLINE_EXT MixerLineSrc;
+
+    /* get first entry */
+    Entry = MixerInfo->LineList.Flink;
+
+    while(Entry != &MixerInfo->LineList)
+    {
+        MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
+        DPRINT("dwSource %x dwSource %x\n", MixerLineSrc->Line.dwSource, dwSource);
+        if (MixerLineSrc->Line.dwSource == dwSource)
+            return MixerLineSrc;
+
+        Entry = Entry->Flink;
+    }
+
+    return NULL;
+}
+
+LPMIXERCONTROL_DATA
+GetMixerControlDataById(
+    PLIST_ENTRY ListHead,
+    DWORD dwControlId)
+{
+    PLIST_ENTRY Entry;
+    LPMIXERCONTROL_DATA Control;
+
+    /* get first entry */
+    Entry = ListHead->Flink;
+
+    while(Entry != ListHead)
+    {
+        Control = (LPMIXERCONTROL_DATA)CONTAINING_RECORD(Entry, MIXERCONTROL_DATA, Entry);
+        DPRINT("dwSource %x dwSource %x\n", Control->dwControlID, dwControlId);
+        if (Control->dwControlID == dwControlId)
+            return Control;
+
+        Entry = Entry->Flink;
+    }
+    return NULL;
+}
+
+NTSTATUS
+GetMixerControlById(
+    LPMIXER_INFO MixerInfo,
+    DWORD dwControlID,
+    LPMIXERLINE_EXT *MixerLine,
+    LPMIXERCONTROLW *MixerControl,
+    PULONG NodeId)
+{
+    PLIST_ENTRY Entry;
+    LPMIXERLINE_EXT MixerLineSrc;
+    ULONG Index;
+
+    /* get first entry */
+    Entry = MixerInfo->LineList.Flink;
+
+    while(Entry != &MixerInfo->LineList)
+    {
+        MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
+
+        for(Index = 0; Index < MixerLineSrc->Line.cConnections; Index++)
+        {
+            if (MixerLineSrc->LineControls[Index].dwControlID == dwControlID)
+            {
+                *MixerLine = MixerLineSrc;
+                *MixerControl = &MixerLineSrc->LineControls[Index];
+                *NodeId = MixerLineSrc->NodeIds[Index];
+                return STATUS_SUCCESS;
+            }
+        }
+        Entry = Entry->Flink;
+    }
+
+    return STATUS_NOT_FOUND;
+}
+
+
+
+LPMIXERLINE_EXT
+GetSourceMixerLineByLineId(
+    LPMIXER_INFO MixerInfo,
+    DWORD dwLineID)
+{
+    PLIST_ENTRY Entry;
+    LPMIXERLINE_EXT MixerLineSrc;
+
+    /* get first entry */
+    Entry = MixerInfo->LineList.Flink;
+
+    while(Entry != &MixerInfo->LineList)
+    {
+        MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
+        DPRINT("dwLineID %x dwLineID %x\n", MixerLineSrc->Line.dwLineID, dwLineID);
+        if (MixerLineSrc->Line.dwLineID == dwLineID)
+            return MixerLineSrc;
+
+        Entry = Entry->Flink;
+    }
+
+    return NULL;
+}
+
+
+
+ULONG
+GetPinCount(
+    IN PFILE_OBJECT FileObject)
+{
+    KSPROPERTY Pin;
+    NTSTATUS Status;
+    ULONG NumPins, BytesReturned;
+
+    Pin.Flags = KSPROPERTY_TYPE_GET;
+    Pin.Set = KSPROPSETID_Pin;
+    Pin.Id = KSPROPERTY_PIN_CTYPES;
+
+    Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSPROPERTY), (PVOID)&NumPins, sizeof(ULONG), &BytesReturned);
+    if (!NT_SUCCESS(Status))
+        return 0;
+
+    return NumPins;
+}
+
 
 ULONG
 GetSysAudioDeviceCount(
@@ -83,27 +228,53 @@ GetSysAudioDevicePnpName(
     return Status;
 }
 
+ULONG
+GetDeviceIndexFromPnpName(
+    IN PDEVICE_OBJECT DeviceObject,
+    LPWSTR Device)
+{
+    ULONG Count, Index;
+    LPWSTR DeviceName;
+    NTSTATUS Status;
+
+    /* get device count */
+    Count = GetSysAudioDeviceCount(DeviceObject);
+
+    if (!Count)
+        return MAXULONG;
+
+    for(Index = 0; Index < Count; Index++)
+    {
+        /* get device name */
+        Status = GetSysAudioDevicePnpName(DeviceObject, Index, &DeviceName);
+        if (NT_SUCCESS(Status))
+        {
+            if (!wcsicmp(Device, DeviceName))
+            {
+                /* found device index */
+                ExFreePool(DeviceName);
+                return Index;
+            }
+            ExFreePool(DeviceName);
+        }
+    }
+
+    return MAXULONG;
+}
+
+
 NTSTATUS
-OpenSysAudioDeviceByIndex(
-    IN  PDEVICE_OBJECT DeviceObject,
-    IN  ULONG DeviceIndex,
-    IN  PHANDLE DeviceHandle,
-    IN  PFILE_OBJECT * FileObject)
+OpenDevice(
+    IN LPWSTR Device,
+    OUT PHANDLE DeviceHandle,
+    OUT PFILE_OBJECT * FileObject)
 {
-    LPWSTR Device = NULL;
     NTSTATUS Status;
     HANDLE hDevice;
 
-    Status = GetSysAudioDevicePnpName(DeviceObject, DeviceIndex, &Device);
-    if (!NT_SUCCESS(Status))
-        return Status;
-
     /* now open the device */
     Status = WdmAudOpenSysAudioDevice(Device, &hDevice);
 
-    /* free device buffer */
-    ExFreePool(Device);
-
     if (!NT_SUCCESS(Status))
     {
         return Status;
@@ -121,12 +292,37 @@ OpenSysAudioDeviceByIndex(
         }
     }
 
+    return Status;
+
+}
+
+
+NTSTATUS
+OpenSysAudioDeviceByIndex(
+    IN  PDEVICE_OBJECT DeviceObject,
+    IN  ULONG DeviceIndex,
+    IN  PHANDLE DeviceHandle,
+    IN  PFILE_OBJECT * FileObject)
+{
+    LPWSTR Device = NULL;
+    NTSTATUS Status;
+
+    Status = GetSysAudioDevicePnpName(DeviceObject, DeviceIndex, &Device);
+    if (!NT_SUCCESS(Status))
+        return Status;
+
+    Status = OpenDevice(Device, DeviceHandle, FileObject);
+
+    /* free device buffer */
+    ExFreePool(Device);
+
     return Status;
 }
 
 NTSTATUS
-GetFilterNodeTypes(
-    PFILE_OBJECT FileObject,
+GetFilterNodeProperty(
+    IN PFILE_OBJECT FileObject,
+    IN ULONG PropertyId,
     PKSMULTIPLE_ITEM * Item)
 {
     NTSTATUS Status;
@@ -135,7 +331,7 @@ GetFilterNodeTypes(
     KSPROPERTY Property;
 
     /* setup query request */
-    Property.Id = KSPROPERTY_TOPOLOGY_NODES;
+    Property.Id = PropertyId;
     Property.Flags = KSPROPERTY_TYPE_GET;
     Property.Set = KSPROPSETID_Topology;
 
@@ -214,174 +410,1287 @@ GetNodeTypeIndex(
 }
 
 ULONG
-GetNumOfMixerDevices(
-    IN  PDEVICE_OBJECT DeviceObject)
+GetControlTypeFromTopologyNode(
+    IN LPGUID NodeType)
+{
+    if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_AGC))
+    {
+        // automatic gain control
+        return MIXERCONTROL_CONTROLTYPE_ONOFF;
+    }
+    else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_LOUDNESS))
+    {
+        // loudness control
+        return MIXERCONTROL_CONTROLTYPE_LOUDNESS;
+    }
+    else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUTE ))
+    {
+        // mute control
+        return MIXERCONTROL_CONTROLTYPE_MUTE;
+    }
+    else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_TONE))
+    {
+        // tpne control
+        //FIXME
+        // MIXERCONTROL_CONTROLTYPE_ONOFF if KSPROPERTY_AUDIO_BASS_BOOST is supported
+        // MIXERCONTROL_CONTROLTYPE_BASS if KSPROPERTY_AUDIO_BASS is supported
+        // MIXERCONTROL_CONTROLTYPE_TREBLE if KSPROPERTY_AUDIO_TREBLE is supported
+        UNIMPLEMENTED;
+        return MIXERCONTROL_CONTROLTYPE_ONOFF;
+    }
+    else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_VOLUME))
+    {
+        // volume control
+        return MIXERCONTROL_CONTROLTYPE_VOLUME;
+    }
+    else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_PEAKMETER))
+    {
+        // peakmeter control
+        return MIXERCONTROL_CONTROLTYPE_PEAKMETER;
+    }
+    else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUX))
+    {
+        // mux control
+        return MIXERCONTROL_CONTROLTYPE_MUX;
+    }
+    else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_MUX))
+    {
+        // mux control
+        return MIXERCONTROL_CONTROLTYPE_MUX;
+    }
+    else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_STEREO_WIDE))
+    {
+        // stero wide control
+        return MIXERCONTROL_CONTROLTYPE_FADER;
+    }
+    else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_CHORUS))
+    {
+        // chorus control
+        return MIXERCONTROL_CONTROLTYPE_FADER;
+    }
+    else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_REVERB))
+    {
+        // reverb control
+        return MIXERCONTROL_CONTROLTYPE_FADER;
+    }
+    else if (IsEqualGUIDAligned(NodeType, (LPGUID)&KSNODETYPE_SUPERMIX))
+    {
+        // supermix control
+        // MIXERCONTROL_CONTROLTYPE_MUTE if KSPROPERTY_AUDIO_MUTE is supported 
+        UNIMPLEMENTED;
+        return MIXERCONTROL_CONTROLTYPE_VOLUME;
+    }
+    UNIMPLEMENTED
+    return 0;
+}
+
+NTSTATUS
+GetPhysicalConnection(
+    IN PFILE_OBJECT FileObject,
+    IN ULONG PinId,
+    OUT PKSPIN_PHYSICALCONNECTION *OutConnection)
 {
-    ULONG DeviceCount, Index, Count;
+    KSP_PIN Pin;
     NTSTATUS Status;
-    HANDLE hDevice;
-    PFILE_OBJECT FileObject;
-    PKSMULTIPLE_ITEM MultipleItem;
+    ULONG BytesReturned;
+    PKSPIN_PHYSICALCONNECTION Connection;
 
-    /* get number of devices */
-    DeviceCount = GetSysAudioDeviceCount(DeviceObject);
+    /* setup the request */
+    Pin.Property.Flags = KSPROPERTY_TYPE_GET;
+    Pin.Property.Id = KSPROPERTY_PIN_PHYSICALCONNECTION;
+    Pin.Property.Set = KSPROPSETID_Pin;
+    Pin.PinId = PinId;
 
-    if (!DeviceCount)
-        return 0;
+    /* query the pin for the physical connection */
+    Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
 
-    Index = 0;
-    Count = 0;
-    do
+    if (Status == STATUS_NOT_FOUND)
     {
-        /* open the virtual audio device */
-        Status = OpenSysAudioDeviceByIndex(DeviceObject, Index, &hDevice, &FileObject);
+        /* pin does not have a physical connection */
+        return Status;
+    }
 
-        if (NT_SUCCESS(Status))
+    Connection = ExAllocatePool(NonPagedPool, BytesReturned);
+    if (!Connection)
+    {
+        /* not enough memory */
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* query the pin for the physical connection */
+    Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Connection, BytesReturned, &BytesReturned);
+    if (!NT_SUCCESS(Status))
+    {
+        /* failed to query the physical connection */
+        ExFreePool(Connection);
+        return Status;
+    }
+
+    /* store connection */
+    *OutConnection = Connection;
+    return Status;
+}
+
+NTSTATUS
+GetNodeIndexes(
+    IN PKSMULTIPLE_ITEM MultipleItem,
+    IN ULONG NodeIndex,
+    IN ULONG bNode,
+    IN ULONG bFrom,
+    OUT PULONG NodeReferenceCount,
+    OUT PULONG *NodeReference)
+{
+    ULONG Index, Count = 0;
+    PKSTOPOLOGY_CONNECTION Connection;
+    PULONG Refs;
+
+    /* KSMULTIPLE_ITEM is followed by several KSTOPOLOGY_CONNECTION */
+    Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
+
+    /* first count all referenced nodes */
+    for(Index = 0; Index < MultipleItem->Count; Index++)
+    {
+        //DbgPrint("FromPin %u FromNode %u ToPin %u ToNode %u\n", Connection->FromNodePin, Connection->FromNode, Connection->ToNodePin, Connection->ToNode);
+        if (bNode)
         {
-            /* retrieve all available node types */
-            Status = GetFilterNodeTypes(FileObject, &MultipleItem);
-            if (NT_SUCCESS(Status))
+            if (bFrom)
             {
-                if (CountNodeType(MultipleItem, (LPGUID)&KSNODETYPE_DAC))
+                if (Connection->FromNode == NodeIndex)
                 {
-                    /* increment (output) mixer count */
+                    /* node id has a connection */
                     Count++;
                 }
-
-                if (CountNodeType(MultipleItem, (LPGUID)&KSNODETYPE_ADC))
+            }
+            else
+            {
+                if (Connection->ToNode == NodeIndex)
                 {
-                    /* increment (input) mixer count */
+                    /* node id has a connection */
+                    Count++;
+                }
+            }
+        }
+        else
+        {
+            if (bFrom)
+            {
+                if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
+                {
+                    /* node id has a connection */
+                    Count++;
+                }
+            }
+            else
+            {
+                if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
+                {
+                    /* node id has a connection */
                     Count++;
                 }
-                ExFreePool(MultipleItem);
             }
-            ObDereferenceObject(FileObject);
-            ZwClose(hDevice);
         }
 
-        Index++;
-    }while(Index < DeviceCount);
 
-    return Count;
-}
+        /* move to next connection */
+        Connection++;
+    }
 
-ULONG
-IsOutputMixer(
-    IN PDEVICE_OBJECT DeviceObject,
-    IN ULONG DeviceIndex)
-{
-    ULONG DeviceCount, Index, Count;
-    NTSTATUS Status;
-    HANDLE hDevice;
-    PFILE_OBJECT FileObject;
-    PKSMULTIPLE_ITEM MultipleItem;
+    ASSERT(Count != 0);
 
-    /* get number of devices */
-    DeviceCount = GetSysAudioDeviceCount(DeviceObject);
+    /* now allocate node index array */
+    Refs = ExAllocatePool(NonPagedPool, sizeof(ULONG) * Count);
+    if (!Refs)
+    {
+        /* not enough memory */
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
 
-    if (!DeviceCount)
-        return 0;
+    /* clear node index array */
+    RtlZeroMemory(Refs, Count * sizeof(ULONG));
 
-    Index = 0;
     Count = 0;
-    do
+    Connection = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
+    for(Index = 0; Index < MultipleItem->Count; Index++)
     {
-        /* open the virtual audio device */
-        Status = OpenSysAudioDeviceByIndex(DeviceObject, Index, &hDevice, &FileObject);
-
-        if (NT_SUCCESS(Status))
+        if (bNode)
         {
-            /* retrieve all available node types */
-            Status = GetFilterNodeTypes(FileObject, &MultipleItem);
-            if (NT_SUCCESS(Status))
+            if (bFrom)
             {
-                if (CountNodeType(MultipleItem, (LPGUID)&KSNODETYPE_DAC))
+                if (Connection->FromNode == NodeIndex)
                 {
-                    /* increment (output) mixer count */
-                    if (DeviceIndex == Count)
-                    {
-                        ExFreePool(MultipleItem);
-                        ObDereferenceObject(FileObject);
-                        ZwClose(hDevice);
-                        return TRUE;
-                    }
-
+                    /* node id has a connection */
+                    Refs[Count] = Index;
                     Count++;
                 }
-
-                if (CountNodeType(MultipleItem, (LPGUID)&KSNODETYPE_ADC))
+            }
+            else
+            {
+                if (Connection->ToNode == NodeIndex)
                 {
-                    /* increment (input) mixer count */
-                    if (DeviceIndex == Count)
-                    {
-                        ExFreePool(MultipleItem);
-                        ObDereferenceObject(FileObject);
-                        ZwClose(hDevice);
-                        return FALSE;
-                    }
+                    /* node id has a connection */
+                    Refs[Count] = Index;
+                    Count++;
+                }
+            }
+        }
+        else
+        {
+            if (bFrom)
+            {
+                if (Connection->FromNodePin == NodeIndex && Connection->FromNode == KSFILTER_NODE)
+                {
+                    /* node id has a connection */
+                    Refs[Count] = Index;
+                    Count++;
+                }
+            }
+            else
+            {
+                if (Connection->ToNodePin == NodeIndex && Connection->ToNode == KSFILTER_NODE)
+                {
+                    /* node id has a connection */
+                    Refs[Count] = Index;
                     Count++;
                 }
-                ExFreePool(MultipleItem);
             }
-            ObDereferenceObject(FileObject);
-            ZwClose(hDevice);
         }
 
-        Index++;
-    }while(Index < DeviceCount);
-
-    ASSERT(0);
-    return FALSE;
-}
-
+        /* move to next connection */
+        Connection++;
+    }
 
+    /* store result */
+    *NodeReference = Refs;
+    *NodeReferenceCount = Count;
 
+    return STATUS_SUCCESS;
+}
 
 
 NTSTATUS
-WdmAudMixerCapabilities(
-    IN PDEVICE_OBJECT DeviceObject,
-    IN  PWDMAUD_DEVICE_INFO DeviceInfo,
-    IN  PWDMAUD_CLIENT ClientInfo)
+GetTargetPinsByNodeConnectionIndex(
+    IN PKSMULTIPLE_ITEM NodeConnections,
+    IN PKSMULTIPLE_ITEM NodeTypes,
+    IN ULONG bUpDirection,
+    IN ULONG NodeConnectionIndex,
+    OUT PULONG Pins)
 {
+    PKSTOPOLOGY_CONNECTION Connection;
+    ULONG PinId, NodeConnectionCount, Index;
+    PULONG NodeConnection;
     NTSTATUS Status;
-    LPWSTR Device;
-    WCHAR Buffer[100];
 
-    Status = GetSysAudioDevicePnpName(DeviceObject, DeviceInfo->DeviceIndex,&Device);
+
+    /* sanity check */
+    ASSERT(NodeConnectionIndex < NodeConnections->Count);
+
+    Connection = (PKSTOPOLOGY_CONNECTION)(NodeConnections + 1);
+
+    DPRINT("FromNode %u FromNodePin %u -> ToNode %u ToNodePin %u\n", Connection[NodeConnectionIndex].FromNode, Connection[NodeConnectionIndex].FromNodePin, Connection[NodeConnectionIndex].ToNode, Connection[NodeConnectionIndex].ToNodePin );
+
+    if ((Connection[NodeConnectionIndex].ToNode == KSFILTER_NODE && bUpDirection == FALSE) ||
+        (Connection[NodeConnectionIndex].FromNode == KSFILTER_NODE && bUpDirection == TRUE))
+    {
+        /* iteration stops here */
+       if (bUpDirection)
+           PinId = Connection[NodeConnectionIndex].FromNodePin;
+       else
+           PinId = Connection[NodeConnectionIndex].ToNodePin;
+
+       DPRINT("GetTargetPinsByNodeIndex FOUND Target Pin %u Parsed %u\n", PinId, Pins[PinId]);
+
+       /* mark pin index as a target pin */
+       Pins[PinId] = TRUE;
+       return STATUS_SUCCESS;
+    }
+
+    /* get all node indexes referenced by that node */
+    if (bUpDirection)
+    {
+        Status = GetNodeIndexes(NodeConnections, Connection[NodeConnectionIndex].FromNode, TRUE, FALSE, &NodeConnectionCount, &NodeConnection);
+    }
+    else
+    {
+        Status = GetNodeIndexes(NodeConnections, Connection[NodeConnectionIndex].ToNode, TRUE, TRUE, &NodeConnectionCount, &NodeConnection);
+    }
+
+    if (NT_SUCCESS(Status))
+    {
+        for(Index = 0; Index < NodeConnectionCount; Index++)
+        {
+            /* iterate recursively into the nodes */
+            Status = GetTargetPinsByNodeConnectionIndex(NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Pins);
+            ASSERT(Status == STATUS_SUCCESS);
+        }
+        /* free node connection indexes */
+        ExFreePool(NodeConnection);
+    }
+
+    return Status;
+}
+
+
+
+NTSTATUS
+GetTargetPins(
+    PKSMULTIPLE_ITEM NodeTypes,
+    PKSMULTIPLE_ITEM NodeConnections,
+    IN ULONG NodeIndex,
+    IN ULONG bUpDirection,
+    PULONG Pins,
+    ULONG PinCount)
+{
+    ULONG NodeConnectionCount, Index;
+    NTSTATUS Status;
+    PULONG NodeConnection;
+
+    /* sanity check */
+    ASSERT(NodeIndex != (ULONG)-1);
+
+    /* get all node indexes referenced by that pin */
+    if (bUpDirection)
+        Status = GetNodeIndexes(NodeConnections, NodeIndex, TRUE, FALSE, &NodeConnectionCount, &NodeConnection);
+    else
+        Status = GetNodeIndexes(NodeConnections, NodeIndex, TRUE, TRUE, &NodeConnectionCount, &NodeConnection);
+
+    DPRINT("NodeIndex %u Status %x Count %u\n", NodeIndex, Status, NodeConnectionCount);
+
+    if (NT_SUCCESS(Status))
+    {
+        for(Index = 0; Index < NodeConnectionCount; Index++)
+        {
+            Status = GetTargetPinsByNodeConnectionIndex(NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Pins);
+            ASSERT(Status == STATUS_SUCCESS);
+        }
+        ExFreePool(NodeConnection);
+    }
+
+    return Status;
+}
+
+PULONG
+AllocatePinArray(
+    ULONG PinCount)
+{
+    PULONG Pins = ExAllocatePool(NonPagedPool, PinCount * sizeof(ULONG));
+    if (!Pins)
+        return NULL;
+
+    RtlZeroMemory(Pins, sizeof(ULONG) * PinCount);
+
+    return Pins;
+}
+
+PKSTOPOLOGY_CONNECTION
+GetConnectionByIndex(
+    IN PKSMULTIPLE_ITEM MultipleItem,
+    IN ULONG Index)
+{
+    PKSTOPOLOGY_CONNECTION Descriptor;
+
+    ASSERT(Index < MultipleItem->Count);
+
+    Descriptor = (PKSTOPOLOGY_CONNECTION)(MultipleItem + 1);
+    return &Descriptor[Index];
+}
+
+LPGUID
+GetNodeType(
+    IN PKSMULTIPLE_ITEM MultipleItem,
+    IN ULONG Index)
+{
+    LPGUID NodeType;
+
+    ASSERT(Index < MultipleItem->Count);
+
+    NodeType = (LPGUID)(MultipleItem + 1);
+    return &NodeType[Index];
+}
+
+NTSTATUS
+GetControlsFromPinByConnectionIndex(
+    IN PKSMULTIPLE_ITEM NodeConnections,
+    IN PKSMULTIPLE_ITEM NodeTypes,
+    IN ULONG bUpDirection,
+    IN ULONG NodeConnectionIndex,
+    OUT PULONG Nodes)
+{
+    PKSTOPOLOGY_CONNECTION CurConnection;
+    LPGUID NodeType;
+    ULONG NodeIndex;
+    NTSTATUS Status;
+    ULONG NodeConnectionCount, Index;
+    PULONG NodeConnection;
+
+
+    /* get current connection */
+    CurConnection = GetConnectionByIndex(NodeConnections, NodeConnectionIndex);
+
+    if (bUpDirection)
+        NodeIndex = CurConnection->FromNode;
+    else
+        NodeIndex = CurConnection->ToNode;
+
+    /* get target node type of current connection */
+    NodeType = GetNodeType(NodeTypes, NodeIndex);
+
+    if (IsEqualGUIDAligned(NodeType, &KSNODETYPE_SUM) || IsEqualGUIDAligned(NodeType, &KSNODETYPE_MUX))
+    {
+        if (bUpDirection)
+        {
+            /* add the sum / mux node to destination line */
+            //Nodes[NodeIndex] = TRUE;
+        }
+
+        return STATUS_SUCCESS;
+    }
+
+    /* now add the node */
+    Nodes[NodeIndex] = TRUE;
+
+
+    /* get all node indexes referenced by that node */
+    if (bUpDirection)
+    {
+        Status = GetNodeIndexes(NodeConnections, NodeIndex, TRUE, FALSE, &NodeConnectionCount, &NodeConnection);
+    }
+    else
+    {
+        Status = GetNodeIndexes(NodeConnections, NodeIndex, TRUE, TRUE, &NodeConnectionCount, &NodeConnection);
+    }
+
+    if (NT_SUCCESS(Status))
+    {
+        for(Index = 0; Index < NodeConnectionCount; Index++)
+        {
+            /* iterate recursively into the nodes */
+            Status = GetControlsFromPinByConnectionIndex(NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Nodes);
+            ASSERT(Status == STATUS_SUCCESS);
+        }
+        /* free node connection indexes */
+        ExFreePool(NodeConnection);
+    }
+
+    return Status;
+}
+
+NTSTATUS
+GetControlsFromPin(
+    IN PKSMULTIPLE_ITEM NodeConnections,
+    IN PKSMULTIPLE_ITEM NodeTypes,
+    IN ULONG PinId,
+    IN ULONG bUpDirection,
+    OUT PULONG Nodes)
+{
+    ULONG NodeConnectionCount, Index;
+    NTSTATUS Status;
+    PULONG NodeConnection;
+
+    /* sanity check */
+    ASSERT(PinId != (ULONG)-1);
+
+    /* get all node indexes referenced by that pin */
+    if (bUpDirection)
+        Status = GetNodeIndexes(NodeConnections, PinId, FALSE, FALSE, &NodeConnectionCount, &NodeConnection);
+    else
+        Status = GetNodeIndexes(NodeConnections, PinId, FALSE, TRUE, &NodeConnectionCount, &NodeConnection);
+
+    for(Index = 0; Index < NodeConnectionCount; Index++)
+    {
+        /* get all associated controls */
+        Status = GetControlsFromPinByConnectionIndex(NodeConnections, NodeTypes, bUpDirection, NodeConnection[Index], Nodes);
+    }
+
+    ExFreePool(NodeConnection);
+
+    return Status;
+}
+
+NTSTATUS
+AddMixerControl(
+    IN LPMIXER_INFO MixerInfo,
+    IN PFILE_OBJECT FileObject,
+    IN PKSMULTIPLE_ITEM NodeTypes,
+    IN ULONG NodeIndex,
+    IN LPMIXERLINE_EXT MixerLine,
+    OUT LPMIXERCONTROLW MixerControl)
+{
+    LPGUID NodeType;
+    KSP_NODE Node;
+    ULONG BytesReturned;
+    NTSTATUS Status;
+    LPWSTR Name;
+
+    /* initialize mixer control */
+    MixerControl->cbStruct = sizeof(MIXERCONTROLW);
+    MixerControl->dwControlID = MixerInfo->ControlId;
+
+    /* get node type */
+    NodeType = GetNodeType(NodeTypes, NodeIndex);
+    /* store control type */
+    MixerControl->dwControlType = GetControlTypeFromTopologyNode(NodeType);
+
+    MixerControl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM; //FIXME
+    MixerControl->cMultipleItems = 0; //FIXME
+
+    if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
+    {
+        MixerControl->Bounds.dwMinimum = 0;
+        MixerControl->Bounds.dwMaximum = 1;
+    }
+    else if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
+    {
+        MixerControl->Bounds.dwMinimum = 0;
+        MixerControl->Bounds.dwMaximum = 0xFFFF;
+        MixerControl->Metrics.cSteps = 0xC0; //FIXME
+    }
+
+    /* setup request to retrieve name */
+    Node.NodeId = NodeIndex;
+    Node.Property.Id = KSPROPERTY_TOPOLOGY_NAME;
+    Node.Property.Flags = KSPROPERTY_TYPE_GET;
+    Node.Property.Set = KSPROPSETID_Topology;
+    Node.Reserved = 0;
+
+    /* get node name size */
+    Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), NULL, 0, &BytesReturned);
+
+    if (Status == STATUS_BUFFER_TOO_SMALL)
+    {
+        ASSERT(BytesReturned != 0);
+        Name = ExAllocatePool(NonPagedPool, BytesReturned);
+        if (!Name)
+        {
+            /* not enough memory */
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        /* get node name */
+        Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), (LPVOID)Name, BytesReturned, &BytesReturned);
+        if (NT_SUCCESS(Status))
+        {
+            RtlMoveMemory(MixerControl->szShortName, Name, (min(MIXER_SHORT_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
+            MixerControl->szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
+
+            RtlMoveMemory(MixerControl->szName, Name, (min(MIXER_LONG_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
+            MixerControl->szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
+        }
+
+        /* free name buffer */
+        ExFreePool(Name);
+    }
+
+    MixerInfo->ControlId++;
+
+    if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
+    {
+        KSNODEPROPERTY_AUDIO_CHANNEL Property;
+        ULONG Length;
+        PKSPROPERTY_DESCRIPTION Desc;
+        PKSPROPERTY_MEMBERSHEADER Members;
+        PKSPROPERTY_STEPPING_LONG Range;
+
+        Length = sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG);
+        Desc = ExAllocatePool(NonPagedPool, Length);
+        ASSERT(Desc);
+        RtlZeroMemory(Desc, Length);
+
+        /* setup the request */
+        RtlZeroMemory(&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL));
+
+        Property.NodeProperty.NodeId = NodeIndex;
+        Property.NodeProperty.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL;
+        Property.NodeProperty.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT;
+        Property.NodeProperty.Property.Set = KSPROPSETID_Audio;
+
+        /* get node volume level info */
+        Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), Desc, Length, &BytesReturned);
+
+        if (NT_SUCCESS(Status))
+        {
+            LPMIXERVOLUME_DATA VolumeData;
+            ULONG Steps, MaxRange, Index;
+            LONG Value;
+
+            Members = (PKSPROPERTY_MEMBERSHEADER)(Desc + 1);
+            Range = (PKSPROPERTY_STEPPING_LONG)(Members + 1); //98304
+
+            DPRINT("NodeIndex %u Range Min %d Max %d Steps %x UMin %x UMax %x\n", NodeIndex, Range->Bounds.SignedMinimum, Range->Bounds.SignedMaximum, Range->SteppingDelta, Range->Bounds.UnsignedMinimum, Range->Bounds.UnsignedMaximum);
+
+            VolumeData = ExAllocatePool(NonPagedPool, sizeof(MIXERVOLUME_DATA));
+            if (!VolumeData)
+                return STATUS_INSUFFICIENT_RESOURCES;
+
+            MaxRange = (abs(Range->Bounds.SignedMinimum) + abs(Range->Bounds.SignedMaximum));
+            Steps = MaxRange / Range->SteppingDelta + 1;
+
+            /* store mixer control info there */
+            VolumeData->Header.dwControlID = MixerControl->dwControlID;
+            VolumeData->SignedMaximum = Range->Bounds.SignedMaximum;
+            VolumeData->SignedMinimum = Range->Bounds.SignedMinimum;
+            VolumeData->SteppingDelta = Range->SteppingDelta;
+            VolumeData->ValuesCount = Steps;
+            VolumeData->InputSteppingDelta = 0x10000 / Steps;
+
+            VolumeData->Values = ExAllocatePool(NonPagedPool, sizeof(LONG) * Steps);
+            if (!VolumeData->Values)
+            {
+                ExFreePool(Desc);
+                ExFreePool(VolumeData);
+
+                return STATUS_INSUFFICIENT_RESOURCES;
+            }
+
+            Value = Range->Bounds.SignedMinimum;
+            for(Index = 0; Index < Steps; Index++)
+            {
+                VolumeData->Values[Index] = Value;
+                Value += Range->SteppingDelta;
+            }
+            InsertTailList(&MixerLine->LineControlsExtraData, &VolumeData->Header.Entry);
+       }
+       ExFreePool(Desc);
+    }
+
+
+    DPRINT("Status %x Name %S\n", Status, MixerControl->szName);
+    return STATUS_SUCCESS;
+}
+
+NTSTATUS
+AddMixerSourceLine(
+    IN OUT LPMIXER_INFO MixerInfo,
+    IN PFILE_OBJECT FileObject,
+    IN PKSMULTIPLE_ITEM NodeConnections,
+    IN PKSMULTIPLE_ITEM NodeTypes,
+    IN ULONG DeviceIndex,
+    IN ULONG PinId,
+    IN ULONG bBridgePin,
+    IN ULONG bTargetPin)
+{
+    LPMIXERLINE_EXT SrcLine, DstLine;
+    NTSTATUS Status;
+    KSP_PIN Pin;
+    LPWSTR PinName;
+    GUID NodeType;
+    ULONG BytesReturned, ControlCount, Index;
+    PULONG Nodes;
+
+    if (!bTargetPin)
+    {
+        /* allocate src mixer line */
+        SrcLine = (LPMIXERLINE_EXT)ExAllocatePool(NonPagedPool, sizeof(MIXERLINE_EXT));
+
+        if (!SrcLine)
+            return STATUS_INSUFFICIENT_RESOURCES;
+
+        /* zero struct */
+        RtlZeroMemory(SrcLine, sizeof(MIXERLINE_EXT));
+
+    }
+    else
+    {
+        ASSERT(!IsListEmpty(&MixerInfo->LineList));
+        SrcLine = GetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
+    }
+
+    /* get destination line */
+    DstLine = GetSourceMixerLineByLineId(MixerInfo, DESTINATION_LINE);
+    ASSERT(DstLine);
+
+
+    if (!bTargetPin)
+    {
+        /* initialize mixer src line */
+        SrcLine->DeviceIndex = DeviceIndex;
+        SrcLine->PinId = PinId;
+        SrcLine->Line.cbStruct = sizeof(MIXERLINEW);
+
+        /* initialize mixer destination line */
+        SrcLine->Line.cbStruct = sizeof(MIXERLINEW);
+        SrcLine->Line.dwDestination = 0;
+        SrcLine->Line.dwSource = DstLine->Line.cConnections;
+        SrcLine->Line.dwLineID = (DstLine->Line.cConnections * 0x10000);
+        SrcLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
+        SrcLine->Line.dwUser = 0;
+        SrcLine->Line.cChannels = DstLine->Line.cChannels;
+        SrcLine->Line.cConnections = 0;
+        SrcLine->Line.Target.dwType = 1;
+        SrcLine->Line.Target.dwDeviceID = DstLine->Line.Target.dwDeviceID;
+        SrcLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
+        SrcLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
+        SrcLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
+        InitializeListHead(&SrcLine->LineControlsExtraData);
+        wcscpy(SrcLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
+
+    }
+
+    /* allocate a node arrary */
+    Nodes = ExAllocatePool(NonPagedPool, sizeof(ULONG) * NodeTypes->Count);
+
+    if (!Nodes)
+    {
+        /* not enough memory */
+        if (!bTargetPin)
+        {
+            ExFreePool(SrcLine);
+        }
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* clear nodes array */
+    RtlZeroMemory(Nodes, sizeof(ULONG) * NodeTypes->Count);
+
+    Status = GetControlsFromPin(NodeConnections, NodeTypes, PinId, bTargetPin, Nodes);
+    if (!NT_SUCCESS(Status))
+    {
+        /* something went wrong */
+        if (!bTargetPin)
+        {
+            ExFreePool(SrcLine);
+        }
+        ExFreePool(Nodes);
+        return Status;
+    }
+
+    /* now count all nodes controlled by that pin */
+    ControlCount = 0;
+    for(Index = 0; Index < NodeTypes->Count; Index++)
+    {
+        if (Nodes[Index])
+            ControlCount++;
+    }
+
+    /* now allocate the line controls */
+    if (ControlCount)
+    {
+        SrcLine->LineControls = ExAllocatePool(NonPagedPool, sizeof(MIXERCONTROLW) * ControlCount);
+
+        if (!SrcLine->LineControls)
+        {
+            /* no memory available */
+            if (!bTargetPin)
+            {
+                ExFreePool(SrcLine);
+            }
+            ExFreePool(Nodes);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        SrcLine->NodeIds = ExAllocatePool(NonPagedPool, sizeof(ULONG) * ControlCount);
+        if (!SrcLine->NodeIds)
+        {
+            /* no memory available */
+            ExFreePool(SrcLine->LineControls);
+            if (!bTargetPin)
+            {
+                ExFreePool(SrcLine);
+            }
+            ExFreePool(Nodes);
+            return STATUS_INSUFFICIENT_RESOURCES;
+        }
+
+        /* zero line controls */
+        RtlZeroMemory(SrcLine->LineControls, sizeof(MIXERCONTROLW) * ControlCount);
+        RtlZeroMemory(SrcLine->NodeIds, sizeof(ULONG) * ControlCount);
+
+        ControlCount = 0;
+        for(Index = 0; Index < NodeTypes->Count; Index++)
+        {
+            if (Nodes[Index])
+            {
+                /* store the node index for retrieving / setting details */
+                SrcLine->NodeIds[ControlCount] = Index;
+
+                Status = AddMixerControl(MixerInfo, FileObject, NodeTypes, Index, SrcLine, &SrcLine->LineControls[ControlCount]);
+                if (NT_SUCCESS(Status))
+                {
+                    /* increment control count on success */
+                    ControlCount++;
+                }
+            }
+        }
+        /* store control count */
+        SrcLine->Line.cControls = ControlCount;
+    }
+
+    /* release nodes array */
+    ExFreePool(Nodes);
+
+    /* get pin category */
+    Pin.PinId = PinId;
+    Pin.Reserved = 0;
+    Pin.Property.Flags = KSPROPERTY_TYPE_GET;
+    Pin.Property.Set = KSPROPSETID_Pin;
+    Pin.Property.Id = KSPROPERTY_PIN_CATEGORY;
+
+    /* try get pin category */
+    Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (LPVOID)&NodeType, sizeof(GUID), &BytesReturned);
+    if (NT_SUCCESS(Status))
+    {
+        //FIXME
+        //map component type
+    }
+
+    /* retrieve pin name */
+    Pin.PinId = PinId;
+    Pin.Reserved = 0;
+    Pin.Property.Flags = KSPROPERTY_TYPE_GET;
+    Pin.Property.Set = KSPROPSETID_Pin;
+    Pin.Property.Id = KSPROPERTY_PIN_NAME;
+
+    /* try get pin name size */
+    Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
+
+    if (Status != STATUS_MORE_ENTRIES)
+    {
+        SrcLine->Line.szShortName[0] = L'\0';
+        SrcLine->Line.szName[0] = L'\0';
+    }
+    else
+    {
+        PinName = (LPWSTR)ExAllocatePool(NonPagedPool, BytesReturned);
+        if (PinName)
+        {
+            /* try get pin name */
+            Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (LPVOID)PinName, BytesReturned, &BytesReturned);
+
+            if (NT_SUCCESS(Status))
+            {
+                RtlMoveMemory(SrcLine->Line.szShortName, PinName, (min(MIXER_SHORT_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
+                SrcLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
+
+                RtlMoveMemory(SrcLine->Line.szName, PinName, (min(MIXER_LONG_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
+                SrcLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
+            }
+            ExFreePool(PinName);
+        }
+    }
+
+    /* insert src line */
+    if (!bTargetPin)
+    {
+        InsertTailList(&MixerInfo->LineList, &SrcLine->Entry);
+        DstLine->Line.cConnections++;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+AddMixerSourceLines(
+    IN OUT LPMIXER_INFO MixerInfo,
+    IN PFILE_OBJECT FileObject,
+    IN PKSMULTIPLE_ITEM NodeConnections,
+    IN PKSMULTIPLE_ITEM NodeTypes,
+    IN ULONG DeviceIndex,
+    IN ULONG PinsCount,
+    IN ULONG BridgePinIndex,
+    IN ULONG TargetPinIndex,
+    IN PULONG Pins)
+{
+    ULONG Index;
+    NTSTATUS Status = STATUS_SUCCESS;
+
+    for(Index = PinsCount; Index > 0; Index--)
+    {
+        if (Pins[Index-1])
+        {
+            AddMixerSourceLine(MixerInfo, FileObject, NodeConnections, NodeTypes, DeviceIndex, Index-1, (Index -1 == BridgePinIndex), (Index -1 == TargetPinIndex));
+        }
+    }
+    return Status;
+}
+
+
+
+NTSTATUS
+HandlePhysicalConnection(
+    IN OUT LPMIXER_INFO MixerInfo,
+    IN PDEVICE_OBJECT DeviceObject,
+    IN ULONG bInput,
+    IN PKSPIN_PHYSICALCONNECTION OutConnection)
+{
+    PULONG PinsRef = NULL, PinConnectionIndex = NULL, PinsSrcRef;
+    ULONG PinsRefCount, Index, PinConnectionIndexCount, DeviceIndex;
+    NTSTATUS Status;
+    HANDLE hDevice = NULL;
+    PFILE_OBJECT FileObject = NULL;
+    PKSMULTIPLE_ITEM NodeTypes = NULL;
+    PKSMULTIPLE_ITEM NodeConnections = NULL;
+    PULONG MixerControls;
+    ULONG MixerControlsCount;
+
+
+    /* open the connected filter */
+    Status = OpenDevice(OutConnection->SymbolicLinkName, &hDevice, &FileObject);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("OpenDevice failed with %x\n", Status);
+        return Status;
+    }
+
+    /* get device index */
+    DeviceIndex = GetDeviceIndexFromPnpName(DeviceObject, OutConnection->SymbolicLinkName);
+
+    /* get connected filter pin count */
+    PinsRefCount = GetPinCount(FileObject);
+    ASSERT(PinsRefCount);
+
+    PinsRef = AllocatePinArray(PinsRefCount);
+    if (!PinsRef)
+    {
+        /* no memory */
+        Status = STATUS_INSUFFICIENT_RESOURCES;
+        goto cleanup;
+    }
+
+    /* get topology node types */
+    Status = GetFilterNodeProperty(FileObject, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("GetFilterNodeProperty failed with %x\n", Status);
+        goto cleanup;
+    }
+
+    /* get topology connections */
+    Status = GetFilterNodeProperty(FileObject, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections);
     if (!NT_SUCCESS(Status))
     {
-        DPRINT1("Failed to get device name %x\n", Status);
-        return Status;
+        DPRINT1("GetFilterNodeProperty failed with %x\n", Status);
+        goto cleanup;
+    }
+    /*  gets connection index of the bridge pin which connects to a node */
+    DPRINT("Pin %u\n", OutConnection->Pin);
+    Status = GetNodeIndexes(NodeConnections, OutConnection->Pin, FALSE, !bInput, &PinConnectionIndexCount, &PinConnectionIndex);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("GetNodeIndexes failed with %x\n", Status);
+        goto cleanup;
+    }
+
+    /* there should be no split in the bride pin */
+    ASSERT(PinConnectionIndexCount == 1);
+
+    /* find all target pins of this connection */
+    Status = GetTargetPinsByNodeConnectionIndex(NodeConnections, NodeTypes, FALSE, PinConnectionIndex[0], PinsRef);
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("GetTargetPinsByNodeConnectionIndex failed with %x\n", Status);
+        goto cleanup;
+    }
+
+    for(Index = 0; Index < PinsRefCount; Index++)
+    {
+        if (PinsRef[Index])
+        {
+
+            /* found a target pin, now get all references */
+            Status = GetNodeIndexes(NodeConnections, Index, FALSE, FALSE, &MixerControlsCount, &MixerControls);
+            if (!NT_SUCCESS(Status))
+                break;
+
+            /* sanity check */
+            ASSERT(MixerControlsCount == 1);
+
+
+            PinsSrcRef = AllocatePinArray(PinsRefCount);
+            if (!PinsSrcRef)
+            {
+                /* no memory */
+                ExFreePool(MixerControls);
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto cleanup;
+            }
+            /* now get all connected source pins */
+            Status = GetTargetPinsByNodeConnectionIndex(NodeConnections, NodeTypes, TRUE, MixerControls[0], PinsSrcRef);
+            if (!NT_SUCCESS(Status))
+            {
+                /* no memory */
+                ExFreePool(MixerControls);
+                ExFreePool(PinsSrcRef);
+                Status = STATUS_INSUFFICIENT_RESOURCES;
+                goto cleanup;
+            }
+
+            /* add pins from target line */
+            if (!bInput)
+            {
+                // dont add bridge pin for input mixers
+                PinsSrcRef[Index] = TRUE;
+                PinsSrcRef[OutConnection->Pin] = TRUE;
+            }
+
+            Status = AddMixerSourceLines(MixerInfo, FileObject, NodeConnections, NodeTypes, DeviceIndex, PinsRefCount, OutConnection->Pin, Index, PinsSrcRef);
+
+            ExFreePool(MixerControls);
+            ExFreePool(PinsSrcRef);
+        }
+    }
+
+cleanup:
+
+    if (PinsRef)
+        ExFreePool(PinsRef);
+
+    if (NodeConnections)
+        ExFreePool(NodeConnections);
+
+    if (NodeTypes)
+        ExFreePool(NodeTypes);
+
+    if (FileObject)
+        ObDereferenceObject(FileObject);
+
+    if (hDevice)
+        ZwClose(hDevice);
+
+    if (PinConnectionIndex)
+        ExFreePool(PinConnectionIndex);
+
+
+    return Status;
+}
+
+
+
+NTSTATUS
+InitializeMixer(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN ULONG DeviceIndex,
+    IN OUT LPMIXER_INFO MixerInfo,
+    IN HANDLE hDevice,
+    IN PFILE_OBJECT FileObject,
+    IN ULONG PinCount,
+    IN PKSMULTIPLE_ITEM NodeTypes,
+    IN PKSMULTIPLE_ITEM NodeConnections,
+    IN ULONG NodeIndex,
+    IN ULONG bInput)
+{
+    WCHAR Buffer[100];
+    LPWSTR Device;
+    NTSTATUS Status;
+    PULONG Pins;
+    ULONG Index;
+    PKSPIN_PHYSICALCONNECTION OutConnection;
+    LPMIXERLINE_EXT DestinationLine;
+
+    DestinationLine = ExAllocatePool(NonPagedPool, sizeof(MIXERLINE_EXT));
+    if (!DestinationLine)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    /* intialize mixer caps */
+    MixerInfo->MixCaps.wMid = MM_MICROSOFT; //FIXME
+    MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; //FIXME
+    MixerInfo->MixCaps.vDriverVersion = 1; //FIXME
+    MixerInfo->MixCaps.fdwSupport = 0;
+    MixerInfo->MixCaps.cDestinations = 1;
+
+    /* get target pnp name */
+    Status = GetSysAudioDevicePnpName(DeviceObject, DeviceIndex, &Device);
+    if (NT_SUCCESS(Status))
+    {
+        /* find product name */
+        Status = FindProductName(Device, sizeof(Buffer) / sizeof(WCHAR), Buffer);
+        if (NT_SUCCESS(Status))
+        {
+            if (bInput)
+                wcscat(Buffer, L" Input");
+            else
+                wcscat(Buffer, L" output");
+            RtlMoveMemory(MixerInfo->MixCaps.szPname, Buffer, min(MAXPNAMELEN, wcslen(Buffer)+1) * sizeof(WCHAR));
+            MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] = L'\0';
+        }
+        ExFreePool(Device);
+    }
+
+    /* initialize mixer destination line */
+    RtlZeroMemory(DestinationLine, sizeof(MIXERLINEW));
+    DestinationLine->Line.cbStruct = sizeof(MIXERLINEW);
+    DestinationLine->Line.dwSource = MAXULONG;
+    DestinationLine->Line.dwLineID = DESTINATION_LINE;
+    DestinationLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
+    DestinationLine->Line.dwUser = 0;
+    DestinationLine->Line.dwComponentType = (bInput == 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS : MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
+    DestinationLine->Line.cChannels = 2; //FIXME
+    wcscpy(DestinationLine->Line.szShortName, L"Summe"); //FIXME
+    wcscpy(DestinationLine->Line.szName, L"Summe"); //FIXME
+    DestinationLine->Line.Target.dwType = (bInput == 0 ? MIXERLINE_TARGETTYPE_WAVEOUT : MIXERLINE_TARGETTYPE_WAVEIN);
+    DestinationLine->Line.Target.dwDeviceID = !bInput;
+    DestinationLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
+    DestinationLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
+    DestinationLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
+    wcscpy(DestinationLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
+
+    /* initialize source line list */
+    InitializeListHead(&MixerInfo->LineList);
+    InitializeListHead(&DestinationLine->LineControlsExtraData);
+
+    /* insert destination line */
+    InsertHeadList(&MixerInfo->LineList, &DestinationLine->Entry);
+
+    Pins = AllocatePinArray(PinCount);
+    if (!Pins)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+    if (bInput)
+    {
+        Status = GetTargetPins(NodeTypes, NodeConnections, NodeIndex, TRUE, Pins, PinCount);
+    }
+    else
+    {
+        Status = GetTargetPins(NodeTypes, NodeConnections, NodeIndex, FALSE, Pins, PinCount);
+    }
+
+    for(Index = 0; Index < PinCount; Index++)
+    {
+        if (Pins[Index])
+        {
+            Status = GetPhysicalConnection(FileObject, Index, &OutConnection);
+            if (NT_SUCCESS(Status))
+            {
+                Status = HandlePhysicalConnection(MixerInfo, DeviceObject, bInput, OutConnection);
+                ExFreePool(OutConnection);
+            }
+        }
     }
+    ExFreePool(Pins);
 
-    DeviceInfo->u.MixCaps.cDestinations = 1; //FIXME
+    return STATUS_SUCCESS;
+}
 
-    Status = FindProductName(Device, sizeof(Buffer) / sizeof(WCHAR), Buffer);
+NTSTATUS
+WdmAudMixerInitialize(
+    IN PDEVICE_OBJECT DeviceObject)
+{
+    ULONG DeviceCount, Index, Count, NodeIndex, PinCount;
+    NTSTATUS Status;
+    HANDLE hDevice;
+    PFILE_OBJECT FileObject;
+    PKSMULTIPLE_ITEM NodeTypes, NodeConnections;
+    PWDMAUD_DEVICE_EXTENSION DeviceExtension;
 
-    /* check for success */
-    if (!NT_SUCCESS(Status))
+    /* get device extension */
+    DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+
+    /* get number of devices */
+    DeviceCount = GetSysAudioDeviceCount(DeviceObject);
+
+    if (!DeviceCount)
     {
-        DeviceInfo->u.MixCaps.szPname[0] = L'\0';
+        /* no audio devices available atm */
+        DeviceExtension->MixerInfoCount = 0;
+        DeviceExtension->MixerInfo = NULL;
+        return STATUS_SUCCESS;
     }
-    else
+
+    /* each virtual audio device can at most have an input + output mixer */
+    DeviceExtension->MixerInfo = ExAllocatePool(NonPagedPool, sizeof(MIXER_INFO) * DeviceCount * 2);
+    if (!DeviceExtension->MixerInfo)
     {
-        if (IsOutputMixer(DeviceObject, DeviceInfo->DeviceIndex))
-        {
-            wcscat(Buffer, L" output");
-        }
-        else
+        /* not enough memory */
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    /* clear mixer info */
+    RtlZeroMemory(DeviceExtension->MixerInfo, sizeof(MIXER_INFO) * DeviceCount * 2);
+
+    Index = 0;
+    Count = 0;
+    do
+    {
+        /* open the virtual audio device */
+        Status = OpenSysAudioDeviceByIndex(DeviceObject, Index, &hDevice, &FileObject);
+
+        if (NT_SUCCESS(Status))
         {
-            wcscat(Buffer, L" Input");
+            /* retrieve all available node types */
+            Status = GetFilterNodeProperty(FileObject, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes);
+            if (!NT_SUCCESS(Status))
+            {
+                ObDereferenceObject(FileObject);
+                ZwClose(hDevice);
+                break;
+            }
+
+            Status = GetFilterNodeProperty(FileObject, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections);
+            if (!NT_SUCCESS(Status))
+            {
+                ObDereferenceObject(FileObject);
+                ZwClose(hDevice);
+                ExFreePool(NodeTypes);
+                break;
+            }
+
+            /* get num of pins */
+            PinCount = GetPinCount(FileObject);
+            /* get the first available dac node index */
+            NodeIndex = GetNodeTypeIndex(NodeTypes, (LPGUID)&KSNODETYPE_DAC);
+            if (NodeIndex != (ULONG)-1)
+            {
+                Status = InitializeMixer(DeviceObject, Index, &DeviceExtension->MixerInfo[Count], hDevice, FileObject, PinCount, NodeTypes, NodeConnections, NodeIndex, FALSE);
+                if (NT_SUCCESS(Status))
+                {
+                    /* increment mixer offset */
+                    Count++;
+                }
+            }
+
+            /* get the first available adc node index */
+            NodeIndex = GetNodeTypeIndex(NodeTypes, (LPGUID)&KSNODETYPE_ADC);
+            if (NodeIndex != (ULONG)-1)
+            {
+                Status = InitializeMixer(DeviceObject, Index, &DeviceExtension->MixerInfo[Count], hDevice, FileObject, PinCount, NodeTypes, NodeConnections, NodeIndex, TRUE);
+                if (NT_SUCCESS(Status))
+                {
+                    /* increment mixer offset */
+                    Count++;
+                }
+            }
+
+            /* free node connections array */
+            ExFreePool(NodeTypes);
+            ExFreePool(NodeConnections);
+
+            /* close virtual audio device */
+            ObDereferenceObject(FileObject);
+            ZwClose(hDevice);
+
         }
-        RtlMoveMemory(DeviceInfo->u.MixCaps.szPname, Buffer, min(MAXPNAMELEN, wcslen(Buffer)+1) * sizeof(WCHAR));
-        DeviceInfo->u.MixCaps.szPname[MAXPNAMELEN-1] = L'\0';
-    }
+        /* increment virtual audio device index */
+        Index++;
+    }while(Index < DeviceCount);
+
+    /* store mixer count */
+    DeviceExtension->MixerInfoCount = Count;
 
     return Status;
 }
 
 
+
+NTSTATUS
+WdmAudMixerCapabilities(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN  PWDMAUD_DEVICE_INFO DeviceInfo,
+    IN  PWDMAUD_CLIENT ClientInfo,
+    IN PWDMAUD_DEVICE_EXTENSION DeviceExtension)
+{
+    if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
+    {
+        /* invalid parameter */
+        return STATUS_INVALID_PARAMETER;
+    }
+
+    /* copy cached mixer caps */
+    RtlMoveMemory(&DeviceInfo->u.MixCaps, &DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice].MixCaps, sizeof(MIXERCAPSW));
+
+    return STATUS_SUCCESS;
+}
+
+
 NTSTATUS
 WdmAudControlOpenMixer(
     IN  PDEVICE_OBJECT DeviceObject,
@@ -391,10 +1700,14 @@ WdmAudControlOpenMixer(
 {
     ULONG Index;
     PWDMAUD_HANDLE Handels;
+    PWDMAUD_DEVICE_EXTENSION DeviceExtension;
 
     DPRINT("WdmAudControlOpenMixer\n");
 
-    if (DeviceInfo->DeviceIndex >= GetNumOfMixerDevices(DeviceObject))
+    DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+
+    if (DeviceInfo->DeviceIndex >= DeviceExtension->MixerInfoCount)
     {
         /* mixer index doesnt exist */
         return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, 0);
@@ -442,7 +1755,78 @@ WdmAudGetLineInfo(
     IN  PWDMAUD_DEVICE_INFO DeviceInfo,
     IN  PWDMAUD_CLIENT ClientInfo)
 {
+    PWDMAUD_DEVICE_EXTENSION DeviceExtension;
+    LPMIXERLINE_EXT MixerLineSrc;
+
+    /* get device extension */
+    DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+    if (DeviceInfo->Flags == MIXER_GETLINEINFOF_DESTINATION)
+    {
+        if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
+        {
+            /* invalid parameter */
+            return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
+        }
+
+        if (DeviceInfo->u.MixLine.dwDestination != 0)
+        {
+            /* invalid parameter */
+            return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
+        }
+        MixerLineSrc = GetSourceMixerLineByLineId(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DESTINATION_LINE);
+        ASSERT(MixerLineSrc);
+
+        /* copy cached data */
+        RtlCopyMemory(&DeviceInfo->u.MixLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
+        return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
+    }
+    else if (DeviceInfo->Flags == MIXER_GETLINEINFOF_SOURCE)
+    {
+        if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
+        {
+            /* invalid parameter */
+            return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
+        }
+
+        MixerLineSrc = GetSourceMixerLineByLineId(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DESTINATION_LINE);
+        ASSERT(MixerLineSrc);
+
+        if (DeviceInfo->u.MixLine.dwSource >= MixerLineSrc->Line.cConnections)
+        {
+            DPRINT1("dwSource %u Destinations %u\n", DeviceInfo->u.MixLine.dwSource, MixerLineSrc->Line.cConnections);
+            /* invalid parameter */
+            return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
+        }
+
+        MixerLineSrc = GetSourceMixerLine(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DeviceInfo->u.MixLine.dwSource);
+        if (MixerLineSrc)
+        {
+            DPRINT("Line %u Name %S\n", MixerLineSrc->Line.dwSource, MixerLineSrc->Line.szName);
+            RtlCopyMemory(&DeviceInfo->u.MixLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
+        }
+        return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
+    }
+    else if (DeviceInfo->Flags == MIXER_GETLINEINFOF_LINEID)
+    {
+        if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
+        {
+            /* invalid parameter */
+            return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
+        }
+
+        MixerLineSrc = GetSourceMixerLineByLineId(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DeviceInfo->u.MixLine.dwLineID);
+        ASSERT(MixerLineSrc);
+
+        /* copy cached data */
+        RtlCopyMemory(&DeviceInfo->u.MixLine, &MixerLineSrc->Line, sizeof(MIXERLINEW));
+        return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
+    }
+
+
+    DPRINT1("Flags %x\n", DeviceInfo->Flags);
     UNIMPLEMENTED;
+
     //DbgBreakPoint();
     return SetIrpIoStatus(Irp, STATUS_NOT_IMPLEMENTED, 0);
 
@@ -456,12 +1840,221 @@ WdmAudGetLineControls(
     IN  PWDMAUD_DEVICE_INFO DeviceInfo,
     IN  PWDMAUD_CLIENT ClientInfo)
 {
+    LPMIXERLINE_EXT MixerLineSrc;
+    PWDMAUD_DEVICE_EXTENSION DeviceExtension;
+    ULONG Index;
+
+    /* get device extension */
+    DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+    if (DeviceInfo->Flags == MIXER_GETLINECONTROLSF_ALL)
+    {
+        if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
+        {
+            /* invalid parameter */
+            return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
+        }
+
+        MixerLineSrc = GetSourceMixerLineByLineId(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DeviceInfo->u.MixControls.dwLineID);
+        ASSERT(MixerLineSrc);
+        if (MixerLineSrc)
+        {
+            RtlMoveMemory(DeviceInfo->u.MixControls.pamxctrl, MixerLineSrc->LineControls, min(MixerLineSrc->Line.cControls, DeviceInfo->u.MixControls.cControls) * sizeof(MIXERCONTROLW));
+        }
+        return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
+    }
+    else if (DeviceInfo->Flags == MIXER_GETLINECONTROLSF_ONEBYTYPE)
+    {
+        if ((ULONG)DeviceInfo->hDevice >= DeviceExtension->MixerInfoCount)
+        {
+            /* invalid parameter */
+            return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
+        }
+
+        MixerLineSrc = GetSourceMixerLineByLineId(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DeviceInfo->u.MixControls.dwLineID);
+        ASSERT(MixerLineSrc);
+
+        Index = 0;
+        for(Index = 0; Index < MixerLineSrc->Line.cControls; Index++)
+        {
+            DPRINT1("dwControlType %x\n", MixerLineSrc->LineControls[Index].dwControlType);
+            if (DeviceInfo->u.MixControls.dwControlType == MixerLineSrc->LineControls[Index].dwControlType)
+            {
+                RtlMoveMemory(DeviceInfo->u.MixControls.pamxctrl, &MixerLineSrc->LineControls[Index], sizeof(MIXERCONTROLW));
+                return SetIrpIoStatus(Irp, STATUS_SUCCESS, sizeof(WDMAUD_DEVICE_INFO));
+            }
+        }
+        DPRINT1("DeviceInfo->u.MixControls.dwControlType %x not found in Line %x cControls %u \n", DeviceInfo->u.MixControls.dwControlType, DeviceInfo->u.MixControls.dwLineID, MixerLineSrc->Line.cControls);
+        return SetIrpIoStatus(Irp, STATUS_UNSUCCESSFUL, sizeof(WDMAUD_DEVICE_INFO));
+    }
+
     UNIMPLEMENTED;
     //DbgBreakPoint();
     return SetIrpIoStatus(Irp, STATUS_NOT_IMPLEMENTED, 0);
 
 }
 
+NTSTATUS
+SetGetControlDetails(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN ULONG DeviceId,
+    IN ULONG NodeId,
+    IN PWDMAUD_DEVICE_INFO DeviceInfo,
+    IN ULONG bSet,
+    IN ULONG PropertyId,
+    IN ULONG Channel,
+    IN PLONG InputValue)
+{
+    KSNODEPROPERTY_AUDIO_CHANNEL Property;
+    NTSTATUS Status;
+    HANDLE hDevice;
+    PFILE_OBJECT FileObject;
+    LONG Value;
+    ULONG BytesReturned;
+
+    if (bSet)
+        Value = *InputValue;
+
+    /* open virtual audio device */
+    Status = OpenSysAudioDeviceByIndex(DeviceObject, DeviceId, &hDevice, &FileObject);
+
+    if (!NT_SUCCESS(Status))
+    {
+        /* failed */
+        return Status;
+    }
+
+    /* setup the request */
+    RtlZeroMemory(&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL));
+
+    Property.NodeProperty.NodeId = NodeId;
+    Property.NodeProperty.Property.Id = PropertyId;
+    Property.NodeProperty.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY;
+    Property.NodeProperty.Property.Set = KSPROPSETID_Audio;
+    Property.Channel = Channel;
+
+    if (bSet)
+        Property.NodeProperty.Property.Flags |= KSPROPERTY_TYPE_SET;
+    else
+        Property.NodeProperty.Property.Flags |= KSPROPERTY_TYPE_GET;
+
+    /* send the request */
+    Status = KsSynchronousIoControlDevice(FileObject, KernelMode, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), (PVOID)&Value, sizeof(LONG), &BytesReturned);
+
+    ObDereferenceObject(FileObject);
+    ZwClose(hDevice);
+
+    if (!bSet)
+    {
+        *InputValue = Value;
+    }
+
+    DPRINT1("Status %x bSet %u NodeId %u Value %d PropertyId %u\n", Status, bSet, NodeId, Value, PropertyId);
+    return Status;
+}
+
+NTSTATUS
+SetGetMuteControlDetails(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN ULONG DeviceId,
+    IN ULONG NodeId,
+    IN PWDMAUD_DEVICE_INFO DeviceInfo,
+    IN ULONG bSet)
+{
+    LPMIXERCONTROLDETAILS_BOOLEAN Input;
+    LONG Value;
+    NTSTATUS Status;
+
+    if (DeviceInfo->u.MixDetails.cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
+        return STATUS_INVALID_PARAMETER;
+
+    /* get input */
+    Input = (LPMIXERCONTROLDETAILS_BOOLEAN)DeviceInfo->u.MixDetails.paDetails;
+
+    /* FIXME SEH */
+    if (bSet)
+        Value = Input->fValue;
+
+    /* set control details */
+    Status = SetGetControlDetails(DeviceObject, DeviceId, NodeId, DeviceInfo, bSet, KSPROPERTY_AUDIO_MUTE, MAXULONG, &Value);
+
+    /* FIXME SEH */
+    if (!bSet)
+        Input->fValue = Value;
+
+    return Status;
+}
+
+NTSTATUS
+SetGetVolumeControlDetails(
+    IN PDEVICE_OBJECT DeviceObject,
+    IN ULONG DeviceId,
+    IN ULONG NodeId,
+    IN PWDMAUD_DEVICE_INFO DeviceInfo,
+    IN ULONG bSet,
+    LPMIXERCONTROLW MixerControl,
+    LPMIXERLINE_EXT MixerLine)
+{
+    LPMIXERCONTROLDETAILS_UNSIGNED Input;
+    LONG Value, Index, Channel = 0;
+    NTSTATUS Status;
+    LPMIXERVOLUME_DATA VolumeData;
+
+    if (DeviceInfo->u.MixDetails.cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
+        return STATUS_INVALID_PARAMETER;
+
+    VolumeData = (LPMIXERVOLUME_DATA)GetMixerControlDataById(&MixerLine->LineControlsExtraData, MixerControl->dwControlID);
+    if (!VolumeData)
+        return STATUS_INSUFFICIENT_RESOURCES;
+
+
+    /* get input */
+    Input = (LPMIXERCONTROLDETAILS_UNSIGNED)DeviceInfo->u.MixDetails.paDetails;
+
+    if (bSet)
+    {
+        /* FIXME SEH */
+        Value = Input->dwValue;
+        Index = Value / VolumeData->InputSteppingDelta;
+
+        if (Index >= VolumeData->ValuesCount)
+        {
+            DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
+            DbgBreakPoint();
+            return STATUS_INVALID_PARAMETER;
+        }
+
+        Value = VolumeData->Values[Index];
+    }
+
+    /* set control details */
+    if (bSet)
+    {
+        Status = SetGetControlDetails(DeviceObject, DeviceId, NodeId, DeviceInfo, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
+        Status = SetGetControlDetails(DeviceObject, DeviceId, NodeId, DeviceInfo, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
+    }
+    else
+    {
+        Status = SetGetControlDetails(DeviceObject, DeviceId, NodeId, DeviceInfo, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
+    }
+
+    if (!bSet)
+    {
+        for(Index = 0; Index < VolumeData->ValuesCount; Index++)
+        {
+            if (VolumeData->Values[Index] < Value)
+            {
+                /* FIXME SEH */
+                Input->dwValue = VolumeData->InputSteppingDelta * Index;
+                return Status;
+            }
+        }
+        Input->dwValue = VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
+    }
+
+    return Status;
+}
+
 NTSTATUS
 NTAPI
 WdmAudSetControlDetails(
@@ -470,9 +2063,45 @@ WdmAudSetControlDetails(
     IN  PWDMAUD_DEVICE_INFO DeviceInfo,
     IN  PWDMAUD_CLIENT ClientInfo)
 {
-    UNIMPLEMENTED;
-    //DbgBreakPoint();
-    return SetIrpIoStatus(Irp, STATUS_NOT_IMPLEMENTED, 0);
+    LPMIXERLINE_EXT MixerLine;
+    LPMIXERCONTROLW MixerControl;
+    ULONG NodeId;
+    PWDMAUD_DEVICE_EXTENSION DeviceExtension;
+    NTSTATUS Status;
+
+    DPRINT("cbStruct %u Expected %u dwControlID %u cChannels %u cMultipleItems %u cbDetails %u paDetails %p Flags %x\n", 
+            DeviceInfo->u.MixDetails.cbStruct, sizeof(MIXERCONTROLDETAILS), DeviceInfo->u.MixDetails.dwControlID, DeviceInfo->u.MixDetails.cChannels, DeviceInfo->u.MixDetails.cMultipleItems, DeviceInfo->u.MixDetails.cbDetails, DeviceInfo->u.MixDetails.paDetails, DeviceInfo->Flags);
+
+    if (DeviceInfo->Flags & MIXER_GETCONTROLDETAILSF_LISTTEXT)
+    {
+        UNIMPLEMENTED;
+        return SetIrpIoStatus(Irp, STATUS_NOT_IMPLEMENTED, 0);
+    }
+
+    /* get device extension */
+    DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+    /* get mixer control */
+     Status = GetMixerControlById(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DeviceInfo->u.MixDetails.dwControlID, &MixerLine, &MixerControl, &NodeId);
+
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("MixerControl %x not found\n", DeviceInfo->u.MixDetails.dwControlID);
+        return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
+    }
+
+    Status = STATUS_NOT_IMPLEMENTED;
+    DPRINT("dwLineId %x dwControlID %x dwControlType %x\n", MixerLine->Line.dwLineID, MixerControl->dwControlID, MixerControl->dwControlType);
+    if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
+    {
+        /* send the request */
+        Status = SetGetMuteControlDetails(DeviceObject, MixerLine->DeviceIndex, NodeId, DeviceInfo, TRUE);
+    }
+    else if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
+    {
+        Status = SetGetVolumeControlDetails(DeviceObject, MixerLine->DeviceIndex, NodeId, DeviceInfo, TRUE, MixerControl, MixerLine);
+    }
+    return SetIrpIoStatus(Irp, Status, sizeof(WDMAUD_DEVICE_INFO));
 
 }
 
@@ -484,9 +2113,45 @@ WdmAudGetControlDetails(
     IN  PWDMAUD_DEVICE_INFO DeviceInfo,
     IN  PWDMAUD_CLIENT ClientInfo)
 {
-    UNIMPLEMENTED;
-    //DbgBreakPoint();
-    return SetIrpIoStatus(Irp, STATUS_NOT_IMPLEMENTED, 0);
+    LPMIXERLINE_EXT MixerLine;
+    LPMIXERCONTROLW MixerControl;
+    ULONG NodeId;
+    PWDMAUD_DEVICE_EXTENSION DeviceExtension;
+    NTSTATUS Status;
+
+    DPRINT("cbStruct %u Expected %u dwControlID %u cChannels %u cMultipleItems %u cbDetails %u paDetails %p Flags %x\n", 
+            DeviceInfo->u.MixDetails.cbStruct, sizeof(MIXERCONTROLDETAILS), DeviceInfo->u.MixDetails.dwControlID, DeviceInfo->u.MixDetails.cChannels, DeviceInfo->u.MixDetails.cMultipleItems, DeviceInfo->u.MixDetails.cbDetails, DeviceInfo->u.MixDetails.paDetails, DeviceInfo->Flags);
+
+    if (DeviceInfo->Flags & MIXER_GETCONTROLDETAILSF_LISTTEXT)
+    {
+        UNIMPLEMENTED;
+        return SetIrpIoStatus(Irp, STATUS_NOT_IMPLEMENTED, 0);
+    }
+
+    /* get device extension */
+    DeviceExtension = (PWDMAUD_DEVICE_EXTENSION)DeviceObject->DeviceExtension;
+
+    /* get mixer control */
+     Status = GetMixerControlById(&DeviceExtension->MixerInfo[(ULONG)DeviceInfo->hDevice], DeviceInfo->u.MixDetails.dwControlID, &MixerLine, &MixerControl, &NodeId);
+
+    if (!NT_SUCCESS(Status))
+    {
+        DPRINT1("MixerControl %x not found\n", DeviceInfo->u.MixDetails.dwControlID);
+        return SetIrpIoStatus(Irp, STATUS_INVALID_PARAMETER, 0);
+    }
+
+    Status = STATUS_NOT_IMPLEMENTED;
+    if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
+    {
+        /* send the request */
+        Status = SetGetMuteControlDetails(DeviceObject, MixerLine->DeviceIndex, NodeId, DeviceInfo, FALSE);
+    }
+    else if (MixerControl->dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
+    {
+        Status = SetGetVolumeControlDetails(DeviceObject, MixerLine->DeviceIndex, NodeId, DeviceInfo, FALSE, MixerControl, MixerLine);
+    }
+
+    return SetIrpIoStatus(Irp, Status, sizeof(WDMAUD_DEVICE_INFO));
 
 }