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