xref: /reactos/sdk/lib/drivers/sound/mmixer/controls.c (revision 84344399)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Kernel Streaming
4  * FILE:            lib/drivers/sound/mmixer/controls.c
5  * PURPOSE:         Mixer Control Iteration Functions
6  * PROGRAMMER:      Johannes Anderwald
7  */
8 
9 #include "precomp.h"
10 
11 // #define NDEBUG
12 #include <debug.h>
13 
14 const GUID KSNODETYPE_DESKTOP_MICROPHONE = {0xDFF21BE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
15 const GUID KSNODETYPE_LEGACY_AUDIO_CONNECTOR = {0xDFF21FE4, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
16 const GUID KSNODETYPE_TELEPHONE = {0xDFF21EE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
17 const GUID KSNODETYPE_PHONE_LINE = {0xDFF21EE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
18 const GUID KSNODETYPE_DOWN_LINE_PHONE = {0xDFF21EE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
19 const GUID KSNODETYPE_DESKTOP_SPEAKER = {0xDFF21CE4, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
20 const GUID KSNODETYPE_ROOM_SPEAKER = {0xDFF21CE5, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
21 const GUID KSNODETYPE_COMMUNICATION_SPEAKER = {0xDFF21CE6, 0xF70F, 0x11D0, {0xB9,0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
22 const GUID KSNODETYPE_HEADPHONES = {0xDFF21CE2, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
23 const GUID KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO = {0xDFF21CE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
24 const GUID KSNODETYPE_MICROPHONE = {0xDFF21BE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9,0x22, 0x31, 0x96}};
25 const GUID KSCATEGORY_AUDIO = {0x6994AD04L, 0x93EF, 0x11D0, {0xA3, 0xCC, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
26 const GUID KSNODETYPE_SPDIF_INTERFACE = {0xDFF21FE5, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
27 const GUID KSNODETYPE_ANALOG_CONNECTOR = {0xDFF21FE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
28 const GUID KSNODETYPE_SPEAKER = {0xDFF21CE1, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
29 const GUID KSNODETYPE_CD_PLAYER = {0xDFF220E3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
30 const GUID KSNODETYPE_SYNTHESIZER = {0xDFF220F3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
31 const GUID KSNODETYPE_LINE_CONNECTOR = {0xDFF21FE3, 0xF70F, 0x11D0, {0xB9, 0x17, 0x00, 0xA0,0xC9, 0x22, 0x31, 0x96}};
32 const GUID PINNAME_VIDEO_CAPTURE  = {0xfb6c4281, 0x353, 0x11d1, {0x90, 0x5f, 0x0, 0x0, 0xc0, 0xcc, 0x16, 0xba}};
33 
34 MIXER_STATUS
35 MMixerAddMixerControl(
36     IN PMIXER_CONTEXT MixerContext,
37     IN LPMIXER_INFO MixerInfo,
38     IN HANDLE hMixer,
39     IN PTOPOLOGY Topology,
40     IN ULONG NodeIndex,
41     IN LPMIXERLINE_EXT MixerLine,
42     IN ULONG MaxChannels)
43 {
44     LPGUID NodeType;
45     KSP_NODE Node;
46     ULONG BytesReturned;
47     MIXER_STATUS Status;
48     LPWSTR Name;
49     LPMIXERCONTROL_EXT MixerControl;
50 
51     /* allocate mixer control */
52     MixerControl = MixerContext->Alloc(sizeof(MIXERCONTROL_EXT));
53     if (!MixerControl)
54     {
55         /* no memory */
56         return MM_STATUS_NO_MEMORY;
57     }
58 
59     /* initialize mixer control */
60     MixerControl->hDevice = hMixer;
61     MixerControl->NodeID = NodeIndex;
62     MixerControl->ExtraData = NULL;
63 
64     MixerControl->Control.cbStruct = sizeof(MIXERCONTROLW);
65     MixerControl->Control.dwControlID = MixerInfo->ControlId;
66 
67     /* get node type */
68     NodeType = MMixerGetNodeTypeFromTopology(Topology, NodeIndex);
69     /* store control type */
70     MixerControl->Control.dwControlType = MMixerGetControlTypeFromTopologyNode(NodeType);
71 
72     MixerControl->Control.fdwControl = (MaxChannels > 1 ? 0 : MIXERCONTROL_CONTROLF_UNIFORM);
73     MixerControl->Control.cMultipleItems = 0;
74 
75     /* setup request to retrieve name */
76     Node.NodeId = NodeIndex;
77     Node.Property.Id = KSPROPERTY_TOPOLOGY_NAME;
78     Node.Property.Flags = KSPROPERTY_TYPE_GET;
79     Node.Property.Set = KSPROPSETID_Topology;
80     Node.Reserved = 0;
81 
82     /* get node name size */
83     Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), NULL, 0, &BytesReturned);
84 
85     if (Status == MM_STATUS_MORE_ENTRIES)
86     {
87         ASSERT(BytesReturned != 0);
88         Name = (LPWSTR)MixerContext->Alloc(BytesReturned);
89         if (!Name)
90         {
91             /* not enough memory */
92             return MM_STATUS_NO_MEMORY;
93         }
94 
95         /* get node name */
96         Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Node, sizeof(KSP_NODE), (LPVOID)Name, BytesReturned, &BytesReturned);
97 
98         if (Status == MM_STATUS_SUCCESS)
99         {
100             MixerContext->Copy(MixerControl->Control.szShortName, Name, (min(MIXER_SHORT_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
101             MixerControl->Control.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
102 
103             MixerContext->Copy(MixerControl->Control.szName, Name, (min(MIXER_LONG_NAME_CHARS, wcslen(Name)+1)) * sizeof(WCHAR));
104             MixerControl->Control.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
105         }
106 
107         /* free name buffer */
108         MixerContext->Free(Name);
109     }
110 
111     /* increment control count */
112     MixerInfo->ControlId++;
113 
114     /* insert control */
115     InsertTailList(&MixerLine->ControlsList, &MixerControl->Entry);
116 
117     if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUX)
118     {
119         ULONG NodesCount;
120         PULONG Nodes;
121 
122         /* allocate topology nodes array */
123         Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes);
124 
125         if (Status != MM_STATUS_SUCCESS)
126         {
127             /* out of memory */
128             return STATUS_NO_MEMORY;
129         }
130 
131         /* get connected node count */
132         MMixerGetNextNodesFromNodeIndex(MixerContext, Topology, NodeIndex, TRUE, &NodesCount, Nodes);
133 
134         /* TODO */
135         MixerContext->Free(Nodes);
136 
137         /* setup mux bounds */
138         MixerControl->Control.Bounds.dwMinimum = 0;
139         MixerControl->Control.Bounds.dwMaximum = NodesCount - 1;
140         MixerControl->Control.Metrics.dwReserved[0] = NodesCount;
141         MixerControl->Control.cMultipleItems = NodesCount;
142         MixerControl->Control.fdwControl |= MIXERCONTROL_CONTROLF_UNIFORM | MIXERCONTROL_CONTROLF_MULTIPLE;
143     }
144     else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
145     {
146         MixerControl->Control.Bounds.dwMinimum = 0;
147         MixerControl->Control.Bounds.dwMaximum = 1;
148     }
149     else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_ONOFF)
150     {
151         /* only needs to set bounds */
152         MixerControl->Control.Bounds.dwMinimum = 0;
153         MixerControl->Control.Bounds.dwMaximum = 1;
154     }
155     else if (MixerControl->Control.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
156     {
157         KSNODEPROPERTY_AUDIO_CHANNEL Property;
158         ULONG Length;
159         PKSPROPERTY_DESCRIPTION Desc;
160         PKSPROPERTY_MEMBERSHEADER Members;
161         PKSPROPERTY_STEPPING_LONG Range;
162 
163         MixerControl->Control.Bounds.dwMinimum = 0;
164         MixerControl->Control.Bounds.dwMaximum = 0xFFFF;
165         MixerControl->Control.Metrics.cSteps = 0xC0; /* FIXME */
166 
167         Length = sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) + sizeof(KSPROPERTY_STEPPING_LONG);
168         Desc = (PKSPROPERTY_DESCRIPTION)MixerContext->Alloc(Length);
169         ASSERT(Desc);
170 
171         /* setup the request */
172         RtlZeroMemory(&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL));
173 
174         Property.NodeProperty.NodeId = NodeIndex;
175         Property.NodeProperty.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL;
176         Property.NodeProperty.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
177         Property.NodeProperty.Property.Set = KSPROPSETID_Audio;
178 
179         /* get node volume level info */
180         Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), Desc, Length, &BytesReturned);
181 
182         if (Status == MM_STATUS_SUCCESS)
183         {
184             LPMIXERVOLUME_DATA VolumeData;
185             ULONG Steps, MaxRange, Index;
186             LONG Value;
187 
188             Members = (PKSPROPERTY_MEMBERSHEADER)(Desc + 1);
189             Range = (PKSPROPERTY_STEPPING_LONG)(Members + 1);
190 
191             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);
192 
193             MaxRange = Range->Bounds.UnsignedMaximum  - Range->Bounds.UnsignedMinimum;
194 
195             if (MaxRange)
196             {
197                 ASSERT(MaxRange);
198                 VolumeData = (LPMIXERVOLUME_DATA)MixerContext->Alloc(sizeof(MIXERVOLUME_DATA));
199                 if (!VolumeData)
200                     return MM_STATUS_NO_MEMORY;
201 
202                 Steps = MaxRange / Range->SteppingDelta + 1;
203 
204                 /* store mixer control info there */
205                 VolumeData->Header.dwControlID = MixerControl->Control.dwControlID;
206                 VolumeData->SignedMaximum = Range->Bounds.SignedMaximum;
207                 VolumeData->SignedMinimum = Range->Bounds.SignedMinimum;
208                 VolumeData->SteppingDelta = Range->SteppingDelta;
209                 VolumeData->ValuesCount = Steps;
210                 VolumeData->InputSteppingDelta = 0x10000 / Steps;
211 
212                 VolumeData->Values = (PLONG)MixerContext->Alloc(sizeof(LONG) * Steps);
213                 if (!VolumeData->Values)
214                 {
215                     MixerContext->Free(Desc);
216                     MixerContext->Free(VolumeData);
217                     return MM_STATUS_NO_MEMORY;
218                 }
219 
220                 Value = Range->Bounds.SignedMinimum;
221                 for(Index = 0; Index < Steps; Index++)
222                 {
223                     VolumeData->Values[Index] = Value;
224                     Value += Range->SteppingDelta;
225                 }
226                 MixerControl->ExtraData = VolumeData;
227            }
228        }
229        MixerContext->Free(Desc);
230     }
231 
232     DPRINT("Status %x Name %S\n", Status, MixerControl->Control.szName);
233     return MM_STATUS_SUCCESS;
234 }
235 
236 MIXER_STATUS
237 MMixerCreateDestinationLine(
238     IN PMIXER_CONTEXT MixerContext,
239     IN LPMIXER_INFO MixerInfo,
240     IN ULONG bInputMixer,
241     IN LPWSTR LineName)
242 {
243     LPMIXERLINE_EXT DestinationLine;
244 
245     /* allocate a mixer destination line */
246     DestinationLine = (LPMIXERLINE_EXT) MixerContext->Alloc(sizeof(MIXERLINE_EXT));
247     if (!MixerInfo)
248     {
249         /* no memory */
250         return MM_STATUS_NO_MEMORY;
251     }
252 
253     /* initialize mixer destination line */
254     DestinationLine->Line.cbStruct = sizeof(MIXERLINEW);
255     DestinationLine->Line.cChannels = 2; /* FIXME */
256     DestinationLine->Line.cConnections = 0;
257     DestinationLine->Line.cControls = 0;
258     DestinationLine->Line.dwComponentType = (bInputMixer == 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS : MIXERLINE_COMPONENTTYPE_DST_WAVEIN);
259     DestinationLine->Line.dwDestination = MixerInfo->MixCaps.cDestinations;
260     DestinationLine->Line.dwLineID = MixerInfo->MixCaps.cDestinations + DESTINATION_LINE;
261     DestinationLine->Line.dwSource = MAXULONG;
262     DestinationLine->Line.dwUser = 0;
263     DestinationLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE;
264 
265     if (LineName)
266     {
267         MixerContext->Copy(DestinationLine->Line.szShortName, LineName, (min(MIXER_SHORT_NAME_CHARS, wcslen(LineName)+1)) * sizeof(WCHAR));
268         DestinationLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
269 
270         MixerContext->Copy(DestinationLine->Line.szName, LineName, (min(MIXER_LONG_NAME_CHARS, wcslen(LineName)+1)) * sizeof(WCHAR));
271         DestinationLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
272 
273     }
274 
275     DestinationLine->Line.Target.dwType = (bInputMixer == 0 ? MIXERLINE_TARGETTYPE_WAVEOUT : MIXERLINE_TARGETTYPE_WAVEIN);
276     DestinationLine->Line.Target.dwDeviceID = 0; //FIXME
277     DestinationLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
278     DestinationLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
279     DestinationLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
280 
281     ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == 0);
282     wcscpy(DestinationLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
283 
284     /* initialize extra line */
285     InitializeListHead(&DestinationLine->ControlsList);
286 
287     /* insert into mixer info */
288     InsertTailList(&MixerInfo->LineList, &DestinationLine->Entry);
289 
290     /* increment destination count */
291     MixerInfo->MixCaps.cDestinations++;
292 
293     /* done */
294     return MM_STATUS_SUCCESS;
295 }
296 
297 MIXER_STATUS
298 MMixerGetPinName(
299     IN PMIXER_CONTEXT MixerContext,
300     IN LPMIXER_INFO MixerInfo,
301     IN HANDLE hMixer,
302     IN ULONG PinId,
303     IN OUT LPWSTR * OutBuffer)
304 {
305     KSP_PIN Pin;
306     ULONG BytesReturned;
307     LPWSTR Buffer;
308     MIXER_STATUS Status;
309 
310     /* prepare pin */
311     Pin.PinId = PinId;
312     Pin.Reserved = 0;
313     Pin.Property.Flags = KSPROPERTY_TYPE_GET;
314     Pin.Property.Set = KSPROPSETID_Pin;
315     Pin.Property.Id = KSPROPERTY_PIN_NAME;
316 
317     /* try get pin name size */
318     Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), NULL, 0, &BytesReturned);
319 
320     /* check if buffer overflowed */
321     if (Status == MM_STATUS_MORE_ENTRIES)
322     {
323         /* allocate buffer */
324         Buffer = (LPWSTR)MixerContext->Alloc(BytesReturned);
325         if (!Buffer)
326         {
327             /* out of memory */
328             return MM_STATUS_NO_MEMORY;
329         }
330 
331         /* try get pin name */
332         Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Pin, sizeof(KSP_PIN), (PVOID)Buffer, BytesReturned, &BytesReturned);
333         if (Status != MM_STATUS_SUCCESS)
334         {
335             /* failed to get pin name */
336             MixerContext->Free((PVOID)Buffer);
337             return Status;
338         }
339 
340         /* successfully obtained pin name */
341         *OutBuffer = Buffer;
342         return MM_STATUS_SUCCESS;
343     }
344 
345     /* failed to get pin name */
346     return Status;
347 }
348 
349 MIXER_STATUS
350 MMixerBuildMixerDestinationLine(
351     IN PMIXER_CONTEXT MixerContext,
352     IN OUT LPMIXER_INFO MixerInfo,
353     IN HANDLE hMixer,
354     IN ULONG PinId,
355     IN ULONG bInput)
356 {
357     LPWSTR PinName;
358     MIXER_STATUS Status;
359 
360     /* try get pin name */
361     Status = MMixerGetPinName(MixerContext, MixerInfo, hMixer, PinId, &PinName);
362     if (Status == MM_STATUS_SUCCESS)
363     {
364         /* create mixer destination line */
365 
366         Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, PinName);
367 
368         /* free pin name */
369         MixerContext->Free(PinName);
370     }
371     else
372     {
373         /* create mixer destination line unlocalized */
374         Status = MMixerCreateDestinationLine(MixerContext, MixerInfo, bInput, L"No Name");
375     }
376 
377     return Status;
378 }
379 
380 MIXER_STATUS
381 MMixerBuildTopology(
382     IN PMIXER_CONTEXT MixerContext,
383     IN LPMIXER_DATA MixerData,
384     OUT PTOPOLOGY * OutTopology)
385 {
386     ULONG PinsCount;
387     PKSMULTIPLE_ITEM NodeTypes = NULL;
388     PKSMULTIPLE_ITEM NodeConnections = NULL;
389     MIXER_STATUS Status;
390 
391     if (MixerData->Topology)
392     {
393         /* re-use existing topology */
394         *OutTopology = MixerData->Topology;
395 
396         return MM_STATUS_SUCCESS;
397     }
398 
399     /* get connected filter pin count */
400     PinsCount = MMixerGetFilterPinCount(MixerContext, MixerData->hDevice);
401 
402     if (!PinsCount)
403     {
404         /* referenced filter does not have any pins */
405         return MM_STATUS_UNSUCCESSFUL;
406     }
407 
408     /* get topology node types */
409     Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_NODES, &NodeTypes);
410     if (Status != MM_STATUS_SUCCESS)
411     {
412         /* failed to get topology node types */
413         return Status;
414     }
415 
416     /* get topology connections */
417     Status = MMixerGetFilterTopologyProperty(MixerContext, MixerData->hDevice, KSPROPERTY_TOPOLOGY_CONNECTIONS, &NodeConnections);
418     if (Status != MM_STATUS_SUCCESS)
419     {
420         /* failed to get topology connections */
421         MixerContext->Free(NodeTypes);
422         return Status;
423     }
424 
425     /* create a topology */
426     Status = MMixerCreateTopology(MixerContext, PinsCount, NodeConnections, NodeTypes, OutTopology);
427 
428     /* free node types & connections */
429     MixerContext->Free(NodeConnections);
430     MixerContext->Free(NodeTypes);
431 
432     if (Status == MM_STATUS_SUCCESS)
433     {
434         /* store topology object */
435         MixerData->Topology = *OutTopology;
436     }
437 
438     /* done */
439     return Status;
440 }
441 
442 MIXER_STATUS
443 MMixerCountMixerControls(
444     IN PMIXER_CONTEXT MixerContext,
445     IN PTOPOLOGY Topology,
446     IN ULONG PinId,
447     IN ULONG bInputMixer,
448     IN ULONG bUpStream,
449     OUT PULONG OutNodesCount,
450     OUT PULONG OutNodes,
451     OUT PULONG OutLineTerminator)
452 {
453     PULONG Nodes;
454     ULONG NodesCount, NodeIndex, Count, bTerminator;
455     MIXER_STATUS Status;
456 
457     /* allocate an array to store all nodes which are upstream of this pin */
458     Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes);
459 
460     if (Status != MM_STATUS_SUCCESS)
461     {
462         /* out of memory */
463         return STATUS_NO_MEMORY;
464     }
465 
466     /* mark result array as zero */
467     *OutNodesCount = 0;
468 
469     /* get next nodes */
470     MMixerGetNextNodesFromPinIndex(MixerContext, Topology, PinId, bUpStream, &NodesCount, Nodes);
471 
472     if (NodesCount == 0)
473     {
474         /* a pin which is not connected from any node
475          * a) it is a topology bug (driver bug)
476          * b) the request is from an alternative mixer
477               alternative mixer code scans all pins which have not been used and tries to build lines
478          */
479         DPRINT1("MMixerCountMixerControls PinId %lu is not connected by any node\n", PinId);
480         MMixerPrintTopology(Topology);
481         return MM_STATUS_UNSUCCESSFUL;
482     }
483 
484     /* assume no topology split before getting line terminator */
485     ASSERT(NodesCount == 1);
486 
487     /* get first node */
488     NodeIndex = Nodes[0];
489     Count = 0;
490 
491     do
492     {
493         /* check if the node is a terminator */
494         MMixerIsNodeTerminator(Topology, NodeIndex, &bTerminator);
495 
496         if (bTerminator)
497         {
498             /* found terminator */
499             if (bInputMixer)
500             {
501                 /* add mux source for source destination line */
502                 OutNodes[Count] = NodeIndex;
503                 Count++;
504             }
505             break;
506         }
507 
508         /* store node id */
509         OutNodes[Count] = NodeIndex;
510 
511         /* increment node count */
512         Count++;
513 
514         /* get next nodes upstream */
515         MMixerGetNextNodesFromNodeIndex(MixerContext, Topology, NodeIndex, bUpStream, &NodesCount, Nodes);
516 
517         if (NodesCount != 1)
518         {
519             DPRINT("PinId %lu bInputMixer %lu bUpStream %lu NodeIndex %lu is not connected", PinId, bInputMixer, bUpStream, NodeIndex);
520             break;
521         }
522 
523         ASSERT(NodesCount == 1);
524 
525         /* use first index */
526         NodeIndex = Nodes[0];
527 
528     }while(TRUE);
529 
530     /* free node index */
531     MixerContext->Free(Nodes);
532 
533     /* store nodes count */
534     *OutNodesCount = Count;
535 
536     /* store line terminator */
537     *OutLineTerminator = NodeIndex;
538 
539     /* done */
540     return MM_STATUS_SUCCESS;
541 }
542 
543 MIXER_STATUS
544 MMixerGetChannelCountEnhanced(
545     IN PMIXER_CONTEXT MixerContext,
546     IN LPMIXER_INFO MixerInfo,
547     IN HANDLE hMixer,
548     IN ULONG NodeId,
549     OUT PULONG MaxChannels)
550 {
551     KSPROPERTY_DESCRIPTION Description;
552     PKSPROPERTY_DESCRIPTION NewDescription;
553     PKSPROPERTY_MEMBERSHEADER Header;
554     ULONG BytesReturned;
555     KSP_NODE Request;
556     MIXER_STATUS Status;
557 
558     /* try #1 obtain it via description */
559     Request.NodeId = NodeId;
560     Request.Reserved = 0;
561     Request.Property.Set = KSPROPSETID_Audio;
562     Request.Property.Flags = KSPROPERTY_TYPE_BASICSUPPORT | KSPROPERTY_TYPE_TOPOLOGY;
563     Request.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL;
564 
565     /* get description */
566     Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_NODE), (PVOID)&Description, sizeof(KSPROPERTY_DESCRIPTION), &BytesReturned);
567     if (Status == MM_STATUS_SUCCESS)
568     {
569         if (Description.DescriptionSize >= sizeof(KSPROPERTY_DESCRIPTION) + sizeof(KSPROPERTY_MEMBERSHEADER) && (Description.MembersListCount > 0))
570         {
571             /* allocate new description */
572             NewDescription = MixerContext->Alloc(Description.DescriptionSize);
573 
574             if (!NewDescription)
575             {
576                 /* not enough memory */
577                 return MM_STATUS_NO_MEMORY;
578             }
579 
580             /* get description */
581             Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_NODE), (PVOID)NewDescription, Description.DescriptionSize, &BytesReturned);
582             if (Status == MM_STATUS_SUCCESS)
583             {
584                 /* get header */
585                 Header = (PKSPROPERTY_MEMBERSHEADER)(NewDescription + 1);
586 
587                 if (Header->Flags & KSPROPERTY_MEMBER_FLAG_BASICSUPPORT_MULTICHANNEL)
588                 {
589                     /* found enhanced flag */
590                     ASSERT(Header->MembersCount > 1);
591 
592                     /* store channel count */
593                     *MaxChannels = Header->MembersCount;
594 
595                     /* free description */
596                     MixerContext->Free(NewDescription);
597 
598                     /* done */
599                     return MM_STATUS_SUCCESS;
600                 }
601             }
602 
603             /* free description */
604             MixerContext->Free(NewDescription);
605         }
606     }
607 
608     /* failed to get channel count enhanced */
609     return MM_STATUS_UNSUCCESSFUL;
610 }
611 
612 VOID
613 MMixerGetChannelCountLegacy(
614     IN PMIXER_CONTEXT MixerContext,
615     IN LPMIXER_INFO MixerInfo,
616     IN HANDLE hMixer,
617     IN ULONG NodeId,
618     OUT PULONG MaxChannels)
619 {
620     ULONG BytesReturned;
621     MIXER_STATUS Status;
622     KSNODEPROPERTY_AUDIO_CHANNEL Channel;
623     LONG Volume;
624 
625     /* setup request */
626     Channel.Reserved = 0;
627     Channel.NodeProperty.NodeId = NodeId;
628     Channel.NodeProperty.Reserved = 0;
629     Channel.NodeProperty.Property.Flags = KSPROPERTY_TYPE_GET | KSPROPERTY_TYPE_TOPOLOGY;
630     Channel.NodeProperty.Property.Set = KSPROPSETID_Audio;
631     Channel.Channel = 0;
632     Channel.NodeProperty.Property.Id = KSPROPERTY_AUDIO_VOLUMELEVEL;
633 
634     do
635     {
636         /* get channel volume */
637         Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Channel, sizeof(KSNODEPROPERTY_AUDIO_CHANNEL), (PVOID)&Volume, sizeof(LONG), &BytesReturned);
638         if (Status != MM_STATUS_SUCCESS)
639             break;
640 
641         /* increment channel count */
642         Channel.Channel++;
643 
644     }while(TRUE);
645 
646     /* store channel count */
647     *MaxChannels = Channel.Channel;
648 
649 }
650 
651 VOID
652 MMixerGetMaxChannelsForNode(
653     IN PMIXER_CONTEXT MixerContext,
654     IN LPMIXER_INFO MixerInfo,
655     IN HANDLE hMixer,
656     IN ULONG NodeId,
657     OUT PULONG MaxChannels)
658 {
659     MIXER_STATUS Status;
660 
661     /* try to get it enhanced */
662     Status = MMixerGetChannelCountEnhanced(MixerContext, MixerInfo, hMixer, NodeId, MaxChannels);
663 
664     if (Status != MM_STATUS_SUCCESS)
665     {
666         /* get it old-fashioned way */
667         MMixerGetChannelCountLegacy(MixerContext, MixerInfo, hMixer, NodeId, MaxChannels);
668     }
669 }
670 
671 MIXER_STATUS
672 MMixerAddMixerControlsToMixerLineByNodeIndexArray(
673     IN PMIXER_CONTEXT MixerContext,
674     IN LPMIXER_INFO MixerInfo,
675     IN HANDLE hMixer,
676     IN PTOPOLOGY Topology,
677     IN OUT LPMIXERLINE_EXT DstLine,
678     IN ULONG NodesCount,
679     IN PULONG Nodes)
680 {
681     ULONG Index, Count, bReserved;
682     MIXER_STATUS Status;
683     LPGUID NodeType;
684     ULONG MaxChannels;
685 
686     /* initialize control count */
687     Count = 0;
688 
689     for(Index = 0; Index < NodesCount; Index++)
690     {
691         /* check if the node has already been reserved to a line */
692         MMixerIsTopologyNodeReserved(Topology, Nodes[Index], &bReserved);
693 #if 0 /* MS lies */
694         if (bReserved)
695         {
696             /* node is already used, skip it */
697             continue;
698         }
699 #endif
700         /* set node status as used */
701         MMixerSetTopologyNodeReserved(Topology, Nodes[Index]);
702 
703         /* query node type */
704         NodeType = MMixerGetNodeTypeFromTopology(Topology, Nodes[Index]);
705 
706         if (IsEqualGUIDAligned(NodeType, &KSNODETYPE_VOLUME))
707         {
708             /* calculate maximum channel count for node */
709             MMixerGetMaxChannelsForNode(MixerContext, MixerInfo, hMixer, Nodes[Index], &MaxChannels);
710 
711             DPRINT("NodeId %lu MaxChannels %lu Line %S Id %lu\n", Nodes[Index], MaxChannels, DstLine->Line.szName, DstLine->Line.dwLineID);
712             /* calculate maximum channels */
713             DstLine->Line.cChannels = min(DstLine->Line.cChannels, MaxChannels);
714         }
715         else
716         {
717             /* use default of one channel */
718             MaxChannels = 1;
719         }
720 
721         /* now add the mixer control */
722         Status = MMixerAddMixerControl(MixerContext, MixerInfo, hMixer, Topology, Nodes[Index], DstLine, MaxChannels);
723 
724         if (Status == MM_STATUS_SUCCESS)
725         {
726             /* increment control count */
727             Count++;
728         }
729     }
730 
731     /* store control count */
732     DstLine->Line.cControls = Count;
733 
734     /* done */
735     return MM_STATUS_SUCCESS;
736 }
737 
738 MIXER_STATUS
739 MMixerGetComponentAndTargetType(
740     IN PMIXER_CONTEXT MixerContext,
741     IN OUT LPMIXER_INFO MixerInfo,
742     IN HANDLE hMixer,
743     IN ULONG PinId,
744     OUT PULONG ComponentType,
745     OUT PULONG TargetType)
746 {
747     KSPIN_DATAFLOW DataFlow;
748     KSPIN_COMMUNICATION Communication;
749     MIXER_STATUS Status;
750     KSP_PIN Request;
751     ULONG BytesReturned;
752     GUID Guid;
753     BOOLEAN BridgePin = FALSE;
754     PKSPIN_PHYSICALCONNECTION Connection;
755 
756     /* first dataflow type */
757     Status = MMixerGetPinDataFlowAndCommunication(MixerContext, hMixer, PinId, &DataFlow, &Communication);
758 
759     if (Status != MM_STATUS_SUCCESS)
760     {
761         /* failed to get dataflow */
762         return Status;
763     }
764 
765     /* now get pin category guid */
766     Request.PinId = PinId;
767     Request.Reserved = 0;
768     Request.Property.Flags = KSPROPERTY_TYPE_GET;
769     Request.Property.Set = KSPROPSETID_Pin;
770     Request.Property.Id = KSPROPERTY_PIN_CATEGORY;
771 
772     /* get pin category */
773     Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSP_PIN), &Guid, sizeof(GUID), &BytesReturned);
774     if (Status != MM_STATUS_SUCCESS)
775     {
776         /* failed to get dataflow */
777         return Status;
778     }
779 
780     /* check if it has a physical connection */
781     Status = MMixerGetPhysicalConnection(MixerContext, hMixer, PinId, &Connection);
782     if (Status == MM_STATUS_SUCCESS)
783     {
784         /* pin is a brige pin */
785         BridgePin = TRUE;
786 
787         /* free physical connection */
788         MixerContext->Free(Connection);
789     }
790 
791     if (DataFlow == KSPIN_DATAFLOW_IN)
792     {
793         if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_MICROPHONE) ||
794             IsEqualGUIDAligned(&Guid, &KSNODETYPE_DESKTOP_MICROPHONE))
795         {
796             /* type microphone */
797             *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
798             *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
799         }
800         else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_LEGACY_AUDIO_CONNECTOR) ||
801                  IsEqualGUIDAligned(&Guid, &KSCATEGORY_AUDIO) ||
802                  IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPEAKER))
803         {
804             /* type waveout */
805             *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
806             *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
807         }
808         else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_CD_PLAYER))
809         {
810             /* type cd player */
811             *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
812             *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC;
813         }
814         else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SYNTHESIZER))
815         {
816             /* type synthesizer */
817             *TargetType = MIXERLINE_TARGETTYPE_MIDIOUT;
818             *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_SYNTHESIZER;
819         }
820         else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_LINE_CONNECTOR))
821         {
822             /* type line */
823             *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
824             *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
825         }
826         else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_TELEPHONE) ||
827                  IsEqualGUIDAligned(&Guid, &KSNODETYPE_PHONE_LINE) ||
828                  IsEqualGUIDAligned(&Guid, &KSNODETYPE_DOWN_LINE_PHONE))
829         {
830             /* type telephone */
831             *TargetType =  MIXERLINE_TARGETTYPE_UNDEFINED;
832             *ComponentType =  MIXERLINE_COMPONENTTYPE_SRC_TELEPHONE;
833         }
834         else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_ANALOG_CONNECTOR))
835         {
836             /* type analog */
837             if (BridgePin)
838                 *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
839             else
840                 *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
841 
842             *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_ANALOG;
843         }
844         else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPDIF_INTERFACE))
845         {
846             /* type analog */
847             if (BridgePin)
848                 *TargetType = MIXERLINE_TARGETTYPE_WAVEIN;
849             else
850                 *TargetType = MIXERLINE_TARGETTYPE_WAVEOUT;
851 
852             *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_DIGITAL;
853         }
854         else
855         {
856             /* unknown type */
857             *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
858             *ComponentType = MIXERLINE_COMPONENTTYPE_SRC_UNDEFINED;
859             DPRINT1("Unknown Category for PinId %lu BridgePin %lu\n", PinId, BridgePin);
860         }
861     }
862     else
863     {
864         if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPEAKER) ||
865                  IsEqualGUIDAligned(&Guid, &KSNODETYPE_DESKTOP_SPEAKER) ||
866                  IsEqualGUIDAligned(&Guid, &KSNODETYPE_ROOM_SPEAKER) ||
867                  IsEqualGUIDAligned(&Guid, &KSNODETYPE_COMMUNICATION_SPEAKER))
868         {
869             /* type waveout */
870             *TargetType =  MIXERLINE_TARGETTYPE_WAVEOUT;
871             *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
872         }
873         else if (IsEqualGUIDAligned(&Guid, &KSCATEGORY_AUDIO) ||
874                  IsEqualGUIDAligned(&Guid, &PINNAME_CAPTURE))
875         {
876             /* type wavein */
877             *TargetType =  MIXERLINE_TARGETTYPE_WAVEIN;
878             *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
879         }
880         else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_HEADPHONES) ||
881                  IsEqualGUIDAligned(&Guid, &KSNODETYPE_HEAD_MOUNTED_DISPLAY_AUDIO))
882         {
883             /* type head phones */
884             *TargetType =  MIXERLINE_TARGETTYPE_WAVEOUT;
885             *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_HEADPHONES;
886         }
887         else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_TELEPHONE) ||
888                  IsEqualGUIDAligned(&Guid, &KSNODETYPE_PHONE_LINE) ||
889                  IsEqualGUIDAligned(&Guid, &KSNODETYPE_DOWN_LINE_PHONE))
890         {
891             /* type waveout */
892             *TargetType =   MIXERLINE_TARGETTYPE_UNDEFINED;
893             *ComponentType =   MIXERLINE_COMPONENTTYPE_DST_TELEPHONE;
894         }
895         else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_ANALOG_CONNECTOR))
896         {
897             /* type analog */
898             if (BridgePin)
899             {
900                 *TargetType =  MIXERLINE_TARGETTYPE_WAVEOUT;
901                 *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
902             }
903             else
904             {
905                 *TargetType =  MIXERLINE_TARGETTYPE_WAVEIN;
906                 *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
907             }
908         }
909         else if (IsEqualGUIDAligned(&Guid, &KSNODETYPE_SPDIF_INTERFACE))
910         {
911             /* type spdif */
912             if (BridgePin)
913             {
914                 *TargetType =  MIXERLINE_TARGETTYPE_WAVEOUT;
915                 *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
916             }
917             else
918             {
919                 *TargetType =  MIXERLINE_TARGETTYPE_WAVEIN;
920                 *ComponentType =  MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
921             }
922         }
923         else
924         {
925             /* unknown type */
926             *TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
927             *ComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED;
928             DPRINT1("Unknown Category for PinId %lu BridgePin %lu\n", PinId, BridgePin);
929         }
930     }
931 
932     /* done */
933     return MM_STATUS_SUCCESS;
934 }
935 
936 MIXER_STATUS
937 MMixerBuildMixerSourceLine(
938     IN PMIXER_CONTEXT MixerContext,
939     IN OUT LPMIXER_INFO MixerInfo,
940     IN HANDLE hMixer,
941     IN PTOPOLOGY Topology,
942     IN ULONG PinId,
943     IN ULONG NodesCount,
944     IN PULONG Nodes,
945     IN ULONG DestinationLineID,
946     OUT LPMIXERLINE_EXT * OutSrcLine)
947 {
948     LPMIXERLINE_EXT SrcLine, DstLine;
949     LPWSTR PinName;
950     MIXER_STATUS Status;
951     ULONG ComponentType, TargetType;
952 
953     /* get component and target type */
954     Status = MMixerGetComponentAndTargetType(MixerContext, MixerInfo, hMixer, PinId, &ComponentType, &TargetType);
955     if (Status != MM_STATUS_SUCCESS)
956     {
957         /* failed to get component status */
958         TargetType = MIXERLINE_TARGETTYPE_UNDEFINED;
959         ComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED;
960     }
961 
962     /* construct source line */
963     SrcLine = (LPMIXERLINE_EXT)MixerContext->Alloc(sizeof(MIXERLINE_EXT));
964 
965     if (!SrcLine)
966     {
967         /* no memory */
968         return MM_STATUS_NO_MEMORY;
969     }
970 
971     /* get destination line */
972     DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
973     ASSERT(DstLine);
974 
975     /* initialize mixer src line */
976     SrcLine->PinId = PinId;
977 
978     /* initialize mixer line */
979     SrcLine->Line.cbStruct = sizeof(MIXERLINEW);
980     SrcLine->Line.dwDestination = MixerInfo->MixCaps.cDestinations-1;
981     SrcLine->Line.dwSource = DstLine->Line.cConnections;
982     SrcLine->Line.dwLineID = (DstLine->Line.cConnections * SOURCE_LINE)+ (MixerInfo->MixCaps.cDestinations-1);
983     SrcLine->Line.fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
984     SrcLine->Line.dwComponentType = ComponentType;
985     SrcLine->Line.dwUser = 0;
986     SrcLine->Line.cChannels = DstLine->Line.cChannels;
987     SrcLine->Line.cConnections = 0;
988     SrcLine->Line.Target.dwType = TargetType;
989     SrcLine->Line.Target.dwDeviceID = DstLine->Line.Target.dwDeviceID;
990     SrcLine->Line.Target.wMid = MixerInfo->MixCaps.wMid;
991     SrcLine->Line.Target.wPid = MixerInfo->MixCaps.wPid;
992     SrcLine->Line.Target.vDriverVersion = MixerInfo->MixCaps.vDriverVersion;
993     InitializeListHead(&SrcLine->ControlsList);
994 
995     /* copy name */
996     ASSERT(MixerInfo->MixCaps.szPname[MAXPNAMELEN-1] == L'\0');
997     wcscpy(SrcLine->Line.Target.szPname, MixerInfo->MixCaps.szPname);
998 
999     /* get pin name */
1000     Status = MMixerGetPinName(MixerContext, MixerInfo, hMixer, PinId, &PinName);
1001 
1002     if (Status == MM_STATUS_SUCCESS)
1003     {
1004         /* store pin name as line name */
1005         MixerContext->Copy(SrcLine->Line.szShortName, PinName, (min(MIXER_SHORT_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
1006         SrcLine->Line.szShortName[MIXER_SHORT_NAME_CHARS-1] = L'\0';
1007 
1008         MixerContext->Copy(SrcLine->Line.szName, PinName, (min(MIXER_LONG_NAME_CHARS, wcslen(PinName)+1)) * sizeof(WCHAR));
1009         SrcLine->Line.szName[MIXER_LONG_NAME_CHARS-1] = L'\0';
1010 
1011         /* free pin name buffer */
1012         MixerContext->Free(PinName);
1013     }
1014 
1015     /* add the controls to mixer line */
1016     Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, hMixer, Topology, SrcLine, NodesCount, Nodes);
1017     if (Status != MM_STATUS_SUCCESS)
1018     {
1019         /* failed */
1020         return Status;
1021     }
1022 
1023     /* store result */
1024     *OutSrcLine = SrcLine;
1025 
1026     return MM_STATUS_SUCCESS;
1027 }
1028 
1029 MIXER_STATUS
1030 MMixerAddMixerSourceLines(
1031     IN PMIXER_CONTEXT MixerContext,
1032     IN OUT LPMIXER_INFO MixerInfo,
1033     IN HANDLE hMixer,
1034     IN PTOPOLOGY Topology,
1035     IN ULONG DestinationLineID,
1036     IN ULONG LineTerminator)
1037 {
1038     PULONG AllNodes, AllPins, AllPinNodes;
1039     ULONG AllNodesCount, AllPinsCount, AllPinNodesCount;
1040     ULONG Index, SubIndex, PinId, CurNode, bConnected;
1041     MIXER_STATUS Status;
1042     LPMIXERLINE_EXT DstLine, SrcLine;
1043 
1044     /* get destination line */
1045     DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineID);
1046     ASSERT(DstLine);
1047 
1048     /* allocate an array to store all nodes which are upstream of the line terminator */
1049     Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllNodes);
1050 
1051     /* check for success */
1052     if (Status != MM_STATUS_SUCCESS)
1053     {
1054         /* out of memory */
1055         return MM_STATUS_NO_MEMORY;
1056     }
1057 
1058     /* allocate an array to store all nodes which are downstream of a particular pin */
1059     Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &AllPinNodes);
1060 
1061     /* allocate an array to store all pins which are upstream of this pin */
1062     Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &AllPins);
1063 
1064     /* check for success */
1065     if (Status != MM_STATUS_SUCCESS)
1066     {
1067         /* out of memory */
1068         MixerContext->Free(AllNodes);
1069         return MM_STATUS_NO_MEMORY;
1070     }
1071 
1072      /* get all nodes which indirectly / directly connect to this node */
1073     AllNodesCount = 0;
1074     MMixerGetAllUpOrDownstreamNodesFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllNodesCount, AllNodes);
1075 
1076     /* get all pins which indirectly / directly connect to this node */
1077     AllPinsCount = 0;
1078     MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, LineTerminator, TRUE, &AllPinsCount, AllPins);
1079 
1080     DPRINT("LineTerminator %lu\n", LineTerminator);
1081     DPRINT("PinCount %lu\n", AllPinsCount);
1082     DPRINT("AllNodesCount %lu\n", AllNodesCount);
1083 
1084     /* now construct the source lines which are attached to the destination line */
1085     Index = AllPinsCount;
1086 
1087     do
1088     {
1089         /* get current pin id */
1090         PinId = AllPins[Index - 1];
1091 
1092         /* reset nodes count */
1093         AllPinNodesCount = 0;
1094 
1095         /* now scan all nodes and add them to AllPinNodes array when they are connected to this pin */
1096         for(SubIndex = 0; SubIndex < AllNodesCount; SubIndex++)
1097         {
1098             /* get current node index */
1099             CurNode = AllNodes[SubIndex];
1100 
1101             if (CurNode != MAXULONG && CurNode != LineTerminator)
1102             {
1103                 /* check if that node is connected in some way to the current pin */
1104                 Status = MMixerIsNodeConnectedToPin(MixerContext, Topology, CurNode, PinId, TRUE, &bConnected);
1105 
1106                 if (Status != MM_STATUS_SUCCESS)
1107                     break;
1108 
1109                 if (bConnected)
1110                 {
1111                     /* it is connected */
1112                     AllPinNodes[AllPinNodesCount] = CurNode;
1113                     AllPinNodesCount++;
1114 
1115                     /* clear current index */
1116                     AllNodes[SubIndex] = MAXULONG;
1117                 }
1118             }
1119         }
1120 
1121         /* decrement pin index */
1122         Index--;
1123 
1124         if (AllPinNodesCount)
1125         {
1126 #ifdef MMIXER_DEBUG
1127             ULONG TempIndex;
1128 #endif
1129             /* now build the mixer source line */
1130             Status = MMixerBuildMixerSourceLine(MixerContext, MixerInfo, hMixer, Topology, PinId, AllPinNodesCount, AllPinNodes, DestinationLineID, &SrcLine);
1131 
1132              if (Status == MM_STATUS_SUCCESS)
1133              {
1134                  /* insert into line list */
1135                  InsertTailList(&MixerInfo->LineList, &SrcLine->Entry);
1136 
1137                  /* increment destination line count */
1138                  DstLine->Line.cConnections++;
1139 
1140                  /* mark pin as reserved */
1141                  MMixerSetTopologyPinReserved(Topology, PinId);
1142 
1143 #ifdef MMIXER_DEBUG
1144                  DPRINT1("Adding PinId %lu AllPinNodesCount %lu to DestinationLine %lu\n", PinId, AllPinNodesCount, DestinationLineID);
1145                  for(TempIndex = 0; TempIndex < AllPinNodesCount; TempIndex++)
1146                      DPRINT1("NodeIndex %lu\n", AllPinNodes[TempIndex]);
1147 #endif
1148              }
1149         }
1150         else
1151         {
1152 #ifdef MMIXER_DEBUG
1153             DPRINT1("Discarding DestinationLineID %lu PinId %lu NO NODES!\n", DestinationLineID, PinId);
1154 #endif
1155         }
1156 
1157     }while(Index != 0);
1158 
1159     return MM_STATUS_SUCCESS;
1160 }
1161 
1162 MIXER_STATUS
1163 MMixerAddMixerControlsToDestinationLine(
1164     IN PMIXER_CONTEXT MixerContext,
1165     IN OUT LPMIXER_INFO MixerInfo,
1166     IN HANDLE hMixer,
1167     IN PTOPOLOGY Topology,
1168     IN ULONG PinId,
1169     IN ULONG bInput,
1170     IN ULONG DestinationLineId,
1171     OUT PULONG OutLineTerminator)
1172 {
1173     PULONG Nodes;
1174     ULONG NodesCount, LineTerminator;
1175     MIXER_STATUS Status;
1176     LPMIXERLINE_EXT DstLine;
1177 
1178     /* allocate nodes index array */
1179     Status = MMixerAllocateTopologyNodeArray(MixerContext, Topology, &Nodes);
1180 
1181     /* check for success */
1182     if (Status != MM_STATUS_SUCCESS)
1183     {
1184         /* out of memory */
1185         return Status;
1186     }
1187 
1188     /* get all destination line controls */
1189     Status = MMixerCountMixerControls(MixerContext, Topology, PinId, bInput, TRUE, &NodesCount, Nodes, &LineTerminator);
1190 
1191     /* check for success */
1192     if (Status != MM_STATUS_SUCCESS)
1193     {
1194         /* failed to count controls */
1195         MixerContext->Free(Nodes);
1196         return Status;
1197     }
1198 
1199     /* get destination mixer line */
1200     DstLine = MMixerGetSourceMixerLineByLineId(MixerInfo, DestinationLineId);
1201 
1202     /* sanity check */
1203     ASSERT(DstLine);
1204 
1205     if (NodesCount > 0)
1206     {
1207         /* add all nodes as mixer controls to the destination line */
1208         Status = MMixerAddMixerControlsToMixerLineByNodeIndexArray(MixerContext, MixerInfo, hMixer, Topology, DstLine, NodesCount, Nodes);
1209         if (Status != MM_STATUS_SUCCESS)
1210         {
1211             /* failed to add controls */
1212             MixerContext->Free(Nodes);
1213             return Status;
1214         }
1215     }
1216 
1217     /* store result */
1218     *OutLineTerminator = LineTerminator;
1219 
1220     /* return result */
1221     return Status;
1222 }
1223 
1224 VOID
1225 MMixerApplyOutputFilterHack(
1226     IN PMIXER_CONTEXT MixerContext,
1227     IN LPMIXER_DATA MixerData,
1228     IN HANDLE hMixer,
1229     IN OUT PULONG PinsCount,
1230     IN OUT PULONG Pins)
1231 {
1232     ULONG Count = 0, Index;
1233     MIXER_STATUS Status;
1234     PKSPIN_PHYSICALCONNECTION Connection;
1235 
1236     for(Index = 0; Index < *PinsCount; Index++)
1237     {
1238         /* check if it has a physical connection */
1239         Status = MMixerGetPhysicalConnection(MixerContext, hMixer, Pins[Index], &Connection);
1240 
1241         if (Status == MM_STATUS_SUCCESS)
1242         {
1243             /* remove pin */
1244             MixerContext->Copy(&Pins[Index], &Pins[Index + 1], (*PinsCount - (Index + 1)) * sizeof(ULONG));
1245 
1246             /* free physical connection */
1247             MixerContext->Free(Connection);
1248 
1249             /* decrement index */
1250             Index--;
1251 
1252             /* decrement pin count */
1253             (*PinsCount)--;
1254         }
1255         else
1256         {
1257             /* simple pin */
1258             Count++;
1259         }
1260     }
1261 
1262     /* store result */
1263     *PinsCount = Count;
1264 }
1265 
1266 MIXER_STATUS
1267 MMixerHandleTopologyFilter(
1268     IN PMIXER_CONTEXT MixerContext,
1269     IN PMIXER_LIST MixerList,
1270     IN LPMIXER_DATA MixerData,
1271     IN OUT LPMIXER_INFO MixerInfo,
1272     IN ULONG bInput,
1273     IN ULONG Pin)
1274 {
1275     MIXER_STATUS Status;
1276     ULONG PinsCount, LineTerminator, DestinationLineID;
1277     PULONG Pins;
1278     PTOPOLOGY Topology;
1279 
1280     /* re-use existing topology */
1281     Topology = MixerData->Topology;
1282 
1283     if (!bInput)
1284     {
1285         /* allocate pin index array which will hold all referenced pins */
1286         Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1287         if (Status != MM_STATUS_SUCCESS)
1288         {
1289             /* failed to create topology */
1290             return Status;
1291         }
1292 
1293         /* the mixer is an output mixer
1294         * find end pin of the node path
1295         */
1296         PinsCount = 0;
1297         Status = MMixerGetAllUpOrDownstreamPinsFromPinIndex(MixerContext, Topology, Pin, FALSE, &PinsCount, Pins);
1298 
1299         /* check for success */
1300         if (Status != MM_STATUS_SUCCESS)
1301         {
1302             /* failed to get end pin */
1303             MixerContext->Free(Pins);
1304             //MMixerFreeTopology(Topology);
1305 
1306             /* return error code */
1307             return Status;
1308         }
1309         /* HACK:
1310         * some topologies do not have strict boundaries
1311         * WorkArround: remove all pin ids which have a physical connection
1312         * because bridge pins may belong to different render paths
1313         */
1314         MMixerApplyOutputFilterHack(MixerContext, MixerData, MixerData->hDevice, &PinsCount, Pins);
1315 
1316         /* sanity checks */
1317         ASSERT(PinsCount != 0);
1318         if (PinsCount != 1)
1319         {
1320             DPRINT1("MMixerHandlePhysicalConnection Expected 1 pin but got %lu\n", PinsCount);
1321         }
1322 
1323         /* create destination line */
1324         Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInput);
1325 
1326         /* calculate destination line id */
1327         DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations - 1);
1328 
1329         if (Status != MM_STATUS_SUCCESS)
1330         {
1331             /* failed to build destination line */
1332             MixerContext->Free(Pins);
1333 
1334             /* return error code */
1335             return Status;
1336         }
1337 
1338         /* add mixer controls to destination line */
1339         Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, Pins[0], bInput, DestinationLineID, &LineTerminator);
1340 
1341         if (Status == MM_STATUS_SUCCESS)
1342         {
1343             /* now add the rest of the source lines */
1344             Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator);
1345         }
1346 
1347         /* mark pin as consumed */
1348         MMixerSetTopologyPinReserved(Topology, Pins[0]);
1349 
1350         /* free topology pin array */
1351         MixerContext->Free(Pins);
1352     }
1353     else
1354     {
1355         /* calculate destination line id */
1356         DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations - 1);
1357 
1358         /* add mixer controls */
1359         Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, Pin, bInput, DestinationLineID, &LineTerminator);
1360 
1361         if (Status == MM_STATUS_SUCCESS)
1362         {
1363             /* now add the rest of the source lines */
1364             Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator);
1365         }
1366     }
1367     return Status;
1368 }
1369 
1370 MIXER_STATUS
1371 MMixerHandlePhysicalConnection(
1372     IN PMIXER_CONTEXT MixerContext,
1373     IN PMIXER_LIST MixerList,
1374     IN LPMIXER_DATA MixerData,
1375     IN OUT LPMIXER_INFO MixerInfo,
1376     IN ULONG bInput,
1377     IN PKSPIN_PHYSICALCONNECTION OutConnection)
1378 {
1379     MIXER_STATUS Status;
1380     ULONG PinsCount, LineTerminator, DestinationLineID;
1381     PULONG Pins;
1382     PTOPOLOGY Topology;
1383 
1384     /* first try to open the connected filter */
1385     OutConnection->SymbolicLinkName[1] = L'\\';
1386     MixerData = MMixerGetDataByDeviceName(MixerList, OutConnection->SymbolicLinkName);
1387 
1388      /* check if the linked connection is found */
1389      if (!MixerData)
1390      {
1391          /* filter references invalid physical connection */
1392          return MM_STATUS_UNSUCCESSFUL;
1393      }
1394 
1395     DPRINT("Name %S, Pin %lu bInput %lu\n", OutConnection->SymbolicLinkName, OutConnection->Pin, bInput);
1396 
1397     /* sanity check */
1398     ASSERT(MixerData->MixerInfo == NULL || MixerData->MixerInfo == MixerInfo);
1399 
1400     /* associate with mixer */
1401     MixerData->MixerInfo = MixerInfo;
1402 
1403     if (MixerData->Topology == NULL)
1404     {
1405         /* construct new topology */
1406         Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
1407         if (Status != MM_STATUS_SUCCESS)
1408         {
1409             /* failed to create topology */
1410             return Status;
1411         }
1412 
1413         /* store topology */
1414         MixerData->Topology = Topology;
1415     }
1416     else
1417     {
1418         /* re-use existing topology */
1419         Topology = MixerData->Topology;
1420     }
1421 
1422     /* mark pin as consumed */
1423     MMixerSetTopologyPinReserved(Topology, OutConnection->Pin);
1424 
1425     if (!bInput)
1426     {
1427         /* allocate pin index array which will hold all referenced pins */
1428         Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1429         if (Status != MM_STATUS_SUCCESS)
1430         {
1431             /* failed to create topology */
1432             return Status;
1433         }
1434 
1435         /* the mixer is an output mixer
1436          * find end pin of the node path
1437          */
1438         PinsCount = 0;
1439         Status = MMixerGetAllUpOrDownstreamPinsFromPinIndex(MixerContext, Topology, OutConnection->Pin, FALSE, &PinsCount, Pins);
1440 
1441         /* check for success */
1442         if (Status != MM_STATUS_SUCCESS)
1443         {
1444             /* failed to get end pin */
1445             MixerContext->Free(Pins);
1446             //MMixerFreeTopology(Topology);
1447 
1448             /* return error code */
1449             return Status;
1450         }
1451         /* HACK:
1452          * some topologies do not have strict boundaries
1453          * WorkArround: remove all pin ids which have a physical connection
1454          * because bridge pins may belong to different render paths
1455          */
1456         MMixerApplyOutputFilterHack(MixerContext, MixerData, MixerData->hDevice, &PinsCount, Pins);
1457 
1458         /* sanity checks */
1459         ASSERT(PinsCount != 0);
1460         if (PinsCount != 1)
1461         {
1462             DPRINT1("MMixerHandlePhysicalConnection Expected 1 pin but got %lu\n", PinsCount);
1463         }
1464 
1465         /* create destination line */
1466         Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInput);
1467 
1468         /* calculate destination line id */
1469         DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1);
1470 
1471         if (Status != MM_STATUS_SUCCESS)
1472         {
1473             /* failed to build destination line */
1474             MixerContext->Free(Pins);
1475 
1476             /* return error code */
1477             return Status;
1478         }
1479 
1480         /* add mixer controls to destination line */
1481         Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, Pins[0], bInput, DestinationLineID,  &LineTerminator);
1482 
1483         if (Status == MM_STATUS_SUCCESS)
1484         {
1485             /* now add the rest of the source lines */
1486             Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator);
1487         }
1488 
1489         /* mark pin as consumed */
1490         MMixerSetTopologyPinReserved(Topology, Pins[0]);
1491 
1492         /* free topology pin array */
1493         MixerContext->Free(Pins);
1494     }
1495     else
1496     {
1497         /* calculate destination line id */
1498         DestinationLineID = (DESTINATION_LINE + MixerInfo->MixCaps.cDestinations-1);
1499 
1500         /* add mixer controls */
1501         Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Topology, OutConnection->Pin, bInput, DestinationLineID, &LineTerminator);
1502 
1503         if (Status == MM_STATUS_SUCCESS)
1504         {
1505             /* now add the rest of the source lines */
1506             Status = MMixerAddMixerSourceLines(MixerContext, MixerInfo, MixerData->hDevice, Topology, DestinationLineID, LineTerminator);
1507         }
1508     }
1509 
1510     return Status;
1511 }
1512 
1513 MIXER_STATUS
1514 MMixerInitializeFilter(
1515     IN PMIXER_CONTEXT MixerContext,
1516     IN PMIXER_LIST MixerList,
1517     IN LPMIXER_DATA MixerData,
1518     IN LPMIXER_INFO MixerInfo,
1519     IN PTOPOLOGY Topology,
1520     IN ULONG NodeIndex,
1521     IN ULONG bInputMixer,
1522     IN OUT LPMIXER_INFO * OutMixerInfo)
1523 {
1524     ULONG Index;
1525     MIXER_STATUS Status;
1526     PKSPIN_PHYSICALCONNECTION OutConnection;
1527     ULONG * Pins;
1528     ULONG PinsFound;
1529     ULONG NewMixerInfo = FALSE;
1530 
1531     if (MixerInfo == NULL)
1532     {
1533         /* allocate a mixer info struct */
1534         MixerInfo = (LPMIXER_INFO) MixerContext->Alloc(sizeof(MIXER_INFO));
1535         if (!MixerInfo)
1536         {
1537             /* no memory */
1538             return MM_STATUS_NO_MEMORY;
1539         }
1540 
1541         /* new mixer info */
1542         NewMixerInfo = TRUE;
1543 
1544         /* intialize mixer caps */
1545         MixerInfo->MixCaps.wMid = MM_MICROSOFT; /* FIXME */
1546         MixerInfo->MixCaps.wPid = MM_PID_UNMAPPED; /* FIXME */
1547         MixerInfo->MixCaps.vDriverVersion = 1; /* FIXME */
1548         MixerInfo->MixCaps.fdwSupport = 0;
1549         MixerInfo->MixCaps.cDestinations = 0;
1550 
1551         /* get mixer name */
1552         Status = MMixerGetDeviceName(MixerContext, MixerInfo->MixCaps.szPname, MixerData->hDeviceInterfaceKey);
1553         if (Status != MM_STATUS_SUCCESS)
1554         {
1555             /* try get name with component id */
1556             Status = MMixerGetDeviceNameWithComponentId(MixerContext, MixerData->hDevice, MixerInfo->MixCaps.szPname);
1557             if (Status != MM_STATUS_SUCCESS)
1558             {
1559                 MixerInfo->MixCaps.szPname[0] = L'\0';
1560             }
1561         }
1562 
1563         /* initialize line list */
1564         InitializeListHead(&MixerInfo->LineList);
1565         InitializeListHead(&MixerInfo->EventList);
1566 
1567         /* associate with mixer data */
1568         MixerData->MixerInfo = MixerInfo;
1569     }
1570 
1571     /* store mixer info */
1572     *OutMixerInfo = MixerInfo;
1573 
1574     /* now allocate an array which will receive the indices of the pin
1575      * which has a ADC / DAC nodetype in its path
1576      */
1577     Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1578     ASSERT(Status == MM_STATUS_SUCCESS);
1579 
1580     PinsFound = 0;
1581 
1582     /* now get all sink / source pins, which are attached to the ADC / DAC node
1583      * For sink pins (wave out) search up stream
1584      * For source pins (wave in) search down stream
1585      * The search direction is always the opposite of the current mixer type
1586      */
1587     PinsFound = 0;
1588     MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, !bInputMixer, &PinsFound, Pins);
1589 
1590     /* if there is no pin found, we have a broken topology */
1591     ASSERT(PinsFound != 0);
1592 
1593     /* now create a wave info struct */
1594     Status = MMixerInitializeWaveInfo(MixerContext, MixerList, MixerData, MixerInfo->MixCaps.szPname, bInputMixer, PinsFound, Pins);
1595     if (Status != MM_STATUS_SUCCESS)
1596     {
1597         /* failed to create wave info struct */
1598         MixerContext->Free(MixerInfo);
1599         MixerContext->Free(Pins);
1600         return Status;
1601     }
1602 
1603     /* mark all found pins as reserved */
1604     for(Index = 0; Index < PinsFound; Index++)
1605     {
1606         MMixerSetTopologyPinReserved(Topology, Pins[Index]);
1607     }
1608 
1609     if (bInputMixer)
1610     {
1611         /* pre create the mixer destination line for input mixers */
1612         Status = MMixerBuildMixerDestinationLine(MixerContext, MixerInfo, MixerData->hDevice, Pins[0], bInputMixer);
1613 
1614         if (Status != MM_STATUS_SUCCESS)
1615         {
1616             /* failed to create mixer destination line */
1617             return Status;
1618         }
1619     }
1620 
1621     /* now get the bridge pin which is at the end of node path
1622      * For sink pins (wave out) search down stream
1623      * For source pins (wave in) search up stream
1624      */
1625     MixerContext->Free(Pins);
1626     Status = MMixerAllocateTopologyPinArray(MixerContext, Topology, &Pins);
1627     ASSERT(Status == MM_STATUS_SUCCESS);
1628 
1629     PinsFound = 0;
1630     MMixerGetAllUpOrDownstreamPinsFromNodeIndex(MixerContext, Topology, NodeIndex, bInputMixer, &PinsFound, Pins);
1631 
1632     /* if there is no pin found, we have a broken topology */
1633     ASSERT(PinsFound != 0);
1634 
1635     /* there should be exactly one bridge pin */
1636     ASSERT(PinsFound == 1);
1637 
1638     DPRINT("BridgePin %lu bInputMixer %lu\n", Pins[0], bInputMixer);
1639 
1640     /* does the pin have a physical connection */
1641     Status = MMixerGetPhysicalConnection(MixerContext, MixerData->hDevice, Pins[0], &OutConnection);
1642 
1643     if (Status == MM_STATUS_SUCCESS)
1644     {
1645         /* mark pin as reserved */
1646         MMixerSetTopologyPinReserved(Topology, Pins[0]);
1647 
1648         /* topology on the topoloy filter */
1649         Status = MMixerHandlePhysicalConnection(MixerContext, MixerList, MixerData, MixerInfo, bInputMixer, OutConnection);
1650 
1651         /* free physical connection data */
1652         MixerContext->Free(OutConnection);
1653     }
1654     else
1655     {
1656         /* topology is on the same filter */
1657         Status = MMixerHandleTopologyFilter(MixerContext, MixerList, MixerData, MixerInfo, bInputMixer, Pins[0]);
1658     }
1659 
1660     /* free pins */
1661     MixerContext->Free(Pins);
1662 
1663     if (NewMixerInfo)
1664     {
1665         /* insert mixer */
1666         InsertHeadList(&MixerList->MixerList, &MixerInfo->Entry);
1667         /* increment mixer count */
1668         MixerList->MixerListCount++;
1669     }
1670 
1671     /* done */
1672     return Status;
1673 }
1674 
1675 VOID
1676 MMixerHandleAlternativeMixers(
1677     IN PMIXER_CONTEXT MixerContext,
1678     IN PMIXER_LIST MixerList,
1679     IN LPMIXER_DATA MixerData,
1680     IN PTOPOLOGY Topology)
1681 {
1682     ULONG Index, PinCount, Reserved;
1683     MIXER_STATUS Status;
1684     ULONG DestinationLineID, LineTerminator;
1685     LPMIXERLINE_EXT DstLine;
1686 
1687     DPRINT("DeviceName %S\n", MixerData->DeviceName);
1688 
1689     /* get topology pin count */
1690     MMixerGetTopologyPinCount(Topology, &PinCount);
1691 
1692     for(Index = 0; Index < PinCount; Index++)
1693     {
1694         MMixerIsTopologyPinReserved(Topology, Index, &Reserved);
1695 
1696         /* check if it has already been reserved */
1697         if (Reserved)
1698         {
1699             /* pin has already been reserved */
1700             continue;
1701         }
1702 
1703         DPRINT("MixerName %S Available PinID %lu\n", MixerData->DeviceName, Index);
1704 
1705         /* sanity check */
1706         //ASSERT(MixerData->MixerInfo);
1707 
1708         if (!MixerData->MixerInfo)
1709         {
1710             DPRINT1("Expected mixer info\n");
1711             continue;
1712         }
1713 
1714         /* build the destination line */
1715         Status = MMixerBuildMixerDestinationLine(MixerContext, MixerData->MixerInfo, MixerData->hDevice, Index, TRUE);
1716         if (Status != MM_STATUS_SUCCESS)
1717         {
1718             /* failed to build destination line */
1719             continue;
1720         }
1721 
1722         /* calculate destination line id */
1723         DestinationLineID = (DESTINATION_LINE + MixerData->MixerInfo->MixCaps.cDestinations-1);
1724 
1725         /* add mixer controls to destination line */
1726         Status = MMixerAddMixerControlsToDestinationLine(MixerContext, MixerData->MixerInfo, MixerData->hDevice, MixerData->Topology, Index, TRUE, DestinationLineID,  &LineTerminator);
1727         if (Status == MM_STATUS_SUCCESS)
1728         {
1729             /* now add the rest of the source lines */
1730             Status = MMixerAddMixerSourceLines(MixerContext, MixerData->MixerInfo, MixerData->hDevice, MixerData->Topology, DestinationLineID, LineTerminator);
1731         }
1732 
1733         /* mark pin as consumed */
1734         MMixerSetTopologyPinReserved(Topology, Index);
1735 
1736         /* now grab destination line */
1737         DstLine = MMixerGetSourceMixerLineByLineId(MixerData->MixerInfo, DestinationLineID);
1738 
1739         /* set type and target as undefined */
1740         DstLine->Line.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_UNDEFINED;
1741         DstLine->Line.Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
1742         DstLine->Line.Target.vDriverVersion = 0;
1743         DstLine->Line.Target.wMid = 0;
1744         DstLine->Line.Target.wPid = 0;
1745     }
1746 }
1747 
1748 MIXER_STATUS
1749 MMixerSetupFilter(
1750     IN PMIXER_CONTEXT MixerContext,
1751     IN PMIXER_LIST MixerList,
1752     IN LPMIXER_DATA MixerData,
1753     IN PULONG DeviceCount)
1754 {
1755     MIXER_STATUS Status = MM_STATUS_SUCCESS;
1756     PTOPOLOGY Topology;
1757     ULONG NodeIndex;
1758     LPMIXER_INFO MixerInfo = NULL;
1759 
1760     /* check if topology has already been built */
1761     if (MixerData->Topology == NULL)
1762     {
1763         /* build topology */
1764         Status = MMixerBuildTopology(MixerContext, MixerData, &Topology);
1765 
1766         if (Status != MM_STATUS_SUCCESS)
1767         {
1768             /* failed to build topology */
1769             return Status;
1770         }
1771 
1772         /* store topology */
1773         MixerData->Topology = Topology;
1774     }
1775     else
1776     {
1777         /* re-use topology */
1778         Topology = MixerData->Topology;
1779     }
1780 
1781     /* check if the filter has an wave out node */
1782     NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_DAC);
1783     if (NodeIndex != MAXULONG)
1784     {
1785         /* it has */
1786         Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, NULL, Topology, NodeIndex, FALSE, &MixerInfo);
1787 
1788         /* check for success */
1789         if (Status == MM_STATUS_SUCCESS)
1790         {
1791             /* increment mixer count */
1792             (*DeviceCount)++;
1793         }
1794         else
1795         {
1796             /* reset mixer info in case of error */
1797             MixerInfo = NULL;
1798         }
1799     }
1800 
1801     /* check if the filter has an wave in node */
1802     NodeIndex = MMixerGetNodeIndexFromGuid(Topology, &KSNODETYPE_ADC);
1803     if (NodeIndex != MAXULONG)
1804     {
1805         /* it has */
1806         Status = MMixerInitializeFilter(MixerContext, MixerList, MixerData, MixerInfo, Topology, NodeIndex, TRUE, &MixerInfo);
1807 
1808         /* check for success */
1809         if (Status == MM_STATUS_SUCCESS)
1810         {
1811             /* increment mixer count */
1812             (*DeviceCount)++;
1813         }
1814 
1815     }
1816 
1817     /* TODO: apply hacks for Wave source line */
1818 
1819     /* activate midi devices */
1820     //MMixerInitializeMidiForFilter(MixerContext, MixerList, MixerData, Topology);
1821 
1822     /* done */
1823     return Status;
1824 }
1825 
1826 MIXER_STATUS
1827 MMixerAddEvent(
1828     IN PMIXER_CONTEXT MixerContext,
1829     IN OUT LPMIXER_INFO MixerInfo,
1830     IN PVOID MixerEventContext,
1831     IN PMIXER_EVENT MixerEventRoutine)
1832 {
1833     //KSE_NODE Property;
1834     //KSEVENTDATA EventData
1835     //ULONG BytesReturned;
1836     //MIXER_STATUS Status;
1837     PEVENT_NOTIFICATION_ENTRY EventNotification;
1838 
1839     EventNotification = (PEVENT_NOTIFICATION_ENTRY)MixerContext->Alloc(sizeof(EVENT_NOTIFICATION_ENTRY));
1840     if (!EventNotification)
1841     {
1842         /* not enough memory */
1843         return MM_STATUS_NO_MEMORY;
1844     }
1845 
1846     /* FIXME: what is it supposed to happen with KSEVENTDATA ? */
1847 #if 0
1848     /* setup request */
1849     Property.Event.Set = KSEVENTSETID_AudioControlChange;
1850     Property.Event.Flags = KSEVENT_TYPE_TOPOLOGY|KSEVENT_TYPE_ENABLE;
1851     Property.Event.Id = KSEVENT_CONTROL_CHANGE;
1852 
1853     Property.NodeId = NodeId;
1854     Property.Reserved = 0;
1855 
1856     Status = MixerContext->Control(MixerInfo->hMixer, IOCTL_KS_ENABLE_EVENT, (PVOID)&Property, sizeof(KSP_NODE), (PVOID)EventData, sizeof(KSEVENTDATA), &BytesReturned);
1857     if (Status != MM_STATUS_SUCCESS)
1858     {
1859         /* failed to add event */
1860         MixerContext->FreeEventData(EventData);
1861         return Status;
1862     }
1863 #endif
1864 
1865     /* initialize notification entry */
1866     EventNotification->MixerEventContext = MixerEventContext;
1867     EventNotification->MixerEventRoutine = MixerEventRoutine;
1868 
1869     /* store event */
1870     InsertTailList(&MixerInfo->EventList, &EventNotification->Entry);
1871     return MM_STATUS_SUCCESS;
1872 }
1873 
1874 MIXER_STATUS
1875 MMixerRemoveEvent(
1876     IN PMIXER_CONTEXT MixerContext,
1877     IN OUT LPMIXER_INFO MixerInfo,
1878     IN PVOID MixerEventContext,
1879     IN PMIXER_EVENT MixerEventRoutine)
1880 {
1881     PLIST_ENTRY EventList;
1882     PEVENT_NOTIFICATION_ENTRY NotificationEntry;
1883 
1884     /* Lookup through mixers */
1885     EventList = MixerInfo->EventList.Flink;
1886     while(EventList != &MixerInfo->EventList)
1887     {
1888         NotificationEntry = CONTAINING_RECORD(EventList, EVENT_NOTIFICATION_ENTRY, Entry);
1889         EventList = EventList->Flink;
1890         /* TODO: find a better way to identify an event ? */
1891         if(NotificationEntry->MixerEventRoutine == MixerEventRoutine &&
1892                 NotificationEntry->MixerEventContext == MixerEventContext)
1893         {
1894             DPRINT1("Freeing entry %p\n", NotificationEntry);
1895             /* We found the event to remove */
1896             RemoveEntryList(&NotificationEntry->Entry);
1897             MixerContext->Free(NotificationEntry);
1898         }
1899     }
1900     return MM_STATUS_SUCCESS;
1901 }
1902