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