2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel Streaming
4 * FILE: drivers/wdm/audio/legacy/wdmaud/mixer.c
5 * PURPOSE: System Audio graph builder
6 * PROGRAMMER: Andrew Greenwood
11 const GUID KSNODETYPE_DAC
= {0x507AE360L
, 0xC554, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
12 const GUID KSNODETYPE_ADC
= {0x4D837FE0L
, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
13 const GUID KSNODETYPE_AGC
= {0xE88C9BA0L
, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
14 const GUID KSNODETYPE_LOUDNESS
= {0x41887440L
, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
15 const GUID KSNODETYPE_MUTE
= {0x02B223C0L
, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
16 const GUID KSNODETYPE_TONE
= {0x7607E580L
, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
17 const GUID KSNODETYPE_VOLUME
= {0x3A5ACC00L
, 0xC557, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
18 const GUID KSNODETYPE_PEAKMETER
= {0xa085651e, 0x5f0d, 0x4b36, {0xa8, 0x69, 0xd1, 0x95, 0xd6, 0xab, 0x4b, 0x9e}};
19 const GUID KSNODETYPE_MUX
= {0x2CEAF780, 0xC556, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
20 const GUID KSNODETYPE_STEREO_WIDE
= {0xA9E69800L
, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
21 const GUID KSNODETYPE_CHORUS
= {0x20173F20L
, 0xC559, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
22 const GUID KSNODETYPE_REVERB
= {0xEF0328E0L
, 0xC558, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
23 const GUID KSNODETYPE_SUPERMIX
= {0xE573ADC0L
, 0xC555, 0x11D0, {0x8A, 0x2B, 0x00, 0xA0, 0xC9, 0x25, 0x5A, 0xC1}};
28 LPMIXER_INFO MixerInfo
,
32 LPMIXERLINE_SOURCE MixerLineSrc
;
35 Entry
= MixerInfo
->SourceLineList
.Flink
;
37 while(Entry
!= &MixerInfo
->SourceLineList
)
39 MixerLineSrc
= (LPMIXERLINE_SOURCE
)CONTAINING_RECORD(Entry
, MIXERLINE_SOURCE
, Entry
);
40 DPRINT("dwSource %x dwSource %x\n", MixerLineSrc
->Line
.dwSource
, dwSource
);
41 if (MixerLineSrc
->Line
.dwSource
== dwSource
)
51 GetSourceMixerLineByLineId(
52 LPMIXER_INFO MixerInfo
,
56 LPMIXERLINE_SOURCE MixerLineSrc
;
59 Entry
= MixerInfo
->SourceLineList
.Flink
;
61 while(Entry
!= &MixerInfo
->SourceLineList
)
63 MixerLineSrc
= (LPMIXERLINE_SOURCE
)CONTAINING_RECORD(Entry
, MIXERLINE_SOURCE
, Entry
);
64 DPRINT("dwLineID %x dwLineID %x\n", MixerLineSrc
->Line
.dwLineID
, dwLineID
);
65 if (MixerLineSrc
->Line
.dwLineID
== dwLineID
)
78 IN PFILE_OBJECT FileObject
)
82 ULONG NumPins
, BytesReturned
;
84 Pin
.Flags
= KSPROPERTY_TYPE_GET
;
85 Pin
.Set
= KSPROPSETID_Pin
;
86 Pin
.Id
= KSPROPERTY_PIN_CTYPES
;
88 Status
= KsSynchronousIoControlDevice(FileObject
, KernelMode
, IOCTL_KS_PROPERTY
, (PVOID
)&Pin
, sizeof(KSPROPERTY
), (PVOID
)&NumPins
, sizeof(ULONG
), &BytesReturned
);
89 if (!NT_SUCCESS(Status
))
97 GetSysAudioDeviceCount(
98 IN PDEVICE_OBJECT DeviceObject
)
100 PWDMAUD_DEVICE_EXTENSION DeviceExtension
;
102 ULONG Count
, BytesReturned
;
105 /* setup the query request */
106 Pin
.Set
= KSPROPSETID_Sysaudio
;
107 Pin
.Id
= KSPROPERTY_SYSAUDIO_DEVICE_COUNT
;
108 Pin
.Flags
= KSPROPERTY_TYPE_GET
;
110 DeviceExtension
= (PWDMAUD_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
112 /* query sysaudio for the device count */
113 Status
= KsSynchronousIoControlDevice(DeviceExtension
->FileObject
, KernelMode
, IOCTL_KS_PROPERTY
, (PVOID
)&Pin
, sizeof(KSPROPERTY
), (PVOID
)&Count
, sizeof(ULONG
), &BytesReturned
);
114 if (!NT_SUCCESS(Status
))
121 GetSysAudioDevicePnpName(
122 IN PDEVICE_OBJECT DeviceObject
,
123 IN ULONG DeviceIndex
,
129 PWDMAUD_DEVICE_EXTENSION DeviceExtension
;
131 /* first check if the device index is within bounds */
132 if (DeviceIndex
>= GetSysAudioDeviceCount(DeviceObject
))
133 return STATUS_INVALID_PARAMETER
;
135 /* setup the query request */
136 Pin
.Property
.Set
= KSPROPSETID_Sysaudio
;
137 Pin
.Property
.Id
= KSPROPERTY_SYSAUDIO_DEVICE_INTERFACE_NAME
;
138 Pin
.Property
.Flags
= KSPROPERTY_TYPE_GET
;
139 Pin
.PinId
= DeviceIndex
;
141 DeviceExtension
= (PWDMAUD_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
143 /* query sysaudio for the device path */
144 Status
= KsSynchronousIoControlDevice(DeviceExtension
->FileObject
, KernelMode
, IOCTL_KS_PROPERTY
, (PVOID
)&Pin
, sizeof(KSPROPERTY
) + sizeof(ULONG
), NULL
, 0, &BytesReturned
);
146 /* check if the request failed */
147 if (Status
!= STATUS_BUFFER_TOO_SMALL
|| BytesReturned
== 0)
148 return STATUS_UNSUCCESSFUL
;
150 /* allocate buffer for the device */
151 *Device
= ExAllocatePool(NonPagedPool
, BytesReturned
);
153 return STATUS_INSUFFICIENT_RESOURCES
;
155 /* query sysaudio again for the device path */
156 Status
= KsSynchronousIoControlDevice(DeviceExtension
->FileObject
, KernelMode
, IOCTL_KS_PROPERTY
, (PVOID
)&Pin
, sizeof(KSPROPERTY
) + sizeof(ULONG
), (PVOID
)*Device
, BytesReturned
, &BytesReturned
);
158 if (!NT_SUCCESS(Status
))
171 OUT PHANDLE DeviceHandle
,
172 OUT PFILE_OBJECT
* FileObject
)
177 /* now open the device */
178 Status
= WdmAudOpenSysAudioDevice(Device
, &hDevice
);
180 if (!NT_SUCCESS(Status
))
185 *DeviceHandle
= hDevice
;
189 Status
= ObReferenceObjectByHandle(hDevice
, FILE_READ_DATA
| FILE_WRITE_DATA
, IoFileObjectType
, KernelMode
, (PVOID
*)FileObject
, NULL
);
191 if (!NT_SUCCESS(Status
))
203 OpenSysAudioDeviceByIndex(
204 IN PDEVICE_OBJECT DeviceObject
,
205 IN ULONG DeviceIndex
,
206 IN PHANDLE DeviceHandle
,
207 IN PFILE_OBJECT
* FileObject
)
209 LPWSTR Device
= NULL
;
212 Status
= GetSysAudioDevicePnpName(DeviceObject
, DeviceIndex
, &Device
);
213 if (!NT_SUCCESS(Status
))
216 Status
= OpenDevice(Device
, DeviceHandle
, FileObject
);
218 /* free device buffer */
225 GetFilterNodeProperty(
226 IN PFILE_OBJECT FileObject
,
228 PKSMULTIPLE_ITEM
* Item
)
232 PKSMULTIPLE_ITEM MultipleItem
;
235 /* setup query request */
236 Property
.Id
= PropertyId
;
237 Property
.Flags
= KSPROPERTY_TYPE_GET
;
238 Property
.Set
= KSPROPSETID_Topology
;
240 /* query for required size */
241 Status
= KsSynchronousIoControlDevice(FileObject
, KernelMode
, IOCTL_KS_PROPERTY
, (PVOID
)&Property
, sizeof(KSPROPERTY
), NULL
, 0, &BytesReturned
);
243 /* check for success */
244 if (Status
!= STATUS_MORE_ENTRIES
)
247 /* allocate buffer */
248 MultipleItem
= (PKSMULTIPLE_ITEM
)ExAllocatePool(NonPagedPool
, BytesReturned
);
250 return STATUS_INSUFFICIENT_RESOURCES
;
252 /* query for required size */
253 Status
= KsSynchronousIoControlDevice(FileObject
, KernelMode
, IOCTL_KS_PROPERTY
, (PVOID
)&Property
, sizeof(KSPROPERTY
), (PVOID
)MultipleItem
, BytesReturned
, &BytesReturned
);
255 if (!NT_SUCCESS(Status
))
258 ExFreePool(MultipleItem
);
262 *Item
= MultipleItem
;
268 PKSMULTIPLE_ITEM MultipleItem
,
276 Guid
= (LPGUID
)(MultipleItem
+1);
278 /* iterate through node type array */
279 for(Index
= 0; Index
< MultipleItem
->Count
; Index
++)
281 if (IsEqualGUIDAligned(NodeType
, Guid
))
283 /* found matching guid */
293 PKSMULTIPLE_ITEM MultipleItem
,
299 Guid
= (LPGUID
)(MultipleItem
+1);
301 /* iterate through node type array */
302 for(Index
= 0; Index
< MultipleItem
->Count
; Index
++)
304 if (IsEqualGUIDAligned(NodeType
, Guid
))
306 /* found matching guid */
315 GetControlTypeFromTopologyNode(
318 if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_AGC
))
320 // automatic gain control
321 return MIXERCONTROL_CONTROLTYPE_ONOFF
;
323 else if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_LOUDNESS
))
326 return MIXERCONTROL_CONTROLTYPE_LOUDNESS
;
328 else if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_MUTE
))
331 return MIXERCONTROL_CONTROLTYPE_MUTE
;
333 else if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_TONE
))
337 // MIXERCONTROL_CONTROLTYPE_ONOFF if KSPROPERTY_AUDIO_BASS_BOOST is supported
338 // MIXERCONTROL_CONTROLTYPE_BASS if KSPROPERTY_AUDIO_BASS is supported
339 // MIXERCONTROL_CONTROLTYPE_TREBLE if KSPROPERTY_AUDIO_TREBLE is supported
341 return MIXERCONTROL_CONTROLTYPE_ONOFF
;
343 else if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_VOLUME
))
346 return MIXERCONTROL_CONTROLTYPE_VOLUME
;
348 else if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_PEAKMETER
))
351 return MIXERCONTROL_CONTROLTYPE_PEAKMETER
;
353 else if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_MUX
))
356 return MIXERCONTROL_CONTROLTYPE_MUX
;
358 else if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_MUX
))
361 return MIXERCONTROL_CONTROLTYPE_MUX
;
363 else if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_STEREO_WIDE
))
365 // stero wide control
366 return MIXERCONTROL_CONTROLTYPE_FADER
;
368 else if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_CHORUS
))
371 return MIXERCONTROL_CONTROLTYPE_FADER
;
373 else if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_REVERB
))
376 return MIXERCONTROL_CONTROLTYPE_FADER
;
378 else if (IsEqualGUIDAligned(NodeType
, (LPGUID
)&KSNODETYPE_SUPERMIX
))
381 // MIXERCONTROL_CONTROLTYPE_MUTE if KSPROPERTY_AUDIO_MUTE is supported
383 return MIXERCONTROL_CONTROLTYPE_VOLUME
;
390 GetPhysicalConnection(
391 IN PFILE_OBJECT FileObject
,
393 OUT PKSPIN_PHYSICALCONNECTION
*OutConnection
)
398 PKSPIN_PHYSICALCONNECTION Connection
;
400 /* setup the request */
401 Pin
.Property
.Flags
= KSPROPERTY_TYPE_GET
;
402 Pin
.Property
.Id
= KSPROPERTY_PIN_PHYSICALCONNECTION
;
403 Pin
.Property
.Set
= KSPROPSETID_Pin
;
406 /* query the pin for the physical connection */
407 Status
= KsSynchronousIoControlDevice(FileObject
, KernelMode
, IOCTL_KS_PROPERTY
, (PVOID
)&Pin
, sizeof(KSP_PIN
), NULL
, 0, &BytesReturned
);
409 if (Status
== STATUS_NOT_FOUND
)
411 /* pin does not have a physical connection */
415 Connection
= ExAllocatePool(NonPagedPool
, BytesReturned
);
418 /* not enough memory */
419 return STATUS_INSUFFICIENT_RESOURCES
;
422 /* query the pin for the physical connection */
423 Status
= KsSynchronousIoControlDevice(FileObject
, KernelMode
, IOCTL_KS_PROPERTY
, (PVOID
)&Pin
, sizeof(KSP_PIN
), (PVOID
)Connection
, BytesReturned
, &BytesReturned
);
424 if (!NT_SUCCESS(Status
))
426 /* failed to query the physical connection */
427 ExFreePool(Connection
);
431 /* store connection */
432 *OutConnection
= Connection
;
438 IN PKSMULTIPLE_ITEM MultipleItem
,
442 OUT PULONG NodeReferenceCount
,
443 OUT PULONG
*NodeReference
)
445 ULONG Index
, Count
= 0;
446 PKSTOPOLOGY_CONNECTION Connection
;
449 /* KSMULTIPLE_ITEM is followed by several KSTOPOLOGY_CONNECTION */
450 Connection
= (PKSTOPOLOGY_CONNECTION
)(MultipleItem
+ 1);
452 /* first count all referenced nodes */
453 for(Index
= 0; Index
< MultipleItem
->Count
; Index
++)
455 //DPRINT1("FromPin %u FromNode %u ToPin %u ToNode %u\n", Connection->FromNodePin, Connection->FromNode, Connection->ToNodePin, Connection->ToNode);
460 if (Connection
->FromNode
== NodeIndex
)
462 /* node id has a connection */
468 if (Connection
->ToNode
== NodeIndex
)
470 /* node id has a connection */
479 if (Connection
->FromNodePin
== NodeIndex
)
481 /* node id has a connection */
487 if (Connection
->ToNodePin
== NodeIndex
)
489 /* node id has a connection */
496 /* move to next connection */
502 /* now allocate node index array */
503 Refs
= ExAllocatePool(NonPagedPool
, sizeof(ULONG
) * Count
);
506 /* not enough memory */
507 return STATUS_INSUFFICIENT_RESOURCES
;
511 Connection
= (PKSTOPOLOGY_CONNECTION
)(MultipleItem
+ 1);
512 for(Index
= 0; Index
< MultipleItem
->Count
; Index
++)
518 if (Connection
->FromNode
== NodeIndex
)
520 /* node id has a connection */
527 if (Connection
->ToNode
== NodeIndex
)
529 /* node id has a connection */
539 if (Connection
->FromNodePin
== NodeIndex
)
541 /* node id has a connection */
548 if (Connection
->ToNodePin
== NodeIndex
)
550 /* node id has a connection */
557 /* move to next connection */
562 *NodeReference
= Refs
;
563 *NodeReferenceCount
= Count
;
565 return STATUS_SUCCESS
;
570 GetTargetPinsByNodeConnectionIndex(
571 IN PKSMULTIPLE_ITEM NodeConnections
,
572 IN PKSMULTIPLE_ITEM NodeTypes
,
573 IN ULONG bUpDirection
,
574 IN ULONG NodeConnectionIndex
,
577 PKSTOPOLOGY_CONNECTION Connection
;
578 ULONG PinId
, NodeConnectionCount
, Index
;
579 PULONG NodeConnection
;
584 ASSERT(NodeConnectionIndex
< NodeConnections
->Count
);
586 Connection
= (PKSTOPOLOGY_CONNECTION
)(NodeConnections
+ 1);
588 DPRINT("FromNode %u FromNodePin %u -> ToNode %u ToNodePin %u\n", Connection
[NodeConnectionIndex
].FromNode
, Connection
[NodeConnectionIndex
].FromNodePin
, Connection
[NodeConnectionIndex
].ToNode
, Connection
[NodeConnectionIndex
].ToNodePin
);
590 if ((Connection
[NodeConnectionIndex
].ToNode
== KSFILTER_NODE
&& bUpDirection
== FALSE
) ||
591 (Connection
[NodeConnectionIndex
].FromNode
== KSFILTER_NODE
&& bUpDirection
== TRUE
))
593 /* iteration stops here */
595 PinId
= Connection
[NodeConnectionIndex
].FromNodePin
;
597 PinId
= Connection
[NodeConnectionIndex
].ToNodePin
;
599 DPRINT("GetTargetPinsByNodeIndex FOUND Target Pin %u Parsed %u\n", PinId
, Pins
[PinId
]);
601 /* mark pin index as a target pin */
603 return STATUS_SUCCESS
;
606 /* get all node indexes referenced by that node */
609 Status
= GetNodeIndexes(NodeConnections
, Connection
[NodeConnectionIndex
].FromNode
, TRUE
, FALSE
, &NodeConnectionCount
, &NodeConnection
);
613 Status
= GetNodeIndexes(NodeConnections
, Connection
[NodeConnectionIndex
].ToNode
, TRUE
, TRUE
, &NodeConnectionCount
, &NodeConnection
);
616 if (NT_SUCCESS(Status
))
618 for(Index
= 0; Index
< NodeConnectionCount
; Index
++)
620 /* iterate recursively into the nodes */
621 Status
= GetTargetPinsByNodeConnectionIndex(NodeConnections
, NodeTypes
, bUpDirection
, NodeConnection
[Index
], Pins
);
622 ASSERT(Status
== STATUS_SUCCESS
);
624 /* free node connection indexes */
625 ExFreePool(NodeConnection
);
635 PKSMULTIPLE_ITEM NodeTypes
,
636 PKSMULTIPLE_ITEM NodeConnections
,
638 IN ULONG bUpDirection
,
642 ULONG NodeConnectionCount
, Index
;
644 PULONG NodeConnection
;
647 ASSERT(NodeIndex
!= (ULONG
)-1);
649 /* get all node indexes referenced by that pin */
651 Status
= GetNodeIndexes(NodeConnections
, NodeIndex
, TRUE
, FALSE
, &NodeConnectionCount
, &NodeConnection
);
653 Status
= GetNodeIndexes(NodeConnections
, NodeIndex
, TRUE
, TRUE
, &NodeConnectionCount
, &NodeConnection
);
655 DPRINT("NodeIndex %u Status %x Count %u\n", NodeIndex
, Status
, NodeConnectionCount
);
657 if (NT_SUCCESS(Status
))
659 for(Index
= 0; Index
< NodeConnectionCount
; Index
++)
661 Status
= GetTargetPinsByNodeConnectionIndex(NodeConnections
, NodeTypes
, bUpDirection
, NodeConnection
[Index
], Pins
);
662 ASSERT(Status
== STATUS_SUCCESS
);
664 ExFreePool(NodeConnection
);
674 PULONG Pins
= ExAllocatePool(NonPagedPool
, PinCount
* sizeof(ULONG
));
678 RtlZeroMemory(Pins
, sizeof(ULONG
) * PinCount
);
685 IN OUT LPMIXER_INFO MixerInfo
,
686 IN PFILE_OBJECT FileObject
,
689 LPMIXERLINE_SOURCE SrcLine
;
696 /* allocate src mixer line */
697 SrcLine
= (LPMIXERLINE_SOURCE
)ExAllocatePool(NonPagedPool
, sizeof(MIXERLINE_SOURCE
));
699 return STATUS_INSUFFICIENT_RESOURCES
;
702 RtlZeroMemory(SrcLine
, sizeof(MIXERLINE_SOURCE
));
704 /* initialize mixer src line */
705 SrcLine
->FileObject
= FileObject
;
706 SrcLine
->PinId
= PinId
;
707 SrcLine
->Line
.cbStruct
= sizeof(MIXERLINEW
);
709 /* initialize mixer destination line */
710 SrcLine
->Line
.cbStruct
= sizeof(MIXERLINEW
);
711 SrcLine
->Line
.dwDestination
= 0;
712 SrcLine
->Line
.dwSource
= MixerInfo
->DestinationLine
.cConnections
;
713 SrcLine
->Line
.dwLineID
= (MixerInfo
->DestinationLine
.cConnections
* 0x10000);
714 SrcLine
->Line
.fdwLine
= MIXERLINE_LINEF_ACTIVE
| MIXERLINE_LINEF_SOURCE
;
715 SrcLine
->Line
.dwUser
= 0;
716 SrcLine
->Line
.cChannels
= MixerInfo
->DestinationLine
.cChannels
;
717 SrcLine
->Line
.cConnections
= 0;
718 SrcLine
->Line
.cControls
= 1; //FIXME
721 SrcLine
->LineControls
= ExAllocatePool(NonPagedPool
, SrcLine
->Line
.cControls
* sizeof(MIXERCONTROLW
));
722 if (!SrcLine
->LineControls
)
724 /* not enough memory */
726 return STATUS_INSUFFICIENT_RESOURCES
;
729 /* clear line controls */
730 RtlZeroMemory(SrcLine
->LineControls
, sizeof(MIXERCONTROLW
));
732 /* fill in pseudo mixer control */
733 SrcLine
->LineControls
->dwControlID
= 1; //FIXME
734 SrcLine
->LineControls
->cbStruct
= sizeof(MIXERCONTROLW
);
735 SrcLine
->LineControls
->fdwControl
= 0;
736 SrcLine
->LineControls
->cMultipleItems
= 0;
737 wcscpy(SrcLine
->LineControls
->szName
, L
"test");
738 wcscpy(SrcLine
->LineControls
->szShortName
, L
"test");
741 /* get pin category */
744 Pin
.Property
.Flags
= KSPROPERTY_TYPE_GET
;
745 Pin
.Property
.Set
= KSPROPSETID_Pin
;
746 Pin
.Property
.Id
= KSPROPERTY_PIN_CATEGORY
;
748 /* try get pin category */
749 Status
= KsSynchronousIoControlDevice(FileObject
, KernelMode
, IOCTL_KS_PROPERTY
, (PVOID
)&Pin
, sizeof(KSP_PIN
), (LPVOID
)&NodeType
, sizeof(GUID
), &BytesReturned
);
750 if (NT_SUCCESS(Status
))
756 /* retrieve pin name */
759 Pin
.Property
.Flags
= KSPROPERTY_TYPE_GET
;
760 Pin
.Property
.Set
= KSPROPSETID_Pin
;
761 Pin
.Property
.Id
= KSPROPERTY_PIN_NAME
;
763 /* try get pin name size */
764 Status
= KsSynchronousIoControlDevice(FileObject
, KernelMode
, IOCTL_KS_PROPERTY
, (PVOID
)&Pin
, sizeof(KSP_PIN
), NULL
, 0, &BytesReturned
);
766 if (Status
!= STATUS_MORE_ENTRIES
)
768 SrcLine
->Line
.szShortName
[0] = L
'\0';
769 SrcLine
->Line
.szName
[0] = L
'\0';
773 PinName
= (LPWSTR
)ExAllocatePool(NonPagedPool
, BytesReturned
);
776 /* try get pin name */
777 Status
= KsSynchronousIoControlDevice(FileObject
, KernelMode
, IOCTL_KS_PROPERTY
, (PVOID
)&Pin
, sizeof(KSP_PIN
), (LPVOID
)PinName
, BytesReturned
, &BytesReturned
);
779 if (NT_SUCCESS(Status
))
781 RtlMoveMemory(SrcLine
->Line
.szShortName
, PinName
, (min(MIXER_SHORT_NAME_CHARS
, wcslen(PinName
)+1)) * sizeof(WCHAR
));
782 SrcLine
->Line
.szShortName
[MIXER_SHORT_NAME_CHARS
-1] = L
'\0';
784 RtlMoveMemory(SrcLine
->Line
.szName
, PinName
, (min(MIXER_LONG_NAME_CHARS
, wcslen(PinName
)+1)) * sizeof(WCHAR
));
785 SrcLine
->Line
.szName
[MIXER_LONG_NAME_CHARS
-1] = L
'\0';
791 SrcLine
->Line
.Target
.dwType
= 1;
792 SrcLine
->Line
.Target
.dwDeviceID
= MixerInfo
->DestinationLine
.Target
.dwDeviceID
;
793 SrcLine
->Line
.Target
.wMid
= MixerInfo
->MixCaps
.wMid
;
794 SrcLine
->Line
.Target
.wPid
= MixerInfo
->MixCaps
.wPid
;
795 SrcLine
->Line
.Target
.vDriverVersion
= MixerInfo
->MixCaps
.vDriverVersion
;
796 wcscpy(SrcLine
->Line
.Target
.szPname
, MixerInfo
->MixCaps
.szPname
);
799 /* insert src line */
800 InsertTailList(&MixerInfo
->SourceLineList
, &SrcLine
->Entry
);
801 MixerInfo
->DestinationLine
.cConnections
++;
803 return STATUS_SUCCESS
;
809 IN OUT LPMIXER_INFO MixerInfo
,
810 IN PFILE_OBJECT FileObject
,
815 NTSTATUS Status
= STATUS_SUCCESS
;
817 for(Index
= PinsCount
; Index
> 0; Index
--)
821 AddMixerSourceLine(MixerInfo
, FileObject
, Index
-1);
830 HandlePhysicalConnection(
831 IN OUT LPMIXER_INFO MixerInfo
,
833 IN PKSPIN_PHYSICALCONNECTION OutConnection
)
835 PULONG PinsRef
= NULL
, PinConnectionIndex
= NULL
, PinsSrcRef
;
836 ULONG PinsRefCount
, Index
, PinConnectionIndexCount
;
838 HANDLE hDevice
= NULL
;
839 PFILE_OBJECT FileObject
= NULL
;
840 PKSMULTIPLE_ITEM NodeTypes
= NULL
;
841 PKSMULTIPLE_ITEM NodeConnections
= NULL
;
842 PULONG MixerControls
;
843 ULONG MixerControlsCount
;
846 /* open the connected filter */
847 Status
= OpenDevice(OutConnection
->SymbolicLinkName
, &hDevice
, &FileObject
);
848 if (!NT_SUCCESS(Status
))
850 DPRINT1("OpenDevice failed with %x\n", Status
);
854 /* get connected filter pin count */
855 PinsRefCount
= GetPinCount(FileObject
);
856 ASSERT(PinsRefCount
);
858 PinsRef
= AllocatePinArray(PinsRefCount
);
862 Status
= STATUS_INSUFFICIENT_RESOURCES
;
866 /* get topology node types */
867 Status
= GetFilterNodeProperty(FileObject
, KSPROPERTY_TOPOLOGY_NODES
, &NodeTypes
);
868 if (!NT_SUCCESS(Status
))
870 DPRINT1("GetFilterNodeProperty failed with %x\n", Status
);
874 /* get topology connections */
875 Status
= GetFilterNodeProperty(FileObject
, KSPROPERTY_TOPOLOGY_CONNECTIONS
, &NodeConnections
);
876 if (!NT_SUCCESS(Status
))
878 DPRINT1("GetFilterNodeProperty failed with %x\n", Status
);
881 /* gets connection index of the bridge pin which connects to a node */
882 DPRINT("Pin %u\n", OutConnection
->Pin
);
883 Status
= GetNodeIndexes(NodeConnections
, OutConnection
->Pin
, FALSE
, !bInput
, &PinConnectionIndexCount
, &PinConnectionIndex
);
884 if (!NT_SUCCESS(Status
))
886 DPRINT1("GetNodeIndexes failed with %x\n", Status
);
890 /* there should be no split in the bride pin */
891 ASSERT(PinConnectionIndexCount
== 1);
893 /* find all target pins of this connection */
894 Status
= GetTargetPinsByNodeConnectionIndex(NodeConnections
, NodeTypes
, FALSE
, PinConnectionIndex
[0], PinsRef
);
895 if (!NT_SUCCESS(Status
))
897 DPRINT1("GetTargetPinsByNodeConnectionIndex failed with %x\n", Status
);
901 for(Index
= 0; Index
< PinsRefCount
; Index
++)
906 /* found a target pin, now get all references */
907 Status
= GetNodeIndexes(NodeConnections
, Index
, FALSE
, FALSE
, &MixerControlsCount
, &MixerControls
);
908 if (!NT_SUCCESS(Status
))
912 ASSERT(MixerControlsCount
== 1);
915 PinsSrcRef
= AllocatePinArray(PinsRefCount
);
919 ExFreePool(MixerControls
);
920 Status
= STATUS_INSUFFICIENT_RESOURCES
;
923 /* now get all connected source pins */
924 Status
= GetTargetPinsByNodeConnectionIndex(NodeConnections
, NodeTypes
, TRUE
, MixerControls
[0], PinsSrcRef
);
925 if (!NT_SUCCESS(Status
))
928 ExFreePool(MixerControls
);
929 ExFreePool(PinsSrcRef
);
930 Status
= STATUS_INSUFFICIENT_RESOURCES
;
934 /* add pins from target line */
937 // dont add bridge pin for input mixers
938 PinsSrcRef
[Index
] = TRUE
;
939 PinsSrcRef
[OutConnection
->Pin
] = TRUE
;
942 Status
= AddMixerSourceLines(MixerInfo
, FileObject
, PinsRefCount
, PinsSrcRef
);
944 ExFreePool(MixerControls
);
945 ExFreePool(PinsSrcRef
);
955 ExFreePool(NodeConnections
);
958 ExFreePool(NodeTypes
);
961 ObDereferenceObject(FileObject
);
966 if (PinConnectionIndex
)
967 ExFreePool(PinConnectionIndex
);
977 IN PDEVICE_OBJECT DeviceObject
,
978 IN ULONG DeviceIndex
,
979 IN OUT LPMIXER_INFO MixerInfo
,
981 IN PFILE_OBJECT FileObject
,
983 IN PKSMULTIPLE_ITEM NodeTypes
,
984 IN PKSMULTIPLE_ITEM NodeConnections
,
993 PKSPIN_PHYSICALCONNECTION OutConnection
;
995 /* initialize mixer info */
996 MixerInfo
->hMixer
= hDevice
;
997 MixerInfo
->MixerFileObject
= FileObject
;
999 /* intialize mixer caps */
1000 MixerInfo
->MixCaps
.wMid
= MM_MICROSOFT
; //FIXME
1001 MixerInfo
->MixCaps
.wPid
= MM_PID_UNMAPPED
; //FIXME
1002 MixerInfo
->MixCaps
.vDriverVersion
= 1; //FIXME
1003 MixerInfo
->MixCaps
.fdwSupport
= 0;
1004 MixerInfo
->MixCaps
.cDestinations
= 1;
1006 /* get target pnp name */
1007 Status
= GetSysAudioDevicePnpName(DeviceObject
, DeviceIndex
, &Device
);
1008 if (NT_SUCCESS(Status
))
1010 /* find product name */
1011 Status
= FindProductName(Device
, sizeof(Buffer
) / sizeof(WCHAR
), Buffer
);
1012 if (NT_SUCCESS(Status
))
1015 wcscat(Buffer
, L
" Input");
1017 wcscat(Buffer
, L
" output");
1018 RtlMoveMemory(MixerInfo
->MixCaps
.szPname
, Buffer
, min(MAXPNAMELEN
, wcslen(Buffer
)+1) * sizeof(WCHAR
));
1019 MixerInfo
->MixCaps
.szPname
[MAXPNAMELEN
-1] = L
'\0';
1024 /* initialize mixer destination line */
1025 MixerInfo
->DestinationLine
.cbStruct
= sizeof(MIXERLINEW
);
1026 MixerInfo
->DestinationLine
.dwSource
= MAXULONG
;
1027 MixerInfo
->DestinationLine
.dwLineID
= 0xFFFF0000;
1028 MixerInfo
->DestinationLine
.fdwLine
= MIXERLINE_LINEF_ACTIVE
;
1029 MixerInfo
->DestinationLine
.dwUser
= 0;
1030 MixerInfo
->DestinationLine
.dwComponentType
= (bInput
== 0 ? MIXERLINE_COMPONENTTYPE_DST_SPEAKERS
: MIXERLINE_COMPONENTTYPE_DST_WAVEIN
);
1031 MixerInfo
->DestinationLine
.cChannels
= 2; //FIXME
1032 MixerInfo
->DestinationLine
.cControls
= 0; //FIXME
1033 wcscpy(MixerInfo
->DestinationLine
.szShortName
, L
"Summe"); //FIXME
1034 wcscpy(MixerInfo
->DestinationLine
.szName
, L
"Summe"); //FIXME
1035 MixerInfo
->DestinationLine
.Target
.dwType
= (bInput
== 0 ? MIXERLINE_TARGETTYPE_WAVEOUT
: MIXERLINE_TARGETTYPE_WAVEIN
);
1036 MixerInfo
->DestinationLine
.Target
.dwDeviceID
= !bInput
;
1037 MixerInfo
->DestinationLine
.Target
.wMid
= MixerInfo
->MixCaps
.wMid
;
1038 MixerInfo
->DestinationLine
.Target
.wPid
= MixerInfo
->MixCaps
.wPid
;
1039 MixerInfo
->DestinationLine
.Target
.vDriverVersion
= MixerInfo
->MixCaps
.vDriverVersion
;
1040 wcscpy(MixerInfo
->DestinationLine
.Target
.szPname
, MixerInfo
->MixCaps
.szPname
);
1043 /* initialize source line list */
1044 InitializeListHead(&MixerInfo
->SourceLineList
);
1046 Pins
= AllocatePinArray(PinCount
);
1048 return STATUS_INSUFFICIENT_RESOURCES
;
1052 Status
= GetTargetPins(NodeTypes
, NodeConnections
, NodeIndex
, TRUE
, Pins
, PinCount
);
1056 Status
= GetTargetPins(NodeTypes
, NodeConnections
, NodeIndex
, FALSE
, Pins
, PinCount
);
1059 for(Index
= 0; Index
< PinCount
; Index
++)
1063 Status
= GetPhysicalConnection(FileObject
, Index
, &OutConnection
);
1064 if (NT_SUCCESS(Status
))
1066 Status
= HandlePhysicalConnection(MixerInfo
, bInput
, OutConnection
);
1072 return STATUS_SUCCESS
;
1076 WdmAudMixerInitialize(
1077 IN PDEVICE_OBJECT DeviceObject
)
1079 ULONG DeviceCount
, Index
, Count
, NodeIndex
, PinCount
;
1082 PFILE_OBJECT FileObject
;
1083 PKSMULTIPLE_ITEM NodeTypes
, NodeConnections
;
1085 PWDMAUD_DEVICE_EXTENSION DeviceExtension
;
1087 /* get device extension */
1088 DeviceExtension
= (PWDMAUD_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
1091 /* get number of devices */
1092 DeviceCount
= GetSysAudioDeviceCount(DeviceObject
);
1096 /* no audio devices available atm */
1097 DeviceExtension
->MixerInfoCount
= 0;
1098 DeviceExtension
->MixerInfo
= NULL
;
1099 return STATUS_SUCCESS
;
1102 /* each virtual audio device can at most have an input + output mixer */
1103 DeviceExtension
->MixerInfo
= ExAllocatePool(NonPagedPool
, sizeof(MIXER_INFO
) * DeviceCount
* 2);
1104 if (!DeviceExtension
->MixerInfo
)
1106 /* not enough memory */
1107 return STATUS_INSUFFICIENT_RESOURCES
;
1110 /* clear mixer info */
1111 RtlZeroMemory(DeviceExtension
->MixerInfo
, sizeof(MIXER_INFO
) * DeviceCount
* 2);
1117 /* open the virtual audio device */
1118 Status
= OpenSysAudioDeviceByIndex(DeviceObject
, Index
, &hDevice
, &FileObject
);
1120 if (NT_SUCCESS(Status
))
1122 /* retrieve all available node types */
1123 Status
= GetFilterNodeProperty(FileObject
, KSPROPERTY_TOPOLOGY_NODES
, &NodeTypes
);
1124 if (!NT_SUCCESS(Status
))
1126 ObDereferenceObject(FileObject
);
1131 Status
= GetFilterNodeProperty(FileObject
, KSPROPERTY_TOPOLOGY_CONNECTIONS
, &NodeConnections
);
1132 if (!NT_SUCCESS(Status
))
1134 ObDereferenceObject(FileObject
);
1136 ExFreePool(NodeTypes
);
1140 /* get num of pins */
1141 PinCount
= GetPinCount(FileObject
);
1142 bCloseHandle
= TRUE
;
1143 /* get the first available dac node index */
1144 NodeIndex
= GetNodeTypeIndex(NodeTypes
, (LPGUID
)&KSNODETYPE_DAC
);
1145 if (NodeIndex
!= (ULONG
)-1)
1147 Status
= InitializeMixer(DeviceObject
, Index
, &DeviceExtension
->MixerInfo
[Count
], hDevice
, FileObject
, PinCount
, NodeTypes
, NodeConnections
, NodeIndex
, FALSE
);
1148 if (NT_SUCCESS(Status
))
1150 /* increment mixer offset */
1152 bCloseHandle
= FALSE
;
1156 /* get the first available adc node index */
1157 NodeIndex
= GetNodeTypeIndex(NodeTypes
, (LPGUID
)&KSNODETYPE_ADC
);
1158 if (NodeIndex
!= (ULONG
)-1)
1160 Status
= InitializeMixer(DeviceObject
, Index
, &DeviceExtension
->MixerInfo
[Count
], hDevice
, FileObject
, PinCount
, NodeTypes
, NodeConnections
, NodeIndex
, TRUE
);
1161 if (NT_SUCCESS(Status
))
1163 /* increment mixer offset */
1165 bCloseHandle
= FALSE
;
1169 /* free node connections array */
1170 ExFreePool(NodeTypes
);
1171 ExFreePool(NodeConnections
);
1175 /* close virtual audio device */
1176 ObDereferenceObject(FileObject
);
1180 /* increment virtual audio device index */
1182 }while(Index
< DeviceCount
);
1184 /* store mixer count */
1185 DeviceExtension
->MixerInfoCount
= Count
;
1193 WdmAudMixerCapabilities(
1194 IN PDEVICE_OBJECT DeviceObject
,
1195 IN PWDMAUD_DEVICE_INFO DeviceInfo
,
1196 IN PWDMAUD_CLIENT ClientInfo
,
1197 IN PWDMAUD_DEVICE_EXTENSION DeviceExtension
)
1199 if ((ULONG
)DeviceInfo
->hDevice
>= DeviceExtension
->MixerInfoCount
)
1201 /* invalid parameter */
1202 return STATUS_INVALID_PARAMETER
;
1205 /* copy cached mixer caps */
1206 RtlMoveMemory(&DeviceInfo
->u
.MixCaps
, &DeviceExtension
->MixerInfo
[(ULONG
)DeviceInfo
->hDevice
].MixCaps
, sizeof(MIXERCAPSW
));
1208 return STATUS_SUCCESS
;
1213 WdmAudControlOpenMixer(
1214 IN PDEVICE_OBJECT DeviceObject
,
1216 IN PWDMAUD_DEVICE_INFO DeviceInfo
,
1217 IN PWDMAUD_CLIENT ClientInfo
)
1220 PWDMAUD_HANDLE Handels
;
1221 PWDMAUD_DEVICE_EXTENSION DeviceExtension
;
1223 DPRINT("WdmAudControlOpenMixer\n");
1225 DeviceExtension
= (PWDMAUD_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
1228 if (DeviceInfo
->DeviceIndex
>= DeviceExtension
->MixerInfoCount
)
1230 /* mixer index doesnt exist */
1231 return SetIrpIoStatus(Irp
, STATUS_UNSUCCESSFUL
, 0);
1234 for(Index
= 0; Index
< ClientInfo
->NumPins
; Index
++)
1236 if (ClientInfo
->hPins
[Index
].Handle
== (HANDLE
)DeviceInfo
->DeviceIndex
&& ClientInfo
->hPins
[Index
].Type
== MIXER_DEVICE_TYPE
)
1238 /* re-use pseudo handle */
1239 DeviceInfo
->hDevice
= (HANDLE
)DeviceInfo
->DeviceIndex
;
1240 return SetIrpIoStatus(Irp
, STATUS_SUCCESS
, sizeof(WDMAUD_DEVICE_INFO
));
1244 Handels
= ExAllocatePool(NonPagedPool
, sizeof(WDMAUD_HANDLE
) * (ClientInfo
->NumPins
+1));
1248 if (ClientInfo
->NumPins
)
1250 RtlMoveMemory(Handels
, ClientInfo
->hPins
, sizeof(WDMAUD_HANDLE
) * ClientInfo
->NumPins
);
1251 ExFreePool(ClientInfo
->hPins
);
1254 ClientInfo
->hPins
= Handels
;
1255 ClientInfo
->hPins
[ClientInfo
->NumPins
].Handle
= (HANDLE
)DeviceInfo
->DeviceIndex
;
1256 ClientInfo
->hPins
[ClientInfo
->NumPins
].Type
= MIXER_DEVICE_TYPE
;
1257 ClientInfo
->NumPins
++;
1261 return SetIrpIoStatus(Irp
, STATUS_UNSUCCESSFUL
, sizeof(WDMAUD_DEVICE_INFO
));
1263 DeviceInfo
->hDevice
= (HANDLE
)DeviceInfo
->DeviceIndex
;
1265 return SetIrpIoStatus(Irp
, STATUS_SUCCESS
, sizeof(WDMAUD_DEVICE_INFO
));
1271 IN PDEVICE_OBJECT DeviceObject
,
1273 IN PWDMAUD_DEVICE_INFO DeviceInfo
,
1274 IN PWDMAUD_CLIENT ClientInfo
)
1276 PWDMAUD_DEVICE_EXTENSION DeviceExtension
;
1277 LPMIXERLINE_SOURCE MixerLineSrc
;
1279 /* get device extension */
1280 DeviceExtension
= (PWDMAUD_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
1282 if (DeviceInfo
->Flags
== MIXER_GETLINEINFOF_DESTINATION
)
1284 if ((ULONG
)DeviceInfo
->hDevice
>= DeviceExtension
->MixerInfoCount
)
1286 /* invalid parameter */
1287 return SetIrpIoStatus(Irp
, STATUS_INVALID_PARAMETER
, 0);
1290 if (DeviceInfo
->u
.MixLine
.dwDestination
!= 0)
1292 /* invalid parameter */
1293 return SetIrpIoStatus(Irp
, STATUS_INVALID_PARAMETER
, 0);
1296 /* copy cached data */
1297 RtlCopyMemory(&DeviceInfo
->u
.MixLine
, &DeviceExtension
->MixerInfo
[(ULONG
)DeviceInfo
->hDevice
].DestinationLine
, sizeof(MIXERLINEW
));
1298 return SetIrpIoStatus(Irp
, STATUS_SUCCESS
, sizeof(WDMAUD_DEVICE_INFO
));
1300 else if (DeviceInfo
->Flags
== MIXER_GETLINEINFOF_SOURCE
)
1302 if ((ULONG
)DeviceInfo
->hDevice
>= DeviceExtension
->MixerInfoCount
)
1304 /* invalid parameter */
1305 return SetIrpIoStatus(Irp
, STATUS_INVALID_PARAMETER
, 0);
1308 if (DeviceInfo
->u
.MixLine
.dwSource
>= DeviceExtension
->MixerInfo
[(ULONG
)DeviceInfo
->hDevice
].DestinationLine
.cConnections
)
1310 DPRINT1("dwSource %u Destinations %u\n", DeviceInfo
->u
.MixLine
.dwSource
, DeviceExtension
->MixerInfo
[(ULONG
)DeviceInfo
->hDevice
].DestinationLine
.cConnections
);
1311 /* invalid parameter */
1312 return SetIrpIoStatus(Irp
, STATUS_INVALID_PARAMETER
, 0);
1315 MixerLineSrc
= GetSourceMixerLine(&DeviceExtension
->MixerInfo
[(ULONG
)DeviceInfo
->hDevice
], DeviceInfo
->u
.MixLine
.dwSource
);
1318 DPRINT("Line %u Name %S\n", MixerLineSrc
->Line
.dwSource
, MixerLineSrc
->Line
.szName
);
1319 RtlCopyMemory(&DeviceInfo
->u
.MixLine
, &MixerLineSrc
->Line
, sizeof(MIXERLINEW
));
1321 return SetIrpIoStatus(Irp
, STATUS_SUCCESS
, sizeof(WDMAUD_DEVICE_INFO
));
1324 DPRINT1("Flags %x\n", DeviceInfo
->Flags
);
1328 return SetIrpIoStatus(Irp
, STATUS_NOT_IMPLEMENTED
, 0);
1334 WdmAudGetLineControls(
1335 IN PDEVICE_OBJECT DeviceObject
,
1337 IN PWDMAUD_DEVICE_INFO DeviceInfo
,
1338 IN PWDMAUD_CLIENT ClientInfo
)
1340 LPMIXERLINE_SOURCE MixerLineSrc
;
1341 PWDMAUD_DEVICE_EXTENSION DeviceExtension
;
1343 /* get device extension */
1344 DeviceExtension
= (PWDMAUD_DEVICE_EXTENSION
)DeviceObject
->DeviceExtension
;
1346 if (DeviceInfo
->Flags
== MIXER_GETLINECONTROLSF_ALL
)
1348 if ((ULONG
)DeviceInfo
->hDevice
>= DeviceExtension
->MixerInfoCount
)
1350 /* invalid parameter */
1351 return SetIrpIoStatus(Irp
, STATUS_INVALID_PARAMETER
, 0);
1354 MixerLineSrc
= GetSourceMixerLineByLineId(&DeviceExtension
->MixerInfo
[(ULONG
)DeviceInfo
->hDevice
], DeviceInfo
->u
.MixControls
.dwLineID
);
1355 ASSERT(MixerLineSrc
);
1358 RtlMoveMemory(DeviceInfo
->u
.MixControls
.pamxctrl
, MixerLineSrc
->LineControls
, min(MixerLineSrc
->Line
.cControls
, DeviceInfo
->u
.MixControls
.cControls
) * sizeof(MIXERLINECONTROLSW
));
1360 return SetIrpIoStatus(Irp
, STATUS_SUCCESS
, sizeof(WDMAUD_DEVICE_INFO
));
1368 return SetIrpIoStatus(Irp
, STATUS_NOT_IMPLEMENTED
, 0);
1374 WdmAudSetControlDetails(
1375 IN PDEVICE_OBJECT DeviceObject
,
1377 IN PWDMAUD_DEVICE_INFO DeviceInfo
,
1378 IN PWDMAUD_CLIENT ClientInfo
)
1382 return SetIrpIoStatus(Irp
, STATUS_NOT_IMPLEMENTED
, 0);
1388 WdmAudGetControlDetails(
1389 IN PDEVICE_OBJECT DeviceObject
,
1391 IN PWDMAUD_DEVICE_INFO DeviceInfo
,
1392 IN PWDMAUD_CLIENT ClientInfo
)
1396 return SetIrpIoStatus(Irp
, STATUS_NOT_IMPLEMENTED
, 0);