xref: /reactos/sdk/lib/drivers/sound/mmixer/sup.c (revision 2b7246fd)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS Kernel Streaming
4  * FILE:            lib/drivers/sound/mmixer/sup.c
5  * PURPOSE:         Mixer Support Functions
6  * PROGRAMMER:      Johannes Anderwald
7  */
8 
9 #include "precomp.h"
10 
11 // #define NDEBUG
12 #include <debug.h>
13 
14 const GUID KSNODETYPE_SUM = {0xDA441A60L, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
15 const GUID KSNODETYPE_DAC = {0x507AE360L, 0xC554, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
16 const GUID KSNODETYPE_ADC = {0x4D837FE0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
17 const GUID KSNODETYPE_AGC = {0xE88C9BA0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
18 const GUID KSNODETYPE_LOUDNESS = {0x41887440L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
19 const GUID KSNODETYPE_MUTE =     {0x02B223C0L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
20 const GUID KSNODETYPE_TONE =     {0x7607E580L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
21 const GUID KSNODETYPE_VOLUME =   {0x3A5ACC00L, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
22 const GUID KSNODETYPE_PEAKMETER = {0xa085651e, 0x5f0d, 0x4b36, {0xa8, 0x69, 0xd1, 0x95, 0xd6, 0xab, 0x4b, 0x9e}};
23 const GUID KSNODETYPE_MUX =       {0x2CEAF780, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
24 const GUID KSNODETYPE_STEREO_WIDE = {0xA9E69800L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
25 const GUID KSNODETYPE_CHORUS =      {0x20173F20L, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
26 const GUID KSNODETYPE_REVERB =      {0xEF0328E0L, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
27 const GUID KSNODETYPE_SUPERMIX =    {0xE573ADC0L, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
28 
29 const GUID KSPROPSETID_Audio = {0x45FFAAA0L, 0x6E1B, 0x11D0, {0xBC, 0xF2, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};
30 const GUID KSPROPSETID_Pin                     = {0x8C134960L, 0x51AD, 0x11CF, {0x87, 0x8A, 0x94, 0xF8, 0x01, 0xC1, 0x00, 0x00}};
31 const GUID KSPROPSETID_General                  = {0x1464EDA5L, 0x6A8F, 0x11D1, {0x9A, 0xA7, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
32 const GUID KSPROPSETID_Topology                 = {0x720D4AC0L, 0x7533, 0x11D0, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
33 const GUID KSEVENTSETID_AudioControlChange      = {0xE85E9698L, 0xFA2F, 0x11D1, {0x95, 0xBD, 0x00, 0xC0, 0x4F, 0xB9, 0x25, 0xD3}};
34 
35 const GUID KSDATAFORMAT_TYPE_MUSIC = {0xE725D360L, 0x62CC, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
36 const GUID KSDATAFORMAT_SUBTYPE_MIDI = {0x1D262760L, 0xE957, 0x11CF, {0xA5, 0xD6, 0x28, 0xDB, 0x04, 0xC1, 0x00, 0x00}};
37 const GUID KSDATAFORMAT_SPECIFIER_NONE = {0x0F6417D6L, 0xC318, 0x11D0, {0xA4, 0x3F, 0x00, 0xA0, 0xC9, 0x22, 0x31, 0x96}};
38 
39 MIXER_STATUS
40 MMixerVerifyContext(
41     IN PMIXER_CONTEXT MixerContext)
42 {
43     if (MixerContext->SizeOfStruct != sizeof(MIXER_CONTEXT))
44         return MM_STATUS_INVALID_PARAMETER;
45 
46     if (!MixerContext->Alloc || !MixerContext->Control || !MixerContext->Free || !MixerContext->Open ||
47         !MixerContext->AllocEventData || !MixerContext->FreeEventData ||
48         !MixerContext->Close || !MixerContext->OpenKey || !MixerContext->QueryKeyValue || !MixerContext->CloseKey)
49         return MM_STATUS_INVALID_PARAMETER;
50 
51     if (!MixerContext->MixerContext)
52         return MM_STATUS_INVALID_PARAMETER;
53 
54     return MM_STATUS_SUCCESS;
55 }
56 
57 LPMIXERLINE_EXT
58 MMixerGetMixerLineContainingNodeId(
59     IN LPMIXER_INFO MixerInfo,
60     IN ULONG NodeID)
61 {
62     PLIST_ENTRY Entry, ControlEntry;
63     LPMIXERLINE_EXT MixerLineSrc;
64     LPMIXERCONTROL_EXT MixerControl;
65 
66     /* get first entry */
67     Entry = MixerInfo->LineList.Flink;
68 
69     while(Entry != &MixerInfo->LineList)
70     {
71         MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
72 
73         ControlEntry = MixerLineSrc->ControlsList.Flink;
74         while(ControlEntry != &MixerLineSrc->ControlsList)
75         {
76             MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry);
77             if (MixerControl->NodeID == NodeID)
78             {
79                 return MixerLineSrc;
80             }
81             ControlEntry = ControlEntry->Flink;
82         }
83         Entry = Entry->Flink;
84     }
85 
86     return NULL;
87 }
88 
89 VOID
90 MMixerGetLowestLogicalTopologyPinOffsetFromArray(
91     IN ULONG LogicalPinArrayCount,
92     IN PULONG LogicalPinArray,
93     OUT PULONG PinOffset)
94 {
95     ULONG Index;
96     ULONG LowestId = 0;
97 
98     for(Index = 1; Index < LogicalPinArrayCount; Index++)
99     {
100         if (LogicalPinArray[Index] != MAXULONG)
101         {
102             /* sanity check: logical pin id must be unique */
103             ASSERT(LogicalPinArray[Index] != LogicalPinArray[LowestId]);
104         }
105 
106         if (LogicalPinArray[Index] < LogicalPinArray[LowestId])
107             LowestId = Index;
108     }
109 
110     /* store result */
111     *PinOffset = LowestId;
112 }
113 
114 VOID
115 MMixerFreeMixerInfo(
116     IN PMIXER_CONTEXT MixerContext,
117     IN LPMIXER_INFO MixerInfo)
118 {
119     /* UNIMPLEMENTED;
120      * FIXME
121      * free all lines
122      */
123 
124     MixerContext->Free((PVOID)MixerInfo);
125 }
126 
127 LPMIXER_DATA
128 MMixerGetMixerDataByDeviceHandle(
129     IN PMIXER_CONTEXT MixerContext,
130     IN HANDLE hDevice)
131 {
132     LPMIXER_DATA MixerData;
133     PLIST_ENTRY Entry;
134     PMIXER_LIST MixerList;
135 
136     /* get mixer list */
137     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
138 
139     if (!MixerList->MixerDataCount)
140         return NULL;
141 
142     Entry = MixerList->MixerData.Flink;
143 
144     while(Entry != &MixerList->MixerData)
145     {
146         MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
147 
148         if (MixerData->hDevice == hDevice)
149             return MixerData;
150 
151         /* move to next mixer entry */
152         Entry = Entry->Flink;
153     }
154     return NULL;
155 }
156 
157 LPMIXER_INFO
158 MMixerGetMixerInfoByIndex(
159     IN PMIXER_CONTEXT MixerContext,
160     IN ULONG MixerIndex)
161 {
162     LPMIXER_INFO MixerInfo;
163     PLIST_ENTRY Entry;
164     PMIXER_LIST MixerList;
165     ULONG Index = 0;
166 
167     /* get mixer list */
168     MixerList = (PMIXER_LIST)MixerContext->MixerContext;
169 
170     if (!MixerList->MixerListCount)
171         return NULL;
172 
173     Entry = MixerList->MixerList.Flink;
174 
175     while(Entry != &MixerList->MixerList)
176     {
177         MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
178 
179         if (Index == MixerIndex)
180             return MixerInfo;
181 
182         /* move to next mixer entry */
183         Index++;
184         Entry = Entry->Flink;
185     }
186 
187     return NULL;
188 }
189 
190 MIXER_STATUS
191 MMixerGetMixerByName(
192     IN PMIXER_LIST MixerList,
193     IN LPWSTR MixerName,
194     OUT LPMIXER_INFO *OutMixerInfo)
195 {
196     LPMIXER_INFO MixerInfo;
197     PLIST_ENTRY Entry;
198 
199     Entry = MixerList->MixerList.Flink;
200     while(Entry != &MixerList->MixerList)
201     {
202         MixerInfo = (LPMIXER_INFO)CONTAINING_RECORD(Entry, MIXER_INFO, Entry);
203 
204         DPRINT1("MixerName %S MixerName %S\n", MixerInfo->MixCaps.szPname, MixerName);
205         if (wcsicmp(MixerInfo->MixCaps.szPname, MixerName) == 0)
206         {
207             *OutMixerInfo = MixerInfo;
208             return MM_STATUS_SUCCESS;
209         }
210         /* move to next mixer entry */
211         Entry = Entry->Flink;
212     }
213 
214     return MM_STATUS_UNSUCCESSFUL;
215 }
216 
217 LPMIXERLINE_EXT
218 MMixerGetSourceMixerLineByLineId(
219     LPMIXER_INFO MixerInfo,
220     DWORD dwLineID)
221 {
222     PLIST_ENTRY Entry;
223     LPMIXERLINE_EXT MixerLineSrc;
224 
225     /* get first entry */
226     Entry = MixerInfo->LineList.Flink;
227 
228     while(Entry != &MixerInfo->LineList)
229     {
230         MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
231         DPRINT("dwLineID %x dwLineID %x MixerLineSrc %p\n", MixerLineSrc->Line.dwLineID, dwLineID, MixerLineSrc);
232         if (MixerLineSrc->Line.dwLineID == dwLineID)
233             return MixerLineSrc;
234 
235         Entry = Entry->Flink;
236     }
237 
238     return NULL;
239 }
240 
241 LPGUID
242 MMixerGetNodeType(
243     IN PKSMULTIPLE_ITEM MultipleItem,
244     IN ULONG Index)
245 {
246     LPGUID NodeType;
247 
248     ASSERT(Index < MultipleItem->Count);
249 
250     NodeType = (LPGUID)(MultipleItem + 1);
251     return &NodeType[Index];
252 }
253 
254 LPMIXERLINE_EXT
255 MMixerGetSourceMixerLineByComponentType(
256     LPMIXER_INFO MixerInfo,
257     DWORD dwComponentType)
258 {
259     PLIST_ENTRY Entry;
260     LPMIXERLINE_EXT MixerLineSrc;
261 
262     /* get first entry */
263     Entry = MixerInfo->LineList.Flink;
264 
265     while(Entry != &MixerInfo->LineList)
266     {
267         MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
268         if (MixerLineSrc->Line.dwComponentType == dwComponentType)
269             return MixerLineSrc;
270 
271         Entry = Entry->Flink;
272     }
273 
274     return NULL;
275 }
276 
277 MIXER_STATUS
278 MMixerGetMixerControlById(
279     LPMIXER_INFO MixerInfo,
280     DWORD dwControlID,
281     LPMIXERLINE_EXT *OutMixerLine,
282     LPMIXERCONTROL_EXT *OutMixerControl,
283     PULONG NodeId)
284 {
285     PLIST_ENTRY Entry, ControlEntry;
286     LPMIXERLINE_EXT MixerLineSrc;
287     LPMIXERCONTROL_EXT MixerControl;
288 
289     /* get first entry */
290     Entry = MixerInfo->LineList.Flink;
291 
292     while(Entry != &MixerInfo->LineList)
293     {
294         MixerLineSrc = (LPMIXERLINE_EXT)CONTAINING_RECORD(Entry, MIXERLINE_EXT, Entry);
295 
296         ControlEntry = MixerLineSrc->ControlsList.Flink;
297         while(ControlEntry != &MixerLineSrc->ControlsList)
298         {
299             MixerControl = (LPMIXERCONTROL_EXT)CONTAINING_RECORD(ControlEntry, MIXERCONTROL_EXT, Entry);
300             if (MixerControl->Control.dwControlID == dwControlID)
301             {
302                 if (OutMixerLine)
303                     *OutMixerLine = MixerLineSrc;
304                 if (OutMixerControl)
305                     *OutMixerControl = MixerControl;
306                 if (NodeId)
307                     *NodeId = MixerControl->NodeID;
308                 return MM_STATUS_SUCCESS;
309             }
310             ControlEntry = ControlEntry->Flink;
311         }
312         Entry = Entry->Flink;
313     }
314 
315     return MM_STATUS_UNSUCCESSFUL;
316 }
317 
318 ULONG
319 MMixerGetVolumeControlIndex(
320     LPMIXERVOLUME_DATA VolumeData,
321     LONG Value)
322 {
323     ULONG Index;
324 
325     for(Index = 0; Index < VolumeData->ValuesCount; Index++)
326     {
327         if (VolumeData->Values[Index] > Value)
328         {
329             return VolumeData->InputSteppingDelta * Index;
330         }
331     }
332     return VolumeData->InputSteppingDelta * (VolumeData->ValuesCount-1);
333 }
334 
335 VOID
336 MMixerNotifyControlChange(
337     IN PMIXER_CONTEXT MixerContext,
338     IN LPMIXER_INFO MixerInfo,
339     IN ULONG NotificationType,
340     IN ULONG Value)
341 {
342     PLIST_ENTRY Entry;
343     PEVENT_NOTIFICATION_ENTRY NotificationEntry;
344 
345     /* enumerate list and perform notification */
346     Entry = MixerInfo->EventList.Flink;
347     while(Entry != &MixerInfo->EventList)
348     {
349         /* get notification entry offset */
350         NotificationEntry = (PEVENT_NOTIFICATION_ENTRY)CONTAINING_RECORD(Entry, EVENT_NOTIFICATION_ENTRY, Entry);
351 
352         if (NotificationEntry->MixerEventRoutine)
353         {
354             /* now perform the callback */
355             NotificationEntry->MixerEventRoutine(NotificationEntry->MixerEventContext, (HANDLE)MixerInfo, NotificationType, Value);
356         }
357 
358         /* move to next notification entry */
359         Entry = Entry->Flink;
360     }
361 }
362 
363 MIXER_STATUS
364 MMixerSetGetMuteControlDetails(
365     IN PMIXER_CONTEXT MixerContext,
366     IN LPMIXER_INFO MixerInfo,
367     IN LPMIXERCONTROL_EXT MixerControl,
368     IN ULONG dwLineID,
369     IN LPMIXERCONTROLDETAILS MixerControlDetails,
370     IN ULONG bSet)
371 {
372     LPMIXERCONTROLDETAILS_BOOLEAN Input;
373     LONG Value;
374     MIXER_STATUS Status;
375 
376     if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_BOOLEAN))
377         return MM_STATUS_INVALID_PARAMETER;
378 
379     /* get input */
380     Input = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
381 
382     /* FIXME SEH */
383     if (bSet)
384         Value = Input->fValue;
385 
386     /* set control details */
387     Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, MixerControl->NodeID, bSet, KSPROPERTY_AUDIO_MUTE, 0, &Value);
388 
389     if (Status != MM_STATUS_SUCCESS)
390         return Status;
391 
392     /* FIXME SEH */
393     if (!bSet)
394     {
395         Input->fValue = Value;
396         return Status;
397     }
398     else
399     {
400         /* notify wdmaud clients MM_MIXM_LINE_CHANGE dwLineID */
401         MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_LINE_CHANGE, dwLineID);
402     }
403 
404     return Status;
405 }
406 
407 MIXER_STATUS
408 MMixerSetGetMuxControlDetails(
409     IN PMIXER_CONTEXT MixerContext,
410     IN LPMIXER_INFO MixerInfo,
411     IN ULONG NodeId,
412     IN ULONG bSet,
413     IN ULONG Flags,
414     IN LPMIXERCONTROL_EXT MixerControl,
415     IN LPMIXERCONTROLDETAILS MixerControlDetails,
416     IN LPMIXERLINE_EXT MixerLine)
417 {
418     MIXER_STATUS Status;
419     PULONG LogicalNodes, ConnectedNodes;
420     ULONG LogicalNodesCount, ConnectedNodesCount, Index, CurLogicalPinOffset, BytesReturned, OldLogicalPinOffset;
421     LPMIXER_DATA MixerData;
422     LPMIXERCONTROLDETAILS_LISTTEXTW ListText;
423     LPMIXERCONTROLDETAILS_BOOLEAN Values;
424     LPMIXERLINE_EXT SourceLine;
425     KSNODEPROPERTY Request;
426 
427     DPRINT("MixerControlDetails %p\n", MixerControlDetails);
428     DPRINT("bSet %lx\n", bSet);
429     DPRINT("Flags %lx\n", Flags);
430     DPRINT("NodeId %lu\n", MixerControl->NodeID);
431     DPRINT("MixerControlDetails dwControlID %lu\n", MixerControlDetails->dwControlID);
432     DPRINT("MixerControlDetails cChannels %lu\n", MixerControlDetails->cChannels);
433     DPRINT("MixerControlDetails cMultipleItems %lu\n", MixerControlDetails->cMultipleItems);
434     DPRINT("MixerControlDetails cbDetails %lu\n", MixerControlDetails->cbDetails);
435     DPRINT("MixerControlDetails paDetails %p\n", MixerControlDetails->paDetails);
436 
437     if (MixerControl->Control.fdwControl & MIXERCONTROL_CONTROLF_UNIFORM)
438     {
439         /* control acts uniform */
440         if (MixerControlDetails->cChannels != 1)
441         {
442             /* expected 1 channel */
443             DPRINT1("Expected 1 channel but got %lu\n", MixerControlDetails->cChannels);
444             return MM_STATUS_UNSUCCESSFUL;
445         }
446     }
447 
448     /* check if multiple items match */
449     if (MixerControlDetails->cMultipleItems != MixerControl->Control.cMultipleItems)
450     {
451         DPRINT1("MultipleItems mismatch %lu expected %lu\n", MixerControlDetails->cMultipleItems, MixerControl->Control.cMultipleItems);
452         return MM_STATUS_UNSUCCESSFUL;
453     }
454 
455     if (bSet)
456     {
457         if ((Flags & MIXER_SETCONTROLDETAILSF_QUERYMASK) == MIXER_SETCONTROLDETAILSF_CUSTOM)
458         {
459             /* tell me when this is hit */
460             ASSERT(FALSE);
461         }
462         else if ((Flags & (MIXER_SETCONTROLDETAILSF_VALUE | MIXER_SETCONTROLDETAILSF_CUSTOM)) == MIXER_SETCONTROLDETAILSF_VALUE)
463         {
464             /* sanity check */
465             ASSERT(bSet == TRUE);
466             ASSERT(MixerControlDetails->cbDetails == sizeof(MIXERCONTROLDETAILS_BOOLEAN));
467 
468             Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
469             CurLogicalPinOffset = MAXULONG;
470             for(Index = 0; Index < MixerControlDetails->cMultipleItems; Index++)
471             {
472                 if (Values[Index].fValue)
473                 {
474                     /* mux can only activate one line at a time */
475                     ASSERT(CurLogicalPinOffset == MAXULONG);
476                     CurLogicalPinOffset = Index;
477                 }
478             }
479 
480             /* setup request */
481             Request.NodeId = NodeId;
482             Request.Reserved = 0;
483             Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
484             Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
485             Request.Property.Set = KSPROPSETID_Audio;
486 
487             /* perform getting source */
488             Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
489             if (Status != MM_STATUS_SUCCESS)
490             {
491                 /* failed to get source */
492                 return Status;
493             }
494 
495             DPRINT("OldLogicalPinOffset %lu CurLogicalPinOffset %lu\n", OldLogicalPinOffset, CurLogicalPinOffset);
496 
497             if (OldLogicalPinOffset == CurLogicalPinOffset)
498             {
499                 /* cannot be unselected */
500                 return MM_STATUS_UNSUCCESSFUL;
501             }
502 
503             /* perform setting source */
504             Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_SET;
505             Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &CurLogicalPinOffset, sizeof(ULONG), &BytesReturned);
506             if (Status != MM_STATUS_SUCCESS)
507             {
508                 /* failed to set source */
509                 return Status;
510             }
511 
512             /* notify control change */
513             MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID );
514 
515             return Status;
516         }
517     }
518     else
519     {
520         if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_VALUE)
521         {
522             /* setup request */
523             Request.NodeId = NodeId;
524             Request.Reserved = 0;
525             Request.Property.Flags = KSPROPERTY_TYPE_TOPOLOGY | KSPROPERTY_TYPE_GET;
526             Request.Property.Id = KSPROPERTY_AUDIO_MUX_SOURCE;
527             Request.Property.Set = KSPROPSETID_Audio;
528 
529             /* perform getting source */
530             Status = MixerContext->Control(MixerControl->hDevice, IOCTL_KS_PROPERTY, (PVOID)&Request, sizeof(KSNODEPROPERTY), &OldLogicalPinOffset, sizeof(ULONG), &BytesReturned);
531             if (Status != MM_STATUS_SUCCESS)
532             {
533                 /* failed to get source */
534                 return Status;
535             }
536 
537             /* gets the corresponding mixer data */
538             MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
539 
540             /* sanity check */
541             ASSERT(MixerData);
542             ASSERT(MixerData->Topology);
543             ASSERT(MixerData->MixerInfo == MixerInfo);
544 
545             /* now allocate logical pin array */
546             Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes);
547             if (Status != MM_STATUS_SUCCESS)
548             {
549                 /* no memory */
550                 return MM_STATUS_NO_MEMORY;
551             }
552 
553             /* get logical pin nodes */
554             MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
555 
556             /* sanity check */
557             ASSERT(LogicalNodesCount == MixerControlDetails->cMultipleItems);
558             ASSERT(LogicalNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
559 
560             Values = (LPMIXERCONTROLDETAILS_BOOLEAN)MixerControlDetails->paDetails;
561             for(Index = 0; Index < LogicalNodesCount; Index++)
562             {
563                 /* getting logical pin offset */
564                 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
565 
566                 if (CurLogicalPinOffset == OldLogicalPinOffset)
567                 {
568                     /* mark index as active */
569                     Values[Index].fValue = TRUE;
570                 }
571                 else
572                 {
573                     /* index not active */
574                     Values[Index].fValue = FALSE;
575                 }
576 
577                 /* mark offset as consumed */
578                 LogicalNodes[CurLogicalPinOffset] = MAXULONG;
579             }
580 
581             /* cleanup */
582             MixerContext->Free(LogicalNodes);
583 
584             /* done */
585             return MM_STATUS_SUCCESS;
586         }
587         else if ((Flags & MIXER_GETCONTROLDETAILSF_QUERYMASK) == MIXER_GETCONTROLDETAILSF_LISTTEXT)
588         {
589             /* sanity check */
590             ASSERT(bSet == FALSE);
591 
592             /* gets the corresponding mixer data */
593             MixerData = MMixerGetMixerDataByDeviceHandle(MixerContext, MixerControl->hDevice);
594 
595             /* sanity check */
596             ASSERT(MixerData);
597             ASSERT(MixerData->Topology);
598             ASSERT(MixerData->MixerInfo == MixerInfo);
599 
600             /* now allocate logical pin array */
601             Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &LogicalNodes);
602             if (Status != MM_STATUS_SUCCESS)
603             {
604                 /* no memory */
605                 return MM_STATUS_NO_MEMORY;
606             }
607 
608             /* allocate connected node array */
609             Status = MMixerAllocateTopologyNodeArray(MixerContext, MixerData->Topology, &ConnectedNodes);
610             if (Status != MM_STATUS_SUCCESS)
611             {
612                 /* no memory */
613                 MixerContext->Free(LogicalNodes);
614                 return MM_STATUS_NO_MEMORY;
615             }
616 
617             /* get logical pin nodes */
618             MMixerGetConnectedFromLogicalTopologyPins(MixerData->Topology, MixerControl->NodeID, &LogicalNodesCount, LogicalNodes);
619 
620             /* get connected nodes */
621             MMixerGetNextNodesFromNodeIndex(MixerContext, MixerData->Topology, MixerControl->NodeID, TRUE, &ConnectedNodesCount, ConnectedNodes);
622 
623             /* sanity check */
624             ASSERT(ConnectedNodesCount == LogicalNodesCount);
625             ASSERT(ConnectedNodesCount == MixerControlDetails->cMultipleItems);
626             ASSERT(ConnectedNodesCount == MixerControl->Control.Metrics.dwReserved[0]);
627 
628             ListText = (LPMIXERCONTROLDETAILS_LISTTEXTW)MixerControlDetails->paDetails;
629 
630             for(Index = 0; Index < ConnectedNodesCount; Index++)
631             {
632                 /* getting logical pin offset */
633                 MMixerGetLowestLogicalTopologyPinOffsetFromArray(LogicalNodesCount, LogicalNodes, &CurLogicalPinOffset);
634 
635                 /* get mixer line with that node */
636                 SourceLine = MMixerGetMixerLineContainingNodeId(MixerInfo, ConnectedNodes[CurLogicalPinOffset]);
637 
638                 /* sanity check */
639                 ASSERT(SourceLine);
640 
641                 DPRINT1("PinOffset %lu LogicalPin %lu NodeId %lu LineName %S\n", CurLogicalPinOffset, LogicalNodes[CurLogicalPinOffset], ConnectedNodes[CurLogicalPinOffset], SourceLine->Line.szName);
642 
643                 /* copy details */
644                 ListText[Index].dwParam1 = SourceLine->Line.dwLineID;
645                 ListText[Index].dwParam2 = SourceLine->Line.dwComponentType;
646                 MixerContext->Copy(ListText[Index].szName, SourceLine->Line.szName, (wcslen(SourceLine->Line.szName) + 1) * sizeof(WCHAR));
647 
648                 /* mark offset as consumed */
649                 LogicalNodes[CurLogicalPinOffset] = MAXULONG;
650             }
651 
652             /* cleanup */
653             MixerContext->Free(LogicalNodes);
654             MixerContext->Free(ConnectedNodes);
655 
656             /* done */
657             return MM_STATUS_SUCCESS;
658         }
659     }
660 
661     return MM_STATUS_NOT_IMPLEMENTED;
662 }
663 
664 MIXER_STATUS
665 MMixerSetGetVolumeControlDetails(
666     IN PMIXER_CONTEXT MixerContext,
667     IN LPMIXER_INFO MixerInfo,
668     IN ULONG NodeId,
669     IN ULONG bSet,
670     LPMIXERCONTROL_EXT MixerControl,
671     IN LPMIXERCONTROLDETAILS MixerControlDetails,
672     LPMIXERLINE_EXT MixerLine)
673 {
674     LPMIXERCONTROLDETAILS_UNSIGNED Input;
675     LONG Value;
676     ULONG Index, Channel = 0;
677     ULONG dwValue;
678     MIXER_STATUS Status;
679     LPMIXERVOLUME_DATA VolumeData;
680 
681     if (MixerControlDetails->cbDetails != sizeof(MIXERCONTROLDETAILS_SIGNED))
682         return MM_STATUS_INVALID_PARAMETER;
683 
684     VolumeData = (LPMIXERVOLUME_DATA)MixerControl->ExtraData;
685     if (!VolumeData)
686         return MM_STATUS_UNSUCCESSFUL;
687 
688     /* get input */
689     Input = (LPMIXERCONTROLDETAILS_UNSIGNED)MixerControlDetails->paDetails;
690     if (!Input)
691         return MM_STATUS_UNSUCCESSFUL; /* to prevent dereferencing NULL */
692 
693     if (bSet)
694     {
695         /* FIXME SEH */
696         Value = Input->dwValue;
697         Index = Value / VolumeData->InputSteppingDelta;
698 
699         if (Index >= VolumeData->ValuesCount)
700         {
701             DPRINT1("Index %u out of bounds %u \n", Index, VolumeData->ValuesCount);
702             return MM_STATUS_INVALID_PARAMETER;
703         }
704 
705         Value = VolumeData->Values[Index];
706     }
707 
708     /* set control details */
709     if (bSet)
710     {
711         /* TODO */
712         Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 0, &Value);
713         Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, 1, &Value);
714     }
715     else
716     {
717         Status = MMixerSetGetControlDetails(MixerContext, MixerControl->hDevice, NodeId, bSet, KSPROPERTY_AUDIO_VOLUMELEVEL, Channel, &Value);
718     }
719 
720     if (!bSet)
721     {
722         dwValue = MMixerGetVolumeControlIndex(VolumeData, (LONG)Value);
723         /* FIXME SEH */
724         Input->dwValue = dwValue;
725     }
726     else
727     {
728         /* notify clients of a line change  MM_MIXM_CONTROL_CHANGE with MixerControl->dwControlID */
729         MMixerNotifyControlChange(MixerContext, MixerInfo, MM_MIXM_CONTROL_CHANGE, MixerControl->Control.dwControlID);
730     }
731     return Status;
732 }
733 
734 LPMIXER_DATA
735 MMixerGetDataByDeviceId(
736     IN PMIXER_LIST MixerList,
737     IN ULONG DeviceId)
738 {
739     PLIST_ENTRY Entry;
740     LPMIXER_DATA MixerData;
741 
742     Entry = MixerList->MixerData.Flink;
743     while(Entry != &MixerList->MixerData)
744     {
745         MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
746         if (MixerData->DeviceId == DeviceId)
747         {
748             return MixerData;
749         }
750         Entry = Entry->Flink;
751     }
752     return NULL;
753 }
754 
755 LPMIXER_DATA
756 MMixerGetDataByDeviceName(
757     IN PMIXER_LIST MixerList,
758     IN LPWSTR DeviceName)
759 {
760     PLIST_ENTRY Entry;
761     LPMIXER_DATA MixerData;
762 
763     Entry = MixerList->MixerData.Flink;
764     while(Entry != &MixerList->MixerData)
765     {
766         MixerData = (LPMIXER_DATA)CONTAINING_RECORD(Entry, MIXER_DATA, Entry);
767         if (wcsicmp(&DeviceName[2], &MixerData->DeviceName[2]) == 0)
768         {
769             /* found entry */
770             return MixerData;
771         }
772         Entry = Entry->Flink;
773     }
774     return NULL;
775 }
776 
777 MIXER_STATUS
778 MMixerCreateMixerData(
779     IN PMIXER_CONTEXT MixerContext,
780     IN PMIXER_LIST MixerList,
781     IN ULONG DeviceId,
782     IN LPWSTR DeviceName,
783     IN HANDLE hDevice,
784     IN HANDLE hKey)
785 {
786     LPMIXER_DATA MixerData;
787 
788     MixerData = (LPMIXER_DATA)MixerContext->Alloc(sizeof(MIXER_DATA));
789     if (!MixerData)
790         return MM_STATUS_NO_MEMORY;
791 
792     MixerData->DeviceId = DeviceId;
793     MixerData->DeviceName = DeviceName;
794     MixerData->hDevice = hDevice;
795     MixerData->hDeviceInterfaceKey = hKey;
796     MixerData->Topology = NULL;
797 
798     InsertTailList(&MixerList->MixerData, &MixerData->Entry);
799     MixerList->MixerDataCount++;
800     return MM_STATUS_SUCCESS;
801 }
802 
803 MIXER_STATUS
804 MMixerGetDeviceNameWithComponentId(
805     IN PMIXER_CONTEXT MixerContext,
806     IN HANDLE hMixer,
807     OUT LPWSTR OutDeviceName)
808 {
809     MIXER_STATUS Status;
810     KSPROPERTY Property;
811     KSCOMPONENTID ComponentId;
812     ULONG Length;
813     UNICODE_STRING GuidString;
814     ULONG ResultLength, KeyType;
815     HANDLE hMediaKey, hGuidKey;
816     LPWSTR DeviceName;
817 
818     /* prepare property */
819     Property.Flags = KSPROPERTY_TYPE_GET;
820     Property.Set = KSPROPSETID_General;
821     Property.Id = KSPROPERTY_GENERAL_COMPONENTID;
822 
823     /* try get component id */
824     Status = MixerContext->Control(hMixer, IOCTL_KS_PROPERTY, (PVOID)&Property, sizeof(KSPROPERTY), &ComponentId, sizeof(KSCOMPONENTID), &Length);
825 
826     if (Status == MM_STATUS_SUCCESS)
827     {
828         Status = MixerContext->OpenKey(NULL, L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\MediaCategories", KEY_READ, &hMediaKey);
829         if (Status == MM_STATUS_SUCCESS)
830         {
831             RtlStringFromGUID(&ComponentId.Name, &GuidString);
832             Status = MixerContext->OpenKey(hMediaKey, GuidString.Buffer, KEY_READ, &hGuidKey);
833             RtlFreeUnicodeString(&GuidString);
834             if (Status == MM_STATUS_SUCCESS)
835             {
836                 Status = MixerContext->QueryKeyValue(hGuidKey, L"Name", (PVOID*)&DeviceName, &ResultLength, &KeyType);
837                 if (Status == MM_STATUS_SUCCESS)
838                 {
839                     MixerContext->Copy(OutDeviceName, DeviceName, min(ResultLength, (MAXPNAMELEN-1)*2));
840                     MixerContext->Free(DeviceName);
841                 }
842 
843                 MixerContext->CloseKey(hGuidKey);
844             }
845             MixerContext->CloseKey(hMediaKey);
846         }
847     }
848     return Status;
849 }
850 
851 MIXER_STATUS
852 MMixerGetDeviceName(
853     IN PMIXER_CONTEXT MixerContext,
854     OUT LPWSTR DeviceName,
855     IN HANDLE hKey)
856 {
857     LPWSTR Name;
858     HANDLE hTemp;
859     ULONG Length;
860     ULONG Type;
861     MIXER_STATUS Status;
862 
863     Status = MixerContext->QueryKeyValue(hKey, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
864     if (Status == MM_STATUS_SUCCESS)
865     {
866         /* copy device name */
867         MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
868 
869         /* make sure its null terminated */
870         DeviceName[MAXPNAMELEN-1] = L'\0';
871 
872         /* free device name */
873         MixerContext->Free(Name);
874 
875         /* done */
876         return Status;
877     }
878 
879     Status = MixerContext->OpenKey(hKey, L"Device Parameters", KEY_READ, &hTemp);
880     if (Status != MM_STATUS_SUCCESS)
881         return Status;
882 
883     Status = MixerContext->QueryKeyValue(hTemp, L"FriendlyName", (PVOID*)&Name, &Length, &Type);
884     if (Status == MM_STATUS_SUCCESS)
885     {
886         /* copy device name */
887         MixerContext->Copy(DeviceName, Name, min(wcslen(Name), MAXPNAMELEN-1) * sizeof(WCHAR));
888 
889         /* make sure its null terminated */
890         DeviceName[MAXPNAMELEN-1] = L'\0';
891 
892         /* free device name */
893         MixerContext->Free(Name);
894     }
895 
896     MixerContext->CloseKey(hTemp);
897     return Status;
898 }
899 
900 VOID
901 MMixerInitializePinConnect(
902     IN OUT PKSPIN_CONNECT PinConnect,
903     IN ULONG PinId)
904 {
905     PinConnect->Interface.Set = KSINTERFACESETID_Standard;
906     PinConnect->Interface.Id = KSINTERFACE_STANDARD_STREAMING;
907     PinConnect->Interface.Flags = 0;
908     PinConnect->Medium.Set = KSMEDIUMSETID_Standard;
909     PinConnect->Medium.Id = KSMEDIUM_TYPE_ANYINSTANCE;
910     PinConnect->Medium.Flags = 0;
911     PinConnect->PinToHandle = NULL;
912     PinConnect->PinId = PinId;
913     PinConnect->Priority.PriorityClass = KSPRIORITY_NORMAL;
914     PinConnect->Priority.PrioritySubClass = 1;
915 }
916